Skip to content

Commit

Permalink
improve error handling and use json marshall better, add a small test…
Browse files Browse the repository at this point in the history
… for sem ver
  • Loading branch information
fitz7 committed Dec 14, 2023
1 parent c81ec34 commit 1d85eae
Show file tree
Hide file tree
Showing 7 changed files with 139 additions and 63 deletions.
16 changes: 15 additions & 1 deletion cmd/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,24 @@ var moduleCmd = &cobra.Command{
BackendType: backend,
}

err = tfmodule.CreateModule(createModuleOptions)
fullModulePath, err := tfmodule.CreateModuleDir(createModuleOptions)
if err != nil {
return err
}
defaultFiles, err := tfmodule.CreateDefaultModuleFiles(fullModulePath)
if err != nil {
return fmt.Errorf("error creating moduleName files: %w", err)
}
err = tfmodule.PopulateVersionsFile(defaultFiles[tfmodule.VersionsFile], createModuleOptions)
if err != nil {
return fmt.Errorf("error populating the versions.tf file: %w", err)
}

defer func() {
for _, file := range defaultFiles {
_ = file.Close()
}
}()
return nil
},
}
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ require (
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0-rc5 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
github.com/pkg/errors v0.9.1 // indirect
Expand Down Expand Up @@ -380,7 +380,7 @@ require (
go.uber.org/zap v1.24.0 // indirect
gocloud.dev v0.34.0 // indirect
golang.org/x/crypto v0.16.0 // indirect
golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb // indirect
golang.org/x/exp v0.0.0-20231214170342-aacd6d4b4611 // indirect
golang.org/x/exp/typeparams v0.0.0-20230307190834-24139beb5833 // indirect
golang.org/x/mod v0.14.0 // indirect
golang.org/x/net v0.19.0 // indirect
Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1075,8 +1075,8 @@ github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAv
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
Expand Down Expand Up @@ -1423,8 +1423,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb h1:c0vyKkb6yr3KR7jEfJaOSv4lG7xPkbN6r52aJz1d8a8=
golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
golang.org/x/exp v0.0.0-20231214170342-aacd6d4b4611 h1:qCEDpW1G+vcj3Y7Fy52pEM1AWm3abj8WimGYejI3SC4=
golang.org/x/exp v0.0.0-20231214170342-aacd6d4b4611/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
golang.org/x/exp/typeparams v0.0.0-20230203172020-98cc5a0785f9/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
golang.org/x/exp/typeparams v0.0.0-20230307190834-24139beb5833 h1:jWGQJV4niP+CCmFW9ekjA9Zx8vYORzOUH2/Nl5WPuLQ=
Expand Down
38 changes: 34 additions & 4 deletions internal/releases/releases.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,43 @@
package releases

import (
"encoding/json"
"fmt"
)

func GetLatestProviderRelease(provider string) (map[string]interface{}, error) {
return makeGetRequest(fmt.Sprintf("https://registry.terraform.io/v1/providers/%s", provider))
type ProviderData struct {
Name string
Version string
}
type TerraformData struct {
Name string
Version string
}

func GetLatestProviderRelease(provider string) (*ProviderData, error) {
providerResponse, err := makeGetRequest(fmt.Sprintf("https://registry.terraform.io/v1/providers/%s", provider))
if err != nil {
return nil, err
}
var providerData ProviderData
err = json.Unmarshal(providerResponse, &providerData)
if err != nil {
return nil, err
}

return &providerData, nil

}

func GetLatestTerraformRelease() (map[string]interface{}, error) {
return makeGetRequest("https://api.releases.hashicorp.com/v1/releases/terraform/latest")
func GetLatestTerraformRelease() (*TerraformData, error) {
terraformResponse, err := makeGetRequest("https://api.releases.hashicorp.com/v1/releases/terraform/latest")
if err != nil {
return nil, err
}
var terraformData TerraformData
err = json.Unmarshal(terraformResponse, &terraformData)
if err != nil {
return nil, err
}
return &terraformData, nil
}
12 changes: 2 additions & 10 deletions internal/releases/util.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
package releases

import (
"encoding/json"
"io"
"net/http"
)

func makeGetRequest(apiURL string) (map[string]interface{}, error) {
func makeGetRequest(apiURL string) ([]byte, error) {
req, err := http.NewRequest(http.MethodGet, apiURL, nil)
if err != nil {
return nil, err
Expand All @@ -28,12 +27,5 @@ func makeGetRequest(apiURL string) (map[string]interface{}, error) {
return nil, err
}

var response map[string]interface{}

err = json.Unmarshal(body, &response)
if err != nil {
return nil, err
}

return response, nil
return body, nil
}
64 changes: 22 additions & 42 deletions internal/tfmodule/tfmodule.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"errors"
"fmt"
"os"
"regexp"
"strings"

"github.com/hashicorp/hcl/v2/hclwrite"
Expand All @@ -14,9 +15,9 @@ import (
"github.com/fitz7/tfnew/internal/releases"
)

const versionsFile = "versions.tf"
const VersionsFile = "versions.tf"

var defaultFilenames = []string{versionsFile, "variables.tf", "output.tf", "main.tf"}
var defaultFilenames = []string{VersionsFile, "variables.tf", "output.tf", "main.tf"}

type CreateModuleOptions struct {
Name string
Expand All @@ -26,34 +27,18 @@ type CreateModuleOptions struct {
BackendType string
}

func CreateModule(cmo CreateModuleOptions) error {
func CreateModuleDir(cmo CreateModuleOptions) (string, error) {
fullPathWithModuleName := fmt.Sprintf("%s/%s", fsutils.FindProjectRootDir(), cmo.Name)

err := os.Mkdir(fullPathWithModuleName, 0o755)
if err != nil {
return fmt.Errorf("error creating directory: %w", err)
return "", fmt.Errorf("error creating directory: %w", err)
}

defaultFiles, err := createDefaultModuleFiles(fullPathWithModuleName)
if err != nil {
return fmt.Errorf("error creating moduleName files: %w", err)
}

defer func() {
for _, file := range defaultFiles {
_ = file.Close()
}
}()

err = populateVersionsFile(defaultFiles[versionsFile], cmo)
if err != nil {
return fmt.Errorf("error populating the versions.tf file: %w", err)
}

return nil
return fullPathWithModuleName, nil
}

func createDefaultModuleFiles(path string) (map[string]*os.File, error) {
func CreateDefaultModuleFiles(path string) (map[string]*os.File, error) {
defaultFiles := make(map[string]*os.File)

for _, filename := range defaultFilenames {
Expand All @@ -68,7 +53,7 @@ func createDefaultModuleFiles(path string) (map[string]*os.File, error) {
return defaultFiles, nil
}

func populateVersionsFile(versionsFile *os.File, cmo CreateModuleOptions) error {
func PopulateVersionsFile(versionsFile *os.File, cmo CreateModuleOptions) error {
f := hclwrite.NewEmptyFile()
body := f.Body()

Expand Down Expand Up @@ -176,24 +161,17 @@ func addRequiredProvidersBlock(cmo CreateModuleOptions, body *hclwrite.Body) err
requiredProvidersBody := body.AppendNewBlock("required_providers", []string{}).Body()

for _, provider := range cmo.RequiredProviders {
latestProviderRelease, err := releases.GetLatestProviderRelease(provider)
latestProviderData, err := releases.GetLatestProviderRelease(provider)
if err != nil {
return err
}

providerName, ok := latestProviderRelease["name"].(string)
if !ok {
return fmt.Errorf("could not find name for provider: %s", provider)
}

latestProviderVersion, ok := latestProviderRelease["version"].(string)
if !ok {
return fmt.Errorf("could not find version for provider: %s", provider)
minorProviderVersion, err := truncatePatchVersion(latestProviderData.Version)
if err != nil {
return fmt.Errorf("%s provider version for: %s", latestProviderData.Name, err.Error())
}

minorProviderVersion := truncatePatchVersion(latestProviderVersion)

requiredProvidersBody.SetAttributeValue(providerName, cty.ObjectVal(map[string]cty.Value{
requiredProvidersBody.SetAttributeValue(latestProviderData.Name, cty.ObjectVal(map[string]cty.Value{
"source": cty.StringVal(provider),
"version": cty.StringVal(fmt.Sprintf("~> %s", minorProviderVersion)),
}))
Expand All @@ -214,19 +192,21 @@ func getTerraformVersion(rootModule bool) (string, error) {
return "", errors.New("failed to fetch terraform version")
}

latestTerraformVersion, ok := latestTerraformRelease["version"].(string)
if !ok {
return "", err
minorTerraformVersion, err := truncatePatchVersion(latestTerraformRelease.Version)
if err != nil {
return "", fmt.Errorf("terraform %s", err.Error())
}

minorTerraformVersion := truncatePatchVersion(latestTerraformVersion)

terraformVersion = fmt.Sprintf("~> %s", minorTerraformVersion)
}

return terraformVersion, nil
}

func truncatePatchVersion(version string) string {
return strings.Join(strings.Split(version, ".")[:2], ".")
func truncatePatchVersion(version string) (string, error) {
versionMatch := regexp.MustCompile(`^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$`)
if !versionMatch.MatchString(version) {
return "", errors.New("version returned is not valid semver")
}
return strings.Join(strings.Split(version, ".")[:2], "."), nil
}
60 changes: 60 additions & 0 deletions internal/tfmodule/tfmodule_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package tfmodule

import (
"testing"
)

func Test_truncatePatchVersion(t *testing.T) {
type args struct {
version string
}
tests := []struct {
name string
args args
want string
wantErr bool
}{
{
name: "regular version",
args: args{version: "1.2.3"},
want: "1.2",
wantErr: false,
},
{
name: "minor only version",
args: args{version: "1.2"},
want: "",
wantErr: true,
},
{
name: "major only version",
args: args{version: "1"},
want: "",
wantErr: true,
},
{
name: "regular big",
args: args{version: "12.34.56"},
want: "12.34",
wantErr: false,
},
{
name: "bad version",
args: args{version: "02.34.56"},
want: "",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := truncatePatchVersion(tt.args.version)
if (err != nil) != tt.wantErr {
t.Errorf("truncatePatchVersion() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("truncatePatchVersion() = %v, want %v", got, tt.want)
}
})
}
}

0 comments on commit 1d85eae

Please sign in to comment.