teleport/lib/services/server.go

539 lines
14 KiB
Go
Raw Normal View History

2016-12-28 02:54:55 +00:00
package services
import (
2016-12-28 22:07:03 +00:00
"encoding/json"
"fmt"
"sort"
"strings"
2016-12-28 02:54:55 +00:00
"time"
2016-12-28 22:07:03 +00:00
"github.com/gravitational/teleport/lib/utils"
2017-04-07 23:51:31 +00:00
2016-12-28 22:07:03 +00:00
"github.com/gravitational/trace"
2017-04-07 23:51:31 +00:00
"github.com/jonboulle/clockwork"
2016-12-28 02:54:55 +00:00
)
// Server represents a Node, Proxy or Auth server in a Teleport cluster
type Server interface {
2017-04-07 23:51:31 +00:00
// Resource provides common resource headers
Resource
2016-12-28 02:54:55 +00:00
// GetAddr return server address
GetAddr() string
// GetHostname returns server hostname
GetHostname() string
// GetNamespace returns server namespace
GetNamespace() string
// GetAllLabels returns server's static and dynamic label values merged together
GetAllLabels() map[string]string
// GetLabels returns server's static label key pairs
GetLabels() map[string]string
// GetCmdLabels returns command labels
GetCmdLabels() map[string]CommandLabel
// GetPublicAddr is an optional field that returns the public address this cluster can be reached at.
GetPublicAddr() string
2016-12-28 02:54:55 +00:00
// String returns string representation of the server
String() string
2016-12-29 19:36:57 +00:00
// SetAddr sets server address
SetAddr(addr string)
// SetPublicAddr sets the public address this cluster can be reached at.
SetPublicAddr(string)
2016-12-29 19:36:57 +00:00
// SetNamespace sets server namespace
SetNamespace(namespace string)
2016-12-30 21:25:35 +00:00
// V1 returns V1 version for backwards compatibility
V1() *ServerV1
2016-12-30 22:47:52 +00:00
// MatchAgainst takes a map of labels and returns True if this server
// has ALL of them
//
// Any server matches against an empty label set
MatchAgainst(labels map[string]string) bool
2016-12-30 23:13:45 +00:00
// LabelsString returns a comma separated string with all node's labels
LabelsString() string
2016-12-30 21:25:35 +00:00
}
// ServersToV1 converts list of servers to slice of V1 style ones
func ServersToV1(in []Server) []ServerV1 {
out := make([]ServerV1, len(in))
for i := range in {
out[i] = *(in[i].V1())
}
return out
2016-12-28 02:54:55 +00:00
}
2016-12-29 20:23:58 +00:00
// ServerV2 is version1 resource spec of the server
type ServerV2 struct {
2016-12-28 02:54:55 +00:00
// Kind is a resource kind
Kind string `json:"kind"`
// Version is version
Version string `json:"version"`
// Metadata is User metadata
Metadata Metadata `json:"metadata"`
// Spec contains user specification
2016-12-29 20:23:58 +00:00
Spec ServerSpecV2 `json:"spec"`
2016-12-28 02:54:55 +00:00
}
2017-04-07 23:51:31 +00:00
// GetMetadata returns metadata
func (s *ServerV2) GetMetadata() Metadata {
return s.Metadata
}
2016-12-29 23:16:42 +00:00
// V2 returns version 2 of the resource, itself
func (s *ServerV2) V2() *ServerV2 {
return s
}
// V1 returns V1 version of the resource
func (s *ServerV2) V1() *ServerV1 {
labels := make(map[string]CommandLabelV1, len(s.Spec.CmdLabels))
for key := range s.Spec.CmdLabels {
val := s.Spec.CmdLabels[key]
labels[key] = CommandLabelV1{
Period: val.Period.Duration,
Result: val.Result,
Command: val.Command,
}
}
return &ServerV1{
ID: s.Metadata.Name,
Kind: s.Kind,
Namespace: ProcessNamespace(s.Metadata.Namespace),
Addr: s.Spec.Addr,
Hostname: s.Spec.Hostname,
2016-12-29 23:33:23 +00:00
Labels: s.Metadata.Labels,
2016-12-29 23:16:42 +00:00
CmdLabels: labels,
}
}
2016-12-29 19:36:57 +00:00
// SetNamespace sets server namespace
2016-12-29 20:23:58 +00:00
func (s *ServerV2) SetNamespace(namespace string) {
2016-12-29 19:36:57 +00:00
s.Metadata.Namespace = namespace
}
// SetAddr sets server address
2016-12-29 20:23:58 +00:00
func (s *ServerV2) SetAddr(addr string) {
2016-12-29 19:36:57 +00:00
s.Spec.Addr = addr
}
2017-04-07 23:51:31 +00:00
// SetExpiry sets expiry time for the object
func (s *ServerV2) SetExpiry(expires time.Time) {
s.Metadata.SetExpiry(expires)
}
// Expires retuns object expiry setting
func (s *ServerV2) Expiry() time.Time {
return s.Metadata.Expiry()
}
// SetTTL sets Expires header using realtime clock
func (s *ServerV2) SetTTL(clock clockwork.Clock, ttl time.Duration) {
s.Metadata.SetTTL(clock, ttl)
}
// SetPublicAddr sets the public address this cluster can be reached at.
func (s *ServerV2) SetPublicAddr(addr string) {
s.Spec.PublicAddr = addr
}
2016-12-28 02:54:55 +00:00
// GetName returns server name
2016-12-29 20:23:58 +00:00
func (s *ServerV2) GetName() string {
2016-12-28 02:54:55 +00:00
return s.Metadata.Name
}
2017-04-07 23:51:31 +00:00
// SetName sets the name of the TrustedCluster.
func (s *ServerV2) SetName(e string) {
s.Metadata.Name = e
}
2016-12-28 02:54:55 +00:00
// GetAddr return server address
2016-12-29 20:23:58 +00:00
func (s *ServerV2) GetAddr() string {
2016-12-28 02:54:55 +00:00
return s.Spec.Addr
}
// GetPublicAddr is an optional field that returns the public address this cluster can be reached at.
func (s *ServerV2) GetPublicAddr() string {
return s.Spec.PublicAddr
}
2016-12-28 02:54:55 +00:00
// GetHostname returns server hostname
2016-12-29 20:23:58 +00:00
func (s *ServerV2) GetHostname() string {
2016-12-28 02:54:55 +00:00
return s.Spec.Hostname
}
// GetLabels returns server's static label key pairs
2016-12-29 20:23:58 +00:00
func (s *ServerV2) GetLabels() map[string]string {
2016-12-28 22:07:03 +00:00
return s.Metadata.Labels
2016-12-28 02:54:55 +00:00
}
// GetCmdLabels returns command labels
2016-12-29 20:23:58 +00:00
func (s *ServerV2) GetCmdLabels() map[string]CommandLabel {
2016-12-28 02:54:55 +00:00
if s.Spec.CmdLabels == nil {
return nil
}
2016-12-28 22:07:03 +00:00
out := make(map[string]CommandLabel, len(s.Spec.CmdLabels))
2016-12-28 02:54:55 +00:00
for key := range s.Spec.CmdLabels {
val := s.Spec.CmdLabels[key]
out[key] = &val
}
return out
}
2016-12-29 20:23:58 +00:00
func (s *ServerV2) String() string {
2016-12-28 22:07:03 +00:00
return fmt.Sprintf("Server(name=%v, namespace=%v, addr=%v, labels=%v)", s.Metadata.Name, s.Metadata.Namespace, s.Spec.Addr, s.Metadata.Labels)
2016-12-28 02:54:55 +00:00
}
// GetNamespace returns server namespace
2016-12-29 20:23:58 +00:00
func (s *ServerV2) GetNamespace() string {
2016-12-28 02:54:55 +00:00
return ProcessNamespace(s.Metadata.Namespace)
}
// GetAllLabels returns the full key:value map of both static labels and
// "command labels"
2016-12-29 20:23:58 +00:00
func (s *ServerV2) GetAllLabels() map[string]string {
2016-12-28 02:54:55 +00:00
lmap := make(map[string]string)
2016-12-28 22:07:03 +00:00
for key, value := range s.Metadata.Labels {
2016-12-28 02:54:55 +00:00
lmap[key] = value
}
2016-12-28 22:07:03 +00:00
for key, cmd := range s.Spec.CmdLabels {
2016-12-28 02:54:55 +00:00
lmap[key] = cmd.Result
}
return lmap
}
// MatchAgainst takes a map of labels and returns True if this server
// has ALL of them
//
// Any server matches against an empty label set
2016-12-29 20:23:58 +00:00
func (s *ServerV2) MatchAgainst(labels map[string]string) bool {
2016-12-28 02:54:55 +00:00
if labels != nil {
2016-12-28 22:07:03 +00:00
myLabels := s.GetAllLabels()
2016-12-28 02:54:55 +00:00
for key, value := range labels {
if myLabels[key] != value {
return false
}
}
}
return true
}
// LabelsString returns a comma separated string with all node's labels
2016-12-29 20:23:58 +00:00
func (s *ServerV2) LabelsString() string {
2016-12-28 02:54:55 +00:00
labels := []string{}
2016-12-28 22:07:03 +00:00
for key, val := range s.Metadata.Labels {
2016-12-28 02:54:55 +00:00
labels = append(labels, fmt.Sprintf("%s=%s", key, val))
}
2016-12-28 22:07:03 +00:00
for key, val := range s.Spec.CmdLabels {
2016-12-28 02:54:55 +00:00
labels = append(labels, fmt.Sprintf("%s=%s", key, val.Result))
}
sort.Strings(labels)
return strings.Join(labels, ",")
}
2016-12-29 20:23:58 +00:00
// ServerSpecV2 is a specification for V2 Server
type ServerSpecV2 struct {
2016-12-28 02:54:55 +00:00
// Addr is server host:port address
Addr string `json:"addr"`
// PublicAddr is the public address this cluster can be reached at.
PublicAddr string `json:"public_addr,omitempty"`
2016-12-28 02:54:55 +00:00
// Hostname is server hostname
Hostname string `json:"hostname"`
// CmdLabels is server dynamic labels
2016-12-29 20:23:58 +00:00
CmdLabels map[string]CommandLabelV2 `json:"cmd_labels,omitempty"`
2016-12-28 02:54:55 +00:00
}
2016-12-29 20:23:58 +00:00
// ServerSpecV2Schema is JSON schema for server
const ServerSpecV2Schema = `{
2016-12-28 02:54:55 +00:00
"type": "object",
"additionalProperties": false,
"properties": {
"addr": {"type": "string"},
"public_addr": {"type": "string"},
2016-12-28 02:54:55 +00:00
"hostname": {"type": "string"},
"labels": {
2016-12-29 02:47:33 +00:00
"type": "object",
2016-12-28 02:54:55 +00:00
"patternProperties": {
"^.*$": { "type": "string" }
}
},
"cmd_labels": {
2016-12-29 02:47:33 +00:00
"type": "object",
"patternProperties": {
"^.*$": {
"type": "object",
"additionalProperties": false,
"required": ["command"],
"properties": {
"command": {"type": "array", "items": {"type": "string"}},
"period": {"type": "string"},
"result": {"type": "string"}
}
}
}
2016-12-28 02:54:55 +00:00
}
}
}`
2016-12-29 23:16:42 +00:00
// ServerV1 represents V1 spec of the server
type ServerV1 struct {
2016-12-28 02:54:55 +00:00
Kind string `json:"kind"`
ID string `json:"id"`
Addr string `json:"addr"`
Hostname string `json:"hostname"`
Namespace string `json:"namespace"`
Labels map[string]string `json:"labels"`
2016-12-29 23:16:42 +00:00
CmdLabels map[string]CommandLabelV1 `json:"cmd_labels"`
2016-12-28 02:54:55 +00:00
}
2016-12-29 23:16:42 +00:00
// V1 returns V1 version of the resource
func (s *ServerV1) V1() *ServerV1 {
return s
}
// V2 returns V2 version of the resource
func (s *ServerV1) V2() *ServerV2 {
2016-12-29 20:23:58 +00:00
labels := make(map[string]CommandLabelV2, len(s.CmdLabels))
2016-12-28 02:54:55 +00:00
for key := range s.CmdLabels {
val := s.CmdLabels[key]
2016-12-29 20:23:58 +00:00
labels[key] = CommandLabelV2{
2016-12-28 02:54:55 +00:00
Period: Duration{Duration: val.Period},
Result: val.Result,
Command: val.Command,
}
}
2016-12-29 20:23:58 +00:00
return &ServerV2{
2016-12-28 02:54:55 +00:00
Kind: s.Kind,
2016-12-29 20:23:58 +00:00
Version: V2,
2016-12-28 02:54:55 +00:00
Metadata: Metadata{
Name: s.ID,
2016-12-28 23:50:32 +00:00
Namespace: ProcessNamespace(s.Namespace),
2016-12-28 22:07:03 +00:00
Labels: s.Labels,
2016-12-28 02:54:55 +00:00
},
2016-12-29 20:23:58 +00:00
Spec: ServerSpecV2{
2016-12-28 22:07:03 +00:00
Addr: s.Addr,
Hostname: s.Hostname,
CmdLabels: labels,
2016-12-28 02:54:55 +00:00
},
}
}
2016-12-30 20:20:48 +00:00
// LabelsToV2 converts labels from interface to V2 spec
func LabelsToV2(labels map[string]CommandLabel) map[string]CommandLabelV2 {
out := make(map[string]CommandLabelV2, len(labels))
for key, val := range labels {
out[key] = CommandLabelV2{
Period: NewDuration(val.GetPeriod()),
Result: val.GetResult(),
Command: val.GetCommand(),
}
}
return out
}
2016-12-29 20:23:58 +00:00
// CommandLabelV2 is a label that has a value as a result of the
2016-12-28 02:54:55 +00:00
// output generated by running command, e.g. hostname
type CommandLabel interface {
// GetPeriod returns label period
GetPeriod() time.Duration
2016-12-30 20:20:48 +00:00
// SetPeriod sets label period
SetPeriod(time.Duration)
2016-12-28 02:54:55 +00:00
// GetResult returns label result
GetResult() string
2016-12-30 20:20:48 +00:00
// SetResult sets label result
SetResult(string)
2016-12-28 02:54:55 +00:00
// GetCommand returns to execute and set as a label result
GetCommand() []string
2016-12-31 01:07:54 +00:00
// Clone returns label copy
Clone() CommandLabel
2016-12-28 02:54:55 +00:00
}
2016-12-29 20:23:58 +00:00
// CommandLabelV2 is a label that has a value as a result of the
2016-12-28 02:54:55 +00:00
// output generated by running command, e.g. hostname
2016-12-29 20:23:58 +00:00
type CommandLabelV2 struct {
2016-12-28 02:54:55 +00:00
// Period is a time between command runs
Period Duration `json:"period"`
// Command is a command to run
Command []string `json:"command"` //["/usr/bin/hostname", "--long"]
// Result captures standard output
Result string `json:"result"`
}
2016-12-31 01:07:54 +00:00
// Clone returns label copy
func (c *CommandLabelV2) Clone() CommandLabel {
cp := *c
return &cp
}
2016-12-30 20:20:48 +00:00
// SetResult sets label result
func (c *CommandLabelV2) SetResult(r string) {
c.Result = r
}
// SetPeriod sets label period
func (c *CommandLabelV2) SetPeriod(p time.Duration) {
c.Period.Duration = p
}
2016-12-28 02:54:55 +00:00
// GetPeriod returns label period
2016-12-29 20:23:58 +00:00
func (c *CommandLabelV2) GetPeriod() time.Duration {
2016-12-28 02:54:55 +00:00
return c.Period.Duration
}
// GetResult returns label result
2016-12-29 20:23:58 +00:00
func (c *CommandLabelV2) GetResult() string {
2016-12-28 02:54:55 +00:00
return c.Result
}
// GetCommand returns to execute and set as a label result
2016-12-29 20:23:58 +00:00
func (c *CommandLabelV2) GetCommand() []string {
2016-12-28 02:54:55 +00:00
return c.Command
}
2016-12-29 23:16:42 +00:00
// CommandLabelV1 is a label that has a value as a result of the
2016-12-28 02:54:55 +00:00
// output generated by running command, e.g. hostname
2016-12-29 23:16:42 +00:00
type CommandLabelV1 struct {
2016-12-28 02:54:55 +00:00
// Period is a time between command runs
Period time.Duration `json:"period"`
// Command is a command to run
Command []string `json:"command"` //["/usr/bin/hostname", "--long"]
// Result captures standard output
Result string `json:"result"`
}
// CommandLabels is a set of command labels
type CommandLabels map[string]CommandLabel
// SetEnv sets the value of the label from environment variable
func (c *CommandLabels) SetEnv(v string) error {
if err := json.Unmarshal([]byte(v), c); err != nil {
return trace.Wrap(err, "can not parse Command Labels")
}
return nil
}
2016-12-28 03:54:02 +00:00
// GetServerSchema returns role schema with optionally injected
// schema for extensions
func GetServerSchema() string {
2016-12-29 20:23:58 +00:00
return fmt.Sprintf(V2SchemaTemplate, MetadataSchema, ServerSpecV2Schema)
2016-12-28 03:54:02 +00:00
}
// UnmarshalServerResource unmarshals role from JSON or YAML,
// sets defaults and checks the schema
func UnmarshalServerResource(data []byte, kind string) (Server, error) {
if len(data) == 0 {
return nil, trace.BadParameter("missing server data")
}
var h ResourceHeader
2016-12-28 22:07:03 +00:00
err := json.Unmarshal(data, &h)
2016-12-28 03:54:02 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
switch h.Version {
case "":
2016-12-29 23:16:42 +00:00
var s ServerV1
2016-12-28 22:07:03 +00:00
err := json.Unmarshal(data, &s)
2016-12-28 03:54:02 +00:00
if err != nil {
return nil, trace.Wrap(err)
}
s.Kind = kind
2016-12-29 20:23:58 +00:00
return s.V2(), nil
case V2:
var s ServerV2
2016-12-28 22:07:03 +00:00
if err := utils.UnmarshalWithSchema(GetServerSchema(), &s, data); err != nil {
2016-12-28 03:54:02 +00:00
return nil, trace.BadParameter(err.Error())
}
2017-04-07 23:51:31 +00:00
utils.UTC(&s.Metadata.Expires)
2016-12-28 22:07:03 +00:00
return &s, nil
2016-12-28 03:54:02 +00:00
}
return nil, trace.BadParameter("server resource version %v is not supported", h.Version)
}
var serverMarshaler ServerMarshaler = &TeleportServerMarshaler{}
func SetServerMarshaler(m ServerMarshaler) {
marshalerMutex.Lock()
defer marshalerMutex.Unlock()
serverMarshaler = m
}
func GetServerMarshaler() ServerMarshaler {
marshalerMutex.Lock()
defer marshalerMutex.Unlock()
return serverMarshaler
}
// ServerMarshaler implements marshal/unmarshal of Role implementations
// mostly adds support for extended versions
type ServerMarshaler interface {
// UnmarshalServer from binary representation
UnmarshalServer(bytes []byte, kind string) (Server, error)
// MarshalServer to binary representation
2016-12-29 23:16:42 +00:00
MarshalServer(Server, ...MarshalOption) ([]byte, error)
2016-12-28 03:54:02 +00:00
}
type TeleportServerMarshaler struct{}
// UnmarshalServer unmarshals server from JSON
func (*TeleportServerMarshaler) UnmarshalServer(bytes []byte, kind string) (Server, error) {
return UnmarshalServerResource(bytes, kind)
}
2016-12-29 23:16:42 +00:00
// MarshalServer marshals server into JSON
func (*TeleportServerMarshaler) MarshalServer(s Server, opts ...MarshalOption) ([]byte, error) {
cfg, err := collectOptions(opts)
if err != nil {
return nil, trace.Wrap(err)
}
type serverv1 interface {
V1() *ServerV1
}
type serverv2 interface {
V2() *ServerV2
}
version := cfg.GetVersion()
switch version {
case V1:
v, ok := s.(serverv1)
if !ok {
return nil, trace.BadParameter("don't know how to marshal %v", V1)
}
return json.Marshal(v.V1())
case V2:
v, ok := s.(serverv2)
if !ok {
return nil, trace.BadParameter("don't know how to marshal %v", V2)
}
return json.Marshal(v.V2())
default:
return nil, trace.BadParameter("version %v is not supported", version)
}
2016-12-28 03:54:02 +00:00
}
2016-12-29 02:47:33 +00:00
// SortedServers is a sort wrapper that sorts servers by name
type SortedServers []Server
func (s SortedServers) Len() int {
return len(s)
}
func (s SortedServers) Less(i, j int) bool {
return s[i].GetName() < s[j].GetName()
}
func (s SortedServers) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
// SortedReverseTunnels sorts reverse tunnels by cluster name
type SortedReverseTunnels []ReverseTunnel
func (s SortedReverseTunnels) Len() int {
return len(s)
}
func (s SortedReverseTunnels) Less(i, j int) bool {
return s[i].GetClusterName() < s[j].GetClusterName()
}
func (s SortedReverseTunnels) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}