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,293 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
module GClouder
|
4
|
+
module Resources
|
5
|
+
module Project
|
6
|
+
module IAMPolicyBinding
|
7
|
+
include GClouder::Config::Project
|
8
|
+
include GClouder::Logging
|
9
|
+
include GClouder::GCloud
|
10
|
+
include GClouder::Config::CLIArgs
|
11
|
+
|
12
|
+
def self.header(stage = :ensure)
|
13
|
+
info "[#{stage}] project / iam-policy-binding", indent: 1, title: true
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.clean
|
17
|
+
return if undefined.empty?
|
18
|
+
|
19
|
+
header :clean
|
20
|
+
|
21
|
+
undefined.each do |region, roles|
|
22
|
+
info region, indent: 2, heading: true
|
23
|
+
roles.each do |role|
|
24
|
+
info role["name"], indent: 3, heading: true
|
25
|
+
role["members"].each do |member|
|
26
|
+
message = member
|
27
|
+
message += " (not defined locally)"
|
28
|
+
warning message, indent: 4
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.unmanaged_service_account?(member)
|
35
|
+
is_service_account?(member) && !member.include?(project["project_id"])
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.is_service_account?(member)
|
39
|
+
member.include? "gserviceaccount.com"
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.undefined
|
43
|
+
# each remote role
|
44
|
+
Remote.list.each_with_object({}) do |(region, remote_roles), collection|
|
45
|
+
# each role, {owner, ...}
|
46
|
+
remote_roles.each do |remote_role|
|
47
|
+
role_found = false
|
48
|
+
|
49
|
+
next unless remote_role.key?("members")
|
50
|
+
|
51
|
+
# for each remote member
|
52
|
+
remote_role["members"].each do |remote_member|
|
53
|
+
|
54
|
+
next unless Local.list.key?("global")
|
55
|
+
|
56
|
+
# see if the members role exists locally
|
57
|
+
Local.list["global"].each do |e|
|
58
|
+
next unless e["name"] == remote_role["name"]
|
59
|
+
|
60
|
+
role_found = true
|
61
|
+
|
62
|
+
# if it does then check if member is in member list for role
|
63
|
+
|
64
|
+
# member is defined, so skip it
|
65
|
+
next if e["members"].include?(remote_member)
|
66
|
+
|
67
|
+
# member is one we don't want to manage, so skip it
|
68
|
+
next if unmanaged_service_account?(remote_member)
|
69
|
+
|
70
|
+
# member is undefined so add it to collection
|
71
|
+
|
72
|
+
collection["global"] ||= []
|
73
|
+
|
74
|
+
# add role if it doesn't exist in collection
|
75
|
+
if !resource?(collection["global"], remote_role["name"])
|
76
|
+
collection["global"] << { "name" => remote_role["name"], "members" => [] }
|
77
|
+
end
|
78
|
+
|
79
|
+
# add memeber to role
|
80
|
+
resource_array_append(collection["global"], remote_role["name"], "members", remote_member)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# if entire role is missing from local..
|
85
|
+
next if role_found
|
86
|
+
collection["global"] ||= []
|
87
|
+
collection["global"] << remote_role
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.resource?(resources, resource)
|
93
|
+
!resources.fetch_with_default("name", resource, {}).empty?
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.resource_array_append(resources, resource_name, resource_key, obj)
|
97
|
+
resource = resources.fetch_with_default("name", resource_name, {})
|
98
|
+
resource[resource_key] ||= []
|
99
|
+
resource[resource_key] << obj
|
100
|
+
end
|
101
|
+
|
102
|
+
def self.validate
|
103
|
+
return if Local.list.empty?
|
104
|
+
header :validate
|
105
|
+
|
106
|
+
failure = false
|
107
|
+
|
108
|
+
Local.list.each do |region, roles|
|
109
|
+
info region, indent: 2, heading: true
|
110
|
+
|
111
|
+
next if roles.empty?
|
112
|
+
|
113
|
+
roles.each do |role|
|
114
|
+
if !role.is_a?(Hash)
|
115
|
+
bad "role type is not a Hash: #{role}"
|
116
|
+
failure = true
|
117
|
+
next
|
118
|
+
end
|
119
|
+
|
120
|
+
if !role.key?("name")
|
121
|
+
bad "missing key: name"
|
122
|
+
failure = true
|
123
|
+
next
|
124
|
+
end
|
125
|
+
|
126
|
+
if !role.key?("members")
|
127
|
+
bad "missing key: members"
|
128
|
+
failure = true
|
129
|
+
next
|
130
|
+
end
|
131
|
+
|
132
|
+
info role["name"], indent: 3, heading: true
|
133
|
+
|
134
|
+
unless role["members"].is_a?(Array)
|
135
|
+
bad "value not an array for key: #{role}", indent: 4
|
136
|
+
fatal "failure due to invalid config"
|
137
|
+
end
|
138
|
+
|
139
|
+
next unless role.key?("members")
|
140
|
+
|
141
|
+
role["members"].each do |member|
|
142
|
+
|
143
|
+
if !member.is_a?(String)
|
144
|
+
bad "member isn't a String: #{member}", indent: 3
|
145
|
+
failure = true
|
146
|
+
next
|
147
|
+
end
|
148
|
+
|
149
|
+
info member, indent: 4, heading: true
|
150
|
+
|
151
|
+
good "member is a String", indent: 5
|
152
|
+
|
153
|
+
case member
|
154
|
+
when /^user:/
|
155
|
+
good "member is a 'user'", indent: 5
|
156
|
+
when /^group:/
|
157
|
+
good "member is a 'group'", indent: 5
|
158
|
+
when /^serviceAccount:/
|
159
|
+
good "member is a 'serviceAccount'", indent: 5
|
160
|
+
when /^sink:/
|
161
|
+
good "member is a 'sink'", indent: 5
|
162
|
+
else
|
163
|
+
bad "member is an unknown type", indent: 5
|
164
|
+
failure = true
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
fatal "config validation failure" if failure
|
171
|
+
end
|
172
|
+
|
173
|
+
def self.sink(member)
|
174
|
+
gcloud("beta logging sinks describe #{member.gsub('sink:', '')} | jq -r .writer_identity", force: true).chomp
|
175
|
+
rescue
|
176
|
+
fatal "failed to lookup writer identity for sink: #{member}"
|
177
|
+
end
|
178
|
+
|
179
|
+
def self.ensure
|
180
|
+
return if Local.list.empty?
|
181
|
+
header
|
182
|
+
|
183
|
+
Local.list.each do |region, roles|
|
184
|
+
info region, indent: 2, heading: true
|
185
|
+
|
186
|
+
roles.each do |role|
|
187
|
+
info role["name"], indent: 3, heading: true
|
188
|
+
|
189
|
+
role["members"].each do |member|
|
190
|
+
if member.start_with?("sink:")
|
191
|
+
sink_name = member
|
192
|
+
member = sink(member)
|
193
|
+
|
194
|
+
if member.empty? && cli_args[:dry_run]
|
195
|
+
add "unknown - serviceAccount does not exist [#{sink_name}]", indent: 4
|
196
|
+
next
|
197
|
+
elsif member.empty?
|
198
|
+
fatal "unable to find sink serviceAccount (writer identity) - does sink exist for name: #{sink_name}"
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
if policy_member?(project_id, role["name"], member)
|
203
|
+
good member, indent: 4
|
204
|
+
next
|
205
|
+
end
|
206
|
+
|
207
|
+
if project_owner?
|
208
|
+
add member, indent: 4
|
209
|
+
Binding.ensure(project_id, member, role["name"])
|
210
|
+
next
|
211
|
+
end
|
212
|
+
|
213
|
+
add "#{member} [skipping] (insufficient permissions to create user)", indent: 4
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
def self.policy_member?(project, role, member)
|
220
|
+
bindings = gcloud("--format json projects get-iam-policy #{project} | jq '.bindings[] | select(.role == \"roles/#{role}\")'", force: true)
|
221
|
+
return false if bindings.empty?
|
222
|
+
fatal "could not get policy bindings for project: #{project}" unless bindings.key?("members")
|
223
|
+
bindings["members"].include?(member)
|
224
|
+
end
|
225
|
+
|
226
|
+
def self.project_id
|
227
|
+
project["project_id"]
|
228
|
+
end
|
229
|
+
|
230
|
+
def self.executioner
|
231
|
+
GClouder::Project::ID.id
|
232
|
+
end
|
233
|
+
|
234
|
+
def self.executioner_formatted
|
235
|
+
"user:#{executioner.strip}"
|
236
|
+
end
|
237
|
+
|
238
|
+
def self.project_owner?
|
239
|
+
return false unless executioner
|
240
|
+
policy_member?(project_id, "owner", executioner_formatted)
|
241
|
+
end
|
242
|
+
|
243
|
+
module Local
|
244
|
+
include GClouder::GCloud
|
245
|
+
include GClouder::Config::Project
|
246
|
+
include GClouder::Logging
|
247
|
+
|
248
|
+
def self.list
|
249
|
+
return {} unless project.key?("iam")
|
250
|
+
{ "global" => project["iam"] }
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
module Remote
|
255
|
+
include GClouder::GCloud
|
256
|
+
include GClouder::Config::Project
|
257
|
+
include GClouder::Logging
|
258
|
+
|
259
|
+
def self.list
|
260
|
+
resources.each_with_object({ "global" => [] }) do |data, collection|
|
261
|
+
data["name"] = data["role"].gsub("roles/", "")
|
262
|
+
data.delete("role")
|
263
|
+
collection["global"] << data
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
def self.resources
|
268
|
+
gcloud("--format json projects get-iam-policy #{project_id} | jq .bindings", force: true)
|
269
|
+
end
|
270
|
+
|
271
|
+
def self.policy_member?(project, role, member)
|
272
|
+
bindings = gcloud("--format json projects get-iam-policy #{project} | jq '.bindings[] | select(.role == \"roles/#{role}\")'", force: true)
|
273
|
+
return false if bindings.empty?
|
274
|
+
fatal "could not get policy bindings for project: #{project}" unless bindings.key?("members")
|
275
|
+
bindings["members"].include?(member["name"])
|
276
|
+
end
|
277
|
+
|
278
|
+
def self.project_id
|
279
|
+
project["project_id"]
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
module Binding
|
284
|
+
include GClouder::GCloud
|
285
|
+
|
286
|
+
def self.ensure(project_id, name, role)
|
287
|
+
gcloud("projects add-iam-policy-binding #{project_id} --member='#{name}' --role='roles/#{role}'")
|
288
|
+
end
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
292
|
+
end
|
293
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
module GClouder
|
4
|
+
module Resources
|
5
|
+
module Project
|
6
|
+
include GClouder::Logging
|
7
|
+
include GClouder::Config::Project
|
8
|
+
include GClouder::GCloud
|
9
|
+
|
10
|
+
def self.header(stage = :ensure)
|
11
|
+
info "[#{stage}] project", title: true
|
12
|
+
info
|
13
|
+
end
|
14
|
+
|
15
|
+
# unprivileged exists? method for use by non-billing accounts
|
16
|
+
def self.exists?
|
17
|
+
Resource.resource?("projects", project["project_id"], filter_key: "project_id", silent: true)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.update
|
21
|
+
header
|
22
|
+
Local.ensure
|
23
|
+
end
|
24
|
+
|
25
|
+
module Local
|
26
|
+
include GClouder::Config::Project
|
27
|
+
include GClouder::Logging
|
28
|
+
include GClouder::Shell
|
29
|
+
include GClouder::GCloud
|
30
|
+
include GClouder::Config::CLIArgs
|
31
|
+
|
32
|
+
def self.ensure
|
33
|
+
create_project
|
34
|
+
link_project_to_billing_account
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.create_project
|
38
|
+
if exists?
|
39
|
+
good project_id, indent: 2
|
40
|
+
return
|
41
|
+
end
|
42
|
+
|
43
|
+
# FIXME: wait for project to exist and apis be enabled before continuing..
|
44
|
+
# FIXME: enable compute engine api..
|
45
|
+
|
46
|
+
add project_id, indent: 2
|
47
|
+
gcloud("alpha projects create #{project_id} --enable-cloud-apis --name=#{project_id}")
|
48
|
+
|
49
|
+
# FIXME: billing account isn't listed until linked..
|
50
|
+
#sleep 0.5 until exists? unless cli_args[:dry_run]
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.link_project_to_billing_account
|
54
|
+
if linked_to_billing_account?
|
55
|
+
good "linked to billing account: #{account_id}", indent: 3
|
56
|
+
return
|
57
|
+
end
|
58
|
+
|
59
|
+
add "link to billing account: #{account_id}", indent: 3
|
60
|
+
gcloud("alpha billing accounts projects link #{project_id} --account-id=#{account_id}")
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.linked_to_billing_account?
|
64
|
+
project_data(project_id)["billingEnabled"]
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.exists?
|
68
|
+
! project_data(project_id).empty?
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.project_data(project)
|
72
|
+
shell("gcloud --format json alpha billing accounts projects list #{account_id} | jq '.[] | select(.projectId == \"#{project}\")'")
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.project_id
|
76
|
+
project["project_id"]
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.account_id
|
80
|
+
project["account_id"]
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
module GClouder
|
4
|
+
module Project
|
5
|
+
module ID
|
6
|
+
include GClouder::Config::Project
|
7
|
+
include GClouder::Config::CLIArgs
|
8
|
+
include GClouder::Shell
|
9
|
+
|
10
|
+
def self.id
|
11
|
+
@id
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.load
|
15
|
+
@id ||= current
|
16
|
+
switch(project["project_id"]) if @id.nil?
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.current
|
20
|
+
id = shell("gcloud auth list --format json | jq -r '.[] | select(.status == \"ACTIVE\") | .account'")
|
21
|
+
return id if !id.empty?
|
22
|
+
return if cli_args[:activate_service_accounts]
|
23
|
+
bail
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.bail
|
27
|
+
puts "not authenticated against API and --activate-service-accounts option not passed"
|
28
|
+
puts ""
|
29
|
+
puts "please either:"
|
30
|
+
puts ""
|
31
|
+
puts " run: gcloud auth login && gcloud auth application-default login"
|
32
|
+
puts ""
|
33
|
+
puts " or: specify --activate-service-accounts flag and make sure the relevant keys exist in the keys dir"
|
34
|
+
puts ""
|
35
|
+
exit 1
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.switch(project_id)
|
39
|
+
return unless project_id
|
40
|
+
if cli_args[:activate_service_accounts]
|
41
|
+
shell("gcloud --quiet auth activate-service-account --key-file #{key_file(project_id)}")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.rescue
|
46
|
+
if @id.nil?
|
47
|
+
shell("gcloud config unset account")
|
48
|
+
return
|
49
|
+
end
|
50
|
+
|
51
|
+
switch(@id)
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.reset
|
55
|
+
switch(project["project_id"])
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.default
|
59
|
+
shell("gcloud config set account #{project['project_id']}")
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.dir
|
63
|
+
cli_args[:keys_dir] || File.join(ENV["HOME"], "keys")
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.key_file(project_id)
|
67
|
+
File.join(dir, "gcloud-service-key-#{project_id}.json")
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
module GClouder
|
4
|
+
module Resources
|
5
|
+
module PubSub
|
6
|
+
module Subscriptions
|
7
|
+
include GClouder::Config::CLIArgs
|
8
|
+
include GClouder::Config::Project
|
9
|
+
include GClouder::Logging
|
10
|
+
include GClouder::Resource::Cleaner
|
11
|
+
|
12
|
+
def self.header(stage = :ensure)
|
13
|
+
info "[#{stage}] pub/sub / subscriptions", indent: 1, title: true
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.ensure
|
17
|
+
return if Local.list.empty?
|
18
|
+
header
|
19
|
+
|
20
|
+
Local.list.each do |region, subscriptions|
|
21
|
+
info region, indent: 2, heading: true
|
22
|
+
info
|
23
|
+
subscriptions.each do |subscription|
|
24
|
+
Subscription.ensure(subscription)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.validate
|
30
|
+
return if Local.list.empty?
|
31
|
+
header :validate
|
32
|
+
Local.validate
|
33
|
+
end
|
34
|
+
|
35
|
+
module Local
|
36
|
+
include GClouder::Config::CLIArgs
|
37
|
+
include GClouder::Config::Project
|
38
|
+
include GClouder::Logging
|
39
|
+
|
40
|
+
# FIXME: improve validation
|
41
|
+
def self.validate
|
42
|
+
return if list.empty?
|
43
|
+
|
44
|
+
failure = false
|
45
|
+
|
46
|
+
list.each do |region, subscriptions|
|
47
|
+
info region, indent: 2, heading: true
|
48
|
+
subscriptions.each do |subscription|
|
49
|
+
info subscription["name"], indent: 3, heading: true
|
50
|
+
if !subscription["name"].is_a?(String)
|
51
|
+
bad "#{subscription['name']} is incorrect type #{subscription['name'].class}, should be: String", indent: 4
|
52
|
+
failure = true
|
53
|
+
end
|
54
|
+
|
55
|
+
if cli_args[:debug] || !cli_args[:output_validation]
|
56
|
+
good "name is a String", indent: 4
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
fatal "\nerror: validation failure" if failure
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.list
|
65
|
+
GClouder::Resources::Global.instances(path: %w(pubsub subscriptions))
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
module Remote
|
70
|
+
def self.list
|
71
|
+
{ "global" => instances.fetch("global", []).map { |subscription| { "name" => subscription["subscription_id"] } } }.delete_if { |_k, v| v.empty? }
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.instances
|
75
|
+
Resources::Remote.instances(
|
76
|
+
path: %w(beta pubsub subscriptions)
|
77
|
+
)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
module Subscription
|
82
|
+
include GClouder::GCloud
|
83
|
+
include GClouder::Helpers
|
84
|
+
|
85
|
+
def self.args(subscription)
|
86
|
+
hash_to_args(subscription)
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.ensure(subscription)
|
90
|
+
Resource.ensure :"beta pubsub subscriptions", subscription["name"], args(subscription), filter_key: "subscriptionId", indent: 3
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.purge(subscription)
|
94
|
+
Resource.purge :"beta pubsub subscriptions", subscription["name"]
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
module GClouder
|
4
|
+
module Resources
|
5
|
+
module PubSub
|
6
|
+
module Topics
|
7
|
+
include GClouder::Config::CLIArgs
|
8
|
+
include GClouder::Config::Project
|
9
|
+
include GClouder::Logging
|
10
|
+
include GClouder::Resource::Cleaner
|
11
|
+
|
12
|
+
def self.header(stage = :ensure)
|
13
|
+
info "[#{stage}] pub/sub / topics", indent: 1, title: true
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.ensure
|
17
|
+
return if Local.list.empty?
|
18
|
+
header
|
19
|
+
|
20
|
+
Local.list.each do |region, topics|
|
21
|
+
info region, indent: 2, heading: true
|
22
|
+
info
|
23
|
+
topics.each do |topic|
|
24
|
+
Topic.ensure(topic["name"])
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.validate
|
30
|
+
return if Local.list.empty?
|
31
|
+
header :validate
|
32
|
+
Local.validate
|
33
|
+
end
|
34
|
+
|
35
|
+
module Local
|
36
|
+
include GClouder::Config::CLIArgs
|
37
|
+
include GClouder::Config::Project
|
38
|
+
include GClouder::Logging
|
39
|
+
|
40
|
+
# FIXME: improve validation
|
41
|
+
def self.validate
|
42
|
+
return if list.empty?
|
43
|
+
|
44
|
+
failure = false
|
45
|
+
|
46
|
+
list.each do |region, topics|
|
47
|
+
info region, indent: 2, heading: true
|
48
|
+
topics.each do |topic|
|
49
|
+
info topic["name"], indent: 3, heading: true
|
50
|
+
if !topic["name"].is_a?(String)
|
51
|
+
bad "#{topic['name']} is incorrect type #{topic['name'].class}, should be: String", indent: 4
|
52
|
+
failure = true
|
53
|
+
end
|
54
|
+
|
55
|
+
if cli_args[:debug] || !cli_args[:output_validation]
|
56
|
+
good "name is a String", indent: 4
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
fatal "\nerror: validation failure" if failure
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.list
|
65
|
+
GClouder::Resources::Global.instances(path: %w(pubsub topics))
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
module Remote
|
70
|
+
def self.list
|
71
|
+
{ "global" => instances.fetch("global", []).map { |topic| { "name" => topic["topic_id"] } } }.delete_if { |_k, v| v.empty? }
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.instances
|
75
|
+
Resources::Remote.instances(
|
76
|
+
path: %w(beta pubsub topics)
|
77
|
+
)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
module Topic
|
82
|
+
include GClouder::GCloud
|
83
|
+
|
84
|
+
def self.ensure(topic)
|
85
|
+
Resource.ensure :"beta pubsub topics", topic, filter_key: "topicId"
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.purge(topic)
|
89
|
+
Resource.purge :"beta pubsub topics", topic
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|