From 0d0471b79a4b57bf533ba47791307080431e1aff Mon Sep 17 00:00:00 2001
From: Benjamin Bertrand <benjamin.bertrand@esss.se>
Date: Mon, 18 Mar 2019 10:44:32 +0100
Subject: [PATCH] Fix link to AWX job id for workflow jobs

Add awx_resource field to the task table.
The field is None for local tasks (like excel file creation).
It's the awx resource used to launch the job otherwise.
Currently job or workflow_job.

JIRA INFRA-886 #action In Progress
---
 app/models.py                                 | 12 ++++++-
 app/tasks.py                                  |  3 ++
 app/templates/task/view_task.html             |  2 ++
 ...49_add_awx_resource_field_to_task_table.py | 33 +++++++++++++++++++
 tests/functional/conftest.py                  |  1 +
 tests/functional/test_models.py               | 11 +++++++
 6 files changed, 61 insertions(+), 1 deletion(-)
 create mode 100644 migrations/versions/166572b78449_add_awx_resource_field_to_task_table.py

diff --git a/app/models.py b/app/models.py
index e7d9156..8ad58ba 100644
--- a/app/models.py
+++ b/app/models.py
@@ -294,6 +294,7 @@ class User(db.Model, UserMixin):
         task = Task(
             id=job.id,
             name=name,
+            awx_resource=kwargs.get("resource", None),
             command=job.get_call_string(),
             status=JobStatus(job.status),
             user=self,
@@ -1581,6 +1582,7 @@ class Task(db.Model):
     name = db.Column(db.Text, nullable=False, index=True)
     command = db.Column(db.Text)
     status = db.Column(db.Enum(JobStatus, name="job_status"))
+    awx_resource = db.Column(db.Text)
     awx_job_id = db.Column(db.Integer)
     exception = db.Column(db.Text)
     user_id = db.Column(
@@ -1594,8 +1596,15 @@ class Task(db.Model):
     def awx_job_url(self):
         if self.awx_job_id is None:
             return None
+        if self.awx_resource == "job":
+            route = "jobs/playbook"
+        elif self.awx_resource == "workflow_job":
+            route = "workflows"
+        else:
+            current_app.logger.warning(f"Unknown AWX resource: {self.awx_resource}")
+            return None
         return urllib.parse.urljoin(
-            current_app.config["AWX_URL"], f"/#/jobs/{self.awx_job_id}"
+            current_app.config["AWX_URL"], f"/#/{route}/{self.awx_job_id}"
         )
 
     def __str__(self):
@@ -1608,6 +1617,7 @@ class Task(db.Model):
             "created_at": utils.format_field(self.created_at),
             "ended_at": utils.format_field(self.ended_at),
             "status": self.status.name,
+            "awx_resource": self.awx_resource,
             "awx_job_id": self.awx_job_id,
             "awx_job_url": self.awx_job_url,
             "command": self.command,
diff --git a/app/tasks.py b/app/tasks.py
index 6543c5a..96808d9 100644
--- a/app/tasks.py
+++ b/app/tasks.py
@@ -107,6 +107,7 @@ def trigger_vm_creation(
     )
     task = current_user.launch_task(
         task_name,
+        resource="job",
         func="launch_job_template",
         job_template=job_template,
         extra_vars=extra_vars,
@@ -115,6 +116,7 @@ def trigger_vm_creation(
     if post_job_template and not skip_post_install_job:
         current_user.launch_task(
             "trigger_post_install_job",
+            resource="job",
             func="launch_job_template",
             job_template=post_job_template,
             limit=f"{host.fqdn}",
@@ -144,6 +146,7 @@ def trigger_ztp_configuration(host):
     )
     task = current_user.launch_task(
         "trigger_ztp_configuration",
+        resource="job",
         func="launch_job_template",
         job_template=job_template,
         extra_vars=extra_vars,
diff --git a/app/templates/task/view_task.html b/app/templates/task/view_task.html
index 4a5afbb..bfd1a8f 100644
--- a/app/templates/task/view_task.html
+++ b/app/templates/task/view_task.html
@@ -22,6 +22,8 @@
         <dd class="col-sm-9">{{ task.ended_at }}</dd>
         <dt class="col-sm-3">Status</dt>
         <dd class="col-sm-9">{{ task.status.name }}</dd>
+        <dt class="col-sm-3">AWX resource</dt>
+        <dd class="col-sm-9">{{ task.awx_resource }}</dd>
         <dt class="col-sm-3">AWX job</dt>
         {% if task.awx_job_id %}
         <dd class="col-sm-9"><a href="{{ task.awx_job_url }}">{{ task.awx_job_id }}</a></dd>
diff --git a/migrations/versions/166572b78449_add_awx_resource_field_to_task_table.py b/migrations/versions/166572b78449_add_awx_resource_field_to_task_table.py
new file mode 100644
index 0000000..b0e02bc
--- /dev/null
+++ b/migrations/versions/166572b78449_add_awx_resource_field_to_task_table.py
@@ -0,0 +1,33 @@
+"""Add awx_resource field to Task table
+
+Revision ID: 166572b78449
+Revises: ac04850e5f68
+Create Date: 2019-03-18 08:08:07.399875
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = "166572b78449"
+down_revision = "ac04850e5f68"
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+    op.add_column("task", sa.Column("awx_resource", sa.Text(), nullable=True))
+    # Fill the task awx_resource based on the value from the name column
+    task = sa.sql.table("task", sa.sql.column("name"), sa.sql.column("awx_resource"))
+    # generate_items_excel_file is not an AWX task but a local one (no awx_resource)
+    # others are assumed to be "job"
+    op.execute(
+        task.update()
+        .where(task.c.name != "generate_items_excel_file")
+        .values(awx_resource="job")
+    )
+
+
+def downgrade():
+    op.drop_column("task", "awx_resource")
diff --git a/tests/functional/conftest.py b/tests/functional/conftest.py
index 3c57475..a653fc2 100644
--- a/tests/functional/conftest.py
+++ b/tests/functional/conftest.py
@@ -58,6 +58,7 @@ def app(request):
             "lab.example.org": ["CSEntry Lab"],
             "foo.example.org": ["CSEntry User", "CSEntry Consultant"],
         },
+        "AWX_URL": "https://awx.example.org",
     }
     app = create_app(config=config)
     ctx = app.app_context()
diff --git a/tests/functional/test_models.py b/tests/functional/test_models.py
index b57ed01..ecfd317 100644
--- a/tests/functional/test_models.py
+++ b/tests/functional/test_models.py
@@ -533,3 +533,14 @@ def test_cname_unique_by_domain(db, interface_factory, network_factory, cname_fa
     with pytest.raises(ValidationError) as excinfo:
         cname_factory(name="mycname", interface=interface3)
     assert f"Duplicate cname on the {network1.domain} domain" in str(excinfo.value)
+
+
+def test_task_awx_job_url(db, task_factory):
+    task1 = task_factory(awx_resource="job", awx_job_id=42)
+    assert task1.awx_job_url == "https://awx.example.org/#/jobs/playbook/42"
+    task2 = task_factory(awx_resource="workflow_job", awx_job_id=43)
+    assert task2.awx_job_url == "https://awx.example.org/#/workflows/43"
+    task3 = task_factory(awx_resource="foo", awx_job_id=44)
+    assert task3.awx_job_url is None
+    task4 = task_factory(awx_job_id=45)
+    assert task4.awx_job_url is None
-- 
GitLab