gitea源码

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