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 start service containers beside it

c1929d2fceb5e9e65204d5eef907d0781b9a5391 · Keenan Nemetz · 5d ago · parent 1d06b008

ci/agent/validate_test.go +58

@@ -155,3 +155,61 @@ func TestLeasedSecretsAreRevalidated(t *testing.T) {
155 155 t.Fatalf("a valid job with secrets was refused: %v", err)
156 156 }
157 157 }
158
159 // Services arrive in the lease, so the runner checks the names, images, and
160 // environment it is about to turn into container arguments.
161 func TestServicesAreRevalidated(t *testing.T) {
162 t.Parallel()
163 valid := func() *runnerJob {
164 j := goodJob(t)
165 j.Network = ci.NetworkBridge
166 j.Services = []runnerService{{
167 ID: "db",
168 Image: "postgres:17",
169 Env: map[string]string{"POSTGRES_PASSWORD": "test"},
170 }}
171 return j
172 }
173 if err := validateJob(valid()); err != nil {
174 t.Fatalf("a valid service job was refused: %v", err)
175 }
176 cases := []struct {
177 name string
178 mutate func(*runnerJob)
179 want string
180 }{
181 {"no bridge", func(j *runnerJob) { j.Network = "" }, "network: bridge"},
182 {"none", func(j *runnerJob) { j.Network = ci.NetworkNone }, "network: bridge"},
183 {"bad name", func(j *runnerJob) { j.Services[0].ID = "DB" }, "service name"},
184 {"duplicate name", func(j *runnerJob) {
185 j.Services = append(j.Services, j.Services[0])
186 }, "listed twice"},
187 {"no image", func(j *runnerJob) { j.Services[0].Image = "" }, "image"},
188 {"image file", func(j *runnerJob) { j.Services[0].Image = "file:db" }, "reserved for image-file"},
189 {"bad env", func(j *runnerJob) { j.Services[0].Env = map[string]string{"A B": "1"} }, "variable name"},
190 {"multiline env", func(j *runnerJob) { j.Services[0].Env = map[string]string{"A": "1\n2"} }, "one line"},
191 }
192 for _, c := range cases {
193 job := valid()
194 c.mutate(job)
195 err := validateJob(job)
196 if err == nil {
197 t.Fatalf("%s: accepted", c.name)
198 }
199 if !strings.Contains(err.Error(), c.want) {
200 t.Fatalf("%s: error does not mention %q: %v", c.name, c.want, err)
201 }
202 }
203 }
204
205 func TestTooManyServicesAreRefused(t *testing.T) {
206 t.Parallel()
207 j := goodJob(t)
208 j.Network = ci.NetworkBridge
209 for i := 0; i <= ci.MaxServices; i++ {
210 j.Services = append(j.Services, runnerService{ID: "s" + string(rune('a'+i)), Image: "alpine:3.22"})
211 }
212 if err := validateJob(j); err == nil || !strings.Contains(err.Error(), "services, limit") {
213 t.Fatalf("too many services: %v", err)
214 }
215 }