diff --git a/linode_api4/objects/database.py b/linode_api4/objects/database.py index 58044edb0..dc9db8471 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._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._client.post( + "{}/resume".format(MySQLDatabase.api_endpoint), model=self + ) + + return self.invalidate() + 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._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._client.post( + "{}/resume".format(PostgreSQLDatabase.api_endpoint), model=self + ) + + return self.invalidate() + 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" + )