Skip to content

Commit a0c7951

Browse files
zekthTieske
authored andcommitted
feat(render): add new command to render final file
1 parent 95bc95f commit a0c7951

29 files changed

+674
-80
lines changed

CHANGELOG.md

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

9398
### Fixes
9499

cmd/common.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ func syncMain(ctx context.Context, filenames []string, dry bool, parallelism,
8484
delay int, workspace string,
8585
) error {
8686
// read target file
87-
targetContent, err := file.GetContentFromFiles(filenames)
87+
targetContent, err := file.GetContentFromFiles(filenames, false)
8888
if err != nil {
8989
return err
9090
}

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

+3-10
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,17 @@
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{
18-
Use: "file [sub-command]...",
9+
Use: "file",
1910
Short: "Sub-command to host the decK file manipulation operations",
2011
Long: `Sub-command to host the decK file manipulation operations`,
2112
}
2213

14+
addFileCmd.AddCommand(newFileRenderCmd())
15+
2316
return addFileCmd
2417
}

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

+21-3
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,22 @@ import (
44
"fmt"
55
"log"
66
"os"
7+
"strings"
78

89
"github.com/kong/deck/convert"
910
"github.com/kong/deck/cprint"
11+
"github.com/kong/deck/file"
1012
"github.com/kong/deck/utils"
1113
"github.com/spf13/cobra"
1214
)
1315

1416
var (
1517
convertCmdSourceFormat string
16-
convertCmdDestinationFormat string
18+
convertCmdDestinationFormat string // konnect/kong-gateway-3.x/etc
1719
convertCmdInputFile string
1820
convertCmdOutputFile string
1921
convertCmdAssumeYes bool
22+
convertCmdStateFormat string // yaml/json output
2023
)
2124

2225
func executeConvert(_ *cobra.Command, _ []string) error {
@@ -38,7 +41,13 @@ func executeConvert(_ *cobra.Command, _ []string) error {
3841
return nil
3942
}
4043

41-
err = convert.Convert(convertCmdInputFile, convertCmdOutputFile, sourceFormat, destinationFormat)
44+
err = convert.Convert(
45+
[]string{convertCmdInputFile},
46+
convertCmdOutputFile,
47+
file.Format(strings.ToUpper(convertCmdStateFormat)),
48+
sourceFormat,
49+
destinationFormat,
50+
false)
4251
if err != nil {
4352
return fmt.Errorf("converting file: %w", err)
4453
}
@@ -52,7 +61,13 @@ func executeConvert(_ *cobra.Command, _ []string) error {
5261
return fmt.Errorf("getting files from directory: %w", err)
5362
}
5463
for _, filename := range files {
55-
err = convert.Convert(filename, filename, sourceFormat, destinationFormat)
64+
err = convert.Convert(
65+
[]string{filename},
66+
filename,
67+
file.Format(strings.ToUpper(convertCmdStateFormat)),
68+
sourceFormat,
69+
destinationFormat,
70+
false)
5671
if err != nil {
5772
return fmt.Errorf("converting '%s' file: %w", filename, err)
5873
}
@@ -100,6 +115,9 @@ can be converted into a 'kong-gateway-3.x' configuration file.`,
100115
"file to write configuration to after conversion. Use `-` to write to stdout.")
101116
convertCmd.Flags().BoolVar(&convertCmdAssumeYes, "yes",
102117
false, "assume `yes` to prompts and run non-interactively.")
118+
convertCmd.Flags().StringVar(&convertCmdStateFormat, "format",
119+
"yaml", "output file format: json or yaml.")
120+
103121
return convertCmd
104122
}
105123

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

+1
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@ It can be used to export, import, or sync entities to Kong.`,
226226
fileCmd.AddCommand(newOpenapi2KongCmd())
227227
fileCmd.AddCommand(newConvertCmd(false))
228228
fileCmd.AddCommand(newValidateCmd()) // alias; since this does both file+online
229+
fileCmd.AddCommand(newFileRenderCmd())
229230
}
230231
return rootCmd
231232
}

cmd/validate.go

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

convert/.gitignore

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

convert/convert.go

+72-9
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,38 +43,55 @@ 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 {
53+
func Convert(
54+
inputFilenames []string,
55+
outputFilename string,
56+
outputFormat file.Format,
57+
from Format,
58+
to Format,
59+
mockEnvVars bool,
60+
) error {
4661
var (
4762
outputContent *file.Content
4863
err error
4964
)
5065

51-
inputContent, err := file.GetContentFromFiles([]string{inputFilename})
66+
inputContent, err := file.GetContentFromFiles(inputFilenames, mockEnvVars)
5267
if err != nil {
5368
return err
5469
}
5570

5671
switch {
5772
case from == FormatKongGateway && to == FormatKonnect:
58-
outputContent, err = convertKongGatewayToKonnect(inputContent)
59-
if err != nil {
60-
return err
73+
if len(inputFilenames) > 1 {
74+
return fmt.Errorf("only one input file can be provided when converting from Kong to Konnect format")
6175
}
76+
outputContent, err = convertKongGatewayToKonnect(inputContent)
6277
case from == FormatKongGateway2x && to == FormatKongGateway3x:
63-
outputContent, err = convertKongGateway2xTo3x(inputContent, inputFilename)
64-
if err != nil {
65-
return err
78+
if len(inputFilenames) > 1 {
79+
return fmt.Errorf("only one input file can be provided when converting from Kong 2.x to Kong 3.x format")
6680
}
81+
outputContent, err = convertKongGateway2xTo3x(inputContent, inputFilenames[0])
82+
case from == FormatDistributed && to == FormatKongGateway,
83+
from == FormatDistributed && to == FormatKongGateway2x,
84+
from == FormatDistributed && to == FormatKongGateway3x:
85+
outputContent, err = convertDistributedToKong(inputContent, outputFilename, outputFormat, to)
6786
default:
6887
return fmt.Errorf("cannot convert from '%s' to '%s' format", from, to)
6988
}
7089

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

0 commit comments

Comments
 (0)