API: Add an "expired" status for playbooks, plays and tasks

This status can eventually be set from different contexts and for
different reasons but for now the main use case is to timeout
objects that have been in the running status for too long and will
never complete.

Change-Id: I13682a69ff2aa3d3609528583209007aa93ded0d
Related: https://github.com/ansible-community/ara/issues/26
This commit is contained in:
David Moreau Simard 2020-08-31 21:04:41 -04:00
parent dda3e43bc8
commit c3874e54f3
No known key found for this signature in database
GPG Key ID: 7D4729EC4E64E8B7
7 changed files with 73 additions and 4 deletions

View File

@ -0,0 +1,28 @@
# Generated by Django 2.2.16 on 2020-09-17 12:45
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0006_remove_result_statuses'),
]
operations = [
migrations.AlterField(
model_name='play',
name='status',
field=models.CharField(choices=[('unknown', 'unknown'), ('running', 'running'), ('completed', 'completed'), ('expired', 'expired')], default='unknown', max_length=25),
),
migrations.AlterField(
model_name='playbook',
name='status',
field=models.CharField(choices=[('unknown', 'unknown'), ('expired', 'expired'), ('running', 'running'), ('completed', 'completed'), ('failed', 'failed')], default='unknown', max_length=25),
),
migrations.AlterField(
model_name='task',
name='status',
field=models.CharField(choices=[('unknown', 'unknown'), ('running', 'running'), ('completed', 'completed'), ('expired', 'expired')], default='unknown', max_length=25),
),
]

View File

@ -87,7 +87,14 @@ class Playbook(Duration):
RUNNING = "running"
COMPLETED = "completed"
FAILED = "failed"
STATUS = ((UNKNOWN, "unknown"), (RUNNING, "running"), (COMPLETED, "completed"), (FAILED, "failed"))
EXPIRED = "expired"
STATUS = (
(UNKNOWN, "unknown"),
(EXPIRED, "expired"),
(RUNNING, "running"),
(COMPLETED, "completed"),
(FAILED, "failed"),
)
name = models.CharField(max_length=255, null=True)
ansible_version = models.CharField(max_length=255)
@ -167,7 +174,8 @@ class Play(Duration):
UNKNOWN = "unknown"
RUNNING = "running"
COMPLETED = "completed"
STATUS = ((UNKNOWN, "unknown"), (RUNNING, "running"), (COMPLETED, "completed"))
EXPIRED = "expired"
STATUS = ((UNKNOWN, "unknown"), (RUNNING, "running"), (COMPLETED, "completed"), (EXPIRED, "expired"))
name = models.CharField(max_length=255, blank=True, null=True)
uuid = models.UUIDField()
@ -189,7 +197,8 @@ class Task(Duration):
UNKNOWN = "unknown"
RUNNING = "running"
COMPLETED = "completed"
STATUS = ((UNKNOWN, "unknown"), (RUNNING, "running"), (COMPLETED, "completed"))
EXPIRED = "expired"
STATUS = ((UNKNOWN, "unknown"), (RUNNING, "running"), (COMPLETED, "completed"), (EXPIRED, "expired"))
name = models.TextField(blank=True, null=True)
action = models.TextField()

View File

@ -86,6 +86,15 @@ class PlayTestCase(APITestCase):
play_updated = models.Play.objects.get(id=play.id)
self.assertEqual("update", play_updated.name)
def test_expired_play(self):
play = factories.PlayFactory(status="running")
self.assertEqual("running", play.status)
request = self.client.patch("/api/v1/plays/%s" % play.id, {"status": "expired"})
self.assertEqual(200, request.status_code)
play_updated = models.Play.objects.get(id=play.id)
self.assertEqual("expired", play_updated.status)
def test_get_play(self):
play = factories.PlayFactory()
request = self.client.get("/api/v1/plays/%s" % play.id)

View File

@ -111,6 +111,15 @@ class PlaybookTestCase(APITestCase):
playbook_updated = models.Playbook.objects.get(id=playbook.id)
self.assertNotEqual("wrong", playbook_updated.status)
def test_expired_playbook(self):
playbook = factories.PlaybookFactory(status="running")
self.assertEqual("running", playbook.status)
request = self.client.patch("/api/v1/playbooks/%s" % playbook.id, {"status": "expired"})
self.assertEqual(200, request.status_code)
playbook_updated = models.Playbook.objects.get(id=playbook.id)
self.assertEqual("expired", playbook_updated.status)
def test_get_playbook(self):
playbook = factories.PlaybookFactory()
request = self.client.get("/api/v1/playbooks/%s" % playbook.id)

View File

@ -122,6 +122,15 @@ class TaskTestCase(APITestCase):
task_updated = models.Task.objects.get(id=task.id)
self.assertEqual("update", task_updated.name)
def test_expired_task(self):
task = factories.TaskFactory(status="running")
self.assertEqual("running", task.status)
request = self.client.patch("/api/v1/tasks/%s" % task.id, {"status": "expired"})
self.assertEqual(200, request.status_code)
task_updated = models.Task.objects.get(id=task.id)
self.assertEqual("expired", task_updated.status)
def test_get_task(self):
task = factories.TaskFactory()
request = self.client.get("/api/v1/tasks/%s" % task.id)

View File

@ -10,6 +10,10 @@
<div class="pf-c-alert__icon" title="Playbook is either in progress or was interrupted: data might be inconsistent">
<i class="fas fa-info-circle" aria-hidden="true"></i>
</div>
{% elif status == "expired" %}
<div class="pf-c-alert__icon" title="Playbook was running but never finished, it has expired: data might be inconsistent">
<i class="fas fa-hourglass-half" aria-hidden="true"></i>
</div>
{% else %}
<div class="pf-c-alert__icon" title="Playbook status is unknown">
<i class="fas fa-question-circle" aria-hidden="true"></i>

View File

@ -86,9 +86,10 @@ Examples:
# Return which playbooks would be deleted by ommitting --confirm
ara playbook prune
# Different retention for successful and unsuccessful playbooks
# Different retention for successful, unsuccessful and expired playbooks
ara playbook prune --status ok --days 30 --confirm
ara playbook prune --status failed --days 90 --confirm
ara playbook prune --status expired --days 3 --confirm
# Different retention based on labels
ara playbook prune --label dev --days 7 --confirm