From ef47a743b60013c29001f84caff2d0b7f31870bc Mon Sep 17 00:00:00 2001
From: Andrii Ostapenko <andrii.ostapenko@att.com>
Date: Sat, 11 Jul 2020 21:33:28 -0500
Subject: [PATCH] Add ability to use *-docker-image roles in periodic jobs

Use '{{ zuul.pipeline }}' tag prefix in *-docker-image instead of
'change_{{ zuul.change }}' one when zuul.change is not provided, that is
the case with periodic jobs. This allows to build, upload and promote images
using periodic jobs e.g:

- project:
    periodic:
      - project-buildset-registry

      - project-build-image1:
          dependencies:
            - name: project-buildset-registry
      - project-build-image2:
          dependencies:
            - name: project-buildset-registry

      # pulls from buildset registry and tests both images
      - project-test:
          dependencies:
            - name: project-build-image1
            - name: project-build-image2

      # pre-pulls images from buildset registry for fast build
      - project-upload-image1:
          dependencies:
            - name: project-test
      - project-upload-image2:
          dependencies:
            - name: project-test

      - project-promote:
          dependencies:
            - name: project-upload-image1
            - name: project-upload-image2

This fuctionality will allow to keep latest images up to date for the
case when image incorporates continuously updating code from multiple
repositories.

Using true ternary for tag evaluation because ternary filter requires
all passed to it variables be defined or defaulted [0].

[0] https://github.com/ansible/ansible/issues/51276

Change-Id: I8eb7d2baa24905e7aac51fce0b2f9b1f24f037f9
Signed-off-by: Andrii Ostapenko <andrii.ostapenko@att.com>
---
 roles/build-docker-image/tasks/build.yaml              |  6 +++---
 roles/build-docker-image/tasks/buildx.yaml             | 10 +++++-----
 roles/promote-docker-image/tasks/promote-cleanup.yaml  |  2 +-
 .../tasks/promote-retag-inner.yaml                     |  7 +++++--
 roles/upload-docker-image/tasks/buildx.yaml            |  4 +++-
 roles/upload-docker-image/tasks/push.yaml              |  4 +++-
 .../container/test-build-container-image-release.yaml  |  1 +
 7 files changed, 21 insertions(+), 13 deletions(-)

diff --git a/roles/build-docker-image/tasks/build.yaml b/roles/build-docker-image/tasks/build.yaml
index a02ac21d6..301268041 100644
--- a/roles/build-docker-image/tasks/build.yaml
+++ b/roles/build-docker-image/tasks/build.yaml
@@ -2,6 +2,8 @@
   include_tasks: siblings.yaml
 
 - name: Build a docker image
+  vars:
+    tag_prefix: "{{ ('change_' + zuul.change) if (zuul.change is defined) else zuul.pipeline }}_"
   command: >-
     docker build {{ zj_image.path | default('.') }} -f {{ zj_image.dockerfile | default(docker_dockerfile) }}
     {% if zj_image.target | default(false) -%}
@@ -14,9 +16,7 @@
       --build-arg "ZUUL_SIBLINGS={{ zj_image.siblings | join(' ') }}"
     {% endif -%}
     {% for tag in zj_image.tags | default(['latest']) -%}
-      {% if zuul.change | default(false) -%}
-        --tag {{ docker_registry | ternary(docker_registry + '/', '') }}{{ zj_image.repository }}:change_{{ zuul.change }}_{{ tag }}
-      {% endif -%}
+      --tag {{ docker_registry | ternary(docker_registry + '/', '') }}{{ zj_image.repository }}:{{ tag_prefix }}{{ tag }}
       --tag {{ docker_registry | ternary(docker_registry + '/', '') }}{{ zj_image.repository }}:{{ tag }}
     {% endfor -%}
     {% for label in zj_image.labels | default([]) -%}
diff --git a/roles/build-docker-image/tasks/buildx.yaml b/roles/build-docker-image/tasks/buildx.yaml
index a4ce98bc7..7d8051fb6 100644
--- a/roles/build-docker-image/tasks/buildx.yaml
+++ b/roles/build-docker-image/tasks/buildx.yaml
@@ -5,6 +5,8 @@
 # they can be pulled back onto the host image cache), and also tags
 # them for the buildset registry if one is present.
 - name: Set base docker build command
+  vars:
+    tag_prefix: "change_{{ ('change_' + zuul.change) if (zuul.change is defined) else zuul.pipeline }}_"
   set_fact:
     docker_buildx_command: >-
       docker buildx build {{ zj_image.path | default('.') }} -f {{ zj_image.dockerfile | default(docker_dockerfile) }}
@@ -18,11 +20,9 @@
         --build-arg "ZUUL_SIBLINGS={{ zj_image.siblings | join(' ') }}"
       {% endif -%}
       {% for tag in zj_image.tags | default(['latest']) -%}
-        {% if zuul.change | default(false) -%}
-          --tag {{ temp_registry.host }}:{{ temp_registry.port }}/{{ zj_image.repository }}:change_{{ zuul.change }}_{{ tag }}
-          {% if buildset_registry | default(false) -%}
-            --tag {{ buildset_registry_alias }}:{{ buildset_registry.port }}/{{ zj_image.repository }}:change_{{ zuul.change }}_{{ tag }}
-          {% endif -%}
+        --tag {{ temp_registry.host }}:{{ temp_registry.port }}/{{ zj_image.repository }}:{{ tag_prefix }}{{ tag }}
+        {% if buildset_registry | default(false) -%}
+          --tag {{ buildset_registry_alias }}:{{ buildset_registry.port }}/{{ zj_image.repository }}:{{ tag_prefix }}{{ tag }}
         {% endif -%}
         --tag {{ temp_registry.host }}:{{ temp_registry.port }}/{{ zj_image.repository }}:{{ tag }}
         {% if buildset_registry | default(false) -%}
diff --git a/roles/promote-docker-image/tasks/promote-cleanup.yaml b/roles/promote-docker-image/tasks/promote-cleanup.yaml
index d1fe1b307..ba1470dbd 100644
--- a/roles/promote-docker-image/tasks/promote-cleanup.yaml
+++ b/roles/promote-docker-image/tasks/promote-cleanup.yaml
@@ -9,7 +9,7 @@
   loop_control:
     loop_var: zj_docker_tag
   when:
-    - zj_docker_tag.name.startswith('change_')
+    - zj_docker_tag.name.startswith('change_') or zj_docker_tag.name.startswith(zuul.pipeline))
     # Was updated > 24 hours ago:
     - "{{ ((ansible_date_time.iso8601 | regex_replace('^(....-..-..)T(..:..:..).*Z', '\\1 \\2') | to_datetime) - (zj_docker_tag.last_updated | regex_replace('^(....-..-..)T(..:..:..).*Z', '\\1 \\2') | to_datetime)).seconds > 86400 }}"
   uri:
diff --git a/roles/promote-docker-image/tasks/promote-retag-inner.yaml b/roles/promote-docker-image/tasks/promote-retag-inner.yaml
index 8ddcbaaa4..f3fc4b970 100644
--- a/roles/promote-docker-image/tasks/promote-retag-inner.yaml
+++ b/roles/promote-docker-image/tasks/promote-retag-inner.yaml
@@ -1,7 +1,10 @@
+- name: Set promote_tag_prefix
+  set_fact:
+    promote_tag_prefix: "{{ ('change_' + zuul.change) if (zuul.change is defined) else zuul.pipeline }}"
 - name: Get manifest
   no_log: true
   uri:
-    url: "https://registry.hub.docker.com/v2/{{ zj_image.repository }}/manifests/change_{{ zuul.change }}_{{ zj_image_tag }}"
+    url: "https://registry.hub.docker.com/v2/{{ zj_image.repository }}/manifests/{{ promote_tag_prefix }}_{{ zj_image_tag }}"
     status_code: 200
     headers:
       Accept: "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.docker.distribution.manifest.v2+json"
@@ -21,7 +24,7 @@
 - name: Delete the current change tag
   no_log: true
   uri:
-    url: "https://hub.docker.com/v2/repositories/{{ zj_image.repository }}/tags/change_{{ zuul.change }}_{{ zj_image_tag }}/"
+    url: "https://hub.docker.com/v2/repositories/{{ zj_image.repository }}/tags/{{ promote_tag_prefix }}_{{ zj_image_tag }}/"
     method: DELETE
     status_code: [200, 204]
     headers:
diff --git a/roles/upload-docker-image/tasks/buildx.yaml b/roles/upload-docker-image/tasks/buildx.yaml
index 5141f8edf..ca1e5006e 100644
--- a/roles/upload-docker-image/tasks/buildx.yaml
+++ b/roles/upload-docker-image/tasks/buildx.yaml
@@ -2,6 +2,8 @@
   include_tasks: siblings.yaml
 
 - name: Upload tag to dockerhub
+  vars:
+    promote_tag_prefix: "{{ ('change_' + zuul.change) if (zuul.change is defined) else zuul.pipeline }}_"
   # docker buildx doesn't have a separate push command, only the
   # ability to push at the end of a build. We run build here with
   # all the same arguments as in the buildx build tasks, so they
@@ -18,7 +20,7 @@
       --build-arg "ZUUL_SIBLINGS={{ zj_image.siblings | join(' ') }}"
     {% endif -%}
     {% for tag in zj_image.tags | default(['latest']) -%}
-      --tag {{ docker_registry | ternary(docker_registry + '/', '') }}{{ zj_image.repository }}:{{ upload_docker_image_promote | ternary('change_' + zuul.get('change', '') + '_', '') }}{{ tag }}
+      --tag {{ docker_registry | ternary(docker_registry + '/', '') }}{{ zj_image.repository }}:{{ upload_docker_image_promote | ternary(promote_tag_prefix, '') }}{{ tag }}
     {% endfor -%}
     {% for label in zj_image.labels | default([]) -%}
       --label "{{ label }}"
diff --git a/roles/upload-docker-image/tasks/push.yaml b/roles/upload-docker-image/tasks/push.yaml
index f1d36ca7d..9e005b98e 100644
--- a/roles/upload-docker-image/tasks/push.yaml
+++ b/roles/upload-docker-image/tasks/push.yaml
@@ -1,5 +1,7 @@
 - name: Upload tag to registry
-  command: "docker push {{ docker_registry | ternary(docker_registry + '/', '' ) }}{{ zj_image.repository }}:{{ upload_docker_image_promote | ternary('change_' + zuul.get('change', '') + '_', '') }}{{ zj_image_tag }}"
+  vars:
+    promote_tag_prefix: "{{ ('change_' + zuul.change) if (zuul.change is defined) else zuul.pipeline }}_"
+  command: "docker push {{ docker_registry | ternary(docker_registry + '/', '' ) }}{{ zj_image.repository }}:{{ upload_docker_image_promote | ternary(promote_tag_prefix, '') }}{{ zj_image_tag }}"
   loop: "{{ zj_image.tags | default(['latest']) }}"
   loop_control:
     loop_var: zj_image_tag
diff --git a/test-playbooks/container/test-build-container-image-release.yaml b/test-playbooks/container/test-build-container-image-release.yaml
index eb896eef1..d7ba8ef43 100644
--- a/test-playbooks/container/test-build-container-image-release.yaml
+++ b/test-playbooks/container/test-build-container-image-release.yaml
@@ -20,6 +20,7 @@
     - name: Set simulated zuul variables
       set_fact:
         new_zuul:
+          pipeline: "{{ old_zuul.pipeline }}"
           change_url: "{{ old_zuul.change_url }}"
           executor: "{{ old_zuul.executor }}"
           newrev: c12f3fe1defe8b61d59061363c9c04fb520dae18