-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add generate yaml subcommand to clusterctl
Currently, this only works with --from flag since that is the most generic and can be used for any template. It also provides the --list-variables flag to list variables present in the template
- Loading branch information
Warren Fernandes
committed
Jul 27, 2020
1 parent
f8cac93
commit f881a45
Showing
7 changed files
with
363 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
/* | ||
Copyright 2019 The Kubernetes 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/spf13/cobra" | ||
) | ||
|
||
var generateCmd = &cobra.Command{ | ||
Use: "generate", | ||
Short: "Generate yaml using clusterctl yaml processor.", | ||
Long: `Generate yaml using clusterctl yaml processor.`, | ||
} | ||
|
||
func init() { | ||
RootCmd.AddCommand(generateCmd) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
/* | ||
Copyright 2019 The Kubernetes 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 ( | ||
"fmt" | ||
"io" | ||
"os" | ||
|
||
"github.com/spf13/cobra" | ||
"sigs.k8s.io/cluster-api/cmd/clusterctl/client" | ||
) | ||
|
||
type generateYAMLOptions struct { | ||
url string | ||
listVariables bool | ||
} | ||
|
||
var gyOpts = &generateYAMLOptions{} | ||
|
||
var generateYamlCmd = &cobra.Command{ | ||
Use: "yaml", | ||
Short: "Process yaml using clusterctl's yaml processor", | ||
Long: LongDesc(` | ||
Process yaml using clusterctl's yaml processor. | ||
clusterctl ships with a simple yaml processor that performs variable | ||
substitution that takes into account of default values. | ||
Variable values are either sourced from the clusterctl config file or | ||
from environment variables`), | ||
|
||
Example: Examples(` | ||
# Generates a configuration file with variable values using | ||
a template from a specific URL. | ||
clusterctl generate yaml --from https://github.com/foo-org/foo-repository/blob/master/cluster-template.yaml | ||
# Generates a configuration file with variable values using | ||
a template stored locally. | ||
clusterctl generate yaml --from ~/workspace/cluster-template.yaml`), | ||
|
||
RunE: func(cmd *cobra.Command, args []string) error { | ||
return generateYAML(os.Stdout) | ||
}, | ||
} | ||
|
||
func init() { | ||
// flags for the url source | ||
generateYamlCmd.Flags().StringVar(&gyOpts.url, "from", "", | ||
"The URL to read the template from.") | ||
|
||
// other flags | ||
generateYamlCmd.Flags().BoolVar(&gyOpts.listVariables, "list-variables", false, | ||
"Returns the list of variables expected by the template instead of the template yaml") | ||
|
||
generateCmd.AddCommand(generateYamlCmd) | ||
} | ||
|
||
func generateYAML(w io.Writer) error { | ||
c, err := client.New(cfgFile) | ||
if err != nil { | ||
return err | ||
} | ||
options := client.ProcessYAMLOptions{ | ||
ListVariablesOnly: gyOpts.listVariables, | ||
} | ||
if gyOpts.url != "" { | ||
options.URLSource = &client.URLSourceOptions{ | ||
URL: gyOpts.url, | ||
} | ||
} | ||
printer, err := c.ProcessYAML(options) | ||
if err != nil { | ||
return err | ||
} | ||
if gyOpts.listVariables { | ||
if len(printer.Variables()) > 0 { | ||
fmt.Fprintln(w, "Variables:") | ||
for _, v := range printer.Variables() { | ||
fmt.Fprintf(w, " - %s\n", v) | ||
} | ||
} else { | ||
fmt.Fprintln(w) | ||
} | ||
return nil | ||
} | ||
out, err := printer.Yaml() | ||
if err != nil { | ||
return err | ||
} | ||
_, err = fmt.Fprintln(w, string(out)) | ||
return err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
/* | ||
Copyright 2020 The Kubernetes 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 ( | ||
"bytes" | ||
"io/ioutil" | ||
"os" | ||
"path/filepath" | ||
"testing" | ||
|
||
. "github.com/onsi/gomega" | ||
) | ||
|
||
func Test_generateYAML(t *testing.T) { | ||
g := NewWithT(t) | ||
// create a local template | ||
template, cleanup1 := createTempFile(g, `v1: ${VAR1:=default1} | ||
v2: ${VAR2=default2} | ||
v3: ${VAR3:-default3}`) | ||
defer cleanup1() | ||
|
||
templateWithoutVars, cleanup2 := createTempFile(g, `v1: foobar | ||
v2: bazfoo`) | ||
defer cleanup2() | ||
|
||
tests := []struct { | ||
name string | ||
options *generateYAMLOptions | ||
expectErr bool | ||
expectedOutput string | ||
}{ | ||
{ | ||
name: "prints processed yaml using --from flag", | ||
options: &generateYAMLOptions{url: template}, | ||
expectErr: false, | ||
expectedOutput: `v1: default1 | ||
v2: default2 | ||
v3: default3 | ||
`, | ||
}, | ||
{ | ||
name: "prints variables using --list-variables flag", | ||
options: &generateYAMLOptions{url: template, listVariables: true}, | ||
expectErr: false, | ||
expectedOutput: `Variables: | ||
- VAR1 | ||
- VAR2 | ||
- VAR3 | ||
`, | ||
}, | ||
{ | ||
name: "returns error for bad templateFile path", | ||
options: &generateYAMLOptions{url: "/tmp/do-not-exist", listVariables: true}, | ||
expectErr: true, | ||
}, | ||
{ | ||
name: "prints nothing if there are no variables in the template", | ||
options: &generateYAMLOptions{url: templateWithoutVars, listVariables: true}, | ||
expectErr: false, | ||
expectedOutput: "\n", | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
g := NewWithT(t) | ||
gyOpts = tt.options | ||
buf := bytes.NewBufferString("") | ||
err := generateYAML(buf) | ||
if tt.expectErr { | ||
g.Expect(err).To(HaveOccurred()) | ||
return | ||
} | ||
|
||
output, err := ioutil.ReadAll(buf) | ||
g.Expect(err).ToNot(HaveOccurred()) | ||
g.Expect(string(output)).To(Equal(tt.expectedOutput)) | ||
}) | ||
} | ||
|
||
} | ||
|
||
// createTempFile creates a temporary yaml file inside a temp dir. It returns | ||
// the filepath and a cleanup function for the temp directory. | ||
func createTempFile(g *WithT, contents string) (string, func()) { | ||
dir, err := ioutil.TempDir("", "clusterctl") | ||
g.Expect(err).NotTo(HaveOccurred()) | ||
|
||
templateFile := filepath.Join(dir, "templ.yaml") | ||
g.Expect(ioutil.WriteFile(templateFile, []byte(contents), 0600)).To(Succeed()) | ||
|
||
return templateFile, func() { | ||
os.RemoveAll(dir) | ||
} | ||
} |