linecook-gem 0.6.10 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ae6258ac3d3dbf0e03203631737b4c98b8889cf7
4
- data.tar.gz: 9b0e1c1571e3e0ac148503e91df156e4517a0ebc
3
+ metadata.gz: 4f0a7a7d8d39c7428d53070807e962a4dfd8bdd9
4
+ data.tar.gz: 46f4d3352064af2c1e79f7d74cf4a7c92205f8aa
5
5
  SHA512:
6
- metadata.gz: c89fd5065cd7fa3e11fc2815dadef2d22f6637ad3264d66f3a588e8ee5a16cbc210b995edde1cf63f2a252ed3e44d35ed8274f789a4116a63cace1ff4d11f152
7
- data.tar.gz: 5e90c5ee82015828728efeb7184cdfb58477a762d93230f5998e7cb6bf5863f3b8f5e429e7dbb2cc96de70d4a9d964baa03af1eeb3d14745fbdd9286c051e4e3
6
+ metadata.gz: 7a94f90d4514e3c2f4f9414515cc01d4917b4ab9990f95ab9a9d37bad8fa6ded2e3a7793c6eb15f1994109a6f91205864601af862f763eeeccaa137b2e8622ca
7
+ data.tar.gz: 517faebb8e971e65698ec12bc8ac03b06fced0d3d167a8d4eff45c4672883bf7a2e50dc29dd91cde7a2923c57839c9551c1fb1dd62ce403d533a110d92f00964
data/lib/linecook-gem.rb CHANGED
@@ -1,10 +1,8 @@
1
1
  $LOAD_PATH.unshift(File.expand_path('../lib', __FILE__))
2
+ require 'fileutils'
2
3
 
3
4
  require 'active_support/all'
4
5
  require 'linecook-gem/version'
5
- require 'linecook-gem/util/config'
6
- require 'linecook-gem/util/downloader'
7
- require 'linecook-gem/image/manager'
8
- require 'linecook-gem/builder/manager'
9
- require 'linecook-gem/provisioner/manager'
10
- require 'linecook-gem/packager/manager'
6
+ require 'linecook-gem/config'
7
+ require 'linecook-gem/baker'
8
+ require 'linecook-gem/packager'
@@ -0,0 +1,110 @@
1
+ require 'thor/shell'
2
+ require 'fileutils'
3
+ require 'kitchen'
4
+ require 'kitchen/command/test'
5
+
6
+ require 'linecook-gem/image'
7
+ require 'linecook-gem/baker/docker'
8
+
9
+ module Linecook
10
+ module Baker
11
+ class Baker
12
+
13
+ def initialize(image, directory: nil)
14
+ @directory = File.expand_path(directory || Dir.pwd)
15
+ @image = image
16
+
17
+ Dir.chdir(@directory) do
18
+ load_config
19
+ munge_config
20
+ @driver = driver
21
+ end
22
+
23
+ end
24
+
25
+ def bake(snapshot: nil, upload: nil, keep: nil)
26
+
27
+ FileUtils.mkdir_p(File.join(Dir.pwd, '.kitchen'))
28
+
29
+ Dir.chdir(@directory) { @driver.converge }
30
+
31
+ snapshot ||= upload
32
+ if snapshot
33
+ puts 'Convergence complete, generating snapshot'
34
+ save
35
+ @image.upload if upload
36
+ end
37
+
38
+ rescue => e
39
+ puts e.message
40
+ puts e.backtrace
41
+ raise
42
+ ensure
43
+ if keep
44
+ puts "Preserving build #{@image.id}, you will need to clean it up manually."
45
+ else
46
+ puts "Cleaning up build #{@image.id}"
47
+ @driver.instance.destroy
48
+ end
49
+ end
50
+
51
+ def clean_kitchen
52
+ Dir.chdir(@directory) do
53
+ @config.instances.each do |instance|
54
+ instance.destroy
55
+ end
56
+ end
57
+ end
58
+
59
+ def save
60
+ puts "Saving image to #{@image.path}..."
61
+ clean
62
+ @driver.save
63
+ end
64
+
65
+ private
66
+
67
+ def clean
68
+ if @is_chef
69
+ @driver.exec('userdel -r -f kitchen')
70
+ @driver.exec('groupdel kitchen')
71
+ @driver.exec('rm -rf /etc/chef')
72
+ @driver.exec('rm -rf /tmp/kitchen')
73
+ @driver.exec('rm -f /etc/sudoers.d/kitchen')
74
+ end
75
+ end
76
+
77
+ def load_config
78
+ @config ||= begin
79
+
80
+ Kitchen::Config.new(
81
+ kitchen_root: @directory,
82
+ loader: Kitchen::Loader::YAML.new(
83
+ project_config: ENV['KITCHEN_YAML'] || File.join(@directory, '.kitchen.yml'),
84
+ local_config: ENV['KITCHEN_LOCAL_YAML'],
85
+ global_config: ENV['KITCHEN_GLOBAL_YAML']
86
+ )
87
+ )
88
+
89
+ end
90
+ end
91
+
92
+ def munge_config
93
+ data = @config.send(:data).instance_variable_get(:@data)
94
+ @is_chef = data[:provisioner][:name] =~ /chef/
95
+ if Linecook.config[:chef] &&
96
+ Linecook.config[:chef][:encrypted_data_bag_secret] && @is_chef
97
+ data[:provisioner][:data_bag_secret] = Linecook.config[:chef][:encrypted_data_bag_secret]
98
+ end
99
+
100
+ end
101
+
102
+ def driver
103
+ case @config.loader.read[:driver][:name]
104
+ when 'docker'
105
+ Linecook::Baker::Docker.new(@image, @config)
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,114 @@
1
+ require 'open3'
2
+ require 'tmpdir'
3
+ require 'fileutils'
4
+
5
+ require 'docker'
6
+
7
+ require 'linecook-gem/image'
8
+
9
+ module Linecook
10
+ module Baker
11
+ # FIXME - refactor into a base class with an interface
12
+ class Docker
13
+
14
+ attr_reader :config
15
+
16
+ def initialize(image, config)
17
+ @image = image
18
+ @config = config
19
+ munge_config
20
+ end
21
+
22
+
23
+ def save
24
+ container.stop
25
+ FileUtils.mkdir_p(File.dirname(@image.path))
26
+ pipe, _, _, _ = Open3.popen3("xz -T 0 -0 > #{@image.path}")
27
+
28
+ container.export do |chunk|
29
+ pipe.write(chunk)
30
+ end
31
+
32
+ pipe.flush
33
+ pipe.close
34
+ container.start
35
+ end
36
+
37
+ def instance
38
+ @instance ||= @config.instances.find {|x| @image.name == x.suite.name }
39
+ end
40
+
41
+ def converge
42
+ instance.converge
43
+ end
44
+
45
+ def stop
46
+ container.delete(force: true)
47
+ rescue ::Docker::Error::NotFoundError => e
48
+ puts e.message
49
+ end
50
+
51
+ def exec(command)
52
+ command = ['/bin/bash', '-c', command]
53
+ container.exec(command, tty: true)
54
+ end
55
+
56
+ def inherit(image)
57
+ puts "Inheriting from #{image.id}..."
58
+ import(image) unless image_exists?(image)
59
+ clean_older_images(image)
60
+ end
61
+
62
+ private
63
+ def container
64
+ @container ||= ::Docker::Container::get(@image.id)
65
+ end
66
+
67
+ def image_exists?(image)
68
+ ::Docker::Image.all.find do |docker_image|
69
+ docker_image.info['RepoTags'].first == "#{image.group}:#{image.tag}"
70
+ end
71
+ end
72
+
73
+ def clean_older_images(image)
74
+ puts "Cleaning up older images for #{image.group}..."
75
+ older_images(image).each do |old|
76
+ puts "Removing #{old.info['RepoTags'].first}"
77
+ old.remove(force: true)
78
+ end
79
+ end
80
+
81
+ def older_images(image)
82
+ ::Docker::Image.all.select do |docker_image|
83
+ group, tag = docker_image.info['RepoTags'].first.split(':')
84
+ group == image.group && tag.to_i < image.tag.to_i
85
+ end
86
+ end
87
+
88
+ def import(image)
89
+ puts "Importing #{image.id}..."
90
+ image.fetch
91
+ open(image.path) do |io|
92
+ ::Docker::Image.import_stream(repo: image.group, tag: image.tag, changes: ['CMD ["/sbin/init"]']) do
93
+ io.read(Excon.defaults[:chunk_size] * 10 ) || ""
94
+ end
95
+ end
96
+ end
97
+
98
+
99
+ # May the gods forgive us for all the rules this breaks
100
+ def munge_config
101
+ data = @config.send(:data).instance_variable_get(:@data)
102
+ data[:driver][:instance_name] = @image.id
103
+ suite = data[:suites].find{ |n| n[:name] == @image.name }
104
+ if suite && suite[:inherit]
105
+ inherited = Linecook::Image.new(suite[:inherit][:name], suite[:inherit][:group], suite[:inherit][:tag])
106
+ inherit(inherited)
107
+ data[:driver][:image] = "#{inherited.group}:#{inherited.tag}"
108
+ data[:driver][:provision_command] ||= []
109
+ data[:driver][:provision_command] << 'sed -i \'s/\(PasswordAuthentication no\)/#\1/g\' /etc/ssh/sshd_config'
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
@@ -1,130 +1,62 @@
1
1
  require 'thor'
2
2
  require 'linecook-gem'
3
3
 
4
- class Crypto < Thor
5
- desc 'keygen', 'Generate AES key for securing images'
4
+ class Image < Thor
5
+
6
+ desc 'keygen', 'Generate a new encryption key'
6
7
  def keygen
7
8
  puts Linecook::Crypto.keygen
8
9
  end
9
10
 
10
- desc 'decrypt IMAGE', ''
11
- def decrypt(image)
12
- puts Linecook::Crypto.new.decrypt_file(image)
13
- end
14
-
15
- desc 'encrypt IMAGE', ''
16
- def encrypt(image)
17
- puts Linecook::Crypto.new.encrypt_file(image)
18
- end
19
- end
20
-
21
- class Image < Thor
22
- desc 'crypto SUBCOMMAND', 'Manage image encryption'
23
- subcommand 'crypto', Crypto
24
11
 
25
- desc 'list', 'List images' # add remote flag
26
- method_option :type, type: :string, required: false, banner: 'ID', desc: 'Type of image to list', aliases: '-t'
27
- method_option :profile, type: :string, default: 'private', banner: 'PROFILE', desc: 'Profile (public/private) of image to list', enum: %w(public private), aliases: '-p'
12
+ desc 'list', 'List images'
28
13
  def list
29
- opts = options.symbolize_keys
30
- puts Linecook::ImageManager.list(**opts)
31
- end
32
-
33
- desc 'latest ID', 'Get the latest image by id' # add remote flag
34
- method_option :profile, type: :string, default: 'private', banner: 'PROFILE', desc: 'Profile (public/private) of image to list', enum: %w(public private), aliases: '-p'
35
- def latest(id)
36
- opts = options.symbolize_keys
37
- puts Linecook::ImageManager.latest(id, **opts)
14
+ puts Linecook::Image.new(nil, nil, nil).list
38
15
  end
39
16
 
40
17
  desc 'fetch', 'Fetch and decrypt an image'
41
- method_option :type, type: :string, required: false, banner: 'ID', desc: 'Type of image to list', aliases: '-t'
42
- method_option :latest, type: :boolean, default: false, desc: 'Use the latest build for type', aliases: '-l'
43
- method_option :name, type: :string, required: false, banner: 'NAME', desc: 'Name of the image to snapshot. Specify this, or --latest with a type', aliases: '-n'
18
+ method_option :name, type: :string, required: true, banner: 'NAME', desc: 'Name of the image to fetch.', aliases: '-n'
19
+ method_option :tag, type: :string, default: 'latest', banner: 'NAME', desc: 'Tag of the image to fetch.', aliases: '-t'
20
+ method_option :group, type: :string, required: false, banner: 'ID', desc: 'Group of image to list', aliases: '-g'
44
21
  def fetch
45
- fail 'Must specify image name or use latest' unless options[:name] || options[:latest]
46
- fail 'Must specify type if specifying latest' if options[:latest] && !options[:type]
47
-
48
- image = options[:latest] ? { type: options[:type], name: :latest } : options[:name].to_sym
49
- Linecook::ImageManager.fetch(image)
50
- end
51
-
52
- desc 'clean', 'Clean up cached images'
53
- method_option :type, type: :string, required: true, banner: 'ID', desc: 'Type of image to list', aliases: '-t'
54
- def clean
55
22
  opts = options.symbolize_keys
56
- puts Linecook::ImageManager.clean(**opts)
57
- end
58
-
59
- desc 'upload IMAGE', 'Upload an image'
60
- method_options name: :string
61
- def upload(image)
62
- Linecook::ImageManager.upload(image)
23
+ image = Linecook::Image.new(opts[:name], opts[:group], opts[:tag] )
24
+ image.fetch
63
25
  end
64
26
 
65
- desc 'url IMAGE', 'Get URL for image'
66
- method_option :type, type: :string, required: false, banner: 'ID', desc: 'Type of image to list', aliases: '-t'
67
- def url(image)
27
+ desc 'upload', 'Upload an image'
28
+ method_option :name, type: :string, required: true, banner: 'NAME', desc: 'Name of the image to upload.', aliases: '-n'
29
+ method_option :tag, type: :string, default: 'latest', banner: 'NAME', desc: 'Tag of the image to upload.', aliases: '-t'
30
+ method_option :group, type: :string, required: false, banner: 'ID', desc: 'Group of image to group', aliases: '-g'
31
+ def upload
68
32
  opts = options.symbolize_keys
69
- puts Linecook::ImageManager.url(image, **opts)
33
+ image = Linecook::Image.new(opts[:name], opts[:group], opts[:tag] )
34
+ image.upload
70
35
  end
71
36
 
72
37
  desc 'package', 'Package image'
73
- method_option :name, type: :string, required: false, banner: 'NAME', desc: 'Name of the image to snapshot. Specify this, or --latest with a type', aliases: '-n'
74
- method_option :latest, type: :boolean, default: false, desc: 'Use the latest build for type', aliases: '-l'
75
- method_option :ami, type: :boolean, default: true, desc: 'Create an ami', aliases: '-a'
76
- method_option :type, type: :string, required: false, banner: 'ID', desc: 'Type of image to snapshot', aliases: '-t'
38
+ method_option :name, type: :string, required: true, banner: 'NAME', desc: 'Name of the image to package.', aliases: '-n'
39
+ method_option :tag, type: :string, default: 'latest', banner: 'NAME', desc: 'Tag of the image to package.', aliases: '-t'
40
+ method_option :group, type: :string, required: false, banner: 'ID', desc: 'Group of image to package', aliases: '-g'
41
+ method_option :strategy, type: :string, default: 'packer', banner: 'STRATEGY', enum: ['packer', 'squashfs'], desc: 'Packaging strategy', aliases: '-s'
77
42
  def package
78
- fail 'Must specify image name or use latest' unless options[:name] || options[:latest]
79
- fail 'Must specify type if specifying latest' if options[:latest] && !options[:type]
80
- name = options[:latest] ? Linecook::ImageManager.latest(options[:type]) : options[:name]
81
- Linecook::Packager.package(name, type: options[:type], ami: options[:ami])
82
- end
83
- end
84
-
85
- class Builder < Thor
86
- desc 'info', 'Show builder info'
87
- def info
88
- puts Linecook::Builder.info
89
- end
90
-
91
- desc 'start', 'Start the builder'
92
- def start
93
- Linecook::Builder.start
94
- end
95
-
96
- desc 'stop', 'Stop the builder'
97
- def stop
98
- Linecook::Builder.stop
99
- end
100
-
101
- desc 'ip', 'Show the external ip address of the builder'
102
- def ip
103
- puts Linecook::Builder.ip
104
- end
105
- end
106
-
107
- class Build < Thor
108
- desc 'list', 'Show all builds'
109
- def list
110
- puts Linecook::Builder.builds
111
- end
112
-
113
- desc 'info', 'Show build info'
114
- def info
43
+ opts = options.symbolize_keys
44
+ image = Linecook::Image.new(opts[:name], opts[:group], opts[:tag])
45
+ Linecook::Packager.package(image, name: opts[:strategy])
115
46
  end
116
47
 
117
- desc 'ip', 'Show IP address for build'
118
- def ip
48
+ desc 'save', 'Save running build'
49
+ method_option :name, type: :string, required: true, banner: 'NAME', desc: 'Name of the image to package.', aliases: '-n'
50
+ method_option :tag, type: :string, default: 'latest', banner: 'NAME', desc: 'Tag of the image to package.', aliases: '-t'
51
+ method_option :group, type: :string, required: false, banner: 'ID', desc: 'Group of image to package', aliases: '-g'
52
+ method_option :directory, type: :string, required: false, banner: 'DIR', desc: 'Directory containing kitchen files', aliases: '-d'
53
+ def save
54
+ opts = options.symbolize_keys
55
+ image = Linecook::Image.new(opts[:name], opts[:group], opts[:tag])
56
+ baker = Linecook::Baker::Baker.new(image, directory: opts[:directory])
57
+ baker.save
119
58
  end
120
59
 
121
- desc 'snapshot', 'Take a snapshot of the build with NAME'
122
- method_option :name, type: :string, required: true, banner: 'ROLE_NAME', desc: 'Name of the role to build', aliases: '-n'
123
- method_option :resume, type: :boolean, default: false, desc: 'Resume the build after snapshotting it', aliases: '-r'
124
- def snapshot
125
- build = Linecook::Build.new(name, '')
126
- build.snapshot(save: true)
127
- end
128
60
  end
129
61
 
130
62
  class Linecook::CLI < Thor
@@ -132,24 +64,40 @@ class Linecook::CLI < Thor
132
64
  desc 'image SUBCOMMAND', 'Manage linecook images.'
133
65
  subcommand 'image', Image
134
66
 
135
- desc 'builder SUBCOMMAND', 'Manage the builder.'
136
- subcommand 'builder', Builder
137
-
138
- desc 'build SUBCOMMAND', 'Manage running and completed builds.'
139
- subcommand 'build', Build
140
-
141
67
  desc 'bake', 'Bake a new image.'
142
68
  method_option :name, type: :string, required: true, banner: 'ROLE_NAME', desc: 'Name of the role to build', aliases: '-n'
69
+ method_option :group, type: :string, required: false, banner: 'CLASS', desc: 'Optional class for a build', aliases: '-g'
143
70
  method_option :tag, type: :string, required: false, banner: 'TAG', desc: 'Optional tag for a build', aliases: '-t'
144
- method_option :id, type: :string, required: false, banner: 'ID', desc: 'Optional id for a build', aliases: '-i'
145
- method_option :keep, type: :boolean, default: true, desc: 'Keep the build running when done', aliases: '-k'
146
- method_option :clean, type: :boolean, default: false, desc: 'Clean up all build artifacts', aliases: '-c'
147
- method_option :build, type: :boolean, default: true, desc: 'Build the image', aliases: '-b'
71
+ method_option :directory, type: :string, required: false, banner: 'DIR', desc: 'Directory containing kitchen files', aliases: '-d'
72
+ method_option :keep, type: :boolean, default: false, desc: 'Keep the build running when done', aliases: '-k'
148
73
  method_option :snapshot, type: :boolean, default: false, desc: 'Snapshot the resulting build to create an image', aliases: '-s'
149
- method_option :upload, type: :boolean, default: false, desc: 'Upload the resulting build. Implies --snapshot and --encrypt.', aliases: '-u'
150
- method_option :package, type: :boolean, default: false, desc: 'Package the resulting image. Implies --upload, --snapshot and --encrypt.', aliases: '-p'
74
+ method_option :upload, type: :boolean, default: false, desc: 'Upload the resulting build. Implies --snapshot.', aliases: '-u'
75
+ #method_option :package, type: :boolean, default: false, desc: 'Package the resulting image. Implies --upload and --snapshot', aliases: '-p'
151
76
  def bake
152
77
  opts = options.symbolize_keys
153
- Linecook::Baker.bake(**opts)
78
+ image = Linecook::Image.new(opts[:name], opts[:group], opts[:tag])
79
+ baker = Linecook::Baker::Baker.new(image, directory: opts[:directory])
80
+ baker.bake(snapshot: opts[:snapshot], upload: opts[:upload], keep: opts[:keep])
81
+ end
82
+
83
+ desc 'clean', 'Clean up the kitchen, destroy all builds'
84
+ method_option :directory, type: :string, required: false, banner: 'DIR', desc: 'Directory containing kitchen files', aliases: '-d'
85
+ def clean
86
+ opts = options.symbolize_keys
87
+ image = Linecook::Image.new(nil, nil, nil)
88
+ baker = Linecook::Baker::Baker.new(image, directory: opts[:directory])
89
+ baker.clean_kitchen
90
+ end
91
+
92
+ desc 'man', 'Show the manpage'
93
+ def man
94
+ path = File.join(Gem::Specification.find_by_name('linecook-gem').gem_dir, 'man', 'LINECOOK.1' )
95
+ system("man #{path}")
154
96
  end
97
+
98
+ desc 'version', 'Print the current version'
99
+ def version
100
+ puts Linecook::VERSION
101
+ end
102
+
155
103
  end