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.
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