pantry-chef 0.1

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