uffizzi-cli 1.0.4 → 2.0.29

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.
Files changed (156) hide show
  1. checksums.yaml +4 -4
  2. data/config/uffizzi.rb +1 -0
  3. data/lib/uffizzi/auth_helper.rb +13 -5
  4. data/lib/uffizzi/cli/account.rb +122 -0
  5. data/lib/uffizzi/cli/cluster.rb +363 -0
  6. data/lib/uffizzi/cli/common.rb +2 -4
  7. data/lib/uffizzi/cli/config.rb +5 -4
  8. data/lib/uffizzi/cli/connect.rb +14 -8
  9. data/lib/uffizzi/cli/disconnect.rb +1 -1
  10. data/lib/uffizzi/cli/login.rb +130 -66
  11. data/lib/uffizzi/cli/login_by_identity_token.rb +11 -7
  12. data/lib/uffizzi/cli/preview/service.rb +3 -4
  13. data/lib/uffizzi/cli/preview.rb +33 -32
  14. data/lib/uffizzi/cli/project.rb +17 -9
  15. data/lib/uffizzi/cli.rb +46 -7
  16. data/lib/uffizzi/clients/api/api_client.rb +92 -2
  17. data/lib/uffizzi/clients/api/api_routes.rb +44 -0
  18. data/lib/uffizzi/clients/api/http_client.rb +19 -7
  19. data/lib/uffizzi/config_file.rb +15 -40
  20. data/lib/uffizzi/error.rb +15 -0
  21. data/lib/uffizzi/helpers/config_helper.rb +49 -0
  22. data/lib/uffizzi/helpers/file_helper.rb +25 -0
  23. data/lib/uffizzi/helpers/login_helper.rb +33 -0
  24. data/lib/uffizzi/helpers/project_helper.rb +19 -0
  25. data/lib/uffizzi/promt.rb +4 -0
  26. data/lib/uffizzi/response_helper.rb +33 -14
  27. data/lib/uffizzi/services/cluster_service.rb +59 -0
  28. data/lib/uffizzi/services/compose_file_service.rb +5 -4
  29. data/lib/uffizzi/services/env_variables_service.rb +4 -2
  30. data/lib/uffizzi/services/github_service.rb +21 -0
  31. data/lib/uffizzi/services/kubeconfig_service.rb +132 -0
  32. data/lib/uffizzi/services/preview_service.rb +52 -18
  33. data/lib/uffizzi/shell.rb +19 -16
  34. data/lib/uffizzi/token.rb +37 -0
  35. data/lib/uffizzi/version.rb +1 -1
  36. data/lib/uffizzi.rb +10 -0
  37. data/man/uffizzi +7 -5
  38. data/man/uffizzi-account +29 -0
  39. data/man/uffizzi-account-list +26 -0
  40. data/man/uffizzi-account-list.html +103 -0
  41. data/man/uffizzi-account-list.ronn +19 -0
  42. data/man/uffizzi-account-set-default +29 -0
  43. data/man/uffizzi-account-set-default.html +105 -0
  44. data/man/uffizzi-account-set-default.ronn +22 -0
  45. data/man/uffizzi-account.html +106 -0
  46. data/man/uffizzi-account.ronn +23 -0
  47. data/man/uffizzi-cluster +37 -0
  48. data/man/uffizzi-cluster-create +49 -0
  49. data/man/uffizzi-cluster-create.ronn +41 -0
  50. data/man/uffizzi-cluster-delete +32 -0
  51. data/man/uffizzi-cluster-delete.ronn +24 -0
  52. data/man/uffizzi-cluster-describe +39 -0
  53. data/man/uffizzi-cluster-describe.ronn +30 -0
  54. data/man/uffizzi-cluster-list +34 -0
  55. data/man/uffizzi-cluster-list.ronn +26 -0
  56. data/man/uffizzi-cluster-update-kubeconfig +37 -0
  57. data/man/uffizzi-cluster-update-kubeconfig.ronn +29 -0
  58. data/man/uffizzi-cluster.ronn +32 -0
  59. data/man/uffizzi-compose +39 -0
  60. data/man/uffizzi-compose-create +67 -0
  61. data/man/uffizzi-compose-create.ronn +57 -0
  62. data/man/uffizzi-compose-delete +38 -0
  63. data/man/uffizzi-compose-delete.ronn +29 -0
  64. data/man/uffizzi-compose-describe +38 -0
  65. data/man/uffizzi-compose-describe.ronn +29 -0
  66. data/man/uffizzi-compose-events +38 -0
  67. data/man/uffizzi-compose-events.ronn +29 -0
  68. data/man/uffizzi-compose-list +58 -0
  69. data/man/uffizzi-compose-list.ronn +49 -0
  70. data/man/uffizzi-compose-service-list +42 -0
  71. data/man/uffizzi-compose-service-list.ronn +32 -0
  72. data/man/uffizzi-compose-service-logs +59 -0
  73. data/man/uffizzi-compose-service-logs.ronn +48 -0
  74. data/man/uffizzi-compose-update +61 -0
  75. data/man/uffizzi-compose-update.ronn +51 -0
  76. data/man/uffizzi-compose.ronn +33 -0
  77. data/man/uffizzi-compose_service_logs +59 -0
  78. data/man/uffizzi-compose_service_logs.ronn +50 -0
  79. data/man/uffizzi-config +2 -2
  80. data/man/uffizzi-config.ronn +1 -1
  81. data/man/uffizzi-connect +2 -4
  82. data/man/uffizzi-connect-acr +2 -2
  83. data/man/uffizzi-connect-acr.ronn +1 -1
  84. data/man/uffizzi-connect-docker-hub +2 -2
  85. data/man/uffizzi-connect-docker-hub.ronn +1 -1
  86. data/man/uffizzi-connect-docker-registry +2 -2
  87. data/man/uffizzi-connect-docker-registry.ronn +1 -1
  88. data/man/uffizzi-connect-ecr +2 -2
  89. data/man/uffizzi-connect-ecr.ronn +1 -1
  90. data/man/uffizzi-connect-gcr +2 -2
  91. data/man/uffizzi-connect-gcr.ronn +1 -1
  92. data/man/uffizzi-connect-ghcr +2 -2
  93. data/man/uffizzi-connect-ghcr.ronn +1 -1
  94. data/man/uffizzi-connect.ronn +2 -2
  95. data/man/uffizzi-disconnect +2 -2
  96. data/man/uffizzi-disconnect.ronn +1 -1
  97. data/man/uffizzi-login +7 -3
  98. data/man/uffizzi-login-by-identity-token +3 -3
  99. data/man/uffizzi-login-by-identity-token.ronn +2 -2
  100. data/man/uffizzi-login.html +113 -0
  101. data/man/uffizzi-login.ronn +6 -2
  102. data/man/uffizzi-logout +2 -2
  103. data/man/uffizzi-logout.ronn +1 -1
  104. data/man/uffizzi-preview +9 -9
  105. data/man/uffizzi-preview-create +22 -20
  106. data/man/uffizzi-preview-create.ronn +28 -26
  107. data/man/uffizzi-preview-delete +11 -10
  108. data/man/uffizzi-preview-delete.ronn +12 -11
  109. data/man/uffizzi-preview-describe +10 -10
  110. data/man/uffizzi-preview-describe.ronn +11 -11
  111. data/man/uffizzi-preview-events +12 -11
  112. data/man/uffizzi-preview-events.ronn +13 -12
  113. data/man/uffizzi-preview-list +19 -18
  114. data/man/uffizzi-preview-list.ronn +21 -20
  115. data/man/uffizzi-preview-service-list +16 -12
  116. data/man/uffizzi-preview-service-list.ronn +16 -13
  117. data/man/uffizzi-preview-service-logs +14 -12
  118. data/man/uffizzi-preview-service-logs.ronn +18 -17
  119. data/man/uffizzi-preview-update +19 -18
  120. data/man/uffizzi-preview-update.ronn +21 -20
  121. data/man/uffizzi-preview.ronn +10 -10
  122. data/man/uffizzi-preview_service_logs +14 -12
  123. data/man/uffizzi-preview_service_logs.ronn +18 -17
  124. data/man/uffizzi-project +3 -3
  125. data/man/uffizzi-project-compose +2 -2
  126. data/man/uffizzi-project-compose-describe +2 -2
  127. data/man/uffizzi-project-compose-describe.ronn +1 -1
  128. data/man/uffizzi-project-compose-set +2 -2
  129. data/man/uffizzi-project-compose-set.ronn +1 -1
  130. data/man/uffizzi-project-compose-unset +2 -2
  131. data/man/uffizzi-project-compose-unset.ronn +1 -1
  132. data/man/uffizzi-project-compose.ronn +1 -1
  133. data/man/uffizzi-project-create +2 -2
  134. data/man/uffizzi-project-create.ronn +1 -1
  135. data/man/uffizzi-project-delete +2 -2
  136. data/man/uffizzi-project-delete.ronn +1 -1
  137. data/man/uffizzi-project-describe +3 -3
  138. data/man/uffizzi-project-describe.ronn +1 -1
  139. data/man/uffizzi-project-preview-describe +37 -0
  140. data/man/uffizzi-project-preview-describe.ronn +29 -0
  141. data/man/uffizzi-project-preview-set +66 -0
  142. data/man/uffizzi-project-preview-set.ronn +57 -0
  143. data/man/uffizzi-project-secret +2 -2
  144. data/man/uffizzi-project-secret-create +2 -2
  145. data/man/uffizzi-project-secret-create.ronn +1 -1
  146. data/man/uffizzi-project-secret-delete +2 -2
  147. data/man/uffizzi-project-secret-delete.ronn +1 -1
  148. data/man/uffizzi-project-secret-list +2 -2
  149. data/man/uffizzi-project-secret-list.ronn +1 -1
  150. data/man/uffizzi-project-secret.ronn +1 -1
  151. data/man/uffizzi-project-set-default +2 -2
  152. data/man/uffizzi-project-set-default.ronn +1 -1
  153. data/man/uffizzi-project.html +124 -0
  154. data/man/uffizzi-project.ronn +2 -2
  155. data/man/uffizzi.ronn +12 -10
  156. metadata +131 -19
data/lib/uffizzi/cli.rb CHANGED
@@ -19,6 +19,7 @@ module Uffizzi
19
19
  desc 'login [OPTIONS]', 'Login to Uffizzi to view and manage your previews'
20
20
  method_option :server, required: false, aliases: '-s'
21
21
  method_option :username, required: false, aliases: '-u'
22
+ method_option :email, required: false, aliases: '-e'
22
23
  def login
23
24
  require_relative 'cli/login'
24
25
  Login.new(options).run
@@ -26,7 +27,8 @@ module Uffizzi
26
27
 
27
28
  desc 'login_by_identity_token [OPTIONS]', 'Login or register to Uffizzi to view and manage your previews'
28
29
  method_option :server, required: true, aliases: '-s'
29
- method_option :token, required: true, aliases: '-t'
30
+ method_option :oidc_token, required: true, aliases: '-t'
31
+ method_option :access_token, required: false
30
32
  def login_by_identity_token
31
33
  require_relative 'cli/login_by_identity_token'
32
34
  LoginByIdentityToken.new(options).run
@@ -38,6 +40,10 @@ module Uffizzi
38
40
  Logout.new(options).run
39
41
  end
40
42
 
43
+ desc 'account', 'account'
44
+ require_relative 'cli/account'
45
+ subcommand 'account', Cli::Account
46
+
41
47
  desc 'project', 'project'
42
48
  require_relative 'cli/project'
43
49
  subcommand 'project', Cli::Project
@@ -46,10 +52,14 @@ module Uffizzi
46
52
  require_relative 'cli/config'
47
53
  subcommand 'config', Cli::Config
48
54
 
49
- desc 'preview', 'preview'
55
+ desc 'compose', 'compose'
50
56
  method_option :project, required: false
51
57
  require_relative 'cli/preview'
52
- subcommand 'preview', Cli::Preview
58
+ subcommand 'compose', Cli::Preview
59
+
60
+ desc 'cluster', 'cluster'
61
+ require_relative 'cli/cluster'
62
+ subcommand 'cluster', Cli::Cluster
53
63
 
54
64
  desc 'connect', 'connect'
55
65
  require_relative 'cli/connect'
@@ -61,6 +71,8 @@ module Uffizzi
61
71
  Disconnect.new.run(credential_type)
62
72
  end
63
73
 
74
+ map preview: :compose
75
+
64
76
  class << self
65
77
  protected
66
78
 
@@ -70,10 +82,8 @@ module Uffizzi
70
82
  return Common.show_manual(filename(args)) if show_help?(args, opts)
71
83
 
72
84
  super
73
- rescue Interrupt
74
- raise Uffizzi::Error.new('The command was interrupted')
75
- rescue StandardError => e
76
- raise Uffizzi::Error.new(e.message)
85
+ rescue Interrupt, StandardError => e
86
+ ci_workflow? ? handle_ci_exceptions(e) : handle_repl_exceptions(e)
77
87
  end
78
88
 
79
89
  private
@@ -89,6 +99,35 @@ module Uffizzi
89
99
  help_options = ['--help', '-h', '--help=true']
90
100
  args.empty? || args.include?('help') || opts.any? { |opt| help_options.include?(opt) }
91
101
  end
102
+
103
+ def ci_workflow?
104
+ !['', 'false', 'f', '0'].include?(ENV['CI_WORKFLOW'].to_s.downcase)
105
+ end
106
+
107
+ def handle_ci_exceptions(exception)
108
+ case exception
109
+ when Thor::Error
110
+ raise exception
111
+ when Interrupt
112
+ raise Uffizzi::CliError.new('CI process was interrupted')
113
+ else
114
+ Sentry.capture_exception(exception)
115
+ raise Uffizzi::CliError.new('System Fault')
116
+ end
117
+ end
118
+
119
+ def handle_repl_exceptions(exception)
120
+ case exception
121
+ when Thor::Error
122
+ raise exception
123
+ when Interrupt
124
+ raise Uffizzi::CliError.new('The command was interrupted')
125
+ when StandardError
126
+ raise Uffizzi::CliError.new(exception.message)
127
+ else
128
+ raise exception
129
+ end
130
+ end
92
131
  end
93
132
  end
94
133
  end
@@ -4,6 +4,16 @@ require_relative 'api_routes'
4
4
  require_relative 'http_client'
5
5
 
6
6
  module ApiClient
7
+ class ResponseError < StandardError
8
+ attr_reader :response
9
+
10
+ def initialize(response)
11
+ @response = response
12
+
13
+ super(response.to_s)
14
+ end
15
+ end
16
+
7
17
  include ApiRoutes
8
18
 
9
19
  def create_session(server, params = {})
@@ -27,6 +37,13 @@ module ApiClient
27
37
  build_response(response)
28
38
  end
29
39
 
40
+ def fetch_accounts(server)
41
+ uri = accounts_uri(server)
42
+ response = http_client.make_get_request(uri)
43
+
44
+ build_response(response)
45
+ end
46
+
30
47
  def fetch_projects(server)
31
48
  uri = projects_uri(server)
32
49
  response = http_client.make_get_request(uri)
@@ -34,6 +51,13 @@ module ApiClient
34
51
  build_response(response)
35
52
  end
36
53
 
54
+ def fetch_account_projects(server, account_id)
55
+ uri = account_projects_uri(server, account_id)
56
+ response = http_client.make_get_request(uri)
57
+
58
+ build_response(response)
59
+ end
60
+
37
61
  def fetch_credentials(server, account_id)
38
62
  uri = credentials_uri(server, account_id)
39
63
 
@@ -49,13 +73,20 @@ module ApiClient
49
73
  build_response(response)
50
74
  end
51
75
 
52
- def describe_project(server, project_slug)
76
+ def fetch_project(server, project_slug)
53
77
  uri = project_uri(server, project_slug)
54
78
  response = http_client.make_get_request(uri)
55
79
 
56
80
  build_response(response)
57
81
  end
58
82
 
83
+ def fetch_account(server, account_name)
84
+ uri = account_uri(server, account_name)
85
+ response = http_client.make_get_request(uri)
86
+
87
+ build_response(response)
88
+ end
89
+
59
90
  def create_project(server, account_id, params)
60
91
  uri = create_projects_uri(server, account_id)
61
92
  response = http_client.make_post_request(uri, params)
@@ -210,6 +241,65 @@ module ApiClient
210
241
  build_response(response)
211
242
  end
212
243
 
244
+ def get_k8s_container_description(server, project_slug, deployment_id, container_name)
245
+ uri = k8s_container_description_uri(server, project_slug, deployment_id, container_name)
246
+ response = http_client.make_get_request(uri)
247
+
248
+ build_response(response)
249
+ end
250
+
251
+ def get_project_clusters(server, project_slug, params = nil)
252
+ uri = project_clusters_uri(server, project_slug, oidc_token: params[:oidc_token])
253
+ response = http_client.make_get_request(uri)
254
+
255
+ build_response(response)
256
+ end
257
+
258
+ def create_cluster(server, project_slug, params)
259
+ uri = project_clusters_uri(server, project_slug, oidc_token: nil)
260
+ response = http_client.make_post_request(uri, params)
261
+
262
+ build_response(response)
263
+ end
264
+
265
+ def create_access_token(server, session_id)
266
+ uri = access_tokens_url(server)
267
+
268
+ params = { session_id: session_id }
269
+
270
+ response = http_client.make_post_request(uri, params)
271
+
272
+ build_response(response)
273
+ end
274
+
275
+ def get_cluster(server, project_slug, params)
276
+ uri = cluster_uri(server, project_slug, cluster_name: params[:cluster_name], oidc_token: params[:oidc_token])
277
+ response = http_client.make_get_request(uri)
278
+
279
+ build_response(response)
280
+ end
281
+
282
+ def get_access_token(server, code)
283
+ uri = access_token_url(server, code)
284
+ response = http_client.make_get_request(uri)
285
+
286
+ build_response(response)
287
+ end
288
+
289
+ def delete_cluster(server, project_slug, params)
290
+ uri = cluster_uri(server, project_slug, cluster_name: params[:cluster_name], oidc_token: params[:oidc_token])
291
+ response = http_client.make_delete_request(uri)
292
+
293
+ build_response(response)
294
+ end
295
+
296
+ def get_account_clusters(server, account_id)
297
+ uri = account_clusters_uri(server, account_id)
298
+ response = http_client.make_get_request(uri)
299
+
300
+ build_response(response)
301
+ end
302
+
213
303
  private
214
304
 
215
305
  def http_client
@@ -221,7 +311,7 @@ module ApiClient
221
311
  params[:basic_auth_password] = Uffizzi::ConfigFile.read_option(:basic_auth_password)
222
312
  end
223
313
 
224
- @http_client = Uffizzi::HttpClient.new(params[:cookie], params[:basic_auth_user], params[:basic_auth_password])
314
+ @http_client = Uffizzi::HttpClient.new(params)
225
315
  end
226
316
 
227
317
  @http_client
@@ -3,6 +3,14 @@
3
3
  require 'cgi'
4
4
 
5
5
  module ApiRoutes
6
+ def accounts_uri(server)
7
+ "#{server}/api/cli/v1/accounts"
8
+ end
9
+
10
+ def account_uri(server, account_name)
11
+ "#{server}/api/cli/v1/accounts/#{account_name}"
12
+ end
13
+
6
14
  def compose_file_uri(server, project_slug)
7
15
  "#{server}/api/cli/v1/projects/#{project_slug}/compose_file"
8
16
  end
@@ -15,6 +23,10 @@ module ApiRoutes
15
23
  "#{server}/api/cli/v1/projects"
16
24
  end
17
25
 
26
+ def account_projects_uri(server, account_id)
27
+ "#{server}/api/cli/v1/accounts/#{account_id}/projects"
28
+ end
29
+
18
30
  def create_projects_uri(server, account_id)
19
31
  "#{server}/api/cli/v1/accounts/#{account_id}/projects"
20
32
  end
@@ -81,4 +93,36 @@ module ApiRoutes
81
93
  def preview_service_logs_uri(server, project_slug, deployment_id, container_name)
82
94
  "#{server}/api/cli/v1/projects/#{project_slug}/deployments/#{deployment_id}/containers/#{container_name}/logs"
83
95
  end
96
+
97
+ def k8s_container_description_uri(server, project_slug, deployment_id, container_name)
98
+ "#{server}/api/cli/v1/projects/#{project_slug}/deployments/#{deployment_id}/containers/#{container_name}/k8s_container_description"
99
+ end
100
+
101
+ def project_clusters_uri(server, project_slug, oidc_token:)
102
+ return "#{server}/api/cli/v1/projects/#{project_slug}/clusters" if oidc_token.nil?
103
+
104
+ "#{server}/api/cli/v1/projects/#{project_slug}/clusters?token=#{oidc_token}"
105
+ end
106
+
107
+ def cluster_uri(server, project_slug, cluster_name:, oidc_token:)
108
+ return "#{server}/api/cli/v1/projects/#{project_slug}/clusters/#{cluster_name}" if oidc_token.nil?
109
+
110
+ "#{server}/api/cli/v1/projects/#{project_slug}/clusters/#{cluster_name}?token=#{oidc_token}"
111
+ end
112
+
113
+ def access_token_url(server, code)
114
+ "#{server}/api/cli/v1/access_tokens/#{code}"
115
+ end
116
+
117
+ def access_tokens_url(server)
118
+ "#{server}/api/cli/v1/access_tokens"
119
+ end
120
+
121
+ def browser_sign_in_url(server, session_id)
122
+ "#{server}/sign_in?session_id=#{session_id}"
123
+ end
124
+
125
+ def account_clusters_uri(server, account_id)
126
+ "#{server}/api/cli/v1/accounts/#{account_id}/clusters"
127
+ end
84
128
  end
@@ -7,10 +7,10 @@ module Uffizzi
7
7
  class HttpClient
8
8
  attr_accessor :auth_cookie, :basic_auth_user, :basic_auth_password
9
9
 
10
- def initialize(auth_cookie, basic_auth_user, basic_auth_password)
11
- @auth_cookie = auth_cookie
12
- @basic_auth_user = basic_auth_user
13
- @basic_auth_password = basic_auth_password
10
+ def initialize(params)
11
+ @auth_cookie = params[:cookie]
12
+ @basic_auth_user = params[:basic_auth_user]
13
+ @basic_auth_password = params[:basic_auth_password]
14
14
  end
15
15
 
16
16
  def make_get_request(request_uri)
@@ -41,13 +41,17 @@ module Uffizzi
41
41
  http.request(request)
42
42
  end
43
43
 
44
- raise Uffizzi::Error.new('Not authorized') if response.is_a?(Net::HTTPUnauthorized)
44
+ if response.is_a?(Net::HTTPUnauthorized)
45
+ Uffizzi::Token.delete if Uffizzi::Token.exists?
46
+ raise Uffizzi::Error.new('Not authorized')
47
+ end
45
48
 
46
49
  response
47
50
  end
48
51
 
49
52
  def build_request(uri, params, method)
50
- headers = { 'Content-Type' => 'application/json' }
53
+ access_token = Uffizzi::Token.read
54
+ headers = get_headers(access_token)
51
55
  request = case method
52
56
  when :get
53
57
  Net::HTTP::Get.new(uri.request_uri, headers)
@@ -62,9 +66,17 @@ module Uffizzi
62
66
  request.body = params.to_json
63
67
  end
64
68
  request['Cookie'] = @auth_cookie
65
- request.basic_auth(@basic_auth_user, @basic_auth_password)
69
+ request.basic_auth(@basic_auth_user, @basic_auth_password) unless access_token
66
70
 
67
71
  request
68
72
  end
73
+
74
+ def get_headers(access_token)
75
+ content_type_headers = { 'Content-Type' => 'application/json' }
76
+ auth_headers = access_token ? { 'Authorization' => "Bearer #{access_token}" } : {}
77
+ cli_version = { 'x-uffizzi-cli-version' => Uffizzi::VERSION }
78
+
79
+ content_type_headers.merge(auth_headers).merge(cli_version)
80
+ end
69
81
  end
70
82
  end
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'json'
4
- require 'fileutils'
4
+ require 'uffizzi/helpers/file_helper'
5
5
 
6
6
  module Uffizzi
7
7
  class ConfigFile
8
- CONFIG_PATH = "#{Dir.home}/.config/uffizzi/config_default"
8
+ CONFIG_PATH = "#{Dir.home}/.config/uffizzi/config_default.json"
9
9
 
10
10
  class << self
11
11
  def config_path
@@ -20,23 +20,25 @@ module Uffizzi
20
20
  File.exist?(config_path)
21
21
  end
22
22
 
23
- def read_option(option)
23
+ def read_option(option, nested_option = nil)
24
24
  data = read
25
- return nil unless data.is_a?(Hash)
26
25
 
27
- data[option]
26
+ value = data[option]
27
+ return value.presence if nested_option.nil?
28
+ return nil unless value.is_a?(Hash)
29
+
30
+ value[nested_option].presence
28
31
  end
29
32
 
30
33
  def option_has_value?(option)
31
34
  data = read
32
- return false if !data.is_a?(Hash) || !option_exists?(option)
35
+ return false unless option_exists?(option)
33
36
 
34
- !data[option].empty?
37
+ data[option].present?
35
38
  end
36
39
 
37
40
  def write_option(key, value)
38
41
  data = exists? ? read : {}
39
- return nil unless data.is_a?(Hash)
40
42
 
41
43
  data[key] = value
42
44
  write(data)
@@ -44,7 +46,7 @@ module Uffizzi
44
46
 
45
47
  def unset_option(key)
46
48
  data = read
47
- return nil unless data.is_a?(Hash) || !option_exists?(key)
49
+ return unless option_exists?(key)
48
50
 
49
51
  data[key] = ''
50
52
  write(data)
@@ -56,7 +58,6 @@ module Uffizzi
56
58
 
57
59
  def list
58
60
  data = read
59
- return nil unless data.is_a?(Hash)
60
61
 
61
62
  content = data.reduce('') do |acc, pair|
62
63
  property, value = pair
@@ -70,7 +71,6 @@ module Uffizzi
70
71
 
71
72
  def option_exists?(option)
72
73
  data = read
73
- return false unless data.is_a?(Hash)
74
74
 
75
75
  data.key?(option)
76
76
  end
@@ -79,43 +79,18 @@ module Uffizzi
79
79
 
80
80
  def read
81
81
  data = File.read(config_path)
82
- options = data.split("\n")
83
- options.reduce({}) do |acc, option|
84
- key, value = option.split('=', 2)
85
- acc.merge({ key.strip.to_sym => value.strip })
86
- end
82
+ JSON.parse(data).deep_symbolize_keys
87
83
  rescue Errno::ENOENT => e
88
84
  file_path = e.message.split(' ').last
89
85
  message = "Configuration file not found: #{file_path}\n" \
90
86
  'To configure the uffizzi CLI interactively, run $ uffizzi config'
91
87
  raise Uffizzi::Error.new(message)
88
+ rescue JSON::ParserError
89
+ {}
92
90
  end
93
91
 
94
92
  def write(data)
95
- prepared_data = prepare_data(data)
96
-
97
- lock(config_path) { atomic_write(config_path, "#{config_path}.tmp", prepared_data) }
98
- end
99
-
100
- def prepare_data(data)
101
- data.reduce('') do |acc, option|
102
- key, value = option
103
- "#{acc}#{key} = #{value}\n"
104
- end
105
- end
106
-
107
- def atomic_write(path, temp_path, content)
108
- File.open(temp_path, 'w') { |f| f.write(content) }
109
- FileUtils.mv(temp_path, path)
110
- end
111
-
112
- def lock(path)
113
- dir = File.dirname(path)
114
- FileUtils.mkdir_p(dir) unless File.directory?(dir)
115
-
116
- File.open(path).flock(File::LOCK_EX) if File.exist?(path)
117
- yield
118
- File.open(path).flock(File::LOCK_UN)
93
+ Uffizzi::FileHelper.write_with_lock(config_path, data.to_json)
119
94
  end
120
95
  end
121
96
  end
data/lib/uffizzi/error.rb CHANGED
@@ -1,5 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Uffizzi
4
+ RESPONSE_SERVER_ERROR_HEADER = "Server Error:\n"
5
+ CLI_ERROR_HEADER = "CLI Error:\n"
6
+
4
7
  class Error < Thor::Error; end
8
+
9
+ class ServerResponseError < Thor::Error
10
+ def initialize(message)
11
+ super("#{RESPONSE_SERVER_ERROR_HEADER}#{message}")
12
+ end
13
+ end
14
+
15
+ class CliError < Thor::Error
16
+ def initialize(message)
17
+ super("#{CLI_ERROR_HEADER}#{message}")
18
+ end
19
+ end
5
20
  end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Uffizzi
4
+ module ConfigHelper
5
+ CLUSTER_PARAMS = [:kubeconfig_path].freeze
6
+
7
+ class ConfigParamsError < StandardError
8
+ def initialize(unavailable_params, key)
9
+ msg = "These params #{unavailable_params.join(', ')} is not available for #{key}"
10
+
11
+ super(msg)
12
+ end
13
+ end
14
+
15
+ class << self
16
+ def read_option_from_config(option)
17
+ ConfigFile.option_has_value?(option) ? ConfigFile.read_option(option) : nil
18
+ end
19
+
20
+ def account_config(id, name = nil)
21
+ { id: id, name: name }
22
+ end
23
+
24
+ def update_clusters_config_by_id(id, params)
25
+ unavailable_params = params.keys - CLUSTER_PARAMS
26
+ raise ConfigParamsError.new(unavailable_params, :cluster) if unavailable_params.present?
27
+
28
+ current_cluster = cluster_config_by_id(id) || {}
29
+ new_current_cluster = current_cluster.merge({ id: id }).merge(params)
30
+
31
+ clusters_config_without(id) << new_current_cluster
32
+ end
33
+
34
+ def clusters_config_without(id)
35
+ clusters.reject { |c| c[:id] == id }
36
+ end
37
+
38
+ def cluster_config_by_id(id)
39
+ clusters.detect { |c| c[:id] == id }
40
+ end
41
+
42
+ private
43
+
44
+ def clusters
45
+ read_option_from_config(:clusters) || []
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Uffizzi
4
+ module FileHelper
5
+ class << self
6
+ def write_with_lock(path, data)
7
+ lock(path) { atomic_write(path, "#{path}.tmp", data) }
8
+ end
9
+
10
+ def atomic_write(path, temp_path, content)
11
+ File.open(temp_path, 'w') { |f| f.write(content) }
12
+ FileUtils.mv(temp_path, path)
13
+ end
14
+
15
+ def lock(path)
16
+ dir = File.dirname(path)
17
+ FileUtils.mkdir_p(dir) unless File.directory?(dir)
18
+
19
+ File.open(path).flock(File::LOCK_EX) if File.exist?(path)
20
+ yield
21
+ File.open(path).flock(File::LOCK_UN)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'uffizzi/helpers/config_helper'
4
+
5
+ module Uffizzi
6
+ module LoginHelper
7
+ class << self
8
+ def prepare_request_params(username, password)
9
+ {
10
+ user: {
11
+ email: username,
12
+ password: password,
13
+ },
14
+ }
15
+ end
16
+
17
+ def set_server(options)
18
+ config_server = ConfigFile.exists? ? Uffizzi::ConfigHelper.read_option_from_config(:server) : nil
19
+ server_address = (options[:server] || config_server || Uffizzi.ui.ask('Server:')).sub(/\/+$/, '')
20
+ server_address.start_with?('http:', 'https:') ? server_address : "https://#{server_address}"
21
+ end
22
+
23
+ def set_username(options)
24
+ config_username = ConfigFile.exists? ? Uffizzi::ConfigHelper.read_option_from_config(:username) : nil
25
+ options[:username] || config_username || Uffizzi.ui.ask('Username:')
26
+ end
27
+
28
+ def set_password
29
+ ENV['UFFIZZI_PASSWORD'] || Uffizzi.ui.ask('Password:', echo: false)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -1,9 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'faker'
4
+
3
5
  module Uffizzi
4
6
  module ProjectHelper
5
7
  SLUG_ENDING_LENGTH = 6
6
8
  class << self
9
+ def generate_default_params
10
+ name = generate_name
11
+ {
12
+ name: name,
13
+ description: default_description,
14
+ slug: generate_slug(name),
15
+ }
16
+ end
17
+
7
18
  def generate_slug(name)
8
19
  formatted_name = name.downcase.gsub(/ /, '-').gsub(/[^\w-]+/, '')
9
20
  slug_ending = generate_random_string(SLUG_ENDING_LENGTH)
@@ -17,6 +28,14 @@ module Uffizzi
17
28
  hexatridecimal_base = 36
18
29
  rand(hexatridecimal_base**length).to_s(hexatridecimal_base)
19
30
  end
31
+
32
+ def default_description
33
+ 'Project generated by Uffizzi'
34
+ end
35
+
36
+ def generate_name
37
+ Faker::Lorem.word
38
+ end
20
39
  end
21
40
  end
22
41
  end
data/lib/uffizzi/promt.rb CHANGED
@@ -16,6 +16,10 @@ module Uffizzi
16
16
  def ask(message, **args)
17
17
  @prompt.ask(message, **args)
18
18
  end
19
+
20
+ def yes?(message)
21
+ @prompt.yes?(message)
22
+ end
19
23
  end
20
24
  end
21
25
  end