docker-pier 0.1.0

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.
@@ -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: []