Skip to content

Commit

Permalink
new: MySQL module (#200)
Browse files Browse the repository at this point in the history
* WIP

* Add DBaaS logic

* Support linode_api4 changes

* Add docs

* Add updates

* Improve update logic

* Add mapped object normalization to update logic

* Resolve update logic and update docs

* Fix lint
  • Loading branch information
LBGarber authored Oct 5, 2022
1 parent 90f9553 commit 48acdec
Show file tree
Hide file tree
Showing 9 changed files with 780 additions and 3 deletions.
4 changes: 3 additions & 1 deletion .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,6 @@ disable=protected-access,
too-many-lines,
too-many-branches,
anomalous-backslash-in-string,
too-many-locals
too-many-locals

extension-pkg-whitelist=math
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ Modules for managing Linode infrastructure.

Name | Description |
--- | ------------ |
[linode.cloud.database_mysql](./docs/modules/database_mysql.md)|Manage a Linode MySQL database.|
[linode.cloud.domain](./docs/modules/domain.md)|Manage Linode Domains.|
[linode.cloud.domain_record](./docs/modules/domain_record.md)|Manage Linode Domain Records.|
[linode.cloud.firewall](./docs/modules/firewall.md)|Manage Linode Firewalls.|
Expand Down
172 changes: 172 additions & 0 deletions docs/modules/database_mysql.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
# database_mysql

Manage a Linode MySQL database.


- [Examples](#examples)
- [Parameters](#parameters)
- [Return Values](#return-values)

## Examples

```yaml
- name: Create a basic MySQL database
linode.cloud.database_mysql:
label: my-db
region: us-east
engine: mysql/8.0.26
type: g6-standard-1
allow_list:
- 0.0.0.0/0
state: present
```
```yaml
- name: Create a complex 3 node MySQL database
linode.cloud.database_mysql:
label: my-db
region: us-east
engine: mysql/8.0.26
type: g6-standard-1
allow_list:
- 0.0.0.0/0
encrypted: true
cluster_size: 3
replication_type: semi_synch
ssl_connection: true
state: present
```
```yaml
- name: Delete a MySQL database
linode.cloud.database_mysql:
label: my-db
state: absent
```
## Parameters
| Field | Type | Required | Description |
|-----------|------|----------|------------------------------------------------------------------------------|
| `label` | `str` | **Required** | This database's unique label. |
| `state` | `str` | **Required** | The state of this database. (Choices: `present` `absent` ) |
| `allow_list` | `list` | Optional | A list of IP addresses that can access the Managed Database. Each item must be a range in CIDR format. |
| `cluster_size` | `int` | Optional | The number of Linode Instance nodes deployed to the Managed Database. (Choices: `1` `3` Default: `1`) |
| `encrypted` | `bool` | Optional | Whether the Managed Databases is encrypted. |
| `engine` | `str` | Optional | The Managed Database engine in engine/version format. |
| `region` | `str` | Optional | The Region ID for the Managed Database. |
| `replication_type` | `str` | Optional | The replication method used for the Managed Database. Defaults to none for a single cluster and semi_synch for a high availability cluster. Must be none for a single node cluster. Must be asynch or semi_synch for a high availability cluster. (Choices: `none` `asynch` `semi_synch` Default: `none`) |
| `ssl_connection` | `bool` | Optional | Whether to require SSL credentials to establish a connection to the Managed Database. (Default: `True`) |
| `type` | `str` | Optional | The Linode Instance type used by the Managed Database for its nodes. |
| [`updates` (sub-options)](#updates) | `dict` | Optional | Configuration settings for automated patch update maintenance for the Managed Database. |
| `wait` | `bool` | Optional | Wait for the database to have status `available` before returning. (Default: `True`) |
| `wait_timeout` | `int` | Optional | The amount of time, in seconds, to wait for an image to have status `available`. (Default: `3600`) |





### updates

| Field | Type | Required | Description |
|-----------|------|----------|------------------------------------------------------------------------------|
| `day_of_week` | `int` | **Required** | The day to perform maintenance. 1=Monday, 2=Tuesday, etc. (Choices: `1` `2` `3` `4` `5` `6` `7` ) |
| `duration` | `int` | **Required** | The maximum maintenance window time in hours. (Choices: `1` `3` ) |
| `hour_of_day` | `int` | **Required** | The hour to begin maintenance based in UTC time. |
| `frequency` | `str` | Optional | Whether maintenance occurs on a weekly or monthly basis. (Choices: `weekly` `monthly` Default: `weekly`) |
| `week_of_month` | `int` | Optional | The week of the month to perform monthly frequency updates. Defaults to None. Required for monthly frequency updates. Must be null for weekly frequency updates. |






## Return Values

- `database` - The database in JSON serialized form.

- Sample Response:
```json
{
"allow_list": [
"203.0.113.1/32",
"192.0.1.0/24"
],
"cluster_size": 3,
"created": "2022-01-01T00:01:01",
"encrypted": false,
"engine": "mysql",
"hosts": {
"primary": "lin-123-456-mysql-mysql-primary.servers.linodedb.net",
"secondary": "lin-123-456-mysql-primary-private.servers.linodedb.net"
},
"id": 123,
"label": "example-db",
"port": 3306,
"region": "us-east",
"replication_type": "semi_synch",
"ssl_connection": true,
"status": "active",
"type": "g6-dedicated-2",
"updated": "2022-01-01T00:01:01",
"updates": {
"day_of_week": 1,
"duration": 3,
"frequency": "weekly",
"hour_of_day": 0,
"week_of_month": null
},
"version": "8.0.26"
}
```
- See the [Linode API response documentation](https://www.linode.com/docs/api/databases/#managed-mysql-database-view__response-samples) for a list of returned fields


- `backups` - The database backups in JSON serialized form.

- Sample Response:
```json
[
{
"created":"2022-01-01T00:01:01",
"id":123,
"label":"Scheduled - 02/04/22 11:11 UTC-XcCRmI",
"type":"auto"
}
]
```
- See the [Linode API response documentation](https://www.linode.com/docs/api/databases/#managed-mysql-database-backup-view__responses) for a list of returned fields


- `ssl_cert` - The SSL CA certificate for an accessible Managed MySQL Database.

- Sample Response:
```json
{
"ca_certificate": "LS0tLS1CRUdJ...=="
}
```
- See the [Linode API response documentation](https://www.linode.com/docs/api/databases/#managed-mysql-database-ssl-certificate-view__responses) for a list of returned fields


- `credentials` - The root username and password for an accessible Managed MySQL Database.

- Sample Response:
```json
{
"password": "s3cur3P@ssw0rd",
"username": "linroot"
}
```
- See the [Linode API response documentation](https://www.linode.com/docs/api/databases/#managed-mysql-database-credentials-view__responses) for a list of returned fields


79 changes: 79 additions & 0 deletions plugins/module_utils/doc_fragments/database_mysql.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
"""Documentation fragments for the database_mysql module"""

specdoc_examples = ['''
- name: Create a basic MySQL database
linode.cloud.database_mysql:
label: my-db
region: us-east
engine: mysql/8.0.26
type: g6-standard-1
allow_list:
- 0.0.0.0/0
state: present''', '''
- name: Create a complex 3 node MySQL database
linode.cloud.database_mysql:
label: my-db
region: us-east
engine: mysql/8.0.26
type: g6-standard-1
allow_list:
- 0.0.0.0/0
encrypted: true
cluster_size: 3
replication_type: semi_synch
ssl_connection: true
state: present''', '''
- name: Delete a MySQL database
linode.cloud.database_mysql:
label: my-db
state: absent''']

result_database_samples = ['''{
"allow_list": [
"203.0.113.1/32",
"192.0.1.0/24"
],
"cluster_size": 3,
"created": "2022-01-01T00:01:01",
"encrypted": false,
"engine": "mysql",
"hosts": {
"primary": "lin-123-456-mysql-mysql-primary.servers.linodedb.net",
"secondary": "lin-123-456-mysql-primary-private.servers.linodedb.net"
},
"id": 123,
"label": "example-db",
"port": 3306,
"region": "us-east",
"replication_type": "semi_synch",
"ssl_connection": true,
"status": "active",
"type": "g6-dedicated-2",
"updated": "2022-01-01T00:01:01",
"updates": {
"day_of_week": 1,
"duration": 3,
"frequency": "weekly",
"hour_of_day": 0,
"week_of_month": null
},
"version": "8.0.26"
}''']

result_credentials_samples = ['''{
"password": "s3cur3P@ssw0rd",
"username": "linroot"
}''']

result_ssl_cert_samples = ['''{
"ca_certificate": "LS0tLS1CRUdJ...=="
}''']

result_backups_samples = ['''[
{
"created":"2022-01-01T00:01:01",
"id":123,
"label":"Scheduled - 02/04/22 11:11 UTC-XcCRmI",
"type":"auto"
}
]''']
24 changes: 23 additions & 1 deletion plugins/module_utils/linode_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from typing import Tuple, Any, Optional, cast, Dict, Set, Callable, List

import linode_api4
import polling
from linode_api4 import and_, MappedObject, LKENodePool, LKENodePoolNode, ApiError, LinodeClient
from linode_api4.objects.filtering import Filter, FilterableAttribute, FilterableMetaclass

Expand Down Expand Up @@ -79,7 +80,8 @@ def mapping_to_dict(obj: Any) -> Any:
return obj


def handle_updates(obj: linode_api4.Base, params: dict, mutable_fields: set, register_func: Any):
def handle_updates(obj: linode_api4.Base, params: dict,
mutable_fields: set, register_func: Any) -> Set[str]:
"""Handles updates for a linode_api4 object"""

obj._api_get()
Expand All @@ -88,6 +90,7 @@ def handle_updates(obj: linode_api4.Base, params: dict, mutable_fields: set, reg
params = filter_null_values(params)

put_request = {}
result = set()

for key, new_value in params.items():
if not hasattr(obj, key):
Expand All @@ -98,6 +101,7 @@ def handle_updates(obj: linode_api4.Base, params: dict, mutable_fields: set, reg
if new_value != old_value:
if key in mutable_fields:
put_request[key] = new_value
result.add(key)
register_func('Updated {0}: "{1}" -> "{2}"'.
format(key, old_value, new_value))

Expand All @@ -110,6 +114,8 @@ def handle_updates(obj: linode_api4.Base, params: dict, mutable_fields: set, reg
if len(put_request.keys()) > 0:
obj._client.put(type(obj).api_endpoint, model=obj, data=put_request)

return result


def parse_linode_types(value: any) -> any:
"""Helper function for handle_updates.
Expand All @@ -126,6 +132,9 @@ def parse_linode_types(value: any) -> any:
}:
return value.id

if isinstance(value, MappedObject):
return mapping_to_dict(value)

return value


Expand Down Expand Up @@ -269,3 +278,16 @@ def format_api_error(err: ApiError) -> str:
"""Formats an API error into a readable string"""

return ';'.join(err.errors)


def poll_condition(condition_func: Callable[[], bool], step: int, timeout: int) -> None:
"""Polls for the given condition using the given step and timeout values."""
# Initial attempt
if condition_func():
return

polling.poll(
condition_func,
step=step,
timeout=timeout,
)
Loading

0 comments on commit 48acdec

Please sign in to comment.