diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index b307787..0000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,30 +0,0 @@ -version: 2 -jobs: - build: - docker: - - image: circleci/golang:1.16 - parallelism: 1 - - steps: - - checkout - - - restore_cache: - keys: - - go-mod-v4-{{ checksum "go.sum" }} - - - run: - name: build - command: GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -v -o release/ipvsctl ipvsctl.go - - - save_cache: - key: go-mod-v4-{{ checksum "go.sum" }} - paths: - - "/go/pkg/mod" - - -workflows: - version: 2 - build-workflow: - jobs: - - build - diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..21832aa --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,74 @@ +name: CI + +on: + push: + branches: + - master + - dev + pull_request: + branches: + - master + +jobs: + build: + runs-on: ubuntu-latest + env: + BINARY_NAME: ipvsctl + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version: 1.24 + + - name: Cache Go modules + uses: actions/cache@v3 + with: + path: | + ~/.cache/go-build + ~/go/pkg/mod + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-go- + + - name: Install dependencies + run: | + go install honnef.co/go/tools/cmd/staticcheck@2023.1.6 + go install golang.org/x/lint/golint@v0.0.0-20201208152925-83fdc39ff7b5 + go mod download + echo "$HOME/go/bin" >> $GITHUB_PATH + + - name: Run staticcheck + run: | + echo "::group::Running staticcheck" + staticcheck ./... || true + echo "::endgroup::" + + - name: Run golint + run: | + echo "::group::Running lint" + golint ./... + echo "::endgroup::" + + - name: Run go vet + run: | + echo "::group::Running go vet" + go vet ./... + echo "::endgroup::" + + - name: Build + run: GOOS=linux go build -o dist/${BINARY_NAME} ipvsctl.go + + - name: Run tests + run: | + SKIP_IPVSKERNELREQ=1 go test -v -coverprofile=coverage.out ./... + go tool cover -html=coverage.out -o coverage.html + + - name: Upload HTML coverage report + uses: actions/upload-artifact@v4 + with: + name: coverage-report + path: coverage.html diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..f063ab7 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,35 @@ +name: Release + +on: + push: + tags: + - 'v*.*.*' + +permissions: + contents: write + +jobs: + release: + runs-on: ubuntu-latest + if: startsWith(github.ref, 'refs/tags/v') && github.ref_type == 'tag' && github.base_ref == 'main' + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: 1.24 + + - name: Install dependencies + run: go mode download + + - name: Run GoReleaser + uses: goreleaser/goreleaser-action@v6 + with: + distribution: goreleaser + version: "lastest" + args: release --snapshot --clean + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + diff --git a/.goreleaser.yml b/.goreleaser.yml index 33dbf49..488afe4 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -1,18 +1,27 @@ +project_name: ipvsctl +version: 2 before: hooks: - go mod download builds: - - goos: + - id: ipvsctl + binary: ipvsctl + ldflags: + - -s -w -X main.version=v{{.Version}} -X main.commit={{.Commit}} -X main.date={{.CommitDate}} + env: + - CGO_ENABLED=0 + goos: - linux -archives: - - replacements: - linux: Linux - 386: i386 - amd64: x86_64 + goarch: + - amd64 + - arm64 + mod_timestamp: '{{ .CommitTimestamp }}' + checksum: - name_template: 'checksums.txt' + name_template: "{{ .ProjectName }}-{{ .Version }}_checksums.txt" + algorithm: sha512 snapshot: - name_template: "{{ .Tag }}-next" + version_template: "{{ .Tag }}-next" changelog: sort: asc filters: diff --git a/Makefile b/Makefile index a360cfc..2be2003 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ all: clean lint build .PHONY: build build: - GOOS=linux GOARCH=amd64 go build -o dist/${BINARY_NAME} ipvsctl.go + GOOS=linux GOARCH=arm64 go build -o dist/${BINARY_NAME} ipvsctl.go lint: @for file in ${SOURCES} ; do \ diff --git a/README.md b/README.md index a7aebad..f5bb5ca 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ It is meant as an add-on to ipvsadm, where changes can be applied from models instead of ad-hoc commands. -[![CircleCI](https://circleci.com/gh/aschmidt75/ipvsctl/tree/master.svg?style=svg)](https://circleci.com/gh/aschmidt75/ipvsctl/tree/master) +[![CI](https://github.com/aschmidt75/ipvsctl/actions/workflows/ci.yml/badge.svg)](https://github.com/aschmidt75/ipvsctl/actions/workflows/ci.yml) [![Go Report Card](https://goreportcard.com/badge/github.com/aschmidt75/ipvsctl)](https://goreportcard.com/report/github.com/aschmidt75/ipvsctl) ## Features @@ -155,5 +155,5 @@ $ bats . ## License -(C) 2019 @aschmidt75, Apache 2.0 license +(C) 2019,2025 @aschmidt75, Apache 2.0 license except package ipvs, integrated from https://github.com/docker/libnetwork (C) 2015 Docker, Inc. Apache 2.0 license diff --git a/cmd/apply.go b/cmd/apply.go index 3d834f4..c13d1e2 100644 --- a/cmd/apply.go +++ b/cmd/apply.go @@ -28,9 +28,9 @@ func parseAllowedActions(actionSpec *string) (integration.ApplyActions, error) { res := make(integration.ApplyActions, len(actions)) for _, action := range actions { _, ex := all[integration.ApplyActionType(action)] - if ex == false { + if !ex { // no such action - return integration.ApplyActions{}, fmt.Errorf("Invalid action: %s", action) + return integration.ApplyActions{}, fmt.Errorf("invalid action: %s", action) } res[integration.ApplyActionType(action)] = true } diff --git a/cmd/common.go b/cmd/common.go index 5617db1..76cbe9c 100644 --- a/cmd/common.go +++ b/cmd/common.go @@ -97,7 +97,7 @@ func resolveParams(ipvsconfig *integration.IPVSConfig) (*integration.IPVSConfig, cfg := config.Config() intfAddrMap := make(map[string]string) - if cfg.ParamsHostNetwork == true { + if cfg.ParamsHostNetwork { intfs, err := net.Interfaces() if err != nil { fmt.Fprintf(os.Stderr, "Specified dynamic parameter from local network interfaces, but unable to query them: %s\n", err) @@ -126,7 +126,7 @@ func resolveParams(ipvsconfig *integration.IPVSConfig) (*integration.IPVSConfig, } envMap := make(map[string]string) - if cfg.ParamsHostEnv == true { + if cfg.ParamsHostEnv { for _, e := range os.Environ() { a := strings.Split(e, "=") if len(a) == 2 { diff --git a/doc/libraryexample1/main.go b/doc/libraryexample1/main.go index ad647f8..a6511a8 100644 --- a/doc/libraryexample1/main.go +++ b/doc/libraryexample1/main.go @@ -1,6 +1,5 @@ // libraryexample1 applies a model as a whole on top of an existing // ipvs tables model. -// package main import ( diff --git a/go.mod b/go.mod index 78a0142..8de19ac 100644 --- a/go.mod +++ b/go.mod @@ -1,14 +1,40 @@ module github.com/aschmidt75/ipvsctl -go 1.16 +go 1.24 + +toolchain go1.24.3 require ( github.com/aschmidt75/go-dynamic-params v0.0.1 - github.com/caarlos0/env/v6 v6.0.0 - github.com/jawher/mow.cli v1.1.0 + github.com/caarlos0/env/v6 v6.10.1 + github.com/jawher/mow.cli v1.2.0 github.com/stretchr/testify v1.7.0 - github.com/vishvananda/netlink v1.0.0 - github.com/vishvananda/netns v0.0.0-20190625233234-7109fa855b0f - golang.org/x/sys v0.1.0 - gopkg.in/yaml.v2 v2.2.4 + github.com/vishvananda/netlink v1.3.1 + github.com/vishvananda/netns v0.0.5 + golang.org/x/sys v0.33.0 + gopkg.in/yaml.v2 v2.2.8 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 // indirect + github.com/jtolds/gls v4.20.0+incompatible // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d // indirect + github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 // indirect + github.com/stretchr/objx v0.2.0 // indirect + github.com/yuin/goldmark v1.4.13 // indirect + golang.org/x/crypto v0.37.0 // indirect + golang.org/x/lint v0.0.0-20241112194109-818c5a804067 // indirect + golang.org/x/mod v0.24.0 // indirect + golang.org/x/net v0.39.0 // indirect + golang.org/x/sync v0.13.0 // indirect + golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457 // indirect + golang.org/x/term v0.31.0 // indirect + golang.org/x/text v0.24.0 // indirect + golang.org/x/tools v0.32.0 // indirect + golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 // indirect + gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 // indirect + gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect ) diff --git a/go.sum b/go.sum index 36963d0..29005be 100644 --- a/go.sum +++ b/go.sum @@ -1,14 +1,21 @@ github.com/aschmidt75/go-dynamic-params v0.0.1 h1:+ty7MB0v+HH8yWHv7ECmh3A6KrH4e6uqvQCPEUmz67g= github.com/aschmidt75/go-dynamic-params v0.0.1/go.mod h1:T4JuTxSbzqbkPwtzbMYS4JVrtm85Yn5bCKvFsSCPO1c= +github.com/aschmidt75/go-dynamic-params v0.1.0 h1:7NnVApr4vyZJE38+fDG6fEOoBrgLfVlZc7qOKE5EXzM= +github.com/aschmidt75/go-dynamic-params v0.1.0/go.mod h1:vRyDB+9vb2CO36RbRiK5pkchW7fJe4H/ffZSEtA4Mqg= github.com/caarlos0/env/v6 v6.0.0 h1:NZt6FAoB8ieKO5lEwRdwCzYxWFx7ZYF2R7UcoyaWtyc= github.com/caarlos0/env/v6 v6.0.0/go.mod h1:+wdyOmtjoZIW2GJOc2OYa5NoOFuWD/bIpWqm30NgtRk= +github.com/caarlos0/env/v6 v6.10.1 h1:t1mPSxNpei6M5yAeu1qtRdPAK29Nbcf/n3G7x+b3/II= +github.com/caarlos0/env/v6 v6.10.1/go.mod h1:hvp/ryKXKipEkcuYjs9mI4bBCg+UI0Yhgm5Zu0ddvwc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/jawher/mow.cli v1.1.0 h1:NdtHXRc0CwZQ507wMvQ/IS+Q3W3x2fycn973/b8Zuk8= github.com/jawher/mow.cli v1.1.0/go.mod h1:aNaQlc7ozF3vw6IJ2dHjp2ZFiA4ozMIYY6PyuRJwlUg= +github.com/jawher/mow.cli v1.2.0 h1:e6ViPPy+82A/NFF/cfbq3Lr6q4JHKT9tyHwTCcUQgQw= +github.com/jawher/mow.cli v1.2.0/go.mod h1:y+pcA3jBAdo/GIZx/0rFjw/K2bVEODP9rfZOfaiq8Ko= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -20,22 +27,63 @@ github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:s github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/vishvananda/netlink v1.0.0 h1:bqNY2lgheFIu1meHUFSH3d7vG93AFyqg3oGbJCOJgSM= github.com/vishvananda/netlink v1.0.0/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= +github.com/vishvananda/netlink v1.3.1 h1:3AEMt62VKqz90r0tmNhog0r/PpWKmrEShJU0wJW6bV0= +github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4= github.com/vishvananda/netns v0.0.0-20190625233234-7109fa855b0f h1:nBX3nTcmxEtHSERBJaIo1Qa26VwRaopnZmfDQUXsF4I= github.com/vishvananda/netns v0.0.0-20190625233234-7109fa855b0f/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= +github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY= +github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= +golang.org/x/lint v0.0.0-20241112194109-818c5a804067 h1:adDmSQyFTCiv19j015EGKJBoaa7ElV0Q1Wovb/4G7NA= +golang.org/x/lint v0.0.0-20241112194109-818c5a804067/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= +golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0= +golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU= +golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v2 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v2 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v2 v3.0.0-20220521103104-8f96da9f5d5e h1:3i3ny04XV6HbZ2N1oIBw1UBYATHAOpo4tfTF83JM3Z0= +gopkg.in/yaml.v2 v3.0.0-20220521103104-8f96da9f5d5e/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/integration/apply.go b/integration/apply.go index 8148d60..81c706e 100644 --- a/integration/apply.go +++ b/integration/apply.go @@ -19,6 +19,11 @@ func (e *IPVSApplyError) Error() string { return fmt.Sprintf("Unable to apply new config: %s\nReason: %s", e.what, e.origErr) } +func isActionAllowed(actions ApplyActions, action ApplyActionType) bool { + allowed, found := actions[action] + return found && allowed +} + // Apply compares new config to current config, builds a changeset and // applies the change set items within. func (ipvsconfig *IPVSConfig) Apply(newconfig *IPVSConfig, opts ApplyOpts) error { @@ -34,7 +39,7 @@ func (ipvsconfig *IPVSConfig) Apply(newconfig *IPVSConfig, opts ApplyOpts) error return ipvsconfig.ApplyChangeSet(newconfig, cs, opts) } -// ApplyChangeSet takes a chhange set and applies all change items to +// ApplyChangeSet takes a change set and applies all change items to // the given IPVSConfig func (ipvsconfig *IPVSConfig) ApplyChangeSet(newconfig *IPVSConfig, cs *ChangeSet, opts ApplyOpts) error { @@ -48,44 +53,40 @@ func (ipvsconfig *IPVSConfig) ApplyChangeSet(newconfig *IPVSConfig, cs *ChangeSe // check before hand wether all change set items are covered within allowedActions for _, csiIntf := range cs.Items { - csi := csiIntf.(ChangeSetItem) + csi, ok := csiIntf.(ChangeSetItem) + if !ok { + return fmt.Errorf("invalid item in change set: %v", csiIntf) + } switch csi.Type { case DeleteService: - allowed, found := allowedActions[ApplyActionDeleteService] - if !found || !allowed { + if !isActionAllowed(allowedActions, ApplyActionDeleteService) { return &IPVSApplyError{what: "not allowed to delete a service"} } case AddService: - allowed, found := allowedActions[ApplyActionAddService] - if !found || !allowed { + if !isActionAllowed(allowedActions, ApplyActionAddService) { return &IPVSApplyError{what: "not allowed to add a service"} } // if service has destinations, check as well if allowed if len(csi.Service.Destinations) > 0 { - allowed, found = allowedActions[ApplyActionAddDestination] - if !found || !allowed { + if !isActionAllowed(allowedActions, ApplyActionAddDestination) { return &IPVSApplyError{what: "not allowed to add a destinations"} } } case UpdateService: - allowed, found := allowedActions[ApplyActionUpdateService] - if !found || !allowed { + if !isActionAllowed(allowedActions, ApplyActionUpdateService) { return &IPVSApplyError{what: "not allowed to update a service"} } case AddDestination: - allowed, found := allowedActions[ApplyActionAddDestination] - if !found || !allowed { + if !isActionAllowed(allowedActions, ApplyActionAddDestination) { return &IPVSApplyError{what: "not allowed to add a destination"} } case DeleteDestination: - allowed, found := allowedActions[ApplyActionDeleteDestination] - if !found || !allowed { + if !isActionAllowed(allowedActions, ApplyActionDeleteDestination) { return &IPVSApplyError{what: "not allowed to delete a destination"} } case UpdateDestination: - allowed, found := allowedActions[ApplyActionUpdateDestination] - if !found || !allowed { + if !isActionAllowed(allowedActions, ApplyActionUpdateDestination) { return &IPVSApplyError{what: "not allowed to update a destination"} } default: @@ -94,7 +95,11 @@ func (ipvsconfig *IPVSConfig) ApplyChangeSet(newconfig *IPVSConfig, cs *ChangeSe } for _, csiIntf := range cs.Items { - csi := csiIntf.(ChangeSetItem) + csi, ok := csiIntf.(ChangeSetItem) + if !ok { + return fmt.Errorf("invalid item in change set: %v", csiIntf) + } + ipvsconfig.log.Printf("Applying change set item %#v\n", csi) switch csi.Type { diff --git a/integration/apply_test.go b/integration/apply_test.go index 29016e1..a9efa0e 100644 --- a/integration/apply_test.go +++ b/integration/apply_test.go @@ -48,6 +48,9 @@ func TestMain(m *testing.M) { } func TestApplyGetOnEmptyModel(t *testing.T) { + if os.Getenv("SKIP_IPVSKERNELREQ") == "1" { + t.Skip("Skipping tests that require kernel support") + } const targetModel string = "{}" var newConfig integration.IPVSConfig @@ -60,17 +63,17 @@ func TestApplyGetOnEmptyModel(t *testing.T) { currentConfig := integration.NewIPVSConfigWithLogger(TestLogger) if err := currentConfig.Get(); err != nil { - t.Errorf("Unable to get current ipvs table: %w\n", err) + t.Errorf("Unable to get current ipvs table: %v\n", err) t.FailNow() } if err := currentConfig.Apply(&newConfig, opts); err != nil { - t.Errorf("Unable to apply test model: %w\n", err) + t.Errorf("Unable to apply test model: %v\n", err) t.FailNow() } updatedConfig := integration.NewIPVSConfigWithLogger(TestLogger) if err := updatedConfig.Get(); err != nil { - t.Errorf("Unable to get current ipvs table: %w\n", err) + t.Errorf("Unable to get current ipvs table: %v\n", err) t.FailNow() } @@ -86,6 +89,9 @@ func TestApplyGetOnEmptyModel(t *testing.T) { } func TestApplyGetOnServices(t *testing.T) { + if os.Getenv("SKIP_IPVSKERNELREQ") == "1" { + t.Skip("Skipping tests that require kernel support") + } clearIPVS() @@ -108,17 +114,17 @@ func TestApplyGetOnServices(t *testing.T) { currentConfig := integration.NewIPVSConfigWithLogger(TestLogger) if err := currentConfig.Get(); err != nil { - t.Errorf("Unable to get current ipvs table: %w\n", err) + t.Errorf("Unable to get current ipvs table: %v\n", err) t.FailNow() } if err := currentConfig.Apply(&newConfig, opts); err != nil { - t.Errorf("Unable to apply test model: %w\n", err) + t.Errorf("Unable to apply test model: %v\n", err) t.FailNow() } updatedConfig := integration.NewIPVSConfigWithLogger(TestLogger) if err := updatedConfig.Get(); err != nil { - t.Errorf("Unable to get current ipvs table: %w\n", err) + t.Errorf("Unable to get current ipvs table: %v\n", err) t.FailNow() } @@ -137,13 +143,13 @@ func TestApplyGetOnServices(t *testing.T) { panic(err) } if err := updatedConfig.Apply(&newConfig, opts); err != nil { - t.Errorf("Unable to apply test model: %w\n", err) + t.Errorf("Unable to apply test model: %v\n", err) t.FailNow() } updatedConfig2 := integration.NewIPVSConfigWithLogger(TestLogger) if err := updatedConfig2.Get(); err != nil { - t.Errorf("Unable to get current ipvs table: %w\n", err) + t.Errorf("Unable to get current ipvs table: %v\n", err) t.FailNow() } diff --git a/integration/changeset.go b/integration/changeset.go index c5c834a..ac1917b 100644 --- a/integration/changeset.go +++ b/integration/changeset.go @@ -20,12 +20,12 @@ func (ipvsconfig *IPVSConfig) ChangeSet(newconfig *IPVSConfig, opts ApplyOpts) ( if err != nil { return res, err } - if equal == true { + if equal { found = true } } - if found == false { + if !found { var adr string if service.service != nil { adr = MakeAdressStringFromIpvsService(service.service) @@ -53,12 +53,12 @@ func (ipvsconfig *IPVSConfig) ChangeSet(newconfig *IPVSConfig, opts ApplyOpts) ( if err != nil { return res, err } - if equal == true { + if equal { found = true } } - if found == false { + if !found { res.AddChange(ChangeSetItem{ Type: AddService, Description: fmt.Sprintf("Adding new service %s because it does not yet exist", newService.Address), @@ -77,7 +77,7 @@ func (ipvsconfig *IPVSConfig) ChangeSet(newconfig *IPVSConfig, opts ApplyOpts) ( if err != nil { return res, err } - if equal == true { + if equal { newSched := newService.SchedName if newSched == "" { // default given? @@ -111,12 +111,12 @@ func (ipvsconfig *IPVSConfig) ChangeSet(newconfig *IPVSConfig, opts ApplyOpts) ( if err != nil { return res, err } - if equal == true { + if equal { found = true } } - if found == false { + if !found { var adrService, adrDestination string if service.service != nil { adrService = MakeAdressStringFromIpvsService(service.service) @@ -147,11 +147,11 @@ func (ipvsconfig *IPVSConfig) ChangeSet(newconfig *IPVSConfig, opts ApplyOpts) ( if err != nil { return res, err } - if equal == true { + if equal { found = true } } - if found == false { + if !found { var adrService string if service.service != nil { adrService = MakeAdressStringFromIpvsService(service.service) @@ -178,7 +178,7 @@ func (ipvsconfig *IPVSConfig) ChangeSet(newconfig *IPVSConfig, opts ApplyOpts) ( if err != nil { return res, err } - if equal == false { + if !equal { var adrService string if service.service != nil { adrService = MakeAdressStringFromIpvsService(service.service) diff --git a/integration/model.go b/integration/model.go index 5c5c997..9c06fcb 100644 --- a/integration/model.go +++ b/integration/model.go @@ -235,6 +235,10 @@ func (c *IPVSConfig) NewIpvsServiceStruct(s *Service) (*ipvs.Service, error) { port = *c.Defaults.Port } } + // Ensure the parsed port is within the valid range for uint16 + if port < 0 || port > 65535 { + return nil, errors.New("port out of range") + } var protoAsNum uint16 switch proto { @@ -295,6 +299,10 @@ func (c *IPVSConfig) NewIpvsDestinationStruct(destination *Destination) (*ipvs.D p = *c.Defaults.Port } } + // Ensure the parsed port is within the valid range for uint16 + if p < 0 || p > 65535 { + return nil, errors.New("port out of range") + } df := destination.Forward if df == "" { @@ -339,7 +347,7 @@ func CompareServicesEquality(ca *IPVSConfig, a *Service, cb *IPVSConfig, b *Serv if err != nil { return false, err } - if ident == false { + if !ident { return false, nil } @@ -444,7 +452,7 @@ func CompareDestinationsEquality(ca *IPVSConfig, a *Destination, cb *IPVSConfig, return false, nil } - if opts.KeepWeights == false { + if !opts.KeepWeights { // compare weight aw := a.Weight bw := b.Weight diff --git a/integration/validate_test.go b/integration/validate_test.go index cd550c9..3818bb3 100644 --- a/integration/validate_test.go +++ b/integration/validate_test.go @@ -102,11 +102,11 @@ services: t.Run(test.model, func(t *testing.T) { err := validate(t, test.model) if err == nil { - if test.ok == false { + if !test.ok { t.Error("Should have returned a validation error, but did not") } } else { - if test.ok == true { + if test.ok { t.Error("Should have passed but returned a validation error: %w", err) } @@ -166,11 +166,11 @@ defaults: t.Run(test.model, func(t *testing.T) { err := validate(t, test.model) if err == nil { - if test.ok == false { + if !test.ok { t.Error("Should have returned a validation error, but did not") } } else { - if test.ok == true { + if test.ok { t.Error("Should have passed but returned a validation error: %w", err) } diff --git a/ipvs/ipvs.go b/ipvs/ipvs.go index f64a5ea..8778a5a 100644 --- a/ipvs/ipvs.go +++ b/ipvs/ipvs.go @@ -2,6 +2,7 @@ // Code and documentation copyright 2015 Docker, inc. // Code released under the Apache 2.0 license. +//go:build linux // +build linux package ipvs @@ -193,7 +194,7 @@ func (i *Handle) GetService(s *Service) (*Service, error) { // We are looking for exactly one service otherwise error out if len(res) != 1 { - return nil, fmt.Errorf("Expected only one service obtained=%d", len(res)) + return nil, fmt.Errorf("expected only one service obtained=%d", len(res)) } return res[0], nil diff --git a/ipvs/netlink.go b/ipvs/netlink.go index 9305281..0e15a9c 100644 --- a/ipvs/netlink.go +++ b/ipvs/netlink.go @@ -1,6 +1,7 @@ // from libnetwork: https://github.com/docker/libnetwork // Code and documentation copyright 2015 Docker, inc. // Code released under the Apache 2.0 license. +//go:build linux // +build linux package ipvs @@ -10,6 +11,7 @@ import ( "encoding/binary" "fmt" "net" + "os" "os/exec" "strings" "sync" @@ -64,7 +66,10 @@ func setup() { ipvsOnce.Do(func() { var err error if out, err := exec.Command("modprobe", "-va", "ip_vs").CombinedOutput(); err != nil { - fmt.Sprintf("Running modprobe ip_vs failed with message: `%s`, error: %v", strings.TrimSpace(string(out)), err) + if os.Getenv("SKIP_IPVSKERNELREQ") != "1" { + fmt.Fprintf(os.Stderr, "Running modprobe ip_vs failed with message: `%s`, error: %v", strings.TrimSpace(string(out)), err) + os.Exit(1) + } } ipvsFamily, err = getIPVSFamily() @@ -225,10 +230,10 @@ func execute(s *nl.NetlinkSocket, req *nl.NetlinkRequest, resType uint16) ([][]b done: for { - msgs, err := s.Receive() + msgs, _, err := s.Receive() if err != nil { if s.GetFd() == -1 { - return nil, fmt.Errorf("Socket got closed on receive") + return nil, fmt.Errorf("socket got closed on receive") } if err == syscall.EAGAIN { // timeout fired @@ -241,7 +246,7 @@ done: continue } if m.Header.Pid != pid { - return nil, fmt.Errorf("Wrong pid %d, expected %d", m.Header.Pid, pid) + return nil, fmt.Errorf("wrong pid %d, expected %d", m.Header.Pid, pid) } if m.Header.Type == syscall.NLMSG_DONE { break done