or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

aws-services.mdcli-commands.mdconfiguration.mdindex.mdplugins-extensions.mdutils-testing.md

plugins-extensions.mddocs/

0

# Plugins and Extensions

1

2

LocalStack provides an extensible plugin system that enables custom CLI commands, AWS service providers, runtime extensions, and package management. The plugin architecture allows for modular functionality while maintaining compatibility with LocalStack's core systems.

3

4

## Capabilities

5

6

### CLI Plugin System

7

8

Framework for extending the LocalStack CLI with custom commands and functionality.

9

10

```python { .api }

11

class LocalstackCliPlugin:

12

"""

13

Base class for LocalStack CLI plugins.

14

15

CLI plugins can add custom commands, modify existing commands,

16

and integrate with LocalStack's configuration system.

17

18

Location: localstack.cli.plugin

19

"""

20

21

namespace: str = "localstack.plugins.cli"

22

23

def attach(self, cli) -> None:

24

"""

25

Attach plugin commands and functionality to CLI.

26

27

Called during CLI initialization to register custom commands,

28

modify existing commands, or add global options.

29

30

Args:

31

cli: LocalStack CLI instance for command registration

32

"""

33

34

def load_cli_plugins(cli) -> None:

35

"""

36

Discover and load all CLI plugins from the namespace.

37

38

Automatically called during CLI initialization to load

39

all plugins registered in the 'localstack.plugins.cli' namespace.

40

41

Args:

42

cli: LocalStack CLI instance

43

44

Location: localstack.cli.plugin

45

"""

46

```

47

48

### Service Plugin System

49

50

Plugin framework for implementing custom AWS service providers and extending existing services.

51

52

```python { .api }

53

class ServicePlugin:

54

"""

55

Plugin wrapper for AWS service provider implementations.

56

57

Service plugins enable custom AWS service implementations

58

that integrate with LocalStack's service management system.

59

60

Location: localstack.services.plugins

61

"""

62

63

service: str # AWS service name (e.g., 'myservice')

64

api: str # AWS API identifier

65

66

def create_service(self) -> 'Service':

67

"""

68

Create service instance from this plugin.

69

70

Returns:

71

Configured Service instance with provider implementation

72

"""

73

74

def aws_provider(

75

api: str = None,

76

name: str = "default",

77

should_load: callable = None

78

) -> callable:

79

"""

80

Decorator for registering AWS service provider implementations.

81

82

Use this decorator to mark classes as AWS service providers

83

that implement specific AWS APIs.

84

85

Args:

86

api: AWS service API name (e.g., 's3', 'lambda', 'myservice')

87

name: Provider name for multiple implementations (default: "default")

88

should_load: Optional function returning bool to control loading

89

90

Returns:

91

Decorator function for provider classes

92

93

Location: localstack.services.plugins

94

"""

95

96

class ServiceProvider(Protocol):

97

"""

98

Protocol interface for AWS service provider implementations.

99

100

Service providers implement AWS service operations and integrate

101

with LocalStack's AWS Service Framework (ASF).

102

103

Location: localstack.services.plugins

104

"""

105

106

service: str # AWS service name this provider implements

107

108

# Plugin namespace for AWS service providers

109

PLUGIN_NAMESPACE: str = "localstack.aws.provider"

110

```

111

112

### Extension System

113

114

Runtime extension loading and management for enhanced LocalStack functionality.

115

116

```python { .api }

117

class Extension:

118

"""

119

Base class for LocalStack runtime extensions.

120

121

Extensions can modify LocalStack behavior, add new capabilities,

122

and integrate with the service lifecycle.

123

124

Location: localstack.extensions

125

"""

126

127

name: str # Extension name

128

version: str # Extension version

129

130

def on_extension_load(self) -> None:

131

"""Called when extension is loaded during startup"""

132

133

def on_platform_start(self) -> None:

134

"""Called when LocalStack platform starts"""

135

136

def on_platform_shutdown(self) -> None:

137

"""Called when LocalStack platform shuts down"""

138

139

def load_extensions() -> list['Extension']:

140

"""

141

Discover and load all registered extensions.

142

143

Returns:

144

List of loaded extension instances

145

146

Location: localstack.extensions

147

"""

148

149

def register_extension(extension_class: type) -> None:

150

"""

151

Register extension class for automatic loading.

152

153

Args:

154

extension_class: Extension class to register

155

156

Location: localstack.extensions

157

"""

158

```

159

160

### Package Management System

161

162

Package plugin management for third-party integrations and additional services.

163

164

```python { .api }

165

class PackagePlugin:

166

"""

167

Plugin for managing third-party packages and integrations.

168

169

Package plugins enable installation and management of

170

external tools, services, and dependencies.

171

172

Location: localstack.packages

173

"""

174

175

name: str # Package name

176

version: str # Package version

177

dependencies: list[str] # Package dependencies

178

179

def install(self, target_dir: str) -> bool:

180

"""

181

Install package to target directory.

182

183

Args:

184

target_dir: Installation target directory

185

186

Returns:

187

True if installation successful

188

"""

189

190

def is_installed(self) -> bool:

191

"""

192

Check if package is already installed.

193

194

Returns:

195

True if package is available

196

"""

197

198

def get_version(self) -> str:

199

"""

200

Get installed package version.

201

202

Returns:

203

Version string or None if not installed

204

"""

205

206

def install_package(

207

package_name: str,

208

version: str = None,

209

target: str = None

210

) -> bool:

211

"""

212

Install package using package manager.

213

214

Args:

215

package_name: Name of package to install

216

version: Specific version to install (None for latest)

217

target: Installation target directory

218

219

Returns:

220

True if installation successful

221

222

Location: localstack.packages

223

"""

224

225

def list_available_packages() -> list[dict[str, str]]:

226

"""

227

List packages available for installation.

228

229

Returns:

230

List of package information dictionaries

231

232

Location: localstack.packages

233

"""

234

```

235

236

## Plugin Development

237

238

### Creating CLI Plugins

239

240

Example implementation of a custom CLI plugin:

241

242

```python

243

from localstack.cli.plugin import LocalstackCliPlugin

244

import click

245

246

class MyCliPlugin(LocalstackCliPlugin):

247

"""Custom CLI plugin example"""

248

249

def attach(self, cli):

250

"""Add custom commands to CLI"""

251

252

@cli.group()

253

def mycommands():

254

"""Custom command group"""

255

pass

256

257

@mycommands.command()

258

@click.option('--target', help='Target for operation')

259

def deploy(target):

260

"""Deploy custom resources"""

261

click.echo(f"Deploying to {target}")

262

# Custom deployment logic

263

264

@mycommands.command()

265

def status():

266

"""Check custom resource status"""

267

click.echo("Checking status...")

268

# Custom status logic

269

270

# Register plugin via entry point in setup.py/pyproject.toml:

271

# [project.entry-points."localstack.plugins.cli"]

272

# my-plugin = "mypackage.cli:MyCliPlugin"

273

```

274

275

### Creating Service Providers

276

277

Example implementation of a custom AWS service provider:

278

279

```python

280

from localstack.services.plugins import aws_provider, ServiceProvider

281

from localstack.aws.api import RequestContext

282

from typing import Dict, Any

283

284

@aws_provider(api="myservice")

285

class MyServiceProvider(ServiceProvider):

286

"""Custom AWS service provider"""

287

288

service = "myservice"

289

290

def create_resource(

291

self,

292

context: RequestContext,

293

request: Dict[str, Any]

294

) -> Dict[str, Any]:

295

"""

296

Create a custom resource.

297

298

Args:

299

context: Request context with AWS metadata

300

request: Parsed request parameters

301

302

Returns:

303

AWS-compatible response dictionary

304

"""

305

resource_name = request.get("ResourceName")

306

resource_id = f"resource-{self._generate_id()}"

307

308

# Store resource (implementation specific)

309

self._store_resource(resource_id, {

310

"ResourceId": resource_id,

311

"ResourceName": resource_name,

312

"Status": "ACTIVE"

313

})

314

315

return {

316

"ResourceId": resource_id,

317

"ResourceArn": f"arn:aws:myservice:us-east-1:000000000000:resource/{resource_id}"

318

}

319

320

def list_resources(

321

self,

322

context: RequestContext,

323

request: Dict[str, Any]

324

) -> Dict[str, Any]:

325

"""List all resources"""

326

resources = self._list_stored_resources()

327

328

return {

329

"Resources": [

330

{

331

"ResourceId": r["ResourceId"],

332

"ResourceName": r["ResourceName"],

333

"Status": r["Status"]

334

}

335

for r in resources

336

]

337

}

338

339

def _generate_id(self) -> str:

340

"""Generate unique resource ID"""

341

import uuid

342

return str(uuid.uuid4())[:8]

343

344

def _store_resource(self, resource_id: str, resource: Dict[str, Any]) -> None:

345

"""Store resource (implement with your preferred storage)"""

346

pass

347

348

def _list_stored_resources(self) -> List[Dict[str, Any]]:

349

"""List stored resources"""

350

return []

351

352

# Register via entry point:

353

# [project.entry-points."localstack.aws.provider"]

354

# myservice = "mypackage.services:MyServiceProvider"

355

```

356

357

### Creating Extensions

358

359

Example runtime extension that adds custom functionality:

360

361

```python

362

from localstack.extensions import Extension

363

import logging

364

365

LOG = logging.getLogger(__name__)

366

367

class MyExtension(Extension):

368

"""Custom LocalStack extension"""

369

370

name = "my-extension"

371

version = "1.0.0"

372

373

def on_extension_load(self):

374

"""Initialize extension on load"""

375

LOG.info("Loading my custom extension")

376

self._setup_custom_functionality()

377

378

def on_platform_start(self):

379

"""Execute when LocalStack starts"""

380

LOG.info("LocalStack platform starting - extension active")

381

self._start_background_tasks()

382

383

def on_platform_shutdown(self):

384

"""Cleanup when LocalStack stops"""

385

LOG.info("LocalStack platform shutting down")

386

self._cleanup_resources()

387

388

def _setup_custom_functionality(self):

389

"""Setup extension-specific functionality"""

390

# Add custom middleware, modify configs, etc.

391

pass

392

393

def _start_background_tasks(self):

394

"""Start extension background tasks"""

395

# Start monitoring, periodic tasks, etc.

396

pass

397

398

def _cleanup_resources(self):

399

"""Clean up extension resources"""

400

# Stop tasks, close connections, etc.

401

pass

402

403

# Register extension

404

from localstack.extensions import register_extension

405

register_extension(MyExtension)

406

```

407

408

### Creating Package Plugins

409

410

Example package plugin for managing external dependencies:

411

412

```python

413

from localstack.packages import PackagePlugin

414

import subprocess

415

import os

416

417

class RedisPackage(PackagePlugin):

418

"""Redis server package plugin"""

419

420

name = "redis"

421

version = "7.0.0"

422

dependencies = []

423

424

def install(self, target_dir: str) -> bool:

425

"""Install Redis server"""

426

try:

427

# Download and install Redis

428

redis_url = f"https://download.redis.io/releases/redis-{self.version}.tar.gz"

429

430

# Create target directory

431

os.makedirs(target_dir, exist_ok=True)

432

433

# Download, extract, and build Redis

434

subprocess.run([

435

"curl", "-L", redis_url, "|",

436

"tar", "xz", "-C", target_dir

437

], check=True)

438

439

redis_dir = os.path.join(target_dir, f"redis-{self.version}")

440

subprocess.run(["make"], cwd=redis_dir, check=True)

441

442

return True

443

except Exception as e:

444

print(f"Redis installation failed: {e}")

445

return False

446

447

def is_installed(self) -> bool:

448

"""Check if Redis is installed"""

449

try:

450

subprocess.run(["redis-server", "--version"],

451

check=True, capture_output=True)

452

return True

453

except (subprocess.CalledProcessError, FileNotFoundError):

454

return False

455

456

def get_version(self) -> str:

457

"""Get installed Redis version"""

458

try:

459

result = subprocess.run(

460

["redis-server", "--version"],

461

capture_output=True, text=True, check=True

462

)

463

# Parse version from output

464

return result.stdout.split()[2]

465

except Exception:

466

return None

467

468

# Register via entry point:

469

# [project.entry-points."localstack.packages"]

470

# redis = "mypackage.packages:RedisPackage"

471

```

472

473

## Plugin Discovery and Loading

474

475

### Entry Point Configuration

476

477

Plugins are discovered via Python entry points in `pyproject.toml`:

478

479

```toml

480

[project.entry-points."localstack.plugins.cli"]

481

my-cli-plugin = "mypackage.cli:MyCliPlugin"

482

483

[project.entry-points."localstack.aws.provider"]

484

myservice = "mypackage.services:MyServiceProvider"

485

custom-s3 = "mypackage.s3:CustomS3Provider"

486

487

[project.entry-points."localstack.extensions"]

488

my-extension = "mypackage.extensions:MyExtension"

489

490

[project.entry-points."localstack.packages"]

491

redis = "mypackage.packages:RedisPackage"

492

elasticsearch = "mypackage.packages:ElasticsearchPackage"

493

```

494

495

### Plugin Loading Process

496

497

```python { .api }

498

def discover_plugins(namespace: str) -> list[object]:

499

"""

500

Discover plugins from entry point namespace.

501

502

Args:

503

namespace: Entry point namespace to search

504

505

Returns:

506

List of plugin instances

507

"""

508

509

def load_plugin(plugin_class: type, config: dict = None) -> object:

510

"""

511

Load and initialize plugin instance.

512

513

Args:

514

plugin_class: Plugin class to instantiate

515

config: Optional plugin configuration

516

517

Returns:

518

Initialized plugin instance

519

"""

520

```

521

522

### Plugin Configuration

523

524

Plugins can access LocalStack configuration and define their own settings:

525

526

```python

527

class ConfigurablePlugin(LocalstackCliPlugin):

528

"""Plugin with configuration support"""

529

530

def __init__(self):

531

self.config = self._load_config()

532

533

def _load_config(self) -> dict:

534

"""Load plugin-specific configuration"""

535

from localstack.config import config

536

537

return {

538

"enabled": config.get("MY_PLUGIN_ENABLED", "true").lower() == "true",

539

"api_key": config.get("MY_PLUGIN_API_KEY"),

540

"endpoint": config.get("MY_PLUGIN_ENDPOINT", "https://api.example.com")

541

}

542

543

def attach(self, cli):

544

if not self.config["enabled"]:

545

return # Skip if disabled

546

547

@cli.command()

548

def my_command():

549

"""Custom command with configuration"""

550

api_key = self.config["api_key"]

551

endpoint = self.config["endpoint"]

552

# Use configuration in command logic

553

```

554

555

## Plugin Distribution

556

557

### Package Structure

558

559

Recommended structure for plugin packages:

560

561

```

562

my-localstack-plugin/

563

├── pyproject.toml # Package configuration with entry points

564

├── README.md # Plugin documentation

565

├── my_plugin/

566

│ ├── __init__.py

567

│ ├── cli.py # CLI plugin implementation

568

│ ├── services.py # Service provider implementations

569

│ ├── extensions.py # Runtime extensions

570

│ └── packages.py # Package managers

571

└── tests/

572

├── test_cli.py

573

├── test_services.py

574

└── test_extensions.py

575

```

576

577

### Installation and Usage

578

579

Users install plugins as regular Python packages:

580

581

```bash

582

# Install plugin

583

pip install my-localstack-plugin

584

585

# Plugin commands are automatically available

586

localstack my-custom-command --help

587

588

# Plugin services are automatically loaded

589

localstack start # Custom services included

590

591

# Plugin packages available via LPM

592

localstack lpm install my-custom-package

593

```

594

595

### Plugin Testing

596

597

Test plugins using LocalStack's testing framework:

598

599

```python

600

import pytest

601

from localstack.testing.pytest import localstack

602

603

def test_my_plugin_cli():

604

"""Test custom CLI command"""

605

from click.testing import CliRunner

606

from my_plugin.cli import MyCliPlugin

607

608

runner = CliRunner()

609

# Test CLI plugin functionality

610

611

@pytest.mark.aws

612

def test_my_service_provider(localstack):

613

"""Test custom service provider"""

614

import boto3

615

616

# Create client for custom service

617

client = boto3.client(

618

'myservice',

619

endpoint_url='http://localhost:4566',

620

aws_access_key_id='test',

621

aws_secret_access_key='test'

622

)

623

624

# Test service operations

625

response = client.create_resource(ResourceName='test')

626

assert 'ResourceId' in response

627

```