Module: Spoon

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

Constant Summary collapse

VERSION =
"0.4.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)
  "#{options[:prefix]}#{name}"
end

.D(message) ⇒ Object



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

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

.docker_urlObject



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

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

.get_all_containersObject



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

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

.get_container(name) ⇒ Object



241
242
243
244
245
246
247
248
249
250
251
252
# File 'lib/spoon.rb', line 241

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



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

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

.get_running_containersObject



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

def self.get_running_containers
  Docker::Container.all
end

.host_available?(hostname, port) ⇒ Boolean

Returns:

  • (Boolean)


265
266
267
268
269
270
271
272
273
274
275
276
# File 'lib/spoon.rb', line 265

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



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

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



89
90
91
92
93
94
95
# File 'lib/spoon.rb', line 89

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



148
149
150
151
# File 'lib/spoon.rb', line 148

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



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

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



254
255
256
257
258
259
260
261
262
263
# File 'lib/spoon.rb', line 254

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



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

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)


212
213
214
# File 'lib/spoon.rb', line 212

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

.instance_listObject



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

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

.instance_network(name) ⇒ Object



171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/spoon.rb', line 171

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



216
217
218
219
220
221
222
223
224
225
226
227
# File 'lib/spoon.rb', line 216

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



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

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

.is_running?(container) ⇒ Boolean

Returns:

  • (Boolean)


161
162
163
164
165
166
167
168
169
# File 'lib/spoon.rb', line 161

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


115
116
117
# File 'lib/spoon.rb', line 115

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


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

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
# File 'lib/spoon.rb', line 69

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

.strip_slash(name) ⇒ Object



153
154
155
156
157
158
159
# File 'lib/spoon.rb', line 153

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