Add pull-replication plugin in `dual-primary` topology.

The `replication` plugin (push-replication) is enabled.

Note: This change adds the pull-replication plugin when
multisite is disabled. The reason is because we want to
provide a solution for one site first and after that
scale to the multisite topology.

Bug: Issue 16393
Change-Id: I8a34525f18cab66ba9c6442e52f84f54f1b4cf70
diff --git a/Secrets.md b/Secrets.md
index 16e9711..f0c5c22 100644
--- a/Secrets.md
+++ b/Secrets.md
@@ -93,3 +93,12 @@
 
 * replication_user_id_rsa
 * replication_user_id_rsa.pub
+
+### Pull-Replication Bearer Token Authentication
+
+*Optional*. This is only needed if you are setting up a recipe that requires replication
+(i.e. primary-replica or dual-primary)
+
+```bash
+openssl rand -hex 20 > /tmp/secrets/pull_replication_bearer_token
+```
diff --git a/dual-primary/Makefile b/dual-primary/Makefile
index af51604..da22337 100644
--- a/dual-primary/Makefile
+++ b/dual-primary/Makefile
@@ -14,8 +14,8 @@
 
 SINGLE_SITE_PLUGINS=javamelody high-availability healthcheck metrics-reporter-cloudwatch
 
-HA_SITE_PLUGINS=$(SINGLE_SITE_PLUGINS) aws-dynamodb-refdb
-HA_SITE_PLUGINS_LIBS_LINKS=high-availability
+HA_SITE_PLUGINS=$(SINGLE_SITE_PLUGINS) aws-dynamodb-refdb pull-replication
+HA_SITE_PLUGINS_LIBS_LINKS=high-availability pull-replication
 HA_SITE_MAVEN_LIBS=global-refdb~$(GLOBALREFDB_LIB_VER)
 
 MULTI_SITE_PLUGINS=$(SINGLE_SITE_PLUGINS) multi-site events-kafka websession-broker aws-dynamodb-refdb
@@ -135,6 +135,7 @@
 		ParameterKey=HttpSubdomain,ParameterValue=$(HTTP_PRIMARY1_SUBDOMAIN) \
 		ParameterKey=SshSubdomain,ParameterValue=$(SSH_PRIMARY1_SUBDOMAIN) \
 		ParameterKey=SshReplicaSubdomain,ParameterValue=$(SSH_REPLICA_SUBDOMAIN) \
+		ParameterKey=HttpReplicaSubdomain,ParameterValue=$(HTTP_REPLICA_SUBDOMAIN) \
 		ParameterKey=DockerRegistryUrl,ParameterValue=$(DOCKER_REGISTRY_URI) \
 		ParameterKey=CertificateArn,ParameterValue=$(SSL_CERTIFICATE_ARN) \
 		ParameterKey=GerritKeyPrefix,ParameterValue=$(GERRIT_KEY_PREFIX)\
@@ -197,6 +198,7 @@
 		ParameterKey=HttpSubdomain,ParameterValue=$(HTTP_PRIMARY2_SUBDOMAIN) \
 		ParameterKey=SshSubdomain,ParameterValue=$(SSH_PRIMARY2_SUBDOMAIN) \
 		ParameterKey=SshReplicaSubdomain,ParameterValue=$(SSH_REPLICA_SUBDOMAIN) \
+		ParameterKey=HttpReplicaSubdomain,ParameterValue=$(HTTP_REPLICA_SUBDOMAIN) \
 		ParameterKey=DockerRegistryUrl,ParameterValue=$(DOCKER_REGISTRY_URI) \
 		ParameterKey=CertificateArn,ParameterValue=$(SSL_CERTIFICATE_ARN) \
 		ParameterKey=GerritKeyPrefix,ParameterValue=$(GERRIT_KEY_PREFIX)\
@@ -275,6 +277,8 @@
 		ParameterKey=GerritCPU,ParameterValue=$(GERRIT_CPU) \
 		ParameterKey=GerritHeapLimit,ParameterValue=$(GERRIT_HEAP_LIMIT) \
 		ParameterKey=JgitCacheSize,ParameterValue=$(JGIT_CACHE_SIZE) \
+		ParameterKey=HttpPrimariesGerritSubdomain,ParameterValue=$(HTTP_PRIMARIES_GERRIT_SUBDOMAIN) \
+		ParameterKey=GerritPrimaryInstanceId,ParameterValue=$(GERRIT_PRIMARY_INSTANCE_ID)
 		$(JGIT_OPTIONAL_PARAMS) \
 		$(LDAP_ACCOUNT_PATTERN_PARAM) \
 		$(REPLICA_SERVICE_OPTIONAL_PARAMS) \
diff --git a/dual-primary/cf-service-primary.yml b/dual-primary/cf-service-primary.yml
index 0424ab7..f2013f0 100644
--- a/dual-primary/cf-service-primary.yml
+++ b/dual-primary/cf-service-primary.yml
@@ -40,6 +40,9 @@
   SshReplicaSubdomain:
         Description: The subdomain of the loadbalancer serving SSH traffic to the replicas
         Type: String
+  HttpReplicaSubdomain:
+        Description: The subdomain of the loadbalancer serving HTTP traffic to the replicas
+        Type: String
   HttpPrimariesGerritSubdomain:
         Description: The subdomain of the load balancer serving HTTP traffic to both gerrit-1 and gerrit-2
         Type: String
@@ -337,6 +340,8 @@
                       Value: !FindInMap ['Git', 'SSH', 'Port']
                     - Name: REPLICA_SUBDOMAIN
                       Value: !Ref SshReplicaSubdomain
+                    - Name: HTTP_REPLICA_SUBDOMAIN
+                      Value: !Ref HttpReplicaSubdomain
                     - Name: GERRIT_INSTANCE_ID
                       Value: !Ref InstanceId
                     - Name: METRICS_CLOUDWATCH_ENABLED
diff --git a/dual-primary/cf-service-replica.yml b/dual-primary/cf-service-replica.yml
index 352aa98..6ad486a 100644
--- a/dual-primary/cf-service-replica.yml
+++ b/dual-primary/cf-service-replica.yml
@@ -218,7 +218,12 @@
     Type: String
     Default: false
     AllowedValues: [true, false]
-
+  HttpPrimariesGerritSubdomain:
+    Description: The subdomain of the loadbalancer serving HTTP traffic to the primaries
+    Type: String
+  GerritPrimaryInstanceId:
+    Description: Gerrit InstanceId shared for all primaries in the same HA
+    Type: String
 Mappings:
   XRay:
     Info:
@@ -288,6 +293,8 @@
                       Value: !Sub 'proxy-https://*:${HTTPContainePort}/'
                     - Name: CONTAINER_REPLICA
                       Value: true
+                    - Name: SETUP_REPLICATION
+                      Value: true
                     - Name: GERRIT_KEY_PREFIX
                       Value: !Ref GerritKeyPrefix
                     - Name: AWS_REGION
@@ -324,6 +331,12 @@
                       Value: !Ref MetricsCloudwatchDryRun
                     - Name: METRICS_CLOUDWATCH_EXCLUDE_METRICS_LIST
                       Value: !Join [',', !Ref MetricsCloudwatchExcludeMetrics]
+                    - Name: HOSTED_ZONE_NAME
+                      Value: !Ref HostedZoneName
+                    - Name: HTTP_PRIMARIES_GERRIT_SUBDOMAIN
+                      Value: !Ref HttpPrimariesGerritSubdomain
+                    - Name: GERRIT_PRIMARY_INSTANCE_ID
+                      Value: !Ref GerritPrimaryInstanceId
                   Ulimits:
                     - Name: nofile
                       HardLimit: !Ref FileDescriptorsHardLimit
diff --git a/gerrit/add_secrets_aws_secrets_manager.sh b/gerrit/add_secrets_aws_secrets_manager.sh
index 2b4fa4e..5b0286a 100755
--- a/gerrit/add_secrets_aws_secrets_manager.sh
+++ b/gerrit/add_secrets_aws_secrets_manager.sh
@@ -76,3 +76,8 @@
   echo "Adding Prometheus bearer token..."
   set-secret-string prometheus_bearer_token
 fi
+
+if [ -f "$SECRETS_DIRECTORY/pull_replication_bearer_token" ]; then
+  echo "Adding Pull-Replication Bearer Token..."
+  set-secret-string pull_replication_bearer_token
+fi
diff --git a/gerrit/etc/replication.config.template b/gerrit/etc/replication.config.template
index dadc203..c58b73d 100644
--- a/gerrit/etc/replication.config.template
+++ b/gerrit/etc/replication.config.template
@@ -17,6 +17,19 @@
   replicateHiddenProjects = true
   timeout = 60
 
+[remote "replica-1-pull"]
+  # Issue 16694: pull-replication: Do not force configuration of `url` in remotes
+  url = {{ REPLICA_1_URL }}
+  apiUrl = {{ REPLICA_1_API_URL }}
+  # Issue 16635: pull-replication should provide `mirror` configuration
+  mirror = true
+  # Issue 16577: pull-replication: Do not force configuration of `fetch` in remotes
+  fetch = +refs/*:refs/*
+  threads = 10
+  createMissingRepositories = true
+  replicateProjectDeletions = true
+  replicateHiddenProjects = true
+  timeout = 60
 
 {% if REMOTE_TARGET %}
 [remote "{{REMOTE_TARGET_URL}}"]
diff --git a/gerrit/etc/replication_replica.config.template b/gerrit/etc/replication_replica.config.template
new file mode 100644
index 0000000..59541cd
--- /dev/null
+++ b/gerrit/etc/replication_replica.config.template
@@ -0,0 +1,19 @@
+[replication]
+  lockErrorMaxRetries = 30
+  maxRetries = 30
+
+[gerrit]
+  autoReload = true
+  replicateOnStartup = true
+
+[remote "{{GERRIT_PRIMARY_INSTANCE_ID}}"]
+  url = {{ HTTP_PRIMARIES_LB }}
+  # Issue 16635: pull-replication should provide `mirror` configuration
+  mirror = true
+  fetch = +refs/*:refs/*
+  threads = 10
+  createMissingRepositories = true
+  replicateProjectDeletions = true
+  replicateHiddenProjects = true
+  timeout = 60
+  tagopt= --no-tags
\ No newline at end of file
diff --git a/gerrit/setup_gerrit.py b/gerrit/setup_gerrit.py
index b181698..de3e439 100755
--- a/gerrit/setup_gerrit.py
+++ b/gerrit/setup_gerrit.py
@@ -95,6 +95,8 @@
     with open(GERRIT_CONFIG_DIRECTORY + secretId, 'w', encoding='utf-8') as f:
         f.write(get_secret(GERRIT_KEY_PREFIX + "_" + secretId))
 
+print("Setup replication: " + str(setupReplication))
+
 if setupReplication:
     GERRIT_SSH_DIRECTORY = "/var/gerrit/.ssh"
     GERRIT_REPLICATION_SSH_KEYS = GERRIT_SSH_DIRECTORY + "/id_rsa"
@@ -178,27 +180,45 @@
     })
     f.write(template.render(config_for_template))
 
-containerReplica = (os.getenv('CONTAINER_REPLICA') == 'true')
-if ((not containerReplica) and setupReplication):
-    print("Setting Replication config in '" +
-          GERRIT_CONFIG_DIRECTORY + "replication.config'")
-    template = env.get_template("replication.config.template")
-    with open(GERRIT_CONFIG_DIRECTORY + "replication.config", 'w', encoding='utf-8') as f:
-        REPLICA_FQDN = os.getenv('REPLICA_SUBDOMAIN') + "." + os.getenv('HOSTED_ZONE_NAME')
-        REMOTE_TARGET = os.getenv('REMOTE_REPLICATION_TARGET_HOST', '')
-        # In a multi-site setup, the very first replication needs to be
-        # triggered manually from site-A to site-B, once the latter is ready,
-        # thus "REPLICATE_ON_STARTUP" needs to be disabled
-        REPLICATE_ON_STARTUP = "false" if setupMultiSite else "true"
-        f.write(template.render(
-                REPLICA_1_URL="git://" + REPLICA_FQDN + ":" + os.getenv('GIT_PORT') + "/${name}.git",
-                REPLICA_1_AMDIN_URL="ssh://gerrit@" + REPLICA_FQDN + ":" + os.getenv('GIT_SSH_PORT') + "/var/gerrit/git/${name}.git",
-                REMOTE_TARGET=REMOTE_TARGET,
-                REMOTE_TARGET_URL="git://" + REMOTE_TARGET + ":" + os.getenv('GIT_PORT') + "/${name}.git",
-                REMOTE_ADMIN_TARGET_URL="ssh://gerrit@" + REMOTE_TARGET + ":" + os.getenv('GIT_SSH_PORT') + "/var/gerrit/git/${name}.git",
-                REPLICATE_ON_STARTUP=REPLICATE_ON_STARTUP,
-                MULTISITE_GLOBAL_PROJECTS=os.getenv('MULTISITE_GLOBAL_PROJECTS', '')
-                ))
+if setupReplication:
+    set_secure_password(
+        "auth.bearerToken",
+        get_secret(GERRIT_KEY_PREFIX + "_pull_replication_bearer_token")
+    )
+    containerReplica = (os.getenv('CONTAINER_REPLICA') == 'true')
+    replication_config_path = GERRIT_CONFIG_DIRECTORY + "replication.config"
+
+    print("Is replica: " + str(containerReplica))
+    if containerReplica:
+        print("Setting replica Replication config in " + str(replication_config_path))
+        template = env.get_template("replication_replica.config.template")
+        with open(replication_config_path, 'w', encoding='utf-8') as f:
+            PRIMARIES_FQDN = os.getenv('HTTP_PRIMARIES_GERRIT_SUBDOMAIN') + "." + os.getenv('HOSTED_ZONE_NAME')
+            f.write(template.render(
+                    GERRIT_PRIMARY_INSTANCE_ID=os.getenv('GERRIT_PRIMARY_INSTANCE_ID'),
+                    HTTP_PRIMARIES_LB="https://" + PRIMARIES_FQDN + "/${name}"
+                    ))
+    else:
+        print("Setting primary Replication config in " + str(replication_config_path))
+        template = env.get_template("replication.config.template")
+        with open(replication_config_path, 'w', encoding='utf-8') as f:
+            REPLICA_FQDN = os.getenv('REPLICA_SUBDOMAIN') + "." + os.getenv('HOSTED_ZONE_NAME')
+            HTTP_REPLICA_FQDN = os.getenv('HTTP_REPLICA_SUBDOMAIN') + "." + os.getenv('HOSTED_ZONE_NAME')
+            REMOTE_TARGET = os.getenv('REMOTE_REPLICATION_TARGET_HOST', '')
+            # In a multi-site setup, the very first replication needs to be
+            # triggered manually from site-A to site-B, once the latter is ready,
+            # thus "REPLICATE_ON_STARTUP" needs to be disabled
+            REPLICATE_ON_STARTUP = "false" if setupMultiSite else "true"
+            f.write(template.render(
+                    REPLICA_1_URL="git://" + REPLICA_FQDN + ":" + os.getenv('GIT_PORT') + "/${name}.git",
+                    REPLICA_1_AMDIN_URL="ssh://gerrit@" + REPLICA_FQDN + ":" + os.getenv('GIT_SSH_PORT') + "/var/gerrit/git/${name}.git",
+                    REPLICA_1_API_URL="https://" + HTTP_REPLICA_FQDN,
+                    REMOTE_TARGET=REMOTE_TARGET,
+                    REMOTE_TARGET_URL="git://" + REMOTE_TARGET + ":" + os.getenv('GIT_PORT') + "/${name}.git",
+                    REMOTE_ADMIN_TARGET_URL="ssh://gerrit@" + REMOTE_TARGET + ":" + os.getenv('GIT_SSH_PORT') + "/var/gerrit/git/${name}.git",
+                    REPLICATE_ON_STARTUP=REPLICATE_ON_STARTUP,
+                    MULTISITE_GLOBAL_PROJECTS=os.getenv('MULTISITE_GLOBAL_PROJECTS', '')
+                    ))
 
 CONFIGURATION_FILE = "jgit.config"
 CONFIGURATION_TARGET = GERRIT_CONFIG_DIRECTORY + CONFIGURATION_FILE