Skip to content

Commit bf43461

Browse files
authored
Merge pull request hashicorp#38172 from aristosvo/f-aws_route53profiles
`route53profiles` - `r/association`, `r/resource_association` and `d/profiles` resources added
2 parents 418af0f + 919d31c commit bf43461

20 files changed

+2274
-2
lines changed

.changelog/38172.txt

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
```release-note:new-resource
2+
aws_route53profiles_profile
3+
```
4+
5+
```release-note:new-resource
6+
aws_route53profiles_association
7+
```
8+
9+
```release-note:new-resource
10+
aws_route53profiles_resource_association
11+
```
12+
13+
```release-note:new-data-source
14+
aws_route53profiles_profiles
15+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,302 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package route53profiles
5+
6+
import (
7+
"context"
8+
"errors"
9+
"time"
10+
11+
"github.com/aws/aws-sdk-go-v2/aws"
12+
"github.com/aws/aws-sdk-go-v2/service/route53profiles"
13+
awstypes "github.com/aws/aws-sdk-go-v2/service/route53profiles/types"
14+
"github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts"
15+
"github.com/hashicorp/terraform-plugin-framework/path"
16+
"github.com/hashicorp/terraform-plugin-framework/resource"
17+
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
18+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
19+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
20+
"github.com/hashicorp/terraform-plugin-framework/types"
21+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry"
22+
"github.com/hashicorp/terraform-provider-aws/internal/create"
23+
"github.com/hashicorp/terraform-provider-aws/internal/enum"
24+
"github.com/hashicorp/terraform-provider-aws/internal/errs"
25+
"github.com/hashicorp/terraform-provider-aws/internal/framework"
26+
"github.com/hashicorp/terraform-provider-aws/internal/framework/flex"
27+
fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types"
28+
"github.com/hashicorp/terraform-provider-aws/internal/tfresource"
29+
"github.com/hashicorp/terraform-provider-aws/names"
30+
)
31+
32+
// @FrameworkResource("aws_route53profiles_association", name="Association")
33+
func newResourceAssociation(_ context.Context) (resource.ResourceWithConfigure, error) {
34+
r := &resourceAssociation{}
35+
36+
r.SetDefaultCreateTimeout(30 * time.Minute)
37+
r.SetDefaultUpdateTimeout(30 * time.Minute)
38+
r.SetDefaultDeleteTimeout(30 * time.Minute)
39+
40+
return r, nil
41+
}
42+
43+
const (
44+
ResNameAssociation = "Association"
45+
)
46+
47+
type resourceAssociation struct {
48+
framework.ResourceWithConfigure
49+
framework.WithNoUpdate
50+
framework.WithTimeouts
51+
framework.WithImportByID
52+
}
53+
54+
func (r *resourceAssociation) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
55+
resp.TypeName = "aws_route53profiles_association"
56+
}
57+
58+
func (r *resourceAssociation) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
59+
resp.Schema = schema.Schema{
60+
Attributes: map[string]schema.Attribute{
61+
names.AttrID: framework.IDAttribute(),
62+
names.AttrName: schema.StringAttribute{
63+
Required: true,
64+
PlanModifiers: []planmodifier.String{
65+
stringplanmodifier.RequiresReplace(),
66+
},
67+
},
68+
names.AttrOwnerID: schema.StringAttribute{
69+
Computed: true,
70+
},
71+
"profile_id": schema.StringAttribute{
72+
Required: true,
73+
PlanModifiers: []planmodifier.String{
74+
stringplanmodifier.RequiresReplace(),
75+
},
76+
},
77+
names.AttrResourceID: schema.StringAttribute{
78+
Required: true,
79+
PlanModifiers: []planmodifier.String{
80+
stringplanmodifier.RequiresReplace(),
81+
},
82+
},
83+
names.AttrStatus: schema.StringAttribute{
84+
CustomType: fwtypes.StringEnumType[awstypes.ProfileStatus](),
85+
Computed: true,
86+
},
87+
names.AttrStatusMessage: schema.StringAttribute{
88+
Computed: true,
89+
},
90+
},
91+
Blocks: map[string]schema.Block{
92+
names.AttrTimeouts: timeouts.Block(ctx, timeouts.Opts{
93+
Create: true,
94+
Read: true,
95+
Delete: true,
96+
}),
97+
},
98+
}
99+
}
100+
101+
func (r *resourceAssociation) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
102+
conn := r.Meta().Route53ProfilesClient(ctx)
103+
104+
var state associationResourceModel
105+
resp.Diagnostics.Append(req.Plan.Get(ctx, &state)...)
106+
if resp.Diagnostics.HasError() {
107+
return
108+
}
109+
110+
input := &route53profiles.AssociateProfileInput{}
111+
resp.Diagnostics.Append(flex.Expand(ctx, state, input)...)
112+
113+
out, err := conn.AssociateProfile(ctx, input)
114+
if err != nil {
115+
resp.Diagnostics.AddError(
116+
create.ProblemStandardMessage(names.Route53Profiles, create.ErrActionCreating, ResNameAssociation, state.Name.String(), err),
117+
err.Error(),
118+
)
119+
return
120+
}
121+
if out == nil || out.ProfileAssociation == nil {
122+
resp.Diagnostics.AddError(
123+
create.ProblemStandardMessage(names.Route53Profiles, create.ErrActionCreating, ResNameAssociation, state.Name.String(), nil),
124+
errors.New("empty output").Error(),
125+
)
126+
return
127+
}
128+
129+
state.ID = flex.StringToFramework(ctx, out.ProfileAssociation.Id)
130+
131+
createTimeout := r.CreateTimeout(ctx, state.Timeouts)
132+
profileAssociation, err := waitAssociationCreated(ctx, conn, state.ID.ValueString(), createTimeout)
133+
if err != nil {
134+
resp.Diagnostics.AddError(
135+
create.ProblemStandardMessage(names.Route53Profiles, create.ErrActionWaitingForCreation, ResNameAssociation, state.Name.String(), err),
136+
err.Error(),
137+
)
138+
return
139+
}
140+
141+
resp.Diagnostics.Append(flex.Flatten(ctx, profileAssociation, &state)...)
142+
if resp.Diagnostics.HasError() {
143+
return
144+
}
145+
146+
resp.Diagnostics.Append(resp.State.Set(ctx, state)...)
147+
}
148+
149+
func (r *resourceAssociation) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
150+
conn := r.Meta().Route53ProfilesClient(ctx)
151+
152+
var state associationResourceModel
153+
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
154+
if resp.Diagnostics.HasError() {
155+
return
156+
}
157+
158+
out, err := findAssociationByID(ctx, conn, state.ID.ValueString())
159+
if tfresource.NotFound(err) {
160+
resp.State.RemoveResource(ctx)
161+
return
162+
}
163+
if err != nil {
164+
resp.Diagnostics.AddError(
165+
create.ProblemStandardMessage(names.Route53Profiles, create.ErrActionSetting, ResNameAssociation, state.ID.String(), err),
166+
err.Error(),
167+
)
168+
return
169+
}
170+
171+
resp.Diagnostics.Append(flex.Flatten(ctx, out, &state)...)
172+
if resp.Diagnostics.HasError() {
173+
return
174+
}
175+
176+
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
177+
}
178+
179+
func (r *resourceAssociation) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
180+
conn := r.Meta().Route53ProfilesClient(ctx)
181+
182+
var state associationResourceModel
183+
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
184+
if resp.Diagnostics.HasError() {
185+
return
186+
}
187+
188+
in := &route53profiles.DisassociateProfileInput{
189+
ProfileId: state.ProfileID.ValueStringPointer(),
190+
ResourceId: state.ResourceID.ValueStringPointer(),
191+
}
192+
193+
_, err := conn.DisassociateProfile(ctx, in)
194+
if err != nil {
195+
if errs.IsA[*awstypes.ResourceNotFoundException](err) {
196+
return
197+
}
198+
resp.Diagnostics.AddError(
199+
create.ProblemStandardMessage(names.Route53Profiles, create.ErrActionDeleting, ResNameAssociation, state.ID.String(), err),
200+
err.Error(),
201+
)
202+
return
203+
}
204+
205+
deleteTimeout := r.DeleteTimeout(ctx, state.Timeouts)
206+
_, err = waitAssociationDeleted(ctx, conn, state.ID.ValueString(), deleteTimeout)
207+
if err != nil {
208+
resp.Diagnostics.AddError(
209+
create.ProblemStandardMessage(names.Route53Profiles, create.ErrActionWaitingForDeletion, ResNameAssociation, state.ID.String(), err),
210+
err.Error(),
211+
)
212+
return
213+
}
214+
}
215+
216+
func (r *resourceAssociation) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
217+
resource.ImportStatePassthroughID(ctx, path.Root(names.AttrID), req, resp)
218+
}
219+
220+
func waitAssociationCreated(ctx context.Context, conn *route53profiles.Client, id string, timeout time.Duration) (*awstypes.ProfileAssociation, error) {
221+
stateConf := &retry.StateChangeConf{
222+
Pending: enum.Slice(awstypes.ProfileStatusCreating),
223+
Target: enum.Slice(awstypes.ProfileStatusComplete),
224+
Refresh: statusAssociation(ctx, conn, id),
225+
Timeout: timeout,
226+
NotFoundChecks: 20,
227+
ContinuousTargetOccurence: 2,
228+
}
229+
230+
outputRaw, err := stateConf.WaitForStateContext(ctx)
231+
if out, ok := outputRaw.(*awstypes.ProfileAssociation); ok {
232+
return out, err
233+
}
234+
235+
return nil, err
236+
}
237+
238+
func waitAssociationDeleted(ctx context.Context, conn *route53profiles.Client, id string, timeout time.Duration) (*awstypes.ProfileAssociation, error) {
239+
stateConf := &retry.StateChangeConf{
240+
Pending: enum.Slice(awstypes.ProfileStatusDeleting),
241+
Target: []string{},
242+
Refresh: statusAssociation(ctx, conn, id),
243+
Timeout: timeout,
244+
}
245+
246+
outputRaw, err := stateConf.WaitForStateContext(ctx)
247+
if out, ok := outputRaw.(*awstypes.ProfileAssociation); ok {
248+
return out, err
249+
}
250+
251+
return nil, err
252+
}
253+
254+
func statusAssociation(ctx context.Context, conn *route53profiles.Client, id string) retry.StateRefreshFunc {
255+
return func() (interface{}, string, error) {
256+
out, err := findAssociationByID(ctx, conn, id)
257+
if tfresource.NotFound(err) {
258+
return nil, "", nil
259+
}
260+
261+
if err != nil {
262+
return nil, "", err
263+
}
264+
265+
return out, string(out.Status), nil
266+
}
267+
}
268+
269+
func findAssociationByID(ctx context.Context, conn *route53profiles.Client, id string) (*awstypes.ProfileAssociation, error) {
270+
in := &route53profiles.GetProfileAssociationInput{
271+
ProfileAssociationId: aws.String(id),
272+
}
273+
274+
out, err := conn.GetProfileAssociation(ctx, in)
275+
if err != nil {
276+
if errs.IsA[*awstypes.ResourceNotFoundException](err) {
277+
return nil, &retry.NotFoundError{
278+
LastError: err,
279+
LastRequest: in,
280+
}
281+
}
282+
283+
return nil, err
284+
}
285+
286+
if out == nil || out.ProfileAssociation == nil {
287+
return nil, tfresource.NewEmptyResultError(in)
288+
}
289+
290+
return out.ProfileAssociation, nil
291+
}
292+
293+
type associationResourceModel struct {
294+
ID types.String `tfsdk:"id"`
295+
ResourceID types.String `tfsdk:"resource_id"`
296+
ProfileID types.String `tfsdk:"profile_id"`
297+
Name types.String `tfsdk:"name"`
298+
OwnerId types.String `tfsdk:"owner_id"`
299+
Status fwtypes.StringEnum[awstypes.ProfileStatus] `tfsdk:"status"`
300+
StatusMessage types.String `tfsdk:"status_message"`
301+
Timeouts timeouts.Value `tfsdk:"timeouts"`
302+
}

0 commit comments

Comments
 (0)