altaire-siren 0.1.0 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -2
  3. data/Gemfile +0 -1
  4. data/Gemfile.lock +7 -1
  5. data/altaire-siren.gemspec +1 -0
  6. data/build-schema.rb +206 -0
  7. data/docs/docs/commands/song.md +3 -0
  8. data/fantasia-export.rb +16 -0
  9. data/lib/siren.rb +2 -0
  10. data/lib/siren/cli.rb +16 -1
  11. data/lib/siren/compose.rb +103 -0
  12. data/lib/siren/compose/service.rb +321 -0
  13. data/lib/siren/compose/types.rb +230 -0
  14. data/lib/siren/schemabuilder.rb +56 -0
  15. data/lib/siren/song.rb +78 -50
  16. data/lib/siren/stack.rb +6 -2
  17. data/lib/siren/version.rb +1 -1
  18. data/schemas/docker-compose.yaml +78 -0
  19. metadata +23 -47
  20. data/docs/site/404.html +0 -313
  21. data/docs/site/assets/fonts/font-awesome.css +0 -4
  22. data/docs/site/assets/fonts/material-icons.css +0 -13
  23. data/docs/site/assets/fonts/specimen/FontAwesome.ttf +0 -0
  24. data/docs/site/assets/fonts/specimen/FontAwesome.woff +0 -0
  25. data/docs/site/assets/fonts/specimen/FontAwesome.woff2 +0 -0
  26. data/docs/site/assets/fonts/specimen/MaterialIcons-Regular.ttf +0 -0
  27. data/docs/site/assets/fonts/specimen/MaterialIcons-Regular.woff +0 -0
  28. data/docs/site/assets/fonts/specimen/MaterialIcons-Regular.woff2 +0 -0
  29. data/docs/site/assets/images/favicon.png +0 -0
  30. data/docs/site/assets/images/icons/bitbucket.1b09e088.svg +0 -1
  31. data/docs/site/assets/images/icons/github.f0b8504a.svg +0 -1
  32. data/docs/site/assets/images/icons/gitlab.6dd19c00.svg +0 -1
  33. data/docs/site/assets/javascripts/application.c648116f.js +0 -6
  34. data/docs/site/assets/javascripts/lunr/lunr.da.js +0 -1
  35. data/docs/site/assets/javascripts/lunr/lunr.de.js +0 -1
  36. data/docs/site/assets/javascripts/lunr/lunr.du.js +0 -1
  37. data/docs/site/assets/javascripts/lunr/lunr.es.js +0 -1
  38. data/docs/site/assets/javascripts/lunr/lunr.fi.js +0 -1
  39. data/docs/site/assets/javascripts/lunr/lunr.fr.js +0 -1
  40. data/docs/site/assets/javascripts/lunr/lunr.hu.js +0 -1
  41. data/docs/site/assets/javascripts/lunr/lunr.it.js +0 -1
  42. data/docs/site/assets/javascripts/lunr/lunr.ja.js +0 -1
  43. data/docs/site/assets/javascripts/lunr/lunr.jp.js +0 -1
  44. data/docs/site/assets/javascripts/lunr/lunr.multi.js +0 -1
  45. data/docs/site/assets/javascripts/lunr/lunr.nl.js +0 -1
  46. data/docs/site/assets/javascripts/lunr/lunr.no.js +0 -1
  47. data/docs/site/assets/javascripts/lunr/lunr.pt.js +0 -1
  48. data/docs/site/assets/javascripts/lunr/lunr.ro.js +0 -1
  49. data/docs/site/assets/javascripts/lunr/lunr.ru.js +0 -1
  50. data/docs/site/assets/javascripts/lunr/lunr.stemmer.support.js +0 -1
  51. data/docs/site/assets/javascripts/lunr/lunr.sv.js +0 -1
  52. data/docs/site/assets/javascripts/lunr/lunr.th.js +0 -1
  53. data/docs/site/assets/javascripts/lunr/lunr.tr.js +0 -1
  54. data/docs/site/assets/javascripts/lunr/tinyseg.js +0 -1
  55. data/docs/site/assets/javascripts/lunr/wordcut.js +0 -1
  56. data/docs/site/assets/javascripts/modernizr.74668098.js +0 -1
  57. data/docs/site/assets/stylesheets/application-palette.a8b3c06d.css +0 -1
  58. data/docs/site/assets/stylesheets/application.30686662.css +0 -1
  59. data/docs/site/commands/crypto/index.html +0 -470
  60. data/docs/site/commands/song/index.html +0 -474
  61. data/docs/site/index.html +0 -348
  62. data/docs/site/search/search_index.json +0 -1
  63. data/docs/site/sitemap.xml +0 -18
  64. data/docs/site/sitemap.xml.gz +0 -0
@@ -0,0 +1,321 @@
1
+ require 'shellwords'
2
+
3
+
4
+ module Siren
5
+ class Compose
6
+
7
+ class Service < Struct.new(:compose, :name, :build, :command, :configs, :container_name, :credential_spec, :deploy, :dns, :dns_search, :entrypoint,
8
+ :environment, :expose, :extra_hosts, :healthcheck, :image, :init, :labels, :logging, :networks, :pid, :ports, :secrets, :stop_grace_period,
9
+ :stop_signal, :sysctls, :ulimits, :volumes, :domainname, :hostname, :ipc, :mac_address, :privileged, :read_only, :shm_size, :stdin_open, :tty,
10
+ :user, :working_dir)
11
+
12
+ def initialize (compose, data = {})
13
+ self.compose = compose
14
+ data.each do |key, value|
15
+ self.__send__("#{key}=", value)
16
+ end
17
+ self.deploy = deploy ? Deploy.new(deploy) : Deploy.new
18
+ self.ports ||= []
19
+ self.volumes ||= []
20
+ self.name = namify(self.name)
21
+ end
22
+
23
+ def ports= (ports)
24
+ ports = ports.map do |port|
25
+ if port.is_a?(String)
26
+ target, published, protocol = port.match(/^(\d+)?(?::(\d+))?(\/.+)?$/).to_a[1..-1]
27
+ else
28
+ target, published, protocol, mode = port.values_at("target", "published", "protocol", "mode")
29
+ end
30
+ protocol ||= "tcp"
31
+ {
32
+ "target" => target,
33
+ "published" => published,
34
+ "protocol" => protocol,
35
+ "mode" => mode,
36
+ }
37
+ end
38
+ compose.xdomains.each do |xd|
39
+ next unless xd["service"] == name
40
+ ports.unshift({
41
+ "protocol" => "tcp",
42
+ "target" => xd["port"],
43
+ "published" => "80",
44
+ })
45
+ end
46
+ compose.xports.each do |xp|
47
+ next unless xp["service"] == name
48
+ ports.unshift({
49
+ "protocol" => xp["protocol"],
50
+ "target" => xp["inside"],
51
+ "published" => xp["outside"],
52
+ "mode" => "ingress",
53
+ })
54
+ end
55
+ ports.uniq!{|p|p.values_at("published", "protocol")}
56
+ super(ports)
57
+ end
58
+
59
+ def command= (value)
60
+ value = ["sh", "-c", value] if value.is_a?(String)
61
+ super(value)
62
+ # if configs
63
+ end
64
+
65
+ def resolve_environment (env)
66
+ if env.is_a?(Array)
67
+ env = env.map do |line|
68
+ key, value = line.split("=", 2)
69
+ [key, value || "$#{key}"]
70
+ end.to_h
71
+ end
72
+ env ||= {}
73
+ env.transform_values! do |value|
74
+ value.gsub(/\$[\dA-Z_a-z]+/) do |name|
75
+ compose.xenv[name[1..-1]]
76
+ end
77
+ end
78
+ env.map do |name, value|
79
+ {
80
+ name: name,
81
+ value: value,
82
+ }
83
+ end
84
+ end
85
+
86
+ def namify (*items)
87
+ items.flatten.compact.join("-").downcase.gsub(/[^a-z\d]/, '-').gsub(/-+/, "-")
88
+ end
89
+
90
+ def emit_deployment (stack)
91
+ stack << {
92
+ kind: "Deployment",
93
+ apiVersion: "apps/v1",
94
+ metadata: {
95
+ namespace: compose.name,
96
+ name: name,
97
+ },
98
+ spec: {
99
+ replicas: deploy.replicas,
100
+ selector: {
101
+ matchLabels: {
102
+ stack: compose.name,
103
+ service: name,
104
+ },
105
+ },
106
+ template: {
107
+ metadata: {
108
+ labels: {
109
+ stack: compose.name,
110
+ service: name,
111
+ },
112
+ },
113
+ spec: {
114
+ containers: [
115
+ command: command,
116
+ env: resolve_environment(environment),
117
+ image: image,
118
+ name: name,
119
+ volumeMounts: volumes.map do |volume|
120
+ name, path = volume.split(":", 2)
121
+ {
122
+ mountPath: path,
123
+ name: name,
124
+ }
125
+ end
126
+ ],
127
+ hostname: hostname,
128
+ imagePullSecrets: [{name: "image-pull-secrets"}],
129
+ restartPolicy: { "none" => "Never", "on-failure" => "OnFailure" }[deploy.restart_policy&.condition],
130
+ volumes: volumes.map do |volume|
131
+ name = volume.split(":")[0]
132
+ { name: name, persistentVolumeClaim: { claimName: name } }
133
+ end
134
+ },
135
+ },
136
+ },
137
+ }
138
+ end
139
+
140
+ def emit_services (stack)
141
+ ports.group_by{|port|port["mode"]}.each do |mode, ports|
142
+ stack << {
143
+ kind: "Service",
144
+ apiVersion: "v1",
145
+ metadata: {
146
+ namespace: compose.name,
147
+ name: mode == "ingress" ? "#{name}-nodeport" : name,
148
+ },
149
+ spec: {
150
+ ports: ports.map do |port|
151
+ {
152
+ name: ports.length == 1 ? nil : port["published"],
153
+ port: port["published"].to_i,
154
+ targetPort: port["target"] != port["published"] ? port["target"].to_i : nil,
155
+ protocol: port["protocol"] == "udp" ? "UDP" : nil
156
+ }
157
+ end,
158
+ selector: {
159
+ stack: compose.name,
160
+ service: name,
161
+ },
162
+ type: mode == "ingress" ? "NodePort" : nil,
163
+ }
164
+ }
165
+ end
166
+ end
167
+
168
+ def emit_auth_middleware (stack)
169
+ return if @did_emit_auth_middleware
170
+ did_emit_auth_middleware = true
171
+ stack << {
172
+ kind: "Middleware",
173
+ apiVersion: "traefik.containo.us/v1alpha1",
174
+ metadata: {
175
+ namespace: compose.name,
176
+ name: "altaire-auth",
177
+ },
178
+ spec: {
179
+ forwardAuth: {
180
+ address: "http://auth-server.default.svc.cluster.local/auth",
181
+ },
182
+ },
183
+ }
184
+ end
185
+
186
+ def emit_middlewares (stack)
187
+ compose.xdomains.each do |xd|
188
+ next unless xd["service"] == name
189
+ next if xd["path"] == nil
190
+ stack << {
191
+ kind: "Middleware",
192
+ apiVersion: "traefik.containo.us/v1alpha1",
193
+ metadata: {
194
+ namespace: compose.name,
195
+ name: namify(xd["name"], xd["path"], "strip-prefix"),
196
+ },
197
+ spec: {
198
+ stripPrefix: {
199
+ prefixes: ["/#{xd["path"]}"],
200
+ },
201
+ },
202
+ }
203
+ end
204
+ end
205
+
206
+ def emit_ingress_routes (stack)
207
+ compose.xdomains.each do |xd|
208
+ next unless xd["service"] == name
209
+ emit_auth_middleware stack if xd["auth"]
210
+ stack << {
211
+ kind: "IngressRoute",
212
+ apiVersion: "traefik.containo.us/v1alpha1",
213
+ metadata: {
214
+ namespace: compose.name,
215
+ name: namify(xd["name"], xd["path"]),
216
+ },
217
+ spec: {
218
+ tls: {
219
+ secretName: namify(xd["name"]),
220
+ },
221
+ routes: [{
222
+ kind: "Rule",
223
+ match: nil,
224
+ middlewares: [
225
+ xd["auth"] ? { name: "altaire-auth" } : nil,
226
+ xd["path"] ? { name: namify(xd["name"], xd["path"], "strip-prefix") } : nil,
227
+ ],
228
+ services: [{
229
+ name: name,
230
+ port: 80
231
+ }],
232
+ }.merge(
233
+ if xd["path"]
234
+ {match: "Host(`#{xd["name"]}`) && PathPrefix(`#{xd["path"]}`)"}
235
+ else
236
+ {match: "Host(`#{xd["name"]}`)"}
237
+ end
238
+ )],
239
+ },
240
+ }
241
+ end
242
+ end
243
+
244
+ def emit_domains (stack)
245
+ compose.xdomains.each do |xd|
246
+ stack << {
247
+ kind: "Domain",
248
+ apiVersion: "altaire.com/v1alpha1",
249
+ metadata: {
250
+ namespace: compose.name,
251
+ name: namify(xd["name"]),
252
+ },
253
+ spec: {
254
+ domain: xd["name"],
255
+ service: "traefik",
256
+ },
257
+ }
258
+ end
259
+ end
260
+
261
+ def emit_certificates (stack)
262
+ compose.xdomains.each do |xd|
263
+ stack << {
264
+ kind: "Certificate",
265
+ apiVersion: "cert-manager.io/v1alpha2",
266
+ metadata: {
267
+ namespace: compose.name,
268
+ name: namify(xd["name"]),
269
+ },
270
+ spec: {
271
+ commonName: xd["name"],
272
+ dnsNames: [xd["name"]],
273
+ duration: "2160h0m0s",
274
+ renewBefore: "360h0m0s",
275
+ issuerRef: {
276
+ kind: "ClusterIssuer",
277
+ name: "letsencrypt-production",
278
+ },
279
+ secretName: namify(xd["name"]),
280
+ },
281
+ }
282
+ end
283
+ end
284
+
285
+ def emit_volumes (stack)
286
+ volumes.each do |volume|
287
+ name, path = volume.split(":", 2)
288
+ stack << {
289
+ kind: "PersistentVolumeClaim",
290
+ apiVersion: "v1",
291
+ metadata: {
292
+ namespace: compose.name,
293
+ name: name,
294
+ },
295
+ spec: {
296
+ accessModes: ["ReadWriteOnce"],
297
+ resources: {
298
+ requests: {
299
+ storage: "1Gi",
300
+ },
301
+ },
302
+ storageClassName: "do-block-storage",
303
+ },
304
+ }
305
+ end
306
+ end
307
+
308
+ def to_stack (stack)
309
+ emit_deployment stack
310
+ emit_services stack
311
+ emit_middlewares stack
312
+ emit_ingress_routes stack
313
+ # emit_domains stack
314
+ emit_certificates stack
315
+ emit_volumes stack
316
+ end
317
+
318
+ end
319
+
320
+ end
321
+ end
@@ -0,0 +1,230 @@
1
+ require 'shellwords'
2
+
3
+ class Array
4
+ def deep_compact
5
+ map do |value|
6
+ value = value.deep_compact if value.respond_to?(:deep_compact)
7
+ value
8
+ end.compact.yield_self do |x|
9
+ x.empty? ? nil : x
10
+ end
11
+ end
12
+ end
13
+
14
+ class Hash
15
+ def deep_compact
16
+ transform_values do |value|
17
+ value = value.deep_compact if value.respond_to?(:deep_compact)
18
+ value
19
+ end.compact.yield_self do |x|
20
+ x.empty? ? nil : x
21
+ end
22
+ end
23
+ end
24
+
25
+ module Siren
26
+ class Compose
27
+ module HashInitialize
28
+ def initialize (data = {})
29
+ data.each do |key, value|
30
+ self.__send__("#{key}=", value)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ require_relative "service"
38
+
39
+ module Siren
40
+ class Compose
41
+
42
+ class RestartPolicy < Struct.new(:condition, :delay, :max_attempts, :window)
43
+ include HashInitialize
44
+ end
45
+
46
+ class RollbackConfig < Struct.new(:parallelism, :delay, :failure_action, :monitor, :max_failure_ratio, :order)
47
+ include HashInitialize
48
+ end
49
+
50
+ class UpdateConfig < Struct.new(:parallelism, :delay, :failure_action, :monitor, :max_failure_ratio, :order)
51
+ include HashInitialize
52
+ end
53
+
54
+ class Deploy < Struct.new(:endpoint_mode, :labels, :mode, :placement, :replicas, :resources, :restart_policy, :rollback_config, :update_config)
55
+ include HashInitialize
56
+ def restart_policy= (data)
57
+ data = RestartPolicy.new(data)
58
+ end
59
+ def rollback_config= (data)
60
+ data = RollbackConfig.new(data)
61
+ end
62
+ def update_config= (data)
63
+ data = UpdateConfig.new(data)
64
+ end
65
+ end
66
+
67
+ # restart_policy: object(
68
+ # condition: string(["none", "on-failure", "any"]),
69
+ # delay: duration,
70
+ # max_attemtps: integer,
71
+ # window: duration,
72
+ # ),
73
+ # rollback_config: object(
74
+ # parallelism: integer,
75
+ # delay: duration,
76
+ # failure_action: string(["continue", "pause"]),
77
+ # monitor: duration,
78
+ # max_failure_ratio: number,
79
+ # order: string(["stop-first", "start-first"]),
80
+ # ),
81
+ # update_config: object(
82
+ # parallelism: integer,
83
+ # delay: duration,
84
+ # failure_action: string(["continue", "pause", "rollback"]),
85
+ # monitor: duration,
86
+ # max_failure_ratio: number,
87
+ # order: string(["stop-first", "start-first"]),
88
+ # ),
89
+
90
+ # deploy: object(
91
+ # endpoint_mode: string(["vip", "dnsrr"]),
92
+ # labels: hashlike,
93
+ # mode: string(["global", "replicated"]),
94
+ # placement: object(
95
+ # constraints: array(string),
96
+ # preferences: array(object(spread: string)),
97
+ # ),
98
+ # replicas: integer,
99
+ # resources: object(
100
+ # limits: resources,
101
+ # reservations: resources,
102
+ # ),
103
+
104
+ Volume = Struct.new(:compose, :driver, :driver_opts, :external, :labels, :name)
105
+
106
+ Network = Struct.new(:compose, :driver, :driver_opts, :external, :labels, :name, :attachable, :ipam, :internal)
107
+
108
+ Config = Struct.new(:compose, :file, :external, :name)
109
+
110
+ Secret = Struct.new(:compose, :file, :external, :name)
111
+
112
+ end
113
+ end
114
+
115
+ # version: string(/[0-9]+(\.[0-9]+)?/),
116
+ # services: hash(
117
+ # build: any(string, object(
118
+ # context: string,
119
+ # dockerfile: string,
120
+ # args: hashlike,
121
+ # cache_from: array(string),
122
+ # labels: hashlike,
123
+ # shm_size: string,
124
+ # target: string)
125
+ # ),
126
+ # command: any(string, array(string)),
127
+ # configs: array(any(string, object(
128
+ # source: string, target: string, uid: any(string, integer), gid: any(string, integer), mode: any(string, integer)
129
+ # ))),
130
+ # container_name: string,
131
+ # credential_spec: any(object(file: string), object(registry: string), object(config: string)),
132
+ # deploy: object(
133
+ # endpoint_mode: string(["vip", "dnsrr"]),
134
+ # labels: hashlike,
135
+ # mode: string(["global", "replicated"]),
136
+ # placement: object(
137
+ # constraints: array(string),
138
+ # preferences: array(object(spread: string)),
139
+ # ),
140
+ # replicas: integer,
141
+ # resources: object(
142
+ # limits: resources,
143
+ # reservations: resources,
144
+ # ),
145
+ # restart_policy: object(
146
+ # condition: string(["none", "on-failure", "any"]),
147
+ # delay: duration,
148
+ # max_attemtps: integer,
149
+ # window: duration,
150
+ # ),
151
+ # rollback_config: object(
152
+ # parallelism: integer,
153
+ # delay: duration,
154
+ # failure_action: string(["continue", "pause"]),
155
+ # monitor: duration,
156
+ # max_failure_ratio: number,
157
+ # order: string(["stop-first", "start-first"]),
158
+ # ),
159
+ # update_config: object(
160
+ # parallelism: integer,
161
+ # delay: duration,
162
+ # failure_action: string(["continue", "pause", "rollback"]),
163
+ # monitor: duration,
164
+ # max_failure_ratio: number,
165
+ # order: string(["stop-first", "start-first"]),
166
+ # ),
167
+ # ),
168
+ # dns: any(string, array(string)),
169
+ # dns_search: any(string, array(string)),
170
+ # entrypoint: any(string, array(string)),
171
+ # environment: hashlike,
172
+ # expose: array(string),
173
+ # extra_hosts: array(string),
174
+ # healthcheck: object(
175
+ # test: any(string, array(string)),
176
+ # interval: duration,
177
+ # timeout: duration,
178
+ # retries: integer,
179
+ # start_period: duration,
180
+ # ),
181
+ # image: string,
182
+ # init: bool,
183
+ # labels: hashlike,
184
+ # logging: object(
185
+ # driver: string,
186
+ # options: hash(string),
187
+ # ),
188
+ # networks: any(array(string), hash(object(aliases: array(string), ipv4_address: string, ipv6_address: string))),
189
+ # pid: string("host"),
190
+ # ports: array(any(string, object(target: integer, published: integer, protocol: string(["tcp", "udp"]), mode: string(["host", "ingress"])))),
191
+ # secrets: array(any(string, object(source: string, target: string, uid: integer, gid: integer, mode: integer))),
192
+ # stop_grace_period: duration,
193
+ # stop_signal: string(/^SIG/),
194
+ # sysctls: hashlike,
195
+ # ulimits: hash(any(integer, object(soft: integer, hard: integer))),
196
+ # volumes: array(any(string,
197
+ # object(type: string(["volume", "bind", "tmpfs", "npipe"]),
198
+ # source: string, target: string, read_only: boolean,
199
+ # consistency: string(["consistent", "cached", "delegated"]),
200
+ # volume: object(nocopy: boolean), bind: object(propagation: string),
201
+ # tmpfs: object(size: bytesize)),
202
+ # )),
203
+ # domainname: string,
204
+ # hostname: string,
205
+ # ipc: string,
206
+ # mac_address: string,
207
+ # privileged: boolean,
208
+ # read_only: boolean,
209
+ # shm_size: bytesize,
210
+ # stdin_open: boolean,
211
+ # tty: boolean,
212
+ # user: any(string, integer),
213
+ # working_dir: string,
214
+ # ),
215
+ # volumes: hash(any(null, object(driver: string, driver_opts: hash(string), external: boolean, labels: hashlike, name: string))),
216
+ # networks: hash(any(null, object(
217
+ # driver: string,
218
+ # driver_opts: hash(string),
219
+ # external: any(boolean, object(name: string)),
220
+ # attachable: boolean,
221
+ # ipam: object(
222
+ # driver: string,
223
+ # config: array(object(subnet: string))
224
+ # ),
225
+ # internal: boolean,
226
+ # labels: hashlike,
227
+ # name: string,
228
+ # ))),
229
+ # configs: hash(object(file: string, external: any(boolean, object(name: string)))),
230
+ # secrets: hash(object(file: string, external: boolean, name: string)),