1import logging 2import random 3import string 4 5import pytest 6import salt.config 7import salt.loader 8import salt.states.boto_cloudwatch_event as boto_cloudwatch_event 9from tests.support.mock import MagicMock, patch 10 11boto = pytest.importorskip("boto") 12boto3 = pytest.importorskip("boto3", "1.2.1") 13botocore = pytest.importorskip("botocore", "1.4.41") 14 15log = logging.getLogger(__name__) 16 17 18class GlobalConfig: 19 region = "us-east-1" 20 access_key = "GKTADJGHEIQSXMKKRBJ08H" 21 secret_key = "askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs" 22 conn_parameters = { 23 "region": region, 24 "key": access_key, 25 "keyid": secret_key, 26 "profile": {}, 27 } 28 error_message = ( 29 "An error occurred (101) when calling the {0} operation: Test-defined error" 30 ) 31 error_content = {"Error": {"Code": 101, "Message": "Test-defined error"}} 32 rule_name = "test_thing_type" 33 rule_desc = "test_thing_type_desc" 34 rule_sched = "rate(20 min)" 35 rule_arn = "arn:::::rule/arn" 36 rule_ret = dict( 37 Arn=rule_arn, 38 Description=rule_desc, 39 EventPattern=None, 40 Name=rule_name, 41 RoleArn=None, 42 ScheduleExpression=rule_sched, 43 State="ENABLED", 44 ) 45 46 47@pytest.fixture 48def global_config(): 49 params = GlobalConfig() 50 return params 51 52 53@pytest.fixture 54def configure_loader_modules(): 55 opts = salt.config.DEFAULT_MINION_OPTS.copy() 56 opts["grains"] = salt.loader.grains(opts) 57 ctx = {} 58 utils = salt.loader.utils( 59 opts, 60 whitelist=["boto3", "args", "systemd", "path", "platform"], 61 context=ctx, 62 ) 63 serializers = salt.loader.serializers(opts) 64 funcs = funcs = salt.loader.minion_mods( 65 opts, context=ctx, utils=utils, whitelist=["boto_cloudwatch_event"] 66 ) 67 salt_states = salt.loader.states( 68 opts=opts, 69 functions=funcs, 70 utils=utils, 71 whitelist=["boto_cloudwatch_event"], 72 serializers=serializers, 73 ) 74 return { 75 boto_cloudwatch_event: { 76 "__opts__": opts, 77 "__salt__": funcs, 78 "__utils__": utils, 79 "__states__": salt_states, 80 "__serializers__": serializers, 81 } 82 } 83 84 85def test_present_when_failing_to_describe_rule(global_config): 86 """ 87 Tests exceptions when checking rule existence 88 """ 89 global_config.conn_parameters["key"] = "".join( 90 random.choice(string.ascii_lowercase + string.digits) for _ in range(50) 91 ) 92 patcher = patch("boto3.session.Session") 93 mock_session = patcher.start() 94 session_instance = mock_session.return_value 95 conn = MagicMock() 96 session_instance.client.return_value = conn 97 conn.list_rules.side_effect = botocore.exceptions.ClientError( 98 global_config.error_content, "error on list rules" 99 ) 100 result = boto_cloudwatch_event.__states__["boto_cloudwatch_event.present"]( 101 name="test present", 102 Name=global_config.rule_name, 103 Description=global_config.rule_desc, 104 ScheduleExpression=global_config.rule_sched, 105 Targets=[{"Id": "target1", "Arn": "arn::::::*"}], 106 **global_config.conn_parameters 107 ) 108 assert result.get("result") is False 109 assert "error on list rules" in result.get("comment", {}) 110 111 112def test_present_when_failing_to_create_a_new_rule(global_config): 113 """ 114 Tests present on a rule name that doesn't exist and 115 an error is thrown on creation. 116 """ 117 global_config.conn_parameters["key"] = "".join( 118 random.choice(string.ascii_lowercase + string.digits) for _ in range(50) 119 ) 120 patcher = patch("boto3.session.Session") 121 mock_session = patcher.start() 122 session_instance = mock_session.return_value 123 conn = MagicMock() 124 session_instance.client.return_value = conn 125 conn.list_rules.return_value = {"Rules": []} 126 conn.put_rule.side_effect = botocore.exceptions.ClientError( 127 global_config.error_content, "put_rule" 128 ) 129 result = boto_cloudwatch_event.__states__["boto_cloudwatch_event.present"]( 130 name="test present", 131 Name=global_config.rule_name, 132 Description=global_config.rule_desc, 133 ScheduleExpression=global_config.rule_sched, 134 Targets=[{"Id": "target1", "Arn": "arn::::::*"}], 135 **global_config.conn_parameters 136 ) 137 assert result.get("result") is False 138 assert "put_rule" in result.get("comment", "") 139 140 141def test_present_when_failing_to_describe_the_new_rule(global_config): 142 """ 143 Tests present on a rule name that doesn't exist and 144 an error is thrown when adding targets. 145 """ 146 global_config.conn_parameters["key"] = "".join( 147 random.choice(string.ascii_lowercase + string.digits) for _ in range(50) 148 ) 149 patcher = patch("boto3.session.Session") 150 mock_session = patcher.start() 151 session_instance = mock_session.return_value 152 conn = MagicMock() 153 session_instance.client.return_value = conn 154 conn.list_rules.return_value = {"Rules": []} 155 conn.put_rule.return_value = global_config.rule_ret 156 conn.describe_rule.side_effect = botocore.exceptions.ClientError( 157 global_config.error_content, "describe_rule" 158 ) 159 result = boto_cloudwatch_event.__states__["boto_cloudwatch_event.present"]( 160 name="test present", 161 Name=global_config.rule_name, 162 Description=global_config.rule_desc, 163 ScheduleExpression=global_config.rule_sched, 164 Targets=[{"Id": "target1", "Arn": "arn::::::*"}], 165 **global_config.conn_parameters 166 ) 167 assert result.get("result") is False 168 assert "describe_rule" in result.get("comment", "") 169 170 171def test_present_when_failing_to_create_a_new_rules_targets(global_config): 172 """ 173 Tests present on a rule name that doesn't exist and 174 an error is thrown when adding targets. 175 """ 176 global_config.conn_parameters["key"] = "".join( 177 random.choice(string.ascii_lowercase + string.digits) for _ in range(50) 178 ) 179 patcher = patch("boto3.session.Session") 180 mock_session = patcher.start() 181 session_instance = mock_session.return_value 182 conn = MagicMock() 183 session_instance.client.return_value = conn 184 conn.list_rules.return_value = {"Rules": []} 185 conn.put_rule.return_value = global_config.rule_ret 186 conn.describe_rule.return_value = global_config.rule_ret 187 conn.put_targets.side_effect = botocore.exceptions.ClientError( 188 global_config.error_content, "put_targets" 189 ) 190 result = boto_cloudwatch_event.__states__["boto_cloudwatch_event.present"]( 191 name="test present", 192 Name=global_config.rule_name, 193 Description=global_config.rule_desc, 194 ScheduleExpression=global_config.rule_sched, 195 Targets=[{"Id": "target1", "Arn": "arn::::::*"}], 196 **global_config.conn_parameters 197 ) 198 assert result.get("result") is False 199 assert "put_targets" in result.get("comment", "") 200 201 202def test_present_when_rule_does_not_exist(global_config): 203 """ 204 Tests the successful case of creating a new rule, and updating its 205 targets 206 """ 207 global_config.conn_parameters["key"] = "".join( 208 random.choice(string.ascii_lowercase + string.digits) for _ in range(50) 209 ) 210 patcher = patch("boto3.session.Session") 211 mock_session = patcher.start() 212 session_instance = mock_session.return_value 213 conn = MagicMock() 214 session_instance.client.return_value = conn 215 conn.list_rules.return_value = {"Rules": []} 216 conn.put_rule.return_value = global_config.rule_ret 217 conn.describe_rule.return_value = global_config.rule_ret 218 conn.put_targets.return_value = {"FailedEntryCount": 0} 219 result = boto_cloudwatch_event.__states__["boto_cloudwatch_event.present"]( 220 name="test present", 221 Name=global_config.rule_name, 222 Description=global_config.rule_desc, 223 ScheduleExpression=global_config.rule_sched, 224 Targets=[{"Id": "target1", "Arn": "arn::::::*"}], 225 **global_config.conn_parameters 226 ) 227 assert result.get("result") is True 228 229 230def test_present_when_failing_to_update_an_existing_rule(global_config): 231 """ 232 Tests present on an existing rule where an error is thrown on updating the pool properties. 233 """ 234 global_config.conn_parameters["key"] = "".join( 235 random.choice(string.ascii_lowercase + string.digits) for _ in range(50) 236 ) 237 patcher = patch("boto3.session.Session") 238 mock_session = patcher.start() 239 session_instance = mock_session.return_value 240 conn = MagicMock() 241 session_instance.client.return_value = conn 242 conn.list_rules.return_value = {"Rules": [global_config.rule_ret]} 243 conn.describe_rule.side_effect = botocore.exceptions.ClientError( 244 global_config.error_content, "describe_rule" 245 ) 246 result = boto_cloudwatch_event.__states__["boto_cloudwatch_event.present"]( 247 name="test present", 248 Name=global_config.rule_name, 249 Description=global_config.rule_desc, 250 ScheduleExpression=global_config.rule_sched, 251 Targets=[{"Id": "target1", "Arn": "arn::::::*"}], 252 **global_config.conn_parameters 253 ) 254 assert result.get("result") is False 255 assert "describe_rule" in result.get("comment", "") 256 257 258def test_present_when_failing_to_get_targets(global_config): 259 """ 260 Tests present on an existing rule where put_rule succeeded, but an error 261 is thrown on getting targets 262 """ 263 global_config.conn_parameters["key"] = "".join( 264 random.choice(string.ascii_lowercase + string.digits) for _ in range(50) 265 ) 266 patcher = patch("boto3.session.Session") 267 mock_session = patcher.start() 268 session_instance = mock_session.return_value 269 conn = MagicMock() 270 session_instance.client.return_value = conn 271 conn.list_rules.return_value = {"Rules": [global_config.rule_ret]} 272 conn.put_rule.return_value = global_config.rule_ret 273 conn.describe_rule.return_value = global_config.rule_ret 274 conn.list_targets_by_rule.side_effect = botocore.exceptions.ClientError( 275 global_config.error_content, "list_targets" 276 ) 277 result = boto_cloudwatch_event.__states__["boto_cloudwatch_event.present"]( 278 name="test present", 279 Name=global_config.rule_name, 280 Description=global_config.rule_desc, 281 ScheduleExpression=global_config.rule_sched, 282 Targets=[{"Id": "target1", "Arn": "arn::::::*"}], 283 **global_config.conn_parameters 284 ) 285 assert result.get("result") is False 286 assert "list_targets" in result.get("comment", "") 287 288 289def test_present_when_failing_to_put_targets(global_config): 290 """ 291 Tests present on an existing rule where put_rule succeeded, but an error 292 is thrown on putting targets 293 """ 294 global_config.conn_parameters["key"] = "".join( 295 random.choice(string.ascii_lowercase + string.digits) for _ in range(50) 296 ) 297 patcher = patch("boto3.session.Session") 298 mock_session = patcher.start() 299 session_instance = mock_session.return_value 300 conn = MagicMock() 301 session_instance.client.return_value = conn 302 conn.list_rules.return_value = {"Rules": []} 303 conn.put_rule.return_value = global_config.rule_ret 304 conn.describe_rule.return_value = global_config.rule_ret 305 conn.list_targets.return_value = {"Targets": []} 306 conn.put_targets.side_effect = botocore.exceptions.ClientError( 307 global_config.error_content, "put_targets" 308 ) 309 result = boto_cloudwatch_event.__states__["boto_cloudwatch_event.present"]( 310 name="test present", 311 Name=global_config.rule_name, 312 Description=global_config.rule_desc, 313 ScheduleExpression=global_config.rule_sched, 314 Targets=[{"Id": "target1", "Arn": "arn::::::*"}], 315 **global_config.conn_parameters 316 ) 317 assert result.get("result") is False 318 assert "put_targets" in result.get("comment", "") 319 320 321def test_present_when_putting_targets(global_config): 322 """ 323 Tests present on an existing rule where put_rule succeeded, and targets 324 must be added 325 """ 326 global_config.conn_parameters["key"] = "".join( 327 random.choice(string.ascii_lowercase + string.digits) for _ in range(50) 328 ) 329 patcher = patch("boto3.session.Session") 330 mock_session = patcher.start() 331 session_instance = mock_session.return_value 332 conn = MagicMock() 333 session_instance.client.return_value = conn 334 conn.list_rules.return_value = {"Rules": []} 335 conn.put_rule.return_value = global_config.rule_ret 336 conn.describe_rule.return_value = global_config.rule_ret 337 conn.list_targets.return_value = {"Targets": []} 338 conn.put_targets.return_value = {"FailedEntryCount": 0} 339 result = boto_cloudwatch_event.__states__["boto_cloudwatch_event.present"]( 340 name="test present", 341 Name=global_config.rule_name, 342 Description=global_config.rule_desc, 343 ScheduleExpression=global_config.rule_sched, 344 Targets=[{"Id": "target1", "Arn": "arn::::::*"}], 345 **global_config.conn_parameters 346 ) 347 assert result.get("result") is True 348 349 350def test_present_when_removing_targets(global_config): 351 """ 352 Tests present on an existing rule where put_rule succeeded, and targets 353 must be removed 354 """ 355 global_config.conn_parameters["key"] = "".join( 356 random.choice(string.ascii_lowercase + string.digits) for _ in range(50) 357 ) 358 patcher = patch("boto3.session.Session") 359 mock_session = patcher.start() 360 session_instance = mock_session.return_value 361 conn = MagicMock() 362 session_instance.client.return_value = conn 363 conn.list_rules.return_value = {"Rules": []} 364 conn.put_rule.return_value = global_config.rule_ret 365 conn.describe_rule.return_value = global_config.rule_ret 366 conn.list_targets.return_value = {"Targets": [{"Id": "target1"}, {"Id": "target2"}]} 367 conn.put_targets.return_value = {"FailedEntryCount": 0} 368 result = boto_cloudwatch_event.__states__["boto_cloudwatch_event.present"]( 369 name="test present", 370 Name=global_config.rule_name, 371 Description=global_config.rule_desc, 372 ScheduleExpression=global_config.rule_sched, 373 Targets=[{"Id": "target1", "Arn": "arn::::::*"}], 374 **global_config.conn_parameters 375 ) 376 assert result.get("result") is True 377 378 379def test_absent_when_failing_to_describe_rule(global_config): 380 """ 381 Tests exceptions when checking rule existence 382 """ 383 global_config.conn_parameters["key"] = "".join( 384 random.choice(string.ascii_lowercase + string.digits) for _ in range(50) 385 ) 386 patcher = patch("boto3.session.Session") 387 mock_session = patcher.start() 388 session_instance = mock_session.return_value 389 conn = MagicMock() 390 session_instance.client.return_value = conn 391 conn.list_rules.side_effect = botocore.exceptions.ClientError( 392 global_config.error_content, "error on list rules" 393 ) 394 result = boto_cloudwatch_event.__states__["boto_cloudwatch_event.absent"]( 395 name="test present", 396 Name=global_config.rule_name, 397 **global_config.conn_parameters 398 ) 399 assert result.get("result") is False 400 assert "error on list rules" in result.get("comment", {}) 401 402 403def test_absent_when_rule_does_not_exist(global_config): 404 """ 405 Tests absent on an non-existing rule 406 """ 407 global_config.conn_parameters["key"] = "".join( 408 random.choice(string.ascii_lowercase + string.digits) for _ in range(50) 409 ) 410 patcher = patch("boto3.session.Session") 411 mock_session = patcher.start() 412 session_instance = mock_session.return_value 413 conn = MagicMock() 414 session_instance.client.return_value = conn 415 conn.list_rules.return_value = {"Rules": []} 416 result = boto_cloudwatch_event.__states__["boto_cloudwatch_event.absent"]( 417 name="test absent", 418 Name=global_config.rule_name, 419 **global_config.conn_parameters 420 ) 421 assert result.get("result") is True 422 assert result["changes"] == {} 423 424 425def test_absent_when_failing_to_list_targets(global_config): 426 """ 427 Tests absent on an rule when the list_targets call fails 428 """ 429 global_config.conn_parameters["key"] = "".join( 430 random.choice(string.ascii_lowercase + string.digits) for _ in range(50) 431 ) 432 patcher = patch("boto3.session.Session") 433 mock_session = patcher.start() 434 session_instance = mock_session.return_value 435 conn = MagicMock() 436 session_instance.client.return_value = conn 437 conn.list_rules.return_value = {"Rules": [global_config.rule_ret]} 438 conn.list_targets_by_rule.side_effect = botocore.exceptions.ClientError( 439 global_config.error_content, "list_targets" 440 ) 441 result = boto_cloudwatch_event.__states__["boto_cloudwatch_event.absent"]( 442 name="test absent", 443 Name=global_config.rule_name, 444 **global_config.conn_parameters 445 ) 446 assert result.get("result") is False 447 assert "list_targets" in result.get("comment", "") 448 449 450def test_absent_when_failing_to_remove_targets_exception(global_config): 451 """ 452 Tests absent on an rule when the remove_targets call fails 453 """ 454 global_config.conn_parameters["key"] = "".join( 455 random.choice(string.ascii_lowercase + string.digits) for _ in range(50) 456 ) 457 patcher = patch("boto3.session.Session") 458 mock_session = patcher.start() 459 session_instance = mock_session.return_value 460 conn = MagicMock() 461 session_instance.client.return_value = conn 462 conn.list_rules.return_value = {"Rules": [global_config.rule_ret]} 463 conn.list_targets_by_rule.return_value = {"Targets": [{"Id": "target1"}]} 464 conn.remove_targets.side_effect = botocore.exceptions.ClientError( 465 global_config.error_content, "remove_targets" 466 ) 467 result = boto_cloudwatch_event.__states__["boto_cloudwatch_event.absent"]( 468 name="test absent", 469 Name=global_config.rule_name, 470 **global_config.conn_parameters 471 ) 472 assert result.get("result") is False 473 assert "remove_targets" in result.get("comment", "") 474 475 476def test_absent_when_failing_to_remove_targets_nonexception(global_config): 477 """ 478 Tests absent on an rule when the remove_targets call fails 479 """ 480 global_config.conn_parameters["key"] = "".join( 481 random.choice(string.ascii_lowercase + string.digits) for _ in range(50) 482 ) 483 patcher = patch("boto3.session.Session") 484 mock_session = patcher.start() 485 session_instance = mock_session.return_value 486 conn = MagicMock() 487 session_instance.client.return_value = conn 488 conn.list_rules.return_value = {"Rules": [global_config.rule_ret]} 489 conn.list_targets_by_rule.return_value = {"Targets": [{"Id": "target1"}]} 490 conn.remove_targets.return_value = {"FailedEntryCount": 1} 491 result = boto_cloudwatch_event.__states__["boto_cloudwatch_event.absent"]( 492 name="test absent", 493 Name=global_config.rule_name, 494 **global_config.conn_parameters 495 ) 496 assert result.get("result") is False 497 498 499def test_absent_when_failing_to_delete_rule(global_config): 500 """ 501 Tests absent on an rule when the delete_rule call fails 502 """ 503 global_config.conn_parameters["key"] = "".join( 504 random.choice(string.ascii_lowercase + string.digits) for _ in range(50) 505 ) 506 patcher = patch("boto3.session.Session") 507 mock_session = patcher.start() 508 session_instance = mock_session.return_value 509 conn = MagicMock() 510 session_instance.client.return_value = conn 511 conn.list_rules.return_value = {"Rules": [global_config.rule_ret]} 512 conn.list_targets_by_rule.return_value = {"Targets": [{"Id": "target1"}]} 513 conn.remove_targets.return_value = {"FailedEntryCount": 0} 514 conn.delete_rule.side_effect = botocore.exceptions.ClientError( 515 global_config.error_content, "delete_rule" 516 ) 517 result = boto_cloudwatch_event.__states__["boto_cloudwatch_event.absent"]( 518 name="test absent", 519 Name=global_config.rule_name, 520 **global_config.conn_parameters 521 ) 522 assert result.get("result") is False 523 assert "delete_rule" in result.get("comment", "") 524 525 526def test_absent(global_config): 527 """ 528 Tests absent on an rule 529 """ 530 global_config.conn_parameters["key"] = "".join( 531 random.choice(string.ascii_lowercase + string.digits) for _ in range(50) 532 ) 533 patcher = patch("boto3.session.Session") 534 mock_session = patcher.start() 535 session_instance = mock_session.return_value 536 conn = MagicMock() 537 session_instance.client.return_value = conn 538 conn.list_rules.return_value = {"Rules": [global_config.rule_ret]} 539 conn.list_targets_by_rule.return_value = {"Targets": [{"Id": "target1"}]} 540 conn.remove_targets.return_value = {"FailedEntryCount": 0} 541 result = boto_cloudwatch_event.__states__["boto_cloudwatch_event.absent"]( 542 name="test absent", 543 Name=global_config.rule_name, 544 **global_config.conn_parameters 545 ) 546 assert result.get("result") is True 547