Skip to content

Commit 01a663c

Browse files
authored
Merge pull request #1680 from Amit2465/ay/feat-add-gcp-pubsub-support
feat(gcp): add support for verifying Pub/Sub Topics and Subscriptions
2 parents d0f2b15 + cffb7e9 commit 01a663c

9 files changed

Lines changed: 464 additions & 0 deletions

File tree

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Terraform GCP Pub/Sub Example
2+
3+
This folder contains a simple Terraform module that deploys resources in [GCP](https://cloud.google.com/) to demonstrate
4+
how you can use Terratest to write automated tests for your GCP Terraform code. This module deploys a [Pub/Sub Topic](https://cloud.google.com/pubsub/docs/reference/rest/v1/projects.topics) and a [Pub/Sub Subscription](https://cloud.google.com/pubsub/docs/reference/rest/v1/projects.subscriptions) attached to that topic.
5+
6+
Check out [test/gcp/terraform_gcp_pubsub_example_test.go](../../test/gcp/terraform_gcp_pubsub_example_test.go) to see how
7+
you can write automated tests for this module.
8+
9+
**WARNING**: This module and the automated tests for it deploy real resources into your GCP account which can cost you
10+
money. The resources are typically part of the [GCP Free Tier](https://cloud.google.com/free/), so if you haven't used that up,
11+
it should be free, but you are completely responsible for all GCP charges.
12+
13+
## Running this module manually
14+
15+
1. Sign up for [GCP](https://cloud.google.com/).
16+
1. Configure your GCP credentials using one of the [supported methods for GCP CLI
17+
tools](https://cloud.google.com/sdk/docs/quickstarts).
18+
1. Install [Terraform](https://www.terraform.io/) and make sure it's in your `PATH`.
19+
1. Ensure the desired Project ID is set: `export GOOGLE_CLOUD_PROJECT=your-project-id`.
20+
1. Run `terraform init`.
21+
1. Run `terraform apply`.
22+
1. When you're done, run `terraform destroy`.
23+
24+
## Running automated tests against this module
25+
26+
1. Sign up for [GCP](https://cloud.google.com/free/).
27+
1. Configure your GCP credentials using the [GCP CLI
28+
tools](https://cloud.google.com/sdk/docs/quickstarts).
29+
1. Install [Terraform](https://www.terraform.io/) and make sure it's on your `PATH`.
30+
1. Install [Golang](https://golang.org/) and make sure this code is checked out into your `GOPATH`.
31+
1. Set `GOOGLE_CLOUD_PROJECT` environment variable to your project name.
32+
1. `cd test/gcp`
33+
1. `go test -v -tags=gcp -run TestTerraformGcpPubSubExample`
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# ---------------------------------------------------------------------------------------------------------------------
2+
# PIN TERRAFORM VERSION TO >= 0.12
3+
# The examples have been upgraded to 0.12 syntax
4+
# ---------------------------------------------------------------------------------------------------------------------
5+
6+
terraform {
7+
# This module is now only being tested with Terraform 0.13.x. However, to make upgrading easier, we are setting
8+
# 0.12.26 as the minimum version, as that version added support for required_providers with source URLs, making it
9+
# forwards compatible with 0.13.x code.
10+
required_version = ">= 0.12.26"
11+
}
12+
13+
# ---------------------------------------------------------------------------------------------------------------------
14+
# DEPLOY A PUBSUB TOPIC AND SUBSCRIPTION
15+
# See test/gcp/terraform_gcp_pubsub_example_test.go for how to write automated tests for this code.
16+
# ---------------------------------------------------------------------------------------------------------------------
17+
18+
# website::tag::1:: Deploy a Pub/Sub topic
19+
resource "google_pubsub_topic" "example" {
20+
project = var.gcp_project_id
21+
name = var.topic_name
22+
}
23+
24+
# website::tag::2:: Create a Subscription to the topic so we can verify it
25+
resource "google_pubsub_subscription" "example" {
26+
project = var.gcp_project_id
27+
name = var.subscription_name
28+
topic = google_pubsub_topic.example.name
29+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
output "topic_name" {
2+
description = "The name of the Pub/Sub topic created."
3+
value = google_pubsub_topic.example.name
4+
}
5+
6+
output "subscription_name" {
7+
description = "The name of the Pub/Sub subscription created."
8+
value = google_pubsub_subscription.example.name
9+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# ---------------------------------------------------------------------------------------------------------------------
2+
# ENVIRONMENT VARIABLES
3+
# You must define the following environment variables.
4+
# ---------------------------------------------------------------------------------------------------------------------
5+
6+
# GOOGLE_CREDENTIALS
7+
# or
8+
# GOOGLE_APPLICATION_CREDENTIALS
9+
10+
variable "gcp_project_id" {
11+
description = "The ID of the GCP project in which these resources will be created."
12+
}
13+
14+
# ---------------------------------------------------------------------------------------------------------------------
15+
# REQUIRED PARAMETERS
16+
# You must provide a value for each of these parameters.
17+
# ---------------------------------------------------------------------------------------------------------------------
18+
# (none)
19+
20+
# ---------------------------------------------------------------------------------------------------------------------
21+
# OPTIONAL PARAMETERS
22+
# These parameters have reasonable defaults.
23+
# ---------------------------------------------------------------------------------------------------------------------
24+
25+
variable "topic_name" {
26+
description = "The name of the Pub/Sub topic to create."
27+
type = string
28+
default = "terratest-example-topic"
29+
}
30+
31+
variable "subscription_name" {
32+
description = "The name of the Pub/Sub subscription to create."
33+
type = string
34+
default = "terratest-example-sub"
35+
}

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ require (
4545

4646
require (
4747
cloud.google.com/go/cloudbuild v1.19.0
48+
cloud.google.com/go/pubsub v1.45.1
4849
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0
4950
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1
5051
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/appcontainers/armappcontainers/v3 v3.0.0

go.sum

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,16 @@ cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdB
1313
cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
1414
cloud.google.com/go/iam v1.2.2 h1:ozUSofHUGf/F4tCNy/mu9tHLTaxZFLOUiKzjcgWHGIA=
1515
cloud.google.com/go/iam v1.2.2/go.mod h1:0Ys8ccaZHdI1dEUilwzqng/6ps2YB6vRsjIe00/+6JY=
16+
cloud.google.com/go/kms v1.20.1 h1:og29Wv59uf2FVaZlesaiDAqHFzHaoUyHI3HYp9VUHVg=
17+
cloud.google.com/go/kms v1.20.1/go.mod h1:LywpNiVCvzYNJWS9JUcGJSVTNSwPwi0vBAotzDqn2nc=
1618
cloud.google.com/go/logging v1.12.0 h1:ex1igYcGFd4S/RZWOCU51StlIEuey5bjqwH9ZYjHibk=
1719
cloud.google.com/go/logging v1.12.0/go.mod h1:wwYBt5HlYP1InnrtYI0wtwttpVU1rifnMT7RejksUAM=
1820
cloud.google.com/go/longrunning v0.6.2 h1:xjDfh1pQcWPEvnfjZmwjKQEcHnpz6lHjfy7Fo0MK+hc=
1921
cloud.google.com/go/longrunning v0.6.2/go.mod h1:k/vIs83RN4bE3YCswdXC5PFfWVILjm3hpEUlSko4PiI=
2022
cloud.google.com/go/monitoring v1.21.2 h1:FChwVtClH19E7pJ+e0xUhJPGksctZNVOk2UhMmblmdU=
2123
cloud.google.com/go/monitoring v1.21.2/go.mod h1:hS3pXvaG8KgWTSz+dAdyzPrGUYmi2Q+WFX8g2hqVEZU=
24+
cloud.google.com/go/pubsub v1.45.1 h1:ZC/UzYcrmK12THWn1P72z+Pnp2vu/zCZRXyhAfP1hJY=
25+
cloud.google.com/go/pubsub v1.45.1/go.mod h1:3bn7fTmzZFwaUjllitv1WlsNMkqBgGUb3UdMhI54eCc=
2226
cloud.google.com/go/storage v1.47.0 h1:ajqgt30fnOMmLfWfu1PWcb+V9Dxz6n+9WKjdNg5R4HM=
2327
cloud.google.com/go/storage v1.47.0/go.mod h1:Ks0vP374w0PW6jOUameJbapbQKXqkjGd/OJRp2fb9IQ=
2428
cloud.google.com/go/trace v1.11.2 h1:4ZmaBdL8Ng/ajrgKqY5jfvzqMXbrDcBsUGXOT9aqTtI=
@@ -528,6 +532,8 @@ github.com/zclconf/go-cty v1.15.0 h1:tTCRWxsexYUmtt/wVxgDClUe+uQusuI443uL6e+5sXQ
528532
github.com/zclconf/go-cty v1.15.0/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
529533
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo=
530534
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM=
535+
go.einride.tech/aip v0.68.0 h1:4seM66oLzTpz50u4K1zlJyOXQ3tCzcJN7I22tKkjipw=
536+
go.einride.tech/aip v0.68.0/go.mod h1:7y9FF8VtPWqpxuAxl0KQWqaULxW4zFIesD6zF5RIHHg=
531537
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
532538
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
533539
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=

modules/gcp/pubsub.go

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
package gcp
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"cloud.google.com/go/pubsub"
8+
"github.com/gruntwork-io/terratest/modules/logger"
9+
"github.com/gruntwork-io/terratest/modules/testing"
10+
)
11+
12+
// AssertTopicExistsContext checks if the given Pub/Sub topic exists and fails the test if it does not.
13+
// The ctx parameter supports cancellation and timeouts.
14+
func AssertTopicExistsContext(t testing.TestingT, ctx context.Context, projectID string, topicName string) {
15+
err := AssertTopicExistsContextE(t, ctx, projectID, topicName)
16+
if err != nil {
17+
t.Fatal(err)
18+
}
19+
}
20+
21+
// AssertTopicExistsContextE checks if the given Pub/Sub topic exists and returns an error if it does not.
22+
// The ctx parameter supports cancellation and timeouts.
23+
func AssertTopicExistsContextE(t testing.TestingT, ctx context.Context, projectID string, topicName string) error {
24+
logger.Default.Logf(t, "Verifying Pub/Sub topic %s exists in project %s", topicName, projectID)
25+
26+
client, err := newPubSubClient(ctx, projectID)
27+
if err != nil {
28+
return err
29+
}
30+
defer func() { _ = client.Close() }()
31+
32+
exists, err := client.Topic(topicName).Exists(ctx)
33+
if err != nil {
34+
return fmt.Errorf("failed to check if Pub/Sub topic %s exists in project %s: %w", topicName, projectID, err)
35+
}
36+
37+
if !exists {
38+
return fmt.Errorf("Pub/Sub topic %s does not exist in project %s", topicName, projectID)
39+
}
40+
41+
return nil
42+
}
43+
44+
// AssertSubscriptionExistsContext checks if the given Pub/Sub subscription exists and fails the test if it does not.
45+
// The ctx parameter supports cancellation and timeouts.
46+
func AssertSubscriptionExistsContext(t testing.TestingT, ctx context.Context, projectID string, subscriptionName string) {
47+
err := AssertSubscriptionExistsContextE(t, ctx, projectID, subscriptionName)
48+
if err != nil {
49+
t.Fatal(err)
50+
}
51+
}
52+
53+
// AssertSubscriptionExistsContextE checks if the given Pub/Sub subscription exists and returns an error if it does not.
54+
// The ctx parameter supports cancellation and timeouts.
55+
func AssertSubscriptionExistsContextE(t testing.TestingT, ctx context.Context, projectID string, subscriptionName string) error {
56+
logger.Default.Logf(t, "Verifying Pub/Sub subscription %s exists in project %s", subscriptionName, projectID)
57+
58+
client, err := newPubSubClient(ctx, projectID)
59+
if err != nil {
60+
return err
61+
}
62+
defer func() { _ = client.Close() }()
63+
64+
exists, err := client.Subscription(subscriptionName).Exists(ctx)
65+
if err != nil {
66+
return fmt.Errorf("failed to check if Pub/Sub subscription %s exists in project %s: %w", subscriptionName, projectID, err)
67+
}
68+
69+
if !exists {
70+
return fmt.Errorf("Pub/Sub subscription %s does not exist in project %s", subscriptionName, projectID)
71+
}
72+
73+
return nil
74+
}
75+
76+
// CreateTopicContext creates a new Pub/Sub topic and fails the test if it cannot.
77+
// The ctx parameter supports cancellation and timeouts.
78+
func CreateTopicContext(t testing.TestingT, ctx context.Context, projectID string, topicName string) {
79+
err := CreateTopicContextE(t, ctx, projectID, topicName)
80+
if err != nil {
81+
t.Fatal(err)
82+
}
83+
}
84+
85+
// CreateTopicContextE creates a new Pub/Sub topic and returns an error if it fails.
86+
// The ctx parameter supports cancellation and timeouts.
87+
func CreateTopicContextE(t testing.TestingT, ctx context.Context, projectID string, topicName string) error {
88+
logger.Default.Logf(t, "Creating Pub/Sub topic %s in project %s", topicName, projectID)
89+
90+
client, err := newPubSubClient(ctx, projectID)
91+
if err != nil {
92+
return err
93+
}
94+
defer func() { _ = client.Close() }()
95+
96+
_, err = client.CreateTopic(ctx, topicName)
97+
if err != nil {
98+
return fmt.Errorf("failed to create Pub/Sub topic %s in project %s: %w", topicName, projectID, err)
99+
}
100+
101+
return nil
102+
}
103+
104+
// DeleteTopicContext deletes the given Pub/Sub topic and fails the test if it cannot.
105+
// The ctx parameter supports cancellation and timeouts.
106+
func DeleteTopicContext(t testing.TestingT, ctx context.Context, projectID string, topicName string) {
107+
err := DeleteTopicContextE(t, ctx, projectID, topicName)
108+
if err != nil {
109+
t.Fatal(err)
110+
}
111+
}
112+
113+
// DeleteTopicContextE deletes the given Pub/Sub topic and returns an error if it fails.
114+
// The ctx parameter supports cancellation and timeouts.
115+
func DeleteTopicContextE(t testing.TestingT, ctx context.Context, projectID string, topicName string) error {
116+
logger.Default.Logf(t, "Deleting Pub/Sub topic %s in project %s", topicName, projectID)
117+
118+
client, err := newPubSubClient(ctx, projectID)
119+
if err != nil {
120+
return err
121+
}
122+
defer func() { _ = client.Close() }()
123+
124+
if err := client.Topic(topicName).Delete(ctx); err != nil {
125+
return fmt.Errorf("failed to delete Pub/Sub topic %s in project %s: %w", topicName, projectID, err)
126+
}
127+
128+
return nil
129+
}
130+
131+
// CreateSubscriptionContext creates a new Pub/Sub subscription on the given topic and fails the test if it cannot.
132+
// The ctx parameter supports cancellation and timeouts.
133+
func CreateSubscriptionContext(t testing.TestingT, ctx context.Context, projectID string, subscriptionName string, topicName string) {
134+
err := CreateSubscriptionContextE(t, ctx, projectID, subscriptionName, topicName)
135+
if err != nil {
136+
t.Fatal(err)
137+
}
138+
}
139+
140+
// CreateSubscriptionContextE creates a new Pub/Sub subscription on the given topic and returns an error if it fails.
141+
// The ctx parameter supports cancellation and timeouts.
142+
func CreateSubscriptionContextE(t testing.TestingT, ctx context.Context, projectID string, subscriptionName string, topicName string) error {
143+
logger.Default.Logf(t, "Creating Pub/Sub subscription %s on topic %s in project %s", subscriptionName, topicName, projectID)
144+
145+
client, err := newPubSubClient(ctx, projectID)
146+
if err != nil {
147+
return err
148+
}
149+
defer func() { _ = client.Close() }()
150+
151+
_, err = client.CreateSubscription(ctx, subscriptionName, pubsub.SubscriptionConfig{
152+
Topic: client.Topic(topicName),
153+
})
154+
if err != nil {
155+
return fmt.Errorf("failed to create Pub/Sub subscription %s in project %s: %w", subscriptionName, projectID, err)
156+
}
157+
158+
return nil
159+
}
160+
161+
// DeleteSubscriptionContext deletes the given Pub/Sub subscription and fails the test if it cannot.
162+
// The ctx parameter supports cancellation and timeouts.
163+
func DeleteSubscriptionContext(t testing.TestingT, ctx context.Context, projectID string, subscriptionName string) {
164+
err := DeleteSubscriptionContextE(t, ctx, projectID, subscriptionName)
165+
if err != nil {
166+
t.Fatal(err)
167+
}
168+
}
169+
170+
// DeleteSubscriptionContextE deletes the given Pub/Sub subscription and returns an error if it fails.
171+
// The ctx parameter supports cancellation and timeouts.
172+
func DeleteSubscriptionContextE(t testing.TestingT, ctx context.Context, projectID string, subscriptionName string) error {
173+
logger.Default.Logf(t, "Deleting Pub/Sub subscription %s in project %s", subscriptionName, projectID)
174+
175+
client, err := newPubSubClient(ctx, projectID)
176+
if err != nil {
177+
return err
178+
}
179+
defer func() { _ = client.Close() }()
180+
181+
if err := client.Subscription(subscriptionName).Delete(ctx); err != nil {
182+
return fmt.Errorf("failed to delete Pub/Sub subscription %s in project %s: %w", subscriptionName, projectID, err)
183+
}
184+
185+
return nil
186+
}
187+
188+
// newPubSubClient creates a new Pub/Sub client using the provided project ID and global GCP auth options.
189+
func newPubSubClient(ctx context.Context, projectID string) (*pubsub.Client, error) {
190+
client, err := pubsub.NewClient(ctx, projectID, withOptions()...)
191+
if err != nil {
192+
return nil, err
193+
}
194+
return client, nil
195+
}

0 commit comments

Comments
 (0)