github.com/gravitational/teleport/api@v0.0.0-20240507183017-3110591cbafc/types/server.go (about) 1 /* 2 Copyright 2020 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 "net" 22 "sort" 23 "strings" 24 "time" 25 26 "github.com/google/uuid" 27 "github.com/gravitational/trace" 28 29 "github.com/gravitational/teleport/api/utils" 30 "github.com/gravitational/teleport/api/utils/aws" 31 ) 32 33 // Server represents a Node, Proxy or Auth server in a Teleport cluster 34 type Server interface { 35 // ResourceWithLabels provides common resource headers 36 ResourceWithLabels 37 // GetTeleportVersion returns the teleport version the server is running on 38 GetTeleportVersion() string 39 // GetAddr return server address 40 GetAddr() string 41 // GetHostname returns server hostname 42 GetHostname() string 43 // GetNamespace returns server namespace 44 GetNamespace() string 45 // GetLabels returns server's static label key pairs 46 GetLabels() map[string]string 47 // GetCmdLabels gets command labels 48 GetCmdLabels() map[string]CommandLabel 49 // SetCmdLabels sets command labels. 50 SetCmdLabels(cmdLabels map[string]CommandLabel) 51 // GetPublicAddr returns a public address where this server can be reached. 52 GetPublicAddr() string 53 // GetPublicAddrs returns a list of public addresses where this server can be reached. 54 GetPublicAddrs() []string 55 // GetRotation gets the state of certificate authority rotation. 56 GetRotation() Rotation 57 // SetRotation sets the state of certificate authority rotation. 58 SetRotation(Rotation) 59 // GetUseTunnel gets if a reverse tunnel should be used to connect to this node. 60 GetUseTunnel() bool 61 // SetUseTunnel sets if a reverse tunnel should be used to connect to this node. 62 SetUseTunnel(bool) 63 // String returns string representation of the server 64 String() string 65 // SetAddr sets server address 66 SetAddr(addr string) 67 // SetPublicAddrs sets the public addresses where this server can be reached. 68 SetPublicAddrs([]string) 69 // SetNamespace sets server namespace 70 SetNamespace(namespace string) 71 // GetPeerAddr returns the peer address of the server. 72 GetPeerAddr() string 73 // SetPeerAddr sets the peer address of the server. 74 SetPeerAddr(string) 75 // ProxiedService provides common methods for a proxied service. 76 ProxiedService 77 78 // DeepCopy creates a clone of this server value 79 DeepCopy() Server 80 81 // CloneResource is used to return a clone of the Server and match the CloneAny interface 82 // This is helpful when interfacing with multiple types at the same time in unified resources 83 CloneResource() ResourceWithLabels 84 85 // GetCloudMetadata gets the cloud metadata for the server. 86 GetCloudMetadata() *CloudMetadata 87 // GetAWSInfo returns the AWSInfo for the server. 88 GetAWSInfo() *AWSInfo 89 // SetCloudMetadata sets the server's cloud metadata. 90 SetCloudMetadata(meta *CloudMetadata) 91 92 // IsOpenSSHNode returns whether the connection to this Server must use OpenSSH. 93 // This returns true for SubKindOpenSSHNode and SubKindOpenSSHEICENode. 94 IsOpenSSHNode() bool 95 96 // IsEICE returns whether the Node is an EICE instance. 97 // Must be `openssh-ec2-ice` subkind and have the AccountID and InstanceID information (AWS Metadata or Labels). 98 IsEICE() bool 99 100 // GetAWSInstanceID returns the AWS Instance ID if this node comes from an EC2 instance. 101 GetAWSInstanceID() string 102 // GetAWSAccountID returns the AWS Account ID if this node comes from an EC2 instance. 103 GetAWSAccountID() string 104 } 105 106 // NewServer creates an instance of Server. 107 func NewServer(name, kind string, spec ServerSpecV2) (Server, error) { 108 return NewServerWithLabels(name, kind, spec, map[string]string{}) 109 } 110 111 // NewServerWithLabels is a convenience method to create 112 // ServerV2 with a specific map of labels. 113 func NewServerWithLabels(name, kind string, spec ServerSpecV2, labels map[string]string) (Server, error) { 114 server := &ServerV2{ 115 Kind: kind, 116 Metadata: Metadata{ 117 Name: name, 118 Labels: labels, 119 }, 120 Spec: spec, 121 } 122 if err := server.CheckAndSetDefaults(); err != nil { 123 return nil, trace.Wrap(err) 124 } 125 return server, nil 126 } 127 128 // NewNode is a convenience method to create a Server of Kind Node. 129 func NewNode(name, subKind string, spec ServerSpecV2, labels map[string]string) (Server, error) { 130 server := &ServerV2{ 131 Kind: KindNode, 132 SubKind: subKind, 133 Metadata: Metadata{ 134 Name: name, 135 Labels: labels, 136 }, 137 Spec: spec, 138 } 139 if err := server.CheckAndSetDefaults(); err != nil { 140 return nil, trace.Wrap(err) 141 } 142 return server, nil 143 } 144 145 // NewNode is a convenience method to create an EICE Node. 146 func NewEICENode(spec ServerSpecV2, labels map[string]string) (Server, error) { 147 server := &ServerV2{ 148 Kind: KindNode, 149 SubKind: SubKindOpenSSHEICENode, 150 Metadata: Metadata{ 151 Labels: labels, 152 }, 153 Spec: spec, 154 } 155 if err := server.CheckAndSetDefaults(); err != nil { 156 return nil, trace.Wrap(err) 157 } 158 return server, nil 159 } 160 161 // GetVersion returns resource version 162 func (s *ServerV2) GetVersion() string { 163 return s.Version 164 } 165 166 // GetTeleportVersion returns the teleport version the server is running on 167 func (s *ServerV2) GetTeleportVersion() string { 168 return s.Spec.Version 169 } 170 171 // GetKind returns resource kind 172 func (s *ServerV2) GetKind() string { 173 return s.Kind 174 } 175 176 // GetSubKind returns resource sub kind 177 func (s *ServerV2) GetSubKind() string { 178 // if the server is a node subkind isn't set, this is a teleport node. 179 if s.Kind == KindNode && s.SubKind == "" { 180 return SubKindTeleportNode 181 } 182 183 return s.SubKind 184 } 185 186 // SetSubKind sets resource subkind 187 func (s *ServerV2) SetSubKind(sk string) { 188 s.SubKind = sk 189 } 190 191 // GetResourceID returns resource ID 192 func (s *ServerV2) GetResourceID() int64 { 193 return s.Metadata.ID 194 } 195 196 // SetResourceID sets resource ID 197 func (s *ServerV2) SetResourceID(id int64) { 198 s.Metadata.ID = id 199 } 200 201 // GetRevision returns the revision 202 func (s *ServerV2) GetRevision() string { 203 return s.Metadata.GetRevision() 204 } 205 206 // SetRevision sets the revision 207 func (s *ServerV2) SetRevision(rev string) { 208 s.Metadata.SetRevision(rev) 209 } 210 211 // GetMetadata returns metadata 212 func (s *ServerV2) GetMetadata() Metadata { 213 return s.Metadata 214 } 215 216 // SetNamespace sets server namespace 217 func (s *ServerV2) SetNamespace(namespace string) { 218 s.Metadata.Namespace = namespace 219 } 220 221 // SetAddr sets server address 222 func (s *ServerV2) SetAddr(addr string) { 223 s.Spec.Addr = addr 224 } 225 226 // SetExpiry sets expiry time for the object 227 func (s *ServerV2) SetExpiry(expires time.Time) { 228 s.Metadata.SetExpiry(expires) 229 } 230 231 // Expiry returns object expiry setting 232 func (s *ServerV2) Expiry() time.Time { 233 return s.Metadata.Expiry() 234 } 235 236 // SetPublicAddrs sets the public proxy addresses where this server can be reached. 237 func (s *ServerV2) SetPublicAddrs(addrs []string) { 238 s.Spec.PublicAddrs = addrs 239 } 240 241 // GetName returns server name 242 func (s *ServerV2) GetName() string { 243 return s.Metadata.Name 244 } 245 246 // SetName sets the name of the TrustedCluster. 247 func (s *ServerV2) SetName(e string) { 248 s.Metadata.Name = e 249 } 250 251 // GetAddr return server address 252 func (s *ServerV2) GetAddr() string { 253 return s.Spec.Addr 254 } 255 256 // GetPublicAddr returns a public address where this server can be reached. 257 func (s *ServerV2) GetPublicAddr() string { 258 addrs := s.GetPublicAddrs() 259 if len(addrs) != 0 { 260 return addrs[0] 261 } 262 return "" 263 } 264 265 // GetPublicAddrs returns a list of public addresses where this server can be reached. 266 func (s *ServerV2) GetPublicAddrs() []string { 267 return s.Spec.PublicAddrs 268 } 269 270 // GetRotation gets the state of certificate authority rotation. 271 func (s *ServerV2) GetRotation() Rotation { 272 return s.Spec.Rotation 273 } 274 275 // SetRotation sets the state of certificate authority rotation. 276 func (s *ServerV2) SetRotation(r Rotation) { 277 s.Spec.Rotation = r 278 } 279 280 // GetUseTunnel gets if a reverse tunnel should be used to connect to this node. 281 func (s *ServerV2) GetUseTunnel() bool { 282 return s.Spec.UseTunnel 283 } 284 285 // SetUseTunnel sets if a reverse tunnel should be used to connect to this node. 286 func (s *ServerV2) SetUseTunnel(useTunnel bool) { 287 s.Spec.UseTunnel = useTunnel 288 } 289 290 // GetHostname returns server hostname 291 func (s *ServerV2) GetHostname() string { 292 return s.Spec.Hostname 293 } 294 295 // GetLabel retrieves the label with the provided key. If not found 296 // value will be empty and ok will be false. 297 func (s *ServerV2) GetLabel(key string) (value string, ok bool) { 298 if cmd, ok := s.Spec.CmdLabels[key]; ok { 299 return cmd.Result, ok 300 } 301 302 v, ok := s.Metadata.Labels[key] 303 return v, ok 304 } 305 306 // GetLabels returns server's static label key pairs. 307 // GetLabels and GetStaticLabels are the same, and that is intentional. GetLabels 308 // exists to preserve backwards compatibility, while GetStaticLabels exists to 309 // implement ResourcesWithLabels. 310 func (s *ServerV2) GetLabels() map[string]string { 311 return s.Metadata.Labels 312 } 313 314 // GetStaticLabels returns the server static labels. 315 // GetLabels and GetStaticLabels are the same, and that is intentional. GetLabels 316 // exists to preserve backwards compatibility, while GetStaticLabels exists to 317 // implement ResourcesWithLabels. 318 func (s *ServerV2) GetStaticLabels() map[string]string { 319 return s.Metadata.Labels 320 } 321 322 // SetStaticLabels sets the server static labels. 323 func (s *ServerV2) SetStaticLabels(sl map[string]string) { 324 s.Metadata.Labels = sl 325 } 326 327 // GetCmdLabels returns command labels 328 func (s *ServerV2) GetCmdLabels() map[string]CommandLabel { 329 if s.Spec.CmdLabels == nil { 330 return nil 331 } 332 return V2ToLabels(s.Spec.CmdLabels) 333 } 334 335 // Origin returns the origin value of the resource. 336 func (s *ServerV2) Origin() string { 337 return s.Metadata.Origin() 338 } 339 340 // SetOrigin sets the origin value of the resource. 341 func (s *ServerV2) SetOrigin(origin string) { 342 s.Metadata.SetOrigin(origin) 343 } 344 345 // SetCmdLabels sets dynamic labels. 346 func (s *ServerV2) SetCmdLabels(cmdLabels map[string]CommandLabel) { 347 s.Spec.CmdLabels = LabelsToV2(cmdLabels) 348 } 349 350 func (s *ServerV2) String() string { 351 return fmt.Sprintf("Server(name=%v, namespace=%v, addr=%v, labels=%v)", s.Metadata.Name, s.Metadata.Namespace, s.Spec.Addr, s.Metadata.Labels) 352 } 353 354 // GetNamespace returns server namespace 355 func (s *ServerV2) GetNamespace() string { 356 return ProcessNamespace(s.Metadata.Namespace) 357 } 358 359 // GetProxyID returns the proxy id this server is connected to. 360 func (s *ServerV2) GetProxyIDs() []string { 361 return s.Spec.ProxyIDs 362 } 363 364 // SetProxyID sets the proxy ids this server is connected to. 365 func (s *ServerV2) SetProxyIDs(proxyIDs []string) { 366 s.Spec.ProxyIDs = proxyIDs 367 } 368 369 // GetAllLabels returns the full key:value map of both static labels and 370 // "command labels" 371 func (s *ServerV2) GetAllLabels() map[string]string { 372 // server labels (static and dynamic) 373 labels := CombineLabels(s.Metadata.Labels, s.Spec.CmdLabels) 374 return labels 375 } 376 377 // CombineLabels combines the passed in static and dynamic labels. 378 func CombineLabels(static map[string]string, dynamic map[string]CommandLabelV2) map[string]string { 379 if len(dynamic) == 0 { 380 return static 381 } 382 383 lmap := make(map[string]string, len(static)+len(dynamic)) 384 for key, value := range static { 385 lmap[key] = value 386 } 387 for key, cmd := range dynamic { 388 lmap[key] = cmd.Result 389 } 390 return lmap 391 } 392 393 // GetPeerAddr returns the peer address of the server. 394 func (s *ServerV2) GetPeerAddr() string { 395 return s.Spec.PeerAddr 396 } 397 398 // SetPeerAddr sets the peer address of the server. 399 func (s *ServerV2) SetPeerAddr(addr string) { 400 s.Spec.PeerAddr = addr 401 } 402 403 // setStaticFields sets static resource header and metadata fields. 404 func (s *ServerV2) setStaticFields() { 405 s.Version = V2 406 } 407 408 // IsOpenSSHNode returns whether the connection to this Server must use OpenSSH. 409 // This returns true for SubKindOpenSSHNode and SubKindOpenSSHEICENode. 410 func (s *ServerV2) IsOpenSSHNode() bool { 411 return IsOpenSSHNodeSubKind(s.SubKind) 412 } 413 414 // IsOpenSSHNodeSubKind returns whether the Node SubKind is from a server which accepts connections over the 415 // OpenSSH daemon (instead of a Teleport Node). 416 func IsOpenSSHNodeSubKind(subkind string) bool { 417 return subkind == SubKindOpenSSHNode || subkind == SubKindOpenSSHEICENode 418 } 419 420 // GetAWSAccountID returns the AWS Account ID if this node comes from an EC2 instance. 421 func (s *ServerV2) GetAWSAccountID() string { 422 awsAccountID, _ := s.GetLabel(AWSAccountIDLabel) 423 424 awsMetadata := s.GetAWSInfo() 425 if awsMetadata != nil && awsMetadata.AccountID != "" { 426 awsAccountID = awsMetadata.AccountID 427 } 428 return awsAccountID 429 } 430 431 // GetAWSInstanceID returns the AWS Instance ID if this node comes from an EC2 instance. 432 func (s *ServerV2) GetAWSInstanceID() string { 433 awsInstanceID, _ := s.GetLabel(AWSInstanceIDLabel) 434 435 awsMetadata := s.GetAWSInfo() 436 if awsMetadata != nil && awsMetadata.InstanceID != "" { 437 awsInstanceID = awsMetadata.InstanceID 438 } 439 return awsInstanceID 440 } 441 442 // IsEICE returns whether the Node is an EICE instance. 443 // Must be `openssh-ec2-ice` subkind and have the AccountID and InstanceID information (AWS Metadata or Labels). 444 func (s *ServerV2) IsEICE() bool { 445 if s.SubKind != SubKindOpenSSHEICENode { 446 return false 447 } 448 449 return s.GetAWSAccountID() != "" && s.GetAWSInstanceID() != "" 450 } 451 452 // openSSHNodeCheckAndSetDefaults are common validations for OpenSSH nodes. 453 // They include SubKindOpenSSHNode and SubKindOpenSSHEICENode. 454 func (s *ServerV2) openSSHNodeCheckAndSetDefaults() error { 455 if s.Spec.Addr == "" { 456 return trace.BadParameter("addr must be set when server SubKind is %q", s.GetSubKind()) 457 } 458 if len(s.GetPublicAddrs()) != 0 { 459 return trace.BadParameter("publicAddrs must not be set when server SubKind is %q", s.GetSubKind()) 460 } 461 if s.Spec.Hostname == "" { 462 return trace.BadParameter("hostname must be set when server SubKind is %q", s.GetSubKind()) 463 } 464 465 _, _, err := net.SplitHostPort(s.Spec.Addr) 466 if err != nil { 467 return trace.BadParameter("invalid Addr %q: %v", s.Spec.Addr, err) 468 } 469 return nil 470 } 471 472 // openSSHEC2InstanceConnectEndpointNodeCheckAndSetDefaults are validations for SubKindOpenSSHEICENode. 473 func (s *ServerV2) openSSHEC2InstanceConnectEndpointNodeCheckAndSetDefaults() error { 474 if err := s.openSSHNodeCheckAndSetDefaults(); err != nil { 475 return trace.Wrap(err) 476 } 477 478 // AWS fields are required for SubKindOpenSSHEICENode. 479 switch { 480 case s.Spec.CloudMetadata == nil || s.Spec.CloudMetadata.AWS == nil: 481 return trace.BadParameter("missing AWS CloudMetadata (required for %q SubKind)", s.SubKind) 482 483 case s.Spec.CloudMetadata.AWS.AccountID == "": 484 return trace.BadParameter("missing AWS Account ID (required for %q SubKind)", s.SubKind) 485 486 case s.Spec.CloudMetadata.AWS.Region == "": 487 return trace.BadParameter("missing AWS Region (required for %q SubKind)", s.SubKind) 488 489 case s.Spec.CloudMetadata.AWS.Integration == "": 490 return trace.BadParameter("missing AWS OIDC Integration (required for %q SubKind)", s.SubKind) 491 492 case s.Spec.CloudMetadata.AWS.InstanceID == "": 493 return trace.BadParameter("missing AWS InstanceID (required for %q SubKind)", s.SubKind) 494 495 case s.Spec.CloudMetadata.AWS.VPCID == "": 496 return trace.BadParameter("missing AWS VPC ID (required for %q SubKind)", s.SubKind) 497 498 case s.Spec.CloudMetadata.AWS.SubnetID == "": 499 return trace.BadParameter("missing AWS Subnet ID (required for %q SubKind)", s.SubKind) 500 } 501 502 return nil 503 } 504 505 // serverNameForEICE returns the deterministic Server's name for an EICE instance. 506 // This name must comply with the expected format for EC2 Nodes as defined here: api/utils/aws.IsEC2NodeID 507 // Returns an error if AccountID or InstanceID is not present. 508 func serverNameForEICE(s *ServerV2) (string, error) { 509 awsAccountID := s.GetAWSAccountID() 510 awsInstanceID := s.GetAWSInstanceID() 511 512 if awsAccountID != "" && awsInstanceID != "" { 513 eiceNodeName := fmt.Sprintf("%s-%s", awsAccountID, awsInstanceID) 514 if !aws.IsEC2NodeID(eiceNodeName) { 515 return "", trace.BadParameter("invalid account %q or instance id %q", awsAccountID, awsInstanceID) 516 } 517 return eiceNodeName, nil 518 } 519 520 return "", trace.BadParameter("missing account id or instance id in %s node", SubKindOpenSSHEICENode) 521 } 522 523 // CheckAndSetDefaults checks and set default values for any missing fields. 524 func (s *ServerV2) CheckAndSetDefaults() error { 525 // TODO(awly): default s.Metadata.Expiry if not set (use 526 // defaults.ServerAnnounceTTL). 527 s.setStaticFields() 528 529 if s.Metadata.Name == "" { 530 switch s.SubKind { 531 case SubKindOpenSSHEICENode: 532 // For EICE nodes, use a deterministic name. 533 eiceNodeName, err := serverNameForEICE(s) 534 if err != nil { 535 return trace.Wrap(err) 536 } 537 s.Metadata.Name = eiceNodeName 538 case SubKindOpenSSHNode: 539 // if the server is a registered OpenSSH node, allow the name to be 540 // randomly generated 541 s.Metadata.Name = uuid.NewString() 542 } 543 } 544 545 if err := s.Metadata.CheckAndSetDefaults(); err != nil { 546 return trace.Wrap(err) 547 } 548 549 if s.Kind == "" { 550 return trace.BadParameter("server Kind is empty") 551 } 552 if s.Kind != KindNode && s.SubKind != "" { 553 return trace.BadParameter(`server SubKind must only be set when Kind is "node"`) 554 } 555 556 switch s.SubKind { 557 case "", SubKindTeleportNode: 558 // allow but do nothing 559 case SubKindOpenSSHNode: 560 if err := s.openSSHNodeCheckAndSetDefaults(); err != nil { 561 return trace.Wrap(err) 562 } 563 564 case SubKindOpenSSHEICENode: 565 if err := s.openSSHEC2InstanceConnectEndpointNodeCheckAndSetDefaults(); err != nil { 566 return trace.Wrap(err) 567 } 568 569 default: 570 return trace.BadParameter("invalid SubKind %q", s.SubKind) 571 } 572 573 for key := range s.Spec.CmdLabels { 574 if !IsValidLabelKey(key) { 575 return trace.BadParameter("invalid label key: %q", key) 576 } 577 } 578 579 return nil 580 } 581 582 // MatchSearch goes through select field values and tries to 583 // match against the list of search values. 584 func (s *ServerV2) MatchSearch(values []string) bool { 585 if s.GetKind() != KindNode { 586 return false 587 } 588 589 var custom func(val string) bool 590 if s.GetUseTunnel() { 591 custom = func(val string) bool { 592 return strings.EqualFold(val, "tunnel") 593 } 594 } 595 596 fieldVals := make([]string, 0, (len(s.Metadata.Labels)*2)+(len(s.Spec.CmdLabels)*2)+len(s.Spec.PublicAddrs)+3) 597 598 labels := CombineLabels(s.Metadata.Labels, s.Spec.CmdLabels) 599 for key, value := range labels { 600 fieldVals = append(fieldVals, key, value) 601 } 602 603 fieldVals = append(fieldVals, s.Metadata.Name, s.Spec.Hostname, s.Spec.Addr) 604 fieldVals = append(fieldVals, s.Spec.PublicAddrs...) 605 606 return MatchSearch(fieldVals, values, custom) 607 } 608 609 // DeepCopy creates a clone of this server value 610 func (s *ServerV2) DeepCopy() Server { 611 return utils.CloneProtoMsg(s) 612 } 613 614 // CloneResource creates a clone of this server value 615 func (s *ServerV2) CloneResource() ResourceWithLabels { 616 return s.DeepCopy() 617 } 618 619 // GetCloudMetadata gets the cloud metadata for the server. 620 func (s *ServerV2) GetCloudMetadata() *CloudMetadata { 621 return s.Spec.CloudMetadata 622 } 623 624 // GetAWSInfo gets the AWS Cloud metadata for the server. 625 func (s *ServerV2) GetAWSInfo() *AWSInfo { 626 if s.Spec.CloudMetadata == nil { 627 return nil 628 } 629 630 return s.Spec.CloudMetadata.AWS 631 } 632 633 // SetCloudMetadata sets the server's cloud metadata. 634 func (s *ServerV2) SetCloudMetadata(meta *CloudMetadata) { 635 s.Spec.CloudMetadata = meta 636 } 637 638 // CommandLabel is a label that has a value as a result of the 639 // output generated by running command, e.g. hostname 640 type CommandLabel interface { 641 // GetPeriod returns label period 642 GetPeriod() time.Duration 643 // SetPeriod sets label period 644 SetPeriod(time.Duration) 645 // GetResult returns label result 646 GetResult() string 647 // SetResult sets label result 648 SetResult(string) 649 // GetCommand returns to execute and set as a label result 650 GetCommand() []string 651 // Clone returns label copy 652 Clone() CommandLabel 653 } 654 655 // Clone returns non-shallow copy of the label 656 func (c *CommandLabelV2) Clone() CommandLabel { 657 command := make([]string, len(c.Command)) 658 copy(command, c.Command) 659 return &CommandLabelV2{ 660 Command: command, 661 Period: c.Period, 662 Result: c.Result, 663 } 664 } 665 666 // SetResult sets label result 667 func (c *CommandLabelV2) SetResult(r string) { 668 c.Result = r 669 } 670 671 // SetPeriod sets label period 672 func (c *CommandLabelV2) SetPeriod(p time.Duration) { 673 c.Period = Duration(p) 674 } 675 676 // GetPeriod returns label period 677 func (c *CommandLabelV2) GetPeriod() time.Duration { 678 return c.Period.Duration() 679 } 680 681 // GetResult returns label result 682 func (c *CommandLabelV2) GetResult() string { 683 return c.Result 684 } 685 686 // GetCommand returns to execute and set as a label result 687 func (c *CommandLabelV2) GetCommand() []string { 688 return c.Command 689 } 690 691 // V2ToLabels converts concrete type to command label interface. 692 func V2ToLabels(l map[string]CommandLabelV2) map[string]CommandLabel { 693 out := make(map[string]CommandLabel, len(l)) 694 for key := range l { 695 val := l[key] 696 out[key] = &val 697 } 698 return out 699 } 700 701 // LabelsToV2 converts labels from interface to V2 spec 702 func LabelsToV2(labels map[string]CommandLabel) map[string]CommandLabelV2 { 703 out := make(map[string]CommandLabelV2, len(labels)) 704 for key, val := range labels { 705 out[key] = CommandLabelV2{ 706 Period: NewDuration(val.GetPeriod()), 707 Result: val.GetResult(), 708 Command: val.GetCommand(), 709 } 710 } 711 return out 712 } 713 714 // Servers represents a list of servers. 715 type Servers []Server 716 717 // Len returns the slice length. 718 func (s Servers) Len() int { return len(s) } 719 720 // Less compares servers by name. 721 func (s Servers) Less(i, j int) bool { 722 return s[i].GetName() < s[j].GetName() 723 } 724 725 // Swap swaps two servers. 726 func (s Servers) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 727 728 // SortByCustom custom sorts by given sort criteria. 729 func (s Servers) SortByCustom(sortBy SortBy) error { 730 if sortBy.Field == "" { 731 return nil 732 } 733 734 isDesc := sortBy.IsDesc 735 switch sortBy.Field { 736 case ResourceMetadataName: 737 sort.SliceStable(s, func(i, j int) bool { 738 return stringCompare(s[i].GetName(), s[j].GetName(), isDesc) 739 }) 740 case ResourceSpecHostname: 741 sort.SliceStable(s, func(i, j int) bool { 742 return stringCompare(s[i].GetHostname(), s[j].GetHostname(), isDesc) 743 }) 744 case ResourceSpecAddr: 745 sort.SliceStable(s, func(i, j int) bool { 746 return stringCompare(s[i].GetAddr(), s[j].GetAddr(), isDesc) 747 }) 748 default: 749 return trace.NotImplemented("sorting by field %q for resource %q is not supported", sortBy.Field, KindNode) 750 } 751 752 return nil 753 } 754 755 // AsResources returns as type resources with labels. 756 func (s Servers) AsResources() []ResourceWithLabels { 757 resources := make([]ResourceWithLabels, 0, len(s)) 758 for _, server := range s { 759 resources = append(resources, ResourceWithLabels(server)) 760 } 761 return resources 762 } 763 764 // GetFieldVals returns list of select field values. 765 func (s Servers) GetFieldVals(field string) ([]string, error) { 766 vals := make([]string, 0, len(s)) 767 switch field { 768 case ResourceMetadataName: 769 for _, server := range s { 770 vals = append(vals, server.GetName()) 771 } 772 case ResourceSpecHostname: 773 for _, server := range s { 774 vals = append(vals, server.GetHostname()) 775 } 776 case ResourceSpecAddr: 777 for _, server := range s { 778 vals = append(vals, server.GetAddr()) 779 } 780 default: 781 return nil, trace.NotImplemented("getting field %q for resource %q is not supported", field, KindNode) 782 } 783 784 return vals, nil 785 }