166 lines
5.0 KiB
Go
166 lines
5.0 KiB
Go
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...)
|
|
}
|