Create HCP management token in HCP manager (#19830)

* Create HCP management token in HCP manager

* Change InitializeManagementToken to ManagementTokenUpserter

* Implement and use management token upsert function

* Fix race condition in test

* Add idea for improvement as comment

* Return early in upsertManagementToken if token exists
This commit is contained in:
Nick Cellino 2024-01-19 13:58:49 -05:00 committed by GitHub
parent 98c9702ba3
commit 37a5fddffa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 89 additions and 11 deletions

View File

@ -586,6 +586,55 @@ func (s *Server) initializeManagementToken(name, secretID string) error {
return nil return nil
} }
func (s *Server) upsertManagementToken(name, secretID string) error {
state := s.fsm.State()
if _, err := uuid.ParseUUID(secretID); err != nil {
s.logger.Warn("Configuring a non-UUID management token is deprecated")
}
_, token, err := state.ACLTokenGetBySecret(nil, secretID, nil)
if err != nil {
return fmt.Errorf("failed to get %s: %v", name, err)
}
if token != nil {
return nil
}
accessor, err := lib.GenerateUUID(s.checkTokenUUID)
if err != nil {
return fmt.Errorf("failed to generate the accessor ID for %s: %v", name, err)
}
newToken := structs.ACLToken{
AccessorID: accessor,
SecretID: secretID,
Description: name,
Policies: []structs.ACLTokenPolicyLink{
{
ID: structs.ACLPolicyGlobalManagementID,
},
},
CreateTime: time.Now(),
Local: false,
EnterpriseMeta: *structs.DefaultEnterpriseMetaInDefaultPartition(),
}
newToken.SetHash(true)
req := structs.ACLTokenBatchSetRequest{
Tokens: structs.ACLTokens{&newToken},
CAS: false,
}
if _, err := s.raftApply(structs.ACLTokenSetRequestType, &req); err != nil {
return fmt.Errorf("failed to create %s: %v", name, err)
}
s.logger.Info("Created ACL token", "description", name)
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)

View File

@ -588,6 +588,15 @@ func NewServer(config *Config, flat Deps, externalGRPCServer *grpc.Server,
Logger: logger.Named("hcp_manager"), Logger: logger.Named("hcp_manager"),
SCADAProvider: flat.HCP.Provider, SCADAProvider: flat.HCP.Provider,
TelemetryProvider: flat.HCP.TelemetryProvider, TelemetryProvider: flat.HCP.TelemetryProvider,
ManagementTokenUpserterFn: func(name, secretId string) error {
if s.IsLeader() {
// Idea for improvement: Upsert a token with a well-known accessorId here instead
// of a randomly generated one. This would prevent any possible insertion collision between
// this and the insertion that happens during the ACL initialization process (initializeACLs function)
return s.upsertManagementToken(name, secretId)
}
return nil
},
}) })
var recorder *middleware.RequestRecorder var recorder *middleware.RequestRecorder

View File

@ -26,9 +26,12 @@ type ManagerConfig struct {
SCADAProvider scada.Provider SCADAProvider scada.Provider
TelemetryProvider *hcpProviderImpl TelemetryProvider *hcpProviderImpl
StatusFn StatusCallback StatusFn StatusCallback
MinInterval time.Duration // Idempotent function to upsert the HCP management token. This will be called periodically in
MaxInterval time.Duration // the manager's main loop.
ManagementTokenUpserterFn ManagementTokenUpserter
MinInterval time.Duration
MaxInterval time.Duration
Logger hclog.Logger Logger hclog.Logger
} }
@ -54,6 +57,7 @@ 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 Manager struct { type Manager struct {
logger hclog.Logger logger hclog.Logger
@ -111,6 +115,14 @@ func (m *Manager) Run(ctx context.Context) error {
// main loop // main loop
for { for {
// Check for configured management token from HCP and upsert it if found
if hcpManagement := m.cfg.CloudConfig.ManagementToken; len(hcpManagement) > 0 {
upsertTokenErr := m.cfg.ManagementTokenUpserterFn("HCP Management Token", hcpManagement)
if upsertTokenErr != nil {
m.logger.Error("failed to upsert HCP management token", "err", upsertTokenErr)
}
}
m.cfgMu.RLock() m.cfgMu.RLock()
cfg := m.cfg cfg := m.cfg
m.cfgMu.RUnlock() m.cfgMu.RUnlock()

View File

@ -22,12 +22,18 @@ func TestManager_Run(t *testing.T) {
statusF := func(ctx context.Context) (hcpclient.ServerStatus, error) { statusF := func(ctx context.Context) (hcpclient.ServerStatus, error) {
return hcpclient.ServerStatus{ID: t.Name()}, nil return hcpclient.ServerStatus{ID: t.Name()}, nil
} }
upsertManagementTokenCalled := make(chan struct{}, 1)
upsertManagementTokenF := func(name, secretID string) error {
upsertManagementTokenCalled <- struct{}{}
return nil
}
updateCh := make(chan struct{}, 1) updateCh := make(chan struct{}, 1)
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: "organization/85702e73-8a3d-47dc-291c-379b783c5804/project/8c0547c0-10e8-1ea2-dffe-384bee8da634/hashicorp.consul.global-network-manager.cluster/test",
NodeID: "node-1", NodeID: "node-1",
ManagementToken: "fake-token",
} }
scadaM := scada.NewMockProvider(t) scadaM := scada.NewMockProvider(t)
scadaM.EXPECT().UpdateHCPConfig(cloudCfg).Return(nil) scadaM.EXPECT().UpdateHCPConfig(cloudCfg).Return(nil)
@ -52,12 +58,13 @@ func TestManager_Run(t *testing.T) {
mockTelemetryCfg, nil).Maybe() mockTelemetryCfg, nil).Maybe()
mgr := NewManager(ManagerConfig{ mgr := NewManager(ManagerConfig{
Client: client, Client: client,
Logger: hclog.New(&hclog.LoggerOptions{Output: io.Discard}), Logger: hclog.New(&hclog.LoggerOptions{Output: io.Discard}),
StatusFn: statusF, StatusFn: statusF,
CloudConfig: cloudCfg, ManagementTokenUpserterFn: upsertManagementTokenF,
SCADAProvider: scadaM, CloudConfig: cloudCfg,
TelemetryProvider: telemetryProvider, SCADAProvider: scadaM,
TelemetryProvider: telemetryProvider,
}) })
mgr.testUpdateSent = updateCh mgr.testUpdateSent = updateCh
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
@ -76,6 +83,7 @@ func TestManager_Run(t *testing.T) {
require.Equal(t, client, telemetryProvider.hcpClient) require.Equal(t, client, telemetryProvider.hcpClient)
require.NotNil(t, telemetryProvider.GetHeader()) require.NotNil(t, telemetryProvider.GetHeader())
require.NotNil(t, telemetryProvider.GetHTTPClient()) require.NotNil(t, telemetryProvider.GetHTTPClient())
require.NotEmpty(t, upsertManagementTokenCalled, "upsert management token function not called")
} }
func TestManager_SendUpdate(t *testing.T) { func TestManager_SendUpdate(t *testing.T) {