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,19 @@
1
+ module Pantry
2
+ module Chef
3
+
4
+ # Client syncs up it's local list of Chef Environments with what the Server
5
+ # says the Client should have.
6
+ class SyncEnvironments < Pantry::Commands::SyncDirectory
7
+
8
+ def server_directory(local_root)
9
+ local_root.join("applications", client.application, "chef", "environments")
10
+ end
11
+
12
+ def client_directory(local_root)
13
+ local_root.join("chef", "environments")
14
+ end
15
+
16
+ end
17
+ end
18
+ end
19
+
@@ -0,0 +1,19 @@
1
+ module Pantry
2
+ module Chef
3
+
4
+ # Client syncs up it's local list of Chef Roles with what the Server
5
+ # says the Client should have.
6
+ class SyncRoles < Pantry::Commands::SyncDirectory
7
+
8
+ def server_directory(local_root)
9
+ local_root.join("applications", client.application, "chef", "roles")
10
+ end
11
+
12
+ def client_directory(local_root)
13
+ local_root.join("chef", "roles")
14
+ end
15
+
16
+ end
17
+ end
18
+ end
19
+
@@ -0,0 +1,110 @@
1
+ module Pantry
2
+ module Chef
3
+
4
+ # Given a cookbook, upload it to the server.
5
+ # A given cookbook is stored in two locations. The cookbook tarball the Server receives is stored
6
+ # in Pantry.root/chef/cookbook-cache/[name].tgz to keep it available for sending back down to
7
+ # Clients who need the cookbook. To facilitate using the Chef library for dependency checks,
8
+ # the Server also unpacks the cookbook locally to Pantry.root/chef/cookbooks/[name].
9
+ class UploadCookbook < Pantry::Command
10
+
11
+ command "chef:cookbook:upload COOKBOOK_DIR" do
12
+ description "Upload the cookbook at COOKBOOK_DIR to the server."
13
+ group "Chef"
14
+ end
15
+
16
+ attr_reader :cookbook_tarball
17
+
18
+ def initialize(cookbook_path = nil)
19
+ @cookbook_path = cookbook_path
20
+ end
21
+
22
+ # Multi-step prepratory step here:
23
+ #
24
+ # * Find the cookbook in question
25
+ # * Figure out if it's a valid cookbook (do some checks Chef doesn't itself do)
26
+ # * Tar up the cookbook
27
+ # * Figure out size and a checksum
28
+ # * Package all this information into the message to send to the server
29
+ #
30
+ def prepare_message(options)
31
+ require 'chef/cookbook_loader'
32
+
33
+ cookbook_name = File.basename(@cookbook_path)
34
+ cookbooks_dir = File.dirname(@cookbook_path)
35
+
36
+ loader = ::Chef::CookbookLoader.new([cookbooks_dir])
37
+ cookbook = loader.load_cookbooks[cookbook_name]
38
+
39
+ raise UnknownCookbook, "Unable to find cookbook at #{@cookbook_path}" unless cookbook
40
+ raise MissingMetadata, "No metadata.rb found for cookbook at #{@cookbook_path}" unless File.exist?(File.join(@cookbook_path, "metadata.rb"))
41
+
42
+ tempfile = Tempfile.new(cookbook_name)
43
+ @cookbook_tarball = "#{tempfile.path}.tgz"
44
+ tempfile.unlink
45
+
46
+ # TODO Handle if this fails?
47
+ Dir.chdir(cookbooks_dir) do
48
+ Open3.capture2("tar", "czf", @cookbook_tarball, cookbook_name)
49
+ end
50
+
51
+ Pantry.ui.say("Uploading cookbook #{cookbook_name}...")
52
+
53
+ message = super
54
+ message[:cookbook_name] = cookbook.metadata.name
55
+ message[:cookbook_size] = File.size(@cookbook_tarball)
56
+ message[:cookbook_checksum] = Pantry.file_checksum(@cookbook_tarball)
57
+ message
58
+ end
59
+
60
+ # Server receives request message for a new Cookbook Upload.
61
+ # Checks that the upload is valid
62
+ # Fires off an upload receiver and returns the UUID for the client to use
63
+ def perform(message)
64
+ cookbook_name = message[:cookbook_name]
65
+ cookbook_size = message[:cookbook_size]
66
+ cookbook_checksum = message[:cookbook_checksum]
67
+
68
+ cookbook_cache = Pantry.root.join("chef", "cookbook-cache")
69
+ cookbook_home = Pantry.root.join("chef", "cookbooks")
70
+ FileUtils.mkdir_p(cookbook_cache)
71
+ FileUtils.mkdir_p(cookbook_home)
72
+
73
+ save_tar_path = cookbook_cache.join("#{cookbook_name}.tgz")
74
+
75
+ uploader_info = server.receive_file(cookbook_size, cookbook_checksum)
76
+ uploader_info.on_complete do
77
+ # Store the tarball into the cookbook cache
78
+ FileUtils.mv uploader_info.uploaded_path, save_tar_path
79
+
80
+ # Unpack the cookbook itself
81
+ stdout, stderr = Open3.capture2e(
82
+ "tar", "-xzC", cookbook_home.to_s, "-f", save_tar_path.to_s
83
+ )
84
+ Pantry.logger.debug("[Upload Cookbook] Unpack cookbook #{stdout.inspect}, #{stderr.inspect}")
85
+ end
86
+
87
+ [true, uploader_info.receiver_uuid, uploader_info.file_uuid]
88
+ rescue => ex
89
+ [false, ex.message]
90
+ end
91
+
92
+ def receive_server_response(response)
93
+ upload_allowed = response.body[0]
94
+
95
+ Pantry.logger.debug("[Upload Cookbook] #{response.inspect}")
96
+
97
+ if upload_allowed == "true"
98
+ send_info = client.send_file(@cookbook_tarball,
99
+ response.body[1],
100
+ response.body[2])
101
+ send_info.wait_for_finish
102
+ else
103
+ Pantry.ui.say("ERROR: #{response.body[1]}")
104
+ end
105
+ end
106
+
107
+ end
108
+
109
+ end
110
+ end
@@ -0,0 +1,37 @@
1
+ module Pantry
2
+ module Chef
3
+
4
+ # Upload a data bag file to the server
5
+ class UploadDataBag < Pantry::Commands::UploadFile
6
+
7
+ command "chef:data_bag:upload DATA_BAG_FILE" do
8
+ description "Upload the data bag DATA_BAG_FILE to the server.
9
+ By default the name of the parent directory is the type of data bag.
10
+ Pass in --type to explicitly set the type of data bag.
11
+ Requires an Application."
12
+
13
+ option "-t", "--type DATA_BAG_TYPE",
14
+ "Specify the type of data bag being uploaded.
15
+ Defaults to the name of the parent directory of the data bag file."
16
+
17
+ group "Chef"
18
+ end
19
+
20
+ def required_options
21
+ %i(application)
22
+ end
23
+
24
+ def upload_directory(options)
25
+ Pantry.root.join("applications", options[:application], "chef", "data_bags", options[:type])
26
+ end
27
+
28
+ def prepare_message(options)
29
+ options[:type] ||= File.basename(File.dirname(file_to_upload))
30
+ Pantry.ui.say("Uploading data bag #{File.basename(file_to_upload)}...")
31
+ super
32
+ end
33
+
34
+ end
35
+
36
+ end
37
+ end
@@ -0,0 +1,28 @@
1
+ module Pantry
2
+ module Chef
3
+
4
+ # Upload a environment definition to the server
5
+ class UploadEnvironment < Pantry::Commands::UploadFile
6
+
7
+ command "chef:environment:upload ENV_FILE" do
8
+ description "Upload the file at ENV_FILE as a Chef Environment. Requires an Application."
9
+ group "Chef"
10
+ end
11
+
12
+ def required_options
13
+ %i(application)
14
+ end
15
+
16
+ def upload_directory(options)
17
+ Pantry.root.join("applications", options[:application], "chef", "environments")
18
+ end
19
+
20
+ def prepare_message(options)
21
+ Pantry.ui.say("Uploading environment #{File.basename(file_to_upload)}...")
22
+ super
23
+ end
24
+
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,28 @@
1
+ module Pantry
2
+ module Chef
3
+
4
+ # Upload a role definition to the server
5
+ class UploadRole < Pantry::Commands::UploadFile
6
+
7
+ command "chef:role:upload ROLE_FILE" do
8
+ description "Upload the file at ROLE_FILE as a Chef Role. Requires an Application"
9
+ group "Chef"
10
+ end
11
+
12
+ def required_options
13
+ %i(application)
14
+ end
15
+
16
+ def upload_directory(options)
17
+ Pantry.root.join("applications", options[:application], "chef", "roles")
18
+ end
19
+
20
+ def prepare_message(*args)
21
+ Pantry.ui.say("Uploading role #{File.basename(file_to_upload)}...")
22
+ super
23
+ end
24
+
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,5 @@
1
+ module Pantry
2
+ module Chef
3
+ VERSION = "0.1"
4
+ end
5
+ end
@@ -0,0 +1 @@
1
+ require 'pantry/chef'
@@ -0,0 +1,27 @@
1
+ $:.push File.expand_path("../lib", __FILE__)
2
+ require "pantry/chef/version"
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "pantry-chef"
6
+ s.version = Pantry::Chef::VERSION
7
+ s.platform = Gem::Platform::RUBY
8
+ s.authors = ["Collective Idea", "Jason Roelofs"]
9
+ s.email = ["code@collectiveidea.com", "jasongroelofs@gmail.com"]
10
+ s.license = "MIT"
11
+ s.homepage = "http://pantryops.org/chef"
12
+
13
+ s.summary = "Chef Plugin for Pantry"
14
+ s.description = <<-EOS
15
+ Add Chef support to a Pantry network.
16
+ EOS
17
+
18
+ s.required_ruby_version = ">= 2.0.0"
19
+
20
+ s.files = `git ls-files`.split("\n")
21
+ s.test_files = `git ls-files -- test/*`.split("\n")
22
+ s.require_path = "lib"
23
+ s.bindir = "bin"
24
+
25
+ s.add_runtime_dependency "pantry", "~> 0.1", ">= 0.1.0"
26
+ s.add_runtime_dependency "chef", "~> 11.10", ">= 11.10.0"
27
+ end
@@ -0,0 +1,69 @@
1
+ require 'acceptance/test_helper'
2
+
3
+ describe "Running Chef on a Client" do
4
+
5
+ mock_ui!
6
+
7
+ it "configures chef, syncs cookbooks from server to the client and runs chef solo" do
8
+ set_up_environment(ports_start_at: 12000)
9
+
10
+ # Make sure cookbook is uploaded to the server
11
+ Pantry::CLI.new(
12
+ ["chef:cookbook:upload", fixture_path("cookbooks/mini")],
13
+ identity: "cli1"
14
+ ).run
15
+
16
+ # Add a role definition
17
+ Pantry::CLI.new(
18
+ ["-a", "pantry", "chef:role:upload", fixture_path("roles/app1.rb")],
19
+ identity: "cli2"
20
+ ).run
21
+
22
+ # Add an environment definition
23
+ Pantry::CLI.new(
24
+ ["-a", "pantry", "chef:environment:upload", fixture_path("environments/test.rb")],
25
+ identity: "cli3"
26
+ ).run
27
+
28
+ # Add a data bag
29
+ Pantry::CLI.new(
30
+ ["-a", "pantry", "chef:data_bag:upload", fixture_path("data_bags/settings/test.json")],
31
+ identity: "cli4"
32
+ ).run
33
+
34
+ # Run chef to sync the cookbooks to the client
35
+ Pantry::CLI.new(
36
+ ["-a", "pantry", "-e", "test", "-r", "app1", "chef:run"],
37
+ identity: "cli-runner"
38
+ ).run
39
+
40
+ # Configure chef
41
+ assert File.exists?(Pantry.root.join("etc", "chef", "solo.rb")),
42
+ "Did not write out the solo file"
43
+ assert File.exists?(Pantry.root.join("etc", "chef", "node.json")),
44
+ "Did not write out the node file"
45
+
46
+ # Sync roles and environments
47
+ assert File.exists?(Pantry.root.join("chef", "roles", "app1.rb")),
48
+ "Did not sync the role files"
49
+ assert File.exists?(Pantry.root.join("chef", "environments", "test.rb")),
50
+ "Did not sync the environment files"
51
+
52
+ # Sync Cookbooks
53
+ assert File.exists?(Pantry.root.join("chef", "cookbooks", "mini", "metadata.rb")),
54
+ "Did not receive the mini cookbook from the server"
55
+ assert File.directory?(Pantry.root.join("chef", "cookbooks", "mini", "recipes")),
56
+ "Did not receive the mini cookbook from the server"
57
+
58
+ # Sync Data Bags
59
+ assert File.exists?(Pantry.root.join("chef", "data_bags", "settings", "test.json")),
60
+ "Did not receive the test settings data bag from the server"
61
+
62
+ # Run chef-solo
63
+ assert File.exists?(Pantry.root.join("chef", "cache", "chef-client-running.pid")),
64
+ "Did not run chef-solo"
65
+
66
+ assert_match /Chef Run complete in/, stdout
67
+ end
68
+
69
+ end
@@ -0,0 +1,26 @@
1
+ require 'acceptance/test_helper'
2
+ require 'fileutils'
3
+
4
+ describe "Uploading cookbooks to the server" do
5
+ mock_ui!
6
+
7
+ it "finds the current cookbook and uploads it" do
8
+ set_up_environment(ports_start_at: 11000)
9
+
10
+ Pantry::CLI.new(
11
+ ["chef:cookbook:upload", fixture_path("cookbooks/mini")],
12
+ identity: "cli1"
13
+ ).run
14
+
15
+ # The receiver may still be writing and moving the cookbook into place
16
+ sleep 1
17
+
18
+ assert File.exists?(Pantry.root.join("chef", "cookbooks", "mini", "metadata.rb")),
19
+ "The mini cookbook was not uploaded to the server properly"
20
+ assert File.exists?(Pantry.root.join("chef", "cookbook-cache", "mini.tgz")),
21
+ "The uploaded tar ball was not saved to the upload cache"
22
+ end
23
+
24
+ it "reports any upload errors"
25
+
26
+ end
@@ -0,0 +1,36 @@
1
+ require 'acceptance/test_helper'
2
+ require 'fileutils'
3
+
4
+ describe "Uploading data bag definitions to the server" do
5
+ mock_ui!
6
+
7
+ it "uploads the given file as a Chef data bag" do
8
+ set_up_environment(ports_start_at: 11500)
9
+
10
+ cli = Pantry::CLI.new(
11
+ ["-a", "pantry", "chef:data_bag:upload", fixture_path("data_bags/settings/test.json")],
12
+ identity: "cli1"
13
+ )
14
+ cli.run
15
+
16
+ assert File.exists?(Pantry.root.join(
17
+ "applications", "pantry", "chef", "data_bags", "settings", "test.json")),
18
+ "The test settings data bag was not uploaded to the server properly"
19
+ end
20
+
21
+ it "can be told the explicit name of the data bag type" do
22
+ set_up_environment(ports_start_at: 11510)
23
+
24
+ cli = Pantry::CLI.new(
25
+ ["-a", "pantry", "chef:data_bag:upload", "-t", "users", fixture_path("data_bags/settings/test.json")],
26
+ identity: "cli1"
27
+ )
28
+ cli.run
29
+
30
+ assert File.exists?(Pantry.root.join(
31
+ "applications", "pantry", "chef", "data_bags", "users", "test.json")),
32
+ "The test settings data bag was not uploaded to the server properly"
33
+ end
34
+
35
+ end
36
+
@@ -0,0 +1,22 @@
1
+ require 'acceptance/test_helper'
2
+ require 'fileutils'
3
+
4
+ describe "Uploading environment definitions to the server" do
5
+ mock_ui!
6
+
7
+ it "uploads the given file as a Chef environment" do
8
+ set_up_environment(ports_start_at: 14000)
9
+
10
+ cli = Pantry::CLI.new(
11
+ ["-a", "pantry", "chef:environment:upload", fixture_path("environments/test.rb")],
12
+ identity: "cli1"
13
+ )
14
+ cli.run
15
+
16
+ assert File.exists?(Pantry.root.join(
17
+ "applications", "pantry", "chef", "environments", "test.rb")),
18
+ "The test environment was not uploaded to the server properly"
19
+ end
20
+
21
+ end
22
+
@@ -0,0 +1,21 @@
1
+ require 'acceptance/test_helper'
2
+ require 'fileutils'
3
+
4
+ describe "Uploading role definitions to the server" do
5
+ mock_ui!
6
+
7
+ it "uploads the given file as a Chef role" do
8
+ set_up_environment(ports_start_at: 13000)
9
+
10
+ cli = Pantry::CLI.new(
11
+ ["-a", "pantry", "chef:role:upload", fixture_path("roles/app.rb")],
12
+ identity: "cli1"
13
+ )
14
+ cli.run
15
+
16
+ assert File.exists?(Pantry.root.join("applications", "pantry", "chef", "roles", "app.rb")),
17
+ "The app role was not uploaded to the server properly"
18
+ end
19
+
20
+ end
21
+