Class: ConfCtl::Cli::Cluster

Inherits:
Command
  • Object
show all
Defined in:
lib/confctl/cli/cluster.rb

Instance Attribute Summary

Attributes inherited from Command

#args, #gopts, #opts

Instance Method Summary collapse

Methods inherited from Command

#ask_action, #ask_confirmation, #ask_confirmation!, #initialize, #require_args!, run, #run_method, #use_color?, #use_pager?

Constructor Details

This class inherits a constructor from ConfCtl::Cli::Command

Instance Method Details

#buildObject



32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/confctl/cli/cluster.rb', line 32

def build
  machines = select_machines(args[0]).managed

  raise 'No machines to build' if machines.empty?

  ask_confirmation! do
    puts 'The following machines will be built:'
    list_machines(machines)
  end

  do_build(machines)
end

#changelogObject



219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
# File 'lib/confctl/cli/cluster.rb', line 219

def changelog
  compare_swpins do |io, _host, status, sw_name, spec|
    s = spec.string_changelog_info(
      opts[:downgrade] ? :downgrade : :upgrade,
      status.swpins_info[sw_name],
      color: use_color?,
      verbose: opts[:verbose],
      patch: opts[:patch]
    )
  rescue ConfCtl::Error => e
    io.puts e.message
  else
    io.puts(s || 'no changes')
  end
end

#csshObject



300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
# File 'lib/confctl/cli/cluster.rb', line 300

def cssh
  machines = select_machines_with_managed(args[0]).runnable
  raise 'No machines to open cssh to' if machines.empty?

  ask_confirmation! do
    puts 'Open cssh to the following machines:'
    list_machines(machines)
  end

  nix = ConfCtl::Nix.new

  cssh = [
    'cssh',
    '-l', 'root'
  ]

  machines.each_value do |machine|
    cssh << machine.target_host
  end

  nix.run_command_in_shell(
    packages: ['perlPackages.AppClusterSSH'],
    command: cssh.join(' ')
  )
end

#deployObject

Raises:

  • (GLI::BadCommandLine)


45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/confctl/cli/cluster.rb', line 45

def deploy
  machines = select_machines(args[0]).managed
  action = args[1] || 'switch'

  raise GLI::BadCommandLine, "invalid action '#{action}'" unless %w[boot switch test dry-activate].include?(action)

  if opts[:reboot]
    raise GLI::BadCommandLine, '--reboot can be used only with switch-action boot' if action != 'boot'

    parse_wait_online
  end

  raise 'No machines to deploy' if machines.empty?

  ask_confirmation! do
    puts 'The following machines will be deployed:'
    list_machines(machines)
    puts
    puts "Generation: #{opts[:generation] || 'new build'}"
    puts "Target action: #{action}#{opts[:reboot] ? ' + reboot' : ''}"
  end

  ConfCtl::Hook.call(:cluster_deploy, kwargs: {
    machines:,
    generation: opts[:generation],
    action: opts[:action],
    opts:
  })

  host_generations =
    if opts[:generation]
      find_generations(machines, opts[:generation])
    else
      do_build(machines)
    end

  nix = ConfCtl::Nix.new(show_trace: opts['show-trace'])

  if opts['one-by-one']
    deploy_one_by_one(machines, host_generations, nix, action)
  else
    deploy_in_bulk(machines, host_generations, nix, action)
  end
end

#diffObject



235
236
237
238
239
240
241
242
243
244
245
246
247
# File 'lib/confctl/cli/cluster.rb', line 235

def diff
  compare_swpins do |io, _host, status, sw_name, spec|
    s = spec.string_diff_info(
      opts[:downgrade] ? :downgrade : :upgrade,
      status.swpins_info[sw_name],
      color: use_color?
    )
  rescue ConfCtl::Error => e
    io.puts e.message
  else
    io.puts(s || 'no changes')
  end
end

#health_checkObject



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/confctl/cli/cluster.rb', line 90

def health_check
  machines = select_machines(args[0]).managed.select do |_host, machine|
    machine.health_checks.any?
  end
  raise 'No machines to check or no health checks configured' if machines.empty?

  run_checks = machines.health_checks

  ask_confirmation! do
    puts 'Health checks will be run on the following machines:'

    list_machines(machines, prepend_cols: %w[checks])
    puts
    puts "#{run_checks.length} checks in total"
    puts
  end

  return unless run_health_checks(machines, run_checks).any?

  raise 'Health checks failed'
end

#listObject



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/confctl/cli/cluster.rb', line 13

def list
  if opts[:list]
    prefix = 'cluster.<name>.'
    nix = ConfCtl::Nix.new

    puts 'name'

    nix.module_options.each do |opt|
      next unless opt['name'].start_with?(prefix)

      puts opt['name'][prefix.length..]
    end

    return
  end

  list_machines(select_machines_with_managed(args[0]))
end

#sshObject



281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
# File 'lib/confctl/cli/cluster.rb', line 281

def ssh
  machines = select_machines_with_managed(args[0]).runnable
  raise 'No machines to ssh to' if machines.empty?

  if opts['input-string'] && opts['input-file']
    raise GLI::BadCommandLine, 'use one of --input-string or --input-file'
  end

  if args.length == 1
    raise GLI::BadCommandLine, 'missing command' unless machines.length == 1

    run_ssh_interactive(machines)
    return

  end

  run_ssh_command(machines, args[1..])
end

#statusObject



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
# File 'lib/confctl/cli/cluster.rb', line 112

def status
  machines = select_machines(args[0]).managed.select do |_host, machine|
    machine.target_host || machine.carried?
  end

  raise 'No machines to check' if machines.empty?

  ask_confirmation! do
    if opts[:generation]
      puts 'The following machines will be checked:'
    else
      puts 'The following machines will be built and then checked:'
    end

    list_machines(machines)
    puts
    puts "Generation: #{opts[:generation] || 'new build'}"
  end

  statuses = machines.transform_values do |machine|
    ConfCtl::MachineStatus.new(machine)
  end

  # Evaluate toplevels
  if opts[:generation] == 'none'
    host_generations = nil
  elsif opts[:generation]
    host_generations = find_generations(machines, opts[:generation])

    # Ignore statuses when no generation was found
    statuses.delete_if do |host, _st|
      !host_generations.has_key?(host)
    end
  else
    host_generations = do_build(machines)
    puts
  end

  # Assign configured toplevel and swpins
  if host_generations
    host_generations.each do |host, gen|
      statuses[host].target_toplevel = gen.toplevel
      statuses[host].target_swpin_specs = gen.swpin_specs
    end
  else
    # We're not comparing a system generation, only configured swpins
    ConfCtl::Swpins::ClusterNameList.new(machines:).each do |cn|
      cn.parse

      statuses[cn.name].target_swpin_specs = cn.specs
    end
  end

  # Check runtime status
  tw = ConfCtl::ParallelExecutor.new(machines.length)

  statuses.each_value do |st|
    tw.add do
      st.query(toplevel: opts[:generation] != 'none')
    end
  end

  tw.run

  # Collect all swpins
  swpins = []

  statuses.each_value do |st|
    st.target_swpin_specs.each_key do |name|
      swpins << name unless swpins.include?(name)
    end

    st.evaluate
  end

  # Render results
  cols = %w[host online uptime status generations] + swpins
  rows = []

  statuses.each do |host, st|
    build_generations = ConfCtl::Generation::BuildList.new(host)

    row = {
      'host' => host,
      'online' => st.online? && Rainbow('yes').green,
      'uptime' => st.uptime && format_duration(st.uptime),
      'status' => st.status ? Rainbow('ok').green : Rainbow('outdated').red,
      'generations' => "#{build_generations.count}:#{st.generations && st.generations.count}"
    }

    swpins.each do |name|
      swpin_state = st.swpins_state[name]

      row[name] =
        if swpin_state
          Rainbow(swpin_state.current_version).color(
            swpin_state.uptodate? ? :green : :red
          )
        end
    end

    rows << row
  end

  OutputFormatter.print(rows, cols, layout: :columns, color: use_color?)
end

#test_connectionObject



249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
# File 'lib/confctl/cli/cluster.rb', line 249

def test_connection
  machines = select_machines_with_managed(args[0]).runnable
  raise 'No machines to test' if machines.empty?

  ask_confirmation! do
    puts 'Test SSH connection to the following machines:'
    list_machines(machines)
  end

  succeeded = []
  failed = []

  machines.each do |host, machine|
    mc = ConfCtl::MachineControl.new(machine)

    begin
      mc.test_connection
      succeeded << host
    rescue TTY::Command::ExitError => e
      puts "Unable to connect to #{host}: #{e.message}"
      puts
      failed << host
    end
  end

  puts
  puts "Result: #{succeeded.length} successful, #{failed.length} failed"
  puts
  puts 'Failed machines:'
  failed.each { |host| puts "  #{host}" }
end