diff --git a/IMPLEMENTATION_COVERAGE.md b/IMPLEMENTATION_COVERAGE.md
index cd1d4d482..a863d483d 100644
--- a/IMPLEMENTATION_COVERAGE.md
+++ b/IMPLEMENTATION_COVERAGE.md
@@ -1,4 +1,25 @@
+## accessanalyzer
+0% implemented
+- [ ] create_analyzer
+- [ ] create_archive_rule
+- [ ] delete_analyzer
+- [ ] delete_archive_rule
+- [ ] get_analyzed_resource
+- [ ] get_analyzer
+- [ ] get_archive_rule
+- [ ] get_finding
+- [ ] list_analyzed_resources
+- [ ] list_analyzers
+- [ ] list_archive_rules
+- [ ] list_findings
+- [ ] list_tags_for_resource
+- [ ] start_resource_scan
+- [ ] tag_resource
+- [ ] untag_resource
+- [ ] update_archive_rule
+- [ ] update_findings
+
## acm
38% implemented
- [X] add_tags_to_certificate
@@ -137,11 +158,13 @@
## amplify
0% implemented
- [ ] create_app
+- [ ] create_backend_environment
- [ ] create_branch
- [ ] create_deployment
- [ ] create_domain_association
- [ ] create_webhook
- [ ] delete_app
+- [ ] delete_backend_environment
- [ ] delete_branch
- [ ] delete_domain_association
- [ ] delete_job
@@ -149,12 +172,14 @@
- [ ] generate_access_logs
- [ ] get_app
- [ ] get_artifact_url
+- [ ] get_backend_environment
- [ ] get_branch
- [ ] get_domain_association
- [ ] get_job
- [ ] get_webhook
- [ ] list_apps
- [ ] list_artifacts
+- [ ] list_backend_environments
- [ ] list_branches
- [ ] list_domain_associations
- [ ] list_jobs
@@ -315,6 +340,7 @@
- [ ] delete_api
- [ ] delete_api_mapping
- [ ] delete_authorizer
+- [ ] delete_cors_configuration
- [ ] delete_deployment
- [ ] delete_domain_name
- [ ] delete_integration
@@ -322,6 +348,7 @@
- [ ] delete_model
- [ ] delete_route
- [ ] delete_route_response
+- [ ] delete_route_settings
- [ ] delete_stage
- [ ] get_api
- [ ] get_api_mapping
@@ -347,6 +374,8 @@
- [ ] get_stage
- [ ] get_stages
- [ ] get_tags
+- [ ] import_api
+- [ ] reimport_api
- [ ] tag_resource
- [ ] untag_resource
- [ ] update_api
@@ -361,6 +390,38 @@
- [ ] update_route_response
- [ ] update_stage
+## appconfig
+0% implemented
+- [ ] create_application
+- [ ] create_configuration_profile
+- [ ] create_deployment_strategy
+- [ ] create_environment
+- [ ] delete_application
+- [ ] delete_configuration_profile
+- [ ] delete_deployment_strategy
+- [ ] delete_environment
+- [ ] get_application
+- [ ] get_configuration
+- [ ] get_configuration_profile
+- [ ] get_deployment
+- [ ] get_deployment_strategy
+- [ ] get_environment
+- [ ] list_applications
+- [ ] list_configuration_profiles
+- [ ] list_deployment_strategies
+- [ ] list_deployments
+- [ ] list_environments
+- [ ] list_tags_for_resource
+- [ ] start_deployment
+- [ ] stop_deployment
+- [ ] tag_resource
+- [ ] untag_resource
+- [ ] update_application
+- [ ] update_configuration_profile
+- [ ] update_deployment_strategy
+- [ ] update_environment
+- [ ] validate_configuration
+
## application-autoscaling
0% implemented
- [ ] delete_scaling_policy
@@ -378,21 +439,30 @@
0% implemented
- [ ] create_application
- [ ] create_component
+- [ ] create_log_pattern
- [ ] delete_application
- [ ] delete_component
+- [ ] delete_log_pattern
- [ ] describe_application
- [ ] describe_component
- [ ] describe_component_configuration
- [ ] describe_component_configuration_recommendation
+- [ ] describe_log_pattern
- [ ] describe_observation
- [ ] describe_problem
- [ ] describe_problem_observations
- [ ] list_applications
- [ ] list_components
+- [ ] list_log_pattern_sets
+- [ ] list_log_patterns
- [ ] list_problems
+- [ ] list_tags_for_resource
+- [ ] tag_resource
+- [ ] untag_resource
- [ ] update_application
- [ ] update_component
- [ ] update_component_configuration
+- [ ] update_log_pattern
## appmesh
0% implemented
@@ -477,18 +547,22 @@
## appsync
0% implemented
+- [ ] create_api_cache
- [ ] create_api_key
- [ ] create_data_source
- [ ] create_function
- [ ] create_graphql_api
- [ ] create_resolver
- [ ] create_type
+- [ ] delete_api_cache
- [ ] delete_api_key
- [ ] delete_data_source
- [ ] delete_function
- [ ] delete_graphql_api
- [ ] delete_resolver
- [ ] delete_type
+- [ ] flush_api_cache
+- [ ] get_api_cache
- [ ] get_data_source
- [ ] get_function
- [ ] get_graphql_api
@@ -507,6 +581,7 @@
- [ ] start_schema_creation
- [ ] tag_resource
- [ ] untag_resource
+- [ ] update_api_cache
- [ ] update_api_key
- [ ] update_data_source
- [ ] update_function
@@ -686,6 +761,9 @@
## ce
0% implemented
+- [ ] create_cost_category_definition
+- [ ] delete_cost_category_definition
+- [ ] describe_cost_category_definition
- [ ] get_cost_and_usage
- [ ] get_cost_and_usage_with_resources
- [ ] get_cost_forecast
@@ -700,12 +778,15 @@
- [ ] get_savings_plans_utilization_details
- [ ] get_tags
- [ ] get_usage_forecast
+- [ ] list_cost_category_definitions
+- [ ] update_cost_category_definition
## chime
0% implemented
- [ ] associate_phone_number_with_user
- [ ] associate_phone_numbers_with_voice_connector
- [ ] associate_phone_numbers_with_voice_connector_group
+- [ ] batch_create_attendee
- [ ] batch_create_room_membership
- [ ] batch_delete_phone_number
- [ ] batch_suspend_user
@@ -713,14 +794,18 @@
- [ ] batch_update_phone_number
- [ ] batch_update_user
- [ ] create_account
+- [ ] create_attendee
- [ ] create_bot
+- [ ] create_meeting
- [ ] create_phone_number_order
- [ ] create_room
- [ ] create_room_membership
- [ ] create_voice_connector
- [ ] create_voice_connector_group
- [ ] delete_account
+- [ ] delete_attendee
- [ ] delete_events_configuration
+- [ ] delete_meeting
- [ ] delete_phone_number
- [ ] delete_room
- [ ] delete_room_membership
@@ -735,9 +820,11 @@
- [ ] disassociate_phone_numbers_from_voice_connector_group
- [ ] get_account
- [ ] get_account_settings
+- [ ] get_attendee
- [ ] get_bot
- [ ] get_events_configuration
- [ ] get_global_settings
+- [ ] get_meeting
- [ ] get_phone_number
- [ ] get_phone_number_order
- [ ] get_phone_number_settings
@@ -753,7 +840,9 @@
- [ ] get_voice_connector_termination_health
- [ ] invite_users
- [ ] list_accounts
+- [ ] list_attendees
- [ ] list_bots
+- [ ] list_meetings
- [ ] list_phone_number_orders
- [ ] list_phone_numbers
- [ ] list_room_memberships
@@ -869,7 +958,7 @@
- [ ] upgrade_published_schema
## cloudformation
-40% implemented
+32% implemented
- [ ] cancel_update_stack
- [ ] continue_update_rollback
- [X] create_change_set
@@ -880,6 +969,7 @@
- [X] delete_stack
- [X] delete_stack_instances
- [X] delete_stack_set
+- [ ] deregister_type
- [ ] describe_account_limits
- [X] describe_change_set
- [ ] describe_stack_drift_detection_status
@@ -891,8 +981,11 @@
- [ ] describe_stack_set
- [ ] describe_stack_set_operation
- [X] describe_stacks
+- [ ] describe_type
+- [ ] describe_type_registration
- [ ] detect_stack_drift
- [ ] detect_stack_resource_drift
+- [ ] detect_stack_set_drift
- [ ] estimate_template_cost
- [X] execute_change_set
- [ ] get_stack_policy
@@ -907,7 +1000,13 @@
- [ ] list_stack_set_operations
- [ ] list_stack_sets
- [X] list_stacks
+- [ ] list_type_registrations
+- [ ] list_type_versions
+- [ ] list_types
+- [ ] record_handler_progress
+- [ ] register_type
- [ ] set_stack_policy
+- [ ] set_type_default_version
- [ ] signal_resource
- [ ] stop_stack_set_operation
- [X] update_stack
@@ -1045,6 +1144,7 @@
- [ ] delete_trail
- [ ] describe_trails
- [ ] get_event_selectors
+- [ ] get_insight_selectors
- [ ] get_trail
- [ ] get_trail_status
- [ ] list_public_keys
@@ -1052,31 +1152,38 @@
- [ ] list_trails
- [ ] lookup_events
- [ ] put_event_selectors
+- [ ] put_insight_selectors
- [ ] remove_tags
- [ ] start_logging
- [ ] stop_logging
- [ ] update_trail
## cloudwatch
-39% implemented
+34% implemented
- [X] delete_alarms
- [ ] delete_anomaly_detector
- [X] delete_dashboards
+- [ ] delete_insight_rules
- [ ] describe_alarm_history
- [ ] describe_alarms
- [ ] describe_alarms_for_metric
- [ ] describe_anomaly_detectors
+- [ ] describe_insight_rules
- [ ] disable_alarm_actions
+- [ ] disable_insight_rules
- [ ] enable_alarm_actions
+- [ ] enable_insight_rules
- [X] get_dashboard
+- [ ] get_insight_rule_report
- [ ] get_metric_data
- [X] get_metric_statistics
- [ ] get_metric_widget_image
- [X] list_dashboards
-- [ ] list_metrics
+- [X] list_metrics
- [ ] list_tags_for_resource
- [ ] put_anomaly_detector
- [X] put_dashboard
+- [ ] put_insight_rule
- [X] put_metric_alarm
- [X] put_metric_data
- [X] set_alarm_state
@@ -1088,39 +1195,64 @@
- [ ] batch_delete_builds
- [ ] batch_get_builds
- [ ] batch_get_projects
+- [ ] batch_get_report_groups
+- [ ] batch_get_reports
- [ ] create_project
+- [ ] create_report_group
- [ ] create_webhook
- [ ] delete_project
+- [ ] delete_report
+- [ ] delete_report_group
+- [ ] delete_resource_policy
- [ ] delete_source_credentials
- [ ] delete_webhook
+- [ ] describe_test_cases
+- [ ] get_resource_policy
- [ ] import_source_credentials
- [ ] invalidate_project_cache
- [ ] list_builds
- [ ] list_builds_for_project
- [ ] list_curated_environment_images
- [ ] list_projects
+- [ ] list_report_groups
+- [ ] list_reports
+- [ ] list_reports_for_report_group
+- [ ] list_shared_projects
+- [ ] list_shared_report_groups
- [ ] list_source_credentials
+- [ ] put_resource_policy
- [ ] start_build
- [ ] stop_build
- [ ] update_project
+- [ ] update_report_group
- [ ] update_webhook
## codecommit
0% implemented
+- [ ] associate_approval_rule_template_with_repository
+- [ ] batch_associate_approval_rule_template_with_repositories
- [ ] batch_describe_merge_conflicts
+- [ ] batch_disassociate_approval_rule_template_from_repositories
- [ ] batch_get_commits
- [ ] batch_get_repositories
+- [ ] create_approval_rule_template
- [ ] create_branch
- [ ] create_commit
- [ ] create_pull_request
-- [ ] create_repository
+- [ ] create_pull_request_approval_rule
+- [X] create_repository
- [ ] create_unreferenced_merge_commit
+- [ ] delete_approval_rule_template
- [ ] delete_branch
- [ ] delete_comment_content
- [ ] delete_file
-- [ ] delete_repository
+- [ ] delete_pull_request_approval_rule
+- [X] delete_repository
- [ ] describe_merge_conflicts
- [ ] describe_pull_request_events
+- [ ] disassociate_approval_rule_template_from_repository
+- [ ] evaluate_pull_request_approval_rules
+- [ ] get_approval_rule_template
- [ ] get_blob
- [ ] get_branch
- [ ] get_comment
@@ -1134,11 +1266,16 @@
- [ ] get_merge_conflicts
- [ ] get_merge_options
- [ ] get_pull_request
-- [ ] get_repository
+- [ ] get_pull_request_approval_states
+- [ ] get_pull_request_override_state
+- [X] get_repository
- [ ] get_repository_triggers
+- [ ] list_approval_rule_templates
+- [ ] list_associated_approval_rule_templates_for_repository
- [ ] list_branches
- [ ] list_pull_requests
- [ ] list_repositories
+- [ ] list_repositories_for_approval_rule_template
- [ ] list_tags_for_resource
- [ ] merge_branches_by_fast_forward
- [ ] merge_branches_by_squash
@@ -1146,6 +1283,7 @@
- [ ] merge_pull_request_by_fast_forward
- [ ] merge_pull_request_by_squash
- [ ] merge_pull_request_by_three_way
+- [ ] override_pull_request_approval_rules
- [ ] post_comment_for_compared_commit
- [ ] post_comment_for_pull_request
- [ ] post_comment_reply
@@ -1154,8 +1292,13 @@
- [ ] tag_resource
- [ ] test_repository_triggers
- [ ] untag_resource
+- [ ] update_approval_rule_template_content
+- [ ] update_approval_rule_template_description
+- [ ] update_approval_rule_template_name
- [ ] update_comment
- [ ] update_default_branch
+- [ ] update_pull_request_approval_rule_content
+- [ ] update_pull_request_approval_state
- [ ] update_pull_request_description
- [ ] update_pull_request_status
- [ ] update_pull_request_title
@@ -1211,28 +1354,47 @@
- [ ] update_application
- [ ] update_deployment_group
-## codepipeline
+## codeguru-reviewer
0% implemented
+- [ ] associate_repository
+- [ ] describe_repository_association
+- [ ] disassociate_repository
+- [ ] list_repository_associations
+
+## codeguruprofiler
+0% implemented
+- [ ] configure_agent
+- [ ] create_profiling_group
+- [ ] delete_profiling_group
+- [ ] describe_profiling_group
+- [ ] get_profile
+- [ ] list_profile_times
+- [ ] list_profiling_groups
+- [ ] post_agent_profile
+- [ ] update_profiling_group
+
+## codepipeline
+22% implemented
- [ ] acknowledge_job
- [ ] acknowledge_third_party_job
- [ ] create_custom_action_type
-- [ ] create_pipeline
+- [X] create_pipeline
- [ ] delete_custom_action_type
-- [ ] delete_pipeline
+- [X] delete_pipeline
- [ ] delete_webhook
- [ ] deregister_webhook_with_third_party
- [ ] disable_stage_transition
- [ ] enable_stage_transition
- [ ] get_job_details
-- [ ] get_pipeline
+- [X] get_pipeline
- [ ] get_pipeline_execution
- [ ] get_pipeline_state
- [ ] get_third_party_job_details
- [ ] list_action_executions
- [ ] list_action_types
- [ ] list_pipeline_executions
-- [ ] list_pipelines
-- [ ] list_tags_for_resource
+- [X] list_pipelines
+- [X] list_tags_for_resource
- [ ] list_webhooks
- [ ] poll_for_jobs
- [ ] poll_for_third_party_jobs
@@ -1246,9 +1408,9 @@
- [ ] register_webhook_with_third_party
- [ ] retry_stage_execution
- [ ] start_pipeline_execution
-- [ ] tag_resource
-- [ ] untag_resource
-- [ ] update_pipeline
+- [X] tag_resource
+- [X] untag_resource
+- [X] update_pipeline
## codestar
0% implemented
@@ -1441,13 +1603,17 @@
- [ ] batch_detect_key_phrases
- [ ] batch_detect_sentiment
- [ ] batch_detect_syntax
+- [ ] classify_document
- [ ] create_document_classifier
+- [ ] create_endpoint
- [ ] create_entity_recognizer
- [ ] delete_document_classifier
+- [ ] delete_endpoint
- [ ] delete_entity_recognizer
- [ ] describe_document_classification_job
- [ ] describe_document_classifier
- [ ] describe_dominant_language_detection_job
+- [ ] describe_endpoint
- [ ] describe_entities_detection_job
- [ ] describe_entity_recognizer
- [ ] describe_key_phrases_detection_job
@@ -1461,6 +1627,7 @@
- [ ] list_document_classification_jobs
- [ ] list_document_classifiers
- [ ] list_dominant_language_detection_jobs
+- [ ] list_endpoints
- [ ] list_entities_detection_jobs
- [ ] list_entity_recognizers
- [ ] list_key_phrases_detection_jobs
@@ -1481,6 +1648,7 @@
- [ ] stop_training_entity_recognizer
- [ ] tag_resource
- [ ] untag_resource
+- [ ] update_endpoint
## comprehendmedical
0% implemented
@@ -1496,20 +1664,32 @@
- [ ] stop_entities_detection_v2_job
- [ ] stop_phi_detection_job
+## compute-optimizer
+0% implemented
+- [ ] get_auto_scaling_group_recommendations
+- [ ] get_ec2_instance_recommendations
+- [ ] get_ec2_recommendation_projected_metrics
+- [ ] get_enrollment_status
+- [ ] get_recommendation_summaries
+- [ ] update_enrollment_status
+
## config
-31% implemented
+25% implemented
- [X] batch_get_aggregate_resource_config
- [X] batch_get_resource_config
- [X] delete_aggregation_authorization
- [ ] delete_config_rule
- [X] delete_configuration_aggregator
- [X] delete_configuration_recorder
+- [ ] delete_conformance_pack
- [X] delete_delivery_channel
- [ ] delete_evaluation_results
- [ ] delete_organization_config_rule
+- [ ] delete_organization_conformance_pack
- [ ] delete_pending_aggregation_request
- [ ] delete_remediation_configuration
- [ ] delete_remediation_exceptions
+- [ ] delete_resource_config
- [ ] delete_retention_configuration
- [ ] deliver_config_snapshot
- [ ] describe_aggregate_compliance_by_config_rules
@@ -1522,10 +1702,15 @@
- [X] describe_configuration_aggregators
- [X] describe_configuration_recorder_status
- [X] describe_configuration_recorders
+- [ ] describe_conformance_pack_compliance
+- [ ] describe_conformance_pack_status
+- [ ] describe_conformance_packs
- [ ] describe_delivery_channel_status
- [X] describe_delivery_channels
- [ ] describe_organization_config_rule_statuses
- [ ] describe_organization_config_rules
+- [ ] describe_organization_conformance_pack_statuses
+- [ ] describe_organization_conformance_packs
- [ ] describe_pending_aggregation_requests
- [ ] describe_remediation_configurations
- [ ] describe_remediation_exceptions
@@ -1539,8 +1724,11 @@
- [ ] get_compliance_details_by_resource
- [ ] get_compliance_summary_by_config_rule
- [ ] get_compliance_summary_by_resource_type
+- [ ] get_conformance_pack_compliance_details
+- [ ] get_conformance_pack_compliance_summary
- [ ] get_discovered_resource_counts
- [ ] get_organization_config_rule_detailed_status
+- [ ] get_organization_conformance_pack_detailed_status
- [X] get_resource_config_history
- [X] list_aggregate_discovered_resources
- [X] list_discovered_resources
@@ -1549,11 +1737,14 @@
- [ ] put_config_rule
- [X] put_configuration_aggregator
- [X] put_configuration_recorder
+- [ ] put_conformance_pack
- [X] put_delivery_channel
- [ ] put_evaluations
- [ ] put_organization_config_rule
+- [ ] put_organization_conformance_pack
- [ ] put_remediation_configurations
- [ ] put_remediation_exceptions
+- [ ] put_resource_config
- [ ] put_retention_configuration
- [ ] select_resource_config
- [ ] start_config_rules_evaluation
@@ -1583,6 +1774,7 @@
- [ ] list_tags_for_resource
- [ ] list_user_hierarchy_groups
- [ ] list_users
+- [ ] start_chat_contact
- [ ] start_outbound_voice_contact
- [ ] stop_contact
- [ ] tag_resource
@@ -1594,6 +1786,14 @@
- [ ] update_user_routing_profile
- [ ] update_user_security_profiles
+## connectparticipant
+0% implemented
+- [ ] create_participant_connection
+- [ ] disconnect_participant
+- [ ] get_transcript
+- [ ] send_event
+- [ ] send_message
+
## cur
0% implemented
- [ ] delete_report_definition
@@ -1702,6 +1902,20 @@
- [ ] update_parameter_group
- [ ] update_subnet_group
+## detective
+0% implemented
+- [ ] accept_invitation
+- [ ] create_graph
+- [ ] create_members
+- [ ] delete_graph
+- [ ] delete_members
+- [ ] disassociate_membership
+- [ ] get_members
+- [ ] list_graphs
+- [ ] list_invitations
+- [ ] list_members
+- [ ] reject_invitation
+
## devicefarm
0% implemented
- [ ] create_device_pool
@@ -1982,24 +2196,31 @@
- [ ] delete_log_subscription
- [ ] delete_snapshot
- [ ] delete_trust
+- [ ] deregister_certificate
- [ ] deregister_event_topic
+- [ ] describe_certificate
- [ ] describe_conditional_forwarders
- [ ] describe_directories
- [ ] describe_domain_controllers
- [ ] describe_event_topics
+- [ ] describe_ldaps_settings
- [ ] describe_shared_directories
- [ ] describe_snapshots
- [ ] describe_trusts
+- [ ] disable_ldaps
- [ ] disable_radius
- [ ] disable_sso
+- [ ] enable_ldaps
- [ ] enable_radius
- [ ] enable_sso
- [ ] get_directory_limits
- [ ] get_snapshot_limits
+- [ ] list_certificates
- [ ] list_ip_routes
- [ ] list_log_subscriptions
- [ ] list_schema_extensions
- [ ] list_tags_for_resource
+- [ ] register_certificate
- [ ] register_event_topic
- [ ] reject_shared_directory
- [ ] remove_ip_routes
@@ -2016,7 +2237,7 @@
- [ ] verify_trust
## dynamodb
-19% implemented
+17% implemented
- [ ] batch_get_item
- [ ] batch_write_item
- [ ] create_backup
@@ -2027,14 +2248,17 @@
- [X] delete_table
- [ ] describe_backup
- [ ] describe_continuous_backups
+- [ ] describe_contributor_insights
- [ ] describe_endpoints
- [ ] describe_global_table
- [ ] describe_global_table_settings
- [ ] describe_limits
- [ ] describe_table
+- [ ] describe_table_replica_auto_scaling
- [ ] describe_time_to_live
- [X] get_item
- [ ] list_backups
+- [ ] list_contributor_insights
- [ ] list_global_tables
- [ ] list_tables
- [ ] list_tags_of_resource
@@ -2048,10 +2272,12 @@
- [ ] transact_write_items
- [ ] untag_resource
- [ ] update_continuous_backups
+- [ ] update_contributor_insights
- [ ] update_global_table
- [ ] update_global_table_settings
- [ ] update_item
- [ ] update_table
+- [ ] update_table_replica_auto_scaling
- [ ] update_time_to_live
## dynamodbstreams
@@ -2061,9 +2287,16 @@
- [X] get_shard_iterator
- [X] list_streams
+## ebs
+0% implemented
+- [ ] get_snapshot_block
+- [ ] list_changed_blocks
+- [ ] list_snapshot_blocks
+
## ec2
-28% implemented
+26% implemented
- [ ] accept_reserved_instances_exchange_quote
+- [ ] accept_transit_gateway_peering_attachment
- [ ] accept_transit_gateway_vpc_attachment
- [ ] accept_vpc_endpoint_connections
- [X] accept_vpc_peering_connection
@@ -2079,6 +2312,7 @@
- [ ] associate_iam_instance_profile
- [X] associate_route_table
- [ ] associate_subnet_cidr_block
+- [ ] associate_transit_gateway_multicast_domain
- [ ] associate_transit_gateway_route_table
- [X] associate_vpc_cidr_block
- [ ] attach_classic_link_vpc
@@ -2119,6 +2353,8 @@
- [X] create_key_pair
- [X] create_launch_template
- [ ] create_launch_template_version
+- [ ] create_local_gateway_route
+- [ ] create_local_gateway_route_table_vpc_association
- [X] create_nat_gateway
- [X] create_network_acl
- [X] create_network_acl_entry
@@ -2139,6 +2375,8 @@
- [ ] create_traffic_mirror_session
- [ ] create_traffic_mirror_target
- [ ] create_transit_gateway
+- [ ] create_transit_gateway_multicast_domain
+- [ ] create_transit_gateway_peering_attachment
- [ ] create_transit_gateway_route
- [ ] create_transit_gateway_route_table
- [ ] create_transit_gateway_vpc_attachment
@@ -2163,6 +2401,8 @@
- [X] delete_key_pair
- [ ] delete_launch_template
- [ ] delete_launch_template_versions
+- [ ] delete_local_gateway_route
+- [ ] delete_local_gateway_route_table_vpc_association
- [X] delete_nat_gateway
- [X] delete_network_acl
- [X] delete_network_acl_entry
@@ -2182,6 +2422,8 @@
- [ ] delete_traffic_mirror_session
- [ ] delete_traffic_mirror_target
- [ ] delete_transit_gateway
+- [ ] delete_transit_gateway_multicast_domain
+- [ ] delete_transit_gateway_peering_attachment
- [ ] delete_transit_gateway_route
- [ ] delete_transit_gateway_route_table
- [ ] delete_transit_gateway_vpc_attachment
@@ -2196,6 +2438,8 @@
- [X] delete_vpn_gateway
- [ ] deprovision_byoip_cidr
- [X] deregister_image
+- [ ] deregister_transit_gateway_multicast_group_members
+- [ ] deregister_transit_gateway_multicast_group_sources
- [ ] describe_account_attributes
- [X] describe_addresses
- [ ] describe_aggregate_id_format
@@ -2209,6 +2453,7 @@
- [ ] describe_client_vpn_endpoints
- [ ] describe_client_vpn_routes
- [ ] describe_client_vpn_target_networks
+- [ ] describe_coip_pools
- [ ] describe_conversion_tasks
- [ ] describe_customer_gateways
- [X] describe_dhcp_options
@@ -2216,6 +2461,7 @@
- [ ] describe_elastic_gpus
- [ ] describe_export_image_tasks
- [ ] describe_export_tasks
+- [ ] describe_fast_snapshot_restores
- [ ] describe_fleet_history
- [ ] describe_fleet_instances
- [ ] describe_fleets
@@ -2235,11 +2481,19 @@
- [X] describe_instance_attribute
- [ ] describe_instance_credit_specifications
- [ ] describe_instance_status
+- [ ] describe_instance_type_offerings
+- [ ] describe_instance_types
- [ ] describe_instances
- [X] describe_internet_gateways
- [X] describe_key_pairs
- [ ] describe_launch_template_versions
- [ ] describe_launch_templates
+- [ ] describe_local_gateway_route_table_virtual_interface_group_associations
+- [ ] describe_local_gateway_route_table_vpc_associations
+- [ ] describe_local_gateway_route_tables
+- [ ] describe_local_gateway_virtual_interface_groups
+- [ ] describe_local_gateway_virtual_interfaces
+- [ ] describe_local_gateways
- [ ] describe_moving_addresses
- [ ] describe_nat_gateways
- [ ] describe_network_acls
@@ -2275,6 +2529,8 @@
- [ ] describe_traffic_mirror_sessions
- [ ] describe_traffic_mirror_targets
- [ ] describe_transit_gateway_attachments
+- [ ] describe_transit_gateway_multicast_domains
+- [ ] describe_transit_gateway_peering_attachments
- [ ] describe_transit_gateway_route_tables
- [ ] describe_transit_gateway_vpc_attachments
- [ ] describe_transit_gateways
@@ -2283,8 +2539,8 @@
- [X] describe_volumes
- [ ] describe_volumes_modifications
- [X] describe_vpc_attribute
-- [X] describe_vpc_classic_link
-- [X] describe_vpc_classic_link_dns_support
+- [ ] describe_vpc_classic_link
+- [ ] describe_vpc_classic_link_dns_support
- [ ] describe_vpc_endpoint_connection_notifications
- [ ] describe_vpc_endpoint_connections
- [ ] describe_vpc_endpoint_service_configurations
@@ -2301,6 +2557,7 @@
- [X] detach_volume
- [X] detach_vpn_gateway
- [ ] disable_ebs_encryption_by_default
+- [ ] disable_fast_snapshot_restores
- [ ] disable_transit_gateway_route_table_propagation
- [ ] disable_vgw_route_propagation
- [X] disable_vpc_classic_link
@@ -2310,9 +2567,11 @@
- [ ] disassociate_iam_instance_profile
- [X] disassociate_route_table
- [ ] disassociate_subnet_cidr_block
+- [ ] disassociate_transit_gateway_multicast_domain
- [ ] disassociate_transit_gateway_route_table
- [X] disassociate_vpc_cidr_block
- [ ] enable_ebs_encryption_by_default
+- [ ] enable_fast_snapshot_restores
- [ ] enable_transit_gateway_route_table_propagation
- [ ] enable_vgw_route_propagation
- [ ] enable_volume_io
@@ -2323,8 +2582,10 @@
- [ ] export_image
- [ ] export_transit_gateway_routes
- [ ] get_capacity_reservation_usage
+- [ ] get_coip_pool_usage
- [ ] get_console_output
- [ ] get_console_screenshot
+- [ ] get_default_credit_specification
- [ ] get_ebs_default_kms_key_id
- [ ] get_ebs_encryption_by_default
- [ ] get_host_reservation_purchase_preview
@@ -2332,6 +2593,7 @@
- [ ] get_password_data
- [ ] get_reserved_instances_exchange_quote
- [ ] get_transit_gateway_attachment_propagations
+- [ ] get_transit_gateway_multicast_domain_associations
- [ ] get_transit_gateway_route_table_associations
- [ ] get_transit_gateway_route_table_propagations
- [ ] import_client_vpn_client_certificate_revocation_list
@@ -2342,6 +2604,7 @@
- [ ] import_volume
- [ ] modify_capacity_reservation
- [ ] modify_client_vpn_endpoint
+- [ ] modify_default_credit_specification
- [ ] modify_ebs_default_kms_key_id
- [ ] modify_fleet
- [ ] modify_fpga_image_attribute
@@ -2353,6 +2616,7 @@
- [ ] modify_instance_capacity_reservation_attributes
- [ ] modify_instance_credit_specification
- [ ] modify_instance_event_start_time
+- [ ] modify_instance_metadata_options
- [ ] modify_instance_placement
- [ ] modify_launch_template
- [X] modify_network_interface_attribute
@@ -2384,6 +2648,9 @@
- [ ] purchase_scheduled_instances
- [X] reboot_instances
- [ ] register_image
+- [ ] register_transit_gateway_multicast_group_members
+- [ ] register_transit_gateway_multicast_group_sources
+- [ ] reject_transit_gateway_peering_attachment
- [ ] reject_transit_gateway_vpc_attachment
- [ ] reject_vpc_endpoint_connections
- [X] reject_vpc_peering_connection
@@ -2410,6 +2677,8 @@
- [X] revoke_security_group_ingress
- [ ] run_instances
- [ ] run_scheduled_instances
+- [ ] search_local_gateway_routes
+- [ ] search_transit_gateway_multicast_groups
- [ ] search_transit_gateway_routes
- [ ] send_diagnostic_interrupt
- [X] start_instances
@@ -2425,7 +2694,7 @@
## ec2-instance-connect
0% implemented
-- [ ] send_ssh_public_key
+- [x] send_ssh_public_key
## ecr
27% implemented
@@ -2460,7 +2729,8 @@
- [ ] upload_layer_part
## ecs
-66% implemented
+62% implemented
+- [ ] create_capacity_provider
- [X] create_cluster
- [X] create_service
- [ ] create_task_set
@@ -2471,6 +2741,7 @@
- [ ] delete_task_set
- [X] deregister_container_instance
- [X] deregister_task_definition
+- [ ] describe_capacity_providers
- [X] describe_clusters
- [X] describe_container_instances
- [X] describe_services
@@ -2490,6 +2761,7 @@
- [ ] put_account_setting
- [ ] put_account_setting_default
- [X] put_attributes
+- [ ] put_cluster_capacity_providers
- [X] register_container_instance
- [X] register_task_definition
- [X] run_task
@@ -2527,13 +2799,17 @@
## eks
0% implemented
- [ ] create_cluster
+- [ ] create_fargate_profile
- [ ] create_nodegroup
- [ ] delete_cluster
+- [ ] delete_fargate_profile
- [ ] delete_nodegroup
- [ ] describe_cluster
+- [ ] describe_fargate_profile
- [ ] describe_nodegroup
- [ ] describe_update
- [ ] list_clusters
+- [ ] list_fargate_profiles
- [ ] list_nodegroups
- [ ] list_tags_for_resource
- [ ] list_updates
@@ -2544,6 +2820,12 @@
- [ ] update_nodegroup_config
- [ ] update_nodegroup_version
+## elastic-inference
+0% implemented
+- [ ] list_tags_for_resource
+- [ ] tag_resource
+- [ ] untag_resource
+
## elasticache
0% implemented
- [ ] add_tags_to_resource
@@ -2732,7 +3014,7 @@
- [X] set_subnets
## emr
-51% implemented
+50% implemented
- [ ] add_instance_fleet
- [X] add_instance_groups
- [X] add_job_flow_steps
@@ -2752,6 +3034,7 @@
- [ ] list_instances
- [ ] list_security_configurations
- [X] list_steps
+- [ ] modify_cluster
- [ ] modify_instance_fleet
- [X] modify_instance_groups
- [ ] put_auto_scaling_policy
@@ -2888,6 +3171,39 @@
0% implemented
- [ ] query_forecast
+## frauddetector
+0% implemented
+- [ ] batch_create_variable
+- [ ] batch_get_variable
+- [ ] create_detector_version
+- [ ] create_model_version
+- [ ] create_rule
+- [ ] create_variable
+- [ ] delete_detector_version
+- [ ] delete_event
+- [ ] describe_detector
+- [ ] describe_model_versions
+- [ ] get_detector_version
+- [ ] get_detectors
+- [ ] get_external_models
+- [ ] get_model_version
+- [ ] get_models
+- [ ] get_outcomes
+- [ ] get_prediction
+- [ ] get_rules
+- [ ] get_variables
+- [ ] put_detector
+- [ ] put_external_model
+- [ ] put_model
+- [ ] put_outcome
+- [ ] update_detector_version
+- [ ] update_detector_version_metadata
+- [ ] update_detector_version_status
+- [ ] update_model_version
+- [ ] update_rule_metadata
+- [ ] update_rule_version
+- [ ] update_variable
+
## fsx
0% implemented
- [ ] create_backup
@@ -3344,7 +3660,7 @@
- [ ] describe_events
## iam
-65% implemented
+67% implemented
- [ ] add_client_id_to_open_id_connect_provider
- [X] add_role_to_instance_profile
- [X] add_user_to_group
@@ -3385,7 +3701,7 @@
- [ ] delete_service_linked_role
- [ ] delete_service_specific_credential
- [X] delete_signing_certificate
-- [ ] delete_ssh_public_key
+- [X] delete_ssh_public_key
- [X] delete_user
- [ ] delete_user_permissions_boundary
- [X] delete_user_policy
@@ -3419,7 +3735,7 @@
- [ ] get_service_last_accessed_details
- [ ] get_service_last_accessed_details_with_entities
- [ ] get_service_linked_role_deletion_status
-- [ ] get_ssh_public_key
+- [X] get_ssh_public_key
- [X] get_user
- [X] get_user_policy
- [ ] list_access_keys
@@ -3447,7 +3763,7 @@
- [X] list_signing_certificates
- [ ] list_ssh_public_keys
- [X] list_user_policies
-- [ ] list_user_tags
+- [X] list_user_tags
- [X] list_users
- [X] list_virtual_mfa_devices
- [X] put_group_policy
@@ -3480,11 +3796,56 @@
- [ ] update_server_certificate
- [ ] update_service_specific_credential
- [X] update_signing_certificate
-- [ ] update_ssh_public_key
+- [X] update_ssh_public_key
- [X] update_user
- [X] upload_server_certificate
- [X] upload_signing_certificate
-- [ ] upload_ssh_public_key
+- [X] upload_ssh_public_key
+
+## imagebuilder
+0% implemented
+- [ ] cancel_image_creation
+- [ ] create_component
+- [ ] create_distribution_configuration
+- [ ] create_image
+- [ ] create_image_pipeline
+- [ ] create_image_recipe
+- [ ] create_infrastructure_configuration
+- [ ] delete_component
+- [ ] delete_distribution_configuration
+- [ ] delete_image
+- [ ] delete_image_pipeline
+- [ ] delete_image_recipe
+- [ ] delete_infrastructure_configuration
+- [ ] get_component
+- [ ] get_component_policy
+- [ ] get_distribution_configuration
+- [ ] get_image
+- [ ] get_image_pipeline
+- [ ] get_image_policy
+- [ ] get_image_recipe
+- [ ] get_image_recipe_policy
+- [ ] get_infrastructure_configuration
+- [ ] import_component
+- [ ] list_component_build_versions
+- [ ] list_components
+- [ ] list_distribution_configurations
+- [ ] list_image_build_versions
+- [ ] list_image_pipeline_images
+- [ ] list_image_pipelines
+- [ ] list_image_recipes
+- [ ] list_images
+- [ ] list_infrastructure_configurations
+- [ ] list_tags_for_resource
+- [ ] put_component_policy
+- [ ] put_image_policy
+- [ ] put_image_recipe_policy
+- [ ] start_image_pipeline_execution
+- [ ] tag_resource
+- [ ] untag_resource
+- [ ] update_distribution_configuration
+- [ ] update_image_pipeline
+- [ ] update_infrastructure_configuration
## importexport
0% implemented
@@ -3536,7 +3897,7 @@
- [ ] update_assessment_target
## iot
-22% implemented
+20% implemented
- [ ] accept_certificate_transfer
- [ ] add_thing_to_billing_group
- [X] add_thing_to_thing_group
@@ -3551,9 +3912,11 @@
- [ ] cancel_job
- [ ] cancel_job_execution
- [ ] clear_default_authorizer
+- [ ] confirm_topic_rule_destination
- [ ] create_authorizer
- [ ] create_billing_group
- [ ] create_certificate_from_csr
+- [ ] create_domain_configuration
- [ ] create_dynamic_thing_group
- [X] create_job
- [X] create_keys_and_certificate
@@ -3561,6 +3924,9 @@
- [ ] create_ota_update
- [X] create_policy
- [ ] create_policy_version
+- [ ] create_provisioning_claim
+- [ ] create_provisioning_template
+- [ ] create_provisioning_template_version
- [ ] create_role_alias
- [ ] create_scheduled_audit
- [ ] create_security_profile
@@ -3569,11 +3935,13 @@
- [X] create_thing_group
- [X] create_thing_type
- [ ] create_topic_rule
+- [ ] create_topic_rule_destination
- [ ] delete_account_audit_configuration
- [ ] delete_authorizer
- [ ] delete_billing_group
- [ ] delete_ca_certificate
- [X] delete_certificate
+- [ ] delete_domain_configuration
- [ ] delete_dynamic_thing_group
- [ ] delete_job
- [ ] delete_job_execution
@@ -3581,6 +3949,8 @@
- [ ] delete_ota_update
- [X] delete_policy
- [ ] delete_policy_version
+- [ ] delete_provisioning_template
+- [ ] delete_provisioning_template_version
- [ ] delete_registration_code
- [ ] delete_role_alias
- [ ] delete_scheduled_audit
@@ -3590,6 +3960,7 @@
- [X] delete_thing_group
- [X] delete_thing_type
- [ ] delete_topic_rule
+- [ ] delete_topic_rule_destination
- [ ] delete_v2_logging_level
- [ ] deprecate_thing_type
- [ ] describe_account_audit_configuration
@@ -3601,12 +3972,15 @@
- [ ] describe_ca_certificate
- [X] describe_certificate
- [ ] describe_default_authorizer
+- [ ] describe_domain_configuration
- [ ] describe_endpoint
- [ ] describe_event_configurations
- [ ] describe_index
- [X] describe_job
- [ ] describe_job_execution
- [ ] describe_mitigation_action
+- [ ] describe_provisioning_template
+- [ ] describe_provisioning_template_version
- [ ] describe_role_alias
- [ ] describe_scheduled_audit
- [ ] describe_security_profile
@@ -3633,6 +4007,7 @@
- [ ] get_registration_code
- [ ] get_statistics
- [ ] get_topic_rule
+- [ ] get_topic_rule_destination
- [ ] get_v2_logging_options
- [ ] list_active_violations
- [ ] list_attached_policies
@@ -3645,6 +4020,7 @@
- [ ] list_ca_certificates
- [X] list_certificates
- [ ] list_certificates_by_ca
+- [ ] list_domain_configurations
- [ ] list_indices
- [ ] list_job_executions_for_job
- [ ] list_job_executions_for_thing
@@ -3657,6 +4033,8 @@
- [ ] list_policy_versions
- [X] list_principal_policies
- [X] list_principal_things
+- [ ] list_provisioning_template_versions
+- [ ] list_provisioning_templates
- [ ] list_role_aliases
- [ ] list_scheduled_audits
- [ ] list_security_profiles
@@ -3674,6 +4052,7 @@
- [X] list_things
- [ ] list_things_in_billing_group
- [X] list_things_in_thing_group
+- [ ] list_topic_rule_destinations
- [ ] list_topic_rules
- [ ] list_v2_logging_levels
- [ ] list_violation_events
@@ -3704,11 +4083,13 @@
- [ ] update_billing_group
- [ ] update_ca_certificate
- [X] update_certificate
+- [ ] update_domain_configuration
- [ ] update_dynamic_thing_group
- [ ] update_event_configurations
- [ ] update_indexing_configuration
- [ ] update_job
- [ ] update_mitigation_action
+- [ ] update_provisioning_template
- [ ] update_role_alias
- [ ] update_scheduled_audit
- [ ] update_security_profile
@@ -3716,6 +4097,7 @@
- [X] update_thing
- [X] update_thing_group
- [X] update_thing_groups_for_thing
+- [ ] update_topic_rule_destination
- [ ] validate_security_profile_behaviors
## iot-data
@@ -3830,6 +4212,16 @@
- [ ] describe_detector
- [ ] list_detectors
+## iotsecuretunneling
+0% implemented
+- [ ] close_tunnel
+- [ ] describe_tunnel
+- [ ] list_tags_for_resource
+- [ ] list_tunnels
+- [ ] open_tunnel
+- [ ] tag_resource
+- [ ] untag_resource
+
## iotthingsgraph
0% implemented
- [ ] associate_entity_to_thing
@@ -3889,6 +4281,30 @@
- [ ] update_broker_count
- [ ] update_broker_storage
- [ ] update_cluster_configuration
+- [ ] update_monitoring
+
+## kendra
+0% implemented
+- [ ] batch_delete_document
+- [ ] batch_put_document
+- [ ] create_data_source
+- [ ] create_faq
+- [ ] create_index
+- [ ] delete_faq
+- [ ] delete_index
+- [ ] describe_data_source
+- [ ] describe_faq
+- [ ] describe_index
+- [ ] list_data_source_sync_jobs
+- [ ] list_data_sources
+- [ ] list_faqs
+- [ ] list_indices
+- [ ] query
+- [ ] start_data_source_sync_job
+- [ ] stop_data_source_sync_job
+- [ ] submit_feedback
+- [ ] update_data_source
+- [ ] update_index
## kinesis
50% implemented
@@ -3932,6 +4348,11 @@
0% implemented
- [ ] get_media
+## kinesis-video-signaling
+0% implemented
+- [ ] get_ice_server_config
+- [ ] send_alexa_offer_to_master
+
## kinesisanalytics
0% implemented
- [ ] add_application_cloud_watch_logging_option
@@ -3962,6 +4383,7 @@
- [ ] add_application_input_processing_configuration
- [ ] add_application_output
- [ ] add_application_reference_data_source
+- [ ] add_application_vpc_configuration
- [ ] create_application
- [ ] create_application_snapshot
- [ ] delete_application
@@ -3970,6 +4392,7 @@
- [ ] delete_application_output
- [ ] delete_application_reference_data_source
- [ ] delete_application_snapshot
+- [ ] delete_application_vpc_configuration
- [ ] describe_application
- [ ] describe_application_snapshot
- [ ] discover_input_schema
@@ -3984,19 +4407,28 @@
## kinesisvideo
0% implemented
+- [ ] create_signaling_channel
- [ ] create_stream
+- [ ] delete_signaling_channel
- [ ] delete_stream
+- [ ] describe_signaling_channel
- [ ] describe_stream
- [ ] get_data_endpoint
+- [ ] get_signaling_channel_endpoint
+- [ ] list_signaling_channels
- [ ] list_streams
+- [ ] list_tags_for_resource
- [ ] list_tags_for_stream
+- [ ] tag_resource
- [ ] tag_stream
+- [ ] untag_resource
- [ ] untag_stream
- [ ] update_data_retention
+- [ ] update_signaling_channel
- [ ] update_stream
## kms
-48% implemented
+43% implemented
- [X] cancel_key_deletion
- [ ] connect_custom_key_store
- [ ] create_alias
@@ -4016,11 +4448,14 @@
- [X] enable_key_rotation
- [X] encrypt
- [X] generate_data_key
+- [ ] generate_data_key_pair
+- [ ] generate_data_key_pair_without_plaintext
- [ ] generate_data_key_without_plaintext
- [ ] generate_random
- [X] get_key_policy
- [X] get_key_rotation_status
- [ ] get_parameters_for_import
+- [ ] get_public_key
- [ ] import_key_material
- [ ] list_aliases
- [ ] list_grants
@@ -4033,11 +4468,13 @@
- [ ] retire_grant
- [ ] revoke_grant
- [X] schedule_key_deletion
+- [ ] sign
- [X] tag_resource
- [ ] untag_resource
- [ ] update_alias
- [ ] update_custom_key_store
- [X] update_key_description
+- [ ] verify
## lakeformation
0% implemented
@@ -4056,7 +4493,7 @@
- [ ] update_resource
## lambda
-41% implemented
+32% implemented
- [ ] add_layer_version_permission
- [ ] add_permission
- [ ] create_alias
@@ -4066,28 +4503,37 @@
- [X] delete_event_source_mapping
- [X] delete_function
- [ ] delete_function_concurrency
+- [ ] delete_function_event_invoke_config
- [ ] delete_layer_version
+- [ ] delete_provisioned_concurrency_config
- [ ] get_account_settings
- [ ] get_alias
- [X] get_event_source_mapping
- [X] get_function
+- [ ] get_function_concurrency
- [ ] get_function_configuration
+- [ ] get_function_event_invoke_config
- [ ] get_layer_version
- [ ] get_layer_version_by_arn
- [ ] get_layer_version_policy
- [ ] get_policy
+- [ ] get_provisioned_concurrency_config
- [X] invoke
- [ ] invoke_async
- [ ] list_aliases
- [X] list_event_source_mappings
+- [ ] list_function_event_invoke_configs
- [X] list_functions
- [ ] list_layer_versions
- [ ] list_layers
+- [ ] list_provisioned_concurrency_configs
- [X] list_tags
- [X] list_versions_by_function
- [ ] publish_layer_version
- [ ] publish_version
- [ ] put_function_concurrency
+- [ ] put_function_event_invoke_config
+- [ ] put_provisioned_concurrency_config
- [ ] remove_layer_version_permission
- [ ] remove_permission
- [X] tag_resource
@@ -4096,6 +4542,7 @@
- [X] update_event_source_mapping
- [X] update_function_code
- [X] update_function_configuration
+- [ ] update_function_event_invoke_config
## lex-models
0% implemented
@@ -4151,6 +4598,7 @@
- [ ] get_license_configuration
- [ ] get_service_settings
- [ ] list_associations_for_license_configuration
+- [ ] list_failures_for_license_configuration_operations
- [ ] list_license_configurations
- [ ] list_license_specifications_for_resource
- [ ] list_resource_inventory
@@ -4446,32 +4894,44 @@
- [ ] create_channel
- [ ] create_input
- [ ] create_input_security_group
+- [ ] create_multiplex
+- [ ] create_multiplex_program
- [ ] create_tags
- [ ] delete_channel
- [ ] delete_input
- [ ] delete_input_security_group
+- [ ] delete_multiplex
+- [ ] delete_multiplex_program
- [ ] delete_reservation
- [ ] delete_schedule
- [ ] delete_tags
- [ ] describe_channel
- [ ] describe_input
- [ ] describe_input_security_group
+- [ ] describe_multiplex
+- [ ] describe_multiplex_program
- [ ] describe_offering
- [ ] describe_reservation
- [ ] describe_schedule
- [ ] list_channels
- [ ] list_input_security_groups
- [ ] list_inputs
+- [ ] list_multiplex_programs
+- [ ] list_multiplexes
- [ ] list_offerings
- [ ] list_reservations
- [ ] list_tags_for_resource
- [ ] purchase_offering
- [ ] start_channel
+- [ ] start_multiplex
- [ ] stop_channel
+- [ ] stop_multiplex
- [ ] update_channel
- [ ] update_channel_class
- [ ] update_input
- [ ] update_input_security_group
+- [ ] update_multiplex
+- [ ] update_multiplex_program
- [ ] update_reservation
## mediapackage
@@ -4575,6 +5035,12 @@
- [ ] notify_migration_task_state
- [ ] put_resource_attributes
+## migrationhub-config
+0% implemented
+- [ ] create_home_region_control
+- [ ] describe_home_region_controls
+- [ ] get_home_region
+
## mobile
0% implemented
- [ ] create_project
@@ -4714,6 +5180,37 @@
- [ ] restore_db_cluster_from_snapshot
- [ ] restore_db_cluster_to_point_in_time
+## networkmanager
+0% implemented
+- [ ] associate_customer_gateway
+- [ ] associate_link
+- [ ] create_device
+- [ ] create_global_network
+- [ ] create_link
+- [ ] create_site
+- [ ] delete_device
+- [ ] delete_global_network
+- [ ] delete_link
+- [ ] delete_site
+- [ ] deregister_transit_gateway
+- [ ] describe_global_networks
+- [ ] disassociate_customer_gateway
+- [ ] disassociate_link
+- [ ] get_customer_gateway_associations
+- [ ] get_devices
+- [ ] get_link_associations
+- [ ] get_links
+- [ ] get_sites
+- [ ] get_transit_gateway_registrations
+- [ ] list_tags_for_resource
+- [ ] register_transit_gateway
+- [ ] tag_resource
+- [ ] untag_resource
+- [ ] update_device
+- [ ] update_global_network
+- [ ] update_link
+- [ ] update_site
+
## opsworks
12% implemented
- [ ] assign_instance
@@ -4811,7 +5308,7 @@
- [ ] update_server_engine_attributes
## organizations
-43% implemented
+48% implemented
- [ ] accept_handshake
- [X] attach_policy
- [ ] cancel_handshake
@@ -4826,6 +5323,7 @@
- [ ] delete_policy
- [X] describe_account
- [X] describe_create_account_status
+- [ ] describe_effective_policy
- [ ] describe_handshake
- [X] describe_organization
- [X] describe_organizational_unit
@@ -4850,15 +5348,23 @@
- [X] list_policies
- [X] list_policies_for_target
- [X] list_roots
-- [x] list_tags_for_resource
+- [X] list_tags_for_resource
- [X] list_targets_for_policy
- [X] move_account
- [ ] remove_account_from_organization
-- [x] tag_resource
-- [x] untag_resource
+- [X] tag_resource
+- [X] untag_resource
- [ ] update_organizational_unit
- [ ] update_policy
+## outposts
+0% implemented
+- [ ] create_outpost
+- [ ] get_outpost
+- [ ] get_outpost_instance_types
+- [ ] list_outposts
+- [ ] list_sites
+
## personalize
0% implemented
- [ ] create_batch_inference_job
@@ -4926,6 +5432,7 @@
- [ ] create_push_template
- [ ] create_segment
- [ ] create_sms_template
+- [ ] create_voice_template
- [ ] delete_adm_channel
- [ ] delete_apns_channel
- [ ] delete_apns_sandbox_channel
@@ -4946,6 +5453,7 @@
- [ ] delete_sms_template
- [ ] delete_user_endpoints
- [ ] delete_voice_channel
+- [ ] delete_voice_template
- [ ] get_adm_channel
- [ ] get_apns_channel
- [ ] get_apns_sandbox_channel
@@ -4987,6 +5495,7 @@
- [ ] get_sms_template
- [ ] get_user_endpoints
- [ ] get_voice_channel
+- [ ] get_voice_template
- [ ] list_journeys
- [ ] list_tags_for_resource
- [ ] list_templates
@@ -5018,6 +5527,7 @@
- [ ] update_sms_channel
- [ ] update_sms_template
- [ ] update_voice_channel
+- [ ] update_voice_template
## pinpoint-email
0% implemented
@@ -5116,38 +5626,93 @@
## quicksight
0% implemented
+- [ ] cancel_ingestion
+- [ ] create_dashboard
+- [ ] create_data_set
+- [ ] create_data_source
- [ ] create_group
- [ ] create_group_membership
+- [ ] create_iam_policy_assignment
+- [ ] create_ingestion
+- [ ] create_template
+- [ ] create_template_alias
+- [ ] delete_dashboard
+- [ ] delete_data_set
+- [ ] delete_data_source
- [ ] delete_group
- [ ] delete_group_membership
+- [ ] delete_iam_policy_assignment
+- [ ] delete_template
+- [ ] delete_template_alias
- [ ] delete_user
- [ ] delete_user_by_principal_id
+- [ ] describe_dashboard
+- [ ] describe_dashboard_permissions
+- [ ] describe_data_set
+- [ ] describe_data_set_permissions
+- [ ] describe_data_source
+- [ ] describe_data_source_permissions
- [ ] describe_group
+- [ ] describe_iam_policy_assignment
+- [ ] describe_ingestion
+- [ ] describe_template
+- [ ] describe_template_alias
+- [ ] describe_template_permissions
- [ ] describe_user
- [ ] get_dashboard_embed_url
+- [ ] list_dashboard_versions
+- [ ] list_dashboards
+- [ ] list_data_sets
+- [ ] list_data_sources
- [ ] list_group_memberships
- [ ] list_groups
+- [ ] list_iam_policy_assignments
+- [ ] list_iam_policy_assignments_for_user
+- [ ] list_ingestions
+- [ ] list_tags_for_resource
+- [ ] list_template_aliases
+- [ ] list_template_versions
+- [ ] list_templates
- [ ] list_user_groups
- [ ] list_users
- [ ] register_user
+- [ ] tag_resource
+- [ ] untag_resource
+- [ ] update_dashboard
+- [ ] update_dashboard_permissions
+- [ ] update_dashboard_published_version
+- [ ] update_data_set
+- [ ] update_data_set_permissions
+- [ ] update_data_source
+- [ ] update_data_source_permissions
- [ ] update_group
+- [ ] update_iam_policy_assignment
+- [ ] update_template
+- [ ] update_template_alias
+- [ ] update_template_permissions
- [ ] update_user
## ram
0% implemented
- [ ] accept_resource_share_invitation
- [ ] associate_resource_share
+- [ ] associate_resource_share_permission
- [ ] create_resource_share
- [ ] delete_resource_share
- [ ] disassociate_resource_share
+- [ ] disassociate_resource_share_permission
- [ ] enable_sharing_with_aws_organization
+- [ ] get_permission
- [ ] get_resource_policies
- [ ] get_resource_share_associations
- [ ] get_resource_share_invitations
- [ ] get_resource_shares
- [ ] list_pending_invitation_resources
+- [ ] list_permissions
- [ ] list_principals
+- [ ] list_resource_share_permissions
- [ ] list_resources
+- [ ] promote_resource_share_created_from_policy
- [ ] reject_resource_share_invitation
- [ ] tag_resource
- [ ] untag_resource
@@ -5175,6 +5740,7 @@
- [ ] create_db_instance
- [ ] create_db_instance_read_replica
- [ ] create_db_parameter_group
+- [ ] create_db_proxy
- [ ] create_db_security_group
- [ ] create_db_snapshot
- [ ] create_db_subnet_group
@@ -5189,6 +5755,7 @@
- [ ] delete_db_instance
- [ ] delete_db_instance_automated_backup
- [ ] delete_db_parameter_group
+- [ ] delete_db_proxy
- [ ] delete_db_security_group
- [ ] delete_db_snapshot
- [ ] delete_db_subnet_group
@@ -5196,6 +5763,7 @@
- [ ] delete_global_cluster
- [ ] delete_installation_media
- [ ] delete_option_group
+- [ ] deregister_db_proxy_targets
- [ ] describe_account_attributes
- [ ] describe_certificates
- [ ] describe_custom_availability_zones
@@ -5212,6 +5780,9 @@
- [ ] describe_db_log_files
- [ ] describe_db_parameter_groups
- [ ] describe_db_parameters
+- [ ] describe_db_proxies
+- [ ] describe_db_proxy_target_groups
+- [ ] describe_db_proxy_targets
- [ ] describe_db_security_groups
- [ ] describe_db_snapshot_attributes
- [ ] describe_db_snapshots
@@ -5242,6 +5813,8 @@
- [ ] modify_db_cluster_snapshot_attribute
- [ ] modify_db_instance
- [ ] modify_db_parameter_group
+- [ ] modify_db_proxy
+- [ ] modify_db_proxy_target_group
- [ ] modify_db_snapshot
- [ ] modify_db_snapshot_attribute
- [ ] modify_db_subnet_group
@@ -5252,6 +5825,7 @@
- [ ] promote_read_replica_db_cluster
- [ ] purchase_reserved_db_instances_offering
- [ ] reboot_db_instance
+- [ ] register_db_proxy_targets
- [ ] remove_from_global_cluster
- [ ] remove_role_from_db_cluster
- [ ] remove_role_from_db_instance
@@ -5283,7 +5857,7 @@
- [ ] rollback_transaction
## redshift
-31% implemented
+30% implemented
- [ ] accept_reserved_node_exchange
- [ ] authorize_cluster_security_group_ingress
- [ ] authorize_snapshot_access
@@ -5299,6 +5873,7 @@
- [ ] create_event_subscription
- [ ] create_hsm_client_certificate
- [ ] create_hsm_configuration
+- [ ] create_scheduled_action
- [X] create_snapshot_copy_grant
- [ ] create_snapshot_schedule
- [X] create_tags
@@ -5310,6 +5885,7 @@
- [ ] delete_event_subscription
- [ ] delete_hsm_client_certificate
- [ ] delete_hsm_configuration
+- [ ] delete_scheduled_action
- [X] delete_snapshot_copy_grant
- [ ] delete_snapshot_schedule
- [X] delete_tags
@@ -5335,6 +5911,7 @@
- [ ] describe_reserved_node_offerings
- [ ] describe_reserved_nodes
- [ ] describe_resize
+- [ ] describe_scheduled_actions
- [X] describe_snapshot_copy_grants
- [ ] describe_snapshot_schedules
- [ ] describe_storage
@@ -5355,6 +5932,7 @@
- [ ] modify_cluster_snapshot_schedule
- [ ] modify_cluster_subnet_group
- [ ] modify_event_subscription
+- [ ] modify_scheduled_action
- [X] modify_snapshot_copy_retention_period
- [ ] modify_snapshot_schedule
- [ ] purchase_reserved_node_offering
@@ -5371,12 +5949,17 @@
0% implemented
- [ ] compare_faces
- [ ] create_collection
+- [ ] create_project
+- [ ] create_project_version
- [ ] create_stream_processor
- [ ] delete_collection
- [ ] delete_faces
- [ ] delete_stream_processor
- [ ] describe_collection
+- [ ] describe_project_versions
+- [ ] describe_projects
- [ ] describe_stream_processor
+- [ ] detect_custom_labels
- [ ] detect_faces
- [ ] detect_labels
- [ ] detect_moderation_labels
@@ -5401,7 +5984,9 @@
- [ ] start_face_search
- [ ] start_label_detection
- [ ] start_person_tracking
+- [ ] start_project_version
- [ ] start_stream_processor
+- [ ] stop_project_version
- [ ] stop_stream_processor
## resource-groups
@@ -5420,10 +6005,13 @@
- [X] update_group_query
## resourcegroupstaggingapi
-60% implemented
+37% implemented
+- [ ] describe_report_creation
+- [ ] get_compliance_summary
- [X] get_resources
- [X] get_tag_keys
- [X] get_tag_values
+- [ ] start_report_creation
- [ ] tag_resources
- [ ] untag_resources
@@ -5589,63 +6177,63 @@
- [X] delete_bucket_cors
- [ ] delete_bucket_encryption
- [ ] delete_bucket_inventory_configuration
-- [ ] delete_bucket_lifecycle
+- [X] delete_bucket_lifecycle
- [ ] delete_bucket_metrics_configuration
- [X] delete_bucket_policy
- [ ] delete_bucket_replication
- [X] delete_bucket_tagging
- [ ] delete_bucket_website
-- [ ] delete_object
+- [X] delete_object
- [ ] delete_object_tagging
-- [ ] delete_objects
-- [ ] delete_public_access_block
+- [X] delete_objects
+- [X] delete_public_access_block
- [ ] get_bucket_accelerate_configuration
- [X] get_bucket_acl
- [ ] get_bucket_analytics_configuration
-- [ ] get_bucket_cors
+- [X] get_bucket_cors
- [ ] get_bucket_encryption
- [ ] get_bucket_inventory_configuration
-- [ ] get_bucket_lifecycle
-- [ ] get_bucket_lifecycle_configuration
-- [ ] get_bucket_location
-- [ ] get_bucket_logging
+- [X] get_bucket_lifecycle
+- [X] get_bucket_lifecycle_configuration
+- [X] get_bucket_location
+- [X] get_bucket_logging
- [ ] get_bucket_metrics_configuration
- [ ] get_bucket_notification
- [ ] get_bucket_notification_configuration
- [X] get_bucket_policy
-- [ ] get_bucket_policy_status
+- [X] get_bucket_policy_status
- [ ] get_bucket_replication
- [ ] get_bucket_request_payment
-- [ ] get_bucket_tagging
+- [X] get_bucket_tagging
- [X] get_bucket_versioning
- [ ] get_bucket_website
-- [ ] get_object
-- [ ] get_object_acl
+- [X] get_object
+- [X] get_object_acl
- [ ] get_object_legal_hold
- [ ] get_object_lock_configuration
- [ ] get_object_retention
- [ ] get_object_tagging
- [ ] get_object_torrent
-- [ ] get_public_access_block
+- [X] get_public_access_block
- [ ] head_bucket
- [ ] head_object
- [ ] list_bucket_analytics_configurations
- [ ] list_bucket_inventory_configurations
- [ ] list_bucket_metrics_configurations
-- [ ] list_buckets
-- [ ] list_multipart_uploads
+- [X] list_buckets
+- [X] list_multipart_uploads
- [ ] list_object_versions
-- [ ] list_objects
-- [ ] list_objects_v2
+- [X] list_objects
+- [X] list_objects_v2
- [ ] list_parts
- [X] put_bucket_accelerate_configuration
-- [ ] put_bucket_acl
+- [X] put_bucket_acl
- [ ] put_bucket_analytics_configuration
- [X] put_bucket_cors
- [ ] put_bucket_encryption
- [ ] put_bucket_inventory_configuration
-- [ ] put_bucket_lifecycle
-- [ ] put_bucket_lifecycle_configuration
+- [X] put_bucket_lifecycle
+- [X] put_bucket_lifecycle_configuration
- [X] put_bucket_logging
- [ ] put_bucket_metrics_configuration
- [ ] put_bucket_notification
@@ -5654,15 +6242,15 @@
- [ ] put_bucket_replication
- [ ] put_bucket_request_payment
- [X] put_bucket_tagging
-- [ ] put_bucket_versioning
+- [X] put_bucket_versioning
- [ ] put_bucket_website
-- [ ] put_object
+- [X] put_object
- [ ] put_object_acl
- [ ] put_object_legal_hold
- [ ] put_object_lock_configuration
- [ ] put_object_retention
- [ ] put_object_tagging
-- [ ] put_public_access_block
+- [X] put_public_access_block
- [ ] restore_object
- [ ] select_object_content
- [ ] upload_part
@@ -5670,11 +6258,19 @@
## s3control
0% implemented
+- [ ] create_access_point
- [ ] create_job
+- [ ] delete_access_point
+- [ ] delete_access_point_policy
- [ ] delete_public_access_block
- [ ] describe_job
+- [ ] get_access_point
+- [ ] get_access_point_policy
+- [ ] get_access_point_policy_status
- [ ] get_public_access_block
+- [ ] list_access_points
- [ ] list_jobs
+- [ ] put_access_point_policy
- [ ] put_public_access_block
- [ ] update_job_priority
- [ ] update_job_status
@@ -5682,81 +6278,145 @@
## sagemaker
0% implemented
- [ ] add_tags
+- [ ] associate_trial_component
- [ ] create_algorithm
+- [ ] create_app
+- [ ] create_auto_ml_job
- [ ] create_code_repository
- [ ] create_compilation_job
+- [ ] create_domain
- [ ] create_endpoint
- [ ] create_endpoint_config
+- [ ] create_experiment
+- [ ] create_flow_definition
+- [ ] create_human_task_ui
- [ ] create_hyper_parameter_tuning_job
- [ ] create_labeling_job
- [ ] create_model
- [ ] create_model_package
+- [ ] create_monitoring_schedule
- [ ] create_notebook_instance
- [ ] create_notebook_instance_lifecycle_config
+- [ ] create_presigned_domain_url
- [ ] create_presigned_notebook_instance_url
+- [ ] create_processing_job
- [ ] create_training_job
- [ ] create_transform_job
+- [ ] create_trial
+- [ ] create_trial_component
+- [ ] create_user_profile
- [ ] create_workteam
- [ ] delete_algorithm
+- [ ] delete_app
- [ ] delete_code_repository
+- [ ] delete_domain
- [ ] delete_endpoint
- [ ] delete_endpoint_config
+- [ ] delete_experiment
+- [ ] delete_flow_definition
- [ ] delete_model
- [ ] delete_model_package
+- [ ] delete_monitoring_schedule
- [ ] delete_notebook_instance
- [ ] delete_notebook_instance_lifecycle_config
- [ ] delete_tags
+- [ ] delete_trial
+- [ ] delete_trial_component
+- [ ] delete_user_profile
- [ ] delete_workteam
- [ ] describe_algorithm
+- [ ] describe_app
+- [ ] describe_auto_ml_job
- [ ] describe_code_repository
- [ ] describe_compilation_job
+- [ ] describe_domain
- [ ] describe_endpoint
- [ ] describe_endpoint_config
+- [ ] describe_experiment
+- [ ] describe_flow_definition
+- [ ] describe_human_task_ui
- [ ] describe_hyper_parameter_tuning_job
- [ ] describe_labeling_job
- [ ] describe_model
- [ ] describe_model_package
+- [ ] describe_monitoring_schedule
- [ ] describe_notebook_instance
- [ ] describe_notebook_instance_lifecycle_config
+- [ ] describe_processing_job
- [ ] describe_subscribed_workteam
- [ ] describe_training_job
- [ ] describe_transform_job
+- [ ] describe_trial
+- [ ] describe_trial_component
+- [ ] describe_user_profile
- [ ] describe_workteam
+- [ ] disassociate_trial_component
- [ ] get_search_suggestions
- [ ] list_algorithms
+- [ ] list_apps
+- [ ] list_auto_ml_jobs
+- [ ] list_candidates_for_auto_ml_job
- [ ] list_code_repositories
- [ ] list_compilation_jobs
+- [ ] list_domains
- [ ] list_endpoint_configs
- [ ] list_endpoints
+- [ ] list_experiments
+- [ ] list_flow_definitions
+- [ ] list_human_task_uis
- [ ] list_hyper_parameter_tuning_jobs
- [ ] list_labeling_jobs
- [ ] list_labeling_jobs_for_workteam
- [ ] list_model_packages
- [ ] list_models
+- [ ] list_monitoring_executions
+- [ ] list_monitoring_schedules
- [ ] list_notebook_instance_lifecycle_configs
- [ ] list_notebook_instances
+- [ ] list_processing_jobs
- [ ] list_subscribed_workteams
- [ ] list_tags
- [ ] list_training_jobs
- [ ] list_training_jobs_for_hyper_parameter_tuning_job
- [ ] list_transform_jobs
+- [ ] list_trial_components
+- [ ] list_trials
+- [ ] list_user_profiles
- [ ] list_workteams
- [ ] render_ui_template
- [ ] search
+- [ ] start_monitoring_schedule
- [ ] start_notebook_instance
+- [ ] stop_auto_ml_job
- [ ] stop_compilation_job
- [ ] stop_hyper_parameter_tuning_job
- [ ] stop_labeling_job
+- [ ] stop_monitoring_schedule
- [ ] stop_notebook_instance
+- [ ] stop_processing_job
- [ ] stop_training_job
- [ ] stop_transform_job
- [ ] update_code_repository
+- [ ] update_domain
- [ ] update_endpoint
- [ ] update_endpoint_weights_and_capacities
+- [ ] update_experiment
+- [ ] update_monitoring_schedule
- [ ] update_notebook_instance
- [ ] update_notebook_instance_lifecycle_config
+- [ ] update_trial
+- [ ] update_trial_component
+- [ ] update_user_profile
- [ ] update_workteam
+## sagemaker-a2i-runtime
+0% implemented
+- [ ] delete_human_loop
+- [ ] describe_human_loop
+- [ ] list_human_loops
+- [ ] start_human_loop
+- [ ] stop_human_loop
+
## sagemaker-runtime
0% implemented
- [ ] invoke_endpoint
@@ -5772,6 +6432,38 @@
- [ ] tag_resource
- [ ] untag_resource
+## schemas
+0% implemented
+- [ ] create_discoverer
+- [ ] create_registry
+- [ ] create_schema
+- [ ] delete_discoverer
+- [ ] delete_registry
+- [ ] delete_schema
+- [ ] delete_schema_version
+- [ ] describe_code_binding
+- [ ] describe_discoverer
+- [ ] describe_registry
+- [ ] describe_schema
+- [ ] get_code_binding_source
+- [ ] get_discovered_schema
+- [ ] list_discoverers
+- [ ] list_registries
+- [ ] list_schema_versions
+- [ ] list_schemas
+- [ ] list_tags_for_resource
+- [ ] lock_service_linked_role
+- [ ] put_code_binding
+- [ ] search_schemas
+- [ ] start_discoverer
+- [ ] stop_discoverer
+- [ ] tag_resource
+- [ ] unlock_service_linked_role
+- [ ] untag_resource
+- [ ] update_discoverer
+- [ ] update_registry
+- [ ] update_schema
+
## sdb
0% implemented
- [ ] batch_delete_attributes
@@ -5786,14 +6478,14 @@
- [ ] select
## secretsmanager
-55% implemented
+61% implemented
- [ ] cancel_rotate_secret
- [X] create_secret
- [ ] delete_resource_policy
- [X] delete_secret
- [X] describe_secret
- [X] get_random_password
-- [ ] get_resource_policy
+- [X] get_resource_policy
- [X] get_secret_value
- [X] list_secret_version_ids
- [X] list_secrets
@@ -6076,6 +6768,7 @@
- [ ] delete_configuration_set_event_destination
- [ ] delete_dedicated_ip_pool
- [ ] delete_email_identity
+- [ ] delete_suppressed_destination
- [ ] get_account
- [ ] get_blacklist_reports
- [ ] get_configuration_set
@@ -6087,24 +6780,30 @@
- [ ] get_domain_deliverability_campaign
- [ ] get_domain_statistics_report
- [ ] get_email_identity
+- [ ] get_suppressed_destination
- [ ] list_configuration_sets
- [ ] list_dedicated_ip_pools
- [ ] list_deliverability_test_reports
- [ ] list_domain_deliverability_campaigns
- [ ] list_email_identities
+- [ ] list_suppressed_destinations
- [ ] list_tags_for_resource
- [ ] put_account_dedicated_ip_warmup_attributes
- [ ] put_account_sending_attributes
+- [ ] put_account_suppression_attributes
- [ ] put_configuration_set_delivery_options
- [ ] put_configuration_set_reputation_options
- [ ] put_configuration_set_sending_options
+- [ ] put_configuration_set_suppression_options
- [ ] put_configuration_set_tracking_options
- [ ] put_dedicated_ip_in_pool
- [ ] put_dedicated_ip_warmup_attributes
- [ ] put_deliverability_dashboard_option
- [ ] put_email_identity_dkim_attributes
+- [ ] put_email_identity_dkim_signing_attributes
- [ ] put_email_identity_feedback_attributes
- [ ] put_email_identity_mail_from_attributes
+- [ ] put_suppressed_destination
- [ ] send_email
- [ ] tag_resource
- [ ] untag_resource
@@ -6328,6 +7027,7 @@
- [ ] describe_patch_properties
- [ ] describe_sessions
- [ ] get_automation_execution
+- [ ] get_calendar_state
- [X] get_command_invocation
- [ ] get_connection_status
- [ ] get_default_patch_baseline
@@ -6390,6 +7090,7 @@
- [ ] update_managed_instance_role
- [ ] update_ops_item
- [ ] update_patch_baseline
+- [ ] update_resource_data_sync
- [ ] update_service_setting
## sso
@@ -6457,6 +7158,7 @@
- [ ] delete_tape
- [ ] delete_tape_archive
- [ ] delete_volume
+- [ ] describe_availability_monitor_test
- [ ] describe_bandwidth_rate_limit
- [ ] describe_cache
- [ ] describe_cached_iscsi_volumes
@@ -6494,6 +7196,7 @@
- [ ] set_local_console_password
- [ ] set_smb_guest_password
- [ ] shutdown_gateway
+- [ ] start_availability_monitor_test
- [ ] start_gateway
- [ ] update_bandwidth_rate_limit
- [ ] update_chap_credentials
@@ -6786,6 +7489,45 @@
- [ ] update_web_acl
- [ ] update_xss_match_set
+## wafv2
+0% implemented
+- [ ] associate_web_acl
+- [ ] check_capacity
+- [ ] create_ip_set
+- [ ] create_regex_pattern_set
+- [ ] create_rule_group
+- [ ] create_web_acl
+- [ ] delete_ip_set
+- [ ] delete_logging_configuration
+- [ ] delete_regex_pattern_set
+- [ ] delete_rule_group
+- [ ] delete_web_acl
+- [ ] describe_managed_rule_group
+- [ ] disassociate_web_acl
+- [ ] get_ip_set
+- [ ] get_logging_configuration
+- [ ] get_rate_based_statement_managed_keys
+- [ ] get_regex_pattern_set
+- [ ] get_rule_group
+- [ ] get_sampled_requests
+- [ ] get_web_acl
+- [ ] get_web_acl_for_resource
+- [ ] list_available_managed_rule_groups
+- [ ] list_ip_sets
+- [ ] list_logging_configurations
+- [ ] list_regex_pattern_sets
+- [ ] list_resources_for_web_acl
+- [ ] list_rule_groups
+- [ ] list_tags_for_resource
+- [ ] list_web_acls
+- [ ] put_logging_configuration
+- [ ] tag_resource
+- [ ] untag_resource
+- [ ] update_ip_set
+- [ ] update_regex_pattern_set
+- [ ] update_rule_group
+- [ ] update_web_acl
+
## workdocs
0% implemented
- [ ] abort_document_version_upload
diff --git a/moto/__init__.py b/moto/__init__.py
index 767c0ee27..44b25f41e 100644
--- a/moto/__init__.py
+++ b/moto/__init__.py
@@ -9,6 +9,8 @@ from .batch import mock_batch # noqa
from .cloudformation import mock_cloudformation # noqa
from .cloudformation import mock_cloudformation_deprecated # noqa
from .cloudwatch import mock_cloudwatch, mock_cloudwatch_deprecated # noqa
+from .codecommit import mock_codecommit # noqa
+from .codepipeline import mock_codepipeline # noqa
from .cognitoidentity import mock_cognitoidentity # noqa
from .cognitoidentity import mock_cognitoidentity_deprecated # noqa
from .cognitoidp import mock_cognitoidp, mock_cognitoidp_deprecated # noqa
@@ -20,6 +22,7 @@ from .dynamodb import mock_dynamodb, mock_dynamodb_deprecated # noqa
from .dynamodb2 import mock_dynamodb2, mock_dynamodb2_deprecated # noqa
from .dynamodbstreams import mock_dynamodbstreams # noqa
from .ec2 import mock_ec2, mock_ec2_deprecated # noqa
+from .ec2_instance_connect import mock_ec2_instance_connect # noqa
from .ecr import mock_ecr, mock_ecr_deprecated # noqa
from .ecs import mock_ecs, mock_ecs_deprecated # noqa
from .elb import mock_elb, mock_elb_deprecated # noqa
diff --git a/moto/acm/models.py b/moto/acm/models.py
index a85017040..3df541982 100644
--- a/moto/acm/models.py
+++ b/moto/acm/models.py
@@ -13,8 +13,9 @@ import cryptography.hazmat.primitives.asymmetric.rsa
from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.backends import default_backend
+from moto.core import ACCOUNT_ID as DEFAULT_ACCOUNT_ID
+
-DEFAULT_ACCOUNT_ID = 123456789012
GOOGLE_ROOT_CA = b"""-----BEGIN CERTIFICATE-----
MIIEKDCCAxCgAwIBAgIQAQAhJYiw+lmnd+8Fe2Yn3zANBgkqhkiG9w0BAQsFADBC
MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMS
diff --git a/moto/apigateway/exceptions.py b/moto/apigateway/exceptions.py
index 52c26fa46..434ebc467 100644
--- a/moto/apigateway/exceptions.py
+++ b/moto/apigateway/exceptions.py
@@ -101,3 +101,12 @@ class ApiKeyNotFoundException(RESTError):
super(ApiKeyNotFoundException, self).__init__(
"NotFoundException", "Invalid API Key identifier specified"
)
+
+
+class ApiKeyAlreadyExists(RESTError):
+ code = 409
+
+ def __init__(self):
+ super(ApiKeyAlreadyExists, self).__init__(
+ "ConflictException", "API Key already exists"
+ )
diff --git a/moto/apigateway/models.py b/moto/apigateway/models.py
index 6f1d01c4f..fd2fb7064 100644
--- a/moto/apigateway/models.py
+++ b/moto/apigateway/models.py
@@ -32,6 +32,7 @@ from .exceptions import (
RoleNotSpecified,
NoIntegrationDefined,
NoMethodDefined,
+ ApiKeyAlreadyExists,
)
STAGE_URL = "https://{api_id}.execute-api.{region_name}.amazonaws.com/{stage_name}"
@@ -759,6 +760,10 @@ class APIGatewayBackend(BaseBackend):
return api.delete_deployment(deployment_id)
def create_apikey(self, payload):
+ if payload.get("value") is not None:
+ for api_key in self.get_apikeys():
+ if api_key.get("value") == payload["value"]:
+ raise ApiKeyAlreadyExists()
key = ApiKey(**payload)
self.keys[key["id"]] = key
return key
@@ -842,3 +847,11 @@ class APIGatewayBackend(BaseBackend):
apigateway_backends = {}
for region_name in Session().get_available_regions("apigateway"):
apigateway_backends[region_name] = APIGatewayBackend(region_name)
+for region_name in Session().get_available_regions(
+ "apigateway", partition_name="aws-us-gov"
+):
+ apigateway_backends[region_name] = APIGatewayBackend(region_name)
+for region_name in Session().get_available_regions(
+ "apigateway", partition_name="aws-cn"
+):
+ apigateway_backends[region_name] = APIGatewayBackend(region_name)
diff --git a/moto/apigateway/responses.py b/moto/apigateway/responses.py
index 1089de211..c4c7b403e 100644
--- a/moto/apigateway/responses.py
+++ b/moto/apigateway/responses.py
@@ -9,6 +9,7 @@ from .exceptions import (
BadRequestException,
CrossAccountNotAllowed,
StageNotFoundException,
+ ApiKeyAlreadyExists,
)
@@ -302,7 +303,17 @@ class APIGatewayResponse(BaseResponse):
self.setup_class(request, full_url, headers)
if self.method == "POST":
- apikey_response = self.backend.create_apikey(json.loads(self.body))
+ try:
+ apikey_response = self.backend.create_apikey(json.loads(self.body))
+ except ApiKeyAlreadyExists as error:
+ return (
+ error.code,
+ self.headers,
+ '{{"message":"{0}","code":"{1}"}}'.format(
+ error.message, error.error_type
+ ),
+ )
+
elif self.method == "GET":
apikeys_response = self.backend.get_apikeys()
return 200, {}, json.dumps({"item": apikeys_response})
diff --git a/moto/athena/models.py b/moto/athena/models.py
index 7353e6a6e..6aeca0ffa 100644
--- a/moto/athena/models.py
+++ b/moto/athena/models.py
@@ -1,10 +1,11 @@
from __future__ import unicode_literals
import time
-import boto3
+from boto3 import Session
+
from moto.core import BaseBackend, BaseModel
-ACCOUNT_ID = 123456789012
+from moto.core import ACCOUNT_ID
class TaggableResourceMixin(object):
@@ -77,5 +78,9 @@ class AthenaBackend(BaseBackend):
athena_backends = {}
-for region in boto3.Session().get_available_regions("athena"):
+for region in Session().get_available_regions("athena"):
+ athena_backends[region] = AthenaBackend(region)
+for region in Session().get_available_regions("athena", partition_name="aws-us-gov"):
+ athena_backends[region] = AthenaBackend(region)
+for region in Session().get_available_regions("athena", partition_name="aws-cn"):
athena_backends[region] = AthenaBackend(region)
diff --git a/moto/awslambda/models.py b/moto/awslambda/models.py
index 7a9e90f9d..95a5c4ad5 100644
--- a/moto/awslambda/models.py
+++ b/moto/awslambda/models.py
@@ -23,7 +23,8 @@ import traceback
import weakref
import requests.adapters
-import boto.awslambda
+from boto3 import Session
+
from moto.core import BaseBackend, BaseModel
from moto.core.exceptions import RESTError
from moto.iam.models import iam_backend
@@ -42,11 +43,10 @@ from .utils import make_function_arn, make_function_ver_arn
from moto.sqs import sqs_backends
from moto.dynamodb2 import dynamodb_backends2
from moto.dynamodbstreams import dynamodbstreams_backends
+from moto.core import ACCOUNT_ID
logger = logging.getLogger(__name__)
-ACCOUNT_ID = "123456789012"
-
try:
from tempfile import TemporaryDirectory
@@ -1044,10 +1044,10 @@ def do_validate_s3():
return os.environ.get("VALIDATE_LAMBDA_S3", "") in ["", "1", "true"]
-# Handle us forgotten regions, unless Lambda truly only runs out of US and
-lambda_backends = {
- _region.name: LambdaBackend(_region.name) for _region in boto.awslambda.regions()
-}
-
-lambda_backends["ap-southeast-2"] = LambdaBackend("ap-southeast-2")
-lambda_backends["us-gov-west-1"] = LambdaBackend("us-gov-west-1")
+lambda_backends = {}
+for region in Session().get_available_regions("lambda"):
+ lambda_backends[region] = LambdaBackend(region)
+for region in Session().get_available_regions("lambda", partition_name="aws-us-gov"):
+ lambda_backends[region] = LambdaBackend(region)
+for region in Session().get_available_regions("lambda", partition_name="aws-cn"):
+ lambda_backends[region] = LambdaBackend(region)
diff --git a/moto/backends.py b/moto/backends.py
index 53a5cafc3..a358b8fd2 100644
--- a/moto/backends.py
+++ b/moto/backends.py
@@ -8,6 +8,8 @@ from moto.awslambda import lambda_backends
from moto.batch import batch_backends
from moto.cloudformation import cloudformation_backends
from moto.cloudwatch import cloudwatch_backends
+from moto.codecommit import codecommit_backends
+from moto.codepipeline import codepipeline_backends
from moto.cognitoidentity import cognitoidentity_backends
from moto.cognitoidp import cognitoidp_backends
from moto.config import config_backends
@@ -18,6 +20,7 @@ from moto.dynamodb import dynamodb_backends
from moto.dynamodb2 import dynamodb_backends2
from moto.dynamodbstreams import dynamodbstreams_backends
from moto.ec2 import ec2_backends
+from moto.ec2_instance_connect import ec2_instance_connect_backends
from moto.ecr import ecr_backends
from moto.ecs import ecs_backends
from moto.elb import elb_backends
@@ -60,6 +63,8 @@ BACKENDS = {
"batch": batch_backends,
"cloudformation": cloudformation_backends,
"cloudwatch": cloudwatch_backends,
+ "codecommit": codecommit_backends,
+ "codepipeline": codepipeline_backends,
"cognito-identity": cognitoidentity_backends,
"cognito-idp": cognitoidp_backends,
"config": config_backends,
@@ -69,6 +74,7 @@ BACKENDS = {
"dynamodb2": dynamodb_backends2,
"dynamodbstreams": dynamodbstreams_backends,
"ec2": ec2_backends,
+ "ec2_instance_connect": ec2_instance_connect_backends,
"ecr": ecr_backends,
"ecs": ecs_backends,
"elb": elb_backends,
diff --git a/moto/batch/models.py b/moto/batch/models.py
index ab52db54c..fc35f2997 100644
--- a/moto/batch/models.py
+++ b/moto/batch/models.py
@@ -1,5 +1,4 @@
from __future__ import unicode_literals
-import boto3
import re
import requests.adapters
from itertools import cycle
@@ -12,6 +11,8 @@ import docker
import functools
import threading
import dateutil.parser
+from boto3 import Session
+
from moto.core import BaseBackend, BaseModel
from moto.iam import iam_backends
from moto.ec2 import ec2_backends
@@ -28,11 +29,10 @@ from .utils import (
from moto.ec2.exceptions import InvalidSubnetIdError
from moto.ec2.models import INSTANCE_TYPES as EC2_INSTANCE_TYPES
from moto.iam.exceptions import IAMNotFoundException
-
+from moto.core import ACCOUNT_ID as DEFAULT_ACCOUNT_ID
_orig_adapter_send = requests.adapters.HTTPAdapter.send
logger = logging.getLogger(__name__)
-DEFAULT_ACCOUNT_ID = 123456789012
COMPUTE_ENVIRONMENT_NAME_REGEX = re.compile(
r"^[A-Za-z0-9][A-Za-z0-9_-]{1,126}[A-Za-z0-9]$"
)
@@ -182,7 +182,7 @@ class JobDefinition(BaseModel):
self._region = region_name
self.container_properties = container_properties
self.arn = None
- self.status = "INACTIVE"
+ self.status = "ACTIVE"
if parameters is None:
parameters = {}
@@ -285,7 +285,7 @@ class JobDefinition(BaseModel):
class Job(threading.Thread, BaseModel):
- def __init__(self, name, job_def, job_queue, log_backend):
+ def __init__(self, name, job_def, job_queue, log_backend, container_overrides):
"""
Docker Job
@@ -301,6 +301,7 @@ class Job(threading.Thread, BaseModel):
self.job_name = name
self.job_id = str(uuid.uuid4())
self.job_definition = job_def
+ self.container_overrides = container_overrides
self.job_queue = job_queue
self.job_state = "SUBMITTED" # One of SUBMITTED | PENDING | RUNNABLE | STARTING | RUNNING | SUCCEEDED | FAILED
self.job_queue.jobs.append(self)
@@ -357,6 +358,11 @@ class Job(threading.Thread, BaseModel):
result["statusReason"] = self.job_stopped_reason
return result
+ def _get_container_property(self, p, default):
+ return self.container_overrides.get(
+ p, self.job_definition.container_properties.get(p, default)
+ )
+
def run(self):
"""
Run the container.
@@ -375,8 +381,33 @@ class Job(threading.Thread, BaseModel):
self.job_state = "PENDING"
time.sleep(1)
- image = "alpine:latest"
- cmd = '/bin/sh -c "for a in `seq 1 10`; do echo Hello World; sleep 1; done"'
+ image = self.job_definition.container_properties.get(
+ "image", "alpine:latest"
+ )
+ privileged = self.job_definition.container_properties.get(
+ "privileged", False
+ )
+ cmd = self._get_container_property(
+ "command",
+ '/bin/sh -c "for a in `seq 1 10`; do echo Hello World; sleep 1; done"',
+ )
+ environment = {
+ e["name"]: e["value"]
+ for e in self._get_container_property("environment", [])
+ }
+ volumes = {
+ v["name"]: v["host"]
+ for v in self._get_container_property("volumes", [])
+ }
+ mounts = [
+ docker.types.Mount(
+ m["containerPath"],
+ volumes[m["sourceVolume"]]["sourcePath"],
+ type="bind",
+ read_only=m["readOnly"],
+ )
+ for m in self._get_container_property("mountPoints", [])
+ ]
name = "{0}-{1}".format(self.job_name, self.job_id)
self.job_state = "RUNNABLE"
@@ -384,8 +415,16 @@ class Job(threading.Thread, BaseModel):
time.sleep(1)
self.job_state = "STARTING"
+ log_config = docker.types.LogConfig(type=docker.types.LogConfig.types.JSON)
container = self.docker_client.containers.run(
- image, cmd, detach=True, name=name
+ image,
+ cmd,
+ detach=True,
+ name=name,
+ log_config=log_config,
+ environment=environment,
+ mounts=mounts,
+ privileged=privileged,
)
self.job_state = "RUNNING"
self.job_started_at = datetime.datetime.now()
@@ -815,8 +854,10 @@ class BatchBackend(BaseBackend):
raise InvalidParameterValueException(
"computeResources must contain {0}".format(param)
)
-
- if self.iam_backend.get_role_by_arn(cr["instanceRole"]) is None:
+ for profile in self.iam_backend.get_instance_profiles():
+ if profile.arn == cr["instanceRole"]:
+ break
+ else:
raise InvalidParameterValueException(
"could not find instanceRole {0}".format(cr["instanceRole"])
)
@@ -1208,7 +1249,13 @@ class BatchBackend(BaseBackend):
if queue is None:
raise ClientException("Job queue {0} does not exist".format(job_queue))
- job = Job(job_name, job_def, queue, log_backend=self.logs_backend)
+ job = Job(
+ job_name,
+ job_def,
+ queue,
+ log_backend=self.logs_backend,
+ container_overrides=container_overrides,
+ )
self._jobs[job.job_id] = job
# Here comes the fun
@@ -1271,7 +1318,10 @@ class BatchBackend(BaseBackend):
job.terminate(reason)
-available_regions = boto3.session.Session().get_available_regions("batch")
-batch_backends = {
- region: BatchBackend(region_name=region) for region in available_regions
-}
+batch_backends = {}
+for region in Session().get_available_regions("batch"):
+ batch_backends[region] = BatchBackend(region)
+for region in Session().get_available_regions("batch", partition_name="aws-us-gov"):
+ batch_backends[region] = BatchBackend(region)
+for region in Session().get_available_regions("batch", partition_name="aws-cn"):
+ batch_backends[region] = BatchBackend(region)
diff --git a/moto/cloudformation/models.py b/moto/cloudformation/models.py
index 71ceaf168..0ae5d1ae4 100644
--- a/moto/cloudformation/models.py
+++ b/moto/cloudformation/models.py
@@ -4,7 +4,8 @@ import json
import yaml
import uuid
-import boto.cloudformation
+from boto3 import Session
+
from moto.compat import OrderedDict
from moto.core import BaseBackend, BaseModel
@@ -717,5 +718,13 @@ class CloudFormationBackend(BaseBackend):
cloudformation_backends = {}
-for region in boto.cloudformation.regions():
- cloudformation_backends[region.name] = CloudFormationBackend()
+for region in Session().get_available_regions("cloudformation"):
+ cloudformation_backends[region] = CloudFormationBackend()
+for region in Session().get_available_regions(
+ "cloudformation", partition_name="aws-us-gov"
+):
+ cloudformation_backends[region] = CloudFormationBackend()
+for region in Session().get_available_regions(
+ "cloudformation", partition_name="aws-cn"
+):
+ cloudformation_backends[region] = CloudFormationBackend()
diff --git a/moto/cloudformation/parsing.py b/moto/cloudformation/parsing.py
index 77e3c271c..34d96acc6 100644
--- a/moto/cloudformation/parsing.py
+++ b/moto/cloudformation/parsing.py
@@ -1,5 +1,4 @@
from __future__ import unicode_literals
-import collections
import functools
import logging
import copy
@@ -11,6 +10,7 @@ from moto.awslambda import models as lambda_models
from moto.batch import models as batch_models
from moto.cloudwatch import models as cloudwatch_models
from moto.cognitoidentity import models as cognitoidentity_models
+from moto.compat import collections_abc
from moto.datapipeline import models as datapipeline_models
from moto.dynamodb2 import models as dynamodb2_models
from moto.ec2 import models as ec2_models
@@ -27,6 +27,7 @@ from moto.route53 import models as route53_models
from moto.s3 import models as s3_models
from moto.sns import models as sns_models
from moto.sqs import models as sqs_models
+from moto.core import ACCOUNT_ID
from .utils import random_suffix
from .exceptions import (
ExportNotFound,
@@ -404,7 +405,7 @@ def parse_output(output_logical_id, output_json, resources_map):
return output
-class ResourceMap(collections.Mapping):
+class ResourceMap(collections_abc.Mapping):
"""
This is a lazy loading map for resources. This allows us to create resources
without needing to create a full dependency tree. Upon creation, each
@@ -431,7 +432,7 @@ class ResourceMap(collections.Mapping):
# Create the default resources
self._parsed_resources = {
- "AWS::AccountId": "123456789012",
+ "AWS::AccountId": ACCOUNT_ID,
"AWS::Region": self._region_name,
"AWS::StackId": stack_id,
"AWS::StackName": stack_name,
@@ -633,7 +634,7 @@ class ResourceMap(collections.Mapping):
raise last_exception
-class OutputMap(collections.Mapping):
+class OutputMap(collections_abc.Mapping):
def __init__(self, resources, template, stack_id):
self._template = template
self._stack_id = stack_id
diff --git a/moto/cloudformation/responses.py b/moto/cloudformation/responses.py
index f5e094c15..bf68a6325 100644
--- a/moto/cloudformation/responses.py
+++ b/moto/cloudformation/responses.py
@@ -7,6 +7,7 @@ from six.moves.urllib.parse import urlparse
from moto.core.responses import BaseResponse
from moto.core.utils import amzn_request_id
from moto.s3 import s3_backend
+from moto.core import ACCOUNT_ID
from .models import cloudformation_backends
from .exceptions import ValidationError
@@ -425,7 +426,9 @@ class CloudFormationResponse(BaseResponse):
stackset = self.cloudformation_backend.get_stack_set(stackset_name)
if not stackset.admin_role:
- stackset.admin_role = "arn:aws:iam::123456789012:role/AWSCloudFormationStackSetAdministrationRole"
+ stackset.admin_role = "arn:aws:iam::{AccountId}:role/AWSCloudFormationStackSetAdministrationRole".format(
+ AccountId=ACCOUNT_ID
+ )
if not stackset.execution_role:
stackset.execution_role = "AWSCloudFormationStackSetExecutionRole"
@@ -1051,11 +1054,14 @@ STOP_STACK_SET_OPERATION_RESPONSE_TEMPLATE = """
"""
-DESCRIBE_STACKSET_OPERATION_RESPONSE_TEMPLATE = """
+DESCRIBE_STACKSET_OPERATION_RESPONSE_TEMPLATE = (
+ """
{{ stackset.execution_role }}
- arn:aws:iam::123456789012:role/{{ stackset.admin_role }}
+ arn:aws:iam::"""
+ + ACCOUNT_ID
+ + """:role/{{ stackset.admin_role }}
{{ stackset.id }}
{{ operation.CreationTimestamp }}
{{ operation.OperationId }}
@@ -1072,15 +1078,19 @@ DESCRIBE_STACKSET_OPERATION_RESPONSE_TEMPLATE = """
"""
+)
-LIST_STACK_SET_OPERATION_RESULTS_RESPONSE_TEMPLATE = """
+LIST_STACK_SET_OPERATION_RESULTS_RESPONSE_TEMPLATE = (
+ """
{% for instance in operation.Instances %}
{% for account, region in instance.items() %}
- Function not found: arn:aws:lambda:us-west-2:123456789012:function:AWSCloudFormationStackSetAccountGate
+ Function not found: arn:aws:lambda:us-west-2:"""
+ + ACCOUNT_ID
+ + """:function:AWSCloudFormationStackSetAccountGate
SKIPPED
{{ region }}
@@ -1096,3 +1106,4 @@ LIST_STACK_SET_OPERATION_RESULTS_RESPONSE_TEMPLATE = """
"""
+)
diff --git a/moto/cloudformation/utils.py b/moto/cloudformation/utils.py
index 42dfa0b63..cd8481002 100644
--- a/moto/cloudformation/utils.py
+++ b/moto/cloudformation/utils.py
@@ -7,6 +7,7 @@ import os
import string
from cfnlint import decode, core
+from moto.core import ACCOUNT_ID
def generate_stack_id(stack_name, region="us-east-1", account="123456789"):
@@ -29,8 +30,8 @@ def generate_stackset_id(stackset_name):
def generate_stackset_arn(stackset_id, region_name):
- return "arn:aws:cloudformation:{}:123456789012:stackset/{}".format(
- region_name, stackset_id
+ return "arn:aws:cloudformation:{}:{}:stackset/{}".format(
+ region_name, ACCOUNT_ID, stackset_id
)
diff --git a/moto/cloudwatch/models.py b/moto/cloudwatch/models.py
index 7566e757b..13b31ddfe 100644
--- a/moto/cloudwatch/models.py
+++ b/moto/cloudwatch/models.py
@@ -1,14 +1,16 @@
import json
+
+from boto3 import Session
+
from moto.core.utils import iso_8601_datetime_with_milliseconds
from moto.core import BaseBackend, BaseModel
from moto.core.exceptions import RESTError
-import boto.ec2.cloudwatch
from datetime import datetime, timedelta
from dateutil.tz import tzutc
from uuid import uuid4
from .utils import make_arn_for_dashboard
-DEFAULT_ACCOUNT_ID = 123456789012
+from moto.core import ACCOUNT_ID as DEFAULT_ACCOUNT_ID
_EMPTY_LIST = tuple()
@@ -431,5 +433,11 @@ class LogGroup(BaseModel):
cloudwatch_backends = {}
-for region in boto.ec2.cloudwatch.regions():
- cloudwatch_backends[region.name] = CloudWatchBackend()
+for region in Session().get_available_regions("cloudwatch"):
+ cloudwatch_backends[region] = CloudWatchBackend()
+for region in Session().get_available_regions(
+ "cloudwatch", partition_name="aws-us-gov"
+):
+ cloudwatch_backends[region] = CloudWatchBackend()
+for region in Session().get_available_regions("cloudwatch", partition_name="aws-cn"):
+ cloudwatch_backends[region] = CloudWatchBackend()
diff --git a/moto/codecommit/__init__.py b/moto/codecommit/__init__.py
new file mode 100644
index 000000000..6c5a8f5ad
--- /dev/null
+++ b/moto/codecommit/__init__.py
@@ -0,0 +1,4 @@
+from .models import codecommit_backends
+from ..core.models import base_decorator
+
+mock_codecommit = base_decorator(codecommit_backends)
diff --git a/moto/codecommit/exceptions.py b/moto/codecommit/exceptions.py
new file mode 100644
index 000000000..136af50f1
--- /dev/null
+++ b/moto/codecommit/exceptions.py
@@ -0,0 +1,35 @@
+from moto.core.exceptions import JsonRESTError
+
+
+class RepositoryNameExistsException(JsonRESTError):
+ code = 400
+
+ def __init__(self, repository_name):
+ super(RepositoryNameExistsException, self).__init__(
+ "RepositoryNameExistsException",
+ "Repository named {0} already exists".format(repository_name),
+ )
+
+
+class RepositoryDoesNotExistException(JsonRESTError):
+ code = 400
+
+ def __init__(self, repository_name):
+ super(RepositoryDoesNotExistException, self).__init__(
+ "RepositoryDoesNotExistException",
+ "{0} does not exist".format(repository_name),
+ )
+
+
+class InvalidRepositoryNameException(JsonRESTError):
+ code = 400
+
+ def __init__(self):
+ super(InvalidRepositoryNameException, self).__init__(
+ "InvalidRepositoryNameException",
+ "The repository name is not valid. Repository names can be any valid "
+ "combination of letters, numbers, "
+ "periods, underscores, and dashes between 1 and 100 characters in "
+ "length. Names are case sensitive. "
+ "For more information, see Limits in the AWS CodeCommit User Guide. ",
+ )
diff --git a/moto/codecommit/models.py b/moto/codecommit/models.py
new file mode 100644
index 000000000..6a4e82ad2
--- /dev/null
+++ b/moto/codecommit/models.py
@@ -0,0 +1,69 @@
+from boto3 import Session
+from moto.core import BaseBackend, BaseModel
+from moto.core.utils import iso_8601_datetime_with_milliseconds
+from datetime import datetime
+from moto.iam.models import ACCOUNT_ID
+from .exceptions import RepositoryDoesNotExistException, RepositoryNameExistsException
+import uuid
+
+
+class CodeCommit(BaseModel):
+ def __init__(self, region, repository_description, repository_name):
+ current_date = iso_8601_datetime_with_milliseconds(datetime.utcnow())
+ self.repository_metadata = dict()
+ self.repository_metadata["repositoryName"] = repository_name
+ self.repository_metadata[
+ "cloneUrlSsh"
+ ] = "ssh://git-codecommit.{0}.amazonaws.com/v1/repos/{1}".format(
+ region, repository_name
+ )
+ self.repository_metadata[
+ "cloneUrlHttp"
+ ] = "https://git-codecommit.{0}.amazonaws.com/v1/repos/{1}".format(
+ region, repository_name
+ )
+ self.repository_metadata["creationDate"] = current_date
+ self.repository_metadata["lastModifiedDate"] = current_date
+ self.repository_metadata["repositoryDescription"] = repository_description
+ self.repository_metadata["repositoryId"] = str(uuid.uuid4())
+ self.repository_metadata["Arn"] = "arn:aws:codecommit:{0}:{1}:{2}".format(
+ region, ACCOUNT_ID, repository_name
+ )
+ self.repository_metadata["accountId"] = ACCOUNT_ID
+
+
+class CodeCommitBackend(BaseBackend):
+ def __init__(self):
+ self.repositories = {}
+
+ def create_repository(self, region, repository_name, repository_description):
+ repository = self.repositories.get(repository_name)
+ if repository:
+ raise RepositoryNameExistsException(repository_name)
+
+ self.repositories[repository_name] = CodeCommit(
+ region, repository_description, repository_name
+ )
+
+ return self.repositories[repository_name].repository_metadata
+
+ def get_repository(self, repository_name):
+ repository = self.repositories.get(repository_name)
+ if not repository:
+ raise RepositoryDoesNotExistException(repository_name)
+
+ return repository.repository_metadata
+
+ def delete_repository(self, repository_name):
+ repository = self.repositories.get(repository_name)
+
+ if repository:
+ self.repositories.pop(repository_name)
+ return repository.repository_metadata.get("repositoryId")
+
+ return None
+
+
+codecommit_backends = {}
+for region in Session().get_available_regions("codecommit"):
+ codecommit_backends[region] = CodeCommitBackend()
diff --git a/moto/codecommit/responses.py b/moto/codecommit/responses.py
new file mode 100644
index 000000000..3c6fdc5ea
--- /dev/null
+++ b/moto/codecommit/responses.py
@@ -0,0 +1,57 @@
+import json
+import re
+
+from moto.core.responses import BaseResponse
+from .models import codecommit_backends
+from .exceptions import InvalidRepositoryNameException
+
+
+def _is_repository_name_valid(repository_name):
+ name_regex = re.compile(r"[\w\.-]+")
+ result = name_regex.split(repository_name)
+ if len(result) > 0:
+ for match in result:
+ if len(match) > 0:
+ return False
+ return True
+
+
+class CodeCommitResponse(BaseResponse):
+ @property
+ def codecommit_backend(self):
+ return codecommit_backends[self.region]
+
+ def create_repository(self):
+ if not _is_repository_name_valid(self._get_param("repositoryName")):
+ raise InvalidRepositoryNameException()
+
+ repository_metadata = self.codecommit_backend.create_repository(
+ self.region,
+ self._get_param("repositoryName"),
+ self._get_param("repositoryDescription"),
+ )
+
+ return json.dumps({"repositoryMetadata": repository_metadata})
+
+ def get_repository(self):
+ if not _is_repository_name_valid(self._get_param("repositoryName")):
+ raise InvalidRepositoryNameException()
+
+ repository_metadata = self.codecommit_backend.get_repository(
+ self._get_param("repositoryName")
+ )
+
+ return json.dumps({"repositoryMetadata": repository_metadata})
+
+ def delete_repository(self):
+ if not _is_repository_name_valid(self._get_param("repositoryName")):
+ raise InvalidRepositoryNameException()
+
+ repository_id = self.codecommit_backend.delete_repository(
+ self._get_param("repositoryName")
+ )
+
+ if repository_id:
+ return json.dumps({"repositoryId": repository_id})
+
+ return json.dumps({})
diff --git a/moto/codecommit/urls.py b/moto/codecommit/urls.py
new file mode 100644
index 000000000..1e3cdb1b4
--- /dev/null
+++ b/moto/codecommit/urls.py
@@ -0,0 +1,6 @@
+from __future__ import unicode_literals
+from .responses import CodeCommitResponse
+
+url_bases = ["https?://codecommit.(.+).amazonaws.com"]
+
+url_paths = {"{0}/$": CodeCommitResponse.dispatch}
diff --git a/moto/codepipeline/__init__.py b/moto/codepipeline/__init__.py
new file mode 100644
index 000000000..da1f1fa9d
--- /dev/null
+++ b/moto/codepipeline/__init__.py
@@ -0,0 +1,4 @@
+from .models import codepipeline_backends
+from ..core.models import base_decorator
+
+mock_codepipeline = base_decorator(codepipeline_backends)
diff --git a/moto/codepipeline/exceptions.py b/moto/codepipeline/exceptions.py
new file mode 100644
index 000000000..a4db9aab1
--- /dev/null
+++ b/moto/codepipeline/exceptions.py
@@ -0,0 +1,44 @@
+from moto.core.exceptions import JsonRESTError
+
+
+class InvalidStructureException(JsonRESTError):
+ code = 400
+
+ def __init__(self, message):
+ super(InvalidStructureException, self).__init__(
+ "InvalidStructureException", message
+ )
+
+
+class PipelineNotFoundException(JsonRESTError):
+ code = 400
+
+ def __init__(self, message):
+ super(PipelineNotFoundException, self).__init__(
+ "PipelineNotFoundException", message
+ )
+
+
+class ResourceNotFoundException(JsonRESTError):
+ code = 400
+
+ def __init__(self, message):
+ super(ResourceNotFoundException, self).__init__(
+ "ResourceNotFoundException", message
+ )
+
+
+class InvalidTagsException(JsonRESTError):
+ code = 400
+
+ def __init__(self, message):
+ super(InvalidTagsException, self).__init__("InvalidTagsException", message)
+
+
+class TooManyTagsException(JsonRESTError):
+ code = 400
+
+ def __init__(self, arn):
+ super(TooManyTagsException, self).__init__(
+ "TooManyTagsException", "Tag limit exceeded for resource [{}].".format(arn)
+ )
diff --git a/moto/codepipeline/models.py b/moto/codepipeline/models.py
new file mode 100644
index 000000000..50f07deb0
--- /dev/null
+++ b/moto/codepipeline/models.py
@@ -0,0 +1,218 @@
+import json
+from datetime import datetime
+
+from boto3 import Session
+from moto.core.utils import iso_8601_datetime_with_milliseconds
+
+from moto.iam.exceptions import IAMNotFoundException
+
+from moto.iam import iam_backends
+
+from moto.codepipeline.exceptions import (
+ InvalidStructureException,
+ PipelineNotFoundException,
+ ResourceNotFoundException,
+ InvalidTagsException,
+ TooManyTagsException,
+)
+from moto.core import BaseBackend, BaseModel
+
+from moto.iam.models import ACCOUNT_ID
+
+
+class CodePipeline(BaseModel):
+ def __init__(self, region, pipeline):
+ # the version number for a new pipeline is always 1
+ pipeline["version"] = 1
+
+ self.pipeline = self.add_default_values(pipeline)
+ self.tags = {}
+
+ self._arn = "arn:aws:codepipeline:{0}:{1}:{2}".format(
+ region, ACCOUNT_ID, pipeline["name"]
+ )
+ self._created = datetime.utcnow()
+ self._updated = datetime.utcnow()
+
+ @property
+ def metadata(self):
+ return {
+ "pipelineArn": self._arn,
+ "created": iso_8601_datetime_with_milliseconds(self._created),
+ "updated": iso_8601_datetime_with_milliseconds(self._updated),
+ }
+
+ def add_default_values(self, pipeline):
+ for stage in pipeline["stages"]:
+ for action in stage["actions"]:
+ if "runOrder" not in action:
+ action["runOrder"] = 1
+ if "configuration" not in action:
+ action["configuration"] = {}
+ if "outputArtifacts" not in action:
+ action["outputArtifacts"] = []
+ if "inputArtifacts" not in action:
+ action["inputArtifacts"] = []
+
+ return pipeline
+
+ def validate_tags(self, tags):
+ for tag in tags:
+ if tag["key"].startswith("aws:"):
+ raise InvalidTagsException(
+ "Not allowed to modify system tags. "
+ "System tags start with 'aws:'. "
+ "msg=[Caller is an end user and not allowed to mutate system tags]"
+ )
+
+ if (len(self.tags) + len(tags)) > 50:
+ raise TooManyTagsException(self._arn)
+
+
+class CodePipelineBackend(BaseBackend):
+ def __init__(self):
+ self.pipelines = {}
+
+ @property
+ def iam_backend(self):
+ return iam_backends["global"]
+
+ def create_pipeline(self, region, pipeline, tags):
+ if pipeline["name"] in self.pipelines:
+ raise InvalidStructureException(
+ "A pipeline with the name '{0}' already exists in account '{1}'".format(
+ pipeline["name"], ACCOUNT_ID
+ )
+ )
+
+ try:
+ role = self.iam_backend.get_role_by_arn(pipeline["roleArn"])
+ service_principal = json.loads(role.assume_role_policy_document)[
+ "Statement"
+ ][0]["Principal"]["Service"]
+ if "codepipeline.amazonaws.com" not in service_principal:
+ raise IAMNotFoundException("")
+ except IAMNotFoundException:
+ raise InvalidStructureException(
+ "CodePipeline is not authorized to perform AssumeRole on role {}".format(
+ pipeline["roleArn"]
+ )
+ )
+
+ if len(pipeline["stages"]) < 2:
+ raise InvalidStructureException(
+ "Pipeline has only 1 stage(s). There should be a minimum of 2 stages in a pipeline"
+ )
+
+ self.pipelines[pipeline["name"]] = CodePipeline(region, pipeline)
+
+ if tags:
+ self.pipelines[pipeline["name"]].validate_tags(tags)
+
+ new_tags = {tag["key"]: tag["value"] for tag in tags}
+ self.pipelines[pipeline["name"]].tags.update(new_tags)
+
+ return pipeline, sorted(tags, key=lambda i: i["key"])
+
+ def get_pipeline(self, name):
+ codepipeline = self.pipelines.get(name)
+
+ if not codepipeline:
+ raise PipelineNotFoundException(
+ "Account '{0}' does not have a pipeline with name '{1}'".format(
+ ACCOUNT_ID, name
+ )
+ )
+
+ return codepipeline.pipeline, codepipeline.metadata
+
+ def update_pipeline(self, pipeline):
+ codepipeline = self.pipelines.get(pipeline["name"])
+
+ if not codepipeline:
+ raise ResourceNotFoundException(
+ "The account with id '{0}' does not include a pipeline with the name '{1}'".format(
+ ACCOUNT_ID, pipeline["name"]
+ )
+ )
+
+ # version number is auto incremented
+ pipeline["version"] = codepipeline.pipeline["version"] + 1
+ codepipeline._updated = datetime.utcnow()
+ codepipeline.pipeline = codepipeline.add_default_values(pipeline)
+
+ return codepipeline.pipeline
+
+ def list_pipelines(self):
+ pipelines = []
+
+ for name, codepipeline in self.pipelines.items():
+ pipelines.append(
+ {
+ "name": name,
+ "version": codepipeline.pipeline["version"],
+ "created": codepipeline.metadata["created"],
+ "updated": codepipeline.metadata["updated"],
+ }
+ )
+
+ return sorted(pipelines, key=lambda i: i["name"])
+
+ def delete_pipeline(self, name):
+ self.pipelines.pop(name, None)
+
+ def list_tags_for_resource(self, arn):
+ name = arn.split(":")[-1]
+ pipeline = self.pipelines.get(name)
+
+ if not pipeline:
+ raise ResourceNotFoundException(
+ "The account with id '{0}' does not include a pipeline with the name '{1}'".format(
+ ACCOUNT_ID, name
+ )
+ )
+
+ tags = [{"key": key, "value": value} for key, value in pipeline.tags.items()]
+
+ return sorted(tags, key=lambda i: i["key"])
+
+ def tag_resource(self, arn, tags):
+ name = arn.split(":")[-1]
+ pipeline = self.pipelines.get(name)
+
+ if not pipeline:
+ raise ResourceNotFoundException(
+ "The account with id '{0}' does not include a pipeline with the name '{1}'".format(
+ ACCOUNT_ID, name
+ )
+ )
+
+ pipeline.validate_tags(tags)
+
+ for tag in tags:
+ pipeline.tags.update({tag["key"]: tag["value"]})
+
+ def untag_resource(self, arn, tag_keys):
+ name = arn.split(":")[-1]
+ pipeline = self.pipelines.get(name)
+
+ if not pipeline:
+ raise ResourceNotFoundException(
+ "The account with id '{0}' does not include a pipeline with the name '{1}'".format(
+ ACCOUNT_ID, name
+ )
+ )
+
+ for key in tag_keys:
+ pipeline.tags.pop(key, None)
+
+
+codepipeline_backends = {}
+for region in Session().get_available_regions("codepipeline"):
+ codepipeline_backends[region] = CodePipelineBackend()
+for region in Session().get_available_regions(
+ "codepipeline", partition_name="aws-us-gov"
+):
+ codepipeline_backends[region] = CodePipelineBackend()
+for region in Session().get_available_regions("codepipeline", partition_name="aws-cn"):
+ codepipeline_backends[region] = CodePipelineBackend()
diff --git a/moto/codepipeline/responses.py b/moto/codepipeline/responses.py
new file mode 100644
index 000000000..0223dfae6
--- /dev/null
+++ b/moto/codepipeline/responses.py
@@ -0,0 +1,62 @@
+import json
+
+from moto.core.responses import BaseResponse
+from .models import codepipeline_backends
+
+
+class CodePipelineResponse(BaseResponse):
+ @property
+ def codepipeline_backend(self):
+ return codepipeline_backends[self.region]
+
+ def create_pipeline(self):
+ pipeline, tags = self.codepipeline_backend.create_pipeline(
+ self.region, self._get_param("pipeline"), self._get_param("tags")
+ )
+
+ return json.dumps({"pipeline": pipeline, "tags": tags})
+
+ def get_pipeline(self):
+ pipeline, metadata = self.codepipeline_backend.get_pipeline(
+ self._get_param("name")
+ )
+
+ return json.dumps({"pipeline": pipeline, "metadata": metadata})
+
+ def update_pipeline(self):
+ pipeline = self.codepipeline_backend.update_pipeline(
+ self._get_param("pipeline")
+ )
+
+ return json.dumps({"pipeline": pipeline})
+
+ def list_pipelines(self):
+ pipelines = self.codepipeline_backend.list_pipelines()
+
+ return json.dumps({"pipelines": pipelines})
+
+ def delete_pipeline(self):
+ self.codepipeline_backend.delete_pipeline(self._get_param("name"))
+
+ return ""
+
+ def list_tags_for_resource(self):
+ tags = self.codepipeline_backend.list_tags_for_resource(
+ self._get_param("resourceArn")
+ )
+
+ return json.dumps({"tags": tags})
+
+ def tag_resource(self):
+ self.codepipeline_backend.tag_resource(
+ self._get_param("resourceArn"), self._get_param("tags")
+ )
+
+ return ""
+
+ def untag_resource(self):
+ self.codepipeline_backend.untag_resource(
+ self._get_param("resourceArn"), self._get_param("tagKeys")
+ )
+
+ return ""
diff --git a/moto/codepipeline/urls.py b/moto/codepipeline/urls.py
new file mode 100644
index 000000000..7111020a5
--- /dev/null
+++ b/moto/codepipeline/urls.py
@@ -0,0 +1,6 @@
+from __future__ import unicode_literals
+from .responses import CodePipelineResponse
+
+url_bases = ["https?://codepipeline.(.+).amazonaws.com"]
+
+url_paths = {"{0}/$": CodePipelineResponse.dispatch}
diff --git a/moto/cognitoidentity/models.py b/moto/cognitoidentity/models.py
index 2a4f5d4bc..ae9f308c2 100644
--- a/moto/cognitoidentity/models.py
+++ b/moto/cognitoidentity/models.py
@@ -3,7 +3,7 @@ from __future__ import unicode_literals
import datetime
import json
-import boto.cognito.identity
+from boto3 import Session
from moto.compat import OrderedDict
from moto.core import BaseBackend, BaseModel
@@ -136,5 +136,13 @@ class CognitoIdentityBackend(BaseBackend):
cognitoidentity_backends = {}
-for region in boto.cognito.identity.regions():
- cognitoidentity_backends[region.name] = CognitoIdentityBackend(region.name)
+for region in Session().get_available_regions("cognito-identity"):
+ cognitoidentity_backends[region] = CognitoIdentityBackend(region)
+for region in Session().get_available_regions(
+ "cognito-identity", partition_name="aws-us-gov"
+):
+ cognitoidentity_backends[region] = CognitoIdentityBackend(region)
+for region in Session().get_available_regions(
+ "cognito-identity", partition_name="aws-cn"
+):
+ cognitoidentity_backends[region] = CognitoIdentityBackend(region)
diff --git a/moto/cognitoidp/models.py b/moto/cognitoidp/models.py
index 6700920ce..478ceffb2 100644
--- a/moto/cognitoidp/models.py
+++ b/moto/cognitoidp/models.py
@@ -9,7 +9,7 @@ import os
import time
import uuid
-import boto.cognito.identity
+from boto3 import Session
from jose import jws
from moto.compat import OrderedDict
@@ -749,8 +749,14 @@ class CognitoIdpBackend(BaseBackend):
cognitoidp_backends = {}
-for region in boto.cognito.identity.regions():
- cognitoidp_backends[region.name] = CognitoIdpBackend(region.name)
+for region in Session().get_available_regions("cognito-idp"):
+ cognitoidp_backends[region] = CognitoIdpBackend(region)
+for region in Session().get_available_regions(
+ "cognito-idp", partition_name="aws-us-gov"
+):
+ cognitoidp_backends[region] = CognitoIdpBackend(region)
+for region in Session().get_available_regions("cognito-idp", partition_name="aws-cn"):
+ cognitoidp_backends[region] = CognitoIdpBackend(region)
# Hack to help moto-server process requests on localhost, where the region isn't
diff --git a/moto/compat.py b/moto/compat.py
index d7f5ab5e6..c0acd28a6 100644
--- a/moto/compat.py
+++ b/moto/compat.py
@@ -3,3 +3,8 @@ try:
except ImportError:
# python 2.6 or earlier, use backport
from ordereddict import OrderedDict # noqa
+
+try:
+ import collections.abc as collections_abc # noqa
+except ImportError:
+ import collections as collections_abc # noqa
diff --git a/moto/config/models.py b/moto/config/models.py
index f608b759a..45dccd1ba 100644
--- a/moto/config/models.py
+++ b/moto/config/models.py
@@ -45,7 +45,8 @@ from moto.config.exceptions import (
from moto.core import BaseBackend, BaseModel
from moto.s3.config import s3_config_query
-DEFAULT_ACCOUNT_ID = "123456789012"
+from moto.core import ACCOUNT_ID as DEFAULT_ACCOUNT_ID
+
POP_STRINGS = [
"capitalizeStart",
"CapitalizeStart",
@@ -1083,6 +1084,9 @@ class ConfigBackend(BaseBackend):
config_backends = {}
-boto3_session = Session()
-for region in boto3_session.get_available_regions("config"):
+for region in Session().get_available_regions("config"):
+ config_backends[region] = ConfigBackend()
+for region in Session().get_available_regions("config", partition_name="aws-us-gov"):
+ config_backends[region] = ConfigBackend()
+for region in Session().get_available_regions("config", partition_name="aws-cn"):
config_backends[region] = ConfigBackend()
diff --git a/moto/core/__init__.py b/moto/core/__init__.py
index 4a4dfdfb6..045124fab 100644
--- a/moto/core/__init__.py
+++ b/moto/core/__init__.py
@@ -1,6 +1,6 @@
from __future__ import unicode_literals
-from .models import BaseModel, BaseBackend, moto_api_backend # noqa
+from .models import BaseModel, BaseBackend, moto_api_backend, ACCOUNT_ID # noqa
from .responses import ActionAuthenticatorMixin
moto_api_backends = {"global": moto_api_backend}
diff --git a/moto/core/access_control.py b/moto/core/access_control.py
index 9991063f9..8ba0c3ba1 100644
--- a/moto/core/access_control.py
+++ b/moto/core/access_control.py
@@ -24,7 +24,8 @@ from botocore.awsrequest import AWSRequest
from botocore.credentials import Credentials
from six import string_types
-from moto.iam.models import ACCOUNT_ID, Policy
+from moto.core import ACCOUNT_ID
+from moto.iam.models import Policy
from moto.iam import iam_backend
from moto.core.exceptions import (
SignatureDoesNotMatchError,
diff --git a/moto/core/models.py b/moto/core/models.py
index 5b19137c3..3be3bbd8e 100644
--- a/moto/core/models.py
+++ b/moto/core/models.py
@@ -23,6 +23,9 @@ from .utils import (
)
+ACCOUNT_ID = os.environ.get("MOTO_ACCOUNT_ID", "123456789012")
+
+
class BaseMockAWS(object):
nested_count = 0
diff --git a/moto/core/utils.py b/moto/core/utils.py
index 57ff0f1b4..efad5679c 100644
--- a/moto/core/utils.py
+++ b/moto/core/utils.py
@@ -304,3 +304,27 @@ def path_url(url):
if parsed_url.query:
path = path + "?" + parsed_url.query
return path
+
+
+def py2_strip_unicode_keys(blob):
+ """For Python 2 Only -- this will convert unicode keys in nested Dicts, Lists, and Sets to standard strings."""
+ if type(blob) == unicode: # noqa
+ return str(blob)
+
+ elif type(blob) == dict:
+ for key in list(blob.keys()):
+ value = blob.pop(key)
+ blob[str(key)] = py2_strip_unicode_keys(value)
+
+ elif type(blob) == list:
+ for i in range(0, len(blob)):
+ blob[i] = py2_strip_unicode_keys(blob[i])
+
+ elif type(blob) == set:
+ new_set = set()
+ for value in blob:
+ new_set.add(py2_strip_unicode_keys(value))
+
+ blob = new_set
+
+ return blob
diff --git a/moto/datapipeline/models.py b/moto/datapipeline/models.py
index cc1fe777e..d93deea61 100644
--- a/moto/datapipeline/models.py
+++ b/moto/datapipeline/models.py
@@ -1,7 +1,8 @@
from __future__ import unicode_literals
import datetime
-import boto.datapipeline
+from boto3 import Session
+
from moto.compat import OrderedDict
from moto.core import BaseBackend, BaseModel
from .utils import get_random_pipeline_id, remove_capitalization_of_dict_keys
@@ -142,5 +143,11 @@ class DataPipelineBackend(BaseBackend):
datapipeline_backends = {}
-for region in boto.datapipeline.regions():
- datapipeline_backends[region.name] = DataPipelineBackend()
+for region in Session().get_available_regions("datapipeline"):
+ datapipeline_backends[region] = DataPipelineBackend()
+for region in Session().get_available_regions(
+ "datapipeline", partition_name="aws-us-gov"
+):
+ datapipeline_backends[region] = DataPipelineBackend()
+for region in Session().get_available_regions("datapipeline", partition_name="aws-cn"):
+ datapipeline_backends[region] = DataPipelineBackend(region)
diff --git a/moto/datapipeline/utils.py b/moto/datapipeline/utils.py
index 9135181e7..b14fe6f1a 100644
--- a/moto/datapipeline/utils.py
+++ b/moto/datapipeline/utils.py
@@ -1,5 +1,5 @@
-import collections
import six
+from moto.compat import collections_abc
from moto.core.utils import get_random_hex
@@ -8,13 +8,13 @@ def get_random_pipeline_id():
def remove_capitalization_of_dict_keys(obj):
- if isinstance(obj, collections.Mapping):
+ if isinstance(obj, collections_abc.Mapping):
result = obj.__class__()
for key, value in obj.items():
normalized_key = key[:1].lower() + key[1:]
result[normalized_key] = remove_capitalization_of_dict_keys(value)
return result
- elif isinstance(obj, collections.Iterable) and not isinstance(
+ elif isinstance(obj, collections_abc.Iterable) and not isinstance(
obj, six.string_types
):
result = obj.__class__()
diff --git a/moto/datasync/models.py b/moto/datasync/models.py
index 17a2659fb..702cace5b 100644
--- a/moto/datasync/models.py
+++ b/moto/datasync/models.py
@@ -1,4 +1,5 @@
-import boto3
+from boto3 import Session
+
from moto.compat import OrderedDict
from moto.core import BaseBackend, BaseModel
@@ -226,5 +227,9 @@ class DataSyncBackend(BaseBackend):
datasync_backends = {}
-for region in boto3.Session().get_available_regions("datasync"):
- datasync_backends[region] = DataSyncBackend(region_name=region)
+for region in Session().get_available_regions("datasync"):
+ datasync_backends[region] = DataSyncBackend(region)
+for region in Session().get_available_regions("datasync", partition_name="aws-us-gov"):
+ datasync_backends[region] = DataSyncBackend(region)
+for region in Session().get_available_regions("datasync", partition_name="aws-cn"):
+ datasync_backends[region] = DataSyncBackend(region)
diff --git a/moto/dynamodb/models.py b/moto/dynamodb/models.py
index f00f6042d..f5771ec6e 100644
--- a/moto/dynamodb/models.py
+++ b/moto/dynamodb/models.py
@@ -6,6 +6,7 @@ import json
from moto.compat import OrderedDict
from moto.core import BaseBackend, BaseModel
from moto.core.utils import unix_time
+from moto.core import ACCOUNT_ID
from .comparisons import get_comparison_func
@@ -277,8 +278,8 @@ class Table(BaseModel):
if attribute_name == "StreamArn":
region = "us-east-1"
time = "2000-01-01T00:00:00.000"
- return "arn:aws:dynamodb:{0}:123456789012:table/{1}/stream/{2}".format(
- region, self.name, time
+ return "arn:aws:dynamodb:{0}:{1}:table/{2}/stream/{3}".format(
+ region, ACCOUNT_ID, self.name, time
)
raise UnformattedGetAttTemplateException()
diff --git a/moto/dynamodb2/models.py b/moto/dynamodb2/models.py
index 121f564a4..d4907cba5 100644
--- a/moto/dynamodb2/models.py
+++ b/moto/dynamodb2/models.py
@@ -8,7 +8,7 @@ import re
import uuid
import six
-import boto3
+from boto3 import Session
from botocore.exceptions import ParamValidationError
from moto.compat import OrderedDict
from moto.core import BaseBackend, BaseModel
@@ -586,7 +586,9 @@ class StreamRecord(BaseModel):
self.record["dynamodb"]["OldImage"] = old_a
# This is a substantial overestimate but it's the easiest to do now
- self.record["dynamodb"]["SizeBytes"] = len(json.dumps(self.record["dynamodb"]))
+ self.record["dynamodb"]["SizeBytes"] = len(
+ dynamo_json_dump(self.record["dynamodb"])
+ )
def to_json(self):
return self.record
@@ -1484,7 +1486,10 @@ class DynamoDBBackend(BaseBackend):
return table.ttl
-available_regions = boto3.session.Session().get_available_regions("dynamodb")
-dynamodb_backends = {
- region: DynamoDBBackend(region_name=region) for region in available_regions
-}
+dynamodb_backends = {}
+for region in Session().get_available_regions("dynamodb"):
+ dynamodb_backends[region] = DynamoDBBackend(region)
+for region in Session().get_available_regions("dynamodb", partition_name="aws-us-gov"):
+ dynamodb_backends[region] = DynamoDBBackend(region)
+for region in Session().get_available_regions("dynamodb", partition_name="aws-cn"):
+ dynamodb_backends[region] = DynamoDBBackend(region)
diff --git a/moto/dynamodb2/responses.py b/moto/dynamodb2/responses.py
index 0e39a1da1..c9f3529a9 100644
--- a/moto/dynamodb2/responses.py
+++ b/moto/dynamodb2/responses.py
@@ -438,9 +438,12 @@ class DynamoHandler(BaseResponse):
all_indexes = (table.global_indexes or []) + (table.indexes or [])
indexes_by_name = dict((i["IndexName"], i) for i in all_indexes)
if index_name not in indexes_by_name:
- raise ValueError(
- "Invalid index: %s for table: %s. Available indexes are: %s"
- % (index_name, name, ", ".join(indexes_by_name.keys()))
+ er = "com.amazonaws.dynamodb.v20120810#ResourceNotFoundException"
+ return self.error(
+ er,
+ "Invalid index: {} for table: {}. Available indexes are: {}".format(
+ index_name, name, ", ".join(indexes_by_name.keys())
+ ),
)
index = indexes_by_name[index_name]["KeySchema"]
@@ -481,7 +484,9 @@ class DynamoHandler(BaseResponse):
]
elif "begins_with" in range_key_expression:
range_comparison = "BEGINS_WITH"
- range_values = [value_alias_map[range_key_expression_components[1]]]
+ range_values = [
+ value_alias_map[range_key_expression_components[-1]]
+ ]
else:
range_values = [value_alias_map[range_key_expression_components[2]]]
else:
diff --git a/moto/dynamodbstreams/models.py b/moto/dynamodbstreams/models.py
index 6e99d8ef6..dc6f0e0d3 100644
--- a/moto/dynamodbstreams/models.py
+++ b/moto/dynamodbstreams/models.py
@@ -2,9 +2,10 @@ from __future__ import unicode_literals
import os
import json
-import boto3
import base64
+from boto3 import Session
+
from moto.core import BaseBackend, BaseModel
from moto.dynamodb2.models import dynamodb_backends
@@ -139,7 +140,14 @@ class DynamoDBStreamsBackend(BaseBackend):
return json.dumps(shard_iterator.get(limit))
-available_regions = boto3.session.Session().get_available_regions("dynamodbstreams")
-dynamodbstreams_backends = {
- region: DynamoDBStreamsBackend(region=region) for region in available_regions
-}
+dynamodbstreams_backends = {}
+for region in Session().get_available_regions("dynamodbstreams"):
+ dynamodbstreams_backends[region] = DynamoDBStreamsBackend(region)
+for region in Session().get_available_regions(
+ "dynamodbstreams", partition_name="aws-us-gov"
+):
+ dynamodbstreams_backends[region] = DynamoDBStreamsBackend(region)
+for region in Session().get_available_regions(
+ "dynamodbstreams", partition_name="aws-cn"
+):
+ dynamodbstreams_backends[region] = DynamoDBStreamsBackend(region)
diff --git a/moto/ec2/models.py b/moto/ec2/models.py
index 874536225..93a350914 100644
--- a/moto/ec2/models.py
+++ b/moto/ec2/models.py
@@ -8,9 +8,9 @@ import os
import re
import six
import warnings
-from pkg_resources import resource_filename
-import boto.ec2
+from boto3 import Session
+from pkg_resources import resource_filename
from collections import defaultdict
import weakref
@@ -1473,7 +1473,13 @@ class Zone(object):
class RegionsAndZonesBackend(object):
- regions = [Region(ri.name, ri.endpoint) for ri in boto.ec2.regions()]
+ regions = []
+ for region in Session().get_available_regions("ec2"):
+ regions.append(Region(region, "ec2.{}.amazonaws.com".format(region)))
+ for region in Session().get_available_regions("ec2", partition_name="aws-us-gov"):
+ regions.append(Region(region, "ec2.{}.amazonaws.com".format(region)))
+ for region in Session().get_available_regions("ec2", partition_name="aws-cn"):
+ regions.append(Region(region, "ec2.{}.amazonaws.com.cn".format(region)))
zones = {
"ap-south-1": [
@@ -1536,6 +1542,11 @@ class RegionsAndZonesBackend(object):
zone_id="apne1-az2",
),
],
+ "ap-east-1": [
+ Zone(region_name="ap-east-1", name="ap-east-1a", zone_id="ape1-az1"),
+ Zone(region_name="ap-east-1", name="ap-east-1b", zone_id="ape1-az2"),
+ Zone(region_name="ap-east-1", name="ap-east-1c", zone_id="ape1-az3"),
+ ],
"sa-east-1": [
Zone(region_name="sa-east-1", name="sa-east-1a", zone_id="sae1-az1"),
Zone(region_name="sa-east-1", name="sa-east-1c", zone_id="sae1-az3"),
@@ -1605,10 +1616,32 @@ class RegionsAndZonesBackend(object):
Zone(region_name="us-west-2", name="us-west-2b", zone_id="usw2-az1"),
Zone(region_name="us-west-2", name="us-west-2c", zone_id="usw2-az3"),
],
+ "me-south-1": [
+ Zone(region_name="me-south-1", name="me-south-1a", zone_id="mes1-az1"),
+ Zone(region_name="me-south-1", name="me-south-1b", zone_id="mes1-az2"),
+ Zone(region_name="me-south-1", name="me-south-1c", zone_id="mes1-az3"),
+ ],
"cn-north-1": [
Zone(region_name="cn-north-1", name="cn-north-1a", zone_id="cnn1-az1"),
Zone(region_name="cn-north-1", name="cn-north-1b", zone_id="cnn1-az2"),
],
+ "cn-northwest-1": [
+ Zone(
+ region_name="cn-northwest-1",
+ name="cn-northwest-1a",
+ zone_id="cnnw1-az1",
+ ),
+ Zone(
+ region_name="cn-northwest-1",
+ name="cn-northwest-1b",
+ zone_id="cnnw1-az2",
+ ),
+ Zone(
+ region_name="cn-northwest-1",
+ name="cn-northwest-1c",
+ zone_id="cnnw1-az3",
+ ),
+ ],
"us-gov-west-1": [
Zone(
region_name="us-gov-west-1", name="us-gov-west-1a", zone_id="usgw1-az1"
@@ -1620,6 +1653,17 @@ class RegionsAndZonesBackend(object):
region_name="us-gov-west-1", name="us-gov-west-1c", zone_id="usgw1-az3"
),
],
+ "us-gov-east-1": [
+ Zone(
+ region_name="us-gov-east-1", name="us-gov-east-1a", zone_id="usge1-az1"
+ ),
+ Zone(
+ region_name="us-gov-east-1", name="us-gov-east-1b", zone_id="usge1-az2"
+ ),
+ Zone(
+ region_name="us-gov-east-1", name="us-gov-east-1c", zone_id="usge1-az3"
+ ),
+ ],
}
def describe_regions(self, region_names=[]):
@@ -2449,6 +2493,7 @@ class VPC(TaggedEC2Resource):
self.is_default = "true" if is_default else "false"
self.enable_dns_support = "true"
self.classic_link_enabled = "false"
+ self.classic_link_dns_supported = "false"
# This attribute is set to 'true' only for default VPCs
# or VPCs created using the wizard of the VPC console
self.enable_dns_hostnames = "true" if is_default else "false"
@@ -3306,6 +3351,7 @@ class Route(object):
local=False,
gateway=None,
instance=None,
+ nat_gateway=None,
interface=None,
vpc_pcx=None,
):
@@ -3315,6 +3361,7 @@ class Route(object):
self.local = local
self.gateway = gateway
self.instance = instance
+ self.nat_gateway = nat_gateway
self.interface = interface
self.vpc_pcx = vpc_pcx
@@ -3327,6 +3374,7 @@ class Route(object):
gateway_id = properties.get("GatewayId")
instance_id = properties.get("InstanceId")
interface_id = properties.get("NetworkInterfaceId")
+ nat_gateway_id = properties.get("NatGatewayId")
pcx_id = properties.get("VpcPeeringConnectionId")
route_table_id = properties["RouteTableId"]
@@ -3336,6 +3384,7 @@ class Route(object):
destination_cidr_block=properties.get("DestinationCidrBlock"),
gateway_id=gateway_id,
instance_id=instance_id,
+ nat_gateway_id=nat_gateway_id,
interface_id=interface_id,
vpc_peering_connection_id=pcx_id,
)
@@ -3353,6 +3402,7 @@ class RouteBackend(object):
local=False,
gateway_id=None,
instance_id=None,
+ nat_gateway_id=None,
interface_id=None,
vpc_peering_connection_id=None,
):
@@ -3373,12 +3423,17 @@ class RouteBackend(object):
except ValueError:
raise InvalidDestinationCIDRBlockParameterError(destination_cidr_block)
+ nat_gateway = None
+ if nat_gateway_id is not None:
+ nat_gateway = self.nat_gateways.get(nat_gateway_id)
+
route = Route(
route_table,
destination_cidr_block,
local=local,
gateway=gateway,
instance=self.get_instance(instance_id) if instance_id else None,
+ nat_gateway=nat_gateway,
interface=None,
vpc_pcx=self.get_vpc_peering_connection(vpc_peering_connection_id)
if vpc_peering_connection_id
@@ -4827,7 +4882,35 @@ class NatGatewayBackend(object):
super(NatGatewayBackend, self).__init__()
def get_all_nat_gateways(self, filters):
- return self.nat_gateways.values()
+ nat_gateways = self.nat_gateways.values()
+
+ if filters is not None:
+ if filters.get("nat-gateway-id") is not None:
+ nat_gateways = [
+ nat_gateway
+ for nat_gateway in nat_gateways
+ if nat_gateway.id in filters["nat-gateway-id"]
+ ]
+ if filters.get("vpc-id") is not None:
+ nat_gateways = [
+ nat_gateway
+ for nat_gateway in nat_gateways
+ if nat_gateway.vpc_id in filters["vpc-id"]
+ ]
+ if filters.get("subnet-id") is not None:
+ nat_gateways = [
+ nat_gateway
+ for nat_gateway in nat_gateways
+ if nat_gateway.subnet_id in filters["subnet-id"]
+ ]
+ if filters.get("state") is not None:
+ nat_gateways = [
+ nat_gateway
+ for nat_gateway in nat_gateways
+ if nat_gateway.state in filters["state"]
+ ]
+
+ return nat_gateways
def create_nat_gateway(self, subnet_id, allocation_id):
nat_gateway = NatGateway(self, subnet_id, allocation_id)
diff --git a/moto/ec2/responses/instances.py b/moto/ec2/responses/instances.py
index 4b7a20a17..b9e572d29 100644
--- a/moto/ec2/responses/instances.py
+++ b/moto/ec2/responses/instances.py
@@ -6,6 +6,7 @@ from moto.core.responses import BaseResponse
from moto.core.utils import camelcase_to_underscores
from moto.ec2.utils import filters_from_querystring, dict_from_querystring
from moto.elbv2 import elbv2_backends
+from moto.core import ACCOUNT_ID
class InstanceResponse(BaseResponse):
@@ -246,10 +247,13 @@ class InstanceResponse(BaseResponse):
return EC2_MODIFY_INSTANCE_ATTRIBUTE
-EC2_RUN_INSTANCES = """
+EC2_RUN_INSTANCES = (
+ """
59dbff89-35bd-4eac-99ed-be587EXAMPLE
{{ reservation.id }}
- 123456789012
+ """
+ + ACCOUNT_ID
+ + """
-
sg-245f6a01
@@ -331,7 +335,9 @@ EC2_RUN_INSTANCES = """
in-use
1b:2b:3c:4d:5e:6f
{{ nic.private_ip_address }}
@@ -354,7 +360,9 @@ EC2_RUN_INSTANCES = """
{% endif %}
@@ -364,7 +372,9 @@ EC2_RUN_INSTANCES = """
{% endif %}
@@ -376,14 +386,18 @@ EC2_RUN_INSTANCES = """
+EC2_DESCRIBE_INSTANCES = (
+ """
fdcdcab1-ae5c-489e-9c33-4637c5dda355
{% for reservation in reservations %}
-
{{ reservation.id }}
- 123456789012
+ """
+ + ACCOUNT_ID
+ + """
{% for group in reservation.dynamic_group_list %}
-
@@ -476,7 +490,9 @@ EC2_DESCRIBE_INSTANCES = """
{% if instance.get_tags() %}
{% for tag in instance.get_tags() %}
@@ -499,7 +515,9 @@ EC2_DESCRIBE_INSTANCES = """
in-use
1b:2b:3c:4d:5e:6f
{{ nic.private_ip_address }}
@@ -526,7 +544,9 @@ EC2_DESCRIBE_INSTANCES = """
{% endif %}
@@ -536,7 +556,9 @@ EC2_DESCRIBE_INSTANCES = """
{% endif %}
@@ -554,6 +576,7 @@ EC2_DESCRIBE_INSTANCES = """
diff --git a/moto/ec2/responses/route_tables.py b/moto/ec2/responses/route_tables.py
index ef796e401..b5d65f831 100644
--- a/moto/ec2/responses/route_tables.py
+++ b/moto/ec2/responses/route_tables.py
@@ -18,6 +18,7 @@ class RouteTables(BaseResponse):
destination_cidr_block = self._get_param("DestinationCidrBlock")
gateway_id = self._get_param("GatewayId")
instance_id = self._get_param("InstanceId")
+ nat_gateway_id = self._get_param("NatGatewayId")
interface_id = self._get_param("NetworkInterfaceId")
pcx_id = self._get_param("VpcPeeringConnectionId")
@@ -26,6 +27,7 @@ class RouteTables(BaseResponse):
destination_cidr_block,
gateway_id=gateway_id,
instance_id=instance_id,
+ nat_gateway_id=nat_gateway_id,
interface_id=interface_id,
vpc_peering_connection_id=pcx_id,
)
@@ -173,6 +175,10 @@ DESCRIBE_ROUTE_TABLES_RESPONSE = """
CreateRoute
blackhole
{% endif %}
+ {% if route.nat_gateway %}
+ {{ route.nat_gateway.id }}
+ active
+ {% endif %}
{% endfor %}
diff --git a/moto/ec2/responses/security_groups.py b/moto/ec2/responses/security_groups.py
index d2cfff977..6f2926f61 100644
--- a/moto/ec2/responses/security_groups.py
+++ b/moto/ec2/responses/security_groups.py
@@ -2,6 +2,7 @@ from __future__ import unicode_literals
from moto.core.responses import BaseResponse
from moto.ec2.utils import filters_from_querystring
+from moto.core import ACCOUNT_ID
def try_parse_int(value, default=None):
@@ -171,12 +172,15 @@ DELETE_GROUP_RESPONSE = """
+DESCRIBE_SECURITY_GROUPS_RESPONSE = (
+ """
59dbff89-35bd-4eac-99ed-be587EXAMPLE
{% for group in groups %}
-
- 123456789012
+ """
+ + ACCOUNT_ID
+ + """
{{ group.id }}
{{ group.name }}
{{ group.description }}
@@ -196,7 +200,9 @@ DESCRIBE_SECURITY_GROUPS_RESPONSE = """
{{ source_group.id }}
{{ source_group.name }}
@@ -225,7 +231,9 @@ DESCRIBE_SECURITY_GROUPS_RESPONSE = """
{{ source_group.id }}
{{ source_group.name }}
@@ -255,6 +263,7 @@ DESCRIBE_SECURITY_GROUPS_RESPONSE = """
59dbff89-35bd-4eac-99ed-be587EXAMPLE
diff --git a/moto/ec2/responses/vpc_peering_connections.py b/moto/ec2/responses/vpc_peering_connections.py
index ff792a6cc..3bf86af8a 100644
--- a/moto/ec2/responses/vpc_peering_connections.py
+++ b/moto/ec2/responses/vpc_peering_connections.py
@@ -1,5 +1,6 @@
from __future__ import unicode_literals
from moto.core.responses import BaseResponse
+from moto.core import ACCOUNT_ID
class VPCPeeringConnections(BaseResponse):
@@ -40,7 +41,8 @@ class VPCPeeringConnections(BaseResponse):
return template.render()
-CREATE_VPC_PEERING_CONNECTION_RESPONSE = """
+CREATE_VPC_PEERING_CONNECTION_RESPONSE = (
+ """
7a62c49f-347e-4fc4-9331-6e8eEXAMPLE
@@ -56,7 +58,9 @@ CREATE_VPC_PEERING_CONNECTION_RESPONSE = """
- 123456789012
+ """
+ + ACCOUNT_ID
+ + """
{{ vpc_pcx.peer_vpc.id }}
@@ -68,8 +72,10 @@ CREATE_VPC_PEERING_CONNECTION_RESPONSE = """
"""
+)
-DESCRIBE_VPC_PEERING_CONNECTIONS_RESPONSE = """
+DESCRIBE_VPC_PEERING_CONNECTIONS_RESPONSE = (
+ """
7a62c49f-347e-4fc4-9331-6e8eEXAMPLE
@@ -82,7 +88,9 @@ DESCRIBE_VPC_PEERING_CONNECTIONS_RESPONSE = """
{{ vpc_pcx.vpc.cidr_block }}
- 123456789012
+ """
+ + ACCOUNT_ID
+ + """
{{ vpc_pcx.peer_vpc.id }}
{{ vpc_pcx.peer_vpc.cidr_block }}
@@ -101,6 +109,7 @@ DESCRIBE_VPC_PEERING_CONNECTIONS_RESPONSE = """
"""
+)
DELETE_VPC_PEERING_CONNECTION_RESPONSE = """
@@ -109,7 +118,8 @@ DELETE_VPC_PEERING_CONNECTION_RESPONSE = """
"""
-ACCEPT_VPC_PEERING_CONNECTION_RESPONSE = """
+ACCEPT_VPC_PEERING_CONNECTION_RESPONSE = (
+ """
7a62c49f-347e-4fc4-9331-6e8eEXAMPLE
@@ -120,7 +130,9 @@ ACCEPT_VPC_PEERING_CONNECTION_RESPONSE = """
{{ vpc_pcx.vpc.cidr_block }}
- 123456789012
+ """
+ + ACCOUNT_ID
+ + """
{{ vpc_pcx.peer_vpc.id }}
{{ vpc_pcx.peer_vpc.cidr_block }}
@@ -137,6 +149,7 @@ ACCEPT_VPC_PEERING_CONNECTION_RESPONSE = """
"""
+)
REJECT_VPC_PEERING_CONNECTION_RESPONSE = """
diff --git a/moto/ec2/urls.py b/moto/ec2/urls.py
index b83a9e950..4d85b2f56 100644
--- a/moto/ec2/urls.py
+++ b/moto/ec2/urls.py
@@ -2,6 +2,6 @@ from __future__ import unicode_literals
from .responses import EC2Response
-url_bases = ["https?://ec2.(.+).amazonaws.com(|.cn)"]
+url_bases = ["https?://ec2\.(.+)\.amazonaws\.com(|\.cn)"]
url_paths = {"{0}/": EC2Response.dispatch}
diff --git a/moto/ec2_instance_connect/__init__.py b/moto/ec2_instance_connect/__init__.py
new file mode 100644
index 000000000..c20d59cfa
--- /dev/null
+++ b/moto/ec2_instance_connect/__init__.py
@@ -0,0 +1,4 @@
+from ..core.models import base_decorator
+from .models import ec2_instance_connect_backends
+
+mock_ec2_instance_connect = base_decorator(ec2_instance_connect_backends)
diff --git a/moto/ec2_instance_connect/models.py b/moto/ec2_instance_connect/models.py
new file mode 100644
index 000000000..cc8cc3f33
--- /dev/null
+++ b/moto/ec2_instance_connect/models.py
@@ -0,0 +1,11 @@
+import boto
+from moto.core import BaseBackend
+
+
+class Ec2InstanceConnectBackend(BaseBackend):
+ pass
+
+
+ec2_instance_connect_backends = {}
+for region in boto.ec2.regions():
+ ec2_instance_connect_backends[region.name] = Ec2InstanceConnectBackend()
diff --git a/moto/ec2_instance_connect/responses.py b/moto/ec2_instance_connect/responses.py
new file mode 100644
index 000000000..462f1fddc
--- /dev/null
+++ b/moto/ec2_instance_connect/responses.py
@@ -0,0 +1,9 @@
+import json
+from moto.core.responses import BaseResponse
+
+
+class Ec2InstanceConnectResponse(BaseResponse):
+ def send_ssh_public_key(self):
+ return json.dumps(
+ {"RequestId": "example-2a47-4c91-9700-e37e85162cb6", "Success": True}
+ )
diff --git a/moto/ec2_instance_connect/urls.py b/moto/ec2_instance_connect/urls.py
new file mode 100644
index 000000000..e7078264f
--- /dev/null
+++ b/moto/ec2_instance_connect/urls.py
@@ -0,0 +1,6 @@
+from __future__ import unicode_literals
+from .responses import Ec2InstanceConnectResponse
+
+url_bases = ["https?://ec2-instance-connect\.(.+)\.amazonaws\.com"]
+
+url_paths = {"{0}/$": Ec2InstanceConnectResponse.dispatch}
diff --git a/moto/ecs/models.py b/moto/ecs/models.py
index c9dc998ee..845bdf650 100644
--- a/moto/ecs/models.py
+++ b/moto/ecs/models.py
@@ -3,9 +3,10 @@ import re
import uuid
from datetime import datetime
from random import random, randint
-import boto3
import pytz
+from boto3 import Session
+
from moto.core.exceptions import JsonRESTError
from moto.core import BaseBackend, BaseModel
from moto.core.utils import unix_time
@@ -1302,7 +1303,10 @@ class EC2ContainerServiceBackend(BaseBackend):
raise NotImplementedError()
-available_regions = boto3.session.Session().get_available_regions("ecs")
-ecs_backends = {
- region: EC2ContainerServiceBackend(region) for region in available_regions
-}
+ecs_backends = {}
+for region in Session().get_available_regions("ecs"):
+ ecs_backends[region] = EC2ContainerServiceBackend(region)
+for region in Session().get_available_regions("ecs", partition_name="aws-us-gov"):
+ ecs_backends[region] = EC2ContainerServiceBackend(region)
+for region in Session().get_available_regions("ecs", partition_name="aws-cn"):
+ ecs_backends[region] = EC2ContainerServiceBackend(region)
diff --git a/moto/emr/models.py b/moto/emr/models.py
index b62ce7932..713b15b9f 100644
--- a/moto/emr/models.py
+++ b/moto/emr/models.py
@@ -2,8 +2,8 @@ from __future__ import unicode_literals
from datetime import datetime
from datetime import timedelta
-import boto.emr
import pytz
+from boto3 import Session
from dateutil.parser import parse as dtparse
from moto.core import BaseBackend, BaseModel
from moto.emr.exceptions import EmrError
@@ -460,5 +460,9 @@ class ElasticMapReduceBackend(BaseBackend):
emr_backends = {}
-for region in boto.emr.regions():
- emr_backends[region.name] = ElasticMapReduceBackend(region.name)
+for region in Session().get_available_regions("emr"):
+ emr_backends[region] = ElasticMapReduceBackend(region)
+for region in Session().get_available_regions("emr", partition_name="aws-us-gov"):
+ emr_backends[region] = ElasticMapReduceBackend(region)
+for region in Session().get_available_regions("emr", partition_name="aws-cn"):
+ emr_backends[region] = ElasticMapReduceBackend(region)
diff --git a/moto/events/models.py b/moto/events/models.py
index 0298c7c69..548d41393 100644
--- a/moto/events/models.py
+++ b/moto/events/models.py
@@ -1,7 +1,7 @@
import os
import re
import json
-import boto3
+from boto3 import Session
from moto.core.exceptions import JsonRESTError
from moto.core import BaseBackend, BaseModel
@@ -362,5 +362,10 @@ class EventsBackend(BaseBackend):
self.event_buses.pop(name, None)
-available_regions = boto3.session.Session().get_available_regions("events")
-events_backends = {region: EventsBackend(region) for region in available_regions}
+events_backends = {}
+for region in Session().get_available_regions("events"):
+ events_backends[region] = EventsBackend(region)
+for region in Session().get_available_regions("events", partition_name="aws-us-gov"):
+ events_backends[region] = EventsBackend(region)
+for region in Session().get_available_regions("events", partition_name="aws-cn"):
+ events_backends[region] = EventsBackend(region)
diff --git a/moto/glacier/models.py b/moto/glacier/models.py
index 6a3fc074d..9e91ea3a5 100644
--- a/moto/glacier/models.py
+++ b/moto/glacier/models.py
@@ -4,8 +4,8 @@ import hashlib
import datetime
+from boto3 import Session
-import boto.glacier
from moto.core import BaseBackend, BaseModel
from .utils import get_job_id
@@ -221,5 +221,9 @@ class GlacierBackend(BaseBackend):
glacier_backends = {}
-for region in boto.glacier.regions():
- glacier_backends[region.name] = GlacierBackend(region)
+for region in Session().get_available_regions("glacier"):
+ glacier_backends[region] = GlacierBackend(region)
+for region in Session().get_available_regions("glacier", partition_name="aws-us-gov"):
+ glacier_backends[region] = GlacierBackend(region)
+for region in Session().get_available_regions("glacier", partition_name="aws-cn"):
+ glacier_backends[region] = GlacierBackend(region)
diff --git a/moto/iam/models.py b/moto/iam/models.py
index c67d5b365..18b3a7a6f 100644
--- a/moto/iam/models.py
+++ b/moto/iam/models.py
@@ -14,7 +14,7 @@ from cryptography.hazmat.backends import default_backend
from six.moves.urllib.parse import urlparse
from moto.core.exceptions import RESTError
-from moto.core import BaseBackend, BaseModel
+from moto.core import BaseBackend, BaseModel, ACCOUNT_ID
from moto.core.utils import (
iso_8601_datetime_without_milliseconds,
iso_8601_datetime_with_milliseconds,
@@ -45,8 +45,6 @@ from .utils import (
random_policy_id,
)
-ACCOUNT_ID = 123456789012
-
class MFADevice(object):
"""MFA Device class."""
@@ -309,6 +307,7 @@ class Role(BaseModel):
permissions_boundary,
description,
tags,
+ max_session_duration,
):
self.id = role_id
self.name = name
@@ -320,6 +319,7 @@ class Role(BaseModel):
self.tags = tags
self.description = description
self.permissions_boundary = permissions_boundary
+ self.max_session_duration = max_session_duration
@property
def created_iso_8601(self):
@@ -338,6 +338,7 @@ class Role(BaseModel):
permissions_boundary=properties.get("PermissionsBoundary", ""),
description=properties.get("Description", ""),
tags=properties.get("Tags", {}),
+ max_session_duration=properties.get("MaxSessionDuration", 3600),
)
policies = properties.get("Policies", [])
@@ -542,7 +543,7 @@ class Group(BaseModel):
class User(BaseModel):
- def __init__(self, name, path=None):
+ def __init__(self, name, path=None, tags=None):
self.name = name
self.id = random_resource_id()
self.path = path if path else "/"
@@ -555,6 +556,7 @@ class User(BaseModel):
self.password = None
self.password_reset_required = False
self.signing_certificates = {}
+ self.tags = tags
@property
def arn(self):
@@ -938,9 +940,10 @@ class IAMBackend(BaseBackend):
role.description = role_description
return role
- def update_role(self, role_name, role_description):
+ def update_role(self, role_name, role_description, max_session_duration):
role = self.get_role(role_name)
role.description = role_description
+ role.max_session_duration = max_session_duration
return role
def detach_role_policy(self, policy_arn, role_name):
@@ -1059,6 +1062,7 @@ class IAMBackend(BaseBackend):
permissions_boundary,
description,
tags,
+ max_session_duration,
):
role_id = random_resource_id()
if permissions_boundary and not self.policy_arn_regex.match(
@@ -1084,6 +1088,7 @@ class IAMBackend(BaseBackend):
permissions_boundary,
description,
clean_tags,
+ max_session_duration,
)
self.roles[role_id] = role
return role
@@ -1417,13 +1422,13 @@ class IAMBackend(BaseBackend):
"The group with name {0} cannot be found.".format(group_name)
)
- def create_user(self, user_name, path="/"):
+ def create_user(self, user_name, path="/", tags=None):
if user_name in self.users:
raise IAMConflictException(
"EntityAlreadyExists", "User {0} already exists".format(user_name)
)
- user = User(user_name, path)
+ user = User(user_name, path, tags)
self.users[user_name] = user
return user
@@ -1579,6 +1584,10 @@ class IAMBackend(BaseBackend):
user = self.get_user(user_name)
return user.policies.keys()
+ def list_user_tags(self, user_name):
+ user = self.get_user(user_name)
+ return user.tags
+
def put_user_policy(self, user_name, policy_name, policy_json):
user = self.get_user(user_name)
diff --git a/moto/iam/responses.py b/moto/iam/responses.py
index ea14bef0f..06561d4c4 100644
--- a/moto/iam/responses.py
+++ b/moto/iam/responses.py
@@ -182,6 +182,7 @@ class IamResponse(BaseResponse):
permissions_boundary = self._get_param("PermissionsBoundary")
description = self._get_param("Description")
tags = self._get_multi_param("Tags.member")
+ max_session_duration = self._get_param("MaxSessionDuration", 3600)
role = iam_backend.create_role(
role_name,
@@ -190,6 +191,7 @@ class IamResponse(BaseResponse):
permissions_boundary,
description,
tags,
+ max_session_duration,
)
template = self.response_template(CREATE_ROLE_TEMPLATE)
return template.render(role=role)
@@ -258,7 +260,8 @@ class IamResponse(BaseResponse):
def update_role(self):
role_name = self._get_param("RoleName")
description = self._get_param("Description")
- role = iam_backend.update_role(role_name, description)
+ max_session_duration = self._get_param("MaxSessionDuration", 3600)
+ role = iam_backend.update_role(role_name, description, max_session_duration)
template = self.response_template(UPDATE_ROLE_TEMPLATE)
return template.render(role=role)
@@ -437,8 +440,8 @@ class IamResponse(BaseResponse):
def create_user(self):
user_name = self._get_param("UserName")
path = self._get_param("Path")
-
- user = iam_backend.create_user(user_name, path)
+ tags = self._get_multi_param("Tags.member")
+ user = iam_backend.create_user(user_name, path, tags)
template = self.response_template(USER_TEMPLATE)
return template.render(action="Create", user=user)
@@ -535,6 +538,12 @@ class IamResponse(BaseResponse):
template = self.response_template(LIST_USER_POLICIES_TEMPLATE)
return template.render(policies=policies)
+ def list_user_tags(self):
+ user_name = self._get_param("UserName")
+ tags = iam_backend.list_user_tags(user_name)
+ template = self.response_template(LIST_USER_TAGS_TEMPLATE)
+ return template.render(user_tags=tags or [])
+
def put_user_policy(self):
user_name = self._get_param("UserName")
policy_name = self._get_param("PolicyName")
@@ -1189,9 +1198,12 @@ CREATE_ROLE_TEMPLATE = """
"""
+LIST_USER_TAGS_TEMPLATE = """
+
+
+ {% for tag in user_tags %}
+ -
+ {{ tag.Key }}
+ {{ tag.Value }}
+
+ {% endfor %}
+
+ false
+
+
+ 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE
+
+"""
+
CREATE_ACCESS_KEY_TEMPLATE = """
diff --git a/moto/iot/models.py b/moto/iot/models.py
index 74a3e992c..d59d7533c 100644
--- a/moto/iot/models.py
+++ b/moto/iot/models.py
@@ -9,7 +9,7 @@ import uuid
from collections import OrderedDict
from datetime import datetime
-import boto3
+from boto3 import Session
from moto.core import BaseBackend, BaseModel
from .exceptions import (
@@ -825,5 +825,10 @@ class IoTBackend(BaseBackend):
return self.jobs[job_id]
-available_regions = boto3.session.Session().get_available_regions("iot")
-iot_backends = {region: IoTBackend(region) for region in available_regions}
+iot_backends = {}
+for region in Session().get_available_regions("iot"):
+ iot_backends[region] = IoTBackend(region)
+for region in Session().get_available_regions("iot", partition_name="aws-us-gov"):
+ iot_backends[region] = IoTBackend(region)
+for region in Session().get_available_regions("iot", partition_name="aws-cn"):
+ iot_backends[region] = IoTBackend(region)
diff --git a/moto/iotdata/models.py b/moto/iotdata/models.py
index e534e1d1f..41b69bc7f 100644
--- a/moto/iotdata/models.py
+++ b/moto/iotdata/models.py
@@ -1,8 +1,9 @@
from __future__ import unicode_literals
import json
import time
-import boto3
import jsondiff
+from boto3 import Session
+
from moto.core import BaseBackend, BaseModel
from moto.iot import iot_backends
from .exceptions import (
@@ -205,5 +206,10 @@ class IoTDataPlaneBackend(BaseBackend):
return None
-available_regions = boto3.session.Session().get_available_regions("iot-data")
-iotdata_backends = {region: IoTDataPlaneBackend(region) for region in available_regions}
+iotdata_backends = {}
+for region in Session().get_available_regions("iot-data"):
+ iotdata_backends[region] = IoTDataPlaneBackend(region)
+for region in Session().get_available_regions("iot-data", partition_name="aws-us-gov"):
+ iotdata_backends[region] = IoTDataPlaneBackend(region)
+for region in Session().get_available_regions("iot-data", partition_name="aws-cn"):
+ iotdata_backends[region] = IoTDataPlaneBackend(region)
diff --git a/moto/kinesis/exceptions.py b/moto/kinesis/exceptions.py
index 8c950c355..1f25d6720 100644
--- a/moto/kinesis/exceptions.py
+++ b/moto/kinesis/exceptions.py
@@ -2,6 +2,7 @@ from __future__ import unicode_literals
import json
from werkzeug.exceptions import BadRequest
+from moto.core import ACCOUNT_ID
class ResourceNotFoundError(BadRequest):
@@ -23,14 +24,14 @@ class ResourceInUseError(BadRequest):
class StreamNotFoundError(ResourceNotFoundError):
def __init__(self, stream_name):
super(StreamNotFoundError, self).__init__(
- "Stream {0} under account 123456789012 not found.".format(stream_name)
+ "Stream {0} under account {1} not found.".format(stream_name, ACCOUNT_ID)
)
class ShardNotFoundError(ResourceNotFoundError):
def __init__(self, shard_id):
super(ShardNotFoundError, self).__init__(
- "Shard {0} under account 123456789012 not found.".format(shard_id)
+ "Shard {0} under account {1} not found.".format(shard_id, ACCOUNT_ID)
)
diff --git a/moto/kinesis/models.py b/moto/kinesis/models.py
index 38a622841..ec9655bfa 100644
--- a/moto/kinesis/models.py
+++ b/moto/kinesis/models.py
@@ -2,7 +2,6 @@ from __future__ import unicode_literals
import datetime
import time
-import boto.kinesis
import re
import six
import itertools
@@ -10,9 +9,12 @@ import itertools
from operator import attrgetter
from hashlib import md5
+from boto3 import Session
+
from moto.compat import OrderedDict
from moto.core import BaseBackend, BaseModel
from moto.core.utils import unix_time
+from moto.core import ACCOUNT_ID
from .exceptions import (
StreamNotFoundError,
ShardNotFoundError,
@@ -133,7 +135,7 @@ class Stream(BaseModel):
self.shard_count = shard_count
self.creation_datetime = datetime.datetime.now()
self.region = region
- self.account_number = "123456789012"
+ self.account_number = ACCOUNT_ID
self.shards = {}
self.tags = {}
self.status = "ACTIVE"
@@ -259,8 +261,8 @@ class DeliveryStream(BaseModel):
@property
def arn(self):
- return "arn:aws:firehose:us-east-1:123456789012:deliverystream/{0}".format(
- self.name
+ return "arn:aws:firehose:us-east-1:{1}:deliverystream/{0}".format(
+ self.name, ACCOUNT_ID
)
def destinations_to_dict(self):
@@ -529,5 +531,9 @@ class KinesisBackend(BaseBackend):
kinesis_backends = {}
-for region in boto.kinesis.regions():
- kinesis_backends[region.name] = KinesisBackend()
+for region in Session().get_available_regions("kinesis"):
+ kinesis_backends[region] = KinesisBackend()
+for region in Session().get_available_regions("kinesis", partition_name="aws-us-gov"):
+ kinesis_backends[region] = KinesisBackend()
+for region in Session().get_available_regions("kinesis", partition_name="aws-cn"):
+ kinesis_backends[region] = KinesisBackend()
diff --git a/moto/kms/models.py b/moto/kms/models.py
index 9d7739779..22f0039b2 100644
--- a/moto/kms/models.py
+++ b/moto/kms/models.py
@@ -4,7 +4,7 @@ import os
from collections import defaultdict
from datetime import datetime, timedelta
-import boto.kms
+from boto3 import Session
from moto.core import BaseBackend, BaseModel
from moto.core.utils import iso_8601_datetime_without_milliseconds
@@ -284,5 +284,9 @@ class KmsBackend(BaseBackend):
kms_backends = {}
-for region in boto.kms.regions():
- kms_backends[region.name] = KmsBackend()
+for region in Session().get_available_regions("kms"):
+ kms_backends[region] = KmsBackend()
+for region in Session().get_available_regions("kms", partition_name="aws-us-gov"):
+ kms_backends[region] = KmsBackend()
+for region in Session().get_available_regions("kms", partition_name="aws-cn"):
+ kms_backends[region] = KmsBackend()
diff --git a/moto/logs/models.py b/moto/logs/models.py
index d0639524e..7448319db 100644
--- a/moto/logs/models.py
+++ b/moto/logs/models.py
@@ -1,5 +1,6 @@
+from boto3 import Session
+
from moto.core import BaseBackend
-import boto.logs
from moto.core.utils import unix_time_millis
from .exceptions import (
ResourceNotFoundException,
@@ -558,6 +559,10 @@ class LogsBackend(BaseBackend):
log_group.untag(tags)
-logs_backends = {
- region.name: LogsBackend(region.name) for region in boto.logs.regions()
-}
+logs_backends = {}
+for region in Session().get_available_regions("logs"):
+ logs_backends[region] = LogsBackend(region)
+for region in Session().get_available_regions("logs", partition_name="aws-us-gov"):
+ logs_backends[region] = LogsBackend(region)
+for region in Session().get_available_regions("logs", partition_name="aws-cn"):
+ logs_backends[region] = LogsBackend(region)
diff --git a/moto/opsworks/models.py b/moto/opsworks/models.py
index 336bbde14..96d918cc9 100644
--- a/moto/opsworks/models.py
+++ b/moto/opsworks/models.py
@@ -1,6 +1,7 @@
from __future__ import unicode_literals
from moto.core import BaseBackend, BaseModel
from moto.ec2 import ec2_backends
+from moto.core import ACCOUNT_ID
import uuid
import datetime
from random import choice
@@ -367,7 +368,7 @@ class Stack(BaseModel):
self.id = "{0}".format(uuid.uuid4())
self.layers = []
self.apps = []
- self.account_number = "123456789012"
+ self.account_number = ACCOUNT_ID
self.created_at = datetime.datetime.utcnow()
def __eq__(self, other):
diff --git a/moto/organizations/utils.py b/moto/organizations/utils.py
index dacd58502..e71357ce6 100644
--- a/moto/organizations/utils.py
+++ b/moto/organizations/utils.py
@@ -2,8 +2,10 @@ from __future__ import unicode_literals
import random
import string
+from moto.core import ACCOUNT_ID
-MASTER_ACCOUNT_ID = "123456789012"
+
+MASTER_ACCOUNT_ID = ACCOUNT_ID
MASTER_ACCOUNT_EMAIL = "master@example.com"
DEFAULT_POLICY_ID = "p-FullAWSAccess"
ORGANIZATION_ARN_FORMAT = "arn:aws:organizations::{0}:organization/{1}"
diff --git a/moto/polly/models.py b/moto/polly/models.py
index 3be5b7a0b..cf4c8ab03 100644
--- a/moto/polly/models.py
+++ b/moto/polly/models.py
@@ -2,13 +2,14 @@ from __future__ import unicode_literals
from xml.etree import ElementTree as ET
import datetime
-import boto3
+from boto3 import Session
+
from moto.core import BaseBackend, BaseModel
from .resources import VOICE_DATA
from .utils import make_arn_for_lexicon
-DEFAULT_ACCOUNT_ID = 123456789012
+from moto.core import ACCOUNT_ID as DEFAULT_ACCOUNT_ID
class Lexicon(BaseModel):
@@ -113,7 +114,10 @@ class PollyBackend(BaseBackend):
self._lexicons[name] = lexicon
-available_regions = boto3.session.Session().get_available_regions("polly")
-polly_backends = {
- region: PollyBackend(region_name=region) for region in available_regions
-}
+polly_backends = {}
+for region in Session().get_available_regions("polly"):
+ polly_backends[region] = PollyBackend(region)
+for region in Session().get_available_regions("polly", partition_name="aws-us-gov"):
+ polly_backends[region] = PollyBackend(region)
+for region in Session().get_available_regions("polly", partition_name="aws-cn"):
+ polly_backends[region] = PollyBackend(region)
diff --git a/moto/rds2/models.py b/moto/rds2/models.py
index 686d22ccf..e648765b7 100644
--- a/moto/rds2/models.py
+++ b/moto/rds2/models.py
@@ -5,7 +5,7 @@ import datetime
import os
from collections import defaultdict
-import boto.rds2
+from boto3 import Session
from jinja2 import Template
from re import compile as re_compile
from moto.cloudformation.exceptions import UnformattedGetAttTemplateException
@@ -1501,6 +1501,10 @@ class DBParameterGroup(object):
return db_parameter_group
-rds2_backends = dict(
- (region.name, RDS2Backend(region.name)) for region in boto.rds2.regions()
-)
+rds2_backends = {}
+for region in Session().get_available_regions("rds"):
+ rds2_backends[region] = RDS2Backend(region)
+for region in Session().get_available_regions("rds", partition_name="aws-us-gov"):
+ rds2_backends[region] = RDS2Backend(region)
+for region in Session().get_available_regions("rds", partition_name="aws-cn"):
+ rds2_backends[region] = RDS2Backend(region)
diff --git a/moto/redshift/models.py b/moto/redshift/models.py
index 3eac565f8..17840fb86 100644
--- a/moto/redshift/models.py
+++ b/moto/redshift/models.py
@@ -3,7 +3,7 @@ from __future__ import unicode_literals
import copy
import datetime
-import boto.redshift
+from boto3 import Session
from botocore.exceptions import ClientError
from moto.compat import OrderedDict
from moto.core import BaseBackend, BaseModel
@@ -27,7 +27,7 @@ from .exceptions import (
)
-ACCOUNT_ID = 123456789012
+from moto.core import ACCOUNT_ID
class TaggableResourceMixin(object):
@@ -897,7 +897,9 @@ class RedshiftBackend(BaseBackend):
redshift_backends = {}
-for region in boto.redshift.regions():
- redshift_backends[region.name] = RedshiftBackend(
- ec2_backends[region.name], region.name
- )
+for region in Session().get_available_regions("redshift"):
+ redshift_backends[region] = RedshiftBackend(ec2_backends[region], region)
+for region in Session().get_available_regions("redshift", partition_name="aws-us-gov"):
+ redshift_backends[region] = RedshiftBackend(ec2_backends[region], region)
+for region in Session().get_available_regions("redshift", partition_name="aws-cn"):
+ redshift_backends[region] = RedshiftBackend(ec2_backends[region], region)
diff --git a/moto/resourcegroups/models.py b/moto/resourcegroups/models.py
index 5dd54d197..4dd96408a 100644
--- a/moto/resourcegroups/models.py
+++ b/moto/resourcegroups/models.py
@@ -1,11 +1,13 @@
from __future__ import unicode_literals
from builtins import str
-import boto3
import json
import re
+from boto3 import Session
+
from moto.core import BaseBackend, BaseModel
+from moto.core import ACCOUNT_ID
from .exceptions import BadRequestException
@@ -23,8 +25,8 @@ class FakeResourceGroup(BaseModel):
if self._validate_tags(value=tags):
self._tags = tags
self._raise_errors()
- self.arn = "arn:aws:resource-groups:us-west-1:123456789012:{name}".format(
- name=name
+ self.arn = "arn:aws:resource-groups:us-west-1:{AccountId}:{name}".format(
+ name=name, AccountId=ACCOUNT_ID
)
@staticmethod
@@ -349,7 +351,14 @@ class ResourceGroupsBackend(BaseBackend):
return self.groups.by_name[group_name]
-available_regions = boto3.session.Session().get_available_regions("resource-groups")
-resourcegroups_backends = {
- region: ResourceGroupsBackend(region_name=region) for region in available_regions
-}
+resourcegroups_backends = {}
+for region in Session().get_available_regions("resource-groups"):
+ resourcegroups_backends[region] = ResourceGroupsBackend(region)
+for region in Session().get_available_regions(
+ "resource-groups", partition_name="aws-us-gov"
+):
+ resourcegroups_backends[region] = ResourceGroupsBackend(region)
+for region in Session().get_available_regions(
+ "resource-groups", partition_name="aws-cn"
+):
+ resourcegroups_backends[region] = ResourceGroupsBackend(region)
diff --git a/moto/resourcegroupstaggingapi/models.py b/moto/resourcegroupstaggingapi/models.py
index 7b0c03a88..850ab5c04 100644
--- a/moto/resourcegroupstaggingapi/models.py
+++ b/moto/resourcegroupstaggingapi/models.py
@@ -1,7 +1,8 @@
from __future__ import unicode_literals
import uuid
-import boto3
import six
+from boto3 import Session
+
from moto.core import BaseBackend
from moto.core.exceptions import RESTError
@@ -636,9 +637,14 @@ class ResourceGroupsTaggingAPIBackend(BaseBackend):
# return failed_resources_map
-available_regions = boto3.session.Session().get_available_regions(
- "resourcegroupstaggingapi"
-)
-resourcegroupstaggingapi_backends = {
- region: ResourceGroupsTaggingAPIBackend(region) for region in available_regions
-}
+resourcegroupstaggingapi_backends = {}
+for region in Session().get_available_regions("resourcegroupstaggingapi"):
+ resourcegroupstaggingapi_backends[region] = ResourceGroupsTaggingAPIBackend(region)
+for region in Session().get_available_regions(
+ "resourcegroupstaggingapi", partition_name="aws-us-gov"
+):
+ resourcegroupstaggingapi_backends[region] = ResourceGroupsTaggingAPIBackend(region)
+for region in Session().get_available_regions(
+ "resourcegroupstaggingapi", partition_name="aws-cn"
+):
+ resourcegroupstaggingapi_backends[region] = ResourceGroupsTaggingAPIBackend(region)
diff --git a/moto/s3/exceptions.py b/moto/s3/exceptions.py
index c8236398f..1f2ead639 100644
--- a/moto/s3/exceptions.py
+++ b/moto/s3/exceptions.py
@@ -323,3 +323,27 @@ class BucketSignatureDoesNotMatchError(S3ClientError):
*args,
**kwargs
)
+
+
+class NoSuchPublicAccessBlockConfiguration(S3ClientError):
+ code = 404
+
+ def __init__(self, *args, **kwargs):
+ super(NoSuchPublicAccessBlockConfiguration, self).__init__(
+ "NoSuchPublicAccessBlockConfiguration",
+ "The public access block configuration was not found",
+ *args,
+ **kwargs
+ )
+
+
+class InvalidPublicAccessBlockConfiguration(S3ClientError):
+ code = 400
+
+ def __init__(self, *args, **kwargs):
+ super(InvalidPublicAccessBlockConfiguration, self).__init__(
+ "InvalidRequest",
+ "Must specify at least one configuration.",
+ *args,
+ **kwargs
+ )
diff --git a/moto/s3/models.py b/moto/s3/models.py
index 9c8f64242..fe8e908ef 100644
--- a/moto/s3/models.py
+++ b/moto/s3/models.py
@@ -35,6 +35,8 @@ from .exceptions import (
InvalidTargetBucketForLogging,
DuplicateTagKeys,
CrossLocationLoggingProhibitted,
+ NoSuchPublicAccessBlockConfiguration,
+ InvalidPublicAccessBlockConfiguration,
)
from .utils import clean_key_name, _VersionedKeyStore
@@ -659,11 +661,8 @@ class Notification(BaseModel):
else:
data["filter"] = None
- data[
- "objectPrefixes"
- ] = (
- []
- ) # Not sure why this is a thing since AWS just seems to return this as filters ¯\_(ツ)_/¯
+ # Not sure why this is a thing since AWS just seems to return this as filters ¯\_(ツ)_/¯
+ data["objectPrefixes"] = []
return data
@@ -728,6 +727,38 @@ class NotificationConfiguration(BaseModel):
return data
+def convert_str_to_bool(item):
+ """Converts a boolean string to a boolean value"""
+ if isinstance(item, str):
+ return item.lower() == "true"
+
+ return False
+
+
+class PublicAccessBlock(BaseModel):
+ def __init__(
+ self,
+ block_public_acls,
+ ignore_public_acls,
+ block_public_policy,
+ restrict_public_buckets,
+ ):
+ # The boto XML appears to expect these values to exist as lowercase strings...
+ self.block_public_acls = block_public_acls or "false"
+ self.ignore_public_acls = ignore_public_acls or "false"
+ self.block_public_policy = block_public_policy or "false"
+ self.restrict_public_buckets = restrict_public_buckets or "false"
+
+ def to_config_dict(self):
+ # Need to make the string values booleans for Config:
+ return {
+ "blockPublicAcls": convert_str_to_bool(self.block_public_acls),
+ "ignorePublicAcls": convert_str_to_bool(self.ignore_public_acls),
+ "blockPublicPolicy": convert_str_to_bool(self.block_public_policy),
+ "restrictPublicBuckets": convert_str_to_bool(self.restrict_public_buckets),
+ }
+
+
class FakeBucket(BaseModel):
def __init__(self, name, region_name):
self.name = name
@@ -746,6 +777,7 @@ class FakeBucket(BaseModel):
self.accelerate_configuration = None
self.payer = "BucketOwner"
self.creation_date = datetime.datetime.utcnow()
+ self.public_access_block = None
@property
def location(self):
@@ -1079,13 +1111,16 @@ class FakeBucket(BaseModel):
}
# Make the supplementary configuration:
- # TODO: Implement Public Access Block Support
-
# This is a dobule-wrapped JSON for some reason...
s_config = {
"AccessControlList": json.dumps(json.dumps(self.acl.to_config_dict()))
}
+ if self.public_access_block:
+ s_config["PublicAccessBlockConfiguration"] = json.dumps(
+ self.public_access_block.to_config_dict()
+ )
+
# Tagging is special:
if config_dict["tags"]:
s_config["BucketTaggingConfiguration"] = json.dumps(
@@ -1221,6 +1256,14 @@ class S3Backend(BaseBackend):
bucket = self.get_bucket(bucket_name)
return bucket.website_configuration
+ def get_bucket_public_access_block(self, bucket_name):
+ bucket = self.get_bucket(bucket_name)
+
+ if not bucket.public_access_block:
+ raise NoSuchPublicAccessBlockConfiguration()
+
+ return bucket.public_access_block
+
def set_key(
self, bucket_name, key_name, value, storage=None, etag=None, multipart=None
):
@@ -1309,6 +1352,10 @@ class S3Backend(BaseBackend):
bucket = self.get_bucket(bucket_name)
bucket.delete_cors()
+ def delete_bucket_public_access_block(self, bucket_name):
+ bucket = self.get_bucket(bucket_name)
+ bucket.public_access_block = None
+
def put_bucket_notification_configuration(self, bucket_name, notification_config):
bucket = self.get_bucket(bucket_name)
bucket.set_notification_configuration(notification_config)
@@ -1324,6 +1371,19 @@ class S3Backend(BaseBackend):
raise InvalidRequest("PutBucketAccelerateConfiguration")
bucket.set_accelerate_configuration(accelerate_configuration)
+ def put_bucket_public_access_block(self, bucket_name, pub_block_config):
+ bucket = self.get_bucket(bucket_name)
+
+ if not pub_block_config:
+ raise InvalidPublicAccessBlockConfiguration()
+
+ bucket.public_access_block = PublicAccessBlock(
+ pub_block_config.get("BlockPublicAcls"),
+ pub_block_config.get("IgnorePublicAcls"),
+ pub_block_config.get("BlockPublicPolicy"),
+ pub_block_config.get("RestrictPublicBuckets"),
+ )
+
def initiate_multipart(self, bucket_name, key_name, metadata):
bucket = self.get_bucket(bucket_name)
new_multipart = FakeMultipart(key_name, metadata)
diff --git a/moto/s3/responses.py b/moto/s3/responses.py
index fd3a7b2db..71f21c8e1 100644
--- a/moto/s3/responses.py
+++ b/moto/s3/responses.py
@@ -1,10 +1,11 @@
from __future__ import unicode_literals
import re
+import sys
import six
-from moto.core.utils import str_to_rfc_1123_datetime
+from moto.core.utils import str_to_rfc_1123_datetime, py2_strip_unicode_keys
from six.moves.urllib.parse import parse_qs, urlparse, unquote
import xmltodict
@@ -12,6 +13,7 @@ import xmltodict
from moto.packages.httpretty.core import HTTPrettyRequest
from moto.core.responses import _TemplateEnvironmentMixin, ActionAuthenticatorMixin
from moto.core.utils import path_url
+from moto.core import ACCOUNT_ID
from moto.s3bucket_path.utils import (
bucket_name_from_url as bucketpath_bucket_name_from_url,
@@ -70,6 +72,7 @@ ACTION_MAP = {
"notification": "GetBucketNotification",
"accelerate": "GetAccelerateConfiguration",
"versions": "ListBucketVersions",
+ "public_access_block": "GetPublicAccessBlock",
"DEFAULT": "ListBucket",
},
"PUT": {
@@ -83,6 +86,7 @@ ACTION_MAP = {
"cors": "PutBucketCORS",
"notification": "PutBucketNotification",
"accelerate": "PutAccelerateConfiguration",
+ "public_access_block": "PutPublicAccessBlock",
"DEFAULT": "CreateBucket",
},
"DELETE": {
@@ -90,6 +94,7 @@ ACTION_MAP = {
"policy": "DeleteBucketPolicy",
"tagging": "PutBucketTagging",
"cors": "PutBucketCORS",
+ "public_access_block": "DeletePublicAccessBlock",
"DEFAULT": "DeleteBucket",
},
},
@@ -399,6 +404,12 @@ class ResponseObject(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
return 200, {}, template.render()
template = self.response_template(S3_BUCKET_ACCELERATE)
return template.render(bucket=bucket)
+ elif "publicAccessBlock" in querystring:
+ public_block_config = self.backend.get_bucket_public_access_block(
+ bucket_name
+ )
+ template = self.response_template(S3_PUBLIC_ACCESS_BLOCK_CONFIGURATION)
+ return template.render(public_block_config=public_block_config)
elif "versions" in querystring:
delimiter = querystring.get("delimiter", [None])[0]
@@ -651,6 +662,23 @@ class ResponseObject(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
except Exception as e:
raise e
+ elif "publicAccessBlock" in querystring:
+ parsed_xml = xmltodict.parse(body)
+ parsed_xml["PublicAccessBlockConfiguration"].pop("@xmlns", None)
+
+ # If Python 2, fix the unicode strings:
+ if sys.version_info[0] < 3:
+ parsed_xml = {
+ "PublicAccessBlockConfiguration": py2_strip_unicode_keys(
+ dict(parsed_xml["PublicAccessBlockConfiguration"])
+ )
+ }
+
+ self.backend.put_bucket_public_access_block(
+ bucket_name, parsed_xml["PublicAccessBlockConfiguration"]
+ )
+ return ""
+
else:
if body:
# us-east-1, the default AWS region behaves a bit differently
@@ -706,6 +734,9 @@ class ResponseObject(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
bucket = self.backend.get_bucket(bucket_name)
bucket.delete_lifecycle()
return 204, {}, ""
+ elif "publicAccessBlock" in querystring:
+ self.backend.delete_bucket_public_access_block(bucket_name)
+ return 204, {}, ""
removed_bucket = self.backend.delete_bucket(bucket_name)
@@ -1460,7 +1491,9 @@ S3_ALL_BUCKETS = """
{{ bucket.name }}
+ {% if prefix != None %}
{{ prefix }}
+ {% endif %}
{{ max_keys }}
{{ delimiter }}
{{ is_truncated }}
@@ -1492,7 +1525,9 @@ S3_BUCKET_GET_RESPONSE = """
S3_BUCKET_GET_RESPONSE_V2 = """
{{ bucket.name }}
+{% if prefix != None %}
{{ prefix }}
+{% endif %}
{{ max_keys }}
{{ key_count }}
{% if delimiter %}
@@ -1653,7 +1688,9 @@ S3_BUCKET_GET_VERSIONING = """
S3_BUCKET_GET_VERSIONS = """
{{ bucket.name }}
+ {% if prefix != None %}
{{ prefix }}
+ {% endif %}
{{ key_marker }}
{{ max_keys }}
{{ is_truncated }}
@@ -1856,7 +1893,8 @@ S3_MULTIPART_COMPLETE_RESPONSE = """
"""
-S3_ALL_MULTIPARTS = """
+S3_ALL_MULTIPARTS = (
+ """
{{ bucket_name }}
@@ -1868,7 +1906,9 @@ S3_ALL_MULTIPARTS = """
{{ upload.key_name }}
{{ upload.id }}
- arn:aws:iam::123456789012:user/user1-11111a31-17b5-4fb7-9df5-b111111f13de
+ arn:aws:iam::"""
+ + ACCOUNT_ID
+ + """:user/user1-11111a31-17b5-4fb7-9df5-b111111f13de
user1-11111a31-17b5-4fb7-9df5-b111111f13de
@@ -1881,6 +1921,7 @@ S3_ALL_MULTIPARTS = """
{% endfor %}
"""
+)
S3_NO_POLICY = """
@@ -2053,3 +2094,12 @@ S3_BUCKET_ACCELERATE = """
S3_BUCKET_ACCELERATE_NOT_SET = """
"""
+
+S3_PUBLIC_ACCESS_BLOCK_CONFIGURATION = """
+
+ {{public_block_config.block_public_acls}}
+ {{public_block_config.ignore_public_acls}}
+ {{public_block_config.block_public_policy}}
+ {{public_block_config.restrict_public_buckets}}
+
+"""
diff --git a/moto/secretsmanager/models.py b/moto/secretsmanager/models.py
index 2a1a336d9..294a6401e 100644
--- a/moto/secretsmanager/models.py
+++ b/moto/secretsmanager/models.py
@@ -6,7 +6,7 @@ import json
import uuid
import datetime
-import boto3
+from boto3 import Session
from moto.core import BaseBackend, BaseModel
from .exceptions import (
@@ -491,7 +491,14 @@ class SecretsManagerBackend(BaseBackend):
)
-available_regions = boto3.session.Session().get_available_regions("secretsmanager")
-secretsmanager_backends = {
- region: SecretsManagerBackend(region_name=region) for region in available_regions
-}
+secretsmanager_backends = {}
+for region in Session().get_available_regions("secretsmanager"):
+ secretsmanager_backends[region] = SecretsManagerBackend(region_name=region)
+for region in Session().get_available_regions(
+ "secretsmanager", partition_name="aws-us-gov"
+):
+ secretsmanager_backends[region] = SecretsManagerBackend(region_name=region)
+for region in Session().get_available_regions(
+ "secretsmanager", partition_name="aws-cn"
+):
+ secretsmanager_backends[region] = SecretsManagerBackend(region_name=region)
diff --git a/moto/ses/feedback.py b/moto/ses/feedback.py
index c3d630e59..b0fa293e7 100644
--- a/moto/ses/feedback.py
+++ b/moto/ses/feedback.py
@@ -1,3 +1,5 @@
+from moto.core import ACCOUNT_ID
+
"""
SES Feedback messages
Extracted from https://docs.aws.amazon.com/ses/latest/DeveloperGuide/notification-contents.html
@@ -10,7 +12,7 @@ COMMON_MAIL = {
"source": "sender@example.com",
"sourceArn": "arn:aws:ses:us-west-2:888888888888:identity/example.com",
"sourceIp": "127.0.3.0",
- "sendingAccountId": "123456789012",
+ "sendingAccountId": ACCOUNT_ID,
"destination": ["recipient@example.com"],
"headersTruncated": False,
"headers": [
diff --git a/moto/sns/models.py b/moto/sns/models.py
index 8b125358d..d6791eecf 100644
--- a/moto/sns/models.py
+++ b/moto/sns/models.py
@@ -31,7 +31,8 @@ from .exceptions import (
)
from .utils import make_arn_for_topic, make_arn_for_subscription, is_e164
-DEFAULT_ACCOUNT_ID = 123456789012
+from moto.core import ACCOUNT_ID as DEFAULT_ACCOUNT_ID
+
DEFAULT_PAGE_SIZE = 100
MAXIMUM_MESSAGE_LENGTH = 262144 # 256 KiB
@@ -259,7 +260,9 @@ class Subscription(BaseModel):
"SignatureVersion": "1",
"Signature": "EXAMPLElDMXvB8r9R83tGoNn0ecwd5UjllzsvSvbItzfaMpN2nk5HVSw7XnOn/49IkxDKz8YrlH2qJXj2iZB0Zo2O71c4qQk1fMUDi3LGpij7RCW7AW9vYYsSqIKRnFS94ilu7NFhUzLiieYr4BKHpdTmdD6c0esKEYBpabxDSc=",
"SigningCertURL": "https://sns.us-east-1.amazonaws.com/SimpleNotificationService-f3ecfb7224c7233fe7bb5f59f96de52f.pem",
- "UnsubscribeURL": "https://sns.us-east-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-east-1:123456789012:some-topic:2bcfbf39-05c3-41de-beaa-fcfcc21c8f55",
+ "UnsubscribeURL": "https://sns.us-east-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-east-1:{}:some-topic:2bcfbf39-05c3-41de-beaa-fcfcc21c8f55".format(
+ DEFAULT_ACCOUNT_ID
+ ),
}
if message_attributes:
post_data["MessageAttributes"] = message_attributes
@@ -275,8 +278,11 @@ class PlatformApplication(BaseModel):
@property
def arn(self):
- return "arn:aws:sns:{region}:123456789012:app/{platform}/{name}".format(
- region=self.region, platform=self.platform, name=self.name
+ return "arn:aws:sns:{region}:{AccountId}:app/{platform}/{name}".format(
+ region=self.region,
+ platform=self.platform,
+ name=self.name,
+ AccountId=DEFAULT_ACCOUNT_ID,
)
@@ -305,8 +311,9 @@ class PlatformEndpoint(BaseModel):
@property
def arn(self):
- return "arn:aws:sns:{region}:123456789012:endpoint/{platform}/{name}/{id}".format(
+ return "arn:aws:sns:{region}:{AccountId}:endpoint/{platform}/{name}/{id}".format(
region=self.region,
+ AccountId=DEFAULT_ACCOUNT_ID,
platform=self.application.platform,
name=self.application.name,
id=self.id,
@@ -390,10 +397,6 @@ class SNSBackend(BaseBackend):
return self._get_values_nexttoken(self.topics, next_token)
def delete_topic(self, arn):
- topic = self.get_topic(arn)
- subscriptions = self._get_topic_subscriptions(topic)
- for sub in subscriptions:
- self.unsubscribe(sub.arn)
self.topics.pop(arn)
def get_topic(self, arn):
@@ -432,11 +435,18 @@ class SNSBackend(BaseBackend):
subscription = Subscription(topic, endpoint, protocol)
attributes = {
"PendingConfirmation": "false",
+ "ConfirmationWasAuthenticated": "true",
"Endpoint": endpoint,
"TopicArn": topic_arn,
"Protocol": protocol,
"SubscriptionArn": subscription.arn,
+ "Owner": DEFAULT_ACCOUNT_ID,
+ "RawMessageDelivery": "false",
}
+
+ if protocol in ["http", "https"]:
+ attributes["EffectiveDeliveryPolicy"] = topic.effective_delivery_policy
+
subscription.attributes = attributes
self.subscriptions[subscription.arn] = subscription
return subscription
@@ -452,7 +462,7 @@ class SNSBackend(BaseBackend):
return None
def unsubscribe(self, subscription_arn):
- self.subscriptions.pop(subscription_arn)
+ self.subscriptions.pop(subscription_arn, None)
def list_subscriptions(self, topic_arn=None, next_token=None):
if topic_arn:
@@ -693,21 +703,25 @@ class SNSBackend(BaseBackend):
sns_backends = {}
for region in Session().get_available_regions("sns"):
sns_backends[region] = SNSBackend(region)
+for region in Session().get_available_regions("sns", partition_name="aws-us-gov"):
+ sns_backends[region] = SNSBackend(region)
+for region in Session().get_available_regions("sns", partition_name="aws-cn"):
+ sns_backends[region] = SNSBackend(region)
DEFAULT_EFFECTIVE_DELIVERY_POLICY = {
- "http": {
- "disableSubscriptionOverrides": False,
- "defaultHealthyRetryPolicy": {
- "numNoDelayRetries": 0,
- "numMinDelayRetries": 0,
- "minDelayTarget": 20,
- "maxDelayTarget": 20,
- "numMaxDelayRetries": 0,
- "numRetries": 3,
- "backoffFunction": "linear",
- },
- }
+ "defaultHealthyRetryPolicy": {
+ "numNoDelayRetries": 0,
+ "numMinDelayRetries": 0,
+ "minDelayTarget": 20,
+ "maxDelayTarget": 20,
+ "numMaxDelayRetries": 0,
+ "numRetries": 3,
+ "backoffFunction": "linear",
+ },
+ "sicklyRetryPolicy": None,
+ "throttlePolicy": None,
+ "guaranteed": False,
}
diff --git a/moto/sqs/models.py b/moto/sqs/models.py
index ca3d41f38..40dd6ba97 100644
--- a/moto/sqs/models.py
+++ b/moto/sqs/models.py
@@ -8,7 +8,7 @@ import six
import struct
from xml.sax.saxutils import escape
-import boto.sqs
+from boto3 import Session
from moto.core.exceptions import RESTError
from moto.core import BaseBackend, BaseModel
@@ -32,7 +32,8 @@ from .exceptions import (
InvalidAttributeName,
)
-DEFAULT_ACCOUNT_ID = 123456789012
+from moto.core import ACCOUNT_ID as DEFAULT_ACCOUNT_ID
+
DEFAULT_SENDER_ID = "AIDAIT2UOQQY3AUEKVGXU"
MAXIMUM_MESSAGE_LENGTH = 262144 # 256 KiB
@@ -417,8 +418,8 @@ class Queue(BaseModel):
return result
def url(self, request_url):
- return "{0}://{1}/123456789012/{2}".format(
- request_url.scheme, request_url.netloc, self.name
+ return "{0}://{1}/{2}/{3}".format(
+ request_url.scheme, request_url.netloc, DEFAULT_ACCOUNT_ID, self.name
)
@property
@@ -856,5 +857,9 @@ class SQSBackend(BaseBackend):
sqs_backends = {}
-for region in boto.sqs.regions():
- sqs_backends[region.name] = SQSBackend(region.name)
+for region in Session().get_available_regions("sqs"):
+ sqs_backends[region] = SQSBackend(region)
+for region in Session().get_available_regions("sqs", partition_name="aws-us-gov"):
+ sqs_backends[region] = SQSBackend(region)
+for region in Session().get_available_regions("sqs", partition_name="aws-cn"):
+ sqs_backends[region] = SQSBackend(region)
diff --git a/moto/ssm/models.py b/moto/ssm/models.py
index 65b4d6743..60c47f021 100644
--- a/moto/ssm/models.py
+++ b/moto/ssm/models.py
@@ -13,6 +13,7 @@ import time
import uuid
import itertools
+from .utils import parameter_arn
from .exceptions import (
ValidationException,
InvalidFilterValue,
@@ -60,19 +61,23 @@ class Parameter(BaseModel):
if value.startswith(prefix):
return value[len(prefix) :]
- def response_object(self, decrypt=False):
+ def response_object(self, decrypt=False, region=None):
r = {
"Name": self.name,
"Type": self.type,
"Value": self.decrypt(self.value) if decrypt else self.value,
"Version": self.version,
+ "LastModifiedDate": round(self.last_modified_date, 3),
}
+ if region:
+ r["ARN"] = parameter_arn(region, self.name)
+
return r
def describe_response_object(self, decrypt=False):
r = self.response_object(decrypt)
- r["LastModifiedDate"] = int(self.last_modified_date)
+ r["LastModifiedDate"] = round(self.last_modified_date, 3)
r["LastModifiedUser"] = "N/A"
if self.description:
@@ -510,7 +515,15 @@ class SimpleSystemManagerBackend(BaseBackend):
result.append(self.get_parameter(name, with_decryption))
return result
- def get_parameters_by_path(self, path, with_decryption, recursive, filters=None):
+ def get_parameters_by_path(
+ self,
+ path,
+ with_decryption,
+ recursive,
+ filters=None,
+ next_token=None,
+ max_results=10,
+ ):
"""Implement the get-parameters-by-path-API in the backend."""
result = []
# path could be with or without a trailing /. we handle this
@@ -527,7 +540,19 @@ class SimpleSystemManagerBackend(BaseBackend):
continue
result.append(self.get_parameter(param_name, with_decryption))
- return result
+ return self._get_values_nexttoken(result, max_results, next_token)
+
+ def _get_values_nexttoken(self, values_list, max_results, next_token=None):
+ if next_token is None:
+ next_token = 0
+ next_token = int(next_token)
+ max_results = int(max_results)
+ values = values_list[next_token : next_token + max_results]
+ if len(values) == max_results:
+ next_token = str(next_token + max_results)
+ else:
+ next_token = None
+ return values, next_token
def get_parameter_history(self, name, with_decryption):
if name in self._parameters:
diff --git a/moto/ssm/responses.py b/moto/ssm/responses.py
index 29fd8820c..1b13780a8 100644
--- a/moto/ssm/responses.py
+++ b/moto/ssm/responses.py
@@ -51,7 +51,7 @@ class SimpleSystemManagerResponse(BaseResponse):
}
return json.dumps(error), dict(status=400)
- response = {"Parameter": result.response_object(with_decryption)}
+ response = {"Parameter": result.response_object(with_decryption, self.region)}
return json.dumps(response)
def get_parameters(self):
@@ -63,7 +63,7 @@ class SimpleSystemManagerResponse(BaseResponse):
response = {"Parameters": [], "InvalidParameters": []}
for parameter in result:
- param_data = parameter.response_object(with_decryption)
+ param_data = parameter.response_object(with_decryption, self.region)
response["Parameters"].append(param_data)
param_names = [param.name for param in result]
@@ -77,15 +77,22 @@ class SimpleSystemManagerResponse(BaseResponse):
with_decryption = self._get_param("WithDecryption")
recursive = self._get_param("Recursive", False)
filters = self._get_param("ParameterFilters")
+ token = self._get_param("NextToken")
+ max_results = self._get_param("MaxResults", 10)
- result = self.ssm_backend.get_parameters_by_path(
- path, with_decryption, recursive, filters
+ result, next_token = self.ssm_backend.get_parameters_by_path(
+ path,
+ with_decryption,
+ recursive,
+ filters,
+ next_token=token,
+ max_results=max_results,
)
- response = {"Parameters": []}
+ response = {"Parameters": [], "NextToken": next_token}
for parameter in result:
- param_data = parameter.response_object(with_decryption)
+ param_data = parameter.response_object(with_decryption, self.region)
response["Parameters"].append(param_data)
return json.dumps(response)
diff --git a/moto/ssm/utils.py b/moto/ssm/utils.py
new file mode 100644
index 000000000..3f3762d1c
--- /dev/null
+++ b/moto/ssm/utils.py
@@ -0,0 +1,9 @@
+ACCOUNT_ID = "1234567890"
+
+
+def parameter_arn(region, parameter_name):
+ if parameter_name[0] == "/":
+ parameter_name = parameter_name[1:]
+ return "arn:aws:ssm:{0}:{1}:parameter/{2}".format(
+ region, ACCOUNT_ID, parameter_name
+ )
diff --git a/moto/stepfunctions/models.py b/moto/stepfunctions/models.py
index 665f3b777..de530b863 100644
--- a/moto/stepfunctions/models.py
+++ b/moto/stepfunctions/models.py
@@ -1,6 +1,8 @@
-import boto
import re
from datetime import datetime
+
+from boto3 import Session
+
from moto.core import BaseBackend
from moto.core.utils import iso_8601_datetime_without_milliseconds
from moto.sts.models import ACCOUNT_ID
@@ -280,7 +282,12 @@ class StepFunctionBackend(BaseBackend):
return ACCOUNT_ID
-stepfunction_backends = {
- _region.name: StepFunctionBackend(_region.name)
- for _region in boto.awslambda.regions()
-}
+stepfunction_backends = {}
+for region in Session().get_available_regions("stepfunctions"):
+ stepfunction_backends[region] = StepFunctionBackend(region)
+for region in Session().get_available_regions(
+ "stepfunctions", partition_name="aws-us-gov"
+):
+ stepfunction_backends[region] = StepFunctionBackend(region)
+for region in Session().get_available_regions("stepfunctions", partition_name="aws-cn"):
+ stepfunction_backends[region] = StepFunctionBackend(region)
diff --git a/moto/sts/models.py b/moto/sts/models.py
index d3afc9904..12824b2ed 100644
--- a/moto/sts/models.py
+++ b/moto/sts/models.py
@@ -2,7 +2,7 @@ from __future__ import unicode_literals
import datetime
from moto.core import BaseBackend, BaseModel
from moto.core.utils import iso_8601_datetime_with_milliseconds
-from moto.iam.models import ACCOUNT_ID
+from moto.core import ACCOUNT_ID
from moto.sts.utils import (
random_access_key_id,
random_secret_access_key,
diff --git a/moto/sts/responses.py b/moto/sts/responses.py
index f6d8647c4..f36799b03 100644
--- a/moto/sts/responses.py
+++ b/moto/sts/responses.py
@@ -1,7 +1,7 @@
from __future__ import unicode_literals
from moto.core.responses import BaseResponse
-from moto.iam.models import ACCOUNT_ID
+from moto.core import ACCOUNT_ID
from moto.iam import iam_backend
from .exceptions import STSValidationError
from .models import sts_backend
diff --git a/moto/swf/models/__init__.py b/moto/swf/models/__init__.py
index 50cc29bb3..e5b285f5b 100644
--- a/moto/swf/models/__init__.py
+++ b/moto/swf/models/__init__.py
@@ -1,6 +1,6 @@
from __future__ import unicode_literals
-import boto.swf
+from boto3 import Session
from moto.core import BaseBackend
@@ -418,5 +418,9 @@ class SWFBackend(BaseBackend):
swf_backends = {}
-for region in boto.swf.regions():
- swf_backends[region.name] = SWFBackend(region.name)
+for region in Session().get_available_regions("swf"):
+ swf_backends[region] = SWFBackend(region)
+for region in Session().get_available_regions("swf", partition_name="aws-us-gov"):
+ swf_backends[region] = SWFBackend(region)
+for region in Session().get_available_regions("swf", partition_name="aws-cn"):
+ swf_backends[region] = SWFBackend(region)
diff --git a/requirements-dev.txt b/requirements-dev.txt
index 436a7a51b..c5f055a26 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -2,8 +2,9 @@
mock
nose
black; python_version >= '3.6'
+regex==2019.11.1; python_version >= '3.6' # Needed for black
sure==1.4.11
-coverage
+coverage==4.5.4
flake8==3.7.8
freezegun
flask
diff --git a/scripts/template/lib/models.py.j2 b/scripts/template/lib/models.py.j2
index 28fa4a4e1..84f8dad71 100644
--- a/scripts/template/lib/models.py.j2
+++ b/scripts/template/lib/models.py.j2
@@ -1,5 +1,5 @@
from __future__ import unicode_literals
-import boto3
+from boto3 import Session
from moto.core import BaseBackend, BaseModel
@@ -16,5 +16,10 @@ class {{ service_class }}Backend(BaseBackend):
# add methods from here
-available_regions = boto3.session.Session().get_available_regions("{{ service }}")
-{{ escaped_service }}_backends = {region: {{ service_class }}Backend(region) for region in available_regions}
+{{ escaped_service }}_backends = {}
+for region in Session().get_available_regions("{{ service }}"):
+ {{ escaped_service }}_backends[region] = {{ service_class }}Backend()
+for region in Session().get_available_regions("{{ service }}", partition_name="aws-us-gov"):
+ {{ escaped_service }}_backends[region] = {{ service_class }}Backend()
+for region in Session().get_available_regions("{{ service }}", partition_name="aws-cn"):
+ {{ escaped_service }}_backends[region] = {{ service_class }}Backend()
diff --git a/tests/test_acm/test_acm.py b/tests/test_acm/test_acm.py
index 6f879e55e..b38cd1843 100644
--- a/tests/test_acm/test_acm.py
+++ b/tests/test_acm/test_acm.py
@@ -9,6 +9,7 @@ import uuid
from botocore.exceptions import ClientError
from moto import mock_acm
+from moto.core import ACCOUNT_ID
RESOURCE_FOLDER = os.path.join(os.path.dirname(__file__), "resources")
@@ -19,7 +20,9 @@ SERVER_CRT = _GET_RESOURCE("star_moto_com.pem")
SERVER_COMMON_NAME = "*.moto.com"
SERVER_CRT_BAD = _GET_RESOURCE("star_moto_com-bad.pem")
SERVER_KEY = _GET_RESOURCE("star_moto_com.key")
-BAD_ARN = "arn:aws:acm:us-east-2:123456789012:certificate/_0000000-0000-0000-0000-000000000000"
+BAD_ARN = "arn:aws:acm:us-east-2:{}:certificate/_0000000-0000-0000-0000-000000000000".format(
+ ACCOUNT_ID
+)
def _import_cert(client):
diff --git a/tests/test_apigateway/test_apigateway.py b/tests/test_apigateway/test_apigateway.py
index 8da5efa39..59c0c07f6 100644
--- a/tests/test_apigateway/test_apigateway.py
+++ b/tests/test_apigateway/test_apigateway.py
@@ -9,6 +9,7 @@ from botocore.exceptions import ClientError
import responses
from moto import mock_apigateway, settings
+from moto.core import ACCOUNT_ID
from nose.tools import assert_raises
@@ -881,7 +882,9 @@ def test_put_integration_validation():
client.put_integration(
restApiId=api_id,
resourceId=root_id,
- credentials="arn:aws:iam::123456789012:role/service-role/testfunction-role-oe783psq",
+ credentials="arn:aws:iam::{}:role/service-role/testfunction-role-oe783psq".format(
+ ACCOUNT_ID
+ ),
httpMethod="GET",
type=type,
uri="arn:aws:apigateway:us-west-2:s3:path/b/k",
@@ -903,7 +906,9 @@ def test_put_integration_validation():
client.put_integration(
restApiId=api_id,
resourceId=root_id,
- credentials="arn:aws:iam::123456789012:role/service-role/testfunction-role-oe783psq",
+ credentials="arn:aws:iam::{}:role/service-role/testfunction-role-oe783psq".format(
+ ACCOUNT_ID
+ ),
httpMethod="GET",
type=type,
uri="arn:aws:apigateway:us-west-2:s3:path/b/k",
@@ -1130,6 +1135,23 @@ def test_http_proxying_integration():
requests.get(deploy_url).content.should.equal(b"a fake response")
+@mock_apigateway
+def test_create_api_key():
+ region_name = "us-west-2"
+ client = boto3.client("apigateway", region_name=region_name)
+
+ apikey_value = "12345"
+ apikey_name = "TESTKEY1"
+ payload = {"value": apikey_value, "name": apikey_name}
+
+ client.create_api_key(**payload)
+
+ response = client.get_api_keys()
+ len(response["items"]).should.equal(1)
+
+ client.create_api_key.when.called_with(**payload).should.throw(ClientError)
+
+
@mock_apigateway
def test_api_keys():
region_name = "us-west-2"
@@ -1139,26 +1161,18 @@ def test_api_keys():
apikey_value = "12345"
apikey_name = "TESTKEY1"
- payload = {"value": apikey_value, "name": apikey_name}
+ payload = {
+ "value": apikey_value,
+ "name": apikey_name,
+ "tags": {"tag1": "test_tag1", "tag2": "1"},
+ }
response = client.create_api_key(**payload)
+ apikey_id = response["id"]
apikey = client.get_api_key(apiKey=response["id"])
apikey["name"].should.equal(apikey_name)
apikey["value"].should.equal(apikey_value)
-
- apikey_name = "TESTKEY2"
- payload = {"name": apikey_name, "tags": {"tag1": "test_tag1", "tag2": "1"}}
- response = client.create_api_key(**payload)
- apikey_id = response["id"]
- apikey = client.get_api_key(apiKey=apikey_id)
- apikey["name"].should.equal(apikey_name)
apikey["tags"]["tag1"].should.equal("test_tag1")
apikey["tags"]["tag2"].should.equal("1")
- len(apikey["value"]).should.equal(40)
-
- apikey_name = "TESTKEY3"
- payload = {"name": apikey_name}
- response = client.create_api_key(**payload)
- apikey_id = response["id"]
patch_operations = [
{"op": "replace", "path": "/name", "value": "TESTKEY3_CHANGE"},
@@ -1172,13 +1186,25 @@ def test_api_keys():
response["description"].should.equal("APIKEY UPDATE TEST")
response["enabled"].should.equal(False)
+ updated_api_key = client.get_api_key(apiKey=apikey_id)
+ updated_api_key["name"].should.equal("TESTKEY3_CHANGE")
+ updated_api_key["customerId"].should.equal("12345")
+ updated_api_key["description"].should.equal("APIKEY UPDATE TEST")
+ updated_api_key["enabled"].should.equal(False)
+
response = client.get_api_keys()
- len(response["items"]).should.equal(3)
+ len(response["items"]).should.equal(1)
+
+ payload = {"name": apikey_name}
+ client.create_api_key(**payload)
+
+ response = client.get_api_keys()
+ len(response["items"]).should.equal(2)
client.delete_api_key(apiKey=apikey_id)
response = client.get_api_keys()
- len(response["items"]).should.equal(2)
+ len(response["items"]).should.equal(1)
@mock_apigateway
diff --git a/tests/test_autoscaling/test_launch_configurations.py b/tests/test_autoscaling/test_launch_configurations.py
index 8cd596ee7..ab2743f54 100644
--- a/tests/test_autoscaling/test_launch_configurations.py
+++ b/tests/test_autoscaling/test_launch_configurations.py
@@ -8,6 +8,7 @@ import sure # noqa
from moto import mock_autoscaling_deprecated
from moto import mock_autoscaling
+from moto.core import ACCOUNT_ID
from tests.helpers import requires_boto_gte
@@ -22,7 +23,9 @@ def test_create_launch_configuration():
security_groups=["default", "default2"],
user_data=b"This is some user_data",
instance_monitoring=True,
- instance_profile_name="arn:aws:iam::123456789012:instance-profile/testing",
+ instance_profile_name="arn:aws:iam::{}:instance-profile/testing".format(
+ ACCOUNT_ID
+ ),
spot_price=0.1,
)
conn.create_launch_configuration(config)
@@ -36,7 +39,7 @@ def test_create_launch_configuration():
launch_config.user_data.should.equal(b"This is some user_data")
launch_config.instance_monitoring.enabled.should.equal("true")
launch_config.instance_profile_name.should.equal(
- "arn:aws:iam::123456789012:instance-profile/testing"
+ "arn:aws:iam::{}:instance-profile/testing".format(ACCOUNT_ID)
)
launch_config.spot_price.should.equal(0.1)
@@ -71,7 +74,9 @@ def test_create_launch_configuration_with_block_device_mappings():
security_groups=["default", "default2"],
user_data=b"This is some user_data",
instance_monitoring=True,
- instance_profile_name="arn:aws:iam::123456789012:instance-profile/testing",
+ instance_profile_name="arn:aws:iam::{}:instance-profile/testing".format(
+ ACCOUNT_ID
+ ),
spot_price=0.1,
block_device_mappings=[block_device_mapping],
)
@@ -86,7 +91,7 @@ def test_create_launch_configuration_with_block_device_mappings():
launch_config.user_data.should.equal(b"This is some user_data")
launch_config.instance_monitoring.enabled.should.equal("true")
launch_config.instance_profile_name.should.equal(
- "arn:aws:iam::123456789012:instance-profile/testing"
+ "arn:aws:iam::{}:instance-profile/testing".format(ACCOUNT_ID)
)
launch_config.spot_price.should.equal(0.1)
len(launch_config.block_device_mappings).should.equal(3)
diff --git a/tests/test_awslambda/test_lambda.py b/tests/test_awslambda/test_lambda.py
index e0e8a8205..6fd97e325 100644
--- a/tests/test_awslambda/test_lambda.py
+++ b/tests/test_awslambda/test_lambda.py
@@ -306,8 +306,8 @@ def test_create_function_from_aws_bucket():
result.should.equal(
{
"FunctionName": "testFunction",
- "FunctionArn": "arn:aws:lambda:{}:123456789012:function:testFunction".format(
- _lambda_region
+ "FunctionArn": "arn:aws:lambda:{}:{}:function:testFunction".format(
+ _lambda_region, ACCOUNT_ID
),
"Runtime": "python2.7",
"Role": result["Role"],
@@ -353,8 +353,8 @@ def test_create_function_from_zipfile():
result.should.equal(
{
"FunctionName": "testFunction",
- "FunctionArn": "arn:aws:lambda:{}:123456789012:function:testFunction".format(
- _lambda_region
+ "FunctionArn": "arn:aws:lambda:{}:{}:function:testFunction".format(
+ _lambda_region, ACCOUNT_ID
),
"Runtime": "python2.7",
"Role": result["Role"],
@@ -431,7 +431,7 @@ def test_get_function():
result = conn.get_function(FunctionName="testFunction", Qualifier="$LATEST")
result["Configuration"]["Version"].should.equal("$LATEST")
result["Configuration"]["FunctionArn"].should.equal(
- "arn:aws:lambda:us-west-2:123456789012:function:testFunction:$LATEST"
+ "arn:aws:lambda:us-west-2:{}:function:testFunction:$LATEST".format(ACCOUNT_ID)
)
# Test get function when can't find function name
@@ -620,8 +620,8 @@ def test_list_create_list_get_delete_list():
"CodeSha256": hashlib.sha256(zip_content).hexdigest(),
"CodeSize": len(zip_content),
"Description": "test lambda function",
- "FunctionArn": "arn:aws:lambda:{}:123456789012:function:testFunction".format(
- _lambda_region
+ "FunctionArn": "arn:aws:lambda:{}:{}:function:testFunction".format(
+ _lambda_region, ACCOUNT_ID
),
"FunctionName": "testFunction",
"Handler": "lambda_function.lambda_handler",
@@ -749,16 +749,17 @@ def test_tags_not_found():
"""
conn = boto3.client("lambda", "us-west-2")
conn.list_tags.when.called_with(
- Resource="arn:aws:lambda:123456789012:function:not-found"
+ Resource="arn:aws:lambda:{}:function:not-found".format(ACCOUNT_ID)
).should.throw(botocore.client.ClientError)
conn.tag_resource.when.called_with(
- Resource="arn:aws:lambda:123456789012:function:not-found",
+ Resource="arn:aws:lambda:{}:function:not-found".format(ACCOUNT_ID),
Tags=dict(spam="eggs"),
).should.throw(botocore.client.ClientError)
conn.untag_resource.when.called_with(
- Resource="arn:aws:lambda:123456789012:function:not-found", TagKeys=["spam"]
+ Resource="arn:aws:lambda:{}:function:not-found".format(ACCOUNT_ID),
+ TagKeys=["spam"],
).should.throw(botocore.client.ClientError)
@@ -815,8 +816,8 @@ def test_get_function_created_with_zipfile():
"CodeSha256": hashlib.sha256(zip_content).hexdigest(),
"CodeSize": len(zip_content),
"Description": "test lambda function",
- "FunctionArn": "arn:aws:lambda:{}:123456789012:function:testFunction".format(
- _lambda_region
+ "FunctionArn": "arn:aws:lambda:{}:{}:function:testFunction".format(
+ _lambda_region, ACCOUNT_ID
),
"FunctionName": "testFunction",
"Handler": "lambda_function.handler",
@@ -921,18 +922,15 @@ def test_list_versions_by_function():
assert res["ResponseMetadata"]["HTTPStatusCode"] == 201
versions = conn.list_versions_by_function(FunctionName="testFunction")
assert len(versions["Versions"]) == 3
- assert (
- versions["Versions"][0]["FunctionArn"]
- == "arn:aws:lambda:us-west-2:123456789012:function:testFunction:$LATEST"
- )
- assert (
- versions["Versions"][1]["FunctionArn"]
- == "arn:aws:lambda:us-west-2:123456789012:function:testFunction:1"
- )
- assert (
- versions["Versions"][2]["FunctionArn"]
- == "arn:aws:lambda:us-west-2:123456789012:function:testFunction:2"
- )
+ assert versions["Versions"][0][
+ "FunctionArn"
+ ] == "arn:aws:lambda:us-west-2:{}:function:testFunction:$LATEST".format(ACCOUNT_ID)
+ assert versions["Versions"][1][
+ "FunctionArn"
+ ] == "arn:aws:lambda:us-west-2:{}:function:testFunction:1".format(ACCOUNT_ID)
+ assert versions["Versions"][2][
+ "FunctionArn"
+ ] == "arn:aws:lambda:us-west-2:{}:function:testFunction:2".format(ACCOUNT_ID)
conn.create_function(
FunctionName="testFunction_2",
@@ -947,9 +945,10 @@ def test_list_versions_by_function():
)
versions = conn.list_versions_by_function(FunctionName="testFunction_2")
assert len(versions["Versions"]) == 1
- assert (
- versions["Versions"][0]["FunctionArn"]
- == "arn:aws:lambda:us-west-2:123456789012:function:testFunction_2:$LATEST"
+ assert versions["Versions"][0][
+ "FunctionArn"
+ ] == "arn:aws:lambda:us-west-2:{}:function:testFunction_2:$LATEST".format(
+ ACCOUNT_ID
)
@@ -1426,8 +1425,8 @@ def test_update_function_zip():
"CodeSha256": hashlib.sha256(zip_content_two).hexdigest(),
"CodeSize": len(zip_content_two),
"Description": "test lambda function",
- "FunctionArn": "arn:aws:lambda:{}:123456789012:function:testFunctionZip:2".format(
- _lambda_region
+ "FunctionArn": "arn:aws:lambda:{}:{}:function:testFunctionZip:2".format(
+ _lambda_region, ACCOUNT_ID
),
"FunctionName": "testFunctionZip",
"Handler": "lambda_function.lambda_handler",
@@ -1488,8 +1487,8 @@ def test_update_function_s3():
"CodeSha256": hashlib.sha256(zip_content_two).hexdigest(),
"CodeSize": len(zip_content_two),
"Description": "test lambda function",
- "FunctionArn": "arn:aws:lambda:{}:123456789012:function:testFunctionS3:2".format(
- _lambda_region
+ "FunctionArn": "arn:aws:lambda:{}:{}:function:testFunctionS3:2".format(
+ _lambda_region, ACCOUNT_ID
),
"FunctionName": "testFunctionS3",
"Handler": "lambda_function.lambda_handler",
diff --git a/tests/test_batch/test_batch.py b/tests/test_batch/test_batch.py
index 691d90b6d..141d6b343 100644
--- a/tests/test_batch/test_batch.py
+++ b/tests/test_batch/test_batch.py
@@ -55,6 +55,10 @@ def _setup(ec2_client, iam_client):
RoleName="TestRole", AssumeRolePolicyDocument="some_policy"
)
iam_arn = resp["Role"]["Arn"]
+ iam_client.create_instance_profile(InstanceProfileName="TestRole")
+ iam_client.add_role_to_instance_profile(
+ InstanceProfileName="TestRole", RoleName="TestRole"
+ )
return vpc_id, subnet_id, sg_id, iam_arn
@@ -83,7 +87,7 @@ def test_create_managed_compute_environment():
"subnets": [subnet_id],
"securityGroupIds": [sg_id],
"ec2KeyPair": "string",
- "instanceRole": iam_arn,
+ "instanceRole": iam_arn.replace("role", "instance-profile"),
"tags": {"string": "string"},
"bidPercentage": 123,
"spotIamFleetRole": "string",
@@ -209,7 +213,7 @@ def test_delete_managed_compute_environment():
"subnets": [subnet_id],
"securityGroupIds": [sg_id],
"ec2KeyPair": "string",
- "instanceRole": iam_arn,
+ "instanceRole": iam_arn.replace("role", "instance-profile"),
"tags": {"string": "string"},
"bidPercentage": 123,
"spotIamFleetRole": "string",
@@ -608,6 +612,9 @@ def test_describe_task_definition():
resp = batch_client.describe_job_definitions(jobDefinitions=["sleep10", "test1"])
len(resp["jobDefinitions"]).should.equal(3)
+ for job_definition in resp["jobDefinitions"]:
+ job_definition["status"].should.equal("ACTIVE")
+
@mock_logs
@mock_ec2
diff --git a/tests/test_batch/test_cloudformation.py b/tests/test_batch/test_cloudformation.py
index a8b94f3a3..a6baedb38 100644
--- a/tests/test_batch/test_cloudformation.py
+++ b/tests/test_batch/test_cloudformation.py
@@ -51,6 +51,10 @@ def _setup(ec2_client, iam_client):
RoleName="TestRole", AssumeRolePolicyDocument="some_policy"
)
iam_arn = resp["Role"]["Arn"]
+ iam_client.create_instance_profile(InstanceProfileName="TestRole")
+ iam_client.add_role_to_instance_profile(
+ InstanceProfileName="TestRole", RoleName="TestRole"
+ )
return vpc_id, subnet_id, sg_id, iam_arn
@@ -78,7 +82,7 @@ def test_create_env_cf():
"InstanceTypes": ["optimal"],
"Subnets": [subnet_id],
"SecurityGroupIds": [sg_id],
- "InstanceRole": iam_arn,
+ "InstanceRole": iam_arn.replace("role", "instance-profile"),
},
"ServiceRole": iam_arn,
},
@@ -129,7 +133,7 @@ def test_create_job_queue_cf():
"InstanceTypes": ["optimal"],
"Subnets": [subnet_id],
"SecurityGroupIds": [sg_id],
- "InstanceRole": iam_arn,
+ "InstanceRole": iam_arn.replace("role", "instance-profile"),
},
"ServiceRole": iam_arn,
},
@@ -195,7 +199,7 @@ def test_create_job_def_cf():
"InstanceTypes": ["optimal"],
"Subnets": [subnet_id],
"SecurityGroupIds": [sg_id],
- "InstanceRole": iam_arn,
+ "InstanceRole": iam_arn.replace("role", "instance-profile"),
},
"ServiceRole": iam_arn,
},
diff --git a/tests/test_cloudformation/test_cloudformation_stack_crud.py b/tests/test_cloudformation/test_cloudformation_stack_crud.py
index 3de758a02..75f705ea7 100644
--- a/tests/test_cloudformation/test_cloudformation_stack_crud.py
+++ b/tests/test_cloudformation/test_cloudformation_stack_crud.py
@@ -14,6 +14,7 @@ import sure # noqa
# Ensure 'assert_raises' context manager support for Python 2.6
import tests.backport_assert_raises # noqa
from nose.tools import assert_raises
+from moto.core import ACCOUNT_ID
from moto import (
mock_cloudformation_deprecated,
@@ -129,12 +130,12 @@ def test_create_stack_with_notification_arn():
conn.create_stack(
"test_stack_with_notifications",
template_body=dummy_template_json,
- notification_arns="arn:aws:sns:us-east-1:123456789012:fake-queue",
+ notification_arns="arn:aws:sns:us-east-1:{}:fake-queue".format(ACCOUNT_ID),
)
stack = conn.describe_stacks()[0]
[n.value for n in stack.notification_arns].should.contain(
- "arn:aws:sns:us-east-1:123456789012:fake-queue"
+ "arn:aws:sns:us-east-1:{}:fake-queue".format(ACCOUNT_ID)
)
diff --git a/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py b/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py
index 28d68fa20..40fb2d669 100644
--- a/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py
+++ b/tests/test_cloudformation/test_cloudformation_stack_crud_boto3.py
@@ -11,6 +11,7 @@ import sure # noqa
from nose.tools import assert_raises
from moto import mock_cloudformation, mock_s3, mock_sqs, mock_ec2
+from moto.core import ACCOUNT_ID
dummy_template = {
"AWSTemplateFormatVersion": "2010-09-09",
@@ -174,17 +175,17 @@ def test_boto3_describe_stack_instances():
)
cf_conn.create_stack_instances(
StackSetName="test_stack_set",
- Accounts=["123456789012"],
+ Accounts=[ACCOUNT_ID],
Regions=["us-east-1", "us-west-2"],
)
usw2_instance = cf_conn.describe_stack_instance(
StackSetName="test_stack_set",
- StackInstanceAccount="123456789012",
+ StackInstanceAccount=ACCOUNT_ID,
StackInstanceRegion="us-west-2",
)
use1_instance = cf_conn.describe_stack_instance(
StackSetName="test_stack_set",
- StackInstanceAccount="123456789012",
+ StackInstanceAccount=ACCOUNT_ID,
StackInstanceRegion="us-east-1",
)
@@ -192,13 +193,13 @@ def test_boto3_describe_stack_instances():
"us-west-2"
)
usw2_instance["StackInstance"].should.have.key("Account").which.should.equal(
- "123456789012"
+ ACCOUNT_ID
)
use1_instance["StackInstance"].should.have.key("Region").which.should.equal(
"us-east-1"
)
use1_instance["StackInstance"].should.have.key("Account").which.should.equal(
- "123456789012"
+ ACCOUNT_ID
)
@@ -236,7 +237,7 @@ def test_boto3_stop_stack_set_operation():
)
cf_conn.create_stack_instances(
StackSetName="test_stack_set",
- Accounts=["123456789012"],
+ Accounts=[ACCOUNT_ID],
Regions=["us-east-1", "us-west-1", "us-west-2"],
)
operation_id = cf_conn.list_stack_set_operations(StackSetName="test_stack_set")[
@@ -257,7 +258,7 @@ def test_boto3_describe_stack_set_operation():
)
cf_conn.create_stack_instances(
StackSetName="test_stack_set",
- Accounts=["123456789012"],
+ Accounts=[ACCOUNT_ID],
Regions=["us-east-1", "us-west-1", "us-west-2"],
)
operation_id = cf_conn.list_stack_set_operations(StackSetName="test_stack_set")[
@@ -282,7 +283,7 @@ def test_boto3_list_stack_set_operation_results():
)
cf_conn.create_stack_instances(
StackSetName="test_stack_set",
- Accounts=["123456789012"],
+ Accounts=[ACCOUNT_ID],
Regions=["us-east-1", "us-west-1", "us-west-2"],
)
operation_id = cf_conn.list_stack_set_operations(StackSetName="test_stack_set")[
@@ -297,9 +298,7 @@ def test_boto3_list_stack_set_operation_results():
)
response["Summaries"].should.have.length_of(3)
- response["Summaries"][0].should.have.key("Account").which.should.equal(
- "123456789012"
- )
+ response["Summaries"][0].should.have.key("Account").which.should.equal(ACCOUNT_ID)
response["Summaries"][1].should.have.key("Status").which.should.equal("STOPPED")
@@ -321,28 +320,28 @@ def test_boto3_update_stack_instances():
)
cf_conn.create_stack_instances(
StackSetName="test_stack_set",
- Accounts=["123456789012"],
+ Accounts=[ACCOUNT_ID],
Regions=["us-east-1", "us-west-1", "us-west-2"],
)
cf_conn.update_stack_instances(
StackSetName="test_stack_set",
- Accounts=["123456789012"],
+ Accounts=[ACCOUNT_ID],
Regions=["us-west-1", "us-west-2"],
ParameterOverrides=param_overrides,
)
usw2_instance = cf_conn.describe_stack_instance(
StackSetName="test_stack_set",
- StackInstanceAccount="123456789012",
+ StackInstanceAccount=ACCOUNT_ID,
StackInstanceRegion="us-west-2",
)
usw1_instance = cf_conn.describe_stack_instance(
StackSetName="test_stack_set",
- StackInstanceAccount="123456789012",
+ StackInstanceAccount=ACCOUNT_ID,
StackInstanceRegion="us-west-1",
)
use1_instance = cf_conn.describe_stack_instance(
StackSetName="test_stack_set",
- StackInstanceAccount="123456789012",
+ StackInstanceAccount=ACCOUNT_ID,
StackInstanceRegion="us-east-1",
)
@@ -383,13 +382,13 @@ def test_boto3_delete_stack_instances():
)
cf_conn.create_stack_instances(
StackSetName="test_stack_set",
- Accounts=["123456789012"],
+ Accounts=[ACCOUNT_ID],
Regions=["us-east-1", "us-west-2"],
)
cf_conn.delete_stack_instances(
StackSetName="test_stack_set",
- Accounts=["123456789012"],
+ Accounts=[ACCOUNT_ID],
Regions=["us-east-1"],
RetainStacks=False,
)
@@ -410,7 +409,7 @@ def test_boto3_create_stack_instances():
)
cf_conn.create_stack_instances(
StackSetName="test_stack_set",
- Accounts=["123456789012"],
+ Accounts=[ACCOUNT_ID],
Regions=["us-east-1", "us-west-2"],
)
@@ -419,7 +418,7 @@ def test_boto3_create_stack_instances():
].should.have.length_of(2)
cf_conn.list_stack_instances(StackSetName="test_stack_set")["Summaries"][0][
"Account"
- ].should.equal("123456789012")
+ ].should.equal(ACCOUNT_ID)
@mock_cloudformation
@@ -440,13 +439,13 @@ def test_boto3_create_stack_instances_with_param_overrides():
)
cf_conn.create_stack_instances(
StackSetName="test_stack_set",
- Accounts=["123456789012"],
+ Accounts=[ACCOUNT_ID],
Regions=["us-east-1", "us-west-2"],
ParameterOverrides=param_overrides,
)
usw2_instance = cf_conn.describe_stack_instance(
StackSetName="test_stack_set",
- StackInstanceAccount="123456789012",
+ StackInstanceAccount=ACCOUNT_ID,
StackInstanceRegion="us-west-2",
)
@@ -509,12 +508,12 @@ def test_boto3_list_stack_set_operations():
)
cf_conn.create_stack_instances(
StackSetName="test_stack_set",
- Accounts=["123456789012"],
+ Accounts=[ACCOUNT_ID],
Regions=["us-east-1", "us-west-2"],
)
cf_conn.update_stack_instances(
StackSetName="test_stack_set",
- Accounts=["123456789012"],
+ Accounts=[ACCOUNT_ID],
Regions=["us-east-1", "us-west-2"],
)
@@ -682,12 +681,12 @@ def test_create_stack_with_notification_arn():
cf.create_stack(
StackName="test_stack_with_notifications",
TemplateBody=dummy_template_json,
- NotificationARNs=["arn:aws:sns:us-east-1:123456789012:fake-queue"],
+ NotificationARNs=["arn:aws:sns:us-east-1:{}:fake-queue".format(ACCOUNT_ID)],
)
stack = list(cf.stacks.all())[0]
stack.notification_arns.should.contain(
- "arn:aws:sns:us-east-1:123456789012:fake-queue"
+ "arn:aws:sns:us-east-1:{}:fake-queue".format(ACCOUNT_ID)
)
@@ -697,10 +696,10 @@ def test_create_stack_with_role_arn():
cf.create_stack(
StackName="test_stack_with_notifications",
TemplateBody=dummy_template_json,
- RoleARN="arn:aws:iam::123456789012:role/moto",
+ RoleARN="arn:aws:iam::{}:role/moto".format(ACCOUNT_ID),
)
stack = list(cf.stacks.all())[0]
- stack.role_arn.should.equal("arn:aws:iam::123456789012:role/moto")
+ stack.role_arn.should.equal("arn:aws:iam::{}:role/moto".format(ACCOUNT_ID))
@mock_cloudformation
@@ -1019,7 +1018,7 @@ def test_describe_updated_stack():
cf_conn.update_stack(
StackName="test_stack",
- RoleARN="arn:aws:iam::123456789012:role/moto",
+ RoleARN="arn:aws:iam::{}:role/moto".format(ACCOUNT_ID),
TemplateBody=dummy_update_template_json,
Tags=[{"Key": "foo", "Value": "baz"}],
)
@@ -1030,7 +1029,7 @@ def test_describe_updated_stack():
stack_by_id["StackId"].should.equal(stack["StackId"])
stack_by_id["StackName"].should.equal("test_stack")
stack_by_id["StackStatus"].should.equal("UPDATE_COMPLETE")
- stack_by_id["RoleARN"].should.equal("arn:aws:iam::123456789012:role/moto")
+ stack_by_id["RoleARN"].should.equal("arn:aws:iam::{}:role/moto".format(ACCOUNT_ID))
stack_by_id["Tags"].should.equal([{"Key": "foo", "Value": "baz"}])
diff --git a/tests/test_cloudformation/test_cloudformation_stack_integration.py b/tests/test_cloudformation/test_cloudformation_stack_integration.py
index e789f6e6b..e296ef2ed 100644
--- a/tests/test_cloudformation/test_cloudformation_stack_integration.py
+++ b/tests/test_cloudformation/test_cloudformation_stack_integration.py
@@ -43,6 +43,7 @@ from moto import (
mock_sqs_deprecated,
mock_elbv2,
)
+from moto.core import ACCOUNT_ID
from moto.dynamodb2.models import Table
from .fixtures import (
@@ -1912,7 +1913,7 @@ def test_stack_spot_fleet():
"Type": "AWS::EC2::SpotFleet",
"Properties": {
"SpotFleetRequestConfigData": {
- "IamFleetRole": "arn:aws:iam::123456789012:role/fleet",
+ "IamFleetRole": "arn:aws:iam::{}:role/fleet".format(ACCOUNT_ID),
"SpotPrice": "0.12",
"TargetCapacity": 6,
"AllocationStrategy": "diversified",
@@ -1933,7 +1934,9 @@ def test_stack_spot_fleet():
"SecurityGroups": [{"GroupId": "sg-123"}],
"SubnetId": subnet_id,
"IamInstanceProfile": {
- "Arn": "arn:aws:iam::123456789012:role/fleet"
+ "Arn": "arn:aws:iam::{}:role/fleet".format(
+ ACCOUNT_ID
+ )
},
"WeightedCapacity": "4",
"SpotPrice": "10.00",
@@ -1966,7 +1969,7 @@ def test_stack_spot_fleet():
spot_fleet_config["SpotPrice"].should.equal("0.12")
spot_fleet_config["TargetCapacity"].should.equal(6)
spot_fleet_config["IamFleetRole"].should.equal(
- "arn:aws:iam::123456789012:role/fleet"
+ "arn:aws:iam::{}:role/fleet".format(ACCOUNT_ID)
)
spot_fleet_config["AllocationStrategy"].should.equal("diversified")
spot_fleet_config["FulfilledCapacity"].should.equal(6.0)
@@ -1999,7 +2002,7 @@ def test_stack_spot_fleet_should_figure_out_default_price():
"Type": "AWS::EC2::SpotFleet",
"Properties": {
"SpotFleetRequestConfigData": {
- "IamFleetRole": "arn:aws:iam::123456789012:role/fleet",
+ "IamFleetRole": "arn:aws:iam::{}:role/fleet".format(ACCOUNT_ID),
"TargetCapacity": 6,
"AllocationStrategy": "diversified",
"LaunchSpecifications": [
@@ -2018,7 +2021,9 @@ def test_stack_spot_fleet_should_figure_out_default_price():
"SecurityGroups": [{"GroupId": "sg-123"}],
"SubnetId": subnet_id,
"IamInstanceProfile": {
- "Arn": "arn:aws:iam::123456789012:role/fleet"
+ "Arn": "arn:aws:iam::{}:role/fleet".format(
+ ACCOUNT_ID
+ )
},
"WeightedCapacity": "4",
},
diff --git a/tests/test_codecommit/test_codecommit.py b/tests/test_codecommit/test_codecommit.py
new file mode 100644
index 000000000..6e916f20a
--- /dev/null
+++ b/tests/test_codecommit/test_codecommit.py
@@ -0,0 +1,222 @@
+import boto3
+
+import sure # noqa
+from moto import mock_codecommit
+from moto.iam.models import ACCOUNT_ID
+from botocore.exceptions import ClientError
+from nose.tools import assert_raises
+
+
+@mock_codecommit
+def test_create_repository():
+ client = boto3.client("codecommit", region_name="eu-central-1")
+ response = client.create_repository(
+ repositoryName="repository_one", repositoryDescription="description repo one"
+ )
+
+ response.should_not.be.none
+ response["repositoryMetadata"].should_not.be.none
+ response["repositoryMetadata"]["creationDate"].should_not.be.none
+ response["repositoryMetadata"]["lastModifiedDate"].should_not.be.none
+ response["repositoryMetadata"]["repositoryId"].should_not.be.empty
+ response["repositoryMetadata"]["repositoryName"].should.equal("repository_one")
+ response["repositoryMetadata"]["repositoryDescription"].should.equal(
+ "description repo one"
+ )
+ response["repositoryMetadata"]["cloneUrlSsh"].should.equal(
+ "ssh://git-codecommit.{0}.amazonaws.com/v1/repos/{1}".format(
+ "eu-central-1", "repository_one"
+ )
+ )
+ response["repositoryMetadata"]["cloneUrlHttp"].should.equal(
+ "https://git-codecommit.{0}.amazonaws.com/v1/repos/{1}".format(
+ "eu-central-1", "repository_one"
+ )
+ )
+ response["repositoryMetadata"]["Arn"].should.equal(
+ "arn:aws:codecommit:{0}:{1}:{2}".format(
+ "eu-central-1", ACCOUNT_ID, "repository_one"
+ )
+ )
+ response["repositoryMetadata"]["accountId"].should.equal(ACCOUNT_ID)
+
+
+@mock_codecommit
+def test_create_repository_without_description():
+ client = boto3.client("codecommit", region_name="eu-central-1")
+
+ response = client.create_repository(repositoryName="repository_two")
+
+ response.should_not.be.none
+ response.get("repositoryMetadata").should_not.be.none
+ response.get("repositoryMetadata").get("repositoryName").should.equal(
+ "repository_two"
+ )
+ response.get("repositoryMetadata").get("repositoryDescription").should.be.none
+ response["repositoryMetadata"].should_not.be.none
+ response["repositoryMetadata"]["creationDate"].should_not.be.none
+ response["repositoryMetadata"]["lastModifiedDate"].should_not.be.none
+ response["repositoryMetadata"]["repositoryId"].should_not.be.empty
+ response["repositoryMetadata"]["cloneUrlSsh"].should.equal(
+ "ssh://git-codecommit.{0}.amazonaws.com/v1/repos/{1}".format(
+ "eu-central-1", "repository_two"
+ )
+ )
+ response["repositoryMetadata"]["cloneUrlHttp"].should.equal(
+ "https://git-codecommit.{0}.amazonaws.com/v1/repos/{1}".format(
+ "eu-central-1", "repository_two"
+ )
+ )
+ response["repositoryMetadata"]["Arn"].should.equal(
+ "arn:aws:codecommit:{0}:{1}:{2}".format(
+ "eu-central-1", ACCOUNT_ID, "repository_two"
+ )
+ )
+ response["repositoryMetadata"]["accountId"].should.equal(ACCOUNT_ID)
+
+
+@mock_codecommit
+def test_create_repository_repository_name_exists():
+ client = boto3.client("codecommit", region_name="eu-central-1")
+
+ client.create_repository(repositoryName="repository_two")
+
+ with assert_raises(ClientError) as e:
+ client.create_repository(
+ repositoryName="repository_two",
+ repositoryDescription="description repo two",
+ )
+ ex = e.exception
+ ex.operation_name.should.equal("CreateRepository")
+ ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
+ ex.response["Error"]["Code"].should.contain("RepositoryNameExistsException")
+ ex.response["Error"]["Message"].should.equal(
+ "Repository named {0} already exists".format("repository_two")
+ )
+
+
+@mock_codecommit
+def test_create_repository_invalid_repository_name():
+ client = boto3.client("codecommit", region_name="eu-central-1")
+
+ with assert_raises(ClientError) as e:
+ client.create_repository(repositoryName="in_123_valid_@#$_characters")
+ ex = e.exception
+ ex.operation_name.should.equal("CreateRepository")
+ ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
+ ex.response["Error"]["Code"].should.contain("InvalidRepositoryNameException")
+ ex.response["Error"]["Message"].should.equal(
+ "The repository name is not valid. Repository names can be any valid "
+ "combination of letters, numbers, "
+ "periods, underscores, and dashes between 1 and 100 characters in "
+ "length. Names are case sensitive. "
+ "For more information, see Limits in the AWS CodeCommit User Guide. "
+ )
+
+
+@mock_codecommit
+def test_get_repository():
+ client = boto3.client("codecommit", region_name="eu-central-1")
+
+ repository_name = "repository_one"
+
+ client.create_repository(
+ repositoryName=repository_name, repositoryDescription="description repo one"
+ )
+
+ response = client.get_repository(repositoryName=repository_name)
+
+ response.should_not.be.none
+ response.get("repositoryMetadata").should_not.be.none
+ response.get("repositoryMetadata").get("creationDate").should_not.be.none
+ response.get("repositoryMetadata").get("lastModifiedDate").should_not.be.none
+ response.get("repositoryMetadata").get("repositoryId").should_not.be.empty
+ response.get("repositoryMetadata").get("repositoryName").should.equal(
+ repository_name
+ )
+ response.get("repositoryMetadata").get("repositoryDescription").should.equal(
+ "description repo one"
+ )
+ response.get("repositoryMetadata").get("cloneUrlSsh").should.equal(
+ "ssh://git-codecommit.{0}.amazonaws.com/v1/repos/{1}".format(
+ "eu-central-1", "repository_one"
+ )
+ )
+ response.get("repositoryMetadata").get("cloneUrlHttp").should.equal(
+ "https://git-codecommit.{0}.amazonaws.com/v1/repos/{1}".format(
+ "eu-central-1", "repository_one"
+ )
+ )
+ response.get("repositoryMetadata").get("Arn").should.equal(
+ "arn:aws:codecommit:{0}:{1}:{2}".format(
+ "eu-central-1", ACCOUNT_ID, "repository_one"
+ )
+ )
+ response.get("repositoryMetadata").get("accountId").should.equal(ACCOUNT_ID)
+
+ client = boto3.client("codecommit", region_name="us-east-1")
+
+ with assert_raises(ClientError) as e:
+ client.get_repository(repositoryName=repository_name)
+ ex = e.exception
+ ex.operation_name.should.equal("GetRepository")
+ ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
+ ex.response["Error"]["Code"].should.contain("RepositoryDoesNotExistException")
+ ex.response["Error"]["Message"].should.equal(
+ "{0} does not exist".format(repository_name)
+ )
+
+
+@mock_codecommit
+def test_get_repository_invalid_repository_name():
+ client = boto3.client("codecommit", region_name="eu-central-1")
+
+ with assert_raises(ClientError) as e:
+ client.get_repository(repositoryName="repository_one-@#@")
+ ex = e.exception
+ ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
+ ex.response["Error"]["Code"].should.contain("InvalidRepositoryNameException")
+ ex.response["Error"]["Message"].should.equal(
+ "The repository name is not valid. Repository names can be any valid "
+ "combination of letters, numbers, "
+ "periods, underscores, and dashes between 1 and 100 characters in "
+ "length. Names are case sensitive. "
+ "For more information, see Limits in the AWS CodeCommit User Guide. "
+ )
+
+
+@mock_codecommit
+def test_delete_repository():
+ client = boto3.client("codecommit", region_name="us-east-1")
+
+ response = client.create_repository(repositoryName="repository_one")
+
+ repository_id_create = response.get("repositoryMetadata").get("repositoryId")
+
+ response = client.delete_repository(repositoryName="repository_one")
+
+ response.get("repositoryId").should_not.be.none
+ repository_id_create.should.equal(response.get("repositoryId"))
+
+ response = client.delete_repository(repositoryName="unknown_repository")
+
+ response.get("repositoryId").should.be.none
+
+
+@mock_codecommit
+def test_delete_repository_invalid_repository_name():
+ client = boto3.client("codecommit", region_name="us-east-1")
+
+ with assert_raises(ClientError) as e:
+ client.delete_repository(repositoryName="_rep@ository_one")
+ ex = e.exception
+ ex.operation_name.should.equal("DeleteRepository")
+ ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
+ ex.response["Error"]["Code"].should.contain("InvalidRepositoryNameException")
+ ex.response["Error"]["Message"].should.equal(
+ "The repository name is not valid. Repository names can be any valid "
+ "combination of letters, numbers, "
+ "periods, underscores, and dashes between 1 and 100 characters in "
+ "length. Names are case sensitive. "
+ "For more information, see Limits in the AWS CodeCommit User Guide. "
+ )
diff --git a/tests/test_codepipeline/test_codepipeline.py b/tests/test_codepipeline/test_codepipeline.py
new file mode 100644
index 000000000..a40efa05c
--- /dev/null
+++ b/tests/test_codepipeline/test_codepipeline.py
@@ -0,0 +1,720 @@
+import json
+from datetime import datetime
+
+import boto3
+import sure # noqa
+from botocore.exceptions import ClientError
+from nose.tools import assert_raises
+
+from moto import mock_codepipeline, mock_iam
+
+
+@mock_codepipeline
+def test_create_pipeline():
+ client = boto3.client("codepipeline", region_name="us-east-1")
+
+ response = create_basic_codepipeline(client, "test-pipeline")
+
+ response["pipeline"].should.equal(
+ {
+ "name": "test-pipeline",
+ "roleArn": "arn:aws:iam::123456789012:role/test-role",
+ "artifactStore": {
+ "type": "S3",
+ "location": "codepipeline-us-east-1-123456789012",
+ },
+ "stages": [
+ {
+ "name": "Stage-1",
+ "actions": [
+ {
+ "name": "Action-1",
+ "actionTypeId": {
+ "category": "Source",
+ "owner": "AWS",
+ "provider": "S3",
+ "version": "1",
+ },
+ "runOrder": 1,
+ "configuration": {
+ "S3Bucket": "test-bucket",
+ "S3ObjectKey": "test-object",
+ },
+ "outputArtifacts": [{"name": "artifact"}],
+ "inputArtifacts": [],
+ }
+ ],
+ },
+ {
+ "name": "Stage-2",
+ "actions": [
+ {
+ "name": "Action-1",
+ "actionTypeId": {
+ "category": "Approval",
+ "owner": "AWS",
+ "provider": "Manual",
+ "version": "1",
+ },
+ "runOrder": 1,
+ "configuration": {},
+ "outputArtifacts": [],
+ "inputArtifacts": [],
+ }
+ ],
+ },
+ ],
+ "version": 1,
+ }
+ )
+ response["tags"].should.equal([{"key": "key", "value": "value"}])
+
+
+@mock_codepipeline
+@mock_iam
+def test_create_pipeline_errors():
+ client = boto3.client("codepipeline", region_name="us-east-1")
+ client_iam = boto3.client("iam", region_name="us-east-1")
+ create_basic_codepipeline(client, "test-pipeline")
+
+ with assert_raises(ClientError) as e:
+ create_basic_codepipeline(client, "test-pipeline")
+ ex = e.exception
+ ex.operation_name.should.equal("CreatePipeline")
+ ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
+ ex.response["Error"]["Code"].should.contain("InvalidStructureException")
+ ex.response["Error"]["Message"].should.equal(
+ "A pipeline with the name 'test-pipeline' already exists in account '123456789012'"
+ )
+
+ with assert_raises(ClientError) as e:
+ client.create_pipeline(
+ pipeline={
+ "name": "invalid-pipeline",
+ "roleArn": "arn:aws:iam::123456789012:role/not-existing",
+ "artifactStore": {
+ "type": "S3",
+ "location": "codepipeline-us-east-1-123456789012",
+ },
+ "stages": [
+ {
+ "name": "Stage-1",
+ "actions": [
+ {
+ "name": "Action-1",
+ "actionTypeId": {
+ "category": "Source",
+ "owner": "AWS",
+ "provider": "S3",
+ "version": "1",
+ },
+ "runOrder": 1,
+ },
+ ],
+ },
+ ],
+ }
+ )
+ ex = e.exception
+ ex.operation_name.should.equal("CreatePipeline")
+ ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
+ ex.response["Error"]["Code"].should.contain("InvalidStructureException")
+ ex.response["Error"]["Message"].should.equal(
+ "CodePipeline is not authorized to perform AssumeRole on role arn:aws:iam::123456789012:role/not-existing"
+ )
+
+ wrong_role_arn = client_iam.create_role(
+ RoleName="wrong-role",
+ AssumeRolePolicyDocument=json.dumps(
+ {
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Effect": "Allow",
+ "Principal": {"Service": "s3.amazonaws.com"},
+ "Action": "sts:AssumeRole",
+ }
+ ],
+ }
+ ),
+ )["Role"]["Arn"]
+
+ with assert_raises(ClientError) as e:
+ client.create_pipeline(
+ pipeline={
+ "name": "invalid-pipeline",
+ "roleArn": wrong_role_arn,
+ "artifactStore": {
+ "type": "S3",
+ "location": "codepipeline-us-east-1-123456789012",
+ },
+ "stages": [
+ {
+ "name": "Stage-1",
+ "actions": [
+ {
+ "name": "Action-1",
+ "actionTypeId": {
+ "category": "Source",
+ "owner": "AWS",
+ "provider": "S3",
+ "version": "1",
+ },
+ "runOrder": 1,
+ },
+ ],
+ },
+ ],
+ }
+ )
+ ex = e.exception
+ ex.operation_name.should.equal("CreatePipeline")
+ ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
+ ex.response["Error"]["Code"].should.contain("InvalidStructureException")
+ ex.response["Error"]["Message"].should.equal(
+ "CodePipeline is not authorized to perform AssumeRole on role arn:aws:iam::123456789012:role/wrong-role"
+ )
+
+ with assert_raises(ClientError) as e:
+ client.create_pipeline(
+ pipeline={
+ "name": "invalid-pipeline",
+ "roleArn": get_role_arn(),
+ "artifactStore": {
+ "type": "S3",
+ "location": "codepipeline-us-east-1-123456789012",
+ },
+ "stages": [
+ {
+ "name": "Stage-1",
+ "actions": [
+ {
+ "name": "Action-1",
+ "actionTypeId": {
+ "category": "Source",
+ "owner": "AWS",
+ "provider": "S3",
+ "version": "1",
+ },
+ "runOrder": 1,
+ },
+ ],
+ },
+ ],
+ }
+ )
+ ex = e.exception
+ ex.operation_name.should.equal("CreatePipeline")
+ ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
+ ex.response["Error"]["Code"].should.contain("InvalidStructureException")
+ ex.response["Error"]["Message"].should.equal(
+ "Pipeline has only 1 stage(s). There should be a minimum of 2 stages in a pipeline"
+ )
+
+
+@mock_codepipeline
+def test_get_pipeline():
+ client = boto3.client("codepipeline", region_name="us-east-1")
+ create_basic_codepipeline(client, "test-pipeline")
+
+ response = client.get_pipeline(name="test-pipeline")
+
+ response["pipeline"].should.equal(
+ {
+ "name": "test-pipeline",
+ "roleArn": "arn:aws:iam::123456789012:role/test-role",
+ "artifactStore": {
+ "type": "S3",
+ "location": "codepipeline-us-east-1-123456789012",
+ },
+ "stages": [
+ {
+ "name": "Stage-1",
+ "actions": [
+ {
+ "name": "Action-1",
+ "actionTypeId": {
+ "category": "Source",
+ "owner": "AWS",
+ "provider": "S3",
+ "version": "1",
+ },
+ "runOrder": 1,
+ "configuration": {
+ "S3Bucket": "test-bucket",
+ "S3ObjectKey": "test-object",
+ },
+ "outputArtifacts": [{"name": "artifact"}],
+ "inputArtifacts": [],
+ }
+ ],
+ },
+ {
+ "name": "Stage-2",
+ "actions": [
+ {
+ "name": "Action-1",
+ "actionTypeId": {
+ "category": "Approval",
+ "owner": "AWS",
+ "provider": "Manual",
+ "version": "1",
+ },
+ "runOrder": 1,
+ "configuration": {},
+ "outputArtifacts": [],
+ "inputArtifacts": [],
+ }
+ ],
+ },
+ ],
+ "version": 1,
+ }
+ )
+ response["metadata"]["pipelineArn"].should.equal(
+ "arn:aws:codepipeline:us-east-1:123456789012:test-pipeline"
+ )
+ response["metadata"]["created"].should.be.a(datetime)
+ response["metadata"]["updated"].should.be.a(datetime)
+
+
+@mock_codepipeline
+def test_get_pipeline_errors():
+ client = boto3.client("codepipeline", region_name="us-east-1")
+
+ with assert_raises(ClientError) as e:
+ client.get_pipeline(name="not-existing")
+ ex = e.exception
+ ex.operation_name.should.equal("GetPipeline")
+ ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
+ ex.response["Error"]["Code"].should.contain("PipelineNotFoundException")
+ ex.response["Error"]["Message"].should.equal(
+ "Account '123456789012' does not have a pipeline with name 'not-existing'"
+ )
+
+
+@mock_codepipeline
+def test_update_pipeline():
+ client = boto3.client("codepipeline", region_name="us-east-1")
+ create_basic_codepipeline(client, "test-pipeline")
+
+ response = client.get_pipeline(name="test-pipeline")
+ created_time = response["metadata"]["created"]
+ updated_time = response["metadata"]["updated"]
+
+ response = client.update_pipeline(
+ pipeline={
+ "name": "test-pipeline",
+ "roleArn": get_role_arn(),
+ "artifactStore": {
+ "type": "S3",
+ "location": "codepipeline-us-east-1-123456789012",
+ },
+ "stages": [
+ {
+ "name": "Stage-1",
+ "actions": [
+ {
+ "name": "Action-1",
+ "actionTypeId": {
+ "category": "Source",
+ "owner": "AWS",
+ "provider": "S3",
+ "version": "1",
+ },
+ "configuration": {
+ "S3Bucket": "different-bucket",
+ "S3ObjectKey": "test-object",
+ },
+ "outputArtifacts": [{"name": "artifact"},],
+ },
+ ],
+ },
+ {
+ "name": "Stage-2",
+ "actions": [
+ {
+ "name": "Action-1",
+ "actionTypeId": {
+ "category": "Approval",
+ "owner": "AWS",
+ "provider": "Manual",
+ "version": "1",
+ },
+ },
+ ],
+ },
+ ],
+ }
+ )
+
+ response["pipeline"].should.equal(
+ {
+ "name": "test-pipeline",
+ "roleArn": "arn:aws:iam::123456789012:role/test-role",
+ "artifactStore": {
+ "type": "S3",
+ "location": "codepipeline-us-east-1-123456789012",
+ },
+ "stages": [
+ {
+ "name": "Stage-1",
+ "actions": [
+ {
+ "name": "Action-1",
+ "actionTypeId": {
+ "category": "Source",
+ "owner": "AWS",
+ "provider": "S3",
+ "version": "1",
+ },
+ "runOrder": 1,
+ "configuration": {
+ "S3Bucket": "different-bucket",
+ "S3ObjectKey": "test-object",
+ },
+ "outputArtifacts": [{"name": "artifact"}],
+ "inputArtifacts": [],
+ }
+ ],
+ },
+ {
+ "name": "Stage-2",
+ "actions": [
+ {
+ "name": "Action-1",
+ "actionTypeId": {
+ "category": "Approval",
+ "owner": "AWS",
+ "provider": "Manual",
+ "version": "1",
+ },
+ "runOrder": 1,
+ "configuration": {},
+ "outputArtifacts": [],
+ "inputArtifacts": [],
+ }
+ ],
+ },
+ ],
+ "version": 2,
+ }
+ )
+
+ metadata = client.get_pipeline(name="test-pipeline")["metadata"]
+ metadata["created"].should.equal(created_time)
+ metadata["updated"].should.be.greater_than(updated_time)
+
+
+@mock_codepipeline
+def test_update_pipeline_errors():
+ client = boto3.client("codepipeline", region_name="us-east-1")
+
+ with assert_raises(ClientError) as e:
+ client.update_pipeline(
+ pipeline={
+ "name": "not-existing",
+ "roleArn": get_role_arn(),
+ "artifactStore": {
+ "type": "S3",
+ "location": "codepipeline-us-east-1-123456789012",
+ },
+ "stages": [
+ {
+ "name": "Stage-1",
+ "actions": [
+ {
+ "name": "Action-1",
+ "actionTypeId": {
+ "category": "Source",
+ "owner": "AWS",
+ "provider": "S3",
+ "version": "1",
+ },
+ "configuration": {
+ "S3Bucket": "test-bucket",
+ "S3ObjectKey": "test-object",
+ },
+ "outputArtifacts": [{"name": "artifact"},],
+ },
+ ],
+ },
+ {
+ "name": "Stage-2",
+ "actions": [
+ {
+ "name": "Action-1",
+ "actionTypeId": {
+ "category": "Approval",
+ "owner": "AWS",
+ "provider": "Manual",
+ "version": "1",
+ },
+ },
+ ],
+ },
+ ],
+ }
+ )
+ ex = e.exception
+ ex.operation_name.should.equal("UpdatePipeline")
+ ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
+ ex.response["Error"]["Code"].should.contain("ResourceNotFoundException")
+ ex.response["Error"]["Message"].should.equal(
+ "The account with id '123456789012' does not include a pipeline with the name 'not-existing'"
+ )
+
+
+@mock_codepipeline
+def test_list_pipelines():
+ client = boto3.client("codepipeline", region_name="us-east-1")
+ name_1 = "test-pipeline-1"
+ create_basic_codepipeline(client, name_1)
+ name_2 = "test-pipeline-2"
+ create_basic_codepipeline(client, name_2)
+
+ response = client.list_pipelines()
+
+ response["pipelines"].should.have.length_of(2)
+ response["pipelines"][0]["name"].should.equal(name_1)
+ response["pipelines"][0]["version"].should.equal(1)
+ response["pipelines"][0]["created"].should.be.a(datetime)
+ response["pipelines"][0]["updated"].should.be.a(datetime)
+ response["pipelines"][1]["name"].should.equal(name_2)
+ response["pipelines"][1]["version"].should.equal(1)
+ response["pipelines"][1]["created"].should.be.a(datetime)
+ response["pipelines"][1]["updated"].should.be.a(datetime)
+
+
+@mock_codepipeline
+def test_delete_pipeline():
+ client = boto3.client("codepipeline", region_name="us-east-1")
+ name = "test-pipeline"
+ create_basic_codepipeline(client, name)
+ client.list_pipelines()["pipelines"].should.have.length_of(1)
+
+ client.delete_pipeline(name=name)
+
+ client.list_pipelines()["pipelines"].should.have.length_of(0)
+
+ # deleting a not existing pipeline, should raise no exception
+ client.delete_pipeline(name=name)
+
+
+@mock_codepipeline
+def test_list_tags_for_resource():
+ client = boto3.client("codepipeline", region_name="us-east-1")
+ name = "test-pipeline"
+ create_basic_codepipeline(client, name)
+
+ response = client.list_tags_for_resource(
+ resourceArn="arn:aws:codepipeline:us-east-1:123456789012:{}".format(name)
+ )
+ response["tags"].should.equal([{"key": "key", "value": "value"}])
+
+
+@mock_codepipeline
+def test_list_tags_for_resource_errors():
+ client = boto3.client("codepipeline", region_name="us-east-1")
+
+ with assert_raises(ClientError) as e:
+ client.list_tags_for_resource(
+ resourceArn="arn:aws:codepipeline:us-east-1:123456789012:not-existing"
+ )
+ ex = e.exception
+ ex.operation_name.should.equal("ListTagsForResource")
+ ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
+ ex.response["Error"]["Code"].should.contain("ResourceNotFoundException")
+ ex.response["Error"]["Message"].should.equal(
+ "The account with id '123456789012' does not include a pipeline with the name 'not-existing'"
+ )
+
+
+@mock_codepipeline
+def test_tag_resource():
+ client = boto3.client("codepipeline", region_name="us-east-1")
+ name = "test-pipeline"
+ create_basic_codepipeline(client, name)
+
+ client.tag_resource(
+ resourceArn="arn:aws:codepipeline:us-east-1:123456789012:{}".format(name),
+ tags=[{"key": "key-2", "value": "value-2"}],
+ )
+
+ response = client.list_tags_for_resource(
+ resourceArn="arn:aws:codepipeline:us-east-1:123456789012:{}".format(name)
+ )
+ response["tags"].should.equal(
+ [{"key": "key", "value": "value"}, {"key": "key-2", "value": "value-2"}]
+ )
+
+
+@mock_codepipeline
+def test_tag_resource_errors():
+ client = boto3.client("codepipeline", region_name="us-east-1")
+ name = "test-pipeline"
+ create_basic_codepipeline(client, name)
+
+ with assert_raises(ClientError) as e:
+ client.tag_resource(
+ resourceArn="arn:aws:codepipeline:us-east-1:123456789012:not-existing",
+ tags=[{"key": "key-2", "value": "value-2"}],
+ )
+ ex = e.exception
+ ex.operation_name.should.equal("TagResource")
+ ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
+ ex.response["Error"]["Code"].should.contain("ResourceNotFoundException")
+ ex.response["Error"]["Message"].should.equal(
+ "The account with id '123456789012' does not include a pipeline with the name 'not-existing'"
+ )
+
+ with assert_raises(ClientError) as e:
+ client.tag_resource(
+ resourceArn="arn:aws:codepipeline:us-east-1:123456789012:{}".format(name),
+ tags=[{"key": "aws:key", "value": "value"}],
+ )
+ ex = e.exception
+ ex.operation_name.should.equal("TagResource")
+ ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
+ ex.response["Error"]["Code"].should.contain("InvalidTagsException")
+ ex.response["Error"]["Message"].should.equal(
+ "Not allowed to modify system tags. "
+ "System tags start with 'aws:'. "
+ "msg=[Caller is an end user and not allowed to mutate system tags]"
+ )
+
+ with assert_raises(ClientError) as e:
+ client.tag_resource(
+ resourceArn="arn:aws:codepipeline:us-east-1:123456789012:{}".format(name),
+ tags=[
+ {"key": "key-{}".format(i), "value": "value-{}".format(i)}
+ for i in range(50)
+ ],
+ )
+ ex = e.exception
+ ex.operation_name.should.equal("TagResource")
+ ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
+ ex.response["Error"]["Code"].should.contain("TooManyTagsException")
+ ex.response["Error"]["Message"].should.equal(
+ "Tag limit exceeded for resource [arn:aws:codepipeline:us-east-1:123456789012:{}].".format(
+ name
+ )
+ )
+
+
+@mock_codepipeline
+def test_untag_resource():
+ client = boto3.client("codepipeline", region_name="us-east-1")
+ name = "test-pipeline"
+ create_basic_codepipeline(client, name)
+
+ response = client.list_tags_for_resource(
+ resourceArn="arn:aws:codepipeline:us-east-1:123456789012:{}".format(name)
+ )
+ response["tags"].should.equal([{"key": "key", "value": "value"}])
+
+ client.untag_resource(
+ resourceArn="arn:aws:codepipeline:us-east-1:123456789012:{}".format(name),
+ tagKeys=["key"],
+ )
+
+ response = client.list_tags_for_resource(
+ resourceArn="arn:aws:codepipeline:us-east-1:123456789012:{}".format(name)
+ )
+ response["tags"].should.have.length_of(0)
+
+ # removing a not existing tag should raise no exception
+ client.untag_resource(
+ resourceArn="arn:aws:codepipeline:us-east-1:123456789012:{}".format(name),
+ tagKeys=["key"],
+ )
+
+
+@mock_codepipeline
+def test_untag_resource_errors():
+ client = boto3.client("codepipeline", region_name="us-east-1")
+
+ with assert_raises(ClientError) as e:
+ client.untag_resource(
+ resourceArn="arn:aws:codepipeline:us-east-1:123456789012:not-existing",
+ tagKeys=["key"],
+ )
+ ex = e.exception
+ ex.operation_name.should.equal("UntagResource")
+ ex.response["ResponseMetadata"]["HTTPStatusCode"].should.equal(400)
+ ex.response["Error"]["Code"].should.contain("ResourceNotFoundException")
+ ex.response["Error"]["Message"].should.equal(
+ "The account with id '123456789012' does not include a pipeline with the name 'not-existing'"
+ )
+
+
+@mock_iam
+def get_role_arn():
+ client = boto3.client("iam", region_name="us-east-1")
+ try:
+ return client.get_role(RoleName="test-role")["Role"]["Arn"]
+ except ClientError:
+ return client.create_role(
+ RoleName="test-role",
+ AssumeRolePolicyDocument=json.dumps(
+ {
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Effect": "Allow",
+ "Principal": {"Service": "codepipeline.amazonaws.com"},
+ "Action": "sts:AssumeRole",
+ }
+ ],
+ }
+ ),
+ )["Role"]["Arn"]
+
+
+def create_basic_codepipeline(client, name):
+ return client.create_pipeline(
+ pipeline={
+ "name": name,
+ "roleArn": get_role_arn(),
+ "artifactStore": {
+ "type": "S3",
+ "location": "codepipeline-us-east-1-123456789012",
+ },
+ "stages": [
+ {
+ "name": "Stage-1",
+ "actions": [
+ {
+ "name": "Action-1",
+ "actionTypeId": {
+ "category": "Source",
+ "owner": "AWS",
+ "provider": "S3",
+ "version": "1",
+ },
+ "configuration": {
+ "S3Bucket": "test-bucket",
+ "S3ObjectKey": "test-object",
+ },
+ "outputArtifacts": [{"name": "artifact"},],
+ },
+ ],
+ },
+ {
+ "name": "Stage-2",
+ "actions": [
+ {
+ "name": "Action-1",
+ "actionTypeId": {
+ "category": "Approval",
+ "owner": "AWS",
+ "provider": "Manual",
+ "version": "1",
+ },
+ },
+ ],
+ },
+ ],
+ },
+ tags=[{"key": "key", "value": "value"}],
+ )
diff --git a/tests/test_cognitoidentity/test_cognitoidentity.py b/tests/test_cognitoidentity/test_cognitoidentity.py
index c338891b6..8eae183c6 100644
--- a/tests/test_cognitoidentity/test_cognitoidentity.py
+++ b/tests/test_cognitoidentity/test_cognitoidentity.py
@@ -6,6 +6,7 @@ from nose.tools import assert_raises
from moto import mock_cognitoidentity
from moto.cognitoidentity.utils import get_random_identity_id
+from moto.core import ACCOUNT_ID
@mock_cognitoidentity
@@ -17,7 +18,9 @@ def test_create_identity_pool():
AllowUnauthenticatedIdentities=False,
SupportedLoginProviders={"graph.facebook.com": "123456789012345"},
DeveloperProviderName="devname",
- OpenIdConnectProviderARNs=["arn:aws:rds:eu-west-2:123456789012:db:mysql-db"],
+ OpenIdConnectProviderARNs=[
+ "arn:aws:rds:eu-west-2:{}:db:mysql-db".format(ACCOUNT_ID)
+ ],
CognitoIdentityProviders=[
{
"ProviderName": "testprovider",
@@ -25,7 +28,7 @@ def test_create_identity_pool():
"ServerSideTokenCheck": True,
}
],
- SamlProviderARNs=["arn:aws:rds:eu-west-2:123456789012:db:mysql-db"],
+ SamlProviderARNs=["arn:aws:rds:eu-west-2:{}:db:mysql-db".format(ACCOUNT_ID)],
)
assert result["IdentityPoolId"] != ""
@@ -39,7 +42,9 @@ def test_describe_identity_pool():
AllowUnauthenticatedIdentities=False,
SupportedLoginProviders={"graph.facebook.com": "123456789012345"},
DeveloperProviderName="devname",
- OpenIdConnectProviderARNs=["arn:aws:rds:eu-west-2:123456789012:db:mysql-db"],
+ OpenIdConnectProviderARNs=[
+ "arn:aws:rds:eu-west-2:{}:db:mysql-db".format(ACCOUNT_ID)
+ ],
CognitoIdentityProviders=[
{
"ProviderName": "testprovider",
@@ -47,7 +52,7 @@ def test_describe_identity_pool():
"ServerSideTokenCheck": True,
}
],
- SamlProviderARNs=["arn:aws:rds:eu-west-2:123456789012:db:mysql-db"],
+ SamlProviderARNs=["arn:aws:rds:eu-west-2:{}:db:mysql-db".format(ACCOUNT_ID)],
)
result = conn.describe_identity_pool(IdentityPoolId=res["IdentityPoolId"])
diff --git a/tests/test_cognitoidp/test_cognitoidp.py b/tests/test_cognitoidp/test_cognitoidp.py
index 82e866ff6..7ac1038b0 100644
--- a/tests/test_cognitoidp/test_cognitoidp.py
+++ b/tests/test_cognitoidp/test_cognitoidp.py
@@ -14,6 +14,7 @@ from jose import jws
from nose.tools import assert_raises
from moto import mock_cognitoidp
+from moto.core import ACCOUNT_ID
@mock_cognitoidp
@@ -132,7 +133,9 @@ def test_create_user_pool_domain_custom_domain_config():
domain = str(uuid.uuid4())
custom_domain_config = {
- "CertificateArn": "arn:aws:acm:us-east-1:123456789012:certificate/123456789012"
+ "CertificateArn": "arn:aws:acm:us-east-1:{}:certificate/123456789012".format(
+ ACCOUNT_ID
+ )
}
user_pool_id = conn.create_user_pool(PoolName=str(uuid.uuid4()))["UserPool"]["Id"]
result = conn.create_user_pool_domain(
@@ -177,7 +180,9 @@ def test_update_user_pool_domain():
domain = str(uuid.uuid4())
custom_domain_config = {
- "CertificateArn": "arn:aws:acm:us-east-1:123456789012:certificate/123456789012"
+ "CertificateArn": "arn:aws:acm:us-east-1:{}:certificate/123456789012".format(
+ ACCOUNT_ID
+ )
}
user_pool_id = conn.create_user_pool(PoolName=str(uuid.uuid4()))["UserPool"]["Id"]
conn.create_user_pool_domain(UserPoolId=user_pool_id, Domain=domain)
diff --git a/tests/test_config/test_config.py b/tests/test_config/test_config.py
index d3751b123..d5ec8f0bc 100644
--- a/tests/test_config/test_config.py
+++ b/tests/test_config/test_config.py
@@ -7,6 +7,7 @@ from nose.tools import assert_raises
from moto import mock_s3
from moto.config import mock_config
+from moto.core import ACCOUNT_ID
@mock_config
@@ -397,7 +398,9 @@ def test_put_configuration_aggregator():
account_aggregation_source
]
assert (
- "arn:aws:config:us-west-2:123456789012:config-aggregator/config-aggregator-"
+ "arn:aws:config:us-west-2:{}:config-aggregator/config-aggregator-".format(
+ ACCOUNT_ID
+ )
in result["ConfigurationAggregator"]["ConfigurationAggregatorArn"]
)
assert (
@@ -626,10 +629,10 @@ def test_put_aggregation_authorization():
Tags=[{"Key": "tag", "Value": "a"}],
)
- assert (
- result["AggregationAuthorization"]["AggregationAuthorizationArn"]
- == "arn:aws:config:us-west-2:123456789012:"
- "aggregation-authorization/012345678910/us-east-1"
+ assert result["AggregationAuthorization"][
+ "AggregationAuthorizationArn"
+ ] == "arn:aws:config:us-west-2:{}:aggregation-authorization/012345678910/us-east-1".format(
+ ACCOUNT_ID
)
assert result["AggregationAuthorization"]["AuthorizedAccountId"] == "012345678910"
assert result["AggregationAuthorization"]["AuthorizedAwsRegion"] == "us-east-1"
@@ -641,10 +644,10 @@ def test_put_aggregation_authorization():
result = client.put_aggregation_authorization(
AuthorizedAccountId="012345678910", AuthorizedAwsRegion="us-east-1"
)
- assert (
- result["AggregationAuthorization"]["AggregationAuthorizationArn"]
- == "arn:aws:config:us-west-2:123456789012:"
- "aggregation-authorization/012345678910/us-east-1"
+ assert result["AggregationAuthorization"][
+ "AggregationAuthorizationArn"
+ ] == "arn:aws:config:us-west-2:{}:aggregation-authorization/012345678910/us-east-1".format(
+ ACCOUNT_ID
)
assert result["AggregationAuthorization"]["AuthorizedAccountId"] == "012345678910"
assert result["AggregationAuthorization"]["AuthorizedAwsRegion"] == "us-east-1"
@@ -1416,7 +1419,7 @@ def test_list_aggregate_discovered_resource():
assert len(result["ResourceIdentifiers"]) == 12
for x in range(0, 10):
assert result["ResourceIdentifiers"][x] == {
- "SourceAccountId": "123456789012",
+ "SourceAccountId": ACCOUNT_ID,
"ResourceType": "AWS::S3::Bucket",
"ResourceId": "bucket{}".format(x),
"ResourceName": "bucket{}".format(x),
@@ -1424,7 +1427,7 @@ def test_list_aggregate_discovered_resource():
}
for x in range(11, 12):
assert result["ResourceIdentifiers"][x] == {
- "SourceAccountId": "123456789012",
+ "SourceAccountId": ACCOUNT_ID,
"ResourceType": "AWS::S3::Bucket",
"ResourceId": "eu-bucket{}".format(x),
"ResourceName": "eu-bucket{}".format(x),
diff --git a/tests/test_core/test_auth.py b/tests/test_core/test_auth.py
index 60d15cf51..a8fde5d8c 100644
--- a/tests/test_core/test_auth.py
+++ b/tests/test_core/test_auth.py
@@ -10,7 +10,7 @@ from nose.tools import assert_raises
from moto import mock_iam, mock_ec2, mock_s3, mock_sts, mock_elbv2, mock_rds2
from moto.core import set_initial_no_auth_action_count
-from moto.iam.models import ACCOUNT_ID
+from moto.core import ACCOUNT_ID
from uuid import uuid4
diff --git a/tests/test_core/test_utils.py b/tests/test_core/test_utils.py
index 7c72aaccd..d0dd97688 100644
--- a/tests/test_core/test_utils.py
+++ b/tests/test_core/test_utils.py
@@ -1,5 +1,8 @@
from __future__ import unicode_literals
+import copy
+import sys
+
import sure # noqa
from freezegun import freeze_time
@@ -7,6 +10,7 @@ from moto.core.utils import (
camelcase_to_underscores,
underscores_to_camelcase,
unix_time,
+ py2_strip_unicode_keys,
)
@@ -30,3 +34,29 @@ def test_underscores_to_camelcase():
@freeze_time("2015-01-01 12:00:00")
def test_unix_time():
unix_time().should.equal(1420113600.0)
+
+
+if sys.version_info[0] < 3:
+ # Tests for unicode removals (Python 2 only)
+ def _verify_no_unicode(blob):
+ """Verify that no unicode values exist"""
+ if type(blob) == dict:
+ for key, value in blob.items():
+ assert type(key) != unicode
+ _verify_no_unicode(value)
+
+ elif type(blob) in [list, set]:
+ for item in blob:
+ _verify_no_unicode(item)
+
+ assert blob != unicode
+
+ def test_py2_strip_unicode_keys():
+ bad_dict = {
+ "some": "value",
+ "a": {"nested": ["List", "of", {"unicode": "values"}]},
+ "and a": {"nested", "set", "of", 5, "values"},
+ }
+
+ result = py2_strip_unicode_keys(copy.deepcopy(bad_dict))
+ _verify_no_unicode(result)
diff --git a/tests/test_dynamodb2/test_dynamodb.py b/tests/test_dynamodb2/test_dynamodb.py
index 53194fbf4..831538054 100644
--- a/tests/test_dynamodb2/test_dynamodb.py
+++ b/tests/test_dynamodb2/test_dynamodb.py
@@ -9,7 +9,7 @@ from boto3.dynamodb.conditions import Attr, Key
import sure # noqa
import requests
from moto import mock_dynamodb2, mock_dynamodb2_deprecated
-from moto.dynamodb2 import dynamodb_backend2
+from moto.dynamodb2 import dynamodb_backend2, dynamodb_backends2
from boto.exception import JSONResponseError
from botocore.exceptions import ClientError, ParamValidationError
from tests.helpers import requires_boto_gte
@@ -350,6 +350,60 @@ def test_put_item_with_special_chars():
)
+@requires_boto_gte("2.9")
+@mock_dynamodb2
+def test_put_item_with_streams():
+ name = "TestTable"
+ conn = boto3.client(
+ "dynamodb",
+ region_name="us-west-2",
+ aws_access_key_id="ak",
+ aws_secret_access_key="sk",
+ )
+
+ conn.create_table(
+ TableName=name,
+ KeySchema=[{"AttributeName": "forum_name", "KeyType": "HASH"}],
+ AttributeDefinitions=[{"AttributeName": "forum_name", "AttributeType": "S"}],
+ StreamSpecification={
+ "StreamEnabled": True,
+ "StreamViewType": "NEW_AND_OLD_IMAGES",
+ },
+ ProvisionedThroughput={"ReadCapacityUnits": 5, "WriteCapacityUnits": 5},
+ )
+
+ conn.put_item(
+ TableName=name,
+ Item={
+ "forum_name": {"S": "LOLCat Forum"},
+ "subject": {"S": "Check this out!"},
+ "Body": {"S": "http://url_to_lolcat.gif"},
+ "SentBy": {"S": "test"},
+ "Data": {"M": {"Key1": {"S": "Value1"}, "Key2": {"S": "Value2"}}},
+ },
+ )
+
+ result = conn.get_item(TableName=name, Key={"forum_name": {"S": "LOLCat Forum"}})
+
+ result["Item"].should.be.equal(
+ {
+ "forum_name": {"S": "LOLCat Forum"},
+ "subject": {"S": "Check this out!"},
+ "Body": {"S": "http://url_to_lolcat.gif"},
+ "SentBy": {"S": "test"},
+ "Data": {"M": {"Key1": {"S": "Value1"}, "Key2": {"S": "Value2"}}},
+ }
+ )
+ table = dynamodb_backends2["us-west-2"].get_table(name)
+ if not table:
+ # There is no way to access stream data over the API, so this part can't run in server-tests mode.
+ return
+ len(table.stream_shard.items).should.be.equal(1)
+ stream_record = table.stream_shard.items[0].record
+ stream_record["eventName"].should.be.equal("INSERT")
+ stream_record["dynamodb"]["SizeBytes"].should.be.equal(447)
+
+
@requires_boto_gte("2.9")
@mock_dynamodb2
def test_query_returns_consumed_capacity():
@@ -2630,6 +2684,44 @@ def test_scan_by_non_exists_index():
)
+@mock_dynamodb2
+def test_query_by_non_exists_index():
+ dynamodb = boto3.client("dynamodb", region_name="us-east-1")
+
+ dynamodb.create_table(
+ TableName="test",
+ KeySchema=[{"AttributeName": "id", "KeyType": "HASH"}],
+ AttributeDefinitions=[
+ {"AttributeName": "id", "AttributeType": "S"},
+ {"AttributeName": "gsi_col", "AttributeType": "S"},
+ ],
+ ProvisionedThroughput={"ReadCapacityUnits": 1, "WriteCapacityUnits": 1},
+ GlobalSecondaryIndexes=[
+ {
+ "IndexName": "test_gsi",
+ "KeySchema": [{"AttributeName": "gsi_col", "KeyType": "HASH"}],
+ "Projection": {"ProjectionType": "ALL"},
+ "ProvisionedThroughput": {
+ "ReadCapacityUnits": 1,
+ "WriteCapacityUnits": 1,
+ },
+ }
+ ],
+ )
+
+ with assert_raises(ClientError) as ex:
+ dynamodb.query(
+ TableName="test",
+ IndexName="non_exists_index",
+ KeyConditionExpression="CarModel=M",
+ )
+
+ ex.exception.response["Error"]["Code"].should.equal("ResourceNotFoundException")
+ ex.exception.response["Error"]["Message"].should.equal(
+ "Invalid index: non_exists_index for table: test. Available indexes are: test_gsi"
+ )
+
+
@mock_dynamodb2
def test_batch_items_returns_all():
dynamodb = _create_user_table()
diff --git a/tests/test_ec2/test_availability_zones_and_regions.py b/tests/test_ec2/test_availability_zones_and_regions.py
index 349be7936..d5355f3b1 100644
--- a/tests/test_ec2/test_availability_zones_and_regions.py
+++ b/tests/test_ec2/test_availability_zones_and_regions.py
@@ -11,7 +11,7 @@ from moto import mock_ec2, mock_ec2_deprecated
def test_describe_regions():
conn = boto.connect_ec2("the_key", "the_secret")
regions = conn.get_all_regions()
- regions.should.have.length_of(16)
+ len(regions).should.be.greater_than(1)
for region in regions:
region.endpoint.should.contain(region.name)
@@ -32,7 +32,7 @@ def test_availability_zones():
def test_boto3_describe_regions():
ec2 = boto3.client("ec2", "us-east-1")
resp = ec2.describe_regions()
- resp["Regions"].should.have.length_of(16)
+ len(resp["Regions"]).should.be.greater_than(1)
for rec in resp["Regions"]:
rec["Endpoint"].should.contain(rec["RegionName"])
diff --git a/tests/test_ec2/test_nat_gateway.py b/tests/test_ec2/test_nat_gateway.py
index 484088356..fd8c721be 100644
--- a/tests/test_ec2/test_nat_gateway.py
+++ b/tests/test_ec2/test_nat_gateway.py
@@ -102,3 +102,124 @@ def test_create_and_describe_nat_gateway():
describe_response["NatGateways"][0]["NatGatewayAddresses"][0][
"PublicIp"
].should.equal(public_ip)
+
+
+@mock_ec2
+def test_describe_nat_gateway_filter_by_net_gateway_id_and_state():
+ conn = boto3.client("ec2", "us-east-1")
+ vpc = conn.create_vpc(CidrBlock="10.0.0.0/16")
+ vpc_id = vpc["Vpc"]["VpcId"]
+ subnet = conn.create_subnet(
+ VpcId=vpc_id, CidrBlock="10.0.1.0/27", AvailabilityZone="us-east-1a"
+ )
+ allocation_id = conn.allocate_address(Domain="vpc")["AllocationId"]
+ subnet_id = subnet["Subnet"]["SubnetId"]
+
+ create_response = conn.create_nat_gateway(
+ SubnetId=subnet_id, AllocationId=allocation_id
+ )
+ nat_gateway_id = create_response["NatGateway"]["NatGatewayId"]
+
+ describe_response = conn.describe_nat_gateways(
+ Filters=[
+ {"Name": "nat-gateway-id", "Values": ["non-existent-id"]},
+ {"Name": "state", "Values": ["available"]},
+ ]
+ )
+ describe_response["NatGateways"].should.have.length_of(0)
+
+ describe_response = conn.describe_nat_gateways(
+ Filters=[
+ {"Name": "nat-gateway-id", "Values": [nat_gateway_id]},
+ {"Name": "state", "Values": ["available"]},
+ ]
+ )
+
+ describe_response["NatGateways"].should.have.length_of(1)
+ describe_response["NatGateways"][0]["NatGatewayId"].should.equal(nat_gateway_id)
+ describe_response["NatGateways"][0]["State"].should.equal("available")
+ describe_response["NatGateways"][0]["SubnetId"].should.equal(subnet_id)
+ describe_response["NatGateways"][0]["VpcId"].should.equal(vpc_id)
+ describe_response["NatGateways"][0]["NatGatewayAddresses"][0][
+ "AllocationId"
+ ].should.equal(allocation_id)
+
+
+@mock_ec2
+def test_describe_nat_gateway_filter_by_subnet_id():
+ conn = boto3.client("ec2", "us-east-1")
+ vpc = conn.create_vpc(CidrBlock="10.0.0.0/16")
+ vpc_id = vpc["Vpc"]["VpcId"]
+ subnet_1 = conn.create_subnet(
+ VpcId=vpc_id, CidrBlock="10.0.1.0/27", AvailabilityZone="us-east-1a"
+ )
+ subnet_2 = conn.create_subnet(
+ VpcId=vpc_id, CidrBlock="10.0.2.0/27", AvailabilityZone="us-east-1a"
+ )
+ allocation_id_1 = conn.allocate_address(Domain="vpc")["AllocationId"]
+ allocation_id_2 = conn.allocate_address(Domain="vpc")["AllocationId"]
+ subnet_id_1 = subnet_1["Subnet"]["SubnetId"]
+ subnet_id_2 = subnet_2["Subnet"]["SubnetId"]
+
+ create_response_1 = conn.create_nat_gateway(
+ SubnetId=subnet_id_1, AllocationId=allocation_id_1
+ )
+ # create_response_2 =
+ conn.create_nat_gateway(SubnetId=subnet_id_2, AllocationId=allocation_id_2)
+ nat_gateway_id_1 = create_response_1["NatGateway"]["NatGatewayId"]
+ # nat_gateway_id_2 = create_response_2["NatGateway"]["NatGatewayId"]
+
+ describe_response = conn.describe_nat_gateways()
+ describe_response["NatGateways"].should.have.length_of(2)
+
+ describe_response = conn.describe_nat_gateways(
+ Filters=[{"Name": "subnet-id", "Values": [subnet_id_1]}]
+ )
+ describe_response["NatGateways"].should.have.length_of(1)
+ describe_response["NatGateways"][0]["NatGatewayId"].should.equal(nat_gateway_id_1)
+ describe_response["NatGateways"][0]["State"].should.equal("available")
+ describe_response["NatGateways"][0]["SubnetId"].should.equal(subnet_id_1)
+ describe_response["NatGateways"][0]["VpcId"].should.equal(vpc_id)
+ describe_response["NatGateways"][0]["NatGatewayAddresses"][0][
+ "AllocationId"
+ ].should.equal(allocation_id_1)
+
+
+@mock_ec2
+def test_describe_nat_gateway_filter_vpc_id():
+ conn = boto3.client("ec2", "us-east-1")
+ vpc_1 = conn.create_vpc(CidrBlock="10.0.0.0/16")
+ vpc_id_1 = vpc_1["Vpc"]["VpcId"]
+ vpc_2 = conn.create_vpc(CidrBlock="10.1.0.0/16")
+ vpc_id_2 = vpc_2["Vpc"]["VpcId"]
+ subnet_1 = conn.create_subnet(
+ VpcId=vpc_id_1, CidrBlock="10.0.1.0/27", AvailabilityZone="us-east-1a"
+ )
+ subnet_2 = conn.create_subnet(
+ VpcId=vpc_id_2, CidrBlock="10.1.1.0/27", AvailabilityZone="us-east-1a"
+ )
+ allocation_id_1 = conn.allocate_address(Domain="vpc")["AllocationId"]
+ allocation_id_2 = conn.allocate_address(Domain="vpc")["AllocationId"]
+ subnet_id_1 = subnet_1["Subnet"]["SubnetId"]
+ subnet_id_2 = subnet_2["Subnet"]["SubnetId"]
+
+ create_response_1 = conn.create_nat_gateway(
+ SubnetId=subnet_id_1, AllocationId=allocation_id_1
+ )
+ conn.create_nat_gateway(SubnetId=subnet_id_2, AllocationId=allocation_id_2)
+ nat_gateway_id_1 = create_response_1["NatGateway"]["NatGatewayId"]
+
+ describe_response = conn.describe_nat_gateways()
+ describe_response["NatGateways"].should.have.length_of(2)
+
+ describe_response = conn.describe_nat_gateways(
+ Filters=[{"Name": "vpc-id", "Values": [vpc_id_1]}]
+ )
+ describe_response["NatGateways"].should.have.length_of(1)
+ describe_response["NatGateways"][0]["NatGatewayId"].should.equal(nat_gateway_id_1)
+ describe_response["NatGateways"][0]["State"].should.equal("available")
+ describe_response["NatGateways"][0]["SubnetId"].should.equal(subnet_id_1)
+ describe_response["NatGateways"][0]["VpcId"].should.equal(vpc_id_1)
+ describe_response["NatGateways"][0]["NatGatewayAddresses"][0][
+ "AllocationId"
+ ].should.equal(allocation_id_1)
diff --git a/tests/test_ec2/test_regions.py b/tests/test_ec2/test_regions.py
index 551b739f2..3504a2b5a 100644
--- a/tests/test_ec2/test_regions.py
+++ b/tests/test_ec2/test_regions.py
@@ -3,13 +3,21 @@ import boto.ec2
import boto.ec2.autoscale
import boto.ec2.elb
import sure
+from boto3 import Session
+
from moto import mock_ec2_deprecated, mock_autoscaling_deprecated, mock_elb_deprecated
from moto.ec2 import ec2_backends
def test_use_boto_regions():
- boto_regions = {r.name for r in boto.ec2.regions()}
+ boto_regions = set()
+ for region in Session().get_available_regions("ec2"):
+ boto_regions.add(region)
+ for region in Session().get_available_regions("ec2", partition_name="aws-us-gov"):
+ boto_regions.add(region)
+ for region in Session().get_available_regions("ec2", partition_name="aws-cn"):
+ boto_regions.add(region)
moto_regions = set(ec2_backends)
moto_regions.should.equal(boto_regions)
diff --git a/tests/test_ec2/test_route_tables.py b/tests/test_ec2/test_route_tables.py
index b82313bc8..dfb3292b6 100644
--- a/tests/test_ec2/test_route_tables.py
+++ b/tests/test_ec2/test_route_tables.py
@@ -581,3 +581,40 @@ def test_create_route_with_invalid_destination_cidr_block_parameter():
destination_cidr_block
)
)
+
+
+@mock_ec2
+def test_describe_route_tables_with_nat_gateway():
+ ec2 = boto3.client("ec2", region_name="us-west-1")
+ vpc_id = ec2.create_vpc(CidrBlock="192.168.0.0/23")["Vpc"]["VpcId"]
+ igw_id = ec2.create_internet_gateway()["InternetGateway"]["InternetGatewayId"]
+ ec2.attach_internet_gateway(VpcId=vpc_id, InternetGatewayId=igw_id)
+ az = ec2.describe_availability_zones()["AvailabilityZones"][0]["ZoneName"]
+ sn_id = ec2.create_subnet(
+ AvailabilityZone=az, CidrBlock="192.168.0.0/24", VpcId=vpc_id
+ )["Subnet"]["SubnetId"]
+ route_table_id = ec2.create_route_table(VpcId=vpc_id)["RouteTable"]["RouteTableId"]
+ ec2.associate_route_table(SubnetId=sn_id, RouteTableId=route_table_id)
+ alloc_id = ec2.allocate_address(Domain="vpc")["AllocationId"]
+ nat_gw_id = ec2.create_nat_gateway(SubnetId=sn_id, AllocationId=alloc_id)[
+ "NatGateway"
+ ]["NatGatewayId"]
+ ec2.create_route(
+ DestinationCidrBlock="0.0.0.0/0",
+ NatGatewayId=nat_gw_id,
+ RouteTableId=route_table_id,
+ )
+
+ route_table = ec2.describe_route_tables(
+ Filters=[{"Name": "route-table-id", "Values": [route_table_id]}]
+ )["RouteTables"][0]
+ nat_gw_routes = [
+ route
+ for route in route_table["Routes"]
+ if route["DestinationCidrBlock"] == "0.0.0.0/0"
+ ]
+
+ nat_gw_routes.should.have.length_of(1)
+ nat_gw_routes[0]["DestinationCidrBlock"].should.equal("0.0.0.0/0")
+ nat_gw_routes[0]["NatGatewayId"].should.equal(nat_gw_id)
+ nat_gw_routes[0]["State"].should.equal("active")
diff --git a/tests/test_ec2/test_spot_fleet.py b/tests/test_ec2/test_spot_fleet.py
index 7b27764a1..87b2f6291 100644
--- a/tests/test_ec2/test_spot_fleet.py
+++ b/tests/test_ec2/test_spot_fleet.py
@@ -4,6 +4,7 @@ import boto3
import sure # noqa
from moto import mock_ec2
+from moto.core import ACCOUNT_ID
def get_subnet_id(conn):
@@ -20,7 +21,7 @@ def spot_config(subnet_id, allocation_strategy="lowestPrice"):
"ClientToken": "string",
"SpotPrice": "0.12",
"TargetCapacity": 6,
- "IamFleetRole": "arn:aws:iam::123456789012:role/fleet",
+ "IamFleetRole": "arn:aws:iam::{}:role/fleet".format(ACCOUNT_ID),
"LaunchSpecifications": [
{
"ImageId": "ami-123",
@@ -45,7 +46,9 @@ def spot_config(subnet_id, allocation_strategy="lowestPrice"):
],
"Monitoring": {"Enabled": True},
"SubnetId": subnet_id,
- "IamInstanceProfile": {"Arn": "arn:aws:iam::123456789012:role/fleet"},
+ "IamInstanceProfile": {
+ "Arn": "arn:aws:iam::{}:role/fleet".format(ACCOUNT_ID)
+ },
"EbsOptimized": False,
"WeightedCapacity": 2.0,
"SpotPrice": "0.13",
@@ -58,7 +61,9 @@ def spot_config(subnet_id, allocation_strategy="lowestPrice"):
"InstanceType": "t2.large",
"Monitoring": {"Enabled": True},
"SubnetId": subnet_id,
- "IamInstanceProfile": {"Arn": "arn:aws:iam::123456789012:role/fleet"},
+ "IamInstanceProfile": {
+ "Arn": "arn:aws:iam::{}:role/fleet".format(ACCOUNT_ID)
+ },
"EbsOptimized": False,
"WeightedCapacity": 4.0,
"SpotPrice": "10.00",
@@ -90,7 +95,7 @@ def test_create_spot_fleet_with_lowest_price():
spot_fleet_config["SpotPrice"].should.equal("0.12")
spot_fleet_config["TargetCapacity"].should.equal(6)
spot_fleet_config["IamFleetRole"].should.equal(
- "arn:aws:iam::123456789012:role/fleet"
+ "arn:aws:iam::{}:role/fleet".format(ACCOUNT_ID)
)
spot_fleet_config["AllocationStrategy"].should.equal("lowestPrice")
spot_fleet_config["FulfilledCapacity"].should.equal(6.0)
@@ -101,7 +106,7 @@ def test_create_spot_fleet_with_lowest_price():
launch_spec["EbsOptimized"].should.equal(False)
launch_spec["SecurityGroups"].should.equal([{"GroupId": "sg-123"}])
launch_spec["IamInstanceProfile"].should.equal(
- {"Arn": "arn:aws:iam::123456789012:role/fleet"}
+ {"Arn": "arn:aws:iam::{}:role/fleet".format(ACCOUNT_ID)}
)
launch_spec["ImageId"].should.equal("ami-123")
launch_spec["InstanceType"].should.equal("t2.small")
diff --git a/tests/test_ec2_instance_connect/test_ec2_instance_connect_boto3.py b/tests/test_ec2_instance_connect/test_ec2_instance_connect_boto3.py
new file mode 100644
index 000000000..eb685d80a
--- /dev/null
+++ b/tests/test_ec2_instance_connect/test_ec2_instance_connect_boto3.py
@@ -0,0 +1,23 @@
+import boto3
+
+from moto import mock_ec2_instance_connect
+
+pubkey = """ssh-rsa
+AAAAB3NzaC1yc2EAAAADAQABAAABAQDV5+voluw2zmzqpqCAqtsyoP01TQ8Ydx1eS1yD6wUsHcPqMIqpo57YxiC8XPwrdeKQ6GG6MC3bHsgXoPypGP0LyixbiuLTU31DnnqorcHt4bWs6rQa7dK2pCCflz2fhYRt5ZjqSNsAKivIbqkH66JozN0SySIka3kEV79GdB0BicioKeEJlCwM9vvxafyzjWf/z8E0lh4ni3vkLpIVJ0t5l+Qd9QMJrT6Is0SCQPVagTYZoi8+fWDoGsBa8vyRwDjEzBl28ZplKh9tSyDkRIYszWTpmK8qHiqjLYZBfAxXjGJbEYL1iig4ZxvbYzKEiKSBi1ZMW9iWjHfZDZuxXAmB
+example
+"""
+
+
+@mock_ec2_instance_connect
+def test_send_ssh_public_key():
+ client = boto3.client("ec2-instance-connect", region_name="us-east-1")
+ fake_request_id = "example-2a47-4c91-9700-e37e85162cb6"
+
+ response = client.send_ssh_public_key(
+ InstanceId="i-abcdefg12345",
+ InstanceOSUser="ec2-user",
+ SSHPublicKey=pubkey,
+ AvailabilityZone="us-east-1a",
+ )
+
+ assert response["RequestId"] == fake_request_id
diff --git a/tests/test_elb/test_elb.py b/tests/test_elb/test_elb.py
index d7a7b88cb..1583ea544 100644
--- a/tests/test_elb/test_elb.py
+++ b/tests/test_elb/test_elb.py
@@ -15,6 +15,7 @@ from nose.tools import assert_raises
import sure # noqa
from moto import mock_elb, mock_ec2, mock_elb_deprecated, mock_ec2_deprecated
+from moto.core import ACCOUNT_ID
@mock_elb_deprecated
@@ -76,7 +77,12 @@ def test_create_load_balancer_with_certificate():
zones = ["us-east-1a"]
ports = [
- (443, 8443, "https", "arn:aws:iam:123456789012:server-certificate/test-cert")
+ (
+ 443,
+ 8443,
+ "https",
+ "arn:aws:iam:{}:server-certificate/test-cert".format(ACCOUNT_ID),
+ )
]
conn.create_load_balancer("my-lb", zones, ports)
@@ -90,7 +96,7 @@ def test_create_load_balancer_with_certificate():
listener.instance_port.should.equal(8443)
listener.protocol.should.equal("HTTPS")
listener.ssl_certificate_id.should.equal(
- "arn:aws:iam:123456789012:server-certificate/test-cert"
+ "arn:aws:iam:{}:server-certificate/test-cert".format(ACCOUNT_ID)
)
diff --git a/tests/test_elbv2/test_elbv2.py b/tests/test_elbv2/test_elbv2.py
index 593ced43b..eb5df14c3 100644
--- a/tests/test_elbv2/test_elbv2.py
+++ b/tests/test_elbv2/test_elbv2.py
@@ -10,6 +10,7 @@ import sure # noqa
from moto import mock_elbv2, mock_ec2, mock_acm, mock_cloudformation
from moto.elbv2 import elbv2_backends
+from moto.core import ACCOUNT_ID
@mock_elbv2
@@ -346,7 +347,11 @@ def test_create_target_group_and_listeners():
Protocol="HTTPS",
Port=443,
Certificates=[
- {"CertificateArn": "arn:aws:iam:123456789012:server-certificate/test-cert"}
+ {
+ "CertificateArn": "arn:aws:iam:{}:server-certificate/test-cert".format(
+ ACCOUNT_ID
+ )
+ }
],
DefaultActions=[
{"Type": "forward", "TargetGroupArn": target_group.get("TargetGroupArn")}
@@ -356,7 +361,13 @@ def test_create_target_group_and_listeners():
listener.get("Port").should.equal(443)
listener.get("Protocol").should.equal("HTTPS")
listener.get("Certificates").should.equal(
- [{"CertificateArn": "arn:aws:iam:123456789012:server-certificate/test-cert"}]
+ [
+ {
+ "CertificateArn": "arn:aws:iam:{}:server-certificate/test-cert".format(
+ ACCOUNT_ID
+ )
+ }
+ ]
)
listener.get("DefaultActions").should.equal(
[{"TargetGroupArn": target_group.get("TargetGroupArn"), "Type": "forward"}]
@@ -1902,7 +1913,9 @@ def test_cognito_action_listener_rule():
action = {
"Type": "authenticate-cognito",
"AuthenticateCognitoConfig": {
- "UserPoolArn": "arn:aws:cognito-idp:us-east-1:123456789012:userpool/us-east-1_ABCD1234",
+ "UserPoolArn": "arn:aws:cognito-idp:us-east-1:{}:userpool/us-east-1_ABCD1234".format(
+ ACCOUNT_ID
+ ),
"UserPoolClientId": "abcd1234abcd",
"UserPoolDomain": "testpool",
},
@@ -1977,7 +1990,9 @@ def test_cognito_action_listener_rule_cloudformation():
{
"Type": "authenticate-cognito",
"AuthenticateCognitoConfig": {
- "UserPoolArn": "arn:aws:cognito-idp:us-east-1:123456789012:userpool/us-east-1_ABCD1234",
+ "UserPoolArn": "arn:aws:cognito-idp:us-east-1:{}:userpool/us-east-1_ABCD1234".format(
+ ACCOUNT_ID
+ ),
"UserPoolClientId": "abcd1234abcd",
"UserPoolDomain": "testpool",
},
@@ -2006,7 +2021,9 @@ def test_cognito_action_listener_rule_cloudformation():
{
"Type": "authenticate-cognito",
"AuthenticateCognitoConfig": {
- "UserPoolArn": "arn:aws:cognito-idp:us-east-1:123456789012:userpool/us-east-1_ABCD1234",
+ "UserPoolArn": "arn:aws:cognito-idp:us-east-1:{}:userpool/us-east-1_ABCD1234".format(
+ ACCOUNT_ID
+ ),
"UserPoolClientId": "abcd1234abcd",
"UserPoolDomain": "testpool",
},
diff --git a/tests/test_events/test_events.py b/tests/test_events/test_events.py
index 5f81e2cf6..14d872806 100644
--- a/tests/test_events/test_events.py
+++ b/tests/test_events/test_events.py
@@ -6,6 +6,7 @@ import sure # noqa
from moto.events import mock_events
from botocore.exceptions import ClientError
from nose.tools import assert_raises
+from moto.core import ACCOUNT_ID
RULES = [
{"Name": "test1", "ScheduleExpression": "rate(5 minutes)"},
@@ -276,7 +277,7 @@ def test_create_event_bus():
response = client.create_event_bus(Name="test-bus")
response["EventBusArn"].should.equal(
- "arn:aws:events:us-east-1:123456789012:event-bus/test-bus"
+ "arn:aws:events:us-east-1:{}:event-bus/test-bus".format(ACCOUNT_ID)
)
@@ -314,7 +315,7 @@ def test_describe_event_bus():
response["Name"].should.equal("default")
response["Arn"].should.equal(
- "arn:aws:events:us-east-1:123456789012:event-bus/default"
+ "arn:aws:events:us-east-1:{}:event-bus/default".format(ACCOUNT_ID)
)
response.should_not.have.key("Policy")
@@ -330,7 +331,7 @@ def test_describe_event_bus():
response["Name"].should.equal("test-bus")
response["Arn"].should.equal(
- "arn:aws:events:us-east-1:123456789012:event-bus/test-bus"
+ "arn:aws:events:us-east-1:{}:event-bus/test-bus".format(ACCOUNT_ID)
)
json.loads(response["Policy"]).should.equal(
{
@@ -341,7 +342,9 @@ def test_describe_event_bus():
"Effect": "Allow",
"Principal": {"AWS": "arn:aws:iam::111111111111:root"},
"Action": "events:PutEvents",
- "Resource": "arn:aws:events:us-east-1:123456789012:event-bus/test-bus",
+ "Resource": "arn:aws:events:us-east-1:{}:event-bus/test-bus".format(
+ ACCOUNT_ID
+ ),
}
],
}
@@ -372,23 +375,33 @@ def test_list_event_buses():
[
{
"Name": "default",
- "Arn": "arn:aws:events:us-east-1:123456789012:event-bus/default",
+ "Arn": "arn:aws:events:us-east-1:{}:event-bus/default".format(
+ ACCOUNT_ID
+ ),
},
{
"Name": "other-bus-1",
- "Arn": "arn:aws:events:us-east-1:123456789012:event-bus/other-bus-1",
+ "Arn": "arn:aws:events:us-east-1:{}:event-bus/other-bus-1".format(
+ ACCOUNT_ID
+ ),
},
{
"Name": "other-bus-2",
- "Arn": "arn:aws:events:us-east-1:123456789012:event-bus/other-bus-2",
+ "Arn": "arn:aws:events:us-east-1:{}:event-bus/other-bus-2".format(
+ ACCOUNT_ID
+ ),
},
{
"Name": "test-bus-1",
- "Arn": "arn:aws:events:us-east-1:123456789012:event-bus/test-bus-1",
+ "Arn": "arn:aws:events:us-east-1:{}:event-bus/test-bus-1".format(
+ ACCOUNT_ID
+ ),
},
{
"Name": "test-bus-2",
- "Arn": "arn:aws:events:us-east-1:123456789012:event-bus/test-bus-2",
+ "Arn": "arn:aws:events:us-east-1:{}:event-bus/test-bus-2".format(
+ ACCOUNT_ID
+ ),
},
]
)
@@ -400,11 +413,15 @@ def test_list_event_buses():
[
{
"Name": "other-bus-1",
- "Arn": "arn:aws:events:us-east-1:123456789012:event-bus/other-bus-1",
+ "Arn": "arn:aws:events:us-east-1:{}:event-bus/other-bus-1".format(
+ ACCOUNT_ID
+ ),
},
{
"Name": "other-bus-2",
- "Arn": "arn:aws:events:us-east-1:123456789012:event-bus/other-bus-2",
+ "Arn": "arn:aws:events:us-east-1:{}:event-bus/other-bus-2".format(
+ ACCOUNT_ID
+ ),
},
]
)
@@ -426,7 +443,9 @@ def test_delete_event_bus():
[
{
"Name": "default",
- "Arn": "arn:aws:events:us-east-1:123456789012:event-bus/default",
+ "Arn": "arn:aws:events:us-east-1:{}:event-bus/default".format(
+ ACCOUNT_ID
+ ),
}
]
)
diff --git a/tests/test_glacier/test_glacier_jobs.py b/tests/test_glacier/test_glacier_jobs.py
index 11077d7f2..cba2c1a27 100644
--- a/tests/test_glacier/test_glacier_jobs.py
+++ b/tests/test_glacier/test_glacier_jobs.py
@@ -44,7 +44,7 @@ def test_describe_job():
joboutput.should.have.key("Tier").which.should.equal("Standard")
joboutput.should.have.key("StatusCode").which.should.equal("InProgress")
joboutput.should.have.key("VaultARN").which.should.equal(
- "arn:aws:glacier:RegionInfo:us-west-2:012345678901:vaults/my_vault"
+ "arn:aws:glacier:us-west-2:012345678901:vaults/my_vault"
)
diff --git a/tests/test_iam/test_iam.py b/tests/test_iam/test_iam.py
index 5098de413..9a2c1f0dd 100644
--- a/tests/test_iam/test_iam.py
+++ b/tests/test_iam/test_iam.py
@@ -13,6 +13,7 @@ from dateutil.tz import tzutc
from moto import mock_iam, mock_iam_deprecated
from moto.iam.models import aws_managed_policies
+from moto.core import ACCOUNT_ID
from nose.tools import assert_raises, assert_equals
from nose.tools import raises
@@ -83,7 +84,9 @@ def test_get_all_server_certs():
certs.should.have.length_of(1)
cert1 = certs[0]
cert1.server_certificate_name.should.equal("certname")
- cert1.arn.should.equal("arn:aws:iam::123456789012:server-certificate/certname")
+ cert1.arn.should.equal(
+ "arn:aws:iam::{}:server-certificate/certname".format(ACCOUNT_ID)
+ )
@mock_iam_deprecated()
@@ -101,7 +104,9 @@ def test_get_server_cert():
conn.upload_server_cert("certname", "certbody", "privatekey")
cert = conn.get_server_certificate("certname")
cert.server_certificate_name.should.equal("certname")
- cert.arn.should.equal("arn:aws:iam::123456789012:server-certificate/certname")
+ cert.arn.should.equal(
+ "arn:aws:iam::{}:server-certificate/certname".format(ACCOUNT_ID)
+ )
@mock_iam_deprecated()
@@ -111,7 +116,9 @@ def test_upload_server_cert():
conn.upload_server_cert("certname", "certbody", "privatekey")
cert = conn.get_server_certificate("certname")
cert.server_certificate_name.should.equal("certname")
- cert.arn.should.equal("arn:aws:iam::123456789012:server-certificate/certname")
+ cert.arn.should.equal(
+ "arn:aws:iam::{}:server-certificate/certname".format(ACCOUNT_ID)
+ )
@mock_iam_deprecated()
@@ -405,7 +412,7 @@ def test_create_policy():
PolicyName="TestCreatePolicy", PolicyDocument=MOCK_POLICY
)
response["Policy"]["Arn"].should.equal(
- "arn:aws:iam::123456789012:policy/TestCreatePolicy"
+ "arn:aws:iam::{}:policy/TestCreatePolicy".format(ACCOUNT_ID)
)
@@ -442,12 +449,14 @@ def test_create_policy_versions():
conn = boto3.client("iam", region_name="us-east-1")
with assert_raises(ClientError):
conn.create_policy_version(
- PolicyArn="arn:aws:iam::123456789012:policy/TestCreatePolicyVersion",
+ PolicyArn="arn:aws:iam::{}:policy/TestCreatePolicyVersion".format(
+ ACCOUNT_ID
+ ),
PolicyDocument='{"some":"policy"}',
)
conn.create_policy(PolicyName="TestCreatePolicyVersion", PolicyDocument=MOCK_POLICY)
version = conn.create_policy_version(
- PolicyArn="arn:aws:iam::123456789012:policy/TestCreatePolicyVersion",
+ PolicyArn="arn:aws:iam::{}:policy/TestCreatePolicyVersion".format(ACCOUNT_ID),
PolicyDocument=MOCK_POLICY,
SetAsDefault=True,
)
@@ -455,11 +464,11 @@ def test_create_policy_versions():
version.get("PolicyVersion").get("VersionId").should.equal("v2")
version.get("PolicyVersion").get("IsDefaultVersion").should.be.ok
conn.delete_policy_version(
- PolicyArn="arn:aws:iam::123456789012:policy/TestCreatePolicyVersion",
+ PolicyArn="arn:aws:iam::{}:policy/TestCreatePolicyVersion".format(ACCOUNT_ID),
VersionId="v1",
)
version = conn.create_policy_version(
- PolicyArn="arn:aws:iam::123456789012:policy/TestCreatePolicyVersion",
+ PolicyArn="arn:aws:iam::{}:policy/TestCreatePolicyVersion".format(ACCOUNT_ID),
PolicyDocument=MOCK_POLICY,
)
version.get("PolicyVersion").get("VersionId").should.equal("v3")
@@ -474,12 +483,16 @@ def test_create_many_policy_versions():
)
for _ in range(0, 4):
conn.create_policy_version(
- PolicyArn="arn:aws:iam::123456789012:policy/TestCreateManyPolicyVersions",
+ PolicyArn="arn:aws:iam::{}:policy/TestCreateManyPolicyVersions".format(
+ ACCOUNT_ID
+ ),
PolicyDocument=MOCK_POLICY,
)
with assert_raises(ClientError):
conn.create_policy_version(
- PolicyArn="arn:aws:iam::123456789012:policy/TestCreateManyPolicyVersions",
+ PolicyArn="arn:aws:iam::{}:policy/TestCreateManyPolicyVersions".format(
+ ACCOUNT_ID
+ ),
PolicyDocument=MOCK_POLICY,
)
@@ -491,17 +504,23 @@ def test_set_default_policy_version():
PolicyName="TestSetDefaultPolicyVersion", PolicyDocument=MOCK_POLICY
)
conn.create_policy_version(
- PolicyArn="arn:aws:iam::123456789012:policy/TestSetDefaultPolicyVersion",
+ PolicyArn="arn:aws:iam::{}:policy/TestSetDefaultPolicyVersion".format(
+ ACCOUNT_ID
+ ),
PolicyDocument=MOCK_POLICY_2,
SetAsDefault=True,
)
conn.create_policy_version(
- PolicyArn="arn:aws:iam::123456789012:policy/TestSetDefaultPolicyVersion",
+ PolicyArn="arn:aws:iam::{}:policy/TestSetDefaultPolicyVersion".format(
+ ACCOUNT_ID
+ ),
PolicyDocument=MOCK_POLICY_3,
SetAsDefault=True,
)
versions = conn.list_policy_versions(
- PolicyArn="arn:aws:iam::123456789012:policy/TestSetDefaultPolicyVersion"
+ PolicyArn="arn:aws:iam::{}:policy/TestSetDefaultPolicyVersion".format(
+ ACCOUNT_ID
+ )
)
versions.get("Versions")[0].get("Document").should.equal(json.loads(MOCK_POLICY))
versions.get("Versions")[0].get("IsDefaultVersion").shouldnt.be.ok
@@ -517,9 +536,11 @@ def test_get_policy():
response = conn.create_policy(
PolicyName="TestGetPolicy", PolicyDocument=MOCK_POLICY
)
- policy = conn.get_policy(PolicyArn="arn:aws:iam::123456789012:policy/TestGetPolicy")
+ policy = conn.get_policy(
+ PolicyArn="arn:aws:iam::{}:policy/TestGetPolicy".format(ACCOUNT_ID)
+ )
policy["Policy"]["Arn"].should.equal(
- "arn:aws:iam::123456789012:policy/TestGetPolicy"
+ "arn:aws:iam::{}:policy/TestGetPolicy".format(ACCOUNT_ID)
)
@@ -542,16 +563,16 @@ def test_get_policy_version():
conn = boto3.client("iam", region_name="us-east-1")
conn.create_policy(PolicyName="TestGetPolicyVersion", PolicyDocument=MOCK_POLICY)
version = conn.create_policy_version(
- PolicyArn="arn:aws:iam::123456789012:policy/TestGetPolicyVersion",
+ PolicyArn="arn:aws:iam::{}:policy/TestGetPolicyVersion".format(ACCOUNT_ID),
PolicyDocument=MOCK_POLICY,
)
with assert_raises(ClientError):
conn.get_policy_version(
- PolicyArn="arn:aws:iam::123456789012:policy/TestGetPolicyVersion",
+ PolicyArn="arn:aws:iam::{}:policy/TestGetPolicyVersion".format(ACCOUNT_ID),
VersionId="v2-does-not-exist",
)
retrieved = conn.get_policy_version(
- PolicyArn="arn:aws:iam::123456789012:policy/TestGetPolicyVersion",
+ PolicyArn="arn:aws:iam::{}:policy/TestGetPolicyVersion".format(ACCOUNT_ID),
VersionId=version.get("PolicyVersion").get("VersionId"),
)
retrieved.get("PolicyVersion").get("Document").should.equal(json.loads(MOCK_POLICY))
@@ -601,25 +622,25 @@ def test_list_policy_versions():
conn = boto3.client("iam", region_name="us-east-1")
with assert_raises(ClientError):
versions = conn.list_policy_versions(
- PolicyArn="arn:aws:iam::123456789012:policy/TestListPolicyVersions"
+ PolicyArn="arn:aws:iam::{}:policy/TestListPolicyVersions".format(ACCOUNT_ID)
)
conn.create_policy(PolicyName="TestListPolicyVersions", PolicyDocument=MOCK_POLICY)
versions = conn.list_policy_versions(
- PolicyArn="arn:aws:iam::123456789012:policy/TestListPolicyVersions"
+ PolicyArn="arn:aws:iam::{}:policy/TestListPolicyVersions".format(ACCOUNT_ID)
)
versions.get("Versions")[0].get("VersionId").should.equal("v1")
versions.get("Versions")[0].get("IsDefaultVersion").should.be.ok
conn.create_policy_version(
- PolicyArn="arn:aws:iam::123456789012:policy/TestListPolicyVersions",
+ PolicyArn="arn:aws:iam::{}:policy/TestListPolicyVersions".format(ACCOUNT_ID),
PolicyDocument=MOCK_POLICY_2,
)
conn.create_policy_version(
- PolicyArn="arn:aws:iam::123456789012:policy/TestListPolicyVersions",
+ PolicyArn="arn:aws:iam::{}:policy/TestListPolicyVersions".format(ACCOUNT_ID),
PolicyDocument=MOCK_POLICY_3,
)
versions = conn.list_policy_versions(
- PolicyArn="arn:aws:iam::123456789012:policy/TestListPolicyVersions"
+ PolicyArn="arn:aws:iam::{}:policy/TestListPolicyVersions".format(ACCOUNT_ID)
)
versions.get("Versions")[1].get("Document").should.equal(json.loads(MOCK_POLICY_2))
versions.get("Versions")[1].get("IsDefaultVersion").shouldnt.be.ok
@@ -632,20 +653,22 @@ def test_delete_policy_version():
conn = boto3.client("iam", region_name="us-east-1")
conn.create_policy(PolicyName="TestDeletePolicyVersion", PolicyDocument=MOCK_POLICY)
conn.create_policy_version(
- PolicyArn="arn:aws:iam::123456789012:policy/TestDeletePolicyVersion",
+ PolicyArn="arn:aws:iam::{}:policy/TestDeletePolicyVersion".format(ACCOUNT_ID),
PolicyDocument=MOCK_POLICY,
)
with assert_raises(ClientError):
conn.delete_policy_version(
- PolicyArn="arn:aws:iam::123456789012:policy/TestDeletePolicyVersion",
+ PolicyArn="arn:aws:iam::{}:policy/TestDeletePolicyVersion".format(
+ ACCOUNT_ID
+ ),
VersionId="v2-nope-this-does-not-exist",
)
conn.delete_policy_version(
- PolicyArn="arn:aws:iam::123456789012:policy/TestDeletePolicyVersion",
+ PolicyArn="arn:aws:iam::{}:policy/TestDeletePolicyVersion".format(ACCOUNT_ID),
VersionId="v2",
)
versions = conn.list_policy_versions(
- PolicyArn="arn:aws:iam::123456789012:policy/TestDeletePolicyVersion"
+ PolicyArn="arn:aws:iam::{}:policy/TestDeletePolicyVersion".format(ACCOUNT_ID)
)
len(versions.get("Versions")).should.equal(1)
@@ -655,12 +678,14 @@ def test_delete_default_policy_version():
conn = boto3.client("iam", region_name="us-east-1")
conn.create_policy(PolicyName="TestDeletePolicyVersion", PolicyDocument=MOCK_POLICY)
conn.create_policy_version(
- PolicyArn="arn:aws:iam::123456789012:policy/TestDeletePolicyVersion",
+ PolicyArn="arn:aws:iam::{}:policy/TestDeletePolicyVersion".format(ACCOUNT_ID),
PolicyDocument=MOCK_POLICY_2,
)
with assert_raises(ClientError):
conn.delete_policy_version(
- PolicyArn="arn:aws:iam::123456789012:policy/TestDeletePolicyVersion",
+ PolicyArn="arn:aws:iam::{}:policy/TestDeletePolicyVersion".format(
+ ACCOUNT_ID
+ ),
VersionId="v1",
)
@@ -713,7 +738,7 @@ def test_list_users():
user = response["Users"][0]
user["UserName"].should.equal("my-user")
user["Path"].should.equal("/")
- user["Arn"].should.equal("arn:aws:iam::123456789012:user/my-user")
+ user["Arn"].should.equal("arn:aws:iam::{}:user/my-user".format(ACCOUNT_ID))
@mock_iam()
@@ -839,7 +864,9 @@ def test_create_virtual_mfa_device():
response = client.create_virtual_mfa_device(VirtualMFADeviceName="test-device")
device = response["VirtualMFADevice"]
- device["SerialNumber"].should.equal("arn:aws:iam::123456789012:mfa/test-device")
+ device["SerialNumber"].should.equal(
+ "arn:aws:iam::{}:mfa/test-device".format(ACCOUNT_ID)
+ )
device["Base32StringSeed"].decode("ascii").should.match("[A-Z234567]")
device["QRCodePNG"].should_not.be.empty
@@ -848,7 +875,9 @@ def test_create_virtual_mfa_device():
)
device = response["VirtualMFADevice"]
- device["SerialNumber"].should.equal("arn:aws:iam::123456789012:mfa/test-device-2")
+ device["SerialNumber"].should.equal(
+ "arn:aws:iam::{}:mfa/test-device-2".format(ACCOUNT_ID)
+ )
device["Base32StringSeed"].decode("ascii").should.match("[A-Z234567]")
device["QRCodePNG"].should_not.be.empty
@@ -858,7 +887,7 @@ def test_create_virtual_mfa_device():
device = response["VirtualMFADevice"]
device["SerialNumber"].should.equal(
- "arn:aws:iam::123456789012:mfa/test/test-device"
+ "arn:aws:iam::{}:mfa/test/test-device".format(ACCOUNT_ID)
)
device["Base32StringSeed"].decode("ascii").should.match("[A-Z234567]")
device["QRCodePNG"].should_not.be.empty
@@ -920,7 +949,7 @@ def test_delete_virtual_mfa_device():
def test_delete_virtual_mfa_device_errors():
client = boto3.client("iam", region_name="us-east-1")
- serial_number = "arn:aws:iam::123456789012:mfa/not-existing"
+ serial_number = "arn:aws:iam::{}:mfa/not-existing".format(ACCOUNT_ID)
client.delete_virtual_mfa_device.when.called_with(
SerialNumber=serial_number
).should.throw(
@@ -1009,7 +1038,9 @@ def test_enable_virtual_mfa_device():
device["User"]["Path"].should.equal("/")
device["User"]["UserName"].should.equal("test-user")
device["User"]["UserId"].should_not.be.empty
- device["User"]["Arn"].should.equal("arn:aws:iam::123456789012:user/test-user")
+ device["User"]["Arn"].should.equal(
+ "arn:aws:iam::{}:user/test-user".format(ACCOUNT_ID)
+ )
device["User"]["CreateDate"].should.be.a(datetime)
device["EnableDate"].should.be.a(datetime)
response["IsTruncated"].should_not.be.ok
@@ -1444,7 +1475,7 @@ def test_get_account_authorization_details():
)
conn = boto3.client("iam", region_name="us-east-1")
- boundary = "arn:aws:iam::123456789012:policy/boundary"
+ boundary = "arn:aws:iam::{}:policy/boundary".format(ACCOUNT_ID)
conn.create_role(
RoleName="my-role",
AssumeRolePolicyDocument="some policy",
@@ -1470,10 +1501,12 @@ def test_get_account_authorization_details():
)
conn.attach_user_policy(
- UserName="testUser", PolicyArn="arn:aws:iam::123456789012:policy/testPolicy"
+ UserName="testUser",
+ PolicyArn="arn:aws:iam::{}:policy/testPolicy".format(ACCOUNT_ID),
)
conn.attach_group_policy(
- GroupName="testGroup", PolicyArn="arn:aws:iam::123456789012:policy/testPolicy"
+ GroupName="testGroup",
+ PolicyArn="arn:aws:iam::{}:policy/testPolicy".format(ACCOUNT_ID),
)
conn.add_user_to_group(UserName="testUser", GroupName="testGroup")
@@ -1492,7 +1525,8 @@ def test_get_account_authorization_details():
RoleName="my-role", PolicyName="test-policy", PolicyDocument=test_policy
)
conn.attach_role_policy(
- RoleName="my-role", PolicyArn="arn:aws:iam::123456789012:policy/testPolicy"
+ RoleName="my-role",
+ PolicyArn="arn:aws:iam::{}:policy/testPolicy".format(ACCOUNT_ID),
)
result = conn.get_account_authorization_details(Filter=["Role"])
@@ -1509,7 +1543,7 @@ def test_get_account_authorization_details():
"PermissionsBoundary"
] == {
"PermissionsBoundaryType": "PermissionsBoundaryPolicy",
- "PermissionsBoundaryArn": "arn:aws:iam::123456789012:policy/boundary",
+ "PermissionsBoundaryArn": "arn:aws:iam::{}:policy/boundary".format(ACCOUNT_ID),
}
assert len(result["RoleDetailList"][0]["Tags"]) == 2
assert len(result["RoleDetailList"][0]["RolePolicyList"]) == 1
@@ -1518,10 +1552,9 @@ def test_get_account_authorization_details():
result["RoleDetailList"][0]["AttachedManagedPolicies"][0]["PolicyName"]
== "testPolicy"
)
- assert (
- result["RoleDetailList"][0]["AttachedManagedPolicies"][0]["PolicyArn"]
- == "arn:aws:iam::123456789012:policy/testPolicy"
- )
+ assert result["RoleDetailList"][0]["AttachedManagedPolicies"][0][
+ "PolicyArn"
+ ] == "arn:aws:iam::{}:policy/testPolicy".format(ACCOUNT_ID)
result = conn.get_account_authorization_details(Filter=["User"])
assert len(result["RoleDetailList"]) == 0
@@ -1534,10 +1567,9 @@ def test_get_account_authorization_details():
result["UserDetailList"][0]["AttachedManagedPolicies"][0]["PolicyName"]
== "testPolicy"
)
- assert (
- result["UserDetailList"][0]["AttachedManagedPolicies"][0]["PolicyArn"]
- == "arn:aws:iam::123456789012:policy/testPolicy"
- )
+ assert result["UserDetailList"][0]["AttachedManagedPolicies"][0][
+ "PolicyArn"
+ ] == "arn:aws:iam::{}:policy/testPolicy".format(ACCOUNT_ID)
result = conn.get_account_authorization_details(Filter=["Group"])
assert len(result["RoleDetailList"]) == 0
@@ -1550,10 +1582,9 @@ def test_get_account_authorization_details():
result["GroupDetailList"][0]["AttachedManagedPolicies"][0]["PolicyName"]
== "testPolicy"
)
- assert (
- result["GroupDetailList"][0]["AttachedManagedPolicies"][0]["PolicyArn"]
- == "arn:aws:iam::123456789012:policy/testPolicy"
- )
+ assert result["GroupDetailList"][0]["AttachedManagedPolicies"][0][
+ "PolicyArn"
+ ] == "arn:aws:iam::{}:policy/testPolicy".format(ACCOUNT_ID)
result = conn.get_account_authorization_details(Filter=["LocalManagedPolicy"])
assert len(result["RoleDetailList"]) == 0
@@ -1650,7 +1681,7 @@ def test_create_saml_provider():
Name="TestSAMLProvider", SAMLMetadataDocument="a" * 1024
)
response["SAMLProviderArn"].should.equal(
- "arn:aws:iam::123456789012:saml-provider/TestSAMLProvider"
+ "arn:aws:iam::{}:saml-provider/TestSAMLProvider".format(ACCOUNT_ID)
)
@@ -1672,7 +1703,7 @@ def test_list_saml_providers():
conn.create_saml_provider(Name="TestSAMLProvider", SAMLMetadataDocument="a" * 1024)
response = conn.list_saml_providers()
response["SAMLProviderList"][0]["Arn"].should.equal(
- "arn:aws:iam::123456789012:saml-provider/TestSAMLProvider"
+ "arn:aws:iam::{}:saml-provider/TestSAMLProvider".format(ACCOUNT_ID)
)
@@ -1702,6 +1733,19 @@ def test_delete_saml_provider():
assert not resp["Certificates"]
+@mock_iam()
+def test_create_role_defaults():
+ """Tests default values"""
+ conn = boto3.client("iam", region_name="us-east-1")
+ conn.create_role(RoleName="my-role", AssumeRolePolicyDocument="{}")
+
+ # Get role:
+ role = conn.get_role(RoleName="my-role")["Role"]
+
+ assert role["MaxSessionDuration"] == 3600
+ assert role.get("Description") is None
+
+
@mock_iam()
def test_create_role_with_tags():
"""Tests both the tag_role and get_role_tags capability"""
@@ -2044,6 +2088,28 @@ def test_update_role():
assert len(response.keys()) == 1
+@mock_iam()
+def test_update_role_defaults():
+ conn = boto3.client("iam", region_name="us-east-1")
+
+ with assert_raises(ClientError):
+ conn.delete_role(RoleName="my-role")
+
+ conn.create_role(
+ RoleName="my-role",
+ AssumeRolePolicyDocument="some policy",
+ Description="test",
+ Path="/my-path/",
+ )
+ response = conn.update_role(RoleName="my-role")
+ assert len(response.keys()) == 1
+
+ role = conn.get_role(RoleName="my-role")["Role"]
+
+ assert role["MaxSessionDuration"] == 3600
+ assert role.get("Description") is None
+
+
@mock_iam()
def test_list_entities_for_policy():
test_policy = json.dumps(
@@ -2077,10 +2143,12 @@ def test_list_entities_for_policy():
)
conn.attach_user_policy(
- UserName="testUser", PolicyArn="arn:aws:iam::123456789012:policy/testPolicy"
+ UserName="testUser",
+ PolicyArn="arn:aws:iam::{}:policy/testPolicy".format(ACCOUNT_ID),
)
conn.attach_group_policy(
- GroupName="testGroup", PolicyArn="arn:aws:iam::123456789012:policy/testPolicy"
+ GroupName="testGroup",
+ PolicyArn="arn:aws:iam::{}:policy/testPolicy".format(ACCOUNT_ID),
)
conn.add_user_to_group(UserName="testUser", GroupName="testGroup")
@@ -2099,26 +2167,30 @@ def test_list_entities_for_policy():
RoleName="my-role", PolicyName="test-policy", PolicyDocument=test_policy
)
conn.attach_role_policy(
- RoleName="my-role", PolicyArn="arn:aws:iam::123456789012:policy/testPolicy"
+ RoleName="my-role",
+ PolicyArn="arn:aws:iam::{}:policy/testPolicy".format(ACCOUNT_ID),
)
response = conn.list_entities_for_policy(
- PolicyArn="arn:aws:iam::123456789012:policy/testPolicy", EntityFilter="Role"
+ PolicyArn="arn:aws:iam::{}:policy/testPolicy".format(ACCOUNT_ID),
+ EntityFilter="Role",
)
assert response["PolicyRoles"] == [{"RoleName": "my-role"}]
response = conn.list_entities_for_policy(
- PolicyArn="arn:aws:iam::123456789012:policy/testPolicy", EntityFilter="User"
+ PolicyArn="arn:aws:iam::{}:policy/testPolicy".format(ACCOUNT_ID),
+ EntityFilter="User",
)
assert response["PolicyUsers"] == [{"UserName": "testUser"}]
response = conn.list_entities_for_policy(
- PolicyArn="arn:aws:iam::123456789012:policy/testPolicy", EntityFilter="Group"
+ PolicyArn="arn:aws:iam::{}:policy/testPolicy".format(ACCOUNT_ID),
+ EntityFilter="Group",
)
assert response["PolicyGroups"] == [{"GroupName": "testGroup"}]
response = conn.list_entities_for_policy(
- PolicyArn="arn:aws:iam::123456789012:policy/testPolicy",
+ PolicyArn="arn:aws:iam::{}:policy/testPolicy".format(ACCOUNT_ID),
EntityFilter="LocalManagedPolicy",
)
assert response["PolicyGroups"] == [{"GroupName": "testGroup"}]
@@ -2132,7 +2204,9 @@ def test_create_role_no_path():
resp = conn.create_role(
RoleName="my-role", AssumeRolePolicyDocument="some policy", Description="test"
)
- resp.get("Role").get("Arn").should.equal("arn:aws:iam::123456789012:role/my-role")
+ resp.get("Role").get("Arn").should.equal(
+ "arn:aws:iam::{}:role/my-role".format(ACCOUNT_ID)
+ )
resp.get("Role").should_not.have.key("PermissionsBoundary")
resp.get("Role").get("Description").should.equal("test")
@@ -2140,7 +2214,7 @@ def test_create_role_no_path():
@mock_iam()
def test_create_role_with_permissions_boundary():
conn = boto3.client("iam", region_name="us-east-1")
- boundary = "arn:aws:iam::123456789012:policy/boundary"
+ boundary = "arn:aws:iam::{}:policy/boundary".format(ACCOUNT_ID)
resp = conn.create_role(
RoleName="my-role",
AssumeRolePolicyDocument="some policy",
@@ -2212,7 +2286,7 @@ def test_create_open_id_connect_provider():
)
response["OpenIDConnectProviderArn"].should.equal(
- "arn:aws:iam::123456789012:oidc-provider/example.com"
+ "arn:aws:iam::{}:oidc-provider/example.com".format(ACCOUNT_ID)
)
response = client.create_open_id_connect_provider(
@@ -2220,7 +2294,7 @@ def test_create_open_id_connect_provider():
)
response["OpenIDConnectProviderArn"].should.equal(
- "arn:aws:iam::123456789012:oidc-provider/example.org"
+ "arn:aws:iam::{}:oidc-provider/example.org".format(ACCOUNT_ID)
)
response = client.create_open_id_connect_provider(
@@ -2228,7 +2302,7 @@ def test_create_open_id_connect_provider():
)
response["OpenIDConnectProviderArn"].should.equal(
- "arn:aws:iam::123456789012:oidc-provider/example.org/oidc"
+ "arn:aws:iam::{}:oidc-provider/example.org/oidc".format(ACCOUNT_ID)
)
response = client.create_open_id_connect_provider(
@@ -2236,7 +2310,7 @@ def test_create_open_id_connect_provider():
)
response["OpenIDConnectProviderArn"].should.equal(
- "arn:aws:iam::123456789012:oidc-provider/example.org/oidc-query"
+ "arn:aws:iam::{}:oidc-provider/example.org/oidc-query".format(ACCOUNT_ID)
)
@@ -2450,7 +2524,7 @@ def test_get_account_password_policy_errors():
client.get_account_password_policy.when.called_with().should.throw(
ClientError,
- "The Password Policy with domain name 123456789012 cannot be found.",
+ "The Password Policy with domain name {} cannot be found.".format(ACCOUNT_ID),
)
@@ -2467,7 +2541,7 @@ def test_delete_account_password_policy():
client.get_account_password_policy.when.called_with().should.throw(
ClientError,
- "The Password Policy with domain name 123456789012 cannot be found.",
+ "The Password Policy with domain name {} cannot be found.".format(ACCOUNT_ID),
)
@@ -2596,3 +2670,33 @@ def test_get_account_summary():
"GroupsQuota": 300,
}
)
+
+
+@mock_iam()
+def test_list_user_tags():
+ """Tests both setting a tags on a user in create_user and list_user_tags"""
+ conn = boto3.client("iam", region_name="us-east-1")
+ conn.create_user(UserName="kenny-bania")
+ conn.create_user(
+ UserName="jackie-chiles", Tags=[{"Key": "Sue-Allen", "Value": "Oh-Henry"}]
+ )
+ conn.create_user(
+ UserName="cosmo",
+ Tags=[
+ {"Key": "Stan", "Value": "The Caddy"},
+ {"Key": "like-a", "Value": "glove"},
+ ],
+ )
+ response = conn.list_user_tags(UserName="kenny-bania")
+ response["Tags"].should.equal([])
+ response["IsTruncated"].should_not.be.ok
+
+ response = conn.list_user_tags(UserName="jackie-chiles")
+ response["Tags"].should.equal([{"Key": "Sue-Allen", "Value": "Oh-Henry"}])
+ response["IsTruncated"].should_not.be.ok
+
+ response = conn.list_user_tags(UserName="cosmo")
+ response["Tags"].should.equal(
+ [{"Key": "Stan", "Value": "The Caddy"}, {"Key": "like-a", "Value": "glove"}]
+ )
+ response["IsTruncated"].should_not.be.ok
diff --git a/tests/test_iam/test_iam_groups.py b/tests/test_iam/test_iam_groups.py
index 7b73e89ea..64d838e2b 100644
--- a/tests/test_iam/test_iam_groups.py
+++ b/tests/test_iam/test_iam_groups.py
@@ -10,6 +10,7 @@ from nose.tools import assert_raises
from boto.exception import BotoServerError
from botocore.exceptions import ClientError
from moto import mock_iam, mock_iam_deprecated
+from moto.core import ACCOUNT_ID
MOCK_POLICY = """
{
@@ -51,16 +52,15 @@ def test_get_group_current():
assert result["Group"]["GroupName"] == "my-group"
assert isinstance(result["Group"]["CreateDate"], datetime)
assert result["Group"]["GroupId"]
- assert result["Group"]["Arn"] == "arn:aws:iam::123456789012:group/my-group"
+ assert result["Group"]["Arn"] == "arn:aws:iam::{}:group/my-group".format(ACCOUNT_ID)
assert not result["Users"]
# Make a group with a different path:
other_group = conn.create_group(GroupName="my-other-group", Path="some/location")
assert other_group["Group"]["Path"] == "some/location"
- assert (
- other_group["Group"]["Arn"]
- == "arn:aws:iam::123456789012:group/some/location/my-other-group"
- )
+ assert other_group["Group"][
+ "Arn"
+ ] == "arn:aws:iam::{}:group/some/location/my-other-group".format(ACCOUNT_ID)
@mock_iam_deprecated()
diff --git a/tests/test_kinesis/test_firehose.py b/tests/test_kinesis/test_firehose.py
index 7101c4eaf..5e8c4aa08 100644
--- a/tests/test_kinesis/test_firehose.py
+++ b/tests/test_kinesis/test_firehose.py
@@ -7,6 +7,7 @@ import boto3
import sure # noqa
from moto import mock_kinesis
+from moto.core import ACCOUNT_ID
def create_s3_delivery_stream(client, stream_name):
@@ -14,7 +15,7 @@ def create_s3_delivery_stream(client, stream_name):
DeliveryStreamName=stream_name,
DeliveryStreamType="DirectPut",
ExtendedS3DestinationConfiguration={
- "RoleARN": "arn:aws:iam::123456789012:role/firehose_delivery_role",
+ "RoleARN": "arn:aws:iam::{}:role/firehose_delivery_role".format(ACCOUNT_ID),
"BucketARN": "arn:aws:s3:::kinesis-test",
"Prefix": "myFolder/",
"CompressionFormat": "UNCOMPRESSED",
@@ -26,7 +27,9 @@ def create_s3_delivery_stream(client, stream_name):
},
"SchemaConfiguration": {
"DatabaseName": stream_name,
- "RoleARN": "arn:aws:iam::123456789012:role/firehose_delivery_role",
+ "RoleARN": "arn:aws:iam::{}:role/firehose_delivery_role".format(
+ ACCOUNT_ID
+ ),
"TableName": "outputTable",
},
},
@@ -38,7 +41,7 @@ def create_redshift_delivery_stream(client, stream_name):
return client.create_delivery_stream(
DeliveryStreamName=stream_name,
RedshiftDestinationConfiguration={
- "RoleARN": "arn:aws:iam::123456789012:role/firehose_delivery_role",
+ "RoleARN": "arn:aws:iam::{}:role/firehose_delivery_role".format(ACCOUNT_ID),
"ClusterJDBCURL": "jdbc:redshift://host.amazonaws.com:5439/database",
"CopyCommand": {
"DataTableName": "outputTable",
@@ -47,7 +50,9 @@ def create_redshift_delivery_stream(client, stream_name):
"Username": "username",
"Password": "password",
"S3Configuration": {
- "RoleARN": "arn:aws:iam::123456789012:role/firehose_delivery_role",
+ "RoleARN": "arn:aws:iam::{}:role/firehose_delivery_role".format(
+ ACCOUNT_ID
+ ),
"BucketARN": "arn:aws:s3:::kinesis-test",
"Prefix": "myFolder/",
"BufferingHints": {"SizeInMBs": 123, "IntervalInSeconds": 124},
@@ -81,7 +86,9 @@ def test_create_redshift_delivery_stream():
{
"DestinationId": "string",
"RedshiftDestinationDescription": {
- "RoleARN": "arn:aws:iam::123456789012:role/firehose_delivery_role",
+ "RoleARN": "arn:aws:iam::{}:role/firehose_delivery_role".format(
+ ACCOUNT_ID
+ ),
"ClusterJDBCURL": "jdbc:redshift://host.amazonaws.com:5439/database",
"CopyCommand": {
"DataTableName": "outputTable",
@@ -89,7 +96,9 @@ def test_create_redshift_delivery_stream():
},
"Username": "username",
"S3DestinationDescription": {
- "RoleARN": "arn:aws:iam::123456789012:role/firehose_delivery_role",
+ "RoleARN": "arn:aws:iam::{}:role/firehose_delivery_role".format(
+ ACCOUNT_ID
+ ),
"BucketARN": "arn:aws:s3:::kinesis-test",
"Prefix": "myFolder/",
"BufferingHints": {
@@ -130,7 +139,9 @@ def test_create_s3_delivery_stream():
{
"DestinationId": "string",
"ExtendedS3DestinationDescription": {
- "RoleARN": "arn:aws:iam::123456789012:role/firehose_delivery_role",
+ "RoleARN": "arn:aws:iam::{}:role/firehose_delivery_role".format(
+ ACCOUNT_ID
+ ),
"BucketARN": "arn:aws:s3:::kinesis-test",
"Prefix": "myFolder/",
"CompressionFormat": "UNCOMPRESSED",
@@ -146,7 +157,9 @@ def test_create_s3_delivery_stream():
},
"SchemaConfiguration": {
"DatabaseName": "stream1",
- "RoleARN": "arn:aws:iam::123456789012:role/firehose_delivery_role",
+ "RoleARN": "arn:aws:iam::{}:role/firehose_delivery_role".format(
+ ACCOUNT_ID
+ ),
"TableName": "outputTable",
},
},
@@ -165,7 +178,7 @@ def test_create_stream_without_redshift():
response = client.create_delivery_stream(
DeliveryStreamName="stream1",
S3DestinationConfiguration={
- "RoleARN": "arn:aws:iam::123456789012:role/firehose_delivery_role",
+ "RoleARN": "arn:aws:iam::{}:role/firehose_delivery_role".format(ACCOUNT_ID),
"BucketARN": "arn:aws:s3:::kinesis-test",
"Prefix": "myFolder/",
"BufferingHints": {"SizeInMBs": 123, "IntervalInSeconds": 124},
@@ -191,8 +204,12 @@ def test_create_stream_without_redshift():
{
"DestinationId": "string",
"S3DestinationDescription": {
- "RoleARN": "arn:aws:iam::123456789012:role/firehose_delivery_role",
- "RoleARN": "arn:aws:iam::123456789012:role/firehose_delivery_role",
+ "RoleARN": "arn:aws:iam::{}:role/firehose_delivery_role".format(
+ ACCOUNT_ID
+ ),
+ "RoleARN": "arn:aws:iam::{}:role/firehose_delivery_role".format(
+ ACCOUNT_ID
+ ),
"BucketARN": "arn:aws:s3:::kinesis-test",
"Prefix": "myFolder/",
"BufferingHints": {"SizeInMBs": 123, "IntervalInSeconds": 124},
diff --git a/tests/test_kinesis/test_kinesis.py b/tests/test_kinesis/test_kinesis.py
index 308100d8b..de1764892 100644
--- a/tests/test_kinesis/test_kinesis.py
+++ b/tests/test_kinesis/test_kinesis.py
@@ -8,6 +8,7 @@ import boto3
from boto.kinesis.exceptions import ResourceNotFoundException, InvalidArgumentException
from moto import mock_kinesis, mock_kinesis_deprecated
+from moto.core import ACCOUNT_ID
@mock_kinesis_deprecated
@@ -21,7 +22,9 @@ def test_create_cluster():
stream = stream_response["StreamDescription"]
stream["StreamName"].should.equal("my_stream")
stream["HasMoreShards"].should.equal(False)
- stream["StreamARN"].should.equal("arn:aws:kinesis:us-west-2:123456789012:my_stream")
+ stream["StreamARN"].should.equal(
+ "arn:aws:kinesis:us-west-2:{}:my_stream".format(ACCOUNT_ID)
+ )
stream["StreamStatus"].should.equal("ACTIVE")
shards = stream["Shards"]
@@ -87,7 +90,7 @@ def test_describe_stream_summary():
stream["StreamName"].should.equal(stream_name)
stream["OpenShardCount"].should.equal(shard_count)
stream["StreamARN"].should.equal(
- "arn:aws:kinesis:us-west-2:123456789012:{}".format(stream_name)
+ "arn:aws:kinesis:us-west-2:{}:{}".format(ACCOUNT_ID, stream_name)
)
stream["StreamStatus"].should.equal("ACTIVE")
diff --git a/tests/test_redshift/test_redshift.py b/tests/test_redshift/test_redshift.py
index 528eaa5e0..6bb3b1396 100644
--- a/tests/test_redshift/test_redshift.py
+++ b/tests/test_redshift/test_redshift.py
@@ -18,6 +18,7 @@ from moto import mock_ec2
from moto import mock_ec2_deprecated
from moto import mock_redshift
from moto import mock_redshift_deprecated
+from moto.core import ACCOUNT_ID
@mock_redshift
@@ -998,12 +999,12 @@ def test_create_cluster_status_update():
def test_describe_tags_with_resource_type():
client = boto3.client("redshift", region_name="us-east-1")
cluster_identifier = "my_cluster"
- cluster_arn = "arn:aws:redshift:us-east-1:123456789012:" "cluster:{}".format(
- cluster_identifier
+ cluster_arn = "arn:aws:redshift:us-east-1:{}:" "cluster:{}".format(
+ ACCOUNT_ID, cluster_identifier
)
snapshot_identifier = "my_snapshot"
- snapshot_arn = "arn:aws:redshift:us-east-1:123456789012:" "snapshot:{}/{}".format(
- cluster_identifier, snapshot_identifier
+ snapshot_arn = "arn:aws:redshift:us-east-1:{}:" "snapshot:{}/{}".format(
+ ACCOUNT_ID, cluster_identifier, snapshot_identifier
)
tag_key = "test-tag-key"
tag_value = "test-tag-value"
@@ -1044,7 +1045,9 @@ def test_describe_tags_with_resource_type():
@mock_redshift
def test_describe_tags_cannot_specify_resource_type_and_resource_name():
client = boto3.client("redshift", region_name="us-east-1")
- resource_name = "arn:aws:redshift:us-east-1:123456789012:cluster:cluster-id"
+ resource_name = "arn:aws:redshift:us-east-1:{}:cluster:cluster-id".format(
+ ACCOUNT_ID
+ )
resource_type = "cluster"
client.describe_tags.when.called_with(
ResourceName=resource_name, ResourceType=resource_type
@@ -1055,12 +1058,12 @@ def test_describe_tags_cannot_specify_resource_type_and_resource_name():
def test_describe_tags_with_resource_name():
client = boto3.client("redshift", region_name="us-east-1")
cluster_identifier = "cluster-id"
- cluster_arn = "arn:aws:redshift:us-east-1:123456789012:" "cluster:{}".format(
- cluster_identifier
+ cluster_arn = "arn:aws:redshift:us-east-1:{}:" "cluster:{}".format(
+ ACCOUNT_ID, cluster_identifier
)
snapshot_identifier = "snapshot-id"
- snapshot_arn = "arn:aws:redshift:us-east-1:123456789012:" "snapshot:{}/{}".format(
- cluster_identifier, snapshot_identifier
+ snapshot_arn = "arn:aws:redshift:us-east-1:{}:" "snapshot:{}/{}".format(
+ ACCOUNT_ID, cluster_identifier, snapshot_identifier
)
tag_key = "test-tag-key"
tag_value = "test-tag-value"
@@ -1102,8 +1105,8 @@ def test_describe_tags_with_resource_name():
def test_create_tags():
client = boto3.client("redshift", region_name="us-east-1")
cluster_identifier = "cluster-id"
- cluster_arn = "arn:aws:redshift:us-east-1:123456789012:" "cluster:{}".format(
- cluster_identifier
+ cluster_arn = "arn:aws:redshift:us-east-1:{}:" "cluster:{}".format(
+ ACCOUNT_ID, cluster_identifier
)
tag_key = "test-tag-key"
tag_value = "test-tag-value"
@@ -1133,8 +1136,8 @@ def test_create_tags():
def test_delete_tags():
client = boto3.client("redshift", region_name="us-east-1")
cluster_identifier = "cluster-id"
- cluster_arn = "arn:aws:redshift:us-east-1:123456789012:" "cluster:{}".format(
- cluster_identifier
+ cluster_arn = "arn:aws:redshift:us-east-1:{}:" "cluster:{}".format(
+ ACCOUNT_ID, cluster_identifier
)
tag_key = "test-tag-key"
tag_value = "test-tag-value"
diff --git a/tests/test_s3/test_s3.py b/tests/test_s3/test_s3.py
index 8f3c3538c..682213d13 100644
--- a/tests/test_s3/test_s3.py
+++ b/tests/test_s3/test_s3.py
@@ -32,9 +32,10 @@ from nose.tools import assert_raises
import sure # noqa
-from moto import settings, mock_s3, mock_s3_deprecated
+from moto import settings, mock_s3, mock_s3_deprecated, mock_config
import moto.s3.models as s3model
from moto.core.exceptions import InvalidNextTokenException
+from moto.core.utils import py2_strip_unicode_keys
if settings.TEST_SERVER_MODE:
REDUCED_PART_SIZE = s3model.UPLOAD_PART_MIN_SIZE
@@ -1260,7 +1261,7 @@ def test_boto3_list_objects_truncated_response():
assert listed_object["Key"] == "one"
assert resp["MaxKeys"] == 1
assert resp["IsTruncated"] == True
- assert resp["Prefix"] == "None"
+ assert resp.get("Prefix") is None
assert resp["Delimiter"] == "None"
assert "NextMarker" in resp
@@ -1273,7 +1274,7 @@ def test_boto3_list_objects_truncated_response():
assert listed_object["Key"] == "three"
assert resp["MaxKeys"] == 1
assert resp["IsTruncated"] == True
- assert resp["Prefix"] == "None"
+ assert resp.get("Prefix") is None
assert resp["Delimiter"] == "None"
assert "NextMarker" in resp
@@ -1286,7 +1287,7 @@ def test_boto3_list_objects_truncated_response():
assert listed_object["Key"] == "two"
assert resp["MaxKeys"] == 1
assert resp["IsTruncated"] == False
- assert resp["Prefix"] == "None"
+ assert resp.get("Prefix") is None
assert resp["Delimiter"] == "None"
assert "NextMarker" not in resp
@@ -3278,6 +3279,148 @@ def test_delete_objects_with_url_encoded_key(key):
assert_deleted()
+@mock_s3
+@mock_config
+def test_public_access_block():
+ client = boto3.client("s3")
+ client.create_bucket(Bucket="mybucket")
+
+ # Try to get the public access block (should not exist by default)
+ with assert_raises(ClientError) as ce:
+ client.get_public_access_block(Bucket="mybucket")
+
+ assert (
+ ce.exception.response["Error"]["Code"] == "NoSuchPublicAccessBlockConfiguration"
+ )
+ assert (
+ ce.exception.response["Error"]["Message"]
+ == "The public access block configuration was not found"
+ )
+ assert ce.exception.response["ResponseMetadata"]["HTTPStatusCode"] == 404
+
+ # Put a public block in place:
+ test_map = {
+ "BlockPublicAcls": False,
+ "IgnorePublicAcls": False,
+ "BlockPublicPolicy": False,
+ "RestrictPublicBuckets": False,
+ }
+
+ for field in test_map.keys():
+ # Toggle:
+ test_map[field] = True
+
+ client.put_public_access_block(
+ Bucket="mybucket", PublicAccessBlockConfiguration=test_map
+ )
+
+ # Test:
+ assert (
+ test_map
+ == client.get_public_access_block(Bucket="mybucket")[
+ "PublicAccessBlockConfiguration"
+ ]
+ )
+
+ # Assume missing values are default False:
+ client.put_public_access_block(
+ Bucket="mybucket", PublicAccessBlockConfiguration={"BlockPublicAcls": True}
+ )
+ assert client.get_public_access_block(Bucket="mybucket")[
+ "PublicAccessBlockConfiguration"
+ ] == {
+ "BlockPublicAcls": True,
+ "IgnorePublicAcls": False,
+ "BlockPublicPolicy": False,
+ "RestrictPublicBuckets": False,
+ }
+
+ # Test with a blank PublicAccessBlockConfiguration:
+ with assert_raises(ClientError) as ce:
+ client.put_public_access_block(
+ Bucket="mybucket", PublicAccessBlockConfiguration={}
+ )
+
+ assert ce.exception.response["Error"]["Code"] == "InvalidRequest"
+ assert (
+ ce.exception.response["Error"]["Message"]
+ == "Must specify at least one configuration."
+ )
+ assert ce.exception.response["ResponseMetadata"]["HTTPStatusCode"] == 400
+
+ # Test that things work with AWS Config:
+ config_client = boto3.client("config", region_name="us-east-1")
+ result = config_client.get_resource_config_history(
+ resourceType="AWS::S3::Bucket", resourceId="mybucket"
+ )
+ pub_block_config = json.loads(
+ result["configurationItems"][0]["supplementaryConfiguration"][
+ "PublicAccessBlockConfiguration"
+ ]
+ )
+
+ assert pub_block_config == {
+ "blockPublicAcls": True,
+ "ignorePublicAcls": False,
+ "blockPublicPolicy": False,
+ "restrictPublicBuckets": False,
+ }
+
+ # Delete:
+ client.delete_public_access_block(Bucket="mybucket")
+
+ with assert_raises(ClientError) as ce:
+ client.get_public_access_block(Bucket="mybucket")
+ assert (
+ ce.exception.response["Error"]["Code"] == "NoSuchPublicAccessBlockConfiguration"
+ )
+
+
+@mock_s3
+def test_s3_public_access_block_to_config_dict():
+ from moto.s3.config import s3_config_query
+
+ # With 1 bucket in us-west-2:
+ s3_config_query.backends["global"].create_bucket("bucket1", "us-west-2")
+
+ public_access_block = {
+ "BlockPublicAcls": "True",
+ "IgnorePublicAcls": "False",
+ "BlockPublicPolicy": "True",
+ "RestrictPublicBuckets": "False",
+ }
+
+ # Python 2 unicode issues:
+ if sys.version_info[0] < 3:
+ public_access_block = py2_strip_unicode_keys(public_access_block)
+
+ # Add a public access block:
+ s3_config_query.backends["global"].put_bucket_public_access_block(
+ "bucket1", public_access_block
+ )
+
+ result = (
+ s3_config_query.backends["global"]
+ .buckets["bucket1"]
+ .public_access_block.to_config_dict()
+ )
+
+ convert_bool = lambda x: x == "True"
+ for key, value in public_access_block.items():
+ assert result[
+ "{lowercase}{rest}".format(lowercase=key[0].lower(), rest=key[1:])
+ ] == convert_bool(value)
+
+ # Verify that this resides in the full bucket's to_config_dict:
+ full_result = s3_config_query.backends["global"].buckets["bucket1"].to_config_dict()
+ assert (
+ json.loads(
+ full_result["supplementaryConfiguration"]["PublicAccessBlockConfiguration"]
+ )
+ == result
+ )
+
+
@mock_s3
def test_list_config_discovered_resources():
from moto.s3.config import s3_config_query
diff --git a/tests/test_ses/test_ses_sns_boto3.py b/tests/test_ses/test_ses_sns_boto3.py
index a55c150ff..fc58d88aa 100644
--- a/tests/test_ses/test_ses_sns_boto3.py
+++ b/tests/test_ses/test_ses_sns_boto3.py
@@ -10,6 +10,7 @@ import sure # noqa
from nose import tools
from moto import mock_ses, mock_sns, mock_sqs
from moto.ses.models import SESFeedback
+from moto.core import ACCOUNT_ID
@mock_ses
@@ -35,7 +36,7 @@ def __setup_feedback_env__(
sns_conn.subscribe(
TopicArn=topic_arn,
Protocol="sqs",
- Endpoint="arn:aws:sqs:%s:123456789012:%s" % (region, queue),
+ Endpoint="arn:aws:sqs:%s:%s:%s" % (region, ACCOUNT_ID, queue),
)
# Verify SES domain
ses_conn.verify_domain_identity(Domain=domain)
diff --git a/tests/test_sns/test_application.py b/tests/test_sns/test_application.py
index efa5e0f3e..e4fe93d53 100644
--- a/tests/test_sns/test_application.py
+++ b/tests/test_sns/test_application.py
@@ -3,6 +3,7 @@ from __future__ import unicode_literals
import boto
from boto.exception import BotoServerError
from moto import mock_sns_deprecated
+from moto.core import ACCOUNT_ID
import sure # noqa
@@ -21,7 +22,7 @@ def test_create_platform_application():
"CreatePlatformApplicationResult"
]["PlatformApplicationArn"]
application_arn.should.equal(
- "arn:aws:sns:us-east-1:123456789012:app/APNS/my-application"
+ "arn:aws:sns:us-east-1:{}:app/APNS/my-application".format(ACCOUNT_ID)
)
@@ -137,7 +138,7 @@ def test_create_platform_endpoint():
"CreatePlatformEndpointResult"
]["EndpointArn"]
endpoint_arn.should.contain(
- "arn:aws:sns:us-east-1:123456789012:endpoint/APNS/my-application/"
+ "arn:aws:sns:us-east-1:{}:endpoint/APNS/my-application/".format(ACCOUNT_ID)
)
diff --git a/tests/test_sns/test_application_boto3.py b/tests/test_sns/test_application_boto3.py
index 6f683b051..6f9be2926 100644
--- a/tests/test_sns/test_application_boto3.py
+++ b/tests/test_sns/test_application_boto3.py
@@ -4,6 +4,7 @@ import boto3
from botocore.exceptions import ClientError
from moto import mock_sns
import sure # noqa
+from moto.core import ACCOUNT_ID
@mock_sns
@@ -19,7 +20,7 @@ def test_create_platform_application():
)
application_arn = response["PlatformApplicationArn"]
application_arn.should.equal(
- "arn:aws:sns:us-east-1:123456789012:app/APNS/my-application"
+ "arn:aws:sns:us-east-1:{}:app/APNS/my-application".format(ACCOUNT_ID)
)
@@ -131,7 +132,7 @@ def test_create_platform_endpoint():
endpoint_arn = endpoint["EndpointArn"]
endpoint_arn.should.contain(
- "arn:aws:sns:us-east-1:123456789012:endpoint/APNS/my-application/"
+ "arn:aws:sns:us-east-1:{}:endpoint/APNS/my-application/".format(ACCOUNT_ID)
)
diff --git a/tests/test_sns/test_publishing.py b/tests/test_sns/test_publishing.py
index b45277bde..30fa80f15 100644
--- a/tests/test_sns/test_publishing.py
+++ b/tests/test_sns/test_publishing.py
@@ -7,9 +7,15 @@ from freezegun import freeze_time
import sure # noqa
from moto import mock_sns_deprecated, mock_sqs_deprecated
+from moto.core import ACCOUNT_ID
-
-MESSAGE_FROM_SQS_TEMPLATE = '{\n "Message": "%s",\n "MessageId": "%s",\n "Signature": "EXAMPLElDMXvB8r9R83tGoNn0ecwd5UjllzsvSvbItzfaMpN2nk5HVSw7XnOn/49IkxDKz8YrlH2qJXj2iZB0Zo2O71c4qQk1fMUDi3LGpij7RCW7AW9vYYsSqIKRnFS94ilu7NFhUzLiieYr4BKHpdTmdD6c0esKEYBpabxDSc=",\n "SignatureVersion": "1",\n "SigningCertURL": "https://sns.us-east-1.amazonaws.com/SimpleNotificationService-f3ecfb7224c7233fe7bb5f59f96de52f.pem",\n "Subject": "%s",\n "Timestamp": "2015-01-01T12:00:00.000Z",\n "TopicArn": "arn:aws:sns:%s:123456789012:some-topic",\n "Type": "Notification",\n "UnsubscribeURL": "https://sns.us-east-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-east-1:123456789012:some-topic:2bcfbf39-05c3-41de-beaa-fcfcc21c8f55"\n}'
+MESSAGE_FROM_SQS_TEMPLATE = (
+ '{\n "Message": "%s",\n "MessageId": "%s",\n "Signature": "EXAMPLElDMXvB8r9R83tGoNn0ecwd5UjllzsvSvbItzfaMpN2nk5HVSw7XnOn/49IkxDKz8YrlH2qJXj2iZB0Zo2O71c4qQk1fMUDi3LGpij7RCW7AW9vYYsSqIKRnFS94ilu7NFhUzLiieYr4BKHpdTmdD6c0esKEYBpabxDSc=",\n "SignatureVersion": "1",\n "SigningCertURL": "https://sns.us-east-1.amazonaws.com/SimpleNotificationService-f3ecfb7224c7233fe7bb5f59f96de52f.pem",\n "Subject": "%s",\n "Timestamp": "2015-01-01T12:00:00.000Z",\n "TopicArn": "arn:aws:sns:%s:'
+ + ACCOUNT_ID
+ + ':some-topic",\n "Type": "Notification",\n "UnsubscribeURL": "https://sns.us-east-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-east-1:'
+ + ACCOUNT_ID
+ + ':some-topic:2bcfbf39-05c3-41de-beaa-fcfcc21c8f55"\n}'
+)
@mock_sqs_deprecated
@@ -25,7 +31,9 @@ def test_publish_to_sqs():
sqs_conn = boto.connect_sqs()
sqs_conn.create_queue("test-queue")
- conn.subscribe(topic_arn, "sqs", "arn:aws:sqs:us-east-1:123456789012:test-queue")
+ conn.subscribe(
+ topic_arn, "sqs", "arn:aws:sqs:us-east-1:{}:test-queue".format(ACCOUNT_ID)
+ )
message_to_publish = "my message"
subject_to_publish = "test subject"
@@ -66,7 +74,9 @@ def test_publish_to_sqs_in_different_region():
sqs_conn = boto.sqs.connect_to_region("us-west-2")
sqs_conn.create_queue("test-queue")
- conn.subscribe(topic_arn, "sqs", "arn:aws:sqs:us-west-2:123456789012:test-queue")
+ conn.subscribe(
+ topic_arn, "sqs", "arn:aws:sqs:us-west-2:{}:test-queue".format(ACCOUNT_ID)
+ )
message_to_publish = "my message"
subject_to_publish = "test subject"
diff --git a/tests/test_sns/test_publishing_boto3.py b/tests/test_sns/test_publishing_boto3.py
index 5bda0720c..d85c8fefe 100644
--- a/tests/test_sns/test_publishing_boto3.py
+++ b/tests/test_sns/test_publishing_boto3.py
@@ -12,9 +12,15 @@ import responses
from botocore.exceptions import ClientError
from nose.tools import assert_raises
from moto import mock_sns, mock_sqs
+from moto.core import ACCOUNT_ID
-
-MESSAGE_FROM_SQS_TEMPLATE = '{\n "Message": "%s",\n "MessageId": "%s",\n "Signature": "EXAMPLElDMXvB8r9R83tGoNn0ecwd5UjllzsvSvbItzfaMpN2nk5HVSw7XnOn/49IkxDKz8YrlH2qJXj2iZB0Zo2O71c4qQk1fMUDi3LGpij7RCW7AW9vYYsSqIKRnFS94ilu7NFhUzLiieYr4BKHpdTmdD6c0esKEYBpabxDSc=",\n "SignatureVersion": "1",\n "SigningCertURL": "https://sns.us-east-1.amazonaws.com/SimpleNotificationService-f3ecfb7224c7233fe7bb5f59f96de52f.pem",\n "Subject": "my subject",\n "Timestamp": "2015-01-01T12:00:00.000Z",\n "TopicArn": "arn:aws:sns:%s:123456789012:some-topic",\n "Type": "Notification",\n "UnsubscribeURL": "https://sns.us-east-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-east-1:123456789012:some-topic:2bcfbf39-05c3-41de-beaa-fcfcc21c8f55"\n}'
+MESSAGE_FROM_SQS_TEMPLATE = (
+ '{\n "Message": "%s",\n "MessageId": "%s",\n "Signature": "EXAMPLElDMXvB8r9R83tGoNn0ecwd5UjllzsvSvbItzfaMpN2nk5HVSw7XnOn/49IkxDKz8YrlH2qJXj2iZB0Zo2O71c4qQk1fMUDi3LGpij7RCW7AW9vYYsSqIKRnFS94ilu7NFhUzLiieYr4BKHpdTmdD6c0esKEYBpabxDSc=",\n "SignatureVersion": "1",\n "SigningCertURL": "https://sns.us-east-1.amazonaws.com/SimpleNotificationService-f3ecfb7224c7233fe7bb5f59f96de52f.pem",\n "Subject": "my subject",\n "Timestamp": "2015-01-01T12:00:00.000Z",\n "TopicArn": "arn:aws:sns:%s:'
+ + ACCOUNT_ID
+ + ':some-topic",\n "Type": "Notification",\n "UnsubscribeURL": "https://sns.us-east-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-east-1:'
+ + ACCOUNT_ID
+ + ':some-topic:2bcfbf39-05c3-41de-beaa-fcfcc21c8f55"\n}'
+)
@mock_sqs
@@ -31,7 +37,7 @@ def test_publish_to_sqs():
conn.subscribe(
TopicArn=topic_arn,
Protocol="sqs",
- Endpoint="arn:aws:sqs:us-east-1:123456789012:test-queue",
+ Endpoint="arn:aws:sqs:us-east-1:{}:test-queue".format(ACCOUNT_ID),
)
message = "my message"
with freeze_time("2015-01-01 12:00:00"):
@@ -88,7 +94,7 @@ def test_publish_to_sqs_bad():
conn.subscribe(
TopicArn=topic_arn,
Protocol="sqs",
- Endpoint="arn:aws:sqs:us-east-1:123456789012:test-queue",
+ Endpoint="arn:aws:sqs:us-east-1:{}:test-queue".format(ACCOUNT_ID),
)
message = "my message"
try:
@@ -149,7 +155,7 @@ def test_publish_to_sqs_msg_attr_byte_value():
conn.subscribe(
TopicArn=topic_arn,
Protocol="sqs",
- Endpoint="arn:aws:sqs:us-east-1:123456789012:test-queue",
+ Endpoint="arn:aws:sqs:us-east-1:{}:test-queue".format(ACCOUNT_ID),
)
message = "my message"
conn.publish(
@@ -243,7 +249,7 @@ def test_publish_to_sqs_dump_json():
conn.subscribe(
TopicArn=topic_arn,
Protocol="sqs",
- Endpoint="arn:aws:sqs:us-east-1:123456789012:test-queue",
+ Endpoint="arn:aws:sqs:us-east-1:{}:test-queue".format(ACCOUNT_ID),
)
message = json.dumps(
@@ -289,7 +295,7 @@ def test_publish_to_sqs_in_different_region():
conn.subscribe(
TopicArn=topic_arn,
Protocol="sqs",
- Endpoint="arn:aws:sqs:us-west-2:123456789012:test-queue",
+ Endpoint="arn:aws:sqs:us-west-2:{}:test-queue".format(ACCOUNT_ID),
)
message = "my message"
@@ -348,7 +354,7 @@ def test_publish_subject():
conn.subscribe(
TopicArn=topic_arn,
Protocol="sqs",
- Endpoint="arn:aws:sqs:us-east-1:123456789012:test-queue",
+ Endpoint="arn:aws:sqs:us-east-1:{}:test-queue".format(ACCOUNT_ID),
)
message = "my message"
subject1 = "test subject"
diff --git a/tests/test_sns/test_server.py b/tests/test_sns/test_server.py
index ec8bbe201..78bc147df 100644
--- a/tests/test_sns/test_server.py
+++ b/tests/test_sns/test_server.py
@@ -1,4 +1,5 @@
from __future__ import unicode_literals
+from moto.core import ACCOUNT_ID
import sure # noqa
@@ -16,11 +17,11 @@ def test_sns_server_get():
topic_data = test_client.action_data("CreateTopic", Name="testtopic")
topic_data.should.contain("CreateTopicResult")
topic_data.should.contain(
- "arn:aws:sns:us-east-1:123456789012:testtopic"
+ "arn:aws:sns:us-east-1:{}:testtopic".format(ACCOUNT_ID)
)
topics_data = test_client.action_data("ListTopics")
topics_data.should.contain("ListTopicsResult")
topic_data.should.contain(
- "arn:aws:sns:us-east-1:123456789012:testtopic"
+ "arn:aws:sns:us-east-1:{}:testtopic".format(ACCOUNT_ID)
)
diff --git a/tests/test_sns/test_subscriptions.py b/tests/test_sns/test_subscriptions.py
index fbd4274f4..f773438d7 100644
--- a/tests/test_sns/test_subscriptions.py
+++ b/tests/test_sns/test_subscriptions.py
@@ -54,9 +54,10 @@ def test_deleting_subscriptions_by_deleting_topic():
]["Subscriptions"]
subscriptions.should.have.length_of(1)
subscription = subscriptions[0]
+ subscription_arn = subscription["SubscriptionArn"]
subscription["TopicArn"].should.equal(topic_arn)
subscription["Protocol"].should.equal("http")
- subscription["SubscriptionArn"].should.contain(topic_arn)
+ subscription_arn.should.contain(topic_arn)
subscription["Endpoint"].should.equal("http://example.com/")
# Now delete the topic
@@ -67,12 +68,25 @@ def test_deleting_subscriptions_by_deleting_topic():
topics = topics_json["ListTopicsResponse"]["ListTopicsResult"]["Topics"]
topics.should.have.length_of(0)
- # And there should be zero subscriptions left
+ # And the subscription should still be left
+ subscriptions = conn.get_all_subscriptions()["ListSubscriptionsResponse"][
+ "ListSubscriptionsResult"
+ ]["Subscriptions"]
+ subscriptions.should.have.length_of(1)
+ subscription = subscriptions[0]
+ subscription["SubscriptionArn"].should.equal(subscription_arn)
+
+ # Now delete hanging subscription
+ conn.unsubscribe(subscription_arn)
+
subscriptions = conn.get_all_subscriptions()["ListSubscriptionsResponse"][
"ListSubscriptionsResult"
]["Subscriptions"]
subscriptions.should.have.length_of(0)
+ # Deleting it again should not result in any error
+ conn.unsubscribe(subscription_arn)
+
@mock_sns_deprecated
def test_getting_subscriptions_by_topic():
diff --git a/tests/test_sns/test_subscriptions_boto3.py b/tests/test_sns/test_subscriptions_boto3.py
index 6aab6b369..d91b3566b 100644
--- a/tests/test_sns/test_subscriptions_boto3.py
+++ b/tests/test_sns/test_subscriptions_boto3.py
@@ -8,7 +8,11 @@ from botocore.exceptions import ClientError
from nose.tools import assert_raises
from moto import mock_sns
-from moto.sns.models import DEFAULT_PAGE_SIZE
+from moto.sns.models import (
+ DEFAULT_PAGE_SIZE,
+ DEFAULT_EFFECTIVE_DELIVERY_POLICY,
+ DEFAULT_ACCOUNT_ID,
+)
@mock_sns
@@ -93,34 +97,48 @@ def test_creating_subscription():
@mock_sns
-def test_deleting_subscriptions_by_deleting_topic():
- conn = boto3.client("sns", region_name="us-east-1")
- conn.create_topic(Name="some-topic")
- response = conn.list_topics()
+def test_unsubscribe_from_deleted_topic():
+ client = boto3.client("sns", region_name="us-east-1")
+ client.create_topic(Name="some-topic")
+ response = client.list_topics()
topic_arn = response["Topics"][0]["TopicArn"]
- conn.subscribe(TopicArn=topic_arn, Protocol="http", Endpoint="http://example.com/")
+ client.subscribe(
+ TopicArn=topic_arn, Protocol="http", Endpoint="http://example.com/"
+ )
- subscriptions = conn.list_subscriptions()["Subscriptions"]
+ subscriptions = client.list_subscriptions()["Subscriptions"]
subscriptions.should.have.length_of(1)
subscription = subscriptions[0]
+ subscription_arn = subscription["SubscriptionArn"]
subscription["TopicArn"].should.equal(topic_arn)
subscription["Protocol"].should.equal("http")
- subscription["SubscriptionArn"].should.contain(topic_arn)
+ subscription_arn.should.contain(topic_arn)
subscription["Endpoint"].should.equal("http://example.com/")
# Now delete the topic
- conn.delete_topic(TopicArn=topic_arn)
+ client.delete_topic(TopicArn=topic_arn)
# And there should now be 0 topics
- topics_json = conn.list_topics()
+ topics_json = client.list_topics()
topics = topics_json["Topics"]
topics.should.have.length_of(0)
- # And there should be zero subscriptions left
- subscriptions = conn.list_subscriptions()["Subscriptions"]
+ # And the subscription should still be left
+ subscriptions = client.list_subscriptions()["Subscriptions"]
+ subscriptions.should.have.length_of(1)
+ subscription = subscriptions[0]
+ subscription["SubscriptionArn"].should.equal(subscription_arn)
+
+ # Now delete hanging subscription
+ client.unsubscribe(SubscriptionArn=subscription_arn)
+
+ subscriptions = client.list_subscriptions()["Subscriptions"]
subscriptions.should.have.length_of(0)
+ # Deleting it again should not result in any error
+ client.unsubscribe(SubscriptionArn=subscription_arn)
+
@mock_sns
def test_getting_subscriptions_by_topic():
@@ -195,21 +213,23 @@ def test_subscribe_attributes():
resp = client.subscribe(TopicArn=arn, Protocol="http", Endpoint="http://test.com")
- attributes = client.get_subscription_attributes(
+ response = client.get_subscription_attributes(
SubscriptionArn=resp["SubscriptionArn"]
)
- attributes.should.contain("Attributes")
- attributes["Attributes"].should.contain("PendingConfirmation")
- attributes["Attributes"]["PendingConfirmation"].should.equal("false")
- attributes["Attributes"].should.contain("Endpoint")
- attributes["Attributes"]["Endpoint"].should.equal("http://test.com")
- attributes["Attributes"].should.contain("TopicArn")
- attributes["Attributes"]["TopicArn"].should.equal(arn)
- attributes["Attributes"].should.contain("Protocol")
- attributes["Attributes"]["Protocol"].should.equal("http")
- attributes["Attributes"].should.contain("SubscriptionArn")
- attributes["Attributes"]["SubscriptionArn"].should.equal(resp["SubscriptionArn"])
+ response.should.contain("Attributes")
+ attributes = response["Attributes"]
+ attributes["PendingConfirmation"].should.equal("false")
+ attributes["ConfirmationWasAuthenticated"].should.equal("true")
+ attributes["Endpoint"].should.equal("http://test.com")
+ attributes["TopicArn"].should.equal(arn)
+ attributes["Protocol"].should.equal("http")
+ attributes["SubscriptionArn"].should.equal(resp["SubscriptionArn"])
+ attributes["Owner"].should.equal(str(DEFAULT_ACCOUNT_ID))
+ attributes["RawMessageDelivery"].should.equal("false")
+ json.loads(attributes["EffectiveDeliveryPolicy"]).should.equal(
+ DEFAULT_EFFECTIVE_DELIVERY_POLICY
+ )
@mock_sns
diff --git a/tests/test_sns/test_topics.py b/tests/test_sns/test_topics.py
index 4a5100c94..e91ab6e2d 100644
--- a/tests/test_sns/test_topics.py
+++ b/tests/test_sns/test_topics.py
@@ -8,6 +8,7 @@ import sure # noqa
from boto.exception import BotoServerError
from moto import mock_sns_deprecated
from moto.sns.models import DEFAULT_EFFECTIVE_DELIVERY_POLICY, DEFAULT_PAGE_SIZE
+from moto.core import ACCOUNT_ID
@mock_sns_deprecated
@@ -19,7 +20,7 @@ def test_create_and_delete_topic():
topics = topics_json["ListTopicsResponse"]["ListTopicsResult"]["Topics"]
topics.should.have.length_of(1)
topics[0]["TopicArn"].should.equal(
- "arn:aws:sns:{0}:123456789012:some-topic".format(conn.region.name)
+ "arn:aws:sns:{0}:{1}:some-topic".format(conn.region.name, ACCOUNT_ID)
)
# Delete the topic
@@ -58,7 +59,9 @@ def test_topic_corresponds_to_region():
topic_arn = topics_json["ListTopicsResponse"]["ListTopicsResult"]["Topics"][0][
"TopicArn"
]
- topic_arn.should.equal("arn:aws:sns:{0}:123456789012:some-topic".format(region))
+ topic_arn.should.equal(
+ "arn:aws:sns:{0}:{1}:some-topic".format(region, ACCOUNT_ID)
+ )
@mock_sns_deprecated
@@ -75,9 +78,9 @@ def test_topic_attributes():
"GetTopicAttributesResult"
]["Attributes"]
attributes["TopicArn"].should.equal(
- "arn:aws:sns:{0}:123456789012:some-topic".format(conn.region.name)
+ "arn:aws:sns:{0}:{1}:some-topic".format(conn.region.name, ACCOUNT_ID)
)
- attributes["Owner"].should.equal(123456789012)
+ attributes["Owner"].should.equal(ACCOUNT_ID)
json.loads(attributes["Policy"]).should.equal(
{
"Version": "2008-10-17",
@@ -98,8 +101,10 @@ def test_topic_attributes():
"SNS:Publish",
"SNS:Receive",
],
- "Resource": "arn:aws:sns:us-east-1:123456789012:some-topic",
- "Condition": {"StringEquals": {"AWS:SourceOwner": "123456789012"}},
+ "Resource": "arn:aws:sns:us-east-1:{}:some-topic".format(
+ ACCOUNT_ID
+ ),
+ "Condition": {"StringEquals": {"AWS:SourceOwner": ACCOUNT_ID}},
}
],
}
diff --git a/tests/test_sns/test_topics_boto3.py b/tests/test_sns/test_topics_boto3.py
index e4c9d303f..87800bd84 100644
--- a/tests/test_sns/test_topics_boto3.py
+++ b/tests/test_sns/test_topics_boto3.py
@@ -8,6 +8,7 @@ import sure # noqa
from botocore.exceptions import ClientError
from moto import mock_sns
from moto.sns.models import DEFAULT_EFFECTIVE_DELIVERY_POLICY, DEFAULT_PAGE_SIZE
+from moto.core import ACCOUNT_ID
@mock_sns
@@ -20,8 +21,8 @@ def test_create_and_delete_topic():
topics = topics_json["Topics"]
topics.should.have.length_of(1)
topics[0]["TopicArn"].should.equal(
- "arn:aws:sns:{0}:123456789012:{1}".format(
- conn._client_config.region_name, topic_name
+ "arn:aws:sns:{0}:{1}:{2}".format(
+ conn._client_config.region_name, ACCOUNT_ID, topic_name
)
)
@@ -132,7 +133,9 @@ def test_topic_corresponds_to_region():
conn.create_topic(Name="some-topic")
topics_json = conn.list_topics()
topic_arn = topics_json["Topics"][0]["TopicArn"]
- topic_arn.should.equal("arn:aws:sns:{0}:123456789012:some-topic".format(region))
+ topic_arn.should.equal(
+ "arn:aws:sns:{0}:{1}:some-topic".format(region, ACCOUNT_ID)
+ )
@mock_sns
@@ -145,11 +148,11 @@ def test_topic_attributes():
attributes = conn.get_topic_attributes(TopicArn=topic_arn)["Attributes"]
attributes["TopicArn"].should.equal(
- "arn:aws:sns:{0}:123456789012:some-topic".format(
- conn._client_config.region_name
+ "arn:aws:sns:{0}:{1}:some-topic".format(
+ conn._client_config.region_name, ACCOUNT_ID
)
)
- attributes["Owner"].should.equal("123456789012")
+ attributes["Owner"].should.equal(ACCOUNT_ID)
json.loads(attributes["Policy"]).should.equal(
{
"Version": "2008-10-17",
@@ -170,8 +173,10 @@ def test_topic_attributes():
"SNS:Publish",
"SNS:Receive",
],
- "Resource": "arn:aws:sns:us-east-1:123456789012:some-topic",
- "Condition": {"StringEquals": {"AWS:SourceOwner": "123456789012"}},
+ "Resource": "arn:aws:sns:us-east-1:{}:some-topic".format(
+ ACCOUNT_ID
+ ),
+ "Condition": {"StringEquals": {"AWS:SourceOwner": ACCOUNT_ID}},
}
],
}
@@ -271,15 +276,19 @@ def test_add_remove_permissions():
"SNS:Publish",
"SNS:Receive",
],
- "Resource": "arn:aws:sns:us-east-1:123456789012:test-permissions",
- "Condition": {"StringEquals": {"AWS:SourceOwner": "123456789012"}},
+ "Resource": "arn:aws:sns:us-east-1:{}:test-permissions".format(
+ ACCOUNT_ID
+ ),
+ "Condition": {"StringEquals": {"AWS:SourceOwner": ACCOUNT_ID}},
},
{
"Sid": "test",
"Effect": "Allow",
"Principal": {"AWS": "arn:aws:iam::999999999999:root"},
"Action": "SNS:Publish",
- "Resource": "arn:aws:sns:us-east-1:123456789012:test-permissions",
+ "Resource": "arn:aws:sns:us-east-1:{}:test-permissions".format(
+ ACCOUNT_ID
+ ),
},
],
}
@@ -308,8 +317,10 @@ def test_add_remove_permissions():
"SNS:Publish",
"SNS:Receive",
],
- "Resource": "arn:aws:sns:us-east-1:123456789012:test-permissions",
- "Condition": {"StringEquals": {"AWS:SourceOwner": "123456789012"}},
+ "Resource": "arn:aws:sns:us-east-1:{}:test-permissions".format(
+ ACCOUNT_ID
+ ),
+ "Condition": {"StringEquals": {"AWS:SourceOwner": ACCOUNT_ID}},
}
],
}
@@ -334,7 +345,7 @@ def test_add_remove_permissions():
]
},
"Action": ["SNS:Publish", "SNS:Subscribe"],
- "Resource": "arn:aws:sns:us-east-1:123456789012:test-permissions",
+ "Resource": "arn:aws:sns:us-east-1:{}:test-permissions".format(ACCOUNT_ID),
}
)
diff --git a/tests/test_sqs/test_sqs.py b/tests/test_sqs/test_sqs.py
index 2af94c5fd..639d6e51c 100644
--- a/tests/test_sqs/test_sqs.py
+++ b/tests/test_sqs/test_sqs.py
@@ -21,6 +21,7 @@ from moto import mock_sqs, mock_sqs_deprecated, settings
from nose import SkipTest
from nose.tools import assert_raises
from tests.helpers import requires_boto_gte
+from moto.core import ACCOUNT_ID
@mock_sqs
@@ -283,7 +284,7 @@ def test_create_queues_in_multiple_region():
base_url = "https://us-west-1.queue.amazonaws.com"
west1_conn.list_queues()["QueueUrls"][0].should.equal(
- "{base_url}/123456789012/blah".format(base_url=base_url)
+ "{base_url}/{AccountId}/blah".format(base_url=base_url, AccountId=ACCOUNT_ID)
)
@@ -305,7 +306,9 @@ def test_get_queue_with_prefix():
base_url = "https://us-west-1.queue.amazonaws.com"
queue[0].should.equal(
- "{base_url}/123456789012/test-queue".format(base_url=base_url)
+ "{base_url}/{AccountId}/test-queue".format(
+ base_url=base_url, AccountId=ACCOUNT_ID
+ )
)
@@ -342,7 +345,7 @@ def test_get_queue_attributes():
response["Attributes"]["MaximumMessageSize"].should.equal("65536")
response["Attributes"]["MessageRetentionPeriod"].should.equal("345600")
response["Attributes"]["QueueArn"].should.equal(
- "arn:aws:sqs:us-east-1:123456789012:test-queue"
+ "arn:aws:sqs:us-east-1:{}:test-queue".format(ACCOUNT_ID)
)
response["Attributes"]["ReceiveMessageWaitTimeSeconds"].should.equal("0")
response["Attributes"]["VisibilityTimeout"].should.equal("30")
@@ -361,7 +364,7 @@ def test_get_queue_attributes():
{
"ApproximateNumberOfMessages": "0",
"MaximumMessageSize": "65536",
- "QueueArn": "arn:aws:sqs:us-east-1:123456789012:test-queue",
+ "QueueArn": "arn:aws:sqs:us-east-1:{}:test-queue".format(ACCOUNT_ID),
"VisibilityTimeout": "30",
}
)
@@ -851,7 +854,9 @@ def test_queue_attributes():
attributes = queue.get_attributes()
attributes["QueueArn"].should.look_like(
- "arn:aws:sqs:us-east-1:123456789012:%s" % queue_name
+ "arn:aws:sqs:us-east-1:{AccountId}:{name}".format(
+ AccountId=ACCOUNT_ID, name=queue_name
+ )
)
attributes["VisibilityTimeout"].should.look_like(str(visibility_timeout))
@@ -1402,7 +1407,7 @@ def test_redrive_policy_available():
def test_redrive_policy_non_existent_queue():
sqs = boto3.client("sqs", region_name="us-east-1")
redrive_policy = {
- "deadLetterTargetArn": "arn:aws:sqs:us-east-1:123456789012:no-queue",
+ "deadLetterTargetArn": "arn:aws:sqs:us-east-1:{}:no-queue".format(ACCOUNT_ID),
"maxReceiveCount": 1,
}
diff --git a/tests/test_ssm/test_ssm_boto3.py b/tests/test_ssm/test_ssm_boto3.py
index 1b02536a1..5b978520d 100644
--- a/tests/test_ssm/test_ssm_boto3.py
+++ b/tests/test_ssm/test_ssm_boto3.py
@@ -102,6 +102,18 @@ def test_get_parameters_by_path():
response = client.get_parameters_by_path(Path="/", Recursive=False)
len(response["Parameters"]).should.equal(2)
{p["Value"] for p in response["Parameters"]}.should.equal(set(["bar", "qux"]))
+ {p["ARN"] for p in response["Parameters"]}.should.equal(
+ set(
+ [
+ "arn:aws:ssm:us-east-1:1234567890:parameter/foo",
+ "arn:aws:ssm:us-east-1:1234567890:parameter/baz",
+ ]
+ )
+ )
+ {
+ p["LastModifiedDate"].should.be.a(datetime.datetime)
+ for p in response["Parameters"]
+ }
response = client.get_parameters_by_path(Path="/", Recursive=True)
len(response["Parameters"]).should.equal(9)
@@ -158,6 +170,20 @@ def test_get_parameters_by_path():
len(response["Parameters"]).should.equal(1)
{p["Name"] for p in response["Parameters"]}.should.equal(set(["/baz/pwd"]))
+ response = client.get_parameters_by_path(Path="/", Recursive=True, MaxResults=4)
+ len(response["Parameters"]).should.equal(4)
+ response["NextToken"].should.equal("4")
+ response = client.get_parameters_by_path(
+ Path="/", Recursive=True, MaxResults=4, NextToken=response["NextToken"]
+ )
+ len(response["Parameters"]).should.equal(4)
+ response["NextToken"].should.equal("8")
+ response = client.get_parameters_by_path(
+ Path="/", Recursive=True, MaxResults=4, NextToken=response["NextToken"]
+ )
+ len(response["Parameters"]).should.equal(1)
+ response.should_not.have.key("NextToken")
+
@mock_ssm
def test_put_parameter():
@@ -176,6 +202,11 @@ def test_put_parameter():
response["Parameters"][0]["Value"].should.equal("value")
response["Parameters"][0]["Type"].should.equal("String")
response["Parameters"][0]["Version"].should.equal(1)
+ response["Parameters"][0]["LastModifiedDate"].should.be.a(datetime.datetime)
+ response["Parameters"][0]["ARN"].should.equal(
+ "arn:aws:ssm:us-east-1:1234567890:parameter/test"
+ )
+ initial_modification_date = response["Parameters"][0]["LastModifiedDate"]
try:
client.put_parameter(
@@ -194,6 +225,12 @@ def test_put_parameter():
response["Parameters"][0]["Value"].should.equal("value")
response["Parameters"][0]["Type"].should.equal("String")
response["Parameters"][0]["Version"].should.equal(1)
+ response["Parameters"][0]["LastModifiedDate"].should.equal(
+ initial_modification_date
+ )
+ response["Parameters"][0]["ARN"].should.equal(
+ "arn:aws:ssm:us-east-1:1234567890:parameter/test"
+ )
response = client.put_parameter(
Name="test",
@@ -213,6 +250,12 @@ def test_put_parameter():
response["Parameters"][0]["Value"].should.equal("value 3")
response["Parameters"][0]["Type"].should.equal("String")
response["Parameters"][0]["Version"].should.equal(2)
+ response["Parameters"][0]["LastModifiedDate"].should_not.equal(
+ initial_modification_date
+ )
+ response["Parameters"][0]["ARN"].should.equal(
+ "arn:aws:ssm:us-east-1:1234567890:parameter/test"
+ )
@mock_ssm
@@ -239,6 +282,10 @@ def test_get_parameter():
response["Parameter"]["Name"].should.equal("test")
response["Parameter"]["Value"].should.equal("value")
response["Parameter"]["Type"].should.equal("String")
+ response["Parameter"]["LastModifiedDate"].should.be.a(datetime.datetime)
+ response["Parameter"]["ARN"].should.equal(
+ "arn:aws:ssm:us-east-1:1234567890:parameter/test"
+ )
@mock_ssm
diff --git a/tests/test_stepfunctions/test_stepfunctions.py b/tests/test_stepfunctions/test_stepfunctions.py
index 6c391f0d1..3e0a8115d 100644
--- a/tests/test_stepfunctions/test_stepfunctions.py
+++ b/tests/test_stepfunctions/test_stepfunctions.py
@@ -9,7 +9,7 @@ from botocore.exceptions import ClientError
from nose.tools import assert_raises
from moto import mock_sts, mock_stepfunctions
-
+from moto.core import ACCOUNT_ID
region = "us-east-1"
simple_definition = (
@@ -34,7 +34,7 @@ def test_state_machine_creation_succeeds():
response["ResponseMetadata"]["HTTPStatusCode"].should.equal(200)
response["creationDate"].should.be.a(datetime)
response["stateMachineArn"].should.equal(
- "arn:aws:states:" + region + ":123456789012:stateMachine:" + name
+ "arn:aws:states:" + region + ":" + ACCOUNT_ID + ":stateMachine:" + name
)
@@ -286,7 +286,7 @@ def test_state_machine_can_deleted_nonexisting_machine():
client = boto3.client("stepfunctions", region_name=region)
#
unknown_state_machine = (
- "arn:aws:states:" + region + ":123456789012:stateMachine:unknown"
+ "arn:aws:states:" + region + ":" + ACCOUNT_ID + ":stateMachine:unknown"
)
response = client.delete_state_machine(stateMachineArn=unknown_state_machine)
response["ResponseMetadata"]["HTTPStatusCode"].should.equal(200)
diff --git a/tests/test_sts/test_sts.py b/tests/test_sts/test_sts.py
index 2cb1c49e7..4dee9184f 100644
--- a/tests/test_sts/test_sts.py
+++ b/tests/test_sts/test_sts.py
@@ -10,7 +10,7 @@ import sure # noqa
from moto import mock_sts, mock_sts_deprecated, mock_iam, settings
-from moto.iam.models import ACCOUNT_ID
+from moto.core import ACCOUNT_ID
from moto.sts.responses import MAX_FEDERATION_TOKEN_POLICY_LENGTH