diff --git a/.gitlab/ci/test-on-cng/main.gitlab-ci.yml b/.gitlab/ci/test-on-cng/main.gitlab-ci.yml index 0c07556322c37afc80bed59915451d12de16484d..f038407116daef601503362b39b0d5d4e8730c37 100644 --- a/.gitlab/ci/test-on-cng/main.gitlab-ci.yml +++ b/.gitlab/ci/test-on-cng/main.gitlab-ci.yml @@ -51,7 +51,14 @@ workflow: - source scripts/utils.sh - source scripts/rspec_helpers.sh - cd qa && bundle install - - bundle exec cng create deployment --gitlab-domain "${GITLAB_DOMAIN}" --ci --with-cluster ${EXTRA_DEPLOY_VALUES} + - | + bundle exec cng create deployment kind \ + --gitlab-domain "${GITLAB_DOMAIN}" \ + --timeout 5m \ + --admin-password "${GITLAB_ADMIN_PASSWORD}" \ + --admin-token "${GITLAB_QA_ADMIN_ACCESS_TOKEN}" \ + --ci \ + ${EXTRA_DEPLOY_VALUES} script: - export QA_COMMAND="bundle exec bin/qa ${QA_SCENARIO:=Test::Instance::All} $QA_GITLAB_URL -- --force-color --order random --format documentation" - echo "Running - '$QA_COMMAND'" diff --git a/gems/gitlab-cng/lib/gitlab/cng/commands/create.rb b/gems/gitlab-cng/lib/gitlab/cng/commands/create.rb index 56ff6cc54fea10a0682620905c3d24b23d05f3b1..01375754069b93a5bcb137e6913ca44d5c49b701 100644 --- a/gems/gitlab-cng/lib/gitlab/cng/commands/create.rb +++ b/gems/gitlab-cng/lib/gitlab/cng/commands/create.rb @@ -1,14 +1,13 @@ # frozen_string_literal: true +require_relative "subcommands/deployment" + module Gitlab module Cng module Commands # Create command composed of subcommands that create various resources needed for CNG deployment # class Create < Command - # @return [Array] configurations that are used for kind cluster deployments - KIND_CLUSTER_CONFIGURATIONS = %w[kind].freeze - desc "cluster", "Create kind cluster for local deployments" option :name, desc: "Cluster name", @@ -24,52 +23,20 @@ class Create < Command 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 - desc "deployment [NAME]", "Create CNG deployment from official GitLab Helm chart" - long_desc <<~LONGDESC - This command installs a GitLab chart archive and performs all additional pre-install and post-install setup. - Argument NAME is helm install name and defaults to "gitlab". - Deployment has several optional environment variables it can read before performing chart install: - QA_EE_LICENSE|EE_LICENSE - gitlab test license, if present, will be added to deployment, - LONGDESC - option :configuration, - desc: "Deployment configuration", - default: "kind", - type: :string, - aliases: "-c", - enum: ["kind"] - option :namespace, - desc: "Deployment namespace", - default: "gitlab", - type: :string, - aliases: "-n" - option :set, - desc: "Optional helm chart values (can specify multiple or separate values with commas: key1=val1,key2=val2)", - type: :string, - repeatable: true - option :ci, - desc: "Use CI specific configuration", - default: false, - type: :boolean - option :gitlab_domain, - desc: "Domain for deployed app. Defaults to (your host IP).nip.io", - type: :string - option :with_cluster, - desc: "Create kind cluster for local deployments. \ - Only valid for configurations designed to run against local kind cluster", - type: :boolean - def deployment(name = "gitlab") - if options[:with_cluster] && KIND_CLUSTER_CONFIGURATIONS.include?(options[:configuration]) - invoke :cluster, [], ci: options[:ci] - end - - Deployment::Installation - .new(name, **symbolized_options.slice(:configuration, :namespace, :set, :ci, :gitlab_domain)) - .create - end + desc "deployment [TYPE]", "Create specific type of deployment" + subcommand "deployment", Subcommands::Deployment end end end diff --git a/gems/gitlab-cng/lib/gitlab/cng/commands/subcommands/deployment.rb b/gems/gitlab-cng/lib/gitlab/cng/commands/subcommands/deployment.rb new file mode 100644 index 0000000000000000000000000000000000000000..90f403fa5ac6fe5ed48d00dbfcb36403b680608a --- /dev/null +++ b/gems/gitlab-cng/lib/gitlab/cng/commands/subcommands/deployment.rb @@ -0,0 +1,122 @@ +# frozen_string_literal: true + +require "socket" + +module Gitlab + module Cng + module Commands + module Subcommands + # Different deployment type subcommands + # + # Each public method defines a specific deployment type. + # Each deployment method must call {Deployment::Installaton#create} where installation instance is initialized + # with appropriate configuration class which encapsulates optional deployment hooks and specific helm values + # + class Deployment < Command + class << self + # Add common deployment options for each deployment command defined as public method + # + # @param [String] name + # @return [void] + def method_added(name) + option :namespace, + desc: "Deployment namespace", + default: "gitlab", + type: :string, + aliases: "-n" + option :set, + desc: "Optional helm chart values " \ + "(can specify multiple or separate values with commas: key1=val1,key2=val2)", + type: :string, + repeatable: true + option :ci, + desc: "Use CI specific configuration", + default: false, + type: :boolean + option :timeout, + desc: "Timeout for deployment", + default: "10m", + type: :string + + super(name) + end + end + + desc "kind [NAME]", "Create CNG deployment against local kind k8s cluster where NAME is deployment name" + option :create_cluster, + desc: "Create kind cluster for local deployments before creating deployment", + type: :boolean, + default: true + option :docker_hostname, + desc: "Custom docker hostname if remote docker instance is used, like docker-in-docker, " \ + "only applicable when --create-cluster is true", + type: :string + option :gitlab_domain, + desc: "Domain for deployed app, default to (your host IP).nip.io", + type: :string + option :admin_password, + desc: "Admin password for gitlab, defaults to password commonly used across development environments", + type: :string, + default: "5iveL!fe" + option :admin_token, + desc: "Admin token for gitlab, defaults to value used in gitlab development seed data", + type: :string, + default: "ypCa3Dzb23o5nvsixwPA" + option :host_http_port, + desc: "Host HTTP port for gitlab", + type: :numeric, + default: 80 + option :host_ssh_port, + desc: "Host ssh port for gitlab", + type: :numeric, + default: 22 + def kind(name = "gitlab") + if options[:create_cluster] + invoke(Commands::Create, :cluster, [], **symbolized_options.slice( + :docker_hostname, :ci, :host_http_port, :host_ssh_port + )) + end + + configuration_args = symbolized_options.slice( + :namespace, + :ci, + :gitlab_domain, + :admin_password, + :admin_token, + :host_http_port, + :host_ssh_port + ) + + installation(name, Cng::Deployment::Configurations::Kind.new(**configuration_args)).create + end + + private + + # Installation instance + # + # @param [String] name + # @param [Deployment::Configurations::Base] configuration + # @return [Deployment::Installation] + def installation(name, configuration) + Cng::Deployment::Installation.new( + name, configuration: configuration, + **symbolized_options.slice(:namespace, :set, :ci, :gitlab_domain, :timeout) + ) + end + + # Populate options with default gitlab domain if missing + # + # @return [Hash] + def symbolized_options + @symbolized_options ||= super.tap do |opts| + next unless opts[:gitlab_domain].nil? + + # merge default option lazily to not fetch ip_address_list every time class is loaded + opts.merge!({ gitlab_domain: "#{Socket.ip_address_list.detect(&:ipv4_private?).ip_address}.nip.io" }) + end + end + end + end + end + end +end diff --git a/gems/gitlab-cng/lib/gitlab/cng/lib/deployment/configurations/_base.rb b/gems/gitlab-cng/lib/gitlab/cng/lib/deployment/configurations/_base.rb index dc89fb30f8263f1b55c63405d5f6f2b7eea73c23..458afa8faf9e6652c045acc3c355d0955578ffe3 100644 --- a/gems/gitlab-cng/lib/gitlab/cng/lib/deployment/configurations/_base.rb +++ b/gems/gitlab-cng/lib/gitlab/cng/lib/deployment/configurations/_base.rb @@ -20,9 +20,8 @@ module Configurations class Base include Helpers::Output - def initialize(namespace, kubeclient, ci, gitlab_domain) + def initialize(namespace:, ci:, gitlab_domain:) @namespace = namespace - @kubeclient = kubeclient @ci = ci @gitlab_domain = gitlab_domain end @@ -81,7 +80,14 @@ def gitlab_url private - attr_reader :namespace, :kubeclient, :ci, :gitlab_domain + attr_reader :namespace, :ci, :gitlab_domain + + # Instance of {Kubectl::Client} + # + # @return [Kubectl::Client] + def kubeclient + @kubeclient ||= Kubectl::Client.new(namespace) + end end end end diff --git a/gems/gitlab-cng/lib/gitlab/cng/lib/deployment/configurations/kind.rb b/gems/gitlab-cng/lib/gitlab/cng/lib/deployment/configurations/kind.rb index 2a00e06dcf7c4a2f9efc70a94038e1180c712e42..23f6625a73d68ac4044b1bdeecf2ad5f0365ddd0 100644 --- a/gems/gitlab-cng/lib/gitlab/cng/lib/deployment/configurations/kind.rb +++ b/gems/gitlab-cng/lib/gitlab/cng/lib/deployment/configurations/kind.rb @@ -21,6 +21,23 @@ class Kind < Base fi SH + def initialize( + namespace:, + ci:, + gitlab_domain:, + admin_password:, + admin_token:, + host_http_port:, + host_ssh_port: + ) + super(namespace: namespace, ci: ci, gitlab_domain: gitlab_domain) + + @admin_password = admin_password + @admin_token = admin_token + @host_http_port = host_http_port + @host_ssh_port = host_ssh_port + end + # Run pre-deployment setup # # @return [void] @@ -42,6 +59,12 @@ def run_post_deployment_setup def values { global: { + shell: { + port: host_ssh_port + }, + pages: { + port: host_http_port + }, initialRootPassword: { secret: ADMIN_PASSWORD_SECRET }, @@ -60,8 +83,8 @@ def values service: { type: "NodePort", nodePorts: { - "gitlab-shell": 32022, - http: 32080 + "gitlab-shell": Cng::Kind::Cluster::SSH_PORT, + http: Cng::Kind::Cluster::HTTP_PORT } } } @@ -73,24 +96,12 @@ def values # # @return [String] def gitlab_url - "http://gitlab.#{gitlab_domain}#{ci ? '' : ':32080'}" + @gitlab_url ||= URI("http://gitlab.#{gitlab_domain}:#{host_http_port}").to_s end private - # Gitlab initial admin password, defaults to commonly used password across development environments - # - # @return [String] - def admin_password - @admin_password ||= ENV["GITLAB_ADMIN_PASSWORD"] || "5iveL!fe" - end - - # Gitlab admin user personal access token, defaults to value used in development seed data - # - # @return [String] - def admin_token - @admin_token ||= ENV["GITLAB_ADMIN_ACCESS_TOKEN"] || "ypCa3Dzb23o5nvsixwPA" - end + attr_reader :admin_password, :admin_token, :host_http_port, :host_ssh_port # Token seed script for root user # diff --git a/gems/gitlab-cng/lib/gitlab/cng/lib/deployment/default_values.rb b/gems/gitlab-cng/lib/gitlab/cng/lib/deployment/default_values.rb index 508b8a5291f99ba3602a35b8dec717be6a367a4e..d5368757d4616ec7d210e654476f8ccfe2aad25e 100644 --- a/gems/gitlab-cng/lib/gitlab/cng/lib/deployment/default_values.rb +++ b/gems/gitlab-cng/lib/gitlab/cng/lib/deployment/default_values.rb @@ -47,22 +47,22 @@ def common_values(domain) # @return [Hash] def component_ci_versions { - "gitaly.image.repository" => "#{IMAGE_REPOSITORY}/gitaly", - "gitaly.image.tag" => gitaly_version, - "gitlab-shell.image.repository" => "#{IMAGE_REPOSITORY}/gitlab-shell", - "gitlab-shell.image.tag" => "v#{gitlab_shell_version}", - "migrations.image.repository" => "#{IMAGE_REPOSITORY}/gitlab-toolbox-ee", - "migrations.image.tag" => commit_sha, - "toolbox.image.repository" => "#{IMAGE_REPOSITORY}/gitlab-toolbox-ee", - "toolbox.image.tag" => commit_sha, - "sidekiq.annotations.commit" => commit_short_sha, - "sidekiq.image.repository" => "#{IMAGE_REPOSITORY}/gitlab-sidekiq-ee", - "sidekiq.image.tag" => commit_sha, - "webservice.annotations.commit" => commit_short_sha, - "webservice.image.repository" => "#{IMAGE_REPOSITORY}/gitlab-webservice-ee", - "webservice.image.tag" => commit_sha, - "webservice.workhorse.image" => "#{IMAGE_REPOSITORY}/gitlab-workhorse-ee", - "webservice.workhorse.tag" => commit_sha + "gitlab.gitaly.image.repository" => "#{IMAGE_REPOSITORY}/gitaly", + "gitlab.gitaly.image.tag" => gitaly_version, + "gitlab.gitlab-shell.image.repository" => "#{IMAGE_REPOSITORY}/gitlab-shell", + "gitlab.gitlab-shell.image.tag" => "v#{gitlab_shell_version}", + "gitlab.migrations.image.repository" => "#{IMAGE_REPOSITORY}/gitlab-toolbox-ee", + "gitlab.migrations.image.tag" => commit_sha, + "gitlab.toolbox.image.repository" => "#{IMAGE_REPOSITORY}/gitlab-toolbox-ee", + "gitlab.toolbox.image.tag" => commit_sha, + "gitlab.sidekiq.annotations.commit" => commit_short_sha, + "gitlab.sidekiq.image.repository" => "#{IMAGE_REPOSITORY}/gitlab-sidekiq-ee", + "gitlab.sidekiq.image.tag" => commit_sha, + "gitlab.webservice.annotations.commit" => commit_short_sha, + "gitlab.webservice.image.repository" => "#{IMAGE_REPOSITORY}/gitlab-webservice-ee", + "gitlab.webservice.image.tag" => commit_sha, + "gitlab.webservice.workhorse.image" => "#{IMAGE_REPOSITORY}/gitlab-workhorse-ee", + "gitlab.webservice.workhorse.tag" => commit_sha } end end diff --git a/gems/gitlab-cng/lib/gitlab/cng/lib/deployment/installation.rb b/gems/gitlab-cng/lib/gitlab/cng/lib/deployment/installation.rb index bfa0d5b139e56cec4fcaf3b39ed06dd047ecbfa4..6e65ccc59f3fa1aa32f54d6aadb0b9e32aaaddc7 100644 --- a/gems/gitlab-cng/lib/gitlab/cng/lib/deployment/installation.rb +++ b/gems/gitlab-cng/lib/gitlab/cng/lib/deployment/installation.rb @@ -1,6 +1,5 @@ # frozen_string_literal: true -require "socket" require "yaml" require "active_support/core_ext/hash" @@ -15,12 +14,13 @@ class Installation LICENSE_SECRET = "gitlab-license" - def initialize(name, configuration:, namespace:, ci:, gitlab_domain: nil, set: []) + def initialize(name, configuration:, namespace:, ci:, gitlab_domain:, timeout:, set: []) @name = name @configuration = configuration @namespace = namespace @ci = ci @gitlab_domain = gitlab_domain + @timeout = timeout @set = set end @@ -28,7 +28,7 @@ def initialize(name, configuration:, namespace:, ci:, gitlab_domain: nil, set: [ # # @return [void] def create - log("Creating CNG deployment '#{name}' using '#{configuration}' configuration", :info, bright: true) + log("Creating CNG deployment '#{name}'", :info, bright: true) run_pre_deploy_setup run_deploy run_post_deploy_setup @@ -36,9 +36,16 @@ def create exit(1) end + # Specific component version values used in CI + # + # @return [String] + def component_version_values + @component_version_values ||= DefaultValues.component_ci_versions.map { |k, v| "#{k}=#{v}" } + end + private - attr_reader :name, :configuration, :namespace, :ci, :set + attr_reader :name, :configuration, :namespace, :ci, :set, :gitlab_domain, :timeout alias_method :cli_values, :set # Kubectl client instance @@ -48,25 +55,6 @@ def kubeclient @kubeclient ||= Kubectl::Client.new(namespace) end - # Gitlab app domain - # - # @return [String] - def gitlab_domain - @gitlab_domain ||= "#{Socket.ip_address_list.detect(&:ipv4_private?).ip_address}.nip.io" - end - - # Configuration class instance - # - # @return [Configuration::Base] - def config_instance - @config_instance ||= Configurations.const_get(configuration.capitalize, false).new( - namespace, - kubeclient, - ci, - gitlab_domain - ) - end - # Gitlab license # # @return [String] @@ -105,7 +93,7 @@ def run_pre_deploy_setup create_namespace create_license - config_instance.run_pre_deployment_setup + configuration.run_pre_deployment_setup end end @@ -117,26 +105,26 @@ def run_deploy "upgrade", "--install", name, "gitlab/gitlab", "--namespace", namespace, - "--timeout", "5m", + "--timeout", timeout, "--wait" ] - cmd.push(*DefaultValues.component_ci_versions.flat_map { |k, v| ["--set", "gitlab.#{k}=#{v}"] }) if ci - cmd.push(*cli_values.flat_map { |v| ["--set", v] }) + cmd.push(*component_version_values.flat_map { |v| ["--set", v] }) if ci + cmd.push("--set", cli_values.join(",")) unless cli_values.empty? cmd.push("--values", "-") values = DefaultValues.common_values(gitlab_domain) .deep_merge(license_values) - .deep_merge(config_instance.values) + .deep_merge(configuration.values) .deep_stringify_keys Helpers::Spinner.spin("running helm deployment") { puts run_helm_cmd(cmd, values.to_yaml) } - log("Deployment successfull and app is available via: #{config_instance.gitlab_url}", :success, bright: true) + log("Deployment successful and app is available via: #{configuration.gitlab_url}", :success, bright: true) end # Execute post-deployment setup # # @return [void] def run_post_deploy_setup - Helpers::Spinner.spin("running post-deployment setup") { config_instance.run_post_deployment_setup } + Helpers::Spinner.spin("running post-deployment setup") { configuration.run_post_deployment_setup } end # Add helm chart repo diff --git a/gems/gitlab-cng/lib/gitlab/cng/lib/kind/cluster.rb b/gems/gitlab-cng/lib/gitlab/cng/lib/kind/cluster.rb index 41bc8bb1148a21d9f83b328c45de0549065dbf78..448fa4a11b57456ce3712f8836382f55148c874c 100644 --- a/gems/gitlab-cng/lib/gitlab/cng/lib/kind/cluster.rb +++ b/gems/gitlab-cng/lib/gitlab/cng/lib/kind/cluster.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true require "uri" - -require_relative "configs" +require "tmpdir" +require "erb" module Gitlab module Cng @@ -12,11 +12,15 @@ module Kind class Cluster include Helpers::Output include Helpers::Shell - include Configs - def initialize(ci:, name:, docker_hostname: nil) + HTTP_PORT = 32080 + SSH_PORT = 32022 + + def initialize(ci:, name:, host_http_port:, host_ssh_port:, docker_hostname: nil) @ci = ci @name = name + @host_http_port = host_http_port + @host_ssh_port = host_ssh_port @docker_hostname = ci ? docker_hostname || "docker" : docker_hostname end @@ -34,14 +38,7 @@ def create private - attr_reader :ci, :name, :docker_hostname - - # Check if cluster exists - # - # @return [Boolean] - def cluster_exists? - execute_shell(%w[kind get clusters]).include?(name) - end + attr_reader :ci, :name, :docker_hostname, :host_http_port, :host_ssh_port # Create kind cluster # @@ -54,7 +51,7 @@ def create_cluster "cluster", "--name", name, "--wait", "30s", - "--config", ci ? ci_config(docker_hostname) : default_config(docker_hostname) + "--config", ci ? ci_config : default_config ]) end end @@ -75,6 +72,91 @@ def update_server_url execute_shell(%W[kubectl config set-cluster #{cluster_name} --server=#{uri}]) end end + + # Check if cluster exists + # + # @return [Boolean] + def cluster_exists? + execute_shell(%w[kind get clusters]).include?(name) + end + + # Create temporary kind config file + # + # @param [String] config_yml + # @return [String] + def tmp_config_file(config_yml) + File.join(Dir.tmpdir, "kind-config.yml").tap do |path| + File.write(path, config_yml) + end + end + + # Temporary ci specific kind configuration file + # + # @return [String] file path + def ci_config + config_yml = <<~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: + - "#{docker_hostname}" + extraPortMappings: + - containerPort: #{HTTP_PORT} + hostPort: #{host_http_port} + listenAddress: "0.0.0.0" + - containerPort: #{SSH_PORT} + hostPort: #{host_ssh_port} + listenAddress: "0.0.0.0" + YML + + tmp_config_file(config_yml) + end + + # Temporary kind configuration file + # + # @return [String] file path + def default_config + template = ERB.new(<<~YML, trim_mode: "-") + kind: Cluster + apiVersion: kind.x-k8s.io/v1alpha4 + nodes: + - role: control-plane + kubeadmConfigPatches: + - | + kind: InitConfiguration + nodeRegistration: + kubeletExtraArgs: + node-labels: "ingress-ready=true" + <% if docker_hostname -%> + - | + kind: ClusterConfiguration + apiServer: + certSANs: + - "<%= docker_hostname %>" + <% end -%> + extraPortMappings: + - containerPort: #{HTTP_PORT} + hostPort: #{host_http_port} + listenAddress: "0.0.0.0" + - containerPort: #{SSH_PORT} + hostPort: #{host_ssh_port} + listenAddress: "0.0.0.0" + YML + + tmp_config_file(template.result(binding)) + end end end end diff --git a/gems/gitlab-cng/lib/gitlab/cng/lib/kind/configs.rb b/gems/gitlab-cng/lib/gitlab/cng/lib/kind/configs.rb deleted file mode 100644 index 7406b4bb6141dcb8d671fbbb8f003b69ad351e5b..0000000000000000000000000000000000000000 --- a/gems/gitlab-cng/lib/gitlab/cng/lib/kind/configs.rb +++ /dev/null @@ -1,102 +0,0 @@ -# frozen_string_literal: true - -require "tmpdir" -require "erb" - -module Gitlab - module Cng - module Kind - # Kind configuration file templates - # - module Configs - # Temporary ci specific kind configuration file - # - # @param [String] docker_hostname - # @return [String] file path - def ci_config(docker_hostname) - config_yml = <<~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: - - "#{docker_hostname}" - extraPortMappings: - # containerPort below must match the values file: - # nginx-ingress.controller.service.nodePorts.http - - containerPort: 32080 - hostPort: 80 - listenAddress: "0.0.0.0" - # containerPort below must match the values file: - # nginx-ingress.controller.service.nodePorts.gitlab-shell - - containerPort: 32022 - hostPort: 22 - listenAddress: "0.0.0.0" - YML - - tmp_config_file(config_yml) - end - - # Temporary kind configuration file - # - # @param [String, nil] docker_hostname - # @return [String] file path - def default_config(docker_hostname) - template = ERB.new(<<~YML, trim_mode: "-") - kind: Cluster - apiVersion: kind.x-k8s.io/v1alpha4 - nodes: - - role: control-plane - kubeadmConfigPatches: - - | - kind: InitConfiguration - nodeRegistration: - kubeletExtraArgs: - node-labels: "ingress-ready=true" - <% if docker_hostname -%> - - | - kind: ClusterConfiguration - apiServer: - certSANs: - - "<%= docker_hostname %>" - <% end -%> - extraPortMappings: - # containerPort below must match the values file: - # nginx-ingress.controller.service.nodePorts.http - - containerPort: 32080 - hostPort: 32080 - listenAddress: "0.0.0.0" - # containerPort below must match the values file: - # nginx-ingress.controller.service.nodePorts.ssh - - containerPort: 32022 - hostPort: 32022 - listenAddress: "0.0.0.0" - YML - - tmp_config_file(template.result(binding)) - end - - # Create temporary kind config file - # - # @param [String] config_yml - # @return [String] - def tmp_config_file(config_yml) - File.join(Dir.tmpdir, "kind-config.yml").tap do |path| - File.write(path, config_yml) - end - end - end - end - end -end diff --git a/gems/gitlab-cng/spec/integration/gitlab/cng/cli_spec.rb b/gems/gitlab-cng/spec/integration/gitlab/cng/cli_spec.rb index 8bab0b9186793d12a0efaf6e0d20abe4e75f9292..6ceb84c600914710f744a61e765d95ccae59641a 100644 --- a/gems/gitlab-cng/spec/integration/gitlab/cng/cli_spec.rb +++ b/gems/gitlab-cng/spec/integration/gitlab/cng/cli_spec.rb @@ -3,17 +3,17 @@ RSpec.describe Gitlab::Cng::CLI do shared_examples "command with help" do |args, help_output| it "shows help" do - expect { cli.invoke(*args) }.to output(/#{help_output}/).to_stdout + expect { cli.start(args) }.to output(/#{help_output}/).to_stdout end end - subject(:cli) { described_class.new } + subject(:cli) { described_class } describe "version command" do - it_behaves_like "command with help", [:help, ["version"]], /Print cng orchestrator version/ + it_behaves_like "command with help", %w[help version], /Print cng orchestrator version/ it "executes version command" do - expect { cli.invoke(:version) }.to output(/#{Gitlab::Cng::VERSION}/o).to_stdout + expect { cli.start(%w[version]) }.to output(/#{Gitlab::Cng::VERSION}/o).to_stdout end end @@ -25,10 +25,10 @@ allow(command_instance).to receive(:doctor) end - it_behaves_like "command with help", [:help, ["doctor"]], /Validate presence of all required system dependencies/ + it_behaves_like "command with help", %w[help doctor], /Validate presence of all required system dependencies/ it "invokes doctor command" do - cli.invoke(:doctor) + cli.start(%w[doctor]) expect(command_instance).to have_received(:doctor) end @@ -42,12 +42,13 @@ allow(Gitlab::Cng::Kind::Cluster).to receive(:new).and_return(cluster_instance) end - it_behaves_like "command with help", [:create, %w[help cluster]], /Create kind cluster for local deployments/ + it_behaves_like "command with help", %w[create help cluster], /Create kind cluster for local deployments/ it "invokes cluster create command" do - cli.invoke(:create, %w[cluster]) + cli.start(%w[create cluster]) - expect(Gitlab::Cng::Kind::Cluster).to have_received(:new).with(ci: false, name: "gitlab") + expect(Gitlab::Cng::Kind::Cluster).to have_received(:new) + .with(ci: false, name: "gitlab", host_http_port: 80, host_ssh_port: 22) expect(cluster_instance).to have_received(:create) end end @@ -55,23 +56,22 @@ context "with deployment subcommand" do let(:installation_instance) { instance_double(Gitlab::Cng::Deployment::Installation, create: nil) } - before do - allow(Gitlab::Cng::Deployment::Installation).to receive(:new).and_return(installation_instance) - end + context "with kind deployment" do + let(:configuration_instance) { instance_double(Gitlab::Cng::Deployment::Configurations::Kind) } - it_behaves_like "command with help", [:create, %w[help deployment]], - /This command installs a GitLab chart archive/ + before do + allow(Gitlab::Cng::Deployment::Installation).to receive(:new).and_return(installation_instance) + allow(Gitlab::Cng::Deployment::Configurations::Kind).to receive(:new) + end - it "invokes cluster create command" do - cli.invoke(:create, %w[deployment]) - - expect(Gitlab::Cng::Deployment::Installation).to have_received(:new).with( - "gitlab", - configuration: "kind", - namespace: "gitlab", - ci: false - ) - expect(installation_instance).to have_received(:create) + it_behaves_like "command with help", %w[create deployment help kind], + /Create CNG deployment against local kind k8s cluster/ + + it "invokes kind deployment" do + cli.start(%w[create deployment kind --gitlab-domain 127.0.0.1.nip.io --skip-create-cluster]) + + expect(installation_instance).to have_received(:create) + end end end end diff --git a/gems/gitlab-cng/spec/unit/gitlab/cng/commands/create_spec.rb b/gems/gitlab-cng/spec/unit/gitlab/cng/commands/create_spec.rb index e5e6a1267907c47d94474013c05d7f9ace2c77ae..b394e5147385c91620b5c8a2d3283be8ff5f6349 100644 --- a/gems/gitlab-cng/spec/unit/gitlab/cng/commands/create_spec.rb +++ b/gems/gitlab-cng/spec/unit/gitlab/cng/commands/create_spec.rb @@ -26,58 +26,10 @@ expect(kind_cluster).to have_received(:create) expect(Gitlab::Cng::Kind::Cluster).to have_received(:new).with({ ci: true, - name: "test-cluster" + name: "test-cluster", + host_http_port: 80, + host_ssh_port: 22 }) end end - - describe "deployment command" do - let(:command_name) { "deployment" } - let(:deployment_install) { instance_double(Gitlab::Cng::Deployment::Installation, create: nil) } - - before do - allow(Gitlab::Cng::Deployment::Installation).to receive(:new).and_return(deployment_install) - end - - it "defines deployment command" do - expect_command_to_include_attributes(command_name, { - description: "Create CNG deployment from official GitLab Helm chart", - name: command_name, - usage: "#{command_name} [NAME]" - }) - end - - it "invokes kind cluster creation with correct arguments" do - invoke_command(command_name, [], { - configuration: "kind", - ci: true, - namespace: "gitlab", - gitlab_domain: "127.0.0.1.nip.io" - }) - - expect(deployment_install).to have_received(:create) - expect(Gitlab::Cng::Deployment::Installation).to have_received(:new).with( - "gitlab", - configuration: "kind", - ci: true, - namespace: "gitlab", - gitlab_domain: "127.0.0.1.nip.io" - ) - end - - it "invokes kind cluster creation when --with-cluster argument is passed" do - invoke_command(command_name, [], { - configuration: "kind", - ci: true, - with_cluster: true - }) - - expect(kind_cluster).to have_received(:create) - expect(Gitlab::Cng::Kind::Cluster).to have_received(:new).with({ - ci: true, - name: "gitlab" - }) - expect(deployment_install).to have_received(:create) - end - end end diff --git a/gems/gitlab-cng/spec/unit/gitlab/cng/commands/subcommands/deployment_spec.rb b/gems/gitlab-cng/spec/unit/gitlab/cng/commands/subcommands/deployment_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..ee38785f279c83bdc3bf11b1b705113a81bdb781 --- /dev/null +++ b/gems/gitlab-cng/spec/unit/gitlab/cng/commands/subcommands/deployment_spec.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +RSpec.describe Gitlab::Cng::Commands::Subcommands::Deployment do + include_context "with command testing helper" + + describe "kind deployment command" do + let(:command_name) { "kind" } + + let(:installation_instance) { instance_double(Gitlab::Cng::Deployment::Installation, create: nil) } + let(:configuration_instance) { instance_double(Gitlab::Cng::Deployment::Configurations::Kind) } + let(:cluster_instance) { instance_double(Gitlab::Cng::Kind::Cluster, create: nil) } + let(:ip) { instance_double(Addrinfo, ipv4_private?: true, ip_address: "127.0.0.1") } + + before do + allow(Gitlab::Cng::Deployment::Installation).to receive(:new).and_return(installation_instance) + allow(Gitlab::Cng::Deployment::Configurations::Kind).to receive(:new).and_return(configuration_instance) + allow(Gitlab::Cng::Kind::Cluster).to receive(:new).and_return(cluster_instance) + allow(Socket).to receive(:ip_address_list).and_return([ip]) + end + + it "defines kind deployment" do + expect_command_to_include_attributes(command_name, { + description: "Create CNG deployment against local kind k8s cluster where NAME is deployment name", + name: command_name, + usage: "#{command_name} [NAME]" + }) + end + + it "invokes kind cluster creation with correct arguments" do + invoke_command(command_name, [], { + namespace: "gitlab", + ci: false + }) + + expect(Gitlab::Cng::Deployment::Installation).to have_received(:new).with( + "gitlab", + configuration: configuration_instance, + namespace: "gitlab", + ci: false, + gitlab_domain: "127.0.0.1.nip.io", + timeout: "10m" + ) + expect(Gitlab::Cng::Deployment::Configurations::Kind).to have_received(:new).with( + namespace: "gitlab", + ci: false, + gitlab_domain: "127.0.0.1.nip.io", + admin_password: "5iveL!fe", + admin_token: "ypCa3Dzb23o5nvsixwPA", + host_http_port: 80, + host_ssh_port: 22 + ) + expect(installation_instance).to have_received(:create) + end + + it "create 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(cluster_instance).to have_received(:create) + end + end +end diff --git a/gems/gitlab-cng/spec/unit/gitlab/cng/deployment/configurations/base_spec.rb b/gems/gitlab-cng/spec/unit/gitlab/cng/deployment/configurations/base_spec.rb index ae0271e6d0ec2bffc5c6c283c1098abdc639acad..30600bba8c9ac84b39e21cb0505eaff538a2a742 100644 --- a/gems/gitlab-cng/spec/unit/gitlab/cng/deployment/configurations/base_spec.rb +++ b/gems/gitlab-cng/spec/unit/gitlab/cng/deployment/configurations/base_spec.rb @@ -4,7 +4,11 @@ subject(:configuration) { Class.new(described_class) } let(:config) do - configuration.new("gitlab", Gitlab::Cng::Kubectl::Client.new("gitlab"), false, "domain") + configuration.new( + namespace: "gitlab", + ci: false, + gitlab_domain: "domain" + ) end it "returns empty values by default" do diff --git a/gems/gitlab-cng/spec/unit/gitlab/cng/deployment/configurations/kind_spec.rb b/gems/gitlab-cng/spec/unit/gitlab/cng/deployment/configurations/kind_spec.rb index d9c052488f0a9dd4d8dda80ce498947d3d8affbb..f200bb1f3c335c0dc5c24c6471ffdb7a0b6e777e 100644 --- a/gems/gitlab-cng/spec/unit/gitlab/cng/deployment/configurations/kind_spec.rb +++ b/gems/gitlab-cng/spec/unit/gitlab/cng/deployment/configurations/kind_spec.rb @@ -1,19 +1,22 @@ # frozen_string_literal: true RSpec.describe Gitlab::Cng::Deployment::Configurations::Kind do - subject(:configuration) { described_class.new("gitlab", kubeclient, true, "127.0.0.1.nip.io") } + subject(:configuration) do + described_class.new( + namespace: "gitlab", + ci: true, + gitlab_domain: "127.0.0.1.nip.io", + admin_password: "password", + admin_token: "token", + host_http_port: 80, + host_ssh_port: 22 + ) + end let(:kubeclient) { instance_double(Gitlab::Cng::Kubectl::Client, create_resource: "", execute: "") } - let(:env) do - { - "GITLAB_ADMIN_PASSWORD" => "password", - "GITLAB_ADMIN_ACCESS_TOKEN" => "token" - } - end - - around do |example| - ClimateControl.modify(env) { example.run } + before do + allow(Gitlab::Cng::Kubectl::Client).to receive(:new).and_return(kubeclient) end it "runs pre-deployment setup", :aggregate_failures do @@ -69,6 +72,12 @@ it "returns configuration specific values" do expect(configuration.values).to eq({ global: { + shell: { + port: 22 + }, + pages: { + port: 80 + }, initialRootPassword: { secret: "gitlab-initial-root-password" }, diff --git a/gems/gitlab-cng/spec/unit/gitlab/cng/deployment/installation_spec.rb b/gems/gitlab-cng/spec/unit/gitlab/cng/deployment/installation_spec.rb index 9974af29ae53e9f045366cedbd35ead7c021aee3..1f6e3ae21950dc53412c6366ac94672acbfc654f 100644 --- a/gems/gitlab-cng/spec/unit/gitlab/cng/deployment/installation_spec.rb +++ b/gems/gitlab-cng/spec/unit/gitlab/cng/deployment/installation_spec.rb @@ -4,16 +4,17 @@ subject(:installation) do described_class.new( "gitlab", - configuration: "kind", + configuration: configuration, namespace: "gitlab", - ci: ci + ci: ci, + gitlab_domain: gitlab_domain, + timeout: "10m" ) end - let(:stdin) { StringIO.new } let(:config_values) { { configuration_specific: true } } - let(:ip) { instance_double(Addrinfo, ipv4_private?: true, ip_address: "127.0.0.1") } + let(:gitlab_domain) { "#{ip.ip_address}.nip.io" } let(:kubeclient) do instance_double(Gitlab::Cng::Kubectl::Client, create_namespace: "", create_resource: "", execute: "") @@ -25,7 +26,7 @@ run_pre_deployment_setup: nil, run_post_deployment_setup: nil, values: config_values, - gitlab_url: "http://gitlab.#{ip.ip_address}.nip.io" + gitlab_url: "http://gitlab.#{gitlab_domain}" ) end @@ -42,7 +43,7 @@ { global: { hosts: { - domain: "#{ip.ip_address}.nip.io", + domain: gitlab_domain, https: false }, ingress: { @@ -77,7 +78,6 @@ allow(Gitlab::Cng::Deployment::Configurations::Kind).to receive(:new).and_return(configuration) allow(installation).to receive(:execute_shell) - allow(Socket).to receive(:ip_address_list).and_return([ip]) end around do |example| @@ -88,14 +88,8 @@ let(:ci) { false } it "runs setup and helm deployment" do - expect { installation.create }.to output(/Creating CNG deployment 'gitlab' using 'kind' configuration/).to_stdout + expect { installation.create }.to output(/Creating CNG deployment 'gitlab'/).to_stdout - expect(Gitlab::Cng::Deployment::Configurations::Kind).to have_received(:new).with( - "gitlab", - kubeclient, - ci, - "#{ip.ip_address}.nip.io" - ) expect(installation).to have_received(:execute_shell).with( %w[helm repo add gitlab https://charts.gitlab.io], stdin_data: nil @@ -113,7 +107,7 @@ helm upgrade --install gitlab gitlab/gitlab --namespace gitlab - --timeout 5m + --timeout 10m --wait --values - ], @@ -133,14 +127,14 @@ let(:ci) { true } it "runs helm install with correctly merged values and component versions" do - expect { installation.create }.to output(/Creating CNG deployment 'gitlab' using 'kind' configuration/).to_stdout + expect { installation.create }.to output(/Creating CNG deployment 'gitlab'/).to_stdout expect(installation).to have_received(:execute_shell).with( %W[ helm upgrade --install gitlab gitlab/gitlab --namespace gitlab - --timeout 5m + --timeout 10m --wait --set gitlab.gitaly.image.repository=registry.gitlab.com/gitlab-org/build/cng-mirror/gitaly --set gitlab.gitaly.image.tag=7aa06a578d76bdc294ee8e9acb4f063e7d9f1d5f diff --git a/gems/gitlab-cng/spec/unit/gitlab/cng/kind/cluster_spec.rb b/gems/gitlab-cng/spec/unit/gitlab/cng/kind/cluster_spec.rb index 957c362e5ee1597e303b6684d00179e0d508ceb4..2e102d0f8926ca01a09e5f1d12dfb06bffc562d4 100644 --- a/gems/gitlab-cng/spec/unit/gitlab/cng/kind/cluster_spec.rb +++ b/gems/gitlab-cng/spec/unit/gitlab/cng/kind/cluster_spec.rb @@ -1,7 +1,15 @@ # frozen_string_literal: true RSpec.describe Gitlab::Cng::Kind::Cluster do - subject(:cluster) { described_class.new(ci: ci, name: name, docker_hostname: docker_hostname) } + subject(:cluster) do + described_class.new( + ci: ci, + name: name, + docker_hostname: docker_hostname, + host_http_port: 32080, + host_ssh_port: 32022 + ) + end let(:ci) { false } let(:name) { "gitlab" } @@ -51,15 +59,11 @@ certSANs: - "#{docker_hostname}" extraPortMappings: - # containerPort below must match the values file: - # nginx-ingress.controller.service.nodePorts.http - containerPort: 32080 - hostPort: 80 + hostPort: 32080 listenAddress: "0.0.0.0" - # containerPort below must match the values file: - # nginx-ingress.controller.service.nodePorts.gitlab-shell - containerPort: 32022 - hostPort: 22 + hostPort: 32022 listenAddress: "0.0.0.0" YML end @@ -97,16 +101,12 @@ kubeletExtraArgs: node-labels: "ingress-ready=true" extraPortMappings: - # containerPort below must match the values file: - # nginx-ingress.controller.service.nodePorts.http - - containerPort: 32080 - hostPort: 32080 - listenAddress: "0.0.0.0" - # containerPort below must match the values file: - # nginx-ingress.controller.service.nodePorts.ssh - - containerPort: 32022 - hostPort: 32022 - listenAddress: "0.0.0.0" + - containerPort: 32080 + hostPort: 32080 + listenAddress: "0.0.0.0" + - containerPort: 32022 + hostPort: 32022 + listenAddress: "0.0.0.0" YML end diff --git a/scripts/qa/cng_deploy/cng-kind.sh b/scripts/qa/cng_deploy/cng-kind.sh index 9271cd13923dce5fdadf81a6bf55b0e92c1b8559..5de49b2e6de2a5e0a65ea6af2a137eb2c5ca5b5c 100644 --- a/scripts/qa/cng_deploy/cng-kind.sh +++ b/scripts/qa/cng_deploy/cng-kind.sh @@ -29,15 +29,15 @@ function save_install_logs() { log_with_header "Events of namespace ${NAMESPACE}" kubectl get events --output wide --namespace ${NAMESPACE} - for pod in $(kubectl get pods --no-headers --namespace ${NAMESPACE} --output jsonpath={.items[*].metadata.name}); do + for pod in $(kubectl get pods --no-headers --namespace ${NAMESPACE} --output 'jsonpath={.items[*].metadata.name}'); do log_with_header "Description of pod ${pod}" kubectl describe pod ${pod} --namespace ${NAMESPACE} - for container in $(kubectl get pods ${pod} --no-headers --namespace ${NAMESPACE} --output jsonpath={.spec.initContainers[*].name}); do + for container in $(kubectl get pods ${pod} --no-headers --namespace ${NAMESPACE} --output 'jsonpath={.spec.initContainers[*].name}'); do kubectl logs ${pod} --namespace ${NAMESPACE} --container ${container} >"${container}.log" done - for container in $(kubectl get pods ${pod} --no-headers --namespace ${NAMESPACE} --output jsonpath={.spec.containers[*].name}); do + for container in $(kubectl get pods ${pod} --no-headers --namespace ${NAMESPACE} --output 'jsonpath={.spec.containers[*].name}'); do kubectl logs ${pod} --namespace ${NAMESPACE} --container ${container} >"${container}.log" done done