Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions admission/cocoonset_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,12 +171,12 @@ func validateConnType(path string, ct cocoonv1.ConnType) string {
return fmt.Sprintf("%s.connType must be ssh, rdp, vnc, or adb, got %q", path, ct)
}

// cloneImageError rejects clone-mode images ParseRef cannot split: the snapshot pull joins repo[:tag] under a fixed registry base.
// cloneImageError rejects clone-mode images ParseRef cannot split: the snapshot pull joins repo[:tag] or repo@digest under a fixed registry base.
func cloneImageError(path, mode, image string) string {
if mode != string(cocoonv1.AgentModeClone) || image == "" || ociutil.IsRelativeRef(image) {
return ""
}
return fmt.Sprintf("%s.image %q must be repo[:tag] when mode is clone (no registry port or digest)", path, image)
return fmt.Sprintf("%s.image %q must be repo[:tag] or repo@sha256:<digest> when mode is clone (no registry port)", path, image)
}

// firecrackerModeError rejects firecracker with mode != run: an FC restore freezes the guest MAC+IP, so clones land on a dead lease.
Expand Down
17 changes: 16 additions & 1 deletion admission/cocoonset_validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,27 @@ func TestValidateCocoonSetSpec(t *testing.T) {
wantContains: []string{"agent.mode"},
},
{
name: "rejects clone-mode digest image",
name: "rejects clone-mode malformed digest image",
cs: &cocoonv1.CocoonSet{Spec: cocoonv1.CocoonSetSpec{
Agent: cocoonv1.AgentSpec{Image: "ubuntu@sha256:deadbeef"},
}},
wantContains: []string{"spec.agent.image", "must be repo[:tag]"},
},
{
name: "accepts clone-mode digest image",
cs: &cocoonv1.CocoonSet{Spec: cocoonv1.CocoonSetSpec{
Agent: cocoonv1.AgentSpec{Image: "simular/ubuntu@sha256:" + strings.Repeat("ab", 32)},
}},
},
{
name: "accepts clone-mode toolbox digest image",
cs: &cocoonv1.CocoonSet{Spec: cocoonv1.CocoonSetSpec{
Agent: cocoonv1.AgentSpec{Image: "x"},
Toolboxes: []cocoonv1.ToolboxSpec{
{Name: "tb", Mode: cocoonv1.ToolboxModeClone, Image: "tools@sha256:" + strings.Repeat("cd", 32)},
},
}},
},
{
name: "rejects clone-mode registry-port image",
cs: &cocoonv1.CocoonSet{Spec: cocoonv1.CocoonSetSpec{
Expand Down
2 changes: 1 addition & 1 deletion docs/validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ validation cannot express:
- `spec.toolboxes[*]` non-static modes require `image`, and get the same `os`/`connType`/`backend` enum checks and firecracker-Windows/cloudimg-URL rules as `spec.agent`
- `spec.toolboxes[*].backend` must match `spec.agent.backend` (static toolboxes skip this check)
- `spec.toolboxes[*]` static-mode entries: `connType` may be left unset (falls back to `vnc` when `vncPort` is set, otherwise by OS: Windows→rdp, Android→adb, anything else→ssh); a non-empty value must be one of `ssh` / `rdp` / `vnc` / `adb`
- clone-mode images (`spec.agent.image`, `spec.toolboxes[*].image`) must be a relative `repo[:tag]` with a lowercase repo path — a registry port, a digest, and uppercase repo characters are rejected (a bare registry host such as `ghcr.io/...` is accepted; the tag keeps the OCI tag character set), because the snapshot pull path resolves images under the org registry base and has no external-ref fallback
- clone-mode images (`spec.agent.image`, `spec.toolboxes[*].image`) must be a relative `repo[:tag]` or `repo@sha256:<64 hex>` (or `sha512`) with a lowercase repo path — a registry port, a malformed digest, a tag and a digest together, and uppercase repo characters are rejected (a bare registry host such as `ghcr.io/...` is accepted; the tag keeps the OCI tag character set), because the snapshot pull path resolves images under the org registry base and has no external-ref fallback
- `spec.snapshotPolicy ∈ {always, main-only, never}`
- `spec.hibernatePolicy ∈ {retain, release}`

Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/cocoonstack/cocoon-webhook
go 1.27.1

require (
github.com/cocoonstack/cocoon-common v0.3.6
github.com/cocoonstack/cocoon-common v0.3.7-0.20260928172801-b03e52d1f721
github.com/projecteru2/core v0.1.5
github.com/prometheus/client_golang v1.24.1
github.com/prometheus/client_model v0.6.3
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ github.com/cockroachdb/logtags v0.0.0-20241215232642-bb51bb14a506 h1:ASDL+UJcILM
github.com/cockroachdb/logtags v0.0.0-20241215232642-bb51bb14a506/go.mod h1:Mw7HqKr2kdtu6aYGn3tPmAftiP3QPX63LdK/zcariIo=
github.com/cockroachdb/redact v1.1.8 h1:8eVLLj6juKxiKrAEw2b8cJvNqWq++U8WOfQFuL7KTaA=
github.com/cockroachdb/redact v1.1.8/go.mod h1:GceHHpJ0rMDpYARL5In88Alq/xMBUtVlz7Qxix6ZVkw=
github.com/cocoonstack/cocoon-common v0.3.6 h1:iRrIyeRXHgYuwKYDOB6TjidiZL1oeyfOYkTGXDSD/PU=
github.com/cocoonstack/cocoon-common v0.3.6/go.mod h1:Gw/zBYqntoxpcT//2rBcXHQz6JErin/IsTsEmoQdGr0=
github.com/cocoonstack/cocoon-common v0.3.7-0.20260928172801-b03e52d1f721 h1:xUtBRurq3bzpL2D0P0Apeiq4jjplNLOD/Kbd6G14WwM=
github.com/cocoonstack/cocoon-common v0.3.7-0.20260928172801-b03e52d1f721/go.mod h1:Gw/zBYqntoxpcT//2rBcXHQz6JErin/IsTsEmoQdGr0=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Expand Down
Loading