jisota 0.0.1 → 0.0.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 +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +5 -0
- data/CHANGELOG.md +21 -0
- data/README.md +106 -2
- data/Rakefile +10 -0
- data/jisota.gemspec +1 -0
- data/lib/jisota.rb +4 -2
- data/lib/jisota/collection.rb +2 -4
- data/lib/jisota/command_script.rb +3 -2
- data/lib/jisota/composite_script.rb +2 -2
- data/lib/jisota/configuration.rb +3 -3
- data/lib/jisota/dsl_base.rb +12 -0
- data/lib/jisota/file_script.rb +107 -9
- data/lib/jisota/nil_output.rb +14 -0
- data/lib/jisota/{logger.rb → output.rb} +16 -35
- data/lib/jisota/package.rb +2 -2
- data/lib/jisota/package_script.rb +28 -20
- data/lib/jisota/packages/gem_install.rb +17 -0
- data/lib/jisota/packages/nginx_passenger.rb +34 -0
- data/lib/jisota/packages/ruby.rb +2 -2
- data/lib/jisota/param_parser.rb +30 -19
- data/lib/jisota/provisioner.rb +7 -3
- data/lib/jisota/script_block.rb +13 -14
- data/lib/jisota/script_context.rb +24 -0
- data/lib/jisota/server.rb +2 -1
- data/lib/jisota/ssh_engine.rb +6 -2
- data/lib/jisota/ssh_session.rb +10 -15
- data/lib/jisota/version.rb +1 -1
- data/package_files/nginx_passenger/nginx_service +65 -0
- data/spec/acceptance/ruby_passenger_nginx_spec.rb +24 -0
- data/spec/acceptance/simple_script_spec.rb +8 -24
- data/spec/acceptance/upload_blocks_spec.rb +34 -0
- data/spec/lib/jisota/collection_spec.rb +10 -0
- data/spec/lib/jisota/command_script_spec.rb +4 -3
- data/spec/lib/jisota/composite_script_spec.rb +8 -6
- data/spec/lib/jisota/configuration_spec.rb +1 -3
- data/spec/lib/jisota/dsl_base_spec.rb +37 -0
- data/spec/lib/jisota/file_script_spec.rb +63 -8
- data/spec/lib/jisota/output_spec.rb +84 -0
- data/spec/lib/jisota/package_script_spec.rb +20 -8
- data/spec/lib/jisota/package_spec.rb +2 -6
- data/spec/lib/jisota/packages/apt_spec.rb +7 -4
- data/spec/lib/jisota/packages/gem_install_spec.rb +18 -0
- data/spec/lib/jisota/packages/nginx_passenger_spec.rb +17 -0
- data/spec/lib/jisota/packages/ruby_spec.rb +6 -3
- data/spec/lib/jisota/param_parser_spec.rb +105 -0
- data/spec/lib/jisota/provisioner_spec.rb +30 -0
- data/spec/lib/jisota/role_spec.rb +1 -3
- data/spec/lib/jisota/script_block_spec.rb +7 -4
- data/spec/lib/jisota/ssh_engine_spec.rb +26 -0
- data/spec/lib/jisota/ssh_session_spec.rb +53 -0
- data/spec/spec_helper.rb +11 -1
- data/spec/support/acceptance_helpers.rb +45 -0
- data/spec/test_files/nginx_default.conf +121 -0
- data/spec/vagrant/Vagrantfile +118 -0
- data/spec/vagrant/ssh_key +27 -0
- metadata +55 -7
- data/lib/jisota/upload_file.rb +0 -3
- data/spec/lib/jisota/logger_spec.rb +0 -34
data/lib/jisota/server.rb
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
module Jisota
|
2
2
|
class Server
|
3
|
-
attr_accessor :host, :user, :roles
|
3
|
+
attr_accessor :host, :user, :roles, :key
|
4
4
|
|
5
5
|
def initialize(host = nil, options = {})
|
6
6
|
@roles = []
|
7
7
|
@host = host
|
8
8
|
@user = options[:user]
|
9
9
|
@roles = Array(options[:roles]) if options[:roles]
|
10
|
+
@key = options[:key]
|
10
11
|
end
|
11
12
|
end
|
12
13
|
end
|
data/lib/jisota/ssh_engine.rb
CHANGED
@@ -2,8 +2,12 @@ require 'net/ssh'
|
|
2
2
|
|
3
3
|
module Jisota
|
4
4
|
class SSHEngine
|
5
|
-
def
|
6
|
-
|
5
|
+
def initialize(options = {})
|
6
|
+
@engine = options.fetch(:engine) { Net::SSH }
|
7
|
+
end
|
8
|
+
|
9
|
+
def start(user: , host: , **options)
|
10
|
+
@engine.start(host, user, options) do |ssh_session|
|
7
11
|
yield SSHSession.new(ssh_session)
|
8
12
|
end
|
9
13
|
end
|
data/lib/jisota/ssh_session.rb
CHANGED
@@ -10,39 +10,34 @@ module Jisota
|
|
10
10
|
@scp_engine = options.fetch(:scp_engine) { Net::SCP }
|
11
11
|
end
|
12
12
|
|
13
|
-
def command(command, logger =
|
13
|
+
def command(command, logger = NilOutput.new)
|
14
14
|
exit_code = nil
|
15
15
|
exit_signal = nil
|
16
16
|
error_message = ""
|
17
17
|
|
18
|
-
logger.
|
18
|
+
logger.system_message("Executing #{command}")
|
19
19
|
|
20
20
|
@session.open_channel do |channel|
|
21
21
|
channel.exec(command) do
|
22
|
-
channel.on_data { |_, data| logger.info(data)
|
23
|
-
channel.on_extended_data { |_, _, data| logger.warn(data)
|
22
|
+
channel.on_data { |_, data| logger.info(data) }
|
23
|
+
channel.on_extended_data { |_, _, data| logger.warn(data); error_message << data }
|
24
24
|
channel.on_request("exit-status") { |_, data| exit_code = data.read_long }
|
25
25
|
channel.on_request("exit-signal") { |_, data| exit_signal = data.read_long }
|
26
26
|
end
|
27
27
|
end
|
28
28
|
@session.loop
|
29
29
|
|
30
|
-
if exit_code
|
31
|
-
true
|
32
|
-
else
|
30
|
+
if exit_code > 0
|
33
31
|
logger.error("Error running #{command}:")
|
34
32
|
logger.error(error_message)
|
35
|
-
false
|
36
33
|
end
|
34
|
+
|
35
|
+
exit_code
|
37
36
|
end
|
38
37
|
|
39
|
-
def upload(
|
40
|
-
raise FileNotFoundError, "Upload file not found: #{
|
41
|
-
|
42
|
-
command("mkdir -p tmp/jisota", logger)
|
43
|
-
logger.upload(from: file.from, to: tmp_file) if logger
|
44
|
-
@scp_engine.new(@session).upload!(file.from, tmp_file, recursive: false)
|
45
|
-
command("sudo mkdir -p `dirname #{file.to}` && sudo mv #{tmp_file} #{file.to}", logger)
|
38
|
+
def upload(from: , to: )
|
39
|
+
raise FileNotFoundError, "Upload file not found: #{from}" unless File.exist?(from)
|
40
|
+
@scp_engine.new(@session).upload!(from, to, recursive: false)
|
46
41
|
end
|
47
42
|
end
|
48
43
|
end
|
data/lib/jisota/version.rb
CHANGED
@@ -0,0 +1,65 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
#
|
3
|
+
# How-to:
|
4
|
+
#
|
5
|
+
# Symlink to init.d:
|
6
|
+
# ln -s /path/to/nginx_service /etc/init.d/nginx
|
7
|
+
#
|
8
|
+
# Register service:
|
9
|
+
# sudo update-rc.d nginx defaults 99
|
10
|
+
# (99 is the boot order of all startup services making sure this boots last)
|
11
|
+
#
|
12
|
+
|
13
|
+
### BEGIN INIT INFO
|
14
|
+
# Provides: nginx
|
15
|
+
# Required-Start: $local_fs, $syslog
|
16
|
+
# Required-Stop: $local_fs, $syslog
|
17
|
+
# Default-Start: 2 3 4 5
|
18
|
+
# Default-Stop: 0 1 6
|
19
|
+
# Short-Description: Start nginx at boot time
|
20
|
+
# Description: Enable service provided by nginx
|
21
|
+
### END INIT INFO
|
22
|
+
|
23
|
+
RETVAL=0;
|
24
|
+
bin=/opt/nginx/sbin/nginx
|
25
|
+
|
26
|
+
start() {
|
27
|
+
echo "Starting nginx"
|
28
|
+
$bin
|
29
|
+
}
|
30
|
+
|
31
|
+
stop() {
|
32
|
+
echo "Stopping nginx"
|
33
|
+
$bin -s stop
|
34
|
+
}
|
35
|
+
|
36
|
+
restart() {
|
37
|
+
echo "Restarting nginx"
|
38
|
+
$bin -s reload
|
39
|
+
}
|
40
|
+
|
41
|
+
reload() {
|
42
|
+
echo "Restarting nginx"
|
43
|
+
$bin -s reload
|
44
|
+
}
|
45
|
+
|
46
|
+
case "$1" in
|
47
|
+
start)
|
48
|
+
start
|
49
|
+
;;
|
50
|
+
stop)
|
51
|
+
stop
|
52
|
+
;;
|
53
|
+
restart)
|
54
|
+
restart
|
55
|
+
;;
|
56
|
+
reload)
|
57
|
+
reload
|
58
|
+
;;
|
59
|
+
*)
|
60
|
+
|
61
|
+
echo $"Usage: $0 {start|stop|restart|reload}"
|
62
|
+
exit 1
|
63
|
+
esac
|
64
|
+
|
65
|
+
exit $RETVAL
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'open-uri'
|
3
|
+
|
4
|
+
module Jisota
|
5
|
+
describe "ruby + passenger", type: :acceptance do
|
6
|
+
example "install ruby and nginx_passenger" do
|
7
|
+
config = Jisota.config do
|
8
|
+
role :app do
|
9
|
+
ruby version: "2.1.1"
|
10
|
+
nginx_passenger config_file: "spec/test_files/nginx_default.conf"
|
11
|
+
end
|
12
|
+
|
13
|
+
server host, user: user, roles: :app, key: key
|
14
|
+
end
|
15
|
+
|
16
|
+
Jisota.run(config, logger: Output.new(verbose: true))
|
17
|
+
|
18
|
+
webpage = URI("http://#{host}").read
|
19
|
+
expect(webpage).to match(/Welcome to nginx!/)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
|
@@ -1,24 +1,8 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
module Jisota
|
4
|
-
describe "Execute a simple script" do
|
5
|
-
let(:ssh_engine) do
|
6
|
-
class_double(SSHEngine).tap do |ssh_engine|
|
7
|
-
allow(ssh_engine).to receive(:start).and_yield(ssh_session)
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
let(:ssh_session) { instance_double(SSHSession, command: true, upload: true) }
|
12
|
-
|
4
|
+
describe "Execute a simple script", type: :acceptance do
|
13
5
|
example "Simple script with a package" do
|
14
|
-
run_for_real = ENV["ACCEPTANCE_TEST"] == "run_for_real"
|
15
|
-
|
16
|
-
host = run_for_real ? ENV["ACCEPTANCE_HOST"] : "test.jisota"
|
17
|
-
raise "Must set host with ACCEPTANCE_HOST=<host>" unless host
|
18
|
-
|
19
|
-
user = run_for_real ? ENV["ACCEPTANCE_USER"] : "john_doe"
|
20
|
-
raise "Must set user with ACCEPTANCE_USER=<user>" unless user
|
21
|
-
|
22
6
|
Jisota.global_config do
|
23
7
|
package :touch_global do
|
24
8
|
param :target
|
@@ -39,17 +23,17 @@ module Jisota
|
|
39
23
|
upload from: "spec/test_files/foo", to: "uploads/foo"
|
40
24
|
end
|
41
25
|
|
42
|
-
server host, user: user, roles: :app
|
26
|
+
server host, user: user, key: key, roles: :app
|
43
27
|
end
|
44
28
|
|
45
|
-
config.ssh_engine = ssh_engine unless run_for_real
|
46
29
|
Jisota.run(config)
|
47
30
|
|
48
|
-
|
49
|
-
expect(
|
50
|
-
expect(ssh_session
|
51
|
-
expect(ssh_session
|
52
|
-
expect(ssh_session
|
31
|
+
create_ssh_session do |ssh_session|
|
32
|
+
expect(ssh_session.command("[ -f foo ]")).to eq(0)
|
33
|
+
expect(ssh_session.command("[ -f bar ]")).to eq(0)
|
34
|
+
expect(ssh_session.command("[ -f baz ]")).to eq(0)
|
35
|
+
expect(ssh_session.command("[ -f uploads/foo ]")).to eq(0)
|
36
|
+
expect(ssh_session.command('[ `cat uploads/foo` = "foo" ]')).to eq(0)
|
53
37
|
end
|
54
38
|
end
|
55
39
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Jisota
|
4
|
+
describe 'calling upload blocks', type: :acceptance do
|
5
|
+
example "update and create blocks inside package with params" do
|
6
|
+
config = Jisota.config do
|
7
|
+
package :upload_file do
|
8
|
+
param :file
|
9
|
+
run do
|
10
|
+
upload from: file, to: "foo" do
|
11
|
+
create { cmd %Q{echo "File #{file} created"} }
|
12
|
+
update { cmd %Q{echo "File #{file} updated"} }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
role :app do
|
18
|
+
upload_file "spec/test_files/foo"
|
19
|
+
end
|
20
|
+
|
21
|
+
server host, user: user, roles: :app, key: key
|
22
|
+
end
|
23
|
+
|
24
|
+
create_ssh_session { |ssh_session| ssh_session.command("rm foo") }
|
25
|
+
Jisota.run(config, logger: logger)
|
26
|
+
|
27
|
+
create_ssh_session { |ssh_session| ssh_session.command("echo omg > foo") }
|
28
|
+
Jisota.run(config, logger: logger)
|
29
|
+
|
30
|
+
expect(logger.stdout.writes).to include("File spec/test_files/foo created\n")
|
31
|
+
expect(logger.stdout.writes).to include("File spec/test_files/foo updated\n")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -40,5 +40,15 @@ module Jisota
|
|
40
40
|
expect(result.map(&:value)).to eq([1, 2, 4])
|
41
41
|
end
|
42
42
|
end
|
43
|
+
|
44
|
+
describe "#first" do
|
45
|
+
it "returns the first item in the collection" do
|
46
|
+
collection = Collection.new
|
47
|
+
collection.add(double(key: :foo))
|
48
|
+
collection.add(double(key: :bar))
|
49
|
+
|
50
|
+
expect(collection.first.key).to eq(:foo)
|
51
|
+
end
|
52
|
+
end
|
43
53
|
end
|
44
54
|
end
|
@@ -11,10 +11,11 @@ module Jisota
|
|
11
11
|
describe "#execute" do
|
12
12
|
it "asks ssh_session to execute" do
|
13
13
|
script = CommandScript.new(:foo)
|
14
|
-
ssh_session = instance_double(SSHSession, command:
|
15
|
-
|
14
|
+
ssh_session = instance_double(SSHSession, command: 0)
|
15
|
+
context = ScriptContext.new(ssh_session: ssh_session)
|
16
|
+
result = script.execute(context)
|
16
17
|
|
17
|
-
expect(ssh_session).to have_received(:command).with(:foo,
|
18
|
+
expect(ssh_session).to have_received(:command).with(:foo, anything)
|
18
19
|
expect(result).to be true
|
19
20
|
end
|
20
21
|
end
|
@@ -10,15 +10,17 @@ module Jisota
|
|
10
10
|
|
11
11
|
describe "execute" do
|
12
12
|
let(:ssh_session) { instance_double(SSHSession) }
|
13
|
+
let(:context) { ScriptContext.new(ssh_session: ssh_session) }
|
13
14
|
let(:script) { CompositeScript.new }
|
15
|
+
|
14
16
|
it "executes all scripts" do
|
15
17
|
inner_1 = double(execute: true)
|
16
18
|
inner_2 = double(execute: true)
|
17
19
|
script.scripts << inner_1 << inner_2
|
18
|
-
result = script.execute(
|
20
|
+
result = script.execute(context)
|
19
21
|
|
20
|
-
expect(inner_1).to have_received(:execute).with(
|
21
|
-
expect(inner_2).to have_received(:execute).with(
|
22
|
+
expect(inner_1).to have_received(:execute).with(context)
|
23
|
+
expect(inner_2).to have_received(:execute).with(context)
|
22
24
|
expect(result).to be true
|
23
25
|
end
|
24
26
|
|
@@ -26,10 +28,10 @@ module Jisota
|
|
26
28
|
inner_1 = double(execute: false)
|
27
29
|
inner_2 = double(execute: true)
|
28
30
|
script.scripts << inner_1 << inner_2
|
29
|
-
result = script.execute(
|
31
|
+
result = script.execute(context)
|
30
32
|
|
31
|
-
expect(inner_1).to have_received(:execute).with(
|
32
|
-
expect(inner_2).to_not have_received(:execute).with(
|
33
|
+
expect(inner_1).to have_received(:execute).with(context)
|
34
|
+
expect(inner_2).to_not have_received(:execute).with(context)
|
33
35
|
expect(result).to be false
|
34
36
|
end
|
35
37
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Jisota
|
4
|
+
|
5
|
+
class SomeDSL < DSLBase
|
6
|
+
def set(value)
|
7
|
+
@value = value
|
8
|
+
end
|
9
|
+
|
10
|
+
def get
|
11
|
+
@value
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe DSLBase do
|
16
|
+
describe "#evaluate" do
|
17
|
+
it "evaluates like a true closure" do
|
18
|
+
foo = "bar"
|
19
|
+
dsl = SomeDSL.new
|
20
|
+
dsl.evaluate do
|
21
|
+
set foo
|
22
|
+
end
|
23
|
+
|
24
|
+
expect(dsl.get).to eq("bar")
|
25
|
+
end
|
26
|
+
|
27
|
+
it "handles method_missing" do
|
28
|
+
dsl = SomeDSL.new
|
29
|
+
expect {
|
30
|
+
dsl.evaluate do
|
31
|
+
foo
|
32
|
+
end
|
33
|
+
}.to raise_exception(NameError)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -2,21 +2,76 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
module Jisota
|
4
4
|
describe FileScript do
|
5
|
-
describe "#
|
6
|
-
it "initializes with
|
7
|
-
|
5
|
+
describe "#initialization" do
|
6
|
+
it "initializes with from and to" do
|
7
|
+
script = FileScript.new(from: "foo", to: "bar")
|
8
|
+
expect(script.from).to eq("foo")
|
9
|
+
expect(script.to).to eq("bar")
|
10
|
+
end
|
11
|
+
|
12
|
+
it "accepts create and update args" do
|
13
|
+
script = FileScript.new(from: "foo", to: "bar", create: true, update: :foo)
|
14
|
+
expect(script.create).to be true
|
15
|
+
expect(script.update).to eq(:foo)
|
8
16
|
end
|
9
17
|
end
|
10
18
|
|
11
19
|
describe "#execute" do
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
20
|
+
let(:script) { FileScript.new(from: "foo", to: "bar") }
|
21
|
+
let(:ssh_session) { instance_double(SSHSession, upload: nil, command: 0) }
|
22
|
+
let(:context) { ScriptContext.new(ssh_session: ssh_session) }
|
23
|
+
|
24
|
+
it "uploads and moves the file" do
|
25
|
+
allow(ssh_session).to receive(:command).with(a_string_starting_with("cmp")) { 1 }
|
26
|
+
result = script.execute(context)
|
16
27
|
|
17
|
-
expect(ssh_session).to have_received(:
|
28
|
+
expect(ssh_session).to have_received(:command).with("mkdir -p tmp/jisota", anything)
|
29
|
+
expect(ssh_session).to have_received(:upload) do |options|
|
30
|
+
expect(options[:from]).to eq("foo")
|
31
|
+
expect(options[:to]).to start_with("tmp/jisota/")
|
32
|
+
end
|
33
|
+
expect(ssh_session).to have_received(:command).with(a_string_matching(/sudo mkdir -p .* sudo mv .* bar/), anything)
|
18
34
|
expect(result).to be true
|
19
35
|
end
|
36
|
+
|
37
|
+
it "does not move file if it already exists and is identical" do
|
38
|
+
allow(ssh_session).to receive(:command).with(a_string_starting_with("cmp")) { 0 }
|
39
|
+
result = script.execute(context)
|
40
|
+
|
41
|
+
expect(ssh_session).to_not have_received(:command).with(a_string_matching(/sudo mv/), anything)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "does not move file when target does not exist and create is false" do
|
45
|
+
script.create = false
|
46
|
+
allow(ssh_session).to receive(:command).with(a_string_starting_with("cmp")) { 2 }
|
47
|
+
result = script.execute(context)
|
48
|
+
|
49
|
+
expect(ssh_session).to_not have_received(:command).with(a_string_matching(/sudo mv/), anything)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "does not move file when target exists and update is false" do
|
53
|
+
script.update = false
|
54
|
+
allow(ssh_session).to receive(:command).with(a_string_starting_with("cmp")) { 1 }
|
55
|
+
result = script.execute(context)
|
56
|
+
|
57
|
+
expect(ssh_session).to_not have_received(:command).with(a_string_matching(/sudo mv/), anything)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "runs the callbacks" do
|
61
|
+
allow(ssh_session).to receive(:command).with(a_string_starting_with("cmp")) { 2 }
|
62
|
+
script.create = ScriptBlock.new { cmd "foo" }
|
63
|
+
result = script.execute(context)
|
64
|
+
expect(ssh_session).to have_received(:command).with("foo", anything)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe "DSL" do
|
69
|
+
it "can set simple values on callback" do
|
70
|
+
script = FileScript.new(from: "foo", to: "bar") do
|
71
|
+
create false
|
72
|
+
end
|
73
|
+
expect(script.create).to be false
|
74
|
+
end
|
20
75
|
end
|
21
76
|
end
|
22
77
|
end
|