add platform flag for image add and sync

Signed-off-by: Adam Martin <adam.martin@rancherfederal.com>
This commit is contained in:
Adam Martin
2024-01-28 19:48:16 -05:00
parent 8256aa55ce
commit a05d21c052
5 changed files with 61 additions and 24 deletions

View File

@@ -61,13 +61,15 @@ func storeFile(ctx context.Context, s *store.Layout, fi v1alpha1.File) error {
type AddImageOpts struct {
*RootOpts
Name string
Key string
Name string
Key string
Platform string
}
func (o *AddImageOpts) AddFlags(cmd *cobra.Command) {
f := cmd.Flags()
f.StringVarP(&o.Key, "key", "k", "", "(Optional) Path to the key for digital signature verification")
f.StringVarP(&o.Platform, "platform", "p", "", "(Optional) Specific platform to save. i.e. linux/amd64. Defaults to all if flag is omitted.")
}
func AddImageCmd(ctx context.Context, o *AddImageOpts, s *store.Layout, reference string) error {
@@ -86,10 +88,10 @@ func AddImageCmd(ctx context.Context, o *AddImageOpts, s *store.Layout, referenc
l.Infof("signature verified for image [%s]", cfg.Name)
}
return storeImage(ctx, s, cfg)
return storeImage(ctx, s, cfg, o.Platform)
}
func storeImage(ctx context.Context, s *store.Layout, i v1alpha1.Image) error {
func storeImage(ctx context.Context, s *store.Layout, i v1alpha1.Image, platform string) error {
l := log.FromContext(ctx)
r, err := name.ParseReference(i.Name)
@@ -97,7 +99,7 @@ func storeImage(ctx context.Context, s *store.Layout, i v1alpha1.Image) error {
return err
}
err = cosign.SaveImage(ctx, s, r.Name())
err = cosign.SaveImage(ctx, s, r.Name(), platform)
if err != nil {
return err
}

View File

@@ -66,14 +66,14 @@ func InfoCmd(ctx context.Context, o *InfoOpts, s *store.Layout) error {
return err
}
i := newItem(s, desc, internalManifest, internalDesc.Platform.Architecture, o)
i := newItem(s, desc, internalManifest, fmt.Sprintf("%s/%s", internalDesc.Platform.OS, internalDesc.Platform.Architecture), o)
var emptyItem item
if i != emptyItem {
items = append(items, i)
}
}
// handle single arch docker images
} else if desc.MediaType == consts.DockerManifestSchema2 {
// handle "non" multi-arch images
} else if desc.MediaType == consts.DockerManifestSchema2 || desc.MediaType == consts.OCIManifestSchema1 {
var m ocispec.Manifest
if err := json.NewDecoder(rc).Decode(&m); err != nil {
return err
@@ -90,11 +90,19 @@ func InfoCmd(ctx context.Context, o *InfoOpts, s *store.Layout) error {
if err := json.NewDecoder(rc).Decode(&internalManifest); err != nil {
return err
}
i := newItem(s, desc, m, internalManifest.Architecture, o)
var emptyItem item
if i != emptyItem {
items = append(items, i)
if internalManifest.Architecture != "" {
i := newItem(s, desc, m, fmt.Sprintf("%s/%s", internalManifest.OS, internalManifest.Architecture), o)
var emptyItem item
if i != emptyItem {
items = append(items, i)
}
} else {
i := newItem(s, desc, m, "-", o)
var emptyItem item
if i != emptyItem {
items = append(items, i)
}
}
// handle the rest
} else {
@@ -132,7 +140,7 @@ func InfoCmd(ctx context.Context, o *InfoOpts, s *store.Layout) error {
func buildTable(items ...item) {
// Create a table for the results
table := tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"Reference", "Type", "Arch", "# Layers", "Size"})
table.SetHeader([]string{"Reference", "Type", "Platform", "# Layers", "Size"})
table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
table.SetRowLine(false)
table.SetAutoMergeCellsByColumnIndex([]int{0})
@@ -142,7 +150,7 @@ func buildTable(items ...item) {
row := []string{
i.Reference,
i.Type,
i.Architecture,
i.Platform,
fmt.Sprintf("%d", i.Layers),
i.Size,
}
@@ -163,7 +171,7 @@ func buildJson(item ...item) string {
type item struct {
Reference string
Type string
Architecture string
Platform string
Layers int
Size string
}
@@ -174,12 +182,12 @@ func (a byReferenceAndArch) Len() int { return len(a) }
func (a byReferenceAndArch) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a byReferenceAndArch) Less(i, j int) bool {
if a[i].Reference == a[j].Reference {
return a[i].Architecture < a[j].Architecture
return a[i].Platform < a[j].Platform
}
return a[i].Reference < a[j].Reference
}
func newItem(s *store.Layout, desc ocispec.Descriptor, m ocispec.Manifest, arch string, o *InfoOpts) item {
func newItem(s *store.Layout, desc ocispec.Descriptor, m ocispec.Manifest, plat string, o *InfoOpts) item {
// skip listing cosign items
if desc.Annotations["kind"] == "dev.cosignproject.cosign/atts" ||
desc.Annotations["kind"] == "dev.cosignproject.cosign/sigs" ||
@@ -217,7 +225,7 @@ func newItem(s *store.Layout, desc ocispec.Descriptor, m ocispec.Manifest, arch
return item{
Reference: ref.Name(),
Type: ctype,
Architecture: arch,
Platform: plat,
Layers: len(m.Layers),
Size: byteCountSI(size),
}

View File

@@ -29,6 +29,7 @@ type SyncOpts struct {
ContentFiles []string
Key string
Products []string
Platform string
}
func (o *SyncOpts) AddFlags(cmd *cobra.Command) {
@@ -37,6 +38,7 @@ func (o *SyncOpts) AddFlags(cmd *cobra.Command) {
f.StringSliceVarP(&o.ContentFiles, "files", "f", []string{}, "Path to content files")
f.StringVarP(&o.Key, "key", "k", "", "(Optional) Path to the key for signature verification")
f.StringSliceVar(&o.Products, "products", []string{}, "Used for RGS Carbide customers to supply a product and version and Hauler will retrieve the images. i.e. '--product rancher=v2.7.6'")
f.StringVarP(&o.Platform, "platform", "p", "", "(Optional) Specific platform to save. i.e. linux/amd64. Defaults to all if flag is omitted.")
}
func SyncCmd(ctx context.Context, o *SyncOpts, s *store.Layout) error {
@@ -52,7 +54,7 @@ func SyncCmd(ctx context.Context, o *SyncOpts, s *store.Layout) error {
img := v1alpha1.Image{
Name: manifestLoc,
}
err := storeImage(ctx, s, img)
err := storeImage(ctx, s, img, o.Platform)
if err != nil {
return err
}
@@ -154,8 +156,14 @@ func processContent(ctx context.Context, fi *os.File, o *SyncOpts, s *store.Layo
}
l.Infof("signature verified for image [%s]", i.Name)
}
err = storeImage(ctx, s, i)
// Check if the user provided a platform.
platform := o.Platform
if i.Platform != "" {
platform = i.Platform
}
err = storeImage(ctx, s, i, platform)
if err != nil {
return err
}

View File

@@ -24,4 +24,8 @@ type Image struct {
// Path is the path to the cosign public key used for verifying image signatures
//Key string `json:"key,omitempty"`
Key string `json:"key"`
// Platform of the image to be pulled. If not specified, all platforms will be pulled.
//Platform string `json:"key,omitempty"`
Platform string `json:"platform"`
}

View File

@@ -11,6 +11,7 @@ import (
"time"
"bufio"
"embed"
"strings"
"oras.land/oras-go/pkg/content"
"github.com/rancherfederal/hauler/pkg/store"
@@ -41,7 +42,7 @@ func VerifySignature(ctx context.Context, s *store.Layout, keyPath string, ref s
}
// SaveImage saves image and any signatures/attestations to the store.
func SaveImage(ctx context.Context, s *store.Layout, ref string) error {
func SaveImage(ctx context.Context, s *store.Layout, ref string, platform string) error {
operation := func() error {
cosignBinaryPath, err := getCosignPath(ctx)
if err != nil {
@@ -49,9 +50,23 @@ func SaveImage(ctx context.Context, s *store.Layout, ref string) error {
}
cmd := exec.Command(cosignBinaryPath, "save", ref, "--dir", s.Root)
// Conditionally add platform.
if platform != "" {
cmd.Args = append(cmd.Args, "--platform", platform)
}
output, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("error adding image to store: %v, output: %s", err, output)
if strings.Contains(string(output), "specified reference is not a multiarch image") {
// Rerun the command without the platform flag
cmd = exec.Command(cosignBinaryPath, "save", ref, "--dir", s.Root)
output, err = cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("error adding image to store: %v, output: %s", err, output)
}
} else {
return fmt.Errorf("error adding image to store: %v, output: %s", err, output)
}
}
return nil