Repository: getmoto/moto

Issue:
Implement AutoScaling Resume Processes
As IMPLEMENTATION_COVERAGE.md indicates, AutoScaling **resume_processes** is not currently supported. I would like to help add it, and am creating this issue to associate a PR.


Discussion:

Resolving patch:
diff --git a/moto/autoscaling/models.py b/moto/autoscaling/models.py
--- a/moto/autoscaling/models.py
+++ b/moto/autoscaling/models.py
@@ -1040,8 +1040,31 @@ def detach_load_balancer_target_groups(self, group_name, target_group_arns):
             self.elbv2_backend.deregister_targets(target_group, (asg_targets))
 
     def suspend_processes(self, group_name, scaling_processes):
+        all_proc_names = [
+            "Launch",
+            "Terminate",
+            "AddToLoadBalancer",
+            "AlarmNotification",
+            "AZRebalance",
+            "HealthCheck",
+            "InstanceRefresh",
+            "ReplaceUnhealthy",
+            "ScheduledActions",
+        ]
+        group = self.autoscaling_groups[group_name]
+        set_to_add = set(scaling_processes or all_proc_names)
+        group.suspended_processes = list(
+            set(group.suspended_processes).union(set_to_add)
+        )
+
+    def resume_processes(self, group_name, scaling_processes):
         group = self.autoscaling_groups[group_name]
-        group.suspended_processes = scaling_processes or []
+        if scaling_processes:
+            group.suspended_processes = list(
+                set(group.suspended_processes).difference(set(scaling_processes))
+            )
+        else:
+            group.suspended_processes = []
 
     def set_instance_protection(
         self, group_name, instance_ids, protected_from_scale_in
diff --git a/moto/autoscaling/responses.py b/moto/autoscaling/responses.py
--- a/moto/autoscaling/responses.py
+++ b/moto/autoscaling/responses.py
@@ -359,6 +359,15 @@ def suspend_processes(self):
         template = self.response_template(SUSPEND_PROCESSES_TEMPLATE)
         return template.render()
 
+    def resume_processes(self):
+        autoscaling_group_name = self._get_param("AutoScalingGroupName")
+        scaling_processes = self._get_multi_param("ScalingProcesses.member")
+        self.autoscaling_backend.resume_processes(
+            autoscaling_group_name, scaling_processes
+        )
+        template = self.response_template(RESUME_PROCESSES_TEMPLATE)
+        return template.render()
+
     def set_instance_protection(self):
         group_name = self._get_param("AutoScalingGroupName")
         instance_ids = self._get_multi_param("InstanceIds.member")
@@ -802,6 +811,12 @@ def terminate_instance_in_auto_scaling_group(self):
 </ResponseMetadata>
 </SuspendProcessesResponse>"""
 
+RESUME_PROCESSES_TEMPLATE = """<ResumeProcessesResponse xmlns="http://autoscaling.amazonaws.com/doc/2011-01-01/">
+<ResponseMetadata>
+   <RequestId></RequestId>
+</ResponseMetadata>
+</ResumeProcessesResponse>"""
+
 SET_INSTANCE_HEALTH_TEMPLATE = """<SetInstanceHealthResponse xmlns="http://autoscaling.amazonaws.com/doc/2011-01-01/">
 <SetInstanceHealthResponse></SetInstanceHealthResponse>
 <ResponseMetadata>


Test patch:
diff --git a/tests/test_autoscaling/test_autoscaling.py b/tests/test_autoscaling/test_autoscaling.py
--- a/tests/test_autoscaling/test_autoscaling.py
+++ b/tests/test_autoscaling/test_autoscaling.py
@@ -2391,6 +2391,146 @@ def test_suspend_processes():
     assert launch_suspended is True
 
 
+@mock_autoscaling
+def test_suspend_processes_all_by_default():
+    mocked_networking = setup_networking()
+    client = boto3.client("autoscaling", region_name="us-east-1")
+    client.create_launch_configuration(
+        LaunchConfigurationName="lc", ImageId=EXAMPLE_AMI_ID, InstanceType="t2.medium"
+    )
+    client.create_auto_scaling_group(
+        LaunchConfigurationName="lc",
+        AutoScalingGroupName="test-asg",
+        MinSize=1,
+        MaxSize=1,
+        VPCZoneIdentifier=mocked_networking["subnet1"],
+    )
+
+    # When we suspend with no processes specified
+    client.suspend_processes(AutoScalingGroupName="test-asg")
+
+    res = client.describe_auto_scaling_groups(AutoScalingGroupNames=["test-asg"])
+
+    # All processes should be suspended
+    all_proc_names = [
+        "Launch",
+        "Terminate",
+        "AddToLoadBalancer",
+        "AlarmNotification",
+        "AZRebalance",
+        "HealthCheck",
+        "InstanceRefresh",
+        "ReplaceUnhealthy",
+        "ScheduledActions",
+    ]
+    suspended_proc_names = [
+        proc["ProcessName"]
+        for proc in res["AutoScalingGroups"][0]["SuspendedProcesses"]
+    ]
+    set(suspended_proc_names).should.equal(set(all_proc_names))
+
+
+@mock_autoscaling
+def test_suspend_additional_processes():
+    mocked_networking = setup_networking()
+    client = boto3.client("autoscaling", region_name="us-east-1")
+    client.create_launch_configuration(
+        LaunchConfigurationName="lc", ImageId=EXAMPLE_AMI_ID, InstanceType="t2.medium"
+    )
+    client.create_auto_scaling_group(
+        LaunchConfigurationName="lc",
+        AutoScalingGroupName="test-asg",
+        MinSize=1,
+        MaxSize=1,
+        VPCZoneIdentifier=mocked_networking["subnet1"],
+    )
+
+    # When we suspend the 'Launch' and 'Terminate' processes in separate calls
+    client.suspend_processes(
+        AutoScalingGroupName="test-asg", ScalingProcesses=["Launch"]
+    )
+    client.suspend_processes(
+        AutoScalingGroupName="test-asg", ScalingProcesses=["Terminate"]
+    )
+
+    res = client.describe_auto_scaling_groups(AutoScalingGroupNames=["test-asg"])
+
+    # Both 'Launch' and 'Terminate' should be suspended
+    launch_suspended = False
+    terminate_suspended = False
+    for proc in res["AutoScalingGroups"][0]["SuspendedProcesses"]:
+        if proc.get("ProcessName") == "Launch":
+            launch_suspended = True
+        if proc.get("ProcessName") == "Terminate":
+            terminate_suspended = True
+
+    assert launch_suspended is True
+    assert terminate_suspended is True
+
+
+@mock_autoscaling
+def test_resume_processes():
+    mocked_networking = setup_networking()
+    client = boto3.client("autoscaling", region_name="us-east-1")
+    client.create_launch_configuration(
+        LaunchConfigurationName="lc", ImageId=EXAMPLE_AMI_ID, InstanceType="t2.medium"
+    )
+    client.create_auto_scaling_group(
+        LaunchConfigurationName="lc",
+        AutoScalingGroupName="test-asg",
+        MinSize=1,
+        MaxSize=1,
+        VPCZoneIdentifier=mocked_networking["subnet1"],
+    )
+
+    # When we suspect 'Launch' and 'Termiate' process then resume 'Launch'
+    client.suspend_processes(
+        AutoScalingGroupName="test-asg", ScalingProcesses=["Launch", "Terminate"]
+    )
+
+    client.resume_processes(
+        AutoScalingGroupName="test-asg", ScalingProcesses=["Launch"]
+    )
+
+    res = client.describe_auto_scaling_groups(AutoScalingGroupNames=["test-asg"])
+
+    # Only 'Terminate' should be suspended
+    expected_suspended_processes = [
+        {"ProcessName": "Terminate", "SuspensionReason": ""}
+    ]
+    res["AutoScalingGroups"][0]["SuspendedProcesses"].should.equal(
+        expected_suspended_processes
+    )
+
+
+@mock_autoscaling
+def test_resume_processes_all_by_default():
+    mocked_networking = setup_networking()
+    client = boto3.client("autoscaling", region_name="us-east-1")
+    client.create_launch_configuration(
+        LaunchConfigurationName="lc", ImageId=EXAMPLE_AMI_ID, InstanceType="t2.medium"
+    )
+    client.create_auto_scaling_group(
+        LaunchConfigurationName="lc",
+        AutoScalingGroupName="test-asg",
+        MinSize=1,
+        MaxSize=1,
+        VPCZoneIdentifier=mocked_networking["subnet1"],
+    )
+
+    # When we suspend two processes then resume with no process argument
+    client.suspend_processes(
+        AutoScalingGroupName="test-asg", ScalingProcesses=["Launch", "Terminate"]
+    )
+
+    client.resume_processes(AutoScalingGroupName="test-asg")
+
+    res = client.describe_auto_scaling_groups(AutoScalingGroupNames=["test-asg"])
+
+    # No processes should be suspended
+    res["AutoScalingGroups"][0]["SuspendedProcesses"].should.equal([])
+
+
 @mock_autoscaling
 def test_set_instance_protection():
     mocked_networking = setup_networking()