Commit 43687c62 authored by Kamil Trzciński's avatar Kamil Trzciński

Merge branch...

Merge branch '49056-configure-auto-devops-deployed-applications-with-secrets-that-aren-t-committed-to-the-repo' into 'master'

Configure Auto DevOps deployed applications with secrets from prefixed CI variables

See merge request gitlab-org/gitlab-ce!23719
parents 24985807 82a5cf0a
= _('Environment variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use environment variables for passwords, secret keys, or whatever you want.') = _('Environment variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use environment variables for passwords, secret keys, or whatever you want.')
= _('You may also add variables that are made available to the running application by prepending the variable key with <code>K8S_SECRET_</code>.').html_safe
= link_to _('More information'), help_page_path('ci/variables/README', anchor: 'variables')
---
title: Configure Auto DevOps deployed applications with secrets from prefixed CI variables
merge_request: 23719
author:
type: added
...@@ -596,10 +596,55 @@ rollout 100%: ...@@ -596,10 +596,55 @@ rollout 100%:
fi fi
} }
# Extracts variables prefixed with K8S_SECRET_
# and creates a Kubernetes secret.
#
# e.g. If we have the following environment variables:
# K8S_SECRET_A=value1
# K8S_SECRET_B=multi\ word\ value
#
# Then we will create a secret with the following key-value pairs:
# data:
# A: dmFsdWUxCg==
# B: bXVsdGkgd29yZCB2YWx1ZQo=
function create_application_secret() {
track="${1-stable}"
export APPLICATION_SECRET_NAME=$(application_secret_name "$track")
bash -c '
function k8s_prefixed_variables() {
env | sed -n "s/^K8S_SECRET_\(.*\)$/\1/p"
}
kubectl create secret \
-n "$KUBE_NAMESPACE" generic "$APPLICATION_SECRET_NAME" \
--from-env-file <(k8s_prefixed_variables) -o yaml --dry-run |
kubectl replace -n "$KUBE_NAMESPACE" --force -f -
'
}
function deploy_name() {
name="$CI_ENVIRONMENT_SLUG"
track="${1-stable}"
if [[ "$track" != "stable" ]]; then
name="$name-$track"
fi
echo $name
}
function application_secret_name() {
track="${1-stable}"
name=$(deploy_name "$track")
echo "${name}-secret"
}
function deploy() { function deploy() {
track="${1-stable}" track="${1-stable}"
percentage="${2:-100}" percentage="${2:-100}"
name="$CI_ENVIRONMENT_SLUG" name=$(deploy_name "$track")
replicas="1" replicas="1"
service_enabled="true" service_enabled="true"
...@@ -608,7 +653,6 @@ rollout 100%: ...@@ -608,7 +653,6 @@ rollout 100%:
# if track is different than stable, # if track is different than stable,
# re-use all attached resources # re-use all attached resources
if [[ "$track" != "stable" ]]; then if [[ "$track" != "stable" ]]; then
name="$name-$track"
service_enabled="false" service_enabled="false"
postgres_enabled="false" postgres_enabled="false"
fi fi
...@@ -621,6 +665,8 @@ rollout 100%: ...@@ -621,6 +665,8 @@ rollout 100%:
secret_name='' secret_name=''
fi fi
create_application_secret "$track"
if [[ -n "$DB_INITIALIZE" && -z "$(helm ls -q "^$name$")" ]]; then if [[ -n "$DB_INITIALIZE" && -z "$(helm ls -q "^$name$")" ]]; then
echo "Deploying first release with database initialization..." echo "Deploying first release with database initialization..."
helm upgrade --install \ helm upgrade --install \
...@@ -633,6 +679,7 @@ rollout 100%: ...@@ -633,6 +679,7 @@ rollout 100%:
--set image.secrets[0].name="$secret_name" \ --set image.secrets[0].name="$secret_name" \
--set application.track="$track" \ --set application.track="$track" \
--set application.database_url="$DATABASE_URL" \ --set application.database_url="$DATABASE_URL" \
--set application.secretName="$APPLICATION_SECRET_NAME" \
--set service.url="$CI_ENVIRONMENT_URL" \ --set service.url="$CI_ENVIRONMENT_URL" \
--set replicaCount="$replicas" \ --set replicaCount="$replicas" \
--set postgresql.enabled="$postgres_enabled" \ --set postgresql.enabled="$postgres_enabled" \
...@@ -665,6 +712,7 @@ rollout 100%: ...@@ -665,6 +712,7 @@ rollout 100%:
--set image.secrets[0].name="$secret_name" \ --set image.secrets[0].name="$secret_name" \
--set application.track="$track" \ --set application.track="$track" \
--set application.database_url="$DATABASE_URL" \ --set application.database_url="$DATABASE_URL" \
--set application.secretName="$APPLICATION_SECRET_NAME" \
--set service.url="$CI_ENVIRONMENT_URL" \ --set service.url="$CI_ENVIRONMENT_URL" \
--set replicaCount="$replicas" \ --set replicaCount="$replicas" \
--set postgresql.enabled="$postgres_enabled" \ --set postgresql.enabled="$postgres_enabled" \
...@@ -684,11 +732,7 @@ rollout 100%: ...@@ -684,11 +732,7 @@ rollout 100%:
function scale() { function scale() {
track="${1-stable}" track="${1-stable}"
percentage="${2-100}" percentage="${2-100}"
name="$CI_ENVIRONMENT_SLUG" name=$(deploy_name "$track")
if [[ "$track" != "stable" ]]; then
name="$name-$track"
fi
replicas=$(get_replicas "$track" "$percentage") replicas=$(get_replicas "$track" "$percentage")
...@@ -882,15 +926,14 @@ rollout 100%: ...@@ -882,15 +926,14 @@ rollout 100%:
function delete() { function delete() {
track="${1-stable}" track="${1-stable}"
name="$CI_ENVIRONMENT_SLUG" name=$(deploy_name "$track")
if [[ "$track" != "stable" ]]; then
name="$name-$track"
fi
if [[ -n "$(helm ls -q "^$name$")" ]]; then if [[ -n "$(helm ls -q "^$name$")" ]]; then
helm delete --purge "$name" helm delete --purge "$name"
fi fi
secret_name=$(application_secret_name "$track")
kubectl delete secret --ignore-not-found -n "$KUBE_NAMESPACE" "$secret_name"
} }
before_script: before_script:
......
...@@ -7719,6 +7719,9 @@ msgstr "" ...@@ -7719,6 +7719,9 @@ msgstr ""
msgid "You have reached your project limit" msgid "You have reached your project limit"
msgstr "" msgstr ""
msgid "You may also add variables that are made available to the running application by prepending the variable key with <code>K8S_SECRET_</code>."
msgstr ""
msgid "You must accept our Terms of Service and privacy policy in order to register an account" msgid "You must accept our Terms of Service and privacy policy in order to register an account"
msgstr "" msgstr ""
......
run lambda { |env| [200, { 'Content-Type' => 'text/plain' }, StringIO.new("Hello World!\n")] } run lambda { |env| [200, { 'Content-Type' => 'text/plain' }, StringIO.new("Hello World! #{ENV['OPTIONAL_MESSAGE']}\n")] }
...@@ -5,17 +5,15 @@ require 'pathname' ...@@ -5,17 +5,15 @@ require 'pathname'
module QA module QA
context 'Configure', :orchestrated, :kubernetes do context 'Configure', :orchestrated, :kubernetes do
describe 'Auto DevOps support' do describe 'Auto DevOps support' do
after do def login
@cluster&.remove!
end
[true, false].each do |rbac|
context "when rbac is #{rbac ? 'enabled' : 'disabled'}" do
it 'user creates a new project and runs auto devops' do
Runtime::Browser.visit(:gitlab, Page::Main::Login) Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials } Page::Main::Login.act { sign_in_using_credentials }
end
project = Resource::Project.fabricate! do |p| before(:all) do
login
@project = Resource::Project.fabricate! do |p|
p.name = Runtime::Env.auto_devops_project_name || 'project-with-autodevops' p.name = Runtime::Env.auto_devops_project_name || 'project-with-autodevops'
p.description = 'Project with Auto Devops' p.description = 'Project with Auto Devops'
end end
...@@ -23,14 +21,14 @@ module QA ...@@ -23,14 +21,14 @@ module QA
# Disable code_quality check in Auto DevOps pipeline as it takes # Disable code_quality check in Auto DevOps pipeline as it takes
# too long and times out the test # too long and times out the test
Resource::CiVariable.fabricate! do |resource| Resource::CiVariable.fabricate! do |resource|
resource.project = project resource.project = @project
resource.key = 'CODE_QUALITY_DISABLED' resource.key = 'CODE_QUALITY_DISABLED'
resource.value = '1' resource.value = '1'
end end
# Create Auto Devops compatible repo # Create Auto Devops compatible repo
Resource::Repository::ProjectPush.fabricate! do |push| Resource::Repository::ProjectPush.fabricate! do |push|
push.project = project push.project = @project
push.directory = Pathname push.directory = Pathname
.new(__dir__) .new(__dir__)
.join('../../../../../fixtures/auto_devops_rack') .join('../../../../../fixtures/auto_devops_rack')
...@@ -38,27 +36,71 @@ module QA ...@@ -38,27 +36,71 @@ module QA
end end
Page::Project::Show.act { wait_for_push } Page::Project::Show.act { wait_for_push }
end
[true, false].each do |rbac|
context "when rbac is #{rbac ? 'enabled' : 'disabled'}" do
before(:all) do
# Create and connect K8s cluster # Create and connect K8s cluster
@cluster = Service::KubernetesCluster.new(rbac: rbac).create! @cluster = Service::KubernetesCluster.new(rbac: rbac).create!
kubernetes_cluster = Resource::KubernetesCluster.fabricate! do |cluster| kubernetes_cluster = Resource::KubernetesCluster.fabricate! do |cluster|
cluster.project = project cluster.project = @project
cluster.cluster = @cluster cluster.cluster = @cluster
cluster.install_helm_tiller = true cluster.install_helm_tiller = true
cluster.install_ingress = true cluster.install_ingress = true
cluster.install_prometheus = true cluster.install_prometheus = true
cluster.install_runner = true cluster.install_runner = true
end end
kubernetes_cluster.populate(:ingress_ip) kubernetes_cluster.populate(:ingress_ip)
project.visit! @project.visit!
Page::Project::Menu.act { click_ci_cd_settings } Page::Project::Menu.act { click_ci_cd_settings }
Page::Project::Settings::CICD.perform do |p| Page::Project::Settings::CICD.perform do |p|
p.enable_auto_devops_with_domain( p.enable_auto_devops_with_domain(
"#{kubernetes_cluster.ingress_ip}.nip.io") "#{kubernetes_cluster.ingress_ip}.nip.io")
end end
end
after(:all) do
@cluster&.remove!
end
before do
login
end
it 'runs auto devops' do
@project.visit!
Page::Project::Menu.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: 1200)
end
project.visit! Page::Project::Menu.act { click_operations_environments }
Page::Project::Operations::Environments::Index.perform do |index|
index.go_to_environment('production')
end
Page::Project::Operations::Environments::Show.perform do |show|
show.view_deployment do
expect(page).to have_content('Hello World!')
end
end
end
it 'user sets application secret variable and Auto DevOps passes it to container' do
# Set an application secret CI variable (prefixed with K8S_SECRET_)
Resource::CiVariable.fabricate! do |resource|
resource.project = @project
resource.key = 'K8S_SECRET_OPTIONAL_MESSAGE'
resource.value = 'You can see this application secret'
end
@project.visit!
Page::Project::Menu.act { click_ci_cd_pipelines } Page::Project::Menu.act { click_ci_cd_pipelines }
Page::Project::Pipeline::Index.act { go_to_latest_pipeline } Page::Project::Pipeline::Index.act { go_to_latest_pipeline }
...@@ -69,12 +111,15 @@ module QA ...@@ -69,12 +111,15 @@ module QA
end end
Page::Project::Menu.act { click_operations_environments } Page::Project::Menu.act { click_operations_environments }
Page::Project::Operations::Environments::Index.perform do |index| Page::Project::Operations::Environments::Index.perform do |index|
index.go_to_environment('production') index.go_to_environment('production')
end end
Page::Project::Operations::Environments::Show.perform do |show| Page::Project::Operations::Environments::Show.perform do |show|
show.view_deployment do show.view_deployment do
expect(page).to have_content('Hello World!') expect(page).to have_content('Hello World!')
expect(page).to have_content('You can see this application secret')
end end
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