launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #29940
[Merge] ~cjwatson/launchpad:charm-buildd-manager into launchpad:master
Colin Watson has proposed merging ~cjwatson/launchpad:charm-buildd-manager into launchpad:master.
Commit message:
charm: Add a launchpad-buildd-manager charm
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/441481
I'm sure this needs some more work, but it should be a reasonable starting point.
--
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:charm-buildd-manager into launchpad:master.
diff --git a/charm/Makefile b/charm/Makefile
index 800f2de..e56d6e3 100644
--- a/charm/Makefile
+++ b/charm/Makefile
@@ -15,7 +15,8 @@ ASSET = ../build/$(BUILD_LABEL)/$(TARBALL)
CHARMS := \
launchpad-admin \
- launchpad-appserver
+ launchpad-appserver \
+ launchpad-buildd-manager
all: ## alias to build
all: build
diff --git a/charm/launchpad-buildd-manager/charmcraft.yaml b/charm/launchpad-buildd-manager/charmcraft.yaml
new file mode 100644
index 0000000..7eec557
--- /dev/null
+++ b/charm/launchpad-buildd-manager/charmcraft.yaml
@@ -0,0 +1,60 @@
+type: charm
+bases:
+ - build-on:
+ - name: ubuntu
+ channel: "20.04"
+ architectures: [amd64]
+ run-on:
+ - name: ubuntu
+ channel: "20.04"
+ architectures: [amd64]
+parts:
+ charm-wheels:
+ source: https://git.launchpad.net/~ubuntuone-hackers/ols-charm-deps/+git/wheels
+ source-commit: "59b32ae07f98051385c96d6d8e7e02ca4f197fe5"
+ source-submodules: []
+ source-type: git
+ plugin: dump
+ organize:
+ "*": charm-wheels/
+ prime:
+ - "-charm-wheels"
+ ols-layers:
+ source: https://git.launchpad.net/ols-charm-deps
+ source-commit: "56d219f60a293a6c73759b8439ef5fdb11e19d1f"
+ source-submodules: []
+ source-type: git
+ plugin: dump
+ organize:
+ "*": layers/
+ stage:
+ - layers
+ prime:
+ - "-layers"
+ launchpad-layers:
+ after:
+ - ols-layers
+ source: https://git.launchpad.net/launchpad-layers
+ source-commit: "0311e05ec2e856e61a2620f68aa3ea35c0a3029b"
+ source-submodules: []
+ source-type: git
+ plugin: dump
+ organize:
+ launchpad-base: layers/layer/launchpad-base
+ stage:
+ - layers
+ prime:
+ - "-layers"
+ launchpad-buildd-manager:
+ after:
+ - charm-wheels
+ - launchpad-layers
+ source: .
+ plugin: reactive
+ build-snaps: [charm/2.x/stable]
+ build-packages: [libpq-dev]
+ build-environment:
+ - CHARM_LAYERS_DIR: $CRAFT_STAGE/layers/layer
+ - CHARM_INTERFACES_DIR: $CRAFT_STAGE/layers/interface
+ - PIP_NO_INDEX: "true"
+ - PIP_FIND_LINKS: $CRAFT_STAGE/charm-wheels
diff --git a/charm/launchpad-buildd-manager/config.yaml b/charm/launchpad-buildd-manager/config.yaml
new file mode 100644
index 0000000..ea622d3
--- /dev/null
+++ b/charm/launchpad-buildd-manager/config.yaml
@@ -0,0 +1,60 @@
+options:
+ active:
+ type: boolean
+ description: If true, enable jobs that may change the database.
+ default: true
+ artifactory_read_credentials:
+ type: string
+ description: >
+ Credentials for reading from Artifactory repositories (formatted as
+ "user:token").
+ default: ""
+ builder_proxy_auth_api_admin_secret:
+ type: string
+ description: >
+ Admin secret for requesting tokens from the builder proxy service.
+ default: ""
+ builder_proxy_auth_api_admin_username:
+ type: string
+ description: Admin username for the builder proxy service.
+ default: ""
+ builder_proxy_auth_api_endpoint:
+ type: string
+ description: Endpoint for builder proxy authentication service.
+ default: ""
+ builder_proxy_host:
+ type: string
+ description: Builder HTTP proxy host.
+ default: ""
+ builder_proxy_port:
+ type: int
+ description: Builder HTTP proxy port.
+ default: 3128
+ builder_reset_private_ssh_key:
+ type: string
+ description: >
+ Base64-encoded private SSH key, used to request builder resets.
+ default: ""
+ builder_reset_public_ssh_key:
+ type: string
+ description: >
+ Base64-encoded public SSH key, used to request builder resets.
+ default: ""
+ cibuild_config:
+ type: string
+ description: >
+ YAML-encoded dictionary mapping pillars to dictionaries of
+ configuration items to set for CI builds of those pillars.
+ default: ""
+ socket_timeout:
+ type: int
+ description: >
+ The time in seconds that buildd-manager will wait for a reply from
+ non-virtualized builders.
+ default: 40
+ virtualized_socket_timeout:
+ type: int
+ description: >
+ The time in seconds that buildd-manager will wait for a reply from
+ non-virtualized builders.
+ default: 30
diff --git a/charm/launchpad-buildd-manager/layer.yaml b/charm/launchpad-buildd-manager/layer.yaml
new file mode 100644
index 0000000..38bd356
--- /dev/null
+++ b/charm/launchpad-buildd-manager/layer.yaml
@@ -0,0 +1,12 @@
+includes:
+ - layer:launchpad-base
+repo: https://git.launchpad.net/launchpad
+options:
+ ols-pg:
+ databases:
+ db:
+ name: launchpad_dev
+ roles:
+ - buildd_manager
+ - process_upload
+ - retry_depwait
diff --git a/charm/launchpad-buildd-manager/metadata.yaml b/charm/launchpad-buildd-manager/metadata.yaml
new file mode 100644
index 0000000..0e28e9f
--- /dev/null
+++ b/charm/launchpad-buildd-manager/metadata.yaml
@@ -0,0 +1,16 @@
+name: launchpad-buildd-manager
+display-name: launchpad-buildd-manager
+summary: Launchpad build farm manager
+maintainer: Colin Watson <cjwatson@xxxxxxxxxxxxx>
+description: |
+ Launchpad is an open source suite of tools that help people and teams
+ to work together on software projects.
+
+ This charm runs a service that supervises the Launchpad build farm,
+ dispatching jobs to idle builders and collecting results.
+tags:
+ # https://juju.is/docs/charm-metadata#heading--charm-store-fields
+ - network
+series:
+ - focal
+subordinate: false
diff --git a/charm/launchpad-buildd-manager/reactive/launchpad-buildd-manager.py b/charm/launchpad-buildd-manager/reactive/launchpad-buildd-manager.py
new file mode 100644
index 0000000..87c46de
--- /dev/null
+++ b/charm/launchpad-buildd-manager/reactive/launchpad-buildd-manager.py
@@ -0,0 +1,142 @@
+# Copyright 2023 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+import base64
+import os.path
+import subprocess
+
+import yaml
+from charmhelpers.core import hookenv, host, templating
+from charms.launchpad.base import (
+ config_file_path,
+ configure_cron,
+ configure_lazr,
+ get_service_config,
+ home_dir,
+ lazr_config_files,
+)
+from charms.reactive import helpers, set_state, when, when_not
+from ols import base
+
+
+def base64_decode(value):
+ return base64.b64decode(value.encode("ASCII"))
+
+
+def configure_keys(config):
+ ssh_dir = os.path.join(home_dir(), ".ssh")
+ ssh_private_path = os.path.join(ssh_dir, "builder-reset")
+ ssh_public_path = os.path.join(ssh_dir, "builder-reset.pub")
+ if (
+ config["builder_reset_private_ssh_key"]
+ and config["builder_reset_public_ssh_key"]
+ ):
+ hookenv.log("Writing SSH keys.")
+ if not os.path.exists(ssh_dir):
+ host.mkdir(
+ ssh_dir, owner=base.user(), group=base.user(), perms=0o700
+ )
+ host.write_file(
+ ssh_private_path,
+ base64_decode(config["builder_reset_private_ssh_key"]),
+ owner=base.user(),
+ group=base.user(),
+ perms=0o600,
+ )
+ host.write_file(
+ ssh_public_path,
+ base64_decode(config["builder_reset_public_ssh_key"]),
+ owner=base.user(),
+ group=base.user(),
+ perms=0o644,
+ )
+ else:
+ for path in (ssh_private_path, ssh_public_path):
+ if os.path.exists(path):
+ os.unlink(path)
+
+
+def configure_service(config):
+ hookenv.log("Writing systemd service.")
+ templating.render(
+ "launchpad-buildd-manager.service.j2",
+ "/lib/systemd/system/launchpad-buildd-manager.service",
+ config,
+ )
+ subprocess.run(["systemctl", "daemon-reload"], check=True)
+
+
+def configure_logrotate(config):
+ hookenv.log("Writing logrotate configuration.")
+ templating.render(
+ "logrotate.conf.j2",
+ "/etc/logrotate.d/launchpad-buildd-manager",
+ config,
+ perms=0o644,
+ )
+
+
+def config_files():
+ files = []
+ files.extend(lazr_config_files())
+ files.append(
+ config_file_path("launchpad-buildd-manager/launchpad-lazr.conf")
+ )
+ files.append(
+ config_file_path(
+ "launchpad-buildd-manager-secrets-lazr.conf", secret=True
+ )
+ )
+ return files
+
+
+@when("launchpad.base.configured")
+@when_not("service.configured")
+def configure():
+ config = get_service_config()
+ config["buildd_manager_dir"] = os.path.join(
+ base.base_dir(), "buildd-manager"
+ )
+ config["cibuild_config"] = yaml.safe_load(config["cibuild_config"])
+ host.mkdir(
+ config["buildd_manager_dir"],
+ owner=base.user(),
+ group=base.user(),
+ perms=0o755,
+ )
+ configure_lazr(
+ config,
+ "launchpad-buildd-manager-lazr.conf",
+ "launchpad-buildd-manager/launchpad-lazr.conf",
+ )
+ configure_lazr(
+ config,
+ "launchpad-buildd-manager-secrets-lazr.conf",
+ "launchpad-buildd-manager-secrets-lazr.conf",
+ secret=True,
+ )
+ configure_keys(config)
+ configure_service(config)
+ configure_logrotate(config)
+ configure_cron(config, "crontab.j2")
+
+ if config["active"]:
+ if helpers.any_file_changed(
+ [
+ base.version_info_path(),
+ "/lib/systemd/system/launchpad-buildd-manager.service",
+ ]
+ + config_files()
+ ):
+ hookenv.log("Config files changed; restarting buildd-manager")
+ host.service_restart("launchpad-buildd-manager")
+ host.service_resume("launchpad-buildd-manager")
+ else:
+ host.service_pause("launchpad-buildd-manager")
+
+ set_state("service.configured")
+
+
+@when("service.configured")
+def check_is_running():
+ hookenv.status_set("active", "Ready")
diff --git a/charm/launchpad-buildd-manager/templates/crontab.j2 b/charm/launchpad-buildd-manager/templates/crontab.j2
new file mode 100644
index 0000000..435cf43
--- /dev/null
+++ b/charm/launchpad-buildd-manager/templates/crontab.j2
@@ -0,0 +1,30 @@
+TZ=UTC
+MAILTO={{ cron_mailto }}
+LPCONFIG=launchpad-buildd-manager
+
+{% if active -%}
+# Automatically retry builds in the "Dependency wait" state if their
+# dependencies can now be satisfied.
+25 * * * * {{ code_dir }}/cronscripts/buildd-retry-depwait.py -q --log-file=DEBUG2:{{ logs_dir }}/buildd-retry-depwait.log
+
+# Process uploaded builds.
+* * * * * {{ code_dir }}/scripts/process-upload.py -C buildd --builds {{ buildd_manager_dir }}/ -q --log-file=DEBUG:{{ logs_dir }}/process-build-uploads.log
+
+{% endif -%}
+# Clean up the accepted queue every hour, as it's redundant:
+# https://bugs.launchpad.net/launchpad/+bug/361192
+45 * * * * find {{ buildd_manager_dir }}/accepted/ -maxdepth 1 -type d -execdir rm -rf {} + >/dev/null 2>&1
+
+# Directories older than 1 month can be deleted
+00 00 * * * find {{ buildd_manager_dir }}/rejected/ -maxdepth 1 -type d -mtime +30 -execdir rm -rf {} + >/dev/null 2>&1
+
+# Clean out failed directory: https://portal.admin.canonical.com/C98568
+0 1 * * * find {{ buildd_manager_dir }}/failed/ -maxdepth 1 -type d -mtime +7 -execdir rm -rf {} + >/dev/null 2>&1
+
+# Give up on in-progress downloads from builders after a couple of days
+0 2 * * * find {{ buildd_manager_dir }}/grabbing/ -maxdepth 1 -type d -mtime +2 -execdir rm -rf {} + >/dev/null 2>&1
+
+# Catch up with publishing OOPSes that were temporarily spooled to disk due
+# to RabbitMQ being unavailable.
+*/15 * * * * {{ code_dir }}/bin/datedir2amqp --exchange oopses --host {{ rabbitmq_host }} --username {{ rabbitmq_username }} --password {{ rabbitmq_password }} --vhost {{ rabbitmq_vhost }} --repo {{ oopses_dir }} --key ""
+
diff --git a/charm/launchpad-buildd-manager/templates/launchpad-buildd-manager-lazr.conf b/charm/launchpad-buildd-manager/templates/launchpad-buildd-manager-lazr.conf
new file mode 100644
index 0000000..613f8e6
--- /dev/null
+++ b/charm/launchpad-buildd-manager/templates/launchpad-buildd-manager-lazr.conf
@@ -0,0 +1,36 @@
+# Public configuration data. The contents of this file may be freely shared
+# with developers if needed for debugging.
+
+# A schema's sections, keys, and values are automatically inherited, except
+# for '.optional' sections. Update this config to override key values.
+# Values are strings, except for numbers that look like ints. The tokens
+# true, false, and none are treated as True, False, and None.
+
+{% from "macros.j2" import opt -%}
+
+[meta]
+extends: ../launchpad-base-lazr.conf
+
+[builddmaster]
+authentication_endpoint: http://{{ domain_xmlrpc_private }}:{{ port_xmlrpc }}/authserver
+authentication_timeout: 60
+{{- opt("builder_proxy_auth_api_admin_username", builder_proxy_auth_api_admin_username) }}
+{{- opt("builder_proxy_auth_api_endpoint", builder_proxy_auth_api_endpoint) }}
+{{- opt("builder_proxy_host", builder_proxy_host) }}
+{{- opt("builder_proxy_port", builder_proxy_port) }}
+root: {{ buildd_manager_dir }}
+socket_timeout: {{ socket_timeout }}
+virtualized_socket_timeout: {{ virtualized_socket_timeout }}
+{%- if builder_reset_private_ssh_key and builder_reset_public_ssh_key %}
+vm_resume_command: ssh -o StrictHostKeyChecking=no -i /home/{{ user }}/.ssh/builder-reset ppa@%(vm_host)s ppa-reset %(buildd_name)s
+{%- endif %}
+
+{% if cibuild_config -%}
+{% for pillar, config in cibuild_config.items() -%}
+[cibuild.{{ pillar }}]
+{%- for key, value in config.items() %}
+{{ key }}: {{ value }}
+{%- endfor %}
+{%- endfor %}
+{% endif %}
+
diff --git a/charm/launchpad-buildd-manager/templates/launchpad-buildd-manager-secrets-lazr.conf b/charm/launchpad-buildd-manager/templates/launchpad-buildd-manager-secrets-lazr.conf
new file mode 100644
index 0000000..af20066
--- /dev/null
+++ b/charm/launchpad-buildd-manager/templates/launchpad-buildd-manager-secrets-lazr.conf
@@ -0,0 +1,18 @@
+# Secret configuration data. This is stored in an overlay directory, mainly
+# to avoid accidental information leaks from the public configuration file.
+# Entries in this file should not be shared with developers, although the
+# structure of the file is not secret, only configuration values.
+
+# A schema's sections, keys, and values are automatically inherited, except
+# for '.optional' sections. Update this config to override key values.
+# Values are strings, except for numbers that look like ints. The tokens
+# true, false, and none are treated as True, False, and None.
+
+{% from "macros.j2" import opt -%}
+
+[artifactory]
+{{- opt("read_credentials", artifactory_read_credentials) }}
+
+[builddmaster]
+{{- opt("builder_proxy_auth_api_admin_secret", builder_proxy_auth_api_admin_secret) }}
+
diff --git a/charm/launchpad-buildd-manager/templates/launchpad-buildd-manager.service.j2 b/charm/launchpad-buildd-manager/templates/launchpad-buildd-manager.service.j2
new file mode 100644
index 0000000..0e6ae18
--- /dev/null
+++ b/charm/launchpad-buildd-manager/templates/launchpad-buildd-manager.service.j2
@@ -0,0 +1,18 @@
+[Unit]
+Description=Launchpad build farm manager
+After=network.target
+ConditionPathExists=!{{ code_dir }}/maintenance.txt
+
+[Service]
+User={{ user }}
+Group={{ user }}
+WorkingDirectory={{ code_dir }}
+Environment=LPCONFIG=launchpad-buildd-manager
+ExecStart={{ code_dir }}/bin/twistd --python={{ code_dir }}/daemons/buildd-manager.tac --logfile={{ logs_dir }}/buildd-manager.log --pidfile= --nodaemon --umask=0022
+ExecReload=/bin/kill -USR1 $MAINPID
+Restart=on-failure
+LimitNOFILE=65536
+
+[Install]
+WantedBy=multi-user.target
+
diff --git a/charm/launchpad-buildd-manager/templates/logrotate.conf.j2 b/charm/launchpad-buildd-manager/templates/logrotate.conf.j2
new file mode 100644
index 0000000..657e00d
--- /dev/null
+++ b/charm/launchpad-buildd-manager/templates/logrotate.conf.j2
@@ -0,0 +1,16 @@
+{{ logs_dir }}/*.log
+{
+ rotate 90
+ daily
+ dateext
+ delaycompress
+ compress
+ notifempty
+ missingok
+ create 0644 {{ user }} {{ user }}
+ sharedscripts
+ postrotate
+ systemctl reload buildd-manager.service
+ endscript
+}
+