Support apiextensions.k8s.io/v1

Signed-off-by: Jimmi Dyson <jimmidyson@gmail.com>
pull/8460/head
Jimmi Dyson 5 years ago
parent 73d88af866
commit fc89c12df7
No known key found for this signature in database
GPG Key ID: 87E8177D08CBC17B

@ -39,6 +39,7 @@ import (
batch "k8s.io/api/batch/v1"
v1 "k8s.io/api/core/v1"
extv1beta1 "k8s.io/api/extensions/v1beta1"
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
apiequality "k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/api/errors"
@ -81,16 +82,24 @@ type Client struct {
Log func(string, ...interface{})
}
var addToScheme sync.Once
// New creates a new Client.
func New(getter genericclioptions.RESTClientGetter) *Client {
if getter == nil {
getter = genericclioptions.NewConfigFlags(true)
}
err := apiextv1beta1.AddToScheme(scheme.Scheme)
if err != nil {
panic(err)
}
// Add CRDs to the scheme. They are missing by default.
addToScheme.Do(func() {
if err := apiextv1.AddToScheme(scheme.Scheme); err != nil {
// This should never happen.
panic(err)
}
if err := apiextv1beta1.AddToScheme(scheme.Scheme); err != nil {
panic(err)
}
})
return &Client{
Factory: cmdutil.NewFactory(getter),
@ -385,7 +394,7 @@ func (c *Client) Get(namespace string, reader io.Reader) (string, error) {
return nil
}
//Get the relation pods
// Get the relation pods
objs, err = c.getSelectRelationPod(info, objs)
if err != nil {
c.Log("Warning: get the relation pod is failed, err:%s", err.Error())
@ -733,32 +742,20 @@ func (c *Client) pollCRDEstablished(t time.Duration) ResourceActorFunc {
}
func (c *Client) pollCRDUntilEstablished(timeout time.Duration, info *resource.Info) error {
return wait.PollImmediate(time.Second, timeout, func() (bool, error) {
err := info.Get()
if err != nil {
return false, fmt.Errorf("unable to get CRD: %v", err)
}
crd := &apiextv1beta1.CustomResourceDefinition{}
err = scheme.Scheme.Convert(info.Object, crd, nil)
if err != nil {
return false, fmt.Errorf("unable to convert to CRD type: %v", err)
}
obj, err := asVersioned(info)
if err != nil {
return fmt.Errorf("unable to get versioned CRD: %v", err)
}
for _, cond := range crd.Status.Conditions {
switch cond.Type {
case apiextv1beta1.Established:
if cond.Status == apiextv1beta1.ConditionTrue {
return true, nil
}
case apiextv1beta1.NamesAccepted:
if cond.Status == apiextv1beta1.ConditionFalse {
return false, fmt.Errorf("naming conflict detected for CRD %s", crd.GetName())
}
}
return wait.PollImmediate(time.Second, timeout, func() (bool, error) {
switch value := obj.(type) {
case *apiextv1beta1.CustomResourceDefinition:
return c.crdBetaReady(*value), nil
case *apiextv1.CustomResourceDefinition:
return c.crdReady(*value), nil
default:
return false, fmt.Errorf("invalid CRD kind: %T", value)
}
return false, nil
})
}
@ -839,10 +836,11 @@ func createPatch(target *resource.Info, current runtime.Object) ([]byte, types.P
_, isUnstructured := versionedObject.(runtime.Unstructured)
// On newer K8s versions, CRDs aren't unstructured but has this dedicated type
_, isCRD := versionedObject.(*apiextv1beta1.CustomResourceDefinition)
_, isV1CRD := versionedObject.(*apiextv1.CustomResourceDefinition)
_, isV1Beta1CRD := versionedObject.(*apiextv1beta1.CustomResourceDefinition)
switch {
case runtime.IsNotRegisteredError(err), isUnstructured, isCRD:
case runtime.IsNotRegisteredError(err), isUnstructured, isV1CRD, isV1Beta1CRD:
// fall back to generic JSON merge patch
patch, err := jsonpatch.CreateMergePatch(oldData, newData)
if err != nil {
@ -1171,6 +1169,49 @@ func (c *Client) getSelectRelationPod(info *resource.Info, objs map[string][]run
return objs, nil
}
// Because the v1 extensions API is not available on all supported k8s versions
// yet and because Go doesn't support generics, we need to have a duplicate
// function to support the v1beta1 types
func (c *Client) crdBetaReady(crd apiextv1beta1.CustomResourceDefinition) bool {
for _, cond := range crd.Status.Conditions {
switch cond.Type {
case apiextv1beta1.Established:
if cond.Status == apiextv1beta1.ConditionTrue {
return true
}
case apiextv1beta1.NamesAccepted:
if cond.Status == apiextv1beta1.ConditionFalse {
// This indicates a naming conflict, but it's probably not the
// job of this function to fail because of that. Instead,
// we treat it as a success, since the process should be able to
// continue.
return true
}
}
}
return false
}
func (c *Client) crdReady(crd apiextv1.CustomResourceDefinition) bool {
for _, cond := range crd.Status.Conditions {
switch cond.Type {
case apiextv1.Established:
if cond.Status == apiextv1.ConditionTrue {
return true
}
case apiextv1.NamesAccepted:
if cond.Status == apiextv1.ConditionFalse {
// This indicates a naming conflict, but it's probably not the
// job of this function to fail because of that. Instead,
// we treat it as a success, since the process should be able to
// continue.
return true
}
}
}
return false
}
func asVersionedOrUnstructured(info *resource.Info) runtime.Object {
obj, _ := asVersioned(info)
return obj

@ -27,6 +27,7 @@ import (
"time"
v1 "k8s.io/api/core/v1"
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
@ -43,6 +44,10 @@ func init() {
if err != nil {
panic(err)
}
err = apiextv1.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
@ -52,6 +57,10 @@ func init() {
if err != nil {
panic(err)
}
err = apiextv1.AddToScheme(kubectlscheme.Scheme)
if err != nil {
panic(err)
}
}
var (
@ -533,32 +542,64 @@ func TestResourceSortOrder(t *testing.T) {
func TestWaitUntilCRDEstablished(t *testing.T) {
testCases := map[string]struct {
conditions []apiextv1beta1.CustomResourceDefinitionCondition
conditions []apiextv1.CustomResourceDefinitionCondition
crdManifest string
returnConditionsAfter int
success bool
}{
"crd reaches established state after 2 requests": {
conditions: []apiextv1beta1.CustomResourceDefinitionCondition{
"v1beta1 crd reaches established state after 2 requests": {
conditions: []apiextv1.CustomResourceDefinitionCondition{
{
Type: apiextv1beta1.Established,
Status: apiextv1beta1.ConditionTrue,
Type: apiextv1.Established,
Status: apiextv1.ConditionTrue,
},
},
crdManifest: crdV1Beta1Manifest,
returnConditionsAfter: 2,
success: true,
},
"crd does not reach established state before timeout": {
conditions: []apiextv1beta1.CustomResourceDefinitionCondition{},
"v1beta1 crd does not reach established state before timeout": {
conditions: []apiextv1.CustomResourceDefinitionCondition{},
crdManifest: crdV1Beta1Manifest,
returnConditionsAfter: 100,
success: false,
},
"crd name is not accepted": {
conditions: []apiextv1beta1.CustomResourceDefinitionCondition{
"v1beta1 crd name is not accepted": {
conditions: []apiextv1.CustomResourceDefinitionCondition{
{
Type: apiextv1beta1.NamesAccepted,
Status: apiextv1beta1.ConditionFalse,
Type: apiextv1.NamesAccepted,
Status: apiextv1.ConditionFalse,
},
},
crdManifest: crdV1Beta1Manifest,
returnConditionsAfter: 1,
success: false,
},
"v1 crd reaches established state after 2 requests": {
conditions: []apiextv1.CustomResourceDefinitionCondition{
{
Type: apiextv1.Established,
Status: apiextv1.ConditionTrue,
},
},
crdManifest: crdV1Manifest,
returnConditionsAfter: 2,
success: true,
},
"v1 crd does not reach established state before timeout": {
conditions: []apiextv1.CustomResourceDefinitionCondition{},
crdManifest: crdV1Manifest,
returnConditionsAfter: 100,
success: false,
},
"v1 crd name is not accepted": {
conditions: []apiextv1.CustomResourceDefinitionCondition{
{
Type: apiextv1.NamesAccepted,
Status: apiextv1.ConditionFalse,
},
},
crdManifest: crdV1Manifest,
returnConditionsAfter: 1,
success: false,
},
@ -569,8 +610,8 @@ func TestWaitUntilCRDEstablished(t *testing.T) {
c := newTestClient()
defer c.Cleanup()
crdWithoutConditions := newCrdWithStatus("name", apiextv1beta1.CustomResourceDefinitionStatus{})
crdWithConditions := newCrdWithStatus("name", apiextv1beta1.CustomResourceDefinitionStatus{
crdWithoutConditions := newCrdWithStatus("name", apiextv1.CustomResourceDefinitionStatus{})
crdWithConditions := newCrdWithStatus("name", apiextv1.CustomResourceDefinitionStatus{
Conditions: tc.conditions,
})
@ -579,7 +620,7 @@ func TestWaitUntilCRDEstablished(t *testing.T) {
GroupVersion: schema.GroupVersion{Version: "v1"},
NegotiatedSerializer: unstructuredSerializer,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
var crd apiextv1beta1.CustomResourceDefinition
var crd apiextv1.CustomResourceDefinition
if requestCount < tc.returnConditionsAfter {
crd = crdWithoutConditions
} else {
@ -590,7 +631,7 @@ func TestWaitUntilCRDEstablished(t *testing.T) {
}),
}
err := c.WaitUntilCRDEstablished(strings.NewReader(crdManifest), 5*time.Second)
err := c.WaitUntilCRDEstablished(strings.NewReader(crdV1Beta1Manifest), 5*time.Second)
if err != nil && tc.success {
t.Errorf("%s: expected no error, but got %v", name, err)
}
@ -601,13 +642,13 @@ func TestWaitUntilCRDEstablished(t *testing.T) {
}
}
func newCrdWithStatus(name string, status apiextv1beta1.CustomResourceDefinitionStatus) apiextv1beta1.CustomResourceDefinition {
crd := apiextv1beta1.CustomResourceDefinition{
func newCrdWithStatus(name string, status apiextv1.CustomResourceDefinitionStatus) apiextv1.CustomResourceDefinition {
crd := apiextv1.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: metav1.NamespaceDefault,
},
Spec: apiextv1beta1.CustomResourceDefinitionSpec{},
Spec: apiextv1.CustomResourceDefinitionSpec{},
Status: status,
}
return crd
@ -881,7 +922,7 @@ spec:
- containerPort: 80
`
const crdManifest = `
const crdV1Beta1Manifest = `
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
@ -918,3 +959,41 @@ status:
conditions: []
storedVersions: []
`
const crdV1Manifest = `
apiVersion: apiextensions.k8s.io/v1
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: []
`

Loading…
Cancel
Save