diff --git a/README.md b/README.md index 1bf9383..a7b1155 100644 --- a/README.md +++ b/README.md @@ -182,6 +182,30 @@ resource "apigee_company_app" "helloworld_company_app" { callback_url = "https://www.google.com" } +# Create the shared flow bundle pretty much the same way you create the proxy bundle. +data "archive_file" "sharedflow_bundle" { + type = "zip" + source_dir = "${path.module}/sharedflow_files" + output_path = "${path.module}/sharedflow_files_bundle/sharedflow.zip" +} + +# The Shared Flow +resource "apigee_shared_flow" "helloworld_shared_flow" { + name = "helloworld-sharedflow-terraformed" # The shared flow's name. + bundle = "${data.archive_file.sharedflow_bundle.output_path}" # Apigee APIs require a zip bundle to import a shared flow. + bundle_sha = "${data.archive_file.sharedflow_bundle.output_sha}" # The SHA is used to detect changes for plan/apply. +} + +# A Shared Flow deployment +resource "apigee_shared_flow_deployment" "helloworld_shared_flow_deployment" { + shared_flow_name = "${apigee_shared_flow.helloworld_shared_flow.name}" + org = "${var.org}" + env = "${var.env}" + + # NOTE: revision = "latest" + # will deploy the latest revision of the shared flow + revision = "${apigee_shared_flow.helloworld_shared_flow.revision}" +} ``` ## Contributions diff --git a/apigee/provider.go b/apigee/provider.go index 2356bc9..c53bca9 100644 --- a/apigee/provider.go +++ b/apigee/provider.go @@ -43,14 +43,16 @@ func Provider() terraform.ResourceProvider { }, ResourcesMap: map[string]*schema.Resource{ - "apigee_api_proxy": resourceApiProxy(), - "apigee_api_proxy_deployment": resourceApiProxyDeployment(), - "apigee_company": resourceCompany(), - "apigee_company_app": resourceCompanyApp(), - "apigee_developer": resourceDeveloper(), - "apigee_developer_app": resourceDeveloperApp(), - "apigee_product": resourceProduct(), - "apigee_target_server": resourceTargetServer(), + "apigee_api_proxy": resourceApiProxy(), + "apigee_api_proxy_deployment": resourceApiProxyDeployment(), + "apigee_company": resourceCompany(), + "apigee_company_app": resourceCompanyApp(), + "apigee_developer": resourceDeveloper(), + "apigee_developer_app": resourceDeveloperApp(), + "apigee_product": resourceProduct(), + "apigee_target_server": resourceTargetServer(), + "apigee_shared_flow": resourceSharedFlow(), + "apigee_shared_flow_deployment": resourceSharedFlowDeployment(), }, ConfigureFunc: configureProvider, diff --git a/apigee/resource_shared_flow.go b/apigee/resource_shared_flow.go new file mode 100644 index 0000000..f36829b --- /dev/null +++ b/apigee/resource_shared_flow.go @@ -0,0 +1,169 @@ +package apigee + +import ( + "strings" + + "fmt" + "log" + "strconv" + "time" + + "github.com/gofrs/uuid" + "github.com/hashicorp/terraform/helper/schema" + "github.com/zambien/go-apigee-edge" +) + +func resourceSharedFlow() *schema.Resource { + return &schema.Resource{ + Create: resourceSharedFlowCreate, + Read: resourceSharedFlowRead, + Update: resourceSharedFlowUpdate, + Delete: resourceSharedFlowDelete, + Importer: &schema.ResourceImporter{ + State: resourceSharedFlowImport, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "bundle": { + Type: schema.TypeString, + Required: true, + }, + "bundle_sha": { + Type: schema.TypeString, + Required: true, + }, + //revision_sha is used as a workaround for: https://github.com/hashicorp/terraform/issues/15857 + "revision_sha": { + Type: schema.TypeString, + Computed: true, + }, + "revision": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceSharedFlowCreate(d *schema.ResourceData, meta interface{}) error { + log.Print("[DEBUG] resourceSharedFlowCreate START") + + client := meta.(*apigee.EdgeClient) + + u1, _ := uuid.NewV4() + + sharedFlowRev, _, err := client.SharedFlows.Import(d.Get("name").(string), d.Get("bundle").(string)) + + if err != nil { + log.Printf("[ERROR] resourceSharedFlowCreate error importing shared_flow: %s", err.Error()) + return fmt.Errorf("[ERROR] resourceSharedFlowCreate error importing shared_flow: %s", err.Error()) + } + + d.SetId(u1.String()) + d.Set("name", d.Get("name").(string)) + d.Set("revision", sharedFlowRev.Revision.String()) + d.Set("revision_sha", d.Get("bundle_sha").(string)) + + return resourceSharedFlowRead(d, meta) +} + +func resourceSharedFlowImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + log.Print("[DEBUG] resourceSharedFlowImport START") + + client := meta.(*apigee.EdgeClient) + sharedFlow, _, err := client.SharedFlows.Get(d.Id()) + if err != nil { + return []*schema.ResourceData{}, fmt.Errorf("[DEBUG] resourceSharedFlowImport. Error getting deployment shared flow: %v", err) + } + latestRev := strconv.Itoa(len(sharedFlow.Revisions)) + d.Set("revision", latestRev) + d.Set("name", d.Id()) + return []*schema.ResourceData{d}, nil +} + +func resourceSharedFlowRead(d *schema.ResourceData, meta interface{}) error { + log.Print("[DEBUG] resourceSharedFlowRead START") + + client := meta.(*apigee.EdgeClient) + + u, _, err := client.SharedFlows.Get(d.Get("name").(string)) + if err != nil { + log.Printf("[ERROR] resourceSharedFlowRead error reading shared flows: %s", err.Error()) + if strings.Contains(err.Error(), "404 ") { + log.Printf("[DEBUG] resourceSharedFlowRead 404 encountered. Removing state for shared flow: %#v", d.Get("name").(string)) + d.SetId("") + return nil + } else { + return fmt.Errorf("[ERROR] resourceSharedFlowRead error reading shared flows: %s", err.Error()) + } + } + + latestRev := strconv.Itoa(len(u.Revisions)) + + log.Printf("[DEBUG] resourceSharedFlowRead. revision_sha before: %#v", d.Get("revision_sha").(string)) + d.Set("revision_sha", d.Get("bundle_sha").(string)) + log.Printf("[DEBUG] resourceSharedFlowRead. revision_sha after: %#v", d.Get("revision_sha").(string)) + d.Set("revision", latestRev) + d.Set("name", u.Name) + + return nil +} + +func resourceSharedFlowUpdate(d *schema.ResourceData, meta interface{}) error { + + log.Print("[DEBUG] resourceSharedFlowUpdate START") + + client := meta.(*apigee.EdgeClient) + + if d.HasChange("name") { + log.Printf("[INFO] resourceSharedFlowUpdate name changed to: %#v\n", d.Get("name")) + } + + if d.HasChange("bundle_sha") { + log.Printf("[INFO] resourceSharedFlowUpdate bundle_sha changed to: %#v\n", d.Get("bundle_sha")) + } + + sharedFlowRev, _, err := client.SharedFlows.Import(d.Get("name").(string), d.Get("bundle").(string)) + if err != nil { + log.Printf("[ERROR] resourceSharedFlowUpdate error importing shared flow: %s", err.Error()) + return fmt.Errorf("[ERROR] resourceSharedFlowUpdate error importing shared flow: %s", err.Error()) + } + + d.Set("revision", sharedFlowRev.Revision.String()) + d.Set("revision_sha", d.Get("bundle_sha").(string)) + + return resourceSharedFlowRead(d, meta) +} + +func resourceSharedFlowDelete(d *schema.ResourceData, meta interface{}) error { + + log.Print("[DEBUG] resourceSharedFlowDelete START") + + client := meta.(*apigee.EdgeClient) + + //We have to handle retries in a special way here since this is a DELETE. Note this used to work fine without retries. + deleted := false + tries := 0 + for !deleted && tries < 3 { + _, _, err := client.SharedFlows.Delete(d.Get("name").(string)) + if err != nil { + //This is a race condition with Apigee APIs. Wait and try again. + if strings.Contains(err.Error(), "Undeploy the shared flow and try again") { + log.Printf("[ERROR] resourceSharedFlowDelete shared flow still exists. We will wait and try again.") + time.Sleep(5 * time.Second) + } else { + log.Printf("[ERROR] resourceSharedFlowDelete error deleting shared flow: %s", err.Error()) + return fmt.Errorf("[ERROR] resourceSharedFlowDelete error deleting api_proxshared flowy: %s", err.Error()) + } + } + deleted = true + tries += tries + } + + return nil +} diff --git a/apigee/resource_shared_flow_deployment.go b/apigee/resource_shared_flow_deployment.go new file mode 100644 index 0000000..82b70ce --- /dev/null +++ b/apigee/resource_shared_flow_deployment.go @@ -0,0 +1,257 @@ +package apigee + +import ( + "fmt" + "log" + "strconv" + "strings" + + "github.com/gofrs/uuid" + "github.com/hashicorp/terraform/helper/schema" + "github.com/zambien/go-apigee-edge" +) + +func resourceSharedFlowDeployment() *schema.Resource { + return &schema.Resource{ + Create: resourceSharedFlowDeploymentCreate, + Read: resourceSharedFlowDeploymentRead, + Update: resourceSharedFlowDeploymentUpdate, + Delete: resourceSharedFlowDeploymentDelete, + Importer: &schema.ResourceImporter{ + State: resourceSharedFlowDeploymentImport, + }, + + Schema: map[string]*schema.Schema{ + "shared_flow_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "org": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "env": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "revision": { + Type: schema.TypeString, + Required: true, + }, + "delay": { + Type: schema.TypeInt, + Optional: true, + Default: 0, + }, + "override": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + }, + } +} + +func resourceSharedFlowDeploymentImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + log.Print("[DEBUG] resourceSharedFlowDeploymentImport START") + client := meta.(*apigee.EdgeClient) + + splits := strings.Split(d.Id(), "_") + if len(splits) < 2 { + return []*schema.ResourceData{}, fmt.Errorf("[ERR] Wrong format of resource: %s. Please follow '{name}_{env}_deployment'", d.Id()) + } + nameOffset := len(splits[len(splits)-1]) + len(splits[len(splits)-2]) + envOffset := len(splits[len(splits)-1]) + name := d.Id()[:(len(d.Id())-nameOffset)-2] + IDEnv := d.Id()[len(name)+1 : (len(d.Id())-envOffset)-1] + deployment, _, err := client.SharedFlows.GetDeployments(name) + if err != nil { + log.Printf("[DEBUG] resourceSharedFlowDeploymentImport. Error getting deployment: %v", err) + return nil, nil + } + d.Set("org", deployment.Organization) + d.Set("shared_flow_name", deployment.Name) + d.Set("env", IDEnv) + + return []*schema.ResourceData{d}, nil +} + +func resourceSharedFlowDeploymentRead(d *schema.ResourceData, meta interface{}) (e error) { + log.Print("[DEBUG] resourceSharedFlowDeploymentRead START") + log.Printf("[DEBUG] resourceSharedFlowDeploymentRead shared_flow_name: %#v", d.Get("shared_flow_name").(string)) + + client := meta.(*apigee.EdgeClient) + + found := false + matchedRevision := "0" + + if deployments, _, err := client.SharedFlows.GetDeployments(d.Get("shared_flow_name").(string)); err != nil { + log.Printf("[ERROR] resourceSharedFlowDeploymentRead error getting deployments: %s", err.Error()) + if strings.Contains(err.Error(), "404 ") { + log.Printf("[DEBUG] resourceSharedFlowDeploymentRead 404 encountered. Removing state for deployment shared_flow_name: %#v", d.Get("shared_flow_name").(string)) + d.SetId("") + return nil + } else { + return fmt.Errorf("[ERROR] resourceSharedFlowDeploymentRead error reading deployments: %s", err.Error()) + } + } else { + log.Printf("[DEBUG] resourceSharedFlowDeploymentRead deployments call fired for shared_flow_name: %#v", d.Get("shared_flow_name").(string)) + for _, environment := range deployments.Environments { + log.Printf("[DEBUG] resourceSharedFlowDeploymentRead checking revisions in deployed environment: %#v for expected environment: %#v\n", environment.Name, d.Get("env").(string)) + if environment.Name == d.Get("env").(string) { + //We don't break. Always get the last one if there are multiple deployments. + for _, revision := range environment.Revision { + log.Printf("[DEBUG] resourceSharedFlowDeploymentRead checking deployed revision: %#v for expected revision: %#v\n", revision.Number.String(), d.Get("revision").(string)) + if d.Get("revision").(string) != "latest" && d.Get("revision").(string) == revision.Number.String() { + matchedRevision = revision.Number.String() + found = true + break + } else { + matchedRevision = revision.Number.String() + } + found = true + } + } + } + } + + if found { + log.Printf("[DEBUG] resourceSharedFlowDeploymentRead - deployment found. Revision is: %#v", matchedRevision) + d.Set("revision", matchedRevision) + } else { + log.Print("[DEBUG] resourceSharedFlowDeploymentRead - no deployment found") + d.SetId("") + } + return nil +} + +func resourceSharedFlowDeploymentCreate(d *schema.ResourceData, meta interface{}) error { + + log.Print("[DEBUG] resourceSharedFlowDeploymentCreate START") + + client := meta.(*apigee.EdgeClient) + + sharedFlowName := d.Get("shared_flow_name").(string) + env := d.Get("env").(string) + revInt, _ := strconv.Atoi(d.Get("revision").(string)) + rev := apigee.Revision(revInt) + delay := int(d.Get("delay").(int)) + override := bool(d.Get("override").(bool)) + + if d.Get("revision").(string) == "latest" { + // deploy latest + rev, err := getLatestSharedFlowRevision(client, sharedFlowName) + if err != nil { + return fmt.Errorf("[ERROR] resourceSharedFlowDeploymentCreate error getting latest revision: %v", err) + } + _, _, err = client.SharedFlows.Deploy(sharedFlowName, env, apigee.Revision(rev), delay, override) + if err != nil { + return fmt.Errorf("[ERROR] resourceSharedFlowDeploymentCreate error deploying: %v", err) + } + log.Printf("[DEBUG] resourceSharedFlowDeploymentCreate Deployed revision %d of %s", rev, sharedFlowName) + return resourceSharedFlowDeploymentRead(d, meta) + } + + sharedFlowDep, _, err := client.SharedFlows.Deploy(sharedFlowName, env, rev, delay, override) + + if err != nil { + + if strings.Contains(err.Error(), "conflicts with existing deployment path") { + //create, fail, update + log.Printf("[ERROR] resourceSharedFlowDeploymentCreate error deploying: %s", err.Error()) + log.Print("[DEBUG] resourceSharedFlowDeploymentCreate something got out of sync... maybe someone messing around in apigee directly. Terraform OVERRIDE!!!") + resourceSharedFlowDeploymentUpdate(d, meta) + } else { + log.Printf("[ERROR] resourceSharedFlowDeploymentCreate error deploying: %s", err.Error()) + return fmt.Errorf("[ERROR] resourceSharedFlowDeploymentCreate error deploying: %s", err.Error()) + } + } + + id, _ := uuid.NewV4() + d.SetId(id.String()) + d.Set("revision", sharedFlowDep.Revision.String()) + + return resourceSharedFlowDeploymentRead(d, meta) +} + +func resourceSharedFlowDeploymentUpdate(d *schema.ResourceData, meta interface{}) error { + + log.Print("[DEBUG] resourceSharedFlowDeploymentUpdate START") + + client := meta.(*apigee.EdgeClient) + + sharedFlowName := d.Get("shared_flow_name").(string) + env := d.Get("env").(string) + delay := int(d.Get("delay").(int)) + override := bool(d.Get("override").(bool)) + + //We must set delay and override here if not set. + if delay == 0 { + delay = 15 //seconds + } + if override == false { + override = true + } + + if d.Get("revision").(string) == "latest" { + // deploy latest + rev, err := getLatestSharedFlowRevision(client, sharedFlowName) + if err != nil { + return fmt.Errorf("[ERROR] resourceSharedFlowDeploymentUpdate error getting latest revision: %v", err) + } + _, _, err = client.SharedFlows.ReDeploy(sharedFlowName, env, apigee.Revision(rev), delay, override) + if err != nil { + if strings.Contains(err.Error(), " is already deployed ") { + return resourceSharedFlowDeploymentRead(d, meta) + } + return fmt.Errorf("[ERROR] resourceSharedFlowDeploymentUpdate error deploying: %v", err) + } + log.Printf("[DEBUG] resourceSharedFlowDeploymentUpdate Deployed revision %d of %s", rev, sharedFlowName) + return resourceSharedFlowDeploymentRead(d, meta) + } + + revInt, _ := strconv.Atoi(d.Get("revision").(string)) + rev := apigee.Revision(revInt) + _, _, err := client.SharedFlows.ReDeploy(sharedFlowName, env, rev, delay, override) + + if err != nil { + log.Printf("[ERROR] resourceSharedFlowDeploymentUpdate error redeploying: %s", err.Error()) + if strings.Contains(err.Error(), " is already deployed into environment ") { + return resourceSharedFlowDeploymentRead(d, meta) + } + return fmt.Errorf("[ERROR] resourceSharedFlowDeploymentUpdate error redeploying: %s", err.Error()) + } + + return resourceSharedFlowDeploymentRead(d, meta) +} + +func resourceSharedFlowDeploymentDelete(d *schema.ResourceData, meta interface{}) error { + + log.Print("[DEBUG] resourceSharedFlowDeploymentDelete START") + + client := meta.(*apigee.EdgeClient) + + sharedFlowName := d.Get("shared_flow_name").(string) + env := d.Get("env").(string) + revInt, _ := strconv.Atoi(d.Get("revision").(string)) + rev := apigee.Revision(revInt) + + _, _, err := client.SharedFlows.Undeploy(sharedFlowName, env, rev) + if err != nil { + log.Printf("[ERROR] resourceSharedFlowDeploymentDelete error undeploying: %s", err.Error()) + return fmt.Errorf("[ERROR] resourceSharedFlowDeploymentDelete error undeploying: %s", err.Error()) + } + + return nil +} + +func getLatestSharedFlowRevision(client *apigee.EdgeClient, sharedFlowName string) (int, error) { + sharedFlow, _, err := client.SharedFlows.Get(sharedFlowName) + if err != nil { + return -1, fmt.Errorf("[ERROR] getLatestSharedFlowRevision error reading shared flows: %s", err.Error()) + } + return len(sharedFlow.Revisions), nil +} diff --git a/apigee/resource_shared_flow_deployment_test.go b/apigee/resource_shared_flow_deployment_test.go new file mode 100644 index 0000000..eead39c --- /dev/null +++ b/apigee/resource_shared_flow_deployment_test.go @@ -0,0 +1,146 @@ +package apigee + +import ( + "fmt" + "log" + "strings" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/zambien/go-apigee-edge" +) + +func TestAccSharedFlowDeployment_Updated(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckSharedFlowDeploymentDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccCheckSharedFlowDeploymentConfigRequired, + Check: resource.ComposeTestCheckFunc( + testAccCheckSharedFlowDeploymentExists("apigee_shared_flow_deployment.foo_shared_flow_deployment", "foo_shared_flow_terraformed"), + resource.TestCheckResourceAttr( + "apigee_shared_flow_deployment.foo_shared_flow_deployment", "shared_flow_name", "foo_shared_flow_terraformed"), + resource.TestCheckResourceAttr( + "apigee_shared_flow_deployment.foo_shared_flow_deployment", "org", "zambien-trial"), + resource.TestCheckResourceAttr( + "apigee_shared_flow_deployment.foo_shared_flow_deployment", "revision", "1"), + ), + }, + + resource.TestStep{ + Config: testAccCheckSharedFlowDeploymentConfigUpdated, + Check: resource.ComposeTestCheckFunc( + testAccCheckSharedFlowDeploymentExists("apigee_shared_flow_deployment.foo_shared_flow_deployment", "foo_shared_flow_terraformed"), + resource.TestCheckResourceAttr( + "apigee_shared_flow_deployment.foo_shared_flow_deployment", "shared_flow_name", "foo_shared_flow_terraformed"), + resource.TestCheckResourceAttr( + "apigee_shared_flow_deployment.foo_shared_flow_deployment", "org", "zambien-trial"), + resource.TestCheckResourceAttr( + "apigee_shared_flow_deployment.foo_shared_flow_deployment", "revision", "2"), + resource.TestCheckResourceAttr( + "apigee_shared_flow_deployment.foo_shared_flow_deployment", "delay", "2"), + resource.TestCheckResourceAttr( + "apigee_shared_flow_deployment.foo_shared_flow_deployment", "override", "true"), + ), + }, + }, + }) +} + +func testAccCheckSharedFlowDeploymentDestroy(s *terraform.State) error { + + client := testAccProvider.Meta().(*apigee.EdgeClient) + + if err := sharedFlowDeploymentDestroyHelper(s, client); err != nil { + return err + } + return nil +} + +func testAccCheckSharedFlowDeploymentExists(n string, name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + client := testAccProvider.Meta().(*apigee.EdgeClient) + if err := sharedFlowDeploymentExistsHelper(s, client, name); err != nil { + log.Printf("Error in testAccCheckSharedFlowDeploymentExists: %s", err) + return err + } + return nil + } +} + +const testAccCheckSharedFlowDeploymentConfigRequired = ` +resource "apigee_shared_flow" "foo_shared_flow" { + name = "foo_shared_flow_terraformed" + bundle = "test-fixtures/helloworld_shared_flow.zip" + bundle_sha = "${filebase64sha256("test-fixtures/helloworld_shared_flow.zip")}" +} + +resource "apigee_shared_flow_deployment" "foo_shared_flow_deployment" { + shared_flow_name = apigee_shared_flow.foo_shared_flow.name + org = "zambien-trial" + env = "test" + revision = "1" +} +` + +const testAccCheckSharedFlowDeploymentConfigUpdated = ` +resource "apigee_shared_flow" "foo_shared_flow" { + name = "foo_shared_flow_terraformed" + bundle = "test-fixtures/helloworld_shared_flow2.zip" + bundle_sha = "${filebase64sha256("test-fixtures/helloworld_shared_flow2.zip")}" +} + + +resource "apigee_shared_flow_deployment" "foo_shared_flow_deployment" { + shared_flow_name = apigee_shared_flow.foo_shared_flow.name + org = "zambien-trial" + env = "test" + revision = "2" + delay = "2" + override = true +} +` + +func sharedFlowDeploymentDestroyHelper(s *terraform.State, client *apigee.EdgeClient) error { + + for _, r := range s.RootModule().Resources { + id := r.Primary.ID + + if id == "" { + return fmt.Errorf("No shared flow deployment ID is set") + } + + _, _, err := client.SharedFlows.GetDeployments("foo_shared_flow_deployment") + + if err != nil { + if strings.Contains(err.Error(), "404 ") { + return nil + } + return fmt.Errorf("Received an error retrieving shared flow deployment %+v\n", err) + } + } + + return fmt.Errorf("SharedFlowDeployment still exists") +} + +func sharedFlowDeploymentExistsHelper(s *terraform.State, client *apigee.EdgeClient, name string) error { + + for _, r := range s.RootModule().Resources { + id := r.Primary.ID + + if id == "" { + return fmt.Errorf("No shared flow deployment ID is set") + } + + if sharedFlowDeploymentData, _, err := client.SharedFlows.GetDeployments(name); err != nil { + return fmt.Errorf("Received an error retrieving shared flow deployment %+v\n", sharedFlowDeploymentData) + } else { + log.Printf("Created shared flow deployment name: %s", sharedFlowDeploymentData.Name) + } + + } + return nil +} diff --git a/apigee/resource_shared_flow_test.go b/apigee/resource_shared_flow_test.go new file mode 100644 index 0000000..9de4541 --- /dev/null +++ b/apigee/resource_shared_flow_test.go @@ -0,0 +1,124 @@ +package apigee + +import ( + "fmt" + "log" + "strings" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/zambien/go-apigee-edge" +) + +func TestAccSharedFlow_Updated(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckSharedFlowDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccCheckSharedFlowConfigRequired, + Check: resource.ComposeTestCheckFunc( + testAccCheckSharedFlowExists("apigee_shared_flow.foo_shared_flow", "foo_shared_flow_terraformed"), + resource.TestCheckResourceAttr( + "apigee_shared_flow.foo_shared_flow", "name", "foo_shared_flow_terraformed"), + resource.TestCheckResourceAttr( + "apigee_shared_flow.foo_shared_flow", "bundle", "test-fixtures/helloworld_shared_flow.zip"), + resource.TestCheckResourceAttr( + "apigee_shared_flow.foo_shared_flow", "revision", "1"), + ), + }, + resource.TestStep{ + Config: testAccCheckSharedFlowConfigUpdated, + Check: resource.ComposeTestCheckFunc( + testAccCheckSharedFlowExists("apigee_shared_flow.foo_shared_flow", "foo_shared_flow_terraformed_updated"), + resource.TestCheckResourceAttr( + "apigee_shared_flow.foo_shared_flow", "name", "foo_shared_flow_terraformed_updated"), + resource.TestCheckResourceAttr( + "apigee_shared_flow.foo_shared_flow", "bundle", "test-fixtures/helloworld_shared_flow.zip"), + resource.TestCheckResourceAttr( + "apigee_shared_flow.foo_shared_flow", "revision", "1"), + ), + }, + }, + }) +} + +func testAccCheckSharedFlowDestroy(s *terraform.State) error { + + client := testAccProvider.Meta().(*apigee.EdgeClient) + + if err := sharedFlowDestroyHelper(s, client); err != nil { + return err + } + return nil +} + +func testAccCheckSharedFlowExists(n string, name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + client := testAccProvider.Meta().(*apigee.EdgeClient) + if err := sharedFlowExistsHelper(s, client, name); err != nil { + log.Printf("Error in testAccCheckSharedFlowExists: %s", err) + return err + } + return nil + } +} + +const testAccCheckSharedFlowConfigRequired = ` +resource "apigee_shared_flow" "foo_shared_flow" { + name = "foo_shared_flow_terraformed" + bundle = "test-fixtures/helloworld_shared_flow.zip" + bundle_sha = filebase64sha256("test-fixtures/helloworld_shared_flow.zip") +} +` + +const testAccCheckSharedFlowConfigUpdated = ` +resource "apigee_shared_flow" "foo_shared_flow" { + name = "foo_shared_flow_terraformed_updated" + bundle = "test-fixtures/helloworld_shared_flow.zip" + bundle_sha = filebase64sha256("test-fixtures/helloworld_shared_flow.zip") +} +` + +func sharedFlowDestroyHelper(s *terraform.State, client *apigee.EdgeClient) error { + + for _, r := range s.RootModule().Resources { + id := r.Primary.ID + + if id == "" { + return fmt.Errorf("No shared flow ID is set") + } + + _, _, err := client.SharedFlows.Get("foo_shared_flow") + + if err != nil { + if strings.Contains(err.Error(), "404 ") { + return nil + } + return fmt.Errorf("Received an error retrieving shared flow %+v\n", err) + } + } + + return fmt.Errorf("Shared flow still exists") +} + +func sharedFlowExistsHelper(s *terraform.State, client *apigee.EdgeClient, name string) error { + + for _, r := range s.RootModule().Resources { + id := r.Primary.ID + + if id == "" { + return fmt.Errorf("No shared flow ID is set") + } + + if sharedFlowData, _, err := client.SharedFlows.Get(name); err != nil { + return fmt.Errorf("Received an error retrieving shared flow %+v\n", sharedFlowData) + } else { + log.Printf("Created shared flow name: %s", sharedFlowData.Name) + } + + } + return nil +} diff --git a/apigee/test-fixtures/helloworld_shared_flow.zip b/apigee/test-fixtures/helloworld_shared_flow.zip new file mode 100644 index 0000000..3ed7d6e Binary files /dev/null and b/apigee/test-fixtures/helloworld_shared_flow.zip differ diff --git a/apigee/test-fixtures/helloworld_shared_flow2.zip b/apigee/test-fixtures/helloworld_shared_flow2.zip new file mode 100644 index 0000000..f791cc1 Binary files /dev/null and b/apigee/test-fixtures/helloworld_shared_flow2.zip differ