cindy-cm 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 774f6b5df9922b3a6771968f4fb61e05d8b0f3dc
4
- data.tar.gz: 647e09f6a7ebb222966f785c49492a525fd92c5f
3
+ metadata.gz: f8c7c22d266d8267c749cd4c9b98eb41a52af41c
4
+ data.tar.gz: 880741fc31c94b0f89aa77d6f7c250c1310d322b
5
5
  SHA512:
6
- metadata.gz: 8ecafc4acc58778989dbab0e650d18a70e026ccf985593ca1c1d5adf2003da8c804b90e5bfae9bad47571a90e9260335046f1a264c208fb0c8028052c411f309
7
- data.tar.gz: 2a51c783e0ab07d5b95e0448c5b8e7bf6737cc7804dfc3935ef56fe629c29b23b7bab67d55a1c3d7c946c805dc5c563a7acefbe3566f0c85f56257cdd7f534a8
6
+ metadata.gz: af5a8f616854bad4d4fc7be8dcc234abbd597cda1c00e3a5e1a191ab28b7ebc9fa0fd5aadddf0753d843132f349f9741c465a5915f91d08707086ec1ea7c5d80
7
+ data.tar.gz: 928a046d7dc654ed9f9abbf18ac4677091bb50dc2b1c9d7a797161459fb6fdd336a060bdece1780dcc6fe8a5fe5a827c9bde36df6cd6e939e7162b26a11d09cd
@@ -1,3 +1,9 @@
1
+ ### Changes from 0.1.1 to ?.?.?
2
+
3
+ ### Changes from 0.1.0 to 0.1.1
4
+
5
+ * force encoding to ASCII-8BIT of data send to stdin through ssh
6
+
1
7
  ### Changes from 0.0.1 to 0.1.0
2
8
 
3
9
  * configuration format changed (XML => Ruby)
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  ## Introduction
2
2
 
3
- Tired to modify your configuration files depending on the targeted computer? Turn them out into ERB templates and deploy them in one command.
3
+ Tired to modify your configuration files depending on the targeted host? Turn them out into ERB templates and deploy them in one command.
4
4
 
5
5
  The purpose is to implement a kind of shell with limited dependencies to automate configuration and deployment on various (Unix) environments.
6
6
 
@@ -10,7 +10,7 @@ Dependencies: net-ssh, highline
10
10
 
11
11
  `gem install cindy-cm`
12
12
 
13
- ## Usage
13
+ ## Usage of cindy command
14
14
 
15
15
  * reload => force Cindy to reload its configuration file
16
16
  * environment (shortcut: env)
@@ -22,16 +22,18 @@ Dependencies: net-ssh, highline
22
22
  * deploy => install the generated file on the given environment
23
23
  * print => display output configuration file as it would be deployed on the given environment
24
24
  * details => list all applicable variables to the given template, their values and scopes
25
+
25
26
  ## Example
26
27
 
27
28
  Create ~/.cindy as follows:
29
+
28
30
  ```ruby
29
31
  # create 2 environments named "production" and "development"
30
- environment :development, 'file:///'
32
+ environment :development
31
33
  environment :production, 'ssh://root@www.xxx.tld/'
32
34
 
33
- # register the template /home/julp/cindy/templates/nginx.conf.tpl (see below) for our nginx configuration
34
- template :nginx, '/home/julp/cindy/templates/nginx.conf.tpl' do
35
+ # register the template ~/cindy/templates/nginx.conf.tpl (see below) for our nginx configuration
36
+ template :nginx, '~/cindy/templates/nginx.conf.tpl' do
35
37
  # default variables
36
38
 
37
39
  # have_gzip_static will be set to true or false depending on the result of the following command
@@ -49,10 +51,16 @@ template :nginx, '/home/julp/cindy/templates/nginx.conf.tpl' do
49
51
  var :server_name, 'www.xxx.lan'
50
52
  var :root, '/home/julp/app/public'
51
53
  end
54
+
55
+ # after deployment, check syntax
56
+ postcmd 'nginx -tc $INSTALL_FILE'
57
+ # if no error, reload configuration
58
+ postcmd 'nginx -s reload'
52
59
  end
53
60
  ```
54
61
 
55
- And /home/julp/cindy/templates/nginx.conf.tpl as:
62
+ And ~/cindy/templates/nginx.conf.tpl as:
63
+
56
64
  ```
57
65
  # <%= _install_file_ %>
58
66
 
@@ -71,6 +79,7 @@ server {
71
79
  ```
72
80
 
73
81
  Running `cindy template nginx environment production print`, result in:
82
+
74
83
  ```
75
84
  # /usr/local/etc/nginx.conf
76
85
 
@@ -84,19 +93,20 @@ server {
84
93
  (if we admit that nginx is built, on production environment, with Gzip Precompression module)
85
94
 
86
95
  After `cindy template nginx environment production deploy`, output of `ls -l /etc/nginx.conf*` is:
96
+
87
97
  ```
88
98
  lrwxrwxrwx [...] /usr/local/etc/nginx.conf -> /usr/local/etc/nginx.conf.201502262311
89
99
  -rw-r--r-- [...] /usr/local/etc/nginx.conf.201502262209 # file at previous deployment
90
100
  -rw-r--r-- [...] /usr/local/etc/nginx.conf.201502262311 # current version (1h02m later)
91
101
  ```
92
102
 
93
- ## What is a *command* typed variable?
103
+ ## What is a *Command* object?
94
104
 
95
105
  It is a kind of dynamic variable: instead of hardcoding a value which depends on the remote host, we execute the associated command before each
96
106
  time the template is rendered. It is more convenient mainly if this value can change at any time.
97
107
 
98
- The result of the command is a boolean based on its exit status (0 => true, everithing else => false) if the command does not print anything on
99
- standard output else a string with the content of standard output.
108
+ The result of the command is a boolean based on its exit status (0 => true, everything else => false) if the command does not print anything on
109
+ standard output else a string with the content sent on standard output.
100
110
 
101
111
  In the example above, `nginx -V 2>&1 >/dev/null | grep -qF -- --with-http_gzip_static_module` is intended to determine if nginx, on the remote
102
112
  server, is compiled or not with the gzip_static module.
@@ -4,6 +4,7 @@ require 'cindy/command'
4
4
  require 'cindy/environment'
5
5
  require 'cindy/variable'
6
6
  require 'cindy/template'
7
+ require 'cindy/executor/base'
7
8
  require 'cindy/executor/ssh'
8
9
  require 'cindy/executor/local'
9
10
 
@@ -1,3 +1,5 @@
1
+ require 'logger'
2
+
1
3
  module Cindy
2
4
 
3
5
  class UndefinedEnvironmentError < ::NameError
@@ -11,6 +13,7 @@ module Cindy
11
13
 
12
14
  class Cindy
13
15
 
16
+ # Default location of Cindy's configuration file
14
17
  CONFIGURATION_FILE = File.expand_path '~/.cindy'
15
18
 
16
19
  module DSL
@@ -20,6 +23,10 @@ module Cindy
20
23
  @envname = envname
21
24
  end
22
25
 
26
+ def cmd(command, options = {})
27
+ Command.new command, options
28
+ end
29
+
23
30
  def var(varname, value)
24
31
  @tpl.set_variable @envname, varname, value
25
32
  end
@@ -32,6 +39,10 @@ module Cindy
32
39
  @tpl = tpl
33
40
  end
34
41
 
42
+ def cmd(command, options = {})
43
+ Command.new command, options
44
+ end
45
+
35
46
  def var(varname, value)
36
47
  @tpl.set_variable nil, varname, value
37
48
  end
@@ -42,6 +53,10 @@ module Cindy
42
53
  @tpl.set_path_for_environment envname, file
43
54
  TemplateEnvironmentNode.new(@tpl, envname).instance_eval &block if block_given?
44
55
  end
56
+
57
+ def postcmd(cmd, options = {})
58
+ @tpl.add_postcmd cmd, options
59
+ end
45
60
  end
46
61
 
47
62
  class CindyNode
@@ -55,23 +70,40 @@ module Cindy
55
70
  tpl
56
71
  end
57
72
 
58
- def environment(envname, uri = nil)
73
+ def environment(envname, uri = '')
59
74
  @cindy.environment_add envname, uri
60
75
  end
61
76
  end
62
77
  end
63
78
 
79
+ # DSL
80
+ # \@!method environment(envname, uri = '')
81
+ # \@!method template(tplname, path, &block)
82
+
83
+ # @return [Logger] the logger associated to the cindy instance
84
+ attr_accessor :logger
85
+
86
+ # Creates an "empty" Cindy object
64
87
  def initialize
65
88
  @environments = {}
66
89
  @templates = {}
90
+ @logger = Logger.new STDERR
67
91
  end
68
92
 
93
+ # Creates a Cindy object from a string
94
+ #
95
+ # @param string [String] the configuration string to evaluate
96
+ # @return [Cindy]
69
97
  def self.from_string(string)
70
98
  cindy = Cindy.new
71
99
  DSL::CindyNode.new(cindy).instance_eval string
72
100
  cindy
73
101
  end
74
102
 
103
+ # Creates a Cindy object from a file
104
+ #
105
+ # @param filename [String] the file to evaluate ; default location {CONFIGURATION_FILE} is used if nil
106
+ # @return [Cindy]
75
107
  def self.load(filename = nil)
76
108
  @filename = filename || CONFIGURATION_FILE
77
109
  cindy = Cindy.new
@@ -83,36 +115,67 @@ module Cindy
83
115
  (@environments.values.map(&:to_s) + [''] + @templates.values.map(&:to_s)).join("\n")
84
116
  end
85
117
 
118
+ # Get known environments
119
+ #
120
+ # @return [Array<Symbol>] list of environment names
86
121
  def environments
87
122
  @environments.values
88
123
  end
89
124
 
125
+ # Does environment exist?
126
+ #
127
+ # @param envname [String, Symbol] the name of the environment to check for existence
90
128
  def has_environment?(envname)
91
129
  envname = envname.intern
92
130
  @environments.key? envname
93
131
  end
94
132
 
133
+ # Add a new environment
134
+ #
135
+ # @param envname [String, Symbol] the name of the new environment
136
+ # @param attributes [String] the uri of the host (eg 'ssh://user@1.2.3.4/' for a
137
+ # remote one or 'file:///' to work directely on actual host)
138
+ # @return [Environment] the registered environment
95
139
  def environment_add(envname, attributes)
96
140
  envname = envname.intern
97
141
  # assert !@environments.key? envname
98
142
  @environments[envname] = Environment.new(envname, attributes)
99
143
  end
100
144
 
145
+ # Get registered templates
146
+ #
147
+ # @return [Array<Symbol>] list of template names
101
148
  def templates
102
149
  @templates.values
103
150
  end
104
151
 
152
+ # Does template exist?
153
+ #
154
+ # @param tplname [String, Symbol] the name of the template to check for existence
105
155
  def has_template?(tplname)
106
156
  tplname = tplname.intern
107
157
  @templates.key? tplname
108
158
  end
109
159
 
160
+ # Add a new template
161
+ #
162
+ # @param tplname [String, Symbol] the name of the new template
163
+ # @param file [String] the location of the template file. Note it is expanded
164
+ # ({File#expand_path}) to permit usage of ~ so it may be a good idea to avoid
165
+ # relative paths
166
+ # @return [Template] the registered template
110
167
  def template_add(tplname, file)
111
168
  tplname = tplname.intern
112
169
  # assert !@templates.key? name
113
- @templates[tplname] = Template.new File.expand_path(file), tplname
170
+ @templates[tplname] = Template.new self, File.expand_path(file), tplname
114
171
  end
115
172
 
173
+ # Print on stdout the result of template generation
174
+ #
175
+ # @param envname [String, Symbol] the name of the environment
176
+ # @param tplname [String, Symbol] the name of the template
177
+ # @raise [UndefinedEnvironmentError] if environment does not exist
178
+ # @raise [UndefinedTemplateError] if template does not exist
116
179
  def template_environment_print(envname, tplname)
117
180
  envname = envname.intern
118
181
  tplname = tplname.intern
@@ -121,6 +184,12 @@ module Cindy
121
184
  @templates[tplname].print @environments[envname]
122
185
  end
123
186
 
187
+ # Deploy the result of template generation on the given environment
188
+ #
189
+ # @param envname [String, Symbol] the name of the environment
190
+ # @param tplname [String, Symbol] the name of the template
191
+ # @raise [UndefinedEnvironmentError] if environment does not exist
192
+ # @raise [UndefinedTemplateError] if template does not exist
124
193
  def template_environment_deploy(envname, tplname)
125
194
  envname = envname.intern
126
195
  tplname = tplname.intern
@@ -129,6 +198,12 @@ module Cindy
129
198
  @templates[tplname].deploy @environments[envname]
130
199
  end
131
200
 
201
+ # Print on stdout a detail (value and scope) of applicable variables
202
+ #
203
+ # @param envname [String, Symbol] the name of the environment
204
+ # @param tplname [String, Symbol] the name of the template
205
+ # @raise [UndefinedEnvironmentError] if environment does not exist
206
+ # @raise [UndefinedTemplateError] if template does not exist
132
207
  def template_environment_variables(envname, tplname)
133
208
  envname = envname.intern
134
209
  tplname = tplname.intern
@@ -1,7 +1,8 @@
1
1
  module Cindy
2
2
  class Command
3
- def initialize(command)
3
+ def initialize(command, options = {})
4
4
  @command = command
5
+ @options = options
5
6
  end
6
7
 
7
8
  def to_s
@@ -9,11 +10,11 @@ module Cindy
9
10
  end
10
11
 
11
12
  def inspect
12
- "#{self.class.name}.new(#{@command.inspect})"
13
+ "#{self.class.name}.new(#{@command.inspect}, TODO)"
13
14
  end
14
15
 
15
16
  def call(executor)
16
- executor.exec(@command, nil, true)
17
+ executor.exec(@command, @options)
17
18
  end
18
19
  end
19
20
  end
@@ -0,0 +1,101 @@
1
+ require 'uri'
2
+
3
+ module Cindy
4
+ module Executor
5
+ class CommandFailedError < StandardError
6
+ end
7
+
8
+ class Base
9
+ # X
10
+ #
11
+ # @param [Logger] logger
12
+ def initialize(logger)
13
+ @logger = logger
14
+ end
15
+
16
+ def self.from_uri(uri, logger)
17
+ uri = URI.parse(uri)
18
+ ObjectSpace.each_object(Class).select { |v| return v.new(uri, logger) if v.ancestors.include?(self) && v.handle?(uri) }
19
+ raise Exception.new 'Unexpected protocol'
20
+ end
21
+
22
+ # Executes the given command
23
+ #
24
+ # @param command [String] the command to execute
25
+ # @param options [Hash] the options to execute the command with
26
+ # @option options [String] :stdin the string to send as/on stdin
27
+ # @option options [Boolean] :ignore_failure don't raise an exception if true when the command returns a non 0 exit value
28
+ # @option options [Hash] :env the environment variables to set/pass for command execution
29
+ # @option options [Boolean] :check_status_only simply return true if the command is successful else false
30
+ # @return [String, Boolean]
31
+ def exec(command, options = {})
32
+ =begin
33
+ stdout, stderr, status = exec_imp command, options[:stdin]
34
+ stdout.chomp!
35
+ # <logging>
36
+ if status.zero?
37
+ if stdout.empty?
38
+ @logger.info 'Command "%s" executed successfully' % command
39
+ else
40
+ @logger.info 'Command "%s" executed successfully (with "%s" returned)' % [ command, stdout ]
41
+ end
42
+ else
43
+ @logger.send(options[:ignore_failure] ? :warn : :error, 'Command "%s" failed with "%s"' % [ command, stderr ])
44
+ end
45
+ # </logging>
46
+ return status.zero? if options[:check_status_only]
47
+ raise CommandFailedError.new "Command '#{command}' failed" if !options[:ignore_failure] && 0 != status
48
+ =end
49
+ stdout, stderr, status = exec_and_log command, options
50
+ # ?!?
51
+ stdout
52
+ end
53
+
54
+ def exec!(command, options = {})
55
+ stdout, stderr, status = exec_and_log command, options
56
+ raise CommandFailedError.new "Command '#{command}' failed" unless 0 == status
57
+ stdout
58
+ end
59
+
60
+ # Close the eventual underlaying connection
61
+ def close
62
+ # NOP
63
+ end
64
+
65
+ private
66
+
67
+ def exec_and_log(command, options = {})
68
+ stdout, stderr, status = exec_imp command, options[:stdin]
69
+ stdout.chomp!
70
+ # <logging>
71
+ if status.zero?
72
+ if stdout.empty?
73
+ @logger.info 'Command "%s" executed successfully' % command
74
+ else
75
+ @logger.info 'Command "%s" executed successfully (with "%s" returned)' % [ command, stdout ]
76
+ end
77
+ else
78
+ @logger.send(options[:ignore_failure] ? :warn : :error, 'Command "%s" failed with "%s"' % [ command, stderr ])
79
+ end
80
+ [ stdout, stderr, status ]
81
+ end
82
+
83
+ protected
84
+
85
+ # @abstract
86
+ def self.handle?(uri)
87
+ false
88
+ end
89
+
90
+ # @abstract
91
+ # @param command [String] the command to execute
92
+ # @return [Array<(String, String, Fixnum)>] an array containing the following elements (in this order):
93
+ # 1. output on stdout
94
+ # 2. output on stderr
95
+ # 3. exit status
96
+ def exec_imp(command, stdin_str)
97
+ raise NotImplementedError.new
98
+ end
99
+ end
100
+ end
101
+ end
@@ -1,28 +1,34 @@
1
+ require 'uri'
1
2
  require 'open3'
2
3
 
3
4
  module Cindy
4
5
  module Executor
5
- class Local
6
- def exec(command, stdin_str = nil, status_only = false)
6
+ class Local < Base
7
+ def self.handle?(uri)
8
+ [ nil, 'file' ].include? uri.scheme
9
+ end
10
+
11
+ def initialize(uri, logger)
12
+ super logger
13
+ end
14
+
15
+ def exec_imp(command, stdin_str)
7
16
  exit_status = 1
8
17
  stdout_str = stderr_str = ''
9
- Open3.popen3({ 'PATH' => "#{ENV['PATH']}:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin" }, command) do |stdin, stdout, stderr, wait_thr|
10
- if stdin_str
11
- stdin.write stdin_str
12
- stdin.close
18
+ begin
19
+ Open3.popen3({ 'PATH' => "#{ENV['PATH']}:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin" }, command) do |stdin, stdout, stderr, wait_thr|
20
+ if stdin_str
21
+ stdin.write stdin_str
22
+ stdin.close
23
+ end
24
+ stdout_str = stdout.read
25
+ stderr_str = stderr.read
26
+ exit_status = wait_thr.value.exitstatus
13
27
  end
14
- stdout_str = stdout.read
15
- stderr_str = stderr.read
16
- exit_status = wait_thr.value
28
+ rescue Errno::ENOENT => e
29
+ stderr_str = e.message
17
30
  end
18
- # puts [ command, stderr_str, exit_status ].inspect
19
- raise Exception.new if 0 != exit_status && !stderr_str.empty?
20
- return nil if status_only && 0 != exit_status
21
- stdout_str.chomp
22
- end
23
-
24
- def close
25
- # NOP
31
+ [ stdout_str, stderr_str, exit_status ]
26
32
  end
27
33
  end
28
34
  end
@@ -1,38 +1,38 @@
1
+ require 'uri'
1
2
  require 'net/ssh'
2
3
 
3
4
  module Cindy
4
5
  module Executor
5
- class SSH
6
- def initialize(cnx)
7
- @cnx = cnx
6
+ class SSH < Base
7
+ def self.handle?(uri)
8
+ 'ssh' == uri.scheme
8
9
  end
9
10
 
10
- def exec(command, stdin_str = nil, status_only = false)
11
+ def initialize(uri, logger)
12
+ @cnx = Net::SSH.start(uri.host, uri.user)
13
+ super logger
14
+ end
15
+
16
+ def exec_imp(command, stdin_str)
11
17
  exit_status = 1
12
- # if stdin_str
13
- stdout_str = stderr_str = ''
14
- @cnx.open_channel do |channel|
15
- channel.exec(command) do |ch, success|
16
- channel.on_data do |ch, data|
17
- stdout_str += data
18
- end
19
- channel.on_extended_data do |ch, type, data|
20
- stderr_str += data
21
- end
22
- channel.on_request 'exit-status' do |ch, data|
23
- exit_status = data.read_long
24
- end
25
- channel.send_data stdin_str if stdin_str
26
- channel.eof!
18
+ stdout_str = stderr_str = ''
19
+ @cnx.open_channel do |channel|
20
+ channel.exec(command) do |ch, success|
21
+ channel.on_data do |ch, data|
22
+ stdout_str += data
23
+ end
24
+ channel.on_extended_data do |ch, type, data|
25
+ stderr_str += data
26
+ end
27
+ channel.on_request 'exit-status' do |ch, data|
28
+ exit_status = data.read_long
27
29
  end
30
+ channel.send_data stdin_str.force_encoding('ASCII-8BIT') if stdin_str
31
+ channel.eof!
28
32
  end
29
- @cnx.loop
30
- # else
31
- # result = @cnx.exec!(command)
32
- # result.chomp if result.respond_to? :chomp # as result can be nil <=> result.chomp if result <=> result.try? :chomp (rails way)
33
- # end
34
- return nil if status_only && 0 != exit_status
35
- stdout_str.chomp
33
+ end
34
+ @cnx.loop
35
+ [ stdout_str, stderr_str, exit_status ]
36
36
  end
37
37
 
38
38
  def close
@@ -1,18 +1,20 @@
1
1
  require 'erb'
2
- require 'uri'
3
2
  require 'ostruct'
3
+ require 'shellwords'
4
4
 
5
5
  module Cindy
6
6
  class Template
7
7
 
8
- attr_reader :file, :alias, :paths, :defvars, :envvars
8
+ attr_reader :file, :alias, :paths, :defvars, :envvars, :postcmds
9
9
 
10
- def initialize(file, name)
10
+ def initialize(owner, file, name)
11
+ @owner = owner # owner is Cindy objet
11
12
  @file = file # local template filename
12
13
  @alias = name
13
14
  @paths = {} # remote filenames (<environment name> => <filename>)
14
15
  @defvars = {} # default/global variables
15
16
  @envvars = {} # environment specific variables
17
+ @postcmds = [] # commands to run after deployment
16
18
  end
17
19
 
18
20
  IDENT_STRING = ' ' * 4
@@ -38,15 +40,25 @@ module Cindy
38
40
  puts render(env)
39
41
  end
40
42
 
43
+ def add_postcmd(cmd, options)
44
+ @postcmds << cmd
45
+ end
46
+
41
47
  def deploy(env)
42
48
  executor = executor_for_env env
43
49
  remote_filename = @paths[env.name]
44
50
  sudo = ''
45
- sudo = 'sudo ' unless 0 == executor.exec('id -u').to_i
51
+ sudo = 'sudo' unless 0 == executor.exec('id -u').to_i
46
52
  suffix = executor.exec('date \'+%Y%m%d%H%M\'') # use remote - not local - time machine
47
53
  executor.exec("[ -e \"#{remote_filename}\" ] && [ ! -h \"#{remote_filename}\" ] && #{sudo} mv -i \"#{remote_filename}\" \"#{remote_filename}.pre\"")
48
- executor.exec("#{sudo} tee #{remote_filename}.#{suffix} > /dev/null", render(env, executor))
54
+ executor.exec("#{sudo} tee #{remote_filename}.#{suffix} > /dev/null", stdin: render(env, executor))
49
55
  executor.exec("#{sudo} ln -snf \"#{remote_filename}.#{suffix}\" \"#{remote_filename}\"")
56
+ shell = executor.exec('ps -p $$ -ocomm=')
57
+ env = { 'INSTALL_FILE' => remote_filename }
58
+ env_string = env.inject([]) { |a, b| a << b.map(&:shellescape).join('=') }.join(' ')
59
+ @postcmds.each do |cmd|
60
+ executor.exec("#{sudo} #{'env' if shell =~ /csh\z/} #{env_string} sh -c '#{cmd}'") # TODO: escape single quotes in cmd?
61
+ end
50
62
  executor.close
51
63
  end
52
64
 
@@ -94,15 +106,7 @@ module Cindy
94
106
  private
95
107
 
96
108
  def executor_for_env(env)
97
- uri = URI.parse(env.uri)
98
- case uri.scheme
99
- when nil, 'file'
100
- Executor::Local.new
101
- when 'ssh'
102
- Executor::SSH.new Net::SSH.start(uri.host, uri.user)
103
- else
104
- raise Exception.new 'Unexpected protocol'
105
- end
109
+ Executor::Base.from_uri env.uri, @owner.logger
106
110
  end
107
111
 
108
112
  def render(env, executor = nil)
@@ -1,3 +1,3 @@
1
1
  module Cindy
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.1"
3
3
  end
File without changes
@@ -1,9 +1,20 @@
1
+ require 'logger'
1
2
  require 'minitest_helper'
2
3
 
3
4
  class CindyTest < Minitest::Test
4
5
 
5
- def test_true
6
- assert true
6
+ def test_deploy
7
+ cindy = Cindy::Cindy.from_string <<-EOS
8
+ environment :foo, 'file:///'
9
+
10
+ template :bar, '#{File.join(__dir__, 'templates', 'deploy.tpl')}' do
11
+ on :foo, '/tmp/#{$$}'
12
+ end
13
+ EOS
14
+ cindy.logger = Logger.new File.open('/dev/null', File::WRONLY)
15
+
16
+ cindy.template_environment_deploy :foo, :bar
17
+ assert File.symlink?("/tmp/#{$$}")
7
18
  end
8
19
 
9
20
  end
@@ -0,0 +1,27 @@
1
+ require 'logger'
2
+ require 'minitest_helper'
3
+
4
+ class LocalExecutorTest < Minitest::Test
5
+
6
+ def test_local_executor
7
+ logger = Logger.new File.open('/dev/null', File::WRONLY)
8
+ locexec = Cindy::Executor::Local.new nil, logger
9
+ # unknown command
10
+ assert_raises Cindy::Executor::CommandFailedError do
11
+ locexec.exec "eko foo"
12
+ end
13
+ assert_equal '', locexec.exec("eko foo", ignore_failure: true)
14
+ assert !locexec.exec("eko foo", ignore_failure: true, check_status_only: true)
15
+ # command failure
16
+ assert_raises Cindy::Executor::CommandFailedError do
17
+ locexec.exec 'grep -q foo', stdin: 'bar'
18
+ end
19
+ assert_equal '', locexec.exec("grep -q foo", stdin: 'bar', ignore_failure: true)
20
+ assert !locexec.exec("grep -q foo", stdin: 'bar', ignore_failure: true, check_status_only: true)
21
+ # valid command
22
+ assert_equal 'foo', locexec.exec('echo foo')
23
+ assert_equal 'foo', locexec.exec('echo foo', ignore_failure: true)
24
+ assert locexec.exec('echo foo', ignore_failure: true, check_status_only: true)
25
+ end
26
+
27
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cindy-cm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - julp
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-03-10 00:00:00.000000000 Z
11
+ date: 2015-09-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: net-ssh
@@ -84,14 +84,17 @@ files:
84
84
  - lib/cindy/cli.rb
85
85
  - lib/cindy/command.rb
86
86
  - lib/cindy/environment.rb
87
+ - lib/cindy/executor/base.rb
87
88
  - lib/cindy/executor/local.rb
88
89
  - lib/cindy/executor/ssh.rb
89
90
  - lib/cindy/template.rb
90
91
  - lib/cindy/variable.rb
91
92
  - lib/cindy/version.rb
92
93
  - test/minitest_helper.rb
94
+ - test/templates/deploy.tpl
93
95
  - test/test_cindy.rb
94
96
  - test/test_dsl.rb
97
+ - test/test_local_executor.rb
95
98
  homepage: https://github.com/julp/cindy
96
99
  licenses:
97
100
  - BSD
@@ -112,7 +115,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
112
115
  version: '0'
113
116
  requirements: []
114
117
  rubyforge_project:
115
- rubygems_version: 2.4.6
118
+ rubygems_version: 2.4.8
116
119
  signing_key:
117
120
  specification_version: 4
118
121
  summary: Turn out your configuration files into ERB templates and deploy them