Skip to content

Commit

Permalink
feat: implement --force-yaml-string-quotation
Browse files Browse the repository at this point in the history
fixes grafana#687

Signed-off-by: Matthias Riegler <[email protected]>
  • Loading branch information
xvzf committed Jul 14, 2022
1 parent ac70ed2 commit e5eb792
Show file tree
Hide file tree
Showing 12 changed files with 63 additions and 16 deletions.
2 changes: 2 additions & 0 deletions cmd/tk/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ func exportCmd() *cli.Command {
vars := workflowFlags(cmd.Flags())
getJsonnetOpts := jsonnetFlags(cmd.Flags())
getLabelSelector := labelSelectorFlag(cmd.Flags())
getYAMLOpts := yamlStyleFlags(cmd.Flags())

recursive := cmd.Flags().BoolP("recursive", "r", false, "Look recursively for Tanka environments")

Expand All @@ -62,6 +63,7 @@ func exportCmd() *cli.Command {
Merge: *merge,
Opts: tanka.Opts{
JsonnetOpts: getJsonnetOpts(),
YamlOpts: getYAMLOpts(),
Filters: filters,
Name: vars.name,
},
Expand Down
10 changes: 10 additions & 0 deletions cmd/tk/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,16 @@ func workflowFlags(fs *pflag.FlagSet) *workflowFlagVars {
return &v
}

func yamlStyleFlags(fs *pflag.FlagSet) func() tanka.YAMLOpts {
forceStringQuotation := fs.Bool("force-yaml-string-quotation", false, "enforce YAML quotation for strings")

return func() tanka.YAMLOpts {
return tanka.YAMLOpts{
ForceStringQuotation: *forceStringQuotation,
}
}
}

func labelSelectorFlag(fs *pflag.FlagSet) func() labels.Selector {
labelSelector := fs.StringP("selector", "l", "", "Label selector. Uses the same syntax as kubectl does")

Expand Down
10 changes: 7 additions & 3 deletions cmd/tk/workflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ func showCmd() *cli.Command {

vars := workflowFlags(cmd.Flags())
getJsonnetOpts := jsonnetFlags(cmd.Flags())
getYAMLOpts := yamlStyleFlags(cmd.Flags())

cmd.Run = func(cmd *cli.Command, args []string) error {
if !interactive && !*allowRedirect {
Expand All @@ -212,17 +213,20 @@ Otherwise run tk show --dangerous-allow-redirect to bypass this check.`)
return err
}

pretty, err := tanka.Show(args[0], tanka.Opts{
opts := tanka.Opts{
JsonnetOpts: getJsonnetOpts(),
YamlOpts: getYAMLOpts(),
Filters: filters,
Name: vars.name,
})
}

pretty, err := tanka.Show(args[0], opts)

if err != nil {
return err
}

return pageln(pretty.String())
return pageln(pretty.String(opts.YamlOpts.ForceStringQuotation))
}
return cmd
}
2 changes: 1 addition & 1 deletion pkg/kubernetes/client/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func (k Kubectl) Apply(data manifest.List, opts ApplyOpts) error {
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr

cmd.Stdin = strings.NewReader(data.String())
cmd.Stdin = strings.NewReader(data.String(false))

return cmd.Run()
}
2 changes: 1 addition & 1 deletion pkg/kubernetes/client/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func (k Kubectl) diff(data manifest.List, serverSide bool) (*string, error) {
cmd.Stdout = &raw
}
cmd.Stderr = &fw
cmd.Stdin = strings.NewReader(data.String())
cmd.Stdin = strings.NewReader(data.String(false))
err := cmd.Run()
if diffErr := parseDiffErr(err, fw.buf, k.Info().ClientVersion); diffErr != nil {
return nil, diffErr
Expand Down
2 changes: 1 addition & 1 deletion pkg/kubernetes/client/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func (k Kubectl) GetByLabels(namespace, kind string, labels map[string]string) (
func (k Kubectl) GetByState(data manifest.List, opts GetByStateOpts) (manifest.List, error) {
list, err := k.get("", "", []string{"-f", "-"}, getOpts{
ignoreNotFound: opts.IgnoreNotFound,
stdin: data.String(),
stdin: data.String(false),
})
if err != nil {
return nil, err
Expand Down
2 changes: 1 addition & 1 deletion pkg/kubernetes/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ func StaticDiffer(create bool) Differ {
return func(state manifest.List) (*string, error) {
s := ""
for _, m := range state {
is, should := m.String(), ""
is, should := m.String(false), ""
if create {
is, should = should, is
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/kubernetes/manifest/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func (s *SchemaError) Error() string {

if s.Manifest != nil {
msg += bluef("\nPlease check below object:\n")
msg += SampleString(s.Manifest.String()).Indent(2)
msg += SampleString(s.Manifest.String(false)).Indent(2)
}

return msg
Expand Down
36 changes: 31 additions & 5 deletions pkg/kubernetes/manifest/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,19 @@ import (
"github.com/Masterminds/sprig/v3"
"github.com/pkg/errors"
"github.com/stretchr/objx"
yaml "gopkg.in/yaml.v2"
yaml "gopkg.in/yaml.v3"
)

func yamlForceStringQuotationRecursivePatch(n *yaml.Node) {
if n.Tag == "!!str" {
n.Style = yaml.DoubleQuotedStyle
}
for _, cNode := range n.Content {
// for all child nodes
yamlForceStringQuotationRecursivePatch(cNode)
}
}

// Manifest represents a Kubernetes API object. The fields `apiVersion` and
// `kind` are required, `metadata.name` should be present as well
type Manifest map[string]interface{}
Expand All @@ -31,8 +41,17 @@ func NewFromObj(raw objx.Map) (Manifest, error) {
}

// String returns the Manifest in yaml representation
func (m Manifest) String() string {
y, err := yaml.Marshal(m)
func (m Manifest) String(forceQuotedStrings bool) string {

yamlNode := &yaml.Node{}
if err := yamlNode.Encode(m); err != nil {
panic(errors.Wrap(err, "converting manifest to yaml.Node"))
}
if forceQuotedStrings {
yamlForceStringQuotationRecursivePatch(yamlNode)
}

y, err := yaml.Marshal(yamlNode)
if err != nil {
// this should never go wrong in normal operations
panic(errors.Wrap(err, "formatting manifest"))
Expand Down Expand Up @@ -262,12 +281,19 @@ type List []Manifest

// String returns the List as a yaml stream. In case of an error, it is
// returned as a string instead.
func (m List) String() string {
func (m List) String(forceQuotedStrings bool) string {
buf := bytes.Buffer{}
enc := yaml.NewEncoder(&buf)

for _, d := range m {
if err := enc.Encode(d); err != nil {
yamlNode := &yaml.Node{}
if err := yamlNode.Encode(d); err != nil {
panic(errors.Wrap(err, "converting manifest to yaml.Node"))
}
if forceQuotedStrings {
yamlForceStringQuotationRecursivePatch(yamlNode)
}
if err := enc.Encode(yamlNode); err != nil {
// This should never happen in normal operations
panic(errors.Wrap(err, "formatting manifests"))
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/kubernetes/subsetdiff.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,10 @@ func subsetDiff(c client.Client, m manifest.Manifest) (*difference, error) {
return nil, errors.Wrap(err, "getting state from cluster")
}

should := m.String()
should := m.String(false)

sub := subset(m, rawIs)
is := manifest.Manifest(sub).String()
is := manifest.Manifest(sub).String(false)
if is == "{}\n" {
is = ""
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/tanka/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ func ExportEnvironments(envs []*v1alpha1.Environment, to string, opts *ExportEnv
}

// Write manifest
data := m.String()
data := m.String(opts.Opts.YamlOpts.ForceStringQuotation)
if err := writeExportFile(path, []byte(data)); err != nil {
return err
}
Expand Down
5 changes: 5 additions & 0 deletions pkg/tanka/tanka.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,14 @@ import (

type JsonnetOpts = jsonnet.Opts

type YAMLOpts struct {
ForceStringQuotation bool
}

// Opts specify general, optional properties that apply to all actions
type Opts struct {
JsonnetOpts
YamlOpts YAMLOpts

// Filters are used to optionally select a subset of the resources
Filters process.Matchers
Expand Down

0 comments on commit e5eb792

Please sign in to comment.