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

Adds support for creating KMS KeyRing resources #518

Merged
merged 34 commits into from
Oct 27, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
ab18653
Instantiate the cloudkms client
mrparkers Sep 16, 2017
d18db25
Implement Create and Read for the kms key ring resource
mrparkers Sep 16, 2017
946eef1
Expose the kms key ring resource
mrparkers Sep 16, 2017
7afead9
Create acceptance test for creating a KeyRing, fix read to use KeyRin…
mrparkers Sep 29, 2017
b491ad0
Add cloudkms library to vendor
mrparkers Oct 3, 2017
0ce76d7
Merge branch 'master' into kms-keyring
mrparkers Oct 9, 2017
34e5b64
Address style comments
mrparkers Oct 15, 2017
7536f92
Use fully-qualified keyring name in read operation
mrparkers Oct 15, 2017
5b815d7
Remove call to SetId during read operation
mrparkers Oct 15, 2017
bc9527f
Set ID as entire resource string
mrparkers Oct 15, 2017
1248ab8
Spin up a new project for acceptance test
mrparkers Oct 15, 2017
e3de446
Merge branch 'master' into kms-keyring
mrparkers Oct 15, 2017
d6cb880
Use Getenv for billing and org environment variables
mrparkers Oct 16, 2017
9d7ff51
Merge branch 'master' into kms-keyring
mrparkers Oct 22, 2017
c524051
And test and logs around removal from state
mrparkers Oct 22, 2017
5a7b690
Add comments
mrparkers Oct 22, 2017
33b74c0
Fixes formatting
mrparkers Oct 23, 2017
5c49d08
Log warning instead of info
mrparkers Oct 23, 2017
775d43f
Use a single line for cloudkms client actions
mrparkers Oct 23, 2017
23500d0
Add resource import test
mrparkers Oct 24, 2017
b8d0dbe
Add ability to import resource, update helper functions to use keyRin…
mrparkers Oct 24, 2017
c4317ab
Use shorter terraform ID for easier import
mrparkers Oct 26, 2017
c2106a9
Update import test to use the same config as the basic test
mrparkers Oct 26, 2017
27fc744
Update KeyRing name regex to be consistent with API docs
mrparkers Oct 26, 2017
a2c3363
Add documentation page for resource
mrparkers Oct 26, 2017
82f4785
Add KeyRing documentation to sidebar
mrparkers Oct 26, 2017
6e2a6c0
Adds unit tests around parsing the KeyRing import id
mrparkers Oct 26, 2017
2120b47
Allow for project in id to be autopopulated from config
mrparkers Oct 26, 2017
65cbadc
Throw error in import if project provider is not provided for locatio…
mrparkers Oct 26, 2017
024b45b
Merge branch 'master' into kms-keyring
mrparkers Oct 26, 2017
2791c74
Consistent variable names
mrparkers Oct 26, 2017
248d518
Use tabs in resource config instead of spaces
mrparkers Oct 26, 2017
3fbf0bb
Remove "-x" suffix for docs
mrparkers Oct 26, 2017
6dc9035
Set project attribute on import if different from the project config
mrparkers Oct 26, 2017
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
9 changes: 9 additions & 0 deletions google/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"golang.org/x/oauth2/jwt"
"google.golang.org/api/bigquery/v2"
"google.golang.org/api/cloudbilling/v1"
"google.golang.org/api/cloudkms/v1"
"google.golang.org/api/cloudresourcemanager/v1"
resourceManagerV2Beta1 "google.golang.org/api/cloudresourcemanager/v2beta1"
computeBeta "google.golang.org/api/compute/v0.beta"
Expand Down Expand Up @@ -47,6 +48,7 @@ type Config struct {
clientComputeBeta *computeBeta.Service
clientContainer *container.Service
clientDns *dns.Service
clientKms *cloudkms.Service
clientLogging *cloudlogging.Service
clientPubsub *pubsub.Service
clientResourceManager *cloudresourcemanager.Service
Expand Down Expand Up @@ -155,6 +157,13 @@ func (c *Config) loadAndValidate() error {
}
c.clientDns.UserAgent = userAgent

log.Printf("[INFO] Instantiating Google Cloud KMS Client...")
c.clientKms, err = cloudkms.New(client)
if err != nil {
return err
}
c.clientKms.UserAgent = userAgent

log.Printf("[INFO] Instantiating Google Stackdriver Logging client...")
c.clientLogging, err = cloudlogging.New(client)
if err != nil {
Expand Down
42 changes: 42 additions & 0 deletions google/import_kms_key_ring_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package google

import (
"testing"

"fmt"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
"os"
)

func TestAccGoogleKmsKeyRing_importBasic(t *testing.T) {
skipIfEnvNotSet(t,
[]string{
"GOOGLE_ORG",
"GOOGLE_BILLING_ACCOUNT",
}...,
)

resourceName := "google_kms_key_ring.key_ring"

projectId := "terraform-" + acctest.RandString(10)
projectOrg := os.Getenv("GOOGLE_ORG")
projectBillingAccount := os.Getenv("GOOGLE_BILLING_ACCOUNT")
keyRingName := fmt.Sprintf("tf-test-%s", acctest.RandString(10))

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
resource.TestStep{
Config: testGoogleKmsKeyRing_basic(projectId, projectOrg, projectBillingAccount, keyRingName),
},

resource.TestStep{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}
1 change: 1 addition & 0 deletions google/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ func Provider() terraform.ResourceProvider {
"google_logging_billing_account_sink": resourceLoggingBillingAccountSink(),
"google_logging_folder_sink": resourceLoggingFolderSink(),
"google_logging_project_sink": resourceLoggingProjectSink(),
"google_kms_key_ring": resourceKmsKeyRing(),
"google_sourcerepo_repository": resourceSourceRepoRepository(),
"google_spanner_instance": resourceSpannerInstance(),
"google_spanner_database": resourceSpannerDatabase(),
Expand Down
172 changes: 172 additions & 0 deletions google/resource_kms_key_ring.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
package google

import (
"fmt"
"github.com/hashicorp/terraform/helper/schema"
"google.golang.org/api/cloudkms/v1"
"log"
"regexp"
"strings"
)

func resourceKmsKeyRing() *schema.Resource {
return &schema.Resource{
Create: resourceKmsKeyRingCreate,
Read: resourceKmsKeyRingRead,
Delete: resourceKmsKeyRingDelete,
Importer: &schema.ResourceImporter{
State: resourceKmsKeyRingImportState,
},

Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"location": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"project": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
},
}
}

type kmsKeyRingId struct {
Project string
Location string
Name string
}

func (s *kmsKeyRingId) keyRingId() string {
return fmt.Sprintf("projects/%s/locations/%s/keyRings/%s", s.Project, s.Location, s.Name)
}

func (s *kmsKeyRingId) parentId() string {
return fmt.Sprintf("projects/%s/locations/%s", s.Project, s.Location)
}

func (s *kmsKeyRingId) terraformId() string {
return fmt.Sprintf("%s/%s/%s", s.Project, s.Location, s.Name)
}

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

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

keyRingId := &kmsKeyRingId{
Project: project,
Location: d.Get("location").(string),
Name: d.Get("name").(string),
}

keyRing, err := config.clientKms.Projects.Locations.KeyRings.Create(keyRingId.parentId(), &cloudkms.KeyRing{}).KeyRingId(keyRingId.Name).Do()

if err != nil {
return fmt.Errorf("Error creating KeyRing: %s", err)
}

log.Printf("[DEBUG] Created KeyRing %s", keyRing.Name)

d.SetId(keyRingId.terraformId())

return resourceKmsKeyRingRead(d, meta)
}

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

keyRingId, err := parseKmsKeyRingId(d.Id(), config)
if err != nil {
return err
}

log.Printf("[DEBUG] Executing read for KMS KeyRing %s", keyRingId.keyRingId())

_, err = config.clientKms.Projects.Locations.KeyRings.Get(keyRingId.keyRingId()).Do()

if err != nil {
return fmt.Errorf("Error reading KeyRing: %s", err)
}

return nil
}

/*
Because KMS KeyRing resources cannot be deleted on GCP, we are only going to remove it from state.
Re-creation of this resource through Terraform will produce an error.
*/

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

keyRingId, err := parseKmsKeyRingId(d.Id(), config)
if err != nil {
return err
}

log.Printf("[WARNING] KMS KeyRing resources cannot be deleted from GCP. This KeyRing %s will be removed from Terraform state, but will still be present on the server.", keyRingId.keyRingId())

d.SetId("")

return nil
}

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

keyRingIdRegex := regexp.MustCompile("^([a-z0-9-]+)/([a-z0-9-])+/([a-zA-Z0-9_-]{1,63})$")
keyRingIdWithoutProjectRegex := regexp.MustCompile("^([a-z0-9-])+/([a-zA-Z0-9_-]{1,63})$")

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

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

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

return nil, fmt.Errorf("Invalid KeyRing id format, expecting `{projectId}/{locationId}/{keyRingName}` or `{locationId}/{keyRingName}.`")
}

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

keyRingId, err := parseKmsKeyRingId(d.Id(), config)
if err != nil {
return nil, err
}

d.Set("name", keyRingId.Name)
d.Set("location", keyRingId.Location)

if config.Project != keyRingId.Project {
d.Set("project", keyRingId.Project)
}

d.SetId(keyRingId.terraformId())

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