Skip to content

Commit 7ccabc1

Browse files
Enhance kubeconfig to display all OIDC contexts (#1936)
* Enhance kubeconfig to display all OIDC contexts * Start indexing from 2 * Introduce multiple contexts feature flag * Refactor after rebase
1 parent 0b0e073 commit 7ccabc1

File tree

10 files changed

+497
-55
lines changed

10 files changed

+497
-55
lines changed

cmd/broker/main.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,8 @@ type Config struct {
159159
RegionsSupportingMachineFilePath string
160160

161161
HapRuleFilePath string
162+
163+
MultipleContexts bool `envconfig:"default=false"`
162164
}
163165

164166
type ProfilerConfig struct {
@@ -334,7 +336,7 @@ func main() {
334336
fatalOnError(err, log)
335337

336338
// create kubeconfig builder
337-
kcBuilder := kubeconfig.NewBuilder(kcpK8sClient, skrK8sClientProvider)
339+
kcBuilder := kubeconfig.NewBuilder(kcpK8sClient, skrK8sClientProvider, cfg.MultipleContexts)
338340

339341
// create server
340342
router := httputil.NewRouter()
@@ -459,8 +461,8 @@ func createAPI(router *httputil.Router, servicesConfig broker.ServicesConfig, cf
459461
valuesProvider, logs, cfg.KymaDashboardConfig, kcBuilder, convergedCloudRegionProvider, kcpK8sClient, regionsSupportingMachine, cfg.InfrastructureManager.UseSmallerMachineTypes),
460462
GetInstanceEndpoint: broker.NewGetInstance(cfg.Broker, db.Instances(), db.Operations(), kcBuilder, logs),
461463
LastOperationEndpoint: broker.NewLastOperation(db.Operations(), db.InstancesArchived(), logs),
462-
BindEndpoint: broker.NewBind(cfg.Broker.Binding, db, logs, clientProvider, kubeconfigProvider, publisher),
463-
UnbindEndpoint: broker.NewUnbind(logs, db, brokerBindings.NewServiceAccountBindingsManager(clientProvider, kubeconfigProvider), publisher),
464+
BindEndpoint: broker.NewBind(cfg.Broker.Binding, db, logs, clientProvider, kubeconfigProvider, publisher, cfg.MultipleContexts),
465+
UnbindEndpoint: broker.NewUnbind(logs, db, brokerBindings.NewServiceAccountBindingsManager(clientProvider, kubeconfigProvider, cfg.MultipleContexts), publisher),
464466
GetBindingEndpoint: broker.NewGetBinding(logs, db),
465467
LastBindingOperationEndpoint: broker.NewLastBindingOperation(logs),
466468
}

internal/broker/bind_create.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,14 +70,14 @@ type Credentials struct {
7070
}
7171

7272
func NewBind(cfg BindingConfig, db storage.BrokerStorage, log *slog.Logger, clientProvider broker.ClientProvider, kubeconfigProvider broker.KubeconfigProvider,
73-
publisher event.Publisher) *BindEndpoint {
73+
publisher event.Publisher, multipleContexts bool) *BindEndpoint {
7474
return &BindEndpoint{config: cfg,
7575
instancesStorage: db.Instances(),
7676
bindingsStorage: db.Bindings(),
7777
publisher: publisher,
7878
operationsStorage: db.Operations(),
7979
log: log.With("service", "BindEndpoint"),
80-
serviceAccountBindingManager: broker.NewServiceAccountBindingsManager(clientProvider, kubeconfigProvider),
80+
serviceAccountBindingManager: broker.NewServiceAccountBindingsManager(clientProvider, kubeconfigProvider, multipleContexts),
8181
}
8282
}
8383

internal/broker/bind_create_test.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ func TestCreateBindingEndpoint_dbInsertionInCaseOfError(t *testing.T) {
9393
publisher := event.NewPubSub(log)
9494

9595
//// api handler
96-
bindEndpoint := NewBind(*bindingCfg, db, fixLogger(), &dummyProvider{}, &dummyProvider{}, publisher)
96+
bindEndpoint := NewBind(*bindingCfg, db, fixLogger(), &dummyProvider{}, &dummyProvider{}, publisher, false)
9797

9898
// test relies on checking if got nil on kubeconfig dummyProvider but the instance got inserted either way
9999
t.Run("should INSERT binding despite error on k8s api call", func(t *testing.T) {
@@ -343,7 +343,7 @@ func TestCreateSecondBindingWithTheSameIdButDifferentParams(t *testing.T) {
343343

344344
publisher := event.NewPubSub(log)
345345

346-
svc := NewBind(*bindingCfg, brokerStorage, fixLogger(), nil, nil, publisher)
346+
svc := NewBind(*bindingCfg, brokerStorage, fixLogger(), nil, nil, publisher, false)
347347
params := BindingParams{
348348
ExpirationSeconds: 601,
349349
}
@@ -394,7 +394,7 @@ func TestCreateSecondBindingWithTheSameIdAndParams(t *testing.T) {
394394

395395
publisher := event.NewPubSub(log)
396396

397-
svc := NewBind(*bindingCfg, brokerStorage, fixLogger(), nil, nil, publisher)
397+
svc := NewBind(*bindingCfg, brokerStorage, fixLogger(), nil, nil, publisher, false)
398398
params := BindingParams{
399399
ExpirationSeconds: 600,
400400
}
@@ -446,7 +446,7 @@ func TestCreateSecondBindingWithTheSameIdAndParamsForExpired(t *testing.T) {
446446
// event publisher
447447
publisher := event.NewPubSub(log)
448448

449-
svc := NewBind(*bindingCfg, brokerStorage, fixLogger(), nil, nil, publisher)
449+
svc := NewBind(*bindingCfg, brokerStorage, fixLogger(), nil, nil, publisher, false)
450450
params := BindingParams{
451451
ExpirationSeconds: 600,
452452
}
@@ -500,7 +500,7 @@ func TestCreateSecondBindingWithTheSameIdAndParamsForBindingInProgress(t *testin
500500
// event publisher
501501
publisher := event.NewPubSub(log)
502502

503-
svc := NewBind(*bindingCfg, brokerStorage, fixLogger(), nil, nil, publisher)
503+
svc := NewBind(*bindingCfg, brokerStorage, fixLogger(), nil, nil, publisher, false)
504504
params := BindingParams{
505505
ExpirationSeconds: 600,
506506
}
@@ -551,7 +551,7 @@ func TestCreateSecondBindingWithTheSameIdAndParamsNotExplicitlyDefined(t *testin
551551

552552
publisher := event.NewPubSub(log)
553553

554-
svc := NewBind(*bindingCfg, brokerStorage, fixLogger(), nil, nil, publisher)
554+
svc := NewBind(*bindingCfg, brokerStorage, fixLogger(), nil, nil, publisher, false)
555555

556556
// when
557557
resp, err := svc.Bind(context.Background(), instanceID, bindingID, domain.BindDetails{}, false)
@@ -577,5 +577,5 @@ func prepareBindingEndpoint(t *testing.T, cfg BindingConfig) (*BindEndpoint, sto
577577
err = db.Operations().InsertOperation(operation)
578578
require.NoError(t, err)
579579

580-
return NewBind(cfg, db, log, k8sClientProvider, k8sClientProvider, event.NewPubSub(log)), db
580+
return NewBind(cfg, db, log, k8sClientProvider, k8sClientProvider, event.NewPubSub(log), false), db
581581
}

internal/broker/bind_envtest_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,8 @@ func TestCreateBinding(t *testing.T) {
105105
Level: slog.LevelDebug,
106106
}))
107107
publisher := event.NewPubSub(log)
108-
svc := NewBind(bindingCfg, db, log, skrK8sClientProvider, skrK8sClientProvider, publisher)
109-
unbindSvc := NewUnbind(log, db, brokerBindings.NewServiceAccountBindingsManager(skrK8sClientProvider, skrK8sClientProvider), publisher)
108+
svc := NewBind(bindingCfg, db, log, skrK8sClientProvider, skrK8sClientProvider, publisher, false)
109+
unbindSvc := NewUnbind(log, db, brokerBindings.NewServiceAccountBindingsManager(skrK8sClientProvider, skrK8sClientProvider, false), publisher)
110110

111111
t.Run("should create a new service binding without error", func(t *testing.T) {
112112
// When

internal/broker/bindings/bindings_manager.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,10 @@ type ServiceAccountBindingsManager struct {
4444
kubeconfigBuilder *kubeconfig.Builder
4545
}
4646

47-
func NewServiceAccountBindingsManager(clientProvider ClientProvider, kubeconfigProvider KubeconfigProvider) *ServiceAccountBindingsManager {
47+
func NewServiceAccountBindingsManager(clientProvider ClientProvider, kubeconfigProvider KubeconfigProvider, multipleContexts bool) *ServiceAccountBindingsManager {
4848
return &ServiceAccountBindingsManager{
4949
clientProvider: clientProvider,
50-
kubeconfigBuilder: kubeconfig.NewBuilder(nil, kubeconfigProvider),
50+
kubeconfigBuilder: kubeconfig.NewBuilder(nil, kubeconfigProvider, multipleContexts),
5151
}
5252
}
5353

internal/kubeconfig/builder.go

Lines changed: 50 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -19,26 +19,33 @@ type Config struct {
1919
type Builder struct {
2020
kubeconfigProvider kubeconfigProvider
2121
kcpClient client.Client
22+
multipleContexts bool
2223
}
2324

2425
type kubeconfigProvider interface {
2526
KubeconfigForRuntimeID(runtimeID string) ([]byte, error)
2627
}
2728

28-
func NewBuilder(kcpClient client.Client, provider kubeconfigProvider) *Builder {
29+
func NewBuilder(kcpClient client.Client, provider kubeconfigProvider, multipleContexts bool) *Builder {
2930
return &Builder{
3031
kcpClient: kcpClient,
3132
kubeconfigProvider: provider,
33+
multipleContexts: multipleContexts,
3234
}
3335
}
3436

3537
type kubeconfigData struct {
36-
ContextName string
37-
CAData string
38-
ServerURL string
39-
OIDCIssuerURL string
40-
OIDCClientID string
41-
Token string
38+
ContextName string
39+
CAData string
40+
ServerURL string
41+
OIDCConfigs []OIDCConfig
42+
Token string
43+
}
44+
45+
type OIDCConfig struct {
46+
Name string
47+
IssuerURL string
48+
ClientID string
4249
}
4350

4451
func (b *Builder) BuildFromAdminKubeconfigForBinding(runtimeID string, token string) (string, error) {
@@ -64,12 +71,9 @@ func (b *Builder) BuildFromAdminKubeconfig(instance *internal.Instance, adminKub
6471
if instance.RuntimeID == "" {
6572
return "", fmt.Errorf("RuntimeID must not be empty")
6673
}
67-
issuerURL, clientID, err := b.getOidcDataFromRuntimeResource(instance.RuntimeID)
68-
if err != nil {
69-
return "", fmt.Errorf("while fetching oidc data: %w", err)
70-
}
7174

7275
var kubeconfigContent []byte
76+
var err error
7377
if adminKubeconfig == "" {
7478
kubeconfigContent, err = b.kubeconfigProvider.KubeconfigForRuntimeID(instance.RuntimeID)
7579
if err != nil {
@@ -84,12 +88,16 @@ func (b *Builder) BuildFromAdminKubeconfig(instance *internal.Instance, adminKub
8488
return "", fmt.Errorf("during unmarshal invocation: %w", err)
8589
}
8690

91+
OIDCConfigs, err := b.getOidcDataFromRuntimeResource(instance.RuntimeID, kubeCfg.CurrentContext)
92+
if err != nil {
93+
return "", fmt.Errorf("while fetching oidc data: %w", err)
94+
}
95+
8796
return b.parseTemplate(kubeconfigData{
88-
ContextName: kubeCfg.CurrentContext,
89-
CAData: kubeCfg.Clusters[0].Cluster.CertificateAuthorityData,
90-
ServerURL: kubeCfg.Clusters[0].Cluster.Server,
91-
OIDCIssuerURL: issuerURL,
92-
OIDCClientID: clientID,
97+
ContextName: kubeCfg.CurrentContext,
98+
CAData: kubeCfg.Clusters[0].Cluster.CertificateAuthorityData,
99+
ServerURL: kubeCfg.Clusters[0].Cluster.Server,
100+
OIDCConfigs: OIDCConfigs,
93101
}, kubeconfigTemplate)
94102
}
95103

@@ -158,25 +166,36 @@ func (b *Builder) validKubeconfig(kc kubeconfig) error {
158166
return nil
159167
}
160168

161-
func (b *Builder) getOidcDataFromRuntimeResource(id string) (string, string, error) {
169+
func (b *Builder) getOidcDataFromRuntimeResource(id string, currentContext string) ([]OIDCConfig, error) {
162170
var runtime imv1.Runtime
171+
var oidcConfigs []OIDCConfig
163172
err := b.kcpClient.Get(context.Background(), client.ObjectKey{Name: id, Namespace: kcpNamespace}, &runtime)
164173
if err != nil {
165-
return "", "", err
174+
return nil, err
166175
}
167-
168-
oidcConfig := runtime.Spec.Shoot.Kubernetes.KubeAPIServer.AdditionalOidcConfig
169-
if oidcConfig == nil || len(*oidcConfig) == 0 {
170-
return "", "", fmt.Errorf("runtime resource contains no OIDC config")
171-
}
172-
173-
config := (*oidcConfig)[0]
174-
if config.IssuerURL == nil || *config.IssuerURL == "" {
175-
return "", "", fmt.Errorf("runtime resource contains an empty OIDC issuer URL")
176+
additionalConfigs := runtime.Spec.Shoot.Kubernetes.KubeAPIServer.AdditionalOidcConfig
177+
if additionalConfigs == nil {
178+
return nil, fmt.Errorf("Runtime Resource contains no additional OIDC config")
176179
}
177-
if config.ClientID == nil || *config.ClientID == "" {
178-
return "", "", fmt.Errorf("runtime resource contains an empty OIDC client ID")
180+
for i, config := range *additionalConfigs {
181+
if config.IssuerURL == nil {
182+
return nil, fmt.Errorf("Runtime Resource contains an empty OIDC issuer URL")
183+
}
184+
if config.ClientID == nil {
185+
return nil, fmt.Errorf("Runtime Resource contains an empty OIDC client ID")
186+
}
187+
name := currentContext
188+
if i > 0 {
189+
name = fmt.Sprintf("%s-%d", currentContext, i+1)
190+
}
191+
oidcConfigs = append(oidcConfigs, OIDCConfig{
192+
Name: name,
193+
IssuerURL: *config.IssuerURL,
194+
ClientID: *config.ClientID,
195+
})
196+
if !b.multipleContexts {
197+
return oidcConfigs, nil
198+
}
179199
}
180-
181-
return *config.IssuerURL, *config.ClientID, nil
200+
return oidcConfigs, nil
182201
}

0 commit comments

Comments
 (0)