github.com/sgoings/helm@v2.0.0-alpha.2.0.20170406211108-734e92851ac3+incompatible/pkg/kube/client.go (about)

     1  /*
     2  Copyright 2016 The Kubernetes Authors All rights reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package kube // import "k8s.io/helm/pkg/kube"
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/json"
    22  	goerrors "errors"
    23  	"fmt"
    24  	"io"
    25  	"log"
    26  	"strings"
    27  	"time"
    28  
    29  	jsonpatch "github.com/evanphx/json-patch"
    30  	"k8s.io/kubernetes/pkg/api"
    31  	"k8s.io/kubernetes/pkg/api/errors"
    32  	"k8s.io/kubernetes/pkg/api/meta"
    33  	"k8s.io/kubernetes/pkg/api/v1"
    34  	apps "k8s.io/kubernetes/pkg/apis/apps/v1beta1"
    35  	batchinternal "k8s.io/kubernetes/pkg/apis/batch"
    36  	batch "k8s.io/kubernetes/pkg/apis/batch/v1"
    37  	ext "k8s.io/kubernetes/pkg/apis/extensions"
    38  	extensions "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
    39  	"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
    40  	conditions "k8s.io/kubernetes/pkg/client/unversioned"
    41  	"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
    42  	deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util"
    43  	"k8s.io/kubernetes/pkg/fields"
    44  	"k8s.io/kubernetes/pkg/kubectl"
    45  	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
    46  	"k8s.io/kubernetes/pkg/kubectl/resource"
    47  	"k8s.io/kubernetes/pkg/labels"
    48  	"k8s.io/kubernetes/pkg/runtime"
    49  	"k8s.io/kubernetes/pkg/util/strategicpatch"
    50  	"k8s.io/kubernetes/pkg/util/wait"
    51  	"k8s.io/kubernetes/pkg/watch"
    52  )
    53  
    54  // ErrNoObjectsVisited indicates that during a visit operation, no matching objects were found.
    55  var ErrNoObjectsVisited = goerrors.New("no objects visited")
    56  
    57  // Client represents a client capable of communicating with the Kubernetes API.
    58  type Client struct {
    59  	cmdutil.Factory
    60  	// SchemaCacheDir is the path for loading cached schema.
    61  	SchemaCacheDir string
    62  }
    63  
    64  // deployment holds associated replicaSets for a deployment
    65  type deployment struct {
    66  	replicaSets *ext.ReplicaSet
    67  	deployment  *ext.Deployment
    68  }
    69  
    70  // New create a new Client
    71  func New(config clientcmd.ClientConfig) *Client {
    72  	return &Client{
    73  		Factory:        cmdutil.NewFactory(config),
    74  		SchemaCacheDir: clientcmd.RecommendedSchemaFile,
    75  	}
    76  }
    77  
    78  // ResourceActorFunc performs an action on a single resource.
    79  type ResourceActorFunc func(*resource.Info) error
    80  
    81  // Create creates kubernetes resources from an io.reader
    82  //
    83  // Namespace will set the namespace
    84  func (c *Client) Create(namespace string, reader io.Reader, timeout int64, shouldWait bool) error {
    85  	client, err := c.ClientSet()
    86  	if err != nil {
    87  		return err
    88  	}
    89  	if err := ensureNamespace(client, namespace); err != nil {
    90  		return err
    91  	}
    92  	infos, buildErr := c.BuildUnstructured(namespace, reader)
    93  	if buildErr != nil {
    94  		return buildErr
    95  	}
    96  	if err := perform(c, namespace, infos, createResource); err != nil {
    97  		return err
    98  	}
    99  	if shouldWait {
   100  		return c.waitForResources(time.Duration(timeout)*time.Second, infos)
   101  	}
   102  	return nil
   103  }
   104  
   105  func (c *Client) newBuilder(namespace string, reader io.Reader) *resource.Result {
   106  	schema, err := c.Validator(true, c.SchemaCacheDir)
   107  	if err != nil {
   108  		log.Printf("warning: failed to load schema: %s", err)
   109  	}
   110  	return c.NewBuilder().
   111  		ContinueOnError().
   112  		Schema(schema).
   113  		NamespaceParam(namespace).
   114  		DefaultNamespace().
   115  		Stream(reader, "").
   116  		Flatten().
   117  		Do()
   118  }
   119  
   120  // BuildUnstructured validates for Kubernetes objects and returns unstructured infos.
   121  func (c *Client) BuildUnstructured(namespace string, reader io.Reader) (Result, error) {
   122  	schema, err := c.Validator(true, c.SchemaCacheDir)
   123  	if err != nil {
   124  		log.Printf("warning: failed to load schema: %s", err)
   125  	}
   126  
   127  	mapper, typer, err := c.UnstructuredObject()
   128  	if err != nil {
   129  		log.Printf("failed to load mapper: %s", err)
   130  		return nil, err
   131  	}
   132  	var result Result
   133  	result, err = resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(c.UnstructuredClientForMapping), runtime.UnstructuredJSONScheme).
   134  		ContinueOnError().
   135  		Schema(schema).
   136  		NamespaceParam(namespace).
   137  		DefaultNamespace().
   138  		Stream(reader, "").
   139  		Flatten().
   140  		Do().Infos()
   141  	return result, scrubValidationError(err)
   142  }
   143  
   144  // Build validates for Kubernetes objects and returns resource Infos from a io.Reader.
   145  func (c *Client) Build(namespace string, reader io.Reader) (Result, error) {
   146  	var result Result
   147  	result, err := c.newBuilder(namespace, reader).Infos()
   148  	return result, scrubValidationError(err)
   149  }
   150  
   151  // Get gets kubernetes resources as pretty printed string
   152  //
   153  // Namespace will set the namespace
   154  func (c *Client) Get(namespace string, reader io.Reader) (string, error) {
   155  	// Since we don't know what order the objects come in, let's group them by the types, so
   156  	// that when we print them, they come looking good (headers apply to subgroups, etc.)
   157  	objs := make(map[string][]runtime.Object)
   158  	infos, err := c.BuildUnstructured(namespace, reader)
   159  	if err != nil {
   160  		return "", err
   161  	}
   162  	missing := []string{}
   163  	err = perform(c, namespace, infos, func(info *resource.Info) error {
   164  		log.Printf("Doing get for %s: %q", info.Mapping.GroupVersionKind.Kind, info.Name)
   165  		obj, err := resource.NewHelper(info.Client, info.Mapping).Get(info.Namespace, info.Name, info.Export)
   166  		if err != nil {
   167  			log.Printf("WARNING: Failed Get for resource %q: %s", info.Name, err)
   168  			missing = append(missing, fmt.Sprintf("%v\t\t%s", info.Mapping.Resource, info.Name))
   169  			return nil
   170  		}
   171  		// We need to grab the ObjectReference so we can correctly group the objects.
   172  		or, err := api.GetReference(obj)
   173  		if err != nil {
   174  			log.Printf("FAILED GetReference for: %#v\n%v", obj, err)
   175  			return err
   176  		}
   177  
   178  		// Use APIVersion/Kind as grouping mechanism. I'm not sure if you can have multiple
   179  		// versions per cluster, but this certainly won't hurt anything, so let's be safe.
   180  		objType := or.APIVersion + "/" + or.Kind
   181  		objs[objType] = append(objs[objType], obj)
   182  		return nil
   183  	})
   184  	if err != nil {
   185  		return "", err
   186  	}
   187  
   188  	// Ok, now we have all the objects grouped by types (say, by v1/Pod, v1/Service, etc.), so
   189  	// spin through them and print them. Printer is cool since it prints the header only when
   190  	// an object type changes, so we can just rely on that. Problem is it doesn't seem to keep
   191  	// track of tab widths
   192  	buf := new(bytes.Buffer)
   193  	p := kubectl.NewHumanReadablePrinter(kubectl.PrintOptions{})
   194  	for t, ot := range objs {
   195  		if _, err = buf.WriteString("==> " + t + "\n"); err != nil {
   196  			return "", err
   197  		}
   198  		for _, o := range ot {
   199  			if err := p.PrintObj(o, buf); err != nil {
   200  				log.Printf("failed to print object type %s, object: %q :\n %v", t, o, err)
   201  				return "", err
   202  			}
   203  		}
   204  		if _, err := buf.WriteString("\n"); err != nil {
   205  			return "", err
   206  		}
   207  	}
   208  	if len(missing) > 0 {
   209  		buf.WriteString("==> MISSING\nKIND\t\tNAME\n")
   210  		for _, s := range missing {
   211  			fmt.Fprintln(buf, s)
   212  		}
   213  	}
   214  	return buf.String(), nil
   215  }
   216  
   217  // Update reads in the current configuration and a target configuration from io.reader
   218  //  and creates resources that don't already exists, updates resources that have been modified
   219  //  in the target configuration and deletes resources from the current configuration that are
   220  //  not present in the target configuration
   221  //
   222  // Namespace will set the namespaces
   223  func (c *Client) Update(namespace string, originalReader, targetReader io.Reader, recreate bool, timeout int64, shouldWait bool) error {
   224  	original, err := c.BuildUnstructured(namespace, originalReader)
   225  	if err != nil {
   226  		return fmt.Errorf("failed decoding reader into objects: %s", err)
   227  	}
   228  
   229  	target, err := c.BuildUnstructured(namespace, targetReader)
   230  	if err != nil {
   231  		return fmt.Errorf("failed decoding reader into objects: %s", err)
   232  	}
   233  
   234  	updateErrors := []string{}
   235  
   236  	err = target.Visit(func(info *resource.Info, err error) error {
   237  		if err != nil {
   238  			return err
   239  		}
   240  
   241  		helper := resource.NewHelper(info.Client, info.Mapping)
   242  		if _, err := helper.Get(info.Namespace, info.Name, info.Export); err != nil {
   243  			if !errors.IsNotFound(err) {
   244  				return fmt.Errorf("Could not get information about the resource: err: %s", err)
   245  			}
   246  
   247  			// Since the resource does not exist, create it.
   248  			if err := createResource(info); err != nil {
   249  				return fmt.Errorf("failed to create resource: %s", err)
   250  			}
   251  
   252  			kind := info.Mapping.GroupVersionKind.Kind
   253  			log.Printf("Created a new %s called %q\n", kind, info.Name)
   254  			return nil
   255  		}
   256  
   257  		originalInfo := original.Get(info)
   258  		if originalInfo == nil {
   259  			return fmt.Errorf("no resource with the name %q found", info.Name)
   260  		}
   261  
   262  		if err := updateResource(c, info, originalInfo.Object, recreate); err != nil {
   263  			log.Printf("error updating the resource %q:\n\t %v", info.Name, err)
   264  			updateErrors = append(updateErrors, err.Error())
   265  		}
   266  
   267  		return nil
   268  	})
   269  
   270  	switch {
   271  	case err != nil:
   272  		return err
   273  	case len(updateErrors) != 0:
   274  		return fmt.Errorf(strings.Join(updateErrors, " && "))
   275  	}
   276  
   277  	for _, info := range original.Difference(target) {
   278  		log.Printf("Deleting %q in %s...", info.Name, info.Namespace)
   279  		if err := deleteResource(c, info); err != nil {
   280  			log.Printf("Failed to delete %q, err: %s", info.Name, err)
   281  		}
   282  	}
   283  	if shouldWait {
   284  		return c.waitForResources(time.Duration(timeout)*time.Second, target)
   285  	}
   286  	return nil
   287  }
   288  
   289  // Delete deletes kubernetes resources from an io.reader
   290  //
   291  // Namespace will set the namespace
   292  func (c *Client) Delete(namespace string, reader io.Reader) error {
   293  	infos, err := c.BuildUnstructured(namespace, reader)
   294  	if err != nil {
   295  		return err
   296  	}
   297  	return perform(c, namespace, infos, func(info *resource.Info) error {
   298  		log.Printf("Starting delete for %q %s", info.Name, info.Mapping.GroupVersionKind.Kind)
   299  		err := deleteResource(c, info)
   300  		return skipIfNotFound(err)
   301  	})
   302  }
   303  
   304  func skipIfNotFound(err error) error {
   305  	if errors.IsNotFound(err) {
   306  		log.Printf("%v", err)
   307  		return nil
   308  	}
   309  	return err
   310  }
   311  
   312  func watchTimeout(t time.Duration) ResourceActorFunc {
   313  	return func(info *resource.Info) error {
   314  		return watchUntilReady(t, info)
   315  	}
   316  }
   317  
   318  // WatchUntilReady watches the resource given in the reader, and waits until it is ready.
   319  //
   320  // This function is mainly for hook implementations. It watches for a resource to
   321  // hit a particular milestone. The milestone depends on the Kind.
   322  //
   323  // For most kinds, it checks to see if the resource is marked as Added or Modified
   324  // by the Kubernetes event stream. For some kinds, it does more:
   325  //
   326  // - Jobs: A job is marked "Ready" when it has successfully completed. This is
   327  //   ascertained by watching the Status fields in a job's output.
   328  //
   329  // Handling for other kinds will be added as necessary.
   330  func (c *Client) WatchUntilReady(namespace string, reader io.Reader, timeout int64, shouldWait bool) error {
   331  	infos, err := c.Build(namespace, reader)
   332  	if err != nil {
   333  		return err
   334  	}
   335  	// For jobs, there's also the option to do poll c.Jobs(namespace).Get():
   336  	// https://github.com/adamreese/kubernetes/blob/master/test/e2e/job.go#L291-L300
   337  	return perform(c, namespace, infos, watchTimeout(time.Duration(timeout)*time.Second))
   338  }
   339  
   340  func perform(c *Client, namespace string, infos Result, fn ResourceActorFunc) error {
   341  	if len(infos) == 0 {
   342  		return ErrNoObjectsVisited
   343  	}
   344  
   345  	for _, info := range infos {
   346  		if err := fn(info); err != nil {
   347  			return err
   348  		}
   349  	}
   350  	return nil
   351  }
   352  
   353  func createResource(info *resource.Info) error {
   354  	obj, err := resource.NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, info.Object)
   355  	if err != nil {
   356  		return err
   357  	}
   358  	return info.Refresh(obj, true)
   359  }
   360  
   361  func deleteResource(c *Client, info *resource.Info) error {
   362  	reaper, err := c.Reaper(info.Mapping)
   363  	if err != nil {
   364  		// If there is no reaper for this resources, delete it.
   365  		if kubectl.IsNoSuchReaperError(err) {
   366  			return resource.NewHelper(info.Client, info.Mapping).Delete(info.Namespace, info.Name)
   367  		}
   368  		return err
   369  	}
   370  	log.Printf("Using reaper for deleting %q", info.Name)
   371  	return reaper.Stop(info.Namespace, info.Name, 0, nil)
   372  }
   373  
   374  func createPatch(mapping *meta.RESTMapping, target, current runtime.Object) ([]byte, api.PatchType, error) {
   375  	oldData, err := json.Marshal(current)
   376  	if err != nil {
   377  		return nil, api.StrategicMergePatchType, fmt.Errorf("serializing current configuration: %s", err)
   378  	}
   379  	newData, err := json.Marshal(target)
   380  	if err != nil {
   381  		return nil, api.StrategicMergePatchType, fmt.Errorf("serializing target configuration: %s", err)
   382  	}
   383  
   384  	if api.Semantic.DeepEqual(oldData, newData) {
   385  		return nil, api.StrategicMergePatchType, nil
   386  	}
   387  
   388  	// Get a versioned object
   389  	versionedObject, err := api.Scheme.New(mapping.GroupVersionKind)
   390  	switch {
   391  	case runtime.IsNotRegisteredError(err):
   392  		// fall back to generic JSON merge patch
   393  		patch, err := jsonpatch.CreateMergePatch(oldData, newData)
   394  		return patch, api.MergePatchType, err
   395  	case err != nil:
   396  		return nil, api.StrategicMergePatchType, fmt.Errorf("failed to get versionedObject: %s", err)
   397  	default:
   398  		log.Printf("generating strategic merge patch for %T", target)
   399  		patch, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, versionedObject)
   400  		return patch, api.StrategicMergePatchType, err
   401  	}
   402  }
   403  
   404  func updateResource(c *Client, target *resource.Info, currentObj runtime.Object, recreate bool) error {
   405  	patch, patchType, err := createPatch(target.Mapping, target.Object, currentObj)
   406  	if err != nil {
   407  		return fmt.Errorf("failed to create patch: %s", err)
   408  	}
   409  	if patch == nil {
   410  		log.Printf("Looks like there are no changes for %s %q", target.Mapping.GroupVersionKind.Kind, target.Name)
   411  		// This needs to happen to make sure that tiller has the latest info from the API
   412  		// Otherwise there will be no labels and other functions that use labels will panic
   413  		if err := target.Get(); err != nil {
   414  			return fmt.Errorf("error trying to refresh resource information: %v", err)
   415  		}
   416  		return nil
   417  	}
   418  
   419  	// send patch to server
   420  	helper := resource.NewHelper(target.Client, target.Mapping)
   421  	obj, err := helper.Patch(target.Namespace, target.Name, patchType, patch)
   422  	if err != nil {
   423  		return err
   424  	}
   425  
   426  	target.Refresh(obj, true)
   427  
   428  	if !recreate {
   429  		return nil
   430  	}
   431  
   432  	versioned, err := c.AsVersionedObject(target.Object)
   433  	if runtime.IsNotRegisteredError(err) {
   434  		return nil
   435  	}
   436  	if err != nil {
   437  		return err
   438  	}
   439  
   440  	selector, err := getSelectorFromObject(versioned)
   441  	if err != nil {
   442  		return nil
   443  	}
   444  	client, _ := c.ClientSet()
   445  	return recreatePods(client, target.Namespace, selector)
   446  }
   447  
   448  func getSelectorFromObject(obj runtime.Object) (map[string]string, error) {
   449  	switch typed := obj.(type) {
   450  	case *v1.ReplicationController:
   451  		return typed.Spec.Selector, nil
   452  	case *extensions.ReplicaSet:
   453  		return typed.Spec.Selector.MatchLabels, nil
   454  	case *extensions.Deployment:
   455  		return typed.Spec.Selector.MatchLabels, nil
   456  	case *extensions.DaemonSet:
   457  		return typed.Spec.Selector.MatchLabels, nil
   458  	case *batch.Job:
   459  		return typed.Spec.Selector.MatchLabels, nil
   460  	case *apps.StatefulSet:
   461  		return typed.Spec.Selector.MatchLabels, nil
   462  	default:
   463  		return nil, fmt.Errorf("Unsupported kind when getting selector: %v", obj)
   464  	}
   465  }
   466  
   467  func recreatePods(client *internalclientset.Clientset, namespace string, selector map[string]string) error {
   468  	pods, err := client.Pods(namespace).List(api.ListOptions{
   469  		FieldSelector: fields.Everything(),
   470  		LabelSelector: labels.Set(selector).AsSelector(),
   471  	})
   472  	if err != nil {
   473  		return err
   474  	}
   475  
   476  	// Restart pods
   477  	for _, pod := range pods.Items {
   478  		log.Printf("Restarting pod: %v/%v", pod.Namespace, pod.Name)
   479  
   480  		// Delete each pod for get them restarted with changed spec.
   481  		if err := client.Pods(pod.Namespace).Delete(pod.Name, api.NewPreconditionDeleteOptions(string(pod.UID))); err != nil {
   482  			return err
   483  		}
   484  	}
   485  	return nil
   486  }
   487  
   488  func watchUntilReady(timeout time.Duration, info *resource.Info) error {
   489  	w, err := resource.NewHelper(info.Client, info.Mapping).WatchSingle(info.Namespace, info.Name, info.ResourceVersion)
   490  	if err != nil {
   491  		return err
   492  	}
   493  
   494  	kind := info.Mapping.GroupVersionKind.Kind
   495  	log.Printf("Watching for changes to %s %s with timeout of %v", kind, info.Name, timeout)
   496  
   497  	// What we watch for depends on the Kind.
   498  	// - For a Job, we watch for completion.
   499  	// - For all else, we watch until Ready.
   500  	// In the future, we might want to add some special logic for types
   501  	// like Ingress, Volume, etc.
   502  
   503  	_, err = watch.Until(timeout, w, func(e watch.Event) (bool, error) {
   504  		switch e.Type {
   505  		case watch.Added, watch.Modified:
   506  			// For things like a secret or a config map, this is the best indicator
   507  			// we get. We care mostly about jobs, where what we want to see is
   508  			// the status go into a good state. For other types, like ReplicaSet
   509  			// we don't really do anything to support these as hooks.
   510  			log.Printf("Add/Modify event for %s: %v", info.Name, e.Type)
   511  			if kind == "Job" {
   512  				return waitForJob(e, info.Name)
   513  			}
   514  			return true, nil
   515  		case watch.Deleted:
   516  			log.Printf("Deleted event for %s", info.Name)
   517  			return true, nil
   518  		case watch.Error:
   519  			// Handle error and return with an error.
   520  			log.Printf("Error event for %s", info.Name)
   521  			return true, fmt.Errorf("Failed to deploy %s", info.Name)
   522  		default:
   523  			return false, nil
   524  		}
   525  	})
   526  	return err
   527  }
   528  
   529  func podsReady(pods []api.Pod) bool {
   530  	for _, pod := range pods {
   531  		if !api.IsPodReady(&pod) {
   532  			return false
   533  		}
   534  	}
   535  	return true
   536  }
   537  
   538  func servicesReady(svc []api.Service) bool {
   539  	for _, s := range svc {
   540  		// Make sure the service is not explicitly set to "None" before checking the IP
   541  		if s.Spec.ClusterIP != api.ClusterIPNone && !api.IsServiceIPSet(&s) {
   542  			return false
   543  		}
   544  		// This checks if the service has a LoadBalancer and that balancer has an Ingress defined
   545  		if s.Spec.Type == api.ServiceTypeLoadBalancer && s.Status.LoadBalancer.Ingress == nil {
   546  			return false
   547  		}
   548  	}
   549  	return true
   550  }
   551  
   552  func volumesReady(vols []api.PersistentVolumeClaim) bool {
   553  	for _, v := range vols {
   554  		if v.Status.Phase != api.ClaimBound {
   555  			return false
   556  		}
   557  	}
   558  	return true
   559  }
   560  
   561  func deploymentsReady(deployments []deployment) bool {
   562  	for _, v := range deployments {
   563  		if !(v.replicaSets.Status.ReadyReplicas >= v.deployment.Spec.Replicas-deploymentutil.MaxUnavailable(*v.deployment)) {
   564  			return false
   565  		}
   566  	}
   567  	return true
   568  }
   569  
   570  func getPods(client *internalclientset.Clientset, namespace string, selector map[string]string) ([]api.Pod, error) {
   571  	list, err := client.Pods(namespace).List(api.ListOptions{
   572  		FieldSelector: fields.Everything(),
   573  		LabelSelector: labels.Set(selector).AsSelector(),
   574  	})
   575  	return list.Items, err
   576  }
   577  
   578  // AsVersionedObject converts a runtime.object to a versioned object.
   579  func (c *Client) AsVersionedObject(obj runtime.Object) (runtime.Object, error) {
   580  	json, err := runtime.Encode(runtime.UnstructuredJSONScheme, obj)
   581  	if err != nil {
   582  		return nil, err
   583  	}
   584  	versions := &runtime.VersionedObjects{}
   585  	err = runtime.DecodeInto(c.Decoder(true), json, versions)
   586  	return versions.First(), err
   587  }
   588  
   589  // waitForResources polls to get the current status of all pods, PVCs, and Services
   590  // until all are ready or a timeout is reached
   591  func (c *Client) waitForResources(timeout time.Duration, created Result) error {
   592  	log.Printf("beginning wait for resources with timeout of %v", timeout)
   593  	client, _ := c.ClientSet()
   594  	return wait.Poll(2*time.Second, timeout, func() (bool, error) {
   595  		pods := []api.Pod{}
   596  		services := []api.Service{}
   597  		pvc := []api.PersistentVolumeClaim{}
   598  		replicaSets := []*ext.ReplicaSet{}
   599  		deployments := []deployment{}
   600  		for _, v := range created {
   601  			obj, err := c.AsVersionedObject(v.Object)
   602  			if err != nil && !runtime.IsNotRegisteredError(err) {
   603  				return false, err
   604  			}
   605  			switch value := obj.(type) {
   606  			case (*v1.ReplicationController):
   607  				list, err := getPods(client, value.Namespace, value.Spec.Selector)
   608  				if err != nil {
   609  					return false, err
   610  				}
   611  				pods = append(pods, list...)
   612  			case (*v1.Pod):
   613  				pod, err := client.Pods(value.Namespace).Get(value.Name)
   614  				if err != nil {
   615  					return false, err
   616  				}
   617  				pods = append(pods, *pod)
   618  			case (*extensions.Deployment):
   619  				// Get the RS children first
   620  				rs, err := client.ReplicaSets(value.Namespace).List(api.ListOptions{
   621  					FieldSelector: fields.Everything(),
   622  					LabelSelector: labels.Set(value.Spec.Selector.MatchLabels).AsSelector(),
   623  				})
   624  				if err != nil {
   625  					return false, err
   626  				}
   627  
   628  				for i := range rs.Items {
   629  					replicaSets = append(replicaSets, &rs.Items[i])
   630  				}
   631  
   632  				currentDeployment, err := client.Deployments(value.Namespace).Get(value.Name)
   633  				if err != nil {
   634  					return false, err
   635  				}
   636  				// Find RS associated with deployment
   637  				newReplicaSet, err := deploymentutil.FindNewReplicaSet(currentDeployment, replicaSets)
   638  				if err != nil {
   639  					return false, err
   640  				}
   641  				newDeployment := deployment{
   642  					newReplicaSet,
   643  					currentDeployment,
   644  				}
   645  				deployments = append(deployments, newDeployment)
   646  			case (*extensions.DaemonSet):
   647  				list, err := getPods(client, value.Namespace, value.Spec.Selector.MatchLabels)
   648  				if err != nil {
   649  					return false, err
   650  				}
   651  				pods = append(pods, list...)
   652  			case (*apps.StatefulSet):
   653  				list, err := getPods(client, value.Namespace, value.Spec.Selector.MatchLabels)
   654  				if err != nil {
   655  					return false, err
   656  				}
   657  				pods = append(pods, list...)
   658  			case (*extensions.ReplicaSet):
   659  				list, err := getPods(client, value.Namespace, value.Spec.Selector.MatchLabels)
   660  				if err != nil {
   661  					return false, err
   662  				}
   663  				pods = append(pods, list...)
   664  			case (*v1.PersistentVolumeClaim):
   665  				claim, err := client.PersistentVolumeClaims(value.Namespace).Get(value.Name)
   666  				if err != nil {
   667  					return false, err
   668  				}
   669  				pvc = append(pvc, *claim)
   670  			case (*v1.Service):
   671  				svc, err := client.Services(value.Namespace).Get(value.Name)
   672  				if err != nil {
   673  					return false, err
   674  				}
   675  				services = append(services, *svc)
   676  			}
   677  		}
   678  		return podsReady(pods) && servicesReady(services) && volumesReady(pvc) && deploymentsReady(deployments), nil
   679  	})
   680  }
   681  
   682  // waitForJob is a helper that waits for a job to complete.
   683  //
   684  // This operates on an event returned from a watcher.
   685  func waitForJob(e watch.Event, name string) (bool, error) {
   686  	o, ok := e.Object.(*batchinternal.Job)
   687  	if !ok {
   688  		return true, fmt.Errorf("Expected %s to be a *batch.Job, got %T", name, e.Object)
   689  	}
   690  
   691  	for _, c := range o.Status.Conditions {
   692  		if c.Type == batchinternal.JobComplete && c.Status == api.ConditionTrue {
   693  			return true, nil
   694  		} else if c.Type == batchinternal.JobFailed && c.Status == api.ConditionTrue {
   695  			return true, fmt.Errorf("Job failed: %s", c.Reason)
   696  		}
   697  	}
   698  
   699  	log.Printf("%s: Jobs active: %d, jobs failed: %d, jobs succeeded: %d", name, o.Status.Active, o.Status.Failed, o.Status.Succeeded)
   700  	return false, nil
   701  }
   702  
   703  // scrubValidationError removes kubectl info from the message
   704  func scrubValidationError(err error) error {
   705  	if err == nil {
   706  		return nil
   707  	}
   708  	const stopValidateMessage = "if you choose to ignore these errors, turn validation off with --validate=false"
   709  
   710  	if strings.Contains(err.Error(), stopValidateMessage) {
   711  		return goerrors.New(strings.Replace(err.Error(), "; "+stopValidateMessage, "", -1))
   712  	}
   713  	return err
   714  }
   715  
   716  // WaitAndGetCompletedPodPhase waits up to a timeout until a pod enters a completed phase
   717  // and returns said phase (PodSucceeded or PodFailed qualify)
   718  func (c *Client) WaitAndGetCompletedPodPhase(namespace string, reader io.Reader, timeout time.Duration) (api.PodPhase, error) {
   719  	infos, err := c.Build(namespace, reader)
   720  	if err != nil {
   721  		return api.PodUnknown, err
   722  	}
   723  	info := infos[0]
   724  
   725  	kind := info.Mapping.GroupVersionKind.Kind
   726  	if kind != "Pod" {
   727  		return api.PodUnknown, fmt.Errorf("%s is not a Pod", info.Name)
   728  	}
   729  
   730  	if err := watchPodUntilComplete(timeout, info); err != nil {
   731  		return api.PodUnknown, err
   732  	}
   733  
   734  	if err := info.Get(); err != nil {
   735  		return api.PodUnknown, err
   736  	}
   737  	status := info.Object.(*api.Pod).Status.Phase
   738  
   739  	return status, nil
   740  }
   741  
   742  func watchPodUntilComplete(timeout time.Duration, info *resource.Info) error {
   743  	w, err := resource.NewHelper(info.Client, info.Mapping).WatchSingle(info.Namespace, info.Name, info.ResourceVersion)
   744  	if err != nil {
   745  		return err
   746  	}
   747  
   748  	log.Printf("Watching pod %s for completion with timeout of %v", info.Name, timeout)
   749  	_, err = watch.Until(timeout, w, func(e watch.Event) (bool, error) {
   750  		return conditions.PodCompleted(e)
   751  	})
   752  
   753  	return err
   754  }