Skip to content

Commit

Permalink
Add ClusterRoleBindings creation
Browse files Browse the repository at this point in the history
  • Loading branch information
ruivieira committed Nov 8, 2023
1 parent 8798760 commit e53be78
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 33 deletions.
13 changes: 13 additions & 0 deletions config/rbac/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,10 @@ rules:
- serviceaccounts
verbs:
- create
- delete
- get
- list
- update
- watch
- apiGroups:
- apps
Expand Down Expand Up @@ -124,6 +126,17 @@ rules:
- create
- list
- watch
- apiGroups:
- rbac.authorization.k8s.io
resources:
- clusterrolebindings
verbs:
- create
- delete
- get
- list
- update
- watch
- apiGroups:
- route.openshift.io
resources:
Expand Down
1 change: 1 addition & 0 deletions controllers/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const (
// OAuth constants
const (
OAuthServicePort = 443
OAuthName = "oauth-proxy"
OAuthServicePortName = "oauth-proxy"
OAuthProxyImage = "registry.redhat.io/openshift4/ose-oauth-proxy:latest"
ServiceAccountName = componentName
Expand Down
7 changes: 4 additions & 3 deletions controllers/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
func (r *TrustyAIServiceReconciler) createDeploymentObject(ctx context.Context, cr *trustyaiopendatahubiov1alpha1.TrustyAIService, image string) *appsv1.Deployment {
labels := getCommonLabels(cr.Name)
pvcName := generatePVCName(cr)
serviceAccountName := generateServiceAccountName(cr)

replicas := int32(1)
if cr.Spec.Replicas == nil {
Expand All @@ -30,7 +31,7 @@ func (r *TrustyAIServiceReconciler) createDeploymentObject(ctx context.Context,
}

// Create the OAuth-Proxy container spec
oauthProxyContainer := InjectOAuthProxy(cr, OAuthConfig{ProxyImage: OAuthProxyImage})
oauthProxyContainer := generateOAuthProxyContainer(cr, OAuthConfig{ProxyImage: OAuthProxyImage})

containers := []corev1.Container{
{
Expand Down Expand Up @@ -83,7 +84,7 @@ func (r *TrustyAIServiceReconciler) createDeploymentObject(ctx context.Context,
},
},
}
volumes := InjectOAuthVolumes(cr, OAuthConfig{ProxyImage: OAuthProxyImage})
volumes := generateOAuthVolumes(cr, OAuthConfig{ProxyImage: OAuthProxyImage})

volumes = append(volumes, volume)

Expand All @@ -108,7 +109,7 @@ func (r *TrustyAIServiceReconciler) createDeploymentObject(ctx context.Context,
},
},
Spec: corev1.PodSpec{
ServiceAccountName: cr.Name + "-proxy",
ServiceAccountName: serviceAccountName,
Containers: containers,
Volumes: volumes,
},
Expand Down
34 changes: 14 additions & 20 deletions controllers/oauth.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ type OAuthConfig struct {
ProxyImage string
}

func InjectOAuthProxy(instance *trustyaiopendatahubiov1alpha1.TrustyAIService, oauth OAuthConfig) corev1.Container {
// https://pkg.go.dev/k8s.io/api/core/v1#Container
// generateOAuthProxyContainer create the OAuth-proxy container object for a TrustyAI service instance
func generateOAuthProxyContainer(instance *trustyaiopendatahubiov1alpha1.TrustyAIService, oauth OAuthConfig) corev1.Container {
proxyContainer := corev1.Container{
Name: "oauth-proxy",
Name: OAuthName,
Image: oauth.ProxyImage,
ImagePullPolicy: corev1.PullAlways,
Env: []corev1.EnvVar{{
Expand All @@ -35,14 +35,15 @@ func InjectOAuthProxy(instance *trustyaiopendatahubiov1alpha1.TrustyAIService, o
Args: []string{
"--cookie-secret=SECRET",
"--https-address=:8443",
"--email-domain=*",
fmt.Sprintf("--openshift-service-account=%s", instance.Name+"-proxy"),
"--provider=openshift",
"--tls-cert=/etc/tls/private/tls.crt",
"--tls-key=/etc/tls/private/tls.key",
"--upstream=http://localhost:8080",
"--skip-auth-regex='(^/metrics|^/apis/v1beta1/healthz)'",
"'--openshift-sar={\"namespace\":\"" + instance.Namespace + "\",\"resource\":\"pods\",\"verb\":\"get\"}'",
"'--openshift-delegate-urls={\"/\": {\"namespace\": \"" + instance.Namespace + "\", \"resource\": \"pods\", \"verb\": \"get\"}}'",
fmt.Sprintf("--openshift-sar={\"namespace\":\"%s\",\"resource\":\"pods\",\"verb\":\"get\"}", instance.Namespace),
fmt.Sprintf("--openshift-delegate-urls={\"/\": {\"namespace\": \"%s\", \"resource\": \"pods\", \"verb\": \"get\"}}", instance.Namespace),
},
Ports: []corev1.ContainerPort{{
Name: OAuthServicePortName,
Expand Down Expand Up @@ -88,24 +89,17 @@ func InjectOAuthProxy(instance *trustyaiopendatahubiov1alpha1.TrustyAIService, o
},
},
VolumeMounts: []corev1.VolumeMount{
//{
// Name: "oauth-config",
// MountPath: "/etc/oauth/config",
//},
{
Name: instance.Name + "-tls",
MountPath: "/etc/tls/private",
},
//{
// MountPath: "/etc/oauth/client",
// Name: "oauth-client",
//},
},
}
return proxyContainer
}

func InjectOAuthVolumes(instance *trustyaiopendatahubiov1alpha1.TrustyAIService, oauth OAuthConfig) []corev1.Volume {
// generateOAuthVolumes create the OAuth necessary volume objects
func generateOAuthVolumes(instance *trustyaiopendatahubiov1alpha1.TrustyAIService, oauth OAuthConfig) []corev1.Volume {

volumes := []corev1.Volume{
{
Expand All @@ -120,8 +114,8 @@ func InjectOAuthVolumes(instance *trustyaiopendatahubiov1alpha1.TrustyAIService,
return volumes
}

// NewTrustyAIOAuthService defines the desired OAuth service object
func NewTrustyAIOAuthService(instance *trustyaiopendatahubiov1alpha1.TrustyAIService) *corev1.Service {
// generateTrustyAIOAuthService defines the desired OAuth service object
func generateTrustyAIOAuthService(instance *trustyaiopendatahubiov1alpha1.TrustyAIService) *corev1.Service {
return &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: instance.Name + "-tls",
Expand All @@ -147,12 +141,12 @@ func NewTrustyAIOAuthService(instance *trustyaiopendatahubiov1alpha1.TrustyAISer
}
}

// ReconcileOAuthService will manage the OAuth service reconciliation required
// reconcileOAuthService will manage the OAuth service reconciliation required
// by the service's OAuth proxy
func (r *TrustyAIServiceReconciler) ReconcileOAuthService(ctx context.Context, instance *trustyaiopendatahubiov1alpha1.TrustyAIService) error {
func (r *TrustyAIServiceReconciler) reconcileOAuthService(ctx context.Context, instance *trustyaiopendatahubiov1alpha1.TrustyAIService) error {

// Generate the desired OAuth service
desiredService := NewTrustyAIOAuthService(instance)
// Generate the desired OAuth service object
desiredService := generateTrustyAIOAuthService(instance)

// Create the OAuth service if it does not already exist
foundService := &corev1.Service{}
Expand Down
63 changes: 57 additions & 6 deletions controllers/service_accounts.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,25 @@ import (
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/json"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/log"

corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
ctrl "sigs.k8s.io/controller-runtime"
)

// ...
func generateServiceAccountName(instance *trustyaiopendatahubiov1alpha1.TrustyAIService) string {
return instance.Name + "-proxy"
}

// createServiceAccount creates a service account for this instance's OAuth proxy
func (r *TrustyAIServiceReconciler) createServiceAccount(ctx context.Context, instance *trustyaiopendatahubiov1alpha1.TrustyAIService) error {
routeName := instance.Name
saName := instance.Name + "-proxy"
serviceAccountName := generateServiceAccountName(instance)

// Define the OAuth redirect reference using a struct for easy JSON encoding
// Define the OAuth redirect reference
oauthRedirectRef := struct {
Kind string `json:"kind"`
APIVersion string `json:"apiVersion"`
Expand Down Expand Up @@ -49,14 +54,14 @@ func (r *TrustyAIServiceReconciler) createServiceAccount(ctx context.Context, in

sa := &corev1.ServiceAccount{
ObjectMeta: metav1.ObjectMeta{
Name: saName,
Name: serviceAccountName,
Namespace: instance.Namespace,
Annotations: map[string]string{
"serviceaccounts.openshift.io/oauth-redirectreference.primary": string(oauthRedirectRefJSON),
},
Labels: map[string]string{
"app": componentName,
"app.kubernetes.io/name": saName,
"app.kubernetes.io/name": serviceAccountName,
"app.kubernetes.io/instance": instance.Name,
"app.kubernetes.io/part-of": componentName,
"app.kubernetes.io/version": "0.1.0",
Expand All @@ -82,6 +87,52 @@ func (r *TrustyAIServiceReconciler) createServiceAccount(ctx context.Context, in
return err
}

// ServiceAccount created successfully
err = r.createClusterRoleBinding(ctx, instance, serviceAccountName)
if err != nil {
return err
}

return nil
}

// createClusterRoleBinding creates a binding between the service account and token review cluster role
func (r *TrustyAIServiceReconciler) createClusterRoleBinding(ctx context.Context, instance *trustyaiopendatahubiov1alpha1.TrustyAIService, serviceAccountName string) error {
clusterRoleBinding := &rbacv1.ClusterRoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: instance.Name + "-proxy-rolebinding",
Namespace: instance.Namespace,
},
Subjects: []rbacv1.Subject{
{
Kind: "ServiceAccount",
Name: serviceAccountName,
Namespace: instance.Namespace,
},
},
RoleRef: rbacv1.RoleRef{
Kind: "ClusterRole",
Name: "trustyai-service-operator-proxy-role",
APIGroup: rbacv1.GroupName,
},
}

// Set instance as the owner of the ClusterRoleBinding
if err := controllerutil.SetControllerReference(instance, clusterRoleBinding, r.Scheme); err != nil {
return err
}

// Check if this ClusterRoleBinding already exists
found := &rbacv1.ClusterRoleBinding{}
err := r.Get(ctx, types.NamespacedName{Name: clusterRoleBinding.Name}, found)
if err != nil && errors.IsNotFound(err) {
log.FromContext(ctx).Info("Creating a new ClusterRoleBinding", "Name", clusterRoleBinding.Name)
err = r.Create(ctx, clusterRoleBinding)
if err != nil {
return err
}
} else if err != nil {
return err
}

return nil
}
7 changes: 3 additions & 4 deletions controllers/trustyaiservice_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ type TrustyAIServiceReconciler struct {
//+kubebuilder:rbac:groups=serving.kserve.io,resources=inferenceservices/finalizers,verbs=list;watch;get;update;patch;delete
//+kubebuilder:rbac:groups="",resources=events,verbs=create;patch;update
//+kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups="",resources=serviceaccounts,verbs=get;list;watch;create
//+kubebuilder:rbac:groups="",resources=serviceaccounts,verbs=get;list;watch;create;update;delete
//+kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=clusterrolebindings,verbs=get;list;watch;create;update;delete

// getCommonLabels returns the service's common labels
func getCommonLabels(serviceName string) map[string]string {
Expand Down Expand Up @@ -133,13 +134,11 @@ func (r *TrustyAIServiceReconciler) Reconcile(ctx context.Context, req ctrl.Requ
return RequeueWithError(err)
}

err = r.ReconcileOAuthService(ctx, instance)
err = r.reconcileOAuthService(ctx, instance)
if err != nil {
return RequeueWithError(err)
}

instance.Status.Ready = corev1.ConditionTrue

// CR found, add or update the URL
// Call the function to patch environment variables for Deployments that match the label
shouldContinue, err := r.handleInferenceServices(ctx, instance, req.Namespace, modelMeshLabelKey, modelMeshLabelValue, payloadProcessorName, req.Name, false)
Expand Down

0 comments on commit e53be78

Please sign in to comment.