Auto-DevOps.gitlab-ci.yml 25.9 KB
Newer Older
1 2 3 4 5 6 7 8 9
# Auto DevOps
# This CI/CD configuration provides a standard pipeline for
# * building a Docker image (using a buildpack if necessary),
# * storing the image in the container registry,
# * running tests from a buildpack,
# * running code quality analysis,
# * creating a review app for each topic branch,
# * and continuous deployment to production
#
10 11 12
# Test jobs may be disabled by setting environment variables:
# * test: TEST_DISABLED
# * code_quality: CODE_QUALITY_DISABLED
13
# * license_management: LICENSE_MANAGEMENT_DISABLED
14 15 16 17 18 19 20 21
# * performance: PERFORMANCE_DISABLED
# * sast: SAST_DISABLED
# * dependency_scanning: DEPENDENCY_SCANNING_DISABLED
# * container_scanning: CONTAINER_SCANNING_DISABLED
# * dast: DAST_DISABLED
# * review: REVIEW_DISABLED
# * stop_review: REVIEW_DISABLED
#
22 23 24 25 26
# In order to deploy, you must have a Kubernetes cluster configured either
# via a project integration, or via group/project variables.
# AUTO_DEVOPS_DOMAIN must also be set as a variable at the group or project
# level, or manually added below.
#
27
# Continuous deployment to production is enabled by default.
28 29
# If you want to deploy to staging first, set STAGING_ENABLED environment variable.
# If you want to enable incremental rollout, either manual or time based,
30
# set INCREMENTAL_ROLLOUT_MODE environment variable to "manual" or "timed".
31
# If you want to use canary deployments, set CANARY_ENABLED environment variable.
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
#
# If Auto DevOps fails to detect the proper buildpack, or if you want to
# specify a custom buildpack, set a project variable `BUILDPACK_URL` to the
# repository URL of the buildpack.
# e.g. BUILDPACK_URL=https://github.com/heroku/heroku-buildpack-ruby.git#v142
# If you need multiple buildpacks, add a file to your project called
# `.buildpacks` that contains the URLs, one on each line, in order.
# Note: Auto CI does not work with multiple buildpacks yet

image: alpine:latest

variables:
  # AUTO_DEVOPS_DOMAIN is the application deployment domain and should be set as a variable at the group or project level.
  # AUTO_DEVOPS_DOMAIN: domain.example.com

  POSTGRES_USER: user
  POSTGRES_PASSWORD: testing-password
  POSTGRES_ENABLED: "true"
  POSTGRES_DB: $CI_ENVIRONMENT_SLUG

52
  KUBERNETES_VERSION: 1.10.9
53
  HELM_VERSION: 2.11.0
54

55 56
  DOCKER_DRIVER: overlay2

57 58 59 60
stages:
  - build
  - test
  - review
61
  - dast
62 63 64
  - staging
  - canary
  - production
65 66 67 68
  - incremental rollout 10%
  - incremental rollout 25%
  - incremental rollout 50%
  - incremental rollout 100%
69
  - performance
70 71 72 73
  - cleanup

build:
  stage: build
74
  image: docker:stable-git
75
  services:
76
  - docker:stable-dind
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
  script:
    - setup_docker
    - build
  only:
    - branches

test:
  services:
    - postgres:latest
  variables:
    POSTGRES_DB: test
  stage: test
  image: gliderlabs/herokuish:latest
  script:
    - setup_test_db
    - cp -R . /tmp/app
    - /bin/herokuish buildpack test
  only:
    - branches
96 97 98
  except:
    variables:
      - $TEST_DISABLED
99

100
code_quality:
101
  stage: test
102
  image: docker:stable
103 104
  allow_failure: true
  services:
105
    - docker:stable-dind
106 107
  script:
    - setup_docker
108
    - code_quality
109
  artifacts:
110
    paths: [gl-code-quality-report.json]
111 112
  only:
    - branches
113 114 115
  except:
    variables:
      - $CODE_QUALITY_DISABLED
116

117
license_management:
118
  stage: test
119 120 121
  image: 
    name: "registry.gitlab.com/gitlab-org/security-products/license-management:$CI_SERVER_VERSION_MAJOR-$CI_SERVER_VERSION_MINOR-stable"
    entrypoint: [""]
122 123 124 125 126
  allow_failure: true
  script:
    - license_management
  artifacts:
    paths: [gl-license-management-report.json]
127
  only:
128 129
    refs:
      - branches
130 131
    variables:
      - $GITLAB_FEATURES =~ /\blicense_management\b/
132 133 134 135
  except:
    variables:
      - $LICENSE_MANAGEMENT_DISABLED

136 137
performance:
  stage: performance
138
  image: docker:stable
139 140
  allow_failure: true
  services:
141
    - docker:stable-dind
142
  script:
143
    - setup_docker
144 145 146 147
    - performance
  artifacts:
    paths:
    - performance.json
148
    - sitespeed-results/
149 150 151
  only:
    refs:
      - branches
152
    kubernetes: active
153 154 155
  except:
    variables:
      - $PERFORMANCE_DISABLED
156

157
sast:
158
  stage: test
159
  image: docker:stable
160
  allow_failure: true
161
  services:
162
    - docker:stable-dind
163
  script:
164 165
    - setup_docker
    - sast
166
  artifacts:
167 168
    reports:
      sast: gl-sast-report.json
169
  only:
170 171 172 173
    refs:
      - branches
    variables:
      - $GITLAB_FEATURES =~ /\bsast\b/
174 175 176
  except:
    variables:
      - $SAST_DISABLED
177

178
dependency_scanning:
179
  stage: test
180 181 182 183 184 185 186 187 188
  image: docker:stable
  allow_failure: true
  services:
    - docker:stable-dind
  script:
    - setup_docker
    - dependency_scanning
  artifacts:
    paths: [gl-dependency-scanning-report.json]
189
  only:
190 191 192 193
    refs:
      - branches
    variables:
      - $GITLAB_FEATURES =~ /\bdependency_scanning\b/
194 195 196
  except:
    variables:
      - $DEPENDENCY_SCANNING_DISABLED
197

198
container_scanning:
199
  stage: test
200
  image: docker:stable
Luke Bennett's avatar
Luke Bennett committed
201 202
  allow_failure: true
  services:
203
    - docker:stable-dind
Luke Bennett's avatar
Luke Bennett committed
204 205
  script:
    - setup_docker
206
    - container_scanning
Luke Bennett's avatar
Luke Bennett committed
207
  artifacts:
208
    paths: [gl-container-scanning-report.json]
209
  only:
210 211 212
    refs:
      - branches
    variables:
213
      - $GITLAB_FEATURES =~ /\bcontainer_scanning\b/
214 215 216
  except:
    variables:
      - $CONTAINER_SCANNING_DISABLED
217

218 219 220
dast:
  stage: dast
  allow_failure: true
221
  image: registry.gitlab.com/gitlab-org/security-products/zaproxy
222 223 224 225 226 227 228 229 230
  variables:
    POSTGRES_DB: "false"
  script:
    - dast
  artifacts:
    paths: [gl-dast-report.json]
  only:
    refs:
      - branches
231
    kubernetes: active
232 233
    variables:
      - $GITLAB_FEATURES =~ /\bdast\b/
234
  except:
235 236 237 238
    refs:
      - master
    variables:
      - $DAST_DISABLED
239

240 241 242 243 244 245 246
review:
  stage: review
  script:
    - check_kube_domain
    - install_dependencies
    - download_chart
    - ensure_namespace
247
    - initialize_tiller
248 249
    - create_secret
    - deploy
250
    - persist_environment_url
251 252 253 254
  environment:
    name: review/$CI_COMMIT_REF_NAME
    url: http://$CI_PROJECT_PATH_SLUG-$CI_ENVIRONMENT_SLUG.$AUTO_DEVOPS_DOMAIN
    on_stop: stop_review
255 256
  artifacts:
    paths: [environment_url.txt]
257 258 259
  only:
    refs:
      - branches
260
    kubernetes: active
261
  except:
262 263 264 265
    refs:
      - master
    variables:
      - $REVIEW_DISABLED
266 267 268 269 270 271 272

stop_review:
  stage: cleanup
  variables:
    GIT_STRATEGY: none
  script:
    - install_dependencies
273
    - initialize_tiller
274 275 276 277 278 279 280 281 282
    - delete
  environment:
    name: review/$CI_COMMIT_REF_NAME
    action: stop
  when: manual
  allow_failure: true
  only:
    refs:
      - branches
283
    kubernetes: active
284
  except:
285 286 287 288
    refs:
      - master
    variables:
      - $REVIEW_DISABLED
289 290 291 292

# Staging deploys are disabled by default since
# continuous deployment to production is enabled by default
# If you prefer to automatically deploy to staging and
293 294
# only manually promote to production, enable this job by setting
# STAGING_ENABLED.
295

296
staging:
297 298 299 300 301 302
  stage: staging
  script:
    - check_kube_domain
    - install_dependencies
    - download_chart
    - ensure_namespace
303
    - initialize_tiller
304 305 306 307 308 309 310 311
    - create_secret
    - deploy
  environment:
    name: staging
    url: http://$CI_PROJECT_PATH_SLUG-staging.$AUTO_DEVOPS_DOMAIN
  only:
    refs:
      - master
312
    kubernetes: active
313 314
    variables:
      - $STAGING_ENABLED
315

316 317 318
# Canaries are also disabled by default, but if you want them,
# and know what the downsides are, you can enable this by setting
# CANARY_ENABLED.
319

320
canary:
321 322 323 324 325 326
  stage: canary
  script:
    - check_kube_domain
    - install_dependencies
    - download_chart
    - ensure_namespace
327
    - initialize_tiller
328 329 330 331 332 333 334 335 336
    - create_secret
    - deploy canary
  environment:
    name: production
    url: http://$CI_PROJECT_PATH_SLUG.$AUTO_DEVOPS_DOMAIN
  when: manual
  only:
    refs:
      - master
337
    kubernetes: active
338 339
    variables:
      - $CANARY_ENABLED
340

341
.production: &production_template
342 343 344 345 346 347
  stage: production
  script:
    - check_kube_domain
    - install_dependencies
    - download_chart
    - ensure_namespace
348
    - initialize_tiller
349 350 351
    - create_secret
    - deploy
    - delete canary
352
    - delete rollout
353
    - persist_environment_url
354 355 356
  environment:
    name: production
    url: http://$CI_PROJECT_PATH_SLUG.$AUTO_DEVOPS_DOMAIN
357 358
  artifacts:
    paths: [environment_url.txt]
359 360 361

production:
  <<: *production_template
362 363 364
  only:
    refs:
      - master
365
    kubernetes: active
366 367 368
  except:
    variables:
      - $STAGING_ENABLED
369
      - $CANARY_ENABLED
370
      - $INCREMENTAL_ROLLOUT_ENABLED
371
      - $INCREMENTAL_ROLLOUT_MODE
372 373 374 375

production_manual:
  <<: *production_template
  when: manual
376
  allow_failure: false
377 378 379
  only:
    refs:
      - master
380
    kubernetes: active
381 382
    variables:
      - $STAGING_ENABLED
383
      - $CANARY_ENABLED
384 385 386
  except:
    variables:
      - $INCREMENTAL_ROLLOUT_ENABLED
387
      - $INCREMENTAL_ROLLOUT_MODE
388 389 390 391 392 393 394 395 396

# This job implements incremental rollout on for every push to `master`.

.rollout: &rollout_template
  script:
    - check_kube_domain
    - install_dependencies
    - download_chart
    - ensure_namespace
397
    - initialize_tiller
398 399 400 401 402 403 404 405 406 407 408
    - create_secret
    - deploy rollout $ROLLOUT_PERCENTAGE
    - scale stable $((100-ROLLOUT_PERCENTAGE))
    - delete canary
    - persist_environment_url
  environment:
    name: production
    url: http://$CI_PROJECT_PATH_SLUG.$AUTO_DEVOPS_DOMAIN
  artifacts:
    paths: [environment_url.txt]

409
.manual_rollout_template: &manual_rollout_template
410
  <<: *rollout_template
411
  stage: production
412
  when: manual
413
  # This selectors are backward compatible mode with $INCREMENTAL_ROLLOUT_ENABLED (before 11.4)
414 415 416
  only:
    refs:
      - master
417
    kubernetes: active
418
    variables:
419
      - $INCREMENTAL_ROLLOUT_MODE == "manual"
420
      - $INCREMENTAL_ROLLOUT_ENABLED
421 422 423
  except:
    variables:
      - $INCREMENTAL_ROLLOUT_MODE == "timed"
424

425
.timed_rollout_template: &timed_rollout_template
426
  <<: *rollout_template
427 428
  when: delayed
  start_in: 5 minutes
429 430 431
  only:
    refs:
      - master
432
    kubernetes: active
433
    variables:
434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469
      - $INCREMENTAL_ROLLOUT_MODE == "timed"

timed rollout 10%:
  <<: *timed_rollout_template
  stage: incremental rollout 10%
  variables:
    ROLLOUT_PERCENTAGE: 10

timed rollout 25%:
  <<: *timed_rollout_template
  stage: incremental rollout 25%
  variables:
    ROLLOUT_PERCENTAGE: 25

timed rollout 50%:
  <<: *timed_rollout_template
  stage: incremental rollout 50%
  variables:
    ROLLOUT_PERCENTAGE: 50

timed rollout 100%:
  <<: *timed_rollout_template
  <<: *production_template
  stage: incremental rollout 100%
  variables:
    ROLLOUT_PERCENTAGE: 100

rollout 10%:
  <<: *manual_rollout_template
  variables:
    ROLLOUT_PERCENTAGE: 10

rollout 25%:
  <<: *manual_rollout_template
  variables:
    ROLLOUT_PERCENTAGE: 25
470 471

rollout 50%:
472
  <<: *manual_rollout_template
473 474 475 476
  variables:
    ROLLOUT_PERCENTAGE: 50

rollout 100%:
477
  <<: *manual_rollout_template
478
  <<: *production_template
479
  allow_failure: false
480 481 482 483 484 485 486 487 488 489 490 491

# ---------------------------------------------------------------------------

.auto_devops: &auto_devops |
  # Auto DevOps variables and functions
  [[ "$TRACE" ]] && set -x
  auto_database_url=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${CI_ENVIRONMENT_SLUG}-postgres:5432/${POSTGRES_DB}
  export DATABASE_URL=${DATABASE_URL-$auto_database_url}
  export CI_APPLICATION_REPOSITORY=$CI_REGISTRY_IMAGE/$CI_COMMIT_REF_SLUG
  export CI_APPLICATION_TAG=$CI_COMMIT_SHA
  export CI_CONTAINER_NAME=ci_job_build_${CI_JOB_ID}
  export TILLER_NAMESPACE=$KUBE_NAMESPACE
492 493
  # Extract "MAJOR.MINOR" from CI_SERVER_VERSION and generate "MAJOR-MINOR-stable" for Security Products
  export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')
494

495
  function registry_login() {
496 497 498 499 500
    if [[ -n "$CI_REGISTRY_USER" ]]; then
      echo "Logging to GitLab Container Registry with CI credentials..."
      docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"
      echo ""
    fi
501 502 503 504
  }

  function container_scanning() {
    registry_login
505

Luke Bennett's avatar
Luke Bennett committed
506
    docker run -d --name db arminc/clair-db:latest
507
    docker run -p 6060:6060 --link db:postgres -d --name clair --restart on-failure arminc/clair-local-scan:v2.0.1
Luke Bennett's avatar
Luke Bennett committed
508 509
    apk add -U wget ca-certificates
    docker pull ${CI_APPLICATION_REPOSITORY}:${CI_APPLICATION_TAG}
510 511
    wget https://github.com/arminc/clair-scanner/releases/download/v8/clair-scanner_linux_amd64
    mv clair-scanner_linux_amd64 clair-scanner
Luke Bennett's avatar
Luke Bennett committed
512 513
    chmod +x clair-scanner
    touch clair-whitelist.yml
514 515 516
    retries=0
    echo "Waiting for clair daemon to start"
    while( ! wget -T 10 -q -O /dev/null http://docker:6060/v1/namespaces ) ; do sleep 1 ; echo -n "." ; if [ $retries -eq 10 ] ; then echo " Timeout, aborting." ; exit 1 ; fi ; retries=$(($retries+1)) ; done
517
    ./clair-scanner -c http://docker:6060 --ip $(hostname -i) -r gl-container-scanning-report.json -l clair.log -w clair-whitelist.yml ${CI_APPLICATION_REPOSITORY}:${CI_APPLICATION_TAG} || true
Luke Bennett's avatar
Luke Bennett committed
518
  }
519

520
  function code_quality() {
521
    docker run --env SOURCE_CODE="$PWD" \
522 523
               --volume "$PWD":/code \
               --volume /var/run/docker.sock:/var/run/docker.sock \
524
               "registry.gitlab.com/gitlab-org/security-products/codequality:$SP_VERSION" /code
525 526
  }

527
  function license_management() {
528
    /run.sh analyze .
529 530
  }

531 532 533
  function sast() {
    case "$CI_SERVER_VERSION" in
      *-ee)
534

535 536 537 538 539 540 541 542
        # Deprecation notice for CONFIDENCE_LEVEL variable
        if [ -z "$SAST_CONFIDENCE_LEVEL" -a "$CONFIDENCE_LEVEL" ]; then
          SAST_CONFIDENCE_LEVEL="$CONFIDENCE_LEVEL"
          echo "WARNING: CONFIDENCE_LEVEL is deprecated and MUST be replaced with SAST_CONFIDENCE_LEVEL"
        fi

        docker run --env SAST_CONFIDENCE_LEVEL="${SAST_CONFIDENCE_LEVEL:-3}" \
                   --volume "$PWD:/code" \
543
                   --volume /var/run/docker.sock:/var/run/docker.sock \
544
                   "registry.gitlab.com/gitlab-org/security-products/sast:$SP_VERSION" /app/bin/run /code
545 546 547 548 549 550 551
        ;;
      *)
        echo "GitLab EE is required"
        ;;
    esac
  }

552 553 554 555 556 557 558 559 560 561 562 563 564 565
  function dependency_scanning() {
    case "$CI_SERVER_VERSION" in
      *-ee)
        docker run --env DEP_SCAN_DISABLE_REMOTE_CHECKS="${DEP_SCAN_DISABLE_REMOTE_CHECKS:-false}" \
                   --volume "$PWD:/code" \
                   --volume /var/run/docker.sock:/var/run/docker.sock \
                   "registry.gitlab.com/gitlab-org/security-products/dependency-scanning:$SP_VERSION" /code
        ;;
      *)
        echo "GitLab EE is required"
        ;;
    esac
  }

566 567 568
  function get_replicas() {
    track="${1:-stable}"
    percentage="${2:-100}"
569 570 571 572

    env_track=$( echo $track | tr -s  '[:lower:]'  '[:upper:]' )
    env_slug=$( echo ${CI_ENVIRONMENT_SLUG//-/_} | tr -s  '[:lower:]'  '[:upper:]' )

573
    if [[ "$track" == "stable" ]] || [[ "$track" == "rollout" ]]; then
574 575
      # for stable track get number of replicas from `PRODUCTION_REPLICAS`
      eval new_replicas=\$${env_slug}_REPLICAS
576 577 578
      if [[ -z "$new_replicas" ]]; then
        new_replicas=$REPLICAS
      fi
579 580 581
    else
      # for all tracks get number of replicas from `CANARY_PRODUCTION_REPLICAS`
      eval new_replicas=\$${env_track}_${env_slug}_REPLICAS
582 583 584
      if [[ -z "$new_replicas" ]]; then
        eval new_replicas=\${env_track}_REPLICAS
      fi
585
    fi
586 587 588 589 590 591 592 593 594 595 596 597

    replicas="${new_replicas:-1}"
    replicas="$(($replicas * $percentage / 100))"

    # always return at least one replicas
    if [[ $replicas -gt 0 ]]; then
      echo "$replicas"
    else
      echo 1
    fi
  }

598 599 600
  # Extracts variables prefixed with K8S_SECRET_
  # and creates a Kubernetes secret.
  #
Thong Kuah's avatar
Thong Kuah committed
601
  # e.g. If we have the following environment variables:
602 603 604
  #   K8S_SECRET_A=value1
  #   K8S_SECRET_B=multi\ word\ value
  #
Thong Kuah's avatar
Thong Kuah committed
605 606 607 608
  # Then we will create a secret with the following key-value pairs:
  #   data:
  #     A: dmFsdWUxCg==
  #     B: bXVsdGkgd29yZCB2YWx1ZQo=
609
  function create_application_secret() {
610 611 612
    track="${1-stable}"
    export APPLICATION_SECRET_NAME=$(application_secret_name "$track")

613
    bash -c '
Thong Kuah's avatar
Thong Kuah committed
614 615
      function k8s_prefixed_variables() {
        env | sed -n "s/^K8S_SECRET_\(.*\)$/\1/p"
616 617
      }

Thong Kuah's avatar
Thong Kuah committed
618 619 620 621
      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 -
622
    '
623 624
  }

625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642
  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"
  }

643 644 645
  function deploy() {
    track="${1-stable}"
    percentage="${2:-100}"
646
    name=$(deploy_name "$track")
647 648 649 650 651 652 653 654 655 656

    replicas="1"
    service_enabled="true"
    postgres_enabled="$POSTGRES_ENABLED"

    # if track is different than stable,
    # re-use all attached resources
    if [[ "$track" != "stable" ]]; then
      service_enabled="false"
      postgres_enabled="false"
657 658
    fi

659 660
    replicas=$(get_replicas "$track" "$percentage")

661 662 663 664 665 666
    if [[ "$CI_PROJECT_VISIBILITY" != "public" ]]; then
      secret_name='gitlab-registry'
    else
      secret_name=''
    fi

667
    create_application_secret "$track"
668

669
    if [[ -n "$DB_INITIALIZE" && -z "$(helm ls -q "^$name$")" ]]; then
670
      echo "Deploying first release with database initialization..."
671 672 673 674 675 676 677 678 679 680
      helm upgrade --install \
        --wait \
        --set service.enabled="$service_enabled" \
        --set releaseOverride="$CI_ENVIRONMENT_SLUG" \
        --set image.repository="$CI_APPLICATION_REPOSITORY" \
        --set image.tag="$CI_APPLICATION_TAG" \
        --set image.pullPolicy=IfNotPresent \
        --set image.secrets[0].name="$secret_name" \
        --set application.track="$track" \
        --set application.database_url="$DATABASE_URL" \
681
        --set application.secretName="$APPLICATION_SECRET_NAME" \
682 683 684 685 686 687 688 689 690 691 692 693
        --set service.url="$CI_ENVIRONMENT_URL" \
        --set replicaCount="$replicas" \
        --set postgresql.enabled="$postgres_enabled" \
        --set postgresql.nameOverride="postgres" \
        --set postgresql.postgresUser="$POSTGRES_USER" \
        --set postgresql.postgresPassword="$POSTGRES_PASSWORD" \
        --set postgresql.postgresDatabase="$POSTGRES_DB" \
        --set application.initializeCommand="$DB_INITIALIZE" \
        --namespace="$KUBE_NAMESPACE" \
        "$name" \
        chart/

694
      echo "Deploying second release..."
695 696 697 698 699 700 701 702
      helm upgrade --reuse-values \
        --wait \
        --set application.initializeCommand="" \
        --set application.migrateCommand="$DB_MIGRATE" \
        --namespace="$KUBE_NAMESPACE" \
        "$name" \
        chart/
    else
703
      echo "Deploying new release..."
704 705 706 707 708 709 710 711 712 713
      helm upgrade --install \
        --wait \
        --set service.enabled="$service_enabled" \
        --set releaseOverride="$CI_ENVIRONMENT_SLUG" \
        --set image.repository="$CI_APPLICATION_REPOSITORY" \
        --set image.tag="$CI_APPLICATION_TAG" \
        --set image.pullPolicy=IfNotPresent \
        --set image.secrets[0].name="$secret_name" \
        --set application.track="$track" \
        --set application.database_url="$DATABASE_URL" \
714
        --set application.secretName="$APPLICATION_SECRET_NAME" \
715 716 717 718 719 720 721 722 723 724 725 726
        --set service.url="$CI_ENVIRONMENT_URL" \
        --set replicaCount="$replicas" \
        --set postgresql.enabled="$postgres_enabled" \
        --set postgresql.nameOverride="postgres" \
        --set postgresql.postgresUser="$POSTGRES_USER" \
        --set postgresql.postgresPassword="$POSTGRES_PASSWORD" \
        --set postgresql.postgresDatabase="$POSTGRES_DB" \
        --set application.migrateCommand="$DB_MIGRATE" \
        --namespace="$KUBE_NAMESPACE" \
        "$name" \
        chart/
    fi
727 728

    kubectl rollout status -n "$KUBE_NAMESPACE" -w "deployment/$name"
729 730
  }

731 732 733
  function scale() {
    track="${1-stable}"
    percentage="${2-100}"
734
    name=$(deploy_name "$track")
735 736 737

    replicas=$(get_replicas "$track" "$percentage")

738 739 740 741 742 743 744 745
    if [[ -n "$(helm ls -q "^$name$")" ]]; then
      helm upgrade --reuse-values \
        --wait \
        --set replicaCount="$replicas" \
        --namespace="$KUBE_NAMESPACE" \
        "$name" \
        chart/
    fi
746 747
  }

748 749
  function install_dependencies() {
    apk add -U openssl curl tar gzip bash ca-certificates git
750 751
    curl -L -o /etc/apk/keys/sgerrand.rsa.pub https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub
    curl -L -O https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.28-r0/glibc-2.28-r0.apk
752 753
    apk add glibc-2.28-r0.apk
    rm glibc-2.28-r0.apk
754

755
    curl "https://kubernetes-helm.storage.googleapis.com/helm-v${HELM_VERSION}-linux-amd64.tar.gz" | tar zx
756
    mv linux-amd64/helm /usr/bin/
757
    mv linux-amd64/tiller /usr/bin/
758
    helm version --client
759 760
    tiller -version

761
    curl -L -o /usr/bin/kubectl "https://storage.googleapis.com/kubernetes-release/release/v${KUBERNETES_VERSION}/bin/linux/amd64/kubectl"
762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787
    chmod +x /usr/bin/kubectl
    kubectl version --client
  }

  function setup_docker() {
    if ! docker info &>/dev/null; then
      if [ -z "$DOCKER_HOST" -a "$KUBERNETES_PORT" ]; then
        export DOCKER_HOST='tcp://localhost:2375'
      fi
    fi
  }

  function setup_test_db() {
    if [ -z ${KUBERNETES_PORT+x} ]; then
      DB_HOST=postgres
    else
      DB_HOST=localhost
    fi
    export DATABASE_URL="postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${DB_HOST}:5432/${POSTGRES_DB}"
  }

  function download_chart() {
    if [[ ! -d chart ]]; then
      auto_chart=${AUTO_DEVOPS_CHART:-gitlab/auto-deploy-app}
      auto_chart_name=$(basename $auto_chart)
      auto_chart_name=${auto_chart_name%.tgz}
788
      auto_chart_name=${auto_chart_name%.tar.gz}
789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812
    else
      auto_chart="chart"
      auto_chart_name="chart"
    fi

    helm init --client-only
    helm repo add gitlab https://charts.gitlab.io
    if [[ ! -d "$auto_chart" ]]; then
      helm fetch ${auto_chart} --untar
    fi
    if [ "$auto_chart_name" != "chart" ]; then
      mv ${auto_chart_name} chart
    fi

    helm dependency update chart/
    helm dependency build chart/
  }

  function ensure_namespace() {
    kubectl describe namespace "$KUBE_NAMESPACE" || kubectl create namespace "$KUBE_NAMESPACE"
  }

  function check_kube_domain() {
    if [ -z ${AUTO_DEVOPS_DOMAIN+x} ]; then
813
      echo "In order to deploy or use Review Apps, AUTO_DEVOPS_DOMAIN variable must be set"
814
      echo "You can do it in Auto DevOps project settings or defining a variable at group or project level"
815
      echo "You can also manually add it in .gitlab-ci.yml"
816 817 818 819 820 821 822
      false
    else
      true
    fi
  }

  function build() {
823
    registry_login
824

825 826
    if [[ -f Dockerfile ]]; then
      echo "Building Dockerfile-based application..."
827 828 829 830 831 832 833 834 835 836
      docker build \
        --build-arg HTTP_PROXY="$HTTP_PROXY" \
        --build-arg http_proxy="$http_proxy" \
        --build-arg HTTPS_PROXY="$HTTPS_PROXY" \
        --build-arg https_proxy="$https_proxy" \
        --build-arg FTP_PROXY="$FTP_PROXY" \
        --build-arg ftp_proxy="$ftp_proxy" \
        --build-arg NO_PROXY="$NO_PROXY" \
        --build-arg no_proxy="$no_proxy" \
        -t "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG" .
837 838
    else
      echo "Building Heroku-based application using gliderlabs/herokuish docker image..."
839 840 841 842 843 844 845 846 847 848 849
      docker run -i \
        -e BUILDPACK_URL \
        -e HTTP_PROXY \
        -e http_proxy \
        -e HTTPS_PROXY \
        -e https_proxy \
        -e FTP_PROXY \
        -e ftp_proxy \
        -e NO_PROXY \
        -e no_proxy \
        --name="$CI_CONTAINER_NAME" -v "$(pwd):/tmp/app:ro" gliderlabs/herokuish /bin/herokuish buildpack build
850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865
      docker commit "$CI_CONTAINER_NAME" "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG"
      docker rm "$CI_CONTAINER_NAME" >/dev/null
      echo ""

      echo "Configuring $CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG docker image..."
      docker create --expose 5000 --env PORT=5000 --name="$CI_CONTAINER_NAME" "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG" /bin/herokuish procfile start web
      docker commit "$CI_CONTAINER_NAME" "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG"
      docker rm "$CI_CONTAINER_NAME" >/dev/null
      echo ""
    fi

    echo "Pushing to GitLab Container Registry..."
    docker push "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG"
    echo ""
  }

866
  function initialize_tiller() {
867
    echo "Checking Tiller..."
868

869
    export HELM_HOST="localhost:44134"
870
    tiller -listen ${HELM_HOST} -alsologtostderr > /dev/null 2>&1 &
871
    echo "Tiller is listening on ${HELM_HOST}"
872

873 874 875 876 877 878 879 880
    if ! helm version --debug; then
      echo "Failed to init Tiller."
      return 1
    fi
    echo ""
  }

  function create_secret() {
881
    echo "Create secret..."
882 883 884
    if [[ "$CI_PROJECT_VISIBILITY" == "public" ]]; then
      return
    fi
885

886 887 888
    kubectl create secret -n "$KUBE_NAMESPACE" \
      docker-registry gitlab-registry \
      --docker-server="$CI_REGISTRY" \
889 890
      --docker-username="${CI_DEPLOY_USER:-$CI_REGISTRY_USER}" \
      --docker-password="${CI_DEPLOY_PASSWORD:-$CI_REGISTRY_PASSWORD}" \
891 892 893
      --docker-email="$GITLAB_USER_EMAIL" \
      -o yaml --dry-run | kubectl replace -n "$KUBE_NAMESPACE" --force -f -
  }
894

895 896 897 898 899 900 901 902
  function dast() {
    export CI_ENVIRONMENT_URL=$(cat environment_url.txt)

    mkdir /zap/wrk/
    /zap/zap-baseline.py -J gl-dast-report.json -t "$CI_ENVIRONMENT_URL" || true
    cp /zap/wrk/gl-dast-report.json .
  }

903 904
  function performance() {
    export CI_ENVIRONMENT_URL=$(cat environment_url.txt)
905

906
    mkdir gitlab-exporter
907
    wget -O gitlab-exporter/index.js https://gitlab.com/gitlab-org/gl-performance/raw/10-5/index.js
908

909
    mkdir sitespeed-results
910

911 912 913
    if [ -f .gitlab-urls.txt ]
    then
      sed -i -e 's@^@'"$CI_ENVIRONMENT_URL"'@' .gitlab-urls.txt
914
      docker run --shm-size=1g --rm -v "$(pwd)":/sitespeed.io sitespeedio/sitespeed.io:6.3.1 --plugins.add ./gitlab-exporter --outputFolder sitespeed-results .gitlab-urls.txt
915
    else
916
      docker run --shm-size=1g --rm -v "$(pwd)":/sitespeed.io sitespeedio/sitespeed.io:6.3.1 --plugins.add ./gitlab-exporter --outputFolder sitespeed-results "$CI_ENVIRONMENT_URL"
917
    fi
918

919 920
    mv sitespeed-results/data/performance.json performance.json
  }
921

922 923 924
  function persist_environment_url() {
      echo $CI_ENVIRONMENT_URL > environment_url.txt
  }
925 926 927

  function delete() {
    track="${1-stable}"
928
    name=$(deploy_name "$track")
929

930
    if [[ -n "$(helm ls -q "^$name$")" ]]; then
931
      helm delete --purge "$name"
932
    fi
933

934 935
    secret_name=$(application_secret_name "$track")
    kubectl delete secret --ignore-not-found -n "$KUBE_NAMESPACE" "$secret_name"
936 937 938 939
  }

before_script:
  - *auto_devops