altaire-siren 0.1.0 → 0.1.2

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 (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)),