Skip to content

Commit dfb595a

Browse files
committed
Adjust deploy input normalization produce valid inputs
The new `deploy --dry-run` feature relies on the assumption that JSON-ified normalized input objects are valid input back, which wasn't an assumption we were validating. This adjusts `TestNormalizeInput` to take the output of normalization and pass it back through for an added "roundtrip" test. In doing so, I found a single edge case where our normalized objects were *not* valid/correct inputs (tag->tag copy of a manifest) and made a minor adjustment to the normalization to close that hole. I also added a new test case for that edge case, and made sure *it* round-trips correctly too.
1 parent 10f3b08 commit dfb595a

File tree

3 files changed

+55
-15
lines changed

3 files changed

+55
-15
lines changed

.test/deploy-dry-run-test.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,7 @@
347347
"localhost:3000/cirros"
348348
],
349349
"lookup": {
350-
"": "oisupport/staging-amd64"
350+
"": "oisupport/staging-amd64:34bb44c7d8b6fb7a337fcee0afa7c3a84148e35db6ab83041714c3e6d4c6238b"
351351
},
352352
"copyFrom": "oisupport/staging-amd64:34bb44c7d8b6fb7a337fcee0afa7c3a84148e35db6ab83041714c3e6d4c6238b"
353353
}
@@ -381,5 +381,5 @@
381381
"lookup": {
382382
"": "tianon/test"
383383
},
384-
"copyFrom": "tianon/test:eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee@sha256:73614cc99c500aa4fa061368ed349df24a81844e3c2e6d0c31f290a7c8d73c22"
384+
"copyFrom": "tianon/test@sha256:73614cc99c500aa4fa061368ed349df24a81844e3c2e6d0c31f290a7c8d73c22"
385385
}

cmd/deploy/input.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,8 +223,17 @@ func NormalizeInput(raw inputRaw) (inputNormalized, error) {
223223
normal.Refs[i].Digest = refsDigest
224224
}
225225

226+
// if we have a digest and we're performing a copy, the tag we're copying *from* is no longer relevant information
227+
if refsDigest != "" && normal.CopyFrom != nil {
228+
normal.CopyFrom.Tag = ""
229+
}
230+
226231
// explicitly clear tag and digest from lookup entries (now that we've inferred any "CopyFrom" out of them, they no longer have any meaning)
227232
for d, ref := range normal.Lookup {
233+
if d == "" && refsDigest == "" && ref.Tag != "" && normal.CopyFrom != nil && ref.Tag == normal.CopyFrom.Tag {
234+
// let the "fallback" ref keep a tag when it's the tag we're copying and there's no known digest (this allows our normalized objects to still be completely valid "raw" inputs)
235+
continue
236+
}
228237
ref.Tag = ""
229238
ref.Digest = ""
230239
normal.Lookup[d] = ref

cmd/deploy/input_test.go

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,16 @@ func TestNormalizeInput(t *testing.T) {
368368
"refs": [ "localhost:5000/example:test" ],
369369
"lookup": { "": "tianon/true:oci" }
370370
}`,
371-
`{"type":"manifest","refs":["localhost:5000/example:test"],"lookup":{"":"tianon/true"},"copyFrom":"tianon/true:oci"}`,
371+
`{"type":"manifest","refs":["localhost:5000/example:test"],"lookup":{"":"tianon/true:oci"},"copyFrom":"tianon/true:oci"}`,
372+
},
373+
{
374+
"copy manifest (tag and digest)",
375+
`{
376+
"type": "manifest",
377+
"refs": [ "localhost:5000/example:test" ],
378+
"lookup": { "": "tianon/true:oci@sha256:9ef42f1d602fb423fad935aac1caa0cfdbce1ad7edce64d080a4eb7b13f7cd9d" }
379+
}`,
380+
`{"type":"manifest","refs":["localhost:5000/example:test@sha256:9ef42f1d602fb423fad935aac1caa0cfdbce1ad7edce64d080a4eb7b13f7cd9d"],"lookup":{"":"tianon/true"},"copyFrom":"tianon/true@sha256:9ef42f1d602fb423fad935aac1caa0cfdbce1ad7edce64d080a4eb7b13f7cd9d"}`,
372381
},
373382

374383
{
@@ -431,19 +440,41 @@ func TestNormalizeInput(t *testing.T) {
431440
} {
432441
x := x // https://github.com/golang/go/issues/60078
433442
t.Run(x.name, func(t *testing.T) {
434-
var raw inputRaw
435-
if err := json.Unmarshal([]byte(x.raw), &raw); err != nil {
436-
t.Fatalf("JSON parse error: %v", err)
437-
}
438-
normal, err := NormalizeInput(raw)
439-
if err != nil {
440-
t.Fatalf("normalize error: %v", err)
441-
}
442-
if b, err := json.Marshal(normal); err != nil {
443-
t.Fatalf("JSON generate error: %v", err)
444-
} else if string(b) != x.normal {
445-
t.Fatalf("got:\n%s\n\nexpected:\n%s\n", string(b), x.normal)
443+
var b []byte // this will hold the final "normalized" JSON (so we can test round-trip afterwards)
444+
445+
{ // start a sub-block to ensure variable scoping is clean and roundtrip has to error if there's a typo
446+
var raw inputRaw
447+
if err := json.Unmarshal([]byte(x.raw), &raw); err != nil {
448+
t.Fatalf("JSON parse error: %v", err)
449+
}
450+
normal, err := NormalizeInput(raw)
451+
if err != nil {
452+
t.Fatalf("normalize error: %v", err)
453+
}
454+
b, err = json.Marshal(normal)
455+
if err != nil {
456+
t.Fatalf("JSON generate error: %v", err)
457+
} else if string(b) != x.normal {
458+
t.Fatalf("got:\n%s\n\nexpected:\n%s\n", string(b), x.normal)
459+
}
446460
}
461+
462+
t.Run("roundtrip", func(t *testing.T) {
463+
// now that we've tested that, let's round trip the normalized copy back through the normalizer to make sure it's valid/correctly parsed input too ("deploy --dry-run" leans on that assumption)
464+
var raw inputRaw
465+
if err := json.Unmarshal(b, &raw); err != nil {
466+
t.Fatalf("JSON parse error: %v", err)
467+
}
468+
normal, err := NormalizeInput(raw)
469+
if err != nil {
470+
t.Fatalf("normalize error: %v", err)
471+
}
472+
if roundtripB, err := json.Marshal(normal); err != nil {
473+
t.Fatalf("JSON generate error: %v", err)
474+
} else if string(roundtripB) != x.normal {
475+
t.Fatalf("got:\n%s\n\nexpected:\n%s\n", string(roundtripB), x.normal)
476+
}
477+
})
447478
})
448479
}
449480
}

0 commit comments

Comments
 (0)