0
Fork 0
mirror of https://github.com/project-zot/zot.git synced 2024-12-16 21:56:37 -05:00
zot/pkg/extensions/lint/lint.go
Alexei Dodon 247f6dcd3f
feat: propagate detailed error msgs to client (OCI dist-spec format) (#1681)
Signed-off-by: Alexei Dodon <adodon@cisco.com>
2023-08-23 20:59:52 +03:00

130 lines
3.4 KiB
Go

//go:build lint
// +build lint
package lint
import (
"encoding/json"
"fmt"
godigest "github.com/opencontainers/go-digest"
ispec "github.com/opencontainers/image-spec/specs-go/v1"
zerr "zotregistry.io/zot/errors"
"zotregistry.io/zot/pkg/extensions/config"
"zotregistry.io/zot/pkg/log"
storageTypes "zotregistry.io/zot/pkg/storage/types"
)
type Linter struct {
config *config.LintConfig
log log.Logger
}
func NewLinter(config *config.LintConfig, log log.Logger) *Linter {
return &Linter{
config: config,
log: log,
}
}
func (linter *Linter) CheckMandatoryAnnotations(repo string, manifestDigest godigest.Digest,
imgStore storageTypes.ImageStore,
) (bool, error) {
if linter.config == nil {
return true, nil
}
if (linter.config != nil && !*linter.config.Enable) || len(linter.config.MandatoryAnnotations) == 0 {
return true, nil
}
mandatoryAnnotationsList := linter.config.MandatoryAnnotations
content, err := imgStore.GetBlobContent(repo, manifestDigest)
if err != nil {
linter.log.Error().Err(err).Msg("linter: unable to get image manifest")
return false, err
}
var manifest ispec.Manifest
if err := json.Unmarshal(content, &manifest); err != nil {
linter.log.Error().Err(err).Msg("linter: couldn't unmarshal manifest JSON")
return false, err
}
mandatoryAnnotationsMap := make(map[string]bool)
for _, annotation := range mandatoryAnnotationsList {
mandatoryAnnotationsMap[annotation] = false
}
manifestAnnotations := manifest.Annotations
for annotation := range manifestAnnotations {
if _, ok := mandatoryAnnotationsMap[annotation]; ok {
mandatoryAnnotationsMap[annotation] = true
}
}
missingAnnotations := getMissingAnnotations(mandatoryAnnotationsMap)
if len(missingAnnotations) == 0 {
return true, nil
}
// if there are mandatory annotations missing in the manifest, get config and check these annotations too
configDigest := manifest.Config.Digest
content, err = imgStore.GetBlobContent(repo, configDigest)
if err != nil {
linter.log.Error().Err(err).Msg("linter: couldn't get config JSON " +
configDigest.String())
return false, err
}
var imageConfig ispec.Image
if err := json.Unmarshal(content, &imageConfig); err != nil {
linter.log.Error().Err(err).Msg("linter: couldn't unmarshal config JSON " + configDigest.String())
return false, err
}
configAnnotations := imageConfig.Config.Labels
for annotation := range configAnnotations {
if _, ok := mandatoryAnnotationsMap[annotation]; ok {
mandatoryAnnotationsMap[annotation] = true
}
}
missingAnnotations = getMissingAnnotations(mandatoryAnnotationsMap)
if len(missingAnnotations) > 0 {
msg := fmt.Sprintf("\nlinter: manifest %s\nor config %s\nis missing the next annotations: %s",
string(manifestDigest), string(configDigest), missingAnnotations)
linter.log.Error().Msg(msg)
return false, zerr.NewError(zerr.ErrImageLintAnnotations).AddDetail("missingAnnotations", msg)
}
return true, nil
}
func (linter *Linter) Lint(repo string, manifestDigest godigest.Digest,
imageStore storageTypes.ImageStore,
) (bool, error) {
return linter.CheckMandatoryAnnotations(repo, manifestDigest, imageStore)
}
func getMissingAnnotations(mandatoryAnnotationsMap map[string]bool) []string {
var missingAnnotations []string
for annotation, flag := range mandatoryAnnotationsMap {
if !flag {
missingAnnotations = append(missingAnnotations, annotation)
}
}
return missingAnnotations
}