diff --git a/Gemfile.lock b/Gemfile.lock index c57f739d..5cc977c1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - uffizzi-cli (0.3.8) + uffizzi-cli (0.4.0) thor tty-spinner diff --git a/README.md b/README.md index 5363149f..696049c6 100644 --- a/README.md +++ b/README.md @@ -176,6 +176,16 @@ $ uffizzi config delete OPTION Deletes specified option. +### disconnect ### + +``` +$ uffizzi disconnect CREDENTIAL_TYPE +``` + +Deletes credential of specified type + +Supported credential types - `docker-hub`, `acr`, `ecr`, `gcr` + ## Git workflow for the app: 1. Clone the repository and checkout to `develop` branch diff --git a/lib/uffizzi.rb b/lib/uffizzi.rb index 7864bc9e..78a93968 100644 --- a/lib/uffizzi.rb +++ b/lib/uffizzi.rb @@ -3,6 +3,8 @@ require 'io/console' require 'tty-spinner' +require 'thor' +require 'uffizzi/error' require 'uffizzi/shell' require 'uffizzi/version' require 'uffizzi/clients/api/api_client' @@ -11,8 +13,6 @@ require_relative '../config/uffizzi' module Uffizzi - class Error < StandardError; end - class << self def ui @ui ||= Uffizzi::UI::Shell.new diff --git a/lib/uffizzi/cli.rb b/lib/uffizzi/cli.rb index d19e34ad..47331ed9 100644 --- a/lib/uffizzi/cli.rb +++ b/lib/uffizzi/cli.rb @@ -8,6 +8,10 @@ class CLI < Thor require_relative 'cli/common' class_option :help, type: :boolean, aliases: ['-h', 'help'] + def self.exit_on_failure? + true + end + desc 'version', 'show version' def version require_relative 'version' @@ -54,5 +58,11 @@ def connect(credential_type, credential_file_path = nil) require_relative 'cli/connect' Connect.new.run(credential_type, credential_file_path) end + + desc 'disconect CREDENTIAL_TYPE', 'Disonnect credentials from Uffizzi' + def disconnect(credential_type) + require_relative 'cli/disconnect' + Disconnect.new.run(credential_type) + end end end diff --git a/lib/uffizzi/cli/disconnect.rb b/lib/uffizzi/cli/disconnect.rb new file mode 100644 index 00000000..350ff36c --- /dev/null +++ b/lib/uffizzi/cli/disconnect.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +require 'uffizzi' + +module Uffizzi + class CLI::Disconnect + include ApiClient + + def run(credential_type) + connection_type = case credential_type + when 'docker-hub' + Uffizzi.configuration.credential_types[:dockerhub] + when 'acr' + Uffizzi.configuration.credential_types[:azure] + when 'ecr' + Uffizzi.configuration.credential_types[:amazon] + when 'gcr' + Uffizzi.configuration.credential_types[:google] + else + raise Uffizzi::Error.new('Unsupported credential type.') + end + + response = delete_credential(ConfigFile.read_option(:hostname), connection_type) + + if ResponseHelper.no_content?(response) + Uffizzi.ui.say("Successfully disconnected #{connection_name(credential_type)} connection") + else + ResponseHelper.handle_failed_response(response) + end + end + + private + + def connection_name(credential_type) + { + 'docker-hub' => 'DockerHub', + 'acr' => 'ACR', + 'ecr' => 'ECR', + 'gcr' => 'GCR', + }[credential_type] + end + end +end diff --git a/lib/uffizzi/cli/preview/service.rb b/lib/uffizzi/cli/preview/service.rb index cdb1425b..28cc5cc2 100644 --- a/lib/uffizzi/cli/preview/service.rb +++ b/lib/uffizzi/cli/preview/service.rb @@ -9,7 +9,23 @@ module Uffizzi class CLI::Preview::Service < Thor include ApiClient - desc 'list', 'list' + desc 'uffizzi preview service logs [LOGS_TYPE] [DEPLOYMENT_ID] [CONTAINER_NAME]', 'logs' + def logs(logs_type, deployment_name, container_name = args) + return Uffizzi.ui.say('You are not logged in.') unless Uffizzi::AuthHelper.signed_in? + return Uffizzi.ui.say('This command needs project to be set in config file') unless Uffizzi::AuthHelper.project_set? + + deployment_id = PreviewService.read_deployment_id(deployment_name) + response = service_logs_response(logs_type, deployment_id, container_name) + return Uffizzi.ui.say(response[:errors]) if response[:errors] + + if ResponseHelper.ok?(response) + handle_succeed_logs_response(response, container_name) + else + ResponseHelper.handle_failed_response(response) + end + end + + desc 'uffizzi preview service logs [DEPLOYMENT_ID]', 'list' def list(deployment_name) return Uffizzi.ui.say('You are not logged in.') unless Uffizzi::AuthHelper.signed_in? return Uffizzi.ui.say('This command needs project to be set in config file') unless Uffizzi::AuthHelper.project_set? @@ -20,7 +36,7 @@ def list(deployment_name) response = fetch_deployment_services(hostname, project_slug, deployment_id) if ResponseHelper.ok?(response) - handle_succeed_response(response, deployment_name) + handle_succeed_list_response(response, deployment_name) else ResponseHelper.handle_failed_response(response) end @@ -28,7 +44,19 @@ def list(deployment_name) private - def handle_succeed_response(response, deployment_name) + def service_logs_response(logs_type, deployment_id, container_name) + project_slug = ConfigFile.read_option(:project) + hostname = ConfigFile.read_option(:hostname) + + case logs_type + when 'container' + fetch_deployment_service_logs(hostname, project_slug, deployment_id, container_name) + else + raise Uffizzi::Error.new('Unknown log type') + end + end + + def handle_succeed_list_response(response, deployment_name) services = response[:body][:containers] || [] return Uffizzi.ui.say("There are no services associated with the preview #{deployment_name}") if services.empty? @@ -36,5 +64,14 @@ def handle_succeed_response(response, deployment_name) Uffizzi.ui.say(service) end end + + def handle_succeed_logs_response(response, container_name) + logs = response[:body][:logs] || [] + return Uffizzi.ui.say("The service #{container_name} has no logs") if logs.empty? + + logs.each do |log| + Uffizzi.ui.say(log) + end + end end end diff --git a/lib/uffizzi/clients/api/api_client.rb b/lib/uffizzi/clients/api/api_client.rb index 57acb2cd..a60bcf33 100644 --- a/lib/uffizzi/clients/api/api_client.rb +++ b/lib/uffizzi/clients/api/api_client.rb @@ -41,6 +41,20 @@ def fetch_deployment_services(hostname, project_slug, deployment_id) build_response(response) end + def delete_credential(hostname, credential_type) + uri = delete_credential_uri(hostname, credential_type) + response = Uffizzi::HttpClient.make_delete_request(uri) + + build_response(response) + end + + def fetch_deployment_service_logs(hostname, project_slug, deployment_id, container_name) + uri = preview_service_logs_uri(hostname, project_slug, deployment_id, container_name) + response = Uffizzi::HttpClient.make_get_request(uri) + + build_response(response) + end + def set_compose_file(hostname, params, project_slug) uri = compose_file_uri(hostname, project_slug) response = Uffizzi::HttpClient.make_post_request(uri, params) diff --git a/lib/uffizzi/clients/api/api_routes.rb b/lib/uffizzi/clients/api/api_routes.rb index eea86260..b6cd2f7d 100644 --- a/lib/uffizzi/clients/api/api_routes.rb +++ b/lib/uffizzi/clients/api/api_routes.rb @@ -55,4 +55,12 @@ def credentials_uri(hostname) def preview_services_uri(hostname, project_slug, deployment_id) "#{hostname}/api/cli/v1/projects/#{project_slug}/deployments/#{deployment_id}/containers" end + + def delete_credential_uri(hostname, credential_type) + "#{hostname}/api/cli/v1/account/credentials/#{credential_type}" + end + + def preview_service_logs_uri(hostname, project_slug, deployment_id, container_name) + "#{hostname}/api/cli/v1/projects/#{project_slug}/deployments/#{deployment_id}/containers/#{container_name}/logs" + end end diff --git a/lib/uffizzi/error.rb b/lib/uffizzi/error.rb new file mode 100644 index 00000000..bff31faf --- /dev/null +++ b/lib/uffizzi/error.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +module Uffizzi + class Error < Thor::Error; end +end diff --git a/lib/uffizzi/version.rb b/lib/uffizzi/version.rb index 1aa9f7f5..24b870b4 100644 --- a/lib/uffizzi/version.rb +++ b/lib/uffizzi/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module Uffizzi - VERSION = '0.3.8' + VERSION = '0.4.0' end diff --git a/man/uffizzi-preview_service_logs b/man/uffizzi-preview_service_logs new file mode 100644 index 00000000..afddbc15 --- /dev/null +++ b/man/uffizzi-preview_service_logs @@ -0,0 +1,62 @@ +.\" generated with Ronn-NG/v0.9.1 +.\" http://github.com/apjanke/ronn-ng/tree/0.9.1 +.TH "PREVIEW" "" "March 2022" "" +.SH "NAME" +\fBpreview\fR \- manage previews +.SH "NAME" +.nf +uffizzi preview service logs \- show the logs for a container service +of a preview +.fi +.SH "SYNOPSIS" +.nf +uffizzi preview service logs LOG_TYPE [PREVIEW_ID] [SERVICE] [UFFIZZI_WIDE_FLAG \|\.\|\.\|\.] +.fi +.SH "DESCRIPTION" +.nf +Shows the logs for a given container service of a given preview\. + +This command can fail for the following reasons: + \- There is no preview with the given PREVIEW_ID + \- There is no service with the name SERVICE + +For more information on service logs, see: +https://docs\.uffizzi\.com/cli +.fi +.SH "LOG_TYPE" +.nf +LOG_TYPE is one of the following: + +build + The build logs of a service\. + +container + The container logs of a service\. +.fi +.SH "POSITIONAL ARGUMENTS" +.nf +[PREVIEW_ID] + The ID of the preview that includes the service you want to + show logs for\. + +[SERVICE] + The name of the service you want to show logs for\. +.fi +.SH "UFFIZZI WIDE FLAGS" +.nf +These flags are available to all commands: \-\-project\. Run $ uffizzi +help for details\. +.fi +.SH "EXAMPLES" +.nf +The following command shows build logs for the service web\-app of the +preview with ID deployment\-14: + + $ uffizzi preview service logs build deployment\-14 web\-app + +The following command shows container logs for the service postgres\-db of +the preview with ID deployment\-14: + + $ uffizzi preview service logs container deployment\-14 postgres\-db +.fi + diff --git a/man/uffizzi-preview_service_logs.html b/man/uffizzi-preview_service_logs.html new file mode 100644 index 00000000..aefb4a8e --- /dev/null +++ b/man/uffizzi-preview_service_logs.html @@ -0,0 +1,142 @@ + + + + + + manage previews + + + + +
+ + + +
    +
  1. preview
  2. +
  3. +
  4. preview
  5. +
+ + + +

NAME

+

+ preview - manage previews +

+

NAME

+
uffizzi preview service logs - show the logs for a container service 
+of a preview
+
+ +

SYNOPSIS

+
uffizzi preview service logs LOG_TYPE [PREVIEW_ID] [SERVICE] [UFFIZZI_WIDE_FLAG ...] 
+
+ +

DESCRIPTION

+
Shows the logs for a given container service of a given preview.
+
+This command can fail for the following reasons: 
+    - There is no preview with the given PREVIEW_ID
+    - There is no service with the name SERVICE
+
+For more information on service logs, see:
+https://docs.uffizzi.com/cli
+
+ +

LOG_TYPE

+
LOG_TYPE is one of the following:
+
+build
+    The build logs of a service.
+
+container
+    The container logs of a service.
+
+ +

POSITIONAL ARGUMENTS

+
[PREVIEW_ID]
+    The ID of the preview that includes the service you want to 
+    show logs for.
+
+[SERVICE]
+    The name of the service you want to show logs for.
+
+ +

UFFIZZI WIDE FLAGS

+
These flags are available to all commands: --project. Run $ uffizzi 
+help for details.
+
+ +

EXAMPLES

+
The following command shows build logs for the service web-app of the 
+preview with ID deployment-14:
+
+    $ uffizzi preview service logs build deployment-14 web-app
+
+The following command shows container logs for the service postgres-db of 
+the preview with ID deployment-14:
+
+    $ uffizzi preview service logs container deployment-14 postgres-db
+
+ +
    +
  1. +
  2. March 2022
  3. +
  4. preview
  5. +
+ +
+ + diff --git a/man/uffizzi-preview_service_logs.ronn b/man/uffizzi-preview_service_logs.ronn new file mode 100644 index 00000000..d2e4ec61 --- /dev/null +++ b/man/uffizzi-preview_service_logs.ronn @@ -0,0 +1,53 @@ +uffizzi preview - manage previews +================================================================ + +## NAME + uffizzi preview service logs - show the logs for a container service + of a preview + +## SYNOPSIS + uffizzi preview service logs LOG_TYPE [PREVIEW_ID] [SERVICE] [UFFIZZI_WIDE_FLAG ...] + +## DESCRIPTION + Shows the logs for a given container service of a given preview. + + This command can fail for the following reasons: + - There is no preview with the given PREVIEW_ID + - There is no service with the name SERVICE + + For more information on service logs, see: + https://docs.uffizzi.com/cli + +## LOG_TYPE + LOG_TYPE is one of the following: + + build + The build logs of a service. + + container + The container logs of a service. + +## POSITIONAL ARGUMENTS + [PREVIEW_ID] + The ID of the preview that includes the service you want to + show logs for. + + [SERVICE] + The name of the service you want to show logs for. + + +## UFFIZZI WIDE FLAGS + These flags are available to all commands: --project. Run $ uffizzi + help for details. + +## EXAMPLES + The following command shows build logs for the service web-app of the + preview with ID deployment-14: + + $ uffizzi preview service logs build deployment-14 web-app + + The following command shows container logs for the service postgres-db of + the preview with ID deployment-14: + + $ uffizzi preview service logs container deployment-14 postgres-db + diff --git a/test/fixtures/files/uffizzi/uffizzi_delete_credential_failed.json b/test/fixtures/files/uffizzi/uffizzi_delete_credential_failed.json new file mode 100644 index 00000000..3b1ec215 --- /dev/null +++ b/test/fixtures/files/uffizzi/uffizzi_delete_credential_failed.json @@ -0,0 +1,7 @@ +{ + "errors": { + "title": [ + "Resource Not Found" + ] + } +} diff --git a/test/fixtures/files/uffizzi/uffizzi_preview_service_logs_success.json b/test/fixtures/files/uffizzi/uffizzi_preview_service_logs_success.json new file mode 100644 index 00000000..11d280bd --- /dev/null +++ b/test/fixtures/files/uffizzi/uffizzi_preview_service_logs_success.json @@ -0,0 +1,19 @@ +{ + "logs": [ + "/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration", + "/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/", + "/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh", + "10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf", + "10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf", + "/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh", + "/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh", + "/docker-entrypoint.sh: Configuration complete; ready for start up", + "2022/03/22 00:33:42 [notice] 1#1: using the \"epoll\" event method", + "2022/03/22 00:33:42 [notice] 1#1: nginx/1.21.6", + "2022/03/22 00:33:42 [notice] 1#1: built by gcc 10.2.1 20210110 (Debian 10.2.1-6) ", + "2022/03/22 00:33:42 [notice] 1#1: OS: Linux 5.4.144+", + "2022/03/22 00:33:42 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576", + "2022/03/22 00:33:42 [notice] 1#1: start worker processes", + "2022/03/22 00:33:42 [notice] 1#1: start worker process 33" + ] +} diff --git a/test/support/uffizzi_preview_stub_support.rb b/test/support/uffizzi_preview_stub_support.rb index b0f20fa1..2907187b 100644 --- a/test/support/uffizzi_preview_stub_support.rb +++ b/test/support/uffizzi_preview_stub_support.rb @@ -52,4 +52,10 @@ def stub_uffizzi_preview_services_list(body, project_slug, deployment_id) stub_request(:get, url).to_return(status: 200, body: body.to_json, headers: {}) end + + def stub_uffizzi_preview_service_logs(body, project_slug, deployment_id, container_name) + url = preview_service_logs_uri(Uffizzi.configuration.hostname, project_slug, deployment_id, container_name) + + stub_request(:get, url).to_return(status: 200, body: body.to_json, headers: {}) + end end diff --git a/test/support/uffizzi_stub_support.rb b/test/support/uffizzi_stub_support.rb index e4a183b2..62bb22b0 100644 --- a/test/support/uffizzi_stub_support.rb +++ b/test/support/uffizzi_stub_support.rb @@ -78,4 +78,16 @@ def stub_uffizzi_create_credential_fail(body) stub_request(:post, uri).to_return(status: 422, body: body.to_json) end + + def stub_uffizzi_delete_credential(credential_type) + uri = delete_credential_uri(Uffizzi.configuration.hostname, credential_type) + + stub_request(:delete, uri).to_return(status: 204) + end + + def stub_uffizzi_delete_credential_fail(body, credential_type) + uri = delete_credential_uri(Uffizzi.configuration.hostname, credential_type) + + stub_request(:delete, uri).to_return(status: 422, body: body.to_json) + end end diff --git a/test/uffizzi/cli/disconnect_test.rb b/test/uffizzi/cli/disconnect_test.rb new file mode 100644 index 00000000..c7f4a016 --- /dev/null +++ b/test/uffizzi/cli/disconnect_test.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: true + +require 'test_helper' + +class DisconnectTest < Minitest::Test + def setup + @cli = Uffizzi::CLI.new + + sign_in + end + + def test_disconnect_docker_hub_success + stubbed_uffizzi_delete_credential = stub_uffizzi_delete_credential(Uffizzi.configuration.credential_types[:dockerhub]) + + @cli.disconnect('docker-hub') + + assert_equal('Successfully disconnected DockerHub connection', Uffizzi.ui.last_message) + assert_requested(stubbed_uffizzi_delete_credential) + end + + def test_disconnect_azure_success + stubbed_uffizzi_delete_credential = stub_uffizzi_delete_credential(Uffizzi.configuration.credential_types[:azure]) + + @cli.disconnect('acr') + + assert_equal('Successfully disconnected ACR connection', Uffizzi.ui.last_message) + assert_requested(stubbed_uffizzi_delete_credential) + end + + def test_disconnect_amazon_success + stubbed_uffizzi_delete_credential = stub_uffizzi_delete_credential(Uffizzi.configuration.credential_types[:amazon]) + + @cli.disconnect('ecr') + + assert_equal('Successfully disconnected ECR connection', Uffizzi.ui.last_message) + assert_requested(stubbed_uffizzi_delete_credential) + end + + def test_disconnect_google_success + stubbed_uffizzi_delete_credential = stub_uffizzi_delete_credential(Uffizzi.configuration.credential_types[:google]) + + @cli.disconnect('gcr') + + assert_equal('Successfully disconnected GCR connection', Uffizzi.ui.last_message) + assert_requested(stubbed_uffizzi_delete_credential) + end + + def test_unknown_credential_type + credential_type = generate(:string) + + error = assert_raises(Uffizzi::Error) do + @cli.disconnect(credential_type) + end + + assert_equal('Unsupported credential type.', error.message) + end + + def test_disconnect_credential_failed + body = json_fixture('files/uffizzi/uffizzi_delete_credential_failed.json') + stubbed_uffizzi_delete_credential = stub_uffizzi_delete_credential_fail(body, Uffizzi.configuration.credential_types[:dockerhub]) + + @cli.disconnect('docker-hub') + + assert_equal(body[:errors][:title].first, Uffizzi.ui.last_message) + assert_requested(stubbed_uffizzi_delete_credential) + end +end diff --git a/test/uffizzi/cli/preview/service_test.rb b/test/uffizzi/cli/preview/service_test.rb index 06d68b52..a1d28895 100644 --- a/test/uffizzi/cli/preview/service_test.rb +++ b/test/uffizzi/cli/preview/service_test.rb @@ -20,4 +20,15 @@ def test_list_preview_services assert_requested(stubbed_uffizzi_preview_services_list) end + + def test_list_preview_service_logs + body = json_fixture('files/uffizzi/uffizzi_preview_service_logs_success.json') + container_name = 'redis' + deployment_id = 318 + stubbed_uffizzi_preview_service_logs = stub_uffizzi_preview_service_logs(body, @project_slug, deployment_id, container_name) + + @service.logs('container', "deployment-#{deployment_id}", container_name) + + assert_requested(stubbed_uffizzi_preview_service_logs) + end end