uffizzi-cli 2.2.0 → 2.2.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c57cec1f82e6f54de217f0d1e17b88a8ce5bb2dd58eb702df2a10930508f1203
4
- data.tar.gz: b904d36aef21a479dac014429f09eebd80cb1715dfd71cf2457fea34cb5f5175
3
+ metadata.gz: d24e9582d66d329f45ab42194dfefbf1558843fbc94578dfc8b2127f0d7f3122
4
+ data.tar.gz: fa901b1e7257fc8c9384bcf11c4f41490f767dabbecbf039056aa8b0ed488f38
5
5
  SHA512:
6
- metadata.gz: 9d5f9f27fab4efec5b44910d37e53ce60b65ba6995e89a5e65b1a7d60c0925899f7cce271c2670d728aeb66845da3c075cab9492baf1f2c45ac3d80504ae2b4f
7
- data.tar.gz: c3b2a87f53468a55939cde535cf0389eaca7b2666b581312089ee0c51eed195a58366c7c763b16fad8da53ffe8ca9ba92b4cd4bfea88ccffc09397b4d6f39204
6
+ metadata.gz: cda766f7ab21158e56cfb5e5ea3dabd0416de0fd1d1671c268347465db66ce4a3864f3aa51be04a6fd789848fd6d86cbf2c3e62437aaaf63803f633b5ababc2e
7
+ data.tar.gz: d6e79d2994cc487a7986315371715e15a85d5d42659a4ea21361ac7e289219441ba6ee5e77468dd5380e1ee5a8b9214fed0a4894bb2b7c4f26353647c3954fcc
@@ -16,7 +16,7 @@ module Uffizzi
16
16
  Uffizzi::Token.delete if Uffizzi::Token.exists?
17
17
  end
18
18
 
19
- def check_login(project_option)
19
+ def check_login(project_option = nil)
20
20
  raise Uffizzi::Error.new('You are not logged in. Run `uffizzi login`.') unless signed_in?
21
21
  raise Uffizzi::Error.new('This command needs project to be set in config file') unless project_set?(project_option)
22
22
  end
@@ -76,19 +76,18 @@ module Uffizzi
76
76
  def run(command, command_args = {})
77
77
  Uffizzi.ui.output_format = options[:output]
78
78
  Uffizzi::AuthHelper.check_login(options[:project])
79
- project_slug = options[:project].nil? ? ConfigFile.read_option(:project) : options[:project]
80
79
 
81
80
  case command
82
81
  when 'list'
83
- handle_list_command(project_slug)
82
+ handle_list_command
84
83
  when 'create'
85
- handle_create_command(project_slug, command_args)
84
+ handle_create_command(command_args)
86
85
  when 'describe'
87
- handle_describe_command(project_slug, command_args)
86
+ handle_describe_command(command_args)
88
87
  when 'delete'
89
- handle_delete_command(project_slug, command_args)
88
+ handle_delete_command(command_args)
90
89
  when 'update-kubeconfig'
91
- handle_update_kubeconfig_command(project_slug, command_args)
90
+ handle_update_kubeconfig_command(command_args)
92
91
  when 'disconnect'
93
92
  ClusterDisconnectService.handle(options)
94
93
  when 'sleep'
@@ -98,13 +97,12 @@ module Uffizzi
98
97
  end
99
98
  end
100
99
 
101
- def handle_list_command(project_slug)
100
+ def handle_list_command
102
101
  is_all = options[:all]
103
102
  response = if is_all
104
- get_account_clusters(ConfigFile.read_option(:server), ConfigFile.read_option(:account, :id))
103
+ get_account_clusters(server, ConfigFile.read_option(:account, :id))
105
104
  else
106
- oidc_token = ConfigFile.read_option(:oidc_token)
107
- get_project_clusters(ConfigFile.read_option(:server), project_slug, oidc_token: oidc_token)
105
+ get_project_clusters(server, project_slug, oidc_token: oidc_token)
108
106
  end
109
107
 
110
108
  if ResponseHelper.ok?(response)
@@ -115,7 +113,7 @@ module Uffizzi
115
113
  end
116
114
 
117
115
  # rubocop:disable Metrics/PerceivedComplexity
118
- def handle_create_command(project_slug, command_args)
116
+ def handle_create_command(command_args)
119
117
  Uffizzi.ui.disable_stdout if Uffizzi.ui.output_format
120
118
 
121
119
  if options[:name]
@@ -125,23 +123,20 @@ module Uffizzi
125
123
  end
126
124
 
127
125
  cluster_name = command_args[:name] || options[:name] || ClusterService.generate_name
128
- creation_source = options[:"creation-source"] || ClusterService::MANUAL_CREATION_SOURCE
129
- k8s_version = options[:"k8s-version"]
130
126
  Uffizzi.ui.say_error_and_exit("Cluster name: #{cluster_name} is not valid.") unless ClusterService.valid_name?(cluster_name)
131
127
 
132
- params = cluster_creation_params(
133
- name: cluster_name,
134
- creation_source: creation_source,
135
- manifest_file_path: options[:manifest],
136
- k8s_version: k8s_version,
137
- )
138
- response = create_cluster(ConfigFile.read_option(:server), project_slug, params)
128
+ unless ClusterService.valid_name?(cluster_name)
129
+ Uffizzi.ui.say_error_and_exit("Cluster name: #{cluster_name} is not valid.")
130
+ end
131
+
132
+ params = cluster_creation_params(cluster_name)
133
+ response = create_cluster(server, project_slug, params)
139
134
 
140
135
  return ResponseHelper.handle_failed_response(response) unless ResponseHelper.created?(response)
141
136
 
142
137
  spinner = TTY::Spinner.new("[:spinner] Creating cluster #{cluster_name}...", format: :dots)
143
138
  spinner.auto_spin
144
- cluster_data = ClusterService.wait_cluster_deploy(project_slug, cluster_name, ConfigFile.read_option(:oidc_token))
139
+ cluster_data = ClusterService.wait_cluster_deploy(project_slug, cluster_name, oidc_token)
145
140
 
146
141
  if ClusterService.failed?(cluster_data[:state])
147
142
  spinner.error
@@ -151,26 +146,28 @@ module Uffizzi
151
146
  spinner.success
152
147
  handle_succeed_create_response(cluster_data)
153
148
  rescue SystemExit, Interrupt, SocketError
154
- handle_interrupt_creation(cluster_name, ConfigFile.read_option(:server), project_slug)
149
+ handle_interrupt_creation(cluster_name)
155
150
  end
156
151
  # rubocop:enable Metrics/PerceivedComplexity
157
152
 
158
- def handle_describe_command(project_slug, command_args)
159
- cluster_data = fetch_cluster_data(project_slug, command_args[:cluster_name])
153
+ def handle_describe_command(command_args)
154
+ cluster_data = ClusterService.fetch_cluster_data(command_args[:cluster_name], **cluster_api_connection_params)
155
+ render_data = ClusterService.build_render_data(cluster_data)
160
156
 
161
- handle_succeed_describe(cluster_data)
157
+ Uffizzi.ui.output_format = Uffizzi::UI::Shell::PRETTY_LIST
158
+ Uffizzi.ui.say(render_data)
162
159
  end
163
160
 
164
- def handle_delete_command(project_slug, command_args)
161
+ def handle_delete_command(command_args)
165
162
  cluster_name = command_args[:cluster_name]
166
163
  is_delete_kubeconfig = options[:'delete-config']
167
164
 
168
- return handle_delete_cluster(project_slug, cluster_name) unless is_delete_kubeconfig
165
+ return handle_delete_cluster(cluster_name) unless is_delete_kubeconfig
169
166
 
170
- cluster_data = fetch_cluster_data(project_slug, cluster_name)
167
+ cluster_data = ClusterService.fetch_cluster_data(cluster_name, **cluster_api_connection_params)
171
168
  kubeconfig = parse_kubeconfig(cluster_data[:kubeconfig])
172
169
 
173
- handle_delete_cluster(project_slug, cluster_name)
170
+ handle_delete_cluster(cluster_name)
174
171
  exclude_kubeconfig(cluster_data[:id], kubeconfig) if kubeconfig.present?
175
172
  end
176
173
 
@@ -194,12 +191,12 @@ module Uffizzi
194
191
  end
195
192
  end
196
193
 
197
- def handle_delete_cluster(project_slug, cluster_name)
194
+ def handle_delete_cluster(cluster_name)
198
195
  params = {
199
196
  cluster_name: cluster_name,
200
- oidc_token: ConfigFile.read_option(:oidc_token),
197
+ oidc_token: oidc_token,
201
198
  }
202
- response = delete_cluster(ConfigFile.read_option(:server), project_slug, params)
199
+ response = delete_cluster(server, project_slug, params)
203
200
 
204
201
  if ResponseHelper.no_content?(response)
205
202
  Uffizzi.ui.say("Cluster #{cluster_name} deleted")
@@ -208,10 +205,10 @@ module Uffizzi
208
205
  end
209
206
  end
210
207
 
211
- def handle_update_kubeconfig_command(project_slug, command_args)
208
+ def handle_update_kubeconfig_command(command_args)
212
209
  kubeconfig_path = options[:kubeconfig] || KubeconfigService.default_path
213
210
  cluster_name = command_args[:cluster_name]
214
- cluster_data = fetch_cluster_data(project_slug, cluster_name)
211
+ cluster_data = ClusterService.fetch_cluster_data(cluster_name, **cluster_api_connection_params)
215
212
 
216
213
  unless cluster_data[:kubeconfig].present?
217
214
  say_error_update_kubeconfig(cluster_data)
@@ -289,13 +286,15 @@ module Uffizzi
289
286
  end
290
287
  end
291
288
 
292
- def cluster_creation_params(name:, creation_source:, manifest_file_path:, k8s_version:)
289
+ def cluster_creation_params(cluster_name)
290
+ creation_source = options[:"creation-source"] || ClusterService::MANUAL_CREATION_SOURCE
291
+ manifest_file_path = options[:manifest]
292
+ k8s_version = options[:"k8s-version"]
293
293
  manifest_content = load_manifest_file(manifest_file_path)
294
- oidc_token = Uffizzi::ConfigFile.read_option(:oidc_token)
295
294
 
296
295
  {
297
296
  cluster: {
298
- name: name,
297
+ name: cluster_name,
299
298
  manifest: manifest_content,
300
299
  creation_source: creation_source,
301
300
  k8s_version: k8s_version,
@@ -312,7 +311,7 @@ module Uffizzi
312
311
  raise Uffizzi::Error.new(e.message)
313
312
  end
314
313
 
315
- def handle_interrupt_creation(cluster_name, server, project_slug)
314
+ def handle_interrupt_creation(cluster_name)
316
315
  deletion_response = delete_cluster(server, project_slug, cluster_name: cluster_name)
317
316
  deletion_message = if ResponseHelper.no_content?(deletion_response)
318
317
  "The cluster #{cluster_name} has been disabled."
@@ -348,24 +347,6 @@ module Uffizzi
348
347
  end.join("\n")
349
348
  end
350
349
 
351
- def handle_succeed_describe(cluster_data)
352
- prepared_cluster_data = {
353
- name: cluster_data[:name],
354
- status: cluster_data[:state],
355
- created: Time.strptime(cluster_data[:created_at], '%Y-%m-%dT%H:%M:%S.%N').strftime('%a %b %d %H:%M:%S %Y'),
356
- url: cluster_data[:host],
357
- k8s_version: cluster_data[:k8s_version],
358
- }
359
-
360
- rendered_cluster_data = if Uffizzi.ui.output_format.nil?
361
- prepared_cluster_data.map { |k, v| "- #{k.to_s.upcase}: #{v}" }.join("\n").strip
362
- else
363
- prepared_cluster_data
364
- end
365
-
366
- Uffizzi.ui.say(rendered_cluster_data)
367
- end
368
-
369
350
  def handle_succeed_create_response(cluster_data)
370
351
  kubeconfig_path = options[:kubeconfig] || KubeconfigService.default_path
371
352
  is_update_current_context = options[:'update-current-context']
@@ -432,20 +413,6 @@ module Uffizzi
432
413
  Psych.safe_load(Base64.decode64(kubeconfig))
433
414
  end
434
415
 
435
- def fetch_cluster_data(project_slug, cluster_name)
436
- params = {
437
- cluster_name: cluster_name,
438
- oidc_token: ConfigFile.read_option(:oidc_token),
439
- }
440
- response = get_cluster(ConfigFile.read_option(:server), project_slug, params)
441
-
442
- if ResponseHelper.ok?(response)
443
- response.dig(:body, :cluster)
444
- else
445
- ResponseHelper.handle_failed_response(response)
446
- end
447
- end
448
-
449
416
  def save_previous_current_context(kubeconfig_path, current_context)
450
417
  return if kubeconfig_path.nil? || ConfigHelper.previous_current_context_by_path(kubeconfig_path).present?
451
418
 
@@ -458,5 +425,25 @@ module Uffizzi
458
425
  Uffizzi.ui.say('Please update the current context or provide a cluster name.')
459
426
  Uffizzi.ui.say('$uffizzi cluster sleep my-cluster')
460
427
  end
428
+
429
+ def cluster_api_connection_params
430
+ {
431
+ server: server,
432
+ project_slug: project_slug,
433
+ oidc_token: oidc_token,
434
+ }
435
+ end
436
+
437
+ def oidc_token
438
+ @oidc_token ||= ConfigFile.read_option(:oidc_token)
439
+ end
440
+
441
+ def project_slug
442
+ @project_slug ||= options[:project].nil? ? ConfigFile.read_option(:project) : options[:project]
443
+ end
444
+
445
+ def server
446
+ @server ||= ConfigFile.read_option(:server)
447
+ end
461
448
  end
462
449
  end
@@ -15,67 +15,110 @@ module Uffizzi
15
15
  method_option :kubeconfig, type: :string
16
16
  method_option :'k8s-version', required: false, type: :string
17
17
  def start(config_path = 'skaffold.yaml')
18
- Uffizzi::AuthHelper.check_login(options[:project])
18
+ run('start', config_path: config_path)
19
+ end
20
+
21
+ desc 'stop', 'Stop dev environment'
22
+ def stop
23
+ run('stop')
24
+ end
25
+
26
+ desc 'describe', 'Describe dev environment'
27
+ def describe
28
+ run('describe')
29
+ end
30
+
31
+ desc 'delete', 'Delete dev environment'
32
+ def delete
33
+ run('delete')
34
+ end
35
+
36
+ private
37
+
38
+ def run(command, command_args = {})
39
+ Uffizzi::AuthHelper.check_login
40
+
41
+ case command
42
+ when 'start'
43
+ handle_start_command(command_args)
44
+ when 'stop'
45
+ handle_stop_command
46
+ when 'describe'
47
+ handle_describe_command
48
+ when 'delete'
49
+ handle_delete_command
50
+ end
51
+ end
52
+
53
+ def handle_start_command(command_args)
54
+ config_path = command_args[:config_path]
19
55
  DevService.check_skaffold_existence
20
- DevService.check_running_daemon if options[:quiet]
56
+ DevService.check_no_running_process!
21
57
  DevService.check_skaffold_config_existence(config_path)
22
- cluster_id, cluster_name = start_create_cluster
23
- kubeconfig = wait_cluster_creation(cluster_name)
58
+
59
+ if dev_environment.empty?
60
+ DevService.set_startup_state
61
+ cluster_name = start_create_cluster
62
+ wait_cluster_creation(cluster_name)
63
+ DevService.set_dev_environment_config(cluster_name, config_path, options)
64
+ DevService.set_cluster_deployed_state
65
+ end
24
66
 
25
67
  if options[:quiet]
26
68
  launch_demonise_skaffold(config_path)
27
69
  else
28
- DevService.start_basic_skaffold(config_path, options)
29
- end
30
- ensure
31
- if defined?(cluster_name).present? && defined?(cluster_id).present?
32
- kubeconfig = defined?(kubeconfig).present? ? kubeconfig : nil
33
- handle_delete_cluster(cluster_id, cluster_name, kubeconfig)
70
+ launch_basic_skaffold(config_path)
34
71
  end
35
72
  end
36
73
 
37
- desc 'stop', 'Stop dev environment'
38
- def stop
39
- return Uffizzi.ui.say('Uffizzi dev is not running') unless File.exist?(DevService.pid_path)
74
+ def handle_stop_command
75
+ DevService.check_running_process!
76
+ DevService.stop_process
77
+ Uffizzi.ui.say('Uffizzi dev was stopped')
78
+ end
40
79
 
41
- pid = File.read(DevService.pid_path).to_i
42
- File.delete(DevService.pid_path)
80
+ def handle_describe_command
81
+ DevService.check_environment_exist!
43
82
 
44
- Uffizzi.process.kill('QUIT', pid)
45
- Uffizzi.ui.say('Uffizzi dev was stopped')
46
- rescue Errno::ESRCH
47
- Uffizzi.ui.say('Uffizzi dev is not running')
48
- File.delete(DevService.pid_path)
83
+ cluster_data = fetch_dev_env_cluster!
84
+ cluster_render_data = ClusterService.build_render_data(cluster_data)
85
+ dev_environment_render_data = cluster_render_data.merge(config_path: dev_environment[:config_path])
86
+
87
+ Uffizzi.ui.output_format = Uffizzi::UI::Shell::PRETTY_LIST
88
+ Uffizzi.ui.say(dev_environment_render_data)
49
89
  end
50
90
 
51
- private
91
+ def handle_delete_command
92
+ DevService.check_environment_exist!
93
+
94
+ if DevService.process_running?
95
+ DevService.stop_process
96
+ Uffizzi.ui.say('Uffizzi dev was stopped')
97
+ end
98
+
99
+ cluster_data = fetch_dev_env_cluster!
100
+ handle_delete_cluster(cluster_data)
101
+ DevService.clear_dev_environment_config
102
+ end
52
103
 
53
104
  def start_create_cluster
54
- params = cluster_creation_params(
55
- name: ClusterService.generate_name,
56
- creation_source: ClusterService::MANUAL_CREATION_SOURCE,
57
- k8s_version: options[:"k8s-version"],
58
- )
105
+ params = cluster_creation_params
59
106
  Uffizzi.ui.say('Start creating a cluster')
60
- response = create_cluster(ConfigFile.read_option(:server), project_slug, params)
107
+ response = create_cluster(server, project_slug, params)
61
108
  return ResponseHelper.handle_failed_response(response) unless ResponseHelper.created?(response)
62
109
 
63
- cluster_id = response.dig(:body, :cluster, :id)
64
- cluster_name = response.dig(:body, :cluster, :name)
65
-
66
- [cluster_id, cluster_name]
110
+ response.dig(:body, :cluster, :name)
67
111
  end
68
112
 
69
113
  def wait_cluster_creation(cluster_name)
70
114
  Uffizzi.ui.say('Checking the cluster status...')
71
- cluster_data = ClusterService.wait_cluster_deploy(project_slug, cluster_name, ConfigFile.read_option(:oidc_token))
115
+ cluster_data = ClusterService.wait_cluster_deploy(project_slug, cluster_name, oidc_token)
72
116
 
73
117
  if ClusterService.failed?(cluster_data[:state])
74
118
  Uffizzi.ui.say_error_and_exit("Cluster with name: #{cluster_name} failed to be created.")
75
119
  end
76
120
 
77
121
  handle_succeed_cluster_creation(cluster_data)
78
- parse_kubeconfig(cluster_data[:kubeconfig])
79
122
  end
80
123
 
81
124
  def handle_succeed_cluster_creation(cluster_data)
@@ -109,30 +152,30 @@ module Uffizzi
109
152
  ConfigFile.write_option(:clusters, clusters_config)
110
153
  end
111
154
 
112
- def cluster_creation_params(name:, creation_source:, k8s_version:)
113
- oidc_token = Uffizzi::ConfigFile.read_option(:oidc_token)
114
-
155
+ def cluster_creation_params
115
156
  {
116
157
  cluster: {
117
- name: name,
158
+ name: ClusterService.generate_name,
118
159
  manifest: nil,
119
- creation_source: creation_source,
120
- k8s_version: k8s_version,
160
+ creation_source: ClusterService::MANUAL_CREATION_SOURCE,
161
+ k8s_version: options[:"k8s-version"],
121
162
  },
122
163
  token: oidc_token,
123
164
  }
124
165
  end
125
166
 
126
- def handle_delete_cluster(cluster_id, cluster_name, kubeconfig)
127
- return if cluster_id.nil? || cluster_name.nil?
167
+ def handle_delete_cluster(cluster_data)
168
+ cluster_id = cluster_data[:id]
169
+ cluster_name = cluster_data[:name]
170
+ kubeconfig = parse_kubeconfig(cluster_data[:kubeconfig])
128
171
 
129
172
  exclude_kubeconfig(cluster_id, kubeconfig) if kubeconfig.present?
130
173
 
131
174
  params = {
132
175
  cluster_name: cluster_name,
133
- oidc_token: ConfigFile.read_option(:oidc_token),
176
+ oidc_token: oidc_token,
134
177
  }
135
- response = delete_cluster(ConfigFile.read_option(:server), project_slug, params)
178
+ response = delete_cluster(server, project_slug, params)
136
179
 
137
180
  if ResponseHelper.no_content?(response)
138
181
  Uffizzi.ui.say("Cluster #{cluster_name} deleted")
@@ -180,19 +223,58 @@ module Uffizzi
180
223
  Uffizzi.process.daemon(true)
181
224
 
182
225
  at_exit do
183
- File.delete(DevService.pid_path) if File.exist?(DevService.pid_path)
226
+ DevService.delete_pid
184
227
  end
185
228
 
229
+ DevService.save_pid
186
230
  File.delete(DevService.logs_path) if File.exist?(DevService.logs_path)
187
- File.write(DevService.pid_path, Uffizzi.process.pid)
188
231
  DevService.start_check_pid_file_existence
189
232
  DevService.start_demonised_skaffold(config_path, options)
190
233
  rescue StandardError => e
191
234
  File.open(DevService.logs_path, 'a') { |f| f.puts(e.message) }
192
235
  end
193
236
 
237
+ def launch_basic_skaffold(config_path)
238
+ at_exit do
239
+ DevService.delete_pid
240
+ end
241
+
242
+ DevService.save_pid
243
+ DevService.start_check_pid_file_existence
244
+ DevService.start_basic_skaffold(config_path, options)
245
+ end
246
+
247
+ def fetch_dev_env_cluster!
248
+ if DevService.startup?
249
+ Uffizzi.ui.say_error_and_exit('Dev environment not started yet')
250
+ end
251
+
252
+ cluster_name = dev_environment[:cluster_name]
253
+ ClusterService.fetch_cluster_data(cluster_name, **cluster_api_connection_params)
254
+ end
255
+
256
+ def dev_environment
257
+ @dev_environment ||= DevService.dev_environment
258
+ end
259
+
260
+ def cluster_api_connection_params
261
+ {
262
+ server: server,
263
+ project_slug: project_slug,
264
+ oidc_token: oidc_token,
265
+ }
266
+ end
267
+
194
268
  def project_slug
195
269
  @project_slug ||= ConfigFile.read_option(:project)
196
270
  end
271
+
272
+ def oidc_token
273
+ @oidc_token ||= ConfigFile.read_option(:oidc_token)
274
+ end
275
+
276
+ def server
277
+ @server ||= ConfigFile.read_option(:server)
278
+ end
197
279
  end
198
280
  end
@@ -52,6 +52,14 @@ module Uffizzi
52
52
  cluster_previous_current_contexts.detect { |c| c[:kubeconfig_path] == path }
53
53
  end
54
54
 
55
+ def set_dev_environment(cluster_name, params = {})
56
+ { cluster_name: cluster_name }.merge(params)
57
+ end
58
+
59
+ def dev_environment
60
+ read_option_from_config(:dev_environment) || {}
61
+ end
62
+
55
63
  private
56
64
 
57
65
  def clusters
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'uffizzi/response_helper'
3
4
  require 'uffizzi/clients/api/api_client'
4
5
 
5
6
  class ClusterService
@@ -104,5 +105,28 @@ class ClusterService
104
105
  regex = /\A[a-zA-Z0-9-]*\z/
105
106
  regex.match?(name)
106
107
  end
108
+
109
+ def fetch_cluster_data(cluster_name, server:, project_slug:, oidc_token:)
110
+ params = {
111
+ cluster_name: cluster_name,
112
+ oidc_token: oidc_token,
113
+ }
114
+ response = get_cluster(server, project_slug, params)
115
+
116
+ if Uffizzi::ResponseHelper.ok?(response)
117
+ response.dig(:body, :cluster)
118
+ else
119
+ Uffizzi::ResponseHelper.handle_failed_response(response)
120
+ end
121
+ end
122
+
123
+ def build_render_data(cluster_data)
124
+ {
125
+ name: cluster_data[:name],
126
+ status: cluster_data[:state],
127
+ created: Time.strptime(cluster_data[:created_at], '%Y-%m-%dT%H:%M:%S.%N').strftime('%a %b %d %H:%M:%S %Y'),
128
+ url: cluster_data[:host],
129
+ }
130
+ end
107
131
  end
108
132
  end
@@ -3,21 +3,50 @@
3
3
  require 'uffizzi/clients/api/api_client'
4
4
 
5
5
  class DevService
6
+ DEFAULT_REGISTRY_REPO = 'registry.uffizzi.com'
7
+ STARTUP_STATE = 'startup'
8
+ CLUSTER_DEPLOYED_STATE = 'cluster_deployed'
9
+
6
10
  class << self
7
11
  include ApiClient
8
12
 
9
- DEFAULT_REGISTRY_REPO = 'registry.uffizzi.com'
13
+ def check_no_running_process!
14
+ if process_running?
15
+ Uffizzi.ui.say_error_and_exit("You have already started uffizzi dev. To stop the process do 'uffizzi dev stop'")
16
+ end
17
+ end
10
18
 
11
- def check_running_daemon
12
- return unless File.exist?(pid_path)
19
+ def check_running_process!
20
+ unless process_running?
21
+ Uffizzi.ui.say_error_and_exit('Uffizzi dev is not running')
22
+ end
23
+ end
13
24
 
14
- pid = File.read(pid_path)
15
- File.delete(pid_path) if pid.blank?
16
- Uffizzi.process.kill(0, pid.to_i)
25
+ def check_environment_exist!
26
+ if dev_environment.empty?
27
+ Uffizzi.ui.say_error_and_exit('Uffizzi dev does not exist')
28
+ end
29
+ end
30
+
31
+ def stop_process
32
+ dev_pid = running_pid
33
+ skaffold_pid = running_skaffold_pid
34
+
35
+ Uffizzi.process.kill('INT', skaffold_pid)
36
+ Uffizzi.process.kill('INT', dev_pid)
37
+ delete_pid
38
+ rescue Errno::ESRCH
39
+ delete_pid
40
+ end
41
+
42
+ def process_running?
43
+ pid = running_pid
44
+ return false unless pid.positive?
17
45
 
18
- Uffizzi.ui.say_error_and_exit("You have already started uffizzi dev as daemon. To stop the process do 'uffizzi dev stop'")
46
+ Uffizzi.process.kill(0, pid.to_i)
47
+ true
19
48
  rescue Errno::ESRCH
20
- File.delete(pid_path)
49
+ false
21
50
  end
22
51
 
23
52
  def start_check_pid_file_existence
@@ -34,6 +63,9 @@ class DevService
34
63
  cmd = build_skaffold_dev_command(config_path, options)
35
64
 
36
65
  Uffizzi.ui.popen2e(cmd) do |_stdin, stdout_and_stderr, wait_thr|
66
+ pid = wait_thr.pid
67
+ skaffold_pid = find_skaffold_pid(pid)
68
+ save_skaffold_pid(skaffold_pid)
37
69
  stdout_and_stderr.each { |l| Uffizzi.ui.say(l) }
38
70
  wait_thr.value
39
71
  end
@@ -44,6 +76,10 @@ class DevService
44
76
  cmd = build_skaffold_dev_command(config_path, options)
45
77
 
46
78
  Uffizzi.ui.popen2e(cmd) do |_stdin, stdout_and_stderr, wait_thr|
79
+ pid = wait_thr.pid
80
+ skaffold_pid = find_skaffold_pid(pid)
81
+ save_skaffold_pid(skaffold_pid)
82
+
47
83
  File.open(logs_path, 'a') do |f|
48
84
  stdout_and_stderr.each do |line|
49
85
  f.puts(line)
@@ -80,6 +116,10 @@ class DevService
80
116
  File.join(Uffizzi::ConfigFile::CONFIG_DIR, 'uffizzi_dev.pid')
81
117
  end
82
118
 
119
+ def skaffold_pid_path
120
+ File.join(Uffizzi::ConfigFile::CONFIG_DIR, 'skaffold_dev.pid')
121
+ end
122
+
83
123
  def logs_path
84
124
  File.join(Uffizzi::ConfigFile::CONFIG_DIR, 'uffizzi_dev.log')
85
125
  end
@@ -104,5 +144,67 @@ class DevService
104
144
 
105
145
  File.expand_path(path)
106
146
  end
147
+
148
+ def running_pid
149
+ return nil.to_i unless File.exist?(pid_path)
150
+
151
+ File.read(pid_path).to_i
152
+ end
153
+
154
+ def save_pid
155
+ File.write(pid_path, Uffizzi.process.pid)
156
+ end
157
+
158
+ def delete_pid
159
+ File.delete(pid_path) if File.exist?(pid_path)
160
+ File.delete(skaffold_pid_path) if File.exist?(skaffold_pid_path)
161
+ end
162
+
163
+ def running_skaffold_pid
164
+ return nil.to_i unless File.exist?(skaffold_pid_path)
165
+
166
+ File.read(skaffold_pid_path).to_i
167
+ end
168
+
169
+ def save_skaffold_pid(pid)
170
+ File.write(skaffold_pid_path, pid)
171
+ end
172
+
173
+ def set_dev_environment_config(cluster_name, config_path, options)
174
+ params = options.merge(config_path: File.expand_path(config_path))
175
+ new_dev_environment = Uffizzi::ConfigHelper.set_dev_environment(cluster_name, params)
176
+ Uffizzi::ConfigFile.write_option(:dev_environment, new_dev_environment)
177
+ end
178
+
179
+ def set_startup_state
180
+ new_dev_environment = dev_environment.merge(state: STARTUP_STATE)
181
+ Uffizzi::ConfigFile.write_option(:dev_environment, new_dev_environment)
182
+ end
183
+
184
+ def set_cluster_deployed_state
185
+ new_dev_environment = dev_environment.merge(state: CLUSTER_DEPLOYED_STATE)
186
+ Uffizzi::ConfigFile.write_option(:dev_environment, new_dev_environment)
187
+ end
188
+
189
+ def startup?
190
+ dev_environment[:state] == STARTUP_STATE
191
+ end
192
+
193
+ def clear_dev_environment_config
194
+ Uffizzi::ConfigFile.write_option(:dev_environment, {})
195
+ end
196
+
197
+ def dev_environment
198
+ Uffizzi::ConfigHelper.dev_environment
199
+ end
200
+
201
+ def find_skaffold_pid(ppid)
202
+ ppid_regex = /\w*\s+\d+\s+#{ppid}.*\sskaffold dev/
203
+ pid_regex = /\w*\s+(\d+)\s+#{ppid}.*\sskaffold dev/
204
+
205
+ io = Uffizzi.ui.popen('ps -ef')
206
+ ps = io.readlines.detect { |l| l.match?(ppid_regex) }
207
+ ps.match(pid_regex)[1]
208
+ end
107
209
  end
108
210
  end
data/lib/uffizzi/shell.rb CHANGED
@@ -11,6 +11,7 @@ module Uffizzi
11
11
 
12
12
  PRETTY_JSON = 'pretty-json'
13
13
  REGULAR_JSON = 'json'
14
+ PRETTY_LIST = 'pretty-list'
14
15
 
15
16
  def initialize
16
17
  @shell = Thor::Shell::Basic.new
@@ -55,6 +56,10 @@ module Uffizzi
55
56
  $stdout.stat.pipe?
56
57
  end
57
58
 
59
+ def popen(command)
60
+ IO.popen(command)
61
+ end
62
+
58
63
  def popen2e(command, &block)
59
64
  Open3.popen2e(command, &block)
60
65
  end
@@ -73,12 +78,25 @@ module Uffizzi
73
78
  JSON.pretty_generate(data)
74
79
  end
75
80
 
81
+ def format_to_pretty_list(data)
82
+ case data
83
+ when Array
84
+ data.map { |v| format_to_pretty_list(v) }.join("\n\n")
85
+ when Hash
86
+ data.map { |k, v| "- #{k.to_s.upcase}: #{v}" }.join("\n").strip
87
+ else
88
+ data
89
+ end
90
+ end
91
+
76
92
  def format_message(message)
77
93
  case output_format
78
94
  when PRETTY_JSON
79
95
  format_to_pretty_json(message)
80
96
  when REGULAR_JSON
81
97
  format_to_json(message)
98
+ when PRETTY_LIST
99
+ format_to_pretty_list(message)
82
100
  else
83
101
  message
84
102
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Uffizzi
4
- VERSION = '2.2.0'
4
+ VERSION = '2.2.1'
5
5
  end
data/man/uffizzi CHANGED
@@ -33,6 +33,9 @@ GROUP is one of the following:
33
33
  project
34
34
  Manage Uffizzi project resources including compose files for
35
35
  specifying compose environment (preview) configurations and secrets
36
+
37
+ dev
38
+ Creates a Uffizzi cluster preconfigured for development workflows
36
39
  .fi
37
40
  .SH "COMMAND"
38
41
  .nf
@@ -0,0 +1,36 @@
1
+ .\" generated with Ronn-NG/v0.9.1
2
+ .\" http://github.com/apjanke/ronn-ng/tree/0.9.1
3
+ .TH "UFFIZZI\-DEV\-DESCRIBE" "" "October 2023" ""
4
+ .SH "NAME"
5
+ \fBuffizzi\-dev\-describe\fR
6
+ .P
7
+ $ uffizzi dev describe \-h uffizzi\-dev\-describe \- show metadata for a dev environment ================================================================
8
+ .SH "SYNOPSIS"
9
+ .nf
10
+ uffizzi dev describe <NAME>
11
+ .fi
12
+ .SH "DESCRIPTION"
13
+ .nf
14
+ Shows metadata for a dev environment
15
+
16
+ This command can fail for the following reasons:
17
+ \- The dev environment specified does not exist\.
18
+ \- The dev environment specified belongs to a different project\.
19
+
20
+ For more information on Uffizzi clusters, see:
21
+ https://docs\.uffizzi\.com/references/cli/
22
+ .fi
23
+ .SH "POSITIONAL ARGUMENTS"
24
+ .nf
25
+ [NAME]
26
+ NAME for the dev environment you want to describe\.
27
+ This is an optional argument\.
28
+ .fi
29
+ .SH "EXAMPLES"
30
+ .nf
31
+ The following command prints metadata for the dev
32
+ environment:
33
+
34
+ $ uffizzi dev describe
35
+ .fi
36
+
@@ -0,0 +1,27 @@
1
+ $ uffizzi dev describe -h
2
+ uffizzi-dev-describe - show metadata for a dev environment
3
+ ================================================================
4
+
5
+ ## SYNOPSIS
6
+ uffizzi dev describe <NAME>
7
+
8
+ ## DESCRIPTION
9
+ Shows metadata for a dev environment
10
+
11
+ This command can fail for the following reasons:
12
+ - The dev environment specified does not exist.
13
+ - The dev environment specified belongs to a different project.
14
+
15
+ For more information on Uffizzi clusters, see:
16
+ https://docs.uffizzi.com/references/cli/
17
+
18
+ ## POSITIONAL ARGUMENTS
19
+ [NAME]
20
+ NAME for the dev environment you want to describe.
21
+ This is an optional argument.
22
+
23
+ ## EXAMPLES
24
+ The following command prints metadata for the dev
25
+ environment:
26
+
27
+ $ uffizzi dev describe
data/man/uffizzi.ronn CHANGED
@@ -29,6 +29,9 @@ uffizzi - manage Uffizzi resources
29
29
  Manage Uffizzi project resources including compose files for
30
30
  specifying compose environment (preview) configurations and secrets
31
31
 
32
+ dev
33
+ Creates a Uffizzi cluster preconfigured for development workflows
34
+
32
35
  ## COMMAND
33
36
  COMMAND is one of the following:
34
37
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: uffizzi-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.0
4
+ version: 2.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Josh Thurman
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2023-10-19 00:00:00.000000000 Z
12
+ date: 2023-10-23 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -522,6 +522,8 @@ files:
522
522
  - man/uffizzi-connect-ghcr.ronn
523
523
  - man/uffizzi-connect.ronn
524
524
  - man/uffizzi-dev
525
+ - man/uffizzi-dev-describe
526
+ - man/uffizzi-dev-describe.ronn
525
527
  - man/uffizzi-dev-start
526
528
  - man/uffizzi-dev-start.ronn
527
529
  - man/uffizzi-dev-stop