cloud-mu 2.0.0.pre.beta2 → 2.0.0.pre.beta3

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. data/Berksfile.lock +1 -1
  3. data/cloud-mu.gemspec +4 -3
  4. data/cookbooks/mu-master/templates/default/mu.rc.erb +2 -2
  5. data/cookbooks/mu-tools/files/default/Mu_CA.pem +18 -19
  6. data/cookbooks/mu-tools/recipes/rsyslog.rb +1 -1
  7. data/modules/mu/cleanup.rb +14 -1
  8. data/modules/mu/cloud.rb +40 -22
  9. data/modules/mu/clouds/aws/alarm.rb +6 -0
  10. data/modules/mu/clouds/aws/bucket.rb +29 -0
  11. data/modules/mu/clouds/aws/cache_cluster.rb +6 -0
  12. data/modules/mu/clouds/aws/container_cluster.rb +6 -0
  13. data/modules/mu/clouds/aws/database.rb +6 -0
  14. data/modules/mu/clouds/aws/dnszone.rb +6 -0
  15. data/modules/mu/clouds/aws/endpoint.rb +6 -0
  16. data/modules/mu/clouds/aws/firewall_rule.rb +6 -0
  17. data/modules/mu/clouds/aws/folder.rb +6 -0
  18. data/modules/mu/clouds/aws/function.rb +6 -0
  19. data/modules/mu/clouds/aws/group.rb +6 -0
  20. data/modules/mu/clouds/aws/loadbalancer.rb +6 -0
  21. data/modules/mu/clouds/aws/log.rb +6 -0
  22. data/modules/mu/clouds/aws/msg_queue.rb +6 -0
  23. data/modules/mu/clouds/aws/nosqldb.rb +6 -0
  24. data/modules/mu/clouds/aws/notifier.rb +6 -0
  25. data/modules/mu/clouds/aws/role.rb +97 -11
  26. data/modules/mu/clouds/aws/search_domain.rb +6 -0
  27. data/modules/mu/clouds/aws/server.rb +6 -0
  28. data/modules/mu/clouds/aws/server_pool.rb +6 -0
  29. data/modules/mu/clouds/aws/storage_pool.rb +6 -0
  30. data/modules/mu/clouds/aws/user.rb +6 -0
  31. data/modules/mu/clouds/aws/vpc.rb +25 -1
  32. data/modules/mu/clouds/google.rb +86 -16
  33. data/modules/mu/clouds/google/bucket.rb +78 -3
  34. data/modules/mu/clouds/google/container_cluster.rb +12 -0
  35. data/modules/mu/clouds/google/database.rb +15 -1
  36. data/modules/mu/clouds/google/firewall_rule.rb +18 -2
  37. data/modules/mu/clouds/google/folder.rb +183 -16
  38. data/modules/mu/clouds/google/group.rb +7 -1
  39. data/modules/mu/clouds/google/habitat.rb +139 -24
  40. data/modules/mu/clouds/google/loadbalancer.rb +26 -12
  41. data/modules/mu/clouds/google/server.rb +25 -10
  42. data/modules/mu/clouds/google/server_pool.rb +16 -3
  43. data/modules/mu/clouds/google/user.rb +7 -1
  44. data/modules/mu/clouds/google/vpc.rb +87 -76
  45. data/modules/mu/config.rb +12 -0
  46. data/modules/mu/config/bucket.rb +4 -0
  47. data/modules/mu/config/folder.rb +1 -0
  48. data/modules/mu/config/habitat.rb +1 -1
  49. data/modules/mu/config/role.rb +78 -34
  50. data/modules/mu/config/vpc.rb +1 -0
  51. data/modules/mu/groomers/chef.rb +1 -1
  52. data/modules/mu/kittens.rb +689 -283
  53. metadata +5 -4
@@ -19,6 +19,7 @@ module MU
19
19
  class ServerPool < MU::Cloud::ServerPool
20
20
 
21
21
  @deploy = nil
22
+ @project_id = nil
22
23
  @config = nil
23
24
  attr_reader :mu_name
24
25
  attr_reader :cloud_id
@@ -32,6 +33,11 @@ module MU
32
33
  @cloud_id ||= cloud_id
33
34
  if !mu_name.nil?
34
35
  @mu_name = mu_name
36
+ @config['project'] ||= MU::Cloud::Google.defaultProject(@config['credentials'])
37
+ if !@project_id
38
+ project = MU::Cloud::Google.projectLookup(@config['project'], @deploy, sibling_only: true, raise_on_fail: false)
39
+ @project_id = project.nil? ? @config['project'] : project.cloudobj.cloud_id
40
+ end
35
41
  elsif @config['scrub_mu_isms']
36
42
  @mu_name = @config['name']
37
43
  else
@@ -41,6 +47,7 @@ module MU
41
47
 
42
48
  # Called automatically by {MU::Deploy#createResources}
43
49
  def create
50
+ @project_id = MU::Cloud::Google.projectLookup(@config['project'], @deploy).cloudobj.cloud_id
44
51
  port_objs = []
45
52
 
46
53
  @config['named_ports'].each { |port_cfg|
@@ -95,7 +102,7 @@ module MU
95
102
 
96
103
  MU.log "Creating instance template #{@mu_name}", details: template_obj
97
104
  template = MU::Cloud::Google.compute(credentials: @config['credentials']).insert_instance_template(
98
- @config['project'],
105
+ @project_id,
99
106
  template_obj
100
107
  )
101
108
 
@@ -117,7 +124,7 @@ module MU
117
124
 
118
125
  MU.log "Creating region instance group manager #{@mu_name}", details: mgr_obj
119
126
  mgr = MU::Cloud::Google.compute(credentials: @config['credentials']).insert_region_instance_group_manager(
120
- @config['project'],
127
+ @project_id,
121
128
  @config['region'],
122
129
  mgr_obj
123
130
  )
@@ -143,7 +150,7 @@ module MU
143
150
 
144
151
  MU.log "Creating autoscaler policy #{@mu_name}", details: scaler_obj
145
152
  MU::Cloud::Google.compute(credentials: @config['credentials']).insert_region_autoscaler(
146
- @config['project'],
153
+ @project_id,
147
154
  @config['region'],
148
155
  scaler_obj
149
156
  )
@@ -250,6 +257,12 @@ module MU
250
257
  false
251
258
  end
252
259
 
260
+ # Denote whether this resource implementation is experiment, ready for
261
+ # testing, or ready for production use.
262
+ def self.quality
263
+ MU::Cloud::RELEASE
264
+ end
265
+
253
266
  # Remove all autoscale groups associated with the currently loaded deployment.
254
267
  # @param noop [Boolean]: If true, will only print what would be done
255
268
  # @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
@@ -110,6 +110,12 @@ module MU
110
110
  true
111
111
  end
112
112
 
113
+ # Denote whether this resource implementation is experiment, ready for
114
+ # testing, or ready for production use.
115
+ def self.quality
116
+ MU::Cloud::ALPHA
117
+ end
118
+
113
119
  # Remove all users associated with the currently loaded deployment.
114
120
  # @param noop [Boolean]: If true, will only print what would be done
115
121
  # @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
@@ -142,7 +148,7 @@ module MU
142
148
  # @param region [String]: The cloud provider region.
143
149
  # @param flags [Hash]: Optional flags
144
150
  # @return [OpenStruct]: The cloud provider's complete descriptions of matching user group.
145
- def self.find(cloud_id: nil, region: MU.curRegion, credentials: nil, flags: {})
151
+ def self.find(cloud_id: nil, region: MU.curRegion, credentials: nil, flags: {}, tag_key: nil, tag_value: nil)
146
152
  flags["project"] ||= MU::Cloud::Google.defaultProject(credentials)
147
153
  found = nil
148
154
  resp = MU::Cloud::Google.iam(credentials: credentials).list_project_service_accounts(
@@ -21,6 +21,7 @@ module MU
21
21
 
22
22
  @deploy = nil
23
23
  @config = nil
24
+ @project_id = nil
24
25
  attr_reader :mu_name
25
26
  attr_reader :cloud_id
26
27
  attr_reader :url
@@ -45,6 +46,11 @@ module MU
45
46
  if @cloud_id.nil? or @cloud_id.empty?
46
47
  @cloud_id = MU::Cloud::Google.nameStr(@mu_name)
47
48
  end
49
+ @config['project'] ||= MU::Cloud::Google.defaultProject(@config['credentials'])
50
+ if !@project_id
51
+ project = MU::Cloud::Google.projectLookup(@config['project'], @deploy, sibling_only: true, raise_on_fail: false)
52
+ @project_id = project.nil? ? @config['project'] : project.cloudobj.cloud_id
53
+ end
48
54
  loadSubnets
49
55
  elsif @config['scrub_mu_isms']
50
56
  @mu_name = @config['name']
@@ -56,14 +62,17 @@ module MU
56
62
 
57
63
  # Called automatically by {MU::Deploy#createResources}
58
64
  def create
65
+ @project_id = MU::Cloud::Google.projectLookup(@config['project'], @deploy).cloudobj.cloud_id
66
+
59
67
  networkobj = MU::Cloud::Google.compute(:Network).new(
60
68
  name: MU::Cloud::Google.nameStr(@mu_name),
61
69
  description: @deploy.deploy_id,
62
70
  auto_create_subnetworks: false
63
71
  # i_pv4_range: @config['ip_block']
64
72
  )
65
- MU.log "Creating network #{@mu_name} (#{@config['ip_block']}) in project #{@config['project']}", details: networkobj
66
- resp = MU::Cloud::Google.compute(credentials: @config['credentials']).insert_network(@config['project'], networkobj)
73
+ MU.log "Creating network #{@mu_name} (#{@config['ip_block']}) in project #{@project_id}", details: networkobj
74
+
75
+ resp = MU::Cloud::Google.compute(credentials: @config['credentials']).insert_network(@project_id, networkobj)
67
76
  @url = resp.self_link # XXX needs to go in notify
68
77
  @cloud_id = resp.name
69
78
 
@@ -75,7 +84,7 @@ module MU
75
84
  MU.dupGlobals(parent_thread_id)
76
85
  subnet_name = @config['name']+"-"+subnet['name']
77
86
  subnet_mu_name = MU::Cloud::Google.nameStr(@deploy.getResourceName(subnet_name))
78
- MU.log "Creating subnetwork #{subnet_mu_name} (#{subnet['ip_block']}) in project #{@config['project']}", details: subnet
87
+ MU.log "Creating subnetwork #{subnet_mu_name} (#{subnet['ip_block']}) in project #{@project_id}", details: subnet
79
88
  subnetobj = MU::Cloud::Google.compute(:Subnetwork).new(
80
89
  name: subnet_mu_name,
81
90
  description: @deploy.deploy_id,
@@ -83,7 +92,7 @@ module MU
83
92
  network: @url,
84
93
  region: subnet['availability_zone']
85
94
  )
86
- resp = MU::Cloud::Google.compute(credentials: @config['credentials']).insert_subnetwork(@config['project'], subnet['availability_zone'], subnetobj)
95
+ resp = MU::Cloud::Google.compute(credentials: @config['credentials']).insert_subnetwork(@project_id, subnet['availability_zone'], subnetobj)
87
96
 
88
97
  }
89
98
  }
@@ -120,21 +129,16 @@ module MU
120
129
  def notify
121
130
  base = MU.structToHash(cloud_desc)
122
131
  base["cloud_id"] = @cloud_id
132
+ base["project_id"] = @project_id
123
133
  base.merge!(@config.to_h)
124
- if @config['name'] == "gkeprivate"
125
- pp base.keys
126
- puts base['cloud_id']
127
- end
128
-
129
134
  base
130
135
  end
131
136
 
132
137
  # Describe this VPC from the cloud platform's perspective
133
138
  # @return [Hash]
134
139
  def cloud_desc
135
- @config['project'] ||= MU::Cloud::Google.defaultProject(@config['credentials'])
136
140
 
137
- resp = MU::Cloud::Google.compute(credentials: @config['credentials']).get_network(@config['project'], @cloud_id)
141
+ resp = MU::Cloud::Google.compute(credentials: @config['credentials']).get_network(@project_id, @cloud_id)
138
142
  if @cloud_id.nil? or @cloud_id == ""
139
143
  MU.log "Couldn't describe #{self}, @cloud_id #{@cloud_id.nil? ? "undefined" : "empty" }", MU::ERR
140
144
  return nil
@@ -143,7 +147,7 @@ end
143
147
  resp = resp.to_h
144
148
  @url ||= resp[:self_link]
145
149
  routes = MU::Cloud::Google.compute(credentials: @config['credentials']).list_routes(
146
- @config['project'],
150
+ @project_id,
147
151
  filter: "network eq #{@cloud_id}"
148
152
  ).items
149
153
  resp[:routes] = routes.map { |r| r.to_h } if routes
@@ -154,6 +158,8 @@ end
154
158
 
155
159
  # Called automatically by {MU::Deploy#createResources}
156
160
  def groom
161
+ @project_id = MU::Cloud::Google.projectLookup(@config['project'], @deploy).cloudobj.cloud_id
162
+
157
163
  rtb = @config['route_tables'].first
158
164
 
159
165
  rtb['routes'].each { |route|
@@ -169,21 +175,6 @@ end
169
175
  @config['peers'].each { |peer|
170
176
  if peer['vpc']['vpc_name']
171
177
  peer_obj = @deploy.findLitterMate(name: peer['vpc']['vpc_name'], type: "vpcs")
172
- if peer_obj
173
- if peer_obj.config['peers']
174
- skipme = false
175
- peer_obj.config['peers'].each { |peerpeer|
176
- if peerpeer['vpc']['vpc_name'] == @config['name'] and
177
- (peer['vpc']['vpc_name'] <=> @config['name']) == -1
178
- skipme = true
179
- MU.log "VPCs #{peer['vpc']['vpc_name']} and #{@config['name']} both declare mutual peering connection, ignoring #{@config['name']}'s redundant declaration", MU::DEBUG
180
- # XXX and if deploy_id matches or is unset
181
- end
182
- }
183
- next if skipme
184
- end
185
- end
186
-
187
178
  else
188
179
  tag_key, tag_value = peer['vpc']['tag'].split(/=/, 2) if !peer['vpc']['tag'].nil?
189
180
  if peer['vpc']['deploy_id'].nil? and peer['vpc']['vpc_id'].nil? and tag_key.nil?
@@ -219,13 +210,20 @@ end
219
210
  peer_network: url
220
211
  )
221
212
 
222
- MU.log "Peering #{@url} with #{url}, connection name is #{cnxn_name}", details: peerreq
223
-
224
- MU::Cloud::Google.compute(credentials: @config['credentials']).add_network_peering(
225
- @config['project'],
226
- @cloud_id,
227
- peerreq
228
- )
213
+ begin
214
+ MU.log "Peering #{@cloud_id} with #{peer_obj.cloudobj.cloud_id}, connection name is #{cnxn_name}", details: peerreq
215
+ MU::Cloud::Google.compute(credentials: @config['credentials']).add_network_peering(
216
+ @project_id,
217
+ @cloud_id,
218
+ peerreq
219
+ )
220
+ rescue ::Google::Apis::ClientError => e
221
+ if e.message.match(/operation in progress on the local or peer network/)
222
+ MU.log e.message, MU::DEBUG, details: peerreq
223
+ sleep 10
224
+ retry
225
+ end
226
+ end
229
227
  count += 1
230
228
  }
231
229
  end
@@ -293,7 +291,7 @@ end
293
291
  resp = nil
294
292
  MU::Cloud::Google.listRegions(@config['us_only']).each { |r|
295
293
  resp = MU::Cloud::Google.compute(credentials: @config['credentials']).list_subnetworks(
296
- @config['project'],
294
+ @project_id,
297
295
  r,
298
296
  filter: "network eq #{network[:self_link]}"
299
297
  )
@@ -496,6 +494,12 @@ MU.log "ROUTES TO #{target_instance.name}", MU::WARN, details: resp
496
494
  true
497
495
  end
498
496
 
497
+ # Denote whether this resource implementation is experiment, ready for
498
+ # testing, or ready for production use.
499
+ def self.quality
500
+ MU::Cloud::RELEASE
501
+ end
502
+
499
503
  # Remove all VPC resources associated with the currently loaded deployment.
500
504
  # @param noop [Boolean]: If true, will only print what would be done
501
505
  # @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
@@ -543,6 +547,7 @@ MU.log "ROUTES TO #{target_instance.name}", MU::WARN, details: resp
543
547
  def self.validateConfig(vpc, configurator)
544
548
  ok = true
545
549
 
550
+
546
551
  if vpc['create_standard_subnets']
547
552
  # Manufacture some generic routes, if applicable.
548
553
  if !vpc['route_tables'] or vpc['route_tables'].empty?
@@ -557,46 +562,46 @@ MU.log "ROUTES TO #{target_instance.name}", MU::WARN, details: resp
557
562
  }
558
563
  ]
559
564
  end
560
-
561
- # Generate a set of subnets per route, if none are declared
562
- if !vpc['subnets'] or vpc['subnets'].empty?
563
- if vpc['regions'].nil? or vpc['regions'].empty?
564
- vpc['regions'] = MU::Cloud::Google.listRegions(vpc['us_only'])
565
- end
566
- blocks = configurator.divideNetwork(vpc['ip_block'], vpc['regions'].size*vpc['route_tables'].size, 29)
567
- ok = false if blocks.nil?
568
-
569
- vpc["subnets"] = []
570
- vpc['route_tables'].each { |t|
571
- count = 0
572
- vpc['regions'].each { |r|
573
- block = blocks.shift
574
- vpc["subnets"] << {
575
- "availability_zone" => r,
576
- "route_table" => t["name"],
577
- "ip_block" => block.to_s,
578
- "name" => "Subnet"+count.to_s+t["name"].capitalize,
579
- "map_public_ips" => true
565
+ else
566
+ # If create_standard_subnets is off, and no route_tables were
567
+ # declared at all, let's assume we want purely self-contained
568
+ # private VPC, and create a dummy route accordingly.
569
+ vpc['route_tables'] ||= [
570
+ {
571
+ "name" => "private",
572
+ "routes" => [
573
+ {
574
+ "destination_network" => "0.0.0.0/0"
580
575
  }
581
- count = count + 1
582
- }
576
+ ]
583
577
  }
584
- end
578
+ ]
585
579
  end
586
580
 
587
- # If create_standard_subnets is off, and no route_tables were
588
- # declared at all, let's assume we want purely self-contained
589
- # private VPCs
590
- vpc['route_tables'] ||= [
591
- {
592
- "name" => "private",
593
- "routes" => [
594
- {
595
- "destination_network" => "0.0.0.0/0"
581
+ # Generate a set of subnets per route, if none are declared
582
+ if !vpc['subnets'] or vpc['subnets'].empty?
583
+ if vpc['regions'].nil? or vpc['regions'].empty?
584
+ vpc['regions'] = MU::Cloud::Google.listRegions(vpc['us_only'])
585
+ end
586
+ blocks = configurator.divideNetwork(vpc['ip_block'], vpc['regions'].size*vpc['route_tables'].size, 29)
587
+ ok = false if blocks.nil?
588
+
589
+ vpc["subnets"] = []
590
+ vpc['route_tables'].each { |t|
591
+ count = 0
592
+ vpc['regions'].each { |r|
593
+ block = blocks.shift
594
+ vpc["subnets"] << {
595
+ "availability_zone" => r,
596
+ "route_table" => t["name"],
597
+ "ip_block" => block.to_s,
598
+ "name" => "Subnet"+count.to_s+t["name"].capitalize,
599
+ "map_public_ips" => true
596
600
  }
597
- ]
601
+ count = count + 1
602
+ }
598
603
  }
599
- ]
604
+ end
600
605
 
601
606
  # Google VPCs can't have routes that are anything other than global
602
607
  # (they can be tied to individual instances by tags, but w/e). So we
@@ -617,7 +622,8 @@ MU.log "ROUTES TO #{target_instance.name}", MU::WARN, details: resp
617
622
  "ip_block" => blocks.shift,
618
623
  "route_tables" => [tbl],
619
624
  "parent_block" => vpc['ip_block'],
620
- "subnets" => []
625
+ "subnets" => [],
626
+ "peers" => vpc['peers']
621
627
  }
622
628
  MU.log "Splitting VPC #{newvpc['name']} off from #{vpc['name']}", MU::NOTICE
623
629
 
@@ -626,11 +632,16 @@ MU.log "ROUTES TO #{target_instance.name}", MU::WARN, details: resp
626
632
  newvpc[key] = val
627
633
  }
628
634
  newvpc['peers'] ||= []
635
+ # Add the peer connections we're generating, in addition
629
636
  peernames.each { |peer|
630
- if peer != vpc['name']+"-"+tbl['name']
637
+ if peer != newvpc['name']
631
638
  newvpc['peers'] << { "vpc" => { "vpc_name" => peer } }
632
639
  end
633
640
  }
641
+ newvpc['peers'].reject! { |p|
642
+ p.values.first['vpc_name'] == newvpc['name'] or p.values.first['vpc_name'] == vpc['name']
643
+ }
644
+
634
645
  vpc["subnets"].each { |subnet|
635
646
  newvpc["subnets"] << subnet if subnet["route_table"] == tbl["name"]
636
647
  }
@@ -771,7 +782,7 @@ MU.log "ROUTES TO #{target_instance.name}", MU::WARN, details: resp
771
782
  # several other cases missing for various types of routers (raw IPs, instance ids, etc) XXX
772
783
  elsif route['gateway'] == "#DENY"
773
784
  resp = MU::Cloud::Google.compute(credentials: @config['credentials']).list_routes(
774
- @config['project'],
785
+ @project_id,
775
786
  filter: "network eq #{network}"
776
787
  )
777
788
 
@@ -779,7 +790,7 @@ MU.log "ROUTES TO #{target_instance.name}", MU::WARN, details: resp
779
790
  resp.items.each { |r|
780
791
  next if r.next_hop_gateway.nil? or !r.next_hop_gateway.match(/\/global\/gateways\/default-internet-gateway$/)
781
792
  MU.log "Removing standard route #{r.name} per our #DENY entry"
782
- MU::Cloud::Google.compute(credentials: @config['credentials']).delete_route(@config['project'], r.name)
793
+ MU::Cloud::Google.compute(credentials: @config['credentials']).delete_route(@project_id, r.name)
783
794
  }
784
795
  end
785
796
  elsif route['gateway'] == "#INTERNET"
@@ -796,11 +807,11 @@ MU.log "ROUTES TO #{target_instance.name}", MU::WARN, details: resp
796
807
 
797
808
  if route['gateway'] != "#DENY" and routeobj
798
809
  begin
799
- MU::Cloud::Google.compute(credentials: @config['credentials']).get_route(@config['project'], routename)
810
+ MU::Cloud::Google.compute(credentials: @config['credentials']).get_route(@project_id, routename)
800
811
  rescue ::Google::Apis::ClientError, MU::MuError => e
801
812
  if e.message.match(/notFound/)
802
- MU.log "Creating route #{routename} in project #{@config['project']}", details: routeobj
803
- resp = MU::Cloud::Google.compute(credentials: @config['credentials']).insert_route(@config['project'], routeobj)
813
+ MU.log "Creating route #{routename} in project #{@project_id}", details: routeobj
814
+ resp = MU::Cloud::Google.compute(credentials: @config['credentials']).insert_route(@project_id, routeobj)
804
815
  else
805
816
  # TODO can't update GCP routes, would have to delete and re-create
806
817
  end
@@ -156,6 +156,8 @@ module MU
156
156
  end
157
157
  res_class = Object.const_get("MU").const_get("Cloud").const_get(cloud).const_get(classname)
158
158
  required, res_schema = res_class.schema(self)
159
+ docschema["properties"][attrs[:cfg_plural]]["items"]["description"] ||= ""
160
+ docschema["properties"][attrs[:cfg_plural]]["items"]["description"] += "\n#\n# `#{cloud}`: "+res_class.quality
159
161
  res_schema.each { |key, cfg|
160
162
  if !docschema["properties"][attrs[:cfg_plural]]["items"]["properties"][key]
161
163
  only_children[attrs[:cfg_plural]] ||= {}
@@ -888,6 +890,16 @@ module MU
888
890
  end
889
891
  end
890
892
 
893
+ if descriptor['project']
894
+ if haveLitterMate?(descriptor['project'], "habitats")
895
+ descriptor['dependencies'] ||= []
896
+ descriptor['dependencies'] << {
897
+ "type" => "habitat",
898
+ "name" => descriptor['project']
899
+ }
900
+ end
901
+ end
902
+
891
903
  # Does this resource go in a VPC?
892
904
  if !descriptor["vpc"].nil? and !delay_validation
893
905
  descriptor['vpc']['cloud'] = descriptor['cloud']
@@ -49,6 +49,10 @@ module MU
49
49
  "type" => "string",
50
50
  "default" => "index.html",
51
51
  "description" => "If +web_enabled+, return this object when \"diretory\" (a path not ending in a key/object) is invoked."
52
+ },
53
+ "policies" => {
54
+ "type" => "array",
55
+ "items" => MU::Config::Role.policy_primitive(subobjects: true, grant_to: true, permissions_optional: true)
52
56
  }
53
57
  }
54
58
  }
@@ -26,6 +26,7 @@ module MU
26
26
  "description" => "Set up a cloud provider folder/OU for containing other account-level resources",
27
27
  "properties" => {
28
28
  "name" => { "type" => "string" },
29
+ "parent" => MU::Config::Folder.reference
29
30
  }
30
31
  }
31
32
  end
@@ -26,7 +26,7 @@ module MU
26
26
  "description" => "Generate a cloud habitat (AWS account, Google Cloud project, Azure Directory, etc)",
27
27
  "properties" => {
28
28
  "name" => { "type" => "string" },
29
- "folder" => MU::Config::Folder.reference
29
+ "parent" => MU::Config::Folder.reference
30
30
  }
31
31
  }
32
32
  end