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.
- checksums.yaml +4 -4
- data/config/uffizzi.rb +1 -0
- data/lib/uffizzi/auth_helper.rb +13 -5
- data/lib/uffizzi/cli/account.rb +122 -0
- data/lib/uffizzi/cli/cluster.rb +363 -0
- data/lib/uffizzi/cli/common.rb +2 -4
- data/lib/uffizzi/cli/config.rb +5 -4
- data/lib/uffizzi/cli/connect.rb +14 -8
- data/lib/uffizzi/cli/disconnect.rb +1 -1
- data/lib/uffizzi/cli/login.rb +130 -66
- data/lib/uffizzi/cli/login_by_identity_token.rb +11 -7
- data/lib/uffizzi/cli/preview/service.rb +3 -4
- data/lib/uffizzi/cli/preview.rb +33 -32
- data/lib/uffizzi/cli/project.rb +17 -9
- data/lib/uffizzi/cli.rb +46 -7
- data/lib/uffizzi/clients/api/api_client.rb +92 -2
- data/lib/uffizzi/clients/api/api_routes.rb +44 -0
- data/lib/uffizzi/clients/api/http_client.rb +19 -7
- data/lib/uffizzi/config_file.rb +15 -40
- data/lib/uffizzi/error.rb +15 -0
- data/lib/uffizzi/helpers/config_helper.rb +49 -0
- data/lib/uffizzi/helpers/file_helper.rb +25 -0
- data/lib/uffizzi/helpers/login_helper.rb +33 -0
- data/lib/uffizzi/helpers/project_helper.rb +19 -0
- data/lib/uffizzi/promt.rb +4 -0
- data/lib/uffizzi/response_helper.rb +33 -14
- data/lib/uffizzi/services/cluster_service.rb +59 -0
- data/lib/uffizzi/services/compose_file_service.rb +5 -4
- data/lib/uffizzi/services/env_variables_service.rb +4 -2
- data/lib/uffizzi/services/github_service.rb +21 -0
- data/lib/uffizzi/services/kubeconfig_service.rb +132 -0
- data/lib/uffizzi/services/preview_service.rb +52 -18
- data/lib/uffizzi/shell.rb +19 -16
- data/lib/uffizzi/token.rb +37 -0
- data/lib/uffizzi/version.rb +1 -1
- data/lib/uffizzi.rb +10 -0
- data/man/uffizzi +7 -5
- data/man/uffizzi-account +29 -0
- data/man/uffizzi-account-list +26 -0
- data/man/uffizzi-account-list.html +103 -0
- data/man/uffizzi-account-list.ronn +19 -0
- data/man/uffizzi-account-set-default +29 -0
- data/man/uffizzi-account-set-default.html +105 -0
- data/man/uffizzi-account-set-default.ronn +22 -0
- data/man/uffizzi-account.html +106 -0
- data/man/uffizzi-account.ronn +23 -0
- data/man/uffizzi-cluster +37 -0
- data/man/uffizzi-cluster-create +49 -0
- data/man/uffizzi-cluster-create.ronn +41 -0
- data/man/uffizzi-cluster-delete +32 -0
- data/man/uffizzi-cluster-delete.ronn +24 -0
- data/man/uffizzi-cluster-describe +39 -0
- data/man/uffizzi-cluster-describe.ronn +30 -0
- data/man/uffizzi-cluster-list +34 -0
- data/man/uffizzi-cluster-list.ronn +26 -0
- data/man/uffizzi-cluster-update-kubeconfig +37 -0
- data/man/uffizzi-cluster-update-kubeconfig.ronn +29 -0
- data/man/uffizzi-cluster.ronn +32 -0
- data/man/uffizzi-compose +39 -0
- data/man/uffizzi-compose-create +67 -0
- data/man/uffizzi-compose-create.ronn +57 -0
- data/man/uffizzi-compose-delete +38 -0
- data/man/uffizzi-compose-delete.ronn +29 -0
- data/man/uffizzi-compose-describe +38 -0
- data/man/uffizzi-compose-describe.ronn +29 -0
- data/man/uffizzi-compose-events +38 -0
- data/man/uffizzi-compose-events.ronn +29 -0
- data/man/uffizzi-compose-list +58 -0
- data/man/uffizzi-compose-list.ronn +49 -0
- data/man/uffizzi-compose-service-list +42 -0
- data/man/uffizzi-compose-service-list.ronn +32 -0
- data/man/uffizzi-compose-service-logs +59 -0
- data/man/uffizzi-compose-service-logs.ronn +48 -0
- data/man/uffizzi-compose-update +61 -0
- data/man/uffizzi-compose-update.ronn +51 -0
- data/man/uffizzi-compose.ronn +33 -0
- data/man/uffizzi-compose_service_logs +59 -0
- data/man/uffizzi-compose_service_logs.ronn +50 -0
- data/man/uffizzi-config +2 -2
- data/man/uffizzi-config.ronn +1 -1
- data/man/uffizzi-connect +2 -4
- data/man/uffizzi-connect-acr +2 -2
- data/man/uffizzi-connect-acr.ronn +1 -1
- data/man/uffizzi-connect-docker-hub +2 -2
- data/man/uffizzi-connect-docker-hub.ronn +1 -1
- data/man/uffizzi-connect-docker-registry +2 -2
- data/man/uffizzi-connect-docker-registry.ronn +1 -1
- data/man/uffizzi-connect-ecr +2 -2
- data/man/uffizzi-connect-ecr.ronn +1 -1
- data/man/uffizzi-connect-gcr +2 -2
- data/man/uffizzi-connect-gcr.ronn +1 -1
- data/man/uffizzi-connect-ghcr +2 -2
- data/man/uffizzi-connect-ghcr.ronn +1 -1
- data/man/uffizzi-connect.ronn +2 -2
- data/man/uffizzi-disconnect +2 -2
- data/man/uffizzi-disconnect.ronn +1 -1
- data/man/uffizzi-login +7 -3
- data/man/uffizzi-login-by-identity-token +3 -3
- data/man/uffizzi-login-by-identity-token.ronn +2 -2
- data/man/uffizzi-login.html +113 -0
- data/man/uffizzi-login.ronn +6 -2
- data/man/uffizzi-logout +2 -2
- data/man/uffizzi-logout.ronn +1 -1
- data/man/uffizzi-preview +9 -9
- data/man/uffizzi-preview-create +22 -20
- data/man/uffizzi-preview-create.ronn +28 -26
- data/man/uffizzi-preview-delete +11 -10
- data/man/uffizzi-preview-delete.ronn +12 -11
- data/man/uffizzi-preview-describe +10 -10
- data/man/uffizzi-preview-describe.ronn +11 -11
- data/man/uffizzi-preview-events +12 -11
- data/man/uffizzi-preview-events.ronn +13 -12
- data/man/uffizzi-preview-list +19 -18
- data/man/uffizzi-preview-list.ronn +21 -20
- data/man/uffizzi-preview-service-list +16 -12
- data/man/uffizzi-preview-service-list.ronn +16 -13
- data/man/uffizzi-preview-service-logs +14 -12
- data/man/uffizzi-preview-service-logs.ronn +18 -17
- data/man/uffizzi-preview-update +19 -18
- data/man/uffizzi-preview-update.ronn +21 -20
- data/man/uffizzi-preview.ronn +10 -10
- data/man/uffizzi-preview_service_logs +14 -12
- data/man/uffizzi-preview_service_logs.ronn +18 -17
- data/man/uffizzi-project +3 -3
- data/man/uffizzi-project-compose +2 -2
- data/man/uffizzi-project-compose-describe +2 -2
- data/man/uffizzi-project-compose-describe.ronn +1 -1
- data/man/uffizzi-project-compose-set +2 -2
- data/man/uffizzi-project-compose-set.ronn +1 -1
- data/man/uffizzi-project-compose-unset +2 -2
- data/man/uffizzi-project-compose-unset.ronn +1 -1
- data/man/uffizzi-project-compose.ronn +1 -1
- data/man/uffizzi-project-create +2 -2
- data/man/uffizzi-project-create.ronn +1 -1
- data/man/uffizzi-project-delete +2 -2
- data/man/uffizzi-project-delete.ronn +1 -1
- data/man/uffizzi-project-describe +3 -3
- data/man/uffizzi-project-describe.ronn +1 -1
- data/man/uffizzi-project-preview-describe +37 -0
- data/man/uffizzi-project-preview-describe.ronn +29 -0
- data/man/uffizzi-project-preview-set +66 -0
- data/man/uffizzi-project-preview-set.ronn +57 -0
- data/man/uffizzi-project-secret +2 -2
- data/man/uffizzi-project-secret-create +2 -2
- data/man/uffizzi-project-secret-create.ronn +1 -1
- data/man/uffizzi-project-secret-delete +2 -2
- data/man/uffizzi-project-secret-delete.ronn +1 -1
- data/man/uffizzi-project-secret-list +2 -2
- data/man/uffizzi-project-secret-list.ronn +1 -1
- data/man/uffizzi-project-secret.ronn +1 -1
- data/man/uffizzi-project-set-default +2 -2
- data/man/uffizzi-project-set-default.ronn +1 -1
- data/man/uffizzi-project.html +124 -0
- data/man/uffizzi-project.ronn +2 -2
- data/man/uffizzi.ronn +12 -10
- 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 :
|
|
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 '
|
|
55
|
+
desc 'compose', 'compose'
|
|
50
56
|
method_option :project, required: false
|
|
51
57
|
require_relative 'cli/preview'
|
|
52
|
-
subcommand '
|
|
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
|
-
|
|
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
|
|
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
|
|
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(
|
|
11
|
-
@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
|
-
|
|
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
|
-
|
|
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
|
data/lib/uffizzi/config_file.rb
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'json'
|
|
4
|
-
require '
|
|
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
|
|
35
|
+
return false unless option_exists?(option)
|
|
33
36
|
|
|
34
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|