From c264f4e9021a729cc14f8dd77f1aff67e068ca07 Mon Sep 17 00:00:00 2001 From: Roberto Jung Drebes Date: Wed, 19 Dec 2018 22:33:48 +0100 Subject: [PATCH] terraform: autogenerate Pubsub Topic & Subscription --- products/pubsub/ansible.yaml | 6 + products/pubsub/api.yaml | 55 ++++- products/pubsub/terraform.yaml | 82 +++++++ .../computed_subscription_name.erb | 36 +++ .../computed_subscription_topic.erb | 33 +++ .../decoders/pubsub_subscription.erb | 18 ++ ...bsub_subscription_different_project.tf.erb | 10 + .../examples/pubsub_subscription_pull.tf.erb | 14 ++ .../examples/pubsub_subscription_push.tf.erb | 22 ++ .../examples/pubsub_topic_basic.tf.erb | 7 + .../pubsub_subscription_path.erb | 4 + .../update_encoder/pubsub_subscription.erb | 17 ++ .../resources/resource_pubsub_subscription.go | 228 ------------------ .../resources/resource_pubsub_topic.go | 112 --------- .../resource_pubsub_subscription_test.go | 82 +++++-- .../tests/resource_pubsub_topic_test.go | 65 ----- third_party/terraform/utils/provider.go.erb | 3 +- third_party/terraform/utils/pubsub_utils.go | 32 +++ .../docs/r/pubsub_subscription.html.markdown | 98 -------- .../website/docs/r/pubsub_topic.html.markdown | 50 ---- 20 files changed, 395 insertions(+), 579 deletions(-) create mode 100644 products/pubsub/terraform.yaml create mode 100644 templates/terraform/custom_expand/computed_subscription_name.erb create mode 100644 templates/terraform/custom_expand/computed_subscription_topic.erb create mode 100644 templates/terraform/decoders/pubsub_subscription.erb create mode 100644 templates/terraform/examples/pubsub_subscription_different_project.tf.erb create mode 100644 templates/terraform/examples/pubsub_subscription_pull.tf.erb create mode 100644 templates/terraform/examples/pubsub_subscription_push.tf.erb create mode 100644 templates/terraform/examples/pubsub_topic_basic.tf.erb create mode 100644 templates/terraform/extra_schema_entry/pubsub_subscription_path.erb create mode 100644 templates/terraform/update_encoder/pubsub_subscription.erb delete mode 100644 third_party/terraform/resources/resource_pubsub_subscription.go delete mode 100644 third_party/terraform/resources/resource_pubsub_topic.go delete mode 100644 third_party/terraform/tests/resource_pubsub_topic_test.go create mode 100644 third_party/terraform/utils/pubsub_utils.go delete mode 100644 third_party/terraform/website/docs/r/pubsub_subscription.html.markdown delete mode 100644 third_party/terraform/website/docs/r/pubsub_topic.html.markdown diff --git a/products/pubsub/ansible.yaml b/products/pubsub/ansible.yaml index 18d17b7777c0..ceebf5992338 100644 --- a/products/pubsub/ansible.yaml +++ b/products/pubsub/ansible.yaml @@ -44,12 +44,18 @@ datasources: !ruby/object:Overrides::ResourceOverrides "\"{{resource_name}}\" not in \"{{ results['items'] | map(attribute='name') | list }}\"" overrides: !ruby/object:Overrides::ResourceOverrides Topic: !ruby/object:Overrides::Ansible::ResourceOverride + properties: + labels: !ruby/object:Overrides::Ansible::PropertyOverride + version_added: '2.8' transport: !ruby/object:Api::Resource::Transport encoder: encode_request decoder: decode_request provider_helpers: - 'products/pubsub/helpers/python/provider_topic.py' Subscription: !ruby/object:Overrides::Ansible::ResourceOverride + properties: + labels: !ruby/object:Overrides::Ansible::PropertyOverride + version_added: '2.8' transport: !ruby/object:Api::Resource::Transport encoder: encode_request decoder: decode_request diff --git a/products/pubsub/api.yaml b/products/pubsub/api.yaml index 434253701f8c..b98474350fc0 100644 --- a/products/pubsub/api.yaml +++ b/products/pubsub/api.yaml @@ -27,12 +27,23 @@ objects: create_verb: :PUT description: | A named resource to which messages are sent by publishers. + input: true properties: - !ruby/object:Api::Type::String name: 'name' + required: true description: 'Name of the topic.' + - !ruby/object:Api::Type::KeyValuePairs + name: 'labels' + description: | + A set of key/value label pairs to assign to this Topic. collection_url_response: !ruby/object:Api::Resource::ResponseList items: 'topics' + references: !ruby/object:Api::Resource::ReferenceLinks + guides: + 'Managing Topics': + 'https://cloud.google.com/pubsub/docs/admin#managing_topics' + api: 'https://cloud.google.com/pubsub/docs/reference/rest/v1/projects.topics' - !ruby/object:Api::Resource name: 'Subscription' base_url: projects/{{project}}/subscriptions @@ -40,19 +51,34 @@ objects: description: | A named resource representing the stream of messages from a single, specific topic, to be delivered to the subscribing application. - input: true + update_verb: :PATCH + update_mask: true + update_url: projects/{{project}}/subscriptions/{{name}} collection_url_response: !ruby/object:Api::Resource::ResponseList items: 'subscriptions' + references: !ruby/object:Api::Resource::ReferenceLinks + guides: + 'Managing Subscriptions': + 'https://cloud.google.com/pubsub/docs/admin#managing_subscriptions' + api: 'https://cloud.google.com/pubsub/docs/reference/rest/v1/projects.subscriptions' properties: - !ruby/object:Api::Type::String name: 'name' description: 'Name of the subscription.' + required: true + input: true - !ruby/object:Api::Type::ResourceRef name: 'topic' resource: 'Topic' imports: 'name' description: | A reference to a Topic resource. + required: true + input: true + - !ruby/object:Api::Type::KeyValuePairs + name: 'labels' + description: | + A set of key/value label pairs to assign to this Subscription. - !ruby/object:Api::Type::NestedObject name: 'pushConfig' description: | @@ -66,6 +92,33 @@ objects: A URL locating the endpoint to which messages should be pushed. For example, a Webhook endpoint might use "https://example.com/push". + required: true + - !ruby/object:Api::Type::KeyValuePairs + name: 'attributes' + description: | + Endpoint configuration attributes. + + Every endpoint has a set of API supported attributes that can + be used to control different aspects of the message delivery. + + The currently supported attribute is x-goog-version, which you + can use to change the format of the pushed message. This + attribute indicates the version of the data expected by + the endpoint. This controls the shape of the pushed message + (i.e., its fields and metadata). The endpoint version is + based on the version of the Pub/Sub API. + + If not present during the subscriptions.create call, + it will default to the version of the API used to make + such call. If not present during a subscriptions.modifyPushConfig + call, its value will not be changed. subscriptions.get + calls will always return a valid version, even if the + subscription was created without this attribute. + + The possible values for this attribute are: + + - v1beta1: uses the push format defined in the v1beta1 Pub/Sub API. + - v1 or v1beta2: uses the push format defined in the v1 Pub/Sub API. - !ruby/object:Api::Type::Integer name: 'ackDeadlineSeconds' description: | diff --git a/products/pubsub/terraform.yaml b/products/pubsub/terraform.yaml new file mode 100644 index 000000000000..e741130f2044 --- /dev/null +++ b/products/pubsub/terraform.yaml @@ -0,0 +1,82 @@ +# Copyright 2017 Google Inc. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +--- !ruby/object:Provider::Terraform::Config +overrides: !ruby/object:Overrides::ResourceOverrides + Topic: !ruby/object:Overrides::Terraform::ResourceOverride + id_format: "projects/{{project}}/topics/{{name}}" + examples: + - !ruby/object:Provider::Terraform::Examples + name: "pubsub_topic_basic" + primary_resource_id: "example" + version: <%= version_name %> + vars: + topic_name: "example-topic" + properties: + name: !ruby/object:Overrides::Terraform::PropertyOverride + diff_suppress_func: 'compareSelfLinkOrResourceName' + custom_expand: templates/terraform/custom_expand/resource_from_self_link.go.erb + custom_flatten: templates/terraform/custom_flatten/name_from_self_link.erb + Subscription: !ruby/object:Overrides::Terraform::ResourceOverride + id_format: "projects/{{project}}/subscriptions/{{name}}" + examples: + - !ruby/object:Provider::Terraform::Examples + name: "pubsub_subscription_push" + primary_resource_id: "example" + version: <%= version_name %> + skip_test: true + vars: + topic_name: "example-topic" + subscription_name: "example-subscription" + - !ruby/object:Provider::Terraform::Examples + name: "pubsub_subscription_pull" + primary_resource_id: "example" + version: <%= version_name %> + vars: + topic_name: "example-topic" + subscription_name: "example-subscription" + - !ruby/object:Provider::Terraform::Examples + name: "pubsub_subscription_different_project" + primary_resource_id: "example" + version: <%= version_name %> + skip_test: true + vars: + topic_name: "example-topic" + topic_project: "topic-project" + subscription_name: "example-subscription" + subscription_project: "subscription-project" + docs: !ruby/object:Provider::Terraform::Docs + attributes: | + * `path`: Path of the subscription in the format `projects/{project}/subscriptions/{name}` + properties: + name: !ruby/object:Overrides::Terraform::PropertyOverride + diff_suppress_func: 'comparePubsubSubscriptionBasename' + custom_expand: templates/terraform/custom_expand/computed_subscription_name.erb + custom_flatten: templates/terraform/custom_flatten/name_from_self_link.erb + topic: !ruby/object:Overrides::Terraform::PropertyOverride + diff_suppress_func: 'compareSelfLinkOrResourceName' + custom_expand: templates/terraform/custom_expand/computed_subscription_topic.erb + ackDeadlineSeconds: !ruby/object:Overrides::Terraform::PropertyOverride + default_from_api: true + custom_code: !ruby/object:Provider::Terraform::CustomCode + extra_schema_entry: templates/terraform/extra_schema_entry/pubsub_subscription_path.erb + decoder: templates/terraform/decoders/pubsub_subscription.erb + update_encoder: templates/terraform/update_encoder/pubsub_subscription.erb + + +# This is for copying files over +files: !ruby/object:Provider::Config::Files + # These files have templating (ERB) code that will be run. + # This is usually to add licensing info, autogeneration notices, etc. + compile: +<%= lines(indent(compile('provider/terraform/product~compile.yaml'), 4)) -%> diff --git a/templates/terraform/custom_expand/computed_subscription_name.erb b/templates/terraform/custom_expand/computed_subscription_name.erb new file mode 100644 index 000000000000..fd71eaf14a2b --- /dev/null +++ b/templates/terraform/custom_expand/computed_subscription_name.erb @@ -0,0 +1,36 @@ +<%# # the license inside this if block pertains to this file + # Copyright 2018 Google Inc. + # Licensed under the Apache License, Version 2.0 (the "License"); + # you may not use this file except in compliance with the License. + # You may obtain a copy of the License at + # + # http://www.apache.org/licenses/LICENSE-2.0 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + # See the License for the specific language governing permissions and + # limitations under the License. +#%> +func expand<%= prefix -%><%= titlelize_property(property) -%>(v interface{}, d *schema.ResourceData, config *Config) (interface{}, error) { + project, err := getProject(d, config) + if err != nil { + return "", err + } + + subscription := d.Get("name").(string) + + re := regexp.MustCompile("projects\\/(.*)\\/subscriptions\\/(.*)") + match := re.FindStringSubmatch(subscription) + if len(match) == 3 { + // We need to preserve the behavior where the user passes the subscription name already in the long form, + // however we need it to be stored as the short form since it's used for the replaceVars in the URL. + // The unintuitive behavior is that if the user provides the long form, we use the project from there, not the one + // specified on the resource or provider. + // TODO(drebes): consider depracating the long form behavior for 3.0 + d.Set("project", match[1]) + d.Set("name", match[2]) + return subscription, nil + } + return fmt.Sprintf("projects/%s/subscriptions/%s", project, subscription), nil +} diff --git a/templates/terraform/custom_expand/computed_subscription_topic.erb b/templates/terraform/custom_expand/computed_subscription_topic.erb new file mode 100644 index 000000000000..ab98064120f7 --- /dev/null +++ b/templates/terraform/custom_expand/computed_subscription_topic.erb @@ -0,0 +1,33 @@ +<%# # the license inside this if block pertains to this file + # Copyright 2018 Google Inc. + # Licensed under the Apache License, Version 2.0 (the "License"); + # you may not use this file except in compliance with the License. + # You may obtain a copy of the License at + # + # http://www.apache.org/licenses/LICENSE-2.0 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + # See the License for the specific language governing permissions and + # limitations under the License. +#%> +func expand<%= prefix -%><%= titlelize_property(property) -%>(v interface{}, d *schema.ResourceData, config *Config) (interface{}, error) { + project, err := getProject(d, config) + if err != nil { + return "", err + } + + topic := d.Get("topic").(string) + + re := regexp.MustCompile("projects\\/(.*)\\/topics\\/(.*)") + match := re.FindStringSubmatch(topic) + if len(match) == 3 { + return topic, nil + } else { + // If no full topic given, we expand it to a full topic on the same project + fullTopic := fmt.Sprintf("projects/%s/topics/%s", project, topic) + d.Set("topic", fullTopic) + return fullTopic, nil + } +} diff --git a/templates/terraform/decoders/pubsub_subscription.erb b/templates/terraform/decoders/pubsub_subscription.erb new file mode 100644 index 000000000000..bab01a03593d --- /dev/null +++ b/templates/terraform/decoders/pubsub_subscription.erb @@ -0,0 +1,18 @@ +<%# The license inside this block applies to this file. + # Copyright 2018 Google Inc. + # Licensed under the Apache License, Version 2.0 (the "License"); + # you may not use this file except in compliance with the License. + # You may obtain a copy of the License at + # + # http://www.apache.org/licenses/LICENSE-2.0 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + # See the License for the specific language governing permissions and + # limitations under the License. +-%> + + path := fmt.Sprintf("projects/%s/subscriptions/%s", d.Get("project"), d.Get("name")) + d.Set("path", path) +return res, nil diff --git a/templates/terraform/examples/pubsub_subscription_different_project.tf.erb b/templates/terraform/examples/pubsub_subscription_different_project.tf.erb new file mode 100644 index 000000000000..d3ca9295f54f --- /dev/null +++ b/templates/terraform/examples/pubsub_subscription_different_project.tf.erb @@ -0,0 +1,10 @@ +resource "google_pubsub_topic" "<%= ctx[:primary_resource_id] %>" { + project = "<%= ctx[:vars]['topic_project'] %>" + name = "<%= ctx[:vars]['topic_name'] %>" +} + +resource "google_pubsub_subscription" "<%= ctx[:primary_resource_id] %>" { + project = "<%= ctx[:vars]['subscription_project'] %>" + name = "<%= ctx[:vars]['subscription_name'] %>" + topic = "${google_pubsub_topic.<%= ctx[:primary_resource_id] %>.id}" +} diff --git a/templates/terraform/examples/pubsub_subscription_pull.tf.erb b/templates/terraform/examples/pubsub_subscription_pull.tf.erb new file mode 100644 index 000000000000..d47fb9fa403a --- /dev/null +++ b/templates/terraform/examples/pubsub_subscription_pull.tf.erb @@ -0,0 +1,14 @@ +resource "google_pubsub_topic" "<%= ctx[:primary_resource_id] %>" { + name = "<%= ctx[:vars]['topic_name'] %>" +} + +resource "google_pubsub_subscription" "<%= ctx[:primary_resource_id] %>" { + name = "<%= ctx[:vars]['subscription_name'] %>" + topic = "${google_pubsub_topic.<%= ctx[:primary_resource_id] %>.name}" + + labels = { + foo = "bar" + } + + ack_deadline_seconds = 20 +} diff --git a/templates/terraform/examples/pubsub_subscription_push.tf.erb b/templates/terraform/examples/pubsub_subscription_push.tf.erb new file mode 100644 index 000000000000..b631dfd30976 --- /dev/null +++ b/templates/terraform/examples/pubsub_subscription_push.tf.erb @@ -0,0 +1,22 @@ +resource "google_pubsub_topic" "<%= ctx[:primary_resource_id] %>" { + name = "<%= ctx[:vars]['topic_name'] %>" +} + +resource "google_pubsub_subscription" "<%= ctx[:primary_resource_id] %>" { + name = "<%= ctx[:vars]['subscription_name'] %>" + topic = "${google_pubsub_topic.<%= ctx[:primary_resource_id] %>.name}" + + ack_deadline_seconds = 20 + + labels = { + foo = "bar" + } + + push_config { + push_endpoint = "https://example.com/push" + + attributes { + x-goog-version = "v1" + } + } +} diff --git a/templates/terraform/examples/pubsub_topic_basic.tf.erb b/templates/terraform/examples/pubsub_topic_basic.tf.erb new file mode 100644 index 000000000000..6cc7abb210a6 --- /dev/null +++ b/templates/terraform/examples/pubsub_topic_basic.tf.erb @@ -0,0 +1,7 @@ +resource "google_pubsub_topic" "<%= ctx[:primary_resource_id] %>" { + name = "<%= ctx[:vars]['topic_name'] %>" + + labels = { + foo = "bar" + } +} diff --git a/templates/terraform/extra_schema_entry/pubsub_subscription_path.erb b/templates/terraform/extra_schema_entry/pubsub_subscription_path.erb new file mode 100644 index 000000000000..965b428e8e3f --- /dev/null +++ b/templates/terraform/extra_schema_entry/pubsub_subscription_path.erb @@ -0,0 +1,4 @@ +"path": &schema.Schema{ + Type: schema.TypeString, + Computed: true, +}, diff --git a/templates/terraform/update_encoder/pubsub_subscription.erb b/templates/terraform/update_encoder/pubsub_subscription.erb new file mode 100644 index 000000000000..d3349ea420b4 --- /dev/null +++ b/templates/terraform/update_encoder/pubsub_subscription.erb @@ -0,0 +1,17 @@ +<%# The license inside this block applies to this file. + # Copyright 2018 Google Inc. + # Licensed under the Apache License, Version 2.0 (the "License"); + # you may not use this file except in compliance with the License. + # You may obtain a copy of the License at + # + # http://www.apache.org/licenses/LICENSE-2.0 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + # See the License for the specific language governing permissions and + # limitations under the License. +-%> + newObj := make(map[string]interface{}) + newObj["subscription"] = obj +return newObj, nil diff --git a/third_party/terraform/resources/resource_pubsub_subscription.go b/third_party/terraform/resources/resource_pubsub_subscription.go deleted file mode 100644 index 5ed688932187..000000000000 --- a/third_party/terraform/resources/resource_pubsub_subscription.go +++ /dev/null @@ -1,228 +0,0 @@ -package google - -import ( - "fmt" - - "github.com/hashicorp/terraform/helper/schema" - "google.golang.org/api/pubsub/v1" - "regexp" -) - -func resourcePubsubSubscription() *schema.Resource { - return &schema.Resource{ - Create: resourcePubsubSubscriptionCreate, - Read: resourcePubsubSubscriptionRead, - Update: resourcePubsubSubscriptionUpdate, - Delete: resourcePubsubSubscriptionDelete, - - Importer: &schema.ResourceImporter{ - State: resourcePubsubSubscriptionStateImporter, - }, - - Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - - "topic": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - DiffSuppressFunc: compareSelfLinkOrResourceName, - }, - - "ack_deadline_seconds": { - Type: schema.TypeInt, - Optional: true, - Computed: true, - ForceNew: true, - }, - - "project": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - - "path": { - Type: schema.TypeString, - Computed: true, - }, - - "push_config": { - Type: schema.TypeList, - Optional: true, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "attributes": { - Type: schema.TypeMap, - Optional: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, - - "push_endpoint": { - Type: schema.TypeString, - Required: true, - }, - }, - }, - }, - }, - } -} - -func resourcePubsubSubscriptionCreate(d *schema.ResourceData, meta interface{}) error { - config := meta.(*Config) - - project, err := getProject(d, config) - if err != nil { - return err - } - - name := getComputedSubscriptionName(project, d.Get("name").(string)) - computed_topic_name := getComputedTopicName(project, d.Get("topic").(string)) - - // process optional parameters - var ackDeadlineSeconds int64 - ackDeadlineSeconds = 10 - if v, ok := d.GetOk("ack_deadline_seconds"); ok { - ackDeadlineSeconds = int64(v.(int)) - } - - subscription := &pubsub.Subscription{ - AckDeadlineSeconds: ackDeadlineSeconds, - Topic: computed_topic_name, - PushConfig: expandPubsubSubscriptionPushConfig(d.Get("push_config").([]interface{})), - } - - call := config.clientPubsub.Projects.Subscriptions.Create(name, subscription) - res, err := call.Do() - if err != nil { - return err - } - - d.SetId(res.Name) - - return resourcePubsubSubscriptionRead(d, meta) -} - -func getComputedTopicName(project, topic string) string { - match, _ := regexp.MatchString("projects\\/.*\\/topics\\/.*", topic) - if match { - return topic - } - return fmt.Sprintf("projects/%s/topics/%s", project, topic) -} - -func getComputedSubscriptionName(project, subscription string) string { - match, _ := regexp.MatchString("projects\\/.*\\/subscriptions\\/.*", subscription) - if match { - return subscription - } - return fmt.Sprintf("projects/%s/subscriptions/%s", project, subscription) -} - -func resourcePubsubSubscriptionRead(d *schema.ResourceData, meta interface{}) error { - config := meta.(*Config) - - project, err := getProject(d, config) - if err != nil { - return err - } - - name := d.Id() - subscription, err := config.clientPubsub.Projects.Subscriptions.Get(name).Do() - if err != nil { - return handleNotFoundError(err, d, fmt.Sprintf("Pubsub Subscription %q", name)) - } - - d.Set("name", GetResourceNameFromSelfLink(subscription.Name)) - d.Set("topic", subscription.Topic) - d.Set("ack_deadline_seconds", subscription.AckDeadlineSeconds) - d.Set("path", subscription.Name) - d.Set("push_config", flattenPubsubSubscriptionPushConfig(subscription.PushConfig)) - d.Set("project", project) - - return nil -} - -func resourcePubsubSubscriptionUpdate(d *schema.ResourceData, meta interface{}) error { - config := meta.(*Config) - - d.Partial(true) - - if d.HasChange("push_config") { - _, err := config.clientPubsub.Projects.Subscriptions.ModifyPushConfig(d.Id(), &pubsub.ModifyPushConfigRequest{ - PushConfig: expandPubsubSubscriptionPushConfig(d.Get("push_config").([]interface{})), - }).Do() - - if err != nil { - return fmt.Errorf("Error updating subscription %q: %s", d.Get("name"), err) - } - } - - d.Partial(false) - - return nil -} - -func resourcePubsubSubscriptionDelete(d *schema.ResourceData, meta interface{}) error { - config := meta.(*Config) - - name := d.Id() - call := config.clientPubsub.Projects.Subscriptions.Delete(name) - _, err := call.Do() - if err != nil { - return err - } - - return nil -} - -func resourcePubsubSubscriptionStateImporter(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - config := meta.(*Config) - - project, err := getProject(d, config) - if err != nil { - return nil, err - } - - id := fmt.Sprintf("projects/%s/subscriptions/%s", project, d.Id()) - - d.SetId(id) - - return []*schema.ResourceData{d}, nil -} - -func flattenPubsubSubscriptionPushConfig(pushConfig *pubsub.PushConfig) []map[string]interface{} { - configs := make([]map[string]interface{}, 0, 1) - - if pushConfig == nil || len(pushConfig.PushEndpoint) == 0 { - return configs - } - - configs = append(configs, map[string]interface{}{ - "push_endpoint": pushConfig.PushEndpoint, - "attributes": pushConfig.Attributes, - }) - - return configs -} - -func expandPubsubSubscriptionPushConfig(configured []interface{}) *pubsub.PushConfig { - if len(configured) == 0 || configured[0] == nil { - // An empty `pushConfig` indicates that the Pub/Sub system should stop pushing messages - // from the given subscription and allow messages to be pulled and acknowledged. - return &pubsub.PushConfig{} - } - - pushConfig := configured[0].(map[string]interface{}) - return &pubsub.PushConfig{ - PushEndpoint: pushConfig["push_endpoint"].(string), - Attributes: convertStringMap(pushConfig["attributes"].(map[string]interface{})), - } -} diff --git a/third_party/terraform/resources/resource_pubsub_topic.go b/third_party/terraform/resources/resource_pubsub_topic.go deleted file mode 100644 index 41c64a891e2d..000000000000 --- a/third_party/terraform/resources/resource_pubsub_topic.go +++ /dev/null @@ -1,112 +0,0 @@ -package google - -import ( - "fmt" - "regexp" - - "github.com/hashicorp/terraform/helper/schema" - "google.golang.org/api/pubsub/v1" -) - -func resourcePubsubTopic() *schema.Resource { - return &schema.Resource{ - Create: resourcePubsubTopicCreate, - Read: resourcePubsubTopicRead, - Delete: resourcePubsubTopicDelete, - - Importer: &schema.ResourceImporter{ - State: resourcePubsubTopicStateImporter, - }, - - Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - DiffSuppressFunc: linkDiffSuppress, - }, - - "project": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - }, - } -} - -func resourcePubsubTopicCreate(d *schema.ResourceData, meta interface{}) error { - config := meta.(*Config) - - project, err := getProject(d, config) - if err != nil { - return err - } - - name := fmt.Sprintf("projects/%s/topics/%s", project, d.Get("name").(string)) - topic := &pubsub.Topic{} - - call := config.clientPubsub.Projects.Topics.Create(name, topic) - res, err := call.Do() - if err != nil { - return err - } - - d.SetId(res.Name) - - return resourcePubsubTopicRead(d, meta) -} - -func resourcePubsubTopicRead(d *schema.ResourceData, meta interface{}) error { - config := meta.(*Config) - - project, err := getProject(d, config) - if err != nil { - return err - } - - name := d.Id() - call := config.clientPubsub.Projects.Topics.Get(name) - res, err := call.Do() - if err != nil { - return handleNotFoundError(err, d, fmt.Sprintf("Pubsub Topic %q", name)) - } - - d.Set("name", GetResourceNameFromSelfLink(res.Name)) - d.Set("project", project) - - return nil -} - -func resourcePubsubTopicDelete(d *schema.ResourceData, meta interface{}) error { - config := meta.(*Config) - - name := d.Id() - call := config.clientPubsub.Projects.Topics.Delete(name) - _, err := call.Do() - if err != nil { - return err - } - - return nil -} - -func resourcePubsubTopicStateImporter(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - config := meta.(*Config) - - topicId := regexp.MustCompile("^projects/[^/]+/topics/[^/]+$") - if topicId.MatchString(d.Id()) { - return []*schema.ResourceData{d}, nil - } - - if config.Project == "" { - return nil, fmt.Errorf("The default project for the provider must be set when using the `{name}` id format.") - } - - id := fmt.Sprintf("projects/%s/topics/%s", config.Project, d.Id()) - - d.SetId(id) - - return []*schema.ResourceData{d}, nil -} diff --git a/third_party/terraform/tests/resource_pubsub_subscription_test.go b/third_party/terraform/tests/resource_pubsub_subscription_test.go index 3d0d9767cd18..a292a5865a20 100644 --- a/third_party/terraform/tests/resource_pubsub_subscription_test.go +++ b/third_party/terraform/tests/resource_pubsub_subscription_test.go @@ -6,14 +6,13 @@ import ( "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" - "github.com/hashicorp/terraform/terraform" ) -func TestAccPubsubSubscription_basic(t *testing.T) { +func TestAccPubsubSubscription_fullName(t *testing.T) { t.Parallel() topic := fmt.Sprintf("tf-test-topic-%s", acctest.RandString(10)) - subscription := fmt.Sprintf("tf-test-sub-%s", acctest.RandString(10)) + subscription := fmt.Sprintf("projects/%s/subscriptions/tf-test-sub-%s", getTestProjectFromEnv(), acctest.RandString(10)) resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -21,7 +20,7 @@ func TestAccPubsubSubscription_basic(t *testing.T) { CheckDestroy: testAccCheckPubsubSubscriptionDestroy, Steps: []resource.TestStep{ { - Config: testAccPubsubSubscription_basic(topic, subscription), + Config: testAccPubsubSubscription_fullName(topic, subscription, "bar", 20), }, { ResourceName: "google_pubsub_subscription.foo", @@ -33,6 +32,55 @@ func TestAccPubsubSubscription_basic(t *testing.T) { }) } +func TestAccPubsubSubscription_update(t *testing.T) { + t.Parallel() + + topic := fmt.Sprintf("tf-test-topic-%s", acctest.RandString(10)) + subscriptionShort := fmt.Sprintf("tf-test-sub-%s", acctest.RandString(10)) + subscriptionLong := fmt.Sprintf("projects/%s/subscriptions/%s", getTestProjectFromEnv(), subscriptionShort) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckPubsubSubscriptionDestroy, + Steps: []resource.TestStep{ + { + Config: testAccPubsubSubscription_fullName(topic, subscriptionLong, "bar", 20), + }, + { + ResourceName: "google_pubsub_subscription.foo", + ImportStateId: subscriptionLong, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccPubsubSubscription_fullName(topic, subscriptionLong, "baz", 30), + Check: resource.TestCheckResourceAttr( + "google_pubsub_subscription.foo", "path", subscriptionLong, + ), + }, + { + ResourceName: "google_pubsub_subscription.foo", + ImportStateId: subscriptionLong, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccPubsubSubscription_fullName(topic, subscriptionShort, "baz", 30), + Check: resource.TestCheckResourceAttr( + "google_pubsub_subscription.foo", "path", subscriptionLong, + ), + }, + { + ResourceName: "google_pubsub_subscription.foo", + ImportStateId: subscriptionShort, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + // TODO: Add acceptance test for push delivery. // // Testing push endpoints is tricky for the following reason: @@ -45,23 +93,8 @@ func TestAccPubsubSubscription_basic(t *testing.T) { // An easy way to test this would be to create an App Engine Hello World app. With AppEngine, SSL certificate, DNS and domain registry is handled for us. // App Engine is not yet supported by Terraform but once it is, it will provide an easy path to testing push configs. // Another option would be to use Cloud Functions once Terraform support is added. -func testAccCheckPubsubSubscriptionDestroy(s *terraform.State) error { - for _, rs := range s.RootModule().Resources { - if rs.Type != "google_pubsub_subscription" { - continue - } - - config := testAccProvider.Meta().(*Config) - sub, _ := config.clientPubsub.Projects.Subscriptions.Get(rs.Primary.ID).Do() - if sub != nil { - return fmt.Errorf("Subscription still present") - } - } - return nil -} - -func testAccPubsubSubscription_basic(topic, subscription string) string { +func testAccPubsubSubscription_fullName(topic, subscription, label string, deadline int) string { return fmt.Sprintf(` resource "google_pubsub_topic" "foo" { name = "%s" @@ -69,9 +102,12 @@ resource "google_pubsub_topic" "foo" { resource "google_pubsub_subscription" "foo" { name = "%s" - topic = "${google_pubsub_topic.foo.name}" - ack_deadline_seconds = 20 -}`, topic, subscription) + topic = "${google_pubsub_topic.foo.id}" + labels = { + foo = "%s" + } + ack_deadline_seconds = %d +}`, topic, subscription, label, deadline) } func TestGetComputedTopicName(t *testing.T) { diff --git a/third_party/terraform/tests/resource_pubsub_topic_test.go b/third_party/terraform/tests/resource_pubsub_topic_test.go deleted file mode 100644 index e309d745e702..000000000000 --- a/third_party/terraform/tests/resource_pubsub_topic_test.go +++ /dev/null @@ -1,65 +0,0 @@ -package google - -import ( - "fmt" - "testing" - - "github.com/hashicorp/terraform/helper/acctest" - "github.com/hashicorp/terraform/helper/resource" - "github.com/hashicorp/terraform/terraform" -) - -func TestAccPubsubTopic_basic(t *testing.T) { - t.Parallel() - - topicName := acctest.RandomWithPrefix("tf-test-topic") - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckPubsubTopicDestroy, - Steps: []resource.TestStep{ - { - Config: testAccPubsubTopic_basic(topicName), - }, - // Check importing with just the topic name - { - ResourceName: "google_pubsub_topic.foo", - ImportStateId: topicName, - ImportState: true, - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"force_destroy"}, - }, - // Check importing with the full resource id - { - ResourceName: "google_pubsub_topic.foo", - ImportState: true, - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"force_destroy"}, - }, - }, - }) -} - -func testAccCheckPubsubTopicDestroy(s *terraform.State) error { - for _, rs := range s.RootModule().Resources { - if rs.Type != "google_pubsub_topic" { - continue - } - - config := testAccProvider.Meta().(*Config) - topic, _ := config.clientPubsub.Projects.Topics.Get(rs.Primary.ID).Do() - if topic != nil { - return fmt.Errorf("Topic still present") - } - } - - return nil -} - -func testAccPubsubTopic_basic(name string) string { - return fmt.Sprintf(` -resource "google_pubsub_topic" "foo" { - name = "%s" -}`, name) -} diff --git a/third_party/terraform/utils/provider.go.erb b/third_party/terraform/utils/provider.go.erb index bd082803a49b..cf63bbc33c28 100644 --- a/third_party/terraform/utils/provider.go.erb +++ b/third_party/terraform/utils/provider.go.erb @@ -149,6 +149,7 @@ func ResourceMapWithErrors() (map[string]*schema.Resource, error) { GeneratedComputeResourcesMap, GeneratedCloudBuildResourcesMap, GeneratedDnsResourcesMap, + GeneratedPubsubResourcesMap, GeneratedRedisResourcesMap, GeneratedResourceManagerResourcesMap, GeneratedSourceRepoResourcesMap, @@ -248,11 +249,9 @@ func ResourceMapWithErrors() (map[string]*schema.Resource, error) { "google_project_organization_policy": resourceGoogleProjectOrganizationPolicy(), "google_project_usage_export_bucket": resourceProjectUsageBucket(), "google_project_services": resourceGoogleProjectServices(), - "google_pubsub_topic": resourcePubsubTopic(), "google_pubsub_topic_iam_binding": ResourceIamBindingWithImport(IamPubsubTopicSchema, NewPubsubTopicIamUpdater, PubsubTopicIdParseFunc), "google_pubsub_topic_iam_member": ResourceIamMemberWithImport(IamPubsubTopicSchema, NewPubsubTopicIamUpdater, PubsubTopicIdParseFunc), "google_pubsub_topic_iam_policy": ResourceIamPolicyWithImport(IamPubsubTopicSchema, NewPubsubTopicIamUpdater, PubsubTopicIdParseFunc), - "google_pubsub_subscription": resourcePubsubSubscription(), "google_pubsub_subscription_iam_binding": ResourceIamBindingWithImport(IamPubsubSubscriptionSchema, NewPubsubSubscriptionIamUpdater, PubsubSubscriptionIdParseFunc), "google_pubsub_subscription_iam_member": ResourceIamMemberWithImport(IamPubsubSubscriptionSchema, NewPubsubSubscriptionIamUpdater, PubsubSubscriptionIdParseFunc), "google_pubsub_subscription_iam_policy": ResourceIamPolicyWithImport(IamPubsubSubscriptionSchema, NewPubsubSubscriptionIamUpdater, PubsubSubscriptionIdParseFunc), diff --git a/third_party/terraform/utils/pubsub_utils.go b/third_party/terraform/utils/pubsub_utils.go new file mode 100644 index 000000000000..2057d4f6b84f --- /dev/null +++ b/third_party/terraform/utils/pubsub_utils.go @@ -0,0 +1,32 @@ +package google + +import ( + "fmt" + "regexp" + + "github.com/hashicorp/terraform/helper/schema" +) + +func comparePubsubSubscriptionBasename(_, old, new string, _ *schema.ResourceData) bool { + if GetResourceNameFromSelfLink(old) == GetResourceNameFromSelfLink(new) { + return true + } + + return false +} + +func getComputedSubscriptionName(project, subscription string) string { + match, _ := regexp.MatchString("projects\\/.*\\/subscriptions\\/.*", subscription) + if match { + return subscription + } + return fmt.Sprintf("projects/%s/subscriptions/%s", project, subscription) +} + +func getComputedTopicName(project, topic string) string { + match, _ := regexp.MatchString("projects\\/.*\\/topics\\/.*", topic) + if match { + return topic + } + return fmt.Sprintf("projects/%s/topics/%s", project, topic) +} diff --git a/third_party/terraform/website/docs/r/pubsub_subscription.html.markdown b/third_party/terraform/website/docs/r/pubsub_subscription.html.markdown deleted file mode 100644 index 94e208eeadb2..000000000000 --- a/third_party/terraform/website/docs/r/pubsub_subscription.html.markdown +++ /dev/null @@ -1,98 +0,0 @@ ---- -layout: "google" -page_title: "Google: google_pubsub_subscription" -sidebar_current: "docs-google-pubsub-subscription-x" -description: |- - Creates a subscription in Google's pubsub queueing system ---- - -# google\_pubsub\_subscription - -Creates a subscription in Google's pubsub queueing system. For more information see -[the official documentation](https://cloud.google.com/pubsub/docs) and -[API](https://cloud.google.com/pubsub/docs/reference/rest/v1/projects.subscriptions). - - -## Example Usage - -```hcl -resource "google_pubsub_topic" "default-topic" { - name = "default-topic" -} - -resource "google_pubsub_subscription" "default" { - name = "default-subscription" - topic = "${google_pubsub_topic.default-topic.name}" - - ack_deadline_seconds = 20 - - push_config { - push_endpoint = "https://example.com/push" - - attributes = { - x-goog-version = "v1" - } - } -} -``` - -If the subscription has a topic in a different project: - -```hcl -resource "google_pubsub_topic" "topic-different-project" { - project = "another-project" - name = "topic-different-project" -} - -resource "google_pubsub_subscription" "default" { - name = "default-subscription" - topic = "${google_pubsub_topic.topic-different-project.id}" -} -``` - -## Argument Reference - -The following arguments are supported: - -* `name` - (Required) A unique name for the resource, required by pubsub. - Changing this forces a new resource to be created. - -* `topic` - (Required) The topic name or id to bind this subscription to, required by pubsub. - Changing this forces a new resource to be created. - -- - - - -* `ack_deadline_seconds` - (Optional) The maximum number of seconds a - subscriber has to acknowledge a received message, otherwise the message is - redelivered. Changing this forces a new resource to be created. - -* `project` - (Optional) The ID of the project in which the resource belongs. If it - is not provided, the provider project is used. - -* `push_config` - (Optional) Block configuration for push options. More - configuration options are detailed below. - -The optional `push_config` block supports: - -* `push_endpoint` - (Required) The URL of the endpoint to which messages should - be pushed. Changing this forces a new resource to be created. - -* `attributes` - (Optional) Key-value pairs of API supported attributes used - to control aspects of the message delivery. Currently, only - `x-goog-version` is supported, which controls the format of the data - delivery. For more information, read [the API docs - here](https://cloud.google.com/pubsub/reference/rest/v1/projects.subscriptions#PushConfig.FIELDS.attributes). - Changing this forces a new resource to be created. - -## Attributes Reference - -* `path` - Path of the subscription in the format `projects/{project}/subscriptions/{sub}` - -## Import - -Pubsub subscription can be imported using the `name`, e.g. - -``` -$ terraform import google_pubsub_subscription.default default-subscription -``` - diff --git a/third_party/terraform/website/docs/r/pubsub_topic.html.markdown b/third_party/terraform/website/docs/r/pubsub_topic.html.markdown deleted file mode 100644 index 5f85c824ccd9..000000000000 --- a/third_party/terraform/website/docs/r/pubsub_topic.html.markdown +++ /dev/null @@ -1,50 +0,0 @@ ---- -layout: "google" -page_title: "Google: google_pubsub_topic" -sidebar_current: "docs-google-pubsub-topic-x" -description: |- - Creates a topic in Google's pubsub queueing system ---- - -# google\_pubsub\_topic - -Creates a topic in Google's pubsub queueing system. For more information see -[the official documentation](https://cloud.google.com/pubsub/docs) and -[API](https://cloud.google.com/pubsub/docs/reference/rest/v1/projects.topics). - - -## Example Usage - -```hcl -resource "google_pubsub_topic" "mytopic" { - name = "default-topic" -} -``` - -## Argument Reference - -The following arguments are supported: - -* `name` - (Required) A unique name for the pubsub topic. - Changing this forces a new resource to be created. - -- - - - -* `project` - (Optional) The ID of the project in which the resource belongs. If it - is not provided, the provider project is used. - -## Attributes Reference - -Only the arguments listed above are exposed as attributes. - -## Import - -Pubsub topics can be imported using the `name` or full topic id, e.g. - -``` -$ terraform import google_pubsub_topic.mytopic default-topic -``` -``` -$ terraform import google_pubsub_topic.mytopic projects/my-gcp-project/topics/default-topic -``` -When importing using only the name, the provider project must be set.