pkg/deploy/helm/chart_extender/bundle.go

Summary

Maintainability
A
1 hr
Test Coverage
F
42%
package chart_extender

import (
    "context"
    "encoding/json"
    "fmt"
    "io/ioutil"
    "os"
    "path/filepath"
    "text/template"

    "helm.sh/helm/v3/pkg/chart"
    "helm.sh/helm/v3/pkg/chart/loader"
    "helm.sh/helm/v3/pkg/cli"
    "helm.sh/helm/v3/pkg/postrender"
    "helm.sh/helm/v3/pkg/registry"

    "github.com/werf/logboek"
    "github.com/werf/werf/v2/pkg/deploy/helm"
    "github.com/werf/werf/v2/pkg/deploy/helm/chart_extender/helpers"
    "github.com/werf/werf/v2/pkg/deploy/helm/chart_extender/helpers/secrets"
    "github.com/werf/werf/v2/pkg/deploy/helm/command_helpers"
    "github.com/werf/werf/v2/pkg/deploy/secrets_manager"
)

type BundleOptions struct {
    SecretValueFiles                  []string
    BuildChartDependenciesOpts        command_helpers.BuildChartDependenciesOptions
    ExtraAnnotations                  map[string]string
    ExtraLabels                       map[string]string
    IgnoreInvalidAnnotationsAndLabels bool
    DisableDefaultValues              bool
}

func NewBundle(ctx context.Context, dir string, helmEnvSettings *cli.EnvSettings, registryClient *registry.Client, secretsManager *secrets_manager.SecretsManager, opts BundleOptions) (*Bundle, error) {
    bundle := &Bundle{
        Dir:                            dir,
        SecretValueFiles:               opts.SecretValueFiles,
        HelmEnvSettings:                helmEnvSettings,
        RegistryClient:                 registryClient,
        BuildChartDependenciesOpts:     opts.BuildChartDependenciesOpts,
        ChartExtenderServiceValuesData: helpers.NewChartExtenderServiceValuesData(),
        ChartExtenderContextData:       helpers.NewChartExtenderContextData(ctx),
        secretsManager:                 secretsManager,
        DisableDefaultValues:           opts.DisableDefaultValues,
    }

    extraAnnotationsAndLabelsPostRenderer := helm.NewExtraAnnotationsAndLabelsPostRenderer(nil, nil, opts.IgnoreInvalidAnnotationsAndLabels)

    if dataMap, err := readBundleJsonMap(filepath.Join(bundle.Dir, "extra_annotations.json")); err != nil {
        return nil, err
    } else {
        extraAnnotationsAndLabelsPostRenderer.Add(dataMap, nil)
    }

    if dataMap, err := readBundleJsonMap(filepath.Join(bundle.Dir, "extra_labels.json")); err != nil {
        return nil, err
    } else {
        extraAnnotationsAndLabelsPostRenderer.Add(nil, dataMap)
    }

    extraAnnotationsAndLabelsPostRenderer.Add(opts.ExtraAnnotations, opts.ExtraLabels)

    bundle.ExtraAnnotationsAndLabelsPostRenderer = extraAnnotationsAndLabelsPostRenderer

    return bundle, nil
}

/*
 * Bundle object is chart.ChartExtender compatible object
 * which could be used during helm install/upgrade process
 */
type Bundle struct {
    Dir                                   string
    SecretValueFiles                      []string
    HelmChart                             *chart.Chart
    HelmEnvSettings                       *cli.EnvSettings
    RegistryClient                        *registry.Client
    BuildChartDependenciesOpts            command_helpers.BuildChartDependenciesOptions
    DisableDefaultValues                  bool
    ExtraAnnotationsAndLabelsPostRenderer *helm.ExtraAnnotationsAndLabelsPostRenderer

    secretsManager *secrets_manager.SecretsManager

    *secrets.SecretsRuntimeData
    *helpers.ChartExtenderServiceValuesData
    *helpers.ChartExtenderContextData
    *helpers.ChartExtenderValuesMerger
}

func (bundle *Bundle) ChainPostRenderer(postRenderer postrender.PostRenderer) postrender.PostRenderer {
    var chain []postrender.PostRenderer

    if postRenderer != nil {
        chain = append(chain, postRenderer)
    }

    chain = append(chain, bundle.ExtraAnnotationsAndLabelsPostRenderer)

    return helm.NewPostRendererChain(chain...)
}

// ChartCreated method for the chart.Extender interface
func (bundle *Bundle) ChartCreated(c *chart.Chart) error {
    bundle.HelmChart = c
    bundle.SecretsRuntimeData = secrets.NewSecretsRuntimeData()
    return nil
}

// ChartLoaded method for the chart.Extender interface
func (bundle *Bundle) ChartLoaded(files []*chart.ChartExtenderBufferedFile) error {
    if bundle.secretsManager != nil {
        wd, err := os.Getwd()
        if err != nil {
            return fmt.Errorf("unable to get current working dir: %w", err)
        }

        if err := bundle.SecretsRuntimeData.DecodeAndLoadSecrets(bundle.ChartExtenderContext, files, bundle.Dir, wd, bundle.secretsManager, secrets.DecodeAndLoadSecretsOptions{
            LoadFromLocalFilesystem:    true,
            CustomSecretValueFiles:     bundle.SecretValueFiles,
            WithoutDefaultSecretValues: false,
        }); err != nil {
            return fmt.Errorf("error decoding secrets: %w", err)
        }
    }

    if bundle.DisableDefaultValues {
        logboek.Context(bundle.ChartExtenderContext).Info().LogF("Disable default werf chart values\n")
        bundle.HelmChart.Values = nil
    }

    return nil
}

// ChartDependenciesLoaded method for the chart.Extender interface
func (bundle *Bundle) ChartDependenciesLoaded() error {
    return nil
}

// MakeValues method for the chart.Extender interface
func (bundle *Bundle) MakeValues(inputVals map[string]interface{}) (map[string]interface{}, error) {
    return bundle.MergeValues(bundle.ChartExtenderContext, inputVals, bundle.ServiceValues, bundle.SecretsRuntimeData)
}

// SetupTemplateFuncs method for the chart.Extender interface
func (bundle *Bundle) SetupTemplateFuncs(t *template.Template, funcMap template.FuncMap) {
    helpers.SetupIncludeWrapperFuncs(funcMap)
}

func convertBufferedFilesForChartExtender(files []*loader.BufferedFile) []*chart.ChartExtenderBufferedFile {
    var res []*chart.ChartExtenderBufferedFile
    for _, f := range files {
        f1 := new(chart.ChartExtenderBufferedFile)
        *f1 = chart.ChartExtenderBufferedFile(*f)
        res = append(res, f1)
    }
    return res
}

// LoadDir method for the chart.Extender interface
func (bundle *Bundle) LoadDir(dir string) (bool, []*chart.ChartExtenderBufferedFile, error) {
    files, err := loader.GetFilesFromLocalFilesystem(dir)
    if err != nil {
        return true, nil, err
    }

    res, err := LoadChartDependencies(bundle.ChartExtenderContext, func(ctx context.Context, dir string) ([]*chart.ChartExtenderBufferedFile, error) {
        files, err := loader.GetFilesFromLocalFilesystem(dir)
        if err != nil {
            return nil, err
        }
        return convertBufferedFilesForChartExtender(files), nil
    }, dir, convertBufferedFilesForChartExtender(files), bundle.HelmEnvSettings, bundle.RegistryClient, bundle.BuildChartDependenciesOpts)
    return true, res, err
}

// LocateChart method for the chart.Extender interface
func (bundle *Bundle) LocateChart(name string, settings *cli.EnvSettings) (bool, string, error) {
    return false, "", nil
}

// ReadFile method for the chart.Extender interface
func (bundle *Bundle) ReadFile(filePath string) (bool, []byte, error) {
    return false, nil, nil
}

func writeBundleJsonMap(dataMap map[string]string, path string) error {
    if data, err := json.Marshal(dataMap); err != nil {
        return fmt.Errorf("unable to prepare %q data: %w", path, err)
    } else if err := ioutil.WriteFile(path, append(data, []byte("\n")...), os.ModePerm); err != nil {
        return fmt.Errorf("unable to write %q: %w", path, err)
    } else {
        return nil
    }
}

func readBundleJsonMap(path string) (map[string]string, error) {
    var res map[string]string
    if _, err := os.Stat(path); os.IsNotExist(err) {
        return nil, nil
    } else if err != nil {
        return nil, fmt.Errorf("error accessing %q: %w", path, err)
    } else if data, err := ioutil.ReadFile(path); err != nil {
        return nil, fmt.Errorf("error reading %q: %w", path, err)
    } else if err := json.Unmarshal(data, &res); err != nil {
        return nil, fmt.Errorf("error unmarshalling json from %q: %w", path, err)
    } else {
        return res, nil
    }
}