code.gitea.io/gitea@v1.21.7/models/packages/container/search.go (about) 1 // Copyright 2022 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package container 5 6 import ( 7 "context" 8 "strings" 9 "time" 10 11 "code.gitea.io/gitea/models/db" 12 "code.gitea.io/gitea/models/packages" 13 user_model "code.gitea.io/gitea/models/user" 14 container_module "code.gitea.io/gitea/modules/packages/container" 15 "code.gitea.io/gitea/modules/util" 16 17 "xorm.io/builder" 18 ) 19 20 var ErrContainerBlobNotExist = util.NewNotExistErrorf("container blob does not exist") 21 22 type BlobSearchOptions struct { 23 OwnerID int64 24 Image string 25 Digest string 26 Tag string 27 IsManifest bool 28 Repository string 29 } 30 31 func (opts *BlobSearchOptions) toConds() builder.Cond { 32 var cond builder.Cond = builder.Eq{ 33 "package.type": packages.TypeContainer, 34 } 35 36 if opts.OwnerID != 0 { 37 cond = cond.And(builder.Eq{"package.owner_id": opts.OwnerID}) 38 } 39 if opts.Image != "" { 40 cond = cond.And(builder.Eq{"package.lower_name": strings.ToLower(opts.Image)}) 41 } 42 if opts.Tag != "" { 43 cond = cond.And(builder.Eq{"package_version.lower_version": strings.ToLower(opts.Tag)}) 44 } 45 if opts.IsManifest { 46 cond = cond.And(builder.Eq{"package_file.lower_name": ManifestFilename}) 47 } 48 if opts.Digest != "" { 49 var propsCond builder.Cond = builder.Eq{ 50 "package_property.ref_type": packages.PropertyTypeFile, 51 "package_property.name": container_module.PropertyDigest, 52 "package_property.value": opts.Digest, 53 } 54 55 cond = cond.And(builder.In("package_file.id", builder.Select("package_property.ref_id").Where(propsCond).From("package_property"))) 56 } 57 if opts.Repository != "" { 58 var propsCond builder.Cond = builder.Eq{ 59 "package_property.ref_type": packages.PropertyTypePackage, 60 "package_property.name": container_module.PropertyRepository, 61 "package_property.value": opts.Repository, 62 } 63 64 cond = cond.And(builder.In("package.id", builder.Select("package_property.ref_id").Where(propsCond).From("package_property"))) 65 } 66 67 return cond 68 } 69 70 // GetContainerBlob gets the container blob matching the blob search options 71 // If multiple matching blobs are found (manifests with the same digest) the first (according to the database) is selected. 72 func GetContainerBlob(ctx context.Context, opts *BlobSearchOptions) (*packages.PackageFileDescriptor, error) { 73 pfds, err := getContainerBlobsLimit(ctx, opts, 1) 74 if err != nil { 75 return nil, err 76 } 77 if len(pfds) != 1 { 78 return nil, ErrContainerBlobNotExist 79 } 80 81 return pfds[0], nil 82 } 83 84 // GetContainerBlobs gets the container blobs matching the blob search options 85 func GetContainerBlobs(ctx context.Context, opts *BlobSearchOptions) ([]*packages.PackageFileDescriptor, error) { 86 return getContainerBlobsLimit(ctx, opts, 0) 87 } 88 89 func getContainerBlobsLimit(ctx context.Context, opts *BlobSearchOptions, limit int) ([]*packages.PackageFileDescriptor, error) { 90 pfs := make([]*packages.PackageFile, 0, limit) 91 sess := db.GetEngine(ctx). 92 Join("INNER", "package_version", "package_version.id = package_file.version_id"). 93 Join("INNER", "package", "package.id = package_version.package_id"). 94 Where(opts.toConds()) 95 96 if limit > 0 { 97 sess = sess.Limit(limit) 98 } 99 100 if err := sess.Find(&pfs); err != nil { 101 return nil, err 102 } 103 104 return packages.GetPackageFileDescriptors(ctx, pfs) 105 } 106 107 // GetManifestVersions gets all package versions representing the matching manifest 108 func GetManifestVersions(ctx context.Context, opts *BlobSearchOptions) ([]*packages.PackageVersion, error) { 109 cond := opts.toConds().And(builder.Eq{"package_version.is_internal": false}) 110 111 pvs := make([]*packages.PackageVersion, 0, 10) 112 return pvs, db.GetEngine(ctx). 113 Join("INNER", "package", "package.id = package_version.package_id"). 114 Join("INNER", "package_file", "package_file.version_id = package_version.id"). 115 Where(cond). 116 Find(&pvs) 117 } 118 119 // GetImageTags gets a sorted list of the tags of an image 120 // The result is suitable for the api call. 121 func GetImageTags(ctx context.Context, ownerID int64, image string, n int, last string) ([]string, error) { 122 // Short circuit: n == 0 should return an empty list 123 if n == 0 { 124 return []string{}, nil 125 } 126 127 var cond builder.Cond = builder.Eq{ 128 "package.type": packages.TypeContainer, 129 "package.owner_id": ownerID, 130 "package.lower_name": strings.ToLower(image), 131 "package_version.is_internal": false, 132 } 133 134 var propsCond builder.Cond = builder.Eq{ 135 "package_property.ref_type": packages.PropertyTypeVersion, 136 "package_property.name": container_module.PropertyManifestTagged, 137 } 138 139 cond = cond.And(builder.In("package_version.id", builder.Select("package_property.ref_id").Where(propsCond).From("package_property"))) 140 141 if last != "" { 142 cond = cond.And(builder.Gt{"package_version.lower_version": strings.ToLower(last)}) 143 } 144 145 sess := db.GetEngine(ctx). 146 Table("package_version"). 147 Select("package_version.lower_version"). 148 Join("INNER", "package", "package.id = package_version.package_id"). 149 Where(cond). 150 Asc("package_version.lower_version") 151 152 var tags []string 153 if n > 0 { 154 sess = sess.Limit(n) 155 156 tags = make([]string, 0, n) 157 } else { 158 tags = make([]string, 0, 10) 159 } 160 161 return tags, sess.Find(&tags) 162 } 163 164 type ImageTagsSearchOptions struct { 165 PackageID int64 166 Query string 167 IsTagged bool 168 Sort packages.VersionSort 169 db.Paginator 170 } 171 172 func (opts *ImageTagsSearchOptions) toConds() builder.Cond { 173 var cond builder.Cond = builder.Eq{ 174 "package.type": packages.TypeContainer, 175 "package.id": opts.PackageID, 176 "package_version.is_internal": false, 177 } 178 179 if opts.Query != "" { 180 cond = cond.And(builder.Like{"package_version.lower_version", strings.ToLower(opts.Query)}) 181 } 182 183 var propsCond builder.Cond = builder.Eq{ 184 "package_property.ref_type": packages.PropertyTypeVersion, 185 "package_property.name": container_module.PropertyManifestTagged, 186 } 187 188 in := builder.In("package_version.id", builder.Select("package_property.ref_id").Where(propsCond).From("package_property")) 189 190 if opts.IsTagged { 191 cond = cond.And(in) 192 } else { 193 cond = cond.And(builder.Not{in}) 194 } 195 196 return cond 197 } 198 199 func (opts *ImageTagsSearchOptions) configureOrderBy(e db.Engine) { 200 switch opts.Sort { 201 case packages.SortVersionDesc: 202 e.Desc("package_version.version") 203 case packages.SortVersionAsc: 204 e.Asc("package_version.version") 205 case packages.SortCreatedAsc: 206 e.Asc("package_version.created_unix") 207 default: 208 e.Desc("package_version.created_unix") 209 } 210 } 211 212 // SearchImageTags gets a sorted list of the tags of an image 213 func SearchImageTags(ctx context.Context, opts *ImageTagsSearchOptions) ([]*packages.PackageVersion, int64, error) { 214 sess := db.GetEngine(ctx). 215 Join("INNER", "package", "package.id = package_version.package_id"). 216 Where(opts.toConds()) 217 218 opts.configureOrderBy(sess) 219 220 if opts.Paginator != nil { 221 sess = db.SetSessionPagination(sess, opts) 222 } 223 224 pvs := make([]*packages.PackageVersion, 0, 10) 225 count, err := sess.FindAndCount(&pvs) 226 return pvs, count, err 227 } 228 229 // SearchExpiredUploadedBlobs gets all uploaded blobs which are older than specified 230 func SearchExpiredUploadedBlobs(ctx context.Context, olderThan time.Duration) ([]*packages.PackageFile, error) { 231 var cond builder.Cond = builder.Eq{ 232 "package_version.is_internal": true, 233 "package_version.lower_version": UploadVersion, 234 "package.type": packages.TypeContainer, 235 } 236 cond = cond.And(builder.Lt{"package_file.created_unix": time.Now().Add(-olderThan).Unix()}) 237 238 var pfs []*packages.PackageFile 239 return pfs, db.GetEngine(ctx). 240 Join("INNER", "package_version", "package_version.id = package_file.version_id"). 241 Join("INNER", "package", "package.id = package_version.package_id"). 242 Where(cond). 243 Find(&pfs) 244 } 245 246 // GetRepositories gets a sorted list of all repositories 247 func GetRepositories(ctx context.Context, actor *user_model.User, n int, last string) ([]string, error) { 248 var cond builder.Cond = builder.Eq{ 249 "package.type": packages.TypeContainer, 250 "package_property.ref_type": packages.PropertyTypePackage, 251 "package_property.name": container_module.PropertyRepository, 252 } 253 254 cond = cond.And(builder.Exists( 255 builder. 256 Select("package_version.id"). 257 Where(builder.Eq{"package_version.is_internal": false}.And(builder.Expr("package.id = package_version.package_id"))). 258 From("package_version"), 259 )) 260 261 if last != "" { 262 cond = cond.And(builder.Gt{"package_property.value": strings.ToLower(last)}) 263 } 264 265 if actor.IsGhost() { 266 actor = nil 267 } 268 269 cond = cond.And(user_model.BuildCanSeeUserCondition(actor)) 270 271 sess := db.GetEngine(ctx). 272 Table("package"). 273 Select("package_property.value"). 274 Join("INNER", "user", "`user`.id = package.owner_id"). 275 Join("INNER", "package_property", "package_property.ref_id = package.id"). 276 Where(cond). 277 Asc("package_property.value"). 278 Limit(n) 279 280 repositories := make([]string, 0, n) 281 return repositories, sess.Find(&repositories) 282 }