uffizzi-cli 1.0.4 → 2.0.29

Sign up to get free protection for your applications and to get access to all the features.
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