gitea源码

api_packages_test.go 24KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702
  1. // Copyright 2021 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package integration
  4. import (
  5. "bytes"
  6. "crypto/sha256"
  7. "fmt"
  8. "net/http"
  9. "strings"
  10. "testing"
  11. "time"
  12. auth_model "code.gitea.io/gitea/models/auth"
  13. "code.gitea.io/gitea/models/db"
  14. packages_model "code.gitea.io/gitea/models/packages"
  15. "code.gitea.io/gitea/models/unittest"
  16. user_model "code.gitea.io/gitea/models/user"
  17. container_module "code.gitea.io/gitea/modules/packages/container"
  18. "code.gitea.io/gitea/modules/setting"
  19. api "code.gitea.io/gitea/modules/structs"
  20. "code.gitea.io/gitea/modules/util"
  21. packages_service "code.gitea.io/gitea/services/packages"
  22. packages_cleanup_service "code.gitea.io/gitea/services/packages/cleanup"
  23. repo_service "code.gitea.io/gitea/services/repository"
  24. "code.gitea.io/gitea/tests"
  25. "github.com/stretchr/testify/assert"
  26. )
  27. func TestPackageAPI(t *testing.T) {
  28. defer tests.PrepareTestEnv(t)()
  29. user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
  30. session := loginUser(t, user.Name)
  31. tokenReadPackage := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadPackage)
  32. tokenWritePackage := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWritePackage)
  33. packageName := "test-package"
  34. packageVersion := "1.0.3"
  35. filename := "file.bin"
  36. url := fmt.Sprintf("/api/packages/%s/generic/%s/%s/%s", user.Name, packageName, packageVersion, filename)
  37. req := NewRequestWithBody(t, "PUT", url, bytes.NewReader([]byte{})).
  38. AddBasicAuth(user.Name)
  39. MakeRequest(t, req, http.StatusCreated)
  40. t.Run("ListPackages", func(t *testing.T) {
  41. defer tests.PrintCurrentTest(t)()
  42. req := NewRequest(t, "GET", "/api/v1/packages/"+user.Name).
  43. AddTokenAuth(tokenReadPackage)
  44. resp := MakeRequest(t, req, http.StatusOK)
  45. var apiPackages []*api.Package
  46. DecodeJSON(t, resp, &apiPackages)
  47. assert.Len(t, apiPackages, 1)
  48. assert.Equal(t, string(packages_model.TypeGeneric), apiPackages[0].Type)
  49. assert.Equal(t, packageName, apiPackages[0].Name)
  50. assert.Equal(t, packageVersion, apiPackages[0].Version)
  51. assert.NotNil(t, apiPackages[0].Creator)
  52. assert.Equal(t, user.Name, apiPackages[0].Creator.UserName)
  53. })
  54. t.Run("GetPackage", func(t *testing.T) {
  55. defer tests.PrintCurrentTest(t)()
  56. req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/dummy/%s/%s", user.Name, packageName, packageVersion)).
  57. AddTokenAuth(tokenReadPackage)
  58. MakeRequest(t, req, http.StatusNotFound)
  59. req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s", user.Name, packageName, packageVersion)).
  60. AddTokenAuth(tokenReadPackage)
  61. resp := MakeRequest(t, req, http.StatusOK)
  62. var p *api.Package
  63. DecodeJSON(t, resp, &p)
  64. assert.Equal(t, string(packages_model.TypeGeneric), p.Type)
  65. assert.Equal(t, packageName, p.Name)
  66. assert.Equal(t, packageVersion, p.Version)
  67. assert.NotNil(t, p.Creator)
  68. assert.Equal(t, user.Name, p.Creator.UserName)
  69. })
  70. t.Run("ListPackageVersions", func(t *testing.T) {
  71. defer tests.PrintCurrentTest(t)()
  72. req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s", user.Name, packageName)).
  73. AddTokenAuth(tokenReadPackage)
  74. resp := MakeRequest(t, req, http.StatusOK)
  75. var apiPackages []*api.Package
  76. DecodeJSON(t, resp, &apiPackages)
  77. assert.Len(t, apiPackages, 1)
  78. assert.Equal(t, string(packages_model.TypeGeneric), apiPackages[0].Type)
  79. assert.Equal(t, packageName, apiPackages[0].Name)
  80. assert.Equal(t, packageVersion, apiPackages[0].Version)
  81. })
  82. t.Run("LatestPackageVersion", func(t *testing.T) {
  83. defer tests.PrintCurrentTest(t)()
  84. req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/-/latest", user.Name, packageName)).
  85. AddTokenAuth(tokenReadPackage)
  86. resp := MakeRequest(t, req, http.StatusOK)
  87. var apiPackage *api.Package
  88. DecodeJSON(t, resp, &apiPackage)
  89. assert.Equal(t, string(packages_model.TypeGeneric), apiPackage.Type)
  90. assert.Equal(t, packageName, apiPackage.Name)
  91. assert.Equal(t, packageVersion, apiPackage.Version)
  92. })
  93. t.Run("RepositoryLink", func(t *testing.T) {
  94. defer tests.PrintCurrentTest(t)()
  95. _, err := packages_model.GetPackageByName(t.Context(), user.ID, packages_model.TypeGeneric, packageName)
  96. assert.NoError(t, err)
  97. // no repository link
  98. req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s", user.Name, packageName, packageVersion)).
  99. AddTokenAuth(tokenReadPackage)
  100. resp := MakeRequest(t, req, http.StatusOK)
  101. var ap1 *api.Package
  102. DecodeJSON(t, resp, &ap1)
  103. assert.Nil(t, ap1.Repository)
  104. // create a repository
  105. newRepo, err := repo_service.CreateRepository(t.Context(), user, user, repo_service.CreateRepoOptions{
  106. Name: "repo4",
  107. })
  108. assert.NoError(t, err)
  109. // link to public repository
  110. req = NewRequest(t, "POST", fmt.Sprintf("/api/v1/packages/%s/generic/%s/-/link/%s", user.Name, packageName, newRepo.Name)).AddTokenAuth(tokenWritePackage)
  111. MakeRequest(t, req, http.StatusCreated)
  112. req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s", user.Name, packageName, packageVersion)).
  113. AddTokenAuth(tokenReadPackage)
  114. resp = MakeRequest(t, req, http.StatusOK)
  115. var ap2 *api.Package
  116. DecodeJSON(t, resp, &ap2)
  117. assert.NotNil(t, ap2.Repository)
  118. assert.Equal(t, newRepo.ID, ap2.Repository.ID)
  119. // link to repository without write access, should fail
  120. req = NewRequest(t, "POST", fmt.Sprintf("/api/v1/packages/%s/generic/%s/-/link/%s", user.Name, packageName, "repo3")).AddTokenAuth(tokenWritePackage)
  121. MakeRequest(t, req, http.StatusNotFound)
  122. // remove link
  123. req = NewRequest(t, "POST", fmt.Sprintf("/api/v1/packages/%s/generic/%s/-/unlink", user.Name, packageName)).AddTokenAuth(tokenWritePackage)
  124. MakeRequest(t, req, http.StatusNoContent)
  125. req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s", user.Name, packageName, packageVersion)).
  126. AddTokenAuth(tokenReadPackage)
  127. resp = MakeRequest(t, req, http.StatusOK)
  128. var ap3 *api.Package
  129. DecodeJSON(t, resp, &ap3)
  130. assert.Nil(t, ap3.Repository)
  131. // force link to a repository the currently logged-in user doesn't have access to
  132. privateRepoID := int64(6)
  133. assert.NoError(t, packages_model.SetRepositoryLink(t.Context(), ap1.ID, privateRepoID))
  134. req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s", user.Name, packageName, packageVersion)).AddTokenAuth(tokenReadPackage)
  135. resp = MakeRequest(t, req, http.StatusOK)
  136. var ap4 *api.Package
  137. DecodeJSON(t, resp, &ap4)
  138. assert.Nil(t, ap4.Repository)
  139. assert.NoError(t, packages_model.UnlinkRepositoryFromAllPackages(t.Context(), privateRepoID))
  140. })
  141. t.Run("ListPackageFiles", func(t *testing.T) {
  142. defer tests.PrintCurrentTest(t)()
  143. req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/dummy/%s/%s/files", user.Name, packageName, packageVersion)).
  144. AddTokenAuth(tokenReadPackage)
  145. MakeRequest(t, req, http.StatusNotFound)
  146. req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s/files", user.Name, packageName, packageVersion)).
  147. AddTokenAuth(tokenReadPackage)
  148. resp := MakeRequest(t, req, http.StatusOK)
  149. var files []*api.PackageFile
  150. DecodeJSON(t, resp, &files)
  151. assert.Len(t, files, 1)
  152. assert.Equal(t, int64(0), files[0].Size)
  153. assert.Equal(t, filename, files[0].Name)
  154. assert.Equal(t, "d41d8cd98f00b204e9800998ecf8427e", files[0].HashMD5)
  155. assert.Equal(t, "da39a3ee5e6b4b0d3255bfef95601890afd80709", files[0].HashSHA1)
  156. assert.Equal(t, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", files[0].HashSHA256)
  157. assert.Equal(t, "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e", files[0].HashSHA512)
  158. })
  159. t.Run("DeletePackage", func(t *testing.T) {
  160. defer tests.PrintCurrentTest(t)()
  161. req := NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/packages/%s/dummy/%s/%s", user.Name, packageName, packageVersion)).
  162. AddTokenAuth(tokenWritePackage)
  163. MakeRequest(t, req, http.StatusNotFound)
  164. req = NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s", user.Name, packageName, packageVersion)).
  165. AddTokenAuth(tokenWritePackage)
  166. MakeRequest(t, req, http.StatusNoContent)
  167. })
  168. }
  169. func TestPackageAccess(t *testing.T) {
  170. defer tests.PrepareTestEnv(t)()
  171. admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
  172. user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5})
  173. inactive := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 9})
  174. limitedUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 33})
  175. privateUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 31})
  176. privateOrgMember := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 23}) // user has package write access
  177. limitedOrgMember := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 36}) // user has package write access
  178. publicOrgMember := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 25}) // user has package read access
  179. privateOrgNoMember := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 35})
  180. limitedOrgNoMember := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 22})
  181. publicOrgNoMember := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 17})
  182. uploadPackage := func(doer, owner *user_model.User, filename string, expectedStatus int) {
  183. url := fmt.Sprintf("/api/packages/%s/generic/test-package/1.0/%s.bin", owner.Name, filename)
  184. req := NewRequestWithBody(t, "PUT", url, bytes.NewReader([]byte{1}))
  185. if doer != nil {
  186. req.AddBasicAuth(doer.Name)
  187. }
  188. MakeRequest(t, req, expectedStatus)
  189. }
  190. downloadPackage := func(doer, owner *user_model.User, expectedStatus int) {
  191. url := fmt.Sprintf("/api/packages/%s/generic/test-package/1.0/admin.bin", owner.Name)
  192. req := NewRequest(t, "GET", url)
  193. if doer != nil {
  194. req.AddBasicAuth(doer.Name)
  195. }
  196. MakeRequest(t, req, expectedStatus)
  197. }
  198. type Target struct {
  199. Owner *user_model.User
  200. ExpectedStatus int
  201. }
  202. t.Run("Upload", func(t *testing.T) {
  203. defer tests.PrintCurrentTest(t)()
  204. cases := []struct {
  205. Doer *user_model.User
  206. Filename string
  207. Targets []Target
  208. }{
  209. { // Admins can upload to every owner
  210. Doer: admin,
  211. Filename: "admin",
  212. Targets: []Target{
  213. {admin, http.StatusCreated},
  214. {inactive, http.StatusCreated},
  215. {user, http.StatusCreated},
  216. {limitedUser, http.StatusCreated},
  217. {privateUser, http.StatusCreated},
  218. {privateOrgMember, http.StatusCreated},
  219. {limitedOrgMember, http.StatusCreated},
  220. {publicOrgMember, http.StatusCreated},
  221. {privateOrgNoMember, http.StatusCreated},
  222. {limitedOrgNoMember, http.StatusCreated},
  223. {publicOrgNoMember, http.StatusCreated},
  224. },
  225. },
  226. { // Without credentials no upload should be possible
  227. Doer: nil,
  228. Filename: "nil",
  229. Targets: []Target{
  230. {admin, http.StatusUnauthorized},
  231. {inactive, http.StatusUnauthorized},
  232. {user, http.StatusUnauthorized},
  233. {limitedUser, http.StatusUnauthorized},
  234. {privateUser, http.StatusUnauthorized},
  235. {privateOrgMember, http.StatusUnauthorized},
  236. {limitedOrgMember, http.StatusUnauthorized},
  237. {publicOrgMember, http.StatusUnauthorized},
  238. {privateOrgNoMember, http.StatusUnauthorized},
  239. {limitedOrgNoMember, http.StatusUnauthorized},
  240. {publicOrgNoMember, http.StatusUnauthorized},
  241. },
  242. },
  243. { // Inactive users can't upload anywhere
  244. Doer: inactive,
  245. Filename: "inactive",
  246. Targets: []Target{
  247. {admin, http.StatusUnauthorized},
  248. {inactive, http.StatusUnauthorized},
  249. {user, http.StatusUnauthorized},
  250. {limitedUser, http.StatusUnauthorized},
  251. {privateUser, http.StatusUnauthorized},
  252. {privateOrgMember, http.StatusUnauthorized},
  253. {limitedOrgMember, http.StatusUnauthorized},
  254. {publicOrgMember, http.StatusUnauthorized},
  255. {privateOrgNoMember, http.StatusUnauthorized},
  256. {limitedOrgNoMember, http.StatusUnauthorized},
  257. {publicOrgNoMember, http.StatusUnauthorized},
  258. },
  259. },
  260. { // Normal users can upload to self and orgs in which they are members and have package write access
  261. Doer: user,
  262. Filename: "user",
  263. Targets: []Target{
  264. {admin, http.StatusUnauthorized},
  265. {inactive, http.StatusUnauthorized},
  266. {user, http.StatusCreated},
  267. {limitedUser, http.StatusUnauthorized},
  268. {privateUser, http.StatusUnauthorized},
  269. {privateOrgMember, http.StatusCreated},
  270. {limitedOrgMember, http.StatusCreated},
  271. {publicOrgMember, http.StatusUnauthorized},
  272. {privateOrgNoMember, http.StatusUnauthorized},
  273. {limitedOrgNoMember, http.StatusUnauthorized},
  274. {publicOrgNoMember, http.StatusUnauthorized},
  275. },
  276. },
  277. }
  278. for _, c := range cases {
  279. for _, t := range c.Targets {
  280. uploadPackage(c.Doer, t.Owner, c.Filename, t.ExpectedStatus)
  281. }
  282. }
  283. })
  284. t.Run("Download", func(t *testing.T) {
  285. defer tests.PrintCurrentTest(t)()
  286. cases := []struct {
  287. Doer *user_model.User
  288. Filename string
  289. Targets []Target
  290. }{
  291. { // Admins can access everything
  292. Doer: admin,
  293. Targets: []Target{
  294. {admin, http.StatusOK},
  295. {inactive, http.StatusOK},
  296. {user, http.StatusOK},
  297. {limitedUser, http.StatusOK},
  298. {privateUser, http.StatusOK},
  299. {privateOrgMember, http.StatusOK},
  300. {limitedOrgMember, http.StatusOK},
  301. {publicOrgMember, http.StatusOK},
  302. {privateOrgNoMember, http.StatusOK},
  303. {limitedOrgNoMember, http.StatusOK},
  304. {publicOrgNoMember, http.StatusOK},
  305. },
  306. },
  307. { // Without credentials only public owners are accessible
  308. Doer: nil,
  309. Targets: []Target{
  310. {admin, http.StatusOK},
  311. {inactive, http.StatusOK},
  312. {user, http.StatusOK},
  313. {limitedUser, http.StatusUnauthorized},
  314. {privateUser, http.StatusUnauthorized},
  315. {privateOrgMember, http.StatusUnauthorized},
  316. {limitedOrgMember, http.StatusUnauthorized},
  317. {publicOrgMember, http.StatusOK},
  318. {privateOrgNoMember, http.StatusUnauthorized},
  319. {limitedOrgNoMember, http.StatusUnauthorized},
  320. {publicOrgNoMember, http.StatusOK},
  321. },
  322. },
  323. { // Inactive users have no access
  324. Doer: inactive,
  325. Targets: []Target{
  326. {admin, http.StatusUnauthorized},
  327. {inactive, http.StatusUnauthorized},
  328. {user, http.StatusUnauthorized},
  329. {limitedUser, http.StatusUnauthorized},
  330. {privateUser, http.StatusUnauthorized},
  331. {privateOrgMember, http.StatusUnauthorized},
  332. {limitedOrgMember, http.StatusUnauthorized},
  333. {publicOrgMember, http.StatusUnauthorized},
  334. {privateOrgNoMember, http.StatusUnauthorized},
  335. {limitedOrgNoMember, http.StatusUnauthorized},
  336. {publicOrgNoMember, http.StatusUnauthorized},
  337. },
  338. },
  339. { // Normal users can access self, public or limited users/orgs and private orgs in which they are members
  340. Doer: user,
  341. Targets: []Target{
  342. {admin, http.StatusOK},
  343. {inactive, http.StatusOK},
  344. {user, http.StatusOK},
  345. {limitedUser, http.StatusOK},
  346. {privateUser, http.StatusUnauthorized},
  347. {privateOrgMember, http.StatusOK},
  348. {limitedOrgMember, http.StatusOK},
  349. {publicOrgMember, http.StatusOK},
  350. {privateOrgNoMember, http.StatusUnauthorized},
  351. {limitedOrgNoMember, http.StatusOK},
  352. {publicOrgNoMember, http.StatusOK},
  353. },
  354. },
  355. }
  356. for _, c := range cases {
  357. for _, target := range c.Targets {
  358. downloadPackage(c.Doer, target.Owner, target.ExpectedStatus)
  359. }
  360. }
  361. })
  362. t.Run("API", func(t *testing.T) {
  363. defer tests.PrintCurrentTest(t)()
  364. session := loginUser(t, user.Name)
  365. tokenReadPackage := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadPackage)
  366. for _, target := range []Target{
  367. {admin, http.StatusOK},
  368. {inactive, http.StatusOK},
  369. {user, http.StatusOK},
  370. {limitedUser, http.StatusOK},
  371. {privateUser, http.StatusForbidden},
  372. {privateOrgMember, http.StatusOK},
  373. {limitedOrgMember, http.StatusOK},
  374. {publicOrgMember, http.StatusOK},
  375. {privateOrgNoMember, http.StatusForbidden},
  376. {limitedOrgNoMember, http.StatusOK},
  377. {publicOrgNoMember, http.StatusOK},
  378. } {
  379. req := NewRequest(t, "GET", "/api/v1/packages/"+target.Owner.Name).
  380. AddTokenAuth(tokenReadPackage)
  381. MakeRequest(t, req, target.ExpectedStatus)
  382. }
  383. })
  384. }
  385. func TestPackageQuota(t *testing.T) {
  386. defer tests.PrepareTestEnv(t)()
  387. limitTotalOwnerCount, limitTotalOwnerSize := setting.Packages.LimitTotalOwnerCount, setting.Packages.LimitTotalOwnerSize
  388. // Exceeded quota result in StatusForbidden for normal users but admins are always allowed to upload.
  389. admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
  390. user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 10})
  391. t.Run("Common", func(t *testing.T) {
  392. defer tests.PrintCurrentTest(t)()
  393. limitSizeGeneric := setting.Packages.LimitSizeGeneric
  394. uploadPackage := func(doer *user_model.User, version string, expectedStatus int) {
  395. url := fmt.Sprintf("/api/packages/%s/generic/test-package/%s/file.bin", user.Name, version)
  396. req := NewRequestWithBody(t, "PUT", url, bytes.NewReader([]byte{1})).
  397. AddBasicAuth(doer.Name)
  398. MakeRequest(t, req, expectedStatus)
  399. }
  400. setting.Packages.LimitTotalOwnerCount = 0
  401. uploadPackage(user, "1.0", http.StatusForbidden)
  402. uploadPackage(admin, "1.0", http.StatusCreated)
  403. setting.Packages.LimitTotalOwnerCount = limitTotalOwnerCount
  404. setting.Packages.LimitTotalOwnerSize = 0
  405. uploadPackage(user, "1.1", http.StatusForbidden)
  406. uploadPackage(admin, "1.1", http.StatusCreated)
  407. setting.Packages.LimitTotalOwnerSize = limitTotalOwnerSize
  408. setting.Packages.LimitSizeGeneric = 0
  409. uploadPackage(user, "1.2", http.StatusForbidden)
  410. uploadPackage(admin, "1.2", http.StatusCreated)
  411. setting.Packages.LimitSizeGeneric = limitSizeGeneric
  412. })
  413. t.Run("Container", func(t *testing.T) {
  414. defer tests.PrintCurrentTest(t)()
  415. limitSizeContainer := setting.Packages.LimitSizeContainer
  416. uploadBlob := func(doer *user_model.User, data string, expectedStatus int) {
  417. url := fmt.Sprintf("/v2/%s/quota-test/blobs/uploads?digest=sha256:%x", user.Name, sha256.Sum256([]byte(data)))
  418. req := NewRequestWithBody(t, "POST", url, strings.NewReader(data)).
  419. AddBasicAuth(doer.Name)
  420. MakeRequest(t, req, expectedStatus)
  421. }
  422. setting.Packages.LimitTotalOwnerSize = 0
  423. uploadBlob(user, "2", http.StatusForbidden)
  424. uploadBlob(admin, "2", http.StatusCreated)
  425. setting.Packages.LimitTotalOwnerSize = limitTotalOwnerSize
  426. setting.Packages.LimitSizeContainer = 0
  427. uploadBlob(user, "3", http.StatusForbidden)
  428. uploadBlob(admin, "3", http.StatusCreated)
  429. setting.Packages.LimitSizeContainer = limitSizeContainer
  430. })
  431. }
  432. func TestPackageCleanup(t *testing.T) {
  433. defer tests.PrepareTestEnv(t)()
  434. user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
  435. duration, _ := time.ParseDuration("-1h")
  436. t.Run("Common", func(t *testing.T) {
  437. defer tests.PrintCurrentTest(t)()
  438. // Upload and delete a generic package and upload a container blob
  439. data, _ := util.CryptoRandomBytes(5)
  440. url := fmt.Sprintf("/api/packages/%s/generic/cleanup-test/1.1.1/file.bin", user.Name)
  441. req := NewRequestWithBody(t, "PUT", url, bytes.NewReader(data)).
  442. AddBasicAuth(user.Name)
  443. MakeRequest(t, req, http.StatusCreated)
  444. req = NewRequest(t, "DELETE", url).
  445. AddBasicAuth(user.Name)
  446. MakeRequest(t, req, http.StatusNoContent)
  447. data, _ = util.CryptoRandomBytes(5)
  448. url = fmt.Sprintf("/v2/%s/cleanup-test/blobs/uploads?digest=sha256:%x", user.Name, sha256.Sum256(data))
  449. req = NewRequestWithBody(t, "POST", url, bytes.NewReader(data)).
  450. AddBasicAuth(user.Name)
  451. MakeRequest(t, req, http.StatusCreated)
  452. pbs, err := packages_model.FindExpiredUnreferencedBlobs(t.Context(), duration)
  453. assert.NoError(t, err)
  454. assert.NotEmpty(t, pbs)
  455. _, err = packages_model.GetInternalVersionByNameAndVersion(t.Context(), user.ID, packages_model.TypeContainer, "cleanup-test", container_module.UploadVersion)
  456. assert.NoError(t, err)
  457. err = packages_cleanup_service.CleanupTask(t.Context(), duration)
  458. assert.NoError(t, err)
  459. pbs, err = packages_model.FindExpiredUnreferencedBlobs(t.Context(), duration)
  460. assert.NoError(t, err)
  461. assert.Empty(t, pbs)
  462. _, err = packages_model.GetInternalVersionByNameAndVersion(t.Context(), user.ID, packages_model.TypeContainer, "cleanup-test", container_module.UploadVersion)
  463. assert.ErrorIs(t, err, packages_model.ErrPackageNotExist)
  464. })
  465. t.Run("CleanupRules", func(t *testing.T) {
  466. defer tests.PrintCurrentTest(t)()
  467. type version struct {
  468. Version string
  469. ShouldExist bool
  470. Created int64
  471. }
  472. cases := []struct {
  473. Name string
  474. Versions []version
  475. Rule *packages_model.PackageCleanupRule
  476. }{
  477. {
  478. Name: "Disabled",
  479. Versions: []version{
  480. {Version: "keep", ShouldExist: true},
  481. },
  482. Rule: &packages_model.PackageCleanupRule{
  483. Enabled: false,
  484. },
  485. },
  486. {
  487. Name: "KeepCount",
  488. Versions: []version{
  489. {Version: "keep", ShouldExist: true},
  490. {Version: "v1.0", ShouldExist: true},
  491. {Version: "test-3", ShouldExist: false, Created: 1},
  492. {Version: "test-4", ShouldExist: false, Created: 1},
  493. },
  494. Rule: &packages_model.PackageCleanupRule{
  495. Enabled: true,
  496. KeepCount: 2,
  497. },
  498. },
  499. {
  500. Name: "KeepPattern",
  501. Versions: []version{
  502. {Version: "keep", ShouldExist: true},
  503. {Version: "v1.0", ShouldExist: false},
  504. },
  505. Rule: &packages_model.PackageCleanupRule{
  506. Enabled: true,
  507. KeepPattern: "k.+p",
  508. },
  509. },
  510. {
  511. Name: "RemoveDays",
  512. Versions: []version{
  513. {Version: "keep", ShouldExist: true},
  514. {Version: "v1.0", ShouldExist: false, Created: 1},
  515. },
  516. Rule: &packages_model.PackageCleanupRule{
  517. Enabled: true,
  518. RemoveDays: 60,
  519. },
  520. },
  521. {
  522. Name: "RemovePattern",
  523. Versions: []version{
  524. {Version: "test", ShouldExist: true},
  525. {Version: "test-3", ShouldExist: false},
  526. {Version: "test-4", ShouldExist: false},
  527. },
  528. Rule: &packages_model.PackageCleanupRule{
  529. Enabled: true,
  530. RemovePattern: `t[e]+st-\d+`,
  531. },
  532. },
  533. {
  534. Name: "MatchFullName",
  535. Versions: []version{
  536. {Version: "keep", ShouldExist: true},
  537. {Version: "test", ShouldExist: false},
  538. },
  539. Rule: &packages_model.PackageCleanupRule{
  540. Enabled: true,
  541. RemovePattern: `package/test|different/keep`,
  542. MatchFullName: true,
  543. },
  544. },
  545. {
  546. Name: "Mixed",
  547. Versions: func(limit, removeDays int) []version {
  548. aa := []version{
  549. {Version: "keep", ShouldExist: true, Created: time.Now().Add(time.Duration(10000)).Unix()},
  550. {Version: "dummy", ShouldExist: true, Created: 1},
  551. }
  552. for i := range limit {
  553. aa = append(aa, version{Version: fmt.Sprintf("test-%v", i+3), ShouldExist: util.Iif(i < removeDays, true, false), Created: time.Now().AddDate(0, 0, -i).Unix()})
  554. }
  555. return aa
  556. }(220, 7),
  557. Rule: &packages_model.PackageCleanupRule{
  558. Enabled: true,
  559. KeepCount: 1,
  560. KeepPattern: `dummy`,
  561. RemoveDays: 7,
  562. RemovePattern: `t[e]+st-\d+`,
  563. },
  564. },
  565. }
  566. for _, c := range cases {
  567. t.Run(c.Name, func(t *testing.T) {
  568. defer tests.PrintCurrentTest(t)()
  569. for _, v := range c.Versions {
  570. url := fmt.Sprintf("/api/packages/%s/generic/package/%s/file.bin", user.Name, v.Version)
  571. req := NewRequestWithBody(t, "PUT", url, bytes.NewReader([]byte{1})).
  572. AddBasicAuth(user.Name)
  573. MakeRequest(t, req, http.StatusCreated)
  574. if v.Created != 0 {
  575. pv, err := packages_model.GetVersionByNameAndVersion(t.Context(), user.ID, packages_model.TypeGeneric, "package", v.Version)
  576. assert.NoError(t, err)
  577. _, err = db.GetEngine(t.Context()).Exec("UPDATE package_version SET created_unix = ? WHERE id = ?", v.Created, pv.ID)
  578. assert.NoError(t, err)
  579. }
  580. }
  581. c.Rule.OwnerID = user.ID
  582. c.Rule.Type = packages_model.TypeGeneric
  583. pcr, err := packages_model.InsertCleanupRule(t.Context(), c.Rule)
  584. assert.NoError(t, err)
  585. err = packages_cleanup_service.CleanupTask(t.Context(), duration)
  586. assert.NoError(t, err)
  587. for _, v := range c.Versions {
  588. pv, err := packages_model.GetVersionByNameAndVersion(t.Context(), user.ID, packages_model.TypeGeneric, "package", v.Version)
  589. if v.ShouldExist {
  590. assert.NoError(t, err)
  591. err = packages_service.DeletePackageVersionAndReferences(t.Context(), pv)
  592. assert.NoError(t, err)
  593. } else {
  594. assert.ErrorIs(t, err, packages_model.ErrPackageNotExist, v.Version)
  595. }
  596. }
  597. assert.NoError(t, packages_model.DeleteCleanupRuleByID(t.Context(), pcr.ID))
  598. })
  599. }
  600. })
  601. }