github.com/gravitational/teleport/api@v0.0.0-20240507183017-3110591cbafc/types/kubernetes.go (about) 1 /* 2 Copyright 2021 Gravitational, Inc. 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 types 18 19 import ( 20 "fmt" 21 "regexp" 22 "slices" 23 "sort" 24 "time" 25 26 "github.com/gravitational/trace" 27 28 "github.com/gravitational/teleport/api/types/compare" 29 "github.com/gravitational/teleport/api/utils" 30 ) 31 32 var _ compare.IsEqual[KubeCluster] = (*KubernetesClusterV3)(nil) 33 34 // KubeCluster represents a kubernetes cluster. 35 type KubeCluster interface { 36 // ResourceWithLabels provides common resource methods. 37 ResourceWithLabels 38 // GetNamespace returns the kube cluster namespace. 39 GetNamespace() string 40 // GetStaticLabels returns the kube cluster static labels. 41 GetStaticLabels() map[string]string 42 // SetStaticLabels sets the kube cluster static labels. 43 SetStaticLabels(map[string]string) 44 // GetDynamicLabels returns the kube cluster dynamic labels. 45 GetDynamicLabels() map[string]CommandLabel 46 // SetDynamicLabels sets the kube cluster dynamic labels. 47 SetDynamicLabels(map[string]CommandLabel) 48 // GetKubeconfig returns the kubeconfig payload. 49 GetKubeconfig() []byte 50 // SetKubeconfig sets the kubeconfig. 51 SetKubeconfig([]byte) 52 // String returns string representation of the kube cluster. 53 String() string 54 // GetDescription returns the kube cluster description. 55 GetDescription() string 56 // GetAzureConfig gets the Azure config. 57 GetAzureConfig() KubeAzure 58 // SetAzureConfig sets the Azure config. 59 SetAzureConfig(KubeAzure) 60 // GetAWSConfig gets the AWS config. 61 GetAWSConfig() KubeAWS 62 // SetAWSConfig sets the AWS config. 63 SetAWSConfig(KubeAWS) 64 // GetGCPConfig gets the GCP config. 65 GetGCPConfig() KubeGCP 66 // SetGCPConfig sets the GCP config. 67 SetGCPConfig(KubeGCP) 68 // IsAzure indentifies if the KubeCluster contains Azure details. 69 IsAzure() bool 70 // IsAWS indentifies if the KubeCluster contains AWS details. 71 IsAWS() bool 72 // IsGCP indentifies if the KubeCluster contains GCP details. 73 IsGCP() bool 74 // IsKubeconfig identifies if the KubeCluster contains kubeconfig data. 75 IsKubeconfig() bool 76 // Copy returns a copy of this kube cluster resource. 77 Copy() *KubernetesClusterV3 78 // GetCloud gets the cloud this kube cluster is running on, or an empty string if it 79 // isn't running on a cloud provider. 80 GetCloud() string 81 } 82 83 // DiscoveredEKSCluster represents a server discovered by EKS discovery fetchers. 84 type DiscoveredEKSCluster interface { 85 // KubeCluster is base discovered cluster. 86 KubeCluster 87 // GetKubeCluster returns base cluster. 88 GetKubeCluster() KubeCluster 89 // GetIntegration returns integration name used when discovering this cluster. 90 GetIntegration() string 91 // GetKubeAppDiscovery returns setting showing if Kubernetes App Discovery show be enabled for the discovered cluster. 92 GetKubeAppDiscovery() bool 93 } 94 95 // NewKubernetesClusterV3FromLegacyCluster creates a new Kubernetes cluster resource 96 // from the legacy type. 97 func NewKubernetesClusterV3FromLegacyCluster(namespace string, cluster *KubernetesCluster) (*KubernetesClusterV3, error) { 98 k := &KubernetesClusterV3{ 99 Metadata: Metadata{ 100 Name: cluster.Name, 101 Namespace: namespace, 102 Labels: cluster.StaticLabels, 103 }, 104 Spec: KubernetesClusterSpecV3{ 105 DynamicLabels: cluster.DynamicLabels, 106 }, 107 } 108 109 if err := k.CheckAndSetDefaults(); err != nil { 110 return nil, trace.Wrap(err) 111 } 112 113 return k, nil 114 } 115 116 // NewKubernetesClusterV3WithoutSecrets creates a new copy of the provided cluster 117 // but without secrets/credentials. 118 func NewKubernetesClusterV3WithoutSecrets(cluster KubeCluster) (*KubernetesClusterV3, error) { 119 // Force a copy of the cluster to deep copy the Metadata fields. 120 copiedCluster := cluster.Copy() 121 clusterWithoutCreds, err := NewKubernetesClusterV3( 122 copiedCluster.Metadata, 123 KubernetesClusterSpecV3{ 124 DynamicLabels: copiedCluster.Spec.DynamicLabels, 125 }, 126 ) 127 return clusterWithoutCreds, trace.Wrap(err) 128 } 129 130 // NewKubernetesClusterV3 creates a new Kubernetes cluster resource. 131 func NewKubernetesClusterV3(meta Metadata, spec KubernetesClusterSpecV3) (*KubernetesClusterV3, error) { 132 k := &KubernetesClusterV3{ 133 Metadata: meta, 134 Spec: spec, 135 } 136 137 if err := k.CheckAndSetDefaults(); err != nil { 138 return nil, trace.Wrap(err) 139 } 140 141 return k, nil 142 } 143 144 // GetVersion returns the resource version. 145 func (k *KubernetesClusterV3) GetVersion() string { 146 return k.Version 147 } 148 149 // GetKind returns the resource kind. 150 func (k *KubernetesClusterV3) GetKind() string { 151 return k.Kind 152 } 153 154 // GetSubKind returns the app resource subkind. 155 func (k *KubernetesClusterV3) GetSubKind() string { 156 return k.SubKind 157 } 158 159 // SetSubKind sets the app resource subkind. 160 func (k *KubernetesClusterV3) SetSubKind(sk string) { 161 k.SubKind = sk 162 } 163 164 // GetResourceID returns the app resource ID. 165 func (k *KubernetesClusterV3) GetResourceID() int64 { 166 return k.Metadata.ID 167 } 168 169 // SetResourceID sets the resource ID. 170 func (k *KubernetesClusterV3) SetResourceID(id int64) { 171 k.Metadata.ID = id 172 } 173 174 // GetRevision returns the revision 175 func (k *KubernetesClusterV3) GetRevision() string { 176 return k.Metadata.GetRevision() 177 } 178 179 // SetRevision sets the revision 180 func (k *KubernetesClusterV3) SetRevision(rev string) { 181 k.Metadata.SetRevision(rev) 182 } 183 184 // GetMetadata returns the resource metadata. 185 func (k *KubernetesClusterV3) GetMetadata() Metadata { 186 return k.Metadata 187 } 188 189 // Origin returns the origin value of the resource. 190 func (k *KubernetesClusterV3) Origin() string { 191 return k.Metadata.Origin() 192 } 193 194 // SetOrigin sets the origin value of the resource. 195 func (k *KubernetesClusterV3) SetOrigin(origin string) { 196 k.Metadata.SetOrigin(origin) 197 } 198 199 // GetNamespace returns the kube resource namespace. 200 func (k *KubernetesClusterV3) GetNamespace() string { 201 return k.Metadata.Namespace 202 } 203 204 // SetExpiry sets the kube resource expiration time. 205 func (k *KubernetesClusterV3) SetExpiry(expiry time.Time) { 206 k.Metadata.SetExpiry(expiry) 207 } 208 209 // Expiry returns the kube resource expiration time. 210 func (k *KubernetesClusterV3) Expiry() time.Time { 211 return k.Metadata.Expiry() 212 } 213 214 // GetName returns the kube resource name. 215 func (k *KubernetesClusterV3) GetName() string { 216 return k.Metadata.Name 217 } 218 219 // SetName sets the resource name. 220 func (k *KubernetesClusterV3) SetName(name string) { 221 k.Metadata.Name = name 222 } 223 224 // GetLabel retrieves the label with the provided key. If not found 225 // value will be empty and ok will be false. 226 func (k *KubernetesClusterV3) GetLabel(key string) (value string, ok bool) { 227 if cmd, ok := k.Spec.DynamicLabels[key]; ok { 228 return cmd.Result, ok 229 } 230 231 v, ok := k.Metadata.Labels[key] 232 return v, ok 233 } 234 235 // GetStaticLabels returns the static labels. 236 func (k *KubernetesClusterV3) GetStaticLabels() map[string]string { 237 return k.Metadata.Labels 238 } 239 240 // SetStaticLabels sets the static labels. 241 func (k *KubernetesClusterV3) SetStaticLabels(sl map[string]string) { 242 k.Metadata.Labels = sl 243 } 244 245 // GetKubeconfig returns the kubeconfig payload. 246 func (k *KubernetesClusterV3) GetKubeconfig() []byte { 247 return k.Spec.Kubeconfig 248 } 249 250 // SetKubeconfig sets the kubeconfig. 251 func (k *KubernetesClusterV3) SetKubeconfig(cfg []byte) { 252 k.Spec.Kubeconfig = cfg 253 } 254 255 // GetDynamicLabels returns the dynamic labels. 256 func (k *KubernetesClusterV3) GetDynamicLabels() map[string]CommandLabel { 257 if k.Spec.DynamicLabels == nil { 258 return nil 259 } 260 return V2ToLabels(k.Spec.DynamicLabels) 261 } 262 263 // SetDynamicLabels sets the dynamic labels 264 func (k *KubernetesClusterV3) SetDynamicLabels(dl map[string]CommandLabel) { 265 k.Spec.DynamicLabels = LabelsToV2(dl) 266 } 267 268 // GetAllLabels returns the combined static and dynamic labels. 269 func (k *KubernetesClusterV3) GetAllLabels() map[string]string { 270 return CombineLabels(k.Metadata.Labels, k.Spec.DynamicLabels) 271 } 272 273 // GetDescription returns the description. 274 func (k *KubernetesClusterV3) GetDescription() string { 275 return k.Metadata.Description 276 } 277 278 // GetAzureConfig gets the Azure config. 279 func (k *KubernetesClusterV3) GetAzureConfig() KubeAzure { 280 return k.Spec.Azure 281 } 282 283 // SetAzureConfig sets the Azure config. 284 func (k *KubernetesClusterV3) SetAzureConfig(cfg KubeAzure) { 285 k.Spec.Azure = cfg 286 } 287 288 // GetAWSConfig gets the AWS config. 289 func (k *KubernetesClusterV3) GetAWSConfig() KubeAWS { 290 return k.Spec.AWS 291 } 292 293 // SetAWSConfig sets the AWS config. 294 func (k *KubernetesClusterV3) SetAWSConfig(cfg KubeAWS) { 295 k.Spec.AWS = cfg 296 } 297 298 // GetGCPConfig gets the GCP config. 299 func (k *KubernetesClusterV3) GetGCPConfig() KubeGCP { 300 return k.Spec.GCP 301 } 302 303 // SetGCPConfig sets the GCP config. 304 func (k *KubernetesClusterV3) SetGCPConfig(cfg KubeGCP) { 305 k.Spec.GCP = cfg 306 } 307 308 // IsAzure indentifies if the KubeCluster contains Azure details. 309 func (k *KubernetesClusterV3) IsAzure() bool { 310 return !protoKnownFieldsEqual(&k.Spec.Azure, &KubeAzure{}) 311 } 312 313 // IsAWS indentifies if the KubeCluster contains AWS details. 314 func (k *KubernetesClusterV3) IsAWS() bool { 315 return !protoKnownFieldsEqual(&k.Spec.AWS, &KubeAWS{}) 316 } 317 318 // IsGCP indentifies if the KubeCluster contains GCP details. 319 func (k *KubernetesClusterV3) IsGCP() bool { 320 return !protoKnownFieldsEqual(&k.Spec.GCP, &KubeGCP{}) 321 } 322 323 // GetCloud gets the cloud this kube cluster is running on, or an empty string if it 324 // isn't running on a cloud provider. 325 func (k *KubernetesClusterV3) GetCloud() string { 326 switch { 327 case k.IsAzure(): 328 return CloudAzure 329 case k.IsAWS(): 330 return CloudAWS 331 case k.IsGCP(): 332 return CloudGCP 333 default: 334 return "" 335 } 336 } 337 338 // IsKubeconfig identifies if the KubeCluster contains kubeconfig data. 339 func (k *KubernetesClusterV3) IsKubeconfig() bool { 340 return len(k.Spec.Kubeconfig) > 0 341 } 342 343 // String returns the string representation. 344 func (k *KubernetesClusterV3) String() string { 345 return fmt.Sprintf("KubernetesCluster(Name=%v, Labels=%v)", 346 k.GetName(), k.GetAllLabels()) 347 } 348 349 // Copy returns a copy of this resource. 350 func (k *KubernetesClusterV3) Copy() *KubernetesClusterV3 { 351 return utils.CloneProtoMsg(k) 352 } 353 354 // MatchSearch goes through select field values and tries to 355 // match against the list of search values. 356 func (k *KubernetesClusterV3) MatchSearch(values []string) bool { 357 fieldVals := append(utils.MapToStrings(k.GetAllLabels()), k.GetName()) 358 return MatchSearch(fieldVals, values, nil) 359 } 360 361 // setStaticFields sets static resource header and metadata fields. 362 func (k *KubernetesClusterV3) setStaticFields() { 363 k.Kind = KindKubernetesCluster 364 k.Version = V3 365 } 366 367 // validKubeClusterName filters the allowed characters in kubernetes cluster 368 // names. We need this because cluster names are used for cert filenames on the 369 // client side, in the ~/.tsh directory. Restricting characters helps with 370 // sneaky cluster names being used for client directory traversal and exploits. 371 var validKubeClusterName = regexp.MustCompile(`^[a-zA-Z0-9._-]+$`) 372 373 // ValidateKubeClusterName returns an error if a given string is not a valid 374 // KubeCluster name. 375 func ValidateKubeClusterName(name string) error { 376 return ValidateResourceName(validKubeClusterName, name) 377 } 378 379 // CheckAndSetDefaults checks and sets default values for any missing fields. 380 func (k *KubernetesClusterV3) CheckAndSetDefaults() error { 381 k.setStaticFields() 382 if err := k.Metadata.CheckAndSetDefaults(); err != nil { 383 return trace.Wrap(err) 384 } 385 for key := range k.Spec.DynamicLabels { 386 if !IsValidLabelKey(key) { 387 return trace.BadParameter("kubernetes cluster %q invalid label key: %q", k.GetName(), key) 388 } 389 } 390 391 if err := ValidateKubeClusterName(k.Metadata.Name); err != nil { 392 return trace.Wrap(err, "invalid kubernetes cluster name") 393 } 394 395 if err := k.Spec.Azure.CheckAndSetDefaults(); err != nil && k.IsAzure() { 396 return trace.Wrap(err) 397 } 398 399 if err := k.Spec.AWS.CheckAndSetDefaults(); err != nil && k.IsAWS() { 400 return trace.Wrap(err) 401 } 402 403 if err := k.Spec.GCP.CheckAndSetDefaults(); err != nil && k.IsGCP() { 404 return trace.Wrap(err) 405 } 406 407 return nil 408 } 409 410 // IsEqual determines if two user resources are equivalent to one another. 411 func (k *KubernetesClusterV3) IsEqual(i KubeCluster) bool { 412 if other, ok := i.(*KubernetesClusterV3); ok { 413 return deriveTeleportEqualKubernetesClusterV3(k, other) 414 } 415 return false 416 } 417 418 func (k KubeAzure) CheckAndSetDefaults() error { 419 if len(k.ResourceGroup) == 0 { 420 return trace.BadParameter("invalid Azure ResourceGroup") 421 } 422 423 if len(k.ResourceName) == 0 { 424 return trace.BadParameter("invalid Azure ResourceName") 425 } 426 427 if len(k.SubscriptionID) == 0 { 428 return trace.BadParameter("invalid Azure SubscriptionID") 429 } 430 431 return nil 432 } 433 434 func (k KubeAWS) CheckAndSetDefaults() error { 435 if len(k.Region) == 0 { 436 return trace.BadParameter("invalid AWS Region") 437 } 438 439 if len(k.Name) == 0 { 440 return trace.BadParameter("invalid AWS Name") 441 } 442 443 if len(k.AccountID) == 0 { 444 return trace.BadParameter("invalid AWS AccountID") 445 } 446 447 return nil 448 } 449 450 func (k KubeGCP) CheckAndSetDefaults() error { 451 if len(k.Location) == 0 { 452 return trace.BadParameter("invalid GCP Location") 453 } 454 455 if len(k.ProjectID) == 0 { 456 return trace.BadParameter("invalid GCP ProjectID") 457 } 458 459 if len(k.Name) == 0 { 460 return trace.BadParameter("invalid GCP Name") 461 } 462 463 return nil 464 } 465 466 // KubeClusters represents a list of kube clusters. 467 type KubeClusters []KubeCluster 468 469 // Find returns kube cluster with the specified name or nil. 470 func (s KubeClusters) Find(name string) KubeCluster { 471 for _, cluster := range s { 472 if cluster.GetName() == name { 473 return cluster 474 } 475 } 476 return nil 477 } 478 479 // ToMap returns these kubernetes clusters as a map keyed by cluster name. 480 func (s KubeClusters) ToMap() map[string]KubeCluster { 481 m := make(map[string]KubeCluster) 482 for _, kubeCluster := range s { 483 m[kubeCluster.GetName()] = kubeCluster 484 } 485 return m 486 } 487 488 // Len returns the slice length. 489 func (s KubeClusters) Len() int { return len(s) } 490 491 // Less compares kube clusters by name. 492 func (s KubeClusters) Less(i, j int) bool { 493 return s[i].GetName() < s[j].GetName() 494 } 495 496 // Swap swaps two kube clusters. 497 func (s KubeClusters) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 498 499 // SortByCustom custom sorts by given sort criteria. 500 func (s KubeClusters) SortByCustom(sortBy SortBy) error { 501 if sortBy.Field == "" { 502 return nil 503 } 504 505 isDesc := sortBy.IsDesc 506 switch sortBy.Field { 507 case ResourceMetadataName: 508 sort.SliceStable(s, func(i, j int) bool { 509 return stringCompare(s[i].GetName(), s[j].GetName(), isDesc) 510 }) 511 default: 512 return trace.NotImplemented("sorting by field %q for resource %q is not supported", sortBy.Field, KindKubernetesCluster) 513 } 514 515 return nil 516 } 517 518 // AsResources returns as type resources with labels. 519 func (s KubeClusters) AsResources() ResourcesWithLabels { 520 resources := make(ResourcesWithLabels, 0, len(s)) 521 for _, cluster := range s { 522 resources = append(resources, ResourceWithLabels(cluster)) 523 } 524 return resources 525 } 526 527 // GetFieldVals returns list of select field values. 528 func (s KubeClusters) GetFieldVals(field string) ([]string, error) { 529 vals := make([]string, 0, len(s)) 530 switch field { 531 case ResourceMetadataName: 532 for _, server := range s { 533 vals = append(vals, server.GetName()) 534 } 535 default: 536 return nil, trace.NotImplemented("getting field %q for resource %q is not supported", field, KindKubernetesCluster) 537 } 538 539 return vals, nil 540 } 541 542 // DeduplicateKubeClusters deduplicates kube clusters by name. 543 func DeduplicateKubeClusters(kubeclusters []KubeCluster) []KubeCluster { 544 seen := make(map[string]struct{}) 545 result := make([]KubeCluster, 0, len(kubeclusters)) 546 547 for _, cluster := range kubeclusters { 548 if _, ok := seen[cluster.GetName()]; ok { 549 continue 550 } 551 seen[cluster.GetName()] = struct{}{} 552 result = append(result, cluster) 553 } 554 555 return result 556 } 557 558 var _ ResourceWithLabels = (*KubernetesResourceV1)(nil) 559 560 // NewKubernetesPodV1 creates a new kubernetes resource with kind "pod". 561 func NewKubernetesPodV1(meta Metadata, spec KubernetesResourceSpecV1) (*KubernetesResourceV1, error) { 562 pod := &KubernetesResourceV1{ 563 Kind: KindKubePod, 564 Metadata: meta, 565 Spec: spec, 566 } 567 568 if err := pod.CheckAndSetDefaults(); err != nil { 569 return nil, trace.Wrap(err) 570 } 571 return pod, nil 572 } 573 574 // NewKubernetesResourceV1 creates a new kubernetes resource . 575 func NewKubernetesResourceV1(kind string, meta Metadata, spec KubernetesResourceSpecV1) (*KubernetesResourceV1, error) { 576 resource := &KubernetesResourceV1{ 577 Kind: kind, 578 Metadata: meta, 579 Spec: spec, 580 } 581 if err := resource.CheckAndSetDefaults(); err != nil { 582 return nil, trace.Wrap(err) 583 } 584 return resource, nil 585 } 586 587 // GetKind returns resource kind. 588 func (k *KubernetesResourceV1) GetKind() string { 589 return k.Kind 590 } 591 592 // GetSubKind returns resource subkind. 593 func (k *KubernetesResourceV1) GetSubKind() string { 594 return k.SubKind 595 } 596 597 // GetVersion returns resource version. 598 func (k *KubernetesResourceV1) GetVersion() string { 599 return k.Version 600 } 601 602 // GetMetadata returns object metadata. 603 func (k *KubernetesResourceV1) GetMetadata() Metadata { 604 return k.Metadata 605 } 606 607 // SetSubKind sets resource subkind. 608 func (k *KubernetesResourceV1) SetSubKind(subKind string) { 609 k.SubKind = subKind 610 } 611 612 // GetName returns the name of the resource. 613 func (k *KubernetesResourceV1) GetName() string { 614 return k.Metadata.GetName() 615 } 616 617 // SetName sets the name of the resource. 618 func (k *KubernetesResourceV1) SetName(name string) { 619 k.Metadata.SetName(name) 620 } 621 622 // Expiry returns object expiry setting. 623 func (k *KubernetesResourceV1) Expiry() time.Time { 624 return k.Metadata.Expiry() 625 } 626 627 // SetExpiry sets object expiry. 628 func (k *KubernetesResourceV1) SetExpiry(expire time.Time) { 629 k.Metadata.SetExpiry(expire) 630 } 631 632 // GetResourceID returns resource ID. 633 func (k *KubernetesResourceV1) GetResourceID() int64 { 634 return k.Metadata.ID 635 } 636 637 // SetResourceID sets resource ID. 638 func (k *KubernetesResourceV1) SetResourceID(id int64) { 639 k.Metadata.ID = id 640 } 641 642 // GetRevision returns the revision 643 func (k *KubernetesResourceV1) GetRevision() string { 644 return k.Metadata.GetRevision() 645 } 646 647 // SetRevision sets the revision 648 func (k *KubernetesResourceV1) SetRevision(rev string) { 649 k.Metadata.SetRevision(rev) 650 } 651 652 // CheckAndSetDefaults validates the Resource and sets any empty fields to 653 // default values. 654 func (k *KubernetesResourceV1) CheckAndSetDefaults() error { 655 k.setStaticFields() 656 if !slices.Contains(KubernetesResourcesKinds, k.Kind) { 657 return trace.BadParameter("invalid kind %q defined; allowed values: %v", k.Kind, KubernetesResourcesKinds) 658 } 659 if err := k.Metadata.CheckAndSetDefaults(); err != nil { 660 return trace.Wrap(err) 661 } 662 663 // Unless the resource is cluster-wide, it must have a namespace. 664 if len(k.Spec.Namespace) == 0 && !slices.Contains(KubernetesClusterWideResourceKinds, k.Kind) { 665 return trace.BadParameter("missing kubernetes namespace") 666 } 667 668 return nil 669 } 670 671 // setStaticFields sets static resource header and metadata fields. 672 func (k *KubernetesResourceV1) setStaticFields() { 673 k.Version = V1 674 } 675 676 // Origin returns the origin value of the resource. 677 func (k *KubernetesResourceV1) Origin() string { 678 return k.Metadata.Origin() 679 } 680 681 // SetOrigin sets the origin value of the resource. 682 func (k *KubernetesResourceV1) SetOrigin(origin string) { 683 k.Metadata.SetOrigin(origin) 684 } 685 686 // GetLabel retrieves the label with the provided key. If not found 687 // value will be empty and ok will be false. 688 func (k *KubernetesResourceV1) GetLabel(key string) (value string, ok bool) { 689 v, ok := k.Metadata.Labels[key] 690 return v, ok 691 } 692 693 // GetAllLabels returns all resource's labels. 694 func (k *KubernetesResourceV1) GetAllLabels() map[string]string { 695 return k.Metadata.Labels 696 } 697 698 // GetStaticLabels returns the resource's static labels. 699 func (k *KubernetesResourceV1) GetStaticLabels() map[string]string { 700 return k.Metadata.Labels 701 } 702 703 // SetStaticLabels sets the resource's static labels. 704 func (k *KubernetesResourceV1) SetStaticLabels(sl map[string]string) { 705 k.Metadata.Labels = sl 706 } 707 708 // MatchSearch goes through select field values of a resource 709 // and tries to match against the list of search values. 710 func (k *KubernetesResourceV1) MatchSearch(searchValues []string) bool { 711 fieldVals := append(utils.MapToStrings(k.GetAllLabels()), k.GetName(), k.Spec.Namespace) 712 return MatchSearch(fieldVals, searchValues, nil) 713 } 714 715 // KubeResources represents a list of Kubernetes resources. 716 type KubeResources []*KubernetesResourceV1 717 718 // Find returns Kubernetes resource with the specified name or nil if the resource 719 // was not found. 720 func (k KubeResources) Find(name string) *KubernetesResourceV1 { 721 for _, cluster := range k { 722 if cluster.GetName() == name { 723 return cluster 724 } 725 } 726 return nil 727 } 728 729 // ToMap returns these kubernetes resources as a map keyed by resource name. 730 func (k KubeResources) ToMap() map[string]*KubernetesResourceV1 { 731 m := make(map[string]*KubernetesResourceV1) 732 for _, kubeCluster := range k { 733 m[kubeCluster.GetName()] = kubeCluster 734 } 735 return m 736 } 737 738 // Len returns the slice length. 739 func (k KubeResources) Len() int { return len(k) } 740 741 // Less compares Kubernetes resources by name. 742 func (k KubeResources) Less(i, j int) bool { 743 return k[i].GetName() < k[j].GetName() 744 } 745 746 // Swap swaps two Kubernetes resources. 747 func (k KubeResources) Swap(i, j int) { k[i], k[j] = k[j], k[i] } 748 749 // SortByCustom custom sorts by given sort criteria. 750 func (k KubeResources) SortByCustom(sortBy SortBy) error { 751 if sortBy.Field == "" { 752 return nil 753 } 754 755 isDesc := sortBy.IsDesc 756 switch sortBy.Field { 757 case ResourceMetadataName: 758 sort.SliceStable(k, func(i, j int) bool { 759 return stringCompare(k[i].GetName(), k[j].GetName(), isDesc) 760 }) 761 default: 762 return trace.NotImplemented("sorting by field %q for kubernetes resources is not supported", sortBy.Field) 763 } 764 765 return nil 766 } 767 768 // AsResources returns as type resources with labels. 769 func (k KubeResources) AsResources() ResourcesWithLabels { 770 resources := make(ResourcesWithLabels, 0, len(k)) 771 for _, resource := range k { 772 resources = append(resources, ResourceWithLabels(resource)) 773 } 774 return resources 775 }