2023-01-09 15:37:44 -05:00
|
|
|
package common
|
|
|
|
|
|
|
|
import (
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
godigest "github.com/opencontainers/go-digest"
|
|
|
|
ispec "github.com/opencontainers/image-spec/specs-go/v1"
|
|
|
|
|
|
|
|
zerr "zotregistry.io/zot/errors"
|
|
|
|
"zotregistry.io/zot/pkg/meta/repodb"
|
|
|
|
)
|
|
|
|
|
|
|
|
func UpdateManifestMeta(repoMeta repodb.RepoMetadata, manifestDigest godigest.Digest,
|
|
|
|
manifestMeta repodb.ManifestMetadata,
|
|
|
|
) repodb.RepoMetadata {
|
|
|
|
updatedRepoMeta := repoMeta
|
|
|
|
|
|
|
|
updatedStatistics := repoMeta.Statistics[manifestDigest.String()]
|
|
|
|
updatedStatistics.DownloadCount = manifestMeta.DownloadCount
|
|
|
|
updatedRepoMeta.Statistics[manifestDigest.String()] = updatedStatistics
|
|
|
|
|
|
|
|
if manifestMeta.Signatures == nil {
|
|
|
|
manifestMeta.Signatures = repodb.ManifestSignatures{}
|
|
|
|
}
|
|
|
|
|
|
|
|
updatedRepoMeta.Signatures[manifestDigest.String()] = manifestMeta.Signatures
|
|
|
|
|
|
|
|
return updatedRepoMeta
|
|
|
|
}
|
|
|
|
|
|
|
|
func SignatureAlreadyExists(signatureSlice []repodb.SignatureInfo, sm repodb.SignatureMetadata) bool {
|
|
|
|
for _, sigInfo := range signatureSlice {
|
|
|
|
if sm.SignatureDigest == sigInfo.SignatureManifestDigest {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func ReferenceIsDigest(reference string) bool {
|
|
|
|
_, err := godigest.Parse(reference)
|
|
|
|
|
|
|
|
return err == nil
|
|
|
|
}
|
|
|
|
|
2023-03-09 13:41:48 -05:00
|
|
|
func ValidateRepoReferenceInput(repo, reference string, manifestDigest godigest.Digest) error {
|
2023-01-09 15:37:44 -05:00
|
|
|
if repo == "" {
|
|
|
|
return zerr.ErrEmptyRepoName
|
|
|
|
}
|
|
|
|
|
2023-03-09 13:41:48 -05:00
|
|
|
if reference == "" {
|
2023-01-09 15:37:44 -05:00
|
|
|
return zerr.ErrEmptyTag
|
|
|
|
}
|
|
|
|
|
|
|
|
if manifestDigest == "" {
|
|
|
|
return zerr.ErrEmptyDigest
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func ScoreRepoName(searchText string, repoName string) int {
|
|
|
|
searchTextSlice := strings.Split(searchText, "/")
|
|
|
|
repoNameSlice := strings.Split(repoName, "/")
|
|
|
|
|
|
|
|
if len(searchTextSlice) > len(repoNameSlice) {
|
|
|
|
return -1
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(searchTextSlice) == 1 {
|
|
|
|
// check if it maches first or last name in path
|
|
|
|
if index := strings.Index(repoNameSlice[len(repoNameSlice)-1], searchTextSlice[0]); index != -1 {
|
|
|
|
return index + 1
|
|
|
|
}
|
|
|
|
|
|
|
|
// we'll make repos that match the first name in path less important than matching the last name in path
|
|
|
|
if index := strings.Index(repoNameSlice[0], searchTextSlice[0]); index != -1 {
|
|
|
|
return (index + 1) * 10
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(searchTextSlice) < len(repoNameSlice) &&
|
|
|
|
strings.HasPrefix(repoName, searchText) {
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
|
|
|
// searchText and repoName match perfectly up until the last name in path
|
|
|
|
for i := 0; i < len(searchTextSlice)-1; i++ {
|
|
|
|
if searchTextSlice[i] != repoNameSlice[i] {
|
|
|
|
return -1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// check the last
|
|
|
|
if index := strings.Index(repoNameSlice[len(repoNameSlice)-1], searchTextSlice[len(searchTextSlice)-1]); index != -1 {
|
|
|
|
return (index + 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1
|
|
|
|
}
|
|
|
|
|
|
|
|
func GetImageLastUpdatedTimestamp(configContent ispec.Image) time.Time {
|
|
|
|
var timeStamp *time.Time
|
|
|
|
|
|
|
|
if configContent.Created != nil && !configContent.Created.IsZero() {
|
|
|
|
return *configContent.Created
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(configContent.History) != 0 {
|
|
|
|
timeStamp = configContent.History[len(configContent.History)-1].Created
|
|
|
|
}
|
|
|
|
|
|
|
|
if timeStamp == nil {
|
|
|
|
timeStamp = &time.Time{}
|
|
|
|
}
|
|
|
|
|
|
|
|
return *timeStamp
|
|
|
|
}
|
|
|
|
|
|
|
|
func CheckIsSigned(signatures repodb.ManifestSignatures) bool {
|
|
|
|
for _, signatures := range signatures {
|
|
|
|
if len(signatures) > 0 {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func GetRepoTag(searchText string) (string, string, error) {
|
|
|
|
const repoTagCount = 2
|
|
|
|
|
|
|
|
splitSlice := strings.Split(searchText, ":")
|
|
|
|
|
|
|
|
if len(splitSlice) != repoTagCount {
|
|
|
|
return "", "", zerr.ErrInvalidRepoTagFormat
|
|
|
|
}
|
|
|
|
|
|
|
|
repo := strings.TrimSpace(splitSlice[0])
|
|
|
|
tag := strings.TrimSpace(splitSlice[1])
|
|
|
|
|
|
|
|
return repo, tag, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func GetMapKeys[K comparable, V any](genericMap map[K]V) []K {
|
|
|
|
keys := make([]K, 0, len(genericMap))
|
|
|
|
|
|
|
|
for k := range genericMap {
|
|
|
|
keys = append(keys, k)
|
|
|
|
}
|
|
|
|
|
|
|
|
return keys
|
|
|
|
}
|
|
|
|
|
|
|
|
// acceptedByFilter checks that data contains at least 1 element of each filter
|
|
|
|
// criteria(os, arch) present in filter.
|
|
|
|
func AcceptedByFilter(filter repodb.Filter, data repodb.FilterData) bool {
|
|
|
|
if filter.Arch != nil {
|
|
|
|
foundArch := false
|
|
|
|
for _, arch := range filter.Arch {
|
|
|
|
foundArch = foundArch || containsString(data.ArchList, *arch)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !foundArch {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if filter.Os != nil {
|
|
|
|
foundOs := false
|
|
|
|
for _, os := range filter.Os {
|
|
|
|
foundOs = foundOs || containsString(data.OsList, *os)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !foundOs {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if filter.HasToBeSigned != nil && *filter.HasToBeSigned != data.IsSigned {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
func containsString(strSlice []string, str string) bool {
|
|
|
|
for _, val := range strSlice {
|
|
|
|
if strings.EqualFold(val, str) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|