Add ability to block lightweight tags

This is added to discourage the use of lighweight tags and make
everyone use annotated tags instead.

Solves: Jira GER-1722
Change-Id: I55576a0d606a6c4c1cd36c47190979b4f0a3a428
diff --git a/src/main/java/com/googlesource/gerrit/plugins/eventseiffel/config/EventListenersConfig.java b/src/main/java/com/googlesource/gerrit/plugins/eventseiffel/config/EventListenersConfig.java
index 32f51c5..21bc418 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/eventseiffel/config/EventListenersConfig.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/eventseiffel/config/EventListenersConfig.java
@@ -34,6 +34,7 @@
     static final String ENABLED = "enabled";
     static final String BLOCKED_REFS = "blockedRef";
     static final String BLOCKED_PROJECTS = "blockedProject";
+    static final String BLOCK_LIGHTWEIGHT_TAGS = "blockLightWeightTags";
 
     @VisibleForTesting
     static boolean filterMatches(String filter, String repoName) {
@@ -56,7 +57,8 @@
       return new EventListenersConfig(
           cfg.getBoolean(EVENT_LISTENERS, ENABLED, true),
           toCombinedMatcher(cfg.getStringList(EVENT_LISTENERS, null, BLOCKED_PROJECTS)),
-          toCombinedMatcher(cfg.getStringList(EVENT_LISTENERS, null, BLOCKED_REFS)));
+          toCombinedMatcher(cfg.getStringList(EVENT_LISTENERS, null, BLOCKED_REFS)),
+          cfg.getBoolean(EVENT_LISTENERS, BLOCK_LIGHTWEIGHT_TAGS, false));
     }
 
     private final PluginConfigFactory configFactory;
@@ -81,7 +83,7 @@
       } catch (NoSuchProjectException e) {
         logger.atSevere().withCause(e).log(
             "Unable to read project.config, using default EventListenersConfig");
-        return new EventListenersConfig(true, toCombinedMatcher(""), toCombinedMatcher(""));
+        return new EventListenersConfig(true, toCombinedMatcher(""), toCombinedMatcher(""), false);
       }
     }
   }
@@ -89,15 +91,18 @@
   private final boolean enabled;
   private final Function<String, Boolean> blockedRefMatcher;
   private final Function<String, Boolean> blockedProjectMatcher;
+  private final boolean blockLightWeightTags;
 
   @VisibleForTesting
   public EventListenersConfig(
       boolean enabled,
       Function<String, Boolean> blockedProjectMatcher,
-      Function<String, Boolean> blockedRefMatcher) {
+      Function<String, Boolean> blockedRefMatcher,
+      boolean blockLightWeightTags) {
     this.enabled = enabled;
     this.blockedProjectMatcher = blockedProjectMatcher;
     this.blockedRefMatcher = blockedRefMatcher;
+    this.blockLightWeightTags = blockLightWeightTags;
   }
 
   public boolean isEnabled() {
@@ -111,4 +116,8 @@
   public boolean projectIsBlocked(String repoName) {
     return blockedProjectMatcher.apply(repoName);
   }
+
+  public boolean blockLightWeightTags() {
+    return blockLightWeightTags;
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/eventseiffel/listeners/RefUpdateListener.java b/src/main/java/com/googlesource/gerrit/plugins/eventseiffel/listeners/RefUpdateListener.java
index bd60da4..8da4990 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/eventseiffel/listeners/RefUpdateListener.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/eventseiffel/listeners/RefUpdateListener.java
@@ -51,7 +51,7 @@
       }
       /* Updated reference is a tag. */
       else if (event.getRefName().startsWith(RefNames.REFS_TAGS)) {
-        queue.scheduleArtcCreation(event, branch -> config.refIsBlocked(branch));
+        queue.scheduleArtcCreation(event, branch -> config.refIsBlocked(branch), config.blockLightWeightTags());
       }
     }
   }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/eventseiffel/parsing/EiffelEventParser.java b/src/main/java/com/googlesource/gerrit/plugins/eventseiffel/parsing/EiffelEventParser.java
index 7210ae7..6b38d55 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/eventseiffel/parsing/EiffelEventParser.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/eventseiffel/parsing/EiffelEventParser.java
@@ -221,13 +221,14 @@
       String tagName,
       Long creationTime,
       boolean force,
-      Function<String, Boolean> blockedRefMatcher) {
+      Function<String, Boolean> blockedRefMatcher,
+      boolean blockLightWeightTags) {
     try {
       CompositionDefinedEventKey cd =
           CompositionDefinedEventKey.create(mapper.tagCompositionName(projectName), tagName);
       Optional<UUID> oldCdId = eventHub.getExistingId(cd);
       if (oldCdId.isEmpty() || force) {
-        createAndScheduleCd(projectName, tagName, creationTime, force, blockedRefMatcher);
+        createAndScheduleCd(projectName, tagName, creationTime, force, blockedRefMatcher, blockLightWeightTags);
         Optional<UUID> cdId = eventHub.getExistingId(cd);
         if (cdId.isPresent() && !cdId.equals(oldCdId)) {
           pushToHub(mapper.toArtc(projectName, tagName, creationTime, cdId.get()), force);
@@ -254,12 +255,13 @@
       String tagName,
       Long creationTime,
       boolean force,
-      Function<String, Boolean> blockedRefMatcher) {
+      Function<String, Boolean> blockedRefMatcher,
+      boolean blockLightWeightTags) {
     Optional<UUID> scsId = Optional.empty();
     List<Ref> refs = null;
 
     try {
-      ObjectId objectId = peelTag(projectName, tagName);
+      ObjectId objectId = peelTag(projectName, tagName, blockLightWeightTags);
       if (objectId == null) {
         return;
       }
@@ -344,12 +346,17 @@
     }
   }
 
-  private ObjectId peelTag(String projectName, String tagName) {
+  private ObjectId peelTag(String projectName, String tagName, boolean blockLightWeightTags) {
     try (Repository repo = repoManager.openRepository(Project.nameKey(projectName))) {
       Ref tagRef = repo.getRefDatabase().exactRef(Constants.R_TAGS + tagName);
       if (tagRef != null) {
         ObjectId peeled = repo.getRefDatabase().peel(tagRef).getPeeledObjectId();
-        return peeled != null ? peeled : tagRef.getObjectId();
+        if (peeled != null)
+          return peeled;
+        if (!blockLightWeightTags)
+          return tagRef.getObjectId();
+        logger.atInfo().log("Creation of CD is blocked for lightweight tags");
+        return null;
       }
       logger.atSevere().log("Cannot find tag: %s:%s", projectName, tagName);
     } catch (IOException e) {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/eventseiffel/parsing/EiffelEventParsingQueue.java b/src/main/java/com/googlesource/gerrit/plugins/eventseiffel/parsing/EiffelEventParsingQueue.java
index ba99d07..36e69ca 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/eventseiffel/parsing/EiffelEventParsingQueue.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/eventseiffel/parsing/EiffelEventParsingQueue.java
@@ -144,16 +144,17 @@
   }
 
   public void scheduleArtcCreation(
-      GitReferenceUpdatedListener.Event event, Function<String, Boolean> blockedRefMatcher) {
+      GitReferenceUpdatedListener.Event event, Function<String, Boolean> blockedRefMatcher, boolean blockLightWeightTags) {
     scheduleArtcCreation(
         event.getProjectName(),
         event.getRefName().substring(RefNames.REFS_TAGS.length()),
         TimeUtil.nowMs(),
         false,
-        blockedRefMatcher);
+        blockedRefMatcher,
+        blockLightWeightTags);
   }
 
-  public void scheduleArtcCreation(TagResource resource, boolean force) {
+  public void scheduleArtcCreation(TagResource resource, boolean force, boolean blockLightWeightTags) {
     String tagRef = resource.getRef();
     scheduleArtcCreation(
         resource.getName(),
@@ -162,7 +163,8 @@
             : tagRef,
         resource.getTagInfo().created.getTime(),
         force,
-        branch -> false);
+        branch -> false,
+        blockLightWeightTags);
   }
 
   public void scheduleArtcCreation(
@@ -170,7 +172,8 @@
       String tagName,
       Long creationTime,
       boolean force,
-      Function<String, Boolean> blockedRefMatcher) {
+      Function<String, Boolean> blockedRefMatcher,
+      boolean blockLightWeightTags) {
     schedule(
         new EventParsingWorker(ARTC, projectName, tagName) {
 
@@ -178,7 +181,7 @@
           public void doRun() {
             try {
               eventParser.createAndScheduleArtc(
-                  projectName, tagName, creationTime, force, blockedRefMatcher);
+                  projectName, tagName, creationTime, force, blockedRefMatcher, blockLightWeightTags);
             } catch (Exception e) {
               logger.atSevere().withCause(e).log(
                   "Failed to create ARTC for %s:%s", projectName, tagName);
diff --git a/src/main/java/com/googlesource/gerrit/plugins/eventseiffel/rest/CreateArtcs.java b/src/main/java/com/googlesource/gerrit/plugins/eventseiffel/rest/CreateArtcs.java
index 9a07c0f..7c53a2d 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/eventseiffel/rest/CreateArtcs.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/eventseiffel/rest/CreateArtcs.java
@@ -25,6 +25,8 @@
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import com.googlesource.gerrit.plugins.eventseiffel.parsing.EiffelEventParsingQueue;
+import com.google.inject.Provider;
+import com.googlesource.gerrit.plugins.eventseiffel.config.EventListenersConfig;
 
 @Singleton
 @RequiresCapability(GlobalCapability.ADMINISTRATE_SERVER)
@@ -34,16 +36,18 @@
   }
 
   private final EiffelEventParsingQueue queue;
+  private final Provider<EventListenersConfig> configProvider;
 
   @Inject
-  CreateArtcs(EiffelEventParsingQueue queue) {
+  CreateArtcs(EiffelEventParsingQueue queue, Provider<EventListenersConfig> configProvider) {
     this.queue = queue;
+    this.configProvider = configProvider;
   }
 
   @Override
   public Response<?> apply(TagResource resource, CreateArtcs.Input input)
       throws AuthException, BadRequestException, ResourceConflictException, Exception {
-    queue.scheduleArtcCreation(resource, input.force);
+    queue.scheduleArtcCreation(resource, input.force, configProvider.get().blockLightWeightTags());
     return EventCreationResponse.artc(resource);
   }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/eventseiffel/rest/RestModule.java b/src/main/java/com/googlesource/gerrit/plugins/eventseiffel/rest/RestModule.java
index 29a643e..637f764 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/eventseiffel/rest/RestModule.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/eventseiffel/rest/RestModule.java
@@ -18,11 +18,16 @@
 import static com.google.gerrit.server.project.TagResource.TAG_KIND;
 
 import com.google.gerrit.extensions.restapi.RestApiModule;
+import com.google.inject.Scopes;
+import com.googlesource.gerrit.plugins.eventseiffel.config.EventListenersConfig;
 
 public class RestModule extends RestApiModule {
 
   @Override
   public void configure() {
+    bind(EventListenersConfig.class)
+        .toProvider(EventListenersConfig.Provider.class)
+        .in(Scopes.SINGLETON);
     post(BRANCH_KIND, "createSccs").to(CreateSccs.class);
     post(BRANCH_KIND, "createScss").to(CreateScss.class);
     post(TAG_KIND, "createArtcs").to(CreateArtcs.class);
diff --git a/src/test/java/com/googlesource/gerrit/plugins/eventseiffel/config/EventListenersConfigTest.java b/src/test/java/com/googlesource/gerrit/plugins/eventseiffel/config/EventListenersConfigTest.java
index ab2ca17..bee0989 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/eventseiffel/config/EventListenersConfigTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/eventseiffel/config/EventListenersConfigTest.java
@@ -148,10 +148,10 @@
   }
 
   private EventListenersConfig refsPatterns(String... patterns) {
-    return new EventListenersConfig(true, toCombinedMatcher(), toCombinedMatcher(patterns));
+    return new EventListenersConfig(true, toCombinedMatcher(), toCombinedMatcher(patterns), false);
   }
 
   private EventListenersConfig projectsPatterns(String... patterns) {
-    return new EventListenersConfig(true, toCombinedMatcher(patterns), toCombinedMatcher());
+    return new EventListenersConfig(true, toCombinedMatcher(patterns), toCombinedMatcher(), false);
   }
 }
diff --git a/src/test/java/com/googlesource/gerrit/plugins/eventseiffel/listeners/GerritEventListenersIT.java b/src/test/java/com/googlesource/gerrit/plugins/eventseiffel/listeners/GerritEventListenersIT.java
index b4cd29c..93ec6a6 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/eventseiffel/listeners/GerritEventListenersIT.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/eventseiffel/listeners/GerritEventListenersIT.java
@@ -340,7 +340,7 @@
     @Override
     public EventListenersConfig get() {
       return new EventListenersConfig(
-          enabled, toCombinedMatcher(projectPatterns), toCombinedMatcher(refPatterns));
+          enabled, toCombinedMatcher(projectPatterns), toCombinedMatcher(refPatterns), false);
     }
   }
 
diff --git a/src/test/java/com/googlesource/gerrit/plugins/eventseiffel/parsing/EiffelEventParserIT.java b/src/test/java/com/googlesource/gerrit/plugins/eventseiffel/parsing/EiffelEventParserIT.java
index cf4194c..9257cb0 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/eventseiffel/parsing/EiffelEventParserIT.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/eventseiffel/parsing/EiffelEventParserIT.java
@@ -210,7 +210,7 @@
     String ref = setCdHandled();
     ArtifactEventKey artc = ArtifactEventKey.create(tagPURL(project.get(), ref));
     markAsHandled(artc);
-    eventParser.createAndScheduleArtc(project.get(), ref, EPOCH_MILLIS, false, branch -> false);
+    eventParser.createAndScheduleArtc(project.get(), ref, EPOCH_MILLIS, false, branch -> false, false);
     assertEquals(0, TestEventHub.EVENTS.size());
   }
 
@@ -224,7 +224,7 @@
     markAsHandled(cd);
     ArtifactEventKey artc = ArtifactEventKey.create(tagPURL(project.get(), ref));
     markAsHandled(artc);
-    eventParser.createAndScheduleArtc(project.get(), ref, EPOCH_MILLIS, true, branch -> false);
+    eventParser.createAndScheduleArtc(project.get(), ref, EPOCH_MILLIS, true, branch -> false, false);
     assertEquals(2, TestEventHub.EVENTS.size());
     assertCorrectEvent(0, cd);
     assertCorrectEvent(1, artc);
@@ -258,7 +258,7 @@
     CompositionDefinedEventKey cd =
         CompositionDefinedEventKey.create(tagCompositionName(project.get(), "localhost"), ref);
 
-    eventParser.createAndScheduleArtc(project.get(), ref, EPOCH_MILLIS, false, branch -> false);
+    eventParser.createAndScheduleArtc(project.get(), ref, EPOCH_MILLIS, false, branch -> false, false);
     assertEquals(4, TestEventHub.EVENTS.size());
     assertCorrectEvent(0, scc);
     assertCorrectEvent(1, scs);
@@ -272,7 +272,7 @@
         createTagRef(getHead(repo(), "HEAD").getName(), true).substring(Constants.R_TAGS.length());
 
     eventParser.createAndScheduleArtc(
-        project.get(), ref, EPOCH_MILLIS, false, branch -> branch.equals("refs/heads/master"));
+        project.get(), ref, EPOCH_MILLIS, false, branch -> branch.equals("refs/heads/master"), false);
     assertEquals(0, TestEventHub.EVENTS.size());
   }
 
@@ -290,7 +290,7 @@
         CompositionDefinedEventKey.create(tagCompositionName(project.get(), "localhost"), ref);
 
     eventParser.createAndScheduleArtc(
-        project.get(), ref, EPOCH_MILLIS, false, branch -> branch.equals("refs/heads/master"));
+        project.get(), ref, EPOCH_MILLIS, false, branch -> branch.equals("refs/heads/master"), false);
     assertEquals(4, TestEventHub.EVENTS.size());
     assertCorrectEvent(0, scc);
     assertCorrectEvent(1, scs);
@@ -301,18 +301,34 @@
   @Test
   public void artcQueuedNoTagCreated() throws Exception {
     eventParser.createAndScheduleArtc(
-        project.get(), TAG_NAME, EPOCH_MILLIS, false, branch -> false);
+        project.get(), TAG_NAME, EPOCH_MILLIS, false, branch -> false, false);
     assertEquals(0, TestEventHub.EVENTS.size());
   }
 
   @Test
   public void annotatedTagArtcQueuedscsHandled() throws Exception {
-    assertArtcQueuedScsHandled(true);
+    assertArtcQueuedScsHandled(true, false);
   }
 
   @Test
-  public void LightweightTagArtcQueuedscsHandled() throws Exception {
-    assertArtcQueuedScsHandled(false);
+  public void lightWeightTagArtcQueuedscsHandled() throws Exception {
+    assertArtcQueuedScsHandled(false, false);
+  }
+
+  @Test
+  public void annotatedTagArtcQueuedscsHandledLightWeightBlocked() throws Exception {
+    assertArtcQueuedScsHandled(true, true);
+  }
+
+  @Test
+  public void lightWeightTagArtcQueuedscsHandledLightWeightBlocked() throws Exception {
+    setScsHandled();
+    String ref =
+        createTagRef(getHead(repo(), "HEAD").getName(), false)
+            .substring(Constants.R_TAGS.length());
+
+    eventParser.createAndScheduleArtc(project.get(), ref, EPOCH_MILLIS, false, branch -> false, true);
+    assertEquals(0, TestEventHub.EVENTS.size());
   }
 
   @Test
@@ -320,7 +336,7 @@
     String ref = setCdHandled();
     ArtifactEventKey artc = ArtifactEventKey.create(tagPURL(project.get(), ref));
 
-    eventParser.createAndScheduleArtc(project.get(), ref, EPOCH_MILLIS, false, branch -> false);
+    eventParser.createAndScheduleArtc(project.get(), ref, EPOCH_MILLIS, false, branch -> false, false);
     assertEquals(0, TestEventHub.EVENTS.size());
   }
 
@@ -328,14 +344,14 @@
   @UseLocalDisk
   @GlobalPluginConfig(pluginName = PLUGIN_NAME, name = "EiffelEvent.namespace", value = NAMESPACE)
   public void artcQueuedscsHandledWithCustomNameSpace() throws Exception {
-    assertArtcQueuedScsHandled(false, NAMESPACE);
+    assertArtcQueuedScsHandled(false, false, NAMESPACE);
   }
 
-  private void assertArtcQueuedScsHandled(boolean annotated) throws Exception {
-    assertArtcQueuedScsHandled(annotated, "localhost");
+  private void assertArtcQueuedScsHandled(boolean annotated, boolean blockLightWeight) throws Exception {
+    assertArtcQueuedScsHandled(annotated, blockLightWeight, "localhost");
   }
 
-  private void assertArtcQueuedScsHandled(boolean annotated, String namespace) throws Exception {
+  private void assertArtcQueuedScsHandled(boolean annotated, boolean blockLightWeight, String namespace) throws Exception {
     setScsHandled();
     String ref =
         createTagRef(getHead(repo(), "HEAD").getName(), annotated)
@@ -344,7 +360,7 @@
     CompositionDefinedEventKey cd =
         CompositionDefinedEventKey.create(tagCompositionName(project.get(), namespace), ref);
 
-    eventParser.createAndScheduleArtc(project.get(), ref, EPOCH_MILLIS, false, branch -> false);
+    eventParser.createAndScheduleArtc(project.get(), ref, EPOCH_MILLIS, false, branch -> false, blockLightWeight);
     assertEquals(2, TestEventHub.EVENTS.size());
     assertCorrectEvent(0, cd);
     assertCorrectEvent(1, artc);