0
Fork 0
mirror of https://github.com/project-zot/zot.git synced 2024-12-23 22:27:35 -05:00
zot/pkg/extensions/search/common/oci_layout.go
Shivam Mishra 37d150e32f search: graphql api to give detailed repo info
DetailedRepoInfo graphql api returns detailed repo info given repo name
repo contains its manifests info
Each manifest entry contains digest,signed, tag and layers info
Each layer info containes digest, size

Signed-off-by: Shivam Mishra <shimish2@cisco.com>
2022-02-10 16:34:13 -08:00

278 lines
6.5 KiB
Go

// Package common ...
package common
import (
"encoding/json"
goerrors "errors"
"path"
"strconv"
"strings"
"time"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/types"
godigest "github.com/opencontainers/go-digest"
ispec "github.com/opencontainers/image-spec/specs-go/v1"
"zotregistry.io/zot/errors"
"zotregistry.io/zot/pkg/log"
"zotregistry.io/zot/pkg/storage"
)
const cosignedAnnotation = "dev.cosign.signature.baseimage"
// OciLayoutInfo ...
type OciLayoutUtils struct {
Log log.Logger
StoreController storage.StoreController
}
type RepoInfo struct {
Manifests []Manifest `json:"manifests"`
}
type Manifest struct {
Tag string `json:"tag"`
Digest string `json:"digest"`
IsSigned bool `json:"isSigned"`
Layers []Layer `json:"layers"`
}
type Layer struct {
Size string `json:"size"`
Digest string `json:"digest"`
}
// NewOciLayoutUtils initializes a new OciLayoutUtils object.
func NewOciLayoutUtils(storeController storage.StoreController, log log.Logger) *OciLayoutUtils {
return &OciLayoutUtils{Log: log, StoreController: storeController}
}
// Below method will return image path including root dir, root dir is determined by splitting.
func (olu OciLayoutUtils) GetImageManifests(image string) ([]ispec.Descriptor, error) {
imageStore := olu.StoreController.GetImageStore(image)
buf, err := imageStore.GetIndexContent(image)
if err != nil {
if goerrors.Is(errors.ErrRepoNotFound, err) {
olu.Log.Error().Err(err).Msg("index.json doesn't exist")
return nil, errors.ErrRepoNotFound
}
olu.Log.Error().Err(err).Msg("unable to open index.json")
return nil, errors.ErrRepoNotFound
}
var index ispec.Index
if err := json.Unmarshal(buf, &index); err != nil {
olu.Log.Error().Err(err).Str("dir", path.Join(imageStore.RootDir(), image)).Msg("invalid JSON")
return nil, errors.ErrRepoNotFound
}
return index.Manifests, nil
}
//nolint: interfacer
func (olu OciLayoutUtils) GetImageBlobManifest(imageDir string, digest godigest.Digest) (v1.Manifest, error) {
var blobIndex v1.Manifest
imageStore := olu.StoreController.GetImageStore(imageDir)
blobBuf, err := imageStore.GetBlobContent(imageDir, digest.String())
if err != nil {
olu.Log.Error().Err(err).Msg("unable to open image metadata file")
return blobIndex, err
}
if err := json.Unmarshal(blobBuf, &blobIndex); err != nil {
olu.Log.Error().Err(err).Msg("unable to marshal blob index")
return blobIndex, err
}
return blobIndex, nil
}
//nolint: interfacer
func (olu OciLayoutUtils) GetImageInfo(imageDir string, hash v1.Hash) (ispec.Image, error) {
var imageInfo ispec.Image
imageStore := olu.StoreController.GetImageStore(imageDir)
blobBuf, err := imageStore.GetBlobContent(imageDir, hash.String())
if err != nil {
olu.Log.Error().Err(err).Msg("unable to open image layers file")
return imageInfo, err
}
if err := json.Unmarshal(blobBuf, &imageInfo); err != nil {
olu.Log.Error().Err(err).Msg("unable to marshal blob index")
return imageInfo, err
}
return imageInfo, err
}
func (olu OciLayoutUtils) IsValidImageFormat(image string) (bool, error) {
imageDir, inputTag := GetImageDirAndTag(image)
manifests, err := olu.GetImageManifests(imageDir)
if err != nil {
return false, err
}
for _, manifest := range manifests {
tag, ok := manifest.Annotations[ispec.AnnotationRefName]
if ok && inputTag != "" && tag != inputTag {
continue
}
blobManifest, err := olu.GetImageBlobManifest(imageDir, manifest.Digest)
if err != nil {
return false, err
}
imageLayers := blobManifest.Layers
for _, imageLayer := range imageLayers {
switch imageLayer.MediaType {
case types.OCILayer, types.DockerLayer:
return true, nil
default:
olu.Log.Debug().Msg("image media type not supported for scanning")
return false, errors.ErrScanNotSupported
}
}
}
return false, nil
}
// GetImageTagsWithTimestamp returns a list of image tags with timestamp available in the specified repository.
func (olu OciLayoutUtils) GetImageTagsWithTimestamp(repo string) ([]TagInfo, error) {
tagsInfo := make([]TagInfo, 0)
manifests, err := olu.GetImageManifests(repo)
if err != nil {
olu.Log.Error().Err(err).Msg("unable to read image manifests")
return tagsInfo, err
}
for _, manifest := range manifests {
digest := manifest.Digest
val, ok := manifest.Annotations[ispec.AnnotationRefName]
if ok {
imageBlobManifest, err := olu.GetImageBlobManifest(repo, digest)
if err != nil {
olu.Log.Error().Err(err).Msg("unable to read image blob manifest")
return tagsInfo, err
}
imageInfo, err := olu.GetImageInfo(repo, imageBlobManifest.Config.Digest)
if err != nil {
olu.Log.Error().Err(err).Msg("unable to read image info")
return tagsInfo, err
}
var timeStamp time.Time
if len(imageInfo.History) != 0 {
timeStamp = *imageInfo.History[0].Created
} else {
timeStamp = time.Time{}
}
tagsInfo = append(tagsInfo, TagInfo{Name: val, Timestamp: timeStamp, Digest: digest.String()})
}
}
return tagsInfo, nil
}
func (olu OciLayoutUtils) GetExpandedRepoInfo(name string) (RepoInfo, error) {
repo := RepoInfo{}
manifests := make([]Manifest, 0)
manifestList, err := olu.GetImageManifests(name)
if err != nil {
olu.Log.Error().Err(err).Msg("error getting image manifests")
return RepoInfo{}, err
}
for _, manifest := range manifestList {
manifestInfo := Manifest{}
manifestInfo.Digest = manifest.Digest.Encoded()
manifestInfo.IsSigned = false
tag, ok := manifest.Annotations[ispec.AnnotationRefName]
if !ok {
tag = "latest"
}
manifestInfo.Tag = tag
manifest, err := olu.GetImageBlobManifest(name, manifest.Digest)
if err != nil {
olu.Log.Error().Err(err).Msg("error getting image manifest blob")
return RepoInfo{}, err
}
layers := make([]Layer, 0)
for _, layer := range manifest.Layers {
layerInfo := Layer{}
layerInfo.Digest = layer.Digest.Hex
layerInfo.Size = strconv.FormatInt(layer.Size, 10)
layers = append(layers, layerInfo)
if _, ok := layer.Annotations[cosignedAnnotation]; ok {
manifestInfo.IsSigned = true
}
}
manifestInfo.Layers = layers
manifests = append(manifests, manifestInfo)
}
repo.Manifests = manifests
return repo, nil
}
func GetImageDirAndTag(imageName string) (string, string) {
var imageDir string
var imageTag string
if strings.Contains(imageName, ":") {
splitImageName := strings.Split(imageName, ":")
imageDir = splitImageName[0]
imageTag = splitImageName[1]
} else {
imageDir = imageName
}
return imageDir, imageTag
}