mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-07-16 17:33:14 +00:00
Merge pull request #3784 from monopole/flagEnableHelm
Add flag --enable-helm
This commit is contained in:
@@ -6,7 +6,6 @@ package builtins
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
@@ -16,7 +15,6 @@ import (
|
|||||||
|
|
||||||
"github.com/imdario/mergo"
|
"github.com/imdario/mergo"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"sigs.k8s.io/kustomize/api/filesys"
|
|
||||||
"sigs.k8s.io/kustomize/api/resmap"
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
"sigs.k8s.io/kustomize/api/types"
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
"sigs.k8s.io/yaml"
|
"sigs.k8s.io/yaml"
|
||||||
@@ -25,201 +23,234 @@ import (
|
|||||||
// HelmChartInflationGeneratorPlugin is a plugin to generate resources
|
// HelmChartInflationGeneratorPlugin is a plugin to generate resources
|
||||||
// from a remote or local helm chart.
|
// from a remote or local helm chart.
|
||||||
type HelmChartInflationGeneratorPlugin struct {
|
type HelmChartInflationGeneratorPlugin struct {
|
||||||
h *resmap.PluginHelpers
|
h *resmap.PluginHelpers
|
||||||
types.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
|
types.HelmGlobals
|
||||||
runHelmCommand func([]string) ([]byte, error)
|
types.HelmChart
|
||||||
types.HelmChartArgs
|
|
||||||
tmpDir string
|
tmpDir string
|
||||||
}
|
}
|
||||||
|
|
||||||
var KustomizePlugin HelmChartInflationGeneratorPlugin
|
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
|
// Config uses the input plugin configurations `config` to setup the generator
|
||||||
// options
|
// 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
|
p.h = h
|
||||||
err := yaml.Unmarshal(config, p)
|
if err = yaml.Unmarshal(config, p); err != nil {
|
||||||
if err != nil {
|
return
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
tmpDir, err := filesys.NewTmpConfirmedDir()
|
return p.validateArgs()
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// EncodeValues for writing
|
// This uses the real file system since tmpDir may be used
|
||||||
func (p *HelmChartInflationGeneratorPlugin) EncodeValues(w io.Writer) error {
|
// by the helm subprocess. Cannot use a chroot jail or fake
|
||||||
d, err := yaml.Marshal(p.ValuesLocal)
|
// filesystem since we allow the user to use previously
|
||||||
if err != nil {
|
// downloaded charts. This is safe since this plugin is
|
||||||
return err
|
// owned by kustomize.
|
||||||
}
|
func (p *HelmChartInflationGeneratorPlugin) establishTmpDir() (err error) {
|
||||||
_, err = w.Write(d)
|
if p.tmpDir != "" {
|
||||||
if err != nil {
|
// already done.
|
||||||
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) {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// we must use use loader to read values file
|
p.tmpDir, err = ioutil.TempDir("", "kustomize-helm-")
|
||||||
b, err := p.h.Loader().Load(p.Values)
|
return err
|
||||||
if err != nil {
|
}
|
||||||
|
|
||||||
|
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
|
return err
|
||||||
}
|
}
|
||||||
path, err := p.writeValuesBytes(b)
|
|
||||||
if err != nil {
|
// ConfigHome is not loaded by the plugin, and can be located anywhere.
|
||||||
return err
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *HelmChartInflationGeneratorPlugin) writeValuesBytes(b []byte) (string, error) {
|
func (p *HelmChartInflationGeneratorPlugin) errIfIllegalValuesMerge() error {
|
||||||
path := filepath.Join(p.ChartHome, p.ChartName, "kustomize-values.yaml")
|
if p.ValuesMerge == "" {
|
||||||
err := ioutil.WriteFile(path, b, 0644)
|
// 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 {
|
if err != nil {
|
||||||
return "", err
|
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
|
// Generate implements generator
|
||||||
func (p *HelmChartInflationGeneratorPlugin) Generate() (resmap.ResMap, error) {
|
func (p *HelmChartInflationGeneratorPlugin) Generate() (rm resmap.ResMap, err error) {
|
||||||
// cleanup
|
defer p.cleanup()
|
||||||
defer os.RemoveAll(p.tmpDir)
|
if err = p.checkHelmVersion(); err != nil {
|
||||||
// check helm version. we only support V3
|
|
||||||
err := p.checkHelmVersion()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// pull the chart
|
if path, exists := p.chartExistsLocally(); !exists {
|
||||||
if !p.checkLocalChart() {
|
if p.Repo == "" {
|
||||||
_, err := p.runHelmCommand(p.getPullCommandArgs())
|
return nil, fmt.Errorf(
|
||||||
if err != nil {
|
"no repo specified for pull, no chart found at '%s'", path)
|
||||||
|
}
|
||||||
|
if _, err := p.runHelmCommand(p.pullCommand()); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if len(p.ValuesInline) > 0 {
|
||||||
// inflator config valuesLocal
|
p.ValuesFile, err = p.createNewMergedValuesFile()
|
||||||
if len(p.ValuesLocal) > 0 {
|
|
||||||
err := p.useValuesLocal()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
err := p.copyValues()
|
p.ValuesFile, err = p.copyValuesFile()
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if err != nil {
|
||||||
// render the charts
|
return nil, err
|
||||||
stdout, err := p.runHelmCommand(p.getTemplateCommandArgs())
|
}
|
||||||
|
var stdout []byte
|
||||||
|
stdout, err = p.runHelmCommand(p.templateCommand())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
rm, rmfErr := p.h.ResmapFactory().NewResMapFromBytes(stdout)
|
rm, err = p.h.ResmapFactory().NewResMapFromBytes(stdout)
|
||||||
if rmfErr == nil {
|
if err == nil {
|
||||||
return rm, nil
|
return rm, nil
|
||||||
}
|
}
|
||||||
// try to remove the contents before first "---" because
|
// 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 {
|
if idx := strings.Index(stdoutStr, "---"); idx != -1 {
|
||||||
return p.h.ResmapFactory().NewResMapFromBytes([]byte(stdoutStr[idx:]))
|
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"}
|
args := []string{"template"}
|
||||||
if p.ReleaseName != "" {
|
if p.ReleaseName != "" {
|
||||||
args = append(args, p.ReleaseName)
|
args = append(args, p.ReleaseName)
|
||||||
}
|
}
|
||||||
args = append(args, filepath.Join(p.ChartHome, p.ChartName))
|
args = append(args, filepath.Join(p.absChartHome(), p.Name))
|
||||||
if p.ReleaseNamespace != "" {
|
if p.ValuesFile != "" {
|
||||||
args = append(args, "--namespace", p.ReleaseNamespace)
|
args = append(args, "--values", p.ValuesFile)
|
||||||
}
|
}
|
||||||
if p.Values != "" {
|
if p.ReleaseName == "" {
|
||||||
args = append(args, "--values", p.Values)
|
// 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
|
return args
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *HelmChartInflationGeneratorPlugin) getPullCommandArgs() []string {
|
func (p *HelmChartInflationGeneratorPlugin) pullCommand() []string {
|
||||||
args := []string{"pull", "--untar", "--untardir", p.ChartHome}
|
args := []string{
|
||||||
chartName := fmt.Sprintf("%s/%s", p.ChartRepoName, p.ChartName)
|
"pull",
|
||||||
if p.ChartVersion != "" {
|
"--untar",
|
||||||
args = append(args, "--version", p.ChartVersion)
|
"--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
|
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.
|
// local chart home.
|
||||||
func (p *HelmChartInflationGeneratorPlugin) checkLocalChart() bool {
|
func (p *HelmChartInflationGeneratorPlugin) chartExistsLocally() (string, bool) {
|
||||||
path := filepath.Join(p.ChartHome, p.ChartName)
|
path := filepath.Join(p.absChartHome(), p.Name)
|
||||||
s, err := os.Stat(path)
|
s, err := os.Stat(path)
|
||||||
if err != nil {
|
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
|
// 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) (
|
kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f gFactory) (
|
||||||
result []resmap.Generator, err error) {
|
result []resmap.Generator, err error) {
|
||||||
var c struct {
|
var c struct {
|
||||||
types.HelmChartArgs
|
types.HelmGlobals
|
||||||
|
types.HelmChart
|
||||||
}
|
}
|
||||||
for _, args := range kt.kustomization.HelmChartInflationGenerator {
|
var globals types.HelmGlobals
|
||||||
c.HelmChartArgs = args
|
if kt.kustomization.HelmGlobals != nil {
|
||||||
|
globals = *kt.kustomization.HelmGlobals
|
||||||
|
}
|
||||||
|
for _, chart := range kt.kustomization.HelmCharts {
|
||||||
|
c.HelmGlobals = globals
|
||||||
|
c.HelmChart = chart
|
||||||
p := f()
|
p := f()
|
||||||
if err = kt.configureBuiltinPlugin(p, c, bpt); err != nil {
|
if err = kt.configureBuiltinPlugin(p, c, bpt); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -3,14 +3,13 @@
|
|||||||
|
|
||||||
package krusty_test
|
package krusty_test
|
||||||
|
|
||||||
/*
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
|
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||||
)
|
)
|
||||||
|
|
||||||
var expected string = `
|
const expectedHelm = `
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
data:
|
data:
|
||||||
rcon-password: Q0hBTkdFTUUh
|
rcon-password: Q0hBTkdFTUUh
|
||||||
@@ -18,36 +17,19 @@ kind: Secret
|
|||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
app: test-minecraft
|
app: test-minecraft
|
||||||
chart: minecraft-1.2.0
|
chart: minecraft-3.1.3
|
||||||
heritage: Helm
|
heritage: Helm
|
||||||
release: test
|
release: test
|
||||||
name: test-minecraft
|
name: test-minecraft
|
||||||
type: Opaque
|
type: Opaque
|
||||||
---
|
---
|
||||||
apiVersion: v1
|
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
|
kind: Service
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations: {}
|
||||||
labels:
|
labels:
|
||||||
app: test-minecraft
|
app: test-minecraft
|
||||||
chart: minecraft-1.2.0
|
chart: minecraft-3.1.3
|
||||||
heritage: Helm
|
heritage: Helm
|
||||||
release: test
|
release: test
|
||||||
name: test-minecraft
|
name: test-minecraft
|
||||||
@@ -59,45 +41,43 @@ spec:
|
|||||||
targetPort: minecraft
|
targetPort: minecraft
|
||||||
selector:
|
selector:
|
||||||
app: test-minecraft
|
app: test-minecraft
|
||||||
type: LoadBalancer
|
type: ClusterIP
|
||||||
`
|
`
|
||||||
|
|
||||||
func TestHelmChartInflationGenerator(t *testing.T) {
|
func TestHelmChartInflationGeneratorOld(t *testing.T) {
|
||||||
th := kusttest_test.MakeHarness(t)
|
th := kusttest_test.MakeEnhancedHarnessWithTmpRoot(t)
|
||||||
th.WriteK("/app", `
|
defer th.Reset()
|
||||||
|
if err := th.ErrIfNoHelm(); err != nil {
|
||||||
|
t.Skip("skipping: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
th.WriteK(th.GetRoot(), `
|
||||||
helmChartInflationGenerator:
|
helmChartInflationGenerator:
|
||||||
- chartName: minecraft
|
- chartName: minecraft
|
||||||
chartRepoUrl: https://kubernetes-charts.storage.googleapis.com
|
chartRepoUrl: https://itzg.github.io/minecraft-server-charts
|
||||||
chartVersion: v1.2.0
|
chartVersion: 3.1.3
|
||||||
releaseName: test
|
releaseName: test
|
||||||
releaseNamespace: testNamespace
|
|
||||||
`)
|
`)
|
||||||
|
|
||||||
m := th.Run("/app", th.MakeDefaultOptions())
|
m := th.Run(th.GetRoot(), th.MakeOptionsPluginsEnabled())
|
||||||
th.AssertActualEqualsExpected(m, expected)
|
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.WriteK(th.GetRoot(), `
|
||||||
th := kusttest_test.MakeHarness(t)
|
helmCharts:
|
||||||
th.WriteK("/app", `
|
- name: minecraft
|
||||||
generators:
|
repo: https://itzg.github.io/minecraft-server-charts
|
||||||
- helm.yaml
|
version: 3.1.3
|
||||||
|
releaseName: test
|
||||||
`)
|
`)
|
||||||
|
|
||||||
th.WriteF("/app/helm.yaml", `
|
m := th.Run(th.GetRoot(), th.MakeOptionsPluginsEnabled())
|
||||||
apiVersion: builtin
|
th.AssertActualEqualsExpected(m, expectedHelm)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ package kusttest_test
|
|||||||
import (
|
import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -83,6 +84,11 @@ func makeBaseEnhancedHarness(t *testing.T) *HarnessEnhanced {
|
|||||||
filesys.MakeFsOnDisk())}
|
filesys.MakeFsOnDisk())}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (th *HarnessEnhanced) ErrIfNoHelm() error {
|
||||||
|
_, err := exec.LookPath(th.GetPluginConfig().HelmConfig.Command)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func (th *HarnessEnhanced) GetRoot() string {
|
func (th *HarnessEnhanced) GetRoot() string {
|
||||||
return th.ldr.Root()
|
return th.ldr.Root()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,14 +3,77 @@
|
|||||||
|
|
||||||
package types
|
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 {
|
type HelmChartArgs struct {
|
||||||
ChartName string `json:"chartName,omitempty" yaml:"chartName,omitempty"`
|
ChartName string `json:"chartName,omitempty" yaml:"chartName,omitempty"`
|
||||||
ChartVersion string `json:"chartVersion,omitempty" yaml:"chartVersion,omitempty"`
|
ChartVersion string `json:"chartVersion,omitempty" yaml:"chartVersion,omitempty"`
|
||||||
ChartRepoURL string `json:"chartRepoUrl,omitempty" yaml:"chartRepoUrl,omitempty"`
|
ChartRepoURL string `json:"chartRepoUrl,omitempty" yaml:"chartRepoUrl,omitempty"`
|
||||||
ChartHome string `json:"chartHome,omitempty" yaml:"chartHome,omitempty"`
|
ChartHome string `json:"chartHome,omitempty" yaml:"chartHome,omitempty"`
|
||||||
// Use chartRelease to keep compatible with old exec plugin
|
ChartRepoName string `json:"chartRepoName,omitempty" yaml:"chartRepoName,omitempty"`
|
||||||
ChartRepoName string `json:"chartRelease,omitempty" yaml:"chartRelease,omitempty"`
|
|
||||||
HelmBin string `json:"helmBin,omitempty" yaml:"helmBin,omitempty"`
|
HelmBin string `json:"helmBin,omitempty" yaml:"helmBin,omitempty"`
|
||||||
HelmHome string `json:"helmHome,omitempty" yaml:"helmHome,omitempty"`
|
HelmHome string `json:"helmHome,omitempty" yaml:"helmHome,omitempty"`
|
||||||
Values string `json:"values,omitempty" yaml:"values,omitempty"`
|
Values string `json:"values,omitempty" yaml:"values,omitempty"`
|
||||||
@@ -20,3 +83,32 @@ type HelmChartArgs struct {
|
|||||||
ReleaseNamespace string `json:"releaseNamespace,omitempty" yaml:"releaseNamespace,omitempty"`
|
ReleaseNamespace string `json:"releaseNamespace,omitempty" yaml:"releaseNamespace,omitempty"`
|
||||||
ExtraArgs []string `json:"extraArgs,omitempty" yaml:"extraArgs,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.
|
// the map will have a suffix hash generated from its contents.
|
||||||
SecretGenerator []SecretArgs `json:"secretGenerator,omitempty" yaml:"secretGenerator,omitempty"`
|
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.
|
// HelmChartInflationGenerator is a list of helm chart configurations.
|
||||||
// The resulting resource is a normal operand rendered from
|
// Deprecated. Auto-converted to HelmGlobals and HelmCharts.
|
||||||
// a remote chart by `helm template`
|
|
||||||
HelmChartInflationGenerator []HelmChartArgs `json:"helmChartInflationGenerator,omitempty" yaml:"helmChartInflationGenerator,omitempty"`
|
HelmChartInflationGenerator []HelmChartArgs `json:"helmChartInflationGenerator,omitempty" yaml:"helmChartInflationGenerator,omitempty"`
|
||||||
|
|
||||||
// GeneratorOptions modify behavior of all ConfigMap and Secret generators.
|
// GeneratorOptions modify behavior of all ConfigMap and Secret generators.
|
||||||
@@ -185,6 +190,15 @@ func (k *Kustomization) FixKustomizationPostUnmarshalling() {
|
|||||||
k.SecretGenerator[i].EnvSource = ""
|
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
|
// FixKustomizationPreMarshalling fixes things
|
||||||
|
|||||||
@@ -3,6 +3,11 @@
|
|||||||
|
|
||||||
package types
|
package types
|
||||||
|
|
||||||
|
type HelmConfig struct {
|
||||||
|
Enabled bool
|
||||||
|
Command string
|
||||||
|
}
|
||||||
|
|
||||||
// PluginConfig holds plugin configuration.
|
// PluginConfig holds plugin configuration.
|
||||||
type PluginConfig struct {
|
type PluginConfig struct {
|
||||||
// PluginRestrictions distinguishes plugin restrictions.
|
// PluginRestrictions distinguishes plugin restrictions.
|
||||||
@@ -13,11 +18,17 @@ type PluginConfig struct {
|
|||||||
|
|
||||||
// FnpLoadingOptions sets the way function-based plugin behaviors.
|
// FnpLoadingOptions sets the way function-based plugin behaviors.
|
||||||
FnpLoadingOptions FnPluginLoadingOptions
|
FnpLoadingOptions FnPluginLoadingOptions
|
||||||
|
|
||||||
|
// HelmConfig contains metadata needed for allowing and running helm.
|
||||||
|
HelmConfig HelmConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
func EnabledPluginConfig(b BuiltinPluginLoadingOptions) (pc *PluginConfig) {
|
func EnabledPluginConfig(b BuiltinPluginLoadingOptions) (pc *PluginConfig) {
|
||||||
pc = MakePluginConfig(PluginRestrictionsNone, b)
|
pc = MakePluginConfig(PluginRestrictionsNone, b)
|
||||||
pc.FnpLoadingOptions.EnableStar = true
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,49 +1,63 @@
|
|||||||
# kustomization of a helm chart
|
# kustomization of a helm chart
|
||||||
|
|
||||||
|
[`helm`]: https://helm.sh
|
||||||
[last mile]: https://testingclouds.wordpress.com/2018/07/20/844/
|
[last mile]: https://testingclouds.wordpress.com/2018/07/20/844/
|
||||||
[stable chart]: https://github.com/helm/charts/tree/master/stable
|
[artifact hub]: https://artifacthub.io
|
||||||
[helm charts]: https://github.com/helm/charts
|
[_minecraft_]: https://artifacthub.io/packages/helm/minecraft-server-charts/minecraft
|
||||||
[_minecraft_]: https://github.com/helm/charts/tree/master/stable/minecraft
|
|
||||||
[plugin]: ../docs/plugins
|
[plugin]: ../docs/plugins
|
||||||
|
[built]: https://kubectl.docs.kubernetes.io/references/kustomize/kustomization
|
||||||
|
|
||||||
[Helm charts] aren't natively read by kustomize, but
|
Kustomize is [built] from _generators_ and
|
||||||
kustomize has a builtin HelmChartInflationGenerator that allows one to
|
_transformers_; the former make kubernetes YAML, the
|
||||||
access helm charts.
|
latter transform said YAML.
|
||||||
|
|
||||||
One pattern combining kustomize and helm is
|
Kustomize, via the `helmCharts` field, has the ability to
|
||||||
the [last mile] modification, where
|
use the [`helm`] command line program in a subprocess to
|
||||||
one uses an inflated chart as a base, then
|
inflate a helm chart, generating YAML as part of (or as the
|
||||||
modifies it on the way to the cluster using
|
entirety of) a kustomize base.
|
||||||
kustomize.
|
|
||||||
|
|
||||||
The example arbitrarily uses [_minecraft_] in [stable chart] repository,
|
This YAML can then be modified either in the base directly
|
||||||
but should work for any chart in any valid chart repository.
|
(transformers always run _after_ generators), or via
|
||||||
|
a kustomize overlay.
|
||||||
|
|
||||||
|
Either approach can be viewed as [last mile] modification
|
||||||
|
of the chart output before applying it to a cluster.
|
||||||
|
|
||||||
|
The example below arbitrarily uses the
|
||||||
|
[_minecraft_] chart pulled from the [artifact hub]
|
||||||
|
chart repository.
|
||||||
|
|
||||||
|
## Preparation
|
||||||
|
|
||||||
|
This example defines the `helm` command as
|
||||||
|
<!-- @defineHelmCommand @testHelm -->
|
||||||
|
```
|
||||||
|
helmCommand=~/go/bin/helmV3
|
||||||
|
```
|
||||||
|
|
||||||
|
This value is needed for testing this example in CI/CD.
|
||||||
|
A user doesn't need this if their binary is called
|
||||||
|
`helm` and is on their shell's `PATH`.
|
||||||
|
|
||||||
The following example assumes you have `helm`
|
|
||||||
on your `$PATH`. The plugin only supports helm V3 or later.
|
|
||||||
|
|
||||||
Make a place to work:
|
Make a place to work:
|
||||||
|
|
||||||
<!-- @makeWorkplace @helmtest -->
|
<!-- @makeWorkplace @testHelm -->
|
||||||
|
|
||||||
```
|
```
|
||||||
DEMO_HOME=$(mktemp -d)
|
DEMO_HOME=$(mktemp -d)
|
||||||
mkdir -p $DEMO_HOME/base
|
mkdir -p $DEMO_HOME/base $DEMO_HOME/dev $DEMO_HOME/prod
|
||||||
mkdir -p $DEMO_HOME/dev
|
|
||||||
mkdir -p $DEMO_HOME/prod
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Use a remote chart
|
## Define some variants
|
||||||
|
|
||||||
Define a kustomization representing your _development_
|
Define a kustomization representing your _development_
|
||||||
variant (aka environment).
|
variant.
|
||||||
|
|
||||||
This could involve any number of kustomizations (see
|
This could involve any number of kustomizations (see
|
||||||
other examples), but in this case just add the name
|
other examples), but in this case just add the name
|
||||||
prefix `dev-` to all resources:
|
prefix '`dev-`' to all resources:
|
||||||
|
|
||||||
<!-- @writeKustDev @helmtest -->
|
|
||||||
|
|
||||||
|
<!-- @writeKustDev @testHelm -->
|
||||||
```
|
```
|
||||||
cat <<'EOF' >$DEMO_HOME/dev/kustomization.yaml
|
cat <<'EOF' >$DEMO_HOME/dev/kustomization.yaml
|
||||||
namePrefix: dev-
|
namePrefix: dev-
|
||||||
@@ -53,10 +67,9 @@ EOF
|
|||||||
```
|
```
|
||||||
|
|
||||||
Likewise define a _production_ variant, with a name
|
Likewise define a _production_ variant, with a name
|
||||||
prefix `prod-`:
|
prefix '`prod-`':
|
||||||
|
|
||||||
<!-- @writeKustProd @helmtest -->
|
|
||||||
|
|
||||||
|
<!-- @writeKustProd @testHelm -->
|
||||||
```
|
```
|
||||||
cat <<'EOF' >$DEMO_HOME/prod/kustomization.yaml
|
cat <<'EOF' >$DEMO_HOME/prod/kustomization.yaml
|
||||||
namePrefix: prod-
|
namePrefix: prod-
|
||||||
@@ -67,45 +80,34 @@ EOF
|
|||||||
|
|
||||||
These two variants refer to a common base.
|
These two variants refer to a common base.
|
||||||
|
|
||||||
Define this base:
|
Define this base the usual way by creating a
|
||||||
|
`kustomization` file:
|
||||||
<!-- @writeKustDev @helmtest -->
|
|
||||||
|
|
||||||
|
<!-- @writeKustBase @testHelm -->
|
||||||
```
|
```
|
||||||
cat <<'EOF' >$DEMO_HOME/base/kustomization.yaml
|
cat <<'EOF' >$DEMO_HOME/base/kustomization.yaml
|
||||||
generators:
|
helmCharts:
|
||||||
- chartInflator.yaml
|
- name: minecraft
|
||||||
|
valuesInline:
|
||||||
|
minecraftServer:
|
||||||
|
eula: true
|
||||||
|
difficulty: hard
|
||||||
|
rcon:
|
||||||
|
enabled: true
|
||||||
|
releaseName: moria
|
||||||
|
version: 3.1.3
|
||||||
|
repo: https://itzg.github.io/minecraft-server-charts
|
||||||
EOF
|
EOF
|
||||||
```
|
```
|
||||||
|
|
||||||
The base refers to a generator configuration file
|
The only thing in this particular file is a `helmCharts`
|
||||||
called `chartInflator.yaml`.
|
field, specifying a single chart.
|
||||||
|
|
||||||
This file lets one specify the name of a [stable chart],
|
The `valuesInline` field overrides some native chart values.
|
||||||
and other things like a path to a values file, defaulting
|
|
||||||
to the `values.yaml` that comes with the chart.
|
|
||||||
|
|
||||||
Create the config file `chartInflator.yaml`, specifying
|
|
||||||
the arbitrarily chosen chart name _minecraft_ and the repository
|
|
||||||
url that will be used to find the chart:
|
|
||||||
|
|
||||||
<!-- @writeGeneratorConfig @helmtest -->
|
|
||||||
|
|
||||||
```
|
|
||||||
cat <<'EOF' >$DEMO_HOME/base/chartInflator.yaml
|
|
||||||
apiVersion: builtin
|
|
||||||
kind: HelmChartInflationGenerator
|
|
||||||
metadata:
|
|
||||||
name: notImportantHere
|
|
||||||
chartName: minecraft
|
|
||||||
chartRepoUrl: https://kubernetes-charts.storage.googleapis.com
|
|
||||||
EOF
|
|
||||||
```
|
|
||||||
|
|
||||||
Check the directory layout:
|
Check the directory layout:
|
||||||
|
|
||||||
<!-- @tree -->
|
<!-- @tree -->
|
||||||
|
|
||||||
```
|
```
|
||||||
tree $DEMO_HOME
|
tree $DEMO_HOME
|
||||||
```
|
```
|
||||||
@@ -115,155 +117,249 @@ Expect something like:
|
|||||||
> ```
|
> ```
|
||||||
> /tmp/whatever
|
> /tmp/whatever
|
||||||
> ├── base
|
> ├── base
|
||||||
> │ ├── chartInflator.yaml
|
> │ └── kustomization.yaml
|
||||||
> │ └── kustomization.yaml
|
|
||||||
> ├── dev
|
> ├── dev
|
||||||
> │ └── kustomization.yaml
|
> │ └── kustomization.yaml
|
||||||
> └── prod
|
> └── prod
|
||||||
> └── kustomization.yaml
|
> └── kustomization.yaml
|
||||||
> ```
|
> ```
|
||||||
|
|
||||||
Define a helper function to run kustomize with the
|
### Helm related flags
|
||||||
correct environment and flags for plugins:
|
|
||||||
|
|
||||||
<!-- @defineKustomizeIt @helmtest -->
|
Attempt to build the `base`:
|
||||||
|
|
||||||
|
<!-- @checkFailure @testHelm -->
|
||||||
|
```
|
||||||
|
cmd="kustomize build --helm-command $helmCommand $DEMO_HOME/base"
|
||||||
|
if ($cmd); then
|
||||||
|
echo "Build should fail!" && false # Force test to fail.
|
||||||
|
else
|
||||||
|
echo "Build failed because no --enable-helm flag (desired outcome)."
|
||||||
|
fi
|
||||||
|
```
|
||||||
|
|
||||||
|
This `build` fails and complains about a missing
|
||||||
|
`--enable-helm` flag.
|
||||||
|
|
||||||
|
The flag `--enable-helm` exists to have the user
|
||||||
|
acknowledge that kustomize is running an external program as
|
||||||
|
part of the `build` step. It's like the
|
||||||
|
`--enable-plugins` flag, but with a helm focus.
|
||||||
|
|
||||||
|
The flag `--helm-command` has a default value (`helm` of
|
||||||
|
course) so it's not suitable as an enablement flag. A user
|
||||||
|
with `helm` on their `PATH` need not awkwardly specify
|
||||||
|
`'--helm-command helm'`.
|
||||||
|
|
||||||
|
Given the above, define a helper function to run `kustomize` with the
|
||||||
|
flags required for `helm` use in this demo:
|
||||||
|
|
||||||
|
<!-- @defineKustomizeIt @testHelm -->
|
||||||
```
|
```
|
||||||
function kustomizeIt {
|
function kustomizeIt {
|
||||||
XDG_CONFIG_HOME=$DEMO_HOME \
|
kustomize build \
|
||||||
kustomize build --enable_alpha_plugins \
|
--enable-helm \
|
||||||
|
--helm-command $helmCommand \
|
||||||
$DEMO_HOME/$1
|
$DEMO_HOME/$1
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
### Build the base and the variants
|
||||||
|
|
||||||
Finally, build the `prod` variant. Notice that all
|
Now build the `base`:
|
||||||
resource names now have the `prod-` prefix:
|
|
||||||
|
|
||||||
<!-- @doProd @helmtest -->
|
|
||||||
|
|
||||||
```
|
|
||||||
clear
|
|
||||||
kustomizeIt prod
|
|
||||||
```
|
|
||||||
|
|
||||||
Compare `dev` to `prod`:
|
|
||||||
|
|
||||||
<!-- @doCompare -->
|
|
||||||
|
|
||||||
```
|
|
||||||
diff <(kustomizeIt dev) <(kustomizeIt prod) | more
|
|
||||||
```
|
|
||||||
|
|
||||||
To see the unmodified but inflated chart, run kustomize
|
|
||||||
on the base. Every invocation here is re-downloading
|
|
||||||
and re-inflating the chart.
|
|
||||||
|
|
||||||
<!-- @showBase @helmtest -->
|
|
||||||
|
|
||||||
|
<!-- @showBase @testHelm -->
|
||||||
```
|
```
|
||||||
kustomizeIt base
|
kustomizeIt base
|
||||||
```
|
```
|
||||||
|
|
||||||
## Use a local chart
|
This works, and you see an inflated chart complete
|
||||||
|
with a `Secret`, `Service`, `Deployment`, etc.
|
||||||
|
|
||||||
The example above fetches a new copy of the chart
|
As a side effect of this build, kustomize pulled the chart
|
||||||
and render it with each kustomize
|
and placed it in the `charts` subdirectory of the base.
|
||||||
build, because a local chart home isn't specified
|
Take a look:
|
||||||
in the configuration.
|
|
||||||
|
|
||||||
To suppress fetching, specify a _chart home_
|
|
||||||
explicitly, and just make sure the chart is already
|
|
||||||
there.
|
|
||||||
|
|
||||||
To demo this so that it won't interfere with your
|
|
||||||
existing helm environment, do this:
|
|
||||||
|
|
||||||
**This demo uses Helm V3**
|
|
||||||
|
|
||||||
<!-- @helmInit @helmtest -->
|
|
||||||
|
|
||||||
```
|
|
||||||
helmHome=$DEMO_HOME/dothelm
|
|
||||||
chartHome=$DEMO_HOME/base/charts
|
|
||||||
repoUrl=https://kubernetes-charts.storage.googleapis.com
|
|
||||||
|
|
||||||
function doHelm {
|
|
||||||
helm --home $helmHome $@
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Now download a chart; again use _minecraft_
|
|
||||||
(but you could use anything):
|
|
||||||
|
|
||||||
<!-- @fetchChart @helmtest -->
|
|
||||||
|
|
||||||
```
|
|
||||||
doHelm pull --untar \
|
|
||||||
--untardir $chartHome \
|
|
||||||
--repo $repoUrl \
|
|
||||||
minecraft
|
|
||||||
```
|
|
||||||
|
|
||||||
The tree has more stuff now; helm config data
|
|
||||||
and a complete copy of the chart:
|
|
||||||
|
|
||||||
<!-- @tree -->
|
<!-- @tree -->
|
||||||
|
|
||||||
```
|
```
|
||||||
tree $DEMO_HOME
|
tree $DEMO_HOME
|
||||||
```
|
```
|
||||||
|
|
||||||
Add a `chartHome` field to the generator config file so
|
If the chart had already been there, kustomize would
|
||||||
that it knows where to find the local chart:
|
not have tried to pull it.
|
||||||
|
|
||||||
<!-- @modifyGenConfig @helmtest -->
|
To change the location of the charts, use this
|
||||||
|
in your kustomization file:
|
||||||
|
|
||||||
|
> ```
|
||||||
|
> helmGlobals:
|
||||||
|
> chartHome: charts
|
||||||
|
> ```
|
||||||
|
|
||||||
|
Change `charts` as desired, but it's best to keep it
|
||||||
|
in (or below) the same directory as the `kustomization.yaml` file.
|
||||||
|
If it's outside the kustomization root, the `build` command will
|
||||||
|
fail unless given the flag `'--load-restrictor=none'` to
|
||||||
|
disable file loading restrictions.
|
||||||
|
|
||||||
|
Now build the two variants `dev` and `prod`
|
||||||
|
and compare their differences:
|
||||||
|
|
||||||
|
<!-- @doCompare -->
|
||||||
```
|
```
|
||||||
echo "chartHome: $chartHome" >>$DEMO_HOME/base/chartInflator.yaml
|
diff <(kustomizeIt dev) <(kustomizeIt prod) | more
|
||||||
```
|
```
|
||||||
|
|
||||||
Change the values file, to show that the results
|
This shows so-called _last mile hydration_ of two variants
|
||||||
generated below are from the _locally_ stored chart:
|
made from a common base that happens to be generated from a
|
||||||
|
helm chart.
|
||||||
|
|
||||||
<!-- @valueChange @helmtest -->
|
## How does the pull work?
|
||||||
|
|
||||||
|
The command kustomize used to download the chart
|
||||||
|
is something like
|
||||||
|
|
||||||
|
> ```
|
||||||
|
> $helmCommand pull \
|
||||||
|
> --untar \
|
||||||
|
> --untardir $DEMO_HOME/base/charts \
|
||||||
|
> --repo https://itzg.github.io/minecraft-server-charts \
|
||||||
|
> --version 3.1.3 \
|
||||||
|
> minecraft
|
||||||
|
> ```
|
||||||
|
|
||||||
|
The first use of kustomize above (when the `base` was
|
||||||
|
expanded) fetched the chart and placed it in the `charts`
|
||||||
|
directory next to the `kustomization.yaml` file.
|
||||||
|
|
||||||
|
This chart was reused, _not_ re-fetched, with the variant
|
||||||
|
expansions `prod` and `dev`.
|
||||||
|
|
||||||
|
If a chart exists, kustomize will not overwrite it (so to
|
||||||
|
suppress a pull, simply assure the chart is already in your
|
||||||
|
kustomization root). kustomize won't check dates or version
|
||||||
|
numbers or do anything that smells like cache management.
|
||||||
|
|
||||||
|
> kustomize is a YAML manipulator. It's not a manager
|
||||||
|
> of a cache of things downloaded from the internet.
|
||||||
|
|
||||||
|
## The pull happens once.
|
||||||
|
|
||||||
|
To show that the locally stored chart is being re-used, modify
|
||||||
|
its _values_ file.
|
||||||
|
|
||||||
|
First make note of the password encoded in the production
|
||||||
|
inflation:
|
||||||
|
|
||||||
|
<!-- @checkPassword @testHelm -->
|
||||||
```
|
```
|
||||||
sed -i 's/CHANGEME!/SOMETHINGELSE/' $chartHome/minecraft/values.yaml
|
test 1 == $(kustomizeIt prod | grep -c "rcon-password: Q0hBTkdFTUUh")
|
||||||
sed -i 's/LoadBalancer/NodePort/' $chartHome/minecraft/values.yaml
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Finally, built it
|
The above command succeeds if the value of the generated
|
||||||
|
password is as shown (`Q0hBTkdFTUUh`).
|
||||||
|
|
||||||
<!-- @finalProd @helmtest -->
|
Now change the password in the local values file:
|
||||||
|
|
||||||
|
<!-- @valueChange @testHelm -->
|
||||||
```
|
```
|
||||||
kustomizeIt prod
|
values=$DEMO_HOME/base/charts/minecraft/values.yaml
|
||||||
|
|
||||||
|
grep CHANGEME $values
|
||||||
|
sed -i 's/CHANGEME/SOMETHING_ELSE/' $values
|
||||||
|
grep SOMETHING_ELSE $values
|
||||||
```
|
```
|
||||||
|
|
||||||
and observe the change from `LoadBalancer` to `NodePort`, and
|
Run the build, and confirm that the same `rcon-password`
|
||||||
the change in the encoded password.
|
field in the output has a new value, confirming that the
|
||||||
|
chart used was a _local_ chart, not a chart freshly
|
||||||
|
downloaded from the internet:
|
||||||
|
|
||||||
Clean the directory.
|
|
||||||
|
|
||||||
<!-- @showBase @helmtest -->
|
<!-- @checkPassword2 @testHelm -->
|
||||||
|
```
|
||||||
|
test 1 == $(kustomizeIt prod | grep -c "rcon-password: U09NRVRISU5HX0VMU0Uh")
|
||||||
|
```
|
||||||
|
|
||||||
|
Finally, clean up:
|
||||||
|
|
||||||
|
<!-- @showBase @testHelm -->
|
||||||
```
|
```
|
||||||
rm -r $DEMO_HOME
|
rm -r $DEMO_HOME
|
||||||
```
|
```
|
||||||
|
|
||||||
## How to migrate from old plugin to builtin plugin
|
## Performance
|
||||||
|
|
||||||
[bash-based helm chart inflator]: https://github.com/kubernetes-sigs/kustomize/tree/master/plugin/someteam.example.com/v1/chartinflator
|
To recap, the helm-related kustomization fields make
|
||||||
[go-based builtin helm chart inflator]: https://github.com/kubernetes-sigs/kustomize/tree/master/plugin/builtin/helmchartinflationgenerator
|
kustomize run
|
||||||
|
|
||||||
The [bash-based helm chart inflator] is intended as an example of using bash
|
> ```
|
||||||
to write a generator plugin.
|
> helm pull ...
|
||||||
|
> helm template ...
|
||||||
|
> ```
|
||||||
|
|
||||||
It proved to be popular for inflating helm charts,
|
_as a convenience for the user_ to generate YAML from a helm chart.
|
||||||
so there's now a [go-based builtin helm chart inflator].
|
|
||||||
|
|
||||||
This newer generator is supported as part of the core code, so anyone using the
|
Helm's `pull` command downloads the chart. Helm's `template`
|
||||||
old bash-based example plugin would probably benefit from switching to the
|
command inflates the chart template, spitting the inflated
|
||||||
newer built-in plugin.
|
template to stdout (where kustomize captures it) rather than
|
||||||
|
immediately sending it to a cluster as `helm install`
|
||||||
|
would.
|
||||||
|
|
||||||
Be advised that at the time of writing, the built-in plugin only supports helm v3.
|
To improve performance, a user can retain the chart after
|
||||||
|
the first pull, and commit the chart to their configuration
|
||||||
|
repository (below the `kustomization.yaml` file that refers
|
||||||
|
to it). kustomize only tries to pull the chart if it's not
|
||||||
|
already there.
|
||||||
|
|
||||||
|
To further improve performance, a user can inflate the
|
||||||
|
chart themselves at the command line, e.g.
|
||||||
|
|
||||||
|
> ```
|
||||||
|
> helm template {releaseName} \
|
||||||
|
> --values {valuesFile} \
|
||||||
|
> --version {version} \
|
||||||
|
> --repo {repo} \
|
||||||
|
> {chartName} > {chartName}.yaml
|
||||||
|
> ```
|
||||||
|
|
||||||
|
then commit the resulting `{chartName}.yaml` file to a git
|
||||||
|
repo as a configuration base, mentioning that file as a
|
||||||
|
`resource` in a `kustomization.yaml` file, e.g.
|
||||||
|
|
||||||
|
> ```
|
||||||
|
> resources:
|
||||||
|
> - minecraft_v3.1.3_Chart.yaml
|
||||||
|
> ```
|
||||||
|
|
||||||
|
The user should choose when or if to refresh their local
|
||||||
|
copy of the chart's inflation. kustomize would have no
|
||||||
|
awareness that the YAML was generated by helm, and kustomize
|
||||||
|
wouldn't run `helm` during the `build`. This is analogous
|
||||||
|
to `Go` module vendoring.
|
||||||
|
|
||||||
|
### But it's not really about performance.
|
||||||
|
|
||||||
|
Although the `helm` related fields discussed above are handy
|
||||||
|
for experimentation and development, it's best to avoid them
|
||||||
|
in production.
|
||||||
|
|
||||||
|
The same argument applies to using _remote_ git URL's in
|
||||||
|
other kustomization fields. Handy for experimentation,
|
||||||
|
but ill-advised in production.
|
||||||
|
|
||||||
|
It's irresponsible to depend on a remote configuration
|
||||||
|
that's _not under your control_. Annoying enablement flags
|
||||||
|
like `'--enable-helm'` are intended to _remind_ one of a
|
||||||
|
risk, but offer zero protection from risk. Further, they
|
||||||
|
are useless are reminders, since __annoying things are
|
||||||
|
immediately scripted away and forgotten__, as was done above
|
||||||
|
in the `kustomizeIt` shell function.
|
||||||
|
|
||||||
|
## Best practice
|
||||||
|
|
||||||
|
Don't use remote configuration that you don't control in
|
||||||
|
production.
|
||||||
|
|
||||||
|
Maintain a _local, inflated fork_ of a remote configuration,
|
||||||
|
and have a human rebase / reinflate that fork from time to
|
||||||
|
time to capture upstream changes.
|
||||||
|
|||||||
@@ -35,9 +35,11 @@ mdrip --mode test --blockTimeOut 15m \
|
|||||||
# TODO: make work for non-linux
|
# TODO: make work for non-linux
|
||||||
if onLinuxAndNotOnRemoteCI; then
|
if onLinuxAndNotOnRemoteCI; then
|
||||||
echo "On linux, and not on remote CI. Running expensive tests."
|
echo "On linux, and not on remote CI. Running expensive tests."
|
||||||
# Requires helm.
|
# TODO: remove the HEAD check once --enable-helm flag released.
|
||||||
make $MYGOBIN/helmV3
|
if [ "$version" == "HEAD" ]; then
|
||||||
mdrip --mode test --label helmtest examples/chart.md
|
make $MYGOBIN/helmV3
|
||||||
|
mdrip --mode test --label testHelm examples/chart.md
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Force outside logic to rebuild kustomize rather than
|
# Force outside logic to rebuild kustomize rather than
|
||||||
|
|||||||
@@ -23,7 +23,9 @@ var theFlags struct {
|
|||||||
enable struct {
|
enable struct {
|
||||||
plugins bool
|
plugins bool
|
||||||
managedByLabel bool
|
managedByLabel bool
|
||||||
|
helm bool
|
||||||
}
|
}
|
||||||
|
helmCommand string
|
||||||
loadRestrictor string
|
loadRestrictor string
|
||||||
reorderOutput string
|
reorderOutput string
|
||||||
fnOptions types.FnPluginLoadingOptions
|
fnOptions types.FnPluginLoadingOptions
|
||||||
@@ -102,6 +104,7 @@ func NewCmdBuild(
|
|||||||
AddFlagEnablePlugins(cmd.Flags())
|
AddFlagEnablePlugins(cmd.Flags())
|
||||||
AddFlagReorderOutput(cmd.Flags())
|
AddFlagReorderOutput(cmd.Flags())
|
||||||
AddFlagEnableManagedbyLabel(cmd.Flags())
|
AddFlagEnableManagedbyLabel(cmd.Flags())
|
||||||
|
AddFlagEnableHelm(cmd.Flags())
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,7 +135,10 @@ func HonorKustomizeFlags(kOpts *krusty.Options) *krusty.Options {
|
|||||||
c := types.EnabledPluginConfig(types.BploUseStaticallyLinked)
|
c := types.EnabledPluginConfig(types.BploUseStaticallyLinked)
|
||||||
c.FnpLoadingOptions = theFlags.fnOptions
|
c.FnpLoadingOptions = theFlags.fnOptions
|
||||||
kOpts.PluginConfig = c
|
kOpts.PluginConfig = c
|
||||||
|
} else {
|
||||||
|
kOpts.PluginConfig.HelmConfig.Enabled = theFlags.enable.helm
|
||||||
}
|
}
|
||||||
|
kOpts.PluginConfig.HelmConfig.Command = theFlags.helmCommand
|
||||||
kOpts.AddManagedbyLabel = isManagedByLabelEnabled()
|
kOpts.AddManagedbyLabel = isManagedByLabelEnabled()
|
||||||
return kOpts
|
return kOpts
|
||||||
}
|
}
|
||||||
|
|||||||
24
kustomize/commands/build/flagenablehelm.go
Normal file
24
kustomize/commands/build/flagenablehelm.go
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
// Copyright 2021 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package build
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/spf13/pflag"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AddFlagEnableHelm adds the --enable-helm flag.
|
||||||
|
// The helm plugin is builtin, meaning it's
|
||||||
|
// enabled independently of --enable-alpha-plugins.
|
||||||
|
func AddFlagEnableHelm(set *pflag.FlagSet) {
|
||||||
|
set.BoolVar(
|
||||||
|
&theFlags.enable.helm,
|
||||||
|
"enable-helm",
|
||||||
|
false,
|
||||||
|
"Enable use of the Helm chart inflator generator.")
|
||||||
|
set.StringVar(
|
||||||
|
&theFlags.helmCommand,
|
||||||
|
"helm-command",
|
||||||
|
"helm", // default
|
||||||
|
"helm command (path to executable)")
|
||||||
|
}
|
||||||
@@ -1,11 +1,8 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
// Helm chart generator
|
// Helm chart inflation generator.
|
||||||
//
|
// Uses helm V3 to generate k8s YAML from a helm chart.
|
||||||
// Fetches the given chart from {ChartRepo}/{ChartName},
|
|
||||||
// and inflates it to stdout, using the given values file.
|
|
||||||
// This generator expects helm V3 or later.
|
|
||||||
|
|
||||||
//go:generate pluginator
|
//go:generate pluginator
|
||||||
package main
|
package main
|
||||||
@@ -13,7 +10,6 @@ package main
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
@@ -23,7 +19,6 @@ import (
|
|||||||
|
|
||||||
"github.com/imdario/mergo"
|
"github.com/imdario/mergo"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"sigs.k8s.io/kustomize/api/filesys"
|
|
||||||
"sigs.k8s.io/kustomize/api/resmap"
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
"sigs.k8s.io/kustomize/api/types"
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
"sigs.k8s.io/yaml"
|
"sigs.k8s.io/yaml"
|
||||||
@@ -32,202 +27,235 @@ import (
|
|||||||
// HelmChartInflationGeneratorPlugin is a plugin to generate resources
|
// HelmChartInflationGeneratorPlugin is a plugin to generate resources
|
||||||
// from a remote or local helm chart.
|
// from a remote or local helm chart.
|
||||||
type HelmChartInflationGeneratorPlugin struct {
|
type HelmChartInflationGeneratorPlugin struct {
|
||||||
h *resmap.PluginHelpers
|
h *resmap.PluginHelpers
|
||||||
types.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
|
types.HelmGlobals
|
||||||
runHelmCommand func([]string) ([]byte, error)
|
types.HelmChart
|
||||||
types.HelmChartArgs
|
|
||||||
tmpDir string
|
tmpDir string
|
||||||
}
|
}
|
||||||
|
|
||||||
//noinspection GoUnusedGlobalVariable
|
//noinspection GoUnusedGlobalVariable
|
||||||
var KustomizePlugin HelmChartInflationGeneratorPlugin
|
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
|
// Config uses the input plugin configurations `config` to setup the generator
|
||||||
// options
|
// 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
|
p.h = h
|
||||||
err := yaml.Unmarshal(config, p)
|
if err = yaml.Unmarshal(config, p); err != nil {
|
||||||
if err != nil {
|
return
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
tmpDir, err := filesys.NewTmpConfirmedDir()
|
return p.validateArgs()
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// EncodeValues for writing
|
// This uses the real file system since tmpDir may be used
|
||||||
func (p *HelmChartInflationGeneratorPlugin) EncodeValues(w io.Writer) error {
|
// by the helm subprocess. Cannot use a chroot jail or fake
|
||||||
d, err := yaml.Marshal(p.ValuesLocal)
|
// filesystem since we allow the user to use previously
|
||||||
if err != nil {
|
// downloaded charts. This is safe since this plugin is
|
||||||
return err
|
// owned by kustomize.
|
||||||
}
|
func (p *HelmChartInflationGeneratorPlugin) establishTmpDir() (err error) {
|
||||||
_, err = w.Write(d)
|
if p.tmpDir != "" {
|
||||||
if err != nil {
|
// already done.
|
||||||
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) {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// we must use use loader to read values file
|
p.tmpDir, err = ioutil.TempDir("", "kustomize-helm-")
|
||||||
b, err := p.h.Loader().Load(p.Values)
|
return err
|
||||||
if err != nil {
|
}
|
||||||
|
|
||||||
|
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
|
return err
|
||||||
}
|
}
|
||||||
path, err := p.writeValuesBytes(b)
|
|
||||||
if err != nil {
|
// ConfigHome is not loaded by the plugin, and can be located anywhere.
|
||||||
return err
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *HelmChartInflationGeneratorPlugin) writeValuesBytes(b []byte) (string, error) {
|
func (p *HelmChartInflationGeneratorPlugin) errIfIllegalValuesMerge() error {
|
||||||
path := filepath.Join(p.ChartHome, p.ChartName, "kustomize-values.yaml")
|
if p.ValuesMerge == "" {
|
||||||
err := ioutil.WriteFile(path, b, 0644)
|
// 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 {
|
if err != nil {
|
||||||
return "", err
|
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
|
// Generate implements generator
|
||||||
func (p *HelmChartInflationGeneratorPlugin) Generate() (resmap.ResMap, error) {
|
func (p *HelmChartInflationGeneratorPlugin) Generate() (rm resmap.ResMap, err error) {
|
||||||
// cleanup
|
defer p.cleanup()
|
||||||
defer os.RemoveAll(p.tmpDir)
|
if err = p.checkHelmVersion(); err != nil {
|
||||||
// check helm version. we only support V3
|
|
||||||
err := p.checkHelmVersion()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// pull the chart
|
if path, exists := p.chartExistsLocally(); !exists {
|
||||||
if !p.checkLocalChart() {
|
if p.Repo == "" {
|
||||||
_, err := p.runHelmCommand(p.getPullCommandArgs())
|
return nil, fmt.Errorf(
|
||||||
if err != nil {
|
"no repo specified for pull, no chart found at '%s'", path)
|
||||||
|
}
|
||||||
|
if _, err := p.runHelmCommand(p.pullCommand()); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if len(p.ValuesInline) > 0 {
|
||||||
// inflator config valuesLocal
|
p.ValuesFile, err = p.createNewMergedValuesFile()
|
||||||
if len(p.ValuesLocal) > 0 {
|
|
||||||
err := p.useValuesLocal()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
err := p.copyValues()
|
p.ValuesFile, err = p.copyValuesFile()
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if err != nil {
|
||||||
// render the charts
|
return nil, err
|
||||||
stdout, err := p.runHelmCommand(p.getTemplateCommandArgs())
|
}
|
||||||
|
var stdout []byte
|
||||||
|
stdout, err = p.runHelmCommand(p.templateCommand())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
rm, rmfErr := p.h.ResmapFactory().NewResMapFromBytes(stdout)
|
rm, err = p.h.ResmapFactory().NewResMapFromBytes(stdout)
|
||||||
if rmfErr == nil {
|
if err == nil {
|
||||||
return rm, nil
|
return rm, nil
|
||||||
}
|
}
|
||||||
// try to remove the contents before first "---" because
|
// try to remove the contents before first "---" because
|
||||||
@@ -236,50 +264,49 @@ func (p *HelmChartInflationGeneratorPlugin) Generate() (resmap.ResMap, error) {
|
|||||||
if idx := strings.Index(stdoutStr, "---"); idx != -1 {
|
if idx := strings.Index(stdoutStr, "---"); idx != -1 {
|
||||||
return p.h.ResmapFactory().NewResMapFromBytes([]byte(stdoutStr[idx:]))
|
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"}
|
args := []string{"template"}
|
||||||
if p.ReleaseName != "" {
|
if p.ReleaseName != "" {
|
||||||
args = append(args, p.ReleaseName)
|
args = append(args, p.ReleaseName)
|
||||||
}
|
}
|
||||||
args = append(args, filepath.Join(p.ChartHome, p.ChartName))
|
args = append(args, filepath.Join(p.absChartHome(), p.Name))
|
||||||
if p.ReleaseNamespace != "" {
|
if p.ValuesFile != "" {
|
||||||
args = append(args, "--namespace", p.ReleaseNamespace)
|
args = append(args, "--values", p.ValuesFile)
|
||||||
}
|
}
|
||||||
if p.Values != "" {
|
if p.ReleaseName == "" {
|
||||||
args = append(args, "--values", p.Values)
|
// 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
|
return args
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *HelmChartInflationGeneratorPlugin) getPullCommandArgs() []string {
|
func (p *HelmChartInflationGeneratorPlugin) pullCommand() []string {
|
||||||
args := []string{"pull", "--untar", "--untardir", p.ChartHome}
|
args := []string{
|
||||||
chartName := fmt.Sprintf("%s/%s", p.ChartRepoName, p.ChartName)
|
"pull",
|
||||||
if p.ChartVersion != "" {
|
"--untar",
|
||||||
args = append(args, "--version", p.ChartVersion)
|
"--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
|
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.
|
// local chart home.
|
||||||
func (p *HelmChartInflationGeneratorPlugin) checkLocalChart() bool {
|
func (p *HelmChartInflationGeneratorPlugin) chartExistsLocally() (string, bool) {
|
||||||
path := filepath.Join(p.ChartHome, p.ChartName)
|
path := filepath.Join(p.absChartHome(), p.Name)
|
||||||
s, err := os.Stat(path)
|
s, err := os.Stat(path)
|
||||||
if err != nil {
|
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
|
// checkHelmVersion will return an error if the helm version is not V3
|
||||||
|
|||||||
@@ -1,34 +1,30 @@
|
|||||||
package main_test
|
package main_test
|
||||||
|
|
||||||
/*
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"path/filepath"
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"sigs.k8s.io/kustomize/api/filesys"
|
|
||||||
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
|
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||||
)
|
)
|
||||||
|
|
||||||
//add tests for https://github.com/kubernetes-sigs/kustomize/pull/3713 if you plan to re-enable tests
|
|
||||||
|
|
||||||
func TestHelmChartInflationGenerator(t *testing.T) {
|
func TestHelmChartInflationGenerator(t *testing.T) {
|
||||||
th := kusttest_test.MakeEnhancedHarness(t).
|
th := kusttest_test.MakeEnhancedHarnessWithTmpRoot(t).
|
||||||
PrepBuiltin("HelmChartInflationGenerator")
|
PrepBuiltin("HelmChartInflationGenerator")
|
||||||
defer th.Reset()
|
defer th.Reset()
|
||||||
|
if err := th.ErrIfNoHelm(); err != nil {
|
||||||
|
t.Skip("skipping: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
rm := th.LoadAndRunGenerator(`
|
rm := th.LoadAndRunGenerator(`
|
||||||
apiVersion: builtin
|
apiVersion: builtin
|
||||||
kind: HelmChartInflationGenerator
|
kind: HelmChartInflationGenerator
|
||||||
metadata:
|
metadata:
|
||||||
name: myMap
|
name: myMc
|
||||||
chartName: minecraft
|
name: minecraft
|
||||||
chartRepoUrl: https://charts.helm.sh/stable
|
version: 3.1.3
|
||||||
chartVersion: v1.2.0
|
repo: https://itzg.github.io/minecraft-server-charts
|
||||||
releaseName: test
|
releaseName: moria
|
||||||
releaseNamespace: testNamespace
|
|
||||||
`)
|
`)
|
||||||
|
|
||||||
th.AssertActualEqualsExpected(rm, `
|
th.AssertActualEqualsExpected(rm, `
|
||||||
@@ -38,40 +34,23 @@ data:
|
|||||||
kind: Secret
|
kind: Secret
|
||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
app: test-minecraft
|
app: moria-minecraft
|
||||||
chart: minecraft-1.2.0
|
chart: minecraft-3.1.3
|
||||||
heritage: Helm
|
heritage: Helm
|
||||||
release: test
|
release: moria
|
||||||
name: test-minecraft
|
name: moria-minecraft
|
||||||
type: Opaque
|
type: Opaque
|
||||||
---
|
---
|
||||||
apiVersion: v1
|
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
|
kind: Service
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations: {}
|
||||||
labels:
|
labels:
|
||||||
app: test-minecraft
|
app: moria-minecraft
|
||||||
chart: minecraft-1.2.0
|
chart: minecraft-3.1.3
|
||||||
heritage: Helm
|
heritage: Helm
|
||||||
release: test
|
release: moria
|
||||||
name: test-minecraft
|
name: moria-minecraft
|
||||||
spec:
|
spec:
|
||||||
ports:
|
ports:
|
||||||
- name: minecraft
|
- name: minecraft
|
||||||
@@ -79,93 +58,35 @@ spec:
|
|||||||
protocol: TCP
|
protocol: TCP
|
||||||
targetPort: minecraft
|
targetPort: minecraft
|
||||||
selector:
|
selector:
|
||||||
app: test-minecraft
|
app: moria-minecraft
|
||||||
type: LoadBalancer
|
type: ClusterIP
|
||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHelmChartInflationGeneratorWithValues(t *testing.T) {
|
const expectedInflationFmt = `
|
||||||
th := kusttest_test.MakeEnhancedHarness(t).
|
|
||||||
PrepBuiltin("HelmChartInflationGenerator")
|
|
||||||
defer th.Reset()
|
|
||||||
|
|
||||||
tempDirConfirmed, err := filesys.NewTmpConfirmedDir()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
tempDir := string(tempDirConfirmed)
|
|
||||||
defer os.RemoveAll(tempDir)
|
|
||||||
valuesPath := path.Join(tempDir, "values.yaml")
|
|
||||||
ioutil.WriteFile(valuesPath, []byte(`
|
|
||||||
minecraftServer:
|
|
||||||
eula: TRUE
|
|
||||||
`), 0644)
|
|
||||||
|
|
||||||
rm := th.LoadAndRunGenerator(fmt.Sprintf(`
|
|
||||||
apiVersion: builtin
|
|
||||||
kind: HelmChartInflationGenerator
|
|
||||||
metadata:
|
|
||||||
name: myMap
|
|
||||||
chartName: minecraft
|
|
||||||
chartRepoUrl: https://charts.helm.sh/stable
|
|
||||||
chartVersion: v1.2.0
|
|
||||||
helmBin: helm
|
|
||||||
helmHome: %s
|
|
||||||
chartHome: %s
|
|
||||||
releaseName: test
|
|
||||||
releaseNamespace: testNamespace
|
|
||||||
values: %s
|
|
||||||
valuesLocal:
|
|
||||||
resources:
|
|
||||||
limits:
|
|
||||||
memory: 512Mi
|
|
||||||
cpu: 1000m
|
|
||||||
requests:
|
|
||||||
memory: 512Mi
|
|
||||||
cpu: 200m
|
|
||||||
`, tempDir, tempDir, valuesPath))
|
|
||||||
|
|
||||||
th.AssertActualEqualsExpected(rm, `
|
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
data:
|
data:
|
||||||
rcon-password: Q0hBTkdFTUUh
|
rcon-password: Q0hBTkdFTUUh
|
||||||
kind: Secret
|
kind: Secret
|
||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
app: test-minecraft
|
app: moria-minecraft
|
||||||
chart: minecraft-1.2.0
|
chart: minecraft-3.1.3
|
||||||
heritage: Helm
|
heritage: Helm
|
||||||
release: test
|
release: moria
|
||||||
name: test-minecraft
|
name: moria-minecraft
|
||||||
type: Opaque
|
type: Opaque
|
||||||
---
|
---
|
||||||
apiVersion: v1
|
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
|
kind: Service
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations: {}
|
||||||
labels:
|
labels:
|
||||||
app: test-minecraft
|
app: moria-minecraft
|
||||||
chart: minecraft-1.2.0
|
chart: minecraft-3.1.3
|
||||||
heritage: Helm
|
heritage: Helm
|
||||||
release: test
|
release: moria
|
||||||
name: test-minecraft
|
name: moria-minecraft
|
||||||
spec:
|
spec:
|
||||||
ports:
|
ports:
|
||||||
- name: minecraft
|
- name: minecraft
|
||||||
@@ -173,28 +94,48 @@ spec:
|
|||||||
protocol: TCP
|
protocol: TCP
|
||||||
targetPort: minecraft
|
targetPort: minecraft
|
||||||
selector:
|
selector:
|
||||||
app: test-minecraft
|
app: moria-minecraft
|
||||||
|
type: ClusterIP
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
annotations: {}
|
||||||
|
labels:
|
||||||
|
app: moria-minecraft
|
||||||
|
chart: minecraft-3.1.3
|
||||||
|
heritage: Helm
|
||||||
|
release: moria
|
||||||
|
name: moria-minecraft-rcon
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- name: rcon
|
||||||
|
port: 25575
|
||||||
|
protocol: TCP
|
||||||
|
targetPort: rcon
|
||||||
|
selector:
|
||||||
|
app: moria-minecraft
|
||||||
type: LoadBalancer
|
type: LoadBalancer
|
||||||
---
|
---
|
||||||
apiVersion: apps/v1
|
apiVersion: apps/v1
|
||||||
kind: Deployment
|
kind: Deployment
|
||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
app: test-minecraft
|
app: moria-minecraft
|
||||||
chart: minecraft-1.2.0
|
chart: minecraft-3.1.3
|
||||||
heritage: Helm
|
heritage: Helm
|
||||||
release: test
|
release: moria
|
||||||
name: test-minecraft
|
name: moria-minecraft
|
||||||
spec:
|
spec:
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app: test-minecraft
|
app: moria-minecraft
|
||||||
strategy:
|
strategy:
|
||||||
type: Recreate
|
type: Recreate
|
||||||
template:
|
template:
|
||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
app: test-minecraft
|
app: moria-minecraft
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- env:
|
- env:
|
||||||
@@ -203,9 +144,9 @@ spec:
|
|||||||
- name: TYPE
|
- name: TYPE
|
||||||
value: VANILLA
|
value: VANILLA
|
||||||
- name: VERSION
|
- name: VERSION
|
||||||
value: 1.14.4
|
value: LATEST
|
||||||
- name: DIFFICULTY
|
- name: DIFFICULTY
|
||||||
value: easy
|
value: %s
|
||||||
- name: WHITELIST
|
- name: WHITELIST
|
||||||
value: ""
|
value: ""
|
||||||
- name: OPS
|
- name: OPS
|
||||||
@@ -222,7 +163,7 @@ spec:
|
|||||||
value: "true"
|
value: "true"
|
||||||
- name: ENABLE_COMMAND_BLOCK
|
- name: ENABLE_COMMAND_BLOCK
|
||||||
value: "true"
|
value: "true"
|
||||||
- name: FORCE_gameMode
|
- name: FORCE_GAMEMODE
|
||||||
value: "false"
|
value: "false"
|
||||||
- name: GENERATE_STRUCTURES
|
- name: GENERATE_STRUCTURES
|
||||||
value: "true"
|
value: "true"
|
||||||
@@ -257,47 +198,48 @@ spec:
|
|||||||
- name: ONLINE_MODE
|
- name: ONLINE_MODE
|
||||||
value: "true"
|
value: "true"
|
||||||
- name: MEMORY
|
- name: MEMORY
|
||||||
value: 512M
|
value: 1024M
|
||||||
- name: JVM_OPTS
|
- name: JVM_OPTS
|
||||||
value: ""
|
value: ""
|
||||||
- name: JVM_XX_OPTS
|
- name: JVM_XX_OPTS
|
||||||
value: ""
|
value: ""
|
||||||
|
- name: ENABLE_RCON
|
||||||
|
value: "true"
|
||||||
|
- name: RCON_PASSWORD
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
key: rcon-password
|
||||||
|
name: moria-minecraft
|
||||||
image: itzg/minecraft-server:latest
|
image: itzg/minecraft-server:latest
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
livenessProbe:
|
livenessProbe:
|
||||||
exec:
|
|
||||||
command:
|
|
||||||
- mcstatus
|
|
||||||
- localhost:25565
|
|
||||||
- status
|
|
||||||
failureThreshold: 10
|
failureThreshold: 10
|
||||||
initialDelaySeconds: 30
|
initialDelaySeconds: 30
|
||||||
periodSeconds: 5
|
periodSeconds: 5
|
||||||
successThreshold: 1
|
successThreshold: 1
|
||||||
|
tcpSocket:
|
||||||
|
port: 25565
|
||||||
timeoutSeconds: 1
|
timeoutSeconds: 1
|
||||||
name: test-minecraft
|
name: moria-minecraft
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 25565
|
- containerPort: 25565
|
||||||
name: minecraft
|
name: minecraft
|
||||||
protocol: TCP
|
protocol: TCP
|
||||||
|
- containerPort: 25575
|
||||||
|
name: rcon
|
||||||
|
protocol: TCP
|
||||||
readinessProbe:
|
readinessProbe:
|
||||||
exec:
|
|
||||||
command:
|
|
||||||
- mcstatus
|
|
||||||
- localhost:25565
|
|
||||||
- status
|
|
||||||
failureThreshold: 10
|
failureThreshold: 10
|
||||||
initialDelaySeconds: 30
|
initialDelaySeconds: 30
|
||||||
periodSeconds: 5
|
periodSeconds: 5
|
||||||
successThreshold: 1
|
successThreshold: 1
|
||||||
|
tcpSocket:
|
||||||
|
port: 25565
|
||||||
timeoutSeconds: 1
|
timeoutSeconds: 1
|
||||||
resources:
|
resources:
|
||||||
limits:
|
|
||||||
cpu: 1000m
|
|
||||||
memory: 512Mi
|
|
||||||
requests:
|
requests:
|
||||||
cpu: 200m
|
cpu: %dm
|
||||||
memory: 512Mi
|
memory: %dMi
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
- mountPath: /data
|
- mountPath: /data
|
||||||
name: datadir
|
name: datadir
|
||||||
@@ -305,9 +247,110 @@ spec:
|
|||||||
fsGroup: 2000
|
fsGroup: 2000
|
||||||
runAsUser: 1000
|
runAsUser: 1000
|
||||||
volumes:
|
volumes:
|
||||||
- name: datadir
|
- emptyDir: {}
|
||||||
persistentVolumeClaim:
|
name: datadir
|
||||||
claimName: test-minecraft-datadir
|
`
|
||||||
|
|
||||||
|
func TestHelmChartInflationGeneratorWithValuesInlineOverride(t *testing.T) {
|
||||||
|
th := kusttest_test.MakeEnhancedHarnessWithTmpRoot(t).
|
||||||
|
PrepBuiltin("HelmChartInflationGenerator")
|
||||||
|
defer th.Reset()
|
||||||
|
if err := th.ErrIfNoHelm(); err != nil {
|
||||||
|
t.Skip("skipping: " + err.Error())
|
||||||
|
}
|
||||||
|
rm := th.LoadAndRunGenerator(`
|
||||||
|
apiVersion: builtin
|
||||||
|
kind: HelmChartInflationGenerator
|
||||||
|
metadata:
|
||||||
|
name: myMc
|
||||||
|
name: minecraft
|
||||||
|
version: 3.1.3
|
||||||
|
repo: https://itzg.github.io/minecraft-server-charts
|
||||||
|
releaseName: moria
|
||||||
|
valuesInline:
|
||||||
|
minecraftServer:
|
||||||
|
eula: true
|
||||||
|
difficulty: hard
|
||||||
|
rcon:
|
||||||
|
enabled: true
|
||||||
`)
|
`)
|
||||||
|
th.AssertActualEqualsExpected(
|
||||||
|
rm, fmt.Sprintf(expectedInflationFmt,
|
||||||
|
"hard", // difficulty
|
||||||
|
500, // cpu
|
||||||
|
512, // memory
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHelmChartInflationGeneratorWithLocalValuesFile(t *testing.T) {
|
||||||
|
th := kusttest_test.MakeEnhancedHarnessWithTmpRoot(t).
|
||||||
|
PrepBuiltin("HelmChartInflationGenerator")
|
||||||
|
defer th.Reset()
|
||||||
|
if err := th.ErrIfNoHelm(); err != nil {
|
||||||
|
t.Skip("skipping: " + err.Error())
|
||||||
|
}
|
||||||
|
th.WriteF(filepath.Join(th.GetRoot(), "myValues.yaml"), `
|
||||||
|
minecraftServer:
|
||||||
|
eula: true
|
||||||
|
difficulty: peaceful
|
||||||
|
rcon:
|
||||||
|
enabled: true
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 888m
|
||||||
|
memory: 666Mi
|
||||||
|
`)
|
||||||
|
rm := th.LoadAndRunGenerator(`
|
||||||
|
apiVersion: builtin
|
||||||
|
kind: HelmChartInflationGenerator
|
||||||
|
metadata:
|
||||||
|
name: myMc
|
||||||
|
name: minecraft
|
||||||
|
version: 3.1.3
|
||||||
|
repo: https://itzg.github.io/minecraft-server-charts
|
||||||
|
releaseName: moria
|
||||||
|
valuesFile: myValues.yaml
|
||||||
|
`)
|
||||||
|
th.AssertActualEqualsExpected(
|
||||||
|
rm, fmt.Sprintf(expectedInflationFmt,
|
||||||
|
"peaceful", // difficulty
|
||||||
|
888, // cpu
|
||||||
|
666, // memory
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHelmChartInflationGeneratorWithInLineReplace(t *testing.T) {
|
||||||
|
th := kusttest_test.MakeEnhancedHarnessWithTmpRoot(t).
|
||||||
|
PrepBuiltin("HelmChartInflationGenerator")
|
||||||
|
defer th.Reset()
|
||||||
|
if err := th.ErrIfNoHelm(); err != nil {
|
||||||
|
t.Skip("skipping: " + err.Error())
|
||||||
|
}
|
||||||
|
rm := th.LoadAndRunGenerator(`
|
||||||
|
apiVersion: builtin
|
||||||
|
kind: HelmChartInflationGenerator
|
||||||
|
metadata:
|
||||||
|
name: myMc
|
||||||
|
name: minecraft
|
||||||
|
version: 3.1.3
|
||||||
|
repo: https://itzg.github.io/minecraft-server-charts
|
||||||
|
releaseName: moria
|
||||||
|
valuesInline:
|
||||||
|
minecraftServer:
|
||||||
|
eula: true
|
||||||
|
difficulty: OMG_PLEASE_NO
|
||||||
|
rcon:
|
||||||
|
enabled: true
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 555m
|
||||||
|
memory: 111Mi
|
||||||
|
valuesMerge: replace
|
||||||
|
`)
|
||||||
|
th.AssertActualEqualsExpected(
|
||||||
|
rm, fmt.Sprintf(expectedInflationFmt,
|
||||||
|
"OMG_PLEASE_NO", // difficulty
|
||||||
|
555, // cpu
|
||||||
|
111, // memory
|
||||||
|
))
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm
|
|||||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||||
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||||
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||||
|
github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M=
|
||||||
github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
@@ -102,6 +103,7 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a
|
|||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
||||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
||||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
@@ -159,6 +161,7 @@ github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40T
|
|||||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||||
|
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
||||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
@@ -191,6 +194,7 @@ go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
|||||||
go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
||||||
go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
||||||
go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
||||||
|
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 h1:+FNtrFTmVw0YZGpBGX56XDee331t6JAXeK2bcyhLOOc=
|
||||||
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o=
|
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o=
|
||||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||||
|
|||||||
Reference in New Issue
Block a user