From 53913461db2af1818e47c901604fb1a754eb4457 Mon Sep 17 00:00:00 2001 From: Kyle Havlovitz Date: Tue, 19 Mar 2019 10:06:46 -0700 Subject: [PATCH] Add config types and state store table --- agent/consul/state/service_config.go | 127 +++++++++++++++++++++++++++ agent/structs/service_config.go | 75 ++++++++++++++++ 2 files changed, 202 insertions(+) create mode 100644 agent/consul/state/service_config.go create mode 100644 agent/structs/service_config.go diff --git a/agent/consul/state/service_config.go b/agent/consul/state/service_config.go new file mode 100644 index 000000000..7dd56bf57 --- /dev/null +++ b/agent/consul/state/service_config.go @@ -0,0 +1,127 @@ +package state + +import ( + "fmt" + + "github.com/hashicorp/consul/agent/structs" + memdb "github.com/hashicorp/go-memdb" +) + +const ( + configTableName = "configurations" +) + +// configTableSchema returns a new table schema used to store global service +// and proxy configurations. +func configTableSchema() *memdb.TableSchema { + return &memdb.TableSchema{ + Name: configTableName, + Indexes: map[string]*memdb.IndexSchema{ + "id": &memdb.IndexSchema{ + Name: "id", + AllowMissing: false, + Unique: true, + Indexer: &memdb.CompoundIndex{ + Indexes: []memdb.Indexer{ + &memdb.StringFieldIndex{ + Field: "Kind", + Lowercase: true, + }, + &memdb.StringFieldIndex{ + Field: "Name", + Lowercase: true, + }, + }, + }, + }, + }, + } +} + +func init() { + registerSchema(configTableSchema) +} + +// Configurations is used to pull all the configurations for the snapshot. +func (s *Snapshot) Configurations() ([]structs.Configuration, error) { + ixns, err := s.tx.Get(configTableName, "id") + if err != nil { + return nil, err + } + + var ret []structs.Configuration + for wrapped := ixns.Next(); wrapped != nil; wrapped = ixns.Next() { + ret = append(ret, wrapped.(structs.Configuration)) + } + + return ret, nil +} + +// Configuration is used when restoring from a snapshot. +func (s *Restore) Configuration(c structs.Configuration) error { + // Insert + if err := s.tx.Insert(configTableName, c); err != nil { + return fmt.Errorf("failed restoring configuration object: %s", err) + } + if err := indexUpdateMaxTxn(s.tx, c.ModifyIndex, configTableName); err != nil { + return fmt.Errorf("failed updating index: %s", err) + } + + return nil +} + +// EnsureConfiguration is called to upsert creation of a given configuration. +func (s *Store) EnsureConfiguration(idx uint64, conf structs.Configuration) error { + tx := s.db.Txn(true) + defer tx.Abort() + + // Does it make sense to validate here? We do this for service meta in the state store + // but could also do this in RPC endpoint. More version compatibility that way? + if err := conf.Validate(); err != nil { + return fmt.Errorf("failed validating config: %v", err) + } + + // Check for existing configuration. + existing, err := tx.First("configurations", "id", conf.GetKind(), conf.GetName()) + if err != nil { + return fmt.Errorf("failed configuration lookup: %s", err) + } + + if existing != nil { + conf.CreateIndex = serviceNode.CreateIndex + conf.ModifyIndex = serviceNode.ModifyIndex + } else { + conf.CreateIndex = idx + } + conf.ModifyIndex = idx + + // Insert the configuration and update the index + if err := tx.Insert("configurations", conf); err != nil { + return fmt.Errorf("failed inserting service: %s", err) + } + if err := tx.Insert("index", &IndexEntry{"configurations", idx}); err != nil { + return fmt.Errorf("failed updating index: %s", err) + } + + tx.Commit() + return nil +} + +// Configuration is called to get a given configuration. +func (s *Store) Configuration(idx uint64, kind structs.ConfigurationKind, name string) (structs.Configuration, error) { + tx := s.db.Txn(true) + defer tx.Abort() + + // Get the existing configuration. + existing, err := tx.First("configurations", "id", kind, name) + if err != nil { + return nil, fmt.Errorf("failed configuration lookup: %s", err) + } + + conf, ok := existing.(structs.Configuration) + if !ok { + return nil, fmt.Errorf("configuration %q (%s) is an invalid type: %T", name, kind, conf) + } + + return conf, nil +} diff --git a/agent/structs/service_config.go b/agent/structs/service_config.go new file mode 100644 index 000000000..1a6510818 --- /dev/null +++ b/agent/structs/service_config.go @@ -0,0 +1,75 @@ +package structs + +type ConfigurationKind string + +const ( + ServiceDefaults ConfigurationKind = "service-defaults" + ProxyDefaults ConfigurationKind = "proxy-defaults" +) + +// Should this be an interface or a switch on the existing config types? +type Configuration interface { + GetKind() ConfigurationKind + GetName() string + Validate() error +} + +// ServiceConfiguration is the top-level struct for the configuration of a service +// across the entire cluster. +type ServiceConfiguration struct { + Kind ConfigurationKind + Name string + Protocol string + Connect ConnectConfiguration + ServiceDefinitionDefaults ServiceDefinitionDefaults + + RaftIndex +} + +func (s *ServiceConfiguration) GetKind() ConfigurationKind { + return ServiceDefaults +} + +type ConnectConfiguration struct { + SidecarProxy bool +} + +type ServiceDefinitionDefaults struct { + EnableTagOverride bool + + // Non script/docker checks only + Check *HealthCheck + Checks HealthChecks + + // Kind is allowed to accommodate non-sidecar proxies but it will be an error + // if they also set Connect.DestinationServiceID since sidecars are + // configured via their associated service's config. + Kind ServiceKind + + // Only DestinationServiceName and Config are supported. + Proxy ConnectProxyConfig + + Connect ServiceConnect + + Weights Weights + + // DisableDirectDiscovery is a field that marks the service instance as + // not discoverable. This is useful in two cases: + // 1. Truly headless services like job workers that still need Connect + // sidecars to connect to upstreams. + // 2. Connect applications that expose services only through their sidecar + // and so discovery of their IP/port is meaningless since they can't be + // connected to by that means. + DisableDirectDiscovery bool +} + +// ProxyConfiguration is the top-level struct for global proxy configuration defaults. +type ProxyConfiguration struct { + Kind ConfigurationKind + Name string + ProxyConfig ConnectProxyConfig +} + +func (p *ProxyConfiguration) GetKind() ConfigurationKind { + return ProxyDefaults +}