diff --git a/Makefile b/Makefile index 2d6ef607..078f5b84 100644 --- a/Makefile +++ b/Makefile @@ -20,13 +20,6 @@ lint: gen-mock test: lint go test -timeout 1m -v -covermode=atomic ./... -## Generate mocks -gen-mock: - go get go.uber.org/mock@v0.4.0 - go install go.uber.org/mock/mockgen@v0.4.0 - go generate ./... - go mod tidy - ## build-osx build-osx: ifeq (${GO_VERSION}, 0) diff --git a/cmd/export.go b/cmd/export.go index 8b4123e7..d05a4fc2 100644 --- a/cmd/export.go +++ b/cmd/export.go @@ -57,7 +57,7 @@ func asGraph(s *sw.StateMachineJSON) *dot.Graph { return g } -func taskStateMachine() { +func conditionStateMachine() { g := dot.NewGraph(dot.Directed) pending := g.Node(string(model.StatePending)) @@ -103,7 +103,7 @@ func outofbandActionStatemachine() { func exportStatemachine() { if exportFlagSet.taskSM { - taskStateMachine() + conditionStateMachine() return } diff --git a/cmd/run.go b/cmd/run.go index 9f938c70..15603a52 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -11,10 +11,10 @@ import ( "github.com/metal-toolbox/flasher/internal/model" "github.com/metal-toolbox/flasher/internal/store" "github.com/metal-toolbox/flasher/internal/worker" + "github.com/metal-toolbox/rivets/events/controller" "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/spf13/cobra" - "go.hollow.sh/toolbox/events" // nolint:gosec // profiling endpoint listens on localhost. _ "net/http/pprof" @@ -70,36 +70,48 @@ func runWorker(ctx context.Context) { cancelFunc() }() - inv, err := initInventory(ctx, flasher.Config, flasher.Logger) + repository, err := initStore(ctx, flasher.Config, flasher.Logger) if err != nil { flasher.Logger.Fatal(err) } - stream, err := events.NewStream(*flasher.Config.NatsOptions) + if facilityCode == "" { + flasher.Logger.Fatal("--facility-code parameter required") + } + + natsURL, natsCreds, connectTimeout, err := flasher.NatsParams() if err != nil { flasher.Logger.Fatal(err) } - if useStatusKV && facilityCode == "" { - flasher.Logger.Fatal("--use-kv flag requires a --facility-code parameter") + nc := controller.NewNatsController( + model.AppName, + facilityCode, + "firmwareInstall", + natsURL, + natsCreds, + "firmwareInstall", + controller.WithConcurrency(10), + controller.WithKVReplicas(1), + controller.WithLogger(flasher.Logger), + controller.WithConnectionTimeout(connectTimeout), + ) + + if err := nc.Connect(ctx); err != nil { + flasher.Logger.Fatal(err) } - w := worker.New( - facilityCode, + worker.Run( + ctx, dryrun, - useStatusKV, faultInjection, - flasher.Config.Concurrency, - replicas, - stream, - inv, + repository, + nc, flasher.Logger, ) - - w.Run(ctx) } -func initInventory(ctx context.Context, config *app.Configuration, logger *logrus.Logger) (store.Repository, error) { +func initStore(ctx context.Context, config *app.Configuration, logger *logrus.Logger) (store.Repository, error) { switch { // from CLI flags case strings.HasSuffix(storeKind, ".yml"), strings.HasSuffix(storeKind, ".yaml"): diff --git a/go.mod b/go.mod index 6cc60760..97b71861 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/metal-toolbox/flasher -go 1.20 +go 1.22 require ( github.com/banzaicloud/logrus-runtime-formatter v0.0.0-20190729070250-5ae5475bae5e @@ -17,25 +17,25 @@ require ( github.com/jeremywohl/flatten v1.0.1 github.com/jinzhu/copier v0.4.0 github.com/jpillora/backoff v1.0.0 - github.com/metal-toolbox/conditionorc v1.0.4 - github.com/metal-toolbox/rivets v0.2.2 + github.com/metal-toolbox/rivets v1.0.2-0.20240321173737-71ffdb3bf742 github.com/mitchellh/mapstructure v1.5.0 - github.com/nats-io/nats-server/v2 v2.10.7 + github.com/nats-io/nats-server/v2 v2.10.11 github.com/nats-io/nats.go v1.33.1 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.19.0 github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.8.0 github.com/spf13/viper v1.18.2 - github.com/stretchr/testify v1.8.4 + github.com/stretchr/testify v1.9.0 go.hollow.sh/serverservice v0.16.2 go.hollow.sh/toolbox v0.6.2 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 go.opentelemetry.io/otel v1.24.0 go.opentelemetry.io/otel/trace v1.24.0 + go.uber.org/goleak v1.3.0 go.uber.org/mock v0.4.0 golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 - golang.org/x/net v0.21.0 + golang.org/x/net v0.22.0 golang.org/x/oauth2 v0.17.0 ) @@ -45,14 +45,14 @@ require ( 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/bytedance/sonic v1.11.1 // indirect + github.com/bytedance/sonic v1.11.2 // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect github.com/chenzhuoyu/iasm v0.9.1 // indirect github.com/cockroachdb/cockroach-go/v2 v2.3.6 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/ericlagergren/decimal v0.0.0-20221120152707-495c53812d05 // indirect + github.com/ericlagergren/decimal v0.0.0-20240305081647-93d586550569 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/friendsofgo/errors v0.9.2 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect @@ -64,7 +64,7 @@ require ( 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.18.0 // indirect + github.com/go-playground/validator/v10 v10.19.0 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/gofrs/uuid v4.4.0+incompatible // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect @@ -78,13 +78,13 @@ require ( github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jackc/chunkreader/v2 v2.0.1 // indirect - github.com/jackc/pgconn v1.14.1 // indirect + github.com/jackc/pgconn v1.14.3 // indirect github.com/jackc/pgio v1.0.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect - github.com/jackc/pgproto3/v2 v2.3.2 // indirect + github.com/jackc/pgproto3/v2 v2.3.3 // indirect github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 // indirect github.com/jackc/pgtype v1.14.2 // indirect - github.com/jackc/pgx/v4 v4.18.1 // indirect + github.com/jackc/pgx/v4 v4.18.2 // indirect github.com/jacobweinstock/iamt v0.0.0-20230502042727-d7cdbe67d9ef // indirect github.com/jacobweinstock/registrar v0.4.7 // indirect github.com/jmoiron/sqlx v1.3.5 // indirect @@ -98,7 +98,7 @@ require ( github.com/minio/highwayhash 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/nats-io/jwt/v2 v2.5.3 // indirect + github.com/nats-io/jwt/v2 v2.5.5 // indirect github.com/nats-io/nkeys v0.4.7 // indirect github.com/nats-io/nuid v1.0.1 // indirect github.com/onsi/ginkgo v1.16.5 // indirect @@ -106,7 +106,7 @@ require ( 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.0 // indirect - github.com/prometheus/common v0.48.0 // indirect + github.com/prometheus/common v0.49.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect @@ -116,6 +116,7 @@ require ( github.com/spf13/cast v1.6.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 github.com/ugorji/go/codec v1.2.12 // indirect @@ -136,15 +137,15 @@ require ( go.uber.org/zap v1.27.0 // indirect gocloud.dev v0.36.0 // indirect golang.org/x/arch v0.7.0 // indirect - golang.org/x/crypto v0.20.0 // indirect - golang.org/x/sys v0.17.0 // indirect + golang.org/x/crypto v0.21.0 // indirect + golang.org/x/sys v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.5.0 // indirect golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect - google.golang.org/api v0.167.0 // indirect + google.golang.org/api v0.168.0 // indirect google.golang.org/appengine v1.6.8 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240304212257-790db918fca8 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240304212257-790db918fca8 // indirect google.golang.org/grpc v1.62.0 // indirect google.golang.org/protobuf v1.32.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/go.sum b/go.sum index 89c924bd..9af612a8 100644 --- a/go.sum +++ b/go.sum @@ -42,12 +42,16 @@ 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.23.4 h1:EBT9Nw4q3zyE7G45Wvv3MzolIrCJEuHys5muLY0wvAw= +cloud.google.com/go/compute v1.23.4/go.mod h1:/EJMj55asU6kAFnuZET8zqgwgJ9FvXWXOkkfQZa4ioI= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= 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.5 h1:1jTsCu4bcsNsE4iiqNT5SHwrDRCfRmIaaaVFhRveTJI= +cloud.google.com/go/iam v1.1.5/go.mod h1:rB6P/Ic3mykPbFio+vo7403drjlgvoWfYpJhMXEbzv8= cloud.google.com/go/kms v1.15.5 h1:pj1sRfut2eRbD9pFRjNnPNg/CzJPuQAzUujMIM1vVeM= +cloud.google.com/go/kms v1.15.5/go.mod h1:cU2H5jnp6G2TDpUGZyqTCoy1n16fbubHZjmVXSMtwDI= 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= @@ -107,8 +111,8 @@ github.com/bombsimon/logrusr/v2 v2.0.1 h1:1VgxVNQMCvjirZIYaT9JYn6sAVGVEcNtRE0y4m github.com/bombsimon/logrusr/v2 v2.0.1/go.mod h1:ByVAX+vHdLGAfdroiMg6q0zgq2FODY2lc5YJvzmOJio= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= -github.com/bytedance/sonic v1.11.1 h1:JC0+6c9FoWYYxakaoa+c5QTtJeiSZNeByOBhXtAFSn4= -github.com/bytedance/sonic v1.11.1/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= +github.com/bytedance/sonic v1.11.2 h1:ywfwo0a/3j9HR8wsYGWsIWl2mvRsI950HyoxiBERw5A= +github.com/bytedance/sonic v1.11.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -175,19 +179,21 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/equinix-labs/otel-init-go v0.0.9 h1:hdh0Qifs1vzFnaN6UpJz0pO6A6ZejXjvkEFi8OGTfpE= github.com/equinix-labs/otel-init-go v0.0.9/go.mod h1:5h8apPuPWz/KaMvAb3d0HoPEisQrUnqPmkc2T5SSpX4= github.com/ericlagergren/decimal v0.0.0-20190420051523-6335edbaa640/go.mod h1:mdYyfAkzn9kyJ/kMk/7WE9ufl9lflh+2NvecQ5mAghs= -github.com/ericlagergren/decimal v0.0.0-20221120152707-495c53812d05 h1:S92OBrGuLLZsyM5ybUzgc/mPjIYk2AZqufieooe98uw= -github.com/ericlagergren/decimal v0.0.0-20221120152707-495c53812d05/go.mod h1:M9R1FoZ3y//hwwnJtO51ypFGwm8ZfpxPT/ZLtO1mcgQ= +github.com/ericlagergren/decimal v0.0.0-20240305081647-93d586550569 h1:PNe5U6Gc0FAVbptp1BXy1ZYi8MyBoeIBWLU2DBH9YnA= +github.com/ericlagergren/decimal v0.0.0-20240305081647-93d586550569/go.mod h1:M9R1FoZ3y//hwwnJtO51ypFGwm8ZfpxPT/ZLtO1mcgQ= github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w= +github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/filanov/stateswitch v1.0.1-0.20221115122927-775ae5822fad h1:pc0NHPxVnTThGFQDoxL9nsgrE347+veHR0M5C5s/k1Q= github.com/filanov/stateswitch v1.0.1-0.20221115122927-775ae5822fad/go.mod h1:GYnXtGE0e/uuFBz4CbjJL0JmP3DWwzGtcpjZYYC9ikc= github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/friendsofgo/errors v0.9.2 h1:X6NYxef4efCBdwI7BgS820zFaN7Cphrmb+Pljdzjtgk= github.com/friendsofgo/errors v0.9.2/go.mod h1:yCvFW5AkDIL9qn7suHVLiI/gH228n7PC4Pn44IGoTOI= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -199,10 +205,12 @@ github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uq github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gin-contrib/cors v1.4.0 h1:oJ6gwtUl3lqV0WEIwM/LxPF1QZ5qe2lGWdY2+bz7y0g= +github.com/gin-contrib/cors v1.5.0 h1:DgGKV7DDoOn36DFkNtbHrjoRiT5ExCe+PC9/xp7aKvk= +github.com/gin-contrib/cors v1.5.0/go.mod h1:TvU7MAZ3EwrPLI2ztzTt3tqgvBCq+wn8WpZmfADjupI= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-contrib/zap v0.1.0 h1:RMSFFJo34XZogV62OgOzvrlaMNmXrNxmJ3bFmMwl6Cc= +github.com/gin-contrib/zap v1.1.0 h1:GWzL9+zmK8OJdiycaK2SK1/D3SZIYpieJDD0QCNAU1o= +github.com/gin-contrib/zap v1.1.0/go.mod h1:KzROP9rAL7ofFd1P8lx7Oo2lerwPWNL5vv4f6U/mAk8= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -221,23 +229,27 @@ github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ4 github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/zerologr v1.2.3 h1:up5N9vcH9Xck3jJkXzgyOxozT14R47IyDODz8LM1KSs= +github.com/go-logr/zerologr v1.2.3/go.mod h1:BxwGo7y5zgSHYR1BjbnHPyF/5ZjVKfKxAZANVu6E8Ho= github.com/go-openapi/swag v0.19.9/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= 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.18.0 h1:BvolUXjp4zuvkZ5YN5t7ebzbhlUtPsPm2S9NAZ5nl9U= -github.com/go-playground/validator/v10 v10.18.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/go-playground/validator/v10 v10.19.0 h1:ol+5Fu+cSq9JD7SoSqe04GMI92cbn0+wvQ3bZ8b/AU4= +github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= +github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= +github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= @@ -303,6 +315,7 @@ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -325,13 +338,16 @@ github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLe 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/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= 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.5.0 h1:I7ELFeVBr3yfPIcc8+MWvrjk+3VjbcSzoXm3JVa+jD8= +github.com/google/wire v0.5.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU= 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/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= @@ -363,6 +379,7 @@ github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrj github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= +github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= @@ -409,9 +426,8 @@ github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsU github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= -github.com/jackc/pgconn v1.14.0/go.mod h1:9mBNlny0UvkgJdCDvdVHYSjI+8tD2rnKK69Wz8ti++E= -github.com/jackc/pgconn v1.14.1 h1:smbxIaZA08n6YuxEX1sDyjV/qkbtUtkH20qLkR9MUR4= -github.com/jackc/pgconn v1.14.1/go.mod h1:9mBNlny0UvkgJdCDvdVHYSjI+8tD2rnKK69Wz8ti++E= +github.com/jackc/pgconn v1.14.3 h1:bVoTr12EGANZz66nZPkMInAV/KHD2TxH9npjXXgiB3w= +github.com/jackc/pgconn v1.14.3/go.mod h1:RZbme4uasqzybK2RK5c65VsHxoyaml09lx3tXOcO/VM= github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= @@ -427,25 +443,23 @@ github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvW github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.3.2 h1:7eY55bdBeCz1F2fTzSz69QC+pG46jYq9/jtSPiJ5nn0= -github.com/jackc/pgproto3/v2 v2.3.2/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag= +github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA= github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= -github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= github.com/jackc/pgtype v1.14.2 h1:QBdZQTKpPdBlw2AdKwHEyqUcm/lrl2cwWAHjCMyln/o= github.com/jackc/pgtype v1.14.2/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= -github.com/jackc/pgx/v4 v4.18.1 h1:YP7G1KABtKpB5IHrO9vYwSrCOhs7p3uqhvhhQBptya0= -github.com/jackc/pgx/v4 v4.18.1/go.mod h1:FydWkUyadDmdNH/mHnGob881GawxeEm7TcMCzkb+qQE= +github.com/jackc/pgx/v4 v4.18.2 h1:xVpYkNR5pk5bMCZGfClbO962UIqVABcAGt7ha1s/FeU= +github.com/jackc/pgx/v4 v4.18.2/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw= github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= @@ -496,6 +510,7 @@ github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -523,6 +538,7 @@ github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= @@ -539,10 +555,10 @@ github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4 github.com/mattn/go-sqlite3 v2.0.1+incompatible h1:xQ15muvnzGBHpIpdrNi1DA5x0+TcBZzsIDwmw9uTHzw= github.com/mattn/go-sqlite3 v2.0.1+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/metal-toolbox/conditionorc v1.0.4 h1:2tYpVcd2zQ4FVMUxDnb6NbBAMgS1eMUox2yWoksEhHs= -github.com/metal-toolbox/conditionorc v1.0.4/go.mod h1:D1pMMBKyhJiAcjhZrNDubuA61rFJqB3BKaYgF94nmSs= -github.com/metal-toolbox/rivets v0.2.2 h1:+PM1l+Ebpfioog4iqIZq13BLdcM7vP5Tq2OoaPQx0ts= -github.com/metal-toolbox/rivets v0.2.2/go.mod h1:XYcHa0lsKG3jYKImcCfjtHkjTNewLGonzozOC5IcdKk= +github.com/metal-toolbox/rivets v1.0.1-0.20240311140746-9e07088bc9ba h1:/UQOTjbz7UcGHe5CgC/t+8njrDCHFTOGvGRsaF1BMDo= +github.com/metal-toolbox/rivets v1.0.1-0.20240311140746-9e07088bc9ba/go.mod h1:KyxerbVyMrt9sMYulthYvxR+1Ji9kr87RCaWucxahjw= +github.com/metal-toolbox/rivets v1.0.2-0.20240321173737-71ffdb3bf742 h1:HRt0hqcj7U74RtkLkRqErG8lZVVfgQKm6n3tcNUNBEk= +github.com/metal-toolbox/rivets v1.0.2-0.20240321173737-71ffdb3bf742/go.mod h1:EMQJRT1mjIyFRXxvKNaBlz7Z4Sp88rTaGO8W18olN2I= 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= @@ -571,10 +587,10 @@ github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3P github.com/montanaflynn/stats v0.6.6/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/nats-io/jwt/v2 v2.5.3 h1:/9SWvzc6hTfamcgXJ3uYRpgj+QuY2aLNqRiqrKcrpEo= -github.com/nats-io/jwt/v2 v2.5.3/go.mod h1:iysuPemFcc7p4IoYots3IuELSI4EDe9Y0bQMe+I3Bf4= -github.com/nats-io/nats-server/v2 v2.10.7 h1:f5VDy+GMu7JyuFA0Fef+6TfulfCs5nBTgq7MMkFJx5Y= -github.com/nats-io/nats-server/v2 v2.10.7/go.mod h1:V2JHOvPiPdtfDXTuEUsthUnCvSDeFrK4Xn9hRo6du7c= +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.11 h1:yKUiLVincZISpo3A4YljJQ+HfLltGAgoNNJl99KL8I0= +github.com/nats-io/nats-server/v2 v2.10.11/go.mod h1:dXtOqVWzbMTEj+tUyC/itXjJhW37xh0tUBrTAlqAfx8= github.com/nats-io/nats.go v1.33.1 h1:8TxLZZ/seeEfR97qV0/Bl939tpDnt2Z2fK3HkPypj70= github.com/nats-io/nats.go v1.33.1/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8= github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI= @@ -630,8 +646,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.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= -github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= +github.com/prometheus/common v0.49.0 h1:ToNTdK4zSnPVJmh698mGFkDor9wBI/iGaJy5dbH1EgI= +github.com/prometheus/common v0.49.0/go.mod h1:Kxm+EULxRbUkjGU6WFsQqo3ORzB4tyKvlWFOE9mB2sE= 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= @@ -644,10 +660,12 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A= +github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sagikazarmark/crypt v0.6.0/go.mod h1:U8+INwJo3nBv1m6A/8OBXAq7Jnpspk5AxSgDyEQcea8= @@ -696,6 +714,8 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -706,8 +726,9 @@ 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 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= 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= github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= @@ -740,6 +761,7 @@ github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= github.com/zsais/go-gin-prometheus v0.1.0 h1:bkLv1XCdzqVgQ36ScgRi09MA2UC1t3tAB6nsfErsGO4= +github.com/zsais/go-gin-prometheus v0.1.0/go.mod h1:Slirjzuz8uM8Cw0jmPNqbneoqcUtY2GGjn2bEd4NRLY= go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A= go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v2 v2.305.4/go.mod h1:Ud+VUwIi9/uQHOMA+4ekToJ12lTxlv0zB/+DHwTGEbU= @@ -748,7 +770,8 @@ go.hollow.sh/serverservice v0.16.2 h1:0wt0XJC/rm4OdovB6g8Fsx7BT2X71pG0keJiiLZaY+ go.hollow.sh/serverservice v0.16.2/go.mod h1:Zr99vSFO++ZlDjEf9rtPWi3TiMgMswJCW3D6LIFmMDM= go.hollow.sh/toolbox v0.6.2 h1:g0qKvo7rVgZ05dh7qxbAymPixumCd4MxVbq9gs90/3c= go.hollow.sh/toolbox v0.6.2/go.mod h1:nl+5RDDyYY/+wukOUzHHX2mOyWKRjlTOXUcGxny+tns= -go.infratographer.com/x v0.3.7 h1:kkykoVtC8XrmvC4oZwHWa/15+dv9RhQHgSm8KoEb/Nc= +go.infratographer.com/x v0.3.9 h1:fsfF/w5zHgiNAHvYmvsWlICNha2X53WNLVSKOkyPnWo= +go.infratographer.com/x v0.3.9/go.mod h1:n/61MZRKFbGlS8xUwAhTyDhqcL2Wk6uPsXADC2n5t1I= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -758,8 +781,10 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= 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.42.0 h1:l7AmwSVqozWKKXeZHycpdmpycQECRpoGwJ1FW2sWfTo= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.48.0 h1:P+/g8GpuJGYbOp2tAdKrIPUX9JO02q8Q0YNlHolpibA= +go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.49.0 h1:1f31+6grJmV3X4lxcEvUy13i5/kfDw1nJZwhd8mA4tg= +go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.49.0/go.mod h1:1P/02zM3OwkX9uki+Wmxw3a5GVb6KUXRsa7m7bOC9Fg= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= @@ -783,6 +808,7 @@ go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= @@ -824,9 +850,8 @@ golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/crypto v0.20.0 h1:jmAMJJZXr5KiCw05dfYK9QnqaqKLYXijU23lsEdcQqg= -golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= 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= @@ -917,9 +942,8 @@ golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= -golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= +golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= 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= @@ -956,6 +980,7 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220513210516-0976fa681c29/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1052,12 +1077,11 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.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= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1068,7 +1092,6 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1189,8 +1212,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.167.0 h1:CKHrQD1BLRii6xdkatBDXyKzM0mkawt2QP+H3LtPmSE= -google.golang.org/api v0.167.0/go.mod h1:4FcBc686KFi7QI/U51/2GKKevfZMpM17sCdibqe/bSA= +google.golang.org/api v0.168.0 h1:MBRe+Ki4mMN93jhDDbpuRLjRddooArz4FeSObvUMmjY= +google.golang.org/api v0.168.0/go.mod h1:gpNOiMA2tZ4mf5R9Iwf4rK/Dcz0fbdIgWYWVoxmsyLg= 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= @@ -1278,11 +1301,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-20240213162025-012b6fc9bca9 h1:9+tzLLstTlPTRyJTh+ah5wIMsBW5c4tQwGTN3thOW9Y= -google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de h1:jFNzHPIeuzhdRwVhbZdiym9q0ory/xY3sA+v2wPg8I0= -google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:5iCWqnniDlqZHrd3neWVTOwvh/v6s3232omMecelax8= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de h1:cZGRis4/ot9uVm639a+rHCUaG0JJHEsdyzSQTMX+suY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY= +google.golang.org/genproto v0.0.0-20240205150955-31a09d347014 h1:g/4bk7P6TPMkAUbUhquq98xey1slwvuVJPosdBqYJlU= +google.golang.org/genproto v0.0.0-20240205150955-31a09d347014/go.mod h1:xEgQu1e4stdSSsxPDK8Azkrk/ECl5HvdPf6nbZrTS5M= +google.golang.org/genproto/googleapis/api v0.0.0-20240304212257-790db918fca8 h1:8eadJkXbwDEMNwcB5O0s5Y5eCfyuCLdvaiOIaGTrWmQ= +google.golang.org/genproto/googleapis/api v0.0.0-20240304212257-790db918fca8/go.mod h1:O1cOfN1Cy6QEYr7VxtjOyP5AdAuR0aJ/MYZaaof623Y= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240304212257-790db918fca8 h1:IR+hp6ypxjH24bkMfEJ0yHR21+gwPWdV+/IBrPQyn3k= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240304212257-790db918fca8/go.mod h1:UCOku4NytXMJuLQE5VuqA5lX3PcHCBo8pxNyvkf4xBs= 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= @@ -1341,6 +1365,7 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 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= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= diff --git a/internal/app/config.go b/internal/app/config.go index 578dfe43..53e510ae 100644 --- a/internal/app/config.go +++ b/internal/app/config.go @@ -10,11 +10,11 @@ import ( "github.com/metal-toolbox/flasher/internal/model" "github.com/mitchellh/mapstructure" "github.com/pkg/errors" - "go.hollow.sh/toolbox/events" ) const ( - WorkerConcurrency = 1 + WorkerConcurrency = 1 + defaultNatsConnectTimeout = 60 * time.Second ) var ( @@ -47,16 +47,6 @@ type Configuration struct { // // This parameter is required when StoreKind is set to serverservice. ServerserviceOptions *ServerserviceOptions `mapstructure:"serverservice"` - - // EventsBrokerKind indicates the kind of event broker configuration to enable, - // - // Supported parameter value - nats - EventsBorkerKind string `mapstructure:"events_broker_kind"` - - // NatsOptions defines the NATs events broker configuration parameters. - // - // This parameter is required when EventsBrokerKind is set to nats. - NatsOptions *events.NatsOptions `mapstructure:"nats"` } // ServerserviceOptions defines configuration for the Serverservice client. @@ -89,10 +79,6 @@ func (a *App) LoadConfiguration(cfgFile string, storeKind model.StoreKind) error // these are initialized here so viper can read in configuration from env vars // once https://github.com/spf13/viper/pull/1429 is merged, this can go. a.Config.ServerserviceOptions = &ServerserviceOptions{} - a.Config.NatsOptions = &events.NatsOptions{ - Stream: &events.NatsStreamOptions{}, - Consumer: &events.NatsConsumerOptions{}, - } if cfgFile != "" { fh, err := os.Open(cfgFile) @@ -117,12 +103,6 @@ func (a *App) LoadConfiguration(cfgFile string, storeKind model.StoreKind) error a.envVarAppOverrides() - if a.Config.EventsBorkerKind == "nats" { - if err := a.envVarNatsOverrides(); err != nil { - return errors.Wrap(ErrConfig, "nats env overrides error:"+err.Error()) - } - } - if storeKind == model.InventoryStoreServerservice { if err := a.envVarServerserviceOverrides(); err != nil { return errors.Wrap(ErrConfig, "serverservice env overrides error:"+err.Error()) @@ -165,90 +145,29 @@ func (a *App) envBindVars() error { return nil } -// NATs streaming configuration -var ( - defaultNatsConnectTimeout = 100 * time.Millisecond -) - -// nolint:gocyclo // nats env config load is cyclomatic -func (a *App) envVarNatsOverrides() error { - if a.Config.NatsOptions == nil { - a.Config.NatsOptions = &events.NatsOptions{} - } - +func (a *App) NatsParams() (nurl, credsFile string, connectTimeout time.Duration, err error) { if a.v.GetString("nats.url") != "" { - a.Config.NatsOptions.URL = a.v.GetString("nats.url") - } - - if a.Config.NatsOptions.URL == "" { - return errors.New("missing parameter: nats.url") - } - - if a.v.GetString("nats.publisherSubjectPrefix") != "" { - a.Config.NatsOptions.PublisherSubjectPrefix = a.v.GetString("nats.publisherSubjectPrefix") - } - - if a.Config.NatsOptions.PublisherSubjectPrefix == "" { - return errors.New("missing parameter: nats.publisherSubjectPrefix") - } - - if a.v.GetString("nats.stream.user") != "" { - a.Config.NatsOptions.StreamUser = a.v.GetString("nats.stream.user") + nurl = a.v.GetString("nats.url") } - if a.v.GetString("nats.stream.pass") != "" { - a.Config.NatsOptions.StreamPass = a.v.GetString("nats.stream.pass") + if nurl == "" { + return "", "", 0, errors.New("missing parameter: nats.url") } if a.v.GetString("nats.creds.file") != "" { - a.Config.NatsOptions.CredsFile = a.v.GetString("nats.creds.file") - } - - if a.v.GetString("nats.stream.name") != "" { - if a.Config.NatsOptions.Stream == nil { - a.Config.NatsOptions.Stream = &events.NatsStreamOptions{} - } - - a.Config.NatsOptions.Stream.Name = a.v.GetString("nats.stream.name") - } - - if a.Config.NatsOptions.Stream.Name == "" { - return errors.New("A stream name is required") - } - - if a.v.GetString("nats.consumer.name") != "" { - if a.Config.NatsOptions.Consumer == nil { - a.Config.NatsOptions.Consumer = &events.NatsConsumerOptions{} - } - - a.Config.NatsOptions.Consumer.Name = a.v.GetString("nats.consumer.name") - } - - if len(a.v.GetStringSlice("nats.consumer.subscribeSubjects")) != 0 { - a.Config.NatsOptions.Consumer.SubscribeSubjects = a.v.GetStringSlice("nats.consumer.subscribeSubjects") - } - - if len(a.Config.NatsOptions.Consumer.SubscribeSubjects) == 0 { - return errors.New("missing parameter: nats.consumer.subscribeSubjects") + credsFile = a.v.GetString("nats.creds.file") } - if a.v.GetString("nats.consumer.filterSubject") != "" { - a.Config.NatsOptions.Consumer.FilterSubject = a.v.GetString("nats.consumer.filterSubject") - } - - if a.Config.NatsOptions.Consumer.FilterSubject == "" { - return errors.New("missing parameter: nats.consumer.filterSubject") + if credsFile == "" { + return "", "", 0, errors.New("missing parameter: nats.creds.file") } + connectTimeout = defaultNatsConnectTimeout if a.v.GetDuration("nats.connect.timeout") != 0 { - a.Config.NatsOptions.ConnectTimeout = a.v.GetDuration("nats.connect.timeout") + connectTimeout = a.v.GetDuration("nats.connect.timeout") } - if a.Config.NatsOptions.ConnectTimeout == 0 { - a.Config.NatsOptions.ConnectTimeout = defaultNatsConnectTimeout - } - - return nil + return nurl, credsFile, connectTimeout, nil } // Server service configuration options diff --git a/internal/statemachine/task.go b/internal/statemachine/task.go index 41cb193f..e9333996 100644 --- a/internal/statemachine/task.go +++ b/internal/statemachine/task.go @@ -7,7 +7,9 @@ import ( "github.com/metal-toolbox/flasher/internal/store" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "go.hollow.sh/toolbox/events/registry" + + rctypes "github.com/metal-toolbox/rivets/condition" + "github.com/metal-toolbox/rivets/events/controller" ) var ( @@ -20,6 +22,25 @@ type Publisher interface { Publish(ctx *HandlerContext) } +// StatusPublisher implements the Publisher interface +// to wrap the condition controller publish method +type StatusPublisher struct { + cp controller.ConditionStatusPublisher +} + +func NewNatsConditionStatusPublisher(cp controller.ConditionStatusPublisher) Publisher { + return &StatusPublisher{cp} +} + +func (s *StatusPublisher) Publish(smCtx *HandlerContext) { + s.cp.Publish( + smCtx.Ctx, + smCtx.Asset.ID.String(), + rctypes.State(smCtx.Task.State()), + smCtx.Task.Status.MustMarshal(), + ) +} + // HandlerContext holds references to objects required to complete firmware install task and action transitions. // // The HandlerContext is passed to every transition handler. @@ -57,7 +78,7 @@ type HandlerContext struct { Logger *logrus.Entry // WorkerID is the identifier for the worker executing this task. - WorkerID registry.ControllerID + WorkerID string // ActionStateMachines are sub-statemachines of this Task // each firmware applicable has a Action statemachine that is @@ -70,7 +91,4 @@ type HandlerContext struct { // // It is upto the Action handler implementations to ensure the dry run works as described. Dryrun bool - - // LastRev is the last revision of the status data for this task stored in NATS KV - LastRev uint64 } diff --git a/internal/worker/kv_status.go b/internal/worker/kv_status.go deleted file mode 100644 index 46cfa6e5..00000000 --- a/internal/worker/kv_status.go +++ /dev/null @@ -1,241 +0,0 @@ -//nolint:gomnd,revive //useless opinions -package worker - -import ( - "encoding/json" - "fmt" - "time" - - "github.com/metal-toolbox/flasher/internal/metrics" - sm "github.com/metal-toolbox/flasher/internal/statemachine" - "github.com/metal-toolbox/flasher/types" - "github.com/nats-io/nats.go" - "github.com/sirupsen/logrus" - "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/trace" - - "go.hollow.sh/toolbox/events" - "go.hollow.sh/toolbox/events/pkg/kv" - "go.hollow.sh/toolbox/events/registry" - - rctypes "github.com/metal-toolbox/rivets/condition" -) - -var ( - statusKVName = string(rctypes.FirmwareInstall) - defaultKVOpts = []kv.Option{ - kv.WithDescription("flasher condition status tracking"), - kv.WithTTL(10 * 24 * time.Hour), - } -) - -type statusKVPublisher struct { - kv nats.KeyValue - log *logrus.Logger -} - -// Publish implements the statemachine Publisher interface. -func (s *statusKVPublisher) Publish(hCtx *sm.HandlerContext) { - _, span := otel.Tracer(pkgName).Start( - hCtx.Ctx, - "worker.Publish.KV", - trace.WithSpanKind(trace.SpanKindConsumer), - ) - defer span.End() - - facility := "facility" - if hCtx.Asset.FacilityCode != "" { - facility = hCtx.Asset.FacilityCode - } - key := fmt.Sprintf("%s.%s", facility, hCtx.Task.ID.String()) - payload := statusFromContext(hCtx) - - var err error - var rev uint64 - if hCtx.LastRev == 0 { - rev, err = s.kv.Create(key, payload) - } else { - rev, err = s.kv.Update(key, payload, hCtx.LastRev) - } - - if err != nil { - metrics.NATSError("publish-condition-status") - span.AddEvent("status publish failure", - trace.WithAttributes( - attribute.String("workerID", hCtx.WorkerID.String()), - attribute.String("serverID", hCtx.Asset.ID.String()), - attribute.String("conditionID", hCtx.Task.ID.String()), - attribute.String("error", err.Error()), - ), - ) - s.log.WithError(err).WithFields(logrus.Fields{ - "assetID": hCtx.Asset.ID.String(), - "assetFacilityCode": hCtx.Asset.FacilityCode, - "taskID": hCtx.Task.ID.String(), - "lastRev": hCtx.LastRev, - }).Warn("unable to write task status") - return - } - - s.log.WithFields(logrus.Fields{ - "assetID": hCtx.Asset.ID.String(), - "assetFacilityCode": hCtx.Asset.FacilityCode, - "taskID": hCtx.Task.ID.String(), - "lastRev": hCtx.LastRev, - "key": key, - }).Trace("published task status") - - hCtx.LastRev = rev -} - -func statusFromContext(hCtx *sm.HandlerContext) []byte { - sv := &rctypes.StatusValue{ - WorkerID: hCtx.WorkerID.String(), - Target: hCtx.Asset.ID.String(), - TraceID: trace.SpanFromContext(hCtx.Ctx).SpanContext().TraceID().String(), - SpanID: trace.SpanFromContext(hCtx.Ctx).SpanContext().SpanID().String(), - State: string(hCtx.Task.State()), - Status: hCtx.Task.Status.MustMarshal(), - // ResourceVersion: XXX: the handler context has no concept of this! does this make - // sense at the controller-level? - UpdatedAt: time.Now(), - } - return sv.MustBytes() -} - -func NewStatusKVPublisher(s events.Stream, log *logrus.Logger, opts ...kv.Option) sm.Publisher { - js, ok := s.(*events.NatsJetstream) - if !ok { - log.Fatal("status-kv publisher is only supported on NATS") - } - - kvOpts := defaultKVOpts - kvOpts = append(kvOpts, opts...) - - statusKV, err := kv.CreateOrBindKVBucket(js, statusKVName, kvOpts...) - if err != nil { - log.WithError(err).Fatal("unable to bind status KV bucket") - } - - return &statusKVPublisher{ - kv: statusKV, - log: log, - } -} - -type taskState int - -const ( - notStarted taskState = iota - inProgress // another flasher has started it, is still around and updated recently - complete // task is done - orphaned // the flasher that started this task doesn't exist anymore - indeterminate // we got an error in the process of making the check -) - -//nolint:gocyclo // fun fact: there are no peer-reviewed studies of software quality that support style checking as a benefit -func (o *Worker) taskInProgress(cID string) taskState { - handle, err := events.AsNatsJetStreamContext(o.stream.(*events.NatsJetstream)).KeyValue(statusKVName) - if err != nil { - o.logger.WithError(err).WithFields(logrus.Fields{ - "conditionID": cID, - }).Warn("unable to connect to status KV for condition lookup") - - return indeterminate - } - - lookupKey := fmt.Sprintf("%s.%s", o.facilityCode, cID) - entry, err := handle.Get(lookupKey) - switch err { - case nats.ErrKeyNotFound: - // This should be by far the most common path through this code. - return notStarted - case nil: - break // we'll handle this outside the switch - default: - o.logger.WithError(err).WithFields(logrus.Fields{ - "conditionID": cID, - "lookupKey": lookupKey, - }).Warn("error reading condition status") - - return indeterminate - } - - // we have an status entry for this condition, is is complete? - sv := types.StatusValue{} - if errJson := json.Unmarshal(entry.Value(), &sv); errJson != nil { - o.logger.WithError(errJson).WithFields(logrus.Fields{ - "conditionID": cID, - "lookupKey": lookupKey, - }).Warn("unable to construct a sane status for this condition") - - return indeterminate - } - - if rctypes.State(sv.State) == rctypes.Failed || - rctypes.State(sv.State) == rctypes.Succeeded { - o.logger.WithFields(logrus.Fields{ - "conditionID": cID, - "conditionState": sv.State, - "lookupKey": lookupKey, - }).Info("this condition is already complete") - - return complete - } - - // is the worker handling this condition alive? - worker, err := registry.ControllerIDFromString(sv.WorkerID) - if err != nil { - o.logger.WithError(err).WithFields(logrus.Fields{ - "conditionID": cID, - "workerID": sv.WorkerID, - }).Warn("bad worker id") - - return indeterminate - } - - activeAt, err := registry.LastContact(worker) - switch err { - case nats.ErrKeyNotFound: - // the data for this worker aged-out, it's no longer active - // XXX: the most conservative thing to do here is to return - // indeterminate but most times this will indicate that the - // worker crashed/restarted and this task should be restarted. - o.logger.WithFields(logrus.Fields{ - "conditionID": cID, - "workerID": sv.WorkerID, - }).Info("original worker not found") - - // We're going to restart this condition when we return from - // this function. Use the KV handle we have to delete the - // existing task key. - if err = handle.Delete(lookupKey); err != nil { - o.logger.WithError(err).WithFields(logrus.Fields{ - "conditionID": cID, - "workerID": sv.WorkerID, - "lookupKey": lookupKey, - }).Warn("unable to delete existing condition status") - - return indeterminate - } - - return orphaned - case nil: - timeStr, _ := activeAt.MarshalText() - o.logger.WithError(err).WithFields(logrus.Fields{ - "conditionID": cID, - "workerID": sv.WorkerID, - "lastActive": timeStr, - }).Warn("error looking up worker last contact") - - return inProgress - default: - o.logger.WithError(err).WithFields(logrus.Fields{ - "conditionID": cID, - "workerID": sv.WorkerID, - }).Warn("error looking up worker last contact") - - return indeterminate - } -} diff --git a/internal/worker/kv_status_test.go b/internal/worker/kv_status_test.go deleted file mode 100644 index ebcba4a2..00000000 --- a/internal/worker/kv_status_test.go +++ /dev/null @@ -1,189 +0,0 @@ -package worker - -import ( - "context" - "encoding/json" - "fmt" - "os" - "testing" - "time" - - "github.com/google/uuid" - "github.com/metal-toolbox/flasher/internal/model" - sm "github.com/metal-toolbox/flasher/internal/statemachine" - "github.com/metal-toolbox/flasher/types" - "github.com/nats-io/nats-server/v2/server" - srvtest "github.com/nats-io/nats-server/v2/test" - "github.com/nats-io/nats.go" - "github.com/sirupsen/logrus" - "github.com/stretchr/testify/require" - "go.hollow.sh/toolbox/events" - "go.hollow.sh/toolbox/events/pkg/kv" - "go.hollow.sh/toolbox/events/registry" - - rctypes "github.com/metal-toolbox/rivets/condition" -) - -func startJetStreamServer(t *testing.T) *server.Server { - t.Helper() - opts := srvtest.DefaultTestOptions - opts.Port = -1 - opts.JetStream = true - return srvtest.RunServer(&opts) -} - -func jetStreamContext(t *testing.T, s *server.Server) (*nats.Conn, nats.JetStreamContext) { - t.Helper() - nc, err := nats.Connect(s.ClientURL()) - if err != nil { - t.Fatalf("connect => %v", err) - } - js, err := nc.JetStream(nats.MaxWait(10 * time.Second)) - if err != nil { - t.Fatalf("JetStream => %v", err) - } - return nc, js -} - -func shutdownJetStream(t *testing.T, s *server.Server) { - t.Helper() - var sd string - if config := s.JetStreamConfig(); config != nil { - sd = config.StoreDir - } - s.Shutdown() - if sd != "" { - if err := os.RemoveAll(sd); err != nil { - t.Fatalf("Unable to remove storage %q: %v", sd, err) - } - } - s.WaitForShutdown() -} - -func TestPublisher(t *testing.T) { - srv := startJetStreamServer(t) - defer shutdownJetStream(t, srv) - nc, js := jetStreamContext(t, srv) // nc is closed on evJS.Close(), js needs no cleanup - evJS := events.NewJetstreamFromConn(nc) - defer evJS.Close() - - pub := NewStatusKVPublisher(evJS, logrus.New(), kv.WithReplicas(1)) - require.NotNil(t, pub, "publisher constructor") - - readHandle, err := js.KeyValue("firmwareInstall") - require.NoError(t, err, "read handle") - - taskID := uuid.New() - assetID := uuid.New() - - testContext := &sm.HandlerContext{ - Ctx: context.TODO(), - Task: &model.Task{ - ID: taskID, - Status: model.NewTaskStatusRecord("some-status"), - }, - WorkerID: registry.GetID("kvtest"), - Asset: &model.Asset{ - ID: assetID, - FacilityCode: "fac13", - }, - } - testContext.Task.SetState(model.StatePending) - require.NotPanics(t, func() { pub.Publish(testContext) }, "publish initial") - require.NotEqual(t, 0, testContext.LastRev, "last rev - 1") - - entry, err := readHandle.Get("fac13." + taskID.String()) - require.Equal(t, entry.Revision(), testContext.LastRev, "last rev - 2") - - sv := &types.StatusValue{} - err = json.Unmarshal(entry.Value(), sv) - require.NoError(t, err, "unmarshal") - - require.Equal(t, types.Version, sv.MsgVersion, "version check") - require.Equal(t, assetID.String(), sv.Target, "sv Target") - - require.Contains(t, string(sv.Status), "some-status", "sv Status") - - testContext.Task.SetState(model.StateActive) - require.NotPanics(t, func() { pub.Publish(testContext) }, "publish revision") - - entry, err = readHandle.Get("fac13." + taskID.String()) - require.Equal(t, entry.Revision(), testContext.LastRev, "last rev - 3") -} - -func TestTaskInProgress(t *testing.T) { - srv := startJetStreamServer(t) - defer shutdownJetStream(t, srv) - nc, js := jetStreamContext(t, srv) - evJS := events.NewJetstreamFromConn(nc) - defer evJS.Close() - - // set up the fake status KV - cfg := &nats.KeyValueConfig{ - Bucket: string(rctypes.FirmwareInstall), - } - writeHandle, err := js.CreateKeyValue(cfg) - require.NoError(t, err, "creating KV") - - worker := Worker{ - stream: evJS, - logger: &logrus.Logger{ - Formatter: &logrus.JSONFormatter{}, - }, - facilityCode: "test1", - } - - conditionID := uuid.New() - key := fmt.Sprintf("test1.%s", conditionID.String()) - - // first scenario: nothing in the KV - val := worker.taskInProgress(conditionID.String()) - require.Equal(t, notStarted, val, "empty KV test") - - // write a non StatusValue to the KV - _, err = writeHandle.Put(key, []byte("non-status-value")) - val = worker.taskInProgress(conditionID.String()) - require.Equal(t, indeterminate, val, "bad status value") - - // write a failed StatusValue - sv := &types.StatusValue{ - State: "failed", - } - _, err = writeHandle.Put(key, sv.MustBytes()) - require.NoError(t, err, "finished status value") - val = worker.taskInProgress(conditionID.String()) - require.Equal(t, complete, val, "failed status") - - sv.WorkerID = "some junk id" - sv.State = "pending" - - _, err = writeHandle.Put(key, sv.MustBytes()) - require.NoError(t, err, "update status value to pending") - val = worker.taskInProgress(conditionID.String()) - require.Equal(t, indeterminate, val, "bogus worker id") - - // initialize the registry before we do anything else - err = registry.InitializeRegistryWithOptions(evJS, kv.WithReplicas(1)) - require.NoError(t, err, "initialize registry") - - flasherID := registry.GetID("other-flasher") - err = registry.RegisterController(flasherID) - require.NoError(t, err, "register test flasher") - - sv.WorkerID = flasherID.String() - - _, err = writeHandle.Put(key, sv.MustBytes()) - require.NoError(t, err, "update status value to pending") - val = worker.taskInProgress(conditionID.String()) - require.Equal(t, inProgress, val, "pending status") - - err = registry.DeregisterController(flasherID) - require.NoError(t, err, "deregister test flasher") - - val = worker.taskInProgress(conditionID.String()) - require.Equal(t, orphaned, val, "no live workers") - - // make sure that the Delete fired to clear the KV and make things clean for a new worker - _, err = writeHandle.Get(key) - require.ErrorIs(t, err, nats.ErrKeyNotFound) -} diff --git a/internal/worker/liveness.go b/internal/worker/liveness.go deleted file mode 100644 index 3f80a650..00000000 --- a/internal/worker/liveness.go +++ /dev/null @@ -1,96 +0,0 @@ -package worker - -import ( - "context" - "sync" - "time" - - "go.hollow.sh/toolbox/events" - "go.hollow.sh/toolbox/events/pkg/kv" - "go.hollow.sh/toolbox/events/registry" - - "github.com/metal-toolbox/flasher/internal/metrics" - "github.com/nats-io/nats.go" - "github.com/pkg/errors" -) - -var ( - once sync.Once - checkinCadence = 30 * time.Second - livenessTTL = 3 * time.Minute -) - -// This starts a go-routine to peridocally check in with the NATS kv -func (w *Worker) startWorkerLivenessCheckin(ctx context.Context) { - once.Do(func() { - w.id = registry.GetID(w.name) - natsJS, ok := w.stream.(*events.NatsJetstream) - if !ok { - w.logger.Error("Non-NATS stores are not supported for worker-liveness") - return - } - - opts := []kv.Option{ - kv.WithTTL(livenessTTL), - } - - // any setting of replicas (even 1) chokes NATS in non-clustered mode - if w.replicaCount != 1 { - opts = append(opts, kv.WithReplicas(w.replicaCount)) - } - - if err := registry.InitializeRegistryWithOptions(natsJS, opts...); err != nil { - metrics.NATSError("initialize liveness registry") - w.logger.WithError(err).Error("unable to initialize active worker registry") - return - } - - go w.checkinRoutine(ctx) - }) -} - -func (w *Worker) checkinRoutine(ctx context.Context) { - if err := registry.RegisterController(w.id); err != nil { - w.logger.WithError(err).Warn("unable to do initial worker liveness registration") - } - - tick := time.NewTicker(checkinCadence) - defer tick.Stop() - - var stop bool - for !stop { - select { - case <-tick.C: - err := registry.ControllerCheckin(w.id) - if err != nil { - w.logger.WithError(err). - WithField("id", w.id.String()). - Warn("worker checkin failed") - metrics.NATSError("liveness checkin") - if err = refreshWorkerToken(w.id); err != nil { - w.logger.WithError(err). - WithField("id", w.id.String()). - Fatal("unable to refresh worker liveness token") - } - } - case <-ctx.Done(): - w.logger.Info("liveness check-in stopping on done context") - stop = true - } - } -} - -// try to de-register/re-register this id. -func refreshWorkerToken(id registry.ControllerID) error { - err := registry.DeregisterController(id) - if err != nil && !errors.Is(err, nats.ErrKeyNotFound) { - metrics.NATSError("liveness refresh: de-register") - return err - } - err = registry.RegisterController(id) - if err != nil { - metrics.NATSError("liveness referesh: register") - return err - } - return nil -} diff --git a/internal/worker/task_handler.go b/internal/worker/task_handler.go index d8a7f6c5..7b89da68 100644 --- a/internal/worker/task_handler.go +++ b/internal/worker/task_handler.go @@ -18,7 +18,6 @@ import ( "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" "github.com/sirupsen/logrus" - "go.hollow.sh/toolbox/events/registry" ) var ( @@ -38,7 +37,7 @@ type handler struct { func newHandler( ctx context.Context, dryrun bool, - workerID registry.ControllerID, + workerID string, facilityCode string, task *model.Task, asset *model.Asset, diff --git a/internal/worker/task_handler_test.go b/internal/worker/task_handler_test.go index b3cef04b..184ac7d3 100644 --- a/internal/worker/task_handler_test.go +++ b/internal/worker/task_handler_test.go @@ -8,10 +8,10 @@ import ( "github.com/metal-toolbox/flasher/internal/fixtures" "github.com/metal-toolbox/flasher/internal/model" sm "github.com/metal-toolbox/flasher/internal/statemachine" + "github.com/metal-toolbox/rivets/events/registry" "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "go.hollow.sh/toolbox/events/registry" "go.uber.org/mock/gomock" bconsts "github.com/bmc-toolbox/bmclib/v2/constants" @@ -126,7 +126,7 @@ func TestRemoveFirmwareAlreadyAtDesiredVersion(t *testing.T) { Task: &model.Task{ ID: serverID, // it just needs to be a UUID }, - WorkerID: registry.GetID("test-app"), + WorkerID: registry.GetID("test-app").String(), } expected := []*model.Firmware{ { @@ -207,7 +207,7 @@ func TestPlanInstall1(t *testing.T) { ResetBMCBeforeInstall: true, }, }, - WorkerID: registry.GetID("test-app"), + WorkerID: registry.GetID("test-app").String(), DeviceQueryor: q, } @@ -293,7 +293,7 @@ func TestPlanInstall2(t *testing.T) { ForceInstall: true, }, }, - WorkerID: registry.GetID("test-app"), + WorkerID: registry.GetID("test-app").String(), DeviceQueryor: q, } diff --git a/internal/worker/worker.go b/internal/worker/worker.go index cb9e5f14..de4d55fe 100644 --- a/internal/worker/worker.go +++ b/internal/worker/worker.go @@ -3,32 +3,21 @@ package worker import ( "context" "encoding/json" - "os" - "strconv" "sync" - "sync/atomic" "time" "github.com/google/uuid" - "github.com/metal-toolbox/flasher/internal/metrics" "github.com/metal-toolbox/flasher/internal/model" "github.com/metal-toolbox/flasher/internal/runner" + sm "github.com/metal-toolbox/flasher/internal/statemachine" "github.com/metal-toolbox/flasher/internal/store" "github.com/metal-toolbox/flasher/internal/version" - "github.com/nats-io/nats.go" "github.com/pkg/errors" - "github.com/prometheus/client_golang/prometheus" "github.com/sirupsen/logrus" - "go.hollow.sh/toolbox/events" - "go.hollow.sh/toolbox/events/pkg/kv" - "go.hollow.sh/toolbox/events/registry" "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/trace" - cpv1types "github.com/metal-toolbox/conditionorc/pkg/api/v1/types" - sm "github.com/metal-toolbox/flasher/internal/statemachine" rctypes "github.com/metal-toolbox/rivets/condition" + "github.com/metal-toolbox/rivets/events/controller" ) const ( @@ -47,440 +36,147 @@ var ( // This value should be set to less than the event stream Ack timeout value. taskInprogressTick = 3 * time.Minute - errConditionDeserialize = errors.New("unable to deserialize condition") - errTaskFirmwareParam = errors.New("error in task firmware parameters") - errInitTask = errors.New("error initializing new task from event") + errTaskFirmwareParam = errors.New("error in task firmware parameters") + errInitTask = errors.New("error initializing new task from condition") ) -// Worker holds attributes for firmware install routines. -type Worker struct { - stream events.Stream +type ConditionTaskHandler struct { store store.Repository syncWG *sync.WaitGroup logger *logrus.Logger - name string - id registry.ControllerID // assigned when this worker registers itself facilityCode string - concurrency int - dispatched int32 + controllerID string dryrun bool faultInjection bool - useStatusKV bool - replicaCount int } // NewOutofbandWorker returns a out of band firmware install worker instance -func New( - facilityCode string, +func Run( + ctx context.Context, dryrun, - useStatusKV, faultInjection bool, - concurrency, - replicaCount int, - stream events.Stream, repository store.Repository, + nc *controller.NatsController, logger *logrus.Logger, -) *Worker { - id, _ := os.Hostname() - - return &Worker{ - name: id, - facilityCode: facilityCode, - dryrun: dryrun, - useStatusKV: useStatusKV, - faultInjection: faultInjection, - concurrency: concurrency, - replicaCount: replicaCount, - syncWG: &sync.WaitGroup{}, - stream: stream, - store: repository, - logger: logger, - } -} - -// Run runs the firmware install worker which listens for events to action. -func (o *Worker) Run(ctx context.Context) { - tickerFetchEvents := time.NewTicker(fetchEventsInterval).C - - if err := o.stream.Open(); err != nil { - o.logger.WithError(err).Error("event stream connection error") - return - } - - // returned channel ignored, since this is a Pull based subscription. - _, err := o.stream.Subscribe(ctx) - if err != nil { - o.logger.WithError(err).Error("event stream subscription error") - return - } - - o.logger.Info("connected to event stream.") - - o.startWorkerLivenessCheckin(ctx) +) { + ctx, span := otel.Tracer(pkgName).Start( + ctx, + "Run", + ) + defer span.End() v := version.Current() - o.logger.WithFields( + logger.WithFields( logrus.Fields{ - "version": v.AppVersion, - "commit": v.GitCommit, - "branch": v.GitBranch, - "replica-count": o.replicaCount, - "concurrency": o.concurrency, - "dry-run": o.dryrun, - "fault-injection": o.faultInjection, + "version": v.AppVersion, + "commit": v.GitCommit, + "branch": v.GitBranch, + "dry-run": dryrun, + "faultInjection": faultInjection, }, ).Info("flasher worker running") -Loop: - for { - select { - case <-tickerFetchEvents: - if o.concurrencyLimit() { - continue - } - - o.processEvents(ctx) - - case <-ctx.Done(): - if o.dispatched > 0 { - continue - } - - break Loop + handlerFactory := func() controller.ConditionHandler { + return &ConditionTaskHandler{ + store: repository, + syncWG: &sync.WaitGroup{}, + logger: logger, + dryrun: dryrun, + faultInjection: faultInjection, + facilityCode: nc.FacilityCode(), + controllerID: nc.ID(), } } -} - -func (o *Worker) processEvents(ctx context.Context) { - // XXX: consider having a separate context for message retrieval - msgs, err := o.stream.PullMsg(ctx, 1) - switch { - case err == nil: - case errors.Is(err, nats.ErrTimeout): - o.logger.WithFields( - logrus.Fields{"err": err.Error()}, - ).Trace("no new events") - default: - o.logger.WithFields( - logrus.Fields{"err": err.Error()}, - ).Warn("retrieving new messages") - metrics.NATSError("pull-msg") - } - - for _, msg := range msgs { - if ctx.Err() != nil || o.concurrencyLimit() { - o.eventNak(msg) - - return - } - - // spawn msg process handler - o.syncWG.Add(1) - - go func(msg events.Message) { - defer o.syncWG.Done() - - atomic.AddInt32(&o.dispatched, 1) - defer atomic.AddInt32(&o.dispatched, -1) - - o.processSingleEvent(ctx, msg) - }(msg) - } -} - -func (o *Worker) concurrencyLimit() bool { - return int(o.dispatched) >= o.concurrency -} - -func (o *Worker) eventAckInProgress(event events.Message) { - if err := event.InProgress(); err != nil { - metrics.NATSError("ack-in-progress") - o.logger.WithError(err).Warn("event Ack Inprogress error") - } -} - -func (o *Worker) eventAckComplete(event events.Message) { - if err := event.Ack(); err != nil { - metrics.NATSError("ack") - o.logger.WithError(err).Warn("event Ack error") - } -} - -func (o *Worker) eventNak(event events.Message) { - if err := event.Nak(); err != nil { - metrics.NATSError("nak") - o.logger.WithError(err).Warn("event Nak error") - } -} - -func newTask(conditionID uuid.UUID, params *rctypes.FirmwareInstallTaskParameters) (model.Task, error) { - task := model.Task{ - ID: conditionID, - Parameters: *params, - Status: model.NewTaskStatusRecord("initialized task"), - } - - //nolint:errcheck // this method returns nil unconditionally - task.SetState(model.StatePending) - - if len(params.Firmwares) > 0 { - task.Parameters.Firmwares = params.Firmwares - task.FirmwarePlanMethod = model.FromRequestedFirmware - - return task, nil - } - - if params.FirmwareSetID != uuid.Nil { - task.Parameters.FirmwareSetID = params.FirmwareSetID - task.FirmwarePlanMethod = model.FromFirmwareSet - return task, nil + if err := nc.ListenEvents(ctx, handlerFactory); err != nil { + logger.Fatal(err) } - - return task, errors.Wrap(errTaskFirmwareParam, "no firmware list or firmwareSetID specified") } -func (o *Worker) registerEventCounter(valid bool, response string) { - metrics.EventsCounter.With( - prometheus.Labels{ - "valid": strconv.FormatBool(valid), - "response": response, - }).Inc() -} - -func (o *Worker) processSingleEvent(ctx context.Context, e events.Message) { - // extract parent trace context from the event if any. - ctx = e.ExtractOtelTraceContext(ctx) - - ctx, span := otel.Tracer(pkgName).Start( - ctx, - "worker.processSingleEvent", - // trace.WithSpanKind(trace.SpanKindConsumer), - ) - defer span.End() - - condition, err := conditionFromEvent(e) - if err != nil { - o.logger.WithError(err).WithField( - "subject", e.Subject()).Warn("unable to retrieve condition from message") - - o.registerEventCounter(false, "ack") - o.eventAckComplete(e) - - return - } - - span.SetAttributes(attribute.KeyValue{Key: "conditionKind", Value: attribute.StringValue(condition.ID.String())}) - - // check and see if the task is or has-been handled by another worker - currentState := o.taskInProgress(condition.ID.String()) - switch currentState { - case inProgress: - o.logger.WithField("condition_id", condition.ID.String()).Info("condition is already in progress") - o.eventAckInProgress(e) - metrics.RegisterSpanEvent(span, condition, o.id.String(), "", "ackInProgress") - - return - - case complete: - o.logger.WithField("condition_id", condition.ID.String()).Info("condition is complete") - o.eventAckComplete(e) - metrics.RegisterSpanEvent(span, condition, o.id.String(), "", "ackComplete") - - return - - case orphaned: - o.logger.WithField("condition_id", condition.ID.String()).Warn("restarting this condition") - metrics.RegisterSpanEvent(span, condition, o.id.String(), "", "restarting condition") - - // we need to restart this event - case notStarted: - o.logger.WithField("condition_id", condition.ID.String()).Info("starting new condition") - metrics.RegisterSpanEvent(span, condition, o.id.String(), "", "start new condition") - - // break out here, this is a new event - case indeterminate: - o.logger.WithField("condition_id", condition.ID.String()).Warn("unable to determine state of this condition") - // send it back to NATS to try again - o.eventNak(e) - metrics.RegisterSpanEvent(span, condition, o.id.String(), "", "sent nack, indeterminate state") - - return - } - - task, err := newTaskFromCondition(condition, o.faultInjection) +// Handle implements the controller.ConditionHandler interface +func (h *ConditionTaskHandler) Handle(ctx context.Context, condition *rctypes.Condition, publisher controller.ConditionStatusPublisher) error { + task, err := newTaskFromCondition(condition, h.faultInjection) if err != nil { - o.logger.WithError(err).Warn("error initializing task from condition") - - o.registerEventCounter(false, "ack") - o.eventAckComplete(e) - metrics.RegisterSpanEvent(span, condition, o.id.String(), "", "sent ack, error task init") - - return + return errors.Wrap(errInitTask, err.Error()) } // first try to fetch asset inventory from inventory store - asset, err := o.store.AssetByID(ctx, task.Parameters.AssetID.String()) + asset, err := h.store.AssetByID(ctx, task.Parameters.AssetID.String()) if err != nil { - o.logger.WithFields(logrus.Fields{ - "assetID": task.Parameters.AssetID.String(), - "conditionID": condition.ID, - "err": err.Error(), - }).Warn("asset lookup error") - - o.registerEventCounter(true, "nack") - o.eventNak(e) // have the message bus re-deliver the message - metrics.RegisterSpanEvent( - span, - condition, - o.id.String(), - task.Parameters.AssetID.String(), - "sent nack, store query error", - ) - - return - } - - taskCtx, cancel := context.WithTimeout(ctx, taskTimeout) - defer cancel() - - defer o.registerEventCounter(true, "ack") - defer o.eventAckComplete(e) - metrics.RegisterSpanEvent( - span, - condition, - o.id.String(), - task.Parameters.AssetID.String(), - "sent ack, condition fulfilled", - ) - - o.runTaskWithMonitor(taskCtx, task, asset, e) -} + h.logger.WithFields(logrus.Fields{ + "assetID": task.Parameters.AssetID.String(), + "conditionID": condition.ID, + "controllerID": h.controllerID, + "err": err.Error(), + }).Error("asset lookup error") -func (o *Worker) runTaskWithMonitor(ctx context.Context, task *model.Task, asset *model.Asset, e events.Message) { - // the runTask method is expected to close this channel to indicate its done - doneCh := make(chan bool) - - // monitor sends in progress ack's until the task handler returns. - monitor := func() { - defer o.syncWG.Done() - - ticker := time.NewTicker(taskInprogressTick) - defer ticker.Stop() - - Loop: - for { - select { - case <-ticker.C: - o.eventAckInProgress(e) - case <-doneCh: - break Loop - } - } + return controller.ErrRetryHandler } - o.syncWG.Add(1) - - go monitor() - - o.runTaskHandler(ctx, asset, task, doneCh) - - <-doneCh -} - -func (o *Worker) getStatusPublisher() sm.Publisher { - if o.useStatusKV { - var opts []kv.Option - if o.replicaCount > 1 { - opts = append(opts, kv.WithReplicas(o.replicaCount)) - } - return NewStatusKVPublisher(o.stream, o.logger, opts...) - } - return &statusEmitter{o.stream, o.logger} -} - -func (o *Worker) registerConditionMetrics(startTS time.Time, state string) { - metrics.ConditionRunTimeSummary.With( - prometheus.Labels{ - "condition": string(rctypes.FirmwareInstall), - "state": state, - }, - ).Observe(time.Since(startTS).Seconds()) -} - -func (o *Worker) runTaskHandler(ctx context.Context, asset *model.Asset, task *model.Task, doneCh chan bool) { - defer close(doneCh) - // prepare logger l := logrus.New() - l.Formatter = o.logger.Formatter - l.Level = o.logger.Level + l.Formatter = h.logger.Formatter + l.Level = h.logger.Level hLogger := l.WithFields( logrus.Fields{ - "workerID": o.id.String(), - "conditionID": task.ID.String(), - "assetID": asset.ID.String(), - "bmc": asset.BmcAddress.String(), + "conditionID": condition.ID.String(), + "controllerID": h.controllerID, + "assetID": asset.ID.String(), + "bmc": asset.BmcAddress.String(), }, ) // init handler handler := newHandler( ctx, - o.dryrun, - o.id, - o.facilityCode, + h.dryrun, + h.controllerID, + h.facilityCode, task, asset, - o.store, - o.getStatusPublisher(), + h.store, + sm.NewNatsConditionStatusPublisher(publisher), hLogger, ) // init runner r := runner.New(hLogger) - startTS := time.Now() - o.logger.WithFields(logrus.Fields{ - "deviceID": task.Parameters.AssetID.String(), - "conditionID": task.ID, - }).Info("running task for device") - - // run task handler + hLogger.Info("running task for device") if err := r.RunTask(ctx, task, handler); err != nil { - o.logger.WithFields( - logrus.Fields{ - "deviceID": task.Parameters.AssetID, - "conditionID": task.ID.String(), - "err": err.Error(), - }, - ).Warn("task for device failed") - - o.registerConditionMetrics(startTS, string(rctypes.Failed)) - return + hLogger.WithError(err).Error("task for device failed") + return err } - o.registerConditionMetrics(startTS, string(rctypes.Succeeded)) - - o.logger.WithFields(logrus.Fields{ - "deviceID": task.Parameters.AssetID.String(), - "conditionID": task.ID, - "elapsed": time.Since(startTS).String(), - }).Info("task for device completed") + hLogger.Info("task for device completed") + return nil } -func conditionFromEvent(e events.Message) (*rctypes.Condition, error) { - data := e.Data() - if data == nil { - return nil, errors.New("data field empty") +func newTask(conditionID uuid.UUID, params *rctypes.FirmwareInstallTaskParameters) (model.Task, error) { + task := model.Task{ + ID: conditionID, + Parameters: *params, + Status: model.NewTaskStatusRecord("initialized task"), + } + + //nolint:errcheck // this method returns nil unconditionally + task.SetState(model.StatePending) + + if len(params.Firmwares) > 0 { + task.Parameters.Firmwares = params.Firmwares + task.FirmwarePlanMethod = model.FromRequestedFirmware + + return task, nil } - condition := &rctypes.Condition{} - if err := json.Unmarshal(data, condition); err != nil { - return nil, errors.Wrap(errConditionDeserialize, err.Error()) + if params.FirmwareSetID != uuid.Nil { + task.Parameters.FirmwareSetID = params.FirmwareSetID + task.FirmwarePlanMethod = model.FromFirmwareSet + + return task, nil } - return condition, nil + return task, errors.Wrap(errTaskFirmwareParam, "no firmware list or firmwareSetID specified") } // newTaskFromMsg returns a new task object with the given parameters @@ -501,53 +197,3 @@ func newTaskFromCondition(condition *rctypes.Condition, faultInjection bool) (*m return &task, nil } - -// statusEmitter implements the statemachine.Publisher interface -type statusEmitter struct { - stream events.Stream - logger *logrus.Logger -} - -func (e *statusEmitter) Publish(hCtx *sm.HandlerContext) { - ctx, span := otel.Tracer(pkgName).Start( - hCtx.Ctx, - "worker.Publish.Event", - trace.WithSpanKind(trace.SpanKindConsumer), - ) - defer span.End() - - task := hCtx.Task - update := &cpv1types.ConditionUpdateEvent{ - Kind: rctypes.FirmwareInstall, - ConditionUpdate: cpv1types.ConditionUpdate{ - ConditionID: task.ID, - ServerID: task.Parameters.AssetID, - State: rctypes.State(task.State()), - Status: task.Status.MustMarshal(), - }, - } - - // XXX: This ought to be a method on ConditionUpdate like we have for Condition in - // ConditionOrc - byt, err := json.Marshal(update) - if err != nil { - panic("unable to marshal a condition update" + err.Error()) - } - - if err := e.stream.Publish( - ctx, - string(rctypes.ConditionUpdateEvent), - byt, - ); err != nil { - e.logger.WithError(err).Error("error publishing condition update") - } - - e.logger.WithFields( - logrus.Fields{ - "state": update.ConditionUpdate.State, - "status": update.ConditionUpdate.Status, - "assetID": task.Parameters.AssetID, - "conditionID": task.ID, - }, - ).Trace("condition update published") -}