Skip to content

Commit 05e47cb

Browse files
zekthTieske
authored andcommitted
feat(render): add new command to render final file
1 parent d3f0a67 commit 05e47cb

29 files changed

+678
-82
lines changed

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,11 @@
9393
validated, which allows for working with incomplete or even invalid files in a pipeline.
9494
The functionality is imported from the [go-apiops library](https://github.com/Kong/go-apiops).
9595
[#939](https://github.com/Kong/deck/pull/939)
96+
- Added a new command `file render` to render a final decK file. This will result in a file representing
97+
the state as it would be synced online.
98+
[#963](https://github.com/Kong/deck/pull/963)
99+
- Added a new flag `--format` to `file convert` to enable JSON output.
100+
[#963](https://github.com/Kong/deck/pull/963)
96101

97102
### Fixes
98103

cmd/common.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ func syncMain(ctx context.Context, filenames []string, dry bool, parallelism,
104104
Deleting: []diff.EntityState{},
105105
}
106106
}
107-
targetContent, err := file.GetContentFromFiles(filenames)
107+
targetContent, err := file.GetContentFromFiles(filenames, false)
108108
if err != nil {
109109
return err
110110
}

cmd/common_konnect.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ func syncKonnect(ctx context.Context,
183183
httpClient := utils.HTTPClient()
184184

185185
// read target file
186-
targetContent, err := file.GetContentFromFiles(filenames)
186+
targetContent, err := file.GetContentFromFiles(filenames, false)
187187
if err != nil {
188188
return err
189189
}

cmd/file.go

-9
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,9 @@
1-
/*
2-
Copyright © 2023 NAME HERE <EMAIL ADDRESS>
3-
*/
41
package cmd
52

63
import (
74
"github.com/spf13/cobra"
85
)
96

10-
//
11-
//
12-
// Define the CLI data for the file sub-command
13-
//
14-
//
15-
167
func newAddFileCmd() *cobra.Command {
178
addFileCmd := &cobra.Command{
189
Use: "file [sub-command]...",

cmd/convert.go renamed to cmd/file_convert.go

+21-3
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,22 @@ package cmd
33
import (
44
"fmt"
55
"os"
6+
"strings"
67

78
"github.com/kong/deck/convert"
89
"github.com/kong/deck/cprint"
10+
"github.com/kong/deck/file"
911
"github.com/kong/deck/utils"
1012
"github.com/spf13/cobra"
1113
)
1214

1315
var (
1416
convertCmdSourceFormat string
15-
convertCmdDestinationFormat string
17+
convertCmdDestinationFormat string // konnect/kong-gateway-3.x/etc
1618
convertCmdInputFile string
1719
convertCmdOutputFile string
1820
convertCmdAssumeYes bool
21+
convertCmdStateFormat string // yaml/json output
1922
)
2023

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

40-
err = convert.Convert(convertCmdInputFile, convertCmdOutputFile, sourceFormat, destinationFormat)
43+
err = convert.Convert(
44+
[]string{convertCmdInputFile},
45+
convertCmdOutputFile,
46+
file.Format(strings.ToUpper(convertCmdStateFormat)),
47+
sourceFormat,
48+
destinationFormat,
49+
false)
4150
if err != nil {
4251
return fmt.Errorf("converting file: %w", err)
4352
}
@@ -51,7 +60,13 @@ func executeConvert(_ *cobra.Command, _ []string) error {
5160
return fmt.Errorf("getting files from directory: %w", err)
5261
}
5362
for _, filename := range files {
54-
err = convert.Convert(filename, filename, sourceFormat, destinationFormat)
63+
err = convert.Convert(
64+
[]string{filename},
65+
filename,
66+
file.Format(strings.ToUpper(convertCmdStateFormat)),
67+
sourceFormat,
68+
destinationFormat,
69+
false)
5570
if err != nil {
5671
return fmt.Errorf("converting '%s' file: %w", filename, err)
5772
}
@@ -92,6 +107,9 @@ can be converted into a 'kong-gateway-3.x' configuration file.`,
92107
"file to write configuration to after conversion. Use `-` to write to stdout.")
93108
convertCmd.Flags().BoolVar(&convertCmdAssumeYes, "yes",
94109
false, "assume `yes` to prompts and run non-interactively.")
110+
convertCmd.Flags().StringVar(&convertCmdStateFormat, "format",
111+
"yaml", "output file format: json or yaml.")
112+
95113
return convertCmd
96114
}
97115

cmd/file_render.go

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package cmd
2+
3+
import (
4+
"strings"
5+
6+
"github.com/kong/deck/convert"
7+
"github.com/kong/deck/file"
8+
"github.com/spf13/cobra"
9+
)
10+
11+
var (
12+
fileRenderCmdKongStateFile []string
13+
fileRenderCmdKongFileOutput string
14+
fileRenderCmdStateFormat string
15+
)
16+
17+
func executeFileRenderCmd(_ *cobra.Command, _ []string) error {
18+
return convert.Convert(
19+
fileRenderCmdKongStateFile,
20+
fileRenderCmdKongFileOutput,
21+
file.Format(strings.ToUpper(fileRenderCmdStateFormat)),
22+
convert.FormatDistributed,
23+
convert.FormatKongGateway3x,
24+
true)
25+
}
26+
27+
func newFileRenderCmd() *cobra.Command {
28+
renderCmd := &cobra.Command{
29+
Use: "render",
30+
Short: "Render the configuration as Kong declarative config",
31+
Long: ``,
32+
Args: validateNoArgs,
33+
RunE: executeFileRenderCmd,
34+
PreRunE: func(cmd *cobra.Command, args []string) error {
35+
fileRenderCmdKongStateFile = args
36+
if len(fileRenderCmdKongStateFile) == 0 {
37+
fileRenderCmdKongStateFile = []string{"-"} // default to stdin
38+
}
39+
return preRunSilenceEventsFlag()
40+
},
41+
}
42+
43+
renderCmd.Flags().StringVarP(&fileRenderCmdKongFileOutput, "output-file", "o",
44+
"-", "file to which to write Kong's configuration."+
45+
"Use `-` to write to stdout.")
46+
renderCmd.Flags().StringVar(&fileRenderCmdStateFormat, "format",
47+
"yaml", "output file format: json or yaml.")
48+
49+
return renderCmd
50+
}

cmd/root.go

+3
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,9 @@ It can be used to export, import, or sync entities to Kong.`,
224224
fileCmd.AddCommand(newMergeCmd())
225225
fileCmd.AddCommand(newPatchCmd())
226226
fileCmd.AddCommand(newOpenapi2KongCmd())
227+
// fileCmd.AddCommand(newConvertCmd())
228+
// fileCmd.AddCommand(newValidateCmd()) // alias; since this does both file+online
229+
fileCmd.AddCommand(newFileRenderCmd())
227230
}
228231
return rootCmd
229232
}

cmd/validate.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ this command unless --online flag is used.
4444
_ = sendAnalytics("validate", "", mode)
4545
// read target file
4646
// this does json schema validation as well
47-
targetContent, err := file.GetContentFromFiles(validateCmdKongStateFile)
47+
targetContent, err := file.GetContentFromFiles(validateCmdKongStateFile, false)
4848
if err != nil {
4949
return err
5050
}

convert/.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
output.yaml

convert/convert.go

+77-12
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,24 @@
11
package convert
22

33
import (
4+
"context"
45
"fmt"
56
"strings"
67

8+
"github.com/blang/semver/v4"
79
"github.com/kong/deck/cprint"
10+
"github.com/kong/deck/dump"
811
"github.com/kong/deck/file"
12+
"github.com/kong/deck/state"
913
"github.com/kong/deck/utils"
1014
"github.com/kong/go-kong/kong"
1115
)
1216

1317
type Format string
1418

1519
const (
20+
// FormatDistributed represents the Deck configuration format.
21+
FormatDistributed Format = "distributed"
1622
// FormatKongGateway represents the Kong gateway format.
1723
FormatKongGateway Format = "kong-gateway"
1824
// FormatKonnect represents the Konnect format.
@@ -37,42 +43,61 @@ func ParseFormat(key string) (Format, error) {
3743
return FormatKongGateway2x, nil
3844
case FormatKongGateway3x:
3945
return FormatKongGateway3x, nil
46+
case FormatDistributed:
47+
return FormatDistributed, nil
4048
default:
4149
return "", fmt.Errorf("invalid format: '%v'", key)
4250
}
4351
}
4452

45-
func Convert(inputFilename, outputFilename string, from, to Format) error {
46-
var (
47-
outputContent *file.Content
48-
err error
49-
)
53+
func Convert(
54+
inputFilenames []string,
55+
outputFilename string,
56+
outputFormat file.Format,
57+
from Format,
58+
to Format,
59+
mockEnvVars bool,
60+
) error {
61+
var outputContent *file.Content
5062

51-
inputContent, err := file.GetContentFromFiles([]string{inputFilename})
63+
inputContent, err := file.GetContentFromFiles(inputFilenames, mockEnvVars)
5264
if err != nil {
5365
return err
5466
}
5567

5668
switch {
5769
case from == FormatKongGateway && to == FormatKonnect:
70+
if len(inputFilenames) > 1 {
71+
return fmt.Errorf("only one input file can be provided when converting from Kong to Konnect format")
72+
}
5873
outputContent, err = convertKongGatewayToKonnect(inputContent)
5974
if err != nil {
6075
return err
6176
}
77+
6278
case from == FormatKongGateway2x && to == FormatKongGateway3x:
63-
outputContent, err = convertKongGateway2xTo3x(inputContent, inputFilename)
79+
if len(inputFilenames) > 1 {
80+
return fmt.Errorf("only one input file can be provided when converting from Kong 2.x to Kong 3.x format")
81+
}
82+
outputContent, err = convertKongGateway2xTo3x(inputContent, inputFilenames[0])
6483
if err != nil {
6584
return err
6685
}
86+
87+
case from == FormatDistributed && to == FormatKongGateway,
88+
from == FormatDistributed && to == FormatKongGateway2x,
89+
from == FormatDistributed && to == FormatKongGateway3x:
90+
outputContent, err = convertDistributedToKong(inputContent, outputFilename, outputFormat, to)
91+
if err != nil {
92+
return err
93+
}
94+
6795
default:
6896
return fmt.Errorf("cannot convert from '%s' to '%s' format", from, to)
6997
}
7098

71-
err = file.WriteContentToFile(outputContent, outputFilename, file.YAML)
72-
if err != nil {
73-
return err
74-
}
75-
return nil
99+
err = file.WriteContentToFile(outputContent, outputFilename, outputFormat)
100+
return err
76101
}
77102

78103
func convertKongGateway2xTo3x(input *file.Content, filename string) (*file.Content, error) {
@@ -195,3 +220,43 @@ func removeServiceName(service *file.FService) *file.FService {
195220
serviceCopy.ID = kong.String(utils.UUID())
196221
return serviceCopy
197222
}
223+
224+
// convertDistributedToKong is used to convert one or many distributed format
225+
// files to create one Kong Gateway declarative config. It also leverages some
226+
// deck features like the defaults/centralized plugin configurations.
227+
func convertDistributedToKong(
228+
targetContent *file.Content,
229+
outputFilename string,
230+
format file.Format,
231+
kongFormat Format,
232+
) (*file.Content, error) {
233+
var version semver.Version
234+
235+
switch kongFormat { //nolint:exhaustive
236+
case FormatKongGateway,
237+
FormatKongGateway3x:
238+
version = semver.Version{Major: 3, Minor: 0}
239+
case FormatKongGateway2x:
240+
version = semver.Version{Major: 2, Minor: 8}
241+
}
242+
243+
s, _ := state.NewKongState()
244+
rawState, err := file.Get(context.Background(), targetContent, file.RenderConfig{
245+
CurrentState: s,
246+
KongVersion: version,
247+
}, dump.Config{}, nil)
248+
if err != nil {
249+
return nil, err
250+
}
251+
targetState, err := state.Get(rawState)
252+
if err != nil {
253+
return nil, err
254+
}
255+
256+
// file.KongStateToContent calls file.WriteContentToFile
257+
return file.KongStateToContent(targetState, file.WriteConfig{
258+
Filename: outputFilename,
259+
FileFormat: format,
260+
KongVersion: version.String(),
261+
})
262+
}

0 commit comments

Comments
 (0)