From 095d520585942adc069e270c47f22267b293bc2f Mon Sep 17 00:00:00 2001 From: Jordan Caussat Date: Fri, 9 Mar 2018 16:22:25 +0100 Subject: [PATCH 01/21] TER-235: Add recipes for testing with no cached results --- GNUmakefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/GNUmakefile b/GNUmakefile index c6dc27e..71c8d6a 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -23,6 +23,9 @@ test: testacc: TF_ACC=1 go test $(TEST) -v $(TESTARGS) -timeout 120m +testacc-no-cache: + TF_ACC=1 GOCACHE=off go test $(TEST) -v $(TESTARGS) -timeout 120m + vet: @echo "go vet ." @go vet $$(go list ./... | grep -v vendor/) ; if [ $$? -eq 1 ]; then \ From b0970ca6709dc4e7baf247f6f23b8228f2403e06 Mon Sep 17 00:00:00 2001 From: Jordan Caussat Date: Fri, 9 Mar 2018 16:37:06 +0100 Subject: [PATCH 02/21] TER-235: Update app creation: add app and module expand methods --- ghost/resource_ghost_app.go | 98 ++++++++++++++++++++++++++++---- ghost/resource_ghost_app_test.go | 16 +++++- 2 files changed, 101 insertions(+), 13 deletions(-) diff --git a/ghost/resource_ghost_app.go b/ghost/resource_ghost_app.go index 0d6d97e..6a21573 100644 --- a/ghost/resource_ghost_app.go +++ b/ghost/resource_ghost_app.go @@ -1,6 +1,8 @@ package ghost import ( + "crypto/rand" + "fmt" "log" "cloud-deploy.io/go-st" @@ -46,7 +48,7 @@ func resourceGhostApp() *schema.Resource { }, "autoscale": { Type: schema.TypeList, - Required: true, + Optional: true, MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ @@ -328,7 +330,7 @@ func resourceGhostApp() *schema.Resource { }, "modules": { Type: schema.TypeList, - Optional: true, + Required: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "name": { @@ -357,10 +359,6 @@ func resourceGhostApp() *schema.Resource { Optional: true, Default: 0, }, - "initialized": { - Type: schema.TypeBool, - Optional: true, - }, "build_pack": { Type: schema.TypeString, Optional: true, @@ -425,14 +423,15 @@ func resourceGhostAppCreate(d *schema.ResourceData, meta interface{}) error { name := d.Get("name").(string) d.SetId(name) + log.Printf("[INFO] Creating Ghost app %s", d.Get("name").(string)) + app := expandGhostApp(d) - log.Printf("[INFO] Testing Ghost client get all apps") - apps, err := client.GetApps() + eveMetadata, err := client.CreateApp(app) if err == nil { - log.Println("All apps retrieved: ", apps) + log.Println("[INFO] App created: " + eveMetadata.ID) } else { - log.Printf("error: %v", err) + log.Fatalf("[ERROR] error: %v", err) } return nil @@ -458,3 +457,82 @@ func resourceGhostAppDelete(d *schema.ResourceData, meta interface{}) error { d.SetId("") return nil } + +// Get app from TF configuration +func expandGhostApp(d *schema.ResourceData) ghost.App { + modules := expandGhostAppModules(d) + + buildInfos := &ghost.BuildInfos{ + SourceAmi: "ami-123456", + SshUsername: "admin", + SubnetID: "subnet-123456", + } + + app := ghost.App{ + Name: d.Get("name").(string), + Env: d.Get("env").(string), + Role: d.Get("role").(string), + + Region: d.Get("region").(string), + InstanceType: d.Get("instance_type").(string), + VpcID: d.Get("vpc_id").(string), + + Modules: modules, + + BuildInfos: buildInfos, + } + // EnvironmentInfos: ghost.EnvironmentInfos{ + // InstanceProfile: environmentInfos["instance_profile"], + // KeyName: d.Get("key_name").(string), + // OptionalVolumes: []ghost.OptionalVolume{}, + // RootBlockDevice: ghost.RootBlockDevice{ + // Name: "/dev/xvda", + // Size: 20, + // }, + // SecurityGroups: []string{"sg-123456"}, + // SubnetIDs: []string{"subnet-123456"}, + // }, + + // LogNotifications: d.Get("log_notifications").([]string), + + app.Name = "APP_TEST-" + pseudo_uuid() + + return app +} + +func pseudo_uuid() (uuid string) { + b := make([]byte, 16) + _, err := rand.Read(b) + if err == nil { + uuid = fmt.Sprintf("%X-%X-%X-%X-%X", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]) + } + return +} + +// Get modules from TF configuration +func expandGhostAppModules(d *schema.ResourceData) *[]ghost.Module { + configs := d.Get("modules").([]interface{}) + modules := &[]ghost.Module{} + + // Add each module to modules list + for _, config := range configs { + data := config.(map[string]interface{}) + module := ghost.Module{ + Name: data["name"].(string), + GitRepo: data["git_repo"].(string), + Scope: data["scope"].(string), + Path: data["path"].(string), + BuildPack: data["build_pack"].(string), + PreDeploy: data["pre_deploy"].(string), + PostDeploy: data["post_deploy"].(string), + AfterAllDeploy: data["after_all_deploy"].(string), + LastDeployment: data["last_deployment"].(string), + GID: data["gid"].(int), + UID: data["uid"].(int), + } + + *modules = append(*modules, module) + } + + return modules +} diff --git a/ghost/resource_ghost_app_test.go b/ghost/resource_ghost_app_test.go index 258830e..795dd0f 100644 --- a/ghost/resource_ghost_app_test.go +++ b/ghost/resource_ghost_app_test.go @@ -78,7 +78,10 @@ func testAccGhostAppConfig(name string) string { environment_infos = { instance_profile = "iam.ec2.demo" key_name = "ghost-demo" - root_block_device = {} + root_block_device = { + Name = "test-block-device" + Size = 20 + } optional_volumes = [] subnet_ids = ["subnet-a7e849fe"] security_groups = ["sg-6814f60c"] @@ -89,12 +92,19 @@ func testAccGhostAppConfig(name string) string { } modules = [{ - name = "symfony2" + name = "symfony2" pre_deploy = "ZXhpdCAx" path = "/var/www" scope = "code" git_repo = "https://github.com/KnpLabs/KnpIpsum.git" - }] + }, + { + name = "patate" + pre_deploy = "ZXhpdCAx" + path = "/var/www" + scope = "code" + git_repo = "https://github.com/KnpLabs/KnpIpsum.git" + }] features = [{ version = "5.4" From affa26f23c26a2c5c7bb5cea027ce1107a44c3bf Mon Sep 17 00:00:00 2001 From: Jordan Caussat Date: Fri, 9 Mar 2018 17:24:44 +0100 Subject: [PATCH 03/21] TER-235: Update app creation: add build_infos expand methods --- ghost/resource_ghost_app.go | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/ghost/resource_ghost_app.go b/ghost/resource_ghost_app.go index 6a21573..882c43d 100644 --- a/ghost/resource_ghost_app.go +++ b/ghost/resource_ghost_app.go @@ -462,11 +462,7 @@ func resourceGhostAppDelete(d *schema.ResourceData, meta interface{}) error { func expandGhostApp(d *schema.ResourceData) ghost.App { modules := expandGhostAppModules(d) - buildInfos := &ghost.BuildInfos{ - SourceAmi: "ami-123456", - SshUsername: "admin", - SubnetID: "subnet-123456", - } + buildInfos := expandGhostAppBuildInfos(d) app := ghost.App{ Name: d.Get("name").(string), @@ -536,3 +532,18 @@ func expandGhostAppModules(d *schema.ResourceData) *[]ghost.Module { return modules } + +// Get build_infos from TF configuration +func expandGhostAppBuildInfos(d *schema.ResourceData) *ghost.BuildInfos { + config := d.Get("build_infos").([]interface{}) + data := config[0].(map[string]interface{}) + + buildInfos := &ghost.BuildInfos{ + SshUsername: data["ssh_username"].(string), + SourceAmi: data["source_ami"].(string), + AmiName: data["ami_name"].(string), + SubnetID: data["subnet_id"].(string), + } + + return buildInfos +} From d995862a7ac9dbb041ab0be21185738f6bd4f2a5 Mon Sep 17 00:00:00 2001 From: Jordan Caussat Date: Mon, 12 Mar 2018 10:39:37 +0100 Subject: [PATCH 04/21] TER-235: Update app creation: add environmentInfos expand method and methods needed by this one --- ghost/resource_ghost_app.go | 111 +++++++++++++++++++++++-------- ghost/resource_ghost_app_test.go | 20 ++++-- 2 files changed, 101 insertions(+), 30 deletions(-) diff --git a/ghost/resource_ghost_app.go b/ghost/resource_ghost_app.go index 882c43d..4d41c77 100644 --- a/ghost/resource_ghost_app.go +++ b/ghost/resource_ghost_app.go @@ -143,8 +143,9 @@ func resourceGhostApp() *schema.Resource { Default: true, }, "root_block_device": { - Type: schema.TypeMap, + Type: schema.TypeList, Optional: true, + MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "size": { @@ -160,13 +161,12 @@ func resourceGhostApp() *schema.Resource { }, }, "security_groups": { - Type: schema.TypeSet, + Type: schema.TypeList, Elem: &schema.Schema{Type: schema.TypeString}, Optional: true, - Set: schema.HashString, }, "instance_tags": { - Type: schema.TypeMap, + Type: schema.TypeList, Optional: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ @@ -460,35 +460,20 @@ func resourceGhostAppDelete(d *schema.ResourceData, meta interface{}) error { // Get app from TF configuration func expandGhostApp(d *schema.ResourceData) ghost.App { - modules := expandGhostAppModules(d) - - buildInfos := expandGhostAppBuildInfos(d) - app := ghost.App{ - Name: d.Get("name").(string), - Env: d.Get("env").(string), - Role: d.Get("role").(string), - + Name: d.Get("name").(string), + Env: d.Get("env").(string), + Role: d.Get("role").(string), Region: d.Get("region").(string), InstanceType: d.Get("instance_type").(string), VpcID: d.Get("vpc_id").(string), - Modules: modules, + Modules: expandGhostAppModules(d), + BuildInfos: expandGhostAppBuildInfos(d), + EnvironmentInfos: expandGhostAppEnvironmentInfos(d), - BuildInfos: buildInfos, + LogNotifications: expandGhostAppStringList(d.Get("log_notifications").([]interface{})), } - // EnvironmentInfos: ghost.EnvironmentInfos{ - // InstanceProfile: environmentInfos["instance_profile"], - // KeyName: d.Get("key_name").(string), - // OptionalVolumes: []ghost.OptionalVolume{}, - // RootBlockDevice: ghost.RootBlockDevice{ - // Name: "/dev/xvda", - // Size: 20, - // }, - // SecurityGroups: []string{"sg-123456"}, - // SubnetIDs: []string{"subnet-123456"}, - // }, - // LogNotifications: d.Get("log_notifications").([]string), app.Name = "APP_TEST-" + pseudo_uuid() @@ -547,3 +532,77 @@ func expandGhostAppBuildInfos(d *schema.ResourceData) *ghost.BuildInfos { return buildInfos } + +// Get environment_infos from TF configuration +func expandGhostAppEnvironmentInfos(d *schema.ResourceData) *ghost.EnvironmentInfos { + config := d.Get("environment_infos").([]interface{}) + data := config[0].(map[string]interface{}) + + environmentInfos := &ghost.EnvironmentInfos{ + InstanceProfile: data["instance_profile"].(string), + KeyName: data["key_name"].(string), + PublicIpAddress: data["public_ip_address"].(bool), + SecurityGroups: expandGhostAppStringList(data["security_groups"].([]interface{})), + SubnetIDs: expandGhostAppStringList(data["subnet_ids"].([]interface{})), + InstanceTags: expandGhostAppInstanceTags(data["instance_tags"].([]interface{})), + OptionalVolumes: expandGhostAppOptionalVolumes(data["optional_volumes"].([]interface{})), + RootBlockDevice: expandGhostAppRootBlockDevice(data["root_block_device"].([]interface{})), + } + + return environmentInfos +} + +func expandGhostAppRootBlockDevice(d []interface{}) *ghost.RootBlockDevice { + data := d[0].(map[string]interface{}) + rootBlockDevice := &ghost.RootBlockDevice{ + Name: data["name"].(string), + Size: data["size"].(int), + } + + return rootBlockDevice +} + +func expandGhostAppOptionalVolumes(d []interface{}) *[]ghost.OptionalVolume { + optionalVolumes := &[]ghost.OptionalVolume{} + + for _, config := range d { + data := config.(map[string]interface{}) + optionalVolume := ghost.OptionalVolume{ + DeviceName: data["device_name"].(string), + VolumeType: data["volume_type"].(string), + VolumeSize: data["volume_size"].(int), + Iops: data["iops"].(int), + LaunchBlockDeviceMappings: data["launch_block_device_mappings"].(bool), + } + + *optionalVolumes = append(*optionalVolumes, optionalVolume) + } + + return optionalVolumes +} + +func expandGhostAppInstanceTags(d []interface{}) *[]ghost.InstanceTag { + instanceTags := &[]ghost.InstanceTag{} + + for _, config := range d { + data := config.(map[string]interface{}) + instanceTag := ghost.InstanceTag{ + TagName: data["tag_name"].(string), + TagValue: data["tag_value"].(string), + } + + *instanceTags = append(*instanceTags, instanceTag) + } + + return instanceTags +} + +func expandGhostAppStringList(d []interface{}) []string { + var stringList []string + + for _, str := range d { + stringList = append(stringList, str.(string)) + } + + return stringList +} diff --git a/ghost/resource_ghost_app_test.go b/ghost/resource_ghost_app_test.go index 795dd0f..884ce8a 100644 --- a/ghost/resource_ghost_app_test.go +++ b/ghost/resource_ghost_app_test.go @@ -79,12 +79,24 @@ func testAccGhostAppConfig(name string) string { instance_profile = "iam.ec2.demo" key_name = "ghost-demo" root_block_device = { - Name = "test-block-device" - Size = 20 + name = "testblockdevice" + size = 20 } - optional_volumes = [] + optional_volumes = [{ + device_name = "/dev/xvdd" + volume_type = "gp2" + volume_size = 20 + }] subnet_ids = ["subnet-a7e849fe"] - security_groups = ["sg-6814f60c"] + security_groups = ["sg-6814f60c", "sg-2414f60c"] + instance_tags = [{ + tag_name = "Name" + tag_value = "wordpress" + }, + { + tag_name = "Type" + tag_value = "front" + }] } autoscale = { From 10164bcc187f4f1406e1825e3c0b48d5d5e635da Mon Sep 17 00:00:00 2001 From: Jordan Caussat Date: Mon, 12 Mar 2018 11:13:52 +0100 Subject: [PATCH 05/21] TER-235: Move name generation for testing to the test file --- ghost/resource_ghost_app.go | 37 ++++++++++++++----------------------- 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/ghost/resource_ghost_app.go b/ghost/resource_ghost_app.go index 4d41c77..43ceccf 100644 --- a/ghost/resource_ghost_app.go +++ b/ghost/resource_ghost_app.go @@ -1,8 +1,6 @@ package ghost import ( - "crypto/rand" - "fmt" "log" "cloud-deploy.io/go-st" @@ -461,35 +459,28 @@ func resourceGhostAppDelete(d *schema.ResourceData, meta interface{}) error { // Get app from TF configuration func expandGhostApp(d *schema.ResourceData) ghost.App { app := ghost.App{ - Name: d.Get("name").(string), - Env: d.Get("env").(string), - Role: d.Get("role").(string), - Region: d.Get("region").(string), - InstanceType: d.Get("instance_type").(string), - VpcID: d.Get("vpc_id").(string), - - Modules: expandGhostAppModules(d), + Name: d.Get("name").(string), + Env: d.Get("env").(string), + Role: d.Get("role").(string), + Region: d.Get("region").(string), + InstanceType: d.Get("instance_type").(string), + VpcID: d.Get("vpc_id").(string), + InstanceMonitoring: d.Get("instance_monitoring").(bool), + + Modules: expandGhostAppModules(d), + // Features: + // Autoscale: BuildInfos: expandGhostAppBuildInfos(d), EnvironmentInfos: expandGhostAppEnvironmentInfos(d), - + // LifecycleHooks: + // SafeDeployment: + // EnvironmentVariables: LogNotifications: expandGhostAppStringList(d.Get("log_notifications").([]interface{})), } - // LogNotifications: d.Get("log_notifications").([]string), - - app.Name = "APP_TEST-" + pseudo_uuid() return app } -func pseudo_uuid() (uuid string) { - b := make([]byte, 16) - _, err := rand.Read(b) - if err == nil { - uuid = fmt.Sprintf("%X-%X-%X-%X-%X", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]) - } - return -} - // Get modules from TF configuration func expandGhostAppModules(d *schema.ResourceData) *[]ghost.Module { configs := d.Get("modules").([]interface{}) From 9978437a6e5a9c0a05c37ada690c0d815800bda0 Mon Sep 17 00:00:00 2001 From: Jordan Caussat Date: Mon, 12 Mar 2018 11:20:48 +0100 Subject: [PATCH 06/21] TER-235: Update app creation: add features expand method --- ghost/resource_ghost_app.go | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/ghost/resource_ghost_app.go b/ghost/resource_ghost_app.go index 43ceccf..43feaca 100644 --- a/ghost/resource_ghost_app.go +++ b/ghost/resource_ghost_app.go @@ -290,10 +290,6 @@ func resourceGhostApp() *schema.Resource { Type: schema.TypeString, Optional: true, }, - "parameters": { - Type: schema.TypeString, - Optional: true, - }, "provisioner": { Type: schema.TypeString, Optional: true, @@ -467,8 +463,8 @@ func expandGhostApp(d *schema.ResourceData) ghost.App { VpcID: d.Get("vpc_id").(string), InstanceMonitoring: d.Get("instance_monitoring").(bool), - Modules: expandGhostAppModules(d), - // Features: + Modules: expandGhostAppModules(d), + Features: expandGhostAppFeatures(d), // Autoscale: BuildInfos: expandGhostAppBuildInfos(d), EnvironmentInfos: expandGhostAppEnvironmentInfos(d), @@ -481,6 +477,26 @@ func expandGhostApp(d *schema.ResourceData) ghost.App { return app } +// Get features from TF configuration +func expandGhostAppFeatures(d *schema.ResourceData) *[]ghost.Feature { + configs := d.Get("features").([]interface{}) + features := &[]ghost.Feature{} + + // Add each module to modules list + for _, config := range configs { + data := config.(map[string]interface{}) + feature := ghost.Feature{ + Name: data["name"].(string), + Version: data["version"].(string), + Provisioner: data["provisioner"].(string), + } + + *features = append(*features, feature) + } + + return features +} + // Get modules from TF configuration func expandGhostAppModules(d *schema.ResourceData) *[]ghost.Module { configs := d.Get("modules").([]interface{}) From 7bad7827f53e0cb77e3f7a08654c1a227a263b87 Mon Sep 17 00:00:00 2001 From: Jordan Caussat Date: Tue, 13 Mar 2018 10:03:37 +0100 Subject: [PATCH 07/21] TER-235: Update app creation: add lifecycle hooks expand method and convert all scripts to b64 --- ghost/helpers.go | 9 +++++ ghost/resource_ghost_app.go | 67 ++++++++++++++++++++------------ ghost/resource_ghost_app_test.go | 20 ++++++---- 3 files changed, 64 insertions(+), 32 deletions(-) create mode 100644 ghost/helpers.go diff --git a/ghost/helpers.go b/ghost/helpers.go new file mode 100644 index 0000000..5c32286 --- /dev/null +++ b/ghost/helpers.go @@ -0,0 +1,9 @@ +package ghost + +import ( + "encoding/base64" +) + +func StrToB64(data string) string { + return base64.StdEncoding.EncodeToString([]byte(data)) +} diff --git a/ghost/resource_ghost_app.go b/ghost/resource_ghost_app.go index 43feaca..16f03c7 100644 --- a/ghost/resource_ghost_app.go +++ b/ghost/resource_ghost_app.go @@ -468,7 +468,7 @@ func expandGhostApp(d *schema.ResourceData) ghost.App { // Autoscale: BuildInfos: expandGhostAppBuildInfos(d), EnvironmentInfos: expandGhostAppEnvironmentInfos(d), - // LifecycleHooks: + LifecycleHooks: expandGhostAppLifecycleHooks(d), // SafeDeployment: // EnvironmentVariables: LogNotifications: expandGhostAppStringList(d.Get("log_notifications").([]interface{})), @@ -477,26 +477,6 @@ func expandGhostApp(d *schema.ResourceData) ghost.App { return app } -// Get features from TF configuration -func expandGhostAppFeatures(d *schema.ResourceData) *[]ghost.Feature { - configs := d.Get("features").([]interface{}) - features := &[]ghost.Feature{} - - // Add each module to modules list - for _, config := range configs { - data := config.(map[string]interface{}) - feature := ghost.Feature{ - Name: data["name"].(string), - Version: data["version"].(string), - Provisioner: data["provisioner"].(string), - } - - *features = append(*features, feature) - } - - return features -} - // Get modules from TF configuration func expandGhostAppModules(d *schema.ResourceData) *[]ghost.Module { configs := d.Get("modules").([]interface{}) @@ -510,10 +490,10 @@ func expandGhostAppModules(d *schema.ResourceData) *[]ghost.Module { GitRepo: data["git_repo"].(string), Scope: data["scope"].(string), Path: data["path"].(string), - BuildPack: data["build_pack"].(string), - PreDeploy: data["pre_deploy"].(string), - PostDeploy: data["post_deploy"].(string), - AfterAllDeploy: data["after_all_deploy"].(string), + BuildPack: StrToB64(data["build_pack"].(string)), + PreDeploy: StrToB64(data["pre_deploy"].(string)), + PostDeploy: StrToB64(data["post_deploy"].(string)), + AfterAllDeploy: StrToB64(data["after_all_deploy"].(string)), LastDeployment: data["last_deployment"].(string), GID: data["gid"].(int), UID: data["uid"].(int), @@ -525,6 +505,43 @@ func expandGhostAppModules(d *schema.ResourceData) *[]ghost.Module { return modules } +// Get lifecycle_hooks from TF configuration +func expandGhostAppLifecycleHooks(d *schema.ResourceData) *ghost.LifecycleHooks { + config := d.Get("lifecycle_hooks").([]interface{}) + if len(config) == 0 { + return nil + } + data := config[0].(map[string]interface{}) + + lifecycleHooks := &ghost.LifecycleHooks{ + PreBuildimage: StrToB64(data["pre_buildimage"].(string)), + PostBuildimage: StrToB64(data["post_buildimage"].(string)), + PreBootstrap: StrToB64(data["pre_bootstrap"].(string)), + PostBootstrap: StrToB64(data["post_boostrap"].(string)), + } + + return lifecycleHooks +} + +// Get features from TF configuration +func expandGhostAppFeatures(d *schema.ResourceData) *[]ghost.Feature { + configs := d.Get("features").([]interface{}) + features := &[]ghost.Feature{} + + for _, config := range configs { + data := config.(map[string]interface{}) + feature := ghost.Feature{ + Name: data["name"].(string), + Version: data["version"].(string), + Provisioner: data["provisioner"].(string), + } + + *features = append(*features, feature) + } + + return features +} + // Get build_infos from TF configuration func expandGhostAppBuildInfos(d *schema.ResourceData) *ghost.BuildInfos { config := d.Get("build_infos").([]interface{}) diff --git a/ghost/resource_ghost_app_test.go b/ghost/resource_ghost_app_test.go index 884ce8a..7f638aa 100644 --- a/ghost/resource_ghost_app_test.go +++ b/ghost/resource_ghost_app_test.go @@ -104,18 +104,19 @@ func testAccGhostAppConfig(name string) string { } modules = [{ - name = "symfony2" - pre_deploy = "ZXhpdCAx" + name = "wordpress" + pre_deploy = "" path = "/var/www" scope = "code" git_repo = "https://github.com/KnpLabs/KnpIpsum.git" }, { - name = "patate" - pre_deploy = "ZXhpdCAx" - path = "/var/www" - scope = "code" - git_repo = "https://github.com/KnpLabs/KnpIpsum.git" + name = "wordpress2" + pre_deploy = "ZXhpdCAx" + post_deploy = "ZXhpdCAx" + path = "/var/www" + scope = "code" + git_repo = "https://github.com/KnpLabs/KnpIpsum.git" }] features = [{ @@ -126,6 +127,11 @@ func testAccGhostAppConfig(name string) string { version = "2.2" name = "apache2" }] + + lifecycle_hooks = { + pre_buildimage = "#!/usr/bin/env bash" + post_buildimage = "#!/usr/bin/env bash" + } } `, name) } From 05c27f1d65268dd9f45f221dcf76f614a5c5a9c5 Mon Sep 17 00:00:00 2001 From: Jordan Caussat Date: Tue, 13 Mar 2018 11:58:55 +0100 Subject: [PATCH 08/21] TER-235: Update app creation: add autoscale expand method and test --- ghost/resource_ghost_app.go | 24 +++++++++++++++++++++--- ghost/resource_ghost_app_test.go | 4 +++- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/ghost/resource_ghost_app.go b/ghost/resource_ghost_app.go index 16f03c7..d11e74e 100644 --- a/ghost/resource_ghost_app.go +++ b/ghost/resource_ghost_app.go @@ -463,9 +463,9 @@ func expandGhostApp(d *schema.ResourceData) ghost.App { VpcID: d.Get("vpc_id").(string), InstanceMonitoring: d.Get("instance_monitoring").(bool), - Modules: expandGhostAppModules(d), - Features: expandGhostAppFeatures(d), - // Autoscale: + Modules: expandGhostAppModules(d), + Features: expandGhostAppFeatures(d), + Autoscale: expandGhostAppAutoscale(d), BuildInfos: expandGhostAppBuildInfos(d), EnvironmentInfos: expandGhostAppEnvironmentInfos(d), LifecycleHooks: expandGhostAppLifecycleHooks(d), @@ -505,6 +505,24 @@ func expandGhostAppModules(d *schema.ResourceData) *[]ghost.Module { return modules } +// Get autoscale from TF configuration +func expandGhostAppAutoscale(d *schema.ResourceData) *ghost.Autoscale { + config := d.Get("autoscale").([]interface{}) + if len(config) == 0 { + return nil + } + data := config[0].(map[string]interface{}) + + autoscale := &ghost.Autoscale{ + Name: data["name"].(string), + EnableMetrics: data["enable_metrics"].(bool), + Min: data["min"].(int), + Max: data["max"].(int), + } + + return autoscale +} + // Get lifecycle_hooks from TF configuration func expandGhostAppLifecycleHooks(d *schema.ResourceData) *ghost.LifecycleHooks { config := d.Get("lifecycle_hooks").([]interface{}) diff --git a/ghost/resource_ghost_app_test.go b/ghost/resource_ghost_app_test.go index 7f638aa..7bc64b7 100644 --- a/ghost/resource_ghost_app_test.go +++ b/ghost/resource_ghost_app_test.go @@ -100,7 +100,9 @@ func testAccGhostAppConfig(name string) string { } autoscale = { - name = "" + name = "autoscale" + min = 1 + max = 3 } modules = [{ From 2cc12126559afc36f4c290c4513d833512da7835 Mon Sep 17 00:00:00 2001 From: Jordan Caussat Date: Tue, 13 Mar 2018 12:11:11 +0100 Subject: [PATCH 09/21] TER-235: Update app creation: add environment variables expand method and tests --- ghost/resource_ghost_app.go | 36 ++++++++++++++++++++++++-------- ghost/resource_ghost_app_test.go | 11 +++++++--- 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/ghost/resource_ghost_app.go b/ghost/resource_ghost_app.go index d11e74e..f4d48b7 100644 --- a/ghost/resource_ghost_app.go +++ b/ghost/resource_ghost_app.go @@ -379,6 +379,7 @@ func resourceGhostApp() *schema.Resource { "safe_deployment": { Type: schema.TypeList, Optional: true, + MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "ha_backend": { @@ -463,15 +464,14 @@ func expandGhostApp(d *schema.ResourceData) ghost.App { VpcID: d.Get("vpc_id").(string), InstanceMonitoring: d.Get("instance_monitoring").(bool), - Modules: expandGhostAppModules(d), - Features: expandGhostAppFeatures(d), - Autoscale: expandGhostAppAutoscale(d), - BuildInfos: expandGhostAppBuildInfos(d), - EnvironmentInfos: expandGhostAppEnvironmentInfos(d), - LifecycleHooks: expandGhostAppLifecycleHooks(d), - // SafeDeployment: - // EnvironmentVariables: - LogNotifications: expandGhostAppStringList(d.Get("log_notifications").([]interface{})), + Modules: expandGhostAppModules(d), + Features: expandGhostAppFeatures(d), + Autoscale: expandGhostAppAutoscale(d), + BuildInfos: expandGhostAppBuildInfos(d), + EnvironmentInfos: expandGhostAppEnvironmentInfos(d), + LifecycleHooks: expandGhostAppLifecycleHooks(d), + LogNotifications: expandGhostAppStringList(d.Get("log_notifications").([]interface{})), + EnvironmentVariables: expandGhostAppEnvironmentVariables(d), } return app @@ -505,6 +505,24 @@ func expandGhostAppModules(d *schema.ResourceData) *[]ghost.Module { return modules } +// Get environment variables from TF configuration +func expandGhostAppEnvironmentVariables(d *schema.ResourceData) *[]ghost.EnvironmentVariable { + configs := d.Get("environment_variables").([]interface{}) + environmentVariables := &[]ghost.EnvironmentVariable{} + + for _, config := range configs { + data := config.(map[string]interface{}) + environmentVariable := ghost.EnvironmentVariable{ + Key: data["key"].(string), + Value: data["value"].(string), + } + + *environmentVariables = append(*environmentVariables, environmentVariable) + } + + return environmentVariables +} + // Get autoscale from TF configuration func expandGhostAppAutoscale(d *schema.ResourceData) *ghost.Autoscale { config := d.Get("autoscale").([]interface{}) diff --git a/ghost/resource_ghost_app_test.go b/ghost/resource_ghost_app_test.go index 7bc64b7..2022ace 100644 --- a/ghost/resource_ghost_app_test.go +++ b/ghost/resource_ghost_app_test.go @@ -101,8 +101,8 @@ func testAccGhostAppConfig(name string) string { autoscale = { name = "autoscale" - min = 1 - max = 3 + min = 1 + max = 3 } modules = [{ @@ -131,9 +131,14 @@ func testAccGhostAppConfig(name string) string { }] lifecycle_hooks = { - pre_buildimage = "#!/usr/bin/env bash" + pre_buildimage = "#!/usr/bin/env bash" post_buildimage = "#!/usr/bin/env bash" } + + environment_variables = [{ + key = "myvar" + value = "myvalue" + }] } `, name) } From 45d6f7860741f392780c29d91811b3f30ff501f6 Mon Sep 17 00:00:00 2001 From: Jordan Caussat Date: Tue, 13 Mar 2018 15:30:51 +0100 Subject: [PATCH 10/21] TER-235: Fix typo bootstrap --- ghost/resource_ghost_app.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ghost/resource_ghost_app.go b/ghost/resource_ghost_app.go index f4d48b7..39f2032 100644 --- a/ghost/resource_ghost_app.go +++ b/ghost/resource_ghost_app.go @@ -553,7 +553,7 @@ func expandGhostAppLifecycleHooks(d *schema.ResourceData) *ghost.LifecycleHooks PreBuildimage: StrToB64(data["pre_buildimage"].(string)), PostBuildimage: StrToB64(data["post_buildimage"].(string)), PreBootstrap: StrToB64(data["pre_bootstrap"].(string)), - PostBootstrap: StrToB64(data["post_boostrap"].(string)), + PostBootstrap: StrToB64(data["post_bootstrap"].(string)), } return lifecycleHooks From a0973bedeb6e8f7125b34d60d7fd738e86795bd2 Mon Sep 17 00:00:00 2001 From: Jordan Caussat Date: Tue, 13 Mar 2018 15:52:24 +0100 Subject: [PATCH 11/21] TER-235: Update readme and examples with new tf config --- README.md | 49 +++++++++++++++++++++++++++++++++++++++++------- examples/main.tf | 49 +++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 84 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index bb6d492..c253b49 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ provider "ghost" { } resource "ghost_app" "test" { - name = "wordpress" + name = "%s" env = "dev" role = "webfront" @@ -86,22 +86,47 @@ resource "ghost_app" "test" { environment_infos = { instance_profile = "iam.ec2.demo" key_name = "ghost-demo" - root_block_device = {} - optional_volumes = [] + root_block_device = { + name = "testblockdevice" + size = 20 + } + optional_volumes = [{ + device_name = "/dev/xvdd" + volume_type = "gp2" + volume_size = 20 + }] subnet_ids = ["subnet-a7e849fe"] - security_groups = ["sg-6814f60c"] + security_groups = ["sg-6814f60c", "sg-2414f60c"] + instance_tags = [{ + tag_name = "Name" + tag_value = "wordpress" + }, + { + tag_name = "Type" + tag_value = "front" + }] } autoscale = { - name = "" + name = "autoscale" + min = 1 + max = 3 } modules = [{ - name = "symfony2" - pre_deploy = "ZXhpdCAx" + name = "wordpress" + pre_deploy = "" path = "/var/www" scope = "code" git_repo = "https://github.com/KnpLabs/KnpIpsum.git" + }, + { + name = "wordpress2" + pre_deploy = "ZXhpdCAx" + post_deploy = "ZXhpdCAx" + path = "/var/www" + scope = "code" + git_repo = "https://github.com/KnpLabs/KnpIpsum.git" }] features = [{ @@ -112,5 +137,15 @@ resource "ghost_app" "test" { version = "2.2" name = "apache2" }] + + lifecycle_hooks = { + pre_buildimage = "#!/usr/bin/env bash" + post_buildimage = "#!/usr/bin/env bash" + } + + environment_variables = [{ + key = "myvar" + value = "myvalue" + }] } ``` diff --git a/examples/main.tf b/examples/main.tf index 5e265eb..056759d 100644 --- a/examples/main.tf +++ b/examples/main.tf @@ -5,7 +5,7 @@ provider "ghost" { } resource "ghost_app" "test" { - name = "wordpress" + name = "%s" env = "dev" role = "webfront" @@ -26,22 +26,47 @@ resource "ghost_app" "test" { environment_infos = { instance_profile = "iam.ec2.demo" key_name = "ghost-demo" - root_block_device = {} - optional_volumes = [] + root_block_device = { + name = "testblockdevice" + size = 20 + } + optional_volumes = [{ + device_name = "/dev/xvdd" + volume_type = "gp2" + volume_size = 20 + }] subnet_ids = ["subnet-a7e849fe"] - security_groups = ["sg-6814f60c"] + security_groups = ["sg-6814f60c", "sg-2414f60c"] + instance_tags = [{ + tag_name = "Name" + tag_value = "wordpress" + }, + { + tag_name = "Type" + tag_value = "front" + }] } autoscale = { - name = "" + name = "autoscale" + min = 1 + max = 3 } modules = [{ - name = "symfony2" - pre_deploy = "ZXhpdCAx" + name = "wordpress" + pre_deploy = "" path = "/var/www" scope = "code" git_repo = "https://github.com/KnpLabs/KnpIpsum.git" + }, + { + name = "wordpress2" + pre_deploy = "ZXhpdCAx" + post_deploy = "ZXhpdCAx" + path = "/var/www" + scope = "code" + git_repo = "https://github.com/KnpLabs/KnpIpsum.git" }] features = [{ @@ -52,4 +77,14 @@ resource "ghost_app" "test" { version = "2.2" name = "apache2" }] + + lifecycle_hooks = { + pre_buildimage = "#!/usr/bin/env bash" + post_buildimage = "#!/usr/bin/env bash" + } + + environment_variables = [{ + key = "myvar" + value = "myvalue" + }] } From adf846ee7e25bd99f353b5d972ddd427726a4b97 Mon Sep 17 00:00:00 2001 From: Jordan Caussat Date: Tue, 13 Mar 2018 17:08:45 +0100 Subject: [PATCH 12/21] TER-235: Use eveMetadta.ID as ID instead of the name --- ghost/resource_ghost_app.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ghost/resource_ghost_app.go b/ghost/resource_ghost_app.go index 39f2032..dc753af 100644 --- a/ghost/resource_ghost_app.go +++ b/ghost/resource_ghost_app.go @@ -416,9 +416,6 @@ func resourceGhostApp() *schema.Resource { func resourceGhostAppCreate(d *schema.ResourceData, meta interface{}) error { client := meta.(*ghost.Client) - name := d.Get("name").(string) - d.SetId(name) - log.Printf("[INFO] Creating Ghost app %s", d.Get("name").(string)) app := expandGhostApp(d) @@ -429,6 +426,8 @@ func resourceGhostAppCreate(d *schema.ResourceData, meta interface{}) error { log.Fatalf("[ERROR] error: %v", err) } + d.SetId(eveMetadata.ID) + return nil } From 7d86dde03c06cf9ea4c548abca519823673579b0 Mon Sep 17 00:00:00 2001 From: Jordan Caussat Date: Fri, 16 Mar 2018 12:58:39 +0100 Subject: [PATCH 13/21] TER-235: Add terraform data validation --- ghost/helpers.go | 18 ++ ghost/resource_ghost_app.go | 162 ++++++++----- ghost/resource_ghost_app_test.go | 189 ++++++++++++++- .../terraform/helper/structure/expand_json.go | 11 + .../helper/structure/flatten_json.go | 16 ++ .../helper/structure/normalize_json.go | 24 ++ .../helper/structure/suppress_json_diff.go | 21 ++ .../terraform/helper/validation/validation.go | 222 ++++++++++++++++++ vendor/vendor.json | 6 + 9 files changed, 605 insertions(+), 64 deletions(-) create mode 100644 vendor/github.com/hashicorp/terraform/helper/structure/expand_json.go create mode 100644 vendor/github.com/hashicorp/terraform/helper/structure/flatten_json.go create mode 100644 vendor/github.com/hashicorp/terraform/helper/structure/normalize_json.go create mode 100644 vendor/github.com/hashicorp/terraform/helper/structure/suppress_json_diff.go create mode 100644 vendor/github.com/hashicorp/terraform/helper/validation/validation.go diff --git a/ghost/helpers.go b/ghost/helpers.go index 5c32286..ada0aab 100644 --- a/ghost/helpers.go +++ b/ghost/helpers.go @@ -2,8 +2,26 @@ package ghost import ( "encoding/base64" + "fmt" + "regexp" ) func StrToB64(data string) string { return base64.StdEncoding.EncodeToString([]byte(data)) } + +func B64ToStr(data string) string { + str, _ := base64.StdEncoding.DecodeString(data) + + return string(str) +} + +func MatchesRegexp(exp string) func(v interface{}, k string) (ws []string, errors []error) { + return func(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if !regexp.MustCompile(exp).MatchString(value) { + errors = append(errors, fmt.Errorf("%q must match %s", k, exp)) + } + return + } +} diff --git a/ghost/resource_ghost_app.go b/ghost/resource_ghost_app.go index dc753af..974def6 100644 --- a/ghost/resource_ghost_app.go +++ b/ghost/resource_ghost_app.go @@ -5,6 +5,7 @@ import ( "cloud-deploy.io/go-st" "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" ) func resourceGhostApp() *schema.Resource { @@ -16,24 +17,28 @@ func resourceGhostApp() *schema.Resource { Schema: map[string]*schema.Schema{ "name": { - Type: schema.TypeString, - Required: true, + Type: schema.TypeString, + Required: true, + ValidateFunc: MatchesRegexp(`^[a-zA-Z0-9_.+-]*$`), }, "env": { - Type: schema.TypeString, - Required: true, + Type: schema.TypeString, + Required: true, + ValidateFunc: MatchesRegexp(`^[a-z0-9\-\_]*$`), }, "role": { - Type: schema.TypeString, - Required: true, + Type: schema.TypeString, + Required: true, + ValidateFunc: MatchesRegexp(`^[a-z0-9\-\_]*$`), }, "region": { Type: schema.TypeString, Optional: true, }, "vpc_id": { - Type: schema.TypeString, - Required: true, + Type: schema.TypeString, + Required: true, + ValidateFunc: MatchesRegexp(`^vpc-[a-z0-9]*$`), }, "instance_type": { Type: schema.TypeString, @@ -60,12 +65,14 @@ func resourceGhostApp() *schema.Resource { Default: false, }, "min": { - Type: schema.TypeInt, - Optional: true, + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntAtLeast(0), }, "max": { - Type: schema.TypeInt, - Optional: true, + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntAtLeast(0), }, }, }, @@ -102,21 +109,24 @@ func resourceGhostApp() *schema.Resource { Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "ssh_username": { - Type: schema.TypeString, - Optional: true, - Default: "admin", + Type: schema.TypeString, + Optional: true, + Default: "admin", + ValidateFunc: MatchesRegexp(`^[a-z\_][a-z0-9\_\-]{0,30}$`), }, "source_ami": { - Type: schema.TypeString, - Required: true, + Type: schema.TypeString, + Required: true, + ValidateFunc: MatchesRegexp(`^ami-[a-z0-9]*$`), }, "ami_name": { Type: schema.TypeString, Optional: true, }, "subnet_id": { - Type: schema.TypeString, - Required: true, + Type: schema.TypeString, + Required: true, + ValidateFunc: MatchesRegexp(`^subnet-[a-z0-9]*$`), }, }, }, @@ -128,12 +138,14 @@ func resourceGhostApp() *schema.Resource { Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "instance_profile": { - Type: schema.TypeString, - Optional: true, + Type: schema.TypeString, + Optional: true, + ValidateFunc: MatchesRegexp(`^[a-zA-Z0-9\\+\\=\\,\\.\\@\\-\\_]{1,128}$`), }, "key_name": { - Type: schema.TypeString, - Optional: true, + Type: schema.TypeString, + Optional: true, + ValidateFunc: MatchesRegexp(`^[\p{Latin}\p{P}]{1,255}$`), }, "public_ip_address": { Type: schema.TypeBool, @@ -147,20 +159,25 @@ func resourceGhostApp() *schema.Resource { Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "size": { - Type: schema.TypeInt, - Optional: true, - Default: 20, + Type: schema.TypeInt, + Optional: true, + Default: 20, + ValidateFunc: validation.IntAtLeast(20), }, "name": { - Type: schema.TypeString, - Optional: true, + Type: schema.TypeString, + Optional: true, + ValidateFunc: MatchesRegexp(`^$|^(/[a-z0-9]+/)?[a-z0-9]+$`), }, }, }, }, "security_groups": { - Type: schema.TypeList, - Elem: &schema.Schema{Type: schema.TypeString}, + Type: schema.TypeList, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: MatchesRegexp(`^sg-[a-z0-9]*$`), + }, Optional: true, }, "instance_tags": { @@ -180,8 +197,11 @@ func resourceGhostApp() *schema.Resource { }, }, "subnet_ids": { - Type: schema.TypeList, - Elem: &schema.Schema{Type: schema.TypeString}, + Type: schema.TypeList, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: MatchesRegexp(`^subnet-[a-z0-9]*$`), + }, Optional: true, }, "optional_volumes": { @@ -190,12 +210,15 @@ func resourceGhostApp() *schema.Resource { Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "device_name": { - Type: schema.TypeString, - Required: true, + Type: schema.TypeString, + Required: true, + ValidateFunc: MatchesRegexp(`^/dev/xvd[b-m]$`), }, "volume_type": { Type: schema.TypeString, Required: true, + ValidateFunc: validation.StringInSlice([]string{ + "gp2", "io1", "standard", "st1", "sc1"}, false), }, "volume_size": { Type: schema.TypeInt, @@ -221,8 +244,9 @@ func resourceGhostApp() *schema.Resource { Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "key": { - Type: schema.TypeString, - Required: true, + Type: schema.TypeString, + Required: true, + ValidateFunc: MatchesRegexp(`^[a-zA-Z_]+[a-zA-Z0-9_]*$`), }, "value": { Type: schema.TypeString, @@ -232,8 +256,11 @@ func resourceGhostApp() *schema.Resource { }, }, "log_notifications": { - Type: schema.TypeList, - Elem: &schema.Schema{Type: schema.TypeString}, + Type: schema.TypeList, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: MatchesRegexp(`^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$`), + }, Optional: true, }, "blue_green": { @@ -246,8 +273,9 @@ func resourceGhostApp() *schema.Resource { Optional: true, }, "color": { - Type: schema.TypeString, - Optional: true, + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"blue", "green"}, false), }, "is_online": { Type: schema.TypeBool, @@ -283,16 +311,19 @@ func resourceGhostApp() *schema.Resource { Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "name": { - Type: schema.TypeString, - Required: true, + Type: schema.TypeString, + Required: true, + ValidateFunc: MatchesRegexp(`^[a-zA-Z0-9\\.\\-\\_]*$`), }, "version": { - Type: schema.TypeString, - Optional: true, + Type: schema.TypeString, + Optional: true, + ValidateFunc: MatchesRegexp(`^[a-zA-Z0-9\\.\\-\\_\\/:~\\+=\\,]*$`), }, "provisioner": { - Type: schema.TypeString, - Optional: true, + Type: schema.TypeString, + Optional: true, + ValidateFunc: MatchesRegexp(`^[a-zA-Z0-9]*$`), }, }, }, @@ -328,30 +359,35 @@ func resourceGhostApp() *schema.Resource { Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "name": { - Type: schema.TypeString, - Required: true, + Type: schema.TypeString, + Required: true, + ValidateFunc: MatchesRegexp(`^[a-zA-Z0-9\.\-\_]*$`), }, "git_repo": { Type: schema.TypeString, Required: true, }, "path": { - Type: schema.TypeString, - Required: true, + Type: schema.TypeString, + Required: true, + ValidateFunc: MatchesRegexp(`^(/[a-zA-Z0-9\.\-\_]+)+$`), }, "scope": { - Type: schema.TypeString, - Required: true, + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"system", "code"}, false), }, "uid": { - Type: schema.TypeInt, - Optional: true, - Default: 0, + Type: schema.TypeInt, + Optional: true, + Default: 0, + ValidateFunc: validation.IntAtLeast(0), }, "gid": { - Type: schema.TypeInt, - Optional: true, - Default: 0, + Type: schema.TypeInt, + Optional: true, + Default: 0, + ValidateFunc: validation.IntAtLeast(0), }, "build_pack": { Type: schema.TypeString, @@ -399,12 +435,14 @@ func resourceGhostApp() *schema.Resource { Optional: true, }, "wait_before_deploy": { - Type: schema.TypeInt, - Optional: true, + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntAtLeast(0), }, "wait_after_deploy": { - Type: schema.TypeInt, - Optional: true, + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntAtLeast(0), }, }, }, diff --git a/ghost/resource_ghost_app_test.go b/ghost/resource_ghost_app_test.go index 2022ace..0fa5d32 100644 --- a/ghost/resource_ghost_app_test.go +++ b/ghost/resource_ghost_app_test.go @@ -25,7 +25,16 @@ func TestAccGhostAppBasic(t *testing.T) { testAccCheckGhostAppExists(resourceName), resource.TestCheckResourceAttr(resourceName, "name", envName), resource.TestCheckResourceAttr(resourceName, "env", "dev"), + resource.TestCheckResourceAttr(resourceName, "region", "eu-west-1"), + ), + }, + { + Config: testAccGhostAppConfigUpdated(envName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckGhostAppExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", envName), resource.TestCheckResourceAttr(resourceName, "env", "dev"), + resource.TestCheckResourceAttr(resourceName, "region", "eu-west-2"), ), }, }, @@ -59,7 +68,7 @@ func testAccGhostAppConfig(name string) string { resource "ghost_app" "test" { name = "%s" env = "dev" - role = "webfront" + role = "web-front_" region = "eu-west-1" instance_type = "t2.micro" @@ -116,7 +125,7 @@ func testAccGhostAppConfig(name string) string { name = "wordpress2" pre_deploy = "ZXhpdCAx" post_deploy = "ZXhpdCAx" - path = "/var/www" + path = "/var/www-test.test_ew" scope = "code" git_repo = "https://github.com/KnpLabs/KnpIpsum.git" }] @@ -142,3 +151,179 @@ func testAccGhostAppConfig(name string) string { } `, name) } + +func testAccGhostAppConfigUpdated(name string) string { + return fmt.Sprintf(` + resource "ghost_app" "test" { + name = "%s" + env = "dev" + role = "webfront" + + region = "eu-west-2" + instance_type = "t2.micro" + vpc_id = "vpc-3f1eb65a" + + log_notifications = [ + "ghost-devops@domain.com", + ] + + build_infos = { + subnet_id = "subnet-a7e849fe" + ssh_username = "admin" + source_ami = "ami-03ce4474" + } + + environment_infos = { + instance_profile = "iam.ec2.demo" + key_name = "ghost-demo" + root_block_device = { + name = "testblockdevice" + size = 20 + } + optional_volumes = [{ + device_name = "/dev/xvdd" + volume_type = "gp2" + volume_size = 20 + }] + subnet_ids = ["subnet-a7e849fe"] + security_groups = ["sg-6814f60c"] + instance_tags = [{ + tag_name = "Name" + tag_value = "wordpress" + }, + { + tag_name = "Type" + tag_value = "front" + }] + } + + autoscale = { + name = "autoscale" + min = 1 + max = 2 + } + + modules = [{ + name = "wordpress" + pre_deploy = "" + path = "/var/www" + scope = "code" + git_repo = "https://github.com/KnpLabs/KnpIpsum.git" + }, + { + name = "wordpress2" + pre_deploy = "ZXhpdCAx" + post_deploy = "ZXhpdCAx" + path = "/var/www" + scope = "code" + git_repo = "https://github.com/KnpLabs/KnpIpsum.git" + }] + + features = [{ + version = "5.4" + name = "php5" + }, + { + version = "2.2" + name = "apache2" + }] + + lifecycle_hooks = { + pre_buildimage = "#!/usr/bin/env bash" + } + + environment_variables = [{ + key = "myvar2" + value = "myvalue2" + }] + } + `, name) +} + +func testAccGhostAppConfigOmitEmpty(name string) string { + return fmt.Sprintf(` + resource "ghost_app" "test" { + name = "%s" + env = "dev" + role = "webfront" + + region = "eu-west-2" + instance_type = "t2.micro" + vpc_id = "vpc-3f1eb65a" + + log_notifications = [ + "ghost-devops@domain.com", + ] + + build_infos = { + subnet_id = "subnet-a7e849fe" + ssh_username = "admin" + source_ami = "ami-03ce4474" + } + + environment_infos = { + instance_profile = "iam.ec2.demo" + key_name = "ghost-demo" + root_block_device = { + name = "testblockdevice" + size = 20 + } + optional_volumes = [{ + device_name = "data" + volume_type = "lol" + volume_size = 20 + }] + subnet_ids = ["subnet-a7e849fe"] + security_groups = ["sg-6814f60c"] + instance_tags = [{ + tag_name = "Name" + tag_value = "wordpress" + }, + { + tag_name = "Type" + tag_value = "front" + }] + } + + autoscale = { + name = "autoscale" + min = 0 + max = 2 + } + + modules = [{ + name = "wordpress" + pre_deploy = "" + path = "/var/www" + scope = "code" + git_repo = "https://github.com/KnpLabs/KnpIpsum.git" + }, + { + name = "wordpress2" + pre_deploy = "ZXhpdCAx" + post_deploy = "ZXhpdCAx" + path = "/var/www" + scope = "code" + git_repo = "https://github.com/KnpLabs/KnpIpsum.git" + }] + + features = [{ + version = "5.4" + name = "php5" + }, + { + version = "2.2" + name = "apache2" + }] + + lifecycle_hooks = { + pre_buildimage = "#!/usr/bin/env bash" + } + + environment_variables = [{ + key = "myvar2" + value = "myvalue2" + }] + } + `, name) +} diff --git a/vendor/github.com/hashicorp/terraform/helper/structure/expand_json.go b/vendor/github.com/hashicorp/terraform/helper/structure/expand_json.go new file mode 100644 index 0000000..b3eb90f --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/helper/structure/expand_json.go @@ -0,0 +1,11 @@ +package structure + +import "encoding/json" + +func ExpandJsonFromString(jsonString string) (map[string]interface{}, error) { + var result map[string]interface{} + + err := json.Unmarshal([]byte(jsonString), &result) + + return result, err +} diff --git a/vendor/github.com/hashicorp/terraform/helper/structure/flatten_json.go b/vendor/github.com/hashicorp/terraform/helper/structure/flatten_json.go new file mode 100644 index 0000000..578ad2e --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/helper/structure/flatten_json.go @@ -0,0 +1,16 @@ +package structure + +import "encoding/json" + +func FlattenJsonToString(input map[string]interface{}) (string, error) { + if len(input) == 0 { + return "", nil + } + + result, err := json.Marshal(input) + if err != nil { + return "", err + } + + return string(result), nil +} diff --git a/vendor/github.com/hashicorp/terraform/helper/structure/normalize_json.go b/vendor/github.com/hashicorp/terraform/helper/structure/normalize_json.go new file mode 100644 index 0000000..3256b47 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/helper/structure/normalize_json.go @@ -0,0 +1,24 @@ +package structure + +import "encoding/json" + +// Takes a value containing JSON string and passes it through +// the JSON parser to normalize it, returns either a parsing +// error or normalized JSON string. +func NormalizeJsonString(jsonString interface{}) (string, error) { + var j interface{} + + if jsonString == nil || jsonString.(string) == "" { + return "", nil + } + + s := jsonString.(string) + + err := json.Unmarshal([]byte(s), &j) + if err != nil { + return s, err + } + + bytes, _ := json.Marshal(j) + return string(bytes[:]), nil +} diff --git a/vendor/github.com/hashicorp/terraform/helper/structure/suppress_json_diff.go b/vendor/github.com/hashicorp/terraform/helper/structure/suppress_json_diff.go new file mode 100644 index 0000000..46f794a --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/helper/structure/suppress_json_diff.go @@ -0,0 +1,21 @@ +package structure + +import ( + "reflect" + + "github.com/hashicorp/terraform/helper/schema" +) + +func SuppressJsonDiff(k, old, new string, d *schema.ResourceData) bool { + oldMap, err := ExpandJsonFromString(old) + if err != nil { + return false + } + + newMap, err := ExpandJsonFromString(new) + if err != nil { + return false + } + + return reflect.DeepEqual(oldMap, newMap) +} diff --git a/vendor/github.com/hashicorp/terraform/helper/validation/validation.go b/vendor/github.com/hashicorp/terraform/helper/validation/validation.go new file mode 100644 index 0000000..1fc3a6c --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/helper/validation/validation.go @@ -0,0 +1,222 @@ +package validation + +import ( + "fmt" + "net" + "reflect" + "regexp" + "strings" + "time" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/structure" +) + +// IntBetween returns a SchemaValidateFunc which tests if the provided value +// is of type int and is between min and max (inclusive) +func IntBetween(min, max int) schema.SchemaValidateFunc { + return func(i interface{}, k string) (s []string, es []error) { + v, ok := i.(int) + if !ok { + es = append(es, fmt.Errorf("expected type of %s to be int", k)) + return + } + + if v < min || v > max { + es = append(es, fmt.Errorf("expected %s to be in the range (%d - %d), got %d", k, min, max, v)) + return + } + + return + } +} + +// IntAtLeast returns a SchemaValidateFunc which tests if the provided value +// is of type int and is at least min (inclusive) +func IntAtLeast(min int) schema.SchemaValidateFunc { + return func(i interface{}, k string) (s []string, es []error) { + v, ok := i.(int) + if !ok { + es = append(es, fmt.Errorf("expected type of %s to be int", k)) + return + } + + if v < min { + es = append(es, fmt.Errorf("expected %s to be at least (%d), got %d", k, min, v)) + return + } + + return + } +} + +// IntAtMost returns a SchemaValidateFunc which tests if the provided value +// is of type int and is at most max (inclusive) +func IntAtMost(max int) schema.SchemaValidateFunc { + return func(i interface{}, k string) (s []string, es []error) { + v, ok := i.(int) + if !ok { + es = append(es, fmt.Errorf("expected type of %s to be int", k)) + return + } + + if v > max { + es = append(es, fmt.Errorf("expected %s to be at most (%d), got %d", k, max, v)) + return + } + + return + } +} + +// StringInSlice returns a SchemaValidateFunc which tests if the provided value +// is of type string and matches the value of an element in the valid slice +// will test with in lower case if ignoreCase is true +func StringInSlice(valid []string, ignoreCase bool) schema.SchemaValidateFunc { + return func(i interface{}, k string) (s []string, es []error) { + v, ok := i.(string) + if !ok { + es = append(es, fmt.Errorf("expected type of %s to be string", k)) + return + } + + for _, str := range valid { + if v == str || (ignoreCase && strings.ToLower(v) == strings.ToLower(str)) { + return + } + } + + es = append(es, fmt.Errorf("expected %s to be one of %v, got %s", k, valid, v)) + return + } +} + +// StringLenBetween returns a SchemaValidateFunc which tests if the provided value +// is of type string and has length between min and max (inclusive) +func StringLenBetween(min, max int) schema.SchemaValidateFunc { + return func(i interface{}, k string) (s []string, es []error) { + v, ok := i.(string) + if !ok { + es = append(es, fmt.Errorf("expected type of %s to be string", k)) + return + } + if len(v) < min || len(v) > max { + es = append(es, fmt.Errorf("expected length of %s to be in the range (%d - %d), got %s", k, min, max, v)) + } + return + } +} + +// StringMatch returns a SchemaValidateFunc which tests if the provided value +// matches a given regexp. Optionally an error message can be provided to +// return something friendlier than "must match some globby regexp". +func StringMatch(r *regexp.Regexp, message string) schema.SchemaValidateFunc { + return func(i interface{}, k string) ([]string, []error) { + v, ok := i.(string) + if !ok { + return nil, []error{fmt.Errorf("expected type of %s to be string", k)} + } + + if ok := r.MatchString(v); !ok { + if message != "" { + return nil, []error{fmt.Errorf("invalid value for %s (%s)", k, message)} + + } + return nil, []error{fmt.Errorf("expected value of %s to match regular expression %q", k, r)} + } + return nil, nil + } +} + +// NoZeroValues is a SchemaValidateFunc which tests if the provided value is +// not a zero value. It's useful in situations where you want to catch +// explicit zero values on things like required fields during validation. +func NoZeroValues(i interface{}, k string) (s []string, es []error) { + if reflect.ValueOf(i).Interface() == reflect.Zero(reflect.TypeOf(i)).Interface() { + switch reflect.TypeOf(i).Kind() { + case reflect.String: + es = append(es, fmt.Errorf("%s must not be empty", k)) + case reflect.Int, reflect.Float64: + es = append(es, fmt.Errorf("%s must not be zero", k)) + default: + // this validator should only ever be applied to TypeString, TypeInt and TypeFloat + panic(fmt.Errorf("can't use NoZeroValues with %T attribute %s", i, k)) + } + } + return +} + +// CIDRNetwork returns a SchemaValidateFunc which tests if the provided value +// is of type string, is in valid CIDR network notation, and has significant bits between min and max (inclusive) +func CIDRNetwork(min, max int) schema.SchemaValidateFunc { + return func(i interface{}, k string) (s []string, es []error) { + v, ok := i.(string) + if !ok { + es = append(es, fmt.Errorf("expected type of %s to be string", k)) + return + } + + _, ipnet, err := net.ParseCIDR(v) + if err != nil { + es = append(es, fmt.Errorf( + "expected %s to contain a valid CIDR, got: %s with err: %s", k, v, err)) + return + } + + if ipnet == nil || v != ipnet.String() { + es = append(es, fmt.Errorf( + "expected %s to contain a valid network CIDR, expected %s, got %s", + k, ipnet, v)) + } + + sigbits, _ := ipnet.Mask.Size() + if sigbits < min || sigbits > max { + es = append(es, fmt.Errorf( + "expected %q to contain a network CIDR with between %d and %d significant bits, got: %d", + k, min, max, sigbits)) + } + + return + } +} + +// ValidateJsonString is a SchemaValidateFunc which tests to make sure the +// supplied string is valid JSON. +func ValidateJsonString(v interface{}, k string) (ws []string, errors []error) { + if _, err := structure.NormalizeJsonString(v); err != nil { + errors = append(errors, fmt.Errorf("%q contains an invalid JSON: %s", k, err)) + } + return +} + +// ValidateListUniqueStrings is a ValidateFunc that ensures a list has no +// duplicate items in it. It's useful for when a list is needed over a set +// because order matters, yet the items still need to be unique. +func ValidateListUniqueStrings(v interface{}, k string) (ws []string, errors []error) { + for n1, v1 := range v.([]interface{}) { + for n2, v2 := range v.([]interface{}) { + if v1.(string) == v2.(string) && n1 != n2 { + errors = append(errors, fmt.Errorf("%q: duplicate entry - %s", k, v1.(string))) + } + } + } + return +} + +// ValidateRegexp returns a SchemaValidateFunc which tests to make sure the +// supplied string is a valid regular expression. +func ValidateRegexp(v interface{}, k string) (ws []string, errors []error) { + if _, err := regexp.Compile(v.(string)); err != nil { + errors = append(errors, fmt.Errorf("%q: %s", k, err)) + } + return +} + +// ValidateRFC3339TimeString is a ValidateFunc that ensures a string parses +// as time.RFC3339 format +func ValidateRFC3339TimeString(v interface{}, k string) (ws []string, errors []error) { + if _, err := time.Parse(time.RFC3339, v.(string)); err != nil { + errors = append(errors, fmt.Errorf("%q: invalid RFC3339 timestamp", k)) + } + return +} diff --git a/vendor/vendor.json b/vendor/vendor.json index f0b2143..67ddc86 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -500,6 +500,12 @@ "revision": "26058acdefa6db8a258b554bf137a07d98c7ddc2", "revisionTime": "2018-03-01T14:13:39Z" }, + { + "checksumSHA1": "JL8eJjRMxGI2zz2fMYXhXQpRtPk=", + "path": "github.com/hashicorp/terraform/helper/validation", + "revision": "ecec59f0f0ad69350dfc8beb76c2d95cfd92f476", + "revisionTime": "2018-03-15T21:06:57Z" + }, { "checksumSHA1": "VgChgJYOTM4zrNOWifEqDsdv7Hk=", "path": "github.com/hashicorp/terraform/httpclient", From 80695ac7763f5aaa93d8fa7ece0041d5fb0c874d Mon Sep 17 00:00:00 2001 From: Jordan Caussat Date: Tue, 20 Mar 2018 14:09:25 +0100 Subject: [PATCH 14/21] TER-235: Update go-st package --- vendor/cloud-deploy.io/go-st/spec.go | 116 ++++++++++++++++++++------- vendor/vendor.json | 8 +- 2 files changed, 91 insertions(+), 33 deletions(-) diff --git a/vendor/cloud-deploy.io/go-st/spec.go b/vendor/cloud-deploy.io/go-st/spec.go index a014dc4..4c0755f 100644 --- a/vendor/cloud-deploy.io/go-st/spec.go +++ b/vendor/cloud-deploy.io/go-st/spec.go @@ -38,37 +38,49 @@ type EveCollectionMetadata struct { // Ghost App's build_infos struct type BuildInfos struct { - SourceAmi string `json:"source_ami"` - SshUsername string `json:"ssh_username"` - SubnetID string `json:"subnet_id"` + SourceAmi string `json:"source_ami"` + SshUsername string `json:"ssh_username"` + SubnetID string `json:"subnet_id"` + AmiName string `json:"ami_name,omitempty"` + ContainerImage string `json:"container_image,omitempty"` + SourceContainerImage string `json:"source_container_image,omitempty"` } // Ghost App's environment_infos structs type OptionalVolume struct { - InstanceProfile string `json:"device_name"` - KeyName string `json:"volume_type"` - VolumeSize int `json:"volume_size"` - IOPS int `json:"iops"` + DeviceName string `json:"device_name"` + VolumeType string `json:"volume_type"` + VolumeSize int `json:"volume_size"` + Iops int `json:"iops,omitempty"` + LaunchBlockDeviceMappings bool `json:"launch_block_device_mappings,omitempty"` } type RootBlockDevice struct { - Size int `json:"size"` - Name string `json:"name"` + Size int `json:"size,omitempty"` + Name string `json:"name,omitempty"` +} + +type InstanceTag struct { + TagName string `json:"tag_name"` + TagValue string `json:"tag_value"` } type EnvironmentInfos struct { - InstanceProfile string `json:"instance_profile"` - KeyName string `json:"key_name"` - OptionalVolumes []OptionalVolume `json:"optional_volumes"` - RootBlockDevice RootBlockDevice `json:"root_block_device"` - SecurityGroups []string `json:"security_groups"` - SubnetIDs []string `json:"subnet_ids"` + InstanceProfile string `json:"instance_profile,omitempty"` + KeyName string `json:"key_name,omitempty"` + SecurityGroups []string `json:"security_groups,omitempty"` + SubnetIDs []string `json:"subnet_ids,omitempty"` + OptionalVolumes *[]OptionalVolume `json:"optional_volumes,omitempty"` + RootBlockDevice *RootBlockDevice `json:"root_block_device,omitempty"` + PublicIpAddress bool `json:"public_ip_address"` + InstanceTags *[]InstanceTag `json:"instance_tags,omitempty"` } // Ghost App's feature struct type Feature struct { - Name string `json:"name"` - Version string `json:"version"` + Name string `json:"name"` + Version string `json:"version,omitempty"` + Provisioner string `json:"provisioner,omitempty"` } // Ghost App's module struct @@ -84,10 +96,45 @@ type Module struct { GID int `json:"gid"` // Scripts - BuildPack string `json:"build_pack"` - PreDeploy string `json:"pre_deploy"` - PostDeploy string `json:"post_deploy"` - AfterAllDeploy string `json:"after_all_deploy"` + BuildPack string `json:"build_pack,omitempty"` + PreDeploy string `json:"pre_deploy,omitempty"` + PostDeploy string `json:"post_deploy,omitempty"` + AfterAllDeploy string `json:"after_all_deploy,omitempty"` + LastDeployment string `json:"last_deployment,omitempty"` +} + +type LifecycleHooks struct { + PreBuildimage string `json:"pre_buildimage,omitempty"` + PostBuildimage string `json:"post_buildimage,omitempty"` + PreBootstrap string `json:"pre_bootstrap,omitempty"` + PostBootstrap string `json:"post_bootstrap,omitempty"` +} + +type EnvironmentVariable struct { + Key string `json:"var_key,omitempty"` + Value string `json:"var_value,omitempty"` +} + +type Autoscale struct { + Min int `json:"min"` + Max int `json:"max"` + EnableMetrics bool `json:"enable_metrics"` + Name string `json:"name,omitempty"` +} + +type SafeDeployment struct { + WaitBeforeDeploy int `json:"wait_before_deploy"` + WaitAfterDeploy int `json:"wait_after_deploy"` + LoadBalancerType string `json:"load_balancer_type,omitempty"` + AppTagValue string `json:"app_tag_value,omitempty"` + HaBackend string `json:"ha_backend,omitempty"` + ApiPort string `json:"api_port,omitempty"` +} + +type PendingChange struct { + Field string `json:"field,omitempty"` + Updated string `json:"updated,omitempty"` + User string `json:"user,omitempty"` } // Ghost App struct @@ -99,19 +146,30 @@ type App struct { Env string `json:"env"` Role string `json:"role"` - Region string `json:"region"` - InstanceType string `json:"instance_type"` - VpcID string `json:"vpc_id"` + Region string `json:"region"` + InstanceType string `json:"instance_type"` + InstanceMonitoring bool `json:"instance_monitoring"` + VpcID string `json:"vpc_id"` + + LifecycleHooks *LifecycleHooks `json:"lifecycle_hooks,omitempty"` + + LogNotifications []string `json:"log_notifications,omitempty"` + + BuildInfos *BuildInfos `json:"build_infos,omitempty"` + + EnvironmentInfos *EnvironmentInfos `json:"environment_infos,omitempty"` + + EnvironmentVariables *[]EnvironmentVariable `json:"env_vars,omitempty"` - LogNotifications []string `json:"log_notifications"` + Features *[]Feature `json:"features,omitempty"` - BuildInfos BuildInfos `json:"build_infos"` + Modules *[]Module `json:"modules"` - EnvironmentInfos EnvironmentInfos `json:"environment_infos"` + Autoscale *Autoscale `json:"autoscale,omitempty"` - Features []Feature `json:"features"` + SafeDeployment *SafeDeployment `json:"safe-deployment,omitempty"` - Modules []Module `json:"modules"` + PendingChanges *[]PendingChange `json:"pending_changes,omitempty"` } // Ghost Apps collection diff --git a/vendor/vendor.json b/vendor/vendor.json index 67ddc86..e5f9ed2 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -3,10 +3,10 @@ "ignore": "test", "package": [ { - "checksumSHA1": "xrxkZvjRP9yQDW5wJ8Co7b10IX8=", + "checksumSHA1": "M3O9E6Z9qugnEsi74z6Asg6r46U=", "path": "cloud-deploy.io/go-st", - "revision": "17120a6d613348768fa5915ddd62f39e6851b1a5", - "revisionTime": "2016-11-26T17:29:32Z" + "revision": "0518a6e9eeb01f71abec82ecf50a7ce9d356a8c5", + "revisionTime": "2018-03-20T12:51:06Z" }, { "checksumSHA1": "jQh1fnoKPKMURvKkpdRjN695nAQ=", @@ -1077,5 +1077,5 @@ "revisionTime": "2018-02-28T23:01:36Z" } ], - "rootPath": "bitbucket.org/morea/terraform-provider-ghost" + "rootPath": "cloud-deploy.io/terraform-provider-ghost" } From fcd56970d1c9c01b452d98e76c0b88d5b6bb3111 Mon Sep 17 00:00:00 2001 From: Jordan Caussat Date: Tue, 20 Mar 2018 14:43:33 +0100 Subject: [PATCH 15/21] TER-235: Fix typo in readme, example and app test file --- README.md | 2 +- examples/main.tf | 2 +- ghost/resource_ghost_app_test.go | 190 +------------------------------ 3 files changed, 4 insertions(+), 190 deletions(-) diff --git a/README.md b/README.md index c253b49..263b644 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ provider "ghost" { } resource "ghost_app" "test" { - name = "%s" + name = "wordpress" env = "dev" role = "webfront" diff --git a/examples/main.tf b/examples/main.tf index 056759d..fa0d301 100644 --- a/examples/main.tf +++ b/examples/main.tf @@ -5,7 +5,7 @@ provider "ghost" { } resource "ghost_app" "test" { - name = "%s" + name = "wordpress" env = "dev" role = "webfront" diff --git a/ghost/resource_ghost_app_test.go b/ghost/resource_ghost_app_test.go index 0fa5d32..81c2bd3 100644 --- a/ghost/resource_ghost_app_test.go +++ b/ghost/resource_ghost_app_test.go @@ -25,16 +25,6 @@ func TestAccGhostAppBasic(t *testing.T) { testAccCheckGhostAppExists(resourceName), resource.TestCheckResourceAttr(resourceName, "name", envName), resource.TestCheckResourceAttr(resourceName, "env", "dev"), - resource.TestCheckResourceAttr(resourceName, "region", "eu-west-1"), - ), - }, - { - Config: testAccGhostAppConfigUpdated(envName), - Check: resource.ComposeAggregateTestCheckFunc( - testAccCheckGhostAppExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "name", envName), - resource.TestCheckResourceAttr(resourceName, "env", "dev"), - resource.TestCheckResourceAttr(resourceName, "region", "eu-west-2"), ), }, }, @@ -68,7 +58,7 @@ func testAccGhostAppConfig(name string) string { resource "ghost_app" "test" { name = "%s" env = "dev" - role = "web-front_" + role = "webfront" region = "eu-west-1" instance_type = "t2.micro" @@ -125,7 +115,7 @@ func testAccGhostAppConfig(name string) string { name = "wordpress2" pre_deploy = "ZXhpdCAx" post_deploy = "ZXhpdCAx" - path = "/var/www-test.test_ew" + path = "/var/www-test.test" scope = "code" git_repo = "https://github.com/KnpLabs/KnpIpsum.git" }] @@ -151,179 +141,3 @@ func testAccGhostAppConfig(name string) string { } `, name) } - -func testAccGhostAppConfigUpdated(name string) string { - return fmt.Sprintf(` - resource "ghost_app" "test" { - name = "%s" - env = "dev" - role = "webfront" - - region = "eu-west-2" - instance_type = "t2.micro" - vpc_id = "vpc-3f1eb65a" - - log_notifications = [ - "ghost-devops@domain.com", - ] - - build_infos = { - subnet_id = "subnet-a7e849fe" - ssh_username = "admin" - source_ami = "ami-03ce4474" - } - - environment_infos = { - instance_profile = "iam.ec2.demo" - key_name = "ghost-demo" - root_block_device = { - name = "testblockdevice" - size = 20 - } - optional_volumes = [{ - device_name = "/dev/xvdd" - volume_type = "gp2" - volume_size = 20 - }] - subnet_ids = ["subnet-a7e849fe"] - security_groups = ["sg-6814f60c"] - instance_tags = [{ - tag_name = "Name" - tag_value = "wordpress" - }, - { - tag_name = "Type" - tag_value = "front" - }] - } - - autoscale = { - name = "autoscale" - min = 1 - max = 2 - } - - modules = [{ - name = "wordpress" - pre_deploy = "" - path = "/var/www" - scope = "code" - git_repo = "https://github.com/KnpLabs/KnpIpsum.git" - }, - { - name = "wordpress2" - pre_deploy = "ZXhpdCAx" - post_deploy = "ZXhpdCAx" - path = "/var/www" - scope = "code" - git_repo = "https://github.com/KnpLabs/KnpIpsum.git" - }] - - features = [{ - version = "5.4" - name = "php5" - }, - { - version = "2.2" - name = "apache2" - }] - - lifecycle_hooks = { - pre_buildimage = "#!/usr/bin/env bash" - } - - environment_variables = [{ - key = "myvar2" - value = "myvalue2" - }] - } - `, name) -} - -func testAccGhostAppConfigOmitEmpty(name string) string { - return fmt.Sprintf(` - resource "ghost_app" "test" { - name = "%s" - env = "dev" - role = "webfront" - - region = "eu-west-2" - instance_type = "t2.micro" - vpc_id = "vpc-3f1eb65a" - - log_notifications = [ - "ghost-devops@domain.com", - ] - - build_infos = { - subnet_id = "subnet-a7e849fe" - ssh_username = "admin" - source_ami = "ami-03ce4474" - } - - environment_infos = { - instance_profile = "iam.ec2.demo" - key_name = "ghost-demo" - root_block_device = { - name = "testblockdevice" - size = 20 - } - optional_volumes = [{ - device_name = "data" - volume_type = "lol" - volume_size = 20 - }] - subnet_ids = ["subnet-a7e849fe"] - security_groups = ["sg-6814f60c"] - instance_tags = [{ - tag_name = "Name" - tag_value = "wordpress" - }, - { - tag_name = "Type" - tag_value = "front" - }] - } - - autoscale = { - name = "autoscale" - min = 0 - max = 2 - } - - modules = [{ - name = "wordpress" - pre_deploy = "" - path = "/var/www" - scope = "code" - git_repo = "https://github.com/KnpLabs/KnpIpsum.git" - }, - { - name = "wordpress2" - pre_deploy = "ZXhpdCAx" - post_deploy = "ZXhpdCAx" - path = "/var/www" - scope = "code" - git_repo = "https://github.com/KnpLabs/KnpIpsum.git" - }] - - features = [{ - version = "5.4" - name = "php5" - }, - { - version = "2.2" - name = "apache2" - }] - - lifecycle_hooks = { - pre_buildimage = "#!/usr/bin/env bash" - } - - environment_variables = [{ - key = "myvar2" - value = "myvalue2" - }] - } - `, name) -} From a0e2aea5726e031b27501a715d8b744ef9458189 Mon Sep 17 00:00:00 2001 From: Jordan Caussat Date: Tue, 20 Mar 2018 14:52:15 +0100 Subject: [PATCH 16/21] TER-235: fix regexps in ghost app resource --- ghost/resource_ghost_app.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ghost/resource_ghost_app.go b/ghost/resource_ghost_app.go index 974def6..7f2fa04 100644 --- a/ghost/resource_ghost_app.go +++ b/ghost/resource_ghost_app.go @@ -140,7 +140,7 @@ func resourceGhostApp() *schema.Resource { "instance_profile": { Type: schema.TypeString, Optional: true, - ValidateFunc: MatchesRegexp(`^[a-zA-Z0-9\\+\\=\\,\\.\\@\\-\\_]{1,128}$`), + ValidateFunc: MatchesRegexp(`^[a-zA-Z0-9\+\=\,\.\@\-\_]{1,128}$`), }, "key_name": { Type: schema.TypeString, @@ -313,12 +313,12 @@ func resourceGhostApp() *schema.Resource { "name": { Type: schema.TypeString, Required: true, - ValidateFunc: MatchesRegexp(`^[a-zA-Z0-9\\.\\-\\_]*$`), + ValidateFunc: MatchesRegexp(`^[a-zA-Z0-9\.\-\_]*$`), }, "version": { Type: schema.TypeString, Optional: true, - ValidateFunc: MatchesRegexp(`^[a-zA-Z0-9\\.\\-\\_\\/:~\\+=\\,]*$`), + ValidateFunc: MatchesRegexp(`^[a-zA-Z0-9\.\-\_\/:~\+=\,]*$`), }, "provisioner": { Type: schema.TypeString, From 68a8c5b89aa4a019d7fbea1d52aa205ab6c161d2 Mon Sep 17 00:00:00 2001 From: Jordan Caussat Date: Thu, 22 Mar 2018 18:01:48 +0100 Subject: [PATCH 17/21] TER-235: return Read() for both Update and Create functions --- ghost/resource_ghost_app.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ghost/resource_ghost_app.go b/ghost/resource_ghost_app.go index 7f2fa04..5e3b8fe 100644 --- a/ghost/resource_ghost_app.go +++ b/ghost/resource_ghost_app.go @@ -466,7 +466,7 @@ func resourceGhostAppCreate(d *schema.ResourceData, meta interface{}) error { d.SetId(eveMetadata.ID) - return nil + return resourceGhostAppRead(d, meta) } func resourceGhostAppRead(d *schema.ResourceData, meta interface{}) error { @@ -480,7 +480,7 @@ func resourceGhostAppRead(d *schema.ResourceData, meta interface{}) error { func resourceGhostAppUpdate(d *schema.ResourceData, meta interface{}) error { //client := meta.(*ghost.Client) log.Printf("[INFO] Updating Ghost app %s", d.Get("name").(string)) - return nil + return resourceGhostAppRead(d, meta) } func resourceGhostAppDelete(d *schema.ResourceData, meta interface{}) error { From c0dfe82b7dd8047404a0bb971b4cb6255c9bbf36 Mon Sep 17 00:00:00 2001 From: Jordan Caussat Date: Fri, 23 Mar 2018 15:56:16 +0100 Subject: [PATCH 18/21] TER-235: Fix crash when root_block_device isn't defined --- ghost/resource_ghost_app.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ghost/resource_ghost_app.go b/ghost/resource_ghost_app.go index 5e3b8fe..81271e3 100644 --- a/ghost/resource_ghost_app.go +++ b/ghost/resource_ghost_app.go @@ -650,7 +650,12 @@ func expandGhostAppEnvironmentInfos(d *schema.ResourceData) *ghost.EnvironmentIn } func expandGhostAppRootBlockDevice(d []interface{}) *ghost.RootBlockDevice { + if len(d) == 0 { + return nil + } + data := d[0].(map[string]interface{}) + rootBlockDevice := &ghost.RootBlockDevice{ Name: data["name"].(string), Size: data["size"].(int), From d9fb59c514d4391435b50624cdba40778ebea445 Mon Sep 17 00:00:00 2001 From: Jordan Caussat Date: Fri, 23 Mar 2018 16:32:02 +0100 Subject: [PATCH 19/21] TER-235: add unit tests for expanders and improve expanders' prototype --- ghost/resource_ghost_app.go | 53 ++-- ghost/resource_ghost_app_test.go | 418 +++++++++++++++++++++++++++++++ 2 files changed, 441 insertions(+), 30 deletions(-) diff --git a/ghost/resource_ghost_app.go b/ghost/resource_ghost_app.go index 81271e3..4df47aa 100644 --- a/ghost/resource_ghost_app.go +++ b/ghost/resource_ghost_app.go @@ -501,26 +501,25 @@ func expandGhostApp(d *schema.ResourceData) ghost.App { VpcID: d.Get("vpc_id").(string), InstanceMonitoring: d.Get("instance_monitoring").(bool), - Modules: expandGhostAppModules(d), - Features: expandGhostAppFeatures(d), - Autoscale: expandGhostAppAutoscale(d), - BuildInfos: expandGhostAppBuildInfos(d), - EnvironmentInfos: expandGhostAppEnvironmentInfos(d), - LifecycleHooks: expandGhostAppLifecycleHooks(d), + Modules: expandGhostAppModules(d.Get("modules").([]interface{})), + Features: expandGhostAppFeatures(d.Get("features").([]interface{})), + Autoscale: expandGhostAppAutoscale(d.Get("autoscale").([]interface{})), + BuildInfos: expandGhostAppBuildInfos(d.Get("build_infos").([]interface{})), + EnvironmentInfos: expandGhostAppEnvironmentInfos(d.Get("environment_infos").([]interface{})), + LifecycleHooks: expandGhostAppLifecycleHooks(d.Get("lifecycle_hooks").([]interface{})), LogNotifications: expandGhostAppStringList(d.Get("log_notifications").([]interface{})), - EnvironmentVariables: expandGhostAppEnvironmentVariables(d), + EnvironmentVariables: expandGhostAppEnvironmentVariables(d.Get("environment_variables").([]interface{})), } return app } // Get modules from TF configuration -func expandGhostAppModules(d *schema.ResourceData) *[]ghost.Module { - configs := d.Get("modules").([]interface{}) +func expandGhostAppModules(d []interface{}) *[]ghost.Module { modules := &[]ghost.Module{} // Add each module to modules list - for _, config := range configs { + for _, config := range d { data := config.(map[string]interface{}) module := ghost.Module{ Name: data["name"].(string), @@ -543,11 +542,10 @@ func expandGhostAppModules(d *schema.ResourceData) *[]ghost.Module { } // Get environment variables from TF configuration -func expandGhostAppEnvironmentVariables(d *schema.ResourceData) *[]ghost.EnvironmentVariable { - configs := d.Get("environment_variables").([]interface{}) +func expandGhostAppEnvironmentVariables(d []interface{}) *[]ghost.EnvironmentVariable { environmentVariables := &[]ghost.EnvironmentVariable{} - for _, config := range configs { + for _, config := range d { data := config.(map[string]interface{}) environmentVariable := ghost.EnvironmentVariable{ Key: data["key"].(string), @@ -561,12 +559,11 @@ func expandGhostAppEnvironmentVariables(d *schema.ResourceData) *[]ghost.Environ } // Get autoscale from TF configuration -func expandGhostAppAutoscale(d *schema.ResourceData) *ghost.Autoscale { - config := d.Get("autoscale").([]interface{}) - if len(config) == 0 { +func expandGhostAppAutoscale(d []interface{}) *ghost.Autoscale { + if len(d) == 0 { return nil } - data := config[0].(map[string]interface{}) + data := d[0].(map[string]interface{}) autoscale := &ghost.Autoscale{ Name: data["name"].(string), @@ -579,12 +576,11 @@ func expandGhostAppAutoscale(d *schema.ResourceData) *ghost.Autoscale { } // Get lifecycle_hooks from TF configuration -func expandGhostAppLifecycleHooks(d *schema.ResourceData) *ghost.LifecycleHooks { - config := d.Get("lifecycle_hooks").([]interface{}) - if len(config) == 0 { +func expandGhostAppLifecycleHooks(d []interface{}) *ghost.LifecycleHooks { + if len(d) == 0 { return nil } - data := config[0].(map[string]interface{}) + data := d[0].(map[string]interface{}) lifecycleHooks := &ghost.LifecycleHooks{ PreBuildimage: StrToB64(data["pre_buildimage"].(string)), @@ -597,11 +593,10 @@ func expandGhostAppLifecycleHooks(d *schema.ResourceData) *ghost.LifecycleHooks } // Get features from TF configuration -func expandGhostAppFeatures(d *schema.ResourceData) *[]ghost.Feature { - configs := d.Get("features").([]interface{}) +func expandGhostAppFeatures(d []interface{}) *[]ghost.Feature { features := &[]ghost.Feature{} - for _, config := range configs { + for _, config := range d { data := config.(map[string]interface{}) feature := ghost.Feature{ Name: data["name"].(string), @@ -616,9 +611,8 @@ func expandGhostAppFeatures(d *schema.ResourceData) *[]ghost.Feature { } // Get build_infos from TF configuration -func expandGhostAppBuildInfos(d *schema.ResourceData) *ghost.BuildInfos { - config := d.Get("build_infos").([]interface{}) - data := config[0].(map[string]interface{}) +func expandGhostAppBuildInfos(d []interface{}) *ghost.BuildInfos { + data := d[0].(map[string]interface{}) buildInfos := &ghost.BuildInfos{ SshUsername: data["ssh_username"].(string), @@ -631,9 +625,8 @@ func expandGhostAppBuildInfos(d *schema.ResourceData) *ghost.BuildInfos { } // Get environment_infos from TF configuration -func expandGhostAppEnvironmentInfos(d *schema.ResourceData) *ghost.EnvironmentInfos { - config := d.Get("environment_infos").([]interface{}) - data := config[0].(map[string]interface{}) +func expandGhostAppEnvironmentInfos(d []interface{}) *ghost.EnvironmentInfos { + data := d[0].(map[string]interface{}) environmentInfos := &ghost.EnvironmentInfos{ InstanceProfile: data["instance_profile"].(string), diff --git a/ghost/resource_ghost_app_test.go b/ghost/resource_ghost_app_test.go index 81c2bd3..3453a91 100644 --- a/ghost/resource_ghost_app_test.go +++ b/ghost/resource_ghost_app_test.go @@ -3,6 +3,7 @@ package ghost import ( "fmt" "log" + "reflect" "testing" "cloud-deploy.io/go-st" @@ -141,3 +142,420 @@ func testAccGhostAppConfig(name string) string { } `, name) } + +var ( + app = ghost.App{ + Name: "app_name", + Env: "test", + Role: "web", + Region: "us-west-1", + InstanceType: "t2.micro", + VpcID: "vpc-123456", + InstanceMonitoring: false, + + Modules: &[]ghost.Module{{ + Name: "my_module", + GitRepo: "https://github.com/test/test.git", + Scope: "system", + Path: "/", + BuildPack: StrToB64("#!/usr/bin/env bash"), + PreDeploy: StrToB64("#!/usr/bin/env bash"), + }}, + Features: &[]ghost.Feature{{ + Name: "feature", + Version: "1.0", + Provisioner: "ansible", + }}, + Autoscale: &ghost.Autoscale{ + Name: "autoscale", + EnableMetrics: false, + Min: 0, + Max: 3, + }, + BuildInfos: &ghost.BuildInfos{ + SshUsername: "admin", + SourceAmi: "ami-1", + SubnetID: "subnet-1", + }, + EnvironmentInfos: &ghost.EnvironmentInfos{ + InstanceProfile: "profile", + KeyName: "key", + PublicIpAddress: false, + SecurityGroups: []string{"sg-1", "sg-2"}, + SubnetIDs: []string{"subnet-1", "subnet-2"}, + InstanceTags: &[]ghost.InstanceTag{{ + TagName: "name", + TagValue: "val", + }}, + OptionalVolumes: &[]ghost.OptionalVolume{{ + DeviceName: "my_device", + VolumeType: "gp2", + VolumeSize: 20, + Iops: 3000, + }}, + RootBlockDevice: &ghost.RootBlockDevice{ + Name: "rootblock", + Size: 20, + }, + }, + LifecycleHooks: &ghost.LifecycleHooks{ + PreBuildimage: StrToB64("#!/usr/bin/env bash"), + PostBuildimage: StrToB64("#!/usr/bin/env bash"), + }, + LogNotifications: []string{"log_not@email.com"}, + EnvironmentVariables: &[]ghost.EnvironmentVariable{{ + Key: "env_var_key", + Value: "env_var_value", + }}, + } +) + +// Expanders Unit Tests +func TestExpandGhostAppStringList(t *testing.T) { + cases := []struct { + Input []interface{} + ExpectedOutput []string + }{ + { + []interface{}{ + "1", "2", "3", + }, + []string{ + "1", "2", "3", + }, + }, + { + nil, + nil, + }, + } + + for _, tc := range cases { + output := expandGhostAppStringList(tc.Input) + if !reflect.DeepEqual(output, tc.ExpectedOutput) { + t.Fatalf("Unexpected output from expander.\nExpected: %#v\nGiven: %#v", + tc.ExpectedOutput, output) + } + } +} + +func TestExpandGhostAppInstanceTags(t *testing.T) { + cases := []struct { + Input []interface{} + ExpectedOutput *[]ghost.InstanceTag + }{ + { + []interface{}{ + map[string]interface{}{ + "tag_name": "name", + "tag_value": "val", + }, + }, + app.EnvironmentInfos.InstanceTags, + }, + { + nil, + &[]ghost.InstanceTag{}, + }, + } + + for _, tc := range cases { + output := expandGhostAppInstanceTags(tc.Input) + if !reflect.DeepEqual(output, tc.ExpectedOutput) { + t.Fatalf("Unexpected output from expander.\nExpected: %#v\nGiven: %#v", + tc.ExpectedOutput, output) + } + } +} + +func TestExpandGhostAppOptionalVolume(t *testing.T) { + cases := []struct { + Input []interface{} + ExpectedOutput *[]ghost.OptionalVolume + }{ + { + []interface{}{ + map[string]interface{}{ + "device_name": "my_device", + "volume_type": "gp2", + "volume_size": 20, + "iops": 3000, + "launch_block_device_mappings": false, + }, + }, + app.EnvironmentInfos.OptionalVolumes, + }, + { + nil, + &[]ghost.OptionalVolume{}, + }, + } + + for _, tc := range cases { + output := expandGhostAppOptionalVolumes(tc.Input) + if !reflect.DeepEqual(output, tc.ExpectedOutput) { + t.Fatalf("Unexpected output from expander.\nExpected: %#v\nGiven: %#v", + tc.ExpectedOutput, output) + } + } +} + +func TestExpandGhostAppRootBlockDevice(t *testing.T) { + cases := []struct { + Input []interface{} + ExpectedOutput *ghost.RootBlockDevice + }{ + { + []interface{}{ + map[string]interface{}{ + "name": "rootblock", + "size": 20, + }, + }, + app.EnvironmentInfos.RootBlockDevice, + }, + { + nil, + nil, + }, + } + + for _, tc := range cases { + output := expandGhostAppRootBlockDevice(tc.Input) + if !reflect.DeepEqual(output, tc.ExpectedOutput) { + t.Fatalf("Unexpected output from expander.\nExpected: %#v\nGiven: %#v", + tc.ExpectedOutput, output) + } + } +} + +func TestExpandGhostAppEnvironmentInfos(t *testing.T) { + cases := []struct { + Input []interface{} + ExpectedOutput *ghost.EnvironmentInfos + }{ + { + []interface{}{ + map[string]interface{}{ + "instance_profile": "profile", + "key_name": "key", + "public_ip_address": false, + "security_groups": []interface{}{"sg-1", "sg-2"}, + "subnet_ids": []interface{}{"subnet-1", "subnet-2"}, + "instance_tags": []interface{}{ + map[string]interface{}{ + "tag_name": "name", + "tag_value": "val", + }, + }, + "optional_volumes": []interface{}{ + map[string]interface{}{ + "device_name": "my_device", + "volume_type": "gp2", + "volume_size": 20, + "iops": 3000, + "launch_block_device_mappings": false, + }, + }, + "root_block_device": []interface{}{ + map[string]interface{}{ + "name": "rootblock", + "size": 20, + }, + }, + }, + }, + app.EnvironmentInfos, + }, + } + + for _, tc := range cases { + output := expandGhostAppEnvironmentInfos(tc.Input) + if !reflect.DeepEqual(output, tc.ExpectedOutput) { + t.Fatalf("Unexpected output from expander.\nExpected: %#v\nGiven: %#v", + tc.ExpectedOutput, output) + } + } +} + +func TestExpandGhostAppBuildInfos(t *testing.T) { + cases := []struct { + Input []interface{} + ExpectedOutput *ghost.BuildInfos + }{ + { + []interface{}{ + map[string]interface{}{ + "ssh_username": "admin", + "source_ami": "ami-1", + "subnet_id": "subnet-1", + "ami_name": "", + }, + }, + app.BuildInfos, + }, + } + + for _, tc := range cases { + output := expandGhostAppBuildInfos(tc.Input) + if !reflect.DeepEqual(output, tc.ExpectedOutput) { + t.Fatalf("Unexpected output from expander.\nExpected: %#v\nGiven: %#v", + tc.ExpectedOutput, output) + } + } +} + +func TestExpandGhostAppFeatures(t *testing.T) { + cases := []struct { + Input []interface{} + ExpectedOutput *[]ghost.Feature + }{ + { + []interface{}{ + map[string]interface{}{ + "name": "feature", + "version": "1.0", + "provisioner": "ansible", + }, + }, + app.Features, + }, + { + nil, + &[]ghost.Feature{}, + }, + } + + for _, tc := range cases { + output := expandGhostAppFeatures(tc.Input) + if !reflect.DeepEqual(output, tc.ExpectedOutput) { + t.Fatalf("Unexpected output from expander.\nExpected: %#v\nGiven: %#v", + tc.ExpectedOutput, output) + } + } +} + +func TestExpandGhostAppLifecycleHooks(t *testing.T) { + cases := []struct { + Input []interface{} + ExpectedOutput *ghost.LifecycleHooks + }{ + { + []interface{}{ + map[string]interface{}{ + "pre_buildimage": "#!/usr/bin/env bash", + "post_buildimage": "#!/usr/bin/env bash", + "pre_bootstrap": "", + "post_bootstrap": "", + }, + }, + app.LifecycleHooks, + }, + { + nil, + nil, + }, + } + + for _, tc := range cases { + output := expandGhostAppLifecycleHooks(tc.Input) + if !reflect.DeepEqual(output, tc.ExpectedOutput) { + t.Fatalf("Unexpected output from expander.\nExpected: %#v\nGiven: %#v", + tc.ExpectedOutput, output) + } + } +} + +func TestExpandGhostAppAutoscale(t *testing.T) { + cases := []struct { + Input []interface{} + ExpectedOutput *ghost.Autoscale + }{ + { + []interface{}{ + map[string]interface{}{ + "name": "autoscale", + "enable_metrics": false, + "min": 0, + "max": 3, + }, + }, + app.Autoscale, + }, + { + nil, + nil, + }, + } + + for _, tc := range cases { + output := expandGhostAppAutoscale(tc.Input) + if !reflect.DeepEqual(output, tc.ExpectedOutput) { + t.Fatalf("Unexpected output from expander.\nExpected: %#v\nGiven: %#v", + tc.ExpectedOutput, output) + } + } +} + +func TestExpandGhostAppEnvironmentVariables(t *testing.T) { + cases := []struct { + Input []interface{} + ExpectedOutput *[]ghost.EnvironmentVariable + }{ + { + []interface{}{ + map[string]interface{}{ + "key": "env_var_key", + "value": "env_var_value", + }, + }, + app.EnvironmentVariables, + }, + { + nil, + &[]ghost.EnvironmentVariable{}, + }, + } + + for _, tc := range cases { + output := expandGhostAppEnvironmentVariables(tc.Input) + if !reflect.DeepEqual(output, tc.ExpectedOutput) { + t.Fatalf("Unexpected output from expander.\nExpected: %#v\nGiven: %#v", + tc.ExpectedOutput, output) + } + } +} + +func TestExpandGhostAppModules(t *testing.T) { + cases := []struct { + Input []interface{} + ExpectedOutput *[]ghost.Module + }{ + { + []interface{}{ + map[string]interface{}{ + "name": "my_module", + "git_repo": "https://github.com/test/test.git", + "path": "/", + "scope": "system", + "build_pack": "#!/usr/bin/env bash", + "pre_deploy": "#!/usr/bin/env bash", + "post_deploy": "", + "after_all_deploy": "", + "uid": 0, + "gid": 0, + "last_deployment": "", + }, + }, + app.Modules, + }, + } + + for _, tc := range cases { + output := expandGhostAppModules(tc.Input) + if !reflect.DeepEqual(output, tc.ExpectedOutput) { + t.Fatalf("Unexpected output from expander.\nExpected: %#v\nGiven: %#v", + tc.ExpectedOutput, output) + } + } +} From 3c696762fff8b73f4e87b71ef7e8e1378daa41b8 Mon Sep 17 00:00:00 2001 From: Jordan Caussat Date: Fri, 23 Mar 2018 17:35:33 +0100 Subject: [PATCH 20/21] TER-235: Add helpers tests --- ghost/helpers_test.go | 60 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 ghost/helpers_test.go diff --git a/ghost/helpers_test.go b/ghost/helpers_test.go new file mode 100644 index 0000000..4d70853 --- /dev/null +++ b/ghost/helpers_test.go @@ -0,0 +1,60 @@ +package ghost + +import ( + "reflect" + "testing" +) + +func TestStrToB64(t *testing.T) { + cases := []struct { + Input string + ExpectedOutput string + }{ + {"mystring", "bXlzdHJpbmc="}, + {"", ""}, + } + + for _, tc := range cases { + output := StrToB64(tc.Input) + if !reflect.DeepEqual(output, tc.ExpectedOutput) { + t.Fatalf("Unexpected output from StrToB64.\nExpected: %#v\nGiven: %#v", + tc.ExpectedOutput, output) + } + } +} + +func TestB64ToStr(t *testing.T) { + cases := []struct { + Input string + ExpectedOutput string + }{ + {"bXlzdHJpbmc=", "mystring"}, + {"", ""}, + } + + for _, tc := range cases { + output := B64ToStr(tc.Input) + if !reflect.DeepEqual(output, tc.ExpectedOutput) { + t.Fatalf("Unexpected output from B64ToStr.\nExpected: %#v\nGiven: %#v", + tc.ExpectedOutput, output) + } + } +} + +func TestMatchesRegexp(t *testing.T) { + cases := []struct { + Function func(v interface{}, k string) (ws []string, errors []error) + Value string + Valid bool + }{ + {MatchesRegexp(`^[a-zA-Z0-9]*$`), "thisIsAPositiveTest", true}, + {MatchesRegexp(`^[a-zA-Z0-9]*$`), "thisIsANegativeTest-", false}, + } + + for _, tc := range cases { + _, err := tc.Function(tc.Value, tc.Value) + if (tc.Valid && (err != nil)) || (!tc.Valid && (err == nil)) { + t.Fatalf("Unexpected output from MatchesRegexp: %v", err) + } + } +} From 45d2a08a8a43b32d40c5ef4e01db49323442978e Mon Sep 17 00:00:00 2001 From: Jordan Caussat Date: Mon, 26 Mar 2018 18:40:17 +0200 Subject: [PATCH 21/21] TER-235: Add b64toStr error test cases --- ghost/helpers_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ghost/helpers_test.go b/ghost/helpers_test.go index 4d70853..45bf8ee 100644 --- a/ghost/helpers_test.go +++ b/ghost/helpers_test.go @@ -30,6 +30,8 @@ func TestB64ToStr(t *testing.T) { }{ {"bXlzdHJpbmc=", "mystring"}, {"", ""}, + {"-1", ""}, + {"()", ""}, } for _, tc := range cases {