Skip to content

Commit

Permalink
Refactor of openstack runner manager (#345)
Browse files Browse the repository at this point in the history
* Add refactored RunnerManager which uses the CloudRunnerManager and GitHubRunnerManager.

* Add CloudRunnerManager interface for managing the cloud side of runner instance.

* Add OpenStackRunnerManager as a openstack implementation of the CloudRunnerManager.

* Add GitHubRunnerManager for managing the GitHub side of runner instance.
  • Loading branch information
yhaliaw authored Aug 22, 2024
1 parent e17b1a1 commit 74441c3
Show file tree
Hide file tree
Showing 29 changed files with 3,210 additions and 152 deletions.
1 change: 1 addition & 0 deletions .github/workflows/e2e_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ name: End-to-End tests

on:
pull_request:


jobs:
# test option values defined at test/conftest.py are passed on via repository secret
Expand Down
13 changes: 13 additions & 0 deletions .github/workflows/integration_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,22 @@ jobs:
# test_debug_ssh ensures tmate SSH actions works.
# TODO: Add OpenStack integration versions of these tests.
modules: '["test_charm_scheduled_events", "test_debug_ssh"]'
openstack-interface-tests-private-endpoint:
name: openstack interface test using private-endpoint
uses: canonical/operator-workflows/.github/workflows/integration_test.yaml@main
secrets: inherit
with:
juju-channel: 3.2/stable
pre-run-script: scripts/setup-lxd.sh
provider: lxd
test-tox-env: integration-juju3.2
modules: '["test_runner_manager_openstack"]'
self-hosted-runner: true
self-hosted-runner-label: stg-private-endpoint
openstack-integration-tests-private-endpoint:
name: Integration test using private-endpoint
uses: canonical/operator-workflows/.github/workflows/integration_test.yaml@main
needs: openstack-interface-tests-private-endpoint
secrets: inherit
with:
juju-channel: 3.2/stable
Expand Down
7 changes: 5 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ skips = ["*/*test.py", "*/test_*.py", "*tests/*.py"]
[tool.coverage.run]
branch = true
omit = [
# These are covered by `tests/integration/test_runner_manager_openstack.py`.
"src/openstack_cloud/openstack_cloud.py",
"src/openstack_cloud/openstack_runner_manager.py",
# Contains interface for calling LXD. Tested in integration tests and end to end tests.
"src/lxd.py",
# Contains interface for calling repo policy compliance service. Tested in integration test
Expand Down Expand Up @@ -54,9 +57,9 @@ max-doc-length = 99
max-complexity = 10
exclude = [".git", "__pycache__", ".tox", "build", "dist", "*.egg_info", "venv"]
select = ["E", "W", "F", "C", "N", "R", "D", "H"]
# Ignore W503, E501 because using black creates errors with this
# Ignore W503, E501, E203 because using black creates errors with this
# Ignore D107 Missing docstring in __init__
ignore = ["W503", "E501", "D107"]
ignore = ["W503", "E501", "D107", "E203"]
# D100, D101, D102, D103, D104: Ignore docstring style issues in tests
# temporary disable E402 for the fix in charm.py for lp:2058335
per-file-ignores = ["src/charm.py:E402", "tests/*:D100,D101,D102,D103,D104,D205,D212"]
Expand Down
22 changes: 22 additions & 0 deletions src-docs/errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -403,3 +403,25 @@ Represents an unauthorized connection to OpenStack.



---

<a href="../src/errors.py#L171"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>

## <kbd>class</kbd> `SSHError`
Represents an error while interacting with SSH.





---

<a href="../src/errors.py#L175"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>

## <kbd>class</kbd> `KeyfileError`
Represents missing keyfile for SSH.





32 changes: 32 additions & 0 deletions src-docs/managed_requests.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<!-- markdownlint-disable -->

<a href="../src/apis/managed_requests.py#L0"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>

# <kbd>module</kbd> `managed_requests`
Get configured requests session instance


---

<a href="../src/apis/managed_requests.py#L10"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>

## <kbd>function</kbd> `get_requests_session`

```python
get_requests_session(proxy: ProxyConfig) → Session
```

Get managed requests session instance.



**Args:**

- <b>`proxy`</b>: HTTP proxy configurations.



**Returns:**
Requests session with proxy and retry setup.


8 changes: 5 additions & 3 deletions src-docs/metrics.runner.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ Classes and function to extract the metrics from storage and issue runner metric
```python
extract(
metrics_storage_manager: StorageManager,
ignore_runners: set[str]
runners: set[str],
include: bool = False
) → Iterator[RunnerMetrics]
```

Expand All @@ -38,7 +39,8 @@ In order to avoid DoS attacks, the file size is also checked.
**Args:**

- <b>`metrics_storage_manager`</b>: The metrics storage manager.
- <b>`ignore_runners`</b>: The set of runners to ignore.
- <b>`runners`</b>: The runners to include or exclude.
- <b>`include`</b>: If true the provided runners are included for metric extraction, else the provided runners are excluded.



Expand All @@ -48,7 +50,7 @@ In order to avoid DoS attacks, the file size is also checked.

---

<a href="../src/metrics/runner.py#L139"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>
<a href="../src/metrics/runner.py#L143"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>

## <kbd>function</kbd> `issue_events`

Expand Down
246 changes: 246 additions & 0 deletions src-docs/openstack_cloud.openstack_cloud.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
<!-- markdownlint-disable -->

<a href="../src/openstack_cloud/openstack_cloud.py#L0"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>

# <kbd>module</kbd> `openstack_cloud.openstack_cloud`
Class for accessing OpenStack API for managing servers.



---

<a href="../src/openstack_cloud/openstack_cloud.py#L41"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>

## <kbd>class</kbd> `OpenstackInstance`
Represents an OpenStack instance.



**Attributes:**

- <b>`server_id`</b>: ID of server assigned by OpenStack.
- <b>`server_name`</b>: Name of the server on OpenStack.
- <b>`instance_id`</b>: ID used by OpenstackCloud class to manage the instances. See docs on the OpenstackCloud.
- <b>`addresses`</b>: IP addresses assigned to the server.
- <b>`status`</b>: Status of the server.

<a href="../src/openstack_cloud/openstack_cloud.py#L60"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>

### <kbd>method</kbd> `__init__`

```python
__init__(server: Server, prefix: str)
```

Construct the object.



**Args:**

- <b>`server`</b>: The OpenStack server.
- <b>`prefix`</b>: The name prefix for the servers.



**Raises:**

- <b>`ValueError`</b>: Provided server should not be managed under this prefix.





---

<a href="../src/openstack_cloud/openstack_cloud.py#L126"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>

## <kbd>class</kbd> `OpenstackCloud`
Client to interact with OpenStack cloud.

The OpenStack server name is managed by this cloud. Caller refers to the instances via instance_id. If the caller needs the server name, e.g., for logging, it can be queried with get_server_name.

<a href="../src/openstack_cloud/openstack_cloud.py#L134"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>

### <kbd>method</kbd> `__init__`

```python
__init__(clouds_config: dict[str, dict], cloud: str, prefix: str)
```

Create the object.



**Args:**

- <b>`clouds_config`</b>: The openstack clouds.yaml in dict format.
- <b>`cloud`</b>: The name of cloud to use in the clouds.yaml.
- <b>`prefix`</b>: Prefix attached to names of resource managed by this instance. Used for identifying which resource belongs to this instance.




---

<a href="../src/openstack_cloud/openstack_cloud.py#L335"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>

### <kbd>method</kbd> `cleanup`

```python
cleanup() → None
```

Cleanup unused key files and openstack keypairs.

---

<a href="../src/openstack_cloud/openstack_cloud.py#L224"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>

### <kbd>method</kbd> `delete_instance`

```python
delete_instance(instance_id: str) → None
```

Delete a openstack instance.



**Args:**

- <b>`instance_id`</b>: The instance ID of the instance to delete.

---

<a href="../src/openstack_cloud/openstack_cloud.py#L204"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>

### <kbd>method</kbd> `get_instance`

```python
get_instance(instance_id: str) → OpenstackInstance | None
```

Get OpenStack instance by instance ID.



**Args:**

- <b>`instance_id`</b>: The instance ID.



**Returns:**
The OpenStack instance if found.

---

<a href="../src/openstack_cloud/openstack_cloud.py#L312"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>

### <kbd>method</kbd> `get_instances`

```python
get_instances() → tuple[OpenstackInstance, ]
```

Get all OpenStack instances.



**Returns:**
The OpenStack instances.

---

<a href="../src/openstack_cloud/openstack_cloud.py#L345"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>

### <kbd>method</kbd> `get_server_name`

```python
get_server_name(instance_id: str) → str
```

Get server name on OpenStack.



**Args:**

- <b>`instance_id`</b>: ID used to identify a instance.



**Returns:**
The OpenStack server name.

---

<a href="../src/openstack_cloud/openstack_cloud.py#L259"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>

### <kbd>method</kbd> `get_ssh_connection`

```python
get_ssh_connection(instance: OpenstackInstance) → Connection
```

Get SSH connection to an OpenStack instance.



**Args:**

- <b>`instance`</b>: The OpenStack instance to connect to.



**Raises:**

- <b>`SSHError`</b>: Unable to get a working SSH connection to the instance.
- <b>`KeyfileError`</b>: Unable to find the keyfile to connect to the instance.



**Returns:**
SSH connection object.

---

<a href="../src/openstack_cloud/openstack_cloud.py#L149"><img align="right" style="float:right;" src="https://img.shields.io/badge/-source-cccccc?style=flat-square"></a>

### <kbd>method</kbd> `launch_instance`

```python
launch_instance(
instance_id: str,
image: str,
flavor: str,
network: str,
cloud_init: str
) → OpenstackInstance
```

Create an OpenStack instance.



**Args:**

- <b>`instance_id`</b>: The instance ID to form the instance name.
- <b>`image`</b>: The image used to create the instance.
- <b>`flavor`</b>: The flavor used to create the instance.
- <b>`network`</b>: The network used to create the instance.
- <b>`cloud_init`</b>: The cloud init userdata to startup the instance.



**Raises:**

- <b>`OpenStackError`</b>: Unable to create OpenStack server.



**Returns:**
The OpenStack instance created.


Loading

0 comments on commit 74441c3

Please sign in to comment.