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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5aa0242e35791e35a51a850316bb18a97f1651aa345c64ee05264cb6b7faa702
4
- data.tar.gz: bc9a8c8176c2969edf8728be715a0d45c38dab6a3d0e6da37c8022e04d9e8e24
3
+ metadata.gz: b5c8fa0993d5a84fc2dd50fc8d10bdb64c4d4dd20eb096ef02250a00ebc32881
4
+ data.tar.gz: 3bdd433aa4689010d8e6f3e9aa75477c02a02069ab6dabca3a0880cbfc519699
5
5
  SHA512:
6
- metadata.gz: 993d29e07d6fe739cd3ab82383a948ed2d251f7b00662b02b87cd3a9b1b556ee2e1ed16f224f857ce0689ac64b84d02b74aa711f24f477d546d1f13c378938a1
7
- data.tar.gz: d91befdb081561d8395800f672918518397a70734abc3ff1ec2b57faa0180063f808722afc14de85e056507972d97eb4ef774614d1dd72a54311f5f34629abf0
6
+ metadata.gz: b217ef6cbeede8834a7bdb5bc2aef7964d1d3d12fc8c4418a2a81e683f1d690791e1053ca3c4020417e409f77f0a3935ab110e8d9666a65817ef1bd4fa947363
7
+ data.tar.gz: 2d4aa3181ceaa9cba36b8ed49238fdb226674b5d2e88185e3b0c019cad73f24226b3b8e9117df48cdb07c9219a2bc7884edba0fa20457e5094ee5d28120d2ffe
data/config/uffizzi.rb CHANGED
@@ -22,5 +22,6 @@ module Uffizzi
22
22
  docker_registry: 'UffizziCore::Credential::DockerRegistry',
23
23
  }
24
24
  config.default_server = 'app.uffizzi.com'
25
+ config.default_kubeconfig_path = ENV['CLI_DEFAULT_KUBECONFIG_PATH'] || '~/.kube/config'
25
26
  end
26
27
  end
@@ -4,15 +4,23 @@ module Uffizzi
4
4
  module AuthHelper
5
5
  class << self
6
6
  def signed_in?
7
- ConfigFile.exists? &&
8
- ConfigFile.option_has_value?(:account_id) &&
9
- ConfigFile.option_has_value?(:cookie) &&
10
- ConfigFile.option_has_value?(:server)
7
+ config_data_exists? || Uffizzi::Token.exists?
11
8
  end
12
9
 
13
10
  def sign_out
14
11
  Uffizzi::ConfigFile.unset_option(:cookie)
15
- Uffizzi::ConfigFile.unset_option(:account_id)
12
+ Uffizzi::ConfigFile.unset_option(:account)
13
+ Uffizzi::ConfigFile.unset_option(:project)
14
+ Uffizzi::Token.delete if Uffizzi::Token.exists?
15
+ end
16
+
17
+ private
18
+
19
+ def config_data_exists?
20
+ ConfigFile.exists? &&
21
+ ConfigFile.option_has_value?(:account) &&
22
+ ConfigFile.option_has_value?(:cookie) &&
23
+ ConfigFile.option_has_value?(:server)
16
24
  end
17
25
  end
18
26
  end
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'uffizzi'
4
+ require 'uffizzi/auth_helper'
5
+ require 'uffizzi/response_helper'
6
+ require 'uffizzi/helpers/config_helper'
7
+
8
+ module Uffizzi
9
+ class Cli::Account < Thor
10
+ include ApiClient
11
+
12
+ desc 'list', "List all user's accounts"
13
+ def list
14
+ run('list')
15
+ end
16
+
17
+ desc 'set-default ACCOUNT_NAME', 'set-default'
18
+ def set_default(account_name)
19
+ run('set-default', account_name)
20
+ end
21
+
22
+ map('set-default' => :set_default)
23
+ map('set' => :set_default)
24
+
25
+ private
26
+
27
+ def run(command, account_name = nil)
28
+ return Uffizzi.ui.say('You are not logged in.') unless Uffizzi::AuthHelper.signed_in?
29
+
30
+ case command
31
+ when 'list'
32
+ handle_list_command
33
+ when 'set-default'
34
+ handle_set_default_command(account_name)
35
+ end
36
+ end
37
+
38
+ def handle_list_command
39
+ server = ConfigFile.read_option(:server)
40
+ response = fetch_accounts(server)
41
+
42
+ if ResponseHelper.ok?(response)
43
+ handle_list_success_response(response)
44
+ else
45
+ ResponseHelper.handle_failed_response(response)
46
+ end
47
+ end
48
+
49
+ def handle_set_default_command(account_name)
50
+ response = fetch_account(ConfigFile.read_option(:server), account_name)
51
+
52
+ if ResponseHelper.ok?(response)
53
+ handle_succeed_set_default_response(response)
54
+ elsif ResponseHelper.not_found?(response)
55
+ Uffizzi.ui.say("Account with name #{account_name} does not exist")
56
+ else
57
+ ResponseHelper.handle_failed_response(response)
58
+ end
59
+ end
60
+
61
+ def handle_list_success_response(response)
62
+ accounts = response[:body][:accounts]
63
+ return Uffizzi.ui.say('No accounts found') if accounts.empty?
64
+
65
+ print_accounts(accounts)
66
+ end
67
+
68
+ def print_accounts(accounts)
69
+ accounts_list = accounts.reduce('') do |acc, account|
70
+ "#{acc}#{account[:name]}\n"
71
+ end
72
+ Uffizzi.ui.say(accounts_list)
73
+ end
74
+
75
+ def handle_succeed_set_default_response(response)
76
+ account = response[:body][:account]
77
+ account_id = account[:id]
78
+ account_name = account[:name]
79
+ ConfigFile.write_option(:account, Uffizzi::ConfigHelper.account_config(account_id, account_name))
80
+ Uffizzi.ui.say("The account with name '#{account_name}' was set as default.")
81
+
82
+ projects = account[:projects]
83
+
84
+ project = default_project(projects, account_id)
85
+ if project.nil?
86
+ ConfigFile.unset_option(:project)
87
+ message = "There is no project set. Run the 'uffizzi project set-default' command to set a project"
88
+ return Uffizzi.ui.say(message)
89
+ end
90
+
91
+ slug = project[:slug]
92
+ ConfigFile.write_option(:project, slug)
93
+ Uffizzi.ui.say("The project with slug '#{slug}' was set as default.")
94
+ end
95
+
96
+ def default_project(projects, account_id)
97
+ return projects.first if projects.count == 1
98
+ return create_default_project(account_id) if projects.count.zero?
99
+
100
+ nil
101
+ end
102
+
103
+ def create_default_project(account_id)
104
+ params = Uffizzi::ProjectHelper.generate_default_params
105
+ response = create_project(ConfigFile.read_option(:server), account_id, params)
106
+
107
+ if ResponseHelper.created?(response)
108
+ handle_project_create_success_response(response)
109
+ else
110
+ ResponseHelper.handle_failed_response(response)
111
+ end
112
+ end
113
+
114
+ def handle_project_create_success_response(response)
115
+ project = response[:body][:project]
116
+ ConfigFile.write_option(:project, project[:slug])
117
+ Uffizzi.ui.say("A default project '#{project[:name]}' was successfully created")
118
+
119
+ project
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,363 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'psych'
4
+ require 'faker'
5
+ require 'uffizzi'
6
+ require 'uffizzi/auth_helper'
7
+ require 'uffizzi/helpers/config_helper'
8
+ require 'uffizzi/services/preview_service'
9
+ require 'uffizzi/services/command_service'
10
+ require 'uffizzi/services/cluster_service'
11
+ require 'uffizzi/services/kubeconfig_service'
12
+
13
+ MANUAL = 'manual'
14
+
15
+ module Uffizzi
16
+ class Cli::Cluster < Thor
17
+ class Error < StandardError; end
18
+ include ApiClient
19
+
20
+ desc 'list', 'List all clusters'
21
+ method_option :all, required: false, type: :boolean, aliases: '-a'
22
+ method_option :output, required: false, type: :string, aliases: '-o', enum: ['json', 'pretty-json']
23
+ def list
24
+ run('list')
25
+ end
26
+
27
+ desc 'create', 'Create a cluster'
28
+ method_option :name, type: :string, required: false, aliases: '-n'
29
+ method_option :kubeconfig, type: :string, required: false, aliases: '-k'
30
+ method_option :manifest, type: :string, required: false, aliases: '-m'
31
+ method_option :'update-current-context', type: :boolean, required: false
32
+ method_option :output, required: false, type: :string, aliases: '-o', enum: ['json', 'pretty-json']
33
+ method_option :'creation-source', required: false, type: :string
34
+ def create
35
+ run('create')
36
+ end
37
+
38
+ desc 'describe [NAME]', 'Describe a cluster'
39
+ method_option :output, required: false, type: :string, aliases: '-o', enum: ['json', 'pretty-json']
40
+ def describe(name)
41
+ run('describe', cluster_name: name)
42
+ end
43
+
44
+ desc 'delete [NAME]', 'Delete a cluster'
45
+ method_option :'delete-config', required: false, type: :boolean, aliases: '-dc'
46
+ def delete(name)
47
+ run('delete', cluster_name: name)
48
+ end
49
+
50
+ method_option :kubeconfig, type: :string, required: false, aliases: '-k'
51
+ method_option :print, type: :boolean, required: false, aliases: '-p'
52
+ method_option :quiet, type: :boolean, required: false, aliases: '-q'
53
+ desc 'update-kubeconfig', 'Udpate your kubeconfig'
54
+ def update_kubeconfig(name)
55
+ run('update-kubeconfig', cluster_name: name)
56
+ end
57
+
58
+ private
59
+
60
+ def run(command, command_args = {})
61
+ Uffizzi.ui.output_format = options[:output]
62
+ raise Uffizzi::Error.new('You are not logged in.') unless Uffizzi::AuthHelper.signed_in?
63
+ raise Uffizzi::Error.new('This command needs project to be set in config file') unless CommandService.project_set?(options)
64
+
65
+ project_slug = options[:project].nil? ? ConfigFile.read_option(:project) : options[:project]
66
+
67
+ case command
68
+ when 'list'
69
+ handle_list_command(project_slug)
70
+ when 'create'
71
+ handle_create_command(project_slug)
72
+ when 'describe'
73
+ handle_describe_command(project_slug, command_args)
74
+ when 'delete'
75
+ handle_delete_command(project_slug, command_args)
76
+ when 'update-kubeconfig'
77
+ handle_update_kubeconfig_command(project_slug, command_args)
78
+ end
79
+ end
80
+
81
+ def handle_list_command(project_slug)
82
+ is_all = options[:all]
83
+ response = if is_all
84
+ get_account_clusters(ConfigFile.read_option(:server), ConfigFile.read_option(:account, :id))
85
+ else
86
+ oidc_token = ConfigFile.read_option(:oidc_token)
87
+ get_project_clusters(ConfigFile.read_option(:server), project_slug, oidc_token: oidc_token)
88
+ end
89
+
90
+ if ResponseHelper.ok?(response)
91
+ handle_succeed_list_response(response)
92
+ else
93
+ ResponseHelper.handle_failed_response(response)
94
+ end
95
+ end
96
+
97
+ def handle_create_command(project_slug)
98
+ Uffizzi.ui.disable_stdout if Uffizzi.ui.output_format
99
+ cluster_name = options[:name] || ClusterService.generate_name
100
+ creation_source = options[:"creation-source"] || MANUAL
101
+
102
+ unless ClusterService.valid_name?(cluster_name)
103
+ Uffizzi.ui.say_error_and_exit("Cluster name: #{cluster_name} is not valid.")
104
+ end
105
+
106
+ manifest_file_path = options[:manifest]
107
+ params = cluster_creation_params(cluster_name, creation_source, manifest_file_path)
108
+ response = create_cluster(ConfigFile.read_option(:server), project_slug, params)
109
+
110
+ return ResponseHelper.handle_failed_response(response) unless ResponseHelper.created?(response)
111
+
112
+ spinner = TTY::Spinner.new("[:spinner] Creating cluster #{cluster_name}...", format: :dots)
113
+ spinner.auto_spin
114
+ cluster_data = ClusterService.wait_cluster_deploy(project_slug, cluster_name, ConfigFile.read_option(:oidc_token))
115
+
116
+ if ClusterService.failed?(cluster_data[:state])
117
+ spinner.error
118
+ Uffizzi.ui.say_error_and_exit("Cluster with name: #{cluster_name} failed to be created.")
119
+ end
120
+
121
+ spinner.success
122
+ handle_succeed_create_response(cluster_data)
123
+ rescue SystemExit, Interrupt, SocketError
124
+ handle_interrupt_creation(cluster_name, ConfigFile.read_option(:server), project_slug)
125
+ end
126
+
127
+ def handle_describe_command(project_slug, command_args)
128
+ cluster_data = fetch_cluster_data(project_slug, command_args[:cluster_name])
129
+
130
+ handle_succeed_describe(cluster_data)
131
+ end
132
+
133
+ def handle_delete_command(project_slug, command_args)
134
+ cluster_name = command_args[:cluster_name]
135
+ is_delete_kubeconfig = options[:'delete-config']
136
+
137
+ return handle_delete_cluster(project_slug, cluster_name) unless is_delete_kubeconfig
138
+
139
+ cluster_data = fetch_cluster_data(project_slug, cluster_name)
140
+ kubeconfig = parse_kubeconfig(cluster_data[:kubeconfig])
141
+
142
+ handle_delete_cluster(project_slug, cluster_name)
143
+ exclude_kubeconfig(cluster_data[:id], kubeconfig)
144
+ end
145
+
146
+ def exclude_kubeconfig(cluster_id, kubeconfig)
147
+ cluster_config = Uffizzi::ConfigHelper.cluster_config_by_id(cluster_id)
148
+ return if cluster_config.nil?
149
+
150
+ kubeconfig_path = cluster_config[:kubeconfig_path]
151
+ ConfigFile.write_option(:clusters, Uffizzi::ConfigHelper.clusters_config_without(cluster_id))
152
+
153
+ KubeconfigService.save_to_filepath(kubeconfig_path, kubeconfig) do |kubeconfig_by_path|
154
+ if kubeconfig_by_path.nil?
155
+ msg = "Warning: kubeconfig at path #{kubeconfig_path} does not exist"
156
+ return Uffizzi.ui.say(msg)
157
+ end
158
+
159
+ new_kubeconfig = KubeconfigService.exclude(kubeconfig_by_path, kubeconfig)
160
+ first_context = KubeconfigService.get_first_context(new_kubeconfig)
161
+ new_current_context = first_context.present? ? first_context['name'] : nil
162
+ KubeconfigService.update_current_context(new_kubeconfig, new_current_context)
163
+ end
164
+ end
165
+
166
+ def handle_delete_cluster(project_slug, cluster_name)
167
+ params = {
168
+ cluster_name: cluster_name,
169
+ oidc_token: ConfigFile.read_option(:oidc_token),
170
+ }
171
+ response = delete_cluster(ConfigFile.read_option(:server), project_slug, params)
172
+
173
+ if ResponseHelper.no_content?(response)
174
+ Uffizzi.ui.say("Cluster #{cluster_name} deleted")
175
+ else
176
+ ResponseHelper.handle_failed_response(response)
177
+ end
178
+ end
179
+
180
+ def handle_update_kubeconfig_command(project_slug, command_args)
181
+ kubeconfig_path = options[:kubeconfig] || KubeconfigService.default_path
182
+ cluster_data = fetch_cluster_data(project_slug, command_args[:cluster_name])
183
+
184
+ unless cluster_data[:kubeconfig].present?
185
+ say_error_update_kubeconfig(cluster_data)
186
+ end
187
+
188
+ parsed_kubeconfig = parse_kubeconfig(cluster_data[:kubeconfig])
189
+
190
+ return Uffizzi.ui.say(parsed_kubeconfig.to_yaml) if options[:print]
191
+
192
+ KubeconfigService.save_to_filepath(kubeconfig_path, parsed_kubeconfig) do |kubeconfig_by_path|
193
+ merged_kubeconfig = KubeconfigService.merge(kubeconfig_by_path, parsed_kubeconfig)
194
+ current_context = KubeconfigService.get_current_context(parsed_kubeconfig)
195
+ KubeconfigService.update_current_context(merged_kubeconfig, current_context)
196
+ end
197
+
198
+ update_clusters_config(cluster_data[:id], kubeconfig_path: kubeconfig_path)
199
+
200
+ return if options[:quiet]
201
+
202
+ Uffizzi.ui.say("Kubeconfig was updated by the path: #{kubeconfig_path}")
203
+ end
204
+
205
+ def say_error_update_kubeconfig(cluster_data)
206
+ if ClusterService.failed?(cluster_data[:state])
207
+ Uffizzi.ui.say_error_and_exit('Kubeconfig is empty because cluster failed to be created.')
208
+ end
209
+
210
+ if ClusterService.deploying?(cluster_data[:state])
211
+ Uffizzi.ui.say_error_and_exit('Kubeconfig is empty because cluster is deploying.')
212
+ end
213
+
214
+ if ClusterService.deployed?(cluster_data[:state])
215
+ raise Error.new("Cluster with data: #{cluster_data.to_json} is deployed but kubeconfig does not exist.")
216
+ end
217
+ end
218
+
219
+ def cluster_creation_params(name, creation_source, manifest_file_path)
220
+ manifest_content = load_manifest_file(manifest_file_path)
221
+ oidc_token = Uffizzi::ConfigFile.read_option(:oidc_token)
222
+
223
+ {
224
+ cluster: {
225
+ name: name,
226
+ manifest: manifest_content,
227
+ creation_source: creation_source,
228
+ },
229
+ token: oidc_token,
230
+ }
231
+ end
232
+
233
+ def load_manifest_file(file_path)
234
+ return nil if file_path.nil?
235
+
236
+ File.read(file_path)
237
+ rescue Errno::ENOENT => e
238
+ raise Uffizzi::Error.new(e.message)
239
+ end
240
+
241
+ def handle_interrupt_creation(cluster_name, server, project_slug)
242
+ deletion_response = delete_cluster(server, project_slug, cluster_name: cluster_name)
243
+ deletion_message = if ResponseHelper.no_content?(deletion_response)
244
+ "The cluster #{cluster_name} has been disabled."
245
+ else
246
+ "Couldn't disable the cluster #{cluster_name} - please disable manually."
247
+ end
248
+
249
+ raise Uffizzi::Error.new("The cluster creation was interrupted. #{deletion_message}")
250
+ end
251
+
252
+ def handle_succeed_list_response(response)
253
+ clusters = response[:body][:clusters] || []
254
+ raise Uffizzi::Error.new('The project has no active clusters') if clusters.empty?
255
+
256
+ clusters_data = if Uffizzi.ui.output_format.nil?
257
+ render_plain_cluster_list(clusters)
258
+ else
259
+ clusters.map { |c| c.slice(:name, :project) }
260
+ end
261
+
262
+ Uffizzi.ui.say(clusters_data)
263
+ end
264
+
265
+ def render_plain_cluster_list(clusters)
266
+ clusters.map do |cluster|
267
+ project_name = cluster.dig(:project, :name)
268
+
269
+ if project_name.present?
270
+ "- Cluster name: #{cluster[:name].strip} Project name: #{project_name.strip}"
271
+ else
272
+ "- #{cluster[:name]}"
273
+ end
274
+ end.join("\n")
275
+ end
276
+
277
+ def handle_succeed_describe(cluster_data)
278
+ prepared_cluster_data = {
279
+ name: cluster_data[:name],
280
+ status: cluster_data[:state],
281
+ created: Time.strptime(cluster_data[:created_at], '%Y-%m-%dT%H:%M:%S.%N').strftime('%a %b %d %H:%M:%S %Y'),
282
+ url: cluster_data[:host],
283
+ }
284
+
285
+ rendered_cluster_data = if Uffizzi.ui.output_format.nil?
286
+ prepared_cluster_data.map { |k, v| "- #{k.to_s.upcase}: #{v}" }.join("\n").strip
287
+ else
288
+ prepared_cluster_data
289
+ end
290
+
291
+ Uffizzi.ui.say(rendered_cluster_data)
292
+ end
293
+
294
+ def handle_succeed_create_response(cluster_data)
295
+ kubeconfig_path = options[:kubeconfig]
296
+ is_update_current_context = options[:'update-current-context']
297
+ parsed_kubeconfig = parse_kubeconfig(cluster_data[:kubeconfig])
298
+ rendered_cluster_data = render_cluster_data(cluster_data)
299
+
300
+ Uffizzi.ui.enable_stdout
301
+ Uffizzi.ui.say("Cluster with name: #{rendered_cluster_data[:name]} was created.")
302
+
303
+ unless is_update_current_context
304
+ Uffizzi.ui.say("To update the current context, run:\nuffizzi cluster update-kubeconfig #{cluster_data[:name]}")
305
+ end
306
+
307
+ Uffizzi.ui.say(rendered_cluster_data) if Uffizzi.ui.output_format
308
+
309
+ save_kubeconfig(parsed_kubeconfig, kubeconfig_path)
310
+ update_clusters_config(cluster_data[:id], kubeconfig_path: kubeconfig_path)
311
+ GithubService.write_to_github_env(rendered_cluster_data) if GithubService.github_actions_exists?
312
+ end
313
+
314
+ def save_kubeconfig(kubeconfig, kubeconfig_path)
315
+ kubeconfig_path = kubeconfig_path.nil? ? KubeconfigService.default_path : kubeconfig_path
316
+ is_update_current_context = options[:'update-current-context']
317
+
318
+ KubeconfigService.save_to_filepath(kubeconfig_path, kubeconfig) do |kubeconfig_by_path|
319
+ merged_kubeconfig = KubeconfigService.merge(kubeconfig_by_path, kubeconfig)
320
+
321
+ if is_update_current_context
322
+ current_context = KubeconfigService.get_current_context(kubeconfig)
323
+ KubeconfigService.update_current_context(merged_kubeconfig, current_context)
324
+ else
325
+ merged_kubeconfig
326
+ end
327
+ end
328
+ end
329
+
330
+ def update_clusters_config(id, params)
331
+ clusters_config = Uffizzi::ConfigHelper.update_clusters_config_by_id(id, params)
332
+ ConfigFile.write_option(:clusters, clusters_config)
333
+ end
334
+
335
+ def render_cluster_data(cluster_data)
336
+ kubeconfig = parse_kubeconfig(cluster_data[:kubeconfig])
337
+ raise Uffizzi::Error.new('The kubeconfig data is empty') unless kubeconfig
338
+
339
+ new_cluster_data = cluster_data.slice(:name)
340
+ new_cluster_data[:context_name] = kubeconfig['current-context']
341
+
342
+ new_cluster_data
343
+ end
344
+
345
+ def parse_kubeconfig(kubeconfig)
346
+ Psych.safe_load(Base64.decode64(kubeconfig))
347
+ end
348
+
349
+ def fetch_cluster_data(project_slug, cluster_name)
350
+ params = {
351
+ cluster_name: cluster_name,
352
+ oidc_token: ConfigFile.read_option(:oidc_token),
353
+ }
354
+ response = get_cluster(ConfigFile.read_option(:server), project_slug, params)
355
+
356
+ if ResponseHelper.ok?(response)
357
+ response.dig(:body, :cluster)
358
+ else
359
+ ResponseHelper.handle_failed_response(response)
360
+ end
361
+ end
362
+ end
363
+ end
@@ -1,14 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'open3'
4
-
5
3
  module Uffizzi
6
4
  class Cli::Common
7
5
  class << self
8
6
  def show_manual(command_name)
9
- manual_doc_path = File.join(Uffizzi.root, "man/#{command_name}")
7
+ manual_doc_path = File.join(Uffizzi.root, "man/#{command_name}.ronn")
10
8
 
11
- Open3.pipeline("man #{manual_doc_path}")
9
+ Uffizzi.ui.say(File.read(manual_doc_path))
12
10
  end
13
11
  end
14
12
  end
@@ -5,6 +5,7 @@ require 'uffizzi/clients/api/api_client'
5
5
 
6
6
  module Uffizzi
7
7
  class Cli::Config < Thor
8
+ ACCOUNT_PROPERTY = 'account'
8
9
  include ApiClient
9
10
 
10
11
  desc 'list', 'Lists all options and their values from the config file'
@@ -57,12 +58,10 @@ module Uffizzi
57
58
  Uffizzi.ui.say("Configure the default properties that will be used to authenticate with your \
58
59
  \nUffizzi API service and manage previews.\n")
59
60
  server = Uffizzi.ui.ask('Server: ', default: Uffizzi.configuration.default_server.to_s)
60
- username = Uffizzi.ui.ask('Username: ')
61
- project = Uffizzi.ui.ask('Project: ', default: 'default')
61
+
62
62
  ConfigFile.delete
63
63
  ConfigFile.write_option(:server, server)
64
- ConfigFile.write_option(:username, username)
65
- ConfigFile.write_option(:project, project)
64
+
66
65
  Uffizzi.ui.say('To login, run: uffizzi login')
67
66
  end
68
67
 
@@ -78,6 +77,8 @@ module Uffizzi
78
77
  end
79
78
 
80
79
  def handle_set_command(property, value)
80
+ return handle_set_account(value) if property == ACCOUNT_PROPERTY
81
+
81
82
  ConfigFile.write_option(property.to_sym, value)
82
83
  Uffizzi.ui.say("Updated property [#{property}]")
83
84
  end
@@ -10,7 +10,7 @@ module Uffizzi
10
10
  desc 'list-credentials', 'List existing credentials for an account'
11
11
  def list_credentials
12
12
  server = ConfigFile.read_option(:server)
13
- account_id = ConfigFile.read_option(:account_id)
13
+ account_id = ConfigFile.read_option(:account, :id)
14
14
  response = fetch_credentials(server, account_id)
15
15
  if ResponseHelper.ok?(response)
16
16
  handle_list_credentials_success(response)
@@ -36,9 +36,10 @@ module Uffizzi
36
36
  username: username,
37
37
  password: password,
38
38
  type: type,
39
+ token: ConfigFile.read_option(:oidc_token),
39
40
  }
40
41
  server = ConfigFile.read_option(:server)
41
- account_id = ConfigFile.read_option(:account_id)
42
+ account_id = ConfigFile.read_option(:account, :id)
42
43
 
43
44
  response = if credential_exists
44
45
  update_credential(server, account_id, params, type)
@@ -68,9 +69,10 @@ module Uffizzi
68
69
  username: username,
69
70
  password: password,
70
71
  type: type,
72
+ token: ConfigFile.read_option(:oidc_token),
71
73
  }
72
74
  server = ConfigFile.read_option(:server)
73
- account_id = ConfigFile.read_option(:account_id)
75
+ account_id = ConfigFile.read_option(:account, :id)
74
76
 
75
77
  response = if credential_exists
76
78
  update_credential(server, account_id, params, type)
@@ -100,9 +102,10 @@ module Uffizzi
100
102
  password: password,
101
103
  registry_url: prepare_registry_url(registry_url),
102
104
  type: type,
105
+ token: ConfigFile.read_option(:oidc_token),
103
106
  }
104
107
  server = ConfigFile.read_option(:server)
105
- account_id = ConfigFile.read_option(:account_id)
108
+ account_id = ConfigFile.read_option(:account, :id)
106
109
 
107
110
  response = if credential_exists
108
111
  update_credential(server, account_id, params, type)
@@ -132,9 +135,10 @@ module Uffizzi
132
135
  password: secret_access_key,
133
136
  registry_url: prepare_registry_url(registry_url),
134
137
  type: type,
138
+ token: ConfigFile.read_option(:oidc_token),
135
139
  }
136
140
  server = ConfigFile.read_option(:server)
137
- account_id = ConfigFile.read_option(:account_id)
141
+ account_id = ConfigFile.read_option(:account, :id)
138
142
 
139
143
  response = if credential_exists
140
144
  update_credential(server, account_id, params, type)
@@ -159,9 +163,10 @@ module Uffizzi
159
163
  params = {
160
164
  password: credential_content,
161
165
  type: type,
166
+ token: ConfigFile.read_option(:oidc_token),
162
167
  }
163
168
  server = ConfigFile.read_option(:server)
164
- account_id = ConfigFile.read_option(:account_id)
169
+ account_id = ConfigFile.read_option(:account, :id)
165
170
 
166
171
  response = if credential_exists
167
172
  update_credential(server, account_id, params, type)
@@ -189,9 +194,10 @@ module Uffizzi
189
194
  username: username,
190
195
  password: password,
191
196
  type: type,
197
+ token: ConfigFile.read_option(:oidc_token),
192
198
  }
193
199
  server = ConfigFile.read_option(:server)
194
- account_id = ConfigFile.read_option(:account_id)
200
+ account_id = ConfigFile.read_option(:account, :id)
195
201
 
196
202
  response = if credential_exists
197
203
  update_credential(server, account_id, params, type)
@@ -228,7 +234,7 @@ module Uffizzi
228
234
 
229
235
  def credential_exists?(type)
230
236
  server = ConfigFile.read_option(:server)
231
- account_id = ConfigFile.read_option(:account_id)
237
+ account_id = ConfigFile.read_option(:account, :id)
232
238
  response = check_credential(server, account_id, type)
233
239
  return false if ResponseHelper.ok?(response)
234
240
  return true if ResponseHelper.unprocessable_entity?(response)
@@ -24,7 +24,7 @@ module Uffizzi
24
24
  raise Uffizzi::Error.new('Unsupported credential type.')
25
25
  end
26
26
 
27
- response = delete_credential(ConfigFile.read_option(:server), ConfigFile.read_option(:account_id), connection_type)
27
+ response = delete_credential(ConfigFile.read_option(:server), ConfigFile.read_option(:account, :id), connection_type)
28
28
 
29
29
  if ResponseHelper.no_content?(response)
30
30
  Uffizzi.ui.say("Successfully disconnected from #{connection_name(credential_type)}.")