small_wonder 0.1.6 → 0.1.7
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.
- data/README.md +3 -2
- data/lib/small_wonder.rb +1 -10
- data/lib/small_wonder/application.rb +5 -10
- data/lib/small_wonder/cli.rb +18 -0
- data/lib/small_wonder/configuratorator.rb +35 -19
- data/lib/small_wonder/deploy.rb +86 -2
- data/lib/small_wonder/utils.rb +1 -23
- metadata +4 -4
data/README.md
CHANGED
@@ -23,11 +23,12 @@ Apache License Version 2.0
|
|
23
23
|
chef_repo_path "/path/to/chef-repo" # the path to your chef-repo
|
24
24
|
ssh_user "user" # the ssh user you want Small Wonder and Salticid to use
|
25
25
|
application_deployment_attribute "deployed_applications" # the attribute Small Wonder will save data back to chef using
|
26
|
-
config_template_working_directory "/tmp/small_wonder" # the local directory temporary template work will be done in
|
27
|
-
remote_working_dir "/tmp/small_wonder_#{Time.now.to_i}" # the remote directory temporary template work will be done in
|
28
26
|
application_deployments_dir "/path/to/chef-repo/application_deployments" # path to your role files
|
29
27
|
application_templates_dir "/path/to/chef-repo/application_templates" # path to your template files
|
30
28
|
databag "apps" # the databag that contains application configuration data items
|
29
|
+
default_metadata Hash["timestamp" => Time.now.to_i, "deploy_by" => ENV['USER']] # metadata saved to chef with each deploy
|
30
|
+
pre_deploy_hooks ["/path/to/a/hook/example.rb"]
|
31
|
+
post_deploy_hooks ["/path/to/a/hook/example.rb"]
|
31
32
|
|
32
33
|
In addition to a Small Wonder config, you need a working Knife configuration file.
|
33
34
|
|
data/lib/small_wonder.rb
CHANGED
@@ -11,6 +11,7 @@ require 'chef'
|
|
11
11
|
require 'highline/import'
|
12
12
|
require 'erb'
|
13
13
|
require 'net/scp'
|
14
|
+
require 'tmpdir'
|
14
15
|
|
15
16
|
__DIR__ = File.dirname(__FILE__)
|
16
17
|
|
@@ -77,16 +78,6 @@ module SmallWonder
|
|
77
78
|
|
78
79
|
def main()
|
79
80
|
|
80
|
-
unless SmallWonder::Utils.sane_working_dir?(SmallWonder::Config.remote_working_dir)
|
81
|
-
SmallWonder::Log.error("Your remote working dir looks strange (#{SmallWonder::Config.remote_working_dir})")
|
82
|
-
exit(1)
|
83
|
-
end
|
84
|
-
|
85
|
-
unless SmallWonder::Utils.sane_working_dir?(SmallWonder::Config.config_template_working_directory)
|
86
|
-
SmallWonder::Log.error("Your local working dir looks strange (#{SmallWonder::Config.config_template_working_directory})")
|
87
|
-
exit(1)
|
88
|
-
end
|
89
|
-
|
90
81
|
# inintialize chef/knife config
|
91
82
|
Chef::Config[:node_name] = SmallWonder::Config.node_name
|
92
83
|
Chef::Config[:client_key] = SmallWonder::Config.client_key
|
@@ -42,6 +42,11 @@ module SmallWonder
|
|
42
42
|
|
43
43
|
# save the data back to the chef node
|
44
44
|
update_application_data(node_name, application_name, "status", "initialized")
|
45
|
+
|
46
|
+
# save metadata back to chef
|
47
|
+
if opts[:metadata]
|
48
|
+
update_application_data(node_name, application_name, "data", opts[:metadata])
|
49
|
+
end
|
45
50
|
end
|
46
51
|
|
47
52
|
def version=(version)
|
@@ -56,16 +61,6 @@ module SmallWonder
|
|
56
61
|
|
57
62
|
private
|
58
63
|
|
59
|
-
def get_existing_version(node, application)
|
60
|
-
version = get_chef_data_value(node, application, "version")
|
61
|
-
|
62
|
-
unless version
|
63
|
-
version = "0"
|
64
|
-
end
|
65
|
-
|
66
|
-
version
|
67
|
-
end
|
68
|
-
|
69
64
|
def set_version(node, application, version)
|
70
65
|
update_application_data(node, application, "version", version)
|
71
66
|
end
|
data/lib/small_wonder/cli.rb
CHANGED
@@ -51,6 +51,24 @@ module SmallWonder
|
|
51
51
|
:default => :info,
|
52
52
|
:proc => Proc.new { |l| l.to_sym }
|
53
53
|
|
54
|
+
option :config_template_working_directory,
|
55
|
+
:short => "-D",
|
56
|
+
:long => "--working-dir",
|
57
|
+
:description => "local config template working directory",
|
58
|
+
:default => Dir.mktmpdir
|
59
|
+
|
60
|
+
option :remote_working_dir,
|
61
|
+
:short => "-R",
|
62
|
+
:long => "--remote-working-dir",
|
63
|
+
:description => "remote config template working directory",
|
64
|
+
:default => "/tmp/small_wonder_#{Time.now.to_i}"
|
65
|
+
|
66
|
+
option :dynamic_metadata,
|
67
|
+
:short => "-M JSON",
|
68
|
+
:long => "--metadata JSON",
|
69
|
+
:description => "Metadata JSON",
|
70
|
+
:default => nil
|
71
|
+
|
54
72
|
help = <<-EOH
|
55
73
|
|
56
74
|
Examples:
|
@@ -2,6 +2,13 @@ module SmallWonder
|
|
2
2
|
class Configuratorator
|
3
3
|
|
4
4
|
def self.generate_and_upload_files(application, path, opts = {})
|
5
|
+
file_list = generate_files(application, path, opts)
|
6
|
+
upload_files(application)
|
7
|
+
apply_files(application, path, opts)
|
8
|
+
file_list
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.generate_files(application, path, opts = {})
|
5
12
|
config_template_dir = "#{SmallWonder::Config.application_templates_dir}/#{application.application_name}"
|
6
13
|
|
7
14
|
templates = Dir["#{config_template_dir}/**/*.erb"]
|
@@ -9,11 +16,16 @@ module SmallWonder
|
|
9
16
|
file_list = []
|
10
17
|
|
11
18
|
templates.each do |file|
|
12
|
-
|
13
|
-
|
19
|
+
begin
|
20
|
+
filename = File.basename(file, '.*')
|
21
|
+
filename.gsub!("VERSION", application.version)
|
14
22
|
|
15
|
-
|
16
|
-
|
23
|
+
reldir = File.dirname(file).gsub("#{config_template_dir}", ".")
|
24
|
+
reldir.gsub!("VERSION", application.version)
|
25
|
+
rescue Exception => e
|
26
|
+
SmallWonder::Log.fatal("Something went badly attempting to replace VERSION with the version number, likely missing version data.\nerror: #{e}")
|
27
|
+
exit(1)
|
28
|
+
end
|
17
29
|
|
18
30
|
file_list << "#{reldir}/#{filename}"
|
19
31
|
|
@@ -21,18 +33,32 @@ module SmallWonder
|
|
21
33
|
|
22
34
|
generated_file = generate_file(file, application, config_template_dir, opts)
|
23
35
|
|
24
|
-
file_dir = "#{SmallWonder::Config.config_template_working_directory}/#{application.application_name}/#{reldir}"
|
36
|
+
file_dir = "#{SmallWonder::Config.config_template_working_directory}/#{application.node_name}/#{application.application_name}/#{reldir}"
|
25
37
|
|
26
38
|
FileUtils.mkdir_p(file_dir)
|
27
39
|
|
28
40
|
SmallWonder::Utils.write_file(generated_file, "#{file_dir}/#{filename}")
|
29
41
|
end
|
30
42
|
|
31
|
-
|
43
|
+
file_list
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.upload_files(application)
|
47
|
+
Net::SSH.start(application.node_name, SmallWonder::Config.ssh_user) do |ssh|
|
48
|
+
ssh.exec!("mkdir -p #{SmallWonder::Config.remote_working_dir}")
|
49
|
+
end
|
50
|
+
|
51
|
+
Net::SCP.start(application.node_name, SmallWonder::Config.ssh_user) do |scp|
|
52
|
+
scp.upload!("#{SmallWonder::Config.config_template_working_directory}/#{application.node_name}/#{application.application_name}", SmallWonder::Config.remote_working_dir, {:recursive => true})
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.apply_files(application, path, opts = {})
|
32
57
|
copy_files_to_install_dir(application.node_name, application.application_name, path)
|
33
|
-
cleanup_working_directories(application.node_name, application.application_name)
|
34
58
|
|
35
|
-
|
59
|
+
if opts[:no_cleanup]
|
60
|
+
cleanup_working_directories(application.node_name, application.application_name)
|
61
|
+
end
|
36
62
|
end
|
37
63
|
|
38
64
|
private
|
@@ -60,16 +86,6 @@ module SmallWonder
|
|
60
86
|
template.result(binding)
|
61
87
|
end
|
62
88
|
|
63
|
-
def self.upload_files(node_name, application)
|
64
|
-
Net::SSH.start(node_name, SmallWonder::Config.ssh_user) do |ssh|
|
65
|
-
ssh.exec!("mkdir -p #{SmallWonder::Config.remote_working_dir}")
|
66
|
-
end
|
67
|
-
|
68
|
-
Net::SCP.start(node_name, SmallWonder::Config.ssh_user) do |scp|
|
69
|
-
scp.upload!("#{SmallWonder::Config.config_template_working_directory}/#{application}", SmallWonder::Config.remote_working_dir, {:recursive => true})
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
89
|
def self.copy_files_to_install_dir(node_name, application, path)
|
74
90
|
Net::SSH.start(node_name, SmallWonder::Config.ssh_user) do |ssh|
|
75
91
|
ssh.exec!("echo \"#{@sudo_password}\n\" | sudo -S cp -Rf #{SmallWonder::Config.remote_working_dir}/#{application}/* /#{path}/")
|
@@ -77,7 +93,7 @@ module SmallWonder
|
|
77
93
|
end
|
78
94
|
|
79
95
|
def self.cleanup_working_directories(node_name, application)
|
80
|
-
FileUtils.rm_rf("#{SmallWonder::Config.config_template_working_directory}/#{application}")
|
96
|
+
FileUtils.rm_rf("#{SmallWonder::Config.config_template_working_directory}/#{node_name}/#{application}")
|
81
97
|
|
82
98
|
Net::SSH.start(node_name, SmallWonder::Config.ssh_user) do |ssh|
|
83
99
|
ssh.exec!("rm -rf #{SmallWonder::Config.remote_working_dir}")
|
data/lib/small_wonder/deploy.rb
CHANGED
@@ -48,15 +48,25 @@ module SmallWonder
|
|
48
48
|
SmallWonder::Log.info("Did not get a app version to deploy on the command line, assuming you will set it during the deploy.")
|
49
49
|
end
|
50
50
|
|
51
|
+
metadata = build_metadata(SmallWonder::Config.default_metadata, SmallWonder::Config.dynamic_metadata)
|
52
|
+
|
53
|
+
execute_pre_deploy_hooks(action, application_name, metadata, nodes, SmallWonder::Config.version)
|
54
|
+
|
55
|
+
application_objects = []
|
56
|
+
|
51
57
|
nodes.each do |node|
|
52
58
|
if SmallWonder::Config.version
|
53
|
-
application = SmallWonder::Application.new(node, application_name, {:version => SmallWonder::Config.version})
|
59
|
+
application = SmallWonder::Application.new(node, application_name, {:version => SmallWonder::Config.version, :metadata => metadata})
|
54
60
|
else
|
55
|
-
application = SmallWonder::Application.new(node, application_name)
|
61
|
+
application = SmallWonder::Application.new(node, application_name, {:metadata => metadata})
|
56
62
|
end
|
57
63
|
|
58
64
|
deploy_application(action, application, sudo_password)
|
65
|
+
application_objects << application
|
59
66
|
end
|
67
|
+
|
68
|
+
execute_post_deploy_hooks(action, application_name, metadata, nodes, application_objects, SmallWonder::Config.version)
|
69
|
+
|
60
70
|
end
|
61
71
|
else
|
62
72
|
SmallWonder::Log.info("No nodes found for your search.")
|
@@ -71,6 +81,80 @@ module SmallWonder
|
|
71
81
|
end
|
72
82
|
end
|
73
83
|
|
84
|
+
def self.build_metadata(default = nil, dynamic = nil)
|
85
|
+
metadata = Hash.new
|
86
|
+
|
87
|
+
if default
|
88
|
+
metadata.store("default", default)
|
89
|
+
end
|
90
|
+
|
91
|
+
if dynamic
|
92
|
+
metadata.store("dynamic", JSON.parse(dynamic))
|
93
|
+
end
|
94
|
+
|
95
|
+
metadata
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.build_hook_data(action, application_name, metadata, nodes, application_objects = nil, version = nil)
|
99
|
+
if application_objects
|
100
|
+
objs = []
|
101
|
+
|
102
|
+
application_objects.each do |obj|
|
103
|
+
hash = {}
|
104
|
+
|
105
|
+
obj.instance_variables.each do |var|
|
106
|
+
hash.store(var.to_s.delete("@"), obj.instance_variable_get(var))
|
107
|
+
end
|
108
|
+
|
109
|
+
objs << hash
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
{
|
114
|
+
"action" => action,
|
115
|
+
"application_name" => application_name,
|
116
|
+
"metadata" => metadata,
|
117
|
+
"nodes" => nodes,
|
118
|
+
"version" => version,
|
119
|
+
"application" => objs
|
120
|
+
}
|
121
|
+
end
|
122
|
+
|
123
|
+
def self.execute_pre_deploy_hooks(action, application_name, metadata, nodes, version)
|
124
|
+
data = build_hook_data(action, application_name, metadata, nodes, version)
|
125
|
+
execute_hooks(:pre, data)
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.execute_post_deploy_hooks(action, application_name, metadata, nodes, application_objects, version)
|
129
|
+
data = build_hook_data(action, application_name, metadata, nodes, application_objects, version)
|
130
|
+
execute_hooks(:post, data)
|
131
|
+
end
|
132
|
+
|
133
|
+
def self.execute_hooks(type, data)
|
134
|
+
data_json = data.to_json
|
135
|
+
|
136
|
+
case type
|
137
|
+
when :pre
|
138
|
+
hooks = SmallWonder::Config.pre_deploy_hooks
|
139
|
+
when :post
|
140
|
+
hooks = SmallWonder::Config.post_deploy_hooks
|
141
|
+
end
|
142
|
+
|
143
|
+
if hooks
|
144
|
+
hooks.each do |cmd|
|
145
|
+
begin
|
146
|
+
SmallWonder::Log.info("Running #{type} deploy hook, command: #{cmd}")
|
147
|
+
output = `#{cmd} \'#{data_json}\'`
|
148
|
+
SmallWonder::Log.debug("#{type} deploy hook output:")
|
149
|
+
SmallWonder::Log.debug(output)
|
150
|
+
rescue Exception => e
|
151
|
+
SmallWonder::Log.fatal("#{type} deploy hook failed, command: #{cmd}, data: #{data_json}")
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
157
|
+
|
74
158
|
## deploy step
|
75
159
|
# Creates a new salticid host for node, and calls <app>.deploy on it.
|
76
160
|
def self.run_salticid_task(action, application, sudo_password)
|
data/lib/small_wonder/utils.rb
CHANGED
@@ -36,27 +36,5 @@ module SmallWonder
|
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
|
-
def self.sane_working_dir?(dir)
|
40
|
-
sane = []
|
41
|
-
sane << starts_with?(dir, "/tmp/small_wonder")
|
42
|
-
|
43
|
-
if dir.include?("..")
|
44
|
-
sane << false
|
45
|
-
else
|
46
|
-
sane << true
|
47
|
-
end
|
48
|
-
|
49
|
-
if sane.include?(false)
|
50
|
-
false
|
51
|
-
else
|
52
|
-
true
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def self.starts_with?(string, prefix)
|
57
|
-
prefix = prefix.to_s
|
58
|
-
string[0, prefix.length] == prefix
|
59
|
-
end
|
60
|
-
|
61
39
|
end
|
62
|
-
end
|
40
|
+
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: small_wonder
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 21
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 1
|
9
|
-
-
|
10
|
-
version: 0.1.
|
9
|
+
- 7
|
10
|
+
version: 0.1.7
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Joe Williams
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-
|
18
|
+
date: 2012-10-05 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: mixlib-config
|