forked from hashicorp/terraform-provider-google-beta
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
google_app_engine_service_split_traffic resource (hashicorp#2269) (ha…
…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
1 parent
e1a54e6
commit 64e1b49
Showing
6 changed files
with
650 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
```release-note:new-resource | ||
`google_app_engine_service_split_traffic` | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
320 changes: 320 additions & 0 deletions
320
google-beta/resource_app_engine_service_split_traffic.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
Oops, something went wrong.