Skip to content

Commit 8671f2f

Browse files
author
P4T12ICK
committed
Merge branch 'cichyx96-feat/proper_cloud_provider_abstraction' into develop
2 parents 890de63 + b1443c2 commit 8671f2f

File tree

6 files changed

+157
-217
lines changed

6 files changed

+157
-217
lines changed

attack_range/ARCHITECTURE.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,8 @@ Abstract base class defining the contract for all cloud providers:
105105
- `delete_backend()`: Delete backend storage
106106
- `import_ssh_key()`: Import SSH key (if applicable)
107107
- `delete_ssh_key()`: Delete SSH key (if applicable)
108-
- `update_backend_config()`: Update backend configuration file
108+
- `write_backend_config()`: Create/update backend configuration file
109+
- `get_backend_params()`: Get backend parameters for the current provider
109110

110111
#### AWSProvider (`cloud_providers/aws_provider.py`)
111112
**Responsibility**: AWS-specific operations

attack_range/cloud_providers/aws_provider.py

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from typing import Optional
1313
import os
1414

15-
from .base_provider import BaseCloudProvider
15+
from .base_provider import BaseCloudProvider, BackendParams
1616

1717

1818
class AWSProvider(BaseCloudProvider):
@@ -308,17 +308,17 @@ def delete_ssh_key(self, key_name: str, region: str) -> None:
308308
except Exception as e:
309309
self.logger.warning(f"Failed to delete key pair '{key_name}': {e}")
310310

311-
def update_backend_config(self, backend_params: dict, backend_file_path: str) -> None:
311+
def write_backend_config(self, backend_params: BackendParams, backend_file_path: str) -> None:
312312
"""
313313
Update or create backend.tf file with S3 backend configuration.
314314
315-
:param backend_params: Dictionary containing backend parameters
315+
:param backend_params: Backend parameters
316316
:param backend_file_path: Path to the backend.tf file
317317
"""
318-
bucket_name = backend_params['bucket_name']
319-
region = backend_params['region']
320-
attack_range_id = backend_params.get('attack_range_id', 'unknown')
321-
config_source = backend_params.get('config_source', 'template/config file')
318+
bucket_name = backend_params.aws_bucket_name
319+
region = backend_params.region
320+
attack_range_id = backend_params.attack_range_id or 'unknown'
321+
config_source = backend_params.config_source or 'template/config file'
322322

323323
backend_config = f'''# This file is AUTO-GENERATED based on the template/config file.
324324
# DO NOT EDIT MANUALLY - changes will be overwritten.
@@ -345,3 +345,23 @@ def update_backend_config(self, backend_params: dict, backend_file_path: str) ->
345345
f.write(backend_config)
346346

347347
self.logger.info(f"Backend configuration written to {backend_file_path} (generated from {config_source})")
348+
349+
def get_backend_params(self, attack_range_id: str, config_source: str = "template/config file") -> BackendParams:
350+
"""
351+
Get backend parameters for AWS (S3 bucket).
352+
353+
:param attack_range_id: The attack range ID for naming
354+
:param config_source: Source config file name for backend.tf comments
355+
:return: Backend parameters
356+
"""
357+
backend_name = f"terraform-state-{attack_range_id}"
358+
bucket_name = self.sanitize_name(backend_name)
359+
region = self.get_region(required=True)
360+
361+
return BackendParams(
362+
backend_name=backend_name,
363+
aws_bucket_name=bucket_name,
364+
region=region,
365+
attack_range_id=attack_range_id,
366+
config_source=config_source
367+
)

attack_range/cloud_providers/azure_provider.py

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from typing import Optional
1111
import os
1212

13-
from .base_provider import BaseCloudProvider
13+
from .base_provider import BaseCloudProvider, BackendParams
1414

1515
# Azure imports
1616
try:
@@ -343,19 +343,19 @@ def delete_ssh_key(self, key_name: str, region: str) -> None:
343343
"""
344344
self.logger.info("Azure: No cloud service keys to delete")
345345

346-
def update_backend_config(self, backend_params: dict, backend_file_path: str) -> None:
346+
def write_backend_config(self, backend_params: BackendParams, backend_file_path: str) -> None:
347347
"""
348348
Update or create backend.tf file with Azure backend configuration.
349349
350-
:param backend_params: Dictionary containing backend parameters
350+
:param backend_params: Backend parameters
351351
:param backend_file_path: Path to the backend.tf file
352352
"""
353-
storage_account_name = backend_params['storage_account_name']
354-
container_name = backend_params['container_name']
355-
resource_group_name = backend_params['resource_group_name']
356-
location = backend_params['location']
357-
attack_range_id = backend_params.get('attack_range_id', 'unknown')
358-
config_source = backend_params.get('config_source', 'template/config file')
353+
storage_account_name = backend_params.azure_storage_account_name
354+
container_name = backend_params.azure_container_name
355+
resource_group_name = backend_params.azure_resource_group_name
356+
location = backend_params.azure_location or backend_params.region
357+
attack_range_id = backend_params.attack_range_id or 'unknown'
358+
config_source = backend_params.config_source or 'template/config file'
359359

360360
backend_config = f'''# This file is AUTO-GENERATED based on the template/config file.
361361
# DO NOT EDIT MANUALLY - changes will be overwritten.
@@ -383,3 +383,28 @@ def update_backend_config(self, backend_params: dict, backend_file_path: str) ->
383383
f.write(backend_config)
384384

385385
self.logger.info(f"Backend configuration written to {backend_file_path} (generated from {config_source})")
386+
387+
def get_backend_params(self, attack_range_id: str, config_source: str = "template/config file") -> BackendParams:
388+
"""
389+
Get backend parameters for Azure (Storage Account + Container).
390+
391+
:param attack_range_id: The attack range ID for naming
392+
:param config_source: Source config file name for backend.tf comments
393+
:return: Backend parameters
394+
"""
395+
backend_name = f"terraformstate{attack_range_id.replace('-', '')}"
396+
storage_account_name = self.sanitize_name(backend_name)
397+
container_name = "tfstate"
398+
resource_group_name = f"rg-terraform-state-{attack_range_id}"
399+
location = self.get_region(required=True)
400+
401+
return BackendParams(
402+
backend_name=backend_name,
403+
azure_storage_account_name=storage_account_name,
404+
azure_container_name=container_name,
405+
azure_resource_group_name=resource_group_name,
406+
azure_location=location,
407+
region=location,
408+
attack_range_id=attack_range_id,
409+
config_source=config_source
410+
)

attack_range/cloud_providers/base_provider.py

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,28 @@
55
"""
66

77
from abc import ABC, abstractmethod
8+
from dataclasses import dataclass
89
from typing import Optional
910
import logging
1011

1112

13+
@dataclass
14+
class BackendParams:
15+
"""Shared backend parameters for all cloud providers."""
16+
17+
backend_name: str
18+
region: str
19+
attack_range_id: str
20+
config_source: str = "template/config file"
21+
aws_bucket_name: Optional[str] = None
22+
gcp_bucket_name: Optional[str] = None
23+
gcp_project_id: Optional[str] = None
24+
azure_storage_account_name: Optional[str] = None
25+
azure_container_name: Optional[str] = None
26+
azure_resource_group_name: Optional[str] = None
27+
azure_location: Optional[str] = None
28+
29+
1230
class BaseCloudProvider(ABC):
1331
"""Abstract base class for cloud providers."""
1432

@@ -94,11 +112,27 @@ def delete_ssh_key(self, key_name: str, region: str) -> None:
94112
pass
95113

96114
@abstractmethod
97-
def update_backend_config(self, backend_params: dict, backend_file_path: str) -> None:
115+
def write_backend_config(self, backend_params: BackendParams, backend_file_path: str) -> None:
98116
"""
99-
Update the backend configuration file.
117+
Save on disk the backend configuration file.
100118
101-
:param backend_params: Dictionary containing backend parameters
119+
:param backend_params: Backend parameters for the current provider
102120
:param backend_file_path: Path to the backend.tf file
103121
"""
104122
pass
123+
124+
@abstractmethod
125+
def get_backend_params(self, attack_range_id: str, config_source: str = "template/config file") -> BackendParams:
126+
"""
127+
Get backend parameters for the current provider.
128+
129+
Returns backend parameters with all information needed for backend setup:
130+
- backend_name: Sanitized name for the storage resource
131+
- region: Provider-specific region/location
132+
- All provider-specific parameters needed for write_backend_config()
133+
134+
:param attack_range_id: The attack range ID for naming
135+
:param config_source: Source config file name for backend.tf comments
136+
:return: Backend parameters
137+
"""
138+
pass

attack_range/cloud_providers/gcp_provider.py

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from typing import Optional
1010
import os
1111

12-
from .base_provider import BaseCloudProvider
12+
from .base_provider import BaseCloudProvider, BackendParams
1313

1414
# GCP imports
1515
try:
@@ -231,17 +231,17 @@ def delete_ssh_key(self, key_name: str, region: str) -> None:
231231
"""
232232
self.logger.info("GCP: No cloud service keys to delete")
233233

234-
def update_backend_config(self, backend_params: dict, backend_file_path: str) -> None:
234+
def write_backend_config(self, backend_params: BackendParams, backend_file_path: str) -> None:
235235
"""
236236
Update or create backend.tf file with GCP backend configuration.
237237
238-
:param backend_params: Dictionary containing backend parameters
238+
:param backend_params: Backend parameters
239239
:param backend_file_path: Path to the backend.tf file
240240
"""
241-
bucket_name = backend_params['bucket_name']
242-
project_id = backend_params['project_id']
243-
attack_range_id = backend_params.get('attack_range_id', 'unknown')
244-
config_source = backend_params.get('config_source', 'template/config file')
241+
bucket_name = backend_params.gcp_bucket_name
242+
project_id = backend_params.gcp_project_id
243+
attack_range_id = backend_params.attack_range_id or 'unknown'
244+
config_source = backend_params.config_source or 'template/config file'
245245

246246
backend_config = f'''# This file is AUTO-GENERATED based on the template/config file.
247247
# DO NOT EDIT MANUALLY - changes will be overwritten.
@@ -265,3 +265,25 @@ def update_backend_config(self, backend_params: dict, backend_file_path: str) ->
265265
f.write(backend_config)
266266

267267
self.logger.info(f"Backend configuration written to {backend_file_path} (generated from {config_source})")
268+
269+
def get_backend_params(self, attack_range_id: str, config_source: str = "template/config file") -> BackendParams:
270+
"""
271+
Get backend parameters for GCP (GCS bucket).
272+
273+
:param attack_range_id: The attack range ID for naming
274+
:param config_source: Source config file name for backend.tf comments
275+
:return: Backend parameters
276+
"""
277+
backend_name = f"terraform-state-{attack_range_id}"
278+
bucket_name = self.sanitize_name(backend_name)
279+
project_id = self.get_project_id(required=True)
280+
region = self.get_region(required=True)
281+
282+
return BackendParams(
283+
backend_name=backend_name,
284+
gcp_bucket_name=bucket_name,
285+
gcp_project_id=project_id,
286+
region=region,
287+
attack_range_id=attack_range_id,
288+
config_source=config_source
289+
)

0 commit comments

Comments
 (0)