IAM Role Assignment (#665)

* Initial PoC for a Project Role Assignment resource

Signed-off-by: Benjamin Ritter <benjamin.ritter@stackit.cloud>

* fix: move project_role_assignment into new "authorization" resource group

Signed-off-by: Benjamin Ritter <benjamin.ritter@stackit.cloud>

* feat: add authorization_project_role_assignment acceptance test

Signed-off-by: Benjamin Ritter <benjamin.ritter@stackit.cloud>

* docs: add authorization_project_role_assignment docs and examples

Signed-off-by: Benjamin Ritter <benjamin.ritter@stackit.cloud>

* fix: linting

Signed-off-by: Benjamin Ritter <benjamin.ritter@stackit.cloud>

* feat: add generic role_assignment resources

Signed-off-by: Benjamin Ritter <benjamin.ritter@stackit.cloud>

* feat: add infrastructure for experimental features

Signed-off-by: Benjamin Ritter <benjamin.ritter@stackit.cloud>

* feat: Make IAM resources part of the iam experiment

Signed-off-by: Benjamin Ritter <benjamin.ritter@stackit.cloud>

* fix: Log an error if an experiment does not exist

Signed-off-by: Benjamin Ritter <benjamin.ritter@stackit.cloud>

* fix: Do not cache the experiment check

Caching the experiment check causes problems when
running the provider in debug mode, since
configure in the provider can be called multiple
times there with different configurations, with
different experiments enabled.

Signed-off-by: Benjamin Ritter <benjamin.ritter@stackit.cloud>

---------

Signed-off-by: Benjamin Ritter <benjamin.ritter@stackit.cloud>
Co-authored-by: Benjamin Ritter <benjamin.ritter@stackit.cloud>
This commit is contained in:
Benjamin Ritter 2025-03-14 10:31:05 +01:00 committed by GitHub
parent 69b117f4e7
commit dadea7a904
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 853 additions and 1 deletions

View file

@ -0,0 +1,61 @@
package features
import (
"context"
"fmt"
"slices"
"strings"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
)
var AvailableExperiments []string = []string{"iam"}
// Check if an experiment is valid.
func ValidExperiment(experiment string, diags *diag.Diagnostics) bool {
validExperiment := slices.ContainsFunc(AvailableExperiments, func(e string) bool {
return strings.EqualFold(e, experiment)
})
if !validExperiment {
diags.AddError("Invalid Experiment", fmt.Sprintf("The Experiment %s is invalid. This is most likely a bug in the STACKIT Provider. Please open an issue. Available Experiments: %v", experiment, AvailableExperiments))
}
return validExperiment
}
// Check if an experiment is enabled.
func CheckExperimentEnabled(ctx context.Context, data *core.ProviderData, experiment, resourceType string, diags *diag.Diagnostics) {
if !ValidExperiment(experiment, diags) {
errTitle := fmt.Sprintf("The experiment %s does not exist.", experiment)
errContent := "This is a bug in the STACKIT Terraform Provider. Please open an issue here: https://github.com/stackitcloud/terraform-provider-stackit/issues"
diags.AddError(errTitle, errContent)
return
}
experimentActive := slices.ContainsFunc(data.Experiments, func(e string) bool {
return strings.EqualFold(e, experiment)
})
if experimentActive {
warnTitle := fmt.Sprintf("%s is part of the %s experiment.", resourceType, experiment)
warnContent := fmt.Sprintf("This resource is part of the %s experiment and is likely going to undergo significant changes or be removed in the future. Use it at your own discretion.", experiment)
tflog.Warn(ctx, fmt.Sprintf("%s | %s", warnTitle, warnContent))
diags.AddWarning(warnTitle, warnContent)
return
}
errTitle := fmt.Sprintf("%s is part of the %s experiment, which is currently disabled by default", resourceType, experiment)
errContent := fmt.Sprintf(`Enable the %s experiment by adding it into your provider block.`, experiment)
tflog.Error(ctx, fmt.Sprintf("%s | %s", errTitle, errContent))
diags.AddError(errTitle, errContent)
}
func AddExperimentDescription(description, experiment string) string {
// Callout block: https://developer.hashicorp.com/terraform/registry/providers/docs#callouts
return fmt.Sprintf("%s\n\n~> %s%s%s",
description,
"This resource is part of the ",
experiment,
" experiment and is likely going to undergo significant changes or be removed in the future. Use it at your own discretion.",
)
}

View file

@ -0,0 +1,113 @@
package features
import (
"context"
"testing"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
)
func TestValidExperiment(t *testing.T) {
type args struct {
experiment string
diags *diag.Diagnostics
}
tests := []struct {
name string
args args
want bool
}{
{
name: "valid",
args: args{
experiment: "iam",
diags: &diag.Diagnostics{},
},
want: true,
},
{
name: "invalid",
args: args{
experiment: "foo",
diags: &diag.Diagnostics{},
},
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := ValidExperiment(tt.args.experiment, tt.args.diags); got != tt.want {
t.Errorf("ValidExperiment() = %v, want %v", got, tt.want)
}
})
}
}
func TestCheckExperimentEnabled(t *testing.T) {
type args struct {
ctx context.Context
data *core.ProviderData
experiment string
resourceType string
diags *diag.Diagnostics
}
tests := []struct {
name string
args args
wantDiagsErr bool
wantDiagsWarning bool
}{
{
name: "enabled",
args: args{
ctx: context.Background(),
data: &core.ProviderData{
Experiments: []string{"iam"},
},
experiment: "iam",
diags: &diag.Diagnostics{},
},
wantDiagsErr: false,
wantDiagsWarning: true,
},
{
name: "disabled",
args: args{
ctx: context.Background(),
data: &core.ProviderData{
Experiments: []string{},
},
experiment: "iam",
diags: &diag.Diagnostics{},
},
wantDiagsErr: true,
wantDiagsWarning: false,
},
{
name: "invalid experiment",
args: args{
ctx: context.Background(),
data: &core.ProviderData{
Experiments: []string{"iam"},
},
experiment: "foobar",
resourceType: "provider",
diags: &diag.Diagnostics{},
},
wantDiagsErr: true,
wantDiagsWarning: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
CheckExperimentEnabled(tt.args.ctx, tt.args.data, tt.args.experiment, tt.args.resourceType, tt.args.diags)
if got := tt.args.diags.HasError(); got != tt.wantDiagsErr {
t.Errorf("CheckExperimentEnabled() diags.HasError() = %v, want %v", got, tt.wantDiagsErr)
}
if got := tt.args.diags.WarningsCount() > 0; got != tt.wantDiagsWarning {
t.Errorf("CheckExperimentEnabled() diags.WarningsCount() > 0 = %v, want %v", got, tt.wantDiagsErr)
}
})
}
}