Skip to content

Commit 90a98dd

Browse files
New Resource: aws_ecrpublic_repository (#16865)
* new aws_ecrpublic_repository resource
1 parent 7546a7f commit 90a98dd

6 files changed

Lines changed: 949 additions & 0 deletions

File tree

.changelog/16865.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:new-resource
2+
aws_ecrpublic_repository
3+
```

aws/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -640,6 +640,7 @@ func Provider() *schema.Provider {
640640
"aws_ec2_transit_gateway_vpc_attachment": resourceAwsEc2TransitGatewayVpcAttachment(),
641641
"aws_ec2_transit_gateway_vpc_attachment_accepter": resourceAwsEc2TransitGatewayVpcAttachmentAccepter(),
642642
"aws_ecr_lifecycle_policy": resourceAwsEcrLifecyclePolicy(),
643+
"aws_ecrpublic_repository": resourceAwsEcrPublicRepository(),
643644
"aws_ecr_repository": resourceAwsEcrRepository(),
644645
"aws_ecr_repository_policy": resourceAwsEcrRepositoryPolicy(),
645646
"aws_ecs_capacity_provider": resourceAwsEcsCapacityProvider(),
Lines changed: 371 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,371 @@
1+
package aws
2+
3+
import (
4+
"encoding/base64"
5+
"fmt"
6+
"log"
7+
"regexp"
8+
"time"
9+
10+
"github.com/aws/aws-sdk-go/aws"
11+
"github.com/aws/aws-sdk-go/service/ecrpublic"
12+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
13+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
14+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
15+
)
16+
17+
func resourceAwsEcrPublicRepository() *schema.Resource {
18+
return &schema.Resource{
19+
Create: resourceAwsEcrPublicRepositoryCreate,
20+
Read: resourceAwsEcrPublicRepositoryRead,
21+
Update: resourceAwsEcrPublicRepositoryUpdate,
22+
Delete: resourceAwsEcrPublicRepositoryDelete,
23+
Importer: &schema.ResourceImporter{
24+
State: schema.ImportStatePassthrough,
25+
},
26+
27+
Timeouts: &schema.ResourceTimeout{
28+
Delete: schema.DefaultTimeout(20 * time.Minute),
29+
},
30+
31+
Schema: map[string]*schema.Schema{
32+
"repository_name": {
33+
Type: schema.TypeString,
34+
Required: true,
35+
ForceNew: true,
36+
ValidateFunc: validation.All(
37+
validation.StringLenBetween(2, 205),
38+
validation.StringMatch(regexp.MustCompile(`(?:[a-z0-9]+(?:[._-][a-z0-9]+)*/)*[a-z0-9]+(?:[._-][a-z0-9]+)*`), "see: https://docs.aws.amazon.com/AmazonECRPublic/latest/APIReference/API_CreateRepository.html#API_CreateRepository_RequestSyntax"),
39+
),
40+
},
41+
"catalog_data": {
42+
Type: schema.TypeList,
43+
MaxItems: 1,
44+
Optional: true,
45+
Elem: &schema.Resource{
46+
Schema: map[string]*schema.Schema{
47+
"about_text": {
48+
Type: schema.TypeString,
49+
Optional: true,
50+
ValidateFunc: validation.StringLenBetween(0, 10240),
51+
},
52+
"architectures": {
53+
Type: schema.TypeSet,
54+
Optional: true,
55+
MaxItems: 50,
56+
Elem: &schema.Schema{
57+
Type: schema.TypeString,
58+
},
59+
},
60+
"description": {
61+
Type: schema.TypeString,
62+
Optional: true,
63+
ValidateFunc: validation.StringLenBetween(0, 1024),
64+
},
65+
"logo_image_blob": {
66+
Type: schema.TypeString,
67+
Optional: true,
68+
Computed: true,
69+
},
70+
"operating_systems": {
71+
Type: schema.TypeSet,
72+
Optional: true,
73+
MaxItems: 50,
74+
Elem: &schema.Schema{
75+
Type: schema.TypeString,
76+
},
77+
},
78+
"usage_text": {
79+
Type: schema.TypeString,
80+
Optional: true,
81+
ValidateFunc: validation.StringLenBetween(0, 10240),
82+
},
83+
},
84+
},
85+
DiffSuppressFunc: suppressMissingOptionalConfigurationBlock,
86+
},
87+
"force_destroy": {
88+
Type: schema.TypeBool,
89+
Optional: true,
90+
Default: false,
91+
},
92+
"registry_id": {
93+
Type: schema.TypeString,
94+
Computed: true,
95+
},
96+
"arn": {
97+
Type: schema.TypeString,
98+
Computed: true,
99+
},
100+
"repository_uri": {
101+
Type: schema.TypeString,
102+
Computed: true,
103+
},
104+
},
105+
}
106+
}
107+
108+
func resourceAwsEcrPublicRepositoryCreate(d *schema.ResourceData, meta interface{}) error {
109+
conn := meta.(*AWSClient).ecrpublicconn
110+
111+
input := ecrpublic.CreateRepositoryInput{
112+
RepositoryName: aws.String(d.Get("repository_name").(string)),
113+
}
114+
115+
if v, ok := d.GetOk("catalog_data"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil {
116+
input.CatalogData = expandEcrPublicRepositoryCatalogData(v.([]interface{})[0].(map[string]interface{}))
117+
}
118+
119+
log.Printf("[DEBUG] Creating ECR Public repository: %#v", input)
120+
121+
out, err := conn.CreateRepository(&input)
122+
if err != nil {
123+
return fmt.Errorf("error creating ECR Public repository: %s", err)
124+
}
125+
126+
if out == nil {
127+
return fmt.Errorf("error creating ECR Public Repository: empty response")
128+
}
129+
130+
repository := out.Repository
131+
132+
log.Printf("[DEBUG] ECR Public repository created: %q", aws.StringValue(repository.RepositoryArn))
133+
134+
d.SetId(aws.StringValue(repository.RepositoryName))
135+
136+
return resourceAwsEcrPublicRepositoryRead(d, meta)
137+
}
138+
139+
func resourceAwsEcrPublicRepositoryRead(d *schema.ResourceData, meta interface{}) error {
140+
conn := meta.(*AWSClient).ecrpublicconn
141+
142+
log.Printf("[DEBUG] Reading ECR Public repository %s", d.Id())
143+
var out *ecrpublic.DescribeRepositoriesOutput
144+
input := &ecrpublic.DescribeRepositoriesInput{
145+
RepositoryNames: aws.StringSlice([]string{d.Id()}),
146+
}
147+
148+
var err error
149+
err = resource.Retry(1*time.Minute, func() *resource.RetryError {
150+
out, err = conn.DescribeRepositories(input)
151+
if d.IsNewResource() && isAWSErr(err, ecrpublic.ErrCodeRepositoryNotFoundException, "") {
152+
return resource.RetryableError(err)
153+
}
154+
if err != nil {
155+
return resource.NonRetryableError(err)
156+
}
157+
return nil
158+
})
159+
160+
if isResourceTimeoutError(err) {
161+
out, err = conn.DescribeRepositories(input)
162+
}
163+
164+
if !d.IsNewResource() && isAWSErr(err, ecrpublic.ErrCodeRepositoryNotFoundException, "") {
165+
log.Printf("[WARN] ECR Public Repository (%s) not found, removing from state", d.Id())
166+
d.SetId("")
167+
return nil
168+
}
169+
170+
if err != nil {
171+
return fmt.Errorf("error reading ECR Public repository: %s", err)
172+
}
173+
174+
if out == nil || len(out.Repositories) == 0 || out.Repositories[0] == nil {
175+
return fmt.Errorf("error reading ECR Public Repository (%s): empty response", d.Id())
176+
}
177+
178+
repository := out.Repositories[0]
179+
180+
d.Set("repository_name", d.Id())
181+
d.Set("registry_id", repository.RegistryId)
182+
d.Set("arn", repository.RepositoryArn)
183+
d.Set("repository_uri", repository.RepositoryUri)
184+
185+
if v, ok := d.GetOk("force_destroy"); ok {
186+
d.Set("force_destroy", v.(bool))
187+
} else {
188+
d.Set("force_destroy", false)
189+
}
190+
191+
var catalogOut *ecrpublic.GetRepositoryCatalogDataOutput
192+
catalogInput := &ecrpublic.GetRepositoryCatalogDataInput{
193+
RepositoryName: aws.String(d.Id()),
194+
RegistryId: repository.RegistryId,
195+
}
196+
197+
catalogOut, err = conn.GetRepositoryCatalogData(catalogInput)
198+
199+
if err != nil {
200+
return fmt.Errorf("error reading catalog data for ECR Public repository: %s", err)
201+
}
202+
203+
if catalogOut != nil {
204+
flatCatalogData := flattenEcrPublicRepositoryCatalogData(catalogOut)
205+
if catalogData, ok := d.GetOk("catalog_data"); ok && len(catalogData.([]interface{})) > 0 && catalogData.([]interface{})[0] != nil {
206+
catalogDataMap := catalogData.([]interface{})[0].(map[string]interface{})
207+
if v, ok := catalogDataMap["logo_image_blob"].(string); ok && len(v) > 0 {
208+
flatCatalogData["logo_image_blob"] = v
209+
}
210+
}
211+
d.Set("catalog_data", []interface{}{flatCatalogData})
212+
} else {
213+
d.Set("catalog_data", nil)
214+
}
215+
216+
return nil
217+
}
218+
219+
func resourceAwsEcrPublicRepositoryDelete(d *schema.ResourceData, meta interface{}) error {
220+
conn := meta.(*AWSClient).ecrpublicconn
221+
222+
deleteInput := &ecrpublic.DeleteRepositoryInput{
223+
RepositoryName: aws.String(d.Id()),
224+
RegistryId: aws.String(d.Get("registry_id").(string)),
225+
}
226+
227+
if v, ok := d.GetOk("force_destroy"); ok {
228+
deleteInput.Force = aws.Bool(v.(bool))
229+
}
230+
231+
_, err := conn.DeleteRepository(deleteInput)
232+
233+
if err != nil {
234+
if isAWSErr(err, ecrpublic.ErrCodeRepositoryNotFoundException, "") {
235+
return nil
236+
}
237+
return fmt.Errorf("error deleting ECR Public repository: %s", err)
238+
}
239+
240+
log.Printf("[DEBUG] Waiting for ECR Public Repository %q to be deleted", d.Id())
241+
input := &ecrpublic.DescribeRepositoriesInput{
242+
RepositoryNames: aws.StringSlice([]string{d.Id()}),
243+
}
244+
err = resource.Retry(d.Timeout(schema.TimeoutDelete), func() *resource.RetryError {
245+
_, err = conn.DescribeRepositories(input)
246+
if err != nil {
247+
if isAWSErr(err, ecrpublic.ErrCodeRepositoryNotFoundException, "") {
248+
return nil
249+
}
250+
return resource.NonRetryableError(err)
251+
}
252+
253+
return resource.RetryableError(fmt.Errorf("%q: Timeout while waiting for the ECR Public Repository to be deleted", d.Id()))
254+
})
255+
if isResourceTimeoutError(err) {
256+
_, err = conn.DescribeRepositories(input)
257+
}
258+
259+
if isAWSErr(err, ecrpublic.ErrCodeRepositoryNotFoundException, "") {
260+
return nil
261+
}
262+
263+
if err != nil {
264+
return fmt.Errorf("error deleting ECR Public repository: %s", err)
265+
}
266+
267+
log.Printf("[DEBUG] repository %q deleted.", d.Get("repository_name").(string))
268+
269+
return nil
270+
}
271+
272+
func resourceAwsEcrPublicRepositoryUpdate(d *schema.ResourceData, meta interface{}) error {
273+
conn := meta.(*AWSClient).ecrpublicconn
274+
275+
if d.HasChange("catalog_data") {
276+
if err := resourceAwsEcrPublicRepositoryUpdateCatalogData(conn, d); err != nil {
277+
return err
278+
}
279+
}
280+
281+
return resourceAwsEcrPublicRepositoryRead(d, meta)
282+
}
283+
284+
func flattenEcrPublicRepositoryCatalogData(apiObject *ecrpublic.GetRepositoryCatalogDataOutput) map[string]interface{} {
285+
if apiObject == nil {
286+
return nil
287+
}
288+
289+
catalogData := apiObject.CatalogData
290+
291+
tfMap := map[string]interface{}{}
292+
293+
if v := catalogData.AboutText; v != nil {
294+
tfMap["about_text"] = aws.StringValue(v)
295+
}
296+
297+
if v := catalogData.Architectures; v != nil {
298+
tfMap["architectures"] = aws.StringValueSlice(v)
299+
}
300+
301+
if v := catalogData.Description; v != nil {
302+
tfMap["description"] = aws.StringValue(v)
303+
}
304+
305+
if v := catalogData.OperatingSystems; v != nil {
306+
tfMap["operating_systems"] = aws.StringValueSlice(v)
307+
}
308+
309+
if v := catalogData.UsageText; v != nil {
310+
tfMap["usage_text"] = aws.StringValue(v)
311+
}
312+
313+
return tfMap
314+
}
315+
316+
func expandEcrPublicRepositoryCatalogData(tfMap map[string]interface{}) *ecrpublic.RepositoryCatalogDataInput {
317+
if tfMap == nil {
318+
return nil
319+
}
320+
321+
repositoryCatalogDataInput := &ecrpublic.RepositoryCatalogDataInput{}
322+
323+
if v, ok := tfMap["about_text"].(string); ok && v != "" {
324+
repositoryCatalogDataInput.AboutText = aws.String(v)
325+
}
326+
327+
if v, ok := tfMap["architectures"].(*schema.Set); ok {
328+
repositoryCatalogDataInput.Architectures = expandStringSet(v)
329+
}
330+
331+
if v, ok := tfMap["description"].(string); ok && v != "" {
332+
repositoryCatalogDataInput.Description = aws.String(v)
333+
}
334+
335+
if v, ok := tfMap["logo_image_blob"].(string); ok && len(v) > 0 {
336+
data, _ := base64.StdEncoding.DecodeString(v)
337+
repositoryCatalogDataInput.LogoImageBlob = data
338+
}
339+
340+
if v, ok := tfMap["operating_systems"].(*schema.Set); ok {
341+
repositoryCatalogDataInput.OperatingSystems = expandStringSet(v)
342+
}
343+
344+
if v, ok := tfMap["usage_text"].(string); ok && v != "" {
345+
repositoryCatalogDataInput.UsageText = aws.String(v)
346+
}
347+
348+
return repositoryCatalogDataInput
349+
}
350+
351+
func resourceAwsEcrPublicRepositoryUpdateCatalogData(conn *ecrpublic.ECRPublic, d *schema.ResourceData) error {
352+
353+
if d.HasChange("catalog_data") {
354+
355+
if v, ok := d.GetOk("catalog_data"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil {
356+
input := ecrpublic.PutRepositoryCatalogDataInput{
357+
RepositoryName: aws.String(d.Id()),
358+
RegistryId: aws.String(d.Get("registry_id").(string)),
359+
CatalogData: expandEcrPublicRepositoryCatalogData(v.([]interface{})[0].(map[string]interface{})),
360+
}
361+
362+
_, err := conn.PutRepositoryCatalogData(&input)
363+
364+
if err != nil {
365+
return fmt.Errorf("error updating catalog data for repository(%s): %s", d.Id(), err)
366+
}
367+
}
368+
}
369+
370+
return nil
371+
}

0 commit comments

Comments
 (0)