Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/git/vole.git

Write over SSH with a registered key:

git clone ssh://git@0.0.0.0:2222/vole.git

A free and open-source forge that lives in your Git repo.

Let a job declare the network it needs

d79580247b8f33f7ffdf2f05847733cad12c187a · Keenan Nemetz · 6d ago · parent 4ac42680

Let a job declare the network it needs

A runner's --container-network decided the network for every job it took, so a
machine that could reach the network gave it to builds that never asked, and a
job that did need one could be handed to a machine that had none. The docs said
"add bridge only when a job needs it", which no workflow could express.

A job now says `network: none` or `network: bridge`. The request is pinned with
the run, and the lease only hands a bridge job to a runner that advertises the
capability, so a job waits for a capable machine instead of failing on one that
never could. The runner applies the job's request to its container, which means a
bridge runner can still withhold the network from a job that asked for none.

A job that declares nothing keeps the runner's own setting, which is what every
workflow meant before this field existed. The local run applies the same rule and
refuses a bridge job when it was started without one, so `vole workflow run` either
matches CI or says what to change on the command line.

The workflows page, the run page, and the runner's log line now say what a job
asked for and what it ran with. verify.yaml and release.yaml are deliberately not
in this change: the instance validates a pushed workflow with the binary it is
running, so they can declare the field once that binary knows it.

Closes #159.

server/ssh/runner.go +12−1

@@ -97,6 +97,12 @@ type runnerRequest struct {
97 97 // a permission: a runner that claims a label it does not have only hurts itself,
98 98 // which is why this is a client claim where --repos is server-enforced.
99 99 Labels []string `json:"labels,omitempty"`
100 // Network is the network this machine can give, derived from --container-network.
101 // A job that declares `network: bridge` is only leased to a runner that says it can
102 // provide one, so the job waits for a capable machine instead of running without
103 // the network it asked for. A runner that claims more than it can give only fails
104 // its own jobs, which is the same trade as a label.
105 Network string `json:"network,omitempty"`
100 106 // Wait is how many seconds an idle lease may park before answering "nothing to
101 107 // do". Zero answers at once, which is what a one-shot runner wants: it is here to
102 108 // run the job it was started for, not to wait for the next one.
@@ -161,6 +167,10 @@ type runnerJob struct {
161 167 Trigger string `json:"trigger,omitempty"`
162 168 TriggerAction string `json:"trigger_action,omitempty"`
163 169 EventPayload string `json:"event_payload,omitempty"`
170 // Network is the isolation the job asked for with `network:`, or empty when the
171 // workflow did not say. The runner applies it to the container it starts, so a job
172 // that asks for none gets none even on a machine that could provide a bridge.
173 Network string `json:"network,omitempty"`
164 174 // Image is a registry reference or "file:<name>". For the latter, ImageDefinition
165 175 // is the pinned recipe and ImageSHA is its digest; the runner builds it locally.
166 176 Image string `json:"image"`
@@ -278,7 +288,7 @@ func (s *Server) serveRunner(ch xssh.Channel, user string) {
278 288 // loses exactly that race.
279 289 wake := cfg.Runs.Work()
280 290 lease := runs.LeaseRequest{
281 Owner: owner, Repos: want, Labels: req.Labels,
291 Owner: owner, Repos: want, Labels: req.Labels, Network: req.Network,
282 292 TTL: cfg.Lease, MaxPerRepo: cfg.MaxJobsPerRepo,
283 293 }
284 294 run, err := cfg.Runs.Lease(lease, now)
@@ -323,6 +333,7 @@ func (s *Server) serveRunner(ch xssh.Channel, user string) {
323 333 Trigger: run.Trigger, TriggerAction: run.TriggerAction, EventPayload: run.EventPayload,
324 334 Image: run.Image, ImageFile: run.ImageFile, ImageDefinition: run.ImageDefinition,
325 335 ImageSHA: run.ImageSHA,
336 Network: run.Network,
326 337 Env: run.Env,
327 338 Secrets: secrets, Missing: missing,
328 339 Tag: run.Tag, TagAnnotated: run.TagAnnotated, ReleaseArtifacts: run.ReleaseArtifacts,