diff --git a/.github/workflows/build-docker-image.yml b/.github/workflows/build-docker-image.yml index 67023b07..3b8593fe 100644 --- a/.github/workflows/build-docker-image.yml +++ b/.github/workflows/build-docker-image.yml @@ -35,12 +35,12 @@ concurrency: jobs: build: if: github.repository == 'apache/cloudstack-kubernetes-provider' - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest steps: - name: Set Docker repository name run: echo "DOCKER_REPOSITORY=apache" >> $GITHUB_ENV - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: fetch-depth: 0 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6c1e0f2e..51bdeaf5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,6 +17,9 @@ name: Test-Build +permissions: + contents: read + on: [push, pull_request] concurrency: @@ -25,16 +28,21 @@ concurrency: jobs: build: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Set up Go - uses: actions/setup-go@v5 + uses: actions/setup-go@v6 with: go-version-file: go.mod - name: Run Script run: make test + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v5 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: coverage.txt diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 00000000..ebc7d1b2 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,46 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +name: CodeQL Analysis +on: + push: + branches: [main] + pull_request: + branches: [main] +permissions: + actions: read + contents: read + security-events: write +jobs: + codeql: + name: CodeQL + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + language: ["actions", "go"] + steps: + - name: Checkout repository + uses: actions/checkout@v6 + - name: Initialize CodeQL + uses: github/codeql-action/init@v4 + with: + languages: ${{ matrix.language }} + - name: Autobuild + uses: github/codeql-action/autobuild@v4 + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v4 \ No newline at end of file diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index 76364914..ed7750bd 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -30,9 +30,9 @@ jobs: name: lint runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-go@v5 + - uses: actions/checkout@v6 + - uses: actions/setup-go@v6 with: go-version-file: go.mod - name: golangci-lint - uses: golangci/golangci-lint-action@v6 + uses: golangci/golangci-lint-action@v9 diff --git a/.gitignore b/.gitignore index b85ea428..79e12562 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,31 @@ /cloudstack-ccm +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Code coverage profiles and other test artifacts +*.out +coverage.* +*.coverprofile +profile.cov + +# Dependency directories (remove the comment below to include it) +# vendor/ + +# Go workspace file +go.work +go.work.sum + +# env file +.env + +# Editor/IDE +.idea/ +.vscode/ \ No newline at end of file diff --git a/.golangci.yml b/.golangci.yml index 5bc9b136..379c873f 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -15,39 +15,53 @@ # specific language governing permissions and limitations # under the License. -linters-settings: - goheader: - template: |- - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - +version: "2" +run: + modules-download-mode: readonly + issues-exit-code: 1 linters: enable: - goheader - gosec - - gosimple + - misspell - govet - ineffassign - - misspell - staticcheck - - typecheck - unused - -run: - modules-download-mode: readonly - timeout: 5m - issues-exit-code: 1 + settings: + goheader: + template: |- + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling + paths: + - third_party$ + - builtin$ + - examples$ +formatters: + exclusions: + generated: lax + paths: + - third_party$ + - builtin$ + - examples$ diff --git a/Dockerfile b/Dockerfile index ba02c0dc..7b8145a5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,8 +19,13 @@ FROM --platform=$BUILDPLATFORM golang:1.23 AS builder ARG BUILDPLATFORM ARG TARGETOS ARG TARGETARCH -COPY . /go/src/github.com/apache/cloudstack-kubernetes-provider + WORKDIR /go/src/github.com/apache/cloudstack-kubernetes-provider +COPY go.mod /go/src/github.com/apache/cloudstack-kubernetes-provider/go.mod +COPY go.sum /go/src/github.com/apache/cloudstack-kubernetes-provider/go.sum +RUN go mod download + +COPY . /go/src/github.com/apache/cloudstack-kubernetes-provider RUN make clean && CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} make FROM gcr.io/distroless/static:nonroot diff --git a/Makefile b/Makefile index aebd9ff7..bbb03f0d 100644 --- a/Makefile +++ b/Makefile @@ -42,14 +42,21 @@ clean: cloudstack-ccm: ${CMD_SRC} go build -ldflags ${LDFLAGS} -o $@ $^ -test: - go test -v +test: gofmt + go test -v -coverprofile=coverage.txt -covermode=atomic go vet - @(echo "gofmt -l"; FMTFILES="$$(gofmt -l .)"; if test -n "$${FMTFILES}"; then echo "Go files that need to be reformatted (use 'go fmt'):\n$${FMTFILES}"; exit 1; fi) -docker: +docker: gofmt docker build . -t apache/cloudstack-kubernetes-provider:${GIT_COMMIT_SHORT} docker tag apache/cloudstack-kubernetes-provider:${GIT_COMMIT_SHORT} apache/cloudstack-kubernetes-provider:latest ifneq (${GIT_IS_TAG},NOT_A_TAG) docker tag apache/cloudstack-kubernetes-provider:${GIT_COMMIT_SHORT} apache/cloudstack-kubernetes-provider:${GIT_TAG} endif + +lint: gofmt + @(echo "Running golangci-lint...") + golangci-lint run + +gofmt: + @(echo "Running gofmt...") + @(echo "gofmt -l"; FMTFILES="$$(gofmt -l .)"; if test -n "$${FMTFILES}"; then echo "Go files that need to be reformatted (use 'go fmt'):\n$${FMTFILES}"; exit 1; fi) diff --git a/README.md b/README.md index cdd0aaf1..b16ba926 100644 --- a/README.md +++ b/README.md @@ -126,7 +126,7 @@ Instead, it first obtains the name of the node from Kubernetes, then fetches inf ### Building -At least Go 1.21 is required to build cloudstack-ccm. +At least Go 1.23 is required to build cloudstack-ccm. To build the controller with correct versioning, some build flags need to be passed. A Makefile is provided that sets these build flags to automatically derived values. @@ -151,13 +151,50 @@ The CCM supports the same cloud-config configuration file format used by [the cs so you can simply point it to that. ```bash -./cloudstack-ccm --cloud-provider external-cloudstack --cloud-config ~/.cloud-config --master k8s-apiserver +./cloudstack-ccm --cloud-provider external-cloudstack --cloud-config ./cloud-config --kubeconfig ~/.kube/config ``` Replace k8s-apiserver with the host name of your Kubernetes development clusters's API server. If you don't have a 'real' CloudStack installation, you can also launch a local [simulator instance](https://hub.docker.com/r/cloudstack/simulator) instead. This is very useful for dry-run testing. +### Debugging + +You can use the VSCode extension [Go](https://marketplace.visualstudio.com/items?itemName=golang.go) to debug the CCM. +Add the following configuration to the `.vscode/launch.json` file to launch the CCM and debug it. + +```json +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Launch CloudStack CCM", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "${workspaceFolder}/cmd/cloudstack-ccm", + "env": {}, + "args": [ + "--cloud-provider=external-cloudstack", + "--cloud-config=${workspaceFolder}/cloud-config", + "--kubeconfig=${env:HOME}/.kube/config", + "--leader-elect=false", + "--v=4" + ], + "showLog": true, + "trace": "verbose" + }, + { + "name": "Attach to Process", + "type": "go", + "request": "attach", + "mode": "local", + "processId": 0 + } + ] +} +``` + ## Copyright Copyright 2019 The Apache Software Foundation diff --git a/cloudstack_loadbalancer.go b/cloudstack_loadbalancer.go index 00f536f0..9d51cdde 100644 --- a/cloudstack_loadbalancer.go +++ b/cloudstack_loadbalancer.go @@ -201,11 +201,11 @@ func (cs *CSCloud) EnsureLoadBalancer(ctx context.Context, clusterName string, s for _, lbRule := range lb.rules { protocol := ProtocolFromLoadBalancer(lbRule.Protocol) if protocol == LoadBalancerProtocolInvalid { - return nil, fmt.Errorf("Error parsing protocol %v: %v", lbRule.Protocol, err) + return nil, fmt.Errorf("error parsing protocol %v: %v", lbRule.Protocol, err) } port, err := strconv.ParseInt(lbRule.Publicport, 10, 32) if err != nil { - return nil, fmt.Errorf("Error parsing port %s: %v", lbRule.Publicport, err) + return nil, fmt.Errorf("error parsing port %s: %v", lbRule.Publicport, err) } klog.V(4).Infof("Deleting firewall rules associated with load balancer rule: %v (%v:%v:%v)", lbRule.Name, protocol, lbRule.Publicip, port)