# Using Contexts:
#   some jobs depend on secrets like API tokens to work correctly such as publishing to docker hub
#   or reporting issues to GitHub. All such tokens are stored in CircleCI contexts (https://circleci.com/docs/2.0/contexts).
#   
#   All tokens stored in a contexts are injected into a job as environment variables IF the pipeline that runs the job
#   explicitly enables the context for the job. 
#
#   Contexts are protected with security groups. Jobs that use contexts will not run for commits from people who are not
#   part of the approved security groups for the given context. This means that contributors who are not part of the
#   OpenTelemetry GitHub organisation will not be able to run jobs that depend on contexts. As a result, PR pipelines
#   should never depend on any contexts and never use any tokens/secrets.
#
#   This CI pipeline uses two contexts:
#      - github-release-and-issues-api-token
#        This context makes GITHUB_TOKEN available to jobs. Jobs can use the token to authenticate with the GitHub API.
#        We use this to report failures as issues back to the GitHub project.
#        Any member of the OpenTelemetry GitHub organisation can run jobs that require this context e.g, loadtest-with-github-reports.
#        
#      - dockerhub-token
#        This contexts makes DOCKER_HUB_USERNAME and DOCKER_HUB_PASSWORD environment variables available to the jobs.
#        This is used to publish docker images to Docker Hub.
#        Only project approvers and maintainers can run jobs that depend on this context such e.g, publish-stable.

version: 2.1

orbs:
  win: circleci/windows@2.4.0

executors:
  golang:
    docker:
      - image: cimg/go:1.16

commands:
  verify_dist_files_exist:
    parameters:
      files:
        type: string
        default: |
            bin/otelcol_darwin_amd64
            bin/otelcol_linux_arm64
            bin/otelcol_linux_amd64
            bin/otelcol_windows_amd64.exe
            dist/otel-collector-*arm64.rpm
            dist/otel-collector_*amd64.deb
            dist/otel-collector-*x86_64.rpm
            dist/otel-collector_*arm64.deb
            dist/otel-collector-*amd64.msi
    steps:
      - run:
          name: Check if files exist
          command: |
            files="<< parameters.files >>"
            for f in $files; do
              if [[ ! -f $f ]]
              then
                  echo "$f does not exist."
                  exit 1 
              fi
            done

  attach_to_workspace:
    steps:
      - attach_workspace:
          at: ~/

  save_module_cache:
    steps:
      - save_cache:
          key: cimg-go-pkg-mod-{{ arch }}-{{ checksum "go.sum" }}
          paths:
            - ~/go/pkg/mod

  restore_module_cache:
    steps:
      - run:
          name: create modules dir
          command: mkdir -p ~/go/pkg/mod
      - restore_cache: # restores saved cache if no changes are detected since last run
          keys:
            - cimg-go-pkg-mod-{{ arch }}-{{ checksum "go.sum" }}

  publish_docker_images:
    parameters:
      repo:
        type: string
      tag:
        type: string

    steps:
      - run:
          name: Build image
          command: |
            make docker-otelcol
            docker tag otelcol:latest otel/<< parameters.repo >>:<< parameters.tag >>
            docker tag otelcol:latest otel/<< parameters.repo >>:latest
      - run:
          name: Login to Docker Hub
          command: docker login -u $DOCKER_HUB_USERNAME -p $DOCKER_HUB_PASSWORD
      - run:
          name: Push image
          command: |
            docker push otel/<< parameters.repo >>:<< parameters.tag >>
            docker push otel/<< parameters.repo >>:latest

  github_issue_generator:
    steps:
      - when:
          condition:
            equal: [main, << pipeline.git.branch >>]
          steps:
            - run:
                name: Generate GitHub Issue
                command: go run cmd/issuegenerator/main.go ${TEST_RESULTS}
                when: on_fail

  run_loadtest:
    steps:
      - attach_to_workspace
      - run:
          name: Loadtest
          command: TEST_ARGS="-test.run=$(make -s testbed-list-loadtest | circleci tests split|xargs echo|sed 's/ /|/g')" make testbed-loadtest
      - store_artifacts:
          path: testbed/tests/results
      - store_test_results:
          path: testbed/tests/results/junit

workflows:
  version: 2
  build-and-test:
    jobs:
      - setup-environment:
          filters:
            tags:
              only: /^v[0-9]+\.[0-9]+\.[0-9]+.*/
      - cross-compile:
          requires:
            - setup-environment
          filters:
            tags:
              only: /^v[0-9]+\.[0-9]+\.[0-9]+.*/
      - loadtest-with-github-reports:
          context:
            - github-release-and-issues-api-token
          requires:
            - cross-compile
          filters:
            branches:
              only: main
            tags:
              only: /^v[0-9]+\.[0-9]+\.[0-9]+.*/
      - loadtest:
          requires:
            - cross-compile
          filters:
            branches:
              ignore: main
      - coverage:
          requires:
            - setup-environment
          filters:
            tags:
              only: /^v[0-9]+\.[0-9]+\.[0-9]+.*/
      - windows-msi:
          requires:
            - cross-compile
          filters:
            tags:
              only: /^v[0-9]+\.[0-9]+\.[0-9]+.*/
      - publish-check:
          name: publish-check-main
          requires:
            - cross-compile
            - loadtest-with-github-reports
            - coverage
            - windows-msi
            - deb-package
            - rpm-package
          filters:
            branches:
              only: main
      - publish-check:
          name: publish-check-pr
          requires:
            - cross-compile
            - loadtest
            - coverage
            - windows-msi
            - deb-package
            - rpm-package
          filters:
            branches:
              ignore: main
      - publish-stable:
          context:
            - github-release-and-issues-api-token
            - dockerhub-token
          requires:
            - cross-compile
            - loadtest
            - coverage
            - windows-msi
            - deb-package
            - rpm-package
          filters:
            branches:
              ignore: /.*/
            tags:
              only: /^v[0-9]+\.[0-9]+\.[0-9]+.*/
      - publish-dev:
          context:
            - dockerhub-token
          requires:
            - cross-compile
            - loadtest
            - coverage
            - windows-msi
            - deb-package
            - rpm-package
          filters:
            branches:
              only: main
            tags:
              ignore: /.*/
      - build-package:
          name: deb-package
          package_type: deb
          requires:
            - cross-compile
          filters:
            tags:
              only: /^v[0-9]+\.[0-9]+\.[0-9]+.*/
      - build-package:
          name: rpm-package
          package_type: rpm
          requires:
            - cross-compile
          filters:
            tags:
              only: /^v[0-9]+\.[0-9]+\.[0-9]+.*/

jobs:
  setup-environment:
    executor: golang
    steps:
      - checkout
      - persist_to_workspace:
          root: ~/
          paths: project
      - restore_module_cache
      - persist_to_workspace:
          root: ~/
          paths: go/pkg/mod
      - run:
          name: Install tools
          command: make install-tools
      - persist_to_workspace:
          root: ~/
          paths: go/bin

  cross-compile:
    executor: golang
    parallelism: 4
    steps:
      - attach_to_workspace
      - run:
          name: Build collector for all archs
          command: grep ^binaries-all-sys Makefile|fmt -w 1|tail -n +2|circleci tests split|xargs make
      - run:
          name: Log checksums to console
          command: shasum -a 256 bin/*
      - persist_to_workspace:
          root: ~/
          paths: project/bin

  loadtest-with-github-reports:
    executor: golang
    environment:
      TEST_RESULTS: testbed/tests/results/junit/results.xml
    parallelism: 6
    resource_class: medium+
    steps:
      - run_loadtest
      - github_issue_generator

  loadtest:
    executor: golang
    environment:
      TEST_RESULTS: testbed/tests/results/junit/results.xml
    parallelism: 6
    resource_class: medium+
    steps:
      - run_loadtest

  coverage:
    executor: golang
    steps:
      - attach_to_workspace
      - run:
          name: Install packages.
          command: sudo apt update && sudo apt-get install bzr time
      - run:
          name: Coverage tests
          command: make gotest-with-cover
# DISABLE CODECOV UNTIL THE SCRIPT IS AUDITED AND WE ARE CERTAIN IT IS OK TO TO EXECUTE IT.
#      - run:
#          name: Code coverage
#          command: bash <(curl -s https://codecov.io/bash)

  publish-check:
    docker:
      - image: cimg/go:1.16
    steps:
      - attach_to_workspace
      - setup_remote_docker
      - verify_dist_files_exist
      - run:
          name: Check passed
          command: echo "publish check passed meaning release CI jobs should work as expected"
          when: on_success
      - run:
          name: Check failed
          command: echo "publish check failed. This means release CI jobs will likely fail as well"
          when: on_fail

  publish-stable:
    docker:
      - image: cimg/go:1.16
    steps:
      - attach_to_workspace
      - verify_dist_files_exist
      - setup_remote_docker
      - publish_docker_images:
          repo: opentelemetry-collector
          tag: ${CIRCLE_TAG:1}
      - run:
          name: Prepare release artifacts
          command: |
            cp bin/* dist/
      - run:
          name: Calculate checksums
          command: cd dist && shasum -a 256 * > checksums.txt
      - run:
          name: Create Github release and upload artifacts
          command: ghr -t $GITHUB_TOKEN -u $CIRCLE_PROJECT_USERNAME -r $CIRCLE_PROJECT_REPONAME --replace $CIRCLE_TAG dist/

  publish-dev:
    docker:
      - image: cimg/go:1.16
    steps:
      - attach_to_workspace
      - verify_dist_files_exist
      - setup_remote_docker
      - publish_docker_images:
          repo: opentelemetry-collector-dev
          tag: ${CIRCLE_SHA1}

  windows-msi:
    executor:
      name: win/default
      shell: powershell.exe
    steps:
      - attach_to_workspace
      - run:
          command: mkdir -p dist
      - run:
          name: Install Wix Toolset
          command: .\internal\buildscripts\packaging\msi\make.ps1 Install-Tools
      - run:
          name: Build MSI
          command: |
            $Version = if ($env:CIRCLE_TAG -match '^v(\d+\.\d+\.\d+)') { $Matches[1] } else { "0.0.1" }
            .\internal\buildscripts\packaging\msi\make.ps1 New-MSI -Version $Version
      - run:
          name: Validate MSI
          command: .\internal\buildscripts\packaging\msi\make.ps1 Confirm-MSI
      - persist_to_workspace:
          root: ~/
          paths: project/dist/*.msi

  build-package:
    machine:
      image: ubuntu-1604:202007-01
    parameters:
      package_type:
        type: enum
        enum: ["deb", "rpm"]
    steps:
      - checkout
      - attach_to_workspace
      - run:
          name: Install fpm and dependencies
          command: |
            sudo apt-get update
            sudo apt-get install -y ruby ruby-dev rubygems build-essential rpm
            gem install --no-document fpm -v 1.11.0
      - run:
          name: Build << parameters.package_type >> amd64 package
          command: ./internal/buildscripts/packaging/fpm/<< parameters.package_type >>/build.sh "${CIRCLE_TAG:-}" "amd64" "./dist/"
      - run:
          name: Build << parameters.package_type >> arm64 package
          command: ./internal/buildscripts/packaging/fpm/<< parameters.package_type >>/build.sh "${CIRCLE_TAG:-}" "arm64" "./dist/"
      - run:
          name: Test << parameters.package_type >> package
          command: |
            if [[ "<< parameters.package_type >>" = "deb" ]]; then
                ./internal/buildscripts/packaging/fpm/test.sh dist/otel-collector*amd64.deb
            else
                ./internal/buildscripts/packaging/fpm/test.sh dist/otel-collector*x86_64.rpm
            fi
      - persist_to_workspace:
          root: ~/
          paths: project/dist
