geoengineer 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/README.md +18 -8
  5. data/lib/geoengineer/cli/geo_cli.rb +4 -1
  6. data/lib/geoengineer/environment.rb +1 -1
  7. data/lib/geoengineer/resources/aws_cloudtrail.rb +20 -0
  8. data/lib/geoengineer/resources/aws_cloudwatch_event_rule.rb +26 -0
  9. data/lib/geoengineer/resources/aws_cloudwatch_event_target.rb +50 -0
  10. data/lib/geoengineer/resources/aws_db_instance.rb +5 -1
  11. data/lib/geoengineer/resources/aws_dynamodb_table.rb +24 -0
  12. data/lib/geoengineer/resources/aws_elasticache_cluster.rb +22 -9
  13. data/lib/geoengineer/resources/aws_iam_policy.rb +1 -0
  14. data/lib/geoengineer/resources/aws_iam_role_policy.rb +63 -0
  15. data/lib/geoengineer/resources/aws_kinesis_stream.rb +40 -0
  16. data/lib/geoengineer/resources/aws_lambda_alias.rb +18 -3
  17. data/lib/geoengineer/resources/aws_lambda_function.rb +11 -0
  18. data/lib/geoengineer/resources/aws_lambda_permission.rb +9 -0
  19. data/lib/geoengineer/resources/aws_load_balancer_backend_server_policy.rb +48 -0
  20. data/lib/geoengineer/resources/aws_load_balancer_policy.rb +43 -0
  21. data/lib/geoengineer/resources/aws_network_acl_rule.rb +27 -2
  22. data/lib/geoengineer/resources/aws_redshift_cluster.rb +10 -4
  23. data/lib/geoengineer/resources/aws_route.rb +2 -0
  24. data/lib/geoengineer/resources/aws_vpn_gateway_attachment.rb +8 -0
  25. data/lib/geoengineer/sub_resource.rb +13 -3
  26. data/lib/geoengineer/utils/aws_clients.rb +18 -2
  27. data/lib/geoengineer/utils/has_projects.rb +3 -1
  28. data/lib/geoengineer/utils/has_validations.rb +7 -0
  29. data/lib/geoengineer/version.rb +1 -1
  30. data/spec/resource_spec.rb +14 -0
  31. data/spec/resources/aws_cloudtrail_spec.rb +35 -0
  32. data/spec/resources/aws_cloudwatch_event_rule_spec.rb +33 -0
  33. data/spec/resources/aws_cloudwatch_event_target_spec.rb +54 -0
  34. data/spec/resources/aws_dynamodb_table_spec.rb +24 -0
  35. data/spec/resources/aws_iam_role_spec.rb +21 -18
  36. data/spec/resources/aws_iam_rule_policy_spec.rb +49 -0
  37. data/spec/resources/{aws_iam_user.rb → aws_iam_user_spec.rb} +9 -6
  38. data/spec/resources/aws_kinesis_stream_spec.rb +52 -0
  39. data/spec/resources/aws_lambda_alias_spec.rb +22 -3
  40. data/spec/resources/aws_load_balancer_backend_server_policy_spec.rb +42 -0
  41. data/spec/resources/aws_load_balancer_policy_spec.rb +44 -0
  42. data/spec/resources/aws_network_acl_rule_spec.rb +29 -1
  43. data/spec/resources/{aws_ses_receipt_rule_set.rb → aws_ses_receipt_rule_set_spec.rb} +13 -6
  44. data/spec/resources/{aws_ses_receipt_rule.rb → aws_ses_receipt_rule_spec.rb} +6 -5
  45. data/spec/resources/{aws_sns_topic_subscription.rb → aws_sns_topic_subscription_spec.rb} +0 -0
  46. data/spec/resources/aws_subnet_spec.rb +2 -2
  47. data/spec/resources/aws_vpc_dhcp_options_association_spec.rb +2 -2
  48. data/spec/resources/aws_vpc_spec.rb +2 -2
  49. data/spec/sub_resource_spec.rb +12 -0
  50. data/spec/utils/has_subresources_spec.rb +9 -0
  51. data/spec/utils/has_validations_spec.rb +23 -3
  52. metadata +34 -10
  53. metadata.gz.sig +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ca09f8d5b8d93ce13661980ff56dd0e28763ecbf
4
- data.tar.gz: 4d9e40dfd4402a197a4bab1fb3a184966045fd24
3
+ metadata.gz: dda3981b643cb0887fcdb1e794abfa4b8c6ac9c0
4
+ data.tar.gz: 68d052b3c9cd6a7a3f77571837cfda41b1413161
5
5
  SHA512:
6
- metadata.gz: d99a2fe4223c269d192a8c662626fba2aacddc813ee12fc03fe1a58179a4e6ab5f2a38c21d58cb8aa12ca1e53097906cee497b4853e8ce407003c9890eaf1f7e
7
- data.tar.gz: cb63c4c1a257d390ee22f992a725adb180e8a64d9374875b8cc185efb35b9114522248412cf60db060d7128fde5f7e3c6a563bdedee5b0b7be88c0381ccd695d
6
+ metadata.gz: 8b2bae296535675da928d1decd4e9d96f448876642e953f40d50e6f4ed919ddb3cd6bebc89380a19dddf27305735d8ed2d212af29485baf42ccd582a54751432
7
+ data.tar.gz: 7e9eb34de6c0d0cb88d2f182ed1e618570404d30bc6c84f97c3a04bd13544e2191ba4bb82cc29e1e333f554881701f1630702072a60dea4ae005ca04d303a434
checksums.yaml.gz.sig CHANGED
Binary file
data.tar.gz.sig CHANGED
Binary file
data/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  <a href="https://commons.wikimedia.org/wiki/File:Mantle_of_Responsibility.png"><img src="./assets/mantle.png" align="right" alt="Mantle_of_Responsibility" /></a>
6
6
 
7
- GeoEngineer provides a Ruby DSL and command line tool (`geo`) to *codeify* then plan and execute changes to cloud resources.
7
+ GeoEngineer provides a Ruby DSL and command line tool (`geo`) to *codify* then plan and execute changes to cloud resources.
8
8
 
9
9
  GeoEngineer's goals/requirements/features are:
10
10
 
@@ -29,9 +29,19 @@ Instructions to install Ruby can be found [here](https://www.ruby-lang.org/en/do
29
29
 
30
30
  ### Install GeoEngineer
31
31
 
32
+ Secure install (this will validate the geoengineer gem):
33
+
34
+ ```
35
+ gem cert --add <(curl -Ls https://raw.githubusercontent.com/coinbase/geoengineer/master/certs/geoengineer-gem.pem)
36
+ gem install geoengineer --trust-policy MediumSecurity
37
+ ```
38
+
39
+ *Note: HighSecurity will not work because aws-sdk and other gems are not signed*
40
+
41
+ Or just install normally:
42
+
32
43
  ```
33
- gem cert --add <(https://raw.githubusercontent.com/coinbase/geoengineer/master/certs/geoengineer-gem.pem)
34
- gem install geoengineer --trust-policy HighSecurity
44
+ gem install geoengineer
35
45
  ```
36
46
 
37
47
  Test it is installed correctly with:
@@ -353,7 +363,7 @@ The core models in GeoEngineer are:
353
363
 
354
364
  1. `Environment` contains many resources that may exist outside of a project, like VPCs or routing tables. Also every project defined to be in the environment, for example the `test_www` project is in `staging` but `monorail` is in `staging` and `production` environments.
355
365
  2. `Project` contains many resources and services grouped together into a name.
356
- 3. `Template` has a `type` and `name`, and a group of resources that are defined in a pattern, e.g. every Load Balancer requires a unique security group that allows traffic in. It is an simple abstraction that can dramatically simplify and standardize cloud resources.
366
+ 3. `Template` has a `type` and `name`, and a group of resources that are defined in a pattern, e.g. every Load Balancer requires a unique security group that allows traffic in. It is a simple abstraction that can dramatically simplify and standardize cloud resources.
357
367
  4. `Resource` and `SubResource` are based off of how terraform models cloud resources. A `Resource` instance can have many `SubResource` instances, but a `SubResource` instance belongs to only one `Resource` instance, e.g. a load balancer resource may have a `health_check` sub-resource to only allow specific incoming ports.
358
368
 
359
369
  All these models can have arbitrary attributes assigned to them either by directly assigning on the instance, or through passing a block to the constructor. For example:
@@ -390,7 +400,7 @@ puts resource.lazy_attr
390
400
 
391
401
  ### Environment
392
402
 
393
- The top level class in GeoEngineer is the `environment`, it contains all projects, resources and services, there should only ever be one initialized at a time.
403
+ The top level class in GeoEngineer is the `environment`: it contains all projects, resources and services, and there should only ever be one initialized at a time.
394
404
 
395
405
  An environment can mean many things to different people, e.g. an AWS account, an AWS region, or a specific AWS VPC. The only real constraint is that a resource has one instance per environment, e.g. a load balancer that is defined to be in `staging` and `production` environments, will have an instance in each.
396
406
 
@@ -418,14 +428,14 @@ project = project('org', 'project_name') {
418
428
 
419
429
  This projects organization is `org`, its name `project_name` and will be provisioned in the `staging` and `production` environments. The `org` and `name` must be unique across all other projects.
420
430
 
421
- The method `project` will automatically add the project to the instantiated environment object **only if** that environments name is in the list of environments, otherwise it is ignored.
431
+ The method `project` will automatically add the project to the instantiated environment object **only if** that environment's name is in the list of environments, otherwise it is ignored.
422
432
 
423
433
  ### Templates
424
434
 
425
435
  A template is used to create a group of resources in a recommended pattern. For example, an HTTP service could create a load balancer, a load balancer security group, and a ec2 security group.
426
436
 
427
437
  ```ruby
428
- template_instance = project.from_template('template_type', 'name', { parameter: 'helloworld') { |resource, resource_sg|
438
+ template_instance = project.from_template('template_type', 'name', parameter: 'helloworld') { |resource, resource_sg|
429
439
 
430
440
  # set attribute
431
441
  attribute "custom attribute"
@@ -471,6 +481,6 @@ template.resource('type', 'identifier') {
471
481
 
472
482
  The `type` of a resource must be a valid terraform type, where AWS types are listed [here](https://www.terraform.io/docs/providers/aws/index.html). Some resources are not supported yet by GeoEngineer.
473
483
 
474
- `identifier` is used by GeoEngineer and terraform to reference this resource so must be unique, however it is not stored in the cloud so can be changed without affecting a plan.
484
+ `identifier` is used by GeoEngineer and terraform to reference this resource must be unique, however it is not stored in the cloud so can be changed without affecting a plan.
475
485
 
476
486
  A resource also has a ruby block sent to it that contains parameters and sub-resources. These values are defined by terraform so for reference to what values are required please refer to the [terraform docs](https://www.terraform.io/docs/providers/aws/index.html).
@@ -140,7 +140,10 @@ class GeoCLI
140
140
 
141
141
  @environment.execute_lifecycle(:before, action_name.to_sym)
142
142
  errs = @environment.errors.flatten.sort
143
- return print_validation_errors(errs) unless errs.empty?
143
+ unless errs.empty?
144
+ print_validation_errors(errs)
145
+ exit 1
146
+ end
144
147
 
145
148
  yield args, options
146
149
  @environment.execute_lifecycle(:after, action_name.to_sym)
@@ -64,7 +64,7 @@ class GeoEngineer::Environment
64
64
  # do not add the project if the project is not supported by this environment
65
65
  return NullObject.new unless supported_environments.include?(@name)
66
66
 
67
- projects[name] = project
67
+ project
68
68
  end
69
69
 
70
70
  def resource(type, id, &block)
@@ -0,0 +1,20 @@
1
+ ########################################################################
2
+ # AwsCloudTrail is the +aws_cloudtrail+ terrform resource,
3
+ #
4
+ # {https://www.terraform.io/docs/providers/aws/r/cloudtrail.html}
5
+ ########################################################################
6
+ class GeoEngineer::Resources::AwsCloudtrail < GeoEngineer::Resource
7
+ validate -> { validate_required_attributes([:name, :s3_bucket_name]) }
8
+
9
+ after :initialize, -> { _terraform_id -> { name } }
10
+
11
+ def self._fetch_remote_resources
12
+ cloudtrails = AwsClients.cloudtrail.describe_trails["trail_list"].map(&:to_h)
13
+
14
+ cloudtrails.map do |ct|
15
+ ct[:_terraform_id] = ct[:name]
16
+ ct[:_geo_id] = ct[:name]
17
+ ct
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,26 @@
1
+ ########################################################################
2
+ # AwsCloudwatchEventRule is the +aws_cloudwatch_event_rule+ terrform resource,
3
+ #
4
+ # {https://www.terraform.io/docs/providers/aws/r/cloudwatch_event_rule.html Terraform Docs}
5
+ ########################################################################
6
+ class GeoEngineer::Resources::AwsCloudwatchEventRule < GeoEngineer::Resource
7
+ validate -> { validate_required_attributes([:name, :schedule_expression]) }
8
+
9
+ after :initialize, -> { _terraform_id -> { NullObject.maybe(remote_resource)._terraform_id } }
10
+ after :initialize, -> { _geo_id -> { self[:name] } }
11
+
12
+ def support_tags?
13
+ false
14
+ end
15
+
16
+ def self._fetch_remote_resources
17
+ AwsClients.cloudwatchevents.list_rules.rules.map(&:to_h).map do |cloudwatch_event_rule|
18
+ cloudwatch_event_rule.merge(
19
+ {
20
+ _terraform_id: cloudwatch_event_rule[:name],
21
+ _geo_id: cloudwatch_event_rule[:name]
22
+ }
23
+ )
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,50 @@
1
+ ########################################################################
2
+ # AwsCloudwatchEventTarget is the +aws_cloudwatch_event_target+ terrform resource,
3
+ #
4
+ # {https://www.terraform.io/docs/providers/aws/r/cloudwatch_event_target.html Terraform Docs}
5
+ ########################################################################
6
+ class GeoEngineer::Resources::AwsCloudwatchEventTarget < GeoEngineer::Resource
7
+ validate -> { validate_required_attributes([:rule, :arn, :target_id]) }
8
+
9
+ after :initialize, -> { _terraform_id -> { NullObject.maybe(remote_resource)._terraform_id } }
10
+ after :initialize, -> { _geo_id -> { "#{self.rule}-#{self.target_id}" } }
11
+
12
+ def to_terraform_state
13
+ tfstate = super
14
+ tfstate[:primary][:attributes] = {
15
+ 'target_id' => self.target_id,
16
+ 'rule' => self.rule
17
+ }
18
+ tfstate
19
+ end
20
+
21
+ def support_tags?
22
+ false
23
+ end
24
+
25
+ def self._fetch_remote_resources
26
+ AwsClients
27
+ .cloudwatchevents
28
+ .list_rules
29
+ .rules
30
+ .map(&:to_h)
31
+ .map { |rule| _get_rule_targets(rule) }
32
+ .flatten
33
+ .map do |rule_target|
34
+ rule_target.merge(
35
+ {
36
+ _terraform_id: "#{rule_target[:rule_name]}-#{rule_target[:id]}",
37
+ _geo_id: "#{rule_target[:rule_name]}-#{rule_target[:id]}"
38
+ }
39
+ )
40
+ end
41
+ end
42
+
43
+ def self._get_rule_targets(rule)
44
+ AwsClients
45
+ .cloudwatchevents
46
+ .list_targets_by_rule({ rule: rule[:name] })[:targets]
47
+ .map(&:to_h)
48
+ .map { |target| target.merge({ rule_name: rule[:name] }) }
49
+ end
50
+ end
@@ -14,7 +14,11 @@ class GeoEngineer::Resources::AwsDbInstance < GeoEngineer::Resource
14
14
  )
15
15
  end
16
16
  }
17
- validate -> { validate_required_attributes([:password, :username, :name]) if new? }
17
+ validate -> {
18
+ if new? && !(snapshot_identifier || replicate_source_db)
19
+ validate_required_attributes([:password, :username, :name])
20
+ end
21
+ }
18
22
  validate -> { validate_required_attributes([:instance_class, :engine]) }
19
23
  validate -> { validate_subresource_required_attributes(:access_logs, [:bucket]) }
20
24
 
@@ -0,0 +1,24 @@
1
+ ########################################################################
2
+ # AwsDynamodbTable +aws_dynamodb_table+ terrform resource,
3
+ #
4
+ # {https://www.terraform.io/docs/providers/aws/r/aws_dynamodb_table.html Terraform Docs}
5
+ ########################################################################
6
+ class GeoEngineer::Resources::AwsDynamodbTable < GeoEngineer::Resource
7
+ validate -> { validate_required_attributes([:name, :read_capacity, :write_capacity, :hash_key]) }
8
+
9
+ after :initialize, -> { _terraform_id -> { name } }
10
+
11
+ def support_tags?
12
+ false
13
+ end
14
+
15
+ def self._fetch_remote_resources
16
+ AwsClients.dynamo.list_tables['table_names'].map { |name|
17
+ {
18
+ name: name,
19
+ _geo_id: name,
20
+ _terraform_id: name
21
+ }
22
+ }
23
+ end
24
+ end
@@ -4,6 +4,8 @@
4
4
  # {https://www.terraform.io/docs/providers/aws/r/elasticache_cluster.html Terraform Docs}
5
5
  ########################################################################
6
6
  class GeoEngineer::Resources::AwsElasticacheCluster < GeoEngineer::Resource
7
+ class SecurityGroupError < StandardError; end
8
+
7
9
  validate -> {
8
10
  validate_required_attributes(
9
11
  [
@@ -21,14 +23,17 @@ class GeoEngineer::Resources::AwsElasticacheCluster < GeoEngineer::Resource
21
23
 
22
24
  def to_terraform_state
23
25
  tfstate = super
24
- attributes = {
25
- 'port' => port.to_s,
26
- 'parameter_group_name' => parameter_group_name
27
- }
26
+ attributes = { 'port' => port.to_s, 'parameter_group_name' => parameter_group_name }
28
27
 
29
28
  # Security groups workaround
30
29
  security_group_ids.each_with_index do |sg, i|
31
- attributes["security_group_ids.#{i}"] = sg._terraform_id
30
+ if sg.is_a?(GeoEngineer::Resource)
31
+ attributes["security_group_ids.#{i}"] = sg._terraform_id
32
+ elsif sg.is_a?(String)
33
+ attributes["security_group_ids.#{i}"] = sg
34
+ else
35
+ raise SecurityGroupError, "Please pass a Geo Resource or string ID"
36
+ end
32
37
  end
33
38
  attributes['security_group_ids.#'] = security_group_ids.count.to_s
34
39
 
@@ -41,10 +46,18 @@ class GeoEngineer::Resources::AwsElasticacheCluster < GeoEngineer::Resource
41
46
  end
42
47
 
43
48
  def self._fetch_remote_resources
44
- AwsClients.elasticache.describe_cache_clusters['cache_clusters'].map(&:to_h).map do |ec|
45
- ec[:_geo_id] = ec[:cache_cluster_id]
46
- ec[:_terraform_id] = ec[:cache_cluster_id]
47
- ec
49
+ AwsClients
50
+ .elasticache
51
+ .describe_cache_clusters['cache_clusters']
52
+ .map(&:to_h)
53
+ .select { |ec| ec[:replication_group_id].nil? }
54
+ .map do |ec|
55
+ ec.merge(
56
+ {
57
+ _terraform_id: ec[:cache_cluster_id],
58
+ _geo_id: ec[:cache_cluster_id]
59
+ }
60
+ )
48
61
  end
49
62
  end
50
63
  end
@@ -5,6 +5,7 @@
5
5
  ########################################################################
6
6
  class GeoEngineer::Resources::AwsIamPolicy < GeoEngineer::Resource
7
7
  validate -> { validate_required_attributes([:name, :policy]) }
8
+ validate -> { validate_policy_length(self.policy) }
8
9
 
9
10
  after :initialize, -> {
10
11
  _terraform_id -> { NullObject.maybe(remote_resource)._terraform_id }
@@ -0,0 +1,63 @@
1
+ ########################################################################
2
+ # AwsIamRolePolicy +aws_iam_role_policy+ terrform resource,
3
+ #
4
+ # {https://www.terraform.io/docs/providers/aws/r/iam_role_policy.html Terraform Docs}
5
+ ########################################################################
6
+ class GeoEngineer::Resources::AwsIamRolePolicy < GeoEngineer::Resource
7
+ validate -> { validate_required_attributes([:name, :policy, :role]) }
8
+ validate -> { validate_policy_length(self.policy) }
9
+
10
+ after :initialize, -> {
11
+ _terraform_id -> { "#{role}:#{name}" }
12
+ }
13
+
14
+ def to_terraform_state
15
+ tfstate = super
16
+ attributes = {
17
+ 'policy' => policy,
18
+ 'role' => role,
19
+ 'name' => name
20
+ }
21
+
22
+ tfstate[:primary][:attributes] = attributes
23
+
24
+ tfstate
25
+ end
26
+
27
+ def support_tags?
28
+ false
29
+ end
30
+
31
+ def _policy_file(path, binding_obj = nil)
32
+ _json_file(:policy, path, binding_obj)
33
+ end
34
+
35
+ def self._fetch_remote_resources
36
+ AwsClients
37
+ .iam
38
+ .list_roles
39
+ .roles
40
+ .map(&:to_h)
41
+ .map { |role| _get_role_policies(role) }
42
+ .flatten
43
+ .compact
44
+ .map { |role_policy| _get_policy(role_policy) }
45
+ end
46
+
47
+ def self._get_role_policies(role)
48
+ AwsClients
49
+ .iam
50
+ .list_role_policies({ role_name: role[:role_name] })
51
+ .map(&:policy_names)
52
+ .flatten
53
+ .map { |policy| { role_name: role[:role_name], policy_name: policy } }
54
+ end
55
+
56
+ def self._get_policy(role_policy)
57
+ AwsClients
58
+ .iam
59
+ .get_role_policy(role_policy)
60
+ .to_h
61
+ .merge({ _terraform_id: "#{role_policy[:role_name]}:#{role_policy[:policy_name]}" })
62
+ end
63
+ end
@@ -0,0 +1,40 @@
1
+ ########################################################################
2
+ # AwsKinesisStream is the +aws_kinesis_stream+ terrform resource,
3
+ #
4
+ # {https://www.terraform.io/docs/providers/aws/r/aws_kinesis_stream.html Terraform Docs}
5
+ ########################################################################
6
+ class GeoEngineer::Resources::AwsKinesisStream < GeoEngineer::Resource
7
+ validate -> { validate_required_attributes([:name, :shard_count]) }
8
+ validate -> { validate_has_tag(:Name) }
9
+
10
+ after :initialize, -> { _terraform_id -> { NullObject.maybe(remote_resource)._terraform_id } }
11
+ after :initialize, -> { _geo_id -> { self[:name] } }
12
+
13
+ def to_terraform_state
14
+ tfstate = super
15
+ tfstate[:primary][:attributes] = {
16
+ 'name' => self.name,
17
+ 'shard_count' => self.shard_count.to_s
18
+ }
19
+ tfstate
20
+ end
21
+
22
+ def self._all_streams
23
+ streams = []
24
+ AwsClients.kinesis.list_streams[:stream_names].each do |stream_name|
25
+ AwsClients.kinesis.describe_stream({ stream_name: stream_name }).map(&:to_h).map do |stream|
26
+ streams << stream[:stream_description]
27
+ end
28
+ end
29
+ streams
30
+ end
31
+
32
+ def self._fetch_remote_resources
33
+ self._all_streams.map do |stream|
34
+ stream.merge({
35
+ _terraform_id: stream[:stream_arn],
36
+ _geo_id: stream[:stream_name]
37
+ })
38
+ end
39
+ end
40
+ end
@@ -4,12 +4,18 @@
4
4
  # {https://www.terraform.io/docs/providers/aws/r/lambda_alias.html Terraform Docs}
5
5
  ########################################################################
6
6
  class GeoEngineer::Resources::AwsLambdaAlias < GeoEngineer::Resource
7
+ # Note: function_name here actually means function_arn, even though
8
+ # function_name is also a key on a lambda function.
7
9
  validate -> { validate_required_attributes([:name, :function_name, :function_version]) }
8
10
  validate -> {
9
- !(name =~ /(?!^[0-9]+$)([a-zA-Z0-9\-_]+)/).nil? if name
11
+ if name && (name =~ /(?!^[0-9]+$)([a-zA-Z0-9\-_]+)/).nil?
12
+ "#{name} must match: /(?!^[0-9]+$)([a-zA-Z0-9\-_]+)/"
13
+ end
10
14
  }
11
15
  validate -> {
12
- !(function_version =~ /(\$LATEST|[0-9]+)/).nil? if function_version
16
+ if function_version && (function_version =~ /(\$LATEST|[0-9]+)/).nil?
17
+ "#{function_version} must match: /(\$LATEST|[0-9]+)/"
18
+ end
13
19
  }
14
20
 
15
21
  after :initialize, -> { _terraform_id -> { NullObject.maybe(remote_resource)._terraform_id } }
@@ -19,6 +25,15 @@ class GeoEngineer::Resources::AwsLambdaAlias < GeoEngineer::Resource
19
25
  false
20
26
  end
21
27
 
28
+ def to_terraform_state
29
+ tfstate = super
30
+ tfstate[:primary][:attributes] = {
31
+ 'function_name' => function_name,
32
+ 'name' => name
33
+ }
34
+ tfstate
35
+ end
36
+
22
37
  # TODO(Brad) - May need to implement solution for pagination...
23
38
  def self._fetch_functions
24
39
  AwsClients
@@ -31,7 +46,7 @@ class GeoEngineer::Resources::AwsLambdaAlias < GeoEngineer::Resource
31
46
  def self._fetch_aliases(function)
32
47
  options = { function_name: function[:function_name] }
33
48
  AwsClients.lambda.list_aliases(options)[:aliases].map(&:to_h).map do |f_alias|
34
- geo_id_components = [f_alias[:name], f_alias[:function_name], f_alias[:function_version]]
49
+ geo_id_components = [f_alias[:name], function[:function_arn], f_alias[:function_version]]
35
50
  f_alias.merge(
36
51
  {
37
52
  _terraform_id: f_alias[:alias_arn],