cloud-foundry 0.1.2__tar.gz → 0.1.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. cloud_foundry-0.1.4/.gitignore +10 -0
  2. {cloud_foundry-0.1.2 → cloud_foundry-0.1.4}/PKG-INFO +15 -15
  3. {cloud_foundry-0.1.2 → cloud_foundry-0.1.4}/cloud_foundry/__init__.py +13 -3
  4. cloud_foundry-0.1.4/cloud_foundry/pulumi/__init__.py +0 -0
  5. {cloud_foundry-0.1.2 → cloud_foundry-0.1.4}/cloud_foundry/pulumi/cdn.py +291 -18
  6. {cloud_foundry-0.1.2 → cloud_foundry-0.1.4}/cloud_foundry/pulumi/cdn_api_origin.py +8 -0
  7. cloud_foundry-0.1.4/cloud_foundry/pulumi/connection_store.py +176 -0
  8. {cloud_foundry-0.1.2 → cloud_foundry-0.1.4}/cloud_foundry/pulumi/document_repository.py +28 -4
  9. {cloud_foundry-0.1.2 → cloud_foundry-0.1.4}/cloud_foundry/pulumi/domain.py +8 -0
  10. {cloud_foundry-0.1.2 → cloud_foundry-0.1.4}/cloud_foundry/pulumi/function.py +126 -32
  11. {cloud_foundry-0.1.2 → cloud_foundry-0.1.4}/cloud_foundry/pulumi/python_function.py +12 -9
  12. cloud_foundry-0.1.4/cloud_foundry/pulumi/queue.py +211 -0
  13. {cloud_foundry-0.1.2 → cloud_foundry-0.1.4}/cloud_foundry/pulumi/rest_api.py +27 -20
  14. {cloud_foundry-0.1.2 → cloud_foundry-0.1.4}/cloud_foundry/pulumi/rest_api_logging_role.py +3 -3
  15. cloud_foundry-0.1.4/cloud_foundry/pulumi/topic.py +147 -0
  16. cloud_foundry-0.1.4/cloud_foundry/pulumi/websocket_api.py +463 -0
  17. cloud_foundry-0.1.4/cloud_foundry/pulumi/websocket_function.py +149 -0
  18. {cloud_foundry-0.1.2 → cloud_foundry-0.1.4}/cloud_foundry/python_archive_builder.py +123 -32
  19. cloud_foundry-0.1.4/cloud_foundry/utils/websocket_utils.py +340 -0
  20. {cloud_foundry-0.1.2 → cloud_foundry-0.1.4}/pyproject.toml +12 -3
  21. cloud_foundry-0.1.2/cloud_foundry/.DS_Store +0 -0
  22. {cloud_foundry-0.1.2 → cloud_foundry-0.1.4}/LICENSE +0 -0
  23. {cloud_foundry-0.1.2 → cloud_foundry-0.1.4}/README.md +0 -0
  24. {cloud_foundry-0.1.2 → cloud_foundry-0.1.4}/cloud_foundry/archive_builder.py +0 -0
  25. {cloud_foundry-0.1.2 → cloud_foundry-0.1.4}/cloud_foundry/cloud_foundry.py +0 -0
  26. {cloud_foundry-0.1.2 → cloud_foundry-0.1.4}/cloud_foundry/mail_publisher.py +0 -0
  27. {cloud_foundry-0.1.2 → cloud_foundry-0.1.4}/cloud_foundry/pulumi/api_waf.py +0 -0
  28. {cloud_foundry-0.1.2 → cloud_foundry-0.1.4}/cloud_foundry/pulumi/cdn_site_origin.py +0 -0
  29. {cloud_foundry-0.1.2 → cloud_foundry-0.1.4}/cloud_foundry/pulumi/cloudfront_waf.py +0 -0
  30. {cloud_foundry-0.1.2 → cloud_foundry-0.1.4}/cloud_foundry/pulumi/content_repository.py +0 -0
  31. {cloud_foundry-0.1.2 → cloud_foundry-0.1.4}/cloud_foundry/pulumi/custom_domain.py +0 -0
  32. {cloud_foundry-0.1.2 → cloud_foundry-0.1.4}/cloud_foundry/pulumi/log_group.py +0 -0
  33. {cloud_foundry-0.1.2 → cloud_foundry-0.1.4}/cloud_foundry/pulumi/publisher.py +0 -0
  34. {cloud_foundry-0.1.2 → cloud_foundry-0.1.4}/cloud_foundry/pulumi/site_bucket.py +0 -0
  35. {cloud_foundry-0.1.2 → cloud_foundry-0.1.4}/cloud_foundry/pulumi/ui_publisher.py +0 -0
  36. {cloud_foundry-0.1.2 → cloud_foundry-0.1.4}/cloud_foundry/services/authorization_api/security_lambda.py +0 -0
  37. {cloud_foundry-0.1.2 → cloud_foundry-0.1.4}/cloud_foundry/services/mail_publisher.py +0 -0
  38. {cloud_foundry-0.1.2 → cloud_foundry-0.1.4}/cloud_foundry/utils/aws_openapi_editor.py +0 -0
  39. {cloud_foundry-0.1.2 → cloud_foundry-0.1.4}/cloud_foundry/utils/hash_comparator.py +0 -0
  40. {cloud_foundry-0.1.2 → cloud_foundry-0.1.4}/cloud_foundry/utils/logger.py +0 -0
  41. {cloud_foundry-0.1.2 → cloud_foundry-0.1.4}/cloud_foundry/utils/names.py +0 -0
  42. {cloud_foundry-0.1.2 → cloud_foundry-0.1.4}/cloud_foundry/utils/openapi_editor.py +0 -0
@@ -0,0 +1,10 @@
1
+
2
+ venv/
3
+ __pycache__
4
+ .pytest_cache
5
+ .env
6
+ .coverage
7
+ temp/
8
+ dist/
9
+ volume/
10
+ .coverage
@@ -1,25 +1,26 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cloud-foundry
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
4
  Summary: cloud-foundry is a curated set of cloud components that can be used to assemble cloud-based applications.
5
+ Project-URL: Documentation, https://github.com/DanRepik/cloud-foundry
6
+ Project-URL: Source, https://github.com/DanRepik/cloud-foundry
5
7
  Author-email: Daniel Repik <danrepik@icloud.com>
6
- Requires-Python: >=3.9
7
- Description-Content-Type: text/markdown
8
- Classifier: Programming Language :: Python :: 3.9
9
- Classifier: Operating System :: OS Independent
8
+ License: Apache-2.0
10
9
  License-File: LICENSE
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Programming Language :: Python :: 3.9
12
+ Requires-Python: >=3.9
13
+ Requires-Dist: boto3
11
14
  Requires-Dist: pulumi
12
15
  Requires-Dist: pulumi-aws
13
- Requires-Dist: boto3
14
- Requires-Dist: pytest>=7.0 ; extra == "dev"
15
- Requires-Dist: pytest-cov>=4.0.0 ; extra == "dev"
16
- Requires-Dist: black>=23.0.0 ; extra == "dev"
17
- Requires-Dist: isort>=5.12.0 ; extra == "dev"
18
- Requires-Dist: mypy>=1.0.0 ; extra == "dev"
19
- Requires-Dist: flit>=3.9 ; extra == "dev"
20
- Project-URL: Documentation, https://github.com/DanRepik/cloud-foundry
21
- Project-URL: Source, https://github.com/DanRepik/cloud-foundry
22
16
  Provides-Extra: dev
17
+ Requires-Dist: black>=23.0.0; extra == 'dev'
18
+ Requires-Dist: flit>=3.9; extra == 'dev'
19
+ Requires-Dist: isort>=5.12.0; extra == 'dev'
20
+ Requires-Dist: mypy>=1.0.0; extra == 'dev'
21
+ Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
22
+ Requires-Dist: pytest>=7.0; extra == 'dev'
23
+ Description-Content-Type: text/markdown
23
24
 
24
25
  # Cloud Foundry
25
26
 
@@ -973,4 +974,3 @@ cdn_instance = cdn(
973
974
  #### **Conclusion**
974
975
 
975
976
  The `cdn` function simplifies the deployment of a Content Delivery Network using AWS CloudFront, Route 53, and ACM. It supports static sites, APIs, custom domains, and advanced features like logging and geo-restrictions. This function is ideal for building scalable, secure, and performant web applications.
976
-
@@ -5,13 +5,23 @@ from cloud_foundry.pulumi.function import function # noqa: F401
5
5
  from cloud_foundry.pulumi.python_function import python_function # noqa: F401
6
6
  from cloud_foundry.pulumi.rest_api import RestAPI # noqa: F401
7
7
  from cloud_foundry.pulumi.rest_api import rest_api # noqa: F401
8
+ from cloud_foundry.pulumi.websocket_api import WebSocketAPI # noqa: F401
9
+ from cloud_foundry.pulumi.websocket_function import ( # noqa: F401
10
+ WebSocketFunction,
11
+ )
12
+ from cloud_foundry.pulumi.connection_store import ( # noqa: F401
13
+ ConnectionStore,
14
+ )
8
15
 
9
16
  from cloud_foundry.utils.openapi_editor import OpenAPISpecEditor # noqa: F401
10
17
  from cloud_foundry.pulumi.site_bucket import site_bucket # noqa: F401
11
18
  from cloud_foundry.pulumi.document_repository import document_repository # noqa: F401
12
- from cloud_foundry.pulumi.cdn import cdn # noqa: F401
19
+ from cloud_foundry.pulumi.cdn import cdn, CDN, CDNArgs # noqa: F401
13
20
  from cloud_foundry.pulumi.domain import domain # noqa: F401
14
21
 
15
- from cloud_foundry.utils.names import resource_id # noqa: F401
22
+ from cloud_foundry.pulumi.queue import queue
23
+ from cloud_foundry.pulumi.topic import topic
16
24
 
17
- # from cloud_foundry.mail_publisher import mail_publisher
25
+ from cloud_foundry.utils.names import resource_id
26
+
27
+ version = "0.1.4"
File without changes
@@ -1,3 +1,99 @@
1
+ """CloudFront CDN Component for Cloud Foundry.
2
+
3
+ This module provides a comprehensive CloudFront Content Delivery Network (CDN)
4
+ component with support for multiple origin types, custom domains, SSL/TLS
5
+ certificates, geo-restrictions, and advanced caching configurations.
6
+
7
+ Key Features:
8
+ - Multiple origin types: S3 buckets, API Gateway REST APIs, custom domains
9
+ - Automatic SSL/TLS certificate provisioning via ACM
10
+ - Custom domain configuration with Route53 DNS
11
+ - Geo-restriction support (whitelist/blacklist countries)
12
+ - Origin Shield for improved cache hit ratios
13
+ - Apex domain support
14
+ - CORS and security headers via managed policies
15
+ - Automatic cache behaviors for API origins
16
+
17
+ Origin Types:
18
+ 1. S3 Bucket (SiteOrigin):
19
+ - Static website hosting
20
+ - Origin Access Identity for secure access
21
+ - Example: {"bucket": bucket_resource, "name": "site"}
22
+
23
+ 2. Domain Name (ApiOrigin):
24
+ - Direct domain name for custom origins
25
+ - Path-based routing with cache behaviors
26
+ - Example: {"domain_name": "api.example.com", "path_pattern": "/api/*"}
27
+
28
+ 3. REST API (ApiOrigin):
29
+ - API Gateway REST API integration
30
+ - Automatic domain resolution
31
+ - Example: {"rest_api": rest_api_resource, "path_pattern": "/api/*"}
32
+
33
+ Usage:
34
+ from cloud_foundry.pulumi.cdn import CDN, CDNArgs
35
+
36
+ # Basic CDN with API origin
37
+ cdn = CDN(
38
+ "my-cdn",
39
+ CDNArgs(
40
+ hosted_zone_id="Z1234567890ABC",
41
+ subdomain="api",
42
+ site_domain_name="example.com",
43
+ origins=[
44
+ {
45
+ "name": "api",
46
+ "domain_name": api.domain,
47
+ "path_pattern": "/api/*",
48
+ "origin_shield_region": "us-east-1"
49
+ }
50
+ ]
51
+ )
52
+ )
53
+
54
+ # CDN with S3 bucket and API origin
55
+ cdn = CDN(
56
+ "multi-origin-cdn",
57
+ CDNArgs(
58
+ hosted_zone_id="Z1234567890ABC",
59
+ subdomain="cdn",
60
+ create_apex=True,
61
+ site_domain_name="example.com",
62
+ root_uri="index.html",
63
+ origins=[
64
+ {
65
+ "name": "site",
66
+ "bucket": s3_bucket,
67
+ "is_target_origin": True
68
+ },
69
+ {
70
+ "name": "api",
71
+ "domain_name": "api.example.com",
72
+ "path_pattern": "/api/*"
73
+ }
74
+ ],
75
+ whitelist_countries=["US", "CA", "GB"]
76
+ )
77
+ )
78
+
79
+ Configuration:
80
+ - hosted_zone_id: Route53 hosted zone for DNS records
81
+ - subdomain: Subdomain for CDN distribution (e.g., "www", "cdn")
82
+ - site_domain_name: Base domain name for apex domain support
83
+ - create_apex: Create A record for apex domain (example.com)
84
+ - origins: List of origin configurations (see Origin Types above)
85
+ - error_responses: Custom error response configurations
86
+ - root_uri: Default root object (e.g., "index.html")
87
+ - whitelist_countries: ISO country codes to allow (exclusive with blacklist)
88
+ - blacklist_countries: ISO country codes to block (default: CN, RU, CU, KP, IR, BY)
89
+
90
+ Outputs:
91
+ - distribution: CloudFront Distribution resource
92
+ - domain_name: CDN domain name (custom domain or CloudFront domain)
93
+ - dns_alias: Route53 A record for custom domain
94
+ - apex_alias: Route53 A record for apex domain (if create_apex=True)
95
+ """
96
+
1
97
  import pulumi
2
98
  import pulumi_aws as aws
3
99
  from pulumi import ResourceOptions
@@ -22,12 +118,36 @@ DEFAULT_BLACKLIST_COUNTRIES = [
22
118
 
23
119
 
24
120
  class CDNArgs:
121
+ """Configuration arguments for CDN component.
122
+
123
+ Attributes:
124
+ origins: List of origin configurations. Each origin dict can contain:
125
+ - name (str): Unique identifier for the origin
126
+ - bucket (aws.s3.Bucket): S3 bucket for static site hosting
127
+ - domain_name (str|Output): Custom domain name for API origin
128
+ - rest_api (RestAPI): API Gateway REST API resource
129
+ - path_pattern (str): Path pattern for cache behavior (e.g., "/api/*")
130
+ - origin_path (str): Optional path prefix on origin
131
+ - origin_shield_region (str): AWS region for Origin Shield
132
+ - is_target_origin (bool): Whether this is the default origin
133
+ - api_key_password (str): Optional API key for custom header
134
+ create_apex: Whether to create DNS record for apex domain (default: False)
135
+ hosted_zone_id: Route53 hosted zone ID for DNS records
136
+ subdomain: Subdomain for the CDN (e.g., "www", "cdn", "api")
137
+ site_domain_name: Base domain name for apex domain creation
138
+ error_responses: List of custom error response configurations
139
+ root_uri: Default root object for distribution (e.g., "index.html")
140
+ whitelist_countries: ISO country codes to allow (exclusive with blacklist)
141
+ blacklist_countries: ISO country codes to block (default: CN, RU, CU, KP, IR, BY)
142
+ """
143
+
25
144
  def __init__(
26
145
  self,
27
146
  origins: Optional[List[dict]] = None,
28
147
  create_apex: Optional[bool] = False,
29
148
  hosted_zone_id: Optional[str] = None,
30
149
  subdomain: Optional[str] = None,
150
+ site_domain_name: Optional[str] = None,
31
151
  error_responses: Optional[list] = None,
32
152
  root_uri: Optional[str] = None,
33
153
  whitelist_countries: Optional[List[str]] = None,
@@ -37,6 +157,7 @@ class CDNArgs:
37
157
  self.create_apex = create_apex
38
158
  self.hosted_zone_id = hosted_zone_id
39
159
  self.subdomain = subdomain
160
+ self.site_domain_name = site_domain_name
40
161
  self.error_responses = error_responses
41
162
  self.root_uri = root_uri
42
163
  self.whitelist_countries = whitelist_countries
@@ -44,7 +165,59 @@ class CDNArgs:
44
165
 
45
166
 
46
167
  class CDN(pulumi.ComponentResource):
168
+ """CloudFront CDN Component Resource.
169
+
170
+ Creates a CloudFront distribution with:
171
+ - Multiple origin support (S3, API Gateway, custom domains)
172
+ - SSL/TLS certificate provisioned via ACM
173
+ - Custom domain with Route53 DNS configuration
174
+ - Geo-restriction support
175
+ - Origin Shield for improved performance
176
+ - Managed CORS and security headers
177
+ - Path-based cache behaviors for API origins
178
+
179
+ The component automatically:
180
+ - Provisions and validates ACM certificate
181
+ - Creates Route53 DNS records for custom domain
182
+ - Configures origin-specific cache behaviors
183
+ - Sets up S3 bucket policies for OAI access
184
+ - Applies geo-restrictions
185
+
186
+ Attributes:
187
+ distribution: CloudFront Distribution resource
188
+ domain_name: CDN domain name (custom or CloudFront)
189
+ dns_alias: Route53 A record for subdomain
190
+ apex_alias: Route53 A record for apex domain (if create_apex=True)
191
+ site_origins: List of SiteOrigin resources
192
+ hosted_zone_id: Route53 hosted zone ID
193
+ subdomain: CDN subdomain
194
+
195
+ Example:
196
+ cdn = CDN(
197
+ "my-cdn",
198
+ CDNArgs(
199
+ hosted_zone_id="Z123456",
200
+ subdomain="api",
201
+ origins=[
202
+ {
203
+ "name": "api",
204
+ "domain_name": legis_api.domain,
205
+ "path_pattern": "/api/*",
206
+ "origin_shield_region": "us-east-1"
207
+ }
208
+ ]
209
+ )
210
+ )
211
+ """
212
+
47
213
  def __init__(self, name: str, args: CDNArgs, opts: ResourceOptions = None):
214
+ """Initialize CDN component.
215
+
216
+ Args:
217
+ name: Resource name for the CDN
218
+ args: CDN configuration arguments
219
+ opts: Pulumi resource options
220
+ """
48
221
  super().__init__("cloud_foundry:pulumi:CDN", name, {}, opts)
49
222
 
50
223
  self.hosted_zone_id = args.hosted_zone_id
@@ -61,17 +234,10 @@ class CDN(pulumi.ComponentResource):
61
234
  include_apex=args.create_apex,
62
235
  )
63
236
 
64
- log.info("Creating CloudFront distribution")
65
-
66
- log.info(
67
- "aliases:"
68
- + {
69
- (
70
- [self.domain_name, args.site_domain_name]
71
- if args.create_apex
72
- else [self.domain_name]
73
- )
74
- }
237
+ aliases_list = (
238
+ [self.domain_name, args.site_domain_name]
239
+ if args.create_apex
240
+ else [self.domain_name]
75
241
  )
76
242
 
77
243
  origins, caches, target_origin_id = self.get_origins(name, args.origins)
@@ -86,11 +252,7 @@ class CDN(pulumi.ComponentResource):
86
252
  include_cookies=False,
87
253
  prefix="logs/",
88
254
  ),
89
- aliases=(
90
- [self.domain_name, args.site_domain_name]
91
- if args.create_apex
92
- else [self.domain_name]
93
- ),
255
+ aliases=aliases_list,
94
256
  default_cache_behavior=aws.cloudfront.DistributionDefaultCacheBehaviorArgs(
95
257
  target_origin_id=target_origin_id,
96
258
  viewer_protocol_policy="redirect-to-https",
@@ -184,6 +346,34 @@ class CDN(pulumi.ComponentResource):
184
346
  self.domain_name = self.distribution.domain_name
185
347
 
186
348
  def get_origins(self, name: str, origins: List[dict]):
349
+ """Process and configure CDN origins.
350
+
351
+ Processes the origin configurations and creates appropriate origin resources
352
+ (SiteOrigin for S3, ApiOrigin for APIs/domains). Determines the default
353
+ target origin and configures cache behaviors.
354
+
355
+ Args:
356
+ name: Base name for origin resources
357
+ origins: List of origin configuration dicts with keys:
358
+ - bucket: S3 bucket for static sites
359
+ - domain_name: Custom domain name string
360
+ - rest_api: RestAPI resource
361
+ - name: Origin identifier
362
+ - path_pattern: Cache behavior path pattern
363
+ - origin_path: Path prefix on origin
364
+ - origin_shield_region: Origin Shield region
365
+ - is_target_origin: Mark as default origin
366
+ - api_key_password: API key for custom header
367
+
368
+ Returns:
369
+ Tuple of (cdn_origins, caches, target_origin_id):
370
+ - cdn_origins: List of DistributionOriginArgs
371
+ - caches: List of DistributionOrderedCacheBehaviorArgs
372
+ - target_origin_id: Default origin ID
373
+
374
+ Raises:
375
+ ValueError: If origin configuration is invalid or domain cannot be resolved
376
+ """
187
377
  target_origin_id = None
188
378
  cdn_origins = []
189
379
  caches = []
@@ -204,7 +394,13 @@ class CDN(pulumi.ComponentResource):
204
394
  self.site_origins.append(cdn_origin)
205
395
 
206
396
  elif "domain_name" in origin:
207
- cdn_origin = ApiOrigin(f"{name}-{origin["name"]}", origin)
397
+ cdn_origin = ApiOrigin(
398
+ f"{name}-{origin["name"]}",
399
+ domain_name=origin["domain_name"],
400
+ path_pattern=origin.get("path_pattern"),
401
+ origin_path=origin.get("origin_path"),
402
+ shield_region=origin.get("origin_shield_region"),
403
+ )
208
404
  cdn_origins.append(cdn_origin.distribution_origin)
209
405
  caches.append(cdn_origin.cache_behavior)
210
406
 
@@ -262,6 +458,22 @@ class CDN(pulumi.ComponentResource):
262
458
  def set_up_certificate(
263
459
  self, name, domain_name, alternative_names: Optional[List[str]] = None
264
460
  ):
461
+ """Provision and validate ACM certificate for custom domain.
462
+
463
+ Creates an ACM certificate with DNS validation via Route53. Automatically
464
+ creates validation DNS records and waits for certificate validation.
465
+
466
+ Args:
467
+ name: Resource name prefix
468
+ domain_name: Primary domain name for certificate
469
+ alternative_names: List of Subject Alternative Names (SANs)
470
+
471
+ Returns:
472
+ Tuple of (certificate, validation) resources
473
+
474
+ Raises:
475
+ ValueError: If hosted_zone_id is not configured
476
+ """
265
477
  if not self.hosted_zone_id:
266
478
  raise ValueError(
267
479
  "Hosted zone ID is required for custom domain setup. "
@@ -314,6 +526,21 @@ class CDN(pulumi.ComponentResource):
314
526
  stage_name: str,
315
527
  rest_api_id,
316
528
  ):
529
+ """Configure custom domain for API Gateway REST API.
530
+
531
+ Creates ACM certificate, API Gateway domain name, base path mapping,
532
+ and Route53 DNS record for a custom domain pointing to an API Gateway.
533
+
534
+ Args:
535
+ name: Resource name prefix
536
+ hosted_zone_id: Route53 hosted zone ID for DNS
537
+ domain_name: Custom domain name
538
+ stage_name: API Gateway stage name
539
+ rest_api_id: API Gateway REST API ID
540
+
541
+ Returns:
542
+ The configured domain name string
543
+ """
317
544
  certificate, validation = self.set_up_certificate(name, domain_name)
318
545
 
319
546
  custom_domain = aws.apigateway.DomainName(
@@ -354,6 +581,20 @@ class CDN(pulumi.ComponentResource):
354
581
  return domain_name
355
582
 
356
583
  def find_hosted_zone_id(self, name: str) -> str:
584
+ """Find Route53 hosted zone ID by domain name.
585
+
586
+ Placeholder method for looking up hosted zone ID dynamically.
587
+
588
+ Args:
589
+ name: Domain name to search for
590
+
591
+ Returns:
592
+ Hosted zone ID string
593
+
594
+ Note:
595
+ This method is not yet implemented. Pass hosted_zone_id
596
+ directly in CDNArgs instead.
597
+ """
357
598
  # Implement your logic to find the hosted zone ID
358
599
  pass
359
600
 
@@ -368,7 +609,39 @@ def cdn(
368
609
  root_uri: Optional[str] = None,
369
610
  opts: ResourceOptions = None,
370
611
  ) -> CDN:
371
-
612
+ """Factory function to create a CDN component.
613
+
614
+ Convenience function for creating a CDN without explicitly constructing
615
+ CDNArgs. Wraps the CDN component resource initialization.
616
+
617
+ Args:
618
+ name: Resource name for the CDN
619
+ origins: List of origin configuration dicts
620
+ hosted_zone_id: Route53 hosted zone ID for custom domain
621
+ subdomain: Subdomain for CDN (e.g., "www", "cdn")
622
+ error_responses: Custom error response configurations
623
+ create_apex: Create apex domain A record (default: False)
624
+ root_uri: Default root object (e.g., "index.html")
625
+ opts: Pulumi resource options
626
+
627
+ Returns:
628
+ CDN component resource instance
629
+
630
+ Example:
631
+ my_cdn = cdn(
632
+ "website-cdn",
633
+ origins=[
634
+ {
635
+ "name": "site",
636
+ "bucket": website_bucket,
637
+ "is_target_origin": True
638
+ }
639
+ ],
640
+ hosted_zone_id="Z123456",
641
+ subdomain="www",
642
+ root_uri="index.html"
643
+ )
644
+ """
372
645
  return CDN(
373
646
  name,
374
647
  CDNArgs(
@@ -52,6 +52,7 @@ class ApiOrigin(pulumi.ComponentResource):
52
52
 
53
53
  self.origin_id = f"{name}-api"
54
54
  log.info(f"Creating API origin with ID: {self.origin_id}")
55
+ log.info(f"Domain name type: {type(domain_name)}, value: {domain_name}")
55
56
 
56
57
  # Configure custom headers if API key is provided
57
58
  custom_headers = []
@@ -59,6 +60,13 @@ class ApiOrigin(pulumi.ComponentResource):
59
60
  custom_headers.append({"name": "X-API-Key", "value": api_key_password})
60
61
  log.debug("Custom headers configured for API key.")
61
62
 
63
+ # If domain_name is an Output, we need to handle it properly
64
+ # Pulumi should handle Output automatically, but let's ensure it's properly typed
65
+ if isinstance(domain_name, pulumi.Output):
66
+ log.info(
67
+ "Domain name is a Pulumi Output - will be resolved at deployment time"
68
+ )
69
+
62
70
  # Define the CloudFront distribution origin
63
71
  self.distribution_origin = aws.cloudfront.DistributionOriginArgs(
64
72
  domain_name=domain_name,
@@ -0,0 +1,176 @@
1
+ # connection_store.py
2
+
3
+ import pulumi
4
+ import pulumi_aws as aws
5
+ from cloud_foundry.utils.logger import logger
6
+
7
+ log = logger(__name__)
8
+
9
+
10
+ class ConnectionStore(pulumi.ComponentResource):
11
+ """
12
+ A DynamoDB table for managing WebSocket connections.
13
+
14
+ This component creates a DynamoDB table optimized for storing and
15
+ querying WebSocket connection information including:
16
+ - Connection IDs (primary key)
17
+ - User identification and metadata
18
+ - Connection timestamps
19
+ - Custom routing attributes
20
+ - TTL for automatic cleanup of stale connections
21
+ """
22
+
23
+ table: aws.dynamodb.Table
24
+ table_name: pulumi.Output[str]
25
+ table_arn: pulumi.Output[str]
26
+
27
+ def __init__(
28
+ self,
29
+ name: str,
30
+ *,
31
+ ttl_attribute: str = "ttl",
32
+ ttl_enabled: bool = True,
33
+ read_capacity: int = 5,
34
+ write_capacity: int = 5,
35
+ billing_mode: str = "PAY_PER_REQUEST",
36
+ global_secondary_indexes: list[dict] = None,
37
+ opts=None,
38
+ ):
39
+ """
40
+ Initialize the ConnectionStore component.
41
+
42
+ Args:
43
+ name (str): Name of the connection store
44
+ ttl_attribute (str): Name of the TTL attribute (default: "ttl")
45
+ ttl_enabled (bool): Enable TTL for automatic cleanup
46
+ (default: True)
47
+ read_capacity (int): Provisioned read capacity units
48
+ (only used if billing_mode is PROVISIONED)
49
+ write_capacity (int): Provisioned write capacity units
50
+ (only used if billing_mode is PROVISIONED)
51
+ billing_mode (str): Billing mode - "PAY_PER_REQUEST" or
52
+ "PROVISIONED" (default: "PAY_PER_REQUEST")
53
+ global_secondary_indexes (list[dict]): Additional GSIs for
54
+ querying connections by custom attributes. Each dict should
55
+ have:
56
+ - name (str): Index name
57
+ - hash_key (str): Partition key attribute
58
+ - range_key (str, optional): Sort key attribute
59
+ - projection_type (str): ALL, KEYS_ONLY, or INCLUDE
60
+ - non_key_attributes (list, optional): For INCLUDE projection
61
+ opts (pulumi.ResourceOptions): Pulumi resource options
62
+ """
63
+ super().__init__("cloud_foundry:websocket:ConnectionStore", name, None, opts)
64
+ self.name = name
65
+ self.ttl_attribute = ttl_attribute
66
+ self.ttl_enabled = ttl_enabled
67
+ self.global_secondary_indexes = global_secondary_indexes or []
68
+
69
+ # Create the DynamoDB table
70
+ self.table = self._create_table(billing_mode, read_capacity, write_capacity)
71
+ self.table_name = self.table.name
72
+ self.table_arn = self.table.arn
73
+
74
+ # Register outputs
75
+ self.register_outputs(
76
+ {
77
+ "table_name": self.table_name,
78
+ "table_arn": self.table_arn,
79
+ }
80
+ )
81
+
82
+ def _create_table(self, billing_mode: str, read_capacity: int, write_capacity: int):
83
+ """
84
+ Create the DynamoDB table for connection storage.
85
+
86
+ Args:
87
+ billing_mode: Billing mode for the table
88
+ read_capacity: Provisioned read capacity
89
+ write_capacity: Provisioned write capacity
90
+
91
+ Returns:
92
+ aws.dynamodb.Table: The created table
93
+ """
94
+ log.info("Creating connection store table: %s", self.name)
95
+
96
+ table_name = f"{pulumi.get_project()}-{pulumi.get_stack()}-{self.name}"
97
+
98
+ # Define base attributes
99
+ attributes = [
100
+ aws.dynamodb.TableAttributeArgs(
101
+ name="connectionId",
102
+ type="S",
103
+ ),
104
+ ]
105
+
106
+ # Add attributes for GSIs
107
+ for gsi in self.global_secondary_indexes:
108
+ if gsi.get("hash_key") and not any(
109
+ attr.name == gsi["hash_key"] for attr in attributes
110
+ ):
111
+ attributes.append(
112
+ aws.dynamodb.TableAttributeArgs(
113
+ name=gsi["hash_key"],
114
+ type=gsi.get("hash_key_type", "S"),
115
+ )
116
+ )
117
+ if gsi.get("range_key") and not any(
118
+ attr.name == gsi["range_key"] for attr in attributes
119
+ ):
120
+ attributes.append(
121
+ aws.dynamodb.TableAttributeArgs(
122
+ name=gsi["range_key"],
123
+ type=gsi.get("range_key_type", "S"),
124
+ )
125
+ )
126
+
127
+ # Prepare GSI configurations
128
+ gsi_configs = []
129
+ for gsi in self.global_secondary_indexes:
130
+ gsi_config = aws.dynamodb.TableGlobalSecondaryIndexArgs(
131
+ name=gsi["name"],
132
+ hash_key=gsi["hash_key"],
133
+ projection_type=gsi.get("projection_type", "ALL"),
134
+ )
135
+
136
+ if gsi.get("range_key"):
137
+ gsi_config.range_key = gsi["range_key"]
138
+
139
+ if gsi.get("projection_type") == "INCLUDE":
140
+ gsi_config.non_key_attributes = gsi.get("non_key_attributes", [])
141
+
142
+ if billing_mode == "PROVISIONED":
143
+ gsi_config.read_capacity = gsi.get("read_capacity", 5)
144
+ gsi_config.write_capacity = gsi.get("write_capacity", 5)
145
+
146
+ gsi_configs.append(gsi_config)
147
+
148
+ # Create table configuration
149
+ table_config = {
150
+ "name": table_name,
151
+ "hash_key": "connectionId",
152
+ "attributes": attributes,
153
+ "billing_mode": billing_mode,
154
+ "global_secondary_indexes": gsi_configs if gsi_configs else None,
155
+ }
156
+
157
+ # Add capacity settings for PROVISIONED mode
158
+ if billing_mode == "PROVISIONED":
159
+ table_config["read_capacity"] = read_capacity
160
+ table_config["write_capacity"] = write_capacity
161
+
162
+ # Add TTL configuration if enabled
163
+ if self.ttl_enabled:
164
+ table_config["ttl"] = aws.dynamodb.TableTtlArgs(
165
+ attribute_name=self.ttl_attribute,
166
+ enabled=True,
167
+ )
168
+
169
+ # Create the table
170
+ table = aws.dynamodb.Table(
171
+ f"{self.name}-table",
172
+ **table_config,
173
+ opts=pulumi.ResourceOptions(parent=self),
174
+ )
175
+
176
+ return table