mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-05-17 18:25:26 +00:00
Add flag --enable-helm
This commit is contained in:
@@ -6,7 +6,6 @@ package builtins
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
@@ -16,7 +15,6 @@ import (
|
||||
|
||||
"github.com/imdario/mergo"
|
||||
"github.com/pkg/errors"
|
||||
"sigs.k8s.io/kustomize/api/filesys"
|
||||
"sigs.k8s.io/kustomize/api/resmap"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/yaml"
|
||||
@@ -25,201 +23,234 @@ import (
|
||||
// HelmChartInflationGeneratorPlugin is a plugin to generate resources
|
||||
// from a remote or local helm chart.
|
||||
type HelmChartInflationGeneratorPlugin struct {
|
||||
h *resmap.PluginHelpers
|
||||
types.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
|
||||
runHelmCommand func([]string) ([]byte, error)
|
||||
types.HelmChartArgs
|
||||
h *resmap.PluginHelpers
|
||||
types.HelmGlobals
|
||||
types.HelmChart
|
||||
tmpDir string
|
||||
}
|
||||
|
||||
var KustomizePlugin HelmChartInflationGeneratorPlugin
|
||||
|
||||
const (
|
||||
valuesMergeOptionMerge = "merge"
|
||||
valuesMergeOptionOverride = "override"
|
||||
valuesMergeOptionReplace = "replace"
|
||||
)
|
||||
|
||||
var legalMergeOptions = []string{
|
||||
valuesMergeOptionMerge,
|
||||
valuesMergeOptionOverride,
|
||||
valuesMergeOptionReplace,
|
||||
}
|
||||
|
||||
// Config uses the input plugin configurations `config` to setup the generator
|
||||
// options
|
||||
func (p *HelmChartInflationGeneratorPlugin) Config(h *resmap.PluginHelpers, config []byte) error {
|
||||
func (p *HelmChartInflationGeneratorPlugin) Config(
|
||||
h *resmap.PluginHelpers, config []byte) (err error) {
|
||||
if h.GeneralConfig() == nil {
|
||||
return fmt.Errorf("unable to access general config")
|
||||
}
|
||||
if !h.GeneralConfig().HelmConfig.Enabled {
|
||||
return fmt.Errorf("must specify --enable-helm")
|
||||
}
|
||||
if h.GeneralConfig().HelmConfig.Command == "" {
|
||||
return fmt.Errorf("must specify --helm-command")
|
||||
}
|
||||
p.h = h
|
||||
err := yaml.Unmarshal(config, p)
|
||||
if err != nil {
|
||||
return err
|
||||
if err = yaml.Unmarshal(config, p); err != nil {
|
||||
return
|
||||
}
|
||||
tmpDir, err := filesys.NewTmpConfirmedDir()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.tmpDir = string(tmpDir)
|
||||
if p.ChartName == "" {
|
||||
return fmt.Errorf("chartName cannot be empty")
|
||||
}
|
||||
if p.ChartHome == "" {
|
||||
p.ChartHome = filepath.Join(p.tmpDir, "chart")
|
||||
}
|
||||
if p.ChartRepoName == "" {
|
||||
p.ChartRepoName = "stable"
|
||||
}
|
||||
if p.HelmBin == "" {
|
||||
p.HelmBin = "helm"
|
||||
}
|
||||
if p.HelmHome == "" {
|
||||
p.HelmHome = filepath.Join(p.tmpDir, ".helm")
|
||||
}
|
||||
if p.Values == "" {
|
||||
p.Values = filepath.Join(p.ChartHome, p.ChartName, "values.yaml")
|
||||
}
|
||||
if p.ValuesMerge == "" {
|
||||
p.ValuesMerge = "override"
|
||||
}
|
||||
// runHelmCommand will run `helm` command with args provided. Return stdout
|
||||
// and error if there is any.
|
||||
p.runHelmCommand = func(args []string) ([]byte, error) {
|
||||
stdout := new(bytes.Buffer)
|
||||
stderr := new(bytes.Buffer)
|
||||
cmd := exec.Command(p.HelmBin, args...)
|
||||
cmd.Stdout = stdout
|
||||
cmd.Stderr = stderr
|
||||
cmd.Env = append(os.Environ(),
|
||||
fmt.Sprintf("HELM_CONFIG_HOME=%s", p.HelmHome),
|
||||
fmt.Sprintf("HELM_CACHE_HOME=%s/.cache", p.HelmHome),
|
||||
fmt.Sprintf("HELM_DATA_HOME=%s/.data", p.HelmHome),
|
||||
)
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return stdout.Bytes(),
|
||||
errors.Wrap(
|
||||
fmt.Errorf("failed to run command %s %s", p.HelmBin, strings.Join(args, " ")),
|
||||
stderr.String(),
|
||||
)
|
||||
}
|
||||
return stdout.Bytes(), nil
|
||||
}
|
||||
return nil
|
||||
return p.validateArgs()
|
||||
}
|
||||
|
||||
// EncodeValues for writing
|
||||
func (p *HelmChartInflationGeneratorPlugin) EncodeValues(w io.Writer) error {
|
||||
d, err := yaml.Marshal(p.ValuesLocal)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(d)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// useValuesLocal process (merge) inflator config provided values with chart default values.yaml
|
||||
func (p *HelmChartInflationGeneratorPlugin) useValuesLocal() error {
|
||||
// not override, merge, none
|
||||
if !(p.ValuesMerge == "none" || p.ValuesMerge == "no" || p.ValuesMerge == "false") {
|
||||
var pValues []byte
|
||||
var err error
|
||||
|
||||
if filepath.IsAbs(p.Values) {
|
||||
pValues, err = ioutil.ReadFile(p.Values)
|
||||
} else {
|
||||
pValues, err = p.h.Loader().Load(p.Values)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
chValues := make(map[string]interface{})
|
||||
err = yaml.Unmarshal(pValues, &chValues)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if p.ValuesMerge == "override" {
|
||||
err = mergo.Merge(&chValues, p.ValuesLocal, mergo.WithOverride)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if p.ValuesMerge == "merge" {
|
||||
err = mergo.Merge(&chValues, p.ValuesLocal)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
p.ValuesLocal = chValues
|
||||
}
|
||||
b, err := yaml.Marshal(p.ValuesLocal)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
path, err := p.writeValuesBytes(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.Values = path
|
||||
return nil
|
||||
}
|
||||
|
||||
// copyValues will copy the relative values file into the temp directory
|
||||
// to avoid messing up with CWD.
|
||||
func (p *HelmChartInflationGeneratorPlugin) copyValues() error {
|
||||
// only copy when the values path is not absolute
|
||||
if filepath.IsAbs(p.Values) {
|
||||
// This uses the real file system since tmpDir may be used
|
||||
// by the helm subprocess. Cannot use a chroot jail or fake
|
||||
// filesystem since we allow the user to use previously
|
||||
// downloaded charts. This is safe since this plugin is
|
||||
// owned by kustomize.
|
||||
func (p *HelmChartInflationGeneratorPlugin) establishTmpDir() (err error) {
|
||||
if p.tmpDir != "" {
|
||||
// already done.
|
||||
return nil
|
||||
}
|
||||
// we must use use loader to read values file
|
||||
b, err := p.h.Loader().Load(p.Values)
|
||||
if err != nil {
|
||||
p.tmpDir, err = ioutil.TempDir("", "kustomize-helm-")
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *HelmChartInflationGeneratorPlugin) validateArgs() (err error) {
|
||||
if p.Name == "" {
|
||||
return fmt.Errorf("chart name cannot be empty")
|
||||
}
|
||||
|
||||
// ChartHome might be consulted by the plugin (to read
|
||||
// values files below it), so it must be located under
|
||||
// the loader root (unless root restrictions are
|
||||
// disabled, in which case this can be an absolute path).
|
||||
if p.ChartHome == "" {
|
||||
p.ChartHome = "charts"
|
||||
}
|
||||
|
||||
// The ValuesFile may be consulted by the plugin, so it must
|
||||
// be under the loader root (unless root restrictions are
|
||||
// disabled).
|
||||
if p.ValuesFile == "" {
|
||||
p.ValuesFile = filepath.Join(p.ChartHome, p.Name, "values.yaml")
|
||||
}
|
||||
|
||||
if err = p.errIfIllegalValuesMerge(); err != nil {
|
||||
return err
|
||||
}
|
||||
path, err := p.writeValuesBytes(b)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
// ConfigHome is not loaded by the plugin, and can be located anywhere.
|
||||
if p.ConfigHome == "" {
|
||||
if err = p.establishTmpDir(); err != nil {
|
||||
return errors.Wrap(
|
||||
err, "unable to create tmp dir for HELM_CONFIG_HOME")
|
||||
}
|
||||
p.ConfigHome = filepath.Join(p.tmpDir, "helm")
|
||||
}
|
||||
p.Values = path
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *HelmChartInflationGeneratorPlugin) writeValuesBytes(b []byte) (string, error) {
|
||||
path := filepath.Join(p.ChartHome, p.ChartName, "kustomize-values.yaml")
|
||||
err := ioutil.WriteFile(path, b, 0644)
|
||||
func (p *HelmChartInflationGeneratorPlugin) errIfIllegalValuesMerge() error {
|
||||
if p.ValuesMerge == "" {
|
||||
// Use the default.
|
||||
p.ValuesMerge = valuesMergeOptionOverride
|
||||
return nil
|
||||
}
|
||||
for _, opt := range legalMergeOptions {
|
||||
if p.ValuesMerge == opt {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("valuesMerge must be one of %v", legalMergeOptions)
|
||||
}
|
||||
|
||||
func (p *HelmChartInflationGeneratorPlugin) absChartHome() string {
|
||||
if filepath.IsAbs(p.ChartHome) {
|
||||
return p.ChartHome
|
||||
}
|
||||
return filepath.Join(p.h.Loader().Root(), p.ChartHome)
|
||||
}
|
||||
|
||||
func (p *HelmChartInflationGeneratorPlugin) runHelmCommand(
|
||||
args []string) ([]byte, error) {
|
||||
stdout := new(bytes.Buffer)
|
||||
stderr := new(bytes.Buffer)
|
||||
cmd := exec.Command(p.h.GeneralConfig().HelmConfig.Command, args...)
|
||||
cmd.Stdout = stdout
|
||||
cmd.Stderr = stderr
|
||||
env := []string{
|
||||
fmt.Sprintf("HELM_CONFIG_HOME=%s", p.ConfigHome),
|
||||
fmt.Sprintf("HELM_CACHE_HOME=%s/.cache", p.ConfigHome),
|
||||
fmt.Sprintf("HELM_DATA_HOME=%s/.data", p.ConfigHome)}
|
||||
cmd.Env = append(os.Environ(), env...)
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
helm := p.h.GeneralConfig().HelmConfig.Command
|
||||
err = errors.Wrap(
|
||||
fmt.Errorf(
|
||||
"unable to run: '%s %s' with env=%s (is '%s' installed?)",
|
||||
helm, strings.Join(args, " "), env, helm),
|
||||
stderr.String(),
|
||||
)
|
||||
}
|
||||
return stdout.Bytes(), err
|
||||
}
|
||||
|
||||
// createNewMergedValuesFile replaces/merges original values file with ValuesInline.
|
||||
func (p *HelmChartInflationGeneratorPlugin) createNewMergedValuesFile() (
|
||||
path string, err error) {
|
||||
if p.ValuesMerge == valuesMergeOptionMerge ||
|
||||
p.ValuesMerge == valuesMergeOptionOverride {
|
||||
if err = p.replaceValuesInline(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
var b []byte
|
||||
b, err = yaml.Marshal(p.ValuesInline)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return path, nil
|
||||
return p.writeValuesBytes(b)
|
||||
}
|
||||
|
||||
func (p *HelmChartInflationGeneratorPlugin) replaceValuesInline() error {
|
||||
pValues, err := p.h.Loader().Load(p.ValuesFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
chValues := make(map[string]interface{})
|
||||
if err = yaml.Unmarshal(pValues, &chValues); err != nil {
|
||||
return err
|
||||
}
|
||||
switch p.ValuesMerge {
|
||||
case valuesMergeOptionOverride:
|
||||
err = mergo.Merge(
|
||||
&chValues, p.ValuesInline, mergo.WithOverride)
|
||||
case valuesMergeOptionMerge:
|
||||
err = mergo.Merge(&chValues, p.ValuesInline)
|
||||
}
|
||||
p.ValuesInline = chValues
|
||||
return err
|
||||
}
|
||||
|
||||
// copyValuesFile to avoid branching. TODO: get rid of this.
|
||||
func (p *HelmChartInflationGeneratorPlugin) copyValuesFile() (string, error) {
|
||||
b, err := p.h.Loader().Load(p.ValuesFile)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return p.writeValuesBytes(b)
|
||||
}
|
||||
|
||||
// Write a absolute path file in the tmp file system.
|
||||
func (p *HelmChartInflationGeneratorPlugin) writeValuesBytes(
|
||||
b []byte) (string, error) {
|
||||
if err := p.establishTmpDir(); err != nil {
|
||||
return "", fmt.Errorf("cannot create tmp dir to write helm values")
|
||||
}
|
||||
path := filepath.Join(p.tmpDir, p.Name+"-kustomize-values.yaml")
|
||||
return path, ioutil.WriteFile(path, b, 0644)
|
||||
}
|
||||
|
||||
func (p *HelmChartInflationGeneratorPlugin) cleanup() {
|
||||
if p.tmpDir != "" {
|
||||
os.RemoveAll(p.tmpDir)
|
||||
}
|
||||
}
|
||||
|
||||
// Generate implements generator
|
||||
func (p *HelmChartInflationGeneratorPlugin) Generate() (resmap.ResMap, error) {
|
||||
// cleanup
|
||||
defer os.RemoveAll(p.tmpDir)
|
||||
// check helm version. we only support V3
|
||||
err := p.checkHelmVersion()
|
||||
if err != nil {
|
||||
func (p *HelmChartInflationGeneratorPlugin) Generate() (rm resmap.ResMap, err error) {
|
||||
defer p.cleanup()
|
||||
if err = p.checkHelmVersion(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// pull the chart
|
||||
if !p.checkLocalChart() {
|
||||
_, err := p.runHelmCommand(p.getPullCommandArgs())
|
||||
if err != nil {
|
||||
if path, exists := p.chartExistsLocally(); !exists {
|
||||
if p.Repo == "" {
|
||||
return nil, fmt.Errorf(
|
||||
"no repo specified for pull, no chart found at '%s'", path)
|
||||
}
|
||||
if _, err := p.runHelmCommand(p.pullCommand()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// inflator config valuesLocal
|
||||
if len(p.ValuesLocal) > 0 {
|
||||
err := p.useValuesLocal()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(p.ValuesInline) > 0 {
|
||||
p.ValuesFile, err = p.createNewMergedValuesFile()
|
||||
} else {
|
||||
err := p.copyValues()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.ValuesFile, err = p.copyValuesFile()
|
||||
}
|
||||
|
||||
// render the charts
|
||||
stdout, err := p.runHelmCommand(p.getTemplateCommandArgs())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var stdout []byte
|
||||
stdout, err = p.runHelmCommand(p.templateCommand())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rm, rmfErr := p.h.ResmapFactory().NewResMapFromBytes(stdout)
|
||||
if rmfErr == nil {
|
||||
rm, err = p.h.ResmapFactory().NewResMapFromBytes(stdout)
|
||||
if err == nil {
|
||||
return rm, nil
|
||||
}
|
||||
// try to remove the contents before first "---" because
|
||||
@@ -228,50 +259,49 @@ func (p *HelmChartInflationGeneratorPlugin) Generate() (resmap.ResMap, error) {
|
||||
if idx := strings.Index(stdoutStr, "---"); idx != -1 {
|
||||
return p.h.ResmapFactory().NewResMapFromBytes([]byte(stdoutStr[idx:]))
|
||||
}
|
||||
return nil, rmfErr
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (p *HelmChartInflationGeneratorPlugin) getTemplateCommandArgs() []string {
|
||||
func (p *HelmChartInflationGeneratorPlugin) templateCommand() []string {
|
||||
args := []string{"template"}
|
||||
if p.ReleaseName != "" {
|
||||
args = append(args, p.ReleaseName)
|
||||
}
|
||||
args = append(args, filepath.Join(p.ChartHome, p.ChartName))
|
||||
if p.ReleaseNamespace != "" {
|
||||
args = append(args, "--namespace", p.ReleaseNamespace)
|
||||
args = append(args, filepath.Join(p.absChartHome(), p.Name))
|
||||
if p.ValuesFile != "" {
|
||||
args = append(args, "--values", p.ValuesFile)
|
||||
}
|
||||
if p.Values != "" {
|
||||
args = append(args, "--values", p.Values)
|
||||
if p.ReleaseName == "" {
|
||||
// AFAICT, this doesn't work as intended due to a bug in helm.
|
||||
// See https://github.com/helm/helm/issues/6019
|
||||
// I've tried placing the flag before and after the name argument.
|
||||
args = append(args, "--generate-name")
|
||||
}
|
||||
args = append(args, p.ExtraArgs...)
|
||||
return args
|
||||
}
|
||||
|
||||
func (p *HelmChartInflationGeneratorPlugin) getPullCommandArgs() []string {
|
||||
args := []string{"pull", "--untar", "--untardir", p.ChartHome}
|
||||
chartName := fmt.Sprintf("%s/%s", p.ChartRepoName, p.ChartName)
|
||||
if p.ChartVersion != "" {
|
||||
args = append(args, "--version", p.ChartVersion)
|
||||
func (p *HelmChartInflationGeneratorPlugin) pullCommand() []string {
|
||||
args := []string{
|
||||
"pull",
|
||||
"--untar",
|
||||
"--untardir", p.absChartHome(),
|
||||
"--repo", p.Repo,
|
||||
p.Name}
|
||||
if p.Version != "" {
|
||||
args = append(args, "--version", p.Version)
|
||||
}
|
||||
if p.ChartRepoURL != "" {
|
||||
args = append(args, "--repo", p.ChartRepoURL)
|
||||
chartName = p.ChartName
|
||||
}
|
||||
|
||||
args = append(args, chartName)
|
||||
|
||||
return args
|
||||
}
|
||||
|
||||
// checkLocalChart will return true if the chart does exist in
|
||||
// chartExistsLocally will return true if the chart does exist in
|
||||
// local chart home.
|
||||
func (p *HelmChartInflationGeneratorPlugin) checkLocalChart() bool {
|
||||
path := filepath.Join(p.ChartHome, p.ChartName)
|
||||
func (p *HelmChartInflationGeneratorPlugin) chartExistsLocally() (string, bool) {
|
||||
path := filepath.Join(p.absChartHome(), p.Name)
|
||||
s, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return false
|
||||
return "", false
|
||||
}
|
||||
return s.IsDir()
|
||||
return path, s.IsDir()
|
||||
}
|
||||
|
||||
// checkHelmVersion will return an error if the helm version is not V3
|
||||
|
||||
@@ -116,10 +116,16 @@ var generatorConfigurators = map[builtinhelpers.BuiltinPluginType]func(
|
||||
kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f gFactory) (
|
||||
result []resmap.Generator, err error) {
|
||||
var c struct {
|
||||
types.HelmChartArgs
|
||||
types.HelmGlobals
|
||||
types.HelmChart
|
||||
}
|
||||
for _, args := range kt.kustomization.HelmChartInflationGenerator {
|
||||
c.HelmChartArgs = args
|
||||
var globals types.HelmGlobals
|
||||
if kt.kustomization.HelmGlobals != nil {
|
||||
globals = *kt.kustomization.HelmGlobals
|
||||
}
|
||||
for _, chart := range kt.kustomization.HelmCharts {
|
||||
c.HelmGlobals = globals
|
||||
c.HelmChart = chart
|
||||
p := f()
|
||||
if err = kt.configureBuiltinPlugin(p, c, bpt); err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -3,14 +3,13 @@
|
||||
|
||||
package krusty_test
|
||||
|
||||
/*
|
||||
import (
|
||||
"testing"
|
||||
|
||||
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||
)
|
||||
|
||||
var expected string = `
|
||||
const expectedHelm = `
|
||||
apiVersion: v1
|
||||
data:
|
||||
rcon-password: Q0hBTkdFTUUh
|
||||
@@ -18,36 +17,19 @@ kind: Secret
|
||||
metadata:
|
||||
labels:
|
||||
app: test-minecraft
|
||||
chart: minecraft-1.2.0
|
||||
chart: minecraft-3.1.3
|
||||
heritage: Helm
|
||||
release: test
|
||||
name: test-minecraft
|
||||
type: Opaque
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
annotations:
|
||||
volume.alpha.kubernetes.io/storage-class: default
|
||||
labels:
|
||||
app: test-minecraft
|
||||
chart: minecraft-1.2.0
|
||||
heritage: Helm
|
||||
release: test
|
||||
name: test-minecraft-datadir
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 1Gi
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
annotations: {}
|
||||
labels:
|
||||
app: test-minecraft
|
||||
chart: minecraft-1.2.0
|
||||
chart: minecraft-3.1.3
|
||||
heritage: Helm
|
||||
release: test
|
||||
name: test-minecraft
|
||||
@@ -59,45 +41,43 @@ spec:
|
||||
targetPort: minecraft
|
||||
selector:
|
||||
app: test-minecraft
|
||||
type: LoadBalancer
|
||||
type: ClusterIP
|
||||
`
|
||||
|
||||
func TestHelmChartInflationGenerator(t *testing.T) {
|
||||
th := kusttest_test.MakeHarness(t)
|
||||
th.WriteK("/app", `
|
||||
func TestHelmChartInflationGeneratorOld(t *testing.T) {
|
||||
th := kusttest_test.MakeEnhancedHarnessWithTmpRoot(t)
|
||||
defer th.Reset()
|
||||
if err := th.ErrIfNoHelm(); err != nil {
|
||||
t.Skip("skipping: " + err.Error())
|
||||
}
|
||||
|
||||
th.WriteK(th.GetRoot(), `
|
||||
helmChartInflationGenerator:
|
||||
- chartName: minecraft
|
||||
chartRepoUrl: https://kubernetes-charts.storage.googleapis.com
|
||||
chartVersion: v1.2.0
|
||||
chartRepoUrl: https://itzg.github.io/minecraft-server-charts
|
||||
chartVersion: 3.1.3
|
||||
releaseName: test
|
||||
releaseNamespace: testNamespace
|
||||
`)
|
||||
|
||||
m := th.Run("/app", th.MakeDefaultOptions())
|
||||
th.AssertActualEqualsExpected(m, expected)
|
||||
m := th.Run(th.GetRoot(), th.MakeOptionsPluginsEnabled())
|
||||
th.AssertActualEqualsExpected(m, expectedHelm)
|
||||
}
|
||||
|
||||
func TestHelmChartInflationGenerator(t *testing.T) {
|
||||
th := kusttest_test.MakeEnhancedHarnessWithTmpRoot(t)
|
||||
defer th.Reset()
|
||||
if err := th.ErrIfNoHelm(); err != nil {
|
||||
t.Skip("skipping: " + err.Error())
|
||||
}
|
||||
|
||||
func TestHelmChartInflationGeneratorAsPlugin(t *testing.T) {
|
||||
th := kusttest_test.MakeHarness(t)
|
||||
th.WriteK("/app", `
|
||||
generators:
|
||||
- helm.yaml
|
||||
th.WriteK(th.GetRoot(), `
|
||||
helmCharts:
|
||||
- name: minecraft
|
||||
repo: https://itzg.github.io/minecraft-server-charts
|
||||
version: 3.1.3
|
||||
releaseName: test
|
||||
`)
|
||||
|
||||
th.WriteF("/app/helm.yaml", `
|
||||
apiVersion: builtin
|
||||
kind: HelmChartInflationGenerator
|
||||
metadata:
|
||||
name: myMap
|
||||
chartName: minecraft
|
||||
chartRepoUrl: https://kubernetes-charts.storage.googleapis.com
|
||||
chartVersion: v1.2.0
|
||||
releaseName: test
|
||||
releaseNamespace: testNamespace
|
||||
`)
|
||||
|
||||
m := th.Run("/app", th.MakeDefaultOptions())
|
||||
th.AssertActualEqualsExpected(m, expected)
|
||||
m := th.Run(th.GetRoot(), th.MakeOptionsPluginsEnabled())
|
||||
th.AssertActualEqualsExpected(m, expectedHelm)
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -6,6 +6,7 @@ package kusttest_test
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -83,6 +84,11 @@ func makeBaseEnhancedHarness(t *testing.T) *HarnessEnhanced {
|
||||
filesys.MakeFsOnDisk())}
|
||||
}
|
||||
|
||||
func (th *HarnessEnhanced) ErrIfNoHelm() error {
|
||||
_, err := exec.LookPath(th.GetPluginConfig().HelmConfig.Command)
|
||||
return err
|
||||
}
|
||||
|
||||
func (th *HarnessEnhanced) GetRoot() string {
|
||||
return th.ldr.Root()
|
||||
}
|
||||
|
||||
@@ -3,14 +3,77 @@
|
||||
|
||||
package types
|
||||
|
||||
// HelmChartArgs contains the metadata of how to generate a secret.
|
||||
type HelmGlobals struct {
|
||||
// ChartHome is a file path, relative to the kustomization root,
|
||||
// to a directory containing a subdirectory for each chart to be
|
||||
// included in the kustomization.
|
||||
// The default value of this field is "charts".
|
||||
// So, for example, kustomize looks for the minecraft chart
|
||||
// at {kustomizationRoot}/{ChartHome}/minecraft.
|
||||
// If the chart is there at build time, kustomize will use it as found,
|
||||
// and not check version numbers or dates.
|
||||
// If the chart is not there, kustomize will attempt to pull it
|
||||
// using the version number specified in the kustomization file,
|
||||
// and put it there. To suppress the pull attempt, simply assure
|
||||
// that the chart is already there.
|
||||
ChartHome string `json:"chartHome,omitempty" yaml:"chartHome,omitempty"`
|
||||
|
||||
// ConfigHome defines a value that kustomize should pass to helm via
|
||||
// the HELM_CONFIG_HOME environment variable. kustomize doesn't attempt
|
||||
// to read or write this directory.
|
||||
// If omitted, {tmpDir}/helm is used, where {tmpDir} is some temporary
|
||||
// directory created by kustomize for the benefit of helm.
|
||||
// Likewise, kustomize sets
|
||||
// HELM_CACHE_HOME={ConfigHome}/.cache
|
||||
// HELM_DATA_HOME={ConfigHome}/.data
|
||||
// for the helm subprocess.
|
||||
ConfigHome string `json:"configHome,omitempty" yaml:"configHome,omitempty"`
|
||||
}
|
||||
|
||||
type HelmChart struct {
|
||||
// Name is the name of the chart, e.g. 'minecraft'.
|
||||
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||
|
||||
// Version is the version of the chart, e.g. '3.1.3'
|
||||
Version string `json:"version,omitempty" yaml:"version,omitempty"`
|
||||
|
||||
// Repo is a URL locating the chart on the internet.
|
||||
// This is the argument to helm's `--repo` flag, e.g.
|
||||
// `https://itzg.github.io/minecraft-server-charts`.
|
||||
Repo string `json:"repo,omitempty" yaml:"repo,omitempty"`
|
||||
|
||||
// ReleaseName replaces RELEASE-NAME in chart template output,
|
||||
// making a particular inflation of a chart unique with respect to
|
||||
// other inflations of the same chart in a cluster. It's the first
|
||||
// argument to the helm `install` and `template` commands, i.e.
|
||||
// helm install {RELEASE-NAME} {chartName}
|
||||
// helm template {RELEASE-NAME} {chartName}
|
||||
// If omitted, the flag --generate-name is passed to 'helm template'.
|
||||
ReleaseName string `json:"releaseName,omitempty" yaml:"releaseName,omitempty"`
|
||||
|
||||
// ValuesFile is local file path to a values file to use _instead of_
|
||||
// the default values that accompanied the chart.
|
||||
// The default values are in '{ChartHome}/{Name}/values.yaml'.
|
||||
ValuesFile string `json:"valuesFile,omitempty" yaml:"valuesFile,omitempty"`
|
||||
|
||||
// ValuesInline holds value mappings specified directly,
|
||||
// rather than in a separate file.
|
||||
ValuesInline map[string]interface{} `json:"valuesInline,omitempty" yaml:"valuesInline,omitempty"`
|
||||
|
||||
// ValuesMerge specifies how to treat ValuesInline with respect to Values.
|
||||
// Legal values: 'merge', 'override', 'replace'.
|
||||
// Defaults to 'override'.
|
||||
ValuesMerge string `json:"valuesMerge,omitempty" yaml:"valuesMerge,omitempty"`
|
||||
}
|
||||
|
||||
// HelmChartArgs contains arguments to helm.
|
||||
// Deprecated. Use HelmGlobals and HelmChart instead.
|
||||
type HelmChartArgs struct {
|
||||
ChartName string `json:"chartName,omitempty" yaml:"chartName,omitempty"`
|
||||
ChartVersion string `json:"chartVersion,omitempty" yaml:"chartVersion,omitempty"`
|
||||
ChartRepoURL string `json:"chartRepoUrl,omitempty" yaml:"chartRepoUrl,omitempty"`
|
||||
ChartHome string `json:"chartHome,omitempty" yaml:"chartHome,omitempty"`
|
||||
// Use chartRelease to keep compatible with old exec plugin
|
||||
ChartRepoName string `json:"chartRelease,omitempty" yaml:"chartRelease,omitempty"`
|
||||
ChartName string `json:"chartName,omitempty" yaml:"chartName,omitempty"`
|
||||
ChartVersion string `json:"chartVersion,omitempty" yaml:"chartVersion,omitempty"`
|
||||
ChartRepoURL string `json:"chartRepoUrl,omitempty" yaml:"chartRepoUrl,omitempty"`
|
||||
ChartHome string `json:"chartHome,omitempty" yaml:"chartHome,omitempty"`
|
||||
ChartRepoName string `json:"chartRepoName,omitempty" yaml:"chartRepoName,omitempty"`
|
||||
HelmBin string `json:"helmBin,omitempty" yaml:"helmBin,omitempty"`
|
||||
HelmHome string `json:"helmHome,omitempty" yaml:"helmHome,omitempty"`
|
||||
Values string `json:"values,omitempty" yaml:"values,omitempty"`
|
||||
@@ -20,3 +83,32 @@ type HelmChartArgs struct {
|
||||
ReleaseNamespace string `json:"releaseNamespace,omitempty" yaml:"releaseNamespace,omitempty"`
|
||||
ExtraArgs []string `json:"extraArgs,omitempty" yaml:"extraArgs,omitempty"`
|
||||
}
|
||||
|
||||
// SplitHelmParameters splits helm parameters into
|
||||
// per-chart params and global chart-independent parameters.
|
||||
func SplitHelmParameters(
|
||||
oldArgs []HelmChartArgs) (charts []HelmChart, globals HelmGlobals) {
|
||||
for _, old := range oldArgs {
|
||||
charts = append(charts, makeHelmChartFromHca(&old))
|
||||
if old.HelmHome != "" {
|
||||
// last non-empty wins
|
||||
globals.ConfigHome = old.HelmHome
|
||||
}
|
||||
if old.ChartHome != "" {
|
||||
// last non-empty wins
|
||||
globals.ChartHome = old.ChartHome
|
||||
}
|
||||
}
|
||||
return charts, globals
|
||||
}
|
||||
|
||||
func makeHelmChartFromHca(old *HelmChartArgs) (c HelmChart) {
|
||||
c.Name = old.ChartName
|
||||
c.Version = old.ChartVersion
|
||||
c.Repo = old.ChartRepoURL
|
||||
c.ValuesFile = old.Values
|
||||
c.ValuesInline = old.ValuesLocal
|
||||
c.ValuesMerge = old.ValuesMerge
|
||||
c.ReleaseName = old.ReleaseName
|
||||
return
|
||||
}
|
||||
|
||||
@@ -129,9 +129,14 @@ type Kustomization struct {
|
||||
// the map will have a suffix hash generated from its contents.
|
||||
SecretGenerator []SecretArgs `json:"secretGenerator,omitempty" yaml:"secretGenerator,omitempty"`
|
||||
|
||||
// HelmGlobals contains helm configuration that isn't chart specific.
|
||||
HelmGlobals *HelmGlobals `json:"helmGlobals,omitempty" yaml:"helmGlobals,omitempty"`
|
||||
|
||||
// HelmCharts is a list of helm chart configuration instances.
|
||||
HelmCharts []HelmChart `json:"helmCharts,omitempty" yaml:"helmCharts,omitempty"`
|
||||
|
||||
// HelmChartInflationGenerator is a list of helm chart configurations.
|
||||
// The resulting resource is a normal operand rendered from
|
||||
// a remote chart by `helm template`
|
||||
// Deprecated. Auto-converted to HelmGlobals and HelmCharts.
|
||||
HelmChartInflationGenerator []HelmChartArgs `json:"helmChartInflationGenerator,omitempty" yaml:"helmChartInflationGenerator,omitempty"`
|
||||
|
||||
// GeneratorOptions modify behavior of all ConfigMap and Secret generators.
|
||||
@@ -185,6 +190,15 @@ func (k *Kustomization) FixKustomizationPostUnmarshalling() {
|
||||
k.SecretGenerator[i].EnvSource = ""
|
||||
}
|
||||
}
|
||||
charts, globals := SplitHelmParameters(k.HelmChartInflationGenerator)
|
||||
if k.HelmGlobals == nil {
|
||||
if globals.ChartHome != "" || globals.ConfigHome != "" {
|
||||
k.HelmGlobals = &globals
|
||||
}
|
||||
}
|
||||
k.HelmCharts = append(k.HelmCharts, charts...)
|
||||
// Wipe it for the fix command.
|
||||
k.HelmChartInflationGenerator = nil
|
||||
}
|
||||
|
||||
// FixKustomizationPreMarshalling fixes things
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
|
||||
package types
|
||||
|
||||
type HelmConfig struct {
|
||||
Enabled bool
|
||||
Command string
|
||||
}
|
||||
|
||||
// PluginConfig holds plugin configuration.
|
||||
type PluginConfig struct {
|
||||
// PluginRestrictions distinguishes plugin restrictions.
|
||||
@@ -13,11 +18,17 @@ type PluginConfig struct {
|
||||
|
||||
// FnpLoadingOptions sets the way function-based plugin behaviors.
|
||||
FnpLoadingOptions FnPluginLoadingOptions
|
||||
|
||||
// HelmConfig contains metadata needed for allowing and running helm.
|
||||
HelmConfig HelmConfig
|
||||
}
|
||||
|
||||
func EnabledPluginConfig(b BuiltinPluginLoadingOptions) (pc *PluginConfig) {
|
||||
pc = MakePluginConfig(PluginRestrictionsNone, b)
|
||||
pc.FnpLoadingOptions.EnableStar = true
|
||||
pc.HelmConfig.Enabled = true
|
||||
// If this command is not on PATH, tests needing it should skip.
|
||||
pc.HelmConfig.Command = "helmV3"
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user