diff --git a/pkg/action/install.go b/pkg/action/install.go index 8f1b5528b..4019dbc54 100644 --- a/pkg/action/install.go +++ b/pkg/action/install.go @@ -126,7 +126,7 @@ func (i *Install) installCRDs(crds []*chart.File) error { i.cfg.Log("CRD %s is already present. Skipping.", crdName) continue } - return errors.Wrapf(err, "failed to instal CRD %s", obj.Name) + return errors.Wrapf(err, "failed to install CRD %s", obj.Name) } totalItems = append(totalItems, res...) } diff --git a/pkg/kube/wait.go b/pkg/kube/wait.go index 7198917c4..f0005a61e 100644 --- a/pkg/kube/wait.go +++ b/pkg/kube/wait.go @@ -27,6 +27,7 @@ import ( batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" extensionsv1beta1 "k8s.io/api/extensions/v1beta1" + 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/labels" @@ -114,6 +115,17 @@ func (w *waiter) waitForResources(created ResourceList) error { if err := scheme.Scheme.Convert(v.Object, crd, nil); err != nil { return false, err } + if !w.crdBetaReady(*crd) { + return false, nil + } + case *apiextv1.CustomResourceDefinition: + if err := v.Get(); err != nil { + return false, err + } + crd := &apiextv1.CustomResourceDefinition{} + if err := scheme.Scheme.Convert(v.Object, crd, nil); err != nil { + return false, err + } if !w.crdReady(*crd) { return false, nil } @@ -229,7 +241,10 @@ func (w *waiter) daemonSetReady(ds *appsv1.DaemonSet) bool { return true } -func (w *waiter) crdReady(crd apiextv1beta1.CustomResourceDefinition) bool { +// 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 (w *waiter) crdBetaReady(crd apiextv1beta1.CustomResourceDefinition) bool { for _, cond := range crd.Status.Conditions { switch cond.Type { case apiextv1beta1.Established: @@ -249,6 +264,26 @@ func (w *waiter) crdReady(crd apiextv1beta1.CustomResourceDefinition) bool { return false } +func (w *waiter) 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 (w *waiter) statefulSetReady(sts *appsv1.StatefulSet) bool { // If the update strategy is not a rolling update, there will be nothing to wait for if sts.Spec.UpdateStrategy.Type != appsv1.RollingUpdateStatefulSetStrategyType {