qb 0.3.4 → 0.3.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,8 +1,33 @@
1
+ # Requirements
2
+ # =======================================================================
3
+
4
+ # stdlib
5
+
6
+ # deps
7
+
8
+ # package
9
+ require_relative './cli/help'
10
+ require_relative './cli/play'
11
+ require_relative './cli/run'
12
+ require_relative './cli/setup'
13
+
14
+
15
+ # Requirements
16
+ # =======================================================================
17
+
1
18
  require 'nrser/refinements'
2
19
  using NRSER
3
20
 
4
21
 
22
+ # Declarations
23
+ # =======================================================================
24
+
5
25
  module QB; end
26
+
27
+
28
+ # Definitions
29
+ # =======================================================================
30
+
6
31
  module QB::CLI
7
32
 
8
33
  # Eigenclass (Singleton Class)
@@ -102,6 +127,4 @@ module QB::CLI
102
127
 
103
128
  end # class << self (Eigenclass)
104
129
 
105
- end
106
-
107
- require_relative './cli/play'
130
+ end # module QB::CLI
@@ -0,0 +1,56 @@
1
+ # Requirements
2
+ # =====================================================================
3
+
4
+ # package
5
+
6
+
7
+ # Declarations
8
+ # =======================================================================
9
+
10
+ module QB; end
11
+
12
+
13
+ # Definitions
14
+ # =======================================================================
15
+
16
+ module QB::CLI
17
+
18
+ # Show the help message.
19
+ #
20
+ # @todo
21
+ # We should have more types of help.
22
+ #
23
+ # @return [1]
24
+ # Error exit status - we don't want `qb ... && ...` to move on to the
25
+ # second command when we end up falling back to `help`.
26
+ #
27
+ def self.help
28
+ metadata = if QB.gemspec.metadata && !QB.gemspec.metadata.empty?
29
+ "metadata:\n" + QB.gemspec.metadata.map {|key, value|
30
+ " #{ key }: #{ value }"
31
+ }.join("\n")
32
+ end
33
+
34
+ puts <<-END
35
+ version: #{ QB::VERSION }
36
+
37
+ #{ metadata }
38
+
39
+ syntax:
40
+
41
+ qb ROLE [OPTIONS] DIRECTORY
42
+
43
+ use `qb ROLE -h` for role options.
44
+
45
+ available roles:
46
+
47
+ END
48
+ puts QB::Role.available
49
+ puts
50
+
51
+ return 1
52
+ end # .help
53
+
54
+ end # module QB::CLI
55
+
56
+
@@ -5,19 +5,25 @@
5
5
  require 'qb/ansible/cmds/playbook'
6
6
 
7
7
 
8
+ # Declarations
9
+ # =======================================================================
10
+
8
11
  module QB; end
9
12
 
10
- # @todo document QB::CLI module.
13
+
14
+ # Definitions
15
+ # =======================================================================
16
+
11
17
  module QB::CLI
12
18
 
13
19
  # Play an Ansible playbook (like `state.yml`) in the QB environment
14
20
  # (sets up path env vars, IO streams, etc.).
15
21
  #
16
- # @param [type] arg_name
17
- # @todo Add name param description.
22
+ # @param [Array<String>] args
23
+ # CLI arguments to use.
18
24
  #
19
- # @return [return_type]
20
- # @todo Document return value.
25
+ # @return [Fixnum]
26
+ # The `ansible-playbook` command exit code.
21
27
  #
22
28
  def self.play args
23
29
  if args.empty?
@@ -27,7 +33,7 @@ module QB::CLI
27
33
  playbook_path = QB::Util.resolve args[0]
28
34
 
29
35
  unless playbook_path.file?
30
- raise "Can't find Ansible playbook at #{ path.to_s }"
36
+ raise "Can't find Ansible playbook at `#{ playbook_path.to_s }`"
31
37
  end
32
38
 
33
39
  # By default, we won't change directories to run the command.
@@ -49,11 +55,17 @@ module QB::CLI
49
55
  end
50
56
 
51
57
  cmd = QB::Ansible::Cmds::Playbook.new \
52
- playbook_path: playbook_path,
53
- chdir: chdir
58
+ chdir: chdir,
59
+ playbook_path: playbook_path
60
+
61
+ status = cmd.stream
62
+
63
+ if status != 0
64
+ $stderr.puts "ERROR ansible-playbook failed."
65
+ end
66
+
67
+ exit status
54
68
 
55
69
  end # .play
56
70
 
57
71
  end # module QB::CLI
58
-
59
-
@@ -0,0 +1,291 @@
1
+ # Requirements
2
+ # =====================================================================
3
+
4
+ # package
5
+ require 'qb/cli/help'
6
+
7
+
8
+ # Declarations
9
+ # =======================================================================
10
+
11
+ module QB; end
12
+
13
+
14
+ # Definitions
15
+ # =======================================================================
16
+
17
+ module QB::CLI
18
+
19
+ # Run a QB role.
20
+ #
21
+ # @param [Array<String>] args
22
+ # CLI args to work with.
23
+ #
24
+ # @return [Fixnum]
25
+ # Exit status code from `ansible-playbook` command, unless we invoked
26
+ # help or error'd out in another way before the run (in which case `1`
27
+ # is returned).
28
+ #
29
+ def self.run args
30
+ role_arg = args.shift
31
+ QB.debug "role arg", role_arg
32
+
33
+ begin
34
+ role = QB::Role.require role_arg
35
+ rescue QB::Role::NoMatchesError => e
36
+ puts "ERROR - #{ e.message }\n\n"
37
+ # exits with status code 1
38
+ return help
39
+ rescue QB::Role::MultipleMatchesError => e
40
+ puts "ERROR - #{ e.message }\n\n"
41
+ return 1
42
+ end
43
+
44
+ QB.check_qb_version role
45
+
46
+ options = QB::Options.new role, args
47
+
48
+ QB.debug "role options set on cli", options.role_options.select {|k, o|
49
+ !o.value.nil?
50
+ }
51
+
52
+ QB.debug "qb options", options.qb
53
+
54
+ cwd = Dir.getwd
55
+
56
+ dir = nil
57
+
58
+ if role.uses_default_dir?
59
+ # get the target dir
60
+ dir = case args.length
61
+ when 0
62
+ # in this case, a dir has not been provided
63
+ #
64
+ # in some cases (like projects) the dir can be figured out in other ways:
65
+ #
66
+
67
+ if options.ask?
68
+ default = begin
69
+ role.default_dir cwd, options.role_options
70
+ rescue QB::UserInputError => e
71
+ NRSER::NO_ARG
72
+ end
73
+
74
+ QB::CLI.ask name: "target directory (`qb_dir`)",
75
+ type: t.non_empty_str,
76
+ default: default
77
+
78
+ else
79
+ role.default_dir cwd, options.role_options
80
+ end
81
+
82
+ when 1
83
+ # there is a single positional arg, which is used as dir
84
+ args[0]
85
+
86
+ else
87
+ # there are multiple positional args, which is not allowed
88
+ raise "can't supply more than one argument: #{ args.inspect }"
89
+
90
+ end
91
+
92
+ QB.debug "input_dir", dir
93
+
94
+ # normalize to expanded path (has no trailing slash)
95
+ dir = File.expand_path dir
96
+
97
+ QB.debug "normalized_dir", dir
98
+
99
+ # create the dir if it doesn't exist (so don't have to cover this in
100
+ # every role)
101
+ if role.mkdir
102
+ FileUtils.mkdir_p dir unless File.exists? dir
103
+ end
104
+
105
+ saved_options_path = Pathname.new(dir) + '.qb-options.yml'
106
+
107
+ saved_options = if saved_options_path.exist?
108
+ # convert old _ separated names to - separated
109
+ YAML.load(saved_options_path.read).map {|role_options_key, role_options|
110
+ [
111
+ role_options_key,
112
+ role_options.map {|name, value|
113
+ [QB::Options.cli_ize_name(name), value]
114
+ }.to_h
115
+ ]
116
+ }.to_h.tap {|saved_options|
117
+ QB.debug "found saved options", saved_options
118
+ }
119
+ else
120
+ QB.debug "no saved options"
121
+ {}
122
+ end
123
+
124
+ if saved_options.key? role.options_key
125
+ role_saved_options = saved_options[role.options_key]
126
+
127
+ QB.debug "found saved options for role", role_saved_options
128
+
129
+ role_saved_options.each do |option_cli_name, value|
130
+ option = options.role_options[option_cli_name]
131
+
132
+ if option.value.nil?
133
+ QB.debug "setting from saved options", option: option, value: value
134
+
135
+ option.value = value
136
+ end
137
+ end
138
+ end
139
+ end # unless default_dir == false
140
+
141
+
142
+ # Interactive Input
143
+ # =====================================================================
144
+
145
+ if options.ask?
146
+ # Incomplete
147
+ raise "COMING SOON!!!...?"
148
+ QB::CLI.ask_for_options role: role, options: options
149
+ end
150
+
151
+
152
+ # Validation
153
+ # =====================================================================
154
+ #
155
+ # Should have already been taken care of if we used interactive input.
156
+ #
157
+
158
+ # check that required options are present
159
+ missing = options.role_options.values.select {|option|
160
+ option.required? && option.value.nil?
161
+ }
162
+
163
+ unless missing.empty?
164
+ puts "ERROR: options #{ missing.map {|o| o.cli_name } } are required."
165
+ return 1
166
+ end
167
+
168
+ set_options = options.role_options.select {|k, o| !o.value.nil?}
169
+
170
+ QB.debug "set options", set_options
171
+
172
+ playbook_role = {'role' => role.name}
173
+
174
+ playbook_vars = {
175
+ 'qb_dir' => dir,
176
+ # depreciated due to mass potential for conflict
177
+ 'dir' => dir,
178
+ 'qb_cwd' => cwd,
179
+ 'qb_user_roles_dir' => QB::USER_ROLES_DIR.to_s,
180
+ }
181
+
182
+ set_options.values.each do |option|
183
+ playbook_role[option.var_name] = option.value
184
+ end
185
+
186
+ play =
187
+ {
188
+ 'hosts' => options.qb['hosts'],
189
+ 'vars' => playbook_vars,
190
+ # 'gather_subset' => ['!all'],
191
+ 'gather_facts' => options.qb['facts'],
192
+ 'pre_tasks' => [
193
+ {
194
+ 'qb_facts' => {
195
+ 'qb_dir' => dir,
196
+ }
197
+ },
198
+ ],
199
+ 'roles' => [
200
+ 'nrser.blockinfile',
201
+ playbook_role
202
+ ],
203
+ }
204
+
205
+ if options.qb['user']
206
+ play['become'] = true
207
+ play['become_user'] = options.qb['user']
208
+ end
209
+
210
+ playbook = [play]
211
+
212
+ QB.debug "playbook", playbook
213
+
214
+ env = QB::Ansible::Env.new
215
+
216
+ # stick the role path in front to make sure we get **that** role
217
+ env.roles_path.unshift role.path.expand_path.dirname
218
+
219
+ cmd = QB::Ansible::Cmds::Playbook.new \
220
+ env: env,
221
+ playbook: playbook,
222
+ role_options: options,
223
+ chdir: (File.exists?('./ansible/ansible.cfg') ? './ansible' : nil)
224
+
225
+ # print
226
+ # =====
227
+ #
228
+ # print useful stuff for debugging / running outside of qb
229
+ #
230
+
231
+ if options.qb['print'].include? 'options'
232
+ puts "SET OPTIONS:\n\n#{ YAML.dump set_options }\n\n"
233
+ end
234
+
235
+ if options.qb['print'].include? 'env'
236
+ puts "ENV:\n\n#{ YAML.dump cmd.env.to_h }\n\n"
237
+ end
238
+
239
+ if options.qb['print'].include? 'cmd'
240
+ puts "COMMAND:\n\n#{ cmd.prepare }\n\n"
241
+ end
242
+
243
+ if options.qb['print'].include? 'playbook'
244
+ puts "PLAYBOOK:\n\n#{ YAML.dump playbook }\n\n"
245
+ end
246
+
247
+ # stop here if we're not supposed to run
248
+ exit 0 if !options.qb['run']
249
+
250
+ # run
251
+ # ===
252
+ #
253
+ # stuff below here does stuff
254
+ #
255
+
256
+ # save the options back
257
+ if (
258
+ dir &&
259
+ # we set some options that we can save
260
+ set_options.values.select {|o| o.save? }.length > 0 &&
261
+ # the role says to save options
262
+ role.save_options
263
+ )
264
+ saved_options[role.options_key] = set_options.select{|key, option|
265
+ option.save?
266
+ }.map {|key, option|
267
+ [key, option.value]
268
+ }.to_h
269
+
270
+ unless saved_options_path.dirname.exist?
271
+ FileUtils.mkdir_p saved_options_path.dirname
272
+ end
273
+
274
+ saved_options_path.open('w') do |f|
275
+ f.write YAML.dump(saved_options)
276
+ end
277
+ end
278
+
279
+ status = cmd.stream
280
+
281
+ if status != 0
282
+ $stderr.puts "ERROR ansible-playbook failed."
283
+ end
284
+
285
+ # exit status
286
+ status
287
+ end # .run
288
+
289
+ end # module QB::CLI
290
+
291
+