knife-container 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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