From 7ad3855906f66ebc02af81933be7ceeb6a37dfeb Mon Sep 17 00:00:00 2001 From: Alex Kennedy Date: Tue, 7 Jan 2025 00:34:01 -0800 Subject: [PATCH] chore(docs): Incremental docs improvements (#536) --- LICENSE | 2 +- README.rst | 2 +- docs/basicuse.rst | 160 +++++++++--------- docs/development.rst | 30 ++-- docs/installation.rst | 2 +- docs/kingpin.actors.aws.rst | 31 ++-- docs/kingpin.actors.packagecloud.rst | 5 - docs/kingpin.actors.spotinst.rst | 9 +- examples/complex.json | 4 +- examples/test/server.pem | 6 +- kingpin/actors/aws/base.py | 13 +- kingpin/actors/aws/cloudformation.py | 31 ++-- kingpin/actors/aws/iam.py | 9 +- .../aws/test/integration_cloudformation.py | 16 +- kingpin/actors/aws/test/integration_s3.py | 8 +- .../actors/aws/test/test_cloudformation.py | 116 ++++++------- kingpin/actors/base.py | 37 ++-- kingpin/actors/group.py | 16 +- kingpin/actors/misc.py | 6 +- kingpin/actors/spotinst.py | 8 +- kingpin/actors/utils.py | 19 ++- 21 files changed, 265 insertions(+), 265 deletions(-) diff --git a/LICENSE b/LICENSE index 261eeb9e..79f47e18 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [yyyy] [name of copyright owner] + Copyright 2025 Nextdoor, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.rst b/README.rst index efe69749..d255a19a 100644 --- a/README.rst +++ b/README.rst @@ -7,7 +7,7 @@ Kingpin: Deployment Automation Engine Kingpin provides 3 main functions: -- **API Abstraction** - Job instructions are provided to Kingpin via a JSON based DSL (read below). The schema is strict and consistent from one action to another. +- **API Abstraction** - Job instructions are provided to Kingpin via a JSON based DSL (read below). The schema is strict and consistent from one action to another. - **Automation Engine** - Kingpin leverages python's `tornado `_ engine. - **Parallel Execution** - Aside from non-blocking network IO, Kingpin can execute any action in parallel with another. (Read group.Async below) diff --git a/docs/basicuse.rst b/docs/basicuse.rst index 6902e54c..55e5e515 100644 --- a/docs/basicuse.rst +++ b/docs/basicuse.rst @@ -1,45 +1,26 @@ Basic Use --------- -.. code-block:: guess +For basic command line options, run: + +.. code-block:: console $ kingpin --help - usage: kingpin [-h] [-s JSON/YAML] [-a ACTOR] [-E] [-p PARAMS] [-o OPTIONS] [-d] - [--build-only] [-l LEVEL] [-D] [-c] - - Kingpin v0.3.1a - - optional arguments: - -h, --help show this help message and exit - -s SCRIPT, --script SCRIPT Path to JSON/YAML Deployment Script - -a ACTOR, --actor ACTOR - Name of an Actor to execute (overrides --script) - -E, --explain Explain how an actor works. Requires --actor. - -p PARAMS, --param PARAMS - Actor Parameter to set (ie, warn_on_failure=true) - -o OPTIONS, --option OPTIONS - Actor Options to set (ie, elb_name=foobar) - -d, --dry Executes a dry run only. - --build-only Compile the input script without executing any runs - -l LEVEL, --level LEVEL - Set logging level (INFO|WARN|DEBUG|ERROR) - -D, --debug Equivalent to --level=DEBUG - -c, --color Colorize the log output The simplest use cases of this code can be better understood by looking at the -:download:`simple.json <../examples/simple.json>` file. Executing it is a -simple as this: +:download:`simple.json <../examples/simple.json>` file. Executing it is a simple +as this: -.. code-block:: bash +.. code-block:: console - $ (.venv)$ kingpin -s examples/simple.json -d + $ kingpin --dry --script examples/simple.json 2014-09-01 21:18:09,022 INFO [main stage (DRY Mode)] Beginning 2014-09-01 21:18:09,022 INFO [stage 1 (DRY Mode)] Beginning 2014-09-01 21:18:09,022 INFO [copy serverA (DRY Mode)] Beginning 2014-09-01 21:18:09,023 INFO [copy serverB (DRY Mode)] Beginning 2014-09-01 21:18:09,027 INFO [copy serverC (DRY Mode)] Beginning 2014-09-01 21:18:09,954 INFO [copy serverA (DRY Mode)] Verifying that array "kingpin-integration-testing" exists - ... + [...] 2014-09-01 21:18:14,533 INFO [stage 3 (DRY Mode)] Finished, success? True 2014-09-01 21:18:14,533 INFO [main stage (DRY Mode)] Finished, success? True @@ -114,12 +95,14 @@ The simplest JSON file could look like this: .. code-block:: json - { "actor": "hipchat.Message", - "condition": "true", + { + "actor": "hipchat.Message", + "condition": true, "warn_on_failure": true, "timeout": 30, "options": { - "message": "Beginning release %RELEASE%", "room": "Oncall" + "message": "Beginning release %RELEASE%", + "room": "Oncall" } } @@ -159,11 +142,15 @@ used to enable or disable execution of an actor in a given Kingpin run. The field defaults to enabled, but takes many different values which allow you to choose whether or not to execute portions of your script. -Conditions that behave as ``False``:: +Conditions that behave as ``False``: + +.. code-block:: text 0, '0', 'False', 'FALse', 'FALSE' -Conditions that behave as ``True``:: +Conditions that behave as ``True``: + +.. code-block:: text 'any string', 'true', 'TRUE', '1', 1 @@ -171,11 +158,13 @@ Example usage: .. code-block:: json - { "actor": "hipchat.Message", + { + "actor": "hipchat.Message", "condition": "%SEND_MESSAGE%", "warn_on_failure": true, "options": { - "message": "Beginning release %RELEASE%", "room": "Oncall" + "message": "Beginning release %RELEASE%", + "room": "Oncall" } } @@ -190,15 +179,19 @@ JavaScript style commenting inside of the script. Alternatively, if you're using YAML then you automatically get slightly easier syntax parsing, code commenting, etc. -Take this example:: +Take this example: - { "actor": "misc.Sleep", +.. code-block:: text + { + "actor": "misc.Sleep", /* Cool description */ "desc": 'This is funny', - - /* This shouldn't end with a comma, but does */ - "options": { "time": 30 }, } + "options": { + /* This shouldn't end with a comma, but does */ + "time": 30, + }, + } The above example would fail to parse in most JSON parsers, but in ``demjson`` it works just fine. You could also write this in YAML: @@ -208,7 +201,6 @@ it works just fine. You could also write this in YAML: actor: misc.Sleep # Some description here... desc: This is funny - # Comments are good! options: time: 30 @@ -225,12 +217,11 @@ thus the failure can be ignored if you choose to. Additionally, you can override the *global default* setting on the commandline with an environment variable: -- ``DEFAULT_TIMEOUT`` - Time (in seconds) to use as the default actor - timeout. +- ``DEFAULT_TIMEOUT`` - Time (in seconds) to use as the default actor timeout. Here is an example log output when the timer is exceeded: -.. code-block:: bash +.. code-block:: console $ DEFAULT_TIMEOUT=1 SLEEP=10 kingpin -s examples/sleep.json 11:55:16 INFO Rehearsing... Break a leg! @@ -263,11 +254,13 @@ As an example... If you take the following example code: .. code-block:: json - { "desc": "Outer group", + { + "desc": "Outer group", "actor": "group.Sync", "options": { "acts": [ - { "desc": "Sleep 10 seconds, but fail", + { + "desc": "Sleep 10 seconds, but fail", "actor": "misc.Sleep", "timeout": 1, "warn_on_failure": true, @@ -275,7 +268,8 @@ As an example... If you take the following example code: "sleep": 10 } }, - { "desc": "Sleep 2 seconds, but don't fail", + { + "desc": "Sleep 2 seconds, but don't fail", "actor": "misc.Sleep", "options": { "sleep": 2 @@ -305,7 +299,7 @@ notify you immediately. For an example, take a look at the :download:`complex.json <../examples/complex.json>` file, and these examples of execution. -.. code-block:: bash +.. code-block:: console # Here we forget to set any environment variables $ kingpin -s examples/complex.json -d @@ -330,7 +324,8 @@ Tokens and Contexts can have default values specified after a pipe `|` in the va .. code-block:: json - { "actor": "misc.Sleep", + { + "actor": "misc.Sleep", "desc": "Sleeping because %DESC%", "options": { "sleep": "%SLEEP|60%" @@ -350,7 +345,8 @@ from actor to actor. Here's a fairly trivial example. Take this simple .. code-block:: json - { "actor": "misc.Sleep", + { + "actor": "misc.Sleep", "desc": "Sleeping because %DESC%", "options": { "sleep": "%SLEEP%" @@ -361,7 +357,7 @@ One way to run this would be via the command line with the `$SLEEP` and `$DESC` environment variable set (*output stripped a bit for readability*): -.. code-block:: bash +.. code-block:: console $ SKIP_DRY=1 DESC=pigs SLEEP=0.1 kingpin --debug --script sleeper.json [Kingpin] Checking for required options: ['macro'] @@ -372,9 +368,9 @@ readability*): Building Actor "misc.Sleep" with args: {'init_tokens': '', u'options': {u'sleep': u'0.1'}, u'desc': u'Sleeping because pigs'} [Sleeping because pigs] Checking for required options: ['sleep'] [Sleeping because pigs] Initialized (warn_on_failure=False, strict_init_context=True) - + Lights, camera ... action! - + [Kingpin] Beginning [Kingpin] Condition True evaluates to True [Kingpin] kingpin.actors.misc.Macro._execute() deadline: None(s) @@ -395,7 +391,8 @@ for you, but still leaves the ``%SLEEP%`` token up to you: .. code-block:: json - { "actor": "misc.Macro", + { + "actor": "misc.Macro", "options": { "macro": "sleeper.json", "tokens": { @@ -407,7 +404,7 @@ for you, but still leaves the ``%SLEEP%`` token up to you: Now, watch us instantiate this wrapper - with `$DESC` and `$SLEEP` set. Notice how ``%DESC%`` is overridden by the token from the JSON wrapper? -.. code-block:: bash +.. code-block:: console $ SKIP_DRY=1 DESC=pigs SLEEP=0.1 kingpin --debug --script wrapper.json @@ -425,9 +422,9 @@ Notice how ``%DESC%`` is overridden by the token from the JSON wrapper? Building Actor "misc.Sleep" with args: {'init_tokens': '', u'options': {u'sleep': u'0.1'}, u'desc': u'Sleeping because flying-pigs'} [Sleeping because flying-pigs] Checking for required options: ['sleep'] [Sleeping because flying-pigs] Initialized (warn_on_failure=False, strict_init_context=True) - + Lights, camera ... action! - + [Kingpin] Beginning [Kingpin] Condition True evaluates to True [Kingpin] kingpin.actors.misc.Macro._execute() deadline: None(s) @@ -457,23 +454,27 @@ ability to define usable tokens, but any actor can then reference these tokens. .. code-block:: json - { "desc": "Send out hipchat notifications", + { + "desc": "Send out hipchat notifications", "actor": "group.Sync", "options": { - "contexts": [ { "ROOM": "Systems" } ], - "acts": [ - { "desc": "Notify {ROOM}", - "actor": "hipchat.Message", - "options": { - "room": "{ROOM}", - "message": "Hey room .. I'm done with something" - } - } - ] + "contexts": [ + { "ROOM": "Systems" } + ], + "acts": [ + { + "desc": "Notify {ROOM}", + "actor": "hipchat.Message", + "options": { + "room": "{ROOM}", + "message": "Hey room .. I'm done with something" + } + } + ] } } -.. code-block:: bash +.. code-block:: console 2015-01-14 15:03:16,840 INFO [DRY: Send out hipchat notifications] Beginning 1 actions 2015-01-14 15:03:16,840 INFO [DRY: Notify Systems] Sending message "Hey room .. I'm done with something" to Hipchat room "Systems" @@ -482,14 +483,16 @@ ability to define usable tokens, but any actor can then reference these tokens. .. code-block:: json - { "actor": "group.Async", + { + "actor": "group.Async", "options": { "contexts": [ { "ROOM": "Engineering", "WISDOM": "Get back to work" }, { "ROOM": "Cust Service", "WISDOM": "Have a nice day" } ], "acts": [ - { "desc": "Notify {ROOM}", + { + "desc": "Notify {ROOM}", "actor": "hipchat.Message", "options": { "room": "{ROOM}", @@ -500,7 +503,7 @@ ability to define usable tokens, but any actor can then reference these tokens. } } -.. code-block:: bash +.. code-block:: console 2015-01-14 15:02:22,165 INFO [DRY: kingpin.actor.group.Async] Beginning 2 actions 2015-01-14 15:02:22,165 INFO [DRY: Notify Engineering] Sending message "Hey room .. I'm done with the release. Get back to work" to Hipchat room "Engineering" @@ -518,12 +521,14 @@ then reference that file. Context files support `token-replacement`_ just like .. code-block:: json - { "desc": "Send ending notifications...", + { + "desc": "Send ending notifications...", "actor": "group.Async", "options": { "contexts": "data/notification-rooms.json", "acts": [ - { "desc": "Notify {ROOM}", + { + "desc": "Notify {ROOM}", "actor": "hipchat.Message", "options": { "room": "{ROOM}", @@ -561,7 +566,7 @@ Command-line Execution without JSON For the simple case of executing a single actor without too many options, you are able to pass these options in on the commandline to avoid writing any JSON. -.. code-block:: bash +.. code-block:: console $ kingpin --actor misc.Sleep --explain Sleeps for an arbitrary number of seconds. @@ -575,7 +580,8 @@ are able to pass these options in on the commandline to avoid writing any JSON. .. code-block:: json - { "actor": "misc.Sleep", + { + "actor": "misc.Sleep", "desc": "Sleep for 60 seconds", "options": { "sleep": 60 @@ -590,7 +596,7 @@ are able to pass these options in on the commandline to avoid writing any JSON. documentation. -.. code-block:: bash +.. code-block:: console $ kingpin --actor misc.Sleep --param warn_on_failure=true --option sleep=5 17:54:53 INFO Rehearsing... Break a leg! @@ -603,7 +609,7 @@ documentation. You can stack as many ``--option`` and ``--param`` command line options as you wish. -.. code-block:: bash +.. code-block:: console $ kingpin --actor misc.Sleep --param warn_on_failure=true --param condition=false --option "sleep=0.1" 17:59:46 INFO Rehearsing... Break a leg! diff --git a/docs/development.rst b/docs/development.rst index c1dab1b9..8607c314 100644 --- a/docs/development.rst +++ b/docs/development.rst @@ -7,7 +7,7 @@ Setting up your Environment Check out the code ^^^^^^^^^^^^^^^^^^ -.. code-block:: bash +.. code-block:: console $ git clone https://github.com:Nextdoor/kingpin Cloning into 'kingpin'... @@ -20,7 +20,7 @@ Check out the code Create your VirtualEnvironment ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. code-block:: bash +.. code-block:: console $ make venv $ source .venv/bin/activate @@ -43,11 +43,10 @@ suites. See below for the list. *Executing only the HTTP Tests* -.. code-block:: bash +.. code-block:: console - (.venv)Matts-MacBook-2:kingpin diranged$ INTEGRATION_TESTS=http make integration - INTEGRATION_TESTS=http PYFLAKES_NODOCTEST=True \ - python setup.py integration pep8 pyflakes + $ INTEGRATION_TESTS=http make integration + INTEGRATION_TESTS=http PYFLAKES_NODOCTEST=True python setup.py integration pep8 pyflakes running integration integration_base_get (integration_api.IntegrationRestConsumer) ... ok integration_delete (integration_api.IntegrationRestConsumer) ... ok @@ -78,7 +77,7 @@ suites. See below for the list. Class/Object Architecture ~~~~~~~~~~~~~~~~~~~~~~~~~ -:: +.. code-block:: text kingpin.rb | @@ -142,7 +141,7 @@ This is the basic structure for an actor class. 'name': (str, None, 'Your name'), 'world': (str, None, 'World we\'re saying hello to!'), } - + # Optionally, if you need to do any instantiation-level, non-blocking # validation checks (for example, looking for an API token) you can do # them in the __init__. Do *not* put blocking code in here. @@ -190,17 +189,18 @@ This is the basic structure for an actor class. # The meat of the work happens in the _execute() method. This method # is called by the BaseActor.execute() method. Your method must be - # wrapped in a gen.Coroutine wrapper. Note, the _execute() method takes - # no arguments, all arguments for the acter were passed in to the - # __init__() method. + # wrapped in a gen.Coroutine wrapper. + # + # Note, the _execute() method takes no arguments, all arguments for the + # acter were passed in to the __init__() method. @gen.coroutine def _execute(self): self.log.debug('Warming up the HelloWorld Actor') - + # Fire off an async request to a our private method for sending # hello world messages. Get the response and evaluate res = yield self._send_message( - self.option('name'), self.option('world')) + self.option('name'), self.option('world')) # Got a response. Did our message really go through though? if not res: @@ -411,9 +411,9 @@ actor wants to supply its own default description, it can be done like this: 'sleep': (int), REQUIRED, 'Number of seconds to do nothing.') } -.. code-block:: bash +.. code-block:: console - (.venv)Matts-MacBook-2:kingpin diranged$ python kingpin/bin/deploy.py --color --debug -a misc.Sleep -o sleep=10 --dry + $ python kingpin/bin/deploy.py --color --debug -a misc.Sleep -o sleep=10 --dry 09:55:08 DEBUG 33688 [kingpin.actors.utils ] [get_actor_class ] Tried importing "misc.Sleep" but failed: No module named misc 09:55:08 DEBUG 33688 [kingpin.actors.misc.Sleep ] [_validate_options ] [DRY: Sleeping for 10s] Checking for required options: ['sleep'] 09:55:08 DEBUG 33688 [kingpin.actors.misc.Sleep ] [__init__ ] [DRY: Sleeping for 10s] Initialized (warn_on_failure=False, strict_init_context=True) diff --git a/docs/installation.rst b/docs/installation.rst index 213c5add..ed11aa4f 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -3,6 +3,6 @@ Installation The simplest installation method is via `PyPI `__. -.. code-block:: bash +.. code-block:: console $ pip install kingpin diff --git a/docs/kingpin.actors.aws.rst b/docs/kingpin.actors.aws.rst index 7e45104b..8a261846 100644 --- a/docs/kingpin.actors.aws.rst +++ b/docs/kingpin.actors.aws.rst @@ -1,30 +1,31 @@ Amazon Web Services ~~~~~~~~~~~~~~~~~~~ -Documentation -^^^^^^^^^^^^^ -.. automodule:: kingpin.actors.aws.base - :noindex: - :members: - :exclude-members: ELBNotFound, InvalidMetaData +.. note:: + + There are more actors available in the :mod:`kingpin.actors.aws` module, but + the below are the most commonly used. CloudFormation ^^^^^^^^^^^^^^ -.. automodule:: kingpin.actors.aws.cloudformation +.. autoclass:: kingpin.actors.aws.cloudformation.Stack :noindex: - :members: - :exclude-members: CloudFormationBaseActor, CloudFormationError, InvalidTemplate, StackAlreadyExists, StackNotFound Identity and Access Management (IAM) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. automodule:: kingpin.actors.aws.iam +.. autoclass:: kingpin.actors.aws.iam.Role + :noindex: + +.. autoclass:: kingpin.actors.aws.iam.Group + :noindex: + +.. autoclass:: kingpin.actors.aws.iam.User + :noindex: + +.. autoclass:: kingpin.actors.aws.iam.InstanceProfile :noindex: - :members: - :exclude-members: IAMBaseActor Simple Storage Service (S3) ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. automodule:: kingpin.actors.aws.s3 +.. autoclass:: kingpin.actors.aws.s3.Bucket :noindex: - :members: - :exclude-members: InvalidBucketConfig, S3BaseActor diff --git a/docs/kingpin.actors.packagecloud.rst b/docs/kingpin.actors.packagecloud.rst index 286eed94..80797970 100644 --- a/docs/kingpin.actors.packagecloud.rst +++ b/docs/kingpin.actors.packagecloud.rst @@ -1,11 +1,6 @@ PackageCloud ~~~~~~~~~~~~ -Documentation -^^^^^^^^^^^^^ -.. automodule:: kingpin.actors.packagecloud - :noindex: - Delete ^^^^^^ .. autoclass:: kingpin.actors.packagecloud.Delete diff --git a/docs/kingpin.actors.spotinst.rst b/docs/kingpin.actors.spotinst.rst index aecaf8d8..b3fc5256 100644 --- a/docs/kingpin.actors.spotinst.rst +++ b/docs/kingpin.actors.spotinst.rst @@ -1,15 +1,8 @@ SpotInst -~~~~~~~~~~~~ - -Documentation -^^^^^^^^^^^^^ -.. automodule:: kingpin.actors.spotinst - :noindex: +~~~~~~~~ ElastiGroup ^^^^^^^^^^^ -.. autoclass:: kingpin.actors.spotinst.ElastiGroupSchema - :noindex: .. autoclass:: kingpin.actors.spotinst.ElastiGroup :noindex: diff --git a/examples/complex.json b/examples/complex.json index 75239ac9..8078527b 100644 --- a/examples/complex.json +++ b/examples/complex.json @@ -27,7 +27,7 @@ } ] } }, - + { "desc": "Join New Arrays to ELB", "actor": "group.Async", "options": { "acts": [ @@ -39,7 +39,7 @@ { "desc": "Wait for traffic to settle", "actor": "misc.Sleep", "options": { "sleep": 60 } }, - + { "desc": "Remove Old Arrays from ELB", "actor": "group.Async", "options": { "acts": [ diff --git a/examples/test/server.pem b/examples/test/server.pem index 2bcd24d6..75aefe89 100644 --- a/examples/test/server.pem +++ b/examples/test/server.pem @@ -33,14 +33,14 @@ Certificate: 3e:db Exponent: 65537 (0x10001) X509v3 extensions: - X509v3 Subject Key Identifier: + X509v3 Subject Key Identifier: 1B:AB:32:CE:F1:FC:38:7C:8A:6D:B1:D4:28:91:DB:EE:62:A6:F2:F9 - X509v3 Authority Key Identifier: + X509v3 Authority Key Identifier: keyid:1B:AB:32:CE:F1:FC:38:7C:8A:6D:B1:D4:28:91:DB:EE:62:A6:F2:F9 DirName:/C=US/ST=California/L=San-Francisco/O=Nextdoor.com, Inc./OU=Kingpin Integration Tests serial:D3:93:DC:02:DC:F1:18:9C - X509v3 Basic Constraints: + X509v3 Basic Constraints: CA:TRUE Signature Algorithm: sha1WithRSAEncryption 42:6f:d2:ad:11:9d:ab:a3:11:6a:63:2c:ed:2c:88:93:fd:36: diff --git a/kingpin/actors/aws/base.py b/kingpin/actors/aws/base.py index d9f15e91..015c9f1a 100644 --- a/kingpin/actors/aws/base.py +++ b/kingpin/actors/aws/base.py @@ -23,7 +23,8 @@ **Required Environment Variables** -_Note, these can be skipped only if you have a .aws/credentials file in place._ +.. note:: + These can be skipped only if you have a ``.aws/credentials`` file in place. :AWS_ACCESS_KEY_ID: Your AWS access key @@ -62,14 +63,6 @@ NAMED_API_CALL_QUEUES = {} -class ELBNotFound(exceptions.RecoverableActorFailure): - """Raised when an ELB is not found""" - - -class InvalidMetaData(exceptions.UnrecoverableActorFailure): - """Raised when fetching AWS metadata.""" - - class InvalidPolicy(exceptions.RecoverableActorFailure): """Raised when Amazon indicates that policy JSON is invalid.""" @@ -127,7 +120,7 @@ def __init__(self, *args, **kwargs): self.ecs_conn = boto3.client( service_name="ecs", config=boto_config, **boto3_client_kwargs ) - self.cf3_conn = boto3.client( + self.cfn_conn = boto3.client( service_name="cloudformation", config=boto_config, **boto3_client_kwargs ) self.sqs_conn = boto3.client( diff --git a/kingpin/actors/aws/cloudformation.py b/kingpin/actors/aws/cloudformation.py index 6741679a..13032558 100644 --- a/kingpin/actors/aws/cloudformation.py +++ b/kingpin/actors/aws/cloudformation.py @@ -234,8 +234,11 @@ def _discover_default_params(self, template_body): def _strip_hash_dict(self, template: dict) -> dict: """Strips the hash from the template. - Note: This will also strip the "Outputs" section if no other output exists. This might cause - issues when diffiing a template that contains an outputs section with no outputs. + .. note:: + + This will also strip the "Outputs" section if no other output + exists. This might cause issues when diffiing a template that + contains an outputs section with no outputs. """ # Bail if the user has disabled this feature. @@ -344,14 +347,14 @@ def _validate_template(self, body=None, url=None): cfg = {"TemplateURL": url} self.log.info("Validating template (%s) with AWS..." % url) try: - yield self.api_call(self.cf3_conn.validate_template, **cfg) + yield self.api_call(self.cfn_conn.validate_template, **cfg) except ClientError as e: raise InvalidTemplate(e) elif body is not None: cfg = {"TemplateBody": body} self.log.info("Validating template with AWS...") try: - yield self.api_call(self.cf3_conn.validate_template, **cfg) + yield self.api_call(self.cfn_conn.validate_template, **cfg) except ClientError as e: raise InvalidTemplate(e) @@ -400,7 +403,7 @@ def _get_stack(self, stack): """ try: stacks = yield self.api_call_with_queueing( - self.cf3_conn.describe_stacks, + self.cfn_conn.describe_stacks, queue_name="describe_stacks", StackName=stack, ) @@ -421,7 +424,7 @@ def _get_stack_template(self, stack): """ try: ret = yield self.api_call( - self.cf3_conn.get_template, StackName=stack, TemplateStage="Original" + self.cfn_conn.get_template, StackName=stack, TemplateStage="Original" ) except ClientError as e: raise CloudFormationError(e) @@ -494,7 +497,7 @@ def _get_stack_events(self, stack): """ try: raw = yield self.api_call( - self.cf3_conn.describe_stack_events, StackName=stack + self.cfn_conn.describe_stack_events, StackName=stack ) except ClientError: raise gen.Return([]) @@ -527,7 +530,7 @@ def _delete_stack(self, stack): self.log.info("Deleting stack") try: - ret = yield self.api_call(self.cf3_conn.delete_stack, StackName=stack) + ret = yield self.api_call(self.cfn_conn.delete_stack, StackName=stack) except ClientError as e: raise CloudFormationError(str(e)) @@ -567,7 +570,7 @@ def _create_stack(self, stack): try: stack = yield self.api_call( - self.cf3_conn.create_stack, + self.cfn_conn.create_stack, StackName=stack, Parameters=self._parameters, OnFailure=self.option("on_failure"), @@ -1057,7 +1060,7 @@ def _ensure_template(self, stack): # cannot be deleted once its been applied. if self._dry: yield self.api_call( - self.cf3_conn.delete_change_set, ChangeSetName=change_set_req["Id"] + self.cfn_conn.delete_change_set, ChangeSetName=change_set_req["Id"] ) self.log.info("Done updating template") @@ -1169,7 +1172,7 @@ def _create_change_set(self, stack, uuid=uuid.uuid4().hex): self.log.info("Generating a stack Change Set...") try: change_set_req = yield self.api_call( - self.cf3_conn.create_change_set, **change_opts + self.cfn_conn.create_change_set, **change_opts ) except ClientError as e: raise CloudFormationError(e) @@ -1201,7 +1204,7 @@ def _wait_until_change_set_ready( while True: try: change = yield self.api_call( - self.cf3_conn.describe_change_set, ChangeSetName=change_set_name + self.cfn_conn.describe_change_set, ChangeSetName=change_set_name ) except ClientError as e: # If we hit an intermittent error, lets just loop around and @@ -1281,7 +1284,7 @@ def _execute_change_set(self, change_set_name): self.log.info("Executing change set %s" % change_set_name) try: yield self.api_call( - self.cf3_conn.execute_change_set, ChangeSetName=change_set_name + self.cfn_conn.execute_change_set, ChangeSetName=change_set_name ) except ClientError as e: raise StackFailed(e) @@ -1326,7 +1329,7 @@ def _update_termination_protection(self, stack, new): try: yield self.api_call( - self.cf3_conn.update_termination_protection, + self.cfn_conn.update_termination_protection, StackName=stack["StackName"], EnableTerminationProtection=new, ) diff --git a/kingpin/actors/aws/iam.py b/kingpin/actors/aws/iam.py index 65b9c762..758877a0 100644 --- a/kingpin/actors/aws/iam.py +++ b/kingpin/actors/aws/iam.py @@ -181,10 +181,11 @@ def _get_entity_policies(self, name): """ policies = {} - # Get the list of inline policies attached to an entity. Note, not - # all entities have a concept of inline policies. If - # self.list_entity_policies is None, it returns a TypeError. We'll - # catch that and silently move on. + # Get the list of inline policies attached to an entity. + # + # Note, not all entities have a concept of inline policies. If + # self.list_entity_policies is None, it returns a TypeError. We'll catch + # that and silently move on. policy_names = [] try: self.log.debug("Searching for any inline policies for %s" % name) diff --git a/kingpin/actors/aws/test/integration_cloudformation.py b/kingpin/actors/aws/test/integration_cloudformation.py index 6f4526d3..4e64b833 100644 --- a/kingpin/actors/aws/test/integration_cloudformation.py +++ b/kingpin/actors/aws/test/integration_cloudformation.py @@ -32,11 +32,11 @@ class IntegrationCreate(testing.AsyncTestCase): impact none of your AWS resources. The stack creates a simple S3 bucket, so your credentials must have access to create that buckets. - Note, these tests must be run in-order. The order is defined by - their definition order in this file. Nose follows this order according - to its documentation: + .. note:: - http://nose.readthedocs.org/en/latest/writing_tests.html + These tests must be run in-order. The order is defined by their definition + order in this file. Nose follows this order according to its + documentation: http://nose.readthedocs.org/en/latest/writing_tests.html """ integration = True @@ -112,11 +112,11 @@ class IntegrationStack(testing.AsyncTestCase): impact none of your AWS resources. The stack creates a simple S3 bucket, so your credentials must have access to create that buckets. - Note, these tests must be run in-order. The order is defined by - their definition order in this file. Nose follows this order according - to its documentation: + .. note:: - http://nose.readthedocs.org/en/latest/writing_tests.html + These tests must be run in-order. The order is defined by their definition + order in this file. Nose follows this order according to its + documentation: http://nose.readthedocs.org/en/latest/writing_tests.html """ integration = True diff --git a/kingpin/actors/aws/test/integration_s3.py b/kingpin/actors/aws/test/integration_s3.py index 3b4b5185..e627b2e8 100644 --- a/kingpin/actors/aws/test/integration_s3.py +++ b/kingpin/actors/aws/test/integration_s3.py @@ -31,11 +31,11 @@ class IntegrationS3(testing.AsyncTestCase): an IAM user/role and also an AWS_SESSION_TOKEN if using temporary access credentials, with permissions to read S3 bucket information. - Note, these tests must be run in-order. The order is defined by - their definition order in this file. Nose follows this order according - to its documentation: + .. note:: - http://nose.readthedocs.org/en/latest/writing_tests.html + These tests must be run in-order. The order is defined by their definition + order in this file. Nose follows this order according to its + documentation: http://nose.readthedocs.org/en/latest/writing_tests.html """ integration = True diff --git a/kingpin/actors/aws/test/test_cloudformation.py b/kingpin/actors/aws/test/test_cloudformation.py index 289145c6..8b1cae0d 100644 --- a/kingpin/actors/aws/test/test_cloudformation.py +++ b/kingpin/actors/aws/test/test_cloudformation.py @@ -60,7 +60,7 @@ def setUp(self): self.actor = cloudformation.CloudFormationBaseActor( "unittest", {"region": "us-east-1"} ) - self.actor.cf3_conn = mock.MagicMock(name="cf3_conn") + self.actor.cfn_conn = mock.MagicMock(name="cfn_conn") # Need to recreate the api call queues between tests # because nose creates a new ioloop per test run. @@ -137,14 +137,14 @@ def test_get_s3_client(self): @testing.gen_test def test_validate_template_body(self): yield self.actor._validate_template(body="test body") - self.actor.cf3_conn.validate_template.assert_called_with( + self.actor.cfn_conn.validate_template.assert_called_with( TemplateBody="test body" ) @testing.gen_test def test_validate_template_url(self): yield self.actor._validate_template(url="http://foobar.json") - self.actor.cf3_conn.validate_template.assert_called_with( + self.actor.cfn_conn.validate_template.assert_called_with( TemplateURL="http://foobar.json" ) @@ -162,7 +162,7 @@ def test_validate_template_raises_boto_error(self): }, } - self.actor.cf3_conn.validate_template.side_effect = ClientError( + self.actor.cfn_conn.validate_template.side_effect = ClientError( fake_exc, "FakeOperation" ) with self.assertRaises(cloudformation.InvalidTemplate): @@ -191,7 +191,7 @@ def test_create_parameters(self): @testing.gen_test def test_get_stack(self): - self.actor.cf3_conn.describe_stacks.return_value = { + self.actor.cfn_conn.describe_stacks.return_value = { "Stacks": [create_fake_stack("s1", "UPDATE_COMPLETE")] } @@ -211,7 +211,7 @@ def test_get_stack_not_found(self): "Type": "Sender", }, } - self.actor.cf3_conn.describe_stacks.side_effect = ClientError( + self.actor.cfn_conn.describe_stacks.side_effect = ClientError( fake_exc, "Failure" ) @@ -231,7 +231,7 @@ def test_get_stack_exc(self): "Type": "Sender", }, } - self.actor.cf3_conn.describe_stacks.side_effect = ClientError( + self.actor.cfn_conn.describe_stacks.side_effect = ClientError( fake_exc, "Failure" ) @@ -247,11 +247,11 @@ def test_get_stack_template(self): }, "TemplateBody": {"Fake": "Stack"}, } - self.actor.cf3_conn.get_template.return_value = fake_stack_template + self.actor.cfn_conn.get_template.return_value = fake_stack_template ret = yield self.actor._get_stack_template("test") - self.actor.cf3_conn.get_template.assert_has_calls( + self.actor.cfn_conn.get_template.assert_has_calls( [mock.call(StackName="test", TemplateStage="Original")] ) self.assertEqual(ret, {"Fake": "Stack"}) @@ -269,7 +269,7 @@ def test_get_stack_template_exc(self): "Type": "Sender", }, } - self.actor.cf3_conn.get_template.side_effect = ClientError(fake_exc, "Failure") + self.actor.cfn_conn.get_template.side_effect = ClientError(fake_exc, "Failure") with self.assertRaises(cloudformation.CloudFormationError): yield self.actor._get_stack_template("test") @@ -340,7 +340,7 @@ def test_get_stack_events(self): "AWS::CloudFormation::Stack s3 (DELETE_COMPLETE): ", "AWS::CloudFormation::Stack test (DELETE_COMPLETE): ", ] - self.actor.cf3_conn.describe_stack_events.return_value = fake_events + self.actor.cfn_conn.describe_stack_events.return_value = fake_events ret = yield self.actor._get_stack_events("test") self.assertEqual(ret, expected) @@ -358,7 +358,7 @@ def test_get_stack_events_exc(self): "Type": "Sender", }, } - self.actor.cf3_conn.describe_stack_events.side_effect = ClientError( + self.actor.cfn_conn.describe_stack_events.side_effect = ClientError( fake_exc, "Failure" ) ret = yield self.actor._get_stack_events("test") @@ -366,7 +366,7 @@ def test_get_stack_events_exc(self): @testing.gen_test def test_delete_stack(self): - self.actor.cf3_conn.delete_stack.return_value = { + self.actor.cfn_conn.delete_stack.return_value = { "ResponseMetadata": {"RequestId": "req-id-1"} } self.actor._wait_until_state = mock.MagicMock(name="_wait_until_state") @@ -375,12 +375,12 @@ def test_delete_stack(self): yield self.actor._delete_stack(stack="stack") - self.assertTrue(self.actor.cf3_conn.delete_stack.called) + self.assertTrue(self.actor.cfn_conn.delete_stack.called) self.assertTrue(self.actor._wait_until_state.called) @testing.gen_test def test_delete_stack_raises_boto_error(self): - self.actor.cf3_conn.delete_stack = mock.MagicMock(name="delete_stack") + self.actor.cfn_conn.delete_stack = mock.MagicMock(name="delete_stack") fake_exc = { "ResponseMetadata": { @@ -394,7 +394,7 @@ def test_delete_stack_raises_boto_error(self): }, } - self.actor.cf3_conn.delete_stack.side_effect = ClientError(fake_exc, "Error") + self.actor.cfn_conn.delete_stack.side_effect = ClientError(fake_exc, "Error") with self.assertRaises(cloudformation.CloudFormationError): yield self.actor._delete_stack(stack="stack") @@ -422,8 +422,8 @@ def test_create_stack_file(self): ) actor._wait_until_state = mock.MagicMock(name="_wait_until_state") actor._wait_until_state.side_effect = [tornado_value(None)] - actor.cf3_conn.create_stack = mock.MagicMock(name="create_stack_mock") - actor.cf3_conn.create_stack.return_value = {"StackId": "arn:123"} + actor.cfn_conn.create_stack = mock.MagicMock(name="create_stack_mock") + actor.cfn_conn.create_stack.return_value = {"StackId": "arn:123"} ret = yield actor._create_stack(stack="test") self.assertEqual(ret, "arn:123") @@ -441,11 +441,11 @@ def test_create_stack_file_with_role(self): ) actor._wait_until_state = mock.MagicMock(name="_wait_until_state") actor._wait_until_state.side_effect = [tornado_value(None)] - actor.cf3_conn.create_stack = mock.MagicMock(name="create_stack_mock") - actor.cf3_conn.create_stack.return_value = {"StackId": "arn:123"} + actor.cfn_conn.create_stack = mock.MagicMock(name="create_stack_mock") + actor.cfn_conn.create_stack.return_value = {"StackId": "arn:123"} ret = yield actor._create_stack(stack="test") self.assertEqual(ret, "arn:123") - actor.cf3_conn.create_stack.assert_called_with( + actor.cfn_conn.create_stack.assert_called_with( TemplateBody=mock.ANY, EnableTerminationProtection=False, Parameters=[], @@ -470,11 +470,11 @@ def test_create_stack_file_with_role_yaml(self): ) actor._wait_until_state = mock.MagicMock(name="_wait_until_state") actor._wait_until_state.side_effect = [tornado_value(None)] - actor.cf3_conn.create_stack = mock.MagicMock(name="create_stack_mock") - actor.cf3_conn.create_stack.return_value = {"StackId": "arn:123"} + actor.cfn_conn.create_stack = mock.MagicMock(name="create_stack_mock") + actor.cfn_conn.create_stack.return_value = {"StackId": "arn:123"} ret = yield actor._create_stack(stack="test") self.assertEqual(ret, "arn:123") - actor.cf3_conn.create_stack.assert_called_with( + actor.cfn_conn.create_stack.assert_called_with( TemplateBody=mock.ANY, EnableTerminationProtection=False, Parameters=[], @@ -500,11 +500,11 @@ def test_create_stack_file_with_termination_protection_true(self): actor._options["enable_termination_protection"] = True actor._wait_until_state = mock.MagicMock(name="_wait_until_state") actor._wait_until_state.side_effect = [tornado_value(None)] - actor.cf3_conn.create_stack = mock.MagicMock(name="create_stack_mock") - actor.cf3_conn.create_stack.return_value = {"StackId": "arn:123"} + actor.cfn_conn.create_stack = mock.MagicMock(name="create_stack_mock") + actor.cfn_conn.create_stack.return_value = {"StackId": "arn:123"} ret = yield actor._create_stack(stack="test") self.assertEqual(ret, "arn:123") - actor.cf3_conn.create_stack.assert_called_with( + actor.cfn_conn.create_stack.assert_called_with( TemplateBody=mock.ANY, EnableTerminationProtection=True, Parameters=[], @@ -536,8 +536,8 @@ def test_create_stack_url(self): ) actor._wait_until_state = mock.MagicMock(name="_wait_until_state") actor._wait_until_state.side_effect = [tornado_value(None)] - actor.cf3_conn.create_stack = mock.MagicMock(name="create_stack_mock") - actor.cf3_conn.create_stack.return_value = {"StackId": "arn:123"} + actor.cfn_conn.create_stack = mock.MagicMock(name="create_stack_mock") + actor.cfn_conn.create_stack.return_value = {"StackId": "arn:123"} ret = yield actor._create_stack(stack="unit-test-cf") self.assertEqual(ret, "arn:123") @@ -551,7 +551,7 @@ def test_create_stack_raises_boto_error(self): "template": "examples/test/aws.cloudformation/cf.integration.json", }, ) - actor.cf3_conn.create_stack = mock.MagicMock(name="create_stack_mock") + actor.cfn_conn.create_stack = mock.MagicMock(name="create_stack_mock") fake_exc = { "ResponseMetadata": { @@ -565,7 +565,7 @@ def test_create_stack_raises_boto_error(self): }, } - actor.cf3_conn.create_stack.side_effect = ClientError(fake_exc, "Failure") + actor.cfn_conn.create_stack.side_effect = ClientError(fake_exc, "Failure") with self.assertRaises(cloudformation.CloudFormationError): yield actor._create_stack(stack="test") @@ -579,8 +579,8 @@ def test_create_stack_wait_until_raises_boto_error(self): "template": "examples/test/aws.cloudformation/cf.integration.json", }, ) - actor.cf3_conn.create_stack = mock.MagicMock(name="create_stack_mock") - actor.cf3_conn.create_stack.return_value = {"StackId": "arn:123"} + actor.cfn_conn.create_stack = mock.MagicMock(name="create_stack_mock") + actor.cfn_conn.create_stack.return_value = {"StackId": "arn:123"} actor._wait_until_state = mock.MagicMock(name="_wait_until_state") actor._wait_until_state.side_effect = cloudformation.StackFailed() @@ -723,7 +723,7 @@ def setUp(self): "parameters": {"key1": "value1"}, } ) - self.actor.cf3_conn = mock.MagicMock(name="cf3_conn") + self.actor.cfn_conn = mock.MagicMock(name="cfn_conn") self.actor.s3_conn = mock.MagicMock(name="s3_conn") def test_diff_params_safely(self): @@ -887,7 +887,7 @@ def test_update_stack_update_termination_protection(self): fake_stack = create_fake_stack("fake", "CREATE_COMPLETE") self.actor._options["enable_termination_protection"] = True - self.actor.cf3_conn.update_termination_protection.return_value = tornado_value( + self.actor.cfn_conn.update_termination_protection.return_value = tornado_value( None ) @@ -895,7 +895,7 @@ def test_update_stack_update_termination_protection(self): self.actor._ensure_template.return_value = tornado_value(None) yield self.actor._update_stack(fake_stack) - self.actor.cf3_conn.update_termination_protection.assert_has_calls( + self.actor.cfn_conn.update_termination_protection.assert_has_calls( [mock.call(StackName="fake", EnableTerminationProtection=True)] ) @@ -916,7 +916,7 @@ def test_update_stack_update_termination_protection_error(self): }, } - self.actor.cf3_conn.update_termination_protection.side_effect = ClientError( + self.actor.cfn_conn.update_termination_protection.side_effect = ClientError( fake_update, "FakeOperation" ) with self.assertRaises(cloudformation.StackFailed): @@ -942,7 +942,7 @@ def test_ensure_template_with_url_works(self): ) self.actor._execute_change_set = mock.MagicMock(name="_execute_change") self.actor._execute_change_set.return_value = tornado_value(None) - self.actor.cf3_conn.delete_change_set.return_value = tornado_value(None) + self.actor.cfn_conn.delete_change_set.return_value = tornado_value(None) # Change the actors parameters from the first time it was run -- this # ensures all the lines on the ensure_template method are called @@ -966,13 +966,13 @@ def test_ensure_template_with_url_works(self): self.actor._wait_until_change_set_ready.assert_has_calls( [mock.call("abcd", "Status", "CREATE_COMPLETE")] ) - self.assertFalse(self.actor.cf3_conn.delete_change_set.called) + self.assertFalse(self.actor.cfn_conn.delete_change_set.called) # Quick second execution with _dry set. In this case, we SHOULD call # the delete changset function. self.actor._dry = True yield self.actor._ensure_template(fake_stack) - self.actor.cf3_conn.delete_change_set.assert_has_calls( + self.actor.cfn_conn.delete_change_set.assert_has_calls( [mock.call(ChangeSetName="abcd")] ) @@ -1006,7 +1006,7 @@ def test_ensure_template_different(self): ) self.actor._execute_change_set = mock.MagicMock(name="_execute_change") self.actor._execute_change_set.return_value = tornado_value(None) - self.actor.cf3_conn.delete_change_set.return_value = tornado_value(None) + self.actor.cfn_conn.delete_change_set.return_value = tornado_value(None) # Change the actors parameters from the first time it was run -- this # ensures all the lines on the ensure_template method are called @@ -1030,13 +1030,13 @@ def test_ensure_template_different(self): self.actor._wait_until_change_set_ready.assert_has_calls( [mock.call("abcd", "Status", "CREATE_COMPLETE")] ) - self.assertFalse(self.actor.cf3_conn.delete_change_set.called) + self.assertFalse(self.actor.cfn_conn.delete_change_set.called) # Quick second execution with _dry set. In this case, we SHOULD call # the delete changset function. self.actor._dry = True yield self.actor._ensure_template(fake_stack) - self.actor.cf3_conn.delete_change_set.assert_has_calls( + self.actor.cfn_conn.delete_change_set.assert_has_calls( [mock.call(ChangeSetName="abcd")] ) @@ -1114,11 +1114,11 @@ def test_strip_hash_str(self): @testing.gen_test def test_create_change_set_body(self): - self.actor.cf3_conn.create_change_set.return_value = {"Id": "abcd"} + self.actor.cfn_conn.create_change_set.return_value = {"Id": "abcd"} fake_stack = create_fake_stack("fake", "CREATE_COMPLETE") ret = yield self.actor._create_change_set(fake_stack, "uuid") self.assertEqual(ret, {"Id": "abcd"}) - self.actor.cf3_conn.create_change_set.assert_has_calls( + self.actor.cfn_conn.create_change_set.assert_has_calls( [ mock.call( StackName="arn:aws:cloudformation:us-east-1:xxxx:stack/fake/x", @@ -1133,12 +1133,12 @@ def test_create_change_set_body(self): @testing.gen_test def test_create_change_set_body_with_role(self): - self.actor.cf3_conn.create_change_set.return_value = {"Id": "abcd"} + self.actor.cfn_conn.create_change_set.return_value = {"Id": "abcd"} fake_stack = create_fake_stack("fake", "CREATE_COMPLETE") self.actor._options["role_arn"] = "test_role_arn" ret = yield self.actor._create_change_set(fake_stack, "uuid") self.assertEqual(ret, {"Id": "abcd"}) - self.actor.cf3_conn.create_change_set.assert_has_calls( + self.actor.cfn_conn.create_change_set.assert_has_calls( [ mock.call( StackName="arn:aws:cloudformation:us-east-1:xxxx:stack/fake/x", @@ -1154,14 +1154,14 @@ def test_create_change_set_body_with_role(self): @testing.gen_test def test_create_change_set_url(self): - self.actor.cf3_conn.create_change_set.return_value = {"Id": "abcd"} + self.actor.cfn_conn.create_change_set.return_value = {"Id": "abcd"} template_body = json.dumps({}) self.actor._template_body = template_body self.actor._template_url = "https://foobar.s3.us-east-1.amazonaws.com/bin" fake_stack = create_fake_stack("fake", "CREATE_COMPLETE") ret = yield self.actor._create_change_set(fake_stack, "uuid") self.assertEqual(ret, {"Id": "abcd"}) - self.actor.cf3_conn.create_change_set.assert_has_calls( + self.actor.cfn_conn.create_change_set.assert_has_calls( [ mock.call( StackName="arn:aws:cloudformation:us-east-1:xxxx:stack/fake/x", @@ -1176,7 +1176,7 @@ def test_create_change_set_url(self): @testing.gen_test def test_create_change_set_exc(self): - self.actor.cf3_conn.create_change_set.return_value = {"Id": "abcd"} + self.actor.cfn_conn.create_change_set.return_value = {"Id": "abcd"} fake_exc = { "ResponseMetadata": { "HTTPStatusCode": 400, @@ -1188,7 +1188,7 @@ def test_create_change_set_exc(self): "Type": "Sender", }, } - self.actor.cf3_conn.create_change_set.side_effect = ClientError( + self.actor.cfn_conn.create_change_set.side_effect = ClientError( fake_exc, "FakeOperation" ) fake_stack = create_fake_stack("fake", "CREATE_COMPLETE") @@ -1211,7 +1211,7 @@ def test_wait_until_change_set_ready_complete(self): "Type": "Sender", }, } - self.actor.cf3_conn.describe_change_set.side_effect = [ + self.actor.cfn_conn.describe_change_set.side_effect = [ available, update_in_progress, update_in_progress, @@ -1221,7 +1221,7 @@ def test_wait_until_change_set_ready_complete(self): yield self.actor._wait_until_change_set_ready( "test", "Status", "UPDATE_COMPLETE", sleep=0.01 ) - self.actor.cf3_conn.describe_change_set.assert_has_calls( + self.actor.cfn_conn.describe_change_set.assert_has_calls( [ mock.call(ChangeSetName="test"), mock.call(ChangeSetName="test"), @@ -1235,7 +1235,7 @@ def test_wait_until_change_set_ready_failed_status(self): available = {"Status": "AVAILABLE"} update_in_progress = {"Status": "UPDATE_IN_PROGRESS"} update_failed = {"Status": "UPDATE_FAILED", "StatusReason": "Template error"} - self.actor.cf3_conn.describe_change_set.side_effect = [ + self.actor.cfn_conn.describe_change_set.side_effect = [ available, update_in_progress, update_in_progress, @@ -1251,7 +1251,7 @@ def test_wait_until_change_set_ready_failed_status_no_reason(self): available = {"Status": "AVAILABLE"} update_in_progress = {"Status": "UPDATE_IN_PROGRESS"} update_failed = {"Status": "UPDATE_FAILED"} - self.actor.cf3_conn.describe_change_set.side_effect = [ + self.actor.cfn_conn.describe_change_set.side_effect = [ available, update_in_progress, update_in_progress, @@ -1307,7 +1307,7 @@ def test_execute_change_set(self): yield self.actor._execute_change_set(change_set_name="fake_set") - self.actor.cf3_conn.execute_change_set.assert_has_calls( + self.actor.cfn_conn.execute_change_set.assert_has_calls( [mock.call(ChangeSetName="fake_set")] ) @@ -1341,8 +1341,8 @@ def test_execute_change_set_exc(self): }, } - self.actor.cf3_conn.execute_change_set = mock.MagicMock(name="_wait") - self.actor.cf3_conn.execute_change_set.side_effect = ClientError( + self.actor.cfn_conn.execute_change_set = mock.MagicMock(name="_wait") + self.actor.cfn_conn.execute_change_set.side_effect = ClientError( fake_exc, "FakeOperation" ) diff --git a/kingpin/actors/base.py b/kingpin/actors/base.py index c9c93db1..95ba2c5c 100644 --- a/kingpin/actors/base.py +++ b/kingpin/actors/base.py @@ -314,9 +314,11 @@ def timeout(self, f, *args, **kwargs): Used to wrap the self.execute() method in a timeout that will raise an ActorTimedOut exception if an actor takes too long to execute. - *Note, Tornado 4+ does not allow you to actually kill a task on the - IOLoop.* This means that all we are doing here is notifying the caller - (through the raised exception) that a problem has happened. + .. note:: + + Tornado 4+ does not allow you to actually kill a task on the IOLoop. + This means that all we are doing here is notifying the caller + (through the raised exception) that a problem has happened. Fairly simple Actors should actually 'stop executing' when this exception is raised. Complex actors with very unique behaviors though @@ -571,26 +573,15 @@ class EnsurableBaseActor(BaseActor): **Required Methods:** - :`_set_state`: Creates or destroys the resource depending on - the 'state' parameter that was passed in. - - *Note: The 'state' parameter is automatically added to - the options. You do not need to define it.* - :`_get_state`: Gets the current state of the resource. - :`_set_[option]`: A 'setter' for each option name passed in. - :`_get_[option]`: A 'getter' for each option name passed in. + :`_set_state`: Creates or destroys the resource depending on the 'state' parameter that was passed in. Note: The 'state' parameter is automatically added to the options. You do not need to define it. + :`_get_state`: Gets the current state of the resource. + :`_set_[option]`: A 'setter' for each option name passed in. + :`_get_[option]`: A 'getter' for each option name passed in. **Optional Methods:** - :`_precache`: Called before any setters/getters are triggered. Used - to optionally populate a cache of data to make the - getters faster. For example, if you can make one API - call to get all of the data about a resource, then - store that data locally for fast access. - - :`_compare_[option]`: Optionally you can write your own comparison - method if you're not doing a pure string - comparison between the source and destination. + :`_precache`: Called before any setters/getters are triggered. Used to optionally populate a cache of data to make the getters faster. For example, if you can make one API call to get all of the data about a resource, then store that data locally for fast access. + :`_compare_[option]`: Optionally you can write your own comparison method if you're not doing a pure string comparison between the source and destination. **Examples** @@ -736,8 +727,10 @@ def _ensure(self, option): def _execute(self): """A pretty simple execution pipeline for the actor. - Note: An OrderedDict can be used instead of a plain dict when order - actually matters for the option setting. + .. note:: + + An OrderedDict can be used instead of a plain dict when order + actually matters for the option setting. """ yield self._precache() diff --git a/kingpin/actors/group.py b/kingpin/actors/group.py index 5840209a..276d7a29 100644 --- a/kingpin/actors/group.py +++ b/kingpin/actors/group.py @@ -124,9 +124,11 @@ def _build_actions(self): in the 'contexts' list, a new group of actors is created with that information. - Note: Because groups may contain nested group actors, any options - passed into this actors 'init_context' are also passed into the - actors that we're intantiating. + .. note:: + + Because groups may contain nested group actors, any options passed + into this actors 'init_context' are also passed into the actors that + we're intantiating. """ contexts = self.option("contexts") if not contexts: @@ -209,10 +211,12 @@ def _get_exc_type(self, exc_list): def _execute(self): """Executes the actions configured, and returns. - Note: Expects the sub-class to implement self._run_actions() + .. note:: + + Expects the sub-class to implement ``self._run_actions()``. - If an actor execution fails in _run_actions(), then that exception is - raised up the stack. + If an actor execution fails in ``_run_actions()``, then that exception + is raised up the stack. """ self.log.info("Beginning %s actions" % len(self._actions)) yield self._run_actions() diff --git a/kingpin/actors/misc.py b/kingpin/actors/misc.py index ec6ce3bf..edf2a124 100644 --- a/kingpin/actors/misc.py +++ b/kingpin/actors/misc.py @@ -147,8 +147,10 @@ class Macro(base.BaseActor): def __init__(self, *args, **kwargs): """Pre-parse the script file and compile actors. - Note, we override the default init_tokens={} from the base class and - default it to a _copy_ of the os.environ dict. + .. note:: + + We override the default ``init_tokens={}`` from the base class and + default it to a *copy* of the ``os.environ`` dict. """ super(Macro, self).__init__(*args, **kwargs) diff --git a/kingpin/actors/spotinst.py b/kingpin/actors/spotinst.py index 4ff5e792..477e3e68 100644 --- a/kingpin/actors/spotinst.py +++ b/kingpin/actors/spotinst.py @@ -492,9 +492,11 @@ def _parse_group_config(self): YAML!), replaces any tokens that need replacement, and then sanity checks it against our schema. - Note, contextual tokens (which are evaluated at run time, not - compilation time) are not included here. Instead, those will be - evaluated in the self._precache() method. + .. note:: + + Contextual tokens (which are evaluated at run time, not compilation + time) are not included here. Instead, those will be evaluated in the + ``self._precache()`` method. """ config = self.option("config") diff --git a/kingpin/actors/utils.py b/kingpin/actors/utils.py index 3597541a..57fb39fd 100644 --- a/kingpin/actors/utils.py +++ b/kingpin/actors/utils.py @@ -36,7 +36,9 @@ def dry(dry_message): """Coroutine-compatible decorator to dry-run a method. - Note: this must act on a :py:mod:`~kingpin.actors.base.BaseActor` object. + .. note:: + + This must act on a :py:mod:`~kingpin.actors.base.BaseActor` object. Example usage as decorator: @@ -84,13 +86,18 @@ def timer(f): out in debug statements. Used primarily for tracking Actor execute() methods, but can be used elsewhere as well. - Note: this must act on a :py:mod:`~kingpin.actors.base.BaseActor` object. + .. note:: + + This must act on a :py:mod:`~kingpin.actors.base.BaseActor` object. Example usage: - >>> @gen.coroutine - ... @timer() - ... def execute(self): - ... raise gen.Return() + + .. code-block:: python + + @gen.coroutine + @timer() + def execute(self): + raise gen.Return() """ def _wrap_in_timer(self, *args, **kwargs):