← Back to team overview

launchpad-reviewers team mailing list archive

[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
+}
+