Merge "Allow requesting secrets by a different name" into feature/zuulv3
diff --git a/.zuul.yaml b/.zuul.yaml
index e2628cb..9bac031 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -7,7 +7,6 @@
             voting: false
         - tox-pep8
         - tox-py35
-        - tox-tarball
     gate:
       jobs:
         - tox-docs
diff --git a/tests/base.py b/tests/base.py
index 028a8b1..680c2a7 100755
--- a/tests/base.py
+++ b/tests/base.py
@@ -481,6 +481,25 @@
         self.changes[self.change_number] = c
         return c
 
+    def getFakeBranchCreatedEvent(self, project, branch):
+        path = os.path.join(self.upstream_root, project)
+        repo = git.Repo(path)
+        oldrev = 40 * '0'
+
+        event = {
+            "type": "ref-updated",
+            "submitter": {
+                "name": "User Name",
+            },
+            "refUpdate": {
+                "oldRev": oldrev,
+                "newRev": repo.heads[branch].commit.hexsha,
+                "refName": branch,
+                "project": project,
+            }
+        }
+        return event
+
     def review(self, project, changeid, message, action):
         number, ps = changeid.split(',')
         change = self.changes[int(number)]
diff --git a/tests/unit/test_v3.py b/tests/unit/test_v3.py
index adb6bed..e4e71d9 100755
--- a/tests/unit/test_v3.py
+++ b/tests/unit/test_v3.py
@@ -444,6 +444,9 @@
         file_dict = {'.zuul.yaml': in_repo_conf,
                      'playbooks/project-test2.yaml': in_repo_playbook}
         self.create_branch('org/project', 'stable')
+        self.fake_gerrit.addEvent(
+            self.fake_gerrit.getFakeBranchCreatedEvent(
+                'org/project', 'stable'))
         A = self.fake_gerrit.addFakeChange('org/project', 'stable', 'A',
                                            files=file_dict)
         A.addApproval('Code-Review', 2)
@@ -484,6 +487,9 @@
         # it from a different branch on a different repo.
 
         self.create_branch('org/project1', 'stable')
+        self.fake_gerrit.addEvent(
+            self.fake_gerrit.getFakeBranchCreatedEvent(
+                'org/project1', 'stable'))
 
         in_repo_conf = textwrap.dedent(
             """
diff --git a/zuul/ansible/callback/zuul_stream.py b/zuul/ansible/callback/zuul_stream.py
index 34d4406..a9c1155 100644
--- a/zuul/ansible/callback/zuul_stream.py
+++ b/zuul/ansible/callback/zuul_stream.py
@@ -326,7 +326,7 @@
             # We have a custom zuul module that doesn't want the parameters
             # from its returned splatted to stdout. This is typically for
             # modules that are collecting data to be displayed some other way.
-            for key in result_dict.keys():
+            for key in list(result_dict.keys()):
                 if key != 'changed':
                     result_dict.pop(key)
 
diff --git a/zuul/configloader.py b/zuul/configloader.py
index aead0d8..7da54cd 100644
--- a/zuul/configloader.py
+++ b/zuul/configloader.py
@@ -1402,7 +1402,17 @@
                     tenant, config_semaphore)
                 if 'semaphore' not in classes:
                     continue
-                layout.addSemaphore(SemaphoreParser.fromYaml(config_semaphore))
+                semaphore = SemaphoreParser.fromYaml(config_semaphore)
+                old_semaphore = layout.semaphores.get(semaphore.name)
+                if (old_semaphore and
+                    (old_semaphore.source_context.project ==
+                     semaphore.source_context.project)):
+                    # If a semaphore shows up twice in the same
+                    # project, it's probably due to showing up in
+                    # two branches.  Ignore subsequent
+                    # definitions.
+                    continue
+                layout.addSemaphore(semaphore)
 
         for config_template in data.project_templates:
             classes = TenantParser._getLoadClasses(tenant, config_template)
@@ -1494,7 +1504,9 @@
         if trusted:
             branches = ['master']
         else:
-            branches = project.source.getProjectBranches(project, tenant)
+            # Use the cached branch list; since this is a dynamic
+            # reconfiguration there should not be any branch changes.
+            branches = project.unparsed_branch_config.keys()
 
         for branch in branches:
             fns1 = []
diff --git a/zuul/driver/gerrit/gerritconnection.py b/zuul/driver/gerrit/gerritconnection.py
index de72c69..daec0f5 100644
--- a/zuul/driver/gerrit/gerritconnection.py
+++ b/zuul/driver/gerrit/gerritconnection.py
@@ -122,6 +122,17 @@
                              "from Gerrit. Can not get account information." %
                              (event.type,))
 
+        # This checks whether the event created or deleted a branch so
+        # that Zuul may know to perform a reconfiguration on the
+        # project.
+        if event.type == 'ref-updated' and not event.ref.startswith('refs/'):
+            if event.oldrev == '0' * 40:
+                event.branch_created = True
+                event.branch = event.ref
+            if event.newrev == '0' * 40:
+                event.branch_deleted = True
+                event.branch = event.ref
+
         if event.change_number:
             # TODO(jhesketh): Check if the project exists?
             # and self.connection.sched.getProject(event.project_name):
diff --git a/zuul/driver/github/githubconnection.py b/zuul/driver/github/githubconnection.py
index 616e774..2d9dd94 100644
--- a/zuul/driver/github/githubconnection.py
+++ b/zuul/driver/github/githubconnection.py
@@ -163,6 +163,14 @@
             # necessary for the scheduler to match against particular branches
             event.branch = ref_parts[2]
 
+        # This checks whether the event created or deleted a branch so
+        # that Zuul may know to perform a reconfiguration on the
+        # project.
+        if event.oldrev == '0' * 40:
+            event.branch_created = True
+        if event.newrev == '0' * 40:
+            event.branch_deleted = True
+
         return event
 
     def _event_pull_request(self, body):
diff --git a/zuul/executor/server.py b/zuul/executor/server.py
index 45937ef..9340f09 100644
--- a/zuul/executor/server.py
+++ b/zuul/executor/server.py
@@ -1406,17 +1406,13 @@
             callback_path = '%s:%s' % (
                 self.executor_server.callback_dir,
                 os.path.dirname(ara_callbacks.__file__))
-            callback_whitelist = 'zuul_json,ara'
         else:
             callback_path = self.executor_server.callback_dir
-            callback_whitelist = 'zuul_json'
         with open(jobdir_playbook.ansible_config, 'w') as config:
             config.write('[defaults]\n')
             config.write('hostfile = %s\n' % self.jobdir.inventory)
             config.write('local_tmp = %s/local_tmp\n' %
                          self.jobdir.ansible_cache_root)
-            config.write('remote_tmp = %s/remote_tmp\n' %
-                         self.jobdir.ansible_cache_root)
             config.write('retry_files_enabled = False\n')
             config.write('gathering = smart\n')
             config.write('fact_caching = jsonfile\n')
@@ -1427,7 +1423,6 @@
             config.write('command_warnings = False\n')
             config.write('callback_plugins = %s\n' % callback_path)
             config.write('stdout_callback = zuul_stream\n')
-            config.write('callback_whitelist = %s\n' % callback_whitelist)
             # bump the timeout because busy nodes may take more than
             # 10s to respond
             config.write('timeout = 30\n')
diff --git a/zuul/model.py b/zuul/model.py
index 5a157bc..850bbe2 100644
--- a/zuul/model.py
+++ b/zuul/model.py
@@ -1996,6 +1996,8 @@
         # common
         self.type = None
         self.branch_updated = False
+        self.branch_created = False
+        self.branch_deleted = False
         self.ref = None
         # For management events (eg: enqueue / promote)
         self.tenant_name = None
diff --git a/zuul/scheduler.py b/zuul/scheduler.py
index a64d9e0..52b34ec 100644
--- a/zuul/scheduler.py
+++ b/zuul/scheduler.py
@@ -753,12 +753,15 @@
                                    "source %s",
                                    e.change, project.source)
                     continue
-                if (event.branch_updated and
-                    hasattr(change, 'files') and
-                    change.updatesConfig()):
-                    # The change that just landed updates the config.
-                    # Clear out cached data for this project and
-                    # perform a reconfiguration.
+                if ((event.branch_updated and
+                     hasattr(change, 'files') and
+                     change.updatesConfig()) or
+                    event.branch_created or
+                    event.branch_deleted):
+                    # The change that just landed updates the config
+                    # or a branch was just created or deleted.  Clear
+                    # out cached data for this project and perform a
+                    # reconfiguration.
                     change.project.unparsed_config = None
                     self.reconfigureTenant(tenant)
                 for pipeline in tenant.layout.pipelines.values():