Module: Spoon

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

Constant Summary collapse

VERSION =
"0.2.0"

Class Method Summary collapse

Class Method Details

.apply_prefix(name) ⇒ Object



65
66
67
# File 'lib/spoon.rb', line 65

def self.apply_prefix(name)
  "spoon-#{name}"
end

.D(message) ⇒ Object



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

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

.docker_url ⇒ Object



275
276
277
# File 'lib/spoon.rb', line 275

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

.get_all_containers ⇒ Object



226
227
228
# File 'lib/spoon.rb', line 226

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

.get_container(name) ⇒ Object



238
239
240
241
242
243
244
245
246
247
248
249
# File 'lib/spoon.rb', line 238

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



279
280
281
# File 'lib/spoon.rb', line 279

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

.get_running_containers ⇒ Object



230
231
232
# File 'lib/spoon.rb', line 230

def self.get_running_containers
  Docker::Container.all
end

.host_available?(hostname, port) ⇒ Boolean

Returns:

  • (Boolean)


262
263
264
265
266
267
268
269
270
271
272
273
# File 'lib/spoon.rb', line 262

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_build ⇒ Object



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

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::Image.build_from_dir(options[:builddir], build_opts) do |chunk|
    print_docker_response(chunk)
  end
end

.image_list ⇒ Object



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

.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



251
252
253
254
255
256
257
258
259
260
# File 'lib/spoon.rb', line 251

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



184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# File 'lib/spoon.rb', line 184

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)


209
210
211
# File 'lib/spoon.rb', line 209

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

.instance_list ⇒ Object



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

def self.instance_list
  docker_url
  puts "List of available spoon containers:"
  container_list = get_all_containers
  container_list.each do |container|
    name = container.info["Names"].first.to_s
    if name.start_with? "/#{options[:prefix]}"
      running = is_running?(container) ? "Running" : "Stopped"
      puts "#{remove_prefix(name)} [ #{running} ]".rjust(40)
    end
  end
end

.instance_network(name) ⇒ Object



168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'lib/spoon.rb', line 168

def self.instance_network(name)
  docker_url

  container = get_container(name)

  if is_running?(container)
    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



213
214
215
216
217
218
219
220
221
222
223
224
# File 'lib/spoon.rb', line 213

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



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

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

.is_running?(container) ⇒ Boolean

Returns:

  • (Boolean)


158
159
160
161
162
163
164
165
166
# File 'lib/spoon.rb', line 158

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

.parse_config(config_file) ⇒ Object



61
62
63
# File 'lib/spoon.rb', line 61

def self.parse_config(config_file)
  eval(File.open(config_file).read)
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



69
70
71
72
73
74
75
# File 'lib/spoon.rb', line 69

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

.strip_slash(name) ⇒ Object



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

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