Skip to content

Commit

Permalink
autogenerate KMS CryptoKey, add purpose (#93)
Browse files Browse the repository at this point in the history
Signed-off-by: Modular Magician <magic-modules@google.com>
  • Loading branch information
modular-magician authored and danawillow committed Jun 17, 2019
1 parent 9d39879 commit 6f60892
Show file tree
Hide file tree
Showing 2 changed files with 272 additions and 0 deletions.
124 changes: 124 additions & 0 deletions google/kms_crypto_key.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// ----------------------------------------------------------------------------
//
// *** 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"
"reflect"
"time"
)

func GetKmsCryptoKeyCaiObject(d TerraformResourceData, config *Config) (Asset, error) {
name, err := assetName(d, config, "//kms.googleapis.com/{{key_ring}}/cryptoKeys/{{name}}")
if err != nil {
return Asset{}, err
}
if obj, err := GetKmsCryptoKeyApiObject(d, config); err == nil {
return Asset{
Name: name,
Type: "kms.googleapis.com/CryptoKey",
Resource: &AssetResource{
Version: "v1",
DiscoveryDocumentURI: "https://www.googleapis.com/discovery/v1/apis/kms/v1/rest",
DiscoveryName: "CryptoKey",
Data: obj,
},
}, nil
} else {
return Asset{}, err
}
}

func GetKmsCryptoKeyApiObject(d TerraformResourceData, config *Config) (map[string]interface{}, error) {
obj := make(map[string]interface{})
purposeProp, err := expandKmsCryptoKeyPurpose(d.Get("purpose"), d, config)
if err != nil {
return nil, err
} else if v, ok := d.GetOkExists("purpose"); !isEmptyValue(reflect.ValueOf(purposeProp)) && (ok || !reflect.DeepEqual(v, purposeProp)) {
obj["purpose"] = purposeProp
}
rotationPeriodProp, err := expandKmsCryptoKeyRotationPeriod(d.Get("rotation_period"), d, config)
if err != nil {
return nil, err
} else if v, ok := d.GetOkExists("rotation_period"); !isEmptyValue(reflect.ValueOf(rotationPeriodProp)) && (ok || !reflect.DeepEqual(v, rotationPeriodProp)) {
obj["rotationPeriod"] = rotationPeriodProp
}
versionTemplateProp, err := expandKmsCryptoKeyVersionTemplate(d.Get("version_template"), d, config)
if err != nil {
return nil, err
} else if v, ok := d.GetOkExists("version_template"); !isEmptyValue(reflect.ValueOf(versionTemplateProp)) && (ok || !reflect.DeepEqual(v, versionTemplateProp)) {
obj["versionTemplate"] = versionTemplateProp
}

return resourceKmsCryptoKeyEncoder(d, config, obj)
}

func resourceKmsCryptoKeyEncoder(d TerraformResourceData, meta interface{}, obj map[string]interface{}) (map[string]interface{}, error) {
// if rotationPeriod is set, nextRotationTime must also be set.
if d.Get("rotation_period") != "" {
rotationPeriod := d.Get("rotation_period").(string)
nextRotation, err := kmsCryptoKeyNextRotation(time.Now(), rotationPeriod)

if err != nil {
return nil, fmt.Errorf("Error setting CryptoKey rotation period: %s", err.Error())
}

obj["nextRotationTime"] = nextRotation
}

return obj, nil
}

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

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

func expandKmsCryptoKeyVersionTemplate(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{})

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

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

return transformed, nil
}

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

func expandKmsCryptoKeyVersionTemplateProtectionLevel(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
return v, nil
}
148 changes: 148 additions & 0 deletions google/kms_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@ package google
import (
"fmt"
"regexp"
"strconv"
"strings"
"time"

"github.com/hashicorp/terraform/helper/schema"
"google.golang.org/api/cloudkms/v1"
)

type kmsKeyRingId struct {
Expand Down Expand Up @@ -56,3 +61,146 @@ func parseKmsKeyRingId(id string, config *Config) (*kmsKeyRingId, error) {
}
return nil, fmt.Errorf("Invalid KeyRing id format, expecting `{projectId}/{locationId}/{keyRingName}` or `{locationId}/{keyRingName}.`")
}

func kmsCryptoKeyRingsEquivalent(k, old, new string, d *schema.ResourceData) bool {
keyRingIdWithSpecifiersRegex := regexp.MustCompile("^projects/(" + ProjectRegex + ")/locations/([a-z0-9-])+/keyRings/([a-zA-Z0-9_-]{1,63})$")
normalizedKeyRingIdRegex := regexp.MustCompile("^(" + ProjectRegex + ")/([a-z0-9-])+/([a-zA-Z0-9_-]{1,63})$")
if matches := keyRingIdWithSpecifiersRegex.FindStringSubmatch(new); matches != nil {
normMatches := normalizedKeyRingIdRegex.FindStringSubmatch(old)
return normMatches != nil && normMatches[1] == matches[1] && normMatches[2] == matches[2] && normMatches[3] == matches[3]
}
return false
}

type kmsCryptoKeyId struct {
KeyRingId kmsKeyRingId
Name string
}

func (s *kmsCryptoKeyId) cryptoKeyId() string {
return fmt.Sprintf("%s/cryptoKeys/%s", s.KeyRingId.keyRingId(), s.Name)
}

func (s *kmsCryptoKeyId) terraformId() string {
return fmt.Sprintf("%s/%s", s.KeyRingId.terraformId(), s.Name)
}

func validateKmsCryptoKeyRotationPeriod(value interface{}, _ string) (ws []string, errors []error) {
period := value.(string)
pattern := regexp.MustCompile("^([0-9.]*\\d)s$")
match := pattern.FindStringSubmatch(period)

if len(match) == 0 {
errors = append(errors, fmt.Errorf("Invalid rotation period format: %s", period))
// Cannot continue to validate because we cannot extract a number.
return
}

number := match[1]
seconds, err := strconv.ParseFloat(number, 64)

if err != nil {
errors = append(errors, err)
} else {
if seconds < 86400.0 {
errors = append(errors, fmt.Errorf("Rotation period must be greater than one day"))
}

parts := strings.Split(number, ".")

if len(parts) > 1 && len(parts[1]) > 9 {
errors = append(errors, fmt.Errorf("Rotation period cannot have more than 9 fractional digits"))
}
}

return
}

func kmsCryptoKeyNextRotation(now time.Time, period string) (result string, err error) {
var duration time.Duration

duration, err = time.ParseDuration(period)

if err == nil {
result = now.UTC().Add(duration).Format(time.RFC3339Nano)
}

return
}

func parseKmsCryptoKeyId(id string, config *Config) (*kmsCryptoKeyId, error) {
parts := strings.Split(id, "/")

cryptoKeyIdRegex := regexp.MustCompile("^(" + ProjectRegex + ")/([a-z0-9-])+/([a-zA-Z0-9_-]{1,63})/([a-zA-Z0-9_-]{1,63})$")
cryptoKeyIdWithoutProjectRegex := regexp.MustCompile("^([a-z0-9-])+/([a-zA-Z0-9_-]{1,63})/([a-zA-Z0-9_-]{1,63})$")
cryptoKeyRelativeLinkRegex := regexp.MustCompile("^projects/(" + ProjectRegex + ")/locations/([a-z0-9-]+)/keyRings/([a-zA-Z0-9_-]{1,63})/cryptoKeys/([a-zA-Z0-9_-]{1,63})$")

if cryptoKeyIdRegex.MatchString(id) {
return &kmsCryptoKeyId{
KeyRingId: kmsKeyRingId{
Project: parts[0],
Location: parts[1],
Name: parts[2],
},
Name: parts[3],
}, nil
}

if cryptoKeyIdWithoutProjectRegex.MatchString(id) {
if config.Project == "" {
return nil, fmt.Errorf("The default project for the provider must be set when using the `{location}/{keyRingName}/{cryptoKeyName}` id format.")
}

return &kmsCryptoKeyId{
KeyRingId: kmsKeyRingId{
Project: config.Project,
Location: parts[0],
Name: parts[1],
},
Name: parts[2],
}, nil
}

if parts := cryptoKeyRelativeLinkRegex.FindStringSubmatch(id); parts != nil {
return &kmsCryptoKeyId{
KeyRingId: kmsKeyRingId{
Project: parts[1],
Location: parts[2],
Name: parts[3],
},
Name: parts[4],
}, nil
}
return nil, fmt.Errorf("Invalid CryptoKey id format, expecting `{projectId}/{locationId}/{KeyringName}/{cryptoKeyName}` or `{locationId}/{keyRingName}/{cryptoKeyName}, got id: %s`", id)
}

func clearCryptoKeyVersions(cryptoKeyId *kmsCryptoKeyId, config *Config) error {
versionsClient := config.clientKms.Projects.Locations.KeyRings.CryptoKeys.CryptoKeyVersions

versionsResponse, err := versionsClient.List(cryptoKeyId.cryptoKeyId()).Do()

if err != nil {
return err
}

for _, version := range versionsResponse.CryptoKeyVersions {
request := &cloudkms.DestroyCryptoKeyVersionRequest{}
_, err = versionsClient.Destroy(version.Name, request).Do()

if err != nil {
return err
}
}

return nil
}

func disableCryptoKeyRotation(cryptoKeyId *kmsCryptoKeyId, config *Config) error {
keyClient := config.clientKms.Projects.Locations.KeyRings.CryptoKeys
_, err := keyClient.Patch(cryptoKeyId.cryptoKeyId(), &cloudkms.CryptoKey{
NullFields: []string{"rotationPeriod", "nextRotationTime"},
}).
UpdateMask("rotationPeriod,nextRotationTime").Do()

return err
}

0 comments on commit 6f60892

Please sign in to comment.