Skip to content

Commit

Permalink
Add terraform generate command skeleton
Browse files Browse the repository at this point in the history
  • Loading branch information
sergiught committed Aug 7, 2023
1 parent 1ddffec commit e34ff05
Show file tree
Hide file tree
Showing 3 changed files with 198 additions and 0 deletions.
1 change: 1 addition & 0 deletions internal/cli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ func addSubCommands(rootCmd *cobra.Command, cli *cli) {
rootCmd.AddCommand(testCmd(cli))
rootCmd.AddCommand(logsCmd(cli))
rootCmd.AddCommand(apiCmd(cli))
rootCmd.AddCommand(terraformCmd(cli))

// keep completion at the bottom:
rootCmd.AddCommand(completionCmd(cli))
Expand Down
112 changes: 112 additions & 0 deletions internal/cli/terraform.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package cli

import (
"os"
"path"

"github.com/spf13/cobra"
)

var tfFlags = terraformFlags{
OutputDIR: Flag{
Name: "Output Dir",
LongForm: "output-dir",
ShortForm: "o",
Help: "Output directory for the generated Terraform config files.",
},
}

type (
terraformFlags struct {
OutputDIR Flag
}

terraformInputs struct {
OutputDIR string
}
)

func terraformCmd(cli *cli) *cobra.Command {
cmd := &cobra.Command{
Use: "terraform",
Aliases: []string{"tf"},
Short: "Manage terraform configuration for your Auth0 Tenant",
Long: "This command facilitates the integration of Auth0 with [Terraform](https://www.terraform.io/), an " +
"infrastructure as code tool.",
}

cmd.SetUsageTemplate(resourceUsageTemplate())
cmd.AddCommand(generateTerraformCmd(cli))

return cmd
}

func generateTerraformCmd(cli *cli) *cobra.Command {
var inputs terraformInputs

cmd := &cobra.Command{
Use: "generate",
Aliases: []string{"gen", "export"},
Short: "Generate terraform configuration for your Auth0 Tenant",
Long: "This command is designed to streamline the process of generating Terraform configuration files for " +
"your Auth0 resources, serving as a bridge between the two.\n\n It automatically scans your Auth0 Tenant " +
"and compiles a set of Terraform configuration files based on the existing resources and configurations." +
"\n\nThe generated Terraform files are written in HashiCorp Configuration Language (HCL).",
RunE: generateTerraformCmdRun(cli, &inputs),
}

tfFlags.OutputDIR.RegisterString(cmd, &inputs.OutputDIR, "./")

return cmd
}

func generateTerraformCmdRun(cli *cli, inputs *terraformInputs) func(cmd *cobra.Command, args []string) error {
return func(cmd *cobra.Command, args []string) error {
if err := generateTerraformConfigFiles(inputs); err != nil {
return err
}

cli.renderer.Infof("Terraform config files generated successfully.")
cli.renderer.Infof(
"Follow this " +
"[quickstart](https://registry.terraform.io/providers/auth0/auth0/latest/docs/guides/quickstart) " +
"to go through setting up an Auth0 application for the provider to authenticate against and manage " +
"resources.",
)

return nil
}
}

func generateTerraformConfigFiles(inputs *terraformInputs) error {
const readWritePermission = 0755
if err := os.MkdirAll(inputs.OutputDIR, readWritePermission); err != nil {
if !os.IsExist(err) {
return err
}
}

mainTerraformConfigFile, err := os.Create(path.Join(inputs.OutputDIR, "main.tf"))
if err != nil {
return err
}
defer mainTerraformConfigFile.Close()

mainTerraformConfigFileContent := `terraform {
required_version = "~> 1.5.0"
required_providers {
auth0 = {
source = "auth0/auth0"
version = "1.0.0-beta.1"
}
}
}
provider "auth0" {
debug = true
}
`

_, err = mainTerraformConfigFile.WriteString(mainTerraformConfigFileContent)
return err
}
85 changes: 85 additions & 0 deletions internal/cli/terraform_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package cli

import (
"os"
"path"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestGenerateTerraformConfigFiles(t *testing.T) {
testInputs := terraformInputs{
OutputDIR: "./terraform/dev",
}
defer os.RemoveAll(testInputs.OutputDIR)

t.Run("it can correctly generate the terraform main config file", func(t *testing.T) {
assertTerraformConfigFilesWereGeneratedWithCorrectContent(t, &testInputs)
})

t.Run("it can correctly generate the terraform main config file even if the dir exists", func(t *testing.T) {
err := os.MkdirAll(testInputs.OutputDIR, 0755)
require.NoError(t, err)

assertTerraformConfigFilesWereGeneratedWithCorrectContent(t, &testInputs)
})

t.Run("it fails to create the directory if path is a read only location", func(t *testing.T) {
testInputs := terraformInputs{
OutputDIR: "/terraform/dev",
}

err := generateTerraformConfigFiles(&testInputs)
assert.EqualError(t, err, "mkdir /terraform: read-only file system")
})

t.Run("it fails to create the main.tf file if file is already created and read only", func(t *testing.T) {
err := os.MkdirAll(testInputs.OutputDIR, 0755)
require.NoError(t, err)

mainFilePath := path.Join(testInputs.OutputDIR, "main.tf")
_, err = os.Create(mainFilePath)
require.NoError(t, err)

err = os.Chmod(mainFilePath, 0444)
require.NoError(t, err)

err = generateTerraformConfigFiles(&testInputs)
assert.EqualError(t, err, "open terraform/dev/main.tf: permission denied")
})
}

func assertTerraformConfigFilesWereGeneratedWithCorrectContent(t *testing.T, testInputs *terraformInputs) {
err := generateTerraformConfigFiles(testInputs)
require.NoError(t, err)

// Assert that the directory was created.
_, err = os.Stat(testInputs.OutputDIR)
assert.NoError(t, err)

// Assert that the main.tf file was created with the correct content.
mainTerraformConfigFilePath := path.Join(testInputs.OutputDIR, "main.tf")
_, err = os.Stat(mainTerraformConfigFilePath)
assert.NoError(t, err)

expectedContent := `terraform {
required_version = "~> 1.5.0"
required_providers {
auth0 = {
source = "auth0/auth0"
version = "1.0.0-beta.1"
}
}
}
provider "auth0" {
debug = true
}
`
// Read the file content and check if it matches the expected content
content, err := os.ReadFile(mainTerraformConfigFilePath)
assert.NoError(t, err)
assert.Equal(t, expectedContent, string(content))
}

0 comments on commit e34ff05

Please sign in to comment.