From 1c1b0994b843b6946872fc91dd78e34db52d748d Mon Sep 17 00:00:00 2001 From: Nick Ethier Date: Mon, 26 Sep 2022 14:58:15 -0400 Subject: [PATCH] add HCP integration component (#14723) * add HCP integration * lint: use non-deprecated logging interface --- .changelog/14723.txt | 3 + agent/agent.go | 43 ++- agent/agent_test.go | 65 ++++ agent/config/builder.go | 16 + agent/config/config.go | 9 + agent/config/runtime.go | 11 + agent/config/runtime_test.go | 89 ++--- .../TestRuntimeConfig_Sanitize.golden | 7 + agent/config/testdata/full-config.hcl | 7 + agent/config/testdata/full-config.json | 7 + agent/consul/client_serf.go | 4 +- agent/consul/leader_test.go | 16 +- agent/consul/options.go | 7 +- agent/consul/server.go | 73 +++++ agent/consul/server_serf.go | 4 +- agent/consul/server_test.go | 29 +- agent/hcp/bootstrap/bootstrap.go | 305 ++++++++++++++++++ agent/hcp/bootstrap/testing.go | 160 +++++++++ agent/hcp/client.go | 217 +++++++++++++ agent/hcp/config/config.go | 30 ++ agent/hcp/deps.go | 23 ++ agent/hcp/discover/discover.go | 76 +++++ agent/hcp/manager.go | 177 ++++++++++ agent/hcp/manager_test.go | 102 ++++++ agent/hcp/mock_Client.go | 167 ++++++++++ agent/hcp/scada/capabilities.go | 6 + agent/hcp/scada/mock_Provider.go | 302 +++++++++++++++++ agent/hcp/scada/scada.go | 55 ++++ agent/hcp/testing.go | 177 ++++++++++ agent/hcp/testserver/main.go | 45 +++ agent/proxycfg/ingress_gateway.go | 2 +- agent/retry_join.go | 2 + agent/retry_join_test.go | 2 +- agent/setup.go | 7 + agent/testagent.go | 7 + command/agent/agent.go | 87 +++-- command/agent/startup_logger.go | 64 ++++ go.mod | 47 ++- go.sum | 292 +++++++++++++++-- lib/retry/retry.go | 7 + 40 files changed, 2633 insertions(+), 116 deletions(-) create mode 100644 .changelog/14723.txt create mode 100644 agent/hcp/bootstrap/bootstrap.go create mode 100644 agent/hcp/bootstrap/testing.go create mode 100644 agent/hcp/client.go create mode 100644 agent/hcp/config/config.go create mode 100644 agent/hcp/deps.go create mode 100644 agent/hcp/discover/discover.go create mode 100644 agent/hcp/manager.go create mode 100644 agent/hcp/manager_test.go create mode 100644 agent/hcp/mock_Client.go create mode 100644 agent/hcp/scada/capabilities.go create mode 100644 agent/hcp/scada/mock_Provider.go create mode 100644 agent/hcp/scada/scada.go create mode 100644 agent/hcp/testing.go create mode 100644 agent/hcp/testserver/main.go create mode 100644 command/agent/startup_logger.go diff --git a/.changelog/14723.txt b/.changelog/14723.txt new file mode 100644 index 0000000000..e162bd8e15 --- /dev/null +++ b/.changelog/14723.txt @@ -0,0 +1,3 @@ +```release-note:improvement +agent/hcp: add initial HashiCorp Cloud Platform integration +``` diff --git a/agent/agent.go b/agent/agent.go index d30ef3281b..e5101ab886 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -24,9 +24,11 @@ import ( "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-memdb" "github.com/hashicorp/go-multierror" + "github.com/hashicorp/hcp-scada-provider/capability" "github.com/hashicorp/raft" "github.com/hashicorp/serf/serf" "golang.org/x/net/http2" + "golang.org/x/net/http2/h2c" "google.golang.org/grpc" "github.com/hashicorp/consul/acl" @@ -40,6 +42,8 @@ import ( "github.com/hashicorp/consul/agent/consul/servercert" "github.com/hashicorp/consul/agent/dns" external "github.com/hashicorp/consul/agent/grpc-external" + "github.com/hashicorp/consul/agent/hcp/scada" + libscada "github.com/hashicorp/consul/agent/hcp/scada" "github.com/hashicorp/consul/agent/local" "github.com/hashicorp/consul/agent/proxycfg" proxycfgglue "github.com/hashicorp/consul/agent/proxycfg-glue" @@ -382,6 +386,10 @@ type Agent struct { // xdsServer serves the XDS protocol for configuring Envoy proxies. xdsServer *xds.Server + // scadaProvider is set when HashiCorp Cloud Platform integration is configured and exposes the agent's API over + // an encrypted session to HCP + scadaProvider scada.Provider + // enterpriseAgent embeds fields that we only access in consul-enterprise builds enterpriseAgent } @@ -428,6 +436,7 @@ func New(bd BaseDeps) (*Agent, error) { config: bd.RuntimeConfig, cache: bd.Cache, routineManager: routine.NewManager(bd.Logger), + scadaProvider: bd.HCP.Provider, } // TODO: create rpcClientHealth in BaseDeps once NetRPC is available without Agent @@ -769,6 +778,17 @@ func (a *Agent) Start(ctx context.Context) error { }() } + if a.scadaProvider != nil { + a.scadaProvider.UpdateMeta(map[string]string{ + "consul_server_id": string(a.config.NodeID), + }) + + if err = a.scadaProvider.Start(); err != nil { + a.baseDeps.Logger.Error("scada provider failed to start, some HashiCorp Cloud Platform functionality has been disabled", + "error", err, "resource_id", a.config.Cloud.ResourceID) + } + } + return nil } @@ -954,6 +974,12 @@ func (a *Agent) startListeners(addrs []net.Addr) ([]net.Listener, error) { } l = &tcpKeepAliveListener{l.(*net.TCPListener)} + case *capability.Addr: + l, err = a.scadaProvider.Listen(x.Capability()) + if err != nil { + return nil, err + } + default: closeAll() return nil, fmt.Errorf("unsupported address type %T", addr) @@ -1011,6 +1037,11 @@ func (a *Agent) listenHTTP() ([]apiServer, error) { MaxHeaderBytes: a.config.HTTPMaxHeaderBytes, } + if libscada.IsCapability(l.Addr()) { + // wrap in http2 server handler + httpServer.Handler = h2c.NewHandler(srv.handler(a.config.EnableDebug), &http2.Server{}) + } + // Load the connlimit helper into the server connLimitFn := a.httpConnLimiter.HTTPConnStateFuncWithDefault429Handler(10 * time.Millisecond) @@ -1027,7 +1058,12 @@ func (a *Agent) listenHTTP() ([]apiServer, error) { return nil } - if err := start("http", a.config.HTTPAddrs); err != nil { + httpAddrs := a.config.HTTPAddrs + if a.config.IsCloudEnabled() { + httpAddrs = append(httpAddrs, scada.CAPCoreAPI) + } + + if err := start("http", httpAddrs); err != nil { closeListeners(ln) return nil, err } @@ -1582,6 +1618,11 @@ func (a *Agent) ShutdownAgent() error { a.rpcClientHealth.Close() + // Shutdown SCADA provider + if a.scadaProvider != nil { + a.scadaProvider.Stop() + } + var err error if a.delegate != nil { err = a.delegate.Shutdown() diff --git a/agent/agent_test.go b/agent/agent_test.go index 65593710eb..f54275eb73 100644 --- a/agent/agent_test.go +++ b/agent/agent_test.go @@ -28,10 +28,14 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/google/tcpproxy" + "github.com/hashicorp/consul/agent/hcp" + "github.com/hashicorp/consul/agent/hcp/scada" "github.com/hashicorp/go-hclog" + "github.com/hashicorp/hcp-scada-provider/capability" "github.com/hashicorp/serf/coordinate" "github.com/hashicorp/serf/serf" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "golang.org/x/sync/errgroup" "google.golang.org/grpc" @@ -6049,6 +6053,67 @@ peering { }) } +func TestAgent_startListeners_scada(t *testing.T) { + t.Parallel() + pvd := scada.NewMockProvider(t) + c := capability.NewAddr("testcap") + pvd.EXPECT().Listen(c.Capability()).Return(nil, nil).Once() + bd := BaseDeps{ + Deps: consul.Deps{ + Logger: hclog.NewInterceptLogger(nil), + Tokens: new(token.Store), + GRPCConnPool: &fakeGRPCConnPool{}, + HCP: hcp.Deps{ + Provider: pvd, + }, + }, + RuntimeConfig: &config.RuntimeConfig{}, + Cache: cache.New(cache.Options{}), + } + + bd, err := initEnterpriseBaseDeps(bd, nil) + require.NoError(t, err) + + agent, err := New(bd) + require.NoError(t, err) + + _, err = agent.startListeners([]net.Addr{c}) + require.NoError(t, err) +} + +func TestAgent_scadaProvider(t *testing.T) { + t.Parallel() + + pvd := scada.NewMockProvider(t) + + // this listener is used when mocking out the scada provider + l, err := net.Listen("tcp4", fmt.Sprintf("127.0.0.1:%d", freeport.GetOne(t))) + require.NoError(t, err) + defer require.NoError(t, l.Close()) + + pvd.EXPECT().UpdateMeta(mock.Anything).Once() + pvd.EXPECT().Start().Return(nil).Once() + pvd.EXPECT().Listen(scada.CAPCoreAPI.Capability()).Return(l, nil).Once() + pvd.EXPECT().Stop().Return(nil).Once() + pvd.EXPECT().SessionStatus().Return("test").Once() + a := TestAgent{ + OverrideDeps: func(deps *BaseDeps) { + deps.HCP.Provider = pvd + }, + Overrides: ` +cloud { + resource_id = "organization/0b9de9a3-8403-4ca6-aba8-fca752f42100/project/0b9de9a3-8403-4ca6-aba8-fca752f42100/consul.cluster/0b9de9a3-8403-4ca6-aba8-fca752f42100" + client_id = "test" + client_secret = "test" +}`, + } + defer a.Shutdown() + require.NoError(t, a.Start(t)) + + _, err = api.NewClient(&api.Config{Address: l.Addr().String()}) + require.NoError(t, err) +} + func getExpectedCaPoolByFile(t *testing.T) *x509.CertPool { pool := x509.NewCertPool() data, err := ioutil.ReadFile("../test/ca/root.cer") diff --git a/agent/config/builder.go b/agent/config/builder.go index 25a313fc6c..1650df0ca8 100644 --- a/agent/config/builder.go +++ b/agent/config/builder.go @@ -19,6 +19,7 @@ import ( "time" "github.com/armon/go-metrics/prometheus" + hcpconfig "github.com/hashicorp/consul/agent/hcp/config" "github.com/hashicorp/go-bexpr" "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-multierror" @@ -959,6 +960,7 @@ func (b *builder) build() (rt RuntimeConfig, err error) { AutoEncryptIPSAN: autoEncryptIPSAN, AutoEncryptAllowTLS: autoEncryptAllowTLS, AutoConfig: autoConfig, + Cloud: b.cloudConfigVal(c.Cloud), ConnectEnabled: connectEnabled, ConnectCAProvider: connectCAProvider, ConnectCAConfig: connectCAConfig, @@ -2446,6 +2448,20 @@ func validateAutoConfigAuthorizer(rt RuntimeConfig) error { return nil } +func (b *builder) cloudConfigVal(v *CloudConfigRaw) (val hcpconfig.CloudConfig) { + if v == nil { + return val + } + + val.ResourceID = stringVal(v.ResourceID) + val.ClientID = stringVal(v.ClientID) + val.ClientSecret = stringVal(v.ClientSecret) + val.AuthURL = stringVal(v.AuthURL) + val.Hostname = stringVal(v.Hostname) + + return val +} + // decodeBytes returns the encryption key decoded. func decodeBytes(key string) ([]byte, error) { return base64.StdEncoding.DecodeString(key) diff --git a/agent/config/config.go b/agent/config/config.go index de82d98769..af75b89b2d 100644 --- a/agent/config/config.go +++ b/agent/config/config.go @@ -153,6 +153,7 @@ type Config struct { CheckUpdateInterval *string `mapstructure:"check_update_interval"` Checks []CheckDefinition `mapstructure:"checks"` ClientAddr *string `mapstructure:"client_addr"` + Cloud *CloudConfigRaw `mapstructure:"cloud"` ConfigEntries ConfigEntries `mapstructure:"config_entries"` AutoEncrypt AutoEncrypt `mapstructure:"auto_encrypt"` Connect Connect `mapstructure:"connect"` @@ -859,6 +860,14 @@ type RPC struct { EnableStreaming *bool `mapstructure:"enable_streaming"` } +type CloudConfigRaw struct { + ResourceID *string `mapstructure:"resource_id"` + ClientID *string `mapstructure:"client_id"` + ClientSecret *string `mapstructure:"client_secret"` + Hostname *string `mapstructure:"hostname"` + AuthURL *string `mapstructure:"auth_url"` +} + type TLSProtocolConfig struct { CAFile *string `mapstructure:"ca_file"` CAPath *string `mapstructure:"ca_path"` diff --git a/agent/config/runtime.go b/agent/config/runtime.go index ee82ea477e..6e8651779d 100644 --- a/agent/config/runtime.go +++ b/agent/config/runtime.go @@ -13,6 +13,7 @@ import ( "github.com/hashicorp/consul/agent/cache" "github.com/hashicorp/consul/agent/consul" "github.com/hashicorp/consul/agent/dns" + hcpconfig "github.com/hashicorp/consul/agent/hcp/config" "github.com/hashicorp/consul/agent/structs" "github.com/hashicorp/consul/agent/token" "github.com/hashicorp/consul/api" @@ -157,6 +158,11 @@ type RuntimeConfig struct { // hcl: autopilot { upgrade_version_tag = string } AutopilotUpgradeVersionTag string + // Cloud contains configuration for agents to connect to HCP. + // + // hcl: cloud { ... } + Cloud hcpconfig.CloudConfig + // DNSAllowStale is used to enable lookups with stale // data. This gives horizontal read scalability since // any Consul server can service the query instead of @@ -1679,6 +1685,11 @@ func (c *RuntimeConfig) Sanitized() map[string]interface{} { return sanitize("rt", reflect.ValueOf(c)).Interface().(map[string]interface{}) } +// IsCloudEnabled returns true if a cloud.resource_id is set and the server mode is enabled +func (c *RuntimeConfig) IsCloudEnabled() bool { + return c.ServerMode && c.Cloud.ResourceID != "" +} + // isSecret determines whether a field name represents a field which // may contain a secret. func isSecret(name string) bool { diff --git a/agent/config/runtime_test.go b/agent/config/runtime_test.go index 3cff4c198a..30aa3f0ac9 100644 --- a/agent/config/runtime_test.go +++ b/agent/config/runtime_test.go @@ -19,6 +19,7 @@ import ( "github.com/armon/go-metrics/prometheus" "github.com/google/go-cmp/cmp/cmpopts" + hcpconfig "github.com/hashicorp/consul/agent/hcp/config" "github.com/stretchr/testify/require" "github.com/hashicorp/consul/acl" @@ -5989,44 +5990,51 @@ func TestLoad_FullConfig(t *testing.T) { }, ConnectMeshGatewayWANFederationEnabled: false, ConnectServerlessPluginEnabled: true, - DNSAddrs: []net.Addr{tcpAddr("93.95.95.81:7001"), udpAddr("93.95.95.81:7001")}, - DNSARecordLimit: 29907, - DNSAllowStale: true, - DNSDisableCompression: true, - DNSDomain: "7W1xXSqd", - DNSAltDomain: "1789hsd", - DNSEnableTruncate: true, - DNSMaxStale: 29685 * time.Second, - DNSNodeTTL: 7084 * time.Second, - DNSOnlyPassing: true, - DNSPort: 7001, - DNSRecursorStrategy: "sequential", - DNSRecursorTimeout: 4427 * time.Second, - DNSRecursors: []string{"63.38.39.58", "92.49.18.18"}, - DNSSOA: RuntimeSOAConfig{Refresh: 3600, Retry: 600, Expire: 86400, Minttl: 0}, - DNSServiceTTL: map[string]time.Duration{"*": 32030 * time.Second}, - DNSUDPAnswerLimit: 29909, - DNSNodeMetaTXT: true, - DNSUseCache: true, - DNSCacheMaxAge: 5 * time.Minute, - DataDir: dataDir, - Datacenter: "rzo029wg", - DefaultQueryTime: 16743 * time.Second, - DisableAnonymousSignature: true, - DisableCoordinates: true, - DisableHostNodeID: true, - DisableHTTPUnprintableCharFilter: true, - DisableKeyringFile: true, - DisableRemoteExec: true, - DisableUpdateCheck: true, - DiscardCheckOutput: true, - DiscoveryMaxStale: 5 * time.Second, - EnableAgentTLSForChecks: true, - EnableCentralServiceConfig: false, - EnableDebug: true, - EnableRemoteScriptChecks: true, - EnableLocalScriptChecks: true, - EncryptKey: "A4wELWqH", + Cloud: hcpconfig.CloudConfig{ + ResourceID: "N43DsscE", + ClientID: "6WvsDZCP", + ClientSecret: "lCSMHOpB", + Hostname: "DH4bh7aC", + AuthURL: "332nCdR2", + }, + DNSAddrs: []net.Addr{tcpAddr("93.95.95.81:7001"), udpAddr("93.95.95.81:7001")}, + DNSARecordLimit: 29907, + DNSAllowStale: true, + DNSDisableCompression: true, + DNSDomain: "7W1xXSqd", + DNSAltDomain: "1789hsd", + DNSEnableTruncate: true, + DNSMaxStale: 29685 * time.Second, + DNSNodeTTL: 7084 * time.Second, + DNSOnlyPassing: true, + DNSPort: 7001, + DNSRecursorStrategy: "sequential", + DNSRecursorTimeout: 4427 * time.Second, + DNSRecursors: []string{"63.38.39.58", "92.49.18.18"}, + DNSSOA: RuntimeSOAConfig{Refresh: 3600, Retry: 600, Expire: 86400, Minttl: 0}, + DNSServiceTTL: map[string]time.Duration{"*": 32030 * time.Second}, + DNSUDPAnswerLimit: 29909, + DNSNodeMetaTXT: true, + DNSUseCache: true, + DNSCacheMaxAge: 5 * time.Minute, + DataDir: dataDir, + Datacenter: "rzo029wg", + DefaultQueryTime: 16743 * time.Second, + DisableAnonymousSignature: true, + DisableCoordinates: true, + DisableHostNodeID: true, + DisableHTTPUnprintableCharFilter: true, + DisableKeyringFile: true, + DisableRemoteExec: true, + DisableUpdateCheck: true, + DiscardCheckOutput: true, + DiscoveryMaxStale: 5 * time.Second, + EnableAgentTLSForChecks: true, + EnableCentralServiceConfig: false, + EnableDebug: true, + EnableRemoteScriptChecks: true, + EnableLocalScriptChecks: true, + EncryptKey: "A4wELWqH", StaticRuntimeConfig: StaticRuntimeConfig{ EncryptVerifyIncoming: true, EncryptVerifyOutgoing: true, @@ -6771,6 +6779,11 @@ func TestRuntimeConfig_Sanitize(t *testing.T) { EntryFetchMaxBurst: 42, EntryFetchRate: 0.334, }, + Cloud: hcpconfig.CloudConfig{ + ResourceID: "cluster1", + ClientID: "id", + ClientSecret: "secret", + }, ConsulCoordinateUpdatePeriod: 15 * time.Second, RaftProtocol: 3, RetryJoinLAN: []string{ diff --git a/agent/config/testdata/TestRuntimeConfig_Sanitize.golden b/agent/config/testdata/TestRuntimeConfig_Sanitize.golden index b628a18c86..3a29fb0091 100644 --- a/agent/config/testdata/TestRuntimeConfig_Sanitize.golden +++ b/agent/config/testdata/TestRuntimeConfig_Sanitize.golden @@ -124,6 +124,13 @@ } ], "ClientAddrs": [], + "Cloud": { + "AuthURL": "", + "ClientID": "id", + "ClientSecret": "hidden", + "Hostname": "", + "ResourceID": "cluster1" + }, "ConfigEntryBootstrap": [], "ConnectCAConfig": {}, "ConnectCAProvider": "", diff --git a/agent/config/testdata/full-config.hcl b/agent/config/testdata/full-config.hcl index 09e9aabd58..cab2074e7c 100644 --- a/agent/config/testdata/full-config.hcl +++ b/agent/config/testdata/full-config.hcl @@ -201,6 +201,13 @@ auto_encrypt = { ip_san = ["192.168.4.139", "192.168.4.140"] allow_tls = true } +cloud { + resource_id = "N43DsscE" + client_id = "6WvsDZCP" + client_secret = "lCSMHOpB" + hostname = "DH4bh7aC" + auth_url = "332nCdR2" +} connect { ca_provider = "consul" ca_config { diff --git a/agent/config/testdata/full-config.json b/agent/config/testdata/full-config.json index 946b27e73c..f95363f8e4 100644 --- a/agent/config/testdata/full-config.json +++ b/agent/config/testdata/full-config.json @@ -203,6 +203,13 @@ "ip_san": ["192.168.4.139", "192.168.4.140"], "allow_tls": true }, + "cloud": { + "resource_id": "N43DsscE", + "client_id": "6WvsDZCP", + "client_secret": "lCSMHOpB", + "hostname": "DH4bh7aC", + "auth_url": "332nCdR2" + }, "connect": { "ca_provider": "consul", "ca_config": { diff --git a/agent/consul/client_serf.go b/agent/consul/client_serf.go index 7d993c4ac9..50a0844485 100644 --- a/agent/consul/client_serf.go +++ b/agent/consul/client_serf.go @@ -37,11 +37,11 @@ func (c *Client) setupSerf(conf *serf.Config, ch chan serf.Event, path string) ( serfLogger := c.logger. NamedIntercept(logging.Serf). NamedIntercept(logging.LAN). - StandardLoggerIntercept(&hclog.StandardLoggerOptions{InferLevels: true}) + StandardLogger(&hclog.StandardLoggerOptions{InferLevels: true}) memberlistLogger := c.logger. NamedIntercept(logging.Memberlist). NamedIntercept(logging.LAN). - StandardLoggerIntercept(&hclog.StandardLoggerOptions{InferLevels: true}) + StandardLogger(&hclog.StandardLoggerOptions{InferLevels: true}) conf.MemberlistConfig.Logger = memberlistLogger conf.Logger = serfLogger diff --git a/agent/consul/leader_test.go b/agent/consul/leader_test.go index 3ba328672e..35bc924b7a 100644 --- a/agent/consul/leader_test.go +++ b/agent/consul/leader_test.go @@ -2,6 +2,7 @@ package consul import ( "bufio" + "encoding/json" "fmt" "io" "os" @@ -1457,7 +1458,7 @@ func TestLeader_ConfigEntryBootstrap_Fail(t *testing.T) { }, }, }, - expectMessage: `Failed to apply configuration entry "service-splitter" / "web": discovery chain "web" uses a protocol "tcp" that does not permit advanced routing or splitting behavior"`, + expectMessage: `Failed to apply configuration entry "service-splitter" / "web": discovery chain "web" uses a protocol "tcp" that does not permit advanced routing or splitting behavior`, }, { name: "service-intentions without migration", @@ -1497,7 +1498,7 @@ func TestLeader_ConfigEntryBootstrap_Fail(t *testing.T) { serverCB: func(c *Config) { c.ConnectEnabled = false }, - expectMessage: `Refusing to apply configuration entry "service-intentions" / "web" because Connect must be enabled to bootstrap intentions"`, + expectMessage: `Refusing to apply configuration entry "service-intentions" / "web" because Connect must be enabled to bootstrap intentions`, }, } @@ -1516,9 +1517,11 @@ func TestLeader_ConfigEntryBootstrap_Fail(t *testing.T) { scan := bufio.NewScanner(pr) for scan.Scan() { line := scan.Text() + lineJson := map[string]interface{}{} + json.Unmarshal([]byte(line), &lineJson) if strings.Contains(line, "failed to establish leadership") { - applyErrorLine = line + applyErrorLine = lineJson["error"].(string) ch <- "" return } @@ -1543,9 +1546,10 @@ func TestLeader_ConfigEntryBootstrap_Fail(t *testing.T) { } logger := hclog.NewInterceptLogger(&hclog.LoggerOptions{ - Name: config.NodeName, - Level: testutil.TestLogLevel, - Output: io.MultiWriter(pw, testutil.NewLogBuffer(t)), + Name: config.NodeName, + Level: testutil.TestLogLevel, + Output: io.MultiWriter(pw, testutil.NewLogBuffer(t)), + JSONFormat: true, }) deps := newDefaultDeps(t, config) diff --git a/agent/consul/options.go b/agent/consul/options.go index afc69b51db..e3fca37e66 100644 --- a/agent/consul/options.go +++ b/agent/consul/options.go @@ -1,13 +1,14 @@ package consul import ( - "github.com/hashicorp/go-hclog" "google.golang.org/grpc" "github.com/hashicorp/consul-net-rpc/net/rpc" + "github.com/hashicorp/go-hclog" "github.com/hashicorp/consul/agent/consul/stream" "github.com/hashicorp/consul/agent/grpc-external/limiter" + "github.com/hashicorp/consul/agent/hcp" "github.com/hashicorp/consul/agent/pool" "github.com/hashicorp/consul/agent/router" "github.com/hashicorp/consul/agent/rpc/middleware" @@ -31,6 +32,10 @@ type Deps struct { GetNetRPCInterceptorFunc func(recorder *middleware.RequestRecorder) rpc.ServerServiceCallInterceptor // NewRequestRecorderFunc provides a middleware.RequestRecorder for the server to use; it cannot be nil NewRequestRecorderFunc func(logger hclog.Logger, isLeader func() bool, localDC string) *middleware.RequestRecorder + + // HCP contains the dependencies required when integrating with the HashiCorp Cloud Platform + HCP hcp.Deps + EnterpriseDeps } diff --git a/agent/consul/server.go b/agent/consul/server.go index 991a4535ba..d6e412fe2f 100644 --- a/agent/consul/server.go +++ b/agent/consul/server.go @@ -2,6 +2,7 @@ package consul import ( "context" + "crypto/x509" "errors" "fmt" "io" @@ -17,6 +18,7 @@ import ( "time" "github.com/armon/go-metrics" + "github.com/hashicorp/consul/agent/hcp" connlimit "github.com/hashicorp/go-connlimit" "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-memdb" @@ -60,6 +62,7 @@ import ( "github.com/hashicorp/consul/proto/pbsubscribe" "github.com/hashicorp/consul/tlsutil" "github.com/hashicorp/consul/types" + cslversion "github.com/hashicorp/consul/version" ) // NOTE The "consul.client.rpc" and "consul.client.rpc.exceeded" counters are defined in consul/client.go @@ -379,6 +382,9 @@ type Server struct { // server is able to handle. xdsCapacityController *xdscapacity.Controller + // hcpManager handles pushing server status updates to the HashiCorp Cloud Platform when enabled + hcpManager *hcp.Manager + // embedded struct to hold all the enterprise specific data EnterpriseServer } @@ -448,6 +454,12 @@ func NewServer(config *Config, flat Deps, externalGRPCServer *grpc.Server) (*Ser publisher: flat.EventPublisher, } + s.hcpManager = hcp.NewManager(hcp.ManagerConfig{ + Client: flat.HCP.Client, + StatusFn: s.hcpServerStatus(flat), + Logger: logger.Named("hcp_manager"), + }) + var recorder *middleware.RequestRecorder if flat.NewRequestRecorderFunc != nil { recorder = flat.NewRequestRecorderFunc(serverLogger, s.IsLeader, s.config.Datacenter) @@ -789,6 +801,9 @@ func NewServer(config *Config, flat Deps, externalGRPCServer *grpc.Server) (*Ser // Start the metrics handlers. go s.updateMetrics() + // Now we are setup, configure the HCP manager + go s.hcpManager.Run(&lib.StopChannelContext{StopCh: shutdownCh}) + return s, nil } @@ -1712,6 +1727,9 @@ func (s *Server) trackLeaderChanges() { s.grpcLeaderForwarder.UpdateLeaderAddr(s.config.Datacenter, string(leaderObs.LeaderAddr)) s.peeringBackend.SetLeaderAddress(string(leaderObs.LeaderAddr)) + + // Trigger sending an update to HCP status + s.hcpManager.SendUpdate() case <-s.shutdownCh: s.raft.DeregisterObserver(observer) return @@ -1719,6 +1737,61 @@ func (s *Server) trackLeaderChanges() { } } +// hcpServerStatus is the callback used by the HCP manager to emit status updates to the HashiCorp Cloud Platform when +// enabled. +func (s *Server) hcpServerStatus(deps Deps) hcp.StatusCallback { + return func(ctx context.Context) (status hcp.ServerStatus, err error) { + status.Name = s.config.NodeName + status.ID = string(s.config.NodeID) + status.Version = cslversion.GetHumanVersion() + status.LanAddress = s.config.RPCAdvertise.IP.String() + status.GossipPort = s.config.SerfLANConfig.MemberlistConfig.AdvertisePort + status.RPCPort = s.config.RPCAddr.Port + + tlsCert := s.tlsConfigurator.Cert() + if tlsCert != nil { + status.TLS.Enabled = true + leaf := tlsCert.Leaf + if leaf == nil { + // Parse the leaf cert + leaf, err = x509.ParseCertificate(tlsCert.Certificate[0]) + if err != nil { + // Shouldn't be possible + return + } + } + status.TLS.CertName = leaf.Subject.CommonName + status.TLS.CertSerial = leaf.SerialNumber.String() + status.TLS.CertExpiry = leaf.NotAfter + status.TLS.VerifyIncoming = s.tlsConfigurator.VerifyIncomingRPC() + status.TLS.VerifyOutgoing = s.tlsConfigurator.Base().InternalRPC.VerifyOutgoing + status.TLS.VerifyServerHostname = s.tlsConfigurator.VerifyServerHostname() + } + + status.Raft.IsLeader = s.raft.State() == raft.Leader + _, leaderID := s.raft.LeaderWithID() + status.Raft.KnownLeader = leaderID != "" + status.Raft.AppliedIndex = s.raft.AppliedIndex() + if !status.Raft.IsLeader { + status.Raft.TimeSinceLastContact = time.Since(s.raft.LastContact()) + } + + apState := s.autopilot.GetState() + status.Autopilot.Healthy = apState.Healthy + status.Autopilot.FailureTolerance = apState.FailureTolerance + status.Autopilot.NumServers = len(apState.Servers) + status.Autopilot.NumVoters = len(apState.Voters) + status.Autopilot.MinQuorum = int(s.getAutopilotConfigOrDefault().MinQuorum) + + status.ScadaStatus = "unknown" + if deps.HCP.Provider != nil { + status.ScadaStatus = deps.HCP.Provider.SessionStatus() + } + + return status, nil + } +} + // peersInfoContent is used to help operators understand what happened to the // peers.json file. This is written to a file called peers.info in the same // location. diff --git a/agent/consul/server_serf.go b/agent/consul/server_serf.go index 80f44aedc2..a515589303 100644 --- a/agent/consul/server_serf.go +++ b/agent/consul/server_serf.go @@ -153,11 +153,11 @@ func (s *Server) setupSerfConfig(opts setupSerfOptions) (*serf.Config, error) { serfLogger := s.logger. NamedIntercept(logging.Serf). NamedIntercept(subLoggerName). - StandardLoggerIntercept(&hclog.StandardLoggerOptions{InferLevels: true}) + StandardLogger(&hclog.StandardLoggerOptions{InferLevels: true}) memberlistLogger := s.logger. NamedIntercept(logging.Memberlist). NamedIntercept(subLoggerName). - StandardLoggerIntercept(&hclog.StandardLoggerOptions{InferLevels: true}) + StandardLogger(&hclog.StandardLoggerOptions{InferLevels: true}) conf.MemberlistConfig.Logger = memberlistLogger conf.Logger = serfLogger diff --git a/agent/consul/server_test.go b/agent/consul/server_test.go index 5a3a2dea16..287dca871b 100644 --- a/agent/consul/server_test.go +++ b/agent/consul/server_test.go @@ -1,6 +1,7 @@ package consul import ( + "context" "crypto/tls" "crypto/x509" "fmt" @@ -15,10 +16,12 @@ import ( "github.com/armon/go-metrics" "github.com/google/tcpproxy" + "github.com/hashicorp/consul/agent/hcp" "github.com/hashicorp/go-hclog" - "github.com/hashicorp/go-uuid" + uuid "github.com/hashicorp/go-uuid" "github.com/hashicorp/memberlist" "github.com/hashicorp/raft" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "golang.org/x/time/rate" "google.golang.org/grpc" @@ -2012,3 +2015,27 @@ func TestServer_Peering_LeadershipCheck(t *testing.T) { // test corollary by transitivity to future-proof against any setup bugs require.NotEqual(t, s2.config.RPCAddr.String(), peeringLeaderAddr) } + +func TestServer_hcpManager(t *testing.T) { + _, conf1 := testServerConfig(t) + conf1.BootstrapExpect = 1 + conf1.RPCAdvertise = &net.TCPAddr{IP: []byte{127, 0, 0, 2}, Port: conf1.RPCAddr.Port} + hcp1 := hcp.NewMockClient(t) + hcp1.EXPECT().PushServerStatus(mock.Anything, mock.MatchedBy(func(status *hcp.ServerStatus) bool { + return status.ID == string(conf1.NodeID) + })).Run(func(ctx context.Context, status *hcp.ServerStatus) { + require.Equal(t, status.LanAddress, "127.0.0.2") + }).Call.Return(nil) + + deps1 := newDefaultDeps(t, conf1) + deps1.HCP.Client = hcp1 + s1, err := newServerWithDeps(t, conf1, deps1) + if err != nil { + t.Fatalf("err: %v", err) + } + defer s1.Shutdown() + require.NotNil(t, s1.hcpManager) + waitForLeaderEstablishment(t, s1) + hcp1.AssertExpectations(t) + +} diff --git a/agent/hcp/bootstrap/bootstrap.go b/agent/hcp/bootstrap/bootstrap.go new file mode 100644 index 0000000000..3de7de4350 --- /dev/null +++ b/agent/hcp/bootstrap/bootstrap.go @@ -0,0 +1,305 @@ +// Package bootstrap handles bootstrapping an agent's config from HCP. It must be a +// separate package from other HCP components because it has a dependency on +// agent/config while other components need to be imported and run within the +// server process in agent/consul and that would create a dependency cycle. +package bootstrap + +import ( + "bufio" + "context" + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strings" + "time" + + "github.com/hashicorp/consul/agent/config" + "github.com/hashicorp/consul/agent/hcp" + "github.com/hashicorp/consul/lib" + "github.com/hashicorp/consul/lib/retry" +) + +const ( + caFileName = "server-tls-cas.pem" + certFileName = "server-tls-cert.pem" + keyFileName = "server-tls-key.pem" + configFileName = "server-config.json" + subDir = "hcp-config" +) + +type ConfigLoader func(source config.Source) (config.LoadResult, error) + +// UI is a shim to allow the agent command to pass in it's mitchelh/cli.UI so we +// can output useful messages to the user during bootstrapping. For example if +// we have to retry several times to bootstrap we don't want the agent to just +// stall with no output which is the case if we just returned all intermediate +// warnings or errors. +type UI interface { + Output(string) + Warn(string) + Info(string) + Error(string) +} + +// MaybeBootstrap will use the passed ConfigLoader to read the existing +// configuration, and if required attempt to bootstrap from HCP. It will retry +// until successful or a terminal error condition is found (e.g. permission +// denied). It must be passed a (CLI) UI implementation so it can deliver progress +// updates to the user, for example if it is waiting to retry for a long period. +func MaybeBootstrap(ctx context.Context, loader ConfigLoader, ui UI) (bool, ConfigLoader, error) { + loader = wrapConfigLoader(loader) + res, err := loader(nil) + if err != nil { + return false, nil, err + } + + // Check to see if this is a server and HCP is configured + + if !res.RuntimeConfig.IsCloudEnabled() { + // Not a server, let agent continue unmodified + return false, loader, nil + } + + ui.Output("Bootstrapping configuration from HCP") + + // See if we have existing config on disk + cfgJSON, ok := loadPersistedBootstrapConfig(res.RuntimeConfig, ui) + + if !ok { + // Fetch from HCP + ui.Info("Fetching configuration from HCP") + cfgJSON, err = doHCPBootstrap(ctx, res.RuntimeConfig, ui) + if err != nil { + return false, nil, fmt.Errorf("failed to bootstrap from HCP: %w", err) + } + ui.Info("Configuration fetched from HCP and saved on local disk") + } else { + ui.Info("Loaded configuration from local disk") + } + + // Create a new loader func to return + newLoader := func(source config.Source) (config.LoadResult, error) { + // Don't allow any further attempts to provide a DefaultSource. This should + // only ever be needed later in client agent AutoConfig code but that should + // be mutually exclusive from this bootstrapping mechanism since this is + // only for servers. If we ever try to change that, this clear failure + // should alert future developers that the assumptions are changing rather + // than quietly not applying the config they expect! + if source != nil { + return config.LoadResult{}, + fmt.Errorf("non-nil config source provided to a loader after HCP bootstrap already provided a DefaultSource") + } + // Otherwise, just call to the loader we were passed with our own additional + // JSON as the source. + s := config.FileSource{ + Name: "HCP Bootstrap", + Format: "json", + Data: cfgJSON, + } + return loader(s) + } + + return true, newLoader, nil +} + +func wrapConfigLoader(loader ConfigLoader) ConfigLoader { + return func(source config.Source) (config.LoadResult, error) { + res, err := loader(source) + if err != nil { + return res, err + } + + if res.RuntimeConfig.Cloud.ResourceID == "" { + res.RuntimeConfig.Cloud.ResourceID = os.Getenv("HCP_RESOURCE_ID") + } + return res, nil + } +} + +func doHCPBootstrap(ctx context.Context, rc *config.RuntimeConfig, ui UI) (string, error) { + w := retry.Waiter{ + MinWait: 1 * time.Second, + MaxWait: 5 * time.Minute, + Jitter: retry.NewJitter(50), + } + + var bsCfg *hcp.BootstrapConfig + + client, err := hcp.NewClient(rc.Cloud) + if err != nil { + return "", err + } + + for { + // Note we don't want to shadow `ctx` here since we need that for the Wait + // below. + reqCtx, cancel := context.WithTimeout(ctx, 5*time.Second) + defer cancel() + + resp, err := client.FetchBootstrap(reqCtx) + if err != nil { + ui.Error(fmt.Sprintf("failed to fetch bootstrap config from HCP, will retry in %s: %s", + w.NextWait().Round(time.Second), err)) + if err := w.Wait(ctx); err != nil { + return "", err + } + // Finished waiting, restart loop + continue + } + bsCfg = resp + break + } + + dataDir := rc.DataDir + shouldPersist := true + if dataDir == "" { + // Agent in dev mode, we still need somewhere to persist the certs + // temporarily though to be able to start up at all since we don't support + // inline certs right now. Use temp dir + tmp, err := os.MkdirTemp(os.TempDir(), "consul-dev-") + if err != nil { + return "", fmt.Errorf("failed to create temp dir for certificates: %w", err) + } + dataDir = tmp + shouldPersist = false + } + + // Persist the TLS cert files from the response since we need to refer to them + // as disk files either way. + if err := persistTLSCerts(dataDir, bsCfg); err != nil { + return "", fmt.Errorf("failed to persist TLS certificates to dir %q: %w", dataDir, err) + } + // Update the config JSON to include those TLS cert files + cfgJSON, err := injectTLSCerts(dataDir, bsCfg.ConsulConfig) + if err != nil { + return "", fmt.Errorf("failed to inject TLS Certs into bootstrap config: %w", err) + } + + // Persist the final config we need to add for restarts. Assuming this wasn't + // a tmp dir to start with. + if shouldPersist { + if err := persistBootstrapConfig(dataDir, cfgJSON); err != nil { + return "", fmt.Errorf("failed to persist bootstrap config to dir %q: %w", dataDir, err) + } + } + + return cfgJSON, nil +} + +func persistTLSCerts(dataDir string, bsCfg *hcp.BootstrapConfig) error { + dir := filepath.Join(dataDir, subDir) + + if bsCfg.TLSCert == "" || bsCfg.TLSCertKey == "" { + return fmt.Errorf("unexpected bootstrap response from HCP: missing TLS information") + } + + // Create a subdir if it's not already there + if err := lib.EnsurePath(dir, true); err != nil { + return err + } + + // Write out CA cert(s). We write them all to one file because Go's x509 + // machinery will read as many certs as it finds from each PEM file provided + // and add them separaetly to the CertPool for validation + f, err := os.OpenFile(filepath.Join(dir, caFileName), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) + if err != nil { + return err + } + bf := bufio.NewWriter(f) + for _, caPEM := range bsCfg.TLSCAs { + bf.WriteString(caPEM + "\n") + } + if err := bf.Flush(); err != nil { + return err + } + if err := f.Close(); err != nil { + return err + } + + if err := ioutil.WriteFile(filepath.Join(dir, certFileName), []byte(bsCfg.TLSCert), 0600); err != nil { + return err + } + + if err := ioutil.WriteFile(filepath.Join(dir, keyFileName), []byte(bsCfg.TLSCertKey), 0600); err != nil { + return err + } + + return nil +} + +func injectTLSCerts(dataDir string, bootstrapJSON string) (string, error) { + // Parse just to a map for now as we only have to inject to a specific place + // and parsing whole Config struct is complicated... + var cfg map[string]interface{} + + if err := json.Unmarshal([]byte(bootstrapJSON), &cfg); err != nil { + return "", err + } + + // Inject TLS cert files + cfg["ca_file"] = filepath.Join(dataDir, subDir, caFileName) + cfg["cert_file"] = filepath.Join(dataDir, subDir, certFileName) + cfg["key_file"] = filepath.Join(dataDir, subDir, keyFileName) + + jsonBs, err := json.Marshal(cfg) + if err != nil { + return "", err + } + + return string(jsonBs), nil +} + +func persistBootstrapConfig(dataDir, cfgJSON string) error { + // Persist the important bits we got from bootstrapping. The TLS certs are + // already persisted, just need to persist the config we are going to add. + name := filepath.Join(dataDir, subDir, configFileName) + return ioutil.WriteFile(name, []byte(cfgJSON), 0600) +} + +func loadPersistedBootstrapConfig(rc *config.RuntimeConfig, ui UI) (string, bool) { + // Check if the files all exist + files := []string{ + filepath.Join(rc.DataDir, subDir, configFileName), + filepath.Join(rc.DataDir, subDir, caFileName), + filepath.Join(rc.DataDir, subDir, certFileName), + filepath.Join(rc.DataDir, subDir, keyFileName), + } + hasSome := false + for _, name := range files { + if _, err := os.Stat(name); errors.Is(err, os.ErrNotExist) { + // At least one required file doesn't exist, failed loading. This is not + // an error though + if hasSome { + ui.Warn("ignoring incomplete local bootstrap config files") + } + return "", false + } + hasSome = true + } + + name := filepath.Join(rc.DataDir, subDir, configFileName) + jsonBs, err := ioutil.ReadFile(name) + if err != nil { + ui.Warn(fmt.Sprintf("failed to read local bootstrap config file, ignoring local files: %s", err)) + return "", false + } + + // Check this looks non-empty at least + jsonStr := strings.TrimSpace(string(jsonBs)) + // 50 is arbitrary but config containing the right secrets would always be + // bigger than this in JSON format so it is a reasonable test that this wasn't + // empty or just an empty JSON object or something. + if len(jsonStr) < 50 { + ui.Warn("ignoring incomplete local bootstrap config files") + return "", false + } + + // TODO we could parse the certificates and check they are still valid here + // and force a reload if not. We could also attempt to parse config and check + // it's all valid just in case the local config was really old and has + // deprecated fields or something? + return jsonStr, true +} diff --git a/agent/hcp/bootstrap/testing.go b/agent/hcp/bootstrap/testing.go new file mode 100644 index 0000000000..b1a05a7e50 --- /dev/null +++ b/agent/hcp/bootstrap/testing.go @@ -0,0 +1,160 @@ +package bootstrap + +import ( + "crypto/rand" + "crypto/x509" + "encoding/base64" + "encoding/json" + "fmt" + "net" + "net/http" + "strings" + + gnmmod "github.com/hashicorp/hcp-sdk-go/clients/cloud-global-network-manager-service/preview/2022-02-15/models" + "github.com/hashicorp/hcp-sdk-go/resource" + + "github.com/hashicorp/consul/agent/hcp" + "github.com/hashicorp/consul/tlsutil" + "github.com/hashicorp/go-uuid" +) + +// TestEndpoint returns an hcp.TestEndpoint to be used in an hcp.MockHCPServer. +func TestEndpoint() hcp.TestEndpoint { + // Memoize data so it's consistent for the life of the test server + data := make(map[string]gnmmod.HashicorpCloudGlobalNetworkManager20220215AgentBootstrapResponse) + + return hcp.TestEndpoint{ + Methods: []string{"GET"}, + PathSuffix: "agent/bootstrap_config", + Handler: func(r *http.Request, cluster resource.Resource) (interface{}, error) { + return handleBootstrap(data, cluster) + }, + } +} + +func handleBootstrap(data map[string]gnmmod.HashicorpCloudGlobalNetworkManager20220215AgentBootstrapResponse, cluster resource.Resource) (interface{}, error) { + resp, ok := data[cluster.ID] + if !ok { + // Create new response + r, err := generateClusterData(cluster) + if err != nil { + return nil, err + } + data[cluster.ID] = r + resp = r + } + return resp, nil +} + +func generateClusterData(cluster resource.Resource) (gnmmod.HashicorpCloudGlobalNetworkManager20220215AgentBootstrapResponse, error) { + resp := gnmmod.HashicorpCloudGlobalNetworkManager20220215AgentBootstrapResponse{ + Cluster: &gnmmod.HashicorpCloudGlobalNetworkManager20220215Cluster{}, + Bootstrap: &gnmmod.HashicorpCloudGlobalNetworkManager20220215ClusterBootstrap{ + ServerTLS: &gnmmod.HashicorpCloudGlobalNetworkManager20220215ServerTLS{}, + }, + } + + CACert, CAKey, err := tlsutil.GenerateCA(tlsutil.CAOpts{}) + if err != nil { + return resp, err + } + + resp.Bootstrap.ServerTLS.CertificateAuthorities = append(resp.Bootstrap.ServerTLS.CertificateAuthorities, CACert) + signer, err := tlsutil.ParseSigner(CAKey) + if err != nil { + return resp, err + } + + cert, priv, err := tlsutil.GenerateCert(tlsutil.CertOpts{ + Signer: signer, + CA: CACert, + Name: "server.dc1.consul", + Days: 30, + DNSNames: []string{"server.dc1.consul", "localhost"}, + IPAddresses: append([]net.IP{}, net.ParseIP("127.0.0.1")), + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}, + }) + if err != nil { + return resp, err + } + resp.Bootstrap.ServerTLS.Cert = cert + resp.Bootstrap.ServerTLS.PrivateKey = priv + + // Generate Config. We don't use the read config.Config struct because it + // doesn't have `omitempty` which makes the output gross. We only want a tiny + // subset, so we use a map that ends up with the same structure for now. + + // Gossip key + gossipKeyBs := make([]byte, 32) + _, err = rand.Reader.Read(gossipKeyBs) + if err != nil { + return resp, err + } + + retryJoinArgs := map[string]string{ + "provider": "hcp", + "resource_id": cluster.String(), + "client_id": "test_id", + "client_secret": "test_secret", + } + + cfg := map[string]interface{}{ + "encrypt": base64.StdEncoding.EncodeToString(gossipKeyBs), + "encrypt_verify_incoming": true, + "encrypt_verify_outgoing": true, + + // TLS settings (certs will be added by client since we can't put them inline) + "verify_incoming": true, + "verify_outgoing": true, + "verify_server_hostname": true, + "auto_encrypt": map[string]interface{}{ + "allow_tls": true, + }, + + // Enable HTTPS port, disable HTTP + "ports": map[string]interface{}{ + "https": 8501, + "http": -1, + }, + + // RAFT Peers + "bootstrap_expect": 1, + "retry_join": []string{ + mapArgsString(retryJoinArgs), + }, + } + + // ACLs + management, err := uuid.GenerateUUID() + if err != nil { + return resp, err + } + cfg["acl"] = map[string]interface{}{ + "tokens": map[string]interface{}{ + "initial_management": management, + // Also setup the server's own agent token to be the same so it has + // permission to register itself. + "agent": management, + }, + "default_policy": "deny", + "enabled": true, + "enable_token_persistence": true, + } + + // Encode and return a JSON string in the response + jsonBs, err := json.Marshal(cfg) + if err != nil { + return resp, err + } + resp.Bootstrap.ConsulConfig = string(jsonBs) + + return resp, nil +} + +func mapArgsString(m map[string]string) string { + args := make([]string, len(m)) + for k, v := range m { + args = append(args, fmt.Sprintf("%s=%s", k, v)) + } + return strings.Join(args, " ") +} diff --git a/agent/hcp/client.go b/agent/hcp/client.go new file mode 100644 index 0000000000..070814024a --- /dev/null +++ b/agent/hcp/client.go @@ -0,0 +1,217 @@ +package hcp + +import ( + "context" + "fmt" + "strconv" + "time" + + httptransport "github.com/go-openapi/runtime/client" + "github.com/go-openapi/strfmt" + hcpgnm "github.com/hashicorp/hcp-sdk-go/clients/cloud-global-network-manager-service/preview/2022-02-15/client/global_network_manager_service" + gnmmod "github.com/hashicorp/hcp-sdk-go/clients/cloud-global-network-manager-service/preview/2022-02-15/models" + "github.com/hashicorp/hcp-sdk-go/httpclient" + "github.com/hashicorp/hcp-sdk-go/resource" + + "github.com/hashicorp/consul/agent/hcp/config" + "github.com/hashicorp/consul/version" +) + +// Client interface exposes HCP operations that can be invoked by Consul +//go:generate mockery --name Client --with-expecter --inpackage +type Client interface { + FetchBootstrap(ctx context.Context) (*BootstrapConfig, error) + PushServerStatus(ctx context.Context, status *ServerStatus) error + DiscoverServers(ctx context.Context) ([]string, error) +} + +type BootstrapConfig struct { + Name string + BootstrapExpect int + GossipKey string + TLSCert string + TLSCertKey string + TLSCAs []string + ConsulConfig string +} + +type hcpClient struct { + hc *httptransport.Runtime + cfg config.CloudConfig + gnm hcpgnm.ClientService + resource resource.Resource +} + +func NewClient(cfg config.CloudConfig) (Client, error) { + client := &hcpClient{ + cfg: cfg, + } + + var err error + client.resource, err = resource.FromString(cfg.ResourceID) + if err != nil { + return nil, err + } + + client.hc, err = httpClient(cfg) + if err != nil { + return nil, err + } + + client.gnm = hcpgnm.New(client.hc, nil) + return client, nil +} + +func httpClient(c config.CloudConfig) (*httptransport.Runtime, error) { + cfg, err := c.HCPConfig() + if err != nil { + return nil, err + } + + return httpclient.New(httpclient.Config{ + HCPConfig: cfg, + SourceChannel: "consul " + version.GetHumanVersion(), + }) +} + +func (c *hcpClient) FetchBootstrap(ctx context.Context) (*BootstrapConfig, error) { + params := hcpgnm.NewAgentBootstrapConfigParamsWithContext(ctx). + WithID(c.resource.ID). + WithLocationOrganizationID(c.resource.Organization). + WithLocationProjectID(c.resource.Project) + + resp, err := c.gnm.AgentBootstrapConfig(params, nil) + if err != nil { + return nil, err + } + + return bootstrapConfigFromHCP(resp.Payload), nil +} + +func bootstrapConfigFromHCP(res *gnmmod.HashicorpCloudGlobalNetworkManager20220215AgentBootstrapResponse) *BootstrapConfig { + var serverTLS gnmmod.HashicorpCloudGlobalNetworkManager20220215ServerTLS + if res.Bootstrap.ServerTLS != nil { + serverTLS = *res.Bootstrap.ServerTLS + } + + return &BootstrapConfig{ + Name: res.Bootstrap.ID, + BootstrapExpect: int(res.Bootstrap.BootstrapExpect), + GossipKey: res.Bootstrap.GossipKey, + TLSCert: serverTLS.Cert, + TLSCertKey: serverTLS.PrivateKey, + TLSCAs: serverTLS.CertificateAuthorities, + ConsulConfig: res.Bootstrap.ConsulConfig, + } +} + +func (c *hcpClient) PushServerStatus(ctx context.Context, s *ServerStatus) error { + params := hcpgnm.NewAgentPushServerStateParamsWithContext(ctx). + WithID(c.resource.ID). + WithLocationOrganizationID(c.resource.Organization). + WithLocationProjectID(c.resource.Project) + + params.SetBody(&gnmmod.HashicorpCloudGlobalNetworkManager20220215AgentPushServerStateRequest{ + ServerState: serverStatusToHCP(s), + }) + + _, err := c.gnm.AgentPushServerState(params, nil) + return err +} + +type ServerStatus struct { + ID string + Name string + Version string + LanAddress string + GossipPort int + RPCPort int + + Autopilot ServerAutopilot + Raft ServerRaft + TLS ServerTLSInfo + + ScadaStatus string +} + +type ServerAutopilot struct { + FailureTolerance int + Healthy bool + MinQuorum int + NumServers int + NumVoters int +} + +type ServerRaft struct { + IsLeader bool + KnownLeader bool + AppliedIndex uint64 + TimeSinceLastContact time.Duration +} + +type ServerTLSInfo struct { + Enabled bool + CertExpiry time.Time + CertName string + CertSerial string + VerifyIncoming bool + VerifyOutgoing bool + VerifyServerHostname bool +} + +func serverStatusToHCP(s *ServerStatus) *gnmmod.HashicorpCloudGlobalNetworkManager20220215ServerState { + if s == nil { + return nil + } + return &gnmmod.HashicorpCloudGlobalNetworkManager20220215ServerState{ + Autopilot: &gnmmod.HashicorpCloudGlobalNetworkManager20220215AutoPilotInfo{ + FailureTolerance: int32(s.Autopilot.FailureTolerance), + Healthy: s.Autopilot.Healthy, + MinQuorum: int32(s.Autopilot.MinQuorum), + NumServers: int32(s.Autopilot.NumServers), + NumVoters: int32(s.Autopilot.NumVoters), + }, + GossipPort: int32(s.GossipPort), + ID: s.ID, + LanAddress: s.LanAddress, + Name: s.Name, + Raft: &gnmmod.HashicorpCloudGlobalNetworkManager20220215RaftInfo{ + AppliedIndex: strconv.FormatUint(s.Raft.AppliedIndex, 10), + IsLeader: s.Raft.IsLeader, + KnownLeader: s.Raft.KnownLeader, + TimeSinceLastContact: s.Raft.TimeSinceLastContact.String(), + }, + RPCPort: int32(s.RPCPort), + TLS: &gnmmod.HashicorpCloudGlobalNetworkManager20220215TLSInfo{ + CertExpiry: strfmt.DateTime(s.TLS.CertExpiry), + CertName: s.TLS.CertName, + CertSerial: s.TLS.CertSerial, + Enabled: s.TLS.Enabled, + VerifyIncoming: s.TLS.VerifyIncoming, + VerifyOutgoing: s.TLS.VerifyOutgoing, + VerifyServerHostname: s.TLS.VerifyServerHostname, + }, + Version: s.Version, + ScadaStatus: s.ScadaStatus, + } +} + +func (c *hcpClient) DiscoverServers(ctx context.Context) ([]string, error) { + params := hcpgnm.NewAgentDiscoverParamsWithContext(ctx). + WithID(c.resource.ID). + WithLocationOrganizationID(c.resource.Organization). + WithLocationProjectID(c.resource.Project) + + resp, err := c.gnm.AgentDiscover(params, nil) + if err != nil { + return nil, err + } + var servers []string + for _, srv := range resp.Payload.Servers { + if srv != nil { + servers = append(servers, fmt.Sprintf("%s:%d", srv.LanAddress, srv.GossipPort)) + } + } + + return servers, nil +} diff --git a/agent/hcp/config/config.go b/agent/hcp/config/config.go new file mode 100644 index 0000000000..4dc2ffb5f8 --- /dev/null +++ b/agent/hcp/config/config.go @@ -0,0 +1,30 @@ +package config + +import ( + "crypto/tls" + + hcpcfg "github.com/hashicorp/hcp-sdk-go/config" +) + +// CloudConfig defines configuration for connecting to HCP services +type CloudConfig struct { + ResourceID string + ClientID string + ClientSecret string + Hostname string + AuthURL string +} + +func (c *CloudConfig) HCPConfig(opts ...hcpcfg.HCPConfigOption) (hcpcfg.HCPConfig, error) { + if c.ClientID != "" && c.ClientSecret != "" { + opts = append(opts, hcpcfg.WithClientCredentials(c.ClientID, c.ClientSecret)) + } + if c.AuthURL != "" { + opts = append(opts, hcpcfg.WithAuth(c.AuthURL, &tls.Config{})) + } + if c.Hostname != "" { + opts = append(opts, hcpcfg.WithAPI(c.Hostname, &tls.Config{})) + } + opts = append(opts, hcpcfg.FromEnv()) + return hcpcfg.NewHCPConfig(opts...) +} diff --git a/agent/hcp/deps.go b/agent/hcp/deps.go new file mode 100644 index 0000000000..418d02620e --- /dev/null +++ b/agent/hcp/deps.go @@ -0,0 +1,23 @@ +package hcp + +import ( + "github.com/hashicorp/consul/agent/hcp/config" + "github.com/hashicorp/consul/agent/hcp/scada" + "github.com/hashicorp/go-hclog" +) + +// Deps contains the interfaces that the rest of Consul core depends on for HCP integration. +type Deps struct { + Client Client + Provider scada.Provider +} + +func NewDeps(cfg config.CloudConfig, logger hclog.Logger) (d Deps, err error) { + d.Client, err = NewClient(cfg) + if err != nil { + return + } + + d.Provider, err = scada.New(cfg, logger.Named("hcp.scada")) + return +} diff --git a/agent/hcp/discover/discover.go b/agent/hcp/discover/discover.go new file mode 100644 index 0000000000..8707a03a55 --- /dev/null +++ b/agent/hcp/discover/discover.go @@ -0,0 +1,76 @@ +package discover + +import ( + "context" + "fmt" + "log" + "time" + + "github.com/hashicorp/consul/agent/hcp" + "github.com/hashicorp/consul/agent/hcp/config" +) + +type Provider struct { +} + +var ( + defaultTimeout = 5 * time.Second +) + +type providerConfig struct { + config.CloudConfig + + timeout time.Duration +} + +func (p *Provider) Addrs(args map[string]string, l *log.Logger) ([]string, error) { + cfg, err := parseArgs(args) + if err != nil { + return nil, err + } + + client, err := hcp.NewClient(cfg.CloudConfig) + if err != nil { + return nil, err + } + + ctx, cancel := context.WithTimeout(context.Background(), cfg.timeout) + defer cancel() + servers, err := client.DiscoverServers(ctx) + if err != nil { + return nil, err + } + + return servers, nil +} + +func (p *Provider) Help() string { + return "" +} + +func parseArgs(args map[string]string) (cfg providerConfig, err error) { + cfg.timeout = defaultTimeout + + if id, ok := args["resource_id"]; ok { + cfg.ResourceID = id + } else { + err = fmt.Errorf("'resource_id' was not found and is required") + } + + if cid, ok := args["client_id"]; ok { + cfg.ClientID = cid + } + + if csec, ok := args["client_secret"]; ok { + cfg.ClientSecret = csec + } + + if timeoutRaw, ok := args["timeout"]; ok { + timeout, err := time.ParseDuration(timeoutRaw) + if err != nil { + return cfg, err + } + cfg.timeout = timeout + } + return +} diff --git a/agent/hcp/manager.go b/agent/hcp/manager.go new file mode 100644 index 0000000000..9e3624f6af --- /dev/null +++ b/agent/hcp/manager.go @@ -0,0 +1,177 @@ +package hcp + +import ( + "context" + "sync" + "time" + + "github.com/hashicorp/consul/lib" + "github.com/hashicorp/go-hclog" +) + +var ( + defaultManagerMinInterval = 45 * time.Minute + defaultManagerMaxInterval = 75 * time.Minute +) + +type ManagerConfig struct { + Client Client + + StatusFn StatusCallback + MinInterval time.Duration + MaxInterval time.Duration + + Logger hclog.Logger +} + +func (cfg *ManagerConfig) enabled() bool { + return cfg.Client != nil && cfg.StatusFn != nil +} + +func (cfg *ManagerConfig) nextHeartbeat() time.Duration { + min := cfg.MinInterval + if min == 0 { + min = defaultManagerMinInterval + } + + max := cfg.MaxInterval + if max == 0 { + max = defaultManagerMaxInterval + } + if max < min { + max = min + } + return min + lib.RandomStagger(max-min) +} + +type StatusCallback func(context.Context) (ServerStatus, error) + +type Manager struct { + logger hclog.Logger + + cfg ManagerConfig + cfgMu sync.RWMutex + + updateCh chan struct{} + + // testUpdateSent is set by unit tests to signal when the manager's status update has triggered + testUpdateSent chan struct{} +} + +// NewManager returns an initialized Manager with a zero configuration. It won't +// do anything until UpdateConfig is called with a config that provides +// credentials to contact HCP. +func NewManager(cfg ManagerConfig) *Manager { + return &Manager{ + logger: cfg.Logger, + cfg: cfg, + + updateCh: make(chan struct{}, 1), + } +} + +// Run executes the Manager it's designed to be run in its own goroutine for +// the life of a server agent. It should be run even if HCP is not configured +// yet for servers since a config update might configure it later and +// UpdateConfig called. It will effectively do nothing if there are no HCP +// credentials set other than wait for some to be added. +func (m *Manager) Run(ctx context.Context) { + var err error + m.logger.Debug("HCP manager starting") + + // immediately send initial update + select { + case <-ctx.Done(): + return + case <-m.updateCh: // empty the update chan if there is a queued update to prevent repeated update in main loop + err = m.sendUpdate() + default: + err = m.sendUpdate() + } + + // main loop + for { + m.cfgMu.RLock() + cfg := m.cfg + m.cfgMu.RUnlock() + nextUpdate := cfg.nextHeartbeat() + if err != nil { + m.logger.Error("failed to send server status to HCP", "err", err, "next_heartbeat", nextUpdate.String()) + } + + select { + case <-ctx.Done(): + return + + case <-m.updateCh: + err = m.sendUpdate() + + case <-time.After(nextUpdate): + err = m.sendUpdate() + } + } +} + +func (m *Manager) UpdateConfig(cfg ManagerConfig) { + m.cfgMu.Lock() + defer m.cfgMu.Unlock() + old := m.cfg + m.cfg = cfg + if old.enabled() || cfg.enabled() { + // Only log about this if cloud is actually configured or it would be + // confusing. We check both old and new in case we are disabling cloud or + // enabling it or just updating it. + m.logger.Info("updated HCP configuration") + } + + // Send a new status update since we might have just gotten connection details + // for the first time. + m.SendUpdate() +} + +func (m *Manager) SendUpdate() { + m.logger.Debug("HCP triggering status update") + select { + case m.updateCh <- struct{}{}: + // trigger update + default: + // if chan is full then there is already an update triggered that will soon + // be acted on so don't bother blocking. + } +} + +// TODO: we should have retried on failures here with backoff but take into +// account that if a new update is triggered while we are still retrying we +// should not start another retry loop. Something like have a "dirty" flag which +// we mark on first PushUpdate and then a retry timer as well as the interval +// and a "isRetrying" state or something so that we attempt to send update, but +// then fetch fresh info on each attempt to send so if we are already in a retry +// backoff a new push is a no-op. +func (m *Manager) sendUpdate() error { + m.cfgMu.RLock() + cfg := m.cfg + m.cfgMu.RUnlock() + + if !cfg.enabled() { + return nil + } + + if m.testUpdateSent != nil { + defer func() { + select { + case m.testUpdateSent <- struct{}{}: + default: + } + }() + } + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + s, err := cfg.StatusFn(ctx) + if err != nil { + return err + } + + return m.cfg.Client.PushServerStatus(ctx, &s) +} diff --git a/agent/hcp/manager_test.go b/agent/hcp/manager_test.go new file mode 100644 index 0000000000..68a4505e99 --- /dev/null +++ b/agent/hcp/manager_test.go @@ -0,0 +1,102 @@ +package hcp + +import ( + "io/ioutil" + "testing" + "time" + + "github.com/hashicorp/go-hclog" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "golang.org/x/net/context" +) + +func TestManager_Run(t *testing.T) { + client := NewMockClient(t) + statusF := func(ctx context.Context) (ServerStatus, error) { + return ServerStatus{ID: t.Name()}, nil + } + updateCh := make(chan struct{}, 1) + client.EXPECT().PushServerStatus(mock.Anything, &ServerStatus{ID: t.Name()}).Return(nil).Once() + mgr := NewManager(ManagerConfig{ + Client: client, + Logger: hclog.New(&hclog.LoggerOptions{Output: ioutil.Discard}), + StatusFn: statusF, + }) + mgr.testUpdateSent = updateCh + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) + + go mgr.Run(ctx) + select { + case <-updateCh: + case <-time.After(time.Second): + require.Fail(t, "manager did not send update in expected time") + } + + // Make sure after manager has stopped no more statuses are pushed. + cancel() + mgr.SendUpdate() + client.AssertExpectations(t) +} + +func TestManager_SendUpdate(t *testing.T) { + client := NewMockClient(t) + statusF := func(ctx context.Context) (ServerStatus, error) { + return ServerStatus{ID: t.Name()}, nil + } + updateCh := make(chan struct{}, 1) + + // Expect two calls, once during run startup and again when SendUpdate is called + client.EXPECT().PushServerStatus(mock.Anything, &ServerStatus{ID: t.Name()}).Return(nil).Twice() + mgr := NewManager(ManagerConfig{ + Client: client, + Logger: hclog.New(&hclog.LoggerOptions{Output: ioutil.Discard}), + StatusFn: statusF, + }) + mgr.testUpdateSent = updateCh + go mgr.Run(context.Background()) + select { + case <-updateCh: + case <-time.After(time.Second): + require.Fail(t, "manager did not send update in expected time") + } + mgr.SendUpdate() + select { + case <-updateCh: + case <-time.After(time.Second): + require.Fail(t, "manager did not send update in expected time") + } + client.AssertExpectations(t) +} + +func TestManager_SendUpdate_Periodic(t *testing.T) { + client := NewMockClient(t) + statusF := func(ctx context.Context) (ServerStatus, error) { + return ServerStatus{ID: t.Name()}, nil + } + updateCh := make(chan struct{}, 1) + + // Expect two calls, once during run startup and again when SendUpdate is called + client.EXPECT().PushServerStatus(mock.Anything, &ServerStatus{ID: t.Name()}).Return(nil).Twice() + mgr := NewManager(ManagerConfig{ + Client: client, + Logger: hclog.New(&hclog.LoggerOptions{Output: ioutil.Discard}), + StatusFn: statusF, + MaxInterval: time.Second, + MinInterval: 100 * time.Millisecond, + }) + mgr.testUpdateSent = updateCh + go mgr.Run(context.Background()) + select { + case <-updateCh: + case <-time.After(time.Second): + require.Fail(t, "manager did not send update in expected time") + } + select { + case <-updateCh: + case <-time.After(time.Second): + require.Fail(t, "manager did not send update in expected time") + } + client.AssertExpectations(t) +} diff --git a/agent/hcp/mock_Client.go b/agent/hcp/mock_Client.go new file mode 100644 index 0000000000..ec6129f980 --- /dev/null +++ b/agent/hcp/mock_Client.go @@ -0,0 +1,167 @@ +// Code generated by mockery v2.13.1. DO NOT EDIT. + +package hcp + +import ( + context "context" + + mock "github.com/stretchr/testify/mock" +) + +// MockClient is an autogenerated mock type for the Client type +type MockClient struct { + mock.Mock +} + +type MockClient_Expecter struct { + mock *mock.Mock +} + +func (_m *MockClient) EXPECT() *MockClient_Expecter { + return &MockClient_Expecter{mock: &_m.Mock} +} + +// DiscoverServers provides a mock function with given fields: ctx +func (_m *MockClient) DiscoverServers(ctx context.Context) ([]string, error) { + ret := _m.Called(ctx) + + var r0 []string + if rf, ok := ret.Get(0).(func(context.Context) []string); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]string) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockClient_DiscoverServers_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DiscoverServers' +type MockClient_DiscoverServers_Call struct { + *mock.Call +} + +// DiscoverServers is a helper method to define mock.On call +// - ctx context.Context +func (_e *MockClient_Expecter) DiscoverServers(ctx interface{}) *MockClient_DiscoverServers_Call { + return &MockClient_DiscoverServers_Call{Call: _e.mock.On("DiscoverServers", ctx)} +} + +func (_c *MockClient_DiscoverServers_Call) Run(run func(ctx context.Context)) *MockClient_DiscoverServers_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *MockClient_DiscoverServers_Call) Return(_a0 []string, _a1 error) *MockClient_DiscoverServers_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +// FetchBootstrap provides a mock function with given fields: ctx +func (_m *MockClient) FetchBootstrap(ctx context.Context) (*BootstrapConfig, error) { + ret := _m.Called(ctx) + + var r0 *BootstrapConfig + if rf, ok := ret.Get(0).(func(context.Context) *BootstrapConfig); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*BootstrapConfig) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockClient_FetchBootstrap_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FetchBootstrap' +type MockClient_FetchBootstrap_Call struct { + *mock.Call +} + +// FetchBootstrap is a helper method to define mock.On call +// - ctx context.Context +func (_e *MockClient_Expecter) FetchBootstrap(ctx interface{}) *MockClient_FetchBootstrap_Call { + return &MockClient_FetchBootstrap_Call{Call: _e.mock.On("FetchBootstrap", ctx)} +} + +func (_c *MockClient_FetchBootstrap_Call) Run(run func(ctx context.Context)) *MockClient_FetchBootstrap_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *MockClient_FetchBootstrap_Call) Return(_a0 *BootstrapConfig, _a1 error) *MockClient_FetchBootstrap_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +// PushServerStatus provides a mock function with given fields: ctx, status +func (_m *MockClient) PushServerStatus(ctx context.Context, status *ServerStatus) error { + ret := _m.Called(ctx, status) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, *ServerStatus) error); ok { + r0 = rf(ctx, status) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockClient_PushServerStatus_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PushServerStatus' +type MockClient_PushServerStatus_Call struct { + *mock.Call +} + +// PushServerStatus is a helper method to define mock.On call +// - ctx context.Context +// - status *ServerStatus +func (_e *MockClient_Expecter) PushServerStatus(ctx interface{}, status interface{}) *MockClient_PushServerStatus_Call { + return &MockClient_PushServerStatus_Call{Call: _e.mock.On("PushServerStatus", ctx, status)} +} + +func (_c *MockClient_PushServerStatus_Call) Run(run func(ctx context.Context, status *ServerStatus)) *MockClient_PushServerStatus_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*ServerStatus)) + }) + return _c +} + +func (_c *MockClient_PushServerStatus_Call) Return(_a0 error) *MockClient_PushServerStatus_Call { + _c.Call.Return(_a0) + return _c +} + +type mockConstructorTestingTNewMockClient interface { + mock.TestingT + Cleanup(func()) +} + +// NewMockClient creates a new instance of MockClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewMockClient(t mockConstructorTestingTNewMockClient) *MockClient { + mock := &MockClient{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/agent/hcp/scada/capabilities.go b/agent/hcp/scada/capabilities.go new file mode 100644 index 0000000000..ab60251f08 --- /dev/null +++ b/agent/hcp/scada/capabilities.go @@ -0,0 +1,6 @@ +package scada + +import "github.com/hashicorp/hcp-scada-provider/capability" + +// CAPCoreAPI is the capability used to securely expose the Consul HTTP API to HCP +var CAPCoreAPI = capability.NewAddr("core_api") diff --git a/agent/hcp/scada/mock_Provider.go b/agent/hcp/scada/mock_Provider.go new file mode 100644 index 0000000000..744f54e68e --- /dev/null +++ b/agent/hcp/scada/mock_Provider.go @@ -0,0 +1,302 @@ +// Code generated by mockery v2.14.0. DO NOT EDIT. + +package scada + +import ( + net "net" + + mock "github.com/stretchr/testify/mock" + + time "time" +) + +// MockProvider is an autogenerated mock type for the Provider type +type MockProvider struct { + mock.Mock +} + +type MockProvider_Expecter struct { + mock *mock.Mock +} + +func (_m *MockProvider) EXPECT() *MockProvider_Expecter { + return &MockProvider_Expecter{mock: &_m.Mock} +} + +// GetMeta provides a mock function with given fields: +func (_m *MockProvider) GetMeta() map[string]string { + ret := _m.Called() + + var r0 map[string]string + if rf, ok := ret.Get(0).(func() map[string]string); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(map[string]string) + } + } + + return r0 +} + +// MockProvider_GetMeta_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetMeta' +type MockProvider_GetMeta_Call struct { + *mock.Call +} + +// GetMeta is a helper method to define mock.On call +func (_e *MockProvider_Expecter) GetMeta() *MockProvider_GetMeta_Call { + return &MockProvider_GetMeta_Call{Call: _e.mock.On("GetMeta")} +} + +func (_c *MockProvider_GetMeta_Call) Run(run func()) *MockProvider_GetMeta_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockProvider_GetMeta_Call) Return(_a0 map[string]string) *MockProvider_GetMeta_Call { + _c.Call.Return(_a0) + return _c +} + +// LastError provides a mock function with given fields: +func (_m *MockProvider) LastError() (time.Time, error) { + ret := _m.Called() + + var r0 time.Time + if rf, ok := ret.Get(0).(func() time.Time); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(time.Time) + } + + var r1 error + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockProvider_LastError_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LastError' +type MockProvider_LastError_Call struct { + *mock.Call +} + +// LastError is a helper method to define mock.On call +func (_e *MockProvider_Expecter) LastError() *MockProvider_LastError_Call { + return &MockProvider_LastError_Call{Call: _e.mock.On("LastError")} +} + +func (_c *MockProvider_LastError_Call) Run(run func()) *MockProvider_LastError_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockProvider_LastError_Call) Return(_a0 time.Time, _a1 error) *MockProvider_LastError_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +// Listen provides a mock function with given fields: capability +func (_m *MockProvider) Listen(capability string) (net.Listener, error) { + ret := _m.Called(capability) + + var r0 net.Listener + if rf, ok := ret.Get(0).(func(string) net.Listener); ok { + r0 = rf(capability) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(net.Listener) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(capability) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockProvider_Listen_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Listen' +type MockProvider_Listen_Call struct { + *mock.Call +} + +// Listen is a helper method to define mock.On call +// - capability string +func (_e *MockProvider_Expecter) Listen(capability interface{}) *MockProvider_Listen_Call { + return &MockProvider_Listen_Call{Call: _e.mock.On("Listen", capability)} +} + +func (_c *MockProvider_Listen_Call) Run(run func(capability string)) *MockProvider_Listen_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string)) + }) + return _c +} + +func (_c *MockProvider_Listen_Call) Return(_a0 net.Listener, _a1 error) *MockProvider_Listen_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +// SessionStatus provides a mock function with given fields: +func (_m *MockProvider) SessionStatus() string { + ret := _m.Called() + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// MockProvider_SessionStatus_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SessionStatus' +type MockProvider_SessionStatus_Call struct { + *mock.Call +} + +// SessionStatus is a helper method to define mock.On call +func (_e *MockProvider_Expecter) SessionStatus() *MockProvider_SessionStatus_Call { + return &MockProvider_SessionStatus_Call{Call: _e.mock.On("SessionStatus")} +} + +func (_c *MockProvider_SessionStatus_Call) Run(run func()) *MockProvider_SessionStatus_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockProvider_SessionStatus_Call) Return(_a0 string) *MockProvider_SessionStatus_Call { + _c.Call.Return(_a0) + return _c +} + +// Start provides a mock function with given fields: +func (_m *MockProvider) Start() error { + ret := _m.Called() + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockProvider_Start_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Start' +type MockProvider_Start_Call struct { + *mock.Call +} + +// Start is a helper method to define mock.On call +func (_e *MockProvider_Expecter) Start() *MockProvider_Start_Call { + return &MockProvider_Start_Call{Call: _e.mock.On("Start")} +} + +func (_c *MockProvider_Start_Call) Run(run func()) *MockProvider_Start_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockProvider_Start_Call) Return(_a0 error) *MockProvider_Start_Call { + _c.Call.Return(_a0) + return _c +} + +// Stop provides a mock function with given fields: +func (_m *MockProvider) Stop() error { + ret := _m.Called() + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockProvider_Stop_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Stop' +type MockProvider_Stop_Call struct { + *mock.Call +} + +// Stop is a helper method to define mock.On call +func (_e *MockProvider_Expecter) Stop() *MockProvider_Stop_Call { + return &MockProvider_Stop_Call{Call: _e.mock.On("Stop")} +} + +func (_c *MockProvider_Stop_Call) Run(run func()) *MockProvider_Stop_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockProvider_Stop_Call) Return(_a0 error) *MockProvider_Stop_Call { + _c.Call.Return(_a0) + return _c +} + +// UpdateMeta provides a mock function with given fields: _a0 +func (_m *MockProvider) UpdateMeta(_a0 map[string]string) { + _m.Called(_a0) +} + +// MockProvider_UpdateMeta_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateMeta' +type MockProvider_UpdateMeta_Call struct { + *mock.Call +} + +// UpdateMeta is a helper method to define mock.On call +// - _a0 map[string]string +func (_e *MockProvider_Expecter) UpdateMeta(_a0 interface{}) *MockProvider_UpdateMeta_Call { + return &MockProvider_UpdateMeta_Call{Call: _e.mock.On("UpdateMeta", _a0)} +} + +func (_c *MockProvider_UpdateMeta_Call) Run(run func(_a0 map[string]string)) *MockProvider_UpdateMeta_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(map[string]string)) + }) + return _c +} + +func (_c *MockProvider_UpdateMeta_Call) Return() *MockProvider_UpdateMeta_Call { + _c.Call.Return() + return _c +} + +type mockConstructorTestingTNewMockProvider interface { + mock.TestingT + Cleanup(func()) +} + +// NewMockProvider creates a new instance of MockProvider. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewMockProvider(t mockConstructorTestingTNewMockProvider) *MockProvider { + mock := &MockProvider{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/agent/hcp/scada/scada.go b/agent/hcp/scada/scada.go new file mode 100644 index 0000000000..b74f762b1a --- /dev/null +++ b/agent/hcp/scada/scada.go @@ -0,0 +1,55 @@ +package scada + +import ( + "fmt" + "net" + + "github.com/hashicorp/consul/agent/hcp/config" + "github.com/hashicorp/go-hclog" + libscada "github.com/hashicorp/hcp-scada-provider" + "github.com/hashicorp/hcp-scada-provider/capability" + "github.com/hashicorp/hcp-sdk-go/resource" +) + +// Provider is the interface used in the rest of Consul core when using SCADA, it is aliased here to the same interface +// provided by the hcp-scada-provider library. If the interfaces needs to be extended in the future it can be done so +// with minimal impact on the rest of the codebase. +// +//go:generate mockery --name Provider --with-expecter --inpackage +type Provider interface { + libscada.SCADAProvider +} + +const ( + scadaConsulServiceKey = "consul" +) + +func New(cfg config.CloudConfig, logger hclog.Logger) (Provider, error) { + resource, err := resource.FromString(cfg.ResourceID) + if err != nil { + return nil, fmt.Errorf("failed to parse cloud resource_id: %w", err) + } + + hcpConfig, err := cfg.HCPConfig() + if err != nil { + return nil, fmt.Errorf("failed to build HCPConfig: %w", err) + } + + pvd, err := libscada.New(&libscada.Config{ + Service: scadaConsulServiceKey, + HCPConfig: hcpConfig, + Resource: *resource.Link(), + Logger: logger, + }) + if err != nil { + return nil, err + } + + return pvd, nil +} + +// IsCapability takes a net.Addr and returns true if it is a SCADA capability.Addr +func IsCapability(a net.Addr) bool { + _, ok := a.(*capability.Addr) + return ok +} diff --git a/agent/hcp/testing.go b/agent/hcp/testing.go new file mode 100644 index 0000000000..e32602777d --- /dev/null +++ b/agent/hcp/testing.go @@ -0,0 +1,177 @@ +package hcp + +import ( + "encoding/json" + "fmt" + "log" + "net/http" + "regexp" + "strings" + "sync" + "time" + + gnmmod "github.com/hashicorp/hcp-sdk-go/clients/cloud-global-network-manager-service/preview/2022-02-15/models" + "github.com/hashicorp/hcp-sdk-go/resource" +) + +type TestEndpoint struct { + Methods []string + PathSuffix string + Handler func(r *http.Request, cluster resource.Resource) (interface{}, error) +} + +type MockHCPServer struct { + mu sync.Mutex + handlers map[string]TestEndpoint + + servers map[string]*gnmmod.HashicorpCloudGlobalNetworkManager20220215Server +} + +var basePathRe = regexp.MustCompile("/global-network-manager/[^/]+/organizations/([^/]+)/projects/([^/]+)/clusters/([^/]+)/([^/]+.*)") + +func NewMockHCPServer() *MockHCPServer { + s := &MockHCPServer{ + handlers: make(map[string]TestEndpoint), + servers: make(map[string]*gnmmod.HashicorpCloudGlobalNetworkManager20220215Server), + } + // Define endpoints in this package + s.AddEndpoint(TestEndpoint{ + Methods: []string{"POST"}, + PathSuffix: "agent/server-state", + Handler: s.handleStatus, + }) + s.AddEndpoint(TestEndpoint{ + Methods: []string{"POST"}, + PathSuffix: "agent/discover", + Handler: s.handleDiscover, + }) + return s +} + +// AddEndpoint allows adding additional endpoints from other packages e.g. +// bootstrap (which can't be merged into one package due to dependency cycles). +// It's not safe to call this concurrently with any other call to AddEndpoint or +// ServeHTTP. +func (s *MockHCPServer) AddEndpoint(e TestEndpoint) { + s.handlers[e.PathSuffix] = e +} + +func (s *MockHCPServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { + s.mu.Lock() + defer s.mu.Unlock() + + if r.URL.Path == "/oauth/token" { + mockTokenResponse(w) + return + } + + matches := basePathRe.FindStringSubmatch(r.URL.Path) + if matches == nil || len(matches) < 5 { + w.WriteHeader(404) + log.Printf("ERROR 404: %s %s\n", r.Method, r.URL.Path) + return + } + + cluster := resource.Resource{ + ID: matches[3], + Type: "cluster", + Organization: matches[1], + Project: matches[2], + } + found := false + var resp interface{} + var err error + for _, e := range s.handlers { + if e.PathSuffix == matches[4] { + found = true + if !enforceMethod(w, r, e.Methods) { + return + } + resp, err = e.Handler(r, cluster) + break + } + } + if !found { + w.WriteHeader(404) + log.Printf("ERROR 404: %s %s\n", r.Method, r.URL.Path) + return + } + if err != nil { + errResponse(w, err) + return + } + + if resp == nil { + // no response body + log.Printf("OK 204: %s %s\n", r.Method, r.URL.Path) + w.WriteHeader(http.StatusNoContent) + return + } + + bs, err := json.MarshalIndent(resp, "", " ") + if err != nil { + errResponse(w, err) + return + } + + log.Printf("OK 200: %s %s\n", r.Method, r.URL.Path) + w.Header().Set("content-type", "application/json") + w.WriteHeader(http.StatusOK) + w.Write(bs) +} + +func enforceMethod(w http.ResponseWriter, r *http.Request, methods []string) bool { + for _, m := range methods { + if strings.EqualFold(r.Method, m) { + return true + } + } + // No match, sent 4xx + w.WriteHeader(http.StatusMethodNotAllowed) + log.Printf("ERROR 405: bad method (not in %v): %s %s\n", methods, r.Method, r.URL.Path) + return false +} + +func mockTokenResponse(w http.ResponseWriter) { + w.WriteHeader(http.StatusOK) + w.Write([]byte(`{access_token: "token", token_type: "Bearer"}`)) +} + +func (s *MockHCPServer) handleStatus(r *http.Request, cluster resource.Resource) (interface{}, error) { + var req gnmmod.HashicorpCloudGlobalNetworkManager20220215AgentPushServerStateRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + return nil, err + } + status := req.ServerState + log.Printf("STATUS UPDATE: server=%s version=%s leader=%v hasLeader=%v healthy=%v tlsCertExpiryDays=%1.0f", + status.Name, + status.Version, + status.Raft.IsLeader, + status.Raft.KnownLeader, + status.Autopilot.Healthy, + time.Until(time.Time(status.TLS.CertExpiry)).Hours()/24, + ) + s.servers[status.Name] = &gnmmod.HashicorpCloudGlobalNetworkManager20220215Server{ + GossipPort: status.GossipPort, + ID: status.ID, + LanAddress: status.LanAddress, + Name: status.Name, + RPCPort: status.RPCPort, + } + return "{}", nil +} + +func (s *MockHCPServer) handleDiscover(r *http.Request, cluster resource.Resource) (interface{}, error) { + servers := make([]*gnmmod.HashicorpCloudGlobalNetworkManager20220215Server, len(s.servers)) + for _, server := range s.servers { + servers = append(servers, server) + } + + return gnmmod.HashicorpCloudGlobalNetworkManager20220215AgentDiscoverResponse{Servers: servers}, nil +} + +func errResponse(w http.ResponseWriter, err error) { + log.Printf("ERROR 500: %s\n", err) + w.WriteHeader(500) + w.Write([]byte(fmt.Sprintf(`{"error": %q}`, err.Error()))) +} diff --git a/agent/hcp/testserver/main.go b/agent/hcp/testserver/main.go new file mode 100644 index 0000000000..0166d00682 --- /dev/null +++ b/agent/hcp/testserver/main.go @@ -0,0 +1,45 @@ +package main + +import ( + "flag" + "fmt" + "log" + "net/http" + "os" + "os/signal" + "syscall" + + "github.com/hashicorp/consul/agent/hcp" + "github.com/hashicorp/consul/agent/hcp/bootstrap" +) + +var port int + +func main() { + flag.IntVar(&port, "port", 9999, "port to listen on") + flag.Parse() + + s := hcp.NewMockHCPServer() + s.AddEndpoint(bootstrap.TestEndpoint()) + + sigs := make(chan os.Signal, 1) + signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) + + addr := fmt.Sprintf("127.0.0.1:%d", port) + srv := http.Server{ + Addr: addr, + Handler: s, + } + + log.Printf("Listening on %s\n", addr) + + go func() { + if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { + log.Fatalf("listen: %s\n", err) + } + }() + + <-sigs + log.Println("Shutting down HTTP server") + srv.Close() +} diff --git a/agent/proxycfg/ingress_gateway.go b/agent/proxycfg/ingress_gateway.go index 81a4928369..b21bc8738b 100644 --- a/agent/proxycfg/ingress_gateway.go +++ b/agent/proxycfg/ingress_gateway.go @@ -201,7 +201,7 @@ func makeUpstream(g *structs.GatewayService) structs.Upstream { } func (s *handlerIngressGateway) watchIngressLeafCert(ctx context.Context, snap *ConfigSnapshot) error { - // Note that we DON'T test for TLS.Enabled because we need a leaf cert for the + // Note that we DON'T test for TLS.enabled because we need a leaf cert for the // gateway even without TLS to use as a client cert. if !snap.IngressGateway.GatewayConfigLoaded || !snap.IngressGateway.HostsSet { return nil diff --git a/agent/retry_join.go b/agent/retry_join.go index b807697e84..b3ebcf2a9a 100644 --- a/agent/retry_join.go +++ b/agent/retry_join.go @@ -5,6 +5,7 @@ import ( "strings" "time" + discoverhcp "github.com/hashicorp/consul/agent/hcp/discover" discover "github.com/hashicorp/go-discover" discoverk8s "github.com/hashicorp/go-discover/provider/k8s" "github.com/hashicorp/go-hclog" @@ -114,6 +115,7 @@ func newDiscover() (*discover.Discover, error) { providers[k] = v } providers["k8s"] = &discoverk8s.Provider{} + providers["hcp"] = &discoverhcp.Provider{} return discover.New( discover.WithUserAgent(lib.UserAgent()), diff --git a/agent/retry_join_test.go b/agent/retry_join_test.go index 9bc98797c2..e6e2fac779 100644 --- a/agent/retry_join_test.go +++ b/agent/retry_join_test.go @@ -12,7 +12,7 @@ func TestAgentRetryNewDiscover(t *testing.T) { d, err := newDiscover() require.NoError(t, err) expected := []string{ - "aliyun", "aws", "azure", "digitalocean", "gce", "k8s", "linode", + "aliyun", "aws", "azure", "digitalocean", "gce", "hcp", "k8s", "linode", "mdns", "os", "packet", "scaleway", "softlayer", "tencentcloud", "triton", "vsphere", } diff --git a/agent/setup.go b/agent/setup.go index 25353e1ac9..4fdeab213e 100644 --- a/agent/setup.go +++ b/agent/setup.go @@ -8,6 +8,7 @@ import ( "time" "github.com/armon/go-metrics/prometheus" + "github.com/hashicorp/consul/agent/hcp" "github.com/hashicorp/go-hclog" "google.golang.org/grpc/grpclog" @@ -153,6 +154,12 @@ func NewBaseDeps(configLoader ConfigLoader, logOut io.Writer) (BaseDeps, error) d.EventPublisher = stream.NewEventPublisher(10 * time.Second) d.XDSStreamLimiter = limiter.NewSessionLimiter() + if cfg.IsCloudEnabled() { + d.HCP, err = hcp.NewDeps(cfg.Cloud, d.Logger) + if err != nil { + return d, err + } + } return d, nil } diff --git a/agent/testagent.go b/agent/testagent.go index ea5afff81d..2fb4438ddb 100644 --- a/agent/testagent.go +++ b/agent/testagent.go @@ -85,6 +85,9 @@ type TestAgent struct { // non-user settable configurations Overrides string + // allows the BaseDeps to be modified before starting the embedded agent + OverrideDeps func(deps *BaseDeps) + // Agent is the embedded consul agent. // It is valid after Start(). *Agent @@ -234,6 +237,10 @@ func (a *TestAgent) Start(t *testing.T) error { } a.Config = bd.RuntimeConfig + if a.OverrideDeps != nil { + a.OverrideDeps(&bd) + } + agent, err := New(bd) if err != nil { return fmt.Errorf("Error creating agent: %s", err) diff --git a/command/agent/agent.go b/command/agent/agent.go index ae455297a6..8b6a900a90 100644 --- a/command/agent/agent.go +++ b/command/agent/agent.go @@ -18,6 +18,7 @@ import ( "github.com/hashicorp/consul/agent" "github.com/hashicorp/consul/agent/config" + hcpbootstrap "github.com/hashicorp/consul/agent/hcp/bootstrap" "github.com/hashicorp/consul/command/cli" "github.com/hashicorp/consul/command/flags" "github.com/hashicorp/consul/lib" @@ -152,7 +153,7 @@ func (c *cmd) startupJoinWan(agent *agent.Agent, cfg *config.RuntimeConfig) erro func (c *cmd) run(args []string) int { ui := &mcli.PrefixedUi{ OutputPrefix: "==> ", - InfoPrefix: " ", + InfoPrefix: " ", // Note that startupLogger also uses this prefix ErrorPrefix: "==> ", Ui: c.ui, } @@ -175,6 +176,29 @@ func (c *cmd) run(args []string) int { c.configLoadOpts.DefaultConfig = source return config.Load(c.configLoadOpts) } + + // wait for signal + signalCh := make(chan os.Signal, 10) + signal.Notify(signalCh, os.Interrupt, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGPIPE) + + ctx, cancel := context.WithCancel(context.Background()) + + // startup logger is a shim since we need to be about to log both before and + // after logging is setup properly but before agent has started fully. This + // takes care of that! + suLogger := newStartupLogger() + go handleStartupSignals(ctx, cancel, signalCh, suLogger) + + // See if we need to bootstrap config from HCP before we go any further with + // agent startup. We override loader with the one returned as it may be + // modified to include HCP-provided config. + var err error + _, loader, err = hcpbootstrap.MaybeBootstrap(ctx, loader, ui) + if err != nil { + ui.Error(err.Error()) + return 1 + } + bd, err := agent.NewBaseDeps(loader, logGate) if err != nil { ui.Error(err.Error()) @@ -187,6 +211,9 @@ func (c *cmd) run(args []string) int { return 1 } + // Upgrade our startupLogger to use the real logger now we have it + suLogger.SetLogger(c.logger) + config := bd.RuntimeConfig if config.Logging.LogJSON { // Hide all non-error output when JSON logging is enabled. @@ -229,38 +256,6 @@ func (c *cmd) run(args []string) int { ui.Output("Log data will now stream in as it occurs:\n") logGate.Flush() - // wait for signal - signalCh := make(chan os.Signal, 10) - signal.Notify(signalCh, os.Interrupt, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGPIPE) - - ctx, cancel := context.WithCancel(context.Background()) - - go func() { - for { - var sig os.Signal - select { - case s := <-signalCh: - sig = s - case <-ctx.Done(): - return - } - - switch sig { - case syscall.SIGPIPE: - continue - - case syscall.SIGHUP: - err := fmt.Errorf("cannot reload before agent started") - c.logger.Error("Caught", "signal", sig, "error", err) - - default: - c.logger.Info("Caught", "signal", sig) - cancel() - return - } - } - }() - err = agent.Start(ctx) signal.Stop(signalCh) cancel() @@ -362,6 +357,32 @@ func (c *cmd) run(args []string) int { } } +func handleStartupSignals(ctx context.Context, cancel func(), signalCh chan os.Signal, logger *startupLogger) { + for { + var sig os.Signal + select { + case s := <-signalCh: + sig = s + case <-ctx.Done(): + return + } + + switch sig { + case syscall.SIGPIPE: + continue + + case syscall.SIGHUP: + err := fmt.Errorf("cannot reload before agent started") + logger.Error("Caught", "signal", sig, "error", err) + + default: + logger.Info("Caught", "signal", sig) + cancel() + return + } + } +} + func (c *cmd) Synopsis() string { return synopsis } diff --git a/command/agent/startup_logger.go b/command/agent/startup_logger.go new file mode 100644 index 0000000000..f632b307f0 --- /dev/null +++ b/command/agent/startup_logger.go @@ -0,0 +1,64 @@ +package agent + +import ( + "sync" + + "github.com/hashicorp/go-hclog" +) + +// startupLogger is a shim that allows signal handling (and anything else) to +// log to an appropriate output throughout several startup phases. Initially +// when bootstrapping from HCP we need to log caught signals direct to the UI +// output since logging is not setup yet and won't be if we are interrupted +// before we try to start the agent itself. Later, during agent.Start we could +// block retrieving auto TLS or auto-config from servers so need to handle +// signals, but in this case logging has already started so we should log the +// signal event to the logger. +type startupLogger struct { + mu sync.Mutex + logger hclog.Logger +} + +func newStartupLogger() *startupLogger { + return &startupLogger{ + // Start off just using defaults for hclog since this is too early to have + // parsed logging config even and we just want to get _something_ out to the + // user. + logger: hclog.New(&hclog.LoggerOptions{ + Name: "agent.startup", + // Nothing else output in UI has a time prefix until logging is properly + // setup so use the same prefix as other "Info" lines to make it look less + // strange. Note one less space than in PrefixedUI since hclog puts a + // space between the time prefix and log line already. + TimeFormat: " ", + }), + } +} + +func (l *startupLogger) SetLogger(logger hclog.Logger) { + l.mu.Lock() + defer l.mu.Unlock() + + l.logger = logger +} + +func (l *startupLogger) Info(msg string, args ...interface{}) { + l.mu.Lock() + defer l.mu.Unlock() + + l.logger.Info(msg, args...) +} + +func (l *startupLogger) Warn(msg string, args ...interface{}) { + l.mu.Lock() + defer l.mu.Unlock() + + l.logger.Warn(msg, args...) +} + +func (l *startupLogger) Error(msg string, args ...interface{}) { + l.mu.Lock() + defer l.mu.Unlock() + + l.logger.Error(msg, args...) +} diff --git a/go.mod b/go.mod index 70d58e70dd..5459d09f33 100644 --- a/go.mod +++ b/go.mod @@ -21,6 +21,8 @@ require ( github.com/docker/go-connections v0.3.0 github.com/envoyproxy/go-control-plane v0.10.1 github.com/fsnotify/fsnotify v1.5.1 + github.com/go-openapi/runtime v0.19.24 + github.com/go-openapi/strfmt v0.20.0 github.com/golang/protobuf v1.5.0 github.com/google/go-cmp v0.5.8 github.com/google/gofuzz v1.2.0 @@ -37,7 +39,7 @@ require ( github.com/hashicorp/go-cleanhttp v0.5.1 github.com/hashicorp/go-connlimit v0.3.0 github.com/hashicorp/go-discover v0.0.0-20220411141802-20db45f7f0f9 - github.com/hashicorp/go-hclog v0.14.1 + github.com/hashicorp/go-hclog v1.2.1 github.com/hashicorp/go-memdb v1.3.2 github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-raftchunking v0.6.2 @@ -47,6 +49,8 @@ require ( github.com/hashicorp/go-version v1.2.1 github.com/hashicorp/golang-lru v0.5.4 github.com/hashicorp/hcl v1.0.0 + github.com/hashicorp/hcp-scada-provider v0.1.0 + github.com/hashicorp/hcp-sdk-go v0.23.1-0.20220921131124-49168300a7dc github.com/hashicorp/hil v0.0.0-20200423225030-a18a1cd20038 github.com/hashicorp/memberlist v0.4.0 github.com/hashicorp/raft v1.3.9 @@ -55,7 +59,7 @@ require ( github.com/hashicorp/serf v0.10.0 github.com/hashicorp/vault/api v1.0.5-0.20200717191844-f687267c8086 github.com/hashicorp/vault/sdk v0.1.14-0.20200519221838-e0cfd64bc267 - github.com/hashicorp/yamux v0.0.0-20210826001029-26ff87cf9493 + github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 github.com/imdario/mergo v0.3.13 github.com/kr/text v0.2.0 github.com/miekg/dns v1.1.41 @@ -78,11 +82,11 @@ require ( go.uber.org/goleak v1.1.10 golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a golang.org/x/net v0.0.0-20211216030914-fe4d6282115f - golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d + golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e - google.golang.org/genproto v0.0.0-20200623002339-fbb79eadd5eb + google.golang.org/genproto v0.0.0-20200825200019-8632dd797987 google.golang.org/grpc v1.37.1 google.golang.org/protobuf v1.27.1 gopkg.in/square/go-jose.v2 v2.5.1 @@ -93,7 +97,7 @@ require ( ) require ( - cloud.google.com/go v0.59.0 // indirect + cloud.google.com/go v0.65.0 // indirect github.com/Azure/azure-sdk-for-go v44.0.0+incompatible // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest v0.11.18 // indirect @@ -107,6 +111,9 @@ require ( github.com/Azure/go-autorest/tracing v0.6.0 // indirect github.com/DataDog/datadog-go v3.2.0+incompatible // indirect github.com/Microsoft/go-winio v0.4.3 // indirect + github.com/PuerkitoBio/purell v1.1.1 // indirect + github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect + github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/speakeasy v0.1.0 // indirect github.com/boltdb/bolt v1.3.1 // indirect @@ -120,10 +127,20 @@ require ( github.com/digitalocean/godo v1.10.0 // indirect github.com/dimchansky/utfbom v1.1.0 // indirect github.com/envoyproxy/protoc-gen-validate v0.1.0 // indirect - github.com/fatih/color v1.9.0 // indirect + github.com/fatih/color v1.13.0 // indirect github.com/form3tech-oss/jwt-go v3.2.2+incompatible // indirect github.com/frankban/quicktest v1.11.0 // indirect github.com/go-ole/go-ole v1.2.6 // indirect + github.com/go-openapi/analysis v0.20.0 // indirect + github.com/go-openapi/errors v0.20.1 // indirect + github.com/go-openapi/jsonpointer v0.19.5 // indirect + github.com/go-openapi/jsonreference v0.19.5 // indirect + github.com/go-openapi/loads v0.20.2 // indirect + github.com/go-openapi/spec v0.20.3 // indirect + github.com/go-openapi/swag v0.19.14 // indirect + github.com/go-openapi/validate v0.20.2 // indirect + github.com/go-ozzo/ozzo-validation v3.6.0+incompatible // indirect + github.com/go-stack/stack v1.8.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect github.com/golang/snappy v0.0.1 // indirect @@ -134,21 +151,25 @@ require ( github.com/gophercloud/gophercloud v0.1.0 // indirect github.com/hashicorp/errwrap v1.0.0 // indirect github.com/hashicorp/go-immutable-radix v1.3.0 // indirect - github.com/hashicorp/go-msgpack v0.5.5 // indirect + github.com/hashicorp/go-msgpack v1.1.5 // indirect github.com/hashicorp/go-retryablehttp v0.6.7 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect github.com/hashicorp/mdns v1.0.4 // indirect + github.com/hashicorp/net-rpc-msgpackrpc v0.0.0-20151116020338-a14192a58a69 // indirect github.com/hashicorp/raft-boltdb v0.0.0-20211202195631-7d34b9fb3f42 // indirect github.com/hashicorp/vic v1.5.1-0.20190403131502-bbfe86ec9443 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/josharian/intern v1.0.0 // indirect github.com/joyent/triton-go v1.7.1-0.20200416154420-6801d15b779f // indirect github.com/json-iterator/go v1.1.9 // indirect github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect github.com/linode/linodego v0.7.1 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect - github.com/mattn/go-colorable v0.1.6 // indirect - github.com/mattn/go-isatty v0.0.12 // indirect + github.com/mailru/easyjson v0.7.6 // indirect + github.com/mattn/go-colorable v0.1.12 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect + github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.1 // indirect @@ -166,6 +187,7 @@ require ( github.com/ryanuber/go-glob v1.0.0 // indirect github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect github.com/sirupsen/logrus v1.4.2 // indirect + github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect github.com/softlayer/softlayer-go v0.0.0-20180806151055-260589d94c7d // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/objx v0.4.0 // indirect @@ -175,17 +197,18 @@ require ( github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926 // indirect github.com/vmware/govmomi v0.18.0 // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect - go.opencensus.io v0.22.3 // indirect + go.mongodb.org/mongo-driver v1.4.6 // indirect + go.opencensus.io v0.22.4 // indirect go.opentelemetry.io/proto/otlp v0.7.0 // indirect golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 // indirect golang.org/x/text v0.3.6 // indirect golang.org/x/tools v0.1.0 // indirect - google.golang.org/api v0.28.0 // indirect + google.golang.org/api v0.30.0 // indirect google.golang.org/appengine v1.6.6 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/resty.v1 v1.12.0 // indirect - gopkg.in/yaml.v2 v2.2.8 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/klog v1.0.0 // indirect k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89 // indirect diff --git a/go.sum b/go.sum index a5fdbdd250..eef538621b 100644 --- a/go.sum +++ b/go.sum @@ -11,8 +11,9 @@ cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6 cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.59.0 h1:BM3svUDU3itpc2m5cu5wCyThIYNDlFlts9GASw31GW8= -cloud.google.com/go v0.59.0/go.mod h1:qJxNOVCRTxHfwLhvDxxSI9vQc1zI59b9pEglp1Iv60E= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0 h1:Dg9iHVQfrhq82rUNu9ZxUDrJLaxFUe/HlCVaLyRruq8= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= @@ -29,6 +30,7 @@ cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiy cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/azure-sdk-for-go v44.0.0+incompatible h1:e82Yv2HNpS0kuyeCrV29OPKvEiqfs2/uJHic3/3iKdg= github.com/Azure/azure-sdk-for-go v44.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= @@ -77,13 +79,20 @@ github.com/NYTimes/gziphandler v1.0.1 h1:iLrQrdwjDd52kHDA5op2UBJFjmOb9g+7scBan4R github.com/NYTimes/gziphandler v1.0.1/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af h1:DBNMBMuMiWYu0b+8KMJuWmfCkcxl09JwdlqwDZZ6U14= github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af/go.mod h1:5Jv4cbFiHJMsVxt52+i0Ha45fjshj6wxYr1r19tB9bw= +github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e h1:QEF07wC0T1rKkctt1RINW/+RMTVmiwxETico2l3gxJA= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= @@ -97,8 +106,15 @@ github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= +github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= +github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef h1:46PFijGLmAjMPwCCCo7Jf0W6f9slllCkkv7vyc1yOSg= +github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.25.41/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48= github.com/aws/aws-sdk-go v1.42.34 h1:fqGAiKmCSRY1rEa4G9VqgkKKbNmLKYq5dKmLtQkvYi8= github.com/aws/aws-sdk-go v1.42.34/go.mod h1:OGr6lGMAKGlG9CVrYnWYDKIyb829c6EVBRjxqjmPepc= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -155,6 +171,8 @@ github.com/dnaeon/go-vcr v1.0.1 h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= github.com/docker/go-connections v0.3.0 h1:3lOnM9cSzgGwx8VfK/NGOW5fLQ0GjIlCkaktF+n1M6o= github.com/docker/go-connections v0.3.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= @@ -170,8 +188,9 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrp github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= @@ -182,6 +201,8 @@ github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWp github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -194,14 +215,132 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= +github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= +github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= +github.com/go-openapi/analysis v0.19.4/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= +github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU= +github.com/go-openapi/analysis v0.19.10/go.mod h1:qmhS3VNFxBlquFJ0RGoDtylO9y4pgTAUNE9AEEMdlJQ= +github.com/go-openapi/analysis v0.19.16/go.mod h1:GLInF007N83Ad3m8a/CbQ5TPzdnGT7workfHwuVjNVk= +github.com/go-openapi/analysis v0.20.0 h1:UN09o0kNhleunxW7LR+KnltD0YrJ8FF03pSqvAN3Vro= +github.com/go-openapi/analysis v0.20.0/go.mod h1:BMchjvaHDykmRMsK40iPtvyOfFdMMxlOmQr9FBZk+Og= +github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= +github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= +github.com/go-openapi/errors v0.19.3/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= +github.com/go-openapi/errors v0.19.6/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/errors v0.19.7/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/errors v0.19.9/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/errors v0.20.1 h1:j23mMDtRxMwIobkpId7sWh7Ddcx4ivaoqUbfXx5P+a8= +github.com/go-openapi/errors v0.20.1/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= +github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= +github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= +github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= +github.com/go-openapi/jsonreference v0.19.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM= +github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= +github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= +github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs= +github.com/go-openapi/loads v0.19.3/go.mod h1:YVfqhUCdahYwR3f3iiwQLhicVRvLlU/WO5WPaZvcvSI= +github.com/go-openapi/loads v0.19.5/go.mod h1:dswLCAdonkRufe/gSUC3gN8nTSaB9uaS2es0x5/IbjY= +github.com/go-openapi/loads v0.19.6/go.mod h1:brCsvE6j8mnbmGBh103PT/QLHfbyDxA4hsKvYBNEGVc= +github.com/go-openapi/loads v0.19.7/go.mod h1:brCsvE6j8mnbmGBh103PT/QLHfbyDxA4hsKvYBNEGVc= +github.com/go-openapi/loads v0.20.0/go.mod h1:2LhKquiE513rN5xC6Aan6lYOSddlL8Mp20AW9kpviM4= +github.com/go-openapi/loads v0.20.2 h1:z5p5Xf5wujMxS1y8aP+vxwW5qYT2zdJBbXKmQUG3lcc= +github.com/go-openapi/loads v0.20.2/go.mod h1:hTVUotJ+UonAMMZsvakEgmWKgtulweO9vYP2bQYKA/o= +github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= +github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64= +github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4= +github.com/go-openapi/runtime v0.19.15/go.mod h1:dhGWCTKRXlAfGnQG0ONViOZpjfg0m2gUt9nTQPQZuoo= +github.com/go-openapi/runtime v0.19.16/go.mod h1:5P9104EJgYcizotuXhEuUrzVc+j1RiSjahULvYmlv98= +github.com/go-openapi/runtime v0.19.24 h1:TqagMVlRAOTwllE/7hNKx6rQ10O6T8ZzeJdMjSTKaD4= +github.com/go-openapi/runtime v0.19.24/go.mod h1:Lm9YGCeecBnUUkFTxPC4s1+lwrkJ0pthx8YvyjCfkgk= github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= +github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= +github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= +github.com/go-openapi/spec v0.19.6/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= +github.com/go-openapi/spec v0.19.8/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= +github.com/go-openapi/spec v0.19.15/go.mod h1:+81FIL1JwC5P3/Iuuozq3pPE9dXdIEGxFutcFKaVbmU= +github.com/go-openapi/spec v0.20.0/go.mod h1:+81FIL1JwC5P3/Iuuozq3pPE9dXdIEGxFutcFKaVbmU= +github.com/go-openapi/spec v0.20.1/go.mod h1:93x7oh+d+FQsmsieroS4cmR3u0p/ywH649a3qwC9OsQ= +github.com/go-openapi/spec v0.20.3 h1:uH9RQ6vdyPSs2pSy9fL8QPspDF2AMIMPtmK5coSSjtQ= +github.com/go-openapi/spec v0.20.3/go.mod h1:gG4F8wdEDN+YPBMVnzE85Rbhf+Th2DTvA9nFPQ5AYEg= +github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= +github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= +github.com/go-openapi/strfmt v0.19.2/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= +github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= +github.com/go-openapi/strfmt v0.19.4/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk= +github.com/go-openapi/strfmt v0.19.5/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk= +github.com/go-openapi/strfmt v0.19.11/go.mod h1:UukAYgTaQfqJuAFlNxxMWNvMYiwiXtLsF2VwmoFtbtc= +github.com/go-openapi/strfmt v0.20.0 h1:l2omNtmNbMc39IGptl9BuXBEKcZfS8zjrTsPKTiJiDM= +github.com/go-openapi/strfmt v0.20.0/go.mod h1:UukAYgTaQfqJuAFlNxxMWNvMYiwiXtLsF2VwmoFtbtc= github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= +github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.7/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY= +github.com/go-openapi/swag v0.19.9/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY= +github.com/go-openapi/swag v0.19.12/go.mod h1:eFdyEBkTdoAf/9RXBvj4cr1nH7GD8Kzo5HTt47gr72M= +github.com/go-openapi/swag v0.19.13/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng= +github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= +github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= +github.com/go-openapi/validate v0.19.3/go.mod h1:90Vh6jjkTn+OT1Eefm0ZixWNFjhtOH7vS9k0lo6zwJo= +github.com/go-openapi/validate v0.19.10/go.mod h1:RKEZTUWDkxKQxN2jDT7ZnZi2bhZlbNMAuKvKB+IaGx8= +github.com/go-openapi/validate v0.19.12/go.mod h1:Rzou8hA/CBw8donlS6WNEUQupNvUZ0waH08tGe6kAQ4= +github.com/go-openapi/validate v0.19.15/go.mod h1:tbn/fdOwYHgrhPBzidZfJC2MIVvs9GA7monOmWBbeCI= +github.com/go-openapi/validate v0.20.1/go.mod h1:b60iJT+xNNLfaQJUqLI7946tYiFEOuE9E4k54HpKcJ0= +github.com/go-openapi/validate v0.20.2 h1:AhqDegYV3J3iQkMPJSXkvzymHKMTw0BST3RK3hTT4ts= +github.com/go-openapi/validate v0.20.2/go.mod h1:e7OJoKNgd0twXZwIn0A43tHbvIcr/rZIVCbJBpTUoY0= +github.com/go-ozzo/ozzo-validation v3.6.0+incompatible h1:msy24VGS42fKO9K1vLz82/GeYW1cILu7Nuuj1N3BBkE= +github.com/go-ozzo/ozzo-validation v3.6.0+incompatible/go.mod h1:gsEKFIVnabGBt6mXmxK0MoFy+cZoTJY6mu5Ll3LVLBU= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw= github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= +github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= +github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= +github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= +github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= +github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= +github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= +github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= +github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= +github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= +github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= +github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= +github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= +github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= +github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= +github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= @@ -220,6 +359,7 @@ github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFU github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -248,6 +388,7 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= @@ -262,19 +403,22 @@ github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200507031123-427632fa3b1c/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22 h1:ub2sxhs2A0HRa2dWHavvmWxiVGXNfE9wI+gcTMwED8A= github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/tcpproxy v0.0.0-20180808230851-dfa16c61dad2 h1:AtvtonGEH/fZK0XPNNBdB6swgy7Iudfx88wzyIpwqJ8= github.com/google/tcpproxy v0.0.0-20180808230851-dfa16c61dad2/go.mod h1:DavVbd41y+b7ukKDmlnPR4nGYmkWXR6vHUkjQNiHPBs= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= @@ -313,8 +457,9 @@ github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9 github.com/hashicorp/go-hclog v0.9.1/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-hclog v0.14.1 h1:nQcJDQwIAGnmoUWp8ubocEX40cCml/17YkF6csQLReU= github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-hclog v1.2.1 h1:YQsLlGDJgwhXFpucSPyVbCBviQtjlHv3jLTlp8YmtEw= +github.com/hashicorp/go-hclog v1.2.1/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.0 h1:8exGP7ego3OmkfksihtSouGMZ+hQrhxx+FVELeXpVPE= github.com/hashicorp/go-immutable-radix v1.3.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= @@ -322,8 +467,9 @@ github.com/hashicorp/go-kms-wrapping/entropy v0.1.0/go.mod h1:d1g9WGtAunDNpek8jU github.com/hashicorp/go-memdb v1.3.2 h1:RBKHOsnSszpU6vxq80LzC2BaQjuuvoyaQbkLTf7V7g8= github.com/hashicorp/go-memdb v1.3.2/go.mod h1:Mluclgwib3R93Hk5fxEfiRhB+6Dar64wWh71LpNSe3g= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI= github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-msgpack v1.1.5 h1:9byZdVjKTe5mce63pRVNP1L7UAmdHOTEMGehn6KvJWs= +github.com/hashicorp/go-msgpack v1.1.5/go.mod h1:gWVc3sv/wbDmR3rQsj1CAktEZzoz1YNK9NfGLXJ69/4= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= @@ -355,6 +501,10 @@ github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+l github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/hcp-scada-provider v0.1.0 h1:FSjTw7EBl6GJFv5533harm1vw15OaEYodNGHde908MI= +github.com/hashicorp/hcp-scada-provider v0.1.0/go.mod h1:8Pp3pBLzZ9DL56OHSbf55qhh+TpvmXBuR5cJx9jcdcA= +github.com/hashicorp/hcp-sdk-go v0.23.1-0.20220921131124-49168300a7dc h1:on26TCKYnX7JzZCtwkR/LWHSqMu40PoZ6h/0e6Pq8ug= +github.com/hashicorp/hcp-sdk-go v0.23.1-0.20220921131124-49168300a7dc/go.mod h1:/9UoDY2FYYA8lFaKBb2HmM/jKYZGANmf65q9QRc/cVw= github.com/hashicorp/hil v0.0.0-20200423225030-a18a1cd20038 h1:n9J0rwVWXDpNd5iZnwY7w4WZyq53/rROeI7OVvLW8Ok= github.com/hashicorp/hil v0.0.0-20200423225030-a18a1cd20038/go.mod h1:n2TSygSNwsLJ76m8qFXTSc7beTb+auJxYdqrnoqwZWE= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= @@ -365,6 +515,8 @@ github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOn github.com/hashicorp/memberlist v0.3.1/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= github.com/hashicorp/memberlist v0.4.0 h1:k3uda5gZcltmafuFF+UFqNEl5PrH+yPZ4zkjp1f/H/8= github.com/hashicorp/memberlist v0.4.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0= +github.com/hashicorp/net-rpc-msgpackrpc v0.0.0-20151116020338-a14192a58a69 h1:lc3c72qGlIMDqQpQH82Y4vaglRMMFdJbziYWriR4UcE= +github.com/hashicorp/net-rpc-msgpackrpc v0.0.0-20151116020338-a14192a58a69/go.mod h1:/z+jUGRBlwVpUZfjute9jWaF6/HuhjuFQuL1YXzVD1Q= github.com/hashicorp/raft v1.1.0/go.mod h1:4Ak7FSPnuvmb0GV6vgIAJ4vYT4bek9bb6Q+7HVbyzqM= github.com/hashicorp/raft v1.1.1/go.mod h1:vPAJM8Asw6u8LxC3eJCUZmRP/E4QmUGE1R7g7k8sG/8= github.com/hashicorp/raft v1.2.0/go.mod h1:vPAJM8Asw6u8LxC3eJCUZmRP/E4QmUGE1R7g7k8sG/8= @@ -388,8 +540,8 @@ github.com/hashicorp/vault/sdk v0.1.14-0.20200519221838-e0cfd64bc267/go.mod h1:W github.com/hashicorp/vic v1.5.1-0.20190403131502-bbfe86ec9443 h1:O/pT5C1Q3mVXMyuqg7yuAWUg/jMZR1/0QTzTRdNR6Uw= github.com/hashicorp/vic v1.5.1-0.20190403131502-bbfe86ec9443/go.mod h1:bEpDU35nTu0ey1EXjwNwPjI9xErAsoOCmcMb9GKvyxo= github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= -github.com/hashicorp/yamux v0.0.0-20210826001029-26ff87cf9493 h1:brI5vBRUlAlM34VFmnLPwjnCL/FxAJp9XvOdX6Zt+XE= -github.com/hashicorp/yamux v0.0.0-20210826001029-26ff87cf9493/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= +github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 h1:xixZ2bWeofWV68J+x6AzmKuVM/JWCQwkWm6GW/MUR6I= +github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= @@ -407,7 +559,10 @@ github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9Y github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/joyent/triton-go v0.0.0-20180628001255-830d2b111e62/go.mod h1:U+RSyWxWd04xTqnuOQxnai7XGS2PrPY2cfGoDKtMHjA= github.com/joyent/triton-go v1.7.1-0.20200416154420-6801d15b779f h1:ENpDacvnr8faw5ugQmEF1QYk+f/Y9lXFvuYmRxykago= github.com/joyent/triton-go v1.7.1-0.20200416154420-6801d15b779f/go.mod h1:KDSfL7qe5ZfQqvlDMkVjCztbmcpp/c8M77vhQP8ZPvk= @@ -418,10 +573,13 @@ github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= +github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -431,6 +589,7 @@ github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= @@ -441,16 +600,28 @@ github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= +github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= +github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= -github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= @@ -461,6 +632,8 @@ github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJys github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/cli v1.1.0 h1:tEElEatulEHDeedTxwckzyYMA5c86fbmNIUL1hBIiTg= github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= +github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= +github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= @@ -477,6 +650,8 @@ github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/z github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/pointerstructure v1.2.1 h1:ZhBBeX8tSlRpu/FFhXH4RC4OJzFlqsQhoHZAz4x7TIw= @@ -490,11 +665,14 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2 h1:BQ1HW7hr4IVovMwWg0E0PYcyW8CzqDcVmaew9cujU4s= github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2/go.mod h1:TLb2Sg7HQcgGdloNxkrmtgDNR9uVYF3lfdFIN4Ro6Sk= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20180130162743-b8a9be070da4/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= @@ -510,7 +688,10 @@ github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0Mw github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= +github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/lz4 v2.5.2+incompatible h1:WCjObylUIOlKy/+7Abdn34TLIkXiA4UWUMhxq9m9ZXI= @@ -558,6 +739,8 @@ github.com/renier/xmlrpc v0.0.0-20170708154548-ce4a1a486c03 h1:Wdi9nwnhFNAlseAOe github.com/renier/xmlrpc v0.0.0-20170708154548-ce4a1a486c03/go.mod h1:gRAiPF5C5Nd0eyyRdqIu9qTiFSoZzpTq727b5B8fkkU= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rs/zerolog v1.4.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= @@ -572,13 +755,18 @@ github.com/sean-/conswriter v0.0.0-20180208195008-f5ae3917a627/go.mod h1:7zjs06q github.com/sean-/pager v0.0.0-20180208200047-666be9bf53b5/go.mod h1:BeybITEsBEg6qbIiqJ6/Bqeq25bCLbL7YFmpaFfJDuM= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shirou/gopsutil/v3 v3.22.8 h1:a4s3hXogo5mE2PfdfJIonDbstO/P+9JszdfhAHSzD9Y= github.com/shirou/gopsutil/v3 v3.22.8/go.mod h1:s648gW4IywYzUfE/KjXxUsqrqx/T2xO5VqOXxONeRfI= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA= +github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= github.com/softlayer/softlayer-go v0.0.0-20180806151055-260589d94c7d h1:bVQRCxQvfjNUeRqaY/uT0tFuvuFY0ulgnczuR684Xic= github.com/softlayer/softlayer-go v0.0.0-20180806151055-260589d94c7d/go.mod h1:Cw4GTlQccdRGSEf6KiMju767x0NEHE0YIVPJSaXjlsw= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= @@ -587,6 +775,7 @@ github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B github.com/spf13/afero v1.2.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= @@ -597,6 +786,7 @@ github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DM github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -606,10 +796,13 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/tencentcloud/tencentcloud-sdk-go v1.0.162 h1:8fDzz4GuVg4skjY2B0nMN7h6uN61EDVkuLyI2+qGHhI= github.com/tencentcloud/tencentcloud-sdk-go v1.0.162/go.mod h1:asUz5BPXxgoPGaRgZaVm1iGcUAuHyYUo1nXqKa83cvI= +github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03OUqALw= github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk= github.com/tklauser/numcpus v0.4.0 h1:E53Dm1HjH1/R2/aoCtXtPgzmElmn51aOkhCFSuZq//o= @@ -619,23 +812,36 @@ github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926 h1:G3dpKMzFDjgEh2q1Z github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= github.com/vmware/govmomi v0.18.0 h1:f7QxSmP7meCtoAmiKZogvVbLInT+CZx6Px6K5rYsJZo= github.com/vmware/govmomi v0.18.0/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU= +github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= +github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg= github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= +go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.3.0/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE= +go.mongodb.org/mongo-driver v1.3.4/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE= +go.mongodb.org/mongo-driver v1.4.3/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc= +go.mongodb.org/mongo-driver v1.4.4/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc= +go.mongodb.org/mongo-driver v1.4.6 h1:rh7GdYmDrb8AQSkF8yteAus8qYOgOASWDOv1BWqBXkU= +go.mongodb.org/mongo-driver v1.4.6/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3 h1:8sGtKOrtQqkN1bp2AtX+misvLIlOmsEsNd+9NIcPEm8= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4 h1:LYy1Hy3MJdrCdMwwzxA/dRok4ejH+RwNGbuoD9fCjto= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opentelemetry.io/proto/otlp v0.7.0 h1:rwOQPCuKAKmwGKq2aVNnYIibI6wnV7EvzgfTCzcdGg8= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -648,10 +854,14 @@ golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -694,6 +904,7 @@ golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -701,6 +912,7 @@ golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -708,7 +920,9 @@ golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -723,8 +937,14 @@ golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.0.0-20211216030914-fe4d6282115f h1:hEYJvxw1lSnWIl8X9ofsYMklzaDs90JI2az5YMd4fPM= @@ -733,15 +953,18 @@ golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAG golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58 h1:Mj83v+wSRNEar42a/MQgxk9X42TdEmrOl9i+y8WbxLo= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -759,14 +982,18 @@ golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190523142557-0e01d883c5c5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -791,6 +1018,7 @@ golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -799,8 +1027,10 @@ golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -812,6 +1042,8 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -825,14 +1057,22 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190424220101-1e8e1cfdf96b/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= @@ -862,8 +1102,11 @@ golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWc golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200622203043-20e05c1c8ffa/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= @@ -884,8 +1127,10 @@ google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/ google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0 h1:jMF5hhVfMkTZwHW1SDpKq5CkgWLXOb31Foaca9Zr3oM= google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0 h1:yfrXXP61wVuLb0vBcG6qaOoIoqYEzOQS8jum51jkv2w= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -919,8 +1164,11 @@ google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200623002339-fbb79eadd5eb h1:PUcq6RTy8Gp9xukBme8m2+2Z8pQCmJ7TbPpQd6xNDvk= -google.golang.org/genproto v0.0.0-20200623002339-fbb79eadd5eb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987 h1:PDIOdWxZ8eRizhKa1AAvY53xsvLB1cWorMjslvY3VA8= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -934,6 +1182,8 @@ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8 google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.37.1 h1:ARnQJNWxGyYJpdf/JXscNlQr/uv607ZPU9Z7ogHi+iI= @@ -955,8 +1205,9 @@ gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= @@ -973,9 +1224,14 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/lib/retry/retry.go b/lib/retry/retry.go index 59a979fbcc..8f52768d8a 100644 --- a/lib/retry/retry.go +++ b/lib/retry/retry.go @@ -108,3 +108,10 @@ func (w *Waiter) Wait(ctx context.Context) error { return nil } } + +// NextWait returns the period the next call to Wait with block for assuming +// it's context is not cancelled. It's useful for informing a user how long +// it will be before the next attempt is made. +func (w *Waiter) NextWait() time.Duration { + return w.delay() +}