From 96d5bdadf6cc3e2242dd85554e285303c2eb0325 Mon Sep 17 00:00:00 2001 From: Wilson Tan <86353208+wilsontcm69@users.noreply.github.com> Date: Thu, 3 Oct 2024 23:20:44 +0800 Subject: [PATCH] Add IPv6 Resource (#14) --- cdnetworks/provider.go | 1 + cdnetworks/resource_ipv6.go | 193 ++++++++++++++++++ cdnetworksapi/domain_configuration.go | 44 ++++ docs/resources/ipv6.md | 29 +++ .../st-cdnetworks_ipv6_config/resource.tf | 4 + 5 files changed, 271 insertions(+) create mode 100644 cdnetworks/resource_ipv6.go create mode 100644 docs/resources/ipv6.md create mode 100644 examples/resources/st-cdnetworks_ipv6_config/resource.tf diff --git a/cdnetworks/provider.go b/cdnetworks/provider.go index 12db4b4..98db405 100644 --- a/cdnetworks/provider.go +++ b/cdnetworks/provider.go @@ -173,5 +173,6 @@ func (p *cdnetworksProvider) Resources(_ context.Context) []func() resource.Reso NewQueryStringUrlConfigResource, NewHttpCodeCacheConfigResource, NewIgnoreProtocolResource, + NewIpv6Resource, } } diff --git a/cdnetworks/resource_ipv6.go b/cdnetworks/resource_ipv6.go new file mode 100644 index 0000000..f1a6445 --- /dev/null +++ b/cdnetworks/resource_ipv6.go @@ -0,0 +1,193 @@ +package cdnetworks + +import ( + "context" + "errors" + "time" + + "github.com/cenkalti/backoff/v4" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/types" + + "github.com/myklst/terraform-provider-st-cdnetworks/cdnetworksapi" +) + +type ipv6ResourceModel struct { + DomainId types.String `tfsdk:"domain_id" json:"domain_id"` + EnableIpv6 types.Bool `tfsdk:"enable_ipv6" json:"enable_ipv6"` +} + +type ipv6Resource struct { + client *cdnetworksapi.Client +} + +var ( + _ resource.Resource = &ipv6Resource{} + _ resource.ResourceWithConfigure = &ipv6Resource{} +) + +func NewIpv6Resource() resource.Resource { + return &ipv6Resource{} +} + +func (r *ipv6Resource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_ipv6_config" +} + +func (r *ipv6Resource) Schema(_ context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "Update DNS region IP version, available value: 'V6'", + Attributes: map[string]schema.Attribute{ + "domain_id": &schema.StringAttribute{ + Description: "Domain id", + Required: true, + }, + "enable_ipv6": &schema.BoolAttribute{ + Description: "Ipv6", + Required: true, + }, + }, + } +} + +func (r *ipv6Resource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + r.client = req.ProviderData.(*cdnetworksapi.Client) +} + +func (r *ipv6Resource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var model ipv6ResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &model)...) + if resp.Diagnostics.HasError() { + return + } + + // By Default, IPv4 is enabled + ipVersions := []string{"V4"} + if model.EnableIpv6.ValueBool() { + ipVersions = append(ipVersions, "V6") + } + + addIPv6ConfigResponse, err := r.client.UpdateIPv6Config(model.DomainId.ValueString(), cdnetworksapi.UpdateIPv6ConfigRequest{ + IpVersion: ipVersions, + }) + if err != nil { + resp.Diagnostics.AddError("[API ERROR] Failed to Add IPv6", err.Error()) + return + } + + if *addIPv6ConfigResponse.Code != "0" { + resp.Diagnostics.AddError("[API ERROR] Error response non 0 code, failed to Add IPv6", *addIPv6ConfigResponse.Message) + return + } + + if r.waitForIPv6Config(ctx, model) { + resp.State.Set(ctx, &model) + } else { + resp.Diagnostics.AddError("[API ERROR] Failed to Add IPv6", "Timeout") + } +} + +func (r *ipv6Resource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state *ipv6ResourceModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + queryIPv6Response, err := r.client.QueryIPv6Config(state.DomainId.ValueString()) + if err != nil { + resp.Diagnostics.AddError("[API ERROR] Fail to Query IPv6", err.Error()) + return + } + + state.DomainId = types.StringPointerValue(queryIPv6Response.DomainId) + state.EnableIpv6 = types.BoolPointerValue(queryIPv6Response.UseIpv6) + resp.Diagnostics.Append(resp.State.Set(ctx, state)...) +} + +func (r *ipv6Resource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var state, plan ipv6ResourceModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + ipVersions := []string{"V4"} + if plan.EnableIpv6.ValueBool() { + ipVersions = append(ipVersions, "V6") + } + + updateIpv6Response, err := r.client.UpdateIPv6Config(state.DomainId.ValueString(), cdnetworksapi.UpdateIPv6ConfigRequest{ + IpVersion: ipVersions, + }) + if err != nil { + resp.Diagnostics.AddError("[API ERROR] Failed to Update IPv6", err.Error()) + return + } + + if *updateIpv6Response.Code != "0" { + resp.Diagnostics.AddError("[API ERROR] Failed to Update IPv6", *updateIpv6Response.Message) + return + } + + if r.waitForIPv6Config(ctx, plan) { + state.DomainId = plan.DomainId + state.EnableIpv6 = plan.EnableIpv6 + resp.State.Set(ctx, state) + } else { + resp.Diagnostics.AddError("[API ERROR] Failed to Add IPv6", "Timeout") + } +} + +func (r *ipv6Resource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var model ipv6ResourceModel + diags := req.State.Get(ctx, &model) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + // Due to only have Update() func, force it revert to ipv4 only. + deleteIPv6Response, err := r.client.UpdateIPv6Config(model.DomainId.ValueString(), cdnetworksapi.UpdateIPv6ConfigRequest{ + IpVersion: []string{"V4"}, + }) + if err != nil { + resp.Diagnostics.AddError("[API ERROR] Failed to Del IPv6", err.Error()) + return + } + + if *deleteIPv6Response.Code != "0" { + resp.Diagnostics.AddError("[API ERROR] Failed to Del IPv6", *deleteIPv6Response.Message) + return + } +} + +func (r *ipv6Resource) waitForIPv6Config(ctx context.Context, model ipv6ResourceModel) bool { + checkStatus := func() error { + queryIPv6Response, err := r.client.QueryIPv6Config(model.DomainId.ValueString()) + if err != nil { + return err + } + + if *queryIPv6Response.UseIpv6 == model.EnableIpv6.ValueBool() { + return nil + } + + return errors.New("deployment is in progress") + } + + s := backoff.NewExponentialBackOff() + s.InitialInterval = 10 * time.Second + s.MaxElapsedTime = 5 * time.Minute + + err := backoff.Retry(checkStatus, s) + return err == nil +} diff --git a/cdnetworksapi/domain_configuration.go b/cdnetworksapi/domain_configuration.go index 04c5b19..34745a0 100644 --- a/cdnetworksapi/domain_configuration.go +++ b/cdnetworksapi/domain_configuration.go @@ -190,6 +190,50 @@ func (c *Client) UpdateBackToOriginRewriteConfig(domainId string, request Update return } +//////////////////////////////////////////////////////////////////////////////// +// IPv6 Config +//////////////////////////////////////////////////////////////////////////////// + +// QueryIPv6Config 查询域名是否使用ipv6资源 + +type QueryIPv6ConfigResponse struct { + DomainId *string `json:"domain-id" xml:"domain-id"` + DomainName *string `json:"domain-name" xml:"domain-name"` + UseIpv6 *bool `json:"use-ipv6" xml:"use-ipv6"` +} + +func (c *Client) QueryIPv6Config(domainId string) (response QueryIPv6ConfigResponse, err error) { + var baseResp *BaseResponse + baseResp, err = c.DoXmlApiRequest(Request{ + Method: HttpGet, + Path: "/api/domain/ipv6/" + domainId, + }, &response) + _ = baseResp + return +} + +// UpdateIPv6Config 修改域名是否使用IPv6配置 + +type UpdateIPv6ConfigRequest struct { + XMLName xml.Name `json:"-" xml:"domain"` + IpVersion []string `json:"ipVersion,omitempty" xml:"ipVersion,omitempty"` +} + +type UpdateIPv6ConfigResponse struct { + Code *string `json:"code" xml:"code"` + Message *string `json:"message" xml:"message"` +} + +// In Version 2024-09-20 16:30:05, the API only supports JSON format. +func (c *Client) UpdateIPv6Config(domainId string, request UpdateIPv6ConfigRequest) (response UpdateIPv6ConfigResponse, err error) { + _, err = c.DoJsonApiRequest(Request{ + Method: HttpPut, + Path: "/api/config/ipversion/" + domainId, + Body: request, + }, &response) + return +} + //////////////////////////////////////////////////////////////////////////////// // Http2 Settings //////////////////////////////////////////////////////////////////////////////// diff --git a/docs/resources/ipv6.md b/docs/resources/ipv6.md new file mode 100644 index 0000000..06b67fa --- /dev/null +++ b/docs/resources/ipv6.md @@ -0,0 +1,29 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "st-cdnetworks_ipv6_config Resource - st-cdnetworks" +subcategory: "" +description: |- + The resource control the IPv6 protocol for domain +--- + +# st-cdnetworks_ssl_certificate (Resource) + +The resource control the IPv6 protocol for domain + +## Example Usage + +```terraform +resource "st-cdnetworks_ipv6_config" "test" { + domain_id = "5048000" + enable_ipv6 = true +} +``` + + +## Schema + +### Required + +- `domain_id` (String) Domain ID +- `enable_ipv6` (Boolean) Enable IPv6. + diff --git a/examples/resources/st-cdnetworks_ipv6_config/resource.tf b/examples/resources/st-cdnetworks_ipv6_config/resource.tf new file mode 100644 index 0000000..9fd5db8 --- /dev/null +++ b/examples/resources/st-cdnetworks_ipv6_config/resource.tf @@ -0,0 +1,4 @@ +resource "st-cdnetworks_ipv6_config" "test" { + domain_id = "5048000" + enable_ipv6 = true +}