Skip to content

Commit

Permalink
feat(render): add new command to render final file
Browse files Browse the repository at this point in the history
  • Loading branch information
zekth authored and Tieske committed Jul 14, 2023
1 parent 95bc95f commit 1e23d9a
Show file tree
Hide file tree
Showing 29 changed files with 671 additions and 79 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@
validated, which allows for working with incomplete or even invalid files in a pipeline.
The functionality is imported from the [go-apiops library](https://github.com/Kong/go-apiops).
[#939](https://github.com/Kong/deck/pull/939)
- 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.
[#939](https://github.com/Kong/deck/pull/939)

### Fixes

Expand Down
2 changes: 1 addition & 1 deletion cmd/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ func syncMain(ctx context.Context, filenames []string, dry bool, parallelism,
delay int, workspace string,
) error {
// read target file
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
13 changes: 3 additions & 10 deletions cmd/file.go
Original file line number Diff line number Diff line change
@@ -1,24 +1,17 @@
/*
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]...",
Use: "file",
Short: "Sub-command to host the decK file manipulation operations",
Long: `Sub-command to host the decK file manipulation operations`,
}

addFileCmd.AddCommand(newFileRenderCmd())

return addFileCmd
}
4 changes: 2 additions & 2 deletions cmd/convert.go → cmd/file_convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func executeConvert(_ *cobra.Command, _ []string) error {
return nil
}

err = convert.Convert(convertCmdInputFile, convertCmdOutputFile, sourceFormat, destinationFormat)
err = convert.Convert([]string{convertCmdInputFile}, convertCmdOutputFile, sourceFormat, destinationFormat, false)
if err != nil {
return fmt.Errorf("converting file: %w", err)
}
Expand All @@ -52,7 +52,7 @@ 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, sourceFormat, destinationFormat, false)
if err != nil {
return fmt.Errorf("converting '%s' file: %w", filename, err)
}
Expand Down
74 changes: 74 additions & 0 deletions cmd/file_render.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package cmd

import (
"context"
"fmt"
"strings"

"github.com/blang/semver/v4"
"github.com/kong/deck/file"
"github.com/kong/deck/state"
"github.com/spf13/cobra"
)

var (
fileRenderCmdKongStateFile []string
fileRenderCmdKongFileOutput string
fileRenderCmdStateFormat string
)

func newFileRenderCmd() *cobra.Command {
renderCmd := &cobra.Command{
Use: "render",
Short: "Render the configuration as Kong declarative config",
Long: ``,
Args: validateNoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
return render(cmd.Context(), fileRenderCmdKongStateFile, fileRenderCmdStateFormat)
},
PreRunE: func(cmd *cobra.Command, args []string) error {
if len(fileRenderCmdStateFormat) == 0 {
return fmt.Errorf("a state file with Kong's configuration " +
"must be specified using `-s`/`--state` flag")
}
return preRunSilenceEventsFlag()
},
}

renderCmd.Flags().StringSliceVarP(&fileRenderCmdKongStateFile,
"state", "s", []string{"-"}, "file(s) containing Kong's configuration.\n"+
"This flag can be specified multiple times for multiple files.\n"+
"Use `-` to read from stdin.")
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
}

func render(ctx context.Context, filenames []string, format string) error {
targetContent, err := file.GetContentFromFiles(filenames, true)
if err != nil {
return err
}
s, _ := state.NewKongState()
rawState, err := file.Get(ctx, targetContent, file.RenderConfig{
CurrentState: s,
KongVersion: semver.Version{Major: 3, Minor: 0},
}, dumpConfig, nil)
if err != nil {
return err
}
targetState, err := state.Get(rawState)
if err != nil {
return err
}

return file.KongStateToFile(targetState, file.WriteConfig{
Filename: fileRenderCmdKongFileOutput,
FileFormat: file.Format(strings.ToUpper(format)),
KongVersion: "3.0.0",
})
}
1 change: 1 addition & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ It can be used to export, import, or sync entities to Kong.`,
fileCmd.AddCommand(newOpenapi2KongCmd())
fileCmd.AddCommand(newConvertCmd(false))
fileCmd.AddCommand(newValidateCmd()) // alias; since this does both file+online
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 @@ -43,7 +43,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
75 changes: 66 additions & 9 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,38 +43,49 @@ 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 {
func Convert(inputFilenames []string, outputFilename string, from, to Format, mockEnvVars bool) error {
const outputFormat = file.YAML
var (
outputContent *file.Content
err error
)

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

switch {
case from == FormatKongGateway && to == FormatKonnect:
outputContent, err = convertKongGatewayToKonnect(inputContent)
if err != nil {
return err
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)
case from == FormatKongGateway2x && to == FormatKongGateway3x:
outputContent, err = convertKongGateway2xTo3x(inputContent, inputFilename)
if err != nil {
return err
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])
case from == FormatDistributed && to == FormatKongGateway,
from == FormatDistributed && to == FormatKongGateway2x,
from == FormatDistributed && to == FormatKongGateway3x:
outputContent, err = convertDistributedToKong(inputContent, outputFilename, outputFormat, to)
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
}

err = file.WriteContentToFile(outputContent, outputFilename, outputFormat)
if err != nil {
return err
}
Expand Down Expand Up @@ -195,3 +212,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 1e23d9a

Please sign in to comment.