Skip to content

Commit cd484ef

Browse files
committed
feat(harness): implement subject image or version pinning for each case
1 parent 59fc38f commit cd484ef

4 files changed

Lines changed: 91 additions & 2 deletions

File tree

cmd/harness/main.go

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ func testCmd() *cobra.Command {
149149
continue
150150
}
151151
for _, s := range subs {
152-
pairs = append(pairs, runPair{tc: tc, subject: s})
152+
pairs = append(pairs, runPair{tc: tc, subject: applyCasePin(s, tc)})
153153
}
154154
}
155155
if len(subjectsToLoop) > 1 {
@@ -194,7 +194,7 @@ func testCmd() *cobra.Command {
194194
}
195195
}
196196
for _, s := range subjects {
197-
pairs = append(pairs, runPair{tc: tc, subject: s})
197+
pairs = append(pairs, runPair{tc: tc, subject: applyCasePin(s, tc)})
198198
}
199199
}
200200
if len(pairs) == 0 {
@@ -685,6 +685,21 @@ func versionCmd() *cobra.Command {
685685
}
686686
}
687687

688+
// applyCasePin overlays a case's subject_image/subject_version pin onto a
689+
// registry-resolved Subject. The global --image/--version CLI flags are applied
690+
// later in runner.applySubjectOverrides, so an explicit CLI flag still wins:
691+
//
692+
// CLI --image/--version > case subject_image/subject_version > registry default
693+
func applyCasePin(s config.Subject, tc *config.TestCase) config.Subject {
694+
if tc.SubjectImage != "" {
695+
s = s.WithImage(tc.SubjectImage)
696+
}
697+
if tc.SubjectVersion != "" {
698+
s = s.WithVersion(tc.SubjectVersion)
699+
}
700+
return s
701+
}
702+
688703
func resolveSubjects(tc *config.TestCase, subjectFlag string) ([]config.Subject, error) {
689704
if subjectFlag != "" {
690705
// Support comma-separated subjects: -s vector,fluent-bit,logstash

cmd/harness/main_test.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package main
2+
3+
import (
4+
"testing"
5+
6+
"github.com/VirtualMetric/PipeBench/internal/config"
7+
)
8+
9+
// TestApplyCasePin verifies that a case-level subject pin is applied with the
10+
// correct precedence:
11+
//
12+
// CLI --image/--version > case subject_image/subject_version > registry default
13+
func TestApplyCasePin(t *testing.T) {
14+
t.Run("pin overrides registry default", func(t *testing.T) {
15+
base := config.Subject{Image: "vmetric/director", Version: "2.0.3"}
16+
tc := &config.TestCase{
17+
SubjectImage: "vmetric/director-enterprise",
18+
SubjectVersion: "latest",
19+
}
20+
got := applyCasePin(base, tc)
21+
if got.Image != "vmetric/director-enterprise" {
22+
t.Errorf("image: got %q, want %q", got.Image, "vmetric/director-enterprise")
23+
}
24+
if got.Version != "latest" {
25+
t.Errorf("version: got %q, want %q", got.Version, "latest")
26+
}
27+
})
28+
29+
t.Run("empty pin leaves subject unchanged", func(t *testing.T) {
30+
base := config.Subject{Image: "vmetric/director", Version: "2.0.3"}
31+
tc := &config.TestCase{}
32+
got := applyCasePin(base, tc)
33+
if got.Image != "vmetric/director" {
34+
t.Errorf("image: got %q, want %q", got.Image, "vmetric/director")
35+
}
36+
if got.Version != "2.0.3" {
37+
t.Errorf("version: got %q, want %q", got.Version, "2.0.3")
38+
}
39+
})
40+
41+
t.Run("CLI WithImage/WithVersion overrides pin (precedence)", func(t *testing.T) {
42+
// Simulate: registry default → applyCasePin (enterprise pin) →
43+
// Subject.WithImage/WithVersion (CLI flags, applied by applySubjectOverrides).
44+
base := config.Subject{Image: "vmetric/director", Version: "2.0.3"}
45+
tc := &config.TestCase{
46+
SubjectImage: "vmetric/director-enterprise",
47+
SubjectVersion: "latest",
48+
}
49+
pinned := applyCasePin(base, tc)
50+
51+
// CLI --image=vmetric/director overrides the enterprise pin.
52+
final := pinned.WithImage("vmetric/director").WithVersion("3.0.0")
53+
if final.Image != "vmetric/director" {
54+
t.Errorf("image: got %q, want %q (CLI must win)", final.Image, "vmetric/director")
55+
}
56+
if final.Version != "3.0.0" {
57+
t.Errorf("version: got %q, want %q (CLI must win)", final.Version, "3.0.0")
58+
}
59+
// Pin must not mutate the original.
60+
if pinned.Image != "vmetric/director-enterprise" {
61+
t.Errorf("pin must not mutate: got %q after WithImage call", pinned.Image)
62+
}
63+
})
64+
}

internal/config/case.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,15 @@ type TestCase struct {
8888
// ingest. Generalizes the original hardcoded tls_tcp guard.
8989
Requires []string `yaml:"requires"`
9090

91+
// SubjectImage and SubjectVersion pin the subject container image for this
92+
// specific case. Applied after the registry default and before the global
93+
// CLI --image/--version flags, so: CLI flag > case pin > registry default.
94+
// Leave empty to use the registry default (or whatever --image/--version
95+
// specifies). Non-strict YAML decode means older harness binaries silently
96+
// ignore these fields — they fall back to the registry default.
97+
SubjectImage string `yaml:"subject_image"`
98+
SubjectVersion string `yaml:"subject_version"`
99+
91100
Subjects []string `yaml:"subjects"`
92101
Configurations map[string]Configuration `yaml:"configurations"`
93102
Correctness CorrectnessConfig `yaml:"correctness"`

internal/orchestrator/docker.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,7 @@ services:
509509
user: "0:0"
510510
{{- end }}
511511
{{- if .AgentCommand }}
512+
entrypoint: ""
512513
command: {{ .AgentCommand }}
513514
{{- end }}
514515
{{- if .AgentEnv }}

0 commit comments

Comments
 (0)