Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a kubernetes style resource async class #1409

Merged
merged 1 commit into from
Dec 2, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 116 additions & 0 deletions google-beta/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-beta/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-beta/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
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
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-beta/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-beta/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-beta/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-beta/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