rubble 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/LICENSE.txt +674 -0
- data/README.md +29 -0
- data/bin/rubble +5 -0
- data/demo/applique/qed_test.rb +82 -0
- data/demo/applique/rubble_test.rb +33 -0
- data/demo/environment.md +32 -0
- data/demo/file_set.md +65 -0
- data/demo/logging.md +11 -0
- data/demo/server.md +53 -0
- data/demo/snapshot.md +32 -0
- data/demo/tool.md +36 -0
- data/lib/rubble.rb +15 -0
- data/lib/rubble/application.rb +82 -0
- data/lib/rubble/command/activate.rb +13 -0
- data/lib/rubble/command/base.rb +57 -0
- data/lib/rubble/command/upload.rb +28 -0
- data/lib/rubble/context.rb +33 -0
- data/lib/rubble/dsl.rb +31 -0
- data/lib/rubble/environment.rb +38 -0
- data/lib/rubble/executor/base.rb +134 -0
- data/lib/rubble/executor/local.rb +113 -0
- data/lib/rubble/executor/remote.rb +81 -0
- data/lib/rubble/file_set.rb +41 -0
- data/lib/rubble/logging.rb +23 -0
- data/lib/rubble/plan/base.rb +21 -0
- data/lib/rubble/plan/deploy.rb +11 -0
- data/lib/rubble/resource/base.rb +43 -0
- data/lib/rubble/resource/database.rb +8 -0
- data/lib/rubble/resource/fileset.rb +93 -0
- data/lib/rubble/resource/webapp.rb +22 -0
- data/lib/rubble/scope.rb +44 -0
- data/lib/rubble/server.rb +61 -0
- data/lib/rubble/snapshot.rb +21 -0
- data/lib/rubble/target/base.rb +24 -0
- data/lib/rubble/target/directory.rb +11 -0
- data/lib/rubble/target/mysql.rb +8 -0
- data/lib/rubble/target/tomcat.rb +20 -0
- data/lib/rubble/tool.rb +138 -0
- data/lib/rubble/version.rb +3 -0
- metadata +244 -0
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# Rubble
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'rubble'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install rubble
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it ( https://github.com/[my-github-username]/deploy_tool/fork )
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create a new Pull Request
|
data/bin/rubble
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'ae'
|
2
|
+
|
3
|
+
# Add bin directory to path
|
4
|
+
ENV['PATH'] = File.join(QED::Utils.root, 'bin') + ":" + ENV['PATH']
|
5
|
+
|
6
|
+
# Redirect stdout and return output
|
7
|
+
#
|
8
|
+
# @yield Execute block with redirected stdout
|
9
|
+
# @return [String] Output
|
10
|
+
def redirect
|
11
|
+
begin
|
12
|
+
old_stdout = $stdout
|
13
|
+
old_stderr = $stderr
|
14
|
+
$stdout = StringIO.new('', 'w')
|
15
|
+
$stderr = $stdout
|
16
|
+
yield
|
17
|
+
$stdout.string
|
18
|
+
ensure
|
19
|
+
$stdout = old_stdout
|
20
|
+
$stderr = old_stderr
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Execute a registered command
|
25
|
+
#
|
26
|
+
# @param command [String] Command string
|
27
|
+
# @return [String] Command output
|
28
|
+
def execute_command(command)
|
29
|
+
if command.nil? then
|
30
|
+
raise(ArgumentError, 'Please supply a block with the command to run')
|
31
|
+
end
|
32
|
+
|
33
|
+
command.gsub!(/^#\s*/, '')
|
34
|
+
arguments = Shellwords.shellsplit(command)
|
35
|
+
method_name = "run_#{arguments[0]}_command".to_sym
|
36
|
+
method = method(method_name)
|
37
|
+
|
38
|
+
if method.nil? then
|
39
|
+
raise(ArgumentError, "Please define a method named #{method_name} to test running the #{command[0]} command.")
|
40
|
+
end
|
41
|
+
|
42
|
+
@output = redirect do
|
43
|
+
method.call(arguments[1..-1])
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Remove empty lines and leading and trailing spaces from text
|
48
|
+
#
|
49
|
+
# @param text [String] Text to clean
|
50
|
+
# @return [String] cleaned text
|
51
|
+
def clean(text)
|
52
|
+
raise(ArgumentError, 'Text must not be nil.') if text.nil?
|
53
|
+
|
54
|
+
text.gsub(/\n+/, "\n").gsub(/^\s+|\s+$/, '')
|
55
|
+
end
|
56
|
+
|
57
|
+
# Save the following block as script
|
58
|
+
When /\buse\b[^.]+\bscript in (?:file )?`([a-z]+\.[a-z]+)`/i do |file_name, text|
|
59
|
+
raise(ArgumentError, 'Please supply a block with a script.') if text.nil?
|
60
|
+
|
61
|
+
File.open(file_name, 'w') do |f|
|
62
|
+
f.write(text)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Expect the following block as text output
|
67
|
+
When /\bproduces\b[^.]+\btext output\b/i do |text|
|
68
|
+
raise(ArgumentError, 'Please supply a block with the text') if text.nil?
|
69
|
+
clean(@output).assert == clean(text)
|
70
|
+
end
|
71
|
+
|
72
|
+
# Execute the command in the following block
|
73
|
+
When /\bexecute\b[^.]+\bcommand\b/i do |command|
|
74
|
+
execute_command(command)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Execute the command in the following block and expect it to fail
|
78
|
+
When /\bexecuting\b[^.]+\bfails\b/i do |command|
|
79
|
+
SystemExit.expect do
|
80
|
+
execute_command(command)
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'rubble/application'
|
2
|
+
|
3
|
+
include Rubble
|
4
|
+
|
5
|
+
def run_rubble_command(arguments)
|
6
|
+
Commander::Runner.instance_variable_set :"@singleton", Commander::Runner.new(arguments)
|
7
|
+
app = Rubble::Application.new
|
8
|
+
app.run
|
9
|
+
end
|
10
|
+
|
11
|
+
# Create a Tool object for testing
|
12
|
+
When /create a new test tool/i do |configuration|
|
13
|
+
raise(ArgumentError, 'Please supply a block with configuration.') if configuration.nil?
|
14
|
+
|
15
|
+
@tool = Tool.new
|
16
|
+
@tool.instance_eval(configuration)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Create an Environment object for testing
|
20
|
+
When /create a new test environment/i do |configuration|
|
21
|
+
raise(ArgumentError, 'Please supply a block with configuration.') if configuration.nil?
|
22
|
+
|
23
|
+
@environment = Environment.new(Tool.new, 'test')
|
24
|
+
@environment.instance_eval(configuration)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Create a Server object for testing
|
28
|
+
When /create a new test server/i do |configuration|
|
29
|
+
raise(ArgumentError, 'Please supply a block with configuration.') if configuration.nil?
|
30
|
+
|
31
|
+
@server = Server.new(Tool.new, 'test')
|
32
|
+
@server.instance_eval(configuration)
|
33
|
+
end
|
data/demo/environment.md
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# Environment
|
2
|
+
|
3
|
+
## Configure plans
|
4
|
+
|
5
|
+
An environment allows the configuration of plans.
|
6
|
+
|
7
|
+
Create a new test environment and configure it as follows:
|
8
|
+
|
9
|
+
server 'test' do
|
10
|
+
deploy fileset('test') => directory('test')
|
11
|
+
end
|
12
|
+
|
13
|
+
Now check the configuration.
|
14
|
+
|
15
|
+
@environment.plans.size.assert == 1
|
16
|
+
@environment.plans[0].assert.is_a? Plan::Deploy
|
17
|
+
|
18
|
+
## Configure plans for multiple servers at once
|
19
|
+
|
20
|
+
An environment allows the configuration of multiple servers.
|
21
|
+
|
22
|
+
Create a new test environment and configure it as follows:
|
23
|
+
|
24
|
+
server 'test1', 'test2' do
|
25
|
+
deploy fileset('test') => directory('test')
|
26
|
+
end
|
27
|
+
|
28
|
+
Now check the configuration.
|
29
|
+
|
30
|
+
@environment.plans.size.assert == 2
|
31
|
+
@environment.plans[0].assert.is_a? Plan::Deploy
|
32
|
+
@environment.plans[1].assert.is_a? Plan::Deploy
|
data/demo/file_set.md
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
# File Set
|
2
|
+
|
3
|
+
## Converts paths to Pathname
|
4
|
+
|
5
|
+
A file set converts the base directory to a pathname.
|
6
|
+
|
7
|
+
fileset = FileSet.new('/tmp')
|
8
|
+
fileset.dir.assert.is_a? Pathname
|
9
|
+
|
10
|
+
A file set converts the files to pathnames.
|
11
|
+
|
12
|
+
fileset = FileSet.new('/tmp', 'test.txt')
|
13
|
+
fileset.files[0].assert.is_a? Pathname
|
14
|
+
|
15
|
+
## Reports when it is empty
|
16
|
+
|
17
|
+
A file set reports when it is empty
|
18
|
+
|
19
|
+
fileset = FileSet.new('/tmp')
|
20
|
+
fileset.assert.empty?
|
21
|
+
|
22
|
+
A file set reports when it is not empty
|
23
|
+
|
24
|
+
fileset = FileSet.new('/tmp', 'test.txt')
|
25
|
+
fileset.assert.not.empty?
|
26
|
+
|
27
|
+
## Readonly
|
28
|
+
|
29
|
+
A file set freezes its files
|
30
|
+
|
31
|
+
fileset = FileSet.new('/tmp')
|
32
|
+
RuntimeError.assert.raised? do
|
33
|
+
fileset.files << 'test.txt'
|
34
|
+
end
|
35
|
+
|
36
|
+
## Sorts content
|
37
|
+
|
38
|
+
A file set sorts the contained files
|
39
|
+
|
40
|
+
fileset = FileSet.new('/tmp', 'b.txt', 'a.txt')
|
41
|
+
fileset.files.assert == [Pathname('a.txt'), Pathname('b.txt')]
|
42
|
+
|
43
|
+
## Ensures files are relative
|
44
|
+
|
45
|
+
A file set ensures all files are in the base directory
|
46
|
+
|
47
|
+
FileSet.new('/tmp', 'test.txt').assert != nil
|
48
|
+
FileSet.new('/tmp', '../tmp/test.txt').assert != nil
|
49
|
+
|
50
|
+
ArgumentError.assert.raised? do
|
51
|
+
FileSet.new('/tmp', '.')
|
52
|
+
end
|
53
|
+
|
54
|
+
ArgumentError.assert.raised? do
|
55
|
+
FileSet.new('/tmp', '../usr/test.txt')
|
56
|
+
end
|
57
|
+
|
58
|
+
ArgumentError.assert.raised? do
|
59
|
+
FileSet.new('/tmp', '/test.txt')
|
60
|
+
end
|
61
|
+
|
62
|
+
A file set can return the full paths of the files
|
63
|
+
|
64
|
+
fileset = FileSet.new('/tmp', 'test.txt')
|
65
|
+
fileset.paths.first.assert == Pathname('/tmp/test.txt')
|
data/demo/logging.md
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
# Logging
|
2
|
+
|
3
|
+
## Set the MDC
|
4
|
+
|
5
|
+
The context method allows code to be executed with specific values in the MDC.
|
6
|
+
|
7
|
+
Logging.mdc['test'].assert == nil
|
8
|
+
Logging.context(:test => 'test') do
|
9
|
+
Logging.mdc['test'].assert == 'test'
|
10
|
+
end
|
11
|
+
Logging.mdc['test'].assert == nil
|
data/demo/server.md
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
# Server
|
2
|
+
|
3
|
+
## Configure the Rubble directory
|
4
|
+
|
5
|
+
Create a new test server and configure it as follows:
|
6
|
+
|
7
|
+
rubble_dir '/var/test'
|
8
|
+
|
9
|
+
Now check the configuration.
|
10
|
+
|
11
|
+
@server.rubble_dir.assert == '/var/test'
|
12
|
+
|
13
|
+
## Configure the deploy directory
|
14
|
+
|
15
|
+
Create a new test server and configure it as follows:
|
16
|
+
|
17
|
+
deploy_dir '/var/test'
|
18
|
+
|
19
|
+
Now check the configuration.
|
20
|
+
|
21
|
+
@server.deploy_dir.assert == '/var/test'
|
22
|
+
|
23
|
+
## Configure the user
|
24
|
+
|
25
|
+
Create a new test server and configure it as follows:
|
26
|
+
|
27
|
+
user 'test'
|
28
|
+
|
29
|
+
Now check the configuration.
|
30
|
+
|
31
|
+
@server.user.assert == 'test'
|
32
|
+
|
33
|
+
## Configure the group
|
34
|
+
|
35
|
+
Create a new test server and configure it as follows:
|
36
|
+
|
37
|
+
group 'test'
|
38
|
+
|
39
|
+
Now check the configuration.
|
40
|
+
|
41
|
+
@server.group.assert == 'test'
|
42
|
+
|
43
|
+
## Configure the targets
|
44
|
+
|
45
|
+
Create a new test server and configure it as follows:
|
46
|
+
|
47
|
+
directory 'test'
|
48
|
+
|
49
|
+
Now check the configuration.
|
50
|
+
|
51
|
+
@server.targets.size.assert == 1
|
52
|
+
@server.targets[:directory].size.assert == 1
|
53
|
+
@server.targets[:directory]['test'].assert.is_a? Target::Directory
|
data/demo/snapshot.md
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
## Reports when it is empty
|
2
|
+
|
3
|
+
A snapshot reports when it is empty because it does not contain any file sets.
|
4
|
+
|
5
|
+
snapshot = Snapshot.new('1')
|
6
|
+
snapshot.assert.empty?
|
7
|
+
|
8
|
+
A snapshot reports when it is empty because all its file sets are empty.
|
9
|
+
|
10
|
+
snapshot = Snapshot.new('1', FileSet.new('/tmp'))
|
11
|
+
snapshot.assert.empty?
|
12
|
+
|
13
|
+
A snapshot reports when it is not empty.
|
14
|
+
|
15
|
+
snapshot = Snapshot.new('1', FileSet.new('/tmp', 'a.txt'))
|
16
|
+
snapshot.assert.not.empty?
|
17
|
+
|
18
|
+
## Read only
|
19
|
+
|
20
|
+
A snapshot prevents modification of its version.
|
21
|
+
|
22
|
+
snapshot = Snapshot.new('1')
|
23
|
+
RuntimeError.assert.raised? do
|
24
|
+
snapshot.version << 'test'
|
25
|
+
end
|
26
|
+
|
27
|
+
A snapshot prevents modification of its filesets.
|
28
|
+
|
29
|
+
snapshot = Snapshot.new('1')
|
30
|
+
RuntimeError.assert.raised? do
|
31
|
+
snapshot.filesets << FileSet.new('/tmp')
|
32
|
+
end
|
data/demo/tool.md
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# Tool
|
2
|
+
|
3
|
+
## Configures the resources
|
4
|
+
|
5
|
+
A tool allows the configuration of resources.
|
6
|
+
|
7
|
+
Create a new test tool and configure it as follows:
|
8
|
+
|
9
|
+
fileset 'test'
|
10
|
+
|
11
|
+
Now check the configuration.
|
12
|
+
|
13
|
+
@tool.resources.size.assert == 1
|
14
|
+
@tool.resources[:fileset]['test'].assert.is_a? Resource::Fileset
|
15
|
+
|
16
|
+
## Configures the servers
|
17
|
+
|
18
|
+
Create a new test tool and configure it as follows:
|
19
|
+
|
20
|
+
server 'test'
|
21
|
+
|
22
|
+
Now check the configuration.
|
23
|
+
|
24
|
+
@tool.servers.size.assert == 1
|
25
|
+
@tool.servers['test'].assert.is_a? Server
|
26
|
+
|
27
|
+
## Configures the environments
|
28
|
+
|
29
|
+
Create a new test tool and configure it as follows:
|
30
|
+
|
31
|
+
environment 'test'
|
32
|
+
|
33
|
+
Now check the configuration.
|
34
|
+
|
35
|
+
@tool.environments.size.assert == 1
|
36
|
+
@tool.environments['test'].assert.is_a? Environment
|
data/lib/rubble.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'rubble/version'
|
2
|
+
require 'rubble/tool'
|
3
|
+
require 'rubble/file_set'
|
4
|
+
require 'rubble/resource/database'
|
5
|
+
require 'rubble/resource/webapp'
|
6
|
+
require 'rubble/resource/fileset'
|
7
|
+
require 'rubble/target/tomcat'
|
8
|
+
require 'rubble/target/mysql'
|
9
|
+
require 'rubble/target/directory'
|
10
|
+
require 'rubble/plan/deploy'
|
11
|
+
require 'rubble/command/upload'
|
12
|
+
require 'rubble/command/activate'
|
13
|
+
|
14
|
+
module Rubble
|
15
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'commander'
|
3
|
+
require 'rubble'
|
4
|
+
require 'yaml'
|
5
|
+
require 'logging'
|
6
|
+
|
7
|
+
module Rubble
|
8
|
+
# Rubble application class
|
9
|
+
class Application
|
10
|
+
include Commander::Methods
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@config_file = 'config/deploy.rubble'
|
14
|
+
end
|
15
|
+
|
16
|
+
def tool
|
17
|
+
if @tool.nil? then
|
18
|
+
@tool = Rubble::Tool.new
|
19
|
+
@tool.read_config(@config_file)
|
20
|
+
end
|
21
|
+
|
22
|
+
@tool
|
23
|
+
end
|
24
|
+
|
25
|
+
def run
|
26
|
+
program :name, 'Rubble'
|
27
|
+
program :version, Rubble::VERSION
|
28
|
+
program :description, 'Rubble Deployment Tool'
|
29
|
+
|
30
|
+
Logging.color_scheme('bright',
|
31
|
+
:lines => {
|
32
|
+
:debug => :blue,
|
33
|
+
:warn => :yellow,
|
34
|
+
:error => :orange,
|
35
|
+
:fatal => :red
|
36
|
+
},
|
37
|
+
)
|
38
|
+
|
39
|
+
Logging.logger.root.level = :info
|
40
|
+
Logging.logger.root.appenders = Logging.appenders.stdout(
|
41
|
+
'stdout',
|
42
|
+
:layout => Logging.layouts.pattern(
|
43
|
+
:pattern => '==> %m %X{server}\n',
|
44
|
+
:color_scheme => 'bright'
|
45
|
+
)
|
46
|
+
)
|
47
|
+
|
48
|
+
log = Logging.logger[self]
|
49
|
+
|
50
|
+
global_option('-l', '--loglevel STRING', 'Log level') do |level|
|
51
|
+
Logging.logger.root.level = level.to_sym
|
52
|
+
end
|
53
|
+
|
54
|
+
global_option('-f', '--file FILE', 'Configuration file') do |file|
|
55
|
+
@config_file = file
|
56
|
+
@tool = nil
|
57
|
+
end
|
58
|
+
|
59
|
+
command :upload do |c|
|
60
|
+
c.description = 'Upload a release'
|
61
|
+
c.action do |args, options|
|
62
|
+
tool.execute(args) do |plan, context|
|
63
|
+
command = Rubble::Command::Upload.new(plan, context)
|
64
|
+
command.execute
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
command :activate do |c|
|
70
|
+
c.description = 'Activate a release'
|
71
|
+
c.action do |args, options|
|
72
|
+
tool.execute(args) do |plan, context|
|
73
|
+
command = Rubble::Command::Activate.new(plan, context)
|
74
|
+
command.execute
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
run!
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|