Line data Source code
1 : // Copyright (C) 2013 The Android Open Source Project 2 : // 3 : // Licensed under the Apache License, Version 2.0 (the "License"); 4 : // you may not use this file except in compliance with the License. 5 : // You may obtain a copy of the License at 6 : // 7 : // http://www.apache.org/licenses/LICENSE-2.0 8 : // 9 : // Unless required by applicable law or agreed to in writing, software 10 : // distributed under the License is distributed on an "AS IS" BASIS, 11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 : // See the License for the specific language governing permissions and 13 : // limitations under the License. 14 : 15 : package com.google.gerrit.server.restapi.project; 16 : 17 : import static com.google.common.base.Preconditions.checkState; 18 : import static java.nio.charset.StandardCharsets.UTF_8; 19 : 20 : import com.google.gerrit.common.data.GarbageCollectionResult; 21 : import com.google.gerrit.common.data.GarbageCollectionResult.GcError; 22 : import com.google.gerrit.common.data.GlobalCapability; 23 : import com.google.gerrit.entities.Project; 24 : import com.google.gerrit.extensions.annotations.RequiresCapability; 25 : import com.google.gerrit.extensions.registration.DynamicItem; 26 : import com.google.gerrit.extensions.restapi.BinaryResult; 27 : import com.google.gerrit.extensions.restapi.Response; 28 : import com.google.gerrit.extensions.restapi.RestModifyView; 29 : import com.google.gerrit.extensions.webui.UiAction; 30 : import com.google.gerrit.server.config.UrlFormatter; 31 : import com.google.gerrit.server.git.GarbageCollection; 32 : import com.google.gerrit.server.git.GitRepositoryManager; 33 : import com.google.gerrit.server.git.WorkQueue; 34 : import com.google.gerrit.server.ioutil.HexFormat; 35 : import com.google.gerrit.server.project.ProjectResource; 36 : import com.google.gerrit.server.restapi.project.GarbageCollect.Input; 37 : import com.google.inject.Inject; 38 : import com.google.inject.Singleton; 39 : import java.io.IOException; 40 : import java.io.OutputStream; 41 : import java.io.OutputStreamWriter; 42 : import java.io.PrintWriter; 43 : import java.util.Collections; 44 : import java.util.Optional; 45 : 46 : /** REST endpoint that executes GC on a project. */ 47 : @RequiresCapability(GlobalCapability.RUN_GC) 48 : @Singleton 49 : public class GarbageCollect 50 : implements RestModifyView<ProjectResource, Input>, UiAction<ProjectResource> { 51 2 : public static class Input { 52 : public boolean showProgress; 53 : public boolean aggressive; 54 : public boolean async; 55 : } 56 : 57 : private final boolean canGC; 58 : private final GarbageCollection.Factory garbageCollectionFactory; 59 : private final WorkQueue workQueue; 60 : private final DynamicItem<UrlFormatter> urlFormatter; 61 : 62 : @Inject 63 : GarbageCollect( 64 : GitRepositoryManager repoManager, 65 : GarbageCollection.Factory garbageCollectionFactory, 66 : WorkQueue workQueue, 67 144 : DynamicItem<UrlFormatter> urlFormatter) { 68 144 : this.workQueue = workQueue; 69 144 : this.urlFormatter = urlFormatter; 70 144 : this.canGC = repoManager.canPerformGC(); 71 144 : this.garbageCollectionFactory = garbageCollectionFactory; 72 144 : } 73 : 74 : @Override 75 : public Response<?> apply(ProjectResource rsrc, Input input) { 76 2 : Project.NameKey project = rsrc.getNameKey(); 77 2 : if (input.async) { 78 0 : return applyAsync(project, input); 79 : } 80 2 : return Response.ok(applySync(project, input)); 81 : } 82 : 83 : private Response.Accepted applyAsync(Project.NameKey project, Input input) { 84 0 : Runnable job = 85 0 : new Runnable() { 86 : @Override 87 : public void run() { 88 0 : runGC(project, input, null); 89 0 : } 90 : 91 : @Override 92 : public String toString() { 93 0 : return "Run " 94 0 : + (input.aggressive ? "aggressive " : "") 95 : + "garbage collection on project " 96 0 : + project.get(); 97 : } 98 : }; 99 : 100 : @SuppressWarnings("unchecked") 101 0 : WorkQueue.Task<Void> task = (WorkQueue.Task<Void>) workQueue.getDefaultQueue().submit(job); 102 : 103 0 : Optional<String> url = 104 : urlFormatter 105 0 : .get() 106 0 : .getRestUrl("a/config/server/tasks/" + HexFormat.fromInt(task.getTaskId())); 107 : // We're in a HTTP handler, so must be present. 108 0 : checkState(url.isPresent()); 109 0 : return Response.accepted(url.get()); 110 : } 111 : 112 : @SuppressWarnings("resource") 113 : private BinaryResult applySync(Project.NameKey project, Input input) { 114 2 : return new BinaryResult() { 115 : @Override 116 : public void writeTo(OutputStream out) throws IOException { 117 2 : PrintWriter writer = 118 2 : new PrintWriter(new OutputStreamWriter(out, UTF_8)) { 119 : @Override 120 : public void println() { 121 2 : write('\n'); 122 2 : } 123 : }; 124 : try { 125 2 : PrintWriter progressWriter = input.showProgress ? writer : null; 126 2 : GarbageCollectionResult result = runGC(project, input, progressWriter); 127 2 : String msg = "Garbage collection completed successfully."; 128 2 : if (result.hasErrors()) { 129 0 : for (GcError e : result.getErrors()) { 130 0 : switch (e.getType()) { 131 : case REPOSITORY_NOT_FOUND: 132 0 : msg = "Error: project \"" + e.getProjectName() + "\" not found."; 133 0 : break; 134 : case GC_ALREADY_SCHEDULED: 135 0 : msg = 136 : "Error: garbage collection for project \"" 137 0 : + e.getProjectName() 138 : + "\" was already scheduled."; 139 0 : break; 140 : case GC_FAILED: 141 0 : msg = 142 : "Error: garbage collection for project \"" 143 0 : + e.getProjectName() 144 : + "\" failed."; 145 0 : break; 146 : default: 147 0 : msg = 148 : "Error: garbage collection for project \"" 149 0 : + e.getProjectName() 150 : + "\" failed: " 151 0 : + e.getType() 152 : + "."; 153 : } 154 0 : } 155 : } 156 2 : writer.println(msg); 157 : } finally { 158 2 : writer.flush(); 159 : } 160 2 : } 161 2 : }.setContentType("text/plain").setCharacterEncoding(UTF_8).disableGzip(); 162 : } 163 : 164 : GarbageCollectionResult runGC(Project.NameKey project, Input input, PrintWriter progressWriter) { 165 2 : return garbageCollectionFactory 166 2 : .create() 167 2 : .run(Collections.singletonList(project), input.aggressive, progressWriter); 168 : } 169 : 170 : @Override 171 : public UiAction.Description getDescription(ProjectResource rsrc) { 172 22 : return new UiAction.Description() 173 22 : .setLabel("Run GC") 174 22 : .setTitle("Triggers the Git Garbage Collection for this project.") 175 22 : .setVisible(canGC); 176 : } 177 : }