Skip to content

Commit 92b6db8

Browse files
Merge pull request #22672 from smarterclayton/create_signature
Passing `--to-signature` to release new creates the signature file
2 parents 3552587 + 2522ba4 commit 92b6db8

4 files changed

Lines changed: 98 additions & 0 deletions

File tree

contrib/completions/bash/oc

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

contrib/completions/zsh/oc

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/oc/cli/admin/release/new.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3030
"k8s.io/apimachinery/pkg/util/sets"
3131
"k8s.io/cli-runtime/pkg/genericclioptions"
32+
"k8s.io/client-go/pkg/version"
3233
kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
3334
"k8s.io/kubernetes/pkg/kubectl/util/templates"
3435

@@ -145,6 +146,7 @@ func NewRelease(f kcmdutil.Factory, parentName string, streams genericclioptions
145146
flags.StringVar(&o.ToImage, "to-image", o.ToImage, "The location to upload the release image to.")
146147
flags.StringVar(&o.ToImageBase, "to-image-base", o.ToImageBase, "If specified, the image to add the release layer on top of.")
147148
flags.StringVar(&o.ToImageBaseTag, "to-image-base-tag", o.ToImageBaseTag, "If specified, the image tag in the input to add the release layer on top of. Defaults to cluster-version-operator.")
149+
flags.StringVar(&o.ToSignature, "to-signature", o.ToSignature, "If specified, output a message that can be signed that describes this release. Requires --to-image.")
148150

149151
// misc
150152
flags.StringVarP(&o.Output, "output", "o", o.Output, "Output the mapping definition in this format.")
@@ -189,6 +191,7 @@ type NewOptions struct {
189191
ToImage string
190192
ToImageBase string
191193
ToImageBaseTag string
194+
ToSignature string
192195

193196
Mirror string
194197

@@ -262,6 +265,9 @@ func (o *NewOptions) Validate() error {
262265
return fmt.Errorf("must specify image mappings when no other source is defined")
263266
}
264267
}
268+
if len(o.ToSignature) > 0 && len(o.ToImage) == 0 {
269+
return fmt.Errorf("--to-signature requires --to-image")
270+
}
265271
if len(o.Mirror) > 0 && o.ReferenceMode != "" && o.ReferenceMode != "public" {
266272
return fmt.Errorf("--reference-mode must be public or empty when using --mirror")
267273
}
@@ -1160,6 +1166,22 @@ func (o *NewOptions) write(r io.Reader, is *imageapi.ImageStream, now time.Time)
11601166
fmt.Fprintf(o.ErrOut, "warning: %v\n", err)
11611167
}
11621168

1169+
// TODO: support a dry run that doesn't push the image, but requires append to have a dry-run mode
1170+
toRefWithDigest := toRef
1171+
toRefWithDigest.Tag = ""
1172+
toRefWithDigest.ID = options.ToDigest.String()
1173+
msg, err := createReleaseSignatureMessage(fmt.Sprintf("oc-adm-release-new/%s", version.Get().GitCommit), now, options.ToDigest.String(), toRefWithDigest.Exact())
1174+
if err != nil {
1175+
return err
1176+
}
1177+
if len(o.ToSignature) > 0 {
1178+
if err := ioutil.WriteFile(o.ToSignature, msg, 0644); err != nil {
1179+
return fmt.Errorf("unable to write signature file: %v", err)
1180+
}
1181+
} else {
1182+
klog.V(2).Infof("Signature for output:\n%s", string(msg))
1183+
}
1184+
11631185
default:
11641186
fmt.Fprintf(o.ErrOut, "info: Extracting operator contents to disk without building a release artifact\n")
11651187
if _, err := io.Copy(ioutil.Discard, r); err != nil {
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package release
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"time"
7+
)
8+
9+
// createReleaseSignatureMessage creates the core message to sign the release payload.
10+
func createReleaseSignatureMessage(signer string, now time.Time, releaseDigest, pullSpec string) ([]byte, error) {
11+
if len(signer) == 0 || now.IsZero() || len(releaseDigest) == 0 || len(pullSpec) == 0 {
12+
return nil, fmt.Errorf("you must specify a signer, current timestamp, release digest, and pull spec to sign")
13+
}
14+
15+
sig := &signature{
16+
Critical: criticalSignature{
17+
Type: "atomic container signature",
18+
Image: criticalImage{
19+
DockerManifestDigest: releaseDigest,
20+
},
21+
Identity: criticalIdentity{
22+
DockerReference: pullSpec,
23+
},
24+
},
25+
Optional: optionalSignature{
26+
Creator: signer,
27+
Timestamp: now.Unix(),
28+
},
29+
}
30+
return json.MarshalIndent(sig, "", " ")
31+
}
32+
33+
// An atomic container signature has the following schema:
34+
//
35+
// {
36+
// "critical": {
37+
// "type": "atomic container signature",
38+
// "image": {
39+
// "docker-manifest-digest": "sha256:817a12c32a39bbe394944ba49de563e085f1d3c5266eb8e9723256bc4448680e"
40+
// },
41+
// "identity": {
42+
// "docker-reference": "docker.io/library/busybox:latest"
43+
// }
44+
// },
45+
// "optional": {
46+
// "creator": "some software package v1.0.1-35",
47+
// "timestamp": 1483228800,
48+
// }
49+
// }
50+
type signature struct {
51+
Critical criticalSignature `json:"critical"`
52+
Optional optionalSignature `json:"optional"`
53+
}
54+
55+
type criticalSignature struct {
56+
Type string `json:"type"`
57+
Image criticalImage `json:"image"`
58+
Identity criticalIdentity `json:"identity"`
59+
}
60+
61+
type criticalImage struct {
62+
DockerManifestDigest string `json:"docker-manifest-digest"`
63+
}
64+
65+
type criticalIdentity struct {
66+
DockerReference string `json:"docker-reference"`
67+
}
68+
69+
type optionalSignature struct {
70+
Creator string `json:"creator"`
71+
Timestamp int64 `json:"timestamp"`
72+
}

0 commit comments

Comments
 (0)