(feat)helm: Allow dynamic dependency override (#2205)

Add the --requirements flag to install and upgrade commands

Signed-off-by: Fuxin Hao <haofxpro@gmail.com>
pull/4764/head
Fuxin Hao 7 years ago
parent 8061227ce1
commit 972bb3cade

@ -114,6 +114,7 @@ type installCmd struct {
name string name string
namespace string namespace string
valueFiles valueFiles valueFiles valueFiles
reqFile string
chartPath string chartPath string
dryRun bool dryRun bool
disableHooks bool disableHooks bool
@ -195,6 +196,7 @@ func newInstallCmd(c helm.Interface, out io.Writer) *cobra.Command {
f := cmd.Flags() f := cmd.Flags()
settings.AddFlagsTLS(f) settings.AddFlagsTLS(f)
f.VarP(&inst.valueFiles, "values", "f", "specify values in a YAML file or a URL(can specify multiple)") f.VarP(&inst.valueFiles, "values", "f", "specify values in a YAML file or a URL(can specify multiple)")
f.StringVarP(&inst.reqFile, "requirements", "r", "", "specify a yaml file to override dependencies(specify 'none' to remove all dependencies)")
f.StringVarP(&inst.name, "name", "n", "", "release name. If unspecified, it will autogenerate one for you") f.StringVarP(&inst.name, "name", "n", "", "release name. If unspecified, it will autogenerate one for you")
f.StringVar(&inst.namespace, "namespace", "", "namespace to install the release into. Defaults to the current kube config namespace.") f.StringVar(&inst.namespace, "namespace", "", "namespace to install the release into. Defaults to the current kube config namespace.")
f.BoolVar(&inst.dryRun, "dry-run", false, "simulate an install") f.BoolVar(&inst.dryRun, "dry-run", false, "simulate an install")
@ -254,6 +256,18 @@ func (i *installCmd) run() error {
return prettyError(err) return prettyError(err)
} }
// Override the requirements
if i.reqFile != "" {
if err := chartutil.SetRequirements(chartRequested, i.reqFile); err != nil {
return prettyError(err)
}
// Run `helm dependency update` to update dependencies
i.depUp = true
// Ignore the existing dependencies
chartRequested.Dependencies = nil
}
if req, err := chartutil.LoadRequirements(chartRequested); err == nil { if req, err := chartutil.LoadRequirements(chartRequested); err == nil {
// If checkDependencies returns an error, we have unfulfilled dependencies. // If checkDependencies returns an error, we have unfulfilled dependencies.
// As of Helm 2.4.0, this is treated as a stopping condition: // As of Helm 2.4.0, this is treated as a stopping condition:
@ -268,6 +282,9 @@ func (i *installCmd) run() error {
SkipUpdate: false, SkipUpdate: false,
Getters: getter.All(settings), Getters: getter.All(settings),
} }
if i.reqFile != "" {
man.Requirements = req
}
if err := man.Update(); err != nil { if err := man.Update(); err != nil {
return prettyError(err) return prettyError(err)
} }
@ -277,10 +294,18 @@ func (i *installCmd) run() error {
if err != nil { if err != nil {
return prettyError(err) return prettyError(err)
} }
if i.reqFile != "" {
if err := chartutil.SetRequirements(chartRequested, i.reqFile); err != nil {
return prettyError(err)
}
if err := chartutil.RemoveDependenciesNotPresent(chartRequested); err != nil {
return prettyError(err)
}
}
} else { } else {
return prettyError(err) return prettyError(err)
} }
} }
} else if err != chartutil.ErrRequirementsNotFound { } else if err != chartutil.ErrRequirementsNotFound {
return fmt.Errorf("cannot load requirements: %v", err) return fmt.Errorf("cannot load requirements: %v", err)

@ -24,6 +24,8 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/helm/pkg/chartutil" "k8s.io/helm/pkg/chartutil"
"k8s.io/helm/pkg/downloader"
"k8s.io/helm/pkg/getter"
"k8s.io/helm/pkg/helm" "k8s.io/helm/pkg/helm"
"k8s.io/helm/pkg/renderutil" "k8s.io/helm/pkg/renderutil"
storageerrors "k8s.io/helm/pkg/storage/errors" storageerrors "k8s.io/helm/pkg/storage/errors"
@ -96,6 +98,7 @@ type upgradeCmd struct {
values []string values []string
stringValues []string stringValues []string
fileValues []string fileValues []string
reqFile string
verify bool verify bool
keyring string keyring string
install bool install bool
@ -149,6 +152,7 @@ func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command {
f := cmd.Flags() f := cmd.Flags()
settings.AddFlagsTLS(f) settings.AddFlagsTLS(f)
f.VarP(&upgrade.valueFiles, "values", "f", "specify values in a YAML file or a URL(can specify multiple)") f.VarP(&upgrade.valueFiles, "values", "f", "specify values in a YAML file or a URL(can specify multiple)")
f.StringVarP(&upgrade.reqFile, "requirements", "r", "", "specify a yaml file to override dependencies(specify 'none' to remove all dependencies)")
f.BoolVar(&upgrade.dryRun, "dry-run", false, "simulate an upgrade") f.BoolVar(&upgrade.dryRun, "dry-run", false, "simulate an upgrade")
f.BoolVar(&upgrade.recreate, "recreate-pods", false, "performs pods restart for the resource if applicable") f.BoolVar(&upgrade.recreate, "recreate-pods", false, "performs pods restart for the resource if applicable")
f.BoolVar(&upgrade.force, "force", false, "force resource update through delete/recreate if needed") f.BoolVar(&upgrade.force, "force", false, "force resource update through delete/recreate if needed")
@ -219,6 +223,7 @@ func (u *upgradeCmd) run() error {
out: u.out, out: u.out,
name: u.release, name: u.release,
valueFiles: u.valueFiles, valueFiles: u.valueFiles,
reqFile: u.reqFile,
dryRun: u.dryRun, dryRun: u.dryRun,
verify: u.verify, verify: u.verify,
disableHooks: u.disableHooks, disableHooks: u.disableHooks,
@ -240,22 +245,60 @@ func (u *upgradeCmd) run() error {
return err return err
} }
ch, err := chartutil.Load(chartPath)
if err != nil {
return prettyError(err)
}
// Override requirements
if u.reqFile != "" {
if err = chartutil.SetRequirements(ch, u.reqFile); err != nil {
return prettyError(err)
}
// Ignore existing dependencies
ch.Dependencies = nil
}
// Check chart requirements to make sure all dependencies are present in /charts // Check chart requirements to make sure all dependencies are present in /charts
if ch, err := chartutil.Load(chartPath); err == nil { if req, err := chartutil.LoadRequirements(ch); err == nil {
if req, err := chartutil.LoadRequirements(ch); err == nil { if err := renderutil.CheckDependencies(ch, req); err != nil {
if err := renderutil.CheckDependencies(ch, req); err != nil { if u.reqFile != "" {
return err man := &downloader.Manager{
Out: u.out,
ChartPath: chartPath,
HelmHome: settings.Home,
Keyring: defaultKeyring(),
SkipUpdate: false,
Getters: getter.All(settings),
Requirements: req,
}
if err := man.Update(); err != nil {
return prettyError(err)
}
// Update all dependencies which are present in /charts.
ch, err = chartutil.Load(chartPath)
if err != nil {
return prettyError(err)
}
if err := chartutil.SetRequirements(ch, u.reqFile); err != nil {
return prettyError(err)
}
if err := chartutil.RemoveDependenciesNotPresent(ch); err != nil {
return prettyError(err)
}
} else {
return prettyError(err)
} }
} else if err != chartutil.ErrRequirementsNotFound {
return fmt.Errorf("cannot load requirements: %v", err)
} }
} else { } else if err != chartutil.ErrRequirementsNotFound {
return prettyError(err) return fmt.Errorf("cannot load requirements: %v", err)
} }
resp, err := u.client.UpdateRelease( resp, err := u.client.UpdateReleaseFromChart(
u.release, u.release,
chartPath, ch,
helm.UpdateValueOverrides(rawVals), helm.UpdateValueOverrides(rawVals),
helm.UpgradeDryRun(u.dryRun), helm.UpgradeDryRun(u.dryRun),
helm.UpgradeRecreate(u.recreate), helm.UpgradeRecreate(u.recreate),

@ -95,6 +95,7 @@ helm install [CHART] [flags]
--password string chart repository password where to locate the requested chart --password string chart repository password where to locate the requested chart
--replace re-use the given name, even if that name is already used. This is unsafe in production --replace re-use the given name, even if that name is already used. This is unsafe in production
--repo string chart repository url where to locate the requested chart --repo string chart repository url where to locate the requested chart
-r, --requirements string specify a yaml file to override dependencies(specify 'none' to remove all dependencies)
--set stringArray set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2) --set stringArray set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)
--set-file stringArray set values from respective files specified via the command line (can specify multiple or separate values with commas: key1=path1,key2=path2) --set-file stringArray set values from respective files specified via the command line (can specify multiple or separate values with commas: key1=path1,key2=path2)
--set-string stringArray set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2) --set-string stringArray set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)
@ -128,4 +129,4 @@ helm install [CHART] [flags]
* [helm](helm.md) - The Helm package manager for Kubernetes. * [helm](helm.md) - The Helm package manager for Kubernetes.
###### Auto generated by spf13/cobra on 10-Aug-2018 ###### Auto generated by spf13/cobra on 16-Oct-2018

@ -80,6 +80,7 @@ helm upgrade [RELEASE] [CHART] [flags]
--password string chart repository password where to locate the requested chart --password string chart repository password where to locate the requested chart
--recreate-pods performs pods restart for the resource if applicable --recreate-pods performs pods restart for the resource if applicable
--repo string chart repository url where to locate the requested chart --repo string chart repository url where to locate the requested chart
-r, --requirements string specify a yaml file to override dependencies(specify 'none' to remove all dependencies)
--reset-values when upgrading, reset the values to the ones built into the chart --reset-values when upgrading, reset the values to the ones built into the chart
--reuse-values when upgrading, reuse the last release's values and merge in any overrides from the command line via --set and -f. If '--reset-values' is specified, this is ignored. --reuse-values when upgrading, reuse the last release's values and merge in any overrides from the command line via --set and -f. If '--reset-values' is specified, this is ignored.
--set stringArray set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2) --set stringArray set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)
@ -115,4 +116,4 @@ helm upgrade [RELEASE] [CHART] [flags]
* [helm](helm.md) - The Helm package manager for Kubernetes. * [helm](helm.md) - The Helm package manager for Kubernetes.
###### Auto generated by spf13/cobra on 24-Aug-2018 ###### Auto generated by spf13/cobra on 16-Oct-2018

@ -17,6 +17,8 @@ package chartutil
import ( import (
"errors" "errors"
"fmt"
"io/ioutil"
"log" "log"
"strings" "strings"
"time" "time"
@ -27,8 +29,9 @@ import (
) )
const ( const (
requirementsName = "requirements.yaml" requirementsName = "requirements.yaml"
lockfileName = "requirements.lock" lockfileName = "requirements.lock"
removeRequirementsName = "none"
) )
var ( var (
@ -123,6 +126,46 @@ func LoadRequirementsLock(c *chart.Chart) (*RequirementsLock, error) {
return r, yaml.Unmarshal(data, r) return r, yaml.Unmarshal(data, r)
} }
// SetRequirements sets a requirements file from an in-memory chart.
func SetRequirements(c *chart.Chart, filename string) error {
var data []byte
if filename == removeRequirementsName {
data = []byte("dependencies:")
} else {
var err error
data, err = ioutil.ReadFile(filename)
if err != nil {
return fmt.Errorf("error reading %s: %s", filename, err)
}
}
for _, f := range c.Files {
if f.TypeUrl == requirementsName {
f.Value = data
}
}
return nil
}
// RemoveDependenciesNotPresent removes the charts not present in requirements
func RemoveDependenciesNotPresent(c *chart.Chart) error {
var deps []*chart.Chart
req, err := LoadRequirements(c)
if err != nil {
return err
}
for _, depInRequestments := range req.Dependencies {
for _, dep := range c.Dependencies {
if depInRequestments.Name == dep.Metadata.Name {
deps = append(deps, dep)
}
}
}
c.Dependencies = deps
return nil
}
// ProcessRequirementsConditions disables charts based on condition path value in values // ProcessRequirementsConditions disables charts based on condition path value in values
func ProcessRequirementsConditions(reqs *Requirements, cvals Values) { func ProcessRequirementsConditions(reqs *Requirements, cvals Values) {
var cond string var cond string

@ -57,6 +57,8 @@ type Manager struct {
SkipUpdate bool SkipUpdate bool
// Getter collection for the operation // Getter collection for the operation
Getters []getter.Provider Getters []getter.Provider
// Requirements is the requirements override
Requirements *chartutil.Requirements
} }
// Build rebuilds a local charts directory from a lockfile. // Build rebuilds a local charts directory from a lockfile.
@ -113,15 +115,20 @@ func (m *Manager) Update() error {
return err return err
} }
// If no requirements file is found, we consider this a successful var req *chartutil.Requirements
// completion. if m.Requirements == nil {
req, err := chartutil.LoadRequirements(c) // If no requirements file is found, we consider this a successful
if err != nil { // completion.
if err == chartutil.ErrRequirementsNotFound { req, err = chartutil.LoadRequirements(c)
fmt.Fprintf(m.Out, "No requirements found in %s/charts.\n", m.ChartPath) if err != nil {
return nil if err == chartutil.ErrRequirementsNotFound {
fmt.Fprintf(m.Out, "No requirements found in %s/charts.\n", m.ChartPath)
return nil
}
return err
} }
return err } else {
req = m.Requirements
} }
// Hash requirements.yaml // Hash requirements.yaml

Loading…
Cancel
Save