-
Notifications
You must be signed in to change notification settings - Fork 42
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
index: local: annotate and add functionality
- Loading branch information
1 parent
fb7a262
commit 4ed6787
Showing
3 changed files
with
317 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,249 @@ | ||
package local | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"io/ioutil" | ||
"os" | ||
"path/filepath" | ||
"strings" | ||
|
||
"github.com/pkg/errors" | ||
|
||
"github.com/google/go-containerregistry/pkg/authn" | ||
"github.com/google/go-containerregistry/pkg/name" | ||
v1 "github.com/google/go-containerregistry/pkg/v1" | ||
"github.com/google/go-containerregistry/pkg/v1/layout" | ||
"github.com/google/go-containerregistry/pkg/v1/match" | ||
"github.com/google/go-containerregistry/pkg/v1/mutate" | ||
"github.com/google/go-containerregistry/pkg/v1/remote" | ||
) | ||
|
||
type ImageIndex struct { | ||
docker DockerClient | ||
repoName string | ||
index v1.ImageIndex | ||
} | ||
|
||
var manifestDir string = "out/manifests" | ||
|
||
func (i *ImageIndex) Add(repoName string) error { | ||
ref, err := name.ParseReference(repoName) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
desc, err := remote.Get(ref, remote.WithAuthFromKeychain(authn.DefaultKeychain)) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
img, err := desc.Image() | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
cfg, err := img.ConfigFile() | ||
|
||
if err != nil { | ||
return errors.Wrapf(err, "getting config file for image %q", repoName) | ||
} | ||
if cfg == nil { | ||
return fmt.Errorf("missing config for image %q", repoName) | ||
} | ||
if cfg.OS == "" { | ||
return fmt.Errorf("missing OS for image %q", repoName) | ||
} | ||
|
||
// Not checking the architecture so we can allow to do `manifest annotate` | ||
// if cfg.Architecture == "" { | ||
// return fmt.Errorf("missing Architecture for image %q", repoName) | ||
// } | ||
|
||
platform := v1.Platform{} | ||
platform.Architecture = cfg.Architecture | ||
platform.OS = cfg.OS | ||
|
||
desc.Descriptor.Platform = &platform | ||
|
||
i.index = mutate.AppendManifests(i.index, mutate.IndexAddendum{Add: img, Descriptor: desc.Descriptor}) | ||
|
||
return nil | ||
} | ||
|
||
func (i *ImageIndex) Remove(repoName string) error { | ||
ref, err := name.ParseReference(repoName) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
desc, err := remote.Get(ref, remote.WithAuthFromKeychain(authn.DefaultKeychain)) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
i.index = mutate.RemoveManifests(i.index, match.Digests(desc.Digest)) | ||
|
||
return nil | ||
} | ||
|
||
func (i *ImageIndex) Save(additionalNames ...string) error { | ||
l := layout.Path(manifestDir) | ||
|
||
rawIndex, err := i.index.RawManifest() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = l.WriteFile(makeFileSafeName(i.repoName), rawIndex, os.ModePerm) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func makeFileSafeName(ref string) string { | ||
fileName := strings.Replace(ref, ":", "-", -1) | ||
return strings.Replace(fileName, "/", "_", -1) | ||
} | ||
|
||
func createManifestListDirectory(transaction string) error { | ||
path := filepath.Join("/home/drac98/.docker/manifests", makeFileSafeName(transaction)) | ||
return os.MkdirAll(path, 0o755) | ||
} | ||
|
||
func (i *ImageIndex) ManifestSize() (int64, error) { | ||
return 0, nil | ||
} | ||
|
||
func (i *ImageIndex) Name() string { | ||
return i.repoName | ||
} | ||
|
||
func ManifestDir() string { | ||
return manifestDir | ||
} | ||
|
||
func AppendManifest(repoName string, manifestName string) error { | ||
var manifest v1.IndexManifest | ||
|
||
path := filepath.Join(manifestDir, makeFileSafeName(repoName)) | ||
jsonFile, err := ioutil.ReadFile(path) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = json.Unmarshal([]byte(jsonFile), &manifest) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
ref, err := name.ParseReference(manifestName) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
desc, err := remote.Get(ref, remote.WithAuthFromKeychain(authn.DefaultKeychain)) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
img, err := desc.Image() | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
cfg, err := img.ConfigFile() | ||
|
||
if err != nil { | ||
return errors.Wrapf(err, "getting config file for image %q", repoName) | ||
} | ||
if cfg == nil { | ||
return fmt.Errorf("missing config for image %q", repoName) | ||
} | ||
if cfg.OS == "" { | ||
return fmt.Errorf("missing OS for image %q", repoName) | ||
} | ||
|
||
platform := v1.Platform{} | ||
platform.Architecture = cfg.Architecture | ||
platform.OS = cfg.OS | ||
|
||
desc.Descriptor.Platform = &platform | ||
|
||
manifest.Manifests = append(manifest.Manifests, desc.Descriptor) | ||
|
||
data, err := json.Marshal(manifest) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = os.WriteFile(path, data, os.ModePerm) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
type AnnotateFields struct { | ||
Architecture string | ||
OS string | ||
Variant string | ||
} | ||
|
||
func AnnotateManifest(repoName string, manifestName string, opts AnnotateFields) error { | ||
var manifest v1.IndexManifest | ||
|
||
path := filepath.Join(manifestDir, makeFileSafeName(repoName)) | ||
jsonFile, err := os.ReadFile(path) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = json.Unmarshal([]byte(jsonFile), &manifest) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
ref, err := name.ParseReference(manifestName) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
desc, err := remote.Get(ref, remote.WithAuthFromKeychain(authn.DefaultKeychain)) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
for i, desc_i := range manifest.Manifests { | ||
if desc_i.Digest.String() == desc.Digest.String() { | ||
if opts.Architecture != "" { | ||
manifest.Manifests[i].Platform.Architecture = opts.Architecture | ||
} | ||
|
||
if opts.OS != "" { | ||
manifest.Manifests[i].Platform.OS = opts.OS | ||
} | ||
|
||
if opts.Variant != "" { | ||
manifest.Manifests[i].Platform.Variant = opts.Variant | ||
} | ||
|
||
data, err := json.Marshal(manifest) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = os.WriteFile(path, data, os.ModePerm) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
} | ||
|
||
return errors.Errorf("Manifest %s not found", manifestName) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package local | ||
|
||
import "github.com/buildpacks/imgutil" | ||
|
||
type ImageIndexOption func(*indexOptions) error | ||
|
||
type indexOptions struct { | ||
mediaTypes imgutil.MediaTypes | ||
} | ||
|
||
// WithIndexMediaTypes loads an existing index as a source. | ||
// If mediatype is not found ignore. | ||
func WithIndexMediaTypes(requested imgutil.MediaTypes) ImageIndexOption { | ||
return func(opts *indexOptions) error { | ||
opts.mediaTypes = requested | ||
return nil | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package local | ||
|
||
import ( | ||
"github.com/buildpacks/imgutil" | ||
"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/mutate" | ||
"github.com/google/go-containerregistry/pkg/v1/types" | ||
) | ||
|
||
func NewIndex(repoName string, dockerClient DockerClient, ops ...ImageIndexOption) (*ImageIndex, error) { | ||
if _, err := name.ParseReference(repoName, name.WeakValidation); err != nil { | ||
return nil, err | ||
} | ||
|
||
indexOpts := &indexOptions{} | ||
for _, op := range ops { | ||
if err := op(indexOpts); err != nil { | ||
return nil, err | ||
} | ||
} | ||
|
||
mediaType := defaultMediaType() | ||
if indexOpts.mediaTypes.IndexManifestType() != "" { | ||
mediaType = indexOpts.mediaTypes | ||
} | ||
|
||
index, err := emptyIndex(mediaType.IndexManifestType()) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
ridx := &ImageIndex{ | ||
docker: dockerClient, | ||
repoName: repoName, | ||
index: index, | ||
} | ||
|
||
return ridx, nil | ||
|
||
} | ||
|
||
func emptyIndex(mediaType types.MediaType) (v1.ImageIndex, error) { | ||
return mutate.IndexMediaType(empty.Index, mediaType), nil | ||
} | ||
|
||
func defaultMediaType() imgutil.MediaTypes { | ||
return imgutil.DockerTypes | ||
} |