Skip to content

Commit 3de94aa

Browse files
fix(ske): refine cluster config precedence
1 parent 0cce847 commit 3de94aa

3 files changed

Lines changed: 124 additions & 12 deletions

File tree

docs/stackit_ske_kubeconfig_login.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,15 @@ Login plugin for kubernetes clients, that creates short-lived credentials to aut
88
First you need to obtain a kubeconfig for use with the login command (first or second example).
99
Secondly you use the kubeconfig with your chosen Kubernetes client (third example), the client will automatically retrieve the credentials via the STACKIT CLI.
1010

11+
Project ID and region are resolved with the following precedence:
12+
13+
| Explicit flag | Global config | Exec cluster config | Result |
14+
| --- | --- | --- | --- |
15+
| Not set | Set | Not set | Global config |
16+
| Set | Set | Not set | Explicit flag |
17+
| Not set | Set | Set | Exec cluster config |
18+
| Set | Set | Set | Explicit flag |
19+
1120
```
1221
stackit ske kubeconfig login [flags]
1322
```

internal/cmd/ske/kubeconfig/login/login.go

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,14 @@ func NewCmd(params *types.CmdParams) *cobra.Command {
5959
Long: fmt.Sprintf("%s\n%s\n%s",
6060
"Login plugin for kubernetes clients, that creates short-lived credentials to authenticate against a STACKIT Kubernetes Engine (SKE) cluster.",
6161
"First you need to obtain a kubeconfig for use with the login command (first or second example).",
62-
"Secondly you use the kubeconfig with your chosen Kubernetes client (third example), the client will automatically retrieve the credentials via the STACKIT CLI.",
62+
"Secondly you use the kubeconfig with your chosen Kubernetes client (third example), the client will automatically retrieve the credentials via the STACKIT CLI.\n\n"+
63+
"Project ID and region are resolved with the following precedence:\n\n"+
64+
"| Explicit flag | Global config | Exec cluster config | Result |\n"+
65+
"| --- | --- | --- | --- |\n"+
66+
"| Not set | Set | Not set | Global config |\n"+
67+
"| Set | Set | Not set | Explicit flag |\n"+
68+
"| Not set | Set | Set | Exec cluster config |\n"+
69+
"| Set | Set | Set | Explicit flag |",
6370
),
6471
Args: args.NoArgs,
6572
Example: examples.Build(
@@ -195,10 +202,10 @@ func parseClusterConfig(p *print.Printer, cmd *cobra.Command, idpMode, workloadI
195202
clusterConfig.OrganizationID = organizationID
196203
}
197204
globalFlags := globalflags.Parse(p, cmd)
198-
if globalFlags.ProjectId != "" {
205+
if flagWasExplicitlySet(cmd, globalflags.ProjectIdFlag) || clusterConfig.STACKITProjectID == "" {
199206
clusterConfig.STACKITProjectID = globalFlags.ProjectId
200207
}
201-
if globalFlags.Region != "" {
208+
if flagWasExplicitlySet(cmd, globalflags.RegionFlag) || clusterConfig.Region == "" {
202209
clusterConfig.Region = globalFlags.Region
203210
}
204211

@@ -229,6 +236,11 @@ func parseClusterConfig(p *print.Printer, cmd *cobra.Command, idpMode, workloadI
229236
return clusterConfig, nil
230237
}
231238

239+
func flagWasExplicitlySet(cmd *cobra.Command, name string) bool {
240+
flag := cmd.Flag(name)
241+
return flag != nil && flag.Changed
242+
}
243+
232244
func loadExecCredentialFromEnv() (runtime.Object, error) {
233245
execInfo := os.Getenv("KUBERNETES_EXEC_INFO")
234246
if execInfo == "" {

internal/cmd/ske/kubeconfig/login/login_test.go

Lines changed: 100 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626

2727
"github.com/stackitcloud/stackit-cli/internal/pkg/auth"
2828
"github.com/stackitcloud/stackit-cli/internal/pkg/config"
29+
"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
2930
"github.com/stackitcloud/stackit-cli/internal/pkg/testparams"
3031
"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
3132
)
@@ -112,15 +113,104 @@ func TestParseClusterConfigWithoutExecClusterInfo(t *testing.T) {
112113
}
113114
}
114115

115-
func TestParseClusterConfigPrefersExplicitConfiguration(t *testing.T) {
116+
func TestParseClusterConfigPrecedence(t *testing.T) {
117+
globalProjectID := uuid.NewString()
118+
flagProjectID := uuid.NewString()
119+
globalRegion := "global-region"
120+
flagRegion := "flag-region"
121+
122+
tests := []struct {
123+
name string
124+
clusterConfig bool
125+
explicitFlags bool
126+
expectedID string
127+
expectedRegion string
128+
}{
129+
{
130+
name: "global config without exec cluster config",
131+
expectedID: globalProjectID,
132+
expectedRegion: globalRegion,
133+
},
134+
{
135+
name: "explicit flags without exec cluster config",
136+
explicitFlags: true,
137+
expectedID: flagProjectID,
138+
expectedRegion: flagRegion,
139+
},
140+
{
141+
name: "exec cluster config overrides global config",
142+
clusterConfig: true,
143+
expectedID: testProjectId,
144+
expectedRegion: testRegion,
145+
},
146+
{
147+
name: "explicit flags override exec cluster config",
148+
clusterConfig: true,
149+
explicitFlags: true,
150+
expectedID: flagProjectID,
151+
expectedRegion: flagRegion,
152+
},
153+
}
154+
155+
for _, tt := range tests {
156+
t.Run(tt.name, func(t *testing.T) {
157+
viper.Reset()
158+
t.Cleanup(viper.Reset)
159+
t.Setenv(envServiceAccountEmail, "workload@sa.stackit.cloud")
160+
161+
globalConfig := fmt.Sprintf(`{"project_id":%q,"region":%q}`, globalProjectID, globalRegion)
162+
viper.SetConfigType("json")
163+
if err := viper.ReadConfig(strings.NewReader(globalConfig)); err != nil {
164+
t.Fatalf("read global config: %v", err)
165+
}
166+
167+
execConfig := fixtureClusterConfig()
168+
if !tt.clusterConfig {
169+
execConfig.STACKITProjectID = ""
170+
execConfig.Region = ""
171+
}
172+
configJSON, err := json.Marshal(execConfig)
173+
if err != nil {
174+
t.Fatalf("marshal cluster config: %v", err)
175+
}
176+
setExecCredentialEnv(t, &clientauthenticationv1.Cluster{
177+
Server: "https://api.example.stackit.cloud",
178+
Config: runtime.RawExtension{Raw: configJSON},
179+
})
180+
181+
params := testparams.NewTestParams()
182+
cmd := &cobra.Command{}
183+
if err := globalflags.Configure(cmd.Flags()); err != nil {
184+
t.Fatalf("configure global flags: %v", err)
185+
}
186+
configureFlags(cmd)
187+
if tt.explicitFlags {
188+
if err := cmd.Flags().Set(globalflags.ProjectIdFlag, flagProjectID); err != nil {
189+
t.Fatalf("set project ID flag: %v", err)
190+
}
191+
if err := cmd.Flags().Set(globalflags.RegionFlag, flagRegion); err != nil {
192+
t.Fatalf("set region flag: %v", err)
193+
}
194+
}
195+
196+
actual, err := parseClusterConfig(params.Printer, cmd, true, true, true)
197+
if err != nil {
198+
t.Fatalf("parse cluster config: %v", err)
199+
}
200+
expected := fixtureClusterConfig(func(config *clusterConfig) {
201+
config.STACKITProjectID = tt.expectedID
202+
config.Region = tt.expectedRegion
203+
})
204+
if diff := cmp.Diff(actual, expected, cmpopts.IgnoreFields(clusterConfig{}, "cacheKey")); diff != "" {
205+
t.Fatalf("Data does not match: %s", diff)
206+
}
207+
})
208+
}
209+
}
210+
211+
func TestParseClusterConfigPrefersExplicitClusterIdentity(t *testing.T) {
116212
viper.Reset()
117213
t.Cleanup(viper.Reset)
118-
explicitProjectID := uuid.NewString()
119-
explicitRegion := "explicit-region"
120-
explicitClusterName := "explicit-cluster"
121-
explicitOrganizationID := uuid.NewString()
122-
viper.Set(config.ProjectIdKey, explicitProjectID)
123-
viper.Set(config.RegionKey, explicitRegion)
124214
t.Setenv(envServiceAccountEmail, "workload@sa.stackit.cloud")
125215

126216
configJSON, err := json.Marshal(fixtureClusterConfig())
@@ -132,6 +222,8 @@ func TestParseClusterConfigPrefersExplicitConfiguration(t *testing.T) {
132222
Config: runtime.RawExtension{Raw: configJSON},
133223
})
134224

225+
explicitClusterName := "explicit-cluster"
226+
explicitOrganizationID := uuid.NewString()
135227
params := testparams.NewTestParams()
136228
cmd := &cobra.Command{}
137229
configureFlags(cmd)
@@ -141,13 +233,12 @@ func TestParseClusterConfigPrefersExplicitConfiguration(t *testing.T) {
141233
if err := cmd.Flags().Set(organizationFlag, explicitOrganizationID); err != nil {
142234
t.Fatalf("set organization flag: %v", err)
143235
}
236+
144237
actual, err := parseClusterConfig(params.Printer, cmd, true, true, true)
145238
if err != nil {
146239
t.Fatalf("parse cluster config: %v", err)
147240
}
148241
expected := fixtureClusterConfig(func(config *clusterConfig) {
149-
config.STACKITProjectID = explicitProjectID
150-
config.Region = explicitRegion
151242
config.ClusterName = explicitClusterName
152243
config.OrganizationID = explicitOrganizationID
153244
})

0 commit comments

Comments
 (0)