mirror of
https://github.com/linuxkit/linuxkit.git
synced 2025-07-19 17:26:28 +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"
|
namepkg "github.com/google/go-containerregistry/pkg/name"
|
||||||
"github.com/google/go-containerregistry/pkg/v1/remote"
|
"github.com/google/go-containerregistry/pkg/v1/remote"
|
||||||
"github.com/google/go-containerregistry/pkg/v1/validate"
|
"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"
|
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
@ -57,10 +57,26 @@ func (p *Provider) Push(name string, withManifest bool) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not get digest for index %s: %v", name, err)
|
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...)
|
desc, err := remote.Get(ref, remoteOptions...)
|
||||||
if err == nil && desc != nil && dig == desc.Digest {
|
if err == nil && desc != nil {
|
||||||
fmt.Printf("%s index already available on remote registry, skipping push", name)
|
if dig == desc.Digest {
|
||||||
return nil
|
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)
|
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
|
// 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
|
return err
|
||||||
}
|
}
|
||||||
fmt.Printf("Pushed index %s\n", name)
|
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)
|
log.Debugf("pushing individual images in the index %s", name)
|
||||||
for _, m := range manifest.Manifests {
|
for _, m := range manifest.Manifests {
|
||||||
if m.Platform == nil || m.Platform.Architecture == "" {
|
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)
|
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
|
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"
|
"github.com/google/go-containerregistry/pkg/v1/tarball"
|
||||||
intoto "github.com/in-toto/in-toto-golang/in_toto"
|
intoto "github.com/in-toto/in-toto-golang/in_toto"
|
||||||
lktspec "github.com/linuxkit/linuxkit/src/cmd/linuxkit/spec"
|
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"
|
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
annotationDockerReferenceType = "vnd.docker.reference.type"
|
inTotoJsonMediaType = "application/vnd.in-toto+json"
|
||||||
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"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ImageSource a source for an image in the OCI distribution cache.
|
// 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]
|
desc := descs[0]
|
||||||
|
|
||||||
annotations := map[string]string{
|
annotations := map[string]string{
|
||||||
annotationDockerReferenceType: annotationAttestationManifest,
|
util.AnnotationDockerReferenceType: util.AnnotationAttestationManifest,
|
||||||
annotationDockerReferenceDigest: desc.Digest.String(),
|
util.AnnotationDockerReferenceDigest: desc.Digest.String(),
|
||||||
}
|
}
|
||||||
descs, err = partial.FindManifests(index, matchAllAnnotations(annotations))
|
descs, err = partial.FindManifests(index, matchAllAnnotations(annotations))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -183,7 +179,7 @@ func (c ImageSource) SBoMs() ([]io.ReadCloser, error) {
|
|||||||
var readers []io.ReadCloser
|
var readers []io.ReadCloser
|
||||||
for i, layer := range manifest.Layers {
|
for i, layer := range manifest.Layers {
|
||||||
annotations := layer.Annotations
|
annotations := layer.Annotations
|
||||||
if annotations[annotationInTotoPredicateType] != annotationSPDXDoc || layer.MediaType != inTotoJsonMediaType {
|
if annotations[util.AnnotationInTotoPredicateType] != util.AnnotationSPDXDoc || layer.MediaType != inTotoJsonMediaType {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// get the actual blob of the layer
|
// 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 {
|
if err := json.Unmarshal(buf.Bytes(), &stmt); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if stmt.PredicateType != annotationSPDXDoc {
|
if stmt.PredicateType != util.AnnotationSPDXDoc {
|
||||||
return nil, fmt.Errorf("unexpected predicate type %s", stmt.PredicateType)
|
return nil, fmt.Errorf("unexpected predicate type %s", stmt.PredicateType)
|
||||||
}
|
}
|
||||||
sbom := stmt.Predicate
|
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/remote"
|
||||||
"github.com/google/go-containerregistry/pkg/v1/types"
|
"github.com/google/go-containerregistry/pkg/v1/types"
|
||||||
lktspec "github.com/linuxkit/linuxkit/src/cmd/linuxkit/spec"
|
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"
|
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
@ -320,7 +321,7 @@ func (p *Provider) IndexWrite(ref *reference.Spec, descriptors ...v1.Descriptor)
|
|||||||
appliedManifests[m.Digest] = true
|
appliedManifests[m.Digest] = true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
value, ok := m.Annotations[annotationDockerReferenceDigest]
|
value, ok := m.Annotations[lktutil.AnnotationDockerReferenceDigest]
|
||||||
if !ok {
|
if !ok {
|
||||||
manifest.Manifests = append(manifest.Manifests, m)
|
manifest.Manifests = append(manifest.Manifests, m)
|
||||||
appliedManifests[m.Digest] = true
|
appliedManifests[m.Digest] = true
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/google/go-containerregistry/pkg/v1/empty"
|
"github.com/google/go-containerregistry/pkg/v1/empty"
|
||||||
"github.com/google/go-containerregistry/pkg/v1/mutate"
|
"github.com/google/go-containerregistry/pkg/v1/mutate"
|
||||||
"github.com/google/go-containerregistry/pkg/v1/remote"
|
"github.com/google/go-containerregistry/pkg/v1/remote"
|
||||||
|
"github.com/linuxkit/linuxkit/src/cmd/linuxkit/util"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -30,6 +31,7 @@ func PushManifest(img string, options ...remote.Option) (hash string, length int
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return hash, length, fmt.Errorf("parsing %s: %w", img, err)
|
return hash, length, fmt.Errorf("parsing %s: %w", img, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
adds := make([]mutate.IndexAddendum, 0, len(platformsToSearchForIndex))
|
adds := make([]mutate.IndexAddendum, 0, len(platformsToSearchForIndex))
|
||||||
for i, platform := range platformsToSearchForIndex {
|
for i, platform := range platformsToSearchForIndex {
|
||||||
osArchArr := strings.Split(platform, "/")
|
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
|
// add the desc to the index we will push
|
||||||
index := mutate.AppendManifests(empty.Index, adds...)
|
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()
|
size, err := index.Size()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return hash, length, fmt.Errorf("getting index size: %w", err)
|
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