github.com/stefanmcshane/helm@v0.0.0-20221213002717-88a4a2c6e77d/cmd/helm/install.go (about) 1 /* 2 Copyright The Helm Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package main 18 19 import ( 20 "context" 21 "fmt" 22 "io" 23 "log" 24 "os" 25 "os/signal" 26 "syscall" 27 "time" 28 29 "github.com/pkg/errors" 30 "github.com/spf13/cobra" 31 "github.com/spf13/pflag" 32 33 "github.com/stefanmcshane/helm/cmd/helm/require" 34 "github.com/stefanmcshane/helm/pkg/action" 35 "github.com/stefanmcshane/helm/pkg/chart" 36 "github.com/stefanmcshane/helm/pkg/chart/loader" 37 "github.com/stefanmcshane/helm/pkg/cli/output" 38 "github.com/stefanmcshane/helm/pkg/cli/values" 39 "github.com/stefanmcshane/helm/pkg/downloader" 40 "github.com/stefanmcshane/helm/pkg/getter" 41 "github.com/stefanmcshane/helm/pkg/release" 42 ) 43 44 const installDesc = ` 45 This command installs a chart archive. 46 47 The install argument must be a chart reference, a path to a packaged chart, 48 a path to an unpacked chart directory or a URL. 49 50 To override values in a chart, use either the '--values' flag and pass in a file 51 or use the '--set' flag and pass configuration from the command line, to force 52 a string value use '--set-string'. You can use '--set-file' to set individual 53 values from a file when the value itself is too long for the command line 54 or is dynamically generated. You can also use '--set-json' to set json values 55 (scalars/objects/arrays) from the command line. 56 57 $ helm install -f myvalues.yaml myredis ./redis 58 59 or 60 61 $ helm install --set name=prod myredis ./redis 62 63 or 64 65 $ helm install --set-string long_int=1234567890 myredis ./redis 66 67 or 68 69 $ helm install --set-file my_script=dothings.sh myredis ./redis 70 71 or 72 73 $ helm install --set-json 'master.sidecars=[{"name":"sidecar","image":"myImage","imagePullPolicy":"Always","ports":[{"name":"portname","containerPort":1234}]}]' myredis ./redis 74 75 76 You can specify the '--values'/'-f' flag multiple times. The priority will be given to the 77 last (right-most) file specified. For example, if both myvalues.yaml and override.yaml 78 contained a key called 'Test', the value set in override.yaml would take precedence: 79 80 $ helm install -f myvalues.yaml -f override.yaml myredis ./redis 81 82 You can specify the '--set' flag multiple times. The priority will be given to the 83 last (right-most) set specified. For example, if both 'bar' and 'newbar' values are 84 set for a key called 'foo', the 'newbar' value would take precedence: 85 86 $ helm install --set foo=bar --set foo=newbar myredis ./redis 87 88 Similarly, in the following example 'foo' is set to '["four"]': 89 90 $ helm install --set-json='foo=["one", "two", "three"]' --set-json='foo=["four"]' myredis ./redis 91 92 And in the following example, 'foo' is set to '{"key1":"value1","key2":"bar"}': 93 94 $ helm install --set-json='foo={"key1":"value1","key2":"value2"}' --set-json='foo.key2="bar"' myredis ./redis 95 96 To check the generated manifests of a release without installing the chart, 97 the '--debug' and '--dry-run' flags can be combined. 98 99 If --verify is set, the chart MUST have a provenance file, and the provenance 100 file MUST pass all verification steps. 101 102 There are six different ways you can express the chart you want to install: 103 104 1. By chart reference: helm install mymaria example/mariadb 105 2. By path to a packaged chart: helm install mynginx ./nginx-1.2.3.tgz 106 3. By path to an unpacked chart directory: helm install mynginx ./nginx 107 4. By absolute URL: helm install mynginx https://example.com/charts/nginx-1.2.3.tgz 108 5. By chart reference and repo url: helm install --repo https://example.com/charts/ mynginx nginx 109 6. By OCI registries: helm install mynginx --version 1.2.3 oci://example.com/charts/nginx 110 111 CHART REFERENCES 112 113 A chart reference is a convenient way of referencing a chart in a chart repository. 114 115 When you use a chart reference with a repo prefix ('example/mariadb'), Helm will look in the local 116 configuration for a chart repository named 'example', and will then look for a 117 chart in that repository whose name is 'mariadb'. It will install the latest stable version of that chart 118 until you specify '--devel' flag to also include development version (alpha, beta, and release candidate releases), or 119 supply a version number with the '--version' flag. 120 121 To see the list of chart repositories, use 'helm repo list'. To search for 122 charts in a repository, use 'helm search'. 123 ` 124 125 func newInstallCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { 126 client := action.NewInstall(cfg) 127 valueOpts := &values.Options{} 128 var outfmt output.Format 129 130 cmd := &cobra.Command{ 131 Use: "install [NAME] [CHART]", 132 Short: "install a chart", 133 Long: installDesc, 134 Args: require.MinimumNArgs(1), 135 ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { 136 return compInstall(args, toComplete, client) 137 }, 138 RunE: func(_ *cobra.Command, args []string) error { 139 rel, err := runInstall(args, client, valueOpts, out) 140 if err != nil { 141 return errors.Wrap(err, "INSTALLATION FAILED") 142 } 143 144 return outfmt.Write(out, &statusPrinter{rel, settings.Debug, false}) 145 }, 146 } 147 148 addInstallFlags(cmd, cmd.Flags(), client, valueOpts) 149 bindOutputFlag(cmd, &outfmt) 150 bindPostRenderFlag(cmd, &client.PostRenderer) 151 152 return cmd 153 } 154 155 func addInstallFlags(cmd *cobra.Command, f *pflag.FlagSet, client *action.Install, valueOpts *values.Options) { 156 f.BoolVar(&client.CreateNamespace, "create-namespace", false, "create the release namespace if not present") 157 f.BoolVar(&client.DryRun, "dry-run", false, "simulate an install") 158 f.BoolVar(&client.DisableHooks, "no-hooks", false, "prevent hooks from running during install") 159 f.BoolVar(&client.Replace, "replace", false, "re-use the given name, only if that name is a deleted release which remains in the history. This is unsafe in production") 160 f.DurationVar(&client.Timeout, "timeout", 300*time.Second, "time to wait for any individual Kubernetes operation (like Jobs for hooks)") 161 f.BoolVar(&client.Wait, "wait", false, "if set, will wait until all Pods, PVCs, Services, and minimum number of Pods of a Deployment, StatefulSet, or ReplicaSet are in a ready state before marking the release as successful. It will wait for as long as --timeout") 162 f.BoolVar(&client.WaitForJobs, "wait-for-jobs", false, "if set and --wait enabled, will wait until all Jobs have been completed before marking the release as successful. It will wait for as long as --timeout") 163 f.BoolVarP(&client.GenerateName, "generate-name", "g", false, "generate the name (and omit the NAME parameter)") 164 f.StringVar(&client.NameTemplate, "name-template", "", "specify template used to name the release") 165 f.StringVar(&client.Description, "description", "", "add a custom description") 166 f.BoolVar(&client.Devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored") 167 f.BoolVar(&client.DependencyUpdate, "dependency-update", false, "update dependencies if they are missing before installing the chart") 168 f.BoolVar(&client.DisableOpenAPIValidation, "disable-openapi-validation", false, "if set, the installation process will not validate rendered templates against the Kubernetes OpenAPI Schema") 169 f.BoolVar(&client.Atomic, "atomic", false, "if set, the installation process deletes the installation on failure. The --wait flag will be set automatically if --atomic is used") 170 f.BoolVar(&client.SkipCRDs, "skip-crds", false, "if set, no CRDs will be installed. By default, CRDs are installed if not already present") 171 f.BoolVar(&client.SubNotes, "render-subchart-notes", false, "if set, render subchart notes along with the parent") 172 addValueOptionsFlags(f, valueOpts) 173 addChartPathOptionsFlags(f, &client.ChartPathOptions) 174 175 err := cmd.RegisterFlagCompletionFunc("version", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { 176 requiredArgs := 2 177 if client.GenerateName { 178 requiredArgs = 1 179 } 180 if len(args) != requiredArgs { 181 return nil, cobra.ShellCompDirectiveNoFileComp 182 } 183 return compVersionFlag(args[requiredArgs-1], toComplete) 184 }) 185 186 if err != nil { 187 log.Fatal(err) 188 } 189 } 190 191 func runInstall(args []string, client *action.Install, valueOpts *values.Options, out io.Writer) (*release.Release, error) { 192 debug("Original chart version: %q", client.Version) 193 if client.Version == "" && client.Devel { 194 debug("setting version to >0.0.0-0") 195 client.Version = ">0.0.0-0" 196 } 197 198 name, chart, err := client.NameAndChart(args) 199 if err != nil { 200 return nil, err 201 } 202 client.ReleaseName = name 203 204 cp, err := client.ChartPathOptions.LocateChart(chart, settings) 205 if err != nil { 206 return nil, err 207 } 208 209 debug("CHART PATH: %s\n", cp) 210 211 p := getter.All(settings) 212 vals, err := valueOpts.MergeValues(p) 213 if err != nil { 214 return nil, err 215 } 216 217 // Check chart dependencies to make sure all are present in /charts 218 chartRequested, err := loader.Load(cp) 219 if err != nil { 220 return nil, err 221 } 222 223 if err := checkIfInstallable(chartRequested); err != nil { 224 return nil, err 225 } 226 227 if chartRequested.Metadata.Deprecated { 228 warning("This chart is deprecated") 229 } 230 231 if req := chartRequested.Metadata.Dependencies; req != nil { 232 // If CheckDependencies returns an error, we have unfulfilled dependencies. 233 // As of Helm 2.4.0, this is treated as a stopping condition: 234 // https://github.com/helm/helm/issues/2209 235 if err := action.CheckDependencies(chartRequested, req); err != nil { 236 err = errors.Wrap(err, "An error occurred while checking for chart dependencies. You may need to run `helm dependency build` to fetch missing dependencies") 237 if client.DependencyUpdate { 238 man := &downloader.Manager{ 239 Out: out, 240 ChartPath: cp, 241 Keyring: client.ChartPathOptions.Keyring, 242 SkipUpdate: false, 243 Getters: p, 244 RepositoryConfig: settings.RepositoryConfig, 245 RepositoryCache: settings.RepositoryCache, 246 Debug: settings.Debug, 247 } 248 if err := man.Update(); err != nil { 249 return nil, err 250 } 251 // Reload the chart with the updated Chart.lock file. 252 if chartRequested, err = loader.Load(cp); err != nil { 253 return nil, errors.Wrap(err, "failed reloading chart after repo update") 254 } 255 } else { 256 return nil, err 257 } 258 } 259 } 260 261 client.Namespace = settings.Namespace() 262 263 // Create context and prepare the handle of SIGTERM 264 ctx := context.Background() 265 ctx, cancel := context.WithCancel(ctx) 266 267 // Set up channel on which to send signal notifications. 268 // We must use a buffered channel or risk missing the signal 269 // if we're not ready to receive when the signal is sent. 270 cSignal := make(chan os.Signal, 2) 271 signal.Notify(cSignal, os.Interrupt, syscall.SIGTERM) 272 go func() { 273 <-cSignal 274 fmt.Fprintf(out, "Release %s has been cancelled.\n", args[0]) 275 cancel() 276 }() 277 278 return client.RunWithContext(ctx, chartRequested, vals) 279 } 280 281 // checkIfInstallable validates if a chart can be installed 282 // 283 // Application chart type is only installable 284 func checkIfInstallable(ch *chart.Chart) error { 285 switch ch.Metadata.Type { 286 case "", "application": 287 return nil 288 } 289 return errors.Errorf("%s charts are not installable", ch.Metadata.Type) 290 } 291 292 // Provide dynamic auto-completion for the install and template commands 293 func compInstall(args []string, toComplete string, client *action.Install) ([]string, cobra.ShellCompDirective) { 294 requiredArgs := 1 295 if client.GenerateName { 296 requiredArgs = 0 297 } 298 if len(args) == requiredArgs { 299 return compListCharts(toComplete, true) 300 } 301 return nil, cobra.ShellCompDirectiveNoFileComp 302 }