mirror of
https://github.com/pomerium/pomerium.git
synced 2025-06-05 04:13:11 +02:00
zero: bootstrap config
This commit is contained in:
parent
0affd9268b
commit
40efa26257
9 changed files with 638 additions and 30 deletions
151
internal/zero/bootstrap/bootstrap.go
Normal file
151
internal/zero/bootstrap/bootstrap.go
Normal file
|
@ -0,0 +1,151 @@
|
|||
package bootstrap
|
||||
|
||||
/*
|
||||
* Initial configuration for Pomerium start-up is obtained from the cloud.
|
||||
* Some parameters are derived from the cluster token.
|
||||
*
|
||||
* The expectation is that if the user wishes to survive a cloud outage,
|
||||
* it should be sufficient to set up Pomerium to use a durable database (Postgres)
|
||||
* and receive cloud configuration once.
|
||||
*
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/cipher"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
|
||||
"github.com/pomerium/pomerium/config"
|
||||
"github.com/pomerium/pomerium/internal/log"
|
||||
cluster_api "github.com/pomerium/zero-sdk/cluster"
|
||||
connect_mux "github.com/pomerium/zero-sdk/connect-mux"
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultCheckForUpdateInterval is the default interval to check for updates
|
||||
// if there is no connection to the update service
|
||||
DefaultCheckForUpdateInterval = 5 * time.Minute
|
||||
// DefaultCheckForUpdateIntervalWhenConnected is the default interval to check for updates
|
||||
// if there is a connection to the update service
|
||||
DefaultCheckForUpdateIntervalWhenConnected = 2 * time.Hour
|
||||
)
|
||||
|
||||
// Run initializes the bootstrap config source
|
||||
func (svc *BootstrapConfigSource) Run(ctx context.Context) error {
|
||||
svc.tryLoadInitial(ctx)
|
||||
|
||||
eg, ctx := errgroup.WithContext(ctx)
|
||||
eg.Go(func() error { return svc.watchUpdates(ctx) })
|
||||
eg.Go(func() error { return svc.updateLoop(ctx) })
|
||||
|
||||
return eg.Wait()
|
||||
}
|
||||
|
||||
func (svc *BootstrapConfigSource) watchUpdates(ctx context.Context) error {
|
||||
return svc.connectMux.Watch(ctx,
|
||||
connect_mux.WithOnConnected(func(_ context.Context) {
|
||||
svc.triggerUpdate(DefaultCheckForUpdateIntervalWhenConnected)
|
||||
}),
|
||||
connect_mux.WithOnDisconnected(func(_ context.Context) {
|
||||
svc.triggerUpdate(DefaultCheckForUpdateInterval)
|
||||
}),
|
||||
connect_mux.WithOnBootstrapConfigUpdated(func(_ context.Context) {
|
||||
svc.triggerUpdate(DefaultCheckForUpdateIntervalWhenConnected)
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
func (svc *BootstrapConfigSource) updateLoop(ctx context.Context) error {
|
||||
ticker := time.NewTicker(svc.updateInterval.Load())
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case <-svc.checkForUpdate:
|
||||
case <-ticker.C:
|
||||
}
|
||||
ticker.Reset(svc.updateInterval.Load())
|
||||
|
||||
err := svc.update(ctx)
|
||||
if err != nil {
|
||||
log.Ctx(ctx).Error().Err(err).Msg("failed to update bootstrap config")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// triggerUpdate triggers an update of the bootstrap config
|
||||
// and sets the interval for the next update
|
||||
func (svc *BootstrapConfigSource) triggerUpdate(newUpdateInterval time.Duration) {
|
||||
svc.updateInterval.Store(newUpdateInterval)
|
||||
|
||||
select {
|
||||
case svc.checkForUpdate <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func (svc *BootstrapConfigSource) update(ctx context.Context) error {
|
||||
current := svc.GetConfig()
|
||||
cfg := current.Clone()
|
||||
|
||||
err := tryUpdateAndSave(ctx, cfg.Options, svc.clusterAPI, svc.fileCachePath, svc.fileCipher)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_ = svc.SetConfig(ctx, cfg)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func tryUpdateAndSave(
|
||||
ctx context.Context,
|
||||
dst *config.Options,
|
||||
clusterAPI cluster_api.ClientWithResponsesInterface,
|
||||
fileCachePath string,
|
||||
fileCipher cipher.AEAD,
|
||||
) error {
|
||||
err := LoadBootstrapConfigFromAPI(ctx, dst, clusterAPI)
|
||||
if err != nil {
|
||||
return fmt.Errorf("load bootstrap config from API: %w", err)
|
||||
}
|
||||
|
||||
err = SaveBootstrapConfigToFile(dst, fileCachePath, fileCipher)
|
||||
if err != nil {
|
||||
log.Ctx(ctx).Error().Err(err).
|
||||
Msg("failed to save bootstrap config to file, note it may prevent Pomerium from starting up in case of connectivity issues")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src *BootstrapConfigSource) tryLoadInitial(ctx context.Context) {
|
||||
dst := src.GetConfig()
|
||||
|
||||
err := tryUpdateAndSave(ctx, dst.Options, src.clusterAPI, src.fileCachePath, src.fileCipher)
|
||||
if err != nil {
|
||||
log.Ctx(ctx).Error().Err(err).Msg("failed to load bootstrap config")
|
||||
src.tryLoadFromFile(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
src.SetConfig(ctx, dst)
|
||||
}
|
||||
|
||||
func (src *BootstrapConfigSource) tryLoadFromFile(ctx context.Context) {
|
||||
dst := src.GetConfig()
|
||||
|
||||
err := LoadBootstrapConfigFromFile(dst.Options, src.fileCachePath, src.fileCipher)
|
||||
if err != nil {
|
||||
log.Ctx(ctx).Error().Err(err).Msg("failed to load bootstrap config from file")
|
||||
return
|
||||
}
|
||||
|
||||
src.SetConfig(ctx, dst)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue