Files
attest/pkg/mirror/mirror.go

159 lines
4.6 KiB
Go
Raw Normal View History

2024-04-15 15:20:56 -05:00
package mirror
import (
"fmt"
"os"
"github.com/docker/attest/internal/embed"
"github.com/docker/attest/pkg/attestation"
2024-06-18 11:55:30 -05:00
"github.com/docker/attest/pkg/oci"
2024-04-15 15:20:56 -05:00
"github.com/docker/attest/pkg/tuf"
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/empty"
"github.com/google/go-containerregistry/pkg/v1/layout"
"github.com/google/go-containerregistry/pkg/v1/mutate"
2024-04-15 15:20:56 -05:00
"github.com/google/go-containerregistry/pkg/v1/remote"
)
func NewTUFMirror(root []byte, tufPath, metadataURL, targetsURL string, versionChecker tuf.VersionChecker) (*TUFMirror, error) {
2024-04-15 15:20:56 -05:00
if root == nil {
2024-07-10 17:21:56 -05:00
root = embed.RootDefault.Data
2024-04-15 15:20:56 -05:00
}
tufClient, err := tuf.NewClient(root, tufPath, metadataURL, targetsURL, versionChecker)
2024-04-15 15:20:56 -05:00
if err != nil {
return nil, fmt.Errorf("failed to create TUF client: %w", err)
}
return &TUFMirror{TUFClient: tufClient, tufPath: tufPath, metadataURL: metadataURL, targetsURL: targetsURL}, nil
2024-04-15 15:20:56 -05:00
}
func PushImageToRegistry(image v1.Image, imageName string) error {
2024-04-15 15:20:56 -05:00
ref, err := name.ParseReference(imageName)
if err != nil {
return fmt.Errorf("Failed to parse image name '%s': %w", imageName, err)
2024-04-15 15:20:56 -05:00
}
2024-06-18 11:55:30 -05:00
2024-04-15 15:20:56 -05:00
// Push the image to the registry
2024-06-18 11:55:30 -05:00
return remote.Write(ref, image, oci.MultiKeychainOption())
}
func PushIndexToRegistry(index v1.ImageIndex, imageName string) error {
// Parse the index name
ref, err := name.ParseReference(imageName)
if err != nil {
return fmt.Errorf("Failed to parse image name: %w", err)
2024-04-15 15:20:56 -05:00
}
// Push the index to the registry
return remote.WriteIndex(ref, index, oci.MultiKeychainOption())
2024-04-15 15:20:56 -05:00
}
func SaveImageAsOCILayout(image v1.Image, path string) error {
2024-04-15 15:20:56 -05:00
// Save the image to the local filesystem
2024-06-18 09:39:12 -05:00
err := os.MkdirAll(path, os.ModePerm)
2024-04-15 15:20:56 -05:00
if err != nil {
return fmt.Errorf("failed to create directory: %w", err)
}
index := empty.Index
l, err := layout.Write(path, index)
if err != nil {
return fmt.Errorf("failed to create index: %w", err)
}
return l.AppendImage(image)
}
func SaveIndexAsOCILayout(image v1.ImageIndex, path string) error {
// Save the index to the local filesystem
2024-06-18 09:39:12 -05:00
err := os.MkdirAll(path, os.ModePerm)
if err != nil {
return fmt.Errorf("failed to create directory: %w", err)
}
_, err = layout.Write(path, image)
if err != nil {
return fmt.Errorf("failed to create index: %w", err)
2024-04-15 15:20:56 -05:00
}
return nil
}
func SaveIndex(outputs []*oci.ImageSpec, index v1.ImageIndex, indexName string) error {
// split output by comma and write or push each one
for _, output := range outputs {
if output.Type == oci.OCI {
idx := v1.ImageIndex(empty.Index)
idx = mutate.AppendManifests(idx, mutate.IndexAddendum{
Add: index,
Descriptor: v1.Descriptor{
Annotations: map[string]string{
oci.OCIReferenceTarget: indexName,
},
},
})
err := SaveIndexAsOCILayout(idx, output.Identifier)
if err != nil {
return fmt.Errorf("failed to write signed image: %w", err)
}
} else {
err := PushIndexToRegistry(index, output.Identifier)
if err != nil {
return fmt.Errorf("failed to push signed image: %w", err)
}
}
}
return nil
}
func SaveImage(output *oci.ImageSpec, image v1.Image, imageName string) error {
if output.Type == oci.OCI {
idx := v1.ImageIndex(empty.Index)
idx = mutate.AppendManifests(idx, mutate.IndexAddendum{
Add: image,
Descriptor: v1.Descriptor{
Annotations: map[string]string{
oci.OCIReferenceTarget: imageName,
},
},
})
err := SaveIndexAsOCILayout(idx, output.Identifier)
if err != nil {
return fmt.Errorf("failed to write signed image: %w", err)
}
} else {
err := PushImageToRegistry(image, output.Identifier)
if err != nil {
return fmt.Errorf("failed to push signed image: %w", err)
}
}
return nil
}
func SaveReferrers(manifest *attestation.Manifest, outputs []*oci.ImageSpec) error {
for _, output := range outputs {
2024-08-05 13:24:58 -05:00
// OCI layout output for referrers not supported
if output.Type == oci.OCI {
continue
}
// so that we use the same tag each time to reduce number of tags (tags aren't needed for referrers but we must push one)
attOut, err := oci.ReplaceTagInSpec(output, manifest.SubjectDescriptor.Digest)
if err != nil {
return err
}
// otherwise we end up with the detected platform, though I'm not sure it matters
attOut.Platform = &v1.Platform{
OS: "unknown",
Architecture: "unknown",
}
images, err := manifest.BuildReferringArtifacts()
if err != nil {
return fmt.Errorf("failed to build image: %w", err)
}
for _, image := range images {
2024-08-05 13:24:58 -05:00
err := PushImageToRegistry(image, attOut.Identifier)
if err != nil {
return fmt.Errorf("failed to push image: %w", err)
}
}
}
return nil
}