devops/shuttles/terraform/terraform-provider-helm/helm/provider.go

702 lines
25 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package helm
import (
"context"
"fmt"
"net/url"
"os"
"path/filepath"
"strconv"
"strings"
"sync"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/provider"
"github.com/hashicorp/terraform-plugin-framework/provider/schema"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/cli"
"helm.sh/helm/v3/pkg/registry"
"helm.sh/helm/v3/pkg/storage/driver"
)
var _ provider.Provider = &HelmProvider{}
func New(version string) func() provider.Provider {
return func() provider.Provider {
return &HelmProvider{
version: version,
}
}
}
// Meta contains the client configuration for the provider
type Meta struct {
providerData *HelmProvider
Data *HelmProviderModel
Settings *cli.EnvSettings
RegistryClient *registry.Client
HelmDriver string
// Experimental feature toggles
Experiments map[string]bool
Mutex sync.Mutex
}
// HelmProviderModel contains the configuration for the provider
type HelmProviderModel struct {
Debug types.Bool `tfsdk:"debug"`
PluginsPath types.String `tfsdk:"plugins_path"`
RegistryConfigPath types.String `tfsdk:"registry_config_path"`
RepositoryConfigPath types.String `tfsdk:"repository_config_path"`
RepositoryCache types.String `tfsdk:"repository_cache"`
HelmDriver types.String `tfsdk:"helm_driver"`
BurstLimit types.Int64 `tfsdk:"burst_limit"`
Kubernetes types.Object `tfsdk:"kubernetes"`
Registries types.List `tfsdk:"registries"`
Experiments *ExperimentsConfigModel `tfsdk:"experiments"`
}
// ExperimentsConfigModel configures the experiments that are enabled or disabled
type ExperimentsConfigModel struct {
Manifest types.Bool `tfsdk:"manifest"`
}
// RegistryConfigModel configures an OCI registry
type RegistryConfigModel struct {
URL types.String `tfsdk:"url"`
Username types.String `tfsdk:"username"`
Password types.String `tfsdk:"password"`
}
// KubernetesConfigModel configures a Kubernetes client
type KubernetesConfigModel struct {
Host types.String `tfsdk:"host"`
Username types.String `tfsdk:"username"`
Password types.String `tfsdk:"password"`
Insecure types.Bool `tfsdk:"insecure"`
TLSServerName types.String `tfsdk:"tls_server_name"`
ClientCertificate types.String `tfsdk:"client_certificate"`
ClientKey types.String `tfsdk:"client_key"`
ClusterCACertificate types.String `tfsdk:"cluster_ca_certificate"`
ConfigPaths types.List `tfsdk:"config_paths"`
ConfigPath types.String `tfsdk:"config_path"`
ConfigContext types.String `tfsdk:"config_context"`
ConfigContextAuthInfo types.String `tfsdk:"config_context_auth_info"`
ConfigContextCluster types.String `tfsdk:"config_context_cluster"`
Token types.String `tfsdk:"token"`
ProxyURL types.String `tfsdk:"proxy_url"`
Exec *ExecConfigModel `tfsdk:"exec"`
}
// ExecConfigModel configures an external command to configure the Kubernetes client
type ExecConfigModel struct {
APIVersion types.String `tfsdk:"api_version"`
Command types.String `tfsdk:"command"`
Env types.Map `tfsdk:"env"`
Args types.List `tfsdk:"args"`
}
// HelmProvider is the top level provider struct
type HelmProvider struct {
meta *Meta
version string
}
func (p *HelmProvider) Metadata(ctx context.Context, req provider.MetadataRequest, resp *provider.MetadataResponse) {
resp.TypeName = "helm"
resp.Version = p.version
}
// /////////////////////// START OF SCHEMA CREATION ///////////////////////////////
// Defines attributes that are avaiable in the provider
func (p *HelmProvider) Schema(ctx context.Context, req provider.SchemaRequest, resp *provider.SchemaResponse) {
resp.Schema = schema.Schema{
Description: "Schema to define attributes that are available in the provider",
Attributes: map[string]schema.Attribute{
"debug": schema.BoolAttribute{
Description: "Debug indicates whether or not Helm is running in Debug mode.",
Optional: true,
},
"plugins_path": schema.StringAttribute{
Description: "The path to the helm plugins directory",
Optional: true,
},
"registry_config_path": schema.StringAttribute{
Description: "The path to the registry config file",
Optional: true,
},
"repository_config_path": schema.StringAttribute{
Description: "The path to the file containing repository names and URLs",
Optional: true,
},
"repository_cache": schema.StringAttribute{
Description: "The path to the file containing cached repository indexes",
Optional: true,
},
"helm_driver": schema.StringAttribute{
Description: "The backend storage driver. Values are: configmap, secret, memory, sql",
Optional: true,
Validators: []validator.String{
stringvalidator.OneOf(strings.ToLower(driver.MemoryDriverName),
strings.ToLower(driver.ConfigMapsDriverName),
strings.ToLower(driver.SecretsDriverName),
strings.ToLower(driver.SQLDriverName)),
},
},
"burst_limit": schema.Int64Attribute{
Optional: true,
Description: "Helm burst limit. Increase this if you have a cluster with many CRDs",
},
"kubernetes": schema.SingleNestedAttribute{
Optional: true,
Description: "Kubernetes Configuration",
Attributes: kubernetesResourceSchema(),
},
"registries": schema.ListNestedAttribute{
Optional: true,
Description: "RegistryClient configuration.",
NestedObject: schema.NestedAttributeObject{
Attributes: registriesResourceSchema(),
},
},
"experiments": schema.SingleNestedAttribute{
Optional: true,
Description: "Enable and disable experimental features.",
Attributes: experimentsSchema(),
},
},
}
}
func experimentsSchema() map[string]schema.Attribute {
return map[string]schema.Attribute{
"manifest": schema.BoolAttribute{
Optional: true,
Description: "Enable full diff by storing the rendered manifest in the state.",
},
}
}
func registriesResourceSchema() map[string]schema.Attribute {
return map[string]schema.Attribute{
"url": schema.StringAttribute{
Required: true,
Description: "OCI URL in form of oci://host:port or oci://host",
},
"username": schema.StringAttribute{
Required: true,
Description: "The username to use for the OCI HTTP basic authentication when accessing the Kubernetes master endpoint.",
},
"password": schema.StringAttribute{
Required: true,
Description: "The password to use for the OCI HTTP basic authentication when accessing the Kubernetes master endpoint.",
},
}
}
func kubernetesResourceSchema() map[string]schema.Attribute {
return map[string]schema.Attribute{
"host": schema.StringAttribute{
Optional: true,
Description: "The hostname (in form of URI) of kubernetes master",
},
"username": schema.StringAttribute{
Optional: true,
Description: "The username to use for HTTP basic authentication when accessing the Kubernetes master endpoint",
},
"password": schema.StringAttribute{
Optional: true,
Description: "The password to use for HTTP basic authentication when accessing the Kubernetes master endpoint.",
},
"insecure": schema.BoolAttribute{
Optional: true,
Description: "Whether server should be accessed without verifying the TLS certificate.",
},
"tls_server_name": schema.StringAttribute{
Optional: true,
Description: "Server name passed to the server for SNI and is used in the client to check server certificates against.",
},
"client_certificate": schema.StringAttribute{
Optional: true,
Description: "PEM-encoded client certificate for TLS authentication.",
},
"client_key": schema.StringAttribute{
Optional: true,
Description: "PEM-encoded client certificate key for TLS authentication.",
},
"cluster_ca_certificate": schema.StringAttribute{
Optional: true,
Description: "PEM-encoded root certificates bundle for TLS authentication.",
},
"config_paths": schema.ListAttribute{
Optional: true,
ElementType: types.StringType,
Description: "A list of paths to kube config files. Can be set with KUBE_CONFIG_PATHS environment variable.",
},
"config_path": schema.StringAttribute{
Optional: true,
Description: "Path to the kube config file. Can be set with KUBE_CONFIG_PATH.",
Validators: []validator.String{
stringvalidator.ConflictsWith(
path.Root("kubernetes").AtName("config_paths").Expression(),
),
},
},
"config_context": schema.StringAttribute{
Optional: true,
Description: "Context to choose from the config file. Can be sourced from KUBE_CTX.",
},
"config_context_auth_info": schema.StringAttribute{
Optional: true,
Description: "Authentication info context of the kube config (name of the kubeconfig user, --user flag in kubectl). Can be sourced from KUBE_CTX_AUTH_INFO.",
},
"config_context_cluster": schema.StringAttribute{
Optional: true,
Description: "Cluster context of the kube config (name of the kubeconfig cluster, --cluster flag in kubectl). Can be sourced from KUBE_CTX_CLUSTER.",
},
"token": schema.StringAttribute{
Optional: true,
Description: "Token to authenticate a service account.",
},
"proxy_url": schema.StringAttribute{
Optional: true,
Description: "URL to the proxy to be used for all API requests.",
},
"exec": schema.SingleNestedAttribute{
Optional: true,
Description: "Exec configuration for Kubernetes authentication",
Attributes: execSchema(),
},
}
}
func execSchema() map[string]schema.Attribute {
return map[string]schema.Attribute{
"api_version": schema.StringAttribute{
Required: true,
Description: "API version for the exec plugin.",
},
"command": schema.StringAttribute{
Required: true,
Description: "Command to run for Kubernetes exec plugin",
},
"env": schema.MapAttribute{
Optional: true,
ElementType: types.StringType,
Description: "Environment variables for the exec plugin",
},
"args": schema.ListAttribute{
Optional: true,
ElementType: types.StringType,
Description: "Arguments for the exec plugin",
},
}
}
func execSchemaAttrTypes() map[string]attr.Type {
return map[string]attr.Type{
"api_version": types.StringType,
"command": types.StringType,
"args": types.ListType{ElemType: types.StringType},
"env": types.MapType{ElemType: types.StringType},
}
}
///////////////////// END OF SCHEMA CREATION ///////////////////////////////
// Setting up the provider, anything we need to get the provider running, probbaly authentication. like the api
func (p *HelmProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) {
if req.ClientCapabilities.DeferralAllowed && !req.Config.Raw.IsFullyKnown() {
resp.Deferred = &provider.Deferred{
Reason: provider.DeferredReasonProviderConfigUnknown,
}
}
pluginsPath := os.Getenv("HELM_PLUGINS_PATH")
registryConfigPath := os.Getenv("HELM_REGISTRY_CONFIG_PATH")
repositoryConfigPath := os.Getenv("HELM_REPOSITORY_CONFIG_PATH")
repositoryCache := os.Getenv("HELM_REPOSITORY_CACHE")
helmDriver := os.Getenv("HELM_DRIVER")
burstLimitStr := os.Getenv("HELM_BURST_LIMIT")
kubeHost := os.Getenv("KUBE_HOST")
kubeUser := os.Getenv("KUBE_USER")
kubePassword := os.Getenv("KUBE_PASSWORD")
kubeInsecureStr := os.Getenv("KUBE_INSECURE")
kubeTLSServerName := os.Getenv("KUBE_TLS_SERVER_NAME")
kubeClientCert := os.Getenv("KUBE_CLIENT_CERT_DATA")
kubeClientKey := os.Getenv("KUBE_CLIENT_KEY_DATA")
kubeCaCert := os.Getenv("KUBE_CLUSTER_CA_CERT_DATA")
kubeConfigPaths := os.Getenv("KUBE_CONFIG_PATHS")
kubeConfigPath := os.Getenv("KUBE_CONFIG_PATH")
kubeConfigContext := os.Getenv("KUBE_CTX")
kubeConfigContextAuthInfo := os.Getenv("KUBE_CTX_AUTH_INFO")
kubeConfigContextCluster := os.Getenv("KUBE_CTX_CLUSTER")
kubeToken := os.Getenv("KUBE_TOKEN")
kubeProxy := os.Getenv("KUBE_PROXY")
// Initialize the HelmProviderModel with values from the config
var config HelmProviderModel
diags := req.Config.Get(ctx, &config)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
if !config.PluginsPath.IsNull() {
pluginsPath = config.PluginsPath.ValueString()
}
if !config.RegistryConfigPath.IsNull() {
registryConfigPath = config.RegistryConfigPath.ValueString()
}
if !config.RepositoryConfigPath.IsNull() {
repositoryConfigPath = config.RepositoryConfigPath.ValueString()
}
if !config.RepositoryCache.IsNull() {
repositoryCache = config.RepositoryCache.ValueString()
}
if !config.HelmDriver.IsNull() {
helmDriver = config.HelmDriver.ValueString()
}
var burstLimit int64
if burstLimitStr != "" {
var err error
burstLimit, err = strconv.ParseInt(burstLimitStr, 10, 64)
if err != nil {
resp.Diagnostics.AddError(
"Invalid burst limit",
fmt.Sprintf("Invalid burst limit value: %s", burstLimitStr),
)
return
}
}
if !config.BurstLimit.IsNull() {
burstLimit = config.BurstLimit.ValueInt64()
}
var kubeInsecure bool
if kubeInsecureStr != "" {
var err error
kubeInsecure, err = strconv.ParseBool(kubeInsecureStr)
if err != nil {
resp.Diagnostics.AddError(
"Invalid insecure value",
fmt.Sprintf("Invalid insecure value: %s", kubeInsecureStr),
)
return
}
}
var kubernetesConfig KubernetesConfigModel
if !config.Kubernetes.IsNull() && !config.Kubernetes.IsUnknown() {
diags := req.Config.GetAttribute(ctx, path.Root("kubernetes"), &kubernetesConfig)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
}
if !kubernetesConfig.Insecure.IsNull() {
kubeInsecure = kubernetesConfig.Insecure.ValueBool()
}
var kubeConfigPathsList []attr.Value
if !kubernetesConfig.Host.IsNull() {
kubeHost = kubernetesConfig.Host.ValueString()
}
if !kubernetesConfig.Username.IsNull() {
kubeUser = kubernetesConfig.Username.ValueString()
}
if !kubernetesConfig.Password.IsNull() {
kubePassword = kubernetesConfig.Password.ValueString()
}
if !kubernetesConfig.TLSServerName.IsNull() {
kubeTLSServerName = kubernetesConfig.TLSServerName.ValueString()
}
if !kubernetesConfig.ClientCertificate.IsNull() {
kubeClientCert = kubernetesConfig.ClientCertificate.ValueString()
}
if !kubernetesConfig.ClientKey.IsNull() {
kubeClientKey = kubernetesConfig.ClientKey.ValueString()
}
if !kubernetesConfig.ClusterCACertificate.IsNull() {
kubeCaCert = kubernetesConfig.ClusterCACertificate.ValueString()
}
if kubeConfigPaths != "" {
for _, path := range filepath.SplitList(kubeConfigPaths) {
kubeConfigPathsList = append(kubeConfigPathsList, types.StringValue(path))
}
}
if !kubernetesConfig.ConfigPaths.IsNull() {
var paths []string
diags = kubernetesConfig.ConfigPaths.ElementsAs(ctx, &paths, false)
resp.Diagnostics.Append(diags...)
for _, path := range paths {
kubeConfigPathsList = append(kubeConfigPathsList, types.StringValue(path))
}
}
if !kubernetesConfig.ConfigPath.IsNull() {
kubeConfigPath = kubernetesConfig.ConfigPath.ValueString()
}
if !kubernetesConfig.ConfigContext.IsNull() {
kubeConfigContext = kubernetesConfig.ConfigContext.ValueString()
}
if !kubernetesConfig.ConfigContextAuthInfo.IsNull() {
kubeConfigContextAuthInfo = kubernetesConfig.ConfigContextAuthInfo.ValueString()
}
if !kubernetesConfig.ConfigContextCluster.IsNull() {
kubeConfigContextCluster = kubernetesConfig.ConfigContextCluster.ValueString()
}
if !kubernetesConfig.Token.IsNull() {
kubeToken = kubernetesConfig.Token.ValueString()
}
if !kubernetesConfig.ProxyURL.IsNull() {
kubeProxy = kubernetesConfig.ProxyURL.ValueString()
}
tflog.Debug(ctx, "Config values after overrides", map[string]interface{}{
"config": config,
})
debug := os.Getenv("HELM_DEBUG") == "true" || config.Debug.ValueBool()
settings := cli.New()
settings.Debug = debug
if pluginsPath != "" {
settings.PluginsDirectory = pluginsPath
}
if registryConfigPath != "" {
settings.RegistryConfig = registryConfigPath
}
if repositoryConfigPath != "" {
settings.RepositoryConfig = repositoryConfigPath
}
if repositoryCache != "" {
settings.RepositoryCache = repositoryCache
}
tflog.Debug(ctx, "Helm settings initialized", map[string]interface{}{
"settings": settings,
})
kubeConfigPathsListValue, diags := types.ListValue(types.StringType, kubeConfigPathsList)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
manifestExperiment := false
if config.Experiments != nil {
manifestExperiment = config.Experiments.Manifest.ValueBool()
}
var execAttrValue attr.Value = types.ObjectNull(execSchemaAttrTypes())
if kubernetesConfig.Exec != nil {
// Check if `api_version` and `command` are set (since they're required fields)
if !kubernetesConfig.Exec.APIVersion.IsNull() && !kubernetesConfig.Exec.Command.IsNull() {
execAttrValue = types.ObjectValueMust(execSchemaAttrTypes(), map[string]attr.Value{
"api_version": types.StringValue(kubernetesConfig.Exec.APIVersion.ValueString()),
"command": types.StringValue(kubernetesConfig.Exec.Command.ValueString()),
"args": types.ListValueMust(types.StringType, kubernetesConfig.Exec.Args.Elements()),
"env": types.MapValueMust(types.StringType, kubernetesConfig.Exec.Env.Elements()),
})
}
}
kubernetesConfigObjectValue, diags := types.ObjectValue(map[string]attr.Type{
"host": types.StringType,
"username": types.StringType,
"password": types.StringType,
"insecure": types.BoolType,
"tls_server_name": types.StringType,
"client_certificate": types.StringType,
"client_key": types.StringType,
"cluster_ca_certificate": types.StringType,
"config_paths": types.ListType{ElemType: types.StringType},
"config_path": types.StringType,
"config_context": types.StringType,
"config_context_auth_info": types.StringType,
"config_context_cluster": types.StringType,
"token": types.StringType,
"proxy_url": types.StringType,
"exec": types.ObjectType{AttrTypes: execSchemaAttrTypes()},
}, map[string]attr.Value{
"host": types.StringValue(kubeHost),
"username": types.StringValue(kubeUser),
"password": types.StringValue(kubePassword),
"insecure": types.BoolValue(kubeInsecure),
"tls_server_name": types.StringValue(kubeTLSServerName),
"client_certificate": types.StringValue(kubeClientCert),
"client_key": types.StringValue(kubeClientKey),
"cluster_ca_certificate": types.StringValue(kubeCaCert),
"config_paths": kubeConfigPathsListValue,
"config_path": types.StringValue(kubeConfigPath),
"config_context": types.StringValue(kubeConfigContext),
"config_context_auth_info": types.StringValue(kubeConfigContextAuthInfo),
"config_context_cluster": types.StringValue(kubeConfigContextCluster),
"token": types.StringValue(kubeToken),
"proxy_url": types.StringValue(kubeProxy),
"exec": execAttrValue,
})
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
meta := &Meta{
Data: &HelmProviderModel{
Debug: types.BoolValue(debug),
PluginsPath: types.StringValue(pluginsPath),
RegistryConfigPath: types.StringValue(registryConfigPath),
RepositoryConfigPath: types.StringValue(repositoryConfigPath),
RepositoryCache: types.StringValue(repositoryCache),
HelmDriver: types.StringValue(helmDriver),
BurstLimit: types.Int64Value(burstLimit),
Kubernetes: kubernetesConfigObjectValue,
Experiments: &ExperimentsConfigModel{
Manifest: types.BoolValue(manifestExperiment),
},
},
Settings: settings,
HelmDriver: helmDriver,
Experiments: map[string]bool{
"manifest": manifestExperiment,
},
}
registryClient, err := registry.NewClient()
if err != nil {
resp.Diagnostics.AddError(
"Registry client initialization failed",
fmt.Sprintf("Unable to create Helm registry client: %s", err),
)
return
}
meta.RegistryClient = registryClient
if !config.Registries.IsUnknown() {
var registryConfigs []RegistryConfigModel
diags := config.Registries.ElementsAs(ctx, &registryConfigs, false)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
for _, r := range registryConfigs {
if r.URL.IsNull() || r.Username.IsNull() || r.Password.IsNull() {
resp.Diagnostics.AddError(
"OCI Registry login failed",
"Registry URL, Username, or Password is null",
)
return
}
err := OCIRegistryPerformLogin(ctx, meta, meta.RegistryClient, r.URL.ValueString(), r.Username.ValueString(), r.Password.ValueString())
if err != nil {
resp.Diagnostics.AddError(
"OCI Registry login failed",
err.Error(),
)
return
}
}
} else {
tflog.Debug(ctx, "No registry configurations found")
}
resp.DataSourceData = meta
resp.ResourceData = meta
tflog.Debug(ctx, "Configure method completed successfully")
}
func (p *HelmProvider) DataSources(ctx context.Context) []func() datasource.DataSource {
return []func() datasource.DataSource{
NewHelmTemplate,
}
}
func (p *HelmProvider) Resources(ctx context.Context) []func() resource.Resource {
return []func() resource.Resource{
NewHelmRelease,
}
}
func OCIRegistryLogin(ctx context.Context, meta *Meta, actionConfig *action.Configuration, registryClient *registry.Client, repository, chartName, username, password string) diag.Diagnostics {
var diags diag.Diagnostics
actionConfig.RegistryClient = registryClient
var ociURL string
if registry.IsOCI(repository) {
ociURL = repository
} else if registry.IsOCI(chartName) {
ociURL = chartName
}
if ociURL == "" {
return diags
}
if username != "" && password != "" {
err := OCIRegistryPerformLogin(ctx, meta, registryClient, ociURL, username, password)
if err != nil {
diags.AddError(
"OCI Registry Login Failed",
fmt.Sprintf("Failed to log in to OCI registry %q: %s", ociURL, err.Error()),
)
}
}
return diags
}
// registryClient = client used to comm with the registry, oci urls, un, and pw used for authentication
func OCIRegistryPerformLogin(ctx context.Context, meta *Meta, registryClient *registry.Client, ociURL, username, password string) error {
loggedInOCIRegistries := make(map[string]string)
// getting the oci url, and extracting the host.
u, err := url.Parse(ociURL)
if err != nil {
return fmt.Errorf("could not parse OCI registry URL: %v", err)
}
meta.Mutex.Lock()
defer meta.Mutex.Unlock()
if _, ok := loggedInOCIRegistries[u.Host]; ok {
tflog.Info(ctx, fmt.Sprintf("Already logged into OCI registry %q", u.Host))
return nil
}
// Now we perform the login, with the provided username and password by calling the login method
err = registryClient.Login(u.Host, registry.LoginOptBasicAuth(username, password))
if err != nil {
return fmt.Errorf("could not login to OCI registry %q: %v", u.Host, err)
}
loggedInOCIRegistries[u.Host] = ""
tflog.Info(ctx, fmt.Sprintf("Logged into OCI registry %q", u.Host))
return nil
}
// GetHelmConfiguration retrieves the Helm configuration for a given namespace
func (m *Meta) GetHelmConfiguration(ctx context.Context, namespace string) (*action.Configuration, error) {
if m == nil {
tflog.Error(ctx, "Meta is nil")
return nil, fmt.Errorf("Meta is nil")
}
tflog.Info(context.Background(), "[INFO] GetHelmConfiguration start")
actionConfig := new(action.Configuration)
kc, err := m.NewKubeConfig(ctx, namespace)
if err != nil {
return nil, err
}
if err := actionConfig.Init(kc, namespace, m.HelmDriver, func(format string, v ...interface{}) {
tflog.Info(context.Background(), fmt.Sprintf(format, v...))
}); err != nil {
return nil, err
}
tflog.Info(context.Background(), "[INFO] GetHelmConfiguration success")
// returning the initializing action.Configuration object
return actionConfig, nil
}