diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index bcec6dc..25f1bd1 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -20,7 +20,7 @@ jobs: fetch-depth: 0 - name: ci/check-style - run: make check-style + run: make lint test: runs-on: ubuntu-latest @@ -32,14 +32,3 @@ jobs: - name: ci/test run: make test - - build: - runs-on: ubuntu-latest - steps: - - name: Checkout repo - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - - name: ci/build - run: make docker-build diff --git a/.gitignore b/.gitignore index 7f2411a..f9152d8 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ # VSCode .history +.vscode # Bin build/_output/* diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..0db7b3e --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,9 @@ +--- + +include: + - project: 'mattermost/ci/pipelines' + ref: 'master' + file: 'default.yml' + - project: 'mattermost/ci/pipelines' + ref: 'master' + file: '${CI_PROJECT_NAME}/gitlab-ci.yml' diff --git a/Makefile b/Makefile index b943b16..ee2c6c1 100644 --- a/Makefile +++ b/Makefile @@ -1,139 +1,394 @@ -# Binary -TAG ?= dev-local -BUILD_HASH := $(shell git rev-parse HEAD) -BUILD_HASH_SHORT = $(shell git rev-parse --short HEAD) -BUILD_TIME := $(shell date -u +%Y%m%d.%H%M%S) -LDFLAGS += -X "github.com/mattermost/ponos/function.BuildDate=$(BUILD_DATE)" -LDFLAGS += -X "github.com/mattermost/ponos/function.BuildHash=$(BUILD_HASH)" -LDFLAGS += -X "github.com/mattermost/ponos/function.BuildHashShort=$(BUILD_HASH_SHORT)" +# ==================================================================================== +# Variables -## Golang -GO ?= go -GO_TEST_FLAGS ?= -race +## General Variables +# Branch Variables +PROTECTED_BRANCH := main +CURRENT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD) +# Use repository name as application name +APP_NAME := $(shell basename -s .git `git config --get remote.origin.url`) +# Get current commit +APP_COMMIT := $(shell git log --pretty=format:'%h' -n 1) +# Check if we are in protected branch, if yes use `protected_branch_name-sha` as app version. +# Else check if we are in a release tag, if yes use the tag as app version, else use `dev-sha` as app version. +APP_VERSION ?= $(shell if [ $(PROTECTED_BRANCH) = $(CURRENT_BRANCH) ]; then echo $(PROTECTED_BRANCH); else (git describe --abbrev=0 --exact-match --tags 2>/dev/null || echo dev-$(APP_COMMIT)) ; fi) -## Binaries. -TOOLS_BIN_DIR := $(abspath bin) +# Get current date and format like: 2022-04-27 11:32 +BUILD_DATE := $(shell date +%Y-%m-%d\ %H:%M) -GO_IMAGE_LINT = "golangci/golangci-lint:v1.45.0" +## General Configuration Variables +# We don't need make's built-in rules. +MAKEFLAGS += --no-builtin-rules +# Be pedantic about undefined variables. +MAKEFLAGS += --warn-undefined-variables +# Set help as default target +.DEFAULT_GOAL := help -OUTDATED_VER := master -OUTDATED_BIN := go-mod-outdated -OUTDATED_GEN := $(TOOLS_BIN_DIR)/$(OUTDATED_BIN) +# App Code location +CONFIG_APP_CODE += ./cmd/${BUILD_SERVICE} +BUILD_SERVICE ?= app -## Docker -DOCKER_IMAGE ?= mattermost/ponos-service:${TAG} +## Docker Variables +# Docker executable +DOCKER := $(shell which docker) +# Dockerfile's location +DOCKER_FILE += ./build/Dockerfile +# Docker options to inherit for all docker run commands +DOCKER_OPTS += --rm -u $$(id -u):$$(id -g) --platform "linux/amd64" +# Registry to upload images +DOCKER_REGISTRY ?= docker.io +DOCKER_REGISTRY_REPO ?= mattermost/${APP_NAME}-daily +# Registry credentials +DOCKER_USER ?= user +DOCKER_PASSWORD ?= password +## Docker Images +DOCKER_IMAGE_GO += "golang:${GO_VERSION}@sha256:79138c839452a2a9d767f0bba601bd5f63af4a1d8bb645bf6141bff8f4f33bb8" +DOCKER_IMAGE_GOLINT += "golangci/golangci-lint:v1.45.2@sha256:e84b639c061c8888be91939c78dae9b1525359954e405ab0d9868a46861bd21b" +DOCKER_IMAGE_DOCKERLINT += "hadolint/hadolint:v2.9.2@sha256:d355bd7df747a0f124f3b5e7b21e9dafd0cb19732a276f901f0fdee243ec1f3b" +DOCKER_IMAGE_COSIGN += "bitnami/cosign:1.8.0@sha256:8c2c61c546258fffff18b47bb82a65af6142007306b737129a7bd5429d53629a" +DOCKER_IMAGE_GH_CLI += "registry.internal.mattermost.com/images/build-ci:3.16.0@sha256:f6a229a9ababef3c483f237805ee4c3dbfb63f5de4fbbf58f4c4b6ed8fcd34b6" +DOCKER_IMAGE_AWS_CLI += "amazon/aws-cli:2.7.9@sha256:c95ab2277ee36252dd31b7c50a6a3e82eb558089618bfd22308f8e0da3d753c3" -## Docker Build Versions -DOCKER_BUILD_IMAGE = golang:1.17.9@sha256:55759506d9a7b33b28117977170db9713903799c110db99af2c15a3f603602af -DOCKER_BASE_IMAGE = gcr.io/distroless/static@sha256:d6fa9db9548b5772860fecddb11d84f9ebd7e0321c0cb3c02870402680cc315f +## Cosign Variables +# The public key +COSIGN_PUBLIC_KEY ?= akey +# The private key +COSIGN_KEY ?= akey +# The passphrase used to decrypt the private key +COSIGN_PASSWORD ?= password -## Tools version -TERRAFORM_VERSION=1.1.7 -################################################################################ +## AWS Variables +AWS_BUCKET_NAME ?= abucket +# Secrets +AWS_ACCESS_KEY_ID ?= akey +AWS_SECRET_ACCESS_KEY ?= akey +AWS_REGION ?= aregion -.PHONY: all -## all: builds and pushes the docker image -all: lint test docker-build docker-push +## Go Variables +# Go executable +GO := $(shell which go) +# Extract GO version from go.mod file +GO_VERSION ?= $(shell grep -E '^go' go.mod | awk {'print $$2'}) +# LDFLAGS +GO_LDFLAGS += -X "github.com/mattermost/${APP_NAME}/function.BuildHash=$(APP_COMMIT)" +GO_LDFLAGS += -X "github.com/mattermost/${APP_NAME}/function.buildVersion=$(APP_VERSION)" +GO_LDFLAGS += -X "github.com/mattermost/${APP_NAME}/function.BuildDate=$(BUILD_DATE)" +GO_LDFLAGS += -X "github.com/mattermost/${APP_NAME}/function.goVersion=$(GO_VERSION)" +# Architectures to build for +GO_BUILD_PLATFORMS ?= linux-amd64 linux-arm64 darwin-amd64 darwin-arm64 freebsd-amd64 +GO_BUILD_PLATFORMS_ARTIFACTS = $(foreach cmd,$(addprefix go-build/,${APP_NAME}),$(addprefix $(cmd)-,$(GO_BUILD_PLATFORMS))) +# Build options +GO_BUILD_OPTS += -mod=readonly -trimpath +GO_TEST_OPTS += -mod=readonly -failfast -race +# Temporary folder to output compiled binaries artifacts +GO_OUT_BIN_DIR := ./dist + +## Github Variables +# A github access token that provides access to upload artifacts under releases +GITHUB_TOKEN ?= a_token +# Github organization +GITHUB_ORG := mattermost +# Most probably the name of the repo +GITHUB_REPO := ${APP_NAME} + + +# ==================================================================================== +# Colors + +BLUE := $(shell printf "\033[34m") +YELLOW := $(shell printf "\033[33m") +RED := $(shell printf "\033[31m") +GREEN := $(shell printf "\033[32m") +CYAN := $(shell printf "\033[36m") +CNone := $(shell printf "\033[0m") + +# ==================================================================================== +# Logger + +TIME_LONG = `date +%Y-%m-%d' '%H:%M:%S` +TIME_SHORT = `date +%H:%M:%S` +TIME = $(TIME_SHORT) + +INFO = echo ${TIME} ${BLUE}[ .. ]${CNone} +WARN = echo ${TIME} ${YELLOW}[WARN]${CNone} +ERR = echo ${TIME} ${RED}[FAIL]${CNone} +OK = echo ${TIME} ${GREEN}[ OK ]${CNone} +FAIL = (echo ${TIME} ${RED}[FAIL]${CNone} && false) + +# ==================================================================================== +# Verbosity control hack + +VERBOSE ?= 0 +AT_0 := @ +AT_1 := +AT = $(AT_$(VERBOSE)) + +# ==================================================================================== +# Targets + +help: ## to get help + @echo "Usage:" + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) |\ + awk 'BEGIN {FS = ":.*?## "}; {printf "make ${CYAN}%-30s${CNone} %s\n", $$1, $$2}' .PHONY: build -## build: builds linux binary -build: - @echo Building binary for linux for ${BUILD_SERVICE} - GOOS=linux GOARCH=amd64 CGO_ENABLED=0 $(GO) build -ldflags '$(LDFLAGS)' -trimpath -o build/_output/bin/ponos-${BUILD_SERVICE}-linux-amd64 ./cmd/${BUILD_SERVICE} +build: go-build-docker ## to build + +.PHONY: release +release: build github-release dist s3-release ## to build and release artifacts + +.PHONY: package +package: docker-login docker-build docker-push ## to build, package and push the artifact to a container registry + +.PHONY: sign +sign: docker-sign docker-verify ## to sign the artifact and perform verification + +.PHONY: lint +lint: go-lint docker-lint ## to lint -.PHONY: build-local -## build-local: build the executable for local usage -build-local: - @echo Building for local use only - $(GO) build -o build/_output/bin/ponos-${BUILD_SERVICE} ./cmd/${BUILD_SERVICE} +.PHONY: test +test: go-test ## to test .PHONY: docker-build -## docker-build: builds the docker image -docker-build: - @echo Building Ponos Service Docker Image - docker build \ - --build-arg DOCKER_BUILD_IMAGE=$(DOCKER_BUILD_IMAGE) \ - --build-arg DOCKER_BASE_IMAGE=$(DOCKER_BASE_IMAGE) \ - . -f build/Dockerfile -t $(DOCKER_IMAGE) +docker-build: ## to build the docker image + @$(INFO) Performing Docker build ${APP_NAME}:${APP_VERSION} + $(AT)$(DOCKER) build \ + --build-arg GO_IMAGE=${DOCKER_IMAGE_GO} \ + --build-arg BUILD_SERVICE=${BUILD_SERVICE} \ + -f ${DOCKER_FILE} . \ + -t ${APP_NAME}:${APP_VERSION} || ${FAIL} + @$(OK) Performing Docker build ${APP_NAME}:${APP_VERSION} + +.PHONY: docker-push +docker-push: ## to push the docker image + @$(INFO) Pushing to registry... + $(AT)$(DOCKER) tag ${APP_NAME}:${APP_VERSION} $(DOCKER_REGISTRY)/${DOCKER_REGISTRY_REPO}:${APP_VERSION} || ${FAIL} + $(AT)$(DOCKER) push $(DOCKER_REGISTRY)/${DOCKER_REGISTRY_REPO}:${APP_VERSION} || ${FAIL} +# if we are on a latest semver APP_VERSION tag, also push latest +ifneq ($(shell echo $(APP_VERSION) | egrep '^v([0-9]+\.){0,2}(\*|[0-9]+)'),) + ifeq ($(shell git tag -l --sort=v:refname | tail -n1),$(APP_VERSION)) + $(AT)$(DOCKER) tag ${APP_NAME}:${APP_VERSION} $(DOCKER_REGISTRY)/${DOCKER_REGISTRY_REPO}:latest || ${FAIL} + $(AT)$(DOCKER) push $(DOCKER_REGISTRY)/${DOCKER_REGISTRY_REPO}:latest || ${FAIL} + endif +endif + @$(OK) Pushing to registry $(DOCKER_REGISTRY)/${DOCKER_REGISTRY_REPO}:${APP_VERSION} + +.PHONY: docker-sign +docker-sign: ## to sign the docker image + @$(INFO) Signing the docker image... + $(AT)echo "$${COSIGN_KEY}" > cosign.key && \ + $(DOCKER) run ${DOCKER_OPTS} \ + --entrypoint '/bin/sh' \ + -v $(PWD):/app -w /app \ + -e COSIGN_PASSWORD=${COSIGN_PASSWORD} \ + -e HOME="/tmp" \ + ${DOCKER_IMAGE_COSIGN} \ + -c \ + "echo Signing... && \ + cosign login $(DOCKER_REGISTRY) -u ${DOCKER_USER} -p ${DOCKER_PASSWORD} && \ + cosign sign --key cosign.key $(DOCKER_REGISTRY)/${DOCKER_REGISTRY_REPO}:${APP_VERSION}" || ${FAIL} +# if we are on a latest semver APP_VERSION tag, also sign latest tag +ifneq ($(shell echo $(APP_VERSION) | egrep '^v([0-9]+\.){0,2}(\*|[0-9]+)'),) + ifeq ($(shell git tag -l --sort=v:refname | tail -n1),$(APP_VERSION)) + $(DOCKER) run ${DOCKER_OPTS} \ + --entrypoint '/bin/sh' \ + -v $(PWD):/app -w /app \ + -e COSIGN_PASSWORD=${COSIGN_PASSWORD} \ + -e HOME="/tmp" \ + ${DOCKER_IMAGE_COSIGN} \ + -c \ + "echo Signing... && \ + cosign login $(DOCKER_REGISTRY) -u ${DOCKER_USER} -p ${DOCKER_PASSWORD} && \ + cosign sign --key cosign.key $(DOCKER_REGISTRY)/${DOCKER_REGISTRY_REPO}:latest" || ${FAIL} + endif +endif + $(AT)rm -f cosign.key || ${FAIL} + @$(OK) Signing the docker image: $(DOCKER_REGISTRY)/${DOCKER_REGISTRY_REPO}:${APP_VERSION} + +.PHONY: docker-verify +docker-verify: ## to verify the docker image + @$(INFO) Verifying the published docker image... + $(AT)echo "$${COSIGN_PUBLIC_KEY}" > cosign_public.key && \ + $(DOCKER) run ${DOCKER_OPTS} \ + --entrypoint '/bin/sh' \ + -v $(PWD):/app -w /app \ + ${DOCKER_IMAGE_COSIGN} \ + -c \ + "echo Verifying... && \ + cosign verify --key cosign_public.key $(DOCKER_REGISTRY)/${DOCKER_REGISTRY_REPO}:${APP_VERSION}" || ${FAIL} +# if we are on a latest semver APP_VERSION tag, also verify latest tag +ifneq ($(shell echo $(APP_VERSION) | egrep '^v([0-9]+\.){0,2}(\*|[0-9]+)'),) + ifeq ($(shell git tag -l --sort=v:refname | tail -n1),$(APP_VERSION)) + $(DOCKER) run ${DOCKER_OPTS} \ + --entrypoint '/bin/sh' \ + -v $(PWD):/app -w /app \ + ${DOCKER_IMAGE_COSIGN} \ + -c \ + "echo Verifying... && \ + cosign verify --key cosign_public.key $(DOCKER_REGISTRY)/${DOCKER_REGISTRY_REPO}:latest" || ${FAIL} + endif +endif + $(AT)rm -f cosign_public.key || ${FAIL} + @$(OK) Verifying the published docker image: $(DOCKER_REGISTRY)/${DOCKER_REGISTRY_REPO}:${APP_VERSION} + +.PHONY: docker-sbom +docker-sbom: ## to print a sbom report + @$(INFO) Performing Docker sbom report... + $(AT)$(DOCKER) sbom ${APP_NAME}:${APP_VERSION} || ${FAIL} + @$(OK) Performing Docker sbom report + +.PHONY: docker-scan +docker-scan: ## to print a vulnerability report + @$(INFO) Performing Docker scan report... + $(AT)$(DOCKER) scan ${APP_NAME}:${APP_VERSION} || ${FAIL} + @$(OK) Performing Docker scan report + +.PHONY: docker-lint +docker-lint: ## to lint the Dockerfile + @$(INFO) Dockerfile linting... + $(AT)$(DOCKER) run -i ${DOCKER_OPTS} \ + ${DOCKER_IMAGE_DOCKERLINT} \ + < ${DOCKER_FILE} || ${FAIL} + @$(OK) Dockerfile linting .PHONY: docker-login -## docker-login: docker login to registry -docker-login: - @echo Docker login - echo $(DOCKER_PASSWORD) | docker login --username $(DOCKER_USERNAME) --password-stdin +docker-login: ## to login to a container registry + @$(INFO) Dockerd login to container registry ${DOCKER_REGISTRY}... + $(AT) echo "${DOCKER_PASSWORD}" | $(DOCKER) login --password-stdin -u ${DOCKER_USER} $(DOCKER_REGISTRY) || ${FAIL} + @$(OK) Dockerd login to container registry ${DOCKER_REGISTRY}... -.PHONY: docker-push -## docker-push: push the docker image -docker-push: docker-login - @echo Taging Pushing Ponos Serve Docker Image - docker push mattermost/mattermost-ponos-service:${TAG} - -.PHONY: check-modules -## check-modules: Check outdated modules -check-modules: - @echo Checking outdated modules - $(GO) list -u -m -json all | docker run --rm -i psampaz/go-mod-outdated - -.PHONY: check-style -## check-style: Runs govet and gofmt against all packages. -check-style: govet lint - @echo Checking for style guide compliance +go-build: $(GO_BUILD_PLATFORMS_ARTIFACTS) ## to build binaries -.PHONY: clean -## clean: deletes all -clean: - rm -rf build/_output/bin/ +.PHONY: go-build +go-build/%: + @$(INFO) go build $*... + $(AT)target="$*"; \ + command="$${target%%-*}"; \ + platform_ext="$${target#*-}"; \ + platform="$${platform_ext%.*}"; \ + export GOOS="$${platform%%-*}"; \ + export GOARCH="$${platform#*-}"; \ + echo export GOOS=$${GOOS}; \ + echo export GOARCH=$${GOARCH}; \ + CGO_ENABLED=0 \ + $(GO) build ${GO_BUILD_OPTS} \ + -ldflags '${GO_LDFLAGS}' \ + -o ${GO_OUT_BIN_DIR}/${BUILD_SERVICE}-$* \ + ${CONFIG_APP_CODE} || ${FAIL} + @$(OK) go build $* -.PHONY: dist -## dist-aws: creates the bundle file for AWS Lambda deployments -dist: build - @echo Building dist for AWS Lambda - cp -r static dist - cp manifest.json dist/ - cp build/_output/bin/ponos-app-linux-amd64 dist/ponos - cd dist/; zip -qr go-function ponos; zip -r bundle.zip go-function.zip manifest.json static - -.PHONY: get-terraform -## get-terraform: download terraform only if it's not available. Used in the docker build -get-terraform: - @if [ ! -f build/terraform ]; then \ - curl -Lo build/terraform.zip https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip && cd build && unzip terraform.zip &&\ - chmod +x terraform && rm terraform.zip;\ - fi - -.PHONY: vet -## govet: Runs govet against all packages. -govet: - @echo Running govet - $(GO) vet ./... - @echo Govet success +.PHONY: go-build-docker +go-build-docker: # to build binaries under a controlled docker dedicated go container using DOCKER_IMAGE_GO + @$(INFO) go build docker + $(AT)$(DOCKER) run ${DOCKER_OPTS} \ + -v $(PWD):/app -w /app \ + -e GOCACHE="/tmp" \ + $(DOCKER_IMAGE_GO) \ + /bin/sh -c \ + "cd /app && \ + make go-build" || ${FAIL} + @$(OK) go build docker -.PHONY: lint -## lint: Run golangci-lint on codebase -lint: - @echo Running lint with GolangCI - docker run --rm -v $(PWD):/app -w /app ${GO_IMAGE_LINT} golangci-lint run --timeout=5m +.PHONY: go-run +go-run: ## to run locally for development + @$(INFO) running locally... + $(AT)$(GO) run ${GO_BUILD_OPTS} ${CONFIG_APP_CODE} || ${FAIL} + @$(OK) running locally -.PHONY: run -## run: runs the service -run: build-local - @echo Running chaos engine with debug - build/_output/bin/ponos-${BUILD_SERVICE} +.PHONY: go-test +go-test: ## to run tests + @$(INFO) testing... + $(AT)$(DOCKER) run ${DOCKER_OPTS} \ + -v $(PWD):/app -w /app \ + -e GOCACHE="/tmp" \ + $(DOCKER_IMAGE_GO) \ + /bin/sh -c \ + "cd /app && \ + go test ${GO_TEST_OPTS} ./... " || ${FAIL} + @$(OK) testing -.PHONY: test -## test: tests all packages -test: - @echo Running tests - $(GO) test $(GO_TEST_FLAGS) ./... - -.PHONY: vendor -## vendor: create a vendor folder -vendor: - @echo Running vendor - go mod vendor - -.PHONY: help -## help: prints this help message -help: - @echo "Usage:" - @sed -n 's/^##//p' ${MAKEFILE_LIST} | column -t -s ':' | sed -e 's/^/ /' +.PHONY: go-mod-check +go-mod-check: ## to check go mod files consistency + @$(INFO) Checking go mod files consistency... + $(AT)$(GO) mod tidy + $(AT)git --no-pager diff --exit-code go.mod go.sum || \ + (${WARN} Please run "go mod tidy" and commit the changes in go.mod and go.sum. && ${FAIL} ; exit 128 ) + @$(OK) Checking go mod files consistency + +.PHONY: go-update-dependencies +go-update-dependencies: ## to update go dependencies (vendor) + @$(INFO) updating go dependencies... + $(AT)$(GO) get -u ./... && \ + $(AT)$(GO) mod vendor && \ + $(AT)$(GO) mod tidy || ${FAIL} + @$(OK) updating go dependencies + +.PHONY: go-lint +go-lint: ## to lint go code + @$(INFO) App linting... + $(AT)GOCACHE="/tmp" $(DOCKER) run ${DOCKER_OPTS} \ + -v $(PWD):/app -w /app \ + -e GOCACHE="/tmp" \ + -e GOLANGCI_LINT_CACHE="/tmp" \ + ${DOCKER_IMAGE_GOLINT} \ + golangci-lint run ./... --timeout=10m || ${FAIL} + @$(OK) App linting + +.PHONY: go-fmt +go-fmt: ## to perform formatting + @$(INFO) App code formatting... + $(AT)$(GO) fmt ./... || ${FAIL} + @$(OK) App code formatting... + +.PHONY: go-doc +go-doc: ## to generate documentation + @$(INFO) Generating Documentation... + $(AT)$(GO) run ./scripts/env_config.go ./docs/env_config.md || ${FAIL} + @$(OK) Generating Documentation + +.PHONY: github-release +github-release: ## to publish a release and relevant artifacts to GitHub + @$(INFO) Generating github-release http://github.com/$(GITHUB_ORG)/$(GITHUB_REPO)/releases/tag/$(APP_VERSION) ... +ifeq ($(shell echo $(APP_VERSION) | egrep '^v([0-9]+\.){0,2}(\*|[0-9]+)'),) + $(error "We only support releases from semver tags") +else + $(AT)$(DOCKER) run \ + -v $(PWD):/app -w /app \ + -e GITHUB_TOKEN=${GITHUB_TOKEN} \ + $(DOCKER_IMAGE_GH_CLI) \ + /bin/sh -c \ + "cd /app && \ + gh release create $(APP_VERSION) --generate-notes $(GO_OUT_BIN_DIR)/*" || ${FAIL} +endif + @$(OK) Generating github-release http://github.com/$(GITHUB_ORG)/$(GITHUB_REPO)/releases/tag/$(APP_VERSION) ... + +.PHONY: clean +clean: ## to clean-up + @$(INFO) cleaning /${GO_OUT_BIN_DIR} folder... + $(AT)rm -rf ${GO_OUT_BIN_DIR} || ${FAIL} + @$(OK) cleaning /${GO_OUT_BIN_DIR} folder + +.PHONY: dist +dist: ## to create the bundle file for AWS Lambda deployments + @$(INFO) Building dist for AWS Lambda ... + $(AT)cp -r static dist || ${FAIL} + $(AT)cp manifest.json dist/ || ${FAIL} + $(AT)mv dist/app-ponos-linux-amd64 dist/ponos || ${FAIL} + $(AT)cd dist/; zip -qr go-function ponos; zip -r bundle.zip go-function.zip manifest.json static || ${FAIL} + @$(OK) Building dist for AWS Lambda ... + +.PHONY: s3-release +s3-release: ## to publish bundle file to S3 + @$(INFO) Uploading bundle file to s3://${AWS_BUCKET_NAME}/mattermost-app-$(APP_NAME)-$(APP_VERSION).zip ... +ifeq ($(shell echo $(APP_VERSION) | egrep '^v([0-9]+\.){0,2}(\*|[0-9]+)'),) + $(error "We only support s3-release from semver tags") +else + $(AT)$(DOCKER) run ${DOCKER_OPTS} \ + -v $(PWD):/app -w /app \ + -e AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} \ + -e AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} \ + -e AWS_REGION=${AWS_REGION} \ + $(DOCKER_IMAGE_AWS_CLI) \ + s3 cp /app/dist/bundle.zip "s3://${AWS_BUCKET_NAME}/mattermost-app-$(APP_NAME)-$(APP_VERSION).zip" --cache-control "no-cache" || ${FAIL} +endif + @$(OK) Uploading bundle file to s3://${AWS_BUCKET_NAME}/mattermost-app-$(APP_NAME)-$(APP_VERSION).zip ... diff --git a/build/Dockerfile b/build/Dockerfile index 59df145..ec73fc0 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -1,22 +1,30 @@ -# Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -# See LICENSE.txt for license information. +# This dockerfile is used to build Mattermost ponos +# A multi stage build, with golang used as a builder +# and gcr.io/distroless/static as runner +ARG GO_IMAGE=golang:1.17@sha256:79138c839452a2a9d767f0bba601bd5f63af4a1d8bb645bf6141bff8f4f33bb8 +# hadolint ignore=DL3006 +FROM ${GO_IMAGE} as builder -# Build the mattermost ponos server -ARG DOCKER_BUILD_IMAGE=golang:1.17.9@sha256:55759506d9a7b33b28117977170db9713903799c110db99af2c15a3f603602af -ARG DOCKER_BASE_IMAGE=gcr.io/distroless/static@sha256:d6fa9db9548b5772860fecddb11d84f9ebd7e0321c0cb3c02870402680cc315f -ARG BUILD_VERSION +#GO_BUILD_PLATFORMS holds the platforms that we will build the docker image against +ARG GO_BUILD_PLATFORMS=linux-amd64 +ARG TERRAFORM_VERSION=1.1.7 -FROM ${DOCKER_BUILD_IMAGE} AS build -WORKDIR /opt/ponos -COPY . /opt/ponos -RUN apt-get update -yq && apt-get install -yq unzip -RUN make get-terraform +# Setup directories structure and compile +COPY . /src +WORKDIR /src ENV BUILD_SERVICE=server -RUN make build - -# Final Image -FROM ${DOCKER_BASE_IMAGE} +RUN make go-build +RUN apt-get update -yq && apt-get install --no-install-recommends -yq unzip=6.0-26 +# Setup terraform binary +WORKDIR /src/dist +RUN curl -Lo terraform.zip https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip &&\ + unzip -o terraform.zip &&\ + chmod +x terraform + +# Shrink final image since we only need the binary +# and use distroless container image as runner for security +FROM gcr.io/distroless/static@sha256:d6fa9db9548b5772860fecddb11d84f9ebd7e0321c0cb3c02870402680cc315f as runner LABEL name="Ponos" \ maintainer="cloud-team@mattermost.com" \ @@ -27,12 +35,12 @@ LABEL name="Ponos" \ io.k8s.description="Ponos is a toil work elimination tool for SRE tasks." \ io.k8s.display-name="Mattermost Ponos" -COPY --from=build /opt/ponos/build/_output/bin/ponos-server-linux-amd64 /ponos/ponos-service -COPY --from=build /opt/ponos/build/terraform /usr/local/bin/ -COPY --from=build /opt/ponos/terraform /ponos/terraform +COPY --from=builder /src/dist/server-ponos-linux-amd64 /opt/ponos/bin/ponos-service +COPY --from=builder /src/dist/terraform /usr/local/bin/ +# We should refrain from running as privileged user # Run as UID for nobody USER 65534 -WORKDIR /ponos +WORKDIR /opt/ponos/bin ENTRYPOINT ["./ponos-service"]