docker-pier 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4db1df031b3184db08b38589afed042f43c93f5c
4
+ data.tar.gz: 56f5f252bebdc7300fdf8d109b69827d08125dc3
5
+ SHA512:
6
+ metadata.gz: 621f0d87acabf5d89435c3b5597edee4cdc73a1a61838f1250f8fb2da630941538de95f68a1a0470fe9e400906baa9c61209ba7947883a73e1b48f3a004122fb
7
+ data.tar.gz: eea7967dbb62ab38d06d9047981a6672ad91849770d70967e3504d425d3277e169cd1b70603784dbaf959ee5a48150b1adf0a6b39c0c2f9b2723d155cee8b9f0
@@ -0,0 +1,10 @@
1
+ .DS_Store
2
+ /.bundle/
3
+ /.yardoc
4
+ /Gemfile.lock
5
+ /_yardoc/
6
+ /coverage/
7
+ /doc/
8
+ /pkg/
9
+ /spec/reports/
10
+ /tmp/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in docker-pier.gemspec
4
+ gemspec
@@ -0,0 +1,46 @@
1
+ # Pier
2
+
3
+ Pier is an alternative Docker client, which uses the Ruby [docker-api](https://github.com/swipely/docker-api) gem to access the Docker Remote API. Pier is focused on the use-case of managing one or more clusters of machines which are entirely dedicated to running a Docker swarm.
4
+
5
+ Pier uses [fog](http://fog.io/) for its instance management. For now, only the [fog-libvirt](https://github.com/fog/fog-libvirt) backend is supported.
6
+
7
+ Pier assumes that each cluster has a cluster-manager endpoint, called a "pier." The pier is a running instance that must:
8
+
9
+ * be accessible via SSH;
10
+ * be a manager in your Docker swarm; and
11
+ * have `easy-rsa` installed and set up to generate both server and client certificates.
12
+
13
+ A pier need not be a member of the cluster itself. Pier does not manage the pier, only the cluster visible through the pier.
14
+
15
+ By default, Pier also assumes that each pier is the _libvirt hypervisor_ for its cluster—i.e. that libvirt's endpoint is accessible as `qemu+ssh://[your-pier-endpoint]/system`.
16
+
17
+ ## Installation
18
+
19
+ Add this line to your application's Gemfile:
20
+
21
+ ```ruby
22
+ gem 'docker-pier'
23
+ ```
24
+
25
+ And then execute:
26
+
27
+ $ bundle
28
+
29
+ Or install it yourself as:
30
+
31
+ $ gem install docker-pier
32
+
33
+ ## Usage
34
+
35
+ TODO: Write usage instructions here
36
+
37
+ ## Development
38
+
39
+ After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
40
+
41
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
42
+
43
+ ## Contributing
44
+
45
+ Bug reports and pull requests are welcome on GitHub at https://github.com/tsutsu/docker-pier.
46
+
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "docker-pier"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ ENV['PIER'] ||= '//tsutsu@vizier.staging.meetwalter.co'
10
+
11
+ include DockerPier
12
+
13
+ require "pry"
14
+ Pry.start
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,36 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'docker-pier/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "docker-pier"
8
+ spec.version = DockerPier::VERSION
9
+ spec.authors = ["Levi Aul"]
10
+ spec.email = ["levi@leviaul.com"]
11
+
12
+ spec.summary = %q{An alternative Docker client for dedicated Docker Swarm clusters}
13
+ spec.homepage = "https://github.com/tsutsu/docker-pier"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
16
+ f.match(%r{^(test|spec|features)/})
17
+ end
18
+ spec.bindir = "exe"
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.13"
23
+ spec.add_development_dependency "rake", "~> 10.0"
24
+
25
+ spec.add_runtime_dependency "fog-libvirt", "~> 0.3.0"
26
+ spec.add_runtime_dependency "docker-api", "~> 1.32", ">= 1.32.1"
27
+ spec.add_runtime_dependency "net-ssh", "~> 3.2"
28
+ spec.add_runtime_dependency "net-scp", "~> 1.2", ">= 1.2.1"
29
+ spec.add_runtime_dependency "highline", "~> 1.7", ">= 1.7.8"
30
+ spec.add_runtime_dependency "pry", "~> 0.10.4"
31
+ spec.add_runtime_dependency "main", "~> 6.2", ">= 6.2.1"
32
+ spec.add_runtime_dependency "sequel", "~> 4.39"
33
+ spec.add_runtime_dependency "amalgalite", "~> 1.5"
34
+ spec.add_runtime_dependency "color", "~> 1.8"
35
+ spec.add_runtime_dependency "paint", "~> 1.0", ">= 1.0.1"
36
+ end
@@ -0,0 +1,197 @@
1
+ #!/usr/bin/env ruby
2
+ require "docker-pier"
3
+ require "main"
4
+ require "highline"
5
+
6
+ Main do
7
+ db do
8
+ create_table(:clusters) do
9
+ primary_key :id
10
+ String :name, unique: true, null: false
11
+ String :pier_uri, null: false
12
+ end unless table_exists?(:clusters)
13
+ end
14
+
15
+ def run
16
+ $stderr.puts "use `#{File.basename($0)} --help` for usage"
17
+ end
18
+
19
+
20
+ mode 'list' do
21
+ def run
22
+ db[:clusters].each do |cluster|
23
+ puts "#{cluster[:name]}: #{cluster[:pier_uri]}"
24
+ end
25
+ end
26
+ end
27
+
28
+ mode 'detach' do
29
+ argument('name'){
30
+ description "a cluster nickname"
31
+ required
32
+ }
33
+
34
+ def run
35
+ cluster_name = params[:name].values.first
36
+ pier = db[:clusters].where(name: cluster_name).delete
37
+
38
+ in_use_cluster_name = ENV['DOCKER_MACHINE_NAME']
39
+ if cluster_name == in_use_cluster_name
40
+ puts <<~EOF
41
+ unset DOCKER_TLS_VERIFY
42
+ unset DOCKER_HOST
43
+ unset DOCKER_CERT_PATH
44
+ unset DOCKER_MACHINE_NAME
45
+ EOF
46
+ end
47
+ end
48
+ end
49
+
50
+ mode 'attach' do
51
+ argument('name'){
52
+ description "nickname for the new cluster"
53
+ required
54
+ }
55
+
56
+ argument('uri'){
57
+ description "URI of the cluster's pier"
58
+ required
59
+ }
60
+
61
+ def run
62
+ cluster_name = params[:name].values.first
63
+ pier_uri = URI.parse('//' + params[:uri].values.first + '/')
64
+
65
+ # test validity
66
+ pier = DockerPier::Pier.new(pier_uri, name: cluster_name)
67
+ pier.bootstrap!
68
+
69
+ pier.libvirt.get_node_info
70
+ Docker.info(pier.docker)
71
+ pier.ssh.exec! 'pwd'
72
+
73
+ db[:clusters].insert(nil, cluster_name, pier_uri.to_s)
74
+
75
+ puts <<~EOF
76
+ export DOCKER_TLS_VERIFY=1
77
+ export DOCKER_HOST=#{pier.resolved_docker_uri}
78
+ export DOCKER_CERT_PATH=#{pier.x509_dir}
79
+ export DOCKER_MACHINE_NAME=#{cluster_name}
80
+ EOF
81
+ end
82
+ end
83
+
84
+ mode 'on' do
85
+ argument('name'){
86
+ description "nickname of the cluster to switch to"
87
+ required
88
+ }
89
+
90
+ def run
91
+ cluster_name = params[:name].values.first
92
+ pier = db[:clusters].where(name: cluster_name).first
93
+ unless pier
94
+ $stderr.puts "unknown cluster '#{cluster_name}'"
95
+ return false
96
+ end
97
+
98
+ pier = DockerPier::Pier.new(pier[:pier_uri], name: pier[:name])
99
+
100
+ puts <<~EOF
101
+ export DOCKER_TLS_VERIFY=1
102
+ export DOCKER_HOST=#{pier.resolved_docker_uri}
103
+ export DOCKER_CERT_PATH=#{pier.x509_dir}
104
+ export DOCKER_MACHINE_NAME=#{cluster_name}
105
+ EOF
106
+ end
107
+ end
108
+
109
+ mode 'off' do
110
+ def run
111
+ puts <<~EOF
112
+ unset DOCKER_TLS_VERIFY
113
+ unset DOCKER_HOST
114
+ unset DOCKER_CERT_PATH
115
+ unset DOCKER_MACHINE_NAME
116
+ EOF
117
+ end
118
+ end
119
+
120
+
121
+ mode 'hook:install' do
122
+ def run
123
+ puts <<~EOF
124
+ pier ()
125
+ {
126
+ local subcommand="$1"
127
+ case "${subcommand}" in
128
+ on|off|attach|detach)
129
+ shift
130
+ eval "$(command docker-pier "${subcommand}" "$@")"
131
+ ;;
132
+ *)
133
+ command docker-pier "$@"
134
+ ;;
135
+ esac
136
+ }
137
+ EOF
138
+ end
139
+ end
140
+
141
+
142
+ mode 'nodes' do
143
+ def run
144
+ cluster_name = ENV['DOCKER_MACHINE_NAME']
145
+ pier = db[:clusters].where(name: cluster_name).first
146
+ unless pier
147
+ $stderr.puts "unknown cluster '#{cluster_name}'"
148
+ return false
149
+ end
150
+ pier = DockerPier::Pier.new(pier[:pier_uri], name: pier[:name])
151
+
152
+ pier.nodes.each do |node|
153
+ puts node.inspect
154
+ end
155
+ end
156
+ end
157
+
158
+ mode 'logs' do
159
+ def run
160
+ cluster_name = ENV['DOCKER_MACHINE_NAME']
161
+ pier = db[:clusters].where(name: cluster_name).first
162
+ unless pier
163
+ $stderr.puts "unknown cluster '#{cluster_name}'"
164
+ return false
165
+ end
166
+ pier = DockerPier::Pier.new(pier[:pier_uri], name: pier[:name])
167
+
168
+ handle_maxlen = 0
169
+ tty_width = HighLine::SystemExtensions.terminal_size[0]
170
+
171
+ pier.logs.stream! do |event|
172
+ next if event.lines.empty?
173
+
174
+ handle_ansi = event.task.name
175
+ handle_maxlen = [handle_maxlen, handle_ansi.length].max
176
+ handle_ansi = handle_ansi.rjust(handle_maxlen, ' ')
177
+ handle_ansi = Paint[handle_ansi, event.task.color]
178
+
179
+ time_ansi = Paint[event.time.strftime("%H:%M:%S"), :white]
180
+
181
+ ln1_prefix_ansi = "%s %s " % [handle_ansi, time_ansi]
182
+ lns_indent = ' ' * Paint.unpaint(ln1_prefix_ansi).length
183
+ lns_color = (event.type == :error) ? :red : :default
184
+
185
+ tty_remain = tty_width - lns_indent.length
186
+ lns = event.lines.dup
187
+ lns = lns.map{ |ln| ln.gsub(/(.{1,#{tty_remain}})/, "\\1\n").split("\n") }.flatten
188
+
189
+ puts ln1_prefix_ansi + Paint[lns.shift, lns_color]
190
+
191
+ lns.each do |ln|
192
+ puts lns_indent + Paint[ln, lns_color]
193
+ end
194
+ end
195
+ end
196
+ end
197
+ end
@@ -0,0 +1,4 @@
1
+ require 'docker-pier/version'
2
+ require 'docker-pier/pier'
3
+ require 'docker-pier/node'
4
+ require 'docker-pier/log-stream'
@@ -0,0 +1,94 @@
1
+ require 'net/ssh'
2
+ require 'shellwords'
3
+ require 'json'
4
+ require 'paint'
5
+ require 'date'
6
+ require 'ostruct'
7
+
8
+ require 'docker-pier/log-stream/palette'
9
+
10
+ class Pathname
11
+ def to_shell
12
+ Shellwords.escape(self.to_s)
13
+ end
14
+ end
15
+
16
+ class DockerPier::LogStream
17
+ def initialize(pier)
18
+ @pier = pier
19
+ @palette = DockerPier::LogStream::Palette.new
20
+ @task_colors = Hash.new{ |h,k| h[k] = @palette.draw! }
21
+ end
22
+
23
+ def path
24
+ @path ||= get_path!
25
+ end
26
+
27
+ def get_path!
28
+ logs_dir = Pathname.new '/var/log/fluentd'
29
+ logfile_link = logs_dir + 'docker.log'
30
+
31
+ # fluentd creates the symlink with an absolute path from inside a container,
32
+ # so the dir the link points to is wrong. We assume the logs dir instead.
33
+ logs_link_dest = Pathname.new(@pier.ssh.exec!("readlink #{logfile_link.to_shell}").chomp)
34
+ logs_dir + logs_link_dest.basename
35
+ end
36
+
37
+ def parse_log_ln(ln)
38
+ ts, fluent_source_id, event_json = ln.split(/\s+/, 3)
39
+
40
+ ts = DateTime.parse(ts)
41
+ event = JSON.parse(event_json)
42
+ event_type = (event.delete('source') == 'stderr') ? :error : :output
43
+
44
+ container_id = event.delete('container_name')[1..-1]
45
+ msg = event.delete('log')
46
+
47
+ if msg[0] == '{'
48
+ msg_parts = JSON.parse(msg)
49
+ msg_lns = msg_parts.delete('lines').map{ |ln| ln.chomp }
50
+ msg_parts = OpenStruct.new(msg_parts)
51
+ else
52
+ msg_lns = msg.split("\n")
53
+ end
54
+
55
+ service_parts = container_id.match(/^(\w+)\.(\d+)\.(\w+)$/)
56
+ service = service_parts ? [service_parts[1], service_parts[2].to_i] : nil
57
+
58
+ task_handle = service ? ("%s[%02d]" % service) : container_id
59
+ task_color = @task_colors[task_handle]
60
+
61
+ OpenStruct.new(
62
+ time: ts,
63
+ type: event_type,
64
+ service: service,
65
+ task: OpenStruct.new(name: task_handle, color: task_color),
66
+ location: [:some_node, container_id],
67
+ message: msg_parts,
68
+ lines: msg_lns
69
+ )
70
+ end
71
+
72
+ def stream!
73
+ ssh_session = @pier.ssh
74
+
75
+ channel = ssh_session.open_channel do |ch|
76
+ ch.exec "tail -f #{self.path.to_shell}" do |ch, success|
77
+ raise "could not tail logs on remote" unless success
78
+
79
+ ch.on_data do |_, data|
80
+ data.split("\n").each do |ln|
81
+ event = parse_log_ln(ln)
82
+ yield(event)
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ int_pressed = false
89
+ trap("INT") { int_pressed = true }
90
+ ssh_session.loop(0.1) { not int_pressed }
91
+
92
+ ssh_session.shutdown!
93
+ end
94
+ end
@@ -0,0 +1,164 @@
1
+ class DockerPier::LogStream; end
2
+
3
+ class DockerPier::LogStream::Palette
4
+ def initialize
5
+ @bag = []
6
+ end
7
+
8
+ def draw!
9
+ if @bag.empty?
10
+ i = rand(BASE_COLORS.length)
11
+ @bag = BASE_COLORS[i..-1] + BASE_COLORS[0...i]
12
+ end
13
+
14
+ @bag.shift
15
+ end
16
+
17
+ BASE_COLORS = [
18
+ [0, 238, 0],
19
+ [148, 0, 211],
20
+ [255, 0, 255],
21
+ [255, 0, 0],
22
+ [104, 34, 139],
23
+ [139, 26, 26],
24
+ [127, 255, 0],
25
+ [0, 191, 255],
26
+ [255, 0, 255],
27
+ [0, 205, 0],
28
+ [139, 28, 98],
29
+ [127, 255, 0],
30
+ [255, 0, 0],
31
+ [0, 191, 255],
32
+ [0, 255, 0],
33
+ [199, 21, 133],
34
+ [238, 0, 238],
35
+ [0, 255, 0],
36
+ [205, 0, 0],
37
+ [124, 252, 0],
38
+ [199, 21, 133],
39
+ [124, 252, 0],
40
+ [205, 16, 118],
41
+ [0, 191, 255],
42
+ [102, 205, 0],
43
+ [208, 32, 144],
44
+ [238, 0, 0],
45
+ [118, 238, 0],
46
+ [0, 0, 205],
47
+ [208, 32, 144],
48
+ [0, 0, 205],
49
+ [139, 34, 82],
50
+ [255, 165, 0],
51
+ [0, 178, 238],
52
+ [0, 0, 205],
53
+ [154, 205, 50],
54
+ [205, 0, 205],
55
+ [154, 205, 50],
56
+ [255, 69, 0],
57
+ [148, 0, 211],
58
+ [154, 205, 50],
59
+ [215, 7, 81],
60
+ [255, 165, 0],
61
+ [153, 50, 204],
62
+ [105, 139, 34],
63
+ [255, 69, 0],
64
+ [153, 50, 204],
65
+ [107, 142, 35],
66
+ [154, 50, 205],
67
+ [107, 142, 35],
68
+ [255, 69, 0],
69
+ [205, 41, 144],
70
+ [85, 107, 47],
71
+ [122, 55, 139],
72
+ [238, 173, 14],
73
+ [34, 139, 34],
74
+ [139, 35, 35],
75
+ [34, 139, 34],
76
+ [0, 0, 238],
77
+ [255, 140, 0],
78
+ [0, 255, 255],
79
+ [255, 140, 0],
80
+ [85, 107, 47],
81
+ [0, 255, 255],
82
+ [255, 127, 0],
83
+ [0, 245, 255],
84
+ [85, 26, 139],
85
+ [178, 34, 34],
86
+ [0, 0, 255],
87
+ [238, 64, 0],
88
+ [205, 50, 120],
89
+ [50, 205, 50],
90
+ [0, 0, 255],
91
+ [238, 154, 0],
92
+ [50, 205, 50],
93
+ [205, 38, 38],
94
+ [0, 154, 205],
95
+ [205, 55, 0],
96
+ [16, 78, 139],
97
+ [165, 42, 42],
98
+ [176, 48, 96],
99
+ [0, 255, 127],
100
+ [238, 118, 0],
101
+ [0, 238, 238],
102
+ [0, 255, 127],
103
+ [39, 64, 139],
104
+ [0, 255, 127],
105
+ [218, 165, 32],
106
+ [0, 229, 238],
107
+ [110, 139, 61],
108
+ [0, 250, 154],
109
+ [139, 58, 98],
110
+ [0, 250, 154],
111
+ [139, 54, 38],
112
+ [205, 149, 12],
113
+ [139, 71, 137],
114
+ [205, 155, 29],
115
+ [24, 116, 205],
116
+ [139, 69, 19],
117
+ [139, 69, 19],
118
+ [0, 238, 118],
119
+ [205, 133, 0],
120
+ [139, 69, 19],
121
+ [205, 102, 0],
122
+ [139, 58, 58],
123
+ [71, 60, 139],
124
+ [184, 134, 11],
125
+ [184, 134, 11],
126
+ [72, 61, 139],
127
+ [72, 61, 139],
128
+ [210, 105, 30],
129
+ [139, 62, 47],
130
+ [0, 206, 209],
131
+ [0, 206, 209],
132
+ [205, 102, 29],
133
+ [139, 105, 20],
134
+ [125, 38, 205],
135
+ [0, 205, 102],
136
+ [0, 205, 205],
137
+ [0, 197, 205],
138
+ [139, 71, 93],
139
+ [139, 71, 38],
140
+ [84, 139, 84],
141
+ [70, 130, 180],
142
+ [70, 130, 180],
143
+ [54, 100, 139],
144
+ [160, 82, 45],
145
+ [46, 139, 87],
146
+ [46, 139, 87],
147
+ [46, 139, 87],
148
+ [60, 179, 113],
149
+ [60, 179, 113],
150
+ [139, 76, 57],
151
+ [139, 90, 43],
152
+ [93, 71, 139],
153
+ [32, 178, 170],
154
+ [32, 178, 170],
155
+ [139, 87, 66],
156
+ [74, 112, 139],
157
+ [69, 139, 116],
158
+ [139, 115, 85],
159
+ [95, 158, 160],
160
+ [95, 158, 160],
161
+ [82, 139, 139],
162
+ [83, 134, 139]
163
+ ]
164
+ end
@@ -0,0 +1,20 @@
1
+ require 'docker-pier/version'
2
+ require 'fog/libvirt'
3
+ require 'docker/swarm'
4
+ require 'json'
5
+
6
+ class DockerPier::Node < SimpleDelegator
7
+ def initialize(docker_node, pier)
8
+ @docker_node = docker_node
9
+ @pier = pier
10
+ super @docker_node
11
+ end
12
+
13
+ def libvirt_node
14
+ @libvirt_node ||= (@pier.libvirt.servers.all(name: @docker_node.info['Description']['Hostname']).first rescue nil)
15
+ end
16
+
17
+ def inspect
18
+ "#<DockerPier::Node %s/%s>" % [@pier.name, @docker_node.info['Description']['Hostname']]
19
+ end
20
+ end
@@ -0,0 +1,102 @@
1
+ require 'docker-pier/version'
2
+ require 'docker'
3
+ require 'docker/swarm'
4
+ require 'fog/libvirt'
5
+ require 'net/ssh'
6
+ require 'net/scp'
7
+ require 'resolv'
8
+ require 'pathname'
9
+
10
+ class DockerPier::Pier
11
+ def self.current
12
+ @current ||= self.new(ENV['PIER'])
13
+ end
14
+
15
+ def initialize(pier_uri, opts = {})
16
+ @name = opts.delete(:name) || ENV['DOCKER_MACHINE_NAME'] || Digest::SHA1.hexdigest(pier_uri)
17
+
18
+ pier_uri = URI.parse(pier_uri.to_s) unless pier_uri.kind_of?(URI)
19
+
20
+ @libvirt_uri = pier_uri.dup.tap do |uri|
21
+ uri.scheme = 'qemu+ssh'
22
+ uri.path = '/system'
23
+ uri.query = 'socket=/var/run/libvirt/libvirt-sock'
24
+ end
25
+
26
+ @docker_uri = pier_uri.dup.tap do |uri|
27
+ uri.scheme = 'tcp'
28
+ uri.user = nil
29
+ uri.port = 2376
30
+ uri.path = '/'
31
+ end
32
+
33
+ @ssh_uri = pier_uri.dup.tap do |uri|
34
+ uri.scheme = 'ssh'
35
+ uri.port = 22
36
+ uri.path = '/'
37
+ end
38
+ end
39
+
40
+ attr_reader :name
41
+ attr_reader :libvirt_uri
42
+ attr_reader :docker_uri
43
+ attr_reader :ssh_uri
44
+
45
+
46
+ def x509_dir
47
+ Pathname.new(ENV['HOME']) + '.docker-pier' + 'x509' + @name
48
+ end
49
+
50
+ def resolved_docker_uri
51
+ @docker_uri.dup.tap{ |u| u.host = Resolv.getaddress(u.host) }
52
+ end
53
+
54
+
55
+ def libvirt
56
+ @libvirt ||= Fog::Compute.new(provider: "Libvirt", libvirt_uri: @libvirt_uri.to_s)
57
+ end
58
+
59
+ def docker
60
+ @docker ||= Docker::Connection.new(self.resolved_docker_uri.to_s,
61
+ scheme: "https",
62
+ ssl_ca_file: (self.x509_dir + "ca.pem").to_s,
63
+ client_cert: (self.x509_dir + "cert.pem").to_s,
64
+ client_key: (self.x509_dir + "key.pem").to_s
65
+ )
66
+ end
67
+
68
+ def ssh
69
+ @ssh ||= Net::SSH.start(@ssh_uri.hostname, @ssh_uri.user, password: @ssh_uri.password)
70
+ end
71
+
72
+
73
+ def bootstrap!
74
+ own_hostname = `hostname -f`.chomp
75
+ remote_export_path = Pathname.new('/var/lib/private-x509/clients') + "#{own_hostname}.tar"
76
+
77
+ self.ssh.exec!(<<~EOF)
78
+ if [ ! -f '#{remote_export_path}' ]; then
79
+ /var/lib/private-x509/gen_bundle '#{own_hostname}'
80
+ fi
81
+ EOF
82
+
83
+ local_export_path = self.x509_dir + 'export.tar'
84
+
85
+ self.x509_dir.mkpath
86
+ Dir.chdir(self.x509_dir.to_s) do
87
+ self.ssh.scp.download! remote_export_path.to_s, local_export_path.to_s
88
+ system 'tar', '-x', '-f', local_export_path.to_s
89
+ local_export_path.unlink
90
+ end
91
+ end
92
+
93
+
94
+ def nodes
95
+ docker_nodes = Docker::Swarm::Node.all({}, self.docker)
96
+ docker_nodes.map{ |d| DockerPier::Node.new(d, self) }
97
+ end
98
+
99
+ def logs
100
+ DockerPier::LogStream.new(self)
101
+ end
102
+ end
@@ -0,0 +1,3 @@
1
+ module DockerPier
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,18 @@
1
+ class Docker::Swarm; end
2
+
3
+ require 'docker/swarm/node'
4
+ require 'docker/swarm/service'
5
+ require 'docker/swarm/task'
6
+
7
+ # This class represents a Docker Swarm.
8
+ class Docker::Swarm
9
+ include Docker::Base
10
+
11
+ class << self
12
+ def get(opts = {}, conn = Docker.connection)
13
+ swarm_json = conn.get("/swarm", opts)
14
+ hash = Docker::Util.parse_json(swarm_json) || {}
15
+ new(conn, hash)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,26 @@
1
+ require 'json'
2
+
3
+ # This class represents a Docker Swarm Node.
4
+ class Docker::Swarm::Node
5
+ include Docker::Base
6
+
7
+ class << self
8
+ def get(id, opts = {}, conn = Docker.connection)
9
+ node_json = conn.get("/nodes/#{URI.encode(id)}", opts)
10
+ hash = Docker::Util.parse_json(node_json) || {}
11
+ new(conn, hash)
12
+ end
13
+
14
+ def by_name(node_name, opts = {}, conn = Docker.connection)
15
+ opts = opts.merge(filters: {"name" => [node_name]}.to_json)
16
+ hashes = Docker::Util.parse_json(conn.get('/nodes', opts)) || []
17
+ hash = hashes.first
18
+ new(conn, hash) if hash
19
+ end
20
+
21
+ def all(opts = {}, conn = Docker.connection)
22
+ hashes = Docker::Util.parse_json(conn.get('/nodes', opts)) || []
23
+ hashes.map { |hash| new(conn, hash) }
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,17 @@
1
+ # This class represents a Docker Swarm Service.
2
+ class Docker::Swarm::Service
3
+ include Docker::Base
4
+
5
+ class << self
6
+ def get(id, opts = {}, conn = Docker.connection)
7
+ service_json = conn.get("/services/#{URI.encode(id)}", opts)
8
+ hash = Docker::Util.parse_json(service_json) || {}
9
+ new(conn, hash)
10
+ end
11
+
12
+ def all(opts = {}, conn = Docker.connection)
13
+ hashes = Docker::Util.parse_json(conn.get('/services', opts)) || []
14
+ hashes.map { |hash| new(conn, hash) }
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,33 @@
1
+ # This class represents a Docker Swarm Task.
2
+ class Docker::Swarm::Task
3
+ include Docker::Base
4
+
5
+ def service
6
+ Docker::Service.get(self.info["ServiceID"])
7
+ end
8
+
9
+ def node
10
+ Docker::Node.get(self.info["NodeID"])
11
+ end
12
+
13
+ def state
14
+ self.info['Status']['State'].intern
15
+ end
16
+
17
+ def running?
18
+ self.state == :running
19
+ end
20
+
21
+ class << self
22
+ def get(id, opts = {}, conn = Docker.connection)
23
+ task_json = conn.get("/tasks/#{URI.encode(id)}", opts)
24
+ hash = Docker::Util.parse_json(task_json) || {}
25
+ new(conn, hash)
26
+ end
27
+
28
+ def all(opts = {}, conn = Docker.connection)
29
+ hashes = Docker::Util.parse_json(conn.get('/tasks', opts)) || []
30
+ hashes.map { |hash| new(conn, hash) }
31
+ end
32
+ end
33
+ end
metadata ADDED
@@ -0,0 +1,274 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: docker-pier
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Levi Aul
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-10-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.13'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.13'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: fog-libvirt
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.3.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.3.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: docker-api
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.32'
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: 1.32.1
65
+ type: :runtime
66
+ prerelease: false
67
+ version_requirements: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - "~>"
70
+ - !ruby/object:Gem::Version
71
+ version: '1.32'
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: 1.32.1
75
+ - !ruby/object:Gem::Dependency
76
+ name: net-ssh
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '3.2'
82
+ type: :runtime
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '3.2'
89
+ - !ruby/object:Gem::Dependency
90
+ name: net-scp
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '1.2'
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: 1.2.1
99
+ type: :runtime
100
+ prerelease: false
101
+ version_requirements: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - "~>"
104
+ - !ruby/object:Gem::Version
105
+ version: '1.2'
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: 1.2.1
109
+ - !ruby/object:Gem::Dependency
110
+ name: highline
111
+ requirement: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - "~>"
114
+ - !ruby/object:Gem::Version
115
+ version: '1.7'
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: 1.7.8
119
+ type: :runtime
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - "~>"
124
+ - !ruby/object:Gem::Version
125
+ version: '1.7'
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ version: 1.7.8
129
+ - !ruby/object:Gem::Dependency
130
+ name: pry
131
+ requirement: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - "~>"
134
+ - !ruby/object:Gem::Version
135
+ version: 0.10.4
136
+ type: :runtime
137
+ prerelease: false
138
+ version_requirements: !ruby/object:Gem::Requirement
139
+ requirements:
140
+ - - "~>"
141
+ - !ruby/object:Gem::Version
142
+ version: 0.10.4
143
+ - !ruby/object:Gem::Dependency
144
+ name: main
145
+ requirement: !ruby/object:Gem::Requirement
146
+ requirements:
147
+ - - "~>"
148
+ - !ruby/object:Gem::Version
149
+ version: '6.2'
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: 6.2.1
153
+ type: :runtime
154
+ prerelease: false
155
+ version_requirements: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: '6.2'
160
+ - - ">="
161
+ - !ruby/object:Gem::Version
162
+ version: 6.2.1
163
+ - !ruby/object:Gem::Dependency
164
+ name: sequel
165
+ requirement: !ruby/object:Gem::Requirement
166
+ requirements:
167
+ - - "~>"
168
+ - !ruby/object:Gem::Version
169
+ version: '4.39'
170
+ type: :runtime
171
+ prerelease: false
172
+ version_requirements: !ruby/object:Gem::Requirement
173
+ requirements:
174
+ - - "~>"
175
+ - !ruby/object:Gem::Version
176
+ version: '4.39'
177
+ - !ruby/object:Gem::Dependency
178
+ name: amalgalite
179
+ requirement: !ruby/object:Gem::Requirement
180
+ requirements:
181
+ - - "~>"
182
+ - !ruby/object:Gem::Version
183
+ version: '1.5'
184
+ type: :runtime
185
+ prerelease: false
186
+ version_requirements: !ruby/object:Gem::Requirement
187
+ requirements:
188
+ - - "~>"
189
+ - !ruby/object:Gem::Version
190
+ version: '1.5'
191
+ - !ruby/object:Gem::Dependency
192
+ name: color
193
+ requirement: !ruby/object:Gem::Requirement
194
+ requirements:
195
+ - - "~>"
196
+ - !ruby/object:Gem::Version
197
+ version: '1.8'
198
+ type: :runtime
199
+ prerelease: false
200
+ version_requirements: !ruby/object:Gem::Requirement
201
+ requirements:
202
+ - - "~>"
203
+ - !ruby/object:Gem::Version
204
+ version: '1.8'
205
+ - !ruby/object:Gem::Dependency
206
+ name: paint
207
+ requirement: !ruby/object:Gem::Requirement
208
+ requirements:
209
+ - - "~>"
210
+ - !ruby/object:Gem::Version
211
+ version: '1.0'
212
+ - - ">="
213
+ - !ruby/object:Gem::Version
214
+ version: 1.0.1
215
+ type: :runtime
216
+ prerelease: false
217
+ version_requirements: !ruby/object:Gem::Requirement
218
+ requirements:
219
+ - - "~>"
220
+ - !ruby/object:Gem::Version
221
+ version: '1.0'
222
+ - - ">="
223
+ - !ruby/object:Gem::Version
224
+ version: 1.0.1
225
+ description:
226
+ email:
227
+ - levi@leviaul.com
228
+ executables:
229
+ - docker-pier
230
+ extensions: []
231
+ extra_rdoc_files: []
232
+ files:
233
+ - ".gitignore"
234
+ - Gemfile
235
+ - README.md
236
+ - Rakefile
237
+ - bin/console
238
+ - bin/setup
239
+ - docker-pier.gemspec
240
+ - exe/docker-pier
241
+ - lib/docker-pier.rb
242
+ - lib/docker-pier/log-stream.rb
243
+ - lib/docker-pier/log-stream/palette.rb
244
+ - lib/docker-pier/node.rb
245
+ - lib/docker-pier/pier.rb
246
+ - lib/docker-pier/version.rb
247
+ - lib/docker/swarm.rb
248
+ - lib/docker/swarm/node.rb
249
+ - lib/docker/swarm/service.rb
250
+ - lib/docker/swarm/task.rb
251
+ homepage: https://github.com/tsutsu/docker-pier
252
+ licenses: []
253
+ metadata: {}
254
+ post_install_message:
255
+ rdoc_options: []
256
+ require_paths:
257
+ - lib
258
+ required_ruby_version: !ruby/object:Gem::Requirement
259
+ requirements:
260
+ - - ">="
261
+ - !ruby/object:Gem::Version
262
+ version: '0'
263
+ required_rubygems_version: !ruby/object:Gem::Requirement
264
+ requirements:
265
+ - - ">="
266
+ - !ruby/object:Gem::Version
267
+ version: '0'
268
+ requirements: []
269
+ rubyforge_project:
270
+ rubygems_version: 2.5.1
271
+ signing_key:
272
+ specification_version: 4
273
+ summary: An alternative Docker client for dedicated Docker Swarm clusters
274
+ test_files: []