| 1 | package rollout |
| 2 | |
| 3 | import ( |
| 4 | "fmt" |
| 5 | "strings" |
| 6 | "time" |
| 7 | |
| 8 | "github.com/carlosframework/platform/internal/activator" |
| 9 | "github.com/carlosframework/platform/internal/rolloutwords" |
| 10 | "github.com/carlosframework/platform/internal/status" |
| 11 | ) |
| 12 | |
| 13 | // EventCode is a rollout event's stable internal name (spec §9.4). The codes |
| 14 | // are what the journal and tests key on. The member-facing texts below are |
| 15 | // worded by internal/rolloutwords, and are approved copy. |
| 16 | type EventCode string |
| 17 | |
| 18 | const ( |
| 19 | EvSwapping EventCode = "swap.started" |
| 20 | EvSwapped EventCode = "swap.done" |
| 21 | EvSwapFailed EventCode = "swap.failed" |
| 22 | EvSwapDown EventCode = "swap.down" |
| 23 | EvCohortHalted EventCode = "cohort.halted" |
| 24 | EvFallbackServed EventCode = "fallback.served" |
| 25 | EvConfigPending EventCode = "config.pending" |
| 26 | EvStagingRefused EventCode = "staging.refused" |
| 27 | ) |
| 28 | |
| 29 | // reasonWord turns an attempt's diagnostic text (SwapResult.Reason, an error) |
| 30 | // into one of status's reason words (status.Reason*, the one vocabulary the |
| 31 | // events and the status report share), so no free text, which can name paths |
| 32 | // and internals, reaches a member-facing surface. The raw text goes to the |
| 33 | // operator log instead. Anything unrecognised is a failed health check: the |
| 34 | // swap's checks are the health check, the settle and the exit watch, and the |
| 35 | // health check is the one a spawn or start error also fails. |
| 36 | func reasonWord(text string) string { |
| 37 | t := strings.ToLower(text) |
| 38 | switch { |
| 39 | case strings.Contains(t, "reused"): |
| 40 | return status.ReasonReused |
| 41 | case strings.Contains(t, "staging"), strings.Contains(t, "staged"), strings.Contains(t, "digest"): |
| 42 | return status.ReasonStaging |
| 43 | case strings.Contains(t, "settle"): |
| 44 | return status.ReasonSettle |
| 45 | case strings.Contains(t, "exited"): |
| 46 | return status.ReasonExit |
| 47 | } |
| 48 | return status.ReasonHealth |
| 49 | } |
| 50 | |
| 51 | func textSwapping(build string, cause activator.Cause) string { |
| 52 | return fmt.Sprintf(rolloutwords.EventSwapping, build, rolloutwords.Cause(string(cause))) |
| 53 | } |
| 54 | |
| 55 | // textSwapped carries the retiring generation's drain time (spec §9.4), |
| 56 | // printed to a tenth of a second. |
| 57 | func textSwapped(build string, drain time.Duration) string { |
| 58 | return fmt.Sprintf(rolloutwords.EventSwapped, build, drain.Round(100*time.Millisecond).String()) |
| 59 | } |
| 60 | |
| 61 | // textSwapFailed's reason is a reason word (reasonWord at the emit site), |
| 62 | // never raw text. It is worded as it is given: mapping it again would fold |
| 63 | // the closed words into one another (reasonWord("exit") is a health |
| 64 | // failure). |
| 65 | func textSwapFailed(build, serving, reason string) string { |
| 66 | return fmt.Sprintf(rolloutwords.EventSwapFailed, build, rolloutwords.Reason(reason), serving) |
| 67 | } |
| 68 | |
| 69 | func textSwapDown(build string) string { |
| 70 | return fmt.Sprintf(rolloutwords.EventHoldDown, build) |
| 71 | } |
| 72 | |
| 73 | // textCohortHalted names the box (spec §9.4); the event sink already |
| 74 | // carries the account, the app and the host. reason is a reason word. |
| 75 | func textCohortHalted(build, box, reason string) string { |
| 76 | return fmt.Sprintf(rolloutwords.EventCohortHalted, build, box, rolloutwords.Reason(reason)) |
| 77 | } |
| 78 | |
| 79 | func textFallback(failed, good string) string { |
| 80 | return fmt.Sprintf(rolloutwords.EventFallbackServed, failed, good) |
| 81 | } |
| 82 | |
| 83 | // textConfigPending's reason is a reason word. |
| 84 | func textConfigPending(reason string, wait time.Duration) string { |
| 85 | return fmt.Sprintf(rolloutwords.EventConfigPending, rolloutwords.Reason(reason), wait.String()) |
| 86 | } |
| 87 | |
| 88 | func textStagingRefused(build string) string { |
| 89 | return fmt.Sprintf(rolloutwords.EventStagingRefused, build) |
| 90 | } |
| 91 | |