Skip to content
Open
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
112 changes: 68 additions & 44 deletions mmv1/products/kms/AutokeyConfig.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,66 +26,90 @@
guides:
'Cloud KMS with Autokey': 'https://cloud.google.com/kms/docs/kms-with-autokey'
api: 'https://cloud.google.com/kms/docs/reference/rest/v1/AutokeyConfig'
docs:
id_format: 'folders/{{folder}}/autokeyConfig'
base_url: 'folders/{{folder}}/autokeyConfig'
self_link: 'folders/{{folder}}/autokeyConfig'
# This is a singleton resource that is already created, so create
# is really an update, and therefore should be PATCHed.
create_url: 'folders/{{folder}}/autokeyConfig?updateMask=keyProject'
create_verb: 'PATCH'
update_url: 'folders/{{folder}}/autokeyConfig?updateMask=keyProject'
update_verb: 'PATCH'
delete_url: 'folders/{{folder}}/autokeyConfig?updateMask=keyProject'
delete_verb: 'PATCH'
import_format:
- 'folders/{{folder}}/autokeyConfig'
timeouts:
insert_minutes: 20
update_minutes: 20
delete_minutes: 20
custom_code:
constants: 'templates/terraform/constants/autokey_config_folder_diff.go.tmpl'
pre_create: 'templates/terraform/pre_create/kms_autokey_config_folder.go.tmpl'
pre_read: 'templates/terraform/pre_read/kms_autokey_config_folder.go.tmpl'
pre_update: 'templates/terraform/pre_update/kms_autokey_config_folder.go.tmpl'
pre_delete: 'templates/terraform/pre_delete/kms_autokey_config_folder.go.tmpl'
post_create: 'templates/terraform/post_create/sleep.go.tmpl'
post_update: 'templates/terraform/post_create/sleep.go.tmpl'
test_check_destroy: 'templates/terraform/custom_check_destroy/kms_autokey_config.go.tmpl'
# Using a handwritten sweeper because of pre_delete.
exclude_sweeper: true
include_in_tgc_next: true
tgc_include_handwritten_tests: true
examples:
- name: 'kms_autokey_config_all'
primary_resource_id: 'example-autokeyconfig'
# test depends upon google_project_service_identity service which is still in beta, so we need to keep test limited to beta
min_version: 'beta'
vars:
folder_name: 'folder-cfg'
key_project_name: 'key-proj'
test_env_vars:
org_id: 'ORG_ID'
billing_account: 'BILLING_ACCT'
external_providers: ["random", "time"]

# Base URL now needs to be dynamic
base_url: '{{parent}}/autokeyConfig'
self_link: '{{name}}' # This will be set by the decoder

Check warning on line 32 in mmv1/products/kms/AutokeyConfig.yaml

View workflow job for this annotation

GitHub Actions / lint-yaml

32:23 [comments] too few spaces before comment

parameters:
- name: 'folder'
type: String
description: |
The folder for which to retrieve config.
url_param_only: true
required: true
required: false
immutable: true
conflicts:
- 'project'
diff_suppress_func: 'folderPrefixSuppress'
- name: 'project'
type: String
description: |
The project for which to retrieve config.
url_param_only: true
required: false
immutable: true
conflicts:
- 'folder'
diff_suppress_func: 'projectPrefixSuppress'

properties:
- name: 'name'
type: String
description: 'The resource name for the `AutokeyConfig` in the format `folders/{{folder_id}}/autokeyConfig` or `projects/{{project_id}}/autokeyConfig`.'
output: true
- name: 'keyProject'
type: String
api_name: keyProject
description: |
The target key project for a given folder where KMS Autokey will provision a
CryptoKey for any new KeyHandle the Developer creates. Should have the form
`projects/<project_id_or_number>`.
- name: 'keyProjectResolutionMode'
type: Enum
api_name: keyProjectResolutionMode
description: 'How Autokey determines which project to use when provisioning CMEK keys.'
enum_values:
- 'KEY_PROJECT_RESOLUTION_MODE_UNSPECIFIED'
- 'DEDICATED_KEY_PROJECT'
- 'RESOURCE_PROJECT'
- 'DISABLED'
- name: 'etag'
type: String
description: 'The etag of the AutokeyConfig for optimistic concurrency control.'
output: true

custom_code:
custom_import: templates/terraform/custom_import/kms_autokey_config.go.erb
encoder: templates/terraform/encoders/kms_autokey_config_update.go.erb
decoder: templates/terraform/decoders/kms_autokey_config.go.erb
custom_update: templates/terraform/custom_update/kms_autokey_config.go.erb
custom_create: templates/terraform/custom_create/kms_autokey_config.go.erb
pre_delete: templates/terraform/pre_delete/kms_autokey_config.go.erb
test_check_destroy: 'templates/terraform/custom_check_destroy/kms_autokey_config.go.tmpl'
constants: 'templates/terraform/constants/autokey_config_folder_diff.go.tmpl'

timeouts:
insert_minutes: 20
update_minutes: 20
delete_minutes: 20

# Removed static create_url, update_url, delete_url, id_format, self_link from the top level
# These are now dynamic and handled by custom code.
exclude_sweeper: true
examples:
- name: 'kms_autokey_config_folder' # Example for folder

Check warning on line 101 in mmv1/products/kms/AutokeyConfig.yaml

View workflow job for this annotation

GitHub Actions / lint-yaml

101:39 [comments] too few spaces before comment
primary_resource_id: 'example-autokeyconfig-folder'
min_version: 'beta'
vars:
folder_name: 'my-folder'
key_project_name: 'key-proj'
org_id: 'ORG_ID'
billing_account: 'BILLING_ACCT'
- name: 'kms_autokey_config_project' # Example for project

Check warning on line 109 in mmv1/products/kms/AutokeyConfig.yaml

View workflow job for this annotation

GitHub Actions / lint-yaml

109:40 [comments] too few spaces before comment
primary_resource_id: 'example-autokeyconfig-project'
min_version: 'beta'
vars:
project_name: 'my-resource-proj'
org_id: 'ORG_ID'
billing_account: 'BILLING_ACCT'
Original file line number Diff line number Diff line change
@@ -1,4 +1,58 @@
func folderPrefixSuppress(_, old, new string, d *schema.ResourceData) bool {
prefix := "folders/"
return prefix+old == new || prefix+new == old
const (
autokeyFolderPrefix = "folders/"
autokeyProjectPrefix = "projects/"
autokeyConfigSuffix = "/autokeyConfig"
)

func autokeyHasPrefix(value, prefix string) bool {
return len(value) >= len(prefix) && value[:len(prefix)] == prefix
}

func autokeyTrimPrefix(value, prefix string) string {
if autokeyHasPrefix(value, prefix) {
return value[len(prefix):]
}
return value
}

func autokeyTrimSuffix(value, suffix string) string {
if len(value) >= len(suffix) && value[len(value)-len(suffix):] == suffix {
return value[:len(value)-len(suffix)]
}
return value
}

func autokeyJoin(fields []string) string {
result := ""
for i, field := range fields {
if i > 0 {
result += ","
}
result += field
}
return result
}

func normalizeParent(parent, kind string) string {
if parent == "" {
return parent
}
switch kind {
case "folder":
if !autokeyHasPrefix(parent, autokeyFolderPrefix) {
return autokeyFolderPrefix + parent
}
case "project":
if !autokeyHasPrefix(parent, autokeyProjectPrefix) {
return autokeyProjectPrefix + parent
}
}
return parent
}

func folderPrefixSuppress(k, old, new string, d *schema.ResourceData) bool {
return autokeyTrimPrefix(old, autokeyFolderPrefix) == autokeyTrimPrefix(new, autokeyFolderPrefix)
}
func projectPrefixSuppress(k, old, new string, d *schema.ResourceData) bool {
return autokeyTrimPrefix(old, autokeyProjectPrefix) == autokeyTrimPrefix(new, autokeyProjectPrefix)
}
85 changes: 85 additions & 0 deletions mmv1/templates/terraform/custom_create/kms_autokey_config.go.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent)
if err != nil {
return err
}

var parent string
if v, ok := d.GetOk("folder"); ok {
parent = normalizeParent(v.(string), "folder")
} else if v, ok := d.GetOk("project"); ok {
parent = normalizeParent(v.(string), "project")
} else {
return fmt.Errorf("either folder or project must be set")
}

billingProject := ""
if bp, err := tpgresource.GetBillingProject(d, config); err == nil {
billingProject = bp
}

headers := make(http.Header)
obj := make(map[string]interface{})
keyProjectProp, err := expandKMSAutokeyConfigKeyProject(d.Get("key_project"), d, config)
if err != nil {
return err
} else if v, ok := d.GetOkExists("key_project"); !tpgresource.IsEmptyValue(reflect.ValueOf(keyProjectProp)) && (ok || !reflect.DeepEqual(v, keyProjectProp)) {
obj["keyProject"] = keyProjectProp
}
keyProjectResolutionModeProp, err := expandKMSAutokeyConfigKeyProjectResolutionMode(d.Get("key_project_resolution_mode"), d, config)
if err != nil {
return err
} else if v, ok := d.GetOkExists("key_project_resolution_mode"); !tpgresource.IsEmptyValue(reflect.ValueOf(keyProjectResolutionModeProp)) && (ok || !reflect.DeepEqual(v, keyProjectResolutionModeProp)) {
obj["keyProjectResolutionMode"] = keyProjectResolutionModeProp
}
var updateMask []string
if !tpgresource.IsEmptyValue(reflect.ValueOf(keyProjectProp)) {
updateMask = append(updateMask, "keyProject")
}
if !tpgresource.IsEmptyValue(reflect.ValueOf(keyProjectResolutionModeProp)) {
updateMask = append(updateMask, "keyProjectResolutionMode")
}
var res map[string]interface{}
if len(updateMask) == 0 {
url := config.KMSBasePath + parent + "/autokeyConfig"
res, err = transport_tpg.SendRequest(transport_tpg.SendRequestOptions{
Config: config,
Method: "PATCH",
Project: billingProject,
RawURL: url,
UserAgent: userAgent,
Body: obj,
Timeout: d.Timeout(schema.TimeoutCreate),
Headers: headers,
})
if err != nil {
return fmt.Errorf("Error creating AutokeyConfig: %s", err)
}
} else {
url := config.KMSBasePath + parent + "/autokeyConfig?update_mask=" + autokeyJoin(updateMask)
res, err = transport_tpg.SendRequest(transport_tpg.SendRequestOptions{
Config: config,
Method: "PATCH",
Project: billingProject,
RawURL: url,
UserAgent: userAgent,
Body: obj,
Timeout: d.Timeout(schema.TimeoutCreate),
Headers: headers,
})
if err != nil {
return fmt.Errorf("Error creating AutokeyConfig: %s", err)
}
}
err = resourceKMSAutokeyConfigPostCreateSetComputedFields(d, meta, res)
if err != nil {
return fmt.Errorf("setting computed ID format fields: %w", err)
}
if nameVal, ok := d.Get("name").(string); ok && nameVal != "" {
d.SetId(nameVal)
} else {
return fmt.Errorf("Error constructing id: computed `name` not set")
}

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

return resourceKMSAutokeyConfigRead(d, meta)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
id := d.Id()
if autokeyHasPrefix(id, autokeyFolderPrefix) {
d.Set("folder", id)
} else if autokeyHasPrefix(id, autokeyProjectPrefix) {
d.Set("project", id)
} else {
return nil, fmt.Errorf("invalid import id %q, expected folders/{folder_id} or projects/{project_id}", id)
}
return []*schema.ResourceData{d}, nil
67 changes: 67 additions & 0 deletions mmv1/templates/terraform/custom_update/kms_autokey_config.go.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent)
if err != nil {
return err
}

billingProject := ""
if bp, err := tpgresource.GetBillingProject(d, config); err == nil {
billingProject = bp
}

headers := make(http.Header)

var parent string
if v, ok := d.GetOk("folder"); ok {
parent = normalizeParent(v.(string), "folder")
} else if v, ok := d.GetOk("project"); ok {
parent = normalizeParent(v.(string), "project")
} else {
return fmt.Errorf("either folder or project must be set")
}

body := make(map[string]interface{})
var updateMask []string

if d.HasChange("key_project") {
updateMask = append(updateMask, "keyProject")
if !autokeyHasPrefix(parent, autokeyProjectPrefix) {
keyProjectProp, _ := expandKMSAutokeyConfigKeyProject(d.Get("key_project"), d, config)
if v, ok := d.GetOkExists("key_project"); !tpgresource.IsEmptyValue(reflect.ValueOf(keyProjectProp)) && (ok || !reflect.DeepEqual(v, keyProjectProp)) {
body["keyProject"] = keyProjectProp
} else {
body["keyProject"] = nil
}
}
}

if d.HasChange("key_project_resolution_mode") {
updateMask = append(updateMask, "keyProjectResolutionMode")
keyProjectResolutionModeProp, _ := expandKMSAutokeyConfigKeyProjectResolutionMode(d.Get("key_project_resolution_mode"), d, config)
if v, ok := d.GetOkExists("key_project_resolution_mode"); !tpgresource.IsEmptyValue(reflect.ValueOf(keyProjectResolutionModeProp)) && (ok || !reflect.DeepEqual(v, keyProjectResolutionModeProp)) {
body["keyProjectResolutionMode"] = keyProjectResolutionModeProp
} else {
body["keyProjectResolutionMode"] = nil
}
}

if len(updateMask) == 0 {
return resourceKMSAutokeyConfigRead(d, meta)
}
url := config.KMSBasePath + parent + "/autokeyConfig?update_mask=" + autokeyJoin(updateMask)

_, err = transport_tpg.SendRequest(transport_tpg.SendRequestOptions{
Config: config,
Method: "PATCH",
Project: billingProject,
RawURL: url,
UserAgent: userAgent,
Body: body,
Timeout: d.Timeout(schema.TimeoutUpdate),
Headers: headers,
})
if err != nil {
return fmt.Errorf("Error updating AutokeyConfig: %s", err)
}

return resourceKMSAutokeyConfigRead(d, meta)

25 changes: 25 additions & 0 deletions mmv1/templates/terraform/decoders/kms_autokey_config.go.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Set the resource ID to the name returned from the API
if name, ok := res["name"].(string); ok {
d.SetId(name)
if autokeyHasPrefix(name, autokeyFolderPrefix) {
d.Set("folder", autokeyTrimSuffix(name, autokeyConfigSuffix))
d.Set("project", nil)
} else if autokeyHasPrefix(name, autokeyProjectPrefix) {
d.Set("project", autokeyTrimSuffix(name, autokeyConfigSuffix))
d.Set("folder", nil)
}
}

if v, ok := res["keyProject"].(string); ok {
d.Set("key_project", v)
} else {
d.Set("key_project", nil)
}

if v, ok := res["keyProjectResolutionMode"].(string); ok {
d.Set("key_project_resolution_mode", v)
} else {
d.Set("key_project_resolution_mode", nil)
}

return res, nil
Loading
Loading