hecks 2.6.0 → 2.8.0

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 (216) hide show
  1. checksums.yaml +4 -4
  2. data/exe/hecks +13 -0
  3. data/lib/hecks/adapters/driven/google_authentication.rb +1 -1
  4. data/lib/hecks/adapters/driven/lambda/client.rb +1 -1
  5. data/lib/hecks/adapters/driven/memory.rb +1 -1
  6. data/lib/hecks/adapters/driven/postgres.rb +1 -1
  7. data/lib/hecks/adapters/driven/prism.rb +2 -2
  8. data/lib/hecks/adapters/driven/sqlite/projection.rb +1 -1
  9. data/lib/hecks/adapters/driven/tenant_provisioner.rb +31 -10
  10. data/lib/hecks/behaviors/expectations.rb +2 -2
  11. data/lib/hecks/bluebook/assembly/contract.rb +1 -1
  12. data/lib/hecks/bluebook/assembly/contracts.rb +5 -5
  13. data/lib/hecks/bluebook/assembly/specializer.rb +2 -2
  14. data/lib/hecks/bluebook/assembly.rb +1 -1
  15. data/lib/hecks/bluebook/behaviour/chapter.rb +5 -2
  16. data/lib/hecks/bluebook/behaviour/read_model.rb +18 -0
  17. data/lib/hecks/bluebook/capabilities.rb +2 -2
  18. data/lib/hecks/bluebook/chapter.rb +1 -1
  19. data/lib/hecks/bluebook/dsl/aggregate_builder.rb +2 -2
  20. data/lib/hecks/bluebook/dsl/binding_proxy.rb +6 -1
  21. data/lib/hecks/bluebook/dsl/bluebook_builder.rb +2 -2
  22. data/lib/hecks/bluebook/dsl/bootstrap_table.rb +2 -0
  23. data/lib/hecks/bluebook/dsl/command_builder.rb +3 -3
  24. data/lib/hecks/bluebook/dsl/entity_builder.rb +1 -1
  25. data/lib/hecks/bluebook/dsl/generic_dispatch.rb +1 -1
  26. data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +14 -9
  27. data/lib/hecks/bluebook/dsl/port_operation_builder.rb +1 -1
  28. data/lib/hecks/bluebook/dsl/rule_reference.rb +3 -3
  29. data/lib/hecks/bluebook/dsl/translation_builder.rb +1 -1
  30. data/lib/hecks/bluebook/dsl/world_builder.rb +47 -3
  31. data/lib/hecks/bluebook/hexagon.rb +15 -7
  32. data/lib/hecks/bluebook/meta_validator/judge.rb +3 -3
  33. data/lib/hecks/bluebook/meta_validator/plan.rb +4 -4
  34. data/lib/hecks/bluebook/meta_validator/readings.rb +1 -1
  35. data/lib/hecks/bluebook/meta_validator/reconstruction.rb +2 -2
  36. data/lib/hecks/bluebook/meta_validator/shapes.rb +1 -1
  37. data/lib/hecks/bluebook/meta_validator/syntax_boot.rb +11 -10
  38. data/lib/hecks/bluebook/meta_validator/world_judge.rb +3 -1
  39. data/lib/hecks/bluebook/meta_validator.rb +1 -1
  40. data/lib/hecks/bluebook/model_check/client_profile.rb +4 -70
  41. data/lib/hecks/bluebook/model_check.rb +13 -14
  42. data/lib/hecks/cache_dir.rb +103 -0
  43. data/lib/hecks/cli/document.rb +104 -0
  44. data/lib/hecks/cli/ir.rb +47 -0
  45. data/lib/hecks/cli/mcp.rb +32 -0
  46. data/lib/hecks/cli/mcp_door.rb +461 -0
  47. data/lib/hecks/cli/model_check.rb +270 -0
  48. data/lib/hecks/cli/project_cli.rb +123 -0
  49. data/lib/hecks/cli/project_diagrams.rb +43 -0
  50. data/lib/hecks/cli/run.rb +219 -0
  51. data/lib/hecks/cli/smoke_test.rb +59 -0
  52. data/lib/hecks/cli/stores.rb +54 -0
  53. data/lib/hecks/cli.rb +166 -0
  54. data/lib/hecks/embryonaut_bluebook/lock.rb +72 -0
  55. data/lib/hecks/embryonaut_bluebook/shape.rb +47 -0
  56. data/lib/hecks/embryonaut_bluebook/vendor.rb +194 -0
  57. data/lib/hecks/embryonaut_bluebook/vendor_cli.rb +88 -0
  58. data/lib/hecks/embryonaut_bluebook.rb +29 -3
  59. data/lib/hecks/facade/handle.rb +2 -2
  60. data/lib/hecks/facade/surface/aggregate_door.rb +1 -1
  61. data/lib/hecks/forms/value_object_shape.rb +1 -1
  62. data/lib/hecks/framework/bluebook/console_settings.bluebook +3 -3
  63. data/lib/hecks/language/bluebook/vocabulary.bluebook +16 -0
  64. data/lib/hecks/language/world/world.bluebook +32 -2
  65. data/lib/hecks/mcp_door_scope.rb +176 -0
  66. data/lib/hecks/ports/key_vault.rb +1 -1
  67. data/lib/hecks/ports/persistence/append_only.rb +1 -1
  68. data/lib/hecks/ports/persistence/binding_policy.rb +16 -5
  69. data/lib/hecks/ports/persistence/plugins/era/era_check.rb +3 -2
  70. data/lib/hecks/ports/persistence/plugins/era/era_guard.rb +2 -2
  71. data/lib/hecks/ports/persistence/plugins/era/era_tamper.rb +2 -2
  72. data/lib/hecks/ports/persistence/plugins/era/expected_era.rb +132 -0
  73. data/lib/hecks/ports/persistence/plugins/era/lineage.rb +1 -1
  74. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/era_store.rb +2 -2
  75. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage/head_compiler.rb +1 -1
  76. data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/minter.rb +1 -1
  77. data/lib/hecks/ports/persistence/plugins/era/translation/rule_compiler.rb +13 -7
  78. data/lib/hecks/ports/persistence/plugins/era.rb +7 -4
  79. data/lib/hecks/ports/persistence/postgres_dump/connection.rb +73 -0
  80. data/lib/hecks/ports/persistence/postgres_dump.rb +125 -0
  81. data/lib/hecks/ports/persistence/repository_factory.rb +2 -2
  82. data/lib/hecks/ports/persistence/state_codec.rb +1 -1
  83. data/lib/hecks/ports/persistence.rb +4 -0
  84. data/lib/hecks/projections/deploy/fargate/assembly.rb +193 -0
  85. data/lib/hecks/projections/deploy/fargate/cdn.rb +318 -0
  86. data/lib/hecks/projections/deploy/fargate/cdn_default.rb +87 -0
  87. data/lib/hecks/projections/deploy/fargate/check.rb +172 -0
  88. data/lib/hecks/projections/deploy/fargate/containers.rb +412 -0
  89. data/lib/hecks/projections/deploy/fargate/extras.rb +268 -0
  90. data/lib/hecks/projections/deploy/fargate/monitoring.rb +448 -0
  91. data/lib/hecks/projections/deploy/fargate/settings.rb +235 -0
  92. data/lib/hecks/projections/deploy/fargate/yaml.rb +116 -0
  93. data/lib/hecks/projections/deploy/fargate.rb +188 -139
  94. data/lib/hecks/projections/deploy/lambda.rb +8 -8
  95. data/lib/hecks/projections/deploy/preview/access_yaml.rb +145 -0
  96. data/lib/hecks/projections/deploy/preview/containers.rb +218 -0
  97. data/lib/hecks/projections/deploy/preview/preview.sh.erb +446 -0
  98. data/lib/hecks/projections/deploy/preview/routing_yaml.rb +216 -0
  99. data/lib/hecks/projections/deploy/preview/script.rb +40 -0
  100. data/lib/hecks/projections/deploy/preview/settings.rb +140 -0
  101. data/lib/hecks/projections/deploy/preview/task_yaml.rb +215 -0
  102. data/lib/hecks/projections/deploy/preview/template.rb +247 -0
  103. data/lib/hecks/projections/deploy/preview/yaml_text.rb +48 -0
  104. data/lib/hecks/projections/deploy/preview.rb +103 -0
  105. data/lib/hecks/projections/deploy/scripts/deploy-service.sh.tmpl +182 -0
  106. data/lib/hecks/projections/deploy/scripts/expected-era.header +17 -0
  107. data/lib/hecks/projections/deploy/scripts/hosting.mk.tmpl +54 -0
  108. data/lib/hecks/projections/deploy/scripts/settings.rb +114 -0
  109. data/lib/hecks/projections/deploy/scripts/smoke-after-deploy.sh.tmpl +232 -0
  110. data/lib/hecks/projections/deploy/scripts.rb +171 -0
  111. data/lib/hecks/projections/deploy/smoke/harness.js +366 -0
  112. data/lib/hecks/projections/deploy/smoke/workflow.yml.tmpl +56 -0
  113. data/lib/hecks/projections/deploy/smoke.rb +159 -0
  114. data/lib/hecks/projections/deploy/template_diff/comparer.rb +231 -0
  115. data/lib/hecks/projections/deploy/template_diff/loader.rb +105 -0
  116. data/lib/hecks/projections/deploy/template_diff.rb +173 -0
  117. data/lib/hecks/projections/glossary/sensitivity.rb +1 -1
  118. data/lib/hecks/projections/reference.rb +7 -1
  119. data/lib/hecks/projections.rb +2 -0
  120. data/lib/hecks/projector/exporter.rb +4 -4
  121. data/lib/hecks/projector/target.rb +1 -1
  122. data/lib/hecks/release/gem_pin/published_versions.rb +58 -0
  123. data/lib/hecks/release/gem_pin.rb +112 -0
  124. data/lib/hecks/release/runner/ci_publisher.rb +96 -0
  125. data/lib/hecks/release/runner/commands.rb +68 -0
  126. data/lib/hecks/release/runner/console.rb +52 -0
  127. data/lib/hecks/release/runner/gem_publisher.rb +44 -0
  128. data/lib/hecks/release/runner/git.rb +48 -0
  129. data/lib/hecks/release/runner/npm_publisher.rb +89 -0
  130. data/lib/hecks/release/runner/preflight.rb +119 -0
  131. data/lib/hecks/release/runner/published.rb +55 -0
  132. data/lib/hecks/release/runner/tagger.rb +87 -0
  133. data/lib/hecks/release/runner.rb +244 -0
  134. data/lib/hecks/router/namespace_installer.rb +1 -1
  135. data/lib/hecks/runtime/command_interpreter.rb +3 -3
  136. data/lib/hecks/runtime/command_rules/admissibility.rb +2 -2
  137. data/lib/hecks/runtime/command_rules/arithmetic.rb +2 -2
  138. data/lib/hecks/runtime/entity_element.rb +1 -1
  139. data/lib/hecks/runtime/entity_interpreter.rb +1 -1
  140. data/lib/hecks/runtime/invocation.rb +1 -1
  141. data/lib/hecks/runtime/loader.rb +27 -3
  142. data/lib/hecks/runtime/outbox.rb +1 -1
  143. data/lib/hecks/runtime/reaction_invocation.rb +3 -3
  144. data/lib/hecks/runtime/read_model_interpreter.rb +37 -15
  145. data/lib/hecks/runtime/reference_hop.rb +4 -4
  146. data/lib/hecks/runtime/registry/saga_persistence.rb +8 -2
  147. data/lib/hecks/runtime/registry/verification.rb +15 -13
  148. data/lib/hecks/runtime/registry/world_defaults.rb +106 -0
  149. data/lib/hecks/runtime/registry.rb +22 -13
  150. data/lib/hecks/runtime/saga_interpreter.rb +1 -1
  151. data/lib/hecks/runtime/tenant_check.rb +1 -1
  152. data/lib/hecks/runtime/value/coercion.rb +3 -3
  153. data/lib/hecks/runtime/value/entity_list_coercion.rb +1 -1
  154. data/lib/hecks/storehouse.rb +18 -12
  155. data/lib/hecks/vendoring/git_environment.rb +35 -0
  156. data/lib/hecks/vendoring/git_source.rb +102 -0
  157. data/lib/hecks/vendoring.rb +102 -0
  158. data/lib/hecks/version.rb +1 -1
  159. data/lib/hecks/vocabulary.rb +5 -0
  160. data/lib/hecks.rb +8 -2
  161. metadata +72 -58
  162. data/lib/hecks/bench/cli.rb +0 -74
  163. data/lib/hecks/bench/environment.rb +0 -100
  164. data/lib/hecks/bench/postgres_probe.rb +0 -41
  165. data/lib/hecks/bench/report.rb +0 -116
  166. data/lib/hecks/bench/ruby_runner.rb +0 -151
  167. data/lib/hecks/bench/run.rb +0 -55
  168. data/lib/hecks/bench/rust_runner.rb +0 -165
  169. data/lib/hecks/bench/stats.rb +0 -75
  170. data/lib/hecks/bench/suite.rb +0 -172
  171. data/lib/hecks/bench/workload.rb +0 -137
  172. data/lib/hecks/bench.rb +0 -28
  173. data/lib/hecks/codemod.rb +0 -443
  174. data/lib/hecks/corpus.rb +0 -479
  175. data/lib/hecks/doc/reference.rb +0 -585
  176. data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +0 -684
  177. data/lib/hecks/fuzzing/combination_miner.rb +0 -178
  178. data/lib/hecks/fuzzing/concurrent_dispatch.rb +0 -469
  179. data/lib/hecks/fuzzing/coverage_campaign.rb +0 -161
  180. data/lib/hecks/fuzzing/differential.rb +0 -192
  181. data/lib/hecks/fuzzing/domain_generator.rb +0 -871
  182. data/lib/hecks/fuzzing/era_boundary.rb +0 -154
  183. data/lib/hecks/fuzzing/form_census.rb +0 -285
  184. data/lib/hecks/fuzzing/generated_domain_check.rb +0 -171
  185. data/lib/hecks/fuzzing/invalid_value_generator.rb +0 -125
  186. data/lib/hecks/fuzzing/isolated_boot.rb +0 -604
  187. data/lib/hecks/fuzzing/nondeterministic.rb +0 -79
  188. data/lib/hecks/fuzzing/persistence_parity.rb +0 -253
  189. data/lib/hecks/fuzzing/properties/corrections.rb +0 -125
  190. data/lib/hecks/fuzzing/properties/dispatch_and_mutations.rb +0 -688
  191. data/lib/hecks/fuzzing/properties/guards.rb +0 -398
  192. data/lib/hecks/fuzzing/properties/invariants_and_aggregation.rb +0 -399
  193. data/lib/hecks/fuzzing/properties/lifecycle_and_replay.rb +0 -137
  194. data/lib/hecks/fuzzing/properties/outbox.rb +0 -180
  195. data/lib/hecks/fuzzing/properties/querying.rb +0 -260
  196. data/lib/hecks/fuzzing/properties.rb +0 -267
  197. data/lib/hecks/fuzzing/qa_settings.rb +0 -164
  198. data/lib/hecks/fuzzing/replay.rb +0 -930
  199. data/lib/hecks/fuzzing/rotation_priority.rb +0 -114
  200. data/lib/hecks/fuzzing/rust_gap_manifest.rb +0 -139
  201. data/lib/hecks/fuzzing/self_consistency.rb +0 -825
  202. data/lib/hecks/fuzzing/sequence_generator/adversary.rb +0 -532
  203. data/lib/hecks/fuzzing/sequence_generator/catalog.rb +0 -171
  204. data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +0 -127
  205. data/lib/hecks/fuzzing/sequence_generator/picker.rb +0 -116
  206. data/lib/hecks/fuzzing/sequence_generator/query_binding.rb +0 -138
  207. data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +0 -344
  208. data/lib/hecks/fuzzing/sequence_generator.rb +0 -311
  209. data/lib/hecks/fuzzing/shrinker.rb +0 -309
  210. data/lib/hecks/fuzzing/structural_skips.rb +0 -53
  211. data/lib/hecks/fuzzing/sweep_depth.rb +0 -61
  212. data/lib/hecks/fuzzing/target_capabilities.rb +0 -255
  213. data/lib/hecks/fuzzing/value_generator.rb +0 -340
  214. data/lib/hecks/fuzzing.rb +0 -23
  215. data/lib/hecks/grammar/evolve.rb +0 -524
  216. data/lib/hecks/query_ir.rb +0 -482
@@ -0,0 +1,268 @@
1
+ require_relative "check"
2
+ require_relative "yaml"
3
+
4
+ module Hecks
5
+ module Projections
6
+ module Deploy
7
+ module Fargate
8
+ # The extra resources, parameters, outputs and IAM grants a stack can
9
+ # declare beyond the ones the generator always makes.
10
+ #
11
+ # ## Settings
12
+ #
13
+ # - `buckets`: S3 buckets, each `{id:, name_prefix:, public_read:, cors_origins:}`. The
14
+ # bucket is named `<name_prefix>-<account id>`. `public_read` opens the bucket to
15
+ # anonymous `GetObject`. `cors_origins` allows those origins to `GET` from a browser.
16
+ # - `generated_secrets`: Secrets Manager secrets with a generated value, each
17
+ # `{id:, name:, description:, key:, length:}`. `key` is the JSON field the value is
18
+ # stored under (`secret`).
19
+ # - `session_secret`: `{name:, description:}` names the always-created session secret
20
+ # and describes it.
21
+ # - `task_policies` and `execution_policies`: extra IAM policies for the task role (what
22
+ # the containers may do) and the execution role (what `ECS` may do to start them).
23
+ # Each is `{name:, statements: [{effect:, actions:, resources:}]}`; `effect` is `Allow`
24
+ # by default and resources may be intrinsics.
25
+ # - `parameters`: extra template parameters, a map of name to
26
+ # `{type:, default:, description:, no_echo:}`.
27
+ # - `outputs`: extra template outputs, a map of name to a value or intrinsic.
28
+ module Extras
29
+ module_function
30
+
31
+ BUCKET_KEYS = [:id, :name_prefix, :public_read, :cors_origins].freeze
32
+ SECRET_KEYS = [:id, :name, :description, :key, :length].freeze
33
+ POLICY_KEYS = [:name, :statements].freeze
34
+ STATEMENT_KEYS = [:effect, :actions, :resources].freeze
35
+ PARAMETER_KEYS = [:type, :default, :description, :no_echo].freeze
36
+ PARAMETER_TYPE = /\A(String|Number|CommaDelimitedList|List<Number>|AWS::[A-Za-z0-9:]+|AWS::SSM::Parameter::Value<[^>]+>)\z/
37
+ EXEC_ACTIONS = %w[ssmmessages:CreateControlChannel ssmmessages:CreateDataChannel
38
+ ssmmessages:OpenControlChannel ssmmessages:OpenDataChannel].freeze
39
+
40
+ # Reads and checks the resource-declaring settings.
41
+ #
42
+ # @param settings [Hash{Symbol => Object}] the world's `deployed_to("AwsFargate")`
43
+ # settings
44
+ # @return [Hash{Symbol => Object}] `:buckets`, `:secrets`, `:session_secret`,
45
+ # `:task_policies`,
46
+ # `:execution_policies`, `:parameters` and `:outputs`, each checked and defaulted to
47
+ # empty
48
+ # @raise [ArgumentError] if any of those settings is malformed
49
+ def normalize(settings)
50
+ {
51
+ buckets: buckets(settings.fetch(:buckets, [])),
52
+ secrets: secrets(settings.fetch(:generated_secrets, [])),
53
+ session_secret: session_secret(settings[:session_secret]),
54
+ task_policies: policies(settings.fetch(:task_policies, []), "task_policies"),
55
+ execution_policies: policies(settings.fetch(:execution_policies, []), "execution_policies"),
56
+ parameters: parameters(settings.fetch(:parameters, {})),
57
+ outputs: outputs(settings.fetch(:outputs, {}))
58
+ }
59
+ end
60
+
61
+ # Renders the buckets and generated secrets.
62
+ #
63
+ # @param extras [Hash] the checked settings from `normalize`
64
+ # @return [String] flush-left resources separated by blank lines, or empty
65
+ def resources_yaml(extras)
66
+ (extras[:buckets].flat_map { |bucket| bucket_blocks(bucket) } + extras[:secrets].map do |secret|
67
+ secret_yaml(secret)
68
+ end).join("\n\n")
69
+ end
70
+
71
+ # Renders extra policies as entries of a role's `Policies` list.
72
+ #
73
+ # @param policies [Array<Hash>] the checked policies
74
+ # @return [String] flush-left list entries, or empty
75
+ def policies_yaml(policies)
76
+ policies.map { |policy| policy_yaml(policy) }.join
77
+ end
78
+
79
+ # Renders the policy that lets `aws ecs execute-command` open a session in a container.
80
+ #
81
+ # @return [String] one flush-left `Policies` list entry
82
+ def execute_command_policy_yaml
83
+ policy_yaml(
84
+ name: "EcsExecDebug",
85
+ statements: [{ effect: "Allow", actions: EXEC_ACTIONS, resources: ["*"] }]
86
+ )
87
+ end
88
+
89
+ # Renders the extra template parameters.
90
+ #
91
+ # @param parameters [Hash{String => Hash}] the checked parameters
92
+ # @return [String] flush-left parameters, or empty
93
+ def parameters_yaml(parameters)
94
+ parameters.map do |name, given|
95
+ lines = ["#{name}:", " Type: #{given[:type]}"]
96
+ lines << " Default: #{Yaml.scalar(given[:default])}" if given.key?(:default)
97
+ lines << " Description: #{Yaml.string(given[:description])}" if given[:description]
98
+ lines << " NoEcho: true" if given[:no_echo]
99
+ "#{lines.join("\n")}\n"
100
+ end.join
101
+ end
102
+
103
+ # Renders the extra template outputs.
104
+ #
105
+ # @param outputs [Hash{String => Object}] the checked outputs
106
+ # @return [String] flush-left outputs, or empty
107
+ def outputs_yaml(outputs)
108
+ outputs.map { |name, value| "#{name}:\n Value: #{Yaml.string(value)}\n" }.join
109
+ end
110
+
111
+ def buckets(entries)
112
+ Check.hashes!(entries, "buckets", allowed: BUCKET_KEYS, required: [:id, :name_prefix]).map do |entry|
113
+ {
114
+ id: Check.logical_id!(entry[:id], "buckets id"),
115
+ name_prefix: Check.resource_name!(entry[:name_prefix], "buckets name_prefix").downcase,
116
+ public_read: Check.boolean!(entry.fetch(:public_read, false), "buckets public_read"),
117
+ cors_origins: Check.strings!(entry.fetch(:cors_origins, []), "buckets cors_origins")
118
+ }
119
+ end
120
+ end
121
+ private_class_method :buckets
122
+
123
+ def secrets(entries)
124
+ Check.hashes!(entries, "generated_secrets", allowed: SECRET_KEYS, required: [:id, :name]).map do |entry|
125
+ {
126
+ id: Check.logical_id!(entry[:id], "generated_secrets id"),
127
+ name: Check.resource_name!(entry[:name], "generated_secrets name"),
128
+ description: entry[:description]&.to_s,
129
+ key: Check.resource_name!(entry.fetch(:key, "secret"), "generated_secrets key"),
130
+ length: Check.integer!(entry.fetch(:length, 64), "generated_secrets length", range: 8..512)
131
+ }
132
+ end
133
+ end
134
+ private_class_method :secrets
135
+
136
+ def session_secret(value)
137
+ return {} if value.nil?
138
+
139
+ given = Check.hash!(value, "session_secret", allowed: [:name, :description])
140
+ { name: given[:name] && Check.resource_name!(given[:name], "session_secret name"),
141
+ description: given[:description]&.to_s }.compact
142
+ end
143
+ private_class_method :session_secret
144
+
145
+ def policies(entries, where)
146
+ Check.hashes!(entries, where, allowed: POLICY_KEYS, required: [:name, :statements]).map do |entry|
147
+ statements = Check.hashes!(entry[:statements], "#{where}[#{entry[:name]}] statements",
148
+ allowed: STATEMENT_KEYS, required: [:actions, :resources])
149
+ {
150
+ name: Check.logical_id!(entry[:name], "#{where} name"),
151
+ statements: statements.map do |statement|
152
+ {
153
+ effect: Check.one_of!(statement.fetch(:effect, "Allow"), "#{where} effect", %w[Allow Deny]),
154
+ actions: Check.strings!(statement[:actions], "#{where} actions", min: 1),
155
+ resources: Check.strings!(statement[:resources], "#{where} resources", min: 1)
156
+ }
157
+ end
158
+ }
159
+ end
160
+ end
161
+ private_class_method :policies
162
+
163
+ def parameters(map)
164
+ raise ArgumentError, "parameters must be a hash of name to settings, got #{map.inspect}" unless map.is_a?(Hash)
165
+
166
+ map.to_h do |name, entry|
167
+ key = Check.logical_id!(name, "parameters name")
168
+ given = Check.hash!(entry, "parameters.#{key}", allowed: PARAMETER_KEYS, required: [:type])
169
+ unless PARAMETER_TYPE.match?(given[:type].to_s)
170
+ raise ArgumentError,
171
+ "parameters.#{key} type #{given[:type].inspect} is not a CloudFormation parameter type"
172
+ end
173
+
174
+ [key, given.merge(type: given[:type].to_s, no_echo: given[:no_echo] ? true : false)]
175
+ end
176
+ end
177
+ private_class_method :parameters
178
+
179
+ def outputs(map)
180
+ raise ArgumentError, "outputs must be a hash of name to value, got #{map.inspect}" unless map.is_a?(Hash)
181
+
182
+ map.to_h do |name, value|
183
+ raise ArgumentError, "outputs.#{name} needs a value" if value.nil?
184
+
185
+ [Check.logical_id!(name, "outputs name"), value]
186
+ end
187
+ end
188
+ private_class_method :outputs
189
+
190
+ def bucket_blocks(bucket)
191
+ blocks = [bucket_yaml(bucket)]
192
+ blocks << bucket_policy_yaml(bucket) if bucket[:public_read]
193
+ blocks
194
+ end
195
+ private_class_method :bucket_blocks
196
+
197
+ def bucket_yaml(bucket)
198
+ lines = ["#{bucket[:id]}:", " Type: AWS::S3::Bucket", " Properties:",
199
+ " BucketName: !Sub \"#{bucket[:name_prefix]}-${AWS::AccountId}\""]
200
+ lines.concat(public_access_lines) if bucket[:public_read]
201
+ lines.concat(cors_lines(bucket[:cors_origins])) unless bucket[:cors_origins].empty?
202
+ lines.join("\n")
203
+ end
204
+ private_class_method :bucket_yaml
205
+
206
+ def public_access_lines
207
+ [" PublicAccessBlockConfiguration:", " BlockPublicAcls: false", " BlockPublicPolicy: false",
208
+ " IgnorePublicAcls: false", " RestrictPublicBuckets: false"]
209
+ end
210
+ private_class_method :public_access_lines
211
+
212
+ def cors_lines(origins)
213
+ [" CorsConfiguration:", " CorsRules:", " - AllowedOrigins: #{Yaml.flow_list(origins)}",
214
+ " AllowedMethods: [GET]", " AllowedHeaders: [\"*\"]"]
215
+ end
216
+ private_class_method :cors_lines
217
+
218
+ def bucket_policy_yaml(bucket)
219
+ <<~POLICY.rstrip
220
+ #{bucket[:id]}Policy:
221
+ Type: AWS::S3::BucketPolicy
222
+ Properties:
223
+ Bucket: !Ref #{bucket[:id]}
224
+ PolicyDocument:
225
+ Version: "2012-10-17"
226
+ Statement:
227
+ - Effect: Allow
228
+ Principal: "*"
229
+ Action: s3:GetObject
230
+ Resource: !Sub "${#{bucket[:id]}.Arn}/*"
231
+ POLICY
232
+ end
233
+ private_class_method :bucket_policy_yaml
234
+
235
+ def secret_yaml(secret)
236
+ lines = ["#{secret[:id]}:", " Type: AWS::SecretsManager::Secret", " Properties:", " Name: #{secret[:name]}"]
237
+ lines << " Description: #{Yaml.string(secret[:description])}" if secret[:description]
238
+ lines.push(" GenerateSecretString:", " SecretStringTemplate: '{}'",
239
+ " GenerateStringKey: #{secret[:key]}", " PasswordLength: #{secret[:length]}",
240
+ " ExcludePunctuation: true")
241
+ lines.join("\n")
242
+ end
243
+ private_class_method :secret_yaml
244
+
245
+ def policy_yaml(policy)
246
+ lines = ["- PolicyName: #{policy[:name]}", " PolicyDocument:", " Version: '2012-10-17'", " Statement:"]
247
+ policy[:statements].each do |statement|
248
+ lines << " - Effect: #{statement[:effect]}"
249
+ lines.concat(statement_list("Action", statement[:actions]))
250
+ lines.concat(statement_list("Resource", statement[:resources].map { |resource| Yaml.string(resource) }))
251
+ end
252
+ "#{lines.join("\n")}\n"
253
+ end
254
+ private_class_method :policy_yaml
255
+
256
+ # One entry is written as a scalar, the form IAM policies are usually written in; more are
257
+ # a list.
258
+ def statement_list(key, values)
259
+ return [" #{key}: #{values.first}"] if values.one?
260
+
261
+ [" #{key}:"] + values.map { |value| " - #{value}" }
262
+ end
263
+ private_class_method :statement_list
264
+ end
265
+ end
266
+ end
267
+ end
268
+ end