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