version: 2.1

orbs:
  win: circleci/windows@2.4.0

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

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: [master, << pipeline.git.branch >>]
          steps:
            - run:
                name: Generate GitHub Issue
                command: go run cmd/issuegenerator/main.go ${TEST_RESULTS}
                when: on_fail

  install_fluentbit:
    steps:
      - run: |
          sudo chmod 0777 -R /opt
      - restore_cache: # restores saved cache if no changes are detected since last run
          keys:
            - "cimg-fluentbit-{{ arch }}-1.5.3"
      - run: |
          sudo ln -s /opt/td-agent-bit/bin/td-agent-bit /usr/local/bin/fluent-bit

          if [[ -f /opt/td-agent-bit/bin/td-agent-bit ]]; then
            exit 0
          fi

          wget https://packages.fluentbit.io/ubuntu/bionic/pool/main/t/td-agent-bit/td-agent-bit_1.5.3_amd64.deb
          sudo dpkg -i ./td-agent-bit*.deb
      - save_cache:
          key: cimg-fluentbit-{{ arch }}-1.5.3
          paths:
            - /opt/td-agent-bit

workflows:
  version: 2
  build-and-test:
    jobs:
      - windows-test:
          filters:
            tags:
              only: /^v[0-9]+\.[0-9]+\.[0-9]+.*/
      - setup-environment:
          filters:
            tags:
              only: /^v[0-9]+\.[0-9]+\.[0-9]+.*/
      - lint:
          requires:
            - 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:
          requires:
            - cross-compile
          filters:
            tags:
              only: /^v[0-9]+\.[0-9]+\.[0-9]+.*/
      - correctness:
          requires:
            - cross-compile
          filters:
            tags:
              only: /^v[0-9]+\.[0-9]+\.[0-9]+.*/
      - test:
          requires:
            - setup-environment
          filters:
            tags:
              only: /^v[0-9]+\.[0-9]+\.[0-9]+.*/
      - gosec:
          requires:
            - setup-environment
          filters:
            tags:
              only: /^v[0-9]+\.[0-9]+\.[0-9]+.*/
      - 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:
          requires:
            - cross-compile
            - correctness
            - lint
            - loadtest
            - test
            - gosec
            - coverage
            - windows-test
            - windows-msi
            - deb-package
            - rpm-package
      - publish-stable:
          requires:
            - cross-compile
            - correctness
            - lint
            - loadtest
            - test
            - gosec
            - coverage
            - windows-test
            - windows-msi
            - deb-package
            - rpm-package
          filters:
            branches:
              ignore: /.*/
            tags:
              only: /^v[0-9]+\.[0-9]+\.[0-9]+.*/
      - publish-dev:
          requires:
            - cross-compile
            - correctness
            - lint
            - loadtest
            - test
            - gosec
            - coverage
            - windows-test
            - windows-msi
            - deb-package
            - rpm-package
          filters:
            branches:
              only: master
            tags:
              ignore: /.*/
      - check-links:
          filters:
            tags:
              only: /^v[0-9]+\.[0-9]+\.[0-9]+.*/
      - 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]+.*/
  contrib-test:
    jobs:
      - setup-environment:
          filters:
            tags:
              only: /^v[0-9]+\.[0-9]+\.[0-9]+.*/
      - contribtest:
          requires:
            - setup-environment
          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

  lint:
    executor: golang
    steps:
      - attach_to_workspace
      - run:
          name: Lint
          command: make -j4 checklicense impi lint misspell

  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
      - persist_to_workspace:
          root: ~/
          paths: project/bin

  loadtest:
    executor: golang
    environment:
      TEST_RESULTS: testbed/tests/results/junit/results.xml
    parallelism: 6
    resource_class: medium+
    steps:
      - attach_to_workspace
      - install_fluentbit
      - 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
      - github_issue_generator

  correctness:
    executor: golang
    environment:
      TEST_RESULTS: testbed/tests/results/junit/results.xml
    parallelism: 4
    resource_class: medium+
    steps:
      - attach_to_workspace
      - run:
          name: Loadtest
          command: TEST_ARGS="-test.run=$(make -s testbed-list-correctness | circleci tests split|xargs echo|sed 's/ /|/g')" make testbed-correctness
      - github_issue_generator

  test:
    executor: golang
    environment:
      TEST_RESULTS: unit-test-results/junit/results.xml
    steps:
      - attach_to_workspace
      - run:
          name: Unit tests
          command: |
            mkdir -p unit-test-results/junit
            trap "go-junit-report  -set-exit-code < unit-test-results/go-unit-tests.out > unit-test-results/junit/results.xml" EXIT
            make test | tee unit-test-results/go-unit-tests.out
      - store_artifacts:
          path: unit-test-results
      - store_test_results:
          path: unit-test-results/junit
      - save_module_cache
      - github_issue_generator

  contribtest:
    executor: golang
    steps:
      - attach_to_workspace
      - run:
          name: Contrib repo tests
          command: |
            contrib_path=/tmp/opentelemetry-collector-contrib
            git clone https://github.com/open-telemetry/opentelemetry-collector-contrib.git $contrib_path
            make CONTRIB_PATH=$contrib_path check-contrib

      - save_module_cache

  gosec:
    executor: golang
    steps:
      - attach_to_workspace
      - run:
          name: Golang Security Checker
          command: make lint-gosec

  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 test-with-cover
      - run:
          name: Code coverage
          command: bash <(curl -s https://codecov.io/bash)

  publish-check:
    docker:
      - image: cimg/go:1.15
    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.15
    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.15
    steps:
      - attach_to_workspace
      - verify_dist_files_exist
      - setup_remote_docker
      - publish_docker_images:
          repo: opentelemetry-collector-dev
          tag: ${CIRCLE_SHA1}

  windows-test:
    executor:
      name: win/default
      shell: powershell.exe
    environment:
      GOPATH=~/go
    steps:
      - checkout
      - restore_module_cache
      - run:
          name: Upgrade golang
          command: |
            choco upgrade golang --version=1.15
            refreshenv
      - run:
          name: Unit tests
          command: go test ./...
      - save_module_cache
      - github_issue_generator

  check-links:
    docker:
      - image: circleci/node:9.10
    steps:
      - checkout
      - run:
          name: Check links in markdown docs
          command: |
            pushd $HOME
            npm install --save-dev markdown-link-check@3.8.1
            popd
            export NODE_PATH=$HOME/node_modules
            bash .circleci/check-links/check-links.sh

  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
