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.
- checksums.yaml +7 -0
- data/README.md +104 -0
- data/bin/gclouder +7 -0
- data/lib/gclouder/config/arguments.rb +35 -0
- data/lib/gclouder/config/cli_args.rb +77 -0
- data/lib/gclouder/config/cluster.rb +66 -0
- data/lib/gclouder/config/defaults.rb +35 -0
- data/lib/gclouder/config/files/project.rb +24 -0
- data/lib/gclouder/config/project.rb +34 -0
- data/lib/gclouder/config/resource_representations.rb +31 -0
- data/lib/gclouder/config_loader.rb +25 -0
- data/lib/gclouder/config_section.rb +26 -0
- data/lib/gclouder/dependencies.rb +11 -0
- data/lib/gclouder/gcloud.rb +39 -0
- data/lib/gclouder/gsutil.rb +25 -0
- data/lib/gclouder/header.rb +9 -0
- data/lib/gclouder/helpers.rb +77 -0
- data/lib/gclouder/logging.rb +181 -0
- data/lib/gclouder/mappings/argument.rb +31 -0
- data/lib/gclouder/mappings/file.rb +31 -0
- data/lib/gclouder/mappings/property.rb +31 -0
- data/lib/gclouder/mappings/resource_representation.rb +31 -0
- data/lib/gclouder/monkey_patches/array.rb +19 -0
- data/lib/gclouder/monkey_patches/boolean.rb +12 -0
- data/lib/gclouder/monkey_patches/hash.rb +44 -0
- data/lib/gclouder/monkey_patches/ipaddr.rb +10 -0
- data/lib/gclouder/monkey_patches/string.rb +30 -0
- data/lib/gclouder/resource.rb +63 -0
- data/lib/gclouder/resource_cleaner.rb +58 -0
- data/lib/gclouder/resources/compute/addresses.rb +108 -0
- data/lib/gclouder/resources/compute/bgp-vpns.rb +220 -0
- data/lib/gclouder/resources/compute/disks.rb +99 -0
- data/lib/gclouder/resources/compute/firewall_rules.rb +82 -0
- data/lib/gclouder/resources/compute/instances.rb +147 -0
- data/lib/gclouder/resources/compute/networks/subnets.rb +104 -0
- data/lib/gclouder/resources/compute/networks.rb +110 -0
- data/lib/gclouder/resources/compute/project_info/ssh_keys.rb +171 -0
- data/lib/gclouder/resources/compute/routers.rb +83 -0
- data/lib/gclouder/resources/compute/vpns.rb +199 -0
- data/lib/gclouder/resources/container/clusters.rb +257 -0
- data/lib/gclouder/resources/container/node_pools.rb +193 -0
- data/lib/gclouder/resources/dns.rb +390 -0
- data/lib/gclouder/resources/logging/sinks.rb +98 -0
- data/lib/gclouder/resources/project/iam_policy_binding.rb +293 -0
- data/lib/gclouder/resources/project.rb +85 -0
- data/lib/gclouder/resources/project_id.rb +71 -0
- data/lib/gclouder/resources/pubsub/subscriptions.rb +100 -0
- data/lib/gclouder/resources/pubsub/topics.rb +95 -0
- data/lib/gclouder/resources/storagebuckets.rb +103 -0
- data/lib/gclouder/resources/validate/global.rb +27 -0
- data/lib/gclouder/resources/validate/local.rb +68 -0
- data/lib/gclouder/resources/validate/region.rb +28 -0
- data/lib/gclouder/resources/validate/remote.rb +78 -0
- data/lib/gclouder/resources.rb +148 -0
- data/lib/gclouder/shell.rb +71 -0
- data/lib/gclouder/version.rb +5 -0
- data/lib/gclouder.rb +278 -0
- 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
|