Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support: template --output-dir some_dir #587

Merged
merged 9 commits into from
May 10, 2022
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ var (
importRepository string
// additionalHelmArgs is a list of arguments to add to all helm commands
additionalHelmArgs []string
// templateOutputDir
templateOutputDir string
)

func init() {
Expand All @@ -69,16 +71,19 @@ func init() {
rootCmd.PersistentFlags().BoolVar(&noColor, "no-color", false, "If true, don't colorize output.")
rootCmd.PersistentFlags().StringSliceVar(&additionalHelmArgs, "helm-args", nil, "Additional arguments to pass to helm commands. Can be passed multiple times. used more than once. WARNING: Setting this will completely override any helm_args in the course.")

plotCmd.PersistentFlags().BoolVar(&continueOnError, "continue-on-error", false, "If true, continue plotting releases even if one or more has errors.")
updateCmd.PersistentFlags().BoolVar(&continueOnError, "continue-on-error", false, "If true, continue plotting releases even if one or more has errors.")
diffCmd.PersistentFlags().BoolVar(&continueOnError, "continue-on-error", false, "If true, continue plotting releases even if one or more has errors.")
plotCmd.PersistentFlags().BoolVar(&continueOnError, "continue-on-error", false, "If true, continue to plot releases even if one or more has errors.")
updateCmd.PersistentFlags().BoolVar(&continueOnError, "continue-on-error", false, "If true, continue to update releases even if one or more has errors.")
diffCmd.PersistentFlags().BoolVar(&continueOnError, "continue-on-error", false, "If true, continue to diff releases even if one or more has errors.")

convertCmd.Flags().BoolVarP(&inPlaceConvert, "in-place", "i", false, "If specified, will update the file in place, otherwise outputs to stdout.")

importCmd.Flags().StringVar(&importNamespace, "namespace", "", "Namespace that contains the release to be imported.")
importCmd.Flags().StringVar(&importRelease, "release_name", "", "The name of the release to import.")
importCmd.Flags().StringVar(&importRepository, "repository", "", "The helm repository for the imported release.")

templateCmd.Flags().StringVar(&templateOutputDir, "output-dir", "", "path to the base output directory (eg, ~/myproject/manifests)")
// templateCmd.PersistentFlags().BoolVar(&continueOnError, "continue-on-error", false, "If true, continue to template releases even if one or more has errors.")

rootCmd.AddCommand(
plotCmd,
convertCmd,
Expand Down Expand Up @@ -164,12 +169,15 @@ var templateCmd = &cobra.Command{
color.Red(err.Error())
os.Exit(1)
}
tmpl, err := client.TemplateAll()
tmpl, err := client.TemplateAll(templateOutputDir)
if err != nil {
color.Red(err.Error())
os.Exit(1)
}
fmt.Println(tmpl)

if len(templateOutputDir) < 1 {
fmt.Println(tmpl)
}
},
}

Expand Down
57 changes: 31 additions & 26 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ secrets:
## CLI Usage

```text
$ reckoner --help
Usage: reckoner [OPTIONS] COMMAND [ARGS]...

Options:
Expand All @@ -285,8 +286,9 @@ Commands:
```

You can add `--help` to any `Command` and get output like the one below:

```text
$> reckoner plot --help
$ reckoner plot --help
Usage: reckoner plot [OPTIONS] COURSE_FILE

Install charts with given arguments as listed in yaml file argument
Expand Down Expand Up @@ -327,8 +329,9 @@ Options:
```

Or

```
# reckoner update --help
$ reckoner update --help
Usage: reckoner update [OPTIONS] COURSE_FILE

Checks to see if anything will be changed, if so, update the release,
Expand Down Expand Up @@ -370,34 +373,36 @@ Options:
```

Or
```text
# reckoner template --help
Usage: reckoner template [OPTIONS] COURSE_FILE

Output the template of the chart or charts as they would be installed or
upgraded

Options:
-a, --run-all Run all charts in the course. Mutually
exclusive with 'only'.

-o, --only, --heading <chart> Only run a specific chart by name. Mutually
exclusive with 'run_all'.

--helm-args TEXT Passes the following arg on to helm, can be
used more than once. WARNING: Setting this
will completely override any helm_args in the
course. Also cannot be used for configuring
how helm connects to tiller.

--log-level TEXT Log Level. [INFO | DEBUG | WARN | ERROR].
(default=INFO)

--help Show this message and exit.
```text
$ reckoner template --help
Templates a helm chart for a release or several releases. Automatically sets --create-namespaces=false --dry-run=true

Usage:
reckoner template [flags]

Flags:
-h, --help help for template
--output-dir string path to the base output directory (eg, ~/myproject/manifests)

Global Flags:
--create-namespaces If true, allow reckoner to create namespaces. (default true)
--dry-run Implies helm --dry-run --debug and skips any hooks
--helm-args strings Additional arguments to pass to helm commands. Can
be passed multiple times. used more than once.
WARNING: Setting this will completely override
any helm_args in the course.
--no-color If true, don't colorize output.
-o, --only strings Only install this list of releases. Can be passed
multiple times.
-a, --run-all Install every release in the course file
-v, --v Level number for the log level verbosity
```

Or

```text
# reckoner import --help
$ reckoner import --help
Usage: reckoner import [OPTIONS]

Outputs a chart block that can be used to import the specified release
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ require (
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
github.com/Azure/go-autorest/logger v0.2.1 // indirect
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
github.com/Microsoft/go-winio v0.4.16 // indirect
github.com/Microsoft/go-winio v0.4.17 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // indirect
github.com/acomagu/bufpipe v1.0.3 // indirect
github.com/emirpasic/gods v1.12.0 // indirect
Expand All @@ -46,7 +46,7 @@ require (
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/go-cmp v0.5.6 // indirect
github.com/google/go-github/v30 v30.1.0 // indirect
github.com/google/go-querystring v1.0.0 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/gofuzz v1.1.0 // indirect
github.com/googleapis/gnostic v0.5.5 // indirect
github.com/imdario/mergo v0.3.12 // indirect
Expand Down
6 changes: 4 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,9 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym
github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
github.com/Microsoft/go-winio v0.4.16 h1:FtSW/jqD+l4ba5iPBj9CODVtgfYAD8w2wS923g/cFDk=
github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
github.com/Microsoft/go-winio v0.4.17 h1:iT12IBVClFevaf8PuVyi3UmZOVh4OqnaLxDTW2O6j3w=
github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
Expand Down Expand Up @@ -264,8 +265,9 @@ github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-github/v30 v30.1.0 h1:VLDx+UolQICEOKu2m4uAoMti1SxuEBAl7RSEG16L+Oo=
github.com/google/go-github/v30 v30.1.0/go.mod h1:n8jBpHl45a/rlBUtRJMOG4GhNADUQFEufcolZ95JfU8=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
Expand Down
20 changes: 14 additions & 6 deletions pkg/reckoner/plot.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,16 +110,15 @@ func (c *Client) Plot() error {
}

// TemplateAll runs the same as plot but runs template instead
func (c Client) TemplateAll() (string, error) {
err := c.UpdateHelmRepos()
func (c Client) TemplateAll(templateOutputDir string) (fullOutput string, err error) {
err = c.UpdateHelmRepos()
if err != nil {
return "", nil
return fullOutput, err
}

var fullOutput string
for _, release := range c.CourseFile.Releases {

if err := c.cloneGitRepo(release); err != nil {
err = c.cloneGitRepo(release)
if err != nil {
color.Red(err.Error())
}

Expand All @@ -128,6 +127,15 @@ func (c Client) TemplateAll() (string, error) {
color.Red(err.Error())
continue
}

if len(templateOutputDir) > 0 {
err = c.WriteSplitYaml([]byte(out), templateOutputDir, release.Name)
if err != nil {
color.Red(err.Error())
os.Exit(1)
}
}

fullOutput = fullOutput + out
}
return fullOutput, nil
Expand Down
120 changes: 120 additions & 0 deletions pkg/reckoner/split.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// Copyright 2022 Fairwinds
//
// 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 reckoner

import (
"bytes"
"errors"
"io"
"os"

"gopkg.in/yaml.v3"
)

type k8sMetaBasic struct {
Kind string `yaml:"kind"`
Metadata struct {
Name string `yaml:"name"`
Namespace string `yaml:"namespace,omitempty"`
} `yaml:"metadata"`
}

// splitYAML will take a slice of bytes, assumed to be a multi-page (1+) YAML document,
// and append each page/object it finds in the document to a slice which is
// returned at the end of processing. Each object is typed as a slice of bytes.
// Therefore, this function returns a slice of a slice of bytes.
// splitYAML was shamelessly ripped from https://go.dev/play/p/MZNwxdUzxPo, which was
// found via github issue https://github.com/go-yaml/yaml/pull/301#issuecomment-792871300
func splitYAML(in []byte) (out [][]byte, err error) {
decoder := yaml.NewDecoder(bytes.NewReader(in)) // create a YAML decoder to run through our document

for { // keep going until we run out of bytes to process (end-of-file/EOF)
var value interface{} // something we can use to decode and marshal

err = decoder.Decode(&value) // attempt to decode a page in the document
if err == io.EOF { // we ran out of pages/objects/bytes
break // stop trying to process anything
}
// we might have bytes, but we still need to check if we could decode successfully
if err != nil { // check for errors
return nil, err // bubble up
}

valueBytes, err := yaml.Marshal(value) // marshal the slice of bytes into proper a YAML object
if err != nil { // check for errors
return nil, err // bubble up
}

// we believe we have a valid YAML object
out = append(out, valueBytes) // so append it to the list to be returned later
}

return out, nil // list of YAML objects, each a []byte
}

func writeYAML(in []byte, filename string) (err error) {
file, err := os.Create(filename)
if err != nil {
return err
}

_, err = file.Write([]byte("---\n")) // pagination, just in case a multi-document file is being written
if err != nil {
return err
}

_, err = file.Write(in)
if err != nil {
return err
}

return err
}

func (c *Client) WriteSplitYaml(in []byte, basePath string, releaseName string) (err error) {
releasePath := basePath + "/" + releaseName

objects, err := splitYAML(in) // get list of YAML documents
if err != nil { // check for errors
return err // bubble up
}

// ensure "${--output-dir}/release_name" exists
for _, dir := range []string{basePath, releasePath} {
if _, err := os.Stat(dir); errors.Is(err, os.ErrNotExist) {
err := os.Mkdir(dir, os.ModePerm)
if err != nil {
return err
}
}
}

for _, object := range objects { // loop through documents
meta := k8sMetaBasic{}
err = yaml.Unmarshal(object, &meta)
if err != nil {
return err
}

filename := releasePath + "/" + meta.Kind + "_" + meta.Metadata.Name + ".yml"

err = writeYAML(object, filename) // write out
if err != nil {
return err
}
}

return err
}