mirror of
https://github.com/linuxkit/linuxkit.git
synced 2025-07-19 01:06:27 +00:00
Merge pull request #3957 from deitch/push-with-sboms
push sboms as well
This commit is contained in:
commit
54bd9073c2
40
src/cmd/linuxkit/cache/push.go
vendored
40
src/cmd/linuxkit/cache/push.go
vendored
@ -7,7 +7,7 @@ import (
|
||||
namepkg "github.com/google/go-containerregistry/pkg/name"
|
||||
"github.com/google/go-containerregistry/pkg/v1/remote"
|
||||
"github.com/google/go-containerregistry/pkg/v1/validate"
|
||||
"github.com/linuxkit/linuxkit/src/cmd/linuxkit/registry"
|
||||
"github.com/linuxkit/linuxkit/src/cmd/linuxkit/util"
|
||||
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
@ -57,10 +57,26 @@ func (p *Provider) Push(name string, withManifest bool) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not get digest for index %s: %v", name, err)
|
||||
}
|
||||
manifest, err := ii.IndexManifest()
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not read images in index: %v", err)
|
||||
}
|
||||
|
||||
// get the existing image, if any
|
||||
desc, err := remote.Get(ref, remoteOptions...)
|
||||
if err == nil && desc != nil && dig == desc.Digest {
|
||||
fmt.Printf("%s index already available on remote registry, skipping push", name)
|
||||
return nil
|
||||
if err == nil && desc != nil {
|
||||
if dig == desc.Digest {
|
||||
fmt.Printf("%s index already available on remote registry, skipping push", name)
|
||||
return nil
|
||||
}
|
||||
// we have a different index, need to cross-reference and only override relevant stuff
|
||||
remoteIndex, err := desc.ImageIndex()
|
||||
if err == nil && remoteIndex != nil {
|
||||
ii, err = util.AppendIndex(ii, remoteIndex)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not append remote index to local index: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
log.Debugf("pushing index %s", name)
|
||||
// this is an index, so we not only want to write the index, but tags for each arch-specific image in it
|
||||
@ -68,10 +84,6 @@ func (p *Provider) Push(name string, withManifest bool) error {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Pushed index %s\n", name)
|
||||
manifest, err := ii.IndexManifest()
|
||||
if err != nil {
|
||||
return fmt.Errorf("successfully pushed index, but could not read images in index: %v", err)
|
||||
}
|
||||
log.Debugf("pushing individual images in the index %s", name)
|
||||
for _, m := range manifest.Manifests {
|
||||
if m.Platform == nil || m.Platform.Architecture == "" {
|
||||
@ -113,17 +125,5 @@ func (p *Provider) Push(name string, withManifest bool) error {
|
||||
return fmt.Errorf("name %s unknown in cache", name)
|
||||
}
|
||||
|
||||
if !withManifest {
|
||||
return nil
|
||||
}
|
||||
// Even though we may have pushed the index, we want to be sure that we have an index that includes every architecture on the registry,
|
||||
// not just those that were in our local cache. So we call PushManifest to push an index that includes all arch-specific images
|
||||
// already in the registry.
|
||||
fmt.Printf("Pushing index based on all arch-specific images in registry %s\n", name)
|
||||
_, _, err = registry.PushManifest(name, options...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
16
src/cmd/linuxkit/cache/source.go
vendored
16
src/cmd/linuxkit/cache/source.go
vendored
@ -15,16 +15,12 @@ import (
|
||||
"github.com/google/go-containerregistry/pkg/v1/tarball"
|
||||
intoto "github.com/in-toto/in-toto-golang/in_toto"
|
||||
lktspec "github.com/linuxkit/linuxkit/src/cmd/linuxkit/spec"
|
||||
"github.com/linuxkit/linuxkit/src/cmd/linuxkit/util"
|
||||
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
annotationDockerReferenceType = "vnd.docker.reference.type"
|
||||
annotationAttestationManifest = "attestation-manifest"
|
||||
annotationDockerReferenceDigest = "vnd.docker.reference.digest"
|
||||
annotationInTotoPredicateType = "in-toto.io/predicate-type"
|
||||
annotationSPDXDoc = "https://spdx.dev/Document"
|
||||
inTotoJsonMediaType = "application/vnd.in-toto+json"
|
||||
inTotoJsonMediaType = "application/vnd.in-toto+json"
|
||||
)
|
||||
|
||||
// ImageSource a source for an image in the OCI distribution cache.
|
||||
@ -143,8 +139,8 @@ func (c ImageSource) SBoMs() ([]io.ReadCloser, error) {
|
||||
desc := descs[0]
|
||||
|
||||
annotations := map[string]string{
|
||||
annotationDockerReferenceType: annotationAttestationManifest,
|
||||
annotationDockerReferenceDigest: desc.Digest.String(),
|
||||
util.AnnotationDockerReferenceType: util.AnnotationAttestationManifest,
|
||||
util.AnnotationDockerReferenceDigest: desc.Digest.String(),
|
||||
}
|
||||
descs, err = partial.FindManifests(index, matchAllAnnotations(annotations))
|
||||
if err != nil {
|
||||
@ -183,7 +179,7 @@ func (c ImageSource) SBoMs() ([]io.ReadCloser, error) {
|
||||
var readers []io.ReadCloser
|
||||
for i, layer := range manifest.Layers {
|
||||
annotations := layer.Annotations
|
||||
if annotations[annotationInTotoPredicateType] != annotationSPDXDoc || layer.MediaType != inTotoJsonMediaType {
|
||||
if annotations[util.AnnotationInTotoPredicateType] != util.AnnotationSPDXDoc || layer.MediaType != inTotoJsonMediaType {
|
||||
continue
|
||||
}
|
||||
// get the actual blob of the layer
|
||||
@ -201,7 +197,7 @@ func (c ImageSource) SBoMs() ([]io.ReadCloser, error) {
|
||||
if err := json.Unmarshal(buf.Bytes(), &stmt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if stmt.PredicateType != annotationSPDXDoc {
|
||||
if stmt.PredicateType != util.AnnotationSPDXDoc {
|
||||
return nil, fmt.Errorf("unexpected predicate type %s", stmt.PredicateType)
|
||||
}
|
||||
sbom := stmt.Predicate
|
||||
|
3
src/cmd/linuxkit/cache/write.go
vendored
3
src/cmd/linuxkit/cache/write.go
vendored
@ -20,6 +20,7 @@ import (
|
||||
"github.com/google/go-containerregistry/pkg/v1/remote"
|
||||
"github.com/google/go-containerregistry/pkg/v1/types"
|
||||
lktspec "github.com/linuxkit/linuxkit/src/cmd/linuxkit/spec"
|
||||
lktutil "github.com/linuxkit/linuxkit/src/cmd/linuxkit/util"
|
||||
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
@ -320,7 +321,7 @@ func (p *Provider) IndexWrite(ref *reference.Spec, descriptors ...v1.Descriptor)
|
||||
appliedManifests[m.Digest] = true
|
||||
continue
|
||||
}
|
||||
value, ok := m.Annotations[annotationDockerReferenceDigest]
|
||||
value, ok := m.Annotations[lktutil.AnnotationDockerReferenceDigest]
|
||||
if !ok {
|
||||
manifest.Manifests = append(manifest.Manifests, m)
|
||||
appliedManifests[m.Digest] = true
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
"github.com/google/go-containerregistry/pkg/v1/empty"
|
||||
"github.com/google/go-containerregistry/pkg/v1/mutate"
|
||||
"github.com/google/go-containerregistry/pkg/v1/remote"
|
||||
"github.com/linuxkit/linuxkit/src/cmd/linuxkit/util"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@ -30,6 +31,7 @@ func PushManifest(img string, options ...remote.Option) (hash string, length int
|
||||
if err != nil {
|
||||
return hash, length, fmt.Errorf("parsing %s: %w", img, err)
|
||||
}
|
||||
|
||||
adds := make([]mutate.IndexAddendum, 0, len(platformsToSearchForIndex))
|
||||
for i, platform := range platformsToSearchForIndex {
|
||||
osArchArr := strings.Split(platform, "/")
|
||||
@ -70,6 +72,20 @@ func PushManifest(img string, options ...remote.Option) (hash string, length int
|
||||
|
||||
// add the desc to the index we will push
|
||||
index := mutate.AppendManifests(empty.Index, adds...)
|
||||
// base index with which we are working
|
||||
// get the existing index, if any
|
||||
desc, err := remote.Get(baseRef, options...)
|
||||
if err == nil && desc != nil {
|
||||
ii, err := desc.ImageIndex()
|
||||
if err != nil {
|
||||
return hash, length, fmt.Errorf("could not get index for existing reference %s: %w", img, err)
|
||||
}
|
||||
index, err = util.AppendIndex(index, ii)
|
||||
if err != nil {
|
||||
return hash, length, fmt.Errorf("could not append existing index for %s: %w", img, err)
|
||||
}
|
||||
}
|
||||
|
||||
size, err := index.Size()
|
||||
if err != nil {
|
||||
return hash, length, fmt.Errorf("getting index size: %w", err)
|
||||
|
97
src/cmd/linuxkit/util/index.go
Normal file
97
src/cmd/linuxkit/util/index.go
Normal file
@ -0,0 +1,97 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
v1 "github.com/google/go-containerregistry/pkg/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
AnnotationDockerReferenceDigest = "vnd.docker.reference.digest"
|
||||
AnnotationDockerReferenceType = "vnd.docker.reference.type"
|
||||
AnnotationAttestationManifest = "attestation-manifest"
|
||||
AnnotationInTotoPredicateType = "in-toto.io/predicate-type"
|
||||
AnnotationSPDXDoc = "https://spdx.dev/Document"
|
||||
)
|
||||
|
||||
// AppendIndex appends the elements of secondary ImageIndex into primary ImageIndex,
|
||||
// returning the updated primary ImageIndex.
|
||||
// In the case of conflicts, the primary ImageIndex wins.
|
||||
// For example, if both have a manifest for a specific platform, then use the one from primary.
|
||||
// The append is aware of the buildkit-style attestations, and will keep any attestations that point to a valid
|
||||
// manifest in the list, discarding any that do not.
|
||||
func AppendIndex(primary, secondary v1.ImageIndex) (v1.ImageIndex, error) {
|
||||
primaryManifest, err := primary.IndexManifest()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
secondaryManifest, err := secondary.IndexManifest()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// figure out what already is in the index, and what should be overwritten
|
||||
// what should be checked in the existing index:
|
||||
// 1. platform - if it is in remote index but not in local, add to local
|
||||
// 2. attestation - after all platforms, does it point to something in the updated index?
|
||||
// If not, remove
|
||||
|
||||
// make a map of all the digests already in the index, so we can know what is there
|
||||
var (
|
||||
manifestMap = map[v1.Hash]bool{}
|
||||
platformMap = map[string]bool{}
|
||||
)
|
||||
for _, m := range primaryManifest.Manifests {
|
||||
if m.Platform == nil || m.Platform.Architecture == "" {
|
||||
continue
|
||||
}
|
||||
platformKey := fmt.Sprintf("%s/%s/%s", m.Platform.Architecture, m.Platform.OS, m.Platform.Variant)
|
||||
manifestMap[m.Digest] = true
|
||||
platformMap[platformKey] = true
|
||||
}
|
||||
|
||||
for _, m := range secondaryManifest.Manifests {
|
||||
// ignore any of those without a platform for this run (we will deal witb attestations in a second pass)
|
||||
if m.Platform == nil || m.Platform.Architecture == "" || (m.Platform.Architecture == "unknown" && m.Platform.OS == "unknown") {
|
||||
continue
|
||||
}
|
||||
platformKey := fmt.Sprintf("%s/%s/%s", m.Platform.Architecture, m.Platform.OS, m.Platform.Variant)
|
||||
// primary wins if we already have this platform covered
|
||||
if _, ok := platformMap[platformKey]; ok {
|
||||
continue
|
||||
}
|
||||
if _, ok := manifestMap[m.Digest]; ok {
|
||||
// we already have this one, so we can skip it
|
||||
continue
|
||||
}
|
||||
primaryManifest.Manifests = append(primaryManifest.Manifests, m)
|
||||
manifestMap[m.Digest] = true
|
||||
}
|
||||
|
||||
// now we have assured that all of the images in the remote index are in the local index
|
||||
// or overridden by matching local ones
|
||||
// next we have to make sure that any sboms already on the remote index are still valid
|
||||
// we either add them to the local index, or remove them if they are no longer valid
|
||||
// we assume the ones in the local index are valid because they would have been generated now
|
||||
for _, m := range secondaryManifest.Manifests {
|
||||
if m.Platform == nil || m.Platform.Architecture != "unknown" || m.Platform.OS == "unknown" || m.Annotations == nil || m.Annotations[AnnotationDockerReferenceDigest] == "" {
|
||||
continue
|
||||
}
|
||||
// if we already have this one, we are good
|
||||
if _, ok := manifestMap[m.Digest]; ok {
|
||||
continue
|
||||
}
|
||||
// the hash to which this attestation points
|
||||
hash := m.Annotations[AnnotationDockerReferenceDigest]
|
||||
dig, err := v1.NewHash(hash)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not parse hash %s: %v", hash, err)
|
||||
}
|
||||
// if this points at something not in the local index, do not bother adding it
|
||||
if _, ok := manifestMap[dig]; !ok {
|
||||
continue
|
||||
}
|
||||
primaryManifest.Manifests = append(primaryManifest.Manifests, m)
|
||||
manifestMap[m.Digest] = true
|
||||
}
|
||||
return primary, nil
|
||||
}
|
Loading…
Reference in New Issue
Block a user