diff --git a/CHANGELOG.md b/CHANGELOG.md index 227c69a..1bdae28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ # Changes since 1.2.0 +- feat: add templating command + # 1.2.0 - feat: add wait option - feat: apiVersion 1.1 for wait option and add better version handling diff --git a/cmd/template.go b/cmd/template.go new file mode 100644 index 0000000..7a3613e --- /dev/null +++ b/cmd/template.go @@ -0,0 +1,46 @@ +/* +Copyright © 2023 The Helm Compose Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package cmd + +import ( + "github.com/seacrew/helm-compose/internal/compose" + "github.com/seacrew/helm-compose/internal/config" + "github.com/spf13/cobra" +) + +var templateCmd = &cobra.Command{ + Use: "template [RELEASE ...]", + Short: "Render templates for all releases locally and display the output.", + Long: ``, + RunE: func(cmd *cobra.Command, args []string) error { + cmd.SilenceUsage = true + + if err := compose.CompatibleHelmVersion(); err != nil { + return err + } + + config, err := config.ParseComposeFile(composeFile) + if err != nil { + return err + } + + return compose.Template(config, args) + }, +} + +func init() { + rootCmd.AddCommand(templateCmd) +} diff --git a/docs/commands/template.md b/docs/commands/template.md new file mode 100644 index 0000000..8696ebd --- /dev/null +++ b/docs/commands/template.md @@ -0,0 +1,21 @@ +# helm compose template + +Template all kubernetes resources for the releases specified in your `helm-compose.yaml` and print them out to stdout. + +## Usage + +The following command will print out all kubernetes resources that would be installed or upgraded on stdout. + +``` +helm compose template [releases...] [flags] +``` + +## Options + +``` +Flags: + -h, --help help for template + +Global Flags: + -f, --file string Compose configuration file +``` diff --git a/internal/compose/compose.go b/internal/compose/compose.go index 498d081..a2a7573 100644 --- a/internal/compose/compose.go +++ b/internal/compose/compose.go @@ -21,6 +21,7 @@ import ( cfg "github.com/seacrew/helm-compose/internal/config" prov "github.com/seacrew/helm-compose/internal/provider" + "github.com/seacrew/helm-compose/internal/util" ) func RunUp(config *cfg.Config) error { @@ -126,3 +127,28 @@ func GetRevision(rev int, config *cfg.Config) error { return nil } + +func Template(config *cfg.Config, releases []string) error { + util.PrintColors = false + + for name, url := range config.Repositories { + if err := addHelmRepository(name, url); err != nil { + return err + } + } + + for name, release := range config.Releases { + if len(releases) == 0 { + templateHelmRelease(name, &release) + continue + } + + for _, rel := range releases { + if rel == name { + templateHelmRelease(name, &release) + } + } + } + + return nil +} diff --git a/internal/compose/helm.go b/internal/compose/helm.go index 2cad205..a4bee3e 100644 --- a/internal/compose/helm.go +++ b/internal/compose/helm.go @@ -36,25 +36,33 @@ var ( minVersion = semver.MustParse("v3.0.0") ) +type HelmCommand string + +const ( + HELM_UPGRADE HelmCommand = "upgrade" + HELM_UNINSTALL HelmCommand = "uninstall" + HELM_TEMPLATE HelmCommand = "template" +) + func CompatibleHelmVersion() error { cmd := exec.Command(helm, "version") util.DebugPrint("Executing %s", strings.Join(cmd.Args, " ")) output, err := cmd.CombinedOutput() if err != nil { - return fmt.Errorf("Failed to run `%s version`: %v", os.Getenv("HELM_BIN"), err) + return fmt.Errorf("failed to run `%s version`: %v", os.Getenv("HELM_BIN"), err) } versionOutput := string(output) matches := versionRE.FindStringSubmatch(versionOutput) if matches == nil { - return fmt.Errorf("Failed to find version in output %#v", versionOutput) + return fmt.Errorf("failed to find version in output %#v", versionOutput) } helmVersion, err := semver.NewVersion(matches[1]) if err != nil { - return fmt.Errorf("Failed to parse version %#v: %v", matches[1], err) + return fmt.Errorf("failed to parse version %#v: %v", matches[1], err) } if minVersion.GreaterThan(helmVersion) { @@ -74,10 +82,71 @@ func addHelmRepository(name string, url string) error { } func installHelmRelease(name string, release *cfg.Release) { + args, err := createHelmArguments(HELM_UPGRADE, name, release) + if err != nil { + cp := util.NewColorPrinter(name) + cp.Printf("%s |\t\t%s", name, err) + } + + helmExec(name, args) +} + +func templateHelmRelease(name string, release *cfg.Release) { + args, err := createHelmArguments(HELM_TEMPLATE, name, release) + if err != nil { + cp := util.NewColorPrinter(name) + cp.Printf("# %s |\t\t%s", name, err) + } + + helmExec("", args) +} + +func uninstallHelmRelease(name string, release *cfg.Release) { + var args []string + + args = append(args, "uninstall") + + if release.Namespace != "" { + args = append(args, fmt.Sprintf("--namespace=%s", release.Namespace)) + } + + if release.KubeConfig != "" { + args = append(args, fmt.Sprintf("--kubeconfig=%s", release.KubeConfig)) + } + + if release.KubeContext != "" { + args = append(args, fmt.Sprintf("--kube-context=%s", release.KubeContext)) + } + + if release.DeletionStrategy != "" { + args = append(args, fmt.Sprintf("--cascade=%s", release.DeletionStrategy)) + } + + if release.DeletionTimeout != "" { + args = append(args, fmt.Sprintf("--timeout=%s", release.DeletionTimeout)) + } + + if release.DeletionNoHooks { + args = append(args, "--no-hooks") + } + + if release.KeepHistory { + args = append(args, "--keep-history") + } + + args = append(args, name) + + helmExec(name, args) +} + +func createHelmArguments(command HelmCommand, name string, release *cfg.Release) ([]string, error) { var args []string - args = append(args, "upgrade") - args = append(args, "--install") + args = append(args, string(command)) + + if command == HELM_UPGRADE { + args = append(args, "--install") + } if release.ChartVersion != "" { args = append(args, fmt.Sprintf("--version=%s", release.ChartVersion)) @@ -162,9 +231,7 @@ func installHelmRelease(name string, release *cfg.Release) { data := util.ConvertJson(release.Values[key]) values, err := json.Marshal(data) if err != nil { - cp := util.NewColorPrinter(name) - cp.Printf("%s |\t\t%s", name, err) - return + return nil, err } jsonValues = append(jsonValues, fmt.Sprintf("%s=%s", key, values)) @@ -177,45 +244,7 @@ func installHelmRelease(name string, release *cfg.Release) { args = append(args, name) args = append(args, release.Chart) - helmExec(name, args) -} - -func uninstallHelmRelease(name string, release *cfg.Release) { - var args []string - - args = append(args, "uninstall") - - if release.Namespace != "" { - args = append(args, fmt.Sprintf("--namespace=%s", release.Namespace)) - } - - if release.KubeConfig != "" { - args = append(args, fmt.Sprintf("--kubeconfig=%s", release.KubeConfig)) - } - - if release.KubeContext != "" { - args = append(args, fmt.Sprintf("--kube-context=%s", release.KubeContext)) - } - - if release.DeletionStrategy != "" { - args = append(args, fmt.Sprintf("--cascade=%s", release.DeletionStrategy)) - } - - if release.DeletionTimeout != "" { - args = append(args, fmt.Sprintf("--timeout=%s", release.DeletionTimeout)) - } - - if release.DeletionNoHooks { - args = append(args, "--no-hooks") - } - - if release.KeepHistory { - args = append(args, "--keep-history") - } - - args = append(args, name) - - helmExec(name, args) + return args, nil } func helmExec(name string, args []string) { @@ -224,7 +253,11 @@ func helmExec(name string, args []string) { scanner := bufio.NewScanner(strings.NewReader(output)) for scanner.Scan() { - cp.Printf("%s |\t\t%s", name, scanner.Text()) + if len(name) == 0 { + fmt.Printf("%s\n", scanner.Text()) + } else { + cp.Printf("%s |\t\t%s", name, scanner.Text()) + } } err := scanner.Err() diff --git a/internal/util/colors.go b/internal/util/colors.go index 820458c..7bc900f 100644 --- a/internal/util/colors.go +++ b/internal/util/colors.go @@ -27,6 +27,8 @@ type ColorPrinter struct { colorFunc func(...interface{}) string } +var PrintColors = true + func NewColorPrinter(s string) *ColorPrinter { c := hashColor(s) return &ColorPrinter{ @@ -35,7 +37,12 @@ func NewColorPrinter(s string) *ColorPrinter { } func (c ColorPrinter) Printf(format string, a ...any) { - fmt.Printf(c.colorFunc(format)+"\n", a...) + if PrintColors { + fmt.Printf(c.colorFunc(format)+"\n", a...) + return + } + + fmt.Printf(format+"\n", a...) } var colorFuncs [](func(...interface{}) string) = [](func(...interface{}) string){