Skip to content

Commit

Permalink
index: local: annotate and add functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
husni-faiz committed May 2, 2023
1 parent fb7a262 commit 4ed6787
Show file tree
Hide file tree
Showing 3 changed files with 317 additions and 0 deletions.
249 changes: 249 additions & 0 deletions local/index.go
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)
}
18 changes: 18 additions & 0 deletions local/index_options.go
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
}
}
50 changes: 50 additions & 0 deletions local/new_index.go
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
}

0 comments on commit 4ed6787

Please sign in to comment.