diff options
author | Grzegorz Bizon <grzegorz@gitlab.com> | 2018-06-06 09:53:37 +0000 |
---|---|---|
committer | Grzegorz Bizon <grzegorz@gitlab.com> | 2018-06-06 09:53:37 +0000 |
commit | 25bf4a2b195bbad0caaff409266a8de32c3a5223 (patch) | |
tree | c559864ec6366791ac431f67eba60e5a82bb565e | |
parent | bb6b73cf3c64a6f963e6afc657cab937db46564b (diff) | |
parent | a08cce86dde6155b4de4c894ff43e1e4cfcbdb8e (diff) | |
download | gitlab-ce-25bf4a2b195bbad0caaff409266a8de32c3a5223.tar.gz |
Merge branch 'qa-112-tests-for-auto-devops' into 'master'
QA Tests for Auto DevOps
See merge request gitlab-org/gitlab-ce!19200
-rw-r--r-- | qa/Dockerfile | 9 | ||||
-rw-r--r-- | qa/qa.rb | 12 | ||||
-rw-r--r-- | qa/qa/factory/repository/push.rb | 17 | ||||
-rw-r--r-- | qa/qa/factory/resource/kubernetes_cluster.rb | 55 | ||||
-rw-r--r-- | qa/qa/fixtures/auto_devops_rack/Gemfile | 3 | ||||
-rw-r--r-- | qa/qa/fixtures/auto_devops_rack/Gemfile.lock | 15 | ||||
-rw-r--r-- | qa/qa/fixtures/auto_devops_rack/Rakefile | 7 | ||||
-rw-r--r-- | qa/qa/fixtures/auto_devops_rack/config.ru | 1 | ||||
-rw-r--r-- | qa/qa/page/menu/side.rb | 18 | ||||
-rw-r--r-- | qa/qa/page/project/operations/kubernetes/add.rb | 19 | ||||
-rw-r--r-- | qa/qa/page/project/operations/kubernetes/add_existing.rb | 39 | ||||
-rw-r--r-- | qa/qa/page/project/operations/kubernetes/index.rb | 19 | ||||
-rw-r--r-- | qa/qa/page/project/operations/kubernetes/show.rb | 39 | ||||
-rw-r--r-- | qa/qa/page/project/pipeline/show.rb | 4 | ||||
-rw-r--r-- | qa/qa/page/project/settings/ci_cd.rb | 15 | ||||
-rw-r--r-- | qa/qa/scenario/test/integration/kubernetes.rb | 11 | ||||
-rw-r--r-- | qa/qa/service/kubernetes_cluster.rb | 66 | ||||
-rw-r--r-- | qa/qa/service/runner.rb | 1 | ||||
-rw-r--r-- | qa/qa/specs/features/project/auto_devops_spec.rb | 55 |
19 files changed, 400 insertions, 5 deletions
diff --git a/qa/Dockerfile b/qa/Dockerfile index 77cee9c5461..abf2184e1e2 100644 --- a/qa/Dockerfile +++ b/qa/Dockerfile @@ -28,6 +28,15 @@ RUN apt-get update -q && apt-get install -y google-chrome-stable && apt-get clea RUN wget -q https://chromedriver.storage.googleapis.com/$(wget -q -O - https://chromedriver.storage.googleapis.com/LATEST_RELEASE)/chromedriver_linux64.zip RUN unzip chromedriver_linux64.zip -d /usr/local/bin +## +# Install gcloud and kubectl CLI used in Auto DevOps test to create K8s +# clusters +# +RUN export CLOUD_SDK_REPO="cloud-sdk-$(lsb_release -c -s)" && \ + echo "deb http://packages.cloud.google.com/apt $CLOUD_SDK_REPO main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list && \ + curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - && \ + apt-get update -y && apt-get install google-cloud-sdk kubectl -y + WORKDIR /home/qa COPY ./Gemfile* ./ RUN bundle install @@ -41,6 +41,7 @@ module QA autoload :SecretVariable, 'qa/factory/resource/secret_variable' autoload :Runner, 'qa/factory/resource/runner' autoload :PersonalAccessToken, 'qa/factory/resource/personal_access_token' + autoload :KubernetesCluster, 'qa/factory/resource/kubernetes_cluster' end module Repository @@ -72,6 +73,7 @@ module QA module Integration autoload :LDAP, 'qa/scenario/test/integration/ldap' + autoload :Kubernetes, 'qa/scenario/test/integration/kubernetes' autoload :Mattermost, 'qa/scenario/test/integration/mattermost' end @@ -150,6 +152,15 @@ module QA autoload :Show, 'qa/page/project/issue/show' autoload :Index, 'qa/page/project/issue/index' end + + module Operations + module Kubernetes + autoload :Index, 'qa/page/project/operations/kubernetes/index' + autoload :Add, 'qa/page/project/operations/kubernetes/add' + autoload :AddExisting, 'qa/page/project/operations/kubernetes/add_existing' + autoload :Show, 'qa/page/project/operations/kubernetes/show' + end + end end module Profile @@ -195,6 +206,7 @@ module QA # module Service autoload :Shellout, 'qa/service/shellout' + autoload :KubernetesCluster, 'qa/service/kubernetes_cluster' autoload :Omnibus, 'qa/service/omnibus' autoload :Runner, 'qa/service/runner' end diff --git a/qa/qa/factory/repository/push.rb b/qa/qa/factory/repository/push.rb index 795f1f9cb1a..28711c12701 100644 --- a/qa/qa/factory/repository/push.rb +++ b/qa/qa/factory/repository/push.rb @@ -15,7 +15,7 @@ module QA def initialize @file_name = 'file.txt' @file_content = '# This is test project' - @commit_message = "Add #{@file_name}" + @commit_message = "This is a test commit" @branch_name = 'master' @new_branch = true end @@ -24,6 +24,12 @@ module QA @remote_branch ||= branch_name end + def directory=(dir) + raise "Must set directory as a Pathname" unless dir.is_a?(Pathname) + + @directory = dir + end + def fabricate! project.visit! @@ -43,7 +49,14 @@ module QA repository.checkout(branch_name) end - repository.add_file(file_name, file_content) + if @directory + @directory.each_child do |f| + repository.add_file(f.basename, f.read) if f.file? + end + else + repository.add_file(file_name, file_content) + end + repository.commit(commit_message) repository.push_changes("#{branch_name}:#{remote_branch}") end diff --git a/qa/qa/factory/resource/kubernetes_cluster.rb b/qa/qa/factory/resource/kubernetes_cluster.rb new file mode 100644 index 00000000000..f32cf985e9d --- /dev/null +++ b/qa/qa/factory/resource/kubernetes_cluster.rb @@ -0,0 +1,55 @@ +require 'securerandom' + +module QA + module Factory + module Resource + class KubernetesCluster < Factory::Base + attr_writer :project, :cluster, + :install_helm_tiller, :install_ingress, :install_prometheus, :install_runner + + product :ingress_ip do + Page::Project::Operations::Kubernetes::Show.perform do |page| + page.ingress_ip + end + end + + def fabricate! + @project.visit! + + Page::Menu::Side.act { click_operations_kubernetes } + + Page::Project::Operations::Kubernetes::Index.perform do |page| + page.add_kubernetes_cluster + end + + Page::Project::Operations::Kubernetes::Add.perform do |page| + page.add_existing_cluster + end + + Page::Project::Operations::Kubernetes::AddExisting.perform do |page| + page.set_cluster_name(@cluster.cluster_name) + page.set_api_url(@cluster.api_url) + page.set_ca_certificate(@cluster.ca_certificate) + page.set_token(@cluster.token) + page.add_cluster! + end + + if @install_helm_tiller + Page::Project::Operations::Kubernetes::Show.perform do |page| + # Helm must be installed before everything else + page.install!(:helm) + page.await_installed(:helm) + + page.install!(:ingress) if @install_ingress + page.await_installed(:ingress) if @install_ingress + page.install!(:prometheus) if @install_prometheus + page.await_installed(:prometheus) if @install_prometheus + page.install!(:runner) if @install_runner + page.await_installed(:runner) if @install_runner + end + end + end + end + end + end +end diff --git a/qa/qa/fixtures/auto_devops_rack/Gemfile b/qa/qa/fixtures/auto_devops_rack/Gemfile new file mode 100644 index 00000000000..fc7514242d0 --- /dev/null +++ b/qa/qa/fixtures/auto_devops_rack/Gemfile @@ -0,0 +1,3 @@ +source 'https://rubygems.org' +gem 'rack' +gem 'rake' diff --git a/qa/qa/fixtures/auto_devops_rack/Gemfile.lock b/qa/qa/fixtures/auto_devops_rack/Gemfile.lock new file mode 100644 index 00000000000..09cf72c48ac --- /dev/null +++ b/qa/qa/fixtures/auto_devops_rack/Gemfile.lock @@ -0,0 +1,15 @@ +GEM + remote: https://rubygems.org/ + specs: + rack (2.0.4) + rake (12.3.0) + +PLATFORMS + ruby + +DEPENDENCIES + rack + rake + +BUNDLED WITH + 1.16.1 diff --git a/qa/qa/fixtures/auto_devops_rack/Rakefile b/qa/qa/fixtures/auto_devops_rack/Rakefile new file mode 100644 index 00000000000..c865c9aaac1 --- /dev/null +++ b/qa/qa/fixtures/auto_devops_rack/Rakefile @@ -0,0 +1,7 @@ +require 'rake/testtask' + +task default: %w[test] + +task :test do + puts "ok" +end diff --git a/qa/qa/fixtures/auto_devops_rack/config.ru b/qa/qa/fixtures/auto_devops_rack/config.ru new file mode 100644 index 00000000000..bde8e15488a --- /dev/null +++ b/qa/qa/fixtures/auto_devops_rack/config.ru @@ -0,0 +1 @@ +run lambda { |env| [200, { 'Content-Type' => 'text/plain' }, StringIO.new("Hello World!\n")] } diff --git a/qa/qa/page/menu/side.rb b/qa/qa/page/menu/side.rb index 7e028add2ef..3630b7e8568 100644 --- a/qa/qa/page/menu/side.rb +++ b/qa/qa/page/menu/side.rb @@ -7,9 +7,11 @@ module QA element :settings_link, 'link_to edit_project_path' element :repository_link, "title: 'Repository'" element :pipelines_settings_link, "title: 'CI / CD'" + element :operations_kubernetes_link, "title: _('Kubernetes')" element :issues_link, /link_to.*shortcuts-issues/ element :issues_link_text, "Issues" element :top_level_items, '.sidebar-top-level-items' + element :operations_section, "class: 'shortcuts-operations'" element :activity_link, "title: 'Activity'" end @@ -33,6 +35,14 @@ module QA end end + def click_operations_kubernetes + hover_operations do + within_submenu do + click_link('Kubernetes') + end + end + end + def click_ci_cd_pipelines within_sidebar do click_link('CI / CD') @@ -61,6 +71,14 @@ module QA end end + def hover_operations + within_sidebar do + find('.shortcuts-operations').hover + + yield + end + end + def within_sidebar page.within('.sidebar-top-level-items') do yield diff --git a/qa/qa/page/project/operations/kubernetes/add.rb b/qa/qa/page/project/operations/kubernetes/add.rb new file mode 100644 index 00000000000..9b3c482fa6c --- /dev/null +++ b/qa/qa/page/project/operations/kubernetes/add.rb @@ -0,0 +1,19 @@ +module QA + module Page + module Project + module Operations + module Kubernetes + class Add < Page::Base + view 'app/views/projects/clusters/new.html.haml' do + element :add_kubernetes_cluster_button, "link_to s_('ClusterIntegration|Add an existing Kubernetes cluster')" + end + + def add_existing_cluster + click_on 'Add an existing Kubernetes cluster' + end + end + end + end + end + end +end diff --git a/qa/qa/page/project/operations/kubernetes/add_existing.rb b/qa/qa/page/project/operations/kubernetes/add_existing.rb new file mode 100644 index 00000000000..eef82b5f329 --- /dev/null +++ b/qa/qa/page/project/operations/kubernetes/add_existing.rb @@ -0,0 +1,39 @@ +module QA + module Page + module Project + module Operations + module Kubernetes + class AddExisting < Page::Base + view 'app/views/projects/clusters/user/_form.html.haml' do + element :cluster_name, 'text_field :name' + element :api_url, 'text_field :api_url' + element :ca_certificate, 'text_area :ca_cert' + element :token, 'text_field :token' + element :add_cluster_button, "submit s_('ClusterIntegration|Add Kubernetes cluster')" + end + + def set_cluster_name(name) + fill_in 'cluster_name', with: name + end + + def set_api_url(api_url) + fill_in 'cluster_platform_kubernetes_attributes_api_url', with: api_url + end + + def set_ca_certificate(ca_certificate) + fill_in 'cluster_platform_kubernetes_attributes_ca_cert', with: ca_certificate + end + + def set_token(token) + fill_in 'cluster_platform_kubernetes_attributes_token', with: token + end + + def add_cluster! + click_on 'Add Kubernetes cluster' + end + end + end + end + end + end +end diff --git a/qa/qa/page/project/operations/kubernetes/index.rb b/qa/qa/page/project/operations/kubernetes/index.rb new file mode 100644 index 00000000000..7261b5645da --- /dev/null +++ b/qa/qa/page/project/operations/kubernetes/index.rb @@ -0,0 +1,19 @@ +module QA + module Page + module Project + module Operations + module Kubernetes + class Index < Page::Base + view 'app/views/projects/clusters/_empty_state.html.haml' do + element :add_kubernetes_cluster_button, "link_to s_('ClusterIntegration|Add Kubernetes cluster')" + end + + def add_kubernetes_cluster + click_on 'Add Kubernetes cluster' + end + end + end + end + end + end +end diff --git a/qa/qa/page/project/operations/kubernetes/show.rb b/qa/qa/page/project/operations/kubernetes/show.rb new file mode 100644 index 00000000000..4923304133e --- /dev/null +++ b/qa/qa/page/project/operations/kubernetes/show.rb @@ -0,0 +1,39 @@ +module QA + module Page + module Project + module Operations + module Kubernetes + class Show < Page::Base + view 'app/assets/javascripts/clusters/components/application_row.vue' do + element :application_row, 'js-cluster-application-row-${this.id}' + element :install_button, "s__('ClusterIntegration|Install')" + element :installed_button, "s__('ClusterIntegration|Installed')" + end + + view 'app/assets/javascripts/clusters/components/applications.vue' do + element :ingress_ip_address, 'id="ingress-ip-address"' + end + + def install!(application_name) + within(".js-cluster-application-row-#{application_name}") do + click_on 'Install' + end + end + + def await_installed(application_name) + within(".js-cluster-application-row-#{application_name}") do + page.has_text?('Installed', wait: 300) + end + end + + def ingress_ip + # We need to wait longer since it can take some time before the + # ip address is assigned for the ingress controller + page.find('#ingress-ip-address', wait: 500).value + end + end + end + end + end + end +end diff --git a/qa/qa/page/project/pipeline/show.rb b/qa/qa/page/project/pipeline/show.rb index ec61c47b3bb..de849b3eee8 100644 --- a/qa/qa/page/project/pipeline/show.rb +++ b/qa/qa/page/project/pipeline/show.rb @@ -24,10 +24,10 @@ module QA::Page end end - def has_build?(name, status: :success) + def has_build?(name, status: :success, wait:) within('.pipeline-graph') do within('.ci-job-component', text: name) do - has_selector?(".ci-status-icon-#{status}") + has_selector?(".ci-status-icon-#{status}", wait: wait) end end end diff --git a/qa/qa/page/project/settings/ci_cd.rb b/qa/qa/page/project/settings/ci_cd.rb index 145c3d3ddfa..dfb71e0a9f0 100644 --- a/qa/qa/page/project/settings/ci_cd.rb +++ b/qa/qa/page/project/settings/ci_cd.rb @@ -8,6 +8,13 @@ module QA # rubocop:disable Naming/FileName view 'app/views/projects/settings/ci_cd/show.html.haml' do element :runners_settings, 'Runners settings' element :secret_variables, 'Variables' + element :auto_devops_section, 'Auto DevOps' + end + + view 'app/views/projects/settings/ci_cd/_autodevops_form.html.haml' do + element :enable_auto_devops_button, 'Enable Auto DevOps' + element :domain_input, 'Domain' + element :save_changes_button, "submit 'Save changes'" end def expand_runners_settings(&block) @@ -21,6 +28,14 @@ module QA # rubocop:disable Naming/FileName Settings::SecretVariables.perform(&block) end end + + def enable_auto_devops_with_domain(domain) + expand_section('Auto DevOps') do + choose 'Enable Auto DevOps' + fill_in 'Domain', with: domain + click_on 'Save changes' + end + end end end end diff --git a/qa/qa/scenario/test/integration/kubernetes.rb b/qa/qa/scenario/test/integration/kubernetes.rb new file mode 100644 index 00000000000..7479073e979 --- /dev/null +++ b/qa/qa/scenario/test/integration/kubernetes.rb @@ -0,0 +1,11 @@ +module QA + module Scenario + module Test + module Integration + class Kubernetes < Test::Instance + tags :kubernetes + end + end + end + end +end diff --git a/qa/qa/service/kubernetes_cluster.rb b/qa/qa/service/kubernetes_cluster.rb new file mode 100644 index 00000000000..604bc522983 --- /dev/null +++ b/qa/qa/service/kubernetes_cluster.rb @@ -0,0 +1,66 @@ +require 'securerandom' +require 'mkmf' + +module QA + module Service + class KubernetesCluster + include Service::Shellout + + attr_reader :api_url, :ca_certificate, :token + + def cluster_name + @cluster_name ||= "qa-cluster-#{SecureRandom.hex(4)}-#{Time.now.utc.strftime("%Y%m%d%H%M%S")}" + end + + def create! + validate_dependencies + login_if_not_already_logged_in + + shell <<~CMD.tr("\n", ' ') + gcloud container clusters + create #{cluster_name} + --enable-legacy-authorization + --zone us-central1-a + && gcloud container clusters + get-credentials #{cluster_name} + CMD + + @api_url = `kubectl config view --minify -o jsonpath='{.clusters[].cluster.server}'` + @ca_certificate = Base64.decode64(`kubectl get secrets -o jsonpath="{.items[0].data['ca\\.crt']}"`) + @token = Base64.decode64(`kubectl get secrets -o jsonpath='{.items[0].data.token}'`) + self + end + + def remove! + shell("gcloud container clusters delete #{cluster_name} --quiet --async") + end + + private + + def validate_dependencies + find_executable('gcloud') || raise("You must first install `gcloud` executable to run these tests.") + find_executable('kubectl') || raise("You must first install `kubectl` executable to run these tests.") + end + + def login_if_not_already_logged_in + account = `gcloud auth list --filter=status:ACTIVE --format="value(account)"` + if account.empty? + attempt_login_with_env_vars + else + puts "gcloud account found. Using: #{account} for creating K8s cluster." + end + end + + def attempt_login_with_env_vars + puts "No gcloud account. Attempting to login from env vars GCLOUD_ACCOUNT_EMAIL and GCLOUD_ACCOUNT_KEY." + gcloud_account_key = Tempfile.new('gcloud-account-key') + gcloud_account_key.write(ENV.fetch("GCLOUD_ACCOUNT_KEY")) + gcloud_account_key.close + gcloud_account_email = ENV.fetch("GCLOUD_ACCOUNT_EMAIL") + shell("gcloud auth activate-service-account #{gcloud_account_email} --key-file #{gcloud_account_key.path}") + ensure + gcloud_account_key && gcloud_account_key.unlink + end + end + end +end diff --git a/qa/qa/service/runner.rb b/qa/qa/service/runner.rb index c0352e0467a..9417c707105 100644 --- a/qa/qa/service/runner.rb +++ b/qa/qa/service/runner.rb @@ -3,7 +3,6 @@ require 'securerandom' module QA module Service class Runner - include Scenario::Actable include Service::Shellout attr_accessor :token, :address, :tags, :image diff --git a/qa/qa/specs/features/project/auto_devops_spec.rb b/qa/qa/specs/features/project/auto_devops_spec.rb new file mode 100644 index 00000000000..f3f59d33457 --- /dev/null +++ b/qa/qa/specs/features/project/auto_devops_spec.rb @@ -0,0 +1,55 @@ +module QA + feature 'Auto Devops', :kubernetes do + after do + @cluster&.remove! + end + + scenario 'user creates a new project and runs auto devops' do + Runtime::Browser.visit(:gitlab, Page::Main::Login) + Page::Main::Login.act { sign_in_using_credentials } + + project = Factory::Resource::Project.fabricate! do |p| + p.name = 'project-with-autodevops' + p.description = 'Project with Auto Devops' + end + + # Create Auto Devops compatible repo + Factory::Repository::Push.fabricate! do |push| + push.project = project + push.directory = Pathname + .new(__dir__) + .join('../../../fixtures/auto_devops_rack') + push.commit_message = 'Create Auto DevOps compatible rack application' + end + + Page::Project::Show.act { wait_for_push } + + # Create and connect K8s cluster + @cluster = Service::KubernetesCluster.new.create! + kubernetes_cluster = Factory::Resource::KubernetesCluster.fabricate! do |cluster| + cluster.project = project + cluster.cluster = @cluster + cluster.install_helm_tiller = true + cluster.install_ingress = true + cluster.install_prometheus = true + cluster.install_runner = true + end + + project.visit! + Page::Menu::Side.act { click_ci_cd_settings } + Page::Project::Settings::CICD.perform do |p| + p.enable_auto_devops_with_domain("#{kubernetes_cluster.ingress_ip}.nip.io") + end + + project.visit! + Page::Menu::Side.act { click_ci_cd_pipelines } + Page::Project::Pipeline::Index.act { go_to_latest_pipeline } + + Page::Project::Pipeline::Show.perform do |pipeline| + expect(pipeline).to have_build('build', status: :success, wait: 600) + expect(pipeline).to have_build('test', status: :success, wait: 600) + expect(pipeline).to have_build('production', status: :success, wait: 600) + end + end + end +end |