Module: Spoon

Includes:
Methadone::CLILogging, Methadone::Main, Methadone::SH
Defined in:
lib/spoon.rb,
lib/spoon/version.rb

Constant Summary collapse

VERSION =
"0.6.0"

Class Method Summary collapse

Class Method Details

.apply_prefix(name) ⇒ Object



67
68
69
# File 'lib/spoon.rb', line 67

def self.apply_prefix(name)
  "#{options[:prefix]}#{name}"
end

.D(message) ⇒ Object



291
292
293
294
295
# File 'lib/spoon.rb', line 291

def self.D(message)
  if options[:debug]
    puts "D: #{message}"
  end
end

.docker_urlObject



283
284
285
# File 'lib/spoon.rb', line 283

def self.docker_url
  Docker.url = options[:url]
end

.get_all_containersObject



234
235
236
# File 'lib/spoon.rb', line 234

def self.get_all_containers
  Docker::Container.all(:all => true)
end

.get_container(name) ⇒ Object



246
247
248
249
250
251
252
253
254
255
256
257
# File 'lib/spoon.rb', line 246

def self.get_container(name)
  docker_url
  container_list = get_all_containers

  l_name = strip_slash(name)
  container_list.each do |container|
    if container.info["Names"].first.to_s == "/#{l_name}"
      return container
    end
  end
  return nil
end

.get_port(port, container) ⇒ Object



287
288
289
# File 'lib/spoon.rb', line 287

def self.get_port(port, container)
  container.json['NetworkSettings']['Ports']["#{port}/tcp"].first['HostPort']
end

.get_running_containersObject



238
239
240
# File 'lib/spoon.rb', line 238

def self.get_running_containers
  Docker::Container.all
end

.host_available?(hostname, port) ⇒ Boolean

Returns:

  • (Boolean)


270
271
272
273
274
275
276
277
278
279
280
281
# File 'lib/spoon.rb', line 270

def self.host_available?(hostname, port)
  socket = TCPSocket.new(hostname, port)
  IO.select([socket], nil, nil, 5)
rescue SocketError, Errno::ECONNREFUSED,
  Errno::EHOSTUNREACH, Errno::ENETUNREACH, IOError
  sleep(0.25)
  false
rescue Errno::EPERM, Errno::ETIMEDOUT
  false
ensure
  socket && socket.close
end

.image_buildObject



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/spoon.rb', line 75

def self.image_build
  # Run pre-build commands
  options["pre-build-commands"].each do |command|
    sh command
  end unless options["pre-build-commands"].nil?
  D "pre-build commands complete, building Docker image"

  docker_url
  build_opts = { 't' => options[:image], 'rm' => true }
  docker_connection = ::Docker::Connection.new(options[:url], :read_timeout => 3000)

  Docker::Image.build_from_dir(options[:builddir], build_opts, docker_connection) do |chunk|
    print_docker_response(chunk)
  end
end

.image_listObject



91
92
93
94
95
96
97
# File 'lib/spoon.rb', line 91

def self.image_list
  docker_url
  Docker::Image.all.each do |image|
    next if image.info["RepoTags"] == ["<none>:<none>"]
    puts "Image: #{image.info["RepoTags"]}"
  end
end

.image_name(container) ⇒ Object



151
152
153
154
# File 'lib/spoon.rb', line 151

def self.image_name(container)
  env = Hash[container.json['Config']['Env'].collect { |v| v.split('=') }]
  return env['IMAGE_NAME'] || container.json['Config']['Image'].split(':').first
end

.instance_connect(name, command = '') ⇒ Object



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/spoon.rb', line 121

def self.instance_connect(name, command='')
  docker_url
  if not instance_exists? name
    puts "The `#{name}` container doesn't exist, creating..."
    instance_create(name)
  end

  container = get_container(name)
  unless is_running?(container)
    instance_start(container)
  end

  puts "Connecting to `#{name}`"
  instance_ssh(name, command)
end

.instance_create(name) ⇒ Object



259
260
261
262
263
264
265
266
267
268
# File 'lib/spoon.rb', line 259

def self.instance_create(name)
  docker_url
  container = Docker::Container.create({
    'Image' => options[:image],
    'name' => name,
    'Entrypoint' => 'runit',
    'Hostname' => remove_prefix(name)
  })
  container = container.start({ 'PublishAllPorts' => true })
end

.instance_destroy(name) ⇒ Object



192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
# File 'lib/spoon.rb', line 192

def self.instance_destroy(name)
  docker_url
  container = get_container(name)

  if container
    puts "Destroying #{name}"
    begin
      container.kill
    rescue
      puts "Failed to kill container #{container.id}"
    end

    container.wait(10)

    begin
      container.delete(:force => true)
    rescue
      puts "Failed to remove container #{container.id}"
    end
    puts "Done!"
  else
    puts "No container named: #{name}"
  end
end

.instance_exists?(name) ⇒ Boolean

Returns:

  • (Boolean)


217
218
219
# File 'lib/spoon.rb', line 217

def self.instance_exists?(name)
  get_container(name)
end

.instance_listObject



137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/spoon.rb', line 137

def self.instance_list
  docker_url
  puts "List of available spoon containers:"
  container_list = get_all_containers.select { |c| c.info["Names"].first.to_s.start_with? "/#{options[:prefix]}" }
                                     .sort { |c1, c2| c1.info["Names"].first.to_s <=> c2.info["Names"].first.to_s }
  max_width_container_name = remove_prefix(container_list.max_by {|c| c.info["Names"].first.to_s.length }.info["Names"].first.to_s)
  max_name_width = max_width_container_name.length
  container_list.each do |container|
    name = container.info["Names"].first.to_s
    running = is_running?(container) ? Rainbow("Running").green : Rainbow("Stopped").red
    puts "#{remove_prefix(name)} [ #{running} ]".rjust(max_name_width + 22) + " " + Rainbow(image_name(container)).yellow
  end
end

.instance_network(name) ⇒ Object



174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/spoon.rb', line 174

def self.instance_network(name)
  docker_url

  container = get_container(name)

  if is_running?(container)
    host = URI.parse(options[:url]).host
    puts "Host: #{host}"
    ports = container.json['NetworkSettings']['Ports']
    ports.each do |p_name, p_port|
      tcp_name = p_name.split('/')[0]
      puts "#{tcp_name} -> #{p_port.first['HostPort']}"
    end
  else
    puts "Container is not running, cannot show ports"
  end
end

.instance_ssh(name, command = '') ⇒ Object



221
222
223
224
225
226
227
228
229
230
231
232
# File 'lib/spoon.rb', line 221

def self.instance_ssh(name, command='')
  container = get_container(name)
  host = URI.parse(options[:url]).host
  if container
    ssh_command = "\"#{command}\"" if not command.empty?
    ssh_port = get_port('22', container)
    puts "Waiting for #{name}:#{ssh_port}..." until host_available?(host, ssh_port)
    exec("ssh -t -o StrictHostKeyChecking=no -p #{ssh_port} pairing@#{host} #{ssh_command}")
  else
    puts "No container named: #{container.inspect}"
  end
end

.instance_start(container) ⇒ Object



242
243
244
# File 'lib/spoon.rb', line 242

def self.instance_start(container)
  container.start!
end

.is_running?(container) ⇒ Boolean

Returns:

  • (Boolean)


164
165
166
167
168
169
170
171
172
# File 'lib/spoon.rb', line 164

def self.is_running?(container)
  container = Docker::Container.get(container.info["id"])
  status = container.info["State"]["Running"] || nil
  unless status.nil?
    return status
  else
    return false
  end
end


117
118
119
# File 'lib/spoon.rb', line 117

def self.print_docker_response(json)
  print_parsed_response(JSON.parse(json))
end


99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/spoon.rb', line 99

def self.print_parsed_response(response)
  case response
  when Hash
    response.each do |key, value|
      case key
      when 'stream'
        puts value
      else
        puts "#{key}: #{value}"
      end
    end
  when Array
    response.each do |hash|
      print_parsed_response(hash)
    end
  end
end

.remove_prefix(name) ⇒ Object



71
72
73
# File 'lib/spoon.rb', line 71

def self.remove_prefix(name)
  name.gsub(/\/?#{options[:prefix]}/, '')
end

.strip_slash(name) ⇒ Object



156
157
158
159
160
161
162
# File 'lib/spoon.rb', line 156

def self.strip_slash(name)
  if name.start_with? "/"
    name[1..-1]
  else
    name
  end
end