knife-stackbuilder 0.5.2

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8de9741b23271b0bafcd08a39d7fe5f5e7a53030
4
+ data.tar.gz: 8d134e95054c1bb253f477fa4d10eed38e34ca74
5
+ SHA512:
6
+ metadata.gz: 16f91cc0f5846a7710a50cde9ba66cf2fad80c91a82a3d35246c4109ec6a3d9abfece254b6ff5dd0d68ab533c499c6bdccac8f5e170984c4f24900802b8b5c89
7
+ data.tar.gz: 12fcdbf1f952293f844e4ef8379e565e36148393a16a7ac8a26517639328bc29642ccc51d3dc794c57ffaaceadf34d23deed1cf4ae673684743305e7e219c3bc
data/README.md ADDED
@@ -0,0 +1,115 @@
1
+ # Knife StackBuilder plugin
2
+
3
+ ## Usage
4
+
5
+ ```
6
+ knife stack initialize repo
7
+
8
+ Initializes or validates an existing stack repo. The stack repo should contain the
9
+ following folders along with a Berksfile.
10
+
11
+ * cookbooks
12
+ * environments
13
+ * secrets
14
+ * databags
15
+ * roles
16
+ * stacks
17
+
18
+ --path
19
+
20
+ Path to create and initialize the stack chef repository. If the repository already
21
+ exists then any it will be validated. If the provided path begins with git:, http:
22
+ or https:, it will be assumed to be a git repo. A branch/label may be specified
23
+ by preceding it with a : after the path.
24
+
25
+ i.e. http://github.com/mevansam/mystack:tag1
26
+
27
+ --cert_path | --certs
28
+
29
+ If "--cert_path" is specified then it should point to a directory with a folder for
30
+ each server domain name containing that server's certificate files.
31
+
32
+ If instead "--certs" are specified then it should provide a comma separated list of
33
+ server domain names for which self-signed certificates will be generated.
34
+
35
+ --envs
36
+
37
+ Comma separated list of environments to generate.
38
+
39
+ --cookbooks
40
+
41
+ A comma separated list of cookbooks and their versions to be added to the Berksfile.
42
+
43
+ i.e. "mysql:=5.6.1, wordpress:~> 2.3.0"
44
+ ```
45
+
46
+ ```
47
+ knife stack upload cookbook[s]
48
+ ```
49
+
50
+ ```
51
+ knife stack upload environment[s]
52
+ ```
53
+
54
+ ```
55
+ knife stack upload role[s]
56
+ ```
57
+
58
+ ```
59
+ knife stack upload data bag[s]
60
+ ```
61
+
62
+ ```
63
+ knife stack upload repo
64
+ ```
65
+
66
+ ```
67
+ knife stack build
68
+ ```
69
+
70
+ ## Design
71
+
72
+ ### Berkshelf Cookbook Repository Management
73
+
74
+ ### Externalizing configuration values and order of evaluation
75
+
76
+ #### Requesting user input for non-persisted values
77
+
78
+ #### Including common yaml configurations
79
+
80
+ #### Processing node attributes
81
+
82
+ ### Converging a cluster using target nodes
83
+
84
+ * Magic variables
85
+
86
+ ### Dependency management and orchestration
87
+
88
+ ## To Do:
89
+
90
+ * Use Chef Pushy instead of Knife SSH
91
+ * Add option to execute Chef Push jobs on events
92
+
93
+ ## Contributing
94
+
95
+ 1. Fork the repository on Github
96
+ 2. Write your change
97
+ 3. Write tests for your change (if applicable)
98
+ 4. Run the tests, ensuring they all pass
99
+ 5. Submit a Pull Request
100
+
101
+ ## License and Authors
102
+
103
+ Licensed under the Apache License, Version 2.0 (the "License");
104
+ you may not use this file except in compliance with the License.
105
+ You may obtain a copy of the License at
106
+
107
+ http://www.apache.org/licenses/LICENSE-2.0
108
+
109
+ Unless required by applicable law or agreed to in writing, software
110
+ distributed under the License is distributed on an "AS IS" BASIS,
111
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
112
+ See the License for the specific language governing permissions and
113
+ limitations under the License.
114
+
115
+ Author: Mevan Samaratunga (mevansam@gmail.com)
data/Rakefile ADDED
@@ -0,0 +1,42 @@
1
+ # Copyright (c) 2014 Mevan Samaratunga
2
+
3
+ $:.unshift(File.expand_path("../../rake", __FILE__))
4
+
5
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __FILE__)
6
+
7
+ require "rubygems"
8
+ require "bundler"
9
+ Bundler.setup(:default, :test)
10
+
11
+ require "rake"
12
+ begin
13
+ require "rspec/core/rake_task"
14
+ rescue LoadError
15
+ end
16
+
17
+ gem_helper = Bundler::GemHelper.new(Dir.pwd)
18
+
19
+ desc "Build gem into the pkg directory"
20
+ task "build" do
21
+ gem_helper.build_gem
22
+ end
23
+
24
+ desc "Build and install into gem environment"
25
+ task "install" do
26
+ Rake::Task["bundler:install"].invoke
27
+ gem_helper.install_gem
28
+ end
29
+
30
+ if defined?(RSpec)
31
+ namespace :spec do
32
+ desc "Run Unit Tests"
33
+ rspec_task = RSpec::Core::RakeTask.new(:unit) do |t|
34
+ t.pattern = "spec/**/*_spec.rb"
35
+ t.rspec_opts = %w(--format progress --colour)
36
+ end
37
+ end
38
+
39
+ desc "Run tests"
40
+ task :spec => %w(spec:unit)
41
+ task :default => :spec
42
+ end
File without changes
@@ -0,0 +1,114 @@
1
+ # Copyright (c) 2014 Mevan Samaratunga
2
+
3
+ require 'chef/knife/stackbuilder_base'
4
+
5
+ class Chef
6
+ class Knife
7
+
8
+ class StackBuild < Knife
9
+
10
+ include Knife::StackBuilderBase
11
+
12
+ banner 'knife stack build STACK_FILE (options)'
13
+
14
+ option :stack_id,
15
+ :long => "--stack-id STACK_ID",
16
+ :description => "The unique ID for the stack. If a stack with the given" +
17
+ "ID does not exist then a new one will be create. If a stack for the" +
18
+ "given ID exists then it will be initialized with its current state."
19
+
20
+ option :node,
21
+ :long => "--node NODE_TYPE_NAME[:SCALE]",
22
+ :description => "Applies the events to all hosts of a given node type. The " +
23
+ "node type is the value of the node key of a list of stack nodes"
24
+
25
+ option :overrides,
26
+ :long => "--overrides OVERRIDES",
27
+ :description => "JSON string or file ending with .json containing " +
28
+ "attributes to be overridden"
29
+
30
+ option :events,
31
+ :long => "--events EVENTS",
32
+ :description => "List of comma separate events to apply. If no events are " +
33
+ "provided then events 'create', 'install', 'configure' will be applied " +
34
+ "to each new host that is either created or bootstrapped first time. If " +
35
+ "node already exists then only the 'configure' event is applied. When " +
36
+ "the 'configure' event is applied chef-client will be run on each node. " +
37
+ "If the 'update' event is applied then the nodes run list will be updated " +
38
+ "and chef-client will be run."
39
+
40
+ option :repo_path,
41
+ :long => "--repo-path REPO_PATH",
42
+ :description => "The path to the Chef repo. This is required in order " +
43
+ "to copy the correct encryption keys from the 'secrets' folder to " +
44
+ "the target host as well as to read the externalized environment",
45
+ :default => '.'
46
+
47
+ option :show_stack_file,
48
+ :long => "--show-stack-file",
49
+ :description => "Outputs the parsed yaml of the stack file and exits."
50
+
51
+ def run
52
+ time_start = Time.now
53
+
54
+ StackBuilder::Common::Config.logger.level = Chef::Log.logger.level
55
+
56
+ repo_path = getConfig(:repo_path)
57
+ environment = getConfig(:environment) || '_default'
58
+
59
+ stack_file = name_args.first
60
+ if stack_file=~/[-_+=.0-9a-zA-Z]+/
61
+ stack_file = Dir.getwd + '/' + stack_file + (stack_file.end_with?('.yml') ? '' : '.yml')
62
+ end
63
+ unless File.exist?(stack_file)
64
+ puts "Stack file '#{stack_file}' does not exist."
65
+ exit 1
66
+ end
67
+
68
+ provider = StackBuilder::Chef::NodeProvider.new(repo_path, environment)
69
+ if getConfig(:show_stack_file)
70
+
71
+ env_vars = provider.get_env_vars
72
+ stack = StackBuilder::Common.load_yaml(stack_file, env_vars)
73
+ puts("Stack file:\n#{stack.to_yaml}")
74
+
75
+ else
76
+ # Validate repo path and update
77
+ # environment before building the stack
78
+ repo = StackBuilder::Chef::Repo.new(repo_path)
79
+ repo.upload_environments(environment)
80
+
81
+ stack_id = getConfig(:stack_id) || ENV['STACK_ID']
82
+ stack_overrides = getConfig(:overrides) || ENV['STACK_OVERRIDES']
83
+
84
+ stack = StackBuilder::Stack::Stack.new(
85
+ provider,
86
+ stack_file,
87
+ stack_id,
88
+ stack_overrides )
89
+
90
+ node = getConfig(:node)
91
+ unless node.nil?
92
+ node_name = node.split(':')[0]
93
+ node_scale = node.split(':')[1]
94
+ node_scale = node_scale.to_i unless node_scale.nil?
95
+ end
96
+
97
+ events = nil
98
+ if getConfig(:events)
99
+ events = events = Set.new(getConfig(:events).split(','))
100
+ end
101
+
102
+ stack.orchestrate(events, node_name, node_scale)
103
+ end
104
+
105
+ ensure
106
+ time_elapsed = Time.now - time_start
107
+
108
+ $stdout.printf( "\nStack build for '%s' took %d minutes and '%.3f' seconds\n",
109
+ stack_file, time_elapsed/60, time_elapsed%60 ) if !getConfig(:show_stack_file)
110
+ end
111
+ end
112
+
113
+ end
114
+ end
@@ -0,0 +1,50 @@
1
+ # Copyright (c) 2014 Mevan Samaratunga
2
+
3
+ require 'chef/knife/stackbuilder_base'
4
+
5
+ class Chef
6
+ class Knife
7
+
8
+ class StackDelete < Knife
9
+
10
+ include Knife::StackBuilderBase
11
+
12
+ banner 'knife stack delete STACK_FILE (options)'
13
+
14
+ option :stack_id,
15
+ :long => "--stack-id STACK_ID",
16
+ :description => "The ID of the stack to delete.",
17
+ :required => true
18
+
19
+ option :repo_path,
20
+ :long => "--repo_path REPO_PATH",
21
+ :description => "The path to the Chef repo. This is required " +
22
+ "in order read the externalized environment",
23
+ :default => '.'
24
+
25
+ def run
26
+ StackBuilder::Common::Config.logger.level = Chef::Log.logger.level
27
+
28
+ environment = getConfig(:environment) || '_default'
29
+ stack_file = name_args.first
30
+
31
+ if stack_file=~/[-_+=.0-9a-zA-Z]+/
32
+ stack_file = Dir.getwd + '/' + stack_file + (stack_file.end_with?('.yml') ? '' : '.yml')
33
+ end
34
+
35
+ unless File.exist?(stack_file)
36
+ puts "Stack file '#{stack_file}' does not exist."
37
+ exit 1
38
+ end
39
+
40
+ stack = StackBuilder::Stack::Stack.new(
41
+ StackBuilder::Chef::NodeProvider.new(getConfig(:repo_path), environment),
42
+ stack_file,
43
+ getConfig(:stack_id) )
44
+
45
+ stack.destroy
46
+ end
47
+ end
48
+
49
+ end
50
+ end
@@ -0,0 +1,62 @@
1
+ # Copyright (c) 2014 Mevan Samaratunga
2
+
3
+ require 'chef/knife/stackbuilder_base'
4
+
5
+ class Chef
6
+ class Knife
7
+
8
+ class StackInitializeRepo < Knife
9
+
10
+ include Knife::StackBuilderBase
11
+
12
+ banner 'knife stack initialize repo (options)'
13
+
14
+ option :repo_path,
15
+ :long => "--repo_path REPO_PATH",
16
+ :description => "The path where a skeleton Chef Berkshelf repo will be created. " +
17
+ "If this is no provided the current working directory will be initialized.",
18
+ :default => '.'
19
+
20
+ option :cert_path,
21
+ :long => "--cert_path CERT_PATH",
22
+ :description => "Path containing folders with server certificates. Each folder " +
23
+ "within this path should be named after the server for which the certs are " +
24
+ "meant post-fixed by _{ENV_NAME}. If name is not post-fixed then the cert " +
25
+ "will be uploaded to all environments"
26
+
27
+ option :certs,
28
+ :long => "--certs SERVER_NAMES",
29
+ :description => "Comma separated list of server names for which self-signed " +
30
+ "certificates will be generated."
31
+
32
+ option :envs,
33
+ :long => "--stack_envs ENVIRONMENTS",
34
+ :description => "Comma separated list of environments to generate"
35
+
36
+ option :cookbooks,
37
+ :long => "--cookbooks COOKBOOKS",
38
+ :description => "A comma separated list of cookbooks and their versions to be " +
39
+ "added to the Berksfile i.e. \"mysql:=5.6.1, wordpress:~> 2.3.0\""
40
+
41
+ def run
42
+ StackBuilder::Common::Config.logger.level = Chef::Log.logger.level
43
+
44
+ cert_path = getConfig(:cert_path)
45
+ certs = getConfig(:certs)
46
+
47
+ if !cert_path.nil? && !certs.nil?
48
+ puts "Only one of --cert_path or --certs can be specified."
49
+ show_usage
50
+ exit 1
51
+ end
52
+
53
+ StackBuilder::Chef::Repo.new(
54
+ getConfig(:repo_path),
55
+ cert_path.nil? ? certs : cert_path,
56
+ getConfig(:envs),
57
+ getConfig(:cookbooks) )
58
+ end
59
+ end
60
+
61
+ end
62
+ end
@@ -0,0 +1,38 @@
1
+ # Copyright (c) 2014 Mevan Samaratunga
2
+
3
+ require 'chef/knife/stackbuilder_base'
4
+
5
+ class Chef
6
+ class Knife
7
+
8
+ class StackUploadCertificates < Knife
9
+
10
+ include Knife::StackBuilderBase
11
+
12
+ banner "knife stack upload certificates"
13
+
14
+ option :repo_path,
15
+ :long => "--repo_path REPO_PATH",
16
+ :description => "The path to the Chef Berkshelf repo. All the " +
17
+ "certificates associated with this repository would have " +
18
+ "been copied to a hidden folder '.certs' within this path " +
19
+ "when it was initialized.",
20
+ :default => '.'
21
+
22
+ option :server,
23
+ :long => "--server NAME",
24
+ :description => "The name of the server whose " +
25
+ "certificate is to be uploaded"
26
+
27
+ def run
28
+ StackBuilder::Common::Config.logger.level = Chef::Log.logger.level
29
+
30
+ environment = getConfig(:environment)
31
+
32
+ repo = StackBuilder::Chef::Repo.new(getConfig(:repo_path))
33
+ repo.upload_certificates(environment, getConfig(:server))
34
+ end
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,49 @@
1
+ # Copyright (c) 2014 Mevan Samaratunga
2
+
3
+ require 'chef/knife/stackbuilder_base'
4
+
5
+ class Chef
6
+ class Knife
7
+
8
+ class StackUploadCookbooks < Knife
9
+
10
+ include Knife::StackBuilderBase
11
+
12
+ banner 'knife stack upload cookbooks (options)'
13
+
14
+ option :repo_path,
15
+ :long => "--repo_path REPO_PATH",
16
+ :description => "The path to the Chef repo containing the Berkshelf file.",
17
+ :default => '.'
18
+
19
+ option :cookbook,
20
+ :long => "--cookbook NAME",
21
+ :description => "The cookbook upload/update"
22
+
23
+ option :berks_options,
24
+ :long => "--berks-options options",
25
+ :description => "Comma separated list of berkshelf upload options"
26
+
27
+ option :berks_knife_config,
28
+ :long => "--berks-knife-config options",
29
+ :description => "Knife configuration file to be passed to Berkshelf"
30
+
31
+ def run
32
+ StackBuilder::Common::Config.logger.level = Chef::Log.logger.level
33
+
34
+ repo = StackBuilder::Chef::Repo.new(getConfig(:repo_path))
35
+
36
+ berks_knife_config = getConfig(:berks_knife_config)
37
+ ENV['BERKSHELF_CHEF_CONFIG'] = berks_knife_config unless berks_knife_config.nil?
38
+
39
+ berks_options = getConfig(:berks_options)
40
+ unless berks_options.nil?
41
+ repo.upload_cookbooks(getConfig(:cookbook), berks_options.gsub(/,/, ' '))
42
+ else
43
+ repo.upload_cookbooks(getConfig(:cookbook))
44
+ end
45
+ end
46
+ end
47
+
48
+ end
49
+ end
@@ -0,0 +1,36 @@
1
+ # Copyright (c) 2014 Mevan Samaratunga
2
+
3
+ require 'chef/knife/stackbuilder_base'
4
+
5
+ class Chef
6
+ class Knife
7
+
8
+ class StackUploadDataBags < Knife
9
+
10
+ include Knife::StackBuilderBase
11
+
12
+ banner "knife stack upload data bags (options)"
13
+
14
+ option :repo_path,
15
+ :long => "--repo_path REPO_PATH",
16
+ :description => "The path to the Chef repo containing the data_bags " +
17
+ "within a 'data_bags' folder. All data bags will be encrypted with " +
18
+ "keys per environment located in the 'secrets' folder of the repo.",
19
+ :default => '.'
20
+
21
+ option :data_bag,
22
+ :long => "--data_bag NAME",
23
+ :description => "The data bag to upload/update"
24
+
25
+ def run
26
+ StackBuilder::Common::Config.logger.level = Chef::Log.logger.level
27
+
28
+ environment = getConfig(:environment)
29
+
30
+ repo = StackBuilder::Chef::Repo.new(getConfig(:repo_path))
31
+ repo.upload_data_bags(environment, getConfig(:data_bag))
32
+ end
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,31 @@
1
+ # Copyright (c) 2014 Mevan Samaratunga
2
+
3
+ require 'chef/knife/stackbuilder_base'
4
+
5
+ class Chef
6
+ class Knife
7
+
8
+ class StackUploadEnvironments < Knife
9
+
10
+ include Knife::StackBuilderBase
11
+
12
+ banner "knife stack upload environments (options)"
13
+
14
+ option :repo_path,
15
+ :long => "--repo_path REPO_PATH",
16
+ :description => "The path to the Chef repo containing " +
17
+ "the environments within an 'environments' folder.",
18
+ :default => '.'
19
+
20
+ def run
21
+ StackBuilder::Common::Config.logger.level = Chef::Log.logger.level
22
+
23
+ environment = getConfig(:environment)
24
+
25
+ repo = StackBuilder::Chef::Repo.new(getConfig(:repo_path))
26
+ repo.upload_environments(environment)
27
+ end
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,39 @@
1
+ # Copyright (c) 2014 Mevan Samaratunga
2
+
3
+ require 'chef/knife/stackbuilder_base'
4
+
5
+ class Chef
6
+ class Knife
7
+
8
+ class StackUploadRepo < Knife
9
+
10
+ include Knife::StackBuilderBase
11
+
12
+ banner "knife stack upload repo (options)"
13
+
14
+ option :repo_path,
15
+ :long => "--repo_path REPO_PATH",
16
+ :description => "The path to the Chef repo.",
17
+ :default => '.'
18
+
19
+ def run
20
+ StackBuilder::Common::Config.logger.level = Chef::Log.logger.level
21
+
22
+ environment = getConfig(:environment)
23
+
24
+ repo = StackBuilder::Chef::Repo.new(getConfig(:repo_path))
25
+
26
+ berks_knife_config = getConfig(:berks_knife_config)
27
+ ENV['BERKSHELF_CHEF_CONFIG'] = berks_knife_config unless berks_knife_config.nil?
28
+
29
+ repo.upload_cookbooks
30
+ repo.upload_roles
31
+
32
+ repo.upload_environments(environment)
33
+ repo.upload_data_bags(environment)
34
+ repo.upload_certificates(environment)
35
+ end
36
+ end
37
+
38
+ end
39
+ end
@@ -0,0 +1,33 @@
1
+ # Copyright (c) 2014 Mevan Samaratunga
2
+
3
+ require 'chef/knife/stackbuilder_base'
4
+
5
+ class Chef
6
+ class Knife
7
+
8
+ class StackUploadRoles < Knife
9
+
10
+ include Knife::StackBuilderBase
11
+
12
+ banner "knife stack upload roles (options)"
13
+
14
+ option :repo_path,
15
+ :long => "--repo_path REPO_PATH",
16
+ :description => "The path to the Chef repo containing " +
17
+ "the roles within a 'roles' folder.",
18
+ :default => '.'
19
+
20
+ option :role,
21
+ :long => "--role NAME",
22
+ :description => "The role upload/update"
23
+
24
+ def run
25
+ StackBuilder::Common::Config.logger.level = Chef::Log.logger.level
26
+
27
+ repo = StackBuilder::Chef::Repo.new(getConfig(:repo_path))
28
+ repo.upload_roles(getConfig(:role))
29
+ end
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,32 @@
1
+ # Copyright (c) 2014 Mevan Samaratunga
2
+
3
+ class Chef
4
+ class Knife
5
+
6
+ module StackBuilderBase
7
+
8
+ def self.included(includer)
9
+
10
+ includer.class_eval do
11
+
12
+ deps do
13
+ require 'stackbuilder'
14
+
15
+ config = OpenStruct.new(
16
+ :logger => Chef::Log.logger,
17
+ :enable_caching => false,
18
+ :timeouts => { :CACHE_TIMEOUT => 1800 } )
19
+
20
+ StackBuilder::Common::Config.configure(config)
21
+ end
22
+ end
23
+
24
+ def getConfig(key)
25
+ key = key.to_sym
26
+ config[key] || Chef::Config[:knife][key]
27
+ end
28
+ end
29
+
30
+ end
31
+ end
32
+ end