Skip to content

Commit 6738b60

Browse files
committed
wip
1 parent 3ac2fcc commit 6738b60

File tree

9 files changed

+807
-274
lines changed

9 files changed

+807
-274
lines changed

cmd/run.go

+6-1
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,14 @@ package cmd
33
import (
44
"os"
55
"path"
6+
"time"
67

8+
"github.com/nlewo/comin/internal/builder"
79
"github.com/nlewo/comin/internal/config"
810
"github.com/nlewo/comin/internal/fetcher"
911
"github.com/nlewo/comin/internal/http"
1012
"github.com/nlewo/comin/internal/manager"
13+
"github.com/nlewo/comin/internal/nix"
1114
"github.com/nlewo/comin/internal/prometheus"
1215
"github.com/nlewo/comin/internal/repository"
1316
"github.com/nlewo/comin/internal/scheduler"
@@ -61,7 +64,9 @@ var runCmd = &cobra.Command{
6164
sched := scheduler.New()
6265
sched.FetchRemotes(fetcher, cfg.Remotes)
6366

64-
manager := manager.New(repository, store, metrics, sched, fetcher, gitConfig.Path, gitConfig.Dir, cfg.Hostname, machineId)
67+
builder := builder.New(cfg.Hostname, 5*time.Minute, nix.Eval, 30*time.Minute, nix.Build)
68+
69+
manager := manager.New(repository, store, metrics, sched, fetcher, builder, gitConfig.Path, gitConfig.Dir, cfg.Hostname, machineId)
6570
// go poller.Poller(manager, cfg.Remotes)
6671
http.Serve(manager,
6772
metrics,

internal/builder/builder.go

+256
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
package builder
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"sync"
7+
"time"
8+
9+
"github.com/sirupsen/logrus"
10+
)
11+
12+
type BuildResult struct {
13+
EndAt time.Time
14+
Err error
15+
}
16+
17+
type EvalResult struct {
18+
EndAt time.Time
19+
OutPath string
20+
DrvPath string
21+
MachineId string
22+
Err error
23+
}
24+
25+
type EvalFunc func(ctx context.Context, flakeUrl string, hostname string) (drvPath string, outPath string, machineId string, err error)
26+
type BuildFunc func(ctx context.Context, drvPath string) error
27+
28+
type Builder struct {
29+
hostname string
30+
mu sync.Mutex
31+
evalFunc EvalFunc
32+
buildFunc BuildFunc
33+
evalTimeout time.Duration
34+
buildTimeout time.Duration
35+
IsEvaluating bool
36+
IsBuilding bool
37+
evalCancelFunc context.CancelFunc
38+
buildCancelFunc context.CancelFunc
39+
buildCtx context.Context
40+
generation *Generation
41+
previousGeneration *Generation
42+
EvaluationDone chan Generation
43+
BuildDone chan Generation
44+
}
45+
46+
func New(hostname string, evalTimeout time.Duration, evalFunc EvalFunc, buildTimeout time.Duration, buildFunc BuildFunc) *Builder {
47+
return &Builder{
48+
hostname: hostname,
49+
evalFunc: evalFunc,
50+
evalTimeout: evalTimeout,
51+
buildFunc: buildFunc,
52+
buildTimeout: buildTimeout,
53+
BuildDone: make(chan Generation),
54+
}
55+
}
56+
57+
type Status int64
58+
59+
const (
60+
Init Status = iota
61+
Evaluating
62+
EvaluationSucceeded
63+
EvaluationFailed
64+
Building
65+
BuildSucceeded
66+
BuildFailed
67+
)
68+
69+
func StatusToString(status Status) string {
70+
switch status {
71+
case Init:
72+
return "init"
73+
case Evaluating:
74+
return "evaluating"
75+
case EvaluationSucceeded:
76+
return "evaluation-succeeded"
77+
case EvaluationFailed:
78+
return "evaluation-failed"
79+
case Building:
80+
return "building"
81+
case BuildSucceeded:
82+
return "build-succeeded"
83+
case BuildFailed:
84+
return "build-failed"
85+
}
86+
return ""
87+
}
88+
89+
func StatusFromString(status string) Status {
90+
switch status {
91+
case "init":
92+
return Init
93+
case "evaluating":
94+
return Evaluating
95+
case "evaluation-succeeded":
96+
return EvaluationSucceeded
97+
case "evaluation-failed":
98+
return EvaluationFailed
99+
case "building":
100+
return Building
101+
case "build-succeeded":
102+
return BuildSucceeded
103+
case "build-failed":
104+
return BuildFailed
105+
}
106+
return Init
107+
}
108+
109+
// We consider each created genration is legit to be deployed: hard
110+
// reset is ensured at RepositoryStatus creation.
111+
type Generation struct {
112+
UUID string `json:"uuid"`
113+
FlakeUrl string `json:"flake-url"`
114+
Hostname string `json:"hostname"`
115+
116+
Status Status `json:"status"`
117+
118+
SelectedRemoteUrl string `json:"remote-url"`
119+
SelectedRemoteName string `json:"remote-name"`
120+
SelectedBranchName string `json:"branch-name"`
121+
SelectedCommitId string `json:"commit-id"`
122+
SelectedCommitMsg string `json:"commit-msg"`
123+
SelectedBranchIsTesting bool `json:"branch-is-testing"`
124+
125+
MainCommitId string `json:"main-commit-id"`
126+
MainRemoteName string `json:"main-remote-name"`
127+
MainBranchName string `json:"main-branch-name"`
128+
129+
Evaluated bool `json:"evaluated"`
130+
EvalStartedAt time.Time `json:"eval-started-at"`
131+
EvalEndedAt time.Time `json:"eval-ended-at"`
132+
EvalErr error `json:"-"`
133+
OutPath string `json:"outpath"`
134+
DrvPath string `json:"drvpath"`
135+
MachineId string `json:"machine-id"`
136+
137+
Built bool `json:"built"`
138+
BuildStartedAt time.Time `json:"build-started-at"`
139+
BuildEndedAt time.Time `json:"build-ended-at"`
140+
BuildErr error `json:"-"`
141+
}
142+
143+
func (b *Builder) GetGeneration() Generation {
144+
b.mu.Lock()
145+
defer b.mu.Unlock()
146+
return *b.generation
147+
}
148+
149+
func (b *Builder) GetPreviousGeneration() Generation {
150+
b.mu.Lock()
151+
defer b.mu.Unlock()
152+
return *b.previousGeneration
153+
}
154+
155+
type State struct {
156+
IsBuilding bool
157+
IsEvaluating bool
158+
}
159+
160+
func (b *Builder) State() State {
161+
b.mu.Lock()
162+
defer b.mu.Unlock()
163+
return State{
164+
IsBuilding: b.IsBuilding,
165+
IsEvaluating: b.IsEvaluating,
166+
}
167+
}
168+
169+
func (b *Builder) Stop() {
170+
b.mu.Lock()
171+
if b.IsEvaluating {
172+
b.evalCancelFunc()
173+
b.mu.Unlock()
174+
<-b.EvaluationDone
175+
} else {
176+
b.mu.Unlock()
177+
}
178+
b.mu.Lock()
179+
if b.IsBuilding {
180+
b.buildCancelFunc()
181+
b.mu.Unlock()
182+
<-b.BuildDone
183+
} else {
184+
b.mu.Unlock()
185+
}
186+
}
187+
188+
// Eval evaluates a generation. It cancels current any generation
189+
// evaluation or build.
190+
func (b *Builder) Eval(flakeUrl string) {
191+
var ctx context.Context
192+
// This is to prempt the builder since we don't nede to allow
193+
// several evaluation in parallel
194+
b.Stop()
195+
b.mu.Lock()
196+
b.IsEvaluating = true
197+
b.previousGeneration = b.generation
198+
g := Generation{
199+
FlakeUrl: flakeUrl,
200+
Hostname: b.hostname,
201+
}
202+
b.generation = &g
203+
ctx, b.evalCancelFunc = context.WithCancel(context.Background())
204+
b.mu.Unlock()
205+
206+
b.EvaluationDone = make(chan Generation)
207+
go func() {
208+
ctx, cancel := context.WithTimeout(ctx, b.evalTimeout)
209+
defer cancel()
210+
drvPath, outPath, machineId, err := b.evalFunc(ctx, flakeUrl, b.hostname)
211+
logrus.Infof("builder: evaluation done with machineId=%d drvPath=%s", machineId, drvPath)
212+
b.mu.Lock()
213+
defer b.mu.Unlock()
214+
b.generation.EvalErr = err
215+
b.generation.DrvPath = drvPath
216+
b.generation.OutPath = outPath
217+
b.generation.MachineId = machineId
218+
b.generation.Evaluated = true
219+
b.IsEvaluating = false
220+
b.EvaluationDone <- *b.generation
221+
}()
222+
}
223+
224+
// Build builds a generation.
225+
func (b *Builder) Build() error {
226+
var ctx context.Context
227+
b.mu.Lock()
228+
defer b.mu.Unlock()
229+
230+
if b.generation == nil || !b.generation.Evaluated {
231+
return fmt.Errorf("The generation is not evaluated")
232+
}
233+
if b.IsBuilding {
234+
return fmt.Errorf("The builder is already building")
235+
}
236+
if b.generation.Built {
237+
return fmt.Errorf("The generation is already built")
238+
}
239+
b.IsBuilding = true
240+
ctx, b.buildCancelFunc = context.WithCancel(context.Background())
241+
242+
go func() {
243+
ctx, cancel := context.WithTimeout(ctx, b.buildTimeout)
244+
defer cancel()
245+
err := b.buildFunc(ctx, b.generation.DrvPath)
246+
b.mu.Lock()
247+
defer b.mu.Unlock()
248+
b.generation.BuildErr = err
249+
if b.generation.BuildErr == nil {
250+
b.generation.Built = true
251+
}
252+
b.IsBuilding = false
253+
b.BuildDone <- *b.generation
254+
}()
255+
return nil
256+
}

0 commit comments

Comments
 (0)