From bf62f449e41073fee09f87df858489fab23cf018 Mon Sep 17 00:00:00 2001 From: Nikolay Zamotaev Date: Tue, 2 Oct 2018 17:40:35 +0300 Subject: Remote package upload API implementation Any user with permission to enter admin panel can upload packages to the deployment server. This will be used by a (not yet implemented) tool in qt application manager for automatic package upload from qtcreator. Fixes: AUTOSUITE-623 Change-Id: I5aba9d16480e2161e5e633359070004f66f2b897 Reviewed-by: Dominik Holland --- appstore/urls.py | 1 + store/api.py | 60 +++++++++- store/authdecorators.py | 135 ++++++++++++++++++++++ store/management/commands/store-upload-package.py | 39 +------ store/models.py | 38 +++++- 5 files changed, 233 insertions(+), 40 deletions(-) create mode 100644 store/authdecorators.py diff --git a/appstore/urls.py b/appstore/urls.py index 081acf9..327c28b 100644 --- a/appstore/urls.py +++ b/appstore/urls.py @@ -46,6 +46,7 @@ base_urlpatterns = patterns('', url(r'^app/download/(.*)$', 'store.api.appDownload'), url(r'^category/list$', 'store.api.categoryList'), url(r'^category/icon$', 'store.api.categoryIcon'), + url(r'^upload$', 'store.api.upload'), ) diff --git a/store/api.py b/store/api.py index 9734f6e..f2d911e 100644 --- a/store/api.py +++ b/store/api.py @@ -30,19 +30,19 @@ ############################################################################# import os -import tempfile -import datetime import shutil -import json from django.conf import settings from django.db.models import Q, Count from django.http import HttpResponse, HttpResponseForbidden, Http404, JsonResponse from django.contrib import auth from django.template import Context, loader +from django.views.decorators.csrf import csrf_exempt +from authdecorators import logged_in_or_basicauth, is_staff_member -from models import App, Category, Vendor -from utilities import parsePackageMetadata, packagePath, iconPath, downloadPath, addSignatureToPackage, validateTag +from models import App, Category, Vendor, savePackageFile +from utilities import parsePackageMetadata, parseAndValidatePackageMetadata, addSignatureToPackage +from utilities import packagePath, iconPath, downloadPath, validateTag def hello(request): @@ -105,6 +105,56 @@ def logout(request): return JsonResponse({'status': status}) +@csrf_exempt +@logged_in_or_basicauth() +@is_staff_member() +def upload(request): + status = 'ok' + try: + try: + description = request.REQUEST["description"] + except: + raise Exception('no description') + try: + shortdescription = request.REQUEST["short-description"] + except: + raise Exception('no short description') + try: + category_name = request.REQUEST["category"] + except: + raise Exception('no category') + try: + vendor_name = request.REQUEST["vendor"] + except: + raise Exception('no vendor') + + if request.method == 'POST' and request.FILES['package']: + myfile = request.FILES['package'] + category = Category.objects.all().filter(name__exact=category_name) + vendor = Vendor.objects.all().filter(name__exact=vendor_name) + if len(category) == 0: + raise Exception('Non-existing category') + if len(vendor) == 0: + raise Exception('Non-existing vendor') + + try: + pkgdata = parseAndValidatePackageMetadata(myfile) + except: + raise Exception('Package validation failed') + + myfile.seek(0) + try: + savePackageFile(pkgdata, myfile, category[0], vendor[0], description, shortdescription) + except Exception as error: + raise Exception(error) + else: + raise Exception('no package to upload') + + except Exception as error: + status = str(error) + return JsonResponse({'status': status}) + + def appList(request): apps = App.objects.all() if 'filter' in request.REQUEST: diff --git a/store/authdecorators.py b/store/authdecorators.py new file mode 100644 index 0000000..fbbc01d --- /dev/null +++ b/store/authdecorators.py @@ -0,0 +1,135 @@ +############################################################################# +## +## Copyright (C) 2018 Luxoft +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the Neptune Deployment Server +## +## $QT_BEGIN_LICENSE:GPL-QTAS$ +## Commercial License Usage +## Licensees holding valid commercial Qt Automotive Suite licenses may use +## this file in accordance with the commercial license agreement provided +## with the Software or, alternatively, in accordance with the terms +## contained in a written agreement between you and The Qt Company. For +## licensing terms and conditions see https://www.qt.io/terms-conditions. +## For further information use the contact form at https://www.qt.io/contact-us. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 3 or (at your option) any later version +## approved by the KDE Free Qt Foundation. The licenses are as published by +## the Free Software Foundation and appearing in the file LICENSE.GPL3 +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +## SPDX-License-Identifier: GPL-3.0 +## +############################################################################# + +# Code taken from: https://www.djangosnippets.org/snippets/243/ +# Reuse and licensing is permitted by TOS: https://www.djangosnippets.org/about/tos/ + +import base64 + +from django.http import HttpResponse +from django.contrib.auth import authenticate, login + +############################################################################# + +def view_or_basicauth(view, request, test_func, realm="", *args, **kwargs): + """ + This is a helper function used by both 'logged_in_or_basicauth' and + 'has_perm_or_basicauth' that does the service of determining if they + are already logged in or if they have provided proper http-authorization + and returning the view if all goes well, otherwise responding with a 401. + """ + if test_func(request.user): + # Already logged in, just return the view. + # + return view(request, *args, **kwargs) + + # They are not logged in. See if they provided login credentials + # + if 'HTTP_AUTHORIZATION' in request.META: + auth = request.META['HTTP_AUTHORIZATION'].split() + if len(auth) == 2: + # NOTE: We are only support basic authentication for now. + # + if auth[0].lower() == "basic": + uname, passwd = base64.b64decode(auth[1]).split(':') + user = authenticate(username=uname, password=passwd) + if user is not None: + if user.is_active: + login(request, user) + request.user = user + if test_func(request.user): + return view(request, *args, **kwargs) + + # Either they did not provide an authorization header or + # something in the authorization attempt failed. Send a 401 + # back to them to ask them to authenticate. + # + response = HttpResponse() + response.status_code = 401 + response['WWW-Authenticate'] = 'Basic realm="%s"' % realm + return response + + +############################################################################# + +def logged_in_or_basicauth(realm=""): + """ + A simple decorator that requires a user to be logged in and in staff group. + If they are not logged in the request is examined for a 'authorization' header. + + If the header is present it is tested for basic authentication and + the user is logged in with the provided credentials. + + If the header is not present a http 401 is sent back to the + requester to provide credentials. + + The purpose of this is that in several django projects I have needed + several specific views that need to support basic authentication, yet the + web site as a whole used django's provided authentication. + + The uses for this are for urls that are access programmatically such as + by rss feed readers, yet the view requires a user to be logged in. Many rss + readers support supplying the authentication credentials via http basic + auth (and they do NOT support a redirect to a form where they post a + username/password.) + + Use is simple: + + @logged_in_or_basicauth + def your_view: + ... + + You can provide the name of the realm to ask for authentication within. + """ + + def view_decorator(func): + def wrapper(request, *args, **kwargs): + return view_or_basicauth(func, request, + lambda u: u.is_authenticated(), + realm, *args, **kwargs) + + return wrapper + + return view_decorator + +def is_staff_member(): + def view_decorator(func): + def wrapper(request, *args, **kwargs): + if request.user.is_staff: + return func(request, *args, **kwargs) + else: + response = HttpResponse() + response.status_code = 403 + return response + + return wrapper + return view_decorator + diff --git a/store/management/commands/store-upload-package.py b/store/management/commands/store-upload-package.py index 1468d27..81f96fa 100644 --- a/store/management/commands/store-upload-package.py +++ b/store/management/commands/store-upload-package.py @@ -33,8 +33,8 @@ import os from django.core.management.base import BaseCommand, CommandError from django.core.files.base import ContentFile -from store.models import App, Category, Vendor -from store.utilities import parseAndValidatePackageMetadata, packagePath, makeTagList, writeTempIcon +from store.models import App, Category, Vendor, savePackageFile +from store.utilities import parseAndValidatePackageMetadata from optparse import make_option @@ -83,38 +83,9 @@ class Command(BaseCommand): return 0 packagefile.seek(0) - appId = pkgdata['info']['id'] - name = pkgdata['storeName'] - architecture = pkgdata['architecture'] description = options['description'] - tags = makeTagList(pkgdata) - - success, error = writeTempIcon(appId, architecture, pkgdata['icon']) - if not success: - raise CommandError(error) - - exists = False - app = None try: - app = App.objects.get(appid__exact=appId, architecture__exact= architecture) - exists = True - except App.DoesNotExist: - pass + savePackageFile(pkgdata, ContentFile(packagefile.read()), category[0], vendor[0], description, description) + except Exception as error: + raise CommandError(error) - if exists: - app.appid = appId - app.category = category[0] - app.vendor = vendor[0] - app.name = name - app.tags = tags - app.description = app.briefDescription = description - app.architecture = architecture - app.file.save(packagePath(appId, architecture), ContentFile(packagefile.read())) - app.save() - else: - app, created = App.objects.get_or_create(name=name, tags=tags, vendor=vendor[0], - category=category[0], appid=appId, - briefDescription=description, description=description, - architecture=architecture) - app.file.save(packagePath(appId, architecture), ContentFile(packagefile.read())) - app.save() diff --git a/store/models.py b/store/models.py index 802d141..63b1d89 100644 --- a/store/models.py +++ b/store/models.py @@ -36,7 +36,7 @@ from django.conf import settings from django.contrib.auth.models import User from django.core.files.storage import FileSystemStorage -from utilities import packagePath +from utilities import packagePath, writeTempIcon, makeTagList class Category(models.Model): @@ -142,3 +142,39 @@ class App(models.Model): pass super(App, self).save(*args, **kwargs) + +def savePackageFile(pkgdata, pkgfile, category, vendor, description, shortdescription): + appId = pkgdata['info']['id'] + name = pkgdata['storeName'] + architecture = pkgdata['architecture'] + tags = makeTagList(pkgdata) + success, error = writeTempIcon(appId, architecture, pkgdata['icon']) + if not success: + raise Exception(error) + + exists = False + app = None + try: + app = App.objects.get(appid__exact=appId, architecture__exact=architecture) + exists = True + except App.DoesNotExist: + pass + + if exists: + app.appid = appId + app.category = category + app.vendor = vendor + app.name = name + app.tags = tags + app.description = description + app.briefDescription = shortdescription + app.architecture = architecture + app.file.save(packagePath(appId, architecture), pkgfile) + app.save() + else: + app, created = App.objects.get_or_create(name=name, tags=tags, vendor=vendor, + category=category, appid=appId, + briefDescription=shortdescription, description=description, + architecture=architecture) + app.file.save(packagePath(appId, architecture), pkgfile) + app.save() -- cgit v1.2.3 From 97403e148e15e21b1f8d82e02df1c8ab7528025f Mon Sep 17 00:00:00 2001 From: Nikolay Zamotaev Date: Wed, 24 Oct 2018 20:55:11 +0300 Subject: Update for architecture and bitness detection and processing This is done to match QSysInfo::currentCpuArchitecture(), kernelVersion() and buildAbi() See implementation in neptune3-ui for details. Without this code, neptune3-ui would be unable to get native applications. Change-Id: Ic336b364575bc22c1812693fea254d4898f1ee6f Fixes: AUTOSUITE-670 Reviewed-by: Dominik Holland --- store/api.py | 3 ++- store/osandarch.py | 51 +++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 45 insertions(+), 9 deletions(-) diff --git a/store/api.py b/store/api.py index f2d911e..6e951a4 100644 --- a/store/api.py +++ b/store/api.py @@ -43,6 +43,7 @@ from authdecorators import logged_in_or_basicauth, is_staff_member from models import App, Category, Vendor, savePackageFile from utilities import parsePackageMetadata, parseAndValidatePackageMetadata, addSignatureToPackage from utilities import packagePath, iconPath, downloadPath, validateTag +from osandarch import normalizeArch def hello(request): @@ -64,7 +65,7 @@ def hello(request): break request.session[j] = taglist if 'architecture' in request.REQUEST: - request.session['architecture'] = request.REQUEST['architecture'] + request.session['architecture'] = normalizeArch(request.REQUEST['architecture']) else: request.session['architecture'] = '' return JsonResponse({'status': status}) diff --git a/store/osandarch.py b/store/osandarch.py index f3aa388..c7d15a6 100644 --- a/store/osandarch.py +++ b/store/osandarch.py @@ -42,6 +42,8 @@ # PE32+ executable (DLL) (GUI) x86-64, for MS Windows # PE32+ executable (GUI) x86-64, for MS Windows +import re + def parseMachO(str): # os, arch, bits, endianness if " universal " in str: # Universal binary - not supported @@ -50,7 +52,7 @@ def parseMachO(str): # os, arch, bits, endianness arch = str.split(' ') arch = arch[2] bits = str.split(' ')[1].replace('-bit', '') - endianness = "lsb" + endianness = "little_endian" return [os, arch, bits, endianness] @@ -65,24 +67,28 @@ def parsePE32(str): bits = '64' if arch == '80386': arch = 'i386' - endianness = "lsb" + endianness = "little_endian" return [os, arch, bits, endianness] -def parseElfArch(str, architecture): +def parseElfArch(str, architecture, bits): architecture = architecture.strip() if architecture.startswith("ARM"): if 'aarch64' in architecture: - return 'aarch64' + return 'arm64' if 'armhf' in str: # this does not work for some reason - from_file() returns longer data than from_buffer() - needs fix - return 'armhf' + return 'arm' # because qt does not report it directly elif architecture.startswith("Intel"): if '80386' in architecture: return 'i386' elif architecture.startswith("IBM S/390"): return 's/390' elif "PowerPC" in architecture: - return 'powerpc' + if bits == "64": + return "power64" + else: + return 'power' + # sparc architecture is currently ignored (should be handled similar to power?) return architecture.lower() @@ -90,9 +96,15 @@ def parseElf(str): os = "Linux" arch = str.split(',') arch = arch[1] - arch = parseElfArch(str, arch) bits = str.split(' ')[1].replace('-bit', '') - endianness = str.split(' ')[2].lower() + arch = parseElfArch(str, arch, bits) + endian = str.split(' ')[2].lower() + if endian == "msb": + endianness = "big_endian" + elif endian == "lsb": + endianness = "little_endian" + else: + raise Exception("Unrecognised endianness") return [os, arch, bits, endianness] @@ -117,3 +129,26 @@ def getOsArch(str): if os: return result return None + +def normalizeArch(inputArch): + """ + This function brings requested architecture to common form (currently just parses the bits part and turns it into 32/64) + Input string format is: arch-endianness-word_size-kernelType + + """ + parts = inputArch.split('-') + #Drop anything non-numeric from word_size field + parts[2]=re.sub(r"\D", "", parts[2]) + #Transform kernelType into binary format + temp = parts[3] + if "win" in temp: + parts[3]="pe32" + elif "linux" in temp: + parts[3]="elf" + elif "freebsd" in temp: #How do we treat QNX? + parts[3]="elf" + elif "darwin" in temp: + parts[3]="mach_o" + #Rejoin new architecture + arch = '-'.join(parts) + return arch -- cgit v1.2.3 From 6e509735e47fcfb289f75863953aed6bb6396408 Mon Sep 17 00:00:00 2001 From: Nikolay Zamotaev Date: Wed, 5 Dec 2018 17:01:09 +0300 Subject: Support for tag versions Support for versioned tags. Versioning is in form of either: tag or tag:version Task-number: AUTOSUITE-708 Change-Id: I480ae08b3cdc0ccf6ac1fc1c9724448be9cb1b55 Reviewed-by: Dominik Holland --- store/admin.py | 2 +- store/api.py | 29 ++-- store/migrations/0001_initial.py | 2 +- store/models.py | 4 +- store/tags.py | 339 +++++++++++++++++++++++++++++++++++++++ store/utilities.py | 17 +- 6 files changed, 363 insertions(+), 30 deletions(-) create mode 100644 store/tags.py diff --git a/store/admin.py b/store/admin.py index 1cc06da..a921e2f 100644 --- a/store/admin.py +++ b/store/admin.py @@ -174,7 +174,7 @@ class AppAdminForm(forms.ModelForm): class AppAdmin(admin.ModelAdmin): form = AppAdminForm - list_display = ('name', 'appid', 'architecture', 'version') + list_display = ('name', 'appid', 'architecture', 'version', 'tags') def save_model(self, request, obj, form, change): obj.save() diff --git a/store/api.py b/store/api.py index 6e951a4..b9dc0e6 100644 --- a/store/api.py +++ b/store/api.py @@ -42,8 +42,8 @@ from authdecorators import logged_in_or_basicauth, is_staff_member from models import App, Category, Vendor, savePackageFile from utilities import parsePackageMetadata, parseAndValidatePackageMetadata, addSignatureToPackage -from utilities import packagePath, iconPath, downloadPath, validateTag -from osandarch import normalizeArch +from utilities import packagePath, iconPath, downloadPath +from tags import SoftwareTagList def hello(request): @@ -58,12 +58,11 @@ def hello(request): for j in ("require_tag", "conflicts_tag",): if j in request.REQUEST: #Tags are coma-separated, - taglist = [i.lower() for i in request.REQUEST[j].split(',') if i] - for i in taglist: - if not validateTag(i): #Tags must be alphanumeric (or, even better - limited to ASCII alphanumeric) - status = 'malformed-tag' - break - request.session[j] = taglist + versionmap = SoftwareTagList() + if not versionmap.parse(request.REQUEST[j]): + status = 'malformed-tag' + break + request.session[j] = str(versionmap) if 'architecture' in request.REQUEST: request.session['architecture'] = normalizeArch(request.REQUEST['architecture']) else: @@ -169,13 +168,15 @@ def appList(request): #"require_tag", "conflicts_tag" # Tags are combined by logical AND (for require) and logical OR for conflicts if 'require_tag' in request.session: - for i in request.session['require_tag']: - regex = '(^|,)%s(,|$)' % (i,) - apps = apps.filter(Q(tags__regex = regex)) + require_tags = SoftwareTagList() + require_tags.parse(request.session['require_tag']) + for i in require_tags.make_regex(): + apps = apps.filter(Q(tags__regex = i)) if 'conflicts_tag' in request.session: - for i in request.session['conflicts_tag']: - regex = '(^|,)%s(,|$)' % (i,) - apps = apps.filter(~Q(tags__regex = regex)) + conflict_tags = SoftwareTagList() + conflict_tags.parse(request.session['conflicts_tag']) + for i in conflict_tags.make_regex(): + apps = apps.filter(~Q(tags__regex=i)) # Here goes the logic of listing packages when multiple architectures are available # in /hello request, the target architecture is stored in the session. By definition target machine can support diff --git a/store/migrations/0001_initial.py b/store/migrations/0001_initial.py index 772b1d2..ae4bd24 100644 --- a/store/migrations/0001_initial.py +++ b/store/migrations/0001_initial.py @@ -99,6 +99,6 @@ class Migration(migrations.Migration): ), migrations.AlterUniqueTogether( name='app', - unique_together=set([('appid', 'architecture')]), + unique_together=set([('appid', 'architecture', 'tags')]), ), ] diff --git a/store/models.py b/store/models.py index 63b1d89..dd3b4a1 100644 --- a/store/models.py +++ b/store/models.py @@ -128,10 +128,10 @@ class App(models.Model): class Meta: """Makes the group of id and arch - a unique identifier""" - unique_together = (('appid', 'architecture', ),) + unique_together = (('appid', 'architecture', 'tags'),) def __unicode__(self): - return self.name + " [" + " ".join([self.appid,self.version,self.architecture]) + "]" + return self.name + " [" + " ".join([self.appid,self.version,self.architecture,self.tags]) + "]" def save(self, *args, **kwargs): try: diff --git a/store/tags.py b/store/tags.py new file mode 100644 index 0000000..183a90a --- /dev/null +++ b/store/tags.py @@ -0,0 +1,339 @@ +# vim: set fileencoding=utf-8 : +############################################################################# +## +## Copyright (C) 2018 Pelagicore AG +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the Neptune Deployment Server +## +## $QT_BEGIN_LICENSE:GPL-QTAS$ +## Commercial License Usage +## Licensees holding valid commercial Qt Automotive Suite licenses may use +## this file in accordance with the commercial license agreement provided +## with the Software or, alternatively, in accordance with the terms +## contained in a written agreement between you and The Qt Company. For +## licensing terms and conditions see https://www.qt.io/terms-conditions. +## For further information use the contact form at https://www.qt.io/contact-us. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 3 or (at your option) any later version +## approved by the KDE Free Qt Foundation. The licenses are as published by +## the Free Software Foundation and appearing in the file LICENSE.GPL3 +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +## SPDX-License-Identifier: GPL-3.0 +## +############################################################################# + +import hashlib +import unittest +import re + + +def validateTagVersion(version): + for i in version: + if not i.isalnum(): + if not ((i == "_") or (i == ".")): + return False + return True + + +def validateTag(tag): + if len(tag) == 0: + return False + lst = tag.split(':') + if len(lst) > 2: + return False # More than one version component is not allowed + for i in lst[0]: + if not i.isalnum(): + if i != "_": + return False + if len(lst) > 1: + return validateTagVersion(lst[1]) + return True + + +class SoftwareTag: + def __init__(self, tag): + """ Takes tag and parses it. If it can't parse - raises exception of invalid value + :type tag: str + """ + if not ((type(tag) == str) or (type(tag) == unicode)): + raise (BaseException("Invalid input data-type")) + if not validateTag(tag): + raise (BaseException("Malformed tag")) + tag_version = tag.split(':') + self.tag = tag_version[0].lower() # No, this should be lowercase + self.version = None if len(tag_version) == 1 else tag_version[1] + + def __repr__(self): + return "SoftwareTag()" + + def __str__(self): + if self.version: + return "%s:%s" % (self.tag, self.version) + else: + return self.tag + + def has_version(self): + return self.version is not None + + def match(self, tag): # self is "on server", tag is "request" + if self.tag == tag.tag: + # Names are the same, that is good, matching versions now. + if self.version == tag.version: + return True + else: + if tag.version is None: + return True # qt in request, anything else on server - True + if self.version is not None and self.version.startswith(tag.version + "."): + return True + return False + return False + + def make_regex(self): + if self.version is None: + # versionless tag + temp_string = re.escape(self.tag) + regex = "(%s:[a-z0-9_.]*)|(%s)" % (temp_string, temp_string,) + else: + # tag with versions + temp_string = re.escape("%s:%s" % (self.tag, self.version)) + regex = "(%s\.[a-z0-9_.]*)|(%s)" % (temp_string, temp_string) + return regex + + +class SoftwareTagList: + def __init__(self): + # dictionary of tags, key is - tag name + self.taglist = dict() + + def __str__(self): + lst = list() + for key, value in self.taglist.items(): + lst += [str(i) for i in value] + lst.sort() + return ",".join(lst) + + def __repr__(self): + return "SoftwareTagList()" + + def __getitem__(self, item): + return self.taglist[item] + + def parse(self, tag_string): + self.taglist = dict() + try: + return all(self.append(SoftwareTag(i)) for i in tag_string.split(',')) + except: + return False + + def has_version(self, tag_name): + if tag_name in self.taglist: + # This check is possible, because, when there is tag without version - it is the only tag in the list + if self.taglist[tag_name][0].has_version(): + return True + return False + + def append(self, tag): + # tag should be SoftwareTag, return false or raise exception in case it is not so + if tag.has_version(): + if tag.tag in self.taglist: + # Tag in list - need to check version + if self.has_version(tag.tag) and not any(tag.match(i) for i in self.taglist[tag.tag]): + self.taglist[tag.tag].append(tag) + self.taglist[tag.tag].sort() # this is slow, I guess + else: + # Tag not in list - just add it. + self.taglist[tag.tag] = [tag, ] + else: + # tag without version tag + self.taglist[tag.tag] = [tag, ] + return True + + def is_empty(self): + return len(self.taglist) == 0 + + def make_regex(self): + lst = list() + for key, value in self.taglist.items(): + regex = "(^|,)%s(,|$)" % "|".join([i.make_regex() for i in value]) + lst.append(regex) + return lst + + def match_positive(self, taglist): + # checks that everything from tag list matches current tags + # Start checking with checking if all requested tags in taglist are present in self.taglist + for i in taglist.taglist: + if i not in self.taglist: + return False + # Now we need to check if versions are matching + for tag in taglist.taglist: + if not self.has_version(tag): + # If package tag accepts anything - it already matches, next please + continue + if taglist.has_version(tag) and not any(v1.match(v) for v in taglist[tag] for v1 in self[tag]): + return False + return True + + def match_negative(self, taglist): + # checks that nothing from taglist matches current tags + for i in taglist.taglist: + if i in self.taglist: + if (not taglist.has_version(i)) or (not self.has_version(i)): + return False + # Tag found, version list is present. check if it matches, if it does - check is failed + for version in taglist[i]: + for version1 in self[i]: + if version1.match(version): + return False + return True + + def hash(self): + # Looks like the list is sorted, but well... + return hashlib.md5(str(self)).hexdigest() + + +class TestSoftwareTagMethods(unittest.TestCase): + def test_tag_creation(self): + tag = SoftwareTag('qt') + self.assertFalse(tag.has_version()) + tag = SoftwareTag('qt:5.01') + self.assertTrue(tag.has_version()) + tag = SoftwareTag('qt:5.01_asdf_the_version') + self.assertTrue(tag.has_version()) + tag = SoftwareTag('Qt') + self.assertFalse(tag.has_version()) + with self.assertRaisesRegexp(BaseException, "Malformed tag"): + SoftwareTag('фыва') + with self.assertRaisesRegexp(BaseException, "Malformed tag"): + SoftwareTag('фыва:5.1') + with self.assertRaisesRegexp(BaseException, "Malformed tag"): + SoftwareTag('qt.1:5.1') + with self.assertRaisesRegexp(BaseException, "Invalid input data-type"): + SoftwareTag(1) + + def test_tag_match(self): + tag_13 = SoftwareTag('qt:1.3') + tag_12 = SoftwareTag('qt:1.2') + tag_121 = SoftwareTag('qt:1.2.1') + tag_122 = SoftwareTag('qt:1.2.2') + tag = SoftwareTag('qt') + tag2 = SoftwareTag('neptune') + self.assertFalse(tag_12.match(tag_13)) + self.assertFalse(tag_13.match(tag_12)) + self.assertTrue(tag_121.match(tag_12)) + self.assertTrue(tag_122.match(tag_12)) + self.assertTrue(tag_121.match(tag_121)) + self.assertFalse(tag_12.match(tag_121)) + self.assertFalse(tag.match(tag2)) + self.assertTrue(tag_13.match(tag)) + self.assertFalse(tag.match(tag_13)) + + +class TestSoftwareTagListMethods(unittest.TestCase): + def test_empty(self): + lst = SoftwareTagList() + self.assertTrue(lst.is_empty()) + + def test_not_empty_after_append(self): + lst = SoftwareTagList() + lst.append(SoftwareTag('qt')) + self.assertFalse(lst.is_empty()) + + def test_empty_matches_everything(self): + empty_list = SoftwareTagList() + test_list = SoftwareTagList() + test_list.append(SoftwareTag('qt')) + self.assertTrue(test_list.match_positive(empty_list)) + self.assertTrue(test_list.match_negative(empty_list)) + + def test_match_positive(self): + list_to_test = SoftwareTagList() + list_to_test.parse("qt:5.1,neptune,test:1,second_test") + matching_list = SoftwareTagList() + matching_list.parse("qt") + self.assertTrue(list_to_test.match_positive(matching_list)) + matching_list.parse("qt:5.1") + self.assertTrue(list_to_test.match_positive(matching_list)) + matching_list.parse("qt:5.1,qt:5.2,neptune:1") + self.assertTrue(list_to_test.match_positive(matching_list)) + matching_list.parse("qt:5.1,test:2") + self.assertFalse(list_to_test.match_positive(matching_list)) + matching_list.parse("qt:5.1.1") + self.assertFalse(list_to_test.match_positive(matching_list)) + + def test_match_negative(self): + list_to_test = SoftwareTagList() + list_to_test.parse("qt:5.1,neptune") + matching_list = SoftwareTagList() + matching_list.parse("qt") + self.assertFalse(list_to_test.match_negative(matching_list)) + matching_list.parse("qt:5.1") + self.assertFalse(list_to_test.match_negative(matching_list)) + matching_list.parse("qt:5.1,qt:5.2,neptune:1") + self.assertFalse(list_to_test.match_negative(matching_list)) + matching_list.parse("qt:5.1,qt:5.2") + self.assertFalse(list_to_test.match_negative(matching_list)) + matching_list.parse("test") + self.assertTrue(list_to_test.match_negative(matching_list)) + + def test_append_invalid(self): + lst = SoftwareTagList() + with self.assertRaisesRegexp(BaseException, "Malformed tag"): + self.assertFalse(lst.append(SoftwareTag('qt:1:1'))) # Invalid version + with self.assertRaisesRegexp(BaseException, "Malformed tag"): + self.assertFalse(lst.append(SoftwareTag('фыва'))) # Non-ascii + with self.assertRaisesRegexp(BaseException, "Malformed tag"): + self.assertFalse(lst.append(SoftwareTag(''))) # empty tag is not valid + + def test_append_valid(self): + lst = SoftwareTagList() + # capital letters should be treated as lowercase + self.assertTrue(lst.append(SoftwareTag('QT'))) + # underscore is allowed, capital letters should be treated as lowercase + self.assertTrue(lst.append(SoftwareTag('QT_something'))) + # Version is valid, tag is valid too + self.assertTrue(lst.append(SoftwareTag('qt:1.1.1'))) + + def test_parsing_positive(self): + lst = SoftwareTagList() + self.assertTrue(lst.parse('qt')) + self.assertTrue(lst.parse('qt:5')) + self.assertTrue(lst.parse('qt:5.1')) + self.assertTrue(lst.parse('qt:5.1,qt:5.2')) + self.assertTrue(lst.parse('qt:5.1,qt:5.2,neptune')) + self.assertTrue(lst.parse('qt:5.1,qt:5.2,neptune:5.1,neptune:5.2')) + # This should equal to qt:5.1,qt:5.2,neptune:5.1,neptune:5.2 - due to matching + self.assertTrue(lst.parse('qt:5.1,qt:5.2,qt:5.2,qt:5.2.1,neptune:5.1,neptune:5.2')) + # This equals to: qt, neptune, due to matching + self.assertTrue(lst.parse('qt,qt:5.2,neptune:5.1,neptune')) + + def test_parsing_negative(self): + lst = SoftwareTagList() + self.assertFalse(lst.parse(',,')) # empty tags + self.assertFalse(lst.parse('фыва')) # non-ascii + self.assertFalse(lst.parse('qt:5.1:5.2,qt')) # multiple versions + + def test_hashes_does_not_depend_on_order(self): + lst1 = SoftwareTagList() + lst2 = SoftwareTagList() + self.assertTrue(lst1.parse('qt:5,qt:4,neptune:1')) + self.assertTrue(lst2.parse('neptune:1,qt:4,qt:5')) + self.assertEqual(lst1.hash(), lst2.hash()) + + def test_different_list_different_hash(self): + lst1 = SoftwareTagList() + lst2 = SoftwareTagList() + self.assertTrue(lst1.parse('qt:5,neptune:2')) + self.assertTrue(lst2.parse('neptune:1,qt:5')) + self.assertNotEqual(lst1.hash(), lst2.hash()) + + +if __name__ == '__main__': + unittest.main() diff --git a/store/utilities.py b/store/utilities.py index bce9a7b..0f5465b 100644 --- a/store/utilities.py +++ b/store/utilities.py @@ -44,24 +44,17 @@ from M2Crypto import SMIME, BIO, X509 from OpenSSL.crypto import load_pkcs12, FILETYPE_PEM, dump_privatekey, dump_certificate from django.conf import settings +from tags import SoftwareTagList, SoftwareTag import osandarch -def validateTag(tag): - for i in tag: - if not i.isalnum(): - if i != "_": - return False - return True - def makeTagList(pkgdata): - taglist = set() + taglist = SoftwareTagList() for fields in ('extra', 'extraSigned'): if fields in pkgdata['header']: if 'tags' in pkgdata['header'][fields]: - tags = set(pkgdata['header'][fields]['tags']) # Fill tags list then add them - taglist = taglist.union(tags) - tags = ','.join(taglist) - return tags + for i in list(pkgdata['header'][fields]['tags']): # Fill tags list then add them + taglist.append(SoftwareTag(i)) + return str(taglist) def packagePath(appId = None, architecture = None): path = settings.MEDIA_ROOT + 'packages/' -- cgit v1.2.3 From 43b69a8dff2c0206c98d1cf33d47902a2e536a28 Mon Sep 17 00:00:00 2001 From: Dominik Holland Date: Thu, 13 Dec 2018 10:37:33 +0100 Subject: Qmake harness for building the documentation with qdoc Change-Id: I8ab555de921ce5ff655494d5e67dc10143242947 Reviewed-by: Robert Griebl --- .qmake.conf | 3 +++ doc/online/qtautodeploymentserver.qdocconf | 11 ++++++++++ doc/qtautodeploymentserver-project.qdocconf | 24 +++++++++++++++++++++ doc/qtautodeploymentserver.qdocconf | 2 ++ doc/src/deployment-server.qdoc | 33 +++++++++++++++++++++++++++++ qtauto-deployment-server.pro | 13 ++++++++++++ 6 files changed, 86 insertions(+) create mode 100644 .qmake.conf create mode 100644 doc/online/qtautodeploymentserver.qdocconf create mode 100644 doc/qtautodeploymentserver-project.qdocconf create mode 100644 doc/qtautodeploymentserver.qdocconf create mode 100644 doc/src/deployment-server.qdoc create mode 100644 qtauto-deployment-server.pro diff --git a/.qmake.conf b/.qmake.conf new file mode 100644 index 0000000..9bdff44 --- /dev/null +++ b/.qmake.conf @@ -0,0 +1,3 @@ +MODULE_VERSION = 5.12 + +CONFIG += prepare_docs qt_docs_targets diff --git a/doc/online/qtautodeploymentserver.qdocconf b/doc/online/qtautodeploymentserver.qdocconf new file mode 100644 index 0000000..95a4f9e --- /dev/null +++ b/doc/online/qtautodeploymentserver.qdocconf @@ -0,0 +1,11 @@ +include($QT_INSTALL_DOCS/global/qt-module-defaults-online.qdocconf) + +# Add an .html file with sidebar content, used in the online style +# HTML.stylesheets += style/qt5-sidebar.html + +# override defaults for Qt module documentation +HTML.nosubdirs = "false" +HTML.outputsubdir = "qtauto-deployment-server" + +include(../qtautodeploymentserver-project.qdocconf) + diff --git a/doc/qtautodeploymentserver-project.qdocconf b/doc/qtautodeploymentserver-project.qdocconf new file mode 100644 index 0000000..65a4fd7 --- /dev/null +++ b/doc/qtautodeploymentserver-project.qdocconf @@ -0,0 +1,24 @@ +project = QtAutoDeploymentServer +description = Qt Auto Deployment Server Documentation +version = $QT_VERSION +url = http://doc.qt.io/QtAutoDeploymentServer + +sourcedirs += src +imagedirs += src/images + +qhp.projects = QtAutoDeploymentServer + +qhp.QtAutoDeploymentServer.file = qtautodeploymentserver.qhp +qhp.QtAutoDeploymentServer.namespace = org.qt-project.qtautodeploymentserver.$QT_VERSION_TAG +qhp.QtAutoDeploymentServer.virtualFolder = qtautodeploymentserver +qhp.QtAutoDeploymentServer.indexTitle = Qt Auto Deployment Server +qhp.QtAutoDeploymentServer.indexRoot = + +qhp.QtAutoDeploymentServer.filterAttributes = qtautodeploymentserver $QT_VERSION qtrefdoc +qhp.QtAutoDeploymentServer.customFilters.Qt.name = QtAutoDeploymentServer $QT_VERSION +qhp.QtAutoDeploymentServer.customFilters.Qt.filterAttributes = qtautodeploymentserver $QT_VERSION + +tagfile = qtautodeploymentserver.tags + +buildversion = "Qt Auto Deploymment Server $QT_VERSION" +navigation.homepage = "Qt Auto Deployment Server" diff --git a/doc/qtautodeploymentserver.qdocconf b/doc/qtautodeploymentserver.qdocconf new file mode 100644 index 0000000..fa27088 --- /dev/null +++ b/doc/qtautodeploymentserver.qdocconf @@ -0,0 +1,2 @@ +include($QT_INSTALL_DOCS/global/qt-module-defaults-offline.qdocconf) +include(qtautodeploymentserver-project.qdocconf) diff --git a/doc/src/deployment-server.qdoc b/doc/src/deployment-server.qdoc new file mode 100644 index 0000000..3a8a7c9 --- /dev/null +++ b/doc/src/deployment-server.qdoc @@ -0,0 +1,33 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Auto Deployment Server. +** +** $QT_BEGIN_LICENSE:FDL-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \page qtauto-deployment-server-index.html + \title Qt Auto Deployment Server + +*/ diff --git a/qtauto-deployment-server.pro b/qtauto-deployment-server.pro new file mode 100644 index 0000000..f7f9002 --- /dev/null +++ b/qtauto-deployment-server.pro @@ -0,0 +1,13 @@ +TEMPLATE = aux + +build_online_docs: { + QMAKE_DOCS = $$PWD/doc/online/qtautodeploymentserver.qdocconf +} else { + QMAKE_DOCS = $$PWD/doc/qtautodeploymentserver.qdocconf +} + +OTHER_FILES += \ + $$PWD/doc/*.qdocconf \ + $$PWD/doc/src/*.qdoc + +load(qt_docs) -- cgit v1.2.3 From 1a50e4e42689ba2211e145effdd779247957aa98 Mon Sep 17 00:00:00 2001 From: Robert Griebl Date: Thu, 13 Dec 2018 13:34:03 +0100 Subject: Update copyright statements - added 2019 - everything is (c) Luxoft instead of Pelagicore from now on Change-Id: If1b2f18f41dff7531e0c48329f9bf555192cbc2a Reviewed-by: Nikolay Zamotaev --- README.md | 2 +- appstore/settings.py | 3 ++- appstore/urls.py | 3 ++- appstore/wsgi.py | 3 ++- header.GPL-QTAS | 3 ++- manage.py | 3 ++- store/admin.py | 3 ++- store/api.py | 3 ++- store/authdecorators.py | 3 ++- store/management/commands/expire-downloads.py | 3 ++- store/management/commands/store-sign-package.py | 3 ++- store/management/commands/store-upload-package.py | 3 ++- store/management/commands/verify-upload-package.py | 3 ++- store/migrations/0001_initial.py | 3 ++- store/models.py | 3 ++- store/osandarch.py | 1 + store/tags.py | 1 + store/utilities.py | 3 ++- 18 files changed, 33 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index ebd0327..28a473e 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ This is a PoC deployment server, which can be used together with -the Neptune IVI UI and the Pelagicore Application Manager. +the Neptune IVI UI and the Luxoft Application Manager. **This is a development server only - do NOT use in production.** diff --git a/appstore/settings.py b/appstore/settings.py index 377843a..a405491 100644 --- a/appstore/settings.py +++ b/appstore/settings.py @@ -1,6 +1,7 @@ ############################################################################# ## -## Copyright (C) 2016 Pelagicore AG +## Copyright (C) 2019 Luxoft Sweden AB +## Copyright (C) 2018 Pelagicore AG ## Contact: https://www.qt.io/licensing/ ## ## This file is part of the Neptune Deployment Server diff --git a/appstore/urls.py b/appstore/urls.py index 327c28b..4d0467f 100644 --- a/appstore/urls.py +++ b/appstore/urls.py @@ -1,6 +1,7 @@ ############################################################################# ## -## Copyright (C) 2016 Pelagicore AG +## Copyright (C) 2019 Luxoft Sweden AB +## Copyright (C) 2018 Pelagicore AG ## Contact: https://www.qt.io/licensing/ ## ## This file is part of the Neptune Deployment Server diff --git a/appstore/wsgi.py b/appstore/wsgi.py index 9e80014..3f007d4 100644 --- a/appstore/wsgi.py +++ b/appstore/wsgi.py @@ -1,6 +1,7 @@ ############################################################################# ## -## Copyright (C) 2016 Pelagicore AG +## Copyright (C) 2019 Luxoft Sweden AB +## Copyright (C) 2018 Pelagicore AG ## Contact: https://www.qt.io/licensing/ ## ## This file is part of the Neptune Deployment Server diff --git a/header.GPL-QTAS b/header.GPL-QTAS index 0bd19ca..eb625be 100644 --- a/header.GPL-QTAS +++ b/header.GPL-QTAS @@ -1,6 +1,7 @@ ############################################################################# ## -## Copyright (C) 2016 Pelagicore AG +## Copyright (C) 2019 Luxoft Sweden AB +## Copyright (C) 2018 Pelagicore AG ## Contact: https://www.qt.io/licensing/ ## ## This file is part of the Neptune Deployment Server diff --git a/manage.py b/manage.py index bd587ad..ad7604e 100644 --- a/manage.py +++ b/manage.py @@ -1,6 +1,7 @@ ############################################################################# ## -## Copyright (C) 2016 Pelagicore AG +## Copyright (C) 2019 Luxoft Sweden AB +## Copyright (C) 2018 Pelagicore AG ## Contact: https://www.qt.io/licensing/ ## ## This file is part of the Neptune Deployment Server diff --git a/store/admin.py b/store/admin.py index a921e2f..eff2550 100644 --- a/store/admin.py +++ b/store/admin.py @@ -1,6 +1,7 @@ ############################################################################# ## -## Copyright (C) 2016 Pelagicore AG +## Copyright (C) 2019 Luxoft Sweden AB +## Copyright (C) 2018 Pelagicore AG ## Contact: https://www.qt.io/licensing/ ## ## This file is part of the Neptune Deployment Server diff --git a/store/api.py b/store/api.py index b9dc0e6..0ed54b1 100644 --- a/store/api.py +++ b/store/api.py @@ -1,6 +1,7 @@ ############################################################################# ## -## Copyright (C) 2016 Pelagicore AG +## Copyright (C) 2019 Luxoft Sweden AB +## Copyright (C) 2018 Pelagicore AG ## Contact: https://www.qt.io/licensing/ ## ## This file is part of the Neptune Deployment Server diff --git a/store/authdecorators.py b/store/authdecorators.py index fbbc01d..2a4119c 100644 --- a/store/authdecorators.py +++ b/store/authdecorators.py @@ -1,6 +1,7 @@ ############################################################################# ## -## Copyright (C) 2018 Luxoft +## Copyright (C) 2019 Luxoft Sweden AB +## Copyright (C) 2018 Pelagicore AG ## Contact: https://www.qt.io/licensing/ ## ## This file is part of the Neptune Deployment Server diff --git a/store/management/commands/expire-downloads.py b/store/management/commands/expire-downloads.py index edc5235..94b0d24 100644 --- a/store/management/commands/expire-downloads.py +++ b/store/management/commands/expire-downloads.py @@ -1,6 +1,7 @@ ############################################################################# ## -## Copyright (C) 2016 Pelagicore AG +## Copyright (C) 2019 Luxoft Sweden AB +## Copyright (C) 2018 Pelagicore AG ## Contact: https://www.qt.io/licensing/ ## ## This file is part of the Neptune Deployment Server diff --git a/store/management/commands/store-sign-package.py b/store/management/commands/store-sign-package.py index 479b4c4..b1a42d0 100644 --- a/store/management/commands/store-sign-package.py +++ b/store/management/commands/store-sign-package.py @@ -1,6 +1,7 @@ ############################################################################# ## -## Copyright (C) 2016 Pelagicore AG +## Copyright (C) 2019 Luxoft Sweden AB +## Copyright (C) 2018 Pelagicore AG ## Contact: https://www.qt.io/licensing/ ## ## This file is part of the Neptune Deployment Server diff --git a/store/management/commands/store-upload-package.py b/store/management/commands/store-upload-package.py index 81f96fa..77286bc 100644 --- a/store/management/commands/store-upload-package.py +++ b/store/management/commands/store-upload-package.py @@ -1,6 +1,7 @@ ############################################################################# ## -## Copyright (C) 2018 Luxoft +## Copyright (C) 2019 Luxoft Sweden AB +## Copyright (C) 2018 Pelagicore AG ## Contact: https://www.qt.io/licensing/ ## ## This file is part of the Neptune Deployment Server diff --git a/store/management/commands/verify-upload-package.py b/store/management/commands/verify-upload-package.py index 87ac08a..59df116 100644 --- a/store/management/commands/verify-upload-package.py +++ b/store/management/commands/verify-upload-package.py @@ -1,6 +1,7 @@ ############################################################################# ## -## Copyright (C) 2016 Pelagicore AG +## Copyright (C) 2019 Luxoft Sweden AB +## Copyright (C) 2018 Pelagicore AG ## Contact: https://www.qt.io/licensing/ ## ## This file is part of the Neptune Deployment Server diff --git a/store/migrations/0001_initial.py b/store/migrations/0001_initial.py index ae4bd24..51b0677 100644 --- a/store/migrations/0001_initial.py +++ b/store/migrations/0001_initial.py @@ -1,7 +1,8 @@ # -*- coding: utf-8 -*- ############################################################################# ## -## Copyright (C) 2016 Pelagicore AG +## Copyright (C) 2019 Luxoft Sweden AB +## Copyright (C) 2018 Pelagicore AG ## Contact: https://www.qt.io/licensing/ ## ## This file is part of the Neptune Deployment Server diff --git a/store/models.py b/store/models.py index dd3b4a1..eb3ede9 100644 --- a/store/models.py +++ b/store/models.py @@ -1,6 +1,7 @@ ############################################################################# ## -## Copyright (C) 2016 Pelagicore AG +## Copyright (C) 2019 Luxoft Sweden AB +## Copyright (C) 2018 Pelagicore AG ## Contact: https://www.qt.io/licensing/ ## ## This file is part of the Neptune Deployment Server diff --git a/store/osandarch.py b/store/osandarch.py index c7d15a6..9a2db12 100644 --- a/store/osandarch.py +++ b/store/osandarch.py @@ -1,5 +1,6 @@ ############################################################################# ## +## Copyright (C) 2019 Luxoft Sweden AB ## Copyright (C) 2018 Pelagicore AG ## Contact: https://www.qt.io/licensing/ ## diff --git a/store/tags.py b/store/tags.py index 183a90a..aa73006 100644 --- a/store/tags.py +++ b/store/tags.py @@ -1,6 +1,7 @@ # vim: set fileencoding=utf-8 : ############################################################################# ## +## Copyright (C) 2019 Luxoft Sweden AB ## Copyright (C) 2018 Pelagicore AG ## Contact: https://www.qt.io/licensing/ ## diff --git a/store/utilities.py b/store/utilities.py index 0f5465b..42f78f7 100644 --- a/store/utilities.py +++ b/store/utilities.py @@ -1,6 +1,7 @@ ############################################################################# ## -## Copyright (C) 2016 Pelagicore AG +## Copyright (C) 2019 Luxoft Sweden AB +## Copyright (C) 2018 Pelagicore AG ## Contact: https://www.qt.io/licensing/ ## ## This file is part of the Neptune Deployment Server -- cgit v1.2.3 From 658e70fa01ad88cbfaaf166319c54a69792336e9 Mon Sep 17 00:00:00 2001 From: Jukka Jokiniva Date: Mon, 17 Dec 2018 15:02:26 +0200 Subject: Add missing header.FDL-QTAS template Change-Id: I092f279030a70586697b2947ec35653f1e938f0b Reviewed-by: Dominik Holland --- header.FDL-QTAS | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 header.FDL-QTAS diff --git a/header.FDL-QTAS b/header.FDL-QTAS new file mode 100644 index 0000000..6cac945 --- /dev/null +++ b/header.FDL-QTAS @@ -0,0 +1,27 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Auto Deployment Server. +** +** $QT_BEGIN_LICENSE:FDL-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ -- cgit v1.2.3 From c31a12d694ed3862cb03f305b4a09c9e028ee299 Mon Sep 17 00:00:00 2001 From: Nikolay Zamotaev Date: Fri, 14 Dec 2018 17:13:14 +0300 Subject: Documentation for qtauto deployment server First version. Change-Id: I44132467baedf41812e12f67170005d2508dec1e Fixes: AUTOSUITE-625 Reviewed-by: Svetlana Abramenkova --- doc/src/deployment-server.qdoc | 446 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 446 insertions(+) diff --git a/doc/src/deployment-server.qdoc b/doc/src/deployment-server.qdoc index 3a8a7c9..c505c9b 100644 --- a/doc/src/deployment-server.qdoc +++ b/doc/src/deployment-server.qdoc @@ -29,5 +29,451 @@ /*! \page qtauto-deployment-server-index.html \title Qt Auto Deployment Server + \section1 Introduction + Qt Automotive Suite Deployment server is a reference implementation of a network resource for + hosting and distributing applications in projects based on Qt Application Manager. + Even though the Deployment Server can be used in field for real products, the main purpose + for it is helping development and integration teams to simplify development and testing of + applications on target system. + \section1 Assumptions + \list + \li Applications are identified by a group of (Application ID, Version, Architecture, Tags). + Such groups are unique. + \li Architecture is specified as a group of: (CPU architecture, endianness, bitness and OS). + If package does not contain architecture specific parts, architecture is specified as “All”. + \li CPU architecture is taken as returned by QsysInfo::buildCpuArchitecture(). + \li As it is hard to determine OS for uploaded packages, OS-es are matched by binary format, + requesting anything unix-like will give packages with ELF binaries, anything Windows related + will return packages with PE32 format binaries and packages with Mach_o binaries are returned + for Apple products. See \l {http://doc.qt.io/qt-5/qsysinfo.html#kernelType} {QSysInfo::kernelType()} for more details. + \li When native and non-native applications match selection criteria, native application is preferred. + \li Applications can be further filtered by tags, both as positive (must have) and negative + (must not have) filters. + \li Tags are considered alphanumeric and can contain lowercase latin letters, numbers and + underscore symbol. All tags passed to the server are brought to lowercase. + \li Tags can optionally have version. Version is separated from tag by ':'. Version follows the same + limitations as tag. + \li Tags are matched according to versions (when requesting version 5.12, it will match 5.12.0, but not vice versa). + Also, when requesting non-versioned tag - any version will match. + \li Tag lists in requests and packages are simplified (so qt:5.12,qt:5.12.0 will end up as qt:5.12) + \li \l {https://doc.qt.io/QtApplicationManager/manifest.html#basic-manifest} {Application manifest} + allows for any number of categories assigned to application. Deployment server currently + ignores categories in application manifest and requires manual assignment of exactly one + category to the application. This behavior can be discussed with nzamotaev@luxoft.com . + \li Tag information is parsed from package header's \b{extra} and \b{extraSigned} parts, + from \b{tags} array. All elements of that array are added to package’s tag list. + \li Every package has version. If manifest does not contain version, + it will be assigned version “0.0.0”. + \endlist + + \section1 API + \section2 hello + Checks whether you are using the right Platform and the right API to communicate with the Server. + \table + \header + \li Parameter + \li Description + \row + \li platform + \li The platform the client is running on, this sets the architecture of the packages you get. (see \b{settings.APPSTORE_PLATFORM}) + \row + \li version + \li The Deployment Server HTTP API version you are using to communicate with the server. (see \b{settings.APPSTORE_VERSION}) + \row + \li require_tag + \li Optional parameter for filtering packages by tags. Receives comma-separated list of tags. + Only applications containing any of specified tags should be listed. Tags must be alphanumeric. + \row + \li conflicts_tag + \li Optional parameter for filtering packages by tags. Receives comma-separated list of tags. + No application containing any of the tags should be listed. Tags must be alphanumeric. + \row + \li architecture + \li ptional parameter for filtering packages by architecture. Receives cpu architecture. + If architecture is not specified, only packages showing 'All' architecture are listed. + \endtable + Returns a JSON object: + \table + \header + \li JSON field + \li Value + \li Description + \row + \li {1,5} status + \li ok + \li Successful + \row + \li maintenance + \li Server is in maintenance mode and can not be used at the moment. + \row + \li incompatible-platform + \li You are using incompatible platform. + \row + \li incompatible-version + \li You are using incompatible version of the API + \row + \li malformed-tag + \li Tag had wrong format, was not alphanumeric or could not be parsed. + \endtable + \section2 login + Does a login on the Server with the given username and password. + Either a IMEI or \l {https://en.wikipedia.org/wiki/MAC_address} {other unique hardware identifier} must be provided. This call is needed for downloading apps. + \table + \header + \li Parameter + \li Description + \row + \li username + \li The username + \row + \li password + \li Password for given username + \endtable + Returns a JSON object: + \table + \header + \li JSON field + \li Value + \li Description + \row + \li {1,4} status + \li ok + \li Login successful. + \row + \li missing-credentials + \li No username and/or password provided + \row + \li account-disabled + \li Account is disabled in admin panel + \row + \li authentication-failed + \li Wrong username and/or password or other authentication error + \endtable + \section2 logout + Logs out currently logged in user on server. + + Returns a JSON object: + \table + \header + \li JSON field + \li Value + \li Description + \row + \li {1,2} status + \li ok + \li Successfully logged out + \row + \li failed + \li Was not logged in + \endtable + + \section2 app/list + Lists all apps. Returned List can be filtered by using the \b{category_id} and the \b{filter} arguments. + \table + \header + \li Parameter + \li Description + \row + \li category_id + \li Limits applications to category with this id. + \row + \li filter + \li Only lists apps, whose names match the filter. + \endtable + Returns a JSON array (\b{not an object!}). Each field is a JSON object: + \table + \header + \li JSON field + \li Description + \row + \li id + \li Unique application id. In reverse domain name notation + \row + \li name + \li Application name + \row + \li vendor + \li Vendor name for application (not vendor id) + \row + \li category + \li Category name for application + \row + \li tags + \li JSON array of application tags + \row + \li version + \li Application version. Returned as string. If application information lacks version, “0.0.0” is returned + \row + \li architecture + \li Application architecture. Returned as detected in library components of the application + + If application is not native, contains “All”. Otherwise it is formed like this: mips-little_endian-32-elf + + Where it is a combination of: + \list 1 + \li CPU architecture, as returned by QsysInfo::buildCpuArchitecture() + \li CPU endianness (either \b{little_endian} or \b{big_endian}) + \li ABI bitness + \li binary format (\b{elf}, \b{mach_o} or \b{pe32}) + \endlist + + \row + \li briefDescription + \li Short textual app description (should be limited to 1 line, around 80-130 characters) + \row + \li category_id + \li Numeric category id matching application category field. + \endtable + \section2 app/icon + Returns an icon for the given application id. + \table + \header + \li Parameter + \li Description + \row + \li id + \li Application id + \endtable + Returns a PNG image or a 404 error, if application does not exist + \section2 app/description + Returns a description for the given application id. + \table + \header + \li Parameter + \li Description + \row + \li id + \li Application id + \endtable + Returns application description text - either HTML or plain text. + \section2 app/purchase + Returns an url which can be used for downloading the requested application for certain period of time (configurable in the settings). + \note This is legacy from AppStore, changing the name of this API would involve changes in reference UI. + The real action is preparing package for download. For discussion, contact nzamotaev@luxoft.com. + \table + \header + \li Parameter + \li Description + \row + \li device_id + \li Unique device id for client hardware (currently unused) + \row + \li id + \li Application id + \endtable + Returns a JSON object: + \table + \header + \li JSON field + \li Value + \li Description + \row + \li {1,2} status + \li ok + \li Successful + \row + \li failed + \li Something went wrong. Check error field for more information. + \row + \li error + \li text + \li If status equals to ‘failed’, contains error description. + \row + \li url + \li URL + \li URL for downloading application. Expires in time specified in ‘expiresIn’ field. + \row + \li expiresIn + \li Integer value + \li Time in seconds for which download url stays valid. + \endtable + + \section2 category/list + Lists all the available categories. Also returns ‘All’ metacategory, that is used to hold all available applications. + Returns a JSON array (not an object!). Each field is a JSON object: + \table + \header + \li JSON field + \li Description + \row + \li id + \li Unique category id. + \row + \li name + \li Category name. + \endtable + \section2 category/icon + Returns an icon for the given category id. + \table + \header + \li Parameter + \li Description + \row + \li id + \li Id of the category. + \endtable + Returns a PNG image or empty 1x1 PNG file. + \note Currently takes icon of the first application in the category if it exists. This should be fixed. + \section2 upload + Accepts remote package upload requests. + User must be in the “staff” group to use this API. Also requires either basic authentication, + or previous call to \b{login} method. This is a POST request to server, due to parameters used. + \table + \header + \li Parameter + \li Description + \row + \li description + \li Package description, long version. Can be text or HTML. + \row + \li short-description + \li One line package description. + \row + \li category + \li Category name for the category where the package will be put. + \row + \li vendor + \li Vendor name for the package. + \row + \li package + \li Package itself. This is uploaded as a file parameter. + \endtable + Returns JSON object: + \table + \header + \li Parameter + \li Value + \li Description + \row + \li {1,9} status + \li ok + \li Success + \row + \li no description + \li Description parameter missing + \row + \li no short description + \li Short-description parameter missing + \row + \li no category + \li Category parameter missing + \row + \li no vendor + \li Vendor parameter missing + \row + \li Package validation failed + \li Package did not pass format/sanity validation + \row + \li Non-existing category + \li No category matches passed parameter + \row + \li Non-existing vendor + \li No vendor matches passed parameter + \row + \li no package to upload + \li There was no ‘package’ parameter in request, or it was not a POST request + \endtable + + \section2 API use examples + The deployment server exposes a HTTP API to the world. Arguments to the functions need to be provided using the + HTTP GET/POST syntax. Returned data will be in JSON, PNG or text format, depending on the function. + \section3 Basic workflow + \list 1 + \li Send a \b{hello} request to the server to get the current status and check + whether your platform is compatible with this server instance: + + \tt http:///hello?platform=AM&version=1 + + Returns: + + \tt { { "status": "ok" } } + + \li Login as user \b{user} with password \b{pass}: + + \tt http:///login?username=user&password=pass + + Returns: + + \tt { { "status": "ok" } } + + \li List all applications + + \tt http:///app/list + + Returns: + + \tt { [{ "category": "Entertainment", + "name": "Nice App", + "vendor": "Luxoft", + "briefDescription": "Nice App is a really nice app.", + "category_id": 4, + "id": "com.luxoft.niceapp"}, + .... + ] } + + + \li Request a download for a App: + + \tt http:///app/purchase?device_id=12345&id=com.luxoft.niceapp + + Returns: + + \tt { { "status": "ok", + "url": "http:///app/download/com.luxoft.niceapp.2.npkg", + "expiresIn": 600 + } } + + \li Use the \b{url} provided in step 4 to download the application within + \b{expiresIn} seconds. + + \endlist + + \section1 Installation + \section2 Setting up the server in virtualenv + \code + virtualenv ./venv + ./venv/bin/pip install -r requirements.txt + \endcode + (libffi-dev is also needed)a + + Before running the server, make sure to adapt the \b{APPSTORE_*} settings in \b{appstore/settings.py} to your environment. + + Since package downloads are done via temporary files, one needs to setup a cron-job to cleanup + these temporary files every now and then. The job should be triggered every \b{settings.APPSTORE_DOWNLOAD_EXPIRY/2} + minutes and it just needs to execute: + \code + ./manage.py expire-downloads + \endcode + \section2 Starting the server + Running the server: + \code + ./manage.py runserver 0.0.0.0:8080 + \endcode + will start the server on port 8080, reachable for anyone. One can tweak the listening address to whatever fits the needs. + \section2 Maintenance tasks + \list + \li Cleaning up the downloads directory: + \code + ./manage.py expire-downloads + \endcode + will remove all files from the downloads/ directory, that are older than + \b{settings.APPSTORE_DOWNLOAD_EXPIRY} minutes. This should be called from a cron-job (see above). + \li Manually verifying a package for upload: + \code + ./manage.py verify-upload-package + \endcode + will tell if \b{} is a valid package that can be uploaded to the store. + \li Manually adding a store signature to a package: + \code + ./manage.py store-sign-package [device id] + \endcode + will first verify \b{}. If this succeeds, it will copy \b{} to \b{} and + add a store signature. The optional \b{[device id]} parameter will lock the generated package to the device with this id. + \endlist + + \section1 Architecture + This is a django application. + + \b{TBD} */ -- cgit v1.2.3 From 55dfdca96b04ba0e54ca16001a6411fd5f48d4ad Mon Sep 17 00:00:00 2001 From: Nikolay Zamotaev Date: Tue, 18 Dec 2018 15:03:15 +0300 Subject: Qtauto deployment server documentation update Fixes: AUTOSUITE-625 Change-Id: I5433ce0c2af64109d3e4d3779d698f2c93eadb28 Reviewed-by: Svetlana Abramenkova --- doc/src/deployment-server.qdoc | 141 ++++++++++++++++++++++------------------- 1 file changed, 75 insertions(+), 66 deletions(-) diff --git a/doc/src/deployment-server.qdoc b/doc/src/deployment-server.qdoc index c505c9b..1710ea4 100644 --- a/doc/src/deployment-server.qdoc +++ b/doc/src/deployment-server.qdoc @@ -28,34 +28,35 @@ /*! \page qtauto-deployment-server-index.html - \title Qt Auto Deployment Server - \section1 Introduction + \title Qt Automotive Suite Deployment Server Qt Automotive Suite Deployment server is a reference implementation of a network resource for hosting and distributing applications in projects based on Qt Application Manager. - Even though the Deployment Server can be used in field for real products, the main purpose + Even though the deployment server can be used in the field for real products, the main purpose for it is helping development and integration teams to simplify development and testing of applications on target system. + + When implementing the deployment server, certain assumptions were made. \section1 Assumptions \list - \li Applications are identified by a group of (Application ID, Version, Architecture, Tags). + \li Applications are identified by a group of: Application ID, version, architecture and tags. Such groups are unique. - \li Architecture is specified as a group of: (CPU architecture, endianness, bitness and OS). - If package does not contain architecture specific parts, architecture is specified as “All”. + \li Architecture is specified as a group of: CPU architecture, endianness, bitness and OS. + If a package does not contain architecture specific parts, the architecture is specified as \e{All}. \li CPU architecture is taken as returned by QsysInfo::buildCpuArchitecture(). - \li As it is hard to determine OS for uploaded packages, OS-es are matched by binary format, - requesting anything unix-like will give packages with ELF binaries, anything Windows related + \li As it is hard to determine the OS for uploaded packages, they are matched by binary format. + Thus, requesting anything Unix-like will give packages with ELF binaries, anything Windows related will return packages with PE32 format binaries and packages with Mach_o binaries are returned for Apple products. See \l {http://doc.qt.io/qt-5/qsysinfo.html#kernelType} {QSysInfo::kernelType()} for more details. - \li When native and non-native applications match selection criteria, native application is preferred. + \li When native and non-native applications match selection criteria, the native application is preferred. \li Applications can be further filtered by tags, both as positive (must have) and negative (must not have) filters. - \li Tags are considered alphanumeric and can contain lowercase latin letters, numbers and - underscore symbol. All tags passed to the server are brought to lowercase. - \li Tags can optionally have version. Version is separated from tag by ':'. Version follows the same - limitations as tag. + \li Tags are considered alphanumeric and can contain lowercase Latin letters, numbers and + the underscore symbol. All tags passed to the server are converted to lowercase. + \li Tags can optionally have a version. The version number is separated from tag by a colon (:). The version follows the same + limitations as the tag. \li Tags are matched according to versions (when requesting version 5.12, it will match 5.12.0, but not vice versa). - Also, when requesting non-versioned tag - any version will match. + Also, when requesting a non-versioned tag - any version will match. \li Tag lists in requests and packages are simplified (so qt:5.12,qt:5.12.0 will end up as qt:5.12) \li \l {https://doc.qt.io/QtApplicationManager/manifest.html#basic-manifest} {Application manifest} allows for any number of categories assigned to application. Deployment server currently @@ -63,35 +64,38 @@ category to the application. This behavior can be discussed with nzamotaev@luxoft.com . \li Tag information is parsed from package header's \b{extra} and \b{extraSigned} parts, from \b{tags} array. All elements of that array are added to package’s tag list. - \li Every package has version. If manifest does not contain version, - it will be assigned version “0.0.0”. + \li Every package has a version number. If the manifest does not contain a version field, + the version number will be assigned “0.0.0”. \endlist \section1 API \section2 hello - Checks whether you are using the right Platform and the right API to communicate with the Server. + Checks whether you are using the correct Platform and the right API to communicate with the deployment server. \table \header \li Parameter \li Description \row \li platform - \li The platform the client is running on, this sets the architecture of the packages you get. (see \b{settings.APPSTORE_PLATFORM}) + \li The platform the client is running on. This sets the architecture of the packages + you get (see \b{settings.APPSTORE_PLATFORM} parameter in \b{appstore/settings.py} file). \row \li version - \li The Deployment Server HTTP API version you are using to communicate with the server. (see \b{settings.APPSTORE_VERSION}) + \li The Deployment Server HTTP API version you are using to communicate with the server + (see \b{settings.APPSTORE_VERSION}). \row \li require_tag - \li Optional parameter for filtering packages by tags. Receives comma-separated list of tags. - Only applications containing any of specified tags should be listed. Tags must be alphanumeric. + \li Optional parameter for filtering packages by tags. Receives a comma-separated list of tags. + Only applications containing any of the specified tags should be listed. + Tags must be alphanumeric. \row \li conflicts_tag - \li Optional parameter for filtering packages by tags. Receives comma-separated list of tags. + \li Optional parameter for filtering packages by tags. Receives a comma-separated list of tags. No application containing any of the tags should be listed. Tags must be alphanumeric. \row \li architecture - \li ptional parameter for filtering packages by architecture. Receives cpu architecture. - If architecture is not specified, only packages showing 'All' architecture are listed. + \li Optional parameter for filtering packages by architecture. Receives the CPU architecture. + If architecture is not specified, only packages showing \e{All} architecture are listed. \endtable Returns a JSON object: \table @@ -108,17 +112,17 @@ \li Server is in maintenance mode and can not be used at the moment. \row \li incompatible-platform - \li You are using incompatible platform. + \li You are using an incompatible platform. \row \li incompatible-version - \li You are using incompatible version of the API + \li You are using incompatible version of the API. \row \li malformed-tag \li Tag had wrong format, was not alphanumeric or could not be parsed. \endtable \section2 login - Does a login on the Server with the given username and password. - Either a IMEI or \l {https://en.wikipedia.org/wiki/MAC_address} {other unique hardware identifier} must be provided. This call is needed for downloading apps. + Logs onto the deployment server with the given username and password. + Either an IMEI or a \l {https://en.wikipedia.org/wiki/MAC_address} {unique hardware identifier} must be provided. This call is needed for downloading apps. \table \header \li Parameter @@ -128,7 +132,7 @@ \li The username \row \li password - \li Password for given username + \li Password for the given username \endtable Returns a JSON object: \table @@ -142,16 +146,16 @@ \li Login successful. \row \li missing-credentials - \li No username and/or password provided + \li No username or password provided \row \li account-disabled - \li Account is disabled in admin panel + \li Account is disabled in the admin panel \row \li authentication-failed \li Wrong username and/or password or other authentication error \endtable \section2 logout - Logs out currently logged in user on server. + Logs out the currently logged-in user from the deployment server. Returns a JSON object: \table @@ -169,19 +173,19 @@ \endtable \section2 app/list - Lists all apps. Returned List can be filtered by using the \b{category_id} and the \b{filter} arguments. + Lists all apps. The returned list can be filtered by using the \b{category_id} and the \b{filter} arguments. \table \header \li Parameter \li Description \row \li category_id - \li Limits applications to category with this id. + \li Limits applications to the category with this id. \row \li filter - \li Only lists apps, whose names match the filter. + \li Only lists apps whose names match the filter. \endtable - Returns a JSON array (\b{not an object!}). Each field is a JSON object: + Returns an array of JSON objects (\b{not an object itself!}). \table \header \li JSON field @@ -203,12 +207,14 @@ \li JSON array of application tags \row \li version - \li Application version. Returned as string. If application information lacks version, “0.0.0” is returned + \li Application version. Returned as a string. If the application information lacks + a version number, “0.0.0” is returned \row \li architecture - \li Application architecture. Returned as detected in library components of the application + \li Application architecture. Returned as detected in the library components of the application - If application is not native, contains “All”. Otherwise it is formed like this: mips-little_endian-32-elf + If the application is not native, contains \e{All}. + Otherwise it is formed like this: mips-little_endian-32-elf Where it is a combination of: \list 1 @@ -223,7 +229,7 @@ \li Short textual app description (should be limited to 1 line, around 80-130 characters) \row \li category_id - \li Numeric category id matching application category field. + \li Numeric category id matching the application category field. \endtable \section2 app/icon Returns an icon for the given application id. @@ -235,7 +241,7 @@ \li id \li Application id \endtable - Returns a PNG image or a 404 error, if application does not exist + Returns a PNG image or a 404 error, if the application does not exist. \section2 app/description Returns a description for the given application id. \table @@ -246,11 +252,11 @@ \li id \li Application id \endtable - Returns application description text - either HTML or plain text. + Returns an application description text - either HTML or plain text. \section2 app/purchase - Returns an url which can be used for downloading the requested application for certain period of time (configurable in the settings). - \note This is legacy from AppStore, changing the name of this API would involve changes in reference UI. - The real action is preparing package for download. For discussion, contact nzamotaev@luxoft.com. + Returns a URL which can be used for downloading the requested application for certain period of time (configurable in the settings). + \note This is legacy from AppStore. Changing the name of this API would involve changes in reference UI. + The real action is preparing the package for download. For discussion, contact nzamotaev@luxoft.com. \table \header \li Parameter @@ -274,24 +280,25 @@ \li Successful \row \li failed - \li Something went wrong. Check error field for more information. + \li Something went wrong. Check the error field for more information. \row \li error \li text - \li If status equals to ‘failed’, contains error description. + \li If status equals to \e{failed}, contains an error description. \row \li url \li URL - \li URL for downloading application. Expires in time specified in ‘expiresIn’ field. + \li URL for downloading the application. + Expires in the time specified in the \c{expiresIn} field. \row \li expiresIn \li Integer value - \li Time in seconds for which download url stays valid. + \li Time in seconds for which the download URL stays valid. \endtable \section2 category/list - Lists all the available categories. Also returns ‘All’ metacategory, that is used to hold all available applications. - Returns a JSON array (not an object!). Each field is a JSON object: + Lists all the available categories. Also returns \e{All} metacategory, that is used to hold all available applications. + Returns an array of JSON objects (\b{not an object itself!}). \table \header \li JSON field @@ -313,12 +320,13 @@ \li id \li Id of the category. \endtable - Returns a PNG image or empty 1x1 PNG file. - \note Currently takes icon of the first application in the category if it exists. This should be fixed. + Returns a PNG image or an empty 1x1 PNG file. + \note Currently takes the icon of the first application in the category if it exists. + This should be fixed. \section2 upload Accepts remote package upload requests. - User must be in the “staff” group to use this API. Also requires either basic authentication, - or previous call to \b{login} method. This is a POST request to server, due to parameters used. + The user must be in the \e{staff} group to use this API. Also requires either basic authentication + or a previous call to the \c{login} method. This is a POST request to the server due to the parameters used. \table \header \li Parameter @@ -363,24 +371,24 @@ \li Vendor parameter missing \row \li Package validation failed - \li Package did not pass format/sanity validation + \li Package did not pass format or sanity validation \row \li Non-existing category - \li No category matches passed parameter + \li No category matches the passed parameter \row \li Non-existing vendor \li No vendor matches passed parameter \row \li no package to upload - \li There was no ‘package’ parameter in request, or it was not a POST request + \li There was no \c{package} parameter in the request, or it was not a POST request \endtable \section2 API use examples The deployment server exposes a HTTP API to the world. Arguments to the functions need to be provided using the HTTP GET/POST syntax. Returned data will be in JSON, PNG or text format, depending on the function. - \section3 Basic workflow + \section3 Workflow \list 1 - \li Send a \b{hello} request to the server to get the current status and check + \li Send a \b{hello} request to the server to get the current status and to check whether your platform is compatible with this server instance: \tt http:///hello?platform=AM&version=1 @@ -389,7 +397,7 @@ \tt { { "status": "ok" } } - \li Login as user \b{user} with password \b{pass}: + \li Login as user \e{user} with password \e{pass}: \tt http:///login?username=user&password=pass @@ -413,7 +421,7 @@ ] } - \li Request a download for a App: + \li Request a download for an app: \tt http:///app/purchase?device_id=12345&id=com.luxoft.niceapp @@ -430,12 +438,13 @@ \endlist \section1 Installation - \section2 Setting up the server in virtualenv + \section2 Setting up the Server in virtualenv + Before installing dependencies in the Python virtual environment, libffi-dev package must be + installed. After package installation, prepare the virtual environment: \code virtualenv ./venv ./venv/bin/pip install -r requirements.txt \endcode - (libffi-dev is also needed)a Before running the server, make sure to adapt the \b{APPSTORE_*} settings in \b{appstore/settings.py} to your environment. @@ -450,14 +459,14 @@ \code ./manage.py runserver 0.0.0.0:8080 \endcode - will start the server on port 8080, reachable for anyone. One can tweak the listening address to whatever fits the needs. + will start the server on port 8080, reachable by anyone. One can tweak the listening address to whatever fits the needs. \section2 Maintenance tasks \list \li Cleaning up the downloads directory: \code ./manage.py expire-downloads \endcode - will remove all files from the downloads/ directory, that are older than + will remove all files from the downloads directory, that are older than \b{settings.APPSTORE_DOWNLOAD_EXPIRY} minutes. This should be called from a cron-job (see above). \li Manually verifying a package for upload: \code -- cgit v1.2.3 From 43ae22359264e89ee0383ed8d52032394758929c Mon Sep 17 00:00:00 2001 From: Topi Reinio Date: Thu, 20 Dec 2018 14:50:29 +0100 Subject: Doc: Fix documentation configuration - Fix references to landing page title - Fix typo in buildversion - Use https over http - Use 'Qt Automotive Suite' as navigation.homepage and add a dependency to it Task-number: AUTOSUITE-726 Change-Id: I48b47b872d231689387de1a48cc0cfc1dca76eec Reviewed-by: Dominik Holland --- doc/qtautodeploymentserver-project.qdocconf | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/doc/qtautodeploymentserver-project.qdocconf b/doc/qtautodeploymentserver-project.qdocconf index 65a4fd7..8625f13 100644 --- a/doc/qtautodeploymentserver-project.qdocconf +++ b/doc/qtautodeploymentserver-project.qdocconf @@ -1,7 +1,7 @@ project = QtAutoDeploymentServer -description = Qt Auto Deployment Server Documentation +description = Qt Automotive Suite Deployment Server Documentation version = $QT_VERSION -url = http://doc.qt.io/QtAutoDeploymentServer +url = https://doc.qt.io/QtAutoDeploymentServer sourcedirs += src imagedirs += src/images @@ -11,7 +11,7 @@ qhp.projects = QtAutoDeploymentServer qhp.QtAutoDeploymentServer.file = qtautodeploymentserver.qhp qhp.QtAutoDeploymentServer.namespace = org.qt-project.qtautodeploymentserver.$QT_VERSION_TAG qhp.QtAutoDeploymentServer.virtualFolder = qtautodeploymentserver -qhp.QtAutoDeploymentServer.indexTitle = Qt Auto Deployment Server +qhp.QtAutoDeploymentServer.indexTitle = Qt Automotive Suite Deployment Server qhp.QtAutoDeploymentServer.indexRoot = qhp.QtAutoDeploymentServer.filterAttributes = qtautodeploymentserver $QT_VERSION qtrefdoc @@ -20,5 +20,7 @@ qhp.QtAutoDeploymentServer.customFilters.Qt.filterAttributes = qtautodeployments tagfile = qtautodeploymentserver.tags -buildversion = "Qt Auto Deploymment Server $QT_VERSION" -navigation.homepage = "Qt Auto Deployment Server" +depends = qtautomotivesuite + +buildversion = "Qt Automotive Suite Deployment Server $QT_VERSION" +navigation.homepage = "Qt Automotive Suite" -- cgit v1.2.3 From 1fde901863d0ebf188360b781668c2199c32acdc Mon Sep 17 00:00:00 2001 From: Samuli Piippo Date: Mon, 4 Feb 2019 10:29:15 +0200 Subject: Add .tag file to record sha1 to the source package Task-number: AUTOSUITE-760 Change-Id: Iafd1894b97986ab1b405cd32fdbba32888b8f90f Reviewed-by: Robert Griebl --- .gitattributes | 1 + .tag | 1 + 2 files changed, 2 insertions(+) create mode 100644 .gitattributes create mode 100644 .tag diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..5b4275b --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +.tag export-subst diff --git a/.tag b/.tag new file mode 100644 index 0000000..6828f88 --- /dev/null +++ b/.tag @@ -0,0 +1 @@ +$Format:%H$ -- cgit v1.2.3 From 16c2655046c24805cf37df11a6795497514bb118 Mon Sep 17 00:00:00 2001 From: Kavindra Palaraja Date: Tue, 5 Feb 2019 17:08:00 +0100 Subject: [docs] - Update the overview on the Deployment Server - Added a new diagram based on input from Dominik, Robert, and Vladimir. - This change uses the term "Downloads app" which is currently not in sync with Neptune 3 UI's documentation. A subsequent patch will be made there to change "App store" to "Downloads app" Change-Id: I3fa05f6609420744f7c5cb570be91116207de4d1 Reviewed-by: Bramastyo Harimukti Santoso --- doc/src/deployment-server.qdoc | 83 +++++++++++++-------- .../deployment-server-installation-handshake.png | Bin 0 -> 35810 bytes 2 files changed, 53 insertions(+), 30 deletions(-) create mode 100644 doc/src/images/deployment-server-installation-handshake.png diff --git a/doc/src/deployment-server.qdoc b/doc/src/deployment-server.qdoc index 1710ea4..d3f2e81 100644 --- a/doc/src/deployment-server.qdoc +++ b/doc/src/deployment-server.qdoc @@ -29,43 +29,66 @@ /*! \page qtauto-deployment-server-index.html \title Qt Automotive Suite Deployment Server - Qt Automotive Suite Deployment server is a reference implementation of a network resource for - hosting and distributing applications in projects based on Qt Application Manager. - Even though the deployment server can be used in the field for real products, the main purpose - for it is helping development and integration teams to simplify development and testing of - applications on target system. - When implementing the deployment server, certain assumptions were made. + The Qt Automotive Suite Deployment Server is a new component in the Qt Automotive Suite 5.12. + Previously, it was known as the Neptune Appstore and used for demonstrations purposes. + + This component is a lightweight HTTP server that provides network feeds to application packages + that are available for installation by a Qt Application Manager instance, running on a target + device connected to a network. The UI counterpart for the Deployment Server is the Downloads app + in the Neptune 3 UI. Together, the Deployment Server and the Downloads app enable you to install + different apps that are available in the server via network. + + The key scenario for these components is to install apps during the integration testing + process. Additionally, the code can also be used as a reference implementation for a + fully-featured server, and a new Downloads app on the target device for production. + + The Deployment Server works with the Application Installer in the Qt Application Manager and + acts as an installation source for \c{http://} and \c{https://} schemes. In addition to + application packages, the Deployment Server also hosts meta information about each package, + that is used by the Downloads app to visualize the choices available for the user to select. + This meta information is stored in the form of tags and other information in the package + header, according to the Qt Application Manager’s package format. When a package is uploaded + to the server, the package header is parsed, associated with that package, and then sent to a + Downloads app, that queries for a list of available apps. Using this information, a Downloads + app can inform users about apps that are available and even hide those that are not compatible + with the target installation. The figure below illustrates this installation scenario. + + \image deployment-server-installation-handshake.png "Install an App via the Deployment Server" + + The Deployment Server is implemented in Python, using Django, based on the following assumptions. + \section1 Assumptions \list - \li Applications are identified by a group of: Application ID, version, architecture and tags. - Such groups are unique. - \li Architecture is specified as a group of: CPU architecture, endianness, bitness and OS. + \li Applications are identified with a group of: Application ID, version, architecture, and tags; + these groups are unique. + \li Architecture is specified as a group of: CPU architecture, endianness, bitness, and OS. If a package does not contain architecture specific parts, the architecture is specified as \e{All}. - \li CPU architecture is taken as returned by QsysInfo::buildCpuArchitecture(). - \li As it is hard to determine the OS for uploaded packages, they are matched by binary format. - Thus, requesting anything Unix-like will give packages with ELF binaries, anything Windows related - will return packages with PE32 format binaries and packages with Mach_o binaries are returned - for Apple products. See \l {http://doc.qt.io/qt-5/qsysinfo.html#kernelType} {QSysInfo::kernelType()} for more details. - \li When native and non-native applications match selection criteria, the native application is preferred. - \li Applications can be further filtered by tags, both as positive (must have) and negative - (must not have) filters. - \li Tags are considered alphanumeric and can contain lowercase Latin letters, numbers and + \li CPU architecture is based on the return value from QsysInfo::buildCpuArchitecture(). + \li The installation target is automatically determined by parsing binary files. For example, + detecting an ELF binary means that it's a Linux package; the CPU architecture, such as armv8, + further defines the installation target. + See \l {https://doc.qt.io/qt-5/qsysinfo.html#kernelType} {QSysInfo::kernelType()} for more details. + \li If both native and non-native applications match the selection criteria, then the native application + is preferred. + \li Applications can be further filtered by tags, both as positive (inlucde) and negative (exclude) + filters. + \li Tags are considered alphanumeric and can contain lowercase Latin letters, numbers, and the underscore symbol. All tags passed to the server are converted to lowercase. - \li Tags can optionally have a version. The version number is separated from tag by a colon (:). The version follows the same - limitations as the tag. - \li Tags are matched according to versions (when requesting version 5.12, it will match 5.12.0, but not vice versa). - Also, when requesting a non-versioned tag - any version will match. - \li Tag lists in requests and packages are simplified (so qt:5.12,qt:5.12.0 will end up as qt:5.12) - \li \l {https://doc.qt.io/QtApplicationManager/manifest.html#basic-manifest} {Application manifest} - allows for any number of categories assigned to application. Deployment server currently - ignores categories in application manifest and requires manual assignment of exactly one - category to the application. This behavior can be discussed with nzamotaev@luxoft.com . - \li Tag information is parsed from package header's \b{extra} and \b{extraSigned} parts, + \li Tags can also have an optional version. The version number is separated from tag using a colon (:). + Similar to tags, the version can contain lowercase Latin letters, numbers, and the underscore symbol. + \li Tags are matched according to versions. For example, if you request for "version 5.12", this matches + with "5.12.0", but not vice versa. If you request for a non-versioned tag, any version matches your request. + \li Tag lists in requests and packages are simplified. For example, "qt:5.12,qt:5.12.0" is reduced to "qt:5.12". + \li Although the \l {https://doc.qt.io/QtApplicationManager/manifest.html#basic-manifest} {Application manifest} + allows for any number of categories to be assigned to an application, currently, the Deployment Server + requires manual assignment of only one category to the application. Categories in the application manifest + are ignored. + \li Tag information is parsed from the package header's \b{extra} and \b{extraSigned} parts, from \b{tags} array. All elements of that array are added to package’s tag list. - \li Every package has a version number. If the manifest does not contain a version field, - the version number will be assigned “0.0.0”. + \li Each package has a version number. If the manifest does not contain a version field, a default + version "0.0.0" is assigned. \endlist \section1 API diff --git a/doc/src/images/deployment-server-installation-handshake.png b/doc/src/images/deployment-server-installation-handshake.png new file mode 100644 index 0000000..08cc244 Binary files /dev/null and b/doc/src/images/deployment-server-installation-handshake.png differ -- cgit v1.2.3 From af559c314224584ca656ff5bc1136fdff4ad8400 Mon Sep 17 00:00:00 2001 From: Kavindra Palaraja Date: Wed, 13 Feb 2019 14:56:38 +0100 Subject: [docs] Improve documentation for the Deployment Server MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Simplified the documentation to improve readability * Split out the API Reference from the original topic * Used the correct qdoc tags where possible * Formatted the tables better for the HTTP API Task-number: AUTOSUITE-752 Change-Id: Iaa308b7f995070993b264ee9bfdcaf2e1bd46d8a Reviewed-by: Topi Reiniö Reviewed-by: Nikolay Zamotaev --- doc/src/deployment-server-reference.qdoc | 447 +++++++++++++++++++++++++++++++ doc/src/deployment-server.qdoc | 432 +++-------------------------- 2 files changed, 488 insertions(+), 391 deletions(-) create mode 100644 doc/src/deployment-server-reference.qdoc diff --git a/doc/src/deployment-server-reference.qdoc b/doc/src/deployment-server-reference.qdoc new file mode 100644 index 0000000..c49c3d2 --- /dev/null +++ b/doc/src/deployment-server-reference.qdoc @@ -0,0 +1,447 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Luxoft Sweden AB +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Auto Deployment Server. +** +** $QT_BEGIN_LICENSE:FDL-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \page qtauto-deployment-server-reference.html + \previouspage Qt Automotive Suite Deployment Server + \contentspage Qt Automotive Suite + + \title Qt Automotive Suite Deployment Server API Reference + + \section1 API Reference + + The following tables describe the requests, their parameters, as well as the corresponding responses. + + \section2 hello + + Checks whether you are using the correct Platform and the right API to communicate with the deployment + server. + \table + \header + \li Parameter + \li Description + \row + \li platform + \li The platform on which the client is running. This parameter sets the architecture + of the packages you get. For more information, refer to the + \c{settings.APPSTORE_PLATFORM} parameter in the \c{appstore/settings.py} file. + \row + \li version + \li The Deployment Server's HTTP API version that you are using to communicate with the + server. For more information, see \c{settings.APPSTORE_VERSION}. + \row + \li require_tag + \li An optional parameter used to filter packages by tags. Receives a comma-separated + list of tags; these tags must be alphanumeric. Only applications that contain any + of the specified tags should be listed. + \row + \li conflicts_tag + \li An optional parameter used to filter packages by tags. Receives a comma-separated + list of tags; these tags must be alphanumeric. Applications that contain any of the + speicifed tags should be excluded. + \row + \li architecture + \li An optional parameter used to filter packages by architecture. Receives the CPU + architecture. If the architecture is not speciifed, only packages showing \e{All} + architecture are listed. + \endtable + + Returns a JSON object with the following fields and values. + \table + \header + \li JSON Field + \li Value + \li Description + \row + \li {1,5} status + \li ok + \li Successful + \row + \li maintenance + \li The server is in maintenance mode and cannot be used at the moment. + \row + \li incompatible-platform + \li The platform you are using is not compatible. + \row + \li incompatible-version + \li The API version you are using is not compatible. + \row + \li malformed-tag + \li The tag format is incorrect, may not be alphanumeric, or could + not be parsed. + \endtable + + \section2 login + + Logs onto the deployment server with the given username and password. + Either an IMEI or a unique hardware identifier, such as a MAC address, + must be provided. This call is necessary to be able to download apps. + \table + \header + \li Parameter + \li Description + \row + \li username + \li The username. + \row + \li password + \li The password for the specified username. + \endtable + + Returns a JSON object with the following fields and values. + \table + \header + \li JSON Field + \li Value + \li Description + \row + \li {1,4} status + \li ok + \li Login successful. + \row + \li missing-credentials + \li No username or password was provided. + \row + \li account-disabled + \li The account has been disabled in the Admin panel. + \row + \li authentication-failed + \li The user name and/or password may be wrong; or another authentication error + occurred. + \endtable + + \section2 logout + Logs out the currently logged-in user from the deployment server. + + Returns a JSON object with the following fields and values. + \table + \header + \li JSON Field + \li Value + \li Description + \row + \li {1,2} status + \li ok + \li The user has logged out successfully. + \row + \li failed + \li The user was not logged in. + \endtable + + \section2 app/list + Lists all apps. The returned list can be filtered by using the \c{category_id} and the + \c{filter} parameters. + \table + \header + \li Parameter + \li Description + \row + \li category_id + \li Limits the app to those with this category ID only. + \row + \li filter + \li Lists apps with names that match this filter only. + \endtable + + Returns an array of JSON objects (\b{not an object itself!}). + \table + \header + \li JSON Field + \li Description + \row + \li id + \li A unique app ID, in reverse domain name notation. + \row + \li name + \li The app's name. + \row + \li vendor + \li The vendor name for the app; not the vendor ID. + \row + \li category + \li A category name for the app. + \row + \li tags + \li JSON array of app tags + \row + \li version + \li The app's version, returned as a string. If the there is no version number, the + default version, "0.0.0" is returned. + \row + \li architecture + \li The app's architecture, returned as detected in the app's library components. + + If the application is not native, contains \e{All}. + Otherwise it is formed like this: mips-little_endian-32-elf + + Where it is a combination of: + \list 1 + \li CPU architecture, as returned by \l{https://doc.qt.io/qt-5/qsysinfo.html#buildCpuArchitecture}{QsysInfo::buildCpuArchitecture()} + \li CPU endianness, either \c{little_endian} or \c{big_endian}) + \li ABI bitness + \li Binary format, either \c{elf}, \c{mach_o} or \c{pe32} + \endlist + \row + \li briefDescription + \li A short text that describes the app, limited to 1 line, approximately 80-130 + characters. + \row + \li category_id + \li Numeric category ID that matches the app's category. + \endtable + + \section2 app/icon + Returns an icon for the given application id. + \table + \header + \li Parameter + \li Description + \row + \li id + \li The app ID. + \endtable + + Returns a PNG image if the app exists; an HTTP 404 error otherwise. + + \section2 app/description + + Returns a description for the given app ID. + + \table + \header + \li Parameter + \li Description + \row + \li id + \li app ID + \endtable + + Returns a description text for the app, either HTML or plain text. + + \section2 app/purchase + + Returns a URL that you can use to download the requested app for a certain period of time + only; configurable in the settings. + + \note This request is a legacy from the AppStore. Changing the name of this API would + involve changes in Neptune 3 UI. + + \table + \header + \li Parameter + \li Description + \row + \li device_id + \li A unique device ID for the client hardware; currently not used. + \row + \li id + \li The app ID. + \endtable + Returns a JSON object: + \table + \header + \li JSON Field + \li Value + \li Description + \row + \li {1,2} status + \li ok + \li Successful + \row + \li failed + \li + \li An error has occurred, dheck the error field for more information. + \row + \li error + \li Text. + \li If the status is equal to \c{failed}, contains an error description. + \row + \li url + \li A URL. + \li The URL from where to download the app. Expires according to the value specified + in \c{expiresIn}. + \row + \li expiresIn + \li An integer value. + \li Time in seconds during which the download URL is valid. + \endtable + + \section2 category/list + + Lists all of the available categories. Also returns the \e{All} metacategory, that is used + to hold all available applications. + + Returns an array of JSON objects (\b{not an object itself!}). + + \table + \header + \li JSON field + \li Description + \row + \li id + \li Unique category id. + \row + \li name + \li Category name. + \endtable + + \section2 category/icon + Returns an icon for the given category ID. + + \table + \header + \li Parameter + \li Description + \row + \li id + \li The category ID. + \endtable + + Returns an image in PNG format or an empty 1x1 PNG file. + + \note Currently takes the icon of the first app in the category, if it exists. + + \section2 upload + Accepts remote package upload requests. + The user must be in the \e{staff} group to use this API. Also requires either basic authentication + or a previous call to the \c{login} method. This is a POST request to the server due to the parameters used. + \table + \header + \li Parameter + \li Description + \row + \li description + \li Package description, long version. Can be text or HTML. + \row + \li short-description + \li One line package description. + \row + \li category + \li Category name for the category where the package will be put. + \row + \li vendor + \li Vendor name for the package. + \row + \li package + \li Package itself. This is uploaded as a file parameter. + \endtable + + Returns JSON object: + \table + \header + \li Parameter + \li Value + \li Description + \row + \li {1,9} status + \li ok + \li Success + \row + \li no description + \li The description parameter is missing. + \row + \li no short description + \li The short-description parameter is missing. + \row + \li no category + \li The category parameter is missing. + \row + \li no vendor + \li The vendor parameter is missing. + \row + \li Package validation failed + \li Package did not pass format or sanity validation + \row + \li Non-existing category + \li The specified category does not match the parameter passed. + \row + \li Non-existing vendor + \li The specified vendor does not match the parameter passed. + \row + \li no package to upload + \li There was no \c{package} parameter in the request, or it was not a POST request. + \endtable + + \section2 API Usage Examples + + The Deployment Server exposes an HTTP API. Arguments to these requests need to be provided + using the HTTP GET or POST syntax. The data is returned in JSON, PNG, or text format, depending + on the request. + + \section3 Workflow + + \list 1 + \li Send a \c{hello} request to the server to get the current status and to check + whether your platform is compatible with this server instance: + + \tt http:///hello?platform=AM&version=1 + + Returns: + + \tt { { "status": "ok" } } + + \li Login as \c{user} with password, \c{pass}: + + \tt http:///login?username=user&password=pass + + Returns: + + \tt { { "status": "ok" } } + + \li List all applications + + \tt http:///app/list + + Returns: + + \tt { [{ "category": "Entertainment", + "name": "Nice App", + "vendor": "Luxoft", + "briefDescription": "Nice App is a really nice app.", + "category_id": 4, + "id": "com.luxoft.niceapp"}, + .... + ] } + + + \li Request a download for an app: + + \tt http:///app/purchase?device_id=12345&id=com.luxoft.niceapp + + Returns: + + \tt { { "status": "ok", + "url": "http:///app/download/com.luxoft.niceapp.2.npkg", + "expiresIn": 600 + } } + + \li Use the \c{url} provided in step 4 to download the application within \c{expiresIn} + seconds. + + \endlist +*/ \ No newline at end of file diff --git a/doc/src/deployment-server.qdoc b/doc/src/deployment-server.qdoc index d3f2e81..c6b2c45 100644 --- a/doc/src/deployment-server.qdoc +++ b/doc/src/deployment-server.qdoc @@ -28,6 +28,9 @@ /*! \page qtauto-deployment-server-index.html + \contentspage {Qt Automotive Suite} + \nextpage Qt Automotive Suite Deployment Server API Reference + \title Qt Automotive Suite Deployment Server The Qt Automotive Suite Deployment Server is a new component in the Qt Automotive Suite 5.12. @@ -91,421 +94,68 @@ version "0.0.0" is assigned. \endlist - \section1 API - \section2 hello - Checks whether you are using the correct Platform and the right API to communicate with the deployment server. - \table - \header - \li Parameter - \li Description - \row - \li platform - \li The platform the client is running on. This sets the architecture of the packages - you get (see \b{settings.APPSTORE_PLATFORM} parameter in \b{appstore/settings.py} file). - \row - \li version - \li The Deployment Server HTTP API version you are using to communicate with the server - (see \b{settings.APPSTORE_VERSION}). - \row - \li require_tag - \li Optional parameter for filtering packages by tags. Receives a comma-separated list of tags. - Only applications containing any of the specified tags should be listed. - Tags must be alphanumeric. - \row - \li conflicts_tag - \li Optional parameter for filtering packages by tags. Receives a comma-separated list of tags. - No application containing any of the tags should be listed. Tags must be alphanumeric. - \row - \li architecture - \li Optional parameter for filtering packages by architecture. Receives the CPU architecture. - If architecture is not specified, only packages showing \e{All} architecture are listed. - \endtable - Returns a JSON object: - \table - \header - \li JSON field - \li Value - \li Description - \row - \li {1,5} status - \li ok - \li Successful - \row - \li maintenance - \li Server is in maintenance mode and can not be used at the moment. - \row - \li incompatible-platform - \li You are using an incompatible platform. - \row - \li incompatible-version - \li You are using incompatible version of the API. - \row - \li malformed-tag - \li Tag had wrong format, was not alphanumeric or could not be parsed. - \endtable - \section2 login - Logs onto the deployment server with the given username and password. - Either an IMEI or a \l {https://en.wikipedia.org/wiki/MAC_address} {unique hardware identifier} must be provided. This call is needed for downloading apps. - \table - \header - \li Parameter - \li Description - \row - \li username - \li The username - \row - \li password - \li Password for the given username - \endtable - Returns a JSON object: - \table - \header - \li JSON field - \li Value - \li Description - \row - \li {1,4} status - \li ok - \li Login successful. - \row - \li missing-credentials - \li No username or password provided - \row - \li account-disabled - \li Account is disabled in the admin panel - \row - \li authentication-failed - \li Wrong username and/or password or other authentication error - \endtable - \section2 logout - Logs out the currently logged-in user from the deployment server. - - Returns a JSON object: - \table - \header - \li JSON field - \li Value - \li Description - \row - \li {1,2} status - \li ok - \li Successfully logged out - \row - \li failed - \li Was not logged in - \endtable - - \section2 app/list - Lists all apps. The returned list can be filtered by using the \b{category_id} and the \b{filter} arguments. - \table - \header - \li Parameter - \li Description - \row - \li category_id - \li Limits applications to the category with this id. - \row - \li filter - \li Only lists apps whose names match the filter. - \endtable - Returns an array of JSON objects (\b{not an object itself!}). - \table - \header - \li JSON field - \li Description - \row - \li id - \li Unique application id. In reverse domain name notation - \row - \li name - \li Application name - \row - \li vendor - \li Vendor name for application (not vendor id) - \row - \li category - \li Category name for application - \row - \li tags - \li JSON array of application tags - \row - \li version - \li Application version. Returned as a string. If the application information lacks - a version number, “0.0.0” is returned - \row - \li architecture - \li Application architecture. Returned as detected in the library components of the application - - If the application is not native, contains \e{All}. - Otherwise it is formed like this: mips-little_endian-32-elf - - Where it is a combination of: - \list 1 - \li CPU architecture, as returned by QsysInfo::buildCpuArchitecture() - \li CPU endianness (either \b{little_endian} or \b{big_endian}) - \li ABI bitness - \li binary format (\b{elf}, \b{mach_o} or \b{pe32}) - \endlist - - \row - \li briefDescription - \li Short textual app description (should be limited to 1 line, around 80-130 characters) - \row - \li category_id - \li Numeric category id matching the application category field. - \endtable - \section2 app/icon - Returns an icon for the given application id. - \table - \header - \li Parameter - \li Description - \row - \li id - \li Application id - \endtable - Returns a PNG image or a 404 error, if the application does not exist. - \section2 app/description - Returns a description for the given application id. - \table - \header - \li Parameter - \li Description - \row - \li id - \li Application id - \endtable - Returns an application description text - either HTML or plain text. - \section2 app/purchase - Returns a URL which can be used for downloading the requested application for certain period of time (configurable in the settings). - \note This is legacy from AppStore. Changing the name of this API would involve changes in reference UI. - The real action is preparing the package for download. For discussion, contact nzamotaev@luxoft.com. - \table - \header - \li Parameter - \li Description - \row - \li device_id - \li Unique device id for client hardware (currently unused) - \row - \li id - \li Application id - \endtable - Returns a JSON object: - \table - \header - \li JSON field - \li Value - \li Description - \row - \li {1,2} status - \li ok - \li Successful - \row - \li failed - \li Something went wrong. Check the error field for more information. - \row - \li error - \li text - \li If status equals to \e{failed}, contains an error description. - \row - \li url - \li URL - \li URL for downloading the application. - Expires in the time specified in the \c{expiresIn} field. - \row - \li expiresIn - \li Integer value - \li Time in seconds for which the download URL stays valid. - \endtable - - \section2 category/list - Lists all the available categories. Also returns \e{All} metacategory, that is used to hold all available applications. - Returns an array of JSON objects (\b{not an object itself!}). - \table - \header - \li JSON field - \li Description - \row - \li id - \li Unique category id. - \row - \li name - \li Category name. - \endtable - \section2 category/icon - Returns an icon for the given category id. - \table - \header - \li Parameter - \li Description - \row - \li id - \li Id of the category. - \endtable - Returns a PNG image or an empty 1x1 PNG file. - \note Currently takes the icon of the first application in the category if it exists. - This should be fixed. - \section2 upload - Accepts remote package upload requests. - The user must be in the \e{staff} group to use this API. Also requires either basic authentication - or a previous call to the \c{login} method. This is a POST request to the server due to the parameters used. - \table - \header - \li Parameter - \li Description - \row - \li description - \li Package description, long version. Can be text or HTML. - \row - \li short-description - \li One line package description. - \row - \li category - \li Category name for the category where the package will be put. - \row - \li vendor - \li Vendor name for the package. - \row - \li package - \li Package itself. This is uploaded as a file parameter. - \endtable - Returns JSON object: - \table - \header - \li Parameter - \li Value - \li Description - \row - \li {1,9} status - \li ok - \li Success - \row - \li no description - \li Description parameter missing - \row - \li no short description - \li Short-description parameter missing - \row - \li no category - \li Category parameter missing - \row - \li no vendor - \li Vendor parameter missing - \row - \li Package validation failed - \li Package did not pass format or sanity validation - \row - \li Non-existing category - \li No category matches the passed parameter - \row - \li Non-existing vendor - \li No vendor matches passed parameter - \row - \li no package to upload - \li There was no \c{package} parameter in the request, or it was not a POST request - \endtable - - \section2 API use examples - The deployment server exposes a HTTP API to the world. Arguments to the functions need to be provided using the - HTTP GET/POST syntax. Returned data will be in JSON, PNG or text format, depending on the function. - \section3 Workflow - \list 1 - \li Send a \b{hello} request to the server to get the current status and to check - whether your platform is compatible with this server instance: - - \tt http:///hello?platform=AM&version=1 - - Returns: - - \tt { { "status": "ok" } } - - \li Login as user \e{user} with password \e{pass}: - - \tt http:///login?username=user&password=pass - - Returns: - - \tt { { "status": "ok" } } - - \li List all applications - - \tt http:///app/list - - Returns: - - \tt { [{ "category": "Entertainment", - "name": "Nice App", - "vendor": "Luxoft", - "briefDescription": "Nice App is a really nice app.", - "category_id": 4, - "id": "com.luxoft.niceapp"}, - .... - ] } - - - \li Request a download for an app: - - \tt http:///app/purchase?device_id=12345&id=com.luxoft.niceapp - - Returns: - - \tt { { "status": "ok", - "url": "http:///app/download/com.luxoft.niceapp.2.npkg", - "expiresIn": 600 - } } + \section1 Installation - \li Use the \b{url} provided in step 4 to download the application within - \b{expiresIn} seconds. + \section2 Set up the Server in a Virtual Environment - \endlist + Before you install the dependencies in the Python virtual environment, you must install the + \c{libffi-dev} package. Next, prepare the virtual environment: - \section1 Installation - \section2 Setting up the Server in virtualenv - Before installing dependencies in the Python virtual environment, libffi-dev package must be - installed. After package installation, prepare the virtual environment: \code virtualenv ./venv ./venv/bin/pip install -r requirements.txt \endcode - Before running the server, make sure to adapt the \b{APPSTORE_*} settings in \b{appstore/settings.py} to your environment. + Make sure to adapt the \c{APPSTORE_*} settings in \c{appstore/settings.py} to your environment, + before you run the server. + + Since package downloads are done via temporary files, you need to setup a cron-job to remove + these temporary files periodically. The cron-job should be triggered every + \c{settings.APPSTORE_DOWNLOAD_EXPIRY/2} minutes; it needs to run: - Since package downloads are done via temporary files, one needs to setup a cron-job to cleanup - these temporary files every now and then. The job should be triggered every \b{settings.APPSTORE_DOWNLOAD_EXPIRY/2} - minutes and it just needs to execute: \code ./manage.py expire-downloads \endcode - \section2 Starting the server - Running the server: + + \section2 Start the Server + + To start the server, run the following command in your terminal: + \code ./manage.py runserver 0.0.0.0:8080 \endcode - will start the server on port 8080, reachable by anyone. One can tweak the listening address to whatever fits the needs. - \section2 Maintenance tasks + + This command starts the server on port 8080, and is reachable by anyone. You can modify the + listening address to another address that suits your use case. + + \section2 Maintain the Server + \list - \li Cleaning up the downloads directory: + \li Clean up the downloads directory: \code ./manage.py expire-downloads \endcode - will remove all files from the downloads directory, that are older than - \b{settings.APPSTORE_DOWNLOAD_EXPIRY} minutes. This should be called from a cron-job (see above). - \li Manually verifying a package for upload: + + This command removes all files from the downloads directory, that are older than + \c{settings.APPSTORE_DOWNLOAD_EXPIRY} minutes. Ideally, this command should be run via a cron-job. + + \li Manually verify a package for upload: + \code ./manage.py verify-upload-package \endcode - will tell if \b{} is a valid package that can be uploaded to the store. - \li Manually adding a store signature to a package: + + This command verifies if \c{} is a valid package that can be uploaded to the Downloads + app. + + \li Manually add a store signature to a package: + \code ./manage.py store-sign-package [device id] \endcode - will first verify \b{}. If this succeeds, it will copy \b{} to \b{} and - add a store signature. The optional \b{[device id]} parameter will lock the generated package to the device with this id. - \endlist - \section1 Architecture - This is a django application. - - \b{TBD} + This command first verifies the \c{}. If this verification succeeds, \c{} + is copied over to \c{} and a store signature is added. The optional \c{[device id]} + parameter locks the generated package to the device with this ID. + \endlist */ -- cgit v1.2.3