mirror of
https://github.com/letic/terraform-provider-google.git
synced 2024-10-01 16:21:06 +00:00
provider/google: Add support for Google Compute Stogare buckets.
Configure Google Compute Storage buckets using: * name (compulsory attribute) * predefined_acl (optional, default: `projectPrivate`) * location (optional, default: `US`) * force_destroy (optional, default: `false`) Currently supporting only `predefined_acl`s. Bucket attribute updates happen via re-creation. force_destroy will cause bucket objects to be purged, enabling bucket destruction.
This commit is contained in:
parent
df251f17c9
commit
a4170297a3
12
config.go
12
config.go
@ -15,6 +15,7 @@ import (
|
||||
"golang.org/x/oauth2/jwt"
|
||||
"google.golang.org/api/compute/v1"
|
||||
"google.golang.org/api/dns/v1"
|
||||
"google.golang.org/api/storage/v1"
|
||||
)
|
||||
|
||||
// Config is the configuration structure used to instantiate the Google
|
||||
@ -25,7 +26,8 @@ type Config struct {
|
||||
Region string
|
||||
|
||||
clientCompute *compute.Service
|
||||
clientDns *dns.Service
|
||||
clientDns *dns.Service
|
||||
clientStorage *storage.Service
|
||||
}
|
||||
|
||||
func (c *Config) loadAndValidate() error {
|
||||
@ -55,6 +57,7 @@ func (c *Config) loadAndValidate() error {
|
||||
clientScopes := []string{
|
||||
"https://www.googleapis.com/auth/compute",
|
||||
"https://www.googleapis.com/auth/ndev.clouddns.readwrite",
|
||||
"https://www.googleapis.com/auth/devstorage.full_control",
|
||||
}
|
||||
|
||||
// Get the token for use in our requests
|
||||
@ -114,6 +117,13 @@ func (c *Config) loadAndValidate() error {
|
||||
}
|
||||
c.clientDns.UserAgent = userAgent
|
||||
|
||||
log.Printf("[INFO] Instantiating Google Storage Client...")
|
||||
c.clientStorage, err = storage.New(client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.clientStorage.UserAgent = userAgent
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -41,6 +41,7 @@ func Provider() terraform.ResourceProvider {
|
||||
"google_compute_target_pool": resourceComputeTargetPool(),
|
||||
"google_dns_managed_zone": resourceDnsManagedZone(),
|
||||
"google_dns_record_set": resourceDnsRecordSet(),
|
||||
"google_storage_bucket": resourceStorageBucket(),
|
||||
},
|
||||
|
||||
ConfigureFunc: providerConfigure,
|
||||
|
144
resource_storage_bucket.go
Normal file
144
resource_storage_bucket.go
Normal file
@ -0,0 +1,144 @@
|
||||
package google
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
|
||||
"google.golang.org/api/storage/v1"
|
||||
)
|
||||
|
||||
func resourceStorageBucket() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Create: resourceStorageBucketCreate,
|
||||
Read: resourceStorageBucketRead,
|
||||
Update: resourceStorageBucketUpdate,
|
||||
Delete: resourceStorageBucketDelete,
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
"name": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
"predefined_acl": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Default: "projectPrivate",
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
"location": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Default: "US",
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
"force_destroy": &schema.Schema{
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
Default: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func resourceStorageBucketCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
config := meta.(*Config)
|
||||
|
||||
// Get the bucket and acl
|
||||
bucket := d.Get("name").(string)
|
||||
acl := d.Get("predefined_acl").(string)
|
||||
location := d.Get("location").(string)
|
||||
|
||||
// Create a bucket, setting the acl, location and name.
|
||||
sb := &storage.Bucket{Name: bucket, Location: location}
|
||||
res, err := config.clientStorage.Buckets.Insert(config.Project, sb).PredefinedAcl(acl).Do()
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("Error creating bucket %s: %v", bucket, err)
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] Created bucket %v at location %v\n\n", res.Name, res.SelfLink)
|
||||
|
||||
// Assign the bucket ID as the resource ID
|
||||
d.SetId(res.Id)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resourceStorageBucketUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||
// Only thing you can currently change is force_delete (all other properties have ForceNew)
|
||||
// which is just terraform object state change, so nothing to do here
|
||||
return nil
|
||||
}
|
||||
|
||||
func resourceStorageBucketRead(d *schema.ResourceData, meta interface{}) error {
|
||||
config := meta.(*Config)
|
||||
|
||||
// Get the bucket and acl
|
||||
bucket := d.Get("name").(string)
|
||||
res, err := config.clientStorage.Buckets.Get(bucket).Do()
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("Error reading bucket %s: %v", bucket, err)
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] Read bucket %v at location %v\n\n", res.Name, res.SelfLink)
|
||||
|
||||
// Update the bucket ID according to the resource ID
|
||||
d.SetId(res.Id)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resourceStorageBucketDelete(d *schema.ResourceData, meta interface{}) error {
|
||||
config := meta.(*Config)
|
||||
|
||||
// Get the bucket
|
||||
bucket := d.Get("name").(string)
|
||||
|
||||
for {
|
||||
res, err := config.clientStorage.Objects.List(bucket).Do()
|
||||
if err != nil {
|
||||
fmt.Printf("Error Objects.List failed: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if len(res.Items) != 0 {
|
||||
if d.Get("force_destroy").(bool) {
|
||||
// purge the bucket...
|
||||
log.Printf("[DEBUG] GCS Bucket attempting to forceDestroy\n\n")
|
||||
|
||||
for _, object := range res.Items {
|
||||
log.Printf("[DEBUG] Found %s", object.Name)
|
||||
if err := config.clientStorage.Objects.Delete(bucket, object.Name).Do(); err != nil {
|
||||
log.Fatalf("Error trying to delete object: %s %s\n\n", object.Name, err)
|
||||
} else {
|
||||
log.Printf("Object deleted: %s \n\n", object.Name)
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
delete_err := errors.New("Error trying to delete a bucket containing objects without `force_destroy` set to true")
|
||||
log.Printf("Error! %s : %s\n\n", bucket, delete_err)
|
||||
return delete_err
|
||||
}
|
||||
} else {
|
||||
break // 0 items, bucket empty
|
||||
}
|
||||
}
|
||||
|
||||
// remove empty bucket
|
||||
err := config.clientStorage.Buckets.Delete(bucket).Do()
|
||||
if err != nil {
|
||||
fmt.Printf("Error deleting bucket %s: %v\n\n", bucket, err)
|
||||
return err
|
||||
}
|
||||
log.Printf("[DEBUG] Deleted bucket %v\n\n", bucket)
|
||||
|
||||
return nil
|
||||
}
|
231
resource_storage_bucket_test.go
Normal file
231
resource_storage_bucket_test.go
Normal file
@ -0,0 +1,231 @@
|
||||
package google
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"bytes"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
|
||||
"google.golang.org/api/googleapi"
|
||||
storage "google.golang.org/api/storage/v1"
|
||||
)
|
||||
|
||||
func TestAccStorageDefaults(t *testing.T) {
|
||||
var bucketName string
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccGoogleStorageDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testGoogleStorageBucketsReaderDefaults,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckCloudStorageBucketExists(
|
||||
"google_storage_bucket.bucket", &bucketName),
|
||||
resource.TestCheckResourceAttr(
|
||||
"google_storage_bucket.bucket", "predefined_acl", "projectPrivate"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"google_storage_bucket.bucket", "location", "US"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"google_storage_bucket.bucket", "force_destroy", "false"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccStorageCustomAttributes(t *testing.T) {
|
||||
var bucketName string
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccGoogleStorageDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testGoogleStorageBucketsReaderCustomAttributes,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckCloudStorageBucketExists(
|
||||
"google_storage_bucket.bucket", &bucketName),
|
||||
resource.TestCheckResourceAttr(
|
||||
"google_storage_bucket.bucket", "predefined_acl", "publicReadWrite"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"google_storage_bucket.bucket", "location", "EU"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"google_storage_bucket.bucket", "force_destroy", "true"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccStorageBucketUpdate(t *testing.T) {
|
||||
var bucketName string
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccGoogleStorageDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testGoogleStorageBucketsReaderDefaults,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckCloudStorageBucketExists(
|
||||
"google_storage_bucket.bucket", &bucketName),
|
||||
resource.TestCheckResourceAttr(
|
||||
"google_storage_bucket.bucket", "predefined_acl", "projectPrivate"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"google_storage_bucket.bucket", "location", "US"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"google_storage_bucket.bucket", "force_destroy", "false"),
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: testGoogleStorageBucketsReaderCustomAttributes,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckCloudStorageBucketExists(
|
||||
"google_storage_bucket.bucket", &bucketName),
|
||||
resource.TestCheckResourceAttr(
|
||||
"google_storage_bucket.bucket", "predefined_acl", "publicReadWrite"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"google_storage_bucket.bucket", "location", "EU"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"google_storage_bucket.bucket", "force_destroy", "true"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccStorageForceDestroy(t *testing.T) {
|
||||
var bucketName string
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccGoogleStorageDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testGoogleStorageBucketsReaderCustomAttributes,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckCloudStorageBucketExists(
|
||||
"google_storage_bucket.bucket", &bucketName),
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: testGoogleStorageBucketsReaderCustomAttributes,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckCloudStorageBucketPutItem(&bucketName),
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: "",
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckCloudStorageBucketMissing(&bucketName),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccCheckCloudStorageBucketExists(n string, bucketName *string) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
if !ok {
|
||||
return fmt.Errorf("Not found: %s", n)
|
||||
}
|
||||
|
||||
if rs.Primary.ID == "" {
|
||||
return fmt.Errorf("No Project_ID is set")
|
||||
}
|
||||
|
||||
config := testAccProvider.Meta().(*Config)
|
||||
|
||||
found, err := config.clientStorage.Buckets.Get(rs.Primary.ID).Do()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if found.Id != rs.Primary.ID {
|
||||
return fmt.Errorf("Bucket not found")
|
||||
}
|
||||
|
||||
*bucketName = found.Name
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckCloudStorageBucketPutItem(bucketName *string) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
config := testAccProvider.Meta().(*Config)
|
||||
|
||||
data := bytes.NewBufferString("test")
|
||||
dataReader := bytes.NewReader(data.Bytes())
|
||||
object := &storage.Object{Name: "bucketDestroyTestFile"}
|
||||
|
||||
// This needs to use Media(io.Reader) call, otherwise it does not go to /upload API and fails
|
||||
if res, err := config.clientStorage.Objects.Insert(*bucketName, object).Media(dataReader).Do(); err == nil {
|
||||
fmt.Printf("Created object %v at location %v\n\n", res.Name, res.SelfLink)
|
||||
} else {
|
||||
return fmt.Errorf("Objects.Insert failed: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckCloudStorageBucketMissing(bucketName *string) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
config := testAccProvider.Meta().(*Config)
|
||||
|
||||
_, err := config.clientStorage.Buckets.Get(*bucketName).Do()
|
||||
if err == nil {
|
||||
return fmt.Errorf("Found %s", *bucketName)
|
||||
}
|
||||
|
||||
if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
|
||||
return nil
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testAccGoogleStorageDestroy(s *terraform.State) error {
|
||||
config := testAccProvider.Meta().(*Config)
|
||||
|
||||
for _, rs := range s.RootModule().Resources {
|
||||
if rs.Type != "google_storage_bucket" {
|
||||
continue
|
||||
}
|
||||
|
||||
_, err := config.clientStorage.Buckets.Get(rs.Primary.ID).Do()
|
||||
if err == nil {
|
||||
return fmt.Errorf("Bucket still exists")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var randInt = rand.New(rand.NewSource(time.Now().UnixNano())).Int()
|
||||
|
||||
var testGoogleStorageBucketsReaderDefaults = fmt.Sprintf(`
|
||||
resource "google_storage_bucket" "bucket" {
|
||||
name = "tf-test-bucket-%d"
|
||||
}
|
||||
`, randInt)
|
||||
|
||||
var testGoogleStorageBucketsReaderCustomAttributes = fmt.Sprintf(`
|
||||
resource "google_storage_bucket" "bucket" {
|
||||
name = "tf-test-bucket-%d"
|
||||
predefined_acl = "publicReadWrite"
|
||||
location = "EU"
|
||||
force_destroy = "true"
|
||||
}
|
||||
`, randInt)
|
Loading…
Reference in New Issue
Block a user