Skip to content

Commit 1e3c865

Browse files
compute_snapshot datasource added (#6527) (#12671)
Signed-off-by: Modular Magician <[email protected]> Signed-off-by: Modular Magician <[email protected]>
1 parent c5278cf commit 1e3c865

File tree

5 files changed

+372
-0
lines changed

5 files changed

+372
-0
lines changed

.changelog/6527.txt

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:new-datasource
2+
`google_compute_snapshot'
3+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
package google
2+
3+
import (
4+
"fmt"
5+
"sort"
6+
7+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
8+
"google.golang.org/api/compute/v1"
9+
)
10+
11+
func dataSourceGoogleComputeSnapshot() *schema.Resource {
12+
13+
// Generate datasource schema from resource
14+
dsSchema := datasourceSchemaFromResourceSchema(resourceComputeSnapshot().Schema)
15+
16+
dsSchema["filter"] = &schema.Schema{
17+
Type: schema.TypeString,
18+
Optional: true,
19+
}
20+
dsSchema["most_recent"] = &schema.Schema{
21+
Type: schema.TypeBool,
22+
Optional: true,
23+
}
24+
25+
// Set 'Optional' schema elements
26+
addOptionalFieldsToSchema(dsSchema, "name", "filter", "most_recent", "project")
27+
28+
dsSchema["name"].ExactlyOneOf = []string{"name", "filter"}
29+
dsSchema["filter"].ExactlyOneOf = []string{"name", "filter"}
30+
31+
return &schema.Resource{
32+
Read: dataSourceGoogleComputeSnapshotRead,
33+
Schema: dsSchema,
34+
}
35+
}
36+
37+
func dataSourceGoogleComputeSnapshotRead(d *schema.ResourceData, meta interface{}) error {
38+
config := meta.(*Config)
39+
40+
project, err := getProject(d, config)
41+
if err != nil {
42+
return err
43+
}
44+
45+
if v, ok := d.GetOk("name"); ok {
46+
return retrieveSnapshot(d, meta, project, v.(string))
47+
}
48+
49+
if v, ok := d.GetOk("filter"); ok {
50+
userAgent, err := generateUserAgentString(d, config.userAgent)
51+
if err != nil {
52+
return err
53+
}
54+
55+
projectGetCall := config.NewResourceManagerClient(userAgent).Projects.Get(project)
56+
57+
if config.UserProjectOverride {
58+
billingProject := project
59+
60+
// err == nil indicates that the billing_project value was found
61+
if bp, err := getBillingProject(d, config); err == nil {
62+
billingProject = bp
63+
}
64+
projectGetCall.Header().Add("X-Goog-User-Project", billingProject)
65+
}
66+
67+
//handling the pagination locally
68+
allSnapshots := make([]*compute.Snapshot, 0)
69+
token := ""
70+
for paginate := true; paginate; {
71+
snapshots, err := config.NewComputeClient(userAgent).Snapshots.List(project).Filter(v.(string)).PageToken(token).Do()
72+
if err != nil {
73+
return fmt.Errorf("error retrieving list of snapshots: %s", err)
74+
75+
}
76+
allSnapshots = append(allSnapshots, snapshots.Items...)
77+
78+
token = snapshots.NextPageToken
79+
paginate = token != ""
80+
}
81+
82+
mostRecent := d.Get("most_recent").(bool)
83+
if mostRecent {
84+
sort.Sort(ByCreationTimestampOfSnapshot(allSnapshots))
85+
}
86+
87+
count := len(allSnapshots)
88+
if count == 1 || count > 1 && mostRecent {
89+
return retrieveSnapshot(d, meta, project, allSnapshots[0].Name)
90+
}
91+
92+
return fmt.Errorf("your filter has returned %d snapshot(s). Please refine your filter or set most_recent to return exactly one snapshot", len(allSnapshots))
93+
94+
}
95+
96+
return fmt.Errorf("one of name or filter must be set")
97+
}
98+
99+
func retrieveSnapshot(d *schema.ResourceData, meta interface{}, project, name string) error {
100+
d.SetId("projects/" + project + "/global/snapshots/" + name)
101+
d.Set("name", name)
102+
return resourceComputeSnapshotRead(d, meta)
103+
}
104+
105+
// ByCreationTimestamp implements sort.Interface for []*Snapshot based on
106+
// the CreationTimestamp field.
107+
type ByCreationTimestampOfSnapshot []*compute.Snapshot
108+
109+
func (a ByCreationTimestampOfSnapshot) Len() int { return len(a) }
110+
func (a ByCreationTimestampOfSnapshot) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
111+
func (a ByCreationTimestampOfSnapshot) Less(i, j int) bool {
112+
return a[i].CreationTimestamp > a[j].CreationTimestamp
113+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
package google
2+
3+
import (
4+
"testing"
5+
6+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
7+
)
8+
9+
func TestAccSnapshotDatasource_name(t *testing.T) {
10+
t.Parallel()
11+
12+
vcrTest(t, resource.TestCase{
13+
PreCheck: func() { testAccPreCheck(t) },
14+
Providers: testAccProviders,
15+
Steps: []resource.TestStep{
16+
{
17+
Config: testAccSnapshot_name(getTestProjectFromEnv(), randString(t, 10)),
18+
Check: resource.ComposeTestCheckFunc(
19+
checkDataSourceStateMatchesResourceStateWithIgnores(
20+
"data.google_compute_snapshot.default",
21+
"google_compute_snapshot.default",
22+
map[string]struct{}{"zone": {}},
23+
),
24+
),
25+
},
26+
},
27+
})
28+
}
29+
30+
func TestAccSnapshotDatasource_filter(t *testing.T) {
31+
t.Parallel()
32+
33+
vcrTest(t, resource.TestCase{
34+
PreCheck: func() { testAccPreCheck(t) },
35+
Providers: testAccProviders,
36+
Steps: []resource.TestStep{
37+
{
38+
Config: testAccSnapshot_filter(getTestProjectFromEnv(), randString(t, 10)),
39+
Check: resource.ComposeTestCheckFunc(
40+
checkDataSourceStateMatchesResourceStateWithIgnores(
41+
"data.google_compute_snapshot.default",
42+
"google_compute_snapshot.c",
43+
map[string]struct{}{"zone": {}},
44+
),
45+
),
46+
},
47+
},
48+
})
49+
}
50+
51+
func TestAccSnapshotDatasource_filterMostRecent(t *testing.T) {
52+
t.Parallel()
53+
54+
vcrTest(t, resource.TestCase{
55+
PreCheck: func() { testAccPreCheck(t) },
56+
Providers: testAccProviders,
57+
Steps: []resource.TestStep{
58+
{
59+
Config: testAccSnapshot_filter_mostRecent(getTestProjectFromEnv(), randString(t, 10)),
60+
Check: resource.ComposeTestCheckFunc(
61+
checkDataSourceStateMatchesResourceStateWithIgnores(
62+
"data.google_compute_snapshot.default",
63+
"google_compute_snapshot.c",
64+
map[string]struct{}{"zone": {}},
65+
),
66+
),
67+
},
68+
},
69+
})
70+
}
71+
72+
func testAccSnapshot_name(project, suffix string) string {
73+
return Nprintf(`
74+
data "google_compute_image" "tf-test-image" {
75+
family = "debian-11"
76+
project = "debian-cloud"
77+
}
78+
resource "google_compute_disk" "tf-test-disk" {
79+
name = "debian-disk-%{suffix}"
80+
image = data.google_compute_image.tf-test-image.self_link
81+
size = 10
82+
type = "pd-ssd"
83+
zone = "us-central1-a"
84+
}
85+
86+
resource "google_compute_snapshot" "default" {
87+
name = "tf-test-snapshot-%{suffix}"
88+
description = "Example snapshot."
89+
source_disk = google_compute_disk.tf-test-disk.id
90+
zone = "us-central1-a"
91+
labels = {
92+
my_label = "value"
93+
}
94+
storage_locations = ["us-central1"]
95+
}
96+
data "google_compute_snapshot" "default" {
97+
project = "%{project}"
98+
name = google_compute_snapshot.default.name
99+
}
100+
101+
`, map[string]interface{}{"project": project, "suffix": suffix})
102+
}
103+
104+
func testAccSnapshot_filter(project, suffix string) string {
105+
return Nprintf(`
106+
data "google_compute_image" "tf-test-image" {
107+
family = "debian-11"
108+
project = "debian-cloud"
109+
}
110+
resource "google_compute_disk" "tf-test-disk" {
111+
name = "debian-disk-%{suffix}"
112+
image = data.google_compute_image.tf-test-image.self_link
113+
size = 10
114+
type = "pd-ssd"
115+
zone = "us-central1-a"
116+
}
117+
resource "google_compute_snapshot" "a" {
118+
name = "tf-test-snapshot-a-%{suffix}"
119+
description = "Example snapshot."
120+
source_disk = google_compute_disk.tf-test-disk.id
121+
zone = "us-central1-a"
122+
labels = {
123+
my_label = "a"
124+
}
125+
storage_locations = ["us-central1"]
126+
}
127+
resource "google_compute_snapshot" "b" {
128+
name = "tf-test-snapshot-b-%{suffix}"
129+
description = "Example snapshot."
130+
source_disk = google_compute_disk.tf-test-disk.id
131+
zone = "us-central1-a"
132+
labels = {
133+
my_label = "b"
134+
}
135+
storage_locations = ["us-central1"]
136+
}
137+
resource "google_compute_snapshot" "c" {
138+
name = "tf-test-snapshot-c-%{suffix}"
139+
description = "Example snapshot."
140+
source_disk = google_compute_disk.tf-test-disk.id
141+
zone = "us-central1-a"
142+
labels = {
143+
my_label = "c"
144+
}
145+
storage_locations = ["us-central1"]
146+
}
147+
data "google_compute_snapshot" "default" {
148+
project = "%{project}"
149+
filter = "name = tf-test-snapshot-c-%{suffix}"
150+
depends_on = [google_compute_snapshot.c]
151+
}
152+
`, map[string]interface{}{"project": project, "suffix": suffix})
153+
}
154+
155+
func testAccSnapshot_filter_mostRecent(project, suffix string) string {
156+
return Nprintf(`
157+
data "google_compute_image" "tf-test-image" {
158+
family = "debian-11"
159+
project = "debian-cloud"
160+
}
161+
resource "google_compute_disk" "tf-test-disk" {
162+
name = "debian-disk-%{suffix}"
163+
image = data.google_compute_image.tf-test-image.self_link
164+
size = 10
165+
type = "pd-ssd"
166+
zone = "us-central1-a"
167+
}
168+
resource "google_compute_snapshot" "a" {
169+
name = "tf-test-snapshot-a-%{suffix}"
170+
description = "Example snapshot."
171+
source_disk = google_compute_disk.tf-test-disk.id
172+
zone = "us-central1-a"
173+
labels = {
174+
my_label = "a"
175+
}
176+
storage_locations = ["us-central1"]
177+
}
178+
resource "google_compute_snapshot" "b" {
179+
name = "tf-test-snapshot-b-%{suffix}"
180+
description = "Example snapshot."
181+
source_disk = google_compute_disk.tf-test-disk.id
182+
zone = "us-central1-a"
183+
labels = {
184+
my_label = "b"
185+
}
186+
storage_locations = ["us-central1"]
187+
}
188+
resource "google_compute_snapshot" "c" {
189+
name = "tf-test-snapshot-c-%{suffix}"
190+
description = "Example snapshot."
191+
source_disk = google_compute_disk.tf-test-disk.id
192+
zone = "us-central1-a"
193+
labels = {
194+
my_label = "c"
195+
}
196+
storage_locations = ["us-central1"]
197+
}
198+
data "google_compute_snapshot" "default" {
199+
project = "%{project}"
200+
most_recent = true
201+
filter = "name = tf-test-snapshot-c-%{suffix}"
202+
depends_on = [google_compute_snapshot.c]
203+
}
204+
`, map[string]interface{}{"project": project, "suffix": suffix})
205+
}

google/provider.go

+1
Original file line numberDiff line numberDiff line change
@@ -811,6 +811,7 @@ func Provider() *schema.Provider {
811811
"google_compute_resource_policy": dataSourceGoogleComputeResourcePolicy(),
812812
"google_compute_router": dataSourceGoogleComputeRouter(),
813813
"google_compute_router_status": dataSourceGoogleComputeRouterStatus(),
814+
"google_compute_snapshot": dataSourceGoogleComputeSnapshot(),
814815
"google_compute_ssl_certificate": dataSourceGoogleComputeSslCertificate(),
815816
"google_compute_ssl_policy": dataSourceGoogleComputeSslPolicy(),
816817
"google_compute_subnetwork": dataSourceGoogleComputeSubnetwork(),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
---
2+
subcategory: "Compute Engine"
3+
page_title: "Google: google_compute_snapshot"
4+
description: |-
5+
Get information about a Google Compute Snapshot.
6+
---
7+
8+
# google\_compute\_snapshot
9+
10+
To get more information about Snapshot, see:
11+
12+
* [API documentation](https://cloud.google.com/compute/docs/reference/rest/v1/snapshots)
13+
* How-to Guides
14+
* [Official Documentation](https://cloud.google.com/compute/docs/disks/create-snapshots)
15+
16+
## Example Usage
17+
18+
```hcl
19+
#by name
20+
data "google_compute_snapshot" "snapshot" {
21+
name = "my-snapshot"
22+
}
23+
24+
# using a filter
25+
data "google_compute_snapshot" "latest-snapshot" {
26+
filter = "name != my-snapshot"
27+
most_recent = true
28+
}
29+
```
30+
31+
## Argument Reference
32+
33+
The following arguments are supported:
34+
35+
* `name` - (Optional) The name of the compute snapshot. One of `name` or `filter` must be provided.
36+
37+
* `filter` - (Optional) A filter to retrieve the compute snapshot.
38+
See [gcloud topic filters](https://cloud.google.com/sdk/gcloud/reference/topic/filters) for reference.
39+
If multiple compute snapshot match, either adjust the filter or specify `most_recent`. One of `name` or `filter` must be provided.
40+
41+
* `most_recent` - (Optional) If `filter` is provided, ensures the most recent snapshot is returned when multiple compute snapshot match.
42+
43+
- - -
44+
45+
* `project` - (Optional) The ID of the project in which the resource belongs.
46+
If it is not provided, the provider project is used.
47+
48+
## Attributes Reference
49+
50+
See [google_compute_snapshot](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_snapshot) resource for details of the available attributes.

0 commit comments

Comments
 (0)