diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..4b8d73f8 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,22 @@ +--- +name: ci +on: + push: + pull_request: +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Setup Go + uses: actions/setup-go@v2 + - name: golangci-lint + uses: golangci/golangci-lint-action@v2 + with: + version: v1.32 + - name: GoReleaser + uses: goreleaser/goreleaser-action@v2 + with: + version: latest + args: release --snapshot --rm-dist --skip-publish diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..d437da22 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,30 @@ +--- +name: release +on: + push: + tags: + - v* +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Setup Go + uses: actions/setup-go@v2 + - name: golangci-lint + uses: golangci/golangci-lint-action@v2 + with: + version: v1.32 + - name: Login to DockerHub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: GoReleaser + uses: goreleaser/goreleaser-action@v2 + with: + version: latest + args: release --rm-dist + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..849ddff3 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +dist/ diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 00000000..4ae8c684 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,92 @@ +# options for analysis running +run: + tests: true + timeout: 3m + + +# all available settings of specific linters +linters-settings: + dupl: + threshold: 150 + funlen: + lines: 100 + statements: 50 + goconst: + min-len: 2 + min-occurrences: 3 + gocritic: + enabled-tags: + - diagnostic + - experimental + - opinionated + - performance + - style + disabled-checks: + - whyNoLint + gocyclo: + min-complexity: 15 + goimports: + local-prefixes: github.com/ewohltman/ephemeral-roles + golint: + min-confidence: 0.8 + govet: + check-shadowing: true + maligned: + suggest-new: true + misspell: + locale: UK + +linters: + disable: + - gomnd + - lll + enable: + - bodyclose + - deadcode + - depguard + - dogsled + - dupl + - errcheck + - funlen + - gochecknoglobals + - gochecknoinits + - gocognit + - goconst + - gocritic + - gocyclo + - godox + - gofmt + - goimports + - golint + - goprintffuncname + - gosec + - gosimple + - govet + - ineffassign + - interfacer + - maligned + - misspell + - nakedret + - prealloc + - rowserrcheck + - scopelint # todo + - staticcheck + - structcheck + - stylecheck + - typecheck + - unconvert + - unparam + - unused + - varcheck + - whitespace + - wsl + + +issues: + exclude: + # Very commonly not checked. + - 'Error return value of .(l.Sync|.*Close|.*Flush|os\.Remove(All)?|os\.(Un)?Setenv). is not checked' + - 'exported method (.*\.MarshalJSON|.*\.UnmarshalJSON) should have comment or be unexported' + - 'shadow: declaration of "err" shadows declaration.*' + max-same-issues: 0 + exclude-use-default: false diff --git a/.goreleaser.yml b/.goreleaser.yml new file mode 100644 index 00000000..78b76c23 --- /dev/null +++ b/.goreleaser.yml @@ -0,0 +1,30 @@ +--- +builds: + - env: + - CGO_ENABLED=0 + goos: + - linux + goarch: + - amd64 + main: main.go + binary: kubenurse + id: kubenurse +dockers: + - goos: linux + goarch: amd64 + binaries: + - kubenurse + image_templates: + - "postfinance/kubenurse:latest" + - "postfinance/kubenurse:{{ .Tag }}" + dockerfile: Dockerfile +checksum: + name_template: 'checksums.txt' +snapshot: + name_template: "{{ .Tag }}-next" +changelog: + sort: asc + filters: + exclude: + - '^docs:' + - '^test:' diff --git a/Dockerfile b/Dockerfile index ddc4762d..a2d8b9eb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,22 +1,8 @@ -FROM golang:1.15-alpine as builder -RUN apk --no-cache add git - -ENV GO111MODULE=on -ENV CGO_ENABLED=0 - -WORKDIR /vgo/ -COPY . . - -RUN go get ./... -RUN go test ./... -RUN go build -o /bin/kubenurse . - -# Build runtime -FROM alpine:latest as runtime +FROM alpine:latest MAINTAINER OpenSource PF -RUN apk --no-cache add ca-certificates -COPY --from=builder /bin/kubenurse /bin/kubenurse +RUN apk --no-cache add ca-certificates curl +COPY kubenurse /bin/kubenurse # Run as nobody:x:65534:65534:nobody:/:/sbin/nologin USER 65534 diff --git a/main.go b/main.go index 90ad90f0..0ce263fe 100644 --- a/main.go +++ b/main.go @@ -150,7 +150,7 @@ func aliveHandler(chk *checker.Checker) func(w http.ResponseWriter, r *http.Requ // Generate output output enc := json.NewEncoder(w) enc.SetIndent("", " ") - enc.Encode(out) + _ = enc.Encode(out) } } @@ -180,6 +180,7 @@ func GenerateRoundTripper() (http.RoundTripper, error) { // Append extra CA, if set if extraCA != "" { + //nolint:gosec caCert, err := ioutil.ReadFile(extraCA) if err != nil { @@ -193,7 +194,7 @@ func GenerateRoundTripper() (http.RoundTripper, error) { // Configure transport tlsConfig := &tls.Config{ - InsecureSkipVerify: insecure, + InsecureSkipVerify: insecure, //nolint:gosec RootCAs: rootCAs, } diff --git a/pkg/checker/checker.go b/pkg/checker/checker.go index efffd3ad..ca0851bf 100644 --- a/pkg/checker/checker.go +++ b/pkg/checker/checker.go @@ -13,7 +13,7 @@ import ( ) // New configures the checker with a httpClient and a cache timeout for check -// results. Other parameters of the Checker struct need to be configured seperately. +// results. Other parameters of the Checker struct need to be configured separately. func New(ctx context.Context, httpClient *http.Client, cacheTTL time.Duration) (*Checker, error) { discovery, err := kubediscovery.New(ctx) if err != nil { @@ -28,10 +28,12 @@ func New(ctx context.Context, httpClient *http.Client, cacheTTL time.Duration) ( } // Run runs an check and returns the result togeter with a boolean, if it wasn't -// successfull. It respects the cache. +// successful. It respects the cache. func (c *Checker) Run() (Result, bool) { - var haserr bool - var err error + var ( + haserr bool + err error + ) // Check if a result is cached and return it cacheRes := c.retrieveResultFromCache() @@ -107,12 +109,13 @@ func (c *Checker) MeService() (string, error) { // which are not schedulable are excluded from this check to avoid possible false errors. func (c *Checker) checkNeighbours(nh []kubediscovery.Neighbour) { for _, neighbour := range nh { + neighbour := neighbour // pin if neighbour.NodeSchedulable { check := func() (string, error) { return c.doRequest("http://" + neighbour.PodIP + ":8080/alwayshappy") } - measure(check, "path_"+neighbour.NodeName) + _, _ = measure(check, "path_"+neighbour.NodeName) } } } @@ -126,6 +129,7 @@ func measure(check Check, label string) (string, error) { // Process metrics metrics.DurationSummary.WithLabelValues(label).Observe(time.Since(start).Seconds()) + if err != nil { log.Printf("failed request for %s with %v", label, err) metrics.ErrorCounter.WithLabelValues(label).Inc() diff --git a/pkg/checker/transport.go b/pkg/checker/transport.go index 2e102516..83df907f 100644 --- a/pkg/checker/transport.go +++ b/pkg/checker/transport.go @@ -9,6 +9,7 @@ import ( ) const ( + //nolint:gosec tokenFile = "/var/run/secrets/kubernetes.io/serviceaccount/token" ) @@ -20,7 +21,7 @@ func (c *Checker) doRequest(url string) (string, error) { return "error", fmt.Errorf("could not load token %s: %s", tokenFile, err) } - req, err := http.NewRequest("GET", url, nil) + req, _ := http.NewRequest("GET", url, nil) // Only add the Bearer for API Server Requests if strings.HasSuffix(url, "/version") { @@ -33,7 +34,7 @@ func (c *Checker) doRequest(url string) (string, error) { } // Body is non-nil if err is nil, so close it - resp.Body.Close() + _ = resp.Body.Close() if resp.StatusCode == http.StatusOK { return "ok", nil diff --git a/pkg/kubediscovery/kubediscovery.go b/pkg/kubediscovery/kubediscovery.go index 388996ab..9e01123d 100644 --- a/pkg/kubediscovery/kubediscovery.go +++ b/pkg/kubediscovery/kubediscovery.go @@ -61,10 +61,12 @@ func (c *Client) GetNeighbours(ctx context.Context, namespace, labelSelector str return nil, fmt.Errorf("list pods: %w", err) } - var neighbours []Neighbour + var neighbours = make([]Neighbour, len(pods.Items)) // process pods - for _, pod := range pods.Items { + for idx := range pods.Items { + pod := pods.Items[idx] + n := Neighbour{ PodName: pod.Name, PodIP: pod.Status.PodIP, @@ -73,7 +75,7 @@ func (c *Client) GetNeighbours(ctx context.Context, namespace, labelSelector str NodeName: pod.Spec.NodeName, NodeSchedulable: c.nodeCache.isSchedulable(pod.Spec.NodeName), } - neighbours = append(neighbours, n) + neighbours[idx] = n } return neighbours, nil diff --git a/pkg/kubediscovery/nodewatcher.go b/pkg/kubediscovery/nodewatcher.go index 4f63a00e..830b1291 100644 --- a/pkg/kubediscovery/nodewatcher.go +++ b/pkg/kubediscovery/nodewatcher.go @@ -63,7 +63,7 @@ func (nc *nodeCache) delete(obj interface{}) { nc.mu.Unlock() } -func (nc *nodeCache) update(_ interface{}, obj interface{}) { +func (nc *nodeCache) update(_, obj interface{}) { node := obj.(*corev1.Node) nc.mu.Lock() diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go index c39d1a91..4bc5bce6 100644 --- a/pkg/metrics/metrics.go +++ b/pkg/metrics/metrics.go @@ -1,3 +1,4 @@ +// Package metrics sets-up the metrics which will be exported by kubenurse. TODO: rewrite this package. package metrics import ( @@ -6,6 +7,7 @@ import ( "github.com/prometheus/client_golang/prometheus" ) +//nolint:gochecknoglobals var ( // ErrorCounter provides the kubenurse_errors_total metric ErrorCounter = prometheus.NewCounterVec( @@ -28,6 +30,7 @@ var ( ) ) +//nolint:gochecknoinits func init() { prometheus.MustRegister(ErrorCounter) prometheus.MustRegister(DurationSummary)