uffizzi-cli 2.3.3 → 2.3.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 27943054f960693c2d1b8539a823f15f28e0f12d5e4763440cba3ee2f03f3412
4
- data.tar.gz: 352f258d3a5a3d40b21e4d2c10d01028c4e16ec12a8902a3995cb3a64b687d1f
3
+ metadata.gz: 9881a62c56ed3ef3171721ef7e6f6374482f4068f268aa1f5a0b5864b6c8cec6
4
+ data.tar.gz: cc60bbc447f3f30759a74fa7fa916aab28134b9e05d97ecd1f0a69c8ba042cae
5
5
  SHA512:
6
- metadata.gz: 025a64d8bc2e14ad93b21592d0b1ad5460e960019ef119fbf5afc78622317c0fb600f16fe4132f3bab6fb9c652176e4bae6a54ab49617db55e94f5c860238a1f
7
- data.tar.gz: '09b7dfaab197c2aefe59cb187b570ce818d1fe2952f6545b3fccc432f05a4bdc75151f4f589d3d3abc65d8325f56dc3156a335ae41231e5d02815770b976e48d'
6
+ metadata.gz: 03c1494d690dd6bf8ef266cb815d08260bb0c609a22215f9e3ba8e9618a9598345c7161b6bdee18bafb5a45927f17c821239b069c50781a52420ab1746564a21
7
+ data.tar.gz: 847557bd05438144d2d964308a15dbc270105eb97c19c1971402e7b83c1ac82b90e611609ae0777ebd20f7e778e57ba0b4c1189db5c64c5a9fb58eefc3d6064e
data/README.md CHANGED
@@ -1,178 +1,10 @@
1
1
  # Uffizzi CLI
2
2
 
3
- A command-line interace (CLI) for [Uffizzi App](https://github.com/UffizziCloud/uffizzi_app)
4
-
5
- ## Uffizzi Overview
6
-
7
- Uffizzi is an open-source engine for creating lightweight, ephemeral test environments for APIs and full-stack applications. Uffizzi enables teams to preview new features before merging and to mitigate the risk of introducing regressions into a codebase. Each preview gets a shareable URL that's updated when you push new commits or image tags, so teams can provide continual feedback during the development/QA process. Previews can be configured to expire or be destroyed when a pull request is closed, so environments exist only as long as they are needed. Uffizzi also helps deconflict shared development environments since previews are deployed as isolated namespaces—there is no risk of clobbering another developer's preview.
8
-
9
- While Uffizzi depends on Kubernetes, it does not require end-users to interface with Kubernetes directly. Instead, Uffizzi leverages Docker Compose as its configuration file format, so developers do not need modify Kubernetes manifests or even know about Kubernetes.
10
-
11
- Uffizzi is designed to integrate with any CI/CD system.
12
-
13
- ## Uffizzi Architecture
14
- <img src="https://github.com/UffizziCloud/uffizzi_app/blob/main/docs/images/uffizzi-architecture.png" description="Uffizzi Architecture" width="320"/>
15
-
16
- Uffizzi consists of the following components:
17
-
18
- - [Uffizzi App](https://github.com/UffizziCloud/uffizzi_app) - The primary REST API for creating and managing Previews
19
- - [Uffizzi Controller](https://github.com/UffizziCloud/uffizzi_controller) - A smart proxy service that handles requests from Uffizzi App to the Kubernetes API
20
- - Uffizzi CLI (this repository) - A command-line interface for Uffizzi App
21
-
22
- To host Uffizzi yourself, you will also need the following external dependencies:
23
-
24
- - Kubernetes (k8s) cluster
25
- - Postgres database
26
- - Redis cache
3
+ A command-line interace (CLI) for the [Uffizzi API](https://github.com/UffizziCloud/uffizzi)
27
4
 
28
5
  ## Installation
29
6
 
30
- The Uffizzi CLI can be used interactively or as part of an automated workflow (e.g. GitHub Actions). Both options use the `uffizzi/cli` container image available on Docker Hub.
31
-
32
- ### Interactive mode
33
-
34
- Run the CLI as a Docker container in interactive mode:
35
- ```
36
- docker run --interactive --rm --tty --entrypoint=sh uffizzi/cli
37
- ```
38
-
39
- If you specify the following environment variables, the Docker image's
40
- entrypoint script can log you into Uffizzi before executing your command.
41
-
42
- - `UFFIZZI_USER`
43
- - `UFFIZZI_SERVER`
44
- - `UFFIZZI_PASSWORD`
45
- - `UFFIZZI_PROJECT` (optional)
46
-
47
- ### Automated mode
48
-
49
- If you want to use Uffizzi as part of an automated workflow, you can pass the Uffizzi commands to the Docker run command. For example:
50
-
51
- ```
52
- docker run -it --rm uffizzi/cli project list
53
- ```
54
-
55
- ## Sample commands and examples
56
-
57
- ### help
58
-
59
- The `help` subcommand can be used to see more information about a particular command.
60
-
61
- Examples:
62
-
63
- ```
64
- uffizzi help
65
- ```
66
-
67
- ```
68
- uffizzi preview help
69
- ```
70
-
71
- ```
72
- uffizzi project compose help
73
- ```
74
-
75
- ### login
76
-
77
- ```
78
- uffizzi login --server=localhost:8080 --username=your@email.com
79
- ```
80
-
81
- Log in to the app with the specified server.
82
-
83
- #### login options
84
-
85
- | Option | Aliase | Description |
86
- | ------------ | ------ | ------------------------- |
87
- | `--username` | `-u` | Your email for logging in |
88
- | `--server` | | The URL of the Uffizzi installation |
89
-
90
- If server uses basic authentication you can specify options for it by setting `basic_auth_user` and `basic_auth_password` via `config set` command.
91
-
92
- ### config
93
-
94
- Use this command to configure your cli app.
95
-
96
- ```
97
- $ uffizzi config
98
- ```
99
-
100
- Launching interactive setup guide that sets the values for `server`, `username` and `project`
101
-
102
- ### config subcommands
103
-
104
- This command has 4 subcommands `list`, `get`, `set`, and `delete`.
105
-
106
- ```
107
- uffizzi config list
108
- ```
109
-
110
- Shows all options and their values from the config file.
111
-
112
- ```
113
- uffizzi config get-value OPTION
114
- ```
115
-
116
- Shows the value of the specified option.
117
-
118
- ```
119
- uffizzi config set OPTION VALUE
120
- ```
121
-
122
- Sets specified value for specified option. If a specified option already exists and has value it will be overwritten.
123
-
124
- ```
125
- uffizzi config unset OPTION
126
- ```
127
-
128
- Unsets specified option.
129
-
130
- ### project
131
-
132
- ```
133
- uffizzi project
134
- ```
135
-
136
- Use this command to configure your projects. This command has 2 subcommands `list` and `compose`.
137
-
138
- ```
139
- uffizzi project list
140
- ```
141
-
142
- Shows all your projects' slugs
143
-
144
- If you have only one project it will be added to your config file automatically, if there's more than one project you need to set up your project manually with the commands `uffizzi config set YOUR_PROJECT_SLUG` or `uffizzi project set-default YOUR_PROJECT_SLUG`
145
-
146
- ```
147
- $ uffizzi project set-default PROJECT_SLUG
148
- ```
149
- Create a preview from a compose file.
150
-
151
- Sets the default project given with the given project slug. When set, all commands use this project as the default context unless overridden by the --project flag.
152
-
153
- ### preview
154
-
155
- Create and manage previews
156
-
157
- ```
158
- uffizzi preview create docker-compose.uffizzi.yml
159
- ```
160
- Create a preview from a compose file.
161
-
162
- ```
163
- uffizzi preview delete deployment-21
164
- ```
165
- Delete a preview with preview ID `deployment-21`.
166
-
167
- ### disconnect
168
-
169
- ```
170
- uffizzi disconnect CREDENTIAL_TYPE
171
- ```
172
-
173
- Deletes credential of specified type
174
-
175
- Supported credential types - `docker-hub`, `acr`, `ecr`, `gcr`
7
+ See the [Uffizzi Documentation](https://docs.uffizzi.com) for installation instructions.
176
8
 
177
9
  ## Contributing
178
10
 
@@ -0,0 +1,252 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'uffizzi'
4
+ require 'uffizzi/config_file'
5
+ require 'uffizzi/services/kubeconfig_service'
6
+
7
+ module Uffizzi
8
+ class Cli::Install < Thor
9
+ include ApiClient
10
+
11
+ default_task :controller
12
+
13
+ desc 'controller [HOSTNMAE]', 'Install uffizzi controller to cluster'
14
+ method_option :namespace, type: :string
15
+ method_option :email, type: :string, required: true
16
+ method_option :context, type: :string
17
+ method_option :issuer, type: :string, enum: ['letsencrypt', 'zerossl']
18
+ method_option :'repo-url', type: :string
19
+ def controller(hostname)
20
+ Uffizzi::AuthHelper.check_login
21
+
22
+ InstallService.kubectl_exists?
23
+ InstallService.helm_exists?
24
+
25
+ if options[:context].present? && options[:context] != InstallService.kubeconfig_current_context
26
+ InstallService.set_current_context(options[:context])
27
+ end
28
+
29
+ ask_confirmation
30
+
31
+ uri = parse_hostname(hostname)
32
+ installation_options = build_installation_options(uri)
33
+ check_existence_controller_settings(uri, installation_options)
34
+ helm_values = build_helm_values(installation_options)
35
+
36
+ InstallService.create_helm_values_file(helm_values)
37
+ helm_set_repo
38
+ InstallService.helm_install!(namespace)
39
+ InstallService.delete_helm_values_file
40
+ Uffizzi.ui.say('Helm release is deployed')
41
+
42
+ controller_setting_params = build_controller_setting_params(uri, installation_options)
43
+ create_controller_settings(controller_setting_params) if existing_controller_setting.blank?
44
+ Uffizzi.ui.say('Controller settings are saved')
45
+ say_success(uri)
46
+ end
47
+
48
+ private
49
+
50
+ def helm_set_repo
51
+ return if InstallService.helm_repo_search.present?
52
+
53
+ InstallService.helm_repo_add(options[:'repo-url'])
54
+ end
55
+
56
+ def build_installation_options(uri)
57
+ {
58
+ uri: uri,
59
+ controller_username: Faker::Lorem.characters(number: 10),
60
+ controller_password: generate_password,
61
+ cert_email: options[:email],
62
+ cluster_issuer: options[:issuer] || InstallService::DEFAULT_CLUSTER_ISSUER,
63
+ }
64
+ end
65
+
66
+ def wait_ip
67
+ spinner = TTY::Spinner.new('[:spinner] Waiting IP addess...', format: :dots)
68
+ spinner.auto_spin
69
+
70
+ ip = nil
71
+ try = 0
72
+
73
+ loop do
74
+ ip = InstallService.get_controller_ip(namespace)
75
+ break if ip.present?
76
+
77
+ if try == 30
78
+ spinner.error
79
+
80
+ return 'unknown'
81
+ end
82
+
83
+ try += 1
84
+ sleep(2)
85
+ end
86
+
87
+ spinner.success
88
+
89
+ ip
90
+ end
91
+
92
+ def wait_certificate_request_ready(uri)
93
+ spinner = TTY::Spinner.new('[:spinner] Waiting create certificate for controller host...', format: :dots)
94
+ spinner.auto_spin
95
+
96
+ try = 0
97
+
98
+ loop do
99
+ requests = InstallService.get_certificate_request(namespace, uri)
100
+ break if requests.all? { |r| r['status'].downcase == 'true' }
101
+
102
+ if try == 60
103
+ spinner.error
104
+
105
+ return Uffizzi.ui.say('Stop waiting creation certificate')
106
+ end
107
+
108
+ try += 1
109
+ sleep(2)
110
+ end
111
+
112
+ spinner.success
113
+ end
114
+
115
+ def build_helm_values(params)
116
+ {
117
+ global: {
118
+ uffizzi: {
119
+ controller: {
120
+ username: params[:controller_username],
121
+ password: params[:controller_password],
122
+ },
123
+ },
124
+ },
125
+ clusterIssuer: params.fetch(:cluster_issuer),
126
+ tlsPerDeploymentEnabled: true.to_s,
127
+ certEmail: params.fetch(:cert_email),
128
+ 'ingress-nginx' => {
129
+ controller: {
130
+ ingressClassResource: {
131
+ default: true,
132
+ },
133
+ },
134
+ },
135
+ ingress: {
136
+ hostname: InstallService.build_controller_host(params[:uri].host),
137
+ },
138
+ }.deep_stringify_keys
139
+ end
140
+
141
+ def generate_password
142
+ hexatridecimal_base = 36
143
+ length = 8
144
+ rand(hexatridecimal_base**length).to_s(hexatridecimal_base)
145
+ end
146
+
147
+ def check_existence_controller_settings(uri, installation_options)
148
+ return if existing_controller_setting.blank?
149
+
150
+ Uffizzi.ui.say_error_and_exit('Installation canceled') unless update_and_continue?
151
+
152
+ controller_setting_params = build_controller_setting_params(uri, installation_options)
153
+ update_controller_settings(existing_controller_setting[:id], controller_setting_params)
154
+ end
155
+
156
+ def ask_confirmation
157
+ msg = "\r\n"\
158
+ 'This command will install Uffizzi into the default namespace of'\
159
+ " the '#{InstallService.kubeconfig_current_context}' context."\
160
+ "\r\n"\
161
+ "To install in a different place, use options '--namespace' and/or '--context'."\
162
+ "\r\n\r\n"\
163
+ "After installation, new environments created for account '#{account_name}' will be deployed to this host cluster."\
164
+ "\r\n\r\n"
165
+
166
+ Uffizzi.ui.say(msg)
167
+
168
+ question = 'Okay to proceed?'
169
+ Uffizzi.ui.say_error_and_exit('Installation canceled') unless Uffizzi.prompt.yes?(question)
170
+ end
171
+
172
+ def update_and_continue?
173
+ msg = "\r\n"\
174
+ 'You already have installation controller params. '\
175
+ "\r\n"\
176
+ 'You can update previous params and continue installation or cancel installation.'\
177
+ "\r\n"
178
+
179
+ Uffizzi.ui.say(msg)
180
+
181
+ question = 'Do you want update the controller settings?'
182
+ Uffizzi.prompt.yes?(question)
183
+ end
184
+
185
+ def fetch_controller_settings
186
+ response = get_account_controller_settings(server, account_id)
187
+ return Uffizzi::ResponseHelper.handle_failed_response(response) unless Uffizzi::ResponseHelper.ok?(response)
188
+
189
+ response.dig(:body, :controller_settings)
190
+ end
191
+
192
+ def update_controller_settings(controller_setting_id, params)
193
+ response = update_account_controller_settings(server, account_id, controller_setting_id, params)
194
+ Uffizzi::ResponseHelper.handle_failed_response(response) unless Uffizzi::ResponseHelper.ok?(response)
195
+ end
196
+
197
+ def create_controller_settings(params)
198
+ response = create_account_controller_settings(server, account_id, params)
199
+ Uffizzi::ResponseHelper.handle_failed_response(response) unless Uffizzi::ResponseHelper.created?(response)
200
+ end
201
+
202
+ def build_controller_setting_params(uri, installation_options)
203
+ {
204
+ url: URI::HTTPS.build(host: InstallService.build_controller_host(uri.host)).to_s,
205
+ managed_dns_zone: uri.host,
206
+ login: installation_options[:controller_username],
207
+ password: installation_options[:controller_password],
208
+ }
209
+ end
210
+
211
+ def say_success(uri)
212
+ ip_address = wait_ip
213
+ wait_certificate_request_ready(uri)
214
+
215
+ msg = 'Your Uffizzi controller is ready. To configure DNS,'\
216
+ " create a record for the hostname '*.#{uri.host}' pointing to '#{ip_address}'"
217
+ Uffizzi.ui.say(msg)
218
+ end
219
+
220
+ def parse_hostname(hostname)
221
+ uri = URI.parse(hostname)
222
+ host = uri.host || hostname
223
+
224
+ case uri
225
+ when URI::HTTP, URI::HTTPS
226
+ uri
227
+ else
228
+ URI::HTTPS.build(host: host)
229
+ end
230
+ end
231
+
232
+ def namespace
233
+ options[:namespace] || InstallService::DEFAULT_NAMESPACE
234
+ end
235
+
236
+ def server
237
+ @server ||= ConfigFile.read_option(:server)
238
+ end
239
+
240
+ def account_id
241
+ @account_id ||= ConfigFile.read_option(:account, :id)
242
+ end
243
+
244
+ def account_name
245
+ @account_name ||= ConfigFile.read_option(:account, :name)
246
+ end
247
+
248
+ def existing_controller_setting
249
+ @existing_controller_setting ||= fetch_controller_settings[0]
250
+ end
251
+ end
252
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'uffizzi'
4
+ require 'uffizzi/config_file'
5
+ require 'uffizzi/services/install_service'
6
+ require 'uffizzi/services/kubeconfig_service'
7
+
8
+ module Uffizzi
9
+ class Cli::Uninstall < Thor
10
+ include ApiClient
11
+
12
+ default_task :controller
13
+
14
+ desc 'controller [HOSTNMAE]', 'Install uffizzi controller to cluster'
15
+ method_option :namespace, type: :string
16
+ method_option :context, type: :string
17
+ def controller
18
+ Uffizzi::AuthHelper.check_login
19
+
20
+ InstallService.kubectl_exists?
21
+ InstallService.helm_exists?
22
+
23
+ if options[:context].present? && options[:context] != InstallService.kubeconfig_current_context
24
+ InstallService.set_current_context(options[:context])
25
+ end
26
+
27
+ ask_confirmation
28
+ delete_controller_settings
29
+ InstallService.helm_uninstall!(namespace)
30
+
31
+ helm_unset_repo
32
+ end
33
+
34
+ private
35
+
36
+ def helm_unset_repo
37
+ return if InstallService.helm_repo_search.blank?
38
+
39
+ InstallService.helm_repo_remove
40
+ end
41
+
42
+ def ask_confirmation
43
+ msg = "This command will uninstall Uffizzi from the '#{namespace}'"\
44
+ " namespace of the '#{InstallService.kubeconfig_current_context}' context."\
45
+ "\r\n"\
46
+ "To uninstall a different installation, use options '--namespace' and/or '--context'."\
47
+ "\r\n\r\n"\
48
+ "After uninstalling, new environments created for account '#{account_name}'"\
49
+ "\r\n"\
50
+ 'will be deployed to Uffizzi Cloud (app.uffizzi.com).'\
51
+ "\r\n\r\n"
52
+
53
+ Uffizzi.ui.say(msg)
54
+
55
+ question = 'Okay to proceed?'
56
+ Uffizzi.ui.say_error_and_exit('Uninstallation canceled') unless Uffizzi.prompt.yes?(question)
57
+ end
58
+
59
+ def fetch_controller_settings
60
+ response = get_account_controller_settings(server, account_id)
61
+ return Uffizzi::ResponseHelper.handle_failed_response(response) unless Uffizzi::ResponseHelper.ok?(response)
62
+
63
+ response.dig(:body, :controller_settings)
64
+ end
65
+
66
+ def delete_controller_settings
67
+ return if existing_controller_setting.blank?
68
+
69
+ response = delete_account_controller_settings(server, account_id, existing_controller_setting[:id])
70
+
71
+ if ResponseHelper.no_content?(response)
72
+ Uffizzi.ui.say('Controller settings deleted')
73
+ else
74
+ ResponseHelper.handle_failed_response(response)
75
+ end
76
+ end
77
+
78
+ def namespace
79
+ options[:namespace] || InstallService::DEFAULT_NAMESPACE
80
+ end
81
+
82
+ def server
83
+ @server ||= ConfigFile.read_option(:server)
84
+ end
85
+
86
+ def account_id
87
+ @account_id ||= ConfigFile.read_option(:account, :id)
88
+ end
89
+
90
+ def account_name
91
+ @account_name ||= ConfigFile.read_option(:account, :name)
92
+ end
93
+
94
+ def existing_controller_setting
95
+ @existing_controller_setting ||= fetch_controller_settings[0]
96
+ end
97
+ end
98
+ end
data/lib/uffizzi/cli.rb CHANGED
@@ -75,6 +75,14 @@ module Uffizzi
75
75
  require_relative 'cli/dev'
76
76
  subcommand 'dev', Cli::Dev
77
77
 
78
+ desc 'install', 'install'
79
+ require_relative 'cli/install'
80
+ subcommand 'install', Cli::Install
81
+
82
+ desc 'uninstall', 'uninstall'
83
+ require_relative 'cli/uninstall'
84
+ subcommand 'uninstall', Cli::Uninstall
85
+
78
86
  map preview: :compose
79
87
 
80
88
  class << self
@@ -321,6 +321,34 @@ module ApiClient
321
321
  build_response(response)
322
322
  end
323
323
 
324
+ def get_account_controller_settings(server, account_id)
325
+ uri = account_controller_settings_uri(server, account_id)
326
+ response = http_client.make_get_request(uri)
327
+
328
+ build_response(response)
329
+ end
330
+
331
+ def create_account_controller_settings(server, account_id, params = {})
332
+ uri = account_controller_settings_uri(server, account_id)
333
+ response = http_client.make_post_request(uri, params)
334
+
335
+ build_response(response)
336
+ end
337
+
338
+ def update_account_controller_settings(server, account_id, id, params = {})
339
+ uri = account_controller_setting_uri(server, account_id, id)
340
+ response = http_client.make_put_request(uri, params)
341
+
342
+ build_response(response)
343
+ end
344
+
345
+ def delete_account_controller_settings(server, account_id, id)
346
+ uri = account_controller_setting_uri(server, account_id, id)
347
+ response = http_client.make_delete_request(uri)
348
+
349
+ build_response(response)
350
+ end
351
+
324
352
  private
325
353
 
326
354
  def http_client
@@ -139,4 +139,12 @@ module ApiRoutes
139
139
  def account_clusters_uri(server, account_id)
140
140
  "#{server}/api/cli/v1/accounts/#{account_id}/clusters"
141
141
  end
142
+
143
+ def account_controller_settings_uri(server, account_id)
144
+ "#{server}/api/cli/v1/accounts/#{account_id}/controller_settings"
145
+ end
146
+
147
+ def account_controller_setting_uri(server, account_id, id)
148
+ "#{server}/api/cli/v1/accounts/#{account_id}/controller_settings/#{id}"
149
+ end
142
150
  end
@@ -0,0 +1,153 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'uffizzi/response_helper'
4
+ require 'uffizzi/clients/api/api_client'
5
+
6
+ class InstallService
7
+ DEFAULT_HELM_RELEASE_NAME = 'uffizzi'
8
+ INGRESS_NAME = "#{DEFAULT_HELM_RELEASE_NAME}-controller"
9
+ DEFAULT_HELM_REPO_NAME = 'uffizzi'
10
+ DEFAULT_CONTROLLER_CHART_NAME = 'uffizzi-controller'
11
+ HELM_DEPLOYED_STATUS = 'deployed'
12
+ VALUES_FILE_NAME = 'helm_values.yaml'
13
+ DEFAULT_NAMESPACE = 'default'
14
+ DEFAULT_CLUSTER_ISSUER = 'letsencrypt'
15
+ DEFAULT_CONTROLLER_REPO_URL = 'https://uffizzicloud.github.io/uffizzi_controller'
16
+ DEFAULT_CONTROLLER_DOMAIN_PREFIX = 'controller'
17
+
18
+ class << self
19
+ include ApiClient
20
+
21
+ def kubectl_exists?
22
+ cmd = 'kubectl version -o json'
23
+ execute_command(cmd, say: false).present?
24
+ end
25
+
26
+ def helm_exists?
27
+ cmd = 'helm version --short'
28
+ execute_command(cmd, say: false).present?
29
+ end
30
+
31
+ def helm_repo_remove
32
+ cmd = "helm repo remove #{DEFAULT_HELM_REPO_NAME}"
33
+ execute_command(cmd, skip_error: true)
34
+ end
35
+
36
+ def helm_repo_search
37
+ cmd = "helm search repo #{DEFAULT_HELM_REPO_NAME}/#{DEFAULT_CONTROLLER_CHART_NAME} -o json"
38
+
39
+ execute_command(cmd) do |result, err|
40
+ err.present? ? nil : JSON.parse(result)
41
+ end
42
+ end
43
+
44
+ def helm_repo_add(repo_url)
45
+ repo_url = repo_url || DEFAULT_CONTROLLER_REPO_URL
46
+ cmd = "helm repo add #{DEFAULT_HELM_REPO_NAME} #{repo_url}"
47
+ execute_command(cmd)
48
+ end
49
+
50
+ def helm_install!(namespace)
51
+ Uffizzi.ui.say('Start helm release installation')
52
+
53
+ repo = "#{DEFAULT_HELM_REPO_NAME}/#{DEFAULT_CONTROLLER_CHART_NAME}"
54
+ cmd = "helm upgrade #{DEFAULT_HELM_RELEASE_NAME} #{repo}" \
55
+ " --values #{helm_values_file_path}" \
56
+ " --namespace #{namespace}" \
57
+ ' --create-namespace' \
58
+ ' --install' \
59
+ ' --output json'
60
+
61
+ res = execute_command(cmd, say: false)
62
+ info = JSON.parse(res)['info']
63
+
64
+ return if info['status'] == HELM_DEPLOYED_STATUS
65
+
66
+ Uffizzi.ui.say_error_and_exit(info)
67
+ end
68
+
69
+ def helm_uninstall!(namespace)
70
+ Uffizzi.ui.say('Start helm release uninstallation')
71
+
72
+ cmd = "helm uninstall #{DEFAULT_HELM_RELEASE_NAME} --namespace #{namespace}"
73
+
74
+ execute_command(cmd)
75
+ end
76
+
77
+ def set_current_context(context)
78
+ cmd = "kubectl config use-context #{context}"
79
+ execute_command(cmd)
80
+ end
81
+
82
+ def kubeconfig_current_context
83
+ cmd = 'kubectl config current-context'
84
+
85
+ execute_command(cmd, say: false) { |stdout| stdout.present? && stdout.chop }
86
+ end
87
+
88
+ def get_controller_ip(namespace)
89
+ cmd = "kubectl get ingress -n #{namespace} -o json"
90
+ res = execute_command(cmd, say: false)
91
+ ingress = JSON.parse(res)['items'].detect { |i| i['metadata']['name'] = INGRESS_NAME }
92
+
93
+ return if ingress.blank?
94
+
95
+ load_balancers = ingress.dig('status', 'loadBalancer', 'ingress')
96
+ return if load_balancers.blank?
97
+
98
+ load_balancers.map { |i| i['ip'] }[0]
99
+ end
100
+
101
+ def get_certificate_request(namespace, uri)
102
+ cmd = "kubectl get certificaterequests -n #{namespace} -o json"
103
+ res = execute_command(cmd, say: false)
104
+ certificate_request = JSON.parse(res)['items'].detect { |i| i['metadata']['name'].include?(uri.host) }
105
+
106
+ return [] if certificate_request.nil?
107
+
108
+ conditions = certificate_request.dig('status', 'conditions') || []
109
+ conditions.map { |c| c.slice('type', 'status') }
110
+ end
111
+
112
+ def build_controller_host(host)
113
+ [DEFAULT_CONTROLLER_DOMAIN_PREFIX, host].join('.')
114
+ end
115
+
116
+ def delete_helm_values_file
117
+ File.delete(helm_values_file_path) if File.exist?(helm_values_file_path)
118
+ end
119
+
120
+ def create_helm_values_file(values)
121
+ FileUtils.mkdir_p(helm_values_dir_path) unless File.directory?(helm_values_dir_path)
122
+ File.write(helm_values_file_path, values.to_yaml)
123
+ end
124
+
125
+ def helm_values_file_path
126
+ File.join(helm_values_dir_path, VALUES_FILE_NAME)
127
+ end
128
+
129
+ def helm_values_dir_path
130
+ File.dirname(Uffizzi::ConfigFile.config_path)
131
+ end
132
+
133
+ private
134
+
135
+ def execute_command(command, say: true, skip_error: false)
136
+ stdout_str, stderr_str, status = Uffizzi.ui.capture3(command)
137
+
138
+ return yield(stdout_str, stderr_str) if block_given?
139
+
140
+ if !status.success? && !skip_error
141
+ return Uffizzi.ui.say_error_and_exit(stderr_str)
142
+ end
143
+
144
+ if !status.success? && skip_error
145
+ return Uffizzi.ui.say(stderr_str)
146
+ end
147
+
148
+ say ? Uffizzi.ui.say(stdout_str) : stdout_str
149
+ rescue Errno::ENOENT => e
150
+ Uffizzi.ui.say_error_and_exit(e.message)
151
+ end
152
+ end
153
+ end
@@ -56,7 +56,9 @@ class PreviewService
56
56
 
57
57
  Uffizzi.ui.say('Deployed')
58
58
  Uffizzi.ui.say("Deployment url: https://#{deployment[:preview_url]}")
59
- Uffizzi.ui.say("Deployment proxy url: https://#{deployment[:proxy_preview_url]}")
59
+ if deployment[:proxy_preview_url].present?
60
+ Uffizzi.ui.say("Deployment proxy url: https://#{deployment[:proxy_preview_url]}")
61
+ end
60
62
 
61
63
  activity_items
62
64
  rescue ApiClient::ResponseError => e
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Uffizzi
4
- VERSION = '2.3.3'
4
+ VERSION = '2.3.4'
5
5
  end
data/man/uffizzi CHANGED
@@ -1,6 +1,6 @@
1
1
  .\" generated with Ronn-NG/v0.9.1
2
2
  .\" http://github.com/apjanke/ronn-ng/tree/0.9.1
3
- .TH "UFFIZZI" "" "October 2023" ""
3
+ .TH "UFFIZZI" "" "November 2023" ""
4
4
  .SH "NAME"
5
5
  \fBuffizzi\fR \- manage Uffizzi resources
6
6
  .SH "SYNOPSIS"
data/man/uffizzi-cluster CHANGED
@@ -1,6 +1,6 @@
1
1
  .\" generated with Ronn-NG/v0.9.1
2
2
  .\" http://github.com/apjanke/ronn-ng/tree/0.9.1
3
- .TH "UFFIZZI\-CLUSTER" "" "August 2023" ""
3
+ .TH "UFFIZZI\-CLUSTER" "" "November 2023" ""
4
4
  .SH "NAME"
5
5
  \fBuffizzi\-cluster\fR \- manage clusters
6
6
  .SH "SYNOPSIS"
@@ -30,6 +30,12 @@ COMMAND is one of the following:
30
30
  list
31
31
  List all clusters
32
32
 
33
+ sleep
34
+ Put a cluster to sleep (non\-destructive)
35
+
36
+ wake
37
+ Wake a cluster that is sleeping
38
+
33
39
  update\-kubeconfig
34
40
  Update kubeconfig file
35
41
  .fi
@@ -1,6 +1,6 @@
1
1
  .\" generated with Ronn-NG/v0.9.1
2
2
  .\" http://github.com/apjanke/ronn-ng/tree/0.9.1
3
- .TH "UFFIZZI\-CLUSTER\-CREATE" "" "October 2023" ""
3
+ .TH "UFFIZZI\-CLUSTER\-CREATE" "" "November 2023" ""
4
4
  .SH "NAME"
5
5
  \fBuffizzi\-cluster\-create\fR
6
6
  .P
@@ -1,6 +1,6 @@
1
1
  .\" generated with Ronn-NG/v0.9.1
2
2
  .\" http://github.com/apjanke/ronn-ng/tree/0.9.1
3
- .TH "UFFIZZI\-CLUSTER\-DELETE" "" "September 2023" ""
3
+ .TH "UFFIZZI\-CLUSTER\-DELETE" "" "November 2023" ""
4
4
  .SH "NAME"
5
5
  \fBuffizzi\-cluster\-delete\fR \- delete a cluster
6
6
  .SH "SYNOPSIS"
@@ -1,6 +1,6 @@
1
1
  .\" generated with Ronn-NG/v0.9.1
2
2
  .\" http://github.com/apjanke/ronn-ng/tree/0.9.1
3
- .TH "UFFIZZI\-CLUSTER\-DISCONNECT" "" "September 2023" ""
3
+ .TH "UFFIZZI\-CLUSTER\-DISCONNECT" "" "November 2023" ""
4
4
  .SH "NAME"
5
5
  \fBuffizzi\-cluster\-disconnect\fR \- disconnect from current cluster context
6
6
  .SH "SYNOPSIS"
@@ -0,0 +1,39 @@
1
+ .\" generated with Ronn-NG/v0.9.1
2
+ .\" http://github.com/apjanke/ronn-ng/tree/0.9.1
3
+ .TH "UFFIZZI\-CLUSTER\-SLEEP" "" "November 2023" ""
4
+ .SH "NAME"
5
+ \fBuffizzi\-cluster\-sleep\fR
6
+ .P
7
+ $ uffizzi cluster sleep \-h uffizzi\-cluster\-sleep \- put a cluster to sleep (non\-destructive) ================================================================
8
+ .SH "SYNOPSIS"
9
+ .nf
10
+ uffizzi cluster sleep [CLUSTER_NAME]
11
+ .fi
12
+ .SH "DESCRIPTION"
13
+ .nf
14
+ Scales a Uffizzi cluster down to zero resource utilization
15
+ while keeping the namespace and any stateful resources,
16
+ like persistent volume claims\. If no CLUSTER_NAME is
17
+ specified, the kubeconfig current context is used\.
18
+
19
+ For more information on Uffizzi clusters, see:
20
+ https://docs\.uffizzi\.com/references/cli/
21
+ .fi
22
+ .SH "OPTIONS"
23
+ .nf
24
+ CLUSTER_NAME
25
+ The name of the target Uffizzi cluster
26
+ .fi
27
+ .SH "EXAMPLES"
28
+ .nf
29
+ To put the Uffizzi cluster in the current context to
30
+ sleep, run:
31
+
32
+ $ uffizzi cluster sleep
33
+
34
+ To put a Uffizzi cluster outside the current context to
35
+ sleep, run:
36
+
37
+ $ uffizzi cluster sleep my\-cluster
38
+ .fi
39
+
@@ -0,0 +1,37 @@
1
+ .\" generated with Ronn-NG/v0.9.1
2
+ .\" http://github.com/apjanke/ronn-ng/tree/0.9.1
3
+ .TH "UFFIZZI\-CLUSTER\-WAKE" "" "November 2023" ""
4
+ .SH "NAME"
5
+ \fBuffizzi\-cluster\-wake\fR
6
+ .P
7
+ $ uffizzi cluster wake \-h uffizzi\-cluster\-wake \- wake a cluster that is sleeping ================================================================
8
+ .SH "SYNOPSIS"
9
+ .nf
10
+ uffizzi cluster wake [CLUSTER_NAME]
11
+ .fi
12
+ .SH "DESCRIPTION"
13
+ .nf
14
+ Scales up a Uffizzi cluster to its original resource
15
+ utilization from zero (see \'uffizzi cluster sleep \-h\')\.
16
+ If no CLUSTER_NAME is specified, the kubeconfig current
17
+ context is used\.
18
+
19
+ For more information on Uffizzi clusters, see:
20
+ https://docs\.uffizzi\.com/references/cli/
21
+ .fi
22
+ .SH "OPTIONS"
23
+ .nf
24
+ CLUSTER_NAME
25
+ The name of the target Uffizzi cluster
26
+ .fi
27
+ .SH "EXAMPLES"
28
+ .nf
29
+ To wake the Uffizzi cluster in the current context, run:
30
+
31
+ $ uffizzi cluster wake
32
+
33
+ To wake a Uffizzi cluster outside the current context, run:
34
+
35
+ $ uffizzi cluster wake my\-cluster
36
+ .fi
37
+
data/man/uffizzi-dev CHANGED
@@ -1,6 +1,6 @@
1
1
  .\" generated with Ronn-NG/v0.9.1
2
2
  .\" http://github.com/apjanke/ronn-ng/tree/0.9.1
3
- .TH "UFFIZZI\-DEV" "" "October 2023" ""
3
+ .TH "UFFIZZI\-DEV" "" "November 2023" ""
4
4
  .SH "NAME"
5
5
  \fBuffizzi\-dev\fR \- manage dev environments
6
6
  .SH "SYNOPSIS"
@@ -0,0 +1,33 @@
1
+ .\" generated with Ronn-NG/v0.9.1
2
+ .\" http://github.com/apjanke/ronn-ng/tree/0.9.1
3
+ .TH "UFFIZZI\-DEV\-DELETE" "" "November 2023" ""
4
+ .SH "NAME"
5
+ \fBuffizzi\-dev\-delete\fR
6
+ .P
7
+ uffizzi dev delete \-h uffizzi\-dev\-delete \- delete a development environment ================================================================
8
+ .SH "SYNOPSIS"
9
+ .nf
10
+ uffizzi dev delete
11
+ .fi
12
+ .SH "DESCRIPTION"
13
+ .nf
14
+ Deletes a dev environment and associated Uffizzi
15
+ cluster resources, including any persistent
16
+ volumes, and the namespace itself\. The Uffizzi
17
+ cluster config is deleted from the kubeconfig file\.
18
+
19
+ For more information on Uffizzi clusters, see:
20
+ https://docs\.uffizzi\.com/references/cli/
21
+ .fi
22
+ .SH "FLAGS"
23
+ .nf
24
+ \-\-help, \-h
25
+ Show this message and exit\.
26
+ .fi
27
+ .SH "EXAMPLES"
28
+ .nf
29
+ To delete a dev environment, run:
30
+
31
+ $ uffizzi dev delete
32
+ .fi
33
+
@@ -1,13 +1,13 @@
1
1
  .\" generated with Ronn-NG/v0.9.1
2
2
  .\" http://github.com/apjanke/ronn-ng/tree/0.9.1
3
- .TH "UFFIZZI\-DEV\-DESCRIBE" "" "October 2023" ""
3
+ .TH "UFFIZZI\-DEV\-DESCRIBE" "" "November 2023" ""
4
4
  .SH "NAME"
5
5
  \fBuffizzi\-dev\-describe\fR
6
6
  .P
7
7
  $ uffizzi dev describe \-h uffizzi\-dev\-describe \- show metadata for a dev environment ================================================================
8
8
  .SH "SYNOPSIS"
9
9
  .nf
10
- uffizzi dev describe <NAME>
10
+ uffizzi dev describe
11
11
  .fi
12
12
  .SH "DESCRIPTION"
13
13
  .nf
@@ -20,12 +20,6 @@ This command can fail for the following reasons:
20
20
  For more information on Uffizzi clusters, see:
21
21
  https://docs\.uffizzi\.com/references/cli/
22
22
  .fi
23
- .SH "POSITIONAL ARGUMENTS"
24
- .nf
25
- [NAME]
26
- NAME for the dev environment you want to describe\.
27
- This is an optional argument\.
28
- .fi
29
23
  .SH "EXAMPLES"
30
24
  .nf
31
25
  The following command prints metadata for the dev
@@ -1,6 +1,6 @@
1
1
  .\" generated with Ronn-NG/v0.9.1
2
2
  .\" http://github.com/apjanke/ronn-ng/tree/0.9.1
3
- .TH "UFFIZZI\-DEV\-INGRESS" "" "October 2023" ""
3
+ .TH "UFFIZZI\-DEV\-INGRESS" "" "November 2023" ""
4
4
  .SH "NAME"
5
5
  \fBuffizzi\-dev\-ingress\fR
6
6
  .P
@@ -1,6 +1,6 @@
1
1
  .\" generated with Ronn-NG/v0.9.1
2
2
  .\" http://github.com/apjanke/ronn-ng/tree/0.9.1
3
- .TH "UFFIZZI\-DEV\-INGRESS\-OPEN" "" "October 2023" ""
3
+ .TH "UFFIZZI\-DEV\-INGRESS\-OPEN" "" "November 2023" ""
4
4
  .SH "NAME"
5
5
  \fBuffizzi\-dev\-ingress\-open\fR
6
6
  .P
@@ -1,6 +1,6 @@
1
1
  .\" generated with Ronn-NG/v0.9.1
2
2
  .\" http://github.com/apjanke/ronn-ng/tree/0.9.1
3
- .TH "UFFIZZI\-DEV\-START" "" "October 2023" ""
3
+ .TH "UFFIZZI\-DEV\-START" "" "November 2023" ""
4
4
  .SH "NAME"
5
5
  \fBuffizzi\-dev\-start\fR \- start a development environment
6
6
  .SH "SYNOPSIS"
@@ -24,8 +24,6 @@ path CONFIG_FILE\. Skaffold configurations are currently
24
24
  supported\. For help creating a skaffold\.yaml file, see:
25
25
  https://skaffold\.dev/docs/init/
26
26
 
27
- If a kubeconfig exists
28
-
29
27
  For more information on Uffizzi clusters, see:
30
28
  https://docs\.uffizzi\.com/references/cli/
31
29
  .fi
@@ -80,17 +78,6 @@ run:
80
78
 
81
79
  $ uffizzi dev start \-\-quiet
82
80
 
83
- To push your build artifacts to a private Docker Hub repo
84
- called \'acme/foo\', first add your Docker Hub credentials:
85
-
86
- $ uffizzi connect docker\-hub
87
- (See `uffizzi connect \-h` for other registry options)
88
-
89
- \|\.\|\.\|\.then override the default repo:
90
-
91
- $ uffizzi dev start \e
92
- \-\-default\-repo="hub\.docker\.com/acme/foo"
93
-
94
81
  To start a dev environment using an alternate kubeconfig file,
95
82
  run:
96
83
 
data/man/uffizzi-dev-stop CHANGED
@@ -1,6 +1,6 @@
1
1
  .\" generated with Ronn-NG/v0.9.1
2
2
  .\" http://github.com/apjanke/ronn-ng/tree/0.9.1
3
- .TH "UFFIZZI\-DEV\-STOP" "" "October 2023" ""
3
+ .TH "UFFIZZI\-DEV\-STOP" "" "November 2023" ""
4
4
  .SH "NAME"
5
5
  \fBuffizzi\-dev\-stop\fR \- stop a development environment
6
6
  .SH "SYNOPSIS"
@@ -9,23 +9,11 @@ uffizzi dev stop
9
9
  .fi
10
10
  .SH "DESCRIPTION"
11
11
  .nf
12
- Stops a dev environment and deletes the backing
13
- Uffizzi cluster resources, including any persistent
14
- volumes, and the namespace itself\. The Uffizzi
15
- cluster config is deleted from the kubeconfig file\.
16
-
17
- This command watches for file changes in a given local
18
- project directory, as specified in your configuration file\.
19
- It then serializes those changes and redeploys them onto
20
- a Uffizzi cluster\.
21
-
22
- The command looks for a configuration at the specified
23
- path CONFIG_FILE\. Skaffold configurations are currently
24
- supported\. For help creating a skaffold\.yaml file, see:
25
- https://skaffold\.dev/docs/init/
26
-
27
- For more information on Uffizzi clusters, see:
28
- https://docs\.uffizzi\.com/references/cli/
12
+ Stops the skaffold process for the dev environment\.
13
+ This command does not delete the dev cluster or any
14
+ associated resources\. You can restart the dev environment
15
+ with `uffizzi dev start`\. To delete the dev cluster entirely, see
16
+ `uffizzi dev delete`\.
29
17
  .fi
30
18
  .SH "FLAGS"
31
19
  .nf
@@ -0,0 +1,57 @@
1
+ .\" generated with Ronn-NG/v0.9.1
2
+ .\" http://github.com/apjanke/ronn-ng/tree/0.9.1
3
+ .TH "UFFIZZI\-INSTALL" "" "November 2023" ""
4
+ .SH "NAME"
5
+ \fBuffizzi\-install\fR
6
+ .P
7
+ $ uffizzi install help uffizzi\-install \- install the uffizzi platform ================================================================
8
+ .SH "SYNOPSIS"
9
+ .nf
10
+ uffizzi install [HOSTNAME]
11
+ .fi
12
+ .SH "DESCRIPTION"
13
+ .nf
14
+ Install the Uffizzi platform on a host cluster\. By default, this command uses your
15
+ kubeconfig current context as the host\.
16
+
17
+ The output of this command is an IP address or hostname where your instance of
18
+ the Uffizzi controller service is available\. Uffizzi expects this service to be publicly
19
+ available at the specified HOSTNAME\. Before you can create Uffizzi environments
20
+ on your installation, be sure to configure your DNS to point HOSTNAME to the IP
21
+ or hostname output by this command\.
22
+
23
+ If you\'re looking for an air\-gapped or local installation of Uffizzi, please contact
24
+ sales@uffizzi\.com or try the open\-source version (docs\.uffizzi\.com/open\-source)\.
25
+
26
+ EMAIL is a business email, required for letsencrypt cert authority\.
27
+
28
+ For more information on the Uffizzi installation process, see:
29
+ https://docs\.uffizzi\.com/cli/commands/install
30
+ .fi
31
+ .SH "FLAGS"
32
+ .nf
33
+ \-\-email
34
+ A business email required for letsencrypt
35
+
36
+ \-\-context
37
+ The name of the kubeconfig context to use
38
+
39
+ \-\-namespace
40
+ The namespace where Uffizzi platform will be installed
41
+
42
+ \-\-help
43
+ Display this help page and exit
44
+ .fi
45
+ .SH "EXAMPLES"
46
+ .nf
47
+ To install Uffizzi using the current context at hostname
48
+ \'uffizzi\.example\.com\', run:
49
+
50
+ $ uffizzi install uffizzi\.example\.com \-\-email="jdoe@example\.com"
51
+
52
+ To install Uffizzi using context \'foo\' and namespace \'bar\', run:
53
+
54
+ $ uffizzi install uffizzi\.example\.com \-\-email="jdoe@example\.com" \e
55
+ \-\-context=\'foo\' \-\-namespace=\'bar\'
56
+ .fi
57
+
@@ -0,0 +1,50 @@
1
+ $ uffizzi install help
2
+ uffizzi-install - install the uffizzi platform
3
+ ================================================================
4
+
5
+ ## SYNOPSIS
6
+ uffizzi install [HOSTNAME]
7
+
8
+ ## DESCRIPTION
9
+ Install the Uffizzi platform on a host cluster. By default, this command uses your
10
+ kubeconfig current context as the host.
11
+
12
+ The output of this command is an IP address or hostname where your instance of
13
+ the Uffizzi controller service is available. Uffizzi expects this service to be publicly
14
+ available at the specified HOSTNAME. Before you can create Uffizzi environments
15
+ on your installation, be sure to configure your DNS to point HOSTNAME to the IP
16
+ or hostname output by this command.
17
+
18
+ If you're looking for an air-gapped or local installation of Uffizzi, please contact
19
+ sales@uffizzi.com or try the open-source version (docs.uffizzi.com/open-source).
20
+
21
+ EMAIL is a business email, required for letsencrypt cert authority.
22
+
23
+ For more information on the Uffizzi installation process, see:
24
+ https://docs.uffizzi.com/cli/commands/install
25
+
26
+ ## FLAGS
27
+
28
+ --email
29
+ A business email required for letsencrypt
30
+
31
+ --context
32
+ The name of the kubeconfig context to use
33
+
34
+ --namespace
35
+ The namespace where Uffizzi platform will be installed
36
+
37
+ --help
38
+ Display this help page and exit
39
+
40
+ ## EXAMPLES
41
+
42
+ To install Uffizzi using the current context at hostname
43
+ 'uffizzi.example.com', run:
44
+
45
+ $ uffizzi install uffizzi.example.com --email="jdoe@example.com"
46
+
47
+ To install Uffizzi using context 'foo' and namespace 'bar', run:
48
+
49
+ $ uffizzi install uffizzi.example.com --email="jdoe@example.com" \
50
+ --context='foo' --namespace='bar'
data/man/uffizzi-login CHANGED
@@ -1,6 +1,6 @@
1
1
  .\" generated with Ronn-NG/v0.9.1
2
2
  .\" http://github.com/apjanke/ronn-ng/tree/0.9.1
3
- .TH "UFFIZZI\-LOGIN" "" "September 2023" ""
3
+ .TH "UFFIZZI\-LOGIN" "" "November 2023" ""
4
4
  .SH "NAME"
5
5
  \fBuffizzi\-login\fR \- login to Uffizzi to view and manage your previews\.
6
6
  .SH "SYNOPSIS"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: uffizzi-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.3
4
+ version: 2.3.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Josh Thurman
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2023-11-02 00:00:00.000000000 Z
12
+ date: 2023-11-28 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -426,6 +426,7 @@ files:
426
426
  - lib/uffizzi/cli/dev.rb
427
427
  - lib/uffizzi/cli/dev/ingress.rb
428
428
  - lib/uffizzi/cli/disconnect.rb
429
+ - lib/uffizzi/cli/install.rb
429
430
  - lib/uffizzi/cli/login.rb
430
431
  - lib/uffizzi/cli/login_by_identity_token.rb
431
432
  - lib/uffizzi/cli/logout.rb
@@ -434,6 +435,7 @@ files:
434
435
  - lib/uffizzi/cli/project.rb
435
436
  - lib/uffizzi/cli/project/compose.rb
436
437
  - lib/uffizzi/cli/project/secret.rb
438
+ - lib/uffizzi/cli/uninstall.rb
437
439
  - lib/uffizzi/clients/api/api_client.rb
438
440
  - lib/uffizzi/clients/api/api_routes.rb
439
441
  - lib/uffizzi/clients/api/http_client.rb
@@ -458,6 +460,7 @@ files:
458
460
  - lib/uffizzi/services/dev_service.rb
459
461
  - lib/uffizzi/services/env_variables_service.rb
460
462
  - lib/uffizzi/services/github_service.rb
463
+ - lib/uffizzi/services/install_service.rb
461
464
  - lib/uffizzi/services/kubeconfig_service.rb
462
465
  - lib/uffizzi/services/preview_service.rb
463
466
  - lib/uffizzi/services/project_service.rb
@@ -486,9 +489,11 @@ files:
486
489
  - man/uffizzi-cluster-disconnect.ronn
487
490
  - man/uffizzi-cluster-list
488
491
  - man/uffizzi-cluster-list.ronn
492
+ - man/uffizzi-cluster-sleep
489
493
  - man/uffizzi-cluster-sleep.ronn
490
494
  - man/uffizzi-cluster-update-kubeconfig
491
495
  - man/uffizzi-cluster-update-kubeconfig.ronn
496
+ - man/uffizzi-cluster-wake
492
497
  - man/uffizzi-cluster-wake.ronn
493
498
  - man/uffizzi-cluster.ronn
494
499
  - man/uffizzi-compose
@@ -528,6 +533,7 @@ files:
528
533
  - man/uffizzi-connect-ghcr.ronn
529
534
  - man/uffizzi-connect.ronn
530
535
  - man/uffizzi-dev
536
+ - man/uffizzi-dev-delete
531
537
  - man/uffizzi-dev-delete.ronn
532
538
  - man/uffizzi-dev-describe
533
539
  - man/uffizzi-dev-describe.ronn
@@ -542,6 +548,8 @@ files:
542
548
  - man/uffizzi-dev.ronn
543
549
  - man/uffizzi-disconnect
544
550
  - man/uffizzi-disconnect.ronn
551
+ - man/uffizzi-install
552
+ - man/uffizzi-install.ronn
545
553
  - man/uffizzi-login
546
554
  - man/uffizzi-login-by-identity-token
547
555
  - man/uffizzi-login-by-identity-token.html