|
|
|
@ -24,8 +24,10 @@ import (
|
|
|
|
|
"sort"
|
|
|
|
|
"strings"
|
|
|
|
|
"testing"
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
v1 "k8s.io/api/core/v1"
|
|
|
|
|
"k8s.io/api/core/v1"
|
|
|
|
|
apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
|
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
|
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
|
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
|
|
@ -33,15 +35,35 @@ import (
|
|
|
|
|
"k8s.io/client-go/kubernetes/scheme"
|
|
|
|
|
"k8s.io/client-go/rest/fake"
|
|
|
|
|
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
|
|
|
|
|
kubectlscheme "k8s.io/kubernetes/pkg/kubectl/scheme"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
|
err := apiextv1beta1.AddToScheme(scheme.Scheme)
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Tiller use the scheme from go-client, but the cmdtesting
|
|
|
|
|
// package used here is hardcoded to use the scheme from
|
|
|
|
|
// kubectl. So for testing, we need to add the CustomResourceDefinition
|
|
|
|
|
// type to both schemes.
|
|
|
|
|
err = apiextv1beta1.AddToScheme(kubectlscheme.Scheme)
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
codec = scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
|
|
|
|
|
unstructuredSerializer = resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func getCodec() runtime.Codec {
|
|
|
|
|
return scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func objBody(obj runtime.Object) io.ReadCloser {
|
|
|
|
|
return ioutil.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(codec, obj))))
|
|
|
|
|
return ioutil.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(getCodec(), obj))))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func newPod(name string) v1.Pod {
|
|
|
|
@ -103,7 +125,7 @@ func notFoundBody() *metav1.Status {
|
|
|
|
|
func newResponse(code int, obj runtime.Object) (*http.Response, error) {
|
|
|
|
|
header := http.Header{}
|
|
|
|
|
header.Set("Content-Type", runtime.ContentTypeJSON)
|
|
|
|
|
body := ioutil.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(codec, obj))))
|
|
|
|
|
body := ioutil.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(getCodec(), obj))))
|
|
|
|
|
return &http.Response{StatusCode: code, Header: header, Body: body}, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -434,6 +456,88 @@ func TestResourceSortOrder(t *testing.T) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestWaitUntilCRDEstablished(t *testing.T) {
|
|
|
|
|
testCases := map[string]struct {
|
|
|
|
|
conditions []apiextv1beta1.CustomResourceDefinitionCondition
|
|
|
|
|
returnConditionsAfter int
|
|
|
|
|
success bool
|
|
|
|
|
}{
|
|
|
|
|
"crd reaches established state after 2 requests": {
|
|
|
|
|
conditions: []apiextv1beta1.CustomResourceDefinitionCondition{
|
|
|
|
|
{
|
|
|
|
|
Type: apiextv1beta1.Established,
|
|
|
|
|
Status: apiextv1beta1.ConditionTrue,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
returnConditionsAfter: 2,
|
|
|
|
|
success: true,
|
|
|
|
|
},
|
|
|
|
|
"crd does not reach established state before timeout": {
|
|
|
|
|
conditions: []apiextv1beta1.CustomResourceDefinitionCondition{},
|
|
|
|
|
returnConditionsAfter: 100,
|
|
|
|
|
success: false,
|
|
|
|
|
},
|
|
|
|
|
"crd name is not accepted": {
|
|
|
|
|
conditions: []apiextv1beta1.CustomResourceDefinitionCondition{
|
|
|
|
|
{
|
|
|
|
|
Type: apiextv1beta1.NamesAccepted,
|
|
|
|
|
Status: apiextv1beta1.ConditionFalse,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
returnConditionsAfter: 1,
|
|
|
|
|
success: false,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for tn, tc := range testCases {
|
|
|
|
|
func(name string) {
|
|
|
|
|
c := newTestClient()
|
|
|
|
|
defer c.Cleanup()
|
|
|
|
|
|
|
|
|
|
crdWithoutConditions := newCrdWithStatus("name", apiextv1beta1.CustomResourceDefinitionStatus{})
|
|
|
|
|
crdWithConditions := newCrdWithStatus("name", apiextv1beta1.CustomResourceDefinitionStatus{
|
|
|
|
|
Conditions: tc.conditions,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
requestCount := 0
|
|
|
|
|
c.TestFactory.UnstructuredClient = &fake.RESTClient{
|
|
|
|
|
GroupVersion: schema.GroupVersion{Version: "v1"},
|
|
|
|
|
NegotiatedSerializer: unstructuredSerializer,
|
|
|
|
|
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
|
|
|
|
|
var crd apiextv1beta1.CustomResourceDefinition
|
|
|
|
|
if requestCount < tc.returnConditionsAfter {
|
|
|
|
|
crd = crdWithoutConditions
|
|
|
|
|
} else {
|
|
|
|
|
crd = crdWithConditions
|
|
|
|
|
}
|
|
|
|
|
requestCount += 1
|
|
|
|
|
return newResponse(200, &crd)
|
|
|
|
|
}),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err := c.WaitUntilCRDEstablished(strings.NewReader(crdManifest), 5*time.Second)
|
|
|
|
|
if err != nil && tc.success {
|
|
|
|
|
t.Errorf("%s: expected no error, but got %v", name, err)
|
|
|
|
|
}
|
|
|
|
|
if err == nil && !tc.success {
|
|
|
|
|
t.Errorf("%s: expected error, but didn't get one", name)
|
|
|
|
|
}
|
|
|
|
|
}(tn)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func newCrdWithStatus(name string, status apiextv1beta1.CustomResourceDefinitionStatus) apiextv1beta1.CustomResourceDefinition {
|
|
|
|
|
crd := apiextv1beta1.CustomResourceDefinition{
|
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
|
Name: name,
|
|
|
|
|
Namespace: metav1.NamespaceDefault,
|
|
|
|
|
},
|
|
|
|
|
Spec: apiextv1beta1.CustomResourceDefinitionSpec{},
|
|
|
|
|
Status: status,
|
|
|
|
|
}
|
|
|
|
|
return crd
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestPerform(t *testing.T) {
|
|
|
|
|
tests := []struct {
|
|
|
|
|
name string
|
|
|
|
@ -701,3 +805,41 @@ spec:
|
|
|
|
|
ports:
|
|
|
|
|
- containerPort: 80
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
const crdManifest = `
|
|
|
|
|
apiVersion: apiextensions.k8s.io/v1beta1
|
|
|
|
|
kind: CustomResourceDefinition
|
|
|
|
|
metadata:
|
|
|
|
|
creationTimestamp: null
|
|
|
|
|
labels:
|
|
|
|
|
controller-tools.k8s.io: "1.0"
|
|
|
|
|
name: applications.app.k8s.io
|
|
|
|
|
spec:
|
|
|
|
|
group: app.k8s.io
|
|
|
|
|
names:
|
|
|
|
|
kind: Application
|
|
|
|
|
plural: applications
|
|
|
|
|
scope: Namespaced
|
|
|
|
|
validation:
|
|
|
|
|
openAPIV3Schema:
|
|
|
|
|
properties:
|
|
|
|
|
apiVersion:
|
|
|
|
|
description: 'Description'
|
|
|
|
|
type: string
|
|
|
|
|
kind:
|
|
|
|
|
description: 'Kind'
|
|
|
|
|
type: string
|
|
|
|
|
metadata:
|
|
|
|
|
type: object
|
|
|
|
|
spec:
|
|
|
|
|
type: object
|
|
|
|
|
status:
|
|
|
|
|
type: object
|
|
|
|
|
version: v1beta1
|
|
|
|
|
status:
|
|
|
|
|
acceptedNames:
|
|
|
|
|
kind: ""
|
|
|
|
|
plural: ""
|
|
|
|
|
conditions: []
|
|
|
|
|
storedVersions: []
|
|
|
|
|
`
|
|
|
|
|