mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-12 01:14:22 +00:00
Merge pull request #3136 from natasha41575/NamespaceabilityFromOpenAPI
removed hardcoded list of namespaceable resources
This commit is contained in:
@@ -194,47 +194,47 @@ metadata:
|
|||||||
{
|
{
|
||||||
name: "update-clusterrolebinding",
|
name: "update-clusterrolebinding",
|
||||||
input: `
|
input: `
|
||||||
apiVersion: example.com/v1
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
subjects:
|
subjects:
|
||||||
- name: default
|
- name: default
|
||||||
---
|
---
|
||||||
apiVersion: example.com/v1
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
subjects:
|
subjects:
|
||||||
- name: default
|
- name: default
|
||||||
namespace: foo
|
namespace: foo
|
||||||
---
|
---
|
||||||
apiVersion: example.com/v1
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
subjects:
|
subjects:
|
||||||
- name: something
|
- name: something
|
||||||
---
|
---
|
||||||
apiVersion: example.com/v1
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
subjects:
|
subjects:
|
||||||
- name: something
|
- name: something
|
||||||
namespace: foo
|
namespace: foo
|
||||||
`,
|
`,
|
||||||
expected: `
|
expected: `
|
||||||
apiVersion: example.com/v1
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
subjects:
|
subjects:
|
||||||
- name: default
|
- name: default
|
||||||
namespace: bar
|
namespace: bar
|
||||||
---
|
---
|
||||||
apiVersion: example.com/v1
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
subjects:
|
subjects:
|
||||||
- name: default
|
- name: default
|
||||||
namespace: bar
|
namespace: bar
|
||||||
---
|
---
|
||||||
apiVersion: example.com/v1
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
subjects:
|
subjects:
|
||||||
- name: something
|
- name: something
|
||||||
---
|
---
|
||||||
apiVersion: example.com/v1
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
subjects:
|
subjects:
|
||||||
- name: something
|
- name: something
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ subjects:
|
|||||||
name: default
|
name: default
|
||||||
namespace: irrelevant
|
namespace: irrelevant
|
||||||
---
|
---
|
||||||
apiVersion: admissionregistration.k8s.io/v1beta1
|
apiVersion: admissionregistration.k8s.io/v1
|
||||||
kind: ValidatingWebhookConfiguration
|
kind: ValidatingWebhookConfiguration
|
||||||
metadata:
|
metadata:
|
||||||
name: example
|
name: example
|
||||||
@@ -180,15 +180,17 @@ webhooks:
|
|||||||
name: svc3
|
name: svc3
|
||||||
namespace: random
|
namespace: random
|
||||||
---
|
---
|
||||||
apiVersion: apiextensions.k8s.io/v1beta1
|
apiVersion: apiextensions.k8s.io/v1
|
||||||
kind: CustomResourceDefinition
|
kind: CustomResourceDefinition
|
||||||
metadata:
|
metadata:
|
||||||
name: crds.my.org
|
name: crds.my.org
|
||||||
---
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
metadata:
|
metadata:
|
||||||
name: cr1
|
name: cr1
|
||||||
---
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
metadata:
|
metadata:
|
||||||
name: crb1
|
name: crb1
|
||||||
@@ -197,6 +199,7 @@ subjects:
|
|||||||
name: default
|
name: default
|
||||||
namespace: irrelevant
|
namespace: irrelevant
|
||||||
---
|
---
|
||||||
|
apiVersion: v1
|
||||||
kind: PersistentVolume
|
kind: PersistentVolume
|
||||||
metadata:
|
metadata:
|
||||||
name: pv1
|
name: pv1
|
||||||
@@ -257,7 +260,7 @@ subjects:
|
|||||||
name: default
|
name: default
|
||||||
namespace: newnamespace
|
namespace: newnamespace
|
||||||
---
|
---
|
||||||
apiVersion: admissionregistration.k8s.io/v1beta1
|
apiVersion: admissionregistration.k8s.io/v1
|
||||||
kind: ValidatingWebhookConfiguration
|
kind: ValidatingWebhookConfiguration
|
||||||
metadata:
|
metadata:
|
||||||
name: p1-example-s1
|
name: p1-example-s1
|
||||||
@@ -278,15 +281,17 @@ webhooks:
|
|||||||
namespace: random
|
namespace: random
|
||||||
name: example3
|
name: example3
|
||||||
---
|
---
|
||||||
apiVersion: apiextensions.k8s.io/v1beta1
|
apiVersion: apiextensions.k8s.io/v1
|
||||||
kind: CustomResourceDefinition
|
kind: CustomResourceDefinition
|
||||||
metadata:
|
metadata:
|
||||||
name: crds.my.org
|
name: crds.my.org
|
||||||
---
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
metadata:
|
metadata:
|
||||||
name: p1-cr1-s1
|
name: p1-cr1-s1
|
||||||
---
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
metadata:
|
metadata:
|
||||||
name: p1-crb1-s1
|
name: p1-crb1-s1
|
||||||
@@ -295,6 +300,7 @@ subjects:
|
|||||||
name: default
|
name: default
|
||||||
namespace: newnamespace
|
namespace: newnamespace
|
||||||
---
|
---
|
||||||
|
apiVersion: v1
|
||||||
kind: PersistentVolume
|
kind: PersistentVolume
|
||||||
metadata:
|
metadata:
|
||||||
name: p1-pv1-s1
|
name: p1-pv1-s1
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ package resid
|
|||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -207,5 +208,6 @@ func (x Gvk) toKyamlTypeMeta() yaml.TypeMeta {
|
|||||||
// IsNamespaceableKind returns true if x is a namespaceable Gvk
|
// IsNamespaceableKind returns true if x is a namespaceable Gvk
|
||||||
// Implements https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/#not-all-objects-are-in-a-namespace
|
// Implements https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/#not-all-objects-are-in-a-namespace
|
||||||
func (x Gvk) IsNamespaceableKind() bool {
|
func (x Gvk) IsNamespaceableKind() bool {
|
||||||
return x.toKyamlTypeMeta().IsNamespaceable()
|
isNamespaceScoped, found := openapi.IsNamespaceScoped(x.toKyamlTypeMeta())
|
||||||
|
return !found || isNamespaceScoped
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ package resid
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
var equalsTests = []struct {
|
var equalsTests = []struct {
|
||||||
@@ -255,3 +257,40 @@ func TestSelectByGVK(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -265,7 +265,7 @@ func TestResIdEquals(t *testing.T) {
|
|||||||
Name: "nm",
|
Name: "nm",
|
||||||
},
|
},
|
||||||
gVknResult: false,
|
gVknResult: false,
|
||||||
nsEquals: false,
|
nsEquals: true,
|
||||||
equals: false,
|
equals: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -376,7 +376,7 @@ func TestEffectiveNamespace(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
id: ResId{
|
id: ResId{
|
||||||
Gvk: Gvk{Group: "g", Version: "v", Kind: "Node"},
|
Gvk: Gvk{Group: "", Version: "v1", Kind: "Node"},
|
||||||
Name: "nm",
|
Name: "nm",
|
||||||
},
|
},
|
||||||
expected: TotallyNotANamespace,
|
expected: TotallyNotANamespace,
|
||||||
@@ -384,7 +384,7 @@ func TestEffectiveNamespace(t *testing.T) {
|
|||||||
{
|
{
|
||||||
id: ResId{
|
id: ResId{
|
||||||
Namespace: "foo",
|
Namespace: "foo",
|
||||||
Gvk: Gvk{Group: "g", Version: "v", Kind: "Node"},
|
Gvk: Gvk{Group: "", Version: "v1", Kind: "Node"},
|
||||||
Name: "nm",
|
Name: "nm",
|
||||||
},
|
},
|
||||||
expected: TotallyNotANamespace,
|
expected: TotallyNotANamespace,
|
||||||
|
|||||||
@@ -355,7 +355,7 @@ func TestSubsetThatCouldBeReferencedByResource(t *testing.T) {
|
|||||||
})
|
})
|
||||||
r4 := rf.FromMap(
|
r4 := rf.FromMap(
|
||||||
map[string]interface{}{
|
map[string]interface{}{
|
||||||
"apiVersion": "v1",
|
"apiVersion": "apps/v1",
|
||||||
"kind": "Deployment",
|
"kind": "Deployment",
|
||||||
"metadata": map[string]interface{}{
|
"metadata": map[string]interface{}{
|
||||||
"name": "charlie",
|
"name": "charlie",
|
||||||
@@ -374,7 +374,7 @@ func TestSubsetThatCouldBeReferencedByResource(t *testing.T) {
|
|||||||
r5.AddNamePrefix("little-")
|
r5.AddNamePrefix("little-")
|
||||||
r6 := rf.FromMap(
|
r6 := rf.FromMap(
|
||||||
map[string]interface{}{
|
map[string]interface{}{
|
||||||
"apiVersion": "v1",
|
"apiVersion": "apps/v1",
|
||||||
"kind": "Deployment",
|
"kind": "Deployment",
|
||||||
"metadata": map[string]interface{}{
|
"metadata": map[string]interface{}{
|
||||||
"name": "domino",
|
"name": "domino",
|
||||||
@@ -384,7 +384,7 @@ func TestSubsetThatCouldBeReferencedByResource(t *testing.T) {
|
|||||||
r6.AddNamePrefix("little-")
|
r6.AddNamePrefix("little-")
|
||||||
r7 := rf.FromMap(
|
r7 := rf.FromMap(
|
||||||
map[string]interface{}{
|
map[string]interface{}{
|
||||||
"apiVersion": "v1",
|
"apiVersion": "rbac.authorization.k8s.io/v1",
|
||||||
"kind": "ClusterRoleBinding",
|
"kind": "ClusterRoleBinding",
|
||||||
"metadata": map[string]interface{}{
|
"metadata": map[string]interface{}{
|
||||||
"name": "meh",
|
"name": "meh",
|
||||||
|
|||||||
@@ -114,46 +114,6 @@ type TypeMeta struct {
|
|||||||
Kind string `json:"kind,omitempty" yaml:"kind,omitempty"`
|
Kind string `json:"kind,omitempty" yaml:"kind,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hardcoded list.
|
|
||||||
// TODO(#2861): replace this with data acquired from openapi.
|
|
||||||
var notNamespaceableKinds = []string{
|
|
||||||
"APIService",
|
|
||||||
"CSIDriver",
|
|
||||||
"CSINode",
|
|
||||||
"CertificateSigningRequest",
|
|
||||||
"Cluster",
|
|
||||||
"ClusterRole",
|
|
||||||
"ClusterRoleBinding",
|
|
||||||
"ComponentStatus",
|
|
||||||
"CustomResourceDefinition",
|
|
||||||
"MutatingWebhookConfiguration",
|
|
||||||
"Namespace",
|
|
||||||
"Node",
|
|
||||||
"PersistentVolume",
|
|
||||||
"PodSecurityPolicy",
|
|
||||||
"PriorityClass",
|
|
||||||
"RuntimeClass",
|
|
||||||
"SelfSubjectAccessReview",
|
|
||||||
"SelfSubjectRulesReview",
|
|
||||||
"StorageClass",
|
|
||||||
"SubjectAccessReview",
|
|
||||||
"TokenReview",
|
|
||||||
"ValidatingWebhookConfiguration",
|
|
||||||
"VolumeAttachment",
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsNamespaceable returns true if this TypeMeta is for an object
|
|
||||||
// that can be placed in a namespace.
|
|
||||||
// Implements https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/#not-all-objects-are-in-a-namespace
|
|
||||||
func (tm TypeMeta) IsNamespaceable() bool {
|
|
||||||
for _, k := range notNamespaceableKinds {
|
|
||||||
if k == tm.Kind {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// NameMeta contains name information.
|
// NameMeta contains name information.
|
||||||
type NameMeta struct {
|
type NameMeta struct {
|
||||||
// Name is the metadata.name field of a Resource
|
// Name is the metadata.name field of a Resource
|
||||||
|
|||||||
@@ -112,78 +112,3 @@ func TestIsYNodeEmptySeq(t *testing.T) {
|
|||||||
t.Fatalf("a node with content isn't empty")
|
t.Fatalf("a node with content isn't empty")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIsNameSpaceable1(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
tm TypeMeta
|
|
||||||
isNamespaceable bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
tm: TypeMeta{Kind: "ClusterRole"},
|
|
||||||
isNamespaceable: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tm: TypeMeta{Kind: "Namespace"},
|
|
||||||
isNamespaceable: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tm: TypeMeta{Kind: "Deployment"},
|
|
||||||
isNamespaceable: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tc := range testCases {
|
|
||||||
if tc.tm.IsNamespaceable() {
|
|
||||||
if !tc.isNamespaceable {
|
|
||||||
t.Fatalf("%v is namespaceable, but shouldn't be", tc.tm)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if tc.isNamespaceable {
|
|
||||||
t.Fatalf("%v is not namespaceable, but should be", tc.tm)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIsNameSpaceable2(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
yammy string
|
|
||||||
isNamespaceable bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
yammy: `apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: bilbo
|
|
||||||
namespace: middleEarth
|
|
||||||
`,
|
|
||||||
isNamespaceable: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
yammy: `apiVersion: v1
|
|
||||||
kind: Namespace
|
|
||||||
metadata:
|
|
||||||
name: middleEarth
|
|
||||||
`,
|
|
||||||
isNamespaceable: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tc := range testCases {
|
|
||||||
rn, err := Parse(tc.yammy)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected parse error %v from json: %s", err, tc.yammy)
|
|
||||||
}
|
|
||||||
meta, err := rn.GetMeta()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected meta error %v", err)
|
|
||||||
}
|
|
||||||
if meta.IsNamespaceable() {
|
|
||||||
if !tc.isNamespaceable {
|
|
||||||
t.Fatalf("%v is namespaceable, but shouldn't be", meta)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if tc.isNamespaceable {
|
|
||||||
t.Fatalf("%v is not namespaceable, but should be", meta)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -193,18 +193,22 @@ kind: Namespace
|
|||||||
metadata:
|
metadata:
|
||||||
name: ns1
|
name: ns1
|
||||||
---
|
---
|
||||||
|
apiVersion: apiextensions.k8s.io/v1
|
||||||
kind: CustomResourceDefinition
|
kind: CustomResourceDefinition
|
||||||
metadata:
|
metadata:
|
||||||
name: crd1
|
name: crd1
|
||||||
---
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
metadata:
|
metadata:
|
||||||
name: cr1
|
name: cr1
|
||||||
---
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
metadata:
|
metadata:
|
||||||
name: crb1
|
name: crb1
|
||||||
---
|
---
|
||||||
|
apiVersion: v1
|
||||||
kind: PersistentVolume
|
kind: PersistentVolume
|
||||||
metadata:
|
metadata:
|
||||||
name: pv1
|
name: pv1
|
||||||
|
|||||||
Reference in New Issue
Block a user