diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 151f477d..d721e826 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -66,3 +66,30 @@ git pull --rebase qa git merge --no-ff BRANCH_NAME git push origin qa ``` + +## Release + +1. Merge the branch to develop. +2. Checkout to the develop branch. +3. Run one of the following command depending on the new release version: + +``` +make release_patch +make release_minor +make release_major +``` + +These commands will merge and push the develop branch into main and create a new tag. +Once the tag is pushed, the GHA workflows will release a gem and create new binaries for Linux and MacOs that can be found in the github release artefacts. + +## Homebrew tap + +Uffizzi supports a [Homebrew tap package] (https://github.com/UffizziCloud/homebrew-tap) and it needs to be updated after each release. +1. Go to the [latest release] (https://github.com/UffizziCloud/uffizzi_cli/releases/latest). +2. Copy the link to the source code archive (tar.gz). +3. Run `brew create [link copied in the previous step]` - this will create a new Formula file with the sha and the source code url. +4. Copy over the contents of the existing [Formula](https://github.com/UffizziCloud/homebrew-tap/blob/main/Formula/uffizzi.rb) from the master, replacing the sha and the url for the ones from the newly created Formula. +5. Update the `resource "uffizzi-cli"` to the latest gem and add new dependencies if needed. +6. Run `HOMEBREW_NO_INSTALL_FROM_API=1 brew install --build-from-source --verbose --debug uffizzi` and manually test the new uffizzi version (make sure that all other homebrew uffizzi versions are uninstalled). +7. Run `brew audit --strict --online` to check if the Formula adheres to the Homebrew style. +8. If tests and audit pass, create a PR into master in the UffizziCloud/homebrew-tap [repository] (https://github.com/UffizziCloud/homebrew-tap) with the new Formula. diff --git a/Gemfile.lock b/Gemfile.lock index 117f04f9..0a7d7040 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - uffizzi-cli (2.1.0) + uffizzi-cli (2.1.1) activesupport awesome_print faker diff --git a/Makefile b/Makefile index 2ea2998c..68583f56 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,7 @@ NEXT_PATCH=$(shell docker-compose run --rm gem bash -c "bundle exec bump show-ne NEXT_MINOR=$(shell docker-compose run --rm gem bash -c "bundle exec bump show-next minor") NEXT_MAJOR=$(shell docker-compose run --rm gem bash -c "bundle exec bump show-next major") TAG_FULL_VERSION=v${VERSION} +CURRENT_LOCAL_GEM_NAME := $(shell ls | sort -r | grep 'uffizzi-cli-.*\.gem' | head -1) release_gem: mkdir -p ${HOME}/.gem @@ -53,4 +54,17 @@ lint: run_single_test: docker-compose run --rm gem bash -c 'bundle exec rake test TEST=$(TEST_PATH) TESTOPTS="--name=${TEST_NAME}"' +gem_build: + gem build uffizzi.gemspec + +gem_install: + gem install $(CURRENT_LOCAL_GEM_NAME) + +gem_build_install: + make gem_build + make gem_install + +gem_uninstall: + gem uninstall uffizzi-cli + .PHONY: test diff --git a/lib/uffizzi.rb b/lib/uffizzi.rb index e557536c..0326dad6 100644 --- a/lib/uffizzi.rb +++ b/lib/uffizzi.rb @@ -36,5 +36,9 @@ def prompt def root @root ||= Pathname.new(File.expand_path('..', __dir__)) end + + def process + @process ||= Process + end end end diff --git a/lib/uffizzi/cli/cluster.rb b/lib/uffizzi/cli/cluster.rb index cd3e67f5..02bbd31f 100644 --- a/lib/uffizzi/cli/cluster.rb +++ b/lib/uffizzi/cli/cluster.rb @@ -11,8 +11,6 @@ require 'uffizzi/services/kubeconfig_service' require 'uffizzi/services/cluster/disconnect_service' -MANUAL = 'manual' - module Uffizzi class Cli::Cluster < Thor class Error < StandardError; end @@ -115,7 +113,7 @@ def handle_create_command(project_slug, command_args) end cluster_name = command_args[:name] || options[:name] || ClusterService.generate_name - creation_source = options[:"creation-source"] || MANUAL + creation_source = options[:"creation-source"] || ClusterService::MANUAL_CREATION_SOURCE unless ClusterService.valid_name?(cluster_name) Uffizzi.ui.say_error_and_exit("Cluster name: #{cluster_name} is not valid.") diff --git a/lib/uffizzi/cli/dev.rb b/lib/uffizzi/cli/dev.rb index f6fe6eed..a41bbdb0 100644 --- a/lib/uffizzi/cli/dev.rb +++ b/lib/uffizzi/cli/dev.rb @@ -2,6 +2,7 @@ require 'uffizzi/services/command_service' require 'uffizzi/services/cluster_service' +require 'uffizzi/services/dev_service' require 'uffizzi/services/kubeconfig_service' module Uffizzi @@ -9,12 +10,22 @@ class Cli::Dev < Thor include ApiClient desc 'start [CONFIG]', 'Start dev environment' + method_option :quiet, type: :boolean, aliases: :q + method_option :'default-repo', type: :string + method_option :kubeconfig, type: :string def start(config_path = 'skaffold.yaml') - check_skaffold_existence + DevService.check_skaffold_existence + DevService.check_running_daemon if options[:quiet] + DevService.check_skaffold_config_existence(config_path) check_login cluster_id, cluster_name = start_create_cluster kubeconfig = wait_cluster_creation(cluster_name) - launch_scaffold(config_path) + + if options[:quiet] + launch_demonise_skaffold(config_path) + else + DevService.start_basic_skaffold(config_path, options) + end ensure if defined?(cluster_name).present? && defined?(cluster_id).present? kubeconfig = defined?(kubeconfig).present? ? kubeconfig : nil @@ -22,6 +33,20 @@ def start(config_path = 'skaffold.yaml') end end + desc 'stop', 'Stop dev environment' + def stop + return Uffizzi.ui.say('Uffizzi dev is not running') unless File.exist?(DevService.pid_path) + + pid = File.read(DevService.pid_path).to_i + File.delete(DevService.pid_path) + + Uffizzi.process.kill('QUIT', pid) + Uffizzi.ui.say('Uffizzi dev was stopped') + rescue Errno::ESRCH + Uffizzi.ui.say('Uffizzi dev is not running') + File.delete(DevService.pid_path) + end + private def check_login @@ -31,7 +56,7 @@ def check_login def start_create_cluster cluster_name = ClusterService.generate_name - creation_source = MANUAL + creation_source = ClusterService::MANUAL_CREATION_SOURCE params = cluster_creation_params(cluster_name, creation_source) Uffizzi.ui.say('Start creating a cluster') response = create_cluster(ConfigFile.read_option(:server), project_slug, params) @@ -56,7 +81,7 @@ def wait_cluster_creation(cluster_name) end def handle_succeed_cluster_creation(cluster_data) - kubeconfig_path = KubeconfigService.default_path + kubeconfig_path = options[:kubeconfig] || KubeconfigService.default_path parsed_kubeconfig = parse_kubeconfig(cluster_data[:kubeconfig]) Uffizzi.ui.say("Cluster with name: #{cluster_data[:name]} was created.") @@ -99,6 +124,8 @@ def cluster_creation_params(name, creation_source) end def handle_delete_cluster(cluster_id, cluster_name, kubeconfig) + return if cluster_id.nil? || cluster_name.nil? + exclude_kubeconfig(cluster_id, kubeconfig) if kubeconfig.present? params = { @@ -149,25 +176,19 @@ def parse_kubeconfig(kubeconfig) Psych.safe_load(Base64.decode64(kubeconfig)) end - def launch_scaffold(config_path) - Uffizzi.ui.say('Start skaffold') - cmd = "skaffold dev --filename='#{config_path}'" + def launch_demonise_skaffold(config_path) + Uffizzi.process.daemon(true) - Uffizzi.ui.popen2e(cmd) do |_stdin, stdout_and_stderr, wait_thr| - stdout_and_stderr.each { |l| puts l } - wait_thr.value + at_exit do + File.delete(DevService.pid_path) if File.exist?(DevService.pid_path) end - end - - def check_skaffold_existence - cmd = 'skaffold version' - stdout_str, stderr_str = Uffizzi.ui.capture3(cmd) - - return if stdout_str.present? && stderr_str.blank? - Uffizzi.ui.say_error_and_exit(stderr_str) + File.delete(DevService.logs_path) if File.exist?(DevService.logs_path) + File.write(DevService.pid_path, Uffizzi.process.pid) + DevService.start_check_pid_file_existence + DevService.start_demonised_skaffold(config_path, options) rescue StandardError => e - Uffizzi.ui.say_error_and_exit(e.message) + File.open(DevService.logs_path, 'a') { |f| f.puts(e.message) } end def project_slug diff --git a/lib/uffizzi/config_file.rb b/lib/uffizzi/config_file.rb index 4b84694e..c5c3663c 100644 --- a/lib/uffizzi/config_file.rb +++ b/lib/uffizzi/config_file.rb @@ -5,6 +5,7 @@ module Uffizzi class ConfigFile + CONFIG_DIR = "#{Dir.home}/.config/uffizzi" CONFIG_PATH = "#{Dir.home}/.config/uffizzi/config_default.json" class << self diff --git a/lib/uffizzi/services/cluster_service.rb b/lib/uffizzi/services/cluster_service.rb index 576f77fd..283d2d57 100644 --- a/lib/uffizzi/services/cluster_service.rb +++ b/lib/uffizzi/services/cluster_service.rb @@ -9,6 +9,7 @@ class ClusterService CLUSTER_STATE_FAILED_DEPLOY_NAMESPACE = 'failed_deploy_namespace' CLUSTER_STATE_FAILED = 'failed' CLUSTER_NAME_MAX_LENGTH = 15 + MANUAL_CREATION_SOURCE = 'manual' class << self include ApiClient diff --git a/lib/uffizzi/services/dev_service.rb b/lib/uffizzi/services/dev_service.rb new file mode 100644 index 00000000..57999660 --- /dev/null +++ b/lib/uffizzi/services/dev_service.rb @@ -0,0 +1,108 @@ +# frozen_string_literal: true + +require 'uffizzi/clients/api/api_client' + +class DevService + class << self + include ApiClient + + DEFAULT_REGISTRY_REPO = 'registry.uffizzi.com' + + def check_running_daemon + return unless File.exist?(pid_path) + + pid = File.read(pid_path) + File.delete(pid_path) if pid.blank? + Uffizzi.process.kill(0, pid.to_i) + + Uffizzi.ui.say_error_and_exit("You have already started uffizzi dev as daemon. To stop the process do 'uffizzi dev stop'") + rescue Errno::ESRCH + File.delete(pid_path) + end + + def start_check_pid_file_existence + Thread.new do + loop do + Uffizzi.process.kill('QUIT', Uffizzi.process.pid) unless File.exist?(pid_path) + sleep(1) + end + end + end + + def start_basic_skaffold(config_path, options) + Uffizzi.ui.say('Start skaffold') + cmd = build_skaffold_dev_command(config_path, options) + + Uffizzi.ui.popen2e(cmd) do |_stdin, stdout_and_stderr, wait_thr| + stdout_and_stderr.each { |l| Uffizzi.ui.say(l) } + wait_thr.value + end + end + + def start_demonised_skaffold(config_path, options) + File.write(logs_path, "Start skaffold\n") + cmd = build_skaffold_dev_command(config_path, options) + + Uffizzi.ui.popen2e(cmd) do |_stdin, stdout_and_stderr, wait_thr| + File.open(logs_path, 'a') do |f| + stdout_and_stderr.each do |line| + f.puts(line) + f.flush + end + end + + wait_thr.value + end + end + + def check_skaffold_existence + cmd = 'skaffold version' + stdout_str, stderr_str = Uffizzi.ui.capture3(cmd) + + return if stdout_str.present? && stderr_str.blank? + + Uffizzi.ui.say_error_and_exit(stderr_str) + rescue StandardError => e + Uffizzi.ui.say_error_and_exit(e.message) + end + + def check_skaffold_config_existence(config_path) + msg = 'A valid dev environment configuration is required. Please provide a valid config,'\ + "\r\n"\ + 'or run `skaffold init` to generate a skaffold.yaml configuration.'\ + "\r\n"\ + 'See the `uffizzi dev start --help` page for supported configs and usage details.' + + Uffizzi.ui.say_error_and_exit(msg) unless File.exist?(config_path) + end + + def pid_path + File.join(Uffizzi::ConfigFile::CONFIG_DIR, 'uffizzi_dev.pid') + end + + def logs_path + File.join(Uffizzi::ConfigFile::CONFIG_DIR, 'uffizzi_dev.log') + end + + def build_skaffold_dev_command(config_path, options) + cmd = [ + 'skaffold dev', + "--filename='#{config_path}'", + "--default-repo='#{default_registry_repo(options[:'default-repo'])}'", + "--kubeconfig='#{default_kubeconfig_path(options[:kubeconfig])}'", + ] + + cmd.join(' ') + end + + def default_registry_repo(repo) + repo || DEFAULT_REGISTRY_REPO + end + + def default_kubeconfig_path(kubeconfig_path) + path = kubeconfig_path || KubeconfigService.default_path + + File.expand_path(path) + end + end +end diff --git a/lib/uffizzi/services/kubeconfig_service.rb b/lib/uffizzi/services/kubeconfig_service.rb index 216dbd47..e39f336f 100644 --- a/lib/uffizzi/services/kubeconfig_service.rb +++ b/lib/uffizzi/services/kubeconfig_service.rb @@ -81,7 +81,9 @@ def save_to_filepath(filepath, kubeconfig = nil) end def default_path - kubeconfig_env_path || Uffizzi.configuration.default_kubeconfig_path + path = kubeconfig_env_path || Uffizzi.configuration.default_kubeconfig_path + + File.expand_path(path) end def read_kubeconfig(filepath) diff --git a/lib/uffizzi/version.rb b/lib/uffizzi/version.rb index 46210b97..50b62d34 100644 --- a/lib/uffizzi/version.rb +++ b/lib/uffizzi/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module Uffizzi - VERSION = '2.1.0' + VERSION = '2.1.1' end diff --git a/man/uffizzi-dev-start b/man/uffizzi-dev-start index af8585c0..19a27c2f 100644 --- a/man/uffizzi-dev-start +++ b/man/uffizzi-dev-start @@ -1,29 +1,99 @@ .\" generated with Ronn-NG/v0.9.1 .\" http://github.com/apjanke/ronn-ng/tree/0.9.1 -.TH "UFFIZZI\-DEV\-START" "" "September 2023" "" +.TH "UFFIZZI\-DEV\-START" "" "October 2023" "" .SH "NAME" -\fBuffizzi\-dev\-start\fR \- start development environment +\fBuffizzi\-dev\-start\fR \- start a development environment .SH "SYNOPSIS" .nf -uffizzi dev start [SKAFFOLD_CONFIG_FILE_PATH] +uffizzi dev start [CONFIG_FILE] [FLAGS] .fi .SH "DESCRIPTION" .nf -Creates a new cluster and start skaffold\. If no SKAFFOLD_CONFIG_FILE_PATH is specified, -the default path will be used\. -Default SKAFFOLD_CONFIG_FILE_PATH is \'skaffold\.yaml\' +Creates a Uffizzi cluster preconfigured for development +workflows, including building, pushing, and deploying +your changes every time project files are saved\. +current\-context is updated in kubeconfig file\. + +This command watches for file changes in a given local +project directory, as specified in your configuration file\. +It then serializes those changes and redeploys them onto +a Uffizzi cluster\. + +The command looks for a configuration at the specified +path CONFIG_FILE\. Skaffold configurations are currently +supported\. For help creating a skaffold\.yaml file, see: +https://skaffold\.dev/docs/init/ + +If a kubeconfig exists For more information on Uffizzi clusters, see: https://docs\.uffizzi\.com/references/cli/ .fi +.SH "POSITIONAL ARGUMENTS" +.nf +[CONFIG_FILE] + Path to the development environment configuration file\. + Currently supports skaffold\.yaml files\. +.fi +.SH "FLAGS" +.nf + \-\-build="" + This option specifies whether to build images on the + local environment or on the remote Uffizzi cluster\. + Possible values are "local" or "remote"\. + + \-\-default\-repo="" + A public or private repo used to push/pull build + artifacts\. Overrides the global default image registry: + "registry\.uffizzi\.com"\. See `uffizzi connect \-h` for + adding private registry credentials\. + + \-\-quiet, \-q + Run the development process in detached mode (i\.e\., in + the background)\. Without this option, logs are streamed + to the terminal in the foreground\. Run \'uffizzi dev stop` + to stop the detached process\. + + \-\-help, \-h + Show this message and exit\. + + \-\-kubeconfig="/path/to/your/kubeconfig" + Path to kubeconfig file\. If this option is not specified, + this command looks for the file at ~/\.kube/config\. +.fi .SH "EXAMPLES" .nf -To start development environment, run: +If your configuration file is in the current working +directory and you want to use an auto\-generated name, +run: $ uffizzi dev start -To start development environment with custom skaffold\.yaml file, run: +To start a dev environment using a skaffold\.yaml config +file in directory \'~/foo\', run: + + $ uffizzi dev start ~/foo/skaffold\.yaml + +To start a dev environment in quiet mode, +run: + + $ uffizzi dev start \-\-quiet + +To push your build artifacts to a private Docker Hub repo +called \'acme/foo\', first add your Docker Hub credentials: + + $ uffizzi connect docker\-hub + (See `uffizzi connect \-h` for other registry options) + +\|\.\|\.\|\.then override the default repo: + + $ uffizzi dev start \e + \-\-default\-repo="hub\.docker\.com/acme/foo" + +To start a dev environment using an alternate kubeconfig file, +run: - $ uffizzi cluster create /path/to/skaffold\.yaml + $ uffizzi dev start \e + \-\-kubeconfig="/path/to/alternate/kubeconfig" .fi diff --git a/man/uffizzi-dev-start.ronn b/man/uffizzi-dev-start.ronn index 5a48719e..59e510c4 100644 --- a/man/uffizzi-dev-start.ronn +++ b/man/uffizzi-dev-start.ronn @@ -1,22 +1,90 @@ -uffizzi-dev-start - start development environment +uffizzi-dev-start - start a development environment ================================================================ ## SYNOPSIS - uffizzi dev start [SKAFFOLD_CONFIG_FILE_PATH] + uffizzi dev start [CONFIG_FILE] [FLAGS] ## DESCRIPTION - Creates a new cluster and start skaffold. If no SKAFFOLD_CONFIG_FILE_PATH is specified, - the default path will be used. - Default SKAFFOLD_CONFIG_FILE_PATH is 'skaffold.yaml' + Creates a Uffizzi cluster preconfigured for development + workflows, including building, pushing, and deploying + your changes every time project files are saved. + current-context is updated in kubeconfig file. + + This command watches for file changes in a given local + project directory, as specified in your configuration file. + It then serializes those changes and redeploys them onto + a Uffizzi cluster. + + The command looks for a configuration at the specified + path CONFIG_FILE. Skaffold configurations are currently + supported. For help creating a skaffold.yaml file, see: + https://skaffold.dev/docs/init/ + + If a kubeconfig exists For more information on Uffizzi clusters, see: https://docs.uffizzi.com/references/cli/ +## POSITIONAL ARGUMENTS + [CONFIG_FILE] + Path to the development environment configuration file. + Currently supports skaffold.yaml files. + +## FLAGS + --build="" + This option specifies whether to build images on the + local environment or on the remote Uffizzi cluster. + Possible values are "local" or "remote". + + --default-repo="" + A public or private repo used to push/pull build + artifacts. Overrides the global default image registry: + "registry.uffizzi.com". See `uffizzi connect -h` for + adding private registry credentials. + + --quiet, -q + Run the development process in detached mode (i.e., in + the background). Without this option, logs are streamed + to the terminal in the foreground. Run 'uffizzi dev stop` + to stop the detached process. + + --help, -h + Show this message and exit. + + --kubeconfig="/path/to/your/kubeconfig" + Path to kubeconfig file. If this option is not specified, + this command looks for the file at ~/.kube/config. + ## EXAMPLES - To start development environment, run: + If your configuration file is in the current working + directory and you want to use an auto-generated name, + run: $ uffizzi dev start - To start development environment with custom skaffold.yaml file, run: + To start a dev environment using a skaffold.yaml config + file in directory '~/foo', run: + + $ uffizzi dev start ~/foo/skaffold.yaml + + To start a dev environment in quiet mode, + run: + + $ uffizzi dev start --quiet + + To push your build artifacts to a private Docker Hub repo + called 'acme/foo', first add your Docker Hub credentials: + + $ uffizzi connect docker-hub + (See `uffizzi connect -h` for other registry options) + + ...then override the default repo: + + $ uffizzi dev start \ + --default-repo="hub.docker.com/acme/foo" + + To start a dev environment using an alternate kubeconfig file, + run: - $ uffizzi cluster create /path/to/skaffold.yaml + $ uffizzi dev start \ + --kubeconfig="/path/to/alternate/kubeconfig" diff --git a/man/uffizzi-dev-stop b/man/uffizzi-dev-stop new file mode 100644 index 00000000..cf105533 --- /dev/null +++ b/man/uffizzi-dev-stop @@ -0,0 +1,41 @@ +.\" generated with Ronn-NG/v0.9.1 +.\" http://github.com/apjanke/ronn-ng/tree/0.9.1 +.TH "UFFIZZI\-DEV\-STOP" "" "October 2023" "" +.SH "NAME" +\fBuffizzi\-dev\-stop\fR \- stop a development environment +.SH "SYNOPSIS" +.nf +uffizzi dev stop +.fi +.SH "DESCRIPTION" +.nf +Stops a dev environment and deletes the backing +Uffizzi cluster resources, including any persistent +volumes, and the namespace itself\. The Uffizzi +cluster config is deleted from the kubeconfig file\. + +This command watches for file changes in a given local +project directory, as specified in your configuration file\. +It then serializes those changes and redeploys them onto +a Uffizzi cluster\. + +The command looks for a configuration at the specified +path CONFIG_FILE\. Skaffold configurations are currently +supported\. For help creating a skaffold\.yaml file, see: +https://skaffold\.dev/docs/init/ + +For more information on Uffizzi clusters, see: +https://docs\.uffizzi\.com/references/cli/ +.fi +.SH "FLAGS" +.nf + \-\-help, \-h + Show this message and exit\. +.fi +.SH "EXAMPLES" +.nf +To stop a dev environment, run: + + $ uffizzi dev stop +.fi + diff --git a/man/uffizzi-dev-stop.ronn b/man/uffizzi-dev-stop.ronn new file mode 100644 index 00000000..1c612cc3 --- /dev/null +++ b/man/uffizzi-dev-stop.ronn @@ -0,0 +1,33 @@ +uffizzi-dev-stop - stop a development environment +================================================================ + +## SYNOPSIS + uffizzi dev stop + +## DESCRIPTION + Stops a dev environment and deletes the backing + Uffizzi cluster resources, including any persistent + volumes, and the namespace itself. The Uffizzi + cluster config is deleted from the kubeconfig file. + + This command watches for file changes in a given local + project directory, as specified in your configuration file. + It then serializes those changes and redeploys them onto + a Uffizzi cluster. + + The command looks for a configuration at the specified + path CONFIG_FILE. Skaffold configurations are currently + supported. For help creating a skaffold.yaml file, see: + https://skaffold.dev/docs/init/ + + For more information on Uffizzi clusters, see: + https://docs.uffizzi.com/references/cli/ + +## FLAGS + --help, -h + Show this message and exit. + +## EXAMPLES + To stop a dev environment, run: + + $ uffizzi dev stop diff --git a/test/support/mocks/mock_process.rb b/test/support/mocks/mock_process.rb new file mode 100644 index 00000000..87161260 --- /dev/null +++ b/test/support/mocks/mock_process.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +class MockProcess + attr_accessor :pid + + def initialize + @pid = generate_pid + end + + def kill(sig, pid) + return @pid if sig.zero? && pid == @pid + raise Errno::ESRCH if pid != @pid + + @pid = nil + end + + def daemon + @pid = generate_pid + end + + private + + def generate_pid + (Time.now.utc.to_f * 100_000).to_i + end +end diff --git a/test/test_helper.rb b/test/test_helper.rb index 635d0765..c7e85845 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -20,6 +20,7 @@ require 'uffizzi/config_file' require 'uffizzi/shell' require 'uffizzi/error' +require 'uffizzi/services/dev_service' include AuthSupport include FixtureSupport include UffizziStubSupport @@ -34,16 +35,23 @@ class Minitest::Test TEST_CONFIG_PATH = 'tmp/config_default.json' TEST_TOKEN_PATH = 'tmp/token_default.json' + TEST_PID_PATH = 'tmp/dev.pid' + TEST_DEV_LOGS_PATH = 'tmp/dev-logs.txt' def before_setup super @mock_prompt = MockPrompt.new @mock_shell = MockShell.new + @mock_process = MockProcess.new Uffizzi.stubs(:ui).returns(@mock_shell) Uffizzi.stubs(:prompt).returns(@mock_prompt) + Uffizzi.stubs(:process).returns(@mock_process) Uffizzi::ConfigFile.stubs(:config_path).returns(TEST_CONFIG_PATH) Uffizzi::Token.stubs(:token_path).returns(TEST_TOKEN_PATH) + Uffizzi::ConfigFile.stubs(:config_path).returns(TEST_CONFIG_PATH) + DevService.stubs(:pid_path).returns(TEST_PID_PATH) + DevService.stubs(:logs_path).returns(TEST_DEV_LOGS_PATH) end def before_teardown @@ -58,6 +66,8 @@ def before_teardown File.delete(TEST_CONFIG_PATH) if File.exist?(TEST_CONFIG_PATH) File.delete(TEST_TOKEN_PATH) if File.exist?(TEST_TOKEN_PATH) + File.delete(TEST_PID_PATH) if File.exist?(TEST_PID_PATH) + File.delete(TEST_DEV_LOGS_PATH) if File.exist?(TEST_DEV_LOGS_PATH) end def command_options(options) diff --git a/test/uffizzi/cli/dev_test.rb b/test/uffizzi/cli/dev_test.rb index 081a6b5e..b437f449 100644 --- a/test/uffizzi/cli/dev_test.rb +++ b/test/uffizzi/cli/dev_test.rb @@ -15,6 +15,9 @@ def setup @project_slug = Uffizzi::ConfigFile.read_option(:project) tmp_dir_name = (Time.now.utc.to_f * 100_000).to_i @kubeconfig_path = "/tmp/test/#{tmp_dir_name}/test-kubeconfig.yaml" + @skaffold_file_path = "/tmp/test/#{tmp_dir_name}/skaffold.yaml" + FileUtils.mkdir_p(File.dirname(@skaffold_file_path)) + File.write(@skaffold_file_path, '') end def test_start_dev @@ -26,7 +29,7 @@ def test_start_dev @mock_shell.promise_execute(/skaffold version/, stdout: 'v.2.7.1') @mock_shell.promise_execute(/skaffold dev --filename/, stdout: 'Good') - @dev.start + @dev.start(@skaffold_file_path) cluster_from_config = Uffizzi::ConfigFile.read_option(:clusters) @@ -58,7 +61,7 @@ def test_start_dev_with_existed_current_context @mock_shell.promise_execute(/skaffold version/, stdout: 'v.2.7.1') @mock_shell.promise_execute(/skaffold dev --filename/, stdout: 'Good') - @dev.start + @dev.start(@skaffold_file_path) cluster_from_config = Uffizzi::ConfigFile.read_option(:clusters) current_kubeconfig = Psych.safe_load(File.read(@kubeconfig_path)) @@ -70,4 +73,74 @@ def test_start_dev_with_existed_current_context assert_requested(stubbed_uffizzi_cluster_get_request) assert_requested(stubbed_uffizzi_cluster_delete_request) end + + def test_start_dev_as_daemon + cluster_create_body = json_fixture('files/uffizzi/uffizzi_cluster_deploying.json') + cluster_get_body = json_fixture('files/uffizzi/uffizzi_cluster_deployed.json') + stubbed_uffizzi_cluster_create_request = stub_uffizzi_create_cluster(cluster_create_body, @project_slug) + stubbed_uffizzi_cluster_get_request = stub_get_cluster_request(cluster_get_body, @project_slug) + stubbed_uffizzi_cluster_delete_request = stub_uffizzi_delete_cluster(@project_slug) + @mock_shell.promise_execute(/skaffold version/, stdout: 'v.2.7.1') + @mock_shell.promise_execute(/skaffold dev --filename/, stdout: 'Good') + @dev.options = command_options(quiet: true) + + @dev.start(@skaffold_file_path) + + cluster_from_config = Uffizzi::ConfigFile.read_option(:clusters) + + assert_match('deleted', Uffizzi.ui.last_message) + assert_nil(cluster_from_config) + assert_requested(stubbed_uffizzi_cluster_create_request) + assert_requested(stubbed_uffizzi_cluster_get_request) + assert_requested(stubbed_uffizzi_cluster_delete_request) + end + + def test_start_dev_as_daemon_when_deamon_already_run + @mock_shell.promise_execute(/skaffold version/, stdout: 'v.2.7.1') + @mock_shell.promise_execute(/skaffold dev --filename/, stdout: 'Good') + @dev.options = command_options(quiet: true) + File.write(DevService.pid_path, '1000') + @mock_process.pid = 1000 + + error = assert_raises(MockShell::ExitError) do + @dev.start(@skaffold_file_path) + end + + assert_match('You already start uffizzi', error.message) + end + + def test_start_dev_without_skaffold_config + File.delete(@skaffold_file_path) + @mock_shell.promise_execute(/skaffold version/, stdout: 'v.2.7.1') + + error = assert_raises(MockShell::ExitError) do + @dev.start(@skaffold_file_path) + end + + assert_match('Please provide a valid config', error.message) + end + + def test_start_dev_with_kubeconfig_and_default_repo_flags + default_repo = 'ttl.sh' + kubeconfig_path = '/tmp/some_path' + cluster_create_body = json_fixture('files/uffizzi/uffizzi_cluster_deploying.json') + cluster_get_body = json_fixture('files/uffizzi/uffizzi_cluster_deployed.json') + stubbed_uffizzi_cluster_create_request = stub_uffizzi_create_cluster(cluster_create_body, @project_slug) + stubbed_uffizzi_cluster_get_request = stub_get_cluster_request(cluster_get_body, @project_slug) + stubbed_uffizzi_cluster_delete_request = stub_uffizzi_delete_cluster(@project_slug) + @mock_shell.promise_execute(/skaffold version/, stdout: 'v.2.7.1') + skaffold_dev_regex = /skaffold dev --filename='.*' --default-repo='#{default_repo}' --kubeconfig='#{kubeconfig_path}'/ + @mock_shell.promise_execute(skaffold_dev_regex, stdout: 'Good') + + @dev.options = command_options('default-repo': default_repo, kubeconfig: kubeconfig_path) + @dev.start(@skaffold_file_path) + + cluster_from_config = Uffizzi::ConfigFile.read_option(:clusters) + + assert_match('deleted', Uffizzi.ui.last_message) + assert_nil(cluster_from_config) + assert_requested(stubbed_uffizzi_cluster_create_request) + assert_requested(stubbed_uffizzi_cluster_get_request) + assert_requested(stubbed_uffizzi_cluster_delete_request) + end end