nutkins 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/README.md +10 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/nutkins +87 -0
- data/bin/setup +7 -0
- data/circle.yml +3 -0
- data/lib/nutkins/docker.rb +17 -0
- data/lib/nutkins/download.rb +35 -0
- data/lib/nutkins/version.rb +3 -0
- data/lib/nutkins.rb +207 -0
- data/nutkins.gemspec +35 -0
- metadata +129 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 6aa69760600c24b0d1d482aa132627423d82ab80
|
4
|
+
data.tar.gz: 38fde0b125bbf671cf093bbe95c0d3b0d94e75c2
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d4d063d8d03c2f2930f7f019479dd2a67de7a2687e93193f4f27c2fcd513198b4f77e4521e1399ee950a07ea0c85052078ec7715fe47f1132a38a8125df304b0
|
7
|
+
data.tar.gz: 22475d2f4ba6076a2946085c34ff97afd7d74206fd616998c8c18d6877e4dfbb31dd95eab857a3857f5bbb4bf2f60445438934dbc5b9afb315c7c08ba36c3cb9
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
# Nutkins
|
2
|
+
|
3
|
+
[![build status](https://circleci.com/gh/ohjames/nutkins.png)](https://circleci.com/gh/ohjames/nutkins)
|
4
|
+
|
5
|
+
Nutkins is a tool to manage and test a CoreOS cluster:
|
6
|
+
* Config for each service is stored in a directory including an optional `config.yaml` and a `Dockerfile`.
|
7
|
+
* Easy to create and run test instances of any service in a single command.
|
8
|
+
* Docker images and containers are managed to avoid the difficulties using the raw `docker` command provides around removing images.
|
9
|
+
* A wrapper around [sistero](https://github.com/ohjames/sistero), a profile based cluster management tool is provided to make adding new instances to a cluster trivial (currently only digital ocean is supported).
|
10
|
+
* A wrapper around `fleetctl` to make upgrading services easy.
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "nutkins"
|
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
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/nutkins
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'nutkins'
|
5
|
+
require 'moister'
|
6
|
+
require 'ostruct'
|
7
|
+
|
8
|
+
module Nutkins::Command
|
9
|
+
def self.run args
|
10
|
+
global_config = nil
|
11
|
+
command = nil
|
12
|
+
|
13
|
+
Moister::SubcommandOptionParser.new do |op|
|
14
|
+
op.banner = 'usage: nutkins [global options] command [command options]'
|
15
|
+
|
16
|
+
op.for_all do |op|
|
17
|
+
op.on_tail '-h', '--help', 'show this help message' do
|
18
|
+
puts op
|
19
|
+
exit
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
op.on '-p', '--project dir', 'override path to project', 'project_dir'
|
24
|
+
|
25
|
+
op.subcommand 'build,b *names', 'build docker image from dockerfile'
|
26
|
+
|
27
|
+
op.subcommand 'create,c *names', 'create container from image' do |subop|
|
28
|
+
subop.on '-p', '--preserve', 'preserve existing container', 'preserve'
|
29
|
+
end
|
30
|
+
|
31
|
+
op.subcommand 'delete,d *names', 'delete container corresponding to image'
|
32
|
+
op.subcommand 'delete-all', 'delete containers corresponding to all images'
|
33
|
+
|
34
|
+
op.subcommand 'run,r name', 'run created container' do |subop|
|
35
|
+
subop.on '-r', '--reuse', 'reuse previously created container', 'reuse'
|
36
|
+
subop.on '-s', '--shell', 'add bash shell to console', 'shell'
|
37
|
+
end
|
38
|
+
|
39
|
+
op.subcommand 'exec,e name *cmd', 'execute a command in a running container'
|
40
|
+
op.subcommand 'build-secret,B path', 'build secret files/volumes'
|
41
|
+
op.subcommand 'extract-secrets,X [*names]', 'extract secret files/volumes'
|
42
|
+
|
43
|
+
parsed_cfg = op.parse(args).to_h
|
44
|
+
|
45
|
+
global_config = OpenStruct.new parsed_cfg[:config]
|
46
|
+
command = parsed_cfg[:command]
|
47
|
+
end
|
48
|
+
|
49
|
+
unless command
|
50
|
+
puts 'please supply a command, see --help'
|
51
|
+
exit 1
|
52
|
+
end
|
53
|
+
|
54
|
+
nutkins = Nutkins::CloudManager.new(project_dir: global_config.project_dir)
|
55
|
+
config = OpenStruct.new global_config[command]
|
56
|
+
|
57
|
+
case command
|
58
|
+
when 'build'
|
59
|
+
names = config.names
|
60
|
+
if names.empty?
|
61
|
+
nutkins.build
|
62
|
+
else
|
63
|
+
config.names.each &nutkins.method(:build)
|
64
|
+
end
|
65
|
+
when 'create'
|
66
|
+
config.names.each do |name|
|
67
|
+
nutkins.create name, preserve: config.preserve
|
68
|
+
end
|
69
|
+
when 'delete'
|
70
|
+
config.names.each &nutkins.method(:delete)
|
71
|
+
when 'delete-all'
|
72
|
+
nutkins.delete_all
|
73
|
+
when 'run'
|
74
|
+
nutkins.run config.name, reuse: config.reuse, shell: config.shell
|
75
|
+
when 'exec'
|
76
|
+
nutkins.exec config.name, config.cmd
|
77
|
+
when 'build-secret'
|
78
|
+
nutkins.build_secret config.path
|
79
|
+
when 'extract-secrets'
|
80
|
+
nutkins.extract_secrets config.names
|
81
|
+
end
|
82
|
+
rescue RuntimeError => e
|
83
|
+
puts e.to_s
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
Nutkins::Command::run ARGV
|
data/bin/setup
ADDED
data/circle.yml
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
module Nutkins::Docker
|
2
|
+
def self.image_id_for_tag tag
|
3
|
+
regex = /^#{tag} +/
|
4
|
+
`docker images`.each_line do |line|
|
5
|
+
return line.split(' ')[2] if line =~ regex
|
6
|
+
end
|
7
|
+
nil
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.container_id_for_tag tag
|
11
|
+
regex = /^[0-9a-f]+ +#{tag} +/
|
12
|
+
`docker ps -a`.each_line do |line|
|
13
|
+
return line.split(' ')[0] if line =~ regex
|
14
|
+
end
|
15
|
+
nil
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Nutkins::Download
|
2
|
+
def self.download_file url, output
|
3
|
+
orig_url = url
|
4
|
+
tries = 10
|
5
|
+
while (tries -= 1) >= 0
|
6
|
+
response = Net::HTTP.get_response(URI(url))
|
7
|
+
case response
|
8
|
+
when Net::HTTPRedirection
|
9
|
+
url = response["location"]
|
10
|
+
else
|
11
|
+
open(output, "wb") do |file|
|
12
|
+
file.write(response.body)
|
13
|
+
end
|
14
|
+
return
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
raise "could not download #{orig_url}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.download_resources img_dir, resources
|
22
|
+
resources.each do |resource|
|
23
|
+
source = resource["source"]
|
24
|
+
dest = File.join(img_dir, resource["dest"])
|
25
|
+
unless File.exists? dest
|
26
|
+
FileUtils.mkdir_p File.dirname(dest)
|
27
|
+
print "downloading #{source}"
|
28
|
+
download_file source, dest
|
29
|
+
puts " - done"
|
30
|
+
mode = resource["mode"]
|
31
|
+
File.chmod(mode, dest) if mode
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/nutkins.rb
ADDED
@@ -0,0 +1,207 @@
|
|
1
|
+
require "yaml"
|
2
|
+
require "ostruct"
|
3
|
+
require "net/http"
|
4
|
+
require "uri"
|
5
|
+
require "fileutils"
|
6
|
+
require "json"
|
7
|
+
|
8
|
+
module Nutkins ; end
|
9
|
+
|
10
|
+
require "nutkins/docker"
|
11
|
+
require "nutkins/download"
|
12
|
+
require "nutkins/version"
|
13
|
+
|
14
|
+
# Must be somedomain.net instead of somedomain.net/, otherwise, it will throw exception.
|
15
|
+
module Nutkins
|
16
|
+
CONFIG_FILE_NAME = 'nutkins.yaml'
|
17
|
+
IMG_CONFIG_FILE_NAME = 'nutkin.yaml'
|
18
|
+
VOLUMES_PATH = 'volumes'
|
19
|
+
|
20
|
+
class CloudManager
|
21
|
+
def initialize(project_dir: nil)
|
22
|
+
@project_root = project_dir || Dir.pwd
|
23
|
+
cfg_path = File.join(@project_root, CONFIG_FILE_NAME)
|
24
|
+
if File.exists? cfg_path
|
25
|
+
@config = OpenStruct.new(YAML.load_file cfg_path)
|
26
|
+
else
|
27
|
+
@config = OpenStruct.new
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def build img_name = '.'
|
32
|
+
cfg = get_image_config img_name
|
33
|
+
img_dir = get_project_dir img_name
|
34
|
+
raise "directory `#{img_dir}' does not exist" unless Dir.exists? img_dir
|
35
|
+
|
36
|
+
if img_name == '.'
|
37
|
+
img_name = cfg["image"]
|
38
|
+
raise "nutkin.yaml requires image entry for `build .'" unless img_name
|
39
|
+
end
|
40
|
+
|
41
|
+
build_cfg = cfg["build"]
|
42
|
+
if build_cfg
|
43
|
+
# download each of the files in the resources section if it doesn't exist
|
44
|
+
resources = build_cfg["resources"]
|
45
|
+
Dowload.download_resources img_dir, resources if resources
|
46
|
+
end
|
47
|
+
|
48
|
+
tag = get_tag img_name
|
49
|
+
prev_image_id = Docker.image_id_for_tag tag
|
50
|
+
|
51
|
+
if run_docker "build", "-t", tag, img_dir
|
52
|
+
image_id = Docker.image_id_for_tag tag
|
53
|
+
if not prev_image_id.nil? and image_id != prev_image_id
|
54
|
+
puts "deleting previous image #{prev_image_id}"
|
55
|
+
run_docker "rmi", prev_image_id
|
56
|
+
end
|
57
|
+
else
|
58
|
+
raise "issue building docker image for #{img_name}"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def create img_name, preserve: false, docker_args: []
|
63
|
+
flags = []
|
64
|
+
cfg = get_image_config img_name
|
65
|
+
create_cfg = cfg["create"]
|
66
|
+
if create_cfg
|
67
|
+
(create_cfg["ports"] or []).each do |port|
|
68
|
+
flags.push '-p', "#{port}:#{port}"
|
69
|
+
end
|
70
|
+
|
71
|
+
img_dir = get_project_dir img_name
|
72
|
+
(create_cfg["volumes"] or []).each do |volume|
|
73
|
+
src, dest = volume.split ' -> '
|
74
|
+
src = File.absolute_path File.join(img_dir, VOLUMES_PATH, src)
|
75
|
+
flags.push '-v', "#{src}:#{dest}"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
tag = get_tag img_name
|
80
|
+
prev_container_id = Docker.container_id_for_tag tag unless preserve
|
81
|
+
puts "creating new docker image"
|
82
|
+
unless run_docker "create", "-it", *flags, tag, *docker_args
|
83
|
+
raise "failed to create `#{img_name}' container"
|
84
|
+
end
|
85
|
+
|
86
|
+
unless preserve
|
87
|
+
container_id = Docker.container_id_for_tag tag
|
88
|
+
if not prev_container_id.nil? and container_id != prev_container_id
|
89
|
+
puts "deleting previous container #{prev_container_id}"
|
90
|
+
run_docker "rm", prev_container_id
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
puts "created `#{img_name}' container"
|
95
|
+
end
|
96
|
+
|
97
|
+
def run img_name, reuse: false, shell: false
|
98
|
+
cfg = get_image_config img_name
|
99
|
+
tag = get_tag img_name
|
100
|
+
create_args = []
|
101
|
+
if shell
|
102
|
+
raise '--shell and --reuse arguments are incompatible' if reuse
|
103
|
+
|
104
|
+
# TODO: test for smell-baron
|
105
|
+
create_args = JSON.parse(`docker inspect #{tag}`)[0]["Config"]["Cmd"]
|
106
|
+
create_args.unshift '/bin/bash', '---'
|
107
|
+
create_args.unshift '-f' unless create_args[0] == '-f'
|
108
|
+
# TODO: provide version that doesn't require smell-baron
|
109
|
+
end
|
110
|
+
|
111
|
+
id = reuse && Docker.container_id_for_tag(tag)
|
112
|
+
unless id
|
113
|
+
create img_name, docker_args: create_args
|
114
|
+
id = Docker.container_id_for_tag tag
|
115
|
+
raise "couldn't create container to run `#{img_name}'" unless id
|
116
|
+
end
|
117
|
+
|
118
|
+
Kernel.exec "docker", "start", "-ai", id
|
119
|
+
end
|
120
|
+
|
121
|
+
def delete img_name
|
122
|
+
puts "TODO: delete #{img_name}"
|
123
|
+
end
|
124
|
+
|
125
|
+
def delete_all
|
126
|
+
puts "TODO: delete_all"
|
127
|
+
end
|
128
|
+
|
129
|
+
def build_secret path
|
130
|
+
secret = path
|
131
|
+
path_is_dir = Dir.exists? path
|
132
|
+
if path_is_dir
|
133
|
+
secret += '.tar'
|
134
|
+
system "tar", "cf", secret, "-C", File.dirname(path), File.basename(path)
|
135
|
+
end
|
136
|
+
|
137
|
+
loop do
|
138
|
+
puts "enter passphrase for #{secret}"
|
139
|
+
break if system 'gpg', '-c', secret
|
140
|
+
end
|
141
|
+
|
142
|
+
File.unlink secret if path_is_dir
|
143
|
+
end
|
144
|
+
|
145
|
+
def extract_secrets img_names
|
146
|
+
with_current = img_names.empty?
|
147
|
+
img_names = get_image_names(img_names)
|
148
|
+
img_names.push '.' if with_current
|
149
|
+
|
150
|
+
img_names.each do |img_name|
|
151
|
+
get_secrets(img_name).each do |secret|
|
152
|
+
loop do
|
153
|
+
puts "enter passphrase for #{secret}"
|
154
|
+
break if system 'gpg', secret
|
155
|
+
end
|
156
|
+
|
157
|
+
secret = secret[0..-5]
|
158
|
+
if File.extname(secret) == '.tar'
|
159
|
+
system "tar", "xf", secret, "-C", File.dirname(secret)
|
160
|
+
File.unlink secret
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def exec img_name, *cmd
|
167
|
+
puts "TODO: exec #{img_name}: #{cmd.join ' '}"
|
168
|
+
end
|
169
|
+
|
170
|
+
private
|
171
|
+
def get_image_config path
|
172
|
+
img_cfg_path = File.join get_project_dir(path), IMG_CONFIG_FILE_NAME
|
173
|
+
img_cfg = File.exists?(img_cfg_path) ? YAML.load_file(img_cfg_path) : {}
|
174
|
+
@repository = img_cfg['repository'] || @config.repository
|
175
|
+
img_cfg
|
176
|
+
end
|
177
|
+
|
178
|
+
def get_project_dir path
|
179
|
+
path == '.' ? @project_root : File.join(@project_root, path)
|
180
|
+
end
|
181
|
+
|
182
|
+
def get_tag tag
|
183
|
+
raise "command requires `repository' entry in nutkins.yaml or nutkin.yaml" if @repository.nil?
|
184
|
+
@repository + '/' + tag
|
185
|
+
end
|
186
|
+
|
187
|
+
def get_image_names img_names
|
188
|
+
if img_names.empty?
|
189
|
+
Dir.glob("#{@project_root}/*/Dockerfile").map do |path|
|
190
|
+
File.basename File.dirname(path)
|
191
|
+
end
|
192
|
+
else
|
193
|
+
img_names
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
# can supply img_name or . for project root
|
198
|
+
def get_secrets img_name
|
199
|
+
img_dir = get_project_dir img_name
|
200
|
+
Dir.glob("#{img_dir}/{volumes,secrets}/*.gpg")
|
201
|
+
end
|
202
|
+
|
203
|
+
def run_docker *args
|
204
|
+
system 'docker', *args
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
data/nutkins.gemspec
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'nutkins/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "nutkins"
|
8
|
+
spec.version = Nutkins::VERSION
|
9
|
+
spec.authors = ["James Pike"]
|
10
|
+
spec.email = ["github@chilon.net"]
|
11
|
+
|
12
|
+
spec.summary = %q{CoreOS cluster management tool.}
|
13
|
+
spec.description = spec.summary
|
14
|
+
spec.homepage = "http://github.com/ohjames/nutkins"
|
15
|
+
|
16
|
+
# Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
|
17
|
+
# delete this section to allow pushing this gem to any host.
|
18
|
+
if spec.respond_to?(:metadata)
|
19
|
+
spec.metadata['allowed_push_host'] = "https://rubygems.org"
|
20
|
+
else
|
21
|
+
raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
|
22
|
+
end
|
23
|
+
|
24
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
25
|
+
spec.bindir = "exe"
|
26
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
27
|
+
spec.require_paths = ["lib"]
|
28
|
+
|
29
|
+
spec.add_development_dependency "bundler", "~> 1.9"
|
30
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
31
|
+
spec.add_development_dependency "rspec"
|
32
|
+
|
33
|
+
spec.add_dependency "sistero", "~> 0.4.4"
|
34
|
+
spec.add_dependency "moister", "~> 0.3.0"
|
35
|
+
end
|
metadata
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: nutkins
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- James Pike
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-01-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.9'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.9'
|
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: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: sistero
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.4.4
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.4.4
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: moister
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 0.3.0
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 0.3.0
|
83
|
+
description: CoreOS cluster management tool.
|
84
|
+
email:
|
85
|
+
- github@chilon.net
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- ".gitignore"
|
91
|
+
- ".rspec"
|
92
|
+
- ".travis.yml"
|
93
|
+
- Gemfile
|
94
|
+
- README.md
|
95
|
+
- Rakefile
|
96
|
+
- bin/console
|
97
|
+
- bin/nutkins
|
98
|
+
- bin/setup
|
99
|
+
- circle.yml
|
100
|
+
- lib/nutkins.rb
|
101
|
+
- lib/nutkins/docker.rb
|
102
|
+
- lib/nutkins/download.rb
|
103
|
+
- lib/nutkins/version.rb
|
104
|
+
- nutkins.gemspec
|
105
|
+
homepage: http://github.com/ohjames/nutkins
|
106
|
+
licenses: []
|
107
|
+
metadata:
|
108
|
+
allowed_push_host: https://rubygems.org
|
109
|
+
post_install_message:
|
110
|
+
rdoc_options: []
|
111
|
+
require_paths:
|
112
|
+
- lib
|
113
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
119
|
+
requirements:
|
120
|
+
- - ">="
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: '0'
|
123
|
+
requirements: []
|
124
|
+
rubyforge_project:
|
125
|
+
rubygems_version: 2.5.1
|
126
|
+
signing_key:
|
127
|
+
specification_version: 4
|
128
|
+
summary: CoreOS cluster management tool.
|
129
|
+
test_files: []
|