diff --git a/.github/workflows/push-pr-lint.yaml b/.github/workflows/push-pr-lint.yaml index 8af8781..b3ff6ee 100644 --- a/.github/workflows/push-pr-lint.yaml +++ b/.github/workflows/push-pr-lint.yaml @@ -5,17 +5,17 @@ jobs: lint-test: runs-on: ubuntu-latest steps: - - name: Install Go - uses: actions/setup-go@v4 - with: - go-version: "1.22" - name: Checkout code uses: actions/checkout@v4 + - name: Install Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod - name: golangci-lint - uses: golangci/golangci-lint-action@v3 + uses: golangci/golangci-lint-action@v6 with: args: --config .golangci.yml --timeout 2m - version: v1.57.1 + version: v1.61.0 skip-cache: true - name: Test run: go test ./... @@ -23,14 +23,14 @@ jobs: runs-on: ubuntu-latest needs: [lint-test] steps: - - name: Install Go - uses: actions/setup-go@v4 - with: - go-version: "1.22" - - name: Checkout code uses: actions/checkout@v4 + - name: Install Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + - name: build binary run: make build-linux @@ -39,7 +39,7 @@ jobs: - name: Build flipflop Docker image - no push id: dockerbuild-flipflop - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . push: false diff --git a/.golangci.yml b/.golangci.yml index 823d772..2d18c58 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,14 +1,12 @@ # golangci.com configuration # https://github.com/golangci/golangci/wiki/Configuration service: - golangci-lint-version: 1.50.0 # use the fixed version to not introduce new linters unexpectedly + golangci-lint-version: 1.61.0 # use the fixed version to not introduce new linters unexpectedly linters-settings: govet: - enable: - - fieldalignment + enable-all: true auto-fix: true - check-shadowing: true settings: printf: funcs: @@ -65,7 +63,7 @@ linters: # additional linters - bodyclose - gocritic - - goerr113 + - err113 - goimports - revive - misspell @@ -76,14 +74,12 @@ linters: enable-all: false disable-all: true -run: -# build-tags: - skip-dirs: +issues: + exclude-dirs: - internal/fixtures - skip-files: + - internal/model/mock + exclude-files: - "(.*/)*.*_test.go" - -issues: exclude-rules: - linters: - gosec diff --git a/.mockery.yaml b/.mockery.yaml new file mode 100644 index 0000000..7aeedcc --- /dev/null +++ b/.mockery.yaml @@ -0,0 +1,10 @@ +with-expecter: true +packages: + github.com/metal-toolbox/flipflop/internal/model: + config: + dir: "internal/model/mock" + fileName: "mock_{{.InterfaceName}}.go" + interfaces: + BMCBootMonitor: + UpdateFn: + DelayFn: diff --git a/go.mod b/go.mod index bebcdc3..1e4cf14 100644 --- a/go.mod +++ b/go.mod @@ -1,44 +1,46 @@ module github.com/metal-toolbox/flipflop -go 1.22 +go 1.23 -toolchain go1.22.1 +toolchain go1.23.1 require ( github.com/banzaicloud/logrus-runtime-formatter v0.0.0-20190729070250-5ae5475bae5e - github.com/bmc-toolbox/bmclib/v2 v2.2.2 + github.com/bmc-toolbox/bmclib/v2 v2.3.2 + github.com/bmc-toolbox/common v0.0.0-20240926143744-8c478be881d7 github.com/bombsimon/logrusr/v2 v2.0.1 github.com/coreos/go-oidc v2.2.1+incompatible github.com/equinix-labs/otel-init-go v0.0.9 + github.com/go-logr/logr v1.4.2 github.com/google/uuid v1.6.0 github.com/hashicorp/go-retryablehttp v0.7.7 github.com/jeremywohl/flatten v1.0.1 github.com/metal-toolbox/ctrl v0.2.9 - github.com/metal-toolbox/fleetdb v1.19.5 - github.com/metal-toolbox/rivets v1.3.7 + github.com/metal-toolbox/fleetdb v1.19.7 + github.com/metal-toolbox/rivets v1.3.10 github.com/mitchellh/copystructure v1.2.0 github.com/mitchellh/mapstructure v1.5.0 github.com/pkg/errors v0.9.1 - github.com/prometheus/client_golang v1.20.1 + github.com/prometheus/client_golang v1.20.4 github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.8.1 github.com/spf13/viper v1.19.0 + github.com/stmcginnis/gofish v0.19.0 github.com/stretchr/testify v1.9.0 - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 - go.opentelemetry.io/otel v1.28.0 - go.opentelemetry.io/otel/trace v1.28.0 - golang.org/x/net v0.28.0 - golang.org/x/oauth2 v0.22.0 + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.55.0 + go.opentelemetry.io/otel v1.30.0 + go.opentelemetry.io/otel/trace v1.30.0 + golang.org/x/net v0.30.0 + golang.org/x/oauth2 v0.23.0 ) require ( - dario.cat/mergo v1.0.0 // indirect + dario.cat/mergo v1.0.1 // indirect github.com/Jeffail/gabs/v2 v2.7.0 // indirect github.com/VictorLowther/simplexml v0.0.0-20180716164440-0bff93621230 // indirect github.com/VictorLowther/soap v0.0.0-20150314151524-8e36fca84b22 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/bmc-toolbox/common v0.0.0-20240723142833-87832458b53b // indirect - github.com/bytedance/sonic v1.12.1 // indirect + github.com/bytedance/sonic v1.12.3 // indirect github.com/bytedance/sonic/loader v0.2.0 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect @@ -54,11 +56,10 @@ require ( github.com/ghodss/yaml v1.0.0 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/gin-gonic/gin v1.10.0 // indirect - github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.22.0 // indirect + github.com/go-playground/validator/v10 v10.22.1 // indirect github.com/goccy/go-json v0.10.3 // indirect github.com/gofrs/uuid v4.4.0+incompatible // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect @@ -66,7 +67,7 @@ require ( github.com/gorilla/websocket v1.5.3 // indirect github.com/gosimple/slug v1.14.0 // indirect github.com/gosimple/unidecode v1.0.1 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.21.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect @@ -85,35 +86,34 @@ require ( github.com/jacobweinstock/registrar v0.4.7 // indirect github.com/jmoiron/sqlx v1.4.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.17.9 // indirect + github.com/klauspost/compress v1.17.10 // indirect github.com/klauspost/cpuid/v2 v2.2.8 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/lib/pq v1.10.9 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/metal-toolbox/conditionorc v1.1.8 // indirect + github.com/metal-toolbox/conditionorc v1.1.11 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/nats-io/nats.go v1.36.0 // indirect + github.com/nats-io/nats.go v1.37.0 // indirect github.com/nats-io/nkeys v0.4.7 // indirect github.com/nats-io/nuid v1.0.1 // indirect - github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/pquerna/cachecontrol v0.2.0 // indirect github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.55.0 // indirect + github.com/prometheus/common v0.60.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect github.com/sagikazarmark/locafero v0.6.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/satori/go.uuid v1.2.0 // indirect - github.com/slack-go/slack v0.13.1 // indirect + github.com/slack-go/slack v0.14.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.11.0 // indirect - github.com/spf13/cast v1.6.0 // indirect + github.com/spf13/cast v1.7.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/stmcginnis/gofish v0.15.1-0.20231121142100-22a60a77be91 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect @@ -126,25 +126,26 @@ require ( github.com/volatiletech/sqlboiler/v4 v4.16.2 // indirect github.com/volatiletech/strmangle v0.0.6 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0 // indirect - go.opentelemetry.io/otel/metric v1.28.0 // indirect - go.opentelemetry.io/otel/sdk v1.28.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.30.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.30.0 // indirect + go.opentelemetry.io/otel/metric v1.30.0 // indirect + go.opentelemetry.io/otel/sdk v1.30.0 // indirect go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect - gocloud.dev v0.38.0 // indirect - golang.org/x/arch v0.9.0 // indirect - golang.org/x/crypto v0.26.0 // indirect - golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect - golang.org/x/sys v0.24.0 // indirect - golang.org/x/text v0.17.0 // indirect - golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9 // indirect - google.golang.org/api v0.189.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240730163845-b1a4ccb954bf // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240730163845-b1a4ccb954bf // indirect - google.golang.org/grpc v1.65.0 // indirect + gocloud.dev v0.39.0 // indirect + golang.org/x/arch v0.11.0 // indirect + golang.org/x/crypto v0.28.0 // indirect + golang.org/x/exp v0.0.0-20241004190924-225e2abe05e6 // indirect + golang.org/x/sys v0.26.0 // indirect + golang.org/x/text v0.19.0 // indirect + golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect + google.golang.org/api v0.199.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240930140551-af27646dc61f // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240930140551-af27646dc61f // indirect + google.golang.org/grpc v1.67.1 // indirect google.golang.org/protobuf v1.34.2 // indirect + gopkg.in/go-jose/go-jose.v2 v2.6.3 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index 0dae458..c8be7d3 100644 --- a/go.sum +++ b/go.sum @@ -29,11 +29,12 @@ cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= -cloud.google.com/go v0.115.0 h1:CnFSK6Xo3lDYRoBKEcAtia6VSC837/ZkJuRduSFnr14= -cloud.google.com/go/auth v0.7.2 h1:uiha352VrCDMXg+yoBtaD0tUF4Kv9vrtrWPYXwutnDE= -cloud.google.com/go/auth v0.7.2/go.mod h1:VEc4p5NNxycWQTMQEDQF0bd6aTMb6VgYDXEwiJJQAbs= -cloud.google.com/go/auth/oauth2adapt v0.2.3 h1:MlxF+Pd3OmSudg/b1yZ5lJwoXCEaeedAguodky1PcKI= -cloud.google.com/go/auth/oauth2adapt v0.2.3/go.mod h1:tMQXOfZzFuNuUxOypHlQEXgdfX5cuhwU+ffUuXRJE8I= +cloud.google.com/go v0.115.1 h1:Jo0SM9cQnSkYfp44+v+NQXHpcHqlnRJk2qxh6yvxxxQ= +cloud.google.com/go v0.115.1/go.mod h1:DuujITeaufu3gL68/lOFIirVNJwQeyf5UXyi+Wbgknc= +cloud.google.com/go/auth v0.9.5 h1:4CTn43Eynw40aFVr3GpPqsQponx2jv0BQpjvajsbbzw= +cloud.google.com/go/auth v0.9.5/go.mod h1:Xo0n7n66eHyOWWCnitop6870Ilwo3PiZyodVkkH1xWM= +cloud.google.com/go/auth/oauth2adapt v0.2.4 h1:0GWE/FUsXhf6C+jAkWgYm7X9tK8cuEIfy19DBn6B6bY= +cloud.google.com/go/auth/oauth2adapt v0.2.4/go.mod h1:jC/jOpwFP6JBxhB3P5Rr0a9HLMC/Pe3eaL4NmdvqPtc= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= @@ -46,15 +47,17 @@ cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6m cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= cloud.google.com/go/compute v1.24.0 h1:phWcR2eWzRJaL/kOiJwfFsPs4BaKq1j6vnpZrc1YlVg= -cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY= -cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY= +cloud.google.com/go/compute/metadata v0.5.2 h1:UxK4uu/Tn+I3p2dYWTfiX4wva7aYlKixAHn3fyqngqo= +cloud.google.com/go/compute/metadata v0.5.2/go.mod h1:C66sj2AluDcIqakBq/M8lw8/ybHgOZqin2obFxa/E5k= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY= -cloud.google.com/go/iam v1.1.7 h1:z4VHOhwKLF/+UYXAJDFwGtNF0b6gjsW1Pk9Ml0U/IoM= -cloud.google.com/go/iam v1.1.7/go.mod h1:J4PMPg8TtyurAUvSmPj8FF3EDgY1SPRZxcUGrn7WXGA= -cloud.google.com/go/kms v1.15.8 h1:szIeDCowID8th2i8XE4uRev5PMxQFqW+JjwYxL9h6xs= -cloud.google.com/go/kms v1.15.8/go.mod h1:WoUHcDjD9pluCg7pNds131awnH429QGvRM3N/4MyoVs= +cloud.google.com/go/iam v1.1.13 h1:7zWBXG9ERbMLrzQBRhFliAV+kjcRToDTgQT3CTwYyv4= +cloud.google.com/go/iam v1.1.13/go.mod h1:K8mY0uSXwEXS30KrnVb+j54LB/ntfZu1dr+4zFMNbus= +cloud.google.com/go/kms v1.18.5 h1:75LSlVs60hyHK3ubs2OHd4sE63OAMcM2BdSJc2bkuM4= +cloud.google.com/go/kms v1.18.5/go.mod h1:yXunGUGzabH8rjUPImp2ndHiGolHeWJJ0LODLedicIY= +cloud.google.com/go/longrunning v0.5.12 h1:5LqSIdERr71CqfUsFlJdBpOkBH8FBCFD7P1nTWy3TYE= +cloud.google.com/go/longrunning v0.5.12/go.mod h1:S5hMV8CDJ6r50t2ubVJSKQVv5u0rmik5//KgLO3k4lU= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -65,8 +68,8 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= -dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= -dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= +dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= @@ -108,14 +111,14 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bmc-toolbox/bmclib/v2 v2.2.2 h1:JGkYQPplHSZsfJOjkRBCGUHGU8YdiFMRfJzwzOSUMhU= -github.com/bmc-toolbox/bmclib/v2 v2.2.2/go.mod h1:gFF4iD468hbW1JUdJJx3mbhNGzoLsG47epbMa++grp8= -github.com/bmc-toolbox/common v0.0.0-20240723142833-87832458b53b h1:0LHjikaGWlqEMczrCEZ6w1N/ZqcYlx6WRHkhabRUQEk= -github.com/bmc-toolbox/common v0.0.0-20240723142833-87832458b53b/go.mod h1:Cdnkm+edb6C0pVkyCrwh3JTXAe0iUF9diDG/DztPI9I= +github.com/bmc-toolbox/bmclib/v2 v2.3.2 h1:RwQVFM3GepHlC7yw6krXoxwKozXB37dcN8e5MKrZYwE= +github.com/bmc-toolbox/bmclib/v2 v2.3.2/go.mod h1:t8If/0fHQTRIK/yKDk2H3SgthDNNj+7z2aeftDFRFrU= +github.com/bmc-toolbox/common v0.0.0-20240926143744-8c478be881d7 h1:Xe6j3oMwe82buwBwEpok9wr+v21Io59pqMTZ5rKRVn8= +github.com/bmc-toolbox/common v0.0.0-20240926143744-8c478be881d7/go.mod h1:Cdnkm+edb6C0pVkyCrwh3JTXAe0iUF9diDG/DztPI9I= github.com/bombsimon/logrusr/v2 v2.0.1 h1:1VgxVNQMCvjirZIYaT9JYn6sAVGVEcNtRE0y4mvaOAM= github.com/bombsimon/logrusr/v2 v2.0.1/go.mod h1:ByVAX+vHdLGAfdroiMg6q0zgq2FODY2lc5YJvzmOJio= -github.com/bytedance/sonic v1.12.1 h1:jWl5Qz1fy7X1ioY74WqO0KjAMtAGQs4sYnjiEBiyX24= -github.com/bytedance/sonic v1.12.1/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk= +github.com/bytedance/sonic v1.12.3 h1:W2MGa7RCU1QTeYRTPE3+88mVC0yXmsRQRChiyVocVjU= +github.com/bytedance/sonic v1.12.3/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk= github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM= github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= @@ -231,8 +234,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao= -github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA= +github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= @@ -331,8 +334,8 @@ github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= -github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= +github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM= +github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -340,8 +343,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/wire v0.6.0 h1:HBkoIh4BdSxoyo9PveV8giw7ZsaBOvzWKfcg/6MrVwI= github.com/google/wire v0.6.0/go.mod h1:F4QhpQ9EDIdJ1Mbop/NZBRB+5yrR6qg3BnctaoUk6NA= -github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= -github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= +github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw= +github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= @@ -361,8 +364,8 @@ github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6 github.com/gosimple/unidecode v1.0.1/go.mod h1:CP0Cr1Y1kogOtx0bJblKzsVWrqYaqfNOnHzpgWw4Awc= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.21.0 h1:CWyXh/jylQWp2dtiV33mY4iSSp6yf4lmn+c7/tN+ObI= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.21.0/go.mod h1:nCLIt0w3Ept2NwF8ThLmrppXsfT07oC8k0XNDxd8sVU= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I= github.com/hashicorp/consul/api v1.12.0/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0= github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -488,8 +491,8 @@ github.com/kat-co/vala v0.0.0-20170210184112-42e1d8b61f12/go.mod h1:u9MdXq/QageO github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= -github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/compress v1.17.10 h1:oXAz+Vh0PMUvJczoi+flxpnBEPxoER1IaAnU/NMPtT0= +github.com/klauspost/compress v1.17.10/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= @@ -547,14 +550,14 @@ github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4 github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/metal-toolbox/conditionorc v1.1.8 h1:5wjIdPLdLY2RkIeGZ73tTFEnKJxBzBM5ZJMopDqFgSg= -github.com/metal-toolbox/conditionorc v1.1.8/go.mod h1:eXlXZoOJBmihR4/+xA2o1dj1sQTXkU63Ekma7Ouc+o0= +github.com/metal-toolbox/conditionorc v1.1.11 h1:ukHCQvOJh+z4C3Y18VjXizxnlM1rPIqh9pbEFaoiKwQ= +github.com/metal-toolbox/conditionorc v1.1.11/go.mod h1:COiaJSl8GOoIfpaSscTfCzAKjp2VDjCUjm5iw9KEm/E= github.com/metal-toolbox/ctrl v0.2.9 h1:Q1Hqpqyb71/gg2PcX/qrfoDE8FlydJt4rPQb7/Z8LhI= github.com/metal-toolbox/ctrl v0.2.9/go.mod h1:QVATUIWFx3dbjOoEX0EnJHtRvypRlXZ9HUGaPLRyTG8= -github.com/metal-toolbox/fleetdb v1.19.5 h1:ERgdFAUtWnT/AeVhCGclsENmwPhU88JUcgOZAdxWKYI= -github.com/metal-toolbox/fleetdb v1.19.5/go.mod h1:k9MZXQsJX4NfBoANst6g1468papSs0tzsSyzN3gGWuQ= -github.com/metal-toolbox/rivets v1.3.7 h1:ZM6AbX1xASS91FWi/2i2wh9twVOPJTzpD3c7fcllhBk= -github.com/metal-toolbox/rivets v1.3.7/go.mod h1:8irU6eXgOa3QkjdcGi/aY4vqoMqCkbwVz7iVTYYPCX8= +github.com/metal-toolbox/fleetdb v1.19.7 h1:lTl67gtt5DzTdH4MgLViyXaTaH6FM8BtmM7NX7hXDZY= +github.com/metal-toolbox/fleetdb v1.19.7/go.mod h1:IMbJwyC9DnznuPNjAaP54Y6uWuWu9cPXGH+yoe7jwGA= +github.com/metal-toolbox/rivets v1.3.10 h1:UgYQSx/QJF6Yuzf+YSRF/t9soL6QjMY8sLugf/aMmII= +github.com/metal-toolbox/rivets v1.3.10/go.mod h1:HkF9k8yw3MZqrIkxsi7w7EkTP3h2/t08WBpm+WK/Dsk= github.com/microsoft/go-mssqldb v0.17.0/go.mod h1:OkoNGhGEs8EZqchVTtochlXruEhEOaO4S0d2sB5aeGQ= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= @@ -591,8 +594,8 @@ github.com/nats-io/jwt/v2 v2.5.5 h1:ROfXb50elFq5c9+1ztaUbdlrArNFl2+fQWP6B8HGEq4= github.com/nats-io/jwt/v2 v2.5.5/go.mod h1:ZdWS1nZa6WMZfFwwgpEaqBV8EPGVgOTDHN/wTbz0Y5A= github.com/nats-io/nats-server/v2 v2.10.12 h1:G6u+RDrHkw4bkwn7I911O5jqys7jJVRY6MwgndyUsnE= github.com/nats-io/nats-server/v2 v2.10.12/go.mod h1:H1n6zXtYLFCgXcf/SF8QNTSIFuS8tyZQMN9NguUHdEs= -github.com/nats-io/nats.go v1.36.0 h1:suEUPuWzTSse/XhESwqLxXGuj8vGRuPRoG7MoRN/qyU= -github.com/nats-io/nats.go v1.36.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8= +github.com/nats-io/nats.go v1.37.0 h1:07rauXbVnnJvv1gfIyghFEo6lUcYRY0WXc3x7x0vUxE= +github.com/nats-io/nats.go v1.37.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8= github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI= github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc= github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= @@ -602,8 +605,8 @@ github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144T github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= -github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= -github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= +github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -623,8 +626,8 @@ github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5Fsn github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.20.1 h1:IMJXHOD6eARkQpxo8KkhgEVFlBNm+nkrFUyGlIu7Na8= -github.com/prometheus/client_golang v1.20.1/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_golang v1.20.4 h1:Tgh3Yr67PaOv/uTqloMsCEdeuFTatm5zIq5+qNN23vI= +github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -635,8 +638,8 @@ github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8 github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= -github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= +github.com/prometheus/common v0.60.0 h1:+V9PAREWNvJMAuJ1x1BaWl9dewMW4YrHZQbx0sJNllA= +github.com/prometheus/common v0.60.0/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= @@ -676,8 +679,8 @@ github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrf github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/slack-go/slack v0.13.1 h1:6UkM3U1OnbhPsYeb1IMkQ6HSNOSikWluwOncJt4Tz/o= -github.com/slack-go/slack v0.13.1/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw= +github.com/slack-go/slack v0.14.0 h1:6c0UTfbRnvRssZUsZ2qe0Iu07VAMPjRqOa6oX8ewF4k= +github.com/slack-go/slack v0.14.0/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= @@ -687,8 +690,8 @@ github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= -github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= -github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= +github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= @@ -698,8 +701,8 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/spf13/viper v1.12.0/go.mod h1:b6COn30jlNxbm/V2IqWiNWkJ+vZNiMNksliPCiuKtSI= github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= -github.com/stmcginnis/gofish v0.15.1-0.20231121142100-22a60a77be91 h1:WmABtU8y6kTgzoVUn3FWCQGAfyodve3uz3xno28BrRs= -github.com/stmcginnis/gofish v0.15.1-0.20231121142100-22a60a77be91/go.mod h1:BLDSFTp8pDlf/xDbLZa+F7f7eW0E/CHCboggsu8CznI= +github.com/stmcginnis/gofish v0.19.0 h1:fmxdRZ5WHfs+4ExArMYoeRfoh+SAxLELKtmoVplBkU4= +github.com/stmcginnis/gofish v0.19.0/go.mod h1:lq2jHj2t8Krg0Gx02ABk8MbK7Dz9jvWpO/TGnVksn00= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= @@ -717,7 +720,6 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.5/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t6PwAXzs= @@ -768,22 +770,22 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.53.0 h1:ktt8061VV/UU5pdPF6AcEFyuPxMizf/vU6eD1l+13LI= go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.53.0/go.mod h1:JSRiHPV7E3dbOAP0N6SRPg2nC/cugJnVXRqP018ejtY= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.51.0 h1:A3SayB3rNyt+1S6qpI9mHPkeHTZbD7XILEqWnYZb2l0= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.51.0/go.mod h1:27iA5uvhuRNmalO+iEUdVn5ZMj2qy10Mm+XRIpRmyuU= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg= -go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= -go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0 h1:R3X6ZXmNPRR8ul6i3WgFURCHzaXjHdm0karRG/+dj3s= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0/go.mod h1:QWFXnDavXWwMx2EEcZsf3yxgEKAqsxQ+Syjp+seyInw= -go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= -go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= -go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE= -go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= -go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= -go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 h1:r6I7RJCN86bpD/FQwedZ0vSixDpwuWREjW9oRMsmqDc= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0/go.mod h1:B9yO6b04uB80CzjedvewuqDhxJxi11s7/GtiGa8bAjI= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.55.0 h1:ZIg3ZT/aQ7AfKqdwp7ECpOK6vHqquXXuyTjIO8ZdmPs= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.55.0/go.mod h1:DQAwmETtZV00skUwgD6+0U89g80NKsJE3DCKeLLPQMI= +go.opentelemetry.io/otel v1.30.0 h1:F2t8sK4qf1fAmY9ua4ohFS/K+FUuOPemHUIXHtktrts= +go.opentelemetry.io/otel v1.30.0/go.mod h1:tFw4Br9b7fOS+uEao81PJjVMjW/5fvNCbpsDIXqP0pc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.30.0 h1:lsInsfvhVIfOI6qHVyysXMNDnjO9Npvl7tlDPJFBVd4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.30.0/go.mod h1:KQsVNh4OjgjTG0G6EiNi1jVpnaeeKsKMRwbLN+f1+8M= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.30.0 h1:m0yTiGDLUvVYaTFbAvCkVYIYcvwKt3G7OLoN77NUs/8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.30.0/go.mod h1:wBQbT4UekBfegL2nx0Xk1vBcnzyBPsIVm9hRG4fYcr4= +go.opentelemetry.io/otel/metric v1.30.0 h1:4xNulvn9gjzo4hjg+wzIKG7iNFEaBMX00Qd4QIZs7+w= +go.opentelemetry.io/otel/metric v1.30.0/go.mod h1:aXTfST94tswhWEb+5QjlSqG+cZlmyXy/u8jFpor3WqQ= +go.opentelemetry.io/otel/sdk v1.30.0 h1:cHdik6irO49R5IysVhdn8oaiR9m8XluDaJAs4DfOrYE= +go.opentelemetry.io/otel/sdk v1.30.0/go.mod h1:p14X4Ok8S+sygzblytT1nqG98QG2KYKv++HE0LY/mhg= +go.opentelemetry.io/otel/trace v1.30.0 h1:7UBkkYzeg3C7kQX8VAidWh2biiQbtAKjyIML8dQ9wmc= +go.opentelemetry.io/otel/trace v1.30.0/go.mod h1:5EyKqTzzmyqB9bwtCCq6pDLktPK6fmGf/Dph+8VI02o= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= @@ -807,10 +809,10 @@ go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -gocloud.dev v0.38.0 h1:SpxfaOc/Fp4PeO8ui7wRcCZV0EgXZ+IWcVSLn6ZMSw0= -gocloud.dev v0.38.0/go.mod h1:3XjKvd2E5iVNu/xFImRzjN0d/fkNHe4s0RiKidpEUMQ= -golang.org/x/arch v0.9.0 h1:ub9TgUInamJ8mrZIGlBG6/4TqWeMszd4N8lNorbrr6k= -golang.org/x/arch v0.9.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= +gocloud.dev v0.39.0 h1:EYABYGhAalPUaMrbSKOr5lejxoxvXj99nE8XFtsDgds= +gocloud.dev v0.39.0/go.mod h1:drz+VyYNBvrMTW0KZiBAYEdl8lbNZx+OQ7oQvdrFmSQ= +golang.org/x/arch v0.11.0 h1:KXV8WWKCXm6tRpLirl2szsO5j/oOODwZf4hATmGVNs4= +golang.org/x/arch v0.11.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= @@ -832,8 +834,8 @@ golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ= -golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= -golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= +golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -844,8 +846,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= +golang.org/x/exp v0.0.0-20241004190924-225e2abe05e6 h1:1wqE9dj9NpSm04INVsJhhEUzhuDVjbcyKH91sVyPATw= +golang.org/x/exp v0.0.0-20241004190924-225e2abe05e6/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -926,8 +928,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= -golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= -golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -948,8 +950,8 @@ golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= -golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= +golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1057,8 +1059,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= -golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -1077,13 +1079,13 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= -golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= -golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= +golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -1156,8 +1158,8 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9 h1:LLhsEBxRTBLuKlQxFBYUOU8xyFgXv6cOTp2HASDlsDk= -golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= +golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY= +golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -1197,8 +1199,8 @@ google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRR google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= google.golang.org/api v0.81.0/go.mod h1:FA6Mb/bZxj706H2j+j2d6mHEEaHBmbbWnkfvmorOCko= -google.golang.org/api v0.189.0 h1:equMo30LypAkdkLMBqfeIqtyAnlyig1JSZArl4XPwdI= -google.golang.org/api v0.189.0/go.mod h1:FLWGJKb0hb+pU2j+rJqwbnsF+ym+fQs73rbJ+KAUgy8= +google.golang.org/api v0.199.0 h1:aWUXClp+VFJmqE0JPvpZOK3LDQMyFKYIow4etYd9qxs= +google.golang.org/api v0.199.0/go.mod h1:ohG4qSztDJmZdjK/Ar6MhbAmb/Rpi4JHOqagsh90K28= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1284,12 +1286,12 @@ google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20240722135656-d784300faade h1:lKFsS7wpngDgSCeFn7MoLy+wBDQZ1UQIJD4UNM1Qvkg= -google.golang.org/genproto v0.0.0-20240722135656-d784300faade/go.mod h1:FfBgJBJg9GcpPvKIuHSZ/aE1g2ecGL74upMzGZjiGEY= -google.golang.org/genproto/googleapis/api v0.0.0-20240730163845-b1a4ccb954bf h1:GillM0Ef0pkZPIB+5iO6SDK+4T9pf6TpaYR6ICD5rVE= -google.golang.org/genproto/googleapis/api v0.0.0-20240730163845-b1a4ccb954bf/go.mod h1:OFMYQFHJ4TM3JRlWDZhJbZfra2uqc3WLBZiaaqP4DtU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240730163845-b1a4ccb954bf h1:liao9UHurZLtiEwBgT9LMOnKYsHze6eA6w1KQCMVN2Q= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240730163845-b1a4ccb954bf/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= +google.golang.org/genproto v0.0.0-20240903143218-8af14fe29dc1 h1:BulPr26Jqjnd4eYDVe+YvyR7Yc2vJGkO5/0UxD0/jZU= +google.golang.org/genproto v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:hL97c3SYopEHblzpxRL4lSs523++l8DYxGM1FQiYmb4= +google.golang.org/genproto/googleapis/api v0.0.0-20240930140551-af27646dc61f h1:jTm13A2itBi3La6yTGqn8bVSrc3ZZ1r8ENHlIXBfnRA= +google.golang.org/genproto/googleapis/api v0.0.0-20240930140551-af27646dc61f/go.mod h1:CLGoBuH1VHxAUXVPP8FfPwPEVJB6lz3URE5mY2SuayE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240930140551-af27646dc61f h1:cUMEy+8oS78BWIH9OWazBkzbr090Od9tWBNtZHkOhf0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240930140551-af27646dc61f/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1320,8 +1322,8 @@ google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ5 google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= -google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= +google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= +google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -1346,6 +1348,8 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/go-jose/go-jose.v2 v2.6.3 h1:nt80fvSDlhKWQgSWyHyy5CfmlQr+asih51R8PTWNKKs= +gopkg.in/go-jose/go-jose.v2 v2.6.3/go.mod h1:zzZDPkNNw/c9IE7Z9jr11mBZQhKQTMzoEEIoEdZlFBI= gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= diff --git a/internal/flipflop/handler.go b/internal/flipflop/handler.go index 5de0f97..9f33071 100644 --- a/internal/flipflop/handler.go +++ b/internal/flipflop/handler.go @@ -2,9 +2,11 @@ package flipflop import ( "context" + "fmt" "strings" "time" + "github.com/bmc-toolbox/common" ctrl "github.com/metal-toolbox/ctrl" "github.com/metal-toolbox/flipflop/internal/app" "github.com/metal-toolbox/flipflop/internal/device" @@ -15,6 +17,7 @@ import ( "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" "github.com/sirupsen/logrus" + "github.com/stmcginnis/gofish/redfish" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/trace" ) @@ -31,6 +34,29 @@ type ConditionTaskHandler struct { controllerID string } +var ( + ErrValidationUnsupported = errors.New("firmware validation is unsupported on this vendor") +) + +// return a live session to the BMC (or an error). The caller is responsible for closing the connection +func (cth *ConditionTaskHandler) openBMCConnection(ctx context.Context) error { + var bmc device.Queryor + if cth.cfg.Dryrun { // Fake BMC + bmc = device.NewDryRunBMCClient(cth.server) + cth.logger.Warn("using fake BMC") + } else { + bmc = device.NewBMCClient(cth.server, cth.logger) + } + + err := bmc.Open(ctx) + if err != nil { + cth.logger.WithError(err).Error("bmc: failed to connect") + return err + } + cth.bmc = bmc + return nil +} + func (cth *ConditionTaskHandler) HandleTask(ctx context.Context, genTask *rctypes.Task[any, any], publisher ctrl.Publisher) error { ctx, span := otel.Tracer(pkgName).Start( ctx, @@ -78,25 +104,15 @@ func (cth *ConditionTaskHandler) HandleTask(ctx context.Context, genTask *rctype ) cth.logger = loggerEntry - var bmc device.Queryor - if cth.cfg.Dryrun { // Fake BMC - bmc = device.NewDryRunBMCClient(server) - loggerEntry.Warn("Running BMC Device in Dryrun mode") - } else { - bmc = device.NewBMCClient(server, loggerEntry) - } - - err = bmc.Open(ctx) - if err != nil { - loggerEntry.WithError(err).Error("bmc connection failed to connect") + if err := cth.openBMCConnection(ctx); err != nil { return err } + defer func() { - if err := bmc.Close(ctx); err != nil { + if err := cth.bmc.Close(ctx); err != nil { loggerEntry.WithError(err).Error("bmc connection close error") } }() - cth.bmc = bmc return cth.Run(ctx) } @@ -215,73 +231,141 @@ func (cth *ConditionTaskHandler) setNextBootDevice(ctx context.Context, bootDevi return cth.successful(ctx, "next boot device set successfully: "+bootDevice) } +type statusUpdate func(string) + +type delayFunc func(context.Context) error + func (cth *ConditionTaskHandler) validateFirmware(ctx context.Context) error { cth.logger.Info("starting firmware validation") - deadline := time.Now().Add(cth.task.Parameters.ValidateFirmwareTimeout) - - // First reboot the BMC to ensure it's running the desired firmware - if err := cth.bmc.PowerCycleBMC(ctx); err != nil { - return cth.failedWithError(ctx, "failed to power cycle BMC", err) + // confirm server vendor + if !strings.EqualFold(cth.server.Vendor, common.VendorSupermicro) { + cth.logger.WithField("vendor", cth.server.Vendor).Warn("unsupported vendor for firmware validation") + return cth.failedWithError(ctx, "", fmt.Errorf("%w : %s", ErrValidationUnsupported, cth.server.Vendor)) } - var err error + // get the correct handle to the BMC, we let bmclib deal with the differences between X11/X12/X13. + handle := newSMCValidationHandle(cth.server) - // Next we want to cycle the host, but the BMC will take some - // time to reboot, so retry once every 30 seconds up to our - // timeout deadline (ideally we'd have a way to distinguish - // failures that are due to the BMC not being back online yet - // from ones that aren't going to be resolved by waiting and - // retrying...) - for time.Now().Before(deadline) { - if errDelay := sleepInContext(ctx, 30*time.Second); errDelay != nil { - return cth.failedWithError(ctx, "failed to cycle host power after BMC power cycle", errDelay) + // updateFn publishes status messages back to the KV. Because these are status messages, they are + // advisory. We don't care too much if we miss a status update provided it's not the last one. + updateFn := func(payload string) { + err := cth.publishActive(ctx, payload) + if err != nil { + cth.logger. + WithError(err). + WithField("payload", payload). + Warn("updating condition status") } + } - err = cth.bmc.SetPowerState(ctx, "cycle") - if err == nil { - break + waitForBMC := func(ctx context.Context) error { + var err error + select { + case <-time.After(30 * time.Second): + case <-ctx.Done(): + err = ctx.Err() } + return err } - if err != nil { - return cth.failedWithError(ctx, "failed to cycle host power after BMC power cycle", err) + deadlineCtx, cancel := context.WithTimeout(ctx, cth.task.Parameters.ValidateFirmwareTimeout) + validateErr := validateFirmwareInternal(deadlineCtx, handle, updateFn, waitForBMC) + cancel() + + if clErr := handle.Close(context.Background()); clErr != nil { + cth.logger. + WithError(clErr). + Warn("closing bmc handle") + } + + if validateErr != nil { + cth.logger.WithError(validateErr).Warn("error validating firmware") + return cth.failed(ctx, validateErr.Error()) } - // Finally, wait for the host to boot successfully - for time.Now().Before(deadline) { - // sleep before checking to (hopefully) avoid seeing a - // stale POST code from a previous boot before the - // power-cycle has actually started happening - if errDelay := sleepInContext(ctx, 30*time.Second); errDelay != nil { - return cth.failedWithError(ctx, "failed to retrieve host boot status", errDelay) + done := time.Now() + srvID := cth.task.Parameters.AssetID + fwID := cth.task.Parameters.ValidateFirmwareID + if dbErr := cth.store.ValidateFirmwareSet(ctx, srvID, fwID, done); dbErr != nil { + return cth.failedWithError(ctx, "marking firmware set validated", dbErr) + } + return cth.successful(ctx, "firmware set validated: "+fwID.String()) +} + +// XXX: It is incumbent on the caller to close the BMC handle. +// +//nolint:gocyclo // yeah, I know +func validateFirmwareInternal(ctx context.Context, mon model.BMCBootMonitor, update statusUpdate, delay delayFunc) error { + if err := mon.Open(ctx); err != nil { + return fmt.Errorf("opening bmc connection: %w", err) + } + + // First reset the BMC to ensure it's running the desired firmware + if _, err := mon.BmcReset(ctx, string(redfish.PowerCycleResetType)); err != nil { + return fmt.Errorf("doing bmc reset: %w", err) + } + + update("bmc power cycle sent") + _ = mon.Close(ctx) + + // Next we want to cycle the host, but the BMC will take some time to reboot + bmcConnected := false + for !bmcConnected { + if err := delay(ctx); err != nil { + return fmt.Errorf("context error: %w", err) } - booted, err := cth.bmc.HostBooted(ctx) + if err := mon.Open(ctx); err != nil { + payload := fmt.Sprintf("failed to connect to bmc: %s", err.Error()) + update(payload) + continue + } + bmcConnected = true + update("bmc connection re-established") + } + + bmcPowerStateSet := false + for !bmcPowerStateSet { + if err := delay(ctx); err != nil { + return fmt.Errorf("context error: %w", err) + } + + currentState, err := mon.PowerStateGet(ctx) if err != nil { - return cth.failedWithError(ctx, "failed to retrieve host boot status", err) + payload := fmt.Sprintf("getting bmc power state: %s", err.Error()) + update(payload) + continue } - if booted { - done := time.Now() - srvID := cth.task.Parameters.AssetID - fwID := cth.task.Parameters.ValidateFirmwareID - if dbErr := cth.store.ValidateFirmwareSet(ctx, srvID, fwID, done); dbErr != nil { - return cth.failedWithError(ctx, "marking firmware set validated", dbErr) - } - return cth.successful(ctx, "firmware set validated: "+fwID.String()) + + newDeviceState := "cycle" + if strings.Contains(strings.ToLower(currentState), "off") { + newDeviceState = "on" } - } - return cth.failed(ctx, "host failed to boot successfully before deadline") -} + if _, err := mon.PowerSet(ctx, newDeviceState); err != nil { + update(fmt.Sprintf("bmc set power state to %s: %s", newDeviceState, err.Error())) + continue + } + bmcPowerStateSet = true + update(fmt.Sprintf("device power state set to %s", newDeviceState)) + } -func sleepInContext(ctx context.Context, t time.Duration) error { - select { - case <-time.After(t): - return nil - case <-ctx.Done(): - return context.Canceled + hostBooted := false + var err error + for !hostBooted { + // now we've reset the server, give it a chance to come back + if err = delay(ctx); err != nil { + return fmt.Errorf("context error: %w", err) + } + hostBooted, err = mon.BootComplete() + if err != nil { + update(fmt.Sprintf("checking host boot state: %s", err.Error())) + continue + } + update("host boot complete") } + return nil } // pxeBootPersistent sets up the server to pxe boot persistently @@ -293,6 +377,16 @@ func (cth *ConditionTaskHandler) pxeBootPersistent(ctx context.Context) error { return cth.bmc.SetPowerState(ctx, "on") } +func sleepInContext(ctx context.Context, td time.Duration) error { + var err error + select { + case <-time.After(td): + case <-ctx.Done(): + err = ctx.Err() + } + return err +} + func (cth *ConditionTaskHandler) publish(ctx context.Context, status string, state rctypes.State) error { cth.task.State = state cth.task.Status.Append(status) diff --git a/internal/flipflop/handler_test.go b/internal/flipflop/handler_test.go new file mode 100644 index 0000000..9292ca7 --- /dev/null +++ b/internal/flipflop/handler_test.go @@ -0,0 +1,232 @@ +package flipflop + +import ( + "context" + "errors" + "testing" + + "github.com/stmcginnis/gofish/redfish" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + + ffmock "github.com/metal-toolbox/flipflop/internal/model/mock" +) + +// test the internal parts of a firmware validation task +func TestValidateFirmwareInternal(t *testing.T) { + t.Parallel() + t.Run("fail to open monitor", func(t *testing.T) { + t.Parallel() + mon := ffmock.NewMockBMCBootMonitor(t) + delay := func(_ context.Context) error { + return nil + } + update := func(_ string) {} + + mon.EXPECT().Open( + mock.IsType(context.TODO()), + ).Return(errors.New("pound sand")).Times(1) + err := validateFirmwareInternal(context.TODO(), mon, update, delay) + require.Error(t, err, "expected error on Open") + }) + t.Run("fail to do BmcReset", func(t *testing.T) { + t.Parallel() + mon := ffmock.NewMockBMCBootMonitor(t) + delay := func(_ context.Context) error { + return nil + } + update := func(_ string) {} + + mon.EXPECT().Open( + mock.IsType(context.TODO()), + ).Return(nil).Times(1) + + mon.EXPECT().BmcReset( + mock.IsType(context.TODO()), + string(redfish.PowerCycleResetType), + ).Return(false, errors.New("pound sand")).Times(1) + err := validateFirmwareInternal(context.TODO(), mon, update, delay) + require.Error(t, err, "expected error on Open") + }) + t.Run("timeout reconnecting to bmc", func(t *testing.T) { + t.Parallel() + mon := ffmock.NewMockBMCBootMonitor(t) + delay := func(_ context.Context) error { + return context.Canceled + } + update := func(_ string) {} + + mon.EXPECT().Open( + mock.IsType(context.TODO()), + ).Return(nil).Times(1) + + mon.EXPECT().Close( + mock.IsType(context.TODO()), + ).Return(nil).Times(1) + + mon.EXPECT().BmcReset( + mock.IsType(context.TODO()), + string(redfish.PowerCycleResetType), + ).Return(true, nil).Times(1) + err := validateFirmwareInternal(context.TODO(), mon, update, delay) + require.ErrorIs(t, err, context.Canceled) + }) + t.Run("bmc reopen timeout", func(t *testing.T) { + t.Parallel() + mon := ffmock.NewMockBMCBootMonitor(t) + delay := ffmock.NewMockDelayFn(t) + update := ffmock.NewMockUpdateFn(t) + + delay.On("Execute", mock.IsType(context.TODO())).Return(nil).Times(1) + delay.On("Execute", mock.IsType(context.TODO())).Return(context.Canceled).Times(1) + + mon.On("Open", mock.IsType(context.TODO())).Return(nil).Times(1) + mon.On("Open", mock.IsType(context.TODO())).Return(errors.New("pound sand")).Times(1) + + mon.EXPECT().Close( + mock.IsType(context.TODO()), + ).Return(nil).Times(1) + + mon.EXPECT().BmcReset( + mock.IsType(context.TODO()), + string(redfish.PowerCycleResetType), + ).Return(true, nil).Times(1) + + update.On("Execute", "bmc power cycle sent").Return().Times(1) + update.On("Execute", "failed to connect to bmc: pound sand").Return().Times(1) + + err := validateFirmwareInternal(context.TODO(), mon, update.Execute, delay.Execute) + require.ErrorIs(t, err, context.Canceled) + }) + t.Run("timeout on get power-state", func(t *testing.T) { + t.Parallel() + mon := ffmock.NewMockBMCBootMonitor(t) + delay := ffmock.NewMockDelayFn(t) + update := ffmock.NewMockUpdateFn(t) + + // the scenario: open the connection to the BMC + // do a BMC reset + // close/reopen + // get the power state and fail + // timeout + + mon.On("Open", mock.IsType(context.TODO())).Return(nil).Times(2) + mon.On("BmcReset", mock.IsType(context.TODO()), string(redfish.PowerCycleResetType)).Return(true, nil).Times(1) + mon.On("Close", mock.IsType(context.TODO())).Return(nil).Times(1) + + mon.On("PowerStateGet", mock.IsType(context.TODO())).Return("", errors.New("pound sand")).Times(1) + + delay.On("Execute", mock.IsType(context.TODO())).Return(nil).Times(1) + delay.On("Execute", mock.IsType(context.TODO())).Return(nil).Times(1) + delay.On("Execute", mock.IsType(context.TODO())).Return(context.Canceled).Times(1) + + update.On("Execute", "bmc power cycle sent").Return().Times(1) + update.On("Execute", "bmc connection re-established").Return().Times(1) + update.On("Execute", "getting bmc power state: pound sand").Return().Times(1) + + err := validateFirmwareInternal(context.TODO(), mon, update.Execute, delay.Execute) + require.ErrorIs(t, err, context.Canceled) + }) + t.Run("timeout on power set -- power cycle", func(t *testing.T) { + t.Parallel() + mon := ffmock.NewMockBMCBootMonitor(t) + delay := ffmock.NewMockDelayFn(t) + update := ffmock.NewMockUpdateFn(t) + + mon.On("Open", mock.IsType(context.TODO())).Return(nil).Times(2) + mon.On("BmcReset", mock.IsType(context.TODO()), string(redfish.PowerCycleResetType)).Return(true, nil).Times(1) + mon.On("Close", mock.IsType(context.TODO())).Return(nil).Times(1) + + mon.On("PowerStateGet", mock.IsType(context.TODO())).Return("on", nil).Times(1) + mon.On("PowerSet", mock.IsType(context.TODO()), "cycle").Return(false, errors.New("pound sand")).Times(1) + + delay.On("Execute", mock.IsType(context.TODO())).Return(nil).Times(1) + delay.On("Execute", mock.IsType(context.TODO())).Return(nil).Times(1) + delay.On("Execute", mock.IsType(context.TODO())).Return(context.Canceled).Times(1) + + update.On("Execute", "bmc power cycle sent").Return().Times(1) + update.On("Execute", "bmc connection re-established").Return().Times(1) + update.On("Execute", "bmc set power state to cycle: pound sand").Return().Times(1) + + err := validateFirmwareInternal(context.TODO(), mon, update.Execute, delay.Execute) + require.ErrorIs(t, err, context.Canceled) + }) + t.Run("timeout on power set -- power on", func(t *testing.T) { + t.Parallel() + mon := ffmock.NewMockBMCBootMonitor(t) + delay := ffmock.NewMockDelayFn(t) + update := ffmock.NewMockUpdateFn(t) + + mon.On("Open", mock.IsType(context.TODO())).Return(nil).Times(2) + mon.On("BmcReset", mock.IsType(context.TODO()), string(redfish.PowerCycleResetType)).Return(true, nil).Times(1) + mon.On("Close", mock.IsType(context.TODO())).Return(nil).Times(1) + + mon.On("PowerStateGet", mock.IsType(context.TODO())).Return("off", nil).Times(1) + mon.On("PowerSet", mock.IsType(context.TODO()), "on").Return(false, errors.New("pound sand")).Times(1) + + delay.On("Execute", mock.IsType(context.TODO())).Return(nil).Times(1) + delay.On("Execute", mock.IsType(context.TODO())).Return(nil).Times(1) + delay.On("Execute", mock.IsType(context.TODO())).Return(context.Canceled).Times(1) + + update.On("Execute", "bmc power cycle sent").Return().Times(1) + update.On("Execute", "bmc connection re-established").Return().Times(1) + update.On("Execute", "bmc set power state to on: pound sand").Return().Times(1) + + err := validateFirmwareInternal(context.TODO(), mon, update.Execute, delay.Execute) + require.ErrorIs(t, err, context.Canceled) + }) + t.Run("timeout on boot complete", func(t *testing.T) { + t.Parallel() + mon := ffmock.NewMockBMCBootMonitor(t) + delay := ffmock.NewMockDelayFn(t) + update := ffmock.NewMockUpdateFn(t) + + mon.On("Open", mock.IsType(context.TODO())).Return(nil).Times(2) + mon.On("BmcReset", mock.IsType(context.TODO()), string(redfish.PowerCycleResetType)).Return(true, nil).Times(1) + mon.On("Close", mock.IsType(context.TODO())).Return(nil).Times(1) + + mon.On("PowerStateGet", mock.IsType(context.TODO())).Return("off", nil).Times(1) + mon.On("PowerSet", mock.IsType(context.TODO()), "on").Return(true, nil).Times(1) + mon.On("BootComplete").Return(false, errors.New("pound sand")).Times(1) + + delay.On("Execute", mock.IsType(context.TODO())).Return(nil).Times(1) + delay.On("Execute", mock.IsType(context.TODO())).Return(nil).Times(1) + delay.On("Execute", mock.IsType(context.TODO())).Return(nil).Times(1) + delay.On("Execute", mock.IsType(context.TODO())).Return(context.Canceled).Times(1) + + update.On("Execute", "bmc power cycle sent").Return().Times(1) + update.On("Execute", "bmc connection re-established").Return().Times(1) + update.On("Execute", "device power state set to on").Return().Times(1) + update.On("Execute", "checking host boot state: pound sand").Return().Times(1) + + err := validateFirmwareInternal(context.TODO(), mon, update.Execute, delay.Execute) + require.ErrorIs(t, err, context.Canceled) + }) + t.Run("complete validation", func(t *testing.T) { + t.Parallel() + mon := ffmock.NewMockBMCBootMonitor(t) + delay := ffmock.NewMockDelayFn(t) + update := ffmock.NewMockUpdateFn(t) + + mon.On("Open", mock.IsType(context.TODO())).Return(nil).Times(2) + mon.On("BmcReset", mock.IsType(context.TODO()), string(redfish.PowerCycleResetType)).Return(true, nil).Times(1) + mon.On("Close", mock.IsType(context.TODO())).Return(nil).Times(1) + + mon.On("PowerStateGet", mock.IsType(context.TODO())).Return("off", nil).Times(1) + mon.On("PowerSet", mock.IsType(context.TODO()), "on").Return(true, nil).Times(1) + mon.On("BootComplete").Return(true, nil).Times(1) + + delay.On("Execute", mock.IsType(context.TODO())).Return(nil).Times(1) + delay.On("Execute", mock.IsType(context.TODO())).Return(nil).Times(1) + delay.On("Execute", mock.IsType(context.TODO())).Return(nil).Times(1) + + update.On("Execute", "bmc power cycle sent").Return().Times(1) + update.On("Execute", "bmc connection re-established").Return().Times(1) + update.On("Execute", "device power state set to on").Return().Times(1) + update.On("Execute", "host boot complete").Return().Times(1) + + err := validateFirmwareInternal(context.TODO(), mon, update.Execute, delay.Execute) + require.NoError(t, err) + }) + +} diff --git a/internal/flipflop/validation_client.go b/internal/flipflop/validation_client.go new file mode 100644 index 0000000..c982787 --- /dev/null +++ b/internal/flipflop/validation_client.go @@ -0,0 +1,64 @@ +package flipflop + +/* These functions exist to provide a little nicer client experience around using raw bmclib + types without going through the hassle of trying to get the default client of bmclib to + play nice. */ + +import ( + "crypto/tls" + "net" + "net/http" + "net/http/cookiejar" + "time" + + "github.com/bmc-toolbox/bmclib/v2/providers/supermicro" + "github.com/go-logr/logr" + "github.com/metal-toolbox/flipflop/internal/model" + "golang.org/x/net/publicsuffix" +) + +func defaultBMCTransport() *http.Transport { + return &http.Transport{ + //nolint:gosec // BMCs use self-signed certs + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + DisableKeepAlives: true, + Dial: (&net.Dialer{ + Timeout: 120 * time.Second, + KeepAlive: 120 * time.Second, + }).Dial, + TLSHandshakeTimeout: 120 * time.Second, + ResponseHeaderTimeout: 120 * time.Second, + } +} + +func newHTTPClient(opts ...func(*http.Client)) *http.Client { + // we ignore the error here because cookiejar.New always returns nil + jar, _ := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List}) + + client := &http.Client{ + Timeout: time.Second * 120, + Transport: defaultBMCTransport(), + Jar: jar, + } + + for _, opt := range opts { + if opt != nil { + opt(client) + } + } + + return client +} + +func newSMCValidationHandle(srv *model.Asset) model.BMCBootMonitor { + httpClient := newHTTPClient() + hdl := supermicro.NewClient( + srv.BmcAddress.String(), + srv.BmcUsername, + srv.BmcPassword, + logr.Discard(), + supermicro.WithHttpClient(httpClient), + ) + + return hdl +} diff --git a/internal/model/mock/mock_BMCBootMonitor.go b/internal/model/mock/mock_BMCBootMonitor.go new file mode 100644 index 0000000..a1f399a --- /dev/null +++ b/internal/model/mock/mock_BMCBootMonitor.go @@ -0,0 +1,412 @@ +// Code generated by mockery v2.42.1. DO NOT EDIT. + +package model + +import ( + context "context" + + mock "github.com/stretchr/testify/mock" + + redfish "github.com/stmcginnis/gofish/redfish" +) + +// MockBMCBootMonitor is an autogenerated mock type for the BMCBootMonitor type +type MockBMCBootMonitor struct { + mock.Mock +} + +type MockBMCBootMonitor_Expecter struct { + mock *mock.Mock +} + +func (_m *MockBMCBootMonitor) EXPECT() *MockBMCBootMonitor_Expecter { + return &MockBMCBootMonitor_Expecter{mock: &_m.Mock} +} + +// BmcReset provides a mock function with given fields: _a0, _a1 +func (_m *MockBMCBootMonitor) BmcReset(_a0 context.Context, _a1 string) (bool, error) { + ret := _m.Called(_a0, _a1) + + if len(ret) == 0 { + panic("no return value specified for BmcReset") + } + + var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string) (bool, error)); ok { + return rf(_a0, _a1) + } + if rf, ok := ret.Get(0).(func(context.Context, string) bool); ok { + r0 = rf(_a0, _a1) + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockBMCBootMonitor_BmcReset_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BmcReset' +type MockBMCBootMonitor_BmcReset_Call struct { + *mock.Call +} + +// BmcReset is a helper method to define mock.On call +// - _a0 context.Context +// - _a1 string +func (_e *MockBMCBootMonitor_Expecter) BmcReset(_a0 interface{}, _a1 interface{}) *MockBMCBootMonitor_BmcReset_Call { + return &MockBMCBootMonitor_BmcReset_Call{Call: _e.mock.On("BmcReset", _a0, _a1)} +} + +func (_c *MockBMCBootMonitor_BmcReset_Call) Run(run func(_a0 context.Context, _a1 string)) *MockBMCBootMonitor_BmcReset_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string)) + }) + return _c +} + +func (_c *MockBMCBootMonitor_BmcReset_Call) Return(_a0 bool, _a1 error) *MockBMCBootMonitor_BmcReset_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockBMCBootMonitor_BmcReset_Call) RunAndReturn(run func(context.Context, string) (bool, error)) *MockBMCBootMonitor_BmcReset_Call { + _c.Call.Return(run) + return _c +} + +// BootComplete provides a mock function with given fields: +func (_m *MockBMCBootMonitor) BootComplete() (bool, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for BootComplete") + } + + var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func() (bool, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() bool); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockBMCBootMonitor_BootComplete_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BootComplete' +type MockBMCBootMonitor_BootComplete_Call struct { + *mock.Call +} + +// BootComplete is a helper method to define mock.On call +func (_e *MockBMCBootMonitor_Expecter) BootComplete() *MockBMCBootMonitor_BootComplete_Call { + return &MockBMCBootMonitor_BootComplete_Call{Call: _e.mock.On("BootComplete")} +} + +func (_c *MockBMCBootMonitor_BootComplete_Call) Run(run func()) *MockBMCBootMonitor_BootComplete_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockBMCBootMonitor_BootComplete_Call) Return(_a0 bool, _a1 error) *MockBMCBootMonitor_BootComplete_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockBMCBootMonitor_BootComplete_Call) RunAndReturn(run func() (bool, error)) *MockBMCBootMonitor_BootComplete_Call { + _c.Call.Return(run) + return _c +} + +// Close provides a mock function with given fields: _a0 +func (_m *MockBMCBootMonitor) Close(_a0 context.Context) error { + ret := _m.Called(_a0) + + if len(ret) == 0 { + panic("no return value specified for Close") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(_a0) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockBMCBootMonitor_Close_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Close' +type MockBMCBootMonitor_Close_Call struct { + *mock.Call +} + +// Close is a helper method to define mock.On call +// - _a0 context.Context +func (_e *MockBMCBootMonitor_Expecter) Close(_a0 interface{}) *MockBMCBootMonitor_Close_Call { + return &MockBMCBootMonitor_Close_Call{Call: _e.mock.On("Close", _a0)} +} + +func (_c *MockBMCBootMonitor_Close_Call) Run(run func(_a0 context.Context)) *MockBMCBootMonitor_Close_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *MockBMCBootMonitor_Close_Call) Return(_a0 error) *MockBMCBootMonitor_Close_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockBMCBootMonitor_Close_Call) RunAndReturn(run func(context.Context) error) *MockBMCBootMonitor_Close_Call { + _c.Call.Return(run) + return _c +} + +// GetBootProgress provides a mock function with given fields: +func (_m *MockBMCBootMonitor) GetBootProgress() (*redfish.BootProgress, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for GetBootProgress") + } + + var r0 *redfish.BootProgress + var r1 error + if rf, ok := ret.Get(0).(func() (*redfish.BootProgress, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() *redfish.BootProgress); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*redfish.BootProgress) + } + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockBMCBootMonitor_GetBootProgress_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetBootProgress' +type MockBMCBootMonitor_GetBootProgress_Call struct { + *mock.Call +} + +// GetBootProgress is a helper method to define mock.On call +func (_e *MockBMCBootMonitor_Expecter) GetBootProgress() *MockBMCBootMonitor_GetBootProgress_Call { + return &MockBMCBootMonitor_GetBootProgress_Call{Call: _e.mock.On("GetBootProgress")} +} + +func (_c *MockBMCBootMonitor_GetBootProgress_Call) Run(run func()) *MockBMCBootMonitor_GetBootProgress_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockBMCBootMonitor_GetBootProgress_Call) Return(_a0 *redfish.BootProgress, _a1 error) *MockBMCBootMonitor_GetBootProgress_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockBMCBootMonitor_GetBootProgress_Call) RunAndReturn(run func() (*redfish.BootProgress, error)) *MockBMCBootMonitor_GetBootProgress_Call { + _c.Call.Return(run) + return _c +} + +// Open provides a mock function with given fields: _a0 +func (_m *MockBMCBootMonitor) Open(_a0 context.Context) error { + ret := _m.Called(_a0) + + if len(ret) == 0 { + panic("no return value specified for Open") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(_a0) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockBMCBootMonitor_Open_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Open' +type MockBMCBootMonitor_Open_Call struct { + *mock.Call +} + +// Open is a helper method to define mock.On call +// - _a0 context.Context +func (_e *MockBMCBootMonitor_Expecter) Open(_a0 interface{}) *MockBMCBootMonitor_Open_Call { + return &MockBMCBootMonitor_Open_Call{Call: _e.mock.On("Open", _a0)} +} + +func (_c *MockBMCBootMonitor_Open_Call) Run(run func(_a0 context.Context)) *MockBMCBootMonitor_Open_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *MockBMCBootMonitor_Open_Call) Return(_a0 error) *MockBMCBootMonitor_Open_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockBMCBootMonitor_Open_Call) RunAndReturn(run func(context.Context) error) *MockBMCBootMonitor_Open_Call { + _c.Call.Return(run) + return _c +} + +// PowerSet provides a mock function with given fields: _a0, _a1 +func (_m *MockBMCBootMonitor) PowerSet(_a0 context.Context, _a1 string) (bool, error) { + ret := _m.Called(_a0, _a1) + + if len(ret) == 0 { + panic("no return value specified for PowerSet") + } + + var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string) (bool, error)); ok { + return rf(_a0, _a1) + } + if rf, ok := ret.Get(0).(func(context.Context, string) bool); ok { + r0 = rf(_a0, _a1) + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockBMCBootMonitor_PowerSet_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PowerSet' +type MockBMCBootMonitor_PowerSet_Call struct { + *mock.Call +} + +// PowerSet is a helper method to define mock.On call +// - _a0 context.Context +// - _a1 string +func (_e *MockBMCBootMonitor_Expecter) PowerSet(_a0 interface{}, _a1 interface{}) *MockBMCBootMonitor_PowerSet_Call { + return &MockBMCBootMonitor_PowerSet_Call{Call: _e.mock.On("PowerSet", _a0, _a1)} +} + +func (_c *MockBMCBootMonitor_PowerSet_Call) Run(run func(_a0 context.Context, _a1 string)) *MockBMCBootMonitor_PowerSet_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string)) + }) + return _c +} + +func (_c *MockBMCBootMonitor_PowerSet_Call) Return(_a0 bool, _a1 error) *MockBMCBootMonitor_PowerSet_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockBMCBootMonitor_PowerSet_Call) RunAndReturn(run func(context.Context, string) (bool, error)) *MockBMCBootMonitor_PowerSet_Call { + _c.Call.Return(run) + return _c +} + +// PowerStateGet provides a mock function with given fields: _a0 +func (_m *MockBMCBootMonitor) PowerStateGet(_a0 context.Context) (string, error) { + ret := _m.Called(_a0) + + if len(ret) == 0 { + panic("no return value specified for PowerStateGet") + } + + var r0 string + var r1 error + if rf, ok := ret.Get(0).(func(context.Context) (string, error)); ok { + return rf(_a0) + } + if rf, ok := ret.Get(0).(func(context.Context) string); ok { + r0 = rf(_a0) + } else { + r0 = ret.Get(0).(string) + } + + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(_a0) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockBMCBootMonitor_PowerStateGet_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PowerStateGet' +type MockBMCBootMonitor_PowerStateGet_Call struct { + *mock.Call +} + +// PowerStateGet is a helper method to define mock.On call +// - _a0 context.Context +func (_e *MockBMCBootMonitor_Expecter) PowerStateGet(_a0 interface{}) *MockBMCBootMonitor_PowerStateGet_Call { + return &MockBMCBootMonitor_PowerStateGet_Call{Call: _e.mock.On("PowerStateGet", _a0)} +} + +func (_c *MockBMCBootMonitor_PowerStateGet_Call) Run(run func(_a0 context.Context)) *MockBMCBootMonitor_PowerStateGet_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *MockBMCBootMonitor_PowerStateGet_Call) Return(_a0 string, _a1 error) *MockBMCBootMonitor_PowerStateGet_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockBMCBootMonitor_PowerStateGet_Call) RunAndReturn(run func(context.Context) (string, error)) *MockBMCBootMonitor_PowerStateGet_Call { + _c.Call.Return(run) + return _c +} + +// NewMockBMCBootMonitor creates a new instance of MockBMCBootMonitor. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewMockBMCBootMonitor(t interface { + mock.TestingT + Cleanup(func()) +}) *MockBMCBootMonitor { + mock := &MockBMCBootMonitor{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/model/mock/mock_DelayFn.go b/internal/model/mock/mock_DelayFn.go new file mode 100644 index 0000000..6ce3f74 --- /dev/null +++ b/internal/model/mock/mock_DelayFn.go @@ -0,0 +1,82 @@ +// Code generated by mockery v2.42.1. DO NOT EDIT. + +package model + +import ( + context "context" + + mock "github.com/stretchr/testify/mock" +) + +// MockDelayFn is an autogenerated mock type for the DelayFn type +type MockDelayFn struct { + mock.Mock +} + +type MockDelayFn_Expecter struct { + mock *mock.Mock +} + +func (_m *MockDelayFn) EXPECT() *MockDelayFn_Expecter { + return &MockDelayFn_Expecter{mock: &_m.Mock} +} + +// Execute provides a mock function with given fields: _a0 +func (_m *MockDelayFn) Execute(_a0 context.Context) error { + ret := _m.Called(_a0) + + if len(ret) == 0 { + panic("no return value specified for Execute") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(_a0) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockDelayFn_Execute_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Execute' +type MockDelayFn_Execute_Call struct { + *mock.Call +} + +// Execute is a helper method to define mock.On call +// - _a0 context.Context +func (_e *MockDelayFn_Expecter) Execute(_a0 interface{}) *MockDelayFn_Execute_Call { + return &MockDelayFn_Execute_Call{Call: _e.mock.On("Execute", _a0)} +} + +func (_c *MockDelayFn_Execute_Call) Run(run func(_a0 context.Context)) *MockDelayFn_Execute_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context)) + }) + return _c +} + +func (_c *MockDelayFn_Execute_Call) Return(_a0 error) *MockDelayFn_Execute_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockDelayFn_Execute_Call) RunAndReturn(run func(context.Context) error) *MockDelayFn_Execute_Call { + _c.Call.Return(run) + return _c +} + +// NewMockDelayFn creates a new instance of MockDelayFn. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewMockDelayFn(t interface { + mock.TestingT + Cleanup(func()) +}) *MockDelayFn { + mock := &MockDelayFn{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/model/mock/mock_UpdateFn.go b/internal/model/mock/mock_UpdateFn.go new file mode 100644 index 0000000..7cf96c6 --- /dev/null +++ b/internal/model/mock/mock_UpdateFn.go @@ -0,0 +1,65 @@ +// Code generated by mockery v2.42.1. DO NOT EDIT. + +package model + +import mock "github.com/stretchr/testify/mock" + +// MockUpdateFn is an autogenerated mock type for the UpdateFn type +type MockUpdateFn struct { + mock.Mock +} + +type MockUpdateFn_Expecter struct { + mock *mock.Mock +} + +func (_m *MockUpdateFn) EXPECT() *MockUpdateFn_Expecter { + return &MockUpdateFn_Expecter{mock: &_m.Mock} +} + +// Execute provides a mock function with given fields: _a0 +func (_m *MockUpdateFn) Execute(_a0 string) { + _m.Called(_a0) +} + +// MockUpdateFn_Execute_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Execute' +type MockUpdateFn_Execute_Call struct { + *mock.Call +} + +// Execute is a helper method to define mock.On call +// - _a0 string +func (_e *MockUpdateFn_Expecter) Execute(_a0 interface{}) *MockUpdateFn_Execute_Call { + return &MockUpdateFn_Execute_Call{Call: _e.mock.On("Execute", _a0)} +} + +func (_c *MockUpdateFn_Execute_Call) Run(run func(_a0 string)) *MockUpdateFn_Execute_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string)) + }) + return _c +} + +func (_c *MockUpdateFn_Execute_Call) Return() *MockUpdateFn_Execute_Call { + _c.Call.Return() + return _c +} + +func (_c *MockUpdateFn_Execute_Call) RunAndReturn(run func(string)) *MockUpdateFn_Execute_Call { + _c.Call.Return(run) + return _c +} + +// NewMockUpdateFn creates a new instance of MockUpdateFn. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewMockUpdateFn(t interface { + mock.TestingT + Cleanup(func()) +}) *MockUpdateFn { + mock := &MockUpdateFn{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/model/types.go b/internal/model/types.go index 71a588e..cf9ad75 100644 --- a/internal/model/types.go +++ b/internal/model/types.go @@ -1,9 +1,11 @@ package model import ( + "context" "net" "github.com/google/uuid" + "github.com/stmcginnis/gofish/redfish" ) type ( @@ -46,3 +48,34 @@ type Asset struct { // Facility this Asset is hosted in. FacilityCode string } + +// UpdateFn is a function that publishes the given string as a condition status message +type UpdateFn func(string) + +type DelayFn func(context.Context) error + +type OpenCloser interface { + Open(context.Context) error + Close(context.Context) error +} + +type BMCResetter interface { + BmcReset(context.Context, string) (bool, error) +} + +type PowerMonitor interface { + PowerStateGet(context.Context) (string, error) + PowerSet(context.Context, string) (bool, error) +} + +type BootMonitor interface { + GetBootProgress() (*redfish.BootProgress, error) + BootComplete() (bool, error) +} + +type BMCBootMonitor interface { + OpenCloser + BMCResetter + BootMonitor + PowerMonitor +}