Move resid package from api to kyaml

This commit is contained in:
monopole
2021-04-30 20:39:32 -07:00
parent 5a8a4d47a5
commit c8dddac5b9
52 changed files with 56 additions and 55 deletions

232
kyaml/resid/gvk.go Normal file
View File

@@ -0,0 +1,232 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package resid
import (
"strings"
"sigs.k8s.io/kustomize/kyaml/openapi"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
// Gvk identifies a Kubernetes API type.
// https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md
type Gvk struct {
Group string `json:"group,omitempty" yaml:"group,omitempty"`
Version string `json:"version,omitempty" yaml:"version,omitempty"`
Kind string `json:"kind,omitempty" yaml:"kind,omitempty"`
}
// FromKind makes a Gvk with only the kind specified.
func FromKind(k string) Gvk {
return Gvk{
Kind: k,
}
}
// ParseGroupVersion parses a KRM metadata apiVersion field.
func ParseGroupVersion(apiVersion string) (group, version string) {
if i := strings.Index(apiVersion, "/"); i > -1 {
return apiVersion[:i], apiVersion[i+1:]
}
return "", apiVersion
}
// GvkFromString makes a Gvk from the output of Gvk.String().
func GvkFromString(s string) Gvk {
values := strings.Split(s, fieldSep)
if len(values) != 3 {
// ...then the string didn't come from Gvk.String().
return Gvk{
Group: noGroup,
Version: noVersion,
Kind: noKind,
}
}
g := values[0]
if g == noGroup {
g = ""
}
v := values[1]
if v == noVersion {
v = ""
}
k := values[2]
if k == noKind {
k = ""
}
return Gvk{
Group: g,
Version: v,
Kind: k,
}
}
// Values that are brief but meaningful in logs.
const (
noGroup = "~G"
noVersion = "~V"
noKind = "~K"
fieldSep = "_"
)
// String returns a string representation of the GVK.
func (x Gvk) String() string {
g := x.Group
if g == "" {
g = noGroup
}
v := x.Version
if v == "" {
v = noVersion
}
k := x.Kind
if k == "" {
k = noKind
}
return strings.Join([]string{g, v, k}, fieldSep)
}
// ApiVersion returns the combination of Group and Version
func (x Gvk) ApiVersion() string {
if x.Group == "" {
return x.Version
}
return x.Group + "/" + x.Version
}
// StringWoEmptyField returns a string representation of the GVK. Non-exist
// fields will be omitted.
func (x Gvk) StringWoEmptyField() string {
var s []string
if x.Group != "" {
s = append(s, x.Group)
}
if x.Version != "" {
s = append(s, x.Version)
}
if x.Kind != "" {
s = append(s, x.Kind)
}
return strings.Join(s, fieldSep)
}
// Equals returns true if the Gvk's have equal fields.
func (x Gvk) Equals(o Gvk) bool {
return x.Group == o.Group && x.Version == o.Version && x.Kind == o.Kind
}
// An attempt to order things to help k8s, e.g.
// a Service should come before things that refer to it.
// Namespace should be first.
// In some cases order just specified to provide determinism.
var orderFirst = []string{
"Namespace",
"ResourceQuota",
"StorageClass",
"CustomResourceDefinition",
"ServiceAccount",
"PodSecurityPolicy",
"Role",
"ClusterRole",
"RoleBinding",
"ClusterRoleBinding",
"ConfigMap",
"Secret",
"Endpoints",
"Service",
"LimitRange",
"PriorityClass",
"PersistentVolume",
"PersistentVolumeClaim",
"Deployment",
"StatefulSet",
"CronJob",
"PodDisruptionBudget",
}
var orderLast = []string{
"MutatingWebhookConfiguration",
"ValidatingWebhookConfiguration",
}
var typeOrders = func() map[string]int {
m := map[string]int{}
for i, n := range orderFirst {
m[n] = -len(orderFirst) + i
}
for i, n := range orderLast {
m[n] = 1 + i
}
return m
}()
// IsLessThan returns true if self is less than the argument.
func (x Gvk) IsLessThan(o Gvk) bool {
indexI := typeOrders[x.Kind]
indexJ := typeOrders[o.Kind]
if indexI != indexJ {
return indexI < indexJ
}
return x.String() < o.String()
}
// IsSelected returns true if `selector` selects `x`; otherwise, false.
// If `selector` and `x` are the same, return true.
// If `selector` is nil, it is considered a wildcard match, returning true.
// If selector fields are empty, they are considered wildcards matching
// anything in the corresponding fields, e.g.
//
// this item:
// <Group: "extensions", Version: "v1beta1", Kind: "Deployment">
//
// is selected by
// <Group: "", Version: "", Kind: "Deployment">
//
// but rejected by
// <Group: "apps", Version: "", Kind: "Deployment">
//
func (x Gvk) IsSelected(selector *Gvk) bool {
if selector == nil {
return true
}
if len(selector.Group) > 0 {
if x.Group != selector.Group {
return false
}
}
if len(selector.Version) > 0 {
if x.Version != selector.Version {
return false
}
}
if len(selector.Kind) > 0 {
if x.Kind != selector.Kind {
return false
}
}
return true
}
// toKyamlTypeMeta returns a yaml.TypeMeta from x's information.
func (x Gvk) toKyamlTypeMeta() yaml.TypeMeta {
var apiVersion strings.Builder
if x.Group != "" {
apiVersion.WriteString(x.Group)
apiVersion.WriteString("/")
}
apiVersion.WriteString(x.Version)
return yaml.TypeMeta{
APIVersion: apiVersion.String(),
Kind: x.Kind,
}
}
// IsNamespaceableKind returns true if x is a namespaceable Gvk,
// e.g. instances of Pod and Deployment are namespaceable,
// but instances of Node and Namespace are not namespaceable.
// Alternative name for this method: IsNotClusterScoped.
// Implements https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/#not-all-objects-are-in-a-namespace
func (x Gvk) IsNamespaceableKind() bool {
isNamespaceScoped, found := openapi.IsNamespaceScoped(x.toKyamlTypeMeta())
return !found || isNamespaceScoped
}

297
kyaml/resid/gvk_test.go Normal file
View File

@@ -0,0 +1,297 @@
// Copyright 2018 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package resid
import (
"testing"
"github.com/stretchr/testify/assert"
)
var equalsTests = []struct {
x1 Gvk
x2 Gvk
}{
{Gvk{Group: "a", Version: "b", Kind: "c"},
Gvk{Group: "a", Version: "b", Kind: "c"}},
{Gvk{Version: "b", Kind: "c"},
Gvk{Version: "b", Kind: "c"}},
{Gvk{Kind: "c"},
Gvk{Kind: "c"}},
}
func TestEquals(t *testing.T) {
for _, hey := range equalsTests {
if !hey.x1.Equals(hey.x2) {
t.Fatalf("%v should equal %v", hey.x1, hey.x2)
}
}
}
var lessThanTests = []struct {
x1 Gvk
x2 Gvk
}{
{Gvk{Group: "a", Version: "b", Kind: "CustomResourceDefinition"},
Gvk{Group: "a", Version: "b", Kind: "RoleBinding"}},
{Gvk{Group: "a", Version: "b", Kind: "Namespace"},
Gvk{Group: "a", Version: "b", Kind: "ClusterRole"}},
{Gvk{Group: "a", Version: "b", Kind: "a"},
Gvk{Group: "a", Version: "b", Kind: "b"}},
{Gvk{Group: "a", Version: "b", Kind: "Namespace"},
Gvk{Group: "a", Version: "c", Kind: "Namespace"}},
{Gvk{Group: "a", Version: "c", Kind: "Namespace"},
Gvk{Group: "b", Version: "c", Kind: "Namespace"}},
{Gvk{Group: "b", Version: "c", Kind: "Namespace"},
Gvk{Group: "a", Version: "c", Kind: "ClusterRole"}},
{Gvk{Group: "a", Version: "c", Kind: "Namespace"},
Gvk{Group: "a", Version: "b", Kind: "ClusterRole"}},
{Gvk{Group: "a", Version: "d", Kind: "Namespace"},
Gvk{Group: "b", Version: "c", Kind: "Namespace"}},
{Gvk{Group: "a", Version: "b", Kind: orderFirst[len(orderFirst)-1]},
Gvk{Group: "a", Version: "b", Kind: orderLast[0]}},
{Gvk{Group: "a", Version: "b", Kind: orderFirst[len(orderFirst)-1]},
Gvk{Group: "a", Version: "b", Kind: "CustomKindX"}},
{Gvk{Group: "a", Version: "b", Kind: "CustomKindX"},
Gvk{Group: "a", Version: "b", Kind: orderLast[0]}},
{Gvk{Group: "a", Version: "b", Kind: "CustomKindA"},
Gvk{Group: "a", Version: "b", Kind: "CustomKindB"}},
{Gvk{Group: "a", Version: "b", Kind: "CustomKindX"},
Gvk{Group: "a", Version: "b", Kind: "MutatingWebhookConfiguration"}},
{Gvk{Group: "a", Version: "b", Kind: "MutatingWebhookConfiguration"},
Gvk{Group: "a", Version: "b", Kind: "ValidatingWebhookConfiguration"}},
{Gvk{Group: "a", Version: "b", Kind: "CustomKindX"},
Gvk{Group: "a", Version: "b", Kind: "ValidatingWebhookConfiguration"}},
{Gvk{Group: "a", Version: "b", Kind: "APIService"},
Gvk{Group: "a", Version: "b", Kind: "ValidatingWebhookConfiguration"}},
{Gvk{Group: "a", Version: "b", Kind: "Service"},
Gvk{Group: "a", Version: "b", Kind: "APIService"}},
{Gvk{Group: "a", Version: "b", Kind: "Endpoints"},
Gvk{Group: "a", Version: "b", Kind: "Service"}},
}
func TestIsLessThan1(t *testing.T) {
for _, hey := range lessThanTests {
if !hey.x1.IsLessThan(hey.x2) {
t.Fatalf("%v should be less than %v", hey.x1, hey.x2)
}
if hey.x2.IsLessThan(hey.x1) {
t.Fatalf("%v should not be less than %v", hey.x2, hey.x1)
}
}
}
var stringTests = []struct {
x Gvk
s string
r string
}{
{Gvk{}, "~G_~V_~K", ""},
{Gvk{Kind: "k"}, "~G_~V_k", "k"},
{Gvk{Version: "v"}, "~G_v_~K", "v"},
{Gvk{Version: "v", Kind: "k"}, "~G_v_k", "v_k"},
{Gvk{Group: "g"}, "g_~V_~K", "g"},
{Gvk{Group: "g", Kind: "k"}, "g_~V_k", "g_k"},
{Gvk{Group: "g", Version: "v"}, "g_v_~K", "g_v"},
{Gvk{Group: "g", Version: "v", Kind: "k"}, "g_v_k", "g_v_k"},
}
func TestString(t *testing.T) {
for _, hey := range stringTests {
assert.Equal(t, hey.s, hey.x.String())
}
}
func TestGvkFromString(t *testing.T) {
for _, hey := range stringTests {
assert.Equal(t, hey.x, GvkFromString(hey.s))
}
}
func TestApiVersion(t *testing.T) {
for _, hey := range []struct {
x Gvk
exp string
}{
{Gvk{}, ""},
{Gvk{Kind: "k"}, ""},
{Gvk{Version: "v"}, "v"},
{Gvk{Version: "v", Kind: "k"}, "v"},
{Gvk{Group: "g"}, "g/"},
{Gvk{Group: "g", Kind: "k"}, "g/"},
{Gvk{Group: "g", Version: "v"}, "g/v"},
{Gvk{Group: "g", Version: "v", Kind: "k"}, "g/v"},
} {
assert.Equal(t, hey.exp, hey.x.ApiVersion())
}
}
func TestStringWoEmptyField(t *testing.T) {
for _, hey := range stringTests {
assert.Equal(t, hey.r, hey.x.StringWoEmptyField())
}
}
func TestParseGroupVersion(t *testing.T) {
tests := []struct {
input string
g string
v string
}{
{input: "", g: "", v: ""},
{input: "v1", g: "", v: "v1"},
{input: "apps/v1", g: "apps", v: "v1"},
{input: "/v1", g: "", v: "v1"},
{input: "apps/", g: "apps", v: ""},
{input: "/apps/", g: "", v: "apps/"},
}
for _, tc := range tests {
g, v := ParseGroupVersion(tc.input)
assert.Equal(t, tc.g, g, tc.input)
assert.Equal(t, tc.v, v, tc.input)
}
}
func TestSelectByGVK(t *testing.T) {
type testCase struct {
description string
in Gvk
filter *Gvk
expected bool
}
testCases := []testCase{
{
description: "nil filter",
in: Gvk{},
filter: nil,
expected: true,
},
{
description: "gvk matches",
in: Gvk{
Group: "group1",
Version: "version1",
Kind: "kind1",
},
filter: &Gvk{
Group: "group1",
Version: "version1",
Kind: "kind1",
},
expected: true,
},
{
description: "group doesn't matches",
in: Gvk{
Group: "group1",
Version: "version1",
Kind: "kind1",
},
filter: &Gvk{
Group: "group2",
Version: "version1",
Kind: "kind1",
},
expected: false,
},
{
description: "version doesn't matches",
in: Gvk{
Group: "group1",
Version: "version1",
Kind: "kind1",
},
filter: &Gvk{
Group: "group1",
Version: "version2",
Kind: "kind1",
},
expected: false,
},
{
description: "kind doesn't matches",
in: Gvk{
Group: "group1",
Version: "version1",
Kind: "kind1",
},
filter: &Gvk{
Group: "group1",
Version: "version1",
Kind: "kind2",
},
expected: false,
},
{
description: "no version in filter",
in: Gvk{
Group: "group1",
Version: "version1",
Kind: "kind1",
},
filter: &Gvk{
Group: "group1",
Version: "",
Kind: "kind1",
},
expected: true,
},
{
description: "only kind is set in filter",
in: Gvk{
Group: "group1",
Version: "version1",
Kind: "kind1",
},
filter: &Gvk{
Group: "",
Version: "",
Kind: "kind1",
},
expected: true,
},
}
for _, tc := range testCases {
filtered := tc.in.IsSelected(tc.filter)
assert.Equal(t, tc.expected, filtered, tc.description)
}
}
func TestIsNamespaceableKind(t *testing.T) {
testCases := []struct {
name string
gvk Gvk
expected bool
}{
{
"namespaceable resource",
Gvk{Group: "apps", Version: "v1", Kind: "Deployment"},
true,
},
{
"clusterscoped resource",
Gvk{Group: "", Version: "v1", Kind: "Namespace"},
false,
},
{
"unknown resource (should default to namespaceable)",
Gvk{Group: "example1.com", Version: "v1", Kind: "Bar"},
true,
},
{
"unknown resource (should default to namespaceable)",
Gvk{Group: "apps", Version: "v1", Kind: "ClusterRoleBinding"},
true,
},
}
for i := range testCases {
test := testCases[i]
t.Run(test.name, func(t *testing.T) {
isNamespaceable := test.gvk.IsNamespaceableKind()
assert.Equal(t, test.expected, isNamespaceable)
})
}
}

134
kyaml/resid/resid.go Normal file
View File

@@ -0,0 +1,134 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package resid
import (
"strings"
)
// ResId is an identifier of a k8s resource object.
type ResId struct {
// Gvk of the resource.
Gvk `json:",inline,omitempty" yaml:",inline,omitempty"`
// Name of the resource before transformation.
Name string `json:"name,omitempty" yaml:"name,omitempty"`
// Namespace the resource belongs to.
// An untransformed resource has no namespace.
// A fully transformed resource has the namespace
// from the top most overlay.
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
}
// NewResIdWithNamespace creates new ResId
// in a given namespace.
func NewResIdWithNamespace(k Gvk, n, ns string) ResId {
return ResId{Gvk: k, Name: n, Namespace: ns}
}
// NewResId creates new ResId.
func NewResId(k Gvk, n string) ResId {
return ResId{Gvk: k, Name: n}
}
// NewResIdKindOnly creates a new ResId.
func NewResIdKindOnly(k string, n string) ResId {
return ResId{Gvk: FromKind(k), Name: n}
}
const (
noNamespace = "~X"
noName = "~N"
separator = "|"
TotallyNotANamespace = "_non_namespaceable_"
DefaultNamespace = "default"
)
// String of ResId based on GVK, name and prefix
func (id ResId) String() string {
ns := id.Namespace
if ns == "" {
ns = noNamespace
}
nm := id.Name
if nm == "" {
nm = noName
}
return strings.Join(
[]string{id.Gvk.String(), ns, nm}, separator)
}
func FromString(s string) ResId {
values := strings.Split(s, separator)
g := GvkFromString(values[0])
ns := values[1]
if ns == noNamespace {
ns = ""
}
nm := values[2]
if nm == noName {
nm = ""
}
return ResId{
Gvk: g,
Namespace: ns,
Name: nm,
}
}
// GvknString of ResId based on GVK and name
func (id ResId) GvknString() string {
return id.Gvk.String() + separator + id.Name
}
// GvknEquals returns true if the other id matches
// Group/Version/Kind/name.
func (id ResId) GvknEquals(o ResId) bool {
return id.Name == o.Name && id.Gvk.Equals(o.Gvk)
}
// IsSelectedBy returns true if self is selected by the argument.
func (id ResId) IsSelectedBy(selector ResId) bool {
return (selector.Name == "" || selector.Name == id.Name) &&
(selector.Namespace == "" || selector.IsNsEquals(id)) &&
id.Gvk.IsSelected(&selector.Gvk)
}
// Equals returns true if the other id matches
// namespace/Group/Version/Kind/name.
func (id ResId) Equals(o ResId) bool {
return id.IsNsEquals(o) && id.GvknEquals(o)
}
// IsNsEquals returns true if the id is in
// the same effective namespace.
func (id ResId) IsNsEquals(o ResId) bool {
return id.EffectiveNamespace() == o.EffectiveNamespace()
}
// IsInDefaultNs returns true if id is a namespaceable
// ResId and the Namespace is either not set or set
// to DefaultNamespace.
func (id ResId) IsInDefaultNs() bool {
return id.IsNamespaceableKind() && id.isPutativelyDefaultNs()
}
func (id ResId) isPutativelyDefaultNs() bool {
return id.Namespace == "" || id.Namespace == DefaultNamespace
}
// EffectiveNamespace returns a non-ambiguous, non-empty
// namespace for use in reporting and equality tests.
func (id ResId) EffectiveNamespace() string {
// The order of these checks matters.
if !id.IsNamespaceableKind() {
return TotallyNotANamespace
}
if id.isPutativelyDefaultNs() {
return DefaultNamespace
}
return id.Namespace
}

517
kyaml/resid/resid_test.go Normal file
View File

@@ -0,0 +1,517 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package resid
import (
"testing"
)
var resIdStringTests = []struct {
x ResId
s string
}{
{
ResId{
Namespace: "ns",
Gvk: Gvk{Group: "g", Version: "v", Kind: "k"},
Name: "nm",
},
"g_v_k|ns|nm",
},
{
ResId{
Namespace: "ns",
Gvk: Gvk{Version: "v", Kind: "k"},
Name: "nm",
},
"~G_v_k|ns|nm",
},
{
ResId{
Namespace: "ns",
Gvk: Gvk{Kind: "k"},
Name: "nm",
},
"~G_~V_k|ns|nm",
},
{
ResId{
Namespace: "ns",
Gvk: Gvk{},
Name: "nm",
},
"~G_~V_~K|ns|nm",
},
{
ResId{
Gvk: Gvk{},
Name: "nm",
},
"~G_~V_~K|~X|nm",
},
{
ResId{
Gvk: Gvk{},
Name: "nm",
},
"~G_~V_~K|~X|nm",
},
{
ResId{
Gvk: Gvk{},
},
"~G_~V_~K|~X|~N",
},
{
ResId{
Gvk: Gvk{},
},
"~G_~V_~K|~X|~N",
},
{
ResId{},
"~G_~V_~K|~X|~N",
},
}
func TestResIdString(t *testing.T) {
for _, hey := range resIdStringTests {
if hey.x.String() != hey.s {
t.Fatalf("Actual: %v, Expected: '%s'", hey.x, hey.s)
}
}
}
var gvknStringTests = []struct {
x ResId
s string
}{
{
ResId{
Namespace: "ns",
Gvk: Gvk{Group: "g", Version: "v", Kind: "k"},
Name: "nm",
},
"g_v_k|nm",
},
{
ResId{
Namespace: "ns",
Gvk: Gvk{Version: "v", Kind: "k"},
Name: "nm",
},
"~G_v_k|nm",
},
{
ResId{
Namespace: "ns",
Gvk: Gvk{Kind: "k"},
Name: "nm",
},
"~G_~V_k|nm",
},
{
ResId{
Namespace: "ns",
Gvk: Gvk{},
Name: "nm",
},
"~G_~V_~K|nm",
},
{
ResId{
Gvk: Gvk{},
Name: "nm",
},
"~G_~V_~K|nm",
},
{
ResId{
Gvk: Gvk{},
Name: "nm",
},
"~G_~V_~K|nm",
},
{
ResId{
Gvk: Gvk{},
},
"~G_~V_~K|",
},
{
ResId{
Gvk: Gvk{},
},
"~G_~V_~K|",
},
{
ResId{},
"~G_~V_~K|",
},
}
func TestGvknString(t *testing.T) {
for _, hey := range gvknStringTests {
if hey.x.GvknString() != hey.s {
t.Fatalf("Actual: %s, Expected: '%s'", hey.x.GvknString(), hey.s)
}
}
}
func TestResIdEquals(t *testing.T) {
var GvknEqualsTest = []struct {
id1 ResId
id2 ResId
gVknResult bool
nsEquals bool
equals bool
}{
{
id1: ResId{
Namespace: "X",
Gvk: Gvk{Group: "g", Version: "v", Kind: "k"},
Name: "nm",
},
id2: ResId{
Namespace: "X",
Gvk: Gvk{Group: "g", Version: "v", Kind: "k"},
Name: "nm",
},
gVknResult: true,
nsEquals: true,
equals: true,
},
{
id1: ResId{
Namespace: "X",
Gvk: Gvk{Group: "g", Version: "v", Kind: "k"},
Name: "nm",
},
id2: ResId{
Namespace: "Z",
Gvk: Gvk{Group: "g", Version: "v", Kind: "k"},
Name: "nm",
},
gVknResult: true,
nsEquals: false,
equals: false,
},
{
id1: ResId{
Namespace: "X",
Gvk: Gvk{Group: "g", Version: "v", Kind: "k"},
Name: "nm",
},
id2: ResId{
Gvk: Gvk{Group: "g", Version: "v", Kind: "k"},
Name: "nm",
},
gVknResult: true,
nsEquals: false,
equals: false,
},
{
id1: ResId{
Namespace: "X",
Gvk: Gvk{Version: "v", Kind: "k"},
Name: "nm",
},
id2: ResId{
Namespace: "Z",
Gvk: Gvk{Version: "v", Kind: "k"},
Name: "nm",
},
gVknResult: true,
nsEquals: false,
equals: false,
},
{
id1: ResId{
Namespace: "X",
Gvk: Gvk{Kind: "k"},
Name: "nm",
},
id2: ResId{
Namespace: "Z",
Gvk: Gvk{Kind: "k"},
Name: "nm",
},
gVknResult: true,
nsEquals: false,
equals: false,
},
{
id1: ResId{
Gvk: Gvk{Kind: "k"},
Name: "nm",
},
id2: ResId{
Gvk: Gvk{Kind: "k"},
Name: "nm2",
},
gVknResult: false,
nsEquals: true,
equals: false,
},
{
id1: ResId{
Gvk: Gvk{Kind: "k"},
Name: "nm",
},
id2: ResId{
Gvk: Gvk{Kind: "Node"},
Name: "nm",
},
gVknResult: false,
nsEquals: true,
equals: false,
},
{
id1: ResId{
Gvk: Gvk{Kind: "Node"},
Name: "nm1",
},
id2: ResId{
Gvk: Gvk{Kind: "Node"},
Name: "nm2",
},
gVknResult: false,
nsEquals: true,
equals: false,
},
{
id1: ResId{
Namespace: "default",
Gvk: Gvk{Kind: "k"},
Name: "nm1",
},
id2: ResId{
Gvk: Gvk{Kind: "k"},
Name: "nm2",
},
gVknResult: false,
nsEquals: true,
equals: false,
},
{
id1: ResId{
Namespace: "X",
Name: "nm",
},
id2: ResId{
Namespace: "Z",
Name: "nm",
},
gVknResult: true,
nsEquals: false,
equals: false,
},
}
for _, tst := range GvknEqualsTest {
if tst.id1.GvknEquals(tst.id2) != tst.gVknResult {
t.Fatalf("GvknEquals(\n%v,\n%v\n) should be %v",
tst.id1, tst.id2, tst.gVknResult)
}
if tst.id1.IsNsEquals(tst.id2) != tst.nsEquals {
t.Fatalf("IsNsEquals(\n%v,\n%v\n) should be %v",
tst.id1, tst.id2, tst.equals)
}
if tst.id1.Equals(tst.id2) != tst.equals {
t.Fatalf("Equals(\n%v,\n%v\n) should be %v",
tst.id1, tst.id2, tst.equals)
}
}
}
var ids = []ResId{
{
Namespace: "ns",
Gvk: Gvk{Group: "g", Version: "v", Kind: "k"},
Name: "nm",
},
{
Namespace: "ns",
Gvk: Gvk{Version: "v", Kind: "k"},
Name: "nm",
},
{
Namespace: "ns",
Gvk: Gvk{Kind: "k"},
Name: "nm",
},
{
Namespace: "ns",
Gvk: Gvk{},
Name: "nm",
},
{
Gvk: Gvk{},
Name: "nm",
},
{
Gvk: Gvk{},
Name: "nm",
},
{
Gvk: Gvk{},
},
}
func TestResIdIsSelected(t *testing.T) {
type selectable struct {
id ResId
expectSelected bool
}
var testCases = []struct {
selector ResId
selectables []selectable
}{
{
selector: ResId{
Namespace: "X",
Name: "nm",
Gvk: Gvk{Group: "g", Version: "v", Kind: "k"},
},
selectables: []selectable{
{
id: ResId{
Namespace: "X",
Name: "nm",
Gvk: Gvk{Group: "g", Version: "v", Kind: "k"},
},
expectSelected: true,
},
{
id: ResId{
Namespace: "x",
Name: "nm",
Gvk: Gvk{Group: "g", Version: "v", Kind: "k"},
},
expectSelected: false,
},
{
id: ResId{
Name: "nm",
Gvk: Gvk{Group: "g", Version: "v", Kind: "k"},
},
expectSelected: false,
},
},
},
{
selector: ResId{
/* Namespace wildcard */
Name: "nm",
Gvk: Gvk{Group: "g" /* Version wildcard */, Kind: "k"},
},
selectables: []selectable{
{
id: ResId{
Namespace: "X",
Name: "nm",
Gvk: Gvk{Group: "g", Version: "v", Kind: "k"},
},
expectSelected: true,
},
{
id: ResId{
Namespace: "x",
Name: "nm",
Gvk: Gvk{Group: "g", Version: "v", Kind: "k"},
},
expectSelected: true,
},
{
id: ResId{
Name: "nm",
Gvk: Gvk{Group: "g", Version: "VVV", Kind: "k"},
},
expectSelected: true,
},
},
},
}
for _, tst := range testCases {
for _, pair := range tst.selectables {
if pair.id.IsSelectedBy(tst.selector) {
if !pair.expectSelected {
t.Fatalf(
"expected id %s to NOT be selected by %s",
pair.id, tst.selector)
}
} else {
if pair.expectSelected {
t.Fatalf(
"expected id %s to be selected by %s",
pair.id, tst.selector)
}
}
}
}
}
func TestFromString(t *testing.T) {
for _, id := range ids {
newId := FromString(id.String())
if newId != id {
t.Fatalf("Actual: %v, Expected: '%s'", newId, id)
}
}
}
func TestEffectiveNamespace(t *testing.T) {
var test = []struct {
id ResId
expected string
}{
{
id: ResId{
Gvk: Gvk{Group: "", Version: "v1", Kind: "Node"},
Name: "nm",
},
expected: TotallyNotANamespace,
},
{
id: ResId{
Namespace: "foo",
Gvk: Gvk{Group: "", Version: "v1", Kind: "Node"},
Name: "nm",
},
expected: TotallyNotANamespace,
},
{
id: ResId{
Namespace: "foo",
Gvk: Gvk{Group: "g", Version: "v", Kind: "k"},
Name: "nm",
},
expected: "foo",
},
{
id: ResId{
Namespace: "",
Gvk: Gvk{Group: "g", Version: "v", Kind: "k"},
Name: "nm",
},
expected: DefaultNamespace,
},
{
id: ResId{
Gvk: Gvk{Group: "g", Version: "v", Kind: "k"},
Name: "nm",
},
expected: DefaultNamespace,
},
}
for _, tst := range test {
if actual := tst.id.EffectiveNamespace(); actual != tst.expected {
t.Fatalf("EffectiveNamespace was %s, expected %s",
actual, tst.expected)
}
}
}