Skip to content

Commit f05fb98

Browse files
authored
Add plural data source for retrieving Artifact Registry tags (#14702)
1 parent ff347cc commit f05fb98

4 files changed

Lines changed: 262 additions & 1 deletion

File tree

mmv1/third_party/terraform/provider/provider_mmv1_resources.go.tmpl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,10 @@ var handwrittenDatasources = map[string]*schema.Resource{
2929
"google_artifact_registry_docker_image": artifactregistry.DataSourceArtifactRegistryDockerImage(),
3030
"google_artifact_registry_docker_images": artifactregistry.DataSourceArtifactRegistryDockerImages(),
3131
"google_artifact_registry_locations": artifactregistry.DataSourceGoogleArtifactRegistryLocations(),
32+
"google_artifact_registry_package": artifactregistry.DataSourceArtifactRegistryPackage(),
3233
"google_artifact_registry_repositories": artifactregistry.DataSourceArtifactRegistryRepositories(),
3334
"google_artifact_registry_repository": artifactregistry.DataSourceArtifactRegistryRepository(),
34-
"google_artifact_registry_package": artifactregistry.DataSourceArtifactRegistryPackage(),
35+
"google_artifact_registry_tags": artifactregistry.DataSourceArtifactRegistryTags(),
3536
"google_artifact_registry_version": artifactregistry.DataSourceArtifactRegistryVersion(),
3637
"google_apphub_discovered_workload": apphub.DataSourceApphubDiscoveredWorkload(),
3738
"google_app_engine_default_service_account": appengine.DataSourceGoogleAppEngineDefaultServiceAccount(),
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
package artifactregistry
2+
3+
import (
4+
"fmt"
5+
"net/http"
6+
"net/url"
7+
8+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
9+
"github.com/hashicorp/terraform-provider-google/google/tpgresource"
10+
transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport"
11+
)
12+
13+
func DataSourceArtifactRegistryTags() *schema.Resource {
14+
return &schema.Resource{
15+
Read: dataSourceArtifactRegistryTagsRead,
16+
Schema: map[string]*schema.Schema{
17+
"location": {
18+
Type: schema.TypeString,
19+
Required: true,
20+
},
21+
"repository_id": {
22+
Type: schema.TypeString,
23+
Required: true,
24+
},
25+
"package_name": {
26+
Type: schema.TypeString,
27+
Required: true,
28+
},
29+
"filter": {
30+
Type: schema.TypeString,
31+
Optional: true,
32+
},
33+
"project": {
34+
Type: schema.TypeString,
35+
Optional: true,
36+
},
37+
"tags": {
38+
Type: schema.TypeList,
39+
Computed: true,
40+
Elem: &schema.Resource{
41+
Schema: map[string]*schema.Schema{
42+
"name": {
43+
Type: schema.TypeString,
44+
Computed: true,
45+
},
46+
"version": {
47+
Type: schema.TypeString,
48+
Computed: true,
49+
},
50+
},
51+
},
52+
},
53+
},
54+
}
55+
}
56+
57+
func dataSourceArtifactRegistryTagsRead(d *schema.ResourceData, meta interface{}) error {
58+
config := meta.(*transport_tpg.Config)
59+
userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent)
60+
if err != nil {
61+
return err
62+
}
63+
64+
project, err := tpgresource.GetProject(d, config)
65+
if err != nil {
66+
return err
67+
}
68+
69+
basePath, err := tpgresource.ReplaceVars(d, config, "{{ArtifactRegistryBasePath}}")
70+
if err != nil {
71+
return fmt.Errorf("Error setting Artifact Registry base path: %s", err)
72+
}
73+
74+
resourcePath, err := tpgresource.ReplaceVars(d, config, fmt.Sprintf("projects/{{project}}/locations/{{location}}/repositories/{{repository_id}}/packages/{{package_name}}/tags"))
75+
if err != nil {
76+
return fmt.Errorf("Error setting resource path: %s", err)
77+
}
78+
79+
urlRequest := basePath + resourcePath
80+
81+
filter := ""
82+
if v, ok := d.GetOk("filter"); ok {
83+
filter = v.(string)
84+
85+
u, err := url.Parse(urlRequest)
86+
if err != nil {
87+
return fmt.Errorf("Error parsing URL: %s", err)
88+
}
89+
90+
q := u.Query()
91+
q.Set("filter", filter)
92+
u.RawQuery = q.Encode()
93+
urlRequest = u.String()
94+
}
95+
96+
headers := make(http.Header)
97+
tags := make([]map[string]interface{}, 0)
98+
pageToken := ""
99+
100+
for {
101+
u, err := url.Parse(urlRequest)
102+
if err != nil {
103+
return fmt.Errorf("Error parsing URL: %s", err)
104+
}
105+
106+
q := u.Query()
107+
if pageToken != "" {
108+
q.Set("pageToken", pageToken)
109+
}
110+
u.RawQuery = q.Encode()
111+
112+
res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{
113+
Config: config,
114+
Method: "GET",
115+
RawURL: u.String(),
116+
UserAgent: userAgent,
117+
Headers: headers,
118+
})
119+
120+
if err != nil {
121+
return fmt.Errorf("Error listing Artifact Registry tags: %s", err)
122+
}
123+
124+
if items, ok := res["tags"].([]interface{}); ok {
125+
for _, item := range items {
126+
tag := item.(map[string]interface{})
127+
128+
annotations := make(map[string]string)
129+
if anno, ok := tag["annotations"].(map[string]interface{}); ok {
130+
for k, v := range anno {
131+
if val, ok := v.(string); ok {
132+
annotations[k] = val
133+
}
134+
}
135+
}
136+
137+
getString := func(m map[string]interface{}, key string) string {
138+
if v, ok := m[key].(string); ok {
139+
return v
140+
}
141+
return ""
142+
}
143+
144+
tags = append(tags, map[string]interface{}{
145+
"name": getString(tag, "name"),
146+
"version": getString(tag, "version"),
147+
})
148+
}
149+
}
150+
151+
if nextToken, ok := res["nextPageToken"].(string); ok && nextToken != "" {
152+
pageToken = nextToken
153+
} else {
154+
break
155+
}
156+
}
157+
158+
if err := d.Set("project", project); err != nil {
159+
return fmt.Errorf("Error setting project: %s", err)
160+
}
161+
162+
if err := d.Set("tags", tags); err != nil {
163+
return fmt.Errorf("Error setting Artifact Registry tags: %s", err)
164+
}
165+
166+
d.SetId(resourcePath)
167+
168+
return nil
169+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package artifactregistry_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
7+
"github.com/hashicorp/terraform-provider-google/google/acctest"
8+
)
9+
10+
func TestAccDataSourceArtifactRegistryTags_basic(t *testing.T) {
11+
t.Parallel()
12+
13+
acctest.VcrTest(t, resource.TestCase{
14+
PreCheck: func() { acctest.AccTestPreCheck(t) },
15+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
16+
Steps: []resource.TestStep{
17+
{
18+
Config: testAccDataSourceArtifactRegistryTagsConfig,
19+
Check: resource.ComposeTestCheckFunc(
20+
resource.TestCheckResourceAttrSet("data.google_artifact_registry_tags.this", "project"),
21+
resource.TestCheckResourceAttrSet("data.google_artifact_registry_tags.this", "location"),
22+
resource.TestCheckResourceAttrSet("data.google_artifact_registry_tags.this", "repository_id"),
23+
resource.TestCheckResourceAttrSet("data.google_artifact_registry_tags.this", "package_name"),
24+
resource.TestCheckResourceAttrSet("data.google_artifact_registry_tags.this", "tags.0.name"),
25+
resource.TestCheckResourceAttrSet("data.google_artifact_registry_tags.this", "tags.0.version"),
26+
),
27+
},
28+
},
29+
})
30+
}
31+
32+
// Test the data source against the public AR repos
33+
// https://console.cloud.google.com/artifacts/docker/cloudrun/us/container
34+
// https://console.cloud.google.com/artifacts/docker/go-containerregistry/us/gcr.io
35+
const testAccDataSourceArtifactRegistryTagsConfig = `
36+
data "google_artifact_registry_tags" "this" {
37+
project = "go-containerregistry"
38+
location = "us"
39+
repository_id = "gcr.io"
40+
package_name = "gcrane"
41+
# Filter doesn't work with gcr.io
42+
# filter = "name=\"projects/go-containerregistry/locations/us/repositories/gcr.io/packages/gcrane/tags/latest\""
43+
}
44+
`
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
---
2+
subcategory: "Artifact Registry"
3+
description: |-
4+
Get information about tags within a Google Artifact Registry package.
5+
---
6+
7+
# google_artifact_registry_tags
8+
9+
Get information about Artifact Registry tags.
10+
See [the official documentation](https://cloud.google.com/artifact-registry/docs/overview)
11+
and [API](https://cloud.google.com/artifact-registry/docs/reference/rest/v1/projects.locations.repositories.packages.tags/list).
12+
13+
## Example Usage
14+
15+
```hcl
16+
data "google_artifact_registry_tags" "my_tags" {
17+
location = "us-central1"
18+
repository_id = "example-repo"
19+
package_name = "example-package"
20+
}
21+
```
22+
23+
## Argument Reference
24+
25+
The following arguments are supported:
26+
27+
* `location` - (Required) The location of the Artifact Registry repository.
28+
29+
* `repository_id` - (Required) The last part of the repository name to fetch from.
30+
31+
* `package_name` - (Required) The name of the package.
32+
33+
* `filter` - (Optional) An expression for filtering the results of the request. Filter rules are case insensitive. The fields eligible for filtering are `name` and `version`. Further information can be found in the [REST API](https://cloud.google.com/artifact-registry/docs/reference/rest/v1/projects.locations.repositories.packages.tags/list#query-parameters).
34+
35+
* `project` - (Optional) The project ID in which the resource belongs. If it is not provided, the provider project is used.
36+
37+
## Attributes Reference
38+
39+
The following attributes are exported:
40+
41+
* `tags` - A list of all retrieved Artifact Registry tags. Structure is [defined below](#nested_tags).
42+
43+
<a name="nested_tags"></a>The `tags` block supports:
44+
45+
* `name` - The name of the tag, for example: `projects/p1/locations/us-central1/repositories/repo1/packages/pkg1/tags/tag1`. If the package part contains slashes, the slashes are escaped.
46+
47+
* `version` - The version of the tag.

0 commit comments

Comments
 (0)