github.com/Racer159/helm-experiment@v0.0.0-20230822001441-1eb31183f614/src/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 cmd 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 "helm.sh/helm/v3/cmd/helm/require" 34 "helm.sh/helm/v3/pkg/action" 35 "helm.sh/helm/v3/pkg/chart" 36 "helm.sh/helm/v3/pkg/chart/loader" 37 "helm.sh/helm/v3/pkg/cli/output" 38 "helm.sh/helm/v3/pkg/cli/values" 39 "helm.sh/helm/v3/pkg/downloader" 40 "helm.sh/helm/v3/pkg/getter" 41 "helm.sh/helm/v3/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 registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile, client.InsecureSkipTLSverify) 140 if err != nil { 141 return fmt.Errorf("missing registry client: %w", err) 142 } 143 client.SetRegistryClient(registryClient) 144 145 rel, err := runInstall(args, client, valueOpts, out) 146 if err != nil { 147 return errors.Wrap(err, "INSTALLATION FAILED") 148 } 149 150 return outfmt.Write(out, &statusPrinter{rel, settings.Debug, false, false}) 151 }, 152 } 153 154 addInstallFlags(cmd, cmd.Flags(), client, valueOpts) 155 bindOutputFlag(cmd, &outfmt) 156 bindPostRenderFlag(cmd, &client.PostRenderer) 157 158 return cmd 159 } 160 161 func addInstallFlags(cmd *cobra.Command, f *pflag.FlagSet, client *action.Install, valueOpts *values.Options) { 162 f.BoolVar(&client.CreateNamespace, "create-namespace", false, "create the release namespace if not present") 163 f.BoolVar(&client.DryRun, "dry-run", false, "simulate an install") 164 f.BoolVar(&client.Force, "force", false, "force resource updates through a replacement strategy") 165 f.BoolVar(&client.DisableHooks, "no-hooks", false, "prevent hooks from running during install") 166 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") 167 f.DurationVar(&client.Timeout, "timeout", 300*time.Second, "time to wait for any individual Kubernetes operation (like Jobs for hooks)") 168 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") 169 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") 170 f.BoolVarP(&client.GenerateName, "generate-name", "g", false, "generate the name (and omit the NAME parameter)") 171 f.StringVar(&client.NameTemplate, "name-template", "", "specify template used to name the release") 172 f.StringVar(&client.Description, "description", "", "add a custom description") 173 f.BoolVar(&client.Devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored") 174 f.BoolVar(&client.DependencyUpdate, "dependency-update", false, "update dependencies if they are missing before installing the chart") 175 f.BoolVar(&client.DisableOpenAPIValidation, "disable-openapi-validation", false, "if set, the installation process will not validate rendered templates against the Kubernetes OpenAPI Schema") 176 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") 177 f.BoolVar(&client.SkipCRDs, "skip-crds", false, "if set, no CRDs will be installed. By default, CRDs are installed if not already present") 178 f.BoolVar(&client.SubNotes, "render-subchart-notes", false, "if set, render subchart notes along with the parent") 179 f.BoolVar(&client.EnableDNS, "enable-dns", false, "enable DNS lookups when rendering templates") 180 addValueOptionsFlags(f, valueOpts) 181 addChartPathOptionsFlags(f, &client.ChartPathOptions) 182 183 err := cmd.RegisterFlagCompletionFunc("version", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { 184 requiredArgs := 2 185 if client.GenerateName { 186 requiredArgs = 1 187 } 188 if len(args) != requiredArgs { 189 return nil, cobra.ShellCompDirectiveNoFileComp 190 } 191 return compVersionFlag(args[requiredArgs-1], toComplete) 192 }) 193 194 if err != nil { 195 log.Fatal(err) 196 } 197 } 198 199 func runInstall(args []string, client *action.Install, valueOpts *values.Options, out io.Writer) (*release.Release, error) { 200 debug("Original chart version: %q", client.Version) 201 if client.Version == "" && client.Devel { 202 debug("setting version to >0.0.0-0") 203 client.Version = ">0.0.0-0" 204 } 205 206 name, chart, err := client.NameAndChart(args) 207 if err != nil { 208 return nil, err 209 } 210 client.ReleaseName = name 211 212 cp, err := client.ChartPathOptions.LocateChart(chart, settings) 213 if err != nil { 214 return nil, err 215 } 216 217 debug("CHART PATH: %s\n", cp) 218 219 p := getter.All(settings) 220 vals, err := valueOpts.MergeValues(p) 221 if err != nil { 222 return nil, err 223 } 224 225 // Check chart dependencies to make sure all are present in /charts 226 chartRequested, err := loader.Load(cp) 227 if err != nil { 228 return nil, err 229 } 230 231 if err := checkIfInstallable(chartRequested); err != nil { 232 return nil, err 233 } 234 235 if chartRequested.Metadata.Deprecated { 236 warning("This chart is deprecated") 237 } 238 239 if req := chartRequested.Metadata.Dependencies; req != nil { 240 // If CheckDependencies returns an error, we have unfulfilled dependencies. 241 // As of Helm 2.4.0, this is treated as a stopping condition: 242 // https://github.com/helm/helm/issues/2209 243 if err := action.CheckDependencies(chartRequested, req); err != nil { 244 err = errors.Wrap(err, "An error occurred while checking for chart dependencies. You may need to run `helm dependency build` to fetch missing dependencies") 245 if client.DependencyUpdate { 246 man := &downloader.Manager{ 247 Out: out, 248 ChartPath: cp, 249 Keyring: client.ChartPathOptions.Keyring, 250 SkipUpdate: false, 251 Getters: p, 252 RepositoryConfig: settings.RepositoryConfig, 253 RepositoryCache: settings.RepositoryCache, 254 Debug: settings.Debug, 255 } 256 if err := man.Update(); err != nil { 257 return nil, err 258 } 259 // Reload the chart with the updated Chart.lock file. 260 if chartRequested, err = loader.Load(cp); err != nil { 261 return nil, errors.Wrap(err, "failed reloading chart after repo update") 262 } 263 } else { 264 return nil, err 265 } 266 } 267 } 268 269 client.Namespace = settings.Namespace() 270 271 // Create context and prepare the handle of SIGTERM 272 ctx := context.Background() 273 ctx, cancel := context.WithCancel(ctx) 274 275 // Set up channel on which to send signal notifications. 276 // We must use a buffered channel or risk missing the signal 277 // if we're not ready to receive when the signal is sent. 278 cSignal := make(chan os.Signal, 2) 279 signal.Notify(cSignal, os.Interrupt, syscall.SIGTERM) 280 go func() { 281 <-cSignal 282 fmt.Fprintf(out, "Release %s has been cancelled.\n", args[0]) 283 cancel() 284 }() 285 286 return client.RunWithContext(ctx, chartRequested, vals) 287 } 288 289 // checkIfInstallable validates if a chart can be installed 290 // 291 // Application chart type is only installable 292 func checkIfInstallable(ch *chart.Chart) error { 293 switch ch.Metadata.Type { 294 case "", "application": 295 return nil 296 } 297 return errors.Errorf("%s charts are not installable", ch.Metadata.Type) 298 } 299 300 // Provide dynamic auto-completion for the install and template commands 301 func compInstall(args []string, toComplete string, client *action.Install) ([]string, cobra.ShellCompDirective) { 302 requiredArgs := 1 303 if client.GenerateName { 304 requiredArgs = 0 305 } 306 if len(args) == requiredArgs { 307 return compListCharts(toComplete, true) 308 } 309 return nil, cobra.ShellCompDirectiveNoFileComp 310 }