Deprecate stackit_loadbalancer_credential and add new stackit_loadbalancer_observability_credential (#357)

* Copy over old resource files

* Adjustments to the new observability credential resource.go

* Register new resource in the provider

* Add example

* Adapt acc test

* Update docs

* Add deprecation message

* Fix linter

* Add deprecation message to dscription
This commit is contained in:
João Palet 2024-05-13 15:30:51 +01:00 committed by GitHub
parent 6eb1310056
commit ee905a3a5f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 580 additions and 14 deletions

View file

@ -1,6 +1,6 @@
# STACKIT Provider
# STACKIT Terraform Provider
The STACKIT provider is the official Terraform provider to integrate all the resources developed by STACKIT.
The STACKIT Terraform provider is the official Terraform provider to integrate all the resources developed by [STACKIT](https://www.stackit.de/en/).
## Example Usage
@ -34,14 +34,14 @@ provider "stackit" {
## Authentication
To authenticate, you will need a [service account](https://docs.stackit.cloud/stackit/en/service-accounts-134415819.html). Create it in the STACKIT Portal an assign it the necessary permissions, e.g. `project.owner`. There are multiple ways to authenticate:
To authenticate, you will need a [service account](https://docs.stackit.cloud/stackit/en/service-accounts-134415819.html). Create it in the [STACKIT Portal](https://portal.stackit.cloud/) and assign it the necessary permissions, e.g. `project.owner`. There are multiple ways to authenticate:
- Key flow (recommended)
- Token flow
When setting up authentication, the provider will always try to use the key flow first and search for credentials in several locations, following a specific order:
1. Explicit configuration, e.g. by seting the field `stackit_service_account_key_path` in the provider block (see example below)
1. Explicit configuration, e.g. by setting the field `stackit_service_account_key_path` in the provider block (see example below)
2. Environment variable, e.g. by setting `STACKIT_SERVICE_ACCOUNT_KEY_PATH`
3. Credentials file

View file

@ -4,12 +4,15 @@ page_title: "stackit_loadbalancer_credential Resource - stackit"
subcategory: ""
description: |-
Load balancer credential resource schema. Must have a region specified in the provider configuration.
!> The stackit_loadbalancer_credential resource has been deprecated and will be removed after November 13th 2024. Please use stackit_loadbalancer_observability_credential instead, which offers the exact same functionality.
---
# stackit_loadbalancer_credential (Resource)
Load balancer credential resource schema. Must have a `region` specified in the provider configuration.
!> The `stackit_loadbalancer_credential` resource has been deprecated and will be removed after November 13th 2024. Please use `stackit_loadbalancer_observability_credential` instead, which offers the exact same functionality.
## Example Usage
```terraform

View file

@ -0,0 +1,37 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "stackit_loadbalancer_observability_credential Resource - stackit"
subcategory: ""
description: |-
Load balancer observability credential resource schema. Must have a region specified in the provider configuration. These contain the username and password for the observability service (e.g. Argus) where the load balancer logs/metrics will be pushed into
---
# stackit_loadbalancer_observability_credential (Resource)
Load balancer observability credential resource schema. Must have a `region` specified in the provider configuration. These contain the username and password for the observability service (e.g. Argus) where the load balancer logs/metrics will be pushed into
## Example Usage
```terraform
resource "stackit_loadbalancer_observability_credential" "example" {
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
display_name = "example-credentials"
username = "example-user"
password = "example-password"
}
```
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- `display_name` (String) Observability credential name.
- `password` (String) The username for the observability service (e.g. Argus) where the logs/metrics will be pushed into.
- `project_id` (String) STACKIT project ID to which the load balancer observability credential is associated.
- `username` (String) The password for the observability service (e.g. Argus) where the logs/metrics will be pushed into.
### Read-Only
- `credentials_ref` (String) The credentials reference is used by the Load Balancer to define which credentials it will use.
- `id` (String) Terraform's internal resource ID. It is structured as "`project_id`","`credentials_ref`".

View file

@ -0,0 +1,6 @@
resource "stackit_loadbalancer_observability_credential" "example" {
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
display_name = "example-credentials"
username = "example-user"
password = "example-password"
}

View file

@ -102,10 +102,15 @@ func (r *credentialResource) Schema(_ context.Context, _ resource.SchemaRequest,
"display_name": "Credential name.",
"username": "The username used for the ARGUS instance.",
"password": "The password used for the ARGUS instance.",
"deprecation_message": "The `stackit_loadbalancer_credential` resource has been deprecated and will be removed after November 13th 2024. " +
"Please use `stackit_loadbalancer_observability_credential` instead, which offers the exact same functionality.",
}
resp.Schema = schema.Schema{
Description: descriptions["main"],
Description: fmt.Sprintf("%s\n%s", descriptions["main"], descriptions["deprecation_message"]),
// Callout block: https://developer.hashicorp.com/terraform/registry/providers/docs#callouts
MarkdownDescription: fmt.Sprintf("%s\n\n!> %s", descriptions["main"], descriptions["deprecation_message"]),
DeprecationMessage: descriptions["deprecation_message"],
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Description: descriptions["id"],

View file

@ -94,7 +94,7 @@ func configResources(targetPort string) string {
}
}
resource "stackit_loadbalancer_credential" "credential" {
resource "stackit_loadbalancer_observability_credential" "credential" {
project_id = "%s"
display_name = "%s"
username = "%s"
@ -245,13 +245,13 @@ func TestAccLoadBalancerResource(t *testing.T) {
// Credential
resource.TestCheckResourceAttrPair(
"stackit_loadbalancer_credential.credential", "project_id",
"stackit_loadbalancer_observability_credential.credential", "project_id",
"stackit_loadbalancer.loadbalancer", "project_id",
),
resource.TestCheckResourceAttrSet("stackit_loadbalancer_credential.credential", "credentials_ref"),
resource.TestCheckResourceAttr("stackit_loadbalancer_credential.credential", "display_name", loadBalancerResource["credential_display_name"]),
resource.TestCheckResourceAttr("stackit_loadbalancer_credential.credential", "username", loadBalancerResource["credential_username"]),
resource.TestCheckResourceAttr("stackit_loadbalancer_credential.credential", "password", loadBalancerResource["credential_password"]),
resource.TestCheckResourceAttrSet("stackit_loadbalancer_observability_credential.credential", "credentials_ref"),
resource.TestCheckResourceAttr("stackit_loadbalancer_observability_credential.credential", "display_name", loadBalancerResource["credential_display_name"]),
resource.TestCheckResourceAttr("stackit_loadbalancer_observability_credential.credential", "username", loadBalancerResource["credential_username"]),
resource.TestCheckResourceAttr("stackit_loadbalancer_observability_credential.credential", "password", loadBalancerResource["credential_password"]),
),
},
// Data source
@ -318,11 +318,11 @@ func TestAccLoadBalancerResource(t *testing.T) {
ImportStateVerify: true,
},
{
ResourceName: "stackit_loadbalancer_credential.credential",
ResourceName: "stackit_loadbalancer_observability_credential.credential",
ImportStateIdFunc: func(s *terraform.State) (string, error) {
r, ok := s.RootModule().Resources["stackit_loadbalancer_credential.credential"]
r, ok := s.RootModule().Resources["stackit_loadbalancer_observability_credential.credential"]
if !ok {
return "", fmt.Errorf("couldn't find resource stackit_loadbalancer_credential.credential")
return "", fmt.Errorf("couldn't find resource stackit_loadbalancer_observability_credential.credential")
}
credentialsRef, ok := r.Primary.Attributes["credentials_ref"]
if !ok {

View file

@ -0,0 +1,361 @@
package loadbalancer
import (
"context"
"fmt"
"net/http"
"strings"
"github.com/google/uuid"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/stackitcloud/stackit-sdk-go/core/config"
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/services/loadbalancer"
"github.com/stackitcloud/stackit-sdk-go/services/loadbalancer/wait"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
)
// Ensure the implementation satisfies the expected interfaces.
var (
_ resource.Resource = &observabilityCredentialResource{}
_ resource.ResourceWithConfigure = &observabilityCredentialResource{}
_ resource.ResourceWithImportState = &observabilityCredentialResource{}
)
type Model struct {
Id types.String `tfsdk:"id"` // needed by TF
ProjectId types.String `tfsdk:"project_id"`
DisplayName types.String `tfsdk:"display_name"`
Username types.String `tfsdk:"username"`
Password types.String `tfsdk:"password"`
CredentialsRef types.String `tfsdk:"credentials_ref"`
}
// NewObservabilityCredentialResource is a helper function to simplify the provider implementation.
func NewObservabilityCredentialResource() resource.Resource {
return &observabilityCredentialResource{}
}
// observabilityCredentialResource is the resource implementation.
type observabilityCredentialResource struct {
client *loadbalancer.APIClient
}
// Metadata returns the resource type name.
func (r *observabilityCredentialResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_loadbalancer_observability_credential"
}
// Configure adds the provider configured client to the resource.
func (r *observabilityCredentialResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
// Prevent panic if the provider has not been configured.
if req.ProviderData == nil {
return
}
providerData, ok := req.ProviderData.(core.ProviderData)
if !ok {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error configuring API client", fmt.Sprintf("Expected configure type stackit.ProviderData, got %T", req.ProviderData))
return
}
var apiClient *loadbalancer.APIClient
var err error
if providerData.LoadBalancerCustomEndpoint != "" {
ctx = tflog.SetField(ctx, "loadbalancer_custom_endpoint", providerData.LoadBalancerCustomEndpoint)
apiClient, err = loadbalancer.NewAPIClient(
config.WithCustomAuth(providerData.RoundTripper),
config.WithEndpoint(providerData.LoadBalancerCustomEndpoint),
)
} else {
apiClient, err = loadbalancer.NewAPIClient(
config.WithCustomAuth(providerData.RoundTripper),
config.WithRegion(providerData.Region),
)
}
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error configuring API client", fmt.Sprintf("Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration", err))
return
}
r.client = apiClient
tflog.Info(ctx, "Load Balancer client configured")
}
// Schema defines the schema for the resource.
func (r *observabilityCredentialResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
descriptions := map[string]string{
"main": "Load balancer observability credential resource schema. Must have a `region` specified in the provider configuration. These contain the username and password for the observability service (e.g. Argus) where the load balancer logs/metrics will be pushed into",
"id": "Terraform's internal resource ID. It is structured as \"`project_id`\",\"`credentials_ref`\".",
"credentials_ref": "The credentials reference is used by the Load Balancer to define which credentials it will use.",
"project_id": "STACKIT project ID to which the load balancer observability credential is associated.",
"display_name": "Observability credential name.",
"username": "The password for the observability service (e.g. Argus) where the logs/metrics will be pushed into.",
"password": "The username for the observability service (e.g. Argus) where the logs/metrics will be pushed into.",
}
resp.Schema = schema.Schema{
Description: descriptions["main"],
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Description: descriptions["id"],
Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
"credentials_ref": schema.StringAttribute{
Description: descriptions["credentials_ref"],
Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
"project_id": schema.StringAttribute{
Description: descriptions["project_id"],
Required: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
Validators: []validator.String{
validate.UUID(),
},
},
"display_name": schema.StringAttribute{
Description: descriptions["display_name"],
Required: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"username": schema.StringAttribute{
Description: descriptions["username"],
Required: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"password": schema.StringAttribute{
Description: descriptions["password"],
Required: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
},
}
}
// Create creates the resource and sets the initial Terraform state.
func (r *observabilityCredentialResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { // nolint:gocritic // function signature required by Terraform
// Retrieve values from plan
var model Model
diags := req.Plan.Get(ctx, &model)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
projectId := model.ProjectId.ValueString()
ctx = tflog.SetField(ctx, "project_id", projectId)
// Get status of load balancer functionality
statusResp, err := r.client.GetServiceStatus(ctx, projectId).Execute()
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error getting status of load balancer functionality", fmt.Sprintf("Calling API: %v", err))
return
}
// If load balancer functionality is not enabled, enable it
if *statusResp.Status != wait.FunctionalityStatusReady {
_, err = r.client.EnableService(ctx, projectId).XRequestID(uuid.NewString()).Execute()
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error enabling load balancer functionality", fmt.Sprintf("Calling API: %v", err))
return
}
_, err := wait.EnableServiceWaitHandler(ctx, r.client, projectId).WaitWithContext(ctx)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error enabling load balancer functionality", fmt.Sprintf("Waiting for enablement: %v", err))
return
}
}
// Generate API request body from model
payload, err := toCreatePayload(&model)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating observability credential", fmt.Sprintf("Creating API payload: %v", err))
return
}
// Create new observability credentials
createResp, err := r.client.CreateCredentials(ctx, projectId).CreateCredentialsPayload(*payload).XRequestID(uuid.NewString()).Execute()
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating observability credential", fmt.Sprintf("Calling API: %v", err))
return
}
ctx = tflog.SetField(ctx, "credentials_ref", createResp.Credential.CredentialsRef)
// Map response body to schema
err = mapFields(createResp.Credential, &model)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating observability credential", 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, "Load balancer observability credential created")
}
// Read refreshes the Terraform state with the latest data.
func (r *observabilityCredentialResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { // nolint:gocritic // function signature required by Terraform
var model Model
diags := req.State.Get(ctx, &model)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
projectId := model.ProjectId.ValueString()
credentialsRef := model.CredentialsRef.ValueString()
ctx = tflog.SetField(ctx, "project_id", projectId)
ctx = tflog.SetField(ctx, "credentials_ref", credentialsRef)
// Get credentials
credResp, err := r.client.GetCredentials(ctx, projectId, credentialsRef).Execute()
if err != nil {
oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
if ok && oapiErr.StatusCode == http.StatusNotFound {
resp.State.RemoveResource(ctx)
return
}
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading observability credential", fmt.Sprintf("Calling API: %v", err))
return
}
// Map response body to schema
err = mapFields(credResp.Credential, &model)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading observability credential", 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, "Load balancer observability credential read")
}
func (r *observabilityCredentialResource) 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 observability credential", "Observability credential can't be updated")
}
// Delete deletes the resource and removes the Terraform state on success.
func (r *observabilityCredentialResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { // nolint:gocritic // function signature required by Terraform
var model Model
diags := req.State.Get(ctx, &model)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
projectId := model.ProjectId.ValueString()
credentialsRef := model.CredentialsRef.ValueString()
ctx = tflog.SetField(ctx, "project_id", projectId)
ctx = tflog.SetField(ctx, "credentials_ref", credentialsRef)
// Delete credentials
_, err := r.client.DeleteCredentials(ctx, projectId, credentialsRef).Execute()
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting observability credential", fmt.Sprintf("Calling API: %v", err))
return
}
tflog.Info(ctx, "Load balancer observability credential deleted")
}
// ImportState imports a resource into the Terraform state on success.
// The expected format of the resource import identifier is: project_id,name
func (r *observabilityCredentialResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
idParts := strings.Split(req.ID, core.Separator)
if len(idParts) != 2 || idParts[0] == "" || idParts[1] == "" {
core.LogAndAddError(ctx, &resp.Diagnostics,
"Error importing observability credential",
fmt.Sprintf("Expected import identifier with format: [project_id],[credentials_ref] Got: %q", req.ID),
)
return
}
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("project_id"), idParts[0])...)
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("credentials_ref"), idParts[1])...)
tflog.Info(ctx, "Load balancer observability credential state imported")
}
func toCreatePayload(model *Model) (*loadbalancer.CreateCredentialsPayload, error) {
if model == nil {
return nil, fmt.Errorf("nil model")
}
return &loadbalancer.CreateCredentialsPayload{
DisplayName: conversion.StringValueToPointer(model.DisplayName),
Username: conversion.StringValueToPointer(model.Username),
Password: conversion.StringValueToPointer(model.Password),
}, nil
}
func mapFields(cred *loadbalancer.CredentialsResponse, m *Model) error {
if cred == nil {
return fmt.Errorf("response input is nil")
}
if m == nil {
return fmt.Errorf("model input is nil")
}
var credentialsRef string
if m.CredentialsRef.ValueString() != "" {
credentialsRef = m.CredentialsRef.ValueString()
} else if cred.CredentialsRef != nil {
credentialsRef = *cred.CredentialsRef
} else {
return fmt.Errorf("credentials ref not present")
}
m.CredentialsRef = types.StringValue(credentialsRef)
m.DisplayName = types.StringPointerValue(cred.DisplayName)
var username string
if m.Username.ValueString() != "" {
username = m.Username.ValueString()
} else if cred.Username != nil {
username = *cred.Username
} else {
return fmt.Errorf("username not present")
}
m.Username = types.StringValue(username)
idParts := []string{
m.ProjectId.ValueString(),
m.CredentialsRef.ValueString(),
}
m.Id = types.StringValue(
strings.Join(idParts, core.Separator),
)
return nil
}

View file

@ -0,0 +1,152 @@
package loadbalancer
import (
"testing"
"github.com/google/go-cmp/cmp"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/stackitcloud/stackit-sdk-go/core/utils"
"github.com/stackitcloud/stackit-sdk-go/services/loadbalancer"
)
func TestToCreatePayload(t *testing.T) {
tests := []struct {
description string
input *Model
expected *loadbalancer.CreateCredentialsPayload
isValid bool
}{
{
"default_values_ok",
&Model{},
&loadbalancer.CreateCredentialsPayload{
DisplayName: nil,
Username: nil,
Password: nil,
},
true,
},
{
"simple_values_ok",
&Model{
DisplayName: types.StringValue("display_name"),
Username: types.StringValue("username"),
Password: types.StringValue("password"),
},
&loadbalancer.CreateCredentialsPayload{
DisplayName: utils.Ptr("display_name"),
Username: utils.Ptr("username"),
Password: utils.Ptr("password"),
},
true,
},
{
"nil_model",
nil,
nil,
false,
},
}
for _, tt := range tests {
t.Run(tt.description, func(t *testing.T) {
output, err := toCreatePayload(tt.input)
if !tt.isValid && err == nil {
t.Fatalf("Should have failed")
}
if tt.isValid && err != nil {
t.Fatalf("Should not have failed: %v", err)
}
if tt.isValid {
diff := cmp.Diff(output, tt.expected)
if diff != "" {
t.Fatalf("Data does not match: %s", diff)
}
}
})
}
}
func TestMapFields(t *testing.T) {
tests := []struct {
description string
input *loadbalancer.CredentialsResponse
expected *Model
isValid bool
}{
{
"default_values_ok",
&loadbalancer.CredentialsResponse{
CredentialsRef: utils.Ptr("credentials_ref"),
Username: utils.Ptr("username"),
},
&Model{
Id: types.StringValue("pid,credentials_ref"),
ProjectId: types.StringValue("pid"),
CredentialsRef: types.StringValue("credentials_ref"),
Username: types.StringValue("username"),
},
true,
},
{
"simple_values_ok",
&loadbalancer.CredentialsResponse{
CredentialsRef: utils.Ptr("credentials_ref"),
DisplayName: utils.Ptr("display_name"),
Username: utils.Ptr("username"),
},
&Model{
Id: types.StringValue("pid,credentials_ref"),
ProjectId: types.StringValue("pid"),
CredentialsRef: types.StringValue("credentials_ref"),
DisplayName: types.StringValue("display_name"),
Username: types.StringValue("username"),
},
true,
},
{
"nil_response",
nil,
&Model{},
false,
},
{
"no_username",
&loadbalancer.CredentialsResponse{
CredentialsRef: utils.Ptr("credentials_ref"),
DisplayName: utils.Ptr("display_name"),
},
&Model{},
false,
},
{
"no_credentials_ref",
&loadbalancer.CredentialsResponse{
DisplayName: utils.Ptr("display_name"),
Username: utils.Ptr("username"),
},
&Model{},
false,
},
}
for _, tt := range tests {
t.Run(tt.description, func(t *testing.T) {
model := &Model{
ProjectId: tt.expected.ProjectId,
}
err := mapFields(tt.input, model)
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(model, tt.expected)
if diff != "" {
t.Fatalf("Data does not match: %s", diff)
}
}
})
}
}

View file

@ -16,6 +16,7 @@ import (
dnsZone "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/dns/zone"
loadBalancerCredential "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/loadbalancer/credential"
loadBalancer "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/loadbalancer/loadbalancer"
loadBalancerObservabilityCredential "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/loadbalancer/observability-credential"
logMeCredential "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/logme/credential"
logMeInstance "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/logme/instance"
mariaDBCredential "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/mariadb/credential"
@ -380,6 +381,7 @@ func (p *Provider) Resources(_ context.Context) []func() resource.Resource {
dnsRecordSet.NewRecordSetResource,
loadBalancer.NewLoadBalancerResource,
loadBalancerCredential.NewCredentialResource,
loadBalancerObservabilityCredential.NewObservabilityCredentialResource,
logMeInstance.NewInstanceResource,
logMeCredential.NewCredentialResource,
mariaDBInstance.NewInstanceResource,