AlmaLinux Build System Web-Server (albs-web-server) is designed to control multiple Build System's processes like build, sign and release packages. Web-Server maintains the following functionality:
- Creates, restarts and deletes builds;
- Depending on a request, Web-Server assigns a build from the queue as a task for the Build Node, the Test System, the Sign Node. When the task is done, Web-Server gathers the result.
- Web-server receives a request from the Build Node, and if there is an idle (not started) task, it sends this task to the Build Node. It also works with
failedandstartedstatuses depending on a build result from the Build Node. After the build task is successfully completed, the Web Server schedules this task to the Test System. - When testing the package is successfully completed, Web-Server sends it to the Sign Node and releases the signed package to production repositories.
- Web-server receives a request from the Build Node, and if there is an idle (not started) task, it sends this task to the Build Node. It also works with
- Web-server allows maintaining platforms for builds - you can add a new platform with architectures. It also manages distributions, repositories and signing keys.
- Authorization to the Web-server is via GitHub.
- Web-Server has the Multilib support via beholder and noarch support to copy noarch packages throughout architectures.
- Web-Server sync production repositories into Build System's Pulp.
- Web-server works with
gitea_listener,git_cacherand Pulp.
Mentioned tools and libraries are required for ALBS Web-Server to run in the current state:
- PostgreSQL 13 - database
- Pulp - artifacts storage (packages, repositories, distributions, etc.)
- Redis - storage for source repositories info and frontend info cache
- Nginx
- Docker
- Docker-compose
- Python 3.9
- FastAPI - REST API framework
- SQLAlchemy - database ORM
- Alembic - database schema migration tool;
This config file is needed for the Web-Server to launch gitea-listener:
---
mqtt_queue_host: mosquitto
mqtt_queue_port: 1883
mqtt_queue_topic_unmodified: gitea-webhooks-unmodified
mqtt_queue_topic_modified: gitea-webhooks-modified
mqtt_queue_qos: 2
mqtt_client_id: albs_gitea_listener
mqtt_queue_username:
mqtt_queue_password:
mqtt_queue_clean_session: False
albs_jwt_token:
albs_address: http://web_server:8080
You can start the system using the Docker Compose tool.
Pre-requisites:
dockeranddocker-composetools are installed and set up;
To start the system, run the following command: docker-compose up -d. To rebuild images after your local changes, just run docker-compose up -d --build.
In case you are building containers for the first time, there is how it should be done:
#!/bin/bash
set -e pipefail
mkdir -p ../volumes/pulp/settings
echo "CONTENT_ORIGIN='http://pulp'
ANSIBLE_API_HOSTNAME='http://pulp'
ANSIBLE_CONTENT_HOSTNAME='http://pulp/pulp/content'
TOKEN_AUTH_DISABLED=True" >> ../volumes/pulp/settings/settings.py
docker-compose up -d --build --force-recreate --remove-orphans
sleep 25
docker exec -it albs-web-server_pulp_1 bash -c 'pulpcore-manager reset-admin-password --password="admin"'
Web-server works with multiple parts of the Build System. Web-server works with API requests that are divided by usage.
POST /ping endpoint accepts the following payload:
{
node_status: {
active_tasks: list # accepts a list of integer values;
}
}POST /build_done endpoint accepts the following payload:
{
build_done: {
task_id: integer # accepts a integer value;
status: string # accepts literal values that are 'done', 'failed', 'excluded';
artifacts: {
name: string # accepts a string value;
type: string # accepts literal values that are 'rpm', 'build_log';
href: string # accepts a string value;
}
}
}POST /get_task endpoint accepts the following payload:
{
request: {
supported_arches: list # accepts a list of string values;
}
}This endpoint has a response model that returns information about the platform:
{
id: integer # accepts an integer value;
arch: string # accepts a string value;
ref: {
url: string # accepts a string value;
git_ref: accepts an optional string value # it's an optional value that allows being absent;
}
platform: {
name: string # accepts a string value;
type: string # accepts literal values that are 'rpm', 'deb';
data: dictrionary # dictionary, where a key should be a string while a value could be of any type;
}
created_by: {
name: string # accepts a string value;
email: string # accepts a string value;
}
repositories: string # accepts a list of string values that are 'name' and 'url';
linked_builds: accepts a list of integer values # it's an optional value that allows being absent;
}POST / endpoint accepts the following payload:
{
build: {
platforms: { # check that a list has at least one item;
name: string # accepts a string value;
arch_list: list # accepts a list of string values;
}
tasks: { # check that a list has at least one item;
url: string # accepts a string value;
git_ref: accepts an optional string value # it's an optional value that allows being absent;
}
linked_builds: accepts a list of integer values # it's an optional value that allows being absent;
}
}This endpoint has a response model that returns information about the platform:
{
id: integer # accepts an integer value;
created_at: datetime # accepts date time like year,month, etc;
tasks: {
id: integer # accepts an integer value;
ts: accepts date time timestamp like year, month, etc # it's an optional value that allows being absent;
status: integer # accepts an integer value;
index: integer # accepts an integer value;
arch: string #accepts a string value;
platform: {
id: integer # accepts an integer value;
type: string # accepts a string value;
name: string # accepts a string value;
arch_list: list # accepts a list of string values;
}
ref: {
url: string # accepts a string value;
git_ref: accepts an optional string value # it's an optional value that allows being absent;
}
artifacts: {
id: integer # accepts an integer value;
name: string # accepts a string value;
type: string # accepts a string value;
href: string # accepts a string value;
}
}
user: {
id: integer # accepts an integer value;
username: string # accepts a string value;
email: string # accepts a string value;
}
linked_builds: accepts a list of integer values # it's an optional value that allows being absent;
}GET / this endpoint has a response model that returns one of the options.
It can return a list of platforms:
{
id: integer # accepts an integer value;
created_at: datetime # accepts date time like year, month, etc;
tasks: {
id: integer # accepts a integer value;
ts: accepts date time timestamp like year, month, etc # it's an optional value that allows to be absent;
status: integer # accepts an integer value;
index: integer # accepts an integer value;
arch: string # accepts a string value;
platform: {
id: integer # accepts an integer value;
type: string # accepts a string value;
name: string # accepts a string value
arch_list: list # accepts a list of a string values;
}
ref: {
url: string # accepts a string value;
git_ref: optional string value # it's an optional value that allows being absent;
}
artifacts: {
id: integer # accepts an integer value;
name: string # accepts a string value;
type: string # accepts a string value;
href: string # accepts a string value;
}
}
user: {
id: integer # accepts an integer value;
username: string # accepts a string value;
email: string # accepts a string value;
}
linked_builds: string value # it's an optional value that allows being absent;
}or it can return the following information about the platform:
{
builds: {
id: integer # accepts an integer value;
created_at: datetime # accepts date time like year, month, etc;
tasks: {
id: integer # accepts an integer value;
ts: datetime timestamp like year, month, etc # it's an optional value that allows to be absent;
status: integer # accepts an integer value;
index: integer # accepts an integer value;
arch: string # accepts a string value;
platform: {
id: integer # accepts an integer value;
type: string # accepts a string value;
name: string # accepts a string value
arch_list: list # accepts a list of string values;
}
ref: {
url: string # accepts a string value;
git_ref: optional string value # it's an optional value that allows being absent;
}
artifacts: {
id: integer # accepts an integer value;
name: string # accepts a string value;
type: string # accepts a string value;
href: string # accepts a string value;
}
}
user: {
id: integer # accepts an integer value;
username: string # accepts a string value;
email: string # accepts a string value;
}
linked_builds: string value # it's an optional value that allows being absent;
}
total_builds: optional integer value # it's an optional value that allows being absent;
current_page: optional integer value # it's an optional value that allows being absent;
}GET /{build_id}/ endpoint accepts an integer value build_id . This endpoint has a response model that returns information about the platform:
{
id: integer # accepts an integer value;
created_at: datetime # accepts date time like year, month, etc;
tasks: {
id: integer # accepts an integer value;
ts: date time timestamp like year, month, etc # it's an optional value that allows being absent;
status: integer # accepts an integer value;
index: integer # accepts an integer value;
arch: string # accepts a string value;
platform: {
id: integer # acceptrs an integer value;
type: string # accepts a string value;
name: string # accepts a string value
arch_list: list # accepts a list of string values;
}
ref: {
url: string # accepts string value;
git_ref: optional string value # it's an optional value that allows being absent;
}
artifacts: {
id: integer # accepts an integer value;
name: string # accepts a string value;
type: string # accepts a string value;
href: string # accepts a string value;
}
}
user: {
id: integer # accepts an integer value;
username: string # accepts a string value;
email: string # accepts a string value;
}
linked_builds: string value # it's an optional value that allows being absent;
}PATCH /{build_id}/restart-failed endpoint accepts an integer value build_id. This endpoint has a response model that returns information about the platform:
{
id: integer # accepts an integer value;
created_at: datetime # accepts date time like year, month, etc;
tasks: {
id: integer # accepts an integer value;
ts: date time timestamp like year, month, etc # it's an optional value that allows being absent;
status: integer # accepts an integer value;
index: integer # accepts an integer value;
arch: string # accepts a string value;
platform: {
id: integer # acceptrs an integer value;
type: string # accepts a string value;
name: string # accepts a string value
arch_list: list # accepts a list of string values;
}
ref: {
url: string # accepts string value;
git_ref: optional string value # it's an optional value that allows being absent;
}
artifacts: {
id: integer # accepts an integer value;
name: string # accepts a string value;
type: string # accepts a string value;
href: string # accepts a string value;
}
}
user: {
id: integer # accepts an integer value;
username: string # accepts a string value;
email: string # accepts a string value;
}
linked_builds: string value # it's an optional value that allows being absent;
}DELETE /{build_id}/remove endpoint accepts an integer value build_id. This endpoint returns the '204' status code.
POST / accepts the following payload:
{
distribution: {
name: string # accepts a string value;
platforms: list # accepts a list of a string values;
}
}This endpoint has a response model that returns information about the platform:
{
id: integer # accepts an integer value;
name: string # accepts a string value;
}POST /add/{build_id}/{distribution}/ endpoint accepts a string distribution value and an integer build_id value. This endpoint has a response model that returns a dictionary, where the key should be a string while the value could be of boolean type.
POST /remove/{build_id}/{distribution}/ endpoint accepts a string distribution value and an integer build_id value. This endpoint has a response model that returns a dictionary, where the key should be a string while the value could be of boolean type.
GET / endpoint has a response model that returns the list of platforms:
{
id: integer # accepts an integer value;
name: string # accepts a string value;
}POST / endpoint accepts the following payload:
{
name: string # accepts a string value;
type: literal # accepts literal values that are 'rpm', 'deb';
distr_type: string # accepts a string value;
distr_version: string # accepts a string value;
test_dist_name: string # accepts a string value
arch_list: list # accepts a list of a string values;
repos: {
name: string # aceppts a string value;
arch: string # accepts a string value;
url: string # accepts a string value;
type: string # accepts a string value;
}
data: dictionary # dictionary with a key should be a string while a value could be any type.
}This endpoint has a response model that returns information about the platform:
{
id: integer # accepts an integer value;
name: string # accepts a string value;
arch_list: list # accepts list of a string values;
}PUT / endpoint accepts the following payload:
{
name: strint # accepts a string value;
type: literal value that is 'rpm' or 'deb' # it's an optional value that allows being absent;
distr_type: string value # it's an optional value that allows being absent;
distr_version: string value # it's an optional value that allows being absent;
arch_list: list of string values # it's an optional value that allows bein absent;
repos: { # this is optional and allows being absent;
name: string # accepts a string value;
arch: string # accepts a string value;
url: string # accepts a string value;
type: string # accepts a string value;
}
data: dictionary, where a key should be a string while a value could be of any type # it's an optional value that allows being absent;
}This endpoint has a response model that returns information about the platform:
{
id: integer # accepts an integer value;
name: string # accepts a string value;
arch_list: list # accepts a list of string values;
}GET / endpoint has a response model that returns a list of platforms:
{
id: integer # accepts an integer value;
name: string # accepts a string value;
arch_list: list # accepts a list of string values;
}PATCH /{platform_id}/add-repositories endpoint has a response model that returns information about the platform:
{
id: integer # accepts an integer value;
name: string # accepts a string value;
arch_list: list # accepts a list of string values;
}PATCH /{platform_id}/remove-repositories endpoint has a response model that returns information about the platform:
{
id: integer # accepts an integer value;
name: string # accepts a string value;
arch_list: list # accepts a list of string values;
}GET /alma has a response model that returns a list of projects:
{
name: string # accepts a string value;
clone_url: string # accepts a string value;
tags: list # accepts a list of string values;
branches: list # accepts a list of string values;
}GET /alma/modularity endpoint has a response model that returns a list of projects:
{
name: string # accepts a string value;
clone_url: string # accepts a string value;
tags: list # accepts a list of string values;
branches: list # accepts a list of string values;
}GET / has a response model that returns a list of releases:
{ id: integer # accepts an integer value;
status: integer # accepts an integer value;
build_ids: list # accepts a list of integer values;
plan: dictionary, where the key should be a string while value can be of any type # it's an optional value that allows being absent;
created_by: {
id: integer # accepts an integer value;
username: string # accepts a string value;
email: string # accepts a string value;
}
}POST /new/ endpoint accepts the following payload:
{
builds: list # accepts a list of integer values;
platform_id: integer # accepts an integer value;
reference_platform_id: integer # accepts an integer value;
}This endpoint has a response model that returns information about the release:
{ id: integer # accepts an integer value;
status: integer # accepts an integer value;
build_ids: list # accepts a list of integer values;
plan: dictionary, where the key should be a string while value can be of any type # it's an optional value that allows being absent;
created_by: {
id: integer # accepts an integer value;
username: string # accepts a string value;
email: string # accepts a string value;
}
}PUT /{release_id}/ endpoint accepts an integer value release_id and the following payload:
{
builds: optional list value # accepts a list of integer values;
plan: dictionary, where the key should be a string while value can be of any type # it's an optional value that allows being absent;
}This endpoint has a response model that returns information about the release:
{ id: integer # accepts an integer value;
status: integer # accepts an integer value;
build_ids: list # accepts a list of integer values;
plan: dictionary, where the key should be a string while value can be of any type # it's an optional value that allows being absent;
created_by: {
id: integer # accepts an integer value;
username: string # accepts a string value;
email: string # accepts a string value;
}
}POST /{release_id}/commit/ endpoint accepts an integer value release_id. This endpoint has a response model that returns the result of the release commit:
{
release: {
id: integer # accepts an integer value;
status: integer # accepts an integer value;
build_ids: list # accepts a list of integer values;
plan: dictionary, where key should be a string while value can be of any type # it's an optional value that allows being absent;
created_by: {
id: integer # accepts an integer value;
username: string # accepts a string value;
email: string # accepts a string value;
}
}
message: string # accepts a string value;
}GET / endpoint has a response model that returns a list of repositories:
{
id: integer # accepts an integer value;
name: string # accepts a string value;
arch: string # accepts a string value;
url: string # accepts a string value;
type: string # accepts a string value;
debug: optional boolean value # it's an optional value that allows being absent;
production: optional boolean value # it's an optional value that allows being absent;
pulp_href: optional boolean value # it's an optional value that allows being absent;
}GET /{repository_id}/ endpoint accepts an integer value repository_id. This endpoint has a response model that returns information about the repository or 'None':
{
id: integer # accepts an integer value;
name: string # accepts a string value;
arch: string # accepts a string value;
url: string # accepts a string value;
type: string # accepts a string value;
debug: optional boolean value # it's an optional value that allows being absent;
production: optional boolean value # it's an optional value that allows being absent;
pulp_href: optional boolean value # it's an optional value that allows being absent;
}GET / endpoint has a response model that returns a list of sign keys:
{
id: integer # accepts an integer value;
name: string # accepts a string value;
description: string # accepts a string value;
keyid: string # accepts a string value;
public_url: string # accepts a string value;
inserted: datetime # accepts date time like year, month, etc;
}POST /new/ endpoint accepts the following payload:
{
name: string # accepts a string value;
description: string # accepts a string value;
keyid: string # accepts a string value;
fingerprint: string # accepts a string value;
public_url: string # accepts a string value;
}This endpoint has a response model that returns information about the sign key:
{
id: integer # accepts an integer value;
name: string # accepts a string value;
description: string # accepts a string value;
keyid: string # accepts a string value;
public_url: string # accepts a string value;
inserted: datetime # accepts date time like year, month, etc;
}PUT /{sign_key_id/ endpoint accepts an integer value sign_key_id and the following payload:
{
name: string value # it's an optional value that allows being absent;
description: string value # it's an optional value that allows being absent;
keyid: string value # it's an optional value that allows being absent;
fingerprint: string value # it's an optional value that allows being absent;
public_url: string value # it's an optional value that allows being absent;
}This endpoint has a response model that returns information about the sign key:
{
id: integer # accepts an integer value;
name: string # accepts a string value;
description: string # accepts a string value;
keyid: string # accepts a string value;
public_url: string # accepts a string value;
inserted: datetime # accepts date time like year, month, etc;
}GET / endpoint accepts an integer value build_id. This endpoint has a response model that returns the list of sign tasks:
{
id: integer # accepts an integer value;
build_id: integer # accepts an integer value;
sign_key: {
id: integer # accepts an integer value;
name: string # accepts a string value;
description: string # accepts a string value;
keyid: string # accepts a string value;
public_url: string # accepts a string value;
inserted: datetime # accepts date time like year, month, etc;
}
status: integer # accepts an integer value;
error_message: string value # it's an optional value that allows being absent;
log_href: string value # it's an optional value that allows being absent;
}POST / endpoint accepts the following payload:
{
build_id: integer # accepts an integer value;
sign_key_id: integer # accepts an integer value;
}This endpoint has a response model that returns the sign task:
{
id: integer # accepts an integer value;
build_id: integer # accepts an integer value;
sign_key: {
id: integer # accepts an integer value;
name: string # accepts a string value;
description: string # accepts a string value;
keyid: string # accepts a string value;
public_url: string # accepts a string value;
inserted: datetime # accepts date time like year, month, etc;
}
status: integer # accepts an integer value;
error_message: string value # it's an optional value that allows being absent;
log_href: string value # it's an optional value that allows being absent;
}POST /get_sign_task/ endpoint accepts the following payload:
{
key_ids: string # accepts a list of string values;
}This endpoint has a response model that returns a union of a dictionary with key and value of any type, and the information about available sign tasks:
{
id: integer value # it's an optional value that allows being absent;
build_id: integer value # it's an optional value that allows being absent;
keyid: string value # it's an optional value that allows being absent;
packages: {
key_ids: a list of string values # it's an optional value that allows being absent;
}
}POST /{sign_task_id}/complete/ endpoint accepts an integer value sign_task_id and the following payload:
{
build_id: integer # accepts an ingeter valut
success: boolean # accepts a boolean value;
error_message: string value # it's an optional value that allows being absent;
log_href: string value # it's an optional value that allows being absent;
packages: {
key_ids: a list of string values # it's an optional value that allows being absent;
}
}This endpoint has a response model that returns information about the completed task:
{
success: boolean # accepts a boolean value;
}POST /{test_task_id}/result/ endpoint accepts an integer test_task_id value and the following payload:
{
result: {
api_version: string # accepts a string value;
result: dictionary # a dictionary where key and value are of any type;
}
}PUT /build/{build_id}/restart endpoint accepts an integer build_id value.
PUT /build_task/{build_task_id}/restart endpoint accepts an integer build_task_id value.
GET /{build_task_id}/latest endpoint accepts an integer build_task_id value. This endpoint has a response model that returns a list of platforms:
{
id: integer # accepts an integer value;
package_name: string # accepts a string value;
package_version: string # accepts a string value;
package_release: string value # it's an optional value that allows being absent;
status: integer # accepts an integer value;
revision: integer # accepts an integer value;
alts_response: dictionary # a dictionary where key and value are of any type;
}GET /{build_task_id}/{revision} endpoint accepts an integer build_task_id value and an integer revision value. This endpoint has a response model that returns a list of platforms:
{
id: integer # accepts an integer value;
package_name: string # accepts a string value;
package_version: string # accepts a string value;
package_release: string value # it's an optional value that allows being absent;
status: integer # accepts an integer value;
revision: integer # accepts an integer value;
alts_response: dictionary # a dictionary where key and value are of any type;
}POST /login/github endpoint accepts the following payload:
{
user: {
code: string # accepts a string value;
}
}This endpoint has a response model that returns information about the platform:
{
id: integer # accepts an integer value;
username: string # accepts a string value;
email: string # accepts a string value;
jwt_token: string # accepts a string value;
}GET / endpoint has a response model that returns information about the platform:
id: integer # accepts an integer value;
username: string # accepts a string value;
email: string # accepts a string value;GET /all_user endpoint has a response model that returns information about users:
{
id: integer # accepts an integer value;
username: string # accepts a string value;
email: string # accepts a string value;
}All issues should be reported to the Build System project.