Skip to content

Commit

Permalink
google_app_engine_service_split_traffic resource (hashicorp#2269) (ha…
Browse files Browse the repository at this point in the history
…shicorp#1785)

* Appengine Service resource

* Added custom_create in custom_code.
* Added appengine service resource

* Updates based on review

* Updates based on review

* update based on review

* Updates

* Update

* Update

* Updated the example

* Updated the example and website link

* Skip Delete on test and noop

* Rebased and merged the appengine terraform.yaml

* Updates

* Fixes

* updated the example and excluded split from reading

* updated the example

* Reverted custom_create related code

* Reverted resource.erb

* updates

* removed the error check

* Updates

* Fixes

* Fixed the import in test script

* Updates as per review

* chanded the operation to OpAsync

* Updated the Objects from Async to OpAsync

* Fixed the example primary resource name

* Testing by removing id_format and import_format

* reverted import and id format and also the example

* Fixed the id_format

* Updates based on review

* Typo fix

* Changed url_param_only to api_name

* Updated the resource.erb from upstream/master

Signed-off-by: Modular Magician <[email protected]>
  • Loading branch information
modular-magician authored Feb 20, 2020
1 parent e1a54e6 commit 64e1b49
Show file tree
Hide file tree
Showing 6 changed files with 650 additions and 2 deletions.
3 changes: 3 additions & 0 deletions .changelog/2269.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:new-resource
`google_app_engine_service_split_traffic`
```
5 changes: 3 additions & 2 deletions google-beta/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -556,9 +556,9 @@ func Provider() terraform.ResourceProvider {
return provider
}

// Generated resources: 119
// Generated resources: 120
// Generated IAM resources: 54
// Total generated resources: 173
// Total generated resources: 174
func ResourceMap() map[string]*schema.Resource {
resourceMap, _ := ResourceMapWithErrors()
return resourceMap
Expand All @@ -575,6 +575,7 @@ func ResourceMapWithErrors() (map[string]*schema.Resource, error) {
"google_app_engine_firewall_rule": resourceAppEngineFirewallRule(),
"google_app_engine_standard_app_version": resourceAppEngineStandardAppVersion(),
"google_app_engine_application_url_dispatch_rules": resourceAppEngineApplicationUrlDispatchRules(),
"google_app_engine_service_split_traffic": resourceAppEngineServiceSplitTraffic(),
"google_bigquery_dataset": resourceBigQueryDataset(),
"google_bigquery_data_transfer_config": resourceBigqueryDataTransferConfig(),
"google_bigtable_app_profile": resourceBigtableAppProfile(),
Expand Down
320 changes: 320 additions & 0 deletions google-beta/resource_app_engine_service_split_traffic.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,320 @@
// ----------------------------------------------------------------------------
//
// *** 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"
"log"
"reflect"
"strings"
"time"

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

func resourceAppEngineServiceSplitTraffic() *schema.Resource {
return &schema.Resource{
Create: resourceAppEngineServiceSplitTrafficCreate,
Read: resourceAppEngineServiceSplitTrafficRead,
Update: resourceAppEngineServiceSplitTrafficUpdate,
Delete: resourceAppEngineServiceSplitTrafficDelete,

Importer: &schema.ResourceImporter{
State: resourceAppEngineServiceSplitTrafficImport,
},

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

Schema: map[string]*schema.Schema{
"service": {
Type: schema.TypeString,
Required: true,
Description: `The name of the service these settings apply to.`,
},
"split": {
Type: schema.TypeList,
Required: true,
Description: `Mapping that defines fractional HTTP traffic diversion to different versions within the service.`,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"allocations": {
Type: schema.TypeMap,
Required: true,
Description: `Mapping from version IDs within the service to fractional (0.000, 1] allocations of traffic for that version. Each version can be specified only once, but some versions in the service may not have any traffic allocation. Services that have traffic allocated cannot be deleted until either the service is deleted or their traffic allocation is removed. Allocations must sum to 1. Up to two decimal place precision is supported for IP-based splits and up to three decimal places is supported for cookie-based splits.`,
Elem: &schema.Schema{Type: schema.TypeString},
},
"shard_by": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.StringInSlice([]string{"UNSPECIFIED", "COOKIE", "IP", "RANDOM", ""}, false),
Description: `Mechanism used to determine which version a request is sent to. The traffic selection algorithm will be stable for either type until allocations are changed.`,
},
},
},
},
"migrate_traffic": {
Type: schema.TypeBool,
Optional: true,
Description: `If set to true traffic will be migrated to this version.`,
},
"project": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
},
},
}
}

func resourceAppEngineServiceSplitTrafficCreate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)

obj := make(map[string]interface{})
idProp, err := expandAppEngineServiceSplitTrafficService(d.Get("service"), d, config)
if err != nil {
return err
} else if v, ok := d.GetOkExists("service"); !isEmptyValue(reflect.ValueOf(idProp)) && (ok || !reflect.DeepEqual(v, idProp)) {
obj["id"] = idProp
}
splitProp, err := expandAppEngineServiceSplitTrafficSplit(d.Get("split"), d, config)
if err != nil {
return err
} else if v, ok := d.GetOkExists("split"); !isEmptyValue(reflect.ValueOf(splitProp)) && (ok || !reflect.DeepEqual(v, splitProp)) {
obj["split"] = splitProp
}

lockName, err := replaceVars(d, config, "apps/{{project}}")
if err != nil {
return err
}
mutexKV.Lock(lockName)
defer mutexKV.Unlock(lockName)

url, err := replaceVars(d, config, "{{AppEngineBasePath}}apps/{{project}}/services/{{service}}?migrateTraffic={{migrate_traffic}}&updateMask=split")
if err != nil {
return err
}

log.Printf("[DEBUG] Creating new ServiceSplitTraffic: %#v", obj)
project, err := getProject(d, config)
if err != nil {
return err
}
res, err := sendRequestWithTimeout(config, "PATCH", project, url, obj, d.Timeout(schema.TimeoutCreate))
if err != nil {
return fmt.Errorf("Error creating ServiceSplitTraffic: %s", err)
}

// Store the ID now
id, err := replaceVars(d, config, "apps/{{project}}/services/{{service}}")
if err != nil {
return fmt.Errorf("Error constructing id: %s", err)
}
d.SetId(id)

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

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

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

return resourceAppEngineServiceSplitTrafficRead(d, meta)
}

func resourceAppEngineServiceSplitTrafficRead(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)

url, err := replaceVars(d, config, "{{AppEngineBasePath}}apps/{{project}}/services/{{service}}")
if err != nil {
return err
}

project, err := getProject(d, config)
if err != nil {
return err
}
res, err := sendRequest(config, "GET", project, url, nil)
if err != nil {
return handleNotFoundError(err, d, fmt.Sprintf("AppEngineServiceSplitTraffic %q", d.Id()))
}

if err := d.Set("project", project); err != nil {
return fmt.Errorf("Error reading ServiceSplitTraffic: %s", err)
}

if err := d.Set("service", flattenAppEngineServiceSplitTrafficService(res["id"], d, config)); err != nil {
return fmt.Errorf("Error reading ServiceSplitTraffic: %s", err)
}

return nil
}

func resourceAppEngineServiceSplitTrafficUpdate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)

project, err := getProject(d, config)
if err != nil {
return err
}

obj := make(map[string]interface{})
idProp, err := expandAppEngineServiceSplitTrafficService(d.Get("service"), d, config)
if err != nil {
return err
} else if v, ok := d.GetOkExists("service"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, idProp)) {
obj["id"] = idProp
}
splitProp, err := expandAppEngineServiceSplitTrafficSplit(d.Get("split"), d, config)
if err != nil {
return err
} else if v, ok := d.GetOkExists("split"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, splitProp)) {
obj["split"] = splitProp
}

lockName, err := replaceVars(d, config, "apps/{{project}}")
if err != nil {
return err
}
mutexKV.Lock(lockName)
defer mutexKV.Unlock(lockName)

url, err := replaceVars(d, config, "{{AppEngineBasePath}}apps/{{project}}/services/{{service}}?migrateTraffic={{migrate_traffic}}")
if err != nil {
return err
}

log.Printf("[DEBUG] Updating ServiceSplitTraffic %q: %#v", d.Id(), obj)
updateMask := []string{}

if d.HasChange("service") {
updateMask = append(updateMask, "id")
}

if d.HasChange("split") {
updateMask = append(updateMask, "split")
}
// updateMask is a URL parameter but not present in the schema, so replaceVars
// won't set it
url, err = addQueryParams(url, map[string]string{"updateMask": strings.Join(updateMask, ",")})
if err != nil {
return err
}
res, err := sendRequestWithTimeout(config, "PATCH", project, url, obj, d.Timeout(schema.TimeoutUpdate))

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

err = appEngineOperationWaitTime(
config, res, project, "Updating ServiceSplitTraffic",
int(d.Timeout(schema.TimeoutUpdate).Minutes()))

if err != nil {
return err
}

return resourceAppEngineServiceSplitTrafficRead(d, meta)
}

func resourceAppEngineServiceSplitTrafficDelete(d *schema.ResourceData, meta interface{}) error {
log.Printf("[WARNING] AppEngine ServiceSplitTraffic resources"+
" cannot be deleted from GCP. The resource %s will be removed from Terraform"+
" state, but will still be present on the server.", d.Id())
d.SetId("")

return nil
}

func resourceAppEngineServiceSplitTrafficImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
config := meta.(*Config)
if err := parseImportId([]string{
"apps/(?P<project>[^/]+)/services/(?P<service>[^/]+)",
"(?P<project>[^/]+)/(?P<service>[^/]+)",
"(?P<service>[^/]+)",
}, d, config); err != nil {
return nil, err
}

// Replace import id for the resource id
id, err := replaceVars(d, config, "apps/{{project}}/services/{{service}}")
if err != nil {
return nil, fmt.Errorf("Error constructing id: %s", err)
}
d.SetId(id)

return []*schema.ResourceData{d}, nil
}

func flattenAppEngineServiceSplitTrafficService(v interface{}, d *schema.ResourceData, config *Config) interface{} {
return v
}

func expandAppEngineServiceSplitTrafficService(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
return v, nil
}

func expandAppEngineServiceSplitTrafficSplit(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
l := v.([]interface{})
if len(l) == 0 || l[0] == nil {
return nil, nil
}
raw := l[0]
original := raw.(map[string]interface{})
transformed := make(map[string]interface{})

transformedShardBy, err := expandAppEngineServiceSplitTrafficSplitShardBy(original["shard_by"], d, config)
if err != nil {
return nil, err
} else if val := reflect.ValueOf(transformedShardBy); val.IsValid() && !isEmptyValue(val) {
transformed["shardBy"] = transformedShardBy
}

transformedAllocations, err := expandAppEngineServiceSplitTrafficSplitAllocations(original["allocations"], d, config)
if err != nil {
return nil, err
} else if val := reflect.ValueOf(transformedAllocations); val.IsValid() && !isEmptyValue(val) {
transformed["allocations"] = transformedAllocations
}

return transformed, nil
}

func expandAppEngineServiceSplitTrafficSplitShardBy(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
return v, nil
}

func expandAppEngineServiceSplitTrafficSplitAllocations(v interface{}, d TerraformResourceData, config *Config) (map[string]string, error) {
if v == nil {
return map[string]string{}, nil
}
m := make(map[string]string)
for k, val := range v.(map[string]interface{}) {
m[k] = val.(string)
}
return m, nil
}
Loading

0 comments on commit 64e1b49

Please sign in to comment.