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 JENKINS_URL=fake
GERRIT_URL=fake GERRIT_URL=fake
BROKER_URL=fake BROKER_URL=fake
DOCKER_REGISTRY_URL=fake
DB_NAME=fake DB_NAME=fake
DB_USER=fake DB_USER=fake
DB_PWD=fake DB_PWD=fake

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

@ -17,6 +17,8 @@ from django.contrib import admin
from . import models from . import models
@admin.register(models.DockerTag)
@admin.register(models.DockerImage)
@admin.register(models.Project) @admin.register(models.Project)
class ProjectAdmin(admin.ModelAdmin): class ProjectAdmin(admin.ModelAdmin):
pass 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 logging
import re import re
import json import json
from datetime import datetime
from django.db import models from django.db import models
from django_extensions.db.fields.json import JSONField from django_extensions.db.fields.json import JSONField
from django_extensions.db.fields import ModificationDateTimeField from django_extensions.db.fields import ModificationDateTimeField
@ -77,8 +78,61 @@ class Project(models.Model):
return Project._filter_values(self.json_branches, return Project._filter_values(self.json_branches,
'^refs/heads/(.+)$', regex) '^refs/heads/(.+)$', regex)
def filter_docker_images(self, images):
r = re.compile(self.name)
return filter(r.search, images)
def branches_mrXX(self): def branches_mrXX(self):
return self.filter_branches(r'^mr[0-9]+\.[0-9]+$') return self.filter_branches(r'^mr[0-9]+\.[0-9]+$')
def branches_mrXXX(self): def branches_mrXXX(self):
return self.filter_branches(r'^mr[0-9]+\.[0-9]+\.[0-9]+$') 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 import logging
from celery import shared_task 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 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__) logger = logging.getLogger(__name__)
rd_settings = settings.RELEASE_DASHBOARD_SETTINGS rd_settings = settings.RELEASE_DASHBOARD_SETTINGS
@ -27,12 +28,37 @@ rd_settings = settings.RELEASE_DASHBOARD_SETTINGS
@shared_task(ignore_result=True) @shared_task(ignore_result=True)
def gerrit_fetch_info(projectname): def gerrit_fetch_info(projectname):
project, _ = Project.objects.get_or_create(name=projectname) project, _ = Project.objects.get_or_create(name=projectname)
project.tags = get_gerrit_tags(projectname) project.tags = build.get_gerrit_tags(projectname)
project.branches = get_gerrit_branches(projectname) project.branches = build.get_gerrit_branches(projectname)
project.save() project.save()
@shared_task(ignore_result=True) @shared_task(ignore_result=True)
def gerrit_fetch_all(): def gerrit_fetch_all():
for project in rd_settings['projects']: for project in rd_settings['docker_projects']:
gerrit_fetch_info.delay(project) 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> </select>
</div> </div>
{% endif %} {% endif %}
{% if not docker %}
<div class="form-group"> <div class="form-group">
<label for="version_release">Release version</label> <label for="version_release">Release version</label>
<input class="form-control" id="version_release" <input class="form-control" id="version_release"
@ -36,7 +35,6 @@
{% endfor %} {% endfor %}
</select> </select>
</div> </div>
{% endif %}
<button id="main" type="submit" class="btn btn-default">Submit</button> <button id="main" type="submit" class="btn btn-default">Submit</button>
</div> </div>
<div id="select_text_info" class="panel-footer"></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> <li><a href="{% url 'release_dashboard:build_docker_images'%}">Build Docker Images</a></li>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
{% include "release_dashboard/build_content.html" %} {% include "release_dashboard/build_docker_content.html" %}
{% endblock %} {% endblock %}
{% block extrajs %} {% block extrajs %}
<script src="{% static "release_dashboard/js/build.js" %}"></script> <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'%}"> <a href="{% url 'release_dashboard:refresh_all'%}">
Refresh tag/branch Info</a> Refresh tag/branch Info</a>
</li> </li>
<li class="list-group-item">
<a href="{% url 'release_dashboard:docker_images'%}">
Docker image/tag Info</a>
</li>
<li class="list-group-item"> <li class="list-group-item">
<a href="{% url 'release_dashboard:build_docker_images'%}"> <a href="{% url 'release_dashboard:build_docker_images'%}">
Build project docker images</a> Build project docker images</a>
</li> </li>
<li class="list-group-item">
<a href="{% url 'release_dashboard:refresh_docker_all'%}">
Refresh Docker image/tag Info</a>
</li>
</ul> </ul>
</div> </div>
</div> </div>

@ -1,51 +1,12 @@
{% extends "release_dashboard/base.html" %} {% extends "release_dashboard/base.html" %}
{% load staticfiles %} {% load staticfiles %}
{% block title %}Hotfixes{% endblock %} {% block title %}Refresh GIT{% endblock %}
{% block navlist %} {% block navlist %}
<li><a href="{% url 'release_dashboard:index'%}">Release Dashboard</a></li> <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 %} {% endblock %}
{% block content %} {% block content %}
<div class="container"> {% include "release_dashboard/refresh_content.html" %}
<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>
{% endblock %} {% endblock %}
{% block extrajs %} {% block extrajs %}
<script src="{% static "release_dashboard/js/csrf.js" %}"></script> <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/>. # with this program. If not, see <http://www.gnu.org/licenses/>.
from django.conf.urls import url from django.conf.urls import url
from . import views from views import build, docker
urlpatterns = [ urlpatterns = [
url(r'^$', views.index, name='index'), url(r'^$', build.index, name='index'),
url(r'^build_deps/$', views.build_deps, name='build_deps'), url(r'^build_deps/$', build.build_deps, name='build_deps'),
url(r'^build/$', views.build_release, name='build_release'), url(r'^build/$', build.build_release, name='build_release'),
url(r'^build_trunk_deps/$', views.build_trunk_deps, url(r'^build_trunk_deps/$', build.build_trunk_deps,
name='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'), 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'), {'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>[^/]+)/$', url(r'^hotfix/(?P<branch>[^/]+)/(?P<project>[^/]+)/$',
views.hotfix_build), build.hotfix_build),
url(r'^refresh/$', views.refresh_all, name='refresh_all'), url(r'^refresh/$', build.refresh_all, name='refresh_all'),
url(r'^refresh/(?P<project>[^/]+)/$', views.refresh, name='refresh'), url(r'^refresh/(?P<project>[^/]+)/$', build.refresh, name='refresh'),
url(r'^build_docker/$', views.build_docker_images, url(r'^build_docker/$', docker.build_docker_images,
name='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 requests.auth import HTTPDigestAuth
from django.conf import settings from django.conf import settings
from repoapi.utils import openurl from repoapi.utils import openurl
from release_dashboard.models import Project
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -34,9 +33,6 @@ hotfix_url = ("{base}/job/release-tools-runner/buildWithParameters?"
"PROJECTNAME={project}&repository={project}&" "PROJECTNAME={project}&repository={project}&"
"push={push}&uuid={uuid}") "push={push}&uuid={uuid}")
docker_url = ("{base}/job/build-project-docker/buildWithParameters?"
"token={token}&project={project}&branch={branch}")
def get_response(url): def get_response(url):
auth = HTTPDigestAuth( auth = HTTPDigestAuth(
@ -103,27 +99,6 @@ def trigger_build(project, trigger_release=None,
return "{base}/job/{job}/".format(**params) 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): def get_gerrit_info(url):
if settings.DEBUG: if settings.DEBUG:
logger.debug("Debug mode, would trigger: %s", url) logger.debug("Debug mode, would trigger: %s", url)
@ -134,16 +109,6 @@ def get_gerrit_info(url):
return response.text 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): def get_gerrit_tags(project, regex=None):
url = settings.GERRIT_URL.format("a/projects/%s/tags/" % project) url = settings.GERRIT_URL.format("a/projects/%s/tags/" % project)
return get_gerrit_info(url) 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/>. # with this program. If not, see <http://www.gnu.org/licenses/>.
import logging import logging
import re
import json import json
import uuid import uuid
from django.shortcuts import render from django.shortcuts import render
from django.http import HttpResponseNotFound, JsonResponse from django.http import HttpResponseNotFound, JsonResponse
from django.views.decorators.http import require_http_methods from django.views.decorators.http import require_http_methods
from release_dashboard.models import Project from release_dashboard.models import Project
from .utils import get_tags, get_branches from release_dashboard.utils import build
from .utils import trigger_hotfix, trigger_build, trigger_docker_build from release_dashboard.tasks import gerrit_fetch_info, gerrit_fetch_all
from .tasks import gerrit_fetch_info, gerrit_fetch_all from release_dashboard.forms.build import BuildDepForm, BuildReleaseForm
from .forms.build import BuildDepForm, BuildReleaseForm from release_dashboard.forms.build import BuildTrunkDepForm
from .forms.build import BuildTrunkDepForm, BuildTrunkReleaseForm from release_dashboard.forms.build import BuildTrunkReleaseForm
from .forms.docker import BuildDockerForm from release_dashboard.forms import trunk_projects, trunk_build_deps
from .forms import trunk_projects, trunk_build_deps, docker_projects from release_dashboard.forms import rd_settings
from .forms import rd_settings from . import _projects_versions, _common_versions, _hash_versions
from . import regex_hotfix, regex_master, regex_mr
logger = logging.getLogger(__name__) 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): def index(request):
@ -41,38 +38,6 @@ def index(request):
return render(request, 'release_dashboard/index.html', context) 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", ]) @require_http_methods(["POST", ])
def hotfix_build(request, branch, project): def hotfix_build(request, branch, project):
if project not in rd_settings['projects']: if project not in rd_settings['projects']:
@ -94,20 +59,10 @@ def hotfix_build(request, branch, project):
json_data = json.loads(request.body) json_data = json.loads(request.body)
if json_data['push'] == 'no': if json_data['push'] == 'no':
logger.warn("dryrun for %s:%s", project, branch) 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}) 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): def _build_logic(form, projects):
version_release = form.cleaned_data['version_release'] version_release = form.cleaned_data['version_release']
distribution = form.cleaned_data['distribution'] distribution = form.cleaned_data['distribution']
@ -119,7 +74,7 @@ def _build_logic(form, projects):
logger.debug( logger.debug(
"trying to trigger release %s, project %s", "trying to trigger release %s, project %s",
version_release, pro) version_release, pro)
url = trigger_build("%s-get-code" % pro, url = build.trigger_build("%s-get-code" % pro,
version_release, result[pro], version_release, result[pro],
distribution, flow_uuid) distribution, flow_uuid)
context['projects'].append( context['projects'].append(
@ -212,7 +167,12 @@ def refresh(request, project):
def build_trunk_deps(request): def build_trunk_deps(request):
if request.method == "POST": 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: else:
context = { context = {
'projects': _projects_versions( 'projects': _projects_versions(
@ -250,53 +210,3 @@ def build_trunk_release(request):
'debian': rd_settings['debian_supported'], 'debian': rd_settings['debian_supported'],
} }
return render(request, 'release_dashboard/build_trunk.html', context) 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')) server_config.read(os.path.join(VAR_DIR, 'server.ini'))
JENKINS_URL = server_config.get('server', 'JENKINS_URL') JENKINS_URL = server_config.get('server', 'JENKINS_URL')
GERRIT_URL = server_config.get('server', 'GERRIT_URL') GERRIT_URL = server_config.get('server', 'GERRIT_URL')
DOCKER_REGISTRY_URL = server_config.get('server', 'DOCKER_REGISTRY_URL')
# Database # Database
# https://docs.djangoproject.com/en/1.8/ref/settings/#databases # 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, WORKFRONT_CREDENTIALS = os.path.join(BASE_DIR,
'/etc/jenkins_jobs/workfront.ini') '/etc/jenkins_jobs/workfront.ini')
WORKFRONT_NOTE = True WORKFRONT_NOTE = True
# celery # celery
BROKER_URL = server_config.get('server', 'BROKER_URL') BROKER_URL = server_config.get('server', 'BROKER_URL')
JBI_BASEDIR = os.path.join(VAR_DIR, 'jbi_files') 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={}" GITWEB_URL = "https://git.local/gitweb/?p={}.git;a=commit;h={}"
WORKFRONT_CREDENTIALS = os.path.join(BASE_DIR, '.workfront.ini') WORKFRONT_CREDENTIALS = os.path.join(BASE_DIR, '.workfront.ini')
WORKFRONT_NOTE = True 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 # celery
BROKER_BACKEND = 'memory' BROKER_BACKEND = 'memory'
CELERY_ALWAYS_EAGER = True CELERY_ALWAYS_EAGER = True
# CELERY_EAGER_PROPAGATES_EXCEPTIONS = True CELERY_EAGER_PROPAGATES_EXCEPTIONS = True
JBI_BASEDIR = os.path.join(RESULTS_DIR, 'jbi_files') JBI_BASEDIR = os.path.join(RESULTS_DIR, 'jbi_files')
JBI_ARTIFACT_JOBS = [ JBI_ARTIFACT_JOBS = [
'fake-release-tools-runner', 'fake-release-tools-runner',

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

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

@ -15,7 +15,7 @@
import os import os
from django.test import TestCase, override_settings from django.test import override_settings
from django.conf import settings from django.conf import settings
from repoapi.models import JenkinsBuildInfo from repoapi.models import JenkinsBuildInfo
from repoapi.utils import JBI_CONSOLE_URL, JBI_BUILD_URL, JBI_ARTIFACT_URL 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 # You should have received a copy of the GNU General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>. # with this program. If not, see <http://www.gnu.org/licenses/>.
from django.test import TestCase
from django.utils.dateparse import parse_datetime from django.utils.dateparse import parse_datetime
from repoapi.models import JenkinsBuildInfo from repoapi.models import JenkinsBuildInfo
from repoapi.test.base import BaseTest from repoapi.test.base import BaseTest

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

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

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

@ -40,6 +40,8 @@ WORKDIR /code/
# #
# We need a working rabbit server, so in another terminal: # We need a working rabbit server, so in another terminal:
# % docker run --rm --hostname repoapi-rabbit --name repoapi-rabbit rabbitmq:3 # % 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: # use screen to get a working worker in the background:
# % make worker_dev # % make worker_dev

Loading…
Cancel
Save