Skip to content

Commit

Permalink
Add kong2tf command (#1391)
Browse files Browse the repository at this point in the history
* feat: Add new command for Kong to Terraform conversion

* Granular testing

* fix plugin route reference

* feat(kong2tf): PROTOTYPE kong2tf with import blocks and lifecycle-ignore blocks, plus appropriate switches; patches "route ID is read only field" bug

* Replace templated kong2tf with recursive implementation

* fix: linting issues

---------

Co-authored-by: battlebyte <[email protected]>
Co-authored-by: Jack Tysoe <[email protected]>
Co-authored-by: Prashansa Kulshrestha <[email protected]>
  • Loading branch information
4 people authored Sep 10, 2024
1 parent fb795ad commit a084b3c
Show file tree
Hide file tree
Showing 51 changed files with 2,920 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,5 @@ docs/cli-docs/

# generated test 'actuals'
kong2kic/testdata/**/*-actual.*
kong2tf/testdata/**/*-actual.*
kong2tf/terraform
95 changes: 95 additions & 0 deletions cmd/file_kong2tf.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package cmd

import (
"fmt"
"log"

"github.com/kong/deck/kong2tf"
"github.com/kong/go-apiops/filebasics"
"github.com/kong/go-apiops/logbasics"
"github.com/kong/go-database-reconciler/pkg/file"
"github.com/spf13/cobra"
)

var (
cmdKong2TfInputFilename string
cmdKong2TfOutputFilename string
cmdKong2TfGenerateImportsForControlPlaneID string
cmdKong2TfIgnoreCredentialChanges bool
)

// Executes the CLI command "kong2Tf"
func executeKong2Tf(cmd *cobra.Command, _ []string) error {
_ = sendAnalytics("file-kong2Tf", "", modeLocal)
var (
result string
err error
)

verbosity, _ := cmd.Flags().GetInt("verbose")
logbasics.Initialize(log.LstdFlags, verbosity)

logbasics.Info("Starting execution of executeKong2Tf")

inputContent, err := file.GetContentFromFiles([]string{cmdKong2TfInputFilename}, false)
if err != nil {
log.Printf("Error reading input file '%s'; %v", cmdKong2TfInputFilename, err)
return fmt.Errorf("failed reading input file '%s'; %w", cmdKong2TfInputFilename, err)
}
logbasics.Info("Successfully read input file '%s'", cmdKong2TfInputFilename)

logbasics.Info("Converting Kong configuration to Terraform")

var generateImportsForControlPlaneID *string
if cmdKong2TfGenerateImportsForControlPlaneID != "" {
generateImportsForControlPlaneID = &cmdKong2TfGenerateImportsForControlPlaneID
}
result, err = kong2tf.Convert(inputContent, generateImportsForControlPlaneID, cmdKong2TfIgnoreCredentialChanges)
if err != nil {
log.Printf("Error converting Kong configuration to Terraform; %v", err)
return fmt.Errorf("failed converting Kong configuration to Terraform; %w", err)
}
logbasics.Info("Successfully converted Kong configuration to Terraform")

logbasics.Info("Writing output to file '%s'", cmdKong2TfOutputFilename)
err = filebasics.WriteFile(cmdKong2TfOutputFilename, []byte(result))
if err != nil {
log.Printf("Error writing output to file '%s'; %v", cmdKong2TfOutputFilename, err)
return err
}
logbasics.Info("Successfully wrote output to file '%s'", cmdKong2TfOutputFilename)

logbasics.Info("Finished execution of executeKong2Tf")
return nil
}

//
//
// Define the CLI data for the kong2Tf command
//
//

func newKong2TfCmd() *cobra.Command {
kong2TfCmd := &cobra.Command{
Use: "kong2tf",
Short: "Convert Kong configuration files to Terraform resources",
Long: `Convert Kong configuration files to Terraform resources.
The kong2tf subcommand transforms Kong Gateway entities in deck format,
into Terraform resources.`,
RunE: executeKong2Tf,
Args: cobra.NoArgs,
}

kong2TfCmd.Flags().StringVarP(&cmdKong2TfInputFilename, "state", "s", "-",
"decK file to process. Use - to read from stdin.")
kong2TfCmd.Flags().StringVarP(&cmdKong2TfOutputFilename, "output-file", "o", "-",
"Output file to write. Use - to write to stdout.")
kong2TfCmd.Flags().StringVarP(&cmdKong2TfGenerateImportsForControlPlaneID,
"generate-imports-for-control-plane-id", "g", "", "Generate terraform import statements for the control plane ID.")
kong2TfCmd.Flags().BoolVar(&cmdKong2TfIgnoreCredentialChanges, "ignore-credential-changes", false,
"Enable flag to add a 'lifecycle' block to each consumer credential, "+
"that ignores any changes from local to remote state.")

return kong2TfCmd
}
1 change: 1 addition & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ It can be used to export, import, or sync entities to Kong.`,
fileCmd.AddCommand(newConvertCmd(false))
fileCmd.AddCommand(newValidateCmd(false, false)) // file-based validation
fileCmd.AddCommand(newKong2KicCmd())
fileCmd.AddCommand(newKong2TfCmd())
}
return rootCmd
}
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ require (
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/mitchellh/hashstructure v1.1.0
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,8 @@ github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZ
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/hashstructure v1.1.0 h1:P6P1hdjqAAknpY/M1CGipelZgp+4y9ja9kmUZPXP+H0=
github.com/mitchellh/hashstructure v1.1.0/go.mod h1:xUDAozZz0Wmdiufv0uyhnHkUTN6/6d8ulp4AwfLKrmA=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
Expand Down
51 changes: 51 additions & 0 deletions kong2tf/builder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package kong2tf

import (
"github.com/kong/go-database-reconciler/pkg/file"
)

type ITerraformBuilder interface {
buildControlPlaneVar(*string)
buildServices(*file.Content, *string)
buildRoutes(*file.Content, *string)
buildGlobalPlugins(*file.Content, *string)
buildConsumers(*file.Content, *string, bool)
buildConsumerGroups(*file.Content, *string)
buildUpstreams(*file.Content, *string)
buildCACertificates(*file.Content, *string)
buildCertificates(*file.Content, *string)
buildVaults(*file.Content, *string)
getContent() string
}

func getTerraformBuilder() ITerraformBuilder {
return newDefaultTerraformBuilder()
}

type Director struct {
builder ITerraformBuilder
}

func newDirector(builder ITerraformBuilder) *Director {
return &Director{
builder: builder,
}
}

func (d *Director) builTerraformResources(
content *file.Content,
generateImportsForControlPlaneID *string,
ignoreCredentialChanges bool,
) string {
d.builder.buildControlPlaneVar(generateImportsForControlPlaneID)
d.builder.buildGlobalPlugins(content, generateImportsForControlPlaneID)
d.builder.buildServices(content, generateImportsForControlPlaneID)
d.builder.buildUpstreams(content, generateImportsForControlPlaneID)
d.builder.buildRoutes(content, generateImportsForControlPlaneID)
d.builder.buildConsumers(content, generateImportsForControlPlaneID, ignoreCredentialChanges)
d.builder.buildConsumerGroups(content, generateImportsForControlPlaneID)
d.builder.buildCACertificates(content, generateImportsForControlPlaneID)
d.builder.buildCertificates(content, generateImportsForControlPlaneID)
d.builder.buildVaults(content, generateImportsForControlPlaneID)
return d.builder.getContent()
}
Loading

0 comments on commit a084b3c

Please sign in to comment.