mirror of https://github.com/hashicorp/consul
[CC-7049] Stop the HCP manager when link is deleted (#20351)
* Add Stop method to telemetry provider Stop the main loop of the provider and set the config to disabled. * Add interface for telemetry provider Added for easier testing. Also renamed Run to Start, which better fits with Stop. * Add Stop method to HCP manager * Add manager interface, rename implementation Add interface for easier testing, rename existing Manager to HCPManager. * Stop HCP manager in link Finalizer * Attempt to cleanup if resource has been deleted The link should be cleaned up by the finalizer, but there's an edge case in a multi-server setup where the link is fully deleted on one server before the other server reconciles. This will cover the case where the reconcile happens after the resource is deleted. * Add a delete mananagement token function Passes a function to the HCP manager that deletes the management token that was initially created by the manager. * Delete token as part of stopping the manager * Lock around disabling config, remove descriptionspull/20376/head^2
parent
b9ebaeca1c
commit
b0e87dbe13
|
@ -635,6 +635,38 @@ func (s *Server) upsertManagementToken(name, secretID string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) deleteManagementToken(secretId string) error {
|
||||||
|
state := s.fsm.State()
|
||||||
|
|
||||||
|
// Fetch the token to get its accessor ID and to verify that it's a management token
|
||||||
|
_, token, err := state.ACLTokenGetBySecret(nil, secretId, nil)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get management token: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if token == nil {
|
||||||
|
// token is already deleted
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
accessorID := token.AccessorID
|
||||||
|
if len(token.Policies) != 1 && token.Policies[0].ID != structs.ACLPolicyGlobalManagementID {
|
||||||
|
return fmt.Errorf("failed to delete management token: not a management token")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete the token
|
||||||
|
req := structs.ACLTokenBatchDeleteRequest{
|
||||||
|
TokenIDs: []string{accessorID},
|
||||||
|
}
|
||||||
|
if _, err := s.raftApply(structs.ACLTokenDeleteRequestType, &req); err != nil {
|
||||||
|
return fmt.Errorf("failed to delete management token: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.logger.Info("deleted ACL token", "description", token.Description)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) insertAnonymousToken() error {
|
func (s *Server) insertAnonymousToken() error {
|
||||||
state := s.fsm.State()
|
state := s.fsm.State()
|
||||||
_, token, err := state.ACLTokenGetBySecret(nil, anonymousToken, nil)
|
_, token, err := state.ACLTokenGetBySecret(nil, anonymousToken, nil)
|
||||||
|
|
|
@ -457,7 +457,7 @@ type Server struct {
|
||||||
xdsCapacityController *xdscapacity.Controller
|
xdsCapacityController *xdscapacity.Controller
|
||||||
|
|
||||||
// hcpManager handles pushing server status updates to the HashiCorp Cloud Platform when enabled
|
// hcpManager handles pushing server status updates to the HashiCorp Cloud Platform when enabled
|
||||||
hcpManager *hcp.Manager
|
hcpManager *hcp.HCPManager
|
||||||
|
|
||||||
// embedded struct to hold all the enterprise specific data
|
// embedded struct to hold all the enterprise specific data
|
||||||
EnterpriseServer
|
EnterpriseServer
|
||||||
|
@ -611,6 +611,14 @@ func NewServer(config *Config, flat Deps, externalGRPCServer *grpc.Server,
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
|
ManagementTokenDeleterFn: func(secretId string) error {
|
||||||
|
// Check the state of the server before attempting to delete the token.Otherwise,
|
||||||
|
// the delete will fail and log errors that do not require action from the user.
|
||||||
|
if s.config.ACLsEnabled && s.IsLeader() && s.InPrimaryDatacenter() {
|
||||||
|
return s.deleteManagementToken(secretId)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
var recorder *middleware.RequestRecorder
|
var recorder *middleware.RequestRecorder
|
||||||
|
|
|
@ -2139,6 +2139,17 @@ func TestServer_hcpManager(t *testing.T) {
|
||||||
require.NoError(r, err)
|
require.NoError(r, err)
|
||||||
require.NotNil(r, createdToken)
|
require.NotNil(r, createdToken)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Stop the HCP manager
|
||||||
|
err = s1.hcpManager.Stop()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Validate that the HCP token has been deleted as expected
|
||||||
|
retry.Run(t, func(r *retry.R) {
|
||||||
|
_, createdToken, err := s1.fsm.State().ACLTokenGetBySecret(nil, token, nil)
|
||||||
|
require.NoError(r, err)
|
||||||
|
require.Nil(r, createdToken)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestServer_addServerTLSInfo(t *testing.T) {
|
func TestServer_addServerTLSInfo(t *testing.T) {
|
||||||
|
|
|
@ -5,6 +5,7 @@ package hcp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"reflect"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -20,16 +21,19 @@ var (
|
||||||
defaultManagerMaxInterval = 75 * time.Minute
|
defaultManagerMaxInterval = 75 * time.Minute
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var _ Manager = (*HCPManager)(nil)
|
||||||
|
|
||||||
type ManagerConfig struct {
|
type ManagerConfig struct {
|
||||||
Client hcpclient.Client
|
Client hcpclient.Client
|
||||||
CloudConfig config.CloudConfig
|
CloudConfig config.CloudConfig
|
||||||
SCADAProvider scada.Provider
|
SCADAProvider scada.Provider
|
||||||
TelemetryProvider *hcpProviderImpl
|
TelemetryProvider TelemetryProvider
|
||||||
|
|
||||||
StatusFn StatusCallback
|
StatusFn StatusCallback
|
||||||
// Idempotent function to upsert the HCP management token. This will be called periodically in
|
// Idempotent function to upsert the HCP management token. This will be called periodically in
|
||||||
// the manager's main loop.
|
// the manager's main loop.
|
||||||
ManagementTokenUpserterFn ManagementTokenUpserter
|
ManagementTokenUpserterFn ManagementTokenUpserter
|
||||||
|
ManagementTokenDeleterFn ManagementTokenDeleter
|
||||||
MinInterval time.Duration
|
MinInterval time.Duration
|
||||||
MaxInterval time.Duration
|
MaxInterval time.Duration
|
||||||
|
|
||||||
|
@ -58,8 +62,17 @@ func (cfg *ManagerConfig) nextHeartbeat() time.Duration {
|
||||||
|
|
||||||
type StatusCallback func(context.Context) (hcpclient.ServerStatus, error)
|
type StatusCallback func(context.Context) (hcpclient.ServerStatus, error)
|
||||||
type ManagementTokenUpserter func(name, secretId string) error
|
type ManagementTokenUpserter func(name, secretId string) error
|
||||||
|
type ManagementTokenDeleter func(secretId string) error
|
||||||
|
|
||||||
type Manager struct {
|
//go:generate mockery --name Manager --with-expecter --inpackage
|
||||||
|
type Manager interface {
|
||||||
|
Start(context.Context) error
|
||||||
|
Stop() error
|
||||||
|
GetCloudConfig() config.CloudConfig
|
||||||
|
UpdateConfig(hcpclient.Client, config.CloudConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
type HCPManager struct {
|
||||||
logger hclog.Logger
|
logger hclog.Logger
|
||||||
|
|
||||||
running bool
|
running bool
|
||||||
|
@ -69,14 +82,15 @@ type Manager struct {
|
||||||
cfgMu sync.RWMutex
|
cfgMu sync.RWMutex
|
||||||
|
|
||||||
updateCh chan struct{}
|
updateCh chan struct{}
|
||||||
|
stopCh chan struct{}
|
||||||
|
|
||||||
// testUpdateSent is set by unit tests to signal when the manager's status update has triggered
|
// testUpdateSent is set by unit tests to signal when the manager's status update has triggered
|
||||||
testUpdateSent chan struct{}
|
testUpdateSent chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewManager returns a Manager initialized with the given configuration.
|
// NewManager returns a Manager initialized with the given configuration.
|
||||||
func NewManager(cfg ManagerConfig) *Manager {
|
func NewManager(cfg ManagerConfig) *HCPManager {
|
||||||
return &Manager{
|
return &HCPManager{
|
||||||
logger: cfg.Logger,
|
logger: cfg.Logger,
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
|
|
||||||
|
@ -86,7 +100,7 @@ func NewManager(cfg ManagerConfig) *Manager {
|
||||||
|
|
||||||
// Start executes the logic for connecting to HCP and sending periodic server updates. If the
|
// Start executes the logic for connecting to HCP and sending periodic server updates. If the
|
||||||
// manager has been previously started, it will not start again.
|
// manager has been previously started, it will not start again.
|
||||||
func (m *Manager) Start(ctx context.Context) error {
|
func (m *HCPManager) Start(ctx context.Context) error {
|
||||||
// Check if the manager has already started
|
// Check if the manager has already started
|
||||||
changed := m.setRunning(true)
|
changed := m.setRunning(true)
|
||||||
if !changed {
|
if !changed {
|
||||||
|
@ -117,6 +131,8 @@ func (m *Manager) Start(ctx context.Context) error {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
m.setRunning(false)
|
m.setRunning(false)
|
||||||
return nil
|
return nil
|
||||||
|
case <-m.stopCh:
|
||||||
|
return nil
|
||||||
case <-m.updateCh: // empty the update chan if there is a queued update to prevent repeated update in main loop
|
case <-m.updateCh: // empty the update chan if there is a queued update to prevent repeated update in main loop
|
||||||
err = m.sendUpdate()
|
err = m.sendUpdate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -158,6 +174,9 @@ func (m *Manager) Start(ctx context.Context) error {
|
||||||
m.setRunning(false)
|
m.setRunning(false)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
case <-m.stopCh:
|
||||||
|
return
|
||||||
|
|
||||||
case <-m.updateCh:
|
case <-m.updateCh:
|
||||||
err = m.sendUpdate()
|
err = m.sendUpdate()
|
||||||
|
|
||||||
|
@ -170,7 +189,7 @@ func (m *Manager) Start(ctx context.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) startSCADAProvider() error {
|
func (m *HCPManager) startSCADAProvider() error {
|
||||||
provider := m.cfg.SCADAProvider
|
provider := m.cfg.SCADAProvider
|
||||||
if provider == nil {
|
if provider == nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -197,12 +216,12 @@ func (m *Manager) startSCADAProvider() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) startTelemetryProvider(ctx context.Context) error {
|
func (m *HCPManager) startTelemetryProvider(ctx context.Context) error {
|
||||||
if m.cfg.TelemetryProvider == nil {
|
if m.cfg.TelemetryProvider == nil || reflect.ValueOf(m.cfg.TelemetryProvider).IsNil() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
m.cfg.TelemetryProvider.Run(ctx, &HCPProviderCfg{
|
m.cfg.TelemetryProvider.Start(ctx, &HCPProviderCfg{
|
||||||
HCPClient: m.cfg.Client,
|
HCPClient: m.cfg.Client,
|
||||||
HCPConfig: &m.cfg.CloudConfig,
|
HCPConfig: &m.cfg.CloudConfig,
|
||||||
})
|
})
|
||||||
|
@ -210,14 +229,14 @@ func (m *Manager) startTelemetryProvider(ctx context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) GetCloudConfig() config.CloudConfig {
|
func (m *HCPManager) GetCloudConfig() config.CloudConfig {
|
||||||
m.cfgMu.RLock()
|
m.cfgMu.RLock()
|
||||||
defer m.cfgMu.RUnlock()
|
defer m.cfgMu.RUnlock()
|
||||||
|
|
||||||
return m.cfg.CloudConfig
|
return m.cfg.CloudConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) UpdateConfig(client hcpclient.Client, cloudCfg config.CloudConfig) {
|
func (m *HCPManager) UpdateConfig(client hcpclient.Client, cloudCfg config.CloudConfig) {
|
||||||
m.cfgMu.Lock()
|
m.cfgMu.Lock()
|
||||||
// Save original values
|
// Save original values
|
||||||
originalCfg := m.cfg.CloudConfig
|
originalCfg := m.cfg.CloudConfig
|
||||||
|
@ -234,7 +253,7 @@ func (m *Manager) UpdateConfig(client hcpclient.Client, cloudCfg config.CloudCon
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) SendUpdate() {
|
func (m *HCPManager) SendUpdate() {
|
||||||
m.logger.Debug("HCP triggering status update")
|
m.logger.Debug("HCP triggering status update")
|
||||||
select {
|
select {
|
||||||
case m.updateCh <- struct{}{}:
|
case m.updateCh <- struct{}{}:
|
||||||
|
@ -252,7 +271,7 @@ func (m *Manager) SendUpdate() {
|
||||||
// and a "isRetrying" state or something so that we attempt to send update, but
|
// 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
|
// 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.
|
// backoff a new push is a no-op.
|
||||||
func (m *Manager) sendUpdate() error {
|
func (m *HCPManager) sendUpdate() error {
|
||||||
m.cfgMu.RLock()
|
m.cfgMu.RLock()
|
||||||
cfg := m.cfg
|
cfg := m.cfg
|
||||||
m.cfgMu.RUnlock()
|
m.cfgMu.RUnlock()
|
||||||
|
@ -281,7 +300,7 @@ func (m *Manager) sendUpdate() error {
|
||||||
return cfg.Client.PushServerStatus(ctx, &s)
|
return cfg.Client.PushServerStatus(ctx, &s)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) isRunning() bool {
|
func (m *HCPManager) isRunning() bool {
|
||||||
m.runLock.RLock()
|
m.runLock.RLock()
|
||||||
defer m.runLock.RUnlock()
|
defer m.runLock.RUnlock()
|
||||||
return m.running
|
return m.running
|
||||||
|
@ -290,7 +309,7 @@ func (m *Manager) isRunning() bool {
|
||||||
// setRunning sets the running status of the manager to the given value. If the
|
// setRunning sets the running status of the manager to the given value. If the
|
||||||
// given value is the same as the current running status, it returns false. If
|
// given value is the same as the current running status, it returns false. If
|
||||||
// current status is updated to the given status, it returns true.
|
// current status is updated to the given status, it returns true.
|
||||||
func (m *Manager) setRunning(r bool) bool {
|
func (m *HCPManager) setRunning(r bool) bool {
|
||||||
m.runLock.Lock()
|
m.runLock.Lock()
|
||||||
defer m.runLock.Unlock()
|
defer m.runLock.Unlock()
|
||||||
|
|
||||||
|
@ -298,6 +317,47 @@ func (m *Manager) setRunning(r bool) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize or close the stop channel depending what running status
|
||||||
|
// we're transitioning to. Channel must be initialized on start since
|
||||||
|
// a provider can be stopped and started multiple times.
|
||||||
|
if r {
|
||||||
|
m.stopCh = make(chan struct{})
|
||||||
|
} else {
|
||||||
|
close(m.stopCh)
|
||||||
|
}
|
||||||
|
|
||||||
m.running = r
|
m.running = r
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Stop stops the manager's main loop that sends updates
|
||||||
|
// and stops the SCADA provider and telemetry provider.
|
||||||
|
func (m *HCPManager) Stop() error {
|
||||||
|
changed := m.setRunning(false)
|
||||||
|
if !changed {
|
||||||
|
m.logger.Trace("HCP manager already stopped")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
m.logger.Info("HCP manager stopping")
|
||||||
|
|
||||||
|
m.cfgMu.RLock()
|
||||||
|
defer m.cfgMu.RUnlock()
|
||||||
|
|
||||||
|
if m.cfg.SCADAProvider != nil {
|
||||||
|
m.cfg.SCADAProvider.Stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.cfg.TelemetryProvider != nil && !reflect.ValueOf(m.cfg.TelemetryProvider).IsNil() {
|
||||||
|
m.cfg.TelemetryProvider.Stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.cfg.ManagementTokenDeleterFn != nil && m.cfg.CloudConfig.ManagementToken != "" {
|
||||||
|
err := m.cfg.ManagementTokenDeleterFn(m.cfg.CloudConfig.ManagementToken)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m.logger.Info("HCP manager stopped")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
package hcp
|
package hcp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
@ -11,6 +12,7 @@ import (
|
||||||
hcpclient "github.com/hashicorp/consul/agent/hcp/client"
|
hcpclient "github.com/hashicorp/consul/agent/hcp/client"
|
||||||
"github.com/hashicorp/consul/agent/hcp/config"
|
"github.com/hashicorp/consul/agent/hcp/config"
|
||||||
"github.com/hashicorp/consul/agent/hcp/scada"
|
"github.com/hashicorp/consul/agent/hcp/scada"
|
||||||
|
"github.com/hashicorp/consul/sdk/testutil"
|
||||||
"github.com/hashicorp/go-hclog"
|
"github.com/hashicorp/go-hclog"
|
||||||
"github.com/stretchr/testify/mock"
|
"github.com/stretchr/testify/mock"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
@ -31,7 +33,7 @@ func TestManager_Start(t *testing.T) {
|
||||||
client.EXPECT().PushServerStatus(mock.Anything, &hcpclient.ServerStatus{ID: t.Name()}).Return(nil).Once()
|
client.EXPECT().PushServerStatus(mock.Anything, &hcpclient.ServerStatus{ID: t.Name()}).Return(nil).Once()
|
||||||
|
|
||||||
cloudCfg := config.CloudConfig{
|
cloudCfg := config.CloudConfig{
|
||||||
ResourceID: "organization/85702e73-8a3d-47dc-291c-379b783c5804/project/8c0547c0-10e8-1ea2-dffe-384bee8da634/hashicorp.consul.global-network-manager.cluster/test",
|
ResourceID: "resource-id",
|
||||||
NodeID: "node-1",
|
NodeID: "node-1",
|
||||||
ManagementToken: "fake-token",
|
ManagementToken: "fake-token",
|
||||||
}
|
}
|
||||||
|
@ -44,25 +46,18 @@ func TestManager_Start(t *testing.T) {
|
||||||
).Return().Once()
|
).Return().Once()
|
||||||
scadaM.EXPECT().Start().Return(nil)
|
scadaM.EXPECT().Start().Return(nil)
|
||||||
|
|
||||||
telemetryProvider := &hcpProviderImpl{
|
telemetryM := NewMockTelemetryProvider(t)
|
||||||
httpCfg: &httpCfg{},
|
telemetryM.EXPECT().Start(mock.Anything, &HCPProviderCfg{
|
||||||
logger: hclog.New(&hclog.LoggerOptions{Output: io.Discard}),
|
HCPClient: client,
|
||||||
cfg: defaultDisabledCfg(),
|
HCPConfig: &cloudCfg,
|
||||||
}
|
}).Return(nil).Once()
|
||||||
|
|
||||||
mockTelemetryCfg, err := testTelemetryCfg(&testConfig{
|
|
||||||
refreshInterval: 1 * time.Second,
|
|
||||||
})
|
|
||||||
require.NoError(t, err)
|
|
||||||
client.EXPECT().FetchTelemetryConfig(mock.Anything).Return(
|
|
||||||
mockTelemetryCfg, nil).Maybe()
|
|
||||||
|
|
||||||
mgr := NewManager(ManagerConfig{
|
mgr := NewManager(ManagerConfig{
|
||||||
Logger: hclog.New(&hclog.LoggerOptions{Output: io.Discard}),
|
Logger: hclog.New(&hclog.LoggerOptions{Output: io.Discard}),
|
||||||
StatusFn: statusF,
|
StatusFn: statusF,
|
||||||
ManagementTokenUpserterFn: upsertManagementTokenF,
|
ManagementTokenUpserterFn: upsertManagementTokenF,
|
||||||
SCADAProvider: scadaM,
|
SCADAProvider: scadaM,
|
||||||
TelemetryProvider: telemetryProvider,
|
TelemetryProvider: telemetryM,
|
||||||
})
|
})
|
||||||
mgr.testUpdateSent = updateCh
|
mgr.testUpdateSent = updateCh
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
@ -85,9 +80,6 @@ func TestManager_Start(t *testing.T) {
|
||||||
// Make sure after manager has stopped no more statuses are pushed.
|
// Make sure after manager has stopped no more statuses are pushed.
|
||||||
cancel()
|
cancel()
|
||||||
client.AssertExpectations(t)
|
client.AssertExpectations(t)
|
||||||
require.Equal(t, client, telemetryProvider.hcpClient)
|
|
||||||
require.NotNil(t, telemetryProvider.GetHeader())
|
|
||||||
require.NotNil(t, telemetryProvider.GetHTTPClient())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestManager_StartMultipleTimes(t *testing.T) {
|
func TestManager_StartMultipleTimes(t *testing.T) {
|
||||||
|
@ -270,3 +262,103 @@ func TestManager_SendUpdate_Periodic(t *testing.T) {
|
||||||
}
|
}
|
||||||
client.AssertExpectations(t)
|
client.AssertExpectations(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestManager_Stop(t *testing.T) {
|
||||||
|
client := hcpclient.NewMockClient(t)
|
||||||
|
|
||||||
|
// Configure status functions called in sendUpdate
|
||||||
|
statusF := func(ctx context.Context) (hcpclient.ServerStatus, error) {
|
||||||
|
return hcpclient.ServerStatus{ID: t.Name()}, nil
|
||||||
|
}
|
||||||
|
updateCh := make(chan struct{}, 1)
|
||||||
|
client.EXPECT().PushServerStatus(mock.Anything, &hcpclient.ServerStatus{ID: t.Name()}).Return(nil).Twice()
|
||||||
|
|
||||||
|
// Configure management token creation and cleanup
|
||||||
|
token := "test-token"
|
||||||
|
upsertManagementTokenCalled := make(chan struct{}, 1)
|
||||||
|
upsertManagementTokenF := func(name, secretID string) error {
|
||||||
|
upsertManagementTokenCalled <- struct{}{}
|
||||||
|
if secretID != token {
|
||||||
|
return fmt.Errorf("expected token %q, got %q", token, secretID)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
deleteManagementTokenCalled := make(chan struct{}, 1)
|
||||||
|
deleteManagementTokenF := func(secretID string) error {
|
||||||
|
deleteManagementTokenCalled <- struct{}{}
|
||||||
|
if secretID != token {
|
||||||
|
return fmt.Errorf("expected token %q, got %q", token, secretID)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure the SCADA provider
|
||||||
|
scadaM := scada.NewMockProvider(t)
|
||||||
|
scadaM.EXPECT().UpdateHCPConfig(mock.Anything).Return(nil).Once()
|
||||||
|
scadaM.EXPECT().UpdateMeta(mock.Anything).Return().Once()
|
||||||
|
scadaM.EXPECT().Start().Return(nil).Once()
|
||||||
|
scadaM.EXPECT().Stop().Return(nil).Once()
|
||||||
|
|
||||||
|
// Configure the telemetry provider
|
||||||
|
telemetryM := NewMockTelemetryProvider(t)
|
||||||
|
telemetryM.EXPECT().Start(mock.Anything, mock.Anything).Return(nil).Once()
|
||||||
|
telemetryM.EXPECT().Stop().Return().Once()
|
||||||
|
|
||||||
|
// Configure manager with all its dependencies
|
||||||
|
mgr := NewManager(ManagerConfig{
|
||||||
|
Logger: testutil.Logger(t),
|
||||||
|
StatusFn: statusF,
|
||||||
|
Client: client,
|
||||||
|
ManagementTokenUpserterFn: upsertManagementTokenF,
|
||||||
|
ManagementTokenDeleterFn: deleteManagementTokenF,
|
||||||
|
SCADAProvider: scadaM,
|
||||||
|
TelemetryProvider: telemetryM,
|
||||||
|
CloudConfig: config.CloudConfig{
|
||||||
|
ManagementToken: token,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
mgr.testUpdateSent = updateCh
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
t.Cleanup(cancel)
|
||||||
|
|
||||||
|
// Start the manager
|
||||||
|
err := mgr.Start(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
select {
|
||||||
|
case <-updateCh:
|
||||||
|
case <-time.After(time.Second):
|
||||||
|
require.Fail(t, "manager did not send update in expected time")
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-upsertManagementTokenCalled:
|
||||||
|
case <-time.After(time.Second):
|
||||||
|
require.Fail(t, "manager did not create token in expected time")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send an update to ensure the manager is running in its main loop
|
||||||
|
mgr.SendUpdate()
|
||||||
|
select {
|
||||||
|
case <-updateCh:
|
||||||
|
case <-time.After(time.Second):
|
||||||
|
require.Fail(t, "manager did not send update in expected time")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop the manager
|
||||||
|
err = mgr.Stop()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Validate that the management token delete function is called
|
||||||
|
select {
|
||||||
|
case <-deleteManagementTokenCalled:
|
||||||
|
case <-time.After(time.Millisecond * 100):
|
||||||
|
require.Fail(t, "manager did not create token in expected time")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send an update, expect no update since manager is stopped
|
||||||
|
mgr.SendUpdate()
|
||||||
|
select {
|
||||||
|
case <-updateCh:
|
||||||
|
require.Fail(t, "manager sent update after stopped")
|
||||||
|
case <-time.After(time.Second):
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,209 @@
|
||||||
|
// Code generated by mockery v2.38.0. DO NOT EDIT.
|
||||||
|
|
||||||
|
package hcp
|
||||||
|
|
||||||
|
import (
|
||||||
|
client "github.com/hashicorp/consul/agent/hcp/client"
|
||||||
|
config "github.com/hashicorp/consul/agent/hcp/config"
|
||||||
|
|
||||||
|
context "context"
|
||||||
|
|
||||||
|
mock "github.com/stretchr/testify/mock"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MockManager is an autogenerated mock type for the Manager type
|
||||||
|
type MockManager struct {
|
||||||
|
mock.Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
type MockManager_Expecter struct {
|
||||||
|
mock *mock.Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_m *MockManager) EXPECT() *MockManager_Expecter {
|
||||||
|
return &MockManager_Expecter{mock: &_m.Mock}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCloudConfig provides a mock function with given fields:
|
||||||
|
func (_m *MockManager) GetCloudConfig() config.CloudConfig {
|
||||||
|
ret := _m.Called()
|
||||||
|
|
||||||
|
if len(ret) == 0 {
|
||||||
|
panic("no return value specified for GetCloudConfig")
|
||||||
|
}
|
||||||
|
|
||||||
|
var r0 config.CloudConfig
|
||||||
|
if rf, ok := ret.Get(0).(func() config.CloudConfig); ok {
|
||||||
|
r0 = rf()
|
||||||
|
} else {
|
||||||
|
r0 = ret.Get(0).(config.CloudConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockManager_GetCloudConfig_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetCloudConfig'
|
||||||
|
type MockManager_GetCloudConfig_Call struct {
|
||||||
|
*mock.Call
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCloudConfig is a helper method to define mock.On call
|
||||||
|
func (_e *MockManager_Expecter) GetCloudConfig() *MockManager_GetCloudConfig_Call {
|
||||||
|
return &MockManager_GetCloudConfig_Call{Call: _e.mock.On("GetCloudConfig")}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockManager_GetCloudConfig_Call) Run(run func()) *MockManager_GetCloudConfig_Call {
|
||||||
|
_c.Call.Run(func(args mock.Arguments) {
|
||||||
|
run()
|
||||||
|
})
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockManager_GetCloudConfig_Call) Return(_a0 config.CloudConfig) *MockManager_GetCloudConfig_Call {
|
||||||
|
_c.Call.Return(_a0)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockManager_GetCloudConfig_Call) RunAndReturn(run func() config.CloudConfig) *MockManager_GetCloudConfig_Call {
|
||||||
|
_c.Call.Return(run)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start provides a mock function with given fields: _a0
|
||||||
|
func (_m *MockManager) Start(_a0 context.Context) error {
|
||||||
|
ret := _m.Called(_a0)
|
||||||
|
|
||||||
|
if len(ret) == 0 {
|
||||||
|
panic("no return value specified for Start")
|
||||||
|
}
|
||||||
|
|
||||||
|
var r0 error
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context) error); ok {
|
||||||
|
r0 = rf(_a0)
|
||||||
|
} else {
|
||||||
|
r0 = ret.Error(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockManager_Start_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Start'
|
||||||
|
type MockManager_Start_Call struct {
|
||||||
|
*mock.Call
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start is a helper method to define mock.On call
|
||||||
|
// - _a0 context.Context
|
||||||
|
func (_e *MockManager_Expecter) Start(_a0 interface{}) *MockManager_Start_Call {
|
||||||
|
return &MockManager_Start_Call{Call: _e.mock.On("Start", _a0)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockManager_Start_Call) Run(run func(_a0 context.Context)) *MockManager_Start_Call {
|
||||||
|
_c.Call.Run(func(args mock.Arguments) {
|
||||||
|
run(args[0].(context.Context))
|
||||||
|
})
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockManager_Start_Call) Return(_a0 error) *MockManager_Start_Call {
|
||||||
|
_c.Call.Return(_a0)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockManager_Start_Call) RunAndReturn(run func(context.Context) error) *MockManager_Start_Call {
|
||||||
|
_c.Call.Return(run)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop provides a mock function with given fields:
|
||||||
|
func (_m *MockManager) Stop() error {
|
||||||
|
ret := _m.Called()
|
||||||
|
|
||||||
|
if len(ret) == 0 {
|
||||||
|
panic("no return value specified for Stop")
|
||||||
|
}
|
||||||
|
|
||||||
|
var r0 error
|
||||||
|
if rf, ok := ret.Get(0).(func() error); ok {
|
||||||
|
r0 = rf()
|
||||||
|
} else {
|
||||||
|
r0 = ret.Error(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockManager_Stop_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Stop'
|
||||||
|
type MockManager_Stop_Call struct {
|
||||||
|
*mock.Call
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop is a helper method to define mock.On call
|
||||||
|
func (_e *MockManager_Expecter) Stop() *MockManager_Stop_Call {
|
||||||
|
return &MockManager_Stop_Call{Call: _e.mock.On("Stop")}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockManager_Stop_Call) Run(run func()) *MockManager_Stop_Call {
|
||||||
|
_c.Call.Run(func(args mock.Arguments) {
|
||||||
|
run()
|
||||||
|
})
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockManager_Stop_Call) Return(_a0 error) *MockManager_Stop_Call {
|
||||||
|
_c.Call.Return(_a0)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockManager_Stop_Call) RunAndReturn(run func() error) *MockManager_Stop_Call {
|
||||||
|
_c.Call.Return(run)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateConfig provides a mock function with given fields: _a0, _a1
|
||||||
|
func (_m *MockManager) UpdateConfig(_a0 client.Client, _a1 config.CloudConfig) {
|
||||||
|
_m.Called(_a0, _a1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockManager_UpdateConfig_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateConfig'
|
||||||
|
type MockManager_UpdateConfig_Call struct {
|
||||||
|
*mock.Call
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateConfig is a helper method to define mock.On call
|
||||||
|
// - _a0 client.Client
|
||||||
|
// - _a1 config.CloudConfig
|
||||||
|
func (_e *MockManager_Expecter) UpdateConfig(_a0 interface{}, _a1 interface{}) *MockManager_UpdateConfig_Call {
|
||||||
|
return &MockManager_UpdateConfig_Call{Call: _e.mock.On("UpdateConfig", _a0, _a1)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockManager_UpdateConfig_Call) Run(run func(_a0 client.Client, _a1 config.CloudConfig)) *MockManager_UpdateConfig_Call {
|
||||||
|
_c.Call.Run(func(args mock.Arguments) {
|
||||||
|
run(args[0].(client.Client), args[1].(config.CloudConfig))
|
||||||
|
})
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockManager_UpdateConfig_Call) Return() *MockManager_UpdateConfig_Call {
|
||||||
|
_c.Call.Return()
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockManager_UpdateConfig_Call) RunAndReturn(run func(client.Client, config.CloudConfig)) *MockManager_UpdateConfig_Call {
|
||||||
|
_c.Call.Return(run)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockManager creates a new instance of MockManager. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||||
|
// The first argument is typically a *testing.T value.
|
||||||
|
func NewMockManager(t interface {
|
||||||
|
mock.TestingT
|
||||||
|
Cleanup(func())
|
||||||
|
}) *MockManager {
|
||||||
|
mock := &MockManager{}
|
||||||
|
mock.Mock.Test(t)
|
||||||
|
|
||||||
|
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||||
|
|
||||||
|
return mock
|
||||||
|
}
|
|
@ -0,0 +1,115 @@
|
||||||
|
// Code generated by mockery v2.38.0. DO NOT EDIT.
|
||||||
|
|
||||||
|
package hcp
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "context"
|
||||||
|
|
||||||
|
mock "github.com/stretchr/testify/mock"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MockTelemetryProvider is an autogenerated mock type for the TelemetryProvider type
|
||||||
|
type MockTelemetryProvider struct {
|
||||||
|
mock.Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
type MockTelemetryProvider_Expecter struct {
|
||||||
|
mock *mock.Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_m *MockTelemetryProvider) EXPECT() *MockTelemetryProvider_Expecter {
|
||||||
|
return &MockTelemetryProvider_Expecter{mock: &_m.Mock}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start provides a mock function with given fields: ctx, c
|
||||||
|
func (_m *MockTelemetryProvider) Start(ctx context.Context, c *HCPProviderCfg) error {
|
||||||
|
ret := _m.Called(ctx, c)
|
||||||
|
|
||||||
|
if len(ret) == 0 {
|
||||||
|
panic("no return value specified for Start")
|
||||||
|
}
|
||||||
|
|
||||||
|
var r0 error
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, *HCPProviderCfg) error); ok {
|
||||||
|
r0 = rf(ctx, c)
|
||||||
|
} else {
|
||||||
|
r0 = ret.Error(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockTelemetryProvider_Start_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Start'
|
||||||
|
type MockTelemetryProvider_Start_Call struct {
|
||||||
|
*mock.Call
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start is a helper method to define mock.On call
|
||||||
|
// - ctx context.Context
|
||||||
|
// - c *HCPProviderCfg
|
||||||
|
func (_e *MockTelemetryProvider_Expecter) Start(ctx interface{}, c interface{}) *MockTelemetryProvider_Start_Call {
|
||||||
|
return &MockTelemetryProvider_Start_Call{Call: _e.mock.On("Start", ctx, c)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockTelemetryProvider_Start_Call) Run(run func(ctx context.Context, c *HCPProviderCfg)) *MockTelemetryProvider_Start_Call {
|
||||||
|
_c.Call.Run(func(args mock.Arguments) {
|
||||||
|
run(args[0].(context.Context), args[1].(*HCPProviderCfg))
|
||||||
|
})
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockTelemetryProvider_Start_Call) Return(_a0 error) *MockTelemetryProvider_Start_Call {
|
||||||
|
_c.Call.Return(_a0)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockTelemetryProvider_Start_Call) RunAndReturn(run func(context.Context, *HCPProviderCfg) error) *MockTelemetryProvider_Start_Call {
|
||||||
|
_c.Call.Return(run)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop provides a mock function with given fields:
|
||||||
|
func (_m *MockTelemetryProvider) Stop() {
|
||||||
|
_m.Called()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockTelemetryProvider_Stop_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Stop'
|
||||||
|
type MockTelemetryProvider_Stop_Call struct {
|
||||||
|
*mock.Call
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop is a helper method to define mock.On call
|
||||||
|
func (_e *MockTelemetryProvider_Expecter) Stop() *MockTelemetryProvider_Stop_Call {
|
||||||
|
return &MockTelemetryProvider_Stop_Call{Call: _e.mock.On("Stop")}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockTelemetryProvider_Stop_Call) Run(run func()) *MockTelemetryProvider_Stop_Call {
|
||||||
|
_c.Call.Run(func(args mock.Arguments) {
|
||||||
|
run()
|
||||||
|
})
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockTelemetryProvider_Stop_Call) Return() *MockTelemetryProvider_Stop_Call {
|
||||||
|
_c.Call.Return()
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockTelemetryProvider_Stop_Call) RunAndReturn(run func()) *MockTelemetryProvider_Stop_Call {
|
||||||
|
_c.Call.Return(run)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockTelemetryProvider creates a new instance of MockTelemetryProvider. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||||
|
// The first argument is typically a *testing.T value.
|
||||||
|
func NewMockTelemetryProvider(t interface {
|
||||||
|
mock.TestingT
|
||||||
|
Cleanup(func())
|
||||||
|
}) *MockTelemetryProvider {
|
||||||
|
mock := &MockTelemetryProvider{}
|
||||||
|
mock.Mock.Test(t)
|
||||||
|
|
||||||
|
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||||
|
|
||||||
|
return mock
|
||||||
|
}
|
|
@ -35,6 +35,7 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Ensure hcpProviderImpl implements telemetry provider interfaces.
|
// Ensure hcpProviderImpl implements telemetry provider interfaces.
|
||||||
|
var _ TelemetryProvider = &hcpProviderImpl{}
|
||||||
var _ telemetry.ConfigProvider = &hcpProviderImpl{}
|
var _ telemetry.ConfigProvider = &hcpProviderImpl{}
|
||||||
var _ telemetry.EndpointProvider = &hcpProviderImpl{}
|
var _ telemetry.EndpointProvider = &hcpProviderImpl{}
|
||||||
var _ client.MetricsClientProvider = &hcpProviderImpl{}
|
var _ client.MetricsClientProvider = &hcpProviderImpl{}
|
||||||
|
@ -56,6 +57,9 @@ type hcpProviderImpl struct {
|
||||||
// running indicates if the HCP telemetry config provider has been started
|
// running indicates if the HCP telemetry config provider has been started
|
||||||
running bool
|
running bool
|
||||||
|
|
||||||
|
// stopCh is used to signal that the telemetry config provider should stop running.
|
||||||
|
stopCh chan struct{}
|
||||||
|
|
||||||
// hcpClient is an authenticated client used to make HTTP requests to HCP.
|
// hcpClient is an authenticated client used to make HTTP requests to HCP.
|
||||||
hcpClient client.Client
|
hcpClient client.Client
|
||||||
|
|
||||||
|
@ -94,6 +98,12 @@ type httpCfg struct {
|
||||||
client *retryablehttp.Client
|
client *retryablehttp.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:generate mockery --name TelemetryProvider --with-expecter --inpackage
|
||||||
|
type TelemetryProvider interface {
|
||||||
|
Start(ctx context.Context, c *HCPProviderCfg) error
|
||||||
|
Stop()
|
||||||
|
}
|
||||||
|
|
||||||
type HCPProviderCfg struct {
|
type HCPProviderCfg struct {
|
||||||
HCPClient client.Client
|
HCPClient client.Client
|
||||||
HCPConfig config.CloudConfigurer
|
HCPConfig config.CloudConfigurer
|
||||||
|
@ -111,9 +121,9 @@ func NewHCPProvider(ctx context.Context) *hcpProviderImpl {
|
||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run starts a process that continuously checks for updates to the telemetry configuration
|
// Start starts a process that continuously checks for updates to the telemetry configuration
|
||||||
// by making a request to HCP. It only starts running if it's not already running.
|
// by making a request to HCP. It only starts running if it's not already running.
|
||||||
func (h *hcpProviderImpl) Run(ctx context.Context, c *HCPProviderCfg) error {
|
func (h *hcpProviderImpl) Start(ctx context.Context, c *HCPProviderCfg) error {
|
||||||
changed := h.setRunning(true)
|
changed := h.setRunning(true)
|
||||||
if !changed {
|
if !changed {
|
||||||
// Provider is already running.
|
// Provider is already running.
|
||||||
|
@ -139,7 +149,7 @@ func (h *hcpProviderImpl) run(ctx context.Context) error {
|
||||||
// Try to initialize config once before starting periodic fetch.
|
// Try to initialize config once before starting periodic fetch.
|
||||||
h.updateConfig(ctx)
|
h.updateConfig(ctx)
|
||||||
|
|
||||||
ticker := time.NewTicker(h.cfg.refreshInterval)
|
ticker := time.NewTicker(h.getRefreshInterval())
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
|
@ -149,6 +159,8 @@ func (h *hcpProviderImpl) run(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return nil
|
return nil
|
||||||
|
case <-h.stopCh:
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -224,6 +236,13 @@ func (h *hcpProviderImpl) modifyDynamicCfg(newCfg *dynamicConfig) {
|
||||||
metrics.IncrCounter(internalMetricRefreshSuccess, 1)
|
metrics.IncrCounter(internalMetricRefreshSuccess, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *hcpProviderImpl) getRefreshInterval() time.Duration {
|
||||||
|
h.rw.RLock()
|
||||||
|
defer h.rw.RUnlock()
|
||||||
|
|
||||||
|
return h.cfg.refreshInterval
|
||||||
|
}
|
||||||
|
|
||||||
// GetEndpoint acquires a read lock to return endpoint configuration for consumers.
|
// GetEndpoint acquires a read lock to return endpoint configuration for consumers.
|
||||||
func (h *hcpProviderImpl) GetEndpoint() *url.URL {
|
func (h *hcpProviderImpl) GetEndpoint() *url.URL {
|
||||||
h.rw.RLock()
|
h.rw.RLock()
|
||||||
|
@ -322,7 +341,32 @@ func (h *hcpProviderImpl) setRunning(r bool) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize or close the stop channel depending what running status
|
||||||
|
// we're transitioning to. Channel must be initialized on start since
|
||||||
|
// a provider can be stopped and started multiple times.
|
||||||
|
if r {
|
||||||
|
h.stopCh = make(chan struct{})
|
||||||
|
} else {
|
||||||
|
close(h.stopCh)
|
||||||
|
}
|
||||||
|
|
||||||
h.running = r
|
h.running = r
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Stop acquires a write lock to mark the provider as not running and sends a stop signal to the
|
||||||
|
// main run loop. It also updates the provider with a disabled configuration.
|
||||||
|
func (h *hcpProviderImpl) Stop() {
|
||||||
|
changed := h.setRunning(false)
|
||||||
|
if !changed {
|
||||||
|
h.logger.Trace("telemetry config provider already stopped")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
h.rw.Lock()
|
||||||
|
h.cfg = defaultDisabledCfg()
|
||||||
|
h.rw.Unlock()
|
||||||
|
|
||||||
|
h.logger.Debug("telemetry config provider stopped")
|
||||||
|
}
|
||||||
|
|
|
@ -286,7 +286,7 @@ func TestTelemetryConfigProvider_UpdateConfig(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTelemetryConfigProvider_Run(t *testing.T) {
|
func TestTelemetryConfigProvider_Start(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
@ -311,22 +311,23 @@ func TestTelemetryConfigProvider_Run(t *testing.T) {
|
||||||
mockHCPCfg := &config.MockCloudCfg{}
|
mockHCPCfg := &config.MockCloudCfg{}
|
||||||
|
|
||||||
// Run provider
|
// Run provider
|
||||||
go provider.Run(context.Background(), &HCPProviderCfg{
|
go provider.Start(context.Background(), &HCPProviderCfg{
|
||||||
HCPClient: mockClient,
|
HCPClient: mockClient,
|
||||||
HCPConfig: mockHCPCfg,
|
HCPConfig: mockHCPCfg,
|
||||||
})
|
})
|
||||||
|
|
||||||
var count int
|
// Expect at least two update config calls to validate provider is running
|
||||||
|
// and has entered the main run loop
|
||||||
select {
|
select {
|
||||||
case <-testUpdateConfigCh:
|
case <-testUpdateConfigCh:
|
||||||
// Expect/wait for at least two update config calls
|
|
||||||
count++
|
|
||||||
if count > 2 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case <-time.After(time.Second):
|
case <-time.After(time.Second):
|
||||||
require.Fail(t, "provider did not attempt to update config in expected time")
|
require.Fail(t, "provider did not attempt to update config in expected time")
|
||||||
}
|
}
|
||||||
|
select {
|
||||||
|
case <-testUpdateConfigCh:
|
||||||
|
case <-time.After(time.Millisecond * 500):
|
||||||
|
require.Fail(t, "provider did not attempt to update config in expected time")
|
||||||
|
}
|
||||||
|
|
||||||
mockClient.AssertExpectations(t)
|
mockClient.AssertExpectations(t)
|
||||||
}
|
}
|
||||||
|
@ -351,11 +352,11 @@ func TestTelemetryConfigProvider_MultipleRun(t *testing.T) {
|
||||||
mockHCPCfg := &config.MockCloudCfg{}
|
mockHCPCfg := &config.MockCloudCfg{}
|
||||||
|
|
||||||
// Run provider twice in parallel
|
// Run provider twice in parallel
|
||||||
go provider.Run(context.Background(), &HCPProviderCfg{
|
go provider.Start(context.Background(), &HCPProviderCfg{
|
||||||
HCPClient: mockClient,
|
HCPClient: mockClient,
|
||||||
HCPConfig: mockHCPCfg,
|
HCPConfig: mockHCPCfg,
|
||||||
})
|
})
|
||||||
go provider.Run(context.Background(), &HCPProviderCfg{
|
go provider.Start(context.Background(), &HCPProviderCfg{
|
||||||
HCPClient: mockClient,
|
HCPClient: mockClient,
|
||||||
HCPConfig: mockHCPCfg,
|
HCPConfig: mockHCPCfg,
|
||||||
})
|
})
|
||||||
|
@ -374,7 +375,7 @@ func TestTelemetryConfigProvider_MultipleRun(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try calling run again, should not update again
|
// Try calling run again, should not update again
|
||||||
provider.Run(context.Background(), &HCPProviderCfg{
|
provider.Start(context.Background(), &HCPProviderCfg{
|
||||||
HCPClient: mockClient,
|
HCPClient: mockClient,
|
||||||
HCPConfig: mockHCPCfg,
|
HCPConfig: mockHCPCfg,
|
||||||
})
|
})
|
||||||
|
@ -435,6 +436,62 @@ func TestTelemetryConfigProvider_updateHTTPConfig(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTelemetryConfigProvider_Stop(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
provider := NewHCPProvider(ctx)
|
||||||
|
|
||||||
|
testUpdateConfigCh := make(chan struct{}, 1)
|
||||||
|
provider.testUpdateConfigCh = testUpdateConfigCh
|
||||||
|
|
||||||
|
// Configure mocks
|
||||||
|
mockClient := client.NewMockClient(t)
|
||||||
|
mTelemetryCfg, err := testTelemetryCfg(&testConfig{
|
||||||
|
endpoint: "http://test.com/v1/metrics",
|
||||||
|
filters: "test",
|
||||||
|
labels: map[string]string{
|
||||||
|
"test_label": "123",
|
||||||
|
},
|
||||||
|
refreshInterval: testRefreshInterval,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
mockClient.EXPECT().FetchTelemetryConfig(mock.Anything).Return(mTelemetryCfg, nil)
|
||||||
|
mockHCPCfg := &config.MockCloudCfg{}
|
||||||
|
|
||||||
|
// Run provider
|
||||||
|
provider.Start(context.Background(), &HCPProviderCfg{
|
||||||
|
HCPClient: mockClient,
|
||||||
|
HCPConfig: mockHCPCfg,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Wait for at least two update config calls to ensure provider is running
|
||||||
|
// and has entered the main run loop
|
||||||
|
select {
|
||||||
|
case <-testUpdateConfigCh:
|
||||||
|
case <-time.After(time.Second):
|
||||||
|
require.Fail(t, "provider did not attempt to update config in expected time")
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-testUpdateConfigCh:
|
||||||
|
case <-time.After(time.Millisecond * 500):
|
||||||
|
require.Fail(t, "provider did not attempt to update config in expected time")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop the provider
|
||||||
|
provider.Stop()
|
||||||
|
require.Equal(t, defaultDisabledCfg(), provider.cfg)
|
||||||
|
select {
|
||||||
|
case <-testUpdateConfigCh:
|
||||||
|
require.Fail(t, "provider should not attempt to update config after stop")
|
||||||
|
case <-time.After(time.Second):
|
||||||
|
// Success, no updates have happened after stopping
|
||||||
|
}
|
||||||
|
|
||||||
|
mockClient.AssertExpectations(t)
|
||||||
|
}
|
||||||
|
|
||||||
// mockRaceClient is a mock HCP client that fetches TelemetryConfig.
|
// mockRaceClient is a mock HCP client that fetches TelemetryConfig.
|
||||||
// The mock TelemetryConfig returned can be manually updated at any time.
|
// The mock TelemetryConfig returned can be manually updated at any time.
|
||||||
// It manages concurrent read/write access to config with a sync.RWMutex.
|
// It manages concurrent read/write access to config with a sync.RWMutex.
|
||||||
|
@ -504,7 +561,7 @@ func TestTelemetryConfigProvider_Race(t *testing.T) {
|
||||||
|
|
||||||
// Start the provider goroutine, which fetches client TelemetryConfig every RefreshInterval.
|
// Start the provider goroutine, which fetches client TelemetryConfig every RefreshInterval.
|
||||||
provider := NewHCPProvider(ctx)
|
provider := NewHCPProvider(ctx)
|
||||||
err = provider.Run(context.Background(), &HCPProviderCfg{m, config.MockCloudCfg{}})
|
err = provider.Start(context.Background(), &HCPProviderCfg{m, config.MockCloudCfg{}})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
for count := 0; count < testRaceWriteSampleCount; count++ {
|
for count := 0; count < testRaceWriteSampleCount; count++ {
|
||||||
|
|
|
@ -44,7 +44,7 @@ func LinkController(
|
||||||
hcpClientFn HCPClientFn,
|
hcpClientFn HCPClientFn,
|
||||||
cfg config.CloudConfig,
|
cfg config.CloudConfig,
|
||||||
dataDir string,
|
dataDir string,
|
||||||
hcpManager *hcp.Manager,
|
hcpManager hcp.Manager,
|
||||||
) *controller.Controller {
|
) *controller.Controller {
|
||||||
return controller.NewController("link", pbhcp.LinkType).
|
return controller.NewController("link", pbhcp.LinkType).
|
||||||
// Placement is configured to each server so that the HCP manager is started
|
// Placement is configured to each server so that the HCP manager is started
|
||||||
|
@ -70,7 +70,7 @@ type linkReconciler struct {
|
||||||
hcpAllowV2ResourceApis bool
|
hcpAllowV2ResourceApis bool
|
||||||
hcpClientFn HCPClientFn
|
hcpClientFn HCPClientFn
|
||||||
dataDir string
|
dataDir string
|
||||||
hcpManager *hcp.Manager
|
hcpManager hcp.Manager
|
||||||
}
|
}
|
||||||
|
|
||||||
func hcpAccessLevelToConsul(level *gnmmod.HashicorpCloudGlobalNetworkManager20220215ClusterConsulAccessLevel) pbhcp.AccessLevel {
|
func hcpAccessLevelToConsul(level *gnmmod.HashicorpCloudGlobalNetworkManager20220215ClusterConsulAccessLevel) pbhcp.AccessLevel {
|
||||||
|
@ -101,7 +101,7 @@ func (r *linkReconciler) Reconcile(ctx context.Context, rt controller.Runtime, r
|
||||||
switch {
|
switch {
|
||||||
case status.Code(err) == codes.NotFound:
|
case status.Code(err) == codes.NotFound:
|
||||||
rt.Logger.Trace("link has been deleted")
|
rt.Logger.Trace("link has been deleted")
|
||||||
return nil
|
return cleanup(rt, r.hcpManager, r.dataDir)
|
||||||
case err != nil:
|
case err != nil:
|
||||||
rt.Logger.Error("the resource service has returned an unexpected error", "error", err)
|
rt.Logger.Error("the resource service has returned an unexpected error", "error", err)
|
||||||
return err
|
return err
|
||||||
|
@ -120,10 +120,17 @@ func (r *linkReconciler) Reconcile(ctx context.Context, rt controller.Runtime, r
|
||||||
}
|
}
|
||||||
|
|
||||||
if resource.IsMarkedForDeletion(res) {
|
if resource.IsMarkedForDeletion(res) {
|
||||||
if err = cleanup(ctx, rt, res, r.dataDir); err != nil {
|
if err = cleanup(rt, r.hcpManager, r.dataDir); err != nil {
|
||||||
rt.Logger.Error("error cleaning up link resource", "error", err)
|
rt.Logger.Error("error cleaning up link resource", "error", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err := ensureDeleted(ctx, rt, res)
|
||||||
|
if err != nil {
|
||||||
|
rt.Logger.Error("error deleting link resource", "error", err)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,6 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/hashicorp/go-hclog"
|
|
||||||
"github.com/hashicorp/go-uuid"
|
"github.com/hashicorp/go-uuid"
|
||||||
gnmmod "github.com/hashicorp/hcp-sdk-go/clients/cloud-global-network-manager-service/preview/2022-02-15/models"
|
gnmmod "github.com/hashicorp/hcp-sdk-go/clients/cloud-global-network-manager-service/preview/2022-02-15/models"
|
||||||
"github.com/stretchr/testify/mock"
|
"github.com/stretchr/testify/mock"
|
||||||
|
@ -108,15 +107,11 @@ func (suite *controllerSuite) TestController_Ok() {
|
||||||
ConsulConfig: "{}",
|
ConsulConfig: "{}",
|
||||||
}, nil).Once()
|
}, nil).Once()
|
||||||
|
|
||||||
statusF := func(ctx context.Context) (hcpclient.ServerStatus, error) {
|
hcpMgr := hcp.NewMockManager(suite.T())
|
||||||
return hcpclient.ServerStatus{ID: suite.T().Name()}, nil
|
hcpMgr.EXPECT().GetCloudConfig().Return(config.CloudConfig{})
|
||||||
}
|
hcpMgr.EXPECT().UpdateConfig(mock.Anything, mock.Anything)
|
||||||
mockClient.EXPECT().PushServerStatus(mock.Anything, &hcpclient.ServerStatus{ID: suite.T().Name()}).
|
hcpMgr.EXPECT().Start(mock.Anything).Return(nil)
|
||||||
Return(nil).Once()
|
hcpMgr.EXPECT().Stop().Return(nil)
|
||||||
hcpMgr := hcp.NewManager(hcp.ManagerConfig{
|
|
||||||
Logger: hclog.NewNullLogger(),
|
|
||||||
StatusFn: statusF,
|
|
||||||
})
|
|
||||||
|
|
||||||
dataDir := testutil.TempDir(suite.T(), "test-link-controller")
|
dataDir := testutil.TempDir(suite.T(), "test-link-controller")
|
||||||
suite.dataDir = dataDir
|
suite.dataDir = dataDir
|
||||||
|
@ -173,15 +168,11 @@ func (suite *controllerSuite) TestController_Initialize() {
|
||||||
ResourceID: types.GenerateTestResourceID(suite.T()),
|
ResourceID: types.GenerateTestResourceID(suite.T()),
|
||||||
}
|
}
|
||||||
|
|
||||||
statusF := func(ctx context.Context) (hcpclient.ServerStatus, error) {
|
hcpMgr := hcp.NewMockManager(suite.T())
|
||||||
return hcpclient.ServerStatus{ID: suite.T().Name()}, nil
|
hcpMgr.EXPECT().GetCloudConfig().Return(cloudCfg)
|
||||||
}
|
hcpMgr.EXPECT().UpdateConfig(mock.Anything, mock.Anything)
|
||||||
mockClient.EXPECT().PushServerStatus(mock.Anything, &hcpclient.ServerStatus{ID: suite.T().Name()}).
|
hcpMgr.EXPECT().Start(mock.Anything).Return(nil)
|
||||||
Return(nil).Once()
|
hcpMgr.EXPECT().Stop().Return(nil)
|
||||||
hcpMgr := hcp.NewManager(hcp.ManagerConfig{
|
|
||||||
Logger: hclog.NewNullLogger(),
|
|
||||||
StatusFn: statusF,
|
|
||||||
})
|
|
||||||
|
|
||||||
dataDir := testutil.TempDir(suite.T(), "test-link-controller")
|
dataDir := testutil.TempDir(suite.T(), "test-link-controller")
|
||||||
suite.dataDir = dataDir
|
suite.dataDir = dataDir
|
||||||
|
@ -225,13 +216,16 @@ func (suite *controllerSuite) TestControllerResourceApisEnabled_LinkDisabled() {
|
||||||
_, mockClientFunc := mockHcpClientFn(suite.T())
|
_, mockClientFunc := mockHcpClientFn(suite.T())
|
||||||
dataDir := testutil.TempDir(suite.T(), "test-link-controller")
|
dataDir := testutil.TempDir(suite.T(), "test-link-controller")
|
||||||
suite.dataDir = dataDir
|
suite.dataDir = dataDir
|
||||||
|
|
||||||
|
hcpMgr := hcp.NewMockManager(suite.T())
|
||||||
|
hcpMgr.EXPECT().Stop().Return(nil)
|
||||||
mgr.Register(LinkController(
|
mgr.Register(LinkController(
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
mockClientFunc,
|
mockClientFunc,
|
||||||
config.CloudConfig{},
|
config.CloudConfig{},
|
||||||
dataDir,
|
dataDir,
|
||||||
hcp.NewManager(hcp.ManagerConfig{}),
|
hcpMgr,
|
||||||
))
|
))
|
||||||
mgr.SetRaftLeader(true)
|
mgr.SetRaftLeader(true)
|
||||||
go mgr.Run(suite.ctx)
|
go mgr.Run(suite.ctx)
|
||||||
|
@ -268,9 +262,11 @@ func (suite *controllerSuite) TestControllerResourceApisEnabledWithOverride_Link
|
||||||
|
|
||||||
dataDir := testutil.TempDir(suite.T(), "test-link-controller")
|
dataDir := testutil.TempDir(suite.T(), "test-link-controller")
|
||||||
suite.dataDir = dataDir
|
suite.dataDir = dataDir
|
||||||
hcpMgr := hcp.NewManager(hcp.ManagerConfig{
|
hcpMgr := hcp.NewMockManager(suite.T())
|
||||||
Logger: hclog.NewNullLogger(),
|
hcpMgr.EXPECT().GetCloudConfig().Return(config.CloudConfig{})
|
||||||
})
|
hcpMgr.EXPECT().UpdateConfig(mock.Anything, mock.Anything)
|
||||||
|
hcpMgr.EXPECT().Start(mock.Anything).Return(nil)
|
||||||
|
hcpMgr.EXPECT().Stop().Return(nil)
|
||||||
|
|
||||||
mgr.Register(LinkController(
|
mgr.Register(LinkController(
|
||||||
true,
|
true,
|
||||||
|
@ -322,35 +318,42 @@ func (suite *controllerSuite) TestController_GetClusterError() {
|
||||||
suite.T().Run(name, func(t *testing.T) {
|
suite.T().Run(name, func(t *testing.T) {
|
||||||
// Run the controller manager
|
// Run the controller manager
|
||||||
mgr := controller.NewManager(suite.client, suite.rt.Logger)
|
mgr := controller.NewManager(suite.client, suite.rt.Logger)
|
||||||
mockClient, mockClientFunc := mockHcpClientFn(suite.T())
|
mockClient, mockClientFunc := mockHcpClientFn(t)
|
||||||
mockClient.EXPECT().GetCluster(mock.Anything).Return(nil, tc.expectErr)
|
mockClient.EXPECT().GetCluster(mock.Anything).Return(nil, tc.expectErr)
|
||||||
|
|
||||||
dataDir := testutil.TempDir(suite.T(), "test-link-controller")
|
dataDir := testutil.TempDir(t, "test-link-controller")
|
||||||
suite.dataDir = dataDir
|
suite.dataDir = dataDir
|
||||||
|
|
||||||
|
hcpMgr := hcp.NewMockManager(t)
|
||||||
|
hcpMgr.EXPECT().GetCloudConfig().Return(config.CloudConfig{})
|
||||||
|
hcpMgr.EXPECT().Stop().Return(nil)
|
||||||
|
|
||||||
mgr.Register(LinkController(
|
mgr.Register(LinkController(
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
mockClientFunc,
|
mockClientFunc,
|
||||||
config.CloudConfig{},
|
config.CloudConfig{},
|
||||||
dataDir,
|
dataDir,
|
||||||
hcp.NewManager(hcp.ManagerConfig{}),
|
hcpMgr,
|
||||||
))
|
))
|
||||||
|
|
||||||
mgr.SetRaftLeader(true)
|
mgr.SetRaftLeader(true)
|
||||||
go mgr.Run(suite.ctx)
|
ctx, cancel := context.WithCancel(suite.ctx)
|
||||||
|
t.Cleanup(cancel)
|
||||||
|
go mgr.Run(ctx)
|
||||||
|
|
||||||
linkData := &pbhcp.Link{
|
linkData := &pbhcp.Link{
|
||||||
ClientId: "abc",
|
ClientId: "abc",
|
||||||
ClientSecret: "abc",
|
ClientSecret: "abc",
|
||||||
ResourceId: types.GenerateTestResourceID(suite.T()),
|
ResourceId: types.GenerateTestResourceID(t),
|
||||||
}
|
}
|
||||||
link := rtest.Resource(pbhcp.LinkType, "global").
|
link := rtest.Resource(pbhcp.LinkType, "global").
|
||||||
WithData(suite.T(), linkData).
|
WithData(t, linkData).
|
||||||
Write(suite.T(), suite.client)
|
Write(t, suite.client)
|
||||||
|
|
||||||
suite.T().Cleanup(suite.deleteResourceFunc(link.Id))
|
t.Cleanup(suite.deleteResourceFunc(link.Id))
|
||||||
|
|
||||||
suite.client.WaitForStatusCondition(suite.T(), link.Id, StatusKey, tc.expectCondition)
|
suite.client.WaitForStatusCondition(t, link.Id, StatusKey, tc.expectCondition)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,28 +8,29 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/hashicorp/consul/agent/hcp"
|
||||||
"github.com/hashicorp/consul/agent/hcp/bootstrap"
|
"github.com/hashicorp/consul/agent/hcp/bootstrap"
|
||||||
"github.com/hashicorp/consul/internal/controller"
|
"github.com/hashicorp/consul/internal/controller"
|
||||||
"github.com/hashicorp/consul/internal/resource"
|
"github.com/hashicorp/consul/internal/resource"
|
||||||
"github.com/hashicorp/consul/proto-public/pbresource"
|
"github.com/hashicorp/consul/proto-public/pbresource"
|
||||||
)
|
)
|
||||||
|
|
||||||
func cleanup(ctx context.Context, rt controller.Runtime, res *pbresource.Resource, dataDir string) error {
|
func cleanup(rt controller.Runtime, hcpManager hcp.Manager, dataDir string) error {
|
||||||
rt.Logger.Trace("cleaning up link resource")
|
rt.Logger.Trace("cleaning up link resource")
|
||||||
|
|
||||||
|
rt.Logger.Debug("stopping HCP manager")
|
||||||
|
hcpManager.Stop()
|
||||||
|
|
||||||
if dataDir != "" {
|
if dataDir != "" {
|
||||||
hcpConfigDir := filepath.Join(dataDir, bootstrap.SubDir)
|
hcpConfigDir := filepath.Join(dataDir, bootstrap.SubDir)
|
||||||
rt.Logger.Debug("deleting hcp-config dir", "dir", hcpConfigDir)
|
rt.Logger.Debug("deleting hcp-config dir", "dir", hcpConfigDir)
|
||||||
err := os.RemoveAll(hcpConfigDir)
|
err := os.RemoveAll(hcpConfigDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
rt.Logger.Error("failed to delete hcp-config dir", "dir", hcpConfigDir, "err", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err := ensureDeleted(ctx, rt, res)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ type Dependencies struct {
|
||||||
ResourceApisEnabled bool
|
ResourceApisEnabled bool
|
||||||
HCPAllowV2ResourceApis bool
|
HCPAllowV2ResourceApis bool
|
||||||
DataDir string
|
DataDir string
|
||||||
HCPManager *hcp.Manager
|
HCPManager *hcp.HCPManager
|
||||||
}
|
}
|
||||||
|
|
||||||
func Register(mgr *controller.Manager, deps Dependencies) {
|
func Register(mgr *controller.Manager, deps Dependencies) {
|
||||||
|
|
Loading…
Reference in New Issue