| 1 | package main |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | "encoding/json" |
| 6 | "flag" |
| 7 | "fmt" |
| 8 | "log" |
| 9 | "net/http" |
| 10 | ) |
| 11 | |
| 12 | // ---- carlos system cancel-activation ----------------------------------- |
| 13 | |
| 14 | // runSystemCancelActivation releases an executable activation reservation |
| 15 | // that was never published (claim plan 9, I-4). While one is held, every |
| 16 | // other promote and every rollback of its channel is refused, and no box can |
| 17 | // complete it because no box ever saw it. The console fences the pointer |
| 18 | // before it removes the reservation, and refuses one that was published: |
| 19 | // that one is released when its boxes report completion. |
| 20 | func runSystemCancelActivation(args []string) { |
| 21 | fs := flag.NewFlagSet("system cancel-activation", flag.ExitOnError) |
| 22 | consoleFlag := consoleOption(fs, "console origin (default: the only logged-in one)") |
| 23 | channel := fs.String("channel", "", "canary or stable (required)") |
| 24 | activation := fs.String("activation", "", "the activation ID you expect; refused if a different one is held") |
| 25 | yes := fs.Bool("yes", false, "do not ask") |
| 26 | fs.Usage = flagUsage(fs, "") |
| 27 | fs.Parse(args) |
| 28 | if *channel == "" { |
| 29 | log.Fatal("--channel is required") |
| 30 | } |
| 31 | sess := requireDeployAPISession(*consoleFlag) |
| 32 | if !*yes { |
| 33 | if !interactiveTerminal() { |
| 34 | log.Fatal("no terminal to confirm on; pass --yes") |
| 35 | } |
| 36 | if !promptYes(stdout, fmt.Sprintf("cancel the unpublished activation held for %s? no box changes release [y/N] ", *channel)) { |
| 37 | return |
| 38 | } |
| 39 | } |
| 40 | body := map[string]string{"channel": *channel} |
| 41 | if *activation != "" { |
| 42 | body["activation"] = *activation |
| 43 | } |
| 44 | st, raw, err := sess.postJSON(context.Background(), "/api/cli/system/activation/cancel", body) |
| 45 | if err != nil { |
| 46 | log.Fatalf("carlos system cancel-activation: %v", err) |
| 47 | } |
| 48 | if st != http.StatusOK { |
| 49 | log.Fatalf("carlos system cancel-activation: %s", apiErrorFromBody(st, raw)) |
| 50 | } |
| 51 | var out struct { |
| 52 | Activation string `json:"activation"` |
| 53 | } |
| 54 | _ = json.Unmarshal(raw, &out) |
| 55 | fmt.Fprintf(stdout, "%s %s on %s\n", colorFor(stdout).ok("cancelled activation"), out.Activation, *channel) |
| 56 | } |
| 57 | |