Skip to content

Commit 8174825

Browse files
committed
Allow resources in multiple namespaces
1 parent d7dd7cf commit 8174825

File tree

3 files changed

+66
-86
lines changed

3 files changed

+66
-86
lines changed

pkg/apply/applier.go

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@ package apply
55

66
import (
77
"context"
8-
"fmt"
98
"sort"
109
"time"
1110

1211
"github.com/go-errors/errors"
1312
"github.com/spf13/cobra"
13+
v1 "k8s.io/api/core/v1"
14+
"k8s.io/apimachinery/pkg/api/meta"
1415
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1516
"k8s.io/cli-runtime/pkg/genericclioptions"
1617
"k8s.io/cli-runtime/pkg/resource"
@@ -167,6 +168,13 @@ func (a *Applier) prepareObjects(infos []*resource.Info) (*ResourceObjects, erro
167168
if err != nil {
168169
return nil, err
169170
}
171+
172+
// Verify that the set of resources does not include the namespace
173+
// in which we will place the inventory.
174+
if err := validateNamespace(localInv, localInfos); err != nil {
175+
return nil, err
176+
}
177+
170178
currentObjs, err := object.InfosToObjMetas(localInfos)
171179
if err != nil {
172180
return nil, err
@@ -180,11 +188,7 @@ func (a *Applier) prepareObjects(infos []*resource.Info) (*ResourceObjects, erro
180188
}
181189
// Sort order for applied resources.
182190
sort.Sort(ordering.SortableInfos(localInfos))
183-
// TODO(seans3): Remove this single namespace requirement once we've
184-
// implemented multi-namespace apply.
185-
if !validateNamespace(localInfos) {
186-
return nil, fmt.Errorf("objects have differing namespaces")
187-
}
191+
188192
return &ResourceObjects{
189193
LocalInv: localInv,
190194
Resources: localInfos,
@@ -367,22 +371,23 @@ func handleError(eventChannel chan event.Event, err error) {
367371
}
368372
}
369373

374+
var coreV1Namespace = v1.SchemeGroupVersion.WithKind("Namespace")
375+
370376
// validateNamespace returns true if all the objects in the passed
371377
// infos parameter have the same namespace; false otherwise. Ignores
372378
// cluster-scoped resources.
373-
func validateNamespace(infos []*resource.Info) bool {
374-
currentNamespace := metav1.NamespaceNone
379+
func validateNamespace(inv *resource.Info, infos []*resource.Info) error {
380+
invAcc, _ := meta.Accessor(inv.Object)
381+
invNamespace := invAcc.GetNamespace()
382+
375383
for _, info := range infos {
376-
// Ignore cluster-scoped resources.
377-
if info.Namespaced() {
378-
// If the current namespace has not been set--then set it.
379-
if currentNamespace == metav1.NamespaceNone {
380-
currentNamespace = info.Namespace
381-
}
382-
if currentNamespace != info.Namespace {
383-
return false
384+
acc, _ := meta.Accessor(info.Object)
385+
gvk := info.Object.GetObjectKind().GroupVersionKind()
386+
if gvk == coreV1Namespace && acc.GetName() == invNamespace {
387+
return &inventory.InventoryNamespaceInSet{
388+
Namespace: invNamespace,
384389
}
385390
}
386391
}
387-
return true
392+
return nil
388393
}

pkg/apply/applier_test.go

Lines changed: 31 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -356,24 +356,6 @@ var obj3Info = &resource.Info{
356356
},
357357
}
358358

359-
var defaultObjInfo = &resource.Info{
360-
Namespace: metav1.NamespaceDefault,
361-
Name: "default-obj",
362-
Mapping: &meta.RESTMapping{
363-
Scope: meta.RESTScopeNamespace,
364-
},
365-
Object: &unstructured.Unstructured{
366-
Object: map[string]interface{}{
367-
"apiVersion": "v1",
368-
"kind": "Pod",
369-
"metadata": map[string]interface{}{
370-
"name": "default-obj",
371-
"namespace": metav1.NamespaceDefault,
372-
},
373-
},
374-
},
375-
}
376-
377359
var clusterScopedObjInfo = &resource.Info{
378360
Name: "cluster-scoped-1",
379361
Mapping: &meta.RESTMapping{
@@ -390,75 +372,55 @@ var clusterScopedObjInfo = &resource.Info{
390372
},
391373
}
392374

393-
var clusterScopedObj2Info = &resource.Info{
394-
Name: "cluster-scoped-2",
395-
Mapping: &meta.RESTMapping{
396-
Scope: meta.RESTScopeRoot,
397-
},
398-
Object: &unstructured.Unstructured{
399-
Object: map[string]interface{}{
400-
"apiVersion": "rbac.authorization.k8s.io/v1",
401-
"kind": "ClusterRoleBinding",
402-
"metadata": map[string]interface{}{
403-
"name": "cluster-scoped-2",
375+
func createNamespaceInfo(ns string) *resource.Info {
376+
return &resource.Info{
377+
Name: ns,
378+
Object: &unstructured.Unstructured{
379+
Object: map[string]interface{}{
380+
"apiVersion": "v1",
381+
"kind": "Namespace",
382+
"metadata": map[string]interface{}{
383+
"name": ns,
384+
},
404385
},
405386
},
406-
},
387+
}
407388
}
408389

409390
func TestValidateNamespace(t *testing.T) {
410391
tests := map[string]struct {
411392
objects []*resource.Info
412-
isValid bool
393+
err error
413394
}{
414395
"No resources is valid": {
415396
objects: []*resource.Info{},
416-
isValid: true,
397+
err: nil,
417398
},
418-
"One resource is valid": {
419-
objects: []*resource.Info{obj1Info},
420-
isValid: true,
421-
},
422-
"Two resources with same namespace is valid": {
399+
"Resources other than namespace is valid": {
423400
objects: []*resource.Info{obj1Info, obj2Info},
424-
isValid: true,
425-
},
426-
"Two resources with same namespace and cluster-scoped obj is valid": {
427-
objects: []*resource.Info{obj1Info, clusterScopedObjInfo, obj2Info},
428-
isValid: true,
401+
err: nil,
429402
},
430-
"Single cluster-scoped obj is valid": {
431-
objects: []*resource.Info{clusterScopedObjInfo},
432-
isValid: true,
403+
"Different namespace resource is valid": {
404+
objects: []*resource.Info{createNamespaceInfo("foo")},
405+
err: nil,
433406
},
434-
"Multiple cluster-scoped objs is valid": {
435-
objects: []*resource.Info{clusterScopedObjInfo, clusterScopedObj2Info},
436-
isValid: true,
437-
},
438-
"Two resources with differing namespaces is not valid": {
439-
objects: []*resource.Info{obj1Info, obj3Info},
440-
isValid: false,
441-
},
442-
"Two resources with differing namespaces and cluster-scoped obj is not valid": {
443-
objects: []*resource.Info{clusterScopedObjInfo, obj1Info, obj3Info},
444-
isValid: false,
445-
},
446-
"Three resources, one with differing namespace is not valid": {
447-
objects: []*resource.Info{obj1Info, obj2Info, obj3Info},
448-
isValid: false,
449-
},
450-
"Default namespace not equal to other namespaces": {
451-
objects: []*resource.Info{obj3Info, defaultObjInfo},
452-
isValid: false,
407+
"Same namespace resource is not valid": {
408+
objects: []*resource.Info{obj1Info, createNamespaceInfo(namespace)},
409+
err: inventory.InventoryNamespaceInSet{
410+
Namespace: namespace,
411+
},
453412
},
454413
}
455414

456415
for name, tc := range tests {
457416
t.Run(name, func(t *testing.T) {
458-
actualValid := validateNamespace(tc.objects)
459-
if tc.isValid != actualValid {
460-
t.Errorf("Expected valid namespace (%t), got (%t)", tc.isValid, actualValid)
417+
err := validateNamespace(inventoryObjInfo, tc.objects)
418+
419+
if tc.err != nil {
420+
assert.IsType(t, &inventory.InventoryNamespaceInSet{}, err)
421+
return
461422
}
423+
assert.NoError(t, err)
462424
})
463425
}
464426
}
@@ -523,9 +485,9 @@ func TestReadAndPrepareObjects(t *testing.T) {
523485
pruneIds: []*resource.Info{},
524486
isError: false,
525487
},
526-
"objects can not be in different namespaces": {
488+
"namespace resource for the namespace used in inventory is not allowed": {
527489
resources: []*resource.Info{obj1Info, obj2Info,
528-
inventoryObjInfo, obj3Info},
490+
inventoryObjInfo, obj3Info, createNamespaceInfo(namespace)},
529491
isError: true,
530492
},
531493
}

pkg/inventory/inventory_error.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ const multipleInventoryErrorStr = `Package has multiple inventory object templat
2020
The package should have one and only one inventory object template.
2121
`
2222

23+
const inventoryNamespaceInSet = `Inventory use namespace defined in package.
24+
25+
The inventory cannot use a namespace that is defined in the package.
26+
`
27+
2328
type NoInventoryObjError struct{}
2429

2530
func (g NoInventoryObjError) Error() string {
@@ -33,3 +38,11 @@ type MultipleInventoryObjError struct {
3338
func (g MultipleInventoryObjError) Error() string {
3439
return multipleInventoryErrorStr
3540
}
41+
42+
type InventoryNamespaceInSet struct {
43+
Namespace string
44+
}
45+
46+
func (g InventoryNamespaceInSet) Error() string {
47+
return inventoryNamespaceInSet
48+
}

0 commit comments

Comments
 (0)