111 lines
2.9 KiB
Go
111 lines
2.9 KiB
Go
package supervisor
|
|
|
|
type ReleaseState string
|
|
|
|
const (
|
|
ReleaseCandidate ReleaseState = "CANDIDATE"
|
|
ReleaseHealthy ReleaseState = "HEALTHY"
|
|
ReleaseFailed ReleaseState = "FAILED"
|
|
ReleaseRolledBack ReleaseState = "ROLLED_BACK"
|
|
)
|
|
|
|
type TrafficMode string
|
|
|
|
const (
|
|
TrafficStable TrafficMode = "STABLE"
|
|
TrafficCanaryHeader TrafficMode = "CANARY_HEADER"
|
|
TrafficCandidate TrafficMode = "CANDIDATE"
|
|
)
|
|
|
|
type Release struct {
|
|
ID string `json:"id"`
|
|
Version string `json:"version"`
|
|
State ReleaseState `json:"state"`
|
|
LastKnownGood bool `json:"last_known_good"`
|
|
}
|
|
type Observation struct {
|
|
ReleaseID string `json:"release_id"`
|
|
Ready bool `json:"ready"`
|
|
Reason string `json:"reason"`
|
|
ObservedAtUnixMillis int64 `json:"observed_at_unix_millis"`
|
|
}
|
|
type Lifeboat struct {
|
|
Current Release `json:"current"`
|
|
LastKnownGood Release `json:"last_known_good"`
|
|
RollbackTarget Release `json:"rollback_target"`
|
|
Observations []Observation `json:"observations"`
|
|
TrafficMode TrafficMode `json:"traffic_mode"`
|
|
StableUpstream string `json:"stable_upstream"`
|
|
CandidateUpstream string `json:"candidate_upstream"`
|
|
}
|
|
|
|
func (l *Lifeboat) Start(candidate Release) bool {
|
|
if candidate.ID == "" || candidate.Version == "" {
|
|
return false
|
|
}
|
|
candidate.State = ReleaseCandidate
|
|
if l.LastKnownGood.ID != "" {
|
|
l.RollbackTarget = l.LastKnownGood
|
|
}
|
|
l.Current = candidate
|
|
return true
|
|
}
|
|
|
|
func (l *Lifeboat) EstablishBaseline(release Release) bool {
|
|
if release.ID == "" || release.Version == "" {
|
|
return false
|
|
}
|
|
release.State = ReleaseHealthy
|
|
release.LastKnownGood = true
|
|
l.Current = release
|
|
l.LastKnownGood = release
|
|
l.RollbackTarget = Release{}
|
|
l.TrafficMode = TrafficStable
|
|
return true
|
|
}
|
|
func (l *Lifeboat) Record(o Observation) bool {
|
|
if o.ReleaseID == "" {
|
|
return false
|
|
}
|
|
l.Observations = append(l.Observations, o)
|
|
if o.ReleaseID != l.Current.ID {
|
|
return true
|
|
}
|
|
if o.Ready {
|
|
l.Current.State = ReleaseHealthy
|
|
l.Current.LastKnownGood = true
|
|
l.LastKnownGood = l.Current
|
|
} else {
|
|
l.Current.State = ReleaseFailed
|
|
}
|
|
return true
|
|
}
|
|
func (l *Lifeboat) Rollback() bool {
|
|
target := l.RollbackTarget
|
|
if target.ID == "" {
|
|
target = l.LastKnownGood
|
|
}
|
|
if target.ID == "" {
|
|
return false
|
|
}
|
|
l.Current = target
|
|
l.Current.State = ReleaseRolledBack
|
|
l.Current.LastKnownGood = true
|
|
l.LastKnownGood = l.Current
|
|
l.RollbackTarget = Release{}
|
|
return true
|
|
}
|
|
|
|
func (l *Lifeboat) SetTraffic(mode TrafficMode, stableUpstream, candidateUpstream string) bool {
|
|
if mode != TrafficStable && mode != TrafficCanaryHeader && mode != TrafficCandidate {
|
|
return false
|
|
}
|
|
if stableUpstream == "" || (mode != TrafficStable && candidateUpstream == "") {
|
|
return false
|
|
}
|
|
l.TrafficMode = mode
|
|
l.StableUpstream = stableUpstream
|
|
l.CandidateUpstream = candidateUpstream
|
|
return true
|
|
}
|