Skip to content

Commit 8ab0edb

Browse files
authored
fix: block cluster-scoped resources from being wrapped in an envelope (#32)
1 parent 9c67ff9 commit 8ab0edb

File tree

11 files changed

+224
-156
lines changed

11 files changed

+224
-156
lines changed

pkg/controllers/workgenerator/controller.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1280,8 +1280,11 @@ func extractResFromConfigMap(uConfigMap *unstructured.Unstructured) ([]fleetv1be
12801280
klog.ErrorS(unMarshallErr, "manifest has invalid content", "manifestKey", key, "envelopeResource", klog.KObj(uConfigMap))
12811281
return nil, fmt.Errorf("the object with manifest key `%s` in envelope config `%s` is malformatted, err: %w", key, klog.KObj(uConfigMap), unMarshallErr)
12821282
}
1283+
if len(uManifest.GetNamespace()) == 0 {
1284+
// Block cluster-scoped resources.
1285+
return nil, fmt.Errorf("cannot wrap cluster-scoped resource %s in the envelope %s", uManifest.GetName(), klog.KObj(uConfigMap))
1286+
}
12831287
if len(uManifest.GetNamespace()) != 0 && uManifest.GetNamespace() != configMap.Namespace {
1284-
// if the manifest is a namespaced object but no namespace is specified, it will fail at the apply time instead of here.
12851288
return nil, fmt.Errorf("the namespaced object `%s` in envelope config `%s` is placed in a different namespace `%s` ", uManifest.GetName(), klog.KObj(uConfigMap), uManifest.GetNamespace())
12861289
}
12871290
manifests = append(manifests, fleetv1beta1.Manifest{

pkg/controllers/workgenerator/controller_integration_test.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -698,7 +698,6 @@ var _ = Describe("Test Work Generator Controller", func() {
698698
Workload: placementv1beta1.WorkloadTemplate{
699699
Manifests: []placementv1beta1.Manifest{
700700
{RawExtension: runtime.RawExtension{Raw: testEnvelopeResourceQuota}},
701-
{RawExtension: runtime.RawExtension{Raw: testEnvelopeWebhook}},
702701
},
703702
},
704703
},
@@ -823,7 +822,7 @@ var _ = Describe("Test Work Generator Controller", func() {
823822
Spec: placementv1beta1.WorkSpec{
824823
Workload: placementv1beta1.WorkloadTemplate{
825824
Manifests: []placementv1beta1.Manifest{
826-
{RawExtension: runtime.RawExtension{Raw: testEnvelopeWebhook}},
825+
{RawExtension: runtime.RawExtension{Raw: testEnvelopeResourceQuota2}},
827826
},
828827
},
829828
},

pkg/controllers/workgenerator/controller_test.go

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,23 @@ func TestExtractResFromConfigMap(t *testing.T) {
208208
want: nil,
209209
wantErr: true,
210210
},
211+
"config map with cluster scoped resource should fail": {
212+
uConfigMap: &unstructured.Unstructured{
213+
Object: map[string]interface{}{
214+
"apiVersion": "v1",
215+
"kind": "ConfigMap",
216+
"metadata": map[string]interface{}{
217+
"name": "test-config",
218+
"namespace": "default",
219+
},
220+
"data": map[string]interface{}{
221+
"resource": `{"apiVersion": "admissionregistration.k8s.io/v1", "kind": "ValidatingWebhookConfiguration", "metadata": {"name": "test-webhook"}}`,
222+
},
223+
},
224+
},
225+
want: nil,
226+
wantErr: true,
227+
},
211228
"config map with valid and invalid entries should fail": {
212229
uConfigMap: &unstructured.Unstructured{
213230
Object: map[string]interface{}{
@@ -226,7 +243,7 @@ func TestExtractResFromConfigMap(t *testing.T) {
226243
want: nil,
227244
wantErr: true,
228245
},
229-
"config map with cluster and namespace scoped data in the correct namespace should pass": {
246+
"config map with cluster and namespace scoped data in the correct namespace should fail": {
230247
uConfigMap: &unstructured.Unstructured{
231248
Object: map[string]interface{}{
232249
"apiVersion": "v1",
@@ -241,11 +258,8 @@ func TestExtractResFromConfigMap(t *testing.T) {
241258
},
242259
},
243260
},
244-
want: []fleetv1beta1.Manifest{
245-
{RawExtension: runtime.RawExtension{Raw: []byte(`{"apiVersion": "v1", "kind": "Pod", "metadata": {"name": "test-pod", "namespace": "default"}}`)}},
246-
{RawExtension: runtime.RawExtension{Raw: []byte(`{"apiVersion": "v1", "kind": "ClusterRole", "metadata": {"name": "test-role"}}`)}},
247-
},
248-
wantErr: false,
261+
want: nil,
262+
wantErr: true,
249263
},
250264
"config map with cluster scoped and cross namespaced resources data in a different namespace should fail": {
251265
uConfigMap: &unstructured.Unstructured{
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
apiVersion: v1
2+
kind: ResourceQuota
3+
metadata:
4+
name: mem-cpu-demo
5+
namespace: app
6+
spec:
7+
hard:
8+
requests.cpu: "2"
9+
requests.memory: 2Gi
10+
limits.cpu: "4"
11+
limits.memory: 4Gi

pkg/controllers/workgenerator/manifests/test-envelop-configmap.yaml

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -18,34 +18,3 @@ data:
1818
requests.memory: 1Gi
1919
limits.cpu: "2"
2020
limits.memory: 2Gi
21-
webhook.yaml: |
22-
apiVersion: admissionregistration.k8s.io/v1
23-
kind: MutatingWebhookConfiguration
24-
metadata:
25-
creationTimestamp: null
26-
labels:
27-
azure-workload-identity.io/system: "true"
28-
name: azure-wi-webhook-mutating-webhook-configuration
29-
webhooks:
30-
- admissionReviewVersions:
31-
- v1
32-
- v1beta1
33-
clientConfig:
34-
service:
35-
name: azure-wi-webhook-webhook-service
36-
namespace: app
37-
path: /mutate-v1-pod
38-
failurePolicy: Fail
39-
matchPolicy: Equivalent
40-
name: mutation.azure-workload-identity.io
41-
rules:
42-
- apiGroups:
43-
- ""
44-
apiVersions:
45-
- v1
46-
operations:
47-
- CREATE
48-
- UPDATE
49-
resources:
50-
- pods
51-
sideEffects: None

pkg/controllers/workgenerator/manifests/test-envelop-configmap2.yaml

Lines changed: 11 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -6,34 +6,15 @@ metadata:
66
annotations:
77
kubernetes-fleet.io/envelope-configmap: "true"
88
data:
9-
webhook.yaml: |
10-
apiVersion: admissionregistration.k8s.io/v1
11-
kind: MutatingWebhookConfiguration
9+
resourceQuota.yaml: |
10+
apiVersion: v1
11+
kind: ResourceQuota
1212
metadata:
13-
creationTimestamp: null
14-
labels:
15-
azure-workload-identity.io/system: "true"
16-
name: azure-wi-webhook-mutating-webhook-configuration
17-
webhooks:
18-
- admissionReviewVersions:
19-
- v1
20-
- v1beta1
21-
clientConfig:
22-
service:
23-
name: azure-wi-webhook-webhook-service
24-
namespace: app
25-
path: /mutate-v1-pod
26-
failurePolicy: Fail
27-
matchPolicy: Equivalent
28-
name: mutation.azure-workload-identity.io
29-
rules:
30-
- apiGroups:
31-
- ""
32-
apiVersions:
33-
- v1
34-
operations:
35-
- CREATE
36-
- UPDATE
37-
resources:
38-
- pods
39-
sideEffects: None
13+
name: mem-cpu-demo
14+
namespace: app
15+
spec:
16+
hard:
17+
requests.cpu: "2"
18+
requests.memory: 2Gi
19+
limits.cpu: "4"
20+
limits.memory: 4Gi

pkg/controllers/workgenerator/suite_test.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ var (
6565
wantOverriddenTestResource []byte
6666

6767
// the content of the enveloped resources
68-
testEnvelopeWebhook, testEnvelopeResourceQuota []byte
68+
testEnvelopeResourceQuota, testEnvelopeResourceQuota2 []byte
6969
)
7070

7171
func TestAPIs(t *testing.T) {
@@ -365,15 +365,15 @@ func readTestManifests() {
365365
testPdb, err = yaml.ToJSON(rawByte)
366366
Expect(err).Should(Succeed())
367367

368-
By("Read EnvelopeWebhook")
369-
rawByte, err = os.ReadFile("manifests/webhook.yaml")
370-
Expect(err).Should(Succeed())
371-
testEnvelopeWebhook, err = yaml.ToJSON(rawByte)
372-
Expect(err).Should(Succeed())
373-
374368
By("Read ResourceQuota")
375369
rawByte, err = os.ReadFile("manifests/resourcequota.yaml")
376370
Expect(err).Should(Succeed())
377371
testEnvelopeResourceQuota, err = yaml.ToJSON(rawByte)
378372
Expect(err).Should(Succeed())
373+
374+
By("Read ResourceQuota2")
375+
rawByte, err = os.ReadFile("manifests/resourcequota2.yaml")
376+
Expect(err).Should(Succeed())
377+
testEnvelopeResourceQuota2, err = yaml.ToJSON(rawByte)
378+
Expect(err).Should(Succeed())
379379
}

test/e2e/actuals_test.go

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -613,7 +613,11 @@ func resourcePlacementOverrideFailedConditions(generation int64) []metav1.Condit
613613
}
614614
}
615615

616-
func resourcePlacementWorkSynchronizedFailedConditions(generation int64) []metav1.Condition {
616+
func resourcePlacementWorkSynchronizedFailedConditions(generation int64, hasOverrides bool) []metav1.Condition {
617+
overridenCondReason := condition.OverrideNotSpecifiedReason
618+
if hasOverrides {
619+
overridenCondReason = condition.OverriddenSucceededReason
620+
}
617621
return []metav1.Condition{
618622
{
619623
Type: string(placementv1beta1.ResourceScheduledConditionType),
@@ -631,7 +635,7 @@ func resourcePlacementWorkSynchronizedFailedConditions(generation int64) []metav
631635
Type: string(placementv1beta1.ResourceOverriddenConditionType),
632636
Status: metav1.ConditionTrue,
633637
ObservedGeneration: generation,
634-
Reason: condition.OverriddenSucceededReason,
638+
Reason: overridenCondReason,
635639
},
636640
{
637641
Type: string(placementv1beta1.ResourceWorkSynchronizedConditionType),
@@ -642,7 +646,11 @@ func resourcePlacementWorkSynchronizedFailedConditions(generation int64) []metav
642646
}
643647
}
644648

645-
func crpWorkSynchronizedFailedConditions(generation int64) []metav1.Condition {
649+
func crpWorkSynchronizedFailedConditions(generation int64, hasOverrides bool) []metav1.Condition {
650+
overridenCondReason := condition.OverrideNotSpecifiedReason
651+
if hasOverrides {
652+
overridenCondReason = condition.OverriddenSucceededReason
653+
}
646654
return []metav1.Condition{
647655
{
648656
Type: string(placementv1beta1.ClusterResourcePlacementScheduledConditionType),
@@ -659,7 +667,7 @@ func crpWorkSynchronizedFailedConditions(generation int64) []metav1.Condition {
659667
{
660668
Type: string(placementv1beta1.ClusterResourcePlacementOverriddenConditionType),
661669
Status: metav1.ConditionTrue,
662-
Reason: condition.OverriddenSucceededReason,
670+
Reason: overridenCondReason,
663671
ObservedGeneration: generation,
664672
},
665673
{
@@ -782,17 +790,18 @@ func crpStatusWithWorkSynchronizedUpdatedFailedActual(
782790
}
783791

784792
var wantPlacementStatus []placementv1beta1.ResourcePlacementStatus
793+
hasOverrides := len(wantResourceOverrides) > 0 || len(wantClusterResourceOverrides) > 0
785794
for _, name := range wantSelectedClusters {
786795
wantPlacementStatus = append(wantPlacementStatus, placementv1beta1.ResourcePlacementStatus{
787796
ClusterName: name,
788-
Conditions: resourcePlacementWorkSynchronizedFailedConditions(crp.Generation),
797+
Conditions: resourcePlacementWorkSynchronizedFailedConditions(crp.Generation, hasOverrides),
789798
ApplicableResourceOverrides: wantResourceOverrides,
790799
ApplicableClusterResourceOverrides: wantClusterResourceOverrides,
791800
})
792801
}
793802

794803
wantStatus := placementv1beta1.ClusterResourcePlacementStatus{
795-
Conditions: crpWorkSynchronizedFailedConditions(crp.Generation),
804+
Conditions: crpWorkSynchronizedFailedConditions(crp.Generation, hasOverrides),
796805
PlacementStatuses: wantPlacementStatus,
797806
SelectedResources: wantSelectedResourceIdentifiers,
798807
ObservedResourceIndex: wantObservedResourceIndex,

0 commit comments

Comments
 (0)