feat: update sql server flex configuration for user and database (#46)
## Description
<!-- **Please link some issue here describing what you are trying to achieve.**
In case there is no issue present for your PR, please consider creating one.
At least please give us some description what you are trying to achieve and why your change is needed. -->
relates to #1234
## Checklist
- [ ] Issue was linked above
- [ ] Code format was applied: `make fmt`
- [ ] Examples were added / adjusted (see `examples/` directory)
- [x] Docs are up-to-date: `make generate-docs` (will be checked by CI)
- [ ] Unit tests got implemented or updated
- [ ] Acceptance tests got implemented or updated (see e.g. [here](f5f99d1709/stackit/internal/services/dns/dns_acc_test.go))
- [x] Unit tests are passing: `make test` (will be checked by CI)
- [x] No linter issues: `make lint` (will be checked by CI)
Reviewed-on: #46
Reviewed-by: Marcel_Henselin <marcel.henselin@stackit.cloud>
Co-authored-by: Andre Harms <andre.harms@stackit.cloud>
Co-committed-by: Andre Harms <andre.harms@stackit.cloud>
This commit is contained in:
parent
e21fe64326
commit
399e8ccb0c
23 changed files with 3959 additions and 1764 deletions
|
|
@ -2,9 +2,12 @@ package sqlserverflexalpha
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||||
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
|
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexalpha"
|
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexalpha"
|
||||||
|
|
@ -12,6 +15,7 @@ import (
|
||||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core"
|
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core"
|
||||||
sqlserverflexalphaGen "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/database/datasources_gen"
|
sqlserverflexalphaGen "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/database/datasources_gen"
|
||||||
sqlserverflexUtils "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/utils"
|
sqlserverflexUtils "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/utils"
|
||||||
|
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// dataSourceModel maps the data source schema data.
|
// dataSourceModel maps the data source schema data.
|
||||||
|
|
@ -22,6 +26,7 @@ type dataSourceModel struct {
|
||||||
|
|
||||||
var _ datasource.DataSource = (*databaseDataSource)(nil)
|
var _ datasource.DataSource = (*databaseDataSource)(nil)
|
||||||
|
|
||||||
|
// NewDatabaseDataSource creates a new database data source.
|
||||||
func NewDatabaseDataSource() datasource.DataSource {
|
func NewDatabaseDataSource() datasource.DataSource {
|
||||||
return &databaseDataSource{}
|
return &databaseDataSource{}
|
||||||
}
|
}
|
||||||
|
|
@ -31,6 +36,7 @@ type databaseDataSource struct {
|
||||||
providerData core.ProviderData
|
providerData core.ProviderData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Metadata returns the data source type name.
|
||||||
func (d *databaseDataSource) Metadata(
|
func (d *databaseDataSource) Metadata(
|
||||||
_ context.Context,
|
_ context.Context,
|
||||||
req datasource.MetadataRequest,
|
req datasource.MetadataRequest,
|
||||||
|
|
@ -39,6 +45,7 @@ func (d *databaseDataSource) Metadata(
|
||||||
resp.TypeName = req.ProviderTypeName + "_sqlserverflexalpha_database"
|
resp.TypeName = req.ProviderTypeName + "_sqlserverflexalpha_database"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Schema defines the data source schema.
|
||||||
func (d *databaseDataSource) Schema(ctx context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
func (d *databaseDataSource) Schema(ctx context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
||||||
s := sqlserverflexalphaGen.DatabaseDataSourceSchema(ctx)
|
s := sqlserverflexalphaGen.DatabaseDataSourceSchema(ctx)
|
||||||
s.Attributes["id"] = schema.StringAttribute{
|
s.Attributes["id"] = schema.StringAttribute{
|
||||||
|
|
@ -67,24 +74,92 @@ func (d *databaseDataSource) Configure(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
d.client = apiClient
|
d.client = apiClient
|
||||||
tflog.Info(ctx, "SQL SERVER Flex database client configured")
|
tflog.Info(ctx, "SQL SERVER Flex alpha database client configured")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read retrieves the resource's state from the API.
|
||||||
func (d *databaseDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
|
func (d *databaseDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
|
||||||
var data dataSourceModel
|
|
||||||
|
|
||||||
// Read Terraform configuration data into the model
|
|
||||||
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
|
|
||||||
|
|
||||||
|
var model dataSourceModel
|
||||||
|
diags := req.Config.Get(ctx, &model)
|
||||||
|
resp.Diagnostics.Append(diags...)
|
||||||
if resp.Diagnostics.HasError() {
|
if resp.Diagnostics.HasError() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Todo: Read API call logic
|
ctx = core.InitProviderContext(ctx)
|
||||||
|
|
||||||
// Example data value setting
|
// Extract identifiers from the plan
|
||||||
// data.Id = types.StringValue("example-id")
|
projectId := model.ProjectId.ValueString()
|
||||||
|
instanceId := model.InstanceId.ValueString()
|
||||||
|
region := d.providerData.GetRegionWithOverride(model.Region)
|
||||||
|
databaseName := model.DatabaseName.ValueString()
|
||||||
|
|
||||||
// Save data into Terraform state
|
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
ctx = tflog.SetField(ctx, "instance_id", instanceId)
|
||||||
|
ctx = tflog.SetField(ctx, "region", region)
|
||||||
|
ctx = tflog.SetField(ctx, "database_name", databaseName)
|
||||||
|
|
||||||
|
// Fetch database from the API
|
||||||
|
databaseResp, err := d.client.GetDatabaseRequest(ctx, projectId, region, instanceId, databaseName).Execute()
|
||||||
|
|
||||||
|
if resp.Diagnostics.HasError() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
handleReadError(ctx, &resp.Diagnostics, err, projectId, instanceId)
|
||||||
|
resp.State.RemoveResource(ctx)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = core.LogResponse(ctx)
|
||||||
|
|
||||||
|
// Map response body to schema and populate Computed attribute values
|
||||||
|
err = mapFields(databaseResp, &model, region)
|
||||||
|
if err != nil {
|
||||||
|
core.LogAndAddError(
|
||||||
|
ctx,
|
||||||
|
&resp.Diagnostics,
|
||||||
|
"Error reading database",
|
||||||
|
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, "SQL Server Flex alpha database read")
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleReadError centralizes API error handling for the Read operation.
|
||||||
|
func handleReadError(ctx context.Context, diags *diag.Diagnostics, err error, projectId, instanceId string) {
|
||||||
|
utils.LogError(
|
||||||
|
ctx,
|
||||||
|
diags,
|
||||||
|
err,
|
||||||
|
"Reading database",
|
||||||
|
fmt.Sprintf(
|
||||||
|
"Could not retrieve database for instance %q in project %q.",
|
||||||
|
instanceId,
|
||||||
|
projectId,
|
||||||
|
),
|
||||||
|
map[int]string{
|
||||||
|
http.StatusBadRequest: fmt.Sprintf(
|
||||||
|
"Invalid request parameters for project %q and instance %q.",
|
||||||
|
projectId,
|
||||||
|
instanceId,
|
||||||
|
),
|
||||||
|
http.StatusNotFound: fmt.Sprintf(
|
||||||
|
"Database, instance %q, or project %q not found.",
|
||||||
|
instanceId,
|
||||||
|
projectId,
|
||||||
|
),
|
||||||
|
http.StatusForbidden: fmt.Sprintf("Forbidden access to project %q.", projectId),
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,97 @@
|
||||||
|
package sqlserverflexalpha
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||||
|
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexalpha"
|
||||||
|
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// mapFields maps fields from a ListDatabase API response to a resourceModel for the data source.
|
||||||
|
func mapFields(source *sqlserverflexalpha.GetDatabaseResponse, model *dataSourceModel, region string) error {
|
||||||
|
if source == nil {
|
||||||
|
return fmt.Errorf("response is nil")
|
||||||
|
}
|
||||||
|
if source.Id == nil || *source.Id == 0 {
|
||||||
|
return fmt.Errorf("id not present")
|
||||||
|
}
|
||||||
|
if model == nil {
|
||||||
|
return fmt.Errorf("model given is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
var databaseId int64
|
||||||
|
if model.Id.ValueInt64() != 0 {
|
||||||
|
databaseId = model.Id.ValueInt64()
|
||||||
|
} else if source.Id != nil {
|
||||||
|
databaseId = *source.Id
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("database id not present")
|
||||||
|
}
|
||||||
|
|
||||||
|
model.Id = types.Int64Value(databaseId)
|
||||||
|
model.DatabaseName = types.StringValue(source.GetName())
|
||||||
|
model.Name = types.StringValue(source.GetName())
|
||||||
|
model.Owner = types.StringValue(strings.Trim(source.GetOwner(), "\""))
|
||||||
|
model.Region = types.StringValue(region)
|
||||||
|
model.ProjectId = types.StringValue(model.ProjectId.ValueString())
|
||||||
|
model.InstanceId = types.StringValue(model.InstanceId.ValueString())
|
||||||
|
model.CompatibilityLevel = types.Int64Value(source.GetCompatibilityLevel())
|
||||||
|
model.CollationName = types.StringValue(source.GetCollationName())
|
||||||
|
|
||||||
|
model.TerraformID = utils.BuildInternalTerraformId(
|
||||||
|
model.ProjectId.ValueString(),
|
||||||
|
region,
|
||||||
|
model.InstanceId.ValueString(),
|
||||||
|
model.DatabaseName.ValueString(),
|
||||||
|
)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// mapResourceFields maps fields from a ListDatabase API response to a resourceModel for the resource.
|
||||||
|
func mapResourceFields(source *sqlserverflexalpha.GetDatabaseResponse, model *resourceModel, region string) error {
|
||||||
|
if source == nil {
|
||||||
|
return fmt.Errorf("response is nil")
|
||||||
|
}
|
||||||
|
if source.Id == nil || *source.Id == 0 {
|
||||||
|
return fmt.Errorf("id not present")
|
||||||
|
}
|
||||||
|
if model == nil {
|
||||||
|
return fmt.Errorf("model input is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
var databaseId int64
|
||||||
|
if model.Id.ValueInt64() != 0 {
|
||||||
|
databaseId = model.Id.ValueInt64()
|
||||||
|
} else if source.Id != nil {
|
||||||
|
databaseId = *source.Id
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("database id not present")
|
||||||
|
}
|
||||||
|
|
||||||
|
model.Id = types.Int64Value(databaseId)
|
||||||
|
model.DatabaseName = types.StringValue(source.GetName())
|
||||||
|
model.Name = types.StringValue(source.GetName())
|
||||||
|
model.Owner = types.StringValue(strings.Trim(source.GetOwner(), "\""))
|
||||||
|
model.Region = types.StringValue(region)
|
||||||
|
model.ProjectId = types.StringValue(model.ProjectId.ValueString())
|
||||||
|
model.InstanceId = types.StringValue(model.InstanceId.ValueString())
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// toCreatePayload converts the resource model to an API create payload.
|
||||||
|
func toCreatePayload(model *resourceModel) (*sqlserverflexalpha.CreateDatabaseRequestPayload, error) {
|
||||||
|
if model == nil {
|
||||||
|
return nil, fmt.Errorf("nil model")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &sqlserverflexalpha.CreateDatabaseRequestPayload{
|
||||||
|
Name: model.Name.ValueStringPointer(),
|
||||||
|
Owner: model.Owner.ValueStringPointer(),
|
||||||
|
Collation: model.Collation.ValueStringPointer(),
|
||||||
|
Compatibility: model.Compatibility.ValueInt64Pointer(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,227 @@
|
||||||
|
package sqlserverflexalpha
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||||
|
"github.com/stackitcloud/stackit-sdk-go/core/utils"
|
||||||
|
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexalpha"
|
||||||
|
datasource "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/database/datasources_gen"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMapFields(t *testing.T) {
|
||||||
|
type given struct {
|
||||||
|
source *sqlserverflexalpha.GetDatabaseResponse
|
||||||
|
model *dataSourceModel
|
||||||
|
region string
|
||||||
|
}
|
||||||
|
type expected struct {
|
||||||
|
model *dataSourceModel
|
||||||
|
err bool
|
||||||
|
}
|
||||||
|
|
||||||
|
testcases := []struct {
|
||||||
|
name string
|
||||||
|
given given
|
||||||
|
expected expected
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "should map fields correctly",
|
||||||
|
given: given{
|
||||||
|
source: &sqlserverflexalpha.GetDatabaseResponse{
|
||||||
|
Id: utils.Ptr(int64(1)),
|
||||||
|
Name: utils.Ptr("my-db"),
|
||||||
|
CollationName: utils.Ptr("collation"),
|
||||||
|
CompatibilityLevel: utils.Ptr(int64(150)),
|
||||||
|
Owner: utils.Ptr("\"my-owner\""),
|
||||||
|
},
|
||||||
|
model: &dataSourceModel{
|
||||||
|
DatabaseModel: datasource.DatabaseModel{
|
||||||
|
ProjectId: types.StringValue("my-project"),
|
||||||
|
InstanceId: types.StringValue("my-instance"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
region: "eu01",
|
||||||
|
},
|
||||||
|
expected: expected{
|
||||||
|
model: &dataSourceModel{
|
||||||
|
DatabaseModel: datasource.DatabaseModel{
|
||||||
|
Id: types.Int64Value(1),
|
||||||
|
Name: types.StringValue("my-db"),
|
||||||
|
DatabaseName: types.StringValue("my-db"),
|
||||||
|
Owner: types.StringValue("my-owner"),
|
||||||
|
Region: types.StringValue("eu01"),
|
||||||
|
InstanceId: types.StringValue("my-instance"),
|
||||||
|
ProjectId: types.StringValue("my-project"),
|
||||||
|
CompatibilityLevel: types.Int64Value(150),
|
||||||
|
CollationName: types.StringValue("collation"),
|
||||||
|
},
|
||||||
|
TerraformID: types.StringValue("my-project,eu01,my-instance,my-db"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "should fail on nil source",
|
||||||
|
given: given{
|
||||||
|
source: nil,
|
||||||
|
model: &dataSourceModel{},
|
||||||
|
},
|
||||||
|
expected: expected{err: true},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "should fail on nil source ID",
|
||||||
|
given: given{
|
||||||
|
source: &sqlserverflexalpha.GetDatabaseResponse{Id: nil},
|
||||||
|
model: &dataSourceModel{},
|
||||||
|
},
|
||||||
|
expected: expected{err: true},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "should fail on nil model",
|
||||||
|
given: given{
|
||||||
|
source: &sqlserverflexalpha.GetDatabaseResponse{Id: utils.Ptr(int64(1))},
|
||||||
|
model: nil,
|
||||||
|
},
|
||||||
|
expected: expected{err: true},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testcases {
|
||||||
|
t.Run(
|
||||||
|
tc.name, func(t *testing.T) {
|
||||||
|
err := mapFields(tc.given.source, tc.given.model, tc.given.region)
|
||||||
|
if (err != nil) != tc.expected.err {
|
||||||
|
t.Fatalf("expected error: %v, got: %v", tc.expected.err, err)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
if diff := cmp.Diff(tc.expected.model, tc.given.model); diff != "" {
|
||||||
|
t.Errorf("model mismatch (-want +got):\n%s", diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMapResourceFields(t *testing.T) {
|
||||||
|
type given struct {
|
||||||
|
source *sqlserverflexalpha.GetDatabaseResponse
|
||||||
|
model *resourceModel
|
||||||
|
region string
|
||||||
|
}
|
||||||
|
type expected struct {
|
||||||
|
model *resourceModel
|
||||||
|
err bool
|
||||||
|
}
|
||||||
|
|
||||||
|
testcases := []struct {
|
||||||
|
name string
|
||||||
|
given given
|
||||||
|
expected expected
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "should map fields correctly",
|
||||||
|
given: given{
|
||||||
|
source: &sqlserverflexalpha.GetDatabaseResponse{
|
||||||
|
Id: utils.Ptr(int64(1)),
|
||||||
|
Name: utils.Ptr("my-db"),
|
||||||
|
Owner: utils.Ptr("\"my-owner\""),
|
||||||
|
},
|
||||||
|
model: &resourceModel{
|
||||||
|
ProjectId: types.StringValue("my-project"),
|
||||||
|
InstanceId: types.StringValue("my-instance"),
|
||||||
|
},
|
||||||
|
region: "eu01",
|
||||||
|
},
|
||||||
|
expected: expected{
|
||||||
|
model: &resourceModel{
|
||||||
|
Id: types.Int64Value(1),
|
||||||
|
Name: types.StringValue("my-db"),
|
||||||
|
DatabaseName: types.StringValue("my-db"),
|
||||||
|
InstanceId: types.StringValue("my-instance"),
|
||||||
|
ProjectId: types.StringValue("my-project"),
|
||||||
|
Region: types.StringValue("eu01"),
|
||||||
|
Owner: types.StringValue("my-owner"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "should fail on nil source",
|
||||||
|
given: given{
|
||||||
|
source: nil,
|
||||||
|
model: &resourceModel{},
|
||||||
|
},
|
||||||
|
expected: expected{err: true},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testcases {
|
||||||
|
t.Run(
|
||||||
|
tc.name, func(t *testing.T) {
|
||||||
|
err := mapResourceFields(tc.given.source, tc.given.model, tc.given.region)
|
||||||
|
if (err != nil) != tc.expected.err {
|
||||||
|
t.Fatalf("expected error: %v, got: %v", tc.expected.err, err)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
if diff := cmp.Diff(tc.expected.model, tc.given.model); diff != "" {
|
||||||
|
t.Errorf("model mismatch (-want +got):\n%s", diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestToCreatePayload(t *testing.T) {
|
||||||
|
type given struct {
|
||||||
|
model *resourceModel
|
||||||
|
}
|
||||||
|
type expected struct {
|
||||||
|
payload *sqlserverflexalpha.CreateDatabaseRequestPayload
|
||||||
|
err bool
|
||||||
|
}
|
||||||
|
|
||||||
|
testcases := []struct {
|
||||||
|
name string
|
||||||
|
given given
|
||||||
|
expected expected
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "should convert model to payload",
|
||||||
|
given: given{
|
||||||
|
model: &resourceModel{
|
||||||
|
Name: types.StringValue("my-db"),
|
||||||
|
Owner: types.StringValue("my-owner"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: expected{
|
||||||
|
payload: &sqlserverflexalpha.CreateDatabaseRequestPayload{
|
||||||
|
Name: utils.Ptr("my-db"),
|
||||||
|
Owner: utils.Ptr("my-owner"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "should fail on nil model",
|
||||||
|
given: given{model: nil},
|
||||||
|
expected: expected{err: true},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testcases {
|
||||||
|
t.Run(
|
||||||
|
tc.name, func(t *testing.T) {
|
||||||
|
actual, err := toCreatePayload(tc.given.model)
|
||||||
|
if (err != nil) != tc.expected.err {
|
||||||
|
t.Fatalf("expected error: %v, got: %v", tc.expected.err, err)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
if diff := cmp.Diff(tc.expected.payload, actual); diff != "" {
|
||||||
|
t.Errorf("payload mismatch (-want +got):\n%s", diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,7 +3,9 @@ package sqlserverflexalpha
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
_ "embed"
|
_ "embed"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
|
@ -13,6 +15,7 @@ import (
|
||||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||||
"github.com/stackitcloud/stackit-sdk-go/core/config"
|
"github.com/stackitcloud/stackit-sdk-go/core/config"
|
||||||
|
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
|
||||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexalpha"
|
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexalpha"
|
||||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
|
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
|
||||||
|
|
||||||
|
|
@ -28,6 +31,13 @@ var (
|
||||||
_ resource.ResourceWithImportState = &databaseResource{}
|
_ resource.ResourceWithImportState = &databaseResource{}
|
||||||
_ resource.ResourceWithModifyPlan = &databaseResource{}
|
_ resource.ResourceWithModifyPlan = &databaseResource{}
|
||||||
_ resource.ResourceWithIdentity = &databaseResource{}
|
_ resource.ResourceWithIdentity = &databaseResource{}
|
||||||
|
|
||||||
|
// Define errors
|
||||||
|
errDatabaseNotFound = errors.New("database not found")
|
||||||
|
|
||||||
|
// Error message constants
|
||||||
|
extractErrorSummary = "extracting failed"
|
||||||
|
extractErrorMessage = "Extracting identity data: %v"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewDatabaseResource() resource.Resource {
|
func NewDatabaseResource() resource.Resource {
|
||||||
|
|
@ -137,73 +147,230 @@ func (r *databaseResource) Configure(
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *databaseResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
|
func (r *databaseResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
|
||||||
var data sqlserverflexalphaGen.DatabaseModel
|
var model resourceModel
|
||||||
|
|
||||||
// Read Terraform plan data into the model
|
|
||||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
|
|
||||||
|
|
||||||
|
diags := req.Plan.Get(ctx, &model)
|
||||||
|
resp.Diagnostics.Append(diags...)
|
||||||
if resp.Diagnostics.HasError() {
|
if resp.Diagnostics.HasError() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Create API call logic
|
// Read identity data
|
||||||
|
var identityData DatabaseResourceIdentityModel
|
||||||
|
resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...)
|
||||||
|
if resp.Diagnostics.HasError() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Example data value setting
|
ctx = core.InitProviderContext(ctx)
|
||||||
// data.DatabaseId = types.StringValue("id-from-response")
|
|
||||||
|
|
||||||
// Save data into Terraform state
|
projectId := identityData.ProjectID.ValueString()
|
||||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
region := identityData.ProjectID.ValueString()
|
||||||
|
instanceId := identityData.InstanceID.ValueString()
|
||||||
|
|
||||||
|
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||||
|
ctx = tflog.SetField(ctx, "instance_id", instanceId)
|
||||||
|
ctx = tflog.SetField(ctx, "region", region)
|
||||||
|
|
||||||
|
// Generate API request body from model
|
||||||
|
payload, err := toCreatePayload(&model)
|
||||||
|
if err != nil {
|
||||||
|
core.LogAndAddError(
|
||||||
|
ctx,
|
||||||
|
&resp.Diagnostics,
|
||||||
|
"Error creating database",
|
||||||
|
fmt.Sprintf("Creating API payload: %v", err),
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Create new database
|
||||||
|
databaseResp, err := r.client.CreateDatabaseRequest(
|
||||||
|
ctx,
|
||||||
|
projectId,
|
||||||
|
region,
|
||||||
|
instanceId,
|
||||||
|
).CreateDatabaseRequestPayload(*payload).Execute()
|
||||||
|
if err != nil {
|
||||||
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating database", fmt.Sprintf("Calling API: %v", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = core.LogResponse(ctx)
|
||||||
|
|
||||||
|
if databaseResp == nil || databaseResp.Id == nil {
|
||||||
|
core.LogAndAddError(
|
||||||
|
ctx,
|
||||||
|
&resp.Diagnostics,
|
||||||
|
"Error creating database",
|
||||||
|
"API didn't return database Id. A database might have been created",
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
databaseId := *databaseResp.Id
|
||||||
|
databaseName := model.DatabaseName.String()
|
||||||
|
|
||||||
|
ctx = tflog.SetField(ctx, "database_id", databaseId)
|
||||||
|
ctx = tflog.SetField(ctx, "database_name", databaseName)
|
||||||
|
|
||||||
|
ctx = core.LogResponse(ctx)
|
||||||
|
|
||||||
|
database, err := r.client.GetDatabaseRequest(ctx, projectId, region, instanceId, databaseName).Execute()
|
||||||
|
if err != nil {
|
||||||
|
core.LogAndAddError(
|
||||||
|
ctx,
|
||||||
|
&resp.Diagnostics,
|
||||||
|
"Error creating database",
|
||||||
|
fmt.Sprintf("Getting database details after creation: %v", err),
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map response body to schema
|
||||||
|
err = mapResourceFields(database, &model, region)
|
||||||
|
if err != nil {
|
||||||
|
core.LogAndAddError(
|
||||||
|
ctx,
|
||||||
|
&resp.Diagnostics,
|
||||||
|
"Error creating database",
|
||||||
|
fmt.Sprintf("Processing API payload: %v", err),
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set data returned by API in identity
|
||||||
|
identity := DatabaseResourceIdentityModel{
|
||||||
|
ProjectID: types.StringValue(projectId),
|
||||||
|
Region: types.StringValue(region),
|
||||||
|
InstanceID: types.StringValue(instanceId),
|
||||||
|
DatabaseName: types.StringValue(databaseName),
|
||||||
|
}
|
||||||
|
resp.Diagnostics.Append(resp.Identity.Set(ctx, identity)...)
|
||||||
|
if resp.Diagnostics.HasError() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set state to fully populated data
|
||||||
|
resp.Diagnostics.Append(resp.State.Set(ctx, model)...)
|
||||||
|
if resp.Diagnostics.HasError() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
tflog.Info(ctx, "sqlserverflexalpha.Database created")
|
tflog.Info(ctx, "sqlserverflexalpha.Database created")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *databaseResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
|
func (r *databaseResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
|
||||||
var data resourceModel
|
var model resourceModel
|
||||||
|
diags := req.State.Get(ctx, &model)
|
||||||
// Read Terraform prior state data into the model
|
resp.Diagnostics.Append(diags...)
|
||||||
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
|
|
||||||
|
|
||||||
if resp.Diagnostics.HasError() {
|
if resp.Diagnostics.HasError() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Todo: Read API call logic
|
// Read identity data
|
||||||
|
var identityData DatabaseResourceIdentityModel
|
||||||
|
resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...)
|
||||||
|
if resp.Diagnostics.HasError() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Save updated data into Terraform state
|
ctx = core.InitProviderContext(ctx)
|
||||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
|
||||||
|
projectId, instanceId, region, databaseName, errExt := r.extractIdentityData(model, identityData)
|
||||||
|
if errExt != nil {
|
||||||
|
core.LogAndAddError(
|
||||||
|
ctx,
|
||||||
|
&resp.Diagnostics,
|
||||||
|
extractErrorSummary,
|
||||||
|
fmt.Sprintf(extractErrorMessage, errExt),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||||
|
ctx = tflog.SetField(ctx, "instance_id", instanceId)
|
||||||
|
ctx = tflog.SetField(ctx, "region", region)
|
||||||
|
ctx = tflog.SetField(ctx, "database_name", databaseName)
|
||||||
|
|
||||||
|
databaseResp, err := r.client.GetDatabaseRequest(ctx, projectId, region, instanceId, databaseName).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) || errors.Is(err, errDatabaseNotFound) {
|
||||||
|
resp.State.RemoveResource(ctx)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading database", fmt.Sprintf("Calling API: %v", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = core.LogResponse(ctx)
|
||||||
|
|
||||||
|
// Map response body to schema
|
||||||
|
err = mapResourceFields(databaseResp, &model, region)
|
||||||
|
if err != nil {
|
||||||
|
core.LogAndAddError(
|
||||||
|
ctx,
|
||||||
|
&resp.Diagnostics,
|
||||||
|
"Error reading database",
|
||||||
|
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, "sqlserverflexalpha.Database read")
|
tflog.Info(ctx, "sqlserverflexalpha.Database read")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *databaseResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
|
func (r *databaseResource) Update(ctx context.Context, _ resource.UpdateRequest, resp *resource.UpdateResponse) {
|
||||||
var data resourceModel
|
// TODO: Check update api endpoint - not available at the moment, so return an error for now
|
||||||
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating database", "Database can't be updated")
|
||||||
// Read Terraform plan data into the model
|
|
||||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
|
|
||||||
|
|
||||||
if resp.Diagnostics.HasError() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Todo: Update API call logic
|
|
||||||
|
|
||||||
// Save updated data into Terraform state
|
|
||||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
|
||||||
|
|
||||||
tflog.Info(ctx, "sqlserverflexalpha.Database updated")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *databaseResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
|
func (r *databaseResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
|
||||||
var data sqlserverflexalphaGen.DatabaseModel
|
// nolint:gocritic // function signature required by Terraform
|
||||||
|
var model resourceModel
|
||||||
// Read Terraform prior state data into the model
|
diags := req.State.Get(ctx, &model)
|
||||||
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
|
resp.Diagnostics.Append(diags...)
|
||||||
|
|
||||||
if resp.Diagnostics.HasError() {
|
if resp.Diagnostics.HasError() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Todo: Delete API call logic
|
// Read identity data
|
||||||
|
var identityData DatabaseResourceIdentityModel
|
||||||
|
resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...)
|
||||||
|
if resp.Diagnostics.HasError() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = core.InitProviderContext(ctx)
|
||||||
|
|
||||||
|
projectId, instanceId, region, databaseName, errExt := r.extractIdentityData(model, identityData)
|
||||||
|
if errExt != nil {
|
||||||
|
core.LogAndAddError(
|
||||||
|
ctx,
|
||||||
|
&resp.Diagnostics,
|
||||||
|
extractErrorSummary,
|
||||||
|
fmt.Sprintf(extractErrorMessage, errExt),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||||
|
ctx = tflog.SetField(ctx, "instance_id", instanceId)
|
||||||
|
ctx = tflog.SetField(ctx, "region", region)
|
||||||
|
ctx = tflog.SetField(ctx, "database_name", databaseName)
|
||||||
|
|
||||||
|
// Delete existing record set
|
||||||
|
err := r.client.DeleteDatabaseRequestExecute(ctx, projectId, region, instanceId, databaseName)
|
||||||
|
if err != nil {
|
||||||
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting database", fmt.Sprintf("Calling API: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = core.LogResponse(ctx)
|
||||||
|
|
||||||
tflog.Info(ctx, "sqlserverflexalpha.Database deleted")
|
tflog.Info(ctx, "sqlserverflexalpha.Database deleted")
|
||||||
}
|
}
|
||||||
|
|
@ -312,3 +479,46 @@ func (r *databaseResource) ImportState(
|
||||||
|
|
||||||
tflog.Info(ctx, "Sqlserverflexalpha database state imported")
|
tflog.Info(ctx, "Sqlserverflexalpha database state imported")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// extractIdentityData extracts essential identifiers from the resource model, falling back to the identity model.
|
||||||
|
func (r *databaseResource) extractIdentityData(
|
||||||
|
model resourceModel,
|
||||||
|
identity DatabaseResourceIdentityModel,
|
||||||
|
) (projectId, region, instanceId, databaseName string, err error) {
|
||||||
|
if !model.DatabaseName.IsNull() && !model.DatabaseName.IsUnknown() {
|
||||||
|
databaseName = model.DatabaseName.ValueString()
|
||||||
|
} else {
|
||||||
|
if identity.DatabaseName.IsNull() || identity.DatabaseName.IsUnknown() {
|
||||||
|
return "", "", "", "", fmt.Errorf("database_name not found in config")
|
||||||
|
}
|
||||||
|
databaseName = identity.DatabaseName.ValueString()
|
||||||
|
}
|
||||||
|
|
||||||
|
if !model.ProjectId.IsNull() && !model.ProjectId.IsUnknown() {
|
||||||
|
projectId = model.ProjectId.ValueString()
|
||||||
|
} else {
|
||||||
|
if identity.ProjectID.IsNull() || identity.ProjectID.IsUnknown() {
|
||||||
|
return "", "", "", "", fmt.Errorf("project_id not found in config")
|
||||||
|
}
|
||||||
|
projectId = identity.ProjectID.ValueString()
|
||||||
|
}
|
||||||
|
|
||||||
|
if !model.Region.IsNull() && !model.Region.IsUnknown() {
|
||||||
|
region = r.providerData.GetRegionWithOverride(model.Region)
|
||||||
|
} else {
|
||||||
|
if identity.Region.IsNull() || identity.Region.IsUnknown() {
|
||||||
|
return "", "", "", "", fmt.Errorf("region not found in config")
|
||||||
|
}
|
||||||
|
region = r.providerData.GetRegionWithOverride(identity.Region)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !model.InstanceId.IsNull() && !model.InstanceId.IsUnknown() {
|
||||||
|
instanceId = model.InstanceId.ValueString()
|
||||||
|
} else {
|
||||||
|
if identity.InstanceID.IsNull() || identity.InstanceID.IsUnknown() {
|
||||||
|
return "", "", "", "", fmt.Errorf("instance_id not found in config")
|
||||||
|
}
|
||||||
|
instanceId = identity.InstanceID.ValueString()
|
||||||
|
}
|
||||||
|
return projectId, region, instanceId, databaseName, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -4,23 +4,19 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/hashicorp/terraform-plugin-framework-validators/int64validator"
|
"github.com/hashicorp/terraform-plugin-framework-validators/int64validator"
|
||||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||||
|
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexalpha"
|
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexalpha"
|
||||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
|
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
|
||||||
sqlserverflexUtils "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/utils"
|
|
||||||
|
|
||||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
|
||||||
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
|
|
||||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
|
||||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core"
|
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core"
|
||||||
|
sqlserverflexUtils "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/utils"
|
||||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/utils"
|
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/utils"
|
||||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/validate"
|
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/validate"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
|
|
||||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Ensure the implementation satisfies the expected interfaces.
|
// Ensure the implementation satisfies the expected interfaces.
|
||||||
|
|
@ -34,6 +30,7 @@ func NewUserDataSource() datasource.DataSource {
|
||||||
}
|
}
|
||||||
|
|
||||||
type dataSourceModel struct {
|
type dataSourceModel struct {
|
||||||
|
//TODO: check generated data source for the correct types and pointers
|
||||||
Id types.String `tfsdk:"id"` // needed by TF
|
Id types.String `tfsdk:"id"` // needed by TF
|
||||||
UserId types.Int64 `tfsdk:"user_id"`
|
UserId types.Int64 `tfsdk:"user_id"`
|
||||||
InstanceId types.String `tfsdk:"instance_id"`
|
InstanceId types.String `tfsdk:"instance_id"`
|
||||||
|
|
@ -79,7 +76,7 @@ func (r *userDataSource) Configure(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
r.client = apiClient
|
r.client = apiClient
|
||||||
tflog.Info(ctx, "SQLServer Flex user client configured")
|
tflog.Info(ctx, "SQLServer Flex beta user client configured")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Schema defines the schema for the data source.
|
// Schema defines the schema for the data source.
|
||||||
|
|
@ -223,50 +220,5 @@ func (r *userDataSource) Read(
|
||||||
if resp.Diagnostics.HasError() {
|
if resp.Diagnostics.HasError() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
tflog.Info(ctx, "SQLServer Flex instance read")
|
tflog.Info(ctx, "SQLServer Flex alpha instance read")
|
||||||
}
|
|
||||||
|
|
||||||
func mapDataSourceFields(userResp *sqlserverflexalpha.GetUserResponse, model *dataSourceModel, region string) error {
|
|
||||||
if userResp == nil {
|
|
||||||
return fmt.Errorf("response is nil")
|
|
||||||
}
|
|
||||||
if model == nil {
|
|
||||||
return fmt.Errorf("model input is nil")
|
|
||||||
}
|
|
||||||
user := userResp
|
|
||||||
|
|
||||||
var userId int64
|
|
||||||
if model.UserId.ValueInt64() != 0 {
|
|
||||||
userId = model.UserId.ValueInt64()
|
|
||||||
} else if user.Id != nil {
|
|
||||||
userId = *user.Id
|
|
||||||
} else {
|
|
||||||
return fmt.Errorf("user id not present")
|
|
||||||
}
|
|
||||||
model.Id = utils.BuildInternalTerraformId(
|
|
||||||
model.ProjectId.ValueString(), region, model.InstanceId.ValueString(), strconv.FormatInt(userId, 10),
|
|
||||||
)
|
|
||||||
model.UserId = types.Int64Value(userId)
|
|
||||||
model.Username = types.StringPointerValue(user.Username)
|
|
||||||
|
|
||||||
if user.Roles == nil {
|
|
||||||
model.Roles = types.SetNull(types.StringType)
|
|
||||||
} else {
|
|
||||||
var roles []attr.Value
|
|
||||||
for _, role := range *user.Roles {
|
|
||||||
roles = append(roles, types.StringValue(string(role)))
|
|
||||||
}
|
|
||||||
rolesSet, diags := types.SetValue(types.StringType, roles)
|
|
||||||
if diags.HasError() {
|
|
||||||
return fmt.Errorf("failed to map roles: %w", core.DiagsToError(diags))
|
|
||||||
}
|
|
||||||
model.Roles = rolesSet
|
|
||||||
}
|
|
||||||
model.Host = types.StringPointerValue(user.Host)
|
|
||||||
model.Port = types.Int64PointerValue(user.Port)
|
|
||||||
model.Region = types.StringValue(region)
|
|
||||||
model.Status = types.StringPointerValue(user.Status)
|
|
||||||
model.DefaultDatabase = types.StringPointerValue(user.DefaultDatabase)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,147 +0,0 @@
|
||||||
package sqlserverflexalpha
|
|
||||||
|
|
||||||
import (
|
|
||||||
"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"
|
|
||||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexalpha"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestMapDataSourceFields(t *testing.T) {
|
|
||||||
const testRegion = "region"
|
|
||||||
tests := []struct {
|
|
||||||
description string
|
|
||||||
input *sqlserverflexalpha.GetUserResponse
|
|
||||||
region string
|
|
||||||
expected dataSourceModel
|
|
||||||
isValid bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
"default_values",
|
|
||||||
&sqlserverflexalpha.GetUserResponse{},
|
|
||||||
testRegion,
|
|
||||||
dataSourceModel{
|
|
||||||
Id: types.StringValue("pid,region,iid,1"),
|
|
||||||
UserId: types.Int64Value(1),
|
|
||||||
InstanceId: types.StringValue("iid"),
|
|
||||||
ProjectId: types.StringValue("pid"),
|
|
||||||
Username: types.StringNull(),
|
|
||||||
Roles: types.SetNull(types.StringType),
|
|
||||||
Host: types.StringNull(),
|
|
||||||
Port: types.Int64Null(),
|
|
||||||
Region: types.StringValue(testRegion),
|
|
||||||
Status: types.StringNull(),
|
|
||||||
DefaultDatabase: types.StringNull(),
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"simple_values",
|
|
||||||
&sqlserverflexalpha.GetUserResponse{
|
|
||||||
|
|
||||||
Roles: &[]sqlserverflexalpha.UserRole{
|
|
||||||
"role_1",
|
|
||||||
"role_2",
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
Username: utils.Ptr("username"),
|
|
||||||
Host: utils.Ptr("host"),
|
|
||||||
Port: utils.Ptr(int64(1234)),
|
|
||||||
Status: utils.Ptr("active"),
|
|
||||||
DefaultDatabase: utils.Ptr("default_db"),
|
|
||||||
},
|
|
||||||
testRegion,
|
|
||||||
dataSourceModel{
|
|
||||||
Id: types.StringValue("pid,region,iid,1"),
|
|
||||||
UserId: types.Int64Value(1),
|
|
||||||
InstanceId: types.StringValue("iid"),
|
|
||||||
ProjectId: types.StringValue("pid"),
|
|
||||||
Username: types.StringValue("username"),
|
|
||||||
Roles: types.SetValueMust(
|
|
||||||
types.StringType, []attr.Value{
|
|
||||||
types.StringValue("role_1"),
|
|
||||||
types.StringValue("role_2"),
|
|
||||||
types.StringValue(""),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
Host: types.StringValue("host"),
|
|
||||||
Port: types.Int64Value(1234),
|
|
||||||
Region: types.StringValue(testRegion),
|
|
||||||
Status: types.StringValue("active"),
|
|
||||||
DefaultDatabase: types.StringValue("default_db"),
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"null_fields_and_int_conversions",
|
|
||||||
&sqlserverflexalpha.GetUserResponse{
|
|
||||||
Id: utils.Ptr(int64(1)),
|
|
||||||
Roles: &[]sqlserverflexalpha.UserRole{},
|
|
||||||
Username: nil,
|
|
||||||
Host: nil,
|
|
||||||
Port: utils.Ptr(int64(2123456789)),
|
|
||||||
},
|
|
||||||
testRegion,
|
|
||||||
dataSourceModel{
|
|
||||||
Id: types.StringValue("pid,region,iid,1"),
|
|
||||||
UserId: types.Int64Value(1),
|
|
||||||
InstanceId: types.StringValue("iid"),
|
|
||||||
ProjectId: types.StringValue("pid"),
|
|
||||||
Username: types.StringNull(),
|
|
||||||
Roles: types.SetValueMust(types.StringType, []attr.Value{}),
|
|
||||||
Host: types.StringNull(),
|
|
||||||
Port: types.Int64Value(2123456789),
|
|
||||||
Region: types.StringValue(testRegion),
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"nil_response",
|
|
||||||
nil,
|
|
||||||
testRegion,
|
|
||||||
dataSourceModel{},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"nil_response_2",
|
|
||||||
&sqlserverflexalpha.GetUserResponse{},
|
|
||||||
testRegion,
|
|
||||||
dataSourceModel{},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"no_resource_id",
|
|
||||||
&sqlserverflexalpha.GetUserResponse{},
|
|
||||||
testRegion,
|
|
||||||
dataSourceModel{},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(
|
|
||||||
tt.description, func(t *testing.T) {
|
|
||||||
state := &dataSourceModel{
|
|
||||||
ProjectId: tt.expected.ProjectId,
|
|
||||||
InstanceId: tt.expected.InstanceId,
|
|
||||||
UserId: tt.expected.UserId,
|
|
||||||
}
|
|
||||||
err := mapDataSourceFields(tt.input, state, tt.region)
|
|
||||||
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(state, &tt.expected)
|
|
||||||
if diff != "" {
|
|
||||||
t.Fatalf("Data does not match: %s", diff)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
179
stackit/internal/services/sqlserverflexalpha/user/mapper.go
Normal file
179
stackit/internal/services/sqlserverflexalpha/user/mapper.go
Normal file
|
|
@ -0,0 +1,179 @@
|
||||||
|
package sqlserverflexalpha
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework/attr"
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||||
|
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexalpha"
|
||||||
|
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
|
||||||
|
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core"
|
||||||
|
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// mapDataSourceFields maps the API response to a dataSourceModel.
|
||||||
|
func mapDataSourceFields(userResp *sqlserverflexalpha.GetUserResponse, model *dataSourceModel, region string) error {
|
||||||
|
if userResp == nil {
|
||||||
|
return fmt.Errorf("response is nil")
|
||||||
|
}
|
||||||
|
if model == nil {
|
||||||
|
return fmt.Errorf("model input is nil")
|
||||||
|
}
|
||||||
|
user := userResp
|
||||||
|
|
||||||
|
// Handle user ID
|
||||||
|
var userId int64
|
||||||
|
if model.UserId.ValueInt64() != 0 {
|
||||||
|
userId = model.UserId.ValueInt64()
|
||||||
|
} else if user.Id != nil {
|
||||||
|
userId = *user.Id
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("user id not present")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set main attributes
|
||||||
|
model.Id = utils.BuildInternalTerraformId(
|
||||||
|
model.ProjectId.ValueString(), region, model.InstanceId.ValueString(), strconv.FormatInt(userId, 10),
|
||||||
|
)
|
||||||
|
model.UserId = types.Int64Value(userId)
|
||||||
|
model.Username = types.StringPointerValue(user.Username)
|
||||||
|
|
||||||
|
// Map roles
|
||||||
|
if user.Roles == nil {
|
||||||
|
model.Roles = types.SetNull(types.StringType)
|
||||||
|
} else {
|
||||||
|
var roles []attr.Value
|
||||||
|
for _, role := range *user.Roles {
|
||||||
|
roles = append(roles, types.StringValue(string(role)))
|
||||||
|
}
|
||||||
|
rolesSet, diags := types.SetValue(types.StringType, roles)
|
||||||
|
if diags.HasError() {
|
||||||
|
return fmt.Errorf("failed to map roles: %w", core.DiagsToError(diags))
|
||||||
|
}
|
||||||
|
model.Roles = rolesSet
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set remaining attributes
|
||||||
|
model.Host = types.StringPointerValue(user.Host)
|
||||||
|
model.Port = types.Int64PointerValue(user.Port)
|
||||||
|
model.Region = types.StringValue(region)
|
||||||
|
model.Status = types.StringPointerValue(user.Status)
|
||||||
|
model.DefaultDatabase = types.StringPointerValue(user.DefaultDatabase)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// mapFields maps the API response to a resourceModel.
|
||||||
|
func mapFields(userResp *sqlserverflexalpha.GetUserResponse, model *resourceModel, region string) error {
|
||||||
|
if userResp == nil {
|
||||||
|
return fmt.Errorf("response is nil")
|
||||||
|
}
|
||||||
|
if model == nil {
|
||||||
|
return fmt.Errorf("model input is nil")
|
||||||
|
}
|
||||||
|
user := userResp
|
||||||
|
|
||||||
|
// Handle user ID
|
||||||
|
var userId int64
|
||||||
|
if model.UserId.ValueInt64() != 0 {
|
||||||
|
userId = model.UserId.ValueInt64()
|
||||||
|
} else if user.Id != nil {
|
||||||
|
userId = *user.Id
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("user id not present")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set main attributes
|
||||||
|
model.Id = types.Int64Value(userId)
|
||||||
|
model.UserId = types.Int64Value(userId)
|
||||||
|
model.Username = types.StringPointerValue(user.Username)
|
||||||
|
|
||||||
|
// Map roles
|
||||||
|
if user.Roles != nil {
|
||||||
|
var roles []attr.Value
|
||||||
|
for _, role := range *user.Roles {
|
||||||
|
roles = append(roles, types.StringValue(string(role)))
|
||||||
|
}
|
||||||
|
rolesSet, diags := types.SetValue(types.StringType, roles)
|
||||||
|
if diags.HasError() {
|
||||||
|
return fmt.Errorf("failed to map roles: %w", core.DiagsToError(diags))
|
||||||
|
}
|
||||||
|
model.Roles = types.List(rolesSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure roles is not null
|
||||||
|
if model.Roles.IsNull() || model.Roles.IsUnknown() {
|
||||||
|
model.Roles = types.List(types.SetNull(types.StringType))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set connection details
|
||||||
|
model.Host = types.StringPointerValue(user.Host)
|
||||||
|
model.Port = types.Int64PointerValue(user.Port)
|
||||||
|
model.Region = types.StringValue(region)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// mapFieldsCreate maps the API response from creating a user to a resourceModel.
|
||||||
|
func mapFieldsCreate(userResp *sqlserverflexalpha.CreateUserResponse, model *resourceModel, region string) error {
|
||||||
|
if userResp == nil {
|
||||||
|
return fmt.Errorf("response is nil")
|
||||||
|
}
|
||||||
|
if model == nil {
|
||||||
|
return fmt.Errorf("model input is nil")
|
||||||
|
}
|
||||||
|
user := userResp
|
||||||
|
|
||||||
|
if user.Id == nil {
|
||||||
|
return fmt.Errorf("user id not present")
|
||||||
|
}
|
||||||
|
userId := *user.Id
|
||||||
|
model.Id = types.Int64Value(userId)
|
||||||
|
model.UserId = types.Int64Value(userId)
|
||||||
|
model.Username = types.StringPointerValue(user.Username)
|
||||||
|
|
||||||
|
if user.Password == nil {
|
||||||
|
return fmt.Errorf("user password not present")
|
||||||
|
}
|
||||||
|
model.Password = types.StringValue(*user.Password)
|
||||||
|
|
||||||
|
if user.Roles != nil {
|
||||||
|
var roles []attr.Value
|
||||||
|
for _, role := range *user.Roles {
|
||||||
|
roles = append(roles, types.StringValue(string(role)))
|
||||||
|
}
|
||||||
|
rolesSet, diags := types.SetValue(types.StringType, roles)
|
||||||
|
if diags.HasError() {
|
||||||
|
return fmt.Errorf("failed to map roles: %w", core.DiagsToError(diags))
|
||||||
|
}
|
||||||
|
model.Roles = types.List(rolesSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
if model.Roles.IsNull() || model.Roles.IsUnknown() {
|
||||||
|
model.Roles = types.List(types.SetNull(types.StringType))
|
||||||
|
}
|
||||||
|
|
||||||
|
model.Host = types.StringPointerValue(user.Host)
|
||||||
|
model.Port = types.Int64PointerValue(user.Port)
|
||||||
|
model.Region = types.StringValue(region)
|
||||||
|
model.Status = types.StringPointerValue(user.Status)
|
||||||
|
model.DefaultDatabase = types.StringPointerValue(user.DefaultDatabase)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// toCreatePayload converts a resourceModel to an API CreateUserRequestPayload.
|
||||||
|
func toCreatePayload(
|
||||||
|
model *resourceModel,
|
||||||
|
roles []sqlserverflexalpha.UserRole,
|
||||||
|
) (*sqlserverflexalpha.CreateUserRequestPayload, error) {
|
||||||
|
if model == nil {
|
||||||
|
return nil, fmt.Errorf("nil model")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &sqlserverflexalpha.CreateUserRequestPayload{
|
||||||
|
Username: conversion.StringValueToPointer(model.Username),
|
||||||
|
DefaultDatabase: conversion.StringValueToPointer(model.DefaultDatabase),
|
||||||
|
Roles: &roles,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
525
stackit/internal/services/sqlserverflexalpha/user/mapper_test.go
Normal file
525
stackit/internal/services/sqlserverflexalpha/user/mapper_test.go
Normal file
|
|
@ -0,0 +1,525 @@
|
||||||
|
package sqlserverflexalpha
|
||||||
|
|
||||||
|
import (
|
||||||
|
"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"
|
||||||
|
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexalpha"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMapDataSourceFields(t *testing.T) {
|
||||||
|
const testRegion = "region"
|
||||||
|
tests := []struct {
|
||||||
|
description string
|
||||||
|
input *sqlserverflexalpha.GetUserResponse
|
||||||
|
region string
|
||||||
|
expected dataSourceModel
|
||||||
|
isValid bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"default_values",
|
||||||
|
&sqlserverflexalpha.GetUserResponse{},
|
||||||
|
testRegion,
|
||||||
|
dataSourceModel{
|
||||||
|
Id: types.StringValue("pid,region,iid,1"),
|
||||||
|
UserId: types.Int64Value(1),
|
||||||
|
InstanceId: types.StringValue("iid"),
|
||||||
|
ProjectId: types.StringValue("pid"),
|
||||||
|
Username: types.StringNull(),
|
||||||
|
Roles: types.SetNull(types.StringType),
|
||||||
|
Host: types.StringNull(),
|
||||||
|
Port: types.Int64Null(),
|
||||||
|
Region: types.StringValue(testRegion),
|
||||||
|
Status: types.StringNull(),
|
||||||
|
DefaultDatabase: types.StringNull(),
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"simple_values",
|
||||||
|
&sqlserverflexalpha.GetUserResponse{
|
||||||
|
|
||||||
|
Roles: &[]sqlserverflexalpha.UserRole{
|
||||||
|
"role_1",
|
||||||
|
"role_2",
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
Username: utils.Ptr("username"),
|
||||||
|
Host: utils.Ptr("host"),
|
||||||
|
Port: utils.Ptr(int64(1234)),
|
||||||
|
Status: utils.Ptr("active"),
|
||||||
|
DefaultDatabase: utils.Ptr("default_db"),
|
||||||
|
},
|
||||||
|
testRegion,
|
||||||
|
dataSourceModel{
|
||||||
|
Id: types.StringValue("pid,region,iid,1"),
|
||||||
|
UserId: types.Int64Value(1),
|
||||||
|
InstanceId: types.StringValue("iid"),
|
||||||
|
ProjectId: types.StringValue("pid"),
|
||||||
|
Username: types.StringValue("username"),
|
||||||
|
Roles: types.SetValueMust(
|
||||||
|
types.StringType, []attr.Value{
|
||||||
|
types.StringValue("role_1"),
|
||||||
|
types.StringValue("role_2"),
|
||||||
|
types.StringValue(""),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Host: types.StringValue("host"),
|
||||||
|
Port: types.Int64Value(1234),
|
||||||
|
Region: types.StringValue(testRegion),
|
||||||
|
Status: types.StringValue("active"),
|
||||||
|
DefaultDatabase: types.StringValue("default_db"),
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"null_fields_and_int_conversions",
|
||||||
|
&sqlserverflexalpha.GetUserResponse{
|
||||||
|
Id: utils.Ptr(int64(1)),
|
||||||
|
Roles: &[]sqlserverflexalpha.UserRole{},
|
||||||
|
Username: nil,
|
||||||
|
Host: nil,
|
||||||
|
Port: utils.Ptr(int64(2123456789)),
|
||||||
|
},
|
||||||
|
testRegion,
|
||||||
|
dataSourceModel{
|
||||||
|
Id: types.StringValue("pid,region,iid,1"),
|
||||||
|
UserId: types.Int64Value(1),
|
||||||
|
InstanceId: types.StringValue("iid"),
|
||||||
|
ProjectId: types.StringValue("pid"),
|
||||||
|
Username: types.StringNull(),
|
||||||
|
Roles: types.SetValueMust(types.StringType, []attr.Value{}),
|
||||||
|
Host: types.StringNull(),
|
||||||
|
Port: types.Int64Value(2123456789),
|
||||||
|
Region: types.StringValue(testRegion),
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nil_response",
|
||||||
|
nil,
|
||||||
|
testRegion,
|
||||||
|
dataSourceModel{},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nil_response_2",
|
||||||
|
&sqlserverflexalpha.GetUserResponse{},
|
||||||
|
testRegion,
|
||||||
|
dataSourceModel{},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"no_resource_id",
|
||||||
|
&sqlserverflexalpha.GetUserResponse{},
|
||||||
|
testRegion,
|
||||||
|
dataSourceModel{},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(
|
||||||
|
tt.description, func(t *testing.T) {
|
||||||
|
state := &dataSourceModel{
|
||||||
|
ProjectId: tt.expected.ProjectId,
|
||||||
|
InstanceId: tt.expected.InstanceId,
|
||||||
|
UserId: tt.expected.UserId,
|
||||||
|
}
|
||||||
|
err := mapDataSourceFields(tt.input, state, tt.region)
|
||||||
|
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(state, &tt.expected)
|
||||||
|
if diff != "" {
|
||||||
|
t.Fatalf("Data does not match: %s", diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMapFieldsCreate(t *testing.T) {
|
||||||
|
const testRegion = "region"
|
||||||
|
tests := []struct {
|
||||||
|
description string
|
||||||
|
input *sqlserverflexalpha.CreateUserResponse
|
||||||
|
region string
|
||||||
|
expected resourceModel
|
||||||
|
isValid bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"default_values",
|
||||||
|
&sqlserverflexalpha.CreateUserResponse{
|
||||||
|
Id: utils.Ptr(int64(1)),
|
||||||
|
Password: utils.Ptr(""),
|
||||||
|
},
|
||||||
|
testRegion,
|
||||||
|
resourceModel{
|
||||||
|
Id: types.Int64Value(1),
|
||||||
|
UserId: types.Int64Value(1),
|
||||||
|
InstanceId: types.StringValue("iid"),
|
||||||
|
ProjectId: types.StringValue("pid"),
|
||||||
|
Username: types.StringNull(),
|
||||||
|
Roles: types.List(types.SetNull(types.StringType)),
|
||||||
|
Password: types.StringValue(""),
|
||||||
|
Host: types.StringNull(),
|
||||||
|
Port: types.Int64Null(),
|
||||||
|
Region: types.StringValue(testRegion),
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"simple_values",
|
||||||
|
&sqlserverflexalpha.CreateUserResponse{
|
||||||
|
Id: utils.Ptr(int64(2)),
|
||||||
|
Roles: &[]sqlserverflexalpha.UserRole{
|
||||||
|
"role_1",
|
||||||
|
"role_2",
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
Username: utils.Ptr("username"),
|
||||||
|
Password: utils.Ptr("password"),
|
||||||
|
Host: utils.Ptr("host"),
|
||||||
|
Port: utils.Ptr(int64(1234)),
|
||||||
|
Status: utils.Ptr("status"),
|
||||||
|
DefaultDatabase: utils.Ptr("default_db"),
|
||||||
|
},
|
||||||
|
testRegion,
|
||||||
|
resourceModel{
|
||||||
|
Id: types.Int64Value(2),
|
||||||
|
UserId: types.Int64Value(2),
|
||||||
|
InstanceId: types.StringValue("iid"),
|
||||||
|
ProjectId: types.StringValue("pid"),
|
||||||
|
Username: types.StringValue("username"),
|
||||||
|
Roles: types.List(
|
||||||
|
types.SetValueMust(
|
||||||
|
types.StringType, []attr.Value{
|
||||||
|
types.StringValue("role_1"),
|
||||||
|
types.StringValue("role_2"),
|
||||||
|
types.StringValue(""),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Password: types.StringValue("password"),
|
||||||
|
Host: types.StringValue("host"),
|
||||||
|
Port: types.Int64Value(1234),
|
||||||
|
Region: types.StringValue(testRegion),
|
||||||
|
Status: types.StringValue("status"),
|
||||||
|
DefaultDatabase: types.StringValue("default_db"),
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"null_fields_and_int_conversions",
|
||||||
|
&sqlserverflexalpha.CreateUserResponse{
|
||||||
|
Id: utils.Ptr(int64(3)),
|
||||||
|
Roles: &[]sqlserverflexalpha.UserRole{},
|
||||||
|
Username: nil,
|
||||||
|
Password: utils.Ptr(""),
|
||||||
|
Host: nil,
|
||||||
|
Port: utils.Ptr(int64(2123456789)),
|
||||||
|
},
|
||||||
|
testRegion,
|
||||||
|
resourceModel{
|
||||||
|
Id: types.Int64Value(3),
|
||||||
|
UserId: types.Int64Value(3),
|
||||||
|
InstanceId: types.StringValue("iid"),
|
||||||
|
ProjectId: types.StringValue("pid"),
|
||||||
|
Username: types.StringNull(),
|
||||||
|
Roles: types.List(types.SetValueMust(types.StringType, []attr.Value{})),
|
||||||
|
Password: types.StringValue(""),
|
||||||
|
Host: types.StringNull(),
|
||||||
|
Port: types.Int64Value(2123456789),
|
||||||
|
Region: types.StringValue(testRegion),
|
||||||
|
DefaultDatabase: types.StringNull(),
|
||||||
|
Status: types.StringNull(),
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nil_response",
|
||||||
|
nil,
|
||||||
|
testRegion,
|
||||||
|
resourceModel{},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nil_response_2",
|
||||||
|
&sqlserverflexalpha.CreateUserResponse{},
|
||||||
|
testRegion,
|
||||||
|
resourceModel{},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"no_resource_id",
|
||||||
|
&sqlserverflexalpha.CreateUserResponse{},
|
||||||
|
testRegion,
|
||||||
|
resourceModel{},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"no_password",
|
||||||
|
&sqlserverflexalpha.CreateUserResponse{
|
||||||
|
Id: utils.Ptr(int64(1)),
|
||||||
|
},
|
||||||
|
testRegion,
|
||||||
|
resourceModel{},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(
|
||||||
|
tt.description, func(t *testing.T) {
|
||||||
|
state := &resourceModel{
|
||||||
|
ProjectId: tt.expected.ProjectId,
|
||||||
|
InstanceId: tt.expected.InstanceId,
|
||||||
|
}
|
||||||
|
err := mapFieldsCreate(tt.input, state, tt.region)
|
||||||
|
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(state, &tt.expected)
|
||||||
|
if diff != "" {
|
||||||
|
t.Fatalf("Data does not match: %s", diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMapFields(t *testing.T) {
|
||||||
|
const testRegion = "region"
|
||||||
|
tests := []struct {
|
||||||
|
description string
|
||||||
|
input *sqlserverflexalpha.GetUserResponse
|
||||||
|
region string
|
||||||
|
expected resourceModel
|
||||||
|
isValid bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"default_values",
|
||||||
|
&sqlserverflexalpha.GetUserResponse{},
|
||||||
|
testRegion,
|
||||||
|
resourceModel{
|
||||||
|
Id: types.Int64Value(1),
|
||||||
|
UserId: types.Int64Value(1),
|
||||||
|
InstanceId: types.StringValue("iid"),
|
||||||
|
ProjectId: types.StringValue("pid"),
|
||||||
|
Username: types.StringNull(),
|
||||||
|
Roles: types.List(types.SetNull(types.StringType)),
|
||||||
|
Host: types.StringNull(),
|
||||||
|
Port: types.Int64Null(),
|
||||||
|
Region: types.StringValue(testRegion),
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"simple_values",
|
||||||
|
&sqlserverflexalpha.GetUserResponse{
|
||||||
|
Roles: &[]sqlserverflexalpha.UserRole{
|
||||||
|
"role_1",
|
||||||
|
"role_2",
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
Username: utils.Ptr("username"),
|
||||||
|
Host: utils.Ptr("host"),
|
||||||
|
Port: utils.Ptr(int64(1234)),
|
||||||
|
},
|
||||||
|
testRegion,
|
||||||
|
resourceModel{
|
||||||
|
Id: types.Int64Value(2),
|
||||||
|
UserId: types.Int64Value(2),
|
||||||
|
InstanceId: types.StringValue("iid"),
|
||||||
|
ProjectId: types.StringValue("pid"),
|
||||||
|
Username: types.StringValue("username"),
|
||||||
|
Roles: types.List(
|
||||||
|
types.SetValueMust(
|
||||||
|
types.StringType, []attr.Value{
|
||||||
|
types.StringValue("role_1"),
|
||||||
|
types.StringValue("role_2"),
|
||||||
|
types.StringValue(""),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Host: types.StringValue("host"),
|
||||||
|
Port: types.Int64Value(1234),
|
||||||
|
Region: types.StringValue(testRegion),
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"null_fields_and_int_conversions",
|
||||||
|
&sqlserverflexalpha.GetUserResponse{
|
||||||
|
Id: utils.Ptr(int64(1)),
|
||||||
|
Roles: &[]sqlserverflexalpha.UserRole{},
|
||||||
|
Username: nil,
|
||||||
|
Host: nil,
|
||||||
|
Port: utils.Ptr(int64(2123456789)),
|
||||||
|
},
|
||||||
|
testRegion,
|
||||||
|
resourceModel{
|
||||||
|
Id: types.Int64Value(1),
|
||||||
|
UserId: types.Int64Value(1),
|
||||||
|
InstanceId: types.StringValue("iid"),
|
||||||
|
ProjectId: types.StringValue("pid"),
|
||||||
|
Username: types.StringNull(),
|
||||||
|
Roles: types.List(types.SetValueMust(types.StringType, []attr.Value{})),
|
||||||
|
Host: types.StringNull(),
|
||||||
|
Port: types.Int64Value(2123456789),
|
||||||
|
Region: types.StringValue(testRegion),
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nil_response",
|
||||||
|
nil,
|
||||||
|
testRegion,
|
||||||
|
resourceModel{},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nil_response_2",
|
||||||
|
&sqlserverflexalpha.GetUserResponse{},
|
||||||
|
testRegion,
|
||||||
|
resourceModel{},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"no_resource_id",
|
||||||
|
&sqlserverflexalpha.GetUserResponse{},
|
||||||
|
testRegion,
|
||||||
|
resourceModel{},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(
|
||||||
|
tt.description, func(t *testing.T) {
|
||||||
|
state := &resourceModel{
|
||||||
|
ProjectId: tt.expected.ProjectId,
|
||||||
|
InstanceId: tt.expected.InstanceId,
|
||||||
|
UserId: tt.expected.UserId,
|
||||||
|
}
|
||||||
|
err := mapFields(tt.input, state, tt.region)
|
||||||
|
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(state, &tt.expected)
|
||||||
|
if diff != "" {
|
||||||
|
t.Fatalf("Data does not match: %s", diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestToCreatePayload(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
description string
|
||||||
|
input *resourceModel
|
||||||
|
inputRoles []sqlserverflexalpha.UserRole
|
||||||
|
expected *sqlserverflexalpha.CreateUserRequestPayload
|
||||||
|
isValid bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"default_values",
|
||||||
|
&resourceModel{},
|
||||||
|
[]sqlserverflexalpha.UserRole{},
|
||||||
|
&sqlserverflexalpha.CreateUserRequestPayload{
|
||||||
|
Roles: &[]sqlserverflexalpha.UserRole{},
|
||||||
|
Username: nil,
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default_values",
|
||||||
|
&resourceModel{
|
||||||
|
Username: types.StringValue("username"),
|
||||||
|
},
|
||||||
|
[]sqlserverflexalpha.UserRole{
|
||||||
|
"role_1",
|
||||||
|
"role_2",
|
||||||
|
},
|
||||||
|
&sqlserverflexalpha.CreateUserRequestPayload{
|
||||||
|
Roles: &[]sqlserverflexalpha.UserRole{
|
||||||
|
"role_1",
|
||||||
|
"role_2",
|
||||||
|
},
|
||||||
|
Username: utils.Ptr("username"),
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"null_fields_and_int_conversions",
|
||||||
|
&resourceModel{
|
||||||
|
Username: types.StringNull(),
|
||||||
|
},
|
||||||
|
[]sqlserverflexalpha.UserRole{
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
&sqlserverflexalpha.CreateUserRequestPayload{
|
||||||
|
Roles: &[]sqlserverflexalpha.UserRole{
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
Username: nil,
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nil_model",
|
||||||
|
nil,
|
||||||
|
[]sqlserverflexalpha.UserRole{},
|
||||||
|
nil,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nil_roles",
|
||||||
|
&resourceModel{
|
||||||
|
Username: types.StringValue("username"),
|
||||||
|
},
|
||||||
|
[]sqlserverflexalpha.UserRole{},
|
||||||
|
&sqlserverflexalpha.CreateUserRequestPayload{
|
||||||
|
Roles: &[]sqlserverflexalpha.UserRole{},
|
||||||
|
Username: utils.Ptr("username"),
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(
|
||||||
|
tt.description, func(t *testing.T) {
|
||||||
|
output, err := toCreatePayload(tt.input, tt.inputRoles)
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -9,12 +9,12 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework/resource/identityschema"
|
||||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexalpha"
|
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexalpha"
|
||||||
sqlserverflexalphagen "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/user/resources_gen"
|
sqlserverflexalphagen "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/user/resources_gen"
|
||||||
sqlserverflexalphaUtils "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/utils"
|
sqlserverflexalphaUtils "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexalpha/utils"
|
||||||
sqlserverflexalphaWait "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/wait/sqlserverflexalpha"
|
sqlserverflexalphaWait "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/wait/sqlserverflexalpha"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform-plugin-framework/attr"
|
|
||||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||||
|
|
@ -31,6 +31,7 @@ var (
|
||||||
_ resource.ResourceWithConfigure = &userResource{}
|
_ resource.ResourceWithConfigure = &userResource{}
|
||||||
_ resource.ResourceWithImportState = &userResource{}
|
_ resource.ResourceWithImportState = &userResource{}
|
||||||
_ resource.ResourceWithModifyPlan = &userResource{}
|
_ resource.ResourceWithModifyPlan = &userResource{}
|
||||||
|
_ resource.ResourceWithIdentity = &userResource{}
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewUserResource is a helper function to simplify the provider implementation.
|
// NewUserResource is a helper function to simplify the provider implementation.
|
||||||
|
|
@ -131,6 +132,30 @@ func (r *userResource) Schema(ctx context.Context, _ resource.SchemaRequest, res
|
||||||
resp.Schema = s
|
resp.Schema = s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IdentitySchema defines the schema for the resource's identity attributes.
|
||||||
|
func (r *userResource) IdentitySchema(
|
||||||
|
_ context.Context,
|
||||||
|
_ resource.IdentitySchemaRequest,
|
||||||
|
response *resource.IdentitySchemaResponse,
|
||||||
|
) {
|
||||||
|
response.IdentitySchema = identityschema.Schema{
|
||||||
|
Attributes: map[string]identityschema.Attribute{
|
||||||
|
"project_id": identityschema.StringAttribute{
|
||||||
|
RequiredForImport: true, // must be set during import by the practitioner
|
||||||
|
},
|
||||||
|
"region": identityschema.StringAttribute{
|
||||||
|
RequiredForImport: true, // can be defaulted by the provider configuration
|
||||||
|
},
|
||||||
|
"instance_id": identityschema.StringAttribute{
|
||||||
|
RequiredForImport: true, // can be defaulted by the provider configuration
|
||||||
|
},
|
||||||
|
"user_id": identityschema.Int64Attribute{
|
||||||
|
RequiredForImport: true, // can be defaulted by the provider configuration
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Create creates the resource and sets the initial Terraform state.
|
// Create creates the resource and sets the initial Terraform state.
|
||||||
func (r *userResource) Create(
|
func (r *userResource) Create(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
|
|
@ -402,109 +427,3 @@ func (r *userResource) ImportState(
|
||||||
)
|
)
|
||||||
tflog.Info(ctx, "SQLServer Flex user state imported")
|
tflog.Info(ctx, "SQLServer Flex user state imported")
|
||||||
}
|
}
|
||||||
|
|
||||||
func mapFieldsCreate(userResp *sqlserverflexalpha.CreateUserResponse, model *resourceModel, region string) error {
|
|
||||||
if userResp == nil {
|
|
||||||
return fmt.Errorf("response is nil")
|
|
||||||
}
|
|
||||||
if model == nil {
|
|
||||||
return fmt.Errorf("model input is nil")
|
|
||||||
}
|
|
||||||
user := userResp
|
|
||||||
|
|
||||||
if user.Id == nil {
|
|
||||||
return fmt.Errorf("user id not present")
|
|
||||||
}
|
|
||||||
userId := *user.Id
|
|
||||||
model.Id = types.Int64Value(userId)
|
|
||||||
model.UserId = types.Int64Value(userId)
|
|
||||||
model.Username = types.StringPointerValue(user.Username)
|
|
||||||
|
|
||||||
if user.Password == nil {
|
|
||||||
return fmt.Errorf("user password not present")
|
|
||||||
}
|
|
||||||
model.Password = types.StringValue(*user.Password)
|
|
||||||
|
|
||||||
if user.Roles != nil {
|
|
||||||
var roles []attr.Value
|
|
||||||
for _, role := range *user.Roles {
|
|
||||||
roles = append(roles, types.StringValue(string(role)))
|
|
||||||
}
|
|
||||||
rolesSet, diags := types.SetValue(types.StringType, roles)
|
|
||||||
if diags.HasError() {
|
|
||||||
return fmt.Errorf("failed to map roles: %w", core.DiagsToError(diags))
|
|
||||||
}
|
|
||||||
model.Roles = types.List(rolesSet)
|
|
||||||
}
|
|
||||||
|
|
||||||
if model.Roles.IsNull() || model.Roles.IsUnknown() {
|
|
||||||
model.Roles = types.List(types.SetNull(types.StringType))
|
|
||||||
}
|
|
||||||
|
|
||||||
model.Host = types.StringPointerValue(user.Host)
|
|
||||||
model.Port = types.Int64PointerValue(user.Port)
|
|
||||||
model.Region = types.StringValue(region)
|
|
||||||
model.Status = types.StringPointerValue(user.Status)
|
|
||||||
model.DefaultDatabase = types.StringPointerValue(user.DefaultDatabase)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func mapFields(userResp *sqlserverflexalpha.GetUserResponse, model *resourceModel, region string) error {
|
|
||||||
if userResp == nil {
|
|
||||||
return fmt.Errorf("response is nil")
|
|
||||||
}
|
|
||||||
if model == nil {
|
|
||||||
return fmt.Errorf("model input is nil")
|
|
||||||
}
|
|
||||||
user := userResp
|
|
||||||
|
|
||||||
var userId int64
|
|
||||||
if model.UserId.ValueInt64() != 0 {
|
|
||||||
userId = model.UserId.ValueInt64()
|
|
||||||
} else if user.Id != nil {
|
|
||||||
userId = *user.Id
|
|
||||||
} else {
|
|
||||||
return fmt.Errorf("user id not present")
|
|
||||||
}
|
|
||||||
|
|
||||||
model.Id = types.Int64Value(userId)
|
|
||||||
model.UserId = types.Int64Value(userId)
|
|
||||||
model.Username = types.StringPointerValue(user.Username)
|
|
||||||
|
|
||||||
if user.Roles != nil {
|
|
||||||
var roles []attr.Value
|
|
||||||
for _, role := range *user.Roles {
|
|
||||||
roles = append(roles, types.StringValue(string(role)))
|
|
||||||
}
|
|
||||||
rolesSet, diags := types.SetValue(types.StringType, roles)
|
|
||||||
if diags.HasError() {
|
|
||||||
return fmt.Errorf("failed to map roles: %w", core.DiagsToError(diags))
|
|
||||||
}
|
|
||||||
model.Roles = types.List(rolesSet)
|
|
||||||
}
|
|
||||||
|
|
||||||
if model.Roles.IsNull() || model.Roles.IsUnknown() {
|
|
||||||
model.Roles = types.List(types.SetNull(types.StringType))
|
|
||||||
}
|
|
||||||
|
|
||||||
model.Host = types.StringPointerValue(user.Host)
|
|
||||||
model.Port = types.Int64PointerValue(user.Port)
|
|
||||||
model.Region = types.StringValue(region)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func toCreatePayload(
|
|
||||||
model *resourceModel,
|
|
||||||
roles []sqlserverflexalpha.UserRole,
|
|
||||||
) (*sqlserverflexalpha.CreateUserRequestPayload, error) {
|
|
||||||
if model == nil {
|
|
||||||
return nil, fmt.Errorf("nil model")
|
|
||||||
}
|
|
||||||
|
|
||||||
return &sqlserverflexalpha.CreateUserRequestPayload{
|
|
||||||
Username: conversion.StringValueToPointer(model.Username),
|
|
||||||
DefaultDatabase: conversion.StringValueToPointer(model.DefaultDatabase),
|
|
||||||
Roles: &roles,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,388 +0,0 @@
|
||||||
package sqlserverflexalpha
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
|
||||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
|
||||||
"github.com/stackitcloud/stackit-sdk-go/core/utils"
|
|
||||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexalpha"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestMapFieldsCreate(t *testing.T) {
|
|
||||||
const testRegion = "region"
|
|
||||||
tests := []struct {
|
|
||||||
description string
|
|
||||||
input *sqlserverflexalpha.CreateUserResponse
|
|
||||||
region string
|
|
||||||
expected resourceModel
|
|
||||||
isValid bool
|
|
||||||
}{
|
|
||||||
//{
|
|
||||||
// "default_values",
|
|
||||||
// &sqlserverflexalpha.CreateUserResponse{
|
|
||||||
// Id: utils.Ptr(int64(1)),
|
|
||||||
// Password: utils.Ptr(""),
|
|
||||||
// },
|
|
||||||
// testRegion,
|
|
||||||
// resourceModel{
|
|
||||||
// Id: types.Int64Value(1),
|
|
||||||
// UserId: types.Int64Value(1),
|
|
||||||
// InstanceId: types.StringValue("iid"),
|
|
||||||
// ProjectId: types.StringValue("pid"),
|
|
||||||
// Username: types.StringNull(),
|
|
||||||
// Roles: types.List(types.SetNull(types.StringType)),
|
|
||||||
// Password: types.StringValue(""),
|
|
||||||
// Host: types.StringNull(),
|
|
||||||
// Port: types.Int64Null(),
|
|
||||||
// Region: types.StringValue(testRegion),
|
|
||||||
// },
|
|
||||||
// true,
|
|
||||||
//},
|
|
||||||
//{
|
|
||||||
// "simple_values",
|
|
||||||
// &sqlserverflexalpha.CreateUserResponse{
|
|
||||||
// Id: utils.Ptr(int64(2)),
|
|
||||||
// Roles: &[]sqlserverflexalpha.UserRole{
|
|
||||||
// "role_1",
|
|
||||||
// "role_2",
|
|
||||||
// "",
|
|
||||||
// },
|
|
||||||
// Username: utils.Ptr("username"),
|
|
||||||
// Password: utils.Ptr("password"),
|
|
||||||
// Host: utils.Ptr("host"),
|
|
||||||
// Port: utils.Ptr(int64(1234)),
|
|
||||||
// Status: utils.Ptr("status"),
|
|
||||||
// DefaultDatabase: utils.Ptr("default_db"),
|
|
||||||
// },
|
|
||||||
// testRegion,
|
|
||||||
// resourceModel{
|
|
||||||
// Id: types.Int64Value(2),
|
|
||||||
// UserId: types.Int64Value(2),
|
|
||||||
// InstanceId: types.StringValue("iid"),
|
|
||||||
// ProjectId: types.StringValue("pid"),
|
|
||||||
// Username: types.StringValue("username"),
|
|
||||||
// Roles: types.List(
|
|
||||||
// types.SetValueMust(
|
|
||||||
// types.StringType, []attr.Value{
|
|
||||||
// types.StringValue("role_1"),
|
|
||||||
// types.StringValue("role_2"),
|
|
||||||
// types.StringValue(""),
|
|
||||||
// },
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// Password: types.StringValue("password"),
|
|
||||||
// Host: types.StringValue("host"),
|
|
||||||
// Port: types.Int64Value(1234),
|
|
||||||
// Region: types.StringValue(testRegion),
|
|
||||||
// Status: types.StringValue("status"),
|
|
||||||
// DefaultDatabase: types.StringValue("default_db"),
|
|
||||||
// },
|
|
||||||
// true,
|
|
||||||
//},
|
|
||||||
//{
|
|
||||||
// "null_fields_and_int_conversions",
|
|
||||||
// &sqlserverflexalpha.CreateUserResponse{
|
|
||||||
// Id: utils.Ptr(int64(3)),
|
|
||||||
// Roles: &[]sqlserverflexalpha.UserRole{},
|
|
||||||
// Username: nil,
|
|
||||||
// Password: utils.Ptr(""),
|
|
||||||
// Host: nil,
|
|
||||||
// Port: utils.Ptr(int64(2123456789)),
|
|
||||||
// },
|
|
||||||
// testRegion,
|
|
||||||
// resourceModel{
|
|
||||||
// Id: types.Int64Value(3),
|
|
||||||
// UserId: types.Int64Value(3),
|
|
||||||
// InstanceId: types.StringValue("iid"),
|
|
||||||
// ProjectId: types.StringValue("pid"),
|
|
||||||
// Username: types.StringNull(),
|
|
||||||
// Roles: types.List(types.SetValueMust(types.StringType, []attr.Value{})),
|
|
||||||
// Password: types.StringValue(""),
|
|
||||||
// Host: types.StringNull(),
|
|
||||||
// Port: types.Int64Value(2123456789),
|
|
||||||
// Region: types.StringValue(testRegion),
|
|
||||||
// DefaultDatabase: types.StringNull(),
|
|
||||||
// Status: types.StringNull(),
|
|
||||||
// },
|
|
||||||
// true,
|
|
||||||
//},
|
|
||||||
{
|
|
||||||
"nil_response",
|
|
||||||
nil,
|
|
||||||
testRegion,
|
|
||||||
resourceModel{},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"nil_response_2",
|
|
||||||
&sqlserverflexalpha.CreateUserResponse{},
|
|
||||||
testRegion,
|
|
||||||
resourceModel{},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"no_resource_id",
|
|
||||||
&sqlserverflexalpha.CreateUserResponse{},
|
|
||||||
testRegion,
|
|
||||||
resourceModel{},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"no_password",
|
|
||||||
&sqlserverflexalpha.CreateUserResponse{
|
|
||||||
Id: utils.Ptr(int64(1)),
|
|
||||||
},
|
|
||||||
testRegion,
|
|
||||||
resourceModel{},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(
|
|
||||||
tt.description, func(t *testing.T) {
|
|
||||||
state := &resourceModel{
|
|
||||||
ProjectId: tt.expected.ProjectId,
|
|
||||||
InstanceId: tt.expected.InstanceId,
|
|
||||||
}
|
|
||||||
err := mapFieldsCreate(tt.input, state, tt.region)
|
|
||||||
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(state, &tt.expected)
|
|
||||||
if diff != "" {
|
|
||||||
t.Fatalf("Data does not match: %s", diff)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMapFields(t *testing.T) {
|
|
||||||
const testRegion = "region"
|
|
||||||
tests := []struct {
|
|
||||||
description string
|
|
||||||
input *sqlserverflexalpha.GetUserResponse
|
|
||||||
region string
|
|
||||||
expected resourceModel
|
|
||||||
isValid bool
|
|
||||||
}{
|
|
||||||
//{
|
|
||||||
// "default_values",
|
|
||||||
// &sqlserverflexalpha.GetUserResponse{},
|
|
||||||
// testRegion,
|
|
||||||
// resourceModel{
|
|
||||||
// Id: types.Int64Value(1),
|
|
||||||
// UserId: types.Int64Value(1),
|
|
||||||
// InstanceId: types.StringValue("iid"),
|
|
||||||
// ProjectId: types.StringValue("pid"),
|
|
||||||
// Username: types.StringNull(),
|
|
||||||
// Roles: types.List(types.SetNull(types.StringType)),
|
|
||||||
// Host: types.StringNull(),
|
|
||||||
// Port: types.Int64Null(),
|
|
||||||
// Region: types.StringValue(testRegion),
|
|
||||||
// },
|
|
||||||
// true,
|
|
||||||
//},
|
|
||||||
//{
|
|
||||||
// "simple_values",
|
|
||||||
// &sqlserverflexalpha.GetUserResponse{
|
|
||||||
// Roles: &[]sqlserverflexalpha.UserRole{
|
|
||||||
// "role_1",
|
|
||||||
// "role_2",
|
|
||||||
// "",
|
|
||||||
// },
|
|
||||||
// Username: utils.Ptr("username"),
|
|
||||||
// Host: utils.Ptr("host"),
|
|
||||||
// Port: utils.Ptr(int64(1234)),
|
|
||||||
// },
|
|
||||||
// testRegion,
|
|
||||||
// resourceModel{
|
|
||||||
// Id: types.Int64Value(2),
|
|
||||||
// UserId: types.Int64Value(2),
|
|
||||||
// InstanceId: types.StringValue("iid"),
|
|
||||||
// ProjectId: types.StringValue("pid"),
|
|
||||||
// Username: types.StringValue("username"),
|
|
||||||
// Roles: types.List(
|
|
||||||
// types.SetValueMust(
|
|
||||||
// types.StringType, []attr.Value{
|
|
||||||
// types.StringValue("role_1"),
|
|
||||||
// types.StringValue("role_2"),
|
|
||||||
// types.StringValue(""),
|
|
||||||
// },
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// Host: types.StringValue("host"),
|
|
||||||
// Port: types.Int64Value(1234),
|
|
||||||
// Region: types.StringValue(testRegion),
|
|
||||||
// },
|
|
||||||
// true,
|
|
||||||
//},
|
|
||||||
//{
|
|
||||||
// "null_fields_and_int_conversions",
|
|
||||||
// &sqlserverflexalpha.GetUserResponse{
|
|
||||||
// Id: utils.Ptr(int64(1)),
|
|
||||||
// Roles: &[]sqlserverflexalpha.UserRole{},
|
|
||||||
// Username: nil,
|
|
||||||
// Host: nil,
|
|
||||||
// Port: utils.Ptr(int64(2123456789)),
|
|
||||||
// },
|
|
||||||
// testRegion,
|
|
||||||
// resourceModel{
|
|
||||||
// Id: types.Int64Value(1),
|
|
||||||
// UserId: types.Int64Value(1),
|
|
||||||
// InstanceId: types.StringValue("iid"),
|
|
||||||
// ProjectId: types.StringValue("pid"),
|
|
||||||
// Username: types.StringNull(),
|
|
||||||
// Roles: types.List(types.SetValueMust(types.StringType, []attr.Value{})),
|
|
||||||
// Host: types.StringNull(),
|
|
||||||
// Port: types.Int64Value(2123456789),
|
|
||||||
// Region: types.StringValue(testRegion),
|
|
||||||
// },
|
|
||||||
// true,
|
|
||||||
//},
|
|
||||||
{
|
|
||||||
"nil_response",
|
|
||||||
nil,
|
|
||||||
testRegion,
|
|
||||||
resourceModel{},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"nil_response_2",
|
|
||||||
&sqlserverflexalpha.GetUserResponse{},
|
|
||||||
testRegion,
|
|
||||||
resourceModel{},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"no_resource_id",
|
|
||||||
&sqlserverflexalpha.GetUserResponse{},
|
|
||||||
testRegion,
|
|
||||||
resourceModel{},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(
|
|
||||||
tt.description, func(t *testing.T) {
|
|
||||||
state := &resourceModel{
|
|
||||||
ProjectId: tt.expected.ProjectId,
|
|
||||||
InstanceId: tt.expected.InstanceId,
|
|
||||||
UserId: tt.expected.UserId,
|
|
||||||
}
|
|
||||||
err := mapFields(tt.input, state, tt.region)
|
|
||||||
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(state, &tt.expected)
|
|
||||||
if diff != "" {
|
|
||||||
t.Fatalf("Data does not match: %s", diff)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestToCreatePayload(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
description string
|
|
||||||
input *resourceModel
|
|
||||||
inputRoles []sqlserverflexalpha.UserRole
|
|
||||||
expected *sqlserverflexalpha.CreateUserRequestPayload
|
|
||||||
isValid bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
"default_values",
|
|
||||||
&resourceModel{},
|
|
||||||
[]sqlserverflexalpha.UserRole{},
|
|
||||||
&sqlserverflexalpha.CreateUserRequestPayload{
|
|
||||||
Roles: &[]sqlserverflexalpha.UserRole{},
|
|
||||||
Username: nil,
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"default_values",
|
|
||||||
&resourceModel{
|
|
||||||
Username: types.StringValue("username"),
|
|
||||||
},
|
|
||||||
[]sqlserverflexalpha.UserRole{
|
|
||||||
"role_1",
|
|
||||||
"role_2",
|
|
||||||
},
|
|
||||||
&sqlserverflexalpha.CreateUserRequestPayload{
|
|
||||||
Roles: &[]sqlserverflexalpha.UserRole{
|
|
||||||
"role_1",
|
|
||||||
"role_2",
|
|
||||||
},
|
|
||||||
Username: utils.Ptr("username"),
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"null_fields_and_int_conversions",
|
|
||||||
&resourceModel{
|
|
||||||
Username: types.StringNull(),
|
|
||||||
},
|
|
||||||
[]sqlserverflexalpha.UserRole{
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
&sqlserverflexalpha.CreateUserRequestPayload{
|
|
||||||
Roles: &[]sqlserverflexalpha.UserRole{
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
Username: nil,
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"nil_model",
|
|
||||||
nil,
|
|
||||||
[]sqlserverflexalpha.UserRole{},
|
|
||||||
nil,
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"nil_roles",
|
|
||||||
&resourceModel{
|
|
||||||
Username: types.StringValue("username"),
|
|
||||||
},
|
|
||||||
[]sqlserverflexalpha.UserRole{},
|
|
||||||
&sqlserverflexalpha.CreateUserRequestPayload{
|
|
||||||
Roles: &[]sqlserverflexalpha.UserRole{},
|
|
||||||
Username: utils.Ptr("username"),
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(
|
|
||||||
tt.description, func(t *testing.T) {
|
|
||||||
output, err := toCreatePayload(tt.input, tt.inputRoles)
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -7,6 +7,7 @@ import (
|
||||||
|
|
||||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||||
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
|
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||||
"github.com/stackitcloud/stackit-sdk-go/core/config"
|
"github.com/stackitcloud/stackit-sdk-go/core/config"
|
||||||
|
|
@ -28,7 +29,7 @@ func NewDatabaseDataSource() datasource.DataSource {
|
||||||
|
|
||||||
type dataSourceModel struct {
|
type dataSourceModel struct {
|
||||||
sqlserverflexbetaGen.DatabaseModel
|
sqlserverflexbetaGen.DatabaseModel
|
||||||
TfId types.String `tfsdk:"id"`
|
TerraformId types.String `tfsdk:"id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type databaseDataSource struct {
|
type databaseDataSource struct {
|
||||||
|
|
@ -97,8 +98,6 @@ func (d *databaseDataSource) Configure(
|
||||||
|
|
||||||
func (d *databaseDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
|
func (d *databaseDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
|
||||||
var data dataSourceModel
|
var data dataSourceModel
|
||||||
readErr := "Read DB error"
|
|
||||||
|
|
||||||
// Read Terraform configuration data into the model
|
// Read Terraform configuration data into the model
|
||||||
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
|
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
|
||||||
|
|
||||||
|
|
@ -108,6 +107,7 @@ func (d *databaseDataSource) Read(ctx context.Context, req datasource.ReadReques
|
||||||
|
|
||||||
ctx = core.InitProviderContext(ctx)
|
ctx = core.InitProviderContext(ctx)
|
||||||
|
|
||||||
|
// Extract identifiers from the plan
|
||||||
projectId := data.ProjectId.ValueString()
|
projectId := data.ProjectId.ValueString()
|
||||||
region := d.providerData.GetRegionWithOverride(data.Region)
|
region := d.providerData.GetRegionWithOverride(data.Region)
|
||||||
instanceId := data.InstanceId.ValueString()
|
instanceId := data.InstanceId.ValueString()
|
||||||
|
|
@ -120,44 +120,55 @@ func (d *databaseDataSource) Read(ctx context.Context, req datasource.ReadReques
|
||||||
|
|
||||||
databaseResp, err := d.client.GetDatabaseRequest(ctx, projectId, region, instanceId, databaseName).Execute()
|
databaseResp, err := d.client.GetDatabaseRequest(ctx, projectId, region, instanceId, databaseName).Execute()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.LogError(
|
handleReadError(ctx, &resp.Diagnostics, err, projectId, instanceId)
|
||||||
ctx,
|
|
||||||
&resp.Diagnostics,
|
|
||||||
err,
|
|
||||||
"Reading database",
|
|
||||||
fmt.Sprintf("database with %q does not exist in project %q.", databaseName, projectId),
|
|
||||||
map[int]string{
|
|
||||||
http.StatusForbidden: fmt.Sprintf("Project with %q not found or forbidden access", projectId),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
resp.State.RemoveResource(ctx)
|
resp.State.RemoveResource(ctx)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx = core.LogResponse(ctx)
|
ctx = core.LogResponse(ctx)
|
||||||
|
// Map response body to schema and populate Computed attribute values
|
||||||
dbId, ok := databaseResp.GetIdOk()
|
err = mapFields(databaseResp, &data, region)
|
||||||
if !ok {
|
if err != nil {
|
||||||
core.LogAndAddError(
|
core.LogAndAddError(
|
||||||
ctx,
|
ctx,
|
||||||
&resp.Diagnostics,
|
&resp.Diagnostics,
|
||||||
readErr,
|
"Error reading database",
|
||||||
"Database creation waiting: returned id is nil",
|
fmt.Sprintf("Processing API payload: %v", err),
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
data.Id = types.Int64Value(dbId)
|
|
||||||
data.TfId = utils.BuildInternalTerraformId(projectId, region, instanceId, databaseName)
|
|
||||||
data.Owner = types.StringValue(databaseResp.GetOwner())
|
|
||||||
data.CollationName = types.StringValue(databaseResp.GetCollationName())
|
|
||||||
data.CompatibilityLevel = types.Int64Value(databaseResp.GetCompatibilityLevel())
|
|
||||||
data.DatabaseName = types.StringValue(databaseResp.GetName())
|
|
||||||
// data.InstanceId = types.StringValue(databaseResp.GetInstanceId())
|
|
||||||
// data.Name = types.Sometype(apiResponse.GetName())
|
|
||||||
// data.ProjectId = types.Sometype(apiResponse.GetProjectId())
|
|
||||||
// data.Region = types.Sometype(apiResponse.GetRegion())
|
|
||||||
|
|
||||||
// Save data into Terraform state
|
// Save data into Terraform state
|
||||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
||||||
|
|
||||||
|
tflog.Info(ctx, "SQL Server Flex beta database read")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleReadError centralizes API error handling for the Read operation.
|
||||||
|
func handleReadError(ctx context.Context, diags *diag.Diagnostics, err error, projectId, instanceId string) {
|
||||||
|
utils.LogError(
|
||||||
|
ctx,
|
||||||
|
diags,
|
||||||
|
err,
|
||||||
|
"Reading database",
|
||||||
|
fmt.Sprintf(
|
||||||
|
"Could not retrieve database for instance %q in project %q.",
|
||||||
|
instanceId,
|
||||||
|
projectId,
|
||||||
|
),
|
||||||
|
map[int]string{
|
||||||
|
http.StatusBadRequest: fmt.Sprintf(
|
||||||
|
"Invalid request parameters for project %q and instance %q.",
|
||||||
|
projectId,
|
||||||
|
instanceId,
|
||||||
|
),
|
||||||
|
http.StatusNotFound: fmt.Sprintf(
|
||||||
|
"Database, instance %q, or project %q not found.",
|
||||||
|
instanceId,
|
||||||
|
projectId,
|
||||||
|
),
|
||||||
|
http.StatusForbidden: fmt.Sprintf("Forbidden access to project %q.", projectId),
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,97 @@
|
||||||
|
package sqlserverflexbeta
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||||
|
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexbeta"
|
||||||
|
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// mapFields maps fields from a ListDatabase API response to a resourceModel for the data source.
|
||||||
|
func mapFields(source *sqlserverflexbeta.GetDatabaseResponse, model *dataSourceModel, region string) error {
|
||||||
|
if source == nil {
|
||||||
|
return fmt.Errorf("response is nil")
|
||||||
|
}
|
||||||
|
if source.Id == nil || *source.Id == 0 {
|
||||||
|
return fmt.Errorf("id not present")
|
||||||
|
}
|
||||||
|
if model == nil {
|
||||||
|
return fmt.Errorf("model given is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
var databaseId int64
|
||||||
|
if model.Id.ValueInt64() != 0 {
|
||||||
|
databaseId = model.Id.ValueInt64()
|
||||||
|
} else if source.Id != nil {
|
||||||
|
databaseId = *source.Id
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("database id not present")
|
||||||
|
}
|
||||||
|
|
||||||
|
model.Id = types.Int64Value(databaseId)
|
||||||
|
model.DatabaseName = types.StringValue(source.GetName())
|
||||||
|
model.Name = types.StringValue(source.GetName())
|
||||||
|
model.Owner = types.StringValue(strings.Trim(source.GetOwner(), "\""))
|
||||||
|
model.Region = types.StringValue(region)
|
||||||
|
model.ProjectId = types.StringValue(model.ProjectId.ValueString())
|
||||||
|
model.InstanceId = types.StringValue(model.InstanceId.ValueString())
|
||||||
|
model.CompatibilityLevel = types.Int64Value(source.GetCompatibilityLevel())
|
||||||
|
model.CollationName = types.StringValue(source.GetCollationName())
|
||||||
|
|
||||||
|
model.TerraformId = utils.BuildInternalTerraformId(
|
||||||
|
model.ProjectId.ValueString(),
|
||||||
|
region,
|
||||||
|
model.InstanceId.ValueString(),
|
||||||
|
model.DatabaseName.ValueString(),
|
||||||
|
)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// mapResourceFields maps fields from a ListDatabase API response to a resourceModel for the resource.
|
||||||
|
func mapResourceFields(source *sqlserverflexbeta.GetDatabaseResponse, model *resourceModel, region string) error {
|
||||||
|
if source == nil {
|
||||||
|
return fmt.Errorf("response is nil")
|
||||||
|
}
|
||||||
|
if source.Id == nil || *source.Id == 0 {
|
||||||
|
return fmt.Errorf("id not present")
|
||||||
|
}
|
||||||
|
if model == nil {
|
||||||
|
return fmt.Errorf("model input is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
var databaseId int64
|
||||||
|
if model.Id.ValueInt64() != 0 {
|
||||||
|
databaseId = model.Id.ValueInt64()
|
||||||
|
} else if source.Id != nil {
|
||||||
|
databaseId = *source.Id
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("database id not present")
|
||||||
|
}
|
||||||
|
|
||||||
|
model.Id = types.Int64Value(databaseId)
|
||||||
|
model.DatabaseName = types.StringValue(source.GetName())
|
||||||
|
model.Name = types.StringValue(source.GetName())
|
||||||
|
model.Owner = types.StringValue(strings.Trim(source.GetOwner(), "\""))
|
||||||
|
model.Region = types.StringValue(region)
|
||||||
|
model.ProjectId = types.StringValue(model.ProjectId.ValueString())
|
||||||
|
model.InstanceId = types.StringValue(model.InstanceId.ValueString())
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// toCreatePayload converts the resource model to an API create payload.
|
||||||
|
func toCreatePayload(model *resourceModel) (*sqlserverflexbeta.CreateDatabaseRequestPayload, error) {
|
||||||
|
if model == nil {
|
||||||
|
return nil, fmt.Errorf("nil model")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &sqlserverflexbeta.CreateDatabaseRequestPayload{
|
||||||
|
Name: model.Name.ValueStringPointer(),
|
||||||
|
Owner: model.Owner.ValueStringPointer(),
|
||||||
|
Collation: model.Collation.ValueStringPointer(),
|
||||||
|
Compatibility: model.Compatibility.ValueInt64Pointer(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,227 @@
|
||||||
|
package sqlserverflexbeta
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||||
|
"github.com/stackitcloud/stackit-sdk-go/core/utils"
|
||||||
|
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexbeta"
|
||||||
|
datasource "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexbeta/database/datasources_gen"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMapFields(t *testing.T) {
|
||||||
|
type given struct {
|
||||||
|
source *sqlserverflexbeta.GetDatabaseResponse
|
||||||
|
model *dataSourceModel
|
||||||
|
region string
|
||||||
|
}
|
||||||
|
type expected struct {
|
||||||
|
model *dataSourceModel
|
||||||
|
err bool
|
||||||
|
}
|
||||||
|
|
||||||
|
testcases := []struct {
|
||||||
|
name string
|
||||||
|
given given
|
||||||
|
expected expected
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "should map fields correctly",
|
||||||
|
given: given{
|
||||||
|
source: &sqlserverflexbeta.GetDatabaseResponse{
|
||||||
|
Id: utils.Ptr(int64(1)),
|
||||||
|
Name: utils.Ptr("my-db"),
|
||||||
|
CollationName: utils.Ptr("collation"),
|
||||||
|
CompatibilityLevel: utils.Ptr(int64(150)),
|
||||||
|
Owner: utils.Ptr("\"my-owner\""),
|
||||||
|
},
|
||||||
|
model: &dataSourceModel{
|
||||||
|
DatabaseModel: datasource.DatabaseModel{
|
||||||
|
ProjectId: types.StringValue("my-project"),
|
||||||
|
InstanceId: types.StringValue("my-instance"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
region: "eu01",
|
||||||
|
},
|
||||||
|
expected: expected{
|
||||||
|
model: &dataSourceModel{
|
||||||
|
DatabaseModel: datasource.DatabaseModel{
|
||||||
|
Id: types.Int64Value(1),
|
||||||
|
Name: types.StringValue("my-db"),
|
||||||
|
DatabaseName: types.StringValue("my-db"),
|
||||||
|
Owner: types.StringValue("my-owner"),
|
||||||
|
Region: types.StringValue("eu01"),
|
||||||
|
InstanceId: types.StringValue("my-instance"),
|
||||||
|
ProjectId: types.StringValue("my-project"),
|
||||||
|
CompatibilityLevel: types.Int64Value(150),
|
||||||
|
CollationName: types.StringValue("collation"),
|
||||||
|
},
|
||||||
|
TerraformId: types.StringValue("my-project,eu01,my-instance,my-db"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "should fail on nil source",
|
||||||
|
given: given{
|
||||||
|
source: nil,
|
||||||
|
model: &dataSourceModel{},
|
||||||
|
},
|
||||||
|
expected: expected{err: true},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "should fail on nil source ID",
|
||||||
|
given: given{
|
||||||
|
source: &sqlserverflexbeta.GetDatabaseResponse{Id: nil},
|
||||||
|
model: &dataSourceModel{},
|
||||||
|
},
|
||||||
|
expected: expected{err: true},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "should fail on nil model",
|
||||||
|
given: given{
|
||||||
|
source: &sqlserverflexbeta.GetDatabaseResponse{Id: utils.Ptr(int64(1))},
|
||||||
|
model: nil,
|
||||||
|
},
|
||||||
|
expected: expected{err: true},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testcases {
|
||||||
|
t.Run(
|
||||||
|
tc.name, func(t *testing.T) {
|
||||||
|
err := mapFields(tc.given.source, tc.given.model, tc.given.region)
|
||||||
|
if (err != nil) != tc.expected.err {
|
||||||
|
t.Fatalf("expected error: %v, got: %v", tc.expected.err, err)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
if diff := cmp.Diff(tc.expected.model, tc.given.model); diff != "" {
|
||||||
|
t.Errorf("model mismatch (-want +got):\n%s", diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMapResourceFields(t *testing.T) {
|
||||||
|
type given struct {
|
||||||
|
source *sqlserverflexbeta.GetDatabaseResponse
|
||||||
|
model *resourceModel
|
||||||
|
region string
|
||||||
|
}
|
||||||
|
type expected struct {
|
||||||
|
model *resourceModel
|
||||||
|
err bool
|
||||||
|
}
|
||||||
|
|
||||||
|
testcases := []struct {
|
||||||
|
name string
|
||||||
|
given given
|
||||||
|
expected expected
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "should map fields correctly",
|
||||||
|
given: given{
|
||||||
|
source: &sqlserverflexbeta.GetDatabaseResponse{
|
||||||
|
Id: utils.Ptr(int64(1)),
|
||||||
|
Name: utils.Ptr("my-db"),
|
||||||
|
Owner: utils.Ptr("\"my-owner\""),
|
||||||
|
},
|
||||||
|
model: &resourceModel{
|
||||||
|
ProjectId: types.StringValue("my-project"),
|
||||||
|
InstanceId: types.StringValue("my-instance"),
|
||||||
|
},
|
||||||
|
region: "eu01",
|
||||||
|
},
|
||||||
|
expected: expected{
|
||||||
|
model: &resourceModel{
|
||||||
|
Id: types.Int64Value(1),
|
||||||
|
Name: types.StringValue("my-db"),
|
||||||
|
DatabaseName: types.StringValue("my-db"),
|
||||||
|
InstanceId: types.StringValue("my-instance"),
|
||||||
|
ProjectId: types.StringValue("my-project"),
|
||||||
|
Region: types.StringValue("eu01"),
|
||||||
|
Owner: types.StringValue("my-owner"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "should fail on nil source",
|
||||||
|
given: given{
|
||||||
|
source: nil,
|
||||||
|
model: &resourceModel{},
|
||||||
|
},
|
||||||
|
expected: expected{err: true},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testcases {
|
||||||
|
t.Run(
|
||||||
|
tc.name, func(t *testing.T) {
|
||||||
|
err := mapResourceFields(tc.given.source, tc.given.model, tc.given.region)
|
||||||
|
if (err != nil) != tc.expected.err {
|
||||||
|
t.Fatalf("expected error: %v, got: %v", tc.expected.err, err)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
if diff := cmp.Diff(tc.expected.model, tc.given.model); diff != "" {
|
||||||
|
t.Errorf("model mismatch (-want +got):\n%s", diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestToCreatePayload(t *testing.T) {
|
||||||
|
type given struct {
|
||||||
|
model *resourceModel
|
||||||
|
}
|
||||||
|
type expected struct {
|
||||||
|
payload *sqlserverflexbeta.CreateDatabaseRequestPayload
|
||||||
|
err bool
|
||||||
|
}
|
||||||
|
|
||||||
|
testcases := []struct {
|
||||||
|
name string
|
||||||
|
given given
|
||||||
|
expected expected
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "should convert model to payload",
|
||||||
|
given: given{
|
||||||
|
model: &resourceModel{
|
||||||
|
Name: types.StringValue("my-db"),
|
||||||
|
Owner: types.StringValue("my-owner"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: expected{
|
||||||
|
payload: &sqlserverflexbeta.CreateDatabaseRequestPayload{
|
||||||
|
Name: utils.Ptr("my-db"),
|
||||||
|
Owner: utils.Ptr("my-owner"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "should fail on nil model",
|
||||||
|
given: given{model: nil},
|
||||||
|
expected: expected{err: true},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testcases {
|
||||||
|
t.Run(
|
||||||
|
tc.name, func(t *testing.T) {
|
||||||
|
actual, err := toCreatePayload(tc.given.model)
|
||||||
|
if (err != nil) != tc.expected.err {
|
||||||
|
t.Fatalf("expected error: %v, got: %v", tc.expected.err, err)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
if diff := cmp.Diff(tc.expected.payload, actual); diff != "" {
|
||||||
|
t.Errorf("payload mismatch (-want +got):\n%s", diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,9 +3,10 @@ package sqlserverflexbeta
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
_ "embed"
|
_ "embed"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/hashicorp/terraform-plugin-framework/path"
|
"github.com/hashicorp/terraform-plugin-framework/path"
|
||||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||||
|
|
@ -13,10 +14,9 @@ import (
|
||||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||||
"github.com/stackitcloud/stackit-sdk-go/core/config"
|
"github.com/stackitcloud/stackit-sdk-go/core/config"
|
||||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
|
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
|
||||||
wait "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/wait/sqlserverflexbeta"
|
|
||||||
|
|
||||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexbeta"
|
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexbeta"
|
||||||
|
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
|
||||||
|
|
||||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core"
|
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core"
|
||||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/utils"
|
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/utils"
|
||||||
|
|
@ -30,6 +30,13 @@ var (
|
||||||
_ resource.ResourceWithImportState = &databaseResource{}
|
_ resource.ResourceWithImportState = &databaseResource{}
|
||||||
_ resource.ResourceWithModifyPlan = &databaseResource{}
|
_ resource.ResourceWithModifyPlan = &databaseResource{}
|
||||||
_ resource.ResourceWithIdentity = &databaseResource{}
|
_ resource.ResourceWithIdentity = &databaseResource{}
|
||||||
|
|
||||||
|
// Define errors
|
||||||
|
errDatabaseNotFound = errors.New("database not found")
|
||||||
|
|
||||||
|
// Error message constants
|
||||||
|
extractErrorSummary = "extracting failed"
|
||||||
|
extractErrorMessage = "Extracting identity data: %v"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewDatabaseResource() resource.Resource {
|
func NewDatabaseResource() resource.Resource {
|
||||||
|
|
@ -192,18 +199,24 @@ func (r *databaseResource) Create(ctx context.Context, req resource.CreateReques
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx = core.LogResponse(ctx)
|
if createResp == nil || createResp.Id == nil {
|
||||||
createId, ok := createResp.GetIdOk()
|
|
||||||
if !ok {
|
|
||||||
core.LogAndAddError(
|
core.LogAndAddError(
|
||||||
ctx,
|
ctx,
|
||||||
&resp.Diagnostics,
|
&resp.Diagnostics,
|
||||||
createErr,
|
"Error creating database",
|
||||||
fmt.Sprintf("Calling API: %v", err),
|
"API didn't return database Id. A database might have been created",
|
||||||
)
|
)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
waitResp, err := wait.CreateDatabaseWaitHandler(
|
databaseId := *createResp.Id
|
||||||
|
|
||||||
|
ctx = tflog.SetField(ctx, "database_id", databaseId)
|
||||||
|
|
||||||
|
ctx = core.LogResponse(ctx)
|
||||||
|
|
||||||
|
// TODO: is this neccessary to wait for the database-> API say 200 ?
|
||||||
|
/*waitResp, err := wait.CreateDatabaseWaitHandler(
|
||||||
ctx,
|
ctx,
|
||||||
r.client,
|
r.client,
|
||||||
projectId,
|
projectId,
|
||||||
|
|
@ -263,23 +276,58 @@ func (r *databaseResource) Create(ctx context.Context, req resource.CreateReques
|
||||||
"Database creation waiting: returned name is different",
|
"Database creation waiting: returned name is different",
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
}*/
|
||||||
|
|
||||||
|
database, err := r.client.GetDatabaseRequest(ctx, projectId, region, instanceId, databaseName).Execute()
|
||||||
|
if err != nil {
|
||||||
|
core.LogAndAddError(
|
||||||
|
ctx,
|
||||||
|
&resp.Diagnostics,
|
||||||
|
"Error creating database",
|
||||||
|
fmt.Sprintf("Getting database details after creation: %v", err),
|
||||||
|
)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
data.Id = types.Int64PointerValue(waitResp.Id)
|
// Map response body to schema
|
||||||
data.Name = types.StringPointerValue(waitResp.Name)
|
err = mapResourceFields(database, &data, region)
|
||||||
|
if err != nil {
|
||||||
|
core.LogAndAddError(
|
||||||
|
ctx,
|
||||||
|
&resp.Diagnostics,
|
||||||
|
"Error creating database",
|
||||||
|
fmt.Sprintf("Processing API payload: %v", err),
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set data returned by API in identity
|
||||||
|
identity := DatabaseResourceIdentityModel{
|
||||||
|
ProjectID: types.StringValue(projectId),
|
||||||
|
Region: types.StringValue(region),
|
||||||
|
InstanceID: types.StringValue(instanceId),
|
||||||
|
DatabaseName: types.StringValue(databaseName),
|
||||||
|
}
|
||||||
|
resp.Diagnostics.Append(resp.Identity.Set(ctx, identity)...)
|
||||||
|
if resp.Diagnostics.HasError() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set state to fully populated data
|
||||||
|
resp.Diagnostics.Append(resp.State.Set(ctx, data)...)
|
||||||
|
if resp.Diagnostics.HasError() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Save data into Terraform state
|
// Save data into Terraform state
|
||||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
|
||||||
|
|
||||||
tflog.Info(ctx, "sqlserverflexbeta.Database created")
|
tflog.Info(ctx, "sqlserverflexbeta.Database created")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *databaseResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
|
func (r *databaseResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
|
||||||
var data resourceModel
|
var model resourceModel
|
||||||
readErr := "[Database Read]"
|
diags := req.State.Get(ctx, &model)
|
||||||
|
resp.Diagnostics.Append(diags...)
|
||||||
// Read Terraform prior state data into the model
|
|
||||||
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
|
|
||||||
if resp.Diagnostics.HasError() {
|
if resp.Diagnostics.HasError() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -293,78 +341,66 @@ func (r *databaseResource) Read(ctx context.Context, req resource.ReadRequest, r
|
||||||
|
|
||||||
ctx = core.InitProviderContext(ctx)
|
ctx = core.InitProviderContext(ctx)
|
||||||
|
|
||||||
projectId := identityData.ProjectID.ValueString()
|
projectId, instanceId, region, databaseName, errExt := r.extractIdentityData(model, identityData)
|
||||||
region := identityData.Region.ValueString()
|
if errExt != nil {
|
||||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
|
||||||
ctx = tflog.SetField(ctx, "region", region)
|
|
||||||
|
|
||||||
instanceId := identityData.InstanceID.ValueString()
|
|
||||||
ctx = tflog.SetField(ctx, "instance_id", instanceId)
|
|
||||||
|
|
||||||
dbName := identityData.DatabaseName.ValueString()
|
|
||||||
ctx = tflog.SetField(ctx, "database_name", dbName)
|
|
||||||
|
|
||||||
getResp, err := r.client.GetDatabaseRequest(ctx, projectId, region, instanceId, dbName).Execute()
|
|
||||||
if err != nil {
|
|
||||||
core.LogAndAddError(
|
core.LogAndAddError(
|
||||||
ctx,
|
ctx,
|
||||||
&resp.Diagnostics,
|
&resp.Diagnostics,
|
||||||
readErr,
|
extractErrorSummary,
|
||||||
fmt.Sprintf("Calling API: %v", err),
|
fmt.Sprintf(extractErrorMessage, errExt),
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||||
|
ctx = tflog.SetField(ctx, "instance_id", instanceId)
|
||||||
|
ctx = tflog.SetField(ctx, "region", region)
|
||||||
|
ctx = tflog.SetField(ctx, "database_name", databaseName)
|
||||||
|
|
||||||
|
databaseResp, err := r.client.GetDatabaseRequest(ctx, projectId, region, instanceId, databaseName).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) || errors.Is(err, errDatabaseNotFound) {
|
||||||
|
resp.State.RemoveResource(ctx)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading database", fmt.Sprintf("Calling API: %v", err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx = core.LogResponse(ctx)
|
ctx = core.LogResponse(ctx)
|
||||||
|
|
||||||
data.Id = types.Int64Value(getResp.GetId())
|
// Map response body to schema
|
||||||
data.Owner = types.StringValue(getResp.GetOwner())
|
err = mapResourceFields(databaseResp, &model, region)
|
||||||
data.Name = types.StringValue(getResp.GetName())
|
if err != nil {
|
||||||
data.DatabaseName = types.StringValue(getResp.GetName())
|
core.LogAndAddError(
|
||||||
data.CollationName = types.StringValue(getResp.GetCollationName())
|
ctx,
|
||||||
data.CompatibilityLevel = types.Int64Value(getResp.GetCompatibilityLevel())
|
&resp.Diagnostics,
|
||||||
|
"Error reading database",
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
// Save updated data into Terraform state
|
|
||||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
|
||||||
tflog.Info(ctx, "sqlserverflexbeta.Database read")
|
tflog.Info(ctx, "sqlserverflexbeta.Database read")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *databaseResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
|
func (r *databaseResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
|
||||||
var data resourceModel
|
// TODO: Check update api endpoint - not available at the moment, so return an error for now
|
||||||
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating database", "Database can't be updated")
|
||||||
// Read Terraform prior state data into the model
|
|
||||||
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
|
|
||||||
if resp.Diagnostics.HasError() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read identity data
|
|
||||||
var identityData DatabaseResourceIdentityModel
|
|
||||||
resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...)
|
|
||||||
if resp.Diagnostics.HasError() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx = core.InitProviderContext(ctx)
|
|
||||||
|
|
||||||
projectId := identityData.ProjectID.ValueString()
|
|
||||||
region := identityData.Region.ValueString()
|
|
||||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
|
||||||
ctx = tflog.SetField(ctx, "region", region)
|
|
||||||
|
|
||||||
// Todo: Update API call logic
|
|
||||||
|
|
||||||
// Save updated data into Terraform state
|
|
||||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
|
||||||
|
|
||||||
tflog.Info(ctx, "sqlserverflexbeta.Database updated")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *databaseResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
|
func (r *databaseResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
|
||||||
var data resourceModel
|
// nolint:gocritic // function signature required by Terraform
|
||||||
|
var model resourceModel
|
||||||
// Read Terraform prior state data into the model
|
diags := req.State.Get(ctx, &model)
|
||||||
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
|
resp.Diagnostics.Append(diags...)
|
||||||
if resp.Diagnostics.HasError() {
|
if resp.Diagnostics.HasError() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -378,12 +414,28 @@ func (r *databaseResource) Delete(ctx context.Context, req resource.DeleteReques
|
||||||
|
|
||||||
ctx = core.InitProviderContext(ctx)
|
ctx = core.InitProviderContext(ctx)
|
||||||
|
|
||||||
projectId := identityData.ProjectID.ValueString()
|
projectId, instanceId, region, databaseName, errExt := r.extractIdentityData(model, identityData)
|
||||||
region := identityData.Region.ValueString()
|
if errExt != nil {
|
||||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
core.LogAndAddError(
|
||||||
ctx = tflog.SetField(ctx, "region", region)
|
ctx,
|
||||||
|
&resp.Diagnostics,
|
||||||
|
extractErrorSummary,
|
||||||
|
fmt.Sprintf(extractErrorMessage, errExt),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// Todo: Delete API call logic
|
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||||
|
ctx = tflog.SetField(ctx, "instance_id", instanceId)
|
||||||
|
ctx = tflog.SetField(ctx, "region", region)
|
||||||
|
ctx = tflog.SetField(ctx, "database_name", databaseName)
|
||||||
|
|
||||||
|
// Delete existing record set
|
||||||
|
err := r.client.DeleteDatabaseRequestExecute(ctx, projectId, region, instanceId, databaseName)
|
||||||
|
if err != nil {
|
||||||
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting database", fmt.Sprintf("Calling API: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = core.LogResponse(ctx)
|
||||||
|
|
||||||
tflog.Info(ctx, "sqlserverflexbeta.Database deleted")
|
tflog.Info(ctx, "sqlserverflexbeta.Database deleted")
|
||||||
}
|
}
|
||||||
|
|
@ -504,3 +556,46 @@ func (r *databaseResource) ImportState(
|
||||||
|
|
||||||
tflog.Info(ctx, "Sqlserverflexbeta database state imported")
|
tflog.Info(ctx, "Sqlserverflexbeta database state imported")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// extractIdentityData extracts essential identifiers from the resource model, falling back to the identity model.
|
||||||
|
func (r *databaseResource) extractIdentityData(
|
||||||
|
model resourceModel,
|
||||||
|
identity DatabaseResourceIdentityModel,
|
||||||
|
) (projectId, region, instanceId, databaseName string, err error) {
|
||||||
|
if !model.DatabaseName.IsNull() && !model.DatabaseName.IsUnknown() {
|
||||||
|
databaseName = model.DatabaseName.ValueString()
|
||||||
|
} else {
|
||||||
|
if identity.DatabaseName.IsNull() || identity.DatabaseName.IsUnknown() {
|
||||||
|
return "", "", "", "", fmt.Errorf("database_name not found in config")
|
||||||
|
}
|
||||||
|
databaseName = identity.DatabaseName.ValueString()
|
||||||
|
}
|
||||||
|
|
||||||
|
if !model.ProjectId.IsNull() && !model.ProjectId.IsUnknown() {
|
||||||
|
projectId = model.ProjectId.ValueString()
|
||||||
|
} else {
|
||||||
|
if identity.ProjectID.IsNull() || identity.ProjectID.IsUnknown() {
|
||||||
|
return "", "", "", "", fmt.Errorf("project_id not found in config")
|
||||||
|
}
|
||||||
|
projectId = identity.ProjectID.ValueString()
|
||||||
|
}
|
||||||
|
|
||||||
|
if !model.Region.IsNull() && !model.Region.IsUnknown() {
|
||||||
|
region = r.providerData.GetRegionWithOverride(model.Region)
|
||||||
|
} else {
|
||||||
|
if identity.Region.IsNull() || identity.Region.IsUnknown() {
|
||||||
|
return "", "", "", "", fmt.Errorf("region not found in config")
|
||||||
|
}
|
||||||
|
region = r.providerData.GetRegionWithOverride(identity.Region)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !model.InstanceId.IsNull() && !model.InstanceId.IsUnknown() {
|
||||||
|
instanceId = model.InstanceId.ValueString()
|
||||||
|
} else {
|
||||||
|
if identity.InstanceID.IsNull() || identity.InstanceID.IsUnknown() {
|
||||||
|
return "", "", "", "", fmt.Errorf("instance_id not found in config")
|
||||||
|
}
|
||||||
|
instanceId = identity.InstanceID.ValueString()
|
||||||
|
}
|
||||||
|
return projectId, region, instanceId, databaseName, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,13 +8,12 @@ import (
|
||||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||||
|
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
|
||||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core"
|
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core"
|
||||||
|
sqlserverflexUtils "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexbeta/utils"
|
||||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/utils"
|
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/utils"
|
||||||
|
|
||||||
sqlserverflexbetaPkg "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexbeta"
|
sqlserverflexbetaPkg "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexbeta"
|
||||||
|
|
||||||
// sqlserverflexbetaUtils "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexbeta/utils"
|
|
||||||
|
|
||||||
sqlserverflexbetaGen "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexbeta/user/datasources_gen"
|
sqlserverflexbetaGen "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexbeta/user/datasources_gen"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -29,7 +28,7 @@ func NewUserDataSource() datasource.DataSource {
|
||||||
type dataSourceModel struct {
|
type dataSourceModel struct {
|
||||||
DefaultDatabase types.String `tfsdk:"default_database"`
|
DefaultDatabase types.String `tfsdk:"default_database"`
|
||||||
Host types.String `tfsdk:"host"`
|
Host types.String `tfsdk:"host"`
|
||||||
Id types.Int64 `tfsdk:"id"`
|
Id types.String `tfsdk:"id"`
|
||||||
InstanceId types.String `tfsdk:"instance_id"`
|
InstanceId types.String `tfsdk:"instance_id"`
|
||||||
Port types.Int64 `tfsdk:"port"`
|
Port types.Int64 `tfsdk:"port"`
|
||||||
ProjectId types.String `tfsdk:"project_id"`
|
ProjectId types.String `tfsdk:"project_id"`
|
||||||
|
|
@ -63,50 +62,52 @@ func (d *userDataSource) Configure(
|
||||||
req datasource.ConfigureRequest,
|
req datasource.ConfigureRequest,
|
||||||
resp *datasource.ConfigureResponse,
|
resp *datasource.ConfigureResponse,
|
||||||
) {
|
) {
|
||||||
//var ok bool
|
var ok bool
|
||||||
//d.providerData, ok = conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics)
|
d.providerData, ok = conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics)
|
||||||
//if !ok {
|
if !ok {
|
||||||
// return
|
return
|
||||||
//}
|
}
|
||||||
//
|
|
||||||
//apiClient := sqlserverflexbetaUtils.ConfigureClient(ctx, &d.providerData, &resp.Diagnostics)
|
apiClient := sqlserverflexUtils.ConfigureClient(ctx, &d.providerData, &resp.Diagnostics)
|
||||||
//if resp.Diagnostics.HasError() {
|
if resp.Diagnostics.HasError() {
|
||||||
// return
|
return
|
||||||
//}
|
}
|
||||||
//d.client = apiClient
|
d.client = apiClient
|
||||||
tflog.Info(ctx, fmt.Sprintf("%s client configured", errorPrefix))
|
tflog.Info(ctx, "SQL SERVER Flex alpha database client configured")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *userDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
|
func (d *userDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
|
||||||
var data dataSourceModel
|
var model dataSourceModel
|
||||||
|
diags := req.Config.Get(ctx, &model)
|
||||||
// Read Terraform configuration data into the model
|
resp.Diagnostics.Append(diags...)
|
||||||
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
|
|
||||||
|
|
||||||
if resp.Diagnostics.HasError() {
|
if resp.Diagnostics.HasError() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx = core.InitProviderContext(ctx)
|
ctx = core.InitProviderContext(ctx)
|
||||||
|
|
||||||
projectId := data.ProjectId.ValueString()
|
projectId := model.ProjectId.ValueString()
|
||||||
region := d.providerData.GetRegionWithOverride(data.Region)
|
instanceId := model.InstanceId.ValueString()
|
||||||
instanceId := data.InstanceId.ValueString()
|
userId := model.UserId.ValueInt64()
|
||||||
userId := data.UserId.ValueInt64()
|
region := d.providerData.GetRegionWithOverride(model.Region)
|
||||||
|
|
||||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||||
ctx = tflog.SetField(ctx, "region", region)
|
|
||||||
ctx = tflog.SetField(ctx, "instance_id", instanceId)
|
ctx = tflog.SetField(ctx, "instance_id", instanceId)
|
||||||
ctx = tflog.SetField(ctx, "user_id", userId)
|
ctx = tflog.SetField(ctx, "user_id", userId)
|
||||||
|
ctx = tflog.SetField(ctx, "region", region)
|
||||||
|
|
||||||
userResp, err := d.client.GetUserRequest(ctx, projectId, region, instanceId, userId).Execute()
|
recordSetResp, err := d.client.GetUserRequest(ctx, projectId, region, instanceId, userId).Execute()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.LogError(
|
utils.LogError(
|
||||||
ctx,
|
ctx,
|
||||||
&resp.Diagnostics,
|
&resp.Diagnostics,
|
||||||
err,
|
err,
|
||||||
"Reading user",
|
"Reading user",
|
||||||
fmt.Sprintf("user with ID %q does not exist in project %q.", userId, projectId),
|
fmt.Sprintf(
|
||||||
|
"User with ID %q or instance with ID %q does not exist in project %q.",
|
||||||
|
userId,
|
||||||
|
instanceId,
|
||||||
|
projectId,
|
||||||
|
),
|
||||||
map[int]string{
|
map[int]string{
|
||||||
http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
|
http.StatusForbidden: fmt.Sprintf("Project with ID %q not found or forbidden access", projectId),
|
||||||
},
|
},
|
||||||
|
|
@ -117,22 +118,23 @@ func (d *userDataSource) Read(ctx context.Context, req datasource.ReadRequest, r
|
||||||
|
|
||||||
ctx = core.LogResponse(ctx)
|
ctx = core.LogResponse(ctx)
|
||||||
|
|
||||||
// Todo: Read API call logic
|
// Map response body to schema and populate Computed attribute values
|
||||||
|
err = mapDataSourceFields(recordSetResp, &model, region)
|
||||||
// Example data value setting
|
|
||||||
// data.Id = types.StringValue("example-id")
|
|
||||||
|
|
||||||
err = mapResponseToModel(ctx, userResp, &data, resp.Diagnostics)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
core.LogAndAddError(
|
core.LogAndAddError(
|
||||||
ctx,
|
ctx,
|
||||||
&resp.Diagnostics,
|
&resp.Diagnostics,
|
||||||
fmt.Sprintf("%s Read", errorPrefix),
|
"Error reading user",
|
||||||
fmt.Sprintf("Processing API payload: %v", err),
|
fmt.Sprintf("Processing API payload: %v", err),
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save data into Terraform state
|
// Set refreshed state
|
||||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
diags = resp.State.Set(ctx, model)
|
||||||
|
resp.Diagnostics.Append(diags...)
|
||||||
|
if resp.Diagnostics.HasError() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tflog.Info(ctx, "SQLServer Flex beta instance read")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,98 +0,0 @@
|
||||||
package sqlserverflexbeta
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
|
||||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
|
||||||
|
|
||||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexbeta"
|
|
||||||
)
|
|
||||||
|
|
||||||
func mapResponseToModel(
|
|
||||||
ctx context.Context,
|
|
||||||
resp *sqlserverflexbeta.GetUserResponse,
|
|
||||||
m *dataSourceModel,
|
|
||||||
tfDiags diag.Diagnostics,
|
|
||||||
) error {
|
|
||||||
if resp == nil {
|
|
||||||
return fmt.Errorf("response is nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
m.Id = types.Int64Value(resp.GetId())
|
|
||||||
m.UserId = types.Int64Value(resp.GetId())
|
|
||||||
m.Username = types.StringValue(resp.GetUsername())
|
|
||||||
m.Port = types.Int64Value(resp.GetPort())
|
|
||||||
m.Host = types.StringValue(resp.GetHost())
|
|
||||||
m.DefaultDatabase = types.StringValue(resp.GetDefaultDatabase())
|
|
||||||
m.Status = types.StringValue(resp.GetStatus())
|
|
||||||
|
|
||||||
if resp.Roles != nil {
|
|
||||||
roles, diags := types.ListValueFrom(ctx, types.StringType, *resp.Roles)
|
|
||||||
tfDiags.Append(diags...)
|
|
||||||
if tfDiags.HasError() {
|
|
||||||
return fmt.Errorf("failed to map roles")
|
|
||||||
}
|
|
||||||
m.Roles = roles
|
|
||||||
} else {
|
|
||||||
m.Roles = types.ListNull(types.StringType)
|
|
||||||
}
|
|
||||||
|
|
||||||
if resp.Status != nil {
|
|
||||||
m.Status = types.StringValue(*resp.Status)
|
|
||||||
} else {
|
|
||||||
m.Status = types.StringNull()
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: complete and refactor
|
|
||||||
|
|
||||||
/*
|
|
||||||
sampleList, diags := types.ListValueFrom(ctx, types.StringType, resp.GetList())
|
|
||||||
tfDiags.Append(diags...)
|
|
||||||
if diags.HasError() {
|
|
||||||
return fmt.Errorf(
|
|
||||||
"error converting list response value",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
sample, diags := sqlserverflexbetaResGen.NewSampleValue(
|
|
||||||
sqlserverflexbetaResGen.SampleValue{}.AttributeTypes(ctx),
|
|
||||||
map[string]attr.Value{
|
|
||||||
"field": types.StringValue(string(resp.GetField())),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
tfDiags.Append(diags...)
|
|
||||||
if diags.HasError() {
|
|
||||||
return fmt.Errorf(
|
|
||||||
"error converting sample response value",
|
|
||||||
"sample",
|
|
||||||
types.StringValue(string(resp.GetField())),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
m.Sample = sample
|
|
||||||
*/
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
//func toCreatePayload(
|
|
||||||
// ctx context.Context,
|
|
||||||
// model *resourceModel,
|
|
||||||
//) (*sqlserverflexbeta.CreateUserRequestPayload, error) {
|
|
||||||
// if model == nil {
|
|
||||||
// return nil, fmt.Errorf("nil model")
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// var roles []sqlserverflexbeta.UserRole
|
|
||||||
// if !model.Roles.IsNull() && !model.Roles.IsUnknown() {
|
|
||||||
// diags := model.Roles.ElementsAs(ctx, &roles, false)
|
|
||||||
// if diags.HasError() {
|
|
||||||
// return nil, fmt.Errorf("failed to convert roles: %v", diags)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return &sqlserverflexbeta.CreateUserRequestPayload{
|
|
||||||
// DefaultDatabase: model.DefaultDatabase.ValueStringPointer(),
|
|
||||||
// Username: model.Username.ValueStringPointer(),
|
|
||||||
// Roles: &roles,
|
|
||||||
// }, nil
|
|
||||||
//}
|
|
||||||
179
stackit/internal/services/sqlserverflexbeta/user/mapper.go
Normal file
179
stackit/internal/services/sqlserverflexbeta/user/mapper.go
Normal file
|
|
@ -0,0 +1,179 @@
|
||||||
|
package sqlserverflexbeta
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework/attr"
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||||
|
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexbeta"
|
||||||
|
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
|
||||||
|
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core"
|
||||||
|
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// mapDataSourceFields maps the API response to a dataSourceModel.
|
||||||
|
func mapDataSourceFields(userResp *sqlserverflexbeta.GetUserResponse, model *dataSourceModel, region string) error {
|
||||||
|
if userResp == nil {
|
||||||
|
return fmt.Errorf("response is nil")
|
||||||
|
}
|
||||||
|
if model == nil {
|
||||||
|
return fmt.Errorf("model input is nil")
|
||||||
|
}
|
||||||
|
user := userResp
|
||||||
|
|
||||||
|
// Handle user ID
|
||||||
|
var userId int64
|
||||||
|
if model.UserId.ValueInt64() != 0 {
|
||||||
|
userId = model.UserId.ValueInt64()
|
||||||
|
} else if user.Id != nil {
|
||||||
|
userId = *user.Id
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("user id not present")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set main attributes
|
||||||
|
model.Id = utils.BuildInternalTerraformId(
|
||||||
|
model.ProjectId.ValueString(), region, model.InstanceId.ValueString(), strconv.FormatInt(userId, 10),
|
||||||
|
)
|
||||||
|
model.UserId = types.Int64Value(userId)
|
||||||
|
model.Username = types.StringPointerValue(user.Username)
|
||||||
|
|
||||||
|
// Map roles
|
||||||
|
if user.Roles == nil {
|
||||||
|
model.Roles = types.List(types.SetNull(types.StringType))
|
||||||
|
} else {
|
||||||
|
var roles []attr.Value
|
||||||
|
for _, role := range *user.Roles {
|
||||||
|
roles = append(roles, types.StringValue(string(role)))
|
||||||
|
}
|
||||||
|
rolesSet, diags := types.SetValue(types.StringType, roles)
|
||||||
|
if diags.HasError() {
|
||||||
|
return fmt.Errorf("failed to map roles: %w", core.DiagsToError(diags))
|
||||||
|
}
|
||||||
|
model.Roles = types.List(rolesSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set remaining attributes
|
||||||
|
model.Host = types.StringPointerValue(user.Host)
|
||||||
|
model.Port = types.Int64PointerValue(user.Port)
|
||||||
|
model.Region = types.StringValue(region)
|
||||||
|
model.Status = types.StringPointerValue(user.Status)
|
||||||
|
model.DefaultDatabase = types.StringPointerValue(user.DefaultDatabase)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// mapFields maps the API response to a resourceModel.
|
||||||
|
func mapFields(userResp *sqlserverflexbeta.GetUserResponse, model *resourceModel, region string) error {
|
||||||
|
if userResp == nil {
|
||||||
|
return fmt.Errorf("response is nil")
|
||||||
|
}
|
||||||
|
if model == nil {
|
||||||
|
return fmt.Errorf("model input is nil")
|
||||||
|
}
|
||||||
|
user := userResp
|
||||||
|
|
||||||
|
// Handle user ID
|
||||||
|
var userId int64
|
||||||
|
if model.UserId.ValueInt64() != 0 {
|
||||||
|
userId = model.UserId.ValueInt64()
|
||||||
|
} else if user.Id != nil {
|
||||||
|
userId = *user.Id
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("user id not present")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set main attributes
|
||||||
|
model.Id = types.Int64Value(userId)
|
||||||
|
model.UserId = types.Int64Value(userId)
|
||||||
|
model.Username = types.StringPointerValue(user.Username)
|
||||||
|
|
||||||
|
// Map roles
|
||||||
|
if user.Roles != nil {
|
||||||
|
var roles []attr.Value
|
||||||
|
for _, role := range *user.Roles {
|
||||||
|
roles = append(roles, types.StringValue(string(role)))
|
||||||
|
}
|
||||||
|
rolesSet, diags := types.SetValue(types.StringType, roles)
|
||||||
|
if diags.HasError() {
|
||||||
|
return fmt.Errorf("failed to map roles: %w", core.DiagsToError(diags))
|
||||||
|
}
|
||||||
|
model.Roles = types.List(rolesSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure roles is not null
|
||||||
|
if model.Roles.IsNull() || model.Roles.IsUnknown() {
|
||||||
|
model.Roles = types.List(types.SetNull(types.StringType))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set connection details
|
||||||
|
model.Host = types.StringPointerValue(user.Host)
|
||||||
|
model.Port = types.Int64PointerValue(user.Port)
|
||||||
|
model.Region = types.StringValue(region)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// mapFieldsCreate maps the API response from creating a user to a resourceModel.
|
||||||
|
func mapFieldsCreate(userResp *sqlserverflexbeta.CreateUserResponse, model *resourceModel, region string) error {
|
||||||
|
if userResp == nil {
|
||||||
|
return fmt.Errorf("response is nil")
|
||||||
|
}
|
||||||
|
if model == nil {
|
||||||
|
return fmt.Errorf("model input is nil")
|
||||||
|
}
|
||||||
|
user := userResp
|
||||||
|
|
||||||
|
if user.Id == nil {
|
||||||
|
return fmt.Errorf("user id not present")
|
||||||
|
}
|
||||||
|
userId := *user.Id
|
||||||
|
model.Id = types.Int64Value(userId)
|
||||||
|
model.UserId = types.Int64Value(userId)
|
||||||
|
model.Username = types.StringPointerValue(user.Username)
|
||||||
|
|
||||||
|
if user.Password == nil {
|
||||||
|
return fmt.Errorf("user password not present")
|
||||||
|
}
|
||||||
|
model.Password = types.StringValue(*user.Password)
|
||||||
|
|
||||||
|
if user.Roles != nil {
|
||||||
|
var roles []attr.Value
|
||||||
|
for _, role := range *user.Roles {
|
||||||
|
roles = append(roles, types.StringValue(string(role)))
|
||||||
|
}
|
||||||
|
rolesSet, diags := types.SetValue(types.StringType, roles)
|
||||||
|
if diags.HasError() {
|
||||||
|
return fmt.Errorf("failed to map roles: %w", core.DiagsToError(diags))
|
||||||
|
}
|
||||||
|
model.Roles = types.List(rolesSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
if model.Roles.IsNull() || model.Roles.IsUnknown() {
|
||||||
|
model.Roles = types.List(types.SetNull(types.StringType))
|
||||||
|
}
|
||||||
|
|
||||||
|
model.Host = types.StringPointerValue(user.Host)
|
||||||
|
model.Port = types.Int64PointerValue(user.Port)
|
||||||
|
model.Region = types.StringValue(region)
|
||||||
|
model.Status = types.StringPointerValue(user.Status)
|
||||||
|
model.DefaultDatabase = types.StringPointerValue(user.DefaultDatabase)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// toCreatePayload converts a resourceModel to an API CreateUserRequestPayload.
|
||||||
|
func toCreatePayload(
|
||||||
|
model *resourceModel,
|
||||||
|
roles []sqlserverflexbeta.UserRole,
|
||||||
|
) (*sqlserverflexbeta.CreateUserRequestPayload, error) {
|
||||||
|
if model == nil {
|
||||||
|
return nil, fmt.Errorf("nil model")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &sqlserverflexbeta.CreateUserRequestPayload{
|
||||||
|
Username: conversion.StringValueToPointer(model.Username),
|
||||||
|
DefaultDatabase: conversion.StringValueToPointer(model.DefaultDatabase),
|
||||||
|
Roles: &roles,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
527
stackit/internal/services/sqlserverflexbeta/user/mapper_test.go
Normal file
527
stackit/internal/services/sqlserverflexbeta/user/mapper_test.go
Normal file
|
|
@ -0,0 +1,527 @@
|
||||||
|
package sqlserverflexbeta
|
||||||
|
|
||||||
|
import (
|
||||||
|
"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"
|
||||||
|
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexbeta"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMapDataSourceFields(t *testing.T) {
|
||||||
|
const testRegion = "region"
|
||||||
|
tests := []struct {
|
||||||
|
description string
|
||||||
|
input *sqlserverflexbeta.GetUserResponse
|
||||||
|
region string
|
||||||
|
expected dataSourceModel
|
||||||
|
isValid bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"default_values",
|
||||||
|
&sqlserverflexbeta.GetUserResponse{},
|
||||||
|
testRegion,
|
||||||
|
dataSourceModel{
|
||||||
|
Id: types.StringValue("pid,region,iid,1"),
|
||||||
|
UserId: types.Int64Value(1),
|
||||||
|
InstanceId: types.StringValue("iid"),
|
||||||
|
ProjectId: types.StringValue("pid"),
|
||||||
|
Username: types.StringNull(),
|
||||||
|
Roles: types.List(types.SetNull(types.StringType)),
|
||||||
|
Host: types.StringNull(),
|
||||||
|
Port: types.Int64Null(),
|
||||||
|
Region: types.StringValue(testRegion),
|
||||||
|
Status: types.StringNull(),
|
||||||
|
DefaultDatabase: types.StringNull(),
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"simple_values",
|
||||||
|
&sqlserverflexbeta.GetUserResponse{
|
||||||
|
|
||||||
|
Roles: &[]sqlserverflexbeta.UserRole{
|
||||||
|
"role_1",
|
||||||
|
"role_2",
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
Username: utils.Ptr("username"),
|
||||||
|
Host: utils.Ptr("host"),
|
||||||
|
Port: utils.Ptr(int64(1234)),
|
||||||
|
Status: utils.Ptr("active"),
|
||||||
|
DefaultDatabase: utils.Ptr("default_db"),
|
||||||
|
},
|
||||||
|
testRegion,
|
||||||
|
dataSourceModel{
|
||||||
|
Id: types.StringValue("pid,region,iid,1"),
|
||||||
|
UserId: types.Int64Value(1),
|
||||||
|
InstanceId: types.StringValue("iid"),
|
||||||
|
ProjectId: types.StringValue("pid"),
|
||||||
|
Username: types.StringValue("username"),
|
||||||
|
Roles: types.List(
|
||||||
|
types.SetValueMust(
|
||||||
|
types.StringType, []attr.Value{
|
||||||
|
types.StringValue("role_1"),
|
||||||
|
types.StringValue("role_2"),
|
||||||
|
types.StringValue(""),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Host: types.StringValue("host"),
|
||||||
|
Port: types.Int64Value(1234),
|
||||||
|
Region: types.StringValue(testRegion),
|
||||||
|
Status: types.StringValue("active"),
|
||||||
|
DefaultDatabase: types.StringValue("default_db"),
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"null_fields_and_int_conversions",
|
||||||
|
&sqlserverflexbeta.GetUserResponse{
|
||||||
|
Id: utils.Ptr(int64(1)),
|
||||||
|
Roles: &[]sqlserverflexbeta.UserRole{},
|
||||||
|
Username: nil,
|
||||||
|
Host: nil,
|
||||||
|
Port: utils.Ptr(int64(2123456789)),
|
||||||
|
},
|
||||||
|
testRegion,
|
||||||
|
dataSourceModel{
|
||||||
|
Id: types.StringValue("pid,region,iid,1"),
|
||||||
|
UserId: types.Int64Value(1),
|
||||||
|
InstanceId: types.StringValue("iid"),
|
||||||
|
ProjectId: types.StringValue("pid"),
|
||||||
|
Username: types.StringNull(),
|
||||||
|
Roles: types.List(types.SetValueMust(types.StringType, []attr.Value{})),
|
||||||
|
Host: types.StringNull(),
|
||||||
|
Port: types.Int64Value(2123456789),
|
||||||
|
Region: types.StringValue(testRegion),
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nil_response",
|
||||||
|
nil,
|
||||||
|
testRegion,
|
||||||
|
dataSourceModel{},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nil_response_2",
|
||||||
|
&sqlserverflexbeta.GetUserResponse{},
|
||||||
|
testRegion,
|
||||||
|
dataSourceModel{},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"no_resource_id",
|
||||||
|
&sqlserverflexbeta.GetUserResponse{},
|
||||||
|
testRegion,
|
||||||
|
dataSourceModel{},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(
|
||||||
|
tt.description, func(t *testing.T) {
|
||||||
|
state := &dataSourceModel{
|
||||||
|
ProjectId: tt.expected.ProjectId,
|
||||||
|
InstanceId: tt.expected.InstanceId,
|
||||||
|
UserId: tt.expected.UserId,
|
||||||
|
}
|
||||||
|
err := mapDataSourceFields(tt.input, state, tt.region)
|
||||||
|
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(state, &tt.expected)
|
||||||
|
if diff != "" {
|
||||||
|
t.Fatalf("Data does not match: %s", diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMapFieldsCreate(t *testing.T) {
|
||||||
|
const testRegion = "region"
|
||||||
|
tests := []struct {
|
||||||
|
description string
|
||||||
|
input *sqlserverflexbeta.CreateUserResponse
|
||||||
|
region string
|
||||||
|
expected resourceModel
|
||||||
|
isValid bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"default_values",
|
||||||
|
&sqlserverflexbeta.CreateUserResponse{
|
||||||
|
Id: utils.Ptr(int64(1)),
|
||||||
|
Password: utils.Ptr(""),
|
||||||
|
},
|
||||||
|
testRegion,
|
||||||
|
resourceModel{
|
||||||
|
Id: types.Int64Value(1),
|
||||||
|
UserId: types.Int64Value(1),
|
||||||
|
InstanceId: types.StringValue("iid"),
|
||||||
|
ProjectId: types.StringValue("pid"),
|
||||||
|
Username: types.StringNull(),
|
||||||
|
Roles: types.List(types.SetNull(types.StringType)),
|
||||||
|
Password: types.StringValue(""),
|
||||||
|
Host: types.StringNull(),
|
||||||
|
Port: types.Int64Null(),
|
||||||
|
Region: types.StringValue(testRegion),
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"simple_values",
|
||||||
|
&sqlserverflexbeta.CreateUserResponse{
|
||||||
|
Id: utils.Ptr(int64(2)),
|
||||||
|
Roles: &[]sqlserverflexbeta.UserRole{
|
||||||
|
"role_1",
|
||||||
|
"role_2",
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
Username: utils.Ptr("username"),
|
||||||
|
Password: utils.Ptr("password"),
|
||||||
|
Host: utils.Ptr("host"),
|
||||||
|
Port: utils.Ptr(int64(1234)),
|
||||||
|
Status: utils.Ptr("status"),
|
||||||
|
DefaultDatabase: utils.Ptr("default_db"),
|
||||||
|
},
|
||||||
|
testRegion,
|
||||||
|
resourceModel{
|
||||||
|
Id: types.Int64Value(2),
|
||||||
|
UserId: types.Int64Value(2),
|
||||||
|
InstanceId: types.StringValue("iid"),
|
||||||
|
ProjectId: types.StringValue("pid"),
|
||||||
|
Username: types.StringValue("username"),
|
||||||
|
Roles: types.List(
|
||||||
|
types.SetValueMust(
|
||||||
|
types.StringType, []attr.Value{
|
||||||
|
types.StringValue("role_1"),
|
||||||
|
types.StringValue("role_2"),
|
||||||
|
types.StringValue(""),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Password: types.StringValue("password"),
|
||||||
|
Host: types.StringValue("host"),
|
||||||
|
Port: types.Int64Value(1234),
|
||||||
|
Region: types.StringValue(testRegion),
|
||||||
|
Status: types.StringValue("status"),
|
||||||
|
DefaultDatabase: types.StringValue("default_db"),
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"null_fields_and_int_conversions",
|
||||||
|
&sqlserverflexbeta.CreateUserResponse{
|
||||||
|
Id: utils.Ptr(int64(3)),
|
||||||
|
Roles: &[]sqlserverflexbeta.UserRole{},
|
||||||
|
Username: nil,
|
||||||
|
Password: utils.Ptr(""),
|
||||||
|
Host: nil,
|
||||||
|
Port: utils.Ptr(int64(2123456789)),
|
||||||
|
},
|
||||||
|
testRegion,
|
||||||
|
resourceModel{
|
||||||
|
Id: types.Int64Value(3),
|
||||||
|
UserId: types.Int64Value(3),
|
||||||
|
InstanceId: types.StringValue("iid"),
|
||||||
|
ProjectId: types.StringValue("pid"),
|
||||||
|
Username: types.StringNull(),
|
||||||
|
Roles: types.List(types.SetValueMust(types.StringType, []attr.Value{})),
|
||||||
|
Password: types.StringValue(""),
|
||||||
|
Host: types.StringNull(),
|
||||||
|
Port: types.Int64Value(2123456789),
|
||||||
|
Region: types.StringValue(testRegion),
|
||||||
|
DefaultDatabase: types.StringNull(),
|
||||||
|
Status: types.StringNull(),
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nil_response",
|
||||||
|
nil,
|
||||||
|
testRegion,
|
||||||
|
resourceModel{},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nil_response_2",
|
||||||
|
&sqlserverflexbeta.CreateUserResponse{},
|
||||||
|
testRegion,
|
||||||
|
resourceModel{},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"no_resource_id",
|
||||||
|
&sqlserverflexbeta.CreateUserResponse{},
|
||||||
|
testRegion,
|
||||||
|
resourceModel{},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"no_password",
|
||||||
|
&sqlserverflexbeta.CreateUserResponse{
|
||||||
|
Id: utils.Ptr(int64(1)),
|
||||||
|
},
|
||||||
|
testRegion,
|
||||||
|
resourceModel{},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(
|
||||||
|
tt.description, func(t *testing.T) {
|
||||||
|
state := &resourceModel{
|
||||||
|
ProjectId: tt.expected.ProjectId,
|
||||||
|
InstanceId: tt.expected.InstanceId,
|
||||||
|
}
|
||||||
|
err := mapFieldsCreate(tt.input, state, tt.region)
|
||||||
|
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(state, &tt.expected)
|
||||||
|
if diff != "" {
|
||||||
|
t.Fatalf("Data does not match: %s", diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMapFields(t *testing.T) {
|
||||||
|
const testRegion = "region"
|
||||||
|
tests := []struct {
|
||||||
|
description string
|
||||||
|
input *sqlserverflexbeta.GetUserResponse
|
||||||
|
region string
|
||||||
|
expected resourceModel
|
||||||
|
isValid bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"default_values",
|
||||||
|
&sqlserverflexbeta.GetUserResponse{},
|
||||||
|
testRegion,
|
||||||
|
resourceModel{
|
||||||
|
Id: types.Int64Value(1),
|
||||||
|
UserId: types.Int64Value(1),
|
||||||
|
InstanceId: types.StringValue("iid"),
|
||||||
|
ProjectId: types.StringValue("pid"),
|
||||||
|
Username: types.StringNull(),
|
||||||
|
Roles: types.List(types.SetNull(types.StringType)),
|
||||||
|
Host: types.StringNull(),
|
||||||
|
Port: types.Int64Null(),
|
||||||
|
Region: types.StringValue(testRegion),
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"simple_values",
|
||||||
|
&sqlserverflexbeta.GetUserResponse{
|
||||||
|
Roles: &[]sqlserverflexbeta.UserRole{
|
||||||
|
"role_1",
|
||||||
|
"role_2",
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
Username: utils.Ptr("username"),
|
||||||
|
Host: utils.Ptr("host"),
|
||||||
|
Port: utils.Ptr(int64(1234)),
|
||||||
|
},
|
||||||
|
testRegion,
|
||||||
|
resourceModel{
|
||||||
|
Id: types.Int64Value(2),
|
||||||
|
UserId: types.Int64Value(2),
|
||||||
|
InstanceId: types.StringValue("iid"),
|
||||||
|
ProjectId: types.StringValue("pid"),
|
||||||
|
Username: types.StringValue("username"),
|
||||||
|
Roles: types.List(
|
||||||
|
types.SetValueMust(
|
||||||
|
types.StringType, []attr.Value{
|
||||||
|
types.StringValue("role_1"),
|
||||||
|
types.StringValue("role_2"),
|
||||||
|
types.StringValue(""),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Host: types.StringValue("host"),
|
||||||
|
Port: types.Int64Value(1234),
|
||||||
|
Region: types.StringValue(testRegion),
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"null_fields_and_int_conversions",
|
||||||
|
&sqlserverflexbeta.GetUserResponse{
|
||||||
|
Id: utils.Ptr(int64(1)),
|
||||||
|
Roles: &[]sqlserverflexbeta.UserRole{},
|
||||||
|
Username: nil,
|
||||||
|
Host: nil,
|
||||||
|
Port: utils.Ptr(int64(2123456789)),
|
||||||
|
},
|
||||||
|
testRegion,
|
||||||
|
resourceModel{
|
||||||
|
Id: types.Int64Value(1),
|
||||||
|
UserId: types.Int64Value(1),
|
||||||
|
InstanceId: types.StringValue("iid"),
|
||||||
|
ProjectId: types.StringValue("pid"),
|
||||||
|
Username: types.StringNull(),
|
||||||
|
Roles: types.List(types.SetValueMust(types.StringType, []attr.Value{})),
|
||||||
|
Host: types.StringNull(),
|
||||||
|
Port: types.Int64Value(2123456789),
|
||||||
|
Region: types.StringValue(testRegion),
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nil_response",
|
||||||
|
nil,
|
||||||
|
testRegion,
|
||||||
|
resourceModel{},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nil_response_2",
|
||||||
|
&sqlserverflexbeta.GetUserResponse{},
|
||||||
|
testRegion,
|
||||||
|
resourceModel{},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"no_resource_id",
|
||||||
|
&sqlserverflexbeta.GetUserResponse{},
|
||||||
|
testRegion,
|
||||||
|
resourceModel{},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(
|
||||||
|
tt.description, func(t *testing.T) {
|
||||||
|
state := &resourceModel{
|
||||||
|
ProjectId: tt.expected.ProjectId,
|
||||||
|
InstanceId: tt.expected.InstanceId,
|
||||||
|
UserId: tt.expected.UserId,
|
||||||
|
}
|
||||||
|
err := mapFields(tt.input, state, tt.region)
|
||||||
|
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(state, &tt.expected)
|
||||||
|
if diff != "" {
|
||||||
|
t.Fatalf("Data does not match: %s", diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestToCreatePayload(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
description string
|
||||||
|
input *resourceModel
|
||||||
|
inputRoles []sqlserverflexbeta.UserRole
|
||||||
|
expected *sqlserverflexbeta.CreateUserRequestPayload
|
||||||
|
isValid bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"default_values",
|
||||||
|
&resourceModel{},
|
||||||
|
[]sqlserverflexbeta.UserRole{},
|
||||||
|
&sqlserverflexbeta.CreateUserRequestPayload{
|
||||||
|
Roles: &[]sqlserverflexbeta.UserRole{},
|
||||||
|
Username: nil,
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default_values",
|
||||||
|
&resourceModel{
|
||||||
|
Username: types.StringValue("username"),
|
||||||
|
},
|
||||||
|
[]sqlserverflexbeta.UserRole{
|
||||||
|
"role_1",
|
||||||
|
"role_2",
|
||||||
|
},
|
||||||
|
&sqlserverflexbeta.CreateUserRequestPayload{
|
||||||
|
Roles: &[]sqlserverflexbeta.UserRole{
|
||||||
|
"role_1",
|
||||||
|
"role_2",
|
||||||
|
},
|
||||||
|
Username: utils.Ptr("username"),
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"null_fields_and_int_conversions",
|
||||||
|
&resourceModel{
|
||||||
|
Username: types.StringNull(),
|
||||||
|
},
|
||||||
|
[]sqlserverflexbeta.UserRole{
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
&sqlserverflexbeta.CreateUserRequestPayload{
|
||||||
|
Roles: &[]sqlserverflexbeta.UserRole{
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
Username: nil,
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nil_model",
|
||||||
|
nil,
|
||||||
|
[]sqlserverflexbeta.UserRole{},
|
||||||
|
nil,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nil_roles",
|
||||||
|
&resourceModel{
|
||||||
|
Username: types.StringValue("username"),
|
||||||
|
},
|
||||||
|
[]sqlserverflexbeta.UserRole{},
|
||||||
|
&sqlserverflexbeta.CreateUserRequestPayload{
|
||||||
|
Roles: &[]sqlserverflexbeta.UserRole{},
|
||||||
|
Username: utils.Ptr("username"),
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(
|
||||||
|
tt.description, func(t *testing.T) {
|
||||||
|
output, err := toCreatePayload(tt.input, tt.inputRoles)
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,7 +3,9 @@ package sqlserverflexbeta
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
_ "embed"
|
_ "embed"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
|
@ -12,9 +14,12 @@ import (
|
||||||
"github.com/hashicorp/terraform-plugin-framework/resource/identityschema"
|
"github.com/hashicorp/terraform-plugin-framework/resource/identityschema"
|
||||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||||
"github.com/hashicorp/terraform-plugin-log/tflog"
|
"github.com/hashicorp/terraform-plugin-log/tflog"
|
||||||
"github.com/stackitcloud/stackit-sdk-go/core/config"
|
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
|
||||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexbeta"
|
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexbeta"
|
||||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
|
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/conversion"
|
||||||
|
sqlserverflexbetagen "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexbeta/user/resources_gen"
|
||||||
|
sqlserverflexbetaUtils "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/services/sqlserverflexbeta/utils"
|
||||||
|
sqlserverflexbetaWait "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/wait/sqlserverflexbeta"
|
||||||
|
|
||||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core"
|
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core"
|
||||||
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/utils"
|
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/utils"
|
||||||
|
|
@ -54,319 +59,20 @@ func (r *userResource) Metadata(ctx context.Context, req resource.MetadataReques
|
||||||
resp.TypeName = req.ProviderTypeName + "_sqlserverflexbeta_user"
|
resp.TypeName = req.ProviderTypeName + "_sqlserverflexbeta_user"
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:embed planModifiers.yaml
|
|
||||||
var modifiersFileByte []byte
|
|
||||||
|
|
||||||
func (r *userResource) Schema(ctx context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
|
|
||||||
|
|
||||||
s := sqlserverflexbetaResGen.UserResourceSchema(ctx)
|
|
||||||
|
|
||||||
fields, err := utils.ReadModifiersConfig(modifiersFileByte)
|
|
||||||
if err != nil {
|
|
||||||
resp.Diagnostics.AddError("error during read modifiers config file", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = utils.AddPlanModifiersToResourceSchema(fields, &s)
|
|
||||||
if err != nil {
|
|
||||||
resp.Diagnostics.AddError("error adding plan modifiers", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
resp.Schema = s
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *userResource) IdentitySchema(
|
|
||||||
_ context.Context,
|
|
||||||
_ resource.IdentitySchemaRequest,
|
|
||||||
resp *resource.IdentitySchemaResponse,
|
|
||||||
) {
|
|
||||||
resp.IdentitySchema = identityschema.Schema{
|
|
||||||
Attributes: map[string]identityschema.Attribute{
|
|
||||||
"project_id": identityschema.StringAttribute{
|
|
||||||
RequiredForImport: true, // must be set during import by the practitioner
|
|
||||||
},
|
|
||||||
"region": identityschema.StringAttribute{
|
|
||||||
RequiredForImport: true, // can be defaulted by the provider configuration
|
|
||||||
},
|
|
||||||
"instance_id": identityschema.StringAttribute{
|
|
||||||
RequiredForImport: true, // can be defaulted by the provider configuration
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure adds the provider configured client to the resource.
|
// Configure adds the provider configured client to the resource.
|
||||||
func (r *userResource) Configure(
|
func (r *userResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
|
||||||
ctx context.Context,
|
|
||||||
req resource.ConfigureRequest,
|
|
||||||
resp *resource.ConfigureResponse,
|
|
||||||
) {
|
|
||||||
var ok bool
|
var ok bool
|
||||||
r.providerData, ok = conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics)
|
r.providerData, ok = conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
apiClientConfigOptions := []config.ConfigurationOption{
|
apiClient := sqlserverflexbetaUtils.ConfigureClient(ctx, &r.providerData, &resp.Diagnostics)
|
||||||
config.WithCustomAuth(r.providerData.RoundTripper),
|
if resp.Diagnostics.HasError() {
|
||||||
utils.UserAgentConfigOption(r.providerData.Version),
|
|
||||||
}
|
|
||||||
if r.providerData.SQLServerFlexCustomEndpoint != "" {
|
|
||||||
apiClientConfigOptions = append(
|
|
||||||
apiClientConfigOptions,
|
|
||||||
config.WithEndpoint(r.providerData.SQLServerFlexCustomEndpoint),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
apiClientConfigOptions = append(apiClientConfigOptions, config.WithRegion(r.providerData.GetRegion()))
|
|
||||||
}
|
|
||||||
apiClient, err := sqlserverflexbeta.NewAPIClient(apiClientConfigOptions...)
|
|
||||||
if err != nil {
|
|
||||||
resp.Diagnostics.AddError(
|
|
||||||
"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
|
return
|
||||||
}
|
}
|
||||||
r.client = apiClient
|
r.client = apiClient
|
||||||
tflog.Info(ctx, "sqlserverflexbeta.User client configured")
|
tflog.Info(ctx, "SQLServer Beta Flex user client configured")
|
||||||
}
|
|
||||||
|
|
||||||
func (r *userResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
|
|
||||||
var data resourceModel
|
|
||||||
|
|
||||||
// Read Terraform plan data into the model
|
|
||||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)
|
|
||||||
|
|
||||||
if resp.Diagnostics.HasError() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read identity data
|
|
||||||
var identityData UserResourceIdentityModel
|
|
||||||
resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...)
|
|
||||||
if resp.Diagnostics.HasError() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx = core.InitProviderContext(ctx)
|
|
||||||
|
|
||||||
projectId := identityData.ProjectID.ValueString()
|
|
||||||
region := identityData.Region.ValueString()
|
|
||||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
|
||||||
ctx = tflog.SetField(ctx, "region", region)
|
|
||||||
|
|
||||||
//payload, err := toCreatePayload(ctx, &data)
|
|
||||||
//if err != nil {
|
|
||||||
// core.LogAndAddError(
|
|
||||||
// ctx,
|
|
||||||
// &resp.Diagnostics,
|
|
||||||
// "Error creating User",
|
|
||||||
// fmt.Sprintf("Creating API payload: %v", err),
|
|
||||||
// )
|
|
||||||
// return
|
|
||||||
//}
|
|
||||||
//payload = payload
|
|
||||||
|
|
||||||
// TODO: Create API call logic
|
|
||||||
/*
|
|
||||||
// Generate API request body from model
|
|
||||||
payload, err := toCreatePayload(ctx, &model)
|
|
||||||
if err != nil {
|
|
||||||
core.LogAndAddError(
|
|
||||||
ctx,
|
|
||||||
&resp.Diagnostics,
|
|
||||||
"Error creating User",
|
|
||||||
fmt.Sprintf("Creating API payload: %v", err),
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Create new User
|
|
||||||
createResp, err := r.client.CreateUserRequest(
|
|
||||||
ctx,
|
|
||||||
projectId,
|
|
||||||
region,
|
|
||||||
).CreateUserRequestPayload(*payload).Execute()
|
|
||||||
if err != nil {
|
|
||||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating User", fmt.Sprintf("Calling API: %v", err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx = core.LogResponse(ctx)
|
|
||||||
|
|
||||||
UserId := *createResp.Id
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Example data value setting
|
|
||||||
//data.UserId = types.StringValue("id-from-response")
|
|
||||||
|
|
||||||
// TODO: Set data returned by API in identity
|
|
||||||
identity := UserResourceIdentityModel{
|
|
||||||
ProjectID: types.StringValue(projectId),
|
|
||||||
Region: types.StringValue(region),
|
|
||||||
// TODO: add missing values
|
|
||||||
// UserID: types.StringValue(UserId),
|
|
||||||
}
|
|
||||||
resp.Diagnostics.Append(resp.Identity.Set(ctx, identity)...)
|
|
||||||
if resp.Diagnostics.HasError() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: implement wait handler if needed
|
|
||||||
/*
|
|
||||||
|
|
||||||
waitResp, err := wait.CreateUserWaitHandler(
|
|
||||||
ctx,
|
|
||||||
r.client,
|
|
||||||
projectId,
|
|
||||||
UserId,
|
|
||||||
region,
|
|
||||||
).SetSleepBeforeWait(
|
|
||||||
30 * time.Second,
|
|
||||||
).SetTimeout(
|
|
||||||
90 * time.Minute,
|
|
||||||
).WaitWithContext(ctx)
|
|
||||||
if err != nil {
|
|
||||||
core.LogAndAddError(
|
|
||||||
ctx,
|
|
||||||
&resp.Diagnostics,
|
|
||||||
"Error creating User",
|
|
||||||
fmt.Sprintf("User creation waiting: %v", err),
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if waitResp.Id == nil {
|
|
||||||
core.LogAndAddError(
|
|
||||||
ctx,
|
|
||||||
&resp.Diagnostics,
|
|
||||||
"Error creating User",
|
|
||||||
"User creation waiting: returned id is nil",
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map response body to schema
|
|
||||||
err = mapResponseToModel(ctx, waitResp, &model, resp.Diagnostics)
|
|
||||||
if err != nil {
|
|
||||||
core.LogAndAddError(
|
|
||||||
ctx,
|
|
||||||
&resp.Diagnostics,
|
|
||||||
"Error creating User",
|
|
||||||
fmt.Sprintf("Processing API payload: %v", err),
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Save data into Terraform state
|
|
||||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
|
||||||
|
|
||||||
tflog.Info(ctx, "sqlserverflexbeta.User created")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *userResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
|
|
||||||
var data resourceModel
|
|
||||||
|
|
||||||
// Read Terraform prior state data into the model
|
|
||||||
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
|
|
||||||
if resp.Diagnostics.HasError() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read identity data
|
|
||||||
var identityData UserResourceIdentityModel
|
|
||||||
resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...)
|
|
||||||
if resp.Diagnostics.HasError() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx = core.InitProviderContext(ctx)
|
|
||||||
|
|
||||||
projectId := identityData.ProjectID.ValueString()
|
|
||||||
region := identityData.Region.ValueString()
|
|
||||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
|
||||||
ctx = tflog.SetField(ctx, "region", region)
|
|
||||||
|
|
||||||
// Todo: Read API call logic
|
|
||||||
|
|
||||||
// Save updated data into Terraform state
|
|
||||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
|
||||||
|
|
||||||
// TODO: Set data returned by API in identity
|
|
||||||
identity := UserResourceIdentityModel{
|
|
||||||
ProjectID: types.StringValue(projectId),
|
|
||||||
Region: types.StringValue(region),
|
|
||||||
// InstanceID: types.StringValue(instanceId),
|
|
||||||
}
|
|
||||||
resp.Diagnostics.Append(resp.Identity.Set(ctx, identity)...)
|
|
||||||
if resp.Diagnostics.HasError() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
tflog.Info(ctx, "sqlserverflexbeta.User read")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *userResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
|
|
||||||
var data resourceModel
|
|
||||||
|
|
||||||
// Read Terraform prior state data into the model
|
|
||||||
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
|
|
||||||
if resp.Diagnostics.HasError() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read identity data
|
|
||||||
var identityData UserResourceIdentityModel
|
|
||||||
resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...)
|
|
||||||
if resp.Diagnostics.HasError() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx = core.InitProviderContext(ctx)
|
|
||||||
|
|
||||||
projectId := identityData.ProjectID.ValueString()
|
|
||||||
region := identityData.Region.ValueString()
|
|
||||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
|
||||||
ctx = tflog.SetField(ctx, "region", region)
|
|
||||||
|
|
||||||
// Todo: Update API call logic
|
|
||||||
|
|
||||||
// Save updated data into Terraform state
|
|
||||||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
|
|
||||||
|
|
||||||
tflog.Info(ctx, "sqlserverflexbeta.User updated")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *userResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
|
|
||||||
var data resourceModel
|
|
||||||
|
|
||||||
// Read Terraform prior state data into the model
|
|
||||||
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)
|
|
||||||
if resp.Diagnostics.HasError() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read identity data
|
|
||||||
var identityData UserResourceIdentityModel
|
|
||||||
resp.Diagnostics.Append(req.Identity.Get(ctx, &identityData)...)
|
|
||||||
if resp.Diagnostics.HasError() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx = core.InitProviderContext(ctx)
|
|
||||||
|
|
||||||
projectId := identityData.ProjectID.ValueString()
|
|
||||||
region := identityData.Region.ValueString()
|
|
||||||
ctx = tflog.SetField(ctx, "project_id", projectId)
|
|
||||||
ctx = tflog.SetField(ctx, "region", region)
|
|
||||||
|
|
||||||
// Todo: Delete API call logic
|
|
||||||
|
|
||||||
tflog.Info(ctx, "sqlserverflexbeta.User deleted")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ModifyPlan implements resource.ResourceWithModifyPlan.
|
// ModifyPlan implements resource.ResourceWithModifyPlan.
|
||||||
|
|
@ -397,25 +103,256 @@ func (r *userResource) ModifyPlan(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var identityModel UserResourceIdentityModel
|
|
||||||
identityModel.ProjectID = planModel.ProjectId
|
|
||||||
identityModel.Region = planModel.Region
|
|
||||||
// TODO: complete
|
|
||||||
//if !planModel.InstanceId.IsNull() && !planModel.InstanceId.IsUnknown() {
|
|
||||||
// identityModel.InstanceID = planModel.InstanceId
|
|
||||||
//}
|
|
||||||
|
|
||||||
resp.Diagnostics.Append(resp.Identity.Set(ctx, identityModel)...)
|
|
||||||
if resp.Diagnostics.HasError() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
resp.Diagnostics.Append(resp.Plan.Set(ctx, planModel)...)
|
resp.Diagnostics.Append(resp.Plan.Set(ctx, planModel)...)
|
||||||
if resp.Diagnostics.HasError() {
|
if resp.Diagnostics.HasError() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:embed planModifiers.yaml
|
||||||
|
var modifiersFileByte []byte
|
||||||
|
|
||||||
|
// Schema defines the schema for the resource.
|
||||||
|
func (r *userResource) Schema(ctx context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
|
||||||
|
s := sqlserverflexbetagen.UserResourceSchema(ctx)
|
||||||
|
|
||||||
|
fields, err := utils.ReadModifiersConfig(modifiersFileByte)
|
||||||
|
if err != nil {
|
||||||
|
resp.Diagnostics.AddError("error during read modifiers config file", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = utils.AddPlanModifiersToResourceSchema(fields, &s)
|
||||||
|
if err != nil {
|
||||||
|
resp.Diagnostics.AddError("error adding plan modifiers", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp.Schema = s
|
||||||
|
}
|
||||||
|
|
||||||
|
// IdentitySchema defines the schema for the resource's identity attributes.
|
||||||
|
func (r *userResource) IdentitySchema(
|
||||||
|
_ context.Context,
|
||||||
|
_ resource.IdentitySchemaRequest,
|
||||||
|
response *resource.IdentitySchemaResponse,
|
||||||
|
) {
|
||||||
|
response.IdentitySchema = identityschema.Schema{
|
||||||
|
Attributes: map[string]identityschema.Attribute{
|
||||||
|
"project_id": identityschema.StringAttribute{
|
||||||
|
RequiredForImport: true, // must be set during import by the practitioner
|
||||||
|
},
|
||||||
|
"region": identityschema.StringAttribute{
|
||||||
|
RequiredForImport: true, // can be defaulted by the provider configuration
|
||||||
|
},
|
||||||
|
"instance_id": identityschema.StringAttribute{
|
||||||
|
RequiredForImport: true, // can be defaulted by the provider configuration
|
||||||
|
},
|
||||||
|
"user_id": identityschema.Int64Attribute{
|
||||||
|
RequiredForImport: true, // can be defaulted by the provider configuration
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create creates the resource and sets the initial Terraform state.
|
||||||
|
func (r *userResource) Create(
|
||||||
|
ctx context.Context,
|
||||||
|
req resource.CreateRequest,
|
||||||
|
resp *resource.CreateResponse,
|
||||||
|
) { // nolint:gocritic // function signature required by Terraform
|
||||||
|
var model resourceModel
|
||||||
|
diags := req.Plan.Get(ctx, &model)
|
||||||
|
resp.Diagnostics.Append(diags...)
|
||||||
|
if resp.Diagnostics.HasError() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = core.InitProviderContext(ctx)
|
||||||
|
|
||||||
|
projectId := model.ProjectId.ValueString()
|
||||||
|
instanceId := model.InstanceId.ValueString()
|
||||||
|
region := model.Region.ValueString()
|
||||||
|
|
||||||
|
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||||
|
ctx = tflog.SetField(ctx, "instance_id", instanceId)
|
||||||
|
ctx = tflog.SetField(ctx, "region", region)
|
||||||
|
|
||||||
|
var roles []sqlserverflexbeta.UserRole
|
||||||
|
if !model.Roles.IsNull() && !model.Roles.IsUnknown() {
|
||||||
|
diags = model.Roles.ElementsAs(ctx, &roles, false)
|
||||||
|
resp.Diagnostics.Append(diags...)
|
||||||
|
if resp.Diagnostics.HasError() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate API request body from model
|
||||||
|
payload, err := toCreatePayload(&model, roles)
|
||||||
|
if err != nil {
|
||||||
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating user", fmt.Sprintf("Creating API payload: %v", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Create new user
|
||||||
|
userResp, err := r.client.CreateUserRequest(
|
||||||
|
ctx,
|
||||||
|
projectId,
|
||||||
|
region,
|
||||||
|
instanceId,
|
||||||
|
).CreateUserRequestPayload(*payload).Execute()
|
||||||
|
if err != nil {
|
||||||
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating user", fmt.Sprintf("Calling API: %v", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = core.LogResponse(ctx)
|
||||||
|
|
||||||
|
if userResp == nil || userResp.Id == nil || *userResp.Id == 0 {
|
||||||
|
core.LogAndAddError(
|
||||||
|
ctx,
|
||||||
|
&resp.Diagnostics,
|
||||||
|
"Error creating user",
|
||||||
|
"API didn't return user Id. A user might have been created",
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
userId := *userResp.Id
|
||||||
|
ctx = tflog.SetField(ctx, "user_id", userId)
|
||||||
|
|
||||||
|
// Map response body to schema
|
||||||
|
err = mapFieldsCreate(userResp, &model, region)
|
||||||
|
if err != nil {
|
||||||
|
core.LogAndAddError(
|
||||||
|
ctx,
|
||||||
|
&resp.Diagnostics,
|
||||||
|
"Error creating user",
|
||||||
|
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 user created")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read refreshes the Terraform state with the latest data.
|
||||||
|
func (r *userResource) Read(
|
||||||
|
ctx context.Context,
|
||||||
|
req resource.ReadRequest,
|
||||||
|
resp *resource.ReadResponse,
|
||||||
|
) { // nolint:gocritic // function signature required by Terraform
|
||||||
|
var model resourceModel
|
||||||
|
diags := req.State.Get(ctx, &model)
|
||||||
|
resp.Diagnostics.Append(diags...)
|
||||||
|
if resp.Diagnostics.HasError() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = core.InitProviderContext(ctx)
|
||||||
|
|
||||||
|
projectId := model.ProjectId.ValueString()
|
||||||
|
instanceId := model.InstanceId.ValueString()
|
||||||
|
userId := model.UserId.ValueInt64()
|
||||||
|
region := r.providerData.GetRegionWithOverride(model.Region)
|
||||||
|
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||||
|
ctx = tflog.SetField(ctx, "instance_id", instanceId)
|
||||||
|
ctx = tflog.SetField(ctx, "user_id", userId)
|
||||||
|
ctx = tflog.SetField(ctx, "region", region)
|
||||||
|
|
||||||
|
recordSetResp, err := r.client.GetUserRequest(ctx, projectId, region, instanceId, userId).Execute()
|
||||||
|
if err != nil {
|
||||||
|
var oapiErr *oapierror.GenericOpenAPIError
|
||||||
|
ok := errors.As(
|
||||||
|
err,
|
||||||
|
&oapiErr,
|
||||||
|
)
|
||||||
|
//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 user", fmt.Sprintf("Calling API: %v", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = core.LogResponse(ctx)
|
||||||
|
|
||||||
|
// Map response body to schema
|
||||||
|
err = mapFields(recordSetResp, &model, region)
|
||||||
|
if err != nil {
|
||||||
|
core.LogAndAddError(
|
||||||
|
ctx,
|
||||||
|
&resp.Diagnostics,
|
||||||
|
"Error reading user",
|
||||||
|
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 user read")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update updates the resource and sets the updated Terraform state on success.
|
||||||
|
func (r *userResource) Update(
|
||||||
|
ctx context.Context,
|
||||||
|
_ resource.UpdateRequest,
|
||||||
|
resp *resource.UpdateResponse,
|
||||||
|
) { // nolint:gocritic // function signature required by Terraform
|
||||||
|
// Update shouldn't be called
|
||||||
|
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating user", "User can't be updated")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete deletes the resource and removes the Terraform state on success.
|
||||||
|
func (r *userResource) Delete(
|
||||||
|
ctx context.Context,
|
||||||
|
req resource.DeleteRequest,
|
||||||
|
resp *resource.DeleteResponse,
|
||||||
|
) { // nolint:gocritic // function signature required by Terraform
|
||||||
|
// Retrieve values from plan
|
||||||
|
var model resourceModel
|
||||||
|
diags := req.State.Get(ctx, &model)
|
||||||
|
resp.Diagnostics.Append(diags...)
|
||||||
|
if resp.Diagnostics.HasError() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = core.InitProviderContext(ctx)
|
||||||
|
|
||||||
|
projectId := model.ProjectId.ValueString()
|
||||||
|
instanceId := model.InstanceId.ValueString()
|
||||||
|
userId := model.UserId.ValueInt64()
|
||||||
|
region := model.Region.ValueString()
|
||||||
|
ctx = tflog.SetField(ctx, "project_id", projectId)
|
||||||
|
ctx = tflog.SetField(ctx, "instance_id", instanceId)
|
||||||
|
ctx = tflog.SetField(ctx, "user_id", userId)
|
||||||
|
ctx = tflog.SetField(ctx, "region", region)
|
||||||
|
|
||||||
|
// Delete existing record set
|
||||||
|
// err := r.client.DeleteUserRequest(ctx, projectId, region, instanceId, userId).Execute()
|
||||||
|
|
||||||
|
// Delete existing record set
|
||||||
|
_, err := sqlserverflexbetaWait.DeleteUserWaitHandler(ctx, r.client, projectId, region, instanceId, userId).
|
||||||
|
WaitWithContext(ctx)
|
||||||
|
//err := r.client.DeleteUserRequest(ctx, arg.projectId, arg.region, arg.instanceId, userId).Execute()
|
||||||
|
if err != nil {
|
||||||
|
core.LogAndAddError(ctx, &resp.Diagnostics, "User Delete Error", fmt.Sprintf("Calling API: %v", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = core.LogResponse(ctx)
|
||||||
|
|
||||||
|
tflog.Info(ctx, "SQLServer Flex user deleted")
|
||||||
|
}
|
||||||
|
|
||||||
// ImportState imports a resource into the Terraform state on success.
|
// ImportState imports a resource into the Terraform state on success.
|
||||||
// The expected format of the resource import identifier is: project_id,zone_id,record_set_id
|
// The expected format of the resource import identifier is: project_id,zone_id,record_set_id
|
||||||
func (r *userResource) ImportState(
|
func (r *userResource) ImportState(
|
||||||
|
|
@ -423,6 +360,7 @@ func (r *userResource) ImportState(
|
||||||
req resource.ImportStateRequest,
|
req resource.ImportStateRequest,
|
||||||
resp *resource.ImportStateResponse,
|
resp *resource.ImportStateResponse,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
ctx = core.InitProviderContext(ctx)
|
ctx = core.InitProviderContext(ctx)
|
||||||
|
|
||||||
if req.ID != "" {
|
if req.ID != "" {
|
||||||
|
|
@ -457,7 +395,7 @@ func (r *userResource) ImportState(
|
||||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("instance_id"), idParts[2])...)
|
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("instance_id"), idParts[2])...)
|
||||||
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("user_id"), userId)...)
|
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("user_id"), userId)...)
|
||||||
|
|
||||||
tflog.Info(ctx, "Sqlserverflexbeta user state imported")
|
tflog.Info(ctx, "Postgres Flex user state imported")
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -482,9 +420,8 @@ func (r *userResource) ImportState(
|
||||||
core.LogAndAddWarning(
|
core.LogAndAddWarning(
|
||||||
ctx,
|
ctx,
|
||||||
&resp.Diagnostics,
|
&resp.Diagnostics,
|
||||||
"Sqlserverflexbeta database imported with empty password",
|
"SQLServer Flex user imported with empty password",
|
||||||
"The database password is not imported as it is only available upon creation of a new database. The password field will be empty.",
|
"The user password is not imported as it is only available upon creation of a new user. The password field will be empty.",
|
||||||
)
|
)
|
||||||
tflog.Info(ctx, "Sqlserverflexbeta user state imported")
|
tflog.Info(ctx, "SQLServer Flex user state imported")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
47
stackit/internal/services/sqlserverflexbeta/utils/util.go
Normal file
47
stackit/internal/services/sqlserverflexbeta/utils/util.go
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
sqlserverflex "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexbeta"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||||
|
"github.com/stackitcloud/stackit-sdk-go/core/config"
|
||||||
|
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core"
|
||||||
|
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ConfigureClient(
|
||||||
|
ctx context.Context,
|
||||||
|
providerData *core.ProviderData,
|
||||||
|
diags *diag.Diagnostics,
|
||||||
|
) *sqlserverflex.APIClient {
|
||||||
|
apiClientConfigOptions := []config.ConfigurationOption{
|
||||||
|
config.WithCustomAuth(providerData.RoundTripper),
|
||||||
|
utils.UserAgentConfigOption(providerData.Version),
|
||||||
|
}
|
||||||
|
if providerData.SQLServerFlexCustomEndpoint != "" {
|
||||||
|
apiClientConfigOptions = append(
|
||||||
|
apiClientConfigOptions,
|
||||||
|
config.WithEndpoint(providerData.SQLServerFlexCustomEndpoint),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
apiClientConfigOptions = append(apiClientConfigOptions, config.WithRegion(providerData.GetRegion()))
|
||||||
|
}
|
||||||
|
apiClient, err := sqlserverflex.NewAPIClient(apiClientConfigOptions...)
|
||||||
|
if err != nil {
|
||||||
|
core.LogAndAddError(
|
||||||
|
ctx,
|
||||||
|
diags,
|
||||||
|
"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 nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return apiClient
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,97 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||||
|
sdkClients "github.com/stackitcloud/stackit-sdk-go/core/clients"
|
||||||
|
"github.com/stackitcloud/stackit-sdk-go/core/config"
|
||||||
|
sqlserverflex "tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/pkg_gen/sqlserverflexbeta"
|
||||||
|
|
||||||
|
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/core"
|
||||||
|
"tf-provider.git.onstackit.cloud/stackit-dev-tools/terraform-provider-stackitprivatepreview/stackit/internal/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
testVersion = "1.2.3"
|
||||||
|
testCustomEndpoint = "https://sqlserverflex-custom-endpoint.api.stackit.cloud"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestConfigureClient(t *testing.T) {
|
||||||
|
/* mock authentication by setting service account token env variable */
|
||||||
|
os.Clearenv()
|
||||||
|
err := os.Setenv(sdkClients.ServiceAccountToken, "mock-val")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error setting env variable: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
providerData *core.ProviderData
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
wantErr bool
|
||||||
|
expected *sqlserverflex.APIClient
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "default endpoint",
|
||||||
|
args: args{
|
||||||
|
providerData: &core.ProviderData{
|
||||||
|
Version: testVersion,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: func() *sqlserverflex.APIClient {
|
||||||
|
apiClient, err := sqlserverflex.NewAPIClient(
|
||||||
|
config.WithRegion("eu01"),
|
||||||
|
utils.UserAgentConfigOption(testVersion),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error configuring client: %v", err)
|
||||||
|
}
|
||||||
|
return apiClient
|
||||||
|
}(),
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "custom endpoint",
|
||||||
|
args: args{
|
||||||
|
providerData: &core.ProviderData{
|
||||||
|
Version: testVersion,
|
||||||
|
SQLServerFlexCustomEndpoint: testCustomEndpoint,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: func() *sqlserverflex.APIClient {
|
||||||
|
apiClient, err := sqlserverflex.NewAPIClient(
|
||||||
|
utils.UserAgentConfigOption(testVersion),
|
||||||
|
config.WithEndpoint(testCustomEndpoint),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error configuring client: %v", err)
|
||||||
|
}
|
||||||
|
return apiClient
|
||||||
|
}(),
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(
|
||||||
|
tt.name, func(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
diags := diag.Diagnostics{}
|
||||||
|
|
||||||
|
actual := ConfigureClient(ctx, tt.args.providerData, &diags)
|
||||||
|
if diags.HasError() != tt.wantErr {
|
||||||
|
t.Errorf("ConfigureClient() error = %v, want %v", diags.HasError(), tt.wantErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(actual, tt.expected) {
|
||||||
|
t.Errorf("ConfigureClient() = %v, want %v", actual, tt.expected)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -27,9 +27,35 @@ const (
|
||||||
|
|
||||||
// APIClientInterface Interface needed for tests
|
// APIClientInterface Interface needed for tests
|
||||||
type APIClientInterface interface {
|
type APIClientInterface interface {
|
||||||
GetInstanceRequestExecute(ctx context.Context, projectId, region, instanceId string) (*sqlserverflex.GetInstanceResponse, error)
|
GetInstanceRequestExecute(
|
||||||
GetDatabaseRequestExecute(ctx context.Context, projectId string, region string, instanceId string, databaseName string) (*sqlserverflex.GetDatabaseResponse, error)
|
ctx context.Context,
|
||||||
GetUserRequestExecute(ctx context.Context, projectId string, region string, instanceId string, userId int64) (*sqlserverflex.GetUserResponse, error)
|
projectId, region, instanceId string,
|
||||||
|
) (*sqlserverflex.GetInstanceResponse, error)
|
||||||
|
GetDatabaseRequestExecute(
|
||||||
|
ctx context.Context,
|
||||||
|
projectId string,
|
||||||
|
region string,
|
||||||
|
instanceId string,
|
||||||
|
databaseName string,
|
||||||
|
) (*sqlserverflex.GetDatabaseResponse, error)
|
||||||
|
GetUserRequestExecute(
|
||||||
|
ctx context.Context,
|
||||||
|
projectId string,
|
||||||
|
region string,
|
||||||
|
instanceId string,
|
||||||
|
userId int64,
|
||||||
|
) (*sqlserverflex.GetUserResponse, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// APIClientUserInterface Interface needed for tests
|
||||||
|
type APIClientUserInterface interface {
|
||||||
|
DeleteUserRequestExecute(
|
||||||
|
ctx context.Context,
|
||||||
|
projectId string,
|
||||||
|
region string,
|
||||||
|
instanceId string,
|
||||||
|
userId int64,
|
||||||
|
) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateInstanceWaitHandler will wait for instance creation
|
// CreateInstanceWaitHandler will wait for instance creation
|
||||||
|
|
@ -38,93 +64,115 @@ func CreateInstanceWaitHandler(
|
||||||
a APIClientInterface,
|
a APIClientInterface,
|
||||||
projectId, instanceId, region string,
|
projectId, instanceId, region string,
|
||||||
) *wait.AsyncActionHandler[sqlserverflex.GetInstanceResponse] {
|
) *wait.AsyncActionHandler[sqlserverflex.GetInstanceResponse] {
|
||||||
handler := wait.New(func() (waitFinished bool, response *sqlserverflex.GetInstanceResponse, err error) {
|
handler := wait.New(
|
||||||
s, err := a.GetInstanceRequestExecute(ctx, projectId, region, instanceId)
|
func() (waitFinished bool, response *sqlserverflex.GetInstanceResponse, err error) {
|
||||||
if err != nil {
|
s, err := a.GetInstanceRequestExecute(ctx, projectId, region, instanceId)
|
||||||
return false, nil, err
|
if err != nil {
|
||||||
}
|
return false, nil, err
|
||||||
if s == nil || s.Id == nil || *s.Id != instanceId || s.Status == nil {
|
|
||||||
return false, nil, nil
|
|
||||||
}
|
|
||||||
switch strings.ToLower(string(*s.Status)) {
|
|
||||||
case strings.ToLower(InstanceStateSuccess):
|
|
||||||
if *s.Network.AccessScope == "SNA" {
|
|
||||||
if s.Network.InstanceAddress == nil {
|
|
||||||
tflog.Info(ctx, "Waiting for instance_address")
|
|
||||||
return false, nil, nil
|
|
||||||
}
|
|
||||||
if s.Network.RouterAddress == nil {
|
|
||||||
tflog.Info(ctx, "Waiting for router_address")
|
|
||||||
return false, nil, nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return true, s, nil
|
if s == nil || s.Id == nil || *s.Id != instanceId || s.Status == nil {
|
||||||
case strings.ToLower(InstanceStateUnknown), strings.ToLower(InstanceStateFailed):
|
return false, nil, nil
|
||||||
return true, s, fmt.Errorf("create failed for instance with id %s", instanceId)
|
}
|
||||||
case strings.ToLower(InstanceStatePending), strings.ToLower(InstanceStateProcessing):
|
switch strings.ToLower(string(*s.Status)) {
|
||||||
tflog.Info(ctx, "request is being handled", map[string]interface{}{
|
case strings.ToLower(InstanceStateSuccess):
|
||||||
"status": *s.Status,
|
if *s.Network.AccessScope == "SNA" {
|
||||||
})
|
if s.Network.InstanceAddress == nil {
|
||||||
return false, nil, nil
|
tflog.Info(ctx, "Waiting for instance_address")
|
||||||
default:
|
return false, nil, nil
|
||||||
tflog.Info(ctx, "Wait (create) received unknown status", map[string]interface{}{
|
}
|
||||||
"instanceId": instanceId,
|
if s.Network.RouterAddress == nil {
|
||||||
"status": s.Status,
|
tflog.Info(ctx, "Waiting for router_address")
|
||||||
})
|
return false, nil, nil
|
||||||
return false, s, nil
|
}
|
||||||
}
|
}
|
||||||
})
|
return true, s, nil
|
||||||
|
case strings.ToLower(InstanceStateUnknown), strings.ToLower(InstanceStateFailed):
|
||||||
|
return true, s, fmt.Errorf("create failed for instance with id %s", instanceId)
|
||||||
|
case strings.ToLower(InstanceStatePending), strings.ToLower(InstanceStateProcessing):
|
||||||
|
tflog.Info(
|
||||||
|
ctx, "request is being handled", map[string]interface{}{
|
||||||
|
"status": *s.Status,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return false, nil, nil
|
||||||
|
default:
|
||||||
|
tflog.Info(
|
||||||
|
ctx, "Wait (create) received unknown status", map[string]interface{}{
|
||||||
|
"instanceId": instanceId,
|
||||||
|
"status": s.Status,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return false, s, nil
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
return handler
|
return handler
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateInstanceWaitHandler will wait for instance update
|
// UpdateInstanceWaitHandler will wait for instance update
|
||||||
func UpdateInstanceWaitHandler(ctx context.Context, a APIClientInterface, projectId, instanceId, region string) *wait.AsyncActionHandler[sqlserverflex.GetInstanceResponse] {
|
func UpdateInstanceWaitHandler(
|
||||||
handler := wait.New(func() (waitFinished bool, response *sqlserverflex.GetInstanceResponse, err error) {
|
ctx context.Context,
|
||||||
s, err := a.GetInstanceRequestExecute(ctx, projectId, region, instanceId)
|
a APIClientInterface,
|
||||||
if err != nil {
|
projectId, instanceId, region string,
|
||||||
return false, nil, err
|
) *wait.AsyncActionHandler[sqlserverflex.GetInstanceResponse] {
|
||||||
}
|
handler := wait.New(
|
||||||
if s == nil || s.Id == nil || *s.Id != instanceId || s.Status == nil {
|
func() (waitFinished bool, response *sqlserverflex.GetInstanceResponse, err error) {
|
||||||
return false, nil, nil
|
s, err := a.GetInstanceRequestExecute(ctx, projectId, region, instanceId)
|
||||||
}
|
if err != nil {
|
||||||
switch strings.ToLower(string(*s.Status)) {
|
return false, nil, err
|
||||||
case strings.ToLower(InstanceStateSuccess):
|
}
|
||||||
return true, s, nil
|
if s == nil || s.Id == nil || *s.Id != instanceId || s.Status == nil {
|
||||||
case strings.ToLower(InstanceStateUnknown), strings.ToLower(InstanceStateFailed):
|
return false, nil, nil
|
||||||
return true, s, fmt.Errorf("update failed for instance with id %s", instanceId)
|
}
|
||||||
case strings.ToLower(InstanceStatePending), strings.ToLower(InstanceStateProcessing):
|
switch strings.ToLower(string(*s.Status)) {
|
||||||
tflog.Info(ctx, "request is being handled", map[string]interface{}{
|
case strings.ToLower(InstanceStateSuccess):
|
||||||
"status": *s.Status,
|
return true, s, nil
|
||||||
})
|
case strings.ToLower(InstanceStateUnknown), strings.ToLower(InstanceStateFailed):
|
||||||
return false, s, nil
|
return true, s, fmt.Errorf("update failed for instance with id %s", instanceId)
|
||||||
default:
|
case strings.ToLower(InstanceStatePending), strings.ToLower(InstanceStateProcessing):
|
||||||
tflog.Info(ctx, "Wait (update) received unknown status", map[string]interface{}{
|
tflog.Info(
|
||||||
"instanceId": instanceId,
|
ctx, "request is being handled", map[string]interface{}{
|
||||||
"status": s.Status,
|
"status": *s.Status,
|
||||||
})
|
},
|
||||||
return false, s, nil
|
)
|
||||||
}
|
return false, s, nil
|
||||||
})
|
default:
|
||||||
|
tflog.Info(
|
||||||
|
ctx, "Wait (update) received unknown status", map[string]interface{}{
|
||||||
|
"instanceId": instanceId,
|
||||||
|
"status": s.Status,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return false, s, nil
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
return handler
|
return handler
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteInstanceWaitHandler will wait for instance deletion
|
// DeleteInstanceWaitHandler will wait for instance deletion
|
||||||
func DeleteInstanceWaitHandler(ctx context.Context, a APIClientInterface, projectId, instanceId, region string) *wait.AsyncActionHandler[sqlserverflex.GetInstanceResponse] {
|
func DeleteInstanceWaitHandler(
|
||||||
handler := wait.New(func() (waitFinished bool, response *sqlserverflex.GetInstanceResponse, err error) {
|
ctx context.Context,
|
||||||
s, err := a.GetInstanceRequestExecute(ctx, projectId, region, instanceId)
|
a APIClientInterface,
|
||||||
if err == nil {
|
projectId, instanceId, region string,
|
||||||
return false, s, nil
|
) *wait.AsyncActionHandler[sqlserverflex.GetInstanceResponse] {
|
||||||
}
|
handler := wait.New(
|
||||||
var oapiErr *oapierror.GenericOpenAPIError
|
func() (waitFinished bool, response *sqlserverflex.GetInstanceResponse, err error) {
|
||||||
ok := errors.As(err, &oapiErr)
|
s, err := a.GetInstanceRequestExecute(ctx, projectId, region, instanceId)
|
||||||
if !ok {
|
if err == nil {
|
||||||
return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError")
|
return false, s, nil
|
||||||
}
|
}
|
||||||
if oapiErr.StatusCode != http.StatusNotFound {
|
var oapiErr *oapierror.GenericOpenAPIError
|
||||||
return false, nil, err
|
ok := errors.As(err, &oapiErr)
|
||||||
}
|
if !ok {
|
||||||
return true, nil, nil
|
return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError")
|
||||||
})
|
}
|
||||||
|
if oapiErr.StatusCode != http.StatusNotFound {
|
||||||
|
return false, nil, err
|
||||||
|
}
|
||||||
|
return true, nil, nil
|
||||||
|
},
|
||||||
|
)
|
||||||
handler.SetTimeout(30 * time.Minute)
|
handler.SetTimeout(30 * time.Minute)
|
||||||
return handler
|
return handler
|
||||||
}
|
}
|
||||||
|
|
@ -135,23 +183,60 @@ func CreateDatabaseWaitHandler(
|
||||||
a APIClientInterface,
|
a APIClientInterface,
|
||||||
projectId, instanceId, region, databaseName string,
|
projectId, instanceId, region, databaseName string,
|
||||||
) *wait.AsyncActionHandler[sqlserverflex.GetDatabaseResponse] {
|
) *wait.AsyncActionHandler[sqlserverflex.GetDatabaseResponse] {
|
||||||
handler := wait.New(func() (waitFinished bool, response *sqlserverflex.GetDatabaseResponse, err error) {
|
handler := wait.New(
|
||||||
s, err := a.GetDatabaseRequestExecute(ctx, projectId, region, instanceId, databaseName)
|
func() (waitFinished bool, response *sqlserverflex.GetDatabaseResponse, err error) {
|
||||||
if err != nil {
|
s, err := a.GetDatabaseRequestExecute(ctx, projectId, region, instanceId, databaseName)
|
||||||
return false, nil, err
|
if err != nil {
|
||||||
}
|
return false, nil, err
|
||||||
if s == nil || s.Name == nil || *s.Name != databaseName {
|
}
|
||||||
return false, nil, nil
|
if s == nil || s.Name == nil || *s.Name != databaseName {
|
||||||
}
|
return false, nil, nil
|
||||||
var oapiErr *oapierror.GenericOpenAPIError
|
}
|
||||||
ok := errors.As(err, &oapiErr)
|
var oapiErr *oapierror.GenericOpenAPIError
|
||||||
if !ok {
|
ok := errors.As(err, &oapiErr)
|
||||||
return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError")
|
if !ok {
|
||||||
}
|
return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError")
|
||||||
if oapiErr.StatusCode != http.StatusNotFound {
|
}
|
||||||
return false, nil, err
|
if oapiErr.StatusCode != http.StatusNotFound {
|
||||||
}
|
return false, nil, err
|
||||||
return true, nil, nil
|
}
|
||||||
})
|
return true, nil, nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return handler
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteUserWaitHandler will wait for instance deletion
|
||||||
|
func DeleteUserWaitHandler(
|
||||||
|
ctx context.Context,
|
||||||
|
a APIClientUserInterface,
|
||||||
|
projectId, instanceId, region string,
|
||||||
|
userId int64,
|
||||||
|
) *wait.AsyncActionHandler[struct{}] {
|
||||||
|
handler := wait.New(
|
||||||
|
func() (waitFinished bool, response *struct{}, err error) {
|
||||||
|
err = a.DeleteUserRequestExecute(ctx, projectId, region, instanceId, userId)
|
||||||
|
if err == nil {
|
||||||
|
return false, nil, nil
|
||||||
|
}
|
||||||
|
var oapiErr *oapierror.GenericOpenAPIError
|
||||||
|
ok := errors.As(err, &oapiErr)
|
||||||
|
if !ok {
|
||||||
|
return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError")
|
||||||
|
}
|
||||||
|
|
||||||
|
switch oapiErr.StatusCode {
|
||||||
|
case http.StatusNotFound:
|
||||||
|
return true, nil, nil
|
||||||
|
case http.StatusInternalServerError:
|
||||||
|
tflog.Warn(ctx, "Wait handler got error 500")
|
||||||
|
return false, nil, nil
|
||||||
|
default:
|
||||||
|
return false, nil, err
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
handler.SetTimeout(15 * time.Minute)
|
||||||
|
handler.SetSleepBeforeWait(15 * time.Second)
|
||||||
return handler
|
return handler
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue