Commit b539ef7a authored by Mark Lapierre's avatar Mark Lapierre

Merge branch 'qa-k3d-support' into 'master'

Add K3d local cluster support

See merge request gitlab-org/gitlab!19785
parents f34dd6a2 694a8cca
...@@ -290,6 +290,7 @@ export default { ...@@ -290,6 +290,7 @@ export default {
disabled && 'cluster-application-disabled', disabled && 'cluster-application-disabled',
]" ]"
class="cluster-application-row gl-responsive-table-row gl-responsive-table-row-col-span" class="cluster-application-row gl-responsive-table-row gl-responsive-table-row-col-span"
:data-qa-selector="id"
> >
<div class="gl-responsive-table-row-layout" role="row"> <div class="gl-responsive-table-row-layout" role="row">
<div class="table-section append-right-8 section-align-top" role="gridcell"> <div class="table-section append-right-8 section-align-top" role="gridcell">
...@@ -381,12 +382,16 @@ export default { ...@@ -381,12 +382,16 @@ export default {
:disabled="disabled || installButtonDisabled" :disabled="disabled || installButtonDisabled"
:label="installButtonLabel" :label="installButtonLabel"
class="js-cluster-application-install-button" class="js-cluster-application-install-button"
data-qa-selector="install_button"
:data-qa-application="id"
@click="installClicked" @click="installClicked"
/> />
<uninstall-application-button <uninstall-application-button
v-if="displayUninstallButton" v-if="displayUninstallButton"
v-gl-modal-directive="'uninstall-' + id" v-gl-modal-directive="'uninstall-' + id"
:status="status" :status="status"
data-qa-selector="uninstall_button"
:data-qa-application="id"
class="js-cluster-application-uninstall-button" class="js-cluster-application-uninstall-button"
/> />
<uninstall-application-confirmation-modal <uninstall-application-confirmation-modal
......
...@@ -10,11 +10,11 @@ module Clusters ...@@ -10,11 +10,11 @@ module Clusters
# We do not want to show the group path for clusters belonging to the # We do not want to show the group path for clusters belonging to the
# clusterable, only for the ancestor clusters. # clusterable, only for the ancestor clusters.
def item_link(clusterable_presenter) def item_link(clusterable_presenter, *html_options)
if cluster.group_type? && clusterable != clusterable_presenter.subject if cluster.group_type? && clusterable != clusterable_presenter.subject
contracted_group_name(cluster.group) + ' / ' + link_to_cluster contracted_group_name(cluster.group) + ' / ' + link_to_cluster
else else
link_to_cluster link_to_cluster(*html_options)
end end
end end
...@@ -84,8 +84,8 @@ module Clusters ...@@ -84,8 +84,8 @@ module Clusters
sprite_icon('ellipsis_h', size: 12, css_class: 'vertical-align-middle') sprite_icon('ellipsis_h', size: 12, css_class: 'vertical-align-middle')
end end
def link_to_cluster def link_to_cluster(html_options: {})
link_to_if(can_read_cluster?, cluster.name, show_path) link_to_if(can_read_cluster?, cluster.name, show_path, html_options)
end end
end end
end end
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
.table-section.section-60 .table-section.section-60
.table-mobile-header{ role: "rowheader" }= s_("ClusterIntegration|Kubernetes cluster") .table-mobile-header{ role: "rowheader" }= s_("ClusterIntegration|Kubernetes cluster")
.table-mobile-content .table-mobile-content
= cluster.item_link(clusterable) = cluster.item_link(clusterable, html_options: { data: { qa_selector: 'cluster', qa_cluster_name: cluster.name } })
- unless cluster.enabled? - unless cluster.enabled?
%span.badge.badge-danger Connection disabled %span.badge.badge-danger Connection disabled
.table-section.section-25 .table-section.section-25
......
...@@ -38,6 +38,12 @@ RUN apt-get update -q && apt-get install -y google-chrome-stable && apt-get clea ...@@ -38,6 +38,12 @@ 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 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 RUN unzip chromedriver_linux64.zip -d /usr/local/bin
##
# Install K3d local cluster support
# https://github.com/rancher/k3d
#
RUN curl -s https://raw.githubusercontent.com/rancher/k3d/master/install.sh | TAG=v1.3.4 bash
## ##
# Install gcloud and kubectl CLI used in Auto DevOps test to create K8s # Install gcloud and kubectl CLI used in Auto DevOps test to create K8s
# clusters # clusters
......
...@@ -37,6 +37,7 @@ module QA ...@@ -37,6 +37,7 @@ module QA
autoload :MailHog, 'qa/runtime/mail_hog' autoload :MailHog, 'qa/runtime/mail_hog'
autoload :IPAddress, 'qa/runtime/ip_address' autoload :IPAddress, 'qa/runtime/ip_address'
autoload :Search, 'qa/runtime/search' autoload :Search, 'qa/runtime/search'
autoload :ApplicationSettings, 'qa/runtime/application_settings'
module API module API
autoload :Client, 'qa/runtime/api/client' autoload :Client, 'qa/runtime/api/client'
......
...@@ -13,6 +13,10 @@ module QA ...@@ -13,6 +13,10 @@ module QA
def add_kubernetes_cluster def add_kubernetes_cluster
click_on 'Add Kubernetes cluster' click_on 'Add Kubernetes cluster'
end end
def has_cluster?(cluster)
has_element?(:cluster, cluster_name: cluster.to_s)
end
end end
end end
end end
......
...@@ -6,12 +6,6 @@ module QA ...@@ -6,12 +6,6 @@ module QA
module Operations module Operations
module Kubernetes module Kubernetes
class Show < Page::Base class Show < Page::Base
view 'app/assets/javascripts/clusters/components/application_row.vue' do
element :application_row, 'js-cluster-application-row-${this.id}' # rubocop:disable QA/ElementWithPattern
element :install_button, "__('Install')" # rubocop:disable QA/ElementWithPattern
element :installed_button, "__('Installed')" # rubocop:disable QA/ElementWithPattern
end
view 'app/assets/javascripts/clusters/components/applications.vue' do view 'app/assets/javascripts/clusters/components/applications.vue' do
element :ingress_ip_address, 'id="ingress-endpoint"' # rubocop:disable QA/ElementWithPattern element :ingress_ip_address, 'id="ingress-endpoint"' # rubocop:disable QA/ElementWithPattern
end end
...@@ -22,15 +16,21 @@ module QA ...@@ -22,15 +16,21 @@ module QA
end end
def install!(application_name) def install!(application_name)
within(".js-cluster-application-row-#{application_name}") do within_element(application_name) do
page.has_button?('Install', wait: 30) has_element?(:install_button, application: application_name, wait: 30)
click_on 'Install' click_on 'Install' # TODO replace with click_element
end end
end end
def await_installed(application_name) def await_installed(application_name)
within(".js-cluster-application-row-#{application_name}") do within_element(application_name) do
page.has_text?(/Installed|Uninstall/, wait: 300) has_element?(:uninstall_button, application: application_name, wait: 300)
end
end
def has_application_installed?(application_name)
within_element(application_name) do
has_element?(:uninstall_button, application: application_name, wait: 300)
end end
end end
......
# frozen_string_literal: true
module QA
module Runtime
module ApplicationSettings
extend self
extend Support::Api
APPLICATION_SETTINGS_PATH = '/application/settings'
# Set a GitLab application setting
# Example:
# #set({ allow_local_requests_from_web_hooks_and_services: true })
# #set(allow_local_requests_from_web_hooks_and_services: true)
# https://docs.gitlab.com/ee/api/settings.html
def set_application_settings(**application_settings)
QA::Runtime::Logger.info("Setting application settings: #{application_settings}")
r = put(Runtime::API::Request.new(api_client, APPLICATION_SETTINGS_PATH).url, **application_settings)
raise "Couldn't set application settings #{application_settings.inspect}" unless r.code == QA::Support::Api::HTTP_STATUS_OK
end
def get_application_settings
parse_body(get(Runtime::API::Request.new(api_client, APPLICATION_SETTINGS_PATH).url))
end
private
def api_client
@api_client ||= begin
return Runtime::API::Client.new(:gitlab, personal_access_token: Runtime::Env.admin_personal_access_token) if Runtime::Env.admin_personal_access_token
user = Resource::User.fabricate_via_api! do |user|
user.username = Runtime::User.admin_username
user.password = Runtime::User.admin_password
end
unless user.admin?
raise "Administrator access is required to set application settings. User '#{user.username}' is not an administrator."
end
Runtime::API::Client.new(:gitlab, user: user)
end
end
end
end
end
...@@ -248,6 +248,10 @@ module QA ...@@ -248,6 +248,10 @@ module QA
raise ArgumentError, "Please provide GITHUB_ACCESS_TOKEN" raise ArgumentError, "Please provide GITHUB_ACCESS_TOKEN"
end end
def require_admin_access_token!
admin_personal_access_token || (raise ArgumentError, "GITLAB_QA_ADMIN_ACCESS_TOKEN is required!")
end
# Returns true if there is an environment variable that indicates that # Returns true if there is an environment variable that indicates that
# the feature is supported in the environment under test. # the feature is supported in the environment under test.
# All features are supported by default. # All features are supported by default.
......
...@@ -6,6 +6,8 @@ module QA ...@@ -6,6 +6,8 @@ module QA
class K3d < Base class K3d < Base
def validate_dependencies def validate_dependencies
find_executable('k3d') || raise("You must first install `k3d` executable to run these tests.") find_executable('k3d') || raise("You must first install `k3d` executable to run these tests.")
Runtime::Env.require_admin_access_token!
Runtime::ApplicationSettings.set_application_settings(allow_local_requests_from_web_hooks_and_services: true)
end end
def set_credentials(admin_user) def set_credentials(admin_user)
...@@ -24,6 +26,7 @@ module QA ...@@ -24,6 +26,7 @@ module QA
def teardown def teardown
ENV['KUBECONFIG'] = @old_kubeconfig ENV['KUBECONFIG'] = @old_kubeconfig
shell "k3d delete --name #{cluster_name}" shell "k3d delete --name #{cluster_name}"
Runtime::ApplicationSettings.set_application_settings(allow_local_requests_from_web_hooks_and_services: false)
end end
# Fetch "real" certificate # Fetch "real" certificate
......
...@@ -39,6 +39,10 @@ module QA ...@@ -39,6 +39,10 @@ module QA
@provider.cluster_name @provider.cluster_name
end end
def to_s
cluster_name
end
private private
def fetch_api_url def fetch_api_url
......
# frozen_string_literal: true
module QA
context 'Configure' do
# This test requires GITLAB_QA_ADMIN_ACCESS_TOKEN to be specified
describe 'Kubernetes Cluster Integration', :orchestrated, :kubernetes, :requires_admin, :skip do
context 'Project Clusters' do
let(:cluster) { Service::KubernetesCluster.new(provider_class: Service::ClusterProvider::K3d).create! }
let(:project) do
Resource::Project.fabricate_via_api! do |project|
project.name = 'project-with-k8s'
project.description = 'Project with Kubernetes cluster integration'
end
end
before do
Flow::Login.sign_in
end
after do
cluster.remove!
end
it 'can create and associate a project cluster', :smoke do
Resource::KubernetesCluster.fabricate_via_browser_ui! do |k8s_cluster|
k8s_cluster.project = project
k8s_cluster.cluster = cluster
end
project.visit!
Page::Project::Menu.perform(&:go_to_operations_kubernetes)
Page::Project::Operations::Kubernetes::Index.perform do |index|
expect(index).to have_cluster(cluster)
end
end
it 'installs helm and tiller on a gitlab managed app' do
Resource::KubernetesCluster.fabricate_via_browser_ui! do |k8s_cluster|
k8s_cluster.project = project
k8s_cluster.cluster = cluster
k8s_cluster.install_helm_tiller = true
end
Page::Project::Operations::Kubernetes::Show.perform do |show|
expect(show).to have_application_installed(:helm)
end
end
end
end
end
end
# frozen_string_literal: true
describe QA::Runtime::ApplicationSettings do
let(:api_client) { double('QA::Runtime::API::Client') }
let(:request) { Struct.new(:url).new('http://api') }
let(:get_response) { Struct.new(:body).new("{}") }
before do
allow(described_class).to receive(:api_client).and_return(api_client)
end
describe '.set_application_settings' do
it 'sets application settings' do
expect(QA::Runtime::API::Request)
.to receive(:new)
.with(api_client, '/application/settings')
.and_return(request)
expect(described_class)
.to receive(:put)
.with(request.url, { allow_local_requests_from_web_hooks_and_services: true })
.and_return(Struct.new(:code).new(200))
subject.set_application_settings(allow_local_requests_from_web_hooks_and_services: true)
end
end
describe '.get_application_settings' do
it 'gets application settings' do
expect(QA::Runtime::API::Request)
.to receive(:new)
.with(api_client, '/application/settings')
.and_return(request)
expect(described_class)
.to receive(:get)
.with(request.url)
.and_return(get_response)
subject.get_application_settings
end
end
end
...@@ -230,6 +230,20 @@ describe QA::Runtime::Env do ...@@ -230,6 +230,20 @@ describe QA::Runtime::Env do
end end
end end
describe '.require_admin_access_token!' do
it 'raises ArgumentError if GITLAB_QA_ADMIN_ACCESS_TOKEN is not specified' do
stub_env('GITLAB_QA_ADMIN_ACCESS_TOKEN', nil)
expect { described_class.require_admin_access_token! }.to raise_error(ArgumentError)
end
it 'does not raise exception if GITLAB_QA_ADMIN_ACCESS_TOKEN is specified' do
stub_env('GITLAB_QA_ADMIN_ACCESS_TOKEN', 'foobar123')
expect { described_class.require_admin_access_token! }.not_to raise_error
end
end
describe '.log_destination' do describe '.log_destination' do
it 'returns $stdout if QA_LOG_PATH is not defined' do it 'returns $stdout if QA_LOG_PATH is not defined' do
stub_env('QA_LOG_PATH', nil) stub_env('QA_LOG_PATH', nil)
......
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