Skip to content

Commit 962686b

Browse files
committed
apply feedback
Signed-off-by: Jorge Turrado <jorge.turrado@mail.schwarz>
1 parent 83bd761 commit 962686b

4 files changed

Lines changed: 276 additions & 37 deletions

File tree

docs/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,4 +208,4 @@ Note: AWS specific checks must be skipped as they do not work on STACKIT. For de
208208
- `ske_custom_endpoint` (String) Custom endpoint for the Kubernetes Engine (SKE) service
209209
- `sqlserverflex_custom_endpoint` (String) Custom endpoint for the SQL Server Flex service
210210
- `token_custom_endpoint` (String) Custom endpoint for the token API, which is used to request access tokens when using the key flow
211-
- `use_oidc` (Boolean) Should OIDC be used for Authentication? This can also be sourced from the `STACKIT_USE_OIDC` Environment Variable. Defaults to `false`.
211+
- `use_oidc` (Boolean) Enables OIDC for Authentication. This can also be sourced from the `STACKIT_USE_OIDC` Environment Variable. Defaults to `false`.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package utils
2+
3+
import (
4+
"os"
5+
"strings"
6+
7+
"github.com/hashicorp/terraform-plugin-framework/types"
8+
)
9+
10+
// GetEnvStringOrDefault takes a Framework StringValue and a corresponding Environment Variable name and returns
11+
// either the string value set in the StringValue if not Null / Unknown _or_ the os.GetEnv() value of the Environment
12+
// Variable provided. If both of these are empty, an empty string defaultValue is returned.
13+
func GetEnvStringOrDefault(val types.String, envVar, defaultValue string) string {
14+
if val.IsNull() || val.IsUnknown() {
15+
if v := os.Getenv(envVar); v != "" {
16+
return os.Getenv(envVar)
17+
}
18+
return defaultValue
19+
}
20+
21+
return val.ValueString()
22+
}
23+
24+
// GetEnvBoolIfValueAbsent takes a Framework BoolValue and a corresponding Environment Variable name and returns
25+
// one of the following in priority order:
26+
// 1 - the Boolean value set in the BoolValue if this is not Null / Unknown.
27+
// 2 - the boolean representation of the os.GetEnv() value of the Environment Variable provided (where anything but
28+
// 'true' or '1' is 'false').
29+
// 3 - `false` in all other cases.
30+
func GetEnvBoolIfValueAbsent(val types.Bool, envVar string) bool {
31+
if val.IsNull() || val.IsUnknown() {
32+
v := os.Getenv(envVar)
33+
if strings.EqualFold(v, "true") || strings.EqualFold(v, "1") {
34+
return true
35+
}
36+
}
37+
38+
return val.ValueBool()
39+
}
Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
package utils
2+
3+
import (
4+
"os"
5+
"testing"
6+
7+
"github.com/hashicorp/terraform-plugin-framework/types"
8+
)
9+
10+
func TestGetEnvStringOrDefault(t *testing.T) {
11+
const defaultValue = "default_value"
12+
tests := []struct {
13+
name string
14+
val types.String
15+
envVar string
16+
envValue string
17+
defaultValue string
18+
expected string
19+
}{
20+
{
21+
name: "value is set - should return value",
22+
val: types.StringValue("configured_value"),
23+
envVar: "TEST_ENV_VAR",
24+
envValue: "env_value",
25+
defaultValue: defaultValue,
26+
expected: "configured_value",
27+
},
28+
{
29+
name: "value is null, env var set - should return env value",
30+
val: types.StringNull(),
31+
envVar: "TEST_ENV_VAR",
32+
envValue: "env_value",
33+
defaultValue: defaultValue,
34+
expected: "env_value",
35+
},
36+
{
37+
name: "value is unknown, env var set - should return env value",
38+
val: types.StringUnknown(),
39+
envVar: "TEST_ENV_VAR",
40+
envValue: "env_value",
41+
defaultValue: defaultValue,
42+
expected: "env_value",
43+
},
44+
{
45+
name: "value is null, env var not set - should return default",
46+
val: types.StringNull(),
47+
envVar: "TEST_ENV_VAR",
48+
envValue: "",
49+
defaultValue: defaultValue,
50+
expected: defaultValue,
51+
},
52+
{
53+
name: "value is unknown, env var not set - should return default",
54+
val: types.StringUnknown(),
55+
envVar: "TEST_ENV_VAR",
56+
envValue: "",
57+
defaultValue: defaultValue,
58+
expected: defaultValue,
59+
},
60+
{
61+
name: "value is null, env var not set, empty default - should return empty string",
62+
val: types.StringNull(),
63+
envVar: "TEST_ENV_VAR",
64+
envValue: "",
65+
defaultValue: "",
66+
expected: "",
67+
},
68+
{
69+
name: "value is empty string - should return empty string",
70+
val: types.StringValue(""),
71+
envVar: "TEST_ENV_VAR",
72+
envValue: "env_value",
73+
defaultValue: defaultValue,
74+
expected: "",
75+
},
76+
}
77+
78+
for _, tt := range tests {
79+
t.Run(tt.name, func(t *testing.T) {
80+
// Clean up any previous value first
81+
os.Unsetenv(tt.envVar)
82+
83+
// Setup environment variable
84+
if tt.envValue != "" {
85+
os.Setenv(tt.envVar, tt.envValue)
86+
defer os.Unsetenv(tt.envVar)
87+
}
88+
89+
result := GetEnvStringOrDefault(tt.val, tt.envVar, tt.defaultValue)
90+
91+
if result != tt.expected {
92+
t.Errorf("GetEnvStringOrDefault() = %v, expected %v", result, tt.expected)
93+
}
94+
})
95+
}
96+
}
97+
98+
func TestGetEnvBoolIfValueAbsent(t *testing.T) {
99+
tests := []struct {
100+
name string
101+
val types.Bool
102+
envVar string
103+
envValue string
104+
expected bool
105+
}{
106+
{
107+
name: "value is true - should return true",
108+
val: types.BoolValue(true),
109+
envVar: "TEST_BOOL_ENV_VAR",
110+
envValue: "false",
111+
expected: true,
112+
},
113+
{
114+
name: "value is false - should return false",
115+
val: types.BoolValue(false),
116+
envVar: "TEST_BOOL_ENV_VAR",
117+
envValue: "true",
118+
expected: false,
119+
},
120+
{
121+
name: "value is null, env var is 'true' - should return true",
122+
val: types.BoolNull(),
123+
envVar: "TEST_BOOL_ENV_VAR",
124+
envValue: "true",
125+
expected: true,
126+
},
127+
{
128+
name: "value is null, env var is 'True' (case insensitive) - should return true",
129+
val: types.BoolNull(),
130+
envVar: "TEST_BOOL_ENV_VAR",
131+
envValue: "True",
132+
expected: true,
133+
},
134+
{
135+
name: "value is null, env var is 'TRUE' (case insensitive) - should return true",
136+
val: types.BoolNull(),
137+
envVar: "TEST_BOOL_ENV_VAR",
138+
envValue: "TRUE",
139+
expected: true,
140+
},
141+
{
142+
name: "value is null, env var is '1' - should return true",
143+
val: types.BoolNull(),
144+
envVar: "TEST_BOOL_ENV_VAR",
145+
envValue: "1",
146+
expected: true,
147+
},
148+
{
149+
name: "value is null, env var is 'false' - should return false",
150+
val: types.BoolNull(),
151+
envVar: "TEST_BOOL_ENV_VAR",
152+
envValue: "false",
153+
expected: false,
154+
},
155+
{
156+
name: "value is null, env var is 'False' - should return false",
157+
val: types.BoolNull(),
158+
envVar: "TEST_BOOL_ENV_VAR",
159+
envValue: "False",
160+
expected: false,
161+
},
162+
{
163+
name: "value is null, env var is '0' - should return false",
164+
val: types.BoolNull(),
165+
envVar: "TEST_BOOL_ENV_VAR",
166+
envValue: "0",
167+
expected: false,
168+
},
169+
{
170+
name: "value is null, env var is empty - should return false",
171+
val: types.BoolNull(),
172+
envVar: "TEST_BOOL_ENV_VAR",
173+
envValue: "",
174+
expected: false,
175+
},
176+
{
177+
name: "value is null, env var is random string - should return false",
178+
val: types.BoolNull(),
179+
envVar: "TEST_BOOL_ENV_VAR",
180+
envValue: "random",
181+
expected: false,
182+
},
183+
{
184+
name: "value is unknown, env var is 'true' - should return true",
185+
val: types.BoolUnknown(),
186+
envVar: "TEST_BOOL_ENV_VAR",
187+
envValue: "true",
188+
expected: true,
189+
},
190+
{
191+
name: "value is unknown, env var is '1' - should return true",
192+
val: types.BoolUnknown(),
193+
envVar: "TEST_BOOL_ENV_VAR",
194+
envValue: "1",
195+
expected: true,
196+
},
197+
{
198+
name: "value is unknown, env var is 'false' - should return false",
199+
val: types.BoolUnknown(),
200+
envVar: "TEST_BOOL_ENV_VAR",
201+
envValue: "false",
202+
expected: false,
203+
},
204+
{
205+
name: "value is unknown, env var not set - should return false",
206+
val: types.BoolUnknown(),
207+
envVar: "TEST_BOOL_ENV_VAR",
208+
envValue: "",
209+
expected: false,
210+
},
211+
}
212+
213+
for _, tt := range tests {
214+
t.Run(tt.name, func(t *testing.T) {
215+
// Clean up any previous value first
216+
os.Unsetenv(tt.envVar)
217+
218+
// Setup environment variable
219+
if tt.envValue != "" {
220+
os.Setenv(tt.envVar, tt.envValue)
221+
defer os.Unsetenv(tt.envVar)
222+
}
223+
224+
result := GetEnvBoolIfValueAbsent(tt.val, tt.envVar)
225+
226+
if result != tt.expected {
227+
t.Errorf("GetEnvBoolIfValueAbsent() = %v, expected %v", result, tt.expected)
228+
}
229+
})
230+
}
231+
}

stackit/provider.go

Lines changed: 5 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package stackit
33
import (
44
"context"
55
"fmt"
6-
"os"
76
"strings"
87

98
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
@@ -112,6 +111,7 @@ import (
112111
skeMachineImages "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/ske/provideroptions/machineimages"
113112
sqlServerFlexInstance "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/sqlserverflex/instance"
114113
sqlServerFlexUser "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/sqlserverflex/user"
114+
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
115115
)
116116

117117
// Ensure the implementation satisfies the expected interfaces
@@ -205,7 +205,7 @@ func (p *Provider) Schema(_ context.Context, _ provider.SchemaRequest, resp *pro
205205
"service_account_email": "Service account email. It can also be set using the environment variable STACKIT_SERVICE_ACCOUNT_EMAIL. It is required if you want to use the resource manager project resource. This value is required using OpenID Connect authentication.",
206206
"service_account_federated_token_path": "Path for workload identity assertion. It can also be set using the environment variable STACKIT_FEDERATED_TOKEN_FILE.",
207207
"service_account_federated_token": "The OIDC ID token for use when authenticating as a Service Account using OpenID Connect.",
208-
"use_oidc": "Should OIDC be used for Authentication? This can also be sourced from the `STACKIT_USE_OIDC` Environment Variable. Defaults to `false`.",
208+
"use_oidc": "Enables OIDC for Authentication. This can also be sourced from the `STACKIT_USE_OIDC` Environment Variable. Defaults to `false`.",
209209
"oidc_request_url": "The URL for the OIDC provider from which to request an ID token. For use when authenticating as a Service Account using OpenID Connect.",
210210
"oidc_request_token": "The bearer token for the request to the OIDC provider. For use when authenticating as a Service Account using OpenID Connect.",
211211
"region": "Region will be used as the default location for regional services. Not all services require a region, some are global",
@@ -550,11 +550,11 @@ func (p *Provider) Configure(ctx context.Context, req provider.ConfigureRequest,
550550
}
551551

552552
// Workload Identity Federation via provided OIDC Token from GitHub Actions
553-
if sdkConfig.ServiceAccountFederatedTokenFunc == nil && getEnvBoolIfValueAbsent(providerConfig.UseOIDC, "STACKIT_USE_OIDC") {
553+
if sdkConfig.ServiceAccountFederatedTokenFunc == nil && utils.GetEnvBoolIfValueAbsent(providerConfig.UseOIDC, "STACKIT_USE_OIDC") {
554554
sdkConfig.WorkloadIdentityFederation = true
555555
// https://docs.github.com/en/actions/reference/security/oidc#methods-for-requesting-the-oidc-token
556-
oidcReqURL := getEnvStringOrDefault(providerConfig.OIDCTokenRequestURL, "ACTIONS_ID_TOKEN_REQUEST_URL", "")
557-
oidcReqToken := getEnvStringOrDefault(providerConfig.OIDCTokenRequestToken, "ACTIONS_ID_TOKEN_REQUEST_TOKEN", "")
556+
oidcReqURL := utils.GetEnvStringOrDefault(providerConfig.OIDCTokenRequestURL, "ACTIONS_ID_TOKEN_REQUEST_URL", "")
557+
oidcReqToken := utils.GetEnvStringOrDefault(providerConfig.OIDCTokenRequestToken, "ACTIONS_ID_TOKEN_REQUEST_TOKEN", "")
558558
if oidcReqURL != "" && oidcReqToken != "" {
559559
sdkConfig.ServiceAccountFederatedTokenFunc = oidcadapters.RequestGHOIDCToken(oidcReqURL, oidcReqToken)
560560
}
@@ -759,34 +759,3 @@ func (p *Provider) EphemeralResources(_ context.Context) []func() ephemeral.Ephe
759759
access_token.NewAccessTokenEphemeralResource,
760760
}
761761
}
762-
763-
// getEnvStringOrDefault takes a Framework StringValue and a corresponding Environment Variable name and returns
764-
// either the string value set in the StringValue if not Null / Unknown _or_ the os.GetEnv() value of the Environment
765-
// Variable provided. If both of these are empty, an empty string defaultValue is returned.
766-
func getEnvStringOrDefault(val types.String, envVar, defaultValue string) string {
767-
if val.IsNull() || val.IsUnknown() {
768-
if v := os.Getenv(envVar); v != "" {
769-
return os.Getenv(envVar)
770-
}
771-
return defaultValue
772-
}
773-
774-
return val.ValueString()
775-
}
776-
777-
// getEnvBoolIfValueAbsent takes a Framework BoolValue and a corresponding Environment Variable name and returns
778-
// one of the following in priority order:
779-
// 1 - the Boolean value set in the BoolValue if this is not Null / Unknown.
780-
// 2 - the boolean representation of the os.GetEnv() value of the Environment Variable provided (where anything but
781-
// 'true' or '1' is 'false').
782-
// 3 - `false` in all other cases.
783-
func getEnvBoolIfValueAbsent(val types.Bool, envVar string) bool {
784-
if val.IsNull() || val.IsUnknown() {
785-
v := os.Getenv(envVar)
786-
if strings.EqualFold(v, "true") || strings.EqualFold(v, "1") {
787-
return true
788-
}
789-
}
790-
791-
return val.ValueBool()
792-
}

0 commit comments

Comments
 (0)