Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(render): add new command to render final file #963

Merged
merged 4 commits into from
Jul 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)),
zekth marked this conversation as resolved.
Show resolved Hide resolved
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",
Tieske marked this conversation as resolved.
Show resolved Hide resolved
"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
Loading