diff --git a/qa/gems/gitlab-cng/lib/gitlab/cng/commands/create.rb b/qa/gems/gitlab-cng/lib/gitlab/cng/commands/create.rb index 01375754069b93a5bcb137e6913ca44d5c49b701..732ea2ceaaeccae80fae608ab2e1f7f1531db994 100644 --- a/qa/gems/gitlab-cng/lib/gitlab/cng/commands/create.rb +++ b/qa/gems/gitlab-cng/lib/gitlab/cng/commands/create.rb @@ -8,33 +8,8 @@ module Commands # Create command composed of subcommands that create various resources needed for CNG deployment # class Create < Command - desc "cluster", "Create kind cluster for local deployments" - option :name, - desc: "Cluster name", - default: "gitlab", - type: :string, - aliases: "-n" - option :ci, - desc: "Use CI specific configuration", - default: false, - type: :boolean, - aliases: "-c" - option :docker_hostname, - desc: "Custom docker hostname if remote docker instance is used, like docker-in-docker", - type: :string, - aliases: "-d" - option :host_http_port, - desc: "Extra port mapping for Gitlab HTTP port", - type: :numeric, - default: 80 - option :host_ssh_port, - desc: "Extra port mapping for Gitlab ssh port", - type: :numeric, - default: 22 - def cluster - Kind::Cluster.new(**symbolized_options).create - end - + # TODO: without separate cluster creation command, create subcommands are somewhat redundant, + # consider removing desc "deployment [TYPE]", "Create specific type of deployment" subcommand "deployment", Subcommands::Deployment end diff --git a/qa/gems/gitlab-cng/lib/gitlab/cng/commands/destroy.rb b/qa/gems/gitlab-cng/lib/gitlab/cng/commands/destroy.rb index f65c6c82ff6bccfdded47273e2d087adfcaab824..2619f60a5824ca0cf29e28c6c28e54c566229d61 100644 --- a/qa/gems/gitlab-cng/lib/gitlab/cng/commands/destroy.rb +++ b/qa/gems/gitlab-cng/lib/gitlab/cng/commands/destroy.rb @@ -6,17 +6,12 @@ module Commands # Destroy command consisting of cleanup for cluster and deployments # class Destroy < Command - desc "cluster", "Destroy kind cluster" - option :name, - desc: "Cluster name", - default: "gitlab", - type: :string, - aliases: "-n" + desc "cluster", "Destroy kind cluster created for kind type deployments" def cluster - delete = TTY::Prompt.new.yes?("Are you sure you want to delete cluster #{options[:name]}?") + delete = TTY::Prompt.new.yes?("Are you sure you want to delete cluster #{Kind::Cluster::CLUSTER_NAME}?") return unless delete - Kind::Cluster.destroy(options[:name]) + Kind::Cluster.destroy end desc "deployment [NAME]", "Destroy specific deployment and all it's resources, " \ @@ -35,6 +30,11 @@ def cluster desc: "Timeout for helm release uninstall", default: "10m", type: :string + option :with_cluster, + desc: "Destroy kind cluster that was created for the deployment, " \ + "only applicable for deployment type 'kind'", + default: false, + type: :boolean def deployment(name = Subcommands::Deployment::DEFAULT_HELM_RELEASE_NAME) prompt = TTY::Prompt.new delete = prompt.yes?("Are you sure you want to delete deployment '#{name}'?") @@ -52,6 +52,8 @@ def deployment(name = Subcommands::Deployment::DEFAULT_HELM_RELEASE_NAME) cleanup_configuration: cleanup_configuration, timeout: options[:timeout] ) + + Kind::Cluster.destroy if options[:with_cluster] end end end diff --git a/qa/gems/gitlab-cng/lib/gitlab/cng/commands/subcommands/deployment.rb b/qa/gems/gitlab-cng/lib/gitlab/cng/commands/subcommands/deployment.rb index ce5de1fec883fb37cacbf61ba73792ea8dc4d7d2..ada2fe3afecee3a7b6f65129ce95cbbc1cc77b47 100644 --- a/qa/gems/gitlab-cng/lib/gitlab/cng/commands/subcommands/deployment.rb +++ b/qa/gems/gitlab-cng/lib/gitlab/cng/commands/subcommands/deployment.rb @@ -96,9 +96,9 @@ def kind(name = DEFAULT_HELM_RELEASE_NAME) return print_deploy_args("kind") if options[:print_deploy_args] && options[:ci] if options[:create_cluster] - invoke(Commands::Create, :cluster, [], **symbolized_options.slice( + Kind::Cluster.new(**symbolized_options.slice( :docker_hostname, :ci, :host_http_port, :host_ssh_port - )) + )).create end configuration_args = symbolized_options.slice( diff --git a/qa/gems/gitlab-cng/lib/gitlab/cng/lib/deployment/configurations/kind.rb b/qa/gems/gitlab-cng/lib/gitlab/cng/lib/deployment/configurations/kind.rb index b84fecde4d9d243ce30fa0f64d6000cd92681d9d..96887a6e449b49873ce48c6b3fb11ba0dc276766 100644 --- a/qa/gems/gitlab-cng/lib/gitlab/cng/lib/deployment/configurations/kind.rb +++ b/qa/gems/gitlab-cng/lib/gitlab/cng/lib/deployment/configurations/kind.rb @@ -83,8 +83,8 @@ def values service: { type: "NodePort", nodePorts: { - "gitlab-shell": Cng::Kind::Cluster::SSH_PORT, - http: Cng::Kind::Cluster::HTTP_PORT + "gitlab-shell": Cng::Kind::Cluster.host_port_mapping(host_ssh_port), + http: Cng::Kind::Cluster.host_port_mapping(host_http_port) } } } diff --git a/qa/gems/gitlab-cng/lib/gitlab/cng/lib/kind/cluster.rb b/qa/gems/gitlab-cng/lib/gitlab/cng/lib/kind/cluster.rb index ea8062f8d9cfd2b9a7691ee1e35a790f00d2d2f4..52b49999036a6831420fdd5a8b1344bf60a99925 100644 --- a/qa/gems/gitlab-cng/lib/gitlab/cng/lib/kind/cluster.rb +++ b/qa/gems/gitlab-cng/lib/gitlab/cng/lib/kind/cluster.rb @@ -14,29 +14,50 @@ class Cluster extend Helpers::Output extend Helpers::Shell - HTTP_PORT = 32080 - SSH_PORT = 32022 + CLUSTER_NAME = "gitlab" METRICS_CHART_NAME = "metrics-server" METRICS_CHART_URL = "https://kubernetes-sigs.github.io/metrics-server/" METRICS_CHART_VERSION = "^3.12" - # Destroy kind cluster - # - # @param [String] name - # @return [void] - def self.destroy(name) - log("Destroying cluster '#{name}'", :info, bright: true) - return log("Cluster not found, skipping!", :warn) unless execute_shell(%w[kind get clusters]).include?(name) + class << self + # Destroy kind cluster + # + # @param [String] name + # @return [void] + def destroy + log("Destroying cluster '#{CLUSTER_NAME}'", :info, bright: true) + + unless execute_shell(%w[kind get clusters]).include?(CLUSTER_NAME) + return log("Cluster not found, skipping!", :warn) + end + + Helpers::Spinner.spin("destroying cluster") do + puts execute_shell(%W[kind delete cluster --name #{CLUSTER_NAME}]) + end + end + + # Get configured port mapping + # + # @param [Integer] port + # @return [Integer] + def host_port_mapping(port) + yml = YAML.safe_load(File.read(kind_config_file)) - Helpers::Spinner.spin("destroying cluster") do - puts execute_shell(%W[kind delete cluster --name #{name}]) + yml["nodes"].first["extraPortMappings"].find { |mapping| mapping["hostPort"] == port }["containerPort"] + end + + # Kind cluster configuration file + # + # @return [String] + def kind_config_file + File.join(Helpers::Utils.tmp_dir, "kind-config.yml") end end - def initialize(ci:, name:, host_http_port:, host_ssh_port:, docker_hostname: nil) + def initialize(ci:, host_http_port:, host_ssh_port:, docker_hostname: nil) @ci = ci - @name = name + @name = CLUSTER_NAME @host_http_port = host_http_port @host_ssh_port = host_ssh_port @docker_hostname = ci ? docker_hostname || "docker" : docker_hostname @@ -126,14 +147,12 @@ def cluster_exists? execute_shell(%w[kind get clusters]).include?(name) end - # Create temporary kind config file + # Create kind config file # # @param [String] config_yml # @return [String] def tmp_config_file(config_yml) - File.join(Helpers::Utils.tmp_dir, "kind-config.yml").tap do |path| - File.write(path, config_yml) - end + self.class.kind_config_file.tap { |path| File.write(path, config_yml) } end # Temporary ci specific kind configuration file @@ -159,10 +178,10 @@ def ci_config certSANs: - "#{docker_hostname}" extraPortMappings: - - containerPort: #{HTTP_PORT} + - containerPort: #{http_port} hostPort: #{host_http_port} listenAddress: "0.0.0.0" - - containerPort: #{SSH_PORT} + - containerPort: #{ssh_port} hostPort: #{host_ssh_port} listenAddress: "0.0.0.0" YML @@ -193,16 +212,30 @@ def default_config - "<%= docker_hostname %>" <% end -%> extraPortMappings: - - containerPort: #{HTTP_PORT} + - containerPort: #{http_port} hostPort: #{host_http_port} listenAddress: "0.0.0.0" - - containerPort: #{SSH_PORT} + - containerPort: #{ssh_port} hostPort: #{host_ssh_port} listenAddress: "0.0.0.0" YML tmp_config_file(template.result(binding)) end + + # Random http port to expose outside cluster + # + # @return [Integer] + def http_port + @http_port ||= rand(30000..31000) + end + + # Random ssh port to expose outside cluster + # + # @return [Integer] + def ssh_port + @ssh_port ||= rand(31001..32000) + end end end end diff --git a/qa/gems/gitlab-cng/spec/integration/gitlab/cng/cli_spec.rb b/qa/gems/gitlab-cng/spec/integration/gitlab/cng/cli_spec.rb index 1727d7f8d41fb99e60645c13e5fcab8fafc1ee8a..de907c0051de9852f990d67afa0358ad756a29f4 100644 --- a/qa/gems/gitlab-cng/spec/integration/gitlab/cng/cli_spec.rb +++ b/qa/gems/gitlab-cng/spec/integration/gitlab/cng/cli_spec.rb @@ -45,11 +45,6 @@ end describe "create command" do - context "with cluster subcommand" do - it_behaves_like "command with help", %w[create help cluster], /Create kind cluster for local deployments/ - it_behaves_like "executable command", Gitlab::Cng::Commands::Create, %w[create cluster] - end - context "with deployment subcommand" do context "with kind deployment" do it_behaves_like "command with help", %w[create deployment help kind], diff --git a/qa/gems/gitlab-cng/spec/unit/gitlab/cng/commands/create_spec.rb b/qa/gems/gitlab-cng/spec/unit/gitlab/cng/commands/create_spec.rb deleted file mode 100644 index b394e5147385c91620b5c8a2d3283be8ff5f6349..0000000000000000000000000000000000000000 --- a/qa/gems/gitlab-cng/spec/unit/gitlab/cng/commands/create_spec.rb +++ /dev/null @@ -1,35 +0,0 @@ -# frozen_string_literal: true - -RSpec.describe Gitlab::Cng::Commands::Create do - include_context "with command testing helper" - - let(:kind_cluster) { instance_double(Gitlab::Cng::Kind::Cluster, create: nil) } - - before do - allow(Gitlab::Cng::Kind::Cluster).to receive(:new).and_return(kind_cluster) - end - - describe "cluster command" do - let(:command_name) { "cluster" } - - it "defines cluster command" do - expect_command_to_include_attributes(command_name, { - description: "Create kind cluster for local deployments", - name: command_name, - usage: command_name - }) - end - - it "invokes kind cluster creation with correct arguments" do - invoke_command(command_name, [], { ci: true, name: "test-cluster" }) - - expect(kind_cluster).to have_received(:create) - expect(Gitlab::Cng::Kind::Cluster).to have_received(:new).with({ - ci: true, - name: "test-cluster", - host_http_port: 80, - host_ssh_port: 22 - }) - end - end -end diff --git a/qa/gems/gitlab-cng/spec/unit/gitlab/cng/commands/destroy_spec.rb b/qa/gems/gitlab-cng/spec/unit/gitlab/cng/commands/destroy_spec.rb index bdf3a3ab02c9c2f391aa60f3c55d8139e84e9ade..b12843759f830eb91ea552307672c0e8d44febdb 100644 --- a/qa/gems/gitlab-cng/spec/unit/gitlab/cng/commands/destroy_spec.rb +++ b/qa/gems/gitlab-cng/spec/unit/gitlab/cng/commands/destroy_spec.rb @@ -15,17 +15,16 @@ it "defines cluster command" do expect_command_to_include_attributes(command_name, { - description: "Destroy kind cluster", - name: command_name, + description: "Destroy kind cluster created for kind type deployments", usage: command_name }) end context "with accepted prompt" do it "invokes kind cluster deletion" do - invoke_command(command_name, [], { name: "test-cluster" }) + invoke_command(command_name, []) - expect(Gitlab::Cng::Kind::Cluster).to have_received(:destroy).with("test-cluster") + expect(Gitlab::Cng::Kind::Cluster).to have_received(:destroy) end end @@ -33,7 +32,7 @@ let(:prompt_response) { false } it "skips cluster deletion" do - invoke_command(command_name, [], { name: "test-cluster" }) + invoke_command(command_name, []) expect(Gitlab::Cng::Kind::Cluster).not_to have_received(:destroy) end diff --git a/qa/gems/gitlab-cng/spec/unit/gitlab/cng/commands/subcommands/deployment_spec.rb b/qa/gems/gitlab-cng/spec/unit/gitlab/cng/commands/subcommands/deployment_spec.rb index 482b517bccb089e6bb13e3d4d7e81baad1b37115..01bee2a58c957a1437871b659be3763fdce70e4a 100644 --- a/qa/gems/gitlab-cng/spec/unit/gitlab/cng/commands/subcommands/deployment_spec.rb +++ b/qa/gems/gitlab-cng/spec/unit/gitlab/cng/commands/subcommands/deployment_spec.rb @@ -34,7 +34,7 @@ }) end - it "invokes kind cluster creation with correct arguments" do + it "invokes kind deployment creation with correct arguments" do invoke_command(command_name, [], { namespace: "gitlab", ci: false @@ -61,14 +61,13 @@ expect(installation_instance).to have_received(:create) end - it "create kind cluster before deployment" do + it "creates kind cluster before deployment" do invoke_command(command_name, [], { namespace: "gitlab", ci: true }) - expect(Gitlab::Cng::Kind::Cluster).to have_received(:new) - .with(name: "gitlab", ci: true, host_http_port: 80, host_ssh_port: 22) + expect(Gitlab::Cng::Kind::Cluster).to have_received(:new).with(ci: true, host_http_port: 80, host_ssh_port: 22) expect(cluster_instance).to have_received(:create) end diff --git a/qa/gems/gitlab-cng/spec/unit/gitlab/cng/deployment/configurations/kind_spec.rb b/qa/gems/gitlab-cng/spec/unit/gitlab/cng/deployment/configurations/kind_spec.rb index 4a7489da95505b5aacb62fc228a5a9330db3f39a..f815b0b9a27492101c1fd05b3ef8d2d8002167b4 100644 --- a/qa/gems/gitlab-cng/spec/unit/gitlab/cng/deployment/configurations/kind_spec.rb +++ b/qa/gems/gitlab-cng/spec/unit/gitlab/cng/deployment/configurations/kind_spec.rb @@ -14,8 +14,17 @@ end let(:kubeclient) { instance_double(Gitlab::Cng::Kubectl::Client, create_resource: "", execute: "") } + let(:port_mappings) do + { + 80 => 32080, + 22 => 32222 + } + end before do + allow(Gitlab::Cng::Kind::Cluster).to receive(:host_port_mapping).and_return(port_mappings[22]) + allow(Gitlab::Cng::Kind::Cluster).to receive(:host_port_mapping).with(80).and_return(port_mappings[80]) + allow(Gitlab::Cng::Kubectl::Client).to receive(:new).and_return(kubeclient) end @@ -97,8 +106,8 @@ service: { type: "NodePort", nodePorts: { - "gitlab-shell": 32022, - http: 32080 + "gitlab-shell": port_mappings[22], + http: port_mappings[80] } } } diff --git a/qa/gems/gitlab-cng/spec/unit/gitlab/cng/kind/cluster_spec.rb b/qa/gems/gitlab-cng/spec/unit/gitlab/cng/kind/cluster_spec.rb index d365febd9138b19a35d3bcc0869888f8fb986d3f..3144f8d73b6ae029d1b6cbf1b7188b72a49b593c 100644 --- a/qa/gems/gitlab-cng/spec/unit/gitlab/cng/kind/cluster_spec.rb +++ b/qa/gems/gitlab-cng/spec/unit/gitlab/cng/kind/cluster_spec.rb @@ -5,10 +5,9 @@ subject(:cluster) do described_class.new( ci: ci, - name: name, docker_hostname: docker_hostname, - host_http_port: 32080, - host_ssh_port: 32022 + host_http_port: 80, + host_ssh_port: 22 ) end @@ -19,6 +18,8 @@ let(:command_status) { instance_double(Process::Status, success?: true) } let(:clusters) { "kind" } let(:helm) { instance_double(Gitlab::Cng::Helm::Client, add_helm_chart: nil, upgrade: nil) } + let(:http_container_port) { 30080 } + let(:ssh_container_port) { 31022 } before do allow(Gitlab::Cng::Helpers::Utils).to receive(:tmp_dir).and_return("/tmp") @@ -37,6 +38,9 @@ "--wait", "30s", "--config", tmp_config_path ]).and_return(["", command_status]) + + allow(cluster).to receive(:rand).with(30000..31000).and_return(http_container_port) + allow(cluster).to receive(:rand).with(31001..32000).and_return(ssh_container_port) end context "with ci specific setup" do @@ -63,11 +67,11 @@ certSANs: - "#{docker_hostname}" extraPortMappings: - - containerPort: 32080 - hostPort: 32080 + - containerPort: #{http_container_port} + hostPort: 80 listenAddress: "0.0.0.0" - - containerPort: 32022 - hostPort: 32022 + - containerPort: #{ssh_container_port} + hostPort: 22 listenAddress: "0.0.0.0" YML end @@ -117,11 +121,11 @@ kubeletExtraArgs: node-labels: "ingress-ready=true" extraPortMappings: - - containerPort: 32080 - hostPort: 32080 + - containerPort: #{http_container_port} + hostPort: 80 listenAddress: "0.0.0.0" - - containerPort: 32022 - hostPort: 32022 + - containerPort: #{ssh_container_port} + hostPort: 22 listenAddress: "0.0.0.0" YML end @@ -152,7 +156,7 @@ end end - describe "with cleanup" do + describe "#destroy" do subject(:cluster) { described_class } before do @@ -166,7 +170,7 @@ end it "deletes cluster" do - expect { cluster.destroy("gitlab") }.to output(/Destroying cluster 'gitlab'/).to_stdout + expect { cluster.destroy }.to output(/Destroying cluster 'gitlab'/).to_stdout expect(cluster).to have_received(:execute_shell).with(%w[kind delete cluster --name gitlab]) end end @@ -177,11 +181,52 @@ end it "deletes cluster" do - expect { cluster.destroy("gitlab") }.to output( + expect { cluster.destroy }.to output( match(/Destroying cluster 'gitlab'/).and(match(/Cluster not found, skipping!/)) ).to_stdout expect(cluster).not_to have_received(:execute_shell).with(%w[kind delete cluster --name gitlab]) end end end + + describe "#host_port_mapping" do + let(:http_container_port) { 32080 } + let(:ssh_container_port) { 32022 } + + before do + allow(File).to receive(:read).with(File.join(Gitlab::Cng::Helpers::Utils.tmp_dir, "kind-config.yml")).and_return( + <<~YML + apiVersion: kind.x-k8s.io/v1alpha4 + kind: Cluster + networking: + apiServerAddress: "0.0.0.0" + nodes: + - role: control-plane + kubeadmConfigPatches: + - | + kind: InitConfiguration + nodeRegistration: + kubeletExtraArgs: + node-labels: "ingress-ready=true" + - | + kind: ClusterConfiguration + apiServer: + certSANs: + - "test" + extraPortMappings: + - containerPort: #{http_container_port} + hostPort: 80 + listenAddress: "0.0.0.0" + - containerPort: #{ssh_container_port} + hostPort: 22 + listenAddress: "0.0.0.0" + YML + ) + end + + it "return correct port mappings" do + expect(described_class.host_port_mapping(80)).to eq(http_container_port) + expect(described_class.host_port_mapping(22)).to eq(ssh_container_port) + end + end end