description: |- *Runbook for Scenario 001* --- # Runbook for Scenario 001 1. Get Regions 2. Turn Trade Generator Arc Control Off 1 3. Wait For Trade Generator To Stop1 4. Clear Databases 5. Turn Trade Generator Arc Control On 1 6. Wait For Trade Generator To Publish 1 7. Turn Trade Generator Arc Control Off 2 8. Wait For Trade Generator To Stop 2 9. Get App State 1 10. Turn Primary DNS Arc Control Off 11. Wait For MQ To Drain 12. Wait For Kinesis Streams To Be Processed 1 13. Wait For Dynamo DB To Replicate 14. Wait For Aurora Global Database To Replicate 15. Turn Primary Queue Arc Control Off 16. Turn Primary App Arc Control Off 17. Failover Aurora Global Database 18. Wait For Aurora Global Failover 19. Update Database Secret 20. Restart ECS Service 21. Wait For ECS Service To Start 22. Turn Secondary App Arc Control On 23. Reconcile And Replay Inbound Gateway and Ingestion 24. Wait For Inbound Gateway Reconciliation To Complete 25. Wait For Kinesis Streams To Be Processed 2 26. Reconcile And Replay Ingestion and Matching 27. Wait For Ingestion Reconciliation To Complete 28. Wait For Kinesis Streams To Be Processed 3 29. Reconcile And Replay Matching and Egress 30. Wait For Matching Reconciliation To Complete 31. Wait For Kinesis Streams To Be Processed 4 32. Reconcile And Replay Egress and Outbound Gateway 33. Wait For Egress Reconciliation To Complete 34. Wait For Kinesis Streams To Be Processed 5 35. Get App State 2 36. Compare App States 1 37. Turn Secondary Queue Arc Control On 38. Turn Secondary DNS Arc Control On 39. Turn Trade Generator Arc Control On 2 40. Wait For Trade Generator To Publish 2 41. Turn Trade Generator Arc Control Off 3 42. Wait For Trade Generator To Stop 3 43. Get App State 3 44. Compare App States 2 45. Evaluate Test Case schemaVersion: '0.3' assumeRole: 'arn:aws:iam::285719923712:role/team-app-rotation-automation-role' outputs: - EvaluateTestCase.SUCCESS parameters: APP: type: String description: 'Enter Name of the Application: trade-matching or settlement' allowedValues: - trade-matching - settlement TYPE: type: String description: 'Select Type of Rotation: App Rotation or DR' allowedValues: - App Rotation - DR MODE: type: String allowedValues: - test - prod description: 'Select Mode : test or prod' mainSteps: - name: GetRegions action: 'aws:executeScript' inputs: Runtime: python3.8 Handler: rotation.invoke Script: '' InputPayload: FUNCTION: get_regions APP: '{{APP}}' TYPE: '{{TYPE}}' AWS_REGION: us-east-1 AWS_REGION1: us-east-1 AWS_REGION2: us-west-2 Attachment: rotation.py outputs: - Name: ACTIVE_REGION Selector: $.Payload.active_region Type: String - Name: PASSIVE_REGION Selector: $.Payload.passive_region Type: String - name: TurnTradeGeneratorArcControlOff1 action: 'aws:executeScript' inputs: Runtime: python3.8 Handler: rotation.invoke Script: '' Attachment: rotation.py InputPayload: FUNCTION: update_arc_control APP: trade-matching TYPE: '{{TYPE}}' SCOPE: generator STATE: 'Off' AWS_REGION: us-east-1 - name: WaitForTradeGeneratorToStop1 action: 'aws:executeScript' inputs: Runtime: python3.8 Handler: rotation.invoke Script: '' InputPayload: FUNCTION: sleep DURATION: 60 Attachment: rotation.py - name: ClearDatabases action: 'aws:invokeLambdaFunction' inputs: InvocationType: RequestResponse FunctionName: clean_databases - name: TurnTradeGeneratorArcControlOn1 action: 'aws:executeScript' inputs: Runtime: python3.8 Handler: rotation.invoke Script: '' Attachment: rotation.py InputPayload: FUNCTION: update_arc_control APP: trade-matching TYPE: '{{TYPE}}' SCOPE: generator STATE: 'On' AWS_REGION: us-east-1 - name: WaitForTradeGeneratorToPublish1 action: 'aws:executeScript' inputs: Runtime: python3.8 Handler: rotation.invoke Script: '' InputPayload: FUNCTION: sleep DURATION: 30 Attachment: rotation.py - name: TurnTradeGeneratorArcControlOff2 action: 'aws:executeScript' inputs: Runtime: python3.8 Handler: rotation.invoke Script: '' Attachment: rotation.py InputPayload: FUNCTION: update_arc_control APP: trade-matching TYPE: '{{TYPE}}' SCOPE: generator STATE: 'Off' AWS_REGION: us-east-1 - name: WaitForTradeGeneratorToStop2 action: 'aws:executeScript' inputs: Runtime: python3.8 Handler: rotation.invoke Script: '' Attachment: rotation.py InputPayload: FUNCTION: sleep DURATION: 60 - name: GetAppState1 action: 'aws:invokeLambdaFunction' inputs: InvocationType: RequestResponse FunctionName: get_app_state InputPayload: queryStringParameters: app: '{{APP}}' region: '{{GetRegions.ACTIVE_REGION}}' - name: TurnPrimaryDNSArcControlOff action: 'aws:executeScript' inputs: Runtime: python3.8 Handler: rotation.invoke Script: '' InputPayload: FUNCTION: update_arc_control APP: '{{APP}}' TYPE: '{{TYPE}}' SCOPE: dns STATE: 'Off' AWS_REGION: '{{GetRegions.ACTIVE_REGION}}' Attachment: rotation.py - name: WaitForMQToDrain action: 'aws:executeScript' inputs: Runtime: python3.8 Handler: rotation.invoke Script: '' InputPayload: FUNCTION: wait_for_mq_to_drain APP: '{{APP}}' TYPE: '{{TYPE}}' AWS_REGION: '{{GetRegions.ACTIVE_REGION}}' COMPONENT: inbound-gateway QUEUE: trades Attachment: rotation.py - name: WaitForKinesisStreamsToBeProcessed1 action: 'aws:executeScript' inputs: Runtime: python3.8 Handler: rotation.invoke Script: '' InputPayload: FUNCTION: wait_for_kinesis_streams APP: '{{APP}}' TYPE: '{{TYPE}}' AWS_REGION: '{{GetRegions.ACTIVE_REGION}}' Attachment: rotation.py - name: WaitForDynamoDBToReplicate action: 'aws:executeScript' inputs: Runtime: python3.8 Handler: rotation.invoke Script: '' InputPayload: FUNCTION: wait_for_dynamodb_to_replicate APP: '{{APP}}' TYPE: '{{TYPE}}' AWS_REGION: '{{GetRegions.ACTIVE_REGION}}' AWS_RECEIVING_REGION: '{{GetRegions.PASSIVE_REGION}}' Attachment: rotation.py - name: WaitForAuroraGlobalDatabaseToReplicate action: 'aws:executeScript' inputs: Runtime: python3.8 Handler: rotation.invoke Script: '' InputPayload: FUNCTION: wait_for_aurora_to_replicate APP: '{{APP}}' TYPE: '{{TYPE}}' AWS_REGION: '{{GetRegions.ACTIVE_REGION}}' COMPONENT: core Attachment: rotation.py - name: TurnPrimaryQueueArcControlOff action: 'aws:executeScript' inputs: Runtime: python3.8 Handler: rotation.invoke Script: '' InputPayload: FUNCTION: update_arc_control APP: '{{APP}}' TYPE: '{{TYPE}}' SCOPE: queue STATE: 'Off' AWS_REGION: '{{GetRegions.ACTIVE_REGION}}' Attachment: rotation.py - name: TurnPrimaryAppArcControlOff action: 'aws:executeScript' inputs: Runtime: python3.8 Handler: rotation.invoke Script: '' InputPayload: FUNCTION: update_arc_control APP: '{{APP}}' TYPE: '{{TYPE}}' SCOPE: app STATE: 'Off' AWS_REGION: '{{GetRegions.ACTIVE_REGION}}' Attachment: rotation.py - name: FailoverAuroraGlobalDatabase action: 'aws:executeScript' inputs: Runtime: python3.8 Handler: rotation.invoke Script: '' InputPayload: FUNCTION: rotate_aurora_global_database APP: '{{APP}}' TYPE: '{{TYPE}}' AWS_REGION: '{{GetRegions.ACTIVE_REGION}}' COMPONENT: core Attachment: rotation.py outputs: - Name: GLOBAL_CLUSTER Selector: $.Payload.global_cluster Type: String - name: WaitForAuroraGlobalFailover action: 'aws:executeScript' inputs: Runtime: python3.8 Handler: rotation.invoke Script: '' InputPayload: FUNCTION: wait_for_aurora_to_be_available APP: '{{APP}}' TYPE: '{{TYPE}}' AWS_REGION: '{{GetRegions.ACTIVE_REGION}}' COMPONENT: core Attachment: rotation.py - name: UpdateDatabaseSecret action: 'aws:executeScript' inputs: Runtime: python3.8 Handler: rotation.invoke Script: '' InputPayload: FUNCTION: update_database_secret APP: '{{APP}}' TYPE: '{{TYPE}}' AWS_REGION: '{{GetRegions.PASSIVE_REGION}}' COMPONENT: core Attachment: rotation.py - name: RestartECSService action: 'aws:executeScript' inputs: Runtime: python3.8 Handler: rotation.invoke Script: '' InputPayload: FUNCTION: restart_ecs_service APP: '{{APP}}' TYPE: '{{TYPE}}' AWS_REGION: '{{GetRegions.PASSIVE_REGION}}' COMPONENT: core Attachment: rotation.py - name: WaitForECSServiceToStart action: 'aws:executeScript' inputs: Runtime: python3.8 Handler: rotation.invoke Script: '' Attachment: rotation.py InputPayload: FUNCTION: sleep DURATION: 150 - name: TurnSecondaryAppArcControlOn action: 'aws:executeScript' inputs: Runtime: python3.8 Handler: rotation.invoke Script: '' InputPayload: FUNCTION: update_arc_control APP: '{{APP}}' TYPE: '{{TYPE}}' SCOPE: app STATE: 'On' AWS_REGION: '{{GetRegions.PASSIVE_REGION}}' Attachment: rotation.py - name: ReconcileAndReplayInboundGatewayAndIngestion action: 'aws:executeScript' inputs: Runtime: python3.8 Handler: rotation.invoke Script: '' InputPayload: FUNCTION: reconciliation APP: '{{APP}}' TYPE: '{{TYPE}}' AWS_REGION: '{{GetRegions.PASSIVE_REGION}}' RECONCILIATION: InboundIngress Attachment: rotation.py - name: WaitForInboundGatewayReconciliationToComplete action: 'aws:executeScript' inputs: Runtime: python3.8 Handler: rotation.invoke Script: '' Attachment: rotation.py InputPayload: FUNCTION: sleep DURATION: 45 - name: WaitForKinesisStreamsToBeProcessed2 action: 'aws:executeScript' inputs: Runtime: python3.8 Handler: rotation.invoke Script: '' InputPayload: FUNCTION: wait_for_kinesis_streams APP: '{{APP}}' TYPE: '{{TYPE}}' AWS_REGION: '{{GetRegions.PASSIVE_REGION}}' Attachment: rotation.py - name: ReconcileAndReplayIngestionAndMatching action: 'aws:executeScript' inputs: Runtime: python3.8 Handler: rotation.invoke Script: '' InputPayload: FUNCTION: reconciliation APP: '{{APP}}' TYPE: '{{TYPE}}' AWS_REGION: '{{GetRegions.PASSIVE_REGION}}' RECONCILIATION: IngressCore Attachment: rotation.py - name: WaitForIngestionReconciliationToComplete action: 'aws:executeScript' inputs: Runtime: python3.8 Handler: rotation.invoke Script: '' Attachment: rotation.py InputPayload: FUNCTION: sleep DURATION: 45 - name: WaitForKinesisStreamsToBeProcessed3 action: 'aws:executeScript' inputs: Runtime: python3.8 Handler: rotation.invoke Script: '' InputPayload: FUNCTION: wait_for_kinesis_streams APP: '{{APP}}' TYPE: '{{TYPE}}' AWS_REGION: '{{GetRegions.PASSIVE_REGION}}' Attachment: rotation.py - name: ReconcileAndReplayMatchingAndEgress action: 'aws:executeScript' inputs: Runtime: python3.8 Handler: rotation.invoke Script: '' InputPayload: FUNCTION: reconciliation APP: '{{APP}}' TYPE: '{{TYPE}}' AWS_REGION: '{{GetRegions.PASSIVE_REGION}}' RECONCILIATION: CoreEgress Attachment: rotation.py - name: WaitForMatchingReconciliationToComplete action: 'aws:executeScript' inputs: Runtime: python3.8 Handler: rotation.invoke Script: '' Attachment: rotation.py InputPayload: FUNCTION: sleep DURATION: 45 - name: WaitForKinesisStreamsToBeProcessed4 action: 'aws:executeScript' inputs: Runtime: python3.8 Handler: rotation.invoke Script: '' InputPayload: FUNCTION: wait_for_kinesis_streams APP: '{{APP}}' TYPE: '{{TYPE}}' AWS_REGION: '{{GetRegions.PASSIVE_REGION}}' Attachment: rotation.py - name: ReconcileAndReplayEgressAndOutboundGateway action: 'aws:executeScript' inputs: Runtime: python3.8 Handler: rotation.invoke Script: '' InputPayload: FUNCTION: reconciliation APP: '{{APP}}' TYPE: '{{TYPE}}' AWS_REGION: '{{GetRegions.PASSIVE_REGION}}' RECONCILIATION: EgressOutbound Attachment: rotation.py - name: WaitForEgressReconciliationToComplete action: 'aws:executeScript' inputs: Runtime: python3.8 Handler: rotation.invoke Script: '' Attachment: rotation.py InputPayload: FUNCTION: sleep DURATION: 45 - name: WaitForKinesisStreamsToBeProcessed5 action: 'aws:executeScript' inputs: Runtime: python3.8 Handler: rotation.invoke Script: '' InputPayload: FUNCTION: wait_for_kinesis_streams APP: '{{APP}}' TYPE: '{{TYPE}}' AWS_REGION: '{{GetRegions.PASSIVE_REGION}}' Attachment: rotation.py - name: GetAppState2 action: 'aws:invokeLambdaFunction' inputs: InvocationType: RequestResponse FunctionName: get_app_state InputPayload: queryStringParameters: app: '{{APP}}' region: '{{GetRegions.ACTIVE_REGION}}' - name: CompareAppStates1 action: 'aws:executeScript' inputs: Runtime: python3.8 Handler: compare_app_states Script: |- import json from datetime import datetime from botocore.exceptions import ClientError import boto3 import logging import traceback import base64 def compare_app_states(event, context): print(datetime.now().strftime("%m/%d/%Y, %H:%M:%S") + " get_app_state Invoked") region = event['AWS_REGION'] app_state_1_json = event['APP_STATE_1'] app_state_2_json = event['APP_STATE_2'] # app_state_1 = json.loads(app_state_1_json) # app_state_2 = json.loads(app_state_2_json) # result = False # if (app_state_1["tm_inbound_trade"] == app_state_2["tm_inbound_trade"] and # app_state_1["tm_inbound_settlement"] == app_state_2["tm_inbound_settlement"] and # app_state_1["tm_ingress_trade"] == app_state_2["tm_ingress_trade"] and # app_state_1["tm_ingress_settlement"] == app_state_2["tm_ingress_settlement"] and # app_state_1["tm_core_trade"] == app_state_2["tm_core_trade"] and # app_state_1["tm_core_allocation"] == app_state_2["tm_core_allocation"] and # app_state_1["tm_egress_trade"] == app_state_2["tm_egress_trade"] and # app_state_1["tm_egress_settlement"] == app_state_2["tm_egress_settlement"] and # app_state_1["tm_outbound_trade"] == app_state_2["tm_outbound_trade"] and # app_state_1["tm_outbound_settlement"] == app_state_2["tm_outbound_settlement"] and # app_state_1["sm_inbound_settlement"] == app_state_2["sm_inbound_settlement"] and # app_state_1["sm_ingress_settlement"] == app_state_2["sm_ingress_settlement"] and # app_state_1["sm_core_settlement"] == app_state_2["sm_core_settlement"] and # app_state_1["sm_egress_settlement"] == app_state_2["sm_egress_settlement"] and # app_state_1["sm_outbound_settlement"] == app_state_2["sm_outbound_settlement"]): # result = True # else: # result = False return {'result': (app_state_1_json == app_state_2_json)} class AppState: """ It represents the state of an App. """ def __init__(self): """ Initializes the App State. """ self.tm_inbound_trade = 0 self.tm_inbound_settlement = 0 self.tm_ingress_trade = 0 self.tm_ingress_settlement = 0 self.tm_core_trade = 0 self.tm_core_allocation = 0 self.tm_egress_trade = 0 self.tm_egress_settlement = 0 self.tm_outbound_trade = 0 self.tm_outbound_settlement = 0 self.sm_inbound_settlement = 0 self.sm_ingress_settlement = 0 self.sm_core_settlement = 0 self.sm_egress_settlement = 0 self.sm_outbound_settlement = 0 def to_dict(self): return { 'tm_inbound_trade': self.tm_inbound_trade, 'tm_inbound_settlement': self.tm_inbound_settlement, 'tm_ingress_trade': self.tm_ingress_trade, 'tm_ingress_settlement': self.tm_ingress_settlement, 'tm_core_trade': self.tm_core_trade, 'tm_core_allocation': self.tm_core_allocation, 'tm_egress_trade': self.tm_egress_trade, 'tm_egress_settlement': self.tm_egress_settlement, 'tm_outbound_trade': self.tm_outbound_trade, 'tm_outbound_settlement': self.tm_outbound_settlement, 'sm_inbound_settlement': self.sm_inbound_settlement, 'sm_ingress_settlement': self.sm_ingress_settlement, 'sm_core_settlement': self.sm_core_settlement, 'sm_egress_settlement': self.sm_egress_settlement, 'sm_outbound_settlement': self.sm_outbound_settlement } Attachment: rotation.py InputPayload: FUNCTION: compare_app_states AWS_REGION: us-east-1 APP_STATE_1: '{{GetAppState1.Payload}}' APP_STATE_2: '{{GetAppState2.Payload}}' outputs: - Name: RESULT Selector: $.Payload.result Type: Boolean - name: TurnSecondaryQueueArcControlOn action: 'aws:executeScript' inputs: Runtime: python3.8 Handler: rotation.invoke Script: '' InputPayload: FUNCTION: update_arc_control APP: '{{APP}}' TYPE: '{{TYPE}}' SCOPE: queue STATE: 'On' AWS_REGION: '{{GetRegions.PASSIVE_REGION}}' Attachment: rotation.py - name: TurnSecondaryDnsArcControlOn action: 'aws:executeScript' inputs: Runtime: python3.8 Handler: rotation.invoke Script: '' Attachment: rotation.py InputPayload: FUNCTION: update_arc_control APP: '{{APP}}' TYPE: '{{TYPE}}' SCOPE: dns STATE: 'On' AWS_REGION: '{{GetRegions.PASSIVE_REGION}}' - name: TurnTradeGeneratorArcControlOn2 action: 'aws:executeScript' inputs: Runtime: python3.8 Handler: rotation.invoke Script: '' Attachment: rotation.py InputPayload: FUNCTION: update_arc_control APP: trade-matching TYPE: '{{TYPE}}' SCOPE: generator STATE: 'On' AWS_REGION: us-east-1 - name: WaitForTradeGeneratorToPublish2 action: 'aws:executeScript' inputs: Runtime: python3.8 Handler: rotation.invoke Script: '' Attachment: rotation.py InputPayload: FUNCTION: sleep DURATION: 30 - name: TurnTradeGeneratorArcControlOff3 action: 'aws:executeScript' inputs: Runtime: python3.8 Handler: rotation.invoke Script: '' Attachment: rotation.py InputPayload: FUNCTION: update_arc_control APP: trade-matching TYPE: '{{TYPE}}' SCOPE: generator STATE: 'Off' AWS_REGION: us-east-1 - name: WaitForTradeGeneratorToStop3 action: 'aws:executeScript' inputs: Runtime: python3.8 Handler: rotation.invoke Script: '' Attachment: rotation.py InputPayload: FUNCTION: sleep DURATION: 30 - name: GetAppState3 action: 'aws:invokeLambdaFunction' inputs: InvocationType: RequestResponse FunctionName: get_app_state InputPayload: queryStringParameters: app: '{{APP}}' region: '{{GetRegions.ACTIVE_REGION}}' - name: CompareAppStates2 action: 'aws:executeScript' inputs: Runtime: python3.8 Handler: compare_app_states Script: |- import json from datetime import datetime from botocore.exceptions import ClientError import boto3 import logging import traceback import base64 def compare_app_states(event, context): print(datetime.now().strftime("%m/%d/%Y, %H:%M:%S") + " get_app_state Invoked") region = event['AWS_REGION'] app_state_1_json = event['APP_STATE_1'] app_state_2_json = event['APP_STATE_2'] # app_state_1 = json.loads(app_state_1_json) # app_state_2 = json.loads(app_state_2_json) # result = False # if (app_state_1["tm_inbound_trade"] == app_state_2["tm_inbound_trade"] and # app_state_1["tm_inbound_settlement"] == app_state_2["tm_inbound_settlement"] and # app_state_1["tm_ingress_trade"] == app_state_2["tm_ingress_trade"] and # app_state_1["tm_ingress_settlement"] == app_state_2["tm_ingress_settlement"] and # app_state_1["tm_core_trade"] == app_state_2["tm_core_trade"] and # app_state_1["tm_core_allocation"] == app_state_2["tm_core_allocation"] and # app_state_1["tm_egress_trade"] == app_state_2["tm_egress_trade"] and # app_state_1["tm_egress_settlement"] == app_state_2["tm_egress_settlement"] and # app_state_1["tm_outbound_trade"] == app_state_2["tm_outbound_trade"] and # app_state_1["tm_outbound_settlement"] == app_state_2["tm_outbound_settlement"] and # app_state_1["sm_inbound_settlement"] == app_state_2["sm_inbound_settlement"] and # app_state_1["sm_ingress_settlement"] == app_state_2["sm_ingress_settlement"] and # app_state_1["sm_core_settlement"] == app_state_2["sm_core_settlement"] and # app_state_1["sm_egress_settlement"] == app_state_2["sm_egress_settlement"] and # app_state_1["sm_outbound_settlement"] == app_state_2["sm_outbound_settlement"]): # result = True # else: # result = False return {'result': (app_state_1_json == app_state_2_json)} class AppState: """ It represents the state of an App. """ def __init__(self): """ Initializes the App State. """ self.tm_inbound_trade = 0 self.tm_inbound_settlement = 0 self.tm_ingress_trade = 0 self.tm_ingress_settlement = 0 self.tm_core_trade = 0 self.tm_core_allocation = 0 self.tm_egress_trade = 0 self.tm_egress_settlement = 0 self.tm_outbound_trade = 0 self.tm_outbound_settlement = 0 self.sm_inbound_settlement = 0 self.sm_ingress_settlement = 0 self.sm_core_settlement = 0 self.sm_egress_settlement = 0 self.sm_outbound_settlement = 0 def to_dict(self): return { 'tm_inbound_trade': self.tm_inbound_trade, 'tm_inbound_settlement': self.tm_inbound_settlement, 'tm_ingress_trade': self.tm_ingress_trade, 'tm_ingress_settlement': self.tm_ingress_settlement, 'tm_core_trade': self.tm_core_trade, 'tm_core_allocation': self.tm_core_allocation, 'tm_egress_trade': self.tm_egress_trade, 'tm_egress_settlement': self.tm_egress_settlement, 'tm_outbound_trade': self.tm_outbound_trade, 'tm_outbound_settlement': self.tm_outbound_settlement, 'sm_inbound_settlement': self.sm_inbound_settlement, 'sm_ingress_settlement': self.sm_ingress_settlement, 'sm_core_settlement': self.sm_core_settlement, 'sm_egress_settlement': self.sm_egress_settlement, 'sm_outbound_settlement': self.sm_outbound_settlement } Attachment: rotation.py InputPayload: FUNCTION: compare_app_states AWS_REGION: us-east-1 APP_STATE_1: '{{GetAppState2.Payload}}' APP_STATE_2: '{{GetAppState3.Payload}}' outputs: - Name: RESULT Selector: $.Payload.message Type: Boolean - name: EvaluateTestCase action: 'aws:executeScript' inputs: Runtime: python3.8 Handler: script_handler Script: |- def script_handler(events, context): rpo_is_zero = events['RPO_IS_ZERO'] matches_after_rotation = events['MATCHES_AFTER_ROTATION'] success = False if (rpo_is_zero and not matches_after_rotation): success = True else: success = False return { 'success': success } InputPayload: RPO_IS_ZERO: '{{CompareAppStates1.RESULT}}' MATCHES_AFTER_ROTATION: '{{CompareAppStates2.RESULT}}' outputs: - Name: SUCCESS Selector: $.Payload.success Type: Boolean files: rotation.py: checksums: sha256: