mirror of
https://github.com/portainer/portainer.git
synced 2025-07-21 14:29:40 +02:00
refactor(helm): helm binary to sdk refactor [r8s-229] (#463)
Co-authored-by: stevensbkang <skan070@gmail.com>
This commit is contained in:
parent
0d25f3f430
commit
b5961d79f8
56 changed files with 2222 additions and 819 deletions
165
pkg/libhelm/sdk/client.go
Normal file
165
pkg/libhelm/sdk/client.go
Normal file
|
@ -0,0 +1,165 @@
|
|||
package sdk
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/portainer/portainer/pkg/libhelm/options"
|
||||
"github.com/rs/zerolog/log"
|
||||
"helm.sh/helm/v3/pkg/action"
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
"helm.sh/helm/v3/pkg/cli"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/client-go/discovery"
|
||||
"k8s.io/client-go/discovery/cached/memory"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/restmapper"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/client-go/tools/clientcmd/api"
|
||||
)
|
||||
|
||||
// newRESTClientGetter creates a custom RESTClientGetter using the provided client config
|
||||
type clientConfigGetter struct {
|
||||
clientConfig clientcmd.ClientConfig
|
||||
namespace string
|
||||
}
|
||||
|
||||
// initActionConfig initializes the action configuration with kubernetes config
|
||||
func (hspm *HelmSDKPackageManager) initActionConfig(actionConfig *action.Configuration, namespace string, k8sAccess *options.KubernetesClusterAccess) error {
|
||||
// If namespace is not provided, use the default namespace
|
||||
if namespace == "" {
|
||||
namespace = "default"
|
||||
}
|
||||
|
||||
if k8sAccess == nil {
|
||||
// Use default kubeconfig
|
||||
settings := cli.New()
|
||||
clientGetter := settings.RESTClientGetter()
|
||||
return actionConfig.Init(clientGetter, namespace, "secret", hspm.logf)
|
||||
}
|
||||
|
||||
// Create client config
|
||||
configAPI := generateConfigAPI(namespace, k8sAccess)
|
||||
clientConfig := clientcmd.NewDefaultClientConfig(*configAPI, &clientcmd.ConfigOverrides{})
|
||||
|
||||
// Create a custom RESTClientGetter that uses our in-memory config
|
||||
clientGetter, err := newRESTClientGetter(clientConfig, namespace)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Str("context", "HelmClient").
|
||||
Str("cluster_name", k8sAccess.ClusterName).
|
||||
Str("cluster_url", k8sAccess.ClusterServerURL).
|
||||
Str("user_name", k8sAccess.UserName).
|
||||
Err(err).
|
||||
Msg("failed to create client getter")
|
||||
return err
|
||||
}
|
||||
|
||||
return actionConfig.Init(clientGetter, namespace, "secret", hspm.logf)
|
||||
}
|
||||
|
||||
// generateConfigAPI generates a new kubeconfig configuration
|
||||
func generateConfigAPI(namespace string, k8sAccess *options.KubernetesClusterAccess) *api.Config {
|
||||
// Create in-memory kubeconfig configuration
|
||||
configAPI := api.NewConfig()
|
||||
|
||||
// Create cluster
|
||||
cluster := api.NewCluster()
|
||||
cluster.Server = k8sAccess.ClusterServerURL
|
||||
|
||||
if k8sAccess.CertificateAuthorityFile != "" {
|
||||
// If we have a CA file, use it
|
||||
cluster.CertificateAuthority = k8sAccess.CertificateAuthorityFile
|
||||
} else {
|
||||
// Otherwise skip TLS verification
|
||||
cluster.InsecureSkipTLSVerify = true
|
||||
}
|
||||
|
||||
// Create auth info with token
|
||||
authInfo := api.NewAuthInfo()
|
||||
authInfo.Token = k8sAccess.AuthToken
|
||||
|
||||
// Create context
|
||||
context := api.NewContext()
|
||||
context.Cluster = k8sAccess.ClusterName
|
||||
context.AuthInfo = k8sAccess.UserName
|
||||
context.Namespace = namespace
|
||||
|
||||
// Add to config
|
||||
configAPI.Clusters[k8sAccess.ClusterName] = cluster
|
||||
configAPI.AuthInfos[k8sAccess.UserName] = authInfo
|
||||
configAPI.Contexts[k8sAccess.ContextName] = context
|
||||
configAPI.CurrentContext = k8sAccess.ContextName
|
||||
|
||||
return configAPI
|
||||
}
|
||||
|
||||
func newRESTClientGetter(clientConfig clientcmd.ClientConfig, namespace string) (*clientConfigGetter, error) {
|
||||
if clientConfig == nil {
|
||||
log.Error().
|
||||
Str("context", "HelmClient").
|
||||
Msg("client config is nil")
|
||||
|
||||
return nil, errors.New("client config provided during the helm client initialization was nil. Check the kubernetes cluster access configuration")
|
||||
}
|
||||
|
||||
return &clientConfigGetter{
|
||||
clientConfig: clientConfig,
|
||||
namespace: namespace,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *clientConfigGetter) ToRESTConfig() (*rest.Config, error) {
|
||||
if c.clientConfig == nil {
|
||||
log.Error().
|
||||
Str("context", "HelmClient").
|
||||
Msg("client config is nil")
|
||||
|
||||
return nil, errors.New("client config provided during the helm client initialization was nil. Check the kubernetes cluster access configuration")
|
||||
}
|
||||
|
||||
return c.clientConfig.ClientConfig()
|
||||
}
|
||||
|
||||
func (c *clientConfigGetter) ToDiscoveryClient() (discovery.CachedDiscoveryInterface, error) {
|
||||
config, err := c.ToRESTConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create the discovery client
|
||||
discoveryClient, err := discovery.NewDiscoveryClientForConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Wrap the discovery client with a cached discovery client
|
||||
return memory.NewMemCacheClient(discoveryClient), nil
|
||||
}
|
||||
|
||||
func (c *clientConfigGetter) ToRESTMapper() (meta.RESTMapper, error) {
|
||||
discoveryClient, err := c.ToDiscoveryClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create a REST mapper from the discovery client
|
||||
return restmapper.NewDeferredDiscoveryRESTMapper(discoveryClient), nil
|
||||
}
|
||||
|
||||
func (c *clientConfigGetter) ToRawKubeConfigLoader() clientcmd.ClientConfig {
|
||||
return c.clientConfig
|
||||
}
|
||||
|
||||
// parseValues parses YAML values data into a map
|
||||
func (hspm *HelmSDKPackageManager) parseValues(data []byte) (map[string]any, error) {
|
||||
// Use Helm's built-in chartutil.ReadValues which properly handles the conversion
|
||||
// from map[interface{}]interface{} to map[string]interface{}
|
||||
return chartutil.ReadValues(data)
|
||||
}
|
||||
|
||||
// logf is a log helper function for Helm
|
||||
func (hspm *HelmSDKPackageManager) logf(format string, v ...any) {
|
||||
// Use zerolog for structured logging
|
||||
log.Debug().
|
||||
Str("context", "HelmClient").
|
||||
Msgf(format, v...)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue