Commit 2ae48f1c authored by Dylan Griffith's avatar Dylan Griffith

Add QA integration test for full Auto DevOps flow

parent 8d2b4e02
...@@ -41,6 +41,7 @@ module QA ...@@ -41,6 +41,7 @@ module QA
autoload :SecretVariable, 'qa/factory/resource/secret_variable' autoload :SecretVariable, 'qa/factory/resource/secret_variable'
autoload :Runner, 'qa/factory/resource/runner' autoload :Runner, 'qa/factory/resource/runner'
autoload :PersonalAccessToken, 'qa/factory/resource/personal_access_token' autoload :PersonalAccessToken, 'qa/factory/resource/personal_access_token'
autoload :KubernetesCluster, 'qa/factory/resource/kubernetes_cluster'
end end
module Repository module Repository
...@@ -72,6 +73,7 @@ module QA ...@@ -72,6 +73,7 @@ module QA
module Integration module Integration
autoload :LDAP, 'qa/scenario/test/integration/ldap' autoload :LDAP, 'qa/scenario/test/integration/ldap'
autoload :Kubernetes, 'qa/scenario/test/integration/kubernetes'
autoload :Mattermost, 'qa/scenario/test/integration/mattermost' autoload :Mattermost, 'qa/scenario/test/integration/mattermost'
end end
...@@ -150,6 +152,15 @@ module QA ...@@ -150,6 +152,15 @@ module QA
autoload :Show, 'qa/page/project/issue/show' autoload :Show, 'qa/page/project/issue/show'
autoload :Index, 'qa/page/project/issue/index' autoload :Index, 'qa/page/project/issue/index'
end 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 end
module Profile module Profile
...@@ -195,6 +206,7 @@ module QA ...@@ -195,6 +206,7 @@ module QA
# #
module Service module Service
autoload :Shellout, 'qa/service/shellout' autoload :Shellout, 'qa/service/shellout'
autoload :KubernetesCluster, 'qa/service/kubernetes_cluster'
autoload :Omnibus, 'qa/service/omnibus' autoload :Omnibus, 'qa/service/omnibus'
autoload :Runner, 'qa/service/runner' autoload :Runner, 'qa/service/runner'
end end
......
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
source 'https://rubygems.org'
gem 'rack'
gem 'rake'
GEM
remote: https://rubygems.org/
specs:
rack (2.0.4)
rake (12.3.0)
PLATFORMS
ruby
DEPENDENCIES
rack
rake
BUNDLED WITH
1.16.1
require 'rake/testtask'
task default: %w[test]
task :test do
puts "ok"
end
run lambda { |env| [200, { 'Content-Type' => 'text/plain' }, StringIO.new("Hello World!\n")] }
...@@ -7,9 +7,11 @@ module QA ...@@ -7,9 +7,11 @@ module QA
element :settings_link, 'link_to edit_project_path' element :settings_link, 'link_to edit_project_path'
element :repository_link, "title: 'Repository'" element :repository_link, "title: 'Repository'"
element :pipelines_settings_link, "title: 'CI / CD'" element :pipelines_settings_link, "title: 'CI / CD'"
element :operations_kubernetes_link, "title: _('Kubernetes')"
element :issues_link, /link_to.*shortcuts-issues/ element :issues_link, /link_to.*shortcuts-issues/
element :issues_link_text, "Issues" element :issues_link_text, "Issues"
element :top_level_items, '.sidebar-top-level-items' element :top_level_items, '.sidebar-top-level-items'
element :operations_section, "class: 'shortcuts-operations'"
element :activity_link, "title: 'Activity'" element :activity_link, "title: 'Activity'"
end end
...@@ -33,6 +35,14 @@ module QA ...@@ -33,6 +35,14 @@ module QA
end end
end end
def click_operations_kubernetes
hover_operations do
within_submenu do
click_link('Kubernetes')
end
end
end
def click_ci_cd_pipelines def click_ci_cd_pipelines
within_sidebar do within_sidebar do
click_link('CI / CD') click_link('CI / CD')
...@@ -61,6 +71,14 @@ module QA ...@@ -61,6 +71,14 @@ module QA
end end
end end
def hover_operations
within_sidebar do
find('.shortcuts-operations').hover
yield
end
end
def within_sidebar def within_sidebar
page.within('.sidebar-top-level-items') do page.within('.sidebar-top-level-items') do
yield yield
......
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
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
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
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
...@@ -24,10 +24,10 @@ module QA::Page ...@@ -24,10 +24,10 @@ module QA::Page
end end
end end
def has_build?(name, status: :success) def has_build?(name, status: :success, wait:)
within('.pipeline-graph') do within('.pipeline-graph') do
within('.ci-job-component', text: name) 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 end
end end
......
...@@ -8,6 +8,13 @@ module QA # rubocop:disable Naming/FileName ...@@ -8,6 +8,13 @@ module QA # rubocop:disable Naming/FileName
view 'app/views/projects/settings/ci_cd/show.html.haml' do view 'app/views/projects/settings/ci_cd/show.html.haml' do
element :runners_settings, 'Runners settings' element :runners_settings, 'Runners settings'
element :secret_variables, 'Variables' 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 end
def expand_runners_settings(&block) def expand_runners_settings(&block)
...@@ -21,6 +28,14 @@ module QA # rubocop:disable Naming/FileName ...@@ -21,6 +28,14 @@ module QA # rubocop:disable Naming/FileName
Settings::SecretVariables.perform(&block) Settings::SecretVariables.perform(&block)
end end
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 end
end end
......
module QA
module Scenario
module Test
module Integration
class Kubernetes < Test::Instance
tags :kubernetes
end
end
end
end
end
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
&& 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
...@@ -3,7 +3,6 @@ require 'securerandom' ...@@ -3,7 +3,6 @@ require 'securerandom'
module QA module QA
module Service module Service
class Runner class Runner
include Scenario::Actable
include Service::Shellout include Service::Shellout
attr_accessor :token, :address, :tags, :image attr_accessor :token, :address, :tags, :image
......
module QA
feature 'Auto Devops', :kubernetes do
let(:executor) { "qa-runner-#{Time.now.to_i}" }
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
project.visit!
Git::Repository.perform do |repository|
repository.uri = Page::Project::Show.act do
choose_repository_clone_http
repository_location.uri
end
repository.use_default_credentials
repository.clone
repository.configure_identity('GitLab QA', 'root@gitlab.com')
repository.checkout_new_branch('master')
repository.add_file('config.ru', File.read(File.join(__dir__, "../../../fixtures/auto_devops_rack/config.ru")))
repository.add_file('Gemfile', File.read(File.join(__dir__, "../../../fixtures/auto_devops_rack/Gemfile")))
repository.add_file('Gemfile.lock', File.read(File.join(__dir__, "../../../fixtures/auto_devops_rack/Gemfile.lock")))
repository.add_file('Rakefile', File.read(File.join(__dir__, "../../../fixtures/auto_devops_rack/Rakefile")))
repository.commit('Create auto devops repo')
repository.push_changes("master:master")
end
# Create and connect K8s cluster
@cluster = Service::KubernetesCluster.new.create!
kubernetes_cluster = Factory::Resource::KubernetesCluster.fabricate! do |c|
c.project = project
c.cluster = @cluster
c.install_helm_tiller = true
c.install_ingress = true
c.install_prometheus = true
c.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('sast', status: :success, wait: 600)
expect(pipeline).to have_build('production', status: :success, wait: 600)
expect(pipeline).to have_build('performance', status: :success, wait: 600)
end
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment