From e93ed2f74182d45d86f6fc228a6ceea03775b5a4 Mon Sep 17 00:00:00 2001 From: Ye Chen Date: Wed, 5 Mar 2025 16:07:41 -0500 Subject: [PATCH 1/2] suspend and resume db --- linode_api4/objects/database.py | 48 +++++++++++++ .../databases_mysql_instances_123_resume.json | 1 + ...databases_mysql_instances_123_suspend.json | 1 + ...bases_postgresql_instances_123_resume.json | 1 + ...ases_postgresql_instances_123_suspend.json | 1 + .../models/database/test_database.py | 68 +++++++++++++++++++ test/unit/objects/database_test.py | 56 +++++++++++++++ 7 files changed, 176 insertions(+) create mode 100644 test/fixtures/databases_mysql_instances_123_resume.json create mode 100644 test/fixtures/databases_mysql_instances_123_suspend.json create mode 100644 test/fixtures/databases_postgresql_instances_123_resume.json create mode 100644 test/fixtures/databases_postgresql_instances_123_suspend.json diff --git a/linode_api4/objects/database.py b/linode_api4/objects/database.py index 58044edb0..8bd530432 100644 --- a/linode_api4/objects/database.py +++ b/linode_api4/objects/database.py @@ -265,6 +265,30 @@ def invalidate(self): Base.invalidate(self) + def suspend(self): + """ + Suspend a MySQL Managed Database, releasing idle resources and keeping only necessary data. + + API documentation: https://techdocs.akamai.com/linode-api/reference/suspend-databases-mysql-instance + """ + self.invalidate() + + return self._client.post( + "{}/suspend".format(MySQLDatabase.api_endpoint), model=self + ) + + def resume(self): + """ + Resume a suspended MySQL Managed Database. + + API documentation: https://techdocs.akamai.com/linode-api/reference/resume-databases-mysql-instance + """ + self.invalidate() + + return self._client.post( + "{}/resume".format(MySQLDatabase.api_endpoint), model=self + ) + class PostgreSQLDatabase(Base): """ @@ -405,6 +429,30 @@ def invalidate(self): Base.invalidate(self) + def suspend(self): + """ + Suspend a PostgreSQL Managed Database, releasing idle resources and keeping only necessary data. + + API documentation: https://techdocs.akamai.com/linode-api/reference/suspend-databases-postgre-sql-instance + """ + self.invalidate() + + return self._client.post( + "{}/suspend".format(PostgreSQLDatabase.api_endpoint), model=self + ) + + def resume(self): + """ + Resume a suspended PostgreSQL Managed Database. + + API documentation: https://techdocs.akamai.com/linode-api/reference/resume-databases-postgre-sql-instance + """ + self.invalidate() + + return self._client.post( + "{}/resume".format(PostgreSQLDatabase.api_endpoint), model=self + ) + ENGINE_TYPE_TRANSLATION = { "mysql": MySQLDatabase, diff --git a/test/fixtures/databases_mysql_instances_123_resume.json b/test/fixtures/databases_mysql_instances_123_resume.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/test/fixtures/databases_mysql_instances_123_resume.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/test/fixtures/databases_mysql_instances_123_suspend.json b/test/fixtures/databases_mysql_instances_123_suspend.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/test/fixtures/databases_mysql_instances_123_suspend.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/test/fixtures/databases_postgresql_instances_123_resume.json b/test/fixtures/databases_postgresql_instances_123_resume.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/test/fixtures/databases_postgresql_instances_123_resume.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/test/fixtures/databases_postgresql_instances_123_suspend.json b/test/fixtures/databases_postgresql_instances_123_suspend.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/test/fixtures/databases_postgresql_instances_123_suspend.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/test/integration/models/database/test_database.py b/test/integration/models/database/test_database.py index 1ad5dde3b..351c09c2a 100644 --- a/test/integration/models/database/test_database.py +++ b/test/integration/models/database/test_database.py @@ -165,6 +165,40 @@ def test_database_instance(test_linode_client, test_create_sql_db): assert str(test_create_sql_db.id) in str(dbs.lists) +@pytest.mark.skipif( + os.getenv("RUN_DB_TESTS", "").strip().lower() not in {"yes", "true"}, + reason="RUN_DB_TESTS environment variable must be set to 'yes' or 'true' (case insensitive)", +) +def test_mysql_suspend_resume(test_linode_client, test_create_sql_db): + db = test_linode_client.load(MySQLDatabase, test_create_sql_db.id) + + db.suspend() + + wait_for_condition( + 10, + 300, + get_sql_db_status, + test_linode_client, + test_create_sql_db.id, + "suspended", + ) + + assert db.status == "suspended" + + db.resume() + + wait_for_condition( + 30, + 600, + get_sql_db_status, + test_linode_client, + test_create_sql_db.id, + "active", + ) + + assert db.status == "active" + + # ------- POSTGRESQL DB Test cases ------- @pytest.mark.skipif( os.getenv("RUN_DB_TESTS", "").strip().lower() not in {"yes", "true"}, @@ -411,3 +445,37 @@ def test_reset_postgres_credentials( assert db.credentials.username == "akmadmin" assert db.credentials.password != old_pass + + +@pytest.mark.skipif( + os.getenv("RUN_DB_TESTS", "").strip().lower() not in {"yes", "true"}, + reason="RUN_DB_TESTS environment variable must be set to 'yes' or 'true' (case insensitive)", +) +def test_postgres_suspend_resume(test_linode_client, test_create_postgres_db): + db = test_linode_client.load(PostgreSQLDatabase, test_create_postgres_db.id) + + db.suspend() + + wait_for_condition( + 10, + 300, + get_postgres_db_status, + test_linode_client, + test_create_postgres_db.id, + "suspended", + ) + + assert db.status == "suspended" + + db.resume() + + wait_for_condition( + 30, + 600, + get_postgres_db_status, + test_linode_client, + test_create_postgres_db.id, + "active", + ) + + assert db.status == "active" diff --git a/test/unit/objects/database_test.py b/test/unit/objects/database_test.py index 11b2379aa..51c7de4cd 100644 --- a/test/unit/objects/database_test.py +++ b/test/unit/objects/database_test.py @@ -263,6 +263,34 @@ def test_reset_credentials(self): m.call_url, "/databases/mysql/instances/123/credentials/reset" ) + def test_suspend(self): + """ + Test MySQL Database suspend logic. + """ + with self.mock_post("/databases/mysql/instances/123/suspend") as m: + db = MySQLDatabase(self.client, 123) + + db.suspend() + + self.assertEqual(m.method, "post") + self.assertEqual( + m.call_url, "/databases/mysql/instances/123/suspend" + ) + + def test_resume(self): + """ + Test MySQL Database resume logic. + """ + with self.mock_post("/databases/mysql/instances/123/resume") as m: + db = MySQLDatabase(self.client, 123) + + db.resume() + + self.assertEqual(m.method, "post") + self.assertEqual( + m.call_url, "/databases/mysql/instances/123/resume" + ) + class PostgreSQLDatabaseTest(ClientBaseCase): """ @@ -451,3 +479,31 @@ def test_reset_credentials(self): m.call_url, "/databases/postgresql/instances/123/credentials/reset", ) + + def test_suspend(self): + """ + Test PostgreSQL Database suspend logic. + """ + with self.mock_post("/databases/postgresql/instances/123/suspend") as m: + db = PostgreSQLDatabase(self.client, 123) + + db.suspend() + + self.assertEqual(m.method, "post") + self.assertEqual( + m.call_url, "/databases/postgresql/instances/123/suspend" + ) + + def test_resume(self): + """ + Test PostgreSQL Database resume logic. + """ + with self.mock_post("/databases/postgresql/instances/123/resume") as m: + db = PostgreSQLDatabase(self.client, 123) + + db.resume() + + self.assertEqual(m.method, "post") + self.assertEqual( + m.call_url, "/databases/postgresql/instances/123/resume" + ) From 176b4c904fa3c6d7615589d03900dd9e9227404d Mon Sep 17 00:00:00 2001 From: Ye Chen Date: Wed, 19 Mar 2025 12:44:57 -0400 Subject: [PATCH 2/2] address comment --- linode_api4/objects/database.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/linode_api4/objects/database.py b/linode_api4/objects/database.py index 8bd530432..dc9db8471 100644 --- a/linode_api4/objects/database.py +++ b/linode_api4/objects/database.py @@ -271,24 +271,24 @@ def suspend(self): API documentation: https://techdocs.akamai.com/linode-api/reference/suspend-databases-mysql-instance """ - self.invalidate() - - return self._client.post( + self._client.post( "{}/suspend".format(MySQLDatabase.api_endpoint), model=self ) + return self.invalidate() + def resume(self): """ Resume a suspended MySQL Managed Database. API documentation: https://techdocs.akamai.com/linode-api/reference/resume-databases-mysql-instance """ - self.invalidate() - - return self._client.post( + self._client.post( "{}/resume".format(MySQLDatabase.api_endpoint), model=self ) + return self.invalidate() + class PostgreSQLDatabase(Base): """ @@ -435,24 +435,24 @@ def suspend(self): API documentation: https://techdocs.akamai.com/linode-api/reference/suspend-databases-postgre-sql-instance """ - self.invalidate() - - return self._client.post( + self._client.post( "{}/suspend".format(PostgreSQLDatabase.api_endpoint), model=self ) + return self.invalidate() + def resume(self): """ Resume a suspended PostgreSQL Managed Database. API documentation: https://techdocs.akamai.com/linode-api/reference/resume-databases-postgre-sql-instance """ - self.invalidate() - - return self._client.post( + self._client.post( "{}/resume".format(PostgreSQLDatabase.api_endpoint), model=self ) + return self.invalidate() + ENGINE_TYPE_TRANSLATION = { "mysql": MySQLDatabase,