berkflow 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6f6efb7683cb8b8251dfc35923a520ecabc68273
4
+ data.tar.gz: 20ef5ac9d249a39a5bf1319d3eec4181fcbf72d5
5
+ SHA512:
6
+ metadata.gz: f34faa9ed17ec3cef061cc435dffe70742e2b6ccb99fef0dd05d2513c62c62fb18d7e646d272c00a60f73c00ae9441f18090c7f02df5d43565680ab56c3945f8
7
+ data.tar.gz: 6714994388de16ebafb05eda462b72541b40ae0709f0fc60c3854762bc463bec0f6878ca4a542cc27ee7c84dbc63e1584bd01e26178f85f2ac0c176326d00871
@@ -0,0 +1,23 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
23
+ berkflow_out/
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem "berkshelf", github: "berkshelf/berkshelf"
6
+ gem "ridley", github: "RiotGames/ridley"
data/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright (c) 2014 Jamie Winsor
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
@@ -0,0 +1,66 @@
1
+ # Berkflow
2
+
3
+ A command line tool for managing Chef Environments using Berkshelf and the [Environment Cookbook Pattern](http://vialstudios.logdown.com/posts/166848-the-environment-cookbook-pattern).
4
+
5
+ > TLDR of the Environment Cookbook Pattern; You have one top level cookbook that is locked to a Chef Environment. One application per Chef Environment. This Chef Environment is named `{application_name}-{environment}` (i.e. "myface-dev").
6
+
7
+ ## Installation
8
+
9
+ $ gem install berkflow
10
+
11
+ ## Usage
12
+
13
+ $ blo help
14
+
15
+ ### Upgrading a Chef Environment
16
+
17
+ Berkflow exposes a command for configuring a Chef Environment and running Chef Client on all nodes in that environment.
18
+
19
+ $ blo upgrade myface-dev myface 1.2.3
20
+ Applying cookbook locks to myface-dev...
21
+ Discovering nodes in myface-dev...
22
+ Running Chef Client on 10 nodes...
23
+ Successfully ran Chef Client on 10 nodes
24
+ Done. See berkflow_out/20140331172904 for logs.
25
+
26
+ Your Chef Server must meet the following requirements:
27
+
28
+ * The `myface-dev` environment must exist
29
+ * Version 1.2.3 of the myface cookbook (and it's dependencies) must be uploaded to the server
30
+ * Version 1.2.3 of the myface cookbook must have a Berksfile.lock. A cookbook having a Berksfile.lock is said to be an "Environment Cookbook"
31
+
32
+ > Note: earlier versions of Berkshelf generated a chefignore file that included the Berksfile.lock. This will prevent your Berksfile.lock from being uploaded. Remove this line from the chefignore of your cookbook. This has been fixed in Berkshelf master and will ship with Berkshelf 3.0.
33
+
34
+ By default, the user you are logged into your current machine and your default id_rsa key will be used for SSH authentication. See the help menu for how to override SSH settings.
35
+
36
+ ### Running Chef Client on a Chef Environment
37
+
38
+ Berkflow has you covered if you just want to run Chef Client on all the nodes in your Chef Environment.
39
+
40
+ $ blo run_chef myface-dev
41
+ Discovering nodes in myface-dev...
42
+ Running Chef Client on 10 nodes...
43
+ Successfully ran Chef Client on 10 nodes
44
+ Done. See berkflow_out/20140331180610 for logs.
45
+
46
+ ### Running shell commands on a Chef Environment
47
+
48
+ Running arbitrary shell commands is possible, too!
49
+
50
+ $ blo exec myface-dev "ls -lah"
51
+ Discovering nodes in myface-dev...
52
+ Executing command on 10 nodes...
53
+ Successfully executed command on 10 nodes
54
+ Done. See berkflow_out/20140331180708 for logs.
55
+
56
+ Shell commands executed with `blo exec` are by default not run with sudo. Use the --sudo flag to elevate.
57
+
58
+ $ blo exec myface-dev "ls -lah" --sudo
59
+
60
+ ## Contributing
61
+
62
+ 1. Fork it ( https://github.com/[my-github-username]/berkflow/fork )
63
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
64
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
65
+ 4. Push to the branch (`git push origin my-new-feature`)
66
+ 5. Create a new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'berkflow/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "berkflow"
8
+ spec.version = Berkflow::VERSION
9
+ spec.authors = ["Jamie Winsor"]
10
+ spec.email = ["jamie@vialstudios.com"]
11
+ spec.summary = %q{A Cookbook-Centric Deployment workflow tool}
12
+ spec.description = %q{A CLI for managing Chef Environments using Berkshelf and the Environment Cookbook Pattern.}
13
+ spec.homepage = "https://github.com/reset/berkflow"
14
+ spec.license = "Apache 2.0"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "solve"
22
+ spec.add_dependency "berkshelf", "~> 3.0.0.beta7"
23
+ spec.add_dependency "ridley", "~> 2.5"
24
+ spec.add_dependency "ridley-connectors", "~> 1.7.0"
25
+ spec.add_dependency "thor", "~> 0.18"
26
+
27
+ spec.add_development_dependency "bundler", "~> 1.6"
28
+ spec.add_development_dependency "rake"
29
+ end
data/bin/blo ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ $:.push File.expand_path('../../lib', __FILE__)
3
+ require 'berkflow/cli'
4
+
5
+ Berkflow::Cli.start(ARGV)
@@ -0,0 +1,11 @@
1
+ require 'celluloid/autostart'
2
+ require 'berkflow/version'
3
+ require 'ridley'
4
+ require 'ridley-connectors'
5
+ require 'berkshelf'
6
+
7
+ Celluloid.logger.level = ::Logger::ERROR
8
+
9
+ module Berkflow
10
+ # Your code goes here...
11
+ end
@@ -0,0 +1,204 @@
1
+ require 'berkflow'
2
+ require 'thor'
3
+ require 'solve'
4
+ require 'tempfile'
5
+ require 'fileutils'
6
+
7
+ module Berkflow
8
+ class Cli < Thor
9
+ def initialize(*args)
10
+ super(*args)
11
+
12
+ if @options[:verbose]
13
+ Ridley.logger.level = ::Logger::INFO
14
+ end
15
+
16
+ if @options[:debug]
17
+ Ridley.logger.level = ::Logger::DEBUG
18
+ end
19
+ end
20
+
21
+ class_option :verbose,
22
+ type: :boolean,
23
+ desc: "Output verbose information",
24
+ aliases: "-v",
25
+ default: false
26
+ class_option :debug,
27
+ type: :boolean,
28
+ desc: "Output debug information",
29
+ aliases: "-d",
30
+ default: false
31
+ class_option :ssh_user,
32
+ type: :string,
33
+ desc: "SSH user to execute commands as",
34
+ aliases: "-u",
35
+ default: ENV["USER"]
36
+ class_option :ssh_password,
37
+ type: :string,
38
+ desc: "Perform SSH authentication with the given password",
39
+ aliases: "-p",
40
+ default: nil
41
+ class_option :ssh_key,
42
+ type: :string,
43
+ desc: "Perform SSH authentication with the given key",
44
+ aliases: "-P",
45
+ default: nil
46
+
47
+ method_option :sudo,
48
+ type: :boolean,
49
+ desc: "Execute with sudo",
50
+ default: false
51
+ desc "exec ENV CMD", "execute an arbitrary shell command on all nodes in an environment."
52
+ def exec(environment, command)
53
+ env = find_environment!(environment)
54
+
55
+ say "Discovering nodes in #{environment}..."
56
+ nodes = find_nodes(environment)
57
+
58
+ if nodes.empty?
59
+ say "No nodes in #{environment}. Done."
60
+ exit(0)
61
+ end
62
+
63
+ say "Executing command on #{nodes.length} nodes..."
64
+ success, failures, out = handle_results nodes.map { |node| ridley.node.run(node.public_hostname, command) }
65
+
66
+ unless success.empty?
67
+ say "Successfully executed command on #{success.length} nodes"
68
+ end
69
+
70
+ unless failures.empty?
71
+ error "Failed to execute command on #{failures.length} nodes"
72
+ end
73
+
74
+ say "Done. See #{out} for logs."
75
+ failures.empty? ? exit(0) : exit(1)
76
+ end
77
+
78
+ desc "run_chef ENV", "run chef on all nodes in the given environment."
79
+ def run_chef(environment)
80
+ env = find_environment!(environment)
81
+
82
+ say "Discovering nodes in #{environment}..."
83
+ nodes = find_nodes(environment)
84
+
85
+ if nodes.empty?
86
+ say "No nodes in #{environment}. Done."
87
+ exit(0)
88
+ end
89
+
90
+ say "Running Chef Client on #{nodes.length} nodes..."
91
+ success, failures, out = handle_results nodes.map { |node| ridley.node.chef_run(node.public_hostname) }
92
+
93
+ unless success.empty?
94
+ say "Successfully ran Chef Client on #{success.length} nodes"
95
+ end
96
+
97
+ unless failures.empty?
98
+ error "Failed to run Chef Client on #{failures.length} nodes"
99
+ end
100
+
101
+ say "Done. See #{out} for logs."
102
+ failures.empty? ? exit(0) : exit(1)
103
+ end
104
+
105
+ desc "upgrade ENV APP VERSION", "upgrade an environment to a specific application version."
106
+ def upgrade(environment, application, version)
107
+ version = sanitize_version(version)
108
+ env = find_environment!(environment)
109
+ cookbook = find_cookbook!(application, version)
110
+
111
+ file = Tempfile.new("berkflow")
112
+ unless contents = cookbook.download_file(:root_file, Berkshelf::Lockfile::DEFAULT_FILENAME, file.path)
113
+ error "#{application} (#{version}) did not contain a Berksfile.lock"
114
+ exit(1)
115
+ end
116
+
117
+ say "Applying cookbook locks to #{environment}..."
118
+ lockfile = Berkshelf::Lockfile.from_file(file.path)
119
+ unless lockfile.apply(environment)
120
+ error "Failed to apply Berksfile.lock to #{environment}."
121
+ exit(1)
122
+ end
123
+
124
+ run_chef(environment)
125
+ ensure
126
+ file.close(true) if file
127
+ end
128
+
129
+ private
130
+
131
+ def ridley
132
+ @ridley ||= Ridley.new(server_url: config.chef.chef_server_url, client_name: config.chef.node_name,
133
+ client_key: config.chef.client_key, ssh: {
134
+ user: @options[:ssh_user], password: @options[:ssh_password], keys: @options[:ssh_key],
135
+ sudo: use_sudo?
136
+ })
137
+ end
138
+
139
+ def config
140
+ Berkshelf::Config.instance
141
+ end
142
+
143
+ def handle_results(result_set)
144
+ failure, success = result_set.partition { |result| result.error? }
145
+ log_dir = log_results(success, failure)
146
+ [success, failure, log_dir]
147
+ end
148
+
149
+ def log_results(success, failure)
150
+ out_dir = File.join("berkflow_out", Time.now.strftime("%Y%m%d%H%M%S"))
151
+ success_dir = File.join(out_dir, "success")
152
+ failure_dir = File.join(out_dir, "failure")
153
+
154
+ [success_dir, failure_dir].each { |dir| FileUtils.mkdir_p(dir) }
155
+ success.each { |result| write_logs(result, success_dir) }
156
+ failure.each { |result| write_logs(result, failure_dir) }
157
+ out_dir
158
+ end
159
+
160
+ def sanitize_version(version)
161
+ Solve::Version.new(version).to_s
162
+ rescue Solve::Errors::InvalidVersionFormat
163
+ error "Invalid version: #{version}. Provide a valid SemVer version string. (i.e. 1.2.3)."
164
+ exit(1)
165
+ end
166
+
167
+ def find_cookbook!(application, version)
168
+ unless cookbook = ridley.cookbook.find(application, version)
169
+ error "Cookbook not found: #{application} (#{version})."
170
+ exit(1)
171
+ end
172
+ cookbook
173
+ end
174
+
175
+ def find_environment!(environment)
176
+ unless env = ridley.environment.find(environment)
177
+ error "Environment not found: #{environment}"
178
+ exit(1)
179
+ end
180
+ env
181
+ end
182
+
183
+ def find_nodes(environment)
184
+ ridley.search(:node, "chef_environment:#{environment}")
185
+ end
186
+
187
+ def use_sudo?
188
+ @options[:sudo].nil? ? true : @options[:sudo]
189
+ end
190
+
191
+ def write_logs(result, dir)
192
+ write_stdout(result, dir)
193
+ write_stderr(result, dir)
194
+ end
195
+
196
+ def write_stdout(result, dir)
197
+ File.open(File.join(dir, "#{result.host}.stdout"), "w") { |file| file.write(result.stdout) }
198
+ end
199
+
200
+ def write_stderr(result, dir)
201
+ File.open(File.join(dir, "#{result.host}.stderr"), "w") { |file| file.write(result.stderr) }
202
+ end
203
+ end
204
+ end
@@ -0,0 +1,3 @@
1
+ module Berkflow
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,156 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: berkflow
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Jamie Winsor
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-04-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: solve
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: berkshelf
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: 3.0.0.beta7
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: 3.0.0.beta7
41
+ - !ruby/object:Gem::Dependency
42
+ name: ridley
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '2.5'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '2.5'
55
+ - !ruby/object:Gem::Dependency
56
+ name: ridley-connectors
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 1.7.0
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: 1.7.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: thor
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: '0.18'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: '0.18'
83
+ - !ruby/object:Gem::Dependency
84
+ name: bundler
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ version: '1.6'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ~>
95
+ - !ruby/object:Gem::Version
96
+ version: '1.6'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rake
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ description: A CLI for managing Chef Environments using Berkshelf and the Environment
112
+ Cookbook Pattern.
113
+ email:
114
+ - jamie@vialstudios.com
115
+ executables:
116
+ - blo
117
+ extensions: []
118
+ extra_rdoc_files: []
119
+ files:
120
+ - .gitignore
121
+ - Gemfile
122
+ - Gemfile.lock
123
+ - LICENSE
124
+ - README.md
125
+ - Rakefile
126
+ - berkflow.gemspec
127
+ - bin/blo
128
+ - lib/berkflow.rb
129
+ - lib/berkflow/cli.rb
130
+ - lib/berkflow/version.rb
131
+ homepage: https://github.com/reset/berkflow
132
+ licenses:
133
+ - Apache 2.0
134
+ metadata: {}
135
+ post_install_message:
136
+ rdoc_options: []
137
+ require_paths:
138
+ - lib
139
+ required_ruby_version: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - '>='
142
+ - !ruby/object:Gem::Version
143
+ version: '0'
144
+ required_rubygems_version: !ruby/object:Gem::Requirement
145
+ requirements:
146
+ - - '>='
147
+ - !ruby/object:Gem::Version
148
+ version: '0'
149
+ requirements: []
150
+ rubyforge_project:
151
+ rubygems_version: 2.0.14
152
+ signing_key:
153
+ specification_version: 4
154
+ summary: A Cookbook-Centric Deployment workflow tool
155
+ test_files: []
156
+ has_rdoc: