(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
namespace string
valueFiles valueFiles
reqFile string
chartPath string
dryRun bool
disableHooks bool
@ -195,6 +196,7 @@ func newInstallCmd(c helm.Interface, out io.Writer) *cobra.Command {
f := cmd.Flags()
settings.AddFlagsTLS(f)
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.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")
@ -254,6 +256,18 @@ func (i *installCmd) run() error {
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 checkDependencies returns an error, we have unfulfilled dependencies.
// As of Helm 2.4.0, this is treated as a stopping condition:
@ -268,6 +282,9 @@ func (i *installCmd) run() error {
SkipUpdate: false,
Getters: getter.All(settings),
}
if i.reqFile != "" {
man.Requirements = req
}
if err := man.Update(); err != nil {
return prettyError(err)
}
@ -277,10 +294,18 @@ func (i *installCmd) run() error {
if err != nil {
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 {
return prettyError(err)
}
}
} else if err != chartutil.ErrRequirementsNotFound {
return fmt.Errorf("cannot load requirements: %v", err)

@ -24,6 +24,8 @@ import (
"github.com/spf13/cobra"
"k8s.io/helm/pkg/chartutil"
"k8s.io/helm/pkg/downloader"
"k8s.io/helm/pkg/getter"
"k8s.io/helm/pkg/helm"
"k8s.io/helm/pkg/renderutil"
storageerrors "k8s.io/helm/pkg/storage/errors"
@ -96,6 +98,7 @@ type upgradeCmd struct {
values []string
stringValues []string
fileValues []string
reqFile string
verify bool
keyring string
install bool
@ -149,6 +152,7 @@ func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command {
f := cmd.Flags()
settings.AddFlagsTLS(f)
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.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")
@ -219,6 +223,7 @@ func (u *upgradeCmd) run() error {
out: u.out,
name: u.release,
valueFiles: u.valueFiles,
reqFile: u.reqFile,
dryRun: u.dryRun,
verify: u.verify,
disableHooks: u.disableHooks,
@ -240,22 +245,60 @@ func (u *upgradeCmd) run() error {
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
if ch, err := chartutil.Load(chartPath); err == nil {
if req, err := chartutil.LoadRequirements(ch); err == nil {
if err := renderutil.CheckDependencies(ch, req); err != nil {
return err
if req, err := chartutil.LoadRequirements(ch); err == nil {
if err := renderutil.CheckDependencies(ch, req); err != nil {
if u.reqFile != "" {
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 {
return prettyError(err)
} else if err != chartutil.ErrRequirementsNotFound {
return fmt.Errorf("cannot load requirements: %v", err)
}
resp, err := u.client.UpdateRelease(
resp, err := u.client.UpdateReleaseFromChart(
u.release,
chartPath,
ch,
helm.UpdateValueOverrides(rawVals),
helm.UpgradeDryRun(u.dryRun),
helm.UpgradeRecreate(u.recreate),

@ -95,6 +95,7 @@ helm install [CHART] [flags]
--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
--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-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)
@ -128,4 +129,4 @@ helm install [CHART] [flags]
* [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
--recreate-pods performs pods restart for the resource if applicable
--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
--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)
@ -115,4 +116,4 @@ helm upgrade [RELEASE] [CHART] [flags]
* [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 (
"errors"
"fmt"
"io/ioutil"
"log"
"strings"
"time"
@ -27,8 +29,9 @@ import (
)
const (
requirementsName = "requirements.yaml"
lockfileName = "requirements.lock"
requirementsName = "requirements.yaml"
lockfileName = "requirements.lock"
removeRequirementsName = "none"
)
var (
@ -123,6 +126,46 @@ func LoadRequirementsLock(c *chart.Chart) (*RequirementsLock, error) {
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
func ProcessRequirementsConditions(reqs *Requirements, cvals Values) {
var cond string

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

Loading…
Cancel
Save