Skip to content

Commit

Permalink
[sync]:feat: Operator disable create usergroup if detect user enabled…
Browse files Browse the repository at this point in the history
… external auth

- manual cherry-pick from opendatahub-io#1297

Signed-off-by: Wen Zhou <[email protected]>
(cherry picked from commit 5bed921)
  • Loading branch information
zdtsw authored and VaishnaviHire committed Oct 15, 2024
1 parent 005b9d2 commit e57b802
Show file tree
Hide file tree
Showing 8 changed files with 250 additions and 10 deletions.
14 changes: 11 additions & 3 deletions bundle/manifests/rhods-operator.clusterserviceversion.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ metadata:
categories: AI/Machine Learning, Big Data
certified: "False"
containerImage: quay.io/opendatahub/opendatahub-operator:v2.0.0
createdAt: "2024-07-24T19:29:46Z"
createdAt: "2024-10-15T10:03:01Z"
features.operators.openshift.io/disconnected: "true"
features.operators.openshift.io/fips-compliant: "false"
features.operators.openshift.io/proxy-aware: "false"
Expand Down Expand Up @@ -169,7 +169,7 @@ metadata:
operators.operatorframework.io/builder: operator-sdk-v1.31.0
operators.operatorframework.io/project_layout: go.kubebuilder.io/v3
repository: https://github.com/red-hat-data-services/rhods-operator
name: rhods-operator.v2.12.0
name: rhods-operator.v2.13.1
namespace: placeholder
spec:
apiservicedefinitions: {}
Expand Down Expand Up @@ -518,6 +518,14 @@ spec:
verbs:
- create
- patch
- apiGroups:
- config.openshift.io
resources:
- authentications
verbs:
- get
- list
- watch
- apiGroups:
- config.openshift.io
resources:
Expand Down Expand Up @@ -1778,4 +1786,4 @@ spec:
minKubeVersion: 1.25.0
provider:
name: Red Hat
version: 2.12.0
version: 2.13.1
175 changes: 175 additions & 0 deletions config/crd/external/config.openshift.io_authentications.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.16.1
name: authentications.config.openshift.io
spec:
group: config.openshift.io
names:
kind: Authentication
listKind: AuthenticationList
plural: authentications
singular: authentication
scope: Cluster
versions:
- name: v1
schema:
openAPIV3Schema:
description: |-
Authentication specifies cluster-wide settings for authentication (like OAuth and
webhook token authenticators). The canonical name of an instance is `cluster`.
Compatibility level 1: Stable within a major release for a minimum of 12 months or 3 minor releases (whichever is longer).
properties:
apiVersion:
description: |-
APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
type: string
kind:
description: |-
Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
type: string
metadata:
type: object
spec:
description: spec holds user settable values for configuration
properties:
oauthMetadata:
description: |-
oauthMetadata contains the discovery endpoint data for OAuth 2.0
Authorization Server Metadata for an external OAuth server.
This discovery document can be viewed from its served location:
oc get --raw '/.well-known/oauth-authorization-server'
For further details, see the IETF Draft:
https://tools.ietf.org/html/draft-ietf-oauth-discovery-04#section-2
If oauthMetadata.name is non-empty, this value has precedence
over any metadata reference stored in status.
The key "oauthMetadata" is used to locate the data.
If specified and the config map or expected key is not found, no metadata is served.
If the specified metadata is not valid, no metadata is served.
The namespace for this config map is openshift-config.
properties:
name:
description: name is the metadata.name of the referenced config
map
type: string
required:
- name
type: object
serviceAccountIssuer:
description: |-
serviceAccountIssuer is the identifier of the bound service account token
issuer.
The default is https://kubernetes.default.svc
WARNING: Updating this field will not result in immediate invalidation of all bound tokens with the
previous issuer value. Instead, the tokens issued by previous service account issuer will continue to
be trusted for a time period chosen by the platform (currently set to 24h).
This time period is subject to change over time.
This allows internal components to transition to use new service account issuer without service distruption.
type: string
type:
description: |-
type identifies the cluster managed, user facing authentication mode in use.
Specifically, it manages the component that responds to login attempts.
The default is IntegratedOAuth.
type: string
webhookTokenAuthenticator:
description: |-
webhookTokenAuthenticator configures a remote token reviewer.
These remote authentication webhooks can be used to verify bearer tokens
via the tokenreviews.authentication.k8s.io REST API. This is required to
honor bearer tokens that are provisioned by an external authentication service.
properties:
kubeConfig:
description: |-
kubeConfig references a secret that contains kube config file data which
describes how to access the remote webhook service.
The namespace for the referenced secret is openshift-config.
For further details, see:
https://kubernetes.io/docs/reference/access-authn-authz/authentication/#webhook-token-authentication
The key "kubeConfig" is used to locate the data.
If the secret or expected key is not found, the webhook is not honored.
If the specified kube config data is not valid, the webhook is not honored.
properties:
name:
description: name is the metadata.name of the referenced secret
type: string
required:
- name
type: object
required:
- kubeConfig
type: object
webhookTokenAuthenticators:
description: webhookTokenAuthenticators is DEPRECATED, setting it
has no effect.
items:
description: |-
deprecatedWebhookTokenAuthenticator holds the necessary configuration options for a remote token authenticator.
It's the same as WebhookTokenAuthenticator but it's missing the 'required' validation on KubeConfig field.
properties:
kubeConfig:
description: |-
kubeConfig contains kube config file data which describes how to access the remote webhook service.
For further details, see:
https://kubernetes.io/docs/reference/access-authn-authz/authentication/#webhook-token-authentication
The key "kubeConfig" is used to locate the data.
If the secret or expected key is not found, the webhook is not honored.
If the specified kube config data is not valid, the webhook is not honored.
The namespace for this secret is determined by the point of use.
properties:
name:
description: name is the metadata.name of the referenced
secret
type: string
required:
- name
type: object
type: object
type: array
type: object
status:
description: status holds observed values from the cluster. They may not
be overridden.
properties:
integratedOAuthMetadata:
description: |-
integratedOAuthMetadata contains the discovery endpoint data for OAuth 2.0
Authorization Server Metadata for the in-cluster integrated OAuth server.
This discovery document can be viewed from its served location:
oc get --raw '/.well-known/oauth-authorization-server'
For further details, see the IETF Draft:
https://tools.ietf.org/html/draft-ietf-oauth-discovery-04#section-2
This contains the observed value based on cluster state.
An explicitly set value in spec.oauthMetadata has precedence over this field.
This field has no meaning if authentication spec.type is not set to IntegratedOAuth.
The key "oauthMetadata" is used to locate the data.
If the config map or expected key is not found, no metadata is served.
If the specified metadata is not valid, no metadata is served.
The namespace for this config map is openshift-config-managed.
properties:
name:
description: name is the metadata.name of the referenced config
map
type: string
required:
- name
type: object
type: object
required:
- spec
type: object
served: true
storage: true
8 changes: 8 additions & 0 deletions config/rbac/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,14 @@ rules:
verbs:
- create
- patch
- apiGroups:
- config.openshift.io
resources:
- authentications
verbs:
- get
- list
- watch
- apiGroups:
- config.openshift.io
resources:
Expand Down
28 changes: 22 additions & 6 deletions controllers/dscinitialization/dscinitialization_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
corev1 "k8s.io/api/core/v1"
networkingv1 "k8s.io/api/networking/v1"
rbacv1 "k8s.io/api/rbac/v1"
k8serr "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/tools/record"
Expand Down Expand Up @@ -75,6 +76,7 @@ type DSCInitializationReconciler struct {
// +kubebuilder:rbac:groups="dscinitialization.opendatahub.io",resources=dscinitializations,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups="features.opendatahub.io",resources=featuretrackers,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups="features.opendatahub.io",resources=featuretrackers/status,verbs=get;update;patch;delete
// +kubebuilder:rbac:groups="config.openshift.io",resources=authentications,verbs=get;watch;list

// Reconcile contains controller logic specific to DSCInitialization instance updates.
func (r *DSCInitializationReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { //nolint:funlen,gocyclo,maintidx
Expand Down Expand Up @@ -224,11 +226,20 @@ func (r *DSCInitializationReconciler) Reconcile(ctx context.Context, req ctrl.Re
}
return ctrl.Result{}, nil
default:
createUsergroup, err := cluster.IsDefaultAuthMethod(ctx, r.Client)
if err != nil && !k8serr.IsNotFound(err) { // only keep reconcile if real error but not missing CRD or missing CR
return ctrl.Result{}, err
}
switch platform {
case cluster.SelfManagedRhods:
err := r.createUserGroup(ctx, instance, "rhods-admins")
if err != nil {
return reconcile.Result{}, err
// Check if user opted for disabling creating user groups
if !createUsergroup {
r.Log.Info("DSCI disabled usergroup creation")
} else {
err := r.createUserGroup(ctx, instance, "rhods-admins")
if err != nil {
return reconcile.Result{}, err
}
}
if instance.Spec.Monitoring.ManagementState == operatorv1.Managed {
r.Log.Info("Monitoring enabled, won't apply changes", "cluster", "Self-Managed RHODS Mode")
Expand Down Expand Up @@ -257,9 +268,14 @@ func (r *DSCInitializationReconciler) Reconcile(ctx context.Context, req ctrl.Re
}
}
default:
err := r.createUserGroup(ctx, instance, "odh-admins")
if err != nil {
return reconcile.Result{}, err
// Check if user opted for disabling creating user groups
if !createUsergroup {
r.Log.Info("DSCI disabled usergroup creation")
} else {
err := r.createUserGroup(ctx, instance, "odh-admins")
if err != nil {
return reconcile.Result{}, err
}
}
if instance.Spec.Monitoring.ManagementState == operatorv1.Managed {
r.Log.Info("Monitoring enabled, won't apply changes", "cluster", "ODH Mode")
Expand Down
13 changes: 12 additions & 1 deletion controllers/dscinitialization/dscinitialization_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"

operatorv1 "github.com/openshift/api/operator/v1"
userv1 "github.com/openshift/api/user/v1"
corev1 "k8s.io/api/core/v1"
networkingv1 "k8s.io/api/networking/v1"
rbacv1 "k8s.io/api/rbac/v1"
Expand All @@ -21,6 +22,7 @@ import (
const (
workingNamespace = "test-operator-ns"
applicationNamespace = "test-application-ns"
usergroupName = "odh-admins"
configmapName = "odh-common-config"
applicationName = "default-dsci"
monitoringNamespace = "test-monitoring-ns"
Expand Down Expand Up @@ -119,6 +121,15 @@ var _ = Describe("DataScienceCluster initialization", func() {
Expect(foundConfigMap.Data).To(Equal(expectedConfigmapData))
})

It("Should not create user group when we do not have authentications CR in the cluster", func(ctx context.Context) {
userGroup := &userv1.Group{}
Eventually(objectExists(usergroupName, "", userGroup)).
WithContext(ctx).
WithTimeout(timeout).
WithPolling(interval).
Should(BeFalse())
})

})

Context("Monitoring Resource", func() {
Expand Down Expand Up @@ -368,7 +379,7 @@ func namespaceExists(ns string, obj client.Object) func(ctx context.Context) boo
}
}

func objectExists(ns string, name string, obj client.Object) func(ctx context.Context) bool { //nolint:unparam
func objectExists(ns string, name string, obj client.Object) func(ctx context.Context) bool {
return func(ctx context.Context) bool {
err := k8sClient.Get(ctx, client.ObjectKey{Name: ns, Namespace: name}, obj)

Expand Down
2 changes: 2 additions & 0 deletions controllers/dscinitialization/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"testing"
"time"

configv1 "github.com/openshift/api/config/v1"
routev1 "github.com/openshift/api/route/v1"
userv1 "github.com/openshift/api/user/v1"
ofapi "github.com/operator-framework/api/pkg/operators/v1alpha1"
Expand Down Expand Up @@ -116,6 +117,7 @@ var _ = BeforeSuite(func() {
utilruntime.Must(routev1.Install(testScheme))
utilruntime.Must(userv1.Install(testScheme))
utilruntime.Must(monitoringv1.AddToScheme(testScheme))
utilruntime.Must(configv1.Install(testScheme))
// +kubebuilder:scaffold:scheme

k8sClient, err = client.New(cfg, client.Options{Scheme: testScheme})
Expand Down
18 changes: 18 additions & 0 deletions pkg/cluster/cluster_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ import (
"strings"

"github.com/blang/semver/v4"
configv1 "github.com/openshift/api/config/v1"
"github.com/operator-framework/api/pkg/lib/version"
ofapiv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1"
corev1 "k8s.io/api/core/v1"
k8serr "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/client"
Expand Down Expand Up @@ -169,3 +171,19 @@ func GetRelease(ctx context.Context, cli client.Client) (Release, error) {
initRelease.Version = csv.Spec.Version
return initRelease, nil
}

// IsDefaultAuthMethod returns true if the default authentication method is IntegratedOAuth or empty.
// This will give indication that Operator should create userGroups or not in the cluster.
func IsDefaultAuthMethod(ctx context.Context, cli client.Client) (bool, error) {
authenticationobj := &configv1.Authentication{}
if err := cli.Get(ctx, client.ObjectKey{Name: ClusterAuthenticationObj, Namespace: ""}, authenticationobj); err != nil {
if errors.Is(err, &meta.NoKindMatchError{}) { // when CRD is missing, conver error type
return false, k8serr.NewNotFound(configv1.Resource("authentications"), ClusterAuthenticationObj)
}
return false, err
}
// for now, HPC support "" "None" "IntegratedOAuth"(default) "OIDC"
// other offering support "" "None" "IntegratedOAuth"(default)
// we only create userGroups for "IntegratedOAuth" or "" and leave other or new supported type value in the future
return authenticationobj.Spec.Type == configv1.AuthenticationTypeIntegratedOAuth || authenticationobj.Spec.Type == "", nil
}
2 changes: 2 additions & 0 deletions pkg/cluster/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ const (
OpenDataHub Platform = "Open Data Hub"
// Unknown indicates that operator is not deployed using OLM.
Unknown Platform = ""
// Default cluster-scope Authentication CR name.
ClusterAuthenticationObj = "cluster"
)

0 comments on commit e57b802

Please sign in to comment.