Skip to content

Commit

Permalink
Add a kubernetes style resource async class
Browse files Browse the repository at this point in the history
Signed-off-by: Modular Magician <[email protected]>
  • Loading branch information
chrisst authored and modular-magician committed Nov 27, 2019
1 parent 4beb716 commit 9c2dc9e
Show file tree
Hide file tree
Showing 60 changed files with 421 additions and 173 deletions.
116 changes: 116 additions & 0 deletions google/cloudrun_polling.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package google

import (
"errors"
"fmt"

"time"

"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
)

const readyStatus string = "Ready"

type Condition struct {
Type string
Status string
Reason string
Message string
}

// KnativeStatus is a struct that can contain a Knative style resource's Status block. It is not
// intended to be used for anything other than polling for the success of the given resource.
type KnativeStatus struct {
Metadata struct {
Name string
Namespace string
SelfLink string
}
Status struct {
Conditions []Condition
}
}

// ConditionByType is a helper method for extracting a given condition
func (s KnativeStatus) ConditionByType(typ string) *Condition {
for _, condition := range s.Status.Conditions {
if condition.Type == typ {
c := condition
return &c
}
}
return nil
}

// LatestMessage will return a human consumable status of the resource. This can
// be used to determine the human actionable error the GET doesn't return an explicit
// error but the resource is in an error state.
func (s KnativeStatus) LatestMessage() string {
c := s.ConditionByType(readyStatus)
if c != nil {
return fmt.Sprintf("%s - %s", c.Reason, c.Message)
}

return ""
}

// State will return a string representing the status of the Ready condition.
// No other conditions are currently returned as part of the state.
func (s KnativeStatus) State(res interface{}) string {
for _, condition := range s.Status.Conditions {
if condition.Type == "Ready" {
return fmt.Sprintf("%s:%s", condition.Type, condition.Status)
}
}
return "Empty"
}

// CloudRunPolling allows for polling against a cloud run resource that implements the
// Kubernetes style status schema.
type CloudRunPolling struct {
Config *Config
WaitURL string
}

func (p *CloudRunPolling) PendingStates() []string {
return []string{"Ready:Unknown", "Empty"}
}
func (p *CloudRunPolling) TargetStates() []string {
return []string{"Ready:True"}
}
func (p *CloudRunPolling) ErrorStates() []string {
return []string{"Ready:False"}
}

func cloudRunPollingWaitTime(config *Config, res map[string]interface{}, project, url, activity string, timeoutMinutes int) error {
w := &CloudRunPolling{}

scc := &resource.StateChangeConf{
Pending: w.PendingStates(),
Target: w.TargetStates(),
Refresh: func() (interface{}, string, error) {
res, err := sendRequest(config, "GET", project, url, nil)
if err != nil {
return res, "", err
}

status := KnativeStatus{}
err = Convert(res, &status)
if err != nil {
return res, "", err
}

for _, errState := range w.ErrorStates() {
if status.State(res) == errState {
err = errors.New(status.LatestMessage())
}
}

return res, status.State(res), err
},
Timeout: time.Duration(timeoutMinutes) * time.Minute,
}

_, err := scc.WaitForState()
return err
}
6 changes: 3 additions & 3 deletions google/resource_access_context_manager_access_level.go
Original file line number Diff line number Diff line change
Expand Up @@ -279,14 +279,14 @@ func resourceAccessContextManagerAccessLevelCreate(d *schema.ResourceData, meta
}
d.SetId(id)

waitErr := accessContextManagerOperationWaitTime(
err = accessContextManagerOperationWaitTime(
config, res, "Creating AccessLevel",
int(d.Timeout(schema.TimeoutCreate).Minutes()))

if waitErr != nil {
if err != nil {
// The resource didn't actually create
d.SetId("")
return fmt.Errorf("Error waiting to create AccessLevel: %s", waitErr)
return fmt.Errorf("Error waiting to create AccessLevel: %s", err)
}

log.Printf("[DEBUG] Finished creating AccessLevel %q: %#v", d.Id(), res)
Expand Down
6 changes: 3 additions & 3 deletions google/resource_access_context_manager_access_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,14 +108,14 @@ func resourceAccessContextManagerAccessPolicyCreate(d *schema.ResourceData, meta
}
d.SetId(id)

waitErr := accessContextManagerOperationWaitTime(
err = accessContextManagerOperationWaitTime(
config, res, "Creating AccessPolicy",
int(d.Timeout(schema.TimeoutCreate).Minutes()))

if waitErr != nil {
if err != nil {
// The resource didn't actually create
d.SetId("")
return fmt.Errorf("Error waiting to create AccessPolicy: %s", waitErr)
return fmt.Errorf("Error waiting to create AccessPolicy: %s", err)
}

log.Printf("[DEBUG] Finished creating AccessPolicy %q: %#v", d.Id(), res)
Expand Down
6 changes: 3 additions & 3 deletions google/resource_access_context_manager_service_perimeter.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,14 +224,14 @@ func resourceAccessContextManagerServicePerimeterCreate(d *schema.ResourceData,
}
d.SetId(id)

waitErr := accessContextManagerOperationWaitTime(
err = accessContextManagerOperationWaitTime(
config, res, "Creating ServicePerimeter",
int(d.Timeout(schema.TimeoutCreate).Minutes()))

if waitErr != nil {
if err != nil {
// The resource didn't actually create
d.SetId("")
return fmt.Errorf("Error waiting to create ServicePerimeter: %s", waitErr)
return fmt.Errorf("Error waiting to create ServicePerimeter: %s", err)
}

log.Printf("[DEBUG] Finished creating ServicePerimeter %q: %#v", d.Id(), res)
Expand Down
6 changes: 3 additions & 3 deletions google/resource_app_engine_application_url_dispatch_rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,14 +119,14 @@ func resourceAppEngineApplicationUrlDispatchRulesCreate(d *schema.ResourceData,
}
d.SetId(id)

waitErr := appEngineOperationWaitTime(
err = appEngineOperationWaitTime(
config, res, project, "Creating ApplicationUrlDispatchRules",
int(d.Timeout(schema.TimeoutCreate).Minutes()))

if waitErr != nil {
if err != nil {
// The resource didn't actually create
d.SetId("")
return fmt.Errorf("Error waiting to create ApplicationUrlDispatchRules: %s", waitErr)
return fmt.Errorf("Error waiting to create ApplicationUrlDispatchRules: %s", err)
}

log.Printf("[DEBUG] Finished creating ApplicationUrlDispatchRules %q: %#v", d.Id(), res)
Expand Down
6 changes: 3 additions & 3 deletions google/resource_app_engine_domain_mapping.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,14 +189,14 @@ func resourceAppEngineDomainMappingCreate(d *schema.ResourceData, meta interface
}
d.SetId(id)

waitErr := appEngineOperationWaitTime(
err = appEngineOperationWaitTime(
config, res, project, "Creating DomainMapping",
int(d.Timeout(schema.TimeoutCreate).Minutes()))

if waitErr != nil {
if err != nil {
// The resource didn't actually create
d.SetId("")
return fmt.Errorf("Error waiting to create DomainMapping: %s", waitErr)
return fmt.Errorf("Error waiting to create DomainMapping: %s", err)
}

log.Printf("[DEBUG] Finished creating DomainMapping %q: %#v", d.Id(), res)
Expand Down
6 changes: 3 additions & 3 deletions google/resource_app_engine_standard_app_version.go
Original file line number Diff line number Diff line change
Expand Up @@ -397,14 +397,14 @@ func resourceAppEngineStandardAppVersionCreate(d *schema.ResourceData, meta inte
}
d.SetId(id)

waitErr := appEngineOperationWaitTime(
err = appEngineOperationWaitTime(
config, res, project, "Creating StandardAppVersion",
int(d.Timeout(schema.TimeoutCreate).Minutes()))

if waitErr != nil {
if err != nil {
// The resource didn't actually create
d.SetId("")
return fmt.Errorf("Error waiting to create StandardAppVersion: %s", waitErr)
return fmt.Errorf("Error waiting to create StandardAppVersion: %s", err)
}

log.Printf("[DEBUG] Finished creating StandardAppVersion %q: %#v", d.Id(), res)
Expand Down
32 changes: 29 additions & 3 deletions google/resource_cloud_run_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ func resourceCloudRunService() *schema.Resource {
},

Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(4 * time.Minute),
Update: schema.DefaultTimeout(4 * time.Minute),
Create: schema.DefaultTimeout(6 * time.Minute),
Update: schema.DefaultTimeout(6 * time.Minute),
Delete: schema.DefaultTimeout(4 * time.Minute),
},

Expand Down Expand Up @@ -614,6 +614,19 @@ func resourceCloudRunServiceCreate(d *schema.ResourceData, meta interface{}) err
}
d.SetId(id)

waitURL, err := replaceVars(d, config, "{{CloudRunBasePath}}serving.knative.dev/v1/namespaces/{{project}}/services/{{name}}")
if err != nil {
return err
}

err = cloudRunPollingWaitTime(
config, res, project, waitURL, "Creating Service",
int(d.Timeout(schema.TimeoutCreate).Minutes()))

if err != nil {
return fmt.Errorf("Error waiting to create Service: %s", err)
}

log.Printf("[DEBUG] Finished creating Service %q: %#v", d.Id(), res)

return resourceCloudRunServiceRead(d, meta)
Expand Down Expand Up @@ -705,12 +718,25 @@ func resourceCloudRunServiceUpdate(d *schema.ResourceData, meta interface{}) err
}

log.Printf("[DEBUG] Updating Service %q: %#v", d.Id(), obj)
_, err = sendRequestWithTimeout(config, "PUT", project, url, obj, d.Timeout(schema.TimeoutUpdate))
res, err := sendRequestWithTimeout(config, "PUT", project, url, obj, d.Timeout(schema.TimeoutUpdate))

if err != nil {
return fmt.Errorf("Error updating Service %q: %s", d.Id(), err)
}

waitURL, err := replaceVars(d, config, "{{CloudRunBasePath}}serving.knative.dev/v1/namespaces/{{project}}/services/{{name}}")
if err != nil {
return err
}

err = cloudRunPollingWaitTime(
config, res, project, waitURL, "Updating Service",
int(d.Timeout(schema.TimeoutUpdate).Minutes()))

if err != nil {
return err
}

return resourceCloudRunServiceRead(d, meta)
}

Expand Down
101 changes: 101 additions & 0 deletions google/resource_cloud_run_service_generated_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// ----------------------------------------------------------------------------
//
// *** AUTO GENERATED CODE *** AUTO GENERATED CODE ***
//
// ----------------------------------------------------------------------------
//
// This file is automatically generated by Magic Modules and manual
// changes will be clobbered when the file is regenerated.
//
// Please read more about how to change this file in
// .github/CONTRIBUTING.md.
//
// ----------------------------------------------------------------------------

package google

import (
"fmt"
"strings"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/terraform"
)

func TestAccCloudRunService_cloudRunServiceBasicExample(t *testing.T) {
t.Parallel()

context := map[string]interface{}{
"namespace": getTestProjectFromEnv(),
"random_suffix": acctest.RandString(10),
}

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckCloudRunServiceDestroy,
Steps: []resource.TestStep{
{
Config: testAccCloudRunService_cloudRunServiceBasicExample(context),
},
{
ResourceName: "google_cloud_run_service.default",
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func testAccCloudRunService_cloudRunServiceBasicExample(context map[string]interface{}) string {
return Nprintf(`
resource "google_cloud_run_service" "default" {
name = "tftest-cloudrun%{random_suffix}"
location = "us-central1"
metadata {
namespace = "%{namespace}"
}
template {
spec {
containers {
image = "gcr.io/cloudrun/hello"
}
}
}
traffic {
percent = 100
latest_revision = true
}
}
`, context)
}

func testAccCheckCloudRunServiceDestroy(s *terraform.State) error {
for name, rs := range s.RootModule().Resources {
if rs.Type != "google_cloud_run_service" {
continue
}
if strings.HasPrefix(name, "data.") {
continue
}

config := testAccProvider.Meta().(*Config)

url, err := replaceVarsForTest(config, rs, "{{CloudRunBasePath}}serving.knative.dev/v1/namespaces/{{project}}/services/{{name}}")
if err != nil {
return err
}

_, err = sendRequest(config, "GET", "", url, nil)
if err == nil {
return fmt.Errorf("CloudRunService still exists at %s", url)
}
}

return nil
}
Loading

0 comments on commit 9c2dc9e

Please sign in to comment.