github.com/codefresh-io/kcfi@v0.0.0-20230301195427-c1578715cc46/cmd/kcfi/template.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 "bytes" 21 "fmt" 22 "io" 23 "path/filepath" 24 "regexp" 25 "sort" 26 "strings" 27 28 "github.com/spf13/cobra" 29 30 "github.com/codefresh-io/kcfi/pkg/helm-internal/completion" 31 "helm.sh/helm/v3/cmd/helm/require" 32 "helm.sh/helm/v3/pkg/action" 33 "helm.sh/helm/v3/pkg/chartutil" 34 "helm.sh/helm/v3/pkg/cli/values" 35 "helm.sh/helm/v3/pkg/releaseutil" 36 ) 37 38 const templateDesc = ` 39 Render chart templates locally and display the output. 40 41 Any values that would normally be looked up or retrieved in-cluster will be 42 faked locally. Additionally, none of the server-side testing of chart validity 43 (e.g. whether an API is supported) is done. 44 ` 45 46 func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { 47 var validate bool 48 var includeCrds bool 49 client := action.NewInstall(cfg) 50 valueOpts := &values.Options{} 51 var extraAPIs []string 52 var showFiles []string 53 54 cmd := &cobra.Command{ 55 Use: "template [NAME] [CHART]", 56 Short: "locally render templates", 57 Long: templateDesc, 58 Args: require.MinimumNArgs(1), 59 RunE: func(_ *cobra.Command, args []string) error { 60 client.DryRun = true 61 client.ReleaseName = "RELEASE-NAME" 62 client.Replace = true // Skip the name check 63 client.ClientOnly = !validate 64 client.APIVersions = chartutil.VersionSet(extraAPIs) 65 client.IncludeCRDs = includeCrds 66 rel, err := runInstall(args, client, valueOpts, out) 67 68 if err != nil && !settings.Debug { 69 if rel != nil { 70 return fmt.Errorf("%w\n\nUse --debug flag to render out invalid YAML", err) 71 } 72 return err 73 } 74 75 // We ignore a potential error here because, when the --debug flag was specified, 76 // we always want to print the YAML, even if it is not valid. The error is still returned afterwards. 77 if rel != nil { 78 var manifests bytes.Buffer 79 fmt.Fprintln(&manifests, strings.TrimSpace(rel.Manifest)) 80 81 if !client.DisableHooks { 82 for _, m := range rel.Hooks { 83 fmt.Fprintf(&manifests, "---\n# Source: %s\n%s\n", m.Path, m.Manifest) 84 } 85 } 86 87 // if we have a list of files to render, then check that each of the 88 // provided files exists in the chart. 89 if len(showFiles) > 0 { 90 // This is necessary to ensure consistent manifest ordering when using --show-only 91 // with globs or directory names. 92 splitManifests := releaseutil.SplitManifests(manifests.String()) 93 manifestsKeys := make([]string, 0, len(splitManifests)) 94 for k := range splitManifests { 95 manifestsKeys = append(manifestsKeys, k) 96 } 97 sort.Sort(releaseutil.BySplitManifestsOrder(manifestsKeys)) 98 99 manifestNameRegex := regexp.MustCompile("# Source: [^/]+/(.+)") 100 var manifestsToRender []string 101 for _, f := range showFiles { 102 missing := true 103 for _, manifestKey := range manifestsKeys { 104 manifest := splitManifests[manifestKey] 105 submatch := manifestNameRegex.FindStringSubmatch(manifest) 106 if len(submatch) == 0 { 107 continue 108 } 109 manifestName := submatch[1] 110 // manifest.Name is rendered using linux-style filepath separators on Windows as 111 // well as macOS/linux. 112 manifestPathSplit := strings.Split(manifestName, "/") 113 manifestPath := filepath.Join(manifestPathSplit...) 114 115 // if the filepath provided matches a manifest path in the 116 // chart, render that manifest 117 if matched, _ := filepath.Match(f, manifestPath); !matched { 118 continue 119 } 120 manifestsToRender = append(manifestsToRender, manifest) 121 missing = false 122 } 123 if missing { 124 return fmt.Errorf("could not find template %s in chart", f) 125 } 126 } 127 for _, m := range manifestsToRender { 128 fmt.Fprintf(out, "---\n%s\n", m) 129 } 130 } else { 131 fmt.Fprintf(out, "%s", manifests.String()) 132 } 133 } 134 135 return err 136 }, 137 } 138 139 // Function providing dynamic auto-completion 140 completion.RegisterValidArgsFunc(cmd, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { 141 return compInstall(args, toComplete, client) 142 }) 143 144 f := cmd.Flags() 145 addInstallFlags(f, client, valueOpts) 146 f.StringArrayVarP(&showFiles, "show-only", "s", []string{}, "only show manifests rendered from the given templates") 147 f.StringVar(&client.OutputDir, "output-dir", "", "writes the executed templates to files in output-dir instead of stdout") 148 f.BoolVar(&validate, "validate", false, "validate your manifests against the Kubernetes cluster you are currently pointing at. This is the same validation performed on an install") 149 f.BoolVar(&includeCrds, "include-crds", false, "include CRDs in the templated output") 150 f.BoolVar(&client.IsUpgrade, "is-upgrade", false, "set .Release.IsUpgrade instead of .Release.IsInstall") 151 f.StringArrayVarP(&extraAPIs, "api-versions", "a", []string{}, "Kubernetes api versions used for Capabilities.APIVersions") 152 f.BoolVar(&client.UseReleaseName, "release-name", false, "use release name in the output-dir path.") 153 bindPostRenderFlag(cmd, &client.PostRenderer) 154 155 return cmd 156 }