Skip to content

Commit

Permalink
feat: GitHub runner version (#20)
Browse files Browse the repository at this point in the history
* feat: runner version config parsing

* feat: runner version pass to cli

* chore: remove mac artifacts

* chore: ignore mac fs artifacts

* chore: revert unneeded charmcraft reformat

* fix: charmcraft type

* fix: no runner version
  • Loading branch information
yanksyoon committed Jul 9, 2024
1 parent 95c20b2 commit 562097b
Show file tree
Hide file tree
Showing 7 changed files with 260 additions and 94 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,7 @@ __pycache__/
*.egg-info/
*/*.rock
clouds.yaml

# Mac filesystem artifact
**/.DS_Store
**./.DS_Store
38 changes: 22 additions & 16 deletions charmcraft.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,25 +22,25 @@ links:
type: charm
bases:
- build-on:
- name: ubuntu
channel: "22.04"
architectures:
- arm64
- name: ubuntu
channel: "22.04"
architectures:
- arm64
run-on:
- name: ubuntu
channel: "22.04"
architectures:
- arm64
- name: ubuntu
channel: "22.04"
architectures:
- arm64
- build-on:
- name: ubuntu
channel: "22.04"
architectures:
- amd64
- name: ubuntu
channel: "22.04"
architectures:
- amd64
run-on:
- name: ubuntu
channel: "22.04"
architectures:
- amd64
- name: ubuntu
channel: "22.04"
architectures:
- amd64

config:
options:
Expand Down Expand Up @@ -106,6 +106,12 @@ config:
type: int
default: 5
description: Number of image revisions to keep before deletion.
runner-version:
type: string
default: ""
description: |
The GitHub runner version to use, e.g. 2.317.0. Empty default value will fetch the latest
version by default. See https://github.com/actions/runner/releases.
provides:
image:
Expand Down
113 changes: 59 additions & 54 deletions src/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,28 +131,30 @@ def configure_cron(run_config: state.BuilderRunConfig, interval: int) -> bool:
Returns:
True if cron is reconfigured. False otherwise.
"""
builder_exec_command: str = " ".join(
[
# HOME path is required for GO modules.
f"HOME={UBUNTU_HOME}",
"/usr/bin/run-one",
"/usr/bin/sudo",
"--preserve-env",
str(GITHUB_RUNNER_IMAGE_BUILDER_PATH),
"run",
run_config.cloud_name,
IMAGE_NAME_TMPL.format(
IMAGE_BASE=run_config.base.value,
ARCH=run_config.arch.value,
),
"--base-image",
run_config.base.value,
"--keep-revisions",
str(run_config.num_revisions),
"--callback-script",
str(run_config.callback_script.absolute()),
]
)
commands = [
# HOME path is required for GO modules.
f"HOME={UBUNTU_HOME}",
"/usr/bin/run-one",
"/usr/bin/sudo",
"--preserve-env",
str(GITHUB_RUNNER_IMAGE_BUILDER_PATH),
"run",
run_config.cloud_name,
IMAGE_NAME_TMPL.format(
IMAGE_BASE=run_config.base.value,
ARCH=run_config.arch.value,
),
"--base-image",
run_config.base.value,
"--keep-revisions",
str(run_config.num_revisions),
"--callback-script",
str(run_config.callback_script.absolute()),
]
if run_config.runner_version:
commands += ["--runner-version", run_config.runner_version]

builder_exec_command: str = " ".join(commands)
cron_text = (
f"0 */{interval} * * * {UBUNTU_USER} {builder_exec_command} "
f">> {OUTPUT_LOG_PATH} 2>&1 || {state.FAILED_CALLBACK_SCRIPT_PATH.absolute()}\n"
Expand Down Expand Up @@ -191,41 +193,44 @@ def run(config: state.BuilderRunConfig) -> None:
BuilderRunError: if there was an error while launching the subprocess.
"""
try:
commands = [
"(",
# HOME path is required for GO modules.
f"HOME={UBUNTU_HOME}",
"/usr/bin/run-one",
"/usr/bin/sudo",
"--preserve-env",
str(GITHUB_RUNNER_IMAGE_BUILDER_PATH),
"run",
config.cloud_name,
IMAGE_NAME_TMPL.format(
IMAGE_BASE=config.base.value,
ARCH=config.arch.value,
),
"--base-image",
config.base.value,
"--keep-revisions",
str(config.num_revisions),
"--callback-script",
str(config.callback_script.absolute()),
]
if config.runner_version:
commands += ["--runner-version", config.runner_version]
commands += [
">>",
str(OUTPUT_LOG_PATH),
"2>&1",
"||",
# Run the callback script without Openstack ID argument to let the charm know
# about the error.
str(state.FAILED_CALLBACK_SCRIPT_PATH.absolute()),
")",
"&",
]
# The callback invokes another hook - which cannot be run when another hook is already
# running. Call the process as a background and exit immediately.
subprocess.Popen( # pylint: disable=consider-using-with
" ".join(
[
"(",
# HOME path is required for GO modules.
f"HOME={UBUNTU_HOME}",
"/usr/bin/run-one",
"/usr/bin/sudo",
"--preserve-env",
str(GITHUB_RUNNER_IMAGE_BUILDER_PATH),
"run",
config.cloud_name,
IMAGE_NAME_TMPL.format(
IMAGE_BASE=config.base.value,
ARCH=config.arch.value,
),
"--base-image",
config.base.value,
"--keep-revisions",
str(config.num_revisions),
"--callback-script",
str(config.callback_script.absolute()),
">>",
str(OUTPUT_LOG_PATH),
"2>&1",
"||",
# Run the callback script without Openstack ID argument to let the charm know
# about the error.
str(state.FAILED_CALLBACK_SCRIPT_PATH.absolute()),
")",
"&",
]
),
" ".join(commands),
# run as shell for log redirection, the command is trusted
shell=True, # nosec: B602
user=UBUNTU_USER,
Expand Down
36 changes: 36 additions & 0 deletions src/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
OPENSTACK_USER_DOMAIN_CONFIG_NAME = "openstack-user-domain-name"
OPENSTACK_USER_CONFIG_NAME = "openstack-user-name"
REVISION_HISTORY_LIMIT_CONFIG_NAME = "revision-history-limit"
RUNNER_VERSION_CONFIG_NAME = "runner-version"

SUCCESS_CALLBACK_SCRIPT_PATH = pathlib.Path("/home/ubuntu/on_build_success_callback.sh")
FAILED_CALLBACK_SCRIPT_PATH = pathlib.Path("/home/ubuntu/on_build_failed_callback.sh")
Expand Down Expand Up @@ -179,13 +180,15 @@ class BuilderRunConfig:
cloud_config: The Openstack clouds.yaml passed as charm config.
cloud_name: The Openstack cloud name to connect to from clouds.yaml.
num_revisions: Number of images to keep before deletion.
runner_version: The GitHub runner version to embed in the image. Latest version if empty.
callback_script: Path to callback script.
"""

arch: Arch
base: BaseImage
cloud_config: dict[str, typing.Any]
num_revisions: int
runner_version: str
callback_script: pathlib.Path = SUCCESS_CALLBACK_SCRIPT_PATH

@property
Expand Down Expand Up @@ -228,6 +231,7 @@ def from_charm(cls, charm: ops.CharmBase) -> "BuilderRunConfig":

try:
revision_history_limit = _parse_revision_history_limit(charm)
runner_version = _parse_runner_version(charm=charm)
except ValueError as exc:
raise BuildConfigInvalidError(msg=str(exc)) from exc

Expand All @@ -236,6 +240,7 @@ def from_charm(cls, charm: ops.CharmBase) -> "BuilderRunConfig":
base=base_image,
cloud_config=cloud_config,
num_revisions=revision_history_limit,
runner_version=runner_version,
)


Expand Down Expand Up @@ -281,6 +286,37 @@ def _parse_revision_history_limit(charm: ops.CharmBase) -> int:
return revision_history


def _parse_runner_version(charm: ops.CharmBase) -> str:
"""Parse the runner version configuration value.
Args:
charm: The charm instance.
Raises:
ValueError: If an invalid version number is provided.
Returns:
The semantic version number of the GitHub runner.
"""
version_str = typing.cast(str, charm.config.get(RUNNER_VERSION_CONFIG_NAME, ""))
if not version_str:
return ""

parts = version_str.split(".")
if len(parts) != 3:
raise ValueError("The runner version must be in semantic version format.")
try:
major = int(parts[0])
minor = int(parts[1])
patch = int(parts[2])
except ValueError as exc:
raise ValueError("The runner version numbers must be an integer.") from exc
if major < 0 or minor < 0 or patch < 0:
raise ValueError("The runner version numbers cannot be negative.")

return version_str


class InvalidCloudConfigError(Exception):
"""Represents an error with openstack cloud config."""

Expand Down
2 changes: 2 additions & 0 deletions tests/unit/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
OPENSTACK_USER_CONFIG_NAME,
OPENSTACK_USER_DOMAIN_CONFIG_NAME,
REVISION_HISTORY_LIMIT_CONFIG_NAME,
RUNNER_VERSION_CONFIG_NAME,
)

T = typing.TypeVar("T")
Expand Down Expand Up @@ -60,6 +61,7 @@ class Meta: # pylint: disable=too-few-public-methods
OPENSTACK_USER_DOMAIN_CONFIG_NAME: "user_domain_name",
OPENSTACK_USER_CONFIG_NAME: "username",
REVISION_HISTORY_LIMIT_CONFIG_NAME: "5",
RUNNER_VERSION_CONFIG_NAME: "1.234.5",
}
)

Expand Down
Loading

0 comments on commit 562097b

Please sign in to comment.