github.com/gravitational/teleport/api@v0.0.0-20240507183017-3110591cbafc/types/databaseserver.go (about)

     1  /*
     2  Copyright 2020-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  	"sort"
    22  	"time"
    23  
    24  	"github.com/gravitational/trace"
    25  	"golang.org/x/exp/maps"
    26  
    27  	"github.com/gravitational/teleport/api"
    28  	"github.com/gravitational/teleport/api/utils"
    29  )
    30  
    31  // DatabaseServer represents a database access server.
    32  type DatabaseServer interface {
    33  	// ResourceWithLabels provides common resource methods.
    34  	ResourceWithLabels
    35  	// GetNamespace returns server namespace.
    36  	GetNamespace() string
    37  	// GetTeleportVersion returns the teleport version the server is running on.
    38  	GetTeleportVersion() string
    39  	// GetHostname returns the server hostname.
    40  	GetHostname() string
    41  	// GetHostID returns ID of the host the server is running on.
    42  	GetHostID() string
    43  	// GetRotation gets the state of certificate authority rotation.
    44  	GetRotation() Rotation
    45  	// SetRotation sets the state of certificate authority rotation.
    46  	SetRotation(Rotation)
    47  	// String returns string representation of the server.
    48  	String() string
    49  	// Copy returns a copy of this database server object.
    50  	Copy() DatabaseServer
    51  
    52  	// CloneResource returns a copy of the DatabaseServer as a ResourceWithLabels
    53  	CloneResource() ResourceWithLabels
    54  	// GetDatabase returns the database this database server proxies.
    55  	GetDatabase() Database
    56  	// SetDatabase sets the database this database server proxies.
    57  	SetDatabase(Database) error
    58  	// ProxiedService provides common methods for a proxied service.
    59  	ProxiedService
    60  }
    61  
    62  // NewDatabaseServerV3 creates a new database server instance.
    63  func NewDatabaseServerV3(meta Metadata, spec DatabaseServerSpecV3) (*DatabaseServerV3, error) {
    64  	s := &DatabaseServerV3{
    65  		Metadata: meta,
    66  		Spec:     spec,
    67  	}
    68  	if err := s.CheckAndSetDefaults(); err != nil {
    69  		return nil, trace.Wrap(err)
    70  	}
    71  	return s, nil
    72  }
    73  
    74  // GetVersion returns the database server resource version.
    75  func (s *DatabaseServerV3) GetVersion() string {
    76  	return s.Version
    77  }
    78  
    79  // GetTeleportVersion returns the Teleport version the server is running.
    80  func (s *DatabaseServerV3) GetTeleportVersion() string {
    81  	return s.Spec.Version
    82  }
    83  
    84  // GetHostname returns the database server hostname.
    85  func (s *DatabaseServerV3) GetHostname() string {
    86  	return s.Spec.Hostname
    87  }
    88  
    89  // GetHostID returns ID of the host the server is running on.
    90  func (s *DatabaseServerV3) GetHostID() string {
    91  	return s.Spec.HostID
    92  }
    93  
    94  // GetKind returns the resource kind.
    95  func (s *DatabaseServerV3) GetKind() string {
    96  	return s.Kind
    97  }
    98  
    99  // GetSubKind returns the resource subkind.
   100  func (s *DatabaseServerV3) GetSubKind() string {
   101  	return s.SubKind
   102  }
   103  
   104  // SetSubKind sets the resource subkind.
   105  func (s *DatabaseServerV3) SetSubKind(sk string) {
   106  	s.SubKind = sk
   107  }
   108  
   109  // GetResourceID returns the resource ID.
   110  func (s *DatabaseServerV3) GetResourceID() int64 {
   111  	return s.Metadata.ID
   112  }
   113  
   114  // SetResourceID sets the resource ID.
   115  func (s *DatabaseServerV3) SetResourceID(id int64) {
   116  	s.Metadata.ID = id
   117  }
   118  
   119  // GetRevision returns the revision
   120  func (s *DatabaseServerV3) GetRevision() string {
   121  	return s.Metadata.GetRevision()
   122  }
   123  
   124  // SetRevision sets the revision
   125  func (s *DatabaseServerV3) SetRevision(rev string) {
   126  	s.Metadata.SetRevision(rev)
   127  }
   128  
   129  // GetMetadata returns the resource metadata.
   130  func (s *DatabaseServerV3) GetMetadata() Metadata {
   131  	return s.Metadata
   132  }
   133  
   134  // GetNamespace returns the resource namespace.
   135  func (s *DatabaseServerV3) GetNamespace() string {
   136  	return s.Metadata.Namespace
   137  }
   138  
   139  // SetExpiry sets the resource expiry time.
   140  func (s *DatabaseServerV3) SetExpiry(expiry time.Time) {
   141  	s.Metadata.SetExpiry(expiry)
   142  }
   143  
   144  // Expiry returns the resource expiry time.
   145  func (s *DatabaseServerV3) Expiry() time.Time {
   146  	return s.Metadata.Expiry()
   147  }
   148  
   149  // GetName returns the resource name.
   150  func (s *DatabaseServerV3) GetName() string {
   151  	return s.Metadata.Name
   152  }
   153  
   154  // SetName sets the resource name.
   155  func (s *DatabaseServerV3) SetName(name string) {
   156  	s.Metadata.Name = name
   157  }
   158  
   159  // GetRotation returns the server CA rotation state.
   160  func (s *DatabaseServerV3) GetRotation() Rotation {
   161  	return s.Spec.Rotation
   162  }
   163  
   164  // SetRotation sets the server CA rotation state.
   165  func (s *DatabaseServerV3) SetRotation(r Rotation) {
   166  	s.Spec.Rotation = r
   167  }
   168  
   169  // GetDatabase returns the database this database server proxies.
   170  func (s *DatabaseServerV3) GetDatabase() Database {
   171  	if s.Spec.Database == nil {
   172  		return nil
   173  	}
   174  	return s.Spec.Database
   175  }
   176  
   177  // SetDatabase sets the database this database server proxies.
   178  func (s *DatabaseServerV3) SetDatabase(database Database) error {
   179  	databaseV3, ok := database.(*DatabaseV3)
   180  	if !ok {
   181  		return trace.BadParameter("expected *DatabaseV3, got %T", database)
   182  	}
   183  	s.Spec.Database = databaseV3
   184  	return nil
   185  }
   186  
   187  // GetProxyID returns a list of proxy ids this server is connected to.
   188  func (s *DatabaseServerV3) GetProxyIDs() []string {
   189  	return s.Spec.ProxyIDs
   190  }
   191  
   192  // SetProxyID sets the proxy ids this server is connected to.
   193  func (s *DatabaseServerV3) SetProxyIDs(proxyIDs []string) {
   194  	s.Spec.ProxyIDs = proxyIDs
   195  }
   196  
   197  // String returns the server string representation.
   198  func (s *DatabaseServerV3) String() string {
   199  	return fmt.Sprintf("DatabaseServer(Name=%v, Version=%v, Hostname=%v, HostID=%v, Database=%v)",
   200  		s.GetName(), s.GetTeleportVersion(), s.GetHostname(), s.GetHostID(), s.GetDatabase())
   201  }
   202  
   203  // setStaticFields sets static resource header and metadata fields.
   204  func (s *DatabaseServerV3) setStaticFields() {
   205  	s.Kind = KindDatabaseServer
   206  	s.Version = V3
   207  }
   208  
   209  // CheckAndSetDefaults checks and sets default values for any missing fields.
   210  func (s *DatabaseServerV3) CheckAndSetDefaults() error {
   211  	s.setStaticFields()
   212  	if err := s.Metadata.CheckAndSetDefaults(); err != nil {
   213  		return trace.Wrap(err)
   214  	}
   215  	if s.Spec.HostID == "" {
   216  		return trace.BadParameter("missing database server HostID")
   217  	}
   218  	if s.Spec.Hostname == "" {
   219  		return trace.BadParameter("missing database server Hostname")
   220  	}
   221  	if s.Spec.Version == "" {
   222  		s.Spec.Version = api.Version
   223  	}
   224  
   225  	if s.Spec.Database == nil {
   226  		return trace.BadParameter("missing database server Database")
   227  	}
   228  
   229  	if err := s.Spec.Database.CheckAndSetDefaults(); err != nil {
   230  		return trace.Wrap(err)
   231  	}
   232  
   233  	return nil
   234  }
   235  
   236  // Origin returns the origin value of the resource.
   237  func (s *DatabaseServerV3) Origin() string {
   238  	return s.Metadata.Origin()
   239  }
   240  
   241  // SetOrigin sets the origin value of the resource.
   242  func (s *DatabaseServerV3) SetOrigin(origin string) {
   243  	s.Metadata.SetOrigin(origin)
   244  }
   245  
   246  // GetLabel retrieves the label with the provided key. If not found
   247  // value will be empty and ok will be false.
   248  func (s *DatabaseServerV3) GetLabel(key string) (value string, ok bool) {
   249  	if s.Spec.Database != nil {
   250  		if v, ok := s.Spec.Database.GetLabel(key); ok {
   251  			return v, ok
   252  		}
   253  	}
   254  
   255  	v, ok := s.Metadata.Labels[key]
   256  	return v, ok
   257  }
   258  
   259  // GetAllLabels returns all resource's labels. Considering:
   260  // * Static labels from `Metadata.Labels` and `Spec.Database`.
   261  // * Dynamic labels from `Spec.DynamicLabels`.
   262  func (s *DatabaseServerV3) GetAllLabels() map[string]string {
   263  	staticLabels := map[string]string{}
   264  	maps.Copy(staticLabels, s.Metadata.Labels)
   265  	if s.Spec.Database != nil {
   266  		maps.Copy(staticLabels, s.Spec.Database.GetAllLabels())
   267  	}
   268  
   269  	return staticLabels
   270  }
   271  
   272  // GetStaticLabels returns the database server static labels.
   273  func (s *DatabaseServerV3) GetStaticLabels() map[string]string {
   274  	return s.Metadata.Labels
   275  }
   276  
   277  // SetStaticLabels sets the database server static labels.
   278  func (s *DatabaseServerV3) SetStaticLabels(sl map[string]string) {
   279  	s.Metadata.Labels = sl
   280  }
   281  
   282  // Copy returns a copy of this database server object.
   283  func (s *DatabaseServerV3) Copy() DatabaseServer {
   284  	return utils.CloneProtoMsg(s)
   285  }
   286  
   287  // CloneResource returns a copy of this database server object.
   288  func (s *DatabaseServerV3) CloneResource() ResourceWithLabels {
   289  	return s.Copy()
   290  }
   291  
   292  // MatchSearch goes through select field values and tries to
   293  // match against the list of search values.
   294  func (s *DatabaseServerV3) MatchSearch(values []string) bool {
   295  	return MatchSearch(nil, values, nil)
   296  }
   297  
   298  // DatabaseServers represents a list of database servers.
   299  type DatabaseServers []DatabaseServer
   300  
   301  // Len returns the slice length.
   302  func (s DatabaseServers) Len() int { return len(s) }
   303  
   304  // Less compares database servers by name and host ID.
   305  func (s DatabaseServers) Less(i, j int) bool {
   306  	switch {
   307  	case s[i].GetName() < s[j].GetName():
   308  		return true
   309  	case s[i].GetName() > s[j].GetName():
   310  		return false
   311  	default:
   312  		return s[i].GetHostID() < s[j].GetHostID()
   313  	}
   314  }
   315  
   316  // Swap swaps two database servers.
   317  func (s DatabaseServers) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
   318  
   319  // SortByCustom custom sorts by given sort criteria.
   320  func (s DatabaseServers) SortByCustom(sortBy SortBy) error {
   321  	if sortBy.Field == "" {
   322  		return nil
   323  	}
   324  
   325  	// We assume sorting by type DatabaseServer, we are really
   326  	// wanting to sort its contained resource Database.
   327  	isDesc := sortBy.IsDesc
   328  	switch sortBy.Field {
   329  	case ResourceMetadataName:
   330  		sort.SliceStable(s, func(i, j int) bool {
   331  			return stringCompare(s[i].GetDatabase().GetName(), s[j].GetDatabase().GetName(), isDesc)
   332  		})
   333  	case ResourceSpecDescription:
   334  		sort.SliceStable(s, func(i, j int) bool {
   335  			return stringCompare(s[i].GetDatabase().GetDescription(), s[j].GetDatabase().GetDescription(), isDesc)
   336  		})
   337  	case ResourceSpecType:
   338  		sort.SliceStable(s, func(i, j int) bool {
   339  			return stringCompare(s[i].GetDatabase().GetType(), s[j].GetDatabase().GetType(), isDesc)
   340  		})
   341  	default:
   342  		return trace.NotImplemented("sorting by field %q for resource %q is not supported", sortBy.Field, KindDatabaseServer)
   343  	}
   344  
   345  	return nil
   346  }
   347  
   348  // AsResources returns db servers as type resources with labels.
   349  func (s DatabaseServers) AsResources() []ResourceWithLabels {
   350  	resources := make([]ResourceWithLabels, 0, len(s))
   351  	for _, server := range s {
   352  		resources = append(resources, ResourceWithLabels(server))
   353  	}
   354  	return resources
   355  }
   356  
   357  // GetFieldVals returns list of select field values.
   358  func (s DatabaseServers) GetFieldVals(field string) ([]string, error) {
   359  	vals := make([]string, 0, len(s))
   360  	switch field {
   361  	case ResourceMetadataName:
   362  		for _, server := range s {
   363  			vals = append(vals, server.GetDatabase().GetName())
   364  		}
   365  	case ResourceSpecDescription:
   366  		for _, server := range s {
   367  			vals = append(vals, server.GetDatabase().GetDescription())
   368  		}
   369  	case ResourceSpecType:
   370  		for _, server := range s {
   371  			vals = append(vals, server.GetDatabase().GetType())
   372  		}
   373  	default:
   374  		return nil, trace.NotImplemented("getting field %q for resource %q is not supported", field, KindDatabaseServer)
   375  	}
   376  
   377  	return vals, nil
   378  }
   379  
   380  // ToDatabases converts database servers to a list of databases and
   381  // deduplicates the databases by name.
   382  func (s DatabaseServers) ToDatabases() []Database {
   383  	databases := make([]Database, 0, len(s))
   384  	for _, server := range s {
   385  		databases = append(databases, server.GetDatabase())
   386  	}
   387  	return DeduplicateDatabases(databases)
   388  }