velero install: wait for restic daemonset to be ready (#1859)

* velero install: wait for restic daemonset to be ready

Signed-off-by: Steve Kriss <krisss@vmware.com>

* changelog

Signed-off-by: Steve Kriss <krisss@vmware.com>
pull/1864/head
Steve Kriss 2019-09-09 15:54:47 -06:00 committed by Nolan Brubaker
parent 8da9ef2b03
commit 11190065fd
3 changed files with 56 additions and 1 deletions

View File

@ -0,0 +1 @@
velero install: if `--use-restic` and `--wait` are specified, wait up to a minute for restic daemonset to be ready

View File

@ -238,10 +238,17 @@ func (o *InstallOptions) Run(c *cobra.Command, f client.Factory) error {
} }
if o.Wait { if o.Wait {
fmt.Println("Waiting for Velero to be ready.") fmt.Println("Waiting for Velero deployment to be ready.")
if _, err = install.DeploymentIsReady(factory, o.Namespace); err != nil { if _, err = install.DeploymentIsReady(factory, o.Namespace); err != nil {
return errors.Wrap(err, errorMsg) return errors.Wrap(err, errorMsg)
} }
if o.UseRestic {
fmt.Println("Waiting for Velero restic daemonset to be ready.")
if _, err = install.DaemonSetIsReady(factory, o.Namespace); err != nil {
return errors.Wrap(err, errorMsg)
}
}
} }
if o.SecretFile == "" { if o.SecretFile == "" {
fmt.Printf("\nNo secret file was specified, no Secret created.\n\n") fmt.Printf("\nNo secret file was specified, no Secret created.\n\n")

View File

@ -175,6 +175,53 @@ func DeploymentIsReady(factory client.DynamicFactory, namespace string) (bool, e
return isReady, err return isReady, err
} }
// DaemonSetIsReady will poll the kubernetes API server to ensure the restic daemonset is ready, i.e. that
// pods are scheduled and available on all of the the desired nodes.
func DaemonSetIsReady(factory client.DynamicFactory, namespace string) (bool, error) {
gvk := schema.FromAPIVersionAndKind(appsv1.SchemeGroupVersion.String(), "DaemonSet")
apiResource := metav1.APIResource{
Name: "daemonsets",
Namespaced: true,
}
c, err := factory.ClientForGroupVersionResource(gvk.GroupVersion(), apiResource, namespace)
if err != nil {
return false, errors.Wrapf(err, "Error creating client for daemonset polling")
}
// declare this variable out of scope so we can return it
var isReady bool
var readyObservations int32
err = wait.PollImmediate(time.Second, time.Minute, func() (bool, error) {
unstructuredDaemonSet, err := c.Get("restic", metav1.GetOptions{})
if apierrors.IsNotFound(err) {
return false, nil
} else if err != nil {
return false, errors.Wrap(err, "error waiting for daemonset to be ready")
}
daemonSet := new(appsv1.DaemonSet)
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(unstructuredDaemonSet.Object, daemonSet); err != nil {
return false, errors.Wrap(err, "error converting daemonset from unstructured")
}
if daemonSet.Status.NumberAvailable == daemonSet.Status.DesiredNumberScheduled {
readyObservations++
}
// Wait for 5 observations of the daemonset being "ready" to be consistent with our check for
// the deployment being ready.
if readyObservations > 4 {
isReady = true
return true, nil
} else {
return false, nil
}
})
return isReady, err
}
// GroupResources groups resources based on whether the resources are CustomResourceDefinitions or other types of kubernetes objects // GroupResources groups resources based on whether the resources are CustomResourceDefinitions or other types of kubernetes objects
// This is useful to wait for readiness before creating CRD objects // This is useful to wait for readiness before creating CRD objects
func GroupResources(resources *unstructured.UnstructuredList) *ResourceGroup { func GroupResources(resources *unstructured.UnstructuredList) *ResourceGroup {