Merge pull request #2881 from ikaven1024/ut-interpret
[UT] add ut for interpret command
This commit is contained in:
commit
e6ba531a73
|
@ -0,0 +1,106 @@
|
||||||
|
package interpret
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||||
|
"k8s.io/cli-runtime/pkg/resource"
|
||||||
|
|
||||||
|
cmdtesting "github.com/karmada-io/karmada/pkg/karmadactl/util/testing"
|
||||||
|
"github.com/karmada-io/karmada/pkg/util/interpreter"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestOptions_runCheck(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
options *Options
|
||||||
|
want string
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Has errors in file",
|
||||||
|
options: &Options{
|
||||||
|
Rules: interpreter.AllResourceInterpreterCustomizationRules,
|
||||||
|
FilenameOptions: resource.FilenameOptions{Filenames: []string{"./testdata/customization_error.yml"}},
|
||||||
|
Check: true,
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
want: `-----------------------------------
|
||||||
|
SOURCE: not-customization
|
||||||
|
not a ResourceInterpreterCustomization, got /v1, Kind=Pod
|
||||||
|
-----------------------------------
|
||||||
|
SOURCE: api-version-unset
|
||||||
|
target.apiVersion no set
|
||||||
|
-----------------------------------
|
||||||
|
SOURCE: kind-unset
|
||||||
|
target.kind no set
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "customization has error",
|
||||||
|
options: &Options{
|
||||||
|
Rules: interpreter.AllResourceInterpreterCustomizationRules,
|
||||||
|
FilenameOptions: resource.FilenameOptions{Filenames: []string{"./testdata/customization_check.yml"}},
|
||||||
|
Check: true,
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
want: `-----------------------------------
|
||||||
|
SOURCE: customization-check
|
||||||
|
TARGET: apps/v1 Deployment
|
||||||
|
RULERS:
|
||||||
|
Retain: PASS
|
||||||
|
InterpretReplica: ERROR: <string> line:1(column:10) near 'format': parse error
|
||||||
|
ReviseReplica: UNSET
|
||||||
|
InterpretStatus: UNSET
|
||||||
|
AggregateStatus: UNSET
|
||||||
|
InterpretHealth: UNSET
|
||||||
|
InterpretDependency: UNSET
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "customization has no error",
|
||||||
|
options: &Options{
|
||||||
|
Rules: interpreter.AllResourceInterpreterCustomizationRules,
|
||||||
|
FilenameOptions: resource.FilenameOptions{Filenames: []string{"./testdata/customization.yml"}},
|
||||||
|
Check: true,
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
want: `-----------------------------------
|
||||||
|
SOURCE: customization
|
||||||
|
TARGET: apps/v1 Deployment
|
||||||
|
RULERS:
|
||||||
|
Retain: PASS
|
||||||
|
InterpretReplica: PASS
|
||||||
|
ReviseReplica: PASS
|
||||||
|
InterpretStatus: PASS
|
||||||
|
AggregateStatus: PASS
|
||||||
|
InterpretHealth: PASS
|
||||||
|
InterpretDependency: PASS
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
tf := cmdtesting.NewTestFactory()
|
||||||
|
defer tf.Cleanup()
|
||||||
|
|
||||||
|
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
|
||||||
|
tt.options.IOStreams = streams
|
||||||
|
if err := tt.options.Complete(tf, nil, nil); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := tt.options.Validate(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := tt.options.Run()
|
||||||
|
if (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("runCheck() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
}
|
||||||
|
if got := buf.String(); got != tt.want {
|
||||||
|
t.Errorf("runCheck() = %q, want %q", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,209 @@
|
||||||
|
package interpret
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||||
|
"k8s.io/cli-runtime/pkg/resource"
|
||||||
|
|
||||||
|
cmdtesting "github.com/karmada-io/karmada/pkg/karmadactl/util/testing"
|
||||||
|
"github.com/karmada-io/karmada/pkg/util/interpreter"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestOptions_runExecute(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
options *Options
|
||||||
|
want string
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "execute retain",
|
||||||
|
options: &Options{
|
||||||
|
FilenameOptions: resource.FilenameOptions{Filenames: []string{"./testdata/customization.yml"}},
|
||||||
|
Operation: "retain",
|
||||||
|
DesiredFile: "./testdata/desired.yml",
|
||||||
|
ObservedFile: "./testdata/observed.yml",
|
||||||
|
Rules: interpreter.AllResourceInterpreterCustomizationRules,
|
||||||
|
},
|
||||||
|
want: `---
|
||||||
|
# [1/1] retained:
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
cluster: cluster1
|
||||||
|
name: nginx
|
||||||
|
spec:
|
||||||
|
replicas: 3
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: nginx
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: nginx:alpine
|
||||||
|
name: nginx
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "execute interpretReplica",
|
||||||
|
options: &Options{
|
||||||
|
FilenameOptions: resource.FilenameOptions{Filenames: []string{"./testdata/customization.yml"}},
|
||||||
|
Operation: "interpretReplica",
|
||||||
|
ObservedFile: "./testdata/observed.yml",
|
||||||
|
Rules: interpreter.AllResourceInterpreterCustomizationRules,
|
||||||
|
},
|
||||||
|
want: `---
|
||||||
|
# [1/2] replica:
|
||||||
|
3
|
||||||
|
---
|
||||||
|
# [2/2] requires:
|
||||||
|
resourceRequest:
|
||||||
|
cpu: 100m
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "execute reviseReplica",
|
||||||
|
options: &Options{
|
||||||
|
FilenameOptions: resource.FilenameOptions{Filenames: []string{"./testdata/customization.yml"}},
|
||||||
|
Operation: "reviseReplica",
|
||||||
|
ObservedFile: "./testdata/observed.yml",
|
||||||
|
DesiredReplica: 2,
|
||||||
|
Rules: interpreter.AllResourceInterpreterCustomizationRules,
|
||||||
|
},
|
||||||
|
want: `---
|
||||||
|
# [1/1] revised:
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
cluster: cluster1
|
||||||
|
name: nginx
|
||||||
|
spec:
|
||||||
|
replicas: 2
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: nginx
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: nginx:alpine
|
||||||
|
name: nginx
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpu: 100m
|
||||||
|
status:
|
||||||
|
readyReplicas: 3
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "execute interpretStatus",
|
||||||
|
options: &Options{
|
||||||
|
FilenameOptions: resource.FilenameOptions{Filenames: []string{"./testdata/customization.yml"}},
|
||||||
|
Operation: "interpretStatus",
|
||||||
|
ObservedFile: "./testdata/observed.yml",
|
||||||
|
Rules: interpreter.AllResourceInterpreterCustomizationRules,
|
||||||
|
},
|
||||||
|
want: `---
|
||||||
|
# [1/1] status:
|
||||||
|
readyReplicas: 3
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "execute interpretHealth",
|
||||||
|
options: &Options{
|
||||||
|
FilenameOptions: resource.FilenameOptions{Filenames: []string{"./testdata/customization.yml"}},
|
||||||
|
Operation: "interpretHealth",
|
||||||
|
ObservedFile: "./testdata/observed.yml",
|
||||||
|
Rules: interpreter.AllResourceInterpreterCustomizationRules,
|
||||||
|
},
|
||||||
|
want: `---
|
||||||
|
# [1/1] healthy:
|
||||||
|
true
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "execute interpretDependency",
|
||||||
|
options: &Options{
|
||||||
|
FilenameOptions: resource.FilenameOptions{Filenames: []string{"./testdata/customization.yml"}},
|
||||||
|
Operation: "interpretDependency",
|
||||||
|
ObservedFile: "./testdata/observed.yml",
|
||||||
|
Rules: interpreter.AllResourceInterpreterCustomizationRules,
|
||||||
|
},
|
||||||
|
want: `---
|
||||||
|
# [1/1] dependencies:
|
||||||
|
- apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
name: nginx
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "execute aggregateStatus",
|
||||||
|
options: &Options{
|
||||||
|
FilenameOptions: resource.FilenameOptions{Filenames: []string{"./testdata/customization.yml"}},
|
||||||
|
Operation: "aggregateStatus",
|
||||||
|
ObservedFile: "./testdata/observed.yml",
|
||||||
|
StatusFile: "./testdata/status.yml",
|
||||||
|
Rules: interpreter.AllResourceInterpreterCustomizationRules,
|
||||||
|
},
|
||||||
|
want: `---
|
||||||
|
# [1/1] aggregatedStatus:
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
cluster: cluster1
|
||||||
|
name: nginx
|
||||||
|
spec:
|
||||||
|
replicas: 3
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: nginx
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: nginx:alpine
|
||||||
|
name: nginx
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpu: 100m
|
||||||
|
status:
|
||||||
|
readyReplicas: 5
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
tf := cmdtesting.NewTestFactory()
|
||||||
|
defer tf.Cleanup()
|
||||||
|
|
||||||
|
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
|
||||||
|
tt.options.IOStreams = streams
|
||||||
|
if err := tt.options.Complete(tf, nil, nil); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := tt.options.Validate(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := tt.options.Run()
|
||||||
|
if (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("runExecute() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
}
|
||||||
|
if got := buf.String(); got != tt.want {
|
||||||
|
t.Errorf("runExecute() = %q, want %q", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -220,7 +220,7 @@ func getUnstructuredObjectFromResult(result *resource.Result) (*unstructured.Uns
|
||||||
func asResourceInterpreterCustomization(o runtime.Object) (*configv1alpha1.ResourceInterpreterCustomization, error) {
|
func asResourceInterpreterCustomization(o runtime.Object) (*configv1alpha1.ResourceInterpreterCustomization, error) {
|
||||||
c, ok := o.(*configv1alpha1.ResourceInterpreterCustomization)
|
c, ok := o.(*configv1alpha1.ResourceInterpreterCustomization)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("not a ResourceInterpreterCustomization: %#v", o)
|
return nil, fmt.Errorf("not a ResourceInterpreterCustomization, got %v", o.GetObjectKind().GroupVersionKind())
|
||||||
}
|
}
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
package interpret
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestOptions_Validate(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
options *Options
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "operation is unknown",
|
||||||
|
options: &Options{
|
||||||
|
Operation: "unknown-operation",
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if err := tt.options.Validate(); (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("Validate() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
apiVersion: config.karmada.io/v1alpha1
|
||||||
|
kind: ResourceInterpreterCustomization
|
||||||
|
metadata:
|
||||||
|
name: customization
|
||||||
|
spec:
|
||||||
|
target:
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
customizations:
|
||||||
|
retention:
|
||||||
|
luaScript: >
|
||||||
|
function Retain(desiredObj, runtimeObj)
|
||||||
|
desiredObj.metadata.annotations.cluster = runtimeObj.metadata.annotations.cluster
|
||||||
|
return desiredObj
|
||||||
|
end
|
||||||
|
replicaResource:
|
||||||
|
luaScript: >
|
||||||
|
function GetReplicas(obj)
|
||||||
|
replica = obj.spec.replicas
|
||||||
|
requirement = {
|
||||||
|
resourceRequest = obj.spec.template.spec.containers[1].resources.limits,
|
||||||
|
nodeClaim = {
|
||||||
|
nodeSelector = obj.spec.template.spec.nodeSelector,
|
||||||
|
tolerations = obj.spec.template.spec.tolerations
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return replica, requirement
|
||||||
|
end
|
||||||
|
replicaRevision:
|
||||||
|
luaScript: >
|
||||||
|
function ReviseReplica(obj, desiredReplica)
|
||||||
|
obj.spec.replicas = desiredReplica
|
||||||
|
return obj
|
||||||
|
end
|
||||||
|
statusReflection:
|
||||||
|
luaScript: >
|
||||||
|
function ReflectStatus(observedObj)
|
||||||
|
return observedObj.status
|
||||||
|
end
|
||||||
|
statusAggregation:
|
||||||
|
luaScript: >
|
||||||
|
function AggregateStatus(desiredObj, items)
|
||||||
|
desiredObj.status.readyReplicas = 0
|
||||||
|
for i = 1, #items do
|
||||||
|
desiredObj.status.readyReplicas = desiredObj.status.readyReplicas + items[i].status.readyReplicas
|
||||||
|
end
|
||||||
|
return desiredObj
|
||||||
|
end
|
||||||
|
healthInterpretation:
|
||||||
|
luaScript: >
|
||||||
|
function InterpretHealth(observedObj)
|
||||||
|
return observedObj.status.readyReplicas == observedObj.spec.replicas
|
||||||
|
end
|
||||||
|
dependencyInterpretation:
|
||||||
|
luaScript: >
|
||||||
|
function GetDependencies(desiredObj)
|
||||||
|
dependencies = {}
|
||||||
|
dependencies[1] = {
|
||||||
|
apiVersion = "v1",
|
||||||
|
kind = "ServiceAccount",
|
||||||
|
name = desiredObj.metadata.name,
|
||||||
|
namespace = desiredObj.metadata.namespace,
|
||||||
|
}
|
||||||
|
return dependencies
|
||||||
|
end
|
|
@ -0,0 +1,17 @@
|
||||||
|
apiVersion: config.karmada.io/v1alpha1
|
||||||
|
kind: ResourceInterpreterCustomization
|
||||||
|
metadata:
|
||||||
|
name: customization-check
|
||||||
|
spec:
|
||||||
|
target:
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
customizations:
|
||||||
|
retention:
|
||||||
|
luaScript: >
|
||||||
|
function Retain(desiredObj, runtimeObj)
|
||||||
|
return desiredObj
|
||||||
|
end
|
||||||
|
replicaResource:
|
||||||
|
luaScript: >
|
||||||
|
bad format
|
|
@ -0,0 +1,23 @@
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: not-customization
|
||||||
|
---
|
||||||
|
apiVersion: config.karmada.io/v1alpha1
|
||||||
|
kind: ResourceInterpreterCustomization
|
||||||
|
metadata:
|
||||||
|
name: api-version-unset
|
||||||
|
spec:
|
||||||
|
target:
|
||||||
|
kind: Deployment
|
||||||
|
---
|
||||||
|
apiVersion: config.karmada.io/v1alpha1
|
||||||
|
kind: ResourceInterpreterCustomization
|
||||||
|
metadata:
|
||||||
|
name: kind-unset
|
||||||
|
spec:
|
||||||
|
target:
|
||||||
|
apiVersion: apps/v1
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: nginx
|
||||||
|
annotations: {}
|
||||||
|
spec:
|
||||||
|
replicas: 3
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: nginx
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: nginx:alpine
|
||||||
|
name: nginx
|
||||||
|
status: {}
|
|
@ -0,0 +1,24 @@
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: nginx
|
||||||
|
annotations:
|
||||||
|
cluster: cluster1
|
||||||
|
spec:
|
||||||
|
replicas: 3
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: nginx
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: nginx:alpine
|
||||||
|
name: nginx
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpu: 100m
|
||||||
|
status:
|
||||||
|
readyReplicas: 3
|
|
@ -0,0 +1,9 @@
|
||||||
|
clusterName: cluster1
|
||||||
|
status:
|
||||||
|
readyReplicas: 2
|
||||||
|
applied: true
|
||||||
|
---
|
||||||
|
clusterName: cluster2
|
||||||
|
status:
|
||||||
|
readyReplicas: 3
|
||||||
|
applied: true
|
|
@ -0,0 +1,35 @@
|
||||||
|
package testing
|
||||||
|
|
||||||
|
import (
|
||||||
|
cmdtesting "k8s.io/kubectl/pkg/cmd/testing"
|
||||||
|
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||||
|
|
||||||
|
"github.com/karmada-io/karmada/pkg/generated/clientset/versioned"
|
||||||
|
"github.com/karmada-io/karmada/pkg/karmadactl/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestFactory extends cmdutil.Factory
|
||||||
|
type TestFactory struct {
|
||||||
|
*cmdtesting.TestFactory
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ util.Factory = (*TestFactory)(nil)
|
||||||
|
|
||||||
|
// NewTestFactory returns an initialized TestFactory instance
|
||||||
|
func NewTestFactory() *TestFactory {
|
||||||
|
return &TestFactory{
|
||||||
|
TestFactory: cmdtesting.NewTestFactory(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// KarmadaClientSet returns a karmada clientset
|
||||||
|
func (t *TestFactory) KarmadaClientSet() (versioned.Interface, error) {
|
||||||
|
// TODO implement me
|
||||||
|
panic("implement me")
|
||||||
|
}
|
||||||
|
|
||||||
|
// FactoryForMemberCluster returns a cmdutil.Factory for the member cluster
|
||||||
|
func (t *TestFactory) FactoryForMemberCluster(clusterName string) (cmdutil.Factory, error) {
|
||||||
|
// TODO implement me
|
||||||
|
panic("implement me")
|
||||||
|
}
|
171
vendor/k8s.io/apimachinery/pkg/api/meta/testrestmapper/test_restmapper.go
generated
vendored
Normal file
171
vendor/k8s.io/apimachinery/pkg/api/meta/testrestmapper/test_restmapper.go
generated
vendored
Normal file
|
@ -0,0 +1,171 @@
|
||||||
|
/*
|
||||||
|
Copyright 2018 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package testrestmapper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestOnlyStaticRESTMapper returns a union RESTMapper of all known types with priorities chosen in the following order:
|
||||||
|
// 1. legacy kube group preferred version, extensions preferred version, metrics preferred version, legacy
|
||||||
|
// kube any version, extensions any version, metrics any version, all other groups alphabetical preferred version,
|
||||||
|
// all other groups alphabetical.
|
||||||
|
//
|
||||||
|
// TODO callers of this method should be updated to build their own specific restmapper based on their scheme for their tests
|
||||||
|
// TODO the things being tested are related to whether various cases are handled, not tied to the particular types being checked.
|
||||||
|
func TestOnlyStaticRESTMapper(scheme *runtime.Scheme, versionPatterns ...schema.GroupVersion) meta.RESTMapper {
|
||||||
|
unionMapper := meta.MultiRESTMapper{}
|
||||||
|
unionedGroups := sets.NewString()
|
||||||
|
for _, enabledVersion := range scheme.PrioritizedVersionsAllGroups() {
|
||||||
|
if !unionedGroups.Has(enabledVersion.Group) {
|
||||||
|
unionedGroups.Insert(enabledVersion.Group)
|
||||||
|
unionMapper = append(unionMapper, newRESTMapper(enabledVersion.Group, scheme))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(versionPatterns) != 0 {
|
||||||
|
resourcePriority := []schema.GroupVersionResource{}
|
||||||
|
kindPriority := []schema.GroupVersionKind{}
|
||||||
|
for _, versionPriority := range versionPatterns {
|
||||||
|
resourcePriority = append(resourcePriority, versionPriority.WithResource(meta.AnyResource))
|
||||||
|
kindPriority = append(kindPriority, versionPriority.WithKind(meta.AnyKind))
|
||||||
|
}
|
||||||
|
|
||||||
|
return meta.PriorityRESTMapper{Delegate: unionMapper, ResourcePriority: resourcePriority, KindPriority: kindPriority}
|
||||||
|
}
|
||||||
|
|
||||||
|
prioritizedGroups := []string{"", "extensions", "metrics"}
|
||||||
|
resourcePriority, kindPriority := prioritiesForGroups(scheme, prioritizedGroups...)
|
||||||
|
|
||||||
|
prioritizedGroupsSet := sets.NewString(prioritizedGroups...)
|
||||||
|
remainingGroups := sets.String{}
|
||||||
|
for _, enabledVersion := range scheme.PrioritizedVersionsAllGroups() {
|
||||||
|
if !prioritizedGroupsSet.Has(enabledVersion.Group) {
|
||||||
|
remainingGroups.Insert(enabledVersion.Group)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
remainingResourcePriority, remainingKindPriority := prioritiesForGroups(scheme, remainingGroups.List()...)
|
||||||
|
resourcePriority = append(resourcePriority, remainingResourcePriority...)
|
||||||
|
kindPriority = append(kindPriority, remainingKindPriority...)
|
||||||
|
|
||||||
|
return meta.PriorityRESTMapper{Delegate: unionMapper, ResourcePriority: resourcePriority, KindPriority: kindPriority}
|
||||||
|
}
|
||||||
|
|
||||||
|
// prioritiesForGroups returns the resource and kind priorities for a PriorityRESTMapper, preferring the preferred version of each group first,
|
||||||
|
// then any non-preferred version of the group second.
|
||||||
|
func prioritiesForGroups(scheme *runtime.Scheme, groups ...string) ([]schema.GroupVersionResource, []schema.GroupVersionKind) {
|
||||||
|
resourcePriority := []schema.GroupVersionResource{}
|
||||||
|
kindPriority := []schema.GroupVersionKind{}
|
||||||
|
|
||||||
|
for _, group := range groups {
|
||||||
|
availableVersions := scheme.PrioritizedVersionsForGroup(group)
|
||||||
|
if len(availableVersions) > 0 {
|
||||||
|
resourcePriority = append(resourcePriority, availableVersions[0].WithResource(meta.AnyResource))
|
||||||
|
kindPriority = append(kindPriority, availableVersions[0].WithKind(meta.AnyKind))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, group := range groups {
|
||||||
|
resourcePriority = append(resourcePriority, schema.GroupVersionResource{Group: group, Version: meta.AnyVersion, Resource: meta.AnyResource})
|
||||||
|
kindPriority = append(kindPriority, schema.GroupVersionKind{Group: group, Version: meta.AnyVersion, Kind: meta.AnyKind})
|
||||||
|
}
|
||||||
|
|
||||||
|
return resourcePriority, kindPriority
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRESTMapper(group string, scheme *runtime.Scheme) meta.RESTMapper {
|
||||||
|
mapper := meta.NewDefaultRESTMapper(scheme.PrioritizedVersionsForGroup(group))
|
||||||
|
for _, gv := range scheme.PrioritizedVersionsForGroup(group) {
|
||||||
|
for kind := range scheme.KnownTypes(gv) {
|
||||||
|
if ignoredKinds.Has(kind) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
scope := meta.RESTScopeNamespace
|
||||||
|
if rootScopedKinds[gv.WithKind(kind).GroupKind()] {
|
||||||
|
scope = meta.RESTScopeRoot
|
||||||
|
}
|
||||||
|
mapper.Add(gv.WithKind(kind), scope)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mapper
|
||||||
|
}
|
||||||
|
|
||||||
|
// hardcoded is good enough for the test we're running
|
||||||
|
var rootScopedKinds = map[schema.GroupKind]bool{
|
||||||
|
{Group: "admission.k8s.io", Kind: "AdmissionReview"}: true,
|
||||||
|
|
||||||
|
{Group: "admissionregistration.k8s.io", Kind: "ValidatingWebhookConfiguration"}: true,
|
||||||
|
{Group: "admissionregistration.k8s.io", Kind: "MutatingWebhookConfiguration"}: true,
|
||||||
|
|
||||||
|
{Group: "authentication.k8s.io", Kind: "TokenReview"}: true,
|
||||||
|
|
||||||
|
{Group: "authorization.k8s.io", Kind: "SubjectAccessReview"}: true,
|
||||||
|
{Group: "authorization.k8s.io", Kind: "SelfSubjectAccessReview"}: true,
|
||||||
|
{Group: "authorization.k8s.io", Kind: "SelfSubjectRulesReview"}: true,
|
||||||
|
|
||||||
|
{Group: "certificates.k8s.io", Kind: "CertificateSigningRequest"}: true,
|
||||||
|
|
||||||
|
{Group: "", Kind: "Node"}: true,
|
||||||
|
{Group: "", Kind: "Namespace"}: true,
|
||||||
|
{Group: "", Kind: "PersistentVolume"}: true,
|
||||||
|
{Group: "", Kind: "ComponentStatus"}: true,
|
||||||
|
|
||||||
|
{Group: "extensions", Kind: "PodSecurityPolicy"}: true,
|
||||||
|
|
||||||
|
{Group: "policy", Kind: "PodSecurityPolicy"}: true,
|
||||||
|
|
||||||
|
{Group: "extensions", Kind: "PodSecurityPolicy"}: true,
|
||||||
|
|
||||||
|
{Group: "rbac.authorization.k8s.io", Kind: "ClusterRole"}: true,
|
||||||
|
{Group: "rbac.authorization.k8s.io", Kind: "ClusterRoleBinding"}: true,
|
||||||
|
|
||||||
|
{Group: "scheduling.k8s.io", Kind: "PriorityClass"}: true,
|
||||||
|
|
||||||
|
{Group: "storage.k8s.io", Kind: "StorageClass"}: true,
|
||||||
|
{Group: "storage.k8s.io", Kind: "VolumeAttachment"}: true,
|
||||||
|
|
||||||
|
{Group: "apiextensions.k8s.io", Kind: "CustomResourceDefinition"}: true,
|
||||||
|
|
||||||
|
{Group: "apiserver.k8s.io", Kind: "AdmissionConfiguration"}: true,
|
||||||
|
|
||||||
|
{Group: "audit.k8s.io", Kind: "Event"}: true,
|
||||||
|
{Group: "audit.k8s.io", Kind: "Policy"}: true,
|
||||||
|
|
||||||
|
{Group: "apiregistration.k8s.io", Kind: "APIService"}: true,
|
||||||
|
|
||||||
|
{Group: "metrics.k8s.io", Kind: "NodeMetrics"}: true,
|
||||||
|
|
||||||
|
{Group: "wardle.example.com", Kind: "Fischer"}: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
// hardcoded is good enough for the test we're running
|
||||||
|
var ignoredKinds = sets.NewString(
|
||||||
|
"ListOptions",
|
||||||
|
"DeleteOptions",
|
||||||
|
"Status",
|
||||||
|
"PodLogOptions",
|
||||||
|
"PodExecOptions",
|
||||||
|
"PodAttachOptions",
|
||||||
|
"PodPortForwardOptions",
|
||||||
|
"PodProxyOptions",
|
||||||
|
"NodeProxyOptions",
|
||||||
|
"ServiceProxyOptions",
|
||||||
|
)
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
Copyright 2017 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package testing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
openapi_v2 "github.com/google/gnostic/openapiv2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Fake opens and returns a openapi swagger from a file Path. It will
|
||||||
|
// parse only once and then return the same copy everytime.
|
||||||
|
type Fake struct {
|
||||||
|
Path string
|
||||||
|
|
||||||
|
once sync.Once
|
||||||
|
document *openapi_v2.Document
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenAPISchema returns the openapi document and a potential error.
|
||||||
|
func (f *Fake) OpenAPISchema() (*openapi_v2.Document, error) {
|
||||||
|
f.once.Do(func() {
|
||||||
|
_, err := os.Stat(f.Path)
|
||||||
|
if err != nil {
|
||||||
|
f.err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
spec, err := ioutil.ReadFile(f.Path)
|
||||||
|
if err != nil {
|
||||||
|
f.err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
f.document, f.err = openapi_v2.ParseDocument(spec)
|
||||||
|
})
|
||||||
|
return f.document, f.err
|
||||||
|
}
|
||||||
|
|
||||||
|
type Empty struct{}
|
||||||
|
|
||||||
|
func (Empty) OpenAPISchema() (*openapi_v2.Document, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
/*
|
||||||
|
Copyright 2022 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package testing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
openapi_v3 "github.com/google/gnostic/openapiv3"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FakeV3 struct {
|
||||||
|
Path string
|
||||||
|
|
||||||
|
lock sync.Mutex
|
||||||
|
documents map[string]*openapi_v3.Document
|
||||||
|
errors map[string]error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FakeV3) OpenAPIV3Schema(groupVersion string) (*openapi_v3.Document, error) {
|
||||||
|
f.lock.Lock()
|
||||||
|
defer f.lock.Unlock()
|
||||||
|
|
||||||
|
if existing, ok := f.documents[groupVersion]; ok {
|
||||||
|
return existing, nil
|
||||||
|
} else if existingError, ok := f.errors[groupVersion]; ok {
|
||||||
|
return nil, existingError
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := os.Stat(f.Path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
spec, err := ioutil.ReadFile(filepath.Join(f.Path, groupVersion+".json"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.documents == nil {
|
||||||
|
f.documents = make(map[string]*openapi_v3.Document)
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.errors == nil {
|
||||||
|
f.errors = make(map[string]error)
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := openapi_v3.ParseDocument(spec)
|
||||||
|
if err != nil {
|
||||||
|
f.errors[groupVersion] = err
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
f.documents[groupVersion] = result
|
||||||
|
return result, nil
|
||||||
|
}
|
|
@ -0,0 +1,845 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package testing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
|
"k8s.io/apimachinery/pkg/api/meta/testrestmapper"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/conversion"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||||
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||||
|
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||||
|
"k8s.io/cli-runtime/pkg/resource"
|
||||||
|
"k8s.io/client-go/discovery"
|
||||||
|
diskcached "k8s.io/client-go/discovery/cached/disk"
|
||||||
|
"k8s.io/client-go/dynamic"
|
||||||
|
fakedynamic "k8s.io/client-go/dynamic/fake"
|
||||||
|
"k8s.io/client-go/kubernetes"
|
||||||
|
restclient "k8s.io/client-go/rest"
|
||||||
|
"k8s.io/client-go/rest/fake"
|
||||||
|
"k8s.io/client-go/restmapper"
|
||||||
|
scaleclient "k8s.io/client-go/scale"
|
||||||
|
"k8s.io/client-go/tools/clientcmd"
|
||||||
|
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||||
|
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||||
|
"k8s.io/kubectl/pkg/scheme"
|
||||||
|
"k8s.io/kubectl/pkg/util/openapi"
|
||||||
|
openapitesting "k8s.io/kubectl/pkg/util/openapi/testing"
|
||||||
|
"k8s.io/kubectl/pkg/validation"
|
||||||
|
|
||||||
|
openapi_v2 "github.com/google/gnostic/openapiv2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// InternalType is the schema for internal type
|
||||||
|
// +k8s:deepcopy-gen=true
|
||||||
|
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||||
|
type InternalType struct {
|
||||||
|
Kind string
|
||||||
|
APIVersion string
|
||||||
|
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExternalType is the schema for external type
|
||||||
|
// +k8s:deepcopy-gen=true
|
||||||
|
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||||
|
type ExternalType struct {
|
||||||
|
Kind string `json:"kind"`
|
||||||
|
APIVersion string `json:"apiVersion"`
|
||||||
|
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExternalType2 is another schema for external type
|
||||||
|
// +k8s:deepcopy-gen=true
|
||||||
|
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||||
|
type ExternalType2 struct {
|
||||||
|
Kind string `json:"kind"`
|
||||||
|
APIVersion string `json:"apiVersion"`
|
||||||
|
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetObjectKind returns the ObjectKind schema
|
||||||
|
func (obj *InternalType) GetObjectKind() schema.ObjectKind { return obj }
|
||||||
|
|
||||||
|
// SetGroupVersionKind sets the version and kind
|
||||||
|
func (obj *InternalType) SetGroupVersionKind(gvk schema.GroupVersionKind) {
|
||||||
|
obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupVersionKind returns GroupVersionKind schema
|
||||||
|
func (obj *InternalType) GroupVersionKind() schema.GroupVersionKind {
|
||||||
|
return schema.FromAPIVersionAndKind(obj.APIVersion, obj.Kind)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetObjectKind returns the ObjectKind schema
|
||||||
|
func (obj *ExternalType) GetObjectKind() schema.ObjectKind { return obj }
|
||||||
|
|
||||||
|
// SetGroupVersionKind returns the GroupVersionKind schema
|
||||||
|
func (obj *ExternalType) SetGroupVersionKind(gvk schema.GroupVersionKind) {
|
||||||
|
obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupVersionKind returns the GroupVersionKind schema
|
||||||
|
func (obj *ExternalType) GroupVersionKind() schema.GroupVersionKind {
|
||||||
|
return schema.FromAPIVersionAndKind(obj.APIVersion, obj.Kind)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetObjectKind returns the ObjectKind schema
|
||||||
|
func (obj *ExternalType2) GetObjectKind() schema.ObjectKind { return obj }
|
||||||
|
|
||||||
|
// SetGroupVersionKind sets the API version and obj kind from schema
|
||||||
|
func (obj *ExternalType2) SetGroupVersionKind(gvk schema.GroupVersionKind) {
|
||||||
|
obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupVersionKind returns the FromAPIVersionAndKind schema
|
||||||
|
func (obj *ExternalType2) GroupVersionKind() schema.GroupVersionKind {
|
||||||
|
return schema.FromAPIVersionAndKind(obj.APIVersion, obj.Kind)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInternalType returns an initialized InternalType instance
|
||||||
|
func NewInternalType(kind, apiversion, name string) *InternalType {
|
||||||
|
item := InternalType{Kind: kind,
|
||||||
|
APIVersion: apiversion,
|
||||||
|
Name: name}
|
||||||
|
return &item
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertInternalTypeToExternalType(in *InternalType, out *ExternalType, s conversion.Scope) error {
|
||||||
|
out.Kind = in.Kind
|
||||||
|
out.APIVersion = in.APIVersion
|
||||||
|
out.Name = in.Name
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertInternalTypeToExternalType2(in *InternalType, out *ExternalType2, s conversion.Scope) error {
|
||||||
|
out.Kind = in.Kind
|
||||||
|
out.APIVersion = in.APIVersion
|
||||||
|
out.Name = in.Name
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertExternalTypeToInternalType(in *ExternalType, out *InternalType, s conversion.Scope) error {
|
||||||
|
out.Kind = in.Kind
|
||||||
|
out.APIVersion = in.APIVersion
|
||||||
|
out.Name = in.Name
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertExternalType2ToInternalType(in *ExternalType2, out *InternalType, s conversion.Scope) error {
|
||||||
|
out.Kind = in.Kind
|
||||||
|
out.APIVersion = in.APIVersion
|
||||||
|
out.Name = in.Name
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// InternalNamespacedType schema for internal namespaced types
|
||||||
|
// +k8s:deepcopy-gen=true
|
||||||
|
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||||
|
type InternalNamespacedType struct {
|
||||||
|
Kind string
|
||||||
|
APIVersion string
|
||||||
|
|
||||||
|
Name string
|
||||||
|
Namespace string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExternalNamespacedType schema for external namespaced types
|
||||||
|
// +k8s:deepcopy-gen=true
|
||||||
|
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||||
|
type ExternalNamespacedType struct {
|
||||||
|
Kind string `json:"kind"`
|
||||||
|
APIVersion string `json:"apiVersion"`
|
||||||
|
|
||||||
|
Name string `json:"name"`
|
||||||
|
Namespace string `json:"namespace"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExternalNamespacedType2 schema for external namespaced types
|
||||||
|
// +k8s:deepcopy-gen=true
|
||||||
|
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||||
|
type ExternalNamespacedType2 struct {
|
||||||
|
Kind string `json:"kind"`
|
||||||
|
APIVersion string `json:"apiVersion"`
|
||||||
|
|
||||||
|
Name string `json:"name"`
|
||||||
|
Namespace string `json:"namespace"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetObjectKind returns the ObjectKind schema
|
||||||
|
func (obj *InternalNamespacedType) GetObjectKind() schema.ObjectKind { return obj }
|
||||||
|
|
||||||
|
// SetGroupVersionKind sets the API group and kind from schema
|
||||||
|
func (obj *InternalNamespacedType) SetGroupVersionKind(gvk schema.GroupVersionKind) {
|
||||||
|
obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupVersionKind returns the GroupVersionKind schema
|
||||||
|
func (obj *InternalNamespacedType) GroupVersionKind() schema.GroupVersionKind {
|
||||||
|
return schema.FromAPIVersionAndKind(obj.APIVersion, obj.Kind)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetObjectKind returns the ObjectKind schema
|
||||||
|
func (obj *ExternalNamespacedType) GetObjectKind() schema.ObjectKind { return obj }
|
||||||
|
|
||||||
|
// SetGroupVersionKind sets the API version and kind from schema
|
||||||
|
func (obj *ExternalNamespacedType) SetGroupVersionKind(gvk schema.GroupVersionKind) {
|
||||||
|
obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupVersionKind returns the GroupVersionKind schema
|
||||||
|
func (obj *ExternalNamespacedType) GroupVersionKind() schema.GroupVersionKind {
|
||||||
|
return schema.FromAPIVersionAndKind(obj.APIVersion, obj.Kind)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetObjectKind returns the ObjectKind schema
|
||||||
|
func (obj *ExternalNamespacedType2) GetObjectKind() schema.ObjectKind { return obj }
|
||||||
|
|
||||||
|
// SetGroupVersionKind sets the API version and kind from schema
|
||||||
|
func (obj *ExternalNamespacedType2) SetGroupVersionKind(gvk schema.GroupVersionKind) {
|
||||||
|
obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupVersionKind returns the GroupVersionKind schema
|
||||||
|
func (obj *ExternalNamespacedType2) GroupVersionKind() schema.GroupVersionKind {
|
||||||
|
return schema.FromAPIVersionAndKind(obj.APIVersion, obj.Kind)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewInternalNamespacedType returns an initialized instance of InternalNamespacedType
|
||||||
|
func NewInternalNamespacedType(kind, apiversion, name, namespace string) *InternalNamespacedType {
|
||||||
|
item := InternalNamespacedType{Kind: kind,
|
||||||
|
APIVersion: apiversion,
|
||||||
|
Name: name,
|
||||||
|
Namespace: namespace}
|
||||||
|
return &item
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertInternalNamespacedTypeToExternalNamespacedType(in *InternalNamespacedType, out *ExternalNamespacedType, s conversion.Scope) error {
|
||||||
|
out.Kind = in.Kind
|
||||||
|
out.APIVersion = in.APIVersion
|
||||||
|
out.Name = in.Name
|
||||||
|
out.Namespace = in.Namespace
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertInternalNamespacedTypeToExternalNamespacedType2(in *InternalNamespacedType, out *ExternalNamespacedType2, s conversion.Scope) error {
|
||||||
|
out.Kind = in.Kind
|
||||||
|
out.APIVersion = in.APIVersion
|
||||||
|
out.Name = in.Name
|
||||||
|
out.Namespace = in.Namespace
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertExternalNamespacedTypeToInternalNamespacedType(in *ExternalNamespacedType, out *InternalNamespacedType, s conversion.Scope) error {
|
||||||
|
out.Kind = in.Kind
|
||||||
|
out.APIVersion = in.APIVersion
|
||||||
|
out.Name = in.Name
|
||||||
|
out.Namespace = in.Namespace
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertExternalNamespacedType2ToInternalNamespacedType(in *ExternalNamespacedType2, out *InternalNamespacedType, s conversion.Scope) error {
|
||||||
|
out.Kind = in.Kind
|
||||||
|
out.APIVersion = in.APIVersion
|
||||||
|
out.Name = in.Name
|
||||||
|
out.Namespace = in.Namespace
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidVersion of API
|
||||||
|
var ValidVersion = "v1"
|
||||||
|
|
||||||
|
// InternalGV is the internal group version object
|
||||||
|
var InternalGV = schema.GroupVersion{Group: "apitest", Version: runtime.APIVersionInternal}
|
||||||
|
|
||||||
|
// UnlikelyGV is a group version object for unrecognised version
|
||||||
|
var UnlikelyGV = schema.GroupVersion{Group: "apitest", Version: "unlikelyversion"}
|
||||||
|
|
||||||
|
// ValidVersionGV is the valid group version object
|
||||||
|
var ValidVersionGV = schema.GroupVersion{Group: "apitest", Version: ValidVersion}
|
||||||
|
|
||||||
|
// NewExternalScheme returns required objects for ExternalScheme
|
||||||
|
func NewExternalScheme() (*runtime.Scheme, meta.RESTMapper, runtime.Codec) {
|
||||||
|
scheme := runtime.NewScheme()
|
||||||
|
mapper, codec := AddToScheme(scheme)
|
||||||
|
return scheme, mapper, codec
|
||||||
|
}
|
||||||
|
|
||||||
|
func registerConversions(s *runtime.Scheme) error {
|
||||||
|
if err := s.AddConversionFunc((*InternalType)(nil), (*ExternalType)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||||
|
return convertInternalTypeToExternalType(a.(*InternalType), b.(*ExternalType), scope)
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.AddConversionFunc((*InternalType)(nil), (*ExternalType2)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||||
|
return convertInternalTypeToExternalType2(a.(*InternalType), b.(*ExternalType2), scope)
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.AddConversionFunc((*ExternalType)(nil), (*InternalType)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||||
|
return convertExternalTypeToInternalType(a.(*ExternalType), b.(*InternalType), scope)
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.AddConversionFunc((*ExternalType2)(nil), (*InternalType)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||||
|
return convertExternalType2ToInternalType(a.(*ExternalType2), b.(*InternalType), scope)
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.AddConversionFunc((*InternalNamespacedType)(nil), (*ExternalNamespacedType)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||||
|
return convertInternalNamespacedTypeToExternalNamespacedType(a.(*InternalNamespacedType), b.(*ExternalNamespacedType), scope)
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.AddConversionFunc((*InternalNamespacedType)(nil), (*ExternalNamespacedType2)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||||
|
return convertInternalNamespacedTypeToExternalNamespacedType2(a.(*InternalNamespacedType), b.(*ExternalNamespacedType2), scope)
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.AddConversionFunc((*ExternalNamespacedType)(nil), (*InternalNamespacedType)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||||
|
return convertExternalNamespacedTypeToInternalNamespacedType(a.(*ExternalNamespacedType), b.(*InternalNamespacedType), scope)
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.AddConversionFunc((*ExternalNamespacedType2)(nil), (*InternalNamespacedType)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||||
|
return convertExternalNamespacedType2ToInternalNamespacedType(a.(*ExternalNamespacedType2), b.(*InternalNamespacedType), scope)
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddToScheme adds required objects into scheme
|
||||||
|
func AddToScheme(scheme *runtime.Scheme) (meta.RESTMapper, runtime.Codec) {
|
||||||
|
scheme.AddKnownTypeWithName(InternalGV.WithKind("Type"), &InternalType{})
|
||||||
|
scheme.AddKnownTypeWithName(UnlikelyGV.WithKind("Type"), &ExternalType{})
|
||||||
|
// This tests that kubectl will not confuse the external scheme with the internal scheme, even when they accidentally have versions of the same name.
|
||||||
|
scheme.AddKnownTypeWithName(ValidVersionGV.WithKind("Type"), &ExternalType2{})
|
||||||
|
|
||||||
|
scheme.AddKnownTypeWithName(InternalGV.WithKind("NamespacedType"), &InternalNamespacedType{})
|
||||||
|
scheme.AddKnownTypeWithName(UnlikelyGV.WithKind("NamespacedType"), &ExternalNamespacedType{})
|
||||||
|
// This tests that kubectl will not confuse the external scheme with the internal scheme, even when they accidentally have versions of the same name.
|
||||||
|
scheme.AddKnownTypeWithName(ValidVersionGV.WithKind("NamespacedType"), &ExternalNamespacedType2{})
|
||||||
|
|
||||||
|
utilruntime.Must(registerConversions(scheme))
|
||||||
|
|
||||||
|
codecs := serializer.NewCodecFactory(scheme)
|
||||||
|
codec := codecs.LegacyCodec(UnlikelyGV)
|
||||||
|
mapper := meta.NewDefaultRESTMapper([]schema.GroupVersion{UnlikelyGV, ValidVersionGV})
|
||||||
|
for _, gv := range []schema.GroupVersion{UnlikelyGV, ValidVersionGV} {
|
||||||
|
for kind := range scheme.KnownTypes(gv) {
|
||||||
|
gvk := gv.WithKind(kind)
|
||||||
|
|
||||||
|
scope := meta.RESTScopeNamespace
|
||||||
|
mapper.Add(gvk, scope)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mapper, codec
|
||||||
|
}
|
||||||
|
|
||||||
|
type FakeCachedDiscoveryClient struct {
|
||||||
|
discovery.DiscoveryInterface
|
||||||
|
Groups []*metav1.APIGroup
|
||||||
|
Resources []*metav1.APIResourceList
|
||||||
|
PreferredResources []*metav1.APIResourceList
|
||||||
|
Invalidations int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFakeCachedDiscoveryClient() *FakeCachedDiscoveryClient {
|
||||||
|
return &FakeCachedDiscoveryClient{
|
||||||
|
Groups: []*metav1.APIGroup{},
|
||||||
|
Resources: []*metav1.APIResourceList{},
|
||||||
|
PreferredResources: []*metav1.APIResourceList{},
|
||||||
|
Invalidations: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *FakeCachedDiscoveryClient) Fresh() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *FakeCachedDiscoveryClient) Invalidate() {
|
||||||
|
d.Invalidations++
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *FakeCachedDiscoveryClient) ServerGroupsAndResources() ([]*metav1.APIGroup, []*metav1.APIResourceList, error) {
|
||||||
|
return d.Groups, d.Resources, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *FakeCachedDiscoveryClient) ServerGroups() (*metav1.APIGroupList, error) {
|
||||||
|
groupList := &metav1.APIGroupList{Groups: []metav1.APIGroup{}}
|
||||||
|
for _, g := range d.Groups {
|
||||||
|
groupList.Groups = append(groupList.Groups, *g)
|
||||||
|
}
|
||||||
|
return groupList, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *FakeCachedDiscoveryClient) ServerPreferredResources() ([]*metav1.APIResourceList, error) {
|
||||||
|
return d.PreferredResources, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestFactory extends cmdutil.Factory
|
||||||
|
type TestFactory struct {
|
||||||
|
cmdutil.Factory
|
||||||
|
|
||||||
|
kubeConfigFlags *genericclioptions.TestConfigFlags
|
||||||
|
|
||||||
|
Client RESTClient
|
||||||
|
ScaleGetter scaleclient.ScalesGetter
|
||||||
|
UnstructuredClient RESTClient
|
||||||
|
ClientConfigVal *restclient.Config
|
||||||
|
FakeDynamicClient *fakedynamic.FakeDynamicClient
|
||||||
|
|
||||||
|
tempConfigFile *os.File
|
||||||
|
|
||||||
|
UnstructuredClientForMappingFunc resource.FakeClientFunc
|
||||||
|
OpenAPISchemaFunc func() (openapi.Resources, error)
|
||||||
|
FakeOpenAPIGetter discovery.OpenAPISchemaInterface
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTestFactory returns an initialized TestFactory instance
|
||||||
|
func NewTestFactory() *TestFactory {
|
||||||
|
// specify an optionalClientConfig to explicitly use in testing
|
||||||
|
// to avoid polluting an existing user config.
|
||||||
|
tmpFile, err := ioutil.TempFile(os.TempDir(), "cmdtests_temp")
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("unable to create a fake client config: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
loadingRules := &clientcmd.ClientConfigLoadingRules{
|
||||||
|
Precedence: []string{tmpFile.Name()},
|
||||||
|
MigrationRules: map[string]string{},
|
||||||
|
}
|
||||||
|
|
||||||
|
overrides := &clientcmd.ConfigOverrides{ClusterDefaults: clientcmdapi.Cluster{Server: "http://localhost:8080"}}
|
||||||
|
fallbackReader := bytes.NewBuffer([]byte{})
|
||||||
|
clientConfig := clientcmd.NewInteractiveDeferredLoadingClientConfig(loadingRules, overrides, fallbackReader)
|
||||||
|
|
||||||
|
configFlags := genericclioptions.NewTestConfigFlags().
|
||||||
|
WithClientConfig(clientConfig).
|
||||||
|
WithRESTMapper(testRESTMapper())
|
||||||
|
|
||||||
|
restConfig, err := clientConfig.ClientConfig()
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("unable to create a fake restclient config: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
return &TestFactory{
|
||||||
|
Factory: cmdutil.NewFactory(configFlags),
|
||||||
|
kubeConfigFlags: configFlags,
|
||||||
|
FakeDynamicClient: fakedynamic.NewSimpleDynamicClient(scheme.Scheme),
|
||||||
|
tempConfigFile: tmpFile,
|
||||||
|
|
||||||
|
ClientConfigVal: restConfig,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithNamespace is used to mention namespace reactively
|
||||||
|
func (f *TestFactory) WithNamespace(ns string) *TestFactory {
|
||||||
|
f.kubeConfigFlags.WithNamespace(ns)
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithClientConfig sets the client config to use
|
||||||
|
func (f *TestFactory) WithClientConfig(clientConfig clientcmd.ClientConfig) *TestFactory {
|
||||||
|
f.kubeConfigFlags.WithClientConfig(clientConfig)
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *TestFactory) WithDiscoveryClient(discoveryClient discovery.CachedDiscoveryInterface) *TestFactory {
|
||||||
|
f.kubeConfigFlags.WithDiscoveryClient(discoveryClient)
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup cleans up TestFactory temp config file
|
||||||
|
func (f *TestFactory) Cleanup() {
|
||||||
|
if f.tempConfigFile == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Remove(f.tempConfigFile.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToRESTConfig is used to get ClientConfigVal from a TestFactory
|
||||||
|
func (f *TestFactory) ToRESTConfig() (*restclient.Config, error) {
|
||||||
|
return f.ClientConfigVal, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientForMapping is used to Client from a TestFactory
|
||||||
|
func (f *TestFactory) ClientForMapping(mapping *meta.RESTMapping) (resource.RESTClient, error) {
|
||||||
|
return f.Client, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PathOptions returns a new PathOptions with a temp file
|
||||||
|
func (f *TestFactory) PathOptions() *clientcmd.PathOptions {
|
||||||
|
pathOptions := clientcmd.NewDefaultPathOptions()
|
||||||
|
pathOptions.GlobalFile = f.tempConfigFile.Name()
|
||||||
|
pathOptions.EnvVar = ""
|
||||||
|
return pathOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// PathOptionsWithConfig writes a config to a temp file and returns PathOptions
|
||||||
|
func (f *TestFactory) PathOptionsWithConfig(config clientcmdapi.Config) (*clientcmd.PathOptions, error) {
|
||||||
|
pathOptions := f.PathOptions()
|
||||||
|
err := clientcmd.WriteToFile(config, pathOptions.GlobalFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return pathOptions, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnstructuredClientForMapping is used to get UnstructuredClient from a TestFactory
|
||||||
|
func (f *TestFactory) UnstructuredClientForMapping(mapping *meta.RESTMapping) (resource.RESTClient, error) {
|
||||||
|
if f.UnstructuredClientForMappingFunc != nil {
|
||||||
|
return f.UnstructuredClientForMappingFunc(mapping.GroupVersionKind.GroupVersion())
|
||||||
|
}
|
||||||
|
return f.UnstructuredClient, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validator returns a validation schema
|
||||||
|
func (f *TestFactory) Validator(validateDirective string, verifier *resource.QueryParamVerifier) (validation.Schema, error) {
|
||||||
|
return validation.NullSchema{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenAPISchema returns openapi resources
|
||||||
|
func (f *TestFactory) OpenAPISchema() (openapi.Resources, error) {
|
||||||
|
if f.OpenAPISchemaFunc != nil {
|
||||||
|
return f.OpenAPISchemaFunc()
|
||||||
|
}
|
||||||
|
return openapitesting.EmptyResources{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type EmptyOpenAPI struct{}
|
||||||
|
|
||||||
|
func (EmptyOpenAPI) OpenAPISchema() (*openapi_v2.Document, error) {
|
||||||
|
return &openapi_v2.Document{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *TestFactory) OpenAPIGetter() discovery.OpenAPISchemaInterface {
|
||||||
|
if f.FakeOpenAPIGetter != nil {
|
||||||
|
return f.FakeOpenAPIGetter
|
||||||
|
}
|
||||||
|
client, err := f.ToDiscoveryClient()
|
||||||
|
if err != nil {
|
||||||
|
return EmptyOpenAPI{}
|
||||||
|
}
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBuilder returns an initialized resource.Builder instance
|
||||||
|
func (f *TestFactory) NewBuilder() *resource.Builder {
|
||||||
|
return resource.NewFakeBuilder(
|
||||||
|
func(version schema.GroupVersion) (resource.RESTClient, error) {
|
||||||
|
if f.UnstructuredClientForMappingFunc != nil {
|
||||||
|
return f.UnstructuredClientForMappingFunc(version)
|
||||||
|
}
|
||||||
|
if f.UnstructuredClient != nil {
|
||||||
|
return f.UnstructuredClient, nil
|
||||||
|
}
|
||||||
|
return f.Client, nil
|
||||||
|
},
|
||||||
|
f.ToRESTMapper,
|
||||||
|
func() (restmapper.CategoryExpander, error) {
|
||||||
|
return resource.FakeCategoryExpander, nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// KubernetesClientSet initializes and returns the Clientset using TestFactory
|
||||||
|
func (f *TestFactory) KubernetesClientSet() (*kubernetes.Clientset, error) {
|
||||||
|
fakeClient := f.Client.(*fake.RESTClient)
|
||||||
|
clientset := kubernetes.NewForConfigOrDie(f.ClientConfigVal)
|
||||||
|
|
||||||
|
clientset.CoreV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
|
||||||
|
clientset.AuthorizationV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
|
||||||
|
clientset.AuthorizationV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
|
||||||
|
clientset.AuthorizationV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
|
||||||
|
clientset.AuthorizationV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
|
||||||
|
clientset.AutoscalingV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
|
||||||
|
clientset.AutoscalingV2beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
|
||||||
|
clientset.BatchV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
|
||||||
|
clientset.CertificatesV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
|
||||||
|
clientset.CertificatesV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
|
||||||
|
clientset.ExtensionsV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
|
||||||
|
clientset.RbacV1alpha1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
|
||||||
|
clientset.RbacV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
|
||||||
|
clientset.StorageV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
|
||||||
|
clientset.StorageV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
|
||||||
|
clientset.AppsV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
|
||||||
|
clientset.AppsV1beta2().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
|
||||||
|
clientset.AppsV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
|
||||||
|
clientset.PolicyV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
|
||||||
|
clientset.PolicyV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
|
||||||
|
clientset.DiscoveryClient.RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
|
||||||
|
|
||||||
|
return clientset, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DynamicClient returns a dynamic client from TestFactory
|
||||||
|
func (f *TestFactory) DynamicClient() (dynamic.Interface, error) {
|
||||||
|
if f.FakeDynamicClient != nil {
|
||||||
|
return f.FakeDynamicClient, nil
|
||||||
|
}
|
||||||
|
return f.Factory.DynamicClient()
|
||||||
|
}
|
||||||
|
|
||||||
|
// RESTClient returns a REST client from TestFactory
|
||||||
|
func (f *TestFactory) RESTClient() (*restclient.RESTClient, error) {
|
||||||
|
// Swap out the HTTP client out of the client with the fake's version.
|
||||||
|
fakeClient := f.Client.(*fake.RESTClient)
|
||||||
|
restClient, err := restclient.RESTClientFor(f.ClientConfigVal)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
restClient.Client = fakeClient.Client
|
||||||
|
return restClient, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DiscoveryClient returns a discovery client from TestFactory
|
||||||
|
func (f *TestFactory) DiscoveryClient() (discovery.CachedDiscoveryInterface, error) {
|
||||||
|
fakeClient := f.Client.(*fake.RESTClient)
|
||||||
|
|
||||||
|
cacheDir := filepath.Join("", ".kube", "cache", "discovery")
|
||||||
|
cachedClient, err := diskcached.NewCachedDiscoveryClientForConfig(f.ClientConfigVal, cacheDir, "", time.Duration(10*time.Minute))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cachedClient.RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
|
||||||
|
|
||||||
|
return cachedClient, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func testRESTMapper() meta.RESTMapper {
|
||||||
|
groupResources := testDynamicResources()
|
||||||
|
mapper := restmapper.NewDiscoveryRESTMapper(groupResources)
|
||||||
|
// for backwards compatibility with existing tests, allow rest mappings from the scheme to show up
|
||||||
|
// TODO: make this opt-in?
|
||||||
|
mapper = meta.FirstHitRESTMapper{
|
||||||
|
MultiRESTMapper: meta.MultiRESTMapper{
|
||||||
|
mapper,
|
||||||
|
testrestmapper.TestOnlyStaticRESTMapper(scheme.Scheme),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
fakeDs := NewFakeCachedDiscoveryClient()
|
||||||
|
expander := restmapper.NewShortcutExpander(mapper, fakeDs)
|
||||||
|
return expander
|
||||||
|
}
|
||||||
|
|
||||||
|
// ScaleClient returns the ScalesGetter from a TestFactory
|
||||||
|
func (f *TestFactory) ScaleClient() (scaleclient.ScalesGetter, error) {
|
||||||
|
return f.ScaleGetter, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDynamicResources() []*restmapper.APIGroupResources {
|
||||||
|
return []*restmapper.APIGroupResources{
|
||||||
|
{
|
||||||
|
Group: metav1.APIGroup{
|
||||||
|
Versions: []metav1.GroupVersionForDiscovery{
|
||||||
|
{Version: "v1"},
|
||||||
|
},
|
||||||
|
PreferredVersion: metav1.GroupVersionForDiscovery{Version: "v1"},
|
||||||
|
},
|
||||||
|
VersionedResources: map[string][]metav1.APIResource{
|
||||||
|
"v1": {
|
||||||
|
{Name: "pods", Namespaced: true, Kind: "Pod"},
|
||||||
|
{Name: "services", Namespaced: true, Kind: "Service"},
|
||||||
|
{Name: "replicationcontrollers", Namespaced: true, Kind: "ReplicationController"},
|
||||||
|
{Name: "componentstatuses", Namespaced: false, Kind: "ComponentStatus"},
|
||||||
|
{Name: "nodes", Namespaced: false, Kind: "Node"},
|
||||||
|
{Name: "secrets", Namespaced: true, Kind: "Secret"},
|
||||||
|
{Name: "configmaps", Namespaced: true, Kind: "ConfigMap"},
|
||||||
|
{Name: "namespacedtype", Namespaced: true, Kind: "NamespacedType"},
|
||||||
|
{Name: "namespaces", Namespaced: false, Kind: "Namespace"},
|
||||||
|
{Name: "resourcequotas", Namespaced: true, Kind: "ResourceQuota"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Group: metav1.APIGroup{
|
||||||
|
Name: "extensions",
|
||||||
|
Versions: []metav1.GroupVersionForDiscovery{
|
||||||
|
{Version: "v1beta1"},
|
||||||
|
},
|
||||||
|
PreferredVersion: metav1.GroupVersionForDiscovery{Version: "v1beta1"},
|
||||||
|
},
|
||||||
|
VersionedResources: map[string][]metav1.APIResource{
|
||||||
|
"v1beta1": {
|
||||||
|
{Name: "deployments", Namespaced: true, Kind: "Deployment"},
|
||||||
|
{Name: "replicasets", Namespaced: true, Kind: "ReplicaSet"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Group: metav1.APIGroup{
|
||||||
|
Name: "apps",
|
||||||
|
Versions: []metav1.GroupVersionForDiscovery{
|
||||||
|
{Version: "v1beta1"},
|
||||||
|
{Version: "v1beta2"},
|
||||||
|
{Version: "v1"},
|
||||||
|
},
|
||||||
|
PreferredVersion: metav1.GroupVersionForDiscovery{Version: "v1"},
|
||||||
|
},
|
||||||
|
VersionedResources: map[string][]metav1.APIResource{
|
||||||
|
"v1beta1": {
|
||||||
|
{Name: "deployments", Namespaced: true, Kind: "Deployment"},
|
||||||
|
{Name: "replicasets", Namespaced: true, Kind: "ReplicaSet"},
|
||||||
|
},
|
||||||
|
"v1beta2": {
|
||||||
|
{Name: "deployments", Namespaced: true, Kind: "Deployment"},
|
||||||
|
},
|
||||||
|
"v1": {
|
||||||
|
{Name: "deployments", Namespaced: true, Kind: "Deployment"},
|
||||||
|
{Name: "replicasets", Namespaced: true, Kind: "ReplicaSet"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Group: metav1.APIGroup{
|
||||||
|
Name: "batch",
|
||||||
|
Versions: []metav1.GroupVersionForDiscovery{
|
||||||
|
{Version: "v1beta1"},
|
||||||
|
{Version: "v1"},
|
||||||
|
},
|
||||||
|
PreferredVersion: metav1.GroupVersionForDiscovery{Version: "v1"},
|
||||||
|
},
|
||||||
|
VersionedResources: map[string][]metav1.APIResource{
|
||||||
|
"v1beta1": {
|
||||||
|
{Name: "cronjobs", Namespaced: true, Kind: "CronJob"},
|
||||||
|
},
|
||||||
|
"v1": {
|
||||||
|
{Name: "jobs", Namespaced: true, Kind: "Job"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Group: metav1.APIGroup{
|
||||||
|
Name: "autoscaling",
|
||||||
|
Versions: []metav1.GroupVersionForDiscovery{
|
||||||
|
{Version: "v1"},
|
||||||
|
{Version: "v2beta1"},
|
||||||
|
},
|
||||||
|
PreferredVersion: metav1.GroupVersionForDiscovery{Version: "v2beta1"},
|
||||||
|
},
|
||||||
|
VersionedResources: map[string][]metav1.APIResource{
|
||||||
|
"v1": {
|
||||||
|
{Name: "horizontalpodautoscalers", Namespaced: true, Kind: "HorizontalPodAutoscaler"},
|
||||||
|
},
|
||||||
|
"v2beta1": {
|
||||||
|
{Name: "horizontalpodautoscalers", Namespaced: true, Kind: "HorizontalPodAutoscaler"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Group: metav1.APIGroup{
|
||||||
|
Name: "storage.k8s.io",
|
||||||
|
Versions: []metav1.GroupVersionForDiscovery{
|
||||||
|
{Version: "v1beta1"},
|
||||||
|
{Version: "v0"},
|
||||||
|
},
|
||||||
|
PreferredVersion: metav1.GroupVersionForDiscovery{Version: "v1beta1"},
|
||||||
|
},
|
||||||
|
VersionedResources: map[string][]metav1.APIResource{
|
||||||
|
"v1beta1": {
|
||||||
|
{Name: "storageclasses", Namespaced: false, Kind: "StorageClass"},
|
||||||
|
},
|
||||||
|
// bogus version of a known group/version/resource to make sure kubectl falls back to generic object mode
|
||||||
|
"v0": {
|
||||||
|
{Name: "storageclasses", Namespaced: false, Kind: "StorageClass"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Group: metav1.APIGroup{
|
||||||
|
Name: "rbac.authorization.k8s.io",
|
||||||
|
Versions: []metav1.GroupVersionForDiscovery{
|
||||||
|
{Version: "v1beta1"},
|
||||||
|
{Version: "v1"},
|
||||||
|
},
|
||||||
|
PreferredVersion: metav1.GroupVersionForDiscovery{Version: "v1"},
|
||||||
|
},
|
||||||
|
VersionedResources: map[string][]metav1.APIResource{
|
||||||
|
"v1": {
|
||||||
|
{Name: "clusterroles", Namespaced: false, Kind: "ClusterRole"},
|
||||||
|
},
|
||||||
|
"v1beta1": {
|
||||||
|
{Name: "clusterrolebindings", Namespaced: false, Kind: "ClusterRoleBinding"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Group: metav1.APIGroup{
|
||||||
|
Name: "company.com",
|
||||||
|
Versions: []metav1.GroupVersionForDiscovery{
|
||||||
|
{Version: "v1"},
|
||||||
|
},
|
||||||
|
PreferredVersion: metav1.GroupVersionForDiscovery{Version: "v1"},
|
||||||
|
},
|
||||||
|
VersionedResources: map[string][]metav1.APIResource{
|
||||||
|
"v1": {
|
||||||
|
{Name: "bars", Namespaced: true, Kind: "Bar"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Group: metav1.APIGroup{
|
||||||
|
Name: "unit-test.test.com",
|
||||||
|
Versions: []metav1.GroupVersionForDiscovery{
|
||||||
|
{GroupVersion: "unit-test.test.com/v1", Version: "v1"},
|
||||||
|
},
|
||||||
|
PreferredVersion: metav1.GroupVersionForDiscovery{
|
||||||
|
GroupVersion: "unit-test.test.com/v1",
|
||||||
|
Version: "v1"},
|
||||||
|
},
|
||||||
|
VersionedResources: map[string][]metav1.APIResource{
|
||||||
|
"v1": {
|
||||||
|
{Name: "widgets", Namespaced: true, Kind: "Widget"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Group: metav1.APIGroup{
|
||||||
|
Name: "apitest",
|
||||||
|
Versions: []metav1.GroupVersionForDiscovery{
|
||||||
|
{GroupVersion: "apitest/unlikelyversion", Version: "unlikelyversion"},
|
||||||
|
},
|
||||||
|
PreferredVersion: metav1.GroupVersionForDiscovery{
|
||||||
|
GroupVersion: "apitest/unlikelyversion",
|
||||||
|
Version: "unlikelyversion"},
|
||||||
|
},
|
||||||
|
VersionedResources: map[string][]metav1.APIResource{
|
||||||
|
"unlikelyversion": {
|
||||||
|
{Name: "types", SingularName: "type", Namespaced: false, Kind: "Type"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
Copyright 2014 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package testing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
client "k8s.io/client-go/rest"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RESTClient is a client helper for dealing with RESTful resources
|
||||||
|
// in a generic way.
|
||||||
|
type RESTClient interface {
|
||||||
|
Get() *client.Request
|
||||||
|
Post() *client.Request
|
||||||
|
Patch(types.PatchType) *client.Request
|
||||||
|
Delete() *client.Request
|
||||||
|
Put() *client.Request
|
||||||
|
}
|
|
@ -0,0 +1,181 @@
|
||||||
|
/*
|
||||||
|
Copyright 2018 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package testing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||||
|
restclient "k8s.io/client-go/rest"
|
||||||
|
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||||
|
"k8s.io/kubectl/pkg/scheme"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
grace = int64(30)
|
||||||
|
enableServiceLinks = corev1.DefaultEnableServiceLinks
|
||||||
|
)
|
||||||
|
|
||||||
|
func DefaultHeader() http.Header {
|
||||||
|
header := http.Header{}
|
||||||
|
header.Set("Content-Type", runtime.ContentTypeJSON)
|
||||||
|
return header
|
||||||
|
}
|
||||||
|
|
||||||
|
func DefaultClientConfig() *restclient.Config {
|
||||||
|
return &restclient.Config{
|
||||||
|
APIPath: "/api",
|
||||||
|
ContentConfig: restclient.ContentConfig{
|
||||||
|
NegotiatedSerializer: scheme.Codecs,
|
||||||
|
ContentType: runtime.ContentTypeJSON,
|
||||||
|
GroupVersion: &corev1.SchemeGroupVersion,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ObjBody(codec runtime.Codec, obj runtime.Object) io.ReadCloser {
|
||||||
|
return ioutil.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(codec, obj))))
|
||||||
|
}
|
||||||
|
|
||||||
|
func BytesBody(bodyBytes []byte) io.ReadCloser {
|
||||||
|
return ioutil.NopCloser(bytes.NewReader(bodyBytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
func StringBody(body string) io.ReadCloser {
|
||||||
|
return ioutil.NopCloser(bytes.NewReader([]byte(body)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestData() (*corev1.PodList, *corev1.ServiceList, *corev1.ReplicationControllerList) {
|
||||||
|
pods := &corev1.PodList{
|
||||||
|
ListMeta: metav1.ListMeta{
|
||||||
|
ResourceVersion: "15",
|
||||||
|
},
|
||||||
|
Items: []corev1.Pod{
|
||||||
|
{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test", ResourceVersion: "10"},
|
||||||
|
Spec: corev1.PodSpec{
|
||||||
|
RestartPolicy: corev1.RestartPolicyAlways,
|
||||||
|
DNSPolicy: corev1.DNSClusterFirst,
|
||||||
|
TerminationGracePeriodSeconds: &grace,
|
||||||
|
SecurityContext: &corev1.PodSecurityContext{},
|
||||||
|
EnableServiceLinks: &enableServiceLinks,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "bar", Namespace: "test", ResourceVersion: "11"},
|
||||||
|
Spec: corev1.PodSpec{
|
||||||
|
RestartPolicy: corev1.RestartPolicyAlways,
|
||||||
|
DNSPolicy: corev1.DNSClusterFirst,
|
||||||
|
TerminationGracePeriodSeconds: &grace,
|
||||||
|
SecurityContext: &corev1.PodSecurityContext{},
|
||||||
|
EnableServiceLinks: &enableServiceLinks,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
svc := &corev1.ServiceList{
|
||||||
|
ListMeta: metav1.ListMeta{
|
||||||
|
ResourceVersion: "16",
|
||||||
|
},
|
||||||
|
Items: []corev1.Service{
|
||||||
|
{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "12"},
|
||||||
|
Spec: corev1.ServiceSpec{
|
||||||
|
SessionAffinity: "None",
|
||||||
|
Type: corev1.ServiceTypeClusterIP,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
one := int32(1)
|
||||||
|
rc := &corev1.ReplicationControllerList{
|
||||||
|
ListMeta: metav1.ListMeta{
|
||||||
|
ResourceVersion: "17",
|
||||||
|
},
|
||||||
|
Items: []corev1.ReplicationController{
|
||||||
|
{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "rc1", Namespace: "test", ResourceVersion: "18"},
|
||||||
|
Spec: corev1.ReplicationControllerSpec{
|
||||||
|
Replicas: &one,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return pods, svc, rc
|
||||||
|
}
|
||||||
|
|
||||||
|
// EmptyTestData returns no pod, service, or replication controller
|
||||||
|
func EmptyTestData() (*corev1.PodList, *corev1.ServiceList, *corev1.ReplicationControllerList) {
|
||||||
|
pods := &corev1.PodList{
|
||||||
|
ListMeta: metav1.ListMeta{
|
||||||
|
ResourceVersion: "15",
|
||||||
|
},
|
||||||
|
Items: []corev1.Pod{},
|
||||||
|
}
|
||||||
|
svc := &corev1.ServiceList{
|
||||||
|
ListMeta: metav1.ListMeta{
|
||||||
|
ResourceVersion: "16",
|
||||||
|
},
|
||||||
|
Items: []corev1.Service{},
|
||||||
|
}
|
||||||
|
|
||||||
|
rc := &corev1.ReplicationControllerList{
|
||||||
|
ListMeta: metav1.ListMeta{
|
||||||
|
ResourceVersion: "17",
|
||||||
|
},
|
||||||
|
Items: []corev1.ReplicationController{},
|
||||||
|
}
|
||||||
|
return pods, svc, rc
|
||||||
|
}
|
||||||
|
|
||||||
|
func SubresourceTestData() *corev1.Pod {
|
||||||
|
return &corev1.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test", ResourceVersion: "10"},
|
||||||
|
Spec: corev1.PodSpec{
|
||||||
|
RestartPolicy: corev1.RestartPolicyAlways,
|
||||||
|
DNSPolicy: corev1.DNSClusterFirst,
|
||||||
|
TerminationGracePeriodSeconds: &grace,
|
||||||
|
SecurityContext: &corev1.PodSecurityContext{},
|
||||||
|
EnableServiceLinks: &enableServiceLinks,
|
||||||
|
},
|
||||||
|
Status: corev1.PodStatus{
|
||||||
|
Phase: corev1.PodPending,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenResponseWithJsonEncodedBody(bodyStruct interface{}) (*http.Response, error) {
|
||||||
|
jsonBytes, err := json.Marshal(bodyStruct)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &http.Response{StatusCode: http.StatusOK, Header: DefaultHeader(), Body: BytesBody(jsonBytes)}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func InitTestErrorHandler(t *testing.T) {
|
||||||
|
cmdutil.BehaviorOnFatal(func(str string, code int) {
|
||||||
|
t.Errorf("Error running command (exit code %d): %s", code, str)
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,170 @@
|
||||||
|
//go:build !ignore_autogenerated
|
||||||
|
// +build !ignore_autogenerated
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Code generated by deepcopy-gen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package testing
|
||||||
|
|
||||||
|
import (
|
||||||
|
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *ExternalNamespacedType) DeepCopyInto(out *ExternalNamespacedType) {
|
||||||
|
*out = *in
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExternalNamespacedType.
|
||||||
|
func (in *ExternalNamespacedType) DeepCopy() *ExternalNamespacedType {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(ExternalNamespacedType)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||||
|
func (in *ExternalNamespacedType) DeepCopyObject() runtime.Object {
|
||||||
|
if c := in.DeepCopy(); c != nil {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *ExternalNamespacedType2) DeepCopyInto(out *ExternalNamespacedType2) {
|
||||||
|
*out = *in
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExternalNamespacedType2.
|
||||||
|
func (in *ExternalNamespacedType2) DeepCopy() *ExternalNamespacedType2 {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(ExternalNamespacedType2)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||||
|
func (in *ExternalNamespacedType2) DeepCopyObject() runtime.Object {
|
||||||
|
if c := in.DeepCopy(); c != nil {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *ExternalType) DeepCopyInto(out *ExternalType) {
|
||||||
|
*out = *in
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExternalType.
|
||||||
|
func (in *ExternalType) DeepCopy() *ExternalType {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(ExternalType)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||||
|
func (in *ExternalType) DeepCopyObject() runtime.Object {
|
||||||
|
if c := in.DeepCopy(); c != nil {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *ExternalType2) DeepCopyInto(out *ExternalType2) {
|
||||||
|
*out = *in
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExternalType2.
|
||||||
|
func (in *ExternalType2) DeepCopy() *ExternalType2 {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(ExternalType2)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||||
|
func (in *ExternalType2) DeepCopyObject() runtime.Object {
|
||||||
|
if c := in.DeepCopy(); c != nil {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *InternalNamespacedType) DeepCopyInto(out *InternalNamespacedType) {
|
||||||
|
*out = *in
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InternalNamespacedType.
|
||||||
|
func (in *InternalNamespacedType) DeepCopy() *InternalNamespacedType {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(InternalNamespacedType)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||||
|
func (in *InternalNamespacedType) DeepCopyObject() runtime.Object {
|
||||||
|
if c := in.DeepCopy(); c != nil {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *InternalType) DeepCopyInto(out *InternalType) {
|
||||||
|
*out = *in
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InternalType.
|
||||||
|
func (in *InternalType) DeepCopy() *InternalType {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(InternalType)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||||
|
func (in *InternalType) DeepCopyObject() runtime.Object {
|
||||||
|
if c := in.DeepCopy(); c != nil {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
/*
|
||||||
|
Copyright 2017 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package testing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
"k8s.io/kube-openapi/pkg/util/proto"
|
||||||
|
"k8s.io/kube-openapi/pkg/util/proto/testing"
|
||||||
|
"k8s.io/kubectl/pkg/util/openapi"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FakeResources is a wrapper to directly load the openapi schema from a
|
||||||
|
// file, and get the schema for given GVK. This is only for test since
|
||||||
|
// it's assuming that the file is there and everything will go fine.
|
||||||
|
type FakeResources struct {
|
||||||
|
fake testing.Fake
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ openapi.Resources = &FakeResources{}
|
||||||
|
|
||||||
|
// NewFakeResources creates a new FakeResources.
|
||||||
|
func NewFakeResources(path string) *FakeResources {
|
||||||
|
return &FakeResources{
|
||||||
|
fake: testing.Fake{Path: path},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LookupResource will read the schema, parse it and return the
|
||||||
|
// resources. It doesn't return errors and will panic instead.
|
||||||
|
func (f *FakeResources) LookupResource(gvk schema.GroupVersionKind) proto.Schema {
|
||||||
|
s, err := f.fake.OpenAPISchema()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
resources, err := openapi.NewOpenAPIData(s)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return resources.LookupResource(gvk)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EmptyResources implement a Resources that just doesn't have any resources.
|
||||||
|
type EmptyResources struct{}
|
||||||
|
|
||||||
|
var _ openapi.Resources = EmptyResources{}
|
||||||
|
|
||||||
|
// LookupResource will always return nil. It doesn't have any resources.
|
||||||
|
func (f EmptyResources) LookupResource(gvk schema.GroupVersionKind) proto.Schema {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOpenAPISchemaFunc returns a function useful for the TestFactory.
|
||||||
|
func CreateOpenAPISchemaFunc(path string) func() (openapi.Resources, error) {
|
||||||
|
return func() (openapi.Resources, error) {
|
||||||
|
return NewFakeResources(path), nil
|
||||||
|
}
|
||||||
|
}
|
|
@ -840,6 +840,7 @@ k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextension
|
||||||
k8s.io/apimachinery/pkg/api/equality
|
k8s.io/apimachinery/pkg/api/equality
|
||||||
k8s.io/apimachinery/pkg/api/errors
|
k8s.io/apimachinery/pkg/api/errors
|
||||||
k8s.io/apimachinery/pkg/api/meta
|
k8s.io/apimachinery/pkg/api/meta
|
||||||
|
k8s.io/apimachinery/pkg/api/meta/testrestmapper
|
||||||
k8s.io/apimachinery/pkg/api/resource
|
k8s.io/apimachinery/pkg/api/resource
|
||||||
k8s.io/apimachinery/pkg/api/validation
|
k8s.io/apimachinery/pkg/api/validation
|
||||||
k8s.io/apimachinery/pkg/api/validation/path
|
k8s.io/apimachinery/pkg/api/validation/path
|
||||||
|
@ -1455,6 +1456,7 @@ k8s.io/kube-openapi/pkg/schemamutation
|
||||||
k8s.io/kube-openapi/pkg/spec3
|
k8s.io/kube-openapi/pkg/spec3
|
||||||
k8s.io/kube-openapi/pkg/util
|
k8s.io/kube-openapi/pkg/util
|
||||||
k8s.io/kube-openapi/pkg/util/proto
|
k8s.io/kube-openapi/pkg/util/proto
|
||||||
|
k8s.io/kube-openapi/pkg/util/proto/testing
|
||||||
k8s.io/kube-openapi/pkg/util/proto/validation
|
k8s.io/kube-openapi/pkg/util/proto/validation
|
||||||
k8s.io/kube-openapi/pkg/util/sets
|
k8s.io/kube-openapi/pkg/util/sets
|
||||||
k8s.io/kube-openapi/pkg/validation/spec
|
k8s.io/kube-openapi/pkg/validation/spec
|
||||||
|
@ -1468,6 +1470,7 @@ k8s.io/kubectl/pkg/cmd/describe
|
||||||
k8s.io/kubectl/pkg/cmd/exec
|
k8s.io/kubectl/pkg/cmd/exec
|
||||||
k8s.io/kubectl/pkg/cmd/get
|
k8s.io/kubectl/pkg/cmd/get
|
||||||
k8s.io/kubectl/pkg/cmd/logs
|
k8s.io/kubectl/pkg/cmd/logs
|
||||||
|
k8s.io/kubectl/pkg/cmd/testing
|
||||||
k8s.io/kubectl/pkg/cmd/util
|
k8s.io/kubectl/pkg/cmd/util
|
||||||
k8s.io/kubectl/pkg/cmd/util/editor
|
k8s.io/kubectl/pkg/cmd/util/editor
|
||||||
k8s.io/kubectl/pkg/cmd/util/editor/crlf
|
k8s.io/kubectl/pkg/cmd/util/editor/crlf
|
||||||
|
@ -1486,6 +1489,7 @@ k8s.io/kubectl/pkg/util/fieldpath
|
||||||
k8s.io/kubectl/pkg/util/i18n
|
k8s.io/kubectl/pkg/util/i18n
|
||||||
k8s.io/kubectl/pkg/util/interrupt
|
k8s.io/kubectl/pkg/util/interrupt
|
||||||
k8s.io/kubectl/pkg/util/openapi
|
k8s.io/kubectl/pkg/util/openapi
|
||||||
|
k8s.io/kubectl/pkg/util/openapi/testing
|
||||||
k8s.io/kubectl/pkg/util/openapi/validation
|
k8s.io/kubectl/pkg/util/openapi/validation
|
||||||
k8s.io/kubectl/pkg/util/podutils
|
k8s.io/kubectl/pkg/util/podutils
|
||||||
k8s.io/kubectl/pkg/util/prune
|
k8s.io/kubectl/pkg/util/prune
|
||||||
|
|
Loading…
Reference in New Issue