Add sqlserverflex instance (#381)
* Draft implementation sqlserverflex instance * Finish implementation * Fix acc test * Changes after review
This commit is contained in:
parent
6db3a550e6
commit
335e1cabb6
18 changed files with 2400 additions and 104 deletions
61
docs/data-sources/sqlserverflex_instance.md
Normal file
61
docs/data-sources/sqlserverflex_instance.md
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
---
|
||||
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||
page_title: "stackit_sqlserverflex_instance Data Source - stackit"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
MongoDB Flex instance data source schema. Must have a region specified in the provider configuration.
|
||||
---
|
||||
|
||||
# stackit_sqlserverflex_instance (Data Source)
|
||||
|
||||
MongoDB Flex instance data source schema. Must have a `region` specified in the provider configuration.
|
||||
|
||||
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
|
||||
### Required
|
||||
|
||||
- `instance_id` (String) ID of the MongoDB Flex instance.
|
||||
- `project_id` (String) STACKIT project ID to which the instance is associated.
|
||||
|
||||
### Read-Only
|
||||
|
||||
- `acl` (List of String) The Access Control List (ACL) for the MongoDB Flex instance.
|
||||
- `backup_schedule` (String) The backup schedule. Should follow the cron scheduling system format (e.g. "0 0 * * *").
|
||||
- `flavor` (Attributes) (see [below for nested schema](#nestedatt--flavor))
|
||||
- `id` (String) Terraform's internal data source. ID. It is structured as "`project_id`,`instance_id`".
|
||||
- `name` (String) Instance name.
|
||||
- `options` (Attributes) Custom parameters for the MongoDB Flex instance. (see [below for nested schema](#nestedatt--options))
|
||||
- `replicas` (Number)
|
||||
- `storage` (Attributes) (see [below for nested schema](#nestedatt--storage))
|
||||
- `version` (String)
|
||||
|
||||
<a id="nestedatt--flavor"></a>
|
||||
### Nested Schema for `flavor`
|
||||
|
||||
Read-Only:
|
||||
|
||||
- `cpu` (Number)
|
||||
- `description` (String)
|
||||
- `id` (String)
|
||||
- `ram` (Number)
|
||||
|
||||
|
||||
<a id="nestedatt--options"></a>
|
||||
### Nested Schema for `options`
|
||||
|
||||
Read-Only:
|
||||
|
||||
- `edition` (String)
|
||||
- `retention_days` (Number)
|
||||
|
||||
|
||||
<a id="nestedatt--storage"></a>
|
||||
### Nested Schema for `storage`
|
||||
|
||||
Read-Only:
|
||||
|
||||
- `class` (String)
|
||||
- `size` (Number)
|
||||
|
|
@ -165,4 +165,5 @@ Note: AWS specific checks must be skipped as they do not work on STACKIT. For de
|
|||
- `service_account_key_path` (String) Path for the service account key used for authentication. If set, the key flow will be used to authenticate all operations.
|
||||
- `service_account_token` (String) Token used for authentication. If set, the token flow will be used to authenticate all operations.
|
||||
- `ske_custom_endpoint` (String) Custom endpoint for the Kubernetes Engine (SKE) service
|
||||
- `sqlserverflex_custom_endpoint` (String) Custom endpoint for the SQL Server Flex service
|
||||
- `token_custom_endpoint` (String) Custom endpoint for the token API, which is used to request access tokens when using the key flow
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ Optional:
|
|||
- `max_surge` (Number) Maximum number of additional VMs that are created during an update.
|
||||
- `max_unavailable` (Number) Maximum number of VMs that that can be unavailable during an update.
|
||||
- `os_name` (String) The name of the OS image. Defaults to `flatcar`.
|
||||
- `os_version` (String, Deprecated) This field is deprecated, use `os_version_min` to configure the version and `os_version_used` to get the currently used version instead
|
||||
- `os_version` (String, Deprecated) This field is deprecated, use `os_version_min` to configure the version and `os_version_used` to get the currently used version instead.
|
||||
- `os_version_min` (String) The minimum OS image version. This field will be used to set the minimum OS image version on creation/update of the cluster. If unset, the latest supported OS image version will be used. SKE automatically updates the cluster Kubernetes version if you have set `maintenance.enable_kubernetes_version_updates` to true or if there is a mandatory update, as described in [Updates for Kubernetes versions and Operating System versions in SKE](https://docs.stackit.cloud/stackit/en/version-updates-in-ske-10125631.html). To get the current OS image version being used for the node pool, use the read-only `os_version_used` field.
|
||||
- `taints` (Attributes List) Specifies a taint list as defined below. (see [below for nested schema](#nestedatt--node_pools--taints))
|
||||
- `volume_size` (Number) The volume size in GB. Defaults to `20`
|
||||
|
|
|
|||
67
docs/resources/sqlserverflex_instance.md
Normal file
67
docs/resources/sqlserverflex_instance.md
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
---
|
||||
# generated by https://github.com/hashicorp/terraform-plugin-docs
|
||||
page_title: "stackit_sqlserverflex_instance Resource - stackit"
|
||||
subcategory: ""
|
||||
description: |-
|
||||
SQL Server Flex instance resource schema. Must have a region specified in the provider configuration.
|
||||
---
|
||||
|
||||
# stackit_sqlserverflex_instance (Resource)
|
||||
|
||||
SQL Server Flex instance resource schema. Must have a `region` specified in the provider configuration.
|
||||
|
||||
|
||||
|
||||
<!-- schema generated by tfplugindocs -->
|
||||
## Schema
|
||||
|
||||
### Required
|
||||
|
||||
- `flavor` (Attributes) (see [below for nested schema](#nestedatt--flavor))
|
||||
- `name` (String) Instance name.
|
||||
- `project_id` (String) STACKIT project ID to which the instance is associated.
|
||||
|
||||
### Optional
|
||||
|
||||
- `acl` (List of String) The Access Control List (ACL) for the SQL Server Flex instance.
|
||||
- `backup_schedule` (String) The backup schedule. Should follow the cron scheduling system format (e.g. "0 0 * * *")
|
||||
- `options` (Attributes) (see [below for nested schema](#nestedatt--options))
|
||||
- `storage` (Attributes) (see [below for nested schema](#nestedatt--storage))
|
||||
- `version` (String)
|
||||
|
||||
### Read-Only
|
||||
|
||||
- `id` (String) Terraform's internal resource ID. It is structured as "`project_id`,`instance_id`".
|
||||
- `instance_id` (String) ID of the SQL Server Flex instance.
|
||||
- `replicas` (Number)
|
||||
|
||||
<a id="nestedatt--flavor"></a>
|
||||
### Nested Schema for `flavor`
|
||||
|
||||
Required:
|
||||
|
||||
- `cpu` (Number)
|
||||
- `ram` (Number)
|
||||
|
||||
Read-Only:
|
||||
|
||||
- `description` (String)
|
||||
- `id` (String)
|
||||
|
||||
|
||||
<a id="nestedatt--options"></a>
|
||||
### Nested Schema for `options`
|
||||
|
||||
Optional:
|
||||
|
||||
- `edition` (String)
|
||||
- `retention_days` (Number)
|
||||
|
||||
|
||||
<a id="nestedatt--storage"></a>
|
||||
### Nested Schema for `storage`
|
||||
|
||||
Optional:
|
||||
|
||||
- `class` (String)
|
||||
- `size` (Number)
|
||||
1
go.mod
1
go.mod
|
|
@ -28,6 +28,7 @@ require (
|
|||
github.com/stackitcloud/stackit-sdk-go/services/resourcemanager v0.8.0
|
||||
github.com/stackitcloud/stackit-sdk-go/services/secretsmanager v0.8.0
|
||||
github.com/stackitcloud/stackit-sdk-go/services/ske v0.16.0
|
||||
github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex v0.2.0
|
||||
golang.org/x/mod v0.17.0
|
||||
)
|
||||
|
||||
|
|
|
|||
2
go.sum
2
go.sum
|
|
@ -178,6 +178,8 @@ github.com/stackitcloud/stackit-sdk-go/services/secretsmanager v0.8.0 h1:pJBG455
|
|||
github.com/stackitcloud/stackit-sdk-go/services/secretsmanager v0.8.0/go.mod h1:LX0Mcyr7/QP77zf7e05fHCJO38RMuTxr7nEDUDZ3oPQ=
|
||||
github.com/stackitcloud/stackit-sdk-go/services/ske v0.16.0 h1:trrJuRMzgXu6fiiMZiUx6+A1FNKEFhA1vGq5cr5Qn3U=
|
||||
github.com/stackitcloud/stackit-sdk-go/services/ske v0.16.0/go.mod h1:0fFs4R7kg+gU7FNAIzzFvlCZJz6gyZ8CFhbK3eSrAwQ=
|
||||
github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex v0.2.0 h1:aIXxXx6u4+6C02MPb+hdItigeKeen7m+hEEG+Ej9sNs=
|
||||
github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex v0.2.0/go.mod h1:fQJOQMfasStZ8J9iGX0vTjyJoQtLqMXJ5Npb03QJk84=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ type ProviderData struct {
|
|||
RedisCustomEndpoint string
|
||||
ResourceManagerCustomEndpoint string
|
||||
SecretsManagerCustomEndpoint string
|
||||
SQLServerFlexCustomEndpoint string
|
||||
SKECustomEndpoint string
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -643,7 +643,7 @@ func mapFields(ctx context.Context, resp *mongodbflex.GetInstanceResponse, model
|
|||
return fmt.Errorf("creating options: %w", core.DiagsToError(diags))
|
||||
}
|
||||
|
||||
simplifiedModelBackupSchedule := simplifyBackupSchedule(model.BackupSchedule.ValueString())
|
||||
simplifiedModelBackupSchedule := utils.SimplifyBackupSchedule(model.BackupSchedule.ValueString())
|
||||
// If the value returned by the API is different from the one in the model after simplification,
|
||||
// we update the model so that it causes an error in Terraform
|
||||
if simplifiedModelBackupSchedule != types.StringPointerValue(instance.BackupSchedule).ValueString() {
|
||||
|
|
@ -785,7 +785,7 @@ func loadFlavorId(ctx context.Context, client mongoDBFlexClient, model *Model, f
|
|||
flavor.Description = types.StringValue(*f.Description)
|
||||
break
|
||||
}
|
||||
avl = fmt.Sprintf("%s\n- %d CPU, %d GB RAM", avl, *f.Cpu, *f.Cpu)
|
||||
avl = fmt.Sprintf("%s\n- %d CPU, %d GB RAM", avl, *f.Cpu, *f.Memory)
|
||||
}
|
||||
if flavor.Id.ValueString() == "" {
|
||||
return fmt.Errorf("couldn't find flavor, available specs are:%s", avl)
|
||||
|
|
@ -793,17 +793,3 @@ func loadFlavorId(ctx context.Context, client mongoDBFlexClient, model *Model, f
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove leading 0s from backup schedule numbers (e.g. "00 00 * * *" becomes "0 0 * * *")
|
||||
// Needed as the API does it internally and would otherwise cause inconsistent result in Terraform
|
||||
func simplifyBackupSchedule(schedule string) string {
|
||||
regex := regexp.MustCompile(`0+\d+`) // Matches series of one or more zeros followed by a series of one or more digits
|
||||
simplifiedSchedule := regex.ReplaceAllStringFunc(schedule, func(match string) string {
|
||||
simplified := strings.TrimLeft(match, "0")
|
||||
if simplified == "" {
|
||||
simplified = "0"
|
||||
}
|
||||
return simplified
|
||||
})
|
||||
return simplifiedSchedule
|
||||
}
|
||||
|
|
|
|||
|
|
@ -838,75 +838,3 @@ func TestLoadFlavorId(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSimplifyBackupSchedule(t *testing.T) {
|
||||
tests := []struct {
|
||||
description string
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
"simple schedule",
|
||||
"0 0 * * *",
|
||||
"0 0 * * *",
|
||||
},
|
||||
{
|
||||
"schedule with leading zeros",
|
||||
"00 00 * * *",
|
||||
"0 0 * * *",
|
||||
},
|
||||
{
|
||||
"schedule with leading zeros 2",
|
||||
"00 001 * * *",
|
||||
"0 1 * * *",
|
||||
},
|
||||
{
|
||||
"schedule with leading zeros 3",
|
||||
"00 0010 * * *",
|
||||
"0 10 * * *",
|
||||
},
|
||||
{
|
||||
"simple schedule with slash",
|
||||
"0 0/6 * * *",
|
||||
"0 0/6 * * *",
|
||||
},
|
||||
{
|
||||
"schedule with leading zeros and slash",
|
||||
"00 00/6 * * *",
|
||||
"0 0/6 * * *",
|
||||
},
|
||||
{
|
||||
"schedule with leading zeros and slash 2",
|
||||
"00 001/06 * * *",
|
||||
"0 1/6 * * *",
|
||||
},
|
||||
{
|
||||
"simple schedule with comma",
|
||||
"0 10,15 * * *",
|
||||
"0 10,15 * * *",
|
||||
},
|
||||
{
|
||||
"schedule with leading zeros and comma",
|
||||
"0 010,0015 * * *",
|
||||
"0 10,15 * * *",
|
||||
},
|
||||
{
|
||||
"simple schedule with comma and slash",
|
||||
"0 0-11/10 * * *",
|
||||
"0 0-11/10 * * *",
|
||||
},
|
||||
{
|
||||
"schedule with leading zeros, comma, and slash",
|
||||
"00 000-011/010 * * *",
|
||||
"0 0-11/10 * * *",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.description, func(t *testing.T) {
|
||||
output := simplifyBackupSchedule(tt.input)
|
||||
if output != tt.expected {
|
||||
t.Fatalf("Data does not match: %s", output)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -694,7 +694,7 @@ func loadFlavorId(ctx context.Context, client postgresFlexClient, model *Model,
|
|||
flavor.Description = types.StringValue(*f.Description)
|
||||
break
|
||||
}
|
||||
avl = fmt.Sprintf("%s\n- %d CPU, %d GB RAM", avl, *f.Cpu, *f.Cpu)
|
||||
avl = fmt.Sprintf("%s\n- %d CPU, %d GB RAM", avl, *f.Cpu, *f.Memory)
|
||||
}
|
||||
if flavor.Id.ValueString() == "" {
|
||||
return fmt.Errorf("couldn't find flavor, available specs are:%s", avl)
|
||||
|
|
|
|||
237
stackit/internal/services/sqlserverflex/instance/datasource.go
Normal file
237
stackit/internal/services/sqlserverflex/instance/datasource.go
Normal file
|
|
@ -0,0 +1,237 @@
|
|||
package sqlserverflex
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/config"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
|
||||
"github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex"
|
||||
)
|
||||
|
||||
// Ensure the implementation satisfies the expected interfaces.
|
||||
var (
|
||||
_ datasource.DataSource = &instanceDataSource{}
|
||||
)
|
||||
|
||||
// NewInstanceDataSource is a helper function to simplify the provider implementation.
|
||||
func NewInstanceDataSource() datasource.DataSource {
|
||||
return &instanceDataSource{}
|
||||
}
|
||||
|
||||
// instanceDataSource is the data source implementation.
|
||||
type instanceDataSource struct {
|
||||
client *sqlserverflex.APIClient
|
||||
}
|
||||
|
||||
// Metadata returns the data source type name.
|
||||
func (r *instanceDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_sqlserverflex_instance"
|
||||
}
|
||||
|
||||
// Configure adds the provider configured client to the data source.
|
||||
func (r *instanceDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
|
||||
// Prevent panic if the provider has not been configured.
|
||||
if req.ProviderData == nil {
|
||||
return
|
||||
}
|
||||
|
||||
providerData, ok := req.ProviderData.(core.ProviderData)
|
||||
if !ok {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error configuring API client", fmt.Sprintf("Expected configure type stackit.ProviderData, got %T", req.ProviderData))
|
||||
return
|
||||
}
|
||||
|
||||
var apiClient *sqlserverflex.APIClient
|
||||
var err error
|
||||
if providerData.SQLServerFlexCustomEndpoint != "" {
|
||||
apiClient, err = sqlserverflex.NewAPIClient(
|
||||
config.WithCustomAuth(providerData.RoundTripper),
|
||||
config.WithEndpoint(providerData.SQLServerFlexCustomEndpoint),
|
||||
)
|
||||
} else {
|
||||
apiClient, err = sqlserverflex.NewAPIClient(
|
||||
config.WithCustomAuth(providerData.RoundTripper),
|
||||
config.WithRegion(providerData.Region),
|
||||
)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error configuring API client", fmt.Sprintf("Configuring client: %v. This is an error related to the provider configuration, not to the data source configuration", err))
|
||||
return
|
||||
}
|
||||
|
||||
r.client = apiClient
|
||||
tflog.Info(ctx, "MongoDB Flex instance client configured")
|
||||
}
|
||||
|
||||
// Schema defines the schema for the data source.
|
||||
func (r *instanceDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
||||
descriptions := map[string]string{
|
||||
"main": "MongoDB Flex instance data source schema. Must have a `region` specified in the provider configuration.",
|
||||
"id": "Terraform's internal data source. ID. It is structured as \"`project_id`,`instance_id`\".",
|
||||
"instance_id": "ID of the MongoDB Flex instance.",
|
||||
"project_id": "STACKIT project ID to which the instance is associated.",
|
||||
"name": "Instance name.",
|
||||
"acl": "The Access Control List (ACL) for the MongoDB Flex instance.",
|
||||
"backup_schedule": `The backup schedule. Should follow the cron scheduling system format (e.g. "0 0 * * *").`,
|
||||
"options": "Custom parameters for the MongoDB Flex instance.",
|
||||
}
|
||||
|
||||
resp.Schema = schema.Schema{
|
||||
Description: descriptions["main"],
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"id": schema.StringAttribute{
|
||||
Description: descriptions["id"],
|
||||
Computed: true,
|
||||
},
|
||||
"instance_id": schema.StringAttribute{
|
||||
Description: descriptions["instance_id"],
|
||||
Required: true,
|
||||
Validators: []validator.String{
|
||||
validate.UUID(),
|
||||
validate.NoSeparator(),
|
||||
},
|
||||
},
|
||||
"project_id": schema.StringAttribute{
|
||||
Description: descriptions["project_id"],
|
||||
Required: true,
|
||||
Validators: []validator.String{
|
||||
validate.UUID(),
|
||||
validate.NoSeparator(),
|
||||
},
|
||||
},
|
||||
"name": schema.StringAttribute{
|
||||
Description: descriptions["name"],
|
||||
Computed: true,
|
||||
},
|
||||
"acl": schema.ListAttribute{
|
||||
Description: descriptions["acl"],
|
||||
ElementType: types.StringType,
|
||||
Computed: true,
|
||||
},
|
||||
"backup_schedule": schema.StringAttribute{
|
||||
Description: descriptions["backup_schedule"],
|
||||
Computed: true,
|
||||
},
|
||||
"flavor": schema.SingleNestedAttribute{
|
||||
Computed: true,
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"id": schema.StringAttribute{
|
||||
Computed: true,
|
||||
},
|
||||
"description": schema.StringAttribute{
|
||||
Computed: true,
|
||||
},
|
||||
"cpu": schema.Int64Attribute{
|
||||
Computed: true,
|
||||
},
|
||||
"ram": schema.Int64Attribute{
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
"replicas": schema.Int64Attribute{
|
||||
Computed: true,
|
||||
},
|
||||
"storage": schema.SingleNestedAttribute{
|
||||
Computed: true,
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"class": schema.StringAttribute{
|
||||
Computed: true,
|
||||
},
|
||||
"size": schema.Int64Attribute{
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
"version": schema.StringAttribute{
|
||||
Computed: true,
|
||||
},
|
||||
"options": schema.SingleNestedAttribute{
|
||||
Description: descriptions["options"],
|
||||
Computed: true,
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"edition": schema.StringAttribute{
|
||||
Computed: true,
|
||||
},
|
||||
"retention_days": schema.Int64Attribute{
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Read refreshes the Terraform state with the latest data.
|
||||
func (r *instanceDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { // nolint:gocritic // function signature required by Terraform
|
||||
var model Model
|
||||
diags := req.Config.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
projectId := model.ProjectId.ValueString()
|
||||
instanceId := model.InstanceId.ValueString()
|
||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||
ctx = tflog.SetField(ctx, "instance_id", instanceId)
|
||||
instanceResp, err := r.client.GetInstance(ctx, projectId, instanceId).Execute()
|
||||
if err != nil {
|
||||
oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
|
||||
if ok && oapiErr.StatusCode == http.StatusNotFound {
|
||||
resp.State.RemoveResource(ctx)
|
||||
}
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Calling API: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
var flavor = &flavorModel{}
|
||||
if !(model.Flavor.IsNull() || model.Flavor.IsUnknown()) {
|
||||
diags = model.Flavor.As(ctx, flavor, basetypes.ObjectAsOptions{})
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
var storage = &storageModel{}
|
||||
if !(model.Storage.IsNull() || model.Storage.IsUnknown()) {
|
||||
diags = model.Storage.As(ctx, storage, basetypes.ObjectAsOptions{})
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
var options = &optionsModel{}
|
||||
if !(model.Options.IsNull() || model.Options.IsUnknown()) {
|
||||
diags = model.Options.As(ctx, options, basetypes.ObjectAsOptions{})
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
err = mapFields(ctx, instanceResp, &model, flavor, storage, options)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Processing API payload: %v", err))
|
||||
return
|
||||
}
|
||||
// Set refreshed state
|
||||
diags = resp.State.Set(ctx, model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
tflog.Info(ctx, "MongoDB Flex instance read")
|
||||
}
|
||||
841
stackit/internal/services/sqlserverflex/instance/resource.go
Normal file
841
stackit/internal/services/sqlserverflex/instance/resource.go
Normal file
|
|
@ -0,0 +1,841 @@
|
|||
package sqlserverflex
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
|
||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/listplanmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/config"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
|
||||
coreUtils "github.com/stackitcloud/stackit-sdk-go/core/utils"
|
||||
"github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex"
|
||||
"github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex/wait"
|
||||
)
|
||||
|
||||
// Ensure the implementation satisfies the expected interfaces.
|
||||
var (
|
||||
_ resource.Resource = &instanceResource{}
|
||||
_ resource.ResourceWithConfigure = &instanceResource{}
|
||||
_ resource.ResourceWithImportState = &instanceResource{}
|
||||
)
|
||||
|
||||
type Model struct {
|
||||
Id types.String `tfsdk:"id"` // needed by TF
|
||||
InstanceId types.String `tfsdk:"instance_id"`
|
||||
ProjectId types.String `tfsdk:"project_id"`
|
||||
Name types.String `tfsdk:"name"`
|
||||
ACL types.List `tfsdk:"acl"`
|
||||
BackupSchedule types.String `tfsdk:"backup_schedule"`
|
||||
Flavor types.Object `tfsdk:"flavor"`
|
||||
Storage types.Object `tfsdk:"storage"`
|
||||
Version types.String `tfsdk:"version"`
|
||||
Replicas types.Int64 `tfsdk:"replicas"`
|
||||
Options types.Object `tfsdk:"options"`
|
||||
}
|
||||
|
||||
// Struct corresponding to Model.Flavor
|
||||
type flavorModel struct {
|
||||
Id types.String `tfsdk:"id"`
|
||||
Description types.String `tfsdk:"description"`
|
||||
CPU types.Int64 `tfsdk:"cpu"`
|
||||
RAM types.Int64 `tfsdk:"ram"`
|
||||
}
|
||||
|
||||
// Types corresponding to flavorModel
|
||||
var flavorTypes = map[string]attr.Type{
|
||||
"id": basetypes.StringType{},
|
||||
"description": basetypes.StringType{},
|
||||
"cpu": basetypes.Int64Type{},
|
||||
"ram": basetypes.Int64Type{},
|
||||
}
|
||||
|
||||
// Struct corresponding to Model.Storage
|
||||
type storageModel struct {
|
||||
Class types.String `tfsdk:"class"`
|
||||
Size types.Int64 `tfsdk:"size"`
|
||||
}
|
||||
|
||||
// Types corresponding to storageModel
|
||||
var storageTypes = map[string]attr.Type{
|
||||
"class": basetypes.StringType{},
|
||||
"size": basetypes.Int64Type{},
|
||||
}
|
||||
|
||||
// Struct corresponding to Model.Options
|
||||
type optionsModel struct {
|
||||
Edition types.String `tfsdk:"edition"`
|
||||
RetentionDays types.Int64 `tfsdk:"retention_days"`
|
||||
}
|
||||
|
||||
// Types corresponding to optionsModel
|
||||
var optionsTypes = map[string]attr.Type{
|
||||
"edition": basetypes.StringType{},
|
||||
"retention_days": basetypes.Int64Type{},
|
||||
}
|
||||
|
||||
// NewInstanceResource is a helper function to simplify the provider implementation.
|
||||
func NewInstanceResource() resource.Resource {
|
||||
return &instanceResource{}
|
||||
}
|
||||
|
||||
// instanceResource is the resource implementation.
|
||||
type instanceResource struct {
|
||||
client *sqlserverflex.APIClient
|
||||
}
|
||||
|
||||
// Metadata returns the resource type name.
|
||||
func (r *instanceResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
|
||||
resp.TypeName = req.ProviderTypeName + "_sqlserverflex_instance"
|
||||
}
|
||||
|
||||
// Configure adds the provider configured client to the resource.
|
||||
func (r *instanceResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
|
||||
// Prevent panic if the provider has not been configured.
|
||||
if req.ProviderData == nil {
|
||||
return
|
||||
}
|
||||
|
||||
providerData, ok := req.ProviderData.(core.ProviderData)
|
||||
if !ok {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error configuring API client", fmt.Sprintf("Expected configure type stackit.ProviderData, got %T", req.ProviderData))
|
||||
return
|
||||
}
|
||||
|
||||
var apiClient *sqlserverflex.APIClient
|
||||
var err error
|
||||
if providerData.SQLServerFlexCustomEndpoint != "" {
|
||||
apiClient, err = sqlserverflex.NewAPIClient(
|
||||
config.WithCustomAuth(providerData.RoundTripper),
|
||||
config.WithEndpoint(providerData.SQLServerFlexCustomEndpoint),
|
||||
)
|
||||
} else {
|
||||
apiClient, err = sqlserverflex.NewAPIClient(
|
||||
config.WithCustomAuth(providerData.RoundTripper),
|
||||
config.WithRegion(providerData.Region),
|
||||
)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error configuring API client", fmt.Sprintf("Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration", err))
|
||||
return
|
||||
}
|
||||
|
||||
r.client = apiClient
|
||||
tflog.Info(ctx, "SQLServer Flex instance client configured")
|
||||
}
|
||||
|
||||
// Schema defines the schema for the resource.
|
||||
func (r *instanceResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
|
||||
descriptions := map[string]string{
|
||||
"main": "SQLServer Flex instance resource schema. Must have a `region` specified in the provider configuration.",
|
||||
"id": "Terraform's internal resource ID. It is structured as \"`project_id`,`instance_id`\".",
|
||||
"instance_id": "ID of the SQLServer Flex instance.",
|
||||
"project_id": "STACKIT project ID to which the instance is associated.",
|
||||
"name": "Instance name.",
|
||||
"acl": "The Access Control List (ACL) for the SQLServer Flex instance.",
|
||||
"backup_schedule": `The backup schedule. Should follow the cron scheduling system format (e.g. "0 0 * * *")`,
|
||||
"options": "Custom parameters for the SQLServer Flex instance.",
|
||||
}
|
||||
|
||||
resp.Schema = schema.Schema{
|
||||
Description: descriptions["main"],
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"id": schema.StringAttribute{
|
||||
Description: descriptions["id"],
|
||||
Computed: true,
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.UseStateForUnknown(),
|
||||
},
|
||||
},
|
||||
"instance_id": schema.StringAttribute{
|
||||
Description: descriptions["instance_id"],
|
||||
Computed: true,
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.UseStateForUnknown(),
|
||||
},
|
||||
Validators: []validator.String{
|
||||
validate.UUID(),
|
||||
validate.NoSeparator(),
|
||||
},
|
||||
},
|
||||
"project_id": schema.StringAttribute{
|
||||
Description: descriptions["project_id"],
|
||||
Required: true,
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.RequiresReplace(),
|
||||
},
|
||||
Validators: []validator.String{
|
||||
validate.UUID(),
|
||||
validate.NoSeparator(),
|
||||
},
|
||||
},
|
||||
"name": schema.StringAttribute{
|
||||
Description: descriptions["name"],
|
||||
Required: true,
|
||||
Validators: []validator.String{
|
||||
stringvalidator.LengthAtLeast(1),
|
||||
stringvalidator.RegexMatches(
|
||||
regexp.MustCompile("^[a-z]([-a-z0-9]*[a-z0-9])?$"),
|
||||
"must start with a letter, must have lower case letters, numbers or hyphens, and no hyphen at the end",
|
||||
),
|
||||
},
|
||||
},
|
||||
"acl": schema.ListAttribute{
|
||||
Description: descriptions["acl"],
|
||||
ElementType: types.StringType,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
PlanModifiers: []planmodifier.List{
|
||||
listplanmodifier.UseStateForUnknown(),
|
||||
},
|
||||
},
|
||||
"backup_schedule": schema.StringAttribute{
|
||||
Description: descriptions["backup_schedule"],
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.UseStateForUnknown(),
|
||||
},
|
||||
},
|
||||
"flavor": schema.SingleNestedAttribute{
|
||||
Required: true,
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"id": schema.StringAttribute{
|
||||
Computed: true,
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.UseStateForUnknown(),
|
||||
},
|
||||
},
|
||||
"description": schema.StringAttribute{
|
||||
Computed: true,
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.UseStateForUnknown(),
|
||||
},
|
||||
},
|
||||
"cpu": schema.Int64Attribute{
|
||||
Required: true,
|
||||
},
|
||||
"ram": schema.Int64Attribute{
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
"replicas": schema.Int64Attribute{
|
||||
Computed: true,
|
||||
PlanModifiers: []planmodifier.Int64{
|
||||
int64planmodifier.UseStateForUnknown(),
|
||||
},
|
||||
},
|
||||
"storage": schema.SingleNestedAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
PlanModifiers: []planmodifier.Object{
|
||||
objectplanmodifier.RequiresReplace(),
|
||||
objectplanmodifier.UseStateForUnknown(),
|
||||
},
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"class": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.RequiresReplace(),
|
||||
stringplanmodifier.UseStateForUnknown(),
|
||||
},
|
||||
},
|
||||
"size": schema.Int64Attribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
PlanModifiers: []planmodifier.Int64{
|
||||
int64planmodifier.RequiresReplace(),
|
||||
int64planmodifier.UseStateForUnknown(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"version": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.UseStateForUnknown(),
|
||||
},
|
||||
},
|
||||
"options": schema.SingleNestedAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
PlanModifiers: []planmodifier.Object{
|
||||
objectplanmodifier.RequiresReplace(),
|
||||
objectplanmodifier.UseStateForUnknown(),
|
||||
},
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"edition": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
PlanModifiers: []planmodifier.String{
|
||||
stringplanmodifier.RequiresReplace(),
|
||||
stringplanmodifier.UseStateForUnknown(),
|
||||
},
|
||||
},
|
||||
"retention_days": schema.Int64Attribute{
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
PlanModifiers: []planmodifier.Int64{
|
||||
int64planmodifier.RequiresReplace(),
|
||||
int64planmodifier.UseStateForUnknown(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Create creates the resource and sets the initial Terraform state.
|
||||
func (r *instanceResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { // nolint:gocritic // function signature required by Terraform
|
||||
// Retrieve values from plan
|
||||
var model Model
|
||||
diags := req.Plan.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
projectId := model.ProjectId.ValueString()
|
||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||
|
||||
var acl []string
|
||||
if !(model.ACL.IsNull() || model.ACL.IsUnknown()) {
|
||||
diags = model.ACL.ElementsAs(ctx, &acl, false)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
var flavor = &flavorModel{}
|
||||
if !(model.Flavor.IsNull() || model.Flavor.IsUnknown()) {
|
||||
diags = model.Flavor.As(ctx, flavor, basetypes.ObjectAsOptions{})
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
err := loadFlavorId(ctx, r.client, &model, flavor)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Loading flavor ID: %v", err))
|
||||
return
|
||||
}
|
||||
}
|
||||
var storage = &storageModel{}
|
||||
if !(model.Storage.IsNull() || model.Storage.IsUnknown()) {
|
||||
diags = model.Storage.As(ctx, storage, basetypes.ObjectAsOptions{})
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var options = &optionsModel{}
|
||||
if !(model.Options.IsNull() || model.Options.IsUnknown()) {
|
||||
diags = model.Options.As(ctx, options, basetypes.ObjectAsOptions{})
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Generate API request body from model
|
||||
payload, err := toCreatePayload(&model, acl, flavor, storage, options)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Creating API payload: %v", err))
|
||||
return
|
||||
}
|
||||
// Create new instance
|
||||
createResp, err := r.client.CreateInstance(ctx, projectId).CreateInstancePayload(*payload).Execute()
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Calling API: %v", err))
|
||||
return
|
||||
}
|
||||
instanceId := *createResp.Id
|
||||
ctx = tflog.SetField(ctx, "instance_id", instanceId)
|
||||
// The creation waiter sometimes returns an error from the API: "instance with id xxx has unexpected status Failure"
|
||||
// which can be avoided by sleeping before wait
|
||||
waitResp, err := wait.CreateInstanceWaitHandler(ctx, r.client, projectId, instanceId).SetSleepBeforeWait(30 * time.Second).WaitWithContext(ctx)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Instance creation waiting: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
// Map response body to schema
|
||||
err = mapFields(ctx, waitResp, &model, flavor, storage, options)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating instance", fmt.Sprintf("Processing API payload: %v", err))
|
||||
return
|
||||
}
|
||||
// Set state to fully populated data
|
||||
diags = resp.State.Set(ctx, model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
tflog.Info(ctx, "SQLServer Flex instance created")
|
||||
}
|
||||
|
||||
// Read refreshes the Terraform state with the latest data.
|
||||
func (r *instanceResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { // nolint:gocritic // function signature required by Terraform
|
||||
var model Model
|
||||
diags := req.State.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
projectId := model.ProjectId.ValueString()
|
||||
instanceId := model.InstanceId.ValueString()
|
||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||
ctx = tflog.SetField(ctx, "instance_id", instanceId)
|
||||
|
||||
var flavor = &flavorModel{}
|
||||
if !(model.Flavor.IsNull() || model.Flavor.IsUnknown()) {
|
||||
diags = model.Flavor.As(ctx, flavor, basetypes.ObjectAsOptions{})
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
var storage = &storageModel{}
|
||||
if !(model.Storage.IsNull() || model.Storage.IsUnknown()) {
|
||||
diags = model.Storage.As(ctx, storage, basetypes.ObjectAsOptions{})
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var options = &optionsModel{}
|
||||
if !(model.Options.IsNull() || model.Options.IsUnknown()) {
|
||||
diags = model.Options.As(ctx, options, basetypes.ObjectAsOptions{})
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
instanceResp, err := r.client.GetInstance(ctx, projectId, instanceId).Execute()
|
||||
if err != nil {
|
||||
oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
|
||||
if ok && oapiErr.StatusCode == http.StatusNotFound {
|
||||
resp.State.RemoveResource(ctx)
|
||||
return
|
||||
}
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Map response body to schema
|
||||
err = mapFields(ctx, instanceResp, &model, flavor, storage, options)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading instance", fmt.Sprintf("Processing API payload: %v", err))
|
||||
return
|
||||
}
|
||||
// Set refreshed state
|
||||
diags = resp.State.Set(ctx, model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
tflog.Info(ctx, "SQLServer Flex instance read")
|
||||
}
|
||||
|
||||
// Update updates the resource and sets the updated Terraform state on success.
|
||||
func (r *instanceResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { // nolint:gocritic // function signature required by Terraform
|
||||
// Retrieve values from plan
|
||||
var model Model
|
||||
diags := req.Plan.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
projectId := model.ProjectId.ValueString()
|
||||
instanceId := model.InstanceId.ValueString()
|
||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||
ctx = tflog.SetField(ctx, "instance_id", instanceId)
|
||||
|
||||
var acl []string
|
||||
if !(model.ACL.IsNull() || model.ACL.IsUnknown()) {
|
||||
diags = model.ACL.ElementsAs(ctx, &acl, false)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
var flavor = &flavorModel{}
|
||||
if !(model.Flavor.IsNull() || model.Flavor.IsUnknown()) {
|
||||
diags = model.Flavor.As(ctx, flavor, basetypes.ObjectAsOptions{})
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
err := loadFlavorId(ctx, r.client, &model, flavor)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Loading flavor ID: %v", err))
|
||||
return
|
||||
}
|
||||
}
|
||||
var storage = &storageModel{}
|
||||
if !(model.Storage.IsNull() || model.Storage.IsUnknown()) {
|
||||
diags = model.Storage.As(ctx, storage, basetypes.ObjectAsOptions{})
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var options = &optionsModel{}
|
||||
if !(model.Options.IsNull() || model.Options.IsUnknown()) {
|
||||
diags = model.Options.As(ctx, options, basetypes.ObjectAsOptions{})
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Generate API request body from model
|
||||
payload, err := toUpdatePayload(&model, acl, flavor)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Creating API payload: %v", err))
|
||||
return
|
||||
}
|
||||
// Update existing instance
|
||||
_, err = r.client.PartialUpdateInstance(ctx, projectId, instanceId).PartialUpdateInstancePayload(*payload).Execute()
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", err.Error())
|
||||
return
|
||||
}
|
||||
waitResp, err := wait.UpdateInstanceWaitHandler(ctx, r.client, projectId, instanceId).WaitWithContext(ctx)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Instance update waiting: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
// Map response body to schema
|
||||
err = mapFields(ctx, waitResp, &model, flavor, storage, options)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating instance", fmt.Sprintf("Processing API payload: %v", err))
|
||||
return
|
||||
}
|
||||
diags = resp.State.Set(ctx, model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
tflog.Info(ctx, "SQLServer Flex instance updated")
|
||||
}
|
||||
|
||||
// Delete deletes the resource and removes the Terraform state on success.
|
||||
func (r *instanceResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { // nolint:gocritic // function signature required by Terraform
|
||||
// Retrieve values from state
|
||||
var model Model
|
||||
diags := req.State.Get(ctx, &model)
|
||||
resp.Diagnostics.Append(diags...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
projectId := model.ProjectId.ValueString()
|
||||
instanceId := model.InstanceId.ValueString()
|
||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||
ctx = tflog.SetField(ctx, "instance_id", instanceId)
|
||||
|
||||
// Delete existing instance
|
||||
err := r.client.DeleteInstance(ctx, projectId, instanceId).Execute()
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting instance", fmt.Sprintf("Calling API: %v", err))
|
||||
return
|
||||
}
|
||||
_, err = wait.DeleteInstanceWaitHandler(ctx, r.client, projectId, instanceId).WaitWithContext(ctx)
|
||||
if err != nil {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting instance", fmt.Sprintf("Instance deletion waiting: %v", err))
|
||||
return
|
||||
}
|
||||
tflog.Info(ctx, "SQLServer Flex instance deleted")
|
||||
}
|
||||
|
||||
// ImportState imports a resource into the Terraform state on success.
|
||||
// The expected format of the resource import identifier is: project_id,instance_id
|
||||
func (r *instanceResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
|
||||
idParts := strings.Split(req.ID, core.Separator)
|
||||
|
||||
if len(idParts) != 2 || idParts[0] == "" || idParts[1] == "" {
|
||||
core.LogAndAddError(ctx, &resp.Diagnostics,
|
||||
"Error importing instance",
|
||||
fmt.Sprintf("Expected import identifier with format: [project_id],[instance_id] Got: %q", req.ID),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("project_id"), idParts[0])...)
|
||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("instance_id"), idParts[1])...)
|
||||
tflog.Info(ctx, "SQLServer Flex instance state imported")
|
||||
}
|
||||
|
||||
func mapFields(ctx context.Context, resp *sqlserverflex.GetInstanceResponse, model *Model, flavor *flavorModel, storage *storageModel, options *optionsModel) error {
|
||||
if resp == nil {
|
||||
return fmt.Errorf("response input is nil")
|
||||
}
|
||||
if resp.Item == nil {
|
||||
return fmt.Errorf("no instance provided")
|
||||
}
|
||||
if model == nil {
|
||||
return fmt.Errorf("model input is nil")
|
||||
}
|
||||
instance := resp.Item
|
||||
|
||||
var instanceId string
|
||||
if model.InstanceId.ValueString() != "" {
|
||||
instanceId = model.InstanceId.ValueString()
|
||||
} else if instance.Id != nil {
|
||||
instanceId = *instance.Id
|
||||
} else {
|
||||
return fmt.Errorf("instance id not present")
|
||||
}
|
||||
|
||||
var aclList basetypes.ListValue
|
||||
var diags diag.Diagnostics
|
||||
if instance.Acl == nil || instance.Acl.Items == nil {
|
||||
aclList = types.ListNull(types.StringType)
|
||||
} else {
|
||||
respACL := *instance.Acl.Items
|
||||
modelACL, err := utils.ListValuetoStringSlice(model.ACL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
reconciledACL := utils.ReconcileStringSlices(modelACL, respACL)
|
||||
|
||||
aclList, diags = types.ListValueFrom(ctx, types.StringType, reconciledACL)
|
||||
if diags.HasError() {
|
||||
return fmt.Errorf("mapping ACL: %w", core.DiagsToError(diags))
|
||||
}
|
||||
}
|
||||
|
||||
var flavorValues map[string]attr.Value
|
||||
if instance.Flavor == nil {
|
||||
flavorValues = map[string]attr.Value{
|
||||
"id": flavor.Id,
|
||||
"description": flavor.Description,
|
||||
"cpu": flavor.CPU,
|
||||
"ram": flavor.RAM,
|
||||
}
|
||||
} else {
|
||||
flavorValues = map[string]attr.Value{
|
||||
"id": types.StringValue(*instance.Flavor.Id),
|
||||
"description": types.StringValue(*instance.Flavor.Description),
|
||||
"cpu": types.Int64PointerValue(instance.Flavor.Cpu),
|
||||
"ram": types.Int64PointerValue(instance.Flavor.Memory),
|
||||
}
|
||||
}
|
||||
flavorObject, diags := types.ObjectValue(flavorTypes, flavorValues)
|
||||
if diags.HasError() {
|
||||
return fmt.Errorf("creating flavor: %w", core.DiagsToError(diags))
|
||||
}
|
||||
|
||||
var storageValues map[string]attr.Value
|
||||
if instance.Storage == nil {
|
||||
storageValues = map[string]attr.Value{
|
||||
"class": storage.Class,
|
||||
"size": storage.Size,
|
||||
}
|
||||
} else {
|
||||
storageValues = map[string]attr.Value{
|
||||
"class": types.StringValue(*instance.Storage.Class),
|
||||
"size": types.Int64PointerValue(instance.Storage.Size),
|
||||
}
|
||||
}
|
||||
storageObject, diags := types.ObjectValue(storageTypes, storageValues)
|
||||
if diags.HasError() {
|
||||
return fmt.Errorf("creating storage: %w", core.DiagsToError(diags))
|
||||
}
|
||||
|
||||
var optionsValues map[string]attr.Value
|
||||
if instance.Options == nil {
|
||||
optionsValues = map[string]attr.Value{
|
||||
"edition": options.Edition,
|
||||
"retention_days": options.RetentionDays,
|
||||
}
|
||||
} else {
|
||||
retentionDays := options.RetentionDays
|
||||
retentionDaysString, ok := (*instance.Options)["retentionDays"]
|
||||
if ok {
|
||||
retentionDaysValue, err := strconv.ParseInt(retentionDaysString, 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("parse retentionDays to int64: %w", err)
|
||||
}
|
||||
retentionDays = types.Int64Value(retentionDaysValue)
|
||||
}
|
||||
|
||||
edition := options.Edition
|
||||
editionValue, ok := (*instance.Options)["edition"]
|
||||
if ok {
|
||||
edition = types.StringValue(editionValue)
|
||||
}
|
||||
|
||||
optionsValues = map[string]attr.Value{
|
||||
"edition": edition,
|
||||
"retention_days": retentionDays,
|
||||
}
|
||||
}
|
||||
optionsObject, diags := types.ObjectValue(optionsTypes, optionsValues)
|
||||
if diags.HasError() {
|
||||
return fmt.Errorf("creating options: %w", core.DiagsToError(diags))
|
||||
}
|
||||
|
||||
simplifiedModelBackupSchedule := utils.SimplifyBackupSchedule(model.BackupSchedule.ValueString())
|
||||
// If the value returned by the API is different from the one in the model after simplification,
|
||||
// we update the model so that it causes an error in Terraform
|
||||
if simplifiedModelBackupSchedule != types.StringPointerValue(instance.BackupSchedule).ValueString() {
|
||||
model.BackupSchedule = types.StringPointerValue(instance.BackupSchedule)
|
||||
}
|
||||
|
||||
idParts := []string{
|
||||
model.ProjectId.ValueString(),
|
||||
instanceId,
|
||||
}
|
||||
model.Id = types.StringValue(
|
||||
strings.Join(idParts, core.Separator),
|
||||
)
|
||||
model.InstanceId = types.StringValue(instanceId)
|
||||
model.Name = types.StringPointerValue(instance.Name)
|
||||
model.ACL = aclList
|
||||
model.Flavor = flavorObject
|
||||
model.Replicas = types.Int64PointerValue(instance.Replicas)
|
||||
model.Storage = storageObject
|
||||
model.Version = types.StringPointerValue(instance.Version)
|
||||
model.Options = optionsObject
|
||||
return nil
|
||||
}
|
||||
|
||||
func toCreatePayload(model *Model, acl []string, flavor *flavorModel, storage *storageModel, options *optionsModel) (*sqlserverflex.CreateInstancePayload, error) {
|
||||
if model == nil {
|
||||
return nil, fmt.Errorf("nil model")
|
||||
}
|
||||
aclPayload := &sqlserverflex.CreateInstancePayloadAcl{}
|
||||
if acl != nil {
|
||||
aclPayload.Items = &acl
|
||||
}
|
||||
if flavor == nil {
|
||||
return nil, fmt.Errorf("nil flavor")
|
||||
}
|
||||
storagePayload := &sqlserverflex.CreateInstancePayloadStorage{}
|
||||
if storage != nil {
|
||||
storagePayload.Class = conversion.StringValueToPointer(storage.Class)
|
||||
storagePayload.Size = conversion.Int64ValueToPointer(storage.Size)
|
||||
}
|
||||
optionsPayload := &sqlserverflex.CreateInstancePayloadOptions{}
|
||||
if options != nil {
|
||||
optionsPayload.Edition = conversion.StringValueToPointer(options.Edition)
|
||||
retentionDaysInt := conversion.Int64ValueToPointer(options.RetentionDays)
|
||||
var retentionDays *string
|
||||
if retentionDaysInt != nil {
|
||||
retentionDays = coreUtils.Ptr(strconv.FormatInt(*retentionDaysInt, 10))
|
||||
}
|
||||
optionsPayload.RetentionDays = retentionDays
|
||||
}
|
||||
|
||||
return &sqlserverflex.CreateInstancePayload{
|
||||
Acl: aclPayload,
|
||||
BackupSchedule: conversion.StringValueToPointer(model.BackupSchedule),
|
||||
FlavorId: conversion.StringValueToPointer(flavor.Id),
|
||||
Name: conversion.StringValueToPointer(model.Name),
|
||||
Storage: storagePayload,
|
||||
Version: conversion.StringValueToPointer(model.Version),
|
||||
Options: optionsPayload,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func toUpdatePayload(model *Model, acl []string, flavor *flavorModel) (*sqlserverflex.PartialUpdateInstancePayload, error) {
|
||||
if model == nil {
|
||||
return nil, fmt.Errorf("nil model")
|
||||
}
|
||||
aclPayload := &sqlserverflex.CreateInstancePayloadAcl{}
|
||||
if acl != nil {
|
||||
aclPayload.Items = &acl
|
||||
}
|
||||
if flavor == nil {
|
||||
return nil, fmt.Errorf("nil flavor")
|
||||
}
|
||||
|
||||
return &sqlserverflex.PartialUpdateInstancePayload{
|
||||
Acl: aclPayload,
|
||||
BackupSchedule: conversion.StringValueToPointer(model.BackupSchedule),
|
||||
FlavorId: conversion.StringValueToPointer(flavor.Id),
|
||||
Name: conversion.StringValueToPointer(model.Name),
|
||||
Version: conversion.StringValueToPointer(model.Version),
|
||||
}, nil
|
||||
}
|
||||
|
||||
type sqlserverflexClient interface {
|
||||
ListFlavorsExecute(ctx context.Context, projectId string) (*sqlserverflex.ListFlavorsResponse, error)
|
||||
}
|
||||
|
||||
func loadFlavorId(ctx context.Context, client sqlserverflexClient, model *Model, flavor *flavorModel) error {
|
||||
if model == nil {
|
||||
return fmt.Errorf("nil model")
|
||||
}
|
||||
if flavor == nil {
|
||||
return fmt.Errorf("nil flavor")
|
||||
}
|
||||
cpu := conversion.Int64ValueToPointer(flavor.CPU)
|
||||
if cpu == nil {
|
||||
return fmt.Errorf("nil CPU")
|
||||
}
|
||||
ram := conversion.Int64ValueToPointer(flavor.RAM)
|
||||
if ram == nil {
|
||||
return fmt.Errorf("nil RAM")
|
||||
}
|
||||
|
||||
projectId := model.ProjectId.ValueString()
|
||||
res, err := client.ListFlavorsExecute(ctx, projectId)
|
||||
if err != nil {
|
||||
return fmt.Errorf("listing sqlserverflex flavors: %w", err)
|
||||
}
|
||||
|
||||
avl := ""
|
||||
if res.Flavors == nil {
|
||||
return fmt.Errorf("finding flavors for project %s", projectId)
|
||||
}
|
||||
for _, f := range *res.Flavors {
|
||||
if f.Id == nil || f.Cpu == nil || f.Memory == nil {
|
||||
continue
|
||||
}
|
||||
if *f.Cpu == *cpu && *f.Memory == *ram {
|
||||
flavor.Id = types.StringValue(*f.Id)
|
||||
flavor.Description = types.StringValue(*f.Description)
|
||||
break
|
||||
}
|
||||
avl = fmt.Sprintf("%s\n- %d CPU, %d GB RAM", avl, *f.Cpu, *f.Memory)
|
||||
}
|
||||
if flavor.Id.ValueString() == "" {
|
||||
return fmt.Errorf("couldn't find flavor, available specs are:%s", avl)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,809 @@
|
|||
package sqlserverflex
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/utils"
|
||||
"github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex"
|
||||
)
|
||||
|
||||
type sqlserverflexClientMocked struct {
|
||||
returnError bool
|
||||
listFlavorsResp *sqlserverflex.ListFlavorsResponse
|
||||
}
|
||||
|
||||
func (c *sqlserverflexClientMocked) ListFlavorsExecute(_ context.Context, _ string) (*sqlserverflex.ListFlavorsResponse, error) {
|
||||
if c.returnError {
|
||||
return nil, fmt.Errorf("get flavors failed")
|
||||
}
|
||||
|
||||
return c.listFlavorsResp, nil
|
||||
}
|
||||
|
||||
func TestMapFields(t *testing.T) {
|
||||
tests := []struct {
|
||||
description string
|
||||
state Model
|
||||
input *sqlserverflex.GetInstanceResponse
|
||||
flavor *flavorModel
|
||||
storage *storageModel
|
||||
options *optionsModel
|
||||
expected Model
|
||||
isValid bool
|
||||
}{
|
||||
{
|
||||
"default_values",
|
||||
Model{
|
||||
InstanceId: types.StringValue("iid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
},
|
||||
&sqlserverflex.GetInstanceResponse{
|
||||
Item: &sqlserverflex.Instance{},
|
||||
},
|
||||
&flavorModel{},
|
||||
&storageModel{},
|
||||
&optionsModel{},
|
||||
Model{
|
||||
Id: types.StringValue("pid,iid"),
|
||||
InstanceId: types.StringValue("iid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
Name: types.StringNull(),
|
||||
ACL: types.ListNull(types.StringType),
|
||||
BackupSchedule: types.StringNull(),
|
||||
Flavor: types.ObjectValueMust(flavorTypes, map[string]attr.Value{
|
||||
"id": types.StringNull(),
|
||||
"description": types.StringNull(),
|
||||
"cpu": types.Int64Null(),
|
||||
"ram": types.Int64Null(),
|
||||
}),
|
||||
Replicas: types.Int64Null(),
|
||||
Storage: types.ObjectValueMust(storageTypes, map[string]attr.Value{
|
||||
"class": types.StringNull(),
|
||||
"size": types.Int64Null(),
|
||||
}),
|
||||
Options: types.ObjectValueMust(optionsTypes, map[string]attr.Value{
|
||||
"edition": types.StringNull(),
|
||||
"retention_days": types.Int64Null(),
|
||||
}),
|
||||
Version: types.StringNull(),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"simple_values",
|
||||
Model{
|
||||
InstanceId: types.StringValue("iid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
},
|
||||
&sqlserverflex.GetInstanceResponse{
|
||||
Item: &sqlserverflex.Instance{
|
||||
Acl: &sqlserverflex.ACL{
|
||||
Items: &[]string{
|
||||
"ip1",
|
||||
"ip2",
|
||||
"",
|
||||
},
|
||||
},
|
||||
BackupSchedule: utils.Ptr("schedule"),
|
||||
Flavor: &sqlserverflex.Flavor{
|
||||
Cpu: utils.Ptr(int64(12)),
|
||||
Description: utils.Ptr("description"),
|
||||
Id: utils.Ptr("flavor_id"),
|
||||
Memory: utils.Ptr(int64(34)),
|
||||
},
|
||||
Id: utils.Ptr("iid"),
|
||||
Name: utils.Ptr("name"),
|
||||
Replicas: utils.Ptr(int64(56)),
|
||||
Status: utils.Ptr("status"),
|
||||
Storage: &sqlserverflex.Storage{
|
||||
Class: utils.Ptr("class"),
|
||||
Size: utils.Ptr(int64(78)),
|
||||
},
|
||||
Options: &map[string]string{
|
||||
"edition": "edition",
|
||||
"retentionDays": "1",
|
||||
},
|
||||
Version: utils.Ptr("version"),
|
||||
},
|
||||
},
|
||||
&flavorModel{},
|
||||
&storageModel{},
|
||||
&optionsModel{},
|
||||
Model{
|
||||
Id: types.StringValue("pid,iid"),
|
||||
InstanceId: types.StringValue("iid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
Name: types.StringValue("name"),
|
||||
ACL: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("ip1"),
|
||||
types.StringValue("ip2"),
|
||||
types.StringValue(""),
|
||||
}),
|
||||
BackupSchedule: types.StringValue("schedule"),
|
||||
Flavor: types.ObjectValueMust(flavorTypes, map[string]attr.Value{
|
||||
"id": types.StringValue("flavor_id"),
|
||||
"description": types.StringValue("description"),
|
||||
"cpu": types.Int64Value(12),
|
||||
"ram": types.Int64Value(34),
|
||||
}),
|
||||
Replicas: types.Int64Value(56),
|
||||
Storage: types.ObjectValueMust(storageTypes, map[string]attr.Value{
|
||||
"class": types.StringValue("class"),
|
||||
"size": types.Int64Value(78),
|
||||
}),
|
||||
Options: types.ObjectValueMust(optionsTypes, map[string]attr.Value{
|
||||
"edition": types.StringValue("edition"),
|
||||
"retention_days": types.Int64Value(1),
|
||||
}),
|
||||
Version: types.StringValue("version"),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"simple_values_no_flavor_and_storage",
|
||||
Model{
|
||||
InstanceId: types.StringValue("iid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
},
|
||||
&sqlserverflex.GetInstanceResponse{
|
||||
Item: &sqlserverflex.Instance{
|
||||
Acl: &sqlserverflex.ACL{
|
||||
Items: &[]string{
|
||||
"ip1",
|
||||
"ip2",
|
||||
"",
|
||||
},
|
||||
},
|
||||
BackupSchedule: utils.Ptr("schedule"),
|
||||
Flavor: nil,
|
||||
Id: utils.Ptr("iid"),
|
||||
Name: utils.Ptr("name"),
|
||||
Replicas: utils.Ptr(int64(56)),
|
||||
Status: utils.Ptr("status"),
|
||||
Storage: nil,
|
||||
Options: &map[string]string{
|
||||
"edition": "edition",
|
||||
"retentionDays": "1",
|
||||
},
|
||||
Version: utils.Ptr("version"),
|
||||
},
|
||||
},
|
||||
&flavorModel{
|
||||
CPU: types.Int64Value(12),
|
||||
RAM: types.Int64Value(34),
|
||||
},
|
||||
&storageModel{
|
||||
Class: types.StringValue("class"),
|
||||
Size: types.Int64Value(78),
|
||||
},
|
||||
&optionsModel{
|
||||
Edition: types.StringValue("edition"),
|
||||
RetentionDays: types.Int64Value(1),
|
||||
},
|
||||
Model{
|
||||
Id: types.StringValue("pid,iid"),
|
||||
InstanceId: types.StringValue("iid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
Name: types.StringValue("name"),
|
||||
ACL: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("ip1"),
|
||||
types.StringValue("ip2"),
|
||||
types.StringValue(""),
|
||||
}),
|
||||
BackupSchedule: types.StringValue("schedule"),
|
||||
Flavor: types.ObjectValueMust(flavorTypes, map[string]attr.Value{
|
||||
"id": types.StringNull(),
|
||||
"description": types.StringNull(),
|
||||
"cpu": types.Int64Value(12),
|
||||
"ram": types.Int64Value(34),
|
||||
}),
|
||||
Replicas: types.Int64Value(56),
|
||||
Storage: types.ObjectValueMust(storageTypes, map[string]attr.Value{
|
||||
"class": types.StringValue("class"),
|
||||
"size": types.Int64Value(78),
|
||||
}),
|
||||
Options: types.ObjectValueMust(optionsTypes, map[string]attr.Value{
|
||||
"edition": types.StringValue("edition"),
|
||||
"retention_days": types.Int64Value(1),
|
||||
}),
|
||||
Version: types.StringValue("version"),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"acls_unordered",
|
||||
Model{
|
||||
InstanceId: types.StringValue("iid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
ACL: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("ip2"),
|
||||
types.StringValue(""),
|
||||
types.StringValue("ip1"),
|
||||
}),
|
||||
},
|
||||
&sqlserverflex.GetInstanceResponse{
|
||||
Item: &sqlserverflex.Instance{
|
||||
Acl: &sqlserverflex.ACL{
|
||||
Items: &[]string{
|
||||
"",
|
||||
"ip1",
|
||||
"ip2",
|
||||
},
|
||||
},
|
||||
BackupSchedule: utils.Ptr("schedule"),
|
||||
Flavor: nil,
|
||||
Id: utils.Ptr("iid"),
|
||||
Name: utils.Ptr("name"),
|
||||
Replicas: utils.Ptr(int64(56)),
|
||||
Status: utils.Ptr("status"),
|
||||
Storage: nil,
|
||||
Options: &map[string]string{
|
||||
"edition": "edition",
|
||||
"retentionDays": "1",
|
||||
},
|
||||
Version: utils.Ptr("version"),
|
||||
},
|
||||
},
|
||||
&flavorModel{
|
||||
CPU: types.Int64Value(12),
|
||||
RAM: types.Int64Value(34),
|
||||
},
|
||||
&storageModel{
|
||||
Class: types.StringValue("class"),
|
||||
Size: types.Int64Value(78),
|
||||
},
|
||||
&optionsModel{},
|
||||
Model{
|
||||
Id: types.StringValue("pid,iid"),
|
||||
InstanceId: types.StringValue("iid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
Name: types.StringValue("name"),
|
||||
ACL: types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("ip2"),
|
||||
types.StringValue(""),
|
||||
types.StringValue("ip1"),
|
||||
}),
|
||||
BackupSchedule: types.StringValue("schedule"),
|
||||
Flavor: types.ObjectValueMust(flavorTypes, map[string]attr.Value{
|
||||
"id": types.StringNull(),
|
||||
"description": types.StringNull(),
|
||||
"cpu": types.Int64Value(12),
|
||||
"ram": types.Int64Value(34),
|
||||
}),
|
||||
Replicas: types.Int64Value(56),
|
||||
Storage: types.ObjectValueMust(storageTypes, map[string]attr.Value{
|
||||
"class": types.StringValue("class"),
|
||||
"size": types.Int64Value(78),
|
||||
}),
|
||||
Options: types.ObjectValueMust(optionsTypes, map[string]attr.Value{
|
||||
"edition": types.StringValue("edition"),
|
||||
"retention_days": types.Int64Value(1),
|
||||
}),
|
||||
Version: types.StringValue("version"),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"nil_response",
|
||||
Model{
|
||||
InstanceId: types.StringValue("iid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
},
|
||||
nil,
|
||||
&flavorModel{},
|
||||
&storageModel{},
|
||||
&optionsModel{},
|
||||
Model{},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"no_resource_id",
|
||||
Model{
|
||||
InstanceId: types.StringValue("iid"),
|
||||
ProjectId: types.StringValue("pid"),
|
||||
},
|
||||
&sqlserverflex.GetInstanceResponse{},
|
||||
&flavorModel{},
|
||||
&storageModel{},
|
||||
&optionsModel{},
|
||||
Model{},
|
||||
false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.description, func(t *testing.T) {
|
||||
err := mapFields(context.Background(), tt.input, &tt.state, tt.flavor, tt.storage, tt.options)
|
||||
if !tt.isValid && err == nil {
|
||||
t.Fatalf("Should have failed")
|
||||
}
|
||||
if tt.isValid && err != nil {
|
||||
t.Fatalf("Should not have failed: %v", err)
|
||||
}
|
||||
if tt.isValid {
|
||||
diff := cmp.Diff(tt.state, tt.expected)
|
||||
if diff != "" {
|
||||
t.Fatalf("Data does not match: %s", diff)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestToCreatePayload(t *testing.T) {
|
||||
tests := []struct {
|
||||
description string
|
||||
input *Model
|
||||
inputAcl []string
|
||||
inputFlavor *flavorModel
|
||||
inputStorage *storageModel
|
||||
inputOptions *optionsModel
|
||||
expected *sqlserverflex.CreateInstancePayload
|
||||
isValid bool
|
||||
}{
|
||||
{
|
||||
"default_values",
|
||||
&Model{},
|
||||
[]string{},
|
||||
&flavorModel{},
|
||||
&storageModel{},
|
||||
&optionsModel{},
|
||||
&sqlserverflex.CreateInstancePayload{
|
||||
Acl: &sqlserverflex.CreateInstancePayloadAcl{
|
||||
Items: &[]string{},
|
||||
},
|
||||
Storage: &sqlserverflex.CreateInstancePayloadStorage{},
|
||||
Options: &sqlserverflex.CreateInstancePayloadOptions{},
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"simple_values",
|
||||
&Model{
|
||||
BackupSchedule: types.StringValue("schedule"),
|
||||
Name: types.StringValue("name"),
|
||||
Replicas: types.Int64Value(12),
|
||||
Version: types.StringValue("version"),
|
||||
},
|
||||
[]string{
|
||||
"ip_1",
|
||||
"ip_2",
|
||||
},
|
||||
&flavorModel{
|
||||
Id: types.StringValue("flavor_id"),
|
||||
},
|
||||
&storageModel{
|
||||
Class: types.StringValue("class"),
|
||||
Size: types.Int64Value(34),
|
||||
},
|
||||
&optionsModel{
|
||||
Edition: types.StringValue("edition"),
|
||||
RetentionDays: types.Int64Value(1),
|
||||
},
|
||||
&sqlserverflex.CreateInstancePayload{
|
||||
Acl: &sqlserverflex.CreateInstancePayloadAcl{
|
||||
Items: &[]string{
|
||||
"ip_1",
|
||||
"ip_2",
|
||||
},
|
||||
},
|
||||
BackupSchedule: utils.Ptr("schedule"),
|
||||
FlavorId: utils.Ptr("flavor_id"),
|
||||
Name: utils.Ptr("name"),
|
||||
Storage: &sqlserverflex.CreateInstancePayloadStorage{
|
||||
Class: utils.Ptr("class"),
|
||||
Size: utils.Ptr(int64(34)),
|
||||
},
|
||||
Options: &sqlserverflex.CreateInstancePayloadOptions{
|
||||
Edition: utils.Ptr("edition"),
|
||||
RetentionDays: utils.Ptr("1"),
|
||||
},
|
||||
Version: utils.Ptr("version"),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"null_fields_and_int_conversions",
|
||||
&Model{
|
||||
BackupSchedule: types.StringNull(),
|
||||
Name: types.StringNull(),
|
||||
Replicas: types.Int64Value(2123456789),
|
||||
Version: types.StringNull(),
|
||||
},
|
||||
[]string{
|
||||
"",
|
||||
},
|
||||
&flavorModel{
|
||||
Id: types.StringNull(),
|
||||
},
|
||||
&storageModel{
|
||||
Class: types.StringNull(),
|
||||
Size: types.Int64Null(),
|
||||
},
|
||||
&optionsModel{
|
||||
Edition: types.StringNull(),
|
||||
RetentionDays: types.Int64Null(),
|
||||
},
|
||||
&sqlserverflex.CreateInstancePayload{
|
||||
Acl: &sqlserverflex.CreateInstancePayloadAcl{
|
||||
Items: &[]string{
|
||||
"",
|
||||
},
|
||||
},
|
||||
BackupSchedule: nil,
|
||||
FlavorId: nil,
|
||||
Name: nil,
|
||||
Storage: &sqlserverflex.CreateInstancePayloadStorage{
|
||||
Class: nil,
|
||||
Size: nil,
|
||||
},
|
||||
Options: &sqlserverflex.CreateInstancePayloadOptions{},
|
||||
Version: nil,
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"nil_model",
|
||||
nil,
|
||||
[]string{},
|
||||
&flavorModel{},
|
||||
&storageModel{},
|
||||
&optionsModel{},
|
||||
nil,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"nil_acl",
|
||||
&Model{},
|
||||
nil,
|
||||
&flavorModel{},
|
||||
&storageModel{},
|
||||
&optionsModel{},
|
||||
&sqlserverflex.CreateInstancePayload{
|
||||
Acl: &sqlserverflex.CreateInstancePayloadAcl{},
|
||||
Storage: &sqlserverflex.CreateInstancePayloadStorage{},
|
||||
Options: &sqlserverflex.CreateInstancePayloadOptions{},
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"nil_flavor",
|
||||
&Model{},
|
||||
[]string{},
|
||||
nil,
|
||||
&storageModel{},
|
||||
&optionsModel{},
|
||||
nil,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"nil_storage",
|
||||
&Model{},
|
||||
[]string{},
|
||||
&flavorModel{},
|
||||
nil,
|
||||
&optionsModel{},
|
||||
&sqlserverflex.CreateInstancePayload{
|
||||
Acl: &sqlserverflex.CreateInstancePayloadAcl{
|
||||
Items: &[]string{},
|
||||
},
|
||||
Storage: &sqlserverflex.CreateInstancePayloadStorage{},
|
||||
Options: &sqlserverflex.CreateInstancePayloadOptions{},
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"nil_options",
|
||||
&Model{},
|
||||
[]string{},
|
||||
&flavorModel{},
|
||||
&storageModel{},
|
||||
nil,
|
||||
&sqlserverflex.CreateInstancePayload{
|
||||
Acl: &sqlserverflex.CreateInstancePayloadAcl{
|
||||
Items: &[]string{},
|
||||
},
|
||||
Storage: &sqlserverflex.CreateInstancePayloadStorage{},
|
||||
Options: &sqlserverflex.CreateInstancePayloadOptions{},
|
||||
},
|
||||
true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.description, func(t *testing.T) {
|
||||
output, err := toCreatePayload(tt.input, tt.inputAcl, tt.inputFlavor, tt.inputStorage, tt.inputOptions)
|
||||
if !tt.isValid && err == nil {
|
||||
t.Fatalf("Should have failed")
|
||||
}
|
||||
if tt.isValid && err != nil {
|
||||
t.Fatalf("Should not have failed: %v", err)
|
||||
}
|
||||
if tt.isValid {
|
||||
diff := cmp.Diff(output, tt.expected)
|
||||
if diff != "" {
|
||||
t.Fatalf("Data does not match: %s", diff)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestToUpdatePayload(t *testing.T) {
|
||||
tests := []struct {
|
||||
description string
|
||||
input *Model
|
||||
inputAcl []string
|
||||
inputFlavor *flavorModel
|
||||
expected *sqlserverflex.PartialUpdateInstancePayload
|
||||
isValid bool
|
||||
}{
|
||||
{
|
||||
"default_values",
|
||||
&Model{},
|
||||
[]string{},
|
||||
&flavorModel{},
|
||||
&sqlserverflex.PartialUpdateInstancePayload{
|
||||
Acl: &sqlserverflex.CreateInstancePayloadAcl{
|
||||
Items: &[]string{},
|
||||
},
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"simple_values",
|
||||
&Model{
|
||||
BackupSchedule: types.StringValue("schedule"),
|
||||
Name: types.StringValue("name"),
|
||||
Replicas: types.Int64Value(12),
|
||||
Version: types.StringValue("version"),
|
||||
},
|
||||
[]string{
|
||||
"ip_1",
|
||||
"ip_2",
|
||||
},
|
||||
&flavorModel{
|
||||
Id: types.StringValue("flavor_id"),
|
||||
},
|
||||
&sqlserverflex.PartialUpdateInstancePayload{
|
||||
Acl: &sqlserverflex.CreateInstancePayloadAcl{
|
||||
Items: &[]string{
|
||||
"ip_1",
|
||||
"ip_2",
|
||||
},
|
||||
},
|
||||
BackupSchedule: utils.Ptr("schedule"),
|
||||
FlavorId: utils.Ptr("flavor_id"),
|
||||
Name: utils.Ptr("name"),
|
||||
Version: utils.Ptr("version"),
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"null_fields_and_int_conversions",
|
||||
&Model{
|
||||
BackupSchedule: types.StringNull(),
|
||||
Name: types.StringNull(),
|
||||
Replicas: types.Int64Value(2123456789),
|
||||
Version: types.StringNull(),
|
||||
},
|
||||
[]string{
|
||||
"",
|
||||
},
|
||||
&flavorModel{
|
||||
Id: types.StringNull(),
|
||||
},
|
||||
&sqlserverflex.PartialUpdateInstancePayload{
|
||||
Acl: &sqlserverflex.CreateInstancePayloadAcl{
|
||||
Items: &[]string{
|
||||
"",
|
||||
},
|
||||
},
|
||||
BackupSchedule: nil,
|
||||
FlavorId: nil,
|
||||
Name: nil,
|
||||
Version: nil,
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"nil_model",
|
||||
nil,
|
||||
[]string{},
|
||||
&flavorModel{},
|
||||
nil,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"nil_acl",
|
||||
&Model{},
|
||||
nil,
|
||||
&flavorModel{},
|
||||
&sqlserverflex.PartialUpdateInstancePayload{
|
||||
Acl: &sqlserverflex.CreateInstancePayloadAcl{},
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"nil_flavor",
|
||||
&Model{},
|
||||
[]string{},
|
||||
nil,
|
||||
nil,
|
||||
false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.description, func(t *testing.T) {
|
||||
output, err := toUpdatePayload(tt.input, tt.inputAcl, tt.inputFlavor)
|
||||
if !tt.isValid && err == nil {
|
||||
t.Fatalf("Should have failed")
|
||||
}
|
||||
if tt.isValid && err != nil {
|
||||
t.Fatalf("Should not have failed: %v", err)
|
||||
}
|
||||
if tt.isValid {
|
||||
diff := cmp.Diff(output, tt.expected)
|
||||
if diff != "" {
|
||||
t.Fatalf("Data does not match: %s", diff)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadFlavorId(t *testing.T) {
|
||||
tests := []struct {
|
||||
description string
|
||||
inputFlavor *flavorModel
|
||||
mockedResp *sqlserverflex.ListFlavorsResponse
|
||||
expected *flavorModel
|
||||
getFlavorsFails bool
|
||||
isValid bool
|
||||
}{
|
||||
{
|
||||
"ok_flavor",
|
||||
&flavorModel{
|
||||
CPU: types.Int64Value(2),
|
||||
RAM: types.Int64Value(8),
|
||||
},
|
||||
&sqlserverflex.ListFlavorsResponse{
|
||||
Flavors: &[]sqlserverflex.InstanceFlavorEntry{
|
||||
{
|
||||
Id: utils.Ptr("fid-1"),
|
||||
Cpu: utils.Ptr(int64(2)),
|
||||
Description: utils.Ptr("description"),
|
||||
Memory: utils.Ptr(int64(8)),
|
||||
},
|
||||
},
|
||||
},
|
||||
&flavorModel{
|
||||
Id: types.StringValue("fid-1"),
|
||||
Description: types.StringValue("description"),
|
||||
CPU: types.Int64Value(2),
|
||||
RAM: types.Int64Value(8),
|
||||
},
|
||||
false,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"ok_flavor_2",
|
||||
&flavorModel{
|
||||
CPU: types.Int64Value(2),
|
||||
RAM: types.Int64Value(8),
|
||||
},
|
||||
&sqlserverflex.ListFlavorsResponse{
|
||||
Flavors: &[]sqlserverflex.InstanceFlavorEntry{
|
||||
{
|
||||
Id: utils.Ptr("fid-1"),
|
||||
Cpu: utils.Ptr(int64(2)),
|
||||
Description: utils.Ptr("description"),
|
||||
Memory: utils.Ptr(int64(8)),
|
||||
},
|
||||
{
|
||||
Id: utils.Ptr("fid-2"),
|
||||
Cpu: utils.Ptr(int64(1)),
|
||||
Description: utils.Ptr("description"),
|
||||
Memory: utils.Ptr(int64(4)),
|
||||
},
|
||||
},
|
||||
},
|
||||
&flavorModel{
|
||||
Id: types.StringValue("fid-1"),
|
||||
Description: types.StringValue("description"),
|
||||
CPU: types.Int64Value(2),
|
||||
RAM: types.Int64Value(8),
|
||||
},
|
||||
false,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"no_matching_flavor",
|
||||
&flavorModel{
|
||||
CPU: types.Int64Value(2),
|
||||
RAM: types.Int64Value(8),
|
||||
},
|
||||
&sqlserverflex.ListFlavorsResponse{
|
||||
Flavors: &[]sqlserverflex.InstanceFlavorEntry{
|
||||
{
|
||||
Id: utils.Ptr("fid-1"),
|
||||
Cpu: utils.Ptr(int64(1)),
|
||||
Description: utils.Ptr("description"),
|
||||
Memory: utils.Ptr(int64(8)),
|
||||
},
|
||||
{
|
||||
Id: utils.Ptr("fid-2"),
|
||||
Cpu: utils.Ptr(int64(1)),
|
||||
Description: utils.Ptr("description"),
|
||||
Memory: utils.Ptr(int64(4)),
|
||||
},
|
||||
},
|
||||
},
|
||||
&flavorModel{
|
||||
CPU: types.Int64Value(2),
|
||||
RAM: types.Int64Value(8),
|
||||
},
|
||||
false,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"nil_response",
|
||||
&flavorModel{
|
||||
CPU: types.Int64Value(2),
|
||||
RAM: types.Int64Value(8),
|
||||
},
|
||||
&sqlserverflex.ListFlavorsResponse{},
|
||||
&flavorModel{
|
||||
CPU: types.Int64Value(2),
|
||||
RAM: types.Int64Value(8),
|
||||
},
|
||||
false,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"error_response",
|
||||
&flavorModel{
|
||||
CPU: types.Int64Value(2),
|
||||
RAM: types.Int64Value(8),
|
||||
},
|
||||
&sqlserverflex.ListFlavorsResponse{},
|
||||
&flavorModel{
|
||||
CPU: types.Int64Value(2),
|
||||
RAM: types.Int64Value(8),
|
||||
},
|
||||
true,
|
||||
false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.description, func(t *testing.T) {
|
||||
client := &sqlserverflexClientMocked{
|
||||
returnError: tt.getFlavorsFails,
|
||||
listFlavorsResp: tt.mockedResp,
|
||||
}
|
||||
model := &Model{
|
||||
ProjectId: types.StringValue("pid"),
|
||||
}
|
||||
flavorModel := &flavorModel{
|
||||
CPU: tt.inputFlavor.CPU,
|
||||
RAM: tt.inputFlavor.RAM,
|
||||
}
|
||||
err := loadFlavorId(context.Background(), client, model, flavorModel)
|
||||
if !tt.isValid && err == nil {
|
||||
t.Fatalf("Should have failed")
|
||||
}
|
||||
if tt.isValid && err != nil {
|
||||
t.Fatalf("Should not have failed: %v", err)
|
||||
}
|
||||
if tt.isValid {
|
||||
diff := cmp.Diff(flavorModel, tt.expected)
|
||||
if diff != "" {
|
||||
t.Fatalf("Data does not match: %s", diff)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,246 @@
|
|||
package sqlserverflex_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-testing/helper/acctest"
|
||||
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
|
||||
"github.com/hashicorp/terraform-plugin-testing/terraform"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/utils"
|
||||
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/config"
|
||||
"github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex"
|
||||
"github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex/wait"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
|
||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/testutil"
|
||||
)
|
||||
|
||||
// Instance resource data
|
||||
var instanceResource = map[string]string{
|
||||
"project_id": testutil.ProjectId,
|
||||
"name": fmt.Sprintf("tf-acc-%s", acctest.RandStringFromCharSet(7, acctest.CharSetAlphaNum)),
|
||||
"acl": "192.168.0.0/16",
|
||||
"flavor_cpu": "4",
|
||||
"flavor_ram": "16",
|
||||
"flavor_description": "SQLServer-Flex-4.16-Standard-EU01",
|
||||
"storage_class": "premium-perf2-stackit",
|
||||
"storage_size": "40",
|
||||
"version": "2022",
|
||||
"replicas": "1",
|
||||
"options_edition": "developer",
|
||||
"options_retention_days": "64",
|
||||
"flavor_id": "4.16-Single",
|
||||
"backup_schedule": "00 6 * * *",
|
||||
"backup_schedule_updated": "00 12 * * *",
|
||||
}
|
||||
|
||||
func configResources(backupSchedule string) string {
|
||||
return fmt.Sprintf(`
|
||||
%s
|
||||
|
||||
resource "stackit_sqlserverflex_instance" "instance" {
|
||||
project_id = "%s"
|
||||
name = "%s"
|
||||
acl = ["%s"]
|
||||
flavor = {
|
||||
cpu = %s
|
||||
ram = %s
|
||||
}
|
||||
storage = {
|
||||
class = "%s"
|
||||
size = %s
|
||||
}
|
||||
version = "%s"
|
||||
options = {
|
||||
edition = "%s"
|
||||
retention_days = %s
|
||||
}
|
||||
backup_schedule = "%s"
|
||||
}
|
||||
`,
|
||||
testutil.SQLServerFlexProviderConfig(),
|
||||
instanceResource["project_id"],
|
||||
instanceResource["name"],
|
||||
instanceResource["acl"],
|
||||
instanceResource["flavor_cpu"],
|
||||
instanceResource["flavor_ram"],
|
||||
instanceResource["storage_class"],
|
||||
instanceResource["storage_size"],
|
||||
instanceResource["version"],
|
||||
instanceResource["options_edition"],
|
||||
instanceResource["options_retention_days"],
|
||||
backupSchedule,
|
||||
)
|
||||
}
|
||||
|
||||
func TestAccSQLServerFlexResource(t *testing.T) {
|
||||
resource.Test(t, resource.TestCase{
|
||||
ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
|
||||
CheckDestroy: testAccChecksqlserverflexDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
// Creation
|
||||
{
|
||||
Config: configResources(instanceResource["backup_schedule"]),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// Instance
|
||||
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "project_id", instanceResource["project_id"]),
|
||||
resource.TestCheckResourceAttrSet("stackit_sqlserverflex_instance.instance", "instance_id"),
|
||||
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "name", instanceResource["name"]),
|
||||
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "acl.#", "1"),
|
||||
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "acl.0", instanceResource["acl"]),
|
||||
resource.TestCheckResourceAttrSet("stackit_sqlserverflex_instance.instance", "flavor.id"),
|
||||
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "flavor.description", instanceResource["flavor_description"]),
|
||||
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "replicas", instanceResource["replicas"]),
|
||||
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "flavor.cpu", instanceResource["flavor_cpu"]),
|
||||
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "flavor.ram", instanceResource["flavor_ram"]),
|
||||
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "storage.class", instanceResource["storage_class"]),
|
||||
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "storage.size", instanceResource["storage_size"]),
|
||||
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "version", instanceResource["version"]),
|
||||
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "options.edition", instanceResource["options_edition"]),
|
||||
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "options.retention_days", instanceResource["options_retention_days"]),
|
||||
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "backup_schedule", instanceResource["backup_schedule"]),
|
||||
),
|
||||
},
|
||||
// data source
|
||||
{
|
||||
Config: fmt.Sprintf(`
|
||||
%s
|
||||
|
||||
data "stackit_sqlserverflex_instance" "instance" {
|
||||
project_id = stackit_sqlserverflex_instance.instance.project_id
|
||||
instance_id = stackit_sqlserverflex_instance.instance.instance_id
|
||||
}
|
||||
`,
|
||||
configResources(instanceResource["backup_schedule"]),
|
||||
),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// Instance data
|
||||
resource.TestCheckResourceAttr("data.stackit_sqlserverflex_instance.instance", "project_id", instanceResource["project_id"]),
|
||||
resource.TestCheckResourceAttr("data.stackit_sqlserverflex_instance.instance", "name", instanceResource["name"]),
|
||||
resource.TestCheckResourceAttrPair(
|
||||
"data.stackit_sqlserverflex_instance.instance", "project_id",
|
||||
"stackit_sqlserverflex_instance.instance", "project_id",
|
||||
),
|
||||
resource.TestCheckResourceAttrPair(
|
||||
"data.stackit_sqlserverflex_instance.instance", "instance_id",
|
||||
"stackit_sqlserverflex_instance.instance", "instance_id",
|
||||
),
|
||||
resource.TestCheckResourceAttr("data.stackit_sqlserverflex_instance.instance", "acl.#", "1"),
|
||||
resource.TestCheckResourceAttr("data.stackit_sqlserverflex_instance.instance", "acl.0", instanceResource["acl"]),
|
||||
resource.TestCheckResourceAttr("data.stackit_sqlserverflex_instance.instance", "flavor.id", instanceResource["flavor_id"]),
|
||||
resource.TestCheckResourceAttr("data.stackit_sqlserverflex_instance.instance", "flavor.description", instanceResource["flavor_description"]),
|
||||
resource.TestCheckResourceAttr("data.stackit_sqlserverflex_instance.instance", "flavor.cpu", instanceResource["flavor_cpu"]),
|
||||
resource.TestCheckResourceAttr("data.stackit_sqlserverflex_instance.instance", "flavor.ram", instanceResource["flavor_ram"]),
|
||||
resource.TestCheckResourceAttr("data.stackit_sqlserverflex_instance.instance", "replicas", instanceResource["replicas"]),
|
||||
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "options.edition", instanceResource["options_edition"]),
|
||||
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "options.retention_days", instanceResource["options_retention_days"]),
|
||||
resource.TestCheckResourceAttr("data.stackit_sqlserverflex_instance.instance", "backup_schedule", instanceResource["backup_schedule"]),
|
||||
),
|
||||
},
|
||||
// Import
|
||||
{
|
||||
ResourceName: "stackit_sqlserverflex_instance.instance",
|
||||
ImportStateIdFunc: func(s *terraform.State) (string, error) {
|
||||
r, ok := s.RootModule().Resources["stackit_sqlserverflex_instance.instance"]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("couldn't find resource stackit_sqlserverflex_instance.instance")
|
||||
}
|
||||
instanceId, ok := r.Primary.Attributes["instance_id"]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("couldn't find attribute instance_id")
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s,%s", testutil.ProjectId, instanceId), nil
|
||||
},
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
ImportStateVerifyIgnore: []string{"backup_schedule"},
|
||||
ImportStateCheck: func(s []*terraform.InstanceState) error {
|
||||
if len(s) != 1 {
|
||||
return fmt.Errorf("expected 1 state, got %d", len(s))
|
||||
}
|
||||
if s[0].Attributes["backup_schedule"] != instanceResource["backup_schedule"] {
|
||||
return fmt.Errorf("expected backup_schedule %s, got %s", instanceResource["backup_schedule"], s[0].Attributes["backup_schedule"])
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
// Update
|
||||
{
|
||||
Config: configResources(instanceResource["backup_schedule_updated"]),
|
||||
Check: resource.ComposeAggregateTestCheckFunc(
|
||||
// Instance data
|
||||
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "project_id", instanceResource["project_id"]),
|
||||
resource.TestCheckResourceAttrSet("stackit_sqlserverflex_instance.instance", "instance_id"),
|
||||
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "name", instanceResource["name"]),
|
||||
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "acl.#", "1"),
|
||||
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "acl.0", instanceResource["acl"]),
|
||||
resource.TestCheckResourceAttrSet("stackit_sqlserverflex_instance.instance", "flavor.id"),
|
||||
resource.TestCheckResourceAttrSet("stackit_sqlserverflex_instance.instance", "flavor.description"),
|
||||
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "flavor.cpu", instanceResource["flavor_cpu"]),
|
||||
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "flavor.ram", instanceResource["flavor_ram"]),
|
||||
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "replicas", instanceResource["replicas"]),
|
||||
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "storage.class", instanceResource["storage_class"]),
|
||||
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "storage.size", instanceResource["storage_size"]),
|
||||
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "version", instanceResource["version"]),
|
||||
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "options.edition", instanceResource["options_edition"]),
|
||||
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "options.retention_days", instanceResource["options_retention_days"]),
|
||||
resource.TestCheckResourceAttr("stackit_sqlserverflex_instance.instance", "backup_schedule", instanceResource["backup_schedule_updated"]),
|
||||
),
|
||||
},
|
||||
// Deletion is done by the framework implicitly
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccChecksqlserverflexDestroy(s *terraform.State) error {
|
||||
ctx := context.Background()
|
||||
var client *sqlserverflex.APIClient
|
||||
var err error
|
||||
if testutil.SQLServerFlexCustomEndpoint == "" {
|
||||
client, err = sqlserverflex.NewAPIClient()
|
||||
} else {
|
||||
client, err = sqlserverflex.NewAPIClient(
|
||||
config.WithEndpoint(testutil.SQLServerFlexCustomEndpoint),
|
||||
)
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating client: %w", err)
|
||||
}
|
||||
|
||||
instancesToDestroy := []string{}
|
||||
for _, rs := range s.RootModule().Resources {
|
||||
if rs.Type != "stackit_sqlserverflex_instance" {
|
||||
continue
|
||||
}
|
||||
// instance terraform ID: = "[project_id],[instance_id]"
|
||||
instanceId := strings.Split(rs.Primary.ID, core.Separator)[1]
|
||||
instancesToDestroy = append(instancesToDestroy, instanceId)
|
||||
}
|
||||
|
||||
instancesResp, err := client.ListInstances(ctx, testutil.ProjectId).Execute()
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting instancesResp: %w", err)
|
||||
}
|
||||
|
||||
items := *instancesResp.Items
|
||||
for i := range items {
|
||||
if items[i].Id == nil {
|
||||
continue
|
||||
}
|
||||
if utils.Contains(instancesToDestroy, *items[i].Id) {
|
||||
err := client.DeleteInstanceExecute(ctx, testutil.ProjectId, *items[i].Id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("destroying instance %s during CheckDestroy: %w", *items[i].Id, err)
|
||||
}
|
||||
_, err = wait.DeleteInstanceWaitHandler(ctx, client, testutil.ProjectId, *items[i].Id).WaitWithContext(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("destroying instance %s during CheckDestroy: waiting for deletion %w", *items[i].Id, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -52,6 +52,7 @@ var (
|
|||
RedisCustomEndpoint = os.Getenv("TF_ACC_REDIS_CUSTOM_ENDPOINT")
|
||||
ResourceManagerCustomEndpoint = os.Getenv("TF_ACC_RESOURCEMANAGER_CUSTOM_ENDPOINT")
|
||||
SecretsManagerCustomEndpoint = os.Getenv("TF_ACC_SECRETSMANAGER_CUSTOM_ENDPOINT")
|
||||
SQLServerFlexCustomEndpoint = os.Getenv("TF_ACC_SQLSERVERFLEX_CUSTOM_ENDPOINT")
|
||||
SKECustomEndpoint = os.Getenv("TF_ACC_SKE_CUSTOM_ENDPOINT")
|
||||
|
||||
// OpenStack user domain name
|
||||
|
|
@ -294,6 +295,21 @@ func SecretsManagerProviderConfig() string {
|
|||
)
|
||||
}
|
||||
|
||||
func SQLServerFlexProviderConfig() string {
|
||||
if MongoDBFlexCustomEndpoint == "" {
|
||||
return `
|
||||
provider "stackit" {
|
||||
region = "eu01"
|
||||
}`
|
||||
}
|
||||
return fmt.Sprintf(`
|
||||
provider "stackit" {
|
||||
sqlserverflex_custom_endpoint = "%s"
|
||||
}`,
|
||||
SQLServerFlexCustomEndpoint,
|
||||
)
|
||||
}
|
||||
|
||||
func SKEProviderConfig() string {
|
||||
if SKECustomEndpoint == "" {
|
||||
return `
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ package utils
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
|
||||
|
||||
|
|
@ -60,3 +62,17 @@ func ListValuetoStringSlice(list basetypes.ListValue) ([]string, error) {
|
|||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Remove leading 0s from backup schedule numbers (e.g. "00 00 * * *" becomes "0 0 * * *")
|
||||
// Needed as the API does it internally and would otherwise cause inconsistent result in Terraform
|
||||
func SimplifyBackupSchedule(schedule string) string {
|
||||
regex := regexp.MustCompile(`0+\d+`) // Matches series of one or more zeros followed by a series of one or more digits
|
||||
simplifiedSchedule := regex.ReplaceAllStringFunc(schedule, func(match string) string {
|
||||
simplified := strings.TrimLeft(match, "0")
|
||||
if simplified == "" {
|
||||
simplified = "0"
|
||||
}
|
||||
return simplified
|
||||
})
|
||||
return simplifiedSchedule
|
||||
}
|
||||
|
|
|
|||
|
|
@ -120,3 +120,75 @@ func TestListValuetoStrSlice(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSimplifyBackupSchedule(t *testing.T) {
|
||||
tests := []struct {
|
||||
description string
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
"simple schedule",
|
||||
"0 0 * * *",
|
||||
"0 0 * * *",
|
||||
},
|
||||
{
|
||||
"schedule with leading zeros",
|
||||
"00 00 * * *",
|
||||
"0 0 * * *",
|
||||
},
|
||||
{
|
||||
"schedule with leading zeros 2",
|
||||
"00 001 * * *",
|
||||
"0 1 * * *",
|
||||
},
|
||||
{
|
||||
"schedule with leading zeros 3",
|
||||
"00 0010 * * *",
|
||||
"0 10 * * *",
|
||||
},
|
||||
{
|
||||
"simple schedule with slash",
|
||||
"0 0/6 * * *",
|
||||
"0 0/6 * * *",
|
||||
},
|
||||
{
|
||||
"schedule with leading zeros and slash",
|
||||
"00 00/6 * * *",
|
||||
"0 0/6 * * *",
|
||||
},
|
||||
{
|
||||
"schedule with leading zeros and slash 2",
|
||||
"00 001/06 * * *",
|
||||
"0 1/6 * * *",
|
||||
},
|
||||
{
|
||||
"simple schedule with comma",
|
||||
"0 10,15 * * *",
|
||||
"0 10,15 * * *",
|
||||
},
|
||||
{
|
||||
"schedule with leading zeros and comma",
|
||||
"0 010,0015 * * *",
|
||||
"0 10,15 * * *",
|
||||
},
|
||||
{
|
||||
"simple schedule with comma and slash",
|
||||
"0 0-11/10 * * *",
|
||||
"0 0-11/10 * * *",
|
||||
},
|
||||
{
|
||||
"schedule with leading zeros, comma, and slash",
|
||||
"00 000-011/010 * * *",
|
||||
"0 0-11/10 * * *",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.description, func(t *testing.T) {
|
||||
output := SimplifyBackupSchedule(tt.input)
|
||||
if output != tt.expected {
|
||||
t.Fatalf("Data does not match: %s", output)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ import (
|
|||
skeCluster "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/ske/cluster"
|
||||
skeKubeconfig "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/ske/kubeconfig"
|
||||
skeProject "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/ske/project"
|
||||
sqlServerFlexInstance "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/sqlserverflex/instance"
|
||||
|
||||
sdkauth "github.com/stackitcloud/stackit-sdk-go/core/auth"
|
||||
"github.com/stackitcloud/stackit-sdk-go/core/config"
|
||||
|
|
@ -83,6 +84,7 @@ type providerModel struct {
|
|||
PrivateKeyPath types.String `tfsdk:"private_key_path"`
|
||||
Token types.String `tfsdk:"service_account_token"`
|
||||
Region types.String `tfsdk:"region"`
|
||||
ArgusCustomEndpoint types.String `tfsdk:"argus_custom_endpoint"`
|
||||
DNSCustomEndpoint types.String `tfsdk:"dns_custom_endpoint"`
|
||||
IaaSCustomEndpoint types.String `tfsdk:"iaas_custom_endpoint"`
|
||||
PostgreSQLCustomEndpoint types.String `tfsdk:"postgresql_custom_endpoint"`
|
||||
|
|
@ -96,7 +98,7 @@ type providerModel struct {
|
|||
OpenSearchCustomEndpoint types.String `tfsdk:"opensearch_custom_endpoint"`
|
||||
RedisCustomEndpoint types.String `tfsdk:"redis_custom_endpoint"`
|
||||
SecretsManagerCustomEndpoint types.String `tfsdk:"secretsmanager_custom_endpoint"`
|
||||
ArgusCustomEndpoint types.String `tfsdk:"argus_custom_endpoint"`
|
||||
SQLServerFlexCustomEndpoint types.String `tfsdk:"sqlserverflex_custom_endpoint"`
|
||||
SKECustomEndpoint types.String `tfsdk:"ske_custom_endpoint"`
|
||||
ResourceManagerCustomEndpoint types.String `tfsdk:"resourcemanager_custom_endpoint"`
|
||||
TokenCustomEndpoint types.String `tfsdk:"token_custom_endpoint"`
|
||||
|
|
@ -127,9 +129,10 @@ func (p *Provider) Schema(_ context.Context, _ provider.SchemaRequest, resp *pro
|
|||
"postgresql_custom_endpoint": "Custom endpoint for the PostgreSQL service",
|
||||
"postgresflex_custom_endpoint": "Custom endpoint for the PostgresFlex service",
|
||||
"redis_custom_endpoint": "Custom endpoint for the Redis service",
|
||||
"ske_custom_endpoint": "Custom endpoint for the Kubernetes Engine (SKE) service",
|
||||
"resourcemanager_custom_endpoint": "Custom endpoint for the Resource Manager service",
|
||||
"secretsmanager_custom_endpoint": "Custom endpoint for the Secrets Manager service",
|
||||
"sqlserverflex_custom_endpoint": "Custom endpoint for the SQL Server Flex service",
|
||||
"ske_custom_endpoint": "Custom endpoint for the Kubernetes Engine (SKE) service",
|
||||
"token_custom_endpoint": "Custom endpoint for the token API, which is used to request access tokens when using the key flow",
|
||||
"jwks_custom_endpoint": "Custom endpoint for the jwks API, which is used to get the json web key sets (jwks) to validate tokens when using the key flow",
|
||||
}
|
||||
|
|
@ -168,6 +171,10 @@ func (p *Provider) Schema(_ context.Context, _ provider.SchemaRequest, resp *pro
|
|||
Optional: true,
|
||||
Description: descriptions["region"],
|
||||
},
|
||||
"argus_custom_endpoint": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Description: descriptions["argus_custom_endpoint"],
|
||||
},
|
||||
"dns_custom_endpoint": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Description: descriptions["dns_custom_endpoint"],
|
||||
|
|
@ -216,22 +223,22 @@ func (p *Provider) Schema(_ context.Context, _ provider.SchemaRequest, resp *pro
|
|||
Optional: true,
|
||||
Description: descriptions["redis_custom_endpoint"],
|
||||
},
|
||||
"resourcemanager_custom_endpoint": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Description: descriptions["resourcemanager_custom_endpoint"],
|
||||
},
|
||||
"secretsmanager_custom_endpoint": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Description: descriptions["secretsmanager_custom_endpoint"],
|
||||
},
|
||||
"argus_custom_endpoint": schema.StringAttribute{
|
||||
"sqlserverflex_custom_endpoint": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Description: descriptions["argus_custom_endpoint"],
|
||||
Description: descriptions["sqlserverflex_custom_endpoint"],
|
||||
},
|
||||
"ske_custom_endpoint": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Description: descriptions["ske_custom_endpoint"],
|
||||
},
|
||||
"resourcemanager_custom_endpoint": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Description: descriptions["resourcemanager_custom_endpoint"],
|
||||
},
|
||||
"token_custom_endpoint": schema.StringAttribute{
|
||||
Optional: true,
|
||||
Description: descriptions["token_custom_endpoint"],
|
||||
|
|
@ -283,6 +290,9 @@ func (p *Provider) Configure(ctx context.Context, req provider.ConfigureRequest,
|
|||
if !(providerConfig.Region.IsUnknown() || providerConfig.Region.IsNull()) {
|
||||
providerData.Region = providerConfig.Region.ValueString()
|
||||
}
|
||||
if !(providerConfig.ArgusCustomEndpoint.IsUnknown() || providerConfig.ArgusCustomEndpoint.IsNull()) {
|
||||
providerData.ArgusCustomEndpoint = providerConfig.ArgusCustomEndpoint.ValueString()
|
||||
}
|
||||
if !(providerConfig.DNSCustomEndpoint.IsUnknown() || providerConfig.DNSCustomEndpoint.IsNull()) {
|
||||
providerData.DnsCustomEndpoint = providerConfig.DNSCustomEndpoint.ValueString()
|
||||
}
|
||||
|
|
@ -319,18 +329,18 @@ func (p *Provider) Configure(ctx context.Context, req provider.ConfigureRequest,
|
|||
if !(providerConfig.RedisCustomEndpoint.IsUnknown() || providerConfig.RedisCustomEndpoint.IsNull()) {
|
||||
providerData.RedisCustomEndpoint = providerConfig.RedisCustomEndpoint.ValueString()
|
||||
}
|
||||
if !(providerConfig.ArgusCustomEndpoint.IsUnknown() || providerConfig.ArgusCustomEndpoint.IsNull()) {
|
||||
providerData.ArgusCustomEndpoint = providerConfig.ArgusCustomEndpoint.ValueString()
|
||||
}
|
||||
if !(providerConfig.SKECustomEndpoint.IsUnknown() || providerConfig.SKECustomEndpoint.IsNull()) {
|
||||
providerData.SKECustomEndpoint = providerConfig.SKECustomEndpoint.ValueString()
|
||||
}
|
||||
if !(providerConfig.ResourceManagerCustomEndpoint.IsUnknown() || providerConfig.ResourceManagerCustomEndpoint.IsNull()) {
|
||||
providerData.ResourceManagerCustomEndpoint = providerConfig.ResourceManagerCustomEndpoint.ValueString()
|
||||
}
|
||||
if !(providerConfig.SecretsManagerCustomEndpoint.IsUnknown() || providerConfig.SecretsManagerCustomEndpoint.IsNull()) {
|
||||
providerData.SecretsManagerCustomEndpoint = providerConfig.SecretsManagerCustomEndpoint.ValueString()
|
||||
}
|
||||
if !(providerConfig.SQLServerFlexCustomEndpoint.IsUnknown() || providerConfig.SQLServerFlexCustomEndpoint.IsNull()) {
|
||||
providerData.SQLServerFlexCustomEndpoint = providerConfig.SQLServerFlexCustomEndpoint.ValueString()
|
||||
}
|
||||
if !(providerConfig.SKECustomEndpoint.IsUnknown() || providerConfig.SKECustomEndpoint.IsNull()) {
|
||||
providerData.SKECustomEndpoint = providerConfig.SKECustomEndpoint.ValueString()
|
||||
}
|
||||
if !(providerConfig.TokenCustomEndpoint.IsUnknown() || providerConfig.TokenCustomEndpoint.IsNull()) {
|
||||
sdkConfig.TokenCustomUrl = providerConfig.TokenCustomEndpoint.ValueString()
|
||||
}
|
||||
|
|
@ -378,6 +388,7 @@ func (p *Provider) DataSources(_ context.Context) []func() datasource.DataSource
|
|||
resourceManagerProject.NewProjectDataSource,
|
||||
secretsManagerInstance.NewInstanceDataSource,
|
||||
secretsManagerUser.NewUserDataSource,
|
||||
sqlServerFlexInstance.NewInstanceDataSource,
|
||||
skeProject.NewProjectDataSource,
|
||||
skeCluster.NewClusterDataSource,
|
||||
}
|
||||
|
|
@ -417,6 +428,7 @@ func (p *Provider) Resources(_ context.Context) []func() resource.Resource {
|
|||
resourceManagerProject.NewProjectResource,
|
||||
secretsManagerInstance.NewInstanceResource,
|
||||
secretsManagerUser.NewUserResource,
|
||||
sqlServerFlexInstance.NewInstanceResource,
|
||||
skeProject.NewProjectResource,
|
||||
skeCluster.NewClusterResource,
|
||||
skeKubeconfig.NewKubeconfigResource,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue