feat: publish Tend v0.2 preview source
Publish the reviewed allowlisted snapshot whose exact binary completed maintenance deployment, rollback, and reactivation exercises for Gamertan and Sandwich Hime. Private-Source-Commit: 4d7094c8b7c61991bfb67b11fc1558724c874eb2 Private-Source-Tree: 54a2f74804f7acddf3755d7d4da5b97f5fc28381 AI-Assistance: OpenAI Codex assisted implementation, testing, security review, and release verification. Signed-off-by: Cole Speelman <crspeelman@gmail.com>
This commit is contained in:
+31
-10
@@ -177,6 +177,9 @@ func (m Manager) deployBlueGreen(ctx context.Context, cfg config.Config, record
|
||||
if err = m.probeAll(ctx, cfg, slot.Address); err != nil {
|
||||
return fmt.Errorf("post-activation smoke failed: %w", err)
|
||||
}
|
||||
if err = m.probePublic(ctx, cfg, true); err != nil {
|
||||
return fmt.Errorf("public-origin smoke failed: %w", err)
|
||||
}
|
||||
next := state.Record{SchemaVersion: 1, Strategy: cfg.Deployment.Strategy, ActiveSlot: inactive, ActiveRelease: release, PreviousSlot: record.ActiveSlot, PreviousRelease: record.ActiveRelease, UpdatedAt: m.Now().UTC().Format(time.RFC3339)}
|
||||
if err = state.Store(cfg.Deployment.StateFile, cfg.Deployment.Root, next); err != nil {
|
||||
return err
|
||||
@@ -187,10 +190,9 @@ func (m Manager) deployBlueGreen(ctx context.Context, cfg config.Config, record
|
||||
func (m Manager) deploySingleton(ctx context.Context, cfg config.Config, record state.Record, release string) (err error) {
|
||||
single := *cfg.Deployment.Singleton
|
||||
candidateUnit := cfg.Service.Name + "-tend-candidate.service"
|
||||
env := copyMap(single.Environment)
|
||||
env[single.ListenEnv] = single.CandidateAddress
|
||||
env := map[string]string{single.ListenEnv: single.CandidateAddress}
|
||||
binary := filepath.Join(release, cfg.Build.Binary)
|
||||
if err = m.Operator.StartCandidate(ctx, candidateUnit, binary, env); err != nil {
|
||||
if err = m.Operator.StartCandidate(ctx, candidateUnit, binary, cfg.Service.EnvironmentFile, env); err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
@@ -238,6 +240,9 @@ func (m Manager) deploySingleton(ctx context.Context, cfg config.Config, record
|
||||
if err = m.probeAll(ctx, cfg, single.Address); err != nil {
|
||||
return fmt.Errorf("post-activation smoke failed: %w", err)
|
||||
}
|
||||
if err = m.probePublic(ctx, cfg, true); err != nil {
|
||||
return fmt.Errorf("public-origin smoke failed: %w", err)
|
||||
}
|
||||
next := state.Record{SchemaVersion: 1, Strategy: cfg.Deployment.Strategy, ActiveSlot: "singleton", ActiveRelease: release, PreviousSlot: "singleton", PreviousRelease: record.ActiveRelease, UpdatedAt: m.Now().UTC().Format(time.RFC3339)}
|
||||
if err = state.Store(cfg.Deployment.StateFile, cfg.Deployment.Root, next); err != nil {
|
||||
return err
|
||||
@@ -313,6 +318,9 @@ func (m Manager) rollbackBlueGreen(ctx context.Context, cfg config.Config, recor
|
||||
if err = m.probeHealthReadiness(ctx, cfg, slot.Address); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = m.probePublic(ctx, cfg, false); err != nil {
|
||||
return err
|
||||
}
|
||||
next := state.Record{SchemaVersion: 1, Strategy: record.Strategy, ActiveSlot: record.PreviousSlot, ActiveRelease: record.PreviousRelease, PreviousSlot: record.ActiveSlot, PreviousRelease: record.ActiveRelease, UpdatedAt: m.Now().UTC().Format(time.RFC3339)}
|
||||
return state.Store(cfg.Deployment.StateFile, cfg.Deployment.Root, next)
|
||||
}
|
||||
@@ -337,6 +345,9 @@ func (m Manager) rollbackSingleton(ctx context.Context, cfg config.Config, recor
|
||||
if err = m.probeHealthReadiness(ctx, cfg, single.Address); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = m.probePublic(ctx, cfg, false); err != nil {
|
||||
return err
|
||||
}
|
||||
_ = replaceSymlink(single.PreviousLink, record.ActiveRelease)
|
||||
next := state.Record{SchemaVersion: 1, Strategy: record.Strategy, ActiveSlot: "singleton", ActiveRelease: record.PreviousRelease, PreviousSlot: "singleton", PreviousRelease: record.ActiveRelease, UpdatedAt: m.Now().UTC().Format(time.RFC3339)}
|
||||
return state.Store(cfg.Deployment.StateFile, cfg.Deployment.Root, next)
|
||||
@@ -435,6 +446,23 @@ func (m Manager) probeHealthReadiness(ctx context.Context, cfg config.Config, ad
|
||||
return m.probe(ctx, cfg, address, checks)
|
||||
}
|
||||
|
||||
func (m Manager) probePublic(ctx context.Context, cfg config.Config, checkMarkers bool) error {
|
||||
timeout := time.Duration(cfg.Deployment.CandidateTimeoutSecs) * time.Second
|
||||
for _, check := range cfg.Deployment.PublicSmoke {
|
||||
attempt, cancel := context.WithTimeout(ctx, timeout)
|
||||
contains := check.Contains
|
||||
if !checkMarkers {
|
||||
contains = ""
|
||||
}
|
||||
err := m.Operator.ProbeURL(attempt, check.URL, contains)
|
||||
cancel()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m Manager) probe(ctx context.Context, cfg config.Config, address string, checks []config.Smoke) error {
|
||||
timeout := time.Duration(cfg.Deployment.CandidateTimeoutSecs) * time.Second
|
||||
for _, check := range checks {
|
||||
@@ -465,13 +493,6 @@ func slotConfig(bg config.BlueGreen, name string) config.Slot {
|
||||
}
|
||||
return bg.Green
|
||||
}
|
||||
func copyMap(source map[string]string) map[string]string {
|
||||
target := make(map[string]string, len(source)+1)
|
||||
for k, v := range source {
|
||||
target[k] = v
|
||||
}
|
||||
return target
|
||||
}
|
||||
func resolveReleaseLink(root, link string) (string, error) {
|
||||
info, err := os.Lstat(link)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user