qb 0.3.4 → 0.3.5

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