Skip to content

Commit 5484f16

Browse files
(fix)Add custom SA for lls by default (#115)
- Add custom SA to allow root permissions to init container. - This ensure we deploy lls without errors in restricted namespaces Approved-by: leseb Approved-by: rhdedgar Approved-by: mfleader
1 parent 90f99d5 commit 5484f16

File tree

10 files changed

+172
-14
lines changed

10 files changed

+172
-14
lines changed

config/rbac/role.yaml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ rules:
2727
- apiGroups:
2828
- ""
2929
resources:
30+
- serviceaccounts
3031
- services
3132
verbs:
3233
- create
@@ -86,3 +87,37 @@ rules:
8687
- patch
8788
- update
8889
- watch
90+
- apiGroups:
91+
- rbac.authorization.k8s.io
92+
resources:
93+
- clusterrolebindings
94+
verbs:
95+
- create
96+
- delete
97+
- get
98+
- list
99+
- patch
100+
- update
101+
- watch
102+
- apiGroups:
103+
- rbac.authorization.k8s.io
104+
resources:
105+
- clusterroles
106+
verbs:
107+
- get
108+
- list
109+
- watch
110+
- apiGroups:
111+
- security.openshift.io
112+
resources:
113+
- securitycontextconstraints
114+
verbs:
115+
- use
116+
- apiGroups:
117+
- security.openshift.io
118+
resourceNames:
119+
- anyuid
120+
resources:
121+
- securitycontextconstraints
122+
verbs:
123+
- use

controllers/kubebuilder_rbac.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,15 @@ package controllers
1111
// Service permissions - controller creates and manages services
1212
//+kubebuilder:rbac:groups="",resources=services,verbs=get;list;watch;create;update;patch;delete
1313

14-
// PVC permissions - controller creates PVCs (immutable after creation, no update/patch needed)
14+
// ServiceAccount permissions - controller creates and manages service accounts for PVC permissions
15+
//+kubebuilder:rbac:groups="",resources=serviceaccounts,verbs=get;list;watch;create;update;patch;delete
16+
17+
//+kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=clusterrolebindings,verbs=get;list;watch;create;update;patch;delete
18+
//+kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=clusterroles,verbs=get;list;watch
19+
20+
//+kubebuilder:rbac:groups=security.openshift.io,resources=securitycontextconstraints,verbs=use
21+
//+kubebuilder:rbac:groups=security.openshift.io,resources=securitycontextconstraints,resourceNames=anyuid,verbs=use
22+
1523
//+kubebuilder:rbac:groups="",resources=persistentvolumeclaims,verbs=get;list;watch;create
1624

1725
// ConfigMap permissions - controller reads user configmaps and manages operator config configmaps

controllers/llamastackdistribution_controller.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,11 @@ func (r *LlamaStackDistributionReconciler) reconcileResources(ctx context.Contex
230230
return fmt.Errorf("failed to reconcile NetworkPolicy: %w", err)
231231
}
232232

233+
// Reconcile manifest-based resources
234+
if err := r.reconcileManifestResources(ctx, instance); err != nil {
235+
return err
236+
}
237+
233238
// Reconcile the Deployment
234239
if err := r.reconcileDeployment(ctx, instance); err != nil {
235240
return fmt.Errorf("failed to reconcile Deployment: %w", err)
@@ -242,11 +247,6 @@ func (r *LlamaStackDistributionReconciler) reconcileResources(ctx context.Contex
242247
}
243248
}
244249

245-
// Reconcile manifest-based resources
246-
if err := r.reconcileManifestResources(ctx, instance); err != nil {
247-
return err
248-
}
249-
250250
return nil
251251
}
252252

controllers/manifests/base/kustomization.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ kind: Kustomization
33

44
resources:
55
- pvc.yaml
6+
- serviceaccount.yaml
7+
- scc-binding.yaml
68

79
labels:
810
- includeSelectors: true
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
apiVersion: rbac.authorization.k8s.io/v1
2+
kind: ClusterRoleBinding
3+
metadata:
4+
name: crb
5+
subjects:
6+
- kind: ServiceAccount
7+
name: sa
8+
namespace: default
9+
roleRef:
10+
kind: ClusterRole
11+
name: system:openshift:scc:anyuid
12+
apiGroup: rbac.authorization.k8s.io
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# This service account is used to grant privileges to the llama-stack init container
2+
# to update PVC permissions.
3+
apiVersion: v1
4+
kind: ServiceAccount
5+
metadata:
6+
name: sa
7+
annotations:
8+
# This annotation is used by OpenShift to assign the anyuid SCC
9+
# which allows the container to run as any user ID
10+
openshift.io/scc: anyuid

controllers/resource_helper.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -170,12 +170,15 @@ func configurePodStorage(instance *llamav1alpha1.LlamaStackDistribution, contain
170170

171171
// configurePodOverrides applies pod-level overrides from the LlamaStackDistribution spec.
172172
func configurePodOverrides(instance *llamav1alpha1.LlamaStackDistribution, podSpec *corev1.PodSpec) {
173-
if instance.Spec.Server.PodOverrides != nil {
174-
// Set ServiceAccount name if specified
175-
if instance.Spec.Server.PodOverrides.ServiceAccountName != "" {
176-
podSpec.ServiceAccountName = instance.Spec.Server.PodOverrides.ServiceAccountName
177-
}
173+
// Set ServiceAccount name - use override if specified, otherwise use default
174+
if instance.Spec.Server.PodOverrides != nil && instance.Spec.Server.PodOverrides.ServiceAccountName != "" {
175+
podSpec.ServiceAccountName = instance.Spec.Server.PodOverrides.ServiceAccountName
176+
} else {
177+
podSpec.ServiceAccountName = instance.Name + "-sa"
178+
}
178179

180+
// Apply other pod overrides if specified
181+
if instance.Spec.Server.PodOverrides != nil {
179182
// Add volumes if specified
180183
if len(instance.Spec.Server.PodOverrides.Volumes) > 0 {
181184
podSpec.Volumes = append(podSpec.Volumes, instance.Spec.Server.PodOverrides.Volumes...)

controllers/resource_helper_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -561,8 +561,8 @@ func TestPodOverridesWithoutServiceAccount(t *testing.T) {
561561
// Apply pod overrides
562562
configurePodOverrides(instance, &deployment.Spec.Template.Spec)
563563

564-
// Verify ServiceAccount name is empty
565-
if deployment.Spec.Template.Spec.ServiceAccountName != "" {
566-
t.Errorf("expected empty ServiceAccountName, got %s", deployment.Spec.Template.Spec.ServiceAccountName)
564+
// Verify ServiceAccount name is empty (default ServiceAccountName should be set when not explicitly provided)
565+
if deployment.Spec.Template.Spec.ServiceAccountName != instance.Name+"-sa" {
566+
t.Errorf("expected default ServiceAccountName when not explicitly provided, got %s", deployment.Spec.Template.Spec.ServiceAccountName)
567567
}
568568
}

pkg/deploy/kustomizer.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,17 @@ func manageResource(
8686
return fmt.Errorf("failed to unmarshal resource: %w", err)
8787
}
8888

89+
// Check if ClusterRoleBinding references a ClusterRole that exists
90+
if u.GetKind() == "ClusterRoleBinding" {
91+
if shouldSkip, err := CheckClusterRoleExists(ctx, cli, u); err != nil {
92+
return fmt.Errorf("failed to check ClusterRole existence: %w", err)
93+
} else if shouldSkip {
94+
log.FromContext(ctx).V(1).Info("Skipping ClusterRoleBinding - referenced ClusterRole not found",
95+
"clusterRoleBinding", u.GetName())
96+
return nil
97+
}
98+
}
99+
89100
kGvk := res.GetGvk()
90101
gvk := schema.GroupVersionKind{
91102
Group: kGvk.Group,
@@ -204,6 +215,18 @@ func applyPlugins(resMap *resmap.ResMap, ownerInstance *llamav1alpha1.LlamaStack
204215
TargetKind: "PersistentVolumeClaim",
205216
CreateIfNotExists: true,
206217
},
218+
{
219+
SourceValue: ownerInstance.GetNamespace(),
220+
TargetField: "subjects[0].namespace",
221+
TargetKind: "ClusterRoleBinding",
222+
CreateIfNotExists: true,
223+
},
224+
{
225+
SourceValue: ownerInstance.GetName() + "-sa",
226+
TargetField: "subjects[0].name",
227+
TargetKind: "ClusterRoleBinding",
228+
CreateIfNotExists: true,
229+
},
207230
},
208231
})
209232
if err := fieldTransformerPlugin.Transform(*resMap); err != nil {
@@ -233,3 +256,33 @@ func FilterExcludeKinds(resMap *resmap.ResMap, kindsToExclude []string) (*resmap
233256
}
234257
return &filteredResMap, nil
235258
}
259+
260+
// CheckClusterRoleExists checks if a ClusterRoleBinding should be skipped due to missing ClusterRole.
261+
func CheckClusterRoleExists(ctx context.Context, cli client.Client, crb *unstructured.Unstructured) (bool, error) {
262+
roleRef, found, _ := unstructured.NestedMap(crb.Object, "roleRef")
263+
if !found {
264+
return false, nil // No roleRef, don't skip
265+
}
266+
267+
roleName, _, _ := unstructured.NestedString(roleRef, "name")
268+
if roleName == "" {
269+
return false, nil // Empty roleName, don't skip
270+
}
271+
272+
// Check if the referenced ClusterRole exists
273+
clusterRole := &unstructured.Unstructured{}
274+
clusterRole.SetGroupVersionKind(schema.GroupVersionKind{
275+
Group: "rbac.authorization.k8s.io",
276+
Version: "v1",
277+
Kind: "ClusterRole",
278+
})
279+
clusterRole.SetName(roleName)
280+
281+
err := cli.Get(ctx, client.ObjectKey{Name: roleName}, clusterRole)
282+
if err != nil && k8serr.IsNotFound(err) {
283+
return true, nil
284+
} else if err != nil {
285+
return false, err
286+
}
287+
return false, nil
288+
}

release/operator.yaml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2262,6 +2262,7 @@ rules:
22622262
- apiGroups:
22632263
- ""
22642264
resources:
2265+
- serviceaccounts
22652266
- services
22662267
verbs:
22672268
- create
@@ -2321,6 +2322,40 @@ rules:
23212322
- patch
23222323
- update
23232324
- watch
2325+
- apiGroups:
2326+
- rbac.authorization.k8s.io
2327+
resources:
2328+
- clusterrolebindings
2329+
verbs:
2330+
- create
2331+
- delete
2332+
- get
2333+
- list
2334+
- patch
2335+
- update
2336+
- watch
2337+
- apiGroups:
2338+
- rbac.authorization.k8s.io
2339+
resources:
2340+
- clusterroles
2341+
verbs:
2342+
- get
2343+
- list
2344+
- watch
2345+
- apiGroups:
2346+
- security.openshift.io
2347+
resources:
2348+
- securitycontextconstraints
2349+
verbs:
2350+
- use
2351+
- apiGroups:
2352+
- security.openshift.io
2353+
resourceNames:
2354+
- anyuid
2355+
resources:
2356+
- securitycontextconstraints
2357+
verbs:
2358+
- use
23242359
---
23252360
apiVersion: rbac.authorization.k8s.io/v1
23262361
kind: ClusterRole

0 commit comments

Comments
 (0)