cli-utils/pkg/object/unstructured_test.go

338 lines
7.6 KiB
Go

// Copyright 2021 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package object_test
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
cmdtesting "k8s.io/kubectl/pkg/cmd/testing"
. "github.com/fluxcd/cli-utils/pkg/object"
"github.com/fluxcd/cli-utils/pkg/testutil"
)
var rbac = `
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: test-cluster-role
`
var testCRD = `
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: test-crd
spec:
group: example.com
scope: Cluster
names:
kind: crontab
versions:
- name: v1
`
var testCRDv2 = `
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: test-crd
spec:
group: example.com
scope: Cluster
names:
kind: crontab
versions:
- name: v2
`
var testCR = `
apiVersion: example.com/v1
kind: crontab
metadata:
name: test-cr
`
var testNamespace = `
apiVersion: v1,
kind: Namespace
metadata:
name: test-namespace
`
var testPod = `
apiVersion: v1
kind: Pod
metadata:
name: test-pod
namespace: test-namespace
`
var defaultNamespacePod = `
apiVersion: v1
kind: Pod
metadata:
name: test-pod
namespace: default
`
func TestUnstructuredToObjMeta(t *testing.T) {
tests := map[string]struct {
obj *unstructured.Unstructured
expected ObjMetadata
}{
"test RBAC translation": {
obj: testutil.Unstructured(t, rbac),
expected: ObjMetadata{
Name: "test-cluster-role",
GroupKind: schema.GroupKind{
Group: "rbac.authorization.k8s.io",
Kind: "ClusterRole",
},
},
},
"test CRD translation": {
obj: testutil.Unstructured(t, testCRD),
expected: ObjMetadata{
Name: "test-crd",
GroupKind: schema.GroupKind{
Group: "apiextensions.k8s.io",
Kind: "CustomResourceDefinition",
},
},
},
"test pod translation": {
obj: testutil.Unstructured(t, testPod),
expected: ObjMetadata{
Name: "test-pod",
Namespace: "test-namespace",
GroupKind: schema.GroupKind{
Group: "",
Kind: "Pod",
},
},
},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
actual := UnstructuredToObjMetadata(tc.obj)
if tc.expected != actual {
t.Errorf("expected ObjMetadata (%s), got (%s)", tc.expected, actual)
}
})
}
}
func TestIsKindNamespace(t *testing.T) {
tests := map[string]struct {
obj *unstructured.Unstructured
isKindNamespace bool
}{
"cluster-scoped RBAC is not a namespace": {
obj: testutil.Unstructured(t, rbac),
isKindNamespace: false,
},
"test namespace is a namespace": {
obj: testutil.Unstructured(t, testNamespace),
isKindNamespace: true,
},
"test pod is not a namespace": {
obj: testutil.Unstructured(t, testPod),
isKindNamespace: false,
},
"default namespaced pod is not a namespace": {
obj: testutil.Unstructured(t, defaultNamespacePod),
isKindNamespace: false,
},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
actual := IsKindNamespace(tc.obj)
if tc.isKindNamespace != actual {
t.Errorf("expected IsKindNamespace (%t), got (%t) for (%s)",
tc.isKindNamespace, actual, tc.obj)
}
})
}
}
func TestIsCRD(t *testing.T) {
tests := map[string]struct {
obj *unstructured.Unstructured
isCRD bool
}{
"RBAC is not a CRD": {
obj: testutil.Unstructured(t, rbac),
isCRD: false,
},
"test namespace is not a CRD": {
obj: testutil.Unstructured(t, testNamespace),
isCRD: false,
},
"test CRD is a CRD": {
obj: testutil.Unstructured(t, testCRD),
isCRD: true,
},
"test pod is not a CRD": {
obj: testutil.Unstructured(t, testPod),
isCRD: false,
},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
actual := IsCRD(tc.obj)
if tc.isCRD != actual {
t.Errorf("expected IsCRD (%t), got (%t) for (%s)", tc.isCRD, actual, tc.obj)
}
})
}
}
func TestIsNamespaced(t *testing.T) {
tests := map[string]struct {
obj *unstructured.Unstructured
isNamespaced bool
}{
"cluster-scoped RBAC is not namespaced": {
obj: testutil.Unstructured(t, rbac),
isNamespaced: false,
},
"a CRD is cluster-scoped": {
obj: testutil.Unstructured(t, testCRD),
isNamespaced: false,
},
"a namespace is cluster-scoped": {
obj: testutil.Unstructured(t, testNamespace),
isNamespaced: false,
},
"pod is namespaced": {
obj: testutil.Unstructured(t, testPod),
isNamespaced: true,
},
"default namespaced pod is namespaced": {
obj: testutil.Unstructured(t, defaultNamespacePod),
isNamespaced: true,
},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
actual := IsNamespaced(tc.obj)
if tc.isNamespaced != actual {
t.Errorf("expected namespaced (%t), got (%t) for (%s)",
tc.isNamespaced, actual, tc.obj)
}
})
}
}
func TestGetCRDGroupKind(t *testing.T) {
tests := map[string]struct {
obj *unstructured.Unstructured
isCRD bool
groupKind string
}{
"RBAC is not a CRD": {
obj: testutil.Unstructured(t, rbac),
isCRD: false,
groupKind: "",
},
"pod is not a CRD": {
obj: testutil.Unstructured(t, testPod),
isCRD: false,
groupKind: "",
},
"testCRD has example.com/crontab GroupKind": {
obj: testutil.Unstructured(t, testCRD),
isCRD: true,
groupKind: "crontab.example.com",
},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
actualGroupKind, actualIsCRD := GetCRDGroupKind(tc.obj)
if tc.isCRD != actualIsCRD {
t.Errorf("expected IsCRD (%t), got (%t) for (%s)", tc.isCRD, actualIsCRD, tc.obj)
}
if tc.groupKind != actualGroupKind.String() {
t.Errorf("expected CRD GroupKind (%s), got (%s) for (%s)",
tc.groupKind, actualGroupKind, tc.obj)
}
})
}
}
func TestLookupResourceScope(t *testing.T) {
testCases := map[string]struct {
resource *unstructured.Unstructured
crds []*unstructured.Unstructured
expectedScope meta.RESTScope
expectedErr error
}{
"regular resource": {
resource: testutil.Unstructured(t, testPod),
expectedScope: meta.RESTScopeNamespace,
},
"CR not found in the RESTMapper or the provided CRDs": {
resource: testutil.Unstructured(t, testCR),
expectedErr: &UnknownTypeError{
GroupVersionKind: schema.GroupVersionKind{
Group: "example.com",
Version: "v1",
Kind: "crontab",
},
},
},
"CR not found in the RESTMapper or the provided CRDs because version is missing": {
resource: testutil.Unstructured(t, testCR),
crds: []*unstructured.Unstructured{
testutil.Unstructured(t, testCRDv2),
},
expectedErr: &UnknownTypeError{
GroupVersionKind: schema.GroupVersionKind{
Group: "example.com",
Version: "v1",
Kind: "crontab",
},
},
},
"CR found in in the provided CRDs": {
resource: testutil.Unstructured(t, testCR),
crds: []*unstructured.Unstructured{
testutil.Unstructured(t, testCRD),
},
expectedScope: meta.RESTScopeRoot,
},
}
for tn, tc := range testCases {
t.Run(tn, func(t *testing.T) {
tf := cmdtesting.NewTestFactory().WithNamespace("test-ns")
defer tf.Cleanup()
mapper, err := tf.ToRESTMapper()
require.NoError(t, err)
scope, err := LookupResourceScope(tc.resource, tc.crds, mapper)
if tc.expectedErr != nil {
require.Equal(t, tc.expectedErr, err)
return
}
require.NoError(t, err)
assert.Equal(t, tc.expectedScope, scope)
})
}
}