knife-container 0.2.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 +7 -0
- data/.gitignore +23 -0
- data/.rspec +1 -0
- data/.travis.yml +4 -0
- data/CONTRIBUTING.md +152 -0
- data/Gemfile +10 -0
- data/LICENSE +201 -0
- data/README.md +59 -0
- data/Rakefile +16 -0
- data/knife-container.gemspec +31 -0
- data/lib/chef/knife/container_docker_build.rb +243 -0
- data/lib/chef/knife/container_docker_init.rb +262 -0
- data/lib/knife-container/chef_runner.rb +83 -0
- data/lib/knife-container/command.rb +45 -0
- data/lib/knife-container/generator.rb +88 -0
- data/lib/knife-container/helpers.rb +16 -0
- data/lib/knife-container/skeletons/knife_container/files/default/plugins/docker_container.rb +37 -0
- data/lib/knife-container/skeletons/knife_container/metadata.rb +7 -0
- data/lib/knife-container/skeletons/knife_container/recipes/docker_init.rb +181 -0
- data/lib/knife-container/skeletons/knife_container/templates/default/berksfile.erb +5 -0
- data/lib/knife-container/skeletons/knife_container/templates/default/config.rb.erb +16 -0
- data/lib/knife-container/skeletons/knife_container/templates/default/dockerfile.erb +9 -0
- data/lib/knife-container/skeletons/knife_container/templates/default/dockerignore.erb +0 -0
- data/lib/knife-container/skeletons/knife_container/templates/default/node_name.erb +1 -0
- data/lib/knife-container/version.rb +5 -0
- data/spec/functional/docker_container_ohai_spec.rb +20 -0
- data/spec/functional/fixtures/ohai/Dockerfile +3 -0
- data/spec/spec_helper.rb +35 -0
- data/spec/test_helpers.rb +59 -0
- data/spec/unit/container_docker_build_spec.rb +325 -0
- data/spec/unit/container_docker_init_spec.rb +464 -0
- data/spec/unit/fixtures/.chef/encrypted_data_bag_secret +0 -0
- data/spec/unit/fixtures/.chef/trusted_certs/chef_example_com.crt +0 -0
- data/spec/unit/fixtures/.chef/validator.pem +1 -0
- data/spec/unit/fixtures/Berksfile +3 -0
- data/spec/unit/fixtures/cookbooks/dummy/metadata.rb +0 -0
- data/spec/unit/fixtures/cookbooks/nginx/metadata.rb +0 -0
- data/spec/unit/fixtures/environments/dev.json +0 -0
- data/spec/unit/fixtures/nodes/demo.json +0 -0
- data/spec/unit/fixtures/roles/base.json +0 -0
- data/spec/unit/fixtures/site-cookbooks/apt/metadata.rb +0 -0
- metadata +232 -0
@@ -0,0 +1,83 @@
|
|
1
|
+
#
|
2
|
+
# Copyright:: Copyright (c) 2014 Chef Software Inc.
|
3
|
+
# License:: Apache License, Version 2.0
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
#
|
17
|
+
|
18
|
+
require 'chef'
|
19
|
+
|
20
|
+
module KnifeContainer
|
21
|
+
# An adapter to chef's APIs to kick off a chef-client run.
|
22
|
+
class ChefRunner
|
23
|
+
|
24
|
+
attr_reader :cookbook_path
|
25
|
+
attr_reader :run_list
|
26
|
+
|
27
|
+
def initialize(cookbook_path, run_list)
|
28
|
+
@cookbook_path = cookbook_path
|
29
|
+
@run_list = run_list
|
30
|
+
@formatter = nil
|
31
|
+
@ohai = nil
|
32
|
+
end
|
33
|
+
|
34
|
+
def converge
|
35
|
+
configure
|
36
|
+
Chef::Runner.new(run_context).converge
|
37
|
+
end
|
38
|
+
|
39
|
+
def run_context
|
40
|
+
@run_context ||= policy.setup_run_context
|
41
|
+
end
|
42
|
+
|
43
|
+
def policy
|
44
|
+
return @policy_builder if @policy_builder
|
45
|
+
|
46
|
+
@policy_builder = Chef::PolicyBuilder::ExpandNodeObject.new("knife_container", ohai.data, {}, nil, formatter)
|
47
|
+
@policy_builder.load_node
|
48
|
+
@policy_builder.build_node
|
49
|
+
@policy_builder.node.run_list(*run_list)
|
50
|
+
@policy_builder.expand_run_list
|
51
|
+
@policy_builder
|
52
|
+
end
|
53
|
+
|
54
|
+
def formatter
|
55
|
+
@formatter ||= Chef::Formatters.new(:doc, stdout, stderr)
|
56
|
+
end
|
57
|
+
|
58
|
+
def configure
|
59
|
+
Chef::Config.solo = true
|
60
|
+
Chef::Config.cookbook_path = cookbook_path
|
61
|
+
Chef::Config.color = true
|
62
|
+
Chef::Config.diff_disabled = true
|
63
|
+
end
|
64
|
+
|
65
|
+
def ohai
|
66
|
+
return @ohai if @ohai
|
67
|
+
|
68
|
+
@ohai = Ohai::System.new
|
69
|
+
@ohai.all_plugins(["platform", "platform_version"])
|
70
|
+
@ohai
|
71
|
+
end
|
72
|
+
|
73
|
+
def stdout
|
74
|
+
$stdout
|
75
|
+
end
|
76
|
+
|
77
|
+
def stderr
|
78
|
+
$stderr
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
@@ -0,0 +1,45 @@
|
|
1
|
+
#
|
2
|
+
# Copyright:: Copyright (c) 2014 Chef Software Inc.
|
3
|
+
# License:: Apache License, Version 2.0
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
#
|
17
|
+
|
18
|
+
require 'knife-container/generator'
|
19
|
+
require 'knife-container/chef_runner'
|
20
|
+
|
21
|
+
module KnifeContainer
|
22
|
+
module Command
|
23
|
+
|
24
|
+
# An instance of ChefRunner. Calling ChefRunner#converge will trigger
|
25
|
+
# convergence and generate the desired code.
|
26
|
+
def chef_runner
|
27
|
+
@chef_runner ||= ChefRunner.new(docker_cookbook_path, ["knife_container::#{recipe}"])
|
28
|
+
end
|
29
|
+
|
30
|
+
# Path to the directory where the code_generator cookbook is located.
|
31
|
+
# For now, this is hard coded to the 'skeletons' directory in this
|
32
|
+
# repo.
|
33
|
+
def docker_cookbook_path
|
34
|
+
File.expand_path("../skeletons", __FILE__)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Delegates to `Generator.context`, the singleton instance of
|
38
|
+
# Generator::Context
|
39
|
+
def generator_context
|
40
|
+
Generator.context
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
@@ -0,0 +1,88 @@
|
|
1
|
+
#
|
2
|
+
# Copyright:: Copyright (c) 2014 Chef Software Inc.
|
3
|
+
# License:: Apache License, Version 2.0
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
#
|
17
|
+
|
18
|
+
module KnifeContainer
|
19
|
+
|
20
|
+
module Generator
|
21
|
+
|
22
|
+
class Context
|
23
|
+
|
24
|
+
attr_accessor :dockerfile_name
|
25
|
+
attr_accessor :dockerfiles_path
|
26
|
+
attr_accessor :base_image
|
27
|
+
attr_accessor :chef_client_mode
|
28
|
+
attr_accessor :run_list
|
29
|
+
attr_accessor :cookbook_path
|
30
|
+
attr_accessor :role_path
|
31
|
+
attr_accessor :node_path
|
32
|
+
attr_accessor :environment_path
|
33
|
+
attr_accessor :chef_server_url
|
34
|
+
attr_accessor :validation_key
|
35
|
+
attr_accessor :validation_client_name
|
36
|
+
attr_accessor :trusted_certs_dir
|
37
|
+
attr_accessor :encrypted_data_bag_secret
|
38
|
+
attr_accessor :first_boot
|
39
|
+
attr_accessor :berksfile
|
40
|
+
attr_accessor :generate_berksfile
|
41
|
+
attr_accessor :run_berks
|
42
|
+
attr_accessor :force_build
|
43
|
+
attr_accessor :include_credentials
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.reset
|
48
|
+
@context = nil
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.context
|
52
|
+
@context ||= Context.new
|
53
|
+
end
|
54
|
+
|
55
|
+
module TemplateHelper
|
56
|
+
|
57
|
+
def self.delegate_to_app_context(name)
|
58
|
+
define_method(name) do
|
59
|
+
KnifeContainer::Generator.context.public_send(name)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# delegate all the attributes of app_config
|
64
|
+
delegate_to_app_context :dockerfile_name
|
65
|
+
delegate_to_app_context :dockerfiles_path
|
66
|
+
delegate_to_app_context :base_image
|
67
|
+
delegate_to_app_context :chef_client_mode
|
68
|
+
delegate_to_app_context :run_list
|
69
|
+
delegate_to_app_context :cookbook_path
|
70
|
+
delegate_to_app_context :role_path
|
71
|
+
delegate_to_app_context :node_path
|
72
|
+
delegate_to_app_context :environment_path
|
73
|
+
delegate_to_app_context :chef_server_url
|
74
|
+
delegate_to_app_context :validation_key
|
75
|
+
delegate_to_app_context :validation_client_name
|
76
|
+
delegate_to_app_context :trusted_certs_dir
|
77
|
+
delegate_to_app_context :encrypted_data_bag_secret
|
78
|
+
delegate_to_app_context :first_boot
|
79
|
+
delegate_to_app_context :berksfile
|
80
|
+
delegate_to_app_context :generate_berksfile
|
81
|
+
delegate_to_app_context :run_berks
|
82
|
+
delegate_to_app_context :force_build
|
83
|
+
delegate_to_app_context :include_credentials
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
|
2
|
+
module KnifeContainer
|
3
|
+
|
4
|
+
module Helpers
|
5
|
+
#
|
6
|
+
# Generates a short, but random UID for instances.
|
7
|
+
#
|
8
|
+
# @return [String]
|
9
|
+
#
|
10
|
+
def random_uid
|
11
|
+
require 'securerandom' unless defined?(SecureRandom)
|
12
|
+
SecureRandom.hex(3)
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'docker' # it gets this from chef-init
|
2
|
+
|
3
|
+
Ohai.plugin(:DockerContainer) do
|
4
|
+
provides "docker_container"
|
5
|
+
|
6
|
+
def container_id
|
7
|
+
shell_out("hostname").stdout.strip
|
8
|
+
end
|
9
|
+
|
10
|
+
def looks_like_docker?
|
11
|
+
hint?('docker_container') || !!Docker.version && !!Docker::Container.get(container_id)
|
12
|
+
end
|
13
|
+
|
14
|
+
##
|
15
|
+
# The format of the data is collection is the inspect API
|
16
|
+
# http://docs.docker.io/reference/api/docker_remote_api_v1.11/#inspect-a-container
|
17
|
+
#
|
18
|
+
collect_data do
|
19
|
+
metadata_from_hints = hint?('docker_container')
|
20
|
+
|
21
|
+
if looks_like_docker?
|
22
|
+
Ohai::Log.debug("looks_like_docker? == true")
|
23
|
+
docker_container Mash.new
|
24
|
+
|
25
|
+
if metadata_from_hints
|
26
|
+
Ohai::Log.debug("docker_container hints present")
|
27
|
+
metadata_from_hints.each { |k,v| docker_container[k] = v }
|
28
|
+
end
|
29
|
+
|
30
|
+
container = Docker::Container.get(container_id).json
|
31
|
+
container.each { |k,v| docker_container[k] = v }
|
32
|
+
else
|
33
|
+
Ohai::Log.debug("looks_like_docker? == false")
|
34
|
+
false
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,181 @@
|
|
1
|
+
context = KnifeContainer::Generator.context
|
2
|
+
dockerfile_dir = File.join(context.dockerfiles_path, context.dockerfile_name)
|
3
|
+
temp_chef_repo = File.join(dockerfile_dir, "chef")
|
4
|
+
user_chef_repo = File.join(context.dockerfiles_path, "..")
|
5
|
+
|
6
|
+
##
|
7
|
+
# Initial Setup
|
8
|
+
#
|
9
|
+
|
10
|
+
# Create Dockerfile directory (REPO/NAME)
|
11
|
+
directory dockerfile_dir do
|
12
|
+
recursive true
|
13
|
+
end
|
14
|
+
|
15
|
+
# Dockerfile
|
16
|
+
template File.join(dockerfile_dir, "Dockerfile") do
|
17
|
+
source "dockerfile.erb"
|
18
|
+
helpers(KnifeContainer::Generator::TemplateHelper)
|
19
|
+
end
|
20
|
+
|
21
|
+
# .dockerfile
|
22
|
+
template File.join(dockerfile_dir, ".dockerignore") do
|
23
|
+
source "dockerignore.erb"
|
24
|
+
helpers(KnifeContainer::Generator::TemplateHelper)
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
##
|
29
|
+
# Initial Chef Setup
|
30
|
+
#
|
31
|
+
|
32
|
+
# create temp chef-repo
|
33
|
+
directory temp_chef_repo do
|
34
|
+
recursive true
|
35
|
+
end
|
36
|
+
|
37
|
+
# Client Config
|
38
|
+
template File.join(temp_chef_repo, "#{context.chef_client_mode}.rb") do
|
39
|
+
source "config.rb.erb"
|
40
|
+
helpers(KnifeContainer::Generator::TemplateHelper)
|
41
|
+
end
|
42
|
+
|
43
|
+
# First Boot JSON
|
44
|
+
file File.join(temp_chef_repo, "first-boot.json") do
|
45
|
+
content context.first_boot
|
46
|
+
end
|
47
|
+
|
48
|
+
# Node Name
|
49
|
+
template File.join(temp_chef_repo, ".node_name") do
|
50
|
+
source "node_name.erb"
|
51
|
+
helpers(KnifeContainer::Generator::TemplateHelper)
|
52
|
+
end
|
53
|
+
|
54
|
+
##
|
55
|
+
# Resolve run list
|
56
|
+
#
|
57
|
+
require 'chef/run_list/run_list_item'
|
58
|
+
run_list_items = context.run_list.map { |i| Chef::RunList::RunListItem.new(i) }
|
59
|
+
cookbooks = []
|
60
|
+
|
61
|
+
run_list_items.each do |item|
|
62
|
+
# Extract cookbook name from recipe
|
63
|
+
if item.recipe?
|
64
|
+
rmatch = item.name.match(/(.+?)::(.+)/)
|
65
|
+
if rmatch
|
66
|
+
cookbooks << rmatch[1]
|
67
|
+
else
|
68
|
+
cookbooks << item.name
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Generate Berksfile from runlist
|
74
|
+
unless context.run_list.empty?
|
75
|
+
template File.join(dockerfile_dir, "Berksfile") do
|
76
|
+
source "berksfile.erb"
|
77
|
+
variables :cookbooks => cookbooks
|
78
|
+
helpers(KnifeContainer::Generator::TemplateHelper)
|
79
|
+
only_if { context.generate_berksfile }
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Copy over the necessary directories into the temp chef-repo (if local-mode)
|
84
|
+
if context.chef_client_mode == "zero"
|
85
|
+
|
86
|
+
# generate a cookbooks directory unless we are building from a Berksfile
|
87
|
+
unless context.generate_berksfile
|
88
|
+
directory "#{temp_chef_repo}/cookbooks"
|
89
|
+
end
|
90
|
+
|
91
|
+
# Copy over cookbooks that are mentioned in the runlist. There is a gap here
|
92
|
+
# that dependent cookbooks are not copied. This is a result of not having a
|
93
|
+
# depsolver in the chef-client. The solution here is to use the Berkshelf integration.
|
94
|
+
if context.cookbook_path.kind_of?(Array)
|
95
|
+
context.cookbook_path.each do |dir|
|
96
|
+
if File.exists?(File.expand_path(dir))
|
97
|
+
cookbooks.each do |cookbook|
|
98
|
+
if File.exists?("#{File.expand_path(dir)}/#{cookbook}")
|
99
|
+
execute "cp -rf #{File.expand_path(dir)}/#{cookbook} #{temp_chef_repo}/cookbooks/"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
else
|
103
|
+
log "Could not find a '#{File.expand_path(dir)}' directory in your chef-repo."
|
104
|
+
end
|
105
|
+
end
|
106
|
+
elsif File.exists?(File.expand_path(context.cookbook_path))
|
107
|
+
cookbooks.each do |cookbook|
|
108
|
+
if File.exists?("#{File.expand_path(context.cookbook_path)}/#{cookbook}")
|
109
|
+
execute "cp -rf #{File.expand_path(context.cookbook_path)}/#{cookbook} #{temp_chef_repo}/cookbooks/"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
else
|
113
|
+
log "Could not find a '#{File.expand_path(context.cookbook_path)}' directory in your chef-repo."
|
114
|
+
end
|
115
|
+
|
116
|
+
# Because they have a smaller footprint, we will copy over all the roles, environments
|
117
|
+
# and nodes. This behavior will likely change in a future version of knife-container.
|
118
|
+
%w(role environment node).each do |dir|
|
119
|
+
path = context.send(:"#{dir}_path")
|
120
|
+
if path.kind_of?(Array)
|
121
|
+
path.each do |p|
|
122
|
+
execute "cp -r #{File.expand_path(p)}/ #{File.join(temp_chef_repo, "#{dir}s")}" do
|
123
|
+
not_if { Dir["#{p}/*"].empty? }
|
124
|
+
end
|
125
|
+
end
|
126
|
+
elsif path.kind_of?(String)
|
127
|
+
execute "cp -r #{path}/ #{File.join(temp_chef_repo, "#{dir}s/")}" do
|
128
|
+
not_if { Dir["#{path}/*"].empty? }
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
##
|
135
|
+
# Server Only Stuff
|
136
|
+
#
|
137
|
+
if context.chef_client_mode == "client"
|
138
|
+
|
139
|
+
directory File.join(temp_chef_repo, 'secure')
|
140
|
+
|
141
|
+
# Add validation.pem
|
142
|
+
file File.join(temp_chef_repo, 'secure', "validation.pem") do
|
143
|
+
content File.read(context.validation_key)
|
144
|
+
mode '0600'
|
145
|
+
end
|
146
|
+
|
147
|
+
# Copy over trusted certs
|
148
|
+
unless Dir["#{context.trusted_certs_dir}/*"].empty?
|
149
|
+
directory File.join(temp_chef_repo, 'secure', "trusted_certs")
|
150
|
+
execute "cp -r #{context.trusted_certs_dir}/* #{File.join(temp_chef_repo, 'secure', "trusted_certs/")}"
|
151
|
+
end
|
152
|
+
|
153
|
+
# Copy over encrypted_data_bag_key
|
154
|
+
unless context.encrypted_data_bag_secret.nil?
|
155
|
+
if File.exists?(context.encrypted_data_bag_secret)
|
156
|
+
file File.join(temp_chef_repo, 'secure', "encrypted_data_bag_secret") do
|
157
|
+
content File.read(context.encrypted_data_bag_secret)
|
158
|
+
mode '0600'
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
##
|
165
|
+
# Create Ohai Plugin
|
166
|
+
#
|
167
|
+
|
168
|
+
# create Ohai folder
|
169
|
+
# directory File.join(temp_chef_repo, "ohai")
|
170
|
+
|
171
|
+
# docker hints directory
|
172
|
+
# directory File.join(temp_chef_repo, "ohai", "hints")
|
173
|
+
|
174
|
+
# docker plugins directory
|
175
|
+
# directory File.join(temp_chef_repo, "ohai_plugins")
|
176
|
+
|
177
|
+
# docker_container Ohai plugin
|
178
|
+
# cookbook_file File.join(temp_chef_repo, "ohai_plugins", "docker_container.rb") do
|
179
|
+
# source "plugins/docker_container.rb"
|
180
|
+
# mode "0755"
|
181
|
+
# end
|