pantry-chef 0.1

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 (53) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +5 -0
  3. data/.travis.yml +16 -0
  4. data/Gemfile +15 -0
  5. data/LICENSE +20 -0
  6. data/README.md +49 -0
  7. data/Rakefile +18 -0
  8. data/lib/pantry/chef.rb +47 -0
  9. data/lib/pantry/chef/configure_chef.rb +59 -0
  10. data/lib/pantry/chef/list_cookbooks.rb +31 -0
  11. data/lib/pantry/chef/run.rb +30 -0
  12. data/lib/pantry/chef/run_chef_solo.rb +21 -0
  13. data/lib/pantry/chef/send_cookbooks.rb +23 -0
  14. data/lib/pantry/chef/sync_cookbooks.rb +84 -0
  15. data/lib/pantry/chef/sync_data_bags.rb +19 -0
  16. data/lib/pantry/chef/sync_environments.rb +19 -0
  17. data/lib/pantry/chef/sync_roles.rb +19 -0
  18. data/lib/pantry/chef/upload_cookbook.rb +110 -0
  19. data/lib/pantry/chef/upload_data_bag.rb +37 -0
  20. data/lib/pantry/chef/upload_environment.rb +28 -0
  21. data/lib/pantry/chef/upload_role.rb +28 -0
  22. data/lib/pantry/chef/version.rb +5 -0
  23. data/lib/pantry/init.rb +1 -0
  24. data/pantry-chef.gemspec +27 -0
  25. data/test/acceptance/chef/run_test.rb +69 -0
  26. data/test/acceptance/chef/upload_cookbook_test.rb +26 -0
  27. data/test/acceptance/chef/upload_data_bag_test.rb +36 -0
  28. data/test/acceptance/chef/upload_environment_test.rb +22 -0
  29. data/test/acceptance/chef/upload_role_test.rb +21 -0
  30. data/test/acceptance/test_helper.rb +25 -0
  31. data/test/fixtures/cookbooks/bad/recipes/default.rb +1 -0
  32. data/test/fixtures/cookbooks/mini/metadata.rb +5 -0
  33. data/test/fixtures/cookbooks/mini/recipes/default.rb +1 -0
  34. data/test/fixtures/data_bags/settings/test.json +0 -0
  35. data/test/fixtures/environments/test.rb +2 -0
  36. data/test/fixtures/roles/app.rb +2 -0
  37. data/test/fixtures/roles/app1.rb +2 -0
  38. data/test/root_dir/applications/pantry/chef/roles/app.rb +2 -0
  39. data/test/unit/chef/configure_chef_test.rb +64 -0
  40. data/test/unit/chef/list_cookbooks_test.rb +49 -0
  41. data/test/unit/chef/run_chef_solo_test.rb +29 -0
  42. data/test/unit/chef/run_test.rb +5 -0
  43. data/test/unit/chef/send_cookbooks_test.rb +44 -0
  44. data/test/unit/chef/sync_cookbooks_test.rb +40 -0
  45. data/test/unit/chef/sync_data_bags_test.rb +21 -0
  46. data/test/unit/chef/sync_environments_test.rb +21 -0
  47. data/test/unit/chef/sync_roles_test.rb +21 -0
  48. data/test/unit/chef/upload_cookbook_test.rb +132 -0
  49. data/test/unit/chef/upload_data_bag_test.rb +24 -0
  50. data/test/unit/chef/upload_environment_test.rb +11 -0
  51. data/test/unit/chef/upload_role_test.rb +11 -0
  52. data/test/unit/test_helper.rb +26 -0
  53. metadata +166 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 940b6c168265e478641aa40d456c2f70ce2e632e
4
+ data.tar.gz: 8c88964eadedea49879ba9ba17a2865b76c7bb53
5
+ SHA512:
6
+ metadata.gz: b57aa263239fcdc89450645200e497f9baa5e750b5224eeeef1a397d3a13cb648ffbc8808f13c41569f62905dacc87195debde5d76fda8276b5d25685aa933f3
7
+ data.tar.gz: 1fc61ebd37613a5531bcac0b56456776d4d2891cd94604b21b3e4ab6746b3ec799ee5f8c6aaf5057a43feaec023eeaa4081f851c455288dcff32ba806edd6371
@@ -0,0 +1,5 @@
1
+ Gemfile.lock
2
+ *.gem
3
+
4
+ test/test.log
5
+ test/acceptance/data_dir/*
@@ -0,0 +1,16 @@
1
+ before_install:
2
+ - travis_retry sudo apt-add-repository ppa:bpaquet/zeromq4-precise -y
3
+ - travis_retry sudo apt-get update
4
+ - travis_retry sudo apt-get install libzmq-dev -y
5
+ language: ruby
6
+ rvm:
7
+ - 2.0.0
8
+ - 2.1.0
9
+ - ruby-head
10
+ cache:
11
+ - bundler
12
+ - apt
13
+
14
+ matrix:
15
+ allow_failures:
16
+ - rvm: ruby-head
data/Gemfile ADDED
@@ -0,0 +1,15 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "pantry", :github => "pantry/pantry"
4
+
5
+ gemspec
6
+
7
+ group :development do
8
+ gem "rake"
9
+ end
10
+
11
+ group :test do
12
+ gem "minitest"
13
+ gem "mocha"
14
+ gem "fakefs", :require => false
15
+ end
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2014 Collective Idea
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
20
+
@@ -0,0 +1,49 @@
1
+ # Pantry Chef
2
+
3
+ [![Build Status](https://travis-ci.org/pantry/pantry-chef.png?branch=master)](https://travis-ci.org/pantry/pantry-chef) [![Code Climate](https://codeclimate.com/github/pantry/pantry-chef.png)](https://codeclimate.com/github/pantry/pantry-chef)
4
+
5
+ Pantry Chef is a plugin for [Pantry](pantryops.org) to host and serve up Chef data (cookbooks, environments, roles, and data bags). Chef is run on Clients using chef-solo.
6
+
7
+ ## Installation
8
+
9
+ Install the plugin on all nodes in the Pantry network:
10
+
11
+ gem install pantry-chef
12
+
13
+ ## Usage
14
+
15
+ Once installed, using Pantry Chef is simple. You'll need to upload each of your cookbooks to the server.
16
+
17
+ pantry chef:cookbook:upload COOKBOOK_DIR
18
+
19
+ For application specific files (environments, roles, and data bags) you need to specify an application for uploading
20
+
21
+ pantry -a [application] chef:environment:upload ENVIRONMENT_FILE
22
+ ...
23
+
24
+ Once all files are uploaded, trigger a Chef run on all clients:
25
+
26
+ pantry chef:run
27
+
28
+ More details can be found at http://pantryops.org/chef
29
+
30
+ ## Documentation
31
+
32
+ The Documentation for Pantry is available at http://pantryops.org/chef and the RDoc is served up at [rdoc.info/pantry-chef](http://rubydoc.info/github/pantry/pantry-chef/master/frames).
33
+
34
+ ## Project Details
35
+
36
+ * Built and Maintained by [Collective Idea](http://collectiveidea.com)
37
+ * Hosted on Github [pantry/pantry-chef](https://github.com/pantry/pantry-chef)
38
+ * File bug reports on the [Issue tracker](https://github.com/pantry/pantry-chef/issues)
39
+
40
+ ## Contributing
41
+
42
+ * Fork this repository on Github
43
+ * Make your changes and send us a Pull Request
44
+ * All Pull Requests must contain tests
45
+ * Pull Request tests must pass before being accepted
46
+
47
+ ## License
48
+
49
+ Pantry Chef is distributed under the MIT License. See LICENSE for more details.
@@ -0,0 +1,18 @@
1
+ require 'rake/testtask'
2
+
3
+ task :default => "test:all"
4
+
5
+ namespace :test do
6
+ desc "Run all test suites"
7
+ task :all => [:unit, :acceptance]
8
+
9
+ Rake::TestTask.new(:unit) do |t|
10
+ t.libs << "test" << "lib"
11
+ t.pattern = "test/unit/**/*_test.rb"
12
+ end
13
+
14
+ Rake::TestTask.new(:acceptance) do |t|
15
+ t.libs << "test" << "lib"
16
+ t.pattern = "test/acceptance/**/*_test.rb"
17
+ end
18
+ end
@@ -0,0 +1,47 @@
1
+ require 'pantry/chef/version'
2
+
3
+ require 'pantry/chef/upload_cookbook'
4
+ require 'pantry/chef/list_cookbooks'
5
+ require 'pantry/chef/sync_cookbooks'
6
+ require 'pantry/chef/send_cookbooks'
7
+
8
+ require 'pantry/chef/upload_role'
9
+ require 'pantry/chef/sync_roles'
10
+
11
+ require 'pantry/chef/upload_environment'
12
+ require 'pantry/chef/sync_environments'
13
+
14
+ require 'pantry/chef/upload_data_bag'
15
+ require 'pantry/chef/sync_data_bags'
16
+
17
+ require 'pantry/chef/configure_chef'
18
+ require 'pantry/chef/run_chef_solo'
19
+ require 'pantry/chef/run'
20
+
21
+ module Pantry
22
+ module Chef
23
+
24
+ class UnknownCookbook < Exception; end
25
+
26
+ class MissingMetadata < Exception; end
27
+
28
+ class UploadError < Exception; end
29
+
30
+ end
31
+ end
32
+
33
+ Pantry.add_server_command(Pantry::Chef::UploadCookbook)
34
+ Pantry.add_server_command(Pantry::Chef::ListCookbooks)
35
+ Pantry.add_server_command(Pantry::Chef::SendCookbooks)
36
+
37
+ Pantry.add_server_command(Pantry::Chef::UploadRole)
38
+ Pantry.add_server_command(Pantry::Chef::UploadEnvironment)
39
+ Pantry.add_server_command(Pantry::Chef::UploadDataBag)
40
+
41
+ Pantry.add_client_command(Pantry::Chef::Run)
42
+ Pantry.add_client_command(Pantry::Chef::ConfigureChef)
43
+ Pantry.add_client_command(Pantry::Chef::SyncCookbooks)
44
+ Pantry.add_client_command(Pantry::Chef::SyncRoles)
45
+ Pantry.add_client_command(Pantry::Chef::SyncEnvironments)
46
+ Pantry.add_client_command(Pantry::Chef::SyncDataBags)
47
+ Pantry.add_client_command(Pantry::Chef::RunChefSolo)
@@ -0,0 +1,59 @@
1
+ module Pantry
2
+ module Chef
3
+
4
+ class ConfigureChef < Pantry::Command
5
+
6
+ def perform(message)
7
+ @base_chef_dir = Pantry.root.join("chef")
8
+ @etc_dir = Pantry.root.join("etc", "chef")
9
+ create_required_directories
10
+ write_solo_rb
11
+ write_node_json
12
+ # TODO: Error handling response message?
13
+ true
14
+ end
15
+
16
+ protected
17
+
18
+ def create_required_directories
19
+ FileUtils.mkdir_p(@base_chef_dir.join("cache"))
20
+ FileUtils.mkdir_p(@base_chef_dir.join("cookbooks"))
21
+ FileUtils.mkdir_p(@base_chef_dir.join("environments"))
22
+ FileUtils.mkdir_p(@etc_dir)
23
+ end
24
+
25
+ # NOTE: Writes out the file every time this command is run.
26
+ def write_solo_rb
27
+ contents = ["# This file generated by Pantry", ""]
28
+
29
+ if client && client.environment
30
+ contents << %|environment "#{client.environment}"|
31
+ end
32
+
33
+ contents << %|file_cache_path "#{@base_chef_dir.join("cache")}"|
34
+ contents << %|cookbook_path "#{@base_chef_dir.join("cookbooks")}"|
35
+ contents << %|environment_path "#{@base_chef_dir.join("environments")}"|
36
+ contents << %|role_path "#{@base_chef_dir.join("roles")}"|
37
+ contents << %|json_attribs "#{@etc_dir.join("node.json")}"|
38
+
39
+ File.open(@etc_dir.join("solo.rb"), "w+") do |file|
40
+ file.write(contents.join("\n") + "\n")
41
+ end
42
+ end
43
+
44
+ def write_node_json
45
+ return unless client
46
+
47
+ node_json = @etc_dir.join("node.json")
48
+
49
+ File.open(node_json, "w+") do |file|
50
+ file.write({
51
+ "run_list" => client.roles.map {|r| "role[#{r}]" }
52
+ }.to_json)
53
+ end
54
+ end
55
+
56
+ end
57
+
58
+ end
59
+ end
@@ -0,0 +1,31 @@
1
+ module Pantry
2
+ module Chef
3
+
4
+ # List all cookbooks known by the Server.
5
+ # This message includes the size and checksum of the cookbooks's tarball as it's used
6
+ # when sending cookbooks down to a Client.
7
+ class ListCookbooks < Pantry::Command
8
+
9
+ command "chef:cookbook:list" do
10
+ description "List all known cookbooks on the server."
11
+ group "Chef"
12
+ end
13
+
14
+ def perform(message)
15
+ Dir[Pantry.root.join("chef", "cookbook-cache", "*")].map do |cookbook_path|
16
+ [
17
+ File.basename(cookbook_path, ".tgz"),
18
+ File.size(cookbook_path),
19
+ Pantry.file_checksum(cookbook_path)
20
+ ]
21
+ end
22
+ end
23
+
24
+ def receive_server_response(message)
25
+ Pantry.ui.list(message.body.map(&:first).sort)
26
+ end
27
+
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,30 @@
1
+ module Pantry
2
+ module Chef
3
+
4
+ class Run < Pantry::MultiCommand
5
+
6
+ command "chef:run" do
7
+ description "Run Chef on Clients"
8
+ group "Chef"
9
+ end
10
+
11
+ performs [
12
+ ConfigureChef,
13
+ SyncCookbooks,
14
+ SyncRoles,
15
+ SyncEnvironments,
16
+ SyncDataBags,
17
+ RunChefSolo
18
+ ]
19
+
20
+ def receive_client_response(response)
21
+ Pantry.ui.say("Chef on #{response.from} finished")
22
+ Pantry.ui.say(response.body[5][0])
23
+ Pantry.ui.say(response.body[5][1])
24
+ Pantry.ui.say("")
25
+ end
26
+
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,21 @@
1
+ module Pantry
2
+ module Chef
3
+
4
+ # Execute ChefSolo on the current box, returning STDOUT, STDERR, and status code.
5
+ class RunChefSolo < Pantry::Command
6
+
7
+ def perform(message)
8
+ begin
9
+ solo_rb = Pantry.root.join("etc", "chef", "solo.rb")
10
+ stdout, stderr, status = Open3.capture3("chef-solo --config #{solo_rb}")
11
+ [stdout, stderr, status.to_i]
12
+ rescue Exception => e
13
+ # Could not find the chef-solo binary
14
+ ["", e.message, 1]
15
+ end
16
+ end
17
+
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,23 @@
1
+ module Pantry
2
+ module Chef
3
+
4
+ # Given a list of cookbooks and the Receivers waiting for them,
5
+ # set up some senders to start sending the appropriate Cookbook files along.
6
+ class SendCookbooks < Pantry::Command
7
+
8
+ def perform(message)
9
+ message.body.each do |(name, receiver_uuid, file_uuid)|
10
+ server.send_file(
11
+ Pantry.root.join("chef", "cookbook-cache", "#{name}.tgz"),
12
+ receiver_uuid,
13
+ file_uuid
14
+ )
15
+ end
16
+
17
+ true
18
+ end
19
+
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,84 @@
1
+ module Pantry
2
+ module Chef
3
+
4
+ # Client syncs up it's local list of Chef Cookbooks with what the Server
5
+ # says the Client should have.
6
+ class SyncCookbooks < Pantry::Command
7
+
8
+ def perform(message)
9
+ cookbooks_to_download = ask_server_for_cookbook_list
10
+ Pantry.logger.debug("[#{client.identity}] Downloading cookbooks #{cookbooks_to_download}")
11
+
12
+ recievers = build_cookbook_receivers(cookbooks_to_download)
13
+ send_receiver_information_to_server(recievers)
14
+ wait_for_receivers_to_finish(recievers)
15
+ true
16
+ end
17
+
18
+ protected
19
+
20
+ class FileAndReceiverInfo
21
+ include Forwardable
22
+
23
+ attr_reader :name
24
+
25
+ def initialize(name, receiver_info)
26
+ @name = name
27
+ @receiver_info = receiver_info
28
+ end
29
+
30
+ def method_missing(*args, &block)
31
+ @receiver_info.send(*args, &block)
32
+ end
33
+ end
34
+
35
+ def ask_server_for_cookbook_list
36
+ send_request!(Pantry::Chef::ListCookbooks.new.to_message).body
37
+ end
38
+
39
+ def build_cookbook_receivers(cookbook_list)
40
+ cookbook_list.map do |(name, size, checksum)|
41
+ receive_info = FileAndReceiverInfo.new(name, client.receive_file(size, checksum))
42
+ receive_info.on_complete(&unpack_received_file(receive_info))
43
+ receive_info
44
+ end
45
+ end
46
+
47
+ def unpack_received_file(receiver_info)
48
+ lambda do
49
+ stdout, stderr = Open3.capture2e(
50
+ "tar",
51
+ "-xzC", Pantry.root.join("chef", "cookbooks").to_s,
52
+ "-f", receiver_info.uploaded_path
53
+ )
54
+
55
+ Pantry.logger.debug("[#{client.identity}] Unpack cookbook #{stdout.inspect}, #{stderr.inspect}")
56
+ end
57
+ end
58
+
59
+ def send_receiver_information_to_server(receivers)
60
+ download_message = Pantry::Chef::SendCookbooks.new.to_message
61
+
62
+ receivers.each do |receiver_info|
63
+ download_message << [
64
+ receiver_info.name,
65
+ receiver_info.receiver_uuid,
66
+ receiver_info.file_uuid
67
+ ]
68
+ end
69
+
70
+ if receivers.any?
71
+ send_request(download_message)
72
+ end
73
+ end
74
+
75
+ def wait_for_receivers_to_finish(receivers)
76
+ receivers.each do |receive_info|
77
+ receive_info.wait_for_finish
78
+ end
79
+ end
80
+
81
+ end
82
+
83
+ end
84
+ end
@@ -0,0 +1,19 @@
1
+ module Pantry
2
+ module Chef
3
+
4
+ # Client syncs up it's local list of data bags with what the Server
5
+ # says the Client should have.
6
+ class SyncDataBags < Pantry::Commands::SyncDirectory
7
+
8
+ def server_directory(local_root)
9
+ local_root.join("applications", client.application, "chef", "data_bags")
10
+ end
11
+
12
+ def client_directory(local_root)
13
+ local_root.join("chef", "data_bags")
14
+ end
15
+
16
+ end
17
+ end
18
+ end
19
+