Skip to content

Commit

Permalink
feat: add option to enable JSON output for diff/sync (#798)
Browse files Browse the repository at this point in the history
  • Loading branch information
DanielRailean committed Jul 19, 2023
1 parent d3ceb4b commit 7352609
Show file tree
Hide file tree
Showing 12 changed files with 665 additions and 29 deletions.
87 changes: 76 additions & 11 deletions cmd/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package cmd

import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"os"
Expand Down Expand Up @@ -39,6 +41,8 @@ const (
modeKongEnterprise
)

var jsonOutput diff.JSONOutputObject

func getMode(targetContent *file.Content) mode {
if inKonnectMode(targetContent) {
return modeKonnect
Expand Down Expand Up @@ -71,19 +75,35 @@ func workspaceExists(ctx context.Context, config utils.KongClientConfig, workspa
return exists, nil
}

func getWorkspaceName(workspaceFlag string, targetContent *file.Content) string {
func getWorkspaceName(workspaceFlag string, targetContent *file.Content,
enableJSONOutput bool,
) string {
if workspaceFlag != targetContent.Workspace && workspaceFlag != "" {
cprint.DeletePrintf("Warning: Workspace '%v' specified via --workspace flag is "+
"different from workspace '%v' found in state file(s).\n", workspaceFlag, targetContent.Workspace)
warning := fmt.Sprintf("Workspace '%v' specified via --workspace flag is "+
"different from workspace '%v' found in state file(s).", workspaceFlag, targetContent.Workspace)
if enableJSONOutput {
jsonOutput.Warnings = append(jsonOutput.Warnings, warning)
} else {
cprint.DeletePrintf("Warning: " + warning + "\n")
}
return workspaceFlag
}
return targetContent.Workspace
}

func syncMain(ctx context.Context, filenames []string, dry bool, parallelism,
delay int, workspace string,
delay int, workspace string, enableJSONOutput bool,
) error {
// read target file
if enableJSONOutput {
jsonOutput.Errors = []string{}
jsonOutput.Warnings = []string{}
jsonOutput.Changes = diff.EntityChanges{
Creating: []diff.EntityState{},
Updating: []diff.EntityState{},
Deleting: []diff.EntityState{},
}
}
targetContent, err := file.GetContentFromFiles(filenames)
if err != nil {
return err
Expand Down Expand Up @@ -137,7 +157,7 @@ func syncMain(ctx context.Context, filenames []string, dry bool, parallelism,

// prepare to read the current state from Kong
var wsConfig utils.KongClientConfig
workspaceName := getWorkspaceName(workspace, targetContent)
workspaceName := getWorkspaceName(workspace, targetContent, enableJSONOutput)
wsConfig = rootConfig.ForWorkspace(workspaceName)

// load Kong version after workspace
Expand Down Expand Up @@ -206,7 +226,15 @@ func syncMain(ctx context.Context, filenames []string, dry bool, parallelism,
return err
}

cprint.CreatePrintln("creating workspace", wsConfig.Workspace)
if enableJSONOutput {
workspace := diff.EntityState{
Name: wsConfig.Workspace,
Kind: "workspace",
}
jsonOutput.Changes.Creating = append(jsonOutput.Changes.Creating, workspace)
} else {
cprint.CreatePrintln("Creating workspace", wsConfig.Workspace)
}
if !dry {
_, err = rootClient.Workspaces.Create(ctx, &kong.Workspace{Name: &wsConfig.Workspace})
if err != nil {
Expand All @@ -232,14 +260,34 @@ func syncMain(ctx context.Context, filenames []string, dry bool, parallelism,
}

totalOps, err := performDiff(
ctx, currentState, targetState, dry, parallelism, delay, kongClient, mode == modeKonnect)
ctx, currentState, targetState, dry, parallelism, delay, kongClient, mode == modeKonnect, enableJSONOutput)
if err != nil {
return err
if enableJSONOutput {
var errs utils.ErrArray
if errors.As(err, &errs) {
jsonOutput.Errors = append(jsonOutput.Errors, errs.ErrorList()...)
} else {
jsonOutput.Errors = append(jsonOutput.Errors, err.Error())
}
} else {
return err
}
}

if diffCmdNonZeroExitCode && totalOps > 0 {
os.Exit(exitCodeDiffDetection)
}
if enableJSONOutput {
jsonOutputBytes, jsonErr := json.MarshalIndent(jsonOutput, "", "\t")
if jsonErr != nil {
return err
}
jsonOutputString := string(jsonOutputBytes)
if !noMaskValues {
jsonOutputString = diff.MaskEnvVarValue(jsonOutputString)
}

cprint.BluePrintLn(jsonOutputString + "\n")
}
return nil
}

Expand Down Expand Up @@ -281,6 +329,7 @@ func fetchCurrentState(ctx context.Context, client *kong.Client, dumpConfig dump

func performDiff(ctx context.Context, currentState, targetState *state.KongState,
dry bool, parallelism int, delay int, client *kong.Client, isKonnect bool,
enableJSONOutput bool,
) (int, error) {
s, err := diff.NewSyncer(diff.SyncerOpts{
CurrentState: currentState,
Expand All @@ -294,13 +343,29 @@ func performDiff(ctx context.Context, currentState, targetState *state.KongState
return 0, err
}

stats, errs := s.Solve(ctx, parallelism, dry)
stats, errs, changes := s.Solve(ctx, parallelism, dry, enableJSONOutput)
// print stats before error to report completed operations
printStats(stats)
if !enableJSONOutput {
printStats(stats)
}
if errs != nil {
return 0, utils.ErrArray{Errors: errs}
}
totalOps := stats.CreateOps.Count() + stats.UpdateOps.Count() + stats.DeleteOps.Count()

if enableJSONOutput {
jsonOutput.Changes = diff.EntityChanges{
Creating: append(jsonOutput.Changes.Creating, changes.Creating...),
Updating: append(jsonOutput.Changes.Updating, changes.Updating...),
Deleting: append(jsonOutput.Changes.Deleting, changes.Deleting...),
}
jsonOutput.Summary = diff.Summary{
Creating: stats.CreateOps.Count(),
Updating: stats.UpdateOps.Count(),
Deleting: stats.DeleteOps.Count(),
Total: totalOps,
}
}
return int(totalOps), nil
}

Expand Down
4 changes: 2 additions & 2 deletions cmd/common_konnect.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ func resetKonnectV2(ctx context.Context) error {
if err != nil {
return err
}
_, err = performDiff(ctx, currentState, targetState, false, 10, 0, client, true)
_, err = performDiff(ctx, currentState, targetState, false, 10, 0, client, true, resetJSONOutput)
if err != nil {
return err
}
Expand Down Expand Up @@ -258,7 +258,7 @@ func syncKonnect(ctx context.Context,
return err
}

stats, errs := s.Solve(ctx, parallelism, dry)
stats, errs, _ := s.Solve(ctx, parallelism, dry, false)
// print stats before error to report completed operations
printStats(stats)
if errs != nil {
Expand Down
5 changes: 4 additions & 1 deletion cmd/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ var (
diffCmdParallelism int
diffCmdNonZeroExitCode bool
diffWorkspace string
diffJSONOutput bool
)

// newDiffCmd represents the diff command
Expand All @@ -27,7 +28,7 @@ that will be created, updated, or deleted.
Args: validateNoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
return syncMain(cmd.Context(), diffCmdKongStateFile, true,
diffCmdParallelism, 0, diffWorkspace)
diffCmdParallelism, 0, diffWorkspace, diffJSONOutput)
},
PreRunE: func(cmd *cobra.Command, args []string) error {
if len(diffCmdKongStateFile) == 0 {
Expand Down Expand Up @@ -65,6 +66,8 @@ that will be created, updated, or deleted.
"and exit code 1 if an error occurs.")
diffCmd.Flags().BoolVar(&dumpConfig.SkipCACerts, "skip-ca-certificates",
false, "do not diff CA certificates.")
diffCmd.Flags().BoolVar(&diffJSONOutput, "json-output",
false, "generate command execution report in a JSON format")
addSilenceEventsFlag(diffCmd.Flags())
return diffCmd
}
5 changes: 4 additions & 1 deletion cmd/reset.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ var (
resetCmdForce bool
resetWorkspace string
resetAllWorkspaces bool
resetJSONOutput bool
)

// newResetCmd represents the reset command
Expand Down Expand Up @@ -99,7 +100,7 @@ By default, this command will ask for confirmation.`,
if err != nil {
return err
}
_, err = performDiff(ctx, currentState, targetState, false, 10, 0, wsClient, false)
_, err = performDiff(ctx, currentState, targetState, false, 10, 0, wsClient, false, resetJSONOutput)
if err != nil {
return err
}
Expand Down Expand Up @@ -128,6 +129,8 @@ By default, this command will ask for confirmation.`,
false, "reset only the RBAC resources (Kong Enterprise only).")
resetCmd.Flags().BoolVar(&dumpConfig.SkipCACerts, "skip-ca-certificates",
false, "do not reset CA certificates.")
resetCmd.Flags().BoolVar(&resetJSONOutput, "json-output",
false, "generate command execution report in a JSON format")

return resetCmd
}
5 changes: 4 additions & 1 deletion cmd/sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ var (
syncCmdParallelism int
syncCmdDBUpdateDelay int
syncWorkspace string
syncJSONOutput bool
)

// newSyncCmd represents the sync command
Expand All @@ -24,7 +25,7 @@ to get Kong's state in sync with the input state.`,
Args: validateNoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
return syncMain(cmd.Context(), syncCmdKongStateFile, false,
syncCmdParallelism, syncCmdDBUpdateDelay, syncWorkspace)
syncCmdParallelism, syncCmdDBUpdateDelay, syncWorkspace, syncJSONOutput)
},
PreRunE: func(cmd *cobra.Command, args []string) error {
if len(syncCmdKongStateFile) == 0 {
Expand Down Expand Up @@ -62,6 +63,8 @@ to get Kong's state in sync with the input state.`,
"See `db_update_propagation` in kong.conf.")
syncCmd.Flags().BoolVar(&dumpConfig.SkipCACerts, "skip-ca-certificates",
false, "do not sync CA certificates.")
syncCmd.Flags().BoolVar(&syncJSONOutput, "json-output",
false, "generate command execution report in a JSON format")
addSilenceEventsFlag(syncCmd.Flags())
return syncCmd
}
5 changes: 4 additions & 1 deletion cmd/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ var (
validateOnline bool
validateWorkspace string
validateParallelism int
validateJSONOutput bool
)

// newValidateCmd represents the diff command
Expand Down Expand Up @@ -107,6 +108,8 @@ this command unless --online flag is used.
"This takes precedence over _workspace fields in state files.")
validateCmd.Flags().IntVar(&validateParallelism, "parallelism",
10, "Maximum number of concurrent requests to Kong.")
validateCmd.Flags().BoolVar(&validateJSONOutput, "json-output",
false, "generate command execution report in a JSON format")

if err := ensureGetAllMethods(); err != nil {
panic(err.Error())
Expand Down Expand Up @@ -139,7 +142,7 @@ func getKongClient(ctx context.Context, targetContent *file.Content) (*kong.Clie
workspaceName := validateWorkspace
if validateWorkspace != "" {
// check if workspace exists
workspaceName := getWorkspaceName(validateWorkspace, targetContent)
workspaceName := getWorkspaceName(validateWorkspace, targetContent, validateJSONOutput)
workspaceExists, err := workspaceExists(ctx, rootConfig, workspaceName)
if err != nil {
return nil, err
Expand Down
5 changes: 5 additions & 0 deletions cprint/color.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ var (
createPrintln = color.New(color.FgGreen).PrintlnFunc()
deletePrintln = color.New(color.FgRed).PrintlnFunc()
updatePrintln = color.New(color.FgYellow).PrintlnFunc()
bluePrintln = color.New(color.BgBlue).PrintlnFunc()

// CreatePrintln is fmt.Println with red as foreground color.
CreatePrintln = func(a ...interface{}) {
Expand All @@ -69,4 +70,8 @@ var (
UpdatePrintln = func(a ...interface{}) {
conditionalPrintln(updatePrintln, a...)
}

BluePrintLn = func(a ...interface{}) {
conditionalPrintln(bluePrintln, a...)
}
)
Loading

0 comments on commit 7352609

Please sign in to comment.