gclouder 0.1.1

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 (58) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +104 -0
  3. data/bin/gclouder +7 -0
  4. data/lib/gclouder/config/arguments.rb +35 -0
  5. data/lib/gclouder/config/cli_args.rb +77 -0
  6. data/lib/gclouder/config/cluster.rb +66 -0
  7. data/lib/gclouder/config/defaults.rb +35 -0
  8. data/lib/gclouder/config/files/project.rb +24 -0
  9. data/lib/gclouder/config/project.rb +34 -0
  10. data/lib/gclouder/config/resource_representations.rb +31 -0
  11. data/lib/gclouder/config_loader.rb +25 -0
  12. data/lib/gclouder/config_section.rb +26 -0
  13. data/lib/gclouder/dependencies.rb +11 -0
  14. data/lib/gclouder/gcloud.rb +39 -0
  15. data/lib/gclouder/gsutil.rb +25 -0
  16. data/lib/gclouder/header.rb +9 -0
  17. data/lib/gclouder/helpers.rb +77 -0
  18. data/lib/gclouder/logging.rb +181 -0
  19. data/lib/gclouder/mappings/argument.rb +31 -0
  20. data/lib/gclouder/mappings/file.rb +31 -0
  21. data/lib/gclouder/mappings/property.rb +31 -0
  22. data/lib/gclouder/mappings/resource_representation.rb +31 -0
  23. data/lib/gclouder/monkey_patches/array.rb +19 -0
  24. data/lib/gclouder/monkey_patches/boolean.rb +12 -0
  25. data/lib/gclouder/monkey_patches/hash.rb +44 -0
  26. data/lib/gclouder/monkey_patches/ipaddr.rb +10 -0
  27. data/lib/gclouder/monkey_patches/string.rb +30 -0
  28. data/lib/gclouder/resource.rb +63 -0
  29. data/lib/gclouder/resource_cleaner.rb +58 -0
  30. data/lib/gclouder/resources/compute/addresses.rb +108 -0
  31. data/lib/gclouder/resources/compute/bgp-vpns.rb +220 -0
  32. data/lib/gclouder/resources/compute/disks.rb +99 -0
  33. data/lib/gclouder/resources/compute/firewall_rules.rb +82 -0
  34. data/lib/gclouder/resources/compute/instances.rb +147 -0
  35. data/lib/gclouder/resources/compute/networks/subnets.rb +104 -0
  36. data/lib/gclouder/resources/compute/networks.rb +110 -0
  37. data/lib/gclouder/resources/compute/project_info/ssh_keys.rb +171 -0
  38. data/lib/gclouder/resources/compute/routers.rb +83 -0
  39. data/lib/gclouder/resources/compute/vpns.rb +199 -0
  40. data/lib/gclouder/resources/container/clusters.rb +257 -0
  41. data/lib/gclouder/resources/container/node_pools.rb +193 -0
  42. data/lib/gclouder/resources/dns.rb +390 -0
  43. data/lib/gclouder/resources/logging/sinks.rb +98 -0
  44. data/lib/gclouder/resources/project/iam_policy_binding.rb +293 -0
  45. data/lib/gclouder/resources/project.rb +85 -0
  46. data/lib/gclouder/resources/project_id.rb +71 -0
  47. data/lib/gclouder/resources/pubsub/subscriptions.rb +100 -0
  48. data/lib/gclouder/resources/pubsub/topics.rb +95 -0
  49. data/lib/gclouder/resources/storagebuckets.rb +103 -0
  50. data/lib/gclouder/resources/validate/global.rb +27 -0
  51. data/lib/gclouder/resources/validate/local.rb +68 -0
  52. data/lib/gclouder/resources/validate/region.rb +28 -0
  53. data/lib/gclouder/resources/validate/remote.rb +78 -0
  54. data/lib/gclouder/resources.rb +148 -0
  55. data/lib/gclouder/shell.rb +71 -0
  56. data/lib/gclouder/version.rb +5 -0
  57. data/lib/gclouder.rb +278 -0
  58. metadata +102 -0
@@ -0,0 +1,220 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ module GClouder
4
+ module Resources
5
+ module Compute
6
+ module BGPVPNs
7
+ include GClouder::GCloud
8
+ include GClouder::Shell
9
+ include GClouder::Logging
10
+ include GClouder::Config::Project
11
+ include GClouder::Config::CLIArgs
12
+ include GClouder::Resource::Cleaner
13
+
14
+ module Cleaner
15
+ def self.custom
16
+ Proc.new do |local_resources, remote_resource|
17
+ local_resources.select { |r| "bgp-vpn-#{r['name']}" == remote_resource }.length > 0
18
+ end
19
+ end
20
+ end
21
+
22
+ def self.header(stage = :ensure)
23
+ info "[#{stage}] compute / bgp-vpns", indent: 1, title: true
24
+ end
25
+
26
+ def self.validate
27
+ return if Local.list.empty?
28
+ header :validate
29
+ Local.validate
30
+ end
31
+
32
+ def self.ensure
33
+ return if Local.list.empty?
34
+
35
+ header
36
+
37
+ Local.list.each do |region, instances|
38
+ info region, indent: 2, heading: true
39
+
40
+ instances.each do |vpn|
41
+ set_shared_secret(region, vpn)
42
+ info
43
+ BGPVPN.create(region, vpn)
44
+ end
45
+ end
46
+ end
47
+
48
+ def self.dir
49
+ cli_args[:keys_dir] || File.join(ENV["HOME"], "keys")
50
+ end
51
+
52
+ def self.set_shared_secret(region, vpn)
53
+ # if 'shared_secret' key is set, use it
54
+ # if not, fall back to trying to read the secret from an environment variable, the name
55
+ # of which is provided by the 'shared_secret_env_var' key
56
+ unless vpn.key?("shared_secret") || vpn.key?("shared_secret_env_var") || vpn.key?("shared_secret_file")
57
+ if cli_args[:dry_run]
58
+ warning "no shared secret found for VPN"
59
+ else
60
+ fatal "shared_secret_env_var or shared_secret must be set for region/vpn: #{region}/#{vpn["name"]}"
61
+ return false
62
+ end
63
+ end
64
+
65
+ vpn["shared_secret"] = if vpn.key?("shared_secret") && !vpn["shared_secret"].empty? && !vpn["shared_secret"].nil?
66
+ vpn["shared_secret"]
67
+ else
68
+ ENV[vpn["shared_secret_env_var"]] if vpn["shared_secret_env_var"]
69
+ end
70
+
71
+ # this overrides the above for now..
72
+ if vpn.key?("shared_secret_file")
73
+ config_file = File.join(dir, vpn["shared_secret_file"])
74
+
75
+ if !File.exists?(config_file)
76
+ fatal "shared_secret_file specified for vpn but no file found for region/vpn: #{region}/#{vpn["name"]}"
77
+ end
78
+
79
+ vpn["shared_secret"] = File.read(config_file)
80
+ end
81
+ end
82
+
83
+ module Local
84
+ def self.list
85
+ Resources::Region.instances(
86
+ path: %w{bgp-vpns}
87
+ ).delete_if { |_k, v| v.empty? }
88
+ end
89
+
90
+ def self.validate
91
+ # FIXME: better validation
92
+ Resources::Validate::Region.instances(
93
+ list,
94
+ required_keys: GClouder::Config::Arguments.required(["compute", "vpn-tunnels"]),
95
+ permitted_keys: GClouder::Config::Arguments.permitted(["compute", "vpn-tunnels"]),
96
+ ignore_keys: ["ike_version", "shared_secret", "address", "target_vpn_gateway", "bgp", "shared_secret_file", "network"]
97
+ )
98
+ end
99
+ end
100
+
101
+ module Remote
102
+ def self.list
103
+ # FIXME: should we be listing tunnels or gateways?
104
+ Resources::Remote.instances(path: %w(compute target-vpn-gateways))
105
+ end
106
+ end
107
+
108
+ module BGPVPN
109
+ include GClouder::GCloud
110
+ include GClouder::Shell
111
+ include GClouder::Logging
112
+ include GClouder::Helpers
113
+ include GClouder::Config::CLIArgs
114
+
115
+ def self.vpn_address(region, vpn)
116
+ response = gcloud("--format json compute addresses describe #{vpn['address']} --region=#{region}", force: true)
117
+
118
+ unless response.key?("address")
119
+ fatal "could not find address for static ip with key: #{vpn['address']} (is key allocated in project config?)"
120
+ end
121
+
122
+ response["address"]
123
+ end
124
+
125
+ def self.create(region, vpn)
126
+ network = vpn['network']
127
+
128
+ info "#{vpn['name']} (bgp-vpn-#{vpn['name']})", indent: 3
129
+
130
+ # check to see if router exists, if it doesn't then assume we need to create interface and bgp peer
131
+ configure_router = !Resource.resource?("compute routers", "bgp-vpn-#{vpn['name']}", silent: true)
132
+
133
+ # router
134
+ Resource.ensure :"compute routers",
135
+ "bgp-vpn-#{vpn['name']}",
136
+ "--region #{region} \
137
+ --network #{network} \
138
+ --asn #{vpn['bgp']['local_asn']}",
139
+ extra_info: "(router)",
140
+ indent: 4
141
+
142
+ # VPN gateway
143
+ Resource.ensure :"compute target-vpn-gateways",
144
+ "bgp-vpn-#{vpn["name"]}",
145
+ "--network #{network} \
146
+ --region #{region}",
147
+ extra_info: "(gateway)",
148
+ indent: 4
149
+
150
+ address = cli_args[:dry_run] ? "<automatic>" : vpn_address(region, vpn)
151
+
152
+ # forwarding rules
153
+ Resource.ensure :"compute forwarding-rules",
154
+ "bgp-vpn-#{vpn['name']}-esp",
155
+ "--region #{region} \
156
+ --ip-protocol ESP \
157
+ --address #{address} \
158
+ --target-vpn-gateway bgp-vpn-#{vpn['name']}",
159
+ extra_info: "(forwarding-rule)",
160
+ indent: 4
161
+
162
+ Resource.ensure :"compute forwarding-rules",
163
+ "bgp-vpn-#{vpn['name']}-udp500",
164
+ "--region #{region} \
165
+ --ip-protocol UDP \
166
+ --ports 500 \
167
+ --address #{address} \
168
+ --target-vpn-gateway bgp-vpn-#{vpn['name']}",
169
+ extra_info: "(forwarding-rule)",
170
+ indent: 4
171
+
172
+ Resource.ensure :"compute forwarding-rules",
173
+ "bgp-vpn-#{vpn['name']}-udp4500",
174
+ "--region #{region} --ip-protocol UDP --ports 4500 --address #{address} \
175
+ --target-vpn-gateway bgp-vpn-#{vpn['name']}",
176
+ extra_info: "(forwarding-rule)",
177
+ indent: 4
178
+
179
+ # tunnel
180
+ Resource.ensure :"compute vpn-tunnels", "bgp-vpn-#{vpn['name']}",
181
+ "--region #{region} \
182
+ --peer-address #{vpn['peer_address']} \
183
+ --ike-version #{vpn['ike_version']} \
184
+ --router bgp-vpn-#{vpn['name']} \
185
+ --target-vpn-gateway bgp-vpn-#{vpn['name']} \
186
+ --shared-secret #{vpn['shared_secret']}",
187
+ extra_info: "(tunnel)",
188
+ indent: 4
189
+
190
+ if configure_router
191
+ # router interface
192
+ gcloud("compute routers add-interface bgp-vpn-#{vpn['name']} \
193
+ --region #{region} \
194
+ --interface-name bgp-vpn-interface-#{vpn['name']} \
195
+ --vpn-tunnel bgp-vpn-#{vpn['name']} \
196
+ --mask-length #{vpn['bgp']['mask']} \
197
+ --ip-address #{vpn['bgp']['local_address']}",
198
+ failure: false)
199
+ add "bgp-vpn-#{vpn['name']} (router interface)", indent: 4
200
+
201
+ # bgp peer
202
+ gcloud("compute routers add-bgp-peer bgp-vpn-#{vpn['name']} \
203
+ --region #{region} \
204
+ --interface bgp-vpn-interface-#{vpn['name']} \
205
+ --advertised-route-priority #{vpn['bgp']['priority']} \
206
+ --peer-asn #{vpn['bgp']['peer_asn']} \
207
+ --peer-ip-address #{vpn['bgp']['peer_address']} \
208
+ --peer-name #{vpn['name']}",
209
+ failure: false)
210
+ add "bgp-vpn-#{vpn['name']} (bgp peer)", indent: 4
211
+ else
212
+ good "bgp-vpn-#{vpn['name']} (router interface)", indent: 4
213
+ good "bgp-vpn-#{vpn['name']} (bgp peer)", indent: 4
214
+ end
215
+ end
216
+ end
217
+ end
218
+ end
219
+ end
220
+ end
@@ -0,0 +1,99 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ module GClouder
4
+ module Resources
5
+ module Compute
6
+ module Disks
7
+ include GClouder::Config::CLIArgs
8
+ include GClouder::Config::Project
9
+ include GClouder::Logging
10
+ include GClouder::Config::Arguments
11
+ include GClouder::Resource::Cleaner
12
+
13
+ def self.header(stage = :ensure)
14
+ info "[#{stage}] compute / disks", title: true, indent: 1
15
+ end
16
+
17
+ def self.ensure
18
+ return if Local.list.empty?
19
+ header
20
+
21
+ Local.list.each do |region, disks|
22
+ next if disks.empty?
23
+ info region, indent: 2, heading: true
24
+ info
25
+ disks.each do |disk|
26
+ Disk.ensure(disk["name"], disk["zone"], disk["size"], disk["type"])
27
+ end
28
+ end
29
+ end
30
+
31
+ def self.check
32
+ Remote.list.each do |region, disks|
33
+ disks.each do |disk, config|
34
+ local_config = Local.list[region][disk]
35
+ next unless local_config
36
+ next if local_config == config
37
+ info "[compute disks] local resource definition differs from immutable remote resource: #{disk}"
38
+ info "# local config"
39
+ ap local_config
40
+ info "# remote config"
41
+ ap config
42
+ fatal ""
43
+ end
44
+ end
45
+ end
46
+
47
+ def self.validate
48
+ return if Local.list.empty?
49
+ header :validate
50
+ Local.validate
51
+ end
52
+
53
+ module Local
54
+ include GClouder::Logging
55
+
56
+ def self.section
57
+ ["compute", "disks"]
58
+ end
59
+
60
+ def self.list
61
+ Resources::Region.instances(path: %w(compute disks))
62
+ end
63
+
64
+ def self.validate
65
+ Resources::Validate::Region.instances(
66
+ list,
67
+ required_keys: GClouder::Config::Arguments.required(section),
68
+ permitted_keys: GClouder::Config::Arguments.permitted(section),
69
+ ignore_keys: ["size"]
70
+ )
71
+ end
72
+ end
73
+
74
+ module Remote
75
+ def self.list
76
+ vm_disk_pattern = GClouder::Resources::Compute::Instances::Local.instance_names.map{ |disk| "^#{disk}$" }.join("|")
77
+
78
+ Resources::Remote.instances(
79
+ path: ["compute", "disks"],
80
+ skip_instances: { "name" => /^gke|#{vm_disk_pattern}/ },
81
+ )
82
+ end
83
+ end
84
+
85
+ module Disk
86
+ include GClouder::Resource
87
+
88
+ def self.ensure(disk, zone, size, type)
89
+ Resource.ensure :"compute disks", disk, "--zone #{zone} --size #{size} --type #{type}"
90
+ end
91
+
92
+ def self.purge(disk, args)
93
+ Resource.purge :"compute disks", disk, args
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ module GClouder
4
+ module Resources
5
+ module Compute
6
+ module FirewallRules
7
+ include GClouder::Config::CLIArgs
8
+ include GClouder::Logging
9
+ include GClouder::GCloud
10
+ include GClouder::Resource::Cleaner
11
+
12
+ def self.header(stage = :ensure)
13
+ info "[#{stage}] compute / firewall-rules", indent: 1, title: true
14
+ end
15
+
16
+ def self.ensure
17
+ return if Local.list.empty?
18
+ header
19
+ Local.list.each do |region, rules|
20
+ info region, heading: true, indent: 2
21
+ info
22
+ rules.each do |rule|
23
+ Rule.ensure(rule["name"], rule)
24
+ end
25
+ end
26
+ end
27
+
28
+ def self.validate
29
+ return if Local.list.empty?
30
+ header :validate
31
+ Local.validate
32
+ end
33
+
34
+ def self.check
35
+ end
36
+
37
+ module Local
38
+ include GClouder::Config::Project
39
+ include GClouder::Logging
40
+
41
+ def self.validate
42
+ info "global", heading: true, indent: 2
43
+ Resources::Validate::Global.instances(
44
+ list,
45
+ required_keys: GClouder::Config::Arguments.required(%w(compute firewall-rules)),
46
+ permitted_keys: GClouder::Config::Arguments.permitted(%w(compute firewall-rules)),
47
+ ignore_keys: ["internal-icmp"]
48
+ )
49
+ end
50
+
51
+ def self.list
52
+ Resources::Global.instances(path: %w(firewall rules))
53
+ end
54
+ end
55
+
56
+ module Remote
57
+ def self.list
58
+ Resources::Remote.instances(
59
+ path: %w(compute firewall-rules),
60
+ ignore_keys: %w(self_link creation_timestamp id kind self_link),
61
+ skip_instances: { "name" => /^default-.*|^gke-.*|^k8s-fw-.*/, "network" => /^default$/ }
62
+ )
63
+ end
64
+ end
65
+
66
+ module Rule
67
+ include GClouder::Logging
68
+ include GClouder::Helpers
69
+ include GClouder::GCloud
70
+
71
+ def self.ensure(rule, args = {}, silent: false)
72
+ Resource.ensure :"compute firewall-rules", rule, hash_to_args(args), silent: silent
73
+ end
74
+
75
+ def self.purge(rule, silent: false)
76
+ Resource.purge :"compute firewall-rules", rule, silent: silent
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,147 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ module GClouder
4
+ module Resources
5
+ module Compute
6
+ module Instances
7
+ include GClouder::Helpers
8
+ include GClouder::Logging
9
+ include GClouder::Config::CLIArgs
10
+
11
+ def self.header(stage = :ensure)
12
+ info "[#{stage}] compute / instance", indent: 1, title: true
13
+ end
14
+
15
+ def self.validate
16
+ return if Local.list.empty?
17
+ header :validate
18
+ Local.validate
19
+ end
20
+
21
+ def self.ensure
22
+ return if Local.list.empty?
23
+ header
24
+
25
+ Local.list.each do |region, instances|
26
+ next if instances.empty?
27
+ info region, indent: 2, heading: true
28
+ info
29
+ instances.each do |instance|
30
+ Instance.ensure(instance["name"], hash_to_args(instance))
31
+ end
32
+ end
33
+ end
34
+
35
+ def self.check
36
+ return if Remote.list.empty?
37
+ return if Local.list.empty?
38
+ header :check
39
+
40
+ Resources::Validate::Remote.instances(
41
+ Local.manipulated,
42
+ Remote.list,
43
+ skip_keys: [
44
+ "image",
45
+ "zone",
46
+ "network_interfaces"
47
+ ]
48
+ )
49
+ end
50
+
51
+ def self.clean
52
+ return if undefined.empty?
53
+ header :clean
54
+ undefined.each do |instance, zone|
55
+ info zone, heading: true, indent: 2
56
+ info
57
+ warning "#{instance['name']} (not defined locally)"
58
+ #Instance.purge(instance["name"], "--zone=#{zone}")
59
+ end
60
+ end
61
+
62
+ def self.undefined
63
+ Remote.list.map do |region, instances|
64
+ return instances.map do |instance|
65
+ next if Local.list.fetch(region, []).select {|i| i["name"] == instance["name"] }.length > 0
66
+ zone = Resource::Find.zone(:"compute instances", instance["name"], region)
67
+ [instance, zone]
68
+ end.clean
69
+ end
70
+ end
71
+
72
+ module Local
73
+ include GClouder::Resources
74
+
75
+ def self.section
76
+ %w(compute instances)
77
+ end
78
+
79
+ def self.list
80
+ Resources::Region.instances(path: section)
81
+ end
82
+
83
+ def self.instance_names
84
+ list.map { |region, instances| instances.each.map { |instance| instance["name"] } }.flatten
85
+ end
86
+
87
+ def self.validate
88
+ Resources::Validate::Region.instances(
89
+ list,
90
+ required_keys: GClouder::Config::Arguments.required(section),
91
+ permitted_keys: GClouder::Config::Arguments.permitted(section),
92
+ )
93
+ end
94
+
95
+ def self.mappings
96
+ Mappings::Property.section(["compute::instances", "subnet"])
97
+ end
98
+
99
+ def self.create_from_mapping(mappings, value)
100
+ mappings.reverse.inject(value) { |obj, key| key.is_a?(Integer) ? [obj] : { key => obj } }
101
+ end
102
+
103
+ # manipulate local resources so they're comparable with remote..
104
+ #
105
+ # FIXME
106
+ # this could be automated:
107
+ # * iterate over compute::instaces
108
+ # * create key for each value
109
+ # * assign config[key] to newly made key
110
+ def self.manipulated
111
+ list.each do |_region, resources|
112
+ resources.each do |resource|
113
+ data_structure = create_from_mapping(mappings, resource["subnet"])
114
+ resource.merge! data_structure["compute"]["instances"]
115
+ resource.delete("subnet")
116
+ resource
117
+ end
118
+ end
119
+ end
120
+ end
121
+
122
+ module Remote
123
+ def self.list
124
+ get_arguments
125
+ end
126
+
127
+ def self.get_arguments
128
+ Resources::Remote.instances(
129
+ path: ["compute", "instances"],
130
+ skip_instances: { "name" => /^gke/, "status" => /^TERMINATED$/ }
131
+ )
132
+ end
133
+ end
134
+
135
+ module Instance
136
+ def self.ensure(instance, args = nil)
137
+ Resource.ensure :"compute instances", instance, args
138
+ end
139
+
140
+ def self.purge(instance, args = nil)
141
+ Resource.purge :"compute instances", instance, args
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ module GClouder
4
+ module Resources
5
+ module Compute
6
+ module Networks
7
+ module Subnets
8
+ include GClouder::Config::CLIArgs
9
+ include GClouder::Config::Project
10
+ include GClouder::Logging
11
+ include GClouder::Config::Arguments
12
+ include GClouder::Resource::Cleaner
13
+
14
+ def self.header(stage = :ensure)
15
+ info "[#{stage}] compute / network / subnet", title: true
16
+ end
17
+
18
+ def self.validate
19
+ return if Local.list.empty?
20
+ header :validate
21
+ Local.validate
22
+ end
23
+
24
+ def self.ensure
25
+ return if Local.list.empty?
26
+ header
27
+
28
+ Local.list.each do |region, subnets|
29
+ next if subnets.empty?
30
+ info region, heading: true, indent: 2
31
+ info
32
+ subnets.each do |subnet|
33
+ Subnet.ensure(region, subnet["network"], subnet["name"], subnet["range"])
34
+ end
35
+ end
36
+ end
37
+
38
+ def self.check
39
+ return if Remote.list.empty?
40
+ return if Local.list.empty?
41
+ header :check
42
+ Resources::Validate::Remote.instances(Local.list, Remote.list)
43
+ end
44
+
45
+ module Local
46
+ include GClouder::Logging
47
+
48
+ def self.section
49
+ ["compute", "networks", "subnets"]
50
+ end
51
+
52
+ def self.list
53
+ instances
54
+ end
55
+
56
+ def self.validate
57
+ Resources::Validate::Region.instances(
58
+ instances,
59
+ required_keys: GClouder::Config::Arguments.required(section),
60
+ permitted_keys: GClouder::Config::Arguments.permitted(section)
61
+ )
62
+ end
63
+
64
+ def self.instances
65
+ Resources::Region.instances(path: ["subnets"])
66
+ end
67
+
68
+ def self.networks
69
+ collection = { "global" => [] }
70
+ list.each { |_region, subnets| subnets.each { |subnet| collection["global"].push({ "name" => subnet["network"] }) } }
71
+ collection.delete_if { |_k, v| v.empty? }
72
+ end
73
+ end
74
+
75
+ module Remote
76
+ def self.list
77
+ get_arguments
78
+ end
79
+
80
+ def self.get_arguments
81
+ Resources::Remote.instances(
82
+ path: ["compute", "networks", "subnets"],
83
+ ignore_keys: ["ip_cidr_range", "region"],
84
+ skip_instances: { "network" => /^default$/ }
85
+ )
86
+ end
87
+ end
88
+
89
+ module Subnet
90
+ include GClouder::Resource
91
+
92
+ def self.ensure(region, network, subnet, range)
93
+ Resource.ensure :"compute networks subnets", subnet, "--network #{network} --range #{range} --region #{region}"
94
+ end
95
+
96
+ def self.purge(region, subnet)
97
+ Resource.purge :"compute networks subnets", subnet, "--region #{region}", indent: 3
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end