0
# Plugin System
1
2
Event-driven plugin architecture for extending AWS CLI functionality with custom handlers, commands, and integrations. The plugin system enables seamless extension of AWS CLI capabilities without modifying core code.
3
4
## Capabilities
5
6
### Plugin Loading
7
8
Core function for loading and initializing plugins with the AWS CLI.
9
10
```python { .api }
11
def load_plugins(plugin_mapping, event_hooks=None, include_builtins=True):
12
"""
13
Load and initialize plugins for AWS CLI.
14
15
Parameters:
16
plugin_mapping: dict, mapping of plugin names to module paths
17
event_hooks: botocore event emitter, optional event system
18
include_builtins: bool, whether to load built-in plugins (default True)
19
"""
20
```
21
22
### Built-in Plugins
23
24
Pre-defined plugins that are loaded by default.
25
26
```python { .api }
27
BUILTIN_PLUGINS: dict # {'__builtin__': 'awscli.handlers'}
28
```
29
30
**Usage Example:**
31
```python
32
from awscli.plugin import load_plugins
33
34
# Load custom plugins
35
custom_plugins = {
36
'my-plugin': 'mypackage.aws_plugin',
37
'dev-tools': 'devtools.aws_extensions'
38
}
39
40
load_plugins(custom_plugins, include_builtins=True)
41
```
42
43
## Plugin Development
44
45
### Basic Plugin Structure
46
47
```python
48
# mypackage/aws_plugin.py
49
def awscli_initialize(cli):
50
"""
51
Plugin initialization function called by AWS CLI.
52
53
Parameters:
54
cli: AWS CLI instance for registration
55
"""
56
# Register event handlers
57
cli.register('before-call', modify_request)
58
cli.register('after-call', process_response)
59
60
# Register custom commands
61
cli.register('building-command-table.main', add_commands)
62
63
def modify_request(event_name=None, **kwargs):
64
"""Handle before-call events."""
65
print(f"Intercepting request: {event_name}")
66
67
def process_response(parsed, **kwargs):
68
"""Handle after-call events."""
69
print(f"Processing response: {parsed}")
70
71
def add_commands(command_table, session, **kwargs):
72
"""Add custom commands to command table."""
73
from mypackage.commands import CustomCommand
74
command_table['custom'] = CustomCommand(session)
75
```
76
77
### Event Registration Patterns
78
79
```python
80
def register_handlers(cli):
81
"""Register various event handlers."""
82
83
# Command table modification
84
cli.register('building-command-table.main', add_main_commands)
85
cli.register('building-command-table.s3', add_s3_commands)
86
87
# Argument table modification
88
cli.register('building-argument-table.s3.ls', add_s3_ls_args)
89
90
# Request/response processing
91
cli.register('before-call', modify_all_requests)
92
cli.register('before-call.s3.ListObjects', modify_s3_list_request)
93
cli.register('after-call', process_all_responses)
94
95
# Output processing
96
cli.register('doc-output', modify_documentation)
97
```
98
99
## Advanced Plugin Features
100
101
### Command Extension
102
103
```python
104
def add_s3_commands(command_table, session, **kwargs):
105
"""Add custom S3 commands."""
106
from mypackage.s3_commands import S3SyncAdvanced
107
command_table['sync-advanced'] = S3SyncAdvanced(session)
108
109
def add_s3_ls_args(argument_table, **kwargs):
110
"""Add arguments to s3 ls command."""
111
from awscli.arguments import CustomArgument
112
113
argument_table['include-metadata'] = CustomArgument(
114
'include-metadata',
115
help_text='Include object metadata in output',
116
action='store_true'
117
)
118
```
119
120
### Request Modification
121
122
```python
123
def modify_requests(event_name, endpoint, request_dict, **kwargs):
124
"""Modify AWS service requests."""
125
126
# Add custom headers
127
if 'headers' not in request_dict:
128
request_dict['headers'] = {}
129
request_dict['headers']['X-Custom-Plugin'] = 'MyPlugin-1.0'
130
131
# Modify parameters
132
if 'params' in request_dict:
133
# Add default parameters
134
request_dict['params'].setdefault('MaxItems', 100)
135
```
136
137
### Response Processing
138
139
```python
140
def process_responses(parsed, **kwargs):
141
"""Process AWS service responses."""
142
143
# Add custom fields
144
if isinstance(parsed, dict):
145
parsed['_plugin_processed'] = True
146
parsed['_processing_time'] = time.time()
147
148
# Transform data
149
if 'Instances' in parsed:
150
for instance in parsed['Instances']:
151
# Add computed fields
152
instance['_display_name'] = f"{instance.get('InstanceId', 'unknown')}"
153
```
154
155
## Plugin Configuration
156
157
### Plugin Discovery
158
159
Plugins can be configured through AWS CLI configuration:
160
161
```ini
162
# ~/.aws/config
163
[plugins]
164
my-plugin = mypackage.aws_plugin
165
dev-tools = devtools.aws_extensions
166
```
167
168
### Environment-Based Loading
169
170
```python
171
import os
172
from awscli.plugin import load_plugins
173
174
# Load plugins from environment
175
plugin_paths = os.environ.get('AWS_CLI_PLUGINS', '').split(',')
176
plugin_mapping = {}
177
178
for plugin_path in plugin_paths:
179
if plugin_path.strip():
180
name = plugin_path.split('.')[-1]
181
plugin_mapping[name] = plugin_path.strip()
182
183
load_plugins(plugin_mapping)
184
```
185
186
### Conditional Plugin Loading
187
188
```python
189
def conditional_load(cli):
190
"""Load plugins based on conditions."""
191
192
# Only load in development
193
if os.environ.get('AWS_CLI_ENV') == 'development':
194
cli.register('building-command-table.main', add_dev_commands)
195
196
# Load based on AWS profile
197
current_profile = cli.session.get_config_variable('profile')
198
if current_profile == 'production':
199
cli.register('before-call', add_production_safeguards)
200
```
201
202
## Plugin Distribution
203
204
### Package Structure
205
206
```
207
mypackage/
208
├── __init__.py
209
├── aws_plugin.py # Main plugin module
210
├── commands/
211
│ ├── __init__.py
212
│ └── custom_commands.py # Custom command implementations
213
├── handlers/
214
│ ├── __init__.py
215
│ └── event_handlers.py # Event handler implementations
216
└── setup.py # Package configuration
217
```
218
219
### Setup Configuration
220
221
```python
222
# setup.py
223
from setuptools import setup, find_packages
224
225
setup(
226
name='aws-cli-my-plugin',
227
version='1.0.0',
228
packages=find_packages(),
229
install_requires=[
230
'awscli>=1.40.0',
231
'botocore>=1.30.0'
232
],
233
entry_points={
234
'console_scripts': [
235
'aws-my-plugin=mypackage.cli:main'
236
]
237
}
238
)
239
```
240
241
### Plugin Testing
242
243
```python
244
import unittest
245
from awscli.testutils import BaseCLIDriverTest
246
247
class TestMyPlugin(BaseCLIDriverTest):
248
def setUp(self):
249
super().setUp()
250
# Load plugin for testing
251
from mypackage.aws_plugin import awscli_initialize
252
awscli_initialize(self.driver)
253
254
def test_custom_command(self):
255
"""Test custom command functionality."""
256
result = self.run_cmd(['custom', '--help'])
257
self.assertEqual(result.rc, 0)
258
self.assertIn('Custom command', result.stdout)
259
260
def test_event_handler(self):
261
"""Test event handler integration."""
262
result = self.run_cmd(['s3', 'ls'])
263
# Verify plugin modifications
264
self.assertIn('plugin_processed', result.stdout)
265
```