carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package host
2
3import (
4 "bytes"
5 "context"
6 "encoding/json"
7 "errors"
8 "fmt"
9 "io"
10 "net"
11 "net/http"
12 "time"
13
14 execlease "github.com/carlosframework/platform/internal/hostexec"
15)
16
17func (h ClaimRootHTTP) serveExecution(w http.ResponseWriter, r *http.Request, peer StopPeer) {
18 var q execlease.ExecutionRequest
19 dec := json.NewDecoder(http.MaxBytesReader(w, r.Body, 16<<10))
20 dec.DisallowUnknownFields()
21 if dec.Decode(&q) != nil || dec.Decode(new(any)) != io.EOF {
22 w.WriteHeader(http.StatusBadRequest)
23 return
24 }
25 if h.Execution == nil {
26 w.WriteHeader(http.StatusServiceUnavailable)
27 return
28 }
29 out, err := h.Execution.Execute(r.Context(), execlease.ControllerProcess{KernelBootID: peer.BootID, PID: peer.PID, Start: peer.ProcessStart}, q)
30 if errors.Is(err, errReinstallRetired) {
31 w.WriteHeader(http.StatusGone)
32 return
33 }
34 if err != nil {
35 w.WriteHeader(http.StatusConflict)
36 return
37 }
38 w.Header().Set("Content-Type", "application/json")
39 _ = json.NewEncoder(w).Encode(out)
40}
41
42func rootExecutionClient(path string) func(context.Context, execlease.ExecutionRequest) (execlease.ExecutionResponse, error) {
43 return func(ctx context.Context, q execlease.ExecutionRequest) (execlease.ExecutionResponse, error) {
44 var out execlease.ExecutionResponse
45 raw, err := json.Marshal(q)
46 if err != nil {
47 return out, err
48 }
49 ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
50 defer cancel()
51 tr := &http.Transport{DisableKeepAlives: true, DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) {
52 return (&net.Dialer{}).DialContext(ctx, "unix", path)
53 }}
54 defer tr.CloseIdleConnections()
55 for attempt := 0; attempt < 2; attempt++ {
56 req, e := http.NewRequestWithContext(ctx, http.MethodPost, "http://control/claim/execution", bytes.NewReader(raw))
57 if e != nil {
58 return out, e
59 }
60 resp, e := (&http.Client{Transport: tr}).Do(req)
61 if e != nil {
62 err = e
63 continue
64 }
65 if resp.StatusCode == http.StatusGone {
66 resp.Body.Close()
67 return out, fmt.Errorf("%s: %w", q.Host, errReinstallRetired)
68 }
69 if resp.StatusCode != http.StatusOK {
70 resp.Body.Close()
71 return out, execlease.ErrLost
72 }
73 limited := &io.LimitedReader{R: resp.Body, N: (16 << 10) + 1}
74 dec := json.NewDecoder(limited)
75 dec.DisallowUnknownFields()
76 err = dec.Decode(&out)
77 if err == nil && (dec.Decode(new(any)) != io.EOF || limited.N == 0) {
78 err = execlease.ErrState
79 }
80 resp.Body.Close()
81 if err != nil {
82 continue
83 }
84 serving := q.Operation == "executionView" && q.Token == "" && out.Status == "serving" && out.Token == "" && out.SafeUntil == 0 && out.LeaseETag == "" && out.Expires.IsZero()
85 if q.Operation == "executionView" && q.Token == "" && !serving {
86 return execlease.ExecutionResponse{}, execlease.ErrState
87 }
88 if out.Schema != 1 || out.Host != q.Host || out.Incarnation != q.Incarnation || out.Stamp != q.Stamp || out.Session != q.Session || out.Op != q.Op || (!serving && out.Token == "") || out.BootID == "" {
89 return execlease.ExecutionResponse{}, execlease.ErrState
90 }
91 return out, nil
92 }
93 return out, err
94 }
95}
96