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

Add backend bucket signed URL key (for CDN) support #1504

Merged
merged 3 commits into from
Apr 1, 2019
Merged
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
2 changes: 1 addition & 1 deletion build/terraform
2 changes: 1 addition & 1 deletion build/terraform-beta
4 changes: 4 additions & 0 deletions products/compute/ansible.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ manifest: !ruby/object:Provider::Ansible::Manifest
datasources: !ruby/object:Overrides::ResourceOverrides
Autoscaler: !ruby/object:Overrides::Ansible::ResourceOverride
exclude: true
BackendBucketSignedUrlKey: !ruby/object:Overrides::Ansible::ResourceOverride
exclude: true
Snapshot: !ruby/object:Overrides::Ansible::ResourceOverride
exclude: true
ManagedSslCertificate: !ruby/object:Overrides::Ansible::ResourceOverride
Expand Down Expand Up @@ -223,6 +225,8 @@ overrides: !ruby/object:Overrides::ResourceOverrides
# Not yet implemented.
Autoscaler: !ruby/object:Overrides::Ansible::ResourceOverride
exclude: true
BackendBucketSignedUrlKey: !ruby/object:Overrides::Ansible::ResourceOverride
exclude: true
RegionAutoscaler: !ruby/object:Overrides::Ansible::ResourceOverride
exclude: true
Snapshot: !ruby/object:Overrides::Ansible::ResourceOverride
Expand Down
133 changes: 131 additions & 2 deletions products/compute/api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -205,12 +205,45 @@ objects:
guides:
'Using a Cloud Storage bucket as a load balancer backend': 'https://cloud.google.com/compute/docs/load-balancing/http/backend-bucket'
api: 'https://cloud.google.com/compute/docs/reference/v1/backendBuckets'
<%= indent(compile_file({}, 'templates/global_async.yaml.erb'), 4) %>
async: !ruby/object:Api::Async
operation: !ruby/object:Api::Async::Operation
kind: 'compute#operation'
path: 'name'
base_url: 'projects/{{project}}/global/operations/{{op_id}}'
wait_ms: 1000
result: !ruby/object:Api::Async::Result
path: 'targetLink'
status: !ruby/object:Api::Async::Status
path: 'status'
complete: 'DONE'
allowed:
- 'PENDING'
- 'RUNNING'
- 'DONE'
error: !ruby/object:Api::Async::Error
path: 'error/errors'
message: 'message'
properties:
- !ruby/object:Api::Type::String
name: 'bucketName'
description: 'Cloud Storage bucket name.'
required: true
- !ruby/object:Api::Type::NestedObject
name: 'cdnPolicy'
description: 'Cloud CDN configuration for this Backend Bucket.'
properties:
- !ruby/object:Api::Type::Integer
name: 'signedUrlCacheMaxAgeSec'
default_value: 3600
description: |
Maximum number of seconds the response to a signed URL request will
be considered fresh. Defaults to 1hr (3600s). After this time period,
the response will be revalidated before being served.
When serving responses to signed URL requests,
Cloud CDN will internally behave as though
all responses from this backend had a "Cache-Control: public,
max-age=[TTL]" header, regardless of any existing Cache-Control
header. The actual headers served in responses will not be altered.
- !ruby/object:Api::Type::Time
name: 'creationTimestamp'
description: 'Creation timestamp in RFC3339 text format.'
Expand Down Expand Up @@ -239,6 +272,71 @@ objects:
last character, which cannot be a dash.
input: true
required: true
- !ruby/object:Api::Resource
emilymye marked this conversation as resolved.
Show resolved Hide resolved
name: 'BackendBucketSignedUrlKey'
kind: 'compute#BackendBucketSignedUrlKey'
input: true
base_url: projects/{{project}}/global/backendBuckets/{{backend_bucket}}
create_url: projects/{{project}}/global/backendBuckets/{{backend_bucket}}/addSignedUrlKey
create_verb: :POST
delete_url: projects/{{project}}/global/backendBuckets/{{backend_bucket}}/deleteSignedUrlKey?keyName={{name}}
delete_verb: :POST
self_link: projects/{{project}}/global/backendBuckets/{{backend_bucket}}
identity:
- name
nested_query: !ruby/object:Api::Resource::NestedQuery
keys:
- cdnPolicy
- signedUrlKeyNames
is_list_of_ids: true
description: |
A key for signing Cloud CDN signed URLs for BackendBuckets.
references: !ruby/object:Api::Resource::ReferenceLinks
guides:
'Using Signed URLs': 'https://cloud.google.com/cdn/docs/using-signed-urls/'
api: 'https://cloud.google.com/compute/docs/reference/rest/v1/backendBuckets'
async: !ruby/object:Api::Async
operation: !ruby/object:Api::Async::Operation
kind: 'compute#operation'
path: 'name'
base_url: 'projects/{{project}}/global/operations/{{op_id}}'
wait_ms: 1000
result: !ruby/object:Api::Async::Result
path: 'targetLink'
status: !ruby/object:Api::Async::Status
path: 'status'
complete: 'DONE'
allowed:
- 'PENDING'
- 'RUNNING'
- 'DONE'
error: !ruby/object:Api::Async::Error
path: 'error/errors'
message: 'message'
parameters:
- !ruby/object:Api::Type::ResourceRef
name: 'backendBucket'
resource: 'BackendBucket'
imports: 'name'
description: |
The backend bucket this signed URL key belongs.
required: true
input: true
properties:
- !ruby/object:Api::Type::String
name: 'name'
api_name: 'keyName'
description: |
Name of the signed URL key.
required: true
input: true
- !ruby/object:Api::Type::String
name: 'keyValue'
description: |
128-bit key value used for signing the URL. The key value must be a
valid RFC 4648 Section 5 base64url encoded string.
required: true
input: true
- !ruby/object:Api::Resource
name: 'BackendService'
kind: 'compute#backendService'
Expand All @@ -250,7 +348,24 @@ objects:
description: |
Creates a BackendService resource in the specified project using the data
included in the request.
<%= indent(compile_file({}, 'templates/global_async.yaml.erb'), 4) %>
async: !ruby/object:Api::Async
operation: !ruby/object:Api::Async::Operation
kind: 'compute#operation'
path: 'name'
base_url: 'projects/{{project}}/global/operations/{{op_id}}'
wait_ms: 1000
result: !ruby/object:Api::Async::Result
path: 'targetLink'
status: !ruby/object:Api::Async::Status
path: 'status'
complete: 'DONE'
allowed:
- 'PENDING'
- 'RUNNING'
- 'DONE'
error: !ruby/object:Api::Async::Error
path: 'error/errors'
message: 'message'
properties:
- !ruby/object:Api::Type::Integer
name: 'affinityCookieTtlSec'
Expand Down Expand Up @@ -408,6 +523,20 @@ objects:
'&' and '=' will be percent encoded and not treated as
delimiters.
item_type: Api::Type::String
- !ruby/object:Api::Type::Integer
name: 'signedUrlCacheMaxAgeSec'
default_value: 3600
description: |
Maximum number of seconds the response to a signed URL request
will be considered fresh, defaults to 1hr (3600s). After this
time period, the response will be revalidated before
being served.

When serving responses to signed URL requests, Cloud CDN will
internally behave as though all responses from this backend had a
"Cache-Control: public, max-age=[TTL]" header, regardless of any
existing Cache-Control header. The actual headers served in
responses will not be altered.
- !ruby/object:Api::Type::NestedObject
name: 'connectionDraining'
description: 'Settings for connection draining'
Expand Down
2 changes: 2 additions & 0 deletions products/compute/inspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ manifest: !ruby/object:Provider::Inspec::Manifest
overrides: !ruby/object:Overrides::ResourceOverrides
Address: !ruby/object:Overrides::Inspec::ResourceOverride
exclude: true
BackendBucketSignedUrlKey: !ruby/object:Overrides::Inspec::ResourceOverride
exclude: true
DiskType: !ruby/object:Overrides::Inspec::ResourceOverride
exclude: true
Firewall: !ruby/object:Overrides::Inspec::ResourceOverride
Expand Down
29 changes: 29 additions & 0 deletions products/compute/terraform.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,35 @@ overrides: !ruby/object:Overrides::ResourceOverrides
name: !ruby/object:Overrides::Terraform::PropertyOverride
validation: !ruby/object:Provider::Terraform::Validation
regex: '^(?:[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?)$'
cdnPolicy: !ruby/object:Overrides::Terraform::PropertyOverride
default_from_api: true
BackendBucketSignedUrlKey: !ruby/object:Overrides::Terraform::ResourceOverride
exclude_import: true
mutex: signedUrlKey/{{project}}/backendBuckets/{{backend_bucket}}/
examples:
- !ruby/object:Provider::Terraform::Examples
name: "backend_bucket_signed_url_key"
primary_resource_id: "backend_key"
vars:
key_name: "test-key"
backend_name: "test-signed-backend-bucket"
bucket_name: "test-storage-bucket"
skip_test: true
properties:
backendBucket: !ruby/object:Overrides::Terraform::PropertyOverride
ignore_read: true
name: !ruby/object:Overrides::Terraform::PropertyOverride
validation: !ruby/object:Provider::Terraform::Validation
regex: '^(?:[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?)$'
keyValue: !ruby/object:Overrides::Terraform::PropertyOverride
sensitive: true
ignore_read: true
docs: !ruby/object:Provider::Terraform::Docs
warning: |
All arguments including the key's value will be stored in the raw
state as plain-text. [Read more about sensitive data in state](/docs/state/sensitive-data.html).
Because the API does not return the sensitive key value,
we cannot confirm or reverse changes to a key outside of Terraform.
BackendService: !ruby/object:Overrides::Terraform::ResourceOverride
exclude: true
Disk: !ruby/object:Overrides::Terraform::ResourceOverride
Expand Down
17 changes: 17 additions & 0 deletions templates/terraform/examples/backend_bucket_signed_url_key.tf.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
resource "google_compute_backend_bucket_signed_url_key" "backend_key" {
name = "<%= ctx[:vars]['key_name'] %>"
key_value = "pPsVemX8GM46QVeezid6Rw=="
backend_bucket = "${google_compute_backend_bucket.test_backend.name}"
}

resource "google_compute_backend_bucket" "test_backend" {
name = "<%= ctx[:vars]['backend_name'] %>"
description = "Contains beautiful images"
bucket_name = "${google_storage_bucket.bucket.name}"
enable_cdn = true
}

resource "google_storage_bucket" "bucket" {
name = "<%= ctx[:vars]['bucket_name'] %>"
location = "EU"
}
2 changes: 1 addition & 1 deletion templates/terraform/nested_query.go.erb
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
<% end -%>
<% object.identity.each do |prop| -%>
<% if settable_properties.include?(prop) -%>
idProp, err := expand<%= resource_name -%><%= titlelize_property(prop) -%>(d.Get("<%= prop.name.underscore -%>"), d, meta.(*Config))
itemIdV, err := expand<%= resource_name -%><%= titlelize_property(prop) -%>(d.Get("<%= prop.name.underscore -%>"), d, meta.(*Config))
if err != nil {
return nil, err
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package google

import (
"fmt"
"testing"

"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"strings"
)

func TestAccComputeBackendBucketSignedUrlKey_basic(t *testing.T) {
t.Parallel()

context := map[string]interface{}{
"random_suffix": acctest.RandString(10),
}

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckComputeBackendBucketSignedUrlKeyDestroy,
Steps: []resource.TestStep{
{
Config: testAccComputeBackendBucketSignedUrlKey_basic(context),
Check: testAccCheckComputeBackendBucketSignedUrlKeyCreated,
},
},
})
}

func testAccComputeBackendBucketSignedUrlKey_basic(context map[string]interface{}) string {
return Nprintf(`
resource "google_compute_backend_bucket_signed_url_key" "backend_key" {
name = "test-key-%{random_suffix}"
key_value = "iAmAFakeKeyRandomBytes=="
backend_bucket = "${google_compute_backend_bucket.test_backend.name}"
}

resource "google_compute_backend_bucket" "test_backend" {
name = "test-signed-backend-bucket-%{random_suffix}"
description = "Contains beautiful images"
bucket_name = "${google_storage_bucket.bucket.name}"
enable_cdn = true
}

resource "google_storage_bucket" "bucket" {
name = "test-storage-bucket-%{random_suffix}"
location = "EU"
}
`, context)
}

func testAccCheckComputeBackendBucketSignedUrlKeyDestroy(s *terraform.State) error {
exists, err := checkComputeBackendBucketSignedUrlKeyExists(s)
if err != nil && !isGoogleApiErrorWithCode(err, 404) {
return err
}
if exists {
return fmt.Errorf("ComputeBackendBucketSignedUrlKey still exists")
}
return nil
}

func testAccCheckComputeBackendBucketSignedUrlKeyCreated(s *terraform.State) error {
exists, err := checkComputeBackendBucketSignedUrlKeyExists(s)
if err != nil {
return err
}
if !exists {
return fmt.Errorf("expected ComputeBackendBucketSignedUrlKey to have been created")
}
return nil
}

func checkComputeBackendBucketSignedUrlKeyExists(s *terraform.State) (bool, error) {
for name, rs := range s.RootModule().Resources {
if rs.Type != "google_compute_backend_bucket_signed_url_key" {
continue
}
if strings.HasPrefix(name, "data.") {
continue
}

config := testAccProvider.Meta().(*Config)
keyName := rs.Primary.ID

url, err := replaceVarsForTest(rs, "https://www.googleapis.com/compute/v1/projects/{{project}}/global/backendBuckets/{{backend_bucket}}")
if err != nil {
return false, err
}

res, err := sendRequest(config, "GET", url, nil)
if err == nil {
policyRaw, ok := res["cdnPolicy"]
if !ok {
return false, nil
}

policy := policyRaw.(map[string]interface{})
keyNames, ok := policy["signedUrlKeyNames"]
if !ok {
return false, nil
}

// Because the sensitive key value is not returned, all we can do is verify a
// key with this name exists and assume the key value hasn't been changed.
for _, k := range keyNames.([]interface{}) {
if k.(string) == keyName {
// Just return empty map to indicate key was found
return true, nil
}
}
}
}

return false, nil
}
Loading