Skip to content

Commit

Permalink
Add support for json output in inject and uninject commands (#12600)
Browse files Browse the repository at this point in the history
We add support for the `--output/-o` flag in `linkerd inject` and `linkerd uninject` commands.  The supported output formats are yaml (default) and json.  Kubectl is able to accept both of these formats which means that `linkerd inject` and `linkerd uninject` output can be piped into kubectl regardless of which output format is used.

Signed-off-by: Alex Leong <[email protected]>
  • Loading branch information
adleong authored May 21, 2024
1 parent 46c3150 commit 10b1a7a
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 20 deletions.
15 changes: 9 additions & 6 deletions cli/cmd/inject.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ type resourceTransformerInject struct {
closeWaitTimeout time.Duration
}

func runInjectCmd(inputs []io.Reader, errWriter, outWriter io.Writer, transformer *resourceTransformerInject) int {
return transformInput(inputs, errWriter, outWriter, transformer)
func runInjectCmd(inputs []io.Reader, errWriter, outWriter io.Writer, transformer *resourceTransformerInject, output string) int {
return transformInput(inputs, errWriter, outWriter, transformer, output)
}

func newCmdInject() *cobra.Command {
Expand All @@ -56,6 +56,7 @@ func newCmdInject() *cobra.Command {
injectFlags, injectFlagSet := makeInjectFlags(defaults)
var manualOption, enableDebugSidecar bool
var closeWaitTimeout time.Duration
var output string

cmd := &cobra.Command{
Use: "inject [flags] CONFIG-FILE",
Expand Down Expand Up @@ -109,7 +110,7 @@ sub-folders, or coming from stdin.`,
enableDebugSidecar: enableDebugSidecar,
closeWaitTimeout: closeWaitTimeout,
}
exitCode := uninjectAndInject(in, stderr, stdout, transformer)
exitCode := uninjectAndInject(in, stderr, stdout, transformer, output)
os.Exit(exitCode)
return nil
},
Expand All @@ -132,18 +133,20 @@ sub-folders, or coming from stdin.`,
&closeWaitTimeout, "close-wait-timeout", closeWaitTimeout,
"Sets nf_conntrack_tcp_timeout_close_wait")

cmd.Flags().StringVarP(&output, "output", "o", "yaml", "Output format, one of: json|yaml")

cmd.Flags().AddFlagSet(proxyFlagSet)
cmd.Flags().AddFlagSet(injectFlagSet)

return cmd
}

func uninjectAndInject(inputs []io.Reader, errWriter, outWriter io.Writer, transformer *resourceTransformerInject) int {
func uninjectAndInject(inputs []io.Reader, errWriter, outWriter io.Writer, transformer *resourceTransformerInject, output string) int {
var out bytes.Buffer
if exitCode := runUninjectSilentCmd(inputs, errWriter, &out, transformer.values); exitCode != 0 {
if exitCode := runUninjectSilentCmd(inputs, errWriter, &out, transformer.values, "yaml"); exitCode != 0 {
return exitCode
}
return runInjectCmd([]io.Reader{&out}, errWriter, outWriter, transformer)
return runInjectCmd([]io.Reader{&out}, errWriter, outWriter, transformer, output)
}

func (rt resourceTransformerInject) transform(bytes []byte) ([]byte, []inject.Report, error) {
Expand Down
8 changes: 4 additions & 4 deletions cli/cmd/inject_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func testUninjectAndInject(t *testing.T, tc testCase) {
allowNsInject: true,
}

if exitCode := uninjectAndInject([]io.Reader{read}, report, output, transformer); exitCode != 0 {
if exitCode := uninjectAndInject([]io.Reader{read}, report, output, transformer, "yaml"); exitCode != 0 {
t.Errorf("Unexpected error injecting YAML: %v", report)
}
if err := testDataDiffer.DiffTestYAML(tc.goldenFileName, output.String()); err != nil {
Expand Down Expand Up @@ -432,7 +432,7 @@ func testInjectCmd(t *testing.T, tc injectCmd) {
injectProxy: tc.injectProxy,
values: testConfig,
}
exitCode := runInjectCmd([]io.Reader{in}, errBuffer, outBuffer, transformer)
exitCode := runInjectCmd([]io.Reader{in}, errBuffer, outBuffer, transformer, "yaml")
if exitCode != tc.exitCode {
t.Fatalf("Expected exit code to be %d but got: %d", tc.exitCode, exitCode)
}
Expand Down Expand Up @@ -538,7 +538,7 @@ func testInjectFilePath(t *testing.T, tc injectFilePath) {
injectProxy: true,
values: values,
}
if exitCode := runInjectCmd(in, errBuf, actual, transformer); exitCode != 0 {
if exitCode := runInjectCmd(in, errBuf, actual, transformer, "yaml"); exitCode != 0 {
t.Fatal("Unexpected error. Exit code from runInjectCmd: ", exitCode)
}
if err := testDataDiffer.DiffTestYAML(tc.expectedFile, actual.String()); err != nil {
Expand All @@ -565,7 +565,7 @@ func testReadFromFolder(t *testing.T, resourceFolder string, expectedFolder stri
injectProxy: true,
values: values,
}
if exitCode := runInjectCmd(in, errBuf, actual, transformer); exitCode != 0 {
if exitCode := runInjectCmd(in, errBuf, actual, transformer, "yaml"); exitCode != 0 {
t.Fatal("Unexpected error. Exit code from runInjectCmd: ", exitCode)
}

Expand Down
25 changes: 21 additions & 4 deletions cli/cmd/inject_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ type resourceTransformer interface {
}

// Returns the integer representation of os.Exit code; 0 on success and 1 on failure.
func transformInput(inputs []io.Reader, errWriter, outWriter io.Writer, rt resourceTransformer) int {
func transformInput(inputs []io.Reader, errWriter, outWriter io.Writer, rt resourceTransformer, format string) int {
postInjectBuf := &bytes.Buffer{}
reportBuf := &bytes.Buffer{}

for _, input := range inputs {
errs := processYAML(input, postInjectBuf, reportBuf, rt)
errs := processYAML(input, postInjectBuf, reportBuf, rt, format)
if len(errs) > 0 {
fmt.Fprintf(errWriter, "Error transforming resources:\n%v", concatErrors(errs, "\n"))
return 1
Expand All @@ -51,7 +51,7 @@ func transformInput(inputs []io.Reader, errWriter, outWriter io.Writer, rt resou
}

// processYAML takes an input stream of YAML, outputting injected/uninjected YAML to out.
func processYAML(in io.Reader, out io.Writer, report io.Writer, rt resourceTransformer) []error {
func processYAML(in io.Reader, out io.Writer, report io.Writer, rt resourceTransformer, format string) []error {
reader := yamlDecoder.NewYAMLReader(bufio.NewReaderSize(in, 4096))

reports := []inject.Report{}
Expand Down Expand Up @@ -86,9 +86,26 @@ func processYAML(in io.Reader, out io.Writer, report io.Writer, rt resourceTrans
}
reports = append(reports, irs...)

// If the format is set to json, we need to convert the yaml to json
if format == "json" {
result, err = yaml.YAMLToJSON(result)
if err != nil {
errs = append(errs, err)
}
} else if format == "yaml" {
// result is already in yaml format: noop.
} else {
errs = append(errs, fmt.Errorf("unsupported format %s", format))
}

if len(errs) == 0 {
out.Write(result)
out.Write([]byte("---\n"))
if format == "yaml" {
out.Write([]byte("---\n"))
}
if format == "json" {
out.Write([]byte("\n"))
}
}
}

Expand Down
14 changes: 9 additions & 5 deletions cli/cmd/uninject.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,17 @@ type resourceTransformerUninjectSilent struct {
values *linkerd2.Values
}

func runUninjectCmd(inputs []io.Reader, errWriter, outWriter io.Writer, values *linkerd2.Values) int {
return transformInput(inputs, errWriter, outWriter, resourceTransformerUninject{values})
func runUninjectCmd(inputs []io.Reader, errWriter, outWriter io.Writer, values *linkerd2.Values, output string) int {
return transformInput(inputs, errWriter, outWriter, resourceTransformerUninject{values}, output)
}

func runUninjectSilentCmd(inputs []io.Reader, errWriter, outWriter io.Writer, values *linkerd2.Values) int {
return transformInput(inputs, errWriter, outWriter, resourceTransformerUninjectSilent{values})
func runUninjectSilentCmd(inputs []io.Reader, errWriter, outWriter io.Writer, values *linkerd2.Values, output string) int {
return transformInput(inputs, errWriter, outWriter, resourceTransformerUninjectSilent{values}, output)
}

func newCmdUninject() *cobra.Command {
var output string

cmd := &cobra.Command{
Use: "uninject [flags] CONFIG-FILE",
Short: "Remove the Linkerd proxy from a Kubernetes config",
Expand All @@ -53,12 +55,14 @@ sub-folders, or coming from stdin.`,
return err
}

exitCode := runUninjectCmd(in, os.Stderr, os.Stdout, nil)
exitCode := runUninjectCmd(in, os.Stderr, os.Stdout, nil, output)
os.Exit(exitCode)
return nil
},
}

cmd.Flags().StringVarP(&output, "output", "o", "yaml", "Output format, one of: json|yaml")

return cmd
}

Expand Down
2 changes: 1 addition & 1 deletion cli/cmd/uninject_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ func TestUninjectYAML(t *testing.T) {
output := new(bytes.Buffer)
report := new(bytes.Buffer)

exitCode := runUninjectCmd(read, report, output, values)
exitCode := runUninjectCmd(read, report, output, values, "yaml")
if exitCode != 0 {
t.Errorf("Failed to uninject %s", tc.inputFileName)
}
Expand Down

0 comments on commit 10b1a7a

Please sign in to comment.