code.gitea.io/gitea@v1.22.3/services/packages/cleanup/cleanup.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 "fmt" 9 "time" 10 11 "code.gitea.io/gitea/models/db" 12 packages_model "code.gitea.io/gitea/models/packages" 13 user_model "code.gitea.io/gitea/models/user" 14 "code.gitea.io/gitea/modules/log" 15 "code.gitea.io/gitea/modules/optional" 16 packages_module "code.gitea.io/gitea/modules/packages" 17 packages_service "code.gitea.io/gitea/services/packages" 18 alpine_service "code.gitea.io/gitea/services/packages/alpine" 19 cargo_service "code.gitea.io/gitea/services/packages/cargo" 20 container_service "code.gitea.io/gitea/services/packages/container" 21 debian_service "code.gitea.io/gitea/services/packages/debian" 22 rpm_service "code.gitea.io/gitea/services/packages/rpm" 23 ) 24 25 // Task method to execute cleanup rules and cleanup expired package data 26 func CleanupTask(ctx context.Context, olderThan time.Duration) error { 27 if err := ExecuteCleanupRules(ctx); err != nil { 28 return err 29 } 30 31 return CleanupExpiredData(ctx, olderThan) 32 } 33 34 func ExecuteCleanupRules(outerCtx context.Context) error { 35 ctx, committer, err := db.TxContext(outerCtx) 36 if err != nil { 37 return err 38 } 39 defer committer.Close() 40 41 err = packages_model.IterateEnabledCleanupRules(ctx, func(ctx context.Context, pcr *packages_model.PackageCleanupRule) error { 42 select { 43 case <-outerCtx.Done(): 44 return db.ErrCancelledf("While processing package cleanup rules") 45 default: 46 } 47 48 if err := pcr.CompiledPattern(); err != nil { 49 return fmt.Errorf("CleanupRule [%d]: CompilePattern failed: %w", pcr.ID, err) 50 } 51 52 olderThan := time.Now().AddDate(0, 0, -pcr.RemoveDays) 53 54 packages, err := packages_model.GetPackagesByType(ctx, pcr.OwnerID, pcr.Type) 55 if err != nil { 56 return fmt.Errorf("CleanupRule [%d]: GetPackagesByType failed: %w", pcr.ID, err) 57 } 58 59 anyVersionDeleted := false 60 for _, p := range packages { 61 pvs, _, err := packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{ 62 PackageID: p.ID, 63 IsInternal: optional.Some(false), 64 Sort: packages_model.SortCreatedDesc, 65 Paginator: db.NewAbsoluteListOptions(pcr.KeepCount, 200), 66 }) 67 if err != nil { 68 return fmt.Errorf("CleanupRule [%d]: SearchVersions failed: %w", pcr.ID, err) 69 } 70 versionDeleted := false 71 for _, pv := range pvs { 72 if pcr.Type == packages_model.TypeContainer { 73 if skip, err := container_service.ShouldBeSkipped(ctx, pcr, p, pv); err != nil { 74 return fmt.Errorf("CleanupRule [%d]: container.ShouldBeSkipped failed: %w", pcr.ID, err) 75 } else if skip { 76 log.Debug("Rule[%d]: keep '%s/%s' (container)", pcr.ID, p.Name, pv.Version) 77 continue 78 } 79 } 80 81 toMatch := pv.LowerVersion 82 if pcr.MatchFullName { 83 toMatch = p.LowerName + "/" + pv.LowerVersion 84 } 85 86 if pcr.KeepPatternMatcher != nil && pcr.KeepPatternMatcher.MatchString(toMatch) { 87 log.Debug("Rule[%d]: keep '%s/%s' (keep pattern)", pcr.ID, p.Name, pv.Version) 88 continue 89 } 90 if pv.CreatedUnix.AsLocalTime().After(olderThan) { 91 log.Debug("Rule[%d]: keep '%s/%s' (remove days)", pcr.ID, p.Name, pv.Version) 92 continue 93 } 94 if pcr.RemovePatternMatcher != nil && !pcr.RemovePatternMatcher.MatchString(toMatch) { 95 log.Debug("Rule[%d]: keep '%s/%s' (remove pattern)", pcr.ID, p.Name, pv.Version) 96 continue 97 } 98 99 log.Debug("Rule[%d]: remove '%s/%s'", pcr.ID, p.Name, pv.Version) 100 101 if err := packages_service.DeletePackageVersionAndReferences(ctx, pv); err != nil { 102 return fmt.Errorf("CleanupRule [%d]: DeletePackageVersionAndReferences failed: %w", pcr.ID, err) 103 } 104 105 versionDeleted = true 106 anyVersionDeleted = true 107 } 108 109 if versionDeleted { 110 if pcr.Type == packages_model.TypeCargo { 111 owner, err := user_model.GetUserByID(ctx, pcr.OwnerID) 112 if err != nil { 113 return fmt.Errorf("GetUserByID failed: %w", err) 114 } 115 if err := cargo_service.UpdatePackageIndexIfExists(ctx, owner, owner, p.ID); err != nil { 116 return fmt.Errorf("CleanupRule [%d]: cargo.UpdatePackageIndexIfExists failed: %w", pcr.ID, err) 117 } 118 } 119 } 120 } 121 122 if anyVersionDeleted { 123 if pcr.Type == packages_model.TypeDebian { 124 if err := debian_service.BuildAllRepositoryFiles(ctx, pcr.OwnerID); err != nil { 125 return fmt.Errorf("CleanupRule [%d]: debian.BuildAllRepositoryFiles failed: %w", pcr.ID, err) 126 } 127 } else if pcr.Type == packages_model.TypeAlpine { 128 if err := alpine_service.BuildAllRepositoryFiles(ctx, pcr.OwnerID); err != nil { 129 return fmt.Errorf("CleanupRule [%d]: alpine.BuildAllRepositoryFiles failed: %w", pcr.ID, err) 130 } 131 } else if pcr.Type == packages_model.TypeRpm { 132 if err := rpm_service.BuildAllRepositoryFiles(ctx, pcr.OwnerID); err != nil { 133 return fmt.Errorf("CleanupRule [%d]: rpm.BuildAllRepositoryFiles failed: %w", pcr.ID, err) 134 } 135 } 136 } 137 return nil 138 }) 139 if err != nil { 140 return err 141 } 142 143 return committer.Commit() 144 } 145 146 func CleanupExpiredData(outerCtx context.Context, olderThan time.Duration) error { 147 ctx, committer, err := db.TxContext(outerCtx) 148 if err != nil { 149 return err 150 } 151 defer committer.Close() 152 153 if err := container_service.Cleanup(ctx, olderThan); err != nil { 154 return err 155 } 156 157 ps, err := packages_model.FindUnreferencedPackages(ctx) 158 if err != nil { 159 return err 160 } 161 for _, p := range ps { 162 if err := packages_model.DeleteAllProperties(ctx, packages_model.PropertyTypePackage, p.ID); err != nil { 163 return err 164 } 165 if err := packages_model.DeletePackageByID(ctx, p.ID); err != nil { 166 return err 167 } 168 } 169 170 pbs, err := packages_model.FindExpiredUnreferencedBlobs(ctx, olderThan) 171 if err != nil { 172 return err 173 } 174 175 for _, pb := range pbs { 176 if err := packages_model.DeleteBlobByID(ctx, pb.ID); err != nil { 177 return err 178 } 179 } 180 181 if err := committer.Commit(); err != nil { 182 return err 183 } 184 185 contentStore := packages_module.NewContentStore() 186 for _, pb := range pbs { 187 if err := contentStore.Delete(packages_module.BlobHash256Key(pb.HashSHA256)); err != nil { 188 log.Error("Error deleting package blob [%v]: %v", pb.ID, err) 189 } 190 } 191 192 return nil 193 }