linecook-gem 0.6.10 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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