jisota 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +5 -0
  4. data/CHANGELOG.md +21 -0
  5. data/README.md +106 -2
  6. data/Rakefile +10 -0
  7. data/jisota.gemspec +1 -0
  8. data/lib/jisota.rb +4 -2
  9. data/lib/jisota/collection.rb +2 -4
  10. data/lib/jisota/command_script.rb +3 -2
  11. data/lib/jisota/composite_script.rb +2 -2
  12. data/lib/jisota/configuration.rb +3 -3
  13. data/lib/jisota/dsl_base.rb +12 -0
  14. data/lib/jisota/file_script.rb +107 -9
  15. data/lib/jisota/nil_output.rb +14 -0
  16. data/lib/jisota/{logger.rb → output.rb} +16 -35
  17. data/lib/jisota/package.rb +2 -2
  18. data/lib/jisota/package_script.rb +28 -20
  19. data/lib/jisota/packages/gem_install.rb +17 -0
  20. data/lib/jisota/packages/nginx_passenger.rb +34 -0
  21. data/lib/jisota/packages/ruby.rb +2 -2
  22. data/lib/jisota/param_parser.rb +30 -19
  23. data/lib/jisota/provisioner.rb +7 -3
  24. data/lib/jisota/script_block.rb +13 -14
  25. data/lib/jisota/script_context.rb +24 -0
  26. data/lib/jisota/server.rb +2 -1
  27. data/lib/jisota/ssh_engine.rb +6 -2
  28. data/lib/jisota/ssh_session.rb +10 -15
  29. data/lib/jisota/version.rb +1 -1
  30. data/package_files/nginx_passenger/nginx_service +65 -0
  31. data/spec/acceptance/ruby_passenger_nginx_spec.rb +24 -0
  32. data/spec/acceptance/simple_script_spec.rb +8 -24
  33. data/spec/acceptance/upload_blocks_spec.rb +34 -0
  34. data/spec/lib/jisota/collection_spec.rb +10 -0
  35. data/spec/lib/jisota/command_script_spec.rb +4 -3
  36. data/spec/lib/jisota/composite_script_spec.rb +8 -6
  37. data/spec/lib/jisota/configuration_spec.rb +1 -3
  38. data/spec/lib/jisota/dsl_base_spec.rb +37 -0
  39. data/spec/lib/jisota/file_script_spec.rb +63 -8
  40. data/spec/lib/jisota/output_spec.rb +84 -0
  41. data/spec/lib/jisota/package_script_spec.rb +20 -8
  42. data/spec/lib/jisota/package_spec.rb +2 -6
  43. data/spec/lib/jisota/packages/apt_spec.rb +7 -4
  44. data/spec/lib/jisota/packages/gem_install_spec.rb +18 -0
  45. data/spec/lib/jisota/packages/nginx_passenger_spec.rb +17 -0
  46. data/spec/lib/jisota/packages/ruby_spec.rb +6 -3
  47. data/spec/lib/jisota/param_parser_spec.rb +105 -0
  48. data/spec/lib/jisota/provisioner_spec.rb +30 -0
  49. data/spec/lib/jisota/role_spec.rb +1 -3
  50. data/spec/lib/jisota/script_block_spec.rb +7 -4
  51. data/spec/lib/jisota/ssh_engine_spec.rb +26 -0
  52. data/spec/lib/jisota/ssh_session_spec.rb +53 -0
  53. data/spec/spec_helper.rb +11 -1
  54. data/spec/support/acceptance_helpers.rb +45 -0
  55. data/spec/test_files/nginx_default.conf +121 -0
  56. data/spec/vagrant/Vagrantfile +118 -0
  57. data/spec/vagrant/ssh_key +27 -0
  58. metadata +55 -7
  59. data/lib/jisota/upload_file.rb +0 -3
  60. data/spec/lib/jisota/logger_spec.rb +0 -34
@@ -0,0 +1,14 @@
1
+ module Jisota
2
+ class NilOutput < Output
3
+ class NilIO
4
+ def write(*); end
5
+ def flush(*); end
6
+ end
7
+
8
+ def initialize(options = {})
9
+ super
10
+ @stdout = NilIO.new
11
+ @stderr = NilIO.new
12
+ end
13
+ end
14
+ end
@@ -4,7 +4,7 @@ module Jisota
4
4
  #
5
5
  # Will default to use STDOUT and STDERR, but that can be overwritten in the
6
6
  # initializer
7
- class Logger
7
+ class Output
8
8
  attr_accessor :stdout, :stderr, :verbose, :prefix, :indent_level
9
9
 
10
10
  def initialize(options = {})
@@ -15,60 +15,41 @@ module Jisota
15
15
  @indent_level = 0
16
16
  end
17
17
 
18
- def command(command)
19
- prefixed_message "Executing #{command}"
20
- end
21
-
22
- def upload(from: , to: )
23
- prefixed_message "Uploading #{from} -> #{to}"
24
- end
25
-
26
- def package(package_script)
27
- prefixed_message "Package #{package_script}"
28
- end
29
-
30
- def package_cancelled_by_verify(package_script)
31
- prefixed_message "Package #{package_script} already installed"
32
- end
33
-
34
- def prefixed_message(message)
35
- stdout.write(create_message(message, true, true))
18
+ def system_message(message, &block)
19
+ result = ["", prefix, " " * indent_level, message, "\n"].join
20
+ stdout.write(result)
21
+ stdout.flush
22
+ indent(&block) if block_given?
36
23
  end
37
24
 
38
25
  def info(message)
39
26
  stdout.write(message) if verbose
27
+ stdout.flush
40
28
  end
41
29
 
42
30
  def warn(message)
43
31
  stderr.write(message) if verbose
32
+ stderr.flush
44
33
  end
45
34
 
46
35
  def error(message)
47
36
  stderr.write(message)
37
+ stderr.flush
48
38
  end
49
39
 
50
40
  def indent
51
41
  self.indent_level += 1
42
+ if block_given?
43
+ begin
44
+ yield
45
+ ensure
46
+ outdent
47
+ end
48
+ end
52
49
  end
53
50
 
54
51
  def outdent
55
52
  self.indent_level -= 1
56
53
  end
57
-
58
- private
59
-
60
- def create_message(message, newline, use_prefix)
61
- result = ""
62
- if use_prefix
63
- result << prefix
64
- else
65
- result << " " * prefix.size
66
- end
67
- result << " " * indent_level
68
- result << message
69
- result << "\n" if newline
70
- result
71
- end
72
-
73
54
  end
74
55
  end
@@ -5,12 +5,12 @@ module Jisota
5
5
  def initialize(name = nil, &block)
6
6
  @params = []
7
7
  @name = name
8
- DSL.new(self).instance_eval(&block) if block_given?
8
+ DSL.new(self).evaluate(&block) if block_given?
9
9
  end
10
10
 
11
11
  alias_method :key, :name
12
12
 
13
- class DSL
13
+ class DSL < DSLBase
14
14
  def initialize(target)
15
15
  @target = target
16
16
  end
@@ -8,47 +8,55 @@ module Jisota
8
8
  # If the package has a `verify_block`, that will be executed first. If the
9
9
  # result of verify is success, the `run_block` will not be executed.
10
10
  class PackageScript
11
- attr_accessor :package, :args, :packages
11
+ attr_accessor :package, :args
12
12
 
13
- def initialize(package, args = [], packages = Collection.new)
13
+ def initialize(package, args = [])
14
14
  @package = package
15
15
  @args = args
16
- @packages = packages
17
16
  end
18
17
 
19
- def execute(ssh_session, logger = nil)
20
- logger.package(self) if logger
21
- logger.indent if logger
22
- result = execute_verify_and_run(ssh_session, logger)
23
- logger.outdent if logger
24
- result
18
+ def execute(context)
19
+ context.param_scope(parse_params) do
20
+ context.logger.system_message("Package #{self}") do
21
+ execute_verify_and_run(context)
22
+ end
23
+ end
25
24
  end
26
25
 
27
26
  def to_s
28
- "#{package.name} #{args.map(&:inspect).join(", ")}"
27
+ result = ""
28
+ result << package.name.to_s
29
+ if args.any?
30
+ result << " "
31
+ result << args.map(&:inspect).join(", ")
32
+ end
33
+ result
29
34
  end
30
35
 
31
36
  private
32
37
 
33
- def execute_verify_and_run(ssh_session, logger)
34
- parsed_params = ParamParser.new(package.params, args).parse
38
+ def parse_params
39
+ ParamParser.new(package.params, args).parse
40
+ end
41
+
42
+ def execute_verify_and_run(context)
35
43
  if package.verify_block
36
- verify_script = package.verify_block.evaluate(parsed_params, packages)
37
- result = verify_script.execute(ssh_session, logger)
44
+ verify_script = package.verify_block.evaluate(context)
45
+ result = verify_script.execute(context)
38
46
  if result
39
- logger.package_cancelled_by_verify(self) if logger
47
+ context.logger.system_message("Package #{self} already installed")
40
48
  true
41
49
  else
42
- execute_run(parsed_params, ssh_session, logger)
50
+ execute_run(context)
43
51
  end
44
52
  else
45
- execute_run(parsed_params, ssh_session, logger)
53
+ execute_run(context)
46
54
  end
47
55
  end
48
56
 
49
- def execute_run(parsed_params, ssh_session, logger)
50
- run_script = package.run_block.evaluate(parsed_params, packages)
51
- run_script.execute(ssh_session, logger)
57
+ def execute_run(context)
58
+ run_script = package.run_block.evaluate(context)
59
+ run_script.execute(context)
52
60
  end
53
61
  end
54
62
  end
@@ -0,0 +1,17 @@
1
+ require 'jisota'
2
+
3
+ Jisota.global_config do
4
+ package :gem_install do
5
+ description "Installs a gem"
6
+ param :gem_name, required: true
7
+ param :sudo, default: true
8
+
9
+ run do
10
+ cmd "#{sudo ? "sudo " : ""}gem install #{gem_name} --no-document"
11
+ end
12
+
13
+ verify do
14
+ cmd "#{sudo ? "sudo " : ""}gem list #{gem_name} --installed"
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,34 @@
1
+ require 'jisota'
2
+
3
+ Jisota.global_config do
4
+ package :nginx_passenger do
5
+ description "Install nginx with passenger module"
6
+ param :config_file, required: true
7
+
8
+ run do
9
+ gem_install :passenger
10
+ apt "libcurl4-openssl-dev"
11
+ nginx_passenger_install
12
+
13
+ upload from: config_file, to: '/opt/nginx/conf/nginx.conf' do
14
+ update { cmd "sudo service nginx restart" }
15
+ end
16
+
17
+ upload from: "package_files/nginx_passenger/nginx_service", to: "/etc/init.d/nginx" do
18
+ create { cmd 'sudo update-rc.d nginx defaults 99' }
19
+ end
20
+
21
+ nginx_passenger_start
22
+ end
23
+ end
24
+
25
+ package :nginx_passenger_install do
26
+ run { cmd 'sudo passenger-install-nginx-module --auto --auto-download --prefix=/opt/nginx' }
27
+ verify { cmd 'which /opt/nginx/sbin/nginx' }
28
+ end
29
+
30
+ package :nginx_passenger_start do
31
+ run { cmd "sudo service nginx start" }
32
+ verify { cmd "ps -C nginx" }
33
+ end
34
+ end
@@ -9,7 +9,7 @@ Jisota.global_config do
9
9
  run do
10
10
  minor_version = version.match(/\d+\.\d+/)[0]
11
11
 
12
- apt *%w(libffi-dev libssl-dev zlib1g-dev libreadline-dev)
12
+ apt *%w(libffi-dev libssl-dev zlib1g-dev libreadline-dev build-essential)
13
13
  cmd %Q{
14
14
  mkdir -p #{tmp_dir} &&
15
15
  cd #{tmp_dir} &&
@@ -18,7 +18,7 @@ Jisota.global_config do
18
18
  cd ruby-#{version} &&
19
19
  ./configure &&
20
20
  make &&
21
- sudo make install &&
21
+ sudo make install
22
22
  }
23
23
  end
24
24
 
@@ -3,17 +3,17 @@ module Jisota
3
3
  attr_reader :params, :args
4
4
 
5
5
  def initialize(params, args)
6
- @original_params = params.dup
7
- @params = params.dup
8
- @args = args.dup
6
+ @params = params
7
+ @args = args
9
8
  end
10
9
 
11
10
  def parse
11
+ params_work_list = params.dup
12
12
  Hash.new.tap do |result|
13
13
  init_splat_params(result)
14
- add_implicit_args(result)
15
- add_hash_args(result)
16
- set_remaining_params(result)
14
+ add_unnamed_args(result, params_work_list)
15
+ add_named_args(result)
16
+ handle_remaining_params(result, params_work_list)
17
17
  end
18
18
  end
19
19
 
@@ -25,12 +25,11 @@ module Jisota
25
25
  end
26
26
  end
27
27
 
28
- def add_implicit_args(result)
28
+ def add_unnamed_args(result, params_work_list)
29
29
  param = nil
30
- while args.first && !args.first.is_a?(Hash)
31
- param = params.shift unless param && param.splat?
32
- arg = args.shift
33
- raise ParameterError, "No parameter for implicit argument #{arg}" unless param
30
+ unnamed_args.each do |arg|
31
+ param = params_work_list.shift unless param && param.splat?
32
+ raise ParameterError, "No parameter for unnamed argument #{arg}" unless param
34
33
  if param.splat?
35
34
  result[param.name] << arg
36
35
  else
@@ -39,25 +38,37 @@ module Jisota
39
38
  end
40
39
  end
41
40
 
42
- def add_hash_args(result)
43
- return if args.empty?
44
- raise ParameterError, "Hash parameters must be last in argument list" if args.size > 1
45
- args.first.each do |key, value|
46
- param = @original_params.select { |p| p.name == key }.first
41
+ def add_named_args(result)
42
+ named_args.each do |key, value|
43
+ param = params.select { |p| p.name == key }.first
47
44
  raise ParameterError, "No parameter with name #{key.inspect}" unless param
48
45
  if result.has_key?(key)
49
46
  if param.splat?
50
47
  raise ParameterError, "Splat parameter #{key.inspect} only accepts unnamed arguments"
51
48
  else
52
- raise ParameterError, "Parameter #{key.inspect} already set with an implicit argument"
49
+ raise ParameterError, "Parameter #{key.inspect} already set with an unnamed argument"
53
50
  end
54
51
  end
55
52
  result[key] = value
56
53
  end
57
54
  end
58
55
 
59
- def set_remaining_params(result)
60
- params.each do |param|
56
+ def unnamed_args
57
+ args.dup.tap do |result|
58
+ result.pop if named_args?
59
+ end
60
+ end
61
+
62
+ def named_args
63
+ named_args? ? args.last : {}
64
+ end
65
+
66
+ def named_args?
67
+ args.last.is_a?(Hash)
68
+ end
69
+
70
+ def handle_remaining_params(result, params_work_list)
71
+ params_work_list.each do |param|
61
72
  unless result.has_key?(param.name)
62
73
  if param.required? && !param.default?
63
74
  raise ParameterError, "Parameter #{param.name.inspect} is required"
@@ -12,11 +12,15 @@ module Jisota
12
12
  private
13
13
 
14
14
  def run_server(server, ssh, roles, packages, logger)
15
- ssh.start(user: server.user, host: server.host) do |ssh_session|
15
+ ssh_options = { user: server.user, host: server.host }
16
+ ssh_options[:keys] = [server.key] if server.key
17
+
18
+ ssh.start(ssh_options) do |ssh_session|
16
19
  server.roles.each do |role_name|
17
20
  role = roles[role_name]
18
- script = role.script_block.evaluate({}, packages)
19
- script.execute(ssh_session, logger)
21
+ context = ScriptContext.new(ssh_session: ssh_session, logger: logger, packages: packages)
22
+ script = role.script_block.evaluate(context)
23
+ script.execute(context)
20
24
  end
21
25
  end
22
26
  end
@@ -15,26 +15,25 @@ module Jisota
15
15
  @block = block
16
16
  end
17
17
 
18
- def evaluate(args = {}, packages = Collection.new)
18
+ def evaluate(context = ScriptContext.new)
19
19
  CompositeScript.new.tap do |script|
20
- dsl = DSL.new(script, args, packages)
21
- dsl.instance_eval(&block)
20
+ dsl = DSL.new(script, context)
21
+ dsl.evaluate(&block)
22
22
  end
23
23
  end
24
24
 
25
- class DSL
26
- def initialize(script, args, packages)
25
+ class DSL < DSLBase
26
+ def initialize(script, context)
27
27
  @script = script
28
- @args = args
29
- @packages = packages
28
+ @context = context
30
29
  end
31
30
 
32
31
  def cmd(command)
33
32
  @script.scripts << CommandScript.new(command)
34
33
  end
35
34
 
36
- def upload(from:, to: )
37
- @script.scripts << FileScript.new(UploadFile.new(from, to))
35
+ def upload(**options, &block)
36
+ @script.scripts << FileScript.new(**options, &block)
38
37
  end
39
38
 
40
39
  def method_missing(method, *args, &block)
@@ -48,17 +47,17 @@ module Jisota
48
47
  private
49
48
 
50
49
  def has_argument?(name)
51
- @args.has_key?(name)
50
+ @context.params.has_key?(name)
52
51
  end
53
52
 
54
53
  def get_argument(name)
55
- @args.fetch(name)
54
+ @context.params.fetch(name)
56
55
  end
57
56
 
58
57
  def add_package_script(name, args)
59
- if @packages.has_key?(name)
60
- package = @packages[name]
61
- @script.scripts << PackageScript.new(package, args, @packages)
58
+ if @context.packages.has_key?(name)
59
+ package = @context.packages[name]
60
+ @script.scripts << PackageScript.new(package, args)
62
61
  end
63
62
  end
64
63
  end
@@ -0,0 +1,24 @@
1
+ module Jisota
2
+ class ScriptContext
3
+ attr_accessor :ssh_session, :logger, :params, :packages
4
+
5
+ def initialize(options = {})
6
+ @ssh_session = options.fetch(:ssh_session) { nil }
7
+ @logger = options.fetch(:logger) { NilOutput.new }
8
+ @params = options.fetch(:params) { Hash.new }
9
+ @packages = options.fetch(:packages) { Collection.new }
10
+ end
11
+
12
+ def command(cmd)
13
+ ssh_session.command(cmd, logger)
14
+ end
15
+
16
+ def param_scope(scope_params)
17
+ old_params = params
18
+ self.params = scope_params
19
+ result = yield if block_given?
20
+ self.params = old_params
21
+ result
22
+ end
23
+ end
24
+ end