Auto-DevOps.gitlab-ci.yml 26.7 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.11.6
53
  HELM_VERSION: 2.12.2
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
  image:
120 121
    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
  image: docker:stable
  allow_failure: true
  services:
    - docker:stable-dind
  script:
    - setup_docker
    - dependency_scanning
  artifacts:
188 189
    reports:
      dependency_scanning: gl-dependency-scanning-report.json
190
  only:
191 192 193 194
    refs:
      - branches
    variables:
      - $GITLAB_FEATURES =~ /\bdependency_scanning\b/
195 196 197
  except:
    variables:
      - $DEPENDENCY_SCANNING_DISABLED
198

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

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

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

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

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

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

317 318 319
# 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.
320

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

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

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

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

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

.rollout: &rollout_template
  script:
    - check_kube_domain
    - install_dependencies
    - download_chart
    - ensure_namespace
398
    - initialize_tiller
399 400 401 402 403 404 405 406 407 408 409
    - 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]

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

426
.timed_rollout_template: &timed_rollout_template
427
  <<: *rollout_template
428 429
  when: delayed
  start_in: 5 minutes
430 431 432
  only:
    refs:
      - master
433
    kubernetes: active
434
    variables:
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 470
      - $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
471 472

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

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

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

.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
493 494
  # 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/')
495

496
  function registry_login() {
497 498 499 500 501
    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
502 503 504 505
  }

  function container_scanning() {
    registry_login
506

Luke Bennett's avatar
Luke Bennett committed
507
    docker run -d --name db arminc/clair-db:latest
508
    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
509 510
    apk add -U wget ca-certificates
    docker pull ${CI_APPLICATION_REPOSITORY}:${CI_APPLICATION_TAG}
511 512
    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
513 514
    chmod +x clair-scanner
    touch clair-whitelist.yml
515 516 517
    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
518
    ./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
519
  }
520

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

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

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

536 537 538 539 540 541 542 543
        # 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" \
544
                   --volume /var/run/docker.sock:/var/run/docker.sock \
545
                   "registry.gitlab.com/gitlab-org/security-products/sast:$SP_VERSION" /app/bin/run /code
546 547 548 549 550 551 552
        ;;
      *)
        echo "GitLab EE is required"
        ;;
    esac
  }

553 554 555 556 557 558 559 560 561 562 563 564 565 566
  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
  }

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

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

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

    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
  }

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

614
    env | sed -n "s/^K8S_SECRET_\(.*\)$/\1/p" > k8s_prefixed_variables
615

616 617 618 619 620 621 622 623
    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 -

    export APPLICATION_SECRET_CHECKSUM=$(cat k8s_prefixed_variables | sha256sum | cut -d ' ' -f 1)

    rm k8s_prefixed_variables
624 625
  }

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

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

    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"
658 659
    fi

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

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

668
    create_application_secret "$track"
669

670 671 672 673 674 675 676 677
    env_slug=$(echo ${CI_ENVIRONMENT_SLUG//-/_} | tr -s '[:lower:]' '[:upper:]')
    eval env_ADDITIONAL_HOSTS=\$${env_slug}_ADDITIONAL_HOSTS
    if [ -n "$env_ADDITIONAL_HOSTS" ]; then
      additional_hosts="{$env_ADDITIONAL_HOSTS}"
    elif [ -n "$ADDITIONAL_HOSTS" ]; then
      additional_hosts="{$ADDITIONAL_HOSTS}"
    fi

678
    if [[ -n "$DB_INITIALIZE" && -z "$(helm ls -q "^$name$")" ]]; then
679
      echo "Deploying first release with database initialization..."
680 681 682 683 684 685 686 687 688 689
      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" \
690
        --set application.secretName="$APPLICATION_SECRET_NAME" \
691
        --set application.secretChecksum="$APPLICATION_SECRET_CHECKSUM" \
692
        --set service.commonName="le.$AUTO_DEVOPS_DOMAIN" \
693
        --set service.url="$CI_ENVIRONMENT_URL" \
694
        --set service.additionalHosts="$additional_hosts" \
695 696 697 698 699 700 701 702 703 704 705
        --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/

706
      echo "Deploying second release..."
707 708 709 710 711 712 713 714
      helm upgrade --reuse-values \
        --wait \
        --set application.initializeCommand="" \
        --set application.migrateCommand="$DB_MIGRATE" \
        --namespace="$KUBE_NAMESPACE" \
        "$name" \
        chart/
    else
715
      echo "Deploying new release..."
716 717 718 719 720 721 722 723 724 725
      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" \
726
        --set application.secretName="$APPLICATION_SECRET_NAME" \
727
        --set application.secretChecksum="$APPLICATION_SECRET_CHECKSUM" \
728
        --set service.commonName="le.$AUTO_DEVOPS_DOMAIN" \
729
        --set service.url="$CI_ENVIRONMENT_URL" \
730
        --set service.additionalHosts="$additional_hosts" \
731 732 733 734 735 736 737 738 739 740 741
        --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
742 743

    kubectl rollout status -n "$KUBE_NAMESPACE" -w "deployment/$name"
744 745
  }

746 747 748
  function scale() {
    track="${1-stable}"
    percentage="${2-100}"
749
    name=$(deploy_name "$track")
750 751 752

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

753 754 755 756 757 758 759 760
    if [[ -n "$(helm ls -q "^$name$")" ]]; then
      helm upgrade --reuse-values \
        --wait \
        --set replicaCount="$replicas" \
        --namespace="$KUBE_NAMESPACE" \
        "$name" \
        chart/
    fi
761 762
  }

763 764
  function install_dependencies() {
    apk add -U openssl curl tar gzip bash ca-certificates git
765 766
    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
767 768
    apk add glibc-2.28-r0.apk
    rm glibc-2.28-r0.apk
769

770
    curl "https://kubernetes-helm.storage.googleapis.com/helm-v${HELM_VERSION}-linux-amd64.tar.gz" | tar zx
771
    mv linux-amd64/helm /usr/bin/
772
    mv linux-amd64/tiller /usr/bin/
773
    helm version --client
774 775
    tiller -version

776
    curl -L -o /usr/bin/kubectl "https://storage.googleapis.com/kubernetes-release/release/v${KUBERNETES_VERSION}/bin/linux/amd64/kubectl"
777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802
    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}
803
      auto_chart_name=${auto_chart_name%.tar.gz}
804 805 806 807 808 809
    else
      auto_chart="chart"
      auto_chart_name="chart"
    fi

    helm init --client-only
810
    helm repo add gitlab ${AUTO_DEVOPS_CHART_REPOSITORY:-https://charts.gitlab.io}
811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827
    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
828
      echo "In order to deploy or use Review Apps, AUTO_DEVOPS_DOMAIN variable must be set"
829
      echo "You can do it in Auto DevOps project settings or defining a variable at group or project level"
830
      echo "You can also manually add it in .gitlab-ci.yml"
831 832 833 834 835 836 837
      false
    else
      true
    fi
  }

  function build() {
838
    registry_login
839

840 841
    if [[ -f Dockerfile ]]; then
      echo "Building Dockerfile-based application..."
842 843 844 845 846 847 848 849 850 851
      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" .
852 853
    else
      echo "Building Heroku-based application using gliderlabs/herokuish docker image..."
854 855 856 857 858 859 860 861 862 863 864
      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
865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880
      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 ""
  }

881
  function initialize_tiller() {
882
    echo "Checking Tiller..."
883

884
    export HELM_HOST="localhost:44134"
885
    tiller -listen ${HELM_HOST} -alsologtostderr > /dev/null 2>&1 &
886
    echo "Tiller is listening on ${HELM_HOST}"
887

888 889 890 891 892 893 894 895
    if ! helm version --debug; then
      echo "Failed to init Tiller."
      return 1
    fi
    echo ""
  }

  function create_secret() {
896
    echo "Create secret..."
897 898 899
    if [[ "$CI_PROJECT_VISIBILITY" == "public" ]]; then
      return
    fi
900

901 902 903
    kubectl create secret -n "$KUBE_NAMESPACE" \
      docker-registry gitlab-registry \
      --docker-server="$CI_REGISTRY" \
904 905
      --docker-username="${CI_DEPLOY_USER:-$CI_REGISTRY_USER}" \
      --docker-password="${CI_DEPLOY_PASSWORD:-$CI_REGISTRY_PASSWORD}" \
906 907 908
      --docker-email="$GITLAB_USER_EMAIL" \
      -o yaml --dry-run | kubectl replace -n "$KUBE_NAMESPACE" --force -f -
  }
909

910 911 912 913 914 915 916 917
  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 .
  }

918 919
  function performance() {
    export CI_ENVIRONMENT_URL=$(cat environment_url.txt)
920

921
    mkdir gitlab-exporter
922
    wget -O gitlab-exporter/index.js https://gitlab.com/gitlab-org/gl-performance/raw/10-5/index.js
923

924
    mkdir sitespeed-results
925

926 927 928
    if [ -f .gitlab-urls.txt ]
    then
      sed -i -e 's@^@'"$CI_ENVIRONMENT_URL"'@' .gitlab-urls.txt
929
      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
930
    else
931
      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"
932
    fi
933

934 935
    mv sitespeed-results/data/performance.json performance.json
  }
936

937 938 939
  function persist_environment_url() {
      echo $CI_ENVIRONMENT_URL > environment_url.txt
  }
940 941 942

  function delete() {
    track="${1-stable}"
943
    name=$(deploy_name "$track")
944

945
    if [[ -n "$(helm ls -q "^$name$")" ]]; then
946
      helm delete --purge "$name"
947
    fi
948

949 950
    secret_name=$(application_secret_name "$track")
    kubectl delete secret --ignore-not-found -n "$KUBE_NAMESPACE" "$secret_name"
951 952 953 954
  }

before_script:
  - *auto_devops