Skip to content

Commit 76bb90f

Browse files
authored
enable OwnerReferencesPermissionEnforcement in project kind config (#1105)
Signed-off-by: Joe Lanford <[email protected]>
1 parent 6864b0c commit 76bb90f

5 files changed

+115
-79
lines changed

Diff for: config/samples/olm_v1alpha1_clusterextension.yaml

+17-12
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,12 @@ kind: ClusterRole
2828
metadata:
2929
name: argocd-installer-clusterrole
3030
rules:
31-
# Manage ArgoCD CRDs
31+
# Allow ClusterExtension to set blockOwnerDeletion ownerReferences
32+
- apiGroups: [olm.operatorframework.io]
33+
resources: [clusterextensions/finalizers]
34+
verbs: [update]
35+
resourceNames: [argocd]
36+
# Manage ArgoCD CRDs
3237
- apiGroups: [apiextensions.k8s.io]
3338
resources: [customresourcedefinitions]
3439
verbs: [create]
@@ -221,32 +226,32 @@ metadata:
221226
rules:
222227
- apiGroups: [""]
223228
resources: [serviceaccounts]
224-
verbs: [get, list, watch, create, update, patch, delete]
225-
resourceNames: [argocd-operator-controller-manager]
229+
verbs: [create]
226230
- apiGroups: [""]
227231
resources: [serviceaccounts]
232+
verbs: [get, list, watch, update, patch, delete]
233+
resourceNames: [argocd-operator-controller-manager]
234+
- apiGroups: [""]
235+
resources: [configmaps]
228236
verbs: [create]
229237
- apiGroups: [""]
230238
resources: [configmaps]
231-
verbs: [get, list, watch, create, update, patch, delete]
239+
verbs: [get, list, watch, update, patch, delete]
232240
resourceNames: [argocd-operator-manager-config]
233241
- apiGroups: [""]
234-
resources: [configmaps]
242+
resources: [services]
235243
verbs: [create]
236244
- apiGroups: [""]
237245
resources: [services]
238-
verbs: [get, list, watch, create, update, patch, delete]
246+
verbs: [get, list, watch, update, patch, delete]
239247
resourceNames: [argocd-operator-controller-manager-metrics-service]
240-
- apiGroups: [""]
241-
resources: [services]
242-
verbs: [create]
243248
- apiGroups: [apps]
244249
resources: [deployments]
245-
verbs: [get, list, watch, create, update, patch, delete]
246-
resourceNames: [argocd-operator-controller-manager]
250+
verbs: [create]
247251
- apiGroups: [apps]
248252
resources: [deployments]
249-
verbs: [create]
253+
verbs: [get, list, watch, update, patch, delete]
254+
resourceNames: [argocd-operator-controller-manager]
250255
---
251256
apiVersion: rbac.authorization.k8s.io/v1
252257
kind: RoleBinding
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Configuring a service account when the cluster uses the `OwnerReferencesPermissionEnforcement` admission plugin
2+
3+
The [`OwnerReferencesPermissionEnforcement`](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#ownerreferencespermissionenforcement) admission plugin requires a user to have permission to set finalizers on owner objects when creating or updating an object to contain an `ownerReference` with `blockOwnerDeletion: true`.
4+
5+
When operator-controller installs or upgrades a `ClusterExtension`, it sets an `ownerReference` on each object with `blockOwnerDeletion: true`. Therefore serviceaccounts configured in `.spec.serviceAccount.name` must have the following permission in a bound `ClusterRole`:
6+
7+
```yaml
8+
- apiGroups: ["olm.operatorframework.io"]
9+
resources: ["clusterextensions/finalizers"]
10+
verbs: ["update"]
11+
resourceNames: ["<clusterExtensionName>"]
12+
```
13+

Diff for: kind-config.yaml

+6
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,9 @@ nodes:
88
hostPort: 30000
99
listenAddress: "127.0.0.1"
1010
protocol: tcp
11+
kubeadmConfigPatches:
12+
- |
13+
kind: ClusterConfiguration
14+
apiServer:
15+
extraArgs:
16+
enable-admission-plugins: OwnerReferencesPermissionEnforcement

Diff for: test/e2e/cluster_extension_install_test.go

+15-3
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ const (
3939
var pollDuration = time.Minute
4040
var pollInterval = time.Second
4141

42-
func createServiceAccount(ctx context.Context, name types.NamespacedName) (*corev1.ServiceAccount, error) {
42+
func createServiceAccount(ctx context.Context, name types.NamespacedName, clusterExtensionName string) (*corev1.ServiceAccount, error) {
4343
sa := &corev1.ServiceAccount{
4444
ObjectMeta: metav1.ObjectMeta{
4545
Name: name.Name,
@@ -55,6 +55,18 @@ func createServiceAccount(ctx context.Context, name types.NamespacedName) (*core
5555
Name: name.Name,
5656
},
5757
Rules: []rbacv1.PolicyRule{
58+
{
59+
APIGroups: []string{
60+
"olm.operatorframework.io",
61+
},
62+
Resources: []string{
63+
"clusterextensions/finalizers",
64+
},
65+
Verbs: []string{
66+
"update",
67+
},
68+
ResourceNames: []string{clusterExtensionName},
69+
},
5870
{
5971
APIGroups: []string{
6072
"",
@@ -178,7 +190,7 @@ func testInit(t *testing.T) (*ocv1alpha1.ClusterExtension, *catalogd.ClusterCata
178190
Namespace: "default",
179191
}
180192

181-
sa, err := createServiceAccount(context.Background(), defaultNamespace)
193+
sa, err := createServiceAccount(context.Background(), defaultNamespace, clusterExtensionName)
182194
require.NoError(t, err)
183195
return clusterExtension, extensionCatalog, sa
184196
}
@@ -487,7 +499,7 @@ func TestClusterExtensionInstallReResolvesWhenNewCatalog(t *testing.T) {
487499
Name: clusterExtensionName,
488500
},
489501
}
490-
sa, err := createServiceAccount(context.Background(), types.NamespacedName{Name: clusterExtensionName, Namespace: "default"})
502+
sa, err := createServiceAccount(context.Background(), types.NamespacedName{Name: clusterExtensionName, Namespace: "default"}, clusterExtensionName)
491503
require.NoError(t, err)
492504
defer testCleanup(t, extensionCatalog, clusterExtension, sa)
493505
defer getArtifactsOutput(t)

Diff for: test/extension-developer-e2e/extension_developer_test.go

+64-64
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import (
1414
"k8s.io/apimachinery/pkg/api/meta"
1515
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1616
"k8s.io/apimachinery/pkg/runtime"
17-
"k8s.io/apimachinery/pkg/types"
1817
"k8s.io/apimachinery/pkg/util/rand"
1918
ctrl "sigs.k8s.io/controller-runtime"
2019
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -39,31 +38,68 @@ func TestExtensionDeveloper(t *testing.T) {
3938
require.NoError(t, err)
4039

4140
ctx := context.Background()
42-
saName := fmt.Sprintf("serviceaccounts-%s", rand.String(8))
43-
name := types.NamespacedName{
44-
Name: saName,
45-
Namespace: "default",
41+
42+
catalog := &catalogd.ClusterCatalog{
43+
ObjectMeta: metav1.ObjectMeta{
44+
GenerateName: "catalog",
45+
},
46+
Spec: catalogd.ClusterCatalogSpec{
47+
Source: catalogd.CatalogSource{
48+
Type: catalogd.SourceTypeImage,
49+
Image: &catalogd.ImageSource{
50+
Ref: os.Getenv("CATALOG_IMG"),
51+
InsecureSkipTLSVerify: true,
52+
},
53+
},
54+
},
4655
}
56+
require.NoError(t, c.Create(context.Background(), catalog))
57+
58+
installNamespace := "default"
4759

4860
sa := &corev1.ServiceAccount{
4961
ObjectMeta: metav1.ObjectMeta{
50-
Name: name.Name,
51-
Namespace: name.Namespace,
62+
Name: fmt.Sprintf("serviceaccount-%s", rand.String(8)),
63+
Namespace: installNamespace,
5264
},
5365
}
5466
require.NoError(t, c.Create(ctx, sa))
5567

68+
clusterExtension := &ocv1alpha1.ClusterExtension{
69+
ObjectMeta: metav1.ObjectMeta{
70+
Name: "registryv1",
71+
},
72+
Spec: ocv1alpha1.ClusterExtensionSpec{
73+
PackageName: os.Getenv("REG_PKG_NAME"),
74+
InstallNamespace: installNamespace,
75+
ServiceAccount: ocv1alpha1.ServiceAccountReference{
76+
Name: sa.Name,
77+
},
78+
},
79+
}
80+
5681
cr := &rbacv1.ClusterRole{
5782
ObjectMeta: metav1.ObjectMeta{
58-
Name: name.Name,
83+
Name: fmt.Sprintf("clusterrole-%s", rand.String(8)),
5984
},
6085
Rules: []rbacv1.PolicyRule{
86+
{
87+
APIGroups: []string{
88+
"olm.operatorframework.io",
89+
},
90+
Resources: []string{
91+
"clusterextensions/finalizers",
92+
},
93+
Verbs: []string{
94+
"update",
95+
},
96+
ResourceNames: []string{clusterExtension.Name},
97+
},
6198
{
6299
APIGroups: []string{
63100
"",
64101
},
65102
Resources: []string{
66-
"secrets", // for helm
67103
"services",
68104
"serviceaccounts",
69105
},
@@ -139,72 +175,36 @@ func TestExtensionDeveloper(t *testing.T) {
139175

140176
crb := &rbacv1.ClusterRoleBinding{
141177
ObjectMeta: metav1.ObjectMeta{
142-
Name: name.Name,
178+
Name: fmt.Sprintf("clusterrolebinding-%s", rand.String(8)),
143179
},
144180
Subjects: []rbacv1.Subject{
145181
{
146182
Kind: "ServiceAccount",
147-
Name: name.Name,
148-
Namespace: name.Namespace,
183+
Name: sa.Name,
184+
Namespace: sa.Namespace,
149185
},
150186
},
151187
RoleRef: rbacv1.RoleRef{
152188
APIGroup: "rbac.authorization.k8s.io",
153189
Kind: "ClusterRole",
154-
Name: name.Name,
190+
Name: cr.Name,
155191
},
156192
}
157193
require.NoError(t, c.Create(ctx, crb))
158194

159-
clusterExtensions := []*ocv1alpha1.ClusterExtension{
160-
{
161-
ObjectMeta: metav1.ObjectMeta{
162-
Name: "registryv1",
163-
},
164-
Spec: ocv1alpha1.ClusterExtensionSpec{
165-
PackageName: os.Getenv("REG_PKG_NAME"),
166-
InstallNamespace: "default",
167-
ServiceAccount: ocv1alpha1.ServiceAccountReference{
168-
Name: saName,
169-
},
170-
},
171-
},
172-
}
173-
174-
for _, ce := range clusterExtensions {
175-
clusterExtension := ce
176-
t.Run(clusterExtension.ObjectMeta.Name, func(t *testing.T) {
177-
t.Parallel()
178-
catalog := &catalogd.ClusterCatalog{
179-
ObjectMeta: metav1.ObjectMeta{
180-
GenerateName: "catalog",
181-
},
182-
Spec: catalogd.ClusterCatalogSpec{
183-
Source: catalogd.CatalogSource{
184-
Type: catalogd.SourceTypeImage,
185-
Image: &catalogd.ImageSource{
186-
Ref: os.Getenv("CATALOG_IMG"),
187-
InsecureSkipTLSVerify: true,
188-
},
189-
},
190-
},
191-
}
192-
t.Logf("When creating an ClusterExtension that references a package with a %q bundle type", clusterExtension.ObjectMeta.Name)
193-
require.NoError(t, c.Create(context.Background(), catalog))
194-
require.NoError(t, c.Create(context.Background(), clusterExtension))
195-
t.Log("It should have a status condition type of Installed with a status of True and a reason of Success")
196-
require.EventuallyWithT(t, func(ct *assert.CollectT) {
197-
ext := &ocv1alpha1.ClusterExtension{}
198-
assert.NoError(ct, c.Get(context.Background(), client.ObjectKeyFromObject(clusterExtension), ext))
199-
cond := meta.FindStatusCondition(ext.Status.Conditions, ocv1alpha1.TypeInstalled)
200-
if !assert.NotNil(ct, cond) {
201-
return
202-
}
203-
assert.Equal(ct, metav1.ConditionTrue, cond.Status)
204-
assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason)
205-
}, 2*time.Minute, time.Second)
206-
require.NoError(t, c.Delete(context.Background(), catalog))
207-
require.NoError(t, c.Delete(context.Background(), clusterExtension))
208-
})
209-
}
195+
t.Logf("When creating an ClusterExtension that references a package with a %q bundle type", clusterExtension.ObjectMeta.Name)
196+
require.NoError(t, c.Create(context.Background(), clusterExtension))
197+
t.Log("It should have a status condition type of Installed with a status of True and a reason of Success")
198+
require.EventuallyWithT(t, func(ct *assert.CollectT) {
199+
ext := &ocv1alpha1.ClusterExtension{}
200+
assert.NoError(ct, c.Get(context.Background(), client.ObjectKeyFromObject(clusterExtension), ext))
201+
cond := meta.FindStatusCondition(ext.Status.Conditions, ocv1alpha1.TypeInstalled)
202+
if !assert.NotNil(ct, cond) {
203+
return
204+
}
205+
assert.Equal(ct, metav1.ConditionTrue, cond.Status)
206+
assert.Equal(ct, ocv1alpha1.ReasonSuccess, cond.Reason)
207+
}, 2*time.Minute, time.Second)
208+
require.NoError(t, c.Delete(context.Background(), catalog))
209+
require.NoError(t, c.Delete(context.Background(), clusterExtension))
210210
}

0 commit comments

Comments
 (0)