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.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +23 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +4 -0
  5. data/CONTRIBUTING.md +152 -0
  6. data/Gemfile +10 -0
  7. data/LICENSE +201 -0
  8. data/README.md +59 -0
  9. data/Rakefile +16 -0
  10. data/knife-container.gemspec +31 -0
  11. data/lib/chef/knife/container_docker_build.rb +243 -0
  12. data/lib/chef/knife/container_docker_init.rb +262 -0
  13. data/lib/knife-container/chef_runner.rb +83 -0
  14. data/lib/knife-container/command.rb +45 -0
  15. data/lib/knife-container/generator.rb +88 -0
  16. data/lib/knife-container/helpers.rb +16 -0
  17. data/lib/knife-container/skeletons/knife_container/files/default/plugins/docker_container.rb +37 -0
  18. data/lib/knife-container/skeletons/knife_container/metadata.rb +7 -0
  19. data/lib/knife-container/skeletons/knife_container/recipes/docker_init.rb +181 -0
  20. data/lib/knife-container/skeletons/knife_container/templates/default/berksfile.erb +5 -0
  21. data/lib/knife-container/skeletons/knife_container/templates/default/config.rb.erb +16 -0
  22. data/lib/knife-container/skeletons/knife_container/templates/default/dockerfile.erb +9 -0
  23. data/lib/knife-container/skeletons/knife_container/templates/default/dockerignore.erb +0 -0
  24. data/lib/knife-container/skeletons/knife_container/templates/default/node_name.erb +1 -0
  25. data/lib/knife-container/version.rb +5 -0
  26. data/spec/functional/docker_container_ohai_spec.rb +20 -0
  27. data/spec/functional/fixtures/ohai/Dockerfile +3 -0
  28. data/spec/spec_helper.rb +35 -0
  29. data/spec/test_helpers.rb +59 -0
  30. data/spec/unit/container_docker_build_spec.rb +325 -0
  31. data/spec/unit/container_docker_init_spec.rb +464 -0
  32. data/spec/unit/fixtures/.chef/encrypted_data_bag_secret +0 -0
  33. data/spec/unit/fixtures/.chef/trusted_certs/chef_example_com.crt +0 -0
  34. data/spec/unit/fixtures/.chef/validator.pem +1 -0
  35. data/spec/unit/fixtures/Berksfile +3 -0
  36. data/spec/unit/fixtures/cookbooks/dummy/metadata.rb +0 -0
  37. data/spec/unit/fixtures/cookbooks/nginx/metadata.rb +0 -0
  38. data/spec/unit/fixtures/environments/dev.json +0 -0
  39. data/spec/unit/fixtures/nodes/demo.json +0 -0
  40. data/spec/unit/fixtures/roles/base.json +0 -0
  41. data/spec/unit/fixtures/site-cookbooks/apt/metadata.rb +0 -0
  42. 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,7 @@
1
+ name 'knife_container'
2
+ maintainer 'Chef Software, Inc.'
3
+ maintainer_email 'dev@getchef.com'
4
+ license 'Apache 2 License'
5
+ description 'Generates Chef code for knife-container'
6
+ long_description 'Generates Chef code for knife-container'
7
+ version '0.1.0'
@@ -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