TT#17622 build_docker

* provide info of actual images and tags
* refresh docker images/tag via release_dashboard
* keep that info in db
* fix Dockerfile documentation
* cleanup thanks to flake8

Change-Id: I743482da9b4d50f7b1832cad324882f3a49cbcf0
changes/94/13994/10
Victor Seva 9 years ago
parent 01411e6ea1
commit 731dd69df0

1
debian/server.ini vendored

@ -2,6 +2,7 @@
JENKINS_URL=fake
GERRIT_URL=fake
BROKER_URL=fake
DOCKER_REGISTRY_URL=fake
DB_NAME=fake
DB_USER=fake
DB_PWD=fake

@ -13,7 +13,7 @@
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
from django.test import TestCase, override_settings
from django.test import override_settings
from mock import patch, call, mock_open
from hotfix import tasks, utils, models
from repoapi.models import JenkinsBuildInfo

@ -17,6 +17,8 @@ from django.contrib import admin
from . import models
@admin.register(models.DockerTag)
@admin.register(models.DockerImage)
@admin.register(models.Project)
class ProjectAdmin(admin.ModelAdmin):
pass

File diff suppressed because one or more lines are too long

@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9 on 2017-06-24 23:00
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
import django_extensions.db.fields.json
class Migration(migrations.Migration):
dependencies = [
('release_dashboard', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='DockerImage',
fields=[
('id', models.AutoField(auto_created=True,
primary_key=True,
serialize=False,
verbose_name='ID')),
('name', models.CharField(max_length=50, unique=True)),
('project', models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to='release_dashboard.Project')),
],
),
migrations.CreateModel(
name='DockerTag',
fields=[
('id', models.AutoField(auto_created=True,
primary_key=True,
serialize=False,
verbose_name='ID')),
('name', models.CharField(max_length=50)),
('manifests', django_extensions.db.fields.json.JSONField()),
('image', models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to='release_dashboard.DockerImage')),
],
),
migrations.AlterUniqueTogether(
name='dockertag',
unique_together=set([('name', 'image')]),
),
]

@ -15,6 +15,7 @@
import logging
import re
import json
from datetime import datetime
from django.db import models
from django_extensions.db.fields.json import JSONField
from django_extensions.db.fields import ModificationDateTimeField
@ -77,8 +78,61 @@ class Project(models.Model):
return Project._filter_values(self.json_branches,
'^refs/heads/(.+)$', regex)
def filter_docker_images(self, images):
r = re.compile(self.name)
return filter(r.search, images)
def branches_mrXX(self):
return self.filter_branches(r'^mr[0-9]+\.[0-9]+$')
def branches_mrXXX(self):
return self.filter_branches(r'^mr[0-9]+\.[0-9]+\.[0-9]+$')
class DockerImageManager(models.Manager):
def images_with_tags(self):
qs = self.get_queryset().filter(dockertag__isnull=False)
return qs.distinct()
class DockerImage(models.Model):
name = models.CharField(max_length=50, unique=True, null=False)
project = models.ForeignKey(Project, on_delete=models.CASCADE)
objects = DockerImageManager()
def __str__(self):
return "%s[%s]" % (self.name, self.project.name)
@property
def tags(self):
res = self.dockertag_set.all().values_list('name', flat=True)
return res
class DockerTag(models.Model):
name = models.CharField(max_length=50, null=False)
manifests = JSONField(null=False)
image = models.ForeignKey(DockerImage, on_delete=models.CASCADE)
class Meta:
unique_together = (("name", "image"),)
def __str__(self):
return "%s:%s" % (self.image.name, self.name)
@property
def date(self):
if self.manifests is None:
return None
try:
value = self.manifests['history'][-1]['v1Compatibility']
time = json.loads(value)
created = time['created'].split('.')
return datetime.strptime(
created[0],
'%Y-%m-%dT%H:%M:%S')
except Exception as e:
logger.error(e)
return None

@ -0,0 +1,32 @@
# Copyright (C) 2017 The Sipwise Team - http://sipwise.com
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
from rest_framework import serializers
from . import models
class ProjectSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = models.Project
fields = '__all__'
class DockerImageSerializer(serializers.HyperlinkedModelSerializer):
project = serializers.StringRelatedField()
class Meta:
model = models.DockerImage
fields = '__all__'

@ -0,0 +1,66 @@
/**
* refresh info
*/
$('button#refresh_all').click(function(e){
// don't send the form
e.preventDefault();
var button = $(this);
var span = $('span#refresh_all_error');
$.ajax({
url: '/release_panel/docker/refresh/',
type: 'POST',
success: successFunc,
error: errorFunc,
beforeSend: csrftokenFunc
});
function successFunc(data, status) {
span.html('');
span.append('<a href="' + data.url + '">Done</a><br/>');
span.append('This will take a while. Refresh the page in a few');
}
function errorFunc(jqXHR, status, error) {
span.html(error);
button.removeAttr("disabled");
}
//deactivate button
button.attr("disabled", "disabled");
span.html('processing');
span.show();
});
$('button.refresh').click(function(e){
// don't send the form
e.preventDefault();
var button = $(this);
var project = button.attr('id').replace('refresh_','');
var span = $('span#refresh_error_' + project );
function successFunc(data, status) {
span.html('');
span.append('<a href="' + data.url + '">Done</a>');
button.removeAttr("disabled");
}
function errorFunc(jqXHR, status, error) {
span.html(error);
button.removeAttr("disabled");
}
$.ajax({
url: '/release_panel/docker/refresh/' + project + '/',
type: 'POST',
success: successFunc,
error: errorFunc,
beforeSend: csrftokenFunc
});
//deactivate button
button.attr("disabled", "disabled");
span.html('processing');
span.show();
});

@ -16,9 +16,10 @@ from __future__ import absolute_import
import logging
from celery import shared_task
from release_dashboard.models import Project
from release_dashboard.models import Project, DockerImage, DockerTag
from django.conf import settings
from .utils import get_gerrit_tags, get_gerrit_branches
from .utils import build
from .utils import docker
logger = logging.getLogger(__name__)
rd_settings = settings.RELEASE_DASHBOARD_SETTINGS
@ -27,12 +28,37 @@ rd_settings = settings.RELEASE_DASHBOARD_SETTINGS
@shared_task(ignore_result=True)
def gerrit_fetch_info(projectname):
project, _ = Project.objects.get_or_create(name=projectname)
project.tags = get_gerrit_tags(projectname)
project.branches = get_gerrit_branches(projectname)
project.tags = build.get_gerrit_tags(projectname)
project.branches = build.get_gerrit_branches(projectname)
project.save()
@shared_task(ignore_result=True)
def gerrit_fetch_all():
for project in rd_settings['projects']:
gerrit_fetch_info.delay(project)
for project in rd_settings['docker_projects']:
build.gerrit_fetch_info.delay(project)
@shared_task(ignore_result=True)
def docker_fetch_info(imagename):
image = DockerImage.objects.get(name=imagename)
tags = docker.get_docker_tags(imagename)
for tagname in tags:
DockerTag.objects.create(
name=tagname,
image=image,
manifests=docker.get_docker_manifests(image.name, tagname))
@shared_task(ignore_result=True)
def docker_fetch_all():
DockerImage.objects.all().delete()
images = docker.get_docker_repositories()
logger.debug("images: %s" % images)
for projectname in rd_settings['docker_projects']:
project, _ = Project.objects.get_or_create(name=projectname)
for imagename in project.filter_docker_images(images):
image = DockerImage.objects.create(name=imagename,
project=project)
logger.debug("%s created" % image)
docker_fetch_info.delay(image.name)

@ -20,7 +20,6 @@
</select>
</div>
{% endif %}
{% if not docker %}
<div class="form-group">
<label for="version_release">Release version</label>
<input class="form-control" id="version_release"
@ -36,7 +35,6 @@
{% endfor %}
</select>
</div>
{% endif %}
<button id="main" type="submit" class="btn btn-default">Submit</button>
</div>
<div id="select_text_info" class="panel-footer"></div>

@ -6,7 +6,7 @@
<li><a href="{% url 'release_dashboard:build_docker_images'%}">Build Docker Images</a></li>
{% endblock %}
{% block content %}
{% include "release_dashboard/build_content.html" %}
{% include "release_dashboard/build_docker_content.html" %}
{% endblock %}
{% block extrajs %}
<script src="{% static "release_dashboard/js/build.js" %}"></script>

@ -0,0 +1,56 @@
<div class="container">
<form method="POST" class="form-inline">{% csrf_token %}
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Actions</h3>
</div>
<div class="panel-body">
<div class="form-group">
<label for="common_select">Common selection</label>
<select class="form-control" id="common_select"
name="common_select">
<option value="ignore">ignore</option>
{% for v in common_versions.branches %}
<option value="branch/{{ v }}">branch/{{ v }}</option>
{% endfor %}
</select>
</div>
<button id="main" type="submit" class="btn btn-default">Submit</button>
</div>
<div id="select_text_info" class="panel-footer"></div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Data</h3>
</div>
<div class="panel-body">
<table class="table table-condensed">
<thead>
<tr>
<th class="col-md-2">Project</th>
<th class="col-md-2">Version</th>
</tr>
</thead>
<tbody>
{% for p in projects %}
<tr class="repo">
<th class="col-md-2">
<label for="version_{{ p.name }}">{{ p.name }}</label>
</th>
<td class="version col-md-2">
<select class="form-control select-version" id="version_{{ p.name }}" name="version_{{ p.name }}"
project="{{ p.name }}">
<option value="ignore">ignore</option>
{% for b in p.branches %}
<option value="branch/{{ b }}">branch/{{ b }}</option>
{% endfor %}
</select>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</form>
</div>

@ -0,0 +1,43 @@
<div class="container">
<form method="POST" class="form-inline">{% csrf_token %}
<div class="panel panel-default">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Data</h3>
</div>
<div class="panel-body">
<table class="table table-condensed">
<thead>
<tr>
<th>Image</th>
<th>Project</th>
<th>Tags</th>
</tr>
</thead>
<tbody>
{% for i in images %}
<tr>
<th><label>{{ i.name }}</label></th>
<td>{{ i.project.name }}</td>
<td>
{% for tag in i.dockertag_set.all %}
<a href="{{URL_BASE}}{{i.name}}/manifests/{{tag.name}}">
{% if tag.name|length > 10 %}
<span class="label label-warning"
{% else %}
<span class="label label-success"
{% endif %}
data-toggle="tooltip" title="{{tag.date|date:"DATETIME_FORMAT"}}">
{{ tag.name|truncatechars:10 }}
</span>
</a>
{% endfor %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</form>
</div>

@ -0,0 +1,10 @@
{% extends "release_dashboard/base.html" %}
{% load staticfiles %}
{% block title %}Build docker images per project{% endblock %}
{% block navlist %}
<li><a href="{% url 'release_dashboard:index'%}">Release Dashboard</a></li>
<li><a href="{% url 'release_dashboard:docker_images'%}">Docker Images</a></li>
{% endblock %}
{% block content %}
{% include "release_dashboard/docker_content.html" %}
{% endblock %}

@ -39,10 +39,18 @@
<a href="{% url 'release_dashboard:refresh_all'%}">
Refresh tag/branch Info</a>
</li>
<li class="list-group-item">
<a href="{% url 'release_dashboard:docker_images'%}">
Docker image/tag Info</a>
</li>
<li class="list-group-item">
<a href="{% url 'release_dashboard:build_docker_images'%}">
Build project docker images</a>
</li>
<li class="list-group-item">
<a href="{% url 'release_dashboard:refresh_docker_all'%}">
Refresh Docker image/tag Info</a>
</li>
</ul>
</div>
</div>

@ -1,51 +1,12 @@
{% extends "release_dashboard/base.html" %}
{% load staticfiles %}
{% block title %}Hotfixes{% endblock %}
{% block title %}Refresh GIT{% endblock %}
{% block navlist %}
<li><a href="{% url 'release_dashboard:index'%}">Release Dashboard</a></li>
<li><a href="{% url 'release_dashboard:refresh_all'%}">Refresh Info</a></li>
<li><a href="{% url 'release_dashboard:refresh_all'%}">Refresh GIT Info</a></li>
{% endblock %}
{% block content %}
<div class="container">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Actions</h3>
</div>
<div class="panel-body">
<form class="form-inline">{% csrf_token %}
<div class="form-group">
<button type="button" class="btn btn-danger" id="refresh_all">Refresh All</button>
<span style="display:none" id="refresh_all_error"></span>
</div>
</form>
</div>
</div>
<div>
<table class="table table-condensed">
<thead>
<tr>
<th>Project</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<form class="form-inline">{% csrf_token %}</form>
{% for p in projects %}
<tr class="repo">
<th><label for="version_{{ p.name }}">{{ p.name }}</label></th>
<td>
<button type="button" class="btn btn-info refresh" id="refresh_{{ p.name }}" class="refresh">Refresh</button>
</td>
<td>
<span style="display:none" class="text-danger" id="refresh_error_{{ p.name }}"></span>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% include "release_dashboard/refresh_content.html" %}
{% endblock %}
{% block extrajs %}
<script src="{% static "release_dashboard/js/csrf.js" %}"></script>

@ -0,0 +1,40 @@
<div class="container">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Actions</h3>
</div>
<div class="panel-body">
<form class="form-inline">{% csrf_token %}
<div class="form-group">
<button type="button" class="btn btn-danger" id="refresh_all">Refresh All</button>
<span style="display:none" id="refresh_all_error"></span>
</div>
</form>
</div>
</div>
<div>
<table class="table table-condensed">
<thead>
<tr>
<th>Project</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<form class="form-inline">{% csrf_token %}</form>
{% for p in projects %}
<tr class="repo">
<th><label for="version_{{ p.name }}">{{ p.name }}</label></th>
<td>
<button type="button" class="btn btn-info refresh" id="refresh_{{ p.name }}" class="refresh">Refresh</button>
</td>
<td>
<span style="display:none" class="text-danger" id="refresh_error_{{ p.name }}"></span>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>

@ -0,0 +1,14 @@
{% extends "release_dashboard/base.html" %}
{% load staticfiles %}
{% block title %}Refresh Docker{% endblock %}
{% block navlist %}
<li><a href="{% url 'release_dashboard:index'%}">Release Dashboard</a></li>
<li><a href="{% url 'release_dashboard:refresh_docker_all'%}">Refresh Docker Info</a></li>
{% endblock %}
{% block content %}
{% include "release_dashboard/refresh_content.html" %}
{% endblock %}
{% block extrajs %}
<script src="{% static "release_dashboard/js/csrf.js" %}"></script>
<script src="{% static "release_dashboard/js/refresh_docker_info.js" %}"></script>
{% endblock %}

@ -0,0 +1,93 @@
# Copyright (C) 2017 The Sipwise Team - http://sipwise.com
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
# You should have received a copy of the GNU General Public License along
# with this prograproj. If not, see <http://www.gnu.org/licenses/>.
from django.test import TestCase
from release_dashboard.models import Project, DockerImage, DockerTag
import datetime
class DockerImageTestCase(TestCase):
def setUp(self):
self.proj = Project.objects.create(name="fake")
def test_create(self):
image = DockerImage.objects.create(
name='fake-jessie', project=self.proj)
self.assertItemsEqual(self.proj.dockerimage_set.all(),
[image, ])
def test_remove_image(self):
image = DockerImage.objects.create(
name='fake-jessie', project=self.proj)
self.assertItemsEqual(self.proj.dockerimage_set.all(),
[image, ])
image.delete()
self.assertTrue(Project.objects.filter(name="fake").exists())
def test_remove_project(self):
image = DockerImage.objects.create(
name='fake-jessie', project=self.proj)
self.assertItemsEqual(self.proj.dockerimage_set.all(), [image, ])
self.proj.delete()
self.assertFalse(Project.objects.filter(name="fake").exists())
self.assertFalse(DockerImage.objects.filter(name="fake").exists())
def test_filter_images(self):
images = ['fake-jessie', 'other', 'ngcp-fake', 'fake-more']
images_ok = ['fake-jessie', 'ngcp-fake', 'fake-more']
self.assertItemsEqual(
self.proj.filter_docker_images(images), images_ok)
def test_image_tags(self):
image = DockerImage.objects.create(
name='fake-jessie', project=self.proj)
self.assertItemsEqual(image.tags, [])
DockerTag.objects.create(
name='latest',
image=image,
manifests='{}')
self.assertItemsEqual(image.tags, ['latest', ])
DockerTag.objects.create(
name='mr5.4',
image=image,
manifests='{}')
self.assertItemsEqual(image.tags, ['latest', 'mr5.4'])
class DockerImageTest2Case(TestCase):
fixtures = ['test_model_fixtures', ]
def setUp(self):
self.images_with_tags = [
DockerImage.objects.get(name='data-hal-jessie'),
DockerImage.objects.get(name='documentation-jessie'),
DockerImage.objects.get(name='ngcp-panel-selenium'),
DockerImage.objects.get(name='ngcp-panel-tests-rest-api-jessie'),
DockerImage.objects.get(name='ngcp-panel-tests-selenium-jessie'),
]
def test_images_with_tags(self):
self.assertItemsEqual(
DockerImage.objects.images_with_tags(),
self.images_with_tags)
def test_date(self):
tag = DockerTag.objects.get(
name='latest',
image__name='ngcp-panel-tests-selenium-jessie')
self.assertEqual(
tag.date,
datetime.datetime(2016, 11, 07, 20, 30, 25))

@ -0,0 +1,100 @@
# Copyright (C) 2017 The Sipwise Team - http://sipwise.com
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
# You should have received a copy of the GNU General Public License along
# with this prograproj. If not, see <http://www.gnu.org/licenses/>.
from django.test import TestCase, override_settings
from release_dashboard import tasks
from release_dashboard.models import Project, DockerImage
from mock import patch, call
DOCKER_REST_CATALOG = """
{
"repositories":[
"data-hal-jessie",
"data-hal-selenium-jessie",
"other",
"one"]
}
"""
DOCKER_REST_FAKE_TAGS = {
'data-hal-jessie': """{
"name": "data-hal-jessie",
"tags":[
"I3a899",
"latest"]
}""",
'data-hal-selenium-jessie': """{
"name":"data-hal-selenium-jessie",
"tags":["If53a9","latest"]
}"""
}
def fake_tag(url):
if url == "data-hal-jessie/tags/list":
return DOCKER_REST_FAKE_TAGS['data-hal-jessie']
elif url == "_catalog":
return DOCKER_REST_CATALOG
elif url == "data-hal-selenium-jessie/tags/list":
return DOCKER_REST_FAKE_TAGS['data-hal-selenium-jessie']
else:
return "{}"
@override_settings(CELERY_EAGER_PROPAGATES_EXCEPTIONS=True)
@override_settings(DOCKER_REGISTRY_URL='{}')
@override_settings(DEBUG=False)
class TasksDockerTestCase(TestCase):
@patch('release_dashboard.utils.docker.get_docker_info',
side_effect=fake_tag)
def test_docker_fetch_info(self, gdi):
proj = Project.objects.create(name="data-hal")
self.assertEquals(proj.name, "data-hal")
image = DockerImage.objects.create(
name='data-hal-jessie', project=proj)
self.assertItemsEqual(proj.dockerimage_set.all(), [image, ])
result = tasks.docker_fetch_info.delay('data-hal-jessie')
self.assertTrue(result.successful())
image = DockerImage.objects.get(name='data-hal-jessie')
calls = [
call("data-hal-jessie/tags/list"),
call("data-hal-jessie/manifests/I3a899"),
call("data-hal-jessie/manifests/latest"),
]
gdi.assert_has_calls(calls)
self.assertItemsEqual(image.tags, ["I3a899", "latest"])
@patch('release_dashboard.utils.docker.get_docker_info',
side_effect=fake_tag)
def test_docker_fetch_all(self, gdi):
result = tasks.docker_fetch_all.delay()
self.assertTrue(result.successful())
proj = Project.objects.get(name="data-hal")
images = [DockerImage.objects.get(name='data-hal-jessie'),
DockerImage.objects.get(name='data-hal-selenium-jessie')]
self.assertItemsEqual(proj.dockerimage_set.all(), images)
self.assertItemsEqual(images[0].tags, ["I3a899", "latest"])
self.assertItemsEqual(images[1].tags, ["If53a9", "latest"])
calls = [
call("_catalog"),
call("data-hal-jessie/tags/list"),
call("data-hal-jessie/manifests/I3a899"),
call("data-hal-jessie/manifests/latest"),
call("data-hal-selenium-jessie/tags/list"),
call("data-hal-selenium-jessie/manifests/If53a9"),
call("data-hal-selenium-jessie/manifests/latest"),
]
gdi.assert_has_calls(calls)

@ -0,0 +1,83 @@
# Copyright (C) 2017 The Sipwise Team - http://sipwise.com
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
# You should have received a copy of the GNU General Public License along
# with this prograproj. If not, see <http://www.gnu.org/licenses/>.
from django.test import TestCase
from django.test import override_settings
from release_dashboard.utils import docker
from mock import patch, call
DOCKER_REST_CATALOG = """
{
"repositories":[
"fake-jessie",
"fake-selenium-jessie",
"other",
"one"]
}
"""
DOCKER_REST_FAKE_TAGS = {
'fake-jessie': """{
"name": "fake-jessie",
"tags":[
"I3a899b8945688c2ef3a4be6ba6c4c1d4cbf6d548",
"latest"]
}""",
'other': """{"name": "other", "tags":[]}""",
}
def fake_tag(url):
if url == "fake-jessie/tags/list":
return DOCKER_REST_FAKE_TAGS['fake-jessie']
elif url == "other/tags/list":
return DOCKER_REST_FAKE_TAGS['other']
@override_settings(DOCKER_REGISTRY_URL='{}')
@override_settings(DEBUG=False)
class UtilsDockerTestCase(TestCase):
@patch('release_dashboard.utils.docker.get_docker_info')
def test_get_docker_repositories(self, gdi):
gdi.return_value = DOCKER_REST_CATALOG
self.assertItemsEqual(
docker.get_docker_repositories(),
['fake-jessie',
'fake-selenium-jessie',
'other',
'one']
)
@patch('release_dashboard.utils.docker.get_docker_info',
side_effect=fake_tag)
def test_get_docker_tags(self, gdi):
self.assertItemsEqual(
docker.get_docker_tags('fake-jessie'),
["I3a899b8945688c2ef3a4be6ba6c4c1d4cbf6d548",
"latest"])
calls = [
call("fake-jessie/tags/list"),
]
gdi.assert_has_calls(calls)
@patch('release_dashboard.utils.docker.get_docker_info',
side_effect=fake_tag)
def test_get_docker_tags_empty(self, gdi):
self.assertItemsEqual(docker.get_docker_tags('other'), [])
calls = [
call("other/tags/list"),
]
gdi.assert_has_calls(calls)

@ -14,23 +14,29 @@
# with this program. If not, see <http://www.gnu.org/licenses/>.
from django.conf.urls import url
from . import views
from views import build, docker
urlpatterns = [
url(r'^$', views.index, name='index'),
url(r'^build_deps/$', views.build_deps, name='build_deps'),
url(r'^build/$', views.build_release, name='build_release'),
url(r'^build_trunk_deps/$', views.build_trunk_deps,
url(r'^$', build.index, name='index'),
url(r'^build_deps/$', build.build_deps, name='build_deps'),
url(r'^build/$', build.build_release, name='build_release'),
url(r'^build_trunk_deps/$', build.build_trunk_deps,
name='build_trunk_deps'),
url(r'^build_trunk/$', views.build_trunk_release,
url(r'^build_trunk/$', build.build_trunk_release,
name='build_trunk_release'),
url(r'^build_tag/$', views.build_release,
url(r'^build_tag/$', build.build_release,
{'tag_only': True}, name='build_release_tag'),
url(r'^hotfix/$', views.hotfix, name='hotfix'),
url(r'^hotfix/$', build.hotfix, name='hotfix'),
url(r'^hotfix/(?P<branch>[^/]+)/(?P<project>[^/]+)/$',
views.hotfix_build),
url(r'^refresh/$', views.refresh_all, name='refresh_all'),
url(r'^refresh/(?P<project>[^/]+)/$', views.refresh, name='refresh'),
url(r'^build_docker/$', views.build_docker_images,
build.hotfix_build),
url(r'^refresh/$', build.refresh_all, name='refresh_all'),
url(r'^refresh/(?P<project>[^/]+)/$', build.refresh, name='refresh'),
url(r'^build_docker/$', docker.build_docker_images,
name='build_docker_images'),
url(r'^docker/refresh/$', docker.refresh_all,
name='refresh_docker_all'),
url(r'^docker/refresh/(?P<project>[^/]+)/$', docker.refresh,
name='refresh_docker'),
url(r'^docker/$', docker.docker_images,
name='docker_images'),
]

@ -0,0 +1,26 @@
# Copyright (C) 2017 The Sipwise Team - http://sipwise.com
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
from release_dashboard.models import Project
def get_tags(projectname, regex=None):
project, _ = Project.objects.get_or_create(name=projectname)
return project.filter_tags(regex)
def get_branches(projectname, regex=None):
project, _ = Project.objects.get_or_create(name=projectname)
return project.filter_branches(regex)

@ -20,7 +20,6 @@ import requests
from requests.auth import HTTPDigestAuth
from django.conf import settings
from repoapi.utils import openurl
from release_dashboard.models import Project
logger = logging.getLogger(__name__)
@ -34,9 +33,6 @@ hotfix_url = ("{base}/job/release-tools-runner/buildWithParameters?"
"PROJECTNAME={project}&repository={project}&"
"push={push}&uuid={uuid}")
docker_url = ("{base}/job/build-project-docker/buildWithParameters?"
"token={token}&project={project}&branch={branch}")
def get_response(url):
auth = HTTPDigestAuth(
@ -103,27 +99,6 @@ def trigger_build(project, trigger_release=None,
return "{base}/job/{job}/".format(**params)
def trigger_docker_build(project, branch):
if branch == "ignore":
logger.debug("ignoring request to trigger project %s due"
" to request of version 'ignore'", project)
return
branch = branch.split("branch/")[1]
params = {
'base': settings.JENKINS_URL,
'token': urllib.quote(settings.JENKINS_TOKEN),
'project': project,
'branch': urllib.quote(branch),
}
url = docker_url.format(**params)
if settings.DEBUG:
logger.debug("Debug mode, would trigger: %s", url)
else:
openurl(url)
return "{base}/job/build-project-docker/".format(**params)
def get_gerrit_info(url):
if settings.DEBUG:
logger.debug("Debug mode, would trigger: %s", url)
@ -134,16 +109,6 @@ def get_gerrit_info(url):
return response.text
def get_tags(projectname, regex=None):
project, _ = Project.objects.get_or_create(name=projectname)
return project.filter_tags(regex)
def get_branches(projectname, regex=None):
project, _ = Project.objects.get_or_create(name=projectname)
return project.filter_branches(regex)
def get_gerrit_tags(project, regex=None):
url = settings.GERRIT_URL.format("a/projects/%s/tags/" % project)
return get_gerrit_info(url)

@ -0,0 +1,108 @@
# Copyright (C) 2017 The Sipwise Team - http://sipwise.com
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
import logging
import urllib
import requests
import json
from django.conf import settings
from repoapi.utils import openurl
logger = logging.getLogger(__name__)
docker_url = ("{base}/job/build-project-docker/buildWithParameters?"
"token={token}&project={project}&branch={branch}")
def trigger_docker_build(project, branch):
if branch == "ignore":
logger.debug("ignoring request to trigger project %s due"
" to request of version 'ignore'", project)
return
branch = branch.split("branch/")[1]
params = {
'base': settings.JENKINS_URL,
'token': urllib.quote(settings.JENKINS_TOKEN),
'project': project,
'branch': urllib.quote(branch),
}
url = docker_url.format(**params)
if settings.DEBUG:
logger.debug("Debug mode, would trigger: %s", url)
else:
openurl(url)
return "{base}/job/build-project-docker/".format(**params)
def get_docker_info(url):
if settings.DEBUG:
logger.debug("Debug mode, would trigger: %s", url)
else:
logger.debug("trigger: %s", url)
response = requests.get(url)
logger.debug("response: %s" % response)
response.raise_for_status()
return response.text
def get_docker_repositories():
if settings.DEBUG:
result = json.loads(settings.DOCKER_REGISTRY)
return result['repositories']
else:
url = settings.DOCKER_REGISTRY_URL.format("_catalog")
try:
info = get_docker_info(url)
logger.debug("response: %s" % info)
result = json.loads(info)
return result['repositories']
except Exception as e:
logger.error(e)
return []
def get_docker_tags(image):
if settings.DEBUG:
try:
return settings.DOCKER_IMAGES[image]
except Exception as e:
return []
else:
url = settings.DOCKER_REGISTRY_URL.format("%s/tags/list" % image)
try:
info = get_docker_info(url)
logger.debug("response: %s" % info)
result = json.loads(info)
return result['tags']
except Exception as e:
logger.error('image: %s %s' % (image, e))
return []
def get_docker_manifests(image, tag):
if settings.DEBUG:
return '{}'
else:
dru = settings.DOCKER_REGISTRY_URL
url = dru.format("%s/manifests/%s" % (image, tag))
try:
info = get_docker_info(url)
logger.debug("response: %s" % info)
result = json.loads(info)
return result
except Exception as e:
logger.error('image: %s tag:%s %s' % (image, tag, e))
return None

@ -0,0 +1,62 @@
# Copyright (C) 2017 The Sipwise Team - http://sipwise.com
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
import re
from release_dashboard.utils import get_tags, get_branches
regex_hotfix = re.compile(r'^mr[0-9]+\.[0-9]+\.[0-9]+$')
regex_mr = re.compile(r'^mr.+$')
regex_master = re.compile(r'^master$')
def _projects_versions(projects, regex=None,
tags=True, branches=True, master=False):
res = []
for project in projects:
info = {
'name': project,
}
if tags:
info['tags'] = get_tags(project, regex)
if branches:
info['branches'] = get_branches(project, regex)
if master:
info['branches'].append('master')
res.append(info)
return res
def _common_versions(context, tags=True, branches=True):
common_versions = {'tags': set(), 'branches': set()}
for project in context['projects']:
if tags:
common_versions['tags'] |= set(project['tags'])
if branches:
common_versions['branches'] |= set(project['branches'])
context['common_versions'] = {
'tags': sorted(common_versions['tags'], reverse=True),
'branches': sorted(common_versions['branches'], reverse=True),
}
def _hash_versions(data, projects):
result = {}
for i in projects:
try:
result[i] = data["version_{0}".format(i)]
except (KeyError, AttributeError):
pass
return result

@ -14,26 +14,23 @@
# with this program. If not, see <http://www.gnu.org/licenses/>.
import logging
import re
import json
import uuid
from django.shortcuts import render
from django.http import HttpResponseNotFound, JsonResponse
from django.views.decorators.http import require_http_methods
from release_dashboard.models import Project
from .utils import get_tags, get_branches
from .utils import trigger_hotfix, trigger_build, trigger_docker_build
from .tasks import gerrit_fetch_info, gerrit_fetch_all
from .forms.build import BuildDepForm, BuildReleaseForm
from .forms.build import BuildTrunkDepForm, BuildTrunkReleaseForm
from .forms.docker import BuildDockerForm
from .forms import trunk_projects, trunk_build_deps, docker_projects
from .forms import rd_settings
from release_dashboard.utils import build
from release_dashboard.tasks import gerrit_fetch_info, gerrit_fetch_all
from release_dashboard.forms.build import BuildDepForm, BuildReleaseForm
from release_dashboard.forms.build import BuildTrunkDepForm
from release_dashboard.forms.build import BuildTrunkReleaseForm
from release_dashboard.forms import trunk_projects, trunk_build_deps
from release_dashboard.forms import rd_settings
from . import _projects_versions, _common_versions, _hash_versions
from . import regex_hotfix, regex_master, regex_mr
logger = logging.getLogger(__name__)
regex_hotfix = re.compile(r'^mr[0-9]+\.[0-9]+\.[0-9]+$')
regex_mr = re.compile(r'^mr.+$')
regex_master = re.compile(r'^master$')
def index(request):
@ -41,38 +38,6 @@ def index(request):
return render(request, 'release_dashboard/index.html', context)
def _projects_versions(projects, regex=None,
tags=True, branches=True, master=False):
res = []
for project in projects:
info = {
'name': project,
}
if tags:
info['tags'] = get_tags(project, regex)
if branches:
info['branches'] = get_branches(project, regex)
if master:
info['branches'].append('master')
res.append(info)
logger.debug(res)
return res
def _common_versions(context, tags=True, branches=True):
common_versions = {'tags': set(), 'branches': set()}
for project in context['projects']:
if tags:
common_versions['tags'] |= set(project['tags'])
if branches:
common_versions['branches'] |= set(project['branches'])
context['common_versions'] = {
'tags': sorted(common_versions['tags'], reverse=True),
'branches': sorted(common_versions['branches'], reverse=True),
}
@require_http_methods(["POST", ])
def hotfix_build(request, branch, project):
if project not in rd_settings['projects']:
@ -94,20 +59,10 @@ def hotfix_build(request, branch, project):
json_data = json.loads(request.body)
if json_data['push'] == 'no':
logger.warn("dryrun for %s:%s", project, branch)
url = trigger_hotfix(project, branch, json_data['push'])
url = build.trigger_hotfix(project, branch, json_data['push'])
return JsonResponse({'url': url})
def _hash_versions(data, projects):
result = {}
for i in projects:
try:
result[i] = data["version_{0}".format(i)]
except (KeyError, AttributeError):
pass
return result
def _build_logic(form, projects):
version_release = form.cleaned_data['version_release']
distribution = form.cleaned_data['distribution']
@ -119,7 +74,7 @@ def _build_logic(form, projects):
logger.debug(
"trying to trigger release %s, project %s",
version_release, pro)
url = trigger_build("%s-get-code" % pro,
url = build.trigger_build("%s-get-code" % pro,
version_release, result[pro],
distribution, flow_uuid)
context['projects'].append(
@ -212,7 +167,12 @@ def refresh(request, project):
def build_trunk_deps(request):
if request.method == "POST":
pass
form = BuildTrunkDepForm(request.POST)
if form.is_valid():
context = _build_logic(form, rd_settings['build_deps'])
else:
context = {'error': 'form validation error'}
return render(request, 'release_dashboard/build_result.html', context)
else:
context = {
'projects': _projects_versions(
@ -250,53 +210,3 @@ def build_trunk_release(request):
'debian': rd_settings['debian_supported'],
}
return render(request, 'release_dashboard/build_trunk.html', context)
def _build_docker_logic(form, projects):
result = _hash_versions(form.cleaned_data, projects)
context = {'projects': []}
for pro in projects:
try:
logger.debug(
"trying to trigger docker image at branch %s for project %s",
result[pro], pro)
url = trigger_docker_build(pro, result[pro])
context['projects'].append(
{'name': pro, 'url': url})
except KeyError:
logger.error("Houston, we have a problem with"
"trigger for %s", pro)
context['projects'].append(
{'name': pro, 'url': None})
return context
def build_docker_images(request):
if request.method == "POST":
form = BuildDockerForm(request.POST)
if form.is_valid():
context = _build_docker_logic(form, docker_projects)
else:
context = {'error': 'form validation error'}
return render(request,
'release_dashboard/build_result.html',
context)
else:
context = {
'projects': _projects_versions(
docker_projects,
regex_mr,
False,
True,
True,
),
'common_versions': {
'tags': [],
'branches': ['master', ]
},
'docker': True,
}
_common_versions(context, False, True)
return render(request,
'release_dashboard/build_docker.html',
context)

@ -0,0 +1,131 @@
# Copyright (C) 2015 The Sipwise Team - http://sipwise.com
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
import logging
import re
from django.shortcuts import render
from django.http import JsonResponse
from django.views.decorators.http import require_http_methods
from django.conf import settings
from release_dashboard.utils import docker
from release_dashboard.forms.docker import BuildDockerForm
from release_dashboard.forms import docker_projects
from release_dashboard.tasks import docker_fetch_info, docker_fetch_all
from release_dashboard.models import DockerImage
from . import _projects_versions, _common_versions, _hash_versions
from . import regex_mr
logger = logging.getLogger(__name__)
def _get_docker_tags(project, tag=None):
repos = docker.get_docker_repositories()
r = re.compile(".*%s.*" % project)
project_repos = filter(r.match, repos)
logger.debug("%s: %s" % (project, project_repos))
docker_tags = []
for image in project_repos:
res = {'name': image}
tags = docker.get_docker_tags(image)
if tag:
logger.degug("non filtered tags: %s" % tags)
tags = filter(re.compile(tag).match, tags)
res['tags'] = tags
docker_tags.append(res)
logger.debug("docker_tags: %s" % docker_tags)
return docker_tags
def _build_docker_logic(form, projects):
result = _hash_versions(form.cleaned_data, projects)
context = {'projects': []}
for pro in projects:
try:
logger.debug(
"trying to trigger docker image at branch %s for project %s",
result[pro], pro)
url = docker.trigger_docker_build(pro, result[pro])
context['projects'].append(
{'name': pro, 'url': url})
except KeyError:
logger.error("Houston, we have a problem with"
"trigger for %s", pro)
context['projects'].append(
{'name': pro, 'url': None})
return context
def build_docker_images(request):
if request.method == "POST":
form = BuildDockerForm(request.POST)
if form.is_valid():
context = _build_docker_logic(form, docker_projects)
else:
context = {'error': 'form validation error'}
return render(request,
'release_dashboard/build_result.html',
context)
else:
context = {
'projects': _projects_versions(
docker_projects,
regex_mr,
False,
True,
True,
),
'common_versions': {
'tags': [],
'branches': ['master', ]
},
'docker': True,
}
_common_versions(context, False, True)
return render(request,
'release_dashboard/build_docker.html',
context)
def refresh_all(request):
if request.method == "POST":
res = docker_fetch_all.delay()
return JsonResponse({'url': '/flower/task/%s' % res.id})
else:
projects = []
for project in docker_projects:
info = {
'name': project,
'tags': None
}
projects.append(info)
return render(request, 'release_dashboard/refresh_docker.html',
{'projects': projects})
@require_http_methods(["POST", ])
def refresh(request, project):
res = docker_fetch_info.delay(project)
return JsonResponse({'url': '/flower/task/%s' % res.id})
@require_http_methods(["GET", ])
def docker_images(request):
images = DockerImage.objects.images_with_tags
context = {
'images': images,
'URL_BASE': settings.DOCKER_REGISTRY_URL.format(''),
}
return render(request, 'release_dashboard/docker_images.html',
context)

@ -46,6 +46,7 @@ server_config = RawConfigParser()
server_config.read(os.path.join(VAR_DIR, 'server.ini'))
JENKINS_URL = server_config.get('server', 'JENKINS_URL')
GERRIT_URL = server_config.get('server', 'GERRIT_URL')
DOCKER_REGISTRY_URL = server_config.get('server', 'DOCKER_REGISTRY_URL')
# Database
# https://docs.djangoproject.com/en/1.8/ref/settings/#databases
@ -70,6 +71,7 @@ GITWEB_URL = "https://git.mgm.sipwise.com/gitweb/?p={}.git;a=commit;h={}"
WORKFRONT_CREDENTIALS = os.path.join(BASE_DIR,
'/etc/jenkins_jobs/workfront.ini')
WORKFRONT_NOTE = True
# celery
BROKER_URL = server_config.get('server', 'BROKER_URL')
JBI_BASEDIR = os.path.join(VAR_DIR, 'jbi_files')

@ -66,11 +66,38 @@ GERRIT_REST_HTTP_PASSWD = 'verysecrethttppasswd'
GITWEB_URL = "https://git.local/gitweb/?p={}.git;a=commit;h={}"
WORKFRONT_CREDENTIALS = os.path.join(BASE_DIR, '.workfront.ini')
WORKFRONT_NOTE = True
DOCKER_REGISTRY_URL = "https://localhost:5000/v2/{}"
# fake info
DOCKER_REGISTRY = """
{"repositories":["comx-fs-test-jessie","data-hal-jessie","documentation-jessie","janus-admin-jessie","janus-client-jessie","jenkins-configs","jenkins-configs-jessie","kamailio-config-tests-jessie","libswrate-jessie","libtcap-jessie","lua-ngcp-kamailio","lua-ngcp-kamailio-jenkins","lua-ngcp-kamailio-jessie","ngcp-csc-jessie","ngcp-panel-selenium","ngcp-panel-tests-rest-api-jessie","ngcp-panel-tests-selenium-jessie","ngcp-rate-o-mat-unit-tests-jessie","ngcp-rtcengine-test-jessie","ngcp-rtcengine-tests-selenium-jessie","ngcp-rtcengine-tests-selenium-stretch","ngcp-sipwise-snmp-mibs-jessie","ngcp-snmp-jessie","ngcpcfg-jessie","ossbss-perl-testing-wheezy","puppet-octocatalog-diff","puppet-sipwise-jessie","rate-o-mat-functional-tests-jessie","rate-o-mat-jessie","release-dashboard","repoapi-jessie","repos-scripts-jessie","rtpengine-jessie","sipphone-android","sipwise/ce-trunk","sipwise/mr3.8.10","sipwise/mr3.8.2","sipwise/mr3.8.3","sipwise/mr3.8.4","sipwise/mr3.8.5","sipwise/mr3.8.6","sipwise/mr3.8.7","sipwise/mr3.8.8","sipwise/mr3.8.9","sipwise/mr4.0.1","sipwise/mr4.0.2","sipwise/mr4.1.1","sipwise/mr4.1.2","sipwise/mr4.2.1","sipwise/mr4.2.2","sipwise/mr4.3.1","sipwise/mr4.3.2","sipwise/mr4.4.1","sipwise/mr4.4.2","sipwise/mr4.5.1","sipwise/mr4.5.2","sipwise/mr4.5.3","sipwise/mr4.5.4","sipwise/mr5.0.1","sipwise/mr5.0.2","sipwise/mr5.1.1","sipwise/mr5.1.2","sipwise/mr5.2.1","sipwise/mr5.3.1","sipwise-jessie","sipwise-stretch","sipwise-webpage-soap-docker-jessie","sipwise-webpage-soap-jessie","sipwise-wheezy","system-tools-jessie"]}
"""
DOCKER_IMAGES = {
'data-hal-jessie': [
"Ia9b03983d174a1546631f5b42e605235809711ef",
"If508e72c01d9bc78836a40204e508585d1dc3555",
"latest", "mr5.2", "mr5.3.1", "mr5.3"
],
'documentation-jessie': [
"If53a93f4b6d1c82fd7af5672e8b02087e646b507",
"latest", "mr5.2", "mr5.3.1", "mr5.3"
],
'ngcp-panel-selenium': ["latest", ],
'ngcp-panel-tests-rest-api-jessie': [
"I5c5c351e36da15db71fe3addbed4603007e8c304",
"I89e9acd846132508e135f7443482c0371c80d2b2",
"latest"
],
'ngcp-panel-tests-selenium-jessie': [
"I3a899b8945688c2ef3a4be6ba6c4c1d4cbf6d548",
"latest"
],
}
# celery
BROKER_BACKEND = 'memory'
CELERY_ALWAYS_EAGER = True
# CELERY_EAGER_PROPAGATES_EXCEPTIONS = True
CELERY_EAGER_PROPAGATES_EXCEPTIONS = True
JBI_BASEDIR = os.path.join(RESULTS_DIR, 'jbi_files')
JBI_ARTIFACT_JOBS = [
'fake-release-tools-runner',

@ -13,7 +13,6 @@
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
from django.test import TestCase
from repoapi.models import JenkinsBuildInfo
from django.test import override_settings
from repoapi.test.base import BaseTest
@ -42,7 +41,6 @@ class JenkinsBuildInfoTestCase(BaseTest):
@override_settings(JBI_ALLOWED_HOSTS=['jenkins-dev.local'])
def test_job_url_not_allowed(self):
base = "https://%s/job/fake-gerrit/"
job = JenkinsBuildInfo.objects.create(
projectname='fake',
jobname='fake-get-code',
@ -55,7 +53,6 @@ class JenkinsBuildInfoTestCase(BaseTest):
@override_settings(JBI_ALLOWED_HOSTS=[])
def test_job_url_not_allowed_empty(self):
base = "https://%s/job/fake-gerrit/"
job = JenkinsBuildInfo.objects.create(
projectname='fake',
jobname='fake-get-code',
@ -74,10 +71,10 @@ class JenkinsBuildInfoTestCase(BaseTest):
job = JenkinsBuildInfo.objects.create(
projectname='fake',
jobname='fake-get-code',
job_url=JBI_HOST % 'jenkins-dev.local',
buildnumber=1,
result='OK',
param_release='release-mr4.0')
job.job_url = JBI_HOST % 'jenkins-dev.local'
self.assertTrue(job.is_job_url_allowed())
job.job_url = JBI_HOST % 'jenkins.local'
self.assertTrue(job.is_job_url_allowed())

@ -13,7 +13,6 @@
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
from django.test import TestCase
from repoapi.models import JenkinsBuildInfo, GerritRepoInfo
from repoapi.test.base import BaseTest
from mock import patch

@ -15,7 +15,7 @@
import os
from django.test import TestCase, override_settings
from django.test import override_settings
from django.conf import settings
from repoapi.models import JenkinsBuildInfo
from repoapi.utils import JBI_CONSOLE_URL, JBI_BUILD_URL, JBI_ARTIFACT_URL

@ -13,7 +13,6 @@
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
from django.test import TestCase
from django.utils.dateparse import parse_datetime
from repoapi.models import JenkinsBuildInfo
from repoapi.test.base import BaseTest

@ -13,7 +13,6 @@
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
from django.test import TestCase
from repoapi.test.base import BaseTest
from mock import patch
@ -35,13 +34,13 @@ class UtilsTestCase(BaseTest):
self.assertEquals(val, 'mr5.5.1')
@patch('repoapi.utils.executeAndReturnOutput')
def test_get_next_release0(self, ear):
def test_get_next_release1(self, ear):
ear.return_value = [0, "mr5.4.2\n", ""]
val = utils.get_next_release("mr5.4")
self.assertEquals(val, 'mr5.4.2')
@patch('repoapi.utils.executeAndReturnOutput')
def test_get_next_release0(self, ear):
def test_get_next_release2(self, ear):
ear.return_value = [0, "\n", ""]
val = utils.get_next_release("mr5.4")
self.assertEquals(val, None)

@ -13,7 +13,6 @@
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
from django.test import TestCase
from django.conf import settings
from repoapi.models import JenkinsBuildInfo, WorkfrontNoteInfo
from repoapi.test.base import BaseTest

@ -6,7 +6,7 @@ djangorestframework==3.4
django-rest-swagger
markdown
django-filter
six
six==1.10
webassets
celery<4.0
django-celery

@ -40,6 +40,8 @@ WORKDIR /code/
#
# We need a working rabbit server, so in another terminal:
# % docker run --rm --hostname repoapi-rabbit --name repoapi-rabbit rabbitmq:3
# and link both containers:
# % docker run --rm -i -t --link repoapi-rabbit:rabbit -v $(pwd):/code:rw docker.mgm.sipwise.com/repoapi-jessie:latest bash
#
# use screen to get a working worker in the background:
# % make worker_dev

Loading…
Cancel
Save