Repository: getmoto/moto
Pull request #8147 (2024-09-23 22:41:46)

Bump ruby/setup-ruby from 1.192.0 to 1.193.0
Bumps [ruby/setup-ruby](https://github.com/ruby/setup-ruby) from 1.192.0 to 1.193.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/ruby/setup-ruby/releases">ruby/setup-ruby's releases</a>.</em></p>
<blockquote>
<h2>v1.193.0</h2>
<h2>What's Changed</h2>
<ul>
<li>README.md - Windows - clarify installed packages, 2022 vs 2019 by <a href="https://github.com/MSP-Greg"><code>@​MSP-Greg</code></a> in <a href="https://redirect.github.com/ruby/setup-ruby/pull/642">ruby/setup-ruby#642</a></li>
<li>Add truffleruby-24.1.0,truffleruby+graalvm-24.1.0 by <a href="https://github.com/ruby-builder-bot"><code>@​ruby-builder-bot</code></a> in <a href="https://redirect.github.com/ruby/setup-ruby/pull/643">ruby/setup-ruby#643</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a href="https://github.com/ruby/setup-ruby/compare/v1.192.0...v1.193.0">https://github.com/ruby/setup-ruby/compare/v1.192.0...v1.193.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/ruby/setup-ruby/commit/f321cf5a4d1533575411f8752cf25b86478b0442"><code>f321cf5</code></a> Add truffleruby-24.1.0,truffleruby+graalvm-24.1.0</li>
<li><a href="https://github.com/ruby/setup-ruby/commit/6af8267b44275fc0b4fd2180094e29ad705aa44f"><code>6af8267</code></a> README.md - Windows - clarify installed packages, 2022 vs 2019</li>
<li>See full diff in <a href="https://github.com/ruby/setup-ruby/compare/a6b46b8a08edb18935835849f2a17072d5cc8c73...f321cf5a4d1533575411f8752cf25b86478b0442">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=ruby/setup-ruby&package-manager=github_actions&previous-version=1.192.0&new-version=1.193.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>


Patch:
diff --git a/moto/rds/models.py b/moto/rds/models.py
index a03630690..21c5f083a 100644
--- a/moto/rds/models.py
+++ b/moto/rds/models.py
@@ -2904,26 +2904,36 @@ class OptionGroup(RDSBaseModel):
           <EngineName>{{ option_group.engine_name }}</EngineName>
           <OptionGroupDescription>{{ option_group.description }}</OptionGroupDescription>
           <OptionGroupArn>{{ option_group.arn }}</OptionGroupArn>
-          <Options/>
+          <Options>
+              {% for name, option_settings in option_group.options.items() %}
+              <Option>
+                <OptionName>{{ name }}</OptionName>
+                <OptionSettings>
+                  {% for setting in option_settings %}
+                  <OptionSetting>
+                    <Name>{{ setting.get("Name") }}</Name>
+                    <Value>{{ setting.get("Value") }}</Value>
+                  </OptionSetting>
+                  {% endfor %}
+                </OptionSettings>
+              </Option>
+              {% endfor %}
+          </Options>
         </OptionGroup>"""
         )
         return template.render(option_group=self)
 
-    def remove_options(
-        self,
-        options_to_remove: Any,  # pylint: disable=unused-argument
-    ) -> None:
-        # TODO: Check for option in self.options and remove if exists. Raise
-        # error otherwise
-        return
-
-    def add_options(
-        self,
-        options_to_add: Any,  # pylint: disable=unused-argument
-    ) -> None:
-        # TODO: Validate option and add it to self.options. If invalid raise
-        # error
-        return
+    def remove_options(self, options_to_remove: Any) -> None:
+        for option in options_to_remove:
+            if isinstance(option, str):
+                self.options.pop(option, None)
+
+    def add_options(self, options_to_add: Any) -> None:
+        for option in options_to_add:
+            if isinstance(option, str):
+                self.options[option] = {}
+            elif isinstance(option, dict):
+                self.options[option["OptionName"]] = option["OptionSettings"]
 
 
 class DBParameterGroup(CloudFormationModel, RDSBaseModel):
diff --git a/moto/rds/responses.py b/moto/rds/responses.py
index 53a7bcbf9..8be586ff0 100644
--- a/moto/rds/responses.py
+++ b/moto/rds/responses.py
@@ -489,34 +489,11 @@ class RDSResponse(BaseResponse):
 
     def modify_option_group(self) -> str:
         option_group_name = self._get_param("OptionGroupName")
-        count = 1
-        options_to_include = []
-        # TODO: This can probably be refactored with a single call to super.get_multi_param, but there are not enough tests (yet) to verify this
-        while self._get_param(f"OptionsToInclude.member.{count}.OptionName"):
-            options_to_include.append(
-                {
-                    "Port": self._get_param(f"OptionsToInclude.member.{count}.Port"),
-                    "OptionName": self._get_param(
-                        f"OptionsToInclude.member.{count}.OptionName"
-                    ),
-                    "DBSecurityGroupMemberships": self._get_param(
-                        f"OptionsToInclude.member.{count}.DBSecurityGroupMemberships"
-                    ),
-                    "OptionSettings": self._get_param(
-                        f"OptionsToInclude.member.{count}.OptionSettings"
-                    ),
-                    "VpcSecurityGroupMemberships": self._get_param(
-                        f"OptionsToInclude.member.{count}.VpcSecurityGroupMemberships"
-                    ),
-                }
-            )
-            count += 1
+        options_to_include = (self._get_multi_param_dict("OptionsToInclude") or {}).get(
+            "OptionConfiguration", []
+        )
+        options_to_remove = self._get_params().get("OptionsToRemove", [])
 
-        count = 1
-        options_to_remove = []
-        while self._get_param(f"OptionsToRemove.member.{count}"):
-            options_to_remove.append(self._get_param(f"OptionsToRemove.member.{count}"))
-            count += 1
         option_group = self.backend.modify_option_group(
             option_group_name, options_to_include, options_to_remove
         )


Test patch:
diff --git a/.github/workflows/tests_sdk_ruby.yml b/.github/workflows/tests_sdk_ruby.yml
index 189d87f0d..f953187bb 100644
--- a/.github/workflows/tests_sdk_ruby.yml
+++ b/.github/workflows/tests_sdk_ruby.yml
@@ -13,7 +13,7 @@ jobs:
     steps:
       - uses: actions/checkout@v4
       - name: Set up Ruby ${{ matrix.ruby-version }}
-        uses: ruby/setup-ruby@a6b46b8a08edb18935835849f2a17072d5cc8c73
+        uses: ruby/setup-ruby@f321cf5a4d1533575411f8752cf25b86478b0442
         with:
           ruby-version: ${{ matrix.ruby-version }}
       - name: Set up Python 3.8
diff --git a/tests/test_rds/test_rds.py b/tests/test_rds/test_rds.py
index 6ed50fb0e..68913255f 100644
--- a/tests/test_rds/test_rds.py
+++ b/tests/test_rds/test_rds.py
@@ -1,4 +1,5 @@
 import datetime
+from uuid import uuid4
 
 import boto3
 import pytest
@@ -6,6 +7,7 @@ from botocore.exceptions import ClientError
 
 from moto import mock_aws
 from moto.core import DEFAULT_ACCOUNT_ID as ACCOUNT_ID
+from tests import aws_verified
 
 DEFAULT_REGION = "us-west-2"
 
@@ -1468,27 +1470,96 @@ def test_describe_option_group_options():
         )
 
 
-@mock_aws
+@pytest.mark.aws_verified
+@aws_verified
 def test_modify_option_group():
-    conn = boto3.client("rds", region_name=DEFAULT_REGION)
+    conn = boto3.client("rds", region_name="us-east-1")
+    option_group_name = f"og-{str(uuid4())[0:6]}"
     conn.create_option_group(
-        OptionGroupName="test",
+        OptionGroupName=option_group_name,
         EngineName="mysql",
         MajorEngineVersion="5.6",
         OptionGroupDescription="test option group",
     )
-    # TODO: create option and validate before deleting.
-    # if Someone can tell me how the hell to use this function
-    # to add options to an option_group, I can finish coding this.
-    result = conn.modify_option_group(
-        OptionGroupName="test",
-        OptionsToInclude=[],
-        OptionsToRemove=["MEMCACHED"],
-        ApplyImmediately=True,
-    )
-    assert result["OptionGroup"]["EngineName"] == "mysql"
-    assert result["OptionGroup"]["Options"] == []
-    assert result["OptionGroup"]["OptionGroupName"] == "test"
+
+    try:
+        # Verify OptionsToRemove do not have to exist
+        option_group = conn.modify_option_group(
+            OptionGroupName=option_group_name,
+            OptionsToInclude=[],
+            OptionsToRemove=["MEMCACHED"],
+            ApplyImmediately=True,
+        )["OptionGroup"]
+        assert option_group["EngineName"] == "mysql"
+        assert option_group["Options"] == []
+        assert option_group["OptionGroupName"] == option_group_name
+
+        option_groups = conn.describe_option_groups(OptionGroupName=option_group_name)[
+            "OptionGroupsList"
+        ]
+        assert option_groups[0]["Options"] == []
+
+        # Include option
+        # https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Appendix.MySQL.Options.AuditPlugin.html
+        conn.modify_option_group(
+            OptionGroupName=option_group_name,
+            OptionsToInclude=[{"OptionName": "MARIADB_AUDIT_PLUGIN"}],
+            OptionsToRemove=[],
+            ApplyImmediately=True,
+        )["OptionGroup"]
+
+        # Verify it was added successfully
+        option_groups = conn.describe_option_groups(OptionGroupName=option_group_name)[
+            "OptionGroupsList"
+        ]
+
+        options = option_groups[0]["Options"]
+        assert len(options) == 1
+        assert options[0]["OptionName"] == "MARIADB_AUDIT_PLUGIN"
+        # AWS automatically adds a description + default option settings, but Moto does not support that yet
+
+        # Change setting for an existing option
+        conn.modify_option_group(
+            OptionGroupName=option_group_name,
+            OptionsToInclude=[
+                {
+                    "OptionName": "MARIADB_AUDIT_PLUGIN",
+                    "OptionSettings": [
+                        {"Name": "SERVER_AUDIT_FILE_ROTATE_SIZE", "Value": "1000"},
+                    ],
+                }
+            ],
+            ApplyImmediately=True,
+        )["OptionGroup"]
+
+        # Verify it was added successfully
+        option_groups = conn.describe_option_groups(OptionGroupName=option_group_name)[
+            "OptionGroupsList"
+        ]
+
+        options = option_groups[0]["Options"]
+        assert len(options) == 1
+        assert options[0]["OptionName"] == "MARIADB_AUDIT_PLUGIN"
+
+        option_settings = options[0]["OptionSettings"]
+        audit_plugin = [
+            o for o in option_settings if o["Name"] == "SERVER_AUDIT_FILE_ROTATE_SIZE"
+        ][0]
+        audit_plugin["Name"] == "SERVER_AUDIT_FILE_ROTATE_SIZE"
+        audit_plugin["Value"] == "1000"
+
+        # Verify option can be deleted
+        conn.modify_option_group(
+            OptionGroupName=option_group_name,
+            OptionsToRemove=["MARIADB_AUDIT_PLUGIN"],
+            ApplyImmediately=True,
+        )
+        option_groups = conn.describe_option_groups(OptionGroupName=option_group_name)[
+            "OptionGroupsList"
+        ]
+        assert option_groups[0]["Options"] == []
+    finally:
+        conn.delete_option_group(OptionGroupName=option_group_name)
 
 
 @mock_aws