diff --git a/.gitignore b/.gitignore index f1c181e..5221915 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,8 @@ # Output of the go coverage tool, specifically when used with LiteIDE *.out + +/vendor +/.idea +/bin +/tmp \ No newline at end of file diff --git a/Dockerfile.builder b/Dockerfile.builder new file mode 100644 index 0000000..05c72a3 --- /dev/null +++ b/Dockerfile.builder @@ -0,0 +1,37 @@ +FROM centos:7 +LABEL maintainer "Devtools " +LABEL author "Devtools " +ENV LANG=en_US.utf8 +ARG USE_GO_VERSION_FROM_WEBSITE=0 + +# Some packages might seem weird but they are required by the RVM installer. +RUN yum install epel-release -y \ + && yum --enablerepo=centosplus --enablerepo=epel install -y \ + findutils \ + git \ + $(test "$USE_GO_VERSION_FROM_WEBSITE" != 1 && echo "golang") \ + make \ + procps-ng \ + tar \ + wget \ + which \ + && yum clean all + +RUN if [[ "$USE_GO_VERSION_FROM_WEBSITE" = 1 ]]; then cd /tmp \ + && wget https://dl.google.com/go/go1.10.linux-amd64.tar.gz \ + && echo "b5a64335f1490277b585832d1f6c7f8c6c11206cba5cd3f771dcb87b98ad1a33 go1.10.linux-amd64.tar.gz" > checksum \ + && sha256sum -c checksum \ + && tar -C /usr/local -xzf go1.10.linux-amd64.tar.gz \ + && rm -f go1.10.linux-amd64.tar.gz; \ + fi +ENV PATH=$PATH:/usr/local/go/bin + +# Get dep for Go package management and make sure the directory has full rwz permissions for non-root users +ENV GOPATH /tmp/go +RUN mkdir -p $GOPATH/bin && chmod a+rwx $GOPATH +RUN cd $GOPATH/bin \ + curl -L -s https://github.com/golang/dep/releases/download/v0.5.0/dep-linux-amd64 -o dep \ + echo "287b08291e14f1fae8ba44374b26a2b12eb941af3497ed0ca649253e21ba2f83 dep" > dep-linux-amd64.sha256 \ + sha256sum -c dep-linux-amd64.sha256 + +ENTRYPOINT ["/bin/bash"] diff --git a/Dockerfile.deploy b/Dockerfile.deploy new file mode 100644 index 0000000..cc648cd --- /dev/null +++ b/Dockerfile.deploy @@ -0,0 +1,18 @@ +FROM centos:7 +LABEL maintainer "Devtools " +LABEL author "Devtools " +ENV LANG=en_US.utf8 +ENV INSTALL_PREFIX=/usr/local/git-service + +# Create a non-root user and a group with the same name: "devconsole-operator" +ENV USER_NAME=devconsole-operator +RUN useradd --no-create-home -s /bin/bash ${USER_NAME} + +COPY bin/git-service ${INSTALL_PREFIX}/bin/git-service + +# From here onwards, any RUN, CMD, or ENTRYPOINT will be run under the following user +USER ${USER_NAME} + +WORKDIR ${INSTALL_PREFIX} + +ENTRYPOINT [ "bin/git-service" ] diff --git a/Dockerfile.deploy.rhel b/Dockerfile.deploy.rhel new file mode 100644 index 0000000..658ce73 --- /dev/null +++ b/Dockerfile.deploy.rhel @@ -0,0 +1,19 @@ +FROM quay.io/openshiftio/rhel-base-golang:latest + +LABEL maintainer "Devtools " +LABEL author "Devtools " +ENV LANG=en_US.utf8 +ENV INSTALL_PREFIX=/usr/local/git-service + +# Create a non-root user and a group with the same name: "devconsole-operator" +ENV USER_NAME=devconsole-operator +RUN useradd --no-create-home -s /bin/bash ${USER_NAME} + +COPY bin/git-service ${INSTALL_PREFIX}/bin/git-service + +# From here onwards, any RUN, CMD, or ENTRYPOINT will be run under the following user +USER ${USER_NAME} + +WORKDIR ${INSTALL_PREFIX} + +ENTRYPOINT [ "bin/git-service" ] diff --git a/Dockerfile.dev b/Dockerfile.dev new file mode 100644 index 0000000..3d0511c --- /dev/null +++ b/Dockerfile.dev @@ -0,0 +1,6 @@ +FROM centos:7 + +RUN mkdir -p /tmp/ +ADD bin/git-service / + +ENTRYPOINT ["/git-service"] diff --git a/Gopkg.lock b/Gopkg.lock new file mode 100644 index 0000000..10ef811 --- /dev/null +++ b/Gopkg.lock @@ -0,0 +1,9 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + input-imports = [] + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml new file mode 100644 index 0000000..2765dc9 --- /dev/null +++ b/Gopkg.toml @@ -0,0 +1,29 @@ +# Gopkg.toml example +# +# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html +# for detailed Gopkg.toml documentation. +# +# required = ["github.com/user/thing/cmd/thing"] +# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] +# +# [[constraint]] +# name = "github.com/user/project" +# version = "1.0.0" +# +# [[constraint]] +# name = "github.com/user/project2" +# branch = "dev" +# source = "github.com/myfork/project2" +# +# [[override]] +# name = "github.com/x/y" +# version = "2.4.0" +# +# [prune] +# non-go = false +# go-tests = true +# unused-packages = true + +[prune] + go-tests = true + unused-packages = true diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..78758eb --- /dev/null +++ b/Makefile @@ -0,0 +1,272 @@ +PROJECT_NAME=git-service +PACKAGE_NAME:=github.com/redhat-developer/$(PROJECT_NAME) +CUR_DIR=$(shell pwd) +TMP_PATH=$(CUR_DIR)/tmp +INSTALL_PREFIX=$(CUR_DIR)/bin +VENDOR_DIR=vendor +INCLUDE_DIR=make +SOURCE_DIR ?= . +SOURCES := $(shell find $(SOURCE_DIR) -path $(SOURCE_DIR)/vendor -prune -o -name '*.go' -print) + +BINARY_SERVER_BIN=$(INSTALL_PREFIX)/git-service +GO_BIN_NAME=go +GIT_BIN_NAME=git +DEP_BIN_NAME=dep +DOCKER_BIN_NAME=docker +UNAME_S=$(shell uname -s) +GOCOV_BIN=$(VENDOR_DIR)/github.com/axw/gocov/gocov/gocov +GOCOVMERGE_BIN=$(VENDOR_DIR)/github.com/wadey/gocovmerge/gocovmerge +GOCYCLO_DIR=$(VENDOR_DIR)/github.com/fzipp/gocyclo +GOCYCLO_BIN=$(GOCYCLO_DIR)/gocyclo +GOLANGCI_LINT_BIN_NAME:=golangci-lint + +# declares variable that are OS-sensitive +include ./$(INCLUDE_DIR)/test.mk +include ./$(INCLUDE_DIR)/Makefile.dev + +DOCKER_BIN := $(shell command -v $(DOCKER_BIN_NAME) 2> /dev/null) +include ./$(INCLUDE_DIR)/docker.mk + +# This is a fix for a non-existing user in passwd file when running in a docker +# container and trying to clone repos of dependencies +GIT_COMMITTER_NAME ?= "user" +GIT_COMMITTER_EMAIL ?= "user@example.com" +export GIT_COMMITTER_NAME +export GIT_COMMITTER_EMAIL + +COMMIT=$(shell git rev-parse HEAD 2>/dev/null) +GITUNTRACKEDCHANGES := $(shell git status --porcelain --untracked-files=no) +ifneq ($(GITUNTRACKEDCHANGES),) + COMMIT := $(COMMIT)-dirty +endif +BUILD_TIME=`date -u '+%Y-%m-%dT%H:%M:%SZ'` + +.DEFAULT_GOAL := help + +# Call this function with $(call log-info,"Your message") +define log-info = + @echo "INFO: $(1)" +endef + +# ------------------------------------------------------------------- +# Docker build +# ------------------------------------------------------------------- +BUILD_DIR = bin +REGISTRY_URI = quay.io +REGISTRY_NS = ${PROJECT_NAME} +REGISTRY_IMAGE = ${PROJECT_NAME} + +ifeq ($(TARGET),rhel) + REGISTRY_URL := ${REGISTRY_URI}/openshiftio/rhel-${REGISTRY_NS}-${REGISTRY_IMAGE} + DOCKERFILE := Dockerfile.rhel +else + REGISTRY_URL := ${REGISTRY_URI}/openshiftio/${REGISTRY_NS}-${REGISTRY_IMAGE} + DOCKERFILE := Dockerfile +endif + +$(BUILD_DIR): + mkdir $(BUILD_DIR) + +.PHONY: build-linux $(BUILD_DIR) +build-linux: makefiles prebuild-check deps ## Builds the Linux binary for the container image into bin/ folder + CGO_ENABLED=0 GOARCH=amd64 GOOS=linux go build -v $(LDFLAGS) -o $(BUILD_DIR)/$(PROJECT_NAME) + +image: clean-artifacts build-linux + docker build -t $(REGISTRY_URL) \ + --build-arg BUILD_DIR=$(BUILD_DIR)\ + --build-arg PROJECT_NAME=$(PROJECT_NAME)\ + -f $(VENDOR_DIR)/github.com/fabric8-services/fabric8-common/makefile/$(DOCKERFILE) . + +# ------------------------------------------------------------------- +# help! +# ------------------------------------------------------------------- + +.PHONY: help +# Based on https://gist.github.com/rcmachado/af3db315e31383502660 +## Display this help text +help:/ + $(info Available targets) + $(info -----------------) + @awk '/^[a-zA-Z\-\_0-9]+:/ { \ + helpMessage = match(lastLine, /^## (.*)/); \ + helpCommand = substr($$1, 0, index($$1, ":")-1); \ + if (helpMessage) { \ + helpMessage = substr(lastLine, RSTART + 3, RLENGTH); \ + gsub(/##/, "\n ", helpMessage); \ + printf "%-35s - %s\n", helpCommand, helpMessage; \ + lastLine = "" \ + } \ + } \ + { hasComment = match(lastLine, /^## (.*)/); \ + if(hasComment) { \ + lastLine=lastLine$$0; \ + } \ + else { \ + lastLine = $$0 \ + } \ + }' $(MAKEFILE_LIST) +# ------------------------------------------------------------------- +# required tools +# ------------------------------------------------------------------- + +# Find all required tools: +GIT_BIN := $(shell command -v $(GIT_BIN_NAME) 2> /dev/null) +TMP_BIN_DIR := $(TMP_PATH)/bin +DEP_BIN := $(TMP_BIN_DIR)/$(DEP_BIN_NAME) +DEP_VERSION=v0.5.0 +GOLANGCI_LINT_VERSION=1.12 +GOLANGCI_LINT_BIN=$(TMP_BIN_DIR)/$(GOLANGCI_LINT_BIN_NAME) +GO_BIN := $(shell command -v $(GO_BIN_NAME) 2> /dev/null) + +$(INSTALL_PREFIX): + mkdir -p $(INSTALL_PREFIX) +$(TMP_PATH): + mkdir -p $(TMP_PATH) + +.PHONY: prebuild-check +prebuild-check: $(TMP_PATH) $(INSTALL_PREFIX) +# Check that all tools where found +ifndef GIT_BIN + $(error The "$(GIT_BIN_NAME)" executable could not be found in your PATH) +endif +ifndef DEP_BIN + $(error The "$(DEP_BIN_NAME)" executable could not be found in your PATH) +endif +ifndef GO_BIN + $(error The "$(GO_BIN_NAME)" executable could not be found in your PATH) +endif + +# ------------------------------------------------------------------- +# deps +# ------------------------------------------------------------------- + +.PHONY: deps +## Download build dependencies +deps: $(DEP_BIN) $(VENDOR_DIR) + +# install dep in a the tmp/bin dir of the repo +$(DEP_BIN): + @echo "Installing 'dep' $(DEP_VERSION) at '$(TMP_BIN_DIR)'..." + mkdir -p $(TMP_BIN_DIR) +ifeq ($(UNAME_S),Darwin) + @curl -L -s https://github.com/golang/dep/releases/download/$(DEP_VERSION)/dep-darwin-amd64 -o $(DEP_BIN) + @cd $(TMP_BIN_DIR) && \ + curl -L -s https://github.com/golang/dep/releases/download/$(DEP_VERSION)/dep-darwin-amd64.sha256 -o $(TMP_BIN_DIR)/dep-darwin-amd64.sha256 && \ + echo "1a7bdb0d6c31ecba8b3fd213a1170adf707657123e89dff234871af9e0498be2 dep" > dep-darwin-amd64.sha256 && \ + shasum -a 256 --check dep-darwin-amd64.sha256 +else + @curl -L -s https://github.com/golang/dep/releases/download/$(DEP_VERSION)/dep-linux-amd64 -o $(DEP_BIN) + @cd $(TMP_BIN_DIR) && \ + echo "287b08291e14f1fae8ba44374b26a2b12eb941af3497ed0ca649253e21ba2f83 dep" > dep-linux-amd64.sha256 && \ + sha256sum -c dep-linux-amd64.sha256 +endif + @chmod +x $(DEP_BIN) + +$(VENDOR_DIR): Gopkg.toml + @echo "checking dependencies with $(DEP_BIN_NAME)" + @$(DEP_BIN) ensure -v + +# ------------------------------------------------------------------- +# Code format/check +# ------------------------------------------------------------------- +GOFORMAT_FILES := $(shell find . -name '*.go' | grep -vEf $(INCLUDE_DIR)/gofmt_exclude) + +.PHONY: check-go-format +## Exits with an error if there are files that do not match formatting defined by gofmt +check-go-format: prebuild-check deps + @gofmt -s -l ${GOFORMAT_FILES} 2>&1 \ + | tee /tmp/gofmt-errors \ + | read \ + && echo "ERROR: These files differ from gofmt's style (run 'make format-go-code' to fix this):" \ + && cat /tmp/gofmt-errors \ + && exit 1 \ + || true + +.PHONY: analyze-go-code +## Run golangci analysis over the code +analyze-go-code: deps + $(info >>--- RESULTS: GOLANGCI CODE ANALYSIS ---<<) + @go get -u github.com/golangci/golangci-lint/cmd/golangci-lint + @golangci-lint run + +.PHONY: format-go-code +## Formats any go file that does not match formatting defined by gofmt +format-go-code: prebuild-check + @gofmt -s -l -w ${GOFORMAT_FILES} + +# ------------------------------------------------------------------- +# Code format/check with golangci-lint +# ------------------------------------------------------------------- +.PHONY: check-go-code +## Checks the code with golangci-lint +check-go-code: $(GOLANGCI_LINT_BIN) + @echo "checking code..." + $(GOLANGCI_LINT_BIN) run + +# install golangci-lint in the 'tmp/bin' dir of the repo +$(GOLANGCI_LINT_BIN): + @echo "Installing 'golang-ci-lint' $(GOLANGCI_LINT_VERSION) at '$(TMP_BIN_DIR)' ..." + mkdir -p $(TMP_BIN_DIR) +ifeq ($(UNAME_S),Darwin) + @curl -L -s https://github.com/golangci/golangci-lint/releases/download/v$(GOLANGCI_LINT_VERSION)/golangci-lint-$(GOLANGCI_LINT_VERSION)-darwin-amd64.tar.gz -o $(GOLANGCI_LINT_BIN)-$(GOLANGCI_LINT_VERSION)-darwin-amd64.tar.gz && \ + cd $(TMP_BIN_DIR) && curl -L -s https://github.com/golangci/golangci-lint/releases/download/v$(GOLANGCI_LINT_VERSION)/golangci-lint-$(GOLANGCI_LINT_VERSION)-checksums.txt -o golangci-lint-$(GOLANGCI_LINT_VERSION)-checksums.txt && \ + grep "darwin-amd64" golangci-lint-$(GOLANGCI_LINT_VERSION)-checksums.txt > golangci-lint-$(GOLANGCI_LINT_VERSION)-checksum-darwin-amd64.txt && \ + shasum -a 256 --check golangci-lint-$(GOLANGCI_LINT_VERSION)-checksum-darwin-amd64.txt && \ + tar xvf $(GOLANGCI_LINT_BIN)-$(GOLANGCI_LINT_VERSION)-darwin-amd64.tar.gz -C $(TMP_BIN_DIR) && \ + mv $(TMP_BIN_DIR)/golangci-lint-$(GOLANGCI_LINT_VERSION)-darwin-amd64/golangci-lint $(GOLANGCI_LINT_BIN) +else + @curl -L -s https://github.com/golangci/golangci-lint/releases/download/v$(GOLANGCI_LINT_VERSION)/golangci-lint-$(GOLANGCI_LINT_VERSION)-linux-amd64.tar.gz -o $(GOLANGCI_LINT_BIN)-$(GOLANGCI_LINT_VERSION)-linux-amd64.tar.gz && \ + cd $(TMP_BIN_DIR) && curl -L -s https://github.com/golangci/golangci-lint/releases/download/v$(GOLANGCI_LINT_VERSION)/golangci-lint-$(GOLANGCI_LINT_VERSION)-checksums.txt -o golangci-lint-$(GOLANGCI_LINT_VERSION)-checksums.txt && \ + grep "linux-amd64" golangci-lint-$(GOLANGCI_LINT_VERSION)-checksums.txt > golangci-lint-$(GOLANGCI_LINT_VERSION)-checksum-linux-amd64.txt && \ + sha256sum -c golangci-lint-$(GOLANGCI_LINT_VERSION)-checksum-linux-amd64.txt && \ + tar xvf $(GOLANGCI_LINT_BIN)-$(GOLANGCI_LINT_VERSION)-linux-amd64.tar.gz -C $(TMP_BIN_DIR) && \ + mv $(TMP_BIN_DIR)/golangci-lint-$(GOLANGCI_LINT_VERSION)-linux-amd64/golangci-lint $(GOLANGCI_LINT_BIN) +endif + @chmod +x $(GOLANGCI_LINT_BIN) + + +# ------------------------------------------------------------------- +# clean +# ------------------------------------------------------------------- + +# For the global "clean" target all targets in this variable will be executed +CLEAN_TARGETS = + +CLEAN_TARGETS += clean-artifacts +.PHONY: clean-artifacts +# Removes the ./bin directory. +clean-artifacts: + -rm -rf $(INSTALL_PREFIX) + +CLEAN_TARGETS += clean-object-files +.PHONY: clean-object-files +# Runs go clean to remove any executables or other object files. +clean-object-files: + go clean ./... + +CLEAN_TARGETS += clean-vendor +.PHONY: clean-vendor +# Removes the ./vendor directory. +clean-vendor: + -rm -rf $(VENDOR_DIR) + +CLEAN_TARGETS += clean-tmp +.PHONY: clean-tmp +# Removes the ./vendor directory. +clean-tmp: + -rm -rf $(TMP_PATH) + +# Keep this "clean" target here after all `clean-*` sub tasks +.PHONY: clean +## Cleans the project, removes all generated code/bins and vendor packages +clean: $(CLEAN_TARGETS) + +# ------------------------------------------------------------------- +# build the binary executable (to ship in prod) +# ------------------------------------------------------------------- + +.PHONY: build +## Build git service +build: prebuild-check deps check-go-format + @echo "building $(BINARY_SERVER_BIN)..." + go build -v -o $(BINARY_SERVER_BIN) pkg/cmd/main.go diff --git a/deploy/namespace.yaml b/deploy/namespace.yaml new file mode 100644 index 0000000..ac5fc18 --- /dev/null +++ b/deploy/namespace.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: openshift-devops-console +spec: diff --git a/make/Makefile.dev b/make/Makefile.dev new file mode 100644 index 0000000..5b07532 --- /dev/null +++ b/make/Makefile.dev @@ -0,0 +1,56 @@ +DOCKER_REPO?=quay.io/openshiftio +IMAGE_NAME?=git-service +SHORT_COMMIT=$(shell git rev-parse --short HEAD) +ifneq ($(GITUNTRACKEDCHANGES),) +SHORT_COMMIT := $(SHORT_COMMIT)-dirty +endif + +TIMESTAMP:=$(shell date +%s) +TAG?=$(SHORT_COMMIT)-$(TIMESTAMP) + +DEPLOY_DIR:=deploy + +.PHONY: create-resources +create-resources: + @echo "Logging using system:admin..." + @oc login -u system:admin + @echo "Creating sub resources..." + @echo "Creating CRDs..." + @oc create -f https://raw.githubusercontent.com/redhat-developer/devopsconsole-operator/master/deploy/crds/devopsconsole_v1alpha1_gitsource_crd.yaml + @echo "Creating Namespace" + @oc create -f $(DEPLOY_DIR)/namespace.yaml + +.PHONY: build-image +build-image: + docker build -t $(DOCKER_REPO)/$(IMAGE_NAME):$(TAG) -f Dockerfile.dev . + docker tag $(DOCKER_REPO)/$(IMAGE_NAME):$(TAG) $(DOCKER_REPO)/$(IMAGE_NAME):test + +.PHONY: deploy-gitserver-only +deploy-gitserver-only: + @echo "Creating Git Server..." + +.PHONY: clean-all +clean-all: clean-gitserver clean-resources + +.PHONY: clean-gitserver +clean-gitserver: + @echo "Deleting API Server..." + +.PHONY: clean-resources +clean-resources: + @echo "Deleting sub resources..." + @echo "Deleting Namespace" + @oc delete -f $(DEPLOY_DIR)/namespace.yaml || true + @echo "Deleting CRDs..." + @oc delete -f https://raw.githubusercontent.com/redhat-developer/devopsconsole-operator/master/deploy/crds/devopsconsole_v1alpha1_gitsource_crd.yaml || true + +.PHONY: deploy-gitserver +deploy-gitserver: build build-image deploy-gitserver-only + +.PHONY: minishift-start +minishift-start: + minishift start --cpus 4 --memory 8GB + -eval `minishift docker-env` && oc login -u system:admin + +.PHONY: deploy-all +deploy-all: clean-resources create-resources deps prebuild-check deploy-gitserver diff --git a/make/docker.mk b/make/docker.mk new file mode 100644 index 0000000..84e5322 --- /dev/null +++ b/make/docker.mk @@ -0,0 +1,108 @@ +DOCKER_IMAGE_CORE := $(PROJECT_NAME) +DOCKER_IMAGE_DEPLOY := $(PROJECT_NAME)-deploy + +ifeq ($(TARGET),rhel) + DOCKERFILE_DEPLOY := Dockerfile.deploy.rhel +else + DOCKERFILE_DEPLOY := Dockerfile.deploy +endif + +# If running in Jenkins we don't allow for interactively running the container +ifneq ($(BUILD_TAG),) + DOCKER_RUN_INTERACTIVE_SWITCH := +else + DOCKER_RUN_INTERACTIVE_SWITCH := -i +endif + +# The workspace environment is set by Jenkins and defaults to /tmp if not set +WORKSPACE ?= /tmp +DOCKER_BUILD_DIR := $(WORKSPACE)/$(PROJECT_NAME)-build + +# The BUILD_TAG environment variable will be set by jenkins +# to reflect jenkins-${JOB_NAME}-${BUILD_NUMBER} +BUILD_TAG ?= $(PROJECT_NAME)-local-build +DOCKER_CONTAINER_NAME := $(BUILD_TAG) + +# Where is the GOPATH inside the build container? +GOPATH_IN_CONTAINER=/tmp/go +PACKAGE_PATH=$(GOPATH_IN_CONTAINER)/src/$(PACKAGE_NAME) + +.PHONY: docker-image-builder +## Builds the docker image used to build the software +docker-image-builder: + @echo "Building docker image $(DOCKER_IMAGE_CORE)" + docker build --build-arg USE_GO_VERSION_FROM_WEBSITE=$(USE_GO_VERSION_FROM_WEBSITE) -t $(DOCKER_IMAGE_CORE) -f $(CUR_DIR)/Dockerfile.builder $(CUR_DIR) + +.PHONY: docker-image-deploy +## Creates a runnable image using the artifacts from the bin directory +docker-image-deploy: + docker build -t $(DOCKER_IMAGE_DEPLOY) -f $(CUR_DIR)/$(DOCKERFILE_DEPLOY) $(CUR_DIR) + +.PHONY: docker-build-dir +# Creates the docker build directory. +docker-build-dir: + @echo "Creating build directory $(BUILD_DIR)" + mkdir -p $(DOCKER_BUILD_DIR) + +CLEAN_TARGETS += clean-docker-build-container +.PHONY: clean-docker-build-container +# Removes any existing container used to build the software (if any). +clean-docker-build-container: + @echo "Removing container named \"$(DOCKER_CONTAINER_NAME)\" (if any)" +ifneq ($(strip $(shell docker ps -qa --filter "name=$(DOCKER_CONTAINER_NAME)" 2>/dev/null)),) + @docker rm -f $(DOCKER_CONTAINER_NAME) +else + @echo "No container named \"$(DOCKER_CONTAINER_NAME)\" to remove" +endif + +CLEAN_TARGETS += clean-docker-build-dir +.PHONY: clean-docker-build-dir +# Removes the docker build directory. +clean-docker-build-dir: + @echo "Cleaning build directory $(BUILD_DIR)" + -rm -rf $(DOCKER_BUILD_DIR) + +.PHONY: docker-start +## Starts the docker build container in the background (detached mode). +## After calling this command you can invoke all the make targets from the +## normal Makefile (e.g. deps, build) inside the build container +## by prefixing them with "docker-". For example to execute "make deps" +## inside the build container, just run "make docker-deps". +## To remove the container when no longer needed, call "make docker-rm". +docker-start: docker-build-dir docker-image-builder +ifneq ($(strip $(shell docker ps -qa --filter "name=$(DOCKER_CONTAINER_NAME)" 2>/dev/null)),) + @echo "Docker container \"$(DOCKER_CONTAINER_NAME)\" already exists. To recreate, run \"make docker-rm\"." +else + docker run \ + --detach=true \ + -t \ + $(DOCKER_RUN_INTERACTIVE_SWITCH) \ + --name="$(DOCKER_CONTAINER_NAME)" \ + -e ADMIN_POSTGRES_PORT=5432 \ + -v $(CUR_DIR):$(PACKAGE_PATH):Z \ + -u $(shell id -u $(USER)):$(shell id -g $(USER)) \ + -e GOPATH=$(GOPATH_IN_CONTAINER) \ + -w $(PACKAGE_PATH) \ + $(DOCKER_IMAGE_CORE) + @echo "Docker container \"$(DOCKER_CONTAINER_NAME)\" created. Continue with \"make docker-deps\"." +endif + +.PHONY: docker-rm +# Removes the docker build container, if any (see "make docker-start"). +docker-rm: +ifneq ($(strip $(shell docker ps -qa --filter "name=$(DOCKER_CONTAINER_NAME)" 2>/dev/null)),) + docker rm -f "$(DOCKER_CONTAINER_NAME)" +else + @echo "No container named \"$(DOCKER_CONTAINER_NAME)\" to remove." +endif + +# This is a wildcard target to let you call any make target from the normal makefile +# but it will run inside the docker container. This target will only get executed if +# there's no specialized form available. For example if you call "make docker-start" +# not this target gets executed but the "docker-start" target. +docker-%: + $(eval makecommand:=$(subst docker-,,$@)) +ifeq ($(strip $(shell docker ps -qa --filter "name=$(DOCKER_CONTAINER_NAME)" 2>/dev/null)),) + $(error No container name "$(DOCKER_CONTAINER_NAME)" exists to run the command "make $(makecommand)") +endif + docker exec -t $(DOCKER_RUN_INTERACTIVE_SWITCH) "$(DOCKER_CONTAINER_NAME)" bash -ec 'make $(makecommand)' diff --git a/make/gofmt_exclude b/make/gofmt_exclude new file mode 100644 index 0000000..6d03c3e --- /dev/null +++ b/make/gofmt_exclude @@ -0,0 +1 @@ +vendor/.* diff --git a/make/test.mk b/make/test.mk new file mode 100644 index 0000000..541ca48 --- /dev/null +++ b/make/test.mk @@ -0,0 +1,326 @@ +#=============================================================================== +# Testing has become a rather big and interconnected topic and that's why it +# has arrived in it's own file. +# +# We have to types of tests available: +# +# 1. unit tests +# +# The unit tests can be executed fairly simply be running `go test` +# +# Usage +# ----- +# If you want to run the unit tests, type +# +# $ make test-unit +# +# To run all tests, type +# +# $ make test-all +# +# To output unit-test coverage profile information for each function, type +# +# $ make coverage-unit +# +# To output all coverage profile information for each function, type +# +# $ make coverage-all +# +# Artifacts and coverage modes +# ---------------------------- +# Each package generates coverage outputs under tmp/coverage/$(PACKAGE) where +# $(PACKAGE) resolves to the Go package. Here's an example of a coverage file +# for the package "github.com/fabric8-services/fabric8-auth/models" with coverage mode +# "set" generated by the unit tests: +# +# tmp/coverage/github.com/fabric8-services/fabric8-auth/models/coverage.unit.mode-set +# +# For unit-tests all results are combined into this file: +# +# tmp/coverage.unit.mode-$(COVERAGE_MODE) +# +# +# The overall coverage gets combined into this file: +# +# tmp/coverage.mode-$(COVERAGE_MODE) +# +# The $(COVERAGE_MODE) in each filename indicates what coverage mode was used. +# +# These are possible coverage modes (see https://blog.golang.org/cover): +# +# set: did each statement run? (default) +# count: how many times did each statement run? +# atomic: like count, but counts precisely in parallel programs +# +# To choose another coverage mode, simply prefix the invovation of `make`: +# +# $ COVERAGE_MODE=count make test-unit +#=============================================================================== + +# mode can be: set, count, or atomic +COVERAGE_MODE ?= set + +# By default no go test calls will use the -v switch when running tests. +# But if you want you can enable that by setting GO_TEST_VERBOSITY_FLAG=-v +GO_TEST_VERBOSITY_FLAG ?= + + +# By default reduce the amount of log output from tests +ADMIN_LOG_LEVEL ?= error + +# Output directory for coverage information +COV_DIR = $(TMP_PATH)/coverage + +# Files that combine package coverages for unit tests separately +COV_PATH_UNIT = $(TMP_PATH)/coverage.unit.mode-$(COVERAGE_MODE) + +# File that stores overall coverge for all packages and unit-tests +COV_PATH_OVERALL = $(TMP_PATH)/coverage.mode-$(COVERAGE_MODE) + +# This pattern excludes some folders from the coverage calculation (see grep -v) +ALL_PKGS_EXCLUDE_PATTERN = 'vendor\|app\|tool\/cli\|design\|client\|test' + +# This pattern excludes some folders from the go code analysis +GOANALYSIS_PKGS_EXCLUDE_PATTERN="vendor|app|client|tool/cli" +GOANALYSIS_DIRS=$(shell go list -f {{.Dir}} ./... | grep -v -E $(GOANALYSIS_PKGS_EXCLUDE_PATTERN)) + +#------------------------------------------------------------------------------- +# Normal test targets +# +# These test targets are the ones that will be invoked from the outside. If +# they are called and the artifacts already exist, then the artifacts will +# first be cleaned and recreated. This ensures that the tests are always +# executed. +#------------------------------------------------------------------------------- + +.PHONY: test-all +## Runs the unit tests and e2e tests +test-all: prebuild-check test-unit test-e2e + +# Runs the unit tests and produces coverage files for each package. +.PHONY: test-unit-with-coverage +test-unit-with-coverage: prebuild-check clean-coverage-unit $(COV_PATH_UNIT) + +.PHONY: test-unit +## Runs the unit tests without coverage +test-unit: prebuild-check $(SOURCES) + $(call log-info,"Running test: $@") + $(eval TEST_PACKAGES:=$(shell go list ./... | grep -v $(ALL_PKGS_EXCLUDE_PATTERN))) + ADMIN_LOG_LEVEL=$(ADMIN_LOG_LEVEL) go test -vet off $(GO_TEST_VERBOSITY_FLAG) $(TEST_PACKAGES) + +.PHONY: test-e2e +## Runs the e2e tests without coverage +test-e2e: build build-image e2e-setup + $(call log-info,"Running E2E test: $@") + go test ./test/e2e/... -root=$(PWD) -kubeconfig=$(HOME)/.kube/config -globalMan deploy/test/global-manifests.yaml -namespacedMan deploy/test/namespace-manifests.yaml -v -parallel=1 -singleNamespace + +.PHONY: e2e-setup +e2e-setup: e2e-cleanup + oc new-project devconsole-e2e-test || true + +.PHONY: e2e-cleanup +e2e-cleanup: + oc login -u system:admin + oc delete project devconsole-e2e-test || true + +#------------------------------------------------------------------------------- +# Inspect coverage of unit tests, integration tests in either pure +# console mode or in a browser (*-html). +# +# If the test coverage files to be evaluated already exist, then no new +# tests are executed. If they don't exist, we first run the tests. +#------------------------------------------------------------------------------- + +# Prints the total coverage of a given package. +# The total coverage is printed as the last argument in the +# output of "go tool cover". If the requested test name (first argument) +# Is *, then unit, integration tests will be combined +define print-package-coverage +$(eval TEST_NAME:=$(1)) +$(eval PACKAGE_NAME:=$(2)) +$(eval COV_FILE:="$(COV_DIR)/$(PACKAGE_NAME)/coverage.$(TEST_NAME).mode-$(COVERAGE_MODE)") + @if [ "$(TEST_NAME)" == "*" ]; then \ + UNIT_FILE=`echo $(COV_FILE) | sed 's/*/unit/'`; \ + INTEGRATON_FILE=`echo $(COV_FILE) | sed 's/*/integration/'`; \ + COV_FILE=`echo $(COV_FILE) | sed 's/*/combined/'`; \ + if [ ! -e $${UNIT_FILE} ]; then \ + COV_FILE=$${INTEGRATION_FILE}; \ + else \ + if [ ! -e $${INTEGRATION_FILE} ]; then \ + COV_FILE=$${UNIT_FILE}; \ + else \ + $(GOCOVMERGE_BIN) $${UNIT_FILE} $${INTEGRATION_FILE} > $${COV_FILE}; \ + fi; \ + fi; \ +else \ + COV_FILE=$(COV_FILE); \ +fi; \ +if [ -e "$${COV_FILE}" ]; then \ + VAL=`go tool cover -func=$${COV_FILE} \ + | grep '^total:' \ + | grep '\S\+$$' -o \ + | sed 's/%//'`; \ + printf "%-80s %#5.2f%%\n" "$(PACKAGE_NAME)" "$${VAL}"; \ +else \ + printf "%-80s %6s\n" "$(PACKAGE_NAME)" "n/a"; \ +fi +endef + +# Iterates over every package and prints its test coverage +# for a given test name ("unit"). +define package-coverage +$(eval TEST_NAME:=$(1)) +@printf "\n\nPackage coverage:\n" +$(eval TEST_PACKAGES:=$(shell go list ./... | grep -v $(ALL_PKGS_EXCLUDE_PATTERN))) +$(foreach package, $(TEST_PACKAGES), $(call print-package-coverage,$(TEST_NAME),$(package))) +endef + +$(COV_PATH_OVERALL): $(GOCOVMERGE_BIN) + @$(GOCOVMERGE_BIN) $(COV_PATH_UNIT) > $(COV_PATH_OVERALL) + +# Console coverage output: + +# First parameter: file to do in-place replacement with. +define cleanup-coverage-file +@sed -i '/.*\/sqlbindata\.go.*/d' $(1) +@sed -i '/.*\/confbindata\.go.*/d' $(1) +endef + +# Output coverage profile information for each function (only based on unit-tests). +# Re-runs unit-tests if coverage information is outdated. +.PHONY: coverage-unit +## Runs the unit tests with producing coverage files for each package +coverage-unit: prebuild-check $(COV_PATH_UNIT) + $(call cleanup-coverage-file,$(COV_PATH_UNIT)) + @go tool cover -func=$(COV_PATH_UNIT) + $(call package-coverage,unit) + + +.PHONY: coverage-all +# Output coverage profile information for each function. +# Re-runs unit- and integration-tests if coverage information is outdated. +coverage-all: prebuild-check clean-coverage-overall $(COV_PATH_OVERALL) + $(call cleanup-coverage-file,$(COV_PATH_OVERALL)) + @go tool cover -func=$(COV_PATH_OVERALL) + $(call package-coverage,*) + +# Experimental: + +.PHONY: gocov-unit-annotate +# (EXPERIMENTAL) Show actual code and how it is covered with unit tests. +# This target only runs the tests if the coverage file does exist. +gocov-unit-annotate: prebuild-check $(GOCOV_BIN) $(COV_PATH_UNIT) + $(call cleanup-coverage-file,$(COV_PATH_UNIT)) + @$(GOCOV_BIN) convert $(COV_PATH_UNIT) | $(GOCOV_BIN) annotate - + +.PHONY: .gocov-unit-report +.gocov-unit-report: prebuild-check $(GOCOV_BIN) $(COV_PATH_UNIT) + $(call cleanup-coverage-file,$(COV_PATH_UNIT)) + @$(GOCOV_BIN) convert $(COV_PATH_UNIT) | $(GOCOV_BIN) report + + +#------------------------------------------------------------------------------- +# Test artifacts are coverage files for unit and integration tests. +#------------------------------------------------------------------------------- + +# The test-package function executes tests for a package and saves the collected +# coverage output to a directory. After storing the coverage information it is +# also appended to a file of choice (without the "mode"-line) +# +# Parameters: +# 1. Test name (e.g. "unit" or "integration") +# 2. package name "github.com/fabric8-services/fabric8-auth/model" +# 3. File in which to combine the output +# 4. Path to file in which to store names of packages that failed testing +# 5. Environment variable (in the form VAR=VALUE) to be specified for running +# the test. For multiple environment variables, pass "VAR1=VAL1 VAR2=VAL2". +define test-package +$(eval TEST_NAME := $(1)) +$(eval PACKAGE_NAME := $(2)) +$(eval COMBINED_OUT_FILE := $(3)) +$(eval ERRORS_FILE := $(4)) +$(eval ENV_VAR := $(5)) +$(eval ALL_PKGS_COMMA_SEPARATED := $(6)) +@mkdir -p $(COV_DIR)/$(PACKAGE_NAME); +$(eval COV_OUT_FILE := $(COV_DIR)/$(PACKAGE_NAME)/coverage.$(TEST_NAME).mode-$(COVERAGE_MODE)) +@$(ENV_VAR) ADMIN_LOG_LEVEL=$(ADMIN_LOG_LEVEL) \ + go test -vet off $(PACKAGE_NAME) \ + $(GO_TEST_VERBOSITY_FLAG) \ + -coverprofile $(COV_OUT_FILE) \ + -coverpkg $(ALL_PKGS_COMMA_SEPARATED) \ + -covermode=$(COVERAGE_MODE) \ + -timeout 10m \ + $(EXTRA_TEST_PARAMS) \ + || echo $(PACKAGE_NAME) >> $(ERRORS_FILE) + +@if [ -e "$(COV_OUT_FILE)" ]; then \ + if [ ! -e "$(COMBINED_OUT_FILE)" ]; then \ + cp $(COV_OUT_FILE) $(COMBINED_OUT_FILE); \ + else \ + cp $(COMBINED_OUT_FILE) $(COMBINED_OUT_FILE).tmp; \ + $(GOCOVMERGE_BIN) $(COV_OUT_FILE) $(COMBINED_OUT_FILE).tmp > $(COMBINED_OUT_FILE); \ + fi \ +fi +endef + +# Exits the makefile with an error if the file (first parameter) exists. +# Before exiting, the contents of the passed file is printed. +define check-test-results +$(eval ERRORS_FILE := $(1)) +@if [ -e "$(ERRORS_FILE)" ]; then \ +echo ""; \ +echo "ERROR: The following packages did not pass the tests:"; \ +echo "-----------------------------------------------------"; \ +cat $(ERRORS_FILE); \ +echo "-----------------------------------------------------"; \ +echo ""; \ +exit 1; \ +fi +endef + +# NOTE: We don't have prebuild-check as a dependency here because it would cause +# the recipe to be always executed. +$(COV_PATH_UNIT): $(SOURCES) $(GOCOVMERGE_BIN) + $(eval TEST_NAME := unit) + $(eval ERRORS_FILE := $(TMP_PATH)/errors.$(TEST_NAME)) + $(call log-info,"Running test: $(TEST_NAME)") + @mkdir -p $(COV_DIR) + @echo "mode: $(COVERAGE_MODE)" > $(COV_PATH_UNIT) + @-rm -f $(ERRORS_FILE) + $(eval TEST_PACKAGES:=$(shell go list ./... | grep -v $(ALL_PKGS_EXCLUDE_PATTERN))) + $(eval ALL_PKGS_COMMA_SEPARATED:=$(shell echo $(TEST_PACKAGES) | tr ' ' ,)) + $(foreach package, $(TEST_PACKAGES), $(call test-package,$(TEST_NAME),$(package),$(COV_PATH_UNIT),$(ERRORS_FILE),,$(ALL_PKGS_COMMA_SEPARATED))) + $(call check-test-results,$(ERRORS_FILE)) + +#------------------------------------------------------------------------------- +# Additional tools to build +#------------------------------------------------------------------------------- + +$(GOCOV_BIN): prebuild-check + @cd $(VENDOR_DIR)/github.com/axw/gocov/gocov/ && go build + +$(GOCOVMERGE_BIN): prebuild-check + @cd $(VENDOR_DIR)/github.com/wadey/gocovmerge && go build + + +#------------------------------------------------------------------------------- +# Clean targets +#------------------------------------------------------------------------------- + +CLEAN_TARGETS += clean-coverage +.PHONY: clean-coverage +# Removes all coverage files +clean-coverage: clean-coverage-unit clean-coverage-overall + -@rm -rf $(COV_DIR) + +CLEAN_TARGETS += clean-coverage-overall +.PHONY: clean-coverage-overall +# Removes overall coverage file +clean-coverage-overall: + -@rm -f $(COV_PATH_OVERALL) + +CLEAN_TARGETS += clean-coverage-unit +.PHONY: clean-coverage-unit +# Removes unit test coverage file +clean-coverage-unit: + -@rm -f $(COV_PATH_UNIT) diff --git a/minishift/README.md b/minishift/README.md new file mode 100644 index 0000000..d9d2f65 --- /dev/null +++ b/minishift/README.md @@ -0,0 +1,121 @@ +## Running Git Service on Minishift + +These instructions will help you run the service using MiniShift. + +### Prerequisites + +[MiniShift v1.27.0](https://docs.okd.io/latest/minishift/getting-started/installing.html) + +[oc 3.11.0](https://docs.okd.io/latest/cli_reference/get_started_cli.html#installing-the-cli) + +[KVM Hypervisor (for Linux)](https://www.linux-kvm.org/page/Downloads) + +#### Install Minishift + +Make sure you have all prerequisites installed. Please check the list [here](https://docs.openshift.org/latest/minishift/getting-started/installing.html#install-prerequisites) + +Download and put `minishift` in your $PATH by following steps [here](https://docs.openshift.org/latest/minishift/getting-started/installing.html#manually) + +Verify installation by running following command, you should get version number. +```bash +minishift version +``` + +#### Install oc +Install and set up [oc](https://docs.okd.io/latest/cli_reference/get_started_cli.html#installing-the-cli) on your machine: + +```bash +eval $(minishift oc-env) +``` + +Verify installation by running following command, you should get version number. +```bash +oc version +``` + +### Usage +#### TL;DR +``` +make minishift-start +eval $(minishift docker-env) +make deploy-all +``` + +Make sure that you have minishift running. you can check it using `minishift status`. If not then start it using `make minishift-start` target. + +After successfully starting minishift, configure your shell to use docker daemon from minishift using `eval $(minishift docker-env)`. + +Now it's time to run `git-service` and it's required resources from `deploy/resources` on OpenShift use following command: +``` +make deploy-all +``` + +This build uses the `system:admin` account for creating all required resources from `deploy/resources` directory. + +The above command then create deployment for git-service on Openshift. + +#### Start Minishift +We have make target defined to start minishift with required cpu's and configuration. +```bash +make minishift-start +``` + +#### Exposing Docker Env +Make sure to expose minishift docker env using following command: +```bash +eval $(minishift docker-env) +``` + +After running this, you can verify all containers running container inside minishift using `docker ps`. + +**Note**: If you miss this, docker daemon inside minishift will not find the latest image, causing an `ImagePullBackOff` as we are using `ImagePullPolicy: IfNotPresent` + + +### Creating resources required to run Git Service +To create required resources to run Git Service, we have following make target: +```bash +make create-resources +``` + +#### Build and Deploy Git Service +To build docker image and create deployment you can use following target: +```bash +make deploy-gitserver +``` + +#### Creating Resources and Deploying Git Service +If you are too lazy, we have following target for you to create resources, build container and deploy service: + +```bash +make deploy-all +``` + +#### Cleaning Up + +##### Cleaning Git Service +This removes git service. +```bash +make clean-gitserver +``` + +##### Cleaning Resources Created to run Git Service +This removes all resources created to run git service. +```bash +make clean-resources +``` + +##### Cleaning Resources and Git Service +This removes all resources created to run git service along with git service itself. +```bash +make clean-all +``` + +#### ReDeploying Git Service with changes in code +However if you are working on git service code and wants to redeploy latest code change by building container with latest binary. We have +special target for it which will do that for you. + +It won't create all other existing resources again. It'll build container and deploy git service only. + +```bash +make deploy-gitserver +``` \ No newline at end of file diff --git a/pkg/cmd/main.go b/pkg/cmd/main.go new file mode 100644 index 0000000..1bf133b --- /dev/null +++ b/pkg/cmd/main.go @@ -0,0 +1,14 @@ +package main + +import ( + "fmt" + "time" +) + +func main() { + // Placeholder + for { + fmt.Println("Busy doing something super important...") + time.Sleep(time.Second) + } +}