Skip to content

Commit

Permalink
Ignore files with the LocalConfig annotation
Browse files Browse the repository at this point in the history
  • Loading branch information
mortent committed Sep 13, 2020
1 parent 41a715c commit a22f5fb
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 0 deletions.
8 changes: 8 additions & 0 deletions pkg/config/initoptions.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"sigs.k8s.io/cli-utils/pkg/common"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/kio/filters"
)

const (
Expand Down Expand Up @@ -177,6 +178,13 @@ func allInSameNamespace(packageDir string) (string, bool, error) {
if err != nil {
return "", false, err
}

// Filter out any resources with the LocalConfig annotation
nodes, err = (&filters.IsLocalConfig{}).Filter(nodes)
if err != nil {
return "", false, err
}

var ns string
for _, node := range nodes {
rm, err := node.GetMeta()
Expand Down
27 changes: 27 additions & 0 deletions pkg/config/initoptions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,16 @@ metadata:
name: objC
`)

var readFileD = []byte(`
apiVersion: v1
kind: Pod
metadata:
name: objD
namespace: namespaceD
annotations:
config.kubernetes.io/local-config: "true"
`)

func TestComplete(t *testing.T) {
tests := map[string]struct {
args []string
Expand Down Expand Up @@ -97,6 +107,23 @@ func TestComplete(t *testing.T) {
isError: false,
expectedNamespace: "foo",
},
"Resources with the LocalConfig annotation should be ignored": {
args: []string{},
files: map[string][]byte{
"b_test.yaml": readFileB,
"d_test.yaml": readFileD,
},
isError: false,
expectedNamespace: "namespaceB",
},
"If all resources have the LocalConfig annotation use the default namespace": {
args: []string{},
files: map[string][]byte{
"d_test.yaml": readFileD,
},
isError: false,
expectedNamespace: "foo",
},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
Expand Down
17 changes: 17 additions & 0 deletions pkg/manifestreader/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"k8s.io/kubectl/pkg/cmd/util"
"sigs.k8s.io/cli-utils/pkg/apply/solver"
"sigs.k8s.io/cli-utils/pkg/inventory"
"sigs.k8s.io/kustomize/kyaml/kio/filters"
)

// ManifestReader defines the interface for reading a set
Expand Down Expand Up @@ -122,3 +123,19 @@ func setNamespaces(factory util.Factory, infos []*resource.Info,

return nil
}

// filterLocalConfig returns a new slice of infos where all resources
// with the LocalConfig annotation is filtered out.
func filterLocalConfig(infos []*resource.Info) []*resource.Info {
var filterInfos []*resource.Info
for _, inf := range infos {
acc, _ := meta.Accessor(inf.Object)
// Ignoring the value of the LocalConfigAnnotation here. This is to be
// consistent with the behavior in the kyaml library:
// https://github.com/kubernetes-sigs/kustomize/blob/30b58e90a39485bc5724b2278651c5d26b815cb2/kyaml/kio/filters/local.go#L29
if _, found := acc.GetAnnotations()[filters.LocalConfigAnnotation]; !found {
filterInfos = append(filterInfos, inf)
}
}
return filterInfos
}
109 changes: 109 additions & 0 deletions pkg/manifestreader/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package manifestreader

import (
"fmt"
"testing"

"github.com/stretchr/testify/assert"
Expand All @@ -14,6 +15,8 @@ import (
"k8s.io/cli-runtime/pkg/resource"
cmdtesting "k8s.io/kubectl/pkg/cmd/testing"
"k8s.io/kubectl/pkg/scheme"
"sigs.k8s.io/cli-utils/pkg/object"
"sigs.k8s.io/kustomize/kyaml/kio/filters"
)

func TestSetNamespaces(t *testing.T) {
Expand Down Expand Up @@ -154,6 +157,78 @@ func TestSetNamespaces(t *testing.T) {
}
}

var (
depID = object.ObjMetadata{
GroupKind: schema.GroupKind{
Group: "apps",
Kind: "Deployment",
},
Namespace: "default",
Name: "foo",
}

clusterRoleID = object.ObjMetadata{
GroupKind: schema.GroupKind{
Group: "rbac.authorization.k8s.io",
Kind: "ClusterRole",
},
Name: "bar",
}
)

func TestFilterLocalConfigs(t *testing.T) {
testCases := map[string]struct {
input []*resource.Info
expected []string
}{
"don't filter if no annotation": {
input: []*resource.Info{
objMetaToInfo(depID),
objMetaToInfo(clusterRoleID),
},
expected: []string{
depID.Name,
clusterRoleID.Name,
},
},
"filter all if all have annotation": {
input: []*resource.Info{
addAnnotation(t, objMetaToInfo(depID), filters.LocalConfigAnnotation, "true"),
addAnnotation(t, objMetaToInfo(clusterRoleID), filters.LocalConfigAnnotation, "false"),
},
expected: []string{},
},
"filter even if resource have other annotations": {
input: []*resource.Info{
addAnnotation(t,
addAnnotation(
t, objMetaToInfo(depID),
filters.LocalConfigAnnotation, "true"),
"my-annotation", "foo"),
},
expected: []string{},
},
}

for tn, tc := range testCases {
t.Run(tn, func(t *testing.T) {
res := filterLocalConfig(tc.input)

var names []string
for _, inf := range res {
names = append(names, inf.Name)
}

// Avoid test failures due to nil slice and empty slice
// not being equal.
if len(tc.expected) == 0 && len(names) == 0 {
return
}
assert.Equal(t, tc.expected, names)
})
}
}

func toInfo(gvk schema.GroupVersionKind, namespace string) *resource.Info {
return &resource.Info{
Namespace: namespace,
Expand Down Expand Up @@ -187,3 +262,37 @@ func toCRDInfo(gvk schema.GroupVersionKind, gk schema.GroupKind,
},
}
}

func objMetaToInfo(id object.ObjMetadata) *resource.Info {
return &resource.Info{
Namespace: id.Namespace,
Name: id.Name,
Object: &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": fmt.Sprintf("%s/v1", id.GroupKind.Group),
"kind": id.GroupKind.Kind,
"metadata": map[string]interface{}{
"namespace": id.Namespace,
"name": id.Name,
},
},
},
}
}

func addAnnotation(t *testing.T, info *resource.Info, name, val string) *resource.Info {
u := info.Object.(*unstructured.Unstructured)
annos, found, err := unstructured.NestedStringMap(u.Object, "metadata", "annotations")
if err != nil {
t.Fatal(err)
}
if !found {
annos = make(map[string]string)
}
annos[name] = val
err = unstructured.SetNestedStringMap(u.Object, annos, "metadata", "annotations")
if err != nil {
t.Fatal(err)
}
return info
}
1 change: 1 addition & 0 deletions pkg/manifestreader/path.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ func (p *PathManifestReader) Read() ([]*resource.Info, error) {
if err != nil {
return nil, err
}
infos = filterLocalConfig(infos)

err = setNamespaces(p.Factory, infos, p.Namespace, p.EnforceNamespace)
if err != nil {
Expand Down
1 change: 1 addition & 0 deletions pkg/manifestreader/stream.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ func (r *StreamManifestReader) Read() ([]*resource.Info, error) {
if err != nil {
return nil, err
}
infos = filterLocalConfig(infos)

err = setNamespaces(r.Factory, infos, r.Namespace, r.EnforceNamespace)
if err != nil {
Expand Down

0 comments on commit a22f5fb

Please sign in to comment.