Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package configobservercontroller

import (
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/client-go/tools/cache"

configinformers "github.com/openshift/client-go/config/informers/externalversions"
Expand All @@ -24,6 +25,7 @@ func NewConfigObserver(
kubeInformersForNamespaces v1helpers.KubeInformersForNamespaces,
configInformer configinformers.SharedInformerFactory,
resourceSyncer resourcesynccontroller.ResourceSyncer,
enabledClusterCapabilities sets.String,
eventRecorder events.Recorder,
) factory.Controller {
interestingNamespaces := []string{
Expand All @@ -35,19 +37,19 @@ func NewConfigObserver(
preRunCacheSynced := []cache.InformerSynced{
operatorClient.Informer().HasSynced,
configInformer.Config().V1().APIServers().Informer().HasSynced,
configInformer.Config().V1().Consoles().Informer().HasSynced,
configInformer.Config().V1().Infrastructures().Informer().HasSynced,
configInformer.Config().V1().OAuths().Informer().HasSynced,
configInformer.Config().V1().Ingresses().Informer().HasSynced,
configInformer.Config().V1().ClusterVersions().Informer().HasSynced,
}

informers := []factory.Informer{
operatorClient.Informer(),
configInformer.Config().V1().APIServers().Informer(),
configInformer.Config().V1().Consoles().Informer(),
configInformer.Config().V1().Infrastructures().Informer(),
configInformer.Config().V1().OAuths().Informer(),
configInformer.Config().V1().Ingresses().Informer(),
configInformer.Config().V1().ClusterVersions().Informer(),
}

for _, ns := range interestingNamespaces {
Expand Down Expand Up @@ -79,21 +81,30 @@ func NewConfigObserver(
configobserver.WithPrefix(o, configobservation.OAuthServerConfigPrefix))
}

listers := configobservation.Listers{
ConfigMapLister: kubeInformersForNamespaces.ConfigMapLister(),
SecretsLister: kubeInformersForNamespaces.SecretLister(),
IngressLister: configInformer.Config().V1().Ingresses().Lister(),

APIServerLister_: configInformer.Config().V1().APIServers().Lister(),
ClusterVersionLister: configInformer.Config().V1().ClusterVersions().Lister(),
InfrastructureLister: configInformer.Config().V1().Infrastructures().Lister(),
OAuthLister_: configInformer.Config().V1().OAuths().Lister(),
ResourceSync: resourceSyncer,
PreRunCachesSynced: preRunCacheSynced,
}

// Check if the Console capability is enabled on the cluster and sync and add its informer and lister.
if enabledClusterCapabilities.Has("Console") {
preRunCacheSynced = append(preRunCacheSynced, configInformer.Config().V1().Consoles().Informer().HasSynced)
informers = append(informers, configInformer.Config().V1().Consoles().Informer())
listers.ConsoleLister = configInformer.Config().V1().Consoles().Lister()
}

return configobserver.NewNestedConfigObserver(
operatorClient,
eventRecorder,
configobservation.Listers{
ConfigMapLister: kubeInformersForNamespaces.ConfigMapLister(),
SecretsLister: kubeInformersForNamespaces.SecretLister(),
IngressLister: configInformer.Config().V1().Ingresses().Lister(),

APIServerLister_: configInformer.Config().V1().APIServers().Lister(),
ConsoleLister: configInformer.Config().V1().Consoles().Lister(),
InfrastructureLister: configInformer.Config().V1().Infrastructures().Lister(),
OAuthLister_: configInformer.Config().V1().OAuths().Lister(),
ResourceSync: resourceSyncer,
PreRunCachesSynced: preRunCacheSynced,
},
listers,
informers,
[]string{configobservation.OAuthServerConfigPrefix},
"OAuthServer",
Expand Down
20 changes: 19 additions & 1 deletion pkg/controllers/configobservation/console/observe_consoleurl.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"github.com/openshift/library-go/pkg/operator/configobserver"
"github.com/openshift/library-go/pkg/operator/events"

configv1 "github.com/openshift/api/config/v1"

"github.com/openshift/cluster-authentication-operator/pkg/controllers/configobservation"
)

Expand All @@ -20,12 +22,28 @@ func ObserveConsoleURL(genericlisters configobserver.Listers, recorder events.Re
listers := genericlisters.(configobservation.Listers)
errs := []error{}

consoleConfig, err := listers.ConsoleLister.Get("cluster")
clusterVersionConfig, err := listers.ClusterVersionLister.Get("version")
if err != nil {
return existingConfig, append(errs, err)
}

isConsoleCapabilityEnabled := false
for _, capability := range clusterVersionConfig.Status.Capabilities.EnabledCapabilities {
if capability == configv1.ClusterVersionCapabilityConsole {
isConsoleCapabilityEnabled = true
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: you could break here, but 🤷, iterating through the remainder of the list isn't expensive.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there's a break in the next line now, so this thread can be marked resolved.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't the execution be stopped with a hardcoded value once the capability is not allowed?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it should 👍

break
}
}
if !isConsoleCapabilityEnabled {
return existingConfig, nil
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wrap this whole piece with the condition block and remove the capability special case from the error handling

consoleConfig, err := listers.ConsoleLister.Get("cluster")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nil exception panic here in case the capability is not enabled

if err != nil {
return existingConfig, append(errs, err)
}
observedAssetURL := consoleConfig.Status.ConsoleURL

if _, err := url.Parse(observedAssetURL); err != nil { // should never happen
return existingConfig, append(errs, fmt.Errorf("failed to parse consoleURL %q: %w", observedAssetURL, err))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,38 +17,58 @@ import (

func TestObserveConsoleURL(t *testing.T) {
existingConfig := configWithConsoleURL("https://teh.console.my")
noConfig := map[string]interface{}(nil)

tests := []struct {
name string
consoleConfig *configv1.ConsoleStatus
clusterVersion *configv1.ClusterVersionStatus
existingConfig map[string]interface{}
expectedConfig map[string]interface{}
expectedErrs []string
expectedUpdateEvent bool
}{
{
name: "NoConsoleConfig",
name: "NoConsoleConfigConsoleCapabilityEnabled",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what happens when the capability is not enabled but someone creates the CRD and the object?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then I think the cluster would be in an inconsistent state since the the CRD would be there but quite a lot of other resrouces would be missing.
Also is that even a valid scenario? meaning that only a cluster admin can create CRDs, not mentioning that he would/should be the one that decides which set of capabilities should be enabled when provisioning the cluster.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also is that even a valid scenario?

Unless you can prevent it, yes.

Add a unit test, please.

cluster admin can create CRDs

Cluster admin and likely anyone installing an operator.

consoleConfig: nil,
clusterVersion: &configv1.ClusterVersionStatus{Capabilities: configv1.ClusterVersionCapabilitiesStatus{EnabledCapabilities: []configv1.ClusterVersionCapability{configv1.ClusterVersionCapabilityConsole}}},
existingConfig: existingConfig,
expectedConfig: existingConfig,
expectedErrs: []string{"\"cluster\" not found"},
},
{
name: "NoConsoleConfigConsoleCapabilityDisabled",
consoleConfig: nil,
clusterVersion: &configv1.ClusterVersionStatus{Capabilities: configv1.ClusterVersionCapabilitiesStatus{EnabledCapabilities: []configv1.ClusterVersionCapability{}}},
existingConfig: noConfig,
expectedConfig: noConfig,
},
{
name: "ConsoleConfigConsoleCapabilityDisabled",
consoleConfig: &configv1.ConsoleStatus{ConsoleURL: "https://teh.console.my"},
clusterVersion: &configv1.ClusterVersionStatus{Capabilities: configv1.ClusterVersionCapabilitiesStatus{EnabledCapabilities: []configv1.ClusterVersionCapability{}}},
existingConfig: configWithConsoleURL(""),
expectedConfig: configWithConsoleURL(""),
},
{
name: "SameConfig",
consoleConfig: &configv1.ConsoleStatus{ConsoleURL: "https://teh.console.my"},
clusterVersion: &configv1.ClusterVersionStatus{Capabilities: configv1.ClusterVersionCapabilitiesStatus{EnabledCapabilities: []configv1.ClusterVersionCapability{configv1.ClusterVersionCapabilityConsole}}},
existingConfig: existingConfig,
expectedConfig: existingConfig,
},
{
name: "UpdatedConsoleConfig",
consoleConfig: &configv1.ConsoleStatus{ConsoleURL: "https://my-new.console.url"},
clusterVersion: &configv1.ClusterVersionStatus{Capabilities: configv1.ClusterVersionCapabilitiesStatus{EnabledCapabilities: []configv1.ClusterVersionCapability{configv1.ClusterVersionCapabilityConsole}}},
existingConfig: existingConfig,
expectedConfig: configWithConsoleURL("https://my-new.console.url"),
expectedUpdateEvent: true,
},
{
name: "UnparsableConsoleURL",
consoleConfig: &configv1.ConsoleStatus{ConsoleURL: "https://my-new.console.url:port"},
clusterVersion: &configv1.ClusterVersionStatus{Capabilities: configv1.ClusterVersionCapabilitiesStatus{EnabledCapabilities: []configv1.ClusterVersionCapability{configv1.ClusterVersionCapabilityConsole}}},
existingConfig: existingConfig,
expectedConfig: existingConfig,
expectedErrs: []string{
Expand All @@ -58,9 +78,9 @@ func TestObserveConsoleURL(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{})
consoleIndexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{})
if tt.consoleConfig != nil {
if err := indexer.Add(&configv1.Console{
if err := consoleIndexer.Add(&configv1.Console{
ObjectMeta: metav1.ObjectMeta{
Name: "cluster",
},
Expand All @@ -69,8 +89,20 @@ func TestObserveConsoleURL(t *testing.T) {
t.Fatal(err)
}
}
clusterVersionIndexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{})
if tt.clusterVersion != nil {
if err := clusterVersionIndexer.Add(&configv1.ClusterVersion{
ObjectMeta: metav1.ObjectMeta{
Name: "version",
},
Status: *tt.clusterVersion,
}); err != nil {
t.Fatal(err)
}
}
listers := configobservation.Listers{
ConsoleLister: configlistersv1.NewConsoleLister(indexer),
ConsoleLister: configlistersv1.NewConsoleLister(consoleIndexer),
ClusterVersionLister: configlistersv1.NewClusterVersionLister(clusterVersionIndexer),
}

eventRecorder := events.NewInMemoryRecorder(tt.name)
Expand Down
1 change: 1 addition & 0 deletions pkg/controllers/configobservation/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type Listers struct {

APIServerLister_ configlistersv1.APIServerLister
ConsoleLister configlistersv1.ConsoleLister
ClusterVersionLister configlistersv1.ClusterVersionLister
InfrastructureLister configlistersv1.InfrastructureLister
OAuthLister_ configlistersv1.OAuthLister
IngressLister configlistersv1.IngressLister
Expand Down
33 changes: 31 additions & 2 deletions pkg/operator/starter.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@ import (
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/selection"
"k8s.io/apimachinery/pkg/util/sets"
certinformers "k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/cache"
"k8s.io/klog/v2"
apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1"
apiregistrationclient "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset"
Expand Down Expand Up @@ -174,9 +176,22 @@ func RunOperator(ctx context.Context, controllerContext *controllercmd.Controlle
operatorCtx.operatorInformer = operatorConfigInformers
operatorCtx.operatorConfigInformer = configinformer.NewSharedInformerFactoryWithOptions(configClient, resync)

if err := prepareOauthOperator(controllerContext, operatorCtx); err != nil {
// this one needs to be started now, so prepareOauthOperator can get ClusterVersion
// operatorCtx.operatorConfigInformer.Start(ctx.Done())
// allSynced := operatorCtx.operatorConfigInformer.WaitForCacheSync(ctx.Done())
// for t, synced := range allSynced {
// if !synced {
// return fmt.Errorf("informer cache not synchronized for %v", t)
// }
// }

if err := prepareOauthOperator(ctx, controllerContext, operatorCtx); err != nil {
return err
}

// if err := prepareOauthOperator(controllerContext, operatorCtx); err != nil {
// return err
// }
if err := prepareOauthAPIServerOperator(ctx, controllerContext, operatorCtx); err != nil {
return err
}
Expand All @@ -194,6 +209,7 @@ func RunOperator(ctx context.Context, controllerContext *controllercmd.Controlle
for _, informerToRunFn := range operatorCtx.informersToRunFunc {
informerToRunFn(ctx.Done())
}

for _, controllerRunFn := range operatorCtx.controllersToRunFunc {
go controllerRunFn(ctx, 1)
}
Expand All @@ -202,7 +218,7 @@ func RunOperator(ctx context.Context, controllerContext *controllercmd.Controlle
return nil
}

func prepareOauthOperator(controllerContext *controllercmd.ControllerContext, operatorCtx *operatorContext) error {
func prepareOauthOperator(ctx context.Context, controllerContext *controllercmd.ControllerContext, operatorCtx *operatorContext) error {
routeClient, err := routeclient.NewForConfig(controllerContext.ProtoKubeConfig)
if err != nil {
return err
Expand All @@ -224,6 +240,18 @@ func prepareOauthOperator(controllerContext *controllercmd.ControllerContext, op

oauthInformers := oauthinformers.NewSharedInformerFactory(oauthClient, resync)

clusterVersionInformer := operatorCtx.operatorConfigInformer.Config().V1().ClusterVersions()
operatorCtx.operatorConfigInformer.Start(ctx.Done())
if !cache.WaitForNamedCacheSync(configv1.GroupName, ctx.Done(), clusterVersionInformer.Informer().HasSynced) {
return fmt.Errorf("failed to synchronize cluster version informer")
}

clusterVersionConfig, err := clusterVersionInformer.Lister().Get("version")
if err != nil {
return err
}
enabledClusterCapabilities := sets.StringKeySet(clusterVersionConfig.Status.Capabilities.EnabledCapabilities)

// add syncing for the OAuth metadata ConfigMap
if err := operatorCtx.resourceSyncController.SyncConfigMap(
resourcesynccontroller.ResourceLocation{Namespace: "openshift-config-managed", Name: "oauth-openshift"},
Expand Down Expand Up @@ -281,6 +309,7 @@ func prepareOauthOperator(controllerContext *controllercmd.ControllerContext, op
operatorCtx.kubeInformersForNamespaces,
operatorCtx.operatorConfigInformer,
operatorCtx.resourceSyncController,
enabledClusterCapabilities,
controllerContext.EventRecorder,
)

Expand Down