Skip to content

Commit 032f294

Browse files
ezilber-akamaiykim-akamailgarber-akamai
authored
Proj/configurable db params (#553)
* Added support for DB Configurable Params (#527) * Added support for DB Configurable Params features * Added unit tests for config endpoints * Added more unit tests and removed config_update methods * Removed stale fields and updated unit tests * Add integration tests * Add integration tests * remove unused var * remove comments and update test case * update test case * remove assertion * Added support for custom JSON field names in dataclasses * Minor integration test fixes * add get and list test cases --------- Co-authored-by: Youjung Kim <ykim@akamai.com> * Add / prefix to URL in mysql_config_options and postgresql_config_options API calls (#542) * add test coverage for nullable fields (#541) --------- Co-authored-by: Youjung Kim <ykim@akamai.com> Co-authored-by: Lena Garber <114949949+lgarber-akamai@users.noreply.github.com> Co-authored-by: Youjung Kim <126618609+ykim-akamai@users.noreply.github.com>
1 parent 3a1ec42 commit 032f294

12 files changed

+2927
-33
lines changed

linode_api4/groups/database.py

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
from typing import Any, Dict, Union
2+
3+
from linode_api4 import (
4+
MySQLDatabaseConfigOptions,
5+
PostgreSQLDatabaseConfigOptions,
6+
)
17
from linode_api4.errors import UnexpectedResponseError
28
from linode_api4.groups import Group
39
from linode_api4.objects import (
@@ -63,6 +69,26 @@ def engines(self, *filters):
6369
"""
6470
return self.client._get_and_filter(DatabaseEngine, *filters)
6571

72+
def mysql_config_options(self):
73+
"""
74+
Returns a detailed list of all the configuration options for MySQL Databases.
75+
76+
API Documentation: TODO
77+
78+
:returns: The JSON configuration options for MySQL Databases.
79+
"""
80+
return self.client.get("/databases/mysql/config", model=self)
81+
82+
def postgresql_config_options(self):
83+
"""
84+
Returns a detailed list of all the configuration options for PostgreSQL Databases.
85+
86+
API Documentation: TODO
87+
88+
:returns: The JSON configuration options for PostgreSQL Databases.
89+
"""
90+
return self.client.get("/databases/postgresql/config", model=self)
91+
6692
def instances(self, *filters):
6793
"""
6894
Returns a list of Managed Databases active on this account.
@@ -93,7 +119,15 @@ def mysql_instances(self, *filters):
93119
"""
94120
return self.client._get_and_filter(MySQLDatabase, *filters)
95121

96-
def mysql_create(self, label, region, engine, ltype, **kwargs):
122+
def mysql_create(
123+
self,
124+
label,
125+
region,
126+
engine,
127+
ltype,
128+
engine_config: Union[MySQLDatabaseConfigOptions, Dict[str, Any]] = None,
129+
**kwargs,
130+
):
97131
"""
98132
Creates an :any:`MySQLDatabase` on this account with
99133
the given label, region, engine, and node type. For example::
@@ -123,13 +157,16 @@ def mysql_create(self, label, region, engine, ltype, **kwargs):
123157
:type engine: str or Engine
124158
:param ltype: The Linode Type to use for this cluster
125159
:type ltype: str or Type
160+
:param engine_config: The configuration options for this MySQL cluster
161+
:type engine_config: Dict[str, Any] or MySQLDatabaseConfigOptions
126162
"""
127163

128164
params = {
129165
"label": label,
130166
"region": region,
131167
"engine": engine,
132168
"type": ltype,
169+
"engine_config": engine_config,
133170
}
134171
params.update(kwargs)
135172

@@ -216,7 +253,17 @@ def postgresql_instances(self, *filters):
216253
"""
217254
return self.client._get_and_filter(PostgreSQLDatabase, *filters)
218255

219-
def postgresql_create(self, label, region, engine, ltype, **kwargs):
256+
def postgresql_create(
257+
self,
258+
label,
259+
region,
260+
engine,
261+
ltype,
262+
engine_config: Union[
263+
PostgreSQLDatabaseConfigOptions, Dict[str, Any]
264+
] = None,
265+
**kwargs,
266+
):
220267
"""
221268
Creates an :any:`PostgreSQLDatabase` on this account with
222269
the given label, region, engine, and node type. For example::
@@ -246,13 +293,16 @@ def postgresql_create(self, label, region, engine, ltype, **kwargs):
246293
:type engine: str or Engine
247294
:param ltype: The Linode Type to use for this cluster
248295
:type ltype: str or Type
296+
:param engine_config: The configuration options for this PostgreSQL cluster
297+
:type engine_config: Dict[str, Any] or PostgreSQLDatabaseConfigOptions
249298
"""
250299

251300
params = {
252301
"label": label,
253302
"region": region,
254303
"engine": engine,
255304
"type": ltype,
305+
"engine_config": engine_config,
256306
}
257307
params.update(kwargs)
258308

linode_api4/objects/database.py

Lines changed: 150 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
1+
from dataclasses import dataclass, field
2+
from typing import Optional
3+
14
from deprecated import deprecated
25

3-
from linode_api4.objects import Base, DerivedBase, MappedObject, Property
6+
from linode_api4.objects import (
7+
Base,
8+
DerivedBase,
9+
JSONObject,
10+
MappedObject,
11+
Property,
12+
)
413

514

615
class DatabaseType(Base):
@@ -128,6 +137,140 @@ class PostgreSQLDatabaseBackup(DatabaseBackup):
128137
api_endpoint = "/databases/postgresql/instances/{database_id}/backups/{id}"
129138

130139

140+
@dataclass
141+
class MySQLDatabaseConfigMySQLOptions(JSONObject):
142+
"""
143+
MySQLDatabaseConfigMySQLOptions represents the fields in the mysql
144+
field of the MySQLDatabaseConfigOptions class
145+
"""
146+
147+
connect_timeout: Optional[int] = None
148+
default_time_zone: Optional[str] = None
149+
group_concat_max_len: Optional[float] = None
150+
information_schema_stats_expiry: Optional[int] = None
151+
innodb_change_buffer_max_size: Optional[int] = None
152+
innodb_flush_neighbors: Optional[int] = None
153+
innodb_ft_min_token_size: Optional[int] = None
154+
innodb_ft_server_stopword_table: Optional[str] = None
155+
innodb_lock_wait_timeout: Optional[int] = None
156+
innodb_log_buffer_size: Optional[int] = None
157+
innodb_online_alter_log_max_size: Optional[int] = None
158+
innodb_read_io_threads: Optional[int] = None
159+
innodb_rollback_on_timeout: Optional[bool] = None
160+
innodb_thread_concurrency: Optional[int] = None
161+
innodb_write_io_threads: Optional[int] = None
162+
interactive_timeout: Optional[int] = None
163+
internal_tmp_mem_storage_engine: Optional[str] = None
164+
max_allowed_packet: Optional[int] = None
165+
max_heap_table_size: Optional[int] = None
166+
net_buffer_length: Optional[int] = None
167+
net_read_timeout: Optional[int] = None
168+
net_write_timeout: Optional[int] = None
169+
sort_buffer_size: Optional[int] = None
170+
sql_mode: Optional[str] = None
171+
sql_require_primary_key: Optional[bool] = None
172+
tmp_table_size: Optional[int] = None
173+
wait_timeout: Optional[int] = None
174+
175+
176+
@dataclass
177+
class MySQLDatabaseConfigOptions(JSONObject):
178+
"""
179+
MySQLDatabaseConfigOptions is used to specify
180+
a MySQL Database Cluster's configuration options during its creation.
181+
"""
182+
183+
mysql: Optional[MySQLDatabaseConfigMySQLOptions] = None
184+
binlog_retention_period: Optional[int] = None
185+
186+
187+
@dataclass
188+
class PostgreSQLDatabaseConfigPGLookoutOptions(JSONObject):
189+
"""
190+
PostgreSQLDatabasePGLookoutConfigOptions represents the fields in the pglookout
191+
field of the PostgreSQLDatabasePGConfigOptions class
192+
"""
193+
194+
max_failover_replication_time_lag: Optional[int] = None
195+
196+
197+
@dataclass
198+
class PostgreSQLDatabaseConfigPGOptions(JSONObject):
199+
"""
200+
PostgreSQLDatabasePGConfigOptions represents the fields in the pg
201+
field of the PostgreSQLDatabasePGConfigOptions class
202+
"""
203+
204+
autovacuum_analyze_scale_factor: Optional[float] = None
205+
autovacuum_analyze_threshold: Optional[int] = None
206+
autovacuum_max_workers: Optional[int] = None
207+
autovacuum_naptime: Optional[int] = None
208+
autovacuum_vacuum_cost_delay: Optional[int] = None
209+
autovacuum_vacuum_cost_limit: Optional[int] = None
210+
autovacuum_vacuum_scale_factor: Optional[float] = None
211+
autovacuum_vacuum_threshold: Optional[int] = None
212+
bgwriter_delay: Optional[int] = None
213+
bgwriter_flush_after: Optional[int] = None
214+
bgwriter_lru_maxpages: Optional[int] = None
215+
bgwriter_lru_multiplier: Optional[float] = None
216+
deadlock_timeout: Optional[int] = None
217+
default_toast_compression: Optional[str] = None
218+
idle_in_transaction_session_timeout: Optional[int] = None
219+
jit: Optional[bool] = None
220+
max_files_per_process: Optional[int] = None
221+
max_locks_per_transaction: Optional[int] = None
222+
max_logical_replication_workers: Optional[int] = None
223+
max_parallel_workers: Optional[int] = None
224+
max_parallel_workers_per_gather: Optional[int] = None
225+
max_pred_locks_per_transaction: Optional[int] = None
226+
max_replication_slots: Optional[int] = None
227+
max_slot_wal_keep_size: Optional[int] = None
228+
max_stack_depth: Optional[int] = None
229+
max_standby_archive_delay: Optional[int] = None
230+
max_standby_streaming_delay: Optional[int] = None
231+
max_wal_senders: Optional[int] = None
232+
max_worker_processes: Optional[int] = None
233+
password_encryption: Optional[str] = None
234+
pg_partman_bgw_interval: Optional[int] = field(
235+
default=None, metadata={"json_key": "pg_partman_bgw.interval"}
236+
)
237+
pg_partman_bgw_role: Optional[str] = field(
238+
default=None, metadata={"json_key": "pg_partman_bgw.role"}
239+
)
240+
pg_stat_monitor_pgsm_enable_query_plan: Optional[bool] = field(
241+
default=None,
242+
metadata={"json_key": "pg_stat_monitor.pgsm_enable_query_plan"},
243+
)
244+
pg_stat_monitor_pgsm_max_buckets: Optional[int] = field(
245+
default=None, metadata={"json_key": "pg_stat_monitor.pgsm_max_buckets"}
246+
)
247+
pg_stat_statements_track: Optional[str] = field(
248+
default=None, metadata={"json_key": "pg_stat_statements.track"}
249+
)
250+
temp_file_limit: Optional[int] = None
251+
timezone: Optional[str] = None
252+
track_activity_query_size: Optional[int] = None
253+
track_commit_timestamp: Optional[str] = None
254+
track_functions: Optional[str] = None
255+
track_io_timing: Optional[str] = None
256+
wal_sender_timeout: Optional[int] = None
257+
wal_writer_delay: Optional[int] = None
258+
259+
260+
@dataclass
261+
class PostgreSQLDatabaseConfigOptions(JSONObject):
262+
"""
263+
PostgreSQLDatabaseConfigOptions is used to specify
264+
a PostgreSQL Database Cluster's configuration options during its creation.
265+
"""
266+
267+
pg: Optional[PostgreSQLDatabaseConfigPGOptions] = None
268+
pg_stat_monitor_enable: Optional[bool] = None
269+
pglookout: Optional[PostgreSQLDatabaseConfigPGLookoutOptions] = None
270+
shared_buffers_percentage: Optional[float] = None
271+
work_mem: Optional[int] = None
272+
273+
131274
class MySQLDatabase(Base):
132275
"""
133276
An accessible Managed MySQL Database.
@@ -158,6 +301,9 @@ class MySQLDatabase(Base):
158301
"updated": Property(volatile=True, is_datetime=True),
159302
"updates": Property(mutable=True),
160303
"version": Property(),
304+
"engine_config": Property(
305+
mutable=True, json_object=MySQLDatabaseConfigOptions
306+
),
161307
}
162308

163309
@property
@@ -321,6 +467,9 @@ class PostgreSQLDatabase(Base):
321467
"updated": Property(volatile=True, is_datetime=True),
322468
"updates": Property(mutable=True),
323469
"version": Property(),
470+
"engine_config": Property(
471+
mutable=True, json_object=PostgreSQLDatabaseConfigOptions
472+
),
324473
}
325474

326475
@property

linode_api4/objects/serializable.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ def _parse_attr(cls, json_value: Any, field_type: type):
148148
@classmethod
149149
def from_json(cls, json: Dict[str, Any]) -> Optional["JSONObject"]:
150150
"""
151-
Creates an instance of this class from a JSON dict.
151+
Creates an instance of this class from a JSON dict, respecting json_key metadata.
152152
"""
153153
if json is None:
154154
return None
@@ -157,8 +157,12 @@ def from_json(cls, json: Dict[str, Any]) -> Optional["JSONObject"]:
157157

158158
type_hints = get_type_hints(cls)
159159

160-
for k in vars(obj):
161-
setattr(obj, k, cls._parse_attr(json.get(k), type_hints.get(k)))
160+
for f in fields(cls):
161+
json_key = f.metadata.get("json_key", f.name)
162+
field_type = type_hints.get(f.name)
163+
value = json.get(json_key)
164+
parsed_value = cls._parse_attr(value, field_type)
165+
setattr(obj, f.name, parsed_value)
162166

163167
return obj
164168

@@ -211,7 +215,11 @@ def should_include(key: str, value: Any) -> bool:
211215

212216
result = {}
213217

214-
for k, v in vars(self).items():
218+
for f in fields(self):
219+
k = f.name
220+
json_key = f.metadata.get("json_key", k)
221+
v = getattr(self, k)
222+
215223
if not should_include(k, v):
216224
continue
217225

@@ -222,7 +230,7 @@ def should_include(key: str, value: Any) -> bool:
222230
else:
223231
v = attempt_serialize(v)
224232

225-
result[k] = v
233+
result[json_key] = v
226234

227235
return result
228236

0 commit comments

Comments
 (0)