diff --git a/.circleci/.gitattributes b/.circleci/.gitattributes new file mode 100644 index 000000000..f7c6b31eb --- /dev/null +++ b/.circleci/.gitattributes @@ -0,0 +1 @@ +/config.yml linguist-generated diff --git a/.circleci/.gitignore b/.circleci/.gitignore new file mode 100644 index 000000000..3018b3a68 --- /dev/null +++ b/.circleci/.gitignore @@ -0,0 +1 @@ +.tmp/ diff --git a/.circleci/Makefile b/.circleci/Makefile new file mode 100644 index 000000000..3852d19f7 --- /dev/null +++ b/.circleci/Makefile @@ -0,0 +1,80 @@ +# Set SHELL to 'strict mode' without using .SHELLFLAGS for max compatibility. +# See https://fieldnotes.tech/how-to-shell-for-compatible-makefiles/ +SHELL := /usr/bin/env bash -euo pipefail -c + +CIRCLECI := circleci --skip-update-check + +# Set up some documentation/help message variables. +# We do not attempt to install the CircleCI CLI from this Makefile. +CCI_INSTALL_LINK := https://circleci.com/docs/2.0/local-cli/\#installation +CCI_INSTALL_MSG := Please install CircleCI CLI. See $(CCI_INSTALL_LINK) +CCI_VERSION := $(shell $(CIRCLECI) version 2> /dev/null) +ifeq ($(CCI_VERSION),) +# Attempting to use the CLI fails with installation instructions. +CIRCLECI := echo '$(CCI_INSTALL_MSG)'; exit 1; \# +endif + +SOURCE_DIR := config +SOURCE_YML := $(shell [ ! -d $(SOURCE_DIR) ] || find $(SOURCE_DIR) -name '*.yml') +CONFIG_SOURCE := Makefile $(SOURCE_YML) | $(SOURCE_DIR) +OUT := config.yml +TMP := .tmp/config-processed +CONFIG_PACKED := .tmp/config-packed + +default: help + +help: + @echo "Usage:" + @echo " make ci-config: recompile config.yml from $(SOURCE_DIR)/" + @echo " make ci-verify: verify that config.yml is a true mapping from $(SOURCE_DIR)/" + @echo + @echo "Diagnostics:" + @[ -z "$(CCI_VERSION)" ] || echo " circleci-cli version $(CCI_VERSION)" + @[ -n "$(CCI_VERSION)" ] || echo " $(CCI_INSTALL_MSG)" + +$(SOURCE_DIR): + @echo No source directory $(SOURCE_DIR) found.; exit 1 + +# Make sure our .tmp dir exists. +$(shell [ -d .tmp ] || mkdir .tmp) + +.PHONY: ci-config +ci-config: $(OUT) + +.PHONY: ci-verify +ci-verify: config-up-to-date + @$(CIRCLECI) config validate $(OUT) + +define GENERATED_FILE_HEADER +### *** +### WARNING: DO NOT manually EDIT or MERGE this file, it is generated by 'make ci-config'. +### INSTEAD: Edit or merge the source in $(SOURCE_DIR)/ then run 'make ci-config'. +### *** +endef +export GENERATED_FILE_HEADER + +# GEN_CONFIG writes the config to a temporary file. If the whole process succeeds, +# it then moves that file to $@. This makes is an atomic operation, so if it fails +# make doesn't consider a half-baked file up to date. +define GEN_CONFIG + @$(CIRCLECI) config pack $(SOURCE_DIR) > $(CONFIG_PACKED) + @echo "$$GENERATED_FILE_HEADER" > $@.tmp || { rm -f $@; exit 1; } + @$(CIRCLECI) config process $(CONFIG_PACKED) >> $@.tmp || { rm -f $@.tmp; exit 1; } + @mv -f $@.tmp $@ +endef + +$(OUT): $(CONFIG_SOURCE) + $(GEN_CONFIG) + @echo "$@ updated" + +$(TMP): $(CONFIG_SOURCE) + $(GEN_CONFIG) + +.PHONY: config-up-to-date +config-up-to-date: $(TMP) # Note this must not depend on $(OUT)! + @if diff config.yml $<; then \ + echo "Generated $(OUT) is up to date!"; \ + else \ + echo "Generated $(OUT) is out of date, run make $(CONFIG) to update."; \ + exit 1; \ + fi diff --git a/.circleci/README.md b/.circleci/README.md new file mode 100644 index 000000000..c21070097 --- /dev/null +++ b/.circleci/README.md @@ -0,0 +1,3 @@ +# How to use CircleCI multi-file config + +Refer to https://github.com/hashicorp/vault/blob/master/.circleci/README.md . diff --git a/.circleci/config.yml b/.circleci/config.yml index 42e398922..73ae04eed 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,371 +1,936 @@ -version: 2.1 +### *** +### WARNING: DO NOT manually EDIT or MERGE this file, it is generated by 'make ci-config'. +### INSTEAD: Edit or merge the source in config/ then run 'make ci-config'. +### *** +version: 2 +jobs: + test-nomad: + machine: + image: ubuntu-1604:201903-01 + working_directory: ~/go/src/github.com/hashicorp/nomad + environment: + - GIT_PAGER: cat + - GOLANG_VERSION: 1.12.13 + - GOMAXPROCS: 1 + - GOPATH: /home/circleci/go + - GOTESTSUM_JUNITFILE: /tmp/test-reports/results.xml + - NOMAD_SLOW_TEST: 1 + - PAGER: cat + - GOTEST_PKGS: ./nomad/... + - GOTEST_PKGS_EXCLUDE: '' + - GOTESTARCH: amd64 + steps: + - checkout + - run: + command: | + set -x + echo installing golang ${GOLANG_VERSION} + sudo rm -rf /usr/local/go + wget -O /tmp/golang.tar.gz https://dl.google.com/go/go${GOLANG_VERSION}.linux-amd64.tar.gz + sudo tar -C /usr/local -xzf /tmp/golang.tar.gz + rm -rf /tmp/golang.tar.gz + name: install golang + - run: + command: | + sudo rm -rf /usr/bin/protoc + sudo ./scripts/vagrant-linux-priv-protoc.sh + name: install protoc + - run: + command: | + wget -q -O /tmp/consul.zip https://releases.hashicorp.com/consul/1.6.1/consul_1.6.1_linux_amd64.zip + sudo unzip -d /usr/local/bin /tmp/consul.zip + rm -rf /tmp/consul* + name: Install Consul 1.6.1 + - run: + command: | + wget -q -O /tmp/vault.zip https://releases.hashicorp.com/vault/1.2.3/vault_1.2.3_linux_amd64.zip + sudo unzip -d /usr/local/bin /tmp/vault.zip + rm -rf /tmp/vault* + name: Install Vault 1.2.3 + - run: + command: | + if [ ! -z $GOTESTARCH ] && [ $GOTESTARCH == "386" ]; then + sudo apt-get update + sudo apt-get install -y gcc-multilib + else + echo "Skipping 32bit lib installation while building for not 386" + fi + name: Install 32bit gcc libs + - run: + command: PATH="$GOPATH/bin:/usr/local/go/bin:$PATH" make bootstrap + - run: + command: | + if [ -z $GOTEST_PKGS_EXCLUDE ]; + then + unset GOTEST_PKGS_EXCLUDE + else + unset GOTEST_PKGS + fi -references: - common_envs: &COMMON_ENVS - GOMAXPROCS: 1 - NOMAD_SLOW_TEST: 1 - GOTESTSUM_JUNITFILE: /tmp/test-reports/results.xml - ignore_for_ui_branches: &IGNORE_FOR_UI_BRANCHES - filters: - branches: - ignore: /^.-ui\b.*/ + if [ ! -z $GOTESTARCH ]; then + export GOARCH="$GOTESTARCH"; + fi + mkdir -p /tmp/test-reports + sudo -E PATH="$GOPATH/bin:/usr/local/go/bin:$PATH" make generate-structs + sudo -E PATH="$GOPATH/bin:/usr/local/go/bin:$PATH" make test-nomad + name: Running Nomad Tests + no_output_timeout: 20m + - store_test_results: + path: /tmp/test-reports + - store_artifacts: + path: /tmp/test-reports + test-api: + machine: + image: ubuntu-1604:201903-01 + working_directory: ~/go/src/github.com/hashicorp/nomad + environment: + - GIT_PAGER: cat + - GOLANG_VERSION: 1.12.13 + - GOMAXPROCS: 1 + - GOPATH: /home/circleci/go + - GOTESTSUM_JUNITFILE: /tmp/test-reports/results.xml + - NOMAD_SLOW_TEST: 1 + - PAGER: cat + - GOTEST_PKGS: ./api/... + - GOTEST_PKGS_EXCLUDE: '' + - GOTESTARCH: amd64 + steps: + - checkout + - run: + command: | + set -x + echo installing golang ${GOLANG_VERSION} + sudo rm -rf /usr/local/go + wget -O /tmp/golang.tar.gz https://dl.google.com/go/go${GOLANG_VERSION}.linux-amd64.tar.gz + sudo tar -C /usr/local -xzf /tmp/golang.tar.gz + rm -rf /tmp/golang.tar.gz + name: install golang + - run: + command: | + sudo rm -rf /usr/bin/protoc + sudo ./scripts/vagrant-linux-priv-protoc.sh + name: install protoc + - run: + command: | + wget -q -O /tmp/consul.zip https://releases.hashicorp.com/consul/1.6.1/consul_1.6.1_linux_amd64.zip + sudo unzip -d /usr/local/bin /tmp/consul.zip + rm -rf /tmp/consul* + name: Install Consul 1.6.1 + - run: + command: | + wget -q -O /tmp/vault.zip https://releases.hashicorp.com/vault/1.2.3/vault_1.2.3_linux_amd64.zip + sudo unzip -d /usr/local/bin /tmp/vault.zip + rm -rf /tmp/vault* + name: Install Vault 1.2.3 + - run: + command: | + if [ ! -z $GOTESTARCH ] && [ $GOTESTARCH == "386" ]; then + sudo apt-get update + sudo apt-get install -y gcc-multilib + else + echo "Skipping 32bit lib installation while building for not 386" + fi + name: Install 32bit gcc libs + - run: + command: PATH="$GOPATH/bin:/usr/local/go/bin:$PATH" make bootstrap + - run: + command: | + if [ -z $GOTEST_PKGS_EXCLUDE ]; + then + unset GOTEST_PKGS_EXCLUDE + else + unset GOTEST_PKGS + fi + if [ ! -z $GOTESTARCH ]; then + export GOARCH="$GOTESTARCH"; + fi + + mkdir -p /tmp/test-reports + sudo -E PATH="$GOPATH/bin:/usr/local/go/bin:$PATH" make generate-structs + sudo -E PATH="$GOPATH/bin:/usr/local/go/bin:$PATH" make test-nomad + name: Running Nomad Tests + no_output_timeout: 20m + - store_test_results: + path: /tmp/test-reports + - store_artifacts: + path: /tmp/test-reports + test-exec: + machine: + image: ubuntu-1604:201903-01 + working_directory: ~/go/src/github.com/hashicorp/nomad + environment: + - GIT_PAGER: cat + - GOLANG_VERSION: 1.12.13 + - GOMAXPROCS: 1 + - GOPATH: /home/circleci/go + - GOTESTSUM_JUNITFILE: /tmp/test-reports/results.xml + - NOMAD_SLOW_TEST: 1 + - PAGER: cat + - GOTEST_PKGS: ./drivers/exec + - GOTEST_PKGS_EXCLUDE: '' + - GOTESTARCH: amd64 + steps: + - checkout + - run: + command: | + set -x + echo installing golang ${GOLANG_VERSION} + sudo rm -rf /usr/local/go + wget -O /tmp/golang.tar.gz https://dl.google.com/go/go${GOLANG_VERSION}.linux-amd64.tar.gz + sudo tar -C /usr/local -xzf /tmp/golang.tar.gz + rm -rf /tmp/golang.tar.gz + name: install golang + - run: + command: | + sudo rm -rf /usr/bin/protoc + sudo ./scripts/vagrant-linux-priv-protoc.sh + name: install protoc + - run: + command: | + wget -q -O /tmp/consul.zip https://releases.hashicorp.com/consul/1.6.1/consul_1.6.1_linux_amd64.zip + sudo unzip -d /usr/local/bin /tmp/consul.zip + rm -rf /tmp/consul* + name: Install Consul 1.6.1 + - run: + command: | + wget -q -O /tmp/vault.zip https://releases.hashicorp.com/vault/1.2.3/vault_1.2.3_linux_amd64.zip + sudo unzip -d /usr/local/bin /tmp/vault.zip + rm -rf /tmp/vault* + name: Install Vault 1.2.3 + - run: + command: | + if [ ! -z $GOTESTARCH ] && [ $GOTESTARCH == "386" ]; then + sudo apt-get update + sudo apt-get install -y gcc-multilib + else + echo "Skipping 32bit lib installation while building for not 386" + fi + name: Install 32bit gcc libs + - run: + command: PATH="$GOPATH/bin:/usr/local/go/bin:$PATH" make bootstrap + - run: + command: | + if [ -z $GOTEST_PKGS_EXCLUDE ]; + then + unset GOTEST_PKGS_EXCLUDE + else + unset GOTEST_PKGS + fi + + if [ ! -z $GOTESTARCH ]; then + export GOARCH="$GOTESTARCH"; + fi + + mkdir -p /tmp/test-reports + sudo -E PATH="$GOPATH/bin:/usr/local/go/bin:$PATH" make generate-structs + sudo -E PATH="$GOPATH/bin:/usr/local/go/bin:$PATH" make test-nomad + name: Running Nomad Tests + no_output_timeout: 20m + - store_test_results: + path: /tmp/test-reports + - store_artifacts: + path: /tmp/test-reports + test-client: + machine: + image: ubuntu-1604:201903-01 + working_directory: ~/go/src/github.com/hashicorp/nomad + environment: + - GIT_PAGER: cat + - GOLANG_VERSION: 1.12.13 + - GOMAXPROCS: 1 + - GOPATH: /home/circleci/go + - GOTESTSUM_JUNITFILE: /tmp/test-reports/results.xml + - NOMAD_SLOW_TEST: 1 + - PAGER: cat + - GOTEST_PKGS: ./client/... + - GOTEST_PKGS_EXCLUDE: '' + - GOTESTARCH: amd64 + steps: + - checkout + - run: + command: | + set -x + echo installing golang ${GOLANG_VERSION} + sudo rm -rf /usr/local/go + wget -O /tmp/golang.tar.gz https://dl.google.com/go/go${GOLANG_VERSION}.linux-amd64.tar.gz + sudo tar -C /usr/local -xzf /tmp/golang.tar.gz + rm -rf /tmp/golang.tar.gz + name: install golang + - run: + command: | + sudo rm -rf /usr/bin/protoc + sudo ./scripts/vagrant-linux-priv-protoc.sh + name: install protoc + - run: + command: | + wget -q -O /tmp/consul.zip https://releases.hashicorp.com/consul/1.6.1/consul_1.6.1_linux_amd64.zip + sudo unzip -d /usr/local/bin /tmp/consul.zip + rm -rf /tmp/consul* + name: Install Consul 1.6.1 + - run: + command: | + wget -q -O /tmp/vault.zip https://releases.hashicorp.com/vault/1.2.3/vault_1.2.3_linux_amd64.zip + sudo unzip -d /usr/local/bin /tmp/vault.zip + rm -rf /tmp/vault* + name: Install Vault 1.2.3 + - run: + command: | + if [ ! -z $GOTESTARCH ] && [ $GOTESTARCH == "386" ]; then + sudo apt-get update + sudo apt-get install -y gcc-multilib + else + echo "Skipping 32bit lib installation while building for not 386" + fi + name: Install 32bit gcc libs + - run: + command: PATH="$GOPATH/bin:/usr/local/go/bin:$PATH" make bootstrap + - run: + command: | + if [ -z $GOTEST_PKGS_EXCLUDE ]; + then + unset GOTEST_PKGS_EXCLUDE + else + unset GOTEST_PKGS + fi + + if [ ! -z $GOTESTARCH ]; then + export GOARCH="$GOTESTARCH"; + fi + + mkdir -p /tmp/test-reports + sudo -E PATH="$GOPATH/bin:/usr/local/go/bin:$PATH" make generate-structs + sudo -E PATH="$GOPATH/bin:/usr/local/go/bin:$PATH" make test-nomad + name: Running Nomad Tests + no_output_timeout: 20m + - store_test_results: + path: /tmp/test-reports + - store_artifacts: + path: /tmp/test-reports + test-shared-exec: + machine: + image: ubuntu-1604:201903-01 + working_directory: ~/go/src/github.com/hashicorp/nomad + environment: + - GIT_PAGER: cat + - GOLANG_VERSION: 1.12.13 + - GOMAXPROCS: 1 + - GOPATH: /home/circleci/go + - GOTESTSUM_JUNITFILE: /tmp/test-reports/results.xml + - NOMAD_SLOW_TEST: 1 + - PAGER: cat + - GOTEST_PKGS: ./drivers/shared/executor + - GOTEST_PKGS_EXCLUDE: '' + - GOTESTARCH: amd64 + steps: + - checkout + - run: + command: | + set -x + echo installing golang ${GOLANG_VERSION} + sudo rm -rf /usr/local/go + wget -O /tmp/golang.tar.gz https://dl.google.com/go/go${GOLANG_VERSION}.linux-amd64.tar.gz + sudo tar -C /usr/local -xzf /tmp/golang.tar.gz + rm -rf /tmp/golang.tar.gz + name: install golang + - run: + command: | + sudo rm -rf /usr/bin/protoc + sudo ./scripts/vagrant-linux-priv-protoc.sh + name: install protoc + - run: + command: | + wget -q -O /tmp/consul.zip https://releases.hashicorp.com/consul/1.6.1/consul_1.6.1_linux_amd64.zip + sudo unzip -d /usr/local/bin /tmp/consul.zip + rm -rf /tmp/consul* + name: Install Consul 1.6.1 + - run: + command: | + wget -q -O /tmp/vault.zip https://releases.hashicorp.com/vault/1.2.3/vault_1.2.3_linux_amd64.zip + sudo unzip -d /usr/local/bin /tmp/vault.zip + rm -rf /tmp/vault* + name: Install Vault 1.2.3 + - run: + command: | + if [ ! -z $GOTESTARCH ] && [ $GOTESTARCH == "386" ]; then + sudo apt-get update + sudo apt-get install -y gcc-multilib + else + echo "Skipping 32bit lib installation while building for not 386" + fi + name: Install 32bit gcc libs + - run: + command: PATH="$GOPATH/bin:/usr/local/go/bin:$PATH" make bootstrap + - run: + command: | + if [ -z $GOTEST_PKGS_EXCLUDE ]; + then + unset GOTEST_PKGS_EXCLUDE + else + unset GOTEST_PKGS + fi + + if [ ! -z $GOTESTARCH ]; then + export GOARCH="$GOTESTARCH"; + fi + + mkdir -p /tmp/test-reports + sudo -E PATH="$GOPATH/bin:/usr/local/go/bin:$PATH" make generate-structs + sudo -E PATH="$GOPATH/bin:/usr/local/go/bin:$PATH" make test-nomad + name: Running Nomad Tests + no_output_timeout: 20m + - store_test_results: + path: /tmp/test-reports + - store_artifacts: + path: /tmp/test-reports + test-ui: + docker: + - environment: + JOBS: 2 + image: circleci/node:10-browsers + steps: + - checkout + - restore_cache: + keys: + - v1-deps-{{ checksum "ui/yarn.lock" }} + - v1-deps- + - run: + command: cd ui && yarn install + name: yarn install + - save_cache: + key: v1-deps-{{ checksum "ui/yarn.lock" }} + paths: + - ./ui/node_modules + - run: + command: cd ui && yarn run lint:js + name: lint:js + - run: + command: cd ui && yarn run lint:hbs + name: lint:hbs + - run: + command: cd ui && yarn test + name: Ember tests + lint-go: + docker: + - image: golang:1.12.13 + working_directory: /go/src/github.com/hashicorp/nomad + steps: + - checkout + - run: + command: apt-get update; apt-get install -y shellcheck sudo unzip + - run: + command: | + sudo rm -rf /usr/bin/protoc + sudo ./scripts/vagrant-linux-priv-protoc.sh + name: install protoc + - run: + command: "CCI_VERSION=\"0.1.5879\"\nCCI_SHA256=\"f178ea62c781aec06267017404f87983c87f171fd0e66ef3737916246ae66dd6\"\n\nURL=\"https://github.com/CircleCI-Public/circleci-cli/releases/download/v${CCI_VERSION}/circleci-cli_${CCI_VERSION}_linux_amd64.tar.gz\"\n\nmkdir -p /tmp/circleci-cli/\ncurl --fail --show-error --location \\\n -o /tmp/circleci-cli/cli.tar.gz \"${URL}\"\n\necho \"$CCI_SHA256 /tmp/circleci-cli/cli.tar.gz\" | sha256sum -c\n\ntar -xz --strip-components=1 \\\n -C /tmp/circleci-cli \\\n -f /tmp/circleci-cli/cli.tar.gz \\\n \"circleci-cli_${CCI_VERSION}_linux_amd64/circleci\" \n\nsudo cp /tmp/circleci-cli/circleci /usr/bin/circleci-local-cli\n\ncircleci-local-cli version\n" + name: Install CircleCI CLI 0.1.5879 + - run: + command: make deps lint-deps + - run: + command: make check + - run: + command: make checkscripts + - run: + command: make -C .circleci CIRCLECI="circleci-local-cli --skip-update-check" ci-verify + name: check .circleci/config.yml is up-to-date + environment: + - GIT_PAGER: cat + - GOMAXPROCS: 1 + - GOPATH: /go + - GOTESTSUM_JUNITFILE: /tmp/test-reports/results.xml + - NOMAD_SLOW_TEST: 1 + - PAGER: cat + build-website: + docker: + - image: hashicorp/middleman-hashicorp:0.3.35 + steps: + - checkout: + path: ~/project + - restore_cache: + key: static-site-gems-v1-{{ checksum "Gemfile.lock" }} + - run: + command: bundle check || bundle install --path vendor/bundle --retry=3 + name: install gems + - save_cache: + key: static-site-gems-v1-{{ checksum "Gemfile.lock" }} + paths: + - ~/project/website/vendor/bundle + - run: + command: bundle exec middleman build + name: middleman build + - run: + command: ./scripts/deploy.sh + name: website deploy + working_directory: ~/project/website + test-other: + machine: + image: ubuntu-1604:201903-01 + working_directory: ~/go/src/github.com/hashicorp/nomad + environment: + - GIT_PAGER: cat + - GOLANG_VERSION: 1.12.13 + - GOMAXPROCS: 1 + - GOPATH: /home/circleci/go + - GOTESTSUM_JUNITFILE: /tmp/test-reports/results.xml + - NOMAD_SLOW_TEST: 1 + - PAGER: cat + - GOTEST_PKGS: '' + - GOTEST_PKGS_EXCLUDE: ./api|./client|./drivers/docker|./drivers/exec|./drivers/rkt|./drivers/shared/executor|./nomad|./devices + - GOTESTARCH: amd64 + steps: + - checkout + - run: + command: | + set -x + echo installing golang ${GOLANG_VERSION} + sudo rm -rf /usr/local/go + wget -O /tmp/golang.tar.gz https://dl.google.com/go/go${GOLANG_VERSION}.linux-amd64.tar.gz + sudo tar -C /usr/local -xzf /tmp/golang.tar.gz + rm -rf /tmp/golang.tar.gz + name: install golang + - run: + command: | + sudo rm -rf /usr/bin/protoc + sudo ./scripts/vagrant-linux-priv-protoc.sh + name: install protoc + - run: + command: | + wget -q -O /tmp/consul.zip https://releases.hashicorp.com/consul/1.6.1/consul_1.6.1_linux_amd64.zip + sudo unzip -d /usr/local/bin /tmp/consul.zip + rm -rf /tmp/consul* + name: Install Consul 1.6.1 + - run: + command: | + wget -q -O /tmp/vault.zip https://releases.hashicorp.com/vault/1.2.3/vault_1.2.3_linux_amd64.zip + sudo unzip -d /usr/local/bin /tmp/vault.zip + rm -rf /tmp/vault* + name: Install Vault 1.2.3 + - run: + command: | + if [ ! -z $GOTESTARCH ] && [ $GOTESTARCH == "386" ]; then + sudo apt-get update + sudo apt-get install -y gcc-multilib + else + echo "Skipping 32bit lib installation while building for not 386" + fi + name: Install 32bit gcc libs + - run: + command: PATH="$GOPATH/bin:/usr/local/go/bin:$PATH" make bootstrap + - run: + command: | + if [ -z $GOTEST_PKGS_EXCLUDE ]; + then + unset GOTEST_PKGS_EXCLUDE + else + unset GOTEST_PKGS + fi + + if [ ! -z $GOTESTARCH ]; then + export GOARCH="$GOTESTARCH"; + fi + + mkdir -p /tmp/test-reports + sudo -E PATH="$GOPATH/bin:/usr/local/go/bin:$PATH" make generate-structs + sudo -E PATH="$GOPATH/bin:/usr/local/go/bin:$PATH" make test-nomad + name: Running Nomad Tests + no_output_timeout: 20m + - store_test_results: + path: /tmp/test-reports + - store_artifacts: + path: /tmp/test-reports + test-devices: + docker: + - image: golang:1.12.13 + working_directory: /go/src/github.com/hashicorp/nomad + environment: + - GIT_PAGER: cat + - GOMAXPROCS: 1 + - GOPATH: /go + - GOTESTSUM_JUNITFILE: /tmp/test-reports/results.xml + - NOMAD_SLOW_TEST: 1 + - PAGER: cat + - GOTEST_PKGS: ./devices/... + - GOTEST_PKGS_EXCLUDE: '' + - GOTESTARCH: amd64 + steps: + - checkout + - run: + command: apt-get update; apt-get install -y shellcheck sudo unzip + - run: + command: make deps + - run: + command: | + sudo rm -rf /usr/bin/protoc + sudo ./scripts/vagrant-linux-priv-protoc.sh + name: install protoc + - run: + command: | + wget -q -O /tmp/consul.zip https://releases.hashicorp.com/consul/1.6.1/consul_1.6.1_linux_amd64.zip + sudo unzip -d /usr/local/bin /tmp/consul.zip + rm -rf /tmp/consul* + name: Install Consul 1.6.1 + - run: + command: | + wget -q -O /tmp/vault.zip https://releases.hashicorp.com/vault/1.2.3/vault_1.2.3_linux_amd64.zip + sudo unzip -d /usr/local/bin /tmp/vault.zip + rm -rf /tmp/vault* + name: Install Vault 1.2.3 + - run: + command: | + if [ -z $GOTEST_PKGS_EXCLUDE ]; + then + unset GOTEST_PKGS_EXCLUDE + else + unset GOTEST_PKGS + fi + + if [ ! -z $GOTESTARCH ]; then + export GOARCH="$GOTESTARCH"; + fi + + mkdir -p /tmp/test-reports + sudo -E PATH="$GOPATH/bin:/usr/local/go/bin:$PATH" make generate-structs + sudo -E PATH="$GOPATH/bin:/usr/local/go/bin:$PATH" make test-nomad + name: Running Nomad Tests + no_output_timeout: 20m + - store_test_results: + path: /tmp/test-reports + - store_artifacts: + path: /tmp/test-reports + test-website: + machine: + image: ubuntu-1604:201903-01 + working_directory: ~/go/src/github.com/hashicorp/nomad + steps: + - checkout + - run: + command: make test-website + environment: + - GIT_PAGER: cat + - GOLANG_VERSION: 1.12.13 + - GOMAXPROCS: 1 + - GOPATH: /home/circleci/go + - GOTESTSUM_JUNITFILE: /tmp/test-reports/results.xml + - NOMAD_SLOW_TEST: 1 + - PAGER: cat + test-docker: + machine: + image: circleci/classic:201808-01 + working_directory: ~/go/src/github.com/hashicorp/nomad + environment: + - GIT_PAGER: cat + - GOLANG_VERSION: 1.12.13 + - GOMAXPROCS: 1 + - GOPATH: /home/circleci/go + - GOTESTSUM_JUNITFILE: /tmp/test-reports/results.xml + - NOMAD_SLOW_TEST: 1 + - PAGER: cat + - GOTEST_PKGS: ./drivers/docker + - GOTEST_PKGS_EXCLUDE: '' + - GOTESTARCH: amd64 + steps: + - checkout + - run: + command: | + set -x + echo installing golang ${GOLANG_VERSION} + sudo rm -rf /usr/local/go + wget -O /tmp/golang.tar.gz https://dl.google.com/go/go${GOLANG_VERSION}.linux-amd64.tar.gz + sudo tar -C /usr/local -xzf /tmp/golang.tar.gz + rm -rf /tmp/golang.tar.gz + name: install golang + - run: + command: | + sudo rm -rf /usr/bin/protoc + sudo ./scripts/vagrant-linux-priv-protoc.sh + name: install protoc + - run: + command: | + wget -q -O /tmp/consul.zip https://releases.hashicorp.com/consul/1.6.1/consul_1.6.1_linux_amd64.zip + sudo unzip -d /usr/local/bin /tmp/consul.zip + rm -rf /tmp/consul* + name: Install Consul 1.6.1 + - run: + command: | + wget -q -O /tmp/vault.zip https://releases.hashicorp.com/vault/1.2.3/vault_1.2.3_linux_amd64.zip + sudo unzip -d /usr/local/bin /tmp/vault.zip + rm -rf /tmp/vault* + name: Install Vault 1.2.3 + - run: + command: | + if [ ! -z $GOTESTARCH ] && [ $GOTESTARCH == "386" ]; then + sudo apt-get update + sudo apt-get install -y gcc-multilib + else + echo "Skipping 32bit lib installation while building for not 386" + fi + name: Install 32bit gcc libs + - run: + command: PATH="$GOPATH/bin:/usr/local/go/bin:$PATH" make bootstrap + - run: + command: | + if [ -z $GOTEST_PKGS_EXCLUDE ]; + then + unset GOTEST_PKGS_EXCLUDE + else + unset GOTEST_PKGS + fi + + if [ ! -z $GOTESTARCH ]; then + export GOARCH="$GOTESTARCH"; + fi + + mkdir -p /tmp/test-reports + sudo -E PATH="$GOPATH/bin:/usr/local/go/bin:$PATH" make generate-structs + sudo -E PATH="$GOPATH/bin:/usr/local/go/bin:$PATH" make test-nomad + name: Running Nomad Tests + no_output_timeout: 20m + - store_test_results: + path: /tmp/test-reports + - store_artifacts: + path: /tmp/test-reports + build-binaries: + docker: + - image: golang:1.12.13 + working_directory: /go/src/github.com/hashicorp/nomad + environment: + - GIT_PAGER: cat + - GOMAXPROCS: 1 + - GOPATH: /go + - GOTESTSUM_JUNITFILE: /tmp/test-reports/results.xml + - NOMAD_SLOW_TEST: 1 + - PAGER: cat + - GO_TAGS: codegen_generated + steps: + - checkout + - run: + command: apt-get update; apt-get install -y sudo unzip zip + - run: + command: make deps + - run: + command: | + sudo rm -rf /usr/bin/protoc + sudo ./scripts/vagrant-linux-priv-protoc.sh + name: install protoc + - run: + command: sudo -E PATH="$GOPATH/bin:/usr/local/go/bin:$PATH" make generate-structs + - run: + command: make pkg/windows_amd64.zip pkg/linux_amd64.zip + - store_artifacts: + destination: /builds/nomad_windows_amd64.zip + path: pkg/windows_amd64.zip + - store_artifacts: + destination: /builds/nomad_linux_amd64.zip + path: pkg/linux_amd64.zip + test-e2e: + docker: + - image: golang:1.12.13 + working_directory: /go/src/github.com/hashicorp/nomad + steps: + - checkout + - run: + command: apt-get update; apt-get install -y sudo unzip + - run: + command: | + groupadd --gid 3434 circleci + useradd --uid 3434 --gid circleci --shell /bin/bash --create-home circleci + echo 'circleci ALL=NOPASSWD: ALL' >> /etc/sudoers.d/50-circleci + echo 'Defaults env_keep += "DEBIAN_FRONTEND"' >> /etc/sudoers.d/env_keep + chown -R circleci:circleci /go + name: prepare non-root user + - run: + command: sudo -E -H -u circleci PATH=${PATH} make deps + - run: + command: sudo -E -H -u circleci PATH=${PATH} make e2e-test + environment: + - GIT_PAGER: cat + - GOMAXPROCS: 1 + - GOPATH: /go + - GOTESTSUM_JUNITFILE: /tmp/test-reports/results.xml + - NOMAD_SLOW_TEST: 1 + - PAGER: cat + test-32bit: + machine: + image: ubuntu-1604:201903-01 + working_directory: ~/go/src/github.com/hashicorp/nomad + environment: + - GIT_PAGER: cat + - GOLANG_VERSION: 1.12.13 + - GOMAXPROCS: 1 + - GOPATH: /home/circleci/go + - GOTESTSUM_JUNITFILE: /tmp/test-reports/results.xml + - NOMAD_SLOW_TEST: 1 + - PAGER: cat + - GOTEST_PKGS: ./client/fingerprint + - GOTEST_PKGS_EXCLUDE: '' + - GOTESTARCH: '386' + steps: + - checkout + - run: + command: | + set -x + echo installing golang ${GOLANG_VERSION} + sudo rm -rf /usr/local/go + wget -O /tmp/golang.tar.gz https://dl.google.com/go/go${GOLANG_VERSION}.linux-amd64.tar.gz + sudo tar -C /usr/local -xzf /tmp/golang.tar.gz + rm -rf /tmp/golang.tar.gz + name: install golang + - run: + command: | + sudo rm -rf /usr/bin/protoc + sudo ./scripts/vagrant-linux-priv-protoc.sh + name: install protoc + - run: + command: | + wget -q -O /tmp/consul.zip https://releases.hashicorp.com/consul/1.6.1/consul_1.6.1_linux_amd64.zip + sudo unzip -d /usr/local/bin /tmp/consul.zip + rm -rf /tmp/consul* + name: Install Consul 1.6.1 + - run: + command: | + wget -q -O /tmp/vault.zip https://releases.hashicorp.com/vault/1.2.3/vault_1.2.3_linux_amd64.zip + sudo unzip -d /usr/local/bin /tmp/vault.zip + rm -rf /tmp/vault* + name: Install Vault 1.2.3 + - run: + command: | + if [ ! -z $GOTESTARCH ] && [ $GOTESTARCH == "386" ]; then + sudo apt-get update + sudo apt-get install -y gcc-multilib + else + echo "Skipping 32bit lib installation while building for not 386" + fi + name: Install 32bit gcc libs + - run: + command: PATH="$GOPATH/bin:/usr/local/go/bin:$PATH" make bootstrap + - run: + command: | + if [ -z $GOTEST_PKGS_EXCLUDE ]; + then + unset GOTEST_PKGS_EXCLUDE + else + unset GOTEST_PKGS + fi + + if [ ! -z $GOTESTARCH ]; then + export GOARCH="$GOTESTARCH"; + fi + + mkdir -p /tmp/test-reports + sudo -E PATH="$GOPATH/bin:/usr/local/go/bin:$PATH" make generate-structs + sudo -E PATH="$GOPATH/bin:/usr/local/go/bin:$PATH" make test-nomad + name: Running Nomad Tests + no_output_timeout: 20m + - store_test_results: + path: /tmp/test-reports + - store_artifacts: + path: /tmp/test-reports workflows: build-test: jobs: - - lint-go: - <<: *IGNORE_FOR_UI_BRANCHES - - build-binaries - - test-machine: - name: "test-client" - test_packages: "./client/..." - <<: *IGNORE_FOR_UI_BRANCHES - - test-machine: - name: "test-nomad" - test_packages: "./nomad/..." - <<: *IGNORE_FOR_UI_BRANCHES - - test-machine: - # API Tests run in a VM rather than container due to the FS tests - # requiring `mount` priviliges. - name: "test-api" - test_packages: "./api/..." - <<: *IGNORE_FOR_UI_BRANCHES - - test-container: - name: "test-devices" - test_packages: "./devices/..." - <<: *IGNORE_FOR_UI_BRANCHES - - test-machine: - name: "test-other" - exclude_packages: "./api|./client|./drivers/docker|./drivers/exec|./drivers/rkt|./drivers/shared/executor|./nomad|./devices" - <<: *IGNORE_FOR_UI_BRANCHES - - test-machine: - name: "test-docker" - test_packages: "./drivers/docker" - # docker is misbehaving in docker-machine-recent image - # and we get unexpected failures - # e.g. https://circleci.com/gh/hashicorp/nomad/3854 - executor: go-machine - <<: *IGNORE_FOR_UI_BRANCHES - - test-machine: - name: "test-exec" - test_packages: "./drivers/exec" - <<: *IGNORE_FOR_UI_BRANCHES - - test-machine: - name: "test-shared-exec" - test_packages: "./drivers/shared/executor" - <<: *IGNORE_FOR_UI_BRANCHES - - test-machine: - name: "test-32bit" - # Currently we only explicitly test fingerprinting on 32bit - # architectures. - test_packages: "./client/fingerprint" - goarch: "386" - <<: *IGNORE_FOR_UI_BRANCHES - - test-e2e: - <<: *IGNORE_FOR_UI_BRANCHES - - test-ui - - test-website: - <<: *IGNORE_FOR_UI_BRANCHES - + - build-binaries: + filters: + branches: + ignore: + - stable-website + - lint-go: + filters: + branches: + ignore: + - /^.-ui\b.*/ + - /^docs-.*/ + - stable-website + - test-client: + filters: + branches: + ignore: + - /^.-ui\b.*/ + - /^docs-.*/ + - stable-website + - test-nomad: + filters: + branches: + ignore: + - /^.-ui\b.*/ + - /^docs-.*/ + - stable-website + - test-api: + filters: + branches: + ignore: + - /^.-ui\b.*/ + - /^docs-.*/ + - stable-website + - test-devices: + filters: + branches: + ignore: + - /^.-ui\b.*/ + - /^docs-.*/ + - stable-website + - test-other: + filters: + branches: + ignore: + - /^.-ui\b.*/ + - /^docs-.*/ + - stable-website + - test-docker: + filters: + branches: + ignore: + - /^.-ui\b.*/ + - /^docs-.*/ + - stable-website + - test-exec: + filters: + branches: + ignore: + - /^.-ui\b.*/ + - /^docs-.*/ + - stable-website + - test-shared-exec: + filters: + branches: + ignore: + - /^.-ui\b.*/ + - /^docs-.*/ + - stable-website + - test-32bit: + filters: + branches: + ignore: + - /^.-ui\b.*/ + - /^docs-.*/ + - stable-website + - test-e2e: + filters: + branches: + ignore: + - /^.-ui\b.*/ + - /^docs-.*/ + - stable-website + - test-ui: + filters: + branches: + ignore: + - stable-website + - /^docs-.*/ + - test-website: + filters: + branches: + ignore: + - /^.-ui\b.*/ + - /^docs-.*/ + - stable-website website: jobs: - - build-website: - context: static-sites - filters: - branches: - only: stable-website -executors: - go: - working_directory: /go/src/github.com/hashicorp/nomad - docker: - - image: golang:1.12.13 - go-machine: - working_directory: ~/go/src/github.com/hashicorp/nomad - machine: - image: circleci/classic:201808-01 - docker-builder: - working_directory: ~/go/src/github.com/hashicorp/nomad - machine: true # TODO: Find latest docker image id - - # uses a more recent image with unattended upgrades disabled properly - # but seems to break docker builds - go-machine-recent: - working_directory: ~/go/src/github.com/hashicorp/nomad - machine: - image: ubuntu-1604:201903-01 - -jobs: - build-deps-image: - executor: docker-builder - steps: - - checkout - - run: docker build -t hashicorpnomad/ci-build-image:$CIRCLE_SHA1 . -f ./Dockerfile.ci - - run: docker push hashicorpnomad/ci-build-image:$CIRCLE_SHA1 - - lint-go: - executor: go - environment: - <<: *COMMON_ENVS - GOPATH: /go - steps: - - checkout - - run: apt-get update; apt-get install -y shellcheck sudo unzip - - install-protoc - - run: make deps lint-deps - - run: make check - - run: make checkscripts - - build-binaries: - executor: go - environment: - <<: *COMMON_ENVS - GOPATH: /go - # TODO: add ui tag here - GO_TAGS: "codegen_generated" - steps: - - checkout - - run: apt-get update; apt-get install -y sudo unzip zip - - run: make deps - - install-protoc - - run: sudo -E PATH="$GOPATH/bin:/usr/local/go/bin:$PATH" make generate-structs - - run: make pkg/windows_amd64.zip pkg/linux_amd64.zip - - store_artifacts: - path: pkg/windows_amd64.zip - destination: /builds/nomad_windows_amd64.zip - - store_artifacts: - path: pkg/linux_amd64.tar.gz - destination: /builds/nomad_linux_amd64.tar.gz - - test-container: - executor: go - parameters: - test_packages: - type: string - default: "" - exclude_packages: - type: string - default: "" - goarch: - type: string - default: "amd64" - environment: - <<: *COMMON_ENVS - GOTEST_PKGS: "<< parameters.test_packages >>" - GOTEST_PKGS_EXCLUDE: "<< parameters.exclude_packages >>" - GOPATH: /go - GOTESTARCH: "<< parameters.goarch >>" - steps: - - checkout - - run: apt-get update; apt-get install -y shellcheck sudo unzip - - run: make deps - - install-protoc - - install-consul - - install-vault - - run-tests - - store_test_results: - path: /tmp/test-reports - - store_artifacts: - path: /tmp/test-reports - - test-e2e: - executor: go - environment: - <<: *COMMON_ENVS - GOPATH: /go - steps: - - checkout - - run: apt-get update; apt-get install -y sudo unzip - # e2e tests require privileged mount/umount permissions when running as root - # TODO: switch to using machine executor and run as root to test e2e path - - run: - name: prepare non-root user - command: | - groupadd --gid 3434 circleci - useradd --uid 3434 --gid circleci --shell /bin/bash --create-home circleci - echo 'circleci ALL=NOPASSWD: ALL' >> /etc/sudoers.d/50-circleci - echo 'Defaults env_keep += "DEBIAN_FRONTEND"' >> /etc/sudoers.d/env_keep - chown -R circleci:circleci /go - - - run: sudo -E -H -u circleci PATH=${PATH} make deps - - run: sudo -E -H -u circleci PATH=${PATH} make e2e-test - - test-website: - executor: go-machine-recent - environment: - <<: *COMMON_ENVS - steps: - - checkout - - run: make test-website - - test-machine: - executor: "<< parameters.executor >>" - parameters: - test_packages: - type: string - default: "" - exclude_packages: - type: string - default: "" - executor: - type: string - default: "go-machine-recent" - goarch: - type: string - default: "amd64" - environment: - <<: *COMMON_ENVS - GOTEST_PKGS_EXCLUDE: "<< parameters.exclude_packages >>" - GOTEST_PKGS: "<< parameters.test_packages >>" - GOPATH: /home/circleci/go - GOTESTARCH: "<< parameters.goarch >>" - steps: - - checkout - - install-golang - - install-protoc - - install-consul - - install-vault - - run: - name: Install 32bit gcc libs - command: | - if [ ! -z $GOTESTARCH ] && [ $GOTESTARCH == "386" ]; then - sudo apt-get update - sudo apt-get install -y gcc-multilib - else - echo "Skipping 32bit lib installation while building for not 386" - fi - - run: PATH="$GOPATH/bin:/usr/local/go/bin:$PATH" make bootstrap - - run-tests - - store_test_results: - path: /tmp/test-reports - - store_artifacts: - path: /tmp/test-reports - test-ui: - docker: - - image: circleci/node:10-browsers - environment: - # See https://git.io/vdao3 for details. - JOBS: 2 - steps: - - checkout - - restore_cache: - keys: - - v1-deps-{{ checksum "ui/yarn.lock" }} - - v1-deps- - - run: - name: yarn install - command: cd ui && yarn install - - save_cache: - key: v1-deps-{{ checksum "ui/yarn.lock" }} - paths: - - ./ui/node_modules - - run: - name: lint:js - command: cd ui && yarn run lint:js - - run: - name: lint:hbs - command: cd ui && yarn run lint:hbs - - run: - name: Ember tests - command: cd ui && yarn test - - build-website: - # setting the working_directory along with the checkout path allows us to not have - # to cd into the website/ directory for commands - working_directory: ~/project/website - docker: - - image: hashicorp/middleman-hashicorp:0.3.35 - steps: - - checkout: - path: ~/project - - # restores gem cache - - restore_cache: - key: static-site-gems-v1-{{ checksum "Gemfile.lock" }} - - - run: - name: install gems - command: bundle check || bundle install --path vendor/bundle --retry=3 - - # saves gem cache if we have changed the Gemfile - - save_cache: - key: static-site-gems-v1-{{ checksum "Gemfile.lock" }} - paths: - - ~/project/website/vendor/bundle - - - run: - name: middleman build - command: bundle exec middleman build - - - run: - name: website deploy - command: ./scripts/deploy.sh - -commands: - install-golang: - parameters: - version: - type: string - default: "1.12.13" - steps: - - run: - name: install golang << parameters.version >> - command: | - sudo rm -rf /usr/local/go - wget -q -O /tmp/golang.tar.gz https://dl.google.com/go/go<< parameters.version >>.linux-amd64.tar.gz - sudo tar -C /usr/local -xzf /tmp/golang.tar.gz - rm -rf /tmp/golang.tar.gz - - install-vault: - parameters: - version: - type: string - default: 1.2.3 - steps: - - run: - name: Install Vault << parameters.version >> - command: | - wget -q -O /tmp/vault.zip https://releases.hashicorp.com/vault/<< parameters.version >>/vault_<< parameters.version>>_linux_amd64.zip - sudo unzip -d /usr/local/bin /tmp/vault.zip - rm -rf /tmp/vault* - - install-consul: - parameters: - version: - type: string - default: 1.6.1 - steps: - - run: - name: Install Consul << parameters.version >> - command: | - wget -q -O /tmp/consul.zip https://releases.hashicorp.com/consul/<< parameters.version >>/consul_<< parameters.version >>_linux_amd64.zip - sudo unzip -d /usr/local/bin /tmp/consul.zip - rm -rf /tmp/consul* - - install-protoc: - steps: - - run: - name: install protoc - command: | - sudo rm -rf /usr/bin/protoc - sudo ./scripts/vagrant-linux-priv-protoc.sh - - run-tests: - steps: - - run: - name: Running Nomad Tests - command: | - if [ -z $GOTEST_PKGS_EXCLUDE ]; - then - unset GOTEST_PKGS_EXCLUDE - else - unset GOTEST_PKGS - fi - - if [ ! -z $GOTESTARCH ]; then - export GOARCH="$GOTESTARCH"; - fi - - mkdir -p /tmp/test-reports - sudo -E PATH="$GOPATH/bin:/usr/local/go/bin:$PATH" make generate-structs - sudo -E PATH="$GOPATH/bin:/usr/local/go/bin:$PATH" make test-nomad + - build-website: + filters: + branches: + only: stable-website + context: static-sites + version: 2 diff --git a/.circleci/config/commands/install-circleci-local-cli.yml b/.circleci/config/commands/install-circleci-local-cli.yml new file mode 100644 index 000000000..5b89a3f69 --- /dev/null +++ b/.circleci/config/commands/install-circleci-local-cli.yml @@ -0,0 +1,31 @@ +parameters: + version: + type: string + default: 0.1.5879 + + sha256: + type: string + default: f178ea62c781aec06267017404f87983c87f171fd0e66ef3737916246ae66dd6 +steps: + - run: + name: Install CircleCI CLI << parameters.version >> + command: | + CCI_VERSION="<< parameters.version >>" + CCI_SHA256="<< parameters.sha256 >>" + + URL="https://github.com/CircleCI-Public/circleci-cli/releases/download/v${CCI_VERSION}/circleci-cli_${CCI_VERSION}_linux_amd64.tar.gz" + + mkdir -p /tmp/circleci-cli/ + curl --fail --show-error --location \ + -o /tmp/circleci-cli/cli.tar.gz "${URL}" + + echo "$CCI_SHA256 /tmp/circleci-cli/cli.tar.gz" | sha256sum -c + + tar -xz --strip-components=1 \ + -C /tmp/circleci-cli \ + -f /tmp/circleci-cli/cli.tar.gz \ + "circleci-cli_${CCI_VERSION}_linux_amd64/circleci" + + sudo cp /tmp/circleci-cli/circleci /usr/bin/circleci-local-cli + + circleci-local-cli version diff --git a/.circleci/config/commands/install-consul.yml b/.circleci/config/commands/install-consul.yml new file mode 100644 index 000000000..75aeb3b81 --- /dev/null +++ b/.circleci/config/commands/install-consul.yml @@ -0,0 +1,11 @@ +parameters: + version: + type: string + default: 1.6.1 +steps: + - run: + name: Install Consul << parameters.version >> + command: | + wget -q -O /tmp/consul.zip https://releases.hashicorp.com/consul/<< parameters.version >>/consul_<< parameters.version >>_linux_amd64.zip + sudo unzip -d /usr/local/bin /tmp/consul.zip + rm -rf /tmp/consul* diff --git a/.circleci/config/commands/install-golang.yml b/.circleci/config/commands/install-golang.yml new file mode 100644 index 000000000..485ffd9e1 --- /dev/null +++ b/.circleci/config/commands/install-golang.yml @@ -0,0 +1,10 @@ +steps: + - run: + name: install golang + command: | + set -x + echo installing golang ${GOLANG_VERSION} + sudo rm -rf /usr/local/go + wget -O /tmp/golang.tar.gz https://dl.google.com/go/go${GOLANG_VERSION}.linux-amd64.tar.gz + sudo tar -C /usr/local -xzf /tmp/golang.tar.gz + rm -rf /tmp/golang.tar.gz diff --git a/.circleci/config/commands/install-protoc.yml b/.circleci/config/commands/install-protoc.yml new file mode 100644 index 000000000..db0d0dad7 --- /dev/null +++ b/.circleci/config/commands/install-protoc.yml @@ -0,0 +1,6 @@ +steps: + - run: + name: install protoc + command: | + sudo rm -rf /usr/bin/protoc + sudo ./scripts/vagrant-linux-priv-protoc.sh diff --git a/.circleci/config/commands/install-vault.yml b/.circleci/config/commands/install-vault.yml new file mode 100644 index 000000000..3e52cbee2 --- /dev/null +++ b/.circleci/config/commands/install-vault.yml @@ -0,0 +1,11 @@ +parameters: + version: + type: string + default: 1.2.3 +steps: + - run: + name: Install Vault << parameters.version >> + command: | + wget -q -O /tmp/vault.zip https://releases.hashicorp.com/vault/<< parameters.version >>/vault_<< parameters.version>>_linux_amd64.zip + sudo unzip -d /usr/local/bin /tmp/vault.zip + rm -rf /tmp/vault* diff --git a/.circleci/config/commands/run-tests.yml b/.circleci/config/commands/run-tests.yml new file mode 100644 index 000000000..9a90841d3 --- /dev/null +++ b/.circleci/config/commands/run-tests.yml @@ -0,0 +1,19 @@ +steps: + - run: + name: Running Nomad Tests + no_output_timeout: 20m + command: | + if [ -z $GOTEST_PKGS_EXCLUDE ]; + then + unset GOTEST_PKGS_EXCLUDE + else + unset GOTEST_PKGS + fi + + if [ ! -z $GOTESTARCH ]; then + export GOARCH="$GOTESTARCH"; + fi + + mkdir -p /tmp/test-reports + sudo -E PATH="$GOPATH/bin:/usr/local/go/bin:$PATH" make generate-structs + sudo -E PATH="$GOPATH/bin:/usr/local/go/bin:$PATH" make test-nomad diff --git a/.circleci/config/config.yml b/.circleci/config/config.yml new file mode 100644 index 000000000..c18e455f2 --- /dev/null +++ b/.circleci/config/config.yml @@ -0,0 +1,44 @@ +version: 2.1 + +references: + # environment specific references - aim to avoid conflicts + go-machine-image: &go_machine_image + circleci/classic:201808-01 + go-machine-recent-image: &go_machine_recent_image + ubuntu-1604:201903-01 + + # common references + common_envs: &common_envs + GOMAXPROCS: 1 + NOMAD_SLOW_TEST: 1 + GOTESTSUM_JUNITFILE: /tmp/test-reports/results.xml + # disable implicit git paging. CircleCI runs commands with in a tty + # making git assume it's an interactive session. + PAGER: cat + GIT_PAGER: cat + +executors: + go: + working_directory: /go/src/github.com/hashicorp/nomad + docker: + - image: golang:1.12.13 + environment: + <<: *common_envs + GOPATH: /go + + go-machine: + working_directory: ~/go/src/github.com/hashicorp/nomad + machine: + image: *go_machine_image + environment: &machine_env + <<: *common_envs + GOPATH: /home/circleci/go + GOLANG_VERSION: "1.12.13" + + # uses a more recent image with unattended upgrades disabled properly + # but seems to break docker builds + go-machine-recent: + working_directory: ~/go/src/github.com/hashicorp/nomad + machine: + image: *go_machine_recent_image + environment: *machine_env diff --git a/.circleci/config/jobs/build-binaries.yml b/.circleci/config/jobs/build-binaries.yml new file mode 100644 index 000000000..21a0079c5 --- /dev/null +++ b/.circleci/config/jobs/build-binaries.yml @@ -0,0 +1,17 @@ +executor: go +environment: + # TODO: add ui tag here + GO_TAGS: "codegen_generated" +steps: + - checkout + - run: apt-get update; apt-get install -y sudo unzip zip + - run: make deps + - install-protoc + - run: sudo -E PATH="$GOPATH/bin:/usr/local/go/bin:$PATH" make generate-structs + - run: make pkg/windows_amd64.zip pkg/linux_amd64.zip + - store_artifacts: + path: pkg/windows_amd64.zip + destination: /builds/nomad_windows_amd64.zip + - store_artifacts: + path: pkg/linux_amd64.zip + destination: /builds/nomad_linux_amd64.zip diff --git a/.circleci/config/jobs/build-website.yml b/.circleci/config/jobs/build-website.yml new file mode 100644 index 000000000..3dc1384c3 --- /dev/null +++ b/.circleci/config/jobs/build-website.yml @@ -0,0 +1,30 @@ +# setting the working_directory along with the checkout path allows us to not have +# to cd into the website/ directory for commands +working_directory: ~/project/website +docker: + - image: hashicorp/middleman-hashicorp:0.3.35 +steps: + - checkout: + path: ~/project + + # restores gem cache + - restore_cache: + key: static-site-gems-v1-{{ checksum "Gemfile.lock" }} + + - run: + name: install gems + command: bundle check || bundle install --path vendor/bundle --retry=3 + + # saves gem cache if we have changed the Gemfile + - save_cache: + key: static-site-gems-v1-{{ checksum "Gemfile.lock" }} + paths: + - ~/project/website/vendor/bundle + + - run: + name: middleman build + command: bundle exec middleman build + + - run: + name: website deploy + command: ./scripts/deploy.sh diff --git a/.circleci/config/jobs/lint-go.yml b/.circleci/config/jobs/lint-go.yml new file mode 100644 index 000000000..72e955010 --- /dev/null +++ b/.circleci/config/jobs/lint-go.yml @@ -0,0 +1,12 @@ +executor: go +steps: + - checkout + - run: apt-get update; apt-get install -y shellcheck sudo unzip + - install-protoc + - install-circleci-local-cli + - run: make deps lint-deps + - run: make check + - run: make checkscripts + - run: + name: check .circleci/config.yml is up-to-date + command: make -C .circleci CIRCLECI="circleci-local-cli --skip-update-check" ci-verify diff --git a/.circleci/config/jobs/test-container.yml b/.circleci/config/jobs/test-container.yml new file mode 100644 index 000000000..744f931d0 --- /dev/null +++ b/.circleci/config/jobs/test-container.yml @@ -0,0 +1,27 @@ +executor: go +parameters: + test_packages: + type: string + default: "" + exclude_packages: + type: string + default: "" + goarch: + type: string + default: "amd64" +environment: + GOTEST_PKGS: "<< parameters.test_packages >>" + GOTEST_PKGS_EXCLUDE: "<< parameters.exclude_packages >>" + GOTESTARCH: "<< parameters.goarch >>" +steps: + - checkout + - run: apt-get update; apt-get install -y shellcheck sudo unzip + - run: make deps + - install-protoc + - install-consul + - install-vault + - run-tests + - store_test_results: + path: /tmp/test-reports + - store_artifacts: + path: /tmp/test-reports diff --git a/.circleci/config/jobs/test-e2e.yml b/.circleci/config/jobs/test-e2e.yml new file mode 100644 index 000000000..103cc7fb9 --- /dev/null +++ b/.circleci/config/jobs/test-e2e.yml @@ -0,0 +1,17 @@ +executor: go +steps: + - checkout + - run: apt-get update; apt-get install -y sudo unzip + # e2e tests require privileged mount/umount permissions when running as root + # TODO: switch to using machine executor and run as root to test e2e path + - run: + name: prepare non-root user + command: | + groupadd --gid 3434 circleci + useradd --uid 3434 --gid circleci --shell /bin/bash --create-home circleci + echo 'circleci ALL=NOPASSWD: ALL' >> /etc/sudoers.d/50-circleci + echo 'Defaults env_keep += "DEBIAN_FRONTEND"' >> /etc/sudoers.d/env_keep + chown -R circleci:circleci /go + + - run: sudo -E -H -u circleci PATH=${PATH} make deps + - run: sudo -E -H -u circleci PATH=${PATH} make e2e-test diff --git a/.circleci/config/jobs/test-machine.yml b/.circleci/config/jobs/test-machine.yml new file mode 100644 index 000000000..755a99fcc --- /dev/null +++ b/.circleci/config/jobs/test-machine.yml @@ -0,0 +1,39 @@ +executor: "<< parameters.executor >>" +parameters: + test_packages: + type: string + default: "" + exclude_packages: + type: string + default: "" + executor: + type: string + default: "go-machine-recent" + goarch: + type: string + default: "amd64" +environment: + GOTEST_PKGS_EXCLUDE: "<< parameters.exclude_packages >>" + GOTEST_PKGS: "<< parameters.test_packages >>" + GOTESTARCH: "<< parameters.goarch >>" +steps: + - checkout + - install-golang + - install-protoc + - install-consul + - install-vault + - run: + name: Install 32bit gcc libs + command: | + if [ ! -z $GOTESTARCH ] && [ $GOTESTARCH == "386" ]; then + sudo apt-get update + sudo apt-get install -y gcc-multilib + else + echo "Skipping 32bit lib installation while building for not 386" + fi + - run: PATH="$GOPATH/bin:/usr/local/go/bin:$PATH" make bootstrap + - run-tests + - store_test_results: + path: /tmp/test-reports + - store_artifacts: + path: /tmp/test-reports diff --git a/.circleci/config/jobs/test-ui.yml b/.circleci/config/jobs/test-ui.yml new file mode 100644 index 000000000..d29c0f92b --- /dev/null +++ b/.circleci/config/jobs/test-ui.yml @@ -0,0 +1,27 @@ +docker: + - image: circleci/node:10-browsers + environment: + # See https://git.io/vdao3 for details. + JOBS: 2 +steps: + - checkout + - restore_cache: + keys: + - v1-deps-{{ checksum "ui/yarn.lock" }} + - v1-deps- + - run: + name: yarn install + command: cd ui && yarn install + - save_cache: + key: v1-deps-{{ checksum "ui/yarn.lock" }} + paths: + - ./ui/node_modules + - run: + name: lint:js + command: cd ui && yarn run lint:js + - run: + name: lint:hbs + command: cd ui && yarn run lint:hbs + - run: + name: Ember tests + command: cd ui && yarn test diff --git a/.circleci/config/jobs/test-website.yml b/.circleci/config/jobs/test-website.yml new file mode 100644 index 000000000..0a54aa06a --- /dev/null +++ b/.circleci/config/jobs/test-website.yml @@ -0,0 +1,4 @@ +executor: go-machine-recent +steps: + - checkout + - run: make test-website diff --git a/.circleci/config/workflows/build-test.yml b/.circleci/config/workflows/build-test.yml new file mode 100644 index 000000000..f3d7f7b76 --- /dev/null +++ b/.circleci/config/workflows/build-test.yml @@ -0,0 +1,70 @@ +jobs: +- build-binaries: + # almost always build binaries as they may be needed + # for e2e tests + filters: + branches: + ignore: + - stable-website +- lint-go: + filters: &backend_branches_filter + branches: + ignore: + - /^.-ui\b.*/ + - /^docs-.*/ + - stable-website +- test-machine: + name: "test-client" + test_packages: "./client/..." + filters: *backend_branches_filter +- test-machine: + name: "test-nomad" + test_packages: "./nomad/..." + filters: *backend_branches_filter +- test-machine: + # API Tests run in a VM rather than container due to the FS tests + # requiring `mount` priviliges. + name: "test-api" + test_packages: "./api/..." + filters: *backend_branches_filter +- test-container: + name: "test-devices" + test_packages: "./devices/..." + filters: *backend_branches_filter +- test-machine: + name: "test-other" + exclude_packages: "./api|./client|./drivers/docker|./drivers/exec|./drivers/rkt|./drivers/shared/executor|./nomad|./devices" + filters: *backend_branches_filter +- test-machine: + name: "test-docker" + test_packages: "./drivers/docker" + # docker is misbehaving in docker-machine-recent image + # and we get unexpected failures + # e.g. https://circleci.com/gh/hashicorp/nomad/3854 + executor: go-machine + filters: *backend_branches_filter +- test-machine: + name: "test-exec" + test_packages: "./drivers/exec" + filters: *backend_branches_filter +- test-machine: + name: "test-shared-exec" + test_packages: "./drivers/shared/executor" + filters: *backend_branches_filter +- test-machine: + name: "test-32bit" + # Currently we only explicitly test fingerprinting on 32bit + # architectures. + test_packages: "./client/fingerprint" + goarch: "386" + filters: *backend_branches_filter +- test-e2e: + filters: *backend_branches_filter +- test-ui: + filters: + branches: + ignore: + - stable-website + - /^docs-.*/ +- test-website: + filters: *backend_branches_filter diff --git a/.circleci/config/workflows/website.yml b/.circleci/config/workflows/website.yml new file mode 100644 index 000000000..519c460e5 --- /dev/null +++ b/.circleci/config/workflows/website.yml @@ -0,0 +1,6 @@ +jobs: + - build-website: + context: static-sites + filters: + branches: + only: stable-website diff --git a/.gitignore b/.gitignore index 8efd757c2..3713b3b55 100644 --- a/.gitignore +++ b/.gitignore @@ -28,8 +28,8 @@ _testmain.go *.test *.prof -tags -bin/ +/tags +/bin/ /pkg/ .vagrant/ website/build/ diff --git a/CHANGELOG.md b/CHANGELOG.md index c3e70858b..7d3a89ca9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.10.3 (Unreleased) + +IMPROVEMENTS: + +* scheduler: Removed penalty for allocation's previous node if the allocation did not fail. [[GH-6781](https://github.com/hashicorp/nomad/issues/6781)] + ## 0.10.2 (December 4, 2019) FEATURES: diff --git a/README.md b/README.md index 59f3dc4d5..4e2d2efe5 100644 --- a/README.md +++ b/README.md @@ -50,8 +50,8 @@ Documentation & Guides * [Installing Nomad for Production](https://www.nomadproject.io/guides/operations/deployment-guide.html) * [Advanced Job Scheduling on Nomad with Affinities](https://www.nomadproject.io/guides/operating-a-job/advanced-scheduling/affinity.html) * [Increasing Nomad Fault Tolerance with Spread](https://www.nomadproject.io/guides/operating-a-job/advanced-scheduling/spread.html) -* [Load Balancing on Nomad with Fabio & Consul](https://www.nomadproject.io/guides/load-balancing/fabio.html) -* [Deploying Stateful Workloads via Portworx](https://www.nomadproject.io/guides/stateful-workloads/portworx.html) +* [Load Balancing on Nomad with Fabio & Consul](https://learn.hashicorp.com/nomad/load-balancing/fabio) +* [Deploying Stateful Workloads via Portworx](https://learn.hashicorp.com/nomad/stateful-workloads/portworx) * [Running Apache Spark on Nomad](https://www.nomadproject.io/guides/spark/spark.html) * [Integrating Vault with Nomad for Secrets Management](https://www.nomadproject.io/guides/operations/vault-integration/index.html) * [Securing Nomad with TLS](https://www.nomadproject.io/guides/security/securing-nomad.html) diff --git a/client/client_test.go b/client/client_test.go index f1a4c0013..3b4533b9e 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -3,6 +3,7 @@ package client import ( "fmt" "io/ioutil" + "net" "os" "path/filepath" "runtime" @@ -18,6 +19,7 @@ import ( "github.com/hashicorp/nomad/client/state" "github.com/hashicorp/nomad/command/agent/consul" "github.com/hashicorp/nomad/helper/pluginutils/catalog" + "github.com/hashicorp/nomad/helper/pluginutils/singleton" "github.com/hashicorp/nomad/helper/testlog" "github.com/hashicorp/nomad/helper/uuid" "github.com/hashicorp/nomad/nomad" @@ -29,9 +31,7 @@ import ( "github.com/hashicorp/nomad/testutil" "github.com/stretchr/testify/assert" - hclog "github.com/hashicorp/go-hclog" cstate "github.com/hashicorp/nomad/client/state" - ctestutil "github.com/hashicorp/nomad/client/testutil" "github.com/stretchr/testify/require" ) @@ -447,7 +447,6 @@ func TestClient_UpdateAllocStatus(t *testing.T) { func TestClient_WatchAllocs(t *testing.T) { t.Parallel() - ctestutil.ExecCompatible(t) s1, _ := testServer(t, nil) defer s1.Shutdown() testutil.WaitForLeader(t, s1.RPC) @@ -462,6 +461,11 @@ func TestClient_WatchAllocs(t *testing.T) { // Create mock allocations job := mock.Job() + job.TaskGroups[0].Count = 3 + job.TaskGroups[0].Tasks[0].Driver = "mock_driver" + job.TaskGroups[0].Tasks[0].Config = map[string]interface{}{ + "run_for": "10s", + } alloc1 := mock.Alloc() alloc1.JobID = job.ID alloc1.Job = job @@ -603,10 +607,14 @@ func TestClient_SaveRestoreState(t *testing.T) { // Create a new client logger := testlog.HCLogger(t) c1.config.Logger = logger - catalog := consul.NewMockCatalog(logger) + consulCatalog := consul.NewMockCatalog(logger) mockService := consulApi.NewMockConsulServiceClient(t, logger) - c2, err := NewClient(c1.config, catalog, mockService) + // ensure we use non-shutdown driver instances + c1.config.PluginLoader = catalog.TestPluginLoaderWithOptions(t, "", c1.config.Options, nil) + c1.config.PluginSingletonLoader = singleton.NewSingletonLoader(logger, c1.config.PluginLoader) + + c2, err := NewClient(c1.config, consulCatalog, mockService) if err != nil { t.Fatalf("err: %v", err) } @@ -637,97 +645,6 @@ func TestClient_SaveRestoreState(t *testing.T) { } } -func TestClient_RestoreError(t *testing.T) { - t.Parallel() - require := require.New(t) - - s1, _ := testServer(t, nil) - defer s1.Shutdown() - testutil.WaitForLeader(t, s1.RPC) - - c1, cleanup := TestClient(t, func(c *config.Config) { - c.DevMode = false - c.RPCHandler = s1 - }) - defer cleanup() - - // Wait until the node is ready - waitTilNodeReady(c1, t) - - // Create mock allocations - job := mock.Job() - alloc1 := mock.Alloc() - alloc1.NodeID = c1.Node().ID - alloc1.Job = job - alloc1.JobID = job.ID - alloc1.Job.TaskGroups[0].Tasks[0].Driver = "mock_driver" - alloc1.Job.TaskGroups[0].Tasks[0].Config = map[string]interface{}{ - "run_for": "10s", - } - alloc1.ClientStatus = structs.AllocClientStatusRunning - - state := s1.State() - err := state.UpsertJob(100, job) - require.Nil(err) - - err = state.UpsertJobSummary(101, mock.JobSummary(alloc1.JobID)) - require.Nil(err) - - err = state.UpsertAllocs(102, []*structs.Allocation{alloc1}) - require.Nil(err) - - // Allocations should get registered - testutil.WaitForResult(func() (bool, error) { - c1.allocLock.RLock() - ar := c1.allocs[alloc1.ID] - c1.allocLock.RUnlock() - if ar == nil { - return false, fmt.Errorf("nil alloc runner") - } - if ar.Alloc().ClientStatus != structs.AllocClientStatusRunning { - return false, fmt.Errorf("client status: got %v; want %v", ar.Alloc().ClientStatus, structs.AllocClientStatusRunning) - } - return true, nil - }, func(err error) { - t.Fatalf("err: %v", err) - }) - - // Shutdown the client, saves state - if err := c1.Shutdown(); err != nil { - t.Fatalf("err: %v", err) - } - - // Create a new client with a stateDB implementation that errors - logger := testlog.HCLogger(t) - c1.config.Logger = logger - catalog := consul.NewMockCatalog(logger) - mockService := consulApi.NewMockConsulServiceClient(t, logger) - - // This stateDB returns errors for all methods called by restore - stateDBFunc := func(hclog.Logger, string) (cstate.StateDB, error) { - return &cstate.ErrDB{Allocs: []*structs.Allocation{alloc1}}, nil - } - c1.config.StateDBFactory = stateDBFunc - - c2, err := NewClient(c1.config, catalog, mockService) - require.Nil(err) - defer c2.Shutdown() - - // Ensure the allocation has been marked as failed on the server - testutil.WaitForResult(func() (bool, error) { - alloc, err := s1.State().AllocByID(nil, alloc1.ID) - require.Nil(err) - failed := alloc.ClientStatus == structs.AllocClientStatusFailed - if !failed { - return false, fmt.Errorf("Expected failed client status, but got %v", alloc.ClientStatus) - } - return true, nil - }, func(err error) { - require.NoError(err) - }) - -} - func TestClient_AddAllocError(t *testing.T) { t.Parallel() require := require.New(t) @@ -1254,8 +1171,7 @@ func TestClient_UpdateNodeFromFingerprintKeepsConfig(t *testing.T) { // Client without network configured updates to match fingerprint client, cleanup := TestClient(t, nil) defer cleanup() - // capture the platform fingerprinted device name for the next test - dev := client.config.Node.NodeResources.Networks[0].Device + client.updateNodeFromFingerprint(&fingerprint.FingerprintResponse{ NodeResources: &structs.NodeResources{ Cpu: structs.NodeCpuResources{CpuShares: 123}, @@ -1271,6 +1187,14 @@ func TestClient_UpdateNodeFromFingerprintKeepsConfig(t *testing.T) { assert.Equal(t, 80, client.config.Node.Resources.CPU) assert.Equal(t, "any-interface", client.config.Node.Resources.Networks[0].Device) + // lookup an interface. client.Node starts with a hardcoded value, eth0, + // and is only updated async through fingerprinter. + // Let's just lookup network device; anyone will do for this test + interfaces, err := net.Interfaces() + require.NoError(t, err) + require.NotEmpty(t, interfaces) + dev := interfaces[0].Name + // Client with network interface configured keeps the config // setting on update name := "TestClient_UpdateNodeFromFingerprintKeepsConfig2" diff --git a/client/fingerprint/env_aws.go b/client/fingerprint/env_aws.go index 8eac273ad..f468af39b 100644 --- a/client/fingerprint/env_aws.go +++ b/client/fingerprint/env_aws.go @@ -2,7 +2,6 @@ package fingerprint import ( "fmt" - "io/ioutil" "net/http" "net/url" "os" @@ -10,6 +9,10 @@ import ( "strings" "time" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/aws/ec2metadata" + "github.com/aws/aws-sdk-go/aws/session" log "github.com/hashicorp/go-hclog" cleanhttp "github.com/hashicorp/go-cleanhttp" @@ -17,10 +20,6 @@ import ( ) const ( - // This is where the AWS metadata server normally resides. We hardcode the - // "instance" path as well since it's the only one we access here. - DEFAULT_AWS_URL = "http://169.254.169.254/latest/meta-data/" - // AwsMetadataTimeout is the timeout used when contacting the AWS metadata // service AwsMetadataTimeout = 2 * time.Second @@ -50,15 +49,18 @@ var ec2InstanceSpeedMap = map[*regexp.Regexp]int{ // EnvAWSFingerprint is used to fingerprint AWS metadata type EnvAWSFingerprint struct { StaticFingerprinter - timeout time.Duration - logger log.Logger + + // endpoint for EC2 metadata as expected by AWS SDK + endpoint string + + logger log.Logger } // NewEnvAWSFingerprint is used to create a fingerprint from AWS metadata func NewEnvAWSFingerprint(logger log.Logger) Fingerprint { f := &EnvAWSFingerprint{ - logger: logger.Named("env_aws"), - timeout: AwsMetadataTimeout, + logger: logger.Named("env_aws"), + endpoint: strings.TrimSuffix(os.Getenv("AWS_ENV_URL"), "/meta-data/"), } return f } @@ -66,12 +68,16 @@ func NewEnvAWSFingerprint(logger log.Logger) Fingerprint { func (f *EnvAWSFingerprint) Fingerprint(request *FingerprintRequest, response *FingerprintResponse) error { cfg := request.Config + timeout := AwsMetadataTimeout + // Check if we should tighten the timeout if cfg.ReadBoolDefault(TightenNetworkTimeoutsConfig, false) { - f.timeout = 1 * time.Millisecond + timeout = 1 * time.Millisecond } - if !f.isAWS() { + ec2meta := ec2MetaClient(f.endpoint, timeout) + + if !ec2meta.Available() { return nil } @@ -80,16 +86,6 @@ func (f *EnvAWSFingerprint) Fingerprint(request *FingerprintRequest, response *F Device: "eth0", } - metadataURL := os.Getenv("AWS_ENV_URL") - if metadataURL == "" { - metadataURL = DEFAULT_AWS_URL - } - - client := &http.Client{ - Timeout: f.timeout, - Transport: cleanhttp.DefaultTransport(), - } - // Keys and whether they should be namespaced as unique. Any key whose value // uniquely identifies a node, such as ip, should be marked as unique. When // marked as unique, the key isn't included in the computed node class. @@ -105,23 +101,19 @@ func (f *EnvAWSFingerprint) Fingerprint(request *FingerprintRequest, response *F "placement/availability-zone": false, } for k, unique := range keys { - res, err := client.Get(metadataURL + k) - if err != nil { + resp, err := ec2meta.GetMetadata(k) + if awsErr, ok := err.(awserr.RequestFailure); ok { + f.logger.Debug("could not read attribute value", "attribute", k, "error", awsErr) + continue + } else if awsErr, ok := err.(awserr.Error); ok { // if it's a URL error, assume we're not in an AWS environment // TODO: better way to detect AWS? Check xen virtualization? - if _, ok := err.(*url.Error); ok { + if _, ok := awsErr.OrigErr().(*url.Error); ok { return nil } + // not sure what other errors it would return return err - } else if res.StatusCode != http.StatusOK { - f.logger.Debug("could not read attribute value", "attribute", k) - continue - } - resp, err := ioutil.ReadAll(res.Body) - res.Body.Close() - if err != nil { - f.logger.Error("error reading response body for AWS attribute", "attribute", k, "error", err) } // assume we want blank entries @@ -130,7 +122,7 @@ func (f *EnvAWSFingerprint) Fingerprint(request *FingerprintRequest, response *F key = structs.UniqueNamespace(key) } - response.AddAttribute(key, strings.Trim(string(resp), "\n")) + response.AddAttribute(key, strings.Trim(resp, "\n")) } // copy over network specific information @@ -141,10 +133,11 @@ func (f *EnvAWSFingerprint) Fingerprint(request *FingerprintRequest, response *F } // find LinkSpeed from lookup - throughput := f.linkSpeed() - if cfg.NetworkSpeed != 0 { - throughput = cfg.NetworkSpeed - } else if throughput == 0 { + throughput := cfg.NetworkSpeed + if throughput == 0 { + throughput = f.linkSpeed(ec2meta) + } + if throughput == 0 { // Failed to determine speed. Check if the network fingerprint got it found := false if request.Node.Resources != nil && len(request.Node.Resources.Networks) > 0 { @@ -177,75 +170,16 @@ func (f *EnvAWSFingerprint) Fingerprint(request *FingerprintRequest, response *F return nil } -func (f *EnvAWSFingerprint) isAWS() bool { - // Read the internal metadata URL from the environment, allowing test files to - // provide their own - metadataURL := os.Getenv("AWS_ENV_URL") - if metadataURL == "" { - metadataURL = DEFAULT_AWS_URL - } - - client := &http.Client{ - Timeout: f.timeout, - Transport: cleanhttp.DefaultTransport(), - } - - // Query the metadata url for the ami-id, to verify we're on AWS - resp, err := client.Get(metadataURL + "ami-id") - if err != nil { - f.logger.Debug("error querying AWS Metadata URL, skipping") - return false - } - defer resp.Body.Close() - - if resp.StatusCode >= 400 { - // URL not found, which indicates that this isn't AWS - return false - } - - instanceID, err := ioutil.ReadAll(resp.Body) - if err != nil { - f.logger.Debug("error reading AWS Instance ID, skipping") - return false - } - - match, err := regexp.MatchString("ami-*", string(instanceID)) - if err != nil || !match { - return false - } - - return true -} - // EnvAWSFingerprint uses lookup table to approximate network speeds -func (f *EnvAWSFingerprint) linkSpeed() int { +func (f *EnvAWSFingerprint) linkSpeed(ec2meta *ec2metadata.EC2Metadata) int { - // Query the API for the instance type, and use the table above to approximate - // the network speed - metadataURL := os.Getenv("AWS_ENV_URL") - if metadataURL == "" { - metadataURL = DEFAULT_AWS_URL - } - - client := &http.Client{ - Timeout: f.timeout, - Transport: cleanhttp.DefaultTransport(), - } - - res, err := client.Get(metadataURL + "instance-type") + resp, err := ec2meta.GetMetadata("instance-type") if err != nil { f.logger.Error("error reading instance-type", "error", err) return 0 } - body, err := ioutil.ReadAll(res.Body) - res.Body.Close() - if err != nil { - f.logger.Error("error reading response body for instance-type", "error", err) - return 0 - } - - key := strings.Trim(string(body), "\n") + key := strings.Trim(resp, "\n") netSpeed := 0 for reg, speed := range ec2InstanceSpeedMap { if reg.MatchString(key) { @@ -256,3 +190,16 @@ func (f *EnvAWSFingerprint) linkSpeed() int { return netSpeed } + +func ec2MetaClient(endpoint string, timeout time.Duration) *ec2metadata.EC2Metadata { + client := &http.Client{ + Timeout: timeout, + Transport: cleanhttp.DefaultTransport(), + } + + c := aws.NewConfig().WithHTTPClient(client) + if endpoint != "" { + c = c.WithEndpoint(endpoint) + } + return ec2metadata.New(session.New(), c) +} diff --git a/client/fingerprint/env_aws_test.go b/client/fingerprint/env_aws_test.go index 16bb464f0..93198978f 100644 --- a/client/fingerprint/env_aws_test.go +++ b/client/fingerprint/env_aws_test.go @@ -5,17 +5,18 @@ import ( "fmt" "net/http" "net/http/httptest" - "os" "testing" "github.com/hashicorp/nomad/client/config" "github.com/hashicorp/nomad/helper/testlog" "github.com/hashicorp/nomad/nomad/structs" + "github.com/stretchr/testify/require" ) func TestEnvAWSFingerprint_nonAws(t *testing.T) { - os.Setenv("AWS_ENV_URL", "http://127.0.0.1/latest/meta-data/") f := NewEnvAWSFingerprint(testlog.HCLogger(t)) + f.(*EnvAWSFingerprint).endpoint = "http://127.0.0.1/latest" + node := &structs.Node{ Attributes: make(map[string]string), } @@ -23,43 +24,25 @@ func TestEnvAWSFingerprint_nonAws(t *testing.T) { request := &FingerprintRequest{Config: &config.Config{}, Node: node} var response FingerprintResponse err := f.Fingerprint(request, &response) - if err != nil { - t.Fatalf("err: %v", err) - } - - if len(response.Attributes) > 0 { - t.Fatalf("Should not apply") - } + require.NoError(t, err) + require.Empty(t, response.Attributes) } func TestEnvAWSFingerprint_aws(t *testing.T) { + endpoint, cleanup := startFakeEC2Metadata(t) + defer cleanup() + f := NewEnvAWSFingerprint(testlog.HCLogger(t)) + f.(*EnvAWSFingerprint).endpoint = endpoint + node := &structs.Node{ Attributes: make(map[string]string), } - // configure mock server with fixture routes, data - routes := routes{} - if err := json.Unmarshal([]byte(aws_routes), &routes); err != nil { - t.Fatalf("Failed to unmarshal JSON in AWS ENV test: %s", err) - } - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - for _, e := range routes.Endpoints { - if r.RequestURI == e.Uri { - w.Header().Set("Content-Type", e.ContentType) - fmt.Fprintln(w, e.Body) - } - } - })) - defer ts.Close() - os.Setenv("AWS_ENV_URL", ts.URL+"/latest/meta-data/") - request := &FingerprintRequest{Config: &config.Config{}, Node: node} var response FingerprintResponse err := f.Fingerprint(request, &response) - if err != nil { - t.Fatalf("err: %v", err) - } + require.NoError(t, err) keys := []string{ "platform.aws.ami-id", @@ -78,9 +61,7 @@ func TestEnvAWSFingerprint_aws(t *testing.T) { assertNodeAttributeContains(t, response.Attributes, k) } - if len(response.Links) == 0 { - t.Fatalf("Empty links for Node in AWS Fingerprint test") - } + require.NotEmpty(t, response.Links) // confirm we have at least instance-id and ami-id for _, k := range []string{"aws.ec2"} { @@ -88,6 +69,115 @@ func TestEnvAWSFingerprint_aws(t *testing.T) { } } +func TestNetworkFingerprint_AWS(t *testing.T) { + endpoint, cleanup := startFakeEC2Metadata(t) + defer cleanup() + + f := NewEnvAWSFingerprint(testlog.HCLogger(t)) + f.(*EnvAWSFingerprint).endpoint = endpoint + + node := &structs.Node{ + Attributes: make(map[string]string), + } + + request := &FingerprintRequest{Config: &config.Config{}, Node: node} + var response FingerprintResponse + err := f.Fingerprint(request, &response) + require.NoError(t, err) + + assertNodeAttributeContains(t, response.Attributes, "unique.network.ip-address") + + require.NotNil(t, response.NodeResources) + require.Len(t, response.NodeResources.Networks, 1) + + // Test at least the first Network Resource + net := response.NodeResources.Networks[0] + require.NotEmpty(t, net.IP, "Expected Network Resource to have an IP") + require.NotEmpty(t, net.CIDR, "Expected Network Resource to have a CIDR") + require.NotEmpty(t, net.Device, "Expected Network Resource to have a Device Name") +} + +func TestNetworkFingerprint_AWS_network(t *testing.T) { + endpoint, cleanup := startFakeEC2Metadata(t) + defer cleanup() + + f := NewEnvAWSFingerprint(testlog.HCLogger(t)) + f.(*EnvAWSFingerprint).endpoint = endpoint + + { + node := &structs.Node{ + Attributes: make(map[string]string), + } + + request := &FingerprintRequest{Config: &config.Config{}, Node: node} + var response FingerprintResponse + err := f.Fingerprint(request, &response) + require.NoError(t, err) + + require.True(t, response.Detected, "expected response to be applicable") + + assertNodeAttributeContains(t, response.Attributes, "unique.network.ip-address") + + require.NotNil(t, response.NodeResources) + require.Len(t, response.NodeResources.Networks, 1) + + // Test at least the first Network Resource + net := response.NodeResources.Networks[0] + require.NotEmpty(t, net.IP, "Expected Network Resource to have an IP") + require.NotEmpty(t, net.CIDR, "Expected Network Resource to have a CIDR") + require.NotEmpty(t, net.Device, "Expected Network Resource to have a Device Name") + require.Equal(t, 1000, net.MBits) + } + + // Try again this time setting a network speed in the config + { + node := &structs.Node{ + Attributes: make(map[string]string), + } + + cfg := &config.Config{ + NetworkSpeed: 10, + } + + request := &FingerprintRequest{Config: cfg, Node: node} + var response FingerprintResponse + err := f.Fingerprint(request, &response) + require.NoError(t, err) + + assertNodeAttributeContains(t, response.Attributes, "unique.network.ip-address") + + require.NotNil(t, response.NodeResources) + require.Len(t, response.NodeResources.Networks, 1) + + // Test at least the first Network Resource + net := response.NodeResources.Networks[0] + require.NotEmpty(t, net.IP, "Expected Network Resource to have an IP") + require.NotEmpty(t, net.CIDR, "Expected Network Resource to have a CIDR") + require.NotEmpty(t, net.Device, "Expected Network Resource to have a Device Name") + require.Equal(t, 10, net.MBits) + } +} + +/// Utility functions for tests + +func startFakeEC2Metadata(t *testing.T) (endpoint string, cleanup func()) { + routes := routes{} + if err := json.Unmarshal([]byte(aws_routes), &routes); err != nil { + t.Fatalf("Failed to unmarshal JSON in AWS ENV test: %s", err) + } + + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + for _, e := range routes.Endpoints { + if r.RequestURI == e.Uri { + w.Header().Set("Content-Type", e.ContentType) + fmt.Fprintln(w, e.Body) + } + } + })) + + return ts.URL + "/latest", ts.Close +} + type routes struct { Endpoints []*endpoint `json:"endpoints"` } @@ -148,168 +238,3 @@ const aws_routes = ` ] } ` - -func TestNetworkFingerprint_AWS(t *testing.T) { - // configure mock server with fixture routes, data - routes := routes{} - if err := json.Unmarshal([]byte(aws_routes), &routes); err != nil { - t.Fatalf("Failed to unmarshal JSON in AWS ENV test: %s", err) - } - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - for _, e := range routes.Endpoints { - if r.RequestURI == e.Uri { - w.Header().Set("Content-Type", e.ContentType) - fmt.Fprintln(w, e.Body) - } - } - })) - - defer ts.Close() - os.Setenv("AWS_ENV_URL", ts.URL+"/latest/meta-data/") - - f := NewEnvAWSFingerprint(testlog.HCLogger(t)) - node := &structs.Node{ - Attributes: make(map[string]string), - } - - request := &FingerprintRequest{Config: &config.Config{}, Node: node} - var response FingerprintResponse - err := f.Fingerprint(request, &response) - if err != nil { - t.Fatalf("err: %v", err) - } - - assertNodeAttributeContains(t, response.Attributes, "unique.network.ip-address") - - if response.NodeResources == nil || len(response.NodeResources.Networks) == 0 { - t.Fatal("Expected to find Network Resources") - } - - // Test at least the first Network Resource - net := response.NodeResources.Networks[0] - if net.IP == "" { - t.Fatal("Expected Network Resource to have an IP") - } - if net.CIDR == "" { - t.Fatal("Expected Network Resource to have a CIDR") - } - if net.Device == "" { - t.Fatal("Expected Network Resource to have a Device Name") - } -} - -func TestNetworkFingerprint_AWS_network(t *testing.T) { - // configure mock server with fixture routes, data - routes := routes{} - if err := json.Unmarshal([]byte(aws_routes), &routes); err != nil { - t.Fatalf("Failed to unmarshal JSON in AWS ENV test: %s", err) - } - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - for _, e := range routes.Endpoints { - if r.RequestURI == e.Uri { - w.Header().Set("Content-Type", e.ContentType) - fmt.Fprintln(w, e.Body) - } - } - })) - - defer ts.Close() - os.Setenv("AWS_ENV_URL", ts.URL+"/latest/meta-data/") - - f := NewEnvAWSFingerprint(testlog.HCLogger(t)) - { - node := &structs.Node{ - Attributes: make(map[string]string), - } - - request := &FingerprintRequest{Config: &config.Config{}, Node: node} - var response FingerprintResponse - err := f.Fingerprint(request, &response) - if err != nil { - t.Fatalf("err: %v", err) - } - - if !response.Detected { - t.Fatalf("expected response to be applicable") - } - - assertNodeAttributeContains(t, response.Attributes, "unique.network.ip-address") - - if response.NodeResources == nil || len(response.NodeResources.Networks) == 0 { - t.Fatal("Expected to find Network Resources") - } - - // Test at least the first Network Resource - net := response.NodeResources.Networks[0] - if net.IP == "" { - t.Fatal("Expected Network Resource to have an IP") - } - if net.CIDR == "" { - t.Fatal("Expected Network Resource to have a CIDR") - } - if net.Device == "" { - t.Fatal("Expected Network Resource to have a Device Name") - } - if net.MBits != 1000 { - t.Fatalf("Expected Network Resource to have speed %d; got %d", 1000, net.MBits) - } - } - - // Try again this time setting a network speed in the config - { - node := &structs.Node{ - Attributes: make(map[string]string), - } - - cfg := &config.Config{ - NetworkSpeed: 10, - } - - request := &FingerprintRequest{Config: cfg, Node: node} - var response FingerprintResponse - err := f.Fingerprint(request, &response) - if err != nil { - t.Fatalf("err: %v", err) - } - - assertNodeAttributeContains(t, response.Attributes, "unique.network.ip-address") - - if response.NodeResources == nil || len(response.NodeResources.Networks) == 0 { - t.Fatal("Expected to find Network Resources") - } - - // Test at least the first Network Resource - net := response.NodeResources.Networks[0] - if net.IP == "" { - t.Fatal("Expected Network Resource to have an IP") - } - if net.CIDR == "" { - t.Fatal("Expected Network Resource to have a CIDR") - } - if net.Device == "" { - t.Fatal("Expected Network Resource to have a Device Name") - } - if net.MBits != 10 { - t.Fatalf("Expected Network Resource to have speed %d; got %d", 10, net.MBits) - } - } -} - -func TestNetworkFingerprint_notAWS(t *testing.T) { - os.Setenv("AWS_ENV_URL", "http://127.0.0.1/latest/meta-data/") - f := NewEnvAWSFingerprint(testlog.HCLogger(t)) - node := &structs.Node{ - Attributes: make(map[string]string), - } - - request := &FingerprintRequest{Config: &config.Config{}, Node: node} - var response FingerprintResponse - err := f.Fingerprint(request, &response) - if err != nil { - t.Fatalf("err: %v", err) - } - - if len(response.Attributes) > 0 { - t.Fatalf("Should not apply") - } -} diff --git a/client/fingerprint/fingerprint_test.go b/client/fingerprint/fingerprint_test.go index 186a52f13..ee03643c0 100644 --- a/client/fingerprint/fingerprint_test.go +++ b/client/fingerprint/fingerprint_test.go @@ -7,65 +7,37 @@ import ( "github.com/hashicorp/nomad/client/config" "github.com/hashicorp/nomad/nomad/structs" + "github.com/stretchr/testify/require" ) func assertFingerprintOK(t *testing.T, fp Fingerprint, node *structs.Node) *FingerprintResponse { request := &FingerprintRequest{Config: new(config.Config), Node: node} var response FingerprintResponse err := fp.Fingerprint(request, &response) - if err != nil { - t.Fatalf("Failed to fingerprint: %s", err) - } + require.NoError(t, err) - if len(response.Attributes) == 0 { - t.Fatalf("Failed to apply node attributes") - } + require.NotEmpty(t, response.Attributes, "Failed to apply node attributes") return &response } func assertNodeAttributeContains(t *testing.T, nodeAttributes map[string]string, attribute string) { - if nodeAttributes == nil { - t.Errorf("expected an initialized map for node attributes") - return - } + require.NotNil(t, nodeAttributes, "expected an initialized map for node attributes") - actual, found := nodeAttributes[attribute] - if !found { - t.Errorf("Expected to find Attribute `%s`\n\n[DEBUG] %#v", attribute, nodeAttributes) - return - } - if actual == "" { - t.Errorf("Expected non-empty Attribute value for `%s`\n\n[DEBUG] %#v", attribute, nodeAttributes) - } + require.Contains(t, nodeAttributes, attribute) + require.NotEmpty(t, nodeAttributes[attribute]) } func assertNodeAttributeEquals(t *testing.T, nodeAttributes map[string]string, attribute string, expected string) { - if nodeAttributes == nil { - t.Errorf("expected an initialized map for node attributes") - return - } - actual, found := nodeAttributes[attribute] - if !found { - t.Errorf("Expected to find Attribute `%s`; unable to check value\n\n[DEBUG] %#v", attribute, nodeAttributes) - return - } - if expected != actual { - t.Errorf("Expected `%s` Attribute to be `%s`, found `%s`\n\n[DEBUG] %#v", attribute, expected, actual, nodeAttributes) - } + require.NotNil(t, nodeAttributes, "expected an initialized map for node attributes") + + require.Contains(t, nodeAttributes, attribute) + require.Equal(t, expected, nodeAttributes[attribute]) } func assertNodeLinksContains(t *testing.T, nodeLinks map[string]string, link string) { - if nodeLinks == nil { - t.Errorf("expected an initialized map for node links") - return - } - actual, found := nodeLinks[link] - if !found { - t.Errorf("Expected to find Link `%s`\n\n[DEBUG]", link) - return - } - if actual == "" { - t.Errorf("Expected non-empty Link value for `%s`\n\n[DEBUG]", link) - } + require.NotNil(t, nodeLinks, "expected an initialized map for node links") + + require.Contains(t, nodeLinks, link) + require.NotEmpty(t, nodeLinks[link]) } diff --git a/client/fs_endpoint_test.go b/client/fs_endpoint_test.go index 40a9b451d..f6f28422f 100644 --- a/client/fs_endpoint_test.go +++ b/client/fs_endpoint_test.go @@ -1884,13 +1884,14 @@ func TestFS_logsImpl_NoFollow(t *testing.T) { }() // Start streaming logs - go func() { - if err := c.endpoints.FileSystem.logsImpl( - context.Background(), false, false, 0, - OriginStart, task, logType, ad, frames); err != nil { - t.Fatalf("logs() failed: %v", err) - } - }() + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + + if err := c.endpoints.FileSystem.logsImpl( + ctx, false, false, 0, + OriginStart, task, logType, ad, frames); err != nil { + t.Fatalf("logsImpl failed: %v", err) + } select { case <-resultCh: diff --git a/command/agent/metrics_endpoint_test.go b/command/agent/metrics_endpoint_test.go index d97fe2e69..b10e41708 100644 --- a/command/agent/metrics_endpoint_test.go +++ b/command/agent/metrics_endpoint_test.go @@ -91,6 +91,8 @@ func TestHTTP_FreshClientAllocMetrics(t *testing.T) { require.Fail("timed-out waiting for job to complete") }) + nodeID := s.client.NodeID() + // wait for metrics to converge var pending, running, terminal float32 = -1.0, -1.0, -1.0 testutil.WaitForResultRetries(100, func() (bool, error) { @@ -106,6 +108,13 @@ func TestHTTP_FreshClientAllocMetrics(t *testing.T) { metrics := obj.(metrics.MetricsSummary) for _, g := range metrics.Gauges { + + // ignore client metrics belonging to other test nodes + // from other tests that contaminate go-metrics reporting + if g.DisplayLabels["node_id"] != nodeID { + continue + } + if strings.HasSuffix(g.Name, "client.allocations.pending") { pending = g.Value } diff --git a/demo/vagrant/Vagrantfile b/demo/vagrant/Vagrantfile index 17289a4eb..1dedf96d8 100644 --- a/demo/vagrant/Vagrantfile +++ b/demo/vagrant/Vagrantfile @@ -5,6 +5,7 @@ $script = <