diff --git a/.codecov.yml b/.codecov.yml new file mode 100644 index 0000000..5357ab1 --- /dev/null +++ b/.codecov.yml @@ -0,0 +1,18 @@ +# THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. +# +# Generated on 2021-08-13T12:22:39Z by kres 907039b. + +codecov: + require_ci_to_pass: false + +coverage: + status: + project: + default: + target: 50% + threshold: 0.5% + base: auto + if_ci_failed: success + patch: off + +comment: false diff --git a/.conform.yaml b/.conform.yaml new file mode 100644 index 0000000..eaece18 --- /dev/null +++ b/.conform.yaml @@ -0,0 +1,35 @@ +# THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. +# +# Generated on 2021-08-13T12:22:39Z by kres 907039b. + +policies: +- type: commit + spec: + dco: true + gpg: false + spellcheck: + locale: US + maximumOfOneCommit: true + header: + length: 89 + imperative: true + case: lower + invalidLastCharacters: . + body: + required: true + conventional: + types: ["chore","docs","perf","refactor","style","test","release"] + scopes: [".*"] +- type: license + spec: + skipPaths: + - .git/ + includeSuffixes: + - .go + excludeSuffixes: + - .pb.go + - .pb.gw.go + header: | + // This Source Code Form is subject to the terms of the Mozilla Public + // License, v. 2.0. If a copy of the MPL was not distributed with this + // file, You can obtain one at http://mozilla.org/MPL/2.0/. diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..e4d485d --- /dev/null +++ b/.dockerignore @@ -0,0 +1,13 @@ +# THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. +# +# Generated on 2021-08-13T12:32:42Z by kres 907039b. + +** +!internal +!pkg +!cmd +!go.mod +!go.sum +!.golangci.yml +!README.md +!.markdownlint.json diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..1f82599 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,340 @@ +--- +# THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. +# +# Generated on 2021-08-13T12:23:41Z by kres 907039b. + +kind: pipeline +type: kubernetes +name: default + +platform: + os: linux + arch: amd64 + +steps: +- name: setup-ci + pull: always + image: autonomy/build-container:latest + commands: + - sleep 5 + - git fetch --tags + - install-ci-key + - docker buildx create --driver docker-container --platform linux/amd64 --name local --use unix:///var/outer-run/docker.sock + - docker buildx inspect --bootstrap + environment: + SSH_KEY: + from_secret: ssh_key + volumes: + - name: outer-docker-socket + path: /var/outer-run + - name: docker-socket + path: /var/run + - name: buildx + path: /root/.docker/buildx + - name: ssh + path: /root/.ssh + +- name: base + pull: always + image: autonomy/build-container:latest + commands: + - make base + volumes: + - name: outer-docker-socket + path: /var/outer-run + - name: docker-socket + path: /var/run + - name: buildx + path: /root/.docker/buildx + - name: ssh + path: /root/.ssh + depends_on: + - setup-ci + +- name: unit-tests + pull: always + image: autonomy/build-container:latest + commands: + - make unit-tests + volumes: + - name: outer-docker-socket + path: /var/outer-run + - name: docker-socket + path: /var/run + - name: buildx + path: /root/.docker/buildx + - name: ssh + path: /root/.ssh + depends_on: + - base + +- name: unit-tests-race + pull: always + image: autonomy/build-container:latest + commands: + - make unit-tests-race + volumes: + - name: outer-docker-socket + path: /var/outer-run + - name: docker-socket + path: /var/run + - name: buildx + path: /root/.docker/buildx + - name: ssh + path: /root/.ssh + depends_on: + - base + +- name: coverage + pull: always + image: autonomy/build-container:latest + commands: + - make coverage + environment: + CODECOV_TOKEN: + from_secret: CODECOV_TOKEN + volumes: + - name: outer-docker-socket + path: /var/outer-run + - name: docker-socket + path: /var/run + - name: buildx + path: /root/.docker/buildx + - name: ssh + path: /root/.ssh + depends_on: + - unit-tests + +- name: kubespan-manager + pull: always + image: autonomy/build-container:latest + commands: + - make kubespan-manager + volumes: + - name: outer-docker-socket + path: /var/outer-run + - name: docker-socket + path: /var/run + - name: buildx + path: /root/.docker/buildx + - name: ssh + path: /root/.ssh + depends_on: + - base + +- name: lint + pull: always + image: autonomy/build-container:latest + commands: + - make lint + volumes: + - name: outer-docker-socket + path: /var/outer-run + - name: docker-socket + path: /var/run + - name: buildx + path: /root/.docker/buildx + - name: ssh + path: /root/.ssh + depends_on: + - base + +- name: image-kubespan-manager + pull: always + image: autonomy/build-container:latest + commands: + - make image-kubespan-manager + volumes: + - name: outer-docker-socket + path: /var/outer-run + - name: docker-socket + path: /var/run + - name: buildx + path: /root/.docker/buildx + - name: ssh + path: /root/.ssh + depends_on: + - kubespan-manager + - lint + - unit-tests + +- name: push-kubespan-manager + pull: always + image: autonomy/build-container:latest + commands: + - docker login ghcr.io --username "$${GHCR_USERNAME}" --password "$${GHCR_PASSWORD}" + - make image-kubespan-manager + environment: + GHCR_PASSWORD: + from_secret: ghcr_token + GHCR_USERNAME: + from_secret: ghcr_username + PUSH: true + volumes: + - name: outer-docker-socket + path: /var/outer-run + - name: docker-socket + path: /var/run + - name: buildx + path: /root/.docker/buildx + - name: ssh + path: /root/.ssh + when: + event: + exclude: + - pull_request + depends_on: + - image-kubespan-manager + +- name: push-kubespan-manager-latest + pull: always + image: autonomy/build-container:latest + commands: + - docker login ghcr.io --username "$${GHCR_USERNAME}" --password "$${GHCR_PASSWORD}" + - make image-kubespan-manager TAG=latest + environment: + GHCR_PASSWORD: + from_secret: ghcr_token + GHCR_USERNAME: + from_secret: ghcr_username + PUSH: true + volumes: + - name: outer-docker-socket + path: /var/outer-run + - name: docker-socket + path: /var/run + - name: buildx + path: /root/.docker/buildx + - name: ssh + path: /root/.ssh + when: + branch: + - master + event: + exclude: + - pull_request + depends_on: + - push-kubespan-manager + +- name: release-notes + pull: always + image: autonomy/build-container:latest + commands: + - make release-notes + volumes: + - name: outer-docker-socket + path: /var/outer-run + - name: docker-socket + path: /var/run + - name: buildx + path: /root/.docker/buildx + - name: ssh + path: /root/.ssh + when: + event: + - tag + depends_on: + - unit-tests + - coverage + - kubespan-manager + - image-kubespan-manager + - lint + +- name: release + pull: always + image: plugins/github-release + settings: + api_key: + from_secret: github_token + checksum: + - sha256 + - sha512 + draft: true + files: + - _out/* + note: _out/RELEASE_NOTES.md + volumes: + - name: outer-docker-socket + path: /var/outer-run + - name: docker-socket + path: /var/run + - name: buildx + path: /root/.docker/buildx + - name: ssh + path: /root/.ssh + when: + event: + - tag + depends_on: + - release-notes + +services: +- name: docker + image: docker:19.03-dind + entrypoint: + - dockerd + commands: + - --dns=8.8.8.8 + - --dns=8.8.4.4 + - --mtu=1500 + - --log-level=error + privileged: true + volumes: + - name: outer-docker-socket + path: /var/outer-run + - name: docker-socket + path: /var/run + - name: buildx + path: /root/.docker/buildx + - name: ssh + path: /root/.ssh + +volumes: +- name: outer-docker-socket + host: + path: /var/ci-docker +- name: docker-socket + temp: + medium: memory +- name: buildx + temp: + medium: memory +- name: ssh + temp: + medium: memory + +--- +kind: pipeline +type: kubernetes +name: notify + +platform: + os: linux + arch: amd64 + +clone: + disable: true + +steps: +- name: slack + image: plugins/slack + settings: + channel: proj-talos-maintainers + link_names: true + template: "{{#if build.pull }}\n*{{#success build.status}}✓ Success{{else}}✕ Fail{{/success}}*: {{ repo.owner }}/{{ repo.name }} - \n{{else}}\n*{{#success build.status}}✓ Success{{else}}✕ Fail{{/success}}: {{ repo.owner }}/{{ repo.name }} - Build #{{ build.number }}* (type: `{{ build.event }}`)\n{{/if}}\nCommit: \nBranch: \nAuthor: {{ build.author }}\n<{{ build.link }}|Visit build page>" + webhook: + from_secret: slack_webhook + when: + status: + - success + - failure + +trigger: + status: + - success + - failure + +depends_on: +- default + +... diff --git a/.gitignore b/.gitignore index a1ebe38..6abecd6 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,5 @@ -/wglan-manager +# THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. +# +# Generated on 2021-08-13T12:22:39Z by kres 907039b. + +_out diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..dc98bdd --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,150 @@ +# THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. +# +# Generated on 2021-08-13T12:32:42Z by kres 907039b. + + +# options for analysis running +run: + timeout: 10m + issues-exit-code: 1 + tests: true + build-tags: [] + skip-dirs: [] + skip-dirs-use-default: true + skip-files: [] + modules-download-mode: readonly + +# output configuration options +output: + format: colored-line-number + print-issued-lines: true + print-linter-name: true + uniq-by-line: true + path-prefix: "" + + +# all available settings of specific linters +linters-settings: + dogsled: + max-blank-identifiers: 2 + dupl: + threshold: 150 + errcheck: + check-type-assertions: true + check-blank: true + exhaustive: + default-signifies-exhaustive: false + funlen: + lines: 60 + statements: 40 + gci: + local-prefixes: github.com/talos-systems/kubespan-manager + gocognit: + min-complexity: 30 + nestif: + min-complexity: 5 + goconst: + min-len: 3 + min-occurrences: 3 + gocritic: + disabled-checks: [] + gocyclo: + min-complexity: 20 + godot: + check-all: false + godox: + keywords: # default keywords are TODO, BUG, and FIXME, these can be overwritten by this setting + - NOTE + - OPTIMIZE # marks code that should be optimized before merging + - HACK # marks hack-arounds that should be removed before merging + gofmt: + simplify: true + goimports: + local-prefixes: github.com/talos-systems/kubespan-manager + golint: + min-confidence: 0.8 + gomnd: + settings: {} + gomodguard: {} + govet: + check-shadowing: true + enable-all: true + depguard: + list-type: blacklist + include-go-root: false + lll: + line-length: 200 + tab-width: 4 + misspell: + locale: US + ignore-words: [] + nakedret: + max-func-lines: 30 + prealloc: + simple: true + range-loops: true # Report preallocation suggestions on range loops, true by default + for-loops: false # Report preallocation suggestions on for loops, false by default + nolintlint: + allow-unused: false + allow-leading-space: false + allow-no-explanation: [] + require-explanation: false + require-specific: true + rowserrcheck: {} + testpackage: + unparam: + check-exported: false + unused: + check-exported: false + whitespace: + multi-if: false # Enforces newlines (or comments) after every multi-line if statement + multi-func: false # Enforces newlines (or comments) after every multi-line function signature + wsl: + strict-append: true + allow-assign-and-call: true + allow-multiline-assign: true + allow-cuddle-declarations: false + allow-trailing-comment: false + force-case-trailing-whitespace: 0 + force-err-cuddling: false + allow-separated-leading-comment: false + gofumpt: + extra-rules: false + cyclop: + # the maximal code complexity to report + max-complexity: 20 + +linters: + enable-all: true + disable: + - gas + - typecheck + - gochecknoglobals + - gochecknoinits + - funlen + - godox + - gomnd + - goerr113 + - nestif + - wrapcheck + - paralleltest + - exhaustivestruct + - forbidigo + disable-all: false + fast: false + + +issues: + exclude: [] + exclude-rules: [] + exclude-use-default: false + exclude-case-sensitive: false + max-issues-per-linter: 10 + max-same-issues: 3 + + new: false + +severity: + default-severity: error + + case-sensitive: false diff --git a/.markdownlint.json b/.markdownlint.json new file mode 100644 index 0000000..ee19dd0 --- /dev/null +++ b/.markdownlint.json @@ -0,0 +1,9 @@ +# THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. +# +# Generated on 2021-08-13T12:22:39Z by kres 907039b. + +{ + "MD013": false, + "MD033": false, + "default": true + } diff --git a/Dockerfile b/Dockerfile index de95402..30e1777 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,12 +1,96 @@ -FROM golang:alpine AS builder -ENV GO111MODULE on -RUN apk add --no-cache git -WORKDIR $GOPATH/src/github.com/talos-systems/wglan-manager -COPY . . -RUN go get -d -v -RUN go build -o /go/bin/web +# syntax = docker/dockerfile-upstream:1.2.0-labs + +# THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. +# +# Generated on 2021-08-13T12:32:42Z by kres 907039b. + +ARG TOOLCHAIN + +# cleaned up specs and compiled versions +FROM scratch AS generate + +FROM ghcr.io/talos-systems/ca-certificates:v0.3.0-12-g90722c3 AS image-ca-certificates + +FROM ghcr.io/talos-systems/fhs:v0.3.0-12-g90722c3 AS image-fhs + +# runs markdownlint +FROM node:14.8.0-alpine AS lint-markdown +RUN npm i -g markdownlint-cli@0.23.2 +RUN npm i sentences-per-line@0.2.1 +WORKDIR /src +COPY .markdownlint.json . +COPY ./README.md ./README.md +RUN markdownlint --ignore "CHANGELOG.md" --ignore "**/node_modules/**" --ignore '**/hack/chglog/**' --rules /node_modules/sentences-per-line/index.js . + +# base toolchain image +FROM ${TOOLCHAIN} AS toolchain +RUN apk --update --no-cache add bash curl build-base protoc protobuf-dev + +# build tools +FROM toolchain AS tools +ENV GO111MODULE on +ENV CGO_ENABLED 0 +ENV GOPATH /go +RUN curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b /bin v1.38.0 +ARG GOFUMPT_VERSION +RUN cd $(mktemp -d) \ + && go mod init tmp \ + && go get mvdan.cc/gofumpt/gofumports@${GOFUMPT_VERSION} \ + && mv /go/bin/gofumports /bin/gofumports + +# tools and sources +FROM tools AS base +WORKDIR /src +COPY ./go.mod . +COPY ./go.sum . +RUN --mount=type=cache,target=/go/pkg go mod download +RUN --mount=type=cache,target=/go/pkg go mod verify +COPY ./internal ./internal +COPY ./pkg ./pkg +COPY ./cmd ./cmd +RUN --mount=type=cache,target=/go/pkg go list -mod=readonly all >/dev/null + +# builds kubespan-manager-linux-amd64 +FROM base AS kubespan-manager-linux-amd64-build +COPY --from=generate / / +WORKDIR /src/cmd/kubespan-manager +RUN --mount=type=cache,target=/root/.cache/go-build --mount=type=cache,target=/go/pkg go build -ldflags "-s -w" -o /kubespan-manager-linux-amd64 + +# runs gofumpt +FROM base AS lint-gofumpt +RUN find . -name '*.pb.go' | xargs -r rm +RUN find . -name '*.pb.gw.go' | xargs -r rm +RUN FILES="$(gofumports -l -local github.com/talos-systems/kubespan-manager .)" && test -z "${FILES}" || (echo -e "Source code is not formatted with 'gofumports -w -local github.com/talos-systems/kubespan-manager .':\n${FILES}"; exit 1) + +# runs golangci-lint +FROM base AS lint-golangci-lint +COPY .golangci.yml . +ENV GOGC 50 +RUN --mount=type=cache,target=/root/.cache/go-build --mount=type=cache,target=/root/.cache/golangci-lint --mount=type=cache,target=/go/pkg golangci-lint run --config .golangci.yml + +# runs unit-tests with race detector +FROM base AS unit-tests-race +ARG TESTPKGS +RUN --mount=type=cache,target=/root/.cache/go-build --mount=type=cache,target=/go/pkg --mount=type=cache,target=/tmp CGO_ENABLED=1 go test -v -race -count 1 ${TESTPKGS} + +# runs unit-tests +FROM base AS unit-tests-run +ARG TESTPKGS +RUN --mount=type=cache,target=/root/.cache/go-build --mount=type=cache,target=/go/pkg --mount=type=cache,target=/tmp go test -v -covermode=atomic -coverprofile=coverage.txt -coverpkg=${TESTPKGS} -count 1 ${TESTPKGS} + +FROM scratch AS kubespan-manager-linux-amd64 +COPY --from=kubespan-manager-linux-amd64-build /kubespan-manager-linux-amd64 /kubespan-manager-linux-amd64 + +FROM scratch AS unit-tests +COPY --from=unit-tests-run /src/coverage.txt /coverage.txt + +FROM kubespan-manager-linux-${TARGETARCH} AS kubespan-manager + +FROM scratch AS image-kubespan-manager +ARG TARGETARCH +COPY --from=kubespan-manager kubespan-manager-linux-${TARGETARCH} /kubespan-manager +COPY --from=image-fhs / / +COPY --from=image-ca-certificates / / +LABEL org.opencontainers.image.source https://github.com/talos-systems/wglan-manager +ENTRYPOINT ["/kubespan-manager"] -FROM alpine -RUN apk add --no-cache ca-certificates -COPY --from=builder /go/bin/web /go/bin/web -ENTRYPOINT ["/go/bin/web"] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..5012095 --- /dev/null +++ b/Makefile @@ -0,0 +1,154 @@ +# THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. +# +# Generated on 2021-08-13T12:32:42Z by kres 907039b. + +# common variables + +SHA := $(shell git describe --match=none --always --abbrev=8 --dirty) +TAG := $(shell git describe --tag --always --dirty) +BRANCH := $(shell git rev-parse --abbrev-ref HEAD) +ARTIFACTS := _out +REGISTRY ?= ghcr.io +USERNAME ?= talos-systems +REGISTRY_AND_USERNAME ?= $(REGISTRY)/$(USERNAME) +GOFUMPT_VERSION ?= abc0db2c416aca0f60ea33c23c76665f6e7ba0b6 +GO_VERSION ?= 1.16 +PROTOBUF_GO_VERSION ?= 1.25.0 +GRPC_GO_VERSION ?= 1.1.0 +GRPC_GATEWAY_VERSION ?= 2.4.0 +TESTPKGS ?= ./... +KRES_IMAGE ?= ghcr.io/talos-systems/kres:latest + +# docker build settings + +BUILD := docker buildx build +PLATFORM ?= linux/amd64 +PROGRESS ?= auto +PUSH ?= false +CI_ARGS ?= +COMMON_ARGS = --file=Dockerfile +COMMON_ARGS += --progress=$(PROGRESS) +COMMON_ARGS += --platform=$(PLATFORM) +COMMON_ARGS += --push=$(PUSH) +COMMON_ARGS += --build-arg=ARTIFACTS=$(ARTIFACTS) +COMMON_ARGS += --build-arg=SHA=$(SHA) +COMMON_ARGS += --build-arg=TAG=$(TAG) +COMMON_ARGS += --build-arg=USERNAME=$(USERNAME) +COMMON_ARGS += --build-arg=TOOLCHAIN=$(TOOLCHAIN) +COMMON_ARGS += --build-arg=GOFUMPT_VERSION=$(GOFUMPT_VERSION) +COMMON_ARGS += --build-arg=PROTOBUF_GO_VERSION=$(PROTOBUF_GO_VERSION) +COMMON_ARGS += --build-arg=GRPC_GO_VERSION=$(GRPC_GO_VERSION) +COMMON_ARGS += --build-arg=GRPC_GATEWAY_VERSION=$(GRPC_GATEWAY_VERSION) +COMMON_ARGS += --build-arg=TESTPKGS=$(TESTPKGS) +TOOLCHAIN ?= docker.io/golang:1.16-alpine + +# help menu + +export define HELP_MENU_HEADER +# Getting Started + +To build this project, you must have the following installed: + +- git +- make +- docker (19.03 or higher) + +## Creating a Builder Instance + +The build process makes use of experimental Docker features (buildx). +To enable experimental features, add 'experimental: "true"' to '/etc/docker/daemon.json' on +Linux or enable experimental features in Docker GUI for Windows or Mac. + +To create a builder instance, run: + + docker buildx create --name local --use + + +If you already have a compatible builder instance, you may use that instead. + +## Artifacts + +All artifacts will be output to ./$(ARTIFACTS). Images will be tagged with the +registry "$(REGISTRY)", username "$(USERNAME)", and a dynamic tag (e.g. $(IMAGE):$(TAG)). +The registry and username can be overridden by exporting REGISTRY, and USERNAME +respectively. + +endef + +all: unit-tests kubespan-manager image-kubespan-manager lint + +.PHONY: clean +clean: ## Cleans up all artifacts. + @rm -rf $(ARTIFACTS) + +target-%: ## Builds the specified target defined in the Dockerfile. The build result will only remain in the build cache. + @$(BUILD) --target=$* $(COMMON_ARGS) $(TARGET_ARGS) $(CI_ARGS) . + +local-%: ## Builds the specified target defined in the Dockerfile using the local output type. The build result will be output to the specified local destination. + @$(MAKE) target-$* TARGET_ARGS="--output=type=local,dest=$(DEST) $(TARGET_ARGS)" + +lint-golangci-lint: ## Runs golangci-lint linter. + @$(MAKE) target-$@ + +lint-gofumpt: ## Runs gofumpt linter. + @$(MAKE) target-$@ + +.PHONY: fmt +fmt: ## Formats the source code + @docker run --rm -it -v $(PWD):/src -w /src golang:$(GO_VERSION) \ + bash -c "export GO111MODULE=on; export GOPROXY=https://proxy.golang.org; \ + cd /tmp && go mod init tmp && go get mvdan.cc/gofumpt/gofumports@$(GOFUMPT_VERSION) && \ + cd - && gofumports -w -local github.com/talos-systems/kubespan-manager ." + +.PHONY: base +base: ## Prepare base toolchain + @$(MAKE) target-$@ + +.PHONY: unit-tests +unit-tests: ## Performs unit tests + @$(MAKE) local-$@ DEST=$(ARTIFACTS) + +.PHONY: unit-tests-race +unit-tests-race: ## Performs unit tests with race detection enabled. + @$(MAKE) target-$@ + +.PHONY: coverage +coverage: ## Upload coverage data to codecov.io. + bash -c "bash <(curl -s https://codecov.io/bash) -f $(ARTIFACTS)/coverage.txt -X fix" + +.PHONY: $(ARTIFACTS)/kubespan-manager-linux-amd64 +$(ARTIFACTS)/kubespan-manager-linux-amd64: + @$(MAKE) local-kubespan-manager-linux-amd64 DEST=$(ARTIFACTS) + +.PHONY: kubespan-manager-linux-amd64 +kubespan-manager-linux-amd64: $(ARTIFACTS)/kubespan-manager-linux-amd64 ## Builds executable for kubespan-manager-linux-amd64. + +.PHONY: kubespan-manager +kubespan-manager: kubespan-manager-linux-amd64 + +.PHONY: lint-markdown +lint-markdown: ## Runs markdownlint. + @$(MAKE) target-$@ + +.PHONY: lint +lint: lint-golangci-lint lint-gofumpt lint-markdown ## Run all linters for the project. + +.PHONY: image-kubespan-manager +image-kubespan-manager: ## Builds image for kubespan-manager. + @$(MAKE) target-$@ TARGET_ARGS="--tag=$(REGISTRY)/$(USERNAME)/kubespan-manager:$(TAG)" + +.PHONY: rekres +rekres: + @docker pull $(KRES_IMAGE) + @docker run --rm -v $(PWD):/src -w /src -e GITHUB_TOKEN $(KRES_IMAGE) + +.PHONY: help +help: ## This help menu. + @echo "$$HELP_MENU_HEADER" + @grep -E '^[a-zA-Z%_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' + +.PHONY: release-notes +release-notes: + mkdir -p $(ARTIFACTS) + @ARTIFACTS=$(ARTIFACTS) ./hack/release.sh $@ $(ARTIFACTS)/RELEASE_NOTES.md $(TAG) + diff --git a/main.go b/cmd/kubespan-manager/main.go similarity index 77% rename from main.go rename to cmd/kubespan-manager/main.go index b319489..80166cb 100644 --- a/main.go +++ b/cmd/kubespan-manager/main.go @@ -1,6 +1,11 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + package main import ( + "errors" "flag" "log" "net/http" @@ -8,15 +13,16 @@ import ( "time" "github.com/gofiber/fiber/v2" - "github.com/talos-systems/wglan-manager/db" - "github.com/talos-systems/wglan-manager/types" "go.uber.org/zap" + + "github.com/talos-systems/kubespan-manager/internal/db" + "github.com/talos-systems/kubespan-manager/pkg/types" ) -var listenAddr = ":3000" -var devMode bool - -const defaultPort = 5000 +var ( + listenAddr = ":3000" + devMode bool +) var nodeDB db.DB @@ -25,31 +31,29 @@ func init() { flag.BoolVar(&devMode, "debug", false, "enable debug mode") } +//nolint:gocognit func main() { - flag.Parse() logger, err := zap.NewProduction() if err != nil { - log.Fatalln("failed to initialise logger:", err) + log.Fatalln("failed to initialize logger:", err) } if os.Getenv("MODE") == "dev" { devMode = true logger, err = zap.NewDevelopment() + if err != nil { - log.Fatalln("failed to initialise development logger:", err) + log.Fatalln("failed to initialize development logger:", err) } } - defer logger.Sync() // nolint: errcheck - if os.Getenv("REDIS_ADDR") != "" { nodeDB, err = db.NewRedis(os.Getenv("REDIS_ADDR"), logger) if err != nil { log.Fatalln("failed to connect to redis: %w", err) } - } else { nodeDB = db.New(logger) } @@ -60,16 +64,22 @@ func main() { cluster := c.Params("cluster") if cluster == "" { logger.Error("empty cluster for node list") + return c.SendStatus(http.StatusBadRequest) } - list, err := nodeDB.List(c.Context(), cluster) - if len(list) < 1 { - logger.Warn("cluster not found", - zap.String("cluster", cluster), - zap.Error(err), - ) - return c.SendStatus(http.StatusNotFound) + list, e := nodeDB.List(c.Context(), cluster) + if e != nil { + if errors.Is(e, db.ErrNotFound) { + logger.Warn("cluster not found", + zap.String("cluster", cluster), + zap.Error(err), + ) + + return c.SendStatus(http.StatusNotFound) + } + + return c.SendStatus(http.StatusInternalServerError) } logger.Info("listing cluster nodes", @@ -78,13 +88,13 @@ func main() { ) return c.JSON(list) - }) app.Get("/:cluster/:node", func(c *fiber.Ctx) error { cluster := c.Params("cluster", "") if cluster == "" { logger.Error("empty cluster for node get") + return c.SendStatus(http.StatusBadRequest) } @@ -93,17 +103,23 @@ func main() { logger.Error("empty node for node get", zap.String("cluster", c.Params("cluster", "")), ) + return c.SendStatus(http.StatusBadRequest) } - n, err := nodeDB.Get(c.Context(), cluster, node) - if err != nil { - logger.Warn("node not found", - zap.String("cluster", cluster), - zap.String("node", node), - zap.Error(err), - ) - return c.SendStatus(http.StatusNotFound) + n, e := nodeDB.Get(c.Context(), cluster, node) + if e != nil { + if errors.Is(e, db.ErrNotFound) { + logger.Warn("node not found", + zap.String("cluster", cluster), + zap.String("node", node), + zap.Error(err), + ) + + return c.SendStatus(http.StatusNotFound) + } + + return c.SendStatus(http.StatusInternalServerError) } logger.Info("returning cluster node", @@ -121,12 +137,13 @@ func main() { app.Put("/:cluster/:node", func(c *fiber.Ctx) error { var addresses []*types.Address - if err := c.BodyParser(&addresses); err != nil { + if e := c.BodyParser(&addresses); e != nil { logger.Error("failed to parse node PUT", zap.String("cluster", c.Params("cluster", "")), zap.String("node", c.Params("node", "")), zap.Error(err), ) + return c.SendStatus(http.StatusBadRequest) } @@ -146,6 +163,7 @@ func main() { zap.Strings("addresses", addressToString(addresses)), zap.Error(err), ) + return c.SendStatus(http.StatusInternalServerError) } @@ -160,6 +178,7 @@ func main() { zap.String("cluster", c.Params("cluster", "")), zap.Error(err), ) + return c.SendStatus(http.StatusBadRequest) } @@ -171,6 +190,7 @@ func main() { zap.Strings("addresses", addressToString(n.Addresses)), zap.Error(err), ) + return c.SendStatus(http.StatusInternalServerError) } diff --git a/go.mod b/go.mod index 2119a1e..1256e2d 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/talos-systems/wglan-manager +module github.com/talos-systems/kubespan-manager go 1.16 diff --git a/hack/git-chglog/CHANGELOG.tpl.md b/hack/git-chglog/CHANGELOG.tpl.md new file mode 100644 index 0000000..c0856ce --- /dev/null +++ b/hack/git-chglog/CHANGELOG.tpl.md @@ -0,0 +1,26 @@ + + + + +{{ range .Versions }} + +## {{ if .Tag.Previous }}[{{ .Tag.Name }}]({{ $.Info.RepositoryURL }}/compare/{{ .Tag.Previous.Name }}...{{ .Tag.Name }}){{ else }}{{ .Tag.Name }}{{ end }} ({{ datetime "2006-01-02" .Tag.Date }}) + +{{ range .CommitGroups -}} +### {{ .Title }} + +{{ range .Commits -}} +* {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }} +{{ end }} +{{ end -}} + +{{- if .NoteGroups -}} +{{ range .NoteGroups -}} +### {{ .Title }} + +{{ range .Notes }} +{{ .Body }} +{{ end }} +{{ end -}} +{{ end -}} +{{ end -}} diff --git a/hack/git-chglog/config.yaml b/hack/git-chglog/config.yaml new file mode 100644 index 0000000..4087684 --- /dev/null +++ b/hack/git-chglog/config.yaml @@ -0,0 +1,32 @@ +# THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. +# +# Generated on 2021-08-13T12:22:39Z by kres 907039b. + +style: github +template: CHANGELOG.tpl.md +info: + title: CHANGELOG + repository_url: https://github.com/talos-systems/wglan-manager +options: + commits: + # filters: + # Type: + # - feat + # - fix + # - perf + # - refactor + commit_groups: + # title_maps: + # feat: Features + # fix: Bug Fixes + # perf: Performance Improvements + # refactor: Code Refactoring + header: + pattern: "^(\\w*)(?:\\(([\\w\\$\\.\\-\\*\\s]*)\\))?\\:\\s(.*)$" + pattern_maps: + - Type + - Scope + - Subject + notes: + keywords: + - BREAKING CHANGE diff --git a/hack/release.sh b/hack/release.sh new file mode 100755 index 0000000..b045f80 --- /dev/null +++ b/hack/release.sh @@ -0,0 +1,68 @@ +#!/bin/bash + +# THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. +# +# Generated on 2021-08-13T12:22:39Z by kres 907039b. + + +#!/bin/bash + +set -e + +RELEASE_TOOL_IMAGE="ghcr.io/talos-systems/release-tool:latest" + +function release-tool { + docker pull "${RELEASE_TOOL_IMAGE}" >/dev/null + docker run --rm -w /src -v "${PWD}":/src:ro "${RELEASE_TOOL_IMAGE}" -l -d -n -t "${1}" ./hack/release.toml +} + +function changelog { + if [ "$#" -eq 1 ]; then + (release-tool ${1}; echo; cat CHANGELOG.md) > CHANGELOG.md- && mv CHANGELOG.md- CHANGELOG.md + else + echo 1>&2 "Usage: $0 changelog [tag]" + exit 1 + fi +} + +function release-notes { + release-tool "${2}" > "${1}" +} + +function cherry-pick { + if [ $# -ne 2 ]; then + echo 1>&2 "Usage: $0 cherry-pick " + exit 1 + fi + + git checkout $2 + git fetch + git rebase upstream/$2 + git cherry-pick -x $1 +} + +function commit { + if [ $# -ne 1 ]; then + echo 1>&2 "Usage: $0 commit " + exit 1 + fi + + git commit -s -m "release($1): prepare release" -m "This is the official $1 release." +} + +if declare -f "$1" > /dev/null +then + cmd="$1" + shift + $cmd "$@" +else + cat < 299 { return fmt.Errorf("server rejected Node information: %s", resp.Status) @@ -31,14 +42,14 @@ func Add(rootURL, clusterID string, n *types.Node) error { } // AddAddresses adds a list of addresses to a node. -func AddAddresses(rootURL, clusterID string, id string, epList ... *types.Address) error { +func AddAddresses(rootURL, clusterID, id string, epList ...*types.Address) error { buf := new(bytes.Buffer) if err := json.NewEncoder(buf).Encode(epList); err != nil { return fmt.Errorf("failed to encode known endpoints: %w", err) } - req, err := http.NewRequest(http.MethodPut, fmt.Sprintf("%s/%s/%s", rootURL, clusterID, id), buf) + req, err := http.NewRequestWithContext(context.TODO(), http.MethodPut, fmt.Sprintf("%s/%s/%s", rootURL, clusterID, id), buf) if err != nil { return fmt.Errorf("failed to make PUT request: %w", err) } @@ -47,23 +58,28 @@ func AddAddresses(rootURL, clusterID string, id string, epList ... *types.Addres if err != nil { return fmt.Errorf("failed to add endpoints to node %q/%q: %w", clusterID, id, err) } + defer resp.Body.Close() //nolint:errcheck if resp.StatusCode > 299 { return fmt.Errorf("server rejected new endpoints for node %q/%q: %s", clusterID, id, resp.Status) } return nil - } // Get returns the Node defined by the given public key, if and only if it exists within the given Cluster ID. -func Get(rootURL, clusterID string, publicKey string) (*types.Node, error) { - - resp, err := http.Get(fmt.Sprintf("%s/%s/%s", rootURL, clusterID, publicKey)) +func Get(rootURL, clusterID, publicKey string) (*types.Node, error) { + req, err := http.NewRequestWithContext(context.TODO(), http.MethodGet, fmt.Sprintf("%s/%s/%s", rootURL, clusterID, publicKey), nil) if err != nil { return nil, fmt.Errorf("failed to request node %q/%q from server %q: %w", clusterID, publicKey, rootURL, err) } + resp, err := http.DefaultClient.Do(req) + if err != nil { + return nil, fmt.Errorf("failed to request node %q/%q from server %q: %w", clusterID, publicKey, rootURL, err) + } + defer resp.Body.Close() //nolint:errcheck + node := new(types.Node) if err = json.NewDecoder(resp.Body).Decode(node); err != nil { return nil, fmt.Errorf("failed to decode response from server: %w", err) @@ -73,13 +89,17 @@ func Get(rootURL, clusterID string, publicKey string) (*types.Node, error) { } // List returns the set of Nodes associated with the given Cluster ID. -func List(rootURL string, clusterID string) ([]*types.Node,error) { - - resp, err := http.Get(fmt.Sprintf("%s/%s", rootURL, clusterID)) +func List(rootURL, clusterID string) ([]*types.Node, error) { + req, err := http.NewRequestWithContext(context.TODO(), http.MethodGet, fmt.Sprintf("%s/%s", rootURL, clusterID), nil) if err != nil { return nil, fmt.Errorf("failed to request list from server %q: %w", rootURL, err) } - defer resp.Body.Close() // nolint: errcheck + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return nil, fmt.Errorf("failed to request list from server %q: %w", rootURL, err) + } + defer resp.Body.Close() //nolint:errcheck var list []*types.Node if err = json.NewDecoder(resp.Body).Decode(&list); err != nil { @@ -87,6 +107,4 @@ func List(rootURL string, clusterID string) ([]*types.Node,error) { } return list, nil - } - diff --git a/types/types.go b/pkg/types/types.go similarity index 86% rename from types/types.go rename to pkg/types/types.go index 7a02e29..5addb17 100644 --- a/types/types.go +++ b/pkg/types/types.go @@ -1,3 +1,8 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +// Package types contains all manager data types. package types import ( @@ -12,22 +17,19 @@ import ( // Address describes an IP or DNS address with optional Port. type Address struct { - // Name is the DNS name of this NodeAddress, if known. - Name string `json:"name,omitempty"` - - // IP is the IP address of this NodeAddress, if known. - IP netaddr.IP `json:"ip,omitempty"` - - // Port is the port number for this NodeAddress, if known. - Port uint16 `json:"port,omitempty"` - // LastReported indicates the time at which this address was last reported. LastReported time.Time `json:"lastReported"` + // IP is the IP address of this NodeAddress, if known. + IP netaddr.IP `json:"ip,omitempty"` + // Name is the DNS name of this NodeAddress, if known. + Name string `json:"name,omitempty"` + // Port is the port number for this NodeAddress, if known. + Port uint16 `json:"port,omitempty"` } // EqualHost indicates whether two addresses have the same host portion, ignoring the ports. func (a *Address) EqualHost(other *Address) bool { - if ! a.IP.IsZero() || ! other.IP.IsZero() { + if !a.IP.IsZero() || !other.IP.IsZero() { return a.IP == other.IP } @@ -67,7 +69,7 @@ func (a *Address) Endpoint(defaultPort uint16) (*net.UDPAddr, error) { return net.ResolveUDPAddr(proto, fmt.Sprintf("%s:%d", addr, port)) } -// Node describes a Wireguard Peer +// Node describes a Wireguard Peer. type Node struct { // Name is the human-readable identifier of this Node. // Usually, this is the kubernetes Node name. @@ -88,7 +90,7 @@ type Node struct { } // AddAddresses adds a set of addresses to a Node. -func (n *Node) AddAddresses(addresses ... *Address) { +func (n *Node) AddAddresses(addresses ...*Address) { n.mu.Lock() defer n.mu.Unlock() @@ -134,19 +136,17 @@ func (n *Node) ExpireAddressesOlderThan(maxAge time.Duration) { continue } - - a = nil } n.Addresses = n.Addresses[:i] } -// MarshalBinary implements encoding.BinaryMarshaler +// MarshalBinary implements encoding.BinaryMarshaler. func (n *Node) MarshalBinary() ([]byte, error) { return json.Marshal(n) } -// UnmarshalBinary implements encoding.BinaryUnmarshaler +// UnmarshalBinary implements encoding.BinaryUnmarshaler. func (n *Node) UnmarshalBinary(data []byte) error { *n = Node{} diff --git a/types/types_test.go b/pkg/types/types_test.go similarity index 64% rename from types/types_test.go rename to pkg/types/types_test.go index 70dfe14..311f946 100644 --- a/types/types_test.go +++ b/pkg/types/types_test.go @@ -1,18 +1,23 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + package types_test import ( "testing" "time" - "github.com/talos-systems/wglan-manager/types" "inet.af/netaddr" + + "github.com/talos-systems/kubespan-manager/pkg/types" ) func TestEncoderDecoder(t *testing.T) { n := &types.Node{ - Name: "tester", - ID: "IHOPEfmiUG1kE832FAxm77J5WP0O1ZHp9OwqbGowL1E=", - IP: netaddr.MustParseIP("2001:db8:1001::1"), + Name: "tester", + ID: "IHOPEfmiUG1kE832FAxm77J5WP0O1ZHp9OwqbGowL1E=", + IP: netaddr.MustParseIP("2001:db8:1001::1"), Addresses: []*types.Address{ { Name: "mynode.mydomain.com",