From 990488002fa62f1934240d31d9512776540c0224 Mon Sep 17 00:00:00 2001 From: LeCrabe Date: Wed, 17 Apr 2024 15:22:27 +0200 Subject: [PATCH] fixup! feat: setter can now takes maps --- pkg/helper/provider_block.go | 24 +-- pkg/helper/provider_block_test.go | 5 +- pkg/helper/ressource_block.go | 174 ++++++++---------- pkg/helper/ressource_block_test.go | 63 ++++--- pkg/resources/python/provider_test_block.tf | 3 - pkg/resources/python/resource_python_test.go | 41 ++++- .../python/resource_python_test_block.tf | 20 -- .../python/resource_python_test_block2.tf | 11 -- 8 files changed, 159 insertions(+), 182 deletions(-) delete mode 100644 pkg/resources/python/provider_test_block.tf delete mode 100644 pkg/resources/python/resource_python_test_block.tf delete mode 100644 pkg/resources/python/resource_python_test_block2.tf diff --git a/pkg/helper/provider_block.go b/pkg/helper/provider_block.go index 42eb5b9..e89c0a0 100644 --- a/pkg/helper/provider_block.go +++ b/pkg/helper/provider_block.go @@ -2,8 +2,8 @@ package helper // Provider structur type Provider struct { - Provider string - Organisation string + provider string + organisation string } // New function type that accepts pointer to Provider @@ -14,20 +14,15 @@ type ProviderOption func(*Provider) // - desc: Build a new Provider and apply specifics ProviderOption functions // - args: provider name, ProviderOption function // - return: pointer to Provider -func NewProvider(provider string, opts ...ProviderOption) *Provider { +func NewProvider(providerName string) *Provider { // default values const ( defaultOrganisation = "" ) p := &Provider{ - Provider: provider, - Organisation: defaultOrganisation, - } - - // ProviderOption functions - for _, opt := range opts { - opt(p) + provider: providerName, + organisation: defaultOrganisation, } return p @@ -38,7 +33,7 @@ func NewProvider(provider string, opts ...ProviderOption) *Provider { // - args: new organisation name // - return: pointer to Provider func (p *Provider) SetOrganisation(orgName string) *Provider { - p.Organisation = orgName + p.organisation = orgName return p } @@ -47,8 +42,9 @@ func (p *Provider) SetOrganisation(orgName string) *Provider { // - args: none // - return: string func (p *Provider) String() string { - s := `provider "` + p.Provider + `" { - organisation = "` + p.Organisation + `" -}` + s := `provider "` + p.provider + `" { + organisation = "` + p.organisation + `" +} +` return s } diff --git a/pkg/helper/provider_block_test.go b/pkg/helper/provider_block_test.go index fd49865..505099c 100644 --- a/pkg/helper/provider_block_test.go +++ b/pkg/helper/provider_block_test.go @@ -8,11 +8,10 @@ func TestProvider_String(t *testing.T) { fields *Provider want string }{ - // TODO: Add test cases. - {name: "test1", fields: NewProvider("clevercloud").SetOrganisation("clevercloud"), want: `provider "clevercloud" { organisation = "clevercloud" -}`}, +} +`}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/pkg/helper/ressource_block.go b/pkg/helper/ressource_block.go index 9cf0389..68b252e 100644 --- a/pkg/helper/ressource_block.go +++ b/pkg/helper/ressource_block.go @@ -1,15 +1,19 @@ package helper import ( + "reflect" "sort" "strconv" + "strings" + + "go.clever-cloud.com/terraform-provider/pkg" ) type Ressource struct { - Ressource string - Name string - StringValues map[string]string - IntValues map[string]int + ressourceType string + ressourceName string + keyValues map[string]any + blockValues map[string]any } // New function type that accepts pointer to Ressource @@ -18,31 +22,15 @@ type RessourceOption func(*Ressource) // Ressource constructor: // - desc: Build a new Ressource and apply specifics RessourceOption functions -// - args: Ressource name, RessourceOption function +// - args: Ressource type and ressource name, RessourceOption function // - return: pointer to Ressource -func NewRessource(ressource string, opts ...RessourceOption) *Ressource { - // default values - const ( - defaultName = "" - defaultRegion = "par" - dafaultMinInstances = 1 - defaultMaxInstances = 2 - defaultSmallestFlavor = "XS" - defaultBiggestFlavor = "M" - ) +func NewRessource(ressourceType, ressourceName string, opts ...RessourceOption) *Ressource { var r Ressource - r.Ressource = ressource - r.Name = defaultName - r.StringValues = map[string]string{ - "region": defaultRegion, - "smallest_flavor": defaultSmallestFlavor, - "biggest_flavor": defaultBiggestFlavor, - } - r.IntValues = map[string]int{ - "min_instance_count": dafaultMinInstances, - "max_instance_count": defaultMaxInstances, - } + r.ressourceType = ressourceType + r.ressourceName = ressourceName + r.keyValues = map[string]any{} + r.blockValues = map[string]any{} // RessourceOption functions for _, opt := range opts { @@ -52,98 +40,88 @@ func NewRessource(ressource string, opts ...RessourceOption) *Ressource { return &r } -// Name value setter: -// - desc: concatenate function that set Ressource.Name then return Ressource -// - args: new name -// - return: pointer to Ressource -func (r *Ressource) SetName(newName string) *Ressource { - r.Name = newName - return r -} - -// String value setter: -// - desc: set/add key: value to the string values map of a Ressource then return the Ressource -// - args: key + value -// - return: pointer to Ressource -func (p *Ressource) SetOneStringValue(key, value string) *Ressource { - p.StringValues[key] = value - return p -} - -// Integer value setter: -// - desc: set/add key: value to the int values map of a Ressource then return the Ressource +// unit keyValues setter: +// - desc: set/add only one key: value to keyvalues field of a Ressource then return the Ressource // - args: key + value // - return: pointer to Ressource -func (p *Ressource) SetOneIntValue(key string, value int) *Ressource { - p.IntValues[key] = value - return p +func (r *Ressource) SetOneValue(key string, value any) *Ressource { + r.keyValues[key] = value + return r } -// String values setter: -// - desc: set/add a map of key: value to the string values map of a Ressource then return the Ressource -// - args: map of key + value -// - return: pointer to Ressource -func (p *Ressource) SetStringValues(stringMap map[string]string) *Ressource { - for key, value := range stringMap { - p.StringValues[key] = value +// keyValues setter: +// - desc: set/add key: value to keyValues field of a Ressource then return the Ressource +// - args: map of string key + value +// - return: RessourceOption functions +func SetKeyValues(newMap map[string]any) RessourceOption { + return func(r *Ressource) { + for key, value := range newMap { + r.keyValues[key] = value + } } - return p } -// Integer values setter: -// - desc: set/add a map of key: value to the int values map of a Ressource then return the Ressource -// - args: map of key + value -// - return: pointer to Ressource -func (p *Ressource) SetIntValues(intMap map[string]int) *Ressource { - for key, value := range intMap { - p.IntValues[key] = value +// blockValues setter: +// - desc: set/add key: value to kblockValues field of a Ressource then return the Ressource +// - args: map of string key + value +// - return: RessourceOption functions +func SetBlockValues(blockName string, newMap map[string]any) RessourceOption { + return func(r *Ressource) { + r.blockValues[blockName] = newMap } - return p } // Ressource block // - desc: chained function that stringify Ressource into a terraform block // - args: none // - return: string -func (p *Ressource) String() string { - s := `ressource "` + p.Ressource + `" "` + p.Name + `" { - name = "` + p.Name + `" +func (r *Ressource) String() string { + s := `resource "` + r.ressourceType + `" "` + r.ressourceName + `" { ` - // check StringValues not empty - if len(p.StringValues) != 0 { - // sort StringValues keys - tmp := make([]string, 0, len(p.StringValues)) - for k := range p.StringValues { - tmp = append(tmp, k) - } - sort.Strings(tmp) - // create StringValues block - sstring := `` - for _, k := range tmp { - sstring += ` ` + k + ` = "` + p.StringValues[k] + `" + // create keyValues block + s = map_String(r.keyValues, s, ` `, ` =`) + // create blockValues block + s = map_String(r.blockValues, s, ` `, ``) + + // close s + s += `} ` - } - s += sstring + + return s +} + +func map_String(m map[string]any, s, tab, separator string) string { + // sort keyValues keys + valuesKeys := make([]string, 0, len(m)) + for k := range m { + valuesKeys = append(valuesKeys, k) } - // check IntValues not empty - if len(p.IntValues) != 0 { - // sort IntValues keys - tmp := make([]string, 0, len(p.IntValues)) - for k := range p.IntValues { - tmp = append(tmp, k) - } - sort.Strings(tmp) + sort.Strings(valuesKeys) - // create IntValues block - sint := `` - for _, k := range tmp { - sint += ` ` + k + ` = ` + strconv.Itoa(p.IntValues[k]) + ` + // create keyValues block + s = pkg.Reduce(valuesKeys, s, func(acc, key string) string { + switch c_type := m[key].(type) { + case string: + var_tmp := m[key].(string) + return acc + tab + key + ` = "` + strings.ReplaceAll(var_tmp, "\"", "\\\"") + `" +` + case int: + return acc + tab + key + ` = ` + strconv.Itoa(m[key].(int)) + ` +` + case bool: + return acc + tab + key + ` = ` + strconv.FormatBool(m[key].(bool)) + ` +` + case map[string]any: + acc := acc + tab + key + separator + ` { +` + return map_String(m[key].(map[string]any), acc, ` `, separator) + tab + `} +` + default: + return acc + `// Type ` + reflect.TypeOf(c_type).String() + ` of key "` + key + `" not considered yet ` } - s += sint - } - // close s - s += `}` + }) + return s } diff --git a/pkg/helper/ressource_block_test.go b/pkg/helper/ressource_block_test.go index 4368375..5e8ba8d 100644 --- a/pkg/helper/ressource_block_test.go +++ b/pkg/helper/ressource_block_test.go @@ -8,34 +8,47 @@ func TestRessource_String(t *testing.T) { fields *Ressource want string }{ - // TODO: Add test cases. - - {name: "test1", fields: NewRessource("clevercloud").SetName("test1"), want: `ressource "clevercloud" "test1" { - name = "test1" - biggest_flavor = "M" - region = "par" - smallest_flavor = "XS" - max_instance_count = 2 - min_instance_count = 1 -}`}, - {name: "test2", fields: NewRessource("clevercloud_python").SetName("test2").SetOneStringValue("biggest_flavor", "XXL").SetOneIntValue("min_instance_count", 3), want: `ressource "clevercloud_python" "test2" { - name = "test2" + {name: "test1", + fields: NewRessource("clevercloud", "test1"), + want: `resource "clevercloud" "test1" { +} +`}, + { + name: "test2", + fields: NewRessource("clevercloud_python", "test2"). + SetOneValue("biggest_flavor", "XXL"). + SetOneValue("test", 3), + want: `resource "clevercloud_python" "test2" { biggest_flavor = "XXL" - region = "par" - smallest_flavor = "XS" - max_instance_count = 2 - min_instance_count = 3 -}`}, - {name: "test3", fields: NewRessource("clevercloud_python").SetName("test3").SetStringValues(map[string]string{"region": "ici", "teststring": "smt"}).SetIntValues(map[string]int{"min_instance_count": 0, "testint": 12}), want: `ressource "clevercloud_python" "test3" { - name = "test3" - biggest_flavor = "M" - region = "ici" - smallest_flavor = "XS" - teststring = "smt" - max_instance_count = 2 + test = 3 +} +`}, + {name: "test3", + fields: NewRessource( + "clevercloud_python", + "test3", + SetKeyValues(map[string]any{ + "region": "ici", + "teststring": "smt", + "min_instance_count": 0, + "testint": 12, + "map": map[string]any{"test_string": "string", "test_int": 42}}), + SetBlockValues("testblock", map[string]any{"test_string": "string", "test_int": 42})), + want: `resource "clevercloud_python" "test3" { + map = { + test_int = 42 + test_string = "string" + } min_instance_count = 0 + region = "ici" testint = 12 -}`}, + teststring = "smt" + testblock { + test_int = 42 + test_string = "string" + } +} +`}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/pkg/resources/python/provider_test_block.tf b/pkg/resources/python/provider_test_block.tf deleted file mode 100644 index ad9d8c2..0000000 --- a/pkg/resources/python/provider_test_block.tf +++ /dev/null @@ -1,3 +0,0 @@ -provider "clevercloud" { - organisation = "%s" -} diff --git a/pkg/resources/python/resource_python_test.go b/pkg/resources/python/resource_python_test.go index b0d0dc3..01e0822 100644 --- a/pkg/resources/python/resource_python_test.go +++ b/pkg/resources/python/resource_python_test.go @@ -21,12 +21,6 @@ import ( "go.clever-cloud.dev/client" ) -//go:embed resource_python_test_block.tf -var pythonBlock string - -//go:embed resource_python_test_block2.tf -var pythonBlock2 string - var protoV6Provider = map[string]func() (tfprotov6.ProviderServer, error){ "clevercloud": providerserver.NewProtocol6WithError(impl.New("test")()), } @@ -66,7 +60,28 @@ func TestAccPython_basic(t *testing.T) { }, Steps: []resource.TestStep{{ ResourceName: rName, - Config: helper.NewProvider("clevercloud").SetOrganisation(org).String() + fmt.Sprintf(pythonBlock, rName, rName), + Config: helper.NewProvider("clevercloud"). + SetOrganisation(org).String() + helper.NewRessource( + "clevercloud_python", + rName, + helper.SetKeyValues(map[string]any{ + "name": rName, + "region": "par", + "min_instance_count": 1, + "max_instance_count": 2, + "smallest_flavor": "XS", + "biggest_flavor": "M", + "redirect_https": true, + "sticky_sessions": true, + "app_folder": "./app", + "python_version": "2.7", + "pip_requirements": "requirements.txt", + "environment": map[string]any{ + "MY_KEY": "myval", + }, + }), + helper.SetBlockValues("hooks", map[string]any{"post_build": "echo \"build is OK!\""}), + ).String(), Check: resource.ComposeAggregateTestCheckFunc( // Test the state for provider's populated values resource.TestMatchResourceAttr(fullName, "id", regexp.MustCompile(`^app_.*$`)), @@ -146,7 +161,17 @@ func TestAccPython_basic(t *testing.T) { ), }, { ResourceName: rName2, - Config: helper.NewProvider("clevercloud").SetOrganisation(org).String() + fmt.Sprintf(pythonBlock2, rName2, rName2), + Config: helper.NewProvider("clevercloud").SetOrganisation(org).String() + helper.NewRessource("clevercloud_python", + rName2, + helper.SetKeyValues(map[string]any{ + "name": "%s", + "region": "par", + "min_instance_count": 1, + "max_instance_count": 2, + "smallest_flavor": "XS", + "biggest_flavor": "M", + }), + helper.SetBlockValues("deployment", map[string]any{"repository": "https://github.com/CleverCloud/flask-example.git"})).String(), Check: func(state *terraform.State) error { id := state.RootModule().Resources[fullName2].Primary.ID diff --git a/pkg/resources/python/resource_python_test_block.tf b/pkg/resources/python/resource_python_test_block.tf deleted file mode 100644 index c61f2ad..0000000 --- a/pkg/resources/python/resource_python_test_block.tf +++ /dev/null @@ -1,20 +0,0 @@ -resource "clevercloud_python" "%s" { - name = "%s" - region = "par" - min_instance_count = 1 - max_instance_count = 2 - smallest_flavor = "XS" - biggest_flavor = "M" - redirect_https = true - sticky_sessions = true - app_folder = "./app" - python_version = "2.7" - pip_requirements = "requirements.txt" - environment = { - MY_KEY = "myval" - } - hooks { - post_build = "echo \"build is OK!\"" - } - dependencies = [] -} diff --git a/pkg/resources/python/resource_python_test_block2.tf b/pkg/resources/python/resource_python_test_block2.tf deleted file mode 100644 index 8569928..0000000 --- a/pkg/resources/python/resource_python_test_block2.tf +++ /dev/null @@ -1,11 +0,0 @@ -resource "clevercloud_python" "%s" { - name = "%s" - region = "par" - min_instance_count = 1 - max_instance_count = 2 - smallest_flavor = "XS" - biggest_flavor = "M" - deployment { - repository = "https://github.com/CleverCloud/flask-example.git" - } -}