Skip to content

Commit

Permalink
feat(render): add new command to render final file (#963)
Browse files Browse the repository at this point in the history
Co-authored-by: Vincent Le Goff <vincent.legoff@konghq.com>
  • Loading branch information
Tieske and zekth committed Jul 27, 2023
1 parent fa328f9 commit 4b19f9b
Show file tree
Hide file tree
Showing 29 changed files with 679 additions and 82 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,14 @@

> Release date: TBD
### Added

- Added a new command `file render` to render a final decK file. This will result in a file representing
the state as it would be synced online.
[#963](https://github.com/Kong/deck/pull/963)
- Added a new flag `--format` to `file convert` to enable JSON output.
[#963](https://github.com/Kong/deck/pull/963)

### Fixes

- Avoid misleading diffs when configuration file has empty tags.
Expand Down
2 changes: 1 addition & 1 deletion cmd/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ func syncMain(ctx context.Context, filenames []string, dry bool, parallelism,
Deleting: []diff.EntityState{},
}
}
targetContent, err := file.GetContentFromFiles(filenames)
targetContent, err := file.GetContentFromFiles(filenames, false)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/common_konnect.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ func syncKonnect(ctx context.Context,
httpClient := utils.HTTPClient()

// read target file
targetContent, err := file.GetContentFromFiles(filenames)
targetContent, err := file.GetContentFromFiles(filenames, false)
if err != nil {
return err
}
Expand Down
9 changes: 0 additions & 9 deletions cmd/file.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,9 @@
/*
Copyright © 2023 NAME HERE <EMAIL ADDRESS>
*/
package cmd

import (
"github.com/spf13/cobra"
)

//
//
// Define the CLI data for the file sub-command
//
//

func newAddFileCmd() *cobra.Command {
addFileCmd := &cobra.Command{
Use: "file [sub-command]...",
Expand Down
24 changes: 21 additions & 3 deletions cmd/convert.go → cmd/file_convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,22 @@ package cmd
import (
"fmt"
"os"
"strings"

"github.com/kong/deck/convert"
"github.com/kong/deck/cprint"
"github.com/kong/deck/file"
"github.com/kong/deck/utils"
"github.com/spf13/cobra"
)

var (
convertCmdSourceFormat string
convertCmdDestinationFormat string
convertCmdDestinationFormat string // konnect/kong-gateway-3.x/etc
convertCmdInputFile string
convertCmdOutputFile string
convertCmdAssumeYes bool
convertCmdStateFormat string // yaml/json output
)

func executeConvert(_ *cobra.Command, _ []string) error {
Expand All @@ -37,7 +40,13 @@ func executeConvert(_ *cobra.Command, _ []string) error {
return nil
}

err = convert.Convert(convertCmdInputFile, convertCmdOutputFile, sourceFormat, destinationFormat)
err = convert.Convert(
[]string{convertCmdInputFile},
convertCmdOutputFile,
file.Format(strings.ToUpper(convertCmdStateFormat)),
sourceFormat,
destinationFormat,
false)
if err != nil {
return fmt.Errorf("converting file: %w", err)
}
Expand All @@ -51,7 +60,13 @@ func executeConvert(_ *cobra.Command, _ []string) error {
return fmt.Errorf("getting files from directory: %w", err)
}
for _, filename := range files {
err = convert.Convert(filename, filename, sourceFormat, destinationFormat)
err = convert.Convert(
[]string{filename},
filename,
file.Format(strings.ToUpper(convertCmdStateFormat)),
sourceFormat,
destinationFormat,
false)
if err != nil {
return fmt.Errorf("converting '%s' file: %w", filename, err)
}
Expand Down Expand Up @@ -92,6 +107,9 @@ can be converted into a 'kong-gateway-3.x' configuration file.`,
"file to write configuration to after conversion. Use `-` to write to stdout.")
convertCmd.Flags().BoolVar(&convertCmdAssumeYes, "yes",
false, "assume `yes` to prompts and run non-interactively.")
convertCmd.Flags().StringVar(&convertCmdStateFormat, "format",
"yaml", "output file format: json or yaml.")

return convertCmd
}

Expand Down
50 changes: 50 additions & 0 deletions cmd/file_render.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package cmd

import (
"strings"

"github.com/kong/deck/convert"
"github.com/kong/deck/file"
"github.com/spf13/cobra"
)

var (
fileRenderCmdKongStateFile []string
fileRenderCmdKongFileOutput string
fileRenderCmdStateFormat string
)

func executeFileRenderCmd(_ *cobra.Command, _ []string) error {
return convert.Convert(
fileRenderCmdKongStateFile,
fileRenderCmdKongFileOutput,
file.Format(strings.ToUpper(fileRenderCmdStateFormat)),
convert.FormatDistributed,
convert.FormatKongGateway3x,
true)
}

func newFileRenderCmd() *cobra.Command {
renderCmd := &cobra.Command{
Use: "render",
Short: "Render the configuration as Kong declarative config",
Long: ``,
Args: cobra.ArbitraryArgs,
RunE: executeFileRenderCmd,
PreRunE: func(cmd *cobra.Command, args []string) error {
fileRenderCmdKongStateFile = args
if len(fileRenderCmdKongStateFile) == 0 {
fileRenderCmdKongStateFile = []string{"-"} // default to stdin
}
return preRunSilenceEventsFlag()
},
}

renderCmd.Flags().StringVarP(&fileRenderCmdKongFileOutput, "output-file", "o",
"-", "file to which to write Kong's configuration."+
"Use `-` to write to stdout.")
renderCmd.Flags().StringVar(&fileRenderCmdStateFormat, "format",
"yaml", "output file format: json or yaml.")

return renderCmd
}
1 change: 1 addition & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ It can be used to export, import, or sync entities to Kong.`,
fileCmd.AddCommand(newMergeCmd())
fileCmd.AddCommand(newPatchCmd())
fileCmd.AddCommand(newOpenapi2KongCmd())
fileCmd.AddCommand(newFileRenderCmd())
}
return rootCmd
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ this command unless --online flag is used.
_ = sendAnalytics("validate", "", mode)
// read target file
// this does json schema validation as well
targetContent, err := file.GetContentFromFiles(validateCmdKongStateFile)
targetContent, err := file.GetContentFromFiles(validateCmdKongStateFile, false)
if err != nil {
return err
}
Expand Down
1 change: 1 addition & 0 deletions convert/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
output.yaml
89 changes: 77 additions & 12 deletions convert/convert.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
package convert

import (
"context"
"fmt"
"strings"

"github.com/blang/semver/v4"
"github.com/kong/deck/cprint"
"github.com/kong/deck/dump"
"github.com/kong/deck/file"
"github.com/kong/deck/state"
"github.com/kong/deck/utils"
"github.com/kong/go-kong/kong"
)

type Format string

const (
// FormatDistributed represents the Deck configuration format.
FormatDistributed Format = "distributed"
// FormatKongGateway represents the Kong gateway format.
FormatKongGateway Format = "kong-gateway"
// FormatKonnect represents the Konnect format.
Expand All @@ -37,42 +43,61 @@ func ParseFormat(key string) (Format, error) {
return FormatKongGateway2x, nil
case FormatKongGateway3x:
return FormatKongGateway3x, nil
case FormatDistributed:
return FormatDistributed, nil
default:
return "", fmt.Errorf("invalid format: '%v'", key)
}
}

func Convert(inputFilename, outputFilename string, from, to Format) error {
var (
outputContent *file.Content
err error
)
func Convert(
inputFilenames []string,
outputFilename string,
outputFormat file.Format,
from Format,
to Format,
mockEnvVars bool,
) error {
var outputContent *file.Content

inputContent, err := file.GetContentFromFiles([]string{inputFilename})
inputContent, err := file.GetContentFromFiles(inputFilenames, mockEnvVars)
if err != nil {
return err
}

switch {
case from == FormatKongGateway && to == FormatKonnect:
if len(inputFilenames) > 1 {
return fmt.Errorf("only one input file can be provided when converting from Kong to Konnect format")
}
outputContent, err = convertKongGatewayToKonnect(inputContent)
if err != nil {
return err
}

case from == FormatKongGateway2x && to == FormatKongGateway3x:
outputContent, err = convertKongGateway2xTo3x(inputContent, inputFilename)
if len(inputFilenames) > 1 {
return fmt.Errorf("only one input file can be provided when converting from Kong 2.x to Kong 3.x format")
}
outputContent, err = convertKongGateway2xTo3x(inputContent, inputFilenames[0])
if err != nil {
return err
}

case from == FormatDistributed && to == FormatKongGateway,
from == FormatDistributed && to == FormatKongGateway2x,
from == FormatDistributed && to == FormatKongGateway3x:
outputContent, err = convertDistributedToKong(inputContent, outputFilename, outputFormat, to)
if err != nil {
return err
}

default:
return fmt.Errorf("cannot convert from '%s' to '%s' format", from, to)
}

err = file.WriteContentToFile(outputContent, outputFilename, file.YAML)
if err != nil {
return err
}
return nil
err = file.WriteContentToFile(outputContent, outputFilename, outputFormat)
return err
}

func convertKongGateway2xTo3x(input *file.Content, filename string) (*file.Content, error) {
Expand Down Expand Up @@ -195,3 +220,43 @@ func removeServiceName(service *file.FService) *file.FService {
serviceCopy.ID = kong.String(utils.UUID())
return serviceCopy
}

// convertDistributedToKong is used to convert one or many distributed format
// files to create one Kong Gateway declarative config. It also leverages some
// deck features like the defaults/centralized plugin configurations.
func convertDistributedToKong(
targetContent *file.Content,
outputFilename string,
format file.Format,
kongFormat Format,
) (*file.Content, error) {
var version semver.Version

switch kongFormat { //nolint:exhaustive
case FormatKongGateway,
FormatKongGateway3x:
version = semver.Version{Major: 3, Minor: 0}
case FormatKongGateway2x:
version = semver.Version{Major: 2, Minor: 8}
}

s, _ := state.NewKongState()
rawState, err := file.Get(context.Background(), targetContent, file.RenderConfig{
CurrentState: s,
KongVersion: version,
}, dump.Config{}, nil)
if err != nil {
return nil, err
}
targetState, err := state.Get(rawState)
if err != nil {
return nil, err
}

// file.KongStateToContent calls file.WriteContentToFile
return file.KongStateToContent(targetState, file.WriteConfig{
Filename: outputFilename,
FileFormat: format,
KongVersion: version.String(),
})
}
Loading

0 comments on commit 4b19f9b

Please sign in to comment.