pantry-chef 0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/.travis.yml +16 -0
- data/Gemfile +15 -0
- data/LICENSE +20 -0
- data/README.md +49 -0
- data/Rakefile +18 -0
- data/lib/pantry/chef.rb +47 -0
- data/lib/pantry/chef/configure_chef.rb +59 -0
- data/lib/pantry/chef/list_cookbooks.rb +31 -0
- data/lib/pantry/chef/run.rb +30 -0
- data/lib/pantry/chef/run_chef_solo.rb +21 -0
- data/lib/pantry/chef/send_cookbooks.rb +23 -0
- data/lib/pantry/chef/sync_cookbooks.rb +84 -0
- data/lib/pantry/chef/sync_data_bags.rb +19 -0
- data/lib/pantry/chef/sync_environments.rb +19 -0
- data/lib/pantry/chef/sync_roles.rb +19 -0
- data/lib/pantry/chef/upload_cookbook.rb +110 -0
- data/lib/pantry/chef/upload_data_bag.rb +37 -0
- data/lib/pantry/chef/upload_environment.rb +28 -0
- data/lib/pantry/chef/upload_role.rb +28 -0
- data/lib/pantry/chef/version.rb +5 -0
- data/lib/pantry/init.rb +1 -0
- data/pantry-chef.gemspec +27 -0
- data/test/acceptance/chef/run_test.rb +69 -0
- data/test/acceptance/chef/upload_cookbook_test.rb +26 -0
- data/test/acceptance/chef/upload_data_bag_test.rb +36 -0
- data/test/acceptance/chef/upload_environment_test.rb +22 -0
- data/test/acceptance/chef/upload_role_test.rb +21 -0
- data/test/acceptance/test_helper.rb +25 -0
- data/test/fixtures/cookbooks/bad/recipes/default.rb +1 -0
- data/test/fixtures/cookbooks/mini/metadata.rb +5 -0
- data/test/fixtures/cookbooks/mini/recipes/default.rb +1 -0
- data/test/fixtures/data_bags/settings/test.json +0 -0
- data/test/fixtures/environments/test.rb +2 -0
- data/test/fixtures/roles/app.rb +2 -0
- data/test/fixtures/roles/app1.rb +2 -0
- data/test/root_dir/applications/pantry/chef/roles/app.rb +2 -0
- data/test/unit/chef/configure_chef_test.rb +64 -0
- data/test/unit/chef/list_cookbooks_test.rb +49 -0
- data/test/unit/chef/run_chef_solo_test.rb +29 -0
- data/test/unit/chef/run_test.rb +5 -0
- data/test/unit/chef/send_cookbooks_test.rb +44 -0
- data/test/unit/chef/sync_cookbooks_test.rb +40 -0
- data/test/unit/chef/sync_data_bags_test.rb +21 -0
- data/test/unit/chef/sync_environments_test.rb +21 -0
- data/test/unit/chef/sync_roles_test.rb +21 -0
- data/test/unit/chef/upload_cookbook_test.rb +132 -0
- data/test/unit/chef/upload_data_bag_test.rb +24 -0
- data/test/unit/chef/upload_environment_test.rb +11 -0
- data/test/unit/chef/upload_role_test.rb +11 -0
- data/test/unit/test_helper.rb +26 -0
- metadata +166 -0
checksums.yaml
ADDED
@@ -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
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
@@ -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
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
|
+
|
data/README.md
ADDED
@@ -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.
|
data/Rakefile
ADDED
@@ -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
|
data/lib/pantry/chef.rb
ADDED
@@ -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
|
+
|