Skip to content
This repository has been archived by the owner on May 15, 2020. It is now read-only.

Commit

Permalink
Merge pull request #38 from kpn/feature/many-to-many-releases-pipelin…
Browse files Browse the repository at this point in the history
…e-runs

BREAK: Releases renamed field and added m2m ref to pipeline runs
  • Loading branch information
mjholtkamp authored Oct 8, 2019
2 parents 233b3b4 + 9e44574 commit 4b6c5c9
Show file tree
Hide file tree
Showing 8 changed files with 85 additions and 56 deletions.
4 changes: 2 additions & 2 deletions katka/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,5 +71,5 @@ class ApplicationMetadataAdmin(WithUsernameAdminModel):

@admin.register(SCMRelease)
class SCMReleaseAdmin(WithUsernameAdminModel):
fields = ('name', 'released', 'from_hash', 'to_hash', 'scm_pipeline_run')
list_display = ('pk', 'scm_pipeline_run', 'name')
fields = ('name', 'status', 'released', 'from_hash', 'to_hash', 'scm_pipeline_runs')
list_display = ('pk', 'name')
8 changes: 8 additions & 0 deletions katka/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,11 @@
)

STEP_FINAL_STATUSES = (STEP_STATUS_SKIPPED, STEP_STATUS_FAILED, STEP_STATUS_SUCCESS)

RELEASE_STATUS_OPEN = 'open'
RELEASE_STATUS_CLOSED = 'closed'

RELEASE_STATUS_CHOICES = (
(RELEASE_STATUS_OPEN, RELEASE_STATUS_OPEN),
(RELEASE_STATUS_CLOSED, RELEASE_STATUS_CLOSED),
)
27 changes: 27 additions & 0 deletions katka/migrations/0020_multiple_pipeline_runs_per_release.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Generated by Django 2.2.6 on 2019-10-08 08:30

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('katka', '0019_scmsteprun_sequence_id'),
]

operations = [
migrations.RemoveField(
model_name='scmrelease',
name='scm_pipeline_run',
),
migrations.AddField(
model_name='scmrelease',
name='scm_pipeline_runs',
field=models.ManyToManyField(to='katka.SCMPipelineRun'),
),
migrations.AddField(
model_name='scmrelease',
name='status',
field=models.CharField(choices=[('open', 'open'), ('closed', 'closed')], default='open', max_length=30),
),
]
7 changes: 4 additions & 3 deletions katka/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
from encrypted_model_fields.fields import EncryptedCharField
from katka.auditedmodel import AuditedModel
from katka.constants import (
PIPELINE_STATUS_CHOICES, PIPELINE_STATUS_INITIALIZING, STEP_STATUS_CHOICES, STEP_STATUS_NOT_STARTED,
PIPELINE_STATUS_CHOICES, PIPELINE_STATUS_INITIALIZING, RELEASE_STATUS_CHOICES, RELEASE_STATUS_OPEN,
STEP_STATUS_CHOICES, STEP_STATUS_NOT_STARTED,
)
from katka.fields import KatkaSlugField

Expand Down Expand Up @@ -147,11 +148,11 @@ class Meta:

public_identifier = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
name = models.CharField(max_length=255)
status = models.CharField(max_length=30, choices=RELEASE_STATUS_CHOICES, default=RELEASE_STATUS_OPEN)
released = models.DateTimeField(null=True)
from_hash = models.CharField(max_length=64)
to_hash = models.CharField(max_length=64)
# points to the pipeline run that created the release, and contains the information on the deployment pipeline
scm_pipeline_run = models.ForeignKey(SCMPipelineRun, on_delete=models.PROTECT)
scm_pipeline_runs = models.ManyToManyField(SCMPipelineRun)


class ApplicationMetadata(AuditedModel):
Expand Down
10 changes: 5 additions & 5 deletions katka/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,24 +108,24 @@ class Meta:


class SCMReleaseSerializer(KatkaSerializer):
scm_pipeline_run = SCMPipelineRunRelatedField(required=False, read_only=True)
scm_pipeline_runs = SCMPipelineRunRelatedField(required=False, read_only=True, many=True)

released = serializers.DateTimeField(required=False)

class Meta:
model = SCMRelease
fields = ('public_identifier', 'name', 'released', 'from_hash', 'to_hash', 'scm_pipeline_run')
read_only_fields = ('from_hash', 'to_hash', 'scm_pipeline_run')
fields = ('public_identifier', 'name', 'released', 'from_hash', 'to_hash', 'scm_pipeline_runs', 'status')
read_only_fields = ('from_hash', 'to_hash', 'scm_pipeline_runs')


class SCMReleaseCreateSerializer(KatkaSerializer):
scm_pipeline_run = SCMPipelineRunRelatedField(required=False)
scm_pipeline_runs = SCMPipelineRunRelatedField(required=False, many=True)

released = serializers.DateTimeField(required=False)

class Meta:
model = SCMRelease
fields = ('public_identifier', 'name', 'released', 'from_hash', 'to_hash', 'scm_pipeline_run')
fields = ('public_identifier', 'name', 'released', 'from_hash', 'to_hash', 'scm_pipeline_runs', 'status')


class ApplicationMetadataSerializer(serializers.ModelSerializer):
Expand Down
14 changes: 4 additions & 10 deletions katka/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
)
from katka.serializers import (
ApplicationMetadataSerializer, ApplicationSerializer, CredentialSecretSerializer, CredentialSerializer,
ProjectSerializer, SCMPipelineRunSerializer, SCMReleaseCreateSerializer, SCMReleaseSerializer,
SCMRepositorySerializer, SCMServiceSerializer, SCMStepRunSerializer, TeamSerializer,
ProjectSerializer, SCMPipelineRunSerializer, SCMReleaseSerializer, SCMRepositorySerializer, SCMServiceSerializer,
SCMStepRunSerializer, TeamSerializer,
)
from katka.viewsets import AuditViewSet, FilterViewMixin, ReadOnlyAuditViewMixin
from rest_framework.permissions import IsAuthenticated
Expand Down Expand Up @@ -98,19 +98,13 @@ def get_queryset(self):
return super().get_queryset().filter(scm_pipeline_run__application__project__team__group__in=user_groups)


class SCMReleaseViewSet(FilterViewMixin, AuditViewSet):
class SCMReleaseViewSet(FilterViewMixin, ReadOnlyAuditViewMixin):
model = SCMRelease
serializer_class = SCMReleaseSerializer

def get_serializer_class(self):
if self.request.method == 'POST':
return SCMReleaseCreateSerializer

return self.serializer_class

def get_queryset(self):
user_groups = self.request.user.groups.all()
return super().get_queryset().filter(scm_pipeline_run__application__project__team__group__in=user_groups)
return super().get_queryset().filter(scm_pipeline_runs__application__project__team__group__in=user_groups)


class ApplicationMetadataViewSet(AuditViewSet):
Expand Down
8 changes: 4 additions & 4 deletions tests/integration/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -380,11 +380,11 @@ def deactivated_scm_step_run(scm_step_run):
def scm_release(scm_pipeline_run):
scm_release = models.SCMRelease(name='Version 0.13.1',
from_hash='577fe3f6a091aa4bad996623b1548b87f4f9c1f8',
to_hash='a49954f060b1b7605e972c9448a74d4067547443',
scm_pipeline_run=scm_pipeline_run)
to_hash='a49954f060b1b7605e972c9448a74d4067547443')

with username_on_model(models.SCMRelease, 'initial'):
scm_release.save()
scm_release.scm_pipeline_runs.set([scm_pipeline_run])

return scm_release

Expand All @@ -402,11 +402,11 @@ def deactivated_scm_release(scm_release):
def another_scm_release(another_scm_pipeline_run):
scm_release = models.SCMRelease(name='Version 15.0',
from_hash='100763d7144e1f993289bd528dc698dd3906a807',
to_hash='38d72050370e6e0b43df649c9630f7135ef6de0d',
scm_pipeline_run=another_scm_pipeline_run)
to_hash='38d72050370e6e0b43df649c9630f7135ef6de0d')

with username_on_model(models.SCMRelease, 'initial'):
scm_release.save()
scm_release.scm_pipeline_runs.set([another_scm_pipeline_run])

return scm_release

Expand Down
63 changes: 31 additions & 32 deletions tests/integration/test_scmrelease_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def test_get(self, client, scm_release):

def test_delete(self, client, scm_release):
response = client.delete(f'/scm-releases/{scm_release.public_identifier}/')
assert response.status_code == 404
assert response.status_code == 405

def test_update(self, client, scm_pipeline_run, scm_release):
url = f'/scm-releases/{scm_release.public_identifier}/'
Expand All @@ -34,22 +34,22 @@ def test_update(self, client, scm_pipeline_run, scm_release):
'to_hash': '0a032e92f77797d9be0ea3ad6c595392313ded72',
'scm_pipeline_run': scm_pipeline_run.public_identifier}
response = client.put(url, data, content_type='application/json')
assert response.status_code == 404
assert response.status_code == 405

def test_partial_update(self, client, scm_release):
url = f'/scm-releases/{scm_release.public_identifier}/'
data = {'name': 'v15.1'}
response = client.patch(url, data, content_type='application/json')
assert response.status_code == 404
assert response.status_code == 405

def test_create(self, client, scm_pipeline_run, scm_release):
url = f'/scm-releases/'
data = {'name': 'Version 1',
'from_hash': '4015B57A143AEC5156FD1444A017A32137A3FD0F',
'to_hash': '0a032e92f77797d9be0ea3ad6c595392313ded72',
'scm_pipeline_run': scm_pipeline_run.public_identifier}
'scm_pipeline_runs': [scm_pipeline_run.public_identifier]}
response = client.post(url, data=data, content_type='application/json')
assert response.status_code == 403
assert response.status_code == 405


@pytest.mark.django_db
Expand All @@ -64,27 +64,31 @@ def test_list(self, client, logged_in_user, scm_pipeline_run, scm_release):
assert parsed[0]['released'] is None
assert parsed[0]['from_hash'] == '577fe3f6a091aa4bad996623b1548b87f4f9c1f8'
assert parsed[0]['to_hash'] == 'a49954f060b1b7605e972c9448a74d4067547443'
assert UUID(parsed[0]['scm_pipeline_run']) == scm_pipeline_run.public_identifier
assert parsed[0]['status'] == 'open'
assert len(parsed[0]['scm_pipeline_runs']) == 1
assert UUID(parsed[0]['scm_pipeline_runs'][0]) == scm_pipeline_run.public_identifier
UUID(parsed[0]['public_identifier']) # should not raise

def test_filtered_list(self, client, logged_in_user, scm_pipeline_run, scm_release, another_scm_pipeline_run,
another_scm_release):

response = client.get('/scm-releases/'
f'?scm_pipeline_run={str(another_scm_pipeline_run.public_identifier)}')
f'?scm_pipeline_runs={str(another_scm_pipeline_run.public_identifier)}')
assert response.status_code == 200
parsed = response.json()
assert len(parsed) == 1
assert parsed[0]['name'] == 'Version 15.0'
assert parsed[0]['released'] is None
assert parsed[0]['from_hash'] == '100763d7144e1f993289bd528dc698dd3906a807'
assert parsed[0]['to_hash'] == '38d72050370e6e0b43df649c9630f7135ef6de0d'
assert UUID(parsed[0]['scm_pipeline_run']) == another_scm_pipeline_run.public_identifier
assert parsed[0]['status'] == 'open'
assert len(parsed[0]['scm_pipeline_runs']) == 1
assert UUID(parsed[0]['scm_pipeline_runs'][0]) == another_scm_pipeline_run.public_identifier

def test_filtered_list_non_existing_pipeline_run(self, client, logged_in_user, scm_pipeline_run, scm_release,
another_scm_pipeline_run, another_scm_release):

response = client.get('/scm-releases/?scm_pipeline_run=12345678-1234-5678-1234-567812345678')
response = client.get('/scm-releases/?scm_pipeline_runs=12345678-1234-5678-1234-567812345678')
assert response.status_code == 200
parsed = response.json()
assert len(parsed) == 0
Expand All @@ -103,17 +107,17 @@ def test_get(self, client, logged_in_user, scm_pipeline_run, scm_release):
assert parsed['released'] is None
assert parsed['from_hash'] == '577fe3f6a091aa4bad996623b1548b87f4f9c1f8'
assert parsed['to_hash'] == 'a49954f060b1b7605e972c9448a74d4067547443'
assert UUID(parsed['scm_pipeline_run']) == scm_pipeline_run.public_identifier
assert parsed['status'] == 'open'
assert len(parsed['scm_pipeline_runs']) == 1
assert UUID(parsed['scm_pipeline_runs'][0]) == scm_pipeline_run.public_identifier

def test_get_excludes_inactive(self, client, logged_in_user, deactivated_scm_release):
response = client.get(f'/scm-releases/{deactivated_scm_release.public_identifier}/')
assert response.status_code == 404

def test_delete(self, client, logged_in_user, scm_release):
response = client.delete(f'/scm-releases/{scm_release.public_identifier}/')
assert response.status_code == 204
p = models.SCMRelease.objects.get(pk=scm_release.public_identifier)
assert p.deleted is True
assert response.status_code == 405

def test_update(self, client, logged_in_user, scm_pipeline_run, scm_release):
url = f'/scm-releases/{scm_release.public_identifier}/'
Expand All @@ -122,9 +126,9 @@ def test_update(self, client, logged_in_user, scm_pipeline_run, scm_release):
'to_hash': '0a032e92f77797d9be0ea3ad6c595392313ded72',
'scm_pipeline_run': scm_pipeline_run.public_identifier}
response = client.put(url, data, content_type='application/json')
assert response.status_code == 200
assert response.status_code == 405
p = models.SCMRelease.objects.get(pk=scm_release.public_identifier)
assert p.name == 'Version 1'
assert p.name == 'Version 0.13.1'

def test_update_cannot_change_hashes(self, client, logged_in_user, scm_pipeline_run, scm_release):
url = f'/scm-releases/{scm_release.public_identifier}/'
Expand All @@ -133,9 +137,8 @@ def test_update_cannot_change_hashes(self, client, logged_in_user, scm_pipeline_
'to_hash': 'AAAAe92f77797d9be0ea3ad6c595392313ded72',
'scm_pipeline_run': scm_pipeline_run.public_identifier}
response = client.put(url, data, content_type='application/json')
assert response.status_code == 200
assert response.status_code == 405
p = models.SCMRelease.objects.get(pk=scm_release.public_identifier)
assert p.name == 'Version hash'
assert p.from_hash != 'BBBBB57A143AEC5156FD1444A017A32137A3FD0F'
assert p.to_hash != 'AAAAe92f77797d9be0ea3ad6c595392313ded72'

Expand All @@ -144,33 +147,29 @@ def test_update_cannot_change_pipeline_run(self, client, logged_in_user, another
data = {'name': 'Version pipeline run', 'released': '2018-06-16 12:34:56',
'from_hash': '577fe3f6a091aa4bad996623b1548b87f4f9c1f8',
'to_hash': 'a49954f060b1b7605e972c9448a74d4067547443',
'scm_pipeline_run': another_scm_pipeline_run.public_identifier}
'scm_pipeline_runs': [another_scm_pipeline_run.public_identifier]}
response = client.put(url, data, content_type='application/json')
assert response.status_code == 200
assert response.status_code == 405
p = models.SCMRelease.objects.get(pk=scm_release.public_identifier)
assert p.name == 'Version pipeline run'
assert p.scm_pipeline_run.public_identifier != another_scm_pipeline_run.public_identifier
assert p.name == 'Version 0.13.1'
pipeline_runs = p.scm_pipeline_runs.all()
assert len(pipeline_runs) == 1
assert pipeline_runs[0].public_identifier != another_scm_pipeline_run.public_identifier

def test_partial_update(self, client, logged_in_user, scm_release):
url = f'/scm-releases/{scm_release.public_identifier}/'
data = {'name': 'Version B2'}
response = client.patch(url, data, content_type='application/json')
assert response.status_code == 200
assert response.status_code == 405
p = models.SCMRelease.objects.get(pk=scm_release.public_identifier)
assert p.name == 'Version B2'
assert p.name == 'Version 0.13.1'

def test_create(self, client, logged_in_user, scm_pipeline_run):
initial_count = models.SCMRelease.objects.count()
url = f'/scm-releases/'
data = {'name': 'Version create',
'from_hash': '4015B57A143AEC5156FD1444A017A32137A3FD0F',
'to_hash': '0a032e92f77797d9be0ea3ad6c595392313ded72',
'scm_pipeline_run': scm_pipeline_run.public_identifier}
'scm_pipeline_runs': f'{scm_pipeline_run.public_identifier},'}
response = client.post(url, data=data, content_type='application/json')
assert response.status_code == 201
assert models.SCMRelease.objects.filter(name='Version create').exists()
p = models.SCMRelease.objects.filter(name='Version create').first()
assert p.from_hash == '4015B57A143AEC5156FD1444A017A32137A3FD0F'
assert p.to_hash == '0a032e92f77797d9be0ea3ad6c595392313ded72'
assert p.scm_pipeline_run.public_identifier == scm_pipeline_run.public_identifier
assert models.SCMRelease.objects.count() == initial_count + 1
assert response.status_code == 405
assert not models.SCMRelease.objects.filter(name='Version create').exists()

0 comments on commit 4b6c5c9

Please sign in to comment.