atesta 0.0.0 → 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.0
1
+ 0.0.1
data/lib/atesta.rb CHANGED
@@ -0,0 +1,12 @@
1
+ require 'lib/block_attr'
2
+ require 'lib/class_attr'
3
+ require 'lib/execution'
4
+ require 'lib/hash'
5
+ require 'lib/lock'
6
+ require 'lib/node'
7
+ require 'lib/output'
8
+ require 'lib/recipe'
9
+ require 'lib/release_window'
10
+ require 'lib/runtime_error'
11
+ require 'lib/status'
12
+ require 'lib/resource'
data/lib/block_attr.rb ADDED
@@ -0,0 +1,23 @@
1
+ module BlockAttr
2
+
3
+ def self.included klass
4
+ klass.extend ClassMethods
5
+ end
6
+
7
+ module ClassMethods
8
+
9
+ # No Convention over Configuration attributes.
10
+ def block_attr *list
11
+ @attributes = ((@attributes || [])+ list).uniq
12
+ list.each do |new_method|
13
+ instance_eval do
14
+ define_method new_method do |argument|
15
+ instance_variable_set "@#{new_method}", argument
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ end
22
+
23
+ end
data/lib/class_attr.rb ADDED
@@ -0,0 +1,26 @@
1
+
2
+ # Dynamically define class attribute accessors
3
+ module ClassAttr
4
+
5
+ def self.included klass
6
+ klass.extend ClassMethods
7
+ end
8
+
9
+ module ClassMethods
10
+
11
+ def class_attr *list
12
+ list.each do |my_method|
13
+ eval "
14
+ def self.#{my_method}= #{my_method}
15
+ @#{my_method} = #{my_method}
16
+ end
17
+ def self.#{my_method}
18
+ @#{my_method}
19
+ end
20
+ "
21
+ end
22
+ end
23
+
24
+ end
25
+
26
+ end
data/lib/execution.rb ADDED
@@ -0,0 +1,95 @@
1
+ require 'md5'
2
+
3
+ class Execution
4
+
5
+ attr_accessor :user, :always_run
6
+
7
+ def initialize title, value
8
+ @commands = []
9
+ @title = title
10
+ @value = value
11
+ @always_run = false
12
+ end
13
+
14
+ def ignore
15
+ Output.jump
16
+ Output.error 'Ignoring execution', '..'
17
+ Output.ruler
18
+ end
19
+
20
+ def always_run boolean
21
+ @always_run = boolean
22
+ end
23
+
24
+ def banner
25
+ Output.jump
26
+ Output.info @title, @value
27
+ # Output.info 'Hash', serialize
28
+ Output.ruler
29
+ end
30
+
31
+ def serialize
32
+ MD5.hexdigest @title + (@commands * '')
33
+ end
34
+
35
+ def self.block title, value = nil, user = 'deploy', &block
36
+ @object = new(title, value)
37
+ @object.user = user
38
+ @object.execute &block
39
+ end
40
+
41
+ def execute &block
42
+ banner
43
+ yield(self)
44
+ run_commands
45
+ end
46
+
47
+ def run_commands
48
+ st = Status.get
49
+ st.execution!.hashes = [] unless st.execution.respond_to?(:hashes)
50
+ if st.execution.hashes.include?(serialize)
51
+ Output.warn 'Ignoring', 'Already executed..'
52
+ end
53
+ if @always_run
54
+ Output.warn 'Always run switch detected', 'Forcing execution!'
55
+ end
56
+ @commands.map { |cmd| code_for(cmd) }.each do |code|
57
+ Output.command code
58
+ unless @always_run
59
+ next if st.execution.hashes.include? serialize
60
+ end
61
+ run_or_raise code
62
+ end
63
+ unless st.execution.hashes.include? serialize
64
+ st.execution.hashes << serialize
65
+ end
66
+ nil
67
+ end
68
+
69
+ def run_or_raise code
70
+ return if system code
71
+ Output.jump
72
+ Output.ruler :error
73
+ Output.error 'EXECUTION FAILED', code
74
+ Output.ruler :error
75
+ raise 'Que pedo!'
76
+ end
77
+
78
+ def code_for cmd
79
+ if cmd[:path]
80
+ "cd #{cmd[:path]} && sudo -u #{@user} #{cmd[:command]}"
81
+ else
82
+ "sudo -u #{@user} #{cmd[:command]}"
83
+ end
84
+ end
85
+
86
+ def run string, path = nil
87
+ @commands << { :command => string, :path => path }
88
+ end
89
+
90
+ def command string, path = nil
91
+ run string, path
92
+ end
93
+
94
+ end
95
+
data/lib/hash.rb ADDED
@@ -0,0 +1,36 @@
1
+ class Hash
2
+
3
+ # Merges self with another hash, recursively.
4
+ #
5
+ # This code was lovingly stolen from some random gem:
6
+ # http://gemjack.com/gems/tartan-0.1.1/classes/Hash.html
7
+ #
8
+ # Thanks to whoever made it.
9
+ def deep_merge(hash)
10
+ target = dup
11
+
12
+ hash.keys.each do |key|
13
+ if hash[key].is_a? Hash and self[key].is_a? Hash
14
+ target[key] = target[key].deep_merge(hash[key])
15
+ next
16
+ end
17
+
18
+ target[key] = hash[key]
19
+ end
20
+
21
+ target
22
+ end
23
+
24
+ # From: http://www.gemtacular.com/gemdocs/cerberus-0.2.2/doc/classes/Hash.html
25
+ # File lib/cerberus/utils.rb, line 42
26
+ def deep_merge!(second)
27
+ second.each_pair do |k,v|
28
+ if self[k].is_a?(Hash) and second[k].is_a?(Hash)
29
+ self[k].deep_merge!(second[k])
30
+ else
31
+ self[k] = second[k]
32
+ end
33
+ end
34
+ end
35
+
36
+ end
data/lib/lock.rb ADDED
@@ -0,0 +1,22 @@
1
+ class Lock; end
2
+
3
+ class << Lock
4
+
5
+ def use lock_file
6
+ @lock_file = lock_file
7
+ end
8
+
9
+ def locked?
10
+ File.exist? @lock_file
11
+ end
12
+
13
+ def lock
14
+ system "touch #{@lock_file}"
15
+ end
16
+
17
+ def unlock
18
+ lock
19
+ system "rm #{@lock_file}"
20
+ end
21
+
22
+ end
data/lib/node.rb ADDED
@@ -0,0 +1,38 @@
1
+ require 'rubygems'
2
+ require 'hashie'
3
+
4
+ class Node
5
+
6
+ def self.set object
7
+ @node = object
8
+ end
9
+
10
+ def self.get
11
+ @node
12
+ end
13
+
14
+ def dna
15
+ @dna
16
+ end
17
+
18
+ def self.magic matcher, path
19
+ set resolve(matcher, path)
20
+ end
21
+
22
+ def self.resolve matcher, path
23
+ Output.banner 'Loading', 'Merging default attributes...'
24
+ @hash = @dna = eval(File.read path)
25
+ Dir[matcher].each do |attr_file|
26
+ next if File.directory? attr_file
27
+ Output.info 'Merging node', attr_file
28
+ @hash = @hash.deep_merge(eval(File.read attr_file) || {}).
29
+ deep_merge(@hash)
30
+ end
31
+ # TODO: remove ARGV
32
+ @hash[:instance_role] = ARGV[1]
33
+ @hash[:platform] = 'ubuntu'
34
+ Hashie::Mash.new(@hash)
35
+ end
36
+
37
+ end
38
+
data/lib/output.rb ADDED
@@ -0,0 +1,50 @@
1
+ require 'rubygems'
2
+ require 'isna'
3
+
4
+ module Output
5
+
6
+ CHAR = '#'
7
+ RULER = '='
8
+
9
+ def self.jump
10
+ puts ''
11
+ end
12
+
13
+ def self.ruler type_of_output = :info
14
+ puts "[%s;%s;%sm#{CHAR} #{RULER * 77}" % type_for(type_of_output)
15
+ end
16
+
17
+ def self.banner key, value, type_of_output = :info
18
+ jump
19
+ kv key, value, type_for(type_of_output)
20
+ ruler type_of_output
21
+ end
22
+
23
+ def self.command code
24
+ puts "#{code}"
25
+ end
26
+
27
+ def self.type_for symbol
28
+ return [0, 32, 1] if symbol == :info
29
+ return [0, 33, 1] if symbol == :warn
30
+ return [5, 31, 1] if symbol == :error
31
+ [1, 36, 1]
32
+ end
33
+
34
+ def self.info key, value
35
+ kv key, value, self.type_for(:info)
36
+ end
37
+
38
+ def self.warn key, value
39
+ kv key, value, type_for(:warn)
40
+ end
41
+
42
+ def self.error key, value
43
+ kv key, value, type_for(:error)
44
+ end
45
+
46
+ def self.kv key, value, combo
47
+ puts "[%s;%s;%sm#{CHAR} #{key}: #{value}" % combo
48
+ end
49
+
50
+ end
data/lib/recipe.rb ADDED
@@ -0,0 +1,183 @@
1
+ module Recipe
2
+
3
+ attr_accessor :stack
4
+
5
+ def change_binding binding
6
+ @binding = binding
7
+ end
8
+
9
+ def init binding
10
+ change_binding binding
11
+ stack
12
+ end
13
+
14
+ def resource_not_supported *args
15
+ Output.warn 'Resource not supported', args.inspect
16
+ end
17
+
18
+ def clean_stack!
19
+ @stack = []
20
+ end
21
+
22
+ def cron_append object
23
+ @cron_jobs << object
24
+ Output.info 'Cron job detected', object.get_title
25
+ end
26
+
27
+ def cron_variable title, variable
28
+ @cron_jobs ||= []
29
+ @cron_vars ||= []
30
+ @cron_vars << ''
31
+ @cron_vars << "# #{title}"
32
+ @cron_vars << variable
33
+ end
34
+
35
+ def setup_crontab
36
+ Output.jump
37
+ Output.info 'Setting up nice cron jobs', 'this is sweeet root crontab!'
38
+ Output.ruler
39
+ t = ::Tempfile.new('temporal_crontab')
40
+ @cron_vars.each do |line|
41
+ t.puts line
42
+ end
43
+ @cron_jobs.each do |resource|
44
+ t.puts ''
45
+ t.puts "# #{resource.get_title}"
46
+ t.puts "#{resource.run}"
47
+ end
48
+ t.close
49
+ system "cat #{t.path} | crontab"
50
+ system 'crontab -l'
51
+ t.unlink
52
+ end
53
+
54
+ def stack
55
+ @stack ||= []
56
+ end
57
+
58
+ def stack_append object
59
+ @stack << object
60
+ end
61
+
62
+ def template name, &block
63
+ stack_append ::Resource::Template.new(name, &block)
64
+ end
65
+
66
+ def directory name, &block
67
+ stack_append ::Resource::Directory.new(name, &block)
68
+ end
69
+
70
+ def gem_package name, &block
71
+ stack_append ::Resource::Gem.new(name, &block)
72
+ end
73
+
74
+ def package package_name, &block
75
+ block = Proc.new {} unless block
76
+ stack_append ::Resource::Package.new(package_name, &block)
77
+ end
78
+
79
+ def execute title, &block
80
+ stack_append ::Resource::Execute.new(title, &block)
81
+ end
82
+
83
+ def user username, &block
84
+ stack_append ::Resource::User.new(username, &block)
85
+ end
86
+
87
+ def link source, &block
88
+ stack_append ::Resource::Link.new(source, &block)
89
+ end
90
+
91
+ def git repository, &block
92
+ stack_append ::Resource::Git.new(repository, &block)
93
+ end
94
+
95
+ def cron title, &block
96
+ cron_append ::Resource::Cron.new(title, &block)
97
+ end
98
+
99
+ def service name, &block
100
+ stack_append ::Resource::Service.new(name, &block)
101
+ end
102
+
103
+ def system_group groupname, &block
104
+ stack_append ::Resource::Group.new(groupname, &block)
105
+ end
106
+
107
+ def remote_file target, &block
108
+ stack_append ::Resource::RemoteFile.new(target, &block)
109
+ end
110
+
111
+ def file target, &block
112
+ stack_append ::Resource::File.new(target, &block)
113
+ end
114
+
115
+ def swap_release release_path, &block
116
+ stack_append ::Resource::SwapRelease.new(release_path, &block)
117
+ end
118
+
119
+ def version location, &block
120
+ stack_append ::Resource::Version.new(location, &block)
121
+ end
122
+
123
+ def hook target
124
+ target = status.release_path + "/deploy/#{target}.rb"
125
+ Resource::Hook.new(target, @binding).run
126
+ end
127
+
128
+ def node
129
+ @node ||= ::Node.get
130
+ end
131
+
132
+ def include_recipe name
133
+ @recipes ||= []
134
+ if @recipes.include? name
135
+ Output.warn 'Already loaded recipe with name', name
136
+ return
137
+ end
138
+ @recipes << name
139
+ Output.info 'Including recipe', name
140
+ include_file :Recipe, @recipes_target % name
141
+ end
142
+
143
+ def include_file file_type, some_file
144
+ unless ::File.exist? some_file
145
+ Output.warn "#{file_type} file not found (skipping)", some_file
146
+ return
147
+ end
148
+ eval(File.read(some_file), @binding)
149
+ Output.info 'Executable units', stack.size
150
+ end
151
+
152
+ def load_recipes recipes_target, list
153
+ Output.banner 'Loading', 'Instantiating recipes...'
154
+ @recipes_target = recipes_target
155
+ list.each { |recipe| include_recipe recipe }
156
+ end
157
+
158
+ def summary
159
+ Output.banner 'Printing', 'Execution summary'
160
+ Output.info 'Total Recipes', node[:recipes].size
161
+ Output.info 'Total executable stack units', stack.size
162
+ end
163
+
164
+ def status
165
+ Status.get
166
+ end
167
+
168
+ def transaction &block
169
+ yield
170
+ rescue => e
171
+ Output.error 'Rolling back', ' =:D '
172
+ raise e
173
+ end
174
+
175
+ def whoami
176
+ (File.exist? 'env/whoami') ? File.read('env/whoami').chop : 'Unknown user'
177
+ end
178
+
179
+ def server_ip_address
180
+ (File.exist? 'env/ip') ? File.read('env/ip').chop : 'Unknown ip address'
181
+ end
182
+
183
+ end
@@ -0,0 +1,30 @@
1
+ class ReleaseWindow
2
+
3
+ def initialize releases_path, releases, window = 5, matcher = '*'
4
+ @matcher = matcher
5
+ @releases_path = releases_path
6
+ @releases = releases.compact
7
+ @window = window
8
+ end
9
+
10
+ def all_releases
11
+ Dir[@releases_path + @matcher].map do |r|
12
+ File.basename(r)
13
+ end.compact
14
+ end
15
+
16
+ def deletable_releases
17
+ all_releases - current_window
18
+ end
19
+
20
+ def current_window
21
+ @releases.last(@window)
22
+ end
23
+
24
+ def deletable_files
25
+ deletable_releases.map do |r|
26
+ "#{@releases_path}#{r}"
27
+ end
28
+ end
29
+
30
+ end
@@ -0,0 +1,49 @@
1
+ module Resource
2
+
3
+ class Base
4
+
5
+ include ::BlockAttr
6
+ include ::ClassAttr
7
+
8
+ # Access child's class_name statically
9
+ class_attr :class_name
10
+
11
+ # This resource should be executed always??
12
+ # *boolean*
13
+ block_attr :always_run
14
+
15
+ # Setup class name
16
+ def self.inherited name
17
+ @class_name = name
18
+ end
19
+
20
+ # Translate octal to decimal modes
21
+ def unix_mode
22
+ @mode.to_i.to_s(8)
23
+ end
24
+
25
+ # Configure default settings for any resource
26
+ def set_base_defaults
27
+ @not_if = false
28
+ @owner = 'root'
29
+ @always_run = false
30
+ end
31
+
32
+ def not_if condition = nil, &block
33
+ if condition.is_a?(String)
34
+ if system(condition)
35
+ @not_if = true
36
+ end
37
+ end
38
+ if block_given? && yield
39
+ @not_if = true
40
+ end
41
+ end
42
+
43
+ def should_skip?
44
+ @not_if == true
45
+ end
46
+
47
+ end
48
+
49
+ end
@@ -0,0 +1,23 @@
1
+ module Resource
2
+
3
+ class Cron < Base
4
+
5
+ block_attr :title, :command
6
+
7
+ def initialize title, &block
8
+ @command = ''
9
+ @title = title
10
+ self.instance_eval(&block)
11
+ end
12
+
13
+ def get_title
14
+ @title
15
+ end
16
+
17
+ def run
18
+ @command
19
+ end
20
+
21
+ end
22
+
23
+ end
@@ -0,0 +1,24 @@
1
+ module Resource
2
+
3
+ class Directory < Base
4
+
5
+ block_attr :owner, :group, :mode, :action, :recursive, :target
6
+
7
+ def initialize target, &block
8
+ set_base_defaults
9
+ @target = target
10
+ self.instance_eval(&block)
11
+ end
12
+
13
+ def run
14
+ Execution.block 'Creating directory', @target, 'root' do |b|
15
+ b.always_run @always_run
16
+ b.run "mkdir -p #{@target}"
17
+ b.run "chmod #{unix_mode} #{@target}"
18
+ b.run "chown #{@owner}:#{@group} #{@target}"
19
+ end
20
+ end
21
+
22
+ end
23
+
24
+ end
@@ -0,0 +1,30 @@
1
+ module Resource
2
+
3
+ class Execute < Base
4
+
5
+ block_attr :owner, :title, :path
6
+
7
+ def initialize title, &block
8
+ set_base_defaults
9
+ @path = nil
10
+ @commands = []
11
+ @title = title
12
+ self.instance_eval(&block)
13
+ end
14
+
15
+ def command string
16
+ @commands << { :command => string, :path => @path }
17
+ end
18
+
19
+ def run
20
+ Execution.block 'Execution', @title, @owner do |b|
21
+ b.always_run @always_run
22
+ @commands.each do |command_hash|
23
+ b.run "#{command_hash[:command]}", command_hash[:path]
24
+ end
25
+ end
26
+ end
27
+
28
+ end
29
+
30
+ end
@@ -0,0 +1,33 @@
1
+ module Resource
2
+
3
+ class File < Base
4
+
5
+ block_attr :content, :owner, :group, :mode, :action, :target
6
+
7
+ def initialize target, &block
8
+ set_base_defaults
9
+ @target = target
10
+ self.instance_eval(&block)
11
+ end
12
+
13
+ # Compile any given string into a file remotely!
14
+ #
15
+ # 1. try to run everytime
16
+ # 2. if file exists and current string checksum matches the old
17
+ # one skip it!
18
+ # 3. otherwise just re-compile the file!
19
+ #
20
+ def run
21
+ Execution.block 'Creating a custom file', @target, 'root' do |b|
22
+ b.always_run true
23
+ b.run "mkdir -p #{::File.dirname(@target)}"
24
+ ::File.open(@target, 'w+') { |file| file.write @content }
25
+ b.run "chown #{@owner}:#{@owner} #{::File.dirname(@target)}"
26
+ b.run "chown #{@owner}:#{@owner} #{@target}"
27
+ b.run "chmod #{unix_mode} #{@target}"
28
+ end
29
+ end
30
+
31
+ end
32
+
33
+ end
@@ -0,0 +1,27 @@
1
+ module Resource
2
+
3
+ class Gem < Base
4
+
5
+ block_attr :version, :source, :action, :owner, :name
6
+
7
+ def initialize name, &block
8
+ set_base_defaults
9
+ @owner = 'root'
10
+ @name = name
11
+ self.instance_eval(&block)
12
+ end
13
+
14
+ def run
15
+ command = []
16
+ command << "gem install #{@name}"
17
+ command << "--source #{@source}" if @source
18
+ command << "--version #{@version}" if @version
19
+ Execution.block 'Installing gem', @name, @owner do |b|
20
+ b.always_run @always_run
21
+ b.run(command * ' ')
22
+ end
23
+ end
24
+
25
+ end
26
+
27
+ end
@@ -0,0 +1,102 @@
1
+ module Resource
2
+
3
+ class Git < Base
4
+
5
+ block_attr :owner, :repository, :sandbox_path, :sandbox_name
6
+ block_attr :branch, :release_path, :timestamped, :swaping_branch
7
+ block_attr :capsule_object
8
+
9
+ def initialize repository, &block
10
+ set_base_defaults
11
+ @repository = repository
12
+ @sandbox_path = '/tmp'
13
+ @sandbox_name = 'repository_sandbox'
14
+ @always_run = true
15
+ @timestamped = true
16
+ @swaping_branch = 'master'
17
+ self.instance_eval(&block)
18
+ end
19
+
20
+ def run
21
+ clone unless cloned?
22
+ fetch_and_pull
23
+ revision = revision(@branch)
24
+ checkout revision
25
+ key = @timestamped ? Time.now.strftime('%Y%m%d%H%M%S') : revision
26
+ my_release_path = @release_path + key
27
+ relocate my_release_path
28
+ encapsulate_object revision, my_release_path, key
29
+ end
30
+
31
+ protected
32
+
33
+ def encapsulate_object revision, my_release_path, key
34
+ @capsule_object.release_path = my_release_path
35
+ @capsule_object.revision = revision
36
+ @capsule_object.key = key
37
+ end
38
+
39
+ def clone args = []
40
+ Execution.block 'Cloning repository', @owner do |b|
41
+ b.always_run @always_run
42
+ b.run "git clone #{args.join(' ')} #{@repository} #{@sandbox_path}/#{@sandbox_name}", @sandbox_path
43
+ end
44
+ end
45
+
46
+ def fetch_and_pull
47
+ Execution.block 'Updating existing repository changes', @repository, @owner do |b|
48
+ b.always_run @always_run
49
+ b.run "git checkout #{@swaping_branch}", checkout_location
50
+ b.run 'git fetch', checkout_location
51
+ b.run 'git pull', checkout_location
52
+ end
53
+ end
54
+
55
+ def relocate target
56
+ Execution.block 'Installing snapshot of checkout', @owner, @owner do |b|
57
+ b.always_run @always_run
58
+ b.run "touch #{target}"
59
+ b.run "rm -rf #{target}"
60
+ b.run "cp -r #{checkout_location} #{target}"
61
+ b.run "ls #{target} | column"
62
+ end
63
+ end
64
+
65
+ def revision branch
66
+ return @revision if @revision
67
+ Execution.block "Extracting revision hash for", branch, @owner do |b|
68
+ b.always_run @always_run
69
+ b.run "git ls-remote #{@repository} #{branch}"
70
+ end
71
+ @revision = `sudo -u #{@owner} git ls-remote #{@repository} #{branch}`
72
+ @revision = (@revision.scan /[a-zA-Z0-9]{40}/).first
73
+ end
74
+
75
+ def checkout revision
76
+ Execution.block "Using revision to create new branch", "#{revision} ~> #{@owner}", @owner do |b|
77
+ b.always_run @always_run
78
+ b.run "git checkout #{@swaping_branch}", checkout_location
79
+ b.run "git branch -f #{@owner}", checkout_location
80
+ b.run "git branch -D #{@owner}", checkout_location
81
+ b.run "git checkout -b #{@owner} #{revision}", checkout_location
82
+ end
83
+ end
84
+
85
+ def checkout_location
86
+ "#{@sandbox_path}/#{@sandbox_name}"
87
+ end
88
+
89
+ def remove
90
+ Execution.block "Removing existing copy of repository: #{checkout_location}", @owner do |b|
91
+ b.always_run @always_run
92
+ b.run "rm -rf #{checkout_location}"
93
+ end
94
+ end
95
+
96
+ def cloned?
97
+ ::File.exist? checkout_location
98
+ end
99
+
100
+ end
101
+
102
+ end
@@ -0,0 +1,28 @@
1
+ module Resource
2
+
3
+ class Group < Base
4
+
5
+ block_attr :groupname, :gid
6
+
7
+ def initialize groupname, &block
8
+ set_base_defaults
9
+ @groupname = groupname
10
+ self.instance_eval(&block)
11
+ end
12
+
13
+ def run
14
+ Execution.block 'Creating new group', @groupname, 'root' do |b|
15
+ b.always_run @always_run
16
+ groups = `cat /etc/group | cut -d: -f1`
17
+ groups = groups.scan(/[a-zA-Z\-_]+/)
18
+ if groups.include? @groupname
19
+ Output.warn 'Aborted, I think this group already exists', groups.inspect
20
+ else
21
+ b.run "groupadd -g #{@gid} #{@groupname}"
22
+ end
23
+ end
24
+ end
25
+
26
+ end
27
+
28
+ end
@@ -0,0 +1,25 @@
1
+ module Resource
2
+
3
+ class Hook < Base
4
+
5
+ def initialize target, binding
6
+ @target = target
7
+ @binding = binding
8
+ end
9
+
10
+ def run
11
+ if ::File.exist?(@target)
12
+ Output.info 'Hook found', @target
13
+ eval(::File.read(@target), @binding)
14
+ else
15
+ Output.warn 'Hook not found', @target
16
+ end
17
+ end
18
+
19
+ def should_skip?
20
+ false
21
+ end
22
+
23
+ end
24
+
25
+ end
@@ -0,0 +1,27 @@
1
+ module Resource
2
+
3
+ class Link < Base
4
+
5
+ block_attr :source, :to, :owner, :group
6
+
7
+ def initialize source, &block
8
+ set_base_defaults
9
+ @source = source
10
+ @to = 'no_target'
11
+ self.instance_eval(&block)
12
+ end
13
+
14
+ def run
15
+ Execution.block 'Building Link', "#{@source} ~> #{@to}", @owner do |b|
16
+ b.always_run @always_run
17
+ b.run "mkdir -p #{::File.dirname(@to)}"
18
+ b.run "mkdir -p #{::File.dirname(@source)}" unless ::File.exist?(::File.dirname(@source))
19
+ b.run "touch #{@source}"
20
+ b.run "rm -rf #{@source}"
21
+ b.run "ln -s #{@to} #{@source}"
22
+ end
23
+ end
24
+
25
+ end
26
+
27
+ end
@@ -0,0 +1,26 @@
1
+ module Resource
2
+
3
+ class Package < Base
4
+
5
+ block_attr :action, :package_name, :owner
6
+
7
+ def initialize package_name, &block
8
+ set_base_defaults
9
+ @package_name = package_name
10
+ self.instance_eval(&block)
11
+ end
12
+
13
+ def run
14
+ Execution.block 'Installing Package', @package_name, @owner do |b|
15
+ b.always_run @always_run
16
+ b.run "apt-get install -y #{@package_name}"
17
+ end
18
+ end
19
+
20
+ def value_for_platform *args
21
+ @package_name
22
+ end
23
+
24
+ end
25
+
26
+ end
@@ -0,0 +1,44 @@
1
+ module Resource
2
+
3
+ class RemoteFile < Base
4
+
5
+ block_attr :owner, :group, :mode, :source
6
+
7
+ # Install a static local file into a remote server.
8
+ def initialize target, &block
9
+ set_base_defaults
10
+ @pattern = 'cookbooks/*/files/default/'
11
+ @target = target
12
+ self.instance_eval(&block)
13
+ end
14
+
15
+ def content
16
+ (Dir[@pattern + '*'] + Dir[@pattern + '*.*']).each do |file|
17
+ return ::File.read(file) if file.include? @source
18
+ end
19
+ 'Resource was not found'
20
+ end
21
+
22
+ def run
23
+ Execution.block 'Creating a remote file', @target, @owner do |b|
24
+ if ::File.exist? @target
25
+ sums_equal = MD5.hexdigest(::File.read(@target)) == MD5.hexdigest(content)
26
+ else
27
+ sums_equal = false
28
+ @always_run = true
29
+ end
30
+ if sums_equal
31
+ Output.warn 'Skipping', 'checksum comparison matches'
32
+ else
33
+ Output.warn 'Generating file with ruby', @target
34
+ ::File.open(@target, 'w+') { |file| file.write content }
35
+ b.always_run @always_run
36
+ b.run "chown #{@owner}:#{@group} #{@target}"
37
+ b.run "chmod #{unix_mode} #{@target}"
38
+ end
39
+ end
40
+ end
41
+
42
+ end
43
+
44
+ end
@@ -0,0 +1,23 @@
1
+ module Resource
2
+
3
+ class Service < Base
4
+
5
+ block_attr :action
6
+
7
+ def initialize name, &block
8
+ set_base_defaults
9
+ @name = name
10
+ @always_run = true
11
+ self.instance_eval(&block)
12
+ end
13
+
14
+ def run
15
+ Execution.block "Changing #{@name} service", @action, 'root' do |b|
16
+ b.always_run @always_run
17
+ b.run "/etc/init.d/#{@name} #{@action}"
18
+ end
19
+ end
20
+
21
+ end
22
+
23
+ end
@@ -0,0 +1,25 @@
1
+ module Resource
2
+
3
+ class SwapRelease < Base
4
+
5
+ block_attr :owner, :release_path, :current
6
+
7
+ def initialize release_path, &block
8
+ set_base_defaults
9
+ @release_path = release_path
10
+ self.instance_eval(&block)
11
+ end
12
+
13
+ def run
14
+ Execution.block 'Swaping releases', "#{@current} ~> #{@release_path}", 'root' do |b|
15
+ b.always_run true
16
+ b.run "touch #{@current.chop}"
17
+ b.run "rm #{@current.chop}"
18
+ b.run "ln -s #{@release_path} #{@current.chop}"
19
+ b.run "chown #{@owner}:#{@owner} #{@current.chop}"
20
+ end
21
+ end
22
+
23
+ end
24
+
25
+ end
@@ -0,0 +1,68 @@
1
+ require 'erubis'
2
+
3
+ module Resource
4
+
5
+ class Template < Base
6
+
7
+ class_attr :source
8
+
9
+ block_attr :owner, :group, :mode, :source, :action, :variables, :backup, :target
10
+
11
+ PREFIX = ''
12
+
13
+ def run
14
+ Execution.block 'Compiling template file', @target, @owner do |b|
15
+ compile!
16
+ b.always_run @always_run
17
+ end
18
+ Execution.block 'Ensuring file permissions', @target, 'root' do |b|
19
+ b.run "chown #{@owner}:#{@owner} #{@target}"
20
+ b.run "chmod #{unix_mode} #{@target}"
21
+ b.always_run @always_run
22
+ end
23
+ end
24
+
25
+ protected
26
+
27
+ def initialize target, &block
28
+ set_base_defaults
29
+ @target = target
30
+ ensure_target_zone
31
+ self.instance_eval(&block)
32
+ end
33
+
34
+ def target_path
35
+ "#{PREFIX}#{@target}"
36
+ end
37
+
38
+ def needs_compilation?
39
+ return true unless ::File.exist?(target_path)
40
+ MD5.hexdigest(::File.read target_path) != MD5.hexdigest(compile_template)
41
+ end
42
+
43
+ def compile!
44
+ Output.info 'Erubis is compiling', target_path
45
+ ::File.open(target_path, 'w+') { |file| file.write(compile_template) }
46
+ end
47
+
48
+ def ensure_target_zone
49
+ system "mkdir -p #{PREFIX}#{::File.dirname(@target)}"
50
+ true
51
+ end
52
+
53
+ def find_source
54
+ Dir['cookbooks/*/templates/*/*.*'].each do |file|
55
+ return file if file.include? ::File.basename(@source)
56
+ end
57
+ end
58
+
59
+ def compile_template
60
+ @compile_template ||=
61
+ Erubis::Eruby.new(::File.read(find_source)).evaluate(
62
+ { :node => Node.get }.merge(@variables || {})
63
+ )
64
+ end
65
+
66
+ end
67
+
68
+ end
@@ -0,0 +1,29 @@
1
+ module Resource
2
+
3
+ class User < Base
4
+
5
+ block_attr :password, :uid, :gid, :shell, :action, :username
6
+
7
+ def initialize username, &block
8
+ set_base_defaults
9
+ @username = username
10
+ @shell = '/bin/bash'
11
+ self.instance_eval(&block)
12
+ end
13
+
14
+ def run
15
+ Execution.block 'Creating new user', @username, @owner do |b|
16
+ b.always_run @always_run
17
+ users = `cat /etc/passwd | grep "/home" |cut -d: -f1`
18
+ users = users.scan(/[a-zA-Z\-_]+/)
19
+ if users.include? @username
20
+ Output.warn 'Aborted, I think this user already exists', users.inspect
21
+ else
22
+ b.run "useradd -b /home -u #{@uid} -s #{@shell} -g #{@username} -m -k /home/#{@username} #{@username}"
23
+ end
24
+ end
25
+ end
26
+
27
+ end
28
+
29
+ end
@@ -0,0 +1,42 @@
1
+ module Resource
2
+
3
+ class Version < Base
4
+
5
+ block_attr :location, :current_version, :deployed_by, :deployed_time
6
+ block_attr :server_info, :environment, :branch, :ip, :copy_to, :owner
7
+ block_attr :reset
8
+
9
+ def initialize location, &block
10
+ set_base_defaults
11
+ @location = location
12
+ @current_version = 'n/a'
13
+ @deployed_by = 'n/a'
14
+ @server_info = 'n/a'
15
+ @environment = 'n/a'
16
+ @branch = 'n/a'
17
+ @ip = 'n/a'
18
+ @copy_to = '/tmp/'
19
+ @version = ::Deploy::Version.new
20
+ self.instance_eval(&block)
21
+ end
22
+
23
+ def run
24
+ Execution.block 'Deployment version file', @location, 'root' do |b|
25
+ b.always_run true
26
+ @version.deployed_by = @deployed_by
27
+ @version.current_version = @current_version
28
+ @version.add :server_info, @server_info
29
+ @version.add :environment, @environment
30
+ @version.add :branch, @branch
31
+ @version.add :reset, @reset
32
+ @version.add :ip, @ip
33
+ @version.store_to_file @location
34
+ Output.info 'Copying version file to', @copy_to
35
+ b.run "cp #{@location} #{@copy_to}"
36
+ b.run "chown #{@owner}:#{@owner} #{@copy_to}/#{::File.basename(@location)}"
37
+ end
38
+ end
39
+
40
+ end
41
+
42
+ end
data/lib/resource.rb ADDED
@@ -0,0 +1,20 @@
1
+
2
+ module Resource; end
3
+
4
+ require 'lib/resource/base'
5
+ require 'lib/resource/cron'
6
+ require 'lib/resource/directory'
7
+ require 'lib/resource/execute'
8
+ require 'lib/resource/file'
9
+ require 'lib/resource/gem'
10
+ require 'lib/resource/git'
11
+ require 'lib/resource/group'
12
+ require 'lib/resource/hook'
13
+ require 'lib/resource/link'
14
+ require 'lib/resource/package'
15
+ require 'lib/resource/remote_file'
16
+ require 'lib/resource/service'
17
+ require 'lib/resource/swap_release'
18
+ require 'lib/resource/template'
19
+ require 'lib/resource/user'
20
+ require 'lib/resource/version'
@@ -0,0 +1,8 @@
1
+ class RuntimeError
2
+
3
+ def initialize *args
4
+ Status.save
5
+ super
6
+ end
7
+
8
+ end
data/lib/status.rb ADDED
@@ -0,0 +1,37 @@
1
+ require 'rubygems'
2
+ require 'hashie'
3
+ require 'lib/output'
4
+
5
+ class Status; end
6
+
7
+ class << Status
8
+
9
+ # TODO: extract this from class
10
+ FILE = '/tmp/deployment/env/status'
11
+
12
+ def load_or_create
13
+ Output.info 'Loading status file', FILE
14
+ `mkdir -p #{File.dirname(FILE)}`
15
+ (File.exist? FILE) ? eval(File.read(FILE)) : {}
16
+ end
17
+
18
+ def get
19
+ @status ||= Hashie::Mash.new(load_or_create)
20
+ end
21
+
22
+ def reload!
23
+ Output.jump
24
+ Output.info 'Reloading status', FILE
25
+ save
26
+ @status = nil
27
+ end
28
+
29
+ def save
30
+ Output.info 'Saving deployment status to file', FILE
31
+ File.open(FILE, 'w+') do |file|
32
+ file.write Status.get.to_hash.inspect
33
+ end
34
+ end
35
+
36
+ end
37
+
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: atesta
3
3
  version: !ruby/object:Gem::Version
4
- hash: 31
4
+ hash: 29
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 0
10
- version: 0.0.0
9
+ - 1
10
+ version: 0.0.1
11
11
  platform: ruby
12
12
  authors:
13
13
  - kazuyoshi tlacaelel
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-08-26 00:00:00 -05:00
18
+ date: 2011-11-29 00:00:00 -06:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -49,6 +49,35 @@ files:
49
49
  - Rakefile
50
50
  - VERSION
51
51
  - lib/atesta.rb
52
+ - lib/block_attr.rb
53
+ - lib/class_attr.rb
54
+ - lib/execution.rb
55
+ - lib/hash.rb
56
+ - lib/lock.rb
57
+ - lib/node.rb
58
+ - lib/output.rb
59
+ - lib/recipe.rb
60
+ - lib/release_window.rb
61
+ - lib/resource.rb
62
+ - lib/resource/base.rb
63
+ - lib/resource/cron.rb
64
+ - lib/resource/directory.rb
65
+ - lib/resource/execute.rb
66
+ - lib/resource/file.rb
67
+ - lib/resource/gem.rb
68
+ - lib/resource/git.rb
69
+ - lib/resource/group.rb
70
+ - lib/resource/hook.rb
71
+ - lib/resource/link.rb
72
+ - lib/resource/package.rb
73
+ - lib/resource/remote_file.rb
74
+ - lib/resource/service.rb
75
+ - lib/resource/swap_release.rb
76
+ - lib/resource/template.rb
77
+ - lib/resource/user.rb
78
+ - lib/resource/version.rb
79
+ - lib/runtime_error.rb
80
+ - lib/status.rb
52
81
  - test/helper.rb
53
82
  - test/test_atesta.rb
54
83
  has_rdoc: true