Skip to content

Commit e4f2f05

Browse files
author
Ryan Zhang
committed
block crossnamespace envelop object
1 parent 8e42356 commit e4f2f05

File tree

2 files changed

+157
-5
lines changed

2 files changed

+157
-5
lines changed

pkg/controllers/workgenerator/controller.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1245,11 +1245,20 @@ func extractResFromConfigMap(uConfigMap *unstructured.Unstructured) ([]fleetv1be
12451245
return nil, err
12461246
}
12471247
// the list order is not stable as the map traverse is random
1248-
for _, value := range configMap.Data {
1248+
for key, value := range configMap.Data {
1249+
// so we need to check the GVK and annotation of the selected resource
12491250
content, jsonErr := yaml.ToJSON([]byte(value))
12501251
if jsonErr != nil {
12511252
return nil, jsonErr
12521253
}
1254+
var uManifest unstructured.Unstructured
1255+
if unMarshallErr := uManifest.UnmarshalJSON(content); unMarshallErr != nil {
1256+
klog.ErrorS(unMarshallErr, "manifest has invalid content", "manifestKey", key, "envelopResource", klog.KObj(uConfigMap))
1257+
return nil, fmt.Errorf("the object with manifest key `%s` in evenlop config `%s` is malformatted, err: %w", key, klog.KObj(uConfigMap), unMarshallErr)
1258+
}
1259+
if len(uManifest.GetNamespace()) != 0 && uManifest.GetNamespace() != configMap.Namespace {
1260+
return nil, fmt.Errorf("the namespaced object `%s` in evenlop config `%s` is placed in a different namespace `%s` ", uManifest.GetName(), klog.KObj(uConfigMap), uManifest.GetNamespace())
1261+
}
12531262
manifests = append(manifests, fleetv1beta1.Manifest{
12541263
RawExtension: runtime.RawExtension{Raw: content},
12551264
})

pkg/controllers/workgenerator/controller_test.go

Lines changed: 147 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,15 @@ import (
1111
"testing"
1212
"time"
1313

14+
"github.com/google/go-cmp/cmp"
15+
"github.com/google/go-cmp/cmp/cmpopts"
1416
appsv1 "k8s.io/api/apps/v1"
1517
k8serrors "k8s.io/apimachinery/pkg/api/errors"
1618
"k8s.io/apimachinery/pkg/api/meta"
19+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1720
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
1821
"k8s.io/apimachinery/pkg/runtime"
1922
"k8s.io/apimachinery/pkg/runtime/schema"
20-
21-
"github.com/google/go-cmp/cmp"
22-
"github.com/google/go-cmp/cmp/cmpopts"
23-
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2423
"k8s.io/client-go/tools/record"
2524
"k8s.io/utils/ptr"
2625
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -148,6 +147,150 @@ func TestGetWorkNamePrefixFromSnapshotName(t *testing.T) {
148147
}
149148
}
150149

150+
func TestExtractResFromConfigMap(t *testing.T) {
151+
tests := map[string]struct {
152+
uConfigMap *unstructured.Unstructured
153+
want []fleetv1beta1.Manifest
154+
wantErr bool
155+
}{
156+
"valid config map with no entries": {
157+
uConfigMap: &unstructured.Unstructured{
158+
Object: map[string]interface{}{
159+
"apiVersion": "v1",
160+
"kind": "ConfigMap",
161+
},
162+
},
163+
want: []fleetv1beta1.Manifest{},
164+
wantErr: false,
165+
},
166+
"config map with invalid JSON content": {
167+
uConfigMap: &unstructured.Unstructured{
168+
Object: map[string]interface{}{
169+
"apiVersion": "v1",
170+
"kind": "ConfigMap",
171+
"metadata": map[string]interface{}{
172+
"name": "test-config",
173+
"namespace": "default",
174+
},
175+
"data": map[string]interface{}{
176+
"invalid": "{invalid-json}",
177+
},
178+
},
179+
},
180+
want: nil,
181+
wantErr: true,
182+
},
183+
"config map with namespaced resource in different namespace": {
184+
uConfigMap: &unstructured.Unstructured{
185+
Object: map[string]interface{}{
186+
"apiVersion": "v1",
187+
"kind": "ConfigMap",
188+
"metadata": map[string]interface{}{
189+
"name": "test-config",
190+
"namespace": "default",
191+
},
192+
"data": map[string]interface{}{
193+
"resource": `{"apiVersion": "v1", "kind": "Pod", "metadata": {"name": "test-pod", "namespace": "other-namespace"}}`,
194+
},
195+
},
196+
},
197+
want: nil,
198+
wantErr: true,
199+
},
200+
"config map with valid and invalid entries": {
201+
uConfigMap: &unstructured.Unstructured{
202+
Object: map[string]interface{}{
203+
"apiVersion": "v1",
204+
"kind": "ConfigMap",
205+
"metadata": map[string]interface{}{
206+
"name": "test-config",
207+
"namespace": "default",
208+
},
209+
"data": map[string]interface{}{
210+
"valid": `{"apiVersion": "v1", "kind": "Pod", "metadata": {"name": "test-pod", "namespace": "default"}}`,
211+
"invalid": "{invalid-json}",
212+
},
213+
},
214+
},
215+
want: nil,
216+
wantErr: true,
217+
},
218+
"config map with cluster and namespace scoped data": {
219+
uConfigMap: &unstructured.Unstructured{
220+
Object: map[string]interface{}{
221+
"apiVersion": "v1",
222+
"kind": "ConfigMap",
223+
"metadata": map[string]interface{}{
224+
"name": "test-config",
225+
"namespace": "default",
226+
},
227+
"data": map[string]interface{}{
228+
"resource": `{"apiVersion": "v1", "kind": "Pod", "metadata": {"name": "test-pod", "namespace": "default"}}`,
229+
"resource2": `{"apiVersion": "v1", "kind": "ClusterRole", "metadata": {"name": "test-role"}}`,
230+
},
231+
},
232+
},
233+
want: []fleetv1beta1.Manifest{
234+
{RawExtension: runtime.RawExtension{Raw: []byte(`{"apiVersion": "v1", "kind": "Pod", "metadata": {"name": "test-pod", "namespace": "default"}}`)}},
235+
{RawExtension: runtime.RawExtension{Raw: []byte(`{"apiVersion": "v1", "kind": "ClusterRole", "metadata": {"name": "test-role"}}`)}},
236+
},
237+
wantErr: false,
238+
},
239+
"config map with cluster scoped and cross namespaced resources data": {
240+
uConfigMap: &unstructured.Unstructured{
241+
Object: map[string]interface{}{
242+
"apiVersion": "v1",
243+
"kind": "ConfigMap",
244+
"metadata": map[string]interface{}{
245+
"name": "test-config",
246+
"namespace": "default",
247+
},
248+
"data": map[string]interface{}{
249+
"resource": `{"apiVersion": "v1", "kind": "Pod", "metadata": {"name": "test-pod", "namespace": "not-default"}}`,
250+
"resource2": `{"apiVersion": "v1", "kind": "ClusterRole", "metadata": {"name": "test-role"}}`,
251+
},
252+
},
253+
},
254+
want: nil,
255+
wantErr: true,
256+
},
257+
"config map with valid entries in different order": {
258+
uConfigMap: &unstructured.Unstructured{
259+
Object: map[string]interface{}{
260+
"apiVersion": "v1",
261+
"kind": "ConfigMap",
262+
"metadata": map[string]interface{}{
263+
"name": "test-config",
264+
"namespace": "default",
265+
},
266+
"data": map[string]interface{}{
267+
"resource2": `{"apiVersion": "v1", "kind": "Pod", "metadata": {"name": "test-pod1", "namespace": "default"}}`,
268+
"resource1": `{"apiVersion": "v1", "kind": "Pod", "metadata": {"name": "test-pod2", "namespace": "default"}}`,
269+
},
270+
},
271+
},
272+
want: []fleetv1beta1.Manifest{
273+
{RawExtension: runtime.RawExtension{Raw: []byte(`{"apiVersion": "v1", "kind": "Pod", "metadata": {"name": "test-pod2", "namespace": "default"}}`)}},
274+
{RawExtension: runtime.RawExtension{Raw: []byte(`{"apiVersion": "v1", "kind": "Pod", "metadata": {"name": "test-pod1", "namespace": "default"}}`)}},
275+
},
276+
wantErr: false,
277+
},
278+
}
279+
280+
for name, tt := range tests {
281+
t.Run(name, func(t *testing.T) {
282+
got, err := extractResFromConfigMap(tt.uConfigMap)
283+
if (err != nil) != tt.wantErr {
284+
t.Errorf("extractResFromConfigMap() error = %v, wantErr %v", err, tt.wantErr)
285+
return
286+
}
287+
if diff := cmp.Diff(tt.want, got); diff != "" {
288+
t.Errorf("extractResFromConfigMap() mismatch (-want +got):\n%s", diff)
289+
}
290+
})
291+
}
292+
}
293+
151294
func TestUpsertWork(t *testing.T) {
152295
workName := "work"
153296
namespace := "default"

0 commit comments

Comments
 (0)