From 2eedf4f2636bfc3618e222abffdca64daaf7756f Mon Sep 17 00:00:00 2001 From: Siavash Safi Date: Thu, 11 Jun 2026 23:11:49 +0200 Subject: [PATCH 001/120] chore: extract main into an embeddable app package (#5259) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Resolves the long-standing TODO from [#406](https://github.com/prometheus/alertmanager/issues/406) by extracting the Alertmanager process logic out of [`cmd/alertmanager/main.go`](https://github.com/prometheus/alertmanager/blob/main/cmd/alertmanager/main.go) into a new `app` package, and giving it a lifecycle API (`New` / `Start` / `Addr` / `Reload` / `Stop`) so tests and other binaries can embed Alertmanager in-process instead of building and shelling out to the compiled binary. [`cmd/alertmanager/main.go`](https://github.com/prometheus/alertmanager/blob/main/cmd/alertmanager/main.go) shrinks from **722 → 207 lines** and now owns only: kingpin flag parsing, logger construction, `versioncollector` registration, feature-flag / GOMEMLIMIT side effects, and translating OS signals into context cancellation (SIGINT/SIGTERM) plus reload events (SIGHUP) consumed by `app.Run`. ## What this changes ### A reusable `app` package The package is mainly intended for internal use, it can be used by other projects embedding Alertmanager as well. ### A lifecycle API for embedders ```go New(opts) (*App, error) (*App).Start() error (*App).Addr() string // first listener (*App).Addrs() []string // all listeners (*App).Reload(ctx) error (*App).Stop(ctx) error ``` `Run` is a thin wrapper (`New` + `Start` + `serveLoop` + `Stop`) with a deferred `Stop` so cleanup also runs on panic. Listeners are bound at `New` time so `Addr()`/`Addrs()` report the real bound ports (including kernel-assigned `:0`) before serving starts. ### Per-instance state, so multiple instances can coexist The Prometheus collectors that used to be package-level `promauto` variables in [`cmd/alertmanager/main.go`](https://github.com/prometheus/alertmanager/blob/main/cmd/alertmanager/main.go) are now built per `Run()` against `opts.Registerer`, which is threaded through every collaborator (`versioncollector` excepted, which stays process-global in `main.go`). This unblocks running multiple Alertmanager instances in the same process without duplicate-registration panics. ### Deterministic, observable shutdown `setup` registers teardown steps on a LIFO cleanup stack that `Stop` drains in reverse, mirroring Go's `defer` semantics so shutdown ordering follows construction order automatically. Each step is named; `Stop` runs them all, logs any failure by name, and returns the errors joined with the HTTP-shutdown error. The HTTP shutdown honors a single timeout derived from the context passed to `Stop`. ### Isolated config-reload logic The config-scoped subgraph lives in a dedicated `reloader` type. `reloader.reload` performs the stop-old → build-new → wait-for-loading → atomic-swap sequence, and `reloader.stop` tears down the live inhibitor and dispatcher at shutdown. The long-lived singletons (nflog, silences, alerts, cluster peer, API, event recorder, tracing) are constructed once and updated in place on reload (`apih.Update`, `eventRec`/`tracing` `ApplyConfig`) rather than rebuilt. ## Behavioural notes - `prometheus.DefaultRegisterer` is no longer referenced inside `app.Run`; the binary still passes it in via `Options.Registerer`, so on-disk behaviour is identical. - `srv.Shutdown` now actually runs on `Run` exit (previously the deferred `srv.Close` lived inside the listen goroutine and never ran in practice because `os.Exit` killed the process first). Behaviour for the binary is unchanged; embedded callers now get clean HTTP teardown. - Systemd socket activation and `vsock://` listen addresses work under both the binary and embedders; the external URL is derived from the actual bound address. - `tracingManager.Stop` is part of the cleanup stack and always runs, not just on `ctx.Done()`. - `Start`/`Stop` are concurrency-safe and `/-/reload` works in embedded mode (no deadlock without `Run`/`serveLoop`). - `--cluster.listen-address` default moved from a const in `cmd/alertmanager` to the exported `app.DefaultClusterAddr`. ## Known follow-ups (out of scope) - [`matcher/compat.InitFromFlags`](https://github.com/prometheus/alertmanager/blob/main/matcher/compat/parse.go) still mutates package-level state; multi-instance tests with **different** feature flags will collide. Tracked separately. - Migrating the [v2 acceptance harness](https://github.com/prometheus/alertmanager/tree/main/test/with_api_v2) to drive `app.Run` directly instead of building and spawning the binary. Now mechanically possible thanks to `Addr()` / `Stop()` on `*App`; left for a follow-up PR to keep this one reviewable. ## Verification Tests live in [`app`](https://github.com/prometheus/alertmanager/tree/main/app) (lifecycle, listen, options, url, reloader, cluster). Highlights: - **`TestApp_StartStop`** — boot, probe `/-/healthy`, stop, stop again (idempotency). - **`TestApp_TwoSequentialInstances` / `TestApp_TwoConcurrentInstances`** — multiple instances in one process; guards the metrics-per-Registerer behaviour. - **`TestApp_ClusteredStartStop`** — gossip clustering enabled; exercises the peer-dependent paths and `clusterWait`. - **`TestApp_ConcurrentStartStop`** — races `Start` and `Stop` (run under `-race`). - **`TestApp_EmbeddedReloadDoesNotDeadlock`** — `/-/reload` completes in embedded mode. - **`TestApp_New_SetupFailureDoesNotDeadlock`** — setup-failure rollback doesn't block. - **`TestApp_Run_ContextCancel`** — end-to-end `Run` wrapper with ctx cancellation. - **`TestApp_serveLoop` / `TestApp_Stop_AggregatesCleanupErrors`** — serve-loop exits and aggregated shutdown errors. - **`TestReloader_*`** — component swap on reload, error path leaves prior state intact, nil-safe stop. - **`TestListenAll_*` / `TestParseVsockPort` / `TestOptions_Validate` / `TestClusterWait`**. `app` statement coverage is ~84%; the full suite passes under `-race`. Closes #406 Signed-off-by: Siavash Safi --- AGENTS.md | 3 +- app/app.go | 532 ++++++++++++++ app/cluster.go | 29 + app/cluster_test.go | 67 ++ app/lifecycle.go | 314 +++++++++ app/lifecycle_test.go | 468 +++++++++++++ app/listen.go | 96 +++ app/listen_test.go | 116 +++ app/metrics.go | 98 +++ app/options.go | 232 ++++++ app/options_test.go | 121 ++++ app/reloader.go | 263 +++++++ app/reloader_test.go | 173 +++++ app/url.go | 57 ++ .../main_test.go => app/url_test.go | 2 +- cmd/alertmanager/main.go | 659 ++---------------- go.mod | 4 +- 17 files changed, 2643 insertions(+), 591 deletions(-) create mode 100644 app/app.go create mode 100644 app/cluster.go create mode 100644 app/cluster_test.go create mode 100644 app/lifecycle.go create mode 100644 app/lifecycle_test.go create mode 100644 app/listen.go create mode 100644 app/listen_test.go create mode 100644 app/metrics.go create mode 100644 app/options.go create mode 100644 app/options_test.go create mode 100644 app/reloader.go create mode 100644 app/reloader_test.go create mode 100644 app/url.go rename cmd/alertmanager/main_test.go => app/url_test.go (99%) diff --git a/AGENTS.md b/AGENTS.md index 46f3009705..66b494d461 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -16,7 +16,8 @@ Alertmanager handles alerts sent by clients such as Prometheus. It deduplicates, Top‑level packages worth knowing: -- `cmd/alertmanager/` — main binary entry point (`main.go`). +- `cmd/alertmanager/` — main binary entry point (`main.go`); thin wrapper that parses flags and calls `app`. +- `app/` — embeddable Alertmanager runtime extracted from `cmd/alertmanager`. Owns the process lifecycle (`New`/`Start`/`Stop`/`Reload`/`Run`), subsystem wiring (`setup`), config-reload subgraph (`reloader`), listeners and `Options`. Lets tests and other binaries run Alertmanager in‑process. See https://github.com/prometheus/alertmanager/issues/406. - `cmd/amtool/` — CLI for interacting with the Alertmanager API. - `api/` — HTTP API. `api/v2/` is the active API; `api/v1_deprecation_router.go` only returns deprecation responses. - `cli/` — `amtool` command implementations. diff --git a/app/app.go b/app/app.go new file mode 100644 index 0000000000..015a262905 --- /dev/null +++ b/app/app.go @@ -0,0 +1,532 @@ +// Copyright The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package app contains the Alertmanager process logic extracted from +// cmd/alertmanager so that tests and other binaries can embed +// Alertmanager in-process instead of shelling out to a compiled binary. +// See https://github.com/prometheus/alertmanager/issues/406. +package app + +import ( + "context" + "fmt" + "log/slog" + "net" + "net/http" + "os" + "path/filepath" + "strings" + "sync" + "time" + + "github.com/prometheus/common/model" + "github.com/prometheus/common/route" + "github.com/prometheus/common/version" + + "github.com/prometheus/alertmanager/alert" + "github.com/prometheus/alertmanager/api" + "github.com/prometheus/alertmanager/cluster" + "github.com/prometheus/alertmanager/config" + "github.com/prometheus/alertmanager/dispatch" + "github.com/prometheus/alertmanager/eventrecorder" + "github.com/prometheus/alertmanager/eventrecorder/eventrecorderpb" + "github.com/prometheus/alertmanager/httpserver" + "github.com/prometheus/alertmanager/marker" + "github.com/prometheus/alertmanager/nflog" + "github.com/prometheus/alertmanager/notify" + "github.com/prometheus/alertmanager/provider/mem" + "github.com/prometheus/alertmanager/silence" + "github.com/prometheus/alertmanager/tracing" + "github.com/prometheus/alertmanager/ui" +) + +// New wires every Alertmanager subsystem according to opts but does not +// start serving HTTP yet. On error, partial setup is rolled back via the +// same cleanup stack that Stop would drain on success. +func New(opts Options) (*App, error) { + a := &App{ + opts: opts, + serveErrc: make(chan error, 1), + webReload: make(chan chan error), + } + if err := a.setup(); err != nil { + // Roll back partial setup (Stop is idempotent and nil-safe). + _ = a.Stop(context.Background()) + return nil, err + } + return a, nil +} + +// Run starts an Alertmanager instance using opts and blocks until ctx is +// cancelled or an unrecoverable error occurs. It is a thin wrapper over +// New + Start + serveLoop + Stop intended for callers that don't need +// the richer lifecycle API. +// +// The deferred Stop also ensures cleanup runs on panic, matching the +// implicit panic-safety of the original defer-based implementation. +func Run(ctx context.Context, opts Options) error { + a, err := New(opts) + if err != nil { + return err + } + // Stop applies its own shutdownTimeout, so a background context is + // sufficient here; passing a competing deadline would only confuse + // which timeout actually governs the drain. + defer func() { _ = a.Stop(context.Background()) }() + if err := a.Start(); err != nil { + return err + } + return a.serveLoop(ctx) +} + +// App is a running (or runnable) Alertmanager instance built from Options. +// +// Compared to the top-level Run function, App exposes lifecycle hooks +// (Start, Stop, Addr, Reload) so callers — typically tests — can drive an +// instance without OS signals and discover the actually-bound HTTP +// address (useful when listening on ":0"). +// +// Construct an App with New, then call Start to begin serving HTTP. The +// caller is responsible for calling Stop, ideally via a deferred call so +// teardown also runs on panic. An App is single-use: calling Start more +// than once is an error. +type App struct { + opts Options + logger *slog.Logger + + // Lifecycle dependencies retained for use by Start, Reload, and Stop. + coordinator *config.Coordinator + tracingMgr *tracing.Manager + server *http.Server + listeners []net.Listener + + // webReload is the channel exposed by httpserver.Register for the + // /-/reload HTTP endpoint. We read from it in reloadRouter. + webReload chan chan error + + // serveErrc carries errors from the HTTP serve goroutine. It is closed + // when the goroutine exits cleanly. + serveErrc chan error + + // cleanups is the LIFO teardown stack: New (via setup) registers + // cleanups in source order; Stop drains them in reverse so that + // shutdown order mirrors the original `defer` chain in Run. Each + // entry carries a name so Stop can log which step failed and return + // an aggregated error. + cleanups []cleanup + + // mtx serializes Start and Stop so they cannot interleave. An atomic + // flag alone is insufficient: a Stop that observed started==false + // while a concurrent Start had already launched its goroutines (but + // not yet recorded the fact) would skip tearing them down and leak + // them. Holding mtx for the whole body of each method instead means a + // Start racing a Stop either runs entirely before Stop — and is then + // torn down by it — or observes stopped and declines to launch + // anything at all. mtx also guards started, stopped, startErr, stopErr + // and the router channels below. + mtx sync.Mutex + + // started records whether Start launched the serve/reload goroutines; + // stopped records whether Stop has run. Stop uses started to decide + // whether draining serveErrc and tearing down the reload router is + // meaningful — if Start never ran, nothing will ever close serveErrc and + // the drain would deadlock (e.g. during setup-failure rollback). + // Start uses stopped to refuse to launch goroutines after a Stop. + started bool + stopped bool + + // startErr/stopErr memoise the outcome of the first Start/Stop so + // repeated calls are idempotent and return the same result. + startErr error + stopErr error + + // routerQuit signals the reload-routing goroutine (started by Start) + // to exit; routerDone is closed by that goroutine on exit. Both are + // allocated under mtx in Start and only read under mtx in Stop. + routerQuit chan struct{} + routerDone chan struct{} +} + +// setup wires every Alertmanager subsystem and registers their teardown +// hooks on a.cleanups via a.onStop. Stop drains those hooks in LIFO order +// so the shutdown sequence matches the implicit ordering of the original +// defer-based Run implementation. +// +// The config-scoped subgraph (routes, receivers, pipeline, inhibitor and +// dispatcher) — everything rebuilt on reload — lives in the reloader type +// so its subtle swap ordering is isolated and independently testable. +// +// What remains here is the construction of the long-lived singletons. +// It is deliberately one straight-line function rather than a chain of +// helpers: nearly every step depends on locals produced by earlier ones +// (peer, eventRec, silences, alerts, groupMarker, silencer, +// notificationLog, waitFunc, timeoutFunc, ...), so splitting it up would +// only force us to thread a wide state struct between helpers or promote +// those locals to App fields, obscuring the dataflow without simplifying +// anything. The forward dependency order is already enforced by Go's +// variable scoping, and the matching teardown order by the LIFO onStop +// stack drained in Stop. +// +//nolint:gocyclo // intentional, see comment above. +func (a *App) setup() error { + opts := a.opts + if err := opts.validate(); err != nil { + return err + } + + logger := opts.Logger + reg := opts.Registerer + ff := opts.Flagger + m := newMetrics(reg) + + a.logger = logger + + logger.Info("Starting Alertmanager", "version", version.Info()) + startTime := time.Now() + logger.Info("Build context", "build_context", version.BuildContext()) + + if err := os.MkdirAll(opts.DataDir, 0o777); err != nil { + return fmt.Errorf("unable to create data directory: %w", err) + } + + tlsTransportConfig, err := cluster.GetTLSTransportConfig(opts.TLSConfigFile) + if err != nil { + return fmt.Errorf("unable to initialize TLS transport configuration for gossip mesh: %w", err) + } + + var ( + peer *cluster.Peer + // settleCancel cancels the settle context once Settle starts; it + // is a no-op until then so the teardown below can be registered + // immediately after the peer exists. + settleCancel = func() {} + ) + if opts.ClusterBindAddr != "" { + peer, err = cluster.Create( + logger.With("component", "cluster"), + reg, + opts.ClusterBindAddr, + opts.ClusterAdvertiseAddr, + opts.Peers, + true, + opts.PushPullInterval, + opts.GossipInterval, + opts.TCPTimeout, + opts.PeersResolveTimeout, + opts.ProbeTimeout, + opts.ProbeInterval, + tlsTransportConfig, + opts.AllowInsecureAdvertise, + opts.Label, + opts.ClusterPeerName, + ) + if err != nil { + return fmt.Errorf("unable to initialize gossip mesh: %w", err) + } + // Register teardown immediately: a setup step failing between here + // and the Join/Settle block below would otherwise leak the peer's + // sockets and background goroutines. + a.onStop("cluster peer leave", func() error { + settleCancel() + if err := peer.Leave(10 * time.Second); err != nil { + return fmt.Errorf("unable to leave gossip mesh: %w", err) + } + return nil + }) + m.clusterEnabled.Set(1) + } + + stopc := make(chan struct{}) + var wg sync.WaitGroup + + // Load config once for both event recorder initialization and the + // first coordinator apply. Subsequent reloads go through + // configCoordinator.Reload() which reads the file again. + initialConf, err := config.LoadFile(opts.ConfigFile) + if err != nil { + return fmt.Errorf("error loading configuration file: %w", err) + } + + hostname, _ := os.Hostname() + var eventRec eventrecorder.Recorder + if ff.EnableEventRecorder() { + eventRec = eventrecorder.NewRecorderFromConfig(initialConf.EventRecorder, hostname, logger.With("component", "eventrecorder"), reg) + } + a.onStop("event recorder", eventRec.Close) + + recordCtx := eventrecorder.WithEventRecording(context.Background()) + eventRec.RecordEvent(recordCtx, &eventrecorderpb.EventData{ + EventType: &eventrecorderpb.EventData_AlertmanagerStartupEvent{ + AlertmanagerStartupEvent: &eventrecorderpb.AlertmanagerStartupEvent{ + Version: version.Version, + BuildContext: version.BuildContext(), + }, + }, + }) + a.onStop("shutdown event", func() error { + eventRec.RecordEvent(recordCtx, &eventrecorderpb.EventData{ + EventType: &eventrecorderpb.EventData_AlertmanagerShutdownEvent{ + AlertmanagerShutdownEvent: &eventrecorderpb.AlertmanagerShutdownEvent{}, + }, + }) + return nil + }) + + notificationLogOpts := nflog.Options{ + SnapshotFile: filepath.Join(opts.DataDir, "nflog"), + Retention: opts.Retention, + Logger: logger.With("component", "nflog"), + Metrics: reg, + } + notificationLog, err := nflog.New(notificationLogOpts) + if err != nil { + return fmt.Errorf("error creating notification log: %w", err) + } + if peer != nil { + c := peer.AddState("nfl", notificationLog, reg) + notificationLog.SetBroadcast(c.Broadcast) + } + + wg.Go(func() { + notificationLog.Maintenance(opts.MaintenanceInterval, filepath.Join(opts.DataDir, "nflog"), stopc, nil) + }) + + // Register the maintenance teardown as soon as the first maintenance + // goroutine is running. Registering it later (e.g., after silence + // setup) would leak the already-started goroutine(s) if an + // intervening setup step returns an error before the cleanup is + // recorded. close(stopc) stops every maintenance goroutine and + // wg.Wait blocks until they have all exited; both the nflog and + // (subsequently started) silence maintenance goroutines are covered. + a.onStop("maintenance", func() error { + close(stopc) + wg.Wait() + return nil + }) + + groupMarker := marker.NewGroupMarker() + + silenceOpts := silence.Options{ + SnapshotFile: filepath.Join(opts.DataDir, "silences"), + Retention: opts.Retention, + Limits: silence.Limits{ + MaxSilences: func() int { return opts.MaxSilences }, + MaxSilenceSizeBytes: func() int { return opts.MaxSilenceSizeBytes }, + }, + Logger: logger.With("component", "silences"), + Metrics: reg, + Logging: opts.SilenceLogging, + EventRecorder: eventRec, + } + silences, err := silence.New(silenceOpts) + if err != nil { + return fmt.Errorf("error creating silence: %w", err) + } + if peer != nil { + c := peer.AddState("sil", silences, reg) + silences.SetBroadcast(c.Broadcast) + } + + // Start providers before the router potentially sends updates. + wg.Go(func() { + silences.Maintenance(opts.MaintenanceInterval, filepath.Join(opts.DataDir, "silences"), stopc, nil) + }) + + silencer := silence.NewSilencer(silences, logger, eventRec) + + // Peer state listeners have been registered, now we can join and get the initial state. + if peer != nil { + if err := peer.Join(opts.ReconnectInterval, opts.PeerReconnectTimeout); err != nil { + logger.Warn("unable to join gossip mesh", "err", err) + } + settleCtx, cancel := context.WithTimeout(context.Background(), opts.SettleTimeout) + settleCancel = cancel // observed by the teardown registered above. + go peer.Settle(settleCtx, opts.GossipInterval*10) + eventRec.SetClusterPeer(peer) + } + + alerts, err := mem.NewAlerts( + context.Background(), + opts.AlertGCInterval, + opts.PerAlertNameLimit, + silencer, + logger, + eventRec, + reg, + ff, + ) + if err != nil { + return fmt.Errorf("error creating memory provider: %w", err) + } + a.onStop("alerts", func() error { + alerts.Close() + return nil + }) + + // The reloader owns the swappable dispatcher/inhibitor. It is built + // further below (it needs apih, which needs the GroupFunc here), so + // the API's GroupFunc closes over the r variable: it is only invoked + // once the server is serving, long after r is assigned. + var r *reloader + groupFn := func(ctx context.Context, routeFilter func(*dispatch.Route) bool, alertFilter func(*alert.Alert, time.Time) bool) (dispatch.AlertGroups, map[model.Fingerprint][]string, error) { + return r.groups(ctx, routeFilter, alertFilter) + } + + // An interface value that holds a nil concrete value is non-nil. + // Therefore we explicitly pass an empty interface, to detect if the + // cluster is not enabled in notify. + var clusterPeer cluster.ClusterPeer + if peer != nil { + clusterPeer = peer + } + + apih, err := api.New(api.Options{ + Alerts: alerts, + Silences: silences, + GroupMutedFunc: groupMarker.Muted, + Peer: clusterPeer, + Timeout: opts.HTTPTimeout, + Concurrency: opts.GetConcurrency, + Logger: logger.With("component", "api"), + Registry: reg, + RequestDuration: m.requestDuration, + GroupFunc: groupFn, + }) + if err != nil { + return fmt.Errorf("failed to create API: %w", err) + } + + // Bind listeners up front so that Addr/Addrs report concrete bound + // addresses before Start runs and kernel-assigned ":0" ports can be + // discovered by callers. Doing this here (rather than at the end of + // setup) also lets us derive the external URL from the real bound + // address instead of the requested one, which would otherwise carry a + // ":0" port for callers that bind ephemeral ports. + listeners, err := listenAll(opts.WebConfig) + if err != nil { + return err + } + a.listeners = listeners + // Close listeners if setup fails after this point. On a successful + // run server.Shutdown closes them first, so this is then a harmless + // no-op (Close on an already-closed listener just returns an error we + // ignore). + a.onStop("listeners", func() error { + for _, l := range a.listeners { + _ = l.Close() + } + return nil + }) + + amURL, err := extURL(logger, os.Hostname, listeners[0].Addr().String(), opts.ExternalURL) + if err != nil { + return fmt.Errorf("failed to determine external URL: %w", err) + } + logger.Debug("app setup", "external_url", amURL.String()) + + waitFunc := func() time.Duration { return 0 } + if peer != nil { + waitFunc = clusterWait(peer, opts.PeerTimeout) + } + timeoutFunc := func(d time.Duration) time.Duration { + if d < notify.MinTimeout { + d = notify.MinTimeout + } + return d + waitFunc() + } + + tracingManager := tracing.NewManager(logger.With("component", "tracing")) + a.tracingMgr = tracingManager + a.onStop("tracing", func() error { + tracingManager.Stop() + return nil + }) + + configLogger := logger.With("component", "configuration") + configCoordinator := config.NewCoordinator( + opts.ConfigFile, + reg, + configLogger, + ) + a.coordinator = configCoordinator + + // The reloader owns the config-scoped subgraph (templates, routes, + // receivers, pipeline, inhibitor, dispatcher). It rebuilds and swaps + // these on every config apply and stops the live inhibitor+dispatcher + // at shutdown. The long-lived singletons above are updated in place + // (apih.Update, eventRec/tracing ApplyConfig) rather than rebuilt. + r = &reloader{ + alerts: alerts, + apih: apih, + dispatcherMetrics: dispatch.NewDispatcherMetrics(false, reg, ff), + dispatchMaintenanceInterval: opts.DispatchMaintenanceInterval, + dispatchStartDelay: opts.DispatchStartDelay, + eventRecorder: eventRec, + externalURL: amURL, + groupMarker: groupMarker, + logger: logger, + metrics: m, + notificationLog: notificationLog, + peer: peer, + pipelineBuilder: notify.NewPipelineBuilder(reg, ff, eventRec), + retention: opts.Retention, + silencer: silencer, + startTime: startTime, + timeoutFunc: timeoutFunc, + tracingMgr: tracingManager, + waitFunc: waitFunc, + } + a.onStop("dispatcher+inhibitor", r.stop) + + configCoordinator.Subscribe(r.reload) + + if err := configCoordinator.ApplyConfig(initialConf); err != nil { + return fmt.Errorf("failed to apply initial configuration: %w", err) + } + + // Run the tracing manager exactly once. Manager.Run blocks until the + // manager is stopped and only (re)installs the global propagator and + // error handler; ApplyConfig (invoked on every reload above) already + // swaps the tracer provider in place. Starting it per-reload would + // leak a goroutine on each reload. + go tracingManager.Run() + + // Default routePrefix to externalURL path if empty. + routePrefix := opts.RoutePrefix + if routePrefix == "" { + routePrefix = amURL.Path + } + routePrefix = "/" + strings.Trim(routePrefix, "/") + logger.Debug("app setup", "route_prefix", routePrefix) + + router := route.New().WithInstrumentation(m.instrumentHandler) + if routePrefix != "/" { + prefix := routePrefix + router.Get("/", func(w http.ResponseWriter, r *http.Request) { + http.Redirect(w, r, prefix, http.StatusFound) + }) + router = router.WithPrefix(routePrefix) + } + + ui.Register(router) + httpserver.Register(router, a.webReload) + + mux := apih.Register(router, routePrefix) + + a.server = &http.Server{ + // Instrument all handlers with tracing. + Handler: tracing.Middleware(mux), + } + + return nil +} diff --git a/app/cluster.go b/app/cluster.go new file mode 100644 index 0000000000..28538e2d94 --- /dev/null +++ b/app/cluster.go @@ -0,0 +1,29 @@ +// Copyright The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package app + +import ( + "time" + + "github.com/prometheus/alertmanager/cluster" +) + +// clusterWait returns a function that inspects the current peer state and +// returns a duration of one base timeout for each peer with a higher ID +// than ourselves. +func clusterWait(p *cluster.Peer, timeout time.Duration) func() time.Duration { + return func() time.Duration { + return time.Duration(p.Position()) * timeout + } +} diff --git a/app/cluster_test.go b/app/cluster_test.go new file mode 100644 index 0000000000..e4a31aa80b --- /dev/null +++ b/app/cluster_test.go @@ -0,0 +1,67 @@ +// Copyright The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package app + +import ( + "testing" + "time" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/common/promslog" + "github.com/stretchr/testify/require" + + "github.com/prometheus/alertmanager/cluster" +) + +// newTestPeer creates a single-node gossip peer bound to an ephemeral +// port, registering its teardown with t.Cleanup. +func newTestPeer(t *testing.T) *cluster.Peer { + t.Helper() + + peer, err := cluster.Create( + promslog.NewNopLogger(), + prometheus.NewRegistry(), + "127.0.0.1:0", // bind + "", // advertise + nil, // known peers + true, // wait if empty + cluster.DefaultPushPullInterval, + cluster.DefaultGossipInterval, + cluster.DefaultTCPTimeout, + cluster.DefaultResolvePeersTimeout, + cluster.DefaultProbeTimeout, + cluster.DefaultProbeInterval, + nil, // TLS transport config + false, // allow insecure advertise + "", // label + "", // name + ) + require.NoError(t, err) + t.Cleanup(func() { _ = peer.Leave(time.Second) }) + return peer +} + +func TestClusterWait(t *testing.T) { + peer := newTestPeer(t) + + const timeout = 100 * time.Millisecond + wait := clusterWait(peer, timeout) + + // A freshly created single-node peer has position 0, so its wait is + // zero base timeouts; in all cases it is a non-negative multiple of + // the base timeout. + got := wait() + require.GreaterOrEqual(t, got, time.Duration(0)) + require.Equal(t, time.Duration(peer.Position())*timeout, got) +} diff --git a/app/lifecycle.go b/app/lifecycle.go new file mode 100644 index 0000000000..1be4aaa6cd --- /dev/null +++ b/app/lifecycle.go @@ -0,0 +1,314 @@ +// Copyright The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package app + +import ( + "context" + "errors" + "fmt" + "net/http" + "slices" + "time" + + "github.com/prometheus/exporter-toolkit/web" +) + +// shutdownTimeout bounds the graceful-teardown phase of Stop: the HTTP +// server drain plus the subsequent waits for the reload router and serve +// goroutine to exit. It applies even when the caller's context never +// cancels (e.g. context.Background()), so Stop always returns in bounded +// time before running the remaining cleanups. +const shutdownTimeout = 5 * time.Second + +// Start begins serving HTTP traffic on the listeners established by New. +// It returns immediately; the listen goroutine signals any error via the +// channel drained by serveLoop. Subsequent calls are no-ops. +func (a *App) Start() error { + a.mtx.Lock() + defer a.mtx.Unlock() + + if a.started { + // Idempotent: report the outcome of the first call. + return a.startErr + } + if a.stopped { + return errors.New("alertmanager/app: App.Start called after Stop") + } + if a.server == nil || len(a.listeners) == 0 { + a.startErr = errors.New("alertmanager/app: App.Start called before successful New") + return a.startErr + } + + a.routerQuit = make(chan struct{}) + a.routerDone = make(chan struct{}) + + // reloadRouter consumes /-/reload requests and opts.Reload sends so + // they trigger reloads regardless of whether the caller is using + // Run (which also runs serveLoop) or the lifecycle API directly + // (which doesn't). Without this goroutine the /-/reload HTTP + // handler would block forever in embedded mode because its send + // on an unbuffered channel has no receiver. + go a.reloadRouter() + + go func() { + err := web.ServeMultiple(a.listeners, a.server, a.opts.WebConfig, a.logger) + if err != nil && !errors.Is(err, http.ErrServerClosed) { + a.logger.Error("Listen error", "err", err) + a.serveErrc <- err + } + close(a.serveErrc) + }() + + a.started = true + return nil +} + +// reloadRouter forwards reload triggers (HTTP /-/reload and opts.Reload) +// to the config coordinator until routerQuit closes. It is started by +// Start and stopped by Stop after the HTTP server has finished draining, +// so that any in-flight /-/reload handlers can complete their +// send/receive cycle through this goroutine. +func (a *App) reloadRouter() { + defer close(a.routerDone) + // Copy opts.Reload into a local so the select below can nil it out to + // disable that case if an embedder closes it (see the comment there). + reloadCh := a.opts.Reload + for { + select { + case <-a.routerQuit: + return + // opts.Reload is the fire-and-forget trigger (SIGHUP in the + // binary, or a programmatic send by an embedder). There is no + // caller waiting for a result, so reload errors are only logged. + // The channel is embedder-owned and may be closed; the comma-ok + // detects that (a closed channel always reads ready and would + // hot-loop) and disables just this case by nil-ing reloadCh. + case _, ok := <-reloadCh: + if !ok { + reloadCh = nil + continue + } + if err := a.coordinator.Reload(); err != nil { + a.logger.Error("configuration reload failed", "err", err) + } + // webReload is the request/response trigger from the /-/reload + // HTTP handler: it sends a reply channel and blocks for the + // outcome, so we propagate the reload error back over errc + // instead of logging it. This channel is App-owned (allocated in + // New, never closed externally), so it needs no comma-ok guard. + case errc := <-a.webReload: + errc <- a.coordinator.Reload() + } + } +} + +// Addr returns the address of the first bound listener, suitable for +// dialing a single-listener instance (the common case for tests that +// bind ":0"). Use Addrs if configured with multiple listen addresses. +func (a *App) Addr() string { + if len(a.listeners) == 0 { + return "" + } + return a.listeners[0].Addr().String() +} + +// Addrs returns all bound listener addresses in the order given by +// Options.WebConfig.WebListenAddresses. +func (a *App) Addrs() []string { + out := make([]string, len(a.listeners)) + for i, l := range a.listeners { + out[i] = l.Addr().String() + } + return out +} + +// Reload triggers a configuration reload (the programmatic equivalent of +// SIGHUP). Safe to call concurrently with the running App. The reload is +// synchronous and not cancellable, so it takes no context. +// +// It takes mtx for the duration of the reload so it cannot interleave with +// Stop: a reload swaps in (and starts) a new dispatcher/inhibitor, whereas +// Stop's cleanup tears the live ones down. Without this coupling a reload +// racing Stop could start a fresh dispatcher/inhibitor *after* Stop tore +// down the old ones, leaking those goroutines. Holding mtx also lets us +// refuse outright once Stop has begun. (The SIGHUP/HTTP reload paths route +// through reloadRouter, which Stop drains before running cleanups, so they +// are already safe; this guards the directly-callable entry point.) +func (a *App) Reload() error { + a.mtx.Lock() + defer a.mtx.Unlock() + + if a.coordinator == nil { + return errors.New("alertmanager/app: App.Reload called before successful New") + } + if a.stopped { + return errors.New("alertmanager/app: App.Reload called after Stop") + } + return a.coordinator.Reload() +} + +// cleanup is a single named teardown step on the LIFO shutdown stack. +// The name is used purely for logging so operators can see which step +// failed during shutdown. +type cleanup struct { + name string + stop func() error +} + +// Stop gracefully shuts down the App, draining cleanups in reverse +// registration order so that teardown ordering matches the original +// defer chain in Run. Safe to call multiple times; safe to call before +// Start (it will then merely roll back what setup registered). +// +// It returns an aggregated error combining the graceful HTTP shutdown +// failure (if any) with any errors returned by the teardown steps. Each +// failing step is also logged with its name; one failing step does not +// prevent the others from running. +func (a *App) Stop(ctx context.Context) error { + a.mtx.Lock() + defer a.mtx.Unlock() + + if a.stopped { + // Idempotent: report the outcome of the first call. + return a.stopErr + } + a.stopped = true + + // started is read under mtx, paired with the write in Start: holding + // mtx across both bodies guarantees we observe a consistent view (and + // that no Start can launch goroutines after this point). + started := a.started + + var stopErr error + // Bound the whole teardown by shutdownTimeout. Deriving it from ctx + // lets a caller request a faster shutdown via a tighter deadline, but + // the WithTimeout guarantees a finite bound even when ctx never + // cancels on its own — e.g. the context.Background() passed by Run's + // deferred Stop and by New's setup-failure rollback. Using it for the + // waits below (not just Shutdown) is what keeps Stop from hanging on a + // stuck reload or serve goroutine regardless of the caller's ctx. + shutdownCtx, cancel := context.WithTimeout(ctx, shutdownTimeout) + defer cancel() + + // Stop accepting new HTTP traffic first so in-flight handlers + // don't observe collaborators being torn down underneath them. The + // reload router is still running at this point so any in-flight + // /-/reload handler can complete its send/receive cycle and unblock + // Shutdown. + if a.server != nil { + if err := a.server.Shutdown(shutdownCtx); err != nil { + a.logger.Warn("graceful HTTP shutdown failed", "err", err) + stopErr = err + } + } + // HTTP is fully drained; no new /-/reload requests can arrive. + // Terminate the reload router and wait for it to exit before + // running cleanups (Coordinator is among them). The wait is bounded + // in normal operation (the router exits as soon as routerQuit is + // closed), but a stuck coordinator.Reload could block it, so we cap + // it with shutdownCtx. Abandoning the goroutine on timeout is + // acceptable — teardown is best-effort past this point — and we + // surface it in the returned error. + if started { + close(a.routerQuit) + select { + case <-a.routerDone: + case <-shutdownCtx.Done(): + a.logger.Warn("timed out waiting for reload router to exit; abandoning it", "err", shutdownCtx.Err()) + stopErr = errors.Join(stopErr, fmt.Errorf("reload router shutdown: %w", shutdownCtx.Err())) + } + } + // Drain serveErrc so the listen goroutine, if any, exits before we + // release listener resources. ServeMultiple returns once all + // per-listener Serve calls return (which happens once Shutdown + // completes), so this drain is bounded — but, as above, we also cap + // it with shutdownCtx so Stop can't hang here either. + // + // Guard on `started` because serveErrc is allocated in New (so it can + // be non-nil here) but only closed by Start's serve goroutine — + // without this guard, Stop would deadlock when called from New's + // rollback path on setup failure. + if started && a.serveErrc != nil { + drain: + for { + select { + case _, ok := <-a.serveErrc: + if !ok { + break drain + } + case <-shutdownCtx.Done(): + a.logger.Warn("timed out draining serve errors; abandoning the serve goroutine", "err", shutdownCtx.Err()) + stopErr = errors.Join(stopErr, fmt.Errorf("serve drain: %w", shutdownCtx.Err())) + break drain + } + } + } + // Run remaining cleanups in reverse-registration (LIFO) order, + // mirroring Go's `defer` semantics so the in-place transform + // from `defer X` to `a.onStop(X)` in setup is order-preserving. + for _, c := range slices.Backward(a.cleanups) { + if err := c.stop(); err != nil { + a.logger.Warn("teardown step failed", "step", c.name, "err", err) + stopErr = errors.Join(stopErr, fmt.Errorf("%s: %w", c.name, err)) + } + } + a.stopErr = stopErr + return stopErr +} + +// onStop registers a named teardown step to run when Stop is called. +// Cleanups run in LIFO order. Steps return an error only for failures +// worth surfacing to the caller; those that cannot fail return nil. +// +// The cleanups slice is mutated without locking, so registration is +// confined to the single-threaded construction phase (setup, called from +// New before the App is handed to the caller). It is therefore not safe to +// call once the App may be running, i.e. concurrently with Stop. +func (a *App) onStop(name string, fn func() error) { + a.cleanups = append(a.cleanups, cleanup{name: name, stop: fn}) +} + +// serveLoop blocks until ctx is cancelled or an HTTP listener fails. It +// is used by Run only; reload routing is handled by reloadRouter, which +// is started directly from Start so it is also active for embedders that +// drive the App lifecycle without using Run. +func (a *App) serveLoop(ctx context.Context) error { + for { + select { + case <-ctx.Done(): + a.logger.Info("Shutting down gracefully") + // A listener error may have landed on serveErrc at the same + // moment ctx was cancelled; select picks a ready case at + // random, so it could choose ctx.Done() and mask the error. + // Non-blocking drain to surface it rather than reporting a + // clean shutdown over a real failure. + select { + case err, ok := <-a.serveErrc: + if ok { + return fmt.Errorf("alertmanager: HTTP listener failed: %w", err) + } + default: + } + return nil + case err, ok := <-a.serveErrc: + if !ok { + // Channel closed without an error report — the serve + // goroutine exited cleanly (ErrServerClosed). Treat + // this as graceful shutdown. + return nil + } + return fmt.Errorf("alertmanager: HTTP listener failed: %w", err) + } + } +} diff --git a/app/lifecycle_test.go b/app/lifecycle_test.go new file mode 100644 index 0000000000..441c1be9f7 --- /dev/null +++ b/app/lifecycle_test.go @@ -0,0 +1,468 @@ +// Copyright The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package app + +import ( + "context" + "errors" + "io" + "net" + "net/http" + "os" + "path/filepath" + "sync" + "sync/atomic" + "testing" + "time" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/common/promslog" + "github.com/prometheus/exporter-toolkit/web" + "github.com/stretchr/testify/require" + + "github.com/prometheus/alertmanager/config" + "github.com/prometheus/alertmanager/featurecontrol" + "github.com/prometheus/alertmanager/matcher/compat" +) + +const minimalConfig = `route: + receiver: default +receivers: + - name: default +` + +// testOptions returns an Options value that is sufficient to bring up an +// Alertmanager instance bound to an ephemeral port with clustering +// disabled. +func testOptions(t *testing.T) Options { + t.Helper() + + dir := t.TempDir() + configPath := filepath.Join(dir, "alertmanager.yml") + require.NoError(t, os.WriteFile(configPath, []byte(minimalConfig), 0o600)) + + logger := promslog.NewNopLogger() + ff, err := featurecontrol.NewFlags(logger, "") + require.NoError(t, err) + // compat.InitFromFlags mutates package-global state; safe because all + // tests in this package use the same (empty) feature flag set. + compat.InitFromFlags(logger, ff) + + addrs := []string{"127.0.0.1:0"} + systemd := false + webCfg := "" + + // Start from DefaultOptions (clustering disabled by default, which is + // essential when running multiple instances in one process) and only + // override the per-test bits: paths, the ephemeral listener and the + // injected dependencies. + opts := DefaultOptions() + opts.ConfigFile = configPath + opts.DataDir = dir + opts.WebConfig = &web.FlagConfig{ + WebListenAddresses: &addrs, + WebSystemdSocket: &systemd, + WebConfigFile: &webCfg, + } + opts.Logger = logger + opts.Registerer = prometheus.NewRegistry() + opts.Flagger = ff + return opts +} + +// waitHealthy blocks until the instance at addr serves /-/healthy with a +// 200, absorbing the brief window between Start returning and the serve +// goroutine accepting connections. It fails the test if the instance +// never becomes healthy. +func waitHealthy(t *testing.T, addr string) { + t.Helper() + url := "http://" + addr + "/-/healthy" + require.Eventually(t, func() bool { + resp, err := http.Get(url) + if err != nil { + return false + } + _, _ = io.Copy(io.Discard, resp.Body) + _ = resp.Body.Close() + return resp.StatusCode == http.StatusOK + }, 5*time.Second, 50*time.Millisecond, "instance never became healthy") +} + +func TestApp_StartStop(t *testing.T) { + a, err := New(testOptions(t)) + require.NoError(t, err) + require.NoError(t, a.Start()) + + addr := a.Addr() + require.NotEmpty(t, addr, "Addr should be populated after Start") + + waitHealthy(t, addr) + + require.NoError(t, a.Stop(t.Context())) + + // Stop is idempotent. + require.NoError(t, a.Stop(t.Context())) +} + +func TestApp_ClusteredStartStop(t *testing.T) { + // Bring up an instance with gossip clustering enabled so the + // peer-dependent branches in setup (AddState/Join/Settle/ + // SetClusterPeer/clusterWait), the reloader's cluster-peer pipeline + // wiring, and peer.Leave on shutdown are all exercised. + opts := testOptions(t) + opts.ClusterBindAddr = "127.0.0.1:0" + + a, err := New(opts) + require.NoError(t, err) + require.NoError(t, a.Start()) + + waitHealthy(t, a.Addr()) + + require.NoError(t, a.Stop(t.Context())) +} + +func TestApp_Start_BeforeNewFails(t *testing.T) { + // Start on a zero-value App (no successful New) must error rather + // than launch goroutines against a nil server/listeners. + var a App + require.Error(t, a.Start()) +} + +func TestApp_serveLoop(t *testing.T) { + logger := promslog.NewNopLogger() + + t.Run("listener error is surfaced", func(t *testing.T) { + a := &App{logger: logger, serveErrc: make(chan error, 1)} + a.serveErrc <- errors.New("boom") + err := a.serveLoop(context.Background()) + require.ErrorContains(t, err, "boom") + }) + + t.Run("clean serve goroutine exit", func(t *testing.T) { + a := &App{logger: logger, serveErrc: make(chan error, 1)} + close(a.serveErrc) // serve goroutine exited without an error + require.NoError(t, a.serveLoop(context.Background())) + }) + + t.Run("context cancellation", func(t *testing.T) { + a := &App{logger: logger, serveErrc: make(chan error, 1)} + ctx, cancel := context.WithCancel(context.Background()) + cancel() + require.NoError(t, a.serveLoop(ctx)) + }) +} + +func TestApp_reloadRouterClosedReloadChannel(t *testing.T) { + // A closed Options.Reload channel must not spin reloadRouter into a + // hot loop calling coordinator.Reload on every iteration. We assert + // this deterministically — no sleeps — by counting reloads: a closed + // reloadCh must contribute zero, so after N synchronous /-/reload + // round-trips the counter must equal exactly N. With the hot-loop + // regression it would be far larger. + dir := t.TempDir() + configPath := filepath.Join(dir, "alertmanager.yml") + require.NoError(t, os.WriteFile(configPath, []byte(minimalConfig), 0o600)) + + var reloads atomic.Int64 + coord := config.NewCoordinator(configPath, prometheus.NewRegistry(), promslog.NewNopLogger()) + coord.Subscribe(func(*config.Config) error { + reloads.Add(1) + return nil + }) + + reloadCh := make(chan struct{}) + close(reloadCh) + + a := &App{ + logger: promslog.NewNopLogger(), + opts: Options{Reload: reloadCh}, + coordinator: coord, + routerQuit: make(chan struct{}), + routerDone: make(chan struct{}), + webReload: make(chan chan error), + } + + go a.reloadRouter() + + // Drive N synchronous reloads through the /-/reload path. Each send + // blocks until reloadRouter has run one coordinator.Reload, so these + // are exact, ordered steps with no timing assumptions. + const n = 3 + for range n { + errc := make(chan error) + a.webReload <- errc + require.NoError(t, <-errc) + } + + close(a.routerQuit) + select { + case <-a.routerDone: + case <-time.After(time.Second): + t.Fatal("reloadRouter did not exit after routerQuit closed") + } + + // Read after routerDone (happens-before): the closed reloadCh must + // have triggered no reloads of its own. + require.Equal(t, int64(n), reloads.Load()) +} + +func TestApp_ConcurrentStartStop(t *testing.T) { + // Regression: Start and Stop are serialized by a mutex so they cannot + // interleave. Whatever the order, Stop must never close/receive on a + // nil channel, and a Start losing the race to Stop must not leak its + // goroutines (it observes stopped and declines to launch). Run several + // iterations under -race to shake out the interleavings; the final + // require.NoError below also fails the test if any goroutine is still + // blocked on the router (it would deadlock the second Stop). + for i := range 20 { + a, err := New(testOptions(t)) + require.NoError(t, err, "iteration %d", i) + + var wg sync.WaitGroup + wg.Add(2) + go func() { + defer wg.Done() + _ = a.Start() + }() + go func() { + defer wg.Done() + _ = a.Stop(t.Context()) + }() + wg.Wait() + + // Stop is idempotent; the second call must return cleanly. + require.NoError(t, a.Stop(t.Context()), "iteration %d", i) + } +} + +func TestApp_StartAfterStopFails(t *testing.T) { + // Once Stop has run, Start must refuse to launch its goroutines (which + // would otherwise be leaked, since the only thing that tears them down + // has already run). This is the deterministic half of the + // Start/Stop-race invariant exercised by TestApp_ConcurrentStartStop. + a, err := New(testOptions(t)) + require.NoError(t, err) + + require.NoError(t, a.Stop(t.Context())) + + err = a.Start() + require.Error(t, err) + require.Contains(t, err.Error(), "called after Stop") +} + +func TestApp_TwoSequentialInstances(t *testing.T) { + // Validates that per-instance Registerer + cleanup-stack teardown + // allow constructing a second App in the same process without + // duplicate-registration panics or leaked goroutines. + for i := range 2 { + a, err := New(testOptions(t)) + require.NoError(t, err, "iteration %d", i) + require.NoError(t, a.Start(), "iteration %d", i) + require.NotEmpty(t, a.Addr(), "iteration %d", i) + require.NoError(t, a.Stop(t.Context()), "iteration %d", i) + } +} + +func TestApp_TwoConcurrentInstances(t *testing.T) { + // Two live instances on different ephemeral ports, sharing the + // same process. This exercises the metrics-per-Registerer change + // from Phase A and ensures no shutdown-ordering bugs surface when + // Stop runs on one instance while another is still serving. + a1, err := New(testOptions(t)) + require.NoError(t, err) + require.NoError(t, a1.Start()) + defer func() { _ = a1.Stop(t.Context()) }() + + a2, err := New(testOptions(t)) + require.NoError(t, err) + require.NoError(t, a2.Start()) + defer func() { _ = a2.Stop(t.Context()) }() + + require.NotEqual(t, a1.Addr(), a2.Addr(), "instances should bind distinct ports") +} + +func TestApp_Addrs(t *testing.T) { + // Addrs reports every bound listener address, and Addr returns the + // first of them. Bind two ephemeral ports to make sure both the + // ordering and the count are honoured. + opts := testOptions(t) + addrs := []string{"127.0.0.1:0", "127.0.0.1:0"} + opts.WebConfig.WebListenAddresses = &addrs + + a, err := New(opts) + require.NoError(t, err) + defer func() { _ = a.Stop(t.Context()) }() + + got := a.Addrs() + require.Len(t, got, 2) + for i, addr := range got { + require.NotEmpty(t, addr, "Addrs()[%d] should be a concrete bound address", i) + // Ephemeral ":0" requests must resolve to a concrete port. + require.NotContains(t, addr, ":0", "Addrs()[%d] should not retain the :0 port", i) + } + require.Equal(t, got[0], a.Addr(), "Addr should equal the first bound address") +} + +func TestApp_Reload(t *testing.T) { + // The programmatic Reload re-reads the config through the coordinator + // and must succeed for a valid, unchanged configuration. + a, err := New(testOptions(t)) + require.NoError(t, err) + defer func() { _ = a.Stop(t.Context()) }() + + require.NoError(t, a.Reload()) +} + +func TestApp_Reload_BeforeNewFails(t *testing.T) { + // Calling Reload on a zero-value App (no successful New) must return + // an error rather than panicking on the nil coordinator. + var a App + require.Error(t, a.Reload()) +} + +func TestApp_ReloadAfterStopFails(t *testing.T) { + // Once Stop has run, Reload must refuse: a reload swaps in (and + // starts) a new dispatcher/inhibitor, which would leak past a + // completed Stop. The mtx + stopped guard turns that into an error. + a, err := New(testOptions(t)) + require.NoError(t, err) + require.NoError(t, a.Start()) + require.NoError(t, a.Stop(t.Context())) + + err = a.Reload() + require.Error(t, err) + require.Contains(t, err.Error(), "called after Stop") +} + +func TestApp_Stop_AggregatesCleanupErrors(t *testing.T) { + // Stop should run every teardown step even when some fail, and return + // their errors joined together (named by step). + a := &App{logger: promslog.NewNopLogger()} + var order []string + a.onStop("first", func() error { + order = append(order, "first") + return errors.New("boom-first") + }) + a.onStop("second", func() error { + order = append(order, "second") + return nil + }) + a.onStop("third", func() error { + order = append(order, "third") + return errors.New("boom-third") + }) + + err := a.Stop(context.Background()) + require.Error(t, err) + // LIFO: third runs before second before first. + require.Equal(t, []string{"third", "second", "first"}, order) + require.ErrorContains(t, err, "third: boom-third") + require.ErrorContains(t, err, "first: boom-first") +} + +func TestApp_EmbeddedReloadDoesNotDeadlock(t *testing.T) { + // Regression: when callers use the lifecycle API (New + Start + Stop) + // without Run, the /-/reload HTTP handler must not block forever on + // the unbuffered a.webReload channel. The reload-routing goroutine + // started by Start is the consumer. + a, err := New(testOptions(t)) + require.NoError(t, err) + require.NoError(t, a.Start()) + defer func() { _ = a.Stop(t.Context()) }() + + // Wait until the listener is actually serving so a premature POST + // can't fail with connection refused and masquerade as a deadlock. + waitHealthy(t, a.Addr()) + + type reloadResult struct { + err error + status int + } + resultCh := make(chan reloadResult, 1) + go func() { + resp, err := http.Post("http://"+a.Addr()+"/-/reload", "", nil) + if err != nil { + resultCh <- reloadResult{err: err} + return + } + _, _ = io.Copy(io.Discard, resp.Body) + _ = resp.Body.Close() + resultCh <- reloadResult{status: resp.StatusCode} + }() + select { + case r := <-resultCh: + require.NoError(t, r.err) + require.Equal(t, http.StatusOK, r.status) + case <-time.After(5 * time.Second): + t.Fatal("/-/reload deadlocked in embedded mode") + } +} + +func TestApp_New_SetupFailureDoesNotDeadlock(t *testing.T) { + // Regression: setup failure in New triggers the rollback path which + // calls Stop. Stop must not block draining a.serveErrc because Start has + // not run and nothing will ever close that channel. + errCh := make(chan error, 1) + go func() { + // Empty Options fails validate (Logger required), exercising + // the earliest setup-failure path. + _, err := New(Options{}) + errCh <- err + }() + select { + case err := <-errCh: + require.Error(t, err) + case <-time.After(5 * time.Second): + t.Fatal("New deadlocked on setup-failure rollback") + } +} + +func TestApp_Run_ContextCancel(t *testing.T) { + // Exercises the Run wrapper end-to-end: it must serve, then return + // nil (with cleanup run) once ctx is cancelled. Run is opaque (no + // Addr), so we pin a concrete listen address and wait for the server + // to actually answer /-/healthy before cancelling — a real readiness + // signal instead of a sleep. + opts := testOptions(t) + addr := freeLoopbackAddr(t) + addrs := []string{addr} + opts.WebConfig.WebListenAddresses = &addrs + + ctx, cancel := context.WithCancel(t.Context()) + done := make(chan error, 1) + go func() { done <- Run(ctx, opts) }() + + waitHealthy(t, addr) + cancel() + + select { + case err := <-done: + require.NoError(t, err) + case <-time.After(10 * time.Second): + t.Fatal("Run did not return after ctx cancel") + } +} + +// freeLoopbackAddr reserves an ephemeral loopback port and returns it as a +// host:port string. The listener is closed before returning, so there is a +// small window before the caller rebinds it; this is the standard trade-off +// for tests that need to know an address up front (e.g. to poll readiness). +func freeLoopbackAddr(t *testing.T) string { + t.Helper() + l, err := net.Listen("tcp", "127.0.0.1:0") + require.NoError(t, err) + addr := l.Addr().String() + require.NoError(t, l.Close()) + return addr +} diff --git a/app/listen.go b/app/listen.go new file mode 100644 index 0000000000..c73b2a5362 --- /dev/null +++ b/app/listen.go @@ -0,0 +1,96 @@ +// Copyright The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package app + +import ( + "errors" + "fmt" + "net" + "net/url" + "strconv" + "strings" + + "github.com/coreos/go-systemd/v22/activation" + "github.com/mdlayher/vsock" + "github.com/prometheus/exporter-toolkit/web" +) + +// listenAll eagerly binds every listener described by flags so that the +// bound addresses are known before serving begins (Addr/Addrs can then +// report concrete ports, including those chosen by the kernel for ":0"). +// +// It mirrors the listener selection performed by +// exporter-toolkit/web.ListenAndServe so that the binary keeps support +// for systemd socket activation and vsock addresses after the extraction +// into this package. The resulting listeners are later served by +// web.ServeMultiple in Start. +func listenAll(flags *web.FlagConfig) ([]net.Listener, error) { + if flags.WebSystemdSocket != nil && *flags.WebSystemdSocket { + listeners, err := activation.Listeners() + if err != nil { + return nil, fmt.Errorf("alertmanager/app: systemd socket activation: %w", err) + } + if len(listeners) < 1 { + return nil, errors.New("alertmanager/app: no socket activation file descriptors found") + } + return listeners, nil + } + if flags.WebListenAddresses == nil || len(*flags.WebListenAddresses) == 0 { + return nil, web.ErrNoListeners + } + addrs := *flags.WebListenAddresses + listeners := make([]net.Listener, 0, len(addrs)) + for _, addr := range addrs { + l, err := listenOne(addr) + if err != nil { + for _, prev := range listeners { + _ = prev.Close() + } + return nil, fmt.Errorf("alertmanager/app: listen %q: %w", addr, err) + } + listeners = append(listeners, l) + } + return listeners, nil +} + +// listenOne binds a single listener, honouring the "vsock://" scheme used +// by exporter-toolkit and falling back to a TCP listener otherwise. +func listenOne(address string) (net.Listener, error) { + if strings.HasPrefix(address, "vsock://") { + port, err := parseVsockPort(address) + if err != nil { + return nil, err + } + return vsock.Listen(port, nil) + } + return net.Listen("tcp", address) +} + +// parseVsockPort extracts the port from a "vsock://:{port}" address. It +// matches the parsing in exporter-toolkit/web. +func parseVsockPort(address string) (uint32, error) { + uri, err := url.Parse(address) + if err != nil { + return 0, err + } + _, portStr, err := net.SplitHostPort(uri.Host) + if err != nil { + return 0, err + } + port, err := strconv.ParseUint(portStr, 10, 32) + if err != nil { + return 0, err + } + return uint32(port), nil +} diff --git a/app/listen_test.go b/app/listen_test.go new file mode 100644 index 0000000000..31a636e697 --- /dev/null +++ b/app/listen_test.go @@ -0,0 +1,116 @@ +// Copyright The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package app + +import ( + "net" + "testing" + + "github.com/prometheus/exporter-toolkit/web" + "github.com/stretchr/testify/require" +) + +// freePort binds an ephemeral port, records its address, then releases it +// so callers can reuse the (now free) address. There is an inherent race +// between releasing and rebinding, but it is acceptable for tests. +func freePort(t *testing.T) string { + t.Helper() + l, err := net.Listen("tcp", "127.0.0.1:0") + require.NoError(t, err) + addr := l.Addr().String() + require.NoError(t, l.Close()) + return addr +} + +func tcpFlags(addrs ...string) *web.FlagConfig { + a := addrs + systemd := false + cfg := "" + return &web.FlagConfig{ + WebListenAddresses: &a, + WebSystemdSocket: &systemd, + WebConfigFile: &cfg, + } +} + +func TestListenAll_NoListeners(t *testing.T) { + // Neither systemd activation nor any listen address: should surface + // the toolkit's sentinel error rather than binding anything. + empty := []string{} + _, err := listenAll(&web.FlagConfig{WebListenAddresses: &empty}) + require.ErrorIs(t, err, web.ErrNoListeners) + + _, err = listenAll(&web.FlagConfig{}) + require.ErrorIs(t, err, web.ErrNoListeners) +} + +func TestListenAll_MultipleTCP(t *testing.T) { + listeners, err := listenAll(tcpFlags("127.0.0.1:0", "127.0.0.1:0")) + require.NoError(t, err) + t.Cleanup(func() { + for _, l := range listeners { + _ = l.Close() + } + }) + + require.Len(t, listeners, 2) + require.NotEqual(t, listeners[0].Addr().String(), listeners[1].Addr().String(), + "each listener should bind a distinct ephemeral port") +} + +func TestListenAll_PartialBindIsCleanedUp(t *testing.T) { + // Occupy a port so the *second* address fails to bind, forcing + // listenAll to roll back the first (successfully bound) listener. + occupied, err := net.Listen("tcp", "127.0.0.1:0") + require.NoError(t, err) + defer occupied.Close() + busyAddr := occupied.Addr().String() + + firstAddr := freePort(t) + + listeners, err := listenAll(tcpFlags(firstAddr, busyAddr)) + require.Error(t, err) + require.Nil(t, listeners) + + // The first listener must have been closed on the failure path, so + // its address is bindable again. + l, err := net.Listen("tcp", firstAddr) + require.NoError(t, err, "first listener should have been closed during rollback") + require.NoError(t, l.Close()) +} + +func TestParseVsockPort(t *testing.T) { + for _, tc := range []struct { + name string + address string + want uint32 + wantErr bool + }{ + {name: "valid", address: "vsock://:1234", want: 1234}, + {name: "valid high port", address: "vsock://:65535", want: 65535}, + {name: "missing port", address: "vsock://", wantErr: true}, + {name: "non-numeric port", address: "vsock://:abc", wantErr: true}, + {name: "port overflows uint32", address: "vsock://:4294967296", wantErr: true}, + } { + t.Run(tc.name, func(t *testing.T) { + got, err := parseVsockPort(tc.address) + if tc.wantErr { + require.Error(t, err) + return + } + require.NoError(t, err) + require.Equal(t, tc.want, got) + }) + } +} diff --git a/app/metrics.go b/app/metrics.go new file mode 100644 index 0000000000..e2b69437d8 --- /dev/null +++ b/app/metrics.go @@ -0,0 +1,98 @@ +// Copyright The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package app + +import ( + "net/http" + "time" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/prometheus/client_golang/prometheus/promhttp" +) + +// metrics bundles the process-level Prometheus metrics owned by the app +// package. They used to live as package-level variables in +// cmd/alertmanager/main.go and were registered against +// prometheus.DefaultRegisterer at init time. They are now constructed per +// app.Run invocation against the registerer supplied via Options so that +// multiple instances can coexist within a single process (e.g. tests). +type metrics struct { + requestDuration *prometheus.HistogramVec + responseSize *prometheus.HistogramVec + clusterEnabled prometheus.Gauge + configuredReceivers prometheus.Gauge + configuredIntegrations prometheus.Gauge + configuredInhibitionRules prometheus.Gauge +} + +func newMetrics(reg prometheus.Registerer) *metrics { + f := promauto.With(reg) + return &metrics{ + requestDuration: f.NewHistogramVec( + prometheus.HistogramOpts{ + Name: "alertmanager_http_request_duration_seconds", + Help: "Histogram of latencies for HTTP requests.", + Buckets: prometheus.DefBuckets, + NativeHistogramBucketFactor: 1.1, + NativeHistogramMaxBucketNumber: 100, + NativeHistogramMinResetDuration: 1 * time.Hour, + }, + []string{"handler", "method", "code"}, + ), + responseSize: f.NewHistogramVec( + prometheus.HistogramOpts{ + Name: "alertmanager_http_response_size_bytes", + Help: "Histogram of response size for HTTP requests.", + Buckets: prometheus.ExponentialBuckets(100, 10, 7), + }, + []string{"handler", "method"}, + ), + clusterEnabled: f.NewGauge( + prometheus.GaugeOpts{ + Name: "alertmanager_cluster_enabled", + Help: "Indicates whether the clustering is enabled or not.", + }, + ), + configuredReceivers: f.NewGauge( + prometheus.GaugeOpts{ + Name: "alertmanager_receivers", + Help: "Number of configured receivers.", + }, + ), + configuredIntegrations: f.NewGauge( + prometheus.GaugeOpts{ + Name: "alertmanager_integrations", + Help: "Number of configured integrations.", + }, + ), + configuredInhibitionRules: f.NewGauge( + prometheus.GaugeOpts{ + Name: "alertmanager_inhibition_rules", + Help: "Number of configured inhibition rules.", + }, + ), + } +} + +func (m *metrics) instrumentHandler(handlerName string, handler http.HandlerFunc) http.HandlerFunc { + handlerLabel := prometheus.Labels{"handler": handlerName} + return promhttp.InstrumentHandlerDuration( + m.requestDuration.MustCurryWith(handlerLabel), + promhttp.InstrumentHandlerResponseSize( + m.responseSize.MustCurryWith(handlerLabel), + handler, + ), + ) +} diff --git a/app/options.go b/app/options.go new file mode 100644 index 0000000000..aba831f8a3 --- /dev/null +++ b/app/options.go @@ -0,0 +1,232 @@ +// Copyright The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package app + +import ( + "errors" + "fmt" + "log/slog" + "time" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/exporter-toolkit/web" + + "github.com/prometheus/alertmanager/cluster" + "github.com/prometheus/alertmanager/featurecontrol" +) + +// DefaultClusterAddr is the default listen address used when the operator +// does not pass --cluster.listen-address. +const DefaultClusterAddr = "0.0.0.0:9094" + +// Default storage and lifecycle values, mirroring the kingpin flag +// defaults in cmd/alertmanager/main.go so embedders that start from +// DefaultOptions behave like the binary. +const ( + DefaultConfigFile = "alertmanager.yml" + DefaultDataDir = "data/" + DefaultRetention = 120 * time.Hour + DefaultMaintenanceInterval = 15 * time.Minute + DefaultAlertGCInterval = 30 * time.Minute + DefaultDispatchMaintenanceInterval = 30 * time.Second +) + +// Options carries the resolved configuration for a single Alertmanager +// instance. Field names follow the kingpin flags in cmd/alertmanager/main.go +// so that mapping between the two is straightforward. +// +// Logger, Registerer and Flagger are required dependencies; the remaining +// fields default to their zero value (which generally matches the kingpin +// flag default). +type Options struct { + // Storage and lifecycle. + ConfigFile string + DataDir string + Retention time.Duration + MaintenanceInterval time.Duration + MaxSilences int + MaxSilenceSizeBytes int + SilenceLogging bool + AlertGCInterval time.Duration + PerAlertNameLimit int + DispatchMaintenanceInterval time.Duration + DispatchStartDelay time.Duration + + // Web server. + WebConfig *web.FlagConfig + ExternalURL string + RoutePrefix string + GetConcurrency int + HTTPTimeout time.Duration + + // Cluster. + ClusterBindAddr string + ClusterAdvertiseAddr string + ClusterPeerName string + Peers []string + PeerTimeout time.Duration + PeersResolveTimeout time.Duration + GossipInterval time.Duration + PushPullInterval time.Duration + TCPTimeout time.Duration + ProbeTimeout time.Duration + ProbeInterval time.Duration + SettleTimeout time.Duration + ReconnectInterval time.Duration + PeerReconnectTimeout time.Duration + TLSConfigFile string + AllowInsecureAdvertise bool + Label string + + // Injected dependencies. + Logger *slog.Logger + Registerer prometheus.Registerer + Flagger featurecontrol.Flagger + + // Reload triggers a configuration reload each time it receives a + // value. The binary translates SIGHUP into sends on this channel; + // callers can also drive reloads programmatically. A nil channel + // disables external reloads (the /-/reload HTTP endpoint still works). + Reload <-chan struct{} +} + +// DefaultOptions returns an Options value pre-populated with the same +// defaults as the cmd/alertmanager kingpin flags. Clustering is disabled +// (ClusterBindAddr empty) because enabling a gossip listener by default +// would surprise embedders; the cluster timeouts are still seeded so that +// setting ClusterBindAddr is all that's needed to enable HA. +// +// Callers must still supply the required dependencies (Logger, Registerer, +// Flagger) and a WebConfig before passing the result to New or Run. +func DefaultOptions() Options { + return Options{ + ConfigFile: DefaultConfigFile, + DataDir: DefaultDataDir, + Retention: DefaultRetention, + MaintenanceInterval: DefaultMaintenanceInterval, + AlertGCInterval: DefaultAlertGCInterval, + DispatchMaintenanceInterval: DefaultDispatchMaintenanceInterval, + + PeerTimeout: 15 * time.Second, + PeersResolveTimeout: cluster.DefaultResolvePeersTimeout, + GossipInterval: cluster.DefaultGossipInterval, + PushPullInterval: cluster.DefaultPushPullInterval, + TCPTimeout: cluster.DefaultTCPTimeout, + ProbeTimeout: cluster.DefaultProbeTimeout, + ProbeInterval: cluster.DefaultProbeInterval, + SettleTimeout: cluster.DefaultPushPullInterval, + ReconnectInterval: cluster.DefaultReconnectInterval, + PeerReconnectTimeout: cluster.DefaultReconnectTimeout, + } +} + +// usingSystemdSocket reports whether the web server is configured to take +// its listener(s) from systemd socket activation, in which case explicit +// listen addresses are not required. +func (o *Options) usingSystemdSocket() bool { + return o.WebConfig != nil && + o.WebConfig.WebSystemdSocket != nil && + *o.WebConfig.WebSystemdSocket +} + +// validate checks that the Options are internally consistent and that no +// field carries a zero value that would later panic (e.g. a zero interval +// handed to time.NewTicker) or silently misbehave. It is intended to turn +// embedder misconfiguration into a clear error at New time rather than an +// obscure failure deep inside a subsystem. +func (o *Options) validate() error { + // Required injected dependencies. + if o.Logger == nil { + return errors.New("alertmanager/app: Options.Logger is required") + } + if o.Registerer == nil { + return errors.New("alertmanager/app: Options.Registerer is required") + } + if o.Flagger == nil { + return errors.New("alertmanager/app: Options.Flagger is required") + } + + // Storage and config paths. + if o.ConfigFile == "" { + return errors.New("alertmanager/app: Options.ConfigFile is required") + } + if o.DataDir == "" { + return errors.New("alertmanager/app: Options.DataDir is required") + } + + // Intervals that drive time.NewTicker panic on non-positive values, + // so reject them up front with a clear message. + for _, f := range []struct { + name string + val time.Duration + }{ + {"Retention", o.Retention}, + {"MaintenanceInterval", o.MaintenanceInterval}, + {"AlertGCInterval", o.AlertGCInterval}, + {"DispatchMaintenanceInterval", o.DispatchMaintenanceInterval}, + } { + if f.val <= 0 { + return fmt.Errorf("alertmanager/app: Options.%s must be positive", f.name) + } + } + + // Web server. + if o.WebConfig == nil { + return errors.New("alertmanager/app: Options.WebConfig is required") + } + // With systemd socket activation the listeners come from the + // activation file descriptors, so explicit listen addresses are + // optional; otherwise at least one is required. + if !o.usingSystemdSocket() && + (o.WebConfig.WebListenAddresses == nil || len(*o.WebConfig.WebListenAddresses) == 0) { + return errors.New("alertmanager/app: Options.WebConfig must contain at least one listen address (or enable WebSystemdSocket)") + } + // exporter-toolkit/web dereferences WebConfigFile unconditionally when + // serving. The cmd/alertmanager binary always populates it via kingpin, + // but a programmatic embedder might not, which would otherwise surface + // as a nil-pointer panic deep inside the toolkit rather than a clear + // validation error here. + if o.WebConfig.WebConfigFile == nil { + return errors.New("alertmanager/app: Options.WebConfig.WebConfigFile must be set (use a pointer to an empty string to disable web TLS/auth config)") + } + + // Cluster timeouts only matter when HA is enabled. When it is, the + // intervals that feed memberlist tickers must be positive. Note that + // SettleTimeout is intentionally excluded: it is used as a + // context.WithTimeout deadline, so a zero (or negative) value is a + // valid request to settle immediately without waiting — the + // acceptance tests rely on --cluster.settle-timeout=0s. + if o.ClusterBindAddr != "" { + for _, f := range []struct { + name string + val time.Duration + }{ + {"PeerTimeout", o.PeerTimeout}, + {"PeersResolveTimeout", o.PeersResolveTimeout}, + {"GossipInterval", o.GossipInterval}, + {"PushPullInterval", o.PushPullInterval}, + {"TCPTimeout", o.TCPTimeout}, + {"ProbeTimeout", o.ProbeTimeout}, + {"ProbeInterval", o.ProbeInterval}, + {"ReconnectInterval", o.ReconnectInterval}, + {"PeerReconnectTimeout", o.PeerReconnectTimeout}, + } { + if f.val <= 0 { + return fmt.Errorf("alertmanager/app: Options.%s must be positive when clustering is enabled", f.name) + } + } + } + + return nil +} diff --git a/app/options_test.go b/app/options_test.go new file mode 100644 index 0000000000..eb40a18127 --- /dev/null +++ b/app/options_test.go @@ -0,0 +1,121 @@ +// Copyright The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package app + +import ( + "testing" + "time" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/common/promslog" + "github.com/prometheus/exporter-toolkit/web" + "github.com/stretchr/testify/require" + + "github.com/prometheus/alertmanager/featurecontrol" +) + +func TestOptions_Validate(t *testing.T) { + logger := promslog.NewNopLogger() + reg := prometheus.NewRegistry() + ff, err := featurecontrol.NewFlags(logger, "") + require.NoError(t, err) + + addrs := []string{"127.0.0.1:0"} + emptyAddrs := []string{} + cfgFile := "" + systemdOn := true + + // valid returns Options that pass validation: DefaultOptions seeds + // every interval with a sane positive value, and we add the required + // dependencies, config path and a listen address. Each subtest + // mutates one field to exercise a specific branch. + valid := func() Options { + o := DefaultOptions() + o.ConfigFile = "alertmanager.yml" + o.Logger = logger + o.Registerer = reg + o.Flagger = ff + o.WebConfig = &web.FlagConfig{ + WebListenAddresses: &addrs, + WebConfigFile: &cfgFile, + } + return o + } + + base := valid() + require.NoError(t, base.validate()) + + for _, tc := range []struct { + name string + mutate func(*Options) + }{ + {name: "missing logger", mutate: func(o *Options) { o.Logger = nil }}, + {name: "missing registerer", mutate: func(o *Options) { o.Registerer = nil }}, + {name: "missing flagger", mutate: func(o *Options) { o.Flagger = nil }}, + {name: "missing config file", mutate: func(o *Options) { o.ConfigFile = "" }}, + {name: "missing data dir", mutate: func(o *Options) { o.DataDir = "" }}, + {name: "zero retention", mutate: func(o *Options) { o.Retention = 0 }}, + {name: "zero maintenance interval", mutate: func(o *Options) { o.MaintenanceInterval = 0 }}, + {name: "zero alert gc interval", mutate: func(o *Options) { o.AlertGCInterval = 0 }}, + {name: "zero dispatch maintenance interval", mutate: func(o *Options) { o.DispatchMaintenanceInterval = 0 }}, + {name: "negative retention", mutate: func(o *Options) { o.Retention = -time.Second }}, + {name: "missing web config", mutate: func(o *Options) { o.WebConfig = nil }}, + {name: "nil listen addresses", mutate: func(o *Options) { o.WebConfig.WebListenAddresses = nil }}, + {name: "empty listen addresses", mutate: func(o *Options) { o.WebConfig.WebListenAddresses = &emptyAddrs }}, + {name: "nil web config file", mutate: func(o *Options) { o.WebConfig.WebConfigFile = nil }}, + {name: "cluster enabled zero gossip interval", mutate: func(o *Options) { + o.ClusterBindAddr = DefaultClusterAddr + o.GossipInterval = 0 + }}, + {name: "cluster enabled zero probe timeout", mutate: func(o *Options) { + o.ClusterBindAddr = DefaultClusterAddr + o.ProbeTimeout = 0 + }}, + } { + t.Run(tc.name, func(t *testing.T) { + o := valid() + // Copy the WebConfig so per-subtest mutations don't leak + // into the shared addrs/cfgFile pointers. + wc := *o.WebConfig + o.WebConfig = &wc + tc.mutate(&o) + require.Error(t, o.validate()) + }) + } + + t.Run("systemd socket without listen addresses is valid", func(t *testing.T) { + o := valid() + o.WebConfig = &web.FlagConfig{ + WebSystemdSocket: &systemdOn, + WebConfigFile: &cfgFile, + // No WebListenAddresses: systemd provides the listeners. + } + require.NoError(t, o.validate()) + }) + + t.Run("cluster enabled with zero settle timeout is valid", func(t *testing.T) { + // SettleTimeout is a context deadline, so 0 ("settle now") is a + // valid request; the acceptance tests pass --cluster.settle-timeout=0s. + o := valid() + o.ClusterBindAddr = DefaultClusterAddr + o.SettleTimeout = 0 + require.NoError(t, o.validate()) + }) + + t.Run("cluster enabled with defaults is valid", func(t *testing.T) { + o := valid() + o.ClusterBindAddr = DefaultClusterAddr + require.NoError(t, o.validate()) + }) +} diff --git a/app/reloader.go b/app/reloader.go new file mode 100644 index 0000000000..38fb1cee01 --- /dev/null +++ b/app/reloader.go @@ -0,0 +1,263 @@ +// Copyright The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package app + +import ( + "context" + "fmt" + "log/slog" + "net/url" + "sync/atomic" + "time" + + "github.com/prometheus/common/model" + + "github.com/prometheus/alertmanager/alert" + "github.com/prometheus/alertmanager/api" + "github.com/prometheus/alertmanager/cluster" + "github.com/prometheus/alertmanager/config" + "github.com/prometheus/alertmanager/config/receiver" + "github.com/prometheus/alertmanager/dispatch" + "github.com/prometheus/alertmanager/eventrecorder" + "github.com/prometheus/alertmanager/inhibit" + "github.com/prometheus/alertmanager/marker" + "github.com/prometheus/alertmanager/notify" + "github.com/prometheus/alertmanager/provider" + "github.com/prometheus/alertmanager/silence" + "github.com/prometheus/alertmanager/template" + "github.com/prometheus/alertmanager/timeinterval" + "github.com/prometheus/alertmanager/tracing" +) + +// reloader owns the configuration-scoped subgraph of an Alertmanager +// instance: the routing tree, receivers, notification pipeline, inhibitor +// and dispatcher. These are rebuilt and atomically swapped on every +// config apply (reload), whereas the long-lived singletons it depends on +// (alerts, silences, notification log, API, event recorder, tracing, +// cluster peer) are constructed once in setup and only updated in place. +// +// Splitting this out of setup keeps the swap ordering — which is subtle +// (stop old, build new, start + wait-for-loading, then publish) — in one +// cohesive, independently testable place. +type reloader struct { + // Long-lived components: created once during startup. + alerts provider.Alerts + apih *api.API + dispatcherMetrics *dispatch.DispatcherMetrics + eventRecorder eventrecorder.Recorder + groupMarker marker.GroupMarker + logger *slog.Logger + metrics *metrics + notificationLog notify.NotificationLog + peer *cluster.Peer + pipelineBuilder *notify.PipelineBuilder + silencer *silence.Silencer + tracingMgr *tracing.Manager + + // Short-lived components: atomically swapped on every reload. + dispatcher atomic.Pointer[dispatch.Dispatcher] + inhibitor atomic.Pointer[inhibit.Inhibitor] + + // Functions and values used during reload. + waitFunc func() time.Duration + timeoutFunc func(time.Duration) time.Duration + externalURL *url.URL + startTime time.Time + + // Static configuration values. + dispatchStartDelay time.Duration + dispatchMaintenanceInterval time.Duration + retention time.Duration +} + +// groups returns the alert groups from the currently active dispatcher. +// It is wired into the API as its GroupFunc. +func (r *reloader) groups(ctx context.Context, routeFilter func(*dispatch.Route) bool, alertFilter func(*alert.Alert, time.Time) bool) (dispatch.AlertGroups, map[model.Fingerprint][]string, error) { + return r.dispatcher.Load().Groups(ctx, routeFilter, alertFilter) +} + +// reload rebuilds the config-scoped subgraph from conf and atomically +// swaps it in. It is registered as the config coordinator's subscriber, +// so it runs once for the initial config and again on every reload. +// +// All fallible work (template/receiver parsing, tracing config) happens +// before any live state is touched, so a failed reload leaves the +// previously active configuration — event recorder, inhibitor, dispatcher +// and tracing — fully intact. +func (r *reloader) reload(conf *config.Config) error { + // configLogger tags messages emitted by the reload itself; subsystem + // constructors get the base logger and apply their own component tag. + configLogger := r.logger.With("component", "configuration") + + tmpl, err := template.FromGlobs(conf.Templates) + if err != nil { + return fmt.Errorf("failed to parse templates: %w", err) + } + tmpl.ExternalURL = r.externalURL + + // Build the routing tree and record which receivers are used. + routes := dispatch.NewRoute(conf.Route, nil) + activeReceivers := make(map[string]struct{}) + routes.Walk(func(rt *dispatch.Route) { + activeReceivers[rt.RouteOpts.Receiver] = struct{}{} + }) + + // Build the map of receiver to integrations. + receivers := make(map[string][]notify.Integration, len(activeReceivers)) + var integrationsNum int + for _, rcv := range conf.Receivers { + if _, found := activeReceivers[rcv.Name]; !found { + // No need to build a receiver if no route is using it. + configLogger.Info("skipping creation of receiver not referenced by any route", "receiver", rcv.Name) + continue + } + integrations, err := receiver.BuildReceiverIntegrations(rcv, tmpl, r.logger) + if err != nil { + return err + } + // rcv.Name is guaranteed to be unique across all receivers. + receivers[rcv.Name] = integrations + integrationsNum += len(integrations) + } + + // Build the map of time interval names to time interval definitions. + timeIntervals := make(map[string][]timeinterval.TimeInterval, len(conf.MuteTimeIntervals)+len(conf.TimeIntervals)) + for _, ti := range conf.MuteTimeIntervals { + timeIntervals[ti.Name] = ti.TimeIntervals + } + for _, ti := range conf.TimeIntervals { + timeIntervals[ti.Name] = ti.TimeIntervals + } + + intervener := timeinterval.NewIntervener(timeIntervals) + + // Everything above is fallible but side-effect-free on the running + // instance. From here down the steps either cannot fail or only + // replace live components, so reaching this point means the reload + // will succeed. + // + // Apply tracing first: it is the last step that can fail, and doing + // it before stopping the old components keeps them running if it + // errors. + if err := r.tracingMgr.ApplyConfig(conf.TracingConfig); err != nil { + return fmt.Errorf("failed to apply tracing config: %w", err) + } + + // Reload event recorder outputs before stopping the old dispatcher so + // events emitted while it shuts down go to the new outputs. + r.eventRecorder.ApplyConfig(conf.EventRecorder) + + if old := r.inhibitor.Load(); old != nil { + old.Stop() + } + if old := r.dispatcher.Load(); old != nil { + old.Stop() + } + + newInhibitor := inhibit.NewInhibitor(r.alerts, conf.InhibitRules, r.logger, r.eventRecorder) + + // An interface value that holds a nil concrete value is non-nil. + // Therefore we explicitly pass an empty interface, to detect if the + // cluster is not enabled in notify. + var pipelinePeer notify.Peer + if r.peer != nil { + pipelinePeer = r.peer + } + + pipeline := r.pipelineBuilder.New( + receivers, + r.waitFunc, + newInhibitor, + r.silencer, + intervener, + r.groupMarker, + r.notificationLog, + pipelinePeer, + ) + + r.metrics.configuredReceivers.Set(float64(len(activeReceivers))) + r.metrics.configuredIntegrations.Set(float64(integrationsNum)) + r.metrics.configuredInhibitionRules.Set(float64(len(conf.InhibitRules))) + + r.apih.Update(conf, func(ctx context.Context, labels model.LabelSet) { + r.inhibitor.Load().Mutes(ctx, labels) + r.silencer.Mutes(ctx, labels) + }) + + newDispatcher := dispatch.NewDispatcher( + r.alerts, + routes, + pipeline, + r.groupMarker, + r.timeoutFunc, + r.dispatchMaintenanceInterval, + nil, + r.logger, + r.eventRecorder, + r.dispatcherMetrics, + ) + routes.Walk(func(rt *dispatch.Route) { + if rt.RouteOpts.RepeatInterval > r.retention { + configLogger.Warn( + "repeat_interval is greater than the data retention period. It can lead to notifications being repeated more often than expected.", + "repeat_interval", rt.RouteOpts.RepeatInterval, + "retention", r.retention, + "route", rt.Key(), + ) + } + if rt.RouteOpts.RepeatInterval < rt.RouteOpts.GroupInterval { + configLogger.Warn( + "repeat_interval is less than group_interval. Notifications will not repeat until the next group_interval.", + "repeat_interval", rt.RouteOpts.RepeatInterval, + "group_interval", rt.RouteOpts.GroupInterval, + "route", rt.Key(), + ) + } + }) + + // First, start the inhibitor so the inhibition cache can populate. + // Wait for it to load alerts before starting the dispatcher so we + // don't accidentally notify for an alert that will be inhibited. + // Publish it only after loading completes: the API mute callback + // reads r.inhibitor.Load(), so swapping earlier would expose an + // empty inhibition cache to concurrent requests during a reload (the + // pipeline already holds newInhibitor directly, and no dispatcher is + // running to drive it yet, so the old inhibitor stays authoritative + // for the API until the new one is ready). + go newInhibitor.Run() + newInhibitor.WaitForLoading() + r.inhibitor.Store(newInhibitor) + + // Next, start the dispatcher and wait for it to load before swapping + // the dispatcher pointer. This ensures that the API doesn't see the new + // dispatcher before it finishes populating the aggrGroups. + go newDispatcher.Run(r.startTime.Add(r.dispatchStartDelay)) + newDispatcher.WaitForLoading() + r.dispatcher.Store(newDispatcher) + + return nil +} + +// stop tears down the currently active inhibitor and dispatcher. It is +// registered on the App's cleanup stack and is safe to call when no +// config has been applied yet (both pointers nil). +func (r *reloader) stop() error { + if i := r.inhibitor.Load(); i != nil { + i.Stop() + } + if d := r.dispatcher.Load(); d != nil { + d.Stop() + } + return nil +} diff --git a/app/reloader_test.go b/app/reloader_test.go new file mode 100644 index 0000000000..cefa661ac2 --- /dev/null +++ b/app/reloader_test.go @@ -0,0 +1,173 @@ +// Copyright The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package app + +import ( + "context" + "net/url" + "os" + "path/filepath" + "testing" + "time" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/common/model" + "github.com/prometheus/common/promslog" + "github.com/stretchr/testify/require" + + "github.com/prometheus/alertmanager/alert" + "github.com/prometheus/alertmanager/api" + "github.com/prometheus/alertmanager/config" + "github.com/prometheus/alertmanager/dispatch" + "github.com/prometheus/alertmanager/eventrecorder" + "github.com/prometheus/alertmanager/featurecontrol" + "github.com/prometheus/alertmanager/marker" + "github.com/prometheus/alertmanager/nflog" + "github.com/prometheus/alertmanager/notify" + "github.com/prometheus/alertmanager/provider/mem" + "github.com/prometheus/alertmanager/silence" + "github.com/prometheus/alertmanager/tracing" +) + +// newTestReloader builds a reloader backed by real (but local, cluster- +// disabled) collaborators, mirroring how setup wires it. It is the +// minimum needed to exercise reload/stop in isolation from HTTP. +func newTestReloader(t *testing.T) *reloader { + t.Helper() + + logger := promslog.NewNopLogger() + reg := prometheus.NewRegistry() + ff, err := featurecontrol.NewFlags(logger, "") + require.NoError(t, err) + + m := newMetrics(reg) + rec := eventrecorder.NopRecorder() + dir := t.TempDir() + + alerts, err := mem.NewAlerts(context.Background(), 30*time.Minute, 0, nil, logger, rec, reg, ff) + require.NoError(t, err) + t.Cleanup(alerts.Close) + + silences, err := silence.New(silence.Options{ + SnapshotFile: filepath.Join(dir, "silences"), + Logger: logger, + Metrics: reg, + }) + require.NoError(t, err) + silencer := silence.NewSilencer(silences, logger, rec) + + groupMarker := marker.NewGroupMarker() + + nflogger, err := nflog.New(nflog.Options{ + SnapshotFile: filepath.Join(dir, "nflog"), + Logger: logger, + Metrics: reg, + }) + require.NoError(t, err) + + // The reloader owns the dispatcher/inhibitor; the API's GroupFunc + // reads them through r, which is assigned just below (mirroring setup). + var r *reloader + + apih, err := api.New(api.Options{ + Alerts: alerts, + Silences: silences, + GroupMutedFunc: groupMarker.Muted, + Logger: logger, + Registry: reg, + RequestDuration: m.requestDuration, + GroupFunc: func(ctx context.Context, rf func(*dispatch.Route) bool, af func(*alert.Alert, time.Time) bool) (dispatch.AlertGroups, map[model.Fingerprint][]string, error) { + return r.groups(ctx, rf, af) + }, + }) + require.NoError(t, err) + + extURL, err := url.Parse("http://localhost:9093") + require.NoError(t, err) + + r = &reloader{ + logger: logger, + alerts: alerts, + silencer: silencer, + groupMarker: groupMarker, + notificationLog: nflogger, + eventRecorder: rec, + apih: apih, + tracingMgr: tracing.NewManager(logger), + pipelineBuilder: notify.NewPipelineBuilder(reg, ff, rec), + dispatcherMetrics: dispatch.NewDispatcherMetrics(false, reg, ff), + metrics: m, + peer: nil, + waitFunc: func() time.Duration { return 0 }, + timeoutFunc: func(d time.Duration) time.Duration { return d }, + externalURL: extURL, + startTime: time.Now(), + dispatchStartDelay: 0, + dispatchMaintenanceInterval: 30 * time.Second, + retention: 120 * time.Hour, + } + return r +} + +func mustConfig(t *testing.T) *config.Config { + t.Helper() + conf, err := config.Load(minimalConfig) + require.NoError(t, err) + return conf +} + +func TestReloader_SwapsComponents(t *testing.T) { + r := newTestReloader(t) + t.Cleanup(func() { _ = r.stop() }) + + // Initial apply installs a running inhibitor and dispatcher. + require.NoError(t, r.reload(mustConfig(t))) + dispatcher1 := r.dispatcher.Load() + inh1 := r.inhibitor.Load() + require.NotNil(t, dispatcher1) + require.NotNil(t, inh1) + + // A second apply must stop the old pair and publish fresh instances. + require.NoError(t, r.reload(mustConfig(t))) + require.NotNil(t, r.dispatcher.Load()) + require.NotNil(t, r.inhibitor.Load()) + require.NotSame(t, dispatcher1, r.dispatcher.Load(), "dispatcher should be replaced on reload") + require.NotSame(t, inh1, r.inhibitor.Load(), "inhibitor should be replaced on reload") +} + +func TestReloader_ErrorLeavesPreviousStateIntact(t *testing.T) { + r := newTestReloader(t) + t.Cleanup(func() { _ = r.stop() }) + + require.NoError(t, r.reload(mustConfig(t))) + dispatcher1 := r.dispatcher.Load() + inh1 := r.inhibitor.Load() + + // A template that fails to parse makes reload error out before it + // swaps anything, so the previously active components stay in place. + bad := filepath.Join(t.TempDir(), "bad.tmpl") + require.NoError(t, os.WriteFile(bad, []byte("{{ .Foo "), 0o600)) + conf := mustConfig(t) + conf.Templates = []string{bad} + + require.Error(t, r.reload(conf)) + require.Same(t, dispatcher1, r.dispatcher.Load(), "dispatcher must be unchanged after a failed reload") + require.Same(t, inh1, r.inhibitor.Load(), "inhibitor must be unchanged after a failed reload") +} + +func TestReloader_StopIsNilSafe(t *testing.T) { + r := newTestReloader(t) + // stop before any reload (both pointers nil) must not panic. + require.NoError(t, r.stop()) +} diff --git a/app/url.go b/app/url.go new file mode 100644 index 0000000000..0f478e2c27 --- /dev/null +++ b/app/url.go @@ -0,0 +1,57 @@ +// Copyright The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package app + +import ( + "fmt" + "log/slog" + "net" + "net/url" + "strings" +) + +// extURL resolves the externally visible URL of this Alertmanager instance, +// defaulting to http://: when external is empty. +func extURL(logger *slog.Logger, hostnamef func() (string, error), listen, external string) (*url.URL, error) { + if external == "" { + hostname, err := hostnamef() + if err != nil { + return nil, err + } + _, port, err := net.SplitHostPort(listen) + if err != nil { + return nil, err + } + if port == "" { + logger.Warn("no port found for listen address", "address", listen) + } + external = fmt.Sprintf("http://%s:%s/", hostname, port) + } + + u, err := url.Parse(external) + if err != nil { + return nil, err + } + if u.Scheme != "http" && u.Scheme != "https" { + return nil, fmt.Errorf("%q: invalid %q scheme, only 'http' and 'https' are supported", u.String(), u.Scheme) + } + + ppref := strings.TrimRight(u.Path, "/") + if ppref != "" && !strings.HasPrefix(ppref, "/") { + ppref = "/" + ppref + } + u.Path = ppref + + return u, nil +} diff --git a/cmd/alertmanager/main_test.go b/app/url_test.go similarity index 99% rename from cmd/alertmanager/main_test.go rename to app/url_test.go index 28f21bdc2b..b7df64deac 100644 --- a/cmd/alertmanager/main_test.go +++ b/app/url_test.go @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package main +package app import ( "fmt" diff --git a/cmd/alertmanager/main.go b/cmd/alertmanager/main.go index 0e5142e8ac..80690e6e7a 100644 --- a/cmd/alertmanager/main.go +++ b/cmd/alertmanager/main.go @@ -15,120 +15,28 @@ package main import ( "context" - "errors" "fmt" - "log/slog" - "net" - "net/http" - "net/url" "os" "os/signal" - "path/filepath" "runtime" "strings" - "sync" - "sync/atomic" "syscall" - "time" "github.com/KimMachineGun/automemlimit/memlimit" "github.com/alecthomas/kingpin/v2" "github.com/prometheus/client_golang/prometheus" versioncollector "github.com/prometheus/client_golang/prometheus/collectors/version" - "github.com/prometheus/client_golang/prometheus/promauto" - "github.com/prometheus/client_golang/prometheus/promhttp" - "github.com/prometheus/common/model" "github.com/prometheus/common/promslog" promslogflag "github.com/prometheus/common/promslog/flag" - "github.com/prometheus/common/route" "github.com/prometheus/common/version" - "github.com/prometheus/exporter-toolkit/web" webflag "github.com/prometheus/exporter-toolkit/web/kingpinflag" - "github.com/prometheus/alertmanager/alert" - "github.com/prometheus/alertmanager/api" + "github.com/prometheus/alertmanager/app" "github.com/prometheus/alertmanager/cluster" - "github.com/prometheus/alertmanager/config" - "github.com/prometheus/alertmanager/config/receiver" - "github.com/prometheus/alertmanager/dispatch" - "github.com/prometheus/alertmanager/eventrecorder" - "github.com/prometheus/alertmanager/eventrecorder/eventrecorderpb" "github.com/prometheus/alertmanager/featurecontrol" - "github.com/prometheus/alertmanager/httpserver" - "github.com/prometheus/alertmanager/inhibit" - "github.com/prometheus/alertmanager/marker" "github.com/prometheus/alertmanager/matcher/compat" - "github.com/prometheus/alertmanager/nflog" - "github.com/prometheus/alertmanager/notify" - "github.com/prometheus/alertmanager/provider/mem" - "github.com/prometheus/alertmanager/silence" - "github.com/prometheus/alertmanager/template" - "github.com/prometheus/alertmanager/timeinterval" - "github.com/prometheus/alertmanager/tracing" - "github.com/prometheus/alertmanager/ui" ) -var ( - requestDuration = promauto.NewHistogramVec( - prometheus.HistogramOpts{ - Name: "alertmanager_http_request_duration_seconds", - Help: "Histogram of latencies for HTTP requests.", - Buckets: prometheus.DefBuckets, - NativeHistogramBucketFactor: 1.1, - NativeHistogramMaxBucketNumber: 100, - NativeHistogramMinResetDuration: 1 * time.Hour, - }, - []string{"handler", "method", "code"}, - ) - responseSize = promauto.NewHistogramVec( - prometheus.HistogramOpts{ - Name: "alertmanager_http_response_size_bytes", - Help: "Histogram of response size for HTTP requests.", - Buckets: prometheus.ExponentialBuckets(100, 10, 7), - }, - []string{"handler", "method"}, - ) - clusterEnabled = promauto.NewGauge( - prometheus.GaugeOpts{ - Name: "alertmanager_cluster_enabled", - Help: "Indicates whether the clustering is enabled or not.", - }, - ) - configuredReceivers = promauto.NewGauge( - prometheus.GaugeOpts{ - Name: "alertmanager_receivers", - Help: "Number of configured receivers.", - }, - ) - configuredIntegrations = promauto.NewGauge( - prometheus.GaugeOpts{ - Name: "alertmanager_integrations", - Help: "Number of configured integrations.", - }, - ) - configuredInhibitionRules = promauto.NewGauge( - prometheus.GaugeOpts{ - Name: "alertmanager_inhibition_rules", - Help: "Number of configured inhibition rules.", - }, - ) - - promslogConfig = promslog.Config{} -) - -func instrumentHandler(handlerName string, handler http.HandlerFunc) http.HandlerFunc { - handlerLabel := prometheus.Labels{"handler": handlerName} - return promhttp.InstrumentHandlerDuration( - requestDuration.MustCurryWith(handlerLabel), - promhttp.InstrumentHandlerResponseSize( - responseSize.MustCurryWith(handlerLabel), - handler, - ), - ) -} - -const defaultClusterAddr = "0.0.0.0:9094" - func main() { os.Exit(run()) } @@ -150,7 +58,7 @@ func run() int { alertGCInterval = kingpin.Flag("alerts.gc-interval", "Interval between alert GC.").Default("30m").Duration() perAlertNameLimit = kingpin.Flag("alerts.per-alertname-limit", "Maximum number of alerts per alertname. If negative or zero, no limit is set.").Default("0").Int() dispatchMaintenanceInterval = kingpin.Flag("dispatch.maintenance-interval", "Interval between maintenance of aggregation groups in the dispatcher.").Default("30s").Duration() - DispatchStartDelay = kingpin.Flag("dispatch.start-delay", "Minimum amount of time to wait before dispatching alerts. This option should be synced with value of --rules.alert.resend-delay on Prometheus.").Default("0s").Duration() + dispatchStartDelay = kingpin.Flag("dispatch.start-delay", "Minimum amount of time to wait before dispatching alerts. This option should be synced with value of --rules.alert.resend-delay on Prometheus.").Default("0s").Duration() webConfig = webflag.AddFlags(kingpin.CommandLine, ":9093") externalURL = kingpin.Flag("web.external-url", "The URL under which Alertmanager is externally reachable (for example, if Alertmanager is served via a reverse proxy). Used for generating relative and absolute links back to Alertmanager itself. If the URL has a path portion, it will be used to prefix all HTTP endpoints served by Alertmanager. If omitted, relevant URL components will be derived automatically.").String() @@ -162,7 +70,7 @@ func run() int { Default("0.9").Float64() clusterBindAddr = kingpin.Flag("cluster.listen-address", "Listen address for cluster. Set to empty string to disable HA mode."). - Default(defaultClusterAddr).String() + Default(app.DefaultClusterAddr).String() clusterAdvertiseAddr = kingpin.Flag("cluster.advertise-address", "Explicit address to advertise in cluster.").String() clusterPeerName = kingpin.Flag("cluster.peer-name", "Explicit name of the peer, rather than generating a random one").Default("").String() peers = kingpin.Flag("cluster.peer", "Initial peers (may be repeated).").Strings() @@ -182,21 +90,16 @@ func run() int { featureFlags = kingpin.Flag("enable-feature", fmt.Sprintf("Comma-separated experimental features to enable. Valid options: %s", strings.Join(featurecontrol.AllowedFlags, ", "))).Default("").String() ) - prometheus.MustRegister(versioncollector.NewCollector("alertmanager")) - + promslogConfig := promslog.Config{} promslogflag.AddFlags(kingpin.CommandLine, &promslogConfig) kingpin.CommandLine.UsageWriter(os.Stdout) - kingpin.Version(version.Print("alertmanager")) kingpin.CommandLine.GetFlag("help").Short('h') kingpin.Parse() logger := promslog.New(&promslogConfig) - logger.Info("Starting Alertmanager", "version", version.Info()) - startTime := time.Now() - - logger.Info("Build context", "build_context", version.BuildContext()) + prometheus.MustRegister(versioncollector.NewCollector("alertmanager")) ff, err := featurecontrol.NewFlags(logger, *featureFlags) if err != nil { @@ -210,7 +113,6 @@ func run() int { logger.Error("--auto-gomemlimit.ratio must be greater than 0 and less than or equal to 1.") return 1 } - if _, err := memlimit.SetGoMemLimitWithOpts( memlimit.WithRatio(*memlimitRatio), memlimit.WithProvider( @@ -224,495 +126,78 @@ func run() int { } } - err = os.MkdirAll(*dataDir, 0o777) - if err != nil { - logger.Error("Unable to create data directory", "err", err) - return 1 - } - - tlsTransportConfig, err := cluster.GetTLSTransportConfig(*tlsConfigFile) - if err != nil { - logger.Error("unable to initialize TLS transport configuration for gossip mesh", "err", err) - return 1 - } - var peer *cluster.Peer - if *clusterBindAddr != "" { - peer, err = cluster.Create( - logger.With("component", "cluster"), - prometheus.DefaultRegisterer, - *clusterBindAddr, - *clusterAdvertiseAddr, - *peers, - true, - *pushPullInterval, - *gossipInterval, - *tcpTimeout, - *peersResolveTimeout, - *probeTimeout, - *probeInterval, - tlsTransportConfig, - *allowInsecureAdvertise, - *label, - *clusterPeerName, - ) - if err != nil { - logger.Error("unable to initialize gossip mesh", "err", err) - return 1 - } - clusterEnabled.Set(1) - } - - stopc := make(chan struct{}) - var wg sync.WaitGroup - - // Load config once for both event recorder initialization and the first - // coordinator apply. Subsequent reloads (SIGHUP, /-/reload) go - // through configCoordinator.Reload() which reads the file again. - initialConf, err := config.LoadFile(*configFile) - if err != nil { - logger.Error("error loading configuration file", "err", err) - return 1 - } - - hostname, _ := os.Hostname() - var eventRec eventrecorder.Recorder - if ff.EnableEventRecorder() { - eventRec = eventrecorder.NewRecorderFromConfig(initialConf.EventRecorder, hostname, logger.With("component", "eventrecorder"), prometheus.DefaultRegisterer) - } - defer eventRec.Close() - - recordCtx := eventrecorder.WithEventRecording(context.Background()) - eventRec.RecordEvent(recordCtx, &eventrecorderpb.EventData{ - EventType: &eventrecorderpb.EventData_AlertmanagerStartupEvent{ - AlertmanagerStartupEvent: &eventrecorderpb.AlertmanagerStartupEvent{ - Version: version.Version, - BuildContext: version.BuildContext(), - }, - }, - }) - defer func() { - eventRec.RecordEvent(recordCtx, &eventrecorderpb.EventData{ - EventType: &eventrecorderpb.EventData_AlertmanagerShutdownEvent{ - AlertmanagerShutdownEvent: &eventrecorderpb.AlertmanagerShutdownEvent{}, - }, - }) - }() - - notificationLogOpts := nflog.Options{ - SnapshotFile: filepath.Join(*dataDir, "nflog"), - Retention: *retention, - Logger: logger.With("component", "nflog"), - Metrics: prometheus.DefaultRegisterer, - } - - notificationLog, err := nflog.New(notificationLogOpts) - if err != nil { - logger.Error("error creating notification log", "err", err) - return 1 - } - if peer != nil { - c := peer.AddState("nfl", notificationLog, prometheus.DefaultRegisterer) - notificationLog.SetBroadcast(c.Broadcast) - } - - wg.Go(func() { - notificationLog.Maintenance(*maintenanceInterval, filepath.Join(*dataDir, "nflog"), stopc, nil) - }) - - marker := marker.NewGroupMarker() - - silenceOpts := silence.Options{ - SnapshotFile: filepath.Join(*dataDir, "silences"), - Retention: *retention, - Limits: silence.Limits{ - MaxSilences: func() int { return *maxSilences }, - MaxSilenceSizeBytes: func() int { return *maxSilenceSizeBytes }, - }, - Logger: logger.With("component", "silences"), - Metrics: prometheus.DefaultRegisterer, - Logging: *silenceLogging, - EventRecorder: eventRec, - } - - silences, err := silence.New(silenceOpts) - if err != nil { - logger.Error("error creating silence", "err", err) - return 1 - } - if peer != nil { - c := peer.AddState("sil", silences, prometheus.DefaultRegisterer) - silences.SetBroadcast(c.Broadcast) - } - - // Start providers before router potentially sends updates. - wg.Go(func() { - silences.Maintenance(*maintenanceInterval, filepath.Join(*dataDir, "silences"), stopc, nil) - }) - - defer func() { - close(stopc) - wg.Wait() - }() - - silencer := silence.NewSilencer(silences, logger, eventRec) - - // Peer state listeners have been registered, now we can join and get the initial state. - if peer != nil { - err = peer.Join( - *reconnectInterval, - *peerReconnectTimeout, - ) - if err != nil { - logger.Warn("unable to join gossip mesh", "err", err) - } - ctx, cancel := context.WithTimeout(context.Background(), *settleTimeout) - defer func() { - cancel() - if err := peer.Leave(10 * time.Second); err != nil { - logger.Warn("unable to leave gossip mesh", "err", err) - } - }() - go peer.Settle(ctx, *gossipInterval*10) - eventRec.SetClusterPeer(peer) - } - - alerts, err := mem.NewAlerts( - context.Background(), - *alertGCInterval, - *perAlertNameLimit, - silencer, - logger, - eventRec, - prometheus.DefaultRegisterer, - ff, - ) - if err != nil { - logger.Error("error creating memory provider", "err", err) - return 1 - } - defer alerts.Close() - - var disp atomic.Pointer[dispatch.Dispatcher] - defer func() { - disp.Load().Stop() - }() - - groupFn := func(ctx context.Context, routeFilter func(*dispatch.Route) bool, alertFilter func(*alert.Alert, time.Time) bool) (dispatch.AlertGroups, map[model.Fingerprint][]string, error) { - return disp.Load().Groups(ctx, routeFilter, alertFilter) - } - - // An interface value that holds a nil concrete value is non-nil. - // Therefore we explicly pass an empty interface, to detect if the - // cluster is not enabled in notify. - var clusterPeer cluster.ClusterPeer - if peer != nil { - clusterPeer = peer - } - - api, err := api.New(api.Options{ - Alerts: alerts, - Silences: silences, - GroupMutedFunc: marker.Muted, - Peer: clusterPeer, - Timeout: *httpTimeout, - Concurrency: *getConcurrency, - Logger: logger.With("component", "api"), - Registry: prometheus.DefaultRegisterer, - RequestDuration: requestDuration, - GroupFunc: groupFn, - }) - if err != nil { - logger.Error("failed to create API", "err", err) - return 1 - } - - amURL, err := extURL(logger, os.Hostname, (*webConfig.WebListenAddresses)[0], *externalURL) - if err != nil { - logger.Error("failed to determine external URL", "err", err) - return 1 - } - logger.Debug("external url", "externalUrl", amURL.String()) - - waitFunc := func() time.Duration { return 0 } - if peer != nil { - waitFunc = clusterWait(peer, *peerTimeout) - } - timeoutFunc := func(d time.Duration) time.Duration { - if d < notify.MinTimeout { - d = notify.MinTimeout - } - return d + waitFunc() - } - - tracingManager := tracing.NewManager(logger.With("component", "tracing")) - - var ( - inhibitor atomic.Pointer[inhibit.Inhibitor] - tmpl *template.Template - ) - - dispMetrics := dispatch.NewDispatcherMetrics(false, prometheus.DefaultRegisterer, ff) - pipelineBuilder := notify.NewPipelineBuilder(prometheus.DefaultRegisterer, ff, eventRec) - configLogger := logger.With("component", "configuration") - configCoordinator := config.NewCoordinator( - *configFile, - prometheus.DefaultRegisterer, - configLogger, - ) - configCoordinator.Subscribe(func(conf *config.Config) error { - // Reload event recorder outputs first so events emitted during - // the rest of this callback (e.g., by stopping the old - // dispatcher) go to the new outputs. - eventRec.ApplyConfig(conf.EventRecorder) - - tmpl, err = template.FromGlobs(conf.Templates) - if err != nil { - return fmt.Errorf("failed to parse templates: %w", err) - } - tmpl.ExternalURL = amURL - - // Build the routing tree and record which receivers are used. - routes := dispatch.NewRoute(conf.Route, nil) - activeReceivers := make(map[string]struct{}) - routes.Walk(func(r *dispatch.Route) { - activeReceivers[r.RouteOpts.Receiver] = struct{}{} - }) - - // Build the map of receiver to integrations. - receivers := make(map[string][]notify.Integration, len(activeReceivers)) - var integrationsNum int - for _, rcv := range conf.Receivers { - if _, found := activeReceivers[rcv.Name]; !found { - // No need to build a receiver if no route is using it. - configLogger.Info("skipping creation of receiver not referenced by any route", "receiver", rcv.Name) - continue - } - integrations, err := receiver.BuildReceiverIntegrations(rcv, tmpl, logger) - if err != nil { - return err - } - // rcv.Name is guaranteed to be unique across all receivers. - receivers[rcv.Name] = integrations - integrationsNum += len(integrations) - } - - // Build the map of time interval names to time interval definitions. - timeIntervals := make(map[string][]timeinterval.TimeInterval, len(conf.MuteTimeIntervals)+len(conf.TimeIntervals)) - for _, ti := range conf.MuteTimeIntervals { - timeIntervals[ti.Name] = ti.TimeIntervals - } - - for _, ti := range conf.TimeIntervals { - timeIntervals[ti.Name] = ti.TimeIntervals - } - - intervener := timeinterval.NewIntervener(timeIntervals) - - inhibitor.Load().Stop() - disp.Load().Stop() - - newInhibitor := inhibit.NewInhibitor(alerts, conf.InhibitRules, logger, eventRec) - inhibitor.Store(newInhibitor) - - // An interface value that holds a nil concrete value is non-nil. - // Therefore we explicly pass an empty interface, to detect if the - // cluster is not enabled in notify. - var pipelinePeer notify.Peer - if peer != nil { - pipelinePeer = peer - } - - pipeline := pipelineBuilder.New( - receivers, - waitFunc, - newInhibitor, - silencer, - intervener, - marker, - notificationLog, - pipelinePeer, - ) - - configuredReceivers.Set(float64(len(activeReceivers))) - configuredIntegrations.Set(float64(integrationsNum)) - configuredInhibitionRules.Set(float64(len(conf.InhibitRules))) - - api.Update(conf, func(ctx context.Context, labels model.LabelSet) { - inhibitor.Load().Mutes(ctx, labels) - silencer.Mutes(ctx, labels) - }) - - newDisp := dispatch.NewDispatcher( - alerts, - routes, - pipeline, - marker, - timeoutFunc, - *dispatchMaintenanceInterval, - nil, - logger, - eventRec, - dispMetrics, - ) - routes.Walk(func(r *dispatch.Route) { - if r.RouteOpts.RepeatInterval > *retention { - configLogger.Warn( - "repeat_interval is greater than the data retention period. It can lead to notifications being repeated more often than expected.", - "repeat_interval", - r.RouteOpts.RepeatInterval, - "retention", - *retention, - "route", - r.Key(), - ) - } - - if r.RouteOpts.RepeatInterval < r.RouteOpts.GroupInterval { - configLogger.Warn( - "repeat_interval is less than group_interval. Notifications will not repeat until the next group_interval.", - "repeat_interval", - r.RouteOpts.RepeatInterval, - "group_interval", - r.RouteOpts.GroupInterval, - "route", - r.Key(), - ) - } - }) - - // first, start the inhibitor so the inhibition cache can populate - // wait for this to load alerts before starting the dispatcher so - // we don't accidentially notify for an alert that will be inhibited - go newInhibitor.Run() - newInhibitor.WaitForLoading() - - // next, start the dispatcher and wait for it to load before swapping the disp pointer. - // This ensures that the API doesn't see the new dispatcher before it finishes populating - // the aggrGroups - go newDisp.Run(startTime.Add(*DispatchStartDelay)) - newDisp.WaitForLoading() - disp.Store(newDisp) - - err = tracingManager.ApplyConfig(conf.TracingConfig) - if err != nil { - return fmt.Errorf("failed to apply tracing config: %w", err) - } - - go tracingManager.Run() - - return nil - }) - - if err := configCoordinator.ApplyConfig(initialConf); err != nil { - return 1 - } - - // Make routePrefix default to externalURL path if empty string. - if *routePrefix == "" { - *routePrefix = amURL.Path - } - *routePrefix = "/" + strings.Trim(*routePrefix, "/") - logger.Debug("route prefix", "routePrefix", *routePrefix) - - router := route.New().WithInstrumentation(instrumentHandler) - if *routePrefix != "/" { - router.Get("/", func(w http.ResponseWriter, r *http.Request) { - http.Redirect(w, r, *routePrefix, http.StatusFound) - }) - router = router.WithPrefix(*routePrefix) - } - - webReload := make(chan chan error) - - ui.Register(router) - httpserver.Register(router, webReload) - - mux := api.Register(router, *routePrefix) + // Translate OS signals into context cancellation (SIGINT/SIGTERM) and + // reload events (SIGHUP). The app package no longer touches signals + // directly so that it can be embedded in tests. + ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) + defer cancel() - srv := &http.Server{ - // instrument all handlers with tracing - Handler: tracing.Middleware(mux), - } - srvc := make(chan struct{}) + hup := make(chan os.Signal, 1) + signal.Notify(hup, syscall.SIGHUP) + defer signal.Stop(hup) + reload := make(chan struct{}, 1) go func() { - if err := web.ListenAndServe(srv, webConfig, logger); !errors.Is(err, http.ErrServerClosed) { - logger.Error("Listen error", "err", err) - close(srvc) - } - defer func() { - if err := srv.Close(); err != nil { - logger.Error("Error on closing the server", "err", err) + for { + select { + case <-hup: + select { + case reload <- struct{}{}: + default: + } + case <-ctx.Done(): + return } - }() - }() - - var ( - hup = make(chan os.Signal, 1) - term = make(chan os.Signal, 1) - ) - signal.Notify(hup, syscall.SIGHUP) - signal.Notify(term, os.Interrupt, syscall.SIGTERM) - - for { - select { - case <-hup: - // ignore error, already logged in `reload()` - _ = configCoordinator.Reload() - case errc := <-webReload: - errc <- configCoordinator.Reload() - case <-term: - logger.Info("Received SIGTERM, exiting gracefully...") - - // shut down the tracing manager to flush any remaining spans. - // this blocks for up to 5s - tracingManager.Stop() - - return 0 - case <-srvc: - return 1 - } - } -} - -// clusterWait returns a function that inspects the current peer state and returns -// a duration of one base timeout for each peer with a higher ID than ourselves. -func clusterWait(p *cluster.Peer, timeout time.Duration) func() time.Duration { - return func() time.Duration { - return time.Duration(p.Position()) * timeout - } -} - -func extURL(logger *slog.Logger, hostnamef func() (string, error), listen, external string) (*url.URL, error) { - if external == "" { - hostname, err := hostnamef() - if err != nil { - return nil, err - } - _, port, err := net.SplitHostPort(listen) - if err != nil { - return nil, err } - if port == "" { - logger.Warn("no port found for listen address", "address", listen) - } - - external = fmt.Sprintf("http://%s:%s/", hostname, port) - } - - u, err := url.Parse(external) - if err != nil { - return nil, err - } - if u.Scheme != "http" && u.Scheme != "https" { - return nil, fmt.Errorf("%q: invalid %q scheme, only 'http' and 'https' are supported", u.String(), u.Scheme) - } + }() - ppref := strings.TrimRight(u.Path, "/") - if ppref != "" && !strings.HasPrefix(ppref, "/") { - ppref = "/" + ppref + opts := app.Options{ + ConfigFile: *configFile, + DataDir: *dataDir, + Retention: *retention, + MaintenanceInterval: *maintenanceInterval, + MaxSilences: *maxSilences, + MaxSilenceSizeBytes: *maxSilenceSizeBytes, + SilenceLogging: *silenceLogging, + AlertGCInterval: *alertGCInterval, + PerAlertNameLimit: *perAlertNameLimit, + DispatchMaintenanceInterval: *dispatchMaintenanceInterval, + DispatchStartDelay: *dispatchStartDelay, + + WebConfig: webConfig, + ExternalURL: *externalURL, + RoutePrefix: *routePrefix, + GetConcurrency: *getConcurrency, + HTTPTimeout: *httpTimeout, + + ClusterBindAddr: *clusterBindAddr, + ClusterAdvertiseAddr: *clusterAdvertiseAddr, + ClusterPeerName: *clusterPeerName, + Peers: *peers, + PeerTimeout: *peerTimeout, + PeersResolveTimeout: *peersResolveTimeout, + GossipInterval: *gossipInterval, + PushPullInterval: *pushPullInterval, + TCPTimeout: *tcpTimeout, + ProbeTimeout: *probeTimeout, + ProbeInterval: *probeInterval, + SettleTimeout: *settleTimeout, + ReconnectInterval: *reconnectInterval, + PeerReconnectTimeout: *peerReconnectTimeout, + TLSConfigFile: *tlsConfigFile, + AllowInsecureAdvertise: *allowInsecureAdvertise, + Label: *label, + + Logger: logger, + Registerer: prometheus.DefaultRegisterer, + Flagger: ff, + Reload: reload, + } + + if err := app.Run(ctx, opts); err != nil { + logger.Error("alertmanager exited with error", "err", err) + return 1 } - u.Path = ppref - - return u, nil + logger.Info("Received shutdown signal, exiting gracefully...") + return 0 } diff --git a/go.mod b/go.mod index 0b9d9c89f7..e916cdbb05 100644 --- a/go.mod +++ b/go.mod @@ -15,6 +15,7 @@ require ( github.com/cenkalti/backoff/v4 v4.3.0 github.com/cespare/xxhash/v2 v2.3.0 github.com/coder/quartz v0.3.1 + github.com/coreos/go-systemd/v22 v22.7.0 github.com/emersion/go-smtp v0.24.0 github.com/fsnotify/fsnotify v1.10.0 github.com/go-openapi/analysis v0.25.0 @@ -30,6 +31,7 @@ require ( github.com/hashicorp/golang-lru/v2 v2.0.7 github.com/hashicorp/memberlist v0.5.4 github.com/jessevdk/go-flags v1.6.1 + github.com/mdlayher/vsock v1.2.1 github.com/oklog/run v1.2.0 github.com/oklog/ulid/v2 v2.1.1 github.com/prometheus/client_golang v1.23.2 @@ -73,7 +75,6 @@ require ( github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.21 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cenkalti/backoff/v5 v5.0.3 // indirect - github.com/coreos/go-systemd/v22 v22.7.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 // indirect @@ -108,7 +109,6 @@ require ( github.com/klauspost/compress v1.18.6 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/mdlayher/socket v0.4.1 // indirect - github.com/mdlayher/vsock v1.2.1 // indirect github.com/miekg/dns v1.1.68 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect From 37a5cd7dca24569f8429ccc54c256bc8bb941d5a Mon Sep 17 00:00:00 2001 From: Ethan Hunter Date: Fri, 12 Jun 2026 09:01:39 -0600 Subject: [PATCH 002/120] bump version in the VERSION file and update date in CHANGELOG (#5300) Signed-off-by: Ethan Hunter --- CHANGELOG.md | 2 +- VERSION | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 42f451d294..b6785cdc76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ * [FEATURE] ... * [ENHANCEMENT] ... -## 0.33.0 / 2026-06-11 +## 0.33.0 / 2026-06-12 * [CHANGE] The '--enable-feature=auto-gomaxprocs' option has been removed. This flag had no effect since v0.29 and was deprecated in v0.32. It can be safely removed from any startup scripts. #5090, #5251 * [CHANGE] Add `group-key-in-metrics` feature flag. #5047 diff --git a/VERSION b/VERSION index 989b29cc32..be386c9ede 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.32.2 +0.33.0 From d3b133e608b3924f206ea6c1959bb99958a3da10 Mon Sep 17 00:00:00 2001 From: Siavash Safi Date: Mon, 15 Jun 2026 12:33:40 +0200 Subject: [PATCH 003/120] fix(webhook): preserve custom payload string values that look like YAML (#5304) The custom webhook `payload` feature renders a Go template and, when the output decodes into a structured value (e.g. via `toJson`), embeds it as real JSON. DeepCopyWithTemplate did this by recursing back into itself over the decoded value, which re-templated and re-parsed every leaf. Because JSON is a subset of YAML, a quoted leaf such as "value1:" decoded correctly on the first pass but was reinterpreted on the second, turning it into the map {"value1": null}. The same affected strings resembling numbers, booleans, null and dates. Decode the rendered string once and normalize the result into JSON-compatible types without re-templating or re-parsing its leaves, keeping scalar values verbatim. Add regression tests at both the template and webhook levels. Fixes #5302 Signed-off-by: Siavash Safi --- CHANGELOG.md | 1 + notify/webhook/webhook_test.go | 68 ++++++++++++++++++++++++++++++++++ template/template.go | 43 ++++++++++++++++++++- template/template_test.go | 32 ++++++++++++++++ 4 files changed, 143 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b6785cdc76..a72e4e1cf6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ * [CHANGE] ... * [FEATURE] ... * [ENHANCEMENT] ... +* [BUGFIX] webhook: Keep custom `payload` string values verbatim instead of reinterpreting JSON leaves that look like YAML (e.g. values ending with a colon). #5302 ## 0.33.0 / 2026-06-12 diff --git a/notify/webhook/webhook_test.go b/notify/webhook/webhook_test.go index c1c976491e..e4b5823992 100644 --- a/notify/webhook/webhook_test.go +++ b/notify/webhook/webhook_test.go @@ -532,3 +532,71 @@ func TestWebhookCustomPayloadString(t *testing.T) { string(capturedPayload), ) } + +// TestWebhookCustomPayloadPreservesYAMLLikeStrings is a regression test for #5302. +// Label/annotation values that look like YAML (e.g. ending with a colon, or that +// resemble numbers, booleans or null) must be sent verbatim and not reinterpreted. +func TestWebhookCustomPayloadPreservesYAMLLikeStrings(t *testing.T) { + var capturedPayload []byte + + mockTransport := roundTripFunc(func(req *http.Request) *http.Response { + var err error + capturedPayload, err = io.ReadAll(req.Body) + if err != nil { + t.Fatal(err) + } + return &http.Response{ + StatusCode: http.StatusOK, + Body: http.NoBody, + } + }) + + u, err := url.Parse("http://localhost") + require.NoError(t, err) + + payload := ` +{{- $res := list -}} +{{- range .Alerts -}} +{{- $res = append $res .Labels -}} +{{- end -}} +{{ toJson $res }} +` + + conf := &WebhookConfig{ + URL: amcommoncfg.SecretTemplateURL(u.String()), + HTTPConfig: &commoncfg.HTTPClientConfig{}, + Payload: payload, + } + + alerts := []*types.Alert{ + { + Alert: model.Alert{ + Labels: model.LabelSet{ + "alertname": "test1", + "id": "value1:", + "num": "123", + "truthy": "true", + "empty": "null", + }, + StartsAt: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC), + EndsAt: time.Date(2020, 1, 1, 1, 0, 0, 0, time.UTC), + GeneratorURL: "http://generator.url", + }, + }, + } + tmpl := test.CreateTmpl(t) + ctx := notify.WithGroupKey(context.Background(), "{}:{alertname=\"test1\"}") + ctx = notify.WithReceiverName(ctx, "test_receiver") + + n, err := New(conf, tmpl, promslog.NewNopLogger()) + require.NoError(t, err) + n.client.Transport = mockTransport + _, err = n.Notify(ctx, alerts...) + require.NoError(t, err) + + require.NotEmpty(t, capturedPayload) + require.JSONEq(t, + `[{"alertname":"test1","id":"value1:","num":"123","truthy":"true","empty":"null"}]`, + string(capturedPayload), + ) +} diff --git a/template/template.go b/template/template.go index 73f1ac526f..1ddbcfc054 100644 --- a/template/template.go +++ b/template/template.go @@ -494,7 +494,12 @@ func DeepCopyWithTemplate(value any, tmplTextFunc TemplateFunc) (any, error) { // ignore error, thus the string is not an interface return parsed, ok } - return DeepCopyWithTemplate(inlineType, tmplTextFunc) + // inlineType holds structured data decoded from the rendered string. + // This is already final data, so only normalize it into JSON-compatible + // types. It must not be passed back through DeepCopyWithTemplate, because + // re-templating and re-parsing its leaf values would reinterpret strings + // that merely look like YAML (e.g. "value1:" becoming a map). See #5302. + return normalizeYAMLValue(inlineType), nil } return parsed, ok @@ -534,3 +539,39 @@ func DeepCopyWithTemplate(value any, tmplTextFunc TemplateFunc) (any, error) { return value, nil } } + +// normalizeYAMLValue recursively converts a value decoded by yaml.Unmarshal into +// JSON-compatible types: maps become map[string]any (non-string keys are dropped, +// mirroring DeepCopyWithTemplate) and slices/arrays become []any. Scalar leaves are +// returned unchanged, so values are never re-templated or re-parsed. This keeps a +// rendered JSON payload byte-for-byte faithful instead of reinterpreting string +// values that happen to be valid YAML (see #5302). +func normalizeYAMLValue(value any) any { + if value == nil { + return nil + } + + valueMeta := reflect.ValueOf(value) + switch valueMeta.Kind() { + case reflect.Array, reflect.Slice: + converted := make([]any, valueMeta.Len()) + for i := range converted { + converted[i] = normalizeYAMLValue(valueMeta.Index(i).Interface()) + } + return converted + + case reflect.Map: + converted := make(map[string]any, valueMeta.Len()) + for _, keyMeta := range valueMeta.MapKeys() { + strKey, isString := keyMeta.Interface().(string) + if !isString { + continue + } + converted[strKey] = normalizeYAMLValue(valueMeta.MapIndex(keyMeta).Interface()) + } + return converted + + default: + return value + } +} diff --git a/template/template_test.go b/template/template_test.go index 3935f0bec0..faa3050964 100644 --- a/template/template_test.go +++ b/template/template_test.go @@ -807,6 +807,38 @@ func TestDeepCopyWithTemplate(t *testing.T) { fn: identity, want: nil, }, + { + // Regression test for #5302: a rendered JSON document must be taken + // as-is. String leaves that look like YAML (e.g. ending with a colon) + // must not be reinterpreted into maps/scalars. + title: "rendered JSON keeps string leaves verbatim", + input: `[{"alertname":"test1","id":"value1:"}]`, + fn: identity, + want: []any{ + map[string]any{"alertname": "test1", "id": "value1:"}, + }, + }, + { + title: "rendered JSON keeps YAML-like scalar strings as strings", + input: `{"num":"123","truthy":"true","empty":"null","when":"2026-01-01"}`, + fn: identity, + want: map[string]any{ + "num": "123", + "truthy": "true", + "empty": "null", + "when": "2026-01-01", + }, + }, + { + title: "rendered JSON preserves real scalar types", + input: `{"num":123,"truthy":true,"empty":null}`, + fn: identity, + want: map[string]any{ + "num": 123, + "truthy": true, + "empty": nil, + }, + }, } { t.Run(tc.title, func(t *testing.T) { got, err := DeepCopyWithTemplate(tc.input, tc.fn) From 7c35101c763954173da842e80c53f10009b041fb Mon Sep 17 00:00:00 2001 From: Jack Rosenthal Date: Tue, 16 Jun 2026 02:31:19 -0600 Subject: [PATCH 004/120] fix(eventrecorder): skip building discarded mute events on the alert read path (#5307) The `GET /api/v2/alerts` and `/api/v2/alerts/groups` handlers run the silencer and inhibitor over every alert to compute its silenced and inhibited status. When an alert is actively silenced or inhibited, `Silencer.Mutes` and `Inhibitor.Mutes` build a `SilenceMutedAlert` or `InhibitionMutedAlert` protobuf event and pass it to `RecordEvent`. On the read path event recording is not enabled on the context, so `RecordEvent` discards the event, but the proto conversions and fingerprint slices are still built for every muted alert on every request. Change `RecordEvent` to take a builder function (`func() *eventrecorderpb.EventData`) instead of an already-constructed event, and invoke it only after the recording gates pass. Callers on hot read paths now pass a closure, so the proto conversions and fingerprint slices are built only when recording is enabled. Folding the gate into `RecordEvent` keeps the gating logic in one place, so callers cannot forget to check it and no separate predicate can drift out of sync with `RecordEvent`. When recording is enabled the behavior is unchanged. Signed-off-by: Jack Rosenthal --- app/app.go | 24 ++++++++++++++---------- dispatch/dispatch.go | 8 ++++++-- eventrecorder/recorder.go | 9 ++++++++- eventrecorder/recorder_test.go | 20 ++++++++++---------- inhibit/inhibit.go | 12 +++++++----- notify/retry_stage.go | 5 ++++- provider/mem/mem.go | 5 ++++- silence/silence.go | 25 ++++++++++++++++--------- 8 files changed, 69 insertions(+), 39 deletions(-) diff --git a/app/app.go b/app/app.go index 015a262905..cb1c4c6790 100644 --- a/app/app.go +++ b/app/app.go @@ -265,19 +265,23 @@ func (a *App) setup() error { a.onStop("event recorder", eventRec.Close) recordCtx := eventrecorder.WithEventRecording(context.Background()) - eventRec.RecordEvent(recordCtx, &eventrecorderpb.EventData{ - EventType: &eventrecorderpb.EventData_AlertmanagerStartupEvent{ - AlertmanagerStartupEvent: &eventrecorderpb.AlertmanagerStartupEvent{ - Version: version.Version, - BuildContext: version.BuildContext(), + eventRec.RecordEvent(recordCtx, func() *eventrecorderpb.EventData { + return &eventrecorderpb.EventData{ + EventType: &eventrecorderpb.EventData_AlertmanagerStartupEvent{ + AlertmanagerStartupEvent: &eventrecorderpb.AlertmanagerStartupEvent{ + Version: version.Version, + BuildContext: version.BuildContext(), + }, }, - }, + } }) a.onStop("shutdown event", func() error { - eventRec.RecordEvent(recordCtx, &eventrecorderpb.EventData{ - EventType: &eventrecorderpb.EventData_AlertmanagerShutdownEvent{ - AlertmanagerShutdownEvent: &eventrecorderpb.AlertmanagerShutdownEvent{}, - }, + eventRec.RecordEvent(recordCtx, func() *eventrecorderpb.EventData { + return &eventrecorderpb.EventData{ + EventType: &eventrecorderpb.EventData_AlertmanagerShutdownEvent{ + AlertmanagerShutdownEvent: &eventrecorderpb.AlertmanagerShutdownEvent{}, + }, + } }) return nil }) diff --git a/dispatch/dispatch.go b/dispatch/dispatch.go index fea22a215a..8be6a237cc 100644 --- a/dispatch/dispatch.go +++ b/dispatch/dispatch.go @@ -771,7 +771,9 @@ func (ag *aggrGroup) insert(ctx context.Context, alert *alert.Alert) bool { span.RecordError(err) ag.logger.Error(message, "err", err) } else { - ag.recorder.RecordEvent(ctx, notify.NewAlertGroupedEvent(ag.alertGroupInfo(), alert)) + ag.recorder.RecordEvent(ctx, func() *eventrecorderpb.EventData { + return notify.NewAlertGroupedEvent(ag.alertGroupInfo(), alert) + }) } return true } @@ -839,7 +841,9 @@ func (ag *aggrGroup) recordResolvedEvents(resolved types.AlertSlice) { } groupInfo := ag.alertGroupInfo() for _, a := range resolved { - ag.recorder.RecordEvent(ag.ctx, notify.NewAlertResolvedEvent(groupInfo, a)) + ag.recorder.RecordEvent(ag.ctx, func() *eventrecorderpb.EventData { + return notify.NewAlertResolvedEvent(groupInfo, a) + }) } } diff --git a/eventrecorder/recorder.go b/eventrecorder/recorder.go index 49f978cddf..808eef97a3 100644 --- a/eventrecorder/recorder.go +++ b/eventrecorder/recorder.go @@ -302,10 +302,16 @@ func (c *sharedRecorder) marshalAndSend(req writeRequest, outputs []Destination) // event is dropped (never blocks the caller). Recording only occurs // when the context has been decorated with WithEventRecording. // +// The event is supplied as a builder function rather than a value so +// that callers on hot read paths do not pay to construct an event +// (protobuf conversions, fingerprint slices, etc.) that would only be +// discarded when recording is disabled. The builder is invoked only +// after the recording gates pass, and exactly once. +// // The expensive protojson.Marshal call is deferred to the write-loop // goroutine so that the caller's hot path only pays for the proto // wrapping and a channel send. -func (r Recorder) RecordEvent(ctx context.Context, event *eventrecorderpb.EventData) { +func (r Recorder) RecordEvent(ctx context.Context, build func() *eventrecorderpb.EventData) { if r.core == nil || r.core.events == nil { return } @@ -313,6 +319,7 @@ func (r Recorder) RecordEvent(ctx context.Context, event *eventrecorderpb.EventD return } + event := build() eventType := extractEventType(event) wrappedEvent := &eventrecorderpb.Event{ diff --git a/eventrecorder/recorder_test.go b/eventrecorder/recorder_test.go index 643862ed94..9dfa797a15 100644 --- a/eventrecorder/recorder_test.go +++ b/eventrecorder/recorder_test.go @@ -84,7 +84,7 @@ func TestRecordEvent(t *testing.T) { rec := newTestRecorder(out) defer rec.Close() - rec.RecordEvent(recordCtx(), startupEvent()) + rec.RecordEvent(recordCtx(), startupEvent) // Wait for the event to be delivered. require.Eventually(t, func() bool { @@ -98,7 +98,7 @@ func TestRecordEventMultipleDestinations(t *testing.T) { rec := newTestRecorder(out1, out2) defer rec.Close() - rec.RecordEvent(recordCtx(), startupEvent()) + rec.RecordEvent(recordCtx(), startupEvent) require.Eventually(t, func() bool { return out1.eventCount() == 1 && out2.eventCount() == 1 @@ -107,7 +107,7 @@ func TestRecordEventMultipleDestinations(t *testing.T) { func TestNopRecorderDoesNotPanic(t *testing.T) { rec := NopRecorder() - rec.RecordEvent(recordCtx(), startupEvent()) + rec.RecordEvent(recordCtx(), startupEvent) rec.ApplyConfig(Config{}) rec.SetClusterPeer(nil) require.NoError(t, rec.Close()) @@ -115,7 +115,7 @@ func TestNopRecorderDoesNotPanic(t *testing.T) { func TestZeroRecorderDoesNotPanic(t *testing.T) { var rec Recorder - rec.RecordEvent(recordCtx(), startupEvent()) + rec.RecordEvent(recordCtx(), startupEvent) rec.ApplyConfig(Config{}) rec.SetClusterPeer(nil) require.NoError(t, rec.Close()) @@ -128,7 +128,7 @@ func TestZeroRecorderDoesNotPanic(t *testing.T) { func TestNewRecorderFromConfig_NilLogger(t *testing.T) { require.NotPanics(t, func() { rec := NewRecorderFromConfig(Config{}, "test-host", nil, nil) - rec.RecordEvent(recordCtx(), startupEvent()) + rec.RecordEvent(recordCtx(), startupEvent) rec.ApplyConfig(Config{}) require.NoError(t, rec.Close()) }) @@ -140,10 +140,10 @@ func TestRecordingNotEnabledByDefault(t *testing.T) { defer rec.Close() // Without WithEventRecording, events should be silently discarded. - rec.RecordEvent(context.Background(), startupEvent()) + rec.RecordEvent(context.Background(), startupEvent) // Record an event with recording enabled to flush the queue. - rec.RecordEvent(recordCtx(), startupEvent()) + rec.RecordEvent(recordCtx(), startupEvent) require.Eventually(t, func() bool { return out.eventCount() == 1 }, time.Second, 10*time.Millisecond) @@ -155,7 +155,7 @@ func TestApplyConfig(t *testing.T) { defer rec.Close() // Record one event to the initial destination. - rec.RecordEvent(recordCtx(), startupEvent()) + rec.RecordEvent(recordCtx(), startupEvent) require.Eventually(t, func() bool { return out1.eventCount() == 1 }, time.Second, 10*time.Millisecond) @@ -164,7 +164,7 @@ func TestApplyConfig(t *testing.T) { rec.ApplyConfig(Config{}) // Events still flow to the same output after no-op reload. - rec.RecordEvent(recordCtx(), startupEvent()) + rec.RecordEvent(recordCtx(), startupEvent) require.Eventually(t, func() bool { return out1.eventCount() == 2 }, time.Second, 10*time.Millisecond) @@ -194,7 +194,7 @@ func TestMarshalAndSend_DeliversEvent(t *testing.T) { rec := newTestRecorder(out) defer rec.Close() - rec.RecordEvent(recordCtx(), startupEvent()) + rec.RecordEvent(recordCtx(), startupEvent) require.Eventually(t, func() bool { out.mu.Lock() diff --git a/inhibit/inhibit.go b/inhibit/inhibit.go index e3b2c8c36a..1489ae0dac 100644 --- a/inhibit/inhibit.go +++ b/inhibit/inhibit.go @@ -223,11 +223,13 @@ func (ih *Inhibitor) Mutes(ctx context.Context, lset model.LabelSet) bool { ), ) - ih.recorder.RecordEvent(ctx, eventrecorder.NewInhibitionMutedAlertEvent( - []*eventrecorderpb.InhibitRule{eventrecorder.InhibitRuleAsProto(r.SourceMatchers, r.TargetMatchers, r.Equal)}, - fp, lset, - []model.Fingerprint{inhibitedByFP}, - )) + ih.recorder.RecordEvent(ctx, func() *eventrecorderpb.EventData { + return eventrecorder.NewInhibitionMutedAlertEvent( + []*eventrecorderpb.InhibitRule{eventrecorder.InhibitRuleAsProto(r.SourceMatchers, r.TargetMatchers, r.Equal)}, + fp, lset, + []model.Fingerprint{inhibitedByFP}, + ) + }) return true } } diff --git a/notify/retry_stage.go b/notify/retry_stage.go index 7734ffe4f1..4c5723a9a6 100644 --- a/notify/retry_stage.go +++ b/notify/retry_stage.go @@ -27,6 +27,7 @@ import ( "github.com/prometheus/alertmanager/alert" "github.com/prometheus/alertmanager/eventrecorder" + "github.com/prometheus/alertmanager/eventrecorder/eventrecorderpb" ) // RetryStage notifies via passed integration with exponential backoff until it @@ -178,7 +179,9 @@ func (r RetryStage) exec(ctx context.Context, l *slog.Logger, alerts ...*alert.A l.Info("Notify success") } - r.recorder.RecordEvent(ctx, NewNotificationEvent(ctx, sent, r.integration)) + r.recorder.RecordEvent(ctx, func() *eventrecorderpb.EventData { + return NewNotificationEvent(ctx, sent, r.integration) + }) return ctx, alerts, nil } case <-ctx.Done(): diff --git a/provider/mem/mem.go b/provider/mem/mem.go index d4e662804b..7646eb9fdf 100644 --- a/provider/mem/mem.go +++ b/provider/mem/mem.go @@ -29,6 +29,7 @@ import ( "go.opentelemetry.io/otel/trace" "github.com/prometheus/alertmanager/eventrecorder" + "github.com/prometheus/alertmanager/eventrecorder/eventrecorderpb" "github.com/prometheus/alertmanager/featurecontrol" "github.com/prometheus/alertmanager/provider" "github.com/prometheus/alertmanager/store" @@ -347,7 +348,9 @@ func (a *Alerts) Put(ctx context.Context, alerts ...*types.Alert) error { a.callback.PostStore(alert, existing) if !existing { - a.recorder.RecordEvent(ctx, eventrecorder.NewAlertCreatedEvent(alert)) + a.recorder.RecordEvent(ctx, func() *eventrecorderpb.EventData { + return eventrecorder.NewAlertCreatedEvent(alert) + }) } metadata := map[string]string{} diff --git a/silence/silence.go b/silence/silence.go index d2fd4f10ee..7fdb852cf4 100644 --- a/silence/silence.go +++ b/silence/silence.go @@ -48,6 +48,7 @@ import ( "github.com/prometheus/alertmanager/alert" "github.com/prometheus/alertmanager/cluster" "github.com/prometheus/alertmanager/eventrecorder" + "github.com/prometheus/alertmanager/eventrecorder/eventrecorderpb" "github.com/prometheus/alertmanager/marker" "github.com/prometheus/alertmanager/matcher/compat" "github.com/prometheus/alertmanager/pkg/labels" @@ -285,9 +286,11 @@ func (s *Silencer) Mutes(ctx context.Context, lset model.LabelSet) bool { activeIDs = append(activeIDs, sil.Id) allIDs = append(allIDs, sil.Id) - s.recorder.RecordEvent(ctx, eventrecorder.NewSilenceMutedAlertEvent( - eventrecorder.SilenceAsProto(sil), fp, lset, - )) + s.recorder.RecordEvent(ctx, func() *eventrecorderpb.EventData { + return eventrecorder.NewSilenceMutedAlertEvent( + eventrecorder.SilenceAsProto(sil), fp, lset, + ) + }) default: // Do nothing, silence has expired in the meantime. } @@ -877,9 +880,11 @@ func (s *Silences) Set(ctx context.Context, sil *pb.Silence) error { return err } if changed { - s.recorder.RecordEvent(ctx, eventrecorder.NewSilenceUpdatedEvent( - eventrecorder.SilenceAsProto(sil), - )) + s.recorder.RecordEvent(ctx, func() *eventrecorderpb.EventData { + return eventrecorder.NewSilenceUpdatedEvent( + eventrecorder.SilenceAsProto(sil), + ) + }) } return nil } @@ -925,9 +930,11 @@ func (s *Silences) Set(ctx context.Context, sil *pb.Silence) error { return err } if added { - s.recorder.RecordEvent(ctx, eventrecorder.NewSilenceCreatedEvent( - eventrecorder.SilenceAsProto(sil), - )) + s.recorder.RecordEvent(ctx, func() *eventrecorderpb.EventData { + return eventrecorder.NewSilenceCreatedEvent( + eventrecorder.SilenceAsProto(sil), + ) + }) } return nil } From 9c7bee5306e783c39f1e4b1925bbb99a5a25906e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Jun 2026 11:34:31 +0200 Subject: [PATCH 005/120] build(deps): bump golang.org/x/mod from 0.35.0 to 0.36.0 (#5272) Bumps [golang.org/x/mod](https://github.com/golang/mod) from 0.35.0 to 0.36.0. - [Commits](https://github.com/golang/mod/compare/v0.35.0...v0.36.0) --- updated-dependencies: - dependency-name: golang.org/x/mod dependency-version: 0.36.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index e916cdbb05..10bccb47ea 100644 --- a/go.mod +++ b/go.mod @@ -53,7 +53,7 @@ require ( go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 go.opentelemetry.io/otel/sdk v1.43.0 go.opentelemetry.io/otel/trace v1.43.0 - golang.org/x/mod v0.35.0 + golang.org/x/mod v0.36.0 golang.org/x/net v0.55.0 golang.org/x/text v0.37.0 google.golang.org/grpc v1.80.0 diff --git a/go.sum b/go.sum index a093e4efdf..582a7ddeb8 100644 --- a/go.sum +++ b/go.sum @@ -662,8 +662,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM= -golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU= +golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4= +golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= From 256d24e0a57ff737d6ab00387c0a059723e1bf48 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Jun 2026 11:36:29 +0200 Subject: [PATCH 006/120] build(deps): bump actions/stale from 10.2.0 to 10.3.0 (#5275) Bumps [actions/stale](https://github.com/actions/stale) from 10.2.0 to 10.3.0. - [Release notes](https://github.com/actions/stale/releases) - [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/stale/compare/b5d41d4e1d5dceea10e7104786b73624c18a190f...eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899) --- updated-dependencies: - dependency-name: actions/stale dependency-version: 10.3.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/stale.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index b29097c400..74d037f8f1 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -11,7 +11,7 @@ jobs: if: github.repository_owner == 'prometheus' || github.repository_owner == 'prometheus-community' # Don't run this workflow on forks. runs-on: ubuntu-latest steps: - - uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0 + - uses: actions/stale@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 # v10.3.0 with: repo-token: ${{ secrets.GITHUB_TOKEN }} # opt out of defaults to avoid marking issues as stale and closing them From 57f44e7fd4415be095d9ab72235a22d1c7d1e916 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Jun 2026 11:37:17 +0200 Subject: [PATCH 007/120] build(deps): bump actions/checkout from 6.0.2 to 6.0.3 (#5274) Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.2 to 6.0.3. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/de0fac2e4500dabe0009e67214ff5f5447ce83dd...df4cb1c069e1874edd31b4311f1884172cec0e10) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: 6.0.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 6 +++--- .github/workflows/container_description.yml | 4 ++-- .github/workflows/mixin.yml | 2 +- .github/workflows/release.yml | 2 +- .github/workflows/ui-ci.yml | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 238d7c4c7c..51e1f4c651 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,7 +8,7 @@ jobs: name: Test alertmanager frontend runs-on: ubuntu-latest steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: persist-credentials: false - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 @@ -42,7 +42,7 @@ jobs: matrix: thread: [0, 1, 2] steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.0.0 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v4.0.0 with: persist-credentials: false - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 @@ -76,7 +76,7 @@ jobs: EMAIL_NO_AUTH_CONFIG: testdata/noauth.yml EMAIL_AUTH_CONFIG: testdata/auth.yml steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: persist-credentials: false - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 diff --git a/.github/workflows/container_description.yml b/.github/workflows/container_description.yml index d7b879f9c7..3bb36ccf43 100644 --- a/.github/workflows/container_description.yml +++ b/.github/workflows/container_description.yml @@ -18,7 +18,7 @@ jobs: if: github.repository_owner == 'prometheus' || github.repository_owner == 'prometheus-community' # Don't run this workflow on forks. steps: - name: git checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: persist-credentials: false - name: Set docker hub repo name @@ -42,7 +42,7 @@ jobs: if: github.repository_owner == 'prometheus' || github.repository_owner == 'prometheus-community' # Don't run this workflow on forks. steps: - name: git checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: persist-credentials: false - name: Set quay.io org name diff --git a/.github/workflows/mixin.yml b/.github/workflows/mixin.yml index 5280dd648d..a21dfada37 100644 --- a/.github/workflows/mixin.yml +++ b/.github/workflows/mixin.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: persist-credentials: false - name: install Go diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b2b9177620..edc2cdd460 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,7 +17,7 @@ jobs: thread: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] needs: ci steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: persist-credentials: false - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 diff --git a/.github/workflows/ui-ci.yml b/.github/workflows/ui-ci.yml index 4b3ef3efb8..273f691a8d 100644 --- a/.github/workflows/ui-ci.yml +++ b/.github/workflows/ui-ci.yml @@ -20,7 +20,7 @@ jobs: run: working-directory: ./ui/mantine-ui steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: persist-credentials: false - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 From 141a2e33c46d14a9758908bc07ce19045e601f1d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Jun 2026 11:38:26 +0200 Subject: [PATCH 008/120] build(deps): bump the mantine group in /ui/mantine-ui with 3 updates (#5276) Bumps the mantine group in /ui/mantine-ui with 3 updates: [@mantine/code-highlight](https://github.com/mantinedev/mantine/tree/HEAD/packages/@mantine/code-highlight), [@mantine/core](https://github.com/mantinedev/mantine/tree/HEAD/packages/@mantine/core) and [@mantine/hooks](https://github.com/mantinedev/mantine/tree/HEAD/packages/@mantine/hooks). Updates `@mantine/code-highlight` from 9.1.1 to 9.3.0 - [Release notes](https://github.com/mantinedev/mantine/releases) - [Changelog](https://github.com/mantinedev/mantine/blob/master/CHANGELOG.md) - [Commits](https://github.com/mantinedev/mantine/commits/9.3.0/packages/@mantine/code-highlight) Updates `@mantine/core` from 9.1.1 to 9.3.0 - [Release notes](https://github.com/mantinedev/mantine/releases) - [Changelog](https://github.com/mantinedev/mantine/blob/master/CHANGELOG.md) - [Commits](https://github.com/mantinedev/mantine/commits/9.3.0/packages/@mantine/core) Updates `@mantine/hooks` from 9.1.1 to 9.3.0 - [Release notes](https://github.com/mantinedev/mantine/releases) - [Changelog](https://github.com/mantinedev/mantine/blob/master/CHANGELOG.md) - [Commits](https://github.com/mantinedev/mantine/commits/9.3.0/packages/@mantine/hooks) --- updated-dependencies: - dependency-name: "@mantine/code-highlight" dependency-version: 9.3.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: mantine - dependency-name: "@mantine/core" dependency-version: 9.3.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: mantine - dependency-name: "@mantine/hooks" dependency-version: 9.3.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: mantine ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- ui/mantine-ui/package-lock.json | 38 +++++++++++---------------------- ui/mantine-ui/package.json | 2 +- 2 files changed, 14 insertions(+), 26 deletions(-) diff --git a/ui/mantine-ui/package-lock.json b/ui/mantine-ui/package-lock.json index 474ec76803..9cfbda62a0 100644 --- a/ui/mantine-ui/package-lock.json +++ b/ui/mantine-ui/package-lock.json @@ -8,7 +8,7 @@ "name": "alertmanager", "version": "0.0.0", "dependencies": { - "@mantine/code-highlight": "^9.1.1", + "@mantine/code-highlight": "^9.3.0", "@mantine/core": "^9.0.0", "@mantine/hooks": "^9.0.0", "@tanstack/react-query": "^5.100.9", @@ -200,9 +200,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT OR Apache-2.0", "optional": true, "os": [ @@ -220,9 +217,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT OR Apache-2.0", "optional": true, "os": [ @@ -240,9 +234,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT OR Apache-2.0", "optional": true, "os": [ @@ -260,9 +251,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT OR Apache-2.0", "optional": true, "os": [ @@ -572,24 +560,24 @@ "license": "MIT" }, "node_modules/@mantine/code-highlight": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/@mantine/code-highlight/-/code-highlight-9.1.1.tgz", - "integrity": "sha512-+NS5RwozXQZacKc1MzKwFf5auueyIPGpi9CTz0VWeefMeKhvtwfG1HUJbQGZa3YLdFTT7MzU6Id9M8vjzBdDPA==", + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@mantine/code-highlight/-/code-highlight-9.3.0.tgz", + "integrity": "sha512-fS/2Hzyj63PMZgq1H2JLYZR2Cr0hvq6Ax/IjgGrEMSeWamzsAB2DKUZvAkdTH3KYzVUs+pxDktF1ZW3kf1oSng==", "license": "MIT", "dependencies": { "clsx": "^2.1.1" }, "peerDependencies": { - "@mantine/core": "9.1.1", - "@mantine/hooks": "9.1.1", + "@mantine/core": "9.3.0", + "@mantine/hooks": "9.3.0", "react": "^19.2.0", "react-dom": "^19.2.0" } }, "node_modules/@mantine/core": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/@mantine/core/-/core-9.1.1.tgz", - "integrity": "sha512-vClOZdCeZ4oLYuA/3jAOgKGQ6dXbF6ZkzpYz09Gied9nZpB7HcQeb3dcMh8UPBE4f+EM7KlYWk6dch7GoASeaA==", + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@mantine/core/-/core-9.3.0.tgz", + "integrity": "sha512-mHVCm61YVW9ipy9eHiKMqsRUm3TkOErbdw7zHs0HRw5g403nf7tSTqNGvaYE+aX1Py874qMkrUzeQfj4bjiiBA==", "license": "MIT", "dependencies": { "@floating-ui/react": "^0.27.19", @@ -599,15 +587,15 @@ "type-fest": "^5.6.0" }, "peerDependencies": { - "@mantine/hooks": "9.1.1", + "@mantine/hooks": "9.3.0", "react": "^19.2.0", "react-dom": "^19.2.0" } }, "node_modules/@mantine/hooks": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/@mantine/hooks/-/hooks-9.1.1.tgz", - "integrity": "sha512-tTJK73nGFyy1v214TLdvBq0be7QCoc6osfbXVuJgOH3YG85lWk9Mvvor6k+w6hC6HXSqKMqLKePyiGm83xGcMg==", + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@mantine/hooks/-/hooks-9.3.0.tgz", + "integrity": "sha512-QoSr9WI4WsKWrM3qFYYizHUn3+n+CVcFMYe4sdlnmFPStvs6BacPODKJSbFlYl73Z20t82JIy0eKqt4noHQI2g==", "license": "MIT", "peerDependencies": { "react": "^19.2.0" diff --git a/ui/mantine-ui/package.json b/ui/mantine-ui/package.json index fabbb08d6c..547222fc45 100644 --- a/ui/mantine-ui/package.json +++ b/ui/mantine-ui/package.json @@ -18,7 +18,7 @@ "test": "npm run typecheck && npm run check && npm run vitest && npm run build" }, "dependencies": { - "@mantine/code-highlight": "^9.1.1", + "@mantine/code-highlight": "^9.3.0", "@mantine/core": "^9.0.0", "@mantine/hooks": "^9.0.0", "@tanstack/react-query": "^5.100.9", From 684e4589302d1c795cf23ded7f4e8bffc9ac1420 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Jun 2026 11:39:27 +0200 Subject: [PATCH 009/120] build(deps): bump the react group in /ui/mantine-ui with 4 updates (#5277) Bumps the react group in /ui/mantine-ui with 4 updates: [react](https://github.com/facebook/react/tree/HEAD/packages/react), [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react), [react-dom](https://github.com/facebook/react/tree/HEAD/packages/react-dom) and [react-router-dom](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router-dom). Updates `react` from 19.2.5 to 19.2.7 - [Release notes](https://github.com/facebook/react/releases) - [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/react/commits/v19.2.7/packages/react) Updates `@types/react` from 19.2.14 to 19.2.16 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react) Updates `react-dom` from 19.2.5 to 19.2.7 - [Release notes](https://github.com/facebook/react/releases) - [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/react/commits/v19.2.7/packages/react-dom) Updates `react-router-dom` from 7.14.2 to 7.16.0 - [Release notes](https://github.com/remix-run/react-router/releases) - [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router-dom/CHANGELOG.md) - [Commits](https://github.com/remix-run/react-router/commits/react-router-dom@7.16.0/packages/react-router-dom) Updates `@types/react` from 19.2.14 to 19.2.16 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react) --- updated-dependencies: - dependency-name: react dependency-version: 19.2.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: react - dependency-name: "@types/react" dependency-version: 19.2.16 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: react - dependency-name: react-dom dependency-version: 19.2.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: react - dependency-name: react-router-dom dependency-version: 7.16.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: react - dependency-name: "@types/react" dependency-version: 19.2.16 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: react ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- ui/mantine-ui/package-lock.json | 42 ++++++++++++++++----------------- ui/mantine-ui/package.json | 8 +++---- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/ui/mantine-ui/package-lock.json b/ui/mantine-ui/package-lock.json index 9cfbda62a0..ef5b4f2686 100644 --- a/ui/mantine-ui/package-lock.json +++ b/ui/mantine-ui/package-lock.json @@ -13,9 +13,9 @@ "@mantine/hooks": "^9.0.0", "@tanstack/react-query": "^5.100.9", "highlight.js": "^11.11.1", - "react": "^19.2.5", - "react-dom": "^19.2.5", - "react-router-dom": "^7.14.2" + "react": "^19.2.7", + "react-dom": "^19.2.7", + "react-router-dom": "^7.16.0" }, "devDependencies": { "@biomejs/biome": "^2.4.14", @@ -24,7 +24,7 @@ "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", "@types/node": "^25.6.0", - "@types/react": "^19.2.14", + "@types/react": "^19.2.16", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^6.0.1", "jsdom": "^29.1.1", @@ -1080,9 +1080,9 @@ } }, "node_modules/@types/react": { - "version": "19.2.14", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", - "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "version": "19.2.16", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.16.tgz", + "integrity": "sha512-esJiCAnl0kfpNdE69f3So4WJUXy95dLZydX0KwK46riIHDzHM7O9Vtf9xCHW0PXIqvgqNrswl522kA/5yx+F4w==", "devOptional": true, "license": "MIT", "dependencies": { @@ -2206,24 +2206,24 @@ } }, "node_modules/react": { - "version": "19.2.5", - "resolved": "https://registry.npmjs.org/react/-/react-19.2.5.tgz", - "integrity": "sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA==", + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz", + "integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==", "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/react-dom": { - "version": "19.2.5", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.5.tgz", - "integrity": "sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag==", + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.7.tgz", + "integrity": "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==", "license": "MIT", "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { - "react": "^19.2.5" + "react": "^19.2.7" } }, "node_modules/react-is": { @@ -2291,9 +2291,9 @@ } }, "node_modules/react-router": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.14.2.tgz", - "integrity": "sha512-yCqNne6I8IB6rVCH7XUvlBK7/QKyqypBFGv+8dj4QBFJiiRX+FG7/nkdAvGElyvVZ/HQP5N19wzteuTARXi5Gw==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.16.0.tgz", + "integrity": "sha512-wArC8lVyJb3+jM9OpDyW6hLCizACWkvQR/sSGqSs+o5uEXEtGlqdZ4v8hENR3Jad6i+LRkK93q/+bQAcvl6V1A==", "license": "MIT", "dependencies": { "cookie": "^1.0.1", @@ -2313,12 +2313,12 @@ } }, "node_modules/react-router-dom": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.14.2.tgz", - "integrity": "sha512-YZcM5ES8jJSM+KrJ9BdvHHqlnGTg5tH3sC5ChFRj4inosKctdyzBDhOyyHdGk597q2OT6NTrCA1OvB/YDwfekQ==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.16.0.tgz", + "integrity": "sha512-kMUAbimWB5FVbF4Bce4bJsiKJWLIUHq/mEG8+CFDnCSgltptBiG5nguducmsJeGKytlCvQud9Qhzpn49iduTlA==", "license": "MIT", "dependencies": { - "react-router": "7.14.2" + "react-router": "7.16.0" }, "engines": { "node": ">=20.0.0" diff --git a/ui/mantine-ui/package.json b/ui/mantine-ui/package.json index 547222fc45..826613a79e 100644 --- a/ui/mantine-ui/package.json +++ b/ui/mantine-ui/package.json @@ -23,9 +23,9 @@ "@mantine/hooks": "^9.0.0", "@tanstack/react-query": "^5.100.9", "highlight.js": "^11.11.1", - "react": "^19.2.5", - "react-dom": "^19.2.5", - "react-router-dom": "^7.14.2" + "react": "^19.2.7", + "react-dom": "^19.2.7", + "react-router-dom": "^7.16.0" }, "devDependencies": { "@biomejs/biome": "^2.4.14", @@ -34,7 +34,7 @@ "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", "@types/node": "^25.6.0", - "@types/react": "^19.2.14", + "@types/react": "^19.2.16", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^6.0.1", "jsdom": "^29.1.1", From b743baf24b91ecfbc19c6f084a0206c6539c4eed Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Jun 2026 11:40:21 +0200 Subject: [PATCH 010/120] build(deps-dev): bump vitest in /ui/mantine-ui in the testing group (#5278) Bumps the testing group in /ui/mantine-ui with 1 update: [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest). Updates `vitest` from 4.1.5 to 4.1.8 - [Release notes](https://github.com/vitest-dev/vitest/releases) - [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md) - [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.8/packages/vitest) --- updated-dependencies: - dependency-name: vitest dependency-version: 4.1.8 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: testing ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- ui/mantine-ui/package-lock.json | 96 ++++++++++++++++----------------- ui/mantine-ui/package.json | 2 +- 2 files changed, 49 insertions(+), 49 deletions(-) diff --git a/ui/mantine-ui/package-lock.json b/ui/mantine-ui/package-lock.json index ef5b4f2686..8c509af41b 100644 --- a/ui/mantine-ui/package-lock.json +++ b/ui/mantine-ui/package-lock.json @@ -35,7 +35,7 @@ "typescript": "^5.9.3", "vite": "^8.0.10", "vite-tsconfig-paths": "^6.1.1", - "vitest": "^4.1.5" + "vitest": "^4.1.8" } }, "node_modules/@adobe/css-tools": { @@ -1063,9 +1063,9 @@ "license": "MIT" }, "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", "dev": true, "license": "MIT" }, @@ -1126,16 +1126,16 @@ } }, "node_modules/@vitest/expect": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.5.tgz", - "integrity": "sha512-PWBaRY5JoKuRnHlUHfpV/KohFylaDZTupcXN1H9vYryNLOnitSw60Mw9IAE2r67NbwwzBw/Cc/8q9BK3kIX8Kw==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.8.tgz", + "integrity": "sha512-h3nDO677RDLEGlBxyQ5CW8RlMThSKSRLUePLOx09gNIWRL40edgA1GCZSZgf1W55MFAG6/Sw14KeaAnqv0NKdQ==", "dev": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", - "@vitest/spy": "4.1.5", - "@vitest/utils": "4.1.5", + "@vitest/spy": "4.1.8", + "@vitest/utils": "4.1.8", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" }, @@ -1144,13 +1144,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.5.tgz", - "integrity": "sha512-/x2EmFC4mT4NNzqvC3fmesuV97w5FC903KPmey4gsnJiMQ3Be1IlDKVaDaG8iqaLFHqJ2FVEkxZk5VmeLjIItw==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.8.tgz", + "integrity": "sha512-LEiN/xe4OSIbKe9HQIp5OC24agGD9J5CnmMgsLohVVoOPWL9a2sBoR6VBx43jQZb7Kr1l4RCuyCJzcAa0+dojw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "4.1.5", + "@vitest/spy": "4.1.8", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, @@ -1171,9 +1171,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.5.tgz", - "integrity": "sha512-7I3q6l5qr03dVfMX2wCo9FxwSJbPdwKjy2uu/YPpU3wfHvIL4QHwVRp57OfGrDFeUJ8/8QdfBKIV12FTtLn00g==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.8.tgz", + "integrity": "sha512-9GasEBxpZ1VYIpqHf/0+YGg121uSNwCKOJqIrTwWP/TB7DmFCiaBpNl3aPZzoLWfWkuqhbH8vJIVobZkvdo2cA==", "dev": true, "license": "MIT", "dependencies": { @@ -1184,13 +1184,13 @@ } }, "node_modules/@vitest/runner": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.5.tgz", - "integrity": "sha512-2D+o7Pr82IEO46YPpoA/YU0neeyr6FTerQb5Ro7BUnBuv6NQtT/kmVnczngiMEBhzgqz2UZYl5gArejsyERDSQ==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.8.tgz", + "integrity": "sha512-EmVxeBAfMJvycdjd6Hm+RbFBbA9fKvo0Kx37hNpBYoYeavH3RNsBXWDooR1mgD52dCrxIIuP7UotpfiwOikvcg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.1.5", + "@vitest/utils": "4.1.8", "pathe": "^2.0.3" }, "funding": { @@ -1198,14 +1198,14 @@ } }, "node_modules/@vitest/snapshot": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.5.tgz", - "integrity": "sha512-zypXEt4KH/XgKGPUz4eC2AvErYx0My5hfL8oDb1HzGFpEk1P62bxSohdyOmvz+d9UJwanI68MKwr2EquOaOgMQ==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.8.tgz", + "integrity": "sha512-acfZboRmAIf05DEKcBQy33VXojFJjtUdLyo7oOmV9kebb2xdU01UknNiPuPZoJZQyO7DF0gZdTGTpeAzET9QPQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.5", - "@vitest/utils": "4.1.5", + "@vitest/pretty-format": "4.1.8", + "@vitest/utils": "4.1.8", "magic-string": "^0.30.21", "pathe": "^2.0.3" }, @@ -1214,9 +1214,9 @@ } }, "node_modules/@vitest/spy": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.5.tgz", - "integrity": "sha512-2lNOsh6+R2Idnf1TCZqSwYlKN2E/iDlD8sgU59kYVl+OMDmvldO1VDk39smRfpUNwYpNRVn3w4YfuC7KfbBnkQ==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.8.tgz", + "integrity": "sha512-6EevtBp6OZOPF7bmz36HrGMeP3txgVSrgebWxHOafDXGkhIzfXK14f8KF6MuFfgXXUeHxmpD3BQxkV00/3s5mA==", "dev": true, "license": "MIT", "funding": { @@ -1224,13 +1224,13 @@ } }, "node_modules/@vitest/utils": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.5.tgz", - "integrity": "sha512-76wdkrmfXfqGjueGgnb45ITPyUi1ycZ4IHgC2bhPDUfWHklY/q3MdLOAB+TF1e6xfl8NxNY0ZYaPCFNWSsw3Ug==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.8.tgz", + "integrity": "sha512-uOJamYALNhfJ6iolExyQM40yIQwDqYnkKtQ5VCiSe17E33H0aQ/u+1GlRuz4LZBk6Mm3sg90G9hEbmEt37C1Zg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.5", + "@vitest/pretty-format": "4.1.8", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" }, @@ -2883,19 +2883,19 @@ } }, "node_modules/vitest": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.5.tgz", - "integrity": "sha512-9Xx1v3/ih3m9hN+SbfkUyy0JAs72ap3r7joc87XL6jwF0jGg6mFBvQ1SrwaX+h8BlkX6Hz9shdd1uo6AF+ZGpg==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.8.tgz", + "integrity": "sha512-flY6ScbCIt9HThs+C5HS7jvGOB560DJtk/Z15IQROTA6zEy49Nh8T/dofWTQL+n3vswqn87sbJNiuqw1SDp5Ig==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "4.1.5", - "@vitest/mocker": "4.1.5", - "@vitest/pretty-format": "4.1.5", - "@vitest/runner": "4.1.5", - "@vitest/snapshot": "4.1.5", - "@vitest/spy": "4.1.5", - "@vitest/utils": "4.1.5", + "@vitest/expect": "4.1.8", + "@vitest/mocker": "4.1.8", + "@vitest/pretty-format": "4.1.8", + "@vitest/runner": "4.1.8", + "@vitest/snapshot": "4.1.8", + "@vitest/spy": "4.1.8", + "@vitest/utils": "4.1.8", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", @@ -2923,12 +2923,12 @@ "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.1.5", - "@vitest/browser-preview": "4.1.5", - "@vitest/browser-webdriverio": "4.1.5", - "@vitest/coverage-istanbul": "4.1.5", - "@vitest/coverage-v8": "4.1.5", - "@vitest/ui": "4.1.5", + "@vitest/browser-playwright": "4.1.8", + "@vitest/browser-preview": "4.1.8", + "@vitest/browser-webdriverio": "4.1.8", + "@vitest/coverage-istanbul": "4.1.8", + "@vitest/coverage-v8": "4.1.8", + "@vitest/ui": "4.1.8", "happy-dom": "*", "jsdom": "*", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" diff --git a/ui/mantine-ui/package.json b/ui/mantine-ui/package.json index 826613a79e..5019ed870a 100644 --- a/ui/mantine-ui/package.json +++ b/ui/mantine-ui/package.json @@ -45,6 +45,6 @@ "typescript": "^5.9.3", "vite": "^8.0.10", "vite-tsconfig-paths": "^6.1.1", - "vitest": "^4.1.5" + "vitest": "^4.1.8" } } From cf886fd45ba567c40a9dc25798234caac918d6e9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Jun 2026 11:43:19 +0200 Subject: [PATCH 011/120] build(deps): bump github.com/prometheus/common from 0.67.5 to 0.68.1 (#5271) Bumps [github.com/prometheus/common](https://github.com/prometheus/common) from 0.67.5 to 0.68.1. - [Release notes](https://github.com/prometheus/common/releases) - [Changelog](https://github.com/prometheus/common/blob/main/CHANGELOG.md) - [Commits](https://github.com/prometheus/common/compare/v0.67.5...v0.68.1) --- updated-dependencies: - dependency-name: github.com/prometheus/common dependency-version: 0.68.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 10bccb47ea..08d368456c 100644 --- a/go.mod +++ b/go.mod @@ -36,7 +36,7 @@ require ( github.com/oklog/ulid/v2 v2.1.1 github.com/prometheus/client_golang v1.23.2 github.com/prometheus/client_model v0.6.2 - github.com/prometheus/common v0.67.5 + github.com/prometheus/common v0.68.1 github.com/prometheus/exporter-toolkit v0.16.0 github.com/prometheus/sigv4 v0.4.1 github.com/rs/cors v1.11.1 @@ -95,7 +95,7 @@ require ( github.com/go-openapi/swag/typeutils v0.26.0 // indirect github.com/go-openapi/swag/yamlutils v0.26.0 // indirect github.com/go-viper/mapstructure/v2 v2.5.0 // indirect - github.com/golang-jwt/jwt/v5 v5.3.0 // indirect + github.com/golang-jwt/jwt/v5 v5.3.1 // indirect github.com/google/btree v1.1.3 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect @@ -125,7 +125,7 @@ require ( go.yaml.in/yaml/v2 v2.4.4 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/crypto v0.51.0 // indirect - golang.org/x/oauth2 v0.35.0 // indirect + golang.org/x/oauth2 v0.36.0 // indirect golang.org/x/sync v0.20.0 // indirect golang.org/x/sys v0.45.0 // indirect golang.org/x/time v0.15.0 // indirect diff --git a/go.sum b/go.sum index 582a7ddeb8..37abc2cb82 100644 --- a/go.sum +++ b/go.sum @@ -247,8 +247,8 @@ github.com/goccy/go-yaml v1.9.5/go.mod h1:U/jl18uSupI5rdI2jmuCswEA2htH9eXfferR3K github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= -github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= +github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= +github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -498,8 +498,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.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4= -github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw= +github.com/prometheus/common v0.68.1 h1:omjRRl4QP4komogpXuhfeOiisQg7xdy8VM1UY+pStaY= +github.com/prometheus/common v0.68.1/go.mod h1:ZzL3f6u94qUxh9p+tJTrF+FvBS1XXbbRAZCQkytAL0Y= github.com/prometheus/exporter-toolkit v0.16.0 h1:xT/j7L2XKF+VJd6B4fpUw6xWabHrSmsUf6mYmFqyu0s= github.com/prometheus/exporter-toolkit v0.16.0/go.mod h1:d1EL8Z9674xQe/iWhwP2wDyCEoBPbXVeqDbqAUsgJWY= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= @@ -733,8 +733,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.35.0 h1:Mv2mzuHuZuY2+bkyWXIHMfhNdJAdwW3FuWeCPYN5GVQ= -golang.org/x/oauth2 v0.35.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= +golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= 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= From 775d546a787340b9f5f89f996a8dfcae43647874 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Jun 2026 11:45:08 +0200 Subject: [PATCH 012/120] build(deps-dev): bump postcss in /ui/mantine-ui in the styles group (#5280) Bumps the styles group in /ui/mantine-ui with 1 update: [postcss](https://github.com/postcss/postcss). Updates `postcss` from 8.5.13 to 8.5.15 - [Release notes](https://github.com/postcss/postcss/releases) - [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md) - [Commits](https://github.com/postcss/postcss/compare/8.5.13...8.5.15) --- updated-dependencies: - dependency-name: postcss dependency-version: 8.5.15 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: styles ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- ui/mantine-ui/package-lock.json | 16 ++++++++-------- ui/mantine-ui/package.json | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ui/mantine-ui/package-lock.json b/ui/mantine-ui/package-lock.json index 8c509af41b..e1bc5ae618 100644 --- a/ui/mantine-ui/package-lock.json +++ b/ui/mantine-ui/package-lock.json @@ -28,7 +28,7 @@ "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^6.0.1", "jsdom": "^29.1.1", - "postcss": "^8.5.13", + "postcss": "^8.5.15", "postcss-preset-mantine": "1.18.0", "postcss-simple-vars": "^7.0.1", "prop-types": "^15.8.1", @@ -1927,9 +1927,9 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", "dev": true, "funding": [ { @@ -1994,9 +1994,9 @@ "license": "ISC" }, "node_modules/postcss": { - "version": "8.5.13", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.13.tgz", - "integrity": "sha512-qif0+jGGZoLWdHey3UFHHWP0H7Gbmsk8T5VEqyYFbWqPr1XqvLGBbk/sl8V5exGmcYJklJOhOQq1pV9IcsiFag==", + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", "dev": true, "funding": [ { @@ -2014,7 +2014,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.11", + "nanoid": "^3.3.12", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, diff --git a/ui/mantine-ui/package.json b/ui/mantine-ui/package.json index 5019ed870a..fd0ce8e436 100644 --- a/ui/mantine-ui/package.json +++ b/ui/mantine-ui/package.json @@ -38,7 +38,7 @@ "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^6.0.1", "jsdom": "^29.1.1", - "postcss": "^8.5.13", + "postcss": "^8.5.15", "postcss-preset-mantine": "1.18.0", "postcss-simple-vars": "^7.0.1", "prop-types": "^15.8.1", From 508cec5557204c50fa2aa83c22670c74b0566b1d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Jun 2026 11:46:16 +0200 Subject: [PATCH 013/120] build(deps): bump github.com/fsnotify/fsnotify from 1.10.0 to 1.10.1 (#5270) Bumps [github.com/fsnotify/fsnotify](https://github.com/fsnotify/fsnotify) from 1.10.0 to 1.10.1. - [Release notes](https://github.com/fsnotify/fsnotify/releases) - [Changelog](https://github.com/fsnotify/fsnotify/blob/main/CHANGELOG.md) - [Commits](https://github.com/fsnotify/fsnotify/compare/v1.10.0...v1.10.1) --- updated-dependencies: - dependency-name: github.com/fsnotify/fsnotify dependency-version: 1.10.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 08d368456c..fe8835c51c 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/coder/quartz v0.3.1 github.com/coreos/go-systemd/v22 v22.7.0 github.com/emersion/go-smtp v0.24.0 - github.com/fsnotify/fsnotify v1.10.0 + github.com/fsnotify/fsnotify v1.10.1 github.com/go-openapi/analysis v0.25.0 github.com/go-openapi/errors v0.22.7 github.com/go-openapi/loads v0.23.3 diff --git a/go.sum b/go.sum index 37abc2cb82..829edef587 100644 --- a/go.sum +++ b/go.sum @@ -171,8 +171,8 @@ github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2 github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= -github.com/fsnotify/fsnotify v1.10.0 h1:Xx/5Ydg9CeBDX/wi4VJqStNtohYjitZhhlHt4h3St1M= -github.com/fsnotify/fsnotify v1.10.0/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo= +github.com/fsnotify/fsnotify v1.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx59Ho= +github.com/fsnotify/fsnotify v1.10.1/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= From e688599a7191a5745a8ec0dd7ac2f43a3b5e539f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Jun 2026 11:48:22 +0200 Subject: [PATCH 014/120] build(deps-dev): bump @biomejs/biome in /ui/mantine-ui (#5283) Bumps [@biomejs/biome](https://github.com/biomejs/biome/tree/HEAD/packages/@biomejs/biome) from 2.4.14 to 2.5.0. - [Release notes](https://github.com/biomejs/biome/releases) - [Changelog](https://github.com/biomejs/biome/blob/main/packages/@biomejs/biome/CHANGELOG.md) - [Commits](https://github.com/biomejs/biome/commits/@biomejs/biome@2.5.0/packages/@biomejs/biome) --- updated-dependencies: - dependency-name: "@biomejs/biome" dependency-version: 2.4.16 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- ui/mantine-ui/package-lock.json | 84 +++++++++++++++++++-------------- ui/mantine-ui/package.json | 2 +- 2 files changed, 49 insertions(+), 37 deletions(-) diff --git a/ui/mantine-ui/package-lock.json b/ui/mantine-ui/package-lock.json index e1bc5ae618..a5b4325e8b 100644 --- a/ui/mantine-ui/package-lock.json +++ b/ui/mantine-ui/package-lock.json @@ -18,7 +18,7 @@ "react-router-dom": "^7.16.0" }, "devDependencies": { - "@biomejs/biome": "^2.4.14", + "@biomejs/biome": "^2.5.0", "@testing-library/dom": "^10.4.0", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", @@ -132,9 +132,9 @@ } }, "node_modules/@biomejs/biome": { - "version": "2.4.14", - "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.4.14.tgz", - "integrity": "sha512-TmAvxOEgrpLypzVGJ8FulIZnlyA9TxrO1hyqYrCz9r+bwma9xXxuLA5IuYnj55XQneFx460KjRbx6SWGLkg3bQ==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.5.0.tgz", + "integrity": "sha512-4kURkd9hAPrdDM3C9n82ycYgx8hvQcW6MjKTEejruj8rK0N8P3OPpdy8BvI8kt3KWY4ycF5XtDOrktetEfhfuw==", "dev": true, "license": "MIT OR Apache-2.0", "bin": { @@ -148,20 +148,20 @@ "url": "https://opencollective.com/biome" }, "optionalDependencies": { - "@biomejs/cli-darwin-arm64": "2.4.14", - "@biomejs/cli-darwin-x64": "2.4.14", - "@biomejs/cli-linux-arm64": "2.4.14", - "@biomejs/cli-linux-arm64-musl": "2.4.14", - "@biomejs/cli-linux-x64": "2.4.14", - "@biomejs/cli-linux-x64-musl": "2.4.14", - "@biomejs/cli-win32-arm64": "2.4.14", - "@biomejs/cli-win32-x64": "2.4.14" + "@biomejs/cli-darwin-arm64": "2.5.0", + "@biomejs/cli-darwin-x64": "2.5.0", + "@biomejs/cli-linux-arm64": "2.5.0", + "@biomejs/cli-linux-arm64-musl": "2.5.0", + "@biomejs/cli-linux-x64": "2.5.0", + "@biomejs/cli-linux-x64-musl": "2.5.0", + "@biomejs/cli-win32-arm64": "2.5.0", + "@biomejs/cli-win32-x64": "2.5.0" } }, "node_modules/@biomejs/cli-darwin-arm64": { - "version": "2.4.14", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.4.14.tgz", - "integrity": "sha512-XvgoE9XOawUOQPdmvs4J7wPhi/DLwSCGks3AlPJDmh34O0awRTqCED1HRcRDdpf1Zrp4us4MGOOdIxNpbqNF5Q==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.5.0.tgz", + "integrity": "sha512-Mn3Fwi3SA5fgmfCPqmzpWF2DLZnms3BVAhM088nTnGrTZmHS3wwIjcoZPqpXeNgd3DrrLH6xp8vTLIBuJoZiXw==", "cpu": [ "arm64" ], @@ -176,9 +176,9 @@ } }, "node_modules/@biomejs/cli-darwin-x64": { - "version": "2.4.14", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.4.14.tgz", - "integrity": "sha512-jE7hKBCFhOx3uUh+ZkWBfOHxAcILPfhFplNkuID/eZeSTLHzfZzoZxW8fbqY9xXRnPi7jGNAf1iPVR+0yWsM/Q==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.5.0.tgz", + "integrity": "sha512-rg3VPL5P8mYro6pqlXYXuJWph21slVp3SZtAqWSrkZs40d2gTzYmHF8E/X1iTID25btmNKltNDJ926sqVBp7DQ==", "cpu": [ "x64" ], @@ -193,13 +193,16 @@ } }, "node_modules/@biomejs/cli-linux-arm64": { - "version": "2.4.14", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.4.14.tgz", - "integrity": "sha512-2TELhZnW5RSLL063l9rc5xLpA0ZIw0Ccwy/0q384rvNAgFw3yI76bd59547yxowdQr5MNPET/xDLrLuvgSeeWQ==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.5.0.tgz", + "integrity": "sha512-tl+LW8fdD96/xdeWtWwc82LIOc5CoY7N2AsogLTp5R4ECErYt+8Jl/N68ezN9vzSiqPTxw6vjcihoLPYKZHrlw==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT OR Apache-2.0", "optional": true, "os": [ @@ -210,13 +213,16 @@ } }, "node_modules/@biomejs/cli-linux-arm64-musl": { - "version": "2.4.14", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.4.14.tgz", - "integrity": "sha512-/z+6gqAqqUQTHazwStxSXKHg9b8UvqBmDFRp+c4wYbq2KXhELQDon9EoC9RpmQ8JWkqQx/lIUy/cs+MhzDZp6A==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.5.0.tgz", + "integrity": "sha512-vQdM4oSGaf7ZNeGO9w5+Y8SBtyser9M6znxYbm7Ec8wInxJu1WiKxFYZW5Auj2d80bcVvefuGGRxoFOE0eee8g==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT OR Apache-2.0", "optional": true, "os": [ @@ -227,13 +233,16 @@ } }, "node_modules/@biomejs/cli-linux-x64": { - "version": "2.4.14", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.4.14.tgz", - "integrity": "sha512-zHrlQZDBDUz4OLAraYpWKcnLS6HOewBFWYOzY91d1ZjdqZwibOyb6BEu6WuWLugyo0P3riCmsbV9UqV1cSXwQg==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.5.0.tgz", + "integrity": "sha512-zpEGf4RQbFEh8Vt7OmavLyyOzRbtcE9osCqrS1kfvt8jDvxwhKXLSf7n0ebr/ov0RJ9ssP+lhs6C8a9WwFvrQA==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT OR Apache-2.0", "optional": true, "os": [ @@ -244,13 +253,16 @@ } }, "node_modules/@biomejs/cli-linux-x64-musl": { - "version": "2.4.14", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.4.14.tgz", - "integrity": "sha512-R6BWgJdQOwW9ulJatuTVrQkjnODjqHZkKNOqb1sz++3Noe5LYd0i3PchnOBUCYAPHoPWHhjJqbdZlHEu0hpjdA==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.5.0.tgz", + "integrity": "sha512-+9hIcMngJ+yGUahXqZuZ8CoWKJE9SAZsFsM3QDvXpNsLbXZ9lqVzgBhOk/jTSYkOA0GLP9eu3teukqpLUojHMg==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT OR Apache-2.0", "optional": true, "os": [ @@ -261,9 +273,9 @@ } }, "node_modules/@biomejs/cli-win32-arm64": { - "version": "2.4.14", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.4.14.tgz", - "integrity": "sha512-M3EH5hqOI/F/FUA2u4xcLoUgmxd218mvuj/6JL7Hv2toQvr2/AdOvKSpGkoRuWFCtQPVa+ZqkEV3Q5xBA9+XSA==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.5.0.tgz", + "integrity": "sha512-jB0wAvTLI4itx5VidqVUejPQFhRUxiZ9l9FvZ26D5fl6t3qme+ZB4PD3bTSeL1vZ8NI2Rx/zj6H9zcESuGHKGw==", "cpu": [ "arm64" ], @@ -278,9 +290,9 @@ } }, "node_modules/@biomejs/cli-win32-x64": { - "version": "2.4.14", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.4.14.tgz", - "integrity": "sha512-WL0EG5qE+EAKomGXbf2g6VnSKJhTL3tXC0QRzWRwA5VpjxNYa6H4P7ZWfymbGE4IhZZQi1KXQ2R0YjwInmz2fA==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.5.0.tgz", + "integrity": "sha512-VT/lF+GId+67j8aDfLkxdxNoVApsPSTbyAtB3jJq0IWTrY77WXfbPfpngxq0bA6JCEv/7k8C9qWjDRKRznDlyw==", "cpu": [ "x64" ], diff --git a/ui/mantine-ui/package.json b/ui/mantine-ui/package.json index fd0ce8e436..a3bfd2ec7b 100644 --- a/ui/mantine-ui/package.json +++ b/ui/mantine-ui/package.json @@ -28,7 +28,7 @@ "react-router-dom": "^7.16.0" }, "devDependencies": { - "@biomejs/biome": "^2.4.14", + "@biomejs/biome": "^2.5.0", "@testing-library/dom": "^10.4.0", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", From 35ff9575cff793d7e16c046e7e54988ac66f1d4f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Jun 2026 11:49:10 +0200 Subject: [PATCH 015/120] build(deps-dev): bump @types/node in /ui/mantine-ui (#5281) Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 25.6.0 to 25.9.3. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) --- updated-dependencies: - dependency-name: "@types/node" dependency-version: 25.9.1 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- ui/mantine-ui/package-lock.json | 16 ++++++++-------- ui/mantine-ui/package.json | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ui/mantine-ui/package-lock.json b/ui/mantine-ui/package-lock.json index a5b4325e8b..838071c760 100644 --- a/ui/mantine-ui/package-lock.json +++ b/ui/mantine-ui/package-lock.json @@ -23,7 +23,7 @@ "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", - "@types/node": "^25.6.0", + "@types/node": "^25.9.3", "@types/react": "^19.2.16", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^6.0.1", @@ -1082,13 +1082,13 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "25.6.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.0.tgz", - "integrity": "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==", + "version": "25.9.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.3.tgz", + "integrity": "sha512-603BddQMv3pUcr4U2dhujk83N2tTDVr/34wII2B6bJy6g+8WD6yUb11jszNs0gdi4PesVWl7ABt8nYMVpnLUcg==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~7.19.0" + "undici-types": ">=7.24.0 <7.24.7" } }, "node_modules/@types/react": { @@ -2732,9 +2732,9 @@ } }, "node_modules/undici-types": { - "version": "7.19.2", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz", - "integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", + "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", "dev": true, "license": "MIT" }, diff --git a/ui/mantine-ui/package.json b/ui/mantine-ui/package.json index a3bfd2ec7b..099059a40e 100644 --- a/ui/mantine-ui/package.json +++ b/ui/mantine-ui/package.json @@ -33,7 +33,7 @@ "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", - "@types/node": "^25.6.0", + "@types/node": "^25.9.3", "@types/react": "^19.2.16", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^6.0.1", From cd6611b26b4864ff31d18fea4cce80b0de8d425a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Jun 2026 11:50:00 +0200 Subject: [PATCH 016/120] build(deps): bump @tanstack/react-query in /ui/mantine-ui (#5282) Bumps [@tanstack/react-query](https://github.com/TanStack/query/tree/HEAD/packages/react-query) from 5.100.9 to 5.101.0. - [Release notes](https://github.com/TanStack/query/releases) - [Changelog](https://github.com/TanStack/query/blob/main/packages/react-query/CHANGELOG.md) - [Commits](https://github.com/TanStack/query/commits/@tanstack/react-query@5.101.0/packages/react-query) --- updated-dependencies: - dependency-name: "@tanstack/react-query" dependency-version: 5.101.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- ui/mantine-ui/package-lock.json | 16 ++++++++-------- ui/mantine-ui/package.json | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ui/mantine-ui/package-lock.json b/ui/mantine-ui/package-lock.json index 838071c760..dd2f4d38e5 100644 --- a/ui/mantine-ui/package-lock.json +++ b/ui/mantine-ui/package-lock.json @@ -11,7 +11,7 @@ "@mantine/code-highlight": "^9.3.0", "@mantine/core": "^9.0.0", "@mantine/hooks": "^9.0.0", - "@tanstack/react-query": "^5.100.9", + "@tanstack/react-query": "^5.101.0", "highlight.js": "^11.11.1", "react": "^19.2.7", "react-dom": "^19.2.7", @@ -914,9 +914,9 @@ "license": "MIT" }, "node_modules/@tanstack/query-core": { - "version": "5.100.9", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.100.9.tgz", - "integrity": "sha512-SJSFw1S8+kQ0+knv/XGfrbocWoAlT7vDKsSImtLx3ZPQmEcR46hkDjLSvynSy25N8Ms4tIEini1FuBd5k7IscQ==", + "version": "5.101.0", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.101.0.tgz", + "integrity": "sha512-cQetA74EB+seWySv1TTKr828TnP0u39m6LykwDXIo84SNortpDkp30TMEjkqtYCNP9c40uT/iwl6MLiufEt0Ow==", "license": "MIT", "funding": { "type": "github", @@ -924,12 +924,12 @@ } }, "node_modules/@tanstack/react-query": { - "version": "5.100.9", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.100.9.tgz", - "integrity": "sha512-Oa44XkaI3kCNN6ME0KByU3xT3SEUNOMfZpHxL6+wFoTm+OeUFYHKdeYVe0aOXlRDm/f15sgLwEt2HDorIdW8+A==", + "version": "5.101.0", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.101.0.tgz", + "integrity": "sha512-rLlJXSpkqfizLWgkR5+eLeIk0MvTx/meEIR7LRjxic+qxiQP8zVjq7BqQkiCMNLQBlLfuOLqqr6KO5GtrDlmSg==", "license": "MIT", "dependencies": { - "@tanstack/query-core": "5.100.9" + "@tanstack/query-core": "5.101.0" }, "funding": { "type": "github", diff --git a/ui/mantine-ui/package.json b/ui/mantine-ui/package.json index 099059a40e..4b5d04f740 100644 --- a/ui/mantine-ui/package.json +++ b/ui/mantine-ui/package.json @@ -21,7 +21,7 @@ "@mantine/code-highlight": "^9.3.0", "@mantine/core": "^9.0.0", "@mantine/hooks": "^9.0.0", - "@tanstack/react-query": "^5.100.9", + "@tanstack/react-query": "^5.101.0", "highlight.js": "^11.11.1", "react": "^19.2.7", "react-dom": "^19.2.7", From 4d6e30ae857bfc34b245d49f7a2eaffaca7acd33 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Jun 2026 11:59:56 +0200 Subject: [PATCH 017/120] build(deps-dev): bump the vite group across 1 directory with 2 updates (#5279) Bumps the vite group with 2 updates in the /ui/mantine-ui directory: [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/tree/HEAD/packages/plugin-react) and [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite). Updates `@vitejs/plugin-react` from 6.0.1 to 6.0.2 - [Release notes](https://github.com/vitejs/vite-plugin-react/releases) - [Changelog](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite-plugin-react/commits/plugin-react@6.0.2/packages/plugin-react) Updates `vite` from 8.0.10 to 8.0.16 - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v8.0.16/packages/vite) --- updated-dependencies: - dependency-name: "@vitejs/plugin-react" dependency-version: 6.0.2 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: vite - dependency-name: vite dependency-version: 8.0.16 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: vite ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- ui/mantine-ui/package-lock.json | 209 +++++++++++++++++--------------- ui/mantine-ui/package.json | 4 +- 2 files changed, 112 insertions(+), 101 deletions(-) diff --git a/ui/mantine-ui/package-lock.json b/ui/mantine-ui/package-lock.json index dd2f4d38e5..a813f2b18b 100644 --- a/ui/mantine-ui/package-lock.json +++ b/ui/mantine-ui/package-lock.json @@ -26,14 +26,14 @@ "@types/node": "^25.9.3", "@types/react": "^19.2.16", "@types/react-dom": "^19.2.3", - "@vitejs/plugin-react": "^6.0.1", + "@vitejs/plugin-react": "^6.0.2", "jsdom": "^29.1.1", "postcss": "^8.5.15", "postcss-preset-mantine": "1.18.0", "postcss-simple-vars": "^7.0.1", "prop-types": "^15.8.1", "typescript": "^5.9.3", - "vite": "^8.0.10", + "vite": "^8.0.16", "vite-tsconfig-paths": "^6.1.1", "vitest": "^4.1.8" } @@ -614,14 +614,14 @@ } }, "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", - "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.5.tgz", + "integrity": "sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@tybys/wasm-util": "^0.10.1" + "@tybys/wasm-util": "^0.10.2" }, "funding": { "type": "github", @@ -633,9 +633,9 @@ } }, "node_modules/@oxc-project/types": { - "version": "0.127.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.127.0.tgz", - "integrity": "sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ==", + "version": "0.133.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz", + "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==", "dev": true, "license": "MIT", "funding": { @@ -643,9 +643,9 @@ } }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.17.tgz", - "integrity": "sha512-s70pVGhw4zqGeFnXWvAzJDlvxhlRollagdCCKRgOsgUOH3N1l0LIxf83AtGzmb5SiVM4Hjl5HyarMRfdfj3DaQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.3.tgz", + "integrity": "sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==", "cpu": [ "arm64" ], @@ -660,9 +660,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.17.tgz", - "integrity": "sha512-4ksWc9n0mhlZpZ9PMZgTGjeOPRu8MB1Z3Tz0Mo02eWfWCHMW1zN82Qz/pL/rC+yQa+8ZnutMF0JjJe7PjwasYw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.3.tgz", + "integrity": "sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==", "cpu": [ "arm64" ], @@ -677,9 +677,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.17.tgz", - "integrity": "sha512-SUSDOI6WwUVNcWxd02QEBjLdY1VPHvlEkw6T/8nYG322iYWCTxRb1vzk4E+mWWYehTp7ERibq54LSJGjmouOsw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.3.tgz", + "integrity": "sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==", "cpu": [ "x64" ], @@ -694,9 +694,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.17.tgz", - "integrity": "sha512-hwnz3nw9dbJ05EDO/PvcjaaewqqDy7Y1rn1UO81l8iIK1GjenME75dl16ajbvSSMfv66WXSRCYKIqfgq2KCfxw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.3.tgz", + "integrity": "sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==", "cpu": [ "x64" ], @@ -711,9 +711,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.17.tgz", - "integrity": "sha512-IS+W7epTcwANmFSQFrS1SivEXHtl1JtuQA9wlxrZTcNi6mx+FDOYrakGevvvTwgj2JvWiK8B29/qD9BELZPyXQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.3.tgz", + "integrity": "sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==", "cpu": [ "arm" ], @@ -728,13 +728,16 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.17.tgz", - "integrity": "sha512-e6usGaHKW5BMNZOymS1UcEYGowQMWcgZ71Z17Sl/h2+ZziNJ1a9n3Zvcz6LdRyIW5572wBCTH/Z+bKuZouGk9Q==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.3.tgz", + "integrity": "sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -745,13 +748,16 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.17.tgz", - "integrity": "sha512-b/CgbwAJpmrRLp02RPfhbudf5tZnN9nsPWK82znefso832etkem8H7FSZwxrOI9djcdTP7U6YfNhbRnh7djErg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.3.tgz", + "integrity": "sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -762,13 +768,16 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.17.tgz", - "integrity": "sha512-4EII1iNGRUN5WwGbF/kOh/EIkoDN9HsupgLQoXfY+D1oyJm7/F4t5PYU5n8SWZgG0FEwakyM8pGgwcBYruGTlA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.3.tgz", + "integrity": "sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==", "cpu": [ "ppc64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -779,13 +788,16 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.17.tgz", - "integrity": "sha512-AH8oq3XqQo4IibpVXvPeLDI5pzkpYn0WiZAfT05kFzoJ6tQNzwRdDYQ45M8I/gslbodRZwW8uxLhbSBbkv96rA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.3.tgz", + "integrity": "sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==", "cpu": [ "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -796,13 +808,16 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.17.tgz", - "integrity": "sha512-cLnjV3xfo7KslbU41Z7z8BH/E1y5mzUYzAqih1d1MDaIGZRCMqTijqLv76/P7fyHuvUcfGsIpqCdddbxLLK9rA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.3.tgz", + "integrity": "sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -813,13 +828,16 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.17.tgz", - "integrity": "sha512-0phclDw1spsL7dUB37sIARuis2tAgomCJXAHZlpt8PXZ4Ba0dRP1e+66lsRqrfhISeN9bEGNjQs+T/Fbd7oYGw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.3.tgz", + "integrity": "sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -830,9 +848,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.17.tgz", - "integrity": "sha512-0ag/hEgXOwgw4t8QyQvUCxvEg+V0KBcA6YuOx9g0r02MprutRF5dyljgm3EmR02O292UX7UeS6HzWHAl6KgyhA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.3.tgz", + "integrity": "sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==", "cpu": [ "arm64" ], @@ -847,9 +865,9 @@ } }, "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.17.tgz", - "integrity": "sha512-LEXei6vo0E5wTGwpkJ4KoT3OZJRnglwldt5ziLzOlc6qqb55z4tWNq2A+PFqCJuvWWdP53CVhG1Z9NtToDPJrA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.3.tgz", + "integrity": "sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==", "cpu": [ "wasm32" ], @@ -866,9 +884,9 @@ } }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.17.tgz", - "integrity": "sha512-gUmyzBl3SPMa6hrqFUth9sVfcLBlYsbMzBx5PlexMroZStgzGqlZ26pYG89rBb45Mnia+oil6YAIFeEWGWhoZA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.3.tgz", + "integrity": "sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==", "cpu": [ "arm64" ], @@ -883,9 +901,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.17.tgz", - "integrity": "sha512-3hkiolcUAvPB9FLb3UZdfjVVNWherN1f/skkGWJP/fgSQhYUZpSIRr0/I8ZK9TkF3F7kxvJAk0+IcKvPHk9qQg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.3.tgz", + "integrity": "sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==", "cpu": [ "x64" ], @@ -900,9 +918,9 @@ } }, "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-rc.7", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.7.tgz", - "integrity": "sha512-qujRfC8sFVInYSPPMLQByRh7zhwkGFS4+tyMQ83srV1qrxL4g8E2tyxVVyxd0+8QeBM1mIk9KbWxkegRr76XzA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", "dev": true, "license": "MIT" }, @@ -1112,13 +1130,13 @@ } }, "node_modules/@vitejs/plugin-react": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.1.tgz", - "integrity": "sha512-l9X/E3cDb+xY3SWzlG1MOGt2usfEHGMNIaegaUGFsLkb3RCn/k8/TOXBcab+OndDI4TBtktT8/9BwwW8Vi9KUQ==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.2.tgz", + "integrity": "sha512-DlSMqo4WhThw4vB8Mpn0Woe9J+Jfq1geJ61AKW0QEgLzGMNwtIMdxbDUzLxcun8W7NbJO0e2Jg/Nxm3cCSVzzg==", "dev": true, "license": "MIT", "dependencies": { - "@rolldown/pluginutils": "1.0.0-rc.7" + "@rolldown/pluginutils": "^1.0.0" }, "engines": { "node": "^20.19.0 || >=22.12.0" @@ -2387,14 +2405,14 @@ } }, "node_modules/rolldown": { - "version": "1.0.0-rc.17", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.17.tgz", - "integrity": "sha512-ZrT53oAKrtA4+YtBWPQbtPOxIbVDbxT0orcYERKd63VJTF13zPcgXTvD4843L8pcsI7M6MErt8QtON6lrB9tyA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.3.tgz", + "integrity": "sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.127.0", - "@rolldown/pluginutils": "1.0.0-rc.17" + "@oxc-project/types": "=0.133.0", + "@rolldown/pluginutils": "^1.0.0" }, "bin": { "rolldown": "bin/cli.mjs" @@ -2403,29 +2421,22 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.0-rc.17", - "@rolldown/binding-darwin-arm64": "1.0.0-rc.17", - "@rolldown/binding-darwin-x64": "1.0.0-rc.17", - "@rolldown/binding-freebsd-x64": "1.0.0-rc.17", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.17", - "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.17", - "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.17", - "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.17", - "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.17", - "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.17", - "@rolldown/binding-linux-x64-musl": "1.0.0-rc.17", - "@rolldown/binding-openharmony-arm64": "1.0.0-rc.17", - "@rolldown/binding-wasm32-wasi": "1.0.0-rc.17", - "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.17", - "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.17" - } - }, - "node_modules/rolldown/node_modules/@rolldown/pluginutils": { - "version": "1.0.0-rc.17", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.17.tgz", - "integrity": "sha512-n8iosDOt6Ig1UhJ2AYqoIhHWh/isz0xpicHTzpKBeotdVsTEcxsSA/i3EVM7gQAj0rU27OLAxCjzlj15IWY7bg==", - "dev": true, - "license": "MIT" + "@rolldown/binding-android-arm64": "1.0.3", + "@rolldown/binding-darwin-arm64": "1.0.3", + "@rolldown/binding-darwin-x64": "1.0.3", + "@rolldown/binding-freebsd-x64": "1.0.3", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.3", + "@rolldown/binding-linux-arm64-gnu": "1.0.3", + "@rolldown/binding-linux-arm64-musl": "1.0.3", + "@rolldown/binding-linux-ppc64-gnu": "1.0.3", + "@rolldown/binding-linux-s390x-gnu": "1.0.3", + "@rolldown/binding-linux-x64-gnu": "1.0.3", + "@rolldown/binding-linux-x64-musl": "1.0.3", + "@rolldown/binding-openharmony-arm64": "1.0.3", + "@rolldown/binding-wasm32-wasi": "1.0.3", + "@rolldown/binding-win32-arm64-msvc": "1.0.3", + "@rolldown/binding-win32-x64-msvc": "1.0.3" + } }, "node_modules/saxes": { "version": "6.0.0", @@ -2562,9 +2573,9 @@ } }, "node_modules/tinyglobby": { - "version": "0.2.16", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", - "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", "dev": true, "license": "MIT", "dependencies": { @@ -2789,17 +2800,17 @@ "license": "MIT" }, "node_modules/vite": { - "version": "8.0.10", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.10.tgz", - "integrity": "sha512-rZuUu9j6J5uotLDs+cAA4O5H4K1SfPliUlQwqa6YEwSrWDZzP4rhm00oJR5snMewjxF5V/K3D4kctsUTsIU9Mw==", + "version": "8.0.16", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.16.tgz", + "integrity": "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==", "dev": true, "license": "MIT", "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", - "postcss": "^8.5.10", - "rolldown": "1.0.0-rc.17", - "tinyglobby": "^0.2.16" + "postcss": "^8.5.15", + "rolldown": "1.0.3", + "tinyglobby": "^0.2.17" }, "bin": { "vite": "bin/vite.js" @@ -2815,7 +2826,7 @@ }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.1.0", + "@vitejs/devtools": "^0.1.18", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", diff --git a/ui/mantine-ui/package.json b/ui/mantine-ui/package.json index 4b5d04f740..ce23f3c678 100644 --- a/ui/mantine-ui/package.json +++ b/ui/mantine-ui/package.json @@ -36,14 +36,14 @@ "@types/node": "^25.9.3", "@types/react": "^19.2.16", "@types/react-dom": "^19.2.3", - "@vitejs/plugin-react": "^6.0.1", + "@vitejs/plugin-react": "^6.0.2", "jsdom": "^29.1.1", "postcss": "^8.5.15", "postcss-preset-mantine": "1.18.0", "postcss-simple-vars": "^7.0.1", "prop-types": "^15.8.1", "typescript": "^5.9.3", - "vite": "^8.0.10", + "vite": "^8.0.16", "vite-tsconfig-paths": "^6.1.1", "vitest": "^4.1.8" } From 55ad0ccab3adfa5f8e626478f3b2b31d39883983 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Jun 2026 15:05:25 +0200 Subject: [PATCH 018/120] build(deps): bump google.golang.org/grpc from 1.80.0 to 1.81.1 (#5273) Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.80.0 to 1.81.1. - [Release notes](https://github.com/grpc/grpc-go/releases) - [Commits](https://github.com/grpc/grpc-go/compare/v1.80.0...v1.81.1) --- updated-dependencies: - dependency-name: google.golang.org/grpc dependency-version: 1.81.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index fe8835c51c..8c0226b8f8 100644 --- a/go.mod +++ b/go.mod @@ -56,7 +56,7 @@ require ( golang.org/x/mod v0.36.0 golang.org/x/net v0.55.0 golang.org/x/text v0.37.0 - google.golang.org/grpc v1.80.0 + google.golang.org/grpc v1.81.1 google.golang.org/protobuf v1.36.11 gopkg.in/telebot.v3 v3.3.8 gopkg.in/yaml.v2 v2.4.0 diff --git a/go.sum b/go.sum index 829edef587..37f1c2ba25 100644 --- a/go.sum +++ b/go.sum @@ -1070,8 +1070,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.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM= -google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4= +google.golang.org/grpc v1.81.1 h1:VnnIIZ88UzOOKLukQi+ImGz8O1Wdp8nAGGnvOfEIWQQ= +google.golang.org/grpc v1.81.1/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I= 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= From 52d1d8ec9e4a6180203fc51337326d1f0d740927 Mon Sep 17 00:00:00 2001 From: Ethan Hunter Date: Wed, 17 Jun 2026 03:40:51 -0600 Subject: [PATCH 019/120] add receiver matchers to silence proto (#5292) Signed-off-by: Ethan Hunter --- silence/silencepb/silence.pb.go | 39 +++++++++++++++++++++++---------- silence/silencepb/silence.proto | 6 +++++ 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/silence/silencepb/silence.pb.go b/silence/silencepb/silence.pb.go index c3454fb293..210abde2fc 100644 --- a/silence/silencepb/silence.pb.go +++ b/silence/silencepb/silence.pb.go @@ -271,9 +271,15 @@ type Silence struct { Annotations map[string]string `protobuf:"bytes,10,rep,name=annotations,proto3" json:"annotations,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` // Multiple matcher sets with OR logic between them. // At least one matcher set must match for the silence to apply. - MatcherSets []*MatcherSet `protobuf:"bytes,11,rep,name=matcher_sets,json=matcherSets,proto3" json:"matcher_sets,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + MatcherSets []*MatcherSet `protobuf:"bytes,11,rep,name=matcher_sets,json=matcherSets,proto3" json:"matcher_sets,omitempty"` + // Multiple receiver matcher sets with OR logic between them. + // Receiver matchers apply to the labels of receivers, not alerts. At + // least one set of receiver matchers must match for a silence to apply + // to alerts that are sent to that receiver. Unlike alert label matcher, + // a silence with no receiver matchers applies to ALL recievers. + ReceiverMatcherSets []*MatcherSet `protobuf:"bytes,12,rep,name=receiver_matcher_sets,json=receiverMatcherSets,proto3" json:"receiver_matcher_sets,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *Silence) Reset() { @@ -376,6 +382,13 @@ func (x *Silence) GetMatcherSets() []*MatcherSet { return nil } +func (x *Silence) GetReceiverMatcherSets() []*MatcherSet { + if x != nil { + return x.ReceiverMatcherSets + } + return nil +} + // MeshSilence wraps a regular silence with an expiration timestamp // after which the silence may be garbage collected. type MeshSilence struct { @@ -452,7 +465,7 @@ const file_silence_proto_rawDesc = "" + "\ttimestamp\x18\x03 \x01(\v2\x1a.google.protobuf.TimestampR\ttimestamp\"<\n" + "\n" + "MatcherSet\x12.\n" + - "\bmatchers\x18\x01 \x03(\v2\x12.silencepb.MatcherR\bmatchers\"\x9c\x04\n" + + "\bmatchers\x18\x01 \x03(\v2\x12.silencepb.MatcherR\bmatchers\"\xe7\x04\n" + "\aSilence\x12\x0e\n" + "\x02id\x18\x01 \x01(\tR\x02id\x12.\n" + "\bmatchers\x18\x02 \x03(\v2\x12.silencepb.MatcherR\bmatchers\x127\n" + @@ -466,7 +479,8 @@ const file_silence_proto_rawDesc = "" + "\acomment\x18\t \x01(\tR\acomment\x12E\n" + "\vannotations\x18\n" + " \x03(\v2#.silencepb.Silence.AnnotationsEntryR\vannotations\x128\n" + - "\fmatcher_sets\x18\v \x03(\v2\x15.silencepb.MatcherSetR\vmatcherSets\x1a>\n" + + "\fmatcher_sets\x18\v \x03(\v2\x15.silencepb.MatcherSetR\vmatcherSets\x12I\n" + + "\x15receiver_matcher_sets\x18\f \x03(\v2\x15.silencepb.MatcherSetR\x13receiverMatcherSets\x1a>\n" + "\x10AnnotationsEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"v\n" + @@ -510,13 +524,14 @@ var file_silence_proto_depIdxs = []int32{ 2, // 7: silencepb.Silence.comments:type_name -> silencepb.Comment 6, // 8: silencepb.Silence.annotations:type_name -> silencepb.Silence.AnnotationsEntry 3, // 9: silencepb.Silence.matcher_sets:type_name -> silencepb.MatcherSet - 4, // 10: silencepb.MeshSilence.silence:type_name -> silencepb.Silence - 7, // 11: silencepb.MeshSilence.expires_at:type_name -> google.protobuf.Timestamp - 12, // [12:12] is the sub-list for method output_type - 12, // [12:12] is the sub-list for method input_type - 12, // [12:12] is the sub-list for extension type_name - 12, // [12:12] is the sub-list for extension extendee - 0, // [0:12] is the sub-list for field type_name + 3, // 10: silencepb.Silence.receiver_matcher_sets:type_name -> silencepb.MatcherSet + 4, // 11: silencepb.MeshSilence.silence:type_name -> silencepb.Silence + 7, // 12: silencepb.MeshSilence.expires_at:type_name -> google.protobuf.Timestamp + 13, // [13:13] is the sub-list for method output_type + 13, // [13:13] is the sub-list for method input_type + 13, // [13:13] is the sub-list for extension type_name + 13, // [13:13] is the sub-list for extension extendee + 0, // [0:13] is the sub-list for field type_name } func init() { file_silence_proto_init() } diff --git a/silence/silencepb/silence.proto b/silence/silencepb/silence.proto index 658304e8d6..8769ae2333 100644 --- a/silence/silencepb/silence.proto +++ b/silence/silencepb/silence.proto @@ -68,6 +68,12 @@ message Silence { // Multiple matcher sets with OR logic between them. // At least one matcher set must match for the silence to apply. repeated MatcherSet matcher_sets = 11; + // Multiple receiver matcher sets with OR logic between them. + // Receiver matchers apply to the labels of receivers, not alerts. At + // least one set of receiver matchers must match for a silence to apply + // to alerts that are sent to that receiver. Unlike alert label matcher, + // a silence with no receiver matchers applies to ALL receivers. + repeated MatcherSet receiver_matcher_sets = 12; } // MeshSilence wraps a regular silence with an expiration timestamp From a57a6da433a8cf8a7c065e0f0a8dc7d8343d7fab Mon Sep 17 00:00:00 2001 From: Solomon Jacobs Date: Wed, 17 Jun 2026 11:52:31 +0200 Subject: [PATCH 020/120] Bump dependencies (#5308) * build(deps): bump the go-opentelemetry-io group across 1 directory with 8 updates Bumps the go-opentelemetry-io group with 4 updates in the / directory: [go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace](https://github.com/open-telemetry/opentelemetry-go-contrib), [go.opentelemetry.io/otel/exporters/otlp/otlptrace](https://github.com/open-telemetry/opentelemetry-go), [go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc](https://github.com/open-telemetry/opentelemetry-go) and [go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp](https://github.com/open-telemetry/opentelemetry-go). Updates `go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace` from 0.68.0 to 0.69.0 - [Release notes](https://github.com/open-telemetry/opentelemetry-go-contrib/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-go-contrib/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-go-contrib/compare/zpages/v0.68.0...zpages/v0.69.0) Updates `go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp` from 0.68.0 to 0.69.0 - [Release notes](https://github.com/open-telemetry/opentelemetry-go-contrib/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-go-contrib/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-go-contrib/compare/zpages/v0.68.0...zpages/v0.69.0) Updates `go.opentelemetry.io/otel` from 1.43.0 to 1.44.0 - [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.43.0...v1.44.0) Updates `go.opentelemetry.io/otel/exporters/otlp/otlptrace` from 1.43.0 to 1.44.0 - [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.43.0...v1.44.0) Updates `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc` from 1.43.0 to 1.44.0 - [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.43.0...v1.44.0) Updates `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp` from 1.43.0 to 1.44.0 - [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.43.0...v1.44.0) Updates `go.opentelemetry.io/otel/sdk` from 1.43.0 to 1.44.0 - [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.43.0...v1.44.0) Updates `go.opentelemetry.io/otel/trace` from 1.43.0 to 1.44.0 - [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.43.0...v1.44.0) --- updated-dependencies: - dependency-name: go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace dependency-version: 0.69.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-opentelemetry-io - dependency-name: go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp dependency-version: 0.69.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-opentelemetry-io - dependency-name: go.opentelemetry.io/otel dependency-version: 1.44.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-opentelemetry-io - dependency-name: go.opentelemetry.io/otel/exporters/otlp/otlptrace dependency-version: 1.44.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-opentelemetry-io - dependency-name: go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc dependency-version: 1.44.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-opentelemetry-io - dependency-name: go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp dependency-version: 1.44.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-opentelemetry-io - dependency-name: go.opentelemetry.io/otel/sdk dependency-version: 1.44.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-opentelemetry-io - dependency-name: go.opentelemetry.io/otel/trace dependency-version: 1.44.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-opentelemetry-io ... Signed-off-by: dependabot[bot] Signed-off-by: Solomon Jacobs * Bump `go.opentelemetry.io/otel/semconv` Signed-off-by: Solomon Jacobs * build(deps): bump the aws group across 1 directory with 14 updates Bumps the aws group with 8 updates in the / directory: | Package | From | To | | --- | --- | --- | | [github.com/aws/aws-sdk-go-v2](https://github.com/aws/aws-sdk-go-v2) | `1.41.7` | `1.42.0` | | [github.com/aws/aws-sdk-go-v2/config](https://github.com/aws/aws-sdk-go-v2) | `1.32.17` | `1.32.25` | | [github.com/aws/aws-sdk-go-v2/service/sns](https://github.com/aws/aws-sdk-go-v2) | `1.39.17` | `1.40.1` | | [github.com/go-openapi/analysis](https://github.com/go-openapi/analysis) | `0.25.0` | `0.25.2` | | [github.com/go-openapi/errors](https://github.com/go-openapi/errors) | `0.22.7` | `0.22.8` | | [github.com/go-openapi/loads](https://github.com/go-openapi/loads) | `0.23.3` | `0.24.0` | | [github.com/go-openapi/runtime](https://github.com/go-openapi/runtime) | `0.29.4` | `0.32.3` | | [github.com/go-openapi/swag](https://github.com/go-openapi/swag) | `0.26.0` | `0.26.1` | Updates `github.com/aws/aws-sdk-go-v2` from 1.41.7 to 1.42.0 - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/v1.41.7...v1.42.0) Updates `github.com/aws/aws-sdk-go-v2/config` from 1.32.17 to 1.32.25 - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/config/v1.32.17...config/v1.32.25) Updates `github.com/aws/aws-sdk-go-v2/credentials` from 1.19.16 to 1.19.24 - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/credentials/v1.19.16...credentials/v1.19.24) Updates `github.com/aws/aws-sdk-go-v2/service/sns` from 1.39.17 to 1.40.1 - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/service/sns/v1.39.17...v1.40.1) Updates `github.com/aws/aws-sdk-go-v2/service/sts` from 1.42.1 to 1.43.3 - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/service/s3/v1.42.1...service/amp/v1.43.3) Updates `github.com/aws/smithy-go` from 1.25.1 to 1.27.1 - [Release notes](https://github.com/aws/smithy-go/releases) - [Changelog](https://github.com/aws/smithy-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws/smithy-go/compare/v1.25.1...v1.27.1) Updates `github.com/go-openapi/analysis` from 0.25.0 to 0.25.2 - [Release notes](https://github.com/go-openapi/analysis/releases) - [Commits](https://github.com/go-openapi/analysis/compare/v0.25.0...v0.25.2) Updates `github.com/go-openapi/errors` from 0.22.7 to 0.22.8 - [Release notes](https://github.com/go-openapi/errors/releases) - [Commits](https://github.com/go-openapi/errors/compare/v0.22.7...v0.22.8) Updates `github.com/go-openapi/loads` from 0.23.3 to 0.24.0 - [Release notes](https://github.com/go-openapi/loads/releases) - [Commits](https://github.com/go-openapi/loads/compare/v0.23.3...v0.24.0) Updates `github.com/go-openapi/runtime` from 0.29.4 to 0.32.3 - [Release notes](https://github.com/go-openapi/runtime/releases) - [Commits](https://github.com/go-openapi/runtime/compare/v0.29.4...v0.32.3) Updates `github.com/go-openapi/spec` from 0.22.4 to 0.22.5 - [Release notes](https://github.com/go-openapi/spec/releases) - [Commits](https://github.com/go-openapi/spec/compare/v0.22.4...v0.22.5) Updates `github.com/go-openapi/strfmt` from 0.26.2 to 0.26.3 - [Release notes](https://github.com/go-openapi/strfmt/releases) - [Commits](https://github.com/go-openapi/strfmt/compare/v0.26.2...v0.26.3) Updates `github.com/go-openapi/swag` from 0.26.0 to 0.26.1 - [Release notes](https://github.com/go-openapi/swag/releases) - [Commits](https://github.com/go-openapi/swag/compare/v0.26.0...v0.26.1) Updates `github.com/go-openapi/validate` from 0.25.2 to 0.25.3 - [Release notes](https://github.com/go-openapi/validate/releases) - [Commits](https://github.com/go-openapi/validate/compare/v0.25.2...v0.25.3) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go-v2 dependency-version: 1.42.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: aws - dependency-name: github.com/aws/aws-sdk-go-v2/config dependency-version: 1.32.25 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: aws - dependency-name: github.com/aws/aws-sdk-go-v2/credentials dependency-version: 1.19.24 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: aws - dependency-name: github.com/aws/aws-sdk-go-v2/service/sns dependency-version: 1.40.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: aws - dependency-name: github.com/aws/aws-sdk-go-v2/service/sts dependency-version: 1.43.3 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: aws - dependency-name: github.com/aws/smithy-go dependency-version: 1.27.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: aws - dependency-name: github.com/go-openapi/analysis dependency-version: 0.25.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: aws - dependency-name: github.com/go-openapi/errors dependency-version: 0.22.8 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: aws - dependency-name: github.com/go-openapi/loads dependency-version: 0.24.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: aws - dependency-name: github.com/go-openapi/runtime dependency-version: 0.32.3 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: aws - dependency-name: github.com/go-openapi/spec dependency-version: 0.22.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: aws - dependency-name: github.com/go-openapi/strfmt dependency-version: 0.26.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: aws - dependency-name: github.com/go-openapi/swag dependency-version: 0.26.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: aws - dependency-name: github.com/go-openapi/validate dependency-version: 0.25.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: aws ... Signed-off-by: dependabot[bot] Signed-off-by: Solomon Jacobs * replace deprecated `middleware.Spec` Replace the deprecated `middleware.Spec` call with docui.ServeSpec, which it now merely wraps. See: https://pkg.go.dev/github.com/go-openapi/runtime/middleware Signed-off-by: Solomon Jacobs --------- Signed-off-by: dependabot[bot] Signed-off-by: Solomon Jacobs Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- api/v2/api.go | 3 +- go.mod | 97 ++++++++++----------- go.sum | 210 +++++++++++++++++++++++---------------------- tracing/tracing.go | 2 +- 4 files changed, 158 insertions(+), 154 deletions(-) diff --git a/api/v2/api.go b/api/v2/api.go index 5088109333..dddbb01d78 100644 --- a/api/v2/api.go +++ b/api/v2/api.go @@ -29,6 +29,7 @@ import ( "github.com/go-openapi/analysis" "github.com/go-openapi/loads" "github.com/go-openapi/runtime/middleware" + "github.com/go-openapi/runtime/server-middleware/docui" "github.com/go-openapi/strfmt" "github.com/prometheus/client_golang/prometheus" prometheus_model "github.com/prometheus/common/model" @@ -126,7 +127,7 @@ func NewAPI( openAPI.Middleware = func(b middleware.Builder) http.Handler { // Manually create the context so that we can use the singleton swaggerSpecAnalysis. swaggerContext := middleware.NewRoutableContextWithAnalyzedSpec(swaggerSpec, swaggerSpecAnalysis, openAPI, nil) - return middleware.Spec("", swaggerSpec.Raw(), swaggerContext.RoutesHandler(b)) + return docui.ServeSpec(swaggerSpec.Raw(), swaggerContext.RoutesHandler(b), docui.WithSpecPath("/swagger.json")) } openAPI.AlertGetAlertsHandler = alert_ops.GetAlertsHandlerFunc(api.getAlertsHandler) diff --git a/go.mod b/go.mod index 8c0226b8f8..1ee7a83e85 100644 --- a/go.mod +++ b/go.mod @@ -6,26 +6,27 @@ require ( github.com/KimMachineGun/automemlimit v0.7.5 github.com/alecthomas/kingpin/v2 v2.4.0 github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b - github.com/aws/aws-sdk-go-v2 v1.41.7 - github.com/aws/aws-sdk-go-v2/config v1.32.17 - github.com/aws/aws-sdk-go-v2/credentials v1.19.16 - github.com/aws/aws-sdk-go-v2/service/sns v1.39.17 - github.com/aws/aws-sdk-go-v2/service/sts v1.42.1 - github.com/aws/smithy-go v1.25.1 + github.com/aws/aws-sdk-go-v2 v1.42.0 + github.com/aws/aws-sdk-go-v2/config v1.32.25 + github.com/aws/aws-sdk-go-v2/credentials v1.19.24 + github.com/aws/aws-sdk-go-v2/service/sns v1.40.1 + github.com/aws/aws-sdk-go-v2/service/sts v1.43.3 + github.com/aws/smithy-go v1.27.1 github.com/cenkalti/backoff/v4 v4.3.0 github.com/cespare/xxhash/v2 v2.3.0 github.com/coder/quartz v0.3.1 github.com/coreos/go-systemd/v22 v22.7.0 github.com/emersion/go-smtp v0.24.0 github.com/fsnotify/fsnotify v1.10.1 - github.com/go-openapi/analysis v0.25.0 - github.com/go-openapi/errors v0.22.7 - github.com/go-openapi/loads v0.23.3 - github.com/go-openapi/runtime v0.29.4 - github.com/go-openapi/spec v0.22.4 - github.com/go-openapi/strfmt v0.26.2 - github.com/go-openapi/swag v0.26.0 - github.com/go-openapi/validate v0.25.2 + github.com/go-openapi/analysis v0.25.2 + github.com/go-openapi/errors v0.22.8 + github.com/go-openapi/loads v0.24.0 + github.com/go-openapi/runtime v0.32.3 + github.com/go-openapi/runtime/server-middleware v0.30.0 + github.com/go-openapi/spec v0.22.5 + github.com/go-openapi/strfmt v0.26.3 + github.com/go-openapi/swag v0.26.1 + github.com/go-openapi/validate v0.25.3 github.com/google/uuid v1.6.0 github.com/hashicorp/go-sockaddr v1.0.7 github.com/hashicorp/golang-lru/v2 v2.0.7 @@ -45,14 +46,14 @@ require ( github.com/twmb/franz-go/pkg/kfake v0.0.0-20260515175617-8268a5d078c0 github.com/twmb/franz-go/plugin/kslog v1.0.0 github.com/xlab/treeprint v1.2.0 - go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.68.0 - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 - go.opentelemetry.io/otel v1.43.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 - go.opentelemetry.io/otel/sdk v1.43.0 - go.opentelemetry.io/otel/trace v1.43.0 + go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.69.0 + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0 + go.opentelemetry.io/otel v1.44.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.44.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.44.0 + go.opentelemetry.io/otel/sdk v1.44.0 + go.opentelemetry.io/otel/trace v1.44.0 golang.org/x/mod v0.36.0 golang.org/x/net v0.55.0 golang.org/x/text v0.37.0 @@ -64,15 +65,15 @@ require ( require ( github.com/armon/go-metrics v0.4.1 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.23 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.23 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.23 // indirect - github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.24 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.9 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.23 // indirect - github.com/aws/aws-sdk-go-v2/service/signin v1.0.11 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.30.17 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.21 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.29 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.29 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.29 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.30 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.12 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.29 // indirect + github.com/aws/aws-sdk-go-v2/service/signin v1.2.0 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.31.3 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.6 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/davecgh/go-spew v1.1.1 // indirect @@ -81,23 +82,23 @@ require ( github.com/felixge/httpsnoop v1.0.4 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-openapi/jsonpointer v0.22.5 // indirect - github.com/go-openapi/jsonreference v0.21.5 // indirect - github.com/go-openapi/swag/cmdutils v0.26.0 // indirect - github.com/go-openapi/swag/conv v0.26.0 // indirect - github.com/go-openapi/swag/fileutils v0.26.0 // indirect - github.com/go-openapi/swag/jsonname v0.26.0 // indirect - github.com/go-openapi/swag/jsonutils v0.26.0 // indirect - github.com/go-openapi/swag/loading v0.26.0 // indirect - github.com/go-openapi/swag/mangling v0.26.0 // indirect - github.com/go-openapi/swag/netutils v0.26.0 // indirect - github.com/go-openapi/swag/stringutils v0.26.0 // indirect - github.com/go-openapi/swag/typeutils v0.26.0 // indirect - github.com/go-openapi/swag/yamlutils v0.26.0 // indirect + github.com/go-openapi/jsonpointer v0.23.1 // indirect + github.com/go-openapi/jsonreference v0.21.6 // indirect + github.com/go-openapi/swag/cmdutils v0.26.1 // indirect + github.com/go-openapi/swag/conv v0.26.1 // indirect + github.com/go-openapi/swag/fileutils v0.26.1 // indirect + github.com/go-openapi/swag/jsonname v0.26.1 // indirect + github.com/go-openapi/swag/jsonutils v0.26.1 // indirect + github.com/go-openapi/swag/loading v0.26.1 // indirect + github.com/go-openapi/swag/mangling v0.26.1 // indirect + github.com/go-openapi/swag/netutils v0.26.1 // indirect + github.com/go-openapi/swag/stringutils v0.26.1 // indirect + github.com/go-openapi/swag/typeutils v0.26.1 // indirect + github.com/go-openapi/swag/yamlutils v0.26.1 // indirect github.com/go-viper/mapstructure/v2 v2.5.0 // indirect github.com/golang-jwt/jwt/v5 v5.3.1 // indirect github.com/google/btree v1.1.3 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/go-metrics v0.5.4 // indirect @@ -120,7 +121,7 @@ require ( github.com/twmb/franz-go/pkg/kmsg v1.13.1 // indirect github.com/xhit/go-str2duration/v2 v2.1.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect - go.opentelemetry.io/otel/metric v1.43.0 // indirect + go.opentelemetry.io/otel/metric v1.44.0 // indirect go.opentelemetry.io/proto/otlp v1.10.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect @@ -130,7 +131,7 @@ require ( golang.org/x/sys v0.45.0 // indirect golang.org/x/time v0.15.0 // indirect golang.org/x/tools v0.44.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 37f1c2ba25..28de57a06a 100644 --- a/go.sum +++ b/go.sum @@ -77,36 +77,36 @@ github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJ github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/aws/aws-sdk-go-v2 v1.41.7 h1:DWpAJt66FmnnaRIOT/8ASTucrvuDPZASqhhLey6tLY8= -github.com/aws/aws-sdk-go-v2 v1.41.7/go.mod h1:4LAfZOPHNVNQEckOACQx60Y8pSRjIkNZQz1w92xpMJc= -github.com/aws/aws-sdk-go-v2/config v1.32.17 h1:FpL4/758/diKwqbytU0prpuiu60fgXKUWCpDJtApclU= -github.com/aws/aws-sdk-go-v2/config v1.32.17/go.mod h1:OXqUMzgXytfoF9JaKkhrOYsyh72t9G+MJH8mMRaexOE= -github.com/aws/aws-sdk-go-v2/credentials v1.19.16 h1:r3RJBuU7X9ibt8RHbMjWE6y60QbKBiII6wSrXnapxSU= -github.com/aws/aws-sdk-go-v2/credentials v1.19.16/go.mod h1:6cx7zqDENJDbBIIWX6P8s0h6hqHC8Avbjh9Dseo27ug= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.23 h1:UuSfcORqNSz/ey3VPRS8TcVH2Ikf0/sC+Hdj400QI6U= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.23/go.mod h1:+G/OSGiOFnSOkYloKj/9M35s74LgVAdJBSD5lsFfqKg= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.23 h1:GpT/TrnBYuE5gan2cZbTtvP+JlHsutdmlV2YfEyNde0= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.23/go.mod h1:xYWD6BS9ywC5bS3sz9Xh04whO/hzK2plt2Zkyrp4JuA= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.23 h1:bpd8vxhlQi2r1hiueOw02f/duEPTMK59Q4QMAoTTtTo= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.23/go.mod h1:15DfR2nw+CRHIk0tqNyifu3G1YdAOy68RftkhMDDwYk= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.24 h1:OQqn11BtaYv1WLUowvcA30MpzIu8Ti4pcLPIIyoKZrA= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.24/go.mod h1:X5ZJyfwVrWA96GzPmUCWFQaEARPR7gCrpq2E92PJwAE= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.9 h1:FLudkZLt5ci0ozzgkVo8BJGwvqNaZbTWb3UcucAateA= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.9/go.mod h1:w7wZ/s9qK7c8g4al+UyoF1Sp/Z45UwMGcqIzLWVQHWk= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.23 h1:pbrxO/kuIwgEsOPLkaHu0O+m4fNgLU8B3vxQ+72jTPw= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.23/go.mod h1:/CMNUqoj46HpS3MNRDEDIwcgEnrtZlKRaHNaHxIFpNA= -github.com/aws/aws-sdk-go-v2/service/signin v1.0.11 h1:TdJ+HdzOBhU8+iVAOGUTU63VXopcumCOF1paFulHWZc= -github.com/aws/aws-sdk-go-v2/service/signin v1.0.11/go.mod h1:R82ZRExE/nheo0N+T8zHPcLRTcH8MGsnR3BiVGX0TwI= -github.com/aws/aws-sdk-go-v2/service/sns v1.39.17 h1:synXIPC/L4Cc489P0XDcrVJzHSLj7krKRpFLalbGM2k= -github.com/aws/aws-sdk-go-v2/service/sns v1.39.17/go.mod h1:4ABZnI23uNK37waIjGwkubnCwGhepIt9x1GvASfljJA= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.17 h1:7byT8HUWrgoRp6sXjxtZwgOKfhss5fW6SkLBtqzgRoE= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.17/go.mod h1:xNWknVi4Ezm1vg1QsB/5EWpAJURq22uqd38U8qKvOJc= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.21 h1:+1Kl1zx6bWi4X7cKi3VYh29h8BvsCoHQEQ6ST9X8w7w= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.21/go.mod h1:4vIRDq+CJB2xFAXZ+YgGUTiEft7oAQlhIs71xcSeuVg= -github.com/aws/aws-sdk-go-v2/service/sts v1.42.1 h1:F/M5Y9I3nwr2IEpshZgh1GeHpOItExNM9L1euNuh/fk= -github.com/aws/aws-sdk-go-v2/service/sts v1.42.1/go.mod h1:mTNxImtovCOEEuD65mKW7DCsL+2gjEH+RPEAexAzAio= -github.com/aws/smithy-go v1.25.1 h1:J8ERsGSU7d+aCmdQur5Txg6bVoYelvQJgtZehD12GkI= -github.com/aws/smithy-go v1.25.1/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= +github.com/aws/aws-sdk-go-v2 v1.42.0 h1:XvXMJTkFQtpBKIWZnmr9ZEOc2InWM2yldjXEJ/bymhA= +github.com/aws/aws-sdk-go-v2 v1.42.0/go.mod h1:27+ACypSLljLAEKsCYOmrjKh83vuTRkuAe9Uv/3A4bg= +github.com/aws/aws-sdk-go-v2/config v1.32.25 h1:ACCejvStYoilgwrfegSt5ZntCbPrk52qfwyNcnl3omM= +github.com/aws/aws-sdk-go-v2/config v1.32.25/go.mod h1:LJyU8sDRbXUxFn8xMJIGP+v9QYYwveNLI8a/giAOiAs= +github.com/aws/aws-sdk-go-v2/credentials v1.19.24 h1:2hQqYCV9yqyePQ9o6dCrZc/zO8U3TwPr9mIKlZnPu/I= +github.com/aws/aws-sdk-go-v2/credentials v1.19.24/go.mod h1:IDwpACtwqHLISdzfwUUNq4P9DsB/h5BLg4FwJPNfqFY= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.29 h1:r6qZHbT+wxgWO/e9vYNUEtg7lv5+UN3pRqKhLXvnArg= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.29/go.mod h1:QRnaRcTVGKPGRy8w78HMQtKUGRYcnMZAANATkeVA6Mo= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.29 h1:f3vKqSo13fhTYb+JEcXwXefZQE26I1FB5eTSniU67ko= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.29/go.mod h1:MzoLFUArKGpGD+ukmPiTPG1X5x4o6M2kq4v2dr1FiEc= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.29 h1:RdwIf/CuUsvJX3RgJagbOyotl/cxoLY4xviKuE7p2GY= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.29/go.mod h1:71wt8W2EgswdZy9Mf9KNnzxZ3TiZlv4caKghPktDOkA= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.30 h1:VTGy885W5DKBxWRUJbym9hytNaYzsyaPkCHGRRMAOhU= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.30/go.mod h1:AS0HycUvJRFvTt613AYDOgO2jzw+00cVSMny8XB3yMY= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.12 h1:ZD2+BSw9vFsNlKYIasSNt3uDbjqqXIBcM13UJv/Lx2k= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.12/go.mod h1:Ms4zlcVBbXbiP7EVLhl+lgjvA/a7YphqQ3Ih3174EmI= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.29 h1:DRebniUGZ2MqiiIVmQJ04vIXr918hubdHMnarSLEWyU= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.29/go.mod h1:LfRkPCD8YHDM2E5eTkos2UpwYeZnBcVarTa8L59bJHA= +github.com/aws/aws-sdk-go-v2/service/signin v1.2.0 h1:3nXpRcFwRCW8n7HgO2QGy0Dc20eQNfBuUemGQhpF8m8= +github.com/aws/aws-sdk-go-v2/service/signin v1.2.0/go.mod h1:LxYujSTLPRlp2vTtcUO/+1ilrew8ytt6SvQyOgejzFQ= +github.com/aws/aws-sdk-go-v2/service/sns v1.40.1 h1:DLrOlgom0+OYnNaSiVxAXtR6obPjVlbmD+7w5wim9sc= +github.com/aws/aws-sdk-go-v2/service/sns v1.40.1/go.mod h1:V9szvM64GdG5VJUeDRstvLmt/ozgWiSNg3gYnp3mSkk= +github.com/aws/aws-sdk-go-v2/service/sso v1.31.3 h1:ey1XLTYXb9PcLt4535632o5kCGXNXEhNb620Dqwuylo= +github.com/aws/aws-sdk-go-v2/service/sso v1.31.3/go.mod h1:Lk7PlmoTYryQmyBG0EXqj5BcUbj3whXdU2s3yGI3EAc= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.6 h1:yLr03zQE/5Eu5l3QU0Si+xMbLMbSDF2YXsigqXngs6g= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.6/go.mod h1:Q5N6icH+KJZDLh+ESNwzdv6cZ6vLFF/egy3IOxWhmz4= +github.com/aws/aws-sdk-go-v2/service/sts v1.43.3 h1:VrIhKRCSK1umelSgB9RghvA9RTUYeQffyAS5ApXehNI= +github.com/aws/aws-sdk-go-v2/service/sts v1.43.3/go.mod h1:r8wkDOuLaaMFqFiYAb8dGY2A3gJCOujMc6CFOVC4Zhc= +github.com/aws/smithy-go v1.27.1 h1:4T340VFndXtADGF52gYa1POyL7s9E4Z1OeZ1hCscIw8= +github.com/aws/smithy-go v1.27.1/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -188,54 +188,56 @@ github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= 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-openapi/analysis v0.25.0 h1:EnjAq1yO8wEO9HbPmY8vLPEIkdZuuFhCAKBPvCB7bCs= -github.com/go-openapi/analysis v0.25.0/go.mod h1:5WFTRE43WLkPG9r9OtlMfqkkvUTYLVVCIxLlEpyF8kE= -github.com/go-openapi/errors v0.22.7 h1:JLFBGC0Apwdzw3484MmBqspjPbwa2SHvpDm0u5aGhUA= -github.com/go-openapi/errors v0.22.7/go.mod h1://QW6SD9OsWtH6gHllUCddOXDL0tk0ZGNYHwsw4sW3w= -github.com/go-openapi/jsonpointer v0.22.5 h1:8on/0Yp4uTb9f4XvTrM2+1CPrV05QPZXu+rvu2o9jcA= -github.com/go-openapi/jsonpointer v0.22.5/go.mod h1:gyUR3sCvGSWchA2sUBJGluYMbe1zazrYWIkWPjjMUY0= -github.com/go-openapi/jsonreference v0.21.5 h1:6uCGVXU/aNF13AQNggxfysJ+5ZcU4nEAe+pJyVWRdiE= -github.com/go-openapi/jsonreference v0.21.5/go.mod h1:u25Bw85sX4E2jzFodh1FOKMTZLcfifd1Q+iKKOUxExw= -github.com/go-openapi/loads v0.23.3 h1:g5Xap1JfwKkUnZdn+S0L3SzBDpcTIYzZ5Qaag0YDkKQ= -github.com/go-openapi/loads v0.23.3/go.mod h1:NOH07zLajXo8y55hom0omlHWDVVvCwBM/S+csCK8LqA= -github.com/go-openapi/runtime v0.29.4 h1:k2lDxrGoSAJRdhFG2tONKMpkizY/4X1cciSdtzk4Jjo= -github.com/go-openapi/runtime v0.29.4/go.mod h1:K0k/2raY6oqXJnZAgWJB2i/12QKrhUKpZcH4PfV9P18= -github.com/go-openapi/spec v0.22.4 h1:4pxGjipMKu0FzFiu/DPwN3CTBRlVM2yLf/YTWorYfDQ= -github.com/go-openapi/spec v0.22.4/go.mod h1:WQ6Ai0VPWMZgMT4XySjlRIE6GP1bGQOtEThn3gcWLtQ= -github.com/go-openapi/strfmt v0.26.2 h1:ysjheCh4i1rmFEo2LanhELDNucNzfWTZhUDKgWWPaFM= -github.com/go-openapi/strfmt v0.26.2/go.mod h1:fXh1e449cyUn2NYuz+wb3wARBUdMl7qPEZwX00nqivY= -github.com/go-openapi/swag v0.26.0 h1:GVDXCmfvhfu1BxiHo8/FA+BbKmhecHnG3varjON5/RI= -github.com/go-openapi/swag v0.26.0/go.mod h1:82g3193sZJRbocs7bNCqGfIgq8pkuwVwCfhKIRlEQF0= -github.com/go-openapi/swag/cmdutils v0.26.0 h1:iowihOcvq7y4egO8cOq0dmfohz6wfeQ63U1EnuhO2TU= -github.com/go-openapi/swag/cmdutils v0.26.0/go.mod h1:Sm1MVFMkF6guJJ+pQqHnQA3N0j9qALV3NxzDSv6bETM= -github.com/go-openapi/swag/conv v0.26.0 h1:5yGGsPYI1ZCva93U0AoKi/iZrNhaJEjr324YVsiD89I= -github.com/go-openapi/swag/conv v0.26.0/go.mod h1:tpAmIL7X58VPnHHiSO4uE3jBeRamGsFsfdDeDtb5ECE= -github.com/go-openapi/swag/fileutils v0.26.0 h1:WJoPRvsA7QRiiWluowkLJa9jaYR7FCuxmDvnCgaRRxU= -github.com/go-openapi/swag/fileutils v0.26.0/go.mod h1:0WDJ7lp67eNjPMO50wAWYlKvhOb6CQ37rzR7wrgI8Tc= -github.com/go-openapi/swag/jsonname v0.26.0 h1:gV1NFX9M8avo0YSpmWogqfQISigCmpaiNci8cGECU5w= -github.com/go-openapi/swag/jsonname v0.26.0/go.mod h1:urBBR8bZNoDYGr653ynhIx+gTeIz0ARZxHkAPktJK2M= -github.com/go-openapi/swag/jsonutils v0.26.0 h1:FawFML2iAXsPqmERscuMPIHmFsoP1tOqWkxBaKNMsnA= -github.com/go-openapi/swag/jsonutils v0.26.0/go.mod h1:2VmA0CJlyFqgawOaPI9psnjFDqzyivIqLYN34t9p91E= -github.com/go-openapi/swag/jsonutils/fixtures_test v0.26.0 h1:apqeINu/ICHouqiRZbyFvuDge5jCmmLTqGQ9V95EaOM= -github.com/go-openapi/swag/jsonutils/fixtures_test v0.26.0/go.mod h1:AyM6QT8uz5IdKxk5akv0y6u4QvcL9GWERt0Jx/F/R8Y= -github.com/go-openapi/swag/loading v0.26.0 h1:Apg6zaKhCJurpJer0DCxq99qwmhFddBhaMX7kilDcko= -github.com/go-openapi/swag/loading v0.26.0/go.mod h1:dBxQ/6V2uBaAQdevN18VELE6xSpJWZxLX4txe12JwDg= -github.com/go-openapi/swag/mangling v0.26.0 h1:Du2YC4YLA/Y5m/YKQd7AnY5qq0wRKSFZTTt8ktFaXcQ= -github.com/go-openapi/swag/mangling v0.26.0/go.mod h1:jifS7W9vbg+pw63bT+GI53otluMQL3CeemuyCHKwVx0= -github.com/go-openapi/swag/netutils v0.26.0 h1:CmZp+ZT7HrmFwrC3GdGsXBq2+42T1bjKBapcqVpIs3c= -github.com/go-openapi/swag/netutils v0.26.0/go.mod h1:5iK+Ok3ZohWWex1C50BFTPexi03UaPwjW4Oj8kgrpwo= -github.com/go-openapi/swag/stringutils v0.26.0 h1:qZQngLxs5s7SLijc3N2ZO+fUq2o8LjuWAASSrJuh+xg= -github.com/go-openapi/swag/stringutils v0.26.0/go.mod h1:sWn5uY+QIIspwPhvgnqJsH8xqFT2ZbYcvbcFanRyhFE= -github.com/go-openapi/swag/typeutils v0.26.0 h1:2kdEwdiNWy+JJdOvu5MA2IIg2SylWAFuuyQIKYybfq4= -github.com/go-openapi/swag/typeutils v0.26.0/go.mod h1:oovDuIUvTrEHVMqWilQzKzV4YlSKgyZmFh7AlfABNVE= -github.com/go-openapi/swag/yamlutils v0.26.0 h1:H7O8l/8NJJQ/oiReEN+oMpnGMyt8G0hl460nRZxhLMQ= -github.com/go-openapi/swag/yamlutils v0.26.0/go.mod h1:1evKEGAtP37Pkwcc7EWMF0hedX0/x3Rkvei2wtG/TbU= -github.com/go-openapi/testify/enable/yaml/v2 v2.4.2 h1:5zRca5jw7lzVREKCZVNBpysDNBjj74rBh0N2BGQbSR0= -github.com/go-openapi/testify/enable/yaml/v2 v2.4.2/go.mod h1:XVevPw5hUXuV+5AkI1u1PeAm27EQVrhXTTCPAF85LmE= -github.com/go-openapi/testify/v2 v2.4.2 h1:tiByHpvE9uHrrKjOszax7ZvKB7QOgizBWGBLuq0ePx4= -github.com/go-openapi/testify/v2 v2.4.2/go.mod h1:SgsVHtfooshd0tublTtJ50FPKhujf47YRqauXXOUxfw= -github.com/go-openapi/validate v0.25.2 h1:12NsfLAwGegqbGWr2CnvT65X/Q2USJipmJ9b7xDJZz0= -github.com/go-openapi/validate v0.25.2/go.mod h1:Pgl1LpPPGFnZ+ys4/hTlDiRYQdI1ocKypgE+8Q8BLfY= +github.com/go-openapi/analysis v0.25.2 h1:I0vy4n3alz+DHTiN1PRhCb7QZxkK6g5YmswZKv2TKuw= +github.com/go-openapi/analysis v0.25.2/go.mod h1:Uhs1t/2XR10EnwONYILGEzw8gcfGIG5Xk5K2AxnhqDo= +github.com/go-openapi/errors v0.22.8 h1:oP7sW7TWc3wFFjrzzj0nI83H2qMBkNjNfSd+XRejk/I= +github.com/go-openapi/errors v0.22.8/go.mod h1:BuUoHcYrU6E7V9gfj1I5wLQqgtIHnup/alXZ8KdgQ0w= +github.com/go-openapi/jsonpointer v0.23.1 h1:1HBACs7XIwR2RcmItfdSFlALhGbe6S92p0ry4d1GWg4= +github.com/go-openapi/jsonpointer v0.23.1/go.mod h1:iWRmZTrGn7XwYhtPt/fvdSFj1OfNBngqRT2UG3BxSqY= +github.com/go-openapi/jsonreference v0.21.6 h1:NZ5nGfnaM1n4I43Xjm1e5/M2GjOwQwndQz22uhxwD+Y= +github.com/go-openapi/jsonreference v0.21.6/go.mod h1:xzbgtQ3ZbWxvET3AxdzCJlJt6vkovbf+IfSPJjD0tUY= +github.com/go-openapi/loads v0.24.0 h1:4LLorXRPTzIN9V6ngMUZbAscsBOUBk3Oa8cClu/bFrQ= +github.com/go-openapi/loads v0.24.0/go.mod h1:xQMgX+hw5xRAhGrcDXxeMw78IFqUpIzhleu3HqPhyF4= +github.com/go-openapi/runtime v0.32.3 h1:J7Ycy5DJmhhP1By3NifhRUjnkXTrk21qbeqSULjwX8U= +github.com/go-openapi/runtime v0.32.3/go.mod h1:/WTQi0fa5DiGnnCXQKsTkSm15OzJp8Uz3H2t+67TBr4= +github.com/go-openapi/runtime/server-middleware v0.30.0 h1:8rPoJ/xv7JL8BsovaqboKETlpWBArVh8n+0L/GyePog= +github.com/go-openapi/runtime/server-middleware v0.30.0/go.mod h1:OYNT/TxNvB/VK5oe4htM2jDTwlEXuejVJmu0DVZfAMs= +github.com/go-openapi/spec v0.22.5 h1:KhO7RBlKQfonUWX2WzQCoLIXVA6AcNqDGZ3a1Dutdlo= +github.com/go-openapi/spec v0.22.5/go.mod h1:vxpOtMya5TXtENXKE5bKqv5NjocVhyhxHrlZfvKnZ74= +github.com/go-openapi/strfmt v0.26.3 h1:rzmslHarJgBbf2qfGge+X3htclQfmXqBZMm0Too0HhU= +github.com/go-openapi/strfmt v0.26.3/go.mod h1:a5nsUw0oRpQzZeOwx8bi6cKbzFZslpbCKt1LEot+KnQ= +github.com/go-openapi/swag v0.26.1 h1:l5sVEyVpwj+DDYeZyo7wQI/Ebn/mKYIyGB/pFwAfGoQ= +github.com/go-openapi/swag v0.26.1/go.mod h1:yNY38BbIVthxbkDtq1UHBCGasBqjakW3lCR6ANzdBEw= +github.com/go-openapi/swag/cmdutils v0.26.1 h1:f2iE1ijYaJ3nuu5PaEMx3zpEhzhZFgivCJObWEObLIQ= +github.com/go-openapi/swag/cmdutils v0.26.1/go.mod h1:Sm1MVFMkF6guJJ+pQqHnQA3N0j9qALV3NxzDSv6bETM= +github.com/go-openapi/swag/conv v0.26.1 h1:slr5FVkg9Wc3Y5zcwenD8Sd/PQ94b2I/QJI7N7KTBpg= +github.com/go-openapi/swag/conv v0.26.1/go.mod h1:mvQXgPptZk9GTrFgGwWvT4q+dN+zQej9JfmGwnipz1A= +github.com/go-openapi/swag/fileutils v0.26.1 h1:K1XCM2CGhfNsc6YDt6v7Q5+1e59rftYWdcu/isZhvFw= +github.com/go-openapi/swag/fileutils v0.26.1/go.mod h1:mYUgxQAKX4ShS3qvvySx+/9yrlUnDhjiD1CalaQl8lQ= +github.com/go-openapi/swag/jsonname v0.26.1 h1:VReupaV6WxlAsCn0e4DUfgV6bPmINnPpyJDLqSfNPcE= +github.com/go-openapi/swag/jsonname v0.26.1/go.mod h1:OvdW6BoWoj33pTfi7x9vFrgmT+fk7aw0BRwvCE0YOuc= +github.com/go-openapi/swag/jsonutils v0.26.1 h1:2hdBfFkHg+7Wrz2VsCbeyR6hzkRDs7AztnMR2u84yOY= +github.com/go-openapi/swag/jsonutils v0.26.1/go.mod h1:U+RMJH3wa+6BRiphuRtIyI8fW9HPFqFQ4sHk2oRx0UQ= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.26.1 h1:1CD7NiLLb/TXl3tOnFYU4b+mNfb5rtgHkaA+q7RMYYQ= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.26.1/go.mod h1:ZWafc8nMdYzTE3uYY6W86f0n46+IF0g4uUyRhJw/kXc= +github.com/go-openapi/swag/loading v0.26.1 h1:E9K4wqXeROlhjFQ13K9zMz6ojFGXIggGe+ad1odrK9w= +github.com/go-openapi/swag/loading v0.26.1/go.mod h1:3qvRIlWzWdq1HvmldwmuJ2ohpcAryN6xVt2OTKd0/7E= +github.com/go-openapi/swag/mangling v0.26.1 h1:gpYI4WuPKFJJVjV5cDLGlDVJhFIxYjQc7yN5eEb4CqM= +github.com/go-openapi/swag/mangling v0.26.1/go.mod h1:POETDH01hqAdASXfw7ISEd9bCOE6xBHOt8NHmGZRmYM= +github.com/go-openapi/swag/netutils v0.26.1 h1:BNctoc39WTAUMxyAs355fExOPzMZtPbZ0ZZ1Am2FR5M= +github.com/go-openapi/swag/netutils v0.26.1/go.mod h1:y02vByhZhQPAVwOX+0KipXFZ/hUbk6G/Enhf5rGaOkQ= +github.com/go-openapi/swag/stringutils v0.26.1 h1:f88uYyTso7TnHrKM/bUBsQ5e2wKf37cpgo6pvbzd9yU= +github.com/go-openapi/swag/stringutils v0.26.1/go.mod h1:Sc6d3bU8fgk5AyZR8/8jEQ+Is/Ald+TD/IIggPN8UJk= +github.com/go-openapi/swag/typeutils v0.26.1 h1:yg42FgMzRR6PVQ3M3qHz1s+Y6/P4HoJ3cBarXa3OVnU= +github.com/go-openapi/swag/typeutils v0.26.1/go.mod h1:VfnV+oUtSP2vCSCn2aJgnr8OevUYemyIzzS1VOzS10o= +github.com/go-openapi/swag/yamlutils v0.26.1 h1:0TSLK+lXs9vfIhAWzBeI/lOzEnIoot6WTCO1aAeWFTk= +github.com/go-openapi/swag/yamlutils v0.26.1/go.mod h1:7W5b7PRX9MxwL7TjeG7H8HkyBGRsIDRObhyMWFgBI2M= +github.com/go-openapi/testify/enable/yaml/v2 v2.5.1 h1:q9NtHwK4qHF7yZziBPvZyv7zWAIk8ok88Gh2mR6Jpc8= +github.com/go-openapi/testify/enable/yaml/v2 v2.5.1/go.mod h1:JW0MXIotCYps/XsgJnG3a8Q7rE5xAiBwoOD5OfaIQBk= +github.com/go-openapi/testify/v2 v2.5.1 h1:TMdhCaw8fUNraVSf3Omoob1dO/AzBfhtFAPW0an6sBo= +github.com/go-openapi/testify/v2 v2.5.1/go.mod h1:SgsVHtfooshd0tublTtJ50FPKhujf47YRqauXXOUxfw= +github.com/go-openapi/validate v0.25.3 h1:4nzAIavcJ7WveHK2+V1UAkZK3kWcjzxZCzjfZAfavKs= +github.com/go-openapi/validate v0.25.3/go.mod h1:GemfuGMyYpIaBoKpX3z8sLywrmxpzWVOoJ7R0VeAVuk= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= @@ -337,8 +339,8 @@ github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= 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.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 h1:5VipnvEpbqr2gA2VbM+nYVbkIF28c5ZQfqCBQ5g2xfk= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0/go.mod h1:Hyl3n6Twe1hvtd9XUXDec4pTvgMSEixRuQKPTMH2bNs= 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= @@ -583,26 +585,26 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.68.0 h1:cuXaPAfIoJKsYjBjPSb2nKZEmgM43zVr25l37IxhKME= -go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.68.0/go.mod h1:BuzhPofpCzlDi/Q/Xjg54M4/3oWqqyDe2Zeq7A2I0QE= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 h1:CqXxU8VOmDefoh0+ztfGaymYbhdB/tT3zs79QaZTNGY= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0/go.mod h1:BuhAPThV8PBHBvg8ZzZ/Ok3idOdhWIodywz2xEcRbJo= -go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= -go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 h1:88Y4s2C8oTui1LGM6bTWkw0ICGcOLCAI5l6zsD1j20k= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0/go.mod h1:Vl1/iaggsuRlrHf/hfPJPvVag77kKyvrLeD10kpMl+A= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0 h1:RAE+JPfvEmvy+0LzyUA25/SGawPwIUbZ6u0Wug54sLc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0/go.mod h1:AGmbycVGEsRx9mXMZ75CsOyhSP6MFIcj/6dnG+vhVjk= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 h1:3iZJKlCZufyRzPzlQhUIWVmfltrXuGyfjREgGP3UUjc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0/go.mod h1:/G+nUPfhq2e+qiXMGxMwumDrP5jtzU+mWN7/sjT2rak= -go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= -go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= -go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= -go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg= -go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw= -go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= -go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= -go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= +go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.69.0 h1:MCcYL7J6Vt/X0kjqbMZkekCmwsurbQRbL69vkiye2lk= +go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.69.0/go.mod h1:3jnStNwSufK+f5ktjL4EPcwtig4rtd81NS70lqHuXl8= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0 h1:8tvICD4vSTOOsNrsI4Ljf6C+6UKvpTEH5XY3JMoyPoo= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0/go.mod h1:z9+yiacE0IHRqM4qFfkbt/JYlmYXgss8GY/jXoNuPJI= +go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU= +go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0 h1:4YsVu3B8+3qtWYYrsUYgn0OG78pN0rnNPRGX4SbokQI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0/go.mod h1:+wnlSn0mD1ADVMe3v9Z/WIaiz6q6gL2J/ejaAmdmv80= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.44.0 h1:qazEJlUOQzhCpzQpFETGby7EdqjI1wsd0W+6Gg1SCTU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.44.0/go.mod h1:fOD2Yefuxixkx3ahVNf0O/PERb6r4OlbxfATVnYvzCo= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.44.0 h1:lgh3PiVrRUWMLOVSkQicxzZll5NjF1r+AtsX1XRIHw0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.44.0/go.mod h1:5Cnhth3m/AgOeTgE3ex12pPmiu/gGtZit03kSzx9X7s= +go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc= +go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo= +go.opentelemetry.io/otel/sdk v1.44.0 h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58= +go.opentelemetry.io/otel/sdk v1.44.0/go.mod h1:Osuydd3Se74nqjAKxid74N5eC+jfEqfTegHRnq58oK0= +go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRks6si09iEfI= +go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA= +go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk= +go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g= go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk= @@ -1036,10 +1038,10 @@ 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/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 h1:VPWxll4HlMw1Vs/qXtN7BvhZqsS9cdAittCNvVENElA= -google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:7QBABkRtR8z+TEnmXTqIqwJLlzrZKVfAUm7tY3yGv0M= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 h1:m8qni9SQFH0tJc1X0vmnpw/0t+AImlSvp30sEupozUg= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa h1:Kjn0N0tCrDgiAFW+lGO4JZ3ck44CehvJQMAwj9QF0G8= +google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:q4lMZS6kskjT5HvCPrnnypcDPVJqT/f4nfxmkE7gryY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa h1:mZHHdPZl0dbGHCflZgAq/Q468DWVFcU2whhB2KAo8fk= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= 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= diff --git a/tracing/tracing.go b/tracing/tracing.go index 77a8684267..5ec7a2a683 100644 --- a/tracing/tracing.go +++ b/tracing/tracing.go @@ -31,7 +31,7 @@ import ( "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/sdk/resource" tracesdk "go.opentelemetry.io/otel/sdk/trace" - semconv "go.opentelemetry.io/otel/semconv/v1.40.0" + semconv "go.opentelemetry.io/otel/semconv/v1.41.0" "go.opentelemetry.io/otel/trace" "go.opentelemetry.io/otel/trace/noop" "google.golang.org/grpc/credentials" From f2bcb83c5140bfda21730c08501145fc57fc4039 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 17 Jun 2026 14:09:58 +0200 Subject: [PATCH 021/120] build(deps): bump the aws group across 1 directory with 4 updates (#5309) Bumps the aws group with 4 updates in the / directory: [github.com/aws/smithy-go](https://github.com/aws/smithy-go), [github.com/go-openapi/runtime/server-middleware](https://github.com/go-openapi/runtime), [github.com/go-openapi/spec](https://github.com/go-openapi/spec) and [github.com/go-openapi/validate](https://github.com/go-openapi/validate). Updates `github.com/aws/smithy-go` from 1.27.1 to 1.27.2 - [Release notes](https://github.com/aws/smithy-go/releases) - [Changelog](https://github.com/aws/smithy-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws/smithy-go/compare/v1.27.1...v1.27.2) Updates `github.com/go-openapi/runtime/server-middleware` from 0.30.0 to 0.32.3 - [Release notes](https://github.com/go-openapi/runtime/releases) - [Commits](https://github.com/go-openapi/runtime/compare/v0.30.0...v0.32.3) Updates `github.com/go-openapi/spec` from 0.22.5 to 0.22.6 - [Release notes](https://github.com/go-openapi/spec/releases) - [Commits](https://github.com/go-openapi/spec/compare/v0.22.5...v0.22.6) Updates `github.com/go-openapi/validate` from 0.25.3 to 0.26.0 - [Release notes](https://github.com/go-openapi/validate/releases) - [Commits](https://github.com/go-openapi/validate/compare/v0.25.3...v0.26.0) --- updated-dependencies: - dependency-name: github.com/aws/smithy-go dependency-version: 1.27.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: aws - dependency-name: github.com/go-openapi/runtime/server-middleware dependency-version: 0.32.3 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: aws - dependency-name: github.com/go-openapi/spec dependency-version: 0.22.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: aws - dependency-name: github.com/go-openapi/validate dependency-version: 0.26.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: aws ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 8 ++++---- go.sum | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index 1ee7a83e85..28d919115a 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/aws/aws-sdk-go-v2/credentials v1.19.24 github.com/aws/aws-sdk-go-v2/service/sns v1.40.1 github.com/aws/aws-sdk-go-v2/service/sts v1.43.3 - github.com/aws/smithy-go v1.27.1 + github.com/aws/smithy-go v1.27.2 github.com/cenkalti/backoff/v4 v4.3.0 github.com/cespare/xxhash/v2 v2.3.0 github.com/coder/quartz v0.3.1 @@ -22,11 +22,11 @@ require ( github.com/go-openapi/errors v0.22.8 github.com/go-openapi/loads v0.24.0 github.com/go-openapi/runtime v0.32.3 - github.com/go-openapi/runtime/server-middleware v0.30.0 - github.com/go-openapi/spec v0.22.5 + github.com/go-openapi/runtime/server-middleware v0.32.3 + github.com/go-openapi/spec v0.22.6 github.com/go-openapi/strfmt v0.26.3 github.com/go-openapi/swag v0.26.1 - github.com/go-openapi/validate v0.25.3 + github.com/go-openapi/validate v0.26.0 github.com/google/uuid v1.6.0 github.com/hashicorp/go-sockaddr v1.0.7 github.com/hashicorp/golang-lru/v2 v2.0.7 diff --git a/go.sum b/go.sum index 28de57a06a..bc7eb97b82 100644 --- a/go.sum +++ b/go.sum @@ -105,8 +105,8 @@ github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.6 h1:yLr03zQE/5Eu5l3QU0Si+xMb github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.6/go.mod h1:Q5N6icH+KJZDLh+ESNwzdv6cZ6vLFF/egy3IOxWhmz4= github.com/aws/aws-sdk-go-v2/service/sts v1.43.3 h1:VrIhKRCSK1umelSgB9RghvA9RTUYeQffyAS5ApXehNI= github.com/aws/aws-sdk-go-v2/service/sts v1.43.3/go.mod h1:r8wkDOuLaaMFqFiYAb8dGY2A3gJCOujMc6CFOVC4Zhc= -github.com/aws/smithy-go v1.27.1 h1:4T340VFndXtADGF52gYa1POyL7s9E4Z1OeZ1hCscIw8= -github.com/aws/smithy-go v1.27.1/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= +github.com/aws/smithy-go v1.27.2 h1:y9NPmSE6am6LjEFPfqHqG/jJk7AauQvhCJONKh7kpzk= +github.com/aws/smithy-go v1.27.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -200,10 +200,10 @@ github.com/go-openapi/loads v0.24.0 h1:4LLorXRPTzIN9V6ngMUZbAscsBOUBk3Oa8cClu/bF github.com/go-openapi/loads v0.24.0/go.mod h1:xQMgX+hw5xRAhGrcDXxeMw78IFqUpIzhleu3HqPhyF4= github.com/go-openapi/runtime v0.32.3 h1:J7Ycy5DJmhhP1By3NifhRUjnkXTrk21qbeqSULjwX8U= github.com/go-openapi/runtime v0.32.3/go.mod h1:/WTQi0fa5DiGnnCXQKsTkSm15OzJp8Uz3H2t+67TBr4= -github.com/go-openapi/runtime/server-middleware v0.30.0 h1:8rPoJ/xv7JL8BsovaqboKETlpWBArVh8n+0L/GyePog= -github.com/go-openapi/runtime/server-middleware v0.30.0/go.mod h1:OYNT/TxNvB/VK5oe4htM2jDTwlEXuejVJmu0DVZfAMs= -github.com/go-openapi/spec v0.22.5 h1:KhO7RBlKQfonUWX2WzQCoLIXVA6AcNqDGZ3a1Dutdlo= -github.com/go-openapi/spec v0.22.5/go.mod h1:vxpOtMya5TXtENXKE5bKqv5NjocVhyhxHrlZfvKnZ74= +github.com/go-openapi/runtime/server-middleware v0.32.3 h1:Y/6h9ix9NCoMG04XazRwX6eA3alh4+JZ6qXdar5yd24= +github.com/go-openapi/runtime/server-middleware v0.32.3/go.mod h1:fYPep4GdTwg/XqZUjR40uIM/8C12Ba5M+MrGCiwpTHo= +github.com/go-openapi/spec v0.22.6 h1:Tyy1pLaNCM8GBCFLoGYLonjJi6zykqyLCjXLc19ZPic= +github.com/go-openapi/spec v0.22.6/go.mod h1:HZvTHat+iH0PALQRWhrqIHtU/PEqxqd89fu0MxGlMeM= github.com/go-openapi/strfmt v0.26.3 h1:rzmslHarJgBbf2qfGge+X3htclQfmXqBZMm0Too0HhU= github.com/go-openapi/strfmt v0.26.3/go.mod h1:a5nsUw0oRpQzZeOwx8bi6cKbzFZslpbCKt1LEot+KnQ= github.com/go-openapi/swag v0.26.1 h1:l5sVEyVpwj+DDYeZyo7wQI/Ebn/mKYIyGB/pFwAfGoQ= @@ -236,8 +236,8 @@ github.com/go-openapi/testify/enable/yaml/v2 v2.5.1 h1:q9NtHwK4qHF7yZziBPvZyv7zW github.com/go-openapi/testify/enable/yaml/v2 v2.5.1/go.mod h1:JW0MXIotCYps/XsgJnG3a8Q7rE5xAiBwoOD5OfaIQBk= github.com/go-openapi/testify/v2 v2.5.1 h1:TMdhCaw8fUNraVSf3Omoob1dO/AzBfhtFAPW0an6sBo= github.com/go-openapi/testify/v2 v2.5.1/go.mod h1:SgsVHtfooshd0tublTtJ50FPKhujf47YRqauXXOUxfw= -github.com/go-openapi/validate v0.25.3 h1:4nzAIavcJ7WveHK2+V1UAkZK3kWcjzxZCzjfZAfavKs= -github.com/go-openapi/validate v0.25.3/go.mod h1:GemfuGMyYpIaBoKpX3z8sLywrmxpzWVOoJ7R0VeAVuk= +github.com/go-openapi/validate v0.26.0 h1:dxWzQ3F+vb1SajqUxHjwb5T4mTpSHmdrtv5Bi7+ZNhw= +github.com/go-openapi/validate v0.26.0/go.mod h1:b4o00uq7fJeJA+wWhVFCJpKTctzeFwzZImGGmHsl2JA= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= From f29d44c64917858bb313b389a8808d0bdbafd5c5 Mon Sep 17 00:00:00 2001 From: Siavash Safi Date: Wed, 17 Jun 2026 18:22:45 +0200 Subject: [PATCH 022/120] docs: add performance issue fields to bug report template (#5305) Ask reporters of performance issues to provide CPU and memory profiles from both the previous and current versions, along with the number of alerts and silences. Reference the upstream Go diagnostics docs for capturing profiles and point to pprof.me for uploading and sharing profile diffs. Also add a prometheus-operator checklist so users confirm the issue is not a missing operator feature or that it reproduces when running Alertmanager directly. Signed-off-by: Siavash Safi --- .github/ISSUE_TEMPLATE/bug_report.yml | 47 +++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 139aea2c67..bb0f66dbb1 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -72,3 +72,50 @@ body: label: Logs description: Insert Prometheus and Alertmanager logs relevant to the issue here. render: text + - type: markdown + attributes: + value: | + ## Performance issues + + The fields below are only relevant if you are reporting a performance issue (e.g. high CPU or memory usage). You can skip them otherwise. + + You can upload the captured profiles to https://pprof.me/ and share the link to the profile diff between the previous and current versions. + - type: textarea + attributes: + label: CPU profiles + description: | + Please attach CPU profiles from both the previous version (where performance was acceptable) and the current version (where you observe the regression). This helps us compare and identify the cause. + + See the upstream Go documentation on diagnostics for how to capture profiles: https://go.dev/doc/diagnostics. You can capture a CPU profile with: + + `go tool pprof http://:9093/debug/pprof/profile?seconds=30` + - type: textarea + attributes: + label: Memory profiles + description: | + Please attach memory (heap) profiles from both the previous version (where performance was acceptable) and the current version (where you observe the regression). + + See the upstream Go documentation on diagnostics: https://go.dev/doc/diagnostics. You can capture a heap profile with: + + `go tool pprof http://:9093/debug/pprof/heap` + - type: input + attributes: + label: Number of alerts + description: Approximate number of alerts Alertmanager is handling when the issue occurs. + - type: input + attributes: + label: Number of silences + description: Approximate number of silences configured when the issue occurs. + - type: markdown + attributes: + value: | + ## Using prometheus-operator? + + If you deploy Alertmanager via [prometheus-operator](https://github.com/prometheus-operator/prometheus-operator), please confirm the checklist below before opening the issue. + - type: checkboxes + attributes: + label: prometheus-operator checklist + description: Only applicable if you use prometheus-operator to manage Alertmanager. + options: + - label: I have confirmed this issue is not caused by a missing feature in prometheus-operator. + - label: I have reproduced this issue when running Alertmanager directly (without prometheus-operator). From a3734badead5e10aefbb9ea7f9ace717686bf003 Mon Sep 17 00:00:00 2001 From: Christoph Maser Date: Thu, 18 Jun 2026 09:34:40 +0200 Subject: [PATCH 023/120] refactor(msteamsv2): move configuration types into msteamsv2 package (#5310) Signed-off-by: Christoph Maser --- config/config.go | 3 +- config/notifiers.go | 36 ------------------- notify/msteamsv2/config.go | 58 ++++++++++++++++++++++++++++++ notify/msteamsv2/msteamsv2.go | 5 ++- notify/msteamsv2/msteamsv2_test.go | 17 +++++---- 5 files changed, 70 insertions(+), 49 deletions(-) create mode 100644 notify/msteamsv2/config.go diff --git a/config/config.go b/config/config.go index aeea0560a3..efcd15c464 100644 --- a/config/config.go +++ b/config/config.go @@ -37,6 +37,7 @@ import ( "github.com/prometheus/alertmanager/notify/jira" "github.com/prometheus/alertmanager/notify/mattermost" "github.com/prometheus/alertmanager/notify/msteams" + "github.com/prometheus/alertmanager/notify/msteamsv2" "github.com/prometheus/alertmanager/notify/webhook" "github.com/prometheus/alertmanager/timeinterval" "github.com/prometheus/alertmanager/tracing" @@ -965,7 +966,7 @@ type Receiver struct { TelegramConfigs []*TelegramConfig `yaml:"telegram_configs,omitempty" json:"telegram_configs,omitempty"` WebexConfigs []*WebexConfig `yaml:"webex_configs,omitempty" json:"webex_configs,omitempty"` MSTeamsConfigs []*msteams.MSTeamsConfig `yaml:"msteams_configs,omitempty" json:"msteams_configs,omitempty"` - MSTeamsV2Configs []*MSTeamsV2Config `yaml:"msteamsv2_configs,omitempty" json:"msteamsv2_configs,omitempty"` + MSTeamsV2Configs []*msteamsv2.MSTeamsV2Config `yaml:"msteamsv2_configs,omitempty" json:"msteamsv2_configs,omitempty"` JiraConfigs []*jira.JiraConfig `yaml:"jira_configs,omitempty" json:"jira_configs,omitempty"` RocketchatConfigs []*RocketchatConfig `yaml:"rocketchat_configs,omitempty" json:"rocketchat_configs,omitempty"` MattermostConfigs []*mattermost.MattermostConfig `yaml:"mattermost_configs,omitempty" json:"mattermost_configs,omitempty"` diff --git a/config/notifiers.go b/config/notifiers.go index 46626cb7a3..ee981a3fb9 100644 --- a/config/notifiers.go +++ b/config/notifiers.go @@ -164,14 +164,6 @@ var ( Message: `{{ template "telegram.default.message" . }}`, ParseMode: "HTML", } - - DefaultMSTeamsV2Config = MSTeamsV2Config{ - NotifierConfig: amcommoncfg.NotifierConfig{ - VSendResolved: true, - }, - Title: `{{ template "msteamsv2.default.title" . }}`, - Text: `{{ template "msteamsv2.default.text" . }}`, - } ) // WebexConfig configures notifications via Webex. @@ -788,34 +780,6 @@ func (c *TelegramConfig) UnmarshalYAML(unmarshal func(any) error) error { return nil } -type MSTeamsV2Config struct { - amcommoncfg.NotifierConfig `yaml:",inline" json:",inline"` - HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"` - WebhookURL *amcommoncfg.SecretURL `yaml:"webhook_url,omitempty" json:"webhook_url,omitempty"` - WebhookURLFile string `yaml:"webhook_url_file,omitempty" json:"webhook_url_file,omitempty"` - - Title string `yaml:"title,omitempty" json:"title,omitempty"` - Text string `yaml:"text,omitempty" json:"text,omitempty"` -} - -func (c *MSTeamsV2Config) UnmarshalYAML(unmarshal func(any) error) error { - *c = DefaultMSTeamsV2Config - type plain MSTeamsV2Config - if err := unmarshal((*plain)(c)); err != nil { - return err - } - - if c.WebhookURL == nil && c.WebhookURLFile == "" { - return errors.New("one of webhook_url or webhook_url_file must be configured") - } - - if c.WebhookURL != nil && len(c.WebhookURLFile) > 0 { - return errors.New("at most one of webhook_url & webhook_url_file must be configured") - } - - return nil -} - type RocketchatAttachmentField struct { Short *bool `json:"short"` Title string `json:"title,omitempty"` diff --git a/notify/msteamsv2/config.go b/notify/msteamsv2/config.go new file mode 100644 index 0000000000..18de500b96 --- /dev/null +++ b/notify/msteamsv2/config.go @@ -0,0 +1,58 @@ +// Copyright The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package msteamsv2 + +import ( + "errors" + + commoncfg "github.com/prometheus/common/config" + + amcommoncfg "github.com/prometheus/alertmanager/config/common" +) + +var DefaultMSTeamsV2Config = MSTeamsV2Config{ + NotifierConfig: amcommoncfg.NotifierConfig{ + VSendResolved: true, + }, + Title: `{{ template "msteamsv2.default.title" . }}`, + Text: `{{ template "msteamsv2.default.text" . }}`, +} + +type MSTeamsV2Config struct { + amcommoncfg.NotifierConfig `yaml:",inline" json:",inline"` + HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"` + WebhookURL *amcommoncfg.SecretURL `yaml:"webhook_url,omitempty" json:"webhook_url,omitempty"` + WebhookURLFile string `yaml:"webhook_url_file,omitempty" json:"webhook_url_file,omitempty"` + + Title string `yaml:"title,omitempty" json:"title,omitempty"` + Text string `yaml:"text,omitempty" json:"text,omitempty"` +} + +func (c *MSTeamsV2Config) UnmarshalYAML(unmarshal func(any) error) error { + *c = DefaultMSTeamsV2Config + type plain MSTeamsV2Config + if err := unmarshal((*plain)(c)); err != nil { + return err + } + + if c.WebhookURL == nil && c.WebhookURLFile == "" { + return errors.New("one of webhook_url or webhook_url_file must be configured") + } + + if c.WebhookURL != nil && len(c.WebhookURLFile) > 0 { + return errors.New("at most one of webhook_url & webhook_url_file must be configured") + } + + return nil +} diff --git a/notify/msteamsv2/msteamsv2.go b/notify/msteamsv2/msteamsv2.go index dc87666238..5602d503dc 100644 --- a/notify/msteamsv2/msteamsv2.go +++ b/notify/msteamsv2/msteamsv2.go @@ -29,7 +29,6 @@ import ( amcommoncfg "github.com/prometheus/alertmanager/config/common" - "github.com/prometheus/alertmanager/config" "github.com/prometheus/alertmanager/notify" "github.com/prometheus/alertmanager/template" "github.com/prometheus/alertmanager/types" @@ -42,7 +41,7 @@ const ( ) type Notifier struct { - conf *config.MSTeamsV2Config + conf *MSTeamsV2Config tmpl *template.Template logger *slog.Logger client *http.Client @@ -86,7 +85,7 @@ type teamsMessage struct { } // New returns a new notifier that uses the Microsoft Teams Power Platform connector. -func New(c *config.MSTeamsV2Config, t *template.Template, l *slog.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) { +func New(c *MSTeamsV2Config, t *template.Template, l *slog.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) { client, err := notify.NewClientWithTracing(*c.HTTPConfig, "msteamsv2", httpOpts...) if err != nil { return nil, err diff --git a/notify/msteamsv2/msteamsv2_test.go b/notify/msteamsv2/msteamsv2_test.go index 5bae1ab790..d9f570bb29 100644 --- a/notify/msteamsv2/msteamsv2_test.go +++ b/notify/msteamsv2/msteamsv2_test.go @@ -31,7 +31,6 @@ import ( amcommoncfg "github.com/prometheus/alertmanager/config/common" - "github.com/prometheus/alertmanager/config" "github.com/prometheus/alertmanager/notify" "github.com/prometheus/alertmanager/notify/test" "github.com/prometheus/alertmanager/types" @@ -42,7 +41,7 @@ var testWebhookURL, _ = url.Parse("https://example.westeurope.logic.azure.com:44 func TestMSTeamsV2Retry(t *testing.T) { notifier, err := New( - &config.MSTeamsV2Config{ + &MSTeamsV2Config{ WebhookURL: &amcommoncfg.SecretURL{URL: testWebhookURL}, HTTPConfig: &commoncfg.HTTPClientConfig{}, }, @@ -75,7 +74,7 @@ func TestNotifier_Notify_WithReason(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { notifier, err := New( - &config.MSTeamsV2Config{ + &MSTeamsV2Config{ WebhookURL: &amcommoncfg.SecretURL{URL: testWebhookURL}, HTTPConfig: &commoncfg.HTTPClientConfig{}, }, @@ -125,14 +124,14 @@ func TestMSTeamsV2Templating(t *testing.T) { for _, tc := range []struct { title string - cfg *config.MSTeamsV2Config + cfg *MSTeamsV2Config retry bool errMsg string }{ { title: "full-blown message", - cfg: &config.MSTeamsV2Config{ + cfg: &MSTeamsV2Config{ Title: `{{ template "msteams.default.title" . }}`, Text: `{{ template "msteams.default.text" . }}`, }, @@ -140,14 +139,14 @@ func TestMSTeamsV2Templating(t *testing.T) { }, { title: "title with templating errors", - cfg: &config.MSTeamsV2Config{ + cfg: &MSTeamsV2Config{ Title: "{{ ", }, errMsg: "template: :1: unclosed action", }, { title: "message with templating errors", - cfg: &config.MSTeamsV2Config{ + cfg: &MSTeamsV2Config{ Title: `{{ template "msteams.default.title" . }}`, Text: "{{ ", }, @@ -191,7 +190,7 @@ func TestMSTeamsV2RedactedURL(t *testing.T) { secret := "secret" notifier, err := New( - &config.MSTeamsV2Config{ + &MSTeamsV2Config{ WebhookURL: &amcommoncfg.SecretURL{URL: u}, HTTPConfig: &commoncfg.HTTPClientConfig{}, }, @@ -213,7 +212,7 @@ func TestMSTeamsV2ReadingURLFromFile(t *testing.T) { require.NoError(t, err, "writing to temp file failed") notifier, err := New( - &config.MSTeamsV2Config{ + &MSTeamsV2Config{ WebhookURLFile: f.Name(), HTTPConfig: &commoncfg.HTTPClientConfig{}, }, From 42c9e0ebbaa8484ac8d5076c6c4551aeaf73447f Mon Sep 17 00:00:00 2001 From: prombot Date: Wed, 27 May 2026 14:38:47 +0000 Subject: [PATCH 024/120] Update common Prometheus files Signed-off-by: prombot --- .github/workflows/govulncheck.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/govulncheck.yml b/.github/workflows/govulncheck.yml index 1e13fbd990..adc84d6c5a 100644 --- a/.github/workflows/govulncheck.yml +++ b/.github/workflows/govulncheck.yml @@ -21,4 +21,4 @@ jobs: name: Run govulncheck steps: - id: govulncheck - uses: golang/govulncheck-action@31f7c5463448f83528bd771c2d978d940080c9fd # v1.0.4-unreleased + uses: golang/govulncheck-action@b625fbe08f3bccbe446d94fbf87fcc875a4f50ee # v1.0.4 From df1a9cc7d66927da0a6bb2b92febde393f67cdf2 Mon Sep 17 00:00:00 2001 From: Christoph Maser Date: Fri, 19 Jun 2026 09:08:53 +0200 Subject: [PATCH 025/120] docs: add top level tracing configuration key Signed-off-by: Christoph Maser --- docs/configuration.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/configuration.md b/docs/configuration.md index 5d79af0c0a..e4e001b38d 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -174,6 +174,9 @@ time_intervals: # pass `--enable-feature=event-recorder` on the command line to # activate it. See the Event Recorder section below. [ event_recorder: ] + +# Optional tracing configuration. Configures distributed tracing for Alertmanager. +[ traciing: ] ``` ## Route-related settings @@ -831,7 +834,7 @@ wechat_configs: [ - , ... ] ``` -### `` (Shared) +### `` (Shared) An `http_config` allows configuring the HTTP client that the receiver uses to communicate with HTTP-based API services. From 452c5aee64fa69763f9b2b8b66960335dc565e86 Mon Sep 17 00:00:00 2001 From: s3onghyun Date: Thu, 18 Jun 2026 16:02:18 +0900 Subject: [PATCH 026/120] docs: fix Alertmanager port in amtool config routes example Signed-off-by: s3onghyun --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ab7576db48..256363a883 100644 --- a/README.md +++ b/README.md @@ -327,7 +327,7 @@ and it prints out all receivers the alert would match ordered and separated by ` Example of usage: ``` # View routing tree of remote Alertmanager -$ amtool config routes --alertmanager.url=http://localhost:9090 +$ amtool config routes --alertmanager.url=http://localhost:9093 # Test if alert matches expected receiver $ amtool config routes test --config.file=doc/examples/simple.yml --tree --verify.receivers=team-X-pager service=database owner=team-X From aed23da20995e410b9ec1e68c62d2ebaeaced0cc Mon Sep 17 00:00:00 2001 From: arpitjain099 Date: Wed, 13 May 2026 11:51:05 +0000 Subject: [PATCH 027/120] ci: pin contents: read on mixin, publish, release, ui-ci All four workflows: contents: read. Image pushes use docker_hub + quay creds; the GitHub release publish step in release.yml uses PROMBOT_GITHUB_TOKEN. The default GITHUB_TOKEN is only used for the checkout. Signed-off-by: arpitjain099 --- .github/workflows/mixin.yml | 3 +++ .github/workflows/publish.yml | 4 ++++ .github/workflows/release.yml | 5 +++++ .github/workflows/ui-ci.yml | 3 +++ 4 files changed, 15 insertions(+) diff --git a/.github/workflows/mixin.yml b/.github/workflows/mixin.yml index a21dfada37..d44188e495 100644 --- a/.github/workflows/mixin.yml +++ b/.github/workflows/mixin.yml @@ -4,6 +4,9 @@ on: paths: - "doc/alertmanager-mixin/**" +permissions: + contents: read + jobs: mixin: name: mixin-lint diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index d9e78c774b..910dd8f8f9 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -4,6 +4,10 @@ on: # yamllint disable-line rule:truthy push: branches: - main +# Docker Hub + Quay pushes use their own secrets. +permissions: + contents: read + jobs: ci: name: Run ci diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index edc2cdd460..34d3a92b37 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,6 +4,11 @@ on: # yamllint disable-line rule:truthy push: tags: - v* +# Docker Hub + Quay pushes use their own secrets; GitHub release publish +# uses PROMBOT_GITHUB_TOKEN. +permissions: + contents: read + jobs: ci: name: Run ci diff --git a/.github/workflows/ui-ci.yml b/.github/workflows/ui-ci.yml index 273f691a8d..e6940f2d36 100644 --- a/.github/workflows/ui-ci.yml +++ b/.github/workflows/ui-ci.yml @@ -12,6 +12,9 @@ concurrency: group: ${{ github.workflow }}-${{ github.event.number || github.sha }} cancel-in-progress: true +permissions: + contents: read + jobs: test_mantine_ui: name: Test mantine-ui From 9384b4ec8a9f0088a65a89820fc8d39802d5e634 Mon Sep 17 00:00:00 2001 From: Siavash Safi Date: Fri, 19 Jun 2026 09:50:17 +0200 Subject: [PATCH 028/120] feat(eventrecorder): include inhibition rule name in muted alert events (#5315) The inhibition rule name was available at runtime and emitted in tracing spans, but the recorded inhibition_muted_alert events identified rules only by their matchers and equal labels. Add a name field to the InhibitRule proto message and propagate the rule's configured name through InhibitRuleAsProto so recorded events carry it. Signed-off-by: Siavash Safi --- .../eventrecorderpb/eventrecorder.pb.go | 16 +++++++++++++--- .../eventrecorderpb/eventrecorder.proto | 3 +++ eventrecorder/events.go | 3 ++- eventrecorder/events_test.go | 17 +++++++++++++++++ inhibit/inhibit.go | 2 +- 5 files changed, 36 insertions(+), 5 deletions(-) diff --git a/eventrecorder/eventrecorderpb/eventrecorder.pb.go b/eventrecorder/eventrecorderpb/eventrecorder.pb.go index fb50837979..f83b8cd0d2 100644 --- a/eventrecorder/eventrecorderpb/eventrecorder.pb.go +++ b/eventrecorder/eventrecorderpb/eventrecorder.pb.go @@ -1700,7 +1700,9 @@ type InhibitRule struct { TargetMatchers []*Matcher `protobuf:"bytes,2,rep,name=target_matchers,json=targetMatchers,proto3" json:"target_matchers,omitempty"` // Label names whose values must be equal between source and target // alerts for the inhibition to take effect. - EqualLabels []string `protobuf:"bytes,3,rep,name=equal_labels,json=equalLabels,proto3" json:"equal_labels,omitempty"` + EqualLabels []string `protobuf:"bytes,3,rep,name=equal_labels,json=equalLabels,proto3" json:"equal_labels,omitempty"` + // Name is the optional name of the inhibition rule. + Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -1756,6 +1758,13 @@ func (x *InhibitRule) GetEqualLabels() []string { return nil } +func (x *InhibitRule) GetName() string { + if x != nil { + return x.Name + } + return "" +} + // InhibitionMutedAlertEvent is emitted when one or more inhibition // rules suppress an alert. type InhibitionMutedAlertEvent struct { @@ -1938,11 +1947,12 @@ const file_eventrecorder_proto_rawDesc = "" + "\x16SilenceMutedAlertEvent\x122\n" + "\asilence\x18\x01 \x01(\v2\x18.eventrecorderpb.SilenceR\asilence\x12<\n" + "\vmuted_alert\x18\x02 \x01(\v2\x1b.eventrecorderpb.MutedAlertR\n" + - "mutedAlert\"\xb6\x01\n" + + "mutedAlert\"\xca\x01\n" + "\vInhibitRule\x12A\n" + "\x0fsource_matchers\x18\x01 \x03(\v2\x18.eventrecorderpb.MatcherR\x0esourceMatchers\x12A\n" + "\x0ftarget_matchers\x18\x02 \x03(\v2\x18.eventrecorderpb.MatcherR\x0etargetMatchers\x12!\n" + - "\fequal_labels\x18\x03 \x03(\tR\vequalLabels\"\xd5\x01\n" + + "\fequal_labels\x18\x03 \x03(\tR\vequalLabels\x12\x12\n" + + "\x04name\x18\x04 \x01(\tR\x04name\"\xd5\x01\n" + "\x19InhibitionMutedAlertEvent\x12A\n" + "\rinhibit_rules\x18\x01 \x03(\v2\x1c.eventrecorderpb.InhibitRuleR\finhibitRules\x12<\n" + "\vmuted_alert\x18\x02 \x01(\v2\x1b.eventrecorderpb.MutedAlertR\n" + diff --git a/eventrecorder/eventrecorderpb/eventrecorder.proto b/eventrecorder/eventrecorderpb/eventrecorder.proto index 3004b5e55c..3de19298cd 100644 --- a/eventrecorder/eventrecorderpb/eventrecorder.proto +++ b/eventrecorder/eventrecorderpb/eventrecorder.proto @@ -373,6 +373,9 @@ message InhibitRule { // Label names whose values must be equal between source and target // alerts for the inhibition to take effect. repeated string equal_labels = 3; + + // Name is the optional name of the inhibition rule. + string name = 4; } // InhibitionMutedAlertEvent is emitted when one or more inhibition diff --git a/eventrecorder/events.go b/eventrecorder/events.go index 1eccba9eff..4b5e5d5891 100644 --- a/eventrecorder/events.go +++ b/eventrecorder/events.go @@ -174,13 +174,14 @@ func SilenceAsProto(sil *silencepb.Silence) *eventrecorderpb.Silence { // InhibitRuleAsProto converts inhibit rule fields to an // eventrecorderpb.InhibitRule. It accepts the individual fields rather // than the InhibitRule struct to avoid an import cycle. -func InhibitRuleAsProto(sourceMatchers, targetMatchers labels.Matchers, equal map[model.LabelName]struct{}) *eventrecorderpb.InhibitRule { +func InhibitRuleAsProto(name string, sourceMatchers, targetMatchers labels.Matchers, equal map[model.LabelName]struct{}) *eventrecorderpb.InhibitRule { equalLabels := make([]string, 0, len(equal)) for label := range equal { equalLabels = append(equalLabels, string(label)) } slices.Sort(equalLabels) return &eventrecorderpb.InhibitRule{ + Name: name, SourceMatchers: MatchersAsProto(sourceMatchers), TargetMatchers: MatchersAsProto(targetMatchers), EqualLabels: equalLabels, diff --git a/eventrecorder/events_test.go b/eventrecorder/events_test.go index 2be3df8a3f..01983f2fb9 100644 --- a/eventrecorder/events_test.go +++ b/eventrecorder/events_test.go @@ -101,3 +101,20 @@ func TestMatchersAsProto(t *testing.T) { require.Equal(t, eventrecorderpb.Matcher_TYPE_EQUAL, protos[0].Type) require.Equal(t, eventrecorderpb.Matcher_TYPE_NOT_EQUAL, protos[1].Type) } + +func TestInhibitRuleAsProto(t *testing.T) { + source, err := labels.NewMatcher(labels.MatchEqual, "severity", "critical") + require.NoError(t, err) + target, err := labels.NewMatcher(labels.MatchEqual, "severity", "warning") + require.NoError(t, err) + equal := map[model.LabelName]struct{}{"cluster": {}, "alertname": {}} + + proto := InhibitRuleAsProto("my-rule", labels.Matchers{source}, labels.Matchers{target}, equal) + + require.Equal(t, "my-rule", proto.Name) + require.Len(t, proto.SourceMatchers, 1) + require.Equal(t, "severity", proto.SourceMatchers[0].Name) + require.Len(t, proto.TargetMatchers, 1) + require.Equal(t, "severity", proto.TargetMatchers[0].Name) + require.Equal(t, []string{"alertname", "cluster"}, proto.EqualLabels) +} diff --git a/inhibit/inhibit.go b/inhibit/inhibit.go index 1489ae0dac..c441054be6 100644 --- a/inhibit/inhibit.go +++ b/inhibit/inhibit.go @@ -225,7 +225,7 @@ func (ih *Inhibitor) Mutes(ctx context.Context, lset model.LabelSet) bool { ih.recorder.RecordEvent(ctx, func() *eventrecorderpb.EventData { return eventrecorder.NewInhibitionMutedAlertEvent( - []*eventrecorderpb.InhibitRule{eventrecorder.InhibitRuleAsProto(r.SourceMatchers, r.TargetMatchers, r.Equal)}, + []*eventrecorderpb.InhibitRule{eventrecorder.InhibitRuleAsProto(r.Name, r.SourceMatchers, r.TargetMatchers, r.Equal)}, fp, lset, []model.Fingerprint{inhibitedByFP}, ) From fa0daa57f5c0fe17542a4ff24dc5a6e17d924e5f Mon Sep 17 00:00:00 2001 From: Siavash Safi Date: Fri, 19 Jun 2026 12:56:13 +0200 Subject: [PATCH 029/120] build(ui): bump elm to 0.19.1-6 to fix install on Apple Silicon (#5316) The pinned elm@0.19.1 npm package uses an old install script that has no arm64 mapping, so on Apple Silicon it builds a download URL for "binary-for-mac-undefined.gz", receives a 404 HTML page from GitHub, and fails with "incorrect header check" while decompressing. Bump elm to 0.19.1-6, which ships prebuilt compilers as per-platform optional dependencies (@elm_binaries/darwin_arm64, darwin_x64, linux_x64, win32_x64) instead of downloading at install time. This drops the deprecated request-based dependency chain and lets `npm ci` (and the ui-elm build) succeed natively on arm64 Macs. Signed-off-by: Siavash Safi --- ui/app/package-lock.json | 580 +++++---------------------------------- ui/app/package.json | 2 +- 2 files changed, 67 insertions(+), 515 deletions(-) diff --git a/ui/app/package-lock.json b/ui/app/package-lock.json index c5c01756ae..6d95f3ace0 100644 --- a/ui/app/package-lock.json +++ b/ui/app/package-lock.json @@ -12,7 +12,7 @@ "font-awesome": "^4.7.0" }, "devDependencies": { - "elm": "0.19.1", + "elm": "0.19.1-6", "elm-format": "0.8.7", "elm-review": "2.5.0", "elm-test": "0.19.1-revision6", @@ -91,6 +91,62 @@ "win32" ] }, + "node_modules/@elm_binaries/darwin_arm64": { + "version": "0.19.1-0", + "resolved": "https://registry.npmjs.org/@elm_binaries/darwin_arm64/-/darwin_arm64-0.19.1-0.tgz", + "integrity": "sha512-mjbsH7BNHEAmoE2SCJFcfk5fIHwFIpxtSgnEAqMsVLpBUFoEtAeX+LQ+N0vSFJB3WAh73+QYx/xSluxxLcL6dA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@elm_binaries/darwin_x64": { + "version": "0.19.1-0", + "resolved": "https://registry.npmjs.org/@elm_binaries/darwin_x64/-/darwin_x64-0.19.1-0.tgz", + "integrity": "sha512-QGUtrZTPBzaxgi9al6nr+9313wrnUVHuijzUK39UsPS+pa+n6CmWyV/69sHZeX9qy6UfeugE0PzF3qcUiy2GDQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@elm_binaries/linux_x64": { + "version": "0.19.1-0", + "resolved": "https://registry.npmjs.org/@elm_binaries/linux_x64/-/linux_x64-0.19.1-0.tgz", + "integrity": "sha512-T1ZrWVhg2kKAsi8caOd3vp/1A3e21VuCpSG63x8rDie50fHbCytTway9B8WHEdnBFv4mYWiA68dzGxYCiFmU2w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@elm_binaries/win32_x64": { + "version": "0.19.1-0", + "resolved": "https://registry.npmjs.org/@elm_binaries/win32_x64/-/win32_x64-0.19.1-0.tgz", + "integrity": "sha512-yDleiXqSE9EcqKtd9SkC/4RIW8I71YsXzMPL79ub2bBPHjWTcoyyeBbYjoOB9SxSlArJ74HaoBApzT6hY7Zobg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@emnapi/core": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.0.tgz", @@ -580,23 +636,6 @@ "node": ">=0.4.0" } }, - "node_modules/ajv": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", - "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -653,33 +692,6 @@ "node": ">= 8" } }, - "node_modules/asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "safer-buffer": "~2.1.0" - } - }, - "node_modules/assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true, - "license": "MIT" - }, "node_modules/at-least-node": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", @@ -690,23 +702,6 @@ "node": ">= 4.0.0" } }, - "node_modules/aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "*" - } - }, - "node_modules/aws4": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz", - "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==", - "dev": true, - "license": "MIT" - }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -735,16 +730,6 @@ ], "license": "MIT" }, - "node_modules/bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "tweetnacl": "^0.14.3" - } - }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -873,13 +858,6 @@ "node": ">=8" } }, - "node_modules/caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", - "dev": true, - "license": "Apache-2.0" - }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -1001,19 +979,6 @@ "dev": true, "license": "MIT" }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/commander": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", @@ -1031,13 +996,6 @@ "dev": true, "license": "MIT" }, - "node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", - "dev": true, - "license": "MIT" - }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -1053,19 +1011,6 @@ "node": ">= 8" } }, - "node_modules/dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", - "dev": true, - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0" - }, - "engines": { - "node": ">=0.10" - } - }, "node_modules/debug": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", @@ -1120,16 +1065,6 @@ "node": ">=10" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -1147,33 +1082,24 @@ "dev": true, "license": "BSD-3-Clause" }, - "node_modules/ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", - "dev": true, - "license": "MIT", - "dependencies": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, "node_modules/elm": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/elm/-/elm-0.19.1.tgz", - "integrity": "sha512-rehOtJKZvoYDddlrd7AX5NAf0H+LUllnBg3AHaeaIOKWzw4W316d7Bkhlbo7aSG+hVUVWP2ihKwyYkDi589TfA==", - "deprecated": "package.json was changed upon upload, breaking things on Mac and Linux", + "version": "0.19.1-6", + "resolved": "https://registry.npmjs.org/elm/-/elm-0.19.1-6.tgz", + "integrity": "sha512-mKYyierHICPdMx/vhiIacdPmTPnh889gjHOZ75ZAoCxo3lZmSWbGP8HMw78wyctJH0HwvTmeKhlYSWboQNYPeQ==", "dev": true, "hasInstallScript": true, "license": "BSD-3-Clause", - "dependencies": { - "request": "^2.88.0" - }, "bin": { "elm": "bin/elm" }, "engines": { "node": ">=7.0.0" + }, + "optionalDependencies": { + "@elm_binaries/darwin_arm64": "0.19.1-0", + "@elm_binaries/darwin_x64": "0.19.1-0", + "@elm_binaries/linux_x64": "0.19.1-0", + "@elm_binaries/win32_x64": "0.19.1-0" } }, "node_modules/elm-esm": { @@ -1303,37 +1229,6 @@ "dev": true, "license": "MIT" }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true, - "license": "MIT" - }, - "node_modules/extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "license": "MIT" - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" - }, "node_modules/fast-levenshtein": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-3.0.0.tgz", @@ -1435,31 +1330,6 @@ "node": ">=0.10.3" } }, - "node_modules/forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "*" - } - }, - "node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 0.12" - } - }, "node_modules/fs-extra": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", @@ -1514,16 +1384,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", - "dev": true, - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0" - } - }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -1609,31 +1469,6 @@ "dev": true, "license": "ISC" }, - "node_modules/har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=4" - } - }, - "node_modules/har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "deprecated": "this library is no longer supported", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -1651,22 +1486,6 @@ "dev": true, "license": "BSD-2-Clause" }, - "node_modules/http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - }, - "engines": { - "node": ">=0.8", - "npm": ">=1.3.7" - } - }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -1773,13 +1592,6 @@ "node": ">=0.12.0" } }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "dev": true, - "license": "MIT" - }, "node_modules/is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", @@ -1800,13 +1612,6 @@ "dev": true, "license": "ISC" }, - "node_modules/isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", - "dev": true, - "license": "MIT" - }, "node_modules/jquery": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", @@ -1814,13 +1619,6 @@ "license": "MIT", "peer": true }, - "node_modules/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", - "dev": true, - "license": "MIT" - }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -1828,27 +1626,6 @@ "dev": true, "license": "MIT" }, - "node_modules/json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", - "dev": true, - "license": "(AFL-2.1 OR BSD-3-Clause)" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "dev": true, - "license": "ISC" - }, "node_modules/jsonfile": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", @@ -1862,22 +1639,6 @@ "graceful-fs": "^4.1.6" } }, - "node_modules/jsprim": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", - "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", - "dev": true, - "license": "MIT", - "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" - }, - "engines": { - "node": ">=0.6.0" - } - }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -2206,29 +1967,6 @@ "node": ">=8" } }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -2423,16 +2161,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "*" - } - }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -2601,13 +2329,6 @@ "node": ">=8" } }, - "node_modules/performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", - "dev": true, - "license": "MIT" - }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -2683,19 +2404,6 @@ "node": ">= 6" } }, - "node_modules/psl": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", - "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "punycode": "^2.3.1" - }, - "funding": { - "url": "https://github.com/sponsors/lupomontero" - } - }, "node_modules/pump": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", @@ -2707,26 +2415,6 @@ "once": "^1.3.1" } }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/qs": { - "version": "6.5.5", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.5.tgz", - "integrity": "sha512-mzR4sElr1bfCaPJe7m8ilJ6ZXdDaGoObcYR0ZHSsktM/Lt21MVHj5De30GQH2eiZ1qGRTO7LCAzQsUeXTNexWQ==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.6" - } - }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -2755,39 +2443,6 @@ "node": ">=8.10.0" } }, - "node_modules/request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/responselike": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", @@ -2887,13 +2542,6 @@ ], "license": "MIT" }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true, - "license": "MIT" - }, "node_modules/semver": { "version": "5.7.2", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", @@ -2964,32 +2612,6 @@ "node": "*" } }, - "node_modules/sshpk": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", - "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - }, - "bin": { - "sshpk-conv": "bin/sshpk-conv", - "sshpk-sign": "bin/sshpk-sign", - "sshpk-verify": "bin/sshpk-verify" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -3185,20 +2807,6 @@ "node": ">=8.0" } }, - "node_modules/tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=0.8" - } - }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -3207,26 +2815,6 @@ "license": "0BSD", "optional": true }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", - "dev": true, - "license": "Unlicense" - }, "node_modules/type-fest": { "version": "0.21.3", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", @@ -3270,16 +2858,6 @@ "node": ">= 10.0.0" } }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -3287,32 +2865,6 @@ "dev": true, "license": "MIT" }, - "node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", - "dev": true, - "license": "MIT", - "bin": { - "uuid": "bin/uuid" - } - }, - "node_modules/verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, "node_modules/vite": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.0.tgz", diff --git a/ui/app/package.json b/ui/app/package.json index edbcf5f681..6bb42cc1c9 100644 --- a/ui/app/package.json +++ b/ui/app/package.json @@ -6,7 +6,7 @@ "build": "vite build" }, "devDependencies": { - "elm": "0.19.1", + "elm": "0.19.1-6", "elm-format": "0.8.7", "elm-review": "2.5.0", "elm-test": "0.19.1-revision6", From 34fb323152e49e68432c6d1bcea3901b38518c11 Mon Sep 17 00:00:00 2001 From: trouaux Date: Fri, 12 Jun 2026 10:47:51 +0200 Subject: [PATCH 030/120] add AlertmanagerClusterFailedPeers alert Signed-off-by: trouaux --- doc/alertmanager-mixin/alerts.libsonnet | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/doc/alertmanager-mixin/alerts.libsonnet b/doc/alertmanager-mixin/alerts.libsonnet index c4ca621fcb..ad0c71f14e 100644 --- a/doc/alertmanager-mixin/alerts.libsonnet +++ b/doc/alertmanager-mixin/alerts.libsonnet @@ -162,6 +162,22 @@ description: '{{ $value | humanizePercentage }} of Alertmanager instances within the %(alertmanagerClusterName)s cluster have restarted at least 5 times in the last 10m.' % $._config, }, }, + { + alert: 'AlertmanagerClusterFailedPeers', + expr: ||| + # Without max_over_time, failed scrapes could create false negatives, see + # https://www.robustperception.io/alerting-on-gauges-in-prometheus-2-0 for details. + max_over_time(alertmanager_cluster_failed_peers{%(alertmanagerSelector)s}[5m]) > 0 + ||| % $._config, + 'for': '15m', + labels: { + severity: 'warning', + }, + annotations: { + summary: 'An Alertmanager instance has failed peers in the cluster.', + description: 'Alertmanager %(alertmanagerName)s has {{ $value }} failed peers in the %(alertmanagerClusterName)s cluster.' % $._config, + }, + }, ], }, ], From ce8cd596a31f17299d44272e31e6523ae2b2741f Mon Sep 17 00:00:00 2001 From: Solomon Jacobs Date: Sat, 20 Jun 2026 17:37:07 +0200 Subject: [PATCH 031/120] fix(ci): stale comments in release/publish workflows (#5319) The release.yml top-level comment claimed the GitHub release publish step "uses PROMBOT_GITHUB_TOKEN". That was no longer true, as of PR #5257. Signed-off-by: Solomon Jacobs --- .github/workflows/publish.yml | 3 +-- .github/workflows/release.yml | 6 ++---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 910dd8f8f9..34b5e771c0 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -4,7 +4,6 @@ on: # yamllint disable-line rule:truthy push: branches: - main -# Docker Hub + Quay pushes use their own secrets. permissions: contents: read @@ -29,7 +28,7 @@ jobs: name: Publish main branch artefacts runs-on: ubuntu-latest permissions: - packages: write + packages: write # push the image to GHCR via github.token needs: build steps: - uses: prometheus/promci/publish_main@d9d4f5688814f0b77bf003d07fb8c00507390634 # v0.8.2 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 34d3a92b37..cbeb5ddda6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,8 +4,6 @@ on: # yamllint disable-line rule:truthy push: tags: - v* -# Docker Hub + Quay pushes use their own secrets; GitHub release publish -# uses PROMBOT_GITHUB_TOKEN. permissions: contents: read @@ -37,8 +35,8 @@ jobs: name: Publish release artefacts runs-on: ubuntu-latest permissions: - contents: write - packages: write + contents: write # create the GitHub release + packages: write # push the image to GHCR via github.token needs: build steps: - uses: prometheus/promci/publish_release@d9d4f5688814f0b77bf003d07fb8c00507390634 # v0.8.2 From 657d43d4b08992d5c0ab45d85c6c831155eb0cdb Mon Sep 17 00:00:00 2001 From: Christoph Maser Date: Mon, 22 Jun 2026 09:53:08 +0200 Subject: [PATCH 032/120] refactor(opsgenie): move configuration types into opsgenie package (#5320) Signed-off-by: Christoph Maser --- config/common/url.go | 6 +- config/config.go | 5 +- config/config_test.go | 2 +- config/notifiers.go | 80 --------------------- config/notifiers_test.go | 100 -------------------------- notify/opsgenie/config.go | 104 +++++++++++++++++++++++++++ notify/opsgenie/config_test.go | 120 +++++++++++++++++++++++++++++++ notify/opsgenie/opsgenie.go | 5 +- notify/opsgenie/opsgenie_test.go | 25 ++++--- 9 files changed, 245 insertions(+), 202 deletions(-) create mode 100644 notify/opsgenie/config.go create mode 100644 notify/opsgenie/config_test.go diff --git a/config/common/url.go b/config/common/url.go index dc20913b8b..5a5fd10d27 100644 --- a/config/common/url.go +++ b/config/common/url.go @@ -152,8 +152,8 @@ func (s *SecretURL) UnmarshalJSON(data []byte) error { return nil } -// containsTemplating checks if the string contains template syntax. -func containsTemplating(s string) (bool, error) { +// ContainsTemplating checks if the string contains template syntax. +func ContainsTemplating(s string) (bool, error) { if !strings.Contains(s, "{{") { return false, nil } @@ -196,7 +196,7 @@ func (s *SecretTemplateURL) UnmarshalYAML(unmarshal func(any) error) error { } // Check if the URL contains template syntax - isTemplated, err := containsTemplating(urlStr) + isTemplated, err := ContainsTemplating(urlStr) if err != nil { return fmt.Errorf("invalid template syntax: %w", err) } diff --git a/config/config.go b/config/config.go index efcd15c464..f665b4394d 100644 --- a/config/config.go +++ b/config/config.go @@ -38,6 +38,7 @@ import ( "github.com/prometheus/alertmanager/notify/mattermost" "github.com/prometheus/alertmanager/notify/msteams" "github.com/prometheus/alertmanager/notify/msteamsv2" + "github.com/prometheus/alertmanager/notify/opsgenie" "github.com/prometheus/alertmanager/notify/webhook" "github.com/prometheus/alertmanager/timeinterval" "github.com/prometheus/alertmanager/tracing" @@ -477,7 +478,7 @@ func (c *Config) UnmarshalYAML(unmarshal func(any) error) error { } for _, ogc := range rcv.OpsGenieConfigs { if ogc == nil { - ogc = &OpsGenieConfig{} + ogc = &opsgenie.OpsGenieConfig{} } ogc.HTTPConfig = cmp.Or(ogc.HTTPConfig, c.Global.HTTPConfig) ogc.APIURL = cmp.Or(ogc.APIURL, c.Global.OpsGenieAPIURL) @@ -958,7 +959,7 @@ type Receiver struct { PagerdutyConfigs []*PagerdutyConfig `yaml:"pagerduty_configs,omitempty" json:"pagerduty_configs,omitempty"` SlackConfigs []*SlackConfig `yaml:"slack_configs,omitempty" json:"slack_configs,omitempty"` WebhookConfigs []*webhook.WebhookConfig `yaml:"webhook_configs,omitempty" json:"webhook_configs,omitempty"` - OpsGenieConfigs []*OpsGenieConfig `yaml:"opsgenie_configs,omitempty" json:"opsgenie_configs,omitempty"` + OpsGenieConfigs []*opsgenie.OpsGenieConfig `yaml:"opsgenie_configs,omitempty" json:"opsgenie_configs,omitempty"` WechatConfigs []*WechatConfig `yaml:"wechat_configs,omitempty" json:"wechat_configs,omitempty"` PushoverConfigs []*PushoverConfig `yaml:"pushover_configs,omitempty" json:"pushover_configs,omitempty"` VictorOpsConfigs []*VictorOpsConfig `yaml:"victorops_configs,omitempty" json:"victorops_configs,omitempty"` diff --git a/config/config_test.go b/config/config_test.go index 6250a64c5d..3bc045353b 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -1156,7 +1156,7 @@ func TestOpsGenieDeprecatedTeamSpecified(t *testing.T) { } const expectedErr = `yaml: unmarshal errors: - line 16: field teams not found in type config.plain` + line 16: field teams not found in type opsgenie.plain` if err.Error() != expectedErr { t.Errorf("Expected: %s\nGot: %s", expectedErr, err.Error()) } diff --git a/config/notifiers.go b/config/notifiers.go index ee981a3fb9..656e12d386 100644 --- a/config/notifiers.go +++ b/config/notifiers.go @@ -19,7 +19,6 @@ import ( "net/textproto" "regexp" "slices" - "strings" "time" commoncfg "github.com/prometheus/common/config" @@ -99,17 +98,6 @@ var ( TitleLink: `{{ template "rocketchat.default.titlelink" . }}`, } - // DefaultOpsGenieConfig defines default values for OpsGenie configurations. - DefaultOpsGenieConfig = OpsGenieConfig{ - NotifierConfig: amcommoncfg.NotifierConfig{ - VSendResolved: true, - }, - Message: `{{ template "opsgenie.default.message" . }}`, - Description: `{{ template "opsgenie.default.description" . }}`, - Source: `{{ template "opsgenie.default.source" . }}`, - // TODO: Add a details field with all the alerts. - } - // DefaultWechatConfig defines default values for wechat configurations. DefaultWechatConfig = WechatConfig{ NotifierConfig: amcommoncfg.NotifierConfig{ @@ -534,74 +522,6 @@ func (c *WechatConfig) UnmarshalYAML(unmarshal func(any) error) error { return nil } -// OpsGenieConfig configures notifications via OpsGenie. -type OpsGenieConfig struct { - amcommoncfg.NotifierConfig `yaml:",inline" json:",inline"` - - HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"` - - APIKey commoncfg.Secret `yaml:"api_key,omitempty" json:"api_key,omitempty"` - APIKeyFile string `yaml:"api_key_file,omitempty" json:"api_key_file,omitempty"` - APIURL *amcommoncfg.URL `yaml:"api_url,omitempty" json:"api_url,omitempty"` - Message string `yaml:"message,omitempty" json:"message,omitempty"` - Description string `yaml:"description,omitempty" json:"description,omitempty"` - Source string `yaml:"source,omitempty" json:"source,omitempty"` - Details map[string]string `yaml:"details,omitempty" json:"details,omitempty"` - Entity string `yaml:"entity,omitempty" json:"entity,omitempty"` - Responders []OpsGenieConfigResponder `yaml:"responders,omitempty" json:"responders,omitempty"` - Actions string `yaml:"actions,omitempty" json:"actions,omitempty"` - Tags string `yaml:"tags,omitempty" json:"tags,omitempty"` - Note string `yaml:"note,omitempty" json:"note,omitempty"` - Priority string `yaml:"priority,omitempty" json:"priority,omitempty"` - UpdateAlerts bool `yaml:"update_alerts,omitempty" json:"update_alerts,omitempty"` -} - -const opsgenieValidTypesRe = `^(team|teams|user|escalation|schedule)$` - -var opsgenieTypeMatcher = regexp.MustCompile(opsgenieValidTypesRe) - -// UnmarshalYAML implements the yaml.Unmarshaler interface. -func (c *OpsGenieConfig) UnmarshalYAML(unmarshal func(any) error) error { - *c = DefaultOpsGenieConfig - type plain OpsGenieConfig - if err := unmarshal((*plain)(c)); err != nil { - return err - } - - if c.APIKey != "" && len(c.APIKeyFile) > 0 { - return errors.New("at most one of api_key & api_key_file must be configured") - } - - for _, r := range c.Responders { - if r.ID == "" && r.Username == "" && r.Name == "" { - return fmt.Errorf("opsGenieConfig responder %v has to have at least one of id, username or name specified", r) - } - - isTemplated, err := containsTemplating(r.Type) - if err != nil { - return fmt.Errorf("opsGenieConfig responder %v type contains invalid template syntax: %w", r, err) - } - if !isTemplated { - r.Type = strings.ToLower(r.Type) - if !opsgenieTypeMatcher.MatchString(r.Type) { - return fmt.Errorf("opsGenieConfig responder %v type does not match valid options %s", r, opsgenieValidTypesRe) - } - } - } - - return nil -} - -type OpsGenieConfigResponder struct { - // One of those 3 should be filled. - ID string `yaml:"id,omitempty" json:"id,omitempty"` - Name string `yaml:"name,omitempty" json:"name,omitempty"` - Username string `yaml:"username,omitempty" json:"username,omitempty"` - - // team, user, escalation, schedule etc. - Type string `yaml:"type,omitempty" json:"type,omitempty"` -} - // VictorOpsConfig configures notifications via VictorOps. type VictorOpsConfig struct { amcommoncfg.NotifierConfig `yaml:",inline" json:",inline"` diff --git a/config/notifiers_test.go b/config/notifiers_test.go index 9b75b2ec99..e83c4fd371 100644 --- a/config/notifiers_test.go +++ b/config/notifiers_test.go @@ -761,106 +761,6 @@ actions: } } -func TestOpsgenieTypeMatcher(t *testing.T) { - good := []string{"team", "user", "escalation", "schedule"} - for _, g := range good { - if !opsgenieTypeMatcher.MatchString(g) { - t.Fatalf("failed to match with %s", g) - } - } - bad := []string{"0user", "team1", "2escalation3", "sche4dule", "User", "TEAM"} - for _, b := range bad { - if opsgenieTypeMatcher.MatchString(b) { - t.Errorf("mistakenly match with %s", b) - } - } -} - -func TestOpsGenieConfiguration(t *testing.T) { - for _, tc := range []struct { - name string - in string - - err bool - }{ - { - name: "valid configuration", - in: `api_key: xyz -responders: -- id: foo - type: scheDule -- name: bar - type: teams -- username: fred - type: USER -api_url: http://example.com -`, - }, - { - name: "api_key and api_key_file both defined", - in: `api_key: xyz -api_key_file: xyz -api_url: http://example.com -`, - err: true, - }, - { - name: "invalid responder type", - in: `api_key: xyz -responders: -- id: foo - type: wrong -api_url: http://example.com -`, - err: true, - }, - { - name: "missing responder field", - in: `api_key: xyz -responders: -- type: schedule -api_url: http://example.com -`, - err: true, - }, - { - name: "valid responder type template", - in: `api_key: xyz -responders: -- id: foo - type: "{{/* valid comment */}}team" -api_url: http://example.com -`, - }, - { - name: "invalid responder type template", - in: `api_key: xyz -responders: -- id: foo - type: "{{/* invalid comment }}team" -api_url: http://example.com -`, - err: true, - }, - } { - t.Run(tc.name, func(t *testing.T) { - var cfg OpsGenieConfig - - err := yaml.UnmarshalStrict([]byte(tc.in), &cfg) - if tc.err { - if err == nil { - t.Fatalf("expected error but got none") - } - return - } - - if err != nil { - t.Errorf("expected no error, got %v", err) - } - }) - } -} - func TestSNS(t *testing.T) { for _, tc := range []struct { in string diff --git a/notify/opsgenie/config.go b/notify/opsgenie/config.go new file mode 100644 index 0000000000..03fd315a0e --- /dev/null +++ b/notify/opsgenie/config.go @@ -0,0 +1,104 @@ +// Copyright The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package opsgenie + +import ( + "errors" + "fmt" + "regexp" + "strings" + + commoncfg "github.com/prometheus/common/config" + + amcommoncfg "github.com/prometheus/alertmanager/config/common" +) + +// DefaultOpsGenieConfig defines default values for OpsGenie configurations. +var DefaultOpsGenieConfig = OpsGenieConfig{ + NotifierConfig: amcommoncfg.NotifierConfig{ + VSendResolved: true, + }, + Message: `{{ template "opsgenie.default.message" . }}`, + Description: `{{ template "opsgenie.default.description" . }}`, + Source: `{{ template "opsgenie.default.source" . }}`, + // TODO: Add a details field with all the alerts. +} + +var opsgenieTypeMatcher = regexp.MustCompile(opsgenieValidTypesRe) + +// OpsGenieConfig configures notifications via OpsGenie. +type OpsGenieConfig struct { + amcommoncfg.NotifierConfig `yaml:",inline" json:",inline"` + + HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"` + + APIKey commoncfg.Secret `yaml:"api_key,omitempty" json:"api_key,omitempty"` + APIKeyFile string `yaml:"api_key_file,omitempty" json:"api_key_file,omitempty"` + APIURL *amcommoncfg.URL `yaml:"api_url,omitempty" json:"api_url,omitempty"` + Message string `yaml:"message,omitempty" json:"message,omitempty"` + Description string `yaml:"description,omitempty" json:"description,omitempty"` + Source string `yaml:"source,omitempty" json:"source,omitempty"` + Details map[string]string `yaml:"details,omitempty" json:"details,omitempty"` + Entity string `yaml:"entity,omitempty" json:"entity,omitempty"` + Responders []OpsGenieConfigResponder `yaml:"responders,omitempty" json:"responders,omitempty"` + Actions string `yaml:"actions,omitempty" json:"actions,omitempty"` + Tags string `yaml:"tags,omitempty" json:"tags,omitempty"` + Note string `yaml:"note,omitempty" json:"note,omitempty"` + Priority string `yaml:"priority,omitempty" json:"priority,omitempty"` + UpdateAlerts bool `yaml:"update_alerts,omitempty" json:"update_alerts,omitempty"` +} + +const opsgenieValidTypesRe = `^(team|teams|user|escalation|schedule)$` + +// UnmarshalYAML implements the yaml.Unmarshaler interface. +func (c *OpsGenieConfig) UnmarshalYAML(unmarshal func(any) error) error { + *c = DefaultOpsGenieConfig + type plain OpsGenieConfig + if err := unmarshal((*plain)(c)); err != nil { + return err + } + + if c.APIKey != "" && len(c.APIKeyFile) > 0 { + return errors.New("at most one of api_key & api_key_file must be configured") + } + + for _, r := range c.Responders { + if r.ID == "" && r.Username == "" && r.Name == "" { + return fmt.Errorf("opsGenieConfig responder %v has to have at least one of id, username or name specified", r) + } + + isTemplated, err := amcommoncfg.ContainsTemplating(r.Type) + if err != nil { + return fmt.Errorf("opsGenieConfig responder %v type contains invalid template syntax: %w", r, err) + } + if !isTemplated { + r.Type = strings.ToLower(r.Type) + if !opsgenieTypeMatcher.MatchString(r.Type) { + return fmt.Errorf("opsGenieConfig responder %v type does not match valid options %s", r, opsgenieValidTypesRe) + } + } + } + + return nil +} + +type OpsGenieConfigResponder struct { + // One of those 3 should be filled. + ID string `yaml:"id,omitempty" json:"id,omitempty"` + Name string `yaml:"name,omitempty" json:"name,omitempty"` + Username string `yaml:"username,omitempty" json:"username,omitempty"` + + // team, user, escalation, schedule etc. + Type string `yaml:"type,omitempty" json:"type,omitempty"` +} diff --git a/notify/opsgenie/config_test.go b/notify/opsgenie/config_test.go new file mode 100644 index 0000000000..87e0868766 --- /dev/null +++ b/notify/opsgenie/config_test.go @@ -0,0 +1,120 @@ +// Copyright The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package opsgenie + +import ( + "testing" + + "gopkg.in/yaml.v2" +) + +func TestOpsGenieConfiguration(t *testing.T) { + for _, tc := range []struct { + name string + in string + + err bool + }{ + { + name: "valid configuration", + in: `api_key: xyz +responders: +- id: foo + type: scheDule +- name: bar + type: teams +- username: fred + type: USER +api_url: http://example.com +`, + }, + { + name: "api_key and api_key_file both defined", + in: `api_key: xyz +api_key_file: xyz +api_url: http://example.com +`, + err: true, + }, + { + name: "invalid responder type", + in: `api_key: xyz +responders: +- id: foo + type: wrong +api_url: http://example.com +`, + err: true, + }, + { + name: "missing responder field", + in: `api_key: xyz +responders: +- type: schedule +api_url: http://example.com +`, + err: true, + }, + { + name: "valid responder type template", + in: `api_key: xyz +responders: +- id: foo + type: "{{/* valid comment */}}team" +api_url: http://example.com +`, + }, + { + name: "invalid responder type template", + in: `api_key: xyz +responders: +- id: foo + type: "{{/* invalid comment }}team" +api_url: http://example.com +`, + err: true, + }, + } { + t.Run(tc.name, func(t *testing.T) { + var cfg OpsGenieConfig + + err := yaml.UnmarshalStrict([]byte(tc.in), &cfg) + if tc.err { + if err == nil { + t.Fatalf("expected error but got none") + } + return + } + + if err != nil { + t.Errorf("expected no error, got %v", err) + } + }) + } +} + +func TestOpsgenieTypeMatcher(t *testing.T) { + good := []string{"team", "user", "escalation", "schedule"} + for _, g := range good { + if !opsgenieTypeMatcher.MatchString(g) { + t.Fatalf("failed to match with %s", g) + } + } + bad := []string{"0user", "team1", "2escalation3", "sche4dule", "User", "TEAM"} + for _, b := range bad { + if opsgenieTypeMatcher.MatchString(b) { + t.Errorf("mistakenly match with %s", b) + } + } +} diff --git a/notify/opsgenie/opsgenie.go b/notify/opsgenie/opsgenie.go index a267b45e70..96a84894e6 100644 --- a/notify/opsgenie/opsgenie.go +++ b/notify/opsgenie/opsgenie.go @@ -27,7 +27,6 @@ import ( commoncfg "github.com/prometheus/common/config" "github.com/prometheus/common/model" - "github.com/prometheus/alertmanager/config" "github.com/prometheus/alertmanager/notify" "github.com/prometheus/alertmanager/template" "github.com/prometheus/alertmanager/types" @@ -38,7 +37,7 @@ const maxMessageLenRunes = 130 // Notifier implements a Notifier for OpsGenie notifications. type Notifier struct { - conf *config.OpsGenieConfig + conf *OpsGenieConfig tmpl *template.Template logger *slog.Logger client *http.Client @@ -46,7 +45,7 @@ type Notifier struct { } // New returns a new OpsGenie notifier. -func New(c *config.OpsGenieConfig, t *template.Template, l *slog.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) { +func New(c *OpsGenieConfig, t *template.Template, l *slog.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) { client, err := notify.NewClientWithTracing(*c.HTTPConfig, "opsgenie", httpOpts...) if err != nil { return nil, err diff --git a/notify/opsgenie/opsgenie_test.go b/notify/opsgenie/opsgenie_test.go index ec525adb5f..e2c5f98ebd 100644 --- a/notify/opsgenie/opsgenie_test.go +++ b/notify/opsgenie/opsgenie_test.go @@ -30,7 +30,6 @@ import ( amcommoncfg "github.com/prometheus/alertmanager/config/common" - "github.com/prometheus/alertmanager/config" "github.com/prometheus/alertmanager/notify" "github.com/prometheus/alertmanager/notify/test" "github.com/prometheus/alertmanager/types" @@ -38,7 +37,7 @@ import ( func TestOpsGenieRetry(t *testing.T) { notifier, err := New( - &config.OpsGenieConfig{ + &OpsGenieConfig{ HTTPConfig: &commoncfg.HTTPClientConfig{}, }, test.CreateTmpl(t), @@ -59,7 +58,7 @@ func TestOpsGenieRedactedURL(t *testing.T) { key := "key" notifier, err := New( - &config.OpsGenieConfig{ + &OpsGenieConfig{ APIURL: &amcommoncfg.URL{URL: u}, APIKey: commoncfg.Secret(key), HTTPConfig: &commoncfg.HTTPClientConfig{}, @@ -84,7 +83,7 @@ func TestGettingOpsGegineApikeyFromFile(t *testing.T) { require.NoError(t, err, "writing to temp file failed") notifier, err := New( - &config.OpsGenieConfig{ + &OpsGenieConfig{ APIURL: &amcommoncfg.URL{URL: u}, APIKeyFile: f.Name(), HTTPConfig: &commoncfg.HTTPClientConfig{}, @@ -107,21 +106,21 @@ func TestOpsGenie(t *testing.T) { for _, tc := range []struct { title string - cfg *config.OpsGenieConfig + cfg *OpsGenieConfig expectedEmptyAlertBody string expectedBody string }{ { title: "config without details", - cfg: &config.OpsGenieConfig{ + cfg: &OpsGenieConfig{ NotifierConfig: amcommoncfg.NotifierConfig{ VSendResolved: true, }, Message: `{{ .CommonLabels.Message }}`, Description: `{{ .CommonLabels.Description }}`, Source: `{{ .CommonLabels.Source }}`, - Responders: []config.OpsGenieConfigResponder{ + Responders: []OpsGenieConfigResponder{ { Name: `{{ .CommonLabels.ResponderName1 }}`, Type: `{{ .CommonLabels.ResponderType1 }}`, @@ -147,7 +146,7 @@ func TestOpsGenie(t *testing.T) { }, { title: "config with details", - cfg: &config.OpsGenieConfig{ + cfg: &OpsGenieConfig{ NotifierConfig: amcommoncfg.NotifierConfig{ VSendResolved: true, }, @@ -157,7 +156,7 @@ func TestOpsGenie(t *testing.T) { Details: map[string]string{ "Description": `adjusted {{ .CommonLabels.Description }}`, }, - Responders: []config.OpsGenieConfigResponder{ + Responders: []OpsGenieConfigResponder{ { Name: `{{ .CommonLabels.ResponderName1 }}`, Type: `{{ .CommonLabels.ResponderType1 }}`, @@ -183,7 +182,7 @@ func TestOpsGenie(t *testing.T) { }, { title: "config with multiple teams", - cfg: &config.OpsGenieConfig{ + cfg: &OpsGenieConfig{ NotifierConfig: amcommoncfg.NotifierConfig{ VSendResolved: true, }, @@ -193,7 +192,7 @@ func TestOpsGenie(t *testing.T) { Details: map[string]string{ "Description": `adjusted {{ .CommonLabels.Description }}`, }, - Responders: []config.OpsGenieConfigResponder{ + Responders: []OpsGenieConfigResponder{ { Name: `{{ .CommonLabels.ResponderName3 }}`, Type: `{{ .CommonLabels.ResponderType3 }}`, @@ -281,7 +280,7 @@ func TestOpsGenieWithUpdate(t *testing.T) { tmpl := test.CreateTmpl(t) ctx := context.Background() ctx = notify.WithGroupKey(ctx, "1") - opsGenieConfigWithUpdate := config.OpsGenieConfig{ + opsGenieConfigWithUpdate := OpsGenieConfig{ Message: `{{ .CommonLabels.Message }}`, Description: `{{ .CommonLabels.Description }}`, UpdateAlerts: true, @@ -327,7 +326,7 @@ func TestOpsGenieApiKeyFile(t *testing.T) { tmpl := test.CreateTmpl(t) ctx := context.Background() ctx = notify.WithGroupKey(ctx, "1") - opsGenieConfigWithUpdate := config.OpsGenieConfig{ + opsGenieConfigWithUpdate := OpsGenieConfig{ APIKeyFile: `./api_key_file`, APIURL: &amcommoncfg.URL{URL: u}, HTTPConfig: &commoncfg.HTTPClientConfig{}, From b853bc032273c879d9bfdb3a5340729973aa6af4 Mon Sep 17 00:00:00 2001 From: PrometheusBot Date: Tue, 23 Jun 2026 08:13:20 +0200 Subject: [PATCH 033/120] Update common Prometheus files (#5321) Signed-off-by: prombot --- .github/workflows/govulncheck.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/govulncheck.yml b/.github/workflows/govulncheck.yml index adc84d6c5a..621476dec4 100644 --- a/.github/workflows/govulncheck.yml +++ b/.github/workflows/govulncheck.yml @@ -20,5 +20,11 @@ jobs: runs-on: ubuntu-latest name: Run govulncheck steps: + - name: Install snmp_exporter/generator dependencies + id: snmp-deps + run: sudo apt-get update && sudo apt-get -y install libsnmp-dev + if: github.repository == 'prometheus/snmp_exporter' - id: govulncheck - uses: golang/govulncheck-action@b625fbe08f3bccbe446d94fbf87fcc875a4f50ee # v1.0.4 + uses: golang/govulncheck-action@3fa7bd9cee2cfdf3499a8803b226e43de7b7cdb4 # master + env: + GOOS: ${{ contains(github.repository, 'windows_exporter') && 'windows' || '' }} From d4284cfa85d7a239e51f07b816914c5d9c34659b Mon Sep 17 00:00:00 2001 From: PrometheusBot Date: Wed, 24 Jun 2026 06:15:04 +0200 Subject: [PATCH 034/120] Update common Prometheus files (#5323) Signed-off-by: prombot --- Makefile.common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.common b/Makefile.common index ef05881be8..a7c5f553e1 100644 --- a/Makefile.common +++ b/Makefile.common @@ -55,7 +55,7 @@ ifneq ($(shell command -v gotestsum 2> /dev/null),) endif endif -PROMU_VERSION ?= 0.18.1 +PROMU_VERSION ?= 0.20.0 PROMU_URL := https://github.com/prometheus/promu/releases/download/v$(PROMU_VERSION)/promu-$(PROMU_VERSION).$(GO_BUILD_PLATFORM).tar.gz SKIP_GOLANGCI_LINT := From 7e54eebd16028a66263077ff4cee297b51882996 Mon Sep 17 00:00:00 2001 From: Ethan Hunter Date: Thu, 25 Jun 2026 11:21:44 -0600 Subject: [PATCH 035/120] add notification_reason to docs (#5329) Signed-off-by: Ethan Hunter --- docs/configuration.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/configuration.md b/docs/configuration.md index e4e001b38d..907398326f 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1947,6 +1947,7 @@ endpoint: "commonLabels": , "commonAnnotations": , "externalURL": , // backlink to the Alertmanager. + "notification_reason": , // string represent the reason this notification was generated "alerts": [ { "status": "", From 058616dda9cb06f6995d877dc1b9c3c24329ae53 Mon Sep 17 00:00:00 2001 From: Ethan Hunter Date: Fri, 26 Jun 2026 08:41:29 -0600 Subject: [PATCH 036/120] bugfix: ensure legacy matchers field is populated in snapshots (#5330) Signed-off-by: Ethan Hunter --- silence/silence.go | 8 ++++++-- silence/state_test.go | 48 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/silence/silence.go b/silence/silence.go index 7fdb852cf4..f1f185de7c 100644 --- a/silence/silence.go +++ b/silence/silence.go @@ -1366,9 +1366,11 @@ func (s state) MarshalBinary() ([]byte, error) { var buf bytes.Buffer for _, e := range s { - if _, err := protodelim.MarshalTo(&buf, e); err != nil { + b, err := marshalMeshSilence(e) + if err != nil { return nil, err } + buf.Write(b) } return buf.Bytes(), nil } @@ -1399,7 +1401,9 @@ func decodeState(r io.Reader) (state, error) { // the first matcher set to the matchers field for backward compatibility with // older alertmanager versions. func prepareSilenceForMarshalling(sil *pb.Silence) { - if len(sil.MatcherSets) > 0 { + // The nil check is here because of rare cases where this function + // is called on a nil silence. It's up to the caller to decide if it's a bug + if sil != nil && len(sil.MatcherSets) > 0 { sil.Matchers = sil.MatcherSets[0].Matchers } } diff --git a/silence/state_test.go b/silence/state_test.go index e194336a8e..42eee2dbef 100644 --- a/silence/state_test.go +++ b/silence/state_test.go @@ -13,10 +13,17 @@ package silence import ( + "bufio" + "bytes" "testing" "time" "github.com/stretchr/testify/require" + "google.golang.org/protobuf/encoding/protodelim" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/timestamppb" + + pb "github.com/prometheus/alertmanager/silence/silencepb" ) func TestCurrentState(t *testing.T) { @@ -37,3 +44,44 @@ func TestCurrentState(t *testing.T) { expected = CurrentState(pastStartTime, pastEndTime) require.Equal(t, SilenceStateExpired, expected) } + +// TestStateMarshalBinaryPopulatesLegacyMatchers asserts that snapshots and +// cluster local-state payloads written by state.MarshalBinary include the +// deprecated Silence.Matchers field, so that older Alertmanagers that don't +// understand MatcherSets still see the silence's first matcher set. +func TestStateMarshalBinaryPopulatesLegacyMatchers(t *testing.T) { + now := time.Now() + matchers := []*pb.Matcher{ + {Name: "alertname", Pattern: "Foo", Type: pb.Matcher_EQUAL}, + {Name: "severity", Pattern: "warn|crit", Type: pb.Matcher_REGEXP}, + } + sil := &pb.Silence{ + Id: "abc", + MatcherSets: []*pb.MatcherSet{{Matchers: matchers}}, + StartsAt: timestamppb.New(now), + EndsAt: timestamppb.New(now.Add(time.Hour)), + } + st := state{sil.Id: &pb.MeshSilence{ + Silence: sil, + ExpiresAt: timestamppb.New(now.Add(2 * time.Hour)), + }} + + b, err := st.MarshalBinary() + require.NoError(t, err) + + require.Nil(t, sil.Matchers, "MarshalBinary must not mutate in-memory silences") + + // Decode directly via protodelim, bypassing decodeState — decodeState + // strips the legacy field, but we want to observe the on-the-wire shape. + var got pb.MeshSilence + require.NoError(t, protodelim.UnmarshalFrom(bufio.NewReader(bytes.NewReader(b)), &got)) + + require.Len(t, got.Silence.MatcherSets, 1) + require.Len(t, got.Silence.Matchers, len(matchers)) + for i, m := range matchers { + require.True(t, proto.Equal(m, got.Silence.Matchers[i]), + "legacy Matchers[%d] mismatch", i) + require.True(t, proto.Equal(m, got.Silence.MatcherSets[0].Matchers[i]), + "MatcherSets[0].Matchers[%d] mismatch", i) + } +} From 3aef64d62612de7528b0280e295e133797164eae Mon Sep 17 00:00:00 2001 From: Siavash Safi Date: Mon, 29 Jun 2026 11:39:48 +0200 Subject: [PATCH 037/120] fix(api): return empty array instead of null for silence matchers GettableSilenceFromProto left the Matchers field as a nil slice when a silence had no matcher sets, only ever populating it via append. Go's encoding/json marshals a nil slice as null, so such silences were serialized as "matchers": null. The OpenAPI schema declares matchers as a required array, so an empty silence should serialize as [] instead. Initialize Matchers to an empty slice so the field is always a valid JSON array, keeping the v2 API schema-compliant for matcher-less silences. Related to #5326 Signed-off-by: Siavash Safi --- api/v2/compat.go | 1 + 1 file changed, 1 insertion(+) diff --git a/api/v2/compat.go b/api/v2/compat.go index cb5560af05..5db1a9cad1 100644 --- a/api/v2/compat.go +++ b/api/v2/compat.go @@ -38,6 +38,7 @@ func GettableSilenceFromProto(s *silencepb.Silence) (open_api_models.GettableSil Silence: open_api_models.Silence{ StartsAt: &start, EndsAt: &end, + Matchers: open_api_models.Matchers{}, Comment: &s.Comment, CreatedBy: &s.CreatedBy, Annotations: s.Annotations, From 4224f10420906544c9e32b82755f808ef6cb16b7 Mon Sep 17 00:00:00 2001 From: Guido Trotter Date: Wed, 24 Jun 2026 08:49:02 -0400 Subject: [PATCH 038/120] dispatch,template: add route labels (config, inheritance, templates) Add support for defining labels on routes in the routing configuration. Route labels are merged from parent to child routes, allowing label inheritance and overrides at each level of the routing tree. This enables: - Injecting labels into the notification context even when the label is not used for grouping - Using the same dedup key across alert groups with different group_by label sets The Labels field is added to config.Route, propagated through dispatch.RouteOpts (merged into a fresh LabelSet so parents are never mutated), and passed to the notification context via notify.WithRouteLabels. Templates can access route labels through the new routeLabels template function, which renders each label value as a template (so values can reference group labels, other route labels, etc., including recursively). Signed-off-by: Guido Trotter --- config/config.go | 2 + dispatch/dispatch.go | 1 + dispatch/route.go | 14 +++++ dispatch/route_test.go | 117 ++++++++++++++++++++++++++++++++++++++ docs/configuration.md | 13 +++++ notify/context.go | 13 +++++ notify/jira/jira_test.go | 2 +- notify/util.go | 6 +- template/template.go | 73 +++++++++++++++++++++--- template/template_test.go | 59 +++++++++++++++++-- 10 files changed, 284 insertions(+), 16 deletions(-) diff --git a/config/config.go b/config/config.go index f665b4394d..d49680acb6 100644 --- a/config/config.go +++ b/config/config.go @@ -892,6 +892,8 @@ type Route struct { GroupWait *model.Duration `yaml:"group_wait,omitempty" json:"group_wait,omitempty"` GroupInterval *model.Duration `yaml:"group_interval,omitempty" json:"group_interval,omitempty"` RepeatInterval *model.Duration `yaml:"repeat_interval,omitempty" json:"repeat_interval,omitempty"` + + Labels model.LabelSet `yaml:"labels,omitempty" json:"labels,omitempty"` } // UnmarshalYAML implements the yaml.Unmarshaler interface for Route. diff --git a/dispatch/dispatch.go b/dispatch/dispatch.go index 8be6a237cc..3b70662fe8 100644 --- a/dispatch/dispatch.go +++ b/dispatch/dispatch.go @@ -694,6 +694,7 @@ func (ag *aggrGroup) run(nf notifyFunc) { // Populate context with information needed along the pipeline. ctx = notify.WithGroupKey(ctx, ag.GroupKey()) ctx = notify.WithGroupLabels(ctx, ag.labels) + ctx = notify.WithRouteLabels(ctx, ag.opts.Labels) ctx = notify.WithReceiverName(ctx, ag.opts.Receiver) ctx = notify.WithRepeatInterval(ctx, ag.opts.RepeatInterval) ctx = notify.WithMuteTimeIntervals(ctx, ag.opts.MuteTimeIntervals) diff --git a/dispatch/route.go b/dispatch/route.go index 38b49524ca..6dcc9750e3 100644 --- a/dispatch/route.go +++ b/dispatch/route.go @@ -16,6 +16,7 @@ package dispatch import ( "encoding/json" "fmt" + "maps" "sort" "strconv" "strings" @@ -36,6 +37,7 @@ var DefaultRouteOpts = RouteOpts{ GroupBy: map[model.LabelName]struct{}{}, GroupByAll: false, MuteTimeIntervals: []string{}, + Labels: model.LabelSet{}, } // A Route is a node that contains definitions of how to handle alerts. @@ -72,6 +74,15 @@ func newRoute(cr *config.Route, parent *Route, counter *int) *Route { opts = parent.RouteOpts } + // Merge parent route labels and cr.Labels into opts.Labels. Always merge + // into a fresh LabelSet so we never mutate the parent's map. + if len(cr.Labels) != 0 { + merged := model.LabelSet{} + maps.Copy(merged, opts.Labels) + maps.Copy(merged, cr.Labels) + opts.Labels = merged + } + if cr.Receiver != "" { opts.Receiver = cr.Receiver } @@ -249,6 +260,9 @@ type RouteOpts struct { // A list of time intervals for which the route is active. ActiveTimeIntervals []string + + // Merged labels from this route and all of its parent routes. + Labels model.LabelSet } func (ro *RouteOpts) String() string { diff --git a/dispatch/route_test.go b/dispatch/route_test.go index 2672551eb4..dfd227e027 100644 --- a/dispatch/route_test.go +++ b/dispatch/route_test.go @@ -114,6 +114,7 @@ routes: GroupWait: def.GroupWait, GroupInterval: def.GroupInterval, RepeatInterval: def.RepeatInterval, + Labels: def.Labels, }, }, keys: []string{"{}/{owner=\"team-A\"}"}, @@ -131,6 +132,7 @@ routes: GroupWait: def.GroupWait, GroupInterval: def.GroupInterval, RepeatInterval: def.RepeatInterval, + Labels: def.Labels, }, }, keys: []string{"{}/{owner=\"team-A\"}"}, @@ -147,6 +149,7 @@ routes: GroupWait: 2 * time.Minute, GroupInterval: def.GroupInterval, RepeatInterval: def.RepeatInterval, + Labels: def.Labels, }, }, keys: []string{"{}/{owner=~\"^(?:team-(B|C))$\"}"}, @@ -164,6 +167,7 @@ routes: GroupWait: def.GroupWait, GroupInterval: def.GroupInterval, RepeatInterval: def.RepeatInterval, + Labels: def.Labels, }, }, keys: []string{"{}/{owner=\"team-A\"}/{env=\"testing\"}"}, @@ -181,6 +185,7 @@ routes: GroupWait: 1 * time.Minute, GroupInterval: def.GroupInterval, RepeatInterval: def.RepeatInterval, + Labels: def.Labels, }, { Receiver: "notify-productionB", @@ -189,6 +194,7 @@ routes: GroupWait: 30 * time.Second, GroupInterval: 5 * time.Minute, RepeatInterval: 1 * time.Hour, + Labels: def.Labels, }, }, keys: []string{ @@ -208,6 +214,7 @@ routes: GroupWait: def.GroupWait, GroupInterval: def.GroupInterval, RepeatInterval: def.RepeatInterval, + Labels: def.Labels, }, }, keys: []string{"{}/{group_by=\"role\"}"}, @@ -225,6 +232,7 @@ routes: GroupWait: def.GroupWait, GroupInterval: def.GroupInterval, RepeatInterval: def.RepeatInterval, + Labels: def.Labels, }, }, keys: []string{"{}/{group_by=\"role\"}/{env=\"testing\"}"}, @@ -243,6 +251,7 @@ routes: GroupWait: 2 * time.Minute, GroupInterval: def.GroupInterval, RepeatInterval: def.RepeatInterval, + Labels: def.Labels, }, }, keys: []string{"{}/{group_by=\"role\"}/{env=\"testing\"}/{wait=\"long\"}"}, @@ -464,6 +473,7 @@ routes: GroupWait: def.GroupWait, GroupInterval: def.GroupInterval, RepeatInterval: def.RepeatInterval, + Labels: def.Labels, }, }, keys: []string{"{}/{level!=\"critical\",owner=\"team-A\"}"}, @@ -481,6 +491,7 @@ routes: GroupWait: def.GroupWait, GroupInterval: def.GroupInterval, RepeatInterval: def.RepeatInterval, + Labels: def.Labels, }, }, keys: []string{"{}/{level!=\"critical\",owner=\"team-A\"}"}, @@ -497,6 +508,7 @@ routes: GroupWait: 2 * time.Minute, GroupInterval: def.GroupInterval, RepeatInterval: def.RepeatInterval, + Labels: def.Labels, }, }, keys: []string{"{}/{owner=~\"team-(B|C)\"}"}, @@ -514,6 +526,7 @@ routes: GroupWait: def.GroupWait, GroupInterval: def.GroupInterval, RepeatInterval: def.RepeatInterval, + Labels: def.Labels, }, }, keys: []string{"{}/{level!=\"critical\",owner=\"team-A\"}/{baz!~\".*quux\",env=\"testing\"}"}, @@ -531,6 +544,7 @@ routes: GroupWait: 1 * time.Minute, GroupInterval: def.GroupInterval, RepeatInterval: def.RepeatInterval, + Labels: def.Labels, }, { Receiver: "notify-productionB", @@ -539,6 +553,7 @@ routes: GroupWait: 30 * time.Second, GroupInterval: 5 * time.Minute, RepeatInterval: 1 * time.Hour, + Labels: def.Labels, }, }, keys: []string{ @@ -558,6 +573,7 @@ routes: GroupWait: def.GroupWait, GroupInterval: def.GroupInterval, RepeatInterval: def.RepeatInterval, + Labels: def.Labels, }, }, keys: []string{"{}/{group_by=\"role\"}"}, @@ -575,6 +591,7 @@ routes: GroupWait: def.GroupWait, GroupInterval: def.GroupInterval, RepeatInterval: def.RepeatInterval, + Labels: def.Labels, }, }, keys: []string{"{}/{group_by=\"role\"}/{env=\"testing\"}"}, @@ -593,6 +610,7 @@ routes: GroupWait: 2 * time.Minute, GroupInterval: def.GroupInterval, RepeatInterval: def.RepeatInterval, + Labels: def.Labels, }, }, keys: []string{"{}/{group_by=\"role\"}/{env=\"testing\"}/{wait=\"long\"}"}, @@ -700,6 +718,7 @@ routes: GroupWait: def.GroupWait, GroupInterval: def.GroupInterval, RepeatInterval: def.RepeatInterval, + Labels: def.Labels, }, }, keys: []string{"{}/{level!=\"critical\",owner=\"team-A\"}"}, @@ -717,6 +736,7 @@ routes: GroupWait: def.GroupWait, GroupInterval: def.GroupInterval, RepeatInterval: def.RepeatInterval, + Labels: def.Labels, }, }, keys: []string{"{}/{level!=\"critical\",owner=\"team-A\"}"}, @@ -733,6 +753,7 @@ routes: GroupWait: 2 * time.Minute, GroupInterval: def.GroupInterval, RepeatInterval: def.RepeatInterval, + Labels: def.Labels, }, }, keys: []string{"{}/{owner=~\"^(?:team-(B|C))$\"}"}, @@ -750,6 +771,7 @@ routes: GroupWait: def.GroupWait, GroupInterval: def.GroupInterval, RepeatInterval: def.RepeatInterval, + Labels: def.Labels, }, }, keys: []string{"{}/{level!=\"critical\",owner=\"team-A\"}/{baz!~\".*quux\",env=\"testing\"}"}, @@ -767,6 +789,7 @@ routes: GroupWait: 1 * time.Minute, GroupInterval: def.GroupInterval, RepeatInterval: def.RepeatInterval, + Labels: def.Labels, }, { Receiver: "notify-productionB", @@ -775,6 +798,7 @@ routes: GroupWait: 30 * time.Second, GroupInterval: 5 * time.Minute, RepeatInterval: 1 * time.Hour, + Labels: def.Labels, }, }, keys: []string{ @@ -794,6 +818,7 @@ routes: GroupWait: def.GroupWait, GroupInterval: def.GroupInterval, RepeatInterval: def.RepeatInterval, + Labels: def.Labels, }, }, keys: []string{"{}/{group_by=\"role\"}"}, @@ -811,6 +836,7 @@ routes: GroupWait: def.GroupWait, GroupInterval: def.GroupInterval, RepeatInterval: def.RepeatInterval, + Labels: def.Labels, }, }, keys: []string{"{}/{group_by=\"role\"}/{env=\"testing\"}"}, @@ -829,6 +855,7 @@ routes: GroupWait: 2 * time.Minute, GroupInterval: def.GroupInterval, RepeatInterval: def.RepeatInterval, + Labels: def.Labels, }, }, keys: []string{"{}/{group_by=\"role\"}/{env=\"testing\"}/{wait=\"long\"}"}, @@ -854,6 +881,96 @@ routes: } } +func TestRouteLabelsLoading(t *testing.T) { + in := ` +receiver: "notify-def" + +routes: + - matchers: ['{owner="team-A"}', '{level!="critical"}'] + labels: + team: "team-A" + receiver: "notify-A" + + routes: + - matchers: ['{env="testing"}', '{baz!~".*quux"}'] + labels: + team: "team-A-testing" + + - matchers: ['{env="production"}'] + labels: + severity: "production" + - matchers: ['{owner="team-B"}'] + receiver: "notify-B" +` + + var ctree config.Route + if err := yaml.UnmarshalStrict([]byte(in), &ctree); err != nil { + t.Logf("original yaml:\n%s", in) + t.Fatal(err) + } + tree := NewRoute(&ctree, nil) + + tests := []struct { + input model.LabelSet + expectedRouteLabels model.LabelSet + }{ + { + input: model.LabelSet{ + "owner": "team-A", + }, + expectedRouteLabels: model.LabelSet{ + "team": "team-A", + }, + }, + { + input: model.LabelSet{ + "owner": "team-A", + "env": "testing", + }, + expectedRouteLabels: model.LabelSet{ + "team": "team-A-testing", + }, + }, + { + input: model.LabelSet{ + "owner": "team-A", + "env": "production", + }, + expectedRouteLabels: model.LabelSet{ + "team": "team-A", + "severity": "production", + }, + }, + { + input: model.LabelSet{ + "owner": "team-B", + "env": "production", + }, + expectedRouteLabels: model.LabelSet{}, + }, + } + + for _, test := range tests { + var matches []*RouteOpts + + for _, r := range tree.Match(test.input) { + matches = append(matches, &r.RouteOpts) + } + + // This is just to simplify the tests: we construct the tests with only + // one route matching on purpose. In general we can have multiple matches + // and multiple route labels, and that is tested in previous unit tests. + if len(matches) != 1 { + t.Errorf("expected one route, got %d", len(matches)) + continue + } + + if !reflect.DeepEqual(matches[0].Labels, test.expectedRouteLabels) { + t.Errorf("\nexpected:\n%v\ngot:\n%v", test.expectedRouteLabels, matches[0].Labels) + } + } +} + func TestRouteID(t *testing.T) { in := ` receiver: default diff --git a/docs/configuration.md b/docs/configuration.md index 907398326f..a1af3a9ccd 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -230,6 +230,19 @@ match_re: matchers: [ - ... ] +# A set of labels that are attached to the route and made available to +# notification templates via the `routeLabels` template function (and to the +# `routeLabels` field of the `/api/v2/alerts/groups` API response). Route labels +# are merged from parent to child routes: a child route inherits its parent's +# route labels and may override individual labels by redefining them. Unlike +# group_by, route labels do not affect how alerts are grouped. +# +# Label values may themselves be templates that are rendered against the +# notification data of each alert group (so they can reference group labels, +# other route labels, etc.). +labels: + [ : , ... ] + # How long to wait before sending the first notification for a new group of # alerts. Allows to wait for alerts to arrive from other rule groups or # Prometheus servers, and for one or more inhibiting alerts to arrive and mute diff --git a/notify/context.go b/notify/context.go index 6b7c3eeabc..2d67fbee97 100644 --- a/notify/context.go +++ b/notify/context.go @@ -43,6 +43,7 @@ const ( keyAggrGroupID keyFlushID keyGroupMatchers + keyRouteLabels ) // WithReceiverName populates a context with a receiver name. @@ -70,6 +71,11 @@ func WithGroupLabels(ctx context.Context, lset model.LabelSet) context.Context { return context.WithValue(ctx, keyGroupLabels, lset) } +// WithRouteLabels populates a context with route labels. +func WithRouteLabels(ctx context.Context, rl model.LabelSet) context.Context { + return context.WithValue(ctx, keyRouteLabels, rl) +} + // WithNow populates a context with a now timestamp. func WithNow(ctx context.Context, t time.Time) context.Context { return context.WithValue(ctx, keyNow, t) @@ -128,6 +134,13 @@ func GroupLabels(ctx context.Context) (model.LabelSet, bool) { return v, ok } +// RouteLabels extracts route labels from the context. Iff none exists, the +// second argument is false. +func RouteLabels(ctx context.Context) (model.LabelSet, bool) { + v, ok := ctx.Value(keyRouteLabels).(model.LabelSet) + return v, ok +} + // Now extracts a now timestamp from the context. Iff none exists, the // second argument is false. func Now(ctx context.Context) (time.Time, bool) { diff --git a/notify/jira/jira_test.go b/notify/jira/jira_test.go index d267871ae4..0a728337de 100644 --- a/notify/jira/jira_test.go +++ b/notify/jira/jira_test.go @@ -1242,7 +1242,7 @@ func TestJiraPriority(t *testing.T) { tmpl.ExternalURL = u var ( - data = tmpl.Data("jira", model.LabelSet{}, notify.ReasonFirstNotification.String(), tc.alerts...) + data = tmpl.Data("jira", model.LabelSet{}, nil, notify.ReasonFirstNotification.String(), tc.alerts...) tmplTextErr error tmplText = notify.TmplText(tmpl, data, &tmplTextErr) diff --git a/notify/util.go b/notify/util.go index a99e2b067c..a86aabbb2d 100644 --- a/notify/util.go +++ b/notify/util.go @@ -204,12 +204,16 @@ func GetTemplateData(ctx context.Context, tmpl *template.Template, alerts []*typ if !ok { l.Error("Missing group labels") } + routeLabels, ok := RouteLabels(ctx) + if !ok { + l.Error("Missing route labels") + } notificationReason, ok := NotificationReason(ctx) if !ok { l.Error("Missing notification reason") notificationReason = ReasonUnknown } - return tmpl.Data(recv, groupLabels, notificationReason.String(), alerts...) + return tmpl.Data(recv, groupLabels, routeLabels, notificationReason.String(), alerts...) } func readAll(r io.Reader) string { diff --git a/template/template.go b/template/template.go index 1ddbcfc054..84c951c216 100644 --- a/template/template.go +++ b/template/template.go @@ -136,6 +136,30 @@ func (t *Template) FromGlob(path string) error { return nil } +// makeRouteLabelFunc returns a routeLabels template function bound to the given +// data and execute function. Looking up a route label renders its value as a +// template, so route label values may themselves reference group labels, other +// route labels, etc. (including recursively). +func makeRouteLabelFunc(data any, executeFunc func(string, any) (string, error)) func(string) (string, error) { + // The data may be passed either by value or by pointer, so handle both. + var castData *Data + if d, ok := data.(Data); ok { + castData = &d + } else if d, ok := data.(*Data); ok { + castData = d + } + return func(name string) (string, error) { + if castData == nil { + return "", nil + } + lv, ok := castData.RouteLabels[name] + if !ok { + return "", nil + } + return executeFunc(lv, data) + } +} + // ExecuteTextString needs a meaningful doc comment (TODO(fabxc)). func (t *Template) ExecuteTextString(text string, data any) (string, error) { if text == "" { @@ -145,7 +169,12 @@ func (t *Template) ExecuteTextString(text string, data any) (string, error) { if err != nil { return "", err } - tmpl, err = tmpl.New("").Option("missingkey=zero").Parse(text) + + funcs := tmpltext.FuncMap{ + "routeLabels": makeRouteLabelFunc(data, t.ExecuteTextString), + } + + tmpl, err = tmpl.New("").Option("missingkey=zero").Funcs(funcs).Parse(text) if err != nil { return "", err } @@ -163,7 +192,12 @@ func (t *Template) ExecuteHTMLString(html string, data any) (string, error) { if err != nil { return "", err } - tmpl, err = tmpl.New("").Option("missingkey=zero").Parse(html) + + funcs := tmplhtml.FuncMap{ + "routeLabels": makeRouteLabelFunc(data, t.ExecuteHTMLString), + } + + tmpl, err = tmpl.New("").Option("missingkey=zero").Funcs(funcs).Parse(html) if err != nil { return "", err } @@ -203,6 +237,12 @@ var DefaultFuncs = FuncMap{ "stringSlice": func(s ...string) []string { return s }, + // routeLabels is a placeholder needed so templates referencing it parse + // successfully. It is replaced dynamically with the real implementation in + // ExecuteTextString and ExecuteHTMLString. + "routeLabels": func(name string) (string, error) { + return "", nil + }, // date returns the text representation of the time in the specified format. "date": func(fmt string, t time.Time) string { return t.Format(fmt) @@ -261,21 +301,21 @@ type Pair struct { type Pairs []Pair // Names returns a list of names of the pairs. -func (ps Pairs) Names() []string { +func (ps Pairs) Names() Strings { ns := make([]string, 0, len(ps)) for _, p := range ps { ns = append(ns, p.Name) } - return ns + return Strings(ns) } // Values returns a list of values of the pairs. -func (ps Pairs) Values() []string { +func (ps Pairs) Values() Strings { vs := make([]string, 0, len(ps)) for _, p := range ps { vs = append(vs, p.Value) } - return vs + return Strings(vs) } func (ps Pairs) String() string { @@ -291,6 +331,15 @@ func (ps Pairs) String() string { return b.String() } +// Strings is a list of strings exposed to templates. +type Strings []string + +// Join makes strings.Join accessible from templates, e.g. +// {{ .GroupLabels.Values.Join ":" }}. +func (s Strings) Join(sep string) string { + return strings.Join(s, sep) +} + // KV is a set of key/value string pairs. type KV map[string]string @@ -334,12 +383,12 @@ func (kv KV) Remove(keys []string) KV { } // Names returns the names of the label names in the LabelSet. -func (kv KV) Names() []string { +func (kv KV) Names() Strings { return kv.SortedPairs().Names() } // Values returns a list of the values in the LabelSet. -func (kv KV) Values() []string { +func (kv KV) Values() Strings { return kv.SortedPairs().Values() } @@ -361,6 +410,7 @@ type Data struct { GroupLabels KV `json:"groupLabels"` CommonLabels KV `json:"commonLabels"` CommonAnnotations KV `json:"commonAnnotations"` + RouteLabels KV `json:"routeLabels"` ExternalURL string `json:"externalURL"` } @@ -402,7 +452,7 @@ func (as Alerts) Resolved() []Alert { } // Data assembles data for template expansion. -func (t *Template) Data(recv string, groupLabels model.LabelSet, notificationReason string, alerts ...*types.Alert) *Data { +func (t *Template) Data(recv string, groupLabels, routeLabels model.LabelSet, notificationReason string, alerts ...*types.Alert) *Data { typedAlerts := types.Alerts(alerts...) data := &Data{ @@ -413,6 +463,7 @@ func (t *Template) Data(recv string, groupLabels model.LabelSet, notificationRea GroupLabels: KV{}, CommonLabels: KV{}, CommonAnnotations: KV{}, + RouteLabels: KV{}, ExternalURL: t.ExternalURL.String(), } @@ -441,6 +492,10 @@ func (t *Template) Data(recv string, groupLabels model.LabelSet, notificationRea data.GroupLabels[string(k)] = string(v) } + for k, v := range routeLabels { + data.RouteLabels[string(k)] = string(v) + } + if len(alerts) >= 1 { var ( commonLabels = alerts[0].Labels.Clone() diff --git a/template/template_test.go b/template/template_test.go index faa3050964..13fed03cee 100644 --- a/template/template_test.go +++ b/template/template_test.go @@ -34,7 +34,7 @@ func TestPairNames(t *testing.T) { {"name3", "value3"}, } - expected := []string{"name1", "name2", "name3"} + expected := Strings{"name1", "name2", "name3"} require.Equal(t, expected, pairs.Names()) } @@ -45,7 +45,7 @@ func TestPairValues(t *testing.T) { {"name3", "value3"}, } - expected := []string{"value1", "value2", "value3"} + expected := Strings{"value1", "value2", "value3"} require.Equal(t, expected, pairs.Values()) } @@ -97,7 +97,7 @@ func TestKVRemove(t *testing.T) { kv = kv.Remove([]string{"key2", "key4"}) - expected := []string{"key1", "key3"} + expected := Strings{"key1", "key3"} require.Equal(t, expected, kv.Names()) } @@ -143,6 +143,7 @@ func TestData(t *testing.T) { for _, tc := range []struct { receiver string groupLabels model.LabelSet + routeLabels model.LabelSet alerts []*types.Alert exp *Data @@ -280,9 +281,39 @@ func TestData(t *testing.T) { ExternalURL: u.String(), }, }, + { + // test that route labels are passed through + groupLabels: model.LabelSet{ + "label_a": "a", + "label_b": "b", + }, + routeLabels: model.LabelSet{ + "rlabel_a": "rvalue a", + "rlabel_plain": "plain {}", + }, + exp: &Data{ + Status: "resolved", + Alerts: Alerts{}, + NotificationReason: "first notification", + GroupLabels: KV{ + "label_a": "a", + "label_b": "b", + }, + RouteLabels: KV{ + "rlabel_a": "rvalue a", + "rlabel_plain": "plain {}", + }, + CommonLabels: KV{}, + CommonAnnotations: KV{}, + ExternalURL: u.String(), + }, + }, } { t.Run("", func(t *testing.T) { - got := tmpl.Data(tc.receiver, tc.groupLabels, "first notification", tc.alerts...) + got := tmpl.Data(tc.receiver, tc.groupLabels, tc.routeLabels, "first notification", tc.alerts...) + if tc.exp.RouteLabels == nil { + tc.exp.RouteLabels = KV{} + } require.Equal(t, tc.exp, got) }) } @@ -509,6 +540,24 @@ func TestTemplateExpansion(t *testing.T) { in: `{{- $newList := list -}}{{ range .Alerts }}{{ $m := dict "status" .Status "labels" .Labels }}{{ $newList = append $newList $m }}{{ end }}{{ toJson $newList }}`, exp: `[{"labels":null,"status":"firing"},{"labels":null,"status":"resolved"}]`, }, + { + title: "Template using routeLabels", + in: `Simple: {{ routeLabels "rl1" }} - Templated: {{ routeLabels "rl2" }} - Recursive: {{ routeLabels "rl3" }}`, + data: Data{ + GroupLabels: KV{ + "key1": "key1", + "key2": "key2", + "key3": "key3", + "key4": "key4", + }, + RouteLabels: KV{ + "rl1": "rl1", + "rl2": `{{ .GroupLabels.key1 }}`, + "rl3": `{{ routeLabels "rl2" }} recursive`, + }, + }, + exp: "Simple: rl1 - Templated: key1 - Recursive: key1 recursive", + }, } { t.Run(tc.title, func(t *testing.T) { f := tmpl.ExecuteTextString @@ -868,7 +917,7 @@ func BenchmarkTemplateData(b *testing.B) { b.ResetTimer() for b.Loop() { - tmpl.Data("receiver", groupLabels, "firing", alerts...) + tmpl.Data("receiver", groupLabels, nil, "firing", alerts...) } } From 79e2677298be05e9440daa6c6f9950f3b8eedabf Mon Sep 17 00:00:00 2001 From: Guido Trotter Date: Wed, 24 Jun 2026 08:54:57 -0400 Subject: [PATCH 039/120] dispatch: render route labels per aggregation group Thread the notification template through the dispatcher and into each aggregation group so route label values can be rendered as templates against the group's current alerts (e.g. a label value referencing {{ .GroupLabels.x }} or another route label). Rendering is lazy: an aggrGroup tracks a routeLabelsDirty flag set on insert and on deletion of resolved alerts, and only re-renders when the rendered labels are actually requested (RouteLabels()) or when flushing. Access is guarded by an RWMutex with a read-fast-path / write-on-dirty lock upgrade. When all alerts are resolved and deleted, the last rendered labels are retained rather than re-rendered against an empty group. Route labels rendered against the flush batch are placed on the notification context, superseding the static route labels set in run(). Signed-off-by: Guido Trotter --- app/reloader.go | 1 + dispatch/dispatch.go | 116 ++++++++++++++++++--- dispatch/dispatch_bench_test.go | 2 +- dispatch/dispatch_test.go | 172 +++++++++++++++++++++++++++++--- 4 files changed, 262 insertions(+), 29 deletions(-) diff --git a/app/reloader.go b/app/reloader.go index 38fb1cee01..d9c62ca9c8 100644 --- a/app/reloader.go +++ b/app/reloader.go @@ -206,6 +206,7 @@ func (r *reloader) reload(conf *config.Config) error { r.logger, r.eventRecorder, r.dispatcherMetrics, + tmpl, ) routes.Walk(func(rt *dispatch.Route) { if rt.RouteOpts.RepeatInterval > r.retention { diff --git a/dispatch/dispatch.go b/dispatch/dispatch.go index 3b70662fe8..e0539195f4 100644 --- a/dispatch/dispatch.go +++ b/dispatch/dispatch.go @@ -40,6 +40,7 @@ import ( "github.com/prometheus/alertmanager/pkg/labels" "github.com/prometheus/alertmanager/provider" "github.com/prometheus/alertmanager/store" + "github.com/prometheus/alertmanager/template" "github.com/prometheus/alertmanager/tracing" "github.com/prometheus/alertmanager/types" ) @@ -79,6 +80,7 @@ type Dispatcher struct { logger *slog.Logger recorder eventrecorder.Recorder + tmpl *template.Template startTimer *time.Timer state atomic.Int32 @@ -110,6 +112,7 @@ func NewDispatcher( logger *slog.Logger, recorder eventrecorder.Recorder, metrics *DispatcherMetrics, + tmpl *template.Template, ) *Dispatcher { if limits == nil { limits = nilLimits{} @@ -133,6 +136,7 @@ func NewDispatcher( recorder: recorder, metrics: metrics, limits: limits, + tmpl: tmpl, propagator: otel.GetTextMapPropagator(), } disp.state.Store(DispatcherStateUnknown) @@ -478,7 +482,7 @@ func (d *Dispatcher) groupAlert(ctx context.Context, alert *alert.Alert, route * return } - ag := newAggrGroup(d.ctx, groupLabels, route, d.timeout, d.recorder, d.logger) + ag := newAggrGroup(d.ctx, groupLabels, route, d.timeout, d.recorder, d.logger, d.tmpl) // Insert the 1st alert in the group before starting the group's run() // function, to make sure that when the run() will be executed the 1st // alert is already there. @@ -613,6 +617,7 @@ type aggrGroup struct { alerts *store.Alerts marker marker.AlertMarker recorder eventrecorder.Recorder + tmpl *template.Template ctx context.Context cancel func() done chan struct{} @@ -620,6 +625,14 @@ type aggrGroup struct { timeout func(time.Duration) time.Duration running atomic.Bool flushIdx uint64 + + // mtx guards routeLabels and routeLabelsDirty. + mtx sync.RWMutex + // routeLabels holds the route labels rendered against the group's current + // alerts. They only change when alerts are inserted or deleted, so they are + // rendered lazily: routeLabelsDirty marks when a re-render is needed. + routeLabels model.LabelSet + routeLabelsDirty bool } // newAggrGroup returns a new aggregation group. @@ -630,22 +643,25 @@ func newAggrGroup( to func(time.Duration) time.Duration, recorder eventrecorder.Recorder, logger *slog.Logger, + tmpl *template.Template, ) *aggrGroup { if to == nil { to = func(d time.Duration) time.Duration { return d } } ag := &aggrGroup{ - labels: labels, - routeID: r.ID(), - routeKey: r.Key(), - matchers: r.Matchers, - opts: &r.RouteOpts, - timeout: to, - alerts: store.NewAlerts(), - marker: marker.NewAlertMarker(), - recorder: recorder, - done: make(chan struct{}), - flushIdx: 1, + labels: labels, + routeID: r.ID(), + routeKey: r.Key(), + matchers: r.Matchers, + opts: &r.RouteOpts, + timeout: to, + alerts: store.NewAlerts(), + marker: marker.NewAlertMarker(), + recorder: recorder, + tmpl: tmpl, + done: make(chan struct{}), + flushIdx: 1, + routeLabels: model.LabelSet{}, } ag.ctx, ag.cancel = context.WithCancel(ctx) @@ -674,6 +690,70 @@ func (ag *aggrGroup) String() string { return ag.GroupKey() } +// renderRouteLabels renders the route's labels as templates against the given +// alerts and the group's data. A route label value may be a template, so this +// allows it to reference group labels, other route labels, etc. +func (ag *aggrGroup) renderRouteLabels(alerts ...*alert.Alert) model.LabelSet { + if ag.tmpl == nil { + return ag.routeLabels + } + + renderedRouteLabels := make(model.LabelSet, len(ag.opts.Labels)) + + data := ag.tmpl.Data(ag.opts.Receiver, ag.labels, ag.opts.Labels, notify.ReasonUnknown.String(), alerts...) + + logger := ag.logger.With("data", data) + + for label, value := range ag.opts.Labels { + v := string(value) + if rendered, err := ag.tmpl.ExecuteTextString(v, data); err == nil { + renderedRouteLabels[label] = model.LabelValue(rendered) + logger.Debug("rendered route label", "label", label, "value", rendered) + } else { + logger.Error("failed to render route label", "label", label, "value", v, "err", err) + } + } + + return renderedRouteLabels +} + +// RouteLabels returns the route labels rendered against the group's current +// alerts. Rendering is lazy: it only happens when the set of alerts has changed +// since the last render (tracked by routeLabelsDirty). +func (ag *aggrGroup) RouteLabels() model.LabelSet { + upgradedLock := false + + ag.mtx.RLock() + + defer func() { + if upgradedLock { + ag.mtx.Unlock() + } else { + ag.mtx.RUnlock() + } + }() + + if ag.routeLabelsDirty { + // Upgrade the lock because we need to update routeLabels. + upgradedLock = true + ag.mtx.RUnlock() + ag.mtx.Lock() + + if ag.routeLabelsDirty { + alerts := ag.alerts.List() + if len(alerts) > 0 { + ag.routeLabels = ag.renderRouteLabels(alerts...) + } + // If alerts is empty (all resolved/deleted), keep the previously + // rendered routeLabels. The group will be garbage-collected on the + // next maintenance cycle. + ag.routeLabelsDirty = false + } + } + + return ag.routeLabels +} + func (ag *aggrGroup) run(nf notifyFunc) { defer close(ag.done) defer ag.next.Stop() @@ -719,6 +799,10 @@ func (ag *aggrGroup) run(nf notifyFunc) { ) defer span.End() + // Render route labels against the batch of alerts that we are + // about to notify about. + ctx = notify.WithRouteLabels(ctx, ag.renderRouteLabels(alerts...)) + success := nf(ctx, alerts...) if !success { span.SetStatus(codes.Error, "notification failed") @@ -775,6 +859,9 @@ func (ag *aggrGroup) insert(ctx context.Context, alert *alert.Alert) bool { ag.recorder.RecordEvent(ctx, func() *eventrecorderpb.EventData { return notify.NewAlertGroupedEvent(ag.alertGroupInfo(), alert) }) + ag.mtx.Lock() + ag.routeLabelsDirty = true + ag.mtx.Unlock() } return true } @@ -825,6 +912,11 @@ func (ag *aggrGroup) flush(notify func(...*alert.Alert) bool) { if err := ag.alerts.DeleteIfNotModified(resolvedSlice, true); err != nil { ag.logger.Error("error on delete alerts", "err", err) } else { + if len(resolvedSlice) > 0 { + ag.mtx.Lock() + ag.routeLabelsDirty = true + ag.mtx.Unlock() + } // Delete markers for resolved alerts that are not in the store. for _, alert := range resolvedSlice { _, err := ag.alerts.Get(alert.Fingerprint()) diff --git a/dispatch/dispatch_bench_test.go b/dispatch/dispatch_bench_test.go index 45fcdb9e12..353a428433 100644 --- a/dispatch/dispatch_bench_test.go +++ b/dispatch/dispatch_bench_test.go @@ -151,7 +151,7 @@ func setupDispatcher(b *testing.B, route *Route) (*Dispatcher, *mem.Alerts, *rec timeout := func(d time.Duration) time.Duration { return time.Duration(0) } metrics := NewDispatcherMetrics(false, reg, nil) - dispatcher := NewDispatcher(alerts, route, recorder, marker, timeout, 30*time.Second, nil, logger, eventrecorder.NopRecorder(), metrics) + dispatcher := NewDispatcher(alerts, route, recorder, marker, timeout, 30*time.Second, nil, logger, eventrecorder.NopRecorder(), metrics, nil) return dispatcher, alerts, recorder } diff --git a/dispatch/dispatch_test.go b/dispatch/dispatch_test.go index 9766b0e931..94e66788d9 100644 --- a/dispatch/dispatch_test.go +++ b/dispatch/dispatch_test.go @@ -17,6 +17,7 @@ import ( "context" "fmt" "log/slog" + "net/url" "reflect" "runtime" "sort" @@ -37,6 +38,7 @@ import ( "github.com/prometheus/alertmanager/marker" "github.com/prometheus/alertmanager/notify" "github.com/prometheus/alertmanager/provider/mem" + "github.com/prometheus/alertmanager/template" ) const testMaintenanceInterval = 30 * time.Second @@ -163,7 +165,7 @@ func TestAggrGroup(t *testing.T) { // Test regular situation where we wait for group_wait to send out alerts. createdAt := time.Now() - ag := newAggrGroup(context.Background(), lset, route, nil, eventrecorder.NopRecorder(), promslog.NewNopLogger()) + ag := newAggrGroup(context.Background(), lset, route, nil, eventrecorder.NopRecorder(), promslog.NewNopLogger(), nil) go ag.run(ntfy) ctx := context.Background() @@ -182,7 +184,7 @@ func TestAggrGroup(t *testing.T) { // Finally, set all alerts to be resolved. After successful notify the aggregation group // should empty itself. createdAt = time.Now() - ag = newAggrGroup(context.Background(), lset, route, nil, eventrecorder.NopRecorder(), promslog.NewNopLogger()) + ag = newAggrGroup(context.Background(), lset, route, nil, eventrecorder.NopRecorder(), promslog.NewNopLogger(), nil) go ag.run(ntfy) ag.insert(ctx, a1) @@ -324,7 +326,7 @@ route: timeout := func(d time.Duration) time.Duration { return time.Duration(0) } recorder := &recordStage{alerts: make(map[string]map[model.Fingerprint]*alert.Alert)} - dispatcher := NewDispatcher(alerts, route, recorder, marker, timeout, testMaintenanceInterval, nil, logger, eventrecorder.NopRecorder(), NewDispatcherMetrics(false, reg, nil)) + dispatcher := NewDispatcher(alerts, route, recorder, marker, timeout, testMaintenanceInterval, nil, logger, eventrecorder.NopRecorder(), NewDispatcherMetrics(false, reg, nil), nil) go dispatcher.Run(time.Now()) defer dispatcher.Stop() @@ -484,7 +486,7 @@ route: recorder := &recordStage{alerts: make(map[string]map[model.Fingerprint]*alert.Alert)} lim := limits{groups: 6} m := NewDispatcherMetrics(true, reg, nil) - dispatcher := NewDispatcher(alerts, route, recorder, marker, timeout, testMaintenanceInterval, lim, logger, eventrecorder.NopRecorder(), m) + dispatcher := NewDispatcher(alerts, route, recorder, marker, timeout, testMaintenanceInterval, lim, logger, eventrecorder.NopRecorder(), m, nil) go dispatcher.Run(time.Now()) defer dispatcher.Stop() @@ -604,7 +606,7 @@ func TestDispatcherRace(t *testing.T) { timeout := func(d time.Duration) time.Duration { return time.Duration(0) } route := &Route{} - dispatcher := NewDispatcher(alerts, route, nil, marker, timeout, testMaintenanceInterval, nil, logger, eventrecorder.NopRecorder(), NewDispatcherMetrics(false, reg, nil)) + dispatcher := NewDispatcher(alerts, route, nil, marker, timeout, testMaintenanceInterval, nil, logger, eventrecorder.NopRecorder(), NewDispatcherMetrics(false, reg, nil), nil) go dispatcher.Run(time.Now()) dispatcher.Stop() } @@ -633,7 +635,7 @@ func TestDispatcherRaceOnFirstAlertNotDeliveredWhenGroupWaitIsZero(t *testing.T) timeout := func(d time.Duration) time.Duration { return d } recorder := &recordStage{alerts: make(map[string]map[model.Fingerprint]*alert.Alert)} - dispatcher := NewDispatcher(alerts, route, recorder, marker, timeout, testMaintenanceInterval, nil, logger, eventrecorder.NopRecorder(), NewDispatcherMetrics(false, reg, nil)) + dispatcher := NewDispatcher(alerts, route, recorder, marker, timeout, testMaintenanceInterval, nil, logger, eventrecorder.NopRecorder(), NewDispatcherMetrics(false, reg, nil), nil) go dispatcher.Run(time.Now()) defer dispatcher.Stop() @@ -686,7 +688,7 @@ func TestDispatcher_DoMaintenance(t *testing.T) { recorder := &recordStage{alerts: make(map[string]map[model.Fingerprint]*alert.Alert)} ctx := context.Background() - dispatcher := NewDispatcher(alerts, route, recorder, marker, timeout, testMaintenanceInterval, nil, promslog.NewNopLogger(), eventrecorder.NopRecorder(), NewDispatcherMetrics(false, r, nil)) + dispatcher := NewDispatcher(alerts, route, recorder, marker, timeout, testMaintenanceInterval, nil, promslog.NewNopLogger(), eventrecorder.NopRecorder(), NewDispatcherMetrics(false, r, nil), nil) // Manually create the routeAggrGroups structure since we are not calling Run(). dispatcher.routeGroupsSlice = make([]routeAggrGroups, route.Idx+1) dispatcher.routeGroupsSlice[route.Idx] = routeAggrGroups{ @@ -695,7 +697,7 @@ func TestDispatcher_DoMaintenance(t *testing.T) { // Insert an aggregation group with one resolved alert. labels := model.LabelSet{"alertname": "1"} - aggrGroup1 := newAggrGroup(ctx, labels, route, timeout, eventrecorder.NopRecorder(), promslog.NewNopLogger()) + aggrGroup1 := newAggrGroup(ctx, labels, route, timeout, eventrecorder.NopRecorder(), promslog.NewNopLogger(), nil) dispatcher.routeGroupsSlice[route.Idx].groups.Store(aggrGroup1.fingerprint(), aggrGroup1) // Add a resolved alert @@ -773,7 +775,7 @@ func TestGroupAlert_RecoversWhenCASFails(t *testing.T) { timeout := func(d time.Duration) time.Duration { return d } recorder := &recordStage{alerts: make(map[string]map[model.Fingerprint]*alert.Alert)} metrics := NewDispatcherMetrics(false, reg, featurecontrol.NoopFlags{}) - dispatcher := NewDispatcher(alerts, route, recorder, marker.NewGroupMarker(), timeout, testMaintenanceInterval, nil, logger, eventrecorder.NopRecorder(), metrics) + dispatcher := NewDispatcher(alerts, route, recorder, marker.NewGroupMarker(), timeout, testMaintenanceInterval, nil, logger, eventrecorder.NopRecorder(), metrics, nil) // Don't call Run — put the dispatcher manually in the DispatcherStateWaitingToStart // state so groupAlert's final switch falls through the default branch and the // aggregation group's run goroutine is never started. @@ -782,7 +784,7 @@ func TestGroupAlert_RecoversWhenCASFails(t *testing.T) { rounds := 0 for rounds < maxRounds && testutil.ToFloat64(metrics.aggrGroupCreationRetries) == 0 { groupLabels := model.LabelSet{"alertname": model.LabelValue(fmt.Sprintf("shared-%d", rounds))} - destroyedAg := newAggrGroup(context.Background(), groupLabels, route, timeout, eventrecorder.NopRecorder(), logger) + destroyedAg := newAggrGroup(context.Background(), groupLabels, route, timeout, eventrecorder.NopRecorder(), logger, nil) // Mark the store destroyed: empty store + destroyIfEmpty=true. require.NoError(t, destroyedAg.alerts.DeleteIfNotModified(alert.AlertSlice{}, true)) require.True(t, destroyedAg.destroyed()) @@ -858,14 +860,14 @@ func TestGroupAlert_DisplacedAggrGroupGoroutineExits(t *testing.T) { } timeout := func(d time.Duration) time.Duration { return d } recorder := &recordStage{alerts: make(map[string]map[model.Fingerprint]*alert.Alert)} - dispatcher := NewDispatcher(alerts, route, recorder, marker.NewGroupMarker(), timeout, testMaintenanceInterval, nil, logger, eventrecorder.NopRecorder(), NewDispatcherMetrics(false, reg, featurecontrol.NoopFlags{})) + dispatcher := NewDispatcher(alerts, route, recorder, marker.NewGroupMarker(), timeout, testMaintenanceInterval, nil, logger, eventrecorder.NopRecorder(), NewDispatcherMetrics(false, reg, featurecontrol.NoopFlags{}), nil) dispatcher.routeGroupsSlice = []routeAggrGroups{{route: route}} // WaitingToStart so groupAlert won't auto-start the new ag — keeps the // test focused on the displaced group. dispatcher.state.Store(DispatcherStateWaitingToStart) groupLabels := model.LabelSet{"alertname": "displaced"} - displaced := newAggrGroup(context.Background(), groupLabels, route, timeout, eventrecorder.NopRecorder(), logger) + displaced := newAggrGroup(context.Background(), groupLabels, route, timeout, eventrecorder.NopRecorder(), logger, nil) // Mark destroyed so groupAlert can't insert into it and is forced down // the CAS-replace path. require.NoError(t, displaced.alerts.DeleteIfNotModified(alert.AlertSlice{}, true)) @@ -909,7 +911,7 @@ func TestDispatcher_DeleteResolvedAlertsFromMarker(t *testing.T) { logger := promslog.NewNopLogger() // Create an aggregation group - ag := newAggrGroup(ctx, labels, route, timeout, eventrecorder.NopRecorder(), logger) + ag := newAggrGroup(ctx, labels, route, timeout, eventrecorder.NopRecorder(), logger, nil) // Create test alerts: one active and one resolved now := time.Now() @@ -977,7 +979,7 @@ func TestDispatcher_DeleteResolvedAlertsFromMarker(t *testing.T) { logger := promslog.NewNopLogger() // Create an aggregation group - ag := newAggrGroup(ctx, labels, route, timeout, eventrecorder.NopRecorder(), logger) + ag := newAggrGroup(ctx, labels, route, timeout, eventrecorder.NopRecorder(), logger, nil) // Create a resolved alert now := time.Now() @@ -1030,7 +1032,7 @@ func TestDispatcher_DeleteResolvedAlertsFromMarker(t *testing.T) { logger := promslog.NewNopLogger() // Create an aggregation group - ag := newAggrGroup(ctx, labels, route, timeout, eventrecorder.NopRecorder(), logger) + ag := newAggrGroup(ctx, labels, route, timeout, eventrecorder.NopRecorder(), logger, nil) // Create a resolved alert now := time.Now() @@ -1111,7 +1113,7 @@ func TestDispatchOnStartup(t *testing.T) { now := time.Now() startDelay := 2 * time.Second startTime := time.Now().Add(startDelay) - dispatcher := NewDispatcher(alerts, route, recorder, marker, timeout, testMaintenanceInterval, nil, logger, eventrecorder.NopRecorder(), NewDispatcherMetrics(false, reg, nil)) + dispatcher := NewDispatcher(alerts, route, recorder, marker, timeout, testMaintenanceInterval, nil, logger, eventrecorder.NopRecorder(), NewDispatcherMetrics(false, reg, nil), nil) go dispatcher.Run(startTime) defer dispatcher.Stop() @@ -1272,3 +1274,141 @@ func BenchmarkGetGroupLabels(b *testing.B) { } }) } + +// TestRouteLabelsAfterAllAlertsResolved verifies that calling RouteLabels() +// after all alerts have been resolved and deleted does not panic or return +// incorrect results. This exercises the race between flush() emptying the +// alerts store and RouteLabels() attempting to re-render. +func TestRouteLabelsAfterAllAlertsResolved(t *testing.T) { + lset := model.LabelSet{"alertname": "test"} + opts := &RouteOpts{ + Receiver: "test-receiver", + GroupBy: map[model.LabelName]struct{}{"alertname": {}}, + GroupWait: 10 * time.Millisecond, + GroupInterval: 10 * time.Millisecond, + RepeatInterval: 1 * time.Hour, + Labels: model.LabelSet{ + "description": "{{ (index .Alerts 0).Labels.alertname }}", + }, + } + route := &Route{RouteOpts: *opts} + + tmpl, err := template.FromGlobs([]string{}) + require.NoError(t, err) + tmpl.ExternalURL = &url.URL{Scheme: "http", Host: "example.com"} + + ag := newAggrGroup(context.Background(), lset, route, nil, + eventrecorder.NopRecorder(), promslog.NewNopLogger(), tmpl) + + alertsCh := make(chan alert.AlertSlice) + ntfy := func(ctx context.Context, alerts ...*alert.Alert) bool { + alertsCh <- alert.AlertSlice(alerts) + return true + } + go ag.run(ntfy) + defer ag.stop() + + ctx := context.Background() + + // Insert a firing alert and wait for the first flush. + a1 := &alert.Alert{ + Alert: model.Alert{ + Labels: model.LabelSet{"alertname": "test", "instance": "a"}, + StartsAt: time.Now().Add(-time.Hour), + EndsAt: time.Now().Add(time.Hour), + }, + UpdatedAt: time.Now(), + } + ag.insert(ctx, a1) + + select { + case <-alertsCh: + case <-time.After(2 * time.Second): + t.Fatal("timed out waiting for first flush") + } + + // Route labels should be rendered from the alert. + rl := ag.RouteLabels() + require.Equal(t, model.LabelValue("test"), rl["description"], + "route label should be rendered from alert") + + // Now resolve the alert and wait for the flush that deletes it. + a1r := *a1 + a1r.EndsAt = time.Now() + ag.insert(ctx, &a1r) + + select { + case <-alertsCh: + case <-time.After(2 * time.Second): + t.Fatal("timed out waiting for resolved flush") + } + + // After flush deletes resolved alerts, RouteLabels() must not panic and + // should return the last successfully rendered labels. + require.NotPanics(t, func() { + rl = ag.RouteLabels() + }, "RouteLabels() must not panic after all alerts are deleted") + require.Equal(t, model.LabelValue("test"), rl["description"], + "route label should retain last rendered value after alerts are deleted") +} + +// TestRouteLabelsInsertConcurrentWithRouteLabels verifies that concurrent +// insert() and RouteLabels() calls don't have race conditions. Run with -race. +func TestRouteLabelsInsertConcurrentWithRouteLabels(t *testing.T) { + lset := model.LabelSet{"alertname": "test"} + opts := &RouteOpts{ + Receiver: "test-receiver", + GroupBy: map[model.LabelName]struct{}{"alertname": {}}, + GroupWait: 1 * time.Hour, // don't flush + GroupInterval: 1 * time.Hour, + RepeatInterval: 1 * time.Hour, + Labels: model.LabelSet{ + "info": "static-value", + }, + } + route := &Route{RouteOpts: *opts} + + tmpl, err := template.FromGlobs([]string{}) + require.NoError(t, err) + tmpl.ExternalURL = &url.URL{Scheme: "http", Host: "example.com"} + + ag := newAggrGroup(context.Background(), lset, route, nil, + eventrecorder.NopRecorder(), promslog.NewNopLogger(), tmpl) + + ctx := context.Background() + + // Hammer insert() and RouteLabels() concurrently. Under -race this will + // flag any data race on routeLabels/routeLabelsDirty. + var wg sync.WaitGroup + const goroutines = 10 + const iterations = 100 + + for range goroutines { + wg.Go(func() { + for i := range iterations { + a := &alert.Alert{ + Alert: model.Alert{ + Labels: model.LabelSet{ + "alertname": "test", + "i": model.LabelValue(fmt.Sprintf("%d", i)), + }, + StartsAt: time.Now(), + EndsAt: time.Now().Add(time.Hour), + }, + UpdatedAt: time.Now(), + } + ag.insert(ctx, a) + } + }) + } + + for range goroutines { + wg.Go(func() { + for range iterations { + ag.RouteLabels() + } + }) + } + + wg.Wait() +} From fe610eac3258f20b58d74d2e7500be995dfb2589 Mon Sep 17 00:00:00 2001 From: Guido Trotter Date: Wed, 24 Jun 2026 09:02:27 -0400 Subject: [PATCH 040/120] api/v2: expose route labels in /alerts/groups Add a routeLabels field to the alertGroup API model and populate it in the GetAlertGroups handler from the dispatcher's per-group rendered route labels (dispatch.AlertGroup.RouteLabels). Signed-off-by: Guido Trotter --- api/v2/api.go | 7 ++-- api/v2/api_test.go | 61 +++++++++++++++++++++++++++++++++ api/v2/models/alert_group.go | 54 +++++++++++++++++++++++++++++ api/v2/openapi.yaml | 3 ++ api/v2/restapi/embedded_spec.go | 8 +++++ dispatch/dispatch.go | 10 +++--- dispatch/dispatch_test.go | 5 ++- 7 files changed, 140 insertions(+), 8 deletions(-) diff --git a/api/v2/api.go b/api/v2/api.go index dddbb01d78..d4c95cf336 100644 --- a/api/v2/api.go +++ b/api/v2/api.go @@ -512,9 +512,10 @@ func (api *API) getAlertGroupsHandler(params alertgroup_ops.GetAlertGroupsParams } ag := &open_api_models.AlertGroup{ - Receiver: &open_api_models.ReceiverReference{Name: &alertGroup.Receiver}, - Labels: ModelLabelSetToAPILabelSet(alertGroup.Labels), - Alerts: make([]*open_api_models.GettableAlert, 0, len(alertGroup.Alerts)), + Receiver: &open_api_models.ReceiverReference{Name: &alertGroup.Receiver}, + Labels: ModelLabelSetToAPILabelSet(alertGroup.Labels), + RouteLabels: ModelLabelSetToAPILabelSet(alertGroup.RouteLabels), + Alerts: make([]*open_api_models.GettableAlert, 0, len(alertGroup.Alerts)), } for _, alert := range alertGroup.Alerts { diff --git a/api/v2/api_test.go b/api/v2/api_test.go index 440713a983..cf772cb299 100644 --- a/api/v2/api_test.go +++ b/api/v2/api_test.go @@ -35,10 +35,12 @@ import ( "github.com/prometheus/alertmanager/alert" open_api_models "github.com/prometheus/alertmanager/api/v2/models" + alertgroup_ops "github.com/prometheus/alertmanager/api/v2/restapi/operations/alertgroup" general_ops "github.com/prometheus/alertmanager/api/v2/restapi/operations/general" receiver_ops "github.com/prometheus/alertmanager/api/v2/restapi/operations/receiver" silence_ops "github.com/prometheus/alertmanager/api/v2/restapi/operations/silence" "github.com/prometheus/alertmanager/config" + "github.com/prometheus/alertmanager/dispatch" "github.com/prometheus/alertmanager/pkg/labels" "github.com/prometheus/alertmanager/silence" "github.com/prometheus/alertmanager/silence/silencepb" @@ -944,3 +946,62 @@ func TestPostSilences_QuotedMatchers(t *testing.T) { require.Len(t, silProto.MatcherSets[0].Matchers, 1) require.Equal(t, "\"bar\"", silProto.MatcherSets[0].Matchers[0].Pattern) } + +func TestGetAlertGroupsHandlerRouteLabels(t *testing.T) { + in := ` +route: + receiver: team-X + routes: + - receiver: team-X + labels: + team: X + +receivers: +- name: 'team-X' +` + cfg, err := config.Load(in) + require.NoError(t, err) + + group := &dispatch.AlertGroup{ + Labels: model.LabelSet{"alertname": "Foo"}, + RouteLabels: model.LabelSet{"team": "X"}, + Receiver: "team-X", + GroupKey: "key", + RouteID: "route", + } + + api := API{ + uptime: time.Now(), + logger: promslog.NewNopLogger(), + alertGroups: func(context.Context, func(*dispatch.Route) bool, func(*alert.Alert, time.Time) bool) (dispatch.AlertGroups, map[model.Fingerprint][]string, error) { + return dispatch.AlertGroups{group}, map[model.Fingerprint][]string{}, nil + }, + groupMutedFunc: func(routeID, groupKey string) ([]string, bool) { + return nil, false + }, + } + api.Update(cfg, func(context.Context, model.LabelSet) {}) + + r, err := http.NewRequest("GET", "/api/v2/alerts/groups", nil) + require.NoError(t, err) + + truePtr := true + responder := api.getAlertGroupsHandler(alertgroup_ops.GetAlertGroupsParams{ + HTTPRequest: r, + Active: &truePtr, + Silenced: &truePtr, + Inhibited: &truePtr, + Muted: &truePtr, + }) + + w := httptest.NewRecorder() + responder.WriteResponse(w, runtime.JSONProducer()) + require.Equal(t, 200, w.Code) + + body, _ := io.ReadAll(w.Result().Body) + + var groups open_api_models.AlertGroups + require.NoError(t, json.Unmarshal(body, &groups)) + require.Len(t, groups, 1) + require.Equal(t, open_api_models.LabelSet{"team": "X"}, groups[0].RouteLabels) +} diff --git a/api/v2/models/alert_group.go b/api/v2/models/alert_group.go index 6cad0ab2e4..848df30c9d 100644 --- a/api/v2/models/alert_group.go +++ b/api/v2/models/alert_group.go @@ -46,6 +46,10 @@ type AlertGroup struct { // receiver // Required: true Receiver *ReceiverReference `json:"receiver"` + + // route labels + // Required: true + RouteLabels LabelSet `json:"routeLabels"` } // Validate validates this alert group @@ -64,6 +68,10 @@ func (m *AlertGroup) Validate(formats strfmt.Registry) error { res = append(res, err) } + if err := m.validateRouteLabels(formats); err != nil { + res = append(res, err) + } + if len(res) > 0 { return errors.CompositeValidationError(res...) } @@ -149,6 +157,30 @@ func (m *AlertGroup) validateReceiver(formats strfmt.Registry) error { return nil } +func (m *AlertGroup) validateRouteLabels(formats strfmt.Registry) error { + + if err := validate.Required("routeLabels", "body", m.RouteLabels); err != nil { + return err + } + + if m.RouteLabels != nil { + if err := m.RouteLabels.Validate(formats); err != nil { + ve := new(errors.Validation) + if stderrors.As(err, &ve) { + return ve.ValidateName("routeLabels") + } + ce := new(errors.CompositeError) + if stderrors.As(err, &ce) { + return ce.ValidateName("routeLabels") + } + + return err + } + } + + return nil +} + // ContextValidate validate this alert group based on the context it is used func (m *AlertGroup) ContextValidate(ctx context.Context, formats strfmt.Registry) error { var res []error @@ -165,6 +197,10 @@ func (m *AlertGroup) ContextValidate(ctx context.Context, formats strfmt.Registr res = append(res, err) } + if err := m.contextValidateRouteLabels(ctx, formats); err != nil { + res = append(res, err) + } + if len(res) > 0 { return errors.CompositeValidationError(res...) } @@ -239,6 +275,24 @@ func (m *AlertGroup) contextValidateReceiver(ctx context.Context, formats strfmt return nil } +func (m *AlertGroup) contextValidateRouteLabels(ctx context.Context, formats strfmt.Registry) error { + + if err := m.RouteLabels.ContextValidate(ctx, formats); err != nil { + ve := new(errors.Validation) + if stderrors.As(err, &ve) { + return ve.ValidateName("routeLabels") + } + ce := new(errors.CompositeError) + if stderrors.As(err, &ce) { + return ce.ValidateName("routeLabels") + } + + return err + } + + return nil +} + // MarshalBinary interface implementation func (m *AlertGroup) MarshalBinary() ([]byte, error) { if m == nil { diff --git a/api/v2/openapi.yaml b/api/v2/openapi.yaml index 1ee47c30dc..113811bbce 100644 --- a/api/v2/openapi.yaml +++ b/api/v2/openapi.yaml @@ -501,6 +501,8 @@ definitions: properties: labels: $ref: '#/definitions/labelSet' + routeLabels: + $ref: '#/definitions/labelSet' receiver: $ref: '#/definitions/receiverReference' alerts: @@ -509,6 +511,7 @@ definitions: $ref: '#/definitions/gettableAlert' required: - labels + - routeLabels - receiver - alerts alertStatus: diff --git a/api/v2/restapi/embedded_spec.go b/api/v2/restapi/embedded_spec.go index 3e41640c95..b028849f6f 100644 --- a/api/v2/restapi/embedded_spec.go +++ b/api/v2/restapi/embedded_spec.go @@ -425,6 +425,7 @@ func init() { "type": "object", "required": [ "labels", + "routeLabels", "receiver", "alerts" ], @@ -440,6 +441,9 @@ func init() { }, "receiver": { "$ref": "#/definitions/receiverReference" + }, + "routeLabels": { + "$ref": "#/definitions/labelSet" } } }, @@ -1330,6 +1334,7 @@ func init() { "type": "object", "required": [ "labels", + "routeLabels", "receiver", "alerts" ], @@ -1345,6 +1350,9 @@ func init() { }, "receiver": { "$ref": "#/definitions/receiverReference" + }, + "routeLabels": { + "$ref": "#/definitions/labelSet" } } }, diff --git a/dispatch/dispatch.go b/dispatch/dispatch.go index e0539195f4..661dda85d6 100644 --- a/dispatch/dispatch.go +++ b/dispatch/dispatch.go @@ -315,6 +315,7 @@ func (d *Dispatcher) LoadingDone() <-chan struct{} { type AlertGroup struct { Alerts alert.AlertSlice Labels model.LabelSet + RouteLabels model.LabelSet Receiver string GroupKey string RouteID string @@ -368,10 +369,11 @@ func (d *Dispatcher) Groups(ctx context.Context, routeFilter func(*Route) bool, // Process the snapshot without holding sync.Map locks for _, ag := range snapshot { alertGroup := &AlertGroup{ - Labels: ag.labels, - Receiver: receiver, - GroupKey: ag.GroupKey(), - RouteID: ag.routeID, + Labels: ag.labels, + RouteLabels: ag.RouteLabels(), + Receiver: receiver, + GroupKey: ag.GroupKey(), + RouteID: ag.routeID, } alerts := ag.alerts.List() diff --git a/dispatch/dispatch_test.go b/dispatch/dispatch_test.go index 94e66788d9..08e1bdac99 100644 --- a/dispatch/dispatch_test.go +++ b/dispatch/dispatch_test.go @@ -361,10 +361,13 @@ route: ) // Each group should have pre-computed AlertStatuses. Verify and then - // nil them out so the struct comparison below works. + // nil them out so the struct comparison below works. The routes in this + // test define no labels, so RouteLabels is an empty set; nil it out too. for _, ag := range alertGroups { require.NotNil(t, ag.AlertStatuses) ag.AlertStatuses = nil + require.Empty(t, ag.RouteLabels) + ag.RouteLabels = nil } require.Equal(t, AlertGroups{ From 4a6937106d154f1e6c905baac0500ecd786e9869 Mon Sep 17 00:00:00 2001 From: Guido Trotter Date: Wed, 24 Jun 2026 11:33:49 -0400 Subject: [PATCH 041/120] dispatch: make route label caching lock-free MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the aggrGroup route-label cache's RWMutex + dirty-flag + RLock→Lock upgrade dance with a lock-free scheme: an atomic.Pointer to the rendered labels plus an atomic generation counter. - invalidateRouteLabels() (called by insert() and the resolved-alert deletion in flush(), both on the hot alert-ingestion path) is now a single atomic add on the generation counter. - RouteLabels() loads the cached value, then the generation, and treats the cache as valid only if the value's tagged generation still matches. On a miss it renders and stores the result tagged with the generation it observed before reading the alert list. Correctness rests on two orderings: - Writers mutate ag.alerts before bumping the generation, so a render that races an invalidation is tagged with a now-stale generation and is never served (the next reader sees the mismatch and re-renders). - The reader loads the value before the generation; loading them in the other order could pair an old generation with a value cached for it and wrongly accept a stale render. Concurrent cold readers may render redundantly, which is fine: the only caller is the /alerts/groups API path. The mutex never protected rendering itself — renderRouteLabels only reads immutable or self-synchronized state, as the lock-free render on the notify path in run() already demonstrated. Behavior is unchanged except the previously unobservable "retain last rendered labels once the group is empty" case: RouteLabels() now returns an empty set for an empty group (without rendering, to avoid logging errors from templates that reference .Alerts). Empty groups are excluded from the /alerts/groups response anyway, so this is not observable via the API. Signed-off-by: Guido Trotter --- dispatch/dispatch.go | 138 ++++++++++++++++++++++---------------- dispatch/dispatch_test.go | 18 ++--- 2 files changed, 90 insertions(+), 66 deletions(-) diff --git a/dispatch/dispatch.go b/dispatch/dispatch.go index 661dda85d6..ad8c1ec32a 100644 --- a/dispatch/dispatch.go +++ b/dispatch/dispatch.go @@ -628,13 +628,26 @@ type aggrGroup struct { running atomic.Bool flushIdx uint64 - // mtx guards routeLabels and routeLabelsDirty. - mtx sync.RWMutex - // routeLabels holds the route labels rendered against the group's current - // alerts. They only change when alerts are inserted or deleted, so they are - // rendered lazily: routeLabelsDirty marks when a re-render is needed. - routeLabels model.LabelSet - routeLabelsDirty bool + // routeLabels caches the route labels rendered against the group's current + // alerts, for the /api/v2/alerts/groups read path. It is rendered lazily and + // kept up to date with a generation counter rather than a lock, so that + // invalidation on the hot alert-ingestion path is a single atomic add. + // + // routeLabelsGen is bumped (by invalidateRouteLabels) whenever the alert set + // changes. Each cached render is tagged with the generation it was computed + // at; RouteLabels() treats the cache as valid only if its tag still equals + // the current generation. A render that races an invalidation is therefore + // tagged with a now-stale generation: it may be stored, but the next reader + // sees the mismatch and re-renders, so a stale value is never served. + routeLabels atomic.Pointer[renderedRouteLabels] + routeLabelsGen atomic.Uint64 +} + +// renderedRouteLabels is a cached route-label render tagged with the alert-set +// generation it was computed at. See aggrGroup.routeLabels. +type renderedRouteLabels struct { + gen uint64 + labels model.LabelSet } // newAggrGroup returns a new aggregation group. @@ -651,19 +664,18 @@ func newAggrGroup( to = func(d time.Duration) time.Duration { return d } } ag := &aggrGroup{ - labels: labels, - routeID: r.ID(), - routeKey: r.Key(), - matchers: r.Matchers, - opts: &r.RouteOpts, - timeout: to, - alerts: store.NewAlerts(), - marker: marker.NewAlertMarker(), - recorder: recorder, - tmpl: tmpl, - done: make(chan struct{}), - flushIdx: 1, - routeLabels: model.LabelSet{}, + labels: labels, + routeID: r.ID(), + routeKey: r.Key(), + matchers: r.Matchers, + opts: &r.RouteOpts, + timeout: to, + alerts: store.NewAlerts(), + marker: marker.NewAlertMarker(), + recorder: recorder, + tmpl: tmpl, + done: make(chan struct{}), + flushIdx: 1, } ag.ctx, ag.cancel = context.WithCancel(ctx) @@ -697,7 +709,7 @@ func (ag *aggrGroup) String() string { // allows it to reference group labels, other route labels, etc. func (ag *aggrGroup) renderRouteLabels(alerts ...*alert.Alert) model.LabelSet { if ag.tmpl == nil { - return ag.routeLabels + return model.LabelSet{} } renderedRouteLabels := make(model.LabelSet, len(ag.opts.Labels)) @@ -720,40 +732,51 @@ func (ag *aggrGroup) renderRouteLabels(alerts ...*alert.Alert) model.LabelSet { } // RouteLabels returns the route labels rendered against the group's current -// alerts. Rendering is lazy: it only happens when the set of alerts has changed -// since the last render (tracked by routeLabelsDirty). +// alerts, caching the result. Both the read and the cache update are lock-free. +// The cache is invalidated by invalidateRouteLabels (a generation bump) after +// the alert set changes. +// +// Concurrent callers that all miss may render redundantly; that is acceptable +// because the only caller is the (cold) /alerts/groups API path. What must not +// happen — serving a render that predates an invalidation — is prevented by the +// generation tag. func (ag *aggrGroup) RouteLabels() model.LabelSet { - upgradedLock := false - - ag.mtx.RLock() - - defer func() { - if upgradedLock { - ag.mtx.Unlock() - } else { - ag.mtx.RUnlock() - } - }() - - if ag.routeLabelsDirty { - // Upgrade the lock because we need to update routeLabels. - upgradedLock = true - ag.mtx.RUnlock() - ag.mtx.Lock() - - if ag.routeLabelsDirty { - alerts := ag.alerts.List() - if len(alerts) > 0 { - ag.routeLabels = ag.renderRouteLabels(alerts...) - } - // If alerts is empty (all resolved/deleted), keep the previously - // rendered routeLabels. The group will be garbage-collected on the - // next maintenance cycle. - ag.routeLabelsDirty = false - } + // Load the cached value before the generation: if an invalidation happens + // between these two loads, we read the newer generation and treat the cache + // as a miss, which is safe. (Loading gen first could let us pair an old gen + // with a value cached for that gen and wrongly accept a stale render.) + cached := ag.routeLabels.Load() + gen := ag.routeLabelsGen.Load() + if cached != nil && cached.gen == gen { + return cached.labels + } + + alerts := ag.alerts.List() + + var labels model.LabelSet + if len(alerts) == 0 { + // Nothing to render against: e.g. all alerts resolved and deleted and + // the group is awaiting GC. Rendering route-label templates that + // reference .Alerts against an empty set would only log errors, and + // empty groups are excluded from the API response anyway. + labels = model.LabelSet{} + } else { + labels = ag.renderRouteLabels(alerts...) } - return ag.routeLabels + // Tag the render with the generation we observed before reading the alerts. + // If an invalidation raced this render, gen is already stale, so the next + // reader will see the mismatch and re-render rather than serve stale labels. + ag.routeLabels.Store(&renderedRouteLabels{gen: gen, labels: labels}) + return labels +} + +// invalidateRouteLabels marks the cached route labels stale. It must be called +// after the group's alert set has changed and the change is visible via +// ag.alerts, so that any render still in flight is tagged with a generation +// older than this one and will not be served. +func (ag *aggrGroup) invalidateRouteLabels() { + ag.routeLabelsGen.Add(1) } func (ag *aggrGroup) run(nf notifyFunc) { @@ -861,9 +884,8 @@ func (ag *aggrGroup) insert(ctx context.Context, alert *alert.Alert) bool { ag.recorder.RecordEvent(ctx, func() *eventrecorderpb.EventData { return notify.NewAlertGroupedEvent(ag.alertGroupInfo(), alert) }) - ag.mtx.Lock() - ag.routeLabelsDirty = true - ag.mtx.Unlock() + // The alert set changed; the alert is already visible via ag.alerts. + ag.invalidateRouteLabels() } return true } @@ -915,9 +937,9 @@ func (ag *aggrGroup) flush(notify func(...*alert.Alert) bool) { ag.logger.Error("error on delete alerts", "err", err) } else { if len(resolvedSlice) > 0 { - ag.mtx.Lock() - ag.routeLabelsDirty = true - ag.mtx.Unlock() + // The alert set changed; the deletion is already visible via + // ag.alerts. + ag.invalidateRouteLabels() } // Delete markers for resolved alerts that are not in the store. for _, alert := range resolvedSlice { diff --git a/dispatch/dispatch_test.go b/dispatch/dispatch_test.go index 08e1bdac99..b0e4a7e95f 100644 --- a/dispatch/dispatch_test.go +++ b/dispatch/dispatch_test.go @@ -1279,9 +1279,9 @@ func BenchmarkGetGroupLabels(b *testing.B) { } // TestRouteLabelsAfterAllAlertsResolved verifies that calling RouteLabels() -// after all alerts have been resolved and deleted does not panic or return -// incorrect results. This exercises the race between flush() emptying the -// alerts store and RouteLabels() attempting to re-render. +// after all alerts have been resolved and deleted does not panic. This +// exercises the cache invalidation in flush() racing RouteLabels() re-rendering +// against a now-empty alerts store. func TestRouteLabelsAfterAllAlertsResolved(t *testing.T) { lset := model.LabelSet{"alertname": "test"} opts := &RouteOpts{ @@ -1346,13 +1346,15 @@ func TestRouteLabelsAfterAllAlertsResolved(t *testing.T) { t.Fatal("timed out waiting for resolved flush") } - // After flush deletes resolved alerts, RouteLabels() must not panic and - // should return the last successfully rendered labels. + // After flush deletes resolved alerts, RouteLabels() must not panic when it + // re-renders against the now-empty group. The template references + // .Alerts[0], so with no alerts it renders to an empty value rather than + // erroring out the caller. require.NotPanics(t, func() { rl = ag.RouteLabels() }, "RouteLabels() must not panic after all alerts are deleted") - require.Equal(t, model.LabelValue("test"), rl["description"], - "route label should retain last rendered value after alerts are deleted") + require.Empty(t, rl["description"], + "route label renders empty once the group has no alerts") } // TestRouteLabelsInsertConcurrentWithRouteLabels verifies that concurrent @@ -1381,7 +1383,7 @@ func TestRouteLabelsInsertConcurrentWithRouteLabels(t *testing.T) { ctx := context.Background() // Hammer insert() and RouteLabels() concurrently. Under -race this will - // flag any data race on routeLabels/routeLabelsDirty. + // flag any data race on the routeLabels cache or its generation counter. var wg sync.WaitGroup const goroutines = 10 const iterations = 100 From 57aabd216ffca7e4fb7b55f9f21e082fa4db0e9b Mon Sep 17 00:00:00 2001 From: Guido Trotter Date: Wed, 24 Jun 2026 12:47:39 -0400 Subject: [PATCH 042/120] template: make routeLabels resolution safe against cycles The routeLabels template function rendered a label's value by re-running it through ExecuteTextString, which rebuilt the funcmap (and so re-bound routeLabels) on every recursive reference. This had no cycle protection: a config where two route labels reference each other (or a label references itself) recursed until the goroutine stack overflowed. Replace makeRouteLabelFunc with a per-render routeLabelResolver shared across the whole recursion: - caches each label's final value, so a label referenced multiple times (e.g. via a diamond) is rendered once; - tracks the labels currently being rendered and returns a descriptive "route label cycle detected: a -> b -> a" error instead of recursing forever. ExecuteTextString/ExecuteHTMLString now create a resolver and delegate to private execText/execHTML helpers that thread it through nested renders. Signed-off-by: Guido Trotter --- dispatch/dispatch_test.go | 48 +++++++++++++++++ template/template.go | 106 ++++++++++++++++++++++++++++---------- template/template_test.go | 43 ++++++++++++++++ 3 files changed, 170 insertions(+), 27 deletions(-) diff --git a/dispatch/dispatch_test.go b/dispatch/dispatch_test.go index b0e4a7e95f..2ddbc86639 100644 --- a/dispatch/dispatch_test.go +++ b/dispatch/dispatch_test.go @@ -1417,3 +1417,51 @@ func TestRouteLabelsInsertConcurrentWithRouteLabels(t *testing.T) { wg.Wait() } + +// TestRouteLabelsPerGroupOverride verifies that route label rendering is scoped +// to a single aggregation group: a label that references another label via +// routeLabels resolves against that group's own (possibly overridden) label +// set, with no memoization leaking between groups. +func TestRouteLabelsPerGroupOverride(t *testing.T) { + tmpl, err := template.FromGlobs([]string{}) + require.NoError(t, err) + tmpl.ExternalURL = &url.URL{Scheme: "http", Host: "example.com"} + + // "description" references "team" via routeLabels. The two groups differ + // only in the merged value of "team", as a parent route and a child route + // that overrides it would. + newGroup := func(team string) *aggrGroup { + route := &Route{RouteOpts: RouteOpts{ + Receiver: "r", + GroupBy: map[model.LabelName]struct{}{"alertname": {}}, + Labels: model.LabelSet{ + "team": model.LabelValue(team), + "description": `team is {{ routeLabels "team" }}`, + }, + }} + ag := newAggrGroup(context.Background(), model.LabelSet{"alertname": "x"}, + route, nil, eventrecorder.NopRecorder(), promslog.NewNopLogger(), tmpl) + ag.insert(context.Background(), &alert.Alert{ + Alert: model.Alert{ + Labels: model.LabelSet{"alertname": "x"}, + StartsAt: time.Now(), + EndsAt: time.Now().Add(time.Hour), + }, + UpdatedAt: time.Now(), + }) + return ag + } + + parent := newGroup("A") + child := newGroup("B") + + // Render the child first, then the parent, to make sure neither group's + // resolution is influenced by the other's. + childLabels := child.RouteLabels() + parentLabels := parent.RouteLabels() + + require.Equal(t, model.LabelValue("B"), childLabels["team"]) + require.Equal(t, model.LabelValue("team is B"), childLabels["description"]) + require.Equal(t, model.LabelValue("A"), parentLabels["team"]) + require.Equal(t, model.LabelValue("team is A"), parentLabels["description"]) +} diff --git a/template/template.go b/template/template.go index 84c951c216..40ac8322f4 100644 --- a/template/template.go +++ b/template/template.go @@ -136,28 +136,76 @@ func (t *Template) FromGlob(path string) error { return nil } -// makeRouteLabelFunc returns a routeLabels template function bound to the given -// data and execute function. Looking up a route label renders its value as a -// template, so route label values may themselves reference group labels, other -// route labels, etc. (including recursively). -func makeRouteLabelFunc(data any, executeFunc func(string, any) (string, error)) func(string) (string, error) { - // The data may be passed either by value or by pointer, so handle both. - var castData *Data - if d, ok := data.(Data); ok { - castData = &d - } else if d, ok := data.(*Data); ok { - castData = d - } - return func(name string) (string, error) { - if castData == nil { - return "", nil - } - lv, ok := castData.RouteLabels[name] - if !ok { - return "", nil - } - return executeFunc(lv, data) +// routeLabelsOf extracts the route labels from template data, which may be +// passed either by value or by pointer. +func routeLabelsOf(data any) KV { + switch d := data.(type) { + case *Data: + return d.RouteLabels + case Data: + return d.RouteLabels + default: + return nil + } +} + +// routeLabelResolver backs the routeLabels template function for a single +// top-level render. A route label value may itself be a template that +// references group labels, other route labels, etc., so resolving one can +// recurse. The resolver renders each label at most once (memo) and tracks which +// labels are currently being rendered (inProgress) so that a cyclic reference +// returns a descriptive error instead of recursing until the goroutine stack +// overflows (which is a fatal, unrecoverable runtime error in Go). +type routeLabelResolver struct { + raw KV // raw (possibly templated) label values + data any // data to render label values against + exec func(text string, data any, r *routeLabelResolver) (string, error) + memo map[string]string + inProgress map[string]struct{} + stack []string // labels currently being rendered, for the cycle error +} + +func newRouteLabelResolver(data any, exec func(string, any, *routeLabelResolver) (string, error)) *routeLabelResolver { + // memo and inProgress are allocated lazily on the first resolve() call, so a + // template that never references routeLabels (the common case) pays nothing. + return &routeLabelResolver{ + raw: routeLabelsOf(data), + data: data, + exec: exec, + } +} + +// resolve renders the named route label, recursing through any routeLabels +// references it contains. Unknown labels render to the empty string. +func (r *routeLabelResolver) resolve(name string) (string, error) { + raw, ok := r.raw[name] + if !ok { + return "", nil + } + if v, ok := r.memo[name]; ok { + return v, nil } + if _, busy := r.inProgress[name]; busy { + cycle := strings.Join(append(append([]string{}, r.stack...), name), " -> ") + return "", fmt.Errorf("route label cycle detected: %s", cycle) + } + + if r.memo == nil { + r.memo = map[string]string{} + r.inProgress = map[string]struct{}{} + } + + r.inProgress[name] = struct{}{} + r.stack = append(r.stack, name) + v, err := r.exec(raw, r.data, r) + r.stack = r.stack[:len(r.stack)-1] + delete(r.inProgress, name) + if err != nil { + return "", err + } + + r.memo[name] = v + return v, nil } // ExecuteTextString needs a meaningful doc comment (TODO(fabxc)). @@ -165,14 +213,16 @@ func (t *Template) ExecuteTextString(text string, data any) (string, error) { if text == "" { return "", nil } + return t.execText(text, data, newRouteLabelResolver(data, t.execText)) +} + +func (t *Template) execText(text string, data any, r *routeLabelResolver) (string, error) { tmpl, err := t.text.Clone() if err != nil { return "", err } - funcs := tmpltext.FuncMap{ - "routeLabels": makeRouteLabelFunc(data, t.ExecuteTextString), - } + funcs := tmpltext.FuncMap{"routeLabels": r.resolve} tmpl, err = tmpl.New("").Option("missingkey=zero").Funcs(funcs).Parse(text) if err != nil { @@ -188,14 +238,16 @@ func (t *Template) ExecuteHTMLString(html string, data any) (string, error) { if html == "" { return "", nil } + return t.execHTML(html, data, newRouteLabelResolver(data, t.execHTML)) +} + +func (t *Template) execHTML(html string, data any, r *routeLabelResolver) (string, error) { tmpl, err := t.html.Clone() if err != nil { return "", err } - funcs := tmplhtml.FuncMap{ - "routeLabels": makeRouteLabelFunc(data, t.ExecuteHTMLString), - } + funcs := tmplhtml.FuncMap{"routeLabels": r.resolve} tmpl, err = tmpl.New("").Option("missingkey=zero").Funcs(funcs).Parse(html) if err != nil { diff --git a/template/template_test.go b/template/template_test.go index 13fed03cee..259e961f00 100644 --- a/template/template_test.go +++ b/template/template_test.go @@ -558,6 +558,49 @@ func TestTemplateExpansion(t *testing.T) { }, exp: "Simple: rl1 - Templated: key1 - Recursive: key1 recursive", }, + { + title: "routeLabels unknown name renders empty", + in: `[{{ routeLabels "nope" }}]`, + data: Data{ + RouteLabels: KV{"rl1": "rl1"}, + }, + exp: "[]", + }, + { + title: "routeLabels direct cycle is detected, not a stack overflow", + in: `{{ routeLabels "rl1" }}`, + data: Data{ + RouteLabels: KV{ + "rl1": `{{ routeLabels "rl2" }}`, + "rl2": `{{ routeLabels "rl1" }}`, + }, + }, + fail: true, + }, + { + title: "routeLabels self cycle is detected", + in: `{{ routeLabels "rl1" }}`, + data: Data{ + RouteLabels: KV{ + "rl1": `{{ routeLabels "rl1" }}`, + }, + }, + fail: true, + }, + { + title: "routeLabels diamond reference renders once per branch", + in: `{{ routeLabels "top" }}`, + data: Data{ + GroupLabels: KV{"x": "v"}, + RouteLabels: KV{ + "top": `{{ routeLabels "a" }}-{{ routeLabels "b" }}`, + "a": `{{ routeLabels "leaf" }}`, + "b": `{{ routeLabels "leaf" }}`, + "leaf": `{{ .GroupLabels.x }}`, + }, + }, + exp: "v-v", + }, } { t.Run(tc.title, func(t *testing.T) { f := tmpl.ExecuteTextString From 42447795cafca983a7d73ba8fd40f8a340b21b77 Mon Sep 17 00:00:00 2001 From: Guido Trotter Date: Thu, 25 Jun 2026 06:39:00 -0400 Subject: [PATCH 043/120] notify: don't log error for absent route labels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GetTemplateData logged l.Error("Missing route labels") whenever the keyRouteLabels context value was absent. Unlike the receiver, group labels, and notification reason — which the dispatcher always sets, so their absence indicates a real bug — route labels are legitimately absent: a route may have none configured, and callers such as notifier integration tests build the context without them (e.g. notify/webhook/webhook_test.go). The absent value yields a nil LabelSet, which Data() handles safely (ranging a nil map is zero iterations), so the log was spurious noise that pollutes error-rate monitoring. Drop the error and use the (possibly nil) value directly. Signed-off-by: Guido Trotter --- notify/util.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/notify/util.go b/notify/util.go index a86aabbb2d..9ffd082f80 100644 --- a/notify/util.go +++ b/notify/util.go @@ -204,10 +204,9 @@ func GetTemplateData(ctx context.Context, tmpl *template.Template, alerts []*typ if !ok { l.Error("Missing group labels") } - routeLabels, ok := RouteLabels(ctx) - if !ok { - l.Error("Missing route labels") - } + // Route labels are optional (a route may have none, and some callers omit + // them); absence is not an error and a nil LabelSet is handled downstream. + routeLabels, _ := RouteLabels(ctx) notificationReason, ok := NotificationReason(ctx) if !ok { l.Error("Missing notification reason") From e4d315b36598ccaf18c22c9ef466ff96876ddc9c Mon Sep 17 00:00:00 2001 From: Guido Trotter Date: Thu, 25 Jun 2026 07:06:39 -0400 Subject: [PATCH 044/120] template,notify: don't re-execute already-rendered route labels The dispatcher renders route label values (renderRouteLabels), stores the resulting strings on the context, and GetTemplateData copies them into Data.RouteLabels. When a notification template then calls {{ routeLabels "x" }}, the resolver executed those strings as templates a second time. Signed-off-by: Guido Trotter --- notify/util.go | 5 ++++- template/template.go | 44 +++++++++++++++++++++++++++++---------- template/template_test.go | 36 ++++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+), 12 deletions(-) diff --git a/notify/util.go b/notify/util.go index 9ffd082f80..0f9d83c625 100644 --- a/notify/util.go +++ b/notify/util.go @@ -212,7 +212,10 @@ func GetTemplateData(ctx context.Context, tmpl *template.Template, alerts []*typ l.Error("Missing notification reason") notificationReason = ReasonUnknown } - return tmpl.Data(recv, groupLabels, routeLabels, notificationReason.String(), alerts...) + data := tmpl.Data(recv, groupLabels, routeLabels, notificationReason.String(), alerts...) + // Route labels are pre-rendered by the dispatcher; don't execute them again. + template.MarkRouteLabelsResolved(data) + return data } func readAll(r io.Reader) string { diff --git a/template/template.go b/template/template.go index 40ac8322f4..38267479c6 100644 --- a/template/template.go +++ b/template/template.go @@ -137,15 +137,16 @@ func (t *Template) FromGlob(path string) error { } // routeLabelsOf extracts the route labels from template data, which may be -// passed either by value or by pointer. -func routeLabelsOf(data any) KV { +// passed either by value or by pointer, along with whether those values are +// already rendered (and so must be returned verbatim rather than executed). +func routeLabelsOf(data any) (labels KV, resolved bool) { switch d := data.(type) { case *Data: - return d.RouteLabels + return d.RouteLabels, d.routeLabelsResolved case Data: - return d.RouteLabels + return d.RouteLabels, d.routeLabelsResolved default: - return nil + return nil, false } } @@ -157,8 +158,9 @@ func routeLabelsOf(data any) KV { // returns a descriptive error instead of recursing until the goroutine stack // overflows (which is a fatal, unrecoverable runtime error in Go). type routeLabelResolver struct { - raw KV // raw (possibly templated) label values - data any // data to render label values against + raw KV // raw (possibly templated) label values + resolved bool // if true, raw values are already rendered; return verbatim + data any // data to render label values against exec func(text string, data any, r *routeLabelResolver) (string, error) memo map[string]string inProgress map[string]struct{} @@ -168,20 +170,27 @@ type routeLabelResolver struct { func newRouteLabelResolver(data any, exec func(string, any, *routeLabelResolver) (string, error)) *routeLabelResolver { // memo and inProgress are allocated lazily on the first resolve() call, so a // template that never references routeLabels (the common case) pays nothing. + labels, resolved := routeLabelsOf(data) return &routeLabelResolver{ - raw: routeLabelsOf(data), - data: data, - exec: exec, + raw: labels, + resolved: resolved, + data: data, + exec: exec, } } // resolve renders the named route label, recursing through any routeLabels -// references it contains. Unknown labels render to the empty string. +// references it contains. Unknown labels render to the empty string. If the +// values are already rendered (resolved), they are returned verbatim without a +// second execution. func (r *routeLabelResolver) resolve(name string) (string, error) { raw, ok := r.raw[name] if !ok { return "", nil } + if r.resolved { + return raw, nil + } if v, ok := r.memo[name]; ok { return v, nil } @@ -465,6 +474,19 @@ type Data struct { RouteLabels KV `json:"routeLabels"` ExternalURL string `json:"externalURL"` + + // routeLabelsResolved: if true, routeLabels returns RouteLabels values + // verbatim instead of executing them as templates. Set via + // MarkRouteLabelsResolved. Unexported so it is not reachable from templates + // or serialized into JSON webhooks. + routeLabelsResolved bool +} + +// MarkRouteLabelsResolved marks d.RouteLabels as already-rendered, so the +// routeLabels template function returns the values verbatim rather than +// executing them a second time. +func MarkRouteLabelsResolved(d *Data) { + d.routeLabelsResolved = true } // Alert holds one alert for notification templates. diff --git a/template/template_test.go b/template/template_test.go index 259e961f00..a4f4f5ddec 100644 --- a/template/template_test.go +++ b/template/template_test.go @@ -618,6 +618,42 @@ func TestTemplateExpansion(t *testing.T) { } } +// TestRouteLabelsResolvedNotReExecuted checks that resolved route labels (the +// notification path) are returned verbatim, so a rendered value containing +// template metacharacters like {{ $value }} is not executed a second time. +func TestRouteLabelsResolvedNotReExecuted(t *testing.T) { + tmpl, err := New() + require.NoError(t, err) + + data := Data{ + RouteLabels: KV{ + // An already-rendered value containing template metacharacters. + "desc": `disk usage is {{ $value | humanize }}`, + }, + } + MarkRouteLabelsResolved(&data) + + got, err := tmpl.ExecuteTextString(`[{{ routeLabels "desc" }}]`, data) + require.NoError(t, err) + require.Equal(t, `[disk usage is {{ $value | humanize }}]`, got) +} + +// TestRouteLabelsUnresolvedExecuted checks the dispatch-time default: unresolved +// route label values are executed as templates. +func TestRouteLabelsUnresolvedExecuted(t *testing.T) { + tmpl, err := New() + require.NoError(t, err) + + data := Data{ + GroupLabels: KV{"x": "v"}, + RouteLabels: KV{"desc": `{{ .GroupLabels.x }}`}, + } + + got, err := tmpl.ExecuteTextString(`[{{ routeLabels "desc" }}]`, data) + require.NoError(t, err) + require.Equal(t, `[v]`, got) +} + func TestTemplateExpansionWithOptions(t *testing.T) { testOptionWithAdditionalFuncs := func(funcs FuncMap) Option { return func(text *tmpltext.Template, html *tmplhtml.Template) { From fd4165554f597fc831bb54660541494183f3256a Mon Sep 17 00:00:00 2001 From: Guido Trotter Date: Thu, 25 Jun 2026 07:53:39 -0400 Subject: [PATCH 045/120] dispatch,template: share one resolver across a group's route labels renderRouteLabels rendered each route label with a separate ExecuteTextString call, so every label got its own routeLabelResolver (empty memo) and its own template Clone. When one label cross-referenced another via {{ routeLabels "x" }}, the referenced label was rendered both as part of the referrer and again on its own loop iteration, with a fresh Clone each time. Add Template.RouteLabelRenderer, which returns a render-by-name function backed by a single shared resolver. renderRouteLabels now renders each label through it, so a label and anything it references are rendered at most once per group. Signed-off-by: Guido Trotter --- dispatch/dispatch.go | 9 ++++++--- template/template.go | 10 ++++++++++ template/template_test.go | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 3 deletions(-) diff --git a/dispatch/dispatch.go b/dispatch/dispatch.go index ad8c1ec32a..845c6932de 100644 --- a/dispatch/dispatch.go +++ b/dispatch/dispatch.go @@ -718,13 +718,16 @@ func (ag *aggrGroup) renderRouteLabels(alerts ...*alert.Alert) model.LabelSet { logger := ag.logger.With("data", data) + // Render every label through a single shared resolver so a label that + // cross-references another (via {{ routeLabels "x" }}) reuses the rendered + // value instead of re-rendering it per loop iteration. + render := ag.tmpl.RouteLabelRenderer(data) for label, value := range ag.opts.Labels { - v := string(value) - if rendered, err := ag.tmpl.ExecuteTextString(v, data); err == nil { + if rendered, err := render(string(label)); err == nil { renderedRouteLabels[label] = model.LabelValue(rendered) logger.Debug("rendered route label", "label", label, "value", rendered) } else { - logger.Error("failed to render route label", "label", label, "value", v, "err", err) + logger.Error("failed to render route label", "label", label, "value", string(value), "err", err) } } diff --git a/template/template.go b/template/template.go index 38267479c6..67a5c763ba 100644 --- a/template/template.go +++ b/template/template.go @@ -217,6 +217,16 @@ func (r *routeLabelResolver) resolve(name string) (string, error) { return v, nil } +// RouteLabelRenderer returns a function that renders a route label by name from +// data.RouteLabels. All returned renders share a single resolver, so each label +// — and any label it cross-references via {{ routeLabels "x" }} — is rendered at +// most once per call to RouteLabelRenderer (and cycles are still detected). +// It is used by the dispatch-time expansion of a group's route labels, where +// many labels are rendered against the same data and may reference each other. +func (t *Template) RouteLabelRenderer(data *Data) func(name string) (string, error) { + return newRouteLabelResolver(data, t.execText).resolve +} + // ExecuteTextString needs a meaningful doc comment (TODO(fabxc)). func (t *Template) ExecuteTextString(text string, data any) (string, error) { if text == "" { diff --git a/template/template_test.go b/template/template_test.go index a4f4f5ddec..79017b089f 100644 --- a/template/template_test.go +++ b/template/template_test.go @@ -654,6 +654,42 @@ func TestRouteLabelsUnresolvedExecuted(t *testing.T) { require.Equal(t, `[v]`, got) } +// TestRouteLabelRendererSharesResolver checks that rendering a group's route +// labels through a single RouteLabelRenderer renders each label — and any label +// it cross-references — at most once, rather than re-rendering per label. +func TestRouteLabelRendererSharesResolver(t *testing.T) { + var leafRenders int + countLeaf := func() string { + leafRenders++ + return "leaf" + } + tmpl, err := New(func(text *tmpltext.Template, html *tmplhtml.Template) { + text.Funcs(tmpltext.FuncMap{"countLeaf": countLeaf}) + html.Funcs(tmplhtml.FuncMap{"countLeaf": countLeaf}) + }) + require.NoError(t, err) + + // a and b both reference leaf; leaf calls the counter once when rendered. + data := &Data{ + RouteLabels: KV{ + "a": `{{ routeLabels "leaf" }}`, + "b": `{{ routeLabels "leaf" }}`, + "leaf": `{{ countLeaf }}`, + }, + } + + render := tmpl.RouteLabelRenderer(data) + out := map[string]string{} + for name := range data.RouteLabels { + v, err := render(name) + require.NoError(t, err) + out[name] = v + } + + require.Equal(t, map[string]string{"a": "leaf", "b": "leaf", "leaf": "leaf"}, out) + require.Equal(t, 1, leafRenders, "leaf should be rendered exactly once across all labels") +} + func TestTemplateExpansionWithOptions(t *testing.T) { testOptionWithAdditionalFuncs := func(funcs FuncMap) Option { return func(text *tmpltext.Template, html *tmplhtml.Template) { From d413a08f29c19c6db3fc37917179b0256927e954 Mon Sep 17 00:00:00 2001 From: Guido Trotter Date: Thu, 25 Jun 2026 08:02:12 -0400 Subject: [PATCH 046/120] docs,dispatch: document that route labels have no notification reason The notification reason is per-notification and not known at render time, so renderRouteLabels passes ReasonUnknown and {{ .NotificationReason }} always renders "unknown" in route label templates. Signed-off-by: Guido Trotter --- config/config.go | 4 ++++ dispatch/dispatch.go | 1 + docs/configuration.md | 7 +++++++ 3 files changed, 12 insertions(+) diff --git a/config/config.go b/config/config.go index d49680acb6..62718bc46e 100644 --- a/config/config.go +++ b/config/config.go @@ -893,6 +893,10 @@ type Route struct { GroupInterval *model.Duration `yaml:"group_interval,omitempty" json:"group_interval,omitempty"` RepeatInterval *model.Duration `yaml:"repeat_interval,omitempty" json:"repeat_interval,omitempty"` + // Labels are attached to a route and inherited by child routes. + // Values may be Go templates rendered against the current alert group's + // data. Notification-specific fields such as .NotificationReason are + // not available; use notification templates for reason-dependent content. Labels model.LabelSet `yaml:"labels,omitempty" json:"labels,omitempty"` } diff --git a/dispatch/dispatch.go b/dispatch/dispatch.go index 845c6932de..38785d5196 100644 --- a/dispatch/dispatch.go +++ b/dispatch/dispatch.go @@ -714,6 +714,7 @@ func (ag *aggrGroup) renderRouteLabels(alerts ...*alert.Alert) model.LabelSet { renderedRouteLabels := make(model.LabelSet, len(ag.opts.Labels)) + // The notification reason is not available to route labels and is passed as ReasonUnknown. data := ag.tmpl.Data(ag.opts.Receiver, ag.labels, ag.opts.Labels, notify.ReasonUnknown.String(), alerts...) logger := ag.logger.With("data", data) diff --git a/docs/configuration.md b/docs/configuration.md index a1af3a9ccd..8e26e2618c 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -240,6 +240,13 @@ matchers: # Label values may themselves be templates that are rendered against the # notification data of each alert group (so they can reference group labels, # other route labels, etc.). +# +# Route labels are rendered per alert group, independently of any individual +# notification (they are also exposed via the API, where there is no +# notification at all). Fields that are only meaningful for a specific +# notification are therefore not available: in particular `.NotificationReason` +# is always "unknown" in route label templates. Use notification templates for +# reason-dependent content. labels: [ : , ... ] From bc0efe3e077d3f0016da8eade1b438c679e63ab1 Mon Sep 17 00:00:00 2001 From: Guido Trotter Date: Thu, 25 Jun 2026 08:16:04 -0400 Subject: [PATCH 047/120] dispatch: compute RouteLabels only for groups returned by Groups() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Groups() built the AlertGroup (calling ag.RouteLabels()) before filtering the group's alerts, then discarded the group if no alert passed the filter. RouteLabels() can render templates on a cache miss, so this wasted that work for every filtered-out group — noticeable on large deployments hitting the alerts/groups API with selective filters. Move the AlertGroup construction (and the RouteLabels() call) after the empty-filter check so it only runs for groups that are actually returned. Signed-off-by: Guido Trotter --- dispatch/dispatch.go | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/dispatch/dispatch.go b/dispatch/dispatch.go index 38785d5196..c82a2cbddb 100644 --- a/dispatch/dispatch.go +++ b/dispatch/dispatch.go @@ -368,14 +368,6 @@ func (d *Dispatcher) Groups(ctx context.Context, routeFilter func(*Route) bool, // Process the snapshot without holding sync.Map locks for _, ag := range snapshot { - alertGroup := &AlertGroup{ - Labels: ag.labels, - RouteLabels: ag.RouteLabels(), - Receiver: receiver, - GroupKey: ag.GroupKey(), - RouteID: ag.routeID, - } - alerts := ag.alerts.List() filteredAlerts := make([]*alert.Alert, 0, len(alerts)) for _, a := range alerts { @@ -399,6 +391,17 @@ func (d *Dispatcher) Groups(ctx context.Context, routeFilter func(*Route) bool, if len(filteredAlerts) == 0 { continue } + + // Compute RouteLabels() only now that the group will be returned: + // it may render templates on a cache miss, which is wasted work for + // groups filtered out above. + alertGroup := &AlertGroup{ + Labels: ag.labels, + RouteLabels: ag.RouteLabels(), + Receiver: receiver, + GroupKey: ag.GroupKey(), + RouteID: ag.routeID, + } alertGroup.Alerts = filteredAlerts alertGroup.AlertStatuses = make(map[model.Fingerprint]alert.AlertStatus, len(filteredAlerts)) for _, a := range filteredAlerts { From 9d54da302334d6074a9ee9b85a3f0c6a554c3b54 Mon Sep 17 00:00:00 2001 From: Guido Trotter Date: Thu, 25 Jun 2026 08:18:07 -0400 Subject: [PATCH 048/120] config: validate route label names config.Route.UnmarshalYAML validated matcher and group_by label names but not the keys of the new Labels field, so invalid names (hyphens, digit-prefixed, empty) were accepted and propagated to route opts, templates, and the /api/v2/alerts/groups response. Signed-off-by: Guido Trotter --- config/config.go | 6 ++++++ config/config_test.go | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/config/config.go b/config/config.go index 62718bc46e..80cc5783f9 100644 --- a/config/config.go +++ b/config/config.go @@ -913,6 +913,12 @@ func (r *Route) UnmarshalYAML(unmarshal func(any) error) error { } } + for k := range r.Labels { + if !compat.IsValidLabelName(k) { + return fmt.Errorf("invalid label name %q in route labels", k) + } + } + for _, l := range r.GroupByStr { if l == "..." { r.GroupByAll = true diff --git a/config/config_test.go b/config/config_test.go index 3bc045353b..5487af80f7 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -447,6 +447,41 @@ receivers: } } +func TestRouteLabelsInvalidLabelName(t *testing.T) { + in := ` +route: + receiver: team-X-mails + labels: + "-invalid-": value +receivers: +- name: 'team-X-mails' +` + _, err := Load(in) + + expected := `invalid label name "-invalid-" in route labels` + + if err == nil { + t.Fatalf("no error returned, expected:\n%q", expected) + } + if err.Error() != expected { + t.Errorf("\nexpected:\n%q\ngot:\n%q", expected, err.Error()) + } +} + +func TestRouteLabelsValidLabelName(t *testing.T) { + in := ` +route: + receiver: team-X-mails + labels: + team: team-X + severity: "{{ .GroupLabels.severity }}" +receivers: +- name: 'team-X-mails' +` + _, err := Load(in) + require.NoError(t, err) +} + func TestRootRouteExists(t *testing.T) { in := ` receivers: From 53645d05d7c979554595b0a08d58de6e7d72c952 Mon Sep 17 00:00:00 2001 From: Guido Trotter Date: Thu, 25 Jun 2026 09:29:42 -0400 Subject: [PATCH 049/120] dispatch: skip route-label rendering when a route has no labels renderRouteLabels built template data (tmpl.Data, which clones alerts and computes common labels/annotations, O(n_alerts)) and a resolver even when the route had no labels, then ran an empty loop and returned an empty set. Routes without labels are the common case and this ran on every flush and every RouteLabels() cache miss. Return early when ag.opts.Labels is empty, folding in the existing nil-tmpl guard since both return an empty LabelSet. Signed-off-by: Guido Trotter --- dispatch/dispatch.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dispatch/dispatch.go b/dispatch/dispatch.go index c82a2cbddb..47e45151d7 100644 --- a/dispatch/dispatch.go +++ b/dispatch/dispatch.go @@ -711,7 +711,9 @@ func (ag *aggrGroup) String() string { // alerts and the group's data. A route label value may be a template, so this // allows it to reference group labels, other route labels, etc. func (ag *aggrGroup) renderRouteLabels(alerts ...*alert.Alert) model.LabelSet { - if ag.tmpl == nil { + // Nothing to render when the route has no labels (the common case) or there + // is no template engine. Skip building template data, which is O(n_alerts). + if len(ag.opts.Labels) == 0 || ag.tmpl == nil { return model.LabelSet{} } From 512c880a11f55f8b8582c017c1d652d3f7d3710b Mon Sep 17 00:00:00 2001 From: Guido Trotter Date: Thu, 25 Jun 2026 09:37:28 -0400 Subject: [PATCH 050/120] notify,dispatch: test route labels on the notification path The notification path for route labels was untested: GetTemplateData's route-label extraction and the flush-time WithRouteLabels injection were only exercised by the API read path (ag.RouteLabels()). Add two tests: - notify: GetTemplateData populates Data.RouteLabels from the context, marks them resolved, and the routeLabels template function returns them verbatim (including a value containing {{ $value }}, which must not be re-executed). - dispatch: during run(), the flush puts rendered route labels on the notify context, so notify.RouteLabels(ctx) returns both a static label and a templated one rendered against the flushed batch. Signed-off-by: Guido Trotter --- dispatch/dispatch_test.go | 60 +++++++++++++++++++++++++++++++++++++++ notify/util_test.go | 36 +++++++++++++++++++++++ 2 files changed, 96 insertions(+) diff --git a/dispatch/dispatch_test.go b/dispatch/dispatch_test.go index 2ddbc86639..535b678842 100644 --- a/dispatch/dispatch_test.go +++ b/dispatch/dispatch_test.go @@ -1357,6 +1357,66 @@ func TestRouteLabelsAfterAllAlertsResolved(t *testing.T) { "route label renders empty once the group has no alerts") } +// TestRouteLabelsInNotifyContext verifies that the flush path puts the rendered +// route labels on the notification context, so notify.RouteLabels(ctx) inside +// the notify function returns them rendered against the flushed batch. +func TestRouteLabelsInNotifyContext(t *testing.T) { + lset := model.LabelSet{"alertname": "test"} + opts := &RouteOpts{ + Receiver: "test-receiver", + GroupBy: map[model.LabelName]struct{}{"alertname": {}}, + GroupWait: 10 * time.Millisecond, + GroupInterval: 10 * time.Millisecond, + RepeatInterval: 1 * time.Hour, + Labels: model.LabelSet{ + "team": "ops", + "name": "{{ (index .Alerts 0).Labels.alertname }}", + }, + } + route := &Route{RouteOpts: *opts} + + tmpl, err := template.FromGlobs([]string{}) + require.NoError(t, err) + tmpl.ExternalURL = &url.URL{Scheme: "http", Host: "example.com"} + + ag := newAggrGroup(context.Background(), lset, route, nil, + eventrecorder.NopRecorder(), promslog.NewNopLogger(), tmpl) + + type result struct { + labels model.LabelSet + ok bool + } + resultCh := make(chan result, 1) + ntfy := func(ctx context.Context, alerts ...*alert.Alert) bool { + rl, ok := notify.RouteLabels(ctx) + select { + case resultCh <- result{labels: rl, ok: ok}: + default: + } + return true + } + go ag.run(ntfy) + defer ag.stop() + + ag.insert(context.Background(), &alert.Alert{ + Alert: model.Alert{ + Labels: model.LabelSet{"alertname": "test", "instance": "a"}, + StartsAt: time.Now().Add(-time.Hour), + EndsAt: time.Now().Add(time.Hour), + }, + UpdatedAt: time.Now(), + }) + + select { + case got := <-resultCh: + require.True(t, got.ok, "route labels missing from notify context") + require.Equal(t, model.LabelValue("ops"), got.labels["team"], "static route label") + require.Equal(t, model.LabelValue("test"), got.labels["name"], "templated route label rendered against the batch") + case <-time.After(2 * time.Second): + t.Fatal("timed out waiting for flush") + } +} + // TestRouteLabelsInsertConcurrentWithRouteLabels verifies that concurrent // insert() and RouteLabels() calls don't have race conditions. Run with -race. func TestRouteLabelsInsertConcurrentWithRouteLabels(t *testing.T) { diff --git a/notify/util_test.go b/notify/util_test.go index 032fc51508..93d47d79f5 100644 --- a/notify/util_test.go +++ b/notify/util_test.go @@ -15,15 +15,21 @@ package notify import ( "bytes" + "context" "fmt" "io" "net/http" + "net/url" "path" "reflect" "runtime" "testing" + "github.com/prometheus/common/model" + "github.com/prometheus/common/promslog" "github.com/stretchr/testify/require" + + "github.com/prometheus/alertmanager/template" ) func TestTruncate(t *testing.T) { @@ -216,3 +222,33 @@ func TestRetrierCheck(t *testing.T) { }) } } + +func TestGetTemplateDataWithRouteLabels(t *testing.T) { + tmpl, err := template.New() + require.NoError(t, err) + tmpl.ExternalURL = &url.URL{Scheme: "http", Host: "example.com"} + + // A route label value containing template metacharacters: the dispatcher + // has already rendered route labels, so GetTemplateData must mark them + // resolved and the routeLabels function must return them verbatim rather + // than executing them a second time. + ctx := context.Background() + ctx = WithReceiverName(ctx, "test-receiver") + ctx = WithGroupKey(ctx, "test-key") + ctx = WithGroupLabels(ctx, model.LabelSet{"alertname": "Test"}) + ctx = WithNotificationReason(ctx, ReasonFirstNotification) + ctx = WithRouteLabels(ctx, model.LabelSet{ + "team": "ops", + "desc": "value is {{ $value }}", + }) + + data := GetTemplateData(ctx, tmpl, nil, promslog.NewNopLogger()) + + require.Equal(t, "ops", data.RouteLabels["team"]) + require.Equal(t, "value is {{ $value }}", data.RouteLabels["desc"]) + + // The routeLabels template function returns the values verbatim. + got, err := tmpl.ExecuteTextString(`{{ routeLabels "team" }}|{{ routeLabels "desc" }}`, data) + require.NoError(t, err) + require.Equal(t, "ops|value is {{ $value }}", got) +} From df90f76e7c9a5a6ff12ccbb82309c7678ab86835 Mon Sep 17 00:00:00 2001 From: Guido Trotter Date: Fri, 26 Jun 2026 09:09:05 -0400 Subject: [PATCH 051/120] docs: add route labels examples and template reference add the RouteLabels data field and the routeLabels function to docs/notifications.md, an inheritance/override example in docs/configuration.md, and a standalone doc/examples/route_labels.yml. Signed-off-by: Guido Trotter --- doc/examples/route_labels.yml | 62 +++++++++++++++++++++++++++++++++++ docs/configuration.md | 12 +++++++ docs/notifications.md | 2 ++ 3 files changed, 76 insertions(+) create mode 100644 doc/examples/route_labels.yml diff --git a/doc/examples/route_labels.yml b/doc/examples/route_labels.yml new file mode 100644 index 0000000000..6d35dc69fb --- /dev/null +++ b/doc/examples/route_labels.yml @@ -0,0 +1,62 @@ +# Example showing route `labels`. +# +# Route labels are attached to a route, inherited by child routes, and may be +# overridden per route. They are rendered per alert group and exposed to +# notification templates via the `routeLabels` function (and in the +# `routeLabels` field of the /api/v2/alerts/groups API response). +# +# The pattern shown here: a `description` is composed once at the root from a +# `reason` sub-label. Each subtree overrides only `reason`, computing it from +# the labels that branch matched on. The shared description picks up the new +# reason automatically, so the per-branch routes never restate the surrounding +# wording. + +templates: + - '/etc/alertmanager/template/*.tmpl' + +route: + group_by: ['alertname'] + receiver: default + # `description` is defined once and built from `reason`. Child routes only + # override `reason`; they inherit this description unchanged. + labels: + reason: '{{ .GroupLabels.alertname }}' + description: '{{ .GroupLabels.alertname }} firing ({{ routeLabels "reason" }})' + routes: + # Database alerts are grouped by the affected database, so the reason can be + # computed from that branch's grouping label. + - matchers: + - service="database" + receiver: dba + group_by: ['alertname', 'database'] + labels: + reason: 'database {{ .GroupLabels.database }}' + + # Network link alerts match on both endpoints; the reason names the link. + - matchers: + - link_a_device=~".+" + - link_z_device=~".+" + receiver: network + group_by: ['alertname', 'link_a_device', 'link_z_device'] + labels: + reason: '{{ .GroupLabels.link_a_device }} <-> {{ .GroupLabels.link_z_device }}' + +receivers: + # The webhook payload includes the rendered route labels under "routeLabels". + - name: default + webhook_configs: + - url: 'http://127.0.0.1:5001/' + - name: dba + webhook_configs: + - url: 'http://127.0.0.1:5001/' + - name: network + webhook_configs: + - url: 'http://127.0.0.1:5001/' + +# A notification template just renders the shared description: +# +# {{ define "route_labels.text" }}{{ routeLabels "description" }}{{ end }} +# +# For a generic alert this renders e.g. "HighLatency firing (HighLatency)"; +# for the database branch "DiskFull firing (database orders)"; for the link +# branch "LinkDown firing (switch-a <-> switch-b)" -- only `reason` changed. diff --git a/docs/configuration.md b/docs/configuration.md index 8e26e2618c..a74eb2faf0 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -249,6 +249,18 @@ matchers: # reason-dependent content. labels: [ : , ... ] +# Example: `description` is composed once from a `reason` sub-label. A sub-route +# overrides only `reason`, computing it from the labels that branch matched on, +# and the inherited description picks it up automatically: +# route: +# labels: +# reason: '{{ .GroupLabels.alertname }}' +# description: '{{ .GroupLabels.alertname }} firing ({{ routeLabels "reason" }})' +# routes: +# - matchers: [ service="database" ] +# group_by: [ alertname, database ] +# labels: +# reason: 'database {{ .GroupLabels.database }}' # How long to wait before sending the first notification for a new group of # alerts. Allows to wait for alerts to arrive from other rule groups or diff --git a/docs/notifications.md b/docs/notifications.md index 30a3dc20b9..87b96f1aaa 100644 --- a/docs/notifications.md +++ b/docs/notifications.md @@ -27,6 +27,7 @@ Note that some fields are evaluated as text, and others as HTML which will affec | GroupLabels | [KV](#kv) | The labels these alerts were grouped by. | | CommonLabels | [KV](#kv) | The labels common to all of the alerts. | | CommonAnnotations | [KV](#kv) | Set of common annotations to all of the alerts. Used for longer additional strings of information about the alert. | +| RouteLabels | [KV](#kv) | The route [`labels`](configuration.md#route) attached to this alert group. Usually accessed via the `routeLabels` function below. | | ExternalURL | string | Backlink to the Alertmanager that sent the notification. | The `Alerts` type exposes functions for filtering alerts: @@ -94,6 +95,7 @@ templating. | match | pattern, string | [Regexp.MatchString](https://golang.org/pkg/regexp/#MatchString). Match a string using Regexp. | | now | | [time.Now](https://pkg.go.dev/time#Now), returns the current local time. | | reReplaceAll | pattern, replacement, text | [Regexp.ReplaceAllString](http://golang.org/pkg/regexp/#Regexp.ReplaceAllString) Regexp substitution, unanchored. | +| routeLabels | name string | Returns the value of the named route [label](configuration.md#route) for this alert group, or "" if unset. | | safeHtml | text string | [html/template.HTML](https://golang.org/pkg/html/template/#HTML), Marks string as HTML not requiring auto-escaping. | | safeUrl | text string | [html/template.URL](https://golang.org/pkg/html/template/#URL), Marks string as URL not requiring auto-escaping. | | since | time.Time | [time.Since](https://pkg.go.dev/time#Since), returns the duration of how much time passed from the provided time till the current system time. | From 8153c66a5d07cf50f2a5a9abfc75112086cd9a2c Mon Sep 17 00:00:00 2001 From: Guido Trotter Date: Fri, 26 Jun 2026 09:38:54 -0400 Subject: [PATCH 052/120] Update UIs Signed-off-by: Guido Trotter --- ui/app/src/Data/AlertGroup.elm | 3 +++ ui/app/src/Views/AlertList/Updates.elm | 2 +- ui/app/src/Views/AlertList/Views.elm | 27 ++++++++++++++++++++------ ui/mantine-ui/src/data/groups.ts | 1 + 4 files changed, 26 insertions(+), 7 deletions(-) diff --git a/ui/app/src/Data/AlertGroup.elm b/ui/app/src/Data/AlertGroup.elm index bfbc2af827..ad37f303e7 100644 --- a/ui/app/src/Data/AlertGroup.elm +++ b/ui/app/src/Data/AlertGroup.elm @@ -22,6 +22,7 @@ import Json.Encode as Encode type alias AlertGroup = { labels : Dict String String + , routeLabels : Dict String String , receiver : ReceiverReference , alerts : List GettableAlert } @@ -31,6 +32,7 @@ decoder : Decoder AlertGroup decoder = Decode.succeed AlertGroup |> required "labels" (Decode.dict Decode.string) + |> required "routeLabels" (Decode.dict Decode.string) |> required "receiver" ReceiverReference.decoder |> required "alerts" (Decode.list GettableAlert.decoder) @@ -39,6 +41,7 @@ encoder : AlertGroup -> Encode.Value encoder model = Encode.object [ ( "labels", Encode.dict identity Encode.string model.labels ) + , ( "routeLabels", Encode.dict identity Encode.string model.routeLabels ) , ( "receiver", ReceiverReference.encoder model.receiver ) , ( "alerts", Encode.list GettableAlert.encoder model.alerts ) ] diff --git a/ui/app/src/Views/AlertList/Updates.elm b/ui/app/src/Views/AlertList/Updates.elm index 0ed898ea8f..388f7c9de5 100644 --- a/ui/app/src/Views/AlertList/Updates.elm +++ b/ui/app/src/Views/AlertList/Updates.elm @@ -45,7 +45,7 @@ update msg ({ groupBar, alerts, filterBar, receiverBar, alertGroups } as model) |> Dict.toList |> List.map (\( labels, alerts_ ) -> - AlertGroup (Dict.fromList labels) (ReceiverReference "unknown") alerts_ + AlertGroup (Dict.fromList labels) Dict.empty (ReceiverReference "unknown") alerts_ ) newGroupBar = diff --git a/ui/app/src/Views/AlertList/Views.elm b/ui/app/src/Views/AlertList/Views.elm index e14dd45c57..09f365845b 100644 --- a/ui/app/src/Views/AlertList/Views.elm +++ b/ui/app/src/Views/AlertList/Views.elm @@ -93,25 +93,25 @@ defaultAlertGroups activeId activeGroups expandAll groups = [] -> Utils.Views.error "No alert groups found" - [ { labels, receiver, alerts } ] -> + [ { labels, routeLabels, receiver, alerts } ] -> let labels_ = Dict.toList labels in - alertGroup activeId (Set.singleton 0) receiver labels_ alerts 0 expandAll + alertGroup activeId (Set.singleton 0) receiver labels_ (Dict.toList routeLabels) alerts 0 expandAll _ -> div [ class "pl-5" ] (List.indexedMap (\index group -> - alertGroup activeId activeGroups group.receiver (Dict.toList group.labels) group.alerts index expandAll + alertGroup activeId activeGroups group.receiver (Dict.toList group.labels) (Dict.toList group.routeLabels) group.alerts index expandAll ) groups ) -alertGroup : Maybe String -> Set Int -> ReceiverReference -> Labels -> List GettableAlert -> Int -> Bool -> Html Msg -alertGroup activeId activeGroups receiver labels alerts groupId expandAll = +alertGroup : Maybe String -> Set Int -> ReceiverReference -> Labels -> Labels -> List GettableAlert -> Int -> Bool -> Html Msg +alertGroup activeId activeGroups receiver labels routeLabels alerts groupId expandAll = let groupActive = expandAll || Set.member groupId activeGroups @@ -143,6 +143,21 @@ alertGroup activeId activeGroups receiver labels alerts groupId expandAll = ) labels + routeLabels_ = + List.map + (\( key, value ) -> + span + [ class "btn btn-light text-muted mr-1 mb-1" + , style "user-select" "initial" + , style "-moz-user-select" "initial" + , style "-webkit-user-select" "initial" + , style "border-color" "#adb5bd" + , title "Route label" + ] + [ text (key ++ "=\"" ++ value ++ "\"") ] + ) + routeLabels + expandButton = expandAlertGroup groupActive groupId receiver |> Html.map (\msg -> MsgForAlertList (ActiveGroups msg)) @@ -161,7 +176,7 @@ alertGroup activeId activeGroups receiver labels alerts groupId expandAll = [ span [ class "ml-1 mb-0", style "white-space" "nowrap" ] [ text alertText ] ] in div [] - [ div [ class "mb-3" ] (expandButton :: labels_ ++ alertEl) + [ div [ class "mb-3" ] (expandButton :: labels_ ++ routeLabels_ ++ alertEl) , if groupActive then ul [ class "list-group mb-0" ] (List.map (AlertView.view labels activeId) alerts) diff --git a/ui/mantine-ui/src/data/groups.ts b/ui/mantine-ui/src/data/groups.ts index 20daa50fec..fd7bc43e62 100644 --- a/ui/mantine-ui/src/data/groups.ts +++ b/ui/mantine-ui/src/data/groups.ts @@ -3,6 +3,7 @@ import { useSuspenseAPIQuery } from '@/data/api'; type Group = { alerts: Alert[]; labels: Record; + routeLabels: Record; receiver: Receiver; }; From 0c20c4c6c5a4bee08a962d8f108e20283b7d86a8 Mon Sep 17 00:00:00 2001 From: Guido Trotter Date: Fri, 26 Jun 2026 09:58:38 -0400 Subject: [PATCH 053/120] template: rename routeLabels "resolved" to "rendered" The "resolved" naming was easy to confuse with resolved alerts, even though the flag only tracks whether route label templates have already been rendered. Rename MarkRouteLabelsResolved -> MarkRouteLabelsRendered, the routeLabelsResolved field, and related locals/tests accordingly. Signed-off-by: Guido Trotter --- notify/util.go | 2 +- notify/util_test.go | 2 +- template/template.go | 30 +++++++++++++++--------------- template/template_test.go | 14 +++++++------- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/notify/util.go b/notify/util.go index 0f9d83c625..4007d332be 100644 --- a/notify/util.go +++ b/notify/util.go @@ -214,7 +214,7 @@ func GetTemplateData(ctx context.Context, tmpl *template.Template, alerts []*typ } data := tmpl.Data(recv, groupLabels, routeLabels, notificationReason.String(), alerts...) // Route labels are pre-rendered by the dispatcher; don't execute them again. - template.MarkRouteLabelsResolved(data) + template.MarkRouteLabelsRendered(data) return data } diff --git a/notify/util_test.go b/notify/util_test.go index 93d47d79f5..88b56ec9a7 100644 --- a/notify/util_test.go +++ b/notify/util_test.go @@ -230,7 +230,7 @@ func TestGetTemplateDataWithRouteLabels(t *testing.T) { // A route label value containing template metacharacters: the dispatcher // has already rendered route labels, so GetTemplateData must mark them - // resolved and the routeLabels function must return them verbatim rather + // rendered and the routeLabels function must return them verbatim rather // than executing them a second time. ctx := context.Background() ctx = WithReceiverName(ctx, "test-receiver") diff --git a/template/template.go b/template/template.go index 67a5c763ba..31fbdf6db3 100644 --- a/template/template.go +++ b/template/template.go @@ -139,12 +139,12 @@ func (t *Template) FromGlob(path string) error { // routeLabelsOf extracts the route labels from template data, which may be // passed either by value or by pointer, along with whether those values are // already rendered (and so must be returned verbatim rather than executed). -func routeLabelsOf(data any) (labels KV, resolved bool) { +func routeLabelsOf(data any) (labels KV, rendered bool) { switch d := data.(type) { case *Data: - return d.RouteLabels, d.routeLabelsResolved + return d.RouteLabels, d.routeLabelsRendered case Data: - return d.RouteLabels, d.routeLabelsResolved + return d.RouteLabels, d.routeLabelsRendered default: return nil, false } @@ -159,7 +159,7 @@ func routeLabelsOf(data any) (labels KV, resolved bool) { // overflows (which is a fatal, unrecoverable runtime error in Go). type routeLabelResolver struct { raw KV // raw (possibly templated) label values - resolved bool // if true, raw values are already rendered; return verbatim + rendered bool // if true, raw values are already rendered; return verbatim data any // data to render label values against exec func(text string, data any, r *routeLabelResolver) (string, error) memo map[string]string @@ -170,10 +170,10 @@ type routeLabelResolver struct { func newRouteLabelResolver(data any, exec func(string, any, *routeLabelResolver) (string, error)) *routeLabelResolver { // memo and inProgress are allocated lazily on the first resolve() call, so a // template that never references routeLabels (the common case) pays nothing. - labels, resolved := routeLabelsOf(data) + labels, rendered := routeLabelsOf(data) return &routeLabelResolver{ raw: labels, - resolved: resolved, + rendered: rendered, data: data, exec: exec, } @@ -181,14 +181,14 @@ func newRouteLabelResolver(data any, exec func(string, any, *routeLabelResolver) // resolve renders the named route label, recursing through any routeLabels // references it contains. Unknown labels render to the empty string. If the -// values are already rendered (resolved), they are returned verbatim without a -// second execution. +// values are already rendered, they are returned verbatim without a second +// execution. func (r *routeLabelResolver) resolve(name string) (string, error) { raw, ok := r.raw[name] if !ok { return "", nil } - if r.resolved { + if r.rendered { return raw, nil } if v, ok := r.memo[name]; ok { @@ -485,18 +485,18 @@ type Data struct { ExternalURL string `json:"externalURL"` - // routeLabelsResolved: if true, routeLabels returns RouteLabels values + // routeLabelsRendered: if true, routeLabels returns RouteLabels values // verbatim instead of executing them as templates. Set via - // MarkRouteLabelsResolved. Unexported so it is not reachable from templates + // MarkRouteLabelsRendered. Unexported so it is not reachable from templates // or serialized into JSON webhooks. - routeLabelsResolved bool + routeLabelsRendered bool } -// MarkRouteLabelsResolved marks d.RouteLabels as already-rendered, so the +// MarkRouteLabelsRendered marks d.RouteLabels as already-rendered, so the // routeLabels template function returns the values verbatim rather than // executing them a second time. -func MarkRouteLabelsResolved(d *Data) { - d.routeLabelsResolved = true +func MarkRouteLabelsRendered(d *Data) { + d.routeLabelsRendered = true } // Alert holds one alert for notification templates. diff --git a/template/template_test.go b/template/template_test.go index 79017b089f..d9be165822 100644 --- a/template/template_test.go +++ b/template/template_test.go @@ -618,10 +618,10 @@ func TestTemplateExpansion(t *testing.T) { } } -// TestRouteLabelsResolvedNotReExecuted checks that resolved route labels (the -// notification path) are returned verbatim, so a rendered value containing +// TestRouteLabelsRenderedNotReExecuted checks that already-rendered route labels +// (the notification path) are returned verbatim, so a rendered value containing // template metacharacters like {{ $value }} is not executed a second time. -func TestRouteLabelsResolvedNotReExecuted(t *testing.T) { +func TestRouteLabelsRenderedNotReExecuted(t *testing.T) { tmpl, err := New() require.NoError(t, err) @@ -631,16 +631,16 @@ func TestRouteLabelsResolvedNotReExecuted(t *testing.T) { "desc": `disk usage is {{ $value | humanize }}`, }, } - MarkRouteLabelsResolved(&data) + MarkRouteLabelsRendered(&data) got, err := tmpl.ExecuteTextString(`[{{ routeLabels "desc" }}]`, data) require.NoError(t, err) require.Equal(t, `[disk usage is {{ $value | humanize }}]`, got) } -// TestRouteLabelsUnresolvedExecuted checks the dispatch-time default: unresolved -// route label values are executed as templates. -func TestRouteLabelsUnresolvedExecuted(t *testing.T) { +// TestRouteLabelsUnrenderedExecuted checks the dispatch-time default: +// not-yet-rendered route label values are executed as templates. +func TestRouteLabelsUnrenderedExecuted(t *testing.T) { tmpl, err := New() require.NoError(t, err) From b53425ec1c3e5f02f4d6e2efcf8230f6570c0167 Mon Sep 17 00:00:00 2001 From: mihir-dixit2k27 <143348248+mihir-dixit2k27@users.noreply.github.com> Date: Mon, 29 Jun 2026 22:16:57 +0530 Subject: [PATCH 054/120] template: add toDate and mustToDate functions (#5327) Signed-off-by: Mihir Dixit --- template/template.go | 9 +++++++++ template/template_test.go | 16 ++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/template/template.go b/template/template.go index 31fbdf6db3..0f51864147 100644 --- a/template/template.go +++ b/template/template.go @@ -329,6 +329,15 @@ var DefaultFuncs = FuncMap{ "now": time.Now, "since": time.Since, "humanizeDuration": commonTemplates.HumanizeDuration, + // toDate parses s into a time.Time using the given layout, returning zero time on failure. + "toDate": func(layout, s string) time.Time { + t, _ := time.ParseInLocation(layout, s, time.UTC) + return t + }, + // mustToDate parses s into a time.Time using the given layout, returning an error on failure. + "mustToDate": func(layout, s string) (time.Time, error) { + return time.ParseInLocation(layout, s, time.UTC) + }, "toJson": func(v any) (string, error) { bytes, err := json.Marshal(v) if err != nil { diff --git a/template/template_test.go b/template/template_test.go index d9be165822..aaae0a98bb 100644 --- a/template/template_test.go +++ b/template/template_test.go @@ -866,6 +866,22 @@ func TestTemplateFuncs(t *testing.T) { }, }, exp: `[{"status":"firing","labels":{"alertname":"test"},"annotations":null,"startsAt":"0001-01-01T00:00:00Z","endsAt":"0001-01-01T00:00:00Z","generatorURL":"","fingerprint":""}]`, + }, { + title: "Template using toDate with valid input", + in: `{{ toDate "2006-01-02" "2024-03-15" | date "02 Jan 2006" }}`, + exp: "15 Mar 2024", + }, { + title: "Template using toDate with invalid input returns zero time", + in: `{{ toDate "2006-01-02" "not-a-date" | date "2006" }}`, + exp: "0001", + }, { + title: "Template using mustToDate with valid input", + in: `{{ mustToDate "2006-01-02" "2024-03-15" | date "02 Jan 2006" }}`, + exp: "15 Mar 2024", + }, { + title: "Template using mustToDate with invalid input returns error", + in: `{{ mustToDate "2006-01-02" "not-a-date" }}`, + expErr: `template: :1:3: executing "" at : error calling mustToDate: parsing time "not-a-date" as "2006-01-02": cannot parse "not-a-date" as "2006"`, }} { t.Run(tc.title, func(t *testing.T) { wg := sync.WaitGroup{} From 1dee97b337fbd7af7d2bcd0f154b1a4da2b62e41 Mon Sep 17 00:00:00 2001 From: Siavash Safi Date: Wed, 1 Jul 2026 12:13:00 +0200 Subject: [PATCH 055/120] feat(notify): distinguish auth and rate-limit failure reasons (#5332) Authentication errors (HTTP 401/403) and rate limiting (HTTP 429) previously surfaced as the generic "clientError" reason on the alertmanager_notifications_failed_total metric, making it impossible to tell these apart for PagerDuty and other receivers. Add two new failure reasons, "authError" (401/403) and "rateLimited" (429), and refine the centralized GetFailureReasonFromStatusCode so all HTTP integrations benefit automatically. Also fix the discord and webex integrations, which previously dropped the failure reason entirely. Note: this changes the `reason` label values on alertmanager_notifications_failed_total; dashboards or alerts matching reason="clientError" for 401/403/429 must be updated. Signed-off-by: Siavash Safi --- CHANGELOG.md | 5 ++--- notify/discord/discord.go | 2 +- notify/slack/slack_test.go | 2 +- notify/util.go | 22 +++++++++++++++++++++- notify/util_test.go | 21 +++++++++++++++++++++ notify/webex/webex.go | 2 +- 6 files changed, 47 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a72e4e1cf6..216e215d11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,7 @@ ## main / (unreleased) -* [CHANGE] ... -* [FEATURE] ... -* [ENHANCEMENT] ... +* [CHANGE] notify: The `reason` label on `alertmanager_notifications_failed_total` now distinguishes `authError` (HTTP 401/403) and `rateLimited` (HTTP 429) from the generic `clientError`. Dashboards/alerts matching `reason="clientError"` for these codes must be updated. +* [ENHANCEMENT] notify: The discord and webex integrations now report a failure `reason` on `alertmanager_notifications_failed_total`. * [BUGFIX] webhook: Keep custom `payload` string values verbatim instead of reinterpreting JSON leaves that look like YAML (e.g. values ending with a colon). #5302 ## 0.33.0 / 2026-06-12 diff --git a/notify/discord/discord.go b/notify/discord/discord.go index 485838cf89..2cc6d0170b 100644 --- a/notify/discord/discord.go +++ b/notify/discord/discord.go @@ -178,7 +178,7 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error) shouldRetry, err := n.retrier.Check(resp.StatusCode, resp.Body) if err != nil { - return shouldRetry, err + return shouldRetry, notify.NewErrorWithReason(notify.GetFailureReasonFromStatusCode(resp.StatusCode), err) } return false, nil } diff --git a/notify/slack/slack_test.go b/notify/slack/slack_test.go index 68924f7075..9987f21670 100644 --- a/notify/slack/slack_test.go +++ b/notify/slack/slack_test.go @@ -130,7 +130,7 @@ func TestNotifier_Notify_WithReason(t *testing.T) { { name: "with a 4xx status code", statusCode: http.StatusUnauthorized, - expectedReason: notify.ClientErrorReason, + expectedReason: notify.AuthErrorReason, expectedRetry: false, expectedErr: "unexpected status code 401", }, diff --git a/notify/util.go b/notify/util.go index 4007d332be..fe4c9ea508 100644 --- a/notify/util.go +++ b/notify/util.go @@ -290,6 +290,8 @@ const ( ServerErrorReason ContextCanceledReason ContextDeadlineExceededReason + AuthErrorReason + RateLimitedReason ) func (s Reason) String() string { @@ -304,16 +306,34 @@ func (s Reason) String() string { return "contextCanceled" case ContextDeadlineExceededReason: return "contextDeadlineExceeded" + case AuthErrorReason: + return "authError" + case RateLimitedReason: + return "rateLimited" default: panic(fmt.Sprintf("unknown Reason: %d", s)) } } // possibleFailureReasonCategory is a list of possible failure reason. -var possibleFailureReasonCategory = []string{DefaultReason.String(), ClientErrorReason.String(), ServerErrorReason.String(), ContextCanceledReason.String(), ContextDeadlineExceededReason.String()} +var possibleFailureReasonCategory = []string{ + DefaultReason.String(), + ClientErrorReason.String(), + ServerErrorReason.String(), + ContextCanceledReason.String(), + ContextDeadlineExceededReason.String(), + AuthErrorReason.String(), + RateLimitedReason.String(), +} // GetFailureReasonFromStatusCode returns the reason for the failure based on the status code provided. func GetFailureReasonFromStatusCode(statusCode int) Reason { + switch statusCode { + case http.StatusUnauthorized, http.StatusForbidden: + return AuthErrorReason + case http.StatusTooManyRequests: + return RateLimitedReason + } if statusCode/100 == 4 { return ClientErrorReason } diff --git a/notify/util_test.go b/notify/util_test.go index 88b56ec9a7..2c2d4922e8 100644 --- a/notify/util_test.go +++ b/notify/util_test.go @@ -252,3 +252,24 @@ func TestGetTemplateDataWithRouteLabels(t *testing.T) { require.NoError(t, err) require.Equal(t, "ops|value is {{ $value }}", got) } + +func TestGetFailureReasonFromStatusCode(t *testing.T) { + for _, tc := range []struct { + statusCode int + expected Reason + }{ + {http.StatusUnauthorized, AuthErrorReason}, + {http.StatusForbidden, AuthErrorReason}, + {http.StatusTooManyRequests, RateLimitedReason}, + {http.StatusBadRequest, ClientErrorReason}, + {http.StatusNotFound, ClientErrorReason}, + {http.StatusInternalServerError, ServerErrorReason}, + {http.StatusServiceUnavailable, ServerErrorReason}, + {http.StatusOK, DefaultReason}, + {http.StatusMovedPermanently, DefaultReason}, + } { + t.Run(http.StatusText(tc.statusCode), func(t *testing.T) { + require.Equal(t, tc.expected, GetFailureReasonFromStatusCode(tc.statusCode)) + }) + } +} diff --git a/notify/webex/webex.go b/notify/webex/webex.go index 922ea543d5..949bb614f8 100644 --- a/notify/webex/webex.go +++ b/notify/webex/webex.go @@ -108,7 +108,7 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error) shouldRetry, err := n.retrier.Check(resp.StatusCode, resp.Body) if err != nil { - return shouldRetry, err + return shouldRetry, notify.NewErrorWithReason(notify.GetFailureReasonFromStatusCode(resp.StatusCode), err) } return false, nil From e57c3a9da2a0a8cd1e85146a65fa9387e746e8cf Mon Sep 17 00:00:00 2001 From: Ben Kochie Date: Thu, 2 Jul 2026 21:14:17 +0200 Subject: [PATCH 056/120] Update dependabot config (#5333) * Add groups for additional PR consolidation. * Add exclude paths for synced Prometheus files. * Add no sync marker to avoid syncing dependabot.yml from Prometheus. Signed-off-by: SuperQ --- .github/dependabot.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index c2e9cd26eb..e3d3eaeb16 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,3 +1,6 @@ +### +# Avoid syncing from prometheus/prometheus: no_prometheus_repo_sync +### version: 2 updates: - package-ecosystem: "gomod" @@ -13,6 +16,9 @@ updates: go.opentelemetry.io: patterns: - "go.opentelemetry.io/*" + golang.org-x: + patterns: + - "golang.org/x/*" - package-ecosystem: "docker" directory: "/" schedule: @@ -21,6 +27,20 @@ updates: directory: "/" schedule: interval: "monthly" + groups: + promci: + patterns: + - "prometheus/promci*" + codeql: + patterns: + - "github/codeql-action*" + # Exclude configs synced from upstream prometheus/prometheus. + exclude-paths: + - .github/workflows/container_description.yml + - .github/workflows/golangci-lint.yml + - .github/workflows/govulncheck.yml + - .github/workflows/scorecards.yml + - .github/workflows/stale.yml - package-ecosystem: "npm" directory: "/ui/mantine-ui" open-pull-requests-limit: 20 From a425159219d3a075c0a5f0f5a8fb5e4b2ef887fb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 Jul 2026 14:41:44 +0200 Subject: [PATCH 057/120] build(deps): bump the aws group across 1 directory with 11 updates (#5363) Bumps the aws group with 8 updates in the / directory: | Package | From | To | | --- | --- | --- | | [github.com/aws/aws-sdk-go-v2](https://github.com/aws/aws-sdk-go-v2) | `1.42.0` | `1.42.1` | | [github.com/aws/aws-sdk-go-v2/config](https://github.com/aws/aws-sdk-go-v2) | `1.32.25` | `1.32.27` | | [github.com/aws/aws-sdk-go-v2/service/sns](https://github.com/aws/aws-sdk-go-v2) | `1.40.1` | `1.40.3` | | [github.com/go-openapi/analysis](https://github.com/go-openapi/analysis) | `0.25.2` | `0.25.3` | | [github.com/go-openapi/runtime](https://github.com/go-openapi/runtime) | `0.32.3` | `0.32.4` | | [github.com/go-openapi/runtime/server-middleware](https://github.com/go-openapi/runtime) | `0.32.3` | `0.32.4` | | [github.com/go-openapi/strfmt](https://github.com/go-openapi/strfmt) | `0.26.3` | `0.26.4` | | [github.com/go-openapi/swag](https://github.com/go-openapi/swag) | `0.26.1` | `0.27.0` | Updates `github.com/aws/aws-sdk-go-v2` from 1.42.0 to 1.42.1 - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/v1.42.0...v1.42.1) Updates `github.com/aws/aws-sdk-go-v2/config` from 1.32.25 to 1.32.27 - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/config/v1.32.25...config/v1.32.27) Updates `github.com/aws/aws-sdk-go-v2/credentials` from 1.19.24 to 1.19.26 - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/credentials/v1.19.24...credentials/v1.19.26) Updates `github.com/aws/aws-sdk-go-v2/service/sns` from 1.40.1 to 1.40.3 - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/v1.40.1...service/sns/v1.40.3) Updates `github.com/aws/aws-sdk-go-v2/service/sts` from 1.43.3 to 1.43.5 - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/service/amp/v1.43.3...service/sts/v1.43.5) Updates `github.com/aws/smithy-go` from 1.27.2 to 1.27.3 - [Release notes](https://github.com/aws/smithy-go/releases) - [Changelog](https://github.com/aws/smithy-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws/smithy-go/compare/v1.27.2...v1.27.3) Updates `github.com/go-openapi/analysis` from 0.25.2 to 0.25.3 - [Release notes](https://github.com/go-openapi/analysis/releases) - [Commits](https://github.com/go-openapi/analysis/compare/v0.25.2...v0.25.3) Updates `github.com/go-openapi/runtime` from 0.32.3 to 0.32.4 - [Release notes](https://github.com/go-openapi/runtime/releases) - [Commits](https://github.com/go-openapi/runtime/compare/v0.32.3...v0.32.4) Updates `github.com/go-openapi/runtime/server-middleware` from 0.32.3 to 0.32.4 - [Release notes](https://github.com/go-openapi/runtime/releases) - [Commits](https://github.com/go-openapi/runtime/compare/v0.32.3...v0.32.4) Updates `github.com/go-openapi/strfmt` from 0.26.3 to 0.26.4 - [Release notes](https://github.com/go-openapi/strfmt/releases) - [Commits](https://github.com/go-openapi/strfmt/compare/v0.26.3...v0.26.4) Updates `github.com/go-openapi/swag` from 0.26.1 to 0.27.0 - [Release notes](https://github.com/go-openapi/swag/releases) - [Commits](https://github.com/go-openapi/swag/compare/v0.26.1...v0.27.0) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go-v2 dependency-version: 1.42.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: aws - dependency-name: github.com/aws/aws-sdk-go-v2/config dependency-version: 1.32.27 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: aws - dependency-name: github.com/aws/aws-sdk-go-v2/credentials dependency-version: 1.19.26 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: aws - dependency-name: github.com/aws/aws-sdk-go-v2/service/sns dependency-version: 1.40.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: aws - dependency-name: github.com/aws/aws-sdk-go-v2/service/sts dependency-version: 1.43.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: aws - dependency-name: github.com/aws/smithy-go dependency-version: 1.27.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: aws - dependency-name: github.com/go-openapi/analysis dependency-version: 0.25.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: aws - dependency-name: github.com/go-openapi/runtime dependency-version: 0.32.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: aws - dependency-name: github.com/go-openapi/runtime/server-middleware dependency-version: 0.32.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: aws - dependency-name: github.com/go-openapi/strfmt dependency-version: 0.26.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: aws - dependency-name: github.com/go-openapi/swag dependency-version: 0.27.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: aws ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 74 +++++++++++++------------- go.sum | 160 ++++++++++++++++++++++++++++----------------------------- 2 files changed, 117 insertions(+), 117 deletions(-) diff --git a/go.mod b/go.mod index 28d919115a..e88d46c428 100644 --- a/go.mod +++ b/go.mod @@ -6,26 +6,26 @@ require ( github.com/KimMachineGun/automemlimit v0.7.5 github.com/alecthomas/kingpin/v2 v2.4.0 github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b - github.com/aws/aws-sdk-go-v2 v1.42.0 - github.com/aws/aws-sdk-go-v2/config v1.32.25 - github.com/aws/aws-sdk-go-v2/credentials v1.19.24 - github.com/aws/aws-sdk-go-v2/service/sns v1.40.1 - github.com/aws/aws-sdk-go-v2/service/sts v1.43.3 - github.com/aws/smithy-go v1.27.2 + github.com/aws/aws-sdk-go-v2 v1.42.1 + github.com/aws/aws-sdk-go-v2/config v1.32.27 + github.com/aws/aws-sdk-go-v2/credentials v1.19.26 + github.com/aws/aws-sdk-go-v2/service/sns v1.40.3 + github.com/aws/aws-sdk-go-v2/service/sts v1.43.5 + github.com/aws/smithy-go v1.27.3 github.com/cenkalti/backoff/v4 v4.3.0 github.com/cespare/xxhash/v2 v2.3.0 github.com/coder/quartz v0.3.1 github.com/coreos/go-systemd/v22 v22.7.0 github.com/emersion/go-smtp v0.24.0 github.com/fsnotify/fsnotify v1.10.1 - github.com/go-openapi/analysis v0.25.2 + github.com/go-openapi/analysis v0.25.3 github.com/go-openapi/errors v0.22.8 github.com/go-openapi/loads v0.24.0 - github.com/go-openapi/runtime v0.32.3 - github.com/go-openapi/runtime/server-middleware v0.32.3 + github.com/go-openapi/runtime v0.32.4 + github.com/go-openapi/runtime/server-middleware v0.32.4 github.com/go-openapi/spec v0.22.6 - github.com/go-openapi/strfmt v0.26.3 - github.com/go-openapi/swag v0.26.1 + github.com/go-openapi/strfmt v0.26.4 + github.com/go-openapi/swag v0.27.0 github.com/go-openapi/validate v0.26.0 github.com/google/uuid v1.6.0 github.com/hashicorp/go-sockaddr v1.0.7 @@ -55,8 +55,8 @@ require ( go.opentelemetry.io/otel/sdk v1.44.0 go.opentelemetry.io/otel/trace v1.44.0 golang.org/x/mod v0.36.0 - golang.org/x/net v0.55.0 - golang.org/x/text v0.37.0 + golang.org/x/net v0.56.0 + golang.org/x/text v0.38.0 google.golang.org/grpc v1.81.1 google.golang.org/protobuf v1.36.11 gopkg.in/telebot.v3 v3.3.8 @@ -65,15 +65,15 @@ require ( require ( github.com/armon/go-metrics v0.4.1 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.29 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.29 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.29 // indirect - github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.30 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.12 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.29 // indirect - github.com/aws/aws-sdk-go-v2/service/signin v1.2.0 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.31.3 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.6 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.30 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.30 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.30 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.31 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.13 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.30 // indirect + github.com/aws/aws-sdk-go-v2/service/signin v1.2.2 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.31.5 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.8 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/davecgh/go-spew v1.1.1 // indirect @@ -82,19 +82,19 @@ require ( github.com/felixge/httpsnoop v1.0.4 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-openapi/jsonpointer v0.23.1 // indirect + github.com/go-openapi/jsonpointer v0.24.0 // indirect github.com/go-openapi/jsonreference v0.21.6 // indirect - github.com/go-openapi/swag/cmdutils v0.26.1 // indirect - github.com/go-openapi/swag/conv v0.26.1 // indirect - github.com/go-openapi/swag/fileutils v0.26.1 // indirect + github.com/go-openapi/swag/cmdutils v0.27.0 // indirect + github.com/go-openapi/swag/conv v0.27.0 // indirect + github.com/go-openapi/swag/fileutils v0.27.0 // indirect github.com/go-openapi/swag/jsonname v0.26.1 // indirect - github.com/go-openapi/swag/jsonutils v0.26.1 // indirect - github.com/go-openapi/swag/loading v0.26.1 // indirect - github.com/go-openapi/swag/mangling v0.26.1 // indirect - github.com/go-openapi/swag/netutils v0.26.1 // indirect - github.com/go-openapi/swag/stringutils v0.26.1 // indirect - github.com/go-openapi/swag/typeutils v0.26.1 // indirect - github.com/go-openapi/swag/yamlutils v0.26.1 // indirect + github.com/go-openapi/swag/jsonutils v0.27.0 // indirect + github.com/go-openapi/swag/loading v0.27.0 // indirect + github.com/go-openapi/swag/mangling v0.27.0 // indirect + github.com/go-openapi/swag/netutils v0.27.0 // indirect + github.com/go-openapi/swag/stringutils v0.27.0 // indirect + github.com/go-openapi/swag/typeutils v0.27.0 // indirect + github.com/go-openapi/swag/yamlutils v0.27.0 // indirect github.com/go-viper/mapstructure/v2 v2.5.0 // indirect github.com/golang-jwt/jwt/v5 v5.3.1 // indirect github.com/google/btree v1.1.3 // indirect @@ -125,12 +125,12 @@ require ( go.opentelemetry.io/proto/otlp v1.10.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/crypto v0.51.0 // indirect + golang.org/x/crypto v0.53.0 // indirect golang.org/x/oauth2 v0.36.0 // indirect - golang.org/x/sync v0.20.0 // indirect - golang.org/x/sys v0.45.0 // indirect + golang.org/x/sync v0.21.0 // indirect + golang.org/x/sys v0.46.0 // indirect golang.org/x/time v0.15.0 // indirect - golang.org/x/tools v0.44.0 // indirect + golang.org/x/tools v0.45.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index bc7eb97b82..32471c9e74 100644 --- a/go.sum +++ b/go.sum @@ -77,36 +77,36 @@ github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJ github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/aws/aws-sdk-go-v2 v1.42.0 h1:XvXMJTkFQtpBKIWZnmr9ZEOc2InWM2yldjXEJ/bymhA= -github.com/aws/aws-sdk-go-v2 v1.42.0/go.mod h1:27+ACypSLljLAEKsCYOmrjKh83vuTRkuAe9Uv/3A4bg= -github.com/aws/aws-sdk-go-v2/config v1.32.25 h1:ACCejvStYoilgwrfegSt5ZntCbPrk52qfwyNcnl3omM= -github.com/aws/aws-sdk-go-v2/config v1.32.25/go.mod h1:LJyU8sDRbXUxFn8xMJIGP+v9QYYwveNLI8a/giAOiAs= -github.com/aws/aws-sdk-go-v2/credentials v1.19.24 h1:2hQqYCV9yqyePQ9o6dCrZc/zO8U3TwPr9mIKlZnPu/I= -github.com/aws/aws-sdk-go-v2/credentials v1.19.24/go.mod h1:IDwpACtwqHLISdzfwUUNq4P9DsB/h5BLg4FwJPNfqFY= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.29 h1:r6qZHbT+wxgWO/e9vYNUEtg7lv5+UN3pRqKhLXvnArg= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.29/go.mod h1:QRnaRcTVGKPGRy8w78HMQtKUGRYcnMZAANATkeVA6Mo= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.29 h1:f3vKqSo13fhTYb+JEcXwXefZQE26I1FB5eTSniU67ko= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.29/go.mod h1:MzoLFUArKGpGD+ukmPiTPG1X5x4o6M2kq4v2dr1FiEc= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.29 h1:RdwIf/CuUsvJX3RgJagbOyotl/cxoLY4xviKuE7p2GY= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.29/go.mod h1:71wt8W2EgswdZy9Mf9KNnzxZ3TiZlv4caKghPktDOkA= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.30 h1:VTGy885W5DKBxWRUJbym9hytNaYzsyaPkCHGRRMAOhU= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.30/go.mod h1:AS0HycUvJRFvTt613AYDOgO2jzw+00cVSMny8XB3yMY= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.12 h1:ZD2+BSw9vFsNlKYIasSNt3uDbjqqXIBcM13UJv/Lx2k= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.12/go.mod h1:Ms4zlcVBbXbiP7EVLhl+lgjvA/a7YphqQ3Ih3174EmI= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.29 h1:DRebniUGZ2MqiiIVmQJ04vIXr918hubdHMnarSLEWyU= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.29/go.mod h1:LfRkPCD8YHDM2E5eTkos2UpwYeZnBcVarTa8L59bJHA= -github.com/aws/aws-sdk-go-v2/service/signin v1.2.0 h1:3nXpRcFwRCW8n7HgO2QGy0Dc20eQNfBuUemGQhpF8m8= -github.com/aws/aws-sdk-go-v2/service/signin v1.2.0/go.mod h1:LxYujSTLPRlp2vTtcUO/+1ilrew8ytt6SvQyOgejzFQ= -github.com/aws/aws-sdk-go-v2/service/sns v1.40.1 h1:DLrOlgom0+OYnNaSiVxAXtR6obPjVlbmD+7w5wim9sc= -github.com/aws/aws-sdk-go-v2/service/sns v1.40.1/go.mod h1:V9szvM64GdG5VJUeDRstvLmt/ozgWiSNg3gYnp3mSkk= -github.com/aws/aws-sdk-go-v2/service/sso v1.31.3 h1:ey1XLTYXb9PcLt4535632o5kCGXNXEhNb620Dqwuylo= -github.com/aws/aws-sdk-go-v2/service/sso v1.31.3/go.mod h1:Lk7PlmoTYryQmyBG0EXqj5BcUbj3whXdU2s3yGI3EAc= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.6 h1:yLr03zQE/5Eu5l3QU0Si+xMbLMbSDF2YXsigqXngs6g= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.6/go.mod h1:Q5N6icH+KJZDLh+ESNwzdv6cZ6vLFF/egy3IOxWhmz4= -github.com/aws/aws-sdk-go-v2/service/sts v1.43.3 h1:VrIhKRCSK1umelSgB9RghvA9RTUYeQffyAS5ApXehNI= -github.com/aws/aws-sdk-go-v2/service/sts v1.43.3/go.mod h1:r8wkDOuLaaMFqFiYAb8dGY2A3gJCOujMc6CFOVC4Zhc= -github.com/aws/smithy-go v1.27.2 h1:y9NPmSE6am6LjEFPfqHqG/jJk7AauQvhCJONKh7kpzk= -github.com/aws/smithy-go v1.27.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= +github.com/aws/aws-sdk-go-v2 v1.42.1 h1:9eOTgu1z/dVtYpNZ3/8/XbbaX0x/BqE3HUzAzs6K0ek= +github.com/aws/aws-sdk-go-v2 v1.42.1/go.mod h1:5pKeft2eJj+gElQ38Jqg4ibCqh+/AK33/0X3hip7IjM= +github.com/aws/aws-sdk-go-v2/config v1.32.27 h1:SJwJ9Q4kM7v5QVSYYyXj3znRr6lNyZEhSgAXmXXcVbI= +github.com/aws/aws-sdk-go-v2/config v1.32.27/go.mod h1:uBfrzTRedDmB2u+b6+UlaKJy2O6VSH5un2jP24t/KvQ= +github.com/aws/aws-sdk-go-v2/credentials v1.19.26 h1:Si8kk1kyJnuJWCEgiwpBtTdtgSdR7i611596NnC0YIQ= +github.com/aws/aws-sdk-go-v2/credentials v1.19.26/go.mod h1:lBckz+W9SAdNtSDw3pYgQUJDJFcBBWry0GSzw+bK0TY= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.30 h1:/hi1JADLEW9YYryEz1w4GQu0EtP23pP553Cf9KgsDV4= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.30/go.mod h1:/3AOgy4K17Dm4ucMZVC/MJkzy5kmfKUcINRHZyo0koQ= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.30 h1:xM/Is9cKMHa8Jj8zkvWhvrFkZsXJV9E+BB4g0HW0duQ= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.30/go.mod h1:WueJeNDZvK1fMYEWJIkcivBfEzUkTpBhzlrUKKY8EuA= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.30 h1:jn46zC9LdsVR/ZpMIJqMqb8hHv31BlLx3ulVqNspUOk= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.30/go.mod h1:1hTMsAgbdS/AtUi4bw8+gUuh1pceo+eXRLfpSuSQj3M= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.31 h1:3GUprIsfmGcC5SACIyB0e7E0BM1O1b3Erl5CePYIAeQ= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.31/go.mod h1:7PuV1yl5e2xnUbm+RqvVg5i2iBM8EyijZNoI9wsOoOc= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.13 h1:mbRIur/BiHK6SKPjoBIXSE/hJ6g6JGRLuxQy1jGjlN4= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.13/go.mod h1:ITg9em2KbJx1s0y4aqRX5OYWG6HBZ5TVR//OdpEZ2CQ= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.30 h1:/Z5jmNrKsSD7EmDjzAPsm/3L9IuOkzaynklJZ1qX7S4= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.30/go.mod h1:lEzEZnOosE7zi8Z6royW1cFJTD9fpab4Ul1SBrllewk= +github.com/aws/aws-sdk-go-v2/service/signin v1.2.2 h1:69JEZSDTQ+UNbTWQJCZMmbpQb5sfc79KUt0O7Pyfjmo= +github.com/aws/aws-sdk-go-v2/service/signin v1.2.2/go.mod h1:mxC0nT/C8wMMS97DemZPzvUZxvIt+2Iq+eS3JdFZGgg= +github.com/aws/aws-sdk-go-v2/service/sns v1.40.3 h1:ZgC0JhdV3xY7u0nt2Rg91NY6p3SiAzzV8U3Y10DoEEI= +github.com/aws/aws-sdk-go-v2/service/sns v1.40.3/go.mod h1:5EnTxMpMVeiY0vcjjN/a958FFaHrS6XfXcyRBzDKDCE= +github.com/aws/aws-sdk-go-v2/service/sso v1.31.5 h1:xlK3Tdc8FO7Tq1k0+hL+otF33glj+dE+qeM5iINiDvU= +github.com/aws/aws-sdk-go-v2/service/sso v1.31.5/go.mod h1:u8af9Nqkmqnr96f7v9nHqzZT9XBwbXEkTiqT4ROuJSE= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.8 h1:yX1IbiBfC7SdEgDwIGnRaZyPPDRbQPDOJxl8102PcGk= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.8/go.mod h1:DMPWJBjYs6+3+f/qhBFEFPPlQ6NlhWjai3dJNvipJ84= +github.com/aws/aws-sdk-go-v2/service/sts v1.43.5 h1:T3ANO8QWDbzQD8f4+UaX+fvJlyGnOFMKLbW+NGBHg04= +github.com/aws/aws-sdk-go-v2/service/sts v1.43.5/go.mod h1:9gdl4RrflIdpDb2TlXshWgR1F9TeCkvqDx77Vpr4Z/Q= +github.com/aws/smithy-go v1.27.3 h1:F3Zb497UhhskkfpJmfkXswyo+t0sh9OTBnIHjogWbVY= +github.com/aws/smithy-go v1.27.3/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -188,54 +188,54 @@ github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= 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-openapi/analysis v0.25.2 h1:I0vy4n3alz+DHTiN1PRhCb7QZxkK6g5YmswZKv2TKuw= -github.com/go-openapi/analysis v0.25.2/go.mod h1:Uhs1t/2XR10EnwONYILGEzw8gcfGIG5Xk5K2AxnhqDo= +github.com/go-openapi/analysis v0.25.3 h1:4zlcg85pd2xq3sEgjW887n1IpwCpCqTmqeT6dP9OxDw= +github.com/go-openapi/analysis v0.25.3/go.mod h1:6PEmUIra9/rn6SPstzbrMkhFAsMB2qm7g6E+4DRFyCU= github.com/go-openapi/errors v0.22.8 h1:oP7sW7TWc3wFFjrzzj0nI83H2qMBkNjNfSd+XRejk/I= github.com/go-openapi/errors v0.22.8/go.mod h1:BuUoHcYrU6E7V9gfj1I5wLQqgtIHnup/alXZ8KdgQ0w= -github.com/go-openapi/jsonpointer v0.23.1 h1:1HBACs7XIwR2RcmItfdSFlALhGbe6S92p0ry4d1GWg4= -github.com/go-openapi/jsonpointer v0.23.1/go.mod h1:iWRmZTrGn7XwYhtPt/fvdSFj1OfNBngqRT2UG3BxSqY= +github.com/go-openapi/jsonpointer v0.24.0 h1:AA6mCjHYHmZ+1RU2Js089EaOK/iwXXNwQsTgnsTha2M= +github.com/go-openapi/jsonpointer v0.24.0/go.mod h1:Z3rw7dWu1p9IgitXCFamSlA5lmDiklEB6vkaxcNZW5Y= github.com/go-openapi/jsonreference v0.21.6 h1:NZ5nGfnaM1n4I43Xjm1e5/M2GjOwQwndQz22uhxwD+Y= github.com/go-openapi/jsonreference v0.21.6/go.mod h1:xzbgtQ3ZbWxvET3AxdzCJlJt6vkovbf+IfSPJjD0tUY= github.com/go-openapi/loads v0.24.0 h1:4LLorXRPTzIN9V6ngMUZbAscsBOUBk3Oa8cClu/bFrQ= github.com/go-openapi/loads v0.24.0/go.mod h1:xQMgX+hw5xRAhGrcDXxeMw78IFqUpIzhleu3HqPhyF4= -github.com/go-openapi/runtime v0.32.3 h1:J7Ycy5DJmhhP1By3NifhRUjnkXTrk21qbeqSULjwX8U= -github.com/go-openapi/runtime v0.32.3/go.mod h1:/WTQi0fa5DiGnnCXQKsTkSm15OzJp8Uz3H2t+67TBr4= -github.com/go-openapi/runtime/server-middleware v0.32.3 h1:Y/6h9ix9NCoMG04XazRwX6eA3alh4+JZ6qXdar5yd24= -github.com/go-openapi/runtime/server-middleware v0.32.3/go.mod h1:fYPep4GdTwg/XqZUjR40uIM/8C12Ba5M+MrGCiwpTHo= +github.com/go-openapi/runtime v0.32.4 h1:8ElGj/3goG0itt0nBPP6Cm57ehcYyuHoI3O20nxgvkw= +github.com/go-openapi/runtime v0.32.4/go.mod h1:Bz6keOZw1NX4T6f+m42OoT1MBPDt6Re13dbccHyGH/4= +github.com/go-openapi/runtime/server-middleware v0.32.4 h1:AU6eLMq9CXwh8f6kC1pivtkz+7lfo3TmakMBbUisKME= +github.com/go-openapi/runtime/server-middleware v0.32.4/go.mod h1:fYPep4GdTwg/XqZUjR40uIM/8C12Ba5M+MrGCiwpTHo= github.com/go-openapi/spec v0.22.6 h1:Tyy1pLaNCM8GBCFLoGYLonjJi6zykqyLCjXLc19ZPic= github.com/go-openapi/spec v0.22.6/go.mod h1:HZvTHat+iH0PALQRWhrqIHtU/PEqxqd89fu0MxGlMeM= -github.com/go-openapi/strfmt v0.26.3 h1:rzmslHarJgBbf2qfGge+X3htclQfmXqBZMm0Too0HhU= -github.com/go-openapi/strfmt v0.26.3/go.mod h1:a5nsUw0oRpQzZeOwx8bi6cKbzFZslpbCKt1LEot+KnQ= -github.com/go-openapi/swag v0.26.1 h1:l5sVEyVpwj+DDYeZyo7wQI/Ebn/mKYIyGB/pFwAfGoQ= -github.com/go-openapi/swag v0.26.1/go.mod h1:yNY38BbIVthxbkDtq1UHBCGasBqjakW3lCR6ANzdBEw= -github.com/go-openapi/swag/cmdutils v0.26.1 h1:f2iE1ijYaJ3nuu5PaEMx3zpEhzhZFgivCJObWEObLIQ= -github.com/go-openapi/swag/cmdutils v0.26.1/go.mod h1:Sm1MVFMkF6guJJ+pQqHnQA3N0j9qALV3NxzDSv6bETM= -github.com/go-openapi/swag/conv v0.26.1 h1:slr5FVkg9Wc3Y5zcwenD8Sd/PQ94b2I/QJI7N7KTBpg= -github.com/go-openapi/swag/conv v0.26.1/go.mod h1:mvQXgPptZk9GTrFgGwWvT4q+dN+zQej9JfmGwnipz1A= -github.com/go-openapi/swag/fileutils v0.26.1 h1:K1XCM2CGhfNsc6YDt6v7Q5+1e59rftYWdcu/isZhvFw= -github.com/go-openapi/swag/fileutils v0.26.1/go.mod h1:mYUgxQAKX4ShS3qvvySx+/9yrlUnDhjiD1CalaQl8lQ= +github.com/go-openapi/strfmt v0.26.4 h1:yI6IAEfcWow459BD5UzFY430KUwXZwBHrYusPFkhWlc= +github.com/go-openapi/strfmt v0.26.4/go.mod h1:hNJi6nb5ETD6i7A1yRo03M9S6ZoTPPoWff1iUexmfUc= +github.com/go-openapi/swag v0.27.0 h1:8ecSuZlh4NXc3GsmAOqECIYqDTApCWaMe3gO4gjJNEE= +github.com/go-openapi/swag v0.27.0/go.mod h1:Kkgz9Ht0+ul9/aVdFmc9xSyPzUwf/aFF5KiFPBXfSY0= +github.com/go-openapi/swag/cmdutils v0.27.0 h1:aIKiqhB29AaP+7xm8/CPg3uOpeHx2SUp6TvMpu/a31Y= +github.com/go-openapi/swag/cmdutils v0.27.0/go.mod h1:Sm1MVFMkF6guJJ+pQqHnQA3N0j9qALV3NxzDSv6bETM= +github.com/go-openapi/swag/conv v0.27.0 h1:EKOH4feXrvdo8DbSsXSAqRT8fz1epEnS5O2IfXUOzE8= +github.com/go-openapi/swag/conv v0.27.0/go.mod h1:pfiv0uKQTbaGApk8Zs/lZV3uSjmSpa2FO1y183YngN8= +github.com/go-openapi/swag/fileutils v0.27.0 h1:ib5jMUqGq5tY1EyO4inlrabsaeDAleFU+XD1FXQcgp8= +github.com/go-openapi/swag/fileutils v0.27.0/go.mod h1:VvJFZLTZS0AI854gEQz5tk7dBESdLjiNUMSZ/th2ry8= github.com/go-openapi/swag/jsonname v0.26.1 h1:VReupaV6WxlAsCn0e4DUfgV6bPmINnPpyJDLqSfNPcE= github.com/go-openapi/swag/jsonname v0.26.1/go.mod h1:OvdW6BoWoj33pTfi7x9vFrgmT+fk7aw0BRwvCE0YOuc= -github.com/go-openapi/swag/jsonutils v0.26.1 h1:2hdBfFkHg+7Wrz2VsCbeyR6hzkRDs7AztnMR2u84yOY= -github.com/go-openapi/swag/jsonutils v0.26.1/go.mod h1:U+RMJH3wa+6BRiphuRtIyI8fW9HPFqFQ4sHk2oRx0UQ= -github.com/go-openapi/swag/jsonutils/fixtures_test v0.26.1 h1:1CD7NiLLb/TXl3tOnFYU4b+mNfb5rtgHkaA+q7RMYYQ= -github.com/go-openapi/swag/jsonutils/fixtures_test v0.26.1/go.mod h1:ZWafc8nMdYzTE3uYY6W86f0n46+IF0g4uUyRhJw/kXc= -github.com/go-openapi/swag/loading v0.26.1 h1:E9K4wqXeROlhjFQ13K9zMz6ojFGXIggGe+ad1odrK9w= -github.com/go-openapi/swag/loading v0.26.1/go.mod h1:3qvRIlWzWdq1HvmldwmuJ2ohpcAryN6xVt2OTKd0/7E= -github.com/go-openapi/swag/mangling v0.26.1 h1:gpYI4WuPKFJJVjV5cDLGlDVJhFIxYjQc7yN5eEb4CqM= -github.com/go-openapi/swag/mangling v0.26.1/go.mod h1:POETDH01hqAdASXfw7ISEd9bCOE6xBHOt8NHmGZRmYM= -github.com/go-openapi/swag/netutils v0.26.1 h1:BNctoc39WTAUMxyAs355fExOPzMZtPbZ0ZZ1Am2FR5M= -github.com/go-openapi/swag/netutils v0.26.1/go.mod h1:y02vByhZhQPAVwOX+0KipXFZ/hUbk6G/Enhf5rGaOkQ= -github.com/go-openapi/swag/stringutils v0.26.1 h1:f88uYyTso7TnHrKM/bUBsQ5e2wKf37cpgo6pvbzd9yU= -github.com/go-openapi/swag/stringutils v0.26.1/go.mod h1:Sc6d3bU8fgk5AyZR8/8jEQ+Is/Ald+TD/IIggPN8UJk= -github.com/go-openapi/swag/typeutils v0.26.1 h1:yg42FgMzRR6PVQ3M3qHz1s+Y6/P4HoJ3cBarXa3OVnU= -github.com/go-openapi/swag/typeutils v0.26.1/go.mod h1:VfnV+oUtSP2vCSCn2aJgnr8OevUYemyIzzS1VOzS10o= -github.com/go-openapi/swag/yamlutils v0.26.1 h1:0TSLK+lXs9vfIhAWzBeI/lOzEnIoot6WTCO1aAeWFTk= -github.com/go-openapi/swag/yamlutils v0.26.1/go.mod h1:7W5b7PRX9MxwL7TjeG7H8HkyBGRsIDRObhyMWFgBI2M= -github.com/go-openapi/testify/enable/yaml/v2 v2.5.1 h1:q9NtHwK4qHF7yZziBPvZyv7zWAIk8ok88Gh2mR6Jpc8= -github.com/go-openapi/testify/enable/yaml/v2 v2.5.1/go.mod h1:JW0MXIotCYps/XsgJnG3a8Q7rE5xAiBwoOD5OfaIQBk= -github.com/go-openapi/testify/v2 v2.5.1 h1:TMdhCaw8fUNraVSf3Omoob1dO/AzBfhtFAPW0an6sBo= -github.com/go-openapi/testify/v2 v2.5.1/go.mod h1:SgsVHtfooshd0tublTtJ50FPKhujf47YRqauXXOUxfw= +github.com/go-openapi/swag/jsonutils v0.27.0 h1:VYtd9jEQYeU4j8q5vdn5KWotF4vKywhGdMBrALtAsfE= +github.com/go-openapi/swag/jsonutils v0.27.0/go.mod h1:U7pb8AGuwhok3RDicHeHwSG4L3PXSq6PAL98Aon632g= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.27.0 h1:+d7C7Ur/SsGg/UZ9G0JEovnfRqtMNZCJQGKc2h/ojoE= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.27.0/go.mod h1:mofwUWx70wvskwESqRJ//k/9kURmCgyJl5m5Ppoh5kY= +github.com/go-openapi/swag/loading v0.27.0 h1:s8DA9aPEdFH6OluHUYUn3DnIuoTdyWs9RwffXBUfyeI= +github.com/go-openapi/swag/loading v0.27.0/go.mod h1:VOz+Jg6UGGywcmRvYsI4fvtp+bd7NfioseGEPleYdA4= +github.com/go-openapi/swag/mangling v0.27.0 h1:rpPJuqQHa6z2pDiP3iIpXOyNXlSs9cQCxnJSAxzdfOc= +github.com/go-openapi/swag/mangling v0.27.0/go.mod h1:jtBE2+V+3pILxOR7Vgce+Cwp6A2PgZbvVqfNntbVs0w= +github.com/go-openapi/swag/netutils v0.27.0 h1:lEUG+hHvPvLggB3A8snFk0IRKNf9uC0YKc+7WYqvAF8= +github.com/go-openapi/swag/netutils v0.27.0/go.mod h1:J+WYyFMLtvtCGqa6jLv+YNUmIKI3ZRQRrvfNDMoQoEQ= +github.com/go-openapi/swag/stringutils v0.27.0 h1:Of7w/HljWsNZvuxsUAnw3n+hCOyI6HLJOxW2kQRAxio= +github.com/go-openapi/swag/stringutils v0.27.0/go.mod h1:lzRN95CxXmA03XcDWHLOb6nOMcxCqR5rGY0lOgsfRoM= +github.com/go-openapi/swag/typeutils v0.27.0 h1:aCf4MSGo8NLwZP8Q6t32DWLJSvl/WwNqgmEG+xJ6v2o= +github.com/go-openapi/swag/typeutils v0.27.0/go.mod h1:Srm0xFNRZ1Y+vCxJclo5qzx8aj+1pAKda/YfFPrG0dQ= +github.com/go-openapi/swag/yamlutils v0.27.0 h1:bQ6eAMil5X9tdcf7dMn4t15alzG6jddnrKPuKa/zxKM= +github.com/go-openapi/swag/yamlutils v0.27.0/go.mod h1:yRfIo7qqVkmJRQjX8exjA3AfcI8rH1KDNPsTparoCv4= +github.com/go-openapi/testify/enable/yaml/v2 v2.6.0 h1:gGHwAJ0R/5jU8BEGDbfRNR3hL68dAVi84WuOApp29B0= +github.com/go-openapi/testify/enable/yaml/v2 v2.6.0/go.mod h1:tY+St1SGq4NFl0QIqdTY4aEdbChAHxhyB77XQi9iJCo= +github.com/go-openapi/testify/v2 v2.6.0 h1:5PKH2HE7YJ/LuRPQGvSxBRlFXNQhSetBLlGAgUEu3ug= +github.com/go-openapi/testify/v2 v2.6.0/go.mod h1:SgsVHtfooshd0tublTtJ50FPKhujf47YRqauXXOUxfw= github.com/go-openapi/validate v0.26.0 h1:dxWzQ3F+vb1SajqUxHjwb5T4mTpSHmdrtv5Bi7+ZNhw= github.com/go-openapi/validate v0.26.0/go.mod h1:b4o00uq7fJeJA+wWhVFCJpKTctzeFwzZImGGmHsl2JA= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= @@ -627,8 +627,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI= -golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8= +golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto= +golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio= 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= @@ -713,8 +713,8 @@ golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= 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.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8= -golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww= +golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o= +golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec= 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= @@ -749,8 +749,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220513210516-0976fa681c29/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= -golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM= +golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= 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= @@ -829,8 +829,8 @@ golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= -golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw= +golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= 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/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -842,8 +842,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 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.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= -golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= +golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE= +golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4= 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= @@ -904,8 +904,8 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c= -golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI= +golang.org/x/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8= +golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From 4d8f90e1bd9546d5c82e37f5672a9443ee9af8ba Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 Jul 2026 14:44:08 +0200 Subject: [PATCH 058/120] build(deps-dev): bump the vite group across 1 directory with 2 updates (#5355) Bumps the vite group with 2 updates in the /ui/mantine-ui directory: [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/tree/HEAD/packages/plugin-react) and [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite). Updates `@vitejs/plugin-react` from 6.0.2 to 6.0.3 - [Release notes](https://github.com/vitejs/vite-plugin-react/releases) - [Changelog](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite-plugin-react/commits/plugin-react@6.0.3/packages/plugin-react) Updates `vite` from 8.0.16 to 8.1.3 - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v8.1.3/packages/vite) --- updated-dependencies: - dependency-name: "@vitejs/plugin-react" dependency-version: 6.0.3 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: vite - dependency-name: vite dependency-version: 8.1.2 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: vite ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- ui/mantine-ui/package-lock.json | 204 ++++++++++++++++---------------- ui/mantine-ui/package.json | 4 +- 2 files changed, 104 insertions(+), 104 deletions(-) diff --git a/ui/mantine-ui/package-lock.json b/ui/mantine-ui/package-lock.json index a813f2b18b..898ae64ced 100644 --- a/ui/mantine-ui/package-lock.json +++ b/ui/mantine-ui/package-lock.json @@ -26,14 +26,14 @@ "@types/node": "^25.9.3", "@types/react": "^19.2.16", "@types/react-dom": "^19.2.3", - "@vitejs/plugin-react": "^6.0.2", + "@vitejs/plugin-react": "^6.0.3", "jsdom": "^29.1.1", "postcss": "^8.5.15", "postcss-preset-mantine": "1.18.0", "postcss-simple-vars": "^7.0.1", "prop-types": "^15.8.1", "typescript": "^5.9.3", - "vite": "^8.0.16", + "vite": "^8.1.3", "vite-tsconfig-paths": "^6.1.1", "vitest": "^4.1.8" } @@ -460,21 +460,21 @@ } }, "node_modules/@emnapi/core": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", - "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", + "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@emnapi/wasi-threads": "1.2.1", + "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" } }, "node_modules/@emnapi/runtime": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", - "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", "dev": true, "license": "MIT", "optional": true, @@ -483,9 +483,9 @@ } }, "node_modules/@emnapi/wasi-threads": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", - "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", "dev": true, "license": "MIT", "optional": true, @@ -614,14 +614,14 @@ } }, "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.5.tgz", - "integrity": "sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", + "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@tybys/wasm-util": "^0.10.2" + "@tybys/wasm-util": "^0.10.3" }, "funding": { "type": "github", @@ -633,9 +633,9 @@ } }, "node_modules/@oxc-project/types": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz", - "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==", + "version": "0.138.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.138.0.tgz", + "integrity": "sha512-1a7ZKmrRTCoN1XMZ4L0PyyqrMnrNlLyPuOkdSX2MZg7IiIGRUyurNhAm73ptDOraoBcIordsIGKNPKUzy3ZmfA==", "dev": true, "license": "MIT", "funding": { @@ -643,9 +643,9 @@ } }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.3.tgz", - "integrity": "sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.4.tgz", + "integrity": "sha512-EZLpf/8y7GXkkra90ML47kzik/GMP3EMcE9bPyHmRfxLC6z9+aW5A8poCsoxjrT5GfEcNAAvWwUHjvP1pUQkfw==", "cpu": [ "arm64" ], @@ -660,9 +660,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.3.tgz", - "integrity": "sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.4.tgz", + "integrity": "sha512-aUi+HBvmYb7j8krl1+qJgkG8C17fO79gk3c+jPw4S8glRFc1DTija9S3EyaTSQUm5GJXYKDAsugBEhFHH2vYiQ==", "cpu": [ "arm64" ], @@ -677,9 +677,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.3.tgz", - "integrity": "sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.4.tgz", + "integrity": "sha512-F7hHC3gwY11+vByKPRWqwGbeXWVgKmL+pTGCinaEhdihzBV2aQ0fvZOch9cXYUOKuKKq429HeYXOqQLc7wFCEg==", "cpu": [ "x64" ], @@ -694,9 +694,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.3.tgz", - "integrity": "sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.4.tgz", + "integrity": "sha512-sI5yw+7s92SK6odiEhD5lKCBlWcpjHS5qyqpVQbZAJ0fIzEUXrmbl3DH2ybR3PZogulNJF+COLtmA8hUfvkCCQ==", "cpu": [ "x64" ], @@ -711,9 +711,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.3.tgz", - "integrity": "sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.4.tgz", + "integrity": "sha512-mCi0OKgEieFircrtVYmQAFGszRtMnZ6fpZAXrxanXAu7lqZcsK1E1RAaZNG0uKAnxox3B1f4EyQNnoyMfN1vAA==", "cpu": [ "arm" ], @@ -728,9 +728,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.3.tgz", - "integrity": "sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.4.tgz", + "integrity": "sha512-B9Ial3Kv5sh0SHnB1g/QWcUQCEvCF6QKGAl4zXypYj65mVI+B4AhFBwPtSN7pDrJeIx8Z7zdy4ntx+wQABom7w==", "cpu": [ "arm64" ], @@ -748,9 +748,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.3.tgz", - "integrity": "sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.4.tgz", + "integrity": "sha512-lZVym0PuHE1KZ22gmFTC15lAkrg9iTszR617oYRB/iPY1A56ywoJzVKOJBKaot5RiikCObmur6pogpse3gRcng==", "cpu": [ "arm64" ], @@ -768,9 +768,9 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.3.tgz", - "integrity": "sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.4.tgz", + "integrity": "sha512-t2DNiLJWNTbnEHyUzTumldML6ET4/g16467LZoDDJ3tSxGvguL5/NyC2lCsNKuyRycg9XeDQF5SSv+TNOhQEXg==", "cpu": [ "ppc64" ], @@ -788,9 +788,9 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.3.tgz", - "integrity": "sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.4.tgz", + "integrity": "sha512-0WIRnL1Uw4BvTZRLQt+PVgo6ZKTJadlC2btP+/EOXv2f/DWbY0rEgl+y834mIVwP1FkTlWVTrGGJXf12lru7EQ==", "cpu": [ "s390x" ], @@ -808,9 +808,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.3.tgz", - "integrity": "sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.4.tgz", + "integrity": "sha512-JWtGshGfX+oENAKonoNkqEJX+7hC8yfhi9GUyPX1VX4mdh1y5r+ZiJLR5XzAB0aoP6s/PcILsGjKq8O0mm24bw==", "cpu": [ "x64" ], @@ -828,9 +828,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.3.tgz", - "integrity": "sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.4.tgz", + "integrity": "sha512-rT6yQcxUuXs4CnbofqwHRRV0iem349rLMYpTjkgQGLjrY4ado/eDzwPZPTCgTOlF6Nkp8NEv70yLMTn6qkWxsQ==", "cpu": [ "x64" ], @@ -848,9 +848,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.3.tgz", - "integrity": "sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.4.tgz", + "integrity": "sha512-KXMGoboq5cyaCQjDA4GLuRiOwBQ0EyFnJoVViLeZ45/3rFItRODEr+NdsBcVpll40hhNArlm/speWGRvj08LzA==", "cpu": [ "arm64" ], @@ -865,9 +865,9 @@ } }, "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.3.tgz", - "integrity": "sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.4.tgz", + "integrity": "sha512-5K83rb36oJiY7BCyE9zLZtGcPV4g5wvq+xwdO0XPIwDVZI8cyB/AUjkNXGb92/rnmezEkjMOpgY61rtwjQtFwg==", "cpu": [ "wasm32" ], @@ -875,18 +875,18 @@ "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "1.10.0", - "@emnapi/runtime": "1.10.0", - "@napi-rs/wasm-runtime": "^1.1.4" + "@emnapi/core": "1.11.1", + "@emnapi/runtime": "1.11.1", + "@napi-rs/wasm-runtime": "^1.1.6" }, "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.3.tgz", - "integrity": "sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.4.tgz", + "integrity": "sha512-PnWBtw3TV5KOg69HQQDR0mnQuyCmSGR2pAB4DC1rPF808fgKeTUMj2EOEyKATpgiuxuR5APQmiDO7PDgEjTFSA==", "cpu": [ "arm64" ], @@ -901,9 +901,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.3.tgz", - "integrity": "sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.4.tgz", + "integrity": "sha512-M1lpniBePobTfsa7Ks9a199e1akxsXn+GYBUKsEzv3YFzOm1HJAMNwKI3qr0Zq+mxwx9gOZoTdP1yXRYsZUocQ==", "cpu": [ "x64" ], @@ -1057,9 +1057,9 @@ } }, "node_modules/@tybys/wasm-util": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", - "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", "dev": true, "license": "MIT", "optional": true, @@ -1130,13 +1130,13 @@ } }, "node_modules/@vitejs/plugin-react": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.2.tgz", - "integrity": "sha512-DlSMqo4WhThw4vB8Mpn0Woe9J+Jfq1geJ61AKW0QEgLzGMNwtIMdxbDUzLxcun8W7NbJO0e2Jg/Nxm3cCSVzzg==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.3.tgz", + "integrity": "sha512-vmFvco5/QuC2f9Oj+wTk0+9XeDFkHxSamwZKYc7MxYwKICfvUvlMhqKI0VuICPltGqh1neqBKDvO4kes1ya8vg==", "dev": true, "license": "MIT", "dependencies": { - "@rolldown/pluginutils": "^1.0.0" + "@rolldown/pluginutils": "^1.0.1" }, "engines": { "node": "^20.19.0 || >=22.12.0" @@ -2024,9 +2024,9 @@ "license": "ISC" }, "node_modules/postcss": { - "version": "8.5.15", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", - "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "version": "8.5.16", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.16.tgz", + "integrity": "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==", "dev": true, "funding": [ { @@ -2405,13 +2405,13 @@ } }, "node_modules/rolldown": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.3.tgz", - "integrity": "sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.4.tgz", + "integrity": "sha512-IjZYiLxZwpnhwhdBH2ugdTGVSdhCQUmLxLoqyjiL0JxYjyRst+5a0P3xfrTxJ5F638j4Mvvw5FAX5XE6eHpXbA==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.133.0", + "@oxc-project/types": "=0.138.0", "@rolldown/pluginutils": "^1.0.0" }, "bin": { @@ -2421,21 +2421,21 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.3", - "@rolldown/binding-darwin-arm64": "1.0.3", - "@rolldown/binding-darwin-x64": "1.0.3", - "@rolldown/binding-freebsd-x64": "1.0.3", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.3", - "@rolldown/binding-linux-arm64-gnu": "1.0.3", - "@rolldown/binding-linux-arm64-musl": "1.0.3", - "@rolldown/binding-linux-ppc64-gnu": "1.0.3", - "@rolldown/binding-linux-s390x-gnu": "1.0.3", - "@rolldown/binding-linux-x64-gnu": "1.0.3", - "@rolldown/binding-linux-x64-musl": "1.0.3", - "@rolldown/binding-openharmony-arm64": "1.0.3", - "@rolldown/binding-wasm32-wasi": "1.0.3", - "@rolldown/binding-win32-arm64-msvc": "1.0.3", - "@rolldown/binding-win32-x64-msvc": "1.0.3" + "@rolldown/binding-android-arm64": "1.1.4", + "@rolldown/binding-darwin-arm64": "1.1.4", + "@rolldown/binding-darwin-x64": "1.1.4", + "@rolldown/binding-freebsd-x64": "1.1.4", + "@rolldown/binding-linux-arm-gnueabihf": "1.1.4", + "@rolldown/binding-linux-arm64-gnu": "1.1.4", + "@rolldown/binding-linux-arm64-musl": "1.1.4", + "@rolldown/binding-linux-ppc64-gnu": "1.1.4", + "@rolldown/binding-linux-s390x-gnu": "1.1.4", + "@rolldown/binding-linux-x64-gnu": "1.1.4", + "@rolldown/binding-linux-x64-musl": "1.1.4", + "@rolldown/binding-openharmony-arm64": "1.1.4", + "@rolldown/binding-wasm32-wasi": "1.1.4", + "@rolldown/binding-win32-arm64-msvc": "1.1.4", + "@rolldown/binding-win32-x64-msvc": "1.1.4" } }, "node_modules/saxes": { @@ -2800,16 +2800,16 @@ "license": "MIT" }, "node_modules/vite": { - "version": "8.0.16", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.16.tgz", - "integrity": "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==", + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.3.tgz", + "integrity": "sha512-Ds+gBRbj0lwRO2Y5hwnUBdxSwlAve9LeRyU4sNnAr0ewW0gWF0n5bgXgUzbgZ49MV9BVUAQUFYVcDUcilUExMA==", "dev": true, "license": "MIT", "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", - "postcss": "^8.5.15", - "rolldown": "1.0.3", + "postcss": "^8.5.16", + "rolldown": "~1.1.3", "tinyglobby": "^0.2.17" }, "bin": { @@ -2826,7 +2826,7 @@ }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.1.18", + "@vitejs/devtools": "^0.3.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", diff --git a/ui/mantine-ui/package.json b/ui/mantine-ui/package.json index ce23f3c678..812e6de8c2 100644 --- a/ui/mantine-ui/package.json +++ b/ui/mantine-ui/package.json @@ -36,14 +36,14 @@ "@types/node": "^25.9.3", "@types/react": "^19.2.16", "@types/react-dom": "^19.2.3", - "@vitejs/plugin-react": "^6.0.2", + "@vitejs/plugin-react": "^6.0.3", "jsdom": "^29.1.1", "postcss": "^8.5.15", "postcss-preset-mantine": "1.18.0", "postcss-simple-vars": "^7.0.1", "prop-types": "^15.8.1", "typescript": "^5.9.3", - "vite": "^8.0.16", + "vite": "^8.1.3", "vite-tsconfig-paths": "^6.1.1", "vitest": "^4.1.8" } From d81e146699874a8d869059caa99c38be106d916f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 Jul 2026 14:44:45 +0200 Subject: [PATCH 059/120] build(deps): bump github.com/prometheus/common from 0.68.1 to 0.69.0 (#5352) Bumps [github.com/prometheus/common](https://github.com/prometheus/common) from 0.68.1 to 0.69.0. - [Release notes](https://github.com/prometheus/common/releases) - [Changelog](https://github.com/prometheus/common/blob/main/CHANGELOG.md) - [Commits](https://github.com/prometheus/common/compare/v0.68.1...v0.69.0) --- updated-dependencies: - dependency-name: github.com/prometheus/common dependency-version: 0.69.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index e88d46c428..ae2f74c120 100644 --- a/go.mod +++ b/go.mod @@ -37,7 +37,7 @@ require ( github.com/oklog/ulid/v2 v2.1.1 github.com/prometheus/client_golang v1.23.2 github.com/prometheus/client_model v0.6.2 - github.com/prometheus/common v0.68.1 + github.com/prometheus/common v0.69.0 github.com/prometheus/exporter-toolkit v0.16.0 github.com/prometheus/sigv4 v0.4.1 github.com/rs/cors v1.11.1 diff --git a/go.sum b/go.sum index 32471c9e74..79ed637f5e 100644 --- a/go.sum +++ b/go.sum @@ -500,8 +500,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.68.1 h1:omjRRl4QP4komogpXuhfeOiisQg7xdy8VM1UY+pStaY= -github.com/prometheus/common v0.68.1/go.mod h1:ZzL3f6u94qUxh9p+tJTrF+FvBS1XXbbRAZCQkytAL0Y= +github.com/prometheus/common v0.69.0 h1:OA85nJQS/T/MaYh/Q2CcgDKSGWqNIgrBDvDH85CuiNk= +github.com/prometheus/common v0.69.0/go.mod h1:ZzL3f6u94qUxh9p+tJTrF+FvBS1XXbbRAZCQkytAL0Y= github.com/prometheus/exporter-toolkit v0.16.0 h1:xT/j7L2XKF+VJd6B4fpUw6xWabHrSmsUf6mYmFqyu0s= github.com/prometheus/exporter-toolkit v0.16.0/go.mod h1:d1EL8Z9674xQe/iWhwP2wDyCEoBPbXVeqDbqAUsgJWY= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= From 264b0390681af3c9466a290fec7af2beef7c4730 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 Jul 2026 14:45:24 +0200 Subject: [PATCH 060/120] build(deps): bump github.com/mdlayher/vsock from 1.2.1 to 1.3.0 (#5349) Bumps [github.com/mdlayher/vsock](https://github.com/mdlayher/vsock) from 1.2.1 to 1.3.0. - [Release notes](https://github.com/mdlayher/vsock/releases) - [Changelog](https://github.com/mdlayher/vsock/blob/main/CHANGELOG.md) - [Commits](https://github.com/mdlayher/vsock/compare/v1.2.1...v1.3.0) --- updated-dependencies: - dependency-name: github.com/mdlayher/vsock dependency-version: 1.3.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index ae2f74c120..680df9764f 100644 --- a/go.mod +++ b/go.mod @@ -32,7 +32,7 @@ require ( github.com/hashicorp/golang-lru/v2 v2.0.7 github.com/hashicorp/memberlist v0.5.4 github.com/jessevdk/go-flags v1.6.1 - github.com/mdlayher/vsock v1.2.1 + github.com/mdlayher/vsock v1.3.0 github.com/oklog/run v1.2.0 github.com/oklog/ulid/v2 v2.1.1 github.com/prometheus/client_golang v1.23.2 @@ -109,7 +109,7 @@ require ( github.com/julienschmidt/httprouter v1.3.0 // indirect github.com/klauspost/compress v1.18.6 // indirect github.com/kylelemons/godebug v1.1.0 // indirect - github.com/mdlayher/socket v0.4.1 // indirect + github.com/mdlayher/socket v0.6.0 // indirect github.com/miekg/dns v1.1.68 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect diff --git a/go.sum b/go.sum index 79ed637f5e..9d63a3a38c 100644 --- a/go.sum +++ b/go.sum @@ -436,10 +436,10 @@ github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOA github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U= -github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= -github.com/mdlayher/vsock v1.2.1 h1:pC1mTJTvjo1r9n9fbm7S1j04rCgCzhCOS5DY0zqHlnQ= -github.com/mdlayher/vsock v1.2.1/go.mod h1:NRfCibel++DgeMD8z/hP+PPTjlNJsdPOmxcnENvE+SE= +github.com/mdlayher/socket v0.6.0 h1:ScZPaAGyO1icQnbFrhPM8mnXyMu9qukC1K4ZoM2IQKU= +github.com/mdlayher/socket v0.6.0/go.mod h1:q7vozUAnxSqnjHc12Fik5yUKIzfZ8ITCfMkhOtE9z18= +github.com/mdlayher/vsock v1.3.0 h1:bqQfZ1OznI03y6YiXp2sze05RVdzLn/zsfjnjd4+ivI= +github.com/mdlayher/vsock v1.3.0/go.mod h1:WsuksavOvwCnV5UqGHUkvAvCy+Dqy81y4goKQTzxxNY= 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= github.com/miekg/dns v1.1.68 h1:jsSRkNozw7G/mnmXULynzMNIsgY2dHC8LO6U6Ij2JEA= From 62af97a61f1efc75575959f279f1b3a10502a080 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 Jul 2026 14:46:08 +0200 Subject: [PATCH 061/120] build(deps): bump the mantine group across 1 directory with 3 updates (#5339) Bumps the mantine group with 1 update in the /ui/mantine-ui directory: [@mantine/code-highlight](https://github.com/mantinedev/mantine/tree/HEAD/packages/@mantine/code-highlight). Updates `@mantine/code-highlight` from 9.3.0 to 9.4.1 - [Release notes](https://github.com/mantinedev/mantine/releases) - [Changelog](https://github.com/mantinedev/mantine/blob/master/CHANGELOG.md) - [Commits](https://github.com/mantinedev/mantine/commits/9.4.1/packages/@mantine/code-highlight) Updates `@mantine/core` from 9.3.0 to 9.4.1 - [Release notes](https://github.com/mantinedev/mantine/releases) - [Changelog](https://github.com/mantinedev/mantine/blob/master/CHANGELOG.md) - [Commits](https://github.com/mantinedev/mantine/commits/9.4.1/packages/@mantine/core) Updates `@mantine/hooks` from 9.3.0 to 9.4.1 - [Release notes](https://github.com/mantinedev/mantine/releases) - [Changelog](https://github.com/mantinedev/mantine/blob/master/CHANGELOG.md) - [Commits](https://github.com/mantinedev/mantine/commits/9.4.1/packages/@mantine/hooks) --- updated-dependencies: - dependency-name: "@mantine/code-highlight" dependency-version: 9.4.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: mantine - dependency-name: "@mantine/core" dependency-version: 9.4.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: mantine - dependency-name: "@mantine/hooks" dependency-version: 9.4.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: mantine ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- ui/mantine-ui/package-lock.json | 34 ++++++++++++++++----------------- ui/mantine-ui/package.json | 2 +- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/ui/mantine-ui/package-lock.json b/ui/mantine-ui/package-lock.json index 898ae64ced..a49540e926 100644 --- a/ui/mantine-ui/package-lock.json +++ b/ui/mantine-ui/package-lock.json @@ -8,7 +8,7 @@ "name": "alertmanager", "version": "0.0.0", "dependencies": { - "@mantine/code-highlight": "^9.3.0", + "@mantine/code-highlight": "^9.4.1", "@mantine/core": "^9.0.0", "@mantine/hooks": "^9.0.0", "@tanstack/react-query": "^5.101.0", @@ -572,42 +572,42 @@ "license": "MIT" }, "node_modules/@mantine/code-highlight": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@mantine/code-highlight/-/code-highlight-9.3.0.tgz", - "integrity": "sha512-fS/2Hzyj63PMZgq1H2JLYZR2Cr0hvq6Ax/IjgGrEMSeWamzsAB2DKUZvAkdTH3KYzVUs+pxDktF1ZW3kf1oSng==", + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/@mantine/code-highlight/-/code-highlight-9.4.1.tgz", + "integrity": "sha512-C6cMFf2LV+2r/oyn9hVhlMaeWWQ3vIV7zOti2kjdrXOU3ZQkE88c2tknFw8BLnZjO7teY2s+tL2HVDGOht1Y+Q==", "license": "MIT", "dependencies": { "clsx": "^2.1.1" }, "peerDependencies": { - "@mantine/core": "9.3.0", - "@mantine/hooks": "9.3.0", + "@mantine/core": "9.4.1", + "@mantine/hooks": "9.4.1", "react": "^19.2.0", "react-dom": "^19.2.0" } }, "node_modules/@mantine/core": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@mantine/core/-/core-9.3.0.tgz", - "integrity": "sha512-mHVCm61YVW9ipy9eHiKMqsRUm3TkOErbdw7zHs0HRw5g403nf7tSTqNGvaYE+aX1Py874qMkrUzeQfj4bjiiBA==", + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/@mantine/core/-/core-9.4.1.tgz", + "integrity": "sha512-lZWEICrum4+vwKxzh/mk4RB1N6BqZ0Cshdl6lhm7OYLiQzmOaxKtOgyaWz+vr65G8mqTXjalZalB+wmMVBYK2Q==", "license": "MIT", "dependencies": { "@floating-ui/react": "^0.27.19", "clsx": "^2.1.1", "react-number-format": "^5.4.5", "react-remove-scroll": "^2.7.2", - "type-fest": "^5.6.0" + "type-fest": "^5.7.0" }, "peerDependencies": { - "@mantine/hooks": "9.3.0", + "@mantine/hooks": "9.4.1", "react": "^19.2.0", "react-dom": "^19.2.0" } }, "node_modules/@mantine/hooks": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@mantine/hooks/-/hooks-9.3.0.tgz", - "integrity": "sha512-QoSr9WI4WsKWrM3qFYYizHUn3+n+CVcFMYe4sdlnmFPStvs6BacPODKJSbFlYl73Z20t82JIy0eKqt4noHQI2g==", + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/@mantine/hooks/-/hooks-9.4.1.tgz", + "integrity": "sha512-eTI8wmzPx3r98zgKIEuvukmoGTHBhmtI6+9E6o2DbTmEU2eM1bCdjE2vFdf0op2AlRO0KEYEcZhNQi4T/SJk0A==", "license": "MIT", "peerDependencies": { "react": "^19.2.0" @@ -2704,9 +2704,9 @@ "license": "0BSD" }, "node_modules/type-fest": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.6.0.tgz", - "integrity": "sha512-8ZiHFm91orbSAe2PSAiSVBVko18pbhbiB3U9GglSzF/zCGkR+rxpHx6sEMCUm4kxY4LjDIUGgCfUMtwfZfjfUA==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.7.0.tgz", + "integrity": "sha512-1URUxUqfHFM1c+zfSPsa3gnkO7Aq21qyH75SIduNYz4SzY964rn1X2vCMQaHSHhktiw+0kPa2iyb6PUpXqB6Vg==", "license": "(MIT OR CC0-1.0)", "dependencies": { "tagged-tag": "^1.0.0" diff --git a/ui/mantine-ui/package.json b/ui/mantine-ui/package.json index 812e6de8c2..9185753c4b 100644 --- a/ui/mantine-ui/package.json +++ b/ui/mantine-ui/package.json @@ -18,7 +18,7 @@ "test": "npm run typecheck && npm run check && npm run vitest && npm run build" }, "dependencies": { - "@mantine/code-highlight": "^9.3.0", + "@mantine/code-highlight": "^9.4.1", "@mantine/core": "^9.0.0", "@mantine/hooks": "^9.0.0", "@tanstack/react-query": "^5.101.0", From c533955069f3100a8841b8a8bb2fbc9074ea27d0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 Jul 2026 14:51:50 +0200 Subject: [PATCH 062/120] build(deps): bump golang.org/x/mod (#5368) Bumps the golang-org-x group with 1 update in the / directory: [golang.org/x/mod](https://github.com/golang/mod). Updates `golang.org/x/mod` from 0.36.0 to 0.37.0 - [Commits](https://github.com/golang/mod/compare/v0.36.0...v0.37.0) --- updated-dependencies: - dependency-name: golang.org/x/mod dependency-version: 0.37.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: golang-org-x ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 680df9764f..7aa7611c21 100644 --- a/go.mod +++ b/go.mod @@ -54,7 +54,7 @@ require ( go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.44.0 go.opentelemetry.io/otel/sdk v1.44.0 go.opentelemetry.io/otel/trace v1.44.0 - golang.org/x/mod v0.36.0 + golang.org/x/mod v0.37.0 golang.org/x/net v0.56.0 golang.org/x/text v0.38.0 google.golang.org/grpc v1.81.1 diff --git a/go.sum b/go.sum index 9d63a3a38c..c91b95c06e 100644 --- a/go.sum +++ b/go.sum @@ -664,8 +664,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4= -golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ= +golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ= +golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= From f58ca2ee9e11d592c341f79101f7b3b2f4ee7e9f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 Jul 2026 14:52:32 +0200 Subject: [PATCH 063/120] build(deps-dev): bump @types/node in /ui/mantine-ui (#5354) Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 25.9.3 to 26.1.0. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) --- updated-dependencies: - dependency-name: "@types/node" dependency-version: 26.1.0 dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- ui/mantine-ui/package-lock.json | 16 ++++++++-------- ui/mantine-ui/package.json | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ui/mantine-ui/package-lock.json b/ui/mantine-ui/package-lock.json index a49540e926..60fd52585d 100644 --- a/ui/mantine-ui/package-lock.json +++ b/ui/mantine-ui/package-lock.json @@ -23,7 +23,7 @@ "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", - "@types/node": "^25.9.3", + "@types/node": "^26.1.0", "@types/react": "^19.2.16", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^6.0.3", @@ -1100,13 +1100,13 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "25.9.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.3.tgz", - "integrity": "sha512-603BddQMv3pUcr4U2dhujk83N2tTDVr/34wII2B6bJy6g+8WD6yUb11jszNs0gdi4PesVWl7ABt8nYMVpnLUcg==", + "version": "26.1.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.0.tgz", + "integrity": "sha512-O0A1G3xPGy4w7AgQdAQYUlQ+BKk2Oovw8eRpofyp5KdBZULnbe+WqaOVNrm705SHphCiG4XHsACrSmPu1f+Kgw==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": ">=7.24.0 <7.24.7" + "undici-types": "~8.3.0" } }, "node_modules/@types/react": { @@ -2743,9 +2743,9 @@ } }, "node_modules/undici-types": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", - "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz", + "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==", "dev": true, "license": "MIT" }, diff --git a/ui/mantine-ui/package.json b/ui/mantine-ui/package.json index 9185753c4b..04dddfe901 100644 --- a/ui/mantine-ui/package.json +++ b/ui/mantine-ui/package.json @@ -33,7 +33,7 @@ "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", - "@types/node": "^25.9.3", + "@types/node": "^26.1.0", "@types/react": "^19.2.16", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^6.0.3", From a94aa50a19a3efae4e474b3de4f5c66ad64ad973 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 Jul 2026 14:52:56 +0200 Subject: [PATCH 064/120] build(deps): bump github.com/prometheus/exporter-toolkit (#5350) Bumps [github.com/prometheus/exporter-toolkit](https://github.com/prometheus/exporter-toolkit) from 0.16.0 to 0.17.1. - [Release notes](https://github.com/prometheus/exporter-toolkit/releases) - [Commits](https://github.com/prometheus/exporter-toolkit/compare/v0.16.0...v0.17.1) --- updated-dependencies: - dependency-name: github.com/prometheus/exporter-toolkit dependency-version: 0.17.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 7aa7611c21..2d43eebf72 100644 --- a/go.mod +++ b/go.mod @@ -38,7 +38,7 @@ require ( github.com/prometheus/client_golang v1.23.2 github.com/prometheus/client_model v0.6.2 github.com/prometheus/common v0.69.0 - github.com/prometheus/exporter-toolkit v0.16.0 + github.com/prometheus/exporter-toolkit v0.17.1 github.com/prometheus/sigv4 v0.4.1 github.com/rs/cors v1.11.1 github.com/stretchr/testify v1.11.1 diff --git a/go.sum b/go.sum index c91b95c06e..b398917b8c 100644 --- a/go.sum +++ b/go.sum @@ -502,8 +502,8 @@ github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB8 github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/common v0.69.0 h1:OA85nJQS/T/MaYh/Q2CcgDKSGWqNIgrBDvDH85CuiNk= github.com/prometheus/common v0.69.0/go.mod h1:ZzL3f6u94qUxh9p+tJTrF+FvBS1XXbbRAZCQkytAL0Y= -github.com/prometheus/exporter-toolkit v0.16.0 h1:xT/j7L2XKF+VJd6B4fpUw6xWabHrSmsUf6mYmFqyu0s= -github.com/prometheus/exporter-toolkit v0.16.0/go.mod h1:d1EL8Z9674xQe/iWhwP2wDyCEoBPbXVeqDbqAUsgJWY= +github.com/prometheus/exporter-toolkit v0.17.1 h1:psKN4wM7shBL/BxZkDHgm6YZJ3fAVG36+r86An/+7q0= +github.com/prometheus/exporter-toolkit v0.17.1/go.mod h1:dabwPJvxsC5+tsp2iolQrqBWZh+QlISKlYRpj9Hh5xk= 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= From d576499330b7a1c62ef80cd0ef0f3c476e6cf6e2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 Jul 2026 14:59:05 +0200 Subject: [PATCH 065/120] build(deps-dev): bump @biomejs/biome in /ui/mantine-ui (#5353) Bumps [@biomejs/biome](https://github.com/biomejs/biome/tree/HEAD/packages/@biomejs/biome) from 2.5.0 to 2.5.2. - [Release notes](https://github.com/biomejs/biome/releases) - [Changelog](https://github.com/biomejs/biome/blob/main/packages/@biomejs/biome/CHANGELOG.md) - [Commits](https://github.com/biomejs/biome/commits/@biomejs/biome@2.5.2/packages/@biomejs/biome) --- updated-dependencies: - dependency-name: "@biomejs/biome" dependency-version: 2.5.2 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- ui/mantine-ui/package-lock.json | 72 ++++++++++++++++----------------- ui/mantine-ui/package.json | 2 +- 2 files changed, 37 insertions(+), 37 deletions(-) diff --git a/ui/mantine-ui/package-lock.json b/ui/mantine-ui/package-lock.json index 60fd52585d..a65fd04adc 100644 --- a/ui/mantine-ui/package-lock.json +++ b/ui/mantine-ui/package-lock.json @@ -18,7 +18,7 @@ "react-router-dom": "^7.16.0" }, "devDependencies": { - "@biomejs/biome": "^2.5.0", + "@biomejs/biome": "^2.5.2", "@testing-library/dom": "^10.4.0", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", @@ -132,9 +132,9 @@ } }, "node_modules/@biomejs/biome": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.5.0.tgz", - "integrity": "sha512-4kURkd9hAPrdDM3C9n82ycYgx8hvQcW6MjKTEejruj8rK0N8P3OPpdy8BvI8kt3KWY4ycF5XtDOrktetEfhfuw==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.5.2.tgz", + "integrity": "sha512-VQ3RCqr7JmDIX+w6stWYl+g/3bYofN3q2wDBHUKKc/c7i5QWrFKFBZYCYPWTE6agsUPMIZZe6/CMmVUfUAhkKA==", "dev": true, "license": "MIT OR Apache-2.0", "bin": { @@ -148,20 +148,20 @@ "url": "https://opencollective.com/biome" }, "optionalDependencies": { - "@biomejs/cli-darwin-arm64": "2.5.0", - "@biomejs/cli-darwin-x64": "2.5.0", - "@biomejs/cli-linux-arm64": "2.5.0", - "@biomejs/cli-linux-arm64-musl": "2.5.0", - "@biomejs/cli-linux-x64": "2.5.0", - "@biomejs/cli-linux-x64-musl": "2.5.0", - "@biomejs/cli-win32-arm64": "2.5.0", - "@biomejs/cli-win32-x64": "2.5.0" + "@biomejs/cli-darwin-arm64": "2.5.2", + "@biomejs/cli-darwin-x64": "2.5.2", + "@biomejs/cli-linux-arm64": "2.5.2", + "@biomejs/cli-linux-arm64-musl": "2.5.2", + "@biomejs/cli-linux-x64": "2.5.2", + "@biomejs/cli-linux-x64-musl": "2.5.2", + "@biomejs/cli-win32-arm64": "2.5.2", + "@biomejs/cli-win32-x64": "2.5.2" } }, "node_modules/@biomejs/cli-darwin-arm64": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.5.0.tgz", - "integrity": "sha512-Mn3Fwi3SA5fgmfCPqmzpWF2DLZnms3BVAhM088nTnGrTZmHS3wwIjcoZPqpXeNgd3DrrLH6xp8vTLIBuJoZiXw==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.5.2.tgz", + "integrity": "sha512-e7P3P7EkwFc/KiX2AHw4YDLIBOMfG9CPCAwy52k5Bp0dfhkozx9hf6wCmIr2QeXy2XeccJ3V/Sg+hDmzYEqxSg==", "cpu": [ "arm64" ], @@ -176,9 +176,9 @@ } }, "node_modules/@biomejs/cli-darwin-x64": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.5.0.tgz", - "integrity": "sha512-rg3VPL5P8mYro6pqlXYXuJWph21slVp3SZtAqWSrkZs40d2gTzYmHF8E/X1iTID25btmNKltNDJ926sqVBp7DQ==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.5.2.tgz", + "integrity": "sha512-ymzMvjC1Jg0b9K0D26ZdARqFQXs7MocfLC5FOCGfkC0Ss+ACUJkX5364ZM5nT4NLZanHRZNVrZEy+Ibwcvux/g==", "cpu": [ "x64" ], @@ -193,9 +193,9 @@ } }, "node_modules/@biomejs/cli-linux-arm64": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.5.0.tgz", - "integrity": "sha512-tl+LW8fdD96/xdeWtWwc82LIOc5CoY7N2AsogLTp5R4ECErYt+8Jl/N68ezN9vzSiqPTxw6vjcihoLPYKZHrlw==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.5.2.tgz", + "integrity": "sha512-t7sseOmqND57uUWTwlawU6BYj+J06T/9EkydzBhkrgw/FK3QVhjU2wsJR0frljrKZ0/I8A/rYw7284QgqjQfIQ==", "cpu": [ "arm64" ], @@ -213,9 +213,9 @@ } }, "node_modules/@biomejs/cli-linux-arm64-musl": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.5.0.tgz", - "integrity": "sha512-vQdM4oSGaf7ZNeGO9w5+Y8SBtyser9M6znxYbm7Ec8wInxJu1WiKxFYZW5Auj2d80bcVvefuGGRxoFOE0eee8g==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.5.2.tgz", + "integrity": "sha512-w+ANG0ZvTu9IeEg9QnstoOnk6L0fpwJifW6aHR18+cb5Z39bkANItYjAfMrnvce5tmMK+IQ6nPX7/kQFdam5iw==", "cpu": [ "arm64" ], @@ -233,9 +233,9 @@ } }, "node_modules/@biomejs/cli-linux-x64": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.5.0.tgz", - "integrity": "sha512-zpEGf4RQbFEh8Vt7OmavLyyOzRbtcE9osCqrS1kfvt8jDvxwhKXLSf7n0ebr/ov0RJ9ssP+lhs6C8a9WwFvrQA==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.5.2.tgz", + "integrity": "sha512-M/lOZrewzTCRDINbjhQ1gYYru37KlD3kJBQwwKCG0ckz5E9IZwIoJ3X0wBwRXA+yBDIwWUuPBHS67HzJY4dTfA==", "cpu": [ "x64" ], @@ -253,9 +253,9 @@ } }, "node_modules/@biomejs/cli-linux-x64-musl": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.5.0.tgz", - "integrity": "sha512-+9hIcMngJ+yGUahXqZuZ8CoWKJE9SAZsFsM3QDvXpNsLbXZ9lqVzgBhOk/jTSYkOA0GLP9eu3teukqpLUojHMg==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.5.2.tgz", + "integrity": "sha512-VArNLAzND063tF+XY0yPyM+DyahpzOMzOAvb7qs259nhjJWRjvjZdssuA+Rfl+l07+NOesKZ0Xu2yFrXyBMtzw==", "cpu": [ "x64" ], @@ -273,9 +273,9 @@ } }, "node_modules/@biomejs/cli-win32-arm64": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.5.0.tgz", - "integrity": "sha512-jB0wAvTLI4itx5VidqVUejPQFhRUxiZ9l9FvZ26D5fl6t3qme+ZB4PD3bTSeL1vZ8NI2Rx/zj6H9zcESuGHKGw==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.5.2.tgz", + "integrity": "sha512-kbjFFKyZlzYnAuw7sRy5qDoFG6zrP40UK08oPQsWK0ct3NMnGSt+Bs1iviEEyEIP57N5MrykGXdO/wRiaR4lww==", "cpu": [ "arm64" ], @@ -290,9 +290,9 @@ } }, "node_modules/@biomejs/cli-win32-x64": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.5.0.tgz", - "integrity": "sha512-VT/lF+GId+67j8aDfLkxdxNoVApsPSTbyAtB3jJq0IWTrY77WXfbPfpngxq0bA6JCEv/7k8C9qWjDRKRznDlyw==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.5.2.tgz", + "integrity": "sha512-4InchVpdVmdkkkgjQqKpgvyu+VPnoF/7RPSw5YATgEVpt2j72wcCAeV5TwaE9ZGJUZWZn7v2CwSAj6CrMJEx8A==", "cpu": [ "x64" ], diff --git a/ui/mantine-ui/package.json b/ui/mantine-ui/package.json index 04dddfe901..28fedd7745 100644 --- a/ui/mantine-ui/package.json +++ b/ui/mantine-ui/package.json @@ -28,7 +28,7 @@ "react-router-dom": "^7.16.0" }, "devDependencies": { - "@biomejs/biome": "^2.5.0", + "@biomejs/biome": "^2.5.2", "@testing-library/dom": "^10.4.0", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", From 453bf024b6781b6c34c4a99c43f4094e1932dbdc Mon Sep 17 00:00:00 2001 From: Siavash Safi Date: Fri, 3 Jul 2026 15:09:35 +0200 Subject: [PATCH 066/120] fix(dispatch): deflake TestRouteLabelsAfterAllAlertsResolved (#5369) The test synchronized on the notify callback's channel send, which fires before flush() deletes the resolved alerts and invalidates the route-label cache. The subsequent ag.RouteLabels() call then raced the async deletion and occasionally observed the old alert, rendering "test" instead of empty. Replace the single-shot require.NotPanics/require.Empty with require.Eventually so the assertion polls until the empty render becomes observable. Eventually still fails on a panic, so the no-panic guarantee is preserved. Signed-off-by: Siavash Safi --- dispatch/dispatch_test.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/dispatch/dispatch_test.go b/dispatch/dispatch_test.go index 535b678842..19285577a2 100644 --- a/dispatch/dispatch_test.go +++ b/dispatch/dispatch_test.go @@ -1350,11 +1350,16 @@ func TestRouteLabelsAfterAllAlertsResolved(t *testing.T) { // re-renders against the now-empty group. The template references // .Alerts[0], so with no alerts it renders to an empty value rather than // erroring out the caller. - require.NotPanics(t, func() { + // + // The deletion happens on the flush goroutine after the notify function + // returns, so it races the channel receive above. Poll until the empty + // render is observable; require.Eventually also fails the test if any call + // panics. + require.Eventually(t, func() bool { rl = ag.RouteLabels() - }, "RouteLabels() must not panic after all alerts are deleted") - require.Empty(t, rl["description"], - "route label renders empty once the group has no alerts") + return rl["description"] == "" + }, 2*time.Second, 10*time.Millisecond, + "route label should render empty once the group has no alerts") } // TestRouteLabelsInNotifyContext verifies that the flush path puts the rendered From 16bf46143e33707e5031bb01b709740afdbdfc56 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 Jul 2026 15:12:44 +0200 Subject: [PATCH 067/120] build(deps): bump @tanstack/react-query in /ui/mantine-ui (#5351) Bumps [@tanstack/react-query](https://github.com/TanStack/query/tree/HEAD/packages/react-query) from 5.101.0 to 5.101.2. - [Release notes](https://github.com/TanStack/query/releases) - [Changelog](https://github.com/TanStack/query/blob/main/packages/react-query/CHANGELOG.md) - [Commits](https://github.com/TanStack/query/commits/@tanstack/react-query@5.101.2/packages/react-query) --- updated-dependencies: - dependency-name: "@tanstack/react-query" dependency-version: 5.101.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- ui/mantine-ui/package-lock.json | 16 ++++++++-------- ui/mantine-ui/package.json | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ui/mantine-ui/package-lock.json b/ui/mantine-ui/package-lock.json index a65fd04adc..202e180d13 100644 --- a/ui/mantine-ui/package-lock.json +++ b/ui/mantine-ui/package-lock.json @@ -11,7 +11,7 @@ "@mantine/code-highlight": "^9.4.1", "@mantine/core": "^9.0.0", "@mantine/hooks": "^9.0.0", - "@tanstack/react-query": "^5.101.0", + "@tanstack/react-query": "^5.101.2", "highlight.js": "^11.11.1", "react": "^19.2.7", "react-dom": "^19.2.7", @@ -932,9 +932,9 @@ "license": "MIT" }, "node_modules/@tanstack/query-core": { - "version": "5.101.0", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.101.0.tgz", - "integrity": "sha512-cQetA74EB+seWySv1TTKr828TnP0u39m6LykwDXIo84SNortpDkp30TMEjkqtYCNP9c40uT/iwl6MLiufEt0Ow==", + "version": "5.101.2", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.101.2.tgz", + "integrity": "sha512-hH5MLoJhF7KaIGd7q3xTXGXvslI+GYlM1Z/35aSHHWaCJWB7XvTSHYuV3eM7tw+aE0mT/xMro4M4Q9rCGHT0lw==", "license": "MIT", "funding": { "type": "github", @@ -942,12 +942,12 @@ } }, "node_modules/@tanstack/react-query": { - "version": "5.101.0", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.101.0.tgz", - "integrity": "sha512-rLlJXSpkqfizLWgkR5+eLeIk0MvTx/meEIR7LRjxic+qxiQP8zVjq7BqQkiCMNLQBlLfuOLqqr6KO5GtrDlmSg==", + "version": "5.101.2", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.101.2.tgz", + "integrity": "sha512-seDkr6kzGzX1okaaTtZPtgA688CDPlXUz1C6xSg0ESqn04Vuc8tlrYms1s3de+znBqhPVxFRfpAfUf+6XvfPWg==", "license": "MIT", "dependencies": { - "@tanstack/query-core": "5.101.0" + "@tanstack/query-core": "5.101.2" }, "funding": { "type": "github", diff --git a/ui/mantine-ui/package.json b/ui/mantine-ui/package.json index 28fedd7745..0974815a26 100644 --- a/ui/mantine-ui/package.json +++ b/ui/mantine-ui/package.json @@ -21,7 +21,7 @@ "@mantine/code-highlight": "^9.4.1", "@mantine/core": "^9.0.0", "@mantine/hooks": "^9.0.0", - "@tanstack/react-query": "^5.101.0", + "@tanstack/react-query": "^5.101.2", "highlight.js": "^11.11.1", "react": "^19.2.7", "react-dom": "^19.2.7", From 7cfec5c40ec62b2fdac8e920a6b8142506af9cc4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 Jul 2026 13:13:41 +0000 Subject: [PATCH 068/120] build(deps): bump github.com/twmb/franz-go from 1.21.2 to 1.21.5 (#5348) Bumps [github.com/twmb/franz-go](https://github.com/twmb/franz-go) from 1.21.2 to 1.21.5. - [Changelog](https://github.com/twmb/franz-go/blob/master/CHANGELOG.md) - [Commits](https://github.com/twmb/franz-go/compare/v1.21.2...v1.21.5) --- updated-dependencies: - dependency-name: github.com/twmb/franz-go dependency-version: 1.21.5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 2d43eebf72..b9e273cfcc 100644 --- a/go.mod +++ b/go.mod @@ -42,7 +42,7 @@ require ( github.com/prometheus/sigv4 v0.4.1 github.com/rs/cors v1.11.1 github.com/stretchr/testify v1.11.1 - github.com/twmb/franz-go v1.21.2 + github.com/twmb/franz-go v1.21.5 github.com/twmb/franz-go/pkg/kfake v0.0.0-20260515175617-8268a5d078c0 github.com/twmb/franz-go/plugin/kslog v1.0.0 github.com/xlab/treeprint v1.2.0 diff --git a/go.sum b/go.sum index b398917b8c..d227276b32 100644 --- a/go.sum +++ b/go.sum @@ -553,8 +553,8 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= -github.com/twmb/franz-go v1.21.2 h1:WrvV/spF48JzcRylqDQy02Vm6V6W4lhtD9Y4BOYNMu4= -github.com/twmb/franz-go v1.21.2/go.mod h1:rfoMTnVk7107fhTGxfEKIHP/e7tPe6oyij/ywzO0czk= +github.com/twmb/franz-go v1.21.5 h1:cVYI2+JTTKSvohhy8bCOleYrS7G79ZBrLVFIJsoHm8M= +github.com/twmb/franz-go v1.21.5/go.mod h1:rfoMTnVk7107fhTGxfEKIHP/e7tPe6oyij/ywzO0czk= github.com/twmb/franz-go/pkg/kadm v1.18.0 h1:WRf/LZmDdcDXwX7WMbtDU++v+b3NzYh2bCGoPMmzirw= github.com/twmb/franz-go/pkg/kadm v1.18.0/go.mod h1:XeLhGoLXLFzK8/ryv5FfpxPxGwj4oFEGpPJMB/x6KDE= github.com/twmb/franz-go/pkg/kfake v0.0.0-20260515175617-8268a5d078c0 h1:YWmvjmcidrKLLgObwU1k7K9KuesdYTV33OTIS0Ltj8o= From 3a705fca1defd4c9080bcc4fd78521d13e5808ec Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 Jul 2026 15:14:27 +0200 Subject: [PATCH 069/120] build(deps): bump actions/setup-go from 6.4.0 to 6.5.0 (#5335) Bumps [actions/setup-go](https://github.com/actions/setup-go) from 6.4.0 to 6.5.0. - [Release notes](https://github.com/actions/setup-go/releases) - [Commits](https://github.com/actions/setup-go/compare/4a3601121dd01d1626a1e23e37211e3254c1c06c...924ae3a1cded613372ab5595356fb5720e22ba16) --- updated-dependencies: - dependency-name: actions/setup-go dependency-version: 6.5.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/mixin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mixin.yml b/.github/workflows/mixin.yml index d44188e495..b828b73f64 100644 --- a/.github/workflows/mixin.yml +++ b/.github/workflows/mixin.yml @@ -17,7 +17,7 @@ jobs: with: persist-credentials: false - name: install Go - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 + uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 with: go-version: 1.26.x # pin the mixtool version until https://github.com/monitoring-mixins/mixtool/issues/135 is merged. From bc55309d4c64cd30d609d16eeaa8d6b2f32ac69e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 Jul 2026 15:14:51 +0200 Subject: [PATCH 070/120] build(deps): bump the react group across 1 directory with 2 updates (#5340) Bumps the react group with 2 updates in the /ui/mantine-ui directory: [react-router-dom](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router-dom) and [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react). Updates `react-router-dom` from 7.16.0 to 7.18.1 - [Release notes](https://github.com/remix-run/react-router/releases) - [Changelog](https://github.com/remix-run/react-router/blob/react-router-dom@7.18.1/packages/react-router-dom/CHANGELOG.md) - [Commits](https://github.com/remix-run/react-router/commits/react-router-dom@7.18.1/packages/react-router-dom) Updates `@types/react` from 19.2.16 to 19.2.17 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react) --- updated-dependencies: - dependency-name: "@types/react" dependency-version: 19.2.17 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: react - dependency-name: react-router-dom dependency-version: 7.18.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: react ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- ui/mantine-ui/package-lock.json | 24 ++++++++++++------------ ui/mantine-ui/package.json | 4 ++-- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/ui/mantine-ui/package-lock.json b/ui/mantine-ui/package-lock.json index 202e180d13..0af3795feb 100644 --- a/ui/mantine-ui/package-lock.json +++ b/ui/mantine-ui/package-lock.json @@ -15,7 +15,7 @@ "highlight.js": "^11.11.1", "react": "^19.2.7", "react-dom": "^19.2.7", - "react-router-dom": "^7.16.0" + "react-router-dom": "^7.18.1" }, "devDependencies": { "@biomejs/biome": "^2.5.2", @@ -24,7 +24,7 @@ "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", "@types/node": "^26.1.0", - "@types/react": "^19.2.16", + "@types/react": "^19.2.17", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^6.0.3", "jsdom": "^29.1.1", @@ -1110,9 +1110,9 @@ } }, "node_modules/@types/react": { - "version": "19.2.16", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.16.tgz", - "integrity": "sha512-esJiCAnl0kfpNdE69f3So4WJUXy95dLZydX0KwK46riIHDzHM7O9Vtf9xCHW0PXIqvgqNrswl522kA/5yx+F4w==", + "version": "19.2.17", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz", + "integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==", "devOptional": true, "license": "MIT", "dependencies": { @@ -2321,9 +2321,9 @@ } }, "node_modules/react-router": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.16.0.tgz", - "integrity": "sha512-wArC8lVyJb3+jM9OpDyW6hLCizACWkvQR/sSGqSs+o5uEXEtGlqdZ4v8hENR3Jad6i+LRkK93q/+bQAcvl6V1A==", + "version": "7.18.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.18.1.tgz", + "integrity": "sha512-GDLgg3i3uM0aeJO3Fm+TCS+sDQ7gu12T6x0qdTEzcwqEfleci7JwugVNIF3U//0FWKnJT7ptG+20B2jfDqnZAg==", "license": "MIT", "dependencies": { "cookie": "^1.0.1", @@ -2343,12 +2343,12 @@ } }, "node_modules/react-router-dom": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.16.0.tgz", - "integrity": "sha512-kMUAbimWB5FVbF4Bce4bJsiKJWLIUHq/mEG8+CFDnCSgltptBiG5nguducmsJeGKytlCvQud9Qhzpn49iduTlA==", + "version": "7.18.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.18.1.tgz", + "integrity": "sha512-KaZh+X/6UtEp28x51AUYZDMg9NGoz2ja3dNHa+ta/tk40vCzKhQ/RypCWBMLbmDr6//E24Vv5uPsrqXFozdkAg==", "license": "MIT", "dependencies": { - "react-router": "7.16.0" + "react-router": "7.18.1" }, "engines": { "node": ">=20.0.0" diff --git a/ui/mantine-ui/package.json b/ui/mantine-ui/package.json index 0974815a26..c69a99333a 100644 --- a/ui/mantine-ui/package.json +++ b/ui/mantine-ui/package.json @@ -25,7 +25,7 @@ "highlight.js": "^11.11.1", "react": "^19.2.7", "react-dom": "^19.2.7", - "react-router-dom": "^7.16.0" + "react-router-dom": "^7.18.1" }, "devDependencies": { "@biomejs/biome": "^2.5.2", @@ -34,7 +34,7 @@ "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", "@types/node": "^26.1.0", - "@types/react": "^19.2.16", + "@types/react": "^19.2.17", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^6.0.3", "jsdom": "^29.1.1", From edcae41a837f67d8bc7e2338ca6a8774f4e7fb54 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 Jul 2026 13:16:03 +0000 Subject: [PATCH 071/120] build(deps-dev): bump vitest (#5341) Bumps the testing group with 1 update in the /ui/mantine-ui directory: [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest). Updates `vitest` from 4.1.8 to 4.1.9 - [Release notes](https://github.com/vitest-dev/vitest/releases) - [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md) - [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.9/packages/vitest) --- updated-dependencies: - dependency-name: vitest dependency-version: 4.1.9 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: testing ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- ui/mantine-ui/package-lock.json | 90 ++++++++++++++++----------------- ui/mantine-ui/package.json | 2 +- 2 files changed, 46 insertions(+), 46 deletions(-) diff --git a/ui/mantine-ui/package-lock.json b/ui/mantine-ui/package-lock.json index 0af3795feb..48dacb4aa0 100644 --- a/ui/mantine-ui/package-lock.json +++ b/ui/mantine-ui/package-lock.json @@ -35,7 +35,7 @@ "typescript": "^5.9.3", "vite": "^8.1.3", "vite-tsconfig-paths": "^6.1.1", - "vitest": "^4.1.8" + "vitest": "^4.1.9" } }, "node_modules/@adobe/css-tools": { @@ -1156,16 +1156,16 @@ } }, "node_modules/@vitest/expect": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.8.tgz", - "integrity": "sha512-h3nDO677RDLEGlBxyQ5CW8RlMThSKSRLUePLOx09gNIWRL40edgA1GCZSZgf1W55MFAG6/Sw14KeaAnqv0NKdQ==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.9.tgz", + "integrity": "sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==", "dev": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", - "@vitest/spy": "4.1.8", - "@vitest/utils": "4.1.8", + "@vitest/spy": "4.1.9", + "@vitest/utils": "4.1.9", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" }, @@ -1174,13 +1174,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.8.tgz", - "integrity": "sha512-LEiN/xe4OSIbKe9HQIp5OC24agGD9J5CnmMgsLohVVoOPWL9a2sBoR6VBx43jQZb7Kr1l4RCuyCJzcAa0+dojw==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.9.tgz", + "integrity": "sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "4.1.8", + "@vitest/spy": "4.1.9", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, @@ -1201,9 +1201,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.8.tgz", - "integrity": "sha512-9GasEBxpZ1VYIpqHf/0+YGg121uSNwCKOJqIrTwWP/TB7DmFCiaBpNl3aPZzoLWfWkuqhbH8vJIVobZkvdo2cA==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.9.tgz", + "integrity": "sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==", "dev": true, "license": "MIT", "dependencies": { @@ -1214,13 +1214,13 @@ } }, "node_modules/@vitest/runner": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.8.tgz", - "integrity": "sha512-EmVxeBAfMJvycdjd6Hm+RbFBbA9fKvo0Kx37hNpBYoYeavH3RNsBXWDooR1mgD52dCrxIIuP7UotpfiwOikvcg==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.9.tgz", + "integrity": "sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.1.8", + "@vitest/utils": "4.1.9", "pathe": "^2.0.3" }, "funding": { @@ -1228,14 +1228,14 @@ } }, "node_modules/@vitest/snapshot": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.8.tgz", - "integrity": "sha512-acfZboRmAIf05DEKcBQy33VXojFJjtUdLyo7oOmV9kebb2xdU01UknNiPuPZoJZQyO7DF0gZdTGTpeAzET9QPQ==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.9.tgz", + "integrity": "sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.8", - "@vitest/utils": "4.1.8", + "@vitest/pretty-format": "4.1.9", + "@vitest/utils": "4.1.9", "magic-string": "^0.30.21", "pathe": "^2.0.3" }, @@ -1244,9 +1244,9 @@ } }, "node_modules/@vitest/spy": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.8.tgz", - "integrity": "sha512-6EevtBp6OZOPF7bmz36HrGMeP3txgVSrgebWxHOafDXGkhIzfXK14f8KF6MuFfgXXUeHxmpD3BQxkV00/3s5mA==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.9.tgz", + "integrity": "sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA==", "dev": true, "license": "MIT", "funding": { @@ -1254,13 +1254,13 @@ } }, "node_modules/@vitest/utils": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.8.tgz", - "integrity": "sha512-uOJamYALNhfJ6iolExyQM40yIQwDqYnkKtQ5VCiSe17E33H0aQ/u+1GlRuz4LZBk6Mm3sg90G9hEbmEt37C1Zg==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.9.tgz", + "integrity": "sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.8", + "@vitest/pretty-format": "4.1.9", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" }, @@ -2906,19 +2906,19 @@ } }, "node_modules/vitest": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.8.tgz", - "integrity": "sha512-flY6ScbCIt9HThs+C5HS7jvGOB560DJtk/Z15IQROTA6zEy49Nh8T/dofWTQL+n3vswqn87sbJNiuqw1SDp5Ig==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.9.tgz", + "integrity": "sha512-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "4.1.8", - "@vitest/mocker": "4.1.8", - "@vitest/pretty-format": "4.1.8", - "@vitest/runner": "4.1.8", - "@vitest/snapshot": "4.1.8", - "@vitest/spy": "4.1.8", - "@vitest/utils": "4.1.8", + "@vitest/expect": "4.1.9", + "@vitest/mocker": "4.1.9", + "@vitest/pretty-format": "4.1.9", + "@vitest/runner": "4.1.9", + "@vitest/snapshot": "4.1.9", + "@vitest/spy": "4.1.9", + "@vitest/utils": "4.1.9", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", @@ -2946,12 +2946,12 @@ "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.1.8", - "@vitest/browser-preview": "4.1.8", - "@vitest/browser-webdriverio": "4.1.8", - "@vitest/coverage-istanbul": "4.1.8", - "@vitest/coverage-v8": "4.1.8", - "@vitest/ui": "4.1.8", + "@vitest/browser-playwright": "4.1.9", + "@vitest/browser-preview": "4.1.9", + "@vitest/browser-webdriverio": "4.1.9", + "@vitest/coverage-istanbul": "4.1.9", + "@vitest/coverage-v8": "4.1.9", + "@vitest/ui": "4.1.9", "happy-dom": "*", "jsdom": "*", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" diff --git a/ui/mantine-ui/package.json b/ui/mantine-ui/package.json index c69a99333a..2483517d4b 100644 --- a/ui/mantine-ui/package.json +++ b/ui/mantine-ui/package.json @@ -45,6 +45,6 @@ "typescript": "^5.9.3", "vite": "^8.1.3", "vite-tsconfig-paths": "^6.1.1", - "vitest": "^4.1.8" + "vitest": "^4.1.9" } } From 7f974634c6080fd810f1fe2814035d2fabdda066 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 Jul 2026 15:16:50 +0200 Subject: [PATCH 072/120] build(deps): bump the promci group with 4 updates (#5362) Bumps the promci group with 4 updates: [prometheus/promci/build](https://github.com/prometheus/promci), [prometheus/promci-setup](https://github.com/prometheus/promci-setup), [prometheus/promci/publish_main](https://github.com/prometheus/promci) and [prometheus/promci/publish_release](https://github.com/prometheus/promci). Updates `prometheus/promci/build` from 0.8.2 to 0.8.5 - [Release notes](https://github.com/prometheus/promci/releases) - [Commits](https://github.com/prometheus/promci/compare/d9d4f5688814f0b77bf003d07fb8c00507390634...13941414d409d227afd67544e5d306827db5a1a2) Updates `prometheus/promci-setup` from 0.1.0 to 0.2.1 - [Release notes](https://github.com/prometheus/promci-setup/releases) - [Commits](https://github.com/prometheus/promci-setup/compare/5af30ba8c199a91d6c04ebdc3c48e630e355f62d...3e5cd31b34b8ae19efa8f071c5e3cdb44884a7f8) Updates `prometheus/promci/publish_main` from 0.8.2 to 0.8.5 - [Release notes](https://github.com/prometheus/promci/releases) - [Commits](https://github.com/prometheus/promci/compare/d9d4f5688814f0b77bf003d07fb8c00507390634...13941414d409d227afd67544e5d306827db5a1a2) Updates `prometheus/promci/publish_release` from 0.8.2 to 0.8.5 - [Release notes](https://github.com/prometheus/promci/releases) - [Commits](https://github.com/prometheus/promci/compare/d9d4f5688814f0b77bf003d07fb8c00507390634...13941414d409d227afd67544e5d306827db5a1a2) --- updated-dependencies: - dependency-name: prometheus/promci/build dependency-version: 0.8.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: promci - dependency-name: prometheus/promci-setup dependency-version: 0.2.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: promci - dependency-name: prometheus/promci/publish_main dependency-version: 0.8.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: promci - dependency-name: prometheus/promci/publish_release dependency-version: 0.8.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: promci ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 4 ++-- .github/workflows/publish.yml | 4 ++-- .github/workflows/release.yml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 51e1f4c651..c680375e68 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,7 +49,7 @@ jobs: with: name: ui-dist path: ui/app/dist - - uses: prometheus/promci/build@d9d4f5688814f0b77bf003d07fb8c00507390634 # v0.8.2 + - uses: prometheus/promci/build@13941414d409d227afd67544e5d306827db5a1a2 # v0.8.5 with: promu_opts: "-p linux/amd64 -p windows/amd64 -p linux/arm64 -p darwin/amd64 -p darwin/arm64 -p linux/386" parallelism: 3 @@ -83,6 +83,6 @@ jobs: with: name: ui-dist path: ui/app/dist - - uses: prometheus/promci-setup@5af30ba8c199a91d6c04ebdc3c48e630e355f62d # v0.1.0 + - uses: prometheus/promci-setup@3e5cd31b34b8ae19efa8f071c5e3cdb44884a7f8 # v0.2.1 - run: make - run: git diff --exit-code diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 34b5e771c0..d55115912f 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -20,7 +20,7 @@ jobs: thread: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] needs: ci steps: - - uses: prometheus/promci/build@d9d4f5688814f0b77bf003d07fb8c00507390634 # v0.8.2 + - uses: prometheus/promci/build@13941414d409d227afd67544e5d306827db5a1a2 # v0.8.5 with: parallelism: 12 thread: ${{ matrix.thread }} @@ -31,7 +31,7 @@ jobs: packages: write # push the image to GHCR via github.token needs: build steps: - - uses: prometheus/promci/publish_main@d9d4f5688814f0b77bf003d07fb8c00507390634 # v0.8.2 + - uses: prometheus/promci/publish_main@13941414d409d227afd67544e5d306827db5a1a2 # v0.8.5 with: docker_hub_login: ${{ secrets.docker_hub_login }} docker_hub_password: ${{ secrets.docker_hub_password }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cbeb5ddda6..923fca2997 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,7 +27,7 @@ jobs: with: name: ui-dist path: ui/app/dist - - uses: prometheus/promci/build@d9d4f5688814f0b77bf003d07fb8c00507390634 # v0.8.2 + - uses: prometheus/promci/build@13941414d409d227afd67544e5d306827db5a1a2 # v0.8.5 with: parallelism: 12 thread: ${{ matrix.thread }} @@ -39,7 +39,7 @@ jobs: packages: write # push the image to GHCR via github.token needs: build steps: - - uses: prometheus/promci/publish_release@d9d4f5688814f0b77bf003d07fb8c00507390634 # v0.8.2 + - uses: prometheus/promci/publish_release@13941414d409d227afd67544e5d306827db5a1a2 # v0.8.5 with: docker_hub_login: ${{ secrets.docker_hub_login }} docker_hub_password: ${{ secrets.docker_hub_password }} From 9e0a9470f6fc83774eac5af0b579eb0cab82fa68 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 Jul 2026 13:16:52 +0000 Subject: [PATCH 073/120] build(deps): bump google.golang.org/grpc from 1.81.1 to 1.82.0 (#5344) Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.81.1 to 1.82.0. - [Release notes](https://github.com/grpc/grpc-go/releases) - [Commits](https://github.com/grpc/grpc-go/compare/v1.81.1...v1.82.0) --- updated-dependencies: - dependency-name: google.golang.org/grpc dependency-version: 1.82.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index b9e273cfcc..9027f3f733 100644 --- a/go.mod +++ b/go.mod @@ -57,7 +57,7 @@ require ( golang.org/x/mod v0.37.0 golang.org/x/net v0.56.0 golang.org/x/text v0.38.0 - google.golang.org/grpc v1.81.1 + google.golang.org/grpc v1.82.0 google.golang.org/protobuf v1.36.11 gopkg.in/telebot.v3 v3.3.8 gopkg.in/yaml.v2 v2.4.0 diff --git a/go.sum b/go.sum index d227276b32..ae52174cfe 100644 --- a/go.sum +++ b/go.sum @@ -1072,8 +1072,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.81.1 h1:VnnIIZ88UzOOKLukQi+ImGz8O1Wdp8nAGGnvOfEIWQQ= -google.golang.org/grpc v1.81.1/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I= +google.golang.org/grpc v1.82.0 h1:vguDnZUPjE26w09A63VoxZPnvPjB5Riyc0mkXPFmAIU= +google.golang.org/grpc v1.82.0/go.mod h1:yzTZ1TB1Z3SG+LIYaI+WiE8D5+PZ3ArnrSp8zF3+/ZA= 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= From aa174a5a2a72e8c39e4eb23037d1931c9187f956 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 Jul 2026 13:19:44 +0000 Subject: [PATCH 074/120] build(deps): bump actions/checkout from 6.0.3 to 7.0.0 (#5337) Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.3 to 7.0.0. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/df4cb1c069e1874edd31b4311f1884172cec0e10...9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: 7.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 6 +++--- .github/workflows/container_description.yml | 4 ++-- .github/workflows/mixin.yml | 2 +- .github/workflows/release.yml | 2 +- .github/workflows/ui-ci.yml | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c680375e68..fe91ca9260 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,7 +8,7 @@ jobs: name: Test alertmanager frontend runs-on: ubuntu-latest steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 @@ -42,7 +42,7 @@ jobs: matrix: thread: [0, 1, 2] steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v4.0.0 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v4.0.0 with: persist-credentials: false - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 @@ -76,7 +76,7 @@ jobs: EMAIL_NO_AUTH_CONFIG: testdata/noauth.yml EMAIL_AUTH_CONFIG: testdata/auth.yml steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 diff --git a/.github/workflows/container_description.yml b/.github/workflows/container_description.yml index 3bb36ccf43..0e2f274b9d 100644 --- a/.github/workflows/container_description.yml +++ b/.github/workflows/container_description.yml @@ -18,7 +18,7 @@ jobs: if: github.repository_owner == 'prometheus' || github.repository_owner == 'prometheus-community' # Don't run this workflow on forks. steps: - name: git checkout - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - name: Set docker hub repo name @@ -42,7 +42,7 @@ jobs: if: github.repository_owner == 'prometheus' || github.repository_owner == 'prometheus-community' # Don't run this workflow on forks. steps: - name: git checkout - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - name: Set quay.io org name diff --git a/.github/workflows/mixin.yml b/.github/workflows/mixin.yml index b828b73f64..a373e7b06a 100644 --- a/.github/workflows/mixin.yml +++ b/.github/workflows/mixin.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - name: install Go diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 923fca2997..3026e27986 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,7 +20,7 @@ jobs: thread: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] needs: ci steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 diff --git a/.github/workflows/ui-ci.yml b/.github/workflows/ui-ci.yml index e6940f2d36..38343ce814 100644 --- a/.github/workflows/ui-ci.yml +++ b/.github/workflows/ui-ci.yml @@ -23,7 +23,7 @@ jobs: run: working-directory: ./ui/mantine-ui steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 From 9fa45f03b58ac869b38c6dee36379ff6332cfb7e Mon Sep 17 00:00:00 2001 From: Ethan Hunter Date: Fri, 3 Jul 2026 19:45:01 -0600 Subject: [PATCH 075/120] add patch 0.32.3 to the changelog (#5364) Signed-off-by: Ethan Hunter --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 216e215d11..1a990c6698 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ * [ENHANCEMENT] notify: The discord and webex integrations now report a failure `reason` on `alertmanager_notifications_failed_total`. * [BUGFIX] webhook: Keep custom `payload` string values verbatim instead of reinterpreting JSON leaves that look like YAML (e.g. values ending with a colon). #5302 +## 0.32.3 / 2026-07-04 + +* [BUGFIX] doc: fix missing `notification_reason` field in webhook documentation (#5329) +* [BUGFIX] silences: fix silences snapshot missing legacy matchers field. This caused a bug that prevented older alertmanager versions from reading newer snapshots unnecessarily. (#5330) +* [BUGFIX] silence with no matchers should populate an empty array in API response (#5331) + ## 0.33.0 / 2026-06-12 * [CHANGE] The '--enable-feature=auto-gomaxprocs' option has been removed. This flag had no effect since v0.29 and was deprecated in v0.32. It can be safely removed from any startup scripts. #5090, #5251 From 1ad30af920cfd4bb60fff09b6698adb020eac8de Mon Sep 17 00:00:00 2001 From: Ethan Hunter Date: Fri, 3 Jul 2026 19:55:10 -0600 Subject: [PATCH 076/120] prep 0.33.1 release (#5365) Signed-off-by: Ethan Hunter --- CHANGELOG.md | 6 ++++++ VERSION | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a990c6698..297f5ec9c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ * [ENHANCEMENT] notify: The discord and webex integrations now report a failure `reason` on `alertmanager_notifications_failed_total`. * [BUGFIX] webhook: Keep custom `payload` string values verbatim instead of reinterpreting JSON leaves that look like YAML (e.g. values ending with a colon). #5302 +## 0.33.1 / 2026-07-04 + +* [BUGFIX] doc: fix missing `notification_reason` field in webhook documentation (#5329) +* [BUGFIX] silences: fix silences snapshot missing legacy matchers field. This caused a bug that prevented older alertmanager versions from reading newer snapshots unnecessarily. (#5330) +* [BUGFIX] silence with no matchers should populate an empty array in API response (#5331) + ## 0.32.3 / 2026-07-04 * [BUGFIX] doc: fix missing `notification_reason` field in webhook documentation (#5329) diff --git a/VERSION b/VERSION index be386c9ede..8df3f4592f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.33.0 +0.33.1 From bb956c90eb4e3b0ab5b530ef671d9b31ec61c36d Mon Sep 17 00:00:00 2001 From: Ethan Hunter Date: Sat, 4 Jul 2026 09:25:07 -0600 Subject: [PATCH 077/120] add workflow for building release branches (#5370) Signed-off-by: Ethan Hunter --- .github/workflows/build-release-branch.yml | 27 ++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .github/workflows/build-release-branch.yml diff --git a/.github/workflows/build-release-branch.yml b/.github/workflows/build-release-branch.yml new file mode 100644 index 0000000000..5342c37960 --- /dev/null +++ b/.github/workflows/build-release-branch.yml @@ -0,0 +1,27 @@ +--- +name: Build release branch +on: # yamllint disable-line rule:truthy + push: + branches: + - release-* + workflow_dispatch: +permissions: + contents: read + +jobs: + ci: + name: Run ci + uses: ./.github/workflows/ci.yml + + build: + name: Build Alertmanager for all architectures + runs-on: ubuntu-latest + strategy: + matrix: + thread: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] + needs: ci + steps: + - uses: prometheus/promci/build@13941414d409d227afd67544e5d306827db5a1a2 # v0.8.5 + with: + parallelism: 12 + thread: ${{ matrix.thread }} From 85fc7ce83c12c5aa75d7fe5b77db48271dd17965 Mon Sep 17 00:00:00 2001 From: Mihir Dixit Date: Thu, 18 Jun 2026 01:26:54 +0000 Subject: [PATCH 078/120] feat(eventrecorder): add stdout output type Adds a new stdout_outputs destination to the event_recorder configuration. When running Alertmanager in a container, users can now capture structured alert events through the container runtime log driver (Docker, Kubernetes, etc.) without managing a separate file, Kafka cluster, or webhook endpoint. Implementation follows the existing per-type-list pattern used by file_outputs, webhook_outputs, and kafka_outputs. The StdoutOutput implements the Destination interface and serializes events as newline-delimited JSON via protojson, matching the format used by FileOutput. Closes #5306 Signed-off-by: Mihir Dixit --- eventrecorder/config.go | 11 ++- eventrecorder/recorder.go | 3 + eventrecorder/stdout.go | 58 +++++++++++++ eventrecorder/stdout_test.go | 162 +++++++++++++++++++++++++++++++++++ 4 files changed, 232 insertions(+), 2 deletions(-) create mode 100644 eventrecorder/stdout.go create mode 100644 eventrecorder/stdout_test.go diff --git a/eventrecorder/config.go b/eventrecorder/config.go index ff8ecf47f5..622976c797 100644 --- a/eventrecorder/config.go +++ b/eventrecorder/config.go @@ -23,12 +23,13 @@ type Config struct { FileOutputs []FileOutputConfig `yaml:"file_outputs,omitempty" json:"file_outputs,omitempty"` WebhookOutputs []WebhookOutputConfig `yaml:"webhook_outputs,omitempty" json:"webhook_outputs,omitempty"` KafkaOutputs []KafkaOutputConfig `yaml:"kafka_outputs,omitempty" json:"kafka_outputs,omitempty"` + StdoutOutputs []StdoutOutputConfig `yaml:"stdout_outputs,omitempty" json:"stdout_outputs,omitempty"` } // totalOutputs returns the number of configured outputs across all // destination kinds. func (c Config) totalOutputs() int { - return len(c.FileOutputs) + len(c.WebhookOutputs) + len(c.KafkaOutputs) + return len(c.FileOutputs) + len(c.WebhookOutputs) + len(c.KafkaOutputs) + len(c.StdoutOutputs) } // configEqual compares two Config values by their semantically @@ -38,7 +39,8 @@ func (c Config) totalOutputs() int { func configEqual(a, b Config) bool { if len(a.FileOutputs) != len(b.FileOutputs) || len(a.WebhookOutputs) != len(b.WebhookOutputs) || - len(a.KafkaOutputs) != len(b.KafkaOutputs) { + len(a.KafkaOutputs) != len(b.KafkaOutputs) || + len(a.StdoutOutputs) != len(b.StdoutOutputs) { return false } for i := range a.FileOutputs { @@ -56,5 +58,10 @@ func configEqual(a, b Config) bool { return false } } + for i := range a.StdoutOutputs { + if !a.StdoutOutputs[i].equal(b.StdoutOutputs[i]) { + return false + } + } return true } diff --git a/eventrecorder/recorder.go b/eventrecorder/recorder.go index 808eef97a3..c369f02dcb 100644 --- a/eventrecorder/recorder.go +++ b/eventrecorder/recorder.go @@ -207,6 +207,9 @@ func buildOutputs(cfg Config, instance string, m *metrics, logger *slog.Logger) } outputs = append(outputs, ko) } + for range cfg.StdoutOutputs { + outputs = append(outputs, &StdoutOutput{}) + } return outputs } diff --git a/eventrecorder/stdout.go b/eventrecorder/stdout.go new file mode 100644 index 0000000000..1b2652c468 --- /dev/null +++ b/eventrecorder/stdout.go @@ -0,0 +1,58 @@ +// Copyright The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package eventrecorder + +import ( + "os" + + "google.golang.org/protobuf/encoding/protojson" + + "github.com/prometheus/alertmanager/eventrecorder/eventrecorderpb" +) + +// StdoutOutputConfig configures a stdout event recorder output. +// There are no required fields; the presence of an entry in +// stdout_outputs is sufficient to enable the output. +type StdoutOutputConfig struct{} + +// equal reports whether two stdout output configs are semantically equal. +// All StdoutOutputConfig values are identical since the type carries no fields. +func (c StdoutOutputConfig) equal(_ StdoutOutputConfig) bool { return true } + +// StdoutOutput writes events as newline-delimited JSON to os.Stdout. +// This is the recommended output for container deployments where stdout +// is captured by the runtime log driver (Docker, Kubernetes, etc.). +// +// Each event is serialized with protojson and followed by a newline so +// log collectors receive one self-contained JSON object per line. +type StdoutOutput struct{} + +// Name returns the stable identifier used in Prometheus metric labels. +func (s *StdoutOutput) Name() string { return "stdout" } + +// SendEvent serializes the event as a JSON line and writes it to stdout. +// It returns the byte count written (including the trailing newline) and +// any write error encountered. A serialization failure is wrapped in +// serializeError so the recorder attributes it to the correct metric. +func (s *StdoutOutput) SendEvent(event *eventrecorderpb.Event) (int, error) { + data, err := protojson.Marshal(event) + if err != nil { + return 0, &serializeError{err: err} + } + data = append(data, '\n') + return os.Stdout.Write(data) +} + +// Close is a no-op; os.Stdout is owned by the process, not by this output. +func (s *StdoutOutput) Close() error { return nil } diff --git a/eventrecorder/stdout_test.go b/eventrecorder/stdout_test.go new file mode 100644 index 0000000000..69d4619fcc --- /dev/null +++ b/eventrecorder/stdout_test.go @@ -0,0 +1,162 @@ +// Copyright The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package eventrecorder + +import ( + "bytes" + "io" + "os" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/require" + "gopkg.in/yaml.v2" +) + +// captureStdout replaces os.Stdout with a pipe for the duration of fn, +// then returns everything written to it. The original os.Stdout is +// restored via t.Cleanup regardless of how fn exits. +func captureStdout(t *testing.T, fn func()) string { + t.Helper() + r, w, err := os.Pipe() + require.NoError(t, err) + + old := os.Stdout + os.Stdout = w + t.Cleanup(func() { os.Stdout = old }) + + fn() + + require.NoError(t, w.Close()) + var buf bytes.Buffer + _, err = io.Copy(&buf, r) + require.NoError(t, err) + return buf.String() +} + +func TestStdoutOutput_Name(t *testing.T) { + out := &StdoutOutput{} + require.Equal(t, "stdout", out.Name()) +} + +func TestStdoutOutput_SendEvent(t *testing.T) { + out := &StdoutOutput{} + + got := captureStdout(t, func() { + n, err := out.SendEvent(sampleEvent()) + require.NoError(t, err) + require.Positive(t, n) + }) + + // Expect a single newline-terminated JSON object. + require.True(t, strings.HasPrefix(got, "{"), "output should start with a JSON object") + require.True(t, strings.HasSuffix(got, "}\n"), "output should end with a closing brace and newline") + require.Contains(t, got, "alertmanagerStartupEvent") +} + +func TestStdoutOutput_SendEventTwice(t *testing.T) { + out := &StdoutOutput{} + + got := captureStdout(t, func() { + _, err := out.SendEvent(sampleEvent()) + require.NoError(t, err) + _, err = out.SendEvent(sampleEvent()) + require.NoError(t, err) + }) + + lines := strings.Split(strings.TrimRight(got, "\n"), "\n") + require.Len(t, lines, 2, "two events should produce two JSONL lines") + for _, line := range lines { + require.True(t, strings.HasPrefix(line, "{")) + require.Contains(t, line, "alertmanagerStartupEvent") + } +} + +func TestStdoutOutput_Close(t *testing.T) { + out := &StdoutOutput{} + require.NoError(t, out.Close(), "Close must be a no-op and return nil") +} + +// TestStdoutOutput_ImplementsDestination is a compile-time check that +// StdoutOutput satisfies the Destination interface. +func TestStdoutOutput_ImplementsDestination(t *testing.T) { + var _ Destination = (*StdoutOutput)(nil) +} + +// TestStdoutOutput_IntegrationWithRecorder verifies events flow from a +// Recorder through the StdoutOutput and appear on stdout as JSON lines. +func TestStdoutOutput_IntegrationWithRecorder(t *testing.T) { + out := &StdoutOutput{} + rec := newTestRecorder(out) + defer rec.Close() + + var got string + done := make(chan struct{}) + + go func() { + defer close(done) + got = captureStdout(t, func() { + rec.RecordEvent(recordCtx(), startupEvent) + // Give the write loop time to drain the event before we close + // the pipe and read. + time.Sleep(100 * time.Millisecond) + }) + }() + + <-done + require.Contains(t, got, "alertmanagerStartupEvent") +} + +// --- config tests. + +func TestStdoutOutputConfig_Equal(t *testing.T) { + // All StdoutOutputConfig values compare equal since the type has no fields. + a := StdoutOutputConfig{} + b := StdoutOutputConfig{} + require.True(t, a.equal(b)) +} + +func TestEventRecorderConfig_StdoutInTotalOutputs(t *testing.T) { + cfg := Config{ + StdoutOutputs: []StdoutOutputConfig{{}}, + } + require.Equal(t, 1, cfg.totalOutputs()) +} + +func TestEventRecorderConfigEqual_Stdout(t *testing.T) { + a := Config{StdoutOutputs: []StdoutOutputConfig{{}}} + b := Config{StdoutOutputs: []StdoutOutputConfig{{}}} + require.True(t, configEqual(a, b)) + + // Removing the stdout output makes them unequal. + b.StdoutOutputs = nil + require.False(t, configEqual(a, b)) +} + +func TestEventRecorderConfigEqual_StdoutVsFile(t *testing.T) { + // Same total count but in different per-type lists must compare unequal. + a := Config{StdoutOutputs: []StdoutOutputConfig{{}}} + b := Config{FileOutputs: []FileOutputConfig{{Path: "/tmp/events.jsonl"}}} + require.False(t, configEqual(a, b)) +} + +func TestStdoutOutputConfig_UnmarshalYAML(t *testing.T) { + // An empty map is the natural YAML representation of a + // StdoutOutputConfig since it carries no fields. + raw := "stdout_outputs:\n - {}\n" + var cfg Config + require.NoError(t, yaml.Unmarshal([]byte(raw), &cfg)) + require.Len(t, cfg.StdoutOutputs, 1) +} From c0807727e65001420cc7a019ad52499743b3b3f4 Mon Sep 17 00:00:00 2001 From: Mihir Dixit Date: Sat, 20 Jun 2026 03:00:37 +0000 Subject: [PATCH 079/120] eventrecorder: address reviewer feedback Signed-off-by: Mihir Dixit --- eventrecorder/recorder.go | 13 +----------- eventrecorder/stdout_test.go | 40 ++++++++++++++++++++++-------------- 2 files changed, 26 insertions(+), 27 deletions(-) diff --git a/eventrecorder/recorder.go b/eventrecorder/recorder.go index c369f02dcb..1f0a081138 100644 --- a/eventrecorder/recorder.go +++ b/eventrecorder/recorder.go @@ -13,23 +13,12 @@ // Package eventrecorder provides a structured event recorder for // significant Alertmanager events. Events are serialized as JSON and -// fanned out to one or more configured destinations (JSONL file, -// webhook, kafka). +// fanned out to one or more configured destinations. // // RecordEvent never blocks the caller: events are serialized and // placed on a bounded in-memory queue. A background goroutine // drains the queue and sends to destinations. If the queue is full, // events are dropped and a metric is incremented. -// -// Package layout: -// -// - recorder.go Recorder core: types, write loop, fan-out. -// - metrics.go Prometheus metric definitions. -// - events.go Pure proto-conversion helpers and event constructors. -// - config.go Top-level Config: per-type output lists + equality. -// - file.go File output and its config. -// - webhook.go Webhook output and its config. -// - kafka.go Kafka output and its config. package eventrecorder import ( diff --git a/eventrecorder/stdout_test.go b/eventrecorder/stdout_test.go index 69d4619fcc..cdf2286337 100644 --- a/eventrecorder/stdout_test.go +++ b/eventrecorder/stdout_test.go @@ -98,25 +98,35 @@ func TestStdoutOutput_ImplementsDestination(t *testing.T) { // TestStdoutOutput_IntegrationWithRecorder verifies events flow from a // Recorder through the StdoutOutput and appear on stdout as JSON lines. func TestStdoutOutput_IntegrationWithRecorder(t *testing.T) { - out := &StdoutOutput{} - rec := newTestRecorder(out) + // mirror is a mockDestination used solely to detect when the write + // loop has delivered the event, so we know stdout was written. + mirror := newMockDestination("test:mirror") + + r, w, err := os.Pipe() + require.NoError(t, err) + + old := os.Stdout + os.Stdout = w + t.Cleanup(func() { os.Stdout = old }) + + rec := newTestRecorder(&StdoutOutput{}, mirror) defer rec.Close() - var got string - done := make(chan struct{}) + rec.RecordEvent(recordCtx(), startupEvent) - go func() { - defer close(done) - got = captureStdout(t, func() { - rec.RecordEvent(recordCtx(), startupEvent) - // Give the write loop time to drain the event before we close - // the pipe and read. - time.Sleep(100 * time.Millisecond) - }) - }() + // Wait until the write loop has delivered the event to both outputs. + require.Eventually(t, func() bool { + return mirror.eventCount() == 1 + }, time.Second, 10*time.Millisecond) - <-done - require.Contains(t, got, "alertmanagerStartupEvent") + // Close the write end so io.Copy below can reach EOF. + require.NoError(t, w.Close()) + + var buf bytes.Buffer + _, err = io.Copy(&buf, r) + require.NoError(t, err) + + require.Contains(t, buf.String(), "alertmanagerStartupEvent") } // --- config tests. From b8e625201e3af99485698a4b34ac2ca0e98b65c3 Mon Sep 17 00:00:00 2001 From: Mihir Dixit Date: Wed, 24 Jun 2026 14:41:28 +0000 Subject: [PATCH 080/120] docs: add stdout_output section and log.format note Signed-off-by: Mihir Dixit --- docs/configuration.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/docs/configuration.md b/docs/configuration.md index a74eb2faf0..e202385c39 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -2151,6 +2151,10 @@ webhook_outputs: # Kafka outputs. kafka_outputs: [ - ... ] + +# Stdout outputs. +stdout_outputs: + [ - ... ] ``` #### `` @@ -2241,3 +2245,17 @@ topic: # connection uses PLAINTEXT. [ tls_config: ] ``` + +#### `` + +Writes each event as a single JSON line to stdout. This is the +recommended output for container deployments where the runtime log +driver (Docker, Kubernetes, etc.) captures stdout automatically. + +> **Note:** When using `stdout_outputs`, consider also passing +> `--log.format=json` to Alertmanager. Without it, Alertmanager's own +> log lines use logfmt while event records are JSON, producing two +> distinct formats on the same stream that may complicate downstream +> log parsing. + +This output type takes no additional configuration fields. From 963ed72d5a5a1d14280839283483fd858a205528 Mon Sep 17 00:00:00 2001 From: Siavash Safi Date: Mon, 6 Jul 2026 13:44:33 +0200 Subject: [PATCH 081/120] feat(api): add status v3 proto (#5317) - add proto file - run `buf generate` Signed-off-by: Siavash Safi --- api/status/v3/status.pb.go | 592 ++++++++++++++++++ .../v3/statusv3connect/status.connect.go | 123 ++++ buf.gen.yaml | 4 + buf.yaml | 10 +- go.mod | 1 + go.sum | 2 + internal/tools/go.mod | 3 +- internal/tools/go.sum | 4 +- proto/api/status/v3/status.proto | 75 +++ silence/silencepb/silence.pb.go | 2 +- 10 files changed, 808 insertions(+), 8 deletions(-) create mode 100644 api/status/v3/status.pb.go create mode 100644 api/status/v3/statusv3connect/status.connect.go create mode 100644 proto/api/status/v3/status.proto diff --git a/api/status/v3/status.pb.go b/api/status/v3/status.pb.go new file mode 100644 index 0000000000..996c0220d3 --- /dev/null +++ b/api/status/v3/status.pb.go @@ -0,0 +1,592 @@ +// Copyright The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.11 +// protoc (unknown) +// source: status/v3/status.proto + +package statusv3 + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type ClusterStatus_State int32 + +const ( + ClusterStatus_STATE_UNSPECIFIED ClusterStatus_State = 0 + ClusterStatus_STATE_DISABLED ClusterStatus_State = 1 + ClusterStatus_STATE_SETTLING ClusterStatus_State = 2 + ClusterStatus_STATE_READY ClusterStatus_State = 3 +) + +// Enum value maps for ClusterStatus_State. +var ( + ClusterStatus_State_name = map[int32]string{ + 0: "STATE_UNSPECIFIED", + 1: "STATE_DISABLED", + 2: "STATE_SETTLING", + 3: "STATE_READY", + } + ClusterStatus_State_value = map[string]int32{ + "STATE_UNSPECIFIED": 0, + "STATE_DISABLED": 1, + "STATE_SETTLING": 2, + "STATE_READY": 3, + } +) + +func (x ClusterStatus_State) Enum() *ClusterStatus_State { + p := new(ClusterStatus_State) + *p = x + return p +} + +func (x ClusterStatus_State) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (ClusterStatus_State) Descriptor() protoreflect.EnumDescriptor { + return file_status_v3_status_proto_enumTypes[0].Descriptor() +} + +func (ClusterStatus_State) Type() protoreflect.EnumType { + return &file_status_v3_status_proto_enumTypes[0] +} + +func (x ClusterStatus_State) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use ClusterStatus_State.Descriptor instead. +func (ClusterStatus_State) EnumDescriptor() ([]byte, []int) { + return file_status_v3_status_proto_rawDescGZIP(), []int{3, 0} +} + +// AlertmanagerStatus is the top-level status payload. +type AlertmanagerStatus struct { + state protoimpl.MessageState `protogen:"open.v1"` + VersionInfo *VersionInfo `protobuf:"bytes,1,opt,name=version_info,json=versionInfo,proto3" json:"version_info,omitempty"` + Config *AlertmanagerConfig `protobuf:"bytes,2,opt,name=config,proto3" json:"config,omitempty"` + Cluster *ClusterStatus `protobuf:"bytes,3,opt,name=cluster,proto3" json:"cluster,omitempty"` + StartTime *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AlertmanagerStatus) Reset() { + *x = AlertmanagerStatus{} + mi := &file_status_v3_status_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AlertmanagerStatus) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AlertmanagerStatus) ProtoMessage() {} + +func (x *AlertmanagerStatus) ProtoReflect() protoreflect.Message { + mi := &file_status_v3_status_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AlertmanagerStatus.ProtoReflect.Descriptor instead. +func (*AlertmanagerStatus) Descriptor() ([]byte, []int) { + return file_status_v3_status_proto_rawDescGZIP(), []int{0} +} + +func (x *AlertmanagerStatus) GetVersionInfo() *VersionInfo { + if x != nil { + return x.VersionInfo + } + return nil +} + +func (x *AlertmanagerStatus) GetConfig() *AlertmanagerConfig { + if x != nil { + return x.Config + } + return nil +} + +func (x *AlertmanagerStatus) GetCluster() *ClusterStatus { + if x != nil { + return x.Cluster + } + return nil +} + +func (x *AlertmanagerStatus) GetStartTime() *timestamppb.Timestamp { + if x != nil { + return x.StartTime + } + return nil +} + +// VersionInfo describes the running Alertmanager binary. +type VersionInfo struct { + state protoimpl.MessageState `protogen:"open.v1"` + Version string `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"` + Revision string `protobuf:"bytes,2,opt,name=revision,proto3" json:"revision,omitempty"` + Branch string `protobuf:"bytes,3,opt,name=branch,proto3" json:"branch,omitempty"` + BuildUser string `protobuf:"bytes,4,opt,name=build_user,json=buildUser,proto3" json:"build_user,omitempty"` + BuildDate string `protobuf:"bytes,5,opt,name=build_date,json=buildDate,proto3" json:"build_date,omitempty"` + GoVersion string `protobuf:"bytes,6,opt,name=go_version,json=goVersion,proto3" json:"go_version,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *VersionInfo) Reset() { + *x = VersionInfo{} + mi := &file_status_v3_status_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *VersionInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*VersionInfo) ProtoMessage() {} + +func (x *VersionInfo) ProtoReflect() protoreflect.Message { + mi := &file_status_v3_status_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use VersionInfo.ProtoReflect.Descriptor instead. +func (*VersionInfo) Descriptor() ([]byte, []int) { + return file_status_v3_status_proto_rawDescGZIP(), []int{1} +} + +func (x *VersionInfo) GetVersion() string { + if x != nil { + return x.Version + } + return "" +} + +func (x *VersionInfo) GetRevision() string { + if x != nil { + return x.Revision + } + return "" +} + +func (x *VersionInfo) GetBranch() string { + if x != nil { + return x.Branch + } + return "" +} + +func (x *VersionInfo) GetBuildUser() string { + if x != nil { + return x.BuildUser + } + return "" +} + +func (x *VersionInfo) GetBuildDate() string { + if x != nil { + return x.BuildDate + } + return "" +} + +func (x *VersionInfo) GetGoVersion() string { + if x != nil { + return x.GoVersion + } + return "" +} + +// AlertmanagerConfig carries the currently loaded configuration. +type AlertmanagerConfig struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Original YAML configuration as a single string. + Original string `protobuf:"bytes,1,opt,name=original,proto3" json:"original,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AlertmanagerConfig) Reset() { + *x = AlertmanagerConfig{} + mi := &file_status_v3_status_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AlertmanagerConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AlertmanagerConfig) ProtoMessage() {} + +func (x *AlertmanagerConfig) ProtoReflect() protoreflect.Message { + mi := &file_status_v3_status_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AlertmanagerConfig.ProtoReflect.Descriptor instead. +func (*AlertmanagerConfig) Descriptor() ([]byte, []int) { + return file_status_v3_status_proto_rawDescGZIP(), []int{2} +} + +func (x *AlertmanagerConfig) GetOriginal() string { + if x != nil { + return x.Original + } + return "" +} + +// ClusterStatus describes the local node's view of the gossip cluster. +type ClusterStatus struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Cluster name, when configured. + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + State ClusterStatus_State `protobuf:"varint,2,opt,name=state,proto3,enum=status.v3.ClusterStatus_State" json:"state,omitempty"` + Peers []*PeerStatus `protobuf:"bytes,3,rep,name=peers,proto3" json:"peers,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ClusterStatus) Reset() { + *x = ClusterStatus{} + mi := &file_status_v3_status_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ClusterStatus) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ClusterStatus) ProtoMessage() {} + +func (x *ClusterStatus) ProtoReflect() protoreflect.Message { + mi := &file_status_v3_status_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ClusterStatus.ProtoReflect.Descriptor instead. +func (*ClusterStatus) Descriptor() ([]byte, []int) { + return file_status_v3_status_proto_rawDescGZIP(), []int{3} +} + +func (x *ClusterStatus) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *ClusterStatus) GetState() ClusterStatus_State { + if x != nil { + return x.State + } + return ClusterStatus_STATE_UNSPECIFIED +} + +func (x *ClusterStatus) GetPeers() []*PeerStatus { + if x != nil { + return x.Peers + } + return nil +} + +// PeerStatus is a single peer in the gossip cluster. +type PeerStatus struct { + state protoimpl.MessageState `protogen:"open.v1"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *PeerStatus) Reset() { + *x = PeerStatus{} + mi := &file_status_v3_status_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *PeerStatus) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PeerStatus) ProtoMessage() {} + +func (x *PeerStatus) ProtoReflect() protoreflect.Message { + mi := &file_status_v3_status_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PeerStatus.ProtoReflect.Descriptor instead. +func (*PeerStatus) Descriptor() ([]byte, []int) { + return file_status_v3_status_proto_rawDescGZIP(), []int{4} +} + +func (x *PeerStatus) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *PeerStatus) GetAddress() string { + if x != nil { + return x.Address + } + return "" +} + +type GetStatusRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetStatusRequest) Reset() { + *x = GetStatusRequest{} + mi := &file_status_v3_status_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetStatusRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetStatusRequest) ProtoMessage() {} + +func (x *GetStatusRequest) ProtoReflect() protoreflect.Message { + mi := &file_status_v3_status_proto_msgTypes[5] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetStatusRequest.ProtoReflect.Descriptor instead. +func (*GetStatusRequest) Descriptor() ([]byte, []int) { + return file_status_v3_status_proto_rawDescGZIP(), []int{5} +} + +type GetStatusResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Status *AlertmanagerStatus `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetStatusResponse) Reset() { + *x = GetStatusResponse{} + mi := &file_status_v3_status_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetStatusResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetStatusResponse) ProtoMessage() {} + +func (x *GetStatusResponse) ProtoReflect() protoreflect.Message { + mi := &file_status_v3_status_proto_msgTypes[6] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetStatusResponse.ProtoReflect.Descriptor instead. +func (*GetStatusResponse) Descriptor() ([]byte, []int) { + return file_status_v3_status_proto_rawDescGZIP(), []int{6} +} + +func (x *GetStatusResponse) GetStatus() *AlertmanagerStatus { + if x != nil { + return x.Status + } + return nil +} + +var File_status_v3_status_proto protoreflect.FileDescriptor + +const file_status_v3_status_proto_rawDesc = "" + + "\n" + + "\x16status/v3/status.proto\x12\tstatus.v3\x1a\x1fgoogle/protobuf/timestamp.proto\"\xf5\x01\n" + + "\x12AlertmanagerStatus\x129\n" + + "\fversion_info\x18\x01 \x01(\v2\x16.status.v3.VersionInfoR\vversionInfo\x125\n" + + "\x06config\x18\x02 \x01(\v2\x1d.status.v3.AlertmanagerConfigR\x06config\x122\n" + + "\acluster\x18\x03 \x01(\v2\x18.status.v3.ClusterStatusR\acluster\x129\n" + + "\n" + + "start_time\x18\x04 \x01(\v2\x1a.google.protobuf.TimestampR\tstartTime\"\xb8\x01\n" + + "\vVersionInfo\x12\x18\n" + + "\aversion\x18\x01 \x01(\tR\aversion\x12\x1a\n" + + "\brevision\x18\x02 \x01(\tR\brevision\x12\x16\n" + + "\x06branch\x18\x03 \x01(\tR\x06branch\x12\x1d\n" + + "\n" + + "build_user\x18\x04 \x01(\tR\tbuildUser\x12\x1d\n" + + "\n" + + "build_date\x18\x05 \x01(\tR\tbuildDate\x12\x1d\n" + + "\n" + + "go_version\x18\x06 \x01(\tR\tgoVersion\"0\n" + + "\x12AlertmanagerConfig\x12\x1a\n" + + "\boriginal\x18\x01 \x01(\tR\boriginal\"\xdf\x01\n" + + "\rClusterStatus\x12\x12\n" + + "\x04name\x18\x01 \x01(\tR\x04name\x124\n" + + "\x05state\x18\x02 \x01(\x0e2\x1e.status.v3.ClusterStatus.StateR\x05state\x12+\n" + + "\x05peers\x18\x03 \x03(\v2\x15.status.v3.PeerStatusR\x05peers\"W\n" + + "\x05State\x12\x15\n" + + "\x11STATE_UNSPECIFIED\x10\x00\x12\x12\n" + + "\x0eSTATE_DISABLED\x10\x01\x12\x12\n" + + "\x0eSTATE_SETTLING\x10\x02\x12\x0f\n" + + "\vSTATE_READY\x10\x03\":\n" + + "\n" + + "PeerStatus\x12\x12\n" + + "\x04name\x18\x01 \x01(\tR\x04name\x12\x18\n" + + "\aaddress\x18\x02 \x01(\tR\aaddress\"\x12\n" + + "\x10GetStatusRequest\"J\n" + + "\x11GetStatusResponse\x125\n" + + "\x06status\x18\x01 \x01(\v2\x1d.status.v3.AlertmanagerStatusR\x06status2Y\n" + + "\rStatusService\x12H\n" + + "\tGetStatus\x12\x1b.status.v3.GetStatusRequest\x1a\x1c.status.v3.GetStatusResponse\"\x00B;Z9github.com/prometheus/alertmanager/api/status/v3;statusv3b\x06proto3" + +var ( + file_status_v3_status_proto_rawDescOnce sync.Once + file_status_v3_status_proto_rawDescData []byte +) + +func file_status_v3_status_proto_rawDescGZIP() []byte { + file_status_v3_status_proto_rawDescOnce.Do(func() { + file_status_v3_status_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_status_v3_status_proto_rawDesc), len(file_status_v3_status_proto_rawDesc))) + }) + return file_status_v3_status_proto_rawDescData +} + +var file_status_v3_status_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_status_v3_status_proto_msgTypes = make([]protoimpl.MessageInfo, 7) +var file_status_v3_status_proto_goTypes = []any{ + (ClusterStatus_State)(0), // 0: status.v3.ClusterStatus.State + (*AlertmanagerStatus)(nil), // 1: status.v3.AlertmanagerStatus + (*VersionInfo)(nil), // 2: status.v3.VersionInfo + (*AlertmanagerConfig)(nil), // 3: status.v3.AlertmanagerConfig + (*ClusterStatus)(nil), // 4: status.v3.ClusterStatus + (*PeerStatus)(nil), // 5: status.v3.PeerStatus + (*GetStatusRequest)(nil), // 6: status.v3.GetStatusRequest + (*GetStatusResponse)(nil), // 7: status.v3.GetStatusResponse + (*timestamppb.Timestamp)(nil), // 8: google.protobuf.Timestamp +} +var file_status_v3_status_proto_depIdxs = []int32{ + 2, // 0: status.v3.AlertmanagerStatus.version_info:type_name -> status.v3.VersionInfo + 3, // 1: status.v3.AlertmanagerStatus.config:type_name -> status.v3.AlertmanagerConfig + 4, // 2: status.v3.AlertmanagerStatus.cluster:type_name -> status.v3.ClusterStatus + 8, // 3: status.v3.AlertmanagerStatus.start_time:type_name -> google.protobuf.Timestamp + 0, // 4: status.v3.ClusterStatus.state:type_name -> status.v3.ClusterStatus.State + 5, // 5: status.v3.ClusterStatus.peers:type_name -> status.v3.PeerStatus + 1, // 6: status.v3.GetStatusResponse.status:type_name -> status.v3.AlertmanagerStatus + 6, // 7: status.v3.StatusService.GetStatus:input_type -> status.v3.GetStatusRequest + 7, // 8: status.v3.StatusService.GetStatus:output_type -> status.v3.GetStatusResponse + 8, // [8:9] is the sub-list for method output_type + 7, // [7:8] is the sub-list for method input_type + 7, // [7:7] is the sub-list for extension type_name + 7, // [7:7] is the sub-list for extension extendee + 0, // [0:7] is the sub-list for field type_name +} + +func init() { file_status_v3_status_proto_init() } +func file_status_v3_status_proto_init() { + if File_status_v3_status_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_status_v3_status_proto_rawDesc), len(file_status_v3_status_proto_rawDesc)), + NumEnums: 1, + NumMessages: 7, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_status_v3_status_proto_goTypes, + DependencyIndexes: file_status_v3_status_proto_depIdxs, + EnumInfos: file_status_v3_status_proto_enumTypes, + MessageInfos: file_status_v3_status_proto_msgTypes, + }.Build() + File_status_v3_status_proto = out.File + file_status_v3_status_proto_goTypes = nil + file_status_v3_status_proto_depIdxs = nil +} diff --git a/api/status/v3/statusv3connect/status.connect.go b/api/status/v3/statusv3connect/status.connect.go new file mode 100644 index 0000000000..bee0a5cfbc --- /dev/null +++ b/api/status/v3/statusv3connect/status.connect.go @@ -0,0 +1,123 @@ +// Copyright The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by protoc-gen-connect-go. DO NOT EDIT. +// +// Source: status/v3/status.proto + +package statusv3connect + +import ( + connect "connectrpc.com/connect" + context "context" + errors "errors" + v3 "github.com/prometheus/alertmanager/api/status/v3" + http "net/http" + strings "strings" +) + +// This is a compile-time assertion to ensure that this generated file and the connect package are +// compatible. If you get a compiler error that this constant is not defined, this code was +// generated with a version of connect newer than the one compiled into your binary. You can fix the +// problem by either regenerating this code with an older version of connect or updating the connect +// version compiled into your binary. +const _ = connect.IsAtLeastVersion1_13_0 + +const ( + // StatusServiceName is the fully-qualified name of the StatusService service. + StatusServiceName = "status.v3.StatusService" +) + +// These constants are the fully-qualified names of the RPCs defined in this package. They're +// exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route. +// +// Note that these are different from the fully-qualified method names used by +// google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to +// reflection-formatted method names, remove the leading slash and convert the remaining slash to a +// period. +const ( + // StatusServiceGetStatusProcedure is the fully-qualified name of the StatusService's GetStatus RPC. + StatusServiceGetStatusProcedure = "/status.v3.StatusService/GetStatus" +) + +// StatusServiceClient is a client for the status.v3.StatusService service. +type StatusServiceClient interface { + // GetStatus returns the Alertmanager instance and cluster status. + GetStatus(context.Context, *connect.Request[v3.GetStatusRequest]) (*connect.Response[v3.GetStatusResponse], error) +} + +// NewStatusServiceClient constructs a client for the status.v3.StatusService service. By default, +// it uses the Connect protocol with the binary Protobuf Codec, asks for gzipped responses, and +// sends uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the connect.WithGRPC() +// or connect.WithGRPCWeb() options. +// +// The URL supplied here should be the base URL for the Connect or gRPC server (for example, +// http://api.acme.com or https://acme.com/grpc). +func NewStatusServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) StatusServiceClient { + baseURL = strings.TrimRight(baseURL, "/") + statusServiceMethods := v3.File_status_v3_status_proto.Services().ByName("StatusService").Methods() + return &statusServiceClient{ + getStatus: connect.NewClient[v3.GetStatusRequest, v3.GetStatusResponse]( + httpClient, + baseURL+StatusServiceGetStatusProcedure, + connect.WithSchema(statusServiceMethods.ByName("GetStatus")), + connect.WithClientOptions(opts...), + ), + } +} + +// statusServiceClient implements StatusServiceClient. +type statusServiceClient struct { + getStatus *connect.Client[v3.GetStatusRequest, v3.GetStatusResponse] +} + +// GetStatus calls status.v3.StatusService.GetStatus. +func (c *statusServiceClient) GetStatus(ctx context.Context, req *connect.Request[v3.GetStatusRequest]) (*connect.Response[v3.GetStatusResponse], error) { + return c.getStatus.CallUnary(ctx, req) +} + +// StatusServiceHandler is an implementation of the status.v3.StatusService service. +type StatusServiceHandler interface { + // GetStatus returns the Alertmanager instance and cluster status. + GetStatus(context.Context, *connect.Request[v3.GetStatusRequest]) (*connect.Response[v3.GetStatusResponse], error) +} + +// NewStatusServiceHandler builds an HTTP handler from the service implementation. It returns the +// path on which to mount the handler and the handler itself. +// +// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf +// and JSON codecs. They also support gzip compression. +func NewStatusServiceHandler(svc StatusServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) { + statusServiceMethods := v3.File_status_v3_status_proto.Services().ByName("StatusService").Methods() + statusServiceGetStatusHandler := connect.NewUnaryHandler( + StatusServiceGetStatusProcedure, + svc.GetStatus, + connect.WithSchema(statusServiceMethods.ByName("GetStatus")), + connect.WithHandlerOptions(opts...), + ) + return "/status.v3.StatusService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case StatusServiceGetStatusProcedure: + statusServiceGetStatusHandler.ServeHTTP(w, r) + default: + http.NotFound(w, r) + } + }) +} + +// UnimplementedStatusServiceHandler returns CodeUnimplemented from all methods. +type UnimplementedStatusServiceHandler struct{} + +func (UnimplementedStatusServiceHandler) GetStatus(context.Context, *connect.Request[v3.GetStatusRequest]) (*connect.Response[v3.GetStatusResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("status.v3.StatusService.GetStatus is not implemented")) +} diff --git a/buf.gen.yaml b/buf.gen.yaml index b36195aa92..59cf119c50 100644 --- a/buf.gen.yaml +++ b/buf.gen.yaml @@ -4,3 +4,7 @@ plugins: out: . opt: - module=github.com/prometheus/alertmanager + - local: ['go', 'tool', '-modfile=internal/tools/go.mod', 'protoc-gen-connect-go'] + out: . + opt: + - module=github.com/prometheus/alertmanager diff --git a/buf.yaml b/buf.yaml index 120e8f7c1f..225e01263f 100644 --- a/buf.yaml +++ b/buf.yaml @@ -1,11 +1,13 @@ # For details on buf.yaml configuration, visit https://buf.build/docs/configuration/v2/buf-yaml version: v2 modules: - - path: nflog/nflogpb - name: prometheus/alertmanager/nflog - path: cluster/clusterpb name: prometheus/alertmanager/cluster - - path: silence/silencepb - name: prometheus/alertmanager/silence - path: eventrecorder/eventrecorderpb name: prometheus/alertmanager/eventrecorder + - path: nflog/nflogpb + name: prometheus/alertmanager/nflog + - path: proto/api + name: prometheus/alertmanager/api + - path: silence/silencepb + name: prometheus/alertmanager/silence diff --git a/go.mod b/go.mod index 9027f3f733..1fba38a619 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/prometheus/alertmanager go 1.25.0 require ( + connectrpc.com/connect v1.20.0 github.com/KimMachineGun/automemlimit v0.7.5 github.com/alecthomas/kingpin/v2 v2.4.0 github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b diff --git a/go.sum b/go.sum index ae52174cfe..e4e7a846fe 100644 --- a/go.sum +++ b/go.sum @@ -53,6 +53,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= +connectrpc.com/connect v1.20.0 h1:6TNDAB+WeNd2uolWNlYczB5E0KNNaVMNUEx8JEUsPmQ= +connectrpc.com/connect v1.20.0/go.mod h1:A2ygJrukXwWy32vkCAAHNVguZrqZ+jeZ9rGRnGR4dN4= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= diff --git a/internal/tools/go.mod b/internal/tools/go.mod index 2845e65e46..094ddae24b 100644 --- a/internal/tools/go.mod +++ b/internal/tools/go.mod @@ -3,6 +3,7 @@ module github.com/prometheus/prometheus/internal/tools go 1.25.0 tool ( + connectrpc.com/connect/cmd/protoc-gen-connect-go github.com/bufbuild/buf/cmd/buf github.com/go-swagger/go-swagger/cmd/swagger google.golang.org/protobuf/cmd/protoc-gen-go @@ -24,7 +25,7 @@ require ( buf.build/go/spdx v0.2.0 // indirect buf.build/go/standard v0.1.0 // indirect cel.dev/expr v0.25.1 // indirect - connectrpc.com/connect v1.19.1 // indirect + connectrpc.com/connect v1.20.0 // indirect connectrpc.com/otelconnect v0.9.0 // indirect dario.cat/mergo v1.0.2 // indirect github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect diff --git a/internal/tools/go.sum b/internal/tools/go.sum index 405289d35b..99bc13632d 100644 --- a/internal/tools/go.sum +++ b/internal/tools/go.sum @@ -28,8 +28,8 @@ buf.build/go/standard v0.1.0 h1:g98T9IyvAl0vS3Pq8iVk6Cvj2ZiFvoUJRtfyGa0120U= buf.build/go/standard v0.1.0/go.mod h1:PiqpHz/7ZFq+kqvYhc/SK3lxFIB9N/aiH2CFC2JHIQg= cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4= cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= -connectrpc.com/connect v1.19.1 h1:R5M57z05+90EfEvCY1b7hBxDVOUl45PrtXtAV2fOC14= -connectrpc.com/connect v1.19.1/go.mod h1:tN20fjdGlewnSFeZxLKb0xwIZ6ozc3OQs2hTXy4du9w= +connectrpc.com/connect v1.20.0 h1:6TNDAB+WeNd2uolWNlYczB5E0KNNaVMNUEx8JEUsPmQ= +connectrpc.com/connect v1.20.0/go.mod h1:A2ygJrukXwWy32vkCAAHNVguZrqZ+jeZ9rGRnGR4dN4= connectrpc.com/otelconnect v0.9.0 h1:NggB3pzRC3pukQWaYbRHJulxuXvmCKCKkQ9hbrHAWoA= connectrpc.com/otelconnect v0.9.0/go.mod h1:AEkVLjCPXra+ObGFCOClcJkNjS7zPaQSqvO0lCyjfZc= dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= diff --git a/proto/api/status/v3/status.proto b/proto/api/status/v3/status.proto new file mode 100644 index 0000000000..d3c3ff6662 --- /dev/null +++ b/proto/api/status/v3/status.proto @@ -0,0 +1,75 @@ +// Copyright The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package status.v3; + +import "google/protobuf/timestamp.proto"; + +option go_package = "github.com/prometheus/alertmanager/api/status/v3;statusv3"; + +// StatusService exposes Alertmanager instance and cluster status. +service StatusService { + // GetStatus returns the Alertmanager instance and cluster status. + rpc GetStatus(GetStatusRequest) returns (GetStatusResponse) {} +} + +// AlertmanagerStatus is the top-level status payload. +message AlertmanagerStatus { + VersionInfo version_info = 1; + AlertmanagerConfig config = 2; + ClusterStatus cluster = 3; + google.protobuf.Timestamp start_time = 4; +} + +// VersionInfo describes the running Alertmanager binary. +message VersionInfo { + string version = 1; + string revision = 2; + string branch = 3; + string build_user = 4; + string build_date = 5; + string go_version = 6; +} + +// AlertmanagerConfig carries the currently loaded configuration. +message AlertmanagerConfig { + // Original YAML configuration as a single string. + string original = 1; +} + +// ClusterStatus describes the local node's view of the gossip cluster. +message ClusterStatus { + enum State { + STATE_UNSPECIFIED = 0; + STATE_DISABLED = 1; + STATE_SETTLING = 2; + STATE_READY = 3; + } + // Cluster name, when configured. + string name = 1; + State state = 2; + repeated PeerStatus peers = 3; +} + +// PeerStatus is a single peer in the gossip cluster. +message PeerStatus { + string name = 1; + string address = 2; +} + +message GetStatusRequest {} +message GetStatusResponse { + AlertmanagerStatus status = 1; +} diff --git a/silence/silencepb/silence.pb.go b/silence/silencepb/silence.pb.go index 210abde2fc..4694f7989c 100644 --- a/silence/silencepb/silence.pb.go +++ b/silence/silencepb/silence.pb.go @@ -276,7 +276,7 @@ type Silence struct { // Receiver matchers apply to the labels of receivers, not alerts. At // least one set of receiver matchers must match for a silence to apply // to alerts that are sent to that receiver. Unlike alert label matcher, - // a silence with no receiver matchers applies to ALL recievers. + // a silence with no receiver matchers applies to ALL receivers. ReceiverMatcherSets []*MatcherSet `protobuf:"bytes,12,rep,name=receiver_matcher_sets,json=receiverMatcherSets,proto3" json:"receiver_matcher_sets,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache From 348474742571396d8fe0c73c1bcc86f7a8a7fcc4 Mon Sep 17 00:00:00 2001 From: mihir-dixit2k27 <143348248+mihir-dixit2k27@users.noreply.github.com> Date: Fri, 10 Jul 2026 19:58:28 +0530 Subject: [PATCH 082/120] msteamsv2: inherit global proxy_url into partial http_config (#5379) Fixes #5374 When a receiver's `http_config` is `nil`, the global config is copied in as before. When a receiver has a partial `http_config` but no proxy set, the global proxy settings are now merged in so they take effect. **Which user-facing changes does this PR introduce?** ```release-notes [BUGFIX] msteamsv2: Fix global proxy_url not being inherited when a receiver defines a partial http_config without an explicit proxy. ``` **Description** Fixes a bug in `msteamsv2_configs` where a receiver-level `http_config` containing only partial settings (e.g. `http_headers`) would cause the global `proxy_url` to be silently ignored. Previously, the config wiring used `cmp.Or(msteamsv2.HTTPConfig, c.Global.HTTPConfig)` which only falls back to the global config when the receiver's `http_config` is completely absent. If a user sets any field under `http_config` in the receiver, the global proxy is dropped entirely. **Config that triggers the bug:** ```yaml global: http_config: proxy_url: "http://proxy.example.com:3128" receivers: - name: teams msteamsv2_configs: - webhook_url: "" http_config: http_headers: teams_channel_id: values: - my-channel-id ``` **Implementation notes:** - If `http_config` is `nil` on the receiver, the global config is deep-copied in (same behaviour as before, but avoids shared-pointer mutation). - If `http_config` is set but has no proxy, only the `ProxyConfig` field is inherited from global - the rest of the receiver's config is left untouched. - If the receiver explicitly sets its own `proxy_url`, it is not overwritten. - No new dependencies introduced. --------- Signed-off-by: Mihir Dixit --- config/config.go | 11 ++++- config/config_test.go | 100 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+), 1 deletion(-) diff --git a/config/config.go b/config/config.go index 80cc5783f9..ff19d5a8a9 100644 --- a/config/config.go +++ b/config/config.go @@ -592,7 +592,16 @@ func (c *Config) UnmarshalYAML(unmarshal func(any) error) error { if msteamsv2 == nil { return errors.New("missing msteamsv2 config") } - msteamsv2.HTTPConfig = cmp.Or(msteamsv2.HTTPConfig, c.Global.HTTPConfig) + if msteamsv2.HTTPConfig == nil { + // copy the global config so receiver-level mutations don't affect it + httpCfg := *c.Global.HTTPConfig + msteamsv2.HTTPConfig = &httpCfg + } else if msteamsv2.HTTPConfig.ProxyURL.URL == nil { + // receiver has a partial http_config but no proxy_url set, + // so inherit only the proxy_url from global, leaving any + // other proxy fields the receiver set (NoProxy, etc.) intact + msteamsv2.HTTPConfig.ProxyURL = c.Global.HTTPConfig.ProxyURL + } if msteamsv2.WebhookURL == nil && len(msteamsv2.WebhookURLFile) == 0 { return errors.New("no msteamsv2 webhook URL or URLFile provided") } diff --git a/config/config_test.go b/config/config_test.go index 5487af80f7..b096c67bc8 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -1797,3 +1797,103 @@ func TestMattermostNoWebhookURL(t *testing.T) { t.Errorf("Expected: %s\nGot: %s", "missing webhook_url or webhook_url_file on mattermost_config", err.Error()) } } + +// TestMSTeamsV2GlobalProxyInheritedWhenNoLocalHTTPConfig checks that the global +// proxy_url is used when the receiver has no http_config set. +func TestMSTeamsV2GlobalProxyInheritedWhenNoLocalHTTPConfig(t *testing.T) { + in := ` +global: + http_config: + proxy_url: "http://proxy.example.com:3128" + +route: + receiver: teams + +receivers: +- name: teams + msteamsv2_configs: + - webhook_url: "https://example.webhook.office.com/webhookb2/test" +` + cfg, err := Load(in) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + rc := cfg.Receivers[0].MSTeamsV2Configs[0] + if rc.HTTPConfig == nil { + t.Fatal("expected HTTPConfig to be non-nil") + } + if rc.HTTPConfig.ProxyURL.URL == nil { + t.Fatal("expected global proxy_url to be inherited") + } + if got := rc.HTTPConfig.ProxyURL.String(); got != "http://proxy.example.com:3128" { + t.Errorf("expected proxy_url %q, got %q", "http://proxy.example.com:3128", got) + } +} + +// TestMSTeamsV2GlobalProxyInheritedWhenPartialHTTPConfig checks that the global +// proxy_url is used when the receiver sets a partial http_config with no proxy. +func TestMSTeamsV2GlobalProxyInheritedWhenPartialHTTPConfig(t *testing.T) { + in := ` +global: + http_config: + proxy_url: "http://proxy.example.com:3128" + +route: + receiver: teams + +receivers: +- name: teams + msteamsv2_configs: + - webhook_url: "https://example.webhook.office.com/webhookb2/test" + http_config: + follow_redirects: true +` + cfg, err := Load(in) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + rc := cfg.Receivers[0].MSTeamsV2Configs[0] + if rc.HTTPConfig == nil { + t.Fatal("expected HTTPConfig to be non-nil") + } + if rc.HTTPConfig.ProxyURL.URL == nil { + t.Fatal("expected global proxy_url to be inherited into partial http_config") + } + if got := rc.HTTPConfig.ProxyURL.String(); got != "http://proxy.example.com:3128" { + t.Errorf("expected proxy_url %q, got %q", "http://proxy.example.com:3128", got) + } +} + +// TestMSTeamsV2LocalProxyTakesPrecedence checks that a receiver-level proxy_url +// is not overwritten by the global one. +func TestMSTeamsV2LocalProxyTakesPrecedence(t *testing.T) { + in := ` +global: + http_config: + proxy_url: "http://global-proxy.example.com:3128" + +route: + receiver: teams + +receivers: +- name: teams + msteamsv2_configs: + - webhook_url: "https://example.webhook.office.com/webhookb2/test" + http_config: + proxy_url: "http://local-proxy.example.com:8080" +` + cfg, err := Load(in) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + rc := cfg.Receivers[0].MSTeamsV2Configs[0] + if rc.HTTPConfig.ProxyURL.URL == nil { + t.Fatal("expected proxy_url to be set") + } + if got := rc.HTTPConfig.ProxyURL.String(); got != "http://local-proxy.example.com:8080" { + t.Errorf("expected local proxy_url %q, got %q", "http://local-proxy.example.com:8080", got) + } +} From 2512db5dcced023fc2039440f04bf1f296d3c909 Mon Sep 17 00:00:00 2001 From: Guido Trotter Date: Sun, 12 Jul 2026 20:47:22 +0100 Subject: [PATCH 083/120] [deps] upgrade to cenkalti/backoff/v5 (#5378) The newest version is /v7, but we already have v5 in the codebase via OTel, so upgrading to v5 allows us to depend into just one copy of this library. #### Pull Request Checklist Please check all the applicable boxes. - Is this a breaking change? - [X] My changes do not break the existing cluster messages - [X] My changes do not break the existing api - [X] I have signed-off my commits - [X] I will follow [best practices for contributing to this project](https://docs.github.com/en/get-started/exploring-projects-on-github/contributing-to-open-source) #### Which user-facing changes does this PR introduce? ```release-notes NONE ``` Signed-off-by: Guido Trotter Co-authored-by: Guido Trotter --- go.mod | 3 +-- go.sum | 2 -- notify/retry_stage.go | 5 +++-- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 1fba38a619..c3e42f7ac7 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/sns v1.40.3 github.com/aws/aws-sdk-go-v2/service/sts v1.43.5 github.com/aws/smithy-go v1.27.3 - github.com/cenkalti/backoff/v4 v4.3.0 + github.com/cenkalti/backoff/v5 v5.0.3 github.com/cespare/xxhash/v2 v2.3.0 github.com/coder/quartz v0.3.1 github.com/coreos/go-systemd/v22 v22.7.0 @@ -76,7 +76,6 @@ require ( github.com/aws/aws-sdk-go-v2/service/sso v1.31.5 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.8 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 // indirect diff --git a/go.sum b/go.sum index e4e7a846fe..84d74550e3 100644 --- a/go.sum +++ b/go.sum @@ -114,8 +114,6 @@ 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/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= -github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= diff --git a/notify/retry_stage.go b/notify/retry_stage.go index 4c5723a9a6..1fe08cd385 100644 --- a/notify/retry_stage.go +++ b/notify/retry_stage.go @@ -19,7 +19,7 @@ import ( "log/slog" "time" - "github.com/cenkalti/backoff/v4" + "github.com/cenkalti/backoff/v5" "github.com/prometheus/common/model" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" @@ -108,8 +108,9 @@ func (r RetryStage) exec(ctx context.Context, l *slog.Logger, alerts ...*alert.A sent = alerts } + // backoff/v5's ExponentialBackOff never returns Stop from NextBackOff, so + // the ticker retries indefinitely until the context is canceled. b := backoff.NewExponentialBackOff() - b.MaxElapsedTime = 0 // Always retry. tick := backoff.NewTicker(b) defer tick.Stop() From 5aa4e8f1ea0d54eb97b91acf12fdaf0dd82ca942 Mon Sep 17 00:00:00 2001 From: Guido Trotter Date: Tue, 11 Nov 2025 01:18:29 -0500 Subject: [PATCH 084/120] Remove quartz/clock from silence This is only used by tests, and we can instead use testing/synctest to get stable mocked time and time.Sleep for instant time advancement in testing/synctest. Add synctest to TestSilencesSnapshot as it needs stable times even if it doesn't advance them Signed-off-by: Guido Trotter --- silence/silence.go | 10 +- silence/silence_bench_test.go | 35 +- silence/silence_test.go | 1673 ++++++++++++++++----------------- test/util.go | 27 + 4 files changed, 872 insertions(+), 873 deletions(-) create mode 100644 test/util.go diff --git a/silence/silence.go b/silence/silence.go index f1f185de7c..c584901937 100644 --- a/silence/silence.go +++ b/silence/silence.go @@ -32,7 +32,6 @@ import ( "sync" "time" - "github.com/coder/quartz" uuid "github.com/google/uuid" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" @@ -334,8 +333,6 @@ func (s *Silencer) PostGC(ff model.Fingerprints) { // Silences holds a silence state that can be modified, queried, and snapshot. type Silences struct { - clock quartz.Clock - logger *slog.Logger metrics *metrics retention time.Duration @@ -529,7 +526,6 @@ func New(o Options) (*Silences, error) { } s := &Silences{ - clock: quartz.NewReal(), mi: make(matcherIndex, 512), vi: make(versionIndex, 0, 512), logger: promslog.NewNopLogger(), @@ -570,7 +566,7 @@ func New(o Options) (*Silences, error) { } func (s *Silences) nowUTC() time.Time { - return s.clock.Now().UTC() + return time.Now().UTC() } // updateSizeMetrics updates the size metrics for state, matcher index, and version index. @@ -592,7 +588,7 @@ func (s *Silences) Maintenance(interval time.Duration, snapf string, stopc <-cha s.logger.Error("interval or stop signal are missing - not running maintenance") return } - t := s.clock.NewTicker(interval) + t := time.NewTicker(interval) defer t.Stop() var doMaintenance MaintenanceFunc @@ -630,7 +626,7 @@ func (s *Silences) Maintenance(interval time.Duration, snapf string, stopc <-cha s.metrics.maintenanceErrorsTotal.Inc() return err } - s.logger.Debug("Maintenance done", "duration", s.clock.Since(start), "size", size) + s.logger.Debug("Maintenance done", "duration", time.Since(start), "size", size) return nil } diff --git a/silence/silence_bench_test.go b/silence/silence_bench_test.go index 414ece8fbe..5a4ca237db 100644 --- a/silence/silence_bench_test.go +++ b/silence/silence_bench_test.go @@ -22,7 +22,6 @@ import ( "testing" "time" - "github.com/coder/quartz" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/common/model" "github.com/prometheus/common/promslog" @@ -71,9 +70,7 @@ func benchmarkMutes(b *testing.B, totalSilences, matchingSilences int) { silences, err := New(Options{Metrics: prometheus.NewRegistry()}) require.NoError(b, err) - clock := quartz.NewMock(b).WithLogger(quartz.NoOpLogger) - silences.clock = clock - now := clock.Now() + now := time.Now() // Calculate interval to intersperse matching silences var interval int @@ -95,7 +92,7 @@ func benchmarkMutes(b *testing.B, totalSilences, matchingSilences int) { Pattern: "bar", }}, StartsAt: timestamppb.New(now), - EndsAt: timestamppb.New(now.Add(time.Minute)), + EndsAt: timestamppb.New(now.Add(24 * time.Hour)), } matchingCreated++ } else { @@ -107,7 +104,7 @@ func benchmarkMutes(b *testing.B, totalSilences, matchingSilences int) { Pattern: "job" + strconv.Itoa(i), }}, StartsAt: timestamppb.New(now), - EndsAt: timestamppb.New(now.Add(time.Minute)), + EndsAt: timestamppb.New(now.Add(24 * time.Hour)), } } require.NoError(b, silences.Set(b.Context(), s)) @@ -140,9 +137,7 @@ func BenchmarkMutesIncremental(b *testing.B) { silences, err := New(Options{Metrics: prometheus.NewRegistry()}) require.NoError(b, err) - clock := quartz.NewMock(b).WithLogger(quartz.NoOpLogger) - silences.clock = clock - now := clock.Now() + now := time.Now() // Create base set of silences - most don't match, some do // This simulates a realistic production scenario @@ -263,9 +258,7 @@ func benchmarkQuery(b *testing.B, numSilences int) { s, err := New(Options{Metrics: prometheus.NewRegistry()}) require.NoError(b, err) - clock := quartz.NewMock(b).WithLogger(quartz.NoOpLogger) - s.clock = clock - now := clock.Now() + now := time.Now() lset := model.LabelSet{"aaaa": "AAAA", "bbbb": "BBBB", "cccc": "CCCC"} @@ -332,9 +325,7 @@ func benchmarkQueryParallel(b *testing.B, numSilences int) { s, err := New(Options{Metrics: prometheus.NewRegistry()}) require.NoError(b, err) - clock := quartz.NewMock(b).WithLogger(quartz.NoOpLogger) - s.clock = clock - now := clock.Now() + now := time.Now() lset := model.LabelSet{"aaaa": "AAAA", "bbbb": "BBBB", "cccc": "CCCC"} @@ -413,9 +404,7 @@ func benchmarkQueryWithConcurrentAdds(b *testing.B, initialSilences int, addRati s, err := New(Options{Metrics: prometheus.NewRegistry()}) require.NoError(b, err) - clock := quartz.NewMock(b).WithLogger(quartz.NoOpLogger) - s.clock = clock - now := clock.Now() + now := time.Now() lset := model.LabelSet{"aaaa": "AAAA", "bbbb": "BBBB", "cccc": "CCCC"} @@ -508,9 +497,7 @@ func benchmarkMutesParallel(b *testing.B, numSilences int) { silences, err := New(Options{Metrics: prometheus.NewRegistry()}) require.NoError(b, err) - clock := quartz.NewMock(b).WithLogger(quartz.NoOpLogger) - silences.clock = clock - now := clock.Now() + now := time.Now() // Create silences that will match the alert for range numSilences { @@ -521,7 +508,7 @@ func benchmarkMutesParallel(b *testing.B, numSilences int) { Pattern: "bar", }}, StartsAt: timestamppb.New(now), - EndsAt: timestamppb.New(now.Add(time.Minute)), + EndsAt: timestamppb.New(now.Add(24 * time.Hour)), } require.NoError(b, silences.Set(b.Context(), s)) } @@ -567,8 +554,7 @@ func BenchmarkGC(b *testing.B) { func benchmarkGC(b *testing.B, numSilences int, expiredRatio float64) { b.ReportAllocs() - clock := quartz.NewMock(b).WithLogger(quartz.NoOpLogger) - now := clock.Now() + now := time.Now() numExpired := int(float64(numSilences) * expiredRatio) numActive := numSilences - numExpired @@ -623,7 +609,6 @@ func benchmarkGC(b *testing.B, numSilences int, expiredRatio float64) { Metrics: prometheus.NewRegistry(), }) require.NoError(b, err) - s.clock = clock for _, sil := range sils { s.st[sil.Silence.Id] = sil diff --git a/silence/silence_test.go b/silence/silence_test.go index c0830447a1..17aa2c603b 100644 --- a/silence/silence_test.go +++ b/silence/silence_test.go @@ -19,15 +19,14 @@ import ( "log/slog" "math/rand" "os" - "runtime" "sort" "strings" "sync" "sync/atomic" "testing" + "testing/synctest" "time" - "github.com/coder/quartz" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/testutil" "github.com/prometheus/common/model" @@ -43,6 +42,7 @@ import ( "github.com/prometheus/alertmanager/marker" "github.com/prometheus/alertmanager/matcher/compat" pb "github.com/prometheus/alertmanager/silence/silencepb" + "github.com/prometheus/alertmanager/test" ) // checkMutes checks that the marker recorded the expected silenced state @@ -122,7 +122,6 @@ func TestSilenceGCOverTime(t *testing.T) { t.Run("GC does not remove active silences", func(t *testing.T) { s, err := New(Options{Metrics: prometheus.NewRegistry()}) require.NoError(t, err) - s.clock = quartz.NewMock(t) now := s.nowUTC() initialState := state{ "1": &pb.MeshSilence{Silence: &pb.Silence{Id: "1"}, ExpiresAt: timestamppb.New(now)}, @@ -142,11 +141,10 @@ func TestSilenceGCOverTime(t *testing.T) { requireStatesEqual(t, want, s.st) }) - t.Run("GC does not leak cache entries", func(t *testing.T) { + test.SyncTestRun("GC removes expired silences", t, func(t *testing.T) { s, err := New(Options{Metrics: prometheus.NewRegistry()}) require.NoError(t, err) - clock := quartz.NewMock(t) - s.clock = clock + now := time.Now().UTC() sil1 := &pb.Silence{ MatcherSets: []*pb.MatcherSet{{ Matchers: []*pb.Matcher{{ @@ -155,15 +153,15 @@ func TestSilenceGCOverTime(t *testing.T) { Pattern: "bar", }}, }}, - StartsAt: timestamppb.New(clock.Now()), - EndsAt: timestamppb.New(clock.Now().Add(time.Minute)), + StartsAt: timestamppb.New(now), + EndsAt: timestamppb.New(now.Add(time.Minute)), } require.NoError(t, s.Set(t.Context(), sil1)) require.Len(t, s.st, 1) require.Len(t, s.mi, 1) // Move time forward and both silence and cache entry should be garbage // collected. - clock.Advance(time.Minute) + time.Sleep(time.Minute) n, err := s.GC() require.NoError(t, err) require.Equal(t, 1, n) @@ -171,11 +169,10 @@ func TestSilenceGCOverTime(t *testing.T) { require.Empty(t, s.mi) }) - t.Run("replacing a silences does not leak cache entries", func(t *testing.T) { + test.SyncTestRun("replacing a silence does not leak cache entries", t, func(t *testing.T) { s, err := New(Options{Metrics: prometheus.NewRegistry()}) require.NoError(t, err) - clock := quartz.NewMock(t) - s.clock = clock + now := time.Now().UTC() sil1 := &pb.Silence{ MatcherSets: []*pb.MatcherSet{{ Matchers: []*pb.Matcher{{ @@ -184,8 +181,8 @@ func TestSilenceGCOverTime(t *testing.T) { Pattern: "bar", }}, }}, - StartsAt: timestamppb.New(clock.Now()), - EndsAt: timestamppb.New(clock.Now().Add(time.Minute)), + StartsAt: timestamppb.New(now), + EndsAt: timestamppb.New(now.Add(time.Minute)), } require.NoError(t, s.Set(t.Context(), sil1)) require.Len(t, s.st, 1) @@ -204,7 +201,7 @@ func TestSilenceGCOverTime(t *testing.T) { require.Len(t, s.mi, 2) // Move time forward and both silence and cache entry should be garbage // collected. - clock.Advance(time.Minute) + time.Sleep(time.Minute) n, err := s.GC() require.NoError(t, err) require.Equal(t, 2, n) @@ -214,11 +211,10 @@ func TestSilenceGCOverTime(t *testing.T) { // This test checks for a memory leak that occurred in the matcher cache when // updating an existing silence. - t.Run("updating a silence does not leak cache entries", func(t *testing.T) { + test.SyncTestRun("updating a silence does not leak cache entries", t, func(t *testing.T) { s, err := New(Options{Metrics: prometheus.NewRegistry()}) require.NoError(t, err) - clock := quartz.NewMock(t) - s.clock = clock + now := s.nowUTC() sil1 := &pb.Silence{ Id: "1", MatcherSets: []*pb.MatcherSet{{ @@ -228,10 +224,10 @@ func TestSilenceGCOverTime(t *testing.T) { Pattern: "bar", }}, }}, - StartsAt: timestamppb.New(clock.Now()), - EndsAt: timestamppb.New(clock.Now().Add(time.Minute)), + StartsAt: timestamppb.New(now), + EndsAt: timestamppb.New(now.Add(time.Minute)), } - s.st["1"] = &pb.MeshSilence{Silence: sil1, ExpiresAt: timestamppb.New(clock.Now().Add(time.Minute))} + s.st["1"] = &pb.MeshSilence{Silence: sil1, ExpiresAt: timestamppb.New(now.Add(time.Minute))} s.indexSilence(sil1) require.Len(t, s.mi, 1) // must clone sil1 before updating it. @@ -245,7 +241,7 @@ func TestSilenceGCOverTime(t *testing.T) { require.Len(t, s.mi, 1) // Move time forward and both silence and cache entry should be garbage // collected. - clock.Advance(time.Minute) + time.Sleep(time.Minute) n, err := s.GC() require.NoError(t, err) require.Equal(t, 1, n) @@ -253,15 +249,13 @@ func TestSilenceGCOverTime(t *testing.T) { require.Empty(t, s.mi) }) - t.Run("GC collects silences in multiple rounds", func(t *testing.T) { + test.SyncTestRun("GC collects silences in multiple rounds", t, func(t *testing.T) { s, err := New(Options{ Metrics: prometheus.NewRegistry(), Retention: time.Hour, }) - clock := quartz.NewMock(t) - s.clock = clock require.NoError(t, err) - now := s.nowUTC().UTC() + now := s.nowUTC() matcher := &pb.Matcher{ Type: pb.Matcher_EQUAL, @@ -350,7 +344,7 @@ func TestSilenceGCOverTime(t *testing.T) { require.Len(t, s.mi, 60) // Advance time to 91 minutes - Group 1 should be GC'd - clock.Advance(91 * time.Minute) + time.Sleep(91 * time.Minute) n, err = s.GC() require.NoError(t, err) require.Equal(t, 10, n) @@ -358,7 +352,7 @@ func TestSilenceGCOverTime(t *testing.T) { require.Len(t, s.mi, 50) // Advance time to 106 minutes - Group 2 should be GC'd - clock.Advance(15 * time.Minute) + time.Sleep(15 * time.Minute) n, err = s.GC() require.NoError(t, err) require.Equal(t, 10, n) @@ -366,7 +360,7 @@ func TestSilenceGCOverTime(t *testing.T) { require.Len(t, s.mi, 40) // Advance time to 121 minutes - Group 3 should be GC'd - clock.Advance(15 * time.Minute) + time.Sleep(15 * time.Minute) n, err = s.GC() require.NoError(t, err) require.Equal(t, 10, n) @@ -379,13 +373,11 @@ func TestSilenceGCOverTime(t *testing.T) { } }) - t.Run("GC continues and removes erroneous silences", func(t *testing.T) { + test.SyncTestRun("GC continues and removes erroneous silences", t, func(t *testing.T) { reg := prometheus.NewRegistry() s, err := New(Options{Metrics: reg}) require.NoError(t, err) - clock := quartz.NewMock(t) - s.clock = clock - now := clock.Now() + now := time.Now().UTC() // Create a valid silence validSil := &pb.Silence{ @@ -449,151 +441,155 @@ func TestSilenceGCOverTime(t *testing.T) { } func TestSilencesSnapshot(t *testing.T) { - // Check whether storing and loading the snapshot is symmetric. - now := quartz.NewMock(t).Now().UTC() - - cases := []struct { - entries []*pb.MeshSilence - }{ - { - entries: []*pb.MeshSilence{ - { - Silence: &pb.Silence{ - Id: "3be80475-e219-4ee7-b6fc-4b65114e362f", - MatcherSets: []*pb.MatcherSet{{ - Matchers: []*pb.Matcher{ - {Name: "label1", Pattern: "val1", Type: pb.Matcher_EQUAL}, - {Name: "label2", Pattern: "val.+", Type: pb.Matcher_REGEXP}, - }, - }}, - StartsAt: timestamppb.New(now), - EndsAt: timestamppb.New(now), - UpdatedAt: timestamppb.New(now), + synctest.Test(t, func(t *testing.T) { + // Check whether storing and loading the snapshot is symmetric. + now := time.Now().UTC() + + cases := []struct { + entries []*pb.MeshSilence + }{ + { + entries: []*pb.MeshSilence{ + { + Silence: &pb.Silence{ + Id: "3be80475-e219-4ee7-b6fc-4b65114e362f", + MatcherSets: []*pb.MatcherSet{{ + Matchers: []*pb.Matcher{ + {Name: "label1", Pattern: "val1", Type: pb.Matcher_EQUAL}, + {Name: "label2", Pattern: "val.+", Type: pb.Matcher_REGEXP}, + }, + }}, + StartsAt: timestamppb.New(now), + EndsAt: timestamppb.New(now), + UpdatedAt: timestamppb.New(now), + }, + ExpiresAt: timestamppb.New(now), }, - ExpiresAt: timestamppb.New(now), - }, - { - Silence: &pb.Silence{ - Id: "3dfb2528-59ce-41eb-b465-f875a4e744a4", - MatcherSets: []*pb.MatcherSet{{ - Matchers: []*pb.Matcher{ - {Name: "label1", Pattern: "val1", Type: pb.Matcher_NOT_EQUAL}, - {Name: "label2", Pattern: "val.+", Type: pb.Matcher_NOT_REGEXP}, - }, - }}, - StartsAt: timestamppb.New(now), - EndsAt: timestamppb.New(now), - UpdatedAt: timestamppb.New(now), + { + Silence: &pb.Silence{ + Id: "3dfb2528-59ce-41eb-b465-f875a4e744a4", + MatcherSets: []*pb.MatcherSet{{ + Matchers: []*pb.Matcher{ + {Name: "label1", Pattern: "val1", Type: pb.Matcher_NOT_EQUAL}, + {Name: "label2", Pattern: "val.+", Type: pb.Matcher_NOT_REGEXP}, + }, + }}, + StartsAt: timestamppb.New(now), + EndsAt: timestamppb.New(now), + UpdatedAt: timestamppb.New(now), + }, + ExpiresAt: timestamppb.New(now), }, - ExpiresAt: timestamppb.New(now), - }, - { - Silence: &pb.Silence{ - Id: "4b1e760d-182c-4980-b873-c1a6827c9817", - MatcherSets: []*pb.MatcherSet{{ - Matchers: []*pb.Matcher{ - {Name: "label1", Pattern: "val1", Type: pb.Matcher_EQUAL}, - }, - }}, - StartsAt: timestamppb.New(now.Add(time.Hour)), - EndsAt: timestamppb.New(now.Add(2 * time.Hour)), - UpdatedAt: timestamppb.New(now), + { + Silence: &pb.Silence{ + Id: "4b1e760d-182c-4980-b873-c1a6827c9817", + MatcherSets: []*pb.MatcherSet{{ + Matchers: []*pb.Matcher{ + {Name: "label1", Pattern: "val1", Type: pb.Matcher_EQUAL}, + }, + }}, + StartsAt: timestamppb.New(now.Add(time.Hour)), + EndsAt: timestamppb.New(now.Add(2 * time.Hour)), + UpdatedAt: timestamppb.New(now), + }, + ExpiresAt: timestamppb.New(now.Add(24 * time.Hour)), }, - ExpiresAt: timestamppb.New(now.Add(24 * time.Hour)), }, }, - }, - } + } - for _, c := range cases { - f, err := os.CreateTemp(t.TempDir(), "snapshot") - require.NoError(t, err, "creating temp file failed") + for _, c := range cases { + f, err := os.CreateTemp(t.TempDir(), "snapshot") + require.NoError(t, err, "creating temp file failed") - s1 := &Silences{st: state{}, metrics: newMetrics(nil, nil)} - // Setup internal state manually. - for _, e := range c.entries { - s1.st[e.Silence.Id] = e - } - _, err = s1.Snapshot(f) - require.NoError(t, err, "creating snapshot failed") - - require.NoError(t, f.Close(), "closing snapshot file failed") - - f, err = os.Open(f.Name()) - require.NoError(t, err, "opening snapshot file failed") - - // Check again against new nlog instance. - s2 := &Silences{mi: matcherIndex{}, st: state{}} - err = s2.loadSnapshot(f) - require.NoError(t, err, "error loading snapshot") - require.Len(t, s2.st, len(s1.st), "state length mismatch after loading snapshot") - for id, expected := range s1.st { - actual, ok := s2.st[id] - require.True(t, ok, "silence %s missing from loaded state", id) - require.True(t, proto.Equal(expected, actual), "silence %s mismatch after loading snapshot", id) - } + s1 := &Silences{st: state{}, metrics: newMetrics(nil, nil)} + // Setup internal state manually. + for _, e := range c.entries { + s1.st[e.Silence.Id] = e + } + _, err = s1.Snapshot(f) + require.NoError(t, err, "creating snapshot failed") + + require.NoError(t, f.Close(), "closing snapshot file failed") + + f, err = os.Open(f.Name()) + require.NoError(t, err, "opening snapshot file failed") + + // Check again against new nlog instance. + s2 := &Silences{mi: matcherIndex{}, st: state{}} + err = s2.loadSnapshot(f) + require.NoError(t, err, "error loading snapshot") + require.Len(t, s2.st, len(s1.st), "state length mismatch after loading snapshot") + for id, expected := range s1.st { + actual, ok := s2.st[id] + require.True(t, ok, "silence %s missing from loaded state", id) + require.True(t, proto.Equal(expected, actual), "silence %s mismatch after loading snapshot", id) + } - require.NoError(t, f.Close(), "closing snapshot file failed") - } + require.NoError(t, f.Close(), "closing snapshot file failed") + } + }) } // This tests a regression introduced by https://github.com/prometheus/alertmanager/pull/2689. func TestSilences_Maintenance_DefaultMaintenanceFuncDoesntCrash(t *testing.T) { - f, err := os.CreateTemp(t.TempDir(), "snapshot") - require.NoError(t, err, "creating temp file failed") - clock := quartz.NewMock(t) - s := &Silences{st: state{}, logger: promslog.NewNopLogger(), clock: clock, metrics: newMetrics(nil, nil)} - stopc := make(chan struct{}) + synctest.Test(t, func(t *testing.T) { + f, err := os.CreateTemp(t.TempDir(), "snapshot") + require.NoError(t, err, "creating temp file failed") + s := &Silences{st: state{}, logger: promslog.NewNopLogger(), metrics: newMetrics(nil, nil)} + stopc := make(chan struct{}) - done := make(chan struct{}) - go func() { - s.Maintenance(100*time.Millisecond, f.Name(), stopc, nil) - close(done) - }() - runtime.Gosched() + done := make(chan struct{}) + go func() { + s.Maintenance(100*time.Millisecond, f.Name(), stopc, nil) + close(done) + }() + gosched() - clock.Advance(100 * time.Millisecond) - close(stopc) + time.Sleep(100 * time.Millisecond) + close(stopc) - <-done + <-done + }) } func TestSilences_Maintenance_SupportsCustomCallback(t *testing.T) { - f, err := os.CreateTemp(t.TempDir(), "snapshot") - require.NoError(t, err, "creating temp file failed") - clock := quartz.NewMock(t) - reg := prometheus.NewRegistry() - s := &Silences{st: state{}, logger: promslog.NewNopLogger(), clock: clock} - s.metrics = newMetrics(reg, s) - stopc := make(chan struct{}) - - var calls atomic.Int32 - var wg sync.WaitGroup - - wg.Go(func() { - s.Maintenance(10*time.Second, f.Name(), stopc, func() (int64, error) { - calls.Add(1) - return 0, nil + synctest.Test(t, func(t *testing.T) { + f, err := os.CreateTemp(t.TempDir(), "snapshot") + require.NoError(t, err, "creating temp file failed") + reg := prometheus.NewRegistry() + s := &Silences{st: state{}, logger: promslog.NewNopLogger()} + s.metrics = newMetrics(reg, s) + stopc := make(chan struct{}) + + var calls atomic.Int32 + var wg sync.WaitGroup + + wg.Go(func() { + s.Maintenance(10*time.Second, f.Name(), stopc, func() (int64, error) { + calls.Add(1) + return 0, nil + }) }) - }) - gosched() + gosched() - // Before the first tick, no maintenance executed. - clock.Advance(9 * time.Second) - require.EqualValues(t, 0, calls.Load()) + // Before the first tick, no maintenance executed. + time.Sleep(9 * time.Second) + require.EqualValues(t, 0, calls.Load()) - // Tick once. - clock.Advance(1 * time.Second) - require.Eventually(t, func() bool { return calls.Load() == 1 }, 5*time.Second, time.Second) + // Tick once. + time.Sleep(1 * time.Second) + synctest.Wait() + require.EqualValues(t, 1, calls.Load()) - // Stop the maintenance loop. We should get exactly one more execution of the maintenance func. - close(stopc) - wg.Wait() + // Stop the maintenance loop. We should get exactly one more execution of the maintenance func. + close(stopc) + wg.Wait() - require.EqualValues(t, 2, calls.Load()) + require.EqualValues(t, 2, calls.Load()) - // Check the maintenance metrics. - require.NoError(t, testutil.GatherAndCompare(reg, bytes.NewBufferString(` + // Check the maintenance metrics. + require.NoError(t, testutil.GatherAndCompare(reg, bytes.NewBufferString(` # HELP alertmanager_silences_maintenance_errors_total How many maintenances were executed for silences that failed. # TYPE alertmanager_silences_maintenance_errors_total counter alertmanager_silences_maintenance_errors_total 0 @@ -601,6 +597,7 @@ alertmanager_silences_maintenance_errors_total 0 # TYPE alertmanager_silences_maintenance_total counter alertmanager_silences_maintenance_total 2 `), "alertmanager_silences_maintenance_total", "alertmanager_silences_maintenance_errors_total")) + }) } func TestSilencesSetSilence(t *testing.T) { @@ -610,9 +607,6 @@ func TestSilencesSetSilence(t *testing.T) { }) require.NoError(t, err) - clock := quartz.NewMock(t) - s.clock = clock - nowpb := s.nowUTC() sil := &pb.Silence{ @@ -672,215 +666,215 @@ func TestSilencesSetSilence(t *testing.T) { } func TestSilenceSet(t *testing.T) { - s, err := New(Options{ - Metrics: prometheus.NewRegistry(), - Retention: time.Hour, - }) - require.NoError(t, err) + synctest.Test(t, func(t *testing.T) { + s, err := New(Options{ + Metrics: prometheus.NewRegistry(), + Retention: time.Hour, + }) + require.NoError(t, err) - clock := quartz.NewMock(t) - s.clock = clock - start1 := s.nowUTC() + start1 := s.nowUTC() - // Insert silence with fixed start time. - sil1 := &pb.Silence{ - MatcherSets: []*pb.MatcherSet{{ - Matchers: []*pb.Matcher{{Name: "a", Pattern: "b"}}, - }}, - StartsAt: timestamppb.New(start1.Add(2 * time.Minute)), - EndsAt: timestamppb.New(start1.Add(5 * time.Minute)), - } - versionBeforeOp := s.Version() - require.NoError(t, s.Set(t.Context(), sil1)) - require.NotEmpty(t, sil1.Id) - require.NotEqual(t, versionBeforeOp, s.Version()) + // Insert silence with fixed start time. + sil1 := &pb.Silence{ + MatcherSets: []*pb.MatcherSet{{ + Matchers: []*pb.Matcher{{Name: "a", Pattern: "b"}}, + }}, + StartsAt: timestamppb.New(start1.Add(2 * time.Minute)), + EndsAt: timestamppb.New(start1.Add(5 * time.Minute)), + } + versionBeforeOp := s.Version() + require.NoError(t, s.Set(t.Context(), sil1)) + require.NotEmpty(t, sil1.Id) + require.NotEqual(t, versionBeforeOp, s.Version()) - want := state{ - sil1.Id: &pb.MeshSilence{ - Silence: &pb.Silence{ - Id: sil1.Id, - MatcherSets: []*pb.MatcherSet{{ - Matchers: []*pb.Matcher{{Name: "a", Pattern: "b"}}, - }}, - StartsAt: timestamppb.New(start1.Add(2 * time.Minute)), - EndsAt: timestamppb.New(start1.Add(5 * time.Minute)), - UpdatedAt: timestamppb.New(start1), + want := state{ + sil1.Id: &pb.MeshSilence{ + Silence: &pb.Silence{ + Id: sil1.Id, + MatcherSets: []*pb.MatcherSet{{ + Matchers: []*pb.Matcher{{Name: "a", Pattern: "b"}}, + }}, + StartsAt: timestamppb.New(start1.Add(2 * time.Minute)), + EndsAt: timestamppb.New(start1.Add(5 * time.Minute)), + UpdatedAt: timestamppb.New(start1), + }, + ExpiresAt: timestamppb.New(start1.Add(5*time.Minute + s.retention)), }, - ExpiresAt: timestamppb.New(start1.Add(5*time.Minute + s.retention)), - }, - } - requireStatesEqual(t, want, s.st, "unexpected state after silence creation") + } + requireStatesEqual(t, want, s.st, "unexpected state after silence creation") - // Insert silence with unset start time. Must be set to now. - clock.Advance(time.Minute) - start2 := s.nowUTC() + // Insert silence with unset start time. Must be set to now. + time.Sleep(time.Minute) + start2 := s.nowUTC() - sil2 := &pb.Silence{ - MatcherSets: []*pb.MatcherSet{{ - Matchers: []*pb.Matcher{{Name: "a", Pattern: "b"}}, - }}, - EndsAt: timestamppb.New(start2.Add(1 * time.Minute)), - } - versionBeforeOp = s.Version() - require.NoError(t, s.Set(t.Context(), sil2)) - require.NotEmpty(t, sil2.Id) - require.NotEqual(t, versionBeforeOp, s.Version()) + sil2 := &pb.Silence{ + MatcherSets: []*pb.MatcherSet{{ + Matchers: []*pb.Matcher{{Name: "a", Pattern: "b"}}, + }}, + EndsAt: timestamppb.New(start2.Add(1 * time.Minute)), + } + versionBeforeOp = s.Version() + require.NoError(t, s.Set(t.Context(), sil2)) + require.NotEmpty(t, sil2.Id) + require.NotEqual(t, versionBeforeOp, s.Version()) - want = state{ - sil1.Id: want[sil1.Id], - sil2.Id: &pb.MeshSilence{ - Silence: &pb.Silence{ - Id: sil2.Id, - MatcherSets: []*pb.MatcherSet{{ - Matchers: []*pb.Matcher{{Name: "a", Pattern: "b"}}, - }}, - StartsAt: timestamppb.New(start2), - EndsAt: timestamppb.New(start2.Add(1 * time.Minute)), - UpdatedAt: timestamppb.New(start2), + want = state{ + sil1.Id: want[sil1.Id], + sil2.Id: &pb.MeshSilence{ + Silence: &pb.Silence{ + Id: sil2.Id, + MatcherSets: []*pb.MatcherSet{{ + Matchers: []*pb.Matcher{{Name: "a", Pattern: "b"}}, + }}, + StartsAt: timestamppb.New(start2), + EndsAt: timestamppb.New(start2.Add(1 * time.Minute)), + UpdatedAt: timestamppb.New(start2), + }, + ExpiresAt: timestamppb.New(start2.Add(1*time.Minute + s.retention)), }, - ExpiresAt: timestamppb.New(start2.Add(1*time.Minute + s.retention)), - }, - } - requireStatesEqual(t, want, s.st, "unexpected state after silence creation") - - // Should be able to update silence without modifications. It is expected to - // keep the same ID. - sil3 := cloneSilence(sil2) - versionBeforeOp = s.Version() - require.NoError(t, s.Set(t.Context(), sil3)) - require.Equal(t, sil2.Id, sil3.Id) - require.Equal(t, versionBeforeOp, s.Version()) - - // Should be able to update silence with comment. It is also expected to - // keep the same ID. - sil4 := cloneSilence(sil3) - sil4.Comment = "c" - versionBeforeOp = s.Version() - require.NoError(t, s.Set(t.Context(), sil4)) - require.Equal(t, sil3.Id, sil4.Id) - require.Equal(t, versionBeforeOp, s.Version()) - - // Extend sil4 to expire at a later time. This should not expire the - // existing silence, and so should also keep the same ID. - clock.Advance(time.Minute) - start5 := s.nowUTC() - sil5 := cloneSilence(sil4) - sil5.EndsAt = timestamppb.New(start5.Add(100 * time.Minute)) - versionBeforeOp = s.Version() - require.NoError(t, s.Set(t.Context(), sil5)) - require.Equal(t, sil4.Id, sil5.Id) - want = state{ - sil1.Id: want[sil1.Id], - sil2.Id: &pb.MeshSilence{ - Silence: &pb.Silence{ - Id: sil2.Id, - MatcherSets: []*pb.MatcherSet{{ - Matchers: []*pb.Matcher{{Name: "a", Pattern: "b"}}, - }}, - StartsAt: timestamppb.New(start2), - EndsAt: timestamppb.New(start5.Add(100 * time.Minute)), - UpdatedAt: timestamppb.New(start5), - Comment: "c", + } + requireStatesEqual(t, want, s.st, "unexpected state after silence creation") + + // Should be able to update silence without modifications. It is expected to + // keep the same ID. + sil3 := cloneSilence(sil2) + versionBeforeOp = s.Version() + require.NoError(t, s.Set(t.Context(), sil3)) + require.Equal(t, sil2.Id, sil3.Id) + require.Equal(t, versionBeforeOp, s.Version()) + + // Should be able to update silence with comment. It is also expected to + // keep the same ID. + sil4 := cloneSilence(sil3) + sil4.Comment = "c" + versionBeforeOp = s.Version() + require.NoError(t, s.Set(t.Context(), sil4)) + require.Equal(t, sil3.Id, sil4.Id) + require.Equal(t, versionBeforeOp, s.Version()) + + // Extend sil4 to expire at a later time. This should not expire the + // existing silence, and so should also keep the same ID. + time.Sleep(time.Minute) + start5 := s.nowUTC() + sil5 := cloneSilence(sil4) + sil5.EndsAt = timestamppb.New(start5.Add(100 * time.Minute)) + versionBeforeOp = s.Version() + require.NoError(t, s.Set(t.Context(), sil5)) + require.Equal(t, sil4.Id, sil5.Id) + want = state{ + sil1.Id: want[sil1.Id], + sil2.Id: &pb.MeshSilence{ + Silence: &pb.Silence{ + Id: sil2.Id, + MatcherSets: []*pb.MatcherSet{{ + Matchers: []*pb.Matcher{{Name: "a", Pattern: "b"}}, + }}, + StartsAt: timestamppb.New(start2), + EndsAt: timestamppb.New(start5.Add(100 * time.Minute)), + UpdatedAt: timestamppb.New(start5), + Comment: "c", + }, + ExpiresAt: timestamppb.New(start5.Add(100*time.Minute + s.retention)), }, - ExpiresAt: timestamppb.New(start5.Add(100*time.Minute + s.retention)), - }, - } - requireStatesEqual(t, want, s.st, "unexpected state after silence creation") - require.Equal(t, versionBeforeOp, s.Version()) - - // Replace the silence sil5 with another silence with different matchers. - // Unlike previous updates, changing the matchers for an existing silence - // will expire the existing silence and create a new silence. The new - // silence is expected to have a different ID to preserve the history of - // the previous silence. - clock.Advance(time.Minute) - start6 := s.nowUTC() - - sil6 := cloneSilence(sil5) - sil6.MatcherSets = []*pb.MatcherSet{{ - Matchers: []*pb.Matcher{{Name: "a", Pattern: "c"}}, - }} - versionBeforeOp = s.Version() - require.NoError(t, s.Set(t.Context(), sil6)) - require.NotEqual(t, sil5.Id, sil6.Id) - want = state{ - sil1.Id: want[sil1.Id], - sil2.Id: &pb.MeshSilence{ - Silence: &pb.Silence{ - Id: sil2.Id, - MatcherSets: []*pb.MatcherSet{{ - Matchers: []*pb.Matcher{{Name: "a", Pattern: "b"}}, - }}, - StartsAt: timestamppb.New(start2), - EndsAt: timestamppb.New(start6), // Expired - UpdatedAt: timestamppb.New(start6), - Comment: "c", + } + requireStatesEqual(t, want, s.st, "unexpected state after silence creation") + require.Equal(t, versionBeforeOp, s.Version()) + + // Replace the silence sil5 with another silence with different matchers. + // Unlike previous updates, changing the matchers for an existing silence + // will expire the existing silence and create a new silence. The new + // silence is expected to have a different ID to preserve the history of + // the previous silence. + time.Sleep(time.Minute) + start6 := s.nowUTC() + + sil6 := cloneSilence(sil5) + sil6.MatcherSets = []*pb.MatcherSet{{ + Matchers: []*pb.Matcher{{Name: "a", Pattern: "c"}}, + }} + versionBeforeOp = s.Version() + require.NoError(t, s.Set(t.Context(), sil6)) + require.NotEqual(t, sil5.Id, sil6.Id) + want = state{ + sil1.Id: want[sil1.Id], + sil2.Id: &pb.MeshSilence{ + Silence: &pb.Silence{ + Id: sil2.Id, + MatcherSets: []*pb.MatcherSet{{ + Matchers: []*pb.Matcher{{Name: "a", Pattern: "b"}}, + }}, + StartsAt: timestamppb.New(start2), + EndsAt: timestamppb.New(start6), // Expired + UpdatedAt: timestamppb.New(start6), + Comment: "c", + }, + ExpiresAt: timestamppb.New(start6.Add(s.retention)), }, - ExpiresAt: timestamppb.New(start6.Add(s.retention)), - }, - sil6.Id: &pb.MeshSilence{ - Silence: &pb.Silence{ - Id: sil6.Id, - MatcherSets: []*pb.MatcherSet{{ - Matchers: []*pb.Matcher{{Name: "a", Pattern: "c"}}, - }}, - StartsAt: timestamppb.New(start6), - EndsAt: timestamppb.New(start5.Add(100 * time.Minute)), - UpdatedAt: timestamppb.New(start6), - Comment: "c", + sil6.Id: &pb.MeshSilence{ + Silence: &pb.Silence{ + Id: sil6.Id, + MatcherSets: []*pb.MatcherSet{{ + Matchers: []*pb.Matcher{{Name: "a", Pattern: "c"}}, + }}, + StartsAt: timestamppb.New(start6), + EndsAt: timestamppb.New(start5.Add(100 * time.Minute)), + UpdatedAt: timestamppb.New(start6), + Comment: "c", + }, + ExpiresAt: timestamppb.New(start5.Add(100*time.Minute + s.retention)), }, - ExpiresAt: timestamppb.New(start5.Add(100*time.Minute + s.retention)), - }, - } - requireStatesEqual(t, want, s.st, "unexpected state after silence creation") - require.NotEqual(t, versionBeforeOp, s.Version()) - - // Re-create the silence that we just replaced. Changing the start time, - // just like changing the matchers, creates a new silence with a different - // ID. This is again to preserve the history of the original silence. - clock.Advance(time.Minute) - start7 := s.nowUTC() - sil7 := cloneSilence(sil5) - sil7.StartsAt = timestamppb.New(start1) - sil7.EndsAt = timestamppb.New(start1.Add(5 * time.Minute)) - versionBeforeOp = s.Version() - require.NoError(t, s.Set(t.Context(), sil7)) - require.NotEqual(t, sil2.Id, sil7.Id) - want = state{ - sil1.Id: want[sil1.Id], - sil2.Id: want[sil2.Id], - sil6.Id: want[sil6.Id], - sil7.Id: &pb.MeshSilence{ - Silence: &pb.Silence{ - Id: sil7.Id, - MatcherSets: []*pb.MatcherSet{{ - Matchers: []*pb.Matcher{{Name: "a", Pattern: "b"}}, - }}, - StartsAt: timestamppb.New(start7), // New silences have their start time set to "now" when created. - EndsAt: timestamppb.New(start1.Add(5 * time.Minute)), - UpdatedAt: timestamppb.New(start7), - Comment: "c", + } + requireStatesEqual(t, want, s.st, "unexpected state after silence creation") + require.NotEqual(t, versionBeforeOp, s.Version()) + + // Re-create the silence that we just replaced. Changing the start time, + // just like changing the matchers, creates a new silence with a different + // ID. This is again to preserve the history of the original silence. + time.Sleep(time.Minute) + start7 := s.nowUTC() + sil7 := cloneSilence(sil5) + sil7.StartsAt = timestamppb.New(start1) + sil7.EndsAt = timestamppb.New(start1.Add(5 * time.Minute)) + versionBeforeOp = s.Version() + require.NoError(t, s.Set(t.Context(), sil7)) + require.NotEqual(t, sil2.Id, sil7.Id) + want = state{ + sil1.Id: want[sil1.Id], + sil2.Id: want[sil2.Id], + sil6.Id: want[sil6.Id], + sil7.Id: &pb.MeshSilence{ + Silence: &pb.Silence{ + Id: sil7.Id, + MatcherSets: []*pb.MatcherSet{{ + Matchers: []*pb.Matcher{{Name: "a", Pattern: "b"}}, + }}, + StartsAt: timestamppb.New(start7), // New silences have their start time set to "now" when created. + EndsAt: timestamppb.New(start1.Add(5 * time.Minute)), + UpdatedAt: timestamppb.New(start7), + Comment: "c", + }, + ExpiresAt: timestamppb.New(start1.Add(5*time.Minute + s.retention)), }, - ExpiresAt: timestamppb.New(start1.Add(5*time.Minute + s.retention)), - }, - } - requireStatesEqual(t, want, s.st, "unexpected state after silence creation") - require.NotEqual(t, versionBeforeOp, s.Version()) - - // Updating an existing silence with an invalid silence should not expire - // the original silence. - clock.Advance(time.Millisecond) - sil8 := cloneSilence(sil7) - sil8.EndsAt = nil // nil represents zero timestamp - versionBeforeOp = s.Version() - require.EqualError(t, s.Set(t.Context(), sil8), "invalid silence: invalid zero end timestamp") - - // sil7 should not be expired because the update failed. - clock.Advance(time.Millisecond) - sil7, err = s.QueryOne(t.Context(), QIDs(sil7.Id)) - require.NoError(t, err) - require.Equal(t, SilenceStateActive, getState(sil7, s.nowUTC())) - require.Equal(t, versionBeforeOp, s.Version()) + } + requireStatesEqual(t, want, s.st, "unexpected state after silence creation") + require.NotEqual(t, versionBeforeOp, s.Version()) + + // Updating an existing silence with an invalid silence should not expire + // the original silence. + time.Sleep(time.Millisecond) + sil8 := cloneSilence(sil7) + sil8.EndsAt = nil // nil represents zero timestamp + versionBeforeOp = s.Version() + require.EqualError(t, s.Set(t.Context(), sil8), "invalid silence: invalid zero end timestamp") + + // sil7 should not be expired because the update failed. + time.Sleep(time.Millisecond) + sil7, err = s.QueryOne(t.Context(), QIDs(sil7.Id)) + require.NoError(t, err) + require.Equal(t, SilenceStateActive, getState(sil7, s.nowUTC())) + require.Equal(t, versionBeforeOp, s.Version()) + }) } func TestSilenceLimits(t *testing.T) { @@ -1034,66 +1028,65 @@ func TestSilenceNoLimits(t *testing.T) { } func TestSetActiveSilence(t *testing.T) { - s, err := New(Options{ - Metrics: prometheus.NewRegistry(), - Retention: time.Hour, - }) - require.NoError(t, err) + synctest.Test(t, func(t *testing.T) { + s, err := New(Options{ + Metrics: prometheus.NewRegistry(), + Retention: time.Hour, + }) + require.NoError(t, err) - clock := quartz.NewMock(t) - s.clock = clock - now := clock.Now() + now := time.Now().UTC() - startsAt := now.Add(-1 * time.Minute) - endsAt := now.Add(5 * time.Minute) - // Insert silence with fixed start time. - sil1 := &pb.Silence{ - MatcherSets: []*pb.MatcherSet{{ - Matchers: []*pb.Matcher{{Name: "a", Pattern: "b"}}, - }}, - StartsAt: timestamppb.New(startsAt), - EndsAt: timestamppb.New(endsAt), - } - require.NoError(t, s.Set(t.Context(), sil1)) + startsAt := now.Add(-1 * time.Minute) + endsAt := now.Add(5 * time.Minute) + // Insert silence with fixed start time. + sil1 := &pb.Silence{ + MatcherSets: []*pb.MatcherSet{{ + Matchers: []*pb.Matcher{{Name: "a", Pattern: "b"}}, + }}, + StartsAt: timestamppb.New(startsAt), + EndsAt: timestamppb.New(endsAt), + } + require.NoError(t, s.Set(t.Context(), sil1)) - // Update silence with 2 extra nanoseconds so the "seconds" part should not change + // Update silence with 2 extra nanoseconds so the "seconds" part should not change - newStartsAt := now.Add(2 * time.Nanosecond) - newEndsAt := endsAt.Add(2 * time.Minute) + newStartsAt := now.Add(2 * time.Nanosecond) + newEndsAt := endsAt.Add(2 * time.Minute) - sil2 := cloneSilence(sil1) - sil2.Id = sil1.Id - sil2.StartsAt = timestamppb.New(newStartsAt) - sil2.EndsAt = timestamppb.New(newEndsAt) + sil2 := cloneSilence(sil1) + sil2.Id = sil1.Id + sil2.StartsAt = timestamppb.New(newStartsAt) + sil2.EndsAt = timestamppb.New(newEndsAt) - clock.Advance(time.Minute) - now = s.nowUTC() - require.NoError(t, s.Set(t.Context(), sil2)) - require.Equal(t, sil1.Id, sil2.Id) + time.Sleep(time.Minute) + now = s.nowUTC() + require.NoError(t, s.Set(t.Context(), sil2)) + require.Equal(t, sil1.Id, sil2.Id) - want := state{ - sil2.Id: &pb.MeshSilence{ - Silence: &pb.Silence{ - Id: sil1.Id, - MatcherSets: []*pb.MatcherSet{{ - Matchers: []*pb.Matcher{{Name: "a", Pattern: "b"}}, - }}, - StartsAt: timestamppb.New(newStartsAt), - EndsAt: timestamppb.New(newEndsAt), - UpdatedAt: timestamppb.New(now), + want := state{ + sil2.Id: &pb.MeshSilence{ + Silence: &pb.Silence{ + Id: sil1.Id, + MatcherSets: []*pb.MatcherSet{{ + Matchers: []*pb.Matcher{{Name: "a", Pattern: "b"}}, + }}, + StartsAt: timestamppb.New(newStartsAt), + EndsAt: timestamppb.New(newEndsAt), + UpdatedAt: timestamppb.New(now), + }, + ExpiresAt: timestamppb.New(newEndsAt.Add(s.retention)), }, - ExpiresAt: timestamppb.New(newEndsAt.Add(s.retention)), - }, - } - requireStatesEqual(t, want, s.st, "unexpected state after silence creation") + } + requireStatesEqual(t, want, s.st, "unexpected state after silence creation") + }) } func TestSilencesSetFail(t *testing.T) { s, err := New(Options{Metrics: prometheus.NewRegistry()}) require.NoError(t, err) - clock := quartz.NewMock(t) - s.clock = clock + now := time.Now() cases := []struct { s *pb.Silence @@ -1105,7 +1098,7 @@ func TestSilencesSetFail(t *testing.T) { MatcherSets: []*pb.MatcherSet{{ Matchers: []*pb.Matcher{{Name: "a", Pattern: "b"}}, }}, - EndsAt: timestamppb.New(clock.Now().Add(5 * time.Minute)), + EndsAt: timestamppb.New(now.Add(5 * time.Minute)), }, err: ErrNotFound.Error(), }, { @@ -1119,7 +1112,7 @@ func TestSilencesSetFail(t *testing.T) { } func TestQState(t *testing.T) { - now := time.Now().UTC() + now := time.Now() cases := []struct { sil *pb.Silence @@ -2033,35 +2026,106 @@ func TestSilenceCanUpdate(t *testing.T) { } func TestSilenceExpire(t *testing.T) { - s, err := New(Options{Metrics: prometheus.NewRegistry(), Retention: time.Hour}) - require.NoError(t, err) + synctest.Test(t, func(t *testing.T) { + s, err := New(Options{Metrics: prometheus.NewRegistry(), Retention: time.Hour}) + require.NoError(t, err) - clock := quartz.NewMock(t) - s.clock = clock - now := s.nowUTC() + now := s.nowUTC() - m := &pb.Matcher{Type: pb.Matcher_EQUAL, Name: "a", Pattern: "b"} + m := &pb.Matcher{Type: pb.Matcher_EQUAL, Name: "a", Pattern: "b"} - s.st = state{ - "pending": &pb.MeshSilence{Silence: &pb.Silence{ + s.st = state{ + "pending": &pb.MeshSilence{Silence: &pb.Silence{ + Id: "pending", + MatcherSets: []*pb.MatcherSet{{ + Matchers: []*pb.Matcher{m}, + }}, + StartsAt: timestamppb.New(now.Add(time.Minute)), + EndsAt: timestamppb.New(now.Add(time.Hour)), + UpdatedAt: timestamppb.New(now.Add(-time.Hour)), + }}, + "active": &pb.MeshSilence{Silence: &pb.Silence{ + Id: "active", + MatcherSets: []*pb.MatcherSet{{ + Matchers: []*pb.Matcher{m}, + }}, + StartsAt: timestamppb.New(now.Add(-time.Minute)), + EndsAt: timestamppb.New(now.Add(time.Hour)), + UpdatedAt: timestamppb.New(now.Add(-time.Hour)), + }}, + "expired": &pb.MeshSilence{Silence: &pb.Silence{ + Id: "expired", + MatcherSets: []*pb.MatcherSet{{ + Matchers: []*pb.Matcher{m}, + }}, + StartsAt: timestamppb.New(now.Add(-time.Hour)), + EndsAt: timestamppb.New(now.Add(-time.Minute)), + UpdatedAt: timestamppb.New(now.Add(-time.Hour)), + }}, + } + s.vi = versionIndex{ + silenceVersion{id: "pending"}, + silenceVersion{id: "active"}, + silenceVersion{id: "expired"}, + } + count, err := s.CountState(t.Context(), SilenceStatePending) + require.NoError(t, err) + require.Equal(t, 1, count) + + count, err = s.CountState(t.Context(), SilenceStateExpired) + require.NoError(t, err) + require.Equal(t, 1, count) + + require.NoError(t, s.Expire(t.Context(), "pending")) + require.NoError(t, s.Expire(t.Context(), "active")) + + require.NoError(t, s.Expire(t.Context(), "expired")) + + sil, err := s.QueryOne(t.Context(), QIDs("pending")) + require.NoError(t, err) + expectedPending := &pb.Silence{ Id: "pending", MatcherSets: []*pb.MatcherSet{{ Matchers: []*pb.Matcher{m}, }}, - StartsAt: timestamppb.New(now.Add(time.Minute)), - EndsAt: timestamppb.New(now.Add(time.Hour)), - UpdatedAt: timestamppb.New(now.Add(-time.Hour)), - }}, - "active": &pb.MeshSilence{Silence: &pb.Silence{ + StartsAt: timestamppb.New(now), + EndsAt: timestamppb.New(now), + UpdatedAt: timestamppb.New(now), + } + require.True(t, proto.Equal(expectedPending, sil), "pending silence mismatch") + + // Let time pass... + time.Sleep(time.Second) + + count, err = s.CountState(t.Context(), SilenceStatePending) + require.NoError(t, err) + require.Equal(t, 0, count) + + count, err = s.CountState(t.Context(), SilenceStateExpired) + require.NoError(t, err) + require.Equal(t, 3, count) + + // Expiring a pending Silence should make the API return the + // SilenceStateExpired Silence state. + silenceState := CurrentState(sil.StartsAt.AsTime(), sil.EndsAt.AsTime()) + require.Equal(t, SilenceStateExpired, silenceState) + + sil, err = s.QueryOne(t.Context(), QIDs("active")) + require.NoError(t, err) + expectedActive := &pb.Silence{ Id: "active", MatcherSets: []*pb.MatcherSet{{ Matchers: []*pb.Matcher{m}, }}, StartsAt: timestamppb.New(now.Add(-time.Minute)), - EndsAt: timestamppb.New(now.Add(time.Hour)), - UpdatedAt: timestamppb.New(now.Add(-time.Hour)), - }}, - "expired": &pb.MeshSilence{Silence: &pb.Silence{ + EndsAt: timestamppb.New(now), + UpdatedAt: timestamppb.New(now), + } + require.True(t, proto.Equal(expectedActive, sil), "active silence mismatch") + + sil, err = s.QueryOne(t.Context(), QIDs("expired")) + require.NoError(t, err) + expectedExpired := &pb.Silence{ Id: "expired", MatcherSets: []*pb.MatcherSet{{ Matchers: []*pb.Matcher{m}, @@ -2069,371 +2133,300 @@ func TestSilenceExpire(t *testing.T) { StartsAt: timestamppb.New(now.Add(-time.Hour)), EndsAt: timestamppb.New(now.Add(-time.Minute)), UpdatedAt: timestamppb.New(now.Add(-time.Hour)), - }}, - } - s.vi = versionIndex{ - silenceVersion{id: "pending"}, - silenceVersion{id: "active"}, - silenceVersion{id: "expired"}, - } - count, err := s.CountState(t.Context(), SilenceStatePending) - require.NoError(t, err) - require.Equal(t, 1, count) - - count, err = s.CountState(t.Context(), SilenceStateExpired) - require.NoError(t, err) - require.Equal(t, 1, count) - - require.NoError(t, s.Expire(t.Context(), "pending")) - require.NoError(t, s.Expire(t.Context(), "active")) - - require.NoError(t, s.Expire(t.Context(), "expired")) - - sil, err := s.QueryOne(t.Context(), QIDs("pending")) - require.NoError(t, err) - expectedPending := &pb.Silence{ - Id: "pending", - MatcherSets: []*pb.MatcherSet{{ - Matchers: []*pb.Matcher{m}, - }}, - StartsAt: timestamppb.New(now), - EndsAt: timestamppb.New(now), - UpdatedAt: timestamppb.New(now), - } - require.True(t, proto.Equal(expectedPending, sil), "pending silence mismatch") - - // Let time pass... - clock.Advance(time.Second) - - count, err = s.CountState(t.Context(), SilenceStatePending) - require.NoError(t, err) - require.Equal(t, 0, count) - - count, err = s.CountState(t.Context(), SilenceStateExpired) - require.NoError(t, err) - require.Equal(t, 3, count) - - // Expiring a pending Silence should make the API return the - // SilenceStateExpired Silence state. - silenceState := CurrentState(sil.StartsAt.AsTime(), sil.EndsAt.AsTime()) - require.Equal(t, SilenceStateExpired, silenceState) - - sil, err = s.QueryOne(t.Context(), QIDs("active")) - require.NoError(t, err) - expectedActive := &pb.Silence{ - Id: "active", - MatcherSets: []*pb.MatcherSet{{ - Matchers: []*pb.Matcher{m}, - }}, - StartsAt: timestamppb.New(now.Add(-time.Minute)), - EndsAt: timestamppb.New(now), - UpdatedAt: timestamppb.New(now), - } - require.True(t, proto.Equal(expectedActive, sil), "active silence mismatch") - - sil, err = s.QueryOne(t.Context(), QIDs("expired")) - require.NoError(t, err) - expectedExpired := &pb.Silence{ - Id: "expired", - MatcherSets: []*pb.MatcherSet{{ - Matchers: []*pb.Matcher{m}, - }}, - StartsAt: timestamppb.New(now.Add(-time.Hour)), - EndsAt: timestamppb.New(now.Add(-time.Minute)), - UpdatedAt: timestamppb.New(now.Add(-time.Hour)), - } - require.True(t, proto.Equal(expectedExpired, sil), "expired silence mismatch") + } + require.True(t, proto.Equal(expectedExpired, sil), "expired silence mismatch") + }) } // TestSilenceExpireWithZeroRetention covers the problem that, with zero // retention time, a silence explicitly set to expired will also immediately // expire from the silence storage. func TestSilenceExpireWithZeroRetention(t *testing.T) { - s, err := New(Options{Metrics: prometheus.NewRegistry(), Retention: 0}) - require.NoError(t, err) + synctest.Test(t, func(t *testing.T) { + s, err := New(Options{Metrics: prometheus.NewRegistry(), Retention: 0}) + require.NoError(t, err) - clock := quartz.NewMock(t) - s.clock = clock - now := s.nowUTC() + now := s.nowUTC() - m := &pb.Matcher{Type: pb.Matcher_EQUAL, Name: "a", Pattern: "b"} + m := &pb.Matcher{Type: pb.Matcher_EQUAL, Name: "a", Pattern: "b"} - s.st = state{ - "pending": &pb.MeshSilence{Silence: &pb.Silence{ - Id: "pending", - MatcherSets: []*pb.MatcherSet{{ - Matchers: []*pb.Matcher{m}, + s.st = state{ + "pending": &pb.MeshSilence{Silence: &pb.Silence{ + Id: "pending", + MatcherSets: []*pb.MatcherSet{{ + Matchers: []*pb.Matcher{m}, + }}, + StartsAt: timestamppb.New(now.Add(time.Minute)), + EndsAt: timestamppb.New(now.Add(time.Hour)), + UpdatedAt: timestamppb.New(now.Add(-time.Hour)), }}, - StartsAt: timestamppb.New(now.Add(time.Minute)), - EndsAt: timestamppb.New(now.Add(time.Hour)), - UpdatedAt: timestamppb.New(now.Add(-time.Hour)), - }}, - "active": &pb.MeshSilence{Silence: &pb.Silence{ - Id: "active", - MatcherSets: []*pb.MatcherSet{{ - Matchers: []*pb.Matcher{m}, + "active": &pb.MeshSilence{Silence: &pb.Silence{ + Id: "active", + MatcherSets: []*pb.MatcherSet{{ + Matchers: []*pb.Matcher{m}, + }}, + StartsAt: timestamppb.New(now.Add(-time.Minute)), + EndsAt: timestamppb.New(now.Add(time.Hour)), + UpdatedAt: timestamppb.New(now.Add(-time.Hour)), }}, - StartsAt: timestamppb.New(now.Add(-time.Minute)), - EndsAt: timestamppb.New(now.Add(time.Hour)), - UpdatedAt: timestamppb.New(now.Add(-time.Hour)), - }}, - "expired": &pb.MeshSilence{Silence: &pb.Silence{ - Id: "expired", - MatcherSets: []*pb.MatcherSet{{ - Matchers: []*pb.Matcher{m}, + "expired": &pb.MeshSilence{Silence: &pb.Silence{ + Id: "expired", + MatcherSets: []*pb.MatcherSet{{ + Matchers: []*pb.Matcher{m}, + }}, + StartsAt: timestamppb.New(now.Add(-time.Hour)), + EndsAt: timestamppb.New(now.Add(-time.Minute)), + UpdatedAt: timestamppb.New(now.Add(-time.Hour)), }}, - StartsAt: timestamppb.New(now.Add(-time.Hour)), - EndsAt: timestamppb.New(now.Add(-time.Minute)), - UpdatedAt: timestamppb.New(now.Add(-time.Hour)), - }}, - } - s.vi = versionIndex{ - silenceVersion{id: "pending"}, - silenceVersion{id: "active"}, - silenceVersion{id: "expired"}, - } + } + s.vi = versionIndex{ + silenceVersion{id: "pending"}, + silenceVersion{id: "active"}, + silenceVersion{id: "expired"}, + } - count, err := s.CountState(t.Context(), SilenceStatePending) - require.NoError(t, err) - require.Equal(t, 1, count) + count, err := s.CountState(t.Context(), SilenceStatePending) + require.NoError(t, err) + require.Equal(t, 1, count) - count, err = s.CountState(t.Context(), SilenceStateActive) - require.NoError(t, err) - require.Equal(t, 1, count) + count, err = s.CountState(t.Context(), SilenceStateActive) + require.NoError(t, err) + require.Equal(t, 1, count) - count, err = s.CountState(t.Context(), SilenceStateExpired) - require.NoError(t, err) - require.Equal(t, 1, count) + count, err = s.CountState(t.Context(), SilenceStateExpired) + require.NoError(t, err) + require.Equal(t, 1, count) - // Advance time. The silence state management code uses update time when - // merging, and the logic is "first write wins". So we must advance the clock - // one tick for updates to take effect. - clock.Advance(1 * time.Millisecond) + // Advance time. The silence state management code uses update time when + // merging, and the logic is "first write wins". So we must advance the clock + // one tick for updates to take effect. + time.Sleep(1 * time.Millisecond) - require.NoError(t, s.Expire(t.Context(), "pending")) - require.NoError(t, s.Expire(t.Context(), "active")) - require.NoError(t, s.Expire(t.Context(), "expired")) + require.NoError(t, s.Expire(t.Context(), "pending")) + require.NoError(t, s.Expire(t.Context(), "active")) + require.NoError(t, s.Expire(t.Context(), "expired")) - // Advance time again. Despite what the function name says, s.Expire() does - // not expire a silence. It sets the silence to EndAt the current time. This - // means that the silence is active immediately after calling Expire. - clock.Advance(1 * time.Millisecond) + // Advance time again. Despite what the function name says, s.Expire() does + // not expire a silence. It sets the silence to EndAt the current time. This + // means that the silence is active immediately after calling Expire. + time.Sleep(1 * time.Millisecond) - // Verify all silences have expired. - count, err = s.CountState(t.Context(), SilenceStatePending) - require.NoError(t, err) - require.Equal(t, 0, count) + // Verify all silences have expired. + count, err = s.CountState(t.Context(), SilenceStatePending) + require.NoError(t, err) + require.Equal(t, 0, count) - count, err = s.CountState(t.Context(), SilenceStateActive) - require.NoError(t, err) - require.Equal(t, 0, count) + count, err = s.CountState(t.Context(), SilenceStateActive) + require.NoError(t, err) + require.Equal(t, 0, count) - count, err = s.CountState(t.Context(), SilenceStateExpired) - require.NoError(t, err) - require.Equal(t, 3, count) + count, err = s.CountState(t.Context(), SilenceStateExpired) + require.NoError(t, err) + require.Equal(t, 3, count) + }) } // This test checks that invalid silences can be expired. func TestSilenceExpireInvalid(t *testing.T) { - s, err := New(Options{Metrics: prometheus.NewRegistry(), Retention: time.Hour}) - require.NoError(t, err) + synctest.Test(t, func(t *testing.T) { + s, err := New(Options{Metrics: prometheus.NewRegistry(), Retention: time.Hour}) + require.NoError(t, err) - clock := quartz.NewMock(t) - s.clock = clock - now := s.nowUTC() + now := s.nowUTC() - // In this test the matcher has an invalid type. - silence := pb.Silence{ - Id: "active", - MatcherSets: []*pb.MatcherSet{{ - Matchers: []*pb.Matcher{{Type: -1, Name: "a", Pattern: "b"}}, - }}, - StartsAt: timestamppb.New(now.Add(-time.Minute)), - EndsAt: timestamppb.New(now.Add(time.Hour)), - UpdatedAt: timestamppb.New(now.Add(-time.Hour)), - } - // Assert that this silence is invalid. - require.EqualError(t, validateSilence(&silence), "invalid label matcher 0 in set 0: unknown matcher type \"-1\"") + // In this test the matcher has an invalid type. + silence := pb.Silence{ + Id: "active", + MatcherSets: []*pb.MatcherSet{{ + Matchers: []*pb.Matcher{{Type: -1, Name: "a", Pattern: "b"}}, + }}, + StartsAt: timestamppb.New(now.Add(-time.Minute)), + EndsAt: timestamppb.New(now.Add(time.Hour)), + UpdatedAt: timestamppb.New(now.Add(-time.Hour)), + } + // Assert that this silence is invalid. + require.EqualError(t, validateSilence(&silence), "invalid label matcher 0 in set 0: unknown matcher type \"-1\"") - s.st = state{"active": &pb.MeshSilence{Silence: &silence}} - s.vi = versionIndex{silenceVersion{id: "active"}} + s.st = state{"active": &pb.MeshSilence{Silence: &silence}} + s.vi = versionIndex{silenceVersion{id: "active"}} - // The silence should be active. - count, err := s.CountState(t.Context(), SilenceStateActive) - require.NoError(t, err) - require.Equal(t, 1, count) + // The silence should be active. + count, err := s.CountState(t.Context(), SilenceStateActive) + require.NoError(t, err) + require.Equal(t, 1, count) - clock.Advance(time.Millisecond) - require.NoError(t, s.Expire(t.Context(), "active")) - clock.Advance(time.Millisecond) + time.Sleep(time.Millisecond) + require.NoError(t, s.Expire(t.Context(), "active")) + time.Sleep(time.Millisecond) - // The silence should be expired. - count, err = s.CountState(t.Context(), SilenceStateActive) - require.NoError(t, err) - require.Equal(t, 0, count) - count, err = s.CountState(t.Context(), SilenceStateExpired) - require.NoError(t, err) - require.Equal(t, 1, count) + // The silence should be expired. + count, err = s.CountState(t.Context(), SilenceStateActive) + require.NoError(t, err) + require.Equal(t, 0, count) + count, err = s.CountState(t.Context(), SilenceStateExpired) + require.NoError(t, err) + require.Equal(t, 1, count) + }) } func TestSilencer(t *testing.T) { - ss, err := New(Options{Metrics: prometheus.NewRegistry(), Retention: time.Hour}) - require.NoError(t, err) + synctest.Test(t, func(t *testing.T) { + ss, err := New(Options{Metrics: prometheus.NewRegistry(), Retention: time.Hour}) + require.NoError(t, err) - clock := quartz.NewMock(t) - ss.clock = clock - now := ss.nowUTC() + now := ss.nowUTC() - s := NewSilencer(ss, promslog.NewNopLogger(), eventrecorder.NopRecorder()) + s := NewSilencer(ss, promslog.NewNopLogger(), eventrecorder.NopRecorder()) - m := marker.NewAlertMarker() - ctx := marker.WithContext(t.Context(), m) + m := marker.NewAlertMarker() + ctx := marker.WithContext(t.Context(), m) - require.False(t, s.Mutes(ctx, model.LabelSet{"foo": "bar"}), "expected alert not silenced without any silences") - checkMutes(t, m, model.LabelSet{"foo": "bar"}, false, "expected marker not silenced without any silences") + require.False(t, s.Mutes(ctx, model.LabelSet{"foo": "bar"}), "expected alert not silenced without any silences") + checkMutes(t, m, model.LabelSet{"foo": "bar"}, false, "expected marker not silenced without any silences") - sil1 := &pb.Silence{ - MatcherSets: []*pb.MatcherSet{{ - Matchers: []*pb.Matcher{{Name: "foo", Pattern: "baz"}}, - }}, - StartsAt: timestamppb.New(now.Add(-time.Hour)), - EndsAt: timestamppb.New(now.Add(5 * time.Minute)), - } - require.NoError(t, ss.Set(t.Context(), sil1)) + sil1 := &pb.Silence{ + MatcherSets: []*pb.MatcherSet{{ + Matchers: []*pb.Matcher{{Name: "foo", Pattern: "baz"}}, + }}, + StartsAt: timestamppb.New(now.Add(-time.Hour)), + EndsAt: timestamppb.New(now.Add(5 * time.Minute)), + } + require.NoError(t, ss.Set(t.Context(), sil1)) - m = marker.NewAlertMarker() - ctx = marker.WithContext(t.Context(), m) - require.False(t, s.Mutes(ctx, model.LabelSet{"foo": "bar"}), "expected alert not silenced by non-matching silence") - checkMutes(t, m, model.LabelSet{"foo": "bar"}, false, "expected marker not silenced by non-matching silence") + m = marker.NewAlertMarker() + ctx = marker.WithContext(t.Context(), m) + require.False(t, s.Mutes(ctx, model.LabelSet{"foo": "bar"}), "expected alert not silenced by non-matching silence") + checkMutes(t, m, model.LabelSet{"foo": "bar"}, false, "expected marker not silenced by non-matching silence") - sil2 := &pb.Silence{ - MatcherSets: []*pb.MatcherSet{{ - Matchers: []*pb.Matcher{{Name: "foo", Pattern: "bar"}}, - }}, - StartsAt: timestamppb.New(now.Add(-time.Hour)), - EndsAt: timestamppb.New(now.Add(5 * time.Minute)), - } - require.NoError(t, ss.Set(t.Context(), sil2)) - require.NotEmpty(t, sil2.Id) - - m = marker.NewAlertMarker() - ctx = marker.WithContext(t.Context(), m) - require.True(t, s.Mutes(ctx, model.LabelSet{"foo": "bar"}), "expected alert silenced by matching silence") - checkMutes(t, m, model.LabelSet{"foo": "bar"}, true, "expected marker silenced by matching silence") - - // One hour passes, silence expires. - clock.Advance(time.Hour) - now = ss.nowUTC() - - m = marker.NewAlertMarker() - ctx = marker.WithContext(t.Context(), m) - require.False(t, s.Mutes(ctx, model.LabelSet{"foo": "bar"}), "expected alert not silenced by expired silence") - checkMutes(t, m, model.LabelSet{"foo": "bar"}, false, "expected marker not silenced by expired silence") - - // Update silence to start in the future. - err = ss.Set(t.Context(), &pb.Silence{ - Id: sil2.Id, - MatcherSets: []*pb.MatcherSet{{ - Matchers: []*pb.Matcher{{Name: "foo", Pattern: "bar"}}, - }}, - StartsAt: timestamppb.New(now.Add(time.Hour)), - EndsAt: timestamppb.New(now.Add(3 * time.Hour)), - }) - require.NoError(t, err) + sil2 := &pb.Silence{ + MatcherSets: []*pb.MatcherSet{{ + Matchers: []*pb.Matcher{{Name: "foo", Pattern: "bar"}}, + }}, + StartsAt: timestamppb.New(now.Add(-time.Hour)), + EndsAt: timestamppb.New(now.Add(5 * time.Minute)), + } + require.NoError(t, ss.Set(t.Context(), sil2)) + require.NotEmpty(t, sil2.Id) + + m = marker.NewAlertMarker() + ctx = marker.WithContext(t.Context(), m) + require.True(t, s.Mutes(ctx, model.LabelSet{"foo": "bar"}), "expected alert silenced by matching silence") + checkMutes(t, m, model.LabelSet{"foo": "bar"}, true, "expected marker silenced by matching silence") + + // One hour passes, silence expires. + time.Sleep(time.Hour) + now = ss.nowUTC() + + m = marker.NewAlertMarker() + ctx = marker.WithContext(t.Context(), m) + require.False(t, s.Mutes(ctx, model.LabelSet{"foo": "bar"}), "expected alert not silenced by expired silence") + checkMutes(t, m, model.LabelSet{"foo": "bar"}, false, "expected marker not silenced by expired silence") + + // Update silence to start in the future. + err = ss.Set(t.Context(), &pb.Silence{ + Id: sil2.Id, + MatcherSets: []*pb.MatcherSet{{ + Matchers: []*pb.Matcher{{Name: "foo", Pattern: "bar"}}, + }}, + StartsAt: timestamppb.New(now.Add(time.Hour)), + EndsAt: timestamppb.New(now.Add(3 * time.Hour)), + }) + require.NoError(t, err) - m = marker.NewAlertMarker() - ctx = marker.WithContext(t.Context(), m) - require.False(t, s.Mutes(ctx, model.LabelSet{"foo": "bar"}), "expected alert not silenced by future silence") - checkMutes(t, m, model.LabelSet{"foo": "bar"}, false, "expected marker not silenced by future silence") + m = marker.NewAlertMarker() + ctx = marker.WithContext(t.Context(), m) + require.False(t, s.Mutes(ctx, model.LabelSet{"foo": "bar"}), "expected alert not silenced by future silence") + checkMutes(t, m, model.LabelSet{"foo": "bar"}, false, "expected marker not silenced by future silence") - // Two hours pass, silence becomes active. - clock.Advance(2 * time.Hour) - now = ss.nowUTC() + // Two hours pass, silence becomes active. + time.Sleep(2 * time.Hour) + now = ss.nowUTC() - // Exposes issue #2426. - m = marker.NewAlertMarker() - ctx = marker.WithContext(t.Context(), m) - require.True(t, s.Mutes(ctx, model.LabelSet{"foo": "bar"}), "expected alert silenced by activated silence") - checkMutes(t, m, model.LabelSet{"foo": "bar"}, true, "expected marker silenced by activated silence") + // Exposes issue #2426. + m = marker.NewAlertMarker() + ctx = marker.WithContext(t.Context(), m) + require.True(t, s.Mutes(ctx, model.LabelSet{"foo": "bar"}), "expected alert silenced by activated silence") + checkMutes(t, m, model.LabelSet{"foo": "bar"}, true, "expected marker silenced by activated silence") - err = ss.Set(t.Context(), &pb.Silence{ - MatcherSets: []*pb.MatcherSet{{ - Matchers: []*pb.Matcher{{Name: "foo", Pattern: "b..", Type: pb.Matcher_REGEXP}}, - }}, - StartsAt: timestamppb.New(now.Add(time.Hour)), - EndsAt: timestamppb.New(now.Add(3 * time.Hour)), - }) - require.NoError(t, err) + err = ss.Set(t.Context(), &pb.Silence{ + MatcherSets: []*pb.MatcherSet{{ + Matchers: []*pb.Matcher{{Name: "foo", Pattern: "b..", Type: pb.Matcher_REGEXP}}, + }}, + StartsAt: timestamppb.New(now.Add(time.Hour)), + EndsAt: timestamppb.New(now.Add(3 * time.Hour)), + }) + require.NoError(t, err) - // Note that issue #2426 doesn't apply anymore because we added a new silence. - m = marker.NewAlertMarker() - ctx = marker.WithContext(t.Context(), m) - require.True(t, s.Mutes(ctx, model.LabelSet{"foo": "bar"}), "expected alert still silenced by activated silence") - checkMutes(t, m, model.LabelSet{"foo": "bar"}, true, "expected marker still silenced by activated silence") + // Note that issue #2426 doesn't apply anymore because we added a new silence. + m = marker.NewAlertMarker() + ctx = marker.WithContext(t.Context(), m) + require.True(t, s.Mutes(ctx, model.LabelSet{"foo": "bar"}), "expected alert still silenced by activated silence") + checkMutes(t, m, model.LabelSet{"foo": "bar"}, true, "expected marker still silenced by activated silence") - // Two hours pass, first silence expires, overlapping second silence becomes active. - clock.Advance(2 * time.Hour) + // Two hours pass, first silence expires, overlapping second silence becomes active. + time.Sleep(2 * time.Hour) - // Another variant of issue #2426 (overlapping silences). - m = marker.NewAlertMarker() - ctx = marker.WithContext(t.Context(), m) - require.True(t, s.Mutes(ctx, model.LabelSet{"foo": "bar"}), "expected alert silenced by activated second silence") - checkMutes(t, m, model.LabelSet{"foo": "bar"}, true, "expected marker silenced by activated second silence") + // Another variant of issue #2426 (overlapping silences). + m = marker.NewAlertMarker() + ctx = marker.WithContext(t.Context(), m) + require.True(t, s.Mutes(ctx, model.LabelSet{"foo": "bar"}), "expected alert silenced by activated second silence") + checkMutes(t, m, model.LabelSet{"foo": "bar"}, true, "expected marker silenced by activated second silence") + }) } func TestSilencerPostDeleteEvictsCache(t *testing.T) { - ss, err := New(Options{Metrics: prometheus.NewRegistry(), Retention: time.Hour}) - require.NoError(t, err) + synctest.Test(t, func(t *testing.T) { + ss, err := New(Options{Metrics: prometheus.NewRegistry(), Retention: time.Hour}) + require.NoError(t, err) - clock := quartz.NewMock(t) - ss.clock = clock - now := ss.nowUTC() + now := ss.nowUTC() - s := NewSilencer(ss, promslog.NewNopLogger(), eventrecorder.NopRecorder()) + s := NewSilencer(ss, promslog.NewNopLogger(), eventrecorder.NopRecorder()) - lset := model.LabelSet{"foo": "bar"} - fp := lset.Fingerprint() + lset := model.LabelSet{"foo": "bar"} + fp := lset.Fingerprint() - // Create a matching silence. - sil := &pb.Silence{ - MatcherSets: []*pb.MatcherSet{{ - Matchers: []*pb.Matcher{{Name: "foo", Pattern: "bar"}}, - }}, - StartsAt: timestamppb.New(now.Add(-time.Hour)), - EndsAt: timestamppb.New(now.Add(5 * time.Minute)), - } - require.NoError(t, ss.Set(t.Context(), sil)) - - // Mutes populates the cache. - m := marker.NewAlertMarker() - ctx := marker.WithContext(t.Context(), m) - require.True(t, s.Mutes(ctx, lset)) - checkMutes(t, m, lset, true, "expected marker silenced after initial Mutes") - entry := s.cache.get(fp) - require.Positive(t, entry.count(), "cache should have entries after Mutes()") - - // PostGC evicts the cache entry for this fingerprint. - s.PostGC(model.Fingerprints{fp}) - entry = s.cache.get(fp) - require.Equal(t, 0, entry.count(), "cache should be empty after PostGC()") - require.Equal(t, 0, entry.version, "version should be zero for evicted entry") - - // Mutes re-evaluates from scratch (cache miss) and still finds the silence. - m = marker.NewAlertMarker() - ctx = marker.WithContext(t.Context(), m) - require.True(t, s.Mutes(ctx, lset), "expected alert still silenced after cache eviction") - checkMutes(t, m, lset, true, "expected marker silenced after cache eviction") - entry = s.cache.get(fp) - require.Positive(t, entry.count(), "cache should be repopulated after Mutes()") - - // Expire the silence, advance time so it's truly expired. - clock.Advance(time.Hour) - - // PostGC for a different fingerprint should not affect this entry. - otherLset := model.LabelSet{"other": "alert"} - s.PostGC(model.Fingerprints{otherLset.Fingerprint()}) - entry = s.cache.get(fp) - require.Positive(t, entry.count(), "unrelated PostGC should not evict other entries") + // Create a matching silence. + sil := &pb.Silence{ + MatcherSets: []*pb.MatcherSet{{ + Matchers: []*pb.Matcher{{Name: "foo", Pattern: "bar"}}, + }}, + StartsAt: timestamppb.New(now.Add(-time.Hour)), + EndsAt: timestamppb.New(now.Add(5 * time.Minute)), + } + require.NoError(t, ss.Set(t.Context(), sil)) + + // Mutes populates the cache. + m := marker.NewAlertMarker() + ctx := marker.WithContext(t.Context(), m) + require.True(t, s.Mutes(ctx, lset)) + checkMutes(t, m, lset, true, "expected marker silenced after initial Mutes") + entry := s.cache.get(fp) + require.Positive(t, entry.count(), "cache should have entries after Mutes()") + + // PostGC evicts the cache entry for this fingerprint. + s.PostGC(model.Fingerprints{fp}) + entry = s.cache.get(fp) + require.Equal(t, 0, entry.count(), "cache should be empty after PostGC()") + require.Equal(t, 0, entry.version, "version should be zero for evicted entry") + + // Mutes re-evaluates from scratch (cache miss) and still finds the silence. + m = marker.NewAlertMarker() + ctx = marker.WithContext(t.Context(), m) + require.True(t, s.Mutes(ctx, lset), "expected alert still silenced after cache eviction") + checkMutes(t, m, lset, true, "expected marker silenced after cache eviction") + entry = s.cache.get(fp) + require.Positive(t, entry.count(), "cache should be repopulated after Mutes()") + + // Expire the silence, advance time so it's truly expired. + time.Sleep(time.Hour) + + // PostGC for a different fingerprint should not affect this entry. + otherLset := model.LabelSet{"other": "alert"} + s.PostGC(model.Fingerprints{otherLset.Fingerprint()}) + entry = s.cache.get(fp) + require.Positive(t, entry.count(), "unrelated PostGC should not evict other entries") + }) } func TestValidateClassicMatcher(t *testing.T) { @@ -2886,8 +2879,6 @@ func TestLogSilence(t *testing.T) { logger: logger, } - clock := quartz.NewMock(t) - s.clock = clock now := s.nowUTC() silence := &pb.Silence{ @@ -2919,126 +2910,126 @@ func TestLogSilence(t *testing.T) { } func TestSilenceAnnotations(t *testing.T) { - s, err := New(Options{ - Metrics: prometheus.NewRegistry(), - Retention: time.Hour, - }) - require.NoError(t, err) - - clock := quartz.NewMock(t) - s.clock = clock - now := s.nowUTC() + synctest.Test(t, func(t *testing.T) { + s, err := New(Options{ + Metrics: prometheus.NewRegistry(), + Retention: time.Hour, + }) + require.NoError(t, err) - // Create a silence with annotations - sil1 := &pb.Silence{ - Matchers: []*pb.Matcher{{Name: "job", Pattern: "test"}}, - StartsAt: timestamppb.New(now), - EndsAt: timestamppb.New(now.Add(time.Hour)), - Annotations: map[string]string{ - "ticket": "JIRA-123", - "type": "planned", - "test": "integration", - }, - } + now := s.nowUTC() - // Set the silence via the API - require.NoError(t, s.Set(t.Context(), sil1)) - require.NotEmpty(t, sil1.Id) + // Create a silence with annotations + sil1 := &pb.Silence{ + Matchers: []*pb.Matcher{{Name: "job", Pattern: "test"}}, + StartsAt: timestamppb.New(now), + EndsAt: timestamppb.New(now.Add(time.Hour)), + Annotations: map[string]string{ + "ticket": "JIRA-123", + "type": "planned", + "test": "integration", + }, + } - // Query the silence back by ID - queriedSil, err := s.QueryOne(t.Context(), QIDs(sil1.Id)) - require.NoError(t, err) + // Set the silence via the API + require.NoError(t, s.Set(t.Context(), sil1)) + require.NotEmpty(t, sil1.Id) - // Verify all annotations are returned correctly - require.NotNil(t, queriedSil.Annotations) - require.Equal(t, "JIRA-123", queriedSil.Annotations["ticket"]) - require.Equal(t, "planned", queriedSil.Annotations["type"]) - require.Equal(t, "integration", queriedSil.Annotations["test"]) + // Query the silence back by ID + queriedSil, err := s.QueryOne(t.Context(), QIDs(sil1.Id)) + require.NoError(t, err) - // Test querying all silences - allSils, _, err := s.Query(t.Context()) - require.NoError(t, err) - require.Len(t, allSils, 1) - require.Equal(t, queriedSil.Annotations, allSils[0].Annotations) + // Verify all annotations are returned correctly + require.NotNil(t, queriedSil.Annotations) + require.Equal(t, "JIRA-123", queriedSil.Annotations["ticket"]) + require.Equal(t, "planned", queriedSil.Annotations["type"]) + require.Equal(t, "integration", queriedSil.Annotations["test"]) - // Create a second silence with different annotations - sil2 := &pb.Silence{ - Matchers: []*pb.Matcher{{Name: "job", Pattern: "frontend"}}, - StartsAt: timestamppb.New(now), - EndsAt: timestamppb.New(now.Add(time.Hour)), - Annotations: map[string]string{ - "ticket": "JIRA-456", - }, - } - require.NoError(t, s.Set(t.Context(), sil2)) + // Test querying all silences + allSils, _, err := s.Query(t.Context()) + require.NoError(t, err) + require.Len(t, allSils, 1) + require.Equal(t, queriedSil.Annotations, allSils[0].Annotations) - // Query by state and verify both silences have their annotations - activeSils, _, err := s.Query(t.Context(), QState(SilenceStateActive)) - require.NoError(t, err) - require.Len(t, activeSils, 2) - - for _, sil := range activeSils { - require.NotNil(t, sil.Annotations) - switch sil.Id { - case sil1.Id: - require.Len(t, sil.Annotations, 3) - require.Equal(t, "JIRA-123", sil.Annotations["ticket"]) - case sil2.Id: - require.Len(t, sil.Annotations, 1) - require.Equal(t, "JIRA-456", sil.Annotations["ticket"]) - default: - t.Fatalf("unexpected silence ID: %s", sil.Id) + // Create a second silence with different annotations + sil2 := &pb.Silence{ + Matchers: []*pb.Matcher{{Name: "job", Pattern: "frontend"}}, + StartsAt: timestamppb.New(now), + EndsAt: timestamppb.New(now.Add(time.Hour)), + Annotations: map[string]string{ + "ticket": "JIRA-456", + }, } - } + require.NoError(t, s.Set(t.Context(), sil2)) - // Test updating a silence with new annotations - clock.Advance(time.Minute) - sil1Updated := &pb.Silence{ - Id: sil1.Id, - Matchers: []*pb.Matcher{{Name: "job", Pattern: "test"}}, - StartsAt: sil1.StartsAt, - EndsAt: sil1.EndsAt, - Annotations: map[string]string{ - "ticket": "JIRA-123", - "type": "emergency", // changed - "test": "load", // changed - }, - } - require.NoError(t, s.Set(t.Context(), sil1Updated)) + // Query by state and verify both silences have their annotations + activeSils, _, err := s.Query(t.Context(), QState(SilenceStateActive)) + require.NoError(t, err) + require.Len(t, activeSils, 2) + + for _, sil := range activeSils { + require.NotNil(t, sil.Annotations) + switch sil.Id { + case sil1.Id: + require.Len(t, sil.Annotations, 3) + require.Equal(t, "JIRA-123", sil.Annotations["ticket"]) + case sil2.Id: + require.Len(t, sil.Annotations, 1) + require.Equal(t, "JIRA-456", sil.Annotations["ticket"]) + default: + t.Fatalf("unexpected silence ID: %s", sil.Id) + } + } - // Query back and verify annotations were updated - queriedUpdated, err := s.QueryOne(t.Context(), QIDs(sil1.Id)) - require.NoError(t, err) - require.Len(t, queriedUpdated.Annotations, 3) - require.Equal(t, "emergency", queriedUpdated.Annotations["type"]) - require.Equal(t, "load", queriedUpdated.Annotations["test"]) + // Test updating a silence with new annotations + time.Sleep(time.Minute) + sil1Updated := &pb.Silence{ + Id: sil1.Id, + Matchers: []*pb.Matcher{{Name: "job", Pattern: "test"}}, + StartsAt: sil1.StartsAt, + EndsAt: sil1.EndsAt, + Annotations: map[string]string{ + "ticket": "JIRA-123", + "type": "emergency", // changed + "test": "load", // changed + }, + } + require.NoError(t, s.Set(t.Context(), sil1Updated)) - // Test silence with nil annotations - sil3 := &pb.Silence{ - Matchers: []*pb.Matcher{{Name: "job", Pattern: "backend"}}, - StartsAt: timestamppb.New(now), - EndsAt: timestamppb.New(now.Add(time.Hour)), - Annotations: nil, - } - require.NoError(t, s.Set(t.Context(), sil3)) - queriedSil3, err := s.QueryOne(t.Context(), QIDs(sil3.Id)) - require.NoError(t, err) - // nil annotations should be preserved or converted to empty map - if queriedSil3.Annotations != nil { - require.Empty(t, queriedSil3.Annotations) - } + // Query back and verify annotations were updated + queriedUpdated, err := s.QueryOne(t.Context(), QIDs(sil1.Id)) + require.NoError(t, err) + require.Len(t, queriedUpdated.Annotations, 3) + require.Equal(t, "emergency", queriedUpdated.Annotations["type"]) + require.Equal(t, "load", queriedUpdated.Annotations["test"]) + + // Test silence with nil annotations + sil3 := &pb.Silence{ + Matchers: []*pb.Matcher{{Name: "job", Pattern: "backend"}}, + StartsAt: timestamppb.New(now), + EndsAt: timestamppb.New(now.Add(time.Hour)), + Annotations: nil, + } + require.NoError(t, s.Set(t.Context(), sil3)) + queriedSil3, err := s.QueryOne(t.Context(), QIDs(sil3.Id)) + require.NoError(t, err) + // nil annotations should be preserved or converted to empty map + if queriedSil3.Annotations != nil { + require.Empty(t, queriedSil3.Annotations) + } - // Test silence with empty annotations map - sil4 := &pb.Silence{ - Matchers: []*pb.Matcher{{Name: "job", Pattern: "database"}}, - StartsAt: timestamppb.New(now), - EndsAt: timestamppb.New(now.Add(time.Hour)), - Annotations: map[string]string{}, - } - require.NoError(t, s.Set(t.Context(), sil4)) - queriedSil4, err := s.QueryOne(t.Context(), QIDs(sil4.Id)) - require.NoError(t, err) - if queriedSil4.Annotations != nil { - require.Empty(t, queriedSil4.Annotations) - } + // Test silence with empty annotations map + sil4 := &pb.Silence{ + Matchers: []*pb.Matcher{{Name: "job", Pattern: "database"}}, + StartsAt: timestamppb.New(now), + EndsAt: timestamppb.New(now.Add(time.Hour)), + Annotations: map[string]string{}, + } + require.NoError(t, s.Set(t.Context(), sil4)) + queriedSil4, err := s.QueryOne(t.Context(), QIDs(sil4.Id)) + require.NoError(t, err) + if queriedSil4.Annotations != nil { + require.Empty(t, queriedSil4.Annotations) + } + }) } diff --git a/test/util.go b/test/util.go new file mode 100644 index 0000000000..e6a4ae672c --- /dev/null +++ b/test/util.go @@ -0,0 +1,27 @@ +// Copyright The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package test + +import ( + "testing" + "testing/synctest" +) + +// SyncTestRun runs fn as a named subtest with fake time provided by +// testing/synctest. It is a convenience wrapper around t.Run and synctest.Test. +func SyncTestRun(name string, t *testing.T, fn func(t *testing.T)) { + t.Run(name, func(t *testing.T) { + synctest.Test(t, fn) + }) +} From 3d96a3425fd0ae28c8b8f5bd67859dff425ba7bc Mon Sep 17 00:00:00 2001 From: Guido Trotter Date: Mon, 6 Jul 2026 10:46:19 -0400 Subject: [PATCH 085/120] [nflog] remove quartz clock Signed-off-by: Guido Trotter --- go.mod | 1 - go.sum | 2 -- nflog/nflog.go | 8 ++--- nflog/nflog_test.go | 80 +++++++++++++++++++++------------------------ 4 files changed, 40 insertions(+), 51 deletions(-) diff --git a/go.mod b/go.mod index c3e42f7ac7..b2936a4246 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,6 @@ require ( github.com/aws/smithy-go v1.27.3 github.com/cenkalti/backoff/v5 v5.0.3 github.com/cespare/xxhash/v2 v2.3.0 - github.com/coder/quartz v0.3.1 github.com/coreos/go-systemd/v22 v22.7.0 github.com/emersion/go-smtp v0.24.0 github.com/fsnotify/fsnotify v1.10.1 diff --git a/go.sum b/go.sum index 84d74550e3..968d7057d6 100644 --- a/go.sum +++ b/go.sum @@ -136,8 +136,6 @@ github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/coder/quartz v0.3.1 h1:JMJLj4Xj4NLSrUC1R/g/Hn0y9fkyOvb8tf6P0j+kPn0= -github.com/coder/quartz v0.3.1/go.mod h1:BgE7DOj/8NfvRgvKw0jPLDQH/2Lya2kxcTaNJ8X0rZk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/go-systemd/v22 v22.7.0 h1:LAEzFkke61DFROc7zNLX/WA2i5J8gYqe0rSj9KI28KA= diff --git a/nflog/nflog.go b/nflog/nflog.go index 21f149bf71..fd51e9c972 100644 --- a/nflog/nflog.go +++ b/nflog/nflog.go @@ -30,7 +30,6 @@ import ( "sync" "time" - "github.com/coder/quartz" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" "github.com/prometheus/common/promslog" @@ -174,8 +173,6 @@ func (s *Store) Delete(key string) { // Log holds the notification log state for alerts that have been notified. type Log struct { - clock quartz.Clock - logger *slog.Logger metrics *metrics retention time.Duration @@ -347,7 +344,6 @@ func New(o Options) (*Log, error) { } l := &Log{ - clock: quartz.NewReal(), retention: o.Retention, logger: promslog.NewNopLogger(), st: state{}, @@ -381,7 +377,7 @@ func New(o Options) (*Log, error) { } func (l *Log) now() time.Time { - return l.clock.Now() + return time.Now() } // Maintenance garbage collects the notification log state at the given interval. If the snapshot @@ -393,7 +389,7 @@ func (l *Log) Maintenance(interval time.Duration, snapf string, stopc <-chan str l.logger.Error("interval or stop signal are missing - not running maintenance") return } - t := l.clock.NewTicker(interval) + t := time.NewTicker(interval) defer t.Stop() var doMaintenance MaintenanceFunc diff --git a/nflog/nflog_test.go b/nflog/nflog_test.go index 97a63edc3c..f640c96286 100644 --- a/nflog/nflog_test.go +++ b/nflog/nflog_test.go @@ -21,11 +21,11 @@ import ( "sync" "sync/atomic" "testing" + "testing/synctest" "time" pb "github.com/prometheus/alertmanager/nflog/nflogpb" - "github.com/coder/quartz" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/testutil" "github.com/stretchr/testify/require" @@ -34,8 +34,7 @@ import ( ) func TestLogGC(t *testing.T) { - mockClock := quartz.NewMock(t) - now := mockClock.Now() + now := time.Now() // We only care about key names and expiration timestamps. newEntry := func(ts time.Time) *pb.MeshEntry { return &pb.MeshEntry{ @@ -49,7 +48,6 @@ func TestLogGC(t *testing.T) { "a2": newEntry(now.Add(time.Second)), "a3": newEntry(now.Add(-time.Second)), }, - clock: mockClock, metrics: newMetrics(prometheus.NewRegistry()), } n, err := l.GC() @@ -64,8 +62,7 @@ func TestLogGC(t *testing.T) { func TestLogSnapshot(t *testing.T) { // Check whether storing and loading the snapshot is symmetric. - mockClock := quartz.NewMock(t) - now := mockClock.Now().UTC() + now := time.Now().UTC() cases := []struct { entries []*pb.MeshEntry @@ -139,46 +136,46 @@ func TestLogSnapshot(t *testing.T) { } func TestWithMaintenance_SupportsCustomCallback(t *testing.T) { - f, err := os.CreateTemp(t.TempDir(), "snapshot") - require.NoError(t, err, "creating temp file failed") - stopc := make(chan struct{}) - reg := prometheus.NewPedanticRegistry() - opts := Options{ - Metrics: reg, - SnapshotFile: f.Name(), - } + synctest.Test(t, func(t *testing.T) { + f, err := os.CreateTemp(t.TempDir(), "snapshot") + require.NoError(t, err, "creating temp file failed") + stopc := make(chan struct{}) + reg := prometheus.NewPedanticRegistry() + opts := Options{ + Metrics: reg, + SnapshotFile: f.Name(), + } - l, err := New(opts) - clock := quartz.NewMock(t) - l.clock = clock - require.NoError(t, err) + l, err := New(opts) + require.NoError(t, err) - var calls atomic.Int32 - var wg sync.WaitGroup + var calls atomic.Int32 + var wg sync.WaitGroup - wg.Go(func() { - l.Maintenance(100*time.Millisecond, f.Name(), stopc, func() (int64, error) { - calls.Add(1) - return 0, nil + wg.Go(func() { + l.Maintenance(100*time.Millisecond, f.Name(), stopc, func() (int64, error) { + calls.Add(1) + return 0, nil + }) }) - }) - gosched() + gosched() - // Before the first tick, no maintenance executed. - clock.Advance(99 * time.Millisecond) - require.EqualValues(t, 0, calls.Load()) + // Before the first tick, no maintenance executed. + time.Sleep(99 * time.Millisecond) + require.EqualValues(t, 0, calls.Load()) - // Tick once. - clock.Advance(1 * time.Millisecond) - require.Eventually(t, func() bool { return calls.Load() == 1 }, 5*time.Second, time.Second) + // Tick once. + time.Sleep(1 * time.Millisecond) + synctest.Wait() + require.EqualValues(t, 1, calls.Load()) - // Stop the maintenance loop. We should get exactly one more execution of the maintenance func. - close(stopc) - wg.Wait() + // Stop the maintenance loop. We should get exactly one more execution of the maintenance func. + close(stopc) + wg.Wait() - require.EqualValues(t, 2, calls.Load()) - // Check the maintenance metrics. - require.NoError(t, testutil.GatherAndCompare(reg, bytes.NewBufferString(` + require.EqualValues(t, 2, calls.Load()) + // Check the maintenance metrics. + require.NoError(t, testutil.GatherAndCompare(reg, bytes.NewBufferString(` # HELP alertmanager_nflog_maintenance_errors_total How many maintenances were executed for the notification log that failed. # TYPE alertmanager_nflog_maintenance_errors_total counter alertmanager_nflog_maintenance_errors_total 0 @@ -186,6 +183,7 @@ alertmanager_nflog_maintenance_errors_total 0 # TYPE alertmanager_nflog_maintenance_total counter alertmanager_nflog_maintenance_total 2 `), "alertmanager_nflog_maintenance_total", "alertmanager_nflog_maintenance_errors_total")) + }) } func TestReplaceFile(t *testing.T) { @@ -217,8 +215,7 @@ func TestReplaceFile(t *testing.T) { } func TestStateMerge(t *testing.T) { - mockClock := quartz.NewMock(t) - now := mockClock.Now() + now := time.Now() // We only care about key names and timestamps for the // merging logic. @@ -279,8 +276,7 @@ func TestStateMerge(t *testing.T) { func TestStateDataCoding(t *testing.T) { // Check whether encoding and decoding the data is symmetric. - mockClock := quartz.NewMock(t) - now := mockClock.Now().UTC() + now := time.Now().UTC() cases := []struct { entries []*pb.MeshEntry From 9e50fadde9740edd0a22e6f3b4cc92902224e151 Mon Sep 17 00:00:00 2001 From: Christoph Maser Date: Thu, 16 Jul 2026 13:27:50 +0200 Subject: [PATCH 086/120] refactor(telegram): move configuration types into telegram package (#5387) #### Pull Request Checklist - [x] I have signed-off my commits - [x] I will follow [best practices for contributing to this project](https://docs.github.com/en/get-started/exploring-projects-on-github/contributing-to-open-source) #### Which user-facing changes does this PR introduce? ```release-notes NONE ``` Signed-off-by: Christoph Maser --- config/config.go | 3 +- config/notifiers.go | 51 ---------------- config/notifiers_test.go | 80 ------------------------ notify/telegram/config.go | 73 ++++++++++++++++++++++ notify/telegram/config_test.go | 102 +++++++++++++++++++++++++++++++ notify/telegram/telegram.go | 5 +- notify/telegram/telegram_test.go | 46 ++++++-------- 7 files changed, 198 insertions(+), 162 deletions(-) create mode 100644 notify/telegram/config.go create mode 100644 notify/telegram/config_test.go diff --git a/config/config.go b/config/config.go index ff19d5a8a9..9ede247077 100644 --- a/config/config.go +++ b/config/config.go @@ -39,6 +39,7 @@ import ( "github.com/prometheus/alertmanager/notify/msteams" "github.com/prometheus/alertmanager/notify/msteamsv2" "github.com/prometheus/alertmanager/notify/opsgenie" + "github.com/prometheus/alertmanager/notify/telegram" "github.com/prometheus/alertmanager/notify/webhook" "github.com/prometheus/alertmanager/timeinterval" "github.com/prometheus/alertmanager/tracing" @@ -985,7 +986,7 @@ type Receiver struct { PushoverConfigs []*PushoverConfig `yaml:"pushover_configs,omitempty" json:"pushover_configs,omitempty"` VictorOpsConfigs []*VictorOpsConfig `yaml:"victorops_configs,omitempty" json:"victorops_configs,omitempty"` SNSConfigs []*SNSConfig `yaml:"sns_configs,omitempty" json:"sns_configs,omitempty"` - TelegramConfigs []*TelegramConfig `yaml:"telegram_configs,omitempty" json:"telegram_configs,omitempty"` + TelegramConfigs []*telegram.TelegramConfig `yaml:"telegram_configs,omitempty" json:"telegram_configs,omitempty"` WebexConfigs []*WebexConfig `yaml:"webex_configs,omitempty" json:"webex_configs,omitempty"` MSTeamsConfigs []*msteams.MSTeamsConfig `yaml:"msteams_configs,omitempty" json:"msteams_configs,omitempty"` MSTeamsV2Configs []*msteamsv2.MSTeamsV2Config `yaml:"msteamsv2_configs,omitempty" json:"msteamsv2_configs,omitempty"` diff --git a/config/notifiers.go b/config/notifiers.go index 656e12d386..74534a741a 100644 --- a/config/notifiers.go +++ b/config/notifiers.go @@ -143,15 +143,6 @@ var ( Subject: `{{ template "sns.default.subject" . }}`, Message: `{{ template "sns.default.message" . }}`, } - - DefaultTelegramConfig = TelegramConfig{ - NotifierConfig: amcommoncfg.NotifierConfig{ - VSendResolved: true, - }, - DisableNotifications: false, - Message: `{{ template "telegram.default.message" . }}`, - ParseMode: "HTML", - } ) // WebexConfig configures notifications via Webex. @@ -658,48 +649,6 @@ func (c *SNSConfig) UnmarshalYAML(unmarshal func(any) error) error { return nil } -// TelegramConfig configures notifications via Telegram. -type TelegramConfig struct { - amcommoncfg.NotifierConfig `yaml:",inline" json:",inline"` - - HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"` - - APIUrl *amcommoncfg.URL `yaml:"api_url" json:"api_url,omitempty"` - BotToken commoncfg.Secret `yaml:"bot_token,omitempty" json:"token,omitempty"` - BotTokenFile string `yaml:"bot_token_file,omitempty" json:"token_file,omitempty"` - ChatID int64 `yaml:"chat_id,omitempty" json:"chat,omitempty"` - ChatIDFile string `yaml:"chat_id_file,omitempty" json:"chat_file,omitempty"` - MessageThreadID int `yaml:"message_thread_id,omitempty" json:"message_thread_id,omitempty"` - Message string `yaml:"message,omitempty" json:"message,omitempty"` - DisableNotifications bool `yaml:"disable_notifications,omitempty" json:"disable_notifications,omitempty"` - ParseMode string `yaml:"parse_mode,omitempty" json:"parse_mode,omitempty"` -} - -// UnmarshalYAML implements the yaml.Unmarshaler interface. -func (c *TelegramConfig) UnmarshalYAML(unmarshal func(any) error) error { - *c = DefaultTelegramConfig - type plain TelegramConfig - if err := unmarshal((*plain)(c)); err != nil { - return err - } - if c.BotToken != "" && c.BotTokenFile != "" { - return errors.New("at most one of bot_token & bot_token_file must be configured") - } - if c.ChatID == 0 && c.ChatIDFile == "" { - return errors.New("missing chat_id or chat_id_file on telegram_config") - } - if c.ChatID != 0 && c.ChatIDFile != "" { - return errors.New("at most one of chat_id & chat_id_file must be configured") - } - if c.ParseMode != "" && - c.ParseMode != "Markdown" && - c.ParseMode != "MarkdownV2" && - c.ParseMode != "HTML" { - return errors.New("unknown parse_mode on telegram_config, must be Markdown, MarkdownV2, HTML or empty string") - } - return nil -} - type RocketchatAttachmentField struct { Short *bool `json:"short"` Title string `json:"title,omitempty"` diff --git a/config/notifiers_test.go b/config/notifiers_test.go index e83c4fd371..ca8802f650 100644 --- a/config/notifiers_test.go +++ b/config/notifiers_test.go @@ -897,86 +897,6 @@ http_config: } } -func TestTelegramConfiguration(t *testing.T) { - tc := []struct { - name string - in string - expected error - }{ - { - name: "with both bot_token & bot_token_file - it fails", - in: ` -bot_token: xyz -bot_token_file: /file -`, - expected: errors.New("at most one of bot_token & bot_token_file must be configured"), - }, - { - name: "with bot_token and chat_id set - it succeeds", - in: ` -bot_token: xyz -chat_id: 123 -`, - }, - { - name: "with bot_token, chat_id and message_thread_id set - it succeeds", - in: ` -bot_token: xyz -chat_id: 123 -message_thread_id: 456 -`, - }, - { - name: "with bot_token_file and chat_id set - it succeeds", - in: ` -bot_token_file: /file -chat_id: 123 -`, - }, - { - name: "with bot_token_file and chat_id_file set - it succeeds", - in: ` -bot_token_file: /file -chat_id_file: /chat_id_file -`, - }, - { - name: "with no chat_id set - it fails", - in: ` -bot_token: xyz -`, - expected: errors.New("missing chat_id or chat_id_file on telegram_config"), - }, - { - name: "with both chat_id and chat_id_file - it fails", - in: ` -bot_token: xyz -chat_id: 123 -chat_id_file: /file -`, - expected: errors.New("at most one of chat_id & chat_id_file must be configured"), - }, - { - name: "with unknown parse_mode - it fails", - in: ` -bot_token: xyz -chat_id: 123 -parse_mode: invalid -`, - expected: errors.New("unknown parse_mode on telegram_config, must be Markdown, MarkdownV2, HTML or empty string"), - }, - } - - for _, tt := range tc { - t.Run(tt.name, func(t *testing.T) { - var cfg TelegramConfig - err := yaml.UnmarshalStrict([]byte(tt.in), &cfg) - - require.Equal(t, tt.expected, err) - }) - } -} - func newBoolPointer(b bool) *bool { return &b } diff --git a/notify/telegram/config.go b/notify/telegram/config.go new file mode 100644 index 0000000000..01fa674e25 --- /dev/null +++ b/notify/telegram/config.go @@ -0,0 +1,73 @@ +// Copyright The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package telegram + +import ( + "errors" + + commoncfg "github.com/prometheus/common/config" + + amcommoncfg "github.com/prometheus/alertmanager/config/common" +) + +var DefaultTelegramConfig = TelegramConfig{ + NotifierConfig: amcommoncfg.NotifierConfig{ + VSendResolved: true, + }, + DisableNotifications: false, + Message: `{{ template "telegram.default.message" . }}`, + ParseMode: "HTML", +} + +// TelegramConfig configures notifications via Telegram. +type TelegramConfig struct { + amcommoncfg.NotifierConfig `yaml:",inline" json:",inline"` + + HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"` + + APIUrl *amcommoncfg.URL `yaml:"api_url" json:"api_url,omitempty"` + BotToken commoncfg.Secret `yaml:"bot_token,omitempty" json:"token,omitempty"` + BotTokenFile string `yaml:"bot_token_file,omitempty" json:"token_file,omitempty"` + ChatID int64 `yaml:"chat_id,omitempty" json:"chat,omitempty"` + ChatIDFile string `yaml:"chat_id_file,omitempty" json:"chat_file,omitempty"` + MessageThreadID int `yaml:"message_thread_id,omitempty" json:"message_thread_id,omitempty"` + Message string `yaml:"message,omitempty" json:"message,omitempty"` + DisableNotifications bool `yaml:"disable_notifications,omitempty" json:"disable_notifications,omitempty"` + ParseMode string `yaml:"parse_mode,omitempty" json:"parse_mode,omitempty"` +} + +// UnmarshalYAML implements the yaml.Unmarshaler interface. +func (c *TelegramConfig) UnmarshalYAML(unmarshal func(any) error) error { + *c = DefaultTelegramConfig + type plain TelegramConfig + if err := unmarshal((*plain)(c)); err != nil { + return err + } + if c.BotToken != "" && c.BotTokenFile != "" { + return errors.New("at most one of bot_token & bot_token_file must be configured") + } + if c.ChatID == 0 && c.ChatIDFile == "" { + return errors.New("missing chat_id or chat_id_file on telegram_config") + } + if c.ChatID != 0 && c.ChatIDFile != "" { + return errors.New("at most one of chat_id & chat_id_file must be configured") + } + if c.ParseMode != "" && + c.ParseMode != "Markdown" && + c.ParseMode != "MarkdownV2" && + c.ParseMode != "HTML" { + return errors.New("unknown parse_mode on telegram_config, must be Markdown, MarkdownV2, HTML or empty string") + } + return nil +} diff --git a/notify/telegram/config_test.go b/notify/telegram/config_test.go new file mode 100644 index 0000000000..adaa1576df --- /dev/null +++ b/notify/telegram/config_test.go @@ -0,0 +1,102 @@ +// Copyright The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package telegram + +import ( + "errors" + "testing" + + "github.com/stretchr/testify/require" + "gopkg.in/yaml.v2" +) + +func TestTelegramConfiguration(t *testing.T) { + tc := []struct { + name string + in string + expected error + }{ + { + name: "with both bot_token & bot_token_file - it fails", + in: ` +bot_token: xyz +bot_token_file: /file +`, + expected: errors.New("at most one of bot_token & bot_token_file must be configured"), + }, + { + name: "with bot_token and chat_id set - it succeeds", + in: ` +bot_token: xyz +chat_id: 123 +`, + }, + { + name: "with bot_token, chat_id and message_thread_id set - it succeeds", + in: ` +bot_token: xyz +chat_id: 123 +message_thread_id: 456 +`, + }, + { + name: "with bot_token_file and chat_id set - it succeeds", + in: ` +bot_token_file: /file +chat_id: 123 +`, + }, + { + name: "with bot_token_file and chat_id_file set - it succeeds", + in: ` +bot_token_file: /file +chat_id_file: /chat_id_file +`, + }, + { + name: "with no chat_id set - it fails", + in: ` +bot_token: xyz +`, + expected: errors.New("missing chat_id or chat_id_file on telegram_config"), + }, + { + name: "with both chat_id and chat_id_file - it fails", + in: ` +bot_token: xyz +chat_id: 123 +chat_id_file: /file +`, + expected: errors.New("at most one of chat_id & chat_id_file must be configured"), + }, + { + name: "with unknown parse_mode - it fails", + in: ` +bot_token: xyz +chat_id: 123 +parse_mode: invalid +`, + expected: errors.New("unknown parse_mode on telegram_config, must be Markdown, MarkdownV2, HTML or empty string"), + }, + } + + for _, tt := range tc { + t.Run(tt.name, func(t *testing.T) { + var cfg TelegramConfig + err := yaml.UnmarshalStrict([]byte(tt.in), &cfg) + + require.Equal(t, tt.expected, err) + }) + } +} diff --git a/notify/telegram/telegram.go b/notify/telegram/telegram.go index 07bbedbbe8..82919cf5f6 100644 --- a/notify/telegram/telegram.go +++ b/notify/telegram/telegram.go @@ -25,7 +25,6 @@ import ( commoncfg "github.com/prometheus/common/config" "gopkg.in/telebot.v3" - "github.com/prometheus/alertmanager/config" "github.com/prometheus/alertmanager/notify" "github.com/prometheus/alertmanager/template" "github.com/prometheus/alertmanager/types" @@ -36,7 +35,7 @@ const maxMessageLenRunes = 4096 // Notifier implements a Notifier for telegram notifications. type Notifier struct { - conf *config.TelegramConfig + conf *TelegramConfig tmpl *template.Template logger *slog.Logger client *telebot.Bot @@ -44,7 +43,7 @@ type Notifier struct { } // New returns a new Telegram notification handler. -func New(conf *config.TelegramConfig, t *template.Template, l *slog.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) { +func New(conf *TelegramConfig, t *template.Template, l *slog.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) { httpclient, err := notify.NewClientWithTracing(*conf.HTTPConfig, "telegram", httpOpts...) if err != nil { return nil, err diff --git a/notify/telegram/telegram_test.go b/notify/telegram/telegram_test.go index 7192d493fd..78435e7f28 100644 --- a/notify/telegram/telegram_test.go +++ b/notify/telegram/telegram_test.go @@ -33,7 +33,6 @@ import ( amcommoncfg "github.com/prometheus/alertmanager/config/common" - "github.com/prometheus/alertmanager/config" "github.com/prometheus/alertmanager/notify" "github.com/prometheus/alertmanager/notify/test" "github.com/prometheus/alertmanager/types" @@ -41,27 +40,20 @@ import ( func TestTelegramUnmarshal(t *testing.T) { in := ` -route: - receiver: test -receivers: -- name: test - telegram_configs: - - chat_id: 1234 - bot_token: secret - message_thread_id: 1357 + chat_id: 1234 + bot_token: secret + message_thread_id: 1357 + api_url: https://api.telegram.org ` - var c config.Config + var c TelegramConfig err := yaml.Unmarshal([]byte(in), &c) require.NoError(t, err) - require.Len(t, c.Receivers, 1) - require.Len(t, c.Receivers[0].TelegramConfigs, 1) - - require.Equal(t, "https://api.telegram.org", c.Receivers[0].TelegramConfigs[0].APIUrl.String()) - require.Equal(t, commoncfg.Secret("secret"), c.Receivers[0].TelegramConfigs[0].BotToken) - require.Equal(t, int64(1234), c.Receivers[0].TelegramConfigs[0].ChatID) - require.Equal(t, 1357, c.Receivers[0].TelegramConfigs[0].MessageThreadID) - require.Equal(t, "HTML", c.Receivers[0].TelegramConfigs[0].ParseMode) + require.Equal(t, "https://api.telegram.org", c.APIUrl.String()) + require.Equal(t, commoncfg.Secret("secret"), c.BotToken) + require.Equal(t, int64(1234), c.ChatID) + require.Equal(t, 1357, c.MessageThreadID) + require.Equal(t, "HTML", c.ParseMode) } func TestTelegramRetry(t *testing.T) { @@ -73,7 +65,7 @@ func TestTelegramRetry(t *testing.T) { }, } notifier, err := New( - &config.TelegramConfig{ + &TelegramConfig{ HTTPConfig: &commoncfg.HTTPClientConfig{}, APIUrl: &fakeURL, }, @@ -98,12 +90,12 @@ func TestTelegramNotify(t *testing.T) { for _, tc := range []struct { name string - cfg config.TelegramConfig + cfg TelegramConfig expText string }{ { name: "No escaping by default", - cfg: config.TelegramConfig{ + cfg: TelegramConfig{ Message: "x < y", HTTPConfig: &commoncfg.HTTPClientConfig{}, BotToken: commoncfg.Secret(token), @@ -112,7 +104,7 @@ func TestTelegramNotify(t *testing.T) { }, { name: "Characters escaped in HTML mode", - cfg: config.TelegramConfig{ + cfg: TelegramConfig{ ParseMode: "HTML", Message: "x < y", HTTPConfig: &commoncfg.HTTPClientConfig{}, @@ -122,7 +114,7 @@ func TestTelegramNotify(t *testing.T) { }, { name: "Bot token from file", - cfg: config.TelegramConfig{ + cfg: TelegramConfig{ Message: "test", HTTPConfig: &commoncfg.HTTPClientConfig{}, BotTokenFile: fileWithToken.Name(), @@ -131,7 +123,7 @@ func TestTelegramNotify(t *testing.T) { }, { name: "HTML mode with too-large message", - cfg: config.TelegramConfig{ + cfg: TelegramConfig{ ParseMode: "HTML", Message: strings.Repeat("x", 5000), HTTPConfig: &commoncfg.HTTPClientConfig{}, @@ -142,7 +134,7 @@ func TestTelegramNotify(t *testing.T) { }, { name: "Default mode with too-large message", - cfg: config.TelegramConfig{ + cfg: TelegramConfig{ Message: strings.Repeat("y", 5000), HTTPConfig: &commoncfg.HTTPClientConfig{}, BotToken: commoncfg.Secret(token), @@ -151,7 +143,7 @@ func TestTelegramNotify(t *testing.T) { }, { name: "HTML mode with message smaller than limit", - cfg: config.TelegramConfig{ + cfg: TelegramConfig{ ParseMode: "HTML", Message: strings.Repeat("a", 100), HTTPConfig: &commoncfg.HTTPClientConfig{}, @@ -161,7 +153,7 @@ func TestTelegramNotify(t *testing.T) { }, { name: "Default mode with message smaller than limit", - cfg: config.TelegramConfig{ + cfg: TelegramConfig{ Message: strings.Repeat("b", 100), HTTPConfig: &commoncfg.HTTPClientConfig{}, BotToken: commoncfg.Secret(token), From 75a001b308404e77802859ba13f484a3dd498d78 Mon Sep 17 00:00:00 2001 From: Siavash Safi Date: Sat, 18 Jul 2026 12:16:39 +0200 Subject: [PATCH 087/120] eventrecorder: add optional webhook batching (#5392) Allow webhook outputs to send events as JSON arrays, reducing HTTP request overhead for endpoints that support batched ingestion. Add a single batching coordinator per output that flushes events when the configured count, encoded size, or interval limit is reached. Completed batches are dispatched across the existing HTTP worker pool, while partial batches are flushed during shutdown and retries resend the complete payload. Preserve the existing single-object webhook contract by requiring batching to be explicitly enabled with `batch: true`. Document batching controls, duplicate and ordering semantics, and using a batched webhook output with Cloudflare Pipelines: https://developers.cloudflare.com/pipelines/streams/writing-to-streams/ Cover count-, size-, interval-, retry-, and shutdown-triggered delivery. Signed-off-by: Siavash Safi --- CHANGELOG.md | 1 + config/config_test.go | 24 ++++ docs/configuration.md | 40 ++++++- eventrecorder/recorder.go | 2 +- eventrecorder/webhook.go | 189 +++++++++++++++++++++++++++++-- eventrecorder/webhook_test.go | 202 ++++++++++++++++++++++++++++++++++ 6 files changed, 445 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 297f5ec9c4..9f77d126d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ * [CHANGE] notify: The `reason` label on `alertmanager_notifications_failed_total` now distinguishes `authError` (HTTP 401/403) and `rateLimited` (HTTP 429) from the generic `clientError`. Dashboards/alerts matching `reason="clientError"` for these codes must be updated. * [ENHANCEMENT] notify: The discord and webex integrations now report a failure `reason` on `alertmanager_notifications_failed_total`. +* [ENHANCEMENT] eventrecorder: Add optional webhook batching. * [BUGFIX] webhook: Keep custom `payload` string values verbatim instead of reinterpreting JSON leaves that look like YAML (e.g. values ending with a colon). #5302 ## 0.33.1 / 2026-07-04 diff --git a/config/config_test.go b/config/config_test.go index b096c67bc8..1ac0a0172b 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -45,6 +45,30 @@ func TestLoadEmptyString(t *testing.T) { } } +func TestEventRecorderWebhookBatchingConfig(t *testing.T) { + cfg, err := Load(` +route: + receiver: default +receivers: +- name: default +event_recorder: + webhook_outputs: + - url: https://stream-id.ingest.cloudflare.com + batch: true + http_config: + authorization: + credentials_file: pipeline-token +`) + require.NoError(t, err) + require.Len(t, cfg.EventRecorder.WebhookOutputs, 1) + require.True(t, cfg.EventRecorder.WebhookOutputs[0].Batch) + + resolveFilepaths("/etc/alertmanager", cfg) + auth := cfg.EventRecorder.WebhookOutputs[0].HTTPConfig.Authorization + require.NotNil(t, auth) + require.Equal(t, "/etc/alertmanager/pipeline-token", auth.CredentialsFile) +} + func TestDefaultReceiverExists(t *testing.T) { in := ` route: diff --git a/docs/configuration.md b/docs/configuration.md index e202385c39..3d95bca3e5 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -170,7 +170,8 @@ time_intervals: # Optional event recorder configuration. Captures significant # Alertmanager events (startup/shutdown, alert lifecycle, silences, # notifications) and ships them to one or more outputs (file, webhook, -# kafka). Recording is gated behind the `event-recorder` feature flag; +# Kafka, stdout). Recording is gated behind the +# `event-recorder` feature flag; # pass `--enable-feature=event-recorder` on the command line to # activate it. See the Event Recorder section below. [ event_recorder: ] @@ -2174,6 +2175,10 @@ POSTs each event as a JSON body to an HTTP endpoint. Delivery is performed by a bounded worker pool with bounded retries and exponential backoff. +Retries resend the entire event or batch, so receivers should tolerate +duplicate events after ambiguous failures. With multiple workers, requests +may complete out of order; set `workers: 1` when request ordering matters. + ```yaml # URL to POST events to. url: @@ -2187,12 +2192,43 @@ url: # Number of concurrent delivery workers. [ workers: | default = 4 ] -# Maximum number of delivery attempts per event. +# Maximum number of delivery attempts per event or batch. [ max_retries: | default = 3 ] # Base backoff between retries; subsequent attempts use exponential # backoff (base * 2^attempt) capped at 30s. [ retry_backoff: | default = 500ms ] + +# Send events in JSON arrays instead of posting each event as an individual +# JSON object. This changes the webhook payload contract and must only be +# enabled when the receiving endpoint accepts arrays. +[ batch: | default = false ] + +# Maximum number of events in one request when batching is enabled. +[ batch_max_events: | default = 100 ] + +# Soft maximum encoded request size in bytes when batching is enabled. A +# single event larger than the limit is sent alone. +[ batch_max_bytes: | default = 1048576 ] + +# Maximum time an incomplete batch waits before delivery. +[ batch_flush_interval: | default = 100ms ] +``` + +For example, [Cloudflare Pipelines streams](https://developers.cloudflare.com/pipelines/streams/writing-to-streams/) +accept JSON arrays through their HTTP ingestion endpoints and can be configured +as a batched webhook output: + +```yaml +event_recorder: + webhook_outputs: + - url: https://.ingest.cloudflare.com + batch: true + http_config: + # The token must have the "Workers Pipeline Send" permission when + # authentication is enabled for the stream. + authorization: + credentials: ``` #### `` diff --git a/eventrecorder/recorder.go b/eventrecorder/recorder.go index 1f0a081138..7ce27ed218 100644 --- a/eventrecorder/recorder.go +++ b/eventrecorder/recorder.go @@ -183,7 +183,7 @@ func buildOutputs(cfg Config, instance string, m *metrics, logger *slog.Logger) for _, wc := range cfg.WebhookOutputs { wo, err := NewWebhookOutput(wc, m.outputDrops, logger) if err != nil { - logger.Error("Failed to create webhook event recorder output", "url", wc.URL, "err", err) + logger.Error("Failed to create webhook event recorder output", "url", sanitizeSecretURL(wc.URL), "err", err) continue } outputs = append(outputs, wo) diff --git a/eventrecorder/webhook.go b/eventrecorder/webhook.go index b2d4bc548a..1a4972ae1b 100644 --- a/eventrecorder/webhook.go +++ b/eventrecorder/webhook.go @@ -51,6 +51,14 @@ type WebhookOutputConfig struct { // 500ms). Successive attempts use exponential backoff (base * // 2^attempt). RetryBackoff model.Duration `yaml:"retry_backoff,omitempty" json:"retry_backoff,omitempty"` + // Batch enables sending events as JSON arrays instead of individual objects. + Batch bool `yaml:"batch,omitempty" json:"batch,omitempty"` + // BatchMaxEvents is the maximum number of events in one request (default 100). + BatchMaxEvents int `yaml:"batch_max_events,omitempty" json:"batch_max_events,omitempty"` + // BatchMaxBytes is the soft maximum encoded request size (default 1 MiB). + BatchMaxBytes int `yaml:"batch_max_bytes,omitempty" json:"batch_max_bytes,omitempty"` + // BatchFlushInterval is the maximum time an incomplete batch waits (default 100ms). + BatchFlushInterval model.Duration `yaml:"batch_flush_interval,omitempty" json:"batch_flush_interval,omitempty"` } // UnmarshalYAML implements the yaml.Unmarshaler interface, validating @@ -72,6 +80,12 @@ func (c *WebhookOutputConfig) UnmarshalYAML(unmarshal func(any) error) error { if c.URL.Scheme == "" || c.URL.Host == "" { return errors.New("event_recorder webhook output requires an absolute http(s) url") } + if c.BatchMaxEvents < 0 || c.BatchMaxBytes < 0 || c.BatchFlushInterval < 0 { + return errors.New("event_recorder webhook batch settings cannot be negative") + } + if !c.Batch && (c.BatchMaxEvents != 0 || c.BatchMaxBytes != 0 || c.BatchFlushInterval != 0) { + return errors.New("event_recorder webhook batch settings require batch: true") + } return nil } @@ -100,6 +114,15 @@ func (c WebhookOutputConfig) equal(o WebhookOutputConfig) bool { if c.RetryBackoff != o.RetryBackoff { return false } + if c.Batch != o.Batch { + return false + } + if c.Batch && !httpBatchConfigsEqual( + newHTTPBatchConfig(c.BatchMaxEvents, c.BatchMaxBytes, c.BatchFlushInterval), + newHTTPBatchConfig(o.BatchMaxEvents, o.BatchMaxBytes, o.BatchFlushInterval), + ) { + return false + } return reflect.DeepEqual(c.HTTPConfig, o.HTTPConfig) } @@ -109,6 +132,9 @@ const ( defaultWebhookMaxRetries = 3 defaultWebhookRetryBackoff = 500 * time.Millisecond defaultWebhookMaxBackoff = 30 * time.Second + defaultHTTPBatchMaxEvents = 100 + defaultHTTPBatchMaxBytes = 1 << 20 + defaultHTTPBatchInterval = 100 * time.Millisecond webhookQueueSize = 1024 ) @@ -121,19 +147,58 @@ type WebhookOutput struct { client *http.Client url string name string + kind string + batch *httpBatchConfig maxRetries int retryBackoff time.Duration maxBackoff time.Duration logger *slog.Logger drops prometheus.Counter work chan []byte + batches chan []byte done chan struct{} cancel chan struct{} // closed after drain to abort remaining retries wg sync.WaitGroup } +type httpBatchConfig struct { + maxEvents int + maxBytes int + flushInterval time.Duration +} + // NewWebhookOutput creates a new webhook-based event recorder output. func NewWebhookOutput(cfg WebhookOutputConfig, dropsCounter *prometheus.CounterVec, logger *slog.Logger) (*WebhookOutput, error) { + var batch *httpBatchConfig + if cfg.Batch { + batch = newHTTPBatchConfig(cfg.BatchMaxEvents, cfg.BatchMaxBytes, cfg.BatchFlushInterval) + } + return newWebhookOutput(cfg, "webhook", batch, dropsCounter, logger) +} + +func newHTTPBatchConfig(maxEvents, maxBytes int, flushInterval model.Duration) *httpBatchConfig { + batch := &httpBatchConfig{ + maxEvents: defaultHTTPBatchMaxEvents, + maxBytes: defaultHTTPBatchMaxBytes, + flushInterval: defaultHTTPBatchInterval, + } + if maxEvents > 0 { + batch.maxEvents = maxEvents + } + if maxBytes > 0 { + batch.maxBytes = maxBytes + } + if flushInterval > 0 { + batch.flushInterval = time.Duration(flushInterval) + } + return batch +} + +func httpBatchConfigsEqual(a, b *httpBatchConfig) bool { + return *a == *b +} + +func newWebhookOutput(cfg WebhookOutputConfig, kind string, batch *httpBatchConfig, dropsCounter *prometheus.CounterVec, logger *slog.Logger) (*WebhookOutput, error) { httpCfg := commoncfg.DefaultHTTPClientConfig if cfg.HTTPConfig != nil { httpCfg = *cfg.HTTPConfig @@ -141,7 +206,7 @@ func NewWebhookOutput(cfg WebhookOutputConfig, dropsCounter *prometheus.CounterV client, err := commoncfg.NewClientFromConfig(httpCfg, "eventrecorder") if err != nil { - return nil, fmt.Errorf("creating HTTP client for event recorder webhook: %w", err) + return nil, fmt.Errorf("creating HTTP client for event recorder %s: %w", kind, err) } timeout := defaultWebhookTimeout @@ -166,23 +231,36 @@ func NewWebhookOutput(cfg WebhookOutputConfig, dropsCounter *prometheus.CounterV } urlStr := cfg.URL.String() + name := fmt.Sprintf("%s:%s", kind, sanitizeURL(urlStr)) wo := &WebhookOutput{ client: client, url: urlStr, - name: fmt.Sprintf("webhook:%s", sanitizeURL(urlStr)), + name: name, + kind: kind, + batch: batch, maxRetries: maxRetries, retryBackoff: retryBackoff, maxBackoff: defaultWebhookMaxBackoff, logger: logger, - drops: dropsCounter.WithLabelValues(fmt.Sprintf("webhook:%s", sanitizeURL(urlStr))), + drops: dropsCounter.WithLabelValues(name), work: make(chan []byte, webhookQueueSize), done: make(chan struct{}), cancel: make(chan struct{}), } - for range workers { + if batch != nil { wo.wg.Add(1) - go wo.worker() + wo.batches = make(chan []byte, workers) + go wo.batchLoop() + for range workers { + wo.wg.Add(1) + go wo.batchDeliveryWorker() + } + } else { + for range workers { + wo.wg.Add(1) + go wo.worker() + } } return wo, nil @@ -202,6 +280,13 @@ func sanitizeURL(raw string) string { return u.String() } +func sanitizeSecretURL(u *amcommoncfg.SecretURL) string { + if u == nil || u.URL == nil { + return "" + } + return sanitizeURL(u.String()) +} + // Name returns a stable identifier for this output. The URL is // sanitized to avoid leaking credentials. func (wo *WebhookOutput) Name() string { @@ -221,7 +306,7 @@ func (wo *WebhookOutput) SendEvent(event *eventrecorderpb.Event) (int, error) { case wo.work <- data: default: wo.drops.Inc() - wo.logger.Warn("Event recorder webhook queue full, dropping event", "output", wo.name) + wo.logger.Warn("Event recorder HTTP output queue full, dropping event", "output", wo.name) } return len(data), nil } @@ -246,13 +331,97 @@ func (wo *WebhookOutput) worker() { } } +func (wo *WebhookOutput) batchLoop() { + defer wo.wg.Done() + defer close(wo.batches) + + batch := make([][]byte, 0, min(wo.batch.maxEvents, webhookQueueSize)) + batchSize := 2 // Opening and closing brackets. + var timer *time.Timer + var timerC <-chan time.Time + + stopTimer := func() { + if timer != nil { + timer.Stop() + timer = nil + timerC = nil + } + } + flush := func() { + stopTimer() + if len(batch) == 0 { + return + } + wo.batches <- jsonArray(batch, batchSize) + batch = batch[:0] + batchSize = 2 + } + add := func(data []byte) { + additionalSize := len(data) + if len(batch) > 0 { + additionalSize++ // Comma separator. + } + if len(batch) > 0 && (len(batch) >= wo.batch.maxEvents || batchSize+additionalSize > wo.batch.maxBytes) { + flush() + additionalSize = len(data) + } + batch = append(batch, data) + batchSize += additionalSize + if len(batch) == 1 { + timer = time.NewTimer(wo.batch.flushInterval) + timerC = timer.C + } + if len(batch) >= wo.batch.maxEvents || batchSize >= wo.batch.maxBytes { + flush() + } + } + + for { + select { + case data := <-wo.work: + add(data) + case <-timerC: + flush() + case <-wo.done: + for { + select { + case data := <-wo.work: + add(data) + default: + flush() + return + } + } + } + } +} + +func (wo *WebhookOutput) batchDeliveryWorker() { + defer wo.wg.Done() + for data := range wo.batches { + wo.postWithRetry(data) + } +} + +func jsonArray(events [][]byte, size int) []byte { + data := make([]byte, 0, size) + data = append(data, '[') + for i, event := range events { + if i > 0 { + data = append(data, ',') + } + data = append(data, event...) + } + return append(data, ']') +} + func (wo *WebhookOutput) postWithRetry(data []byte) { for attempt := range wo.maxRetries { err := wo.post(data) if err == nil { return } - wo.logger.Warn("Event recorder webhook POST failed", "output", wo.name, "attempt", attempt+1, "err", err) + wo.logger.Warn("Event recorder HTTP output POST failed", "output", wo.name, "attempt", attempt+1, "err", err) if attempt < wo.maxRetries-1 { backoff := min(wo.retryBackoff< Date: Sat, 18 Jul 2026 12:17:45 +0200 Subject: [PATCH 088/120] test(nflog): fix flaky maintenance callback timing (#5394) Use synctest.Wait to establish the maintenance ticker without advancing virtual time, and ensure the goroutine is stopped if an assertion fails. Signed-off-by: Siavash Safi --- nflog/nflog_test.go | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/nflog/nflog_test.go b/nflog/nflog_test.go index f640c96286..538182b01c 100644 --- a/nflog/nflog_test.go +++ b/nflog/nflog_test.go @@ -158,7 +158,14 @@ func TestWithMaintenance_SupportsCustomCallback(t *testing.T) { return 0, nil }) }) - gosched() + stopped := false + defer func() { + if !stopped { + close(stopc) + wg.Wait() + } + }() + synctest.Wait() // Before the first tick, no maintenance executed. time.Sleep(99 * time.Millisecond) @@ -172,6 +179,7 @@ func TestWithMaintenance_SupportsCustomCallback(t *testing.T) { // Stop the maintenance loop. We should get exactly one more execution of the maintenance func. close(stopc) wg.Wait() + stopped = true require.EqualValues(t, 2, calls.Load()) // Check the maintenance metrics. @@ -380,9 +388,3 @@ func TestStateDecodingError(t *testing.T) { _, err = decodeState(bytes.NewReader(msg)) require.Equal(t, ErrInvalidState, err) } - -// runtime.Gosched() does not "suspend" the current goroutine so there's no guarantee that the main goroutine won't -// be able to continue. For more see https://pkg.go.dev/runtime#Gosched. -func gosched() { - time.Sleep(1 * time.Millisecond) -} From a54872a1f22fd1fa7ca33cc40c4007e4c56024c4 Mon Sep 17 00:00:00 2001 From: PrometheusBot Date: Tue, 21 Jul 2026 07:47:51 +0200 Subject: [PATCH 089/120] Synchronize common files from prometheus/prometheus (#5386) Propagating changes from prometheus/prometheus default branch. *Source can be found [here](https://github.com/prometheus/prometheus/blob/main/scripts/sync_repo_files.sh).* To check out this branch locally and push changes back: ``` git remote add prombot https://github.com/prombot/prometheus_alertmanager.git git fetch prombot repo_sync git checkout -b repo_sync prombot/repo_sync ``` Signed-off-by: prombot --- .github/workflows/approve-workflows.yml | 27 +++++++++++++++++++++ .github/workflows/container_description.yml | 3 +++ .github/workflows/govulncheck.yml | 3 +++ .github/workflows/stale.yml | 3 +++ .yamllint | 3 +++ 5 files changed, 39 insertions(+) create mode 100644 .github/workflows/approve-workflows.yml diff --git a/.github/workflows/approve-workflows.yml b/.github/workflows/approve-workflows.yml new file mode 100644 index 0000000000..f372012f2b --- /dev/null +++ b/.github/workflows/approve-workflows.yml @@ -0,0 +1,27 @@ +--- +### +# This action is synced from https://github.com/prometheus/prometheus +### +name: Approve pending workflows + +on: + issue_comment: + types: [created] + +permissions: read-all + +jobs: + approve: + if: >- + github.event.issue.pull_request && + github.event.comment.body == '/workflow-approve' && + (github.repository_owner == 'prometheus' || github.repository_owner == 'prometheus-community') + runs-on: ubuntu-latest + permissions: + actions: write + contents: read + pull-requests: write + steps: + - uses: prometheus/promci/approve_workflows@370e8c15dcec50043cbe66f2f34633d9efc0a190 # v0.9.0 + with: + github_token: ${{ github.token }} diff --git a/.github/workflows/container_description.yml b/.github/workflows/container_description.yml index 0e2f274b9d..591a4d687f 100644 --- a/.github/workflows/container_description.yml +++ b/.github/workflows/container_description.yml @@ -1,4 +1,7 @@ --- +### +# This action is synced from https://github.com/prometheus/prometheus +### name: Push README to Docker Hub on: push: diff --git a/.github/workflows/govulncheck.yml b/.github/workflows/govulncheck.yml index 621476dec4..374df32baf 100644 --- a/.github/workflows/govulncheck.yml +++ b/.github/workflows/govulncheck.yml @@ -1,4 +1,7 @@ --- +### +# This action is synced from https://github.com/prometheus/prometheus +### name: govulncheck on: pull_request: diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 74d037f8f1..5df426b096 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -1,3 +1,6 @@ +### +# This action is synced from https://github.com/prometheus/prometheus +### name: Stale Check on: workflow_dispatch: {} diff --git a/.yamllint b/.yamllint index b329f464fb..b52b0be4f0 100644 --- a/.yamllint +++ b/.yamllint @@ -1,4 +1,7 @@ --- +### +# This file is synced from https://github.com/prometheus/prometheus +### extends: default ignore: | **/node_modules From 949777a35bf92dd6e31c381db17afd716ed7b004 Mon Sep 17 00:00:00 2001 From: dongjiang Date: Tue, 21 Jul 2026 17:30:18 +0800 Subject: [PATCH 090/120] feat(notify): Add AWS external_id support in sigv4 configuration (#5157) Signed-off-by: dongjiang --- docs/configuration.md | 4 ++++ notify/sns/sns.go | 11 ++++++++++- notify/sns/sns_test.go | 4 +++- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 3d95bca3e5..8b7f510158 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1849,6 +1849,10 @@ attributes: # AWS Role ARN, an alternative to using AWS API keys. [ role_arn: ] + +# AWS External ID used when assuming a role. +# Can only be used with role_arn. +[ external_id: ] ``` ### `` diff --git a/notify/sns/sns.go b/notify/sns/sns.go index 873e3328d1..8cc6da5806 100644 --- a/notify/sns/sns.go +++ b/notify/sns/sns.go @@ -182,7 +182,16 @@ func (n *Notifier) createSNSClient(ctx context.Context, tmpl func(string) string return nil, fmt.Errorf("failed to load base config for STS: %w", err) } stsClient := sts.NewFromConfig(stsCfg) - stsProvider := stscreds.NewAssumeRoleProvider(stsClient, n.conf.Sigv4.RoleARN) + stsProvider := stscreds.NewAssumeRoleProvider( + stsClient, + n.conf.Sigv4.RoleARN, + // This adds an optional external_id configuration field that is passed to STS AssumeRole when role_arn is specified. + func(o *stscreds.AssumeRoleOptions) { + if n.conf.Sigv4.ExternalID != "" { + o.ExternalID = aws.String(n.conf.Sigv4.ExternalID) + } + }, + ) // Add the AssumeRole provider to the options for the SNS client config. snsCfgOpts = append(snsCfgOpts, awsconfig.WithCredentialsProvider(aws.NewCredentialsCache(stsProvider))) } diff --git a/notify/sns/sns_test.go b/notify/sns/sns_test.go index 7bafdc733d..9dae19820f 100644 --- a/notify/sns/sns_test.go +++ b/notify/sns/sns_test.go @@ -118,7 +118,9 @@ func TestNotifyWithInvalidTemplate(t *testing.T) { HTTPConfig: &commoncfg.HTTPClientConfig{}, TopicARN: "TestTopic", Sigv4: sigv4.SigV4Config{ - Region: "us-west-2", + Region: "us-west-2", + RoleARN: "my:role/arn", + ExternalID: "external_id", }, } if tc.updateCfg != nil { From ba3dbbf6d696e8aa472c467408b73f112a456d0d Mon Sep 17 00:00:00 2001 From: Will Hegedus Date: Sun, 26 Jul 2026 11:20:05 -0400 Subject: [PATCH 091/120] perf(ui): lazily render the alert list with Html.Lazy and Html.Keyed (#5357) The /alerts view rebuilt and re-diffed the entire alert-group tree on every model update, so interactions such as typing in the filter bar were sluggish when many alerts (10K+) were loaded. This wraps the alert-group rendering in lazy4 so it is skipped while its inputs are unchanged, and render the group and alert lists with Html.Keyed keyed by group identity and alert fingerprint so list diffs are matched by identity rather than position. It's similar to what is done for listing silences [here](https://github.com/prometheus/alertmanager/blob/1dee97b337fbd7af7d2bcd0f154b1a4da2b62e41/ui/app/src/Views/SilenceList/Views.elm). Now, even though loading a 30MB+ response of all alerts is still slow on first load, typing in the filter box is at least responsive. --------- Signed-off-by: Will Hegedus Co-authored-by: Claude Opus 4.8 --- ui/app/src/Views/AlertList/Views.elm | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/ui/app/src/Views/AlertList/Views.elm b/ui/app/src/Views/AlertList/Views.elm index 09f365845b..ec9338626d 100644 --- a/ui/app/src/Views/AlertList/Views.elm +++ b/ui/app/src/Views/AlertList/Views.elm @@ -7,6 +7,8 @@ import Dict import Html exposing (..) import Html.Attributes exposing (..) import Html.Events exposing (..) +import Html.Keyed +import Html.Lazy exposing (lazy4) import Set exposing (Set) import Types exposing (Msg(..)) import Utils.Filter exposing (Filter) @@ -83,10 +85,15 @@ view { alertGroups, groupBar, filterBar, receiverBar, tab, activeId, activeGroup [ i [ class "fa fa-plus mr-3" ] [], text "Expand all groups" ] ) ] - , Utils.Views.apiData (defaultAlertGroups activeId activeGroups expandAll) alertGroups + , lazy4 alertGroupsView activeId activeGroups expandAll alertGroups ] +alertGroupsView : Maybe String -> Set Int -> Bool -> ApiData (List AlertGroup) -> Html Msg +alertGroupsView activeId activeGroups expandAll alertGroups = + Utils.Views.apiData (defaultAlertGroups activeId activeGroups expandAll) alertGroups + + defaultAlertGroups : Maybe String -> Set Int -> Bool -> List AlertGroup -> Html Msg defaultAlertGroups activeId activeGroups expandAll groups = case groups of @@ -178,7 +185,8 @@ alertGroup activeId activeGroups receiver labels routeLabels alerts groupId expa div [] [ div [ class "mb-3" ] (expandButton :: labels_ ++ routeLabels_ ++ alertEl) , if groupActive then - ul [ class "list-group mb-0" ] (List.map (AlertView.view labels activeId) alerts) + Html.Keyed.ul [ class "list-group mb-0" ] + (List.map (\alert -> ( alert.fingerprint, AlertView.view labels activeId alert )) alerts) else text "" From d7b70222325258e872c08c243c6fd26ed2d4dcf8 Mon Sep 17 00:00:00 2001 From: Christoph Maser Date: Sun, 26 Jul 2026 17:22:26 +0200 Subject: [PATCH 092/120] refactor(pagerduty): move configuration types into pagerduty package (#5325) Signed-off-by: Christoph Maser --- config/config.go | 3 +- config/notifiers.go | 88 -------------- config/notifiers_test.go | 163 ------------------------- notify/pagerduty/config.go | 113 ++++++++++++++++++ notify/pagerduty/config_test.go | 184 +++++++++++++++++++++++++++++ notify/pagerduty/pagerduty.go | 5 +- notify/pagerduty/pagerduty_test.go | 61 +++++----- 7 files changed, 329 insertions(+), 288 deletions(-) create mode 100644 notify/pagerduty/config.go create mode 100644 notify/pagerduty/config_test.go diff --git a/config/config.go b/config/config.go index 9ede247077..d8232b21a9 100644 --- a/config/config.go +++ b/config/config.go @@ -39,6 +39,7 @@ import ( "github.com/prometheus/alertmanager/notify/msteams" "github.com/prometheus/alertmanager/notify/msteamsv2" "github.com/prometheus/alertmanager/notify/opsgenie" + "github.com/prometheus/alertmanager/notify/pagerduty" "github.com/prometheus/alertmanager/notify/telegram" "github.com/prometheus/alertmanager/notify/webhook" "github.com/prometheus/alertmanager/timeinterval" @@ -978,7 +979,7 @@ type Receiver struct { DiscordConfigs []*discord.DiscordConfig `yaml:"discord_configs,omitempty" json:"discord_configs,omitempty"` EmailConfigs []*EmailConfig `yaml:"email_configs,omitempty" json:"email_configs,omitempty"` IncidentioConfigs []*incidentio.IncidentioConfig `yaml:"incidentio_configs,omitempty" json:"incidentio_configs,omitempty"` - PagerdutyConfigs []*PagerdutyConfig `yaml:"pagerduty_configs,omitempty" json:"pagerduty_configs,omitempty"` + PagerdutyConfigs []*pagerduty.PagerdutyConfig `yaml:"pagerduty_configs,omitempty" json:"pagerduty_configs,omitempty"` SlackConfigs []*SlackConfig `yaml:"slack_configs,omitempty" json:"slack_configs,omitempty"` WebhookConfigs []*webhook.WebhookConfig `yaml:"webhook_configs,omitempty" json:"webhook_configs,omitempty"` OpsGenieConfigs []*opsgenie.OpsGenieConfig `yaml:"opsgenie_configs,omitempty" json:"opsgenie_configs,omitempty"` diff --git a/config/notifiers.go b/config/notifiers.go index 74534a741a..a9052475d5 100644 --- a/config/notifiers.go +++ b/config/notifiers.go @@ -50,24 +50,6 @@ var ( // DefaultEmailSubject defines the default Subject header of an Email. DefaultEmailSubject = `{{ template "email.default.subject" . }}` - // DefaultPagerdutyDetails defines the default values for PagerDuty details. - DefaultPagerdutyDetails = map[string]any{ - "firing": `{{ .Alerts.Firing | toJson }}`, - "resolved": `{{ .Alerts.Resolved | toJson }}`, - "num_firing": `{{ .Alerts.Firing | len }}`, - "num_resolved": `{{ .Alerts.Resolved | len }}`, - } - - // DefaultPagerdutyConfig defines default values for PagerDuty configurations. - DefaultPagerdutyConfig = PagerdutyConfig{ - NotifierConfig: amcommoncfg.NotifierConfig{ - VSendResolved: true, - }, - Description: `{{ template "pagerduty.default.description" .}}`, - Client: `{{ template "pagerduty.default.client" . }}`, - ClientURL: `{{ template "pagerduty.default.clientURL" . }}`, - } - // DefaultSlackConfig defines default values for Slack configurations. DefaultSlackConfig = SlackConfig{ NotifierConfig: amcommoncfg.NotifierConfig{ @@ -244,76 +226,6 @@ func (c *EmailConfig) UnmarshalYAML(unmarshal func(any) error) error { return nil } -// PagerdutyConfig configures notifications via PagerDuty. -type PagerdutyConfig struct { - amcommoncfg.NotifierConfig `yaml:",inline" json:",inline"` - - HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"` - - ServiceKey commoncfg.Secret `yaml:"service_key,omitempty" json:"service_key,omitempty"` - ServiceKeyFile string `yaml:"service_key_file,omitempty" json:"service_key_file,omitempty"` - RoutingKey commoncfg.Secret `yaml:"routing_key,omitempty" json:"routing_key,omitempty"` - RoutingKeyFile string `yaml:"routing_key_file,omitempty" json:"routing_key_file,omitempty"` - URL *amcommoncfg.URL `yaml:"url,omitempty" json:"url,omitempty"` - Client string `yaml:"client,omitempty" json:"client,omitempty"` - ClientURL string `yaml:"client_url,omitempty" json:"client_url,omitempty"` - Description string `yaml:"description,omitempty" json:"description,omitempty"` - Details map[string]any `yaml:"details,omitempty" json:"details,omitempty"` - Images []PagerdutyImage `yaml:"images,omitempty" json:"images,omitempty"` - Links []PagerdutyLink `yaml:"links,omitempty" json:"links,omitempty"` - Source string `yaml:"source,omitempty" json:"source,omitempty"` - Severity string `yaml:"severity,omitempty" json:"severity,omitempty"` - Class string `yaml:"class,omitempty" json:"class,omitempty"` - Component string `yaml:"component,omitempty" json:"component,omitempty"` - Group string `yaml:"group,omitempty" json:"group,omitempty"` - // Timeout is the maximum time allowed to invoke the pagerduty. Setting this to 0 - // does not impose a timeout. - Timeout time.Duration `yaml:"timeout" json:"timeout"` -} - -// PagerdutyLink is a link. -type PagerdutyLink struct { - Href string `yaml:"href,omitempty" json:"href,omitempty"` - Text string `yaml:"text,omitempty" json:"text,omitempty"` -} - -// PagerdutyImage is an image. -type PagerdutyImage struct { - Src string `yaml:"src,omitempty" json:"src,omitempty"` - Alt string `yaml:"alt,omitempty" json:"alt,omitempty"` - Href string `yaml:"href,omitempty" json:"href,omitempty"` -} - -// UnmarshalYAML implements the yaml.Unmarshaler interface. -func (c *PagerdutyConfig) UnmarshalYAML(unmarshal func(any) error) error { - *c = DefaultPagerdutyConfig - type plain PagerdutyConfig - if err := unmarshal((*plain)(c)); err != nil { - return err - } - if c.RoutingKey == "" && c.ServiceKey == "" && c.RoutingKeyFile == "" && c.ServiceKeyFile == "" { - return errors.New("missing service or routing key in PagerDuty config") - } - if len(c.RoutingKey) > 0 && len(c.RoutingKeyFile) > 0 { - return errors.New("at most one of routing_key & routing_key_file must be configured") - } - if len(c.ServiceKey) > 0 && len(c.ServiceKeyFile) > 0 { - return errors.New("at most one of service_key & service_key_file must be configured") - } - if c.Details == nil { - c.Details = make(map[string]any) - } - if c.Source == "" { - c.Source = c.Client - } - for k, v := range DefaultPagerdutyDetails { - if _, ok := c.Details[k]; !ok { - c.Details[k] = v - } - } - return nil -} - // SlackAction configures a single Slack action that is sent with each notification. // See https://api.slack.com/docs/message-attachments#action_fields and https://api.slack.com/docs/message-buttons // for more information. diff --git a/config/notifiers_test.go b/config/notifiers_test.go index ca8802f650..17649140bf 100644 --- a/config/notifiers_test.go +++ b/config/notifiers_test.go @@ -101,169 +101,6 @@ to: 'a@' } } -func TestPagerdutyTestRoutingKey(t *testing.T) { - t.Run("error if no routing key or key file", func(t *testing.T) { - in := ` -routing_key: '' -` - var cfg PagerdutyConfig - err := yaml.UnmarshalStrict([]byte(in), &cfg) - - expected := "missing service or routing key in PagerDuty config" - - if err == nil { - t.Fatalf("no error returned, expected:\n%v", expected) - } - if err.Error() != expected { - t.Errorf("\nexpected:\n%v\ngot:\n%v", expected, err.Error()) - } - }) - - t.Run("error if both routing key and key file", func(t *testing.T) { - in := ` -routing_key: 'xyz' -routing_key_file: 'xyz' -` - var cfg PagerdutyConfig - err := yaml.UnmarshalStrict([]byte(in), &cfg) - - expected := "at most one of routing_key & routing_key_file must be configured" - - if err == nil { - t.Fatalf("no error returned, expected:\n%v", expected) - } - if err.Error() != expected { - t.Errorf("\nexpected:\n%v\ngot:\n%v", expected, err.Error()) - } - }) -} - -func TestPagerdutyServiceKey(t *testing.T) { - t.Run("error if no service key or key file", func(t *testing.T) { - in := ` -service_key: '' -` - var cfg PagerdutyConfig - err := yaml.UnmarshalStrict([]byte(in), &cfg) - - expected := "missing service or routing key in PagerDuty config" - - if err == nil { - t.Fatalf("no error returned, expected:\n%v", expected) - } - if err.Error() != expected { - t.Errorf("\nexpected:\n%v\ngot:\n%v", expected, err.Error()) - } - }) - - t.Run("error if both service key and key file", func(t *testing.T) { - in := ` -service_key: 'xyz' -service_key_file: 'xyz' -` - var cfg PagerdutyConfig - err := yaml.UnmarshalStrict([]byte(in), &cfg) - - expected := "at most one of service_key & service_key_file must be configured" - - if err == nil { - t.Fatalf("no error returned, expected:\n%v", expected) - } - if err.Error() != expected { - t.Errorf("\nexpected:\n%v\ngot:\n%v", expected, err.Error()) - } - }) -} - -func TestPagerdutyDetails(t *testing.T) { - tests := []struct { - in string - checkFn func(map[string]any) - }{ - { - in: ` -routing_key: 'xyz' -`, - checkFn: func(d map[string]any) { - if len(d) != 4 { - t.Errorf("expected 4 items, got: %d", len(d)) - } - }, - }, - { - in: ` -routing_key: 'xyz' -details: - key1: val1 -`, - checkFn: func(d map[string]any) { - if len(d) != 5 { - t.Errorf("expected 5 items, got: %d", len(d)) - } - }, - }, - { - in: ` -routing_key: 'xyz' -details: - key1: val1 - key2: val2 - firing: firing -`, - checkFn: func(d map[string]any) { - if len(d) != 6 { - t.Errorf("expected 6 items, got: %d", len(d)) - } - }, - }, - } - for _, tc := range tests { - var cfg PagerdutyConfig - err := yaml.UnmarshalStrict([]byte(tc.in), &cfg) - if err != nil { - t.Errorf("expected no error, got:%v", err) - } - - if tc.checkFn != nil { - tc.checkFn(cfg.Details) - } - } -} - -func TestPagerDutySource(t *testing.T) { - for _, tc := range []struct { - title string - in string - - expectedSource string - }{ - { - title: "check source field is backward compatible", - in: ` -routing_key: 'xyz' -client: 'alert-manager-client' -`, - expectedSource: "alert-manager-client", - }, - { - title: "check source field is set", - in: ` -routing_key: 'xyz' -client: 'alert-manager-client' -source: 'alert-manager-source' -`, - expectedSource: "alert-manager-source", - }, - } { - t.Run(tc.title, func(t *testing.T) { - var cfg PagerdutyConfig - err := yaml.UnmarshalStrict([]byte(tc.in), &cfg) - require.NoError(t, err) - require.Equal(t, tc.expectedSource, cfg.Source) - }) - } -} - func TestVictorOpsConfiguration(t *testing.T) { t.Run("valid configuration", func(t *testing.T) { in := ` diff --git a/notify/pagerduty/config.go b/notify/pagerduty/config.go new file mode 100644 index 0000000000..98ba23b4e5 --- /dev/null +++ b/notify/pagerduty/config.go @@ -0,0 +1,113 @@ +// Copyright The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package pagerduty + +import ( + "errors" + "time" + + commoncfg "github.com/prometheus/common/config" + + amcommoncfg "github.com/prometheus/alertmanager/config/common" +) + +var ( + // DefaultPagerdutyDetails defines the default values for PagerDuty details. + DefaultPagerdutyDetails = map[string]any{ + "firing": `{{ .Alerts.Firing | toJson }}`, + "resolved": `{{ .Alerts.Resolved | toJson }}`, + "num_firing": `{{ .Alerts.Firing | len }}`, + "num_resolved": `{{ .Alerts.Resolved | len }}`, + } + + // DefaultPagerdutyConfig defines default values for PagerDuty configurations. + DefaultPagerdutyConfig = PagerdutyConfig{ + NotifierConfig: amcommoncfg.NotifierConfig{ + VSendResolved: true, + }, + Description: `{{ template "pagerduty.default.description" .}}`, + Client: `{{ template "pagerduty.default.client" . }}`, + ClientURL: `{{ template "pagerduty.default.clientURL" . }}`, + } +) + +// PagerdutyConfig configures notifications via PagerDuty. +type PagerdutyConfig struct { + amcommoncfg.NotifierConfig `yaml:",inline" json:",inline"` + + HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"` + + ServiceKey commoncfg.Secret `yaml:"service_key,omitempty" json:"service_key,omitempty"` + ServiceKeyFile string `yaml:"service_key_file,omitempty" json:"service_key_file,omitempty"` + RoutingKey commoncfg.Secret `yaml:"routing_key,omitempty" json:"routing_key,omitempty"` + RoutingKeyFile string `yaml:"routing_key_file,omitempty" json:"routing_key_file,omitempty"` + URL *amcommoncfg.URL `yaml:"url,omitempty" json:"url,omitempty"` + Client string `yaml:"client,omitempty" json:"client,omitempty"` + ClientURL string `yaml:"client_url,omitempty" json:"client_url,omitempty"` + Description string `yaml:"description,omitempty" json:"description,omitempty"` + Details map[string]any `yaml:"details,omitempty" json:"details,omitempty"` + Images []PagerdutyImage `yaml:"images,omitempty" json:"images,omitempty"` + Links []PagerdutyLink `yaml:"links,omitempty" json:"links,omitempty"` + Source string `yaml:"source,omitempty" json:"source,omitempty"` + Severity string `yaml:"severity,omitempty" json:"severity,omitempty"` + Class string `yaml:"class,omitempty" json:"class,omitempty"` + Component string `yaml:"component,omitempty" json:"component,omitempty"` + Group string `yaml:"group,omitempty" json:"group,omitempty"` + // Timeout is the maximum time allowed to invoke the pagerduty. Setting this to 0 + // does not impose a timeout. + Timeout time.Duration `yaml:"timeout" json:"timeout"` +} + +// PagerdutyLink is a link. +type PagerdutyLink struct { + Href string `yaml:"href,omitempty" json:"href,omitempty"` + Text string `yaml:"text,omitempty" json:"text,omitempty"` +} + +// PagerdutyImage is an image. +type PagerdutyImage struct { + Src string `yaml:"src,omitempty" json:"src,omitempty"` + Alt string `yaml:"alt,omitempty" json:"alt,omitempty"` + Href string `yaml:"href,omitempty" json:"href,omitempty"` +} + +// UnmarshalYAML implements the yaml.Unmarshaler interface. +func (c *PagerdutyConfig) UnmarshalYAML(unmarshal func(any) error) error { + *c = DefaultPagerdutyConfig + type plain PagerdutyConfig + if err := unmarshal((*plain)(c)); err != nil { + return err + } + if c.RoutingKey == "" && c.ServiceKey == "" && c.RoutingKeyFile == "" && c.ServiceKeyFile == "" { + return errors.New("missing service or routing key in PagerDuty config") + } + if len(c.RoutingKey) > 0 && len(c.RoutingKeyFile) > 0 { + return errors.New("at most one of routing_key & routing_key_file must be configured") + } + if len(c.ServiceKey) > 0 && len(c.ServiceKeyFile) > 0 { + return errors.New("at most one of service_key & service_key_file must be configured") + } + if c.Details == nil { + c.Details = make(map[string]any) + } + if c.Source == "" { + c.Source = c.Client + } + for k, v := range DefaultPagerdutyDetails { + if _, ok := c.Details[k]; !ok { + c.Details[k] = v + } + } + return nil +} diff --git a/notify/pagerduty/config_test.go b/notify/pagerduty/config_test.go new file mode 100644 index 0000000000..3d31bd0017 --- /dev/null +++ b/notify/pagerduty/config_test.go @@ -0,0 +1,184 @@ +// Copyright The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package pagerduty + +import ( + "testing" + + "github.com/stretchr/testify/require" + "gopkg.in/yaml.v2" +) + +func TestPagerdutyTestRoutingKey(t *testing.T) { + t.Run("error if no routing key or key file", func(t *testing.T) { + in := ` +routing_key: '' +` + var cfg PagerdutyConfig + err := yaml.UnmarshalStrict([]byte(in), &cfg) + + expected := "missing service or routing key in PagerDuty config" + + if err == nil { + t.Fatalf("no error returned, expected:\n%v", expected) + } + if err.Error() != expected { + t.Errorf("\nexpected:\n%v\ngot:\n%v", expected, err.Error()) + } + }) + + t.Run("error if both routing key and key file", func(t *testing.T) { + in := ` +routing_key: 'xyz' +routing_key_file: 'xyz' +` + var cfg PagerdutyConfig + err := yaml.UnmarshalStrict([]byte(in), &cfg) + + expected := "at most one of routing_key & routing_key_file must be configured" + + if err == nil { + t.Fatalf("no error returned, expected:\n%v", expected) + } + if err.Error() != expected { + t.Errorf("\nexpected:\n%v\ngot:\n%v", expected, err.Error()) + } + }) +} + +func TestPagerdutyServiceKey(t *testing.T) { + t.Run("error if no service key or key file", func(t *testing.T) { + in := ` +service_key: '' +` + var cfg PagerdutyConfig + err := yaml.UnmarshalStrict([]byte(in), &cfg) + + expected := "missing service or routing key in PagerDuty config" + + if err == nil { + t.Fatalf("no error returned, expected:\n%v", expected) + } + if err.Error() != expected { + t.Errorf("\nexpected:\n%v\ngot:\n%v", expected, err.Error()) + } + }) + + t.Run("error if both service key and key file", func(t *testing.T) { + in := ` +service_key: 'xyz' +service_key_file: 'xyz' +` + var cfg PagerdutyConfig + err := yaml.UnmarshalStrict([]byte(in), &cfg) + + expected := "at most one of service_key & service_key_file must be configured" + + if err == nil { + t.Fatalf("no error returned, expected:\n%v", expected) + } + if err.Error() != expected { + t.Errorf("\nexpected:\n%v\ngot:\n%v", expected, err.Error()) + } + }) +} + +func TestPagerdutyDetails(t *testing.T) { + tests := []struct { + in string + checkFn func(map[string]any) + }{ + { + in: ` +routing_key: 'xyz' +`, + checkFn: func(d map[string]any) { + if len(d) != 4 { + t.Errorf("expected 4 items, got: %d", len(d)) + } + }, + }, + { + in: ` +routing_key: 'xyz' +details: + key1: val1 +`, + checkFn: func(d map[string]any) { + if len(d) != 5 { + t.Errorf("expected 5 items, got: %d", len(d)) + } + }, + }, + { + in: ` +routing_key: 'xyz' +details: + key1: val1 + key2: val2 + firing: firing +`, + checkFn: func(d map[string]any) { + if len(d) != 6 { + t.Errorf("expected 6 items, got: %d", len(d)) + } + }, + }, + } + for _, tc := range tests { + var cfg PagerdutyConfig + err := yaml.UnmarshalStrict([]byte(tc.in), &cfg) + if err != nil { + t.Errorf("expected no error, got:%v", err) + } + + if tc.checkFn != nil { + tc.checkFn(cfg.Details) + } + } +} + +func TestPagerDutySource(t *testing.T) { + for _, tc := range []struct { + title string + in string + + expectedSource string + }{ + { + title: "check source field is backward compatible", + in: ` +routing_key: 'xyz' +client: 'alert-manager-client' +`, + expectedSource: "alert-manager-client", + }, + { + title: "check source field is set", + in: ` +routing_key: 'xyz' +client: 'alert-manager-client' +source: 'alert-manager-source' +`, + expectedSource: "alert-manager-source", + }, + } { + t.Run(tc.title, func(t *testing.T) { + var cfg PagerdutyConfig + err := yaml.UnmarshalStrict([]byte(tc.in), &cfg) + require.NoError(t, err) + require.Equal(t, tc.expectedSource, cfg.Source) + }) + } +} diff --git a/notify/pagerduty/pagerduty.go b/notify/pagerduty/pagerduty.go index 8532723992..7f9fa002d5 100644 --- a/notify/pagerduty/pagerduty.go +++ b/notify/pagerduty/pagerduty.go @@ -29,7 +29,6 @@ import ( commoncfg "github.com/prometheus/common/config" "github.com/prometheus/common/model" - "github.com/prometheus/alertmanager/config" "github.com/prometheus/alertmanager/notify" "github.com/prometheus/alertmanager/template" "github.com/prometheus/alertmanager/types" @@ -45,7 +44,7 @@ const ( // Notifier implements a Notifier for PagerDuty notifications. type Notifier struct { - conf *config.PagerdutyConfig + conf *PagerdutyConfig tmpl *template.Template logger *slog.Logger apiV1 string // for tests. @@ -54,7 +53,7 @@ type Notifier struct { } // New returns a new PagerDuty notifier. -func New(c *config.PagerdutyConfig, t *template.Template, l *slog.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) { +func New(c *PagerdutyConfig, t *template.Template, l *slog.Logger, httpOpts ...commoncfg.HTTPClientOption) (*Notifier, error) { client, err := notify.NewClientWithTracing(*c.HTTPConfig, "pagerduty", httpOpts...) if err != nil { return nil, err diff --git a/notify/pagerduty/pagerduty_test.go b/notify/pagerduty/pagerduty_test.go index 302b4c3d3b..52c6f82469 100644 --- a/notify/pagerduty/pagerduty_test.go +++ b/notify/pagerduty/pagerduty_test.go @@ -33,7 +33,6 @@ import ( amcommoncfg "github.com/prometheus/alertmanager/config/common" - "github.com/prometheus/alertmanager/config" "github.com/prometheus/alertmanager/notify" "github.com/prometheus/alertmanager/notify/test" "github.com/prometheus/alertmanager/template" @@ -42,7 +41,7 @@ import ( func TestPagerDutyRetryV1(t *testing.T) { notifier, err := New( - &config.PagerdutyConfig{ + &PagerdutyConfig{ ServiceKey: commoncfg.Secret("01234567890123456789012345678901"), HTTPConfig: &commoncfg.HTTPClientConfig{}, }, @@ -60,7 +59,7 @@ func TestPagerDutyRetryV1(t *testing.T) { func TestPagerDutyRetryV2(t *testing.T) { notifier, err := New( - &config.PagerdutyConfig{ + &PagerdutyConfig{ RoutingKey: commoncfg.Secret("01234567890123456789012345678901"), HTTPConfig: &commoncfg.HTTPClientConfig{}, }, @@ -82,7 +81,7 @@ func TestPagerDutyRedactedURLV1(t *testing.T) { key := "01234567890123456789012345678901" notifier, err := New( - &config.PagerdutyConfig{ + &PagerdutyConfig{ ServiceKey: commoncfg.Secret(key), HTTPConfig: &commoncfg.HTTPClientConfig{}, }, @@ -101,7 +100,7 @@ func TestPagerDutyRedactedURLV2(t *testing.T) { key := "01234567890123456789012345678901" notifier, err := New( - &config.PagerdutyConfig{ + &PagerdutyConfig{ URL: &amcommoncfg.URL{URL: u}, RoutingKey: commoncfg.Secret(key), HTTPConfig: &commoncfg.HTTPClientConfig{}, @@ -125,7 +124,7 @@ func TestPagerDutyV1ServiceKeyFromFile(t *testing.T) { defer fn() notifier, err := New( - &config.PagerdutyConfig{ + &PagerdutyConfig{ ServiceKeyFile: f.Name(), HTTPConfig: &commoncfg.HTTPClientConfig{}, }, @@ -149,7 +148,7 @@ func TestPagerDutyV2RoutingKeyFromFile(t *testing.T) { defer fn() notifier, err := New( - &config.PagerdutyConfig{ + &PagerdutyConfig{ URL: &amcommoncfg.URL{URL: u}, RoutingKeyFile: f.Name(), HTTPConfig: &commoncfg.HTTPClientConfig{}, @@ -176,23 +175,23 @@ func TestPagerDutyTemplating(t *testing.T) { for _, tc := range []struct { title string - cfg *config.PagerdutyConfig + cfg *PagerdutyConfig retry bool errMsg string }{ { title: "full-blown legacy message", - cfg: &config.PagerdutyConfig{ + cfg: &PagerdutyConfig{ RoutingKey: commoncfg.Secret("01234567890123456789012345678901"), - Images: []config.PagerdutyImage{ + Images: []PagerdutyImage{ { Src: "{{ .Status }}", Alt: "{{ .Status }}", Href: "{{ .Status }}", }, }, - Links: []config.PagerdutyLink{ + Links: []PagerdutyLink{ { Href: "{{ .Status }}", Text: "{{ .Status }}", @@ -208,16 +207,16 @@ func TestPagerDutyTemplating(t *testing.T) { }, { title: "full-blown legacy message", - cfg: &config.PagerdutyConfig{ + cfg: &PagerdutyConfig{ RoutingKey: commoncfg.Secret("01234567890123456789012345678901"), - Images: []config.PagerdutyImage{ + Images: []PagerdutyImage{ { Src: "{{ .Status }}", Alt: "{{ .Status }}", Href: "{{ .Status }}", }, }, - Links: []config.PagerdutyLink{ + Links: []PagerdutyLink{ { Href: "{{ .Status }}", Text: "{{ .Status }}", @@ -233,7 +232,7 @@ func TestPagerDutyTemplating(t *testing.T) { }, { title: "nested details", - cfg: &config.PagerdutyConfig{ + cfg: &PagerdutyConfig{ RoutingKey: commoncfg.Secret("01234567890123456789012345678901"), Details: map[string]any{ "a": map[string]any{ @@ -251,7 +250,7 @@ func TestPagerDutyTemplating(t *testing.T) { }, { title: "nested details with template error", - cfg: &config.PagerdutyConfig{ + cfg: &PagerdutyConfig{ RoutingKey: commoncfg.Secret("01234567890123456789012345678901"), Details: map[string]any{ "a": map[string]any{ @@ -267,7 +266,7 @@ func TestPagerDutyTemplating(t *testing.T) { }, { title: "details with templating errors", - cfg: &config.PagerdutyConfig{ + cfg: &PagerdutyConfig{ RoutingKey: commoncfg.Secret("01234567890123456789012345678901"), Details: map[string]any{ "firing": `{{ .Alerts.Firing | toJson`, @@ -280,7 +279,7 @@ func TestPagerDutyTemplating(t *testing.T) { }, { title: "v2 message with templating errors", - cfg: &config.PagerdutyConfig{ + cfg: &PagerdutyConfig{ RoutingKey: commoncfg.Secret("01234567890123456789012345678901"), Severity: "{{ ", }, @@ -288,7 +287,7 @@ func TestPagerDutyTemplating(t *testing.T) { }, { title: "v1 message with templating errors", - cfg: &config.PagerdutyConfig{ + cfg: &PagerdutyConfig{ ServiceKey: commoncfg.Secret("01234567890123456789012345678901"), Client: "{{ ", }, @@ -296,14 +295,14 @@ func TestPagerDutyTemplating(t *testing.T) { }, { title: "routing key cannot be empty", - cfg: &config.PagerdutyConfig{ + cfg: &PagerdutyConfig{ RoutingKey: commoncfg.Secret(`{{ "" }}`), }, errMsg: "routing key cannot be empty", }, { title: "service_key cannot be empty", - cfg: &config.PagerdutyConfig{ + cfg: &PagerdutyConfig{ ServiceKey: commoncfg.Secret(`{{ "" }}`), }, errMsg: "service key cannot be empty", @@ -398,7 +397,7 @@ func TestEventSizeEnforcement(t *testing.T) { } notifierV1, err := New( - &config.PagerdutyConfig{ + &PagerdutyConfig{ ServiceKey: commoncfg.Secret("01234567890123456789012345678901"), HTTPConfig: &commoncfg.HTTPClientConfig{}, }, @@ -421,7 +420,7 @@ func TestEventSizeEnforcement(t *testing.T) { } notifierV2, err := New( - &config.PagerdutyConfig{ + &PagerdutyConfig{ RoutingKey: commoncfg.Secret("01234567890123456789012345678901"), HTTPConfig: &commoncfg.HTTPClientConfig{}, }, @@ -445,7 +444,7 @@ func TestPagerDutyEmptySrcHref(t *testing.T) { Links []pagerDutyLink } - images := []config.PagerdutyImage{ + images := []PagerdutyImage{ { Src: "", Alt: "Empty src", @@ -463,7 +462,7 @@ func TestPagerDutyEmptySrcHref(t *testing.T) { }, } - links := []config.PagerdutyLink{ + links := []PagerdutyLink{ { Href: "", Text: "Empty href", @@ -479,11 +478,7 @@ func TestPagerDutyEmptySrcHref(t *testing.T) { if image.Src == "" { continue } - expectedImages = append(expectedImages, pagerDutyImage{ - Src: image.Src, - Alt: image.Alt, - Href: image.Href, - }) + expectedImages = append(expectedImages, pagerDutyImage(image)) } expectedLinks := make([]pagerDutyLink, 0, len(links)) @@ -533,7 +528,7 @@ func TestPagerDutyEmptySrcHref(t *testing.T) { url, err := url.Parse(server.URL) require.NoError(t, err) - pagerDutyConfig := config.PagerdutyConfig{ + pagerDutyConfig := PagerdutyConfig{ HTTPConfig: &commoncfg.HTTPClientConfig{}, RoutingKey: commoncfg.Secret("01234567890123456789012345678901"), URL: &amcommoncfg.URL{URL: url}, @@ -601,7 +596,7 @@ func TestPagerDutyTimeout(t *testing.T) { u, err := url.Parse(srv.URL) require.NoError(t, err) - cfg := config.PagerdutyConfig{ + cfg := PagerdutyConfig{ HTTPConfig: &commoncfg.HTTPClientConfig{}, RoutingKey: commoncfg.Secret("01234567890123456789012345678901"), URL: &amcommoncfg.URL{URL: u}, @@ -872,7 +867,7 @@ func TestRenderDetails(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { n := &Notifier{ - conf: &config.PagerdutyConfig{ + conf: &PagerdutyConfig{ Details: tt.args.details, }, tmpl: test.CreateTmpl(t), From 846bdbffb8d716d7014f2aed7c788b2e1bbdc8cb Mon Sep 17 00:00:00 2001 From: Nutmos Date: Sun, 26 Jul 2026 23:23:18 +0800 Subject: [PATCH 093/120] doc: add global config rocketchat description (#5181) Signed-off-by: Nattapong Ekudomsuk --- docs/configuration.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/configuration.md b/docs/configuration.md index 8b7f510158..be9a73f841 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -118,9 +118,13 @@ global: [ opsgenie_api_key_file: ] [ opsgenie_api_url: | default = "https://api.opsgenie.com/" ] [ rocketchat_api_url: | default = "https://open.rocket.chat/" ] + # The default Rocketchat sender token. It is mutually exclusive with `rocketchat_token_file`. [ rocketchat_token: ] + # Read the default Rocketchat sender token from a file. It is mutually exclusive with `rocketchat_token`. [ rocketchat_token_file: ] + # The default Rocketchat sender token ID. It is mutually exclusive with `rocketchat_token_id_file`. [ rocketchat_token_id: ] + # Read the default Rocketchat sender token ID from a file. It is mutually exclusive with `rocketchat_token_id`. [ rocketchat_token_id_file: ] [ wechat_api_url: | default = "https://qyapi.weixin.qq.com/cgi-bin/" ] [ wechat_api_secret: ] From 2ad68f319c8549bec77f2da3be1a244ad32da1d5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Aug 2026 08:06:30 +0200 Subject: [PATCH 094/120] build(deps): bump google.golang.org/grpc from 1.82.0 to 1.82.1 (#5433) Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.82.0 to 1.82.1.
Release notes

Sourced from google.golang.org/grpc's releases.

Release 1.82.1

Security

  • server: Stop reading from the connection when flooded by HTTP/2 frames. The default value for this limit is 100 frames, excluding DATA and HEADERS, and may be changed by setting environment variable GRPC_GO_EXPERIMENTAL_CONTROL_BUFFER_THROTTLE_LIMIT.
  • xds/rbac: Support Metadata and RequestedServerName permissions matcher fields. If present in a DENY rule, previously these would be ignored and fail-open.
  • xds/rbac: Fix panic when parsing unsupported fields in NotRule/NotId permissions.
  • xds/rbac: Support the deprecated source_ip principal identifier by treating it as equivalent to direct_remote_ip.
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=google.golang.org/grpc&package-manager=go_modules&previous-version=1.82.0&new-version=1.82.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index b2936a4246..66f7610b9e 100644 --- a/go.mod +++ b/go.mod @@ -57,7 +57,7 @@ require ( golang.org/x/mod v0.37.0 golang.org/x/net v0.56.0 golang.org/x/text v0.38.0 - google.golang.org/grpc v1.82.0 + google.golang.org/grpc v1.82.1 google.golang.org/protobuf v1.36.11 gopkg.in/telebot.v3 v3.3.8 gopkg.in/yaml.v2 v2.4.0 diff --git a/go.sum b/go.sum index 968d7057d6..32eea8614c 100644 --- a/go.sum +++ b/go.sum @@ -1070,8 +1070,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.82.0 h1:vguDnZUPjE26w09A63VoxZPnvPjB5Riyc0mkXPFmAIU= -google.golang.org/grpc v1.82.0/go.mod h1:yzTZ1TB1Z3SG+LIYaI+WiE8D5+PZ3ArnrSp8zF3+/ZA= +google.golang.org/grpc v1.82.1 h1:NnAxzGRA0677vCa4BUkOAnO5+FfQqVl9iUXeD0IqcGE= +google.golang.org/grpc v1.82.1/go.mod h1:yzTZ1TB1Z3SG+LIYaI+WiE8D5+PZ3ArnrSp8zF3+/ZA= 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= From 521adb912bb21bba2848cc78981d6a3cf0366c9e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Aug 2026 08:14:50 +0200 Subject: [PATCH 095/120] build(deps): bump the golang-org-x group across 1 directory with 3 updates (#5431) Bumps the golang-org-x group with 2 updates in the / directory: [golang.org/x/mod](https://github.com/golang/mod) and [golang.org/x/net](https://github.com/golang/net). Updates `golang.org/x/mod` from 0.37.0 to 0.38.0
Commits
  • 792ac16 go.mod: update golang.org/x dependencies
  • fe2ec04 all: fix some comments to improve readability
  • See full diff in compare view

Updates `golang.org/x/net` from 0.56.0 to 0.57.0
Commits
  • b8f09f6 go.mod: update golang.org/x dependencies
  • f05f21b idna: reject all-ASCII xn-- labels on all Go versions
  • 0f748cf internal/http3: clean up stream I/O methods usages in tests
  • 0bb961e internal/http3: add net/http.ResponseController support
  • 0ca694d webdav: document Dir's lack of defense against filesystem modification
  • bd5f1dc http2: initialize Transport on NewClientConn
  • 488ff63 bpf: add security considerations to package docs
  • 93d1f25 xsrftoken: avoid token collisions
  • 5a3baee internal/http3: prevent panic in QPACK decoder due to overflow
  • See full diff in compare view

Updates `golang.org/x/text` from 0.38.0 to 0.40.0
Commits
  • 724af9c go.mod: update golang.org/x dependencies
  • bf5b9d6 internal/export/idna: always treat Punycode encoding pure ASCII as an error
  • b326f3d go.mod: update golang.org/x dependencies
  • 5ae8e57 unicode/norm: avoid infinite loop on invalid input
  • 0dc94a2 all: fix some comments
  • See full diff in compare view

Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 14 +++++++------- go.sum | 28 ++++++++++++++-------------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/go.mod b/go.mod index 66f7610b9e..7811e7ac50 100644 --- a/go.mod +++ b/go.mod @@ -54,9 +54,9 @@ require ( go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.44.0 go.opentelemetry.io/otel/sdk v1.44.0 go.opentelemetry.io/otel/trace v1.44.0 - golang.org/x/mod v0.37.0 - golang.org/x/net v0.56.0 - golang.org/x/text v0.38.0 + golang.org/x/mod v0.38.0 + golang.org/x/net v0.57.0 + golang.org/x/text v0.40.0 google.golang.org/grpc v1.82.1 google.golang.org/protobuf v1.36.11 gopkg.in/telebot.v3 v3.3.8 @@ -124,12 +124,12 @@ require ( go.opentelemetry.io/proto/otlp v1.10.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/crypto v0.53.0 // indirect + golang.org/x/crypto v0.54.0 // indirect golang.org/x/oauth2 v0.36.0 // indirect - golang.org/x/sync v0.21.0 // indirect - golang.org/x/sys v0.46.0 // indirect + golang.org/x/sync v0.22.0 // indirect + golang.org/x/sys v0.47.0 // indirect golang.org/x/time v0.15.0 // indirect - golang.org/x/tools v0.45.0 // indirect + golang.org/x/tools v0.47.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 32eea8614c..10f737f785 100644 --- a/go.sum +++ b/go.sum @@ -625,8 +625,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto= -golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio= +golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw= +golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk= 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= @@ -662,8 +662,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ= -golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0= +golang.org/x/mod v0.38.0 h1:MECBjubtXD7yj4HrhIUcywNaGeNVUdfVnxmPajOk4yk= +golang.org/x/mod v0.38.0/go.mod h1:V6Xz0pq8TQ3dGqVQ1FVHuelZpAL0uNhSkk9ogYP3c40= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -711,8 +711,8 @@ golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= 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.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o= -golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec= +golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE= +golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU= 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= @@ -747,8 +747,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220513210516-0976fa681c29/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM= -golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= +golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= 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= @@ -827,8 +827,8 @@ golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw= -golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= 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/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -840,8 +840,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 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.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE= -golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4= +golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs= +golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY= 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= @@ -902,8 +902,8 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8= -golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0= +golang.org/x/tools v0.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q= +golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From 4c8ca7d88502a19a6acb39aa1fd4a2ef6f11bb49 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Aug 2026 15:10:22 +0000 Subject: [PATCH 096/120] build(deps): bump github.com/hashicorp/memberlist from 0.5.4 to 0.6.0 Bumps [github.com/hashicorp/memberlist](https://github.com/hashicorp/memberlist) from 0.5.4 to 0.6.0. - [Release notes](https://github.com/hashicorp/memberlist/releases) - [Commits](https://github.com/hashicorp/memberlist/compare/v0.5.4...v0.6.0) --- updated-dependencies: - dependency-name: github.com/hashicorp/memberlist dependency-version: 0.6.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 7811e7ac50..16d0e07195 100644 --- a/go.mod +++ b/go.mod @@ -30,7 +30,7 @@ require ( github.com/google/uuid v1.6.0 github.com/hashicorp/go-sockaddr v1.0.7 github.com/hashicorp/golang-lru/v2 v2.0.7 - github.com/hashicorp/memberlist v0.5.4 + github.com/hashicorp/memberlist v0.6.0 github.com/jessevdk/go-flags v1.6.1 github.com/mdlayher/vsock v1.3.0 github.com/oklog/run v1.2.0 @@ -100,7 +100,7 @@ require ( github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect - github.com/hashicorp/go-metrics v0.5.4 // indirect + github.com/hashicorp/go-metrics v0.6.0 // indirect github.com/hashicorp/go-msgpack/v2 v2.1.5 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect @@ -109,7 +109,7 @@ require ( github.com/klauspost/compress v1.18.6 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/mdlayher/socket v0.6.0 // indirect - github.com/miekg/dns v1.1.68 // indirect + github.com/miekg/dns v1.1.72 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect diff --git a/go.sum b/go.sum index 10f737f785..bed791d932 100644 --- a/go.sum +++ b/go.sum @@ -352,8 +352,8 @@ github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39E github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-metrics v0.5.4 h1:8mmPiIJkTPPEbAiV97IxdAGNdRdaWwVap1BU6elejKY= -github.com/hashicorp/go-metrics v0.5.4/go.mod h1:CG5yz4NZ/AI/aQt9Ucm/vdBnbh7fvmv4lxZ350i+QQI= +github.com/hashicorp/go-metrics v0.6.0 h1:+kjWqHRH2HxAocneVfB/BI6EeWUUHyPhyQZozMT8Ed4= +github.com/hashicorp/go-metrics v0.6.0/go.mod h1:0B52B5pZ7+qm5Zhzs8Fygr87isvmUgr0Zv9rmJ9qsnQ= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-msgpack/v2 v2.1.5 h1:Ue879bPnutj/hXfmUk6s/jtIK90XxgiUIcXRl656T44= github.com/hashicorp/go-msgpack/v2 v2.1.5/go.mod h1:bjCsRXpZ7NsJdk45PoCQnzRGDaK8TKm5ZnDI/9y3J4M= @@ -380,8 +380,8 @@ github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= -github.com/hashicorp/memberlist v0.5.4 h1:40YY+3qq2tAUhZIMEK8kqusKZBBjdwJ3NUjvYkcxh74= -github.com/hashicorp/memberlist v0.5.4/go.mod h1:OgN6xiIo6RlHUWk+ALjP9e32xWCoQrsOCmHrWCm2MWA= +github.com/hashicorp/memberlist v0.6.0 h1:hhVDLQUzWkLaitLLSrxLLqSD2l2+qiOz1DMr5zb9EQQ= +github.com/hashicorp/memberlist v0.6.0/go.mod h1:a2lqh8KICpm8JibWOmuld7DaA+9QU1YcUtTTTMAtt/M= github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= github.com/hashicorp/serf v0.9.7/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= @@ -440,8 +440,8 @@ github.com/mdlayher/vsock v1.3.0 h1:bqQfZ1OznI03y6YiXp2sze05RVdzLn/zsfjnjd4+ivI= github.com/mdlayher/vsock v1.3.0/go.mod h1:WsuksavOvwCnV5UqGHUkvAvCy+Dqy81y4goKQTzxxNY= 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= -github.com/miekg/dns v1.1.68 h1:jsSRkNozw7G/mnmXULynzMNIsgY2dHC8LO6U6Ij2JEA= -github.com/miekg/dns v1.1.68/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps= +github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI= +github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs= github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= From 20a8bd5929d078633e66e02888170aca63939d8e Mon Sep 17 00:00:00 2001 From: Ethan Hunter Date: Tue, 4 Aug 2026 05:43:58 -0600 Subject: [PATCH 097/120] update release schedule (#5416) Based on discussion in the working group meeting: Update cadence guidelines to 8 weeks (which is closer to what we've been doing) and schedule `0.34` (@SoloJacobs as the shepherd). Signed-off-by: Ethan Hunter --- RELEASE.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/RELEASE.md b/RELEASE.md index d74e260cbe..81468e5eaf 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -3,7 +3,7 @@ This page describes the release process and the currently planned schedule for u ## Release Schedule -Release cadence of first pre-releases being cut is 12 weeks. +Release cadence of first pre-releases being cut is approximately 8 weeks. | release series | date (year-month-day) | release shepherd | |----------------|-----------------------|-------------------------------------------| @@ -14,7 +14,8 @@ Release cadence of first pre-releases being cut is 12 weeks. | v0.30 | 2025-12-12 | Solomon Jacobs (Github: @SoloJacobs) | | v0.31 | 2026-01-31 | Solomon Jacobs (Github: @SoloJacobs) | | v0.32 | 2026-04-08 | Solomon Jacobs (Github: @SoloJacobs) | -| v0.33 | 2026-06-08 | **volunteer welcome** | +| v0.33 | 2026-06-08 | Ethan Hunter (Github: @Spaceman1701) | +| v0.34 | 2026-08-12 | Solomon Jacobs (Github: @SoloJacobs) | If you are interested in volunteering please create a pull request against the [prometheus/alertmanager](https://github.com/prometheus/alertmanager) repository and propose yourself for the release of your choice. From 487a1f8118c0e40c27bda7ca1efd44b3d989371d Mon Sep 17 00:00:00 2001 From: PrometheusBot Date: Tue, 4 Aug 2026 19:30:25 +0200 Subject: [PATCH 098/120] Synchronize common files from prometheus/prometheus (#5438) Propagating changes from prometheus/prometheus default branch. *Source can be found [here](https://github.com/prometheus/prometheus/blob/main/scripts/sync_repo_files.sh).* To check out this branch locally and push changes back: ``` git remote add prombot https://github.com/prombot/prometheus_alertmanager.git git fetch prombot repo_sync git checkout -b repo_sync prombot/repo_sync ``` Signed-off-by: prombot --- Makefile.common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.common b/Makefile.common index a7c5f553e1..5cfd6b8bff 100644 --- a/Makefile.common +++ b/Makefile.common @@ -61,7 +61,7 @@ PROMU_URL := https://github.com/prometheus/promu/releases/download/v$(PROMU_ SKIP_GOLANGCI_LINT := GOLANGCI_LINT := GOLANGCI_LINT_OPTS ?= -GOLANGCI_LINT_VERSION ?= v2.11.4 +GOLANGCI_LINT_VERSION ?= v2.12.2 GOLANGCI_FMT_OPTS ?= # golangci-lint only supports linux, darwin and windows platforms on i386/amd64/arm64. # windows isn't included here because of the path separator being different. From 75f3d55a578e4051fbcaaca5f8b12184d9c6c58e Mon Sep 17 00:00:00 2001 From: mihir-dixit2k27 <143348248+mihir-dixit2k27@users.noreply.github.com> Date: Tue, 4 Aug 2026 23:08:20 +0530 Subject: [PATCH 099/120] api: add active/expired/pending filter params to GET /silences (#5406) Fixes #5404 `GET /api/v2/silences` returns all silences with no way to filter by state. This means callers who only care about active silences have to fetch everything and filter client-side, which is the same problem that was solved for alerts with the existing `active`, `silenced`, `inhibited`, and `unprocessed` params on `GET /alerts`. This PR adds three optional boolean query params that are `active`, `expired`, `pending` to `GET /api/v2/silences`. Omitting a param includes that state (default behaviour is unchanged). Passing `false` excludes it. The filter is applied server-side after the existing label-matcher filter, before sorting. [FEATURE] API: Add active, expired, and pending boolean filter parameters to GET /api/v2/silences to allow filtering silences by state server-side. --------- Signed-off-by: Mihir Dixit --- api/v2/api.go | 19 ++- api/v2/api_test.go | 95 ++++++++++++- .../client/silence/get_silences_parameters.go | 127 +++++++++++++++++- api/v2/openapi.yaml | 15 +++ api/v2/restapi/embedded_spec.go | 42 ++++++ .../silence/get_silences_parameters.go | 126 ++++++++++++++++- .../silence/get_silences_urlbuilder.go | 29 +++- 7 files changed, 441 insertions(+), 12 deletions(-) diff --git a/api/v2/api.go b/api/v2/api.go index d4c95cf336..0207607b95 100644 --- a/api/v2/api.go +++ b/api/v2/api.go @@ -683,7 +683,20 @@ func (api *API) getSilencesHandler(params silence_ops.GetSilencesParams) middlew return silence_ops.NewGetSilencesBadRequest().WithPayload(err.Error()) } - psils, _, err := api.silences.Query(ctx) + // Build the state filter. Params are always non-nil (defaults to true) so + // we only add a state to the query when the caller has not excluded it. + var states []silence.SilenceState + if *params.Active { + states = append(states, silence.SilenceStateActive) + } + if *params.Expired { + states = append(states, silence.SilenceStateExpired) + } + if *params.Pending { + states = append(states, silence.SilenceStatePending) + } + + psils, _, err := api.silences.Query(ctx, silence.QState(states...)) if err != nil { logger.Error("Failed to get silences", "err", err) return silence_ops.NewGetSilencesInternalServerError().WithPayload(err.Error()) @@ -694,12 +707,12 @@ func (api *API) getSilencesHandler(params silence_ops.GetSilencesParams) middlew if !CheckSilenceMatchesFilterLabels(ps, matchers) { continue } - silence, err := GettableSilenceFromProto(ps) + sil, err := GettableSilenceFromProto(ps) if err != nil { logger.Error("Failed to unmarshal silence from proto", "err", err) return silence_ops.NewGetSilencesInternalServerError().WithPayload(err.Error()) } - sils = append(sils, &silence) + sils = append(sils, &sil) } SortSilences(sils) diff --git a/api/v2/api_test.go b/api/v2/api_test.go index cf772cb299..f2233efc50 100644 --- a/api/v2/api_test.go +++ b/api/v2/api_test.go @@ -160,6 +160,93 @@ func TestGetSilencesHandler(t *testing.T) { } } +func boolPtr(b bool) *bool { return &b } + +func TestGetSilencesHandlerStateFilter(t *testing.T) { + now := timestamppb.Now() + silences := newSilences(t) + m := &silencepb.Matcher{Type: silencepb.Matcher_EQUAL, Name: "a", Pattern: "b"} + + // active silence: starts in the past, ends in the future. + activeSil := &silencepb.Silence{ + MatcherSets: []*silencepb.MatcherSet{{Matchers: []*silencepb.Matcher{m}}}, + StartsAt: timestamppb.New(now.AsTime().Add(-time.Hour)), + EndsAt: timestamppb.New(now.AsTime().Add(time.Hour)), + UpdatedAt: now, + } + require.NoError(t, silences.Set(t.Context(), activeSil)) + + // pending silence: starts in the future. + pendingSil := &silencepb.Silence{ + MatcherSets: []*silencepb.MatcherSet{{Matchers: []*silencepb.Matcher{m}}}, + StartsAt: timestamppb.New(now.AsTime().Add(time.Hour)), + EndsAt: timestamppb.New(now.AsTime().Add(2 * time.Hour)), + UpdatedAt: now, + } + require.NoError(t, silences.Set(t.Context(), pendingSil)) + + // expired silence: explicitly expired via Expire(). + expiredSil := &silencepb.Silence{ + MatcherSets: []*silencepb.MatcherSet{{Matchers: []*silencepb.Matcher{m}}}, + StartsAt: timestamppb.New(now.AsTime().Add(-time.Hour)), + EndsAt: timestamppb.New(now.AsTime().Add(time.Hour)), + UpdatedAt: now, + } + require.NoError(t, silences.Set(t.Context(), expiredSil)) + require.NoError(t, silences.Expire(t.Context(), expiredSil.Id)) + + api := API{ + uptime: time.Now(), + silences: silences, + logger: promslog.NewNopLogger(), + } + + callHandler := func(active, expired, pending *bool) []*open_api_models.GettableSilence { + r, err := http.NewRequest("GET", "/api/v2/silences", nil) + require.NoError(t, err) + w := httptest.NewRecorder() + p := runtime.TextProducer() + responder := api.getSilencesHandler(silence_ops.GetSilencesParams{ + HTTPRequest: r, + Active: active, + Expired: expired, + Pending: pending, + }) + responder.WriteResponse(w, p) + require.Equal(t, http.StatusOK, w.Code) + var resp []*open_api_models.GettableSilence + require.NoError(t, json.NewDecoder(w.Body).Decode(&resp)) + return resp + } + + stateSet := func(sils []*open_api_models.GettableSilence) map[string]bool { + states := make(map[string]bool, len(sils)) + for _, s := range sils { + states[string(*s.Status.State)] = true + } + return states + } + + // No filter params (all true) - all three states returned. + require.Len(t, callHandler(boolPtr(true), boolPtr(true), boolPtr(true)), 3) + + // active=false - active silences excluded. + got := stateSet(callHandler(boolPtr(false), boolPtr(true), boolPtr(true))) + require.False(t, got["active"]) + require.True(t, got["expired"] || got["pending"]) + + // expired=false - expired silences excluded. + got = stateSet(callHandler(boolPtr(true), boolPtr(false), boolPtr(true))) + require.False(t, got["expired"]) + + // pending=false - pending silences excluded. + got = stateSet(callHandler(boolPtr(true), boolPtr(true), boolPtr(false))) + require.False(t, got["pending"]) + + // all false - empty result. + require.Empty(t, callHandler(boolPtr(false), boolPtr(false), boolPtr(false))) +} + func TestDeleteSilenceHandler(t *testing.T) { now := timestamppb.Now() silences := newSilences(t) @@ -364,11 +451,11 @@ func getSilences( r, err := http.NewRequest("GET", "/api/v2/silences", nil) require.NoError(t, err) + params := silence_ops.NewGetSilencesParams() + params.HTTPRequest = r + p := runtime.TextProducer() - responder := handlerFunc(silence_ops.GetSilencesParams{ - HTTPRequest: r, - Filter: nil, - }) + responder := handlerFunc(params) responder.WriteResponse(w, p) } diff --git a/api/v2/client/silence/get_silences_parameters.go b/api/v2/client/silence/get_silences_parameters.go index 8e02101200..166edf521e 100644 --- a/api/v2/client/silence/get_silences_parameters.go +++ b/api/v2/client/silence/get_silences_parameters.go @@ -76,12 +76,36 @@ GetSilencesParams contains all the parameters to send to the API endpoint */ type GetSilencesParams struct { + /* Active. + + Include active silences in results. If false, excludes active silences. + + Default: true + */ + Active *bool + + /* Expired. + + Include expired silences in results. If false, excludes expired silences. + + Default: true + */ + Expired *bool + /* Filter. A matcher expression to filter silences. For example `alertname="MyAlert"`. It can be repeated to apply multiple matchers. */ Filter []string + /* Pending. + + Include pending silences in results. If false, excludes pending silences. + + Default: true + */ + Pending *bool + timeout time.Duration Context context.Context HTTPClient *http.Client @@ -99,7 +123,24 @@ func (o *GetSilencesParams) WithDefaults() *GetSilencesParams { // // All values with no default are reset to their zero value. func (o *GetSilencesParams) SetDefaults() { - // no default values defined for this parameter + var ( + activeDefault = bool(true) + + expiredDefault = bool(true) + + pendingDefault = bool(true) + ) + + val := GetSilencesParams{ + Active: &activeDefault, + Expired: &expiredDefault, + Pending: &pendingDefault, + } + + val.timeout = o.timeout + val.Context = o.Context + val.HTTPClient = o.HTTPClient + *o = val } // WithTimeout adds the timeout to the get silences params @@ -135,6 +176,28 @@ func (o *GetSilencesParams) SetHTTPClient(client *http.Client) { o.HTTPClient = client } +// WithActive adds the active to the get silences params +func (o *GetSilencesParams) WithActive(active *bool) *GetSilencesParams { + o.SetActive(active) + return o +} + +// SetActive adds the active to the get silences params +func (o *GetSilencesParams) SetActive(active *bool) { + o.Active = active +} + +// WithExpired adds the expired to the get silences params +func (o *GetSilencesParams) WithExpired(expired *bool) *GetSilencesParams { + o.SetExpired(expired) + return o +} + +// SetExpired adds the expired to the get silences params +func (o *GetSilencesParams) SetExpired(expired *bool) { + o.Expired = expired +} + // WithFilter adds the filter to the get silences params func (o *GetSilencesParams) WithFilter(filter []string) *GetSilencesParams { o.SetFilter(filter) @@ -146,6 +209,17 @@ func (o *GetSilencesParams) SetFilter(filter []string) { o.Filter = filter } +// WithPending adds the pending to the get silences params +func (o *GetSilencesParams) WithPending(pending *bool) *GetSilencesParams { + o.SetPending(pending) + return o +} + +// SetPending adds the pending to the get silences params +func (o *GetSilencesParams) SetPending(pending *bool) { + o.Pending = pending +} + // WriteToRequest writes these params to a swagger request func (o *GetSilencesParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { @@ -154,6 +228,40 @@ func (o *GetSilencesParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.R } var res []error + if o.Active != nil { + + // query param active + var qrActive bool + + if o.Active != nil { + qrActive = *o.Active + } + qActive := swag.FormatBool(qrActive) + if qActive != "" { + + if err := r.SetQueryParam("active", qActive); err != nil { + return err + } + } + } + + if o.Expired != nil { + + // query param expired + var qrExpired bool + + if o.Expired != nil { + qrExpired = *o.Expired + } + qExpired := swag.FormatBool(qrExpired) + if qExpired != "" { + + if err := r.SetQueryParam("expired", qExpired); err != nil { + return err + } + } + } + if o.Filter != nil { // binding items for filter @@ -165,6 +273,23 @@ func (o *GetSilencesParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.R } } + if o.Pending != nil { + + // query param pending + var qrPending bool + + if o.Pending != nil { + qrPending = *o.Pending + } + qPending := swag.FormatBool(qrPending) + if qPending != "" { + + if err := r.SetQueryParam("pending", qPending); err != nil { + return err + } + } + } + if len(res) > 0 { return errors.CompositeValidationError(res...) } diff --git a/api/v2/openapi.yaml b/api/v2/openapi.yaml index 113811bbce..0b6e864446 100644 --- a/api/v2/openapi.yaml +++ b/api/v2/openapi.yaml @@ -70,6 +70,21 @@ paths: collectionFormat: multi items: type: string + - in: query + name: active + type: boolean + description: Include active silences in results. If false, excludes active silences. + default: true + - in: query + name: expired + type: boolean + description: Include expired silences in results. If false, excludes expired silences. + default: true + - in: query + name: pending + type: boolean + description: Include pending silences in results. If false, excludes pending silences. + default: true post: tags: - silence diff --git a/api/v2/restapi/embedded_spec.go b/api/v2/restapi/embedded_spec.go index b028849f6f..36725d260d 100644 --- a/api/v2/restapi/embedded_spec.go +++ b/api/v2/restapi/embedded_spec.go @@ -329,6 +329,27 @@ func init() { "description": "A matcher expression to filter silences. For example ` + "`" + `alertname=\"MyAlert\"` + "`" + `. It can be repeated to apply multiple matchers.", "name": "filter", "in": "query" + }, + { + "type": "boolean", + "default": true, + "description": "Include active silences in results. If false, excludes active silences.", + "name": "active", + "in": "query" + }, + { + "type": "boolean", + "default": true, + "description": "Include expired silences in results. If false, excludes expired silences.", + "name": "expired", + "in": "query" + }, + { + "type": "boolean", + "default": true, + "description": "Include pending silences in results. If false, excludes pending silences.", + "name": "pending", + "in": "query" } ], "responses": { @@ -1229,6 +1250,27 @@ func init() { "description": "A matcher expression to filter silences. For example ` + "`" + `alertname=\"MyAlert\"` + "`" + `. It can be repeated to apply multiple matchers.", "name": "filter", "in": "query" + }, + { + "type": "boolean", + "default": true, + "description": "Include active silences in results. If false, excludes active silences.", + "name": "active", + "in": "query" + }, + { + "type": "boolean", + "default": true, + "description": "Include expired silences in results. If false, excludes expired silences.", + "name": "expired", + "in": "query" + }, + { + "type": "boolean", + "default": true, + "description": "Include pending silences in results. If false, excludes pending silences.", + "name": "pending", + "in": "query" } ], "responses": { diff --git a/api/v2/restapi/operations/silence/get_silences_parameters.go b/api/v2/restapi/operations/silence/get_silences_parameters.go index 47374dd9a9..afb28f657c 100644 --- a/api/v2/restapi/operations/silence/get_silences_parameters.go +++ b/api/v2/restapi/operations/silence/get_silences_parameters.go @@ -26,14 +26,29 @@ import ( "github.com/go-openapi/runtime" "github.com/go-openapi/runtime/middleware" "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" ) // NewGetSilencesParams creates a new GetSilencesParams object -// -// There are no default values defined in the spec. +// with the default values initialized. func NewGetSilencesParams() GetSilencesParams { - return GetSilencesParams{} + var ( + // initialize parameters with default values + + activeDefault = bool(true) + expiredDefault = bool(true) + + pendingDefault = bool(true) + ) + + return GetSilencesParams{ + Active: &activeDefault, + + Expired: &expiredDefault, + + Pending: &pendingDefault, + } } // GetSilencesParams contains all the bound params for the get silences operation @@ -44,11 +59,29 @@ type GetSilencesParams struct { // HTTP Request Object HTTPRequest *http.Request `json:"-"` + /*Include active silences in results. If false, excludes active silences. + In: query + Default: true + */ + Active *bool + + /*Include expired silences in results. If false, excludes expired silences. + In: query + Default: true + */ + Expired *bool + /*A matcher expression to filter silences. For example `alertname="MyAlert"`. It can be repeated to apply multiple matchers. In: query Collection Format: multi */ Filter []string + + /*Include pending silences in results. If false, excludes pending silences. + In: query + Default: true + */ + Pending *bool } // BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface @@ -61,16 +94,79 @@ func (o *GetSilencesParams) BindRequest(r *http.Request, route *middleware.Match o.HTTPRequest = r qs := runtime.Values(r.URL.Query()) + qActive, qhkActive, _ := qs.GetOK("active") + if err := o.bindActive(qActive, qhkActive, route.Formats); err != nil { + res = append(res, err) + } + + qExpired, qhkExpired, _ := qs.GetOK("expired") + if err := o.bindExpired(qExpired, qhkExpired, route.Formats); err != nil { + res = append(res, err) + } + qFilter, qhkFilter, _ := qs.GetOK("filter") if err := o.bindFilter(qFilter, qhkFilter, route.Formats); err != nil { res = append(res, err) } + + qPending, qhkPending, _ := qs.GetOK("pending") + if err := o.bindPending(qPending, qhkPending, route.Formats); err != nil { + res = append(res, err) + } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil } +// bindActive binds and validates parameter Active from query. +func (o *GetSilencesParams) bindActive(rawData []string, hasKey bool, formats strfmt.Registry) error { + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + // Required: false + // AllowEmptyValue: false + + if raw == "" { // empty values pass all other validations + // Default values have been previously initialized by NewGetSilencesParams() + return nil + } + + value, err := swag.ConvertBool(raw) + if err != nil { + return errors.InvalidType("active", "query", "bool", raw) + } + o.Active = &value + + return nil +} + +// bindExpired binds and validates parameter Expired from query. +func (o *GetSilencesParams) bindExpired(rawData []string, hasKey bool, formats strfmt.Registry) error { + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + // Required: false + // AllowEmptyValue: false + + if raw == "" { // empty values pass all other validations + // Default values have been previously initialized by NewGetSilencesParams() + return nil + } + + value, err := swag.ConvertBool(raw) + if err != nil { + return errors.InvalidType("expired", "query", "bool", raw) + } + o.Expired = &value + + return nil +} + // bindFilter binds and validates array parameter Filter from query. // // Arrays are parsed according to CollectionFormat: "multi" (defaults to "csv" when empty). @@ -92,3 +188,27 @@ func (o *GetSilencesParams) bindFilter(rawData []string, hasKey bool, formats st return nil } + +// bindPending binds and validates parameter Pending from query. +func (o *GetSilencesParams) bindPending(rawData []string, hasKey bool, formats strfmt.Registry) error { + var raw string + if len(rawData) > 0 { + raw = rawData[len(rawData)-1] + } + + // Required: false + // AllowEmptyValue: false + + if raw == "" { // empty values pass all other validations + // Default values have been previously initialized by NewGetSilencesParams() + return nil + } + + value, err := swag.ConvertBool(raw) + if err != nil { + return errors.InvalidType("pending", "query", "bool", raw) + } + o.Pending = &value + + return nil +} diff --git a/api/v2/restapi/operations/silence/get_silences_urlbuilder.go b/api/v2/restapi/operations/silence/get_silences_urlbuilder.go index a96c95bf4b..49239bdbfb 100644 --- a/api/v2/restapi/operations/silence/get_silences_urlbuilder.go +++ b/api/v2/restapi/operations/silence/get_silences_urlbuilder.go @@ -29,7 +29,10 @@ import ( // GetSilencesURL generates an URL for the get silences operation type GetSilencesURL struct { - Filter []string + Active *bool + Expired *bool + Filter []string + Pending *bool _basePath string // avoid unkeyed usage @@ -65,6 +68,22 @@ func (o *GetSilencesURL) Build() (*url.URL, error) { qs := make(url.Values) + var activeQ string + if o.Active != nil { + activeQ = swag.FormatBool(*o.Active) + } + if activeQ != "" { + qs.Set("active", activeQ) + } + + var expiredQ string + if o.Expired != nil { + expiredQ = swag.FormatBool(*o.Expired) + } + if expiredQ != "" { + qs.Set("expired", expiredQ) + } + var filterIR []string for _, filterI := range o.Filter { filterIS := filterI @@ -79,6 +98,14 @@ func (o *GetSilencesURL) Build() (*url.URL, error) { qs.Add("filter", qsv) } + var pendingQ string + if o.Pending != nil { + pendingQ = swag.FormatBool(*o.Pending) + } + if pendingQ != "" { + qs.Set("pending", pendingQ) + } + _result.RawQuery = qs.Encode() return &_result, nil From 8d7515af74678b8a854938e5ddfef1e34d69f55e Mon Sep 17 00:00:00 2001 From: Pujitha Paladugu Date: Fri, 7 Aug 2026 07:38:07 -0700 Subject: [PATCH 100/120] template: add base64encode and base64decode template functions (#5413) Motivation: Notification templates have no way to base64-encode a string. Issue #3127 needs this to build a link into Karma, which expects its silence-search query parameter to be a base64-encoded JSON payload (`?m=`), e.g. as an action button URL in a Slack message. Approach: Add `base64encode` and `base64decode` to the DefaultFuncs map in template/template.go, alongside the existing `toJson`/`reReplaceAll`/ `urlUnescape` helpers. Both use base64.URLEncoding (the URL/filename-safe alphabet) rather than StdEncoding, since the output is meant to be embedded directly in a URL query parameter: StdEncoding's `+` and `/` characters can be misinterpreted by URL/form parsers (`+` is commonly decoded as a space), silently corrupting the payload, which URLEncoding avoids. The existing `dict`/`list`/`toJson` functions can already build the JSON structure Karma expects; base64encode is the missing piece to turn it into a link. This only adds new template functions; no existing behavior changes. Validation: go build ./template/... go test ./template/... both pass, including new test cases in template/template_test.go for base64encode, base64encode's URL-safe alphabet (verified against an input whose encoding differs between Std and URL alphabets), base64decode, and the base64decode error path for invalid input. docs/notifications.md's function reference table is updated with both new entries. Fixes #3127 ```release-notes [ENHANCEMENT] Templates: Add `base64encode` and `base64decode` template functions ``` Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com> Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com> Co-authored-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com> --- docs/notifications.md | 2 ++ template/template.go | 14 ++++++++++++++ template/template_test.go | 20 ++++++++++++++++++++ 3 files changed, 36 insertions(+) diff --git a/docs/notifications.md b/docs/notifications.md index 87b96f1aaa..52e91d6a23 100644 --- a/docs/notifications.md +++ b/docs/notifications.md @@ -87,6 +87,8 @@ templating. | Name | Arguments | Description | | ---------------- | -------------------------- | ----------- | | append | slice []any, args ...any | Returns a new slice with the provided arguments appended to the provided slice. | +| base64decode | text string | [base64.URLEncoding.DecodeString](https://pkg.go.dev/encoding/base64#Encoding.DecodeString), decodes a URL-safe base64 encoded string, and the error if it happened. | +| base64encode | text string | [base64.URLEncoding.EncodeToString](https://pkg.go.dev/encoding/base64#Encoding.EncodeToString), returns the URL-safe base64 encoding of a string, e.g. for use in a query parameter. | | date | string, time.Time | Returns the text representation of the time in the specified format. For documentation on formats refer to [pkg.go.dev/time](https://pkg.go.dev/time#pkg-constants). | | dict | values ...any | Returns a map of string to any, constructed from the variadic list of key-value pairs. The number of arguments must be even, and the keys must be strings. | | humanizeDuration | number or string | Returns a human-readable string representing the duration, and the error if it happened. | diff --git a/template/template.go b/template/template.go index 0f51864147..fea4bac291 100644 --- a/template/template.go +++ b/template/template.go @@ -16,6 +16,7 @@ package template import ( "bytes" "embed" + "encoding/base64" "encoding/json" "fmt" tmplhtml "html/template" @@ -345,6 +346,19 @@ var DefaultFuncs = FuncMap{ } return string(bytes), nil }, + // base64encode and base64decode use the URL-safe alphabet so the result + // can be embedded directly in a URL query parameter, e.g. to build a + // silence link for an external dashboard. + "base64encode": func(text string) string { + return base64.URLEncoding.EncodeToString([]byte(text)) + }, + "base64decode": func(text string) (string, error) { + decoded, err := base64.URLEncoding.DecodeString(text) + if err != nil { + return "", err + } + return string(decoded), nil + }, "list": func(args ...any) ([]any, error) { if args == nil { return []any{}, nil diff --git a/template/template_test.go b/template/template_test.go index aaae0a98bb..aba15abd5a 100644 --- a/template/template_test.go +++ b/template/template_test.go @@ -494,6 +494,26 @@ func TestTemplateExpansion(t *testing.T) { }, exp: `[{"status":"firing","labels":null,"annotations":null,"startsAt":"0001-01-01T00:00:00Z","endsAt":"0001-01-01T00:00:00Z","generatorURL":"","fingerprint":""}]`, }, + { + title: "Template using base64encode", + in: `{{ "test" | base64encode }}`, + exp: "dGVzdA==", + }, + { + title: "Template using base64encode produces a URL-safe alphabet", + in: `{{ "flush>>" | base64encode }}`, + exp: "Zmx1c2g-Pg==", + }, + { + title: "Template using base64decode", + in: `{{ "dGVzdA==" | base64decode }}`, + exp: "test", + }, + { + title: "Template using base64decode with invalid input", + in: `{{ "not-valid-base64!" | base64decode }}`, + fail: true, + }, { title: "Template creates empty dict when using dict on nil", in: `{{- $test := dict -}}{{ $test }}`, From 8adf4105ee8210d461f3fbb3214fdcf5e79ebfbe Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 Aug 2026 07:50:19 +0200 Subject: [PATCH 101/120] build(deps): bump the promci group with 3 updates (#5417) Bumps the promci group with 3 updates: [prometheus/promci/build](https://github.com/prometheus/promci), [prometheus/promci/publish_main](https://github.com/prometheus/promci) and [prometheus/promci/publish_release](https://github.com/prometheus/promci). Updates `prometheus/promci/build` from 0.8.5 to 0.9.0 Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build-release-branch.yml | 2 +- .github/workflows/ci.yml | 2 +- .github/workflows/publish.yml | 4 ++-- .github/workflows/release.yml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build-release-branch.yml b/.github/workflows/build-release-branch.yml index 5342c37960..c1795bbd2e 100644 --- a/.github/workflows/build-release-branch.yml +++ b/.github/workflows/build-release-branch.yml @@ -21,7 +21,7 @@ jobs: thread: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] needs: ci steps: - - uses: prometheus/promci/build@13941414d409d227afd67544e5d306827db5a1a2 # v0.8.5 + - uses: prometheus/promci/build@370e8c15dcec50043cbe66f2f34633d9efc0a190 # v0.9.0 with: parallelism: 12 thread: ${{ matrix.thread }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fe91ca9260..cdfa13611d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,7 +49,7 @@ jobs: with: name: ui-dist path: ui/app/dist - - uses: prometheus/promci/build@13941414d409d227afd67544e5d306827db5a1a2 # v0.8.5 + - uses: prometheus/promci/build@370e8c15dcec50043cbe66f2f34633d9efc0a190 # v0.9.0 with: promu_opts: "-p linux/amd64 -p windows/amd64 -p linux/arm64 -p darwin/amd64 -p darwin/arm64 -p linux/386" parallelism: 3 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index d55115912f..9d6bb074e9 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -20,7 +20,7 @@ jobs: thread: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] needs: ci steps: - - uses: prometheus/promci/build@13941414d409d227afd67544e5d306827db5a1a2 # v0.8.5 + - uses: prometheus/promci/build@370e8c15dcec50043cbe66f2f34633d9efc0a190 # v0.9.0 with: parallelism: 12 thread: ${{ matrix.thread }} @@ -31,7 +31,7 @@ jobs: packages: write # push the image to GHCR via github.token needs: build steps: - - uses: prometheus/promci/publish_main@13941414d409d227afd67544e5d306827db5a1a2 # v0.8.5 + - uses: prometheus/promci/publish_main@370e8c15dcec50043cbe66f2f34633d9efc0a190 # v0.9.0 with: docker_hub_login: ${{ secrets.docker_hub_login }} docker_hub_password: ${{ secrets.docker_hub_password }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3026e27986..3378b5dc2b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,7 +27,7 @@ jobs: with: name: ui-dist path: ui/app/dist - - uses: prometheus/promci/build@13941414d409d227afd67544e5d306827db5a1a2 # v0.8.5 + - uses: prometheus/promci/build@370e8c15dcec50043cbe66f2f34633d9efc0a190 # v0.9.0 with: parallelism: 12 thread: ${{ matrix.thread }} @@ -39,7 +39,7 @@ jobs: packages: write # push the image to GHCR via github.token needs: build steps: - - uses: prometheus/promci/publish_release@13941414d409d227afd67544e5d306827db5a1a2 # v0.8.5 + - uses: prometheus/promci/publish_release@370e8c15dcec50043cbe66f2f34633d9efc0a190 # v0.9.0 with: docker_hub_login: ${{ secrets.docker_hub_login }} docker_hub_password: ${{ secrets.docker_hub_password }} From 66612dbdd995cb51949d290f34eb33d260005541 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 Aug 2026 07:51:19 +0200 Subject: [PATCH 102/120] build(deps): bump actions/checkout from 7.0.0 to 7.0.1 (#5418) Bumps [actions/checkout](https://github.com/actions/checkout) from 7.0.0 to 7.0.1. Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 6 +++--- .github/workflows/mixin.yml | 2 +- .github/workflows/release.yml | 2 +- .github/workflows/ui-ci.yml | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cdfa13611d..2b9cf5c615 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,7 +8,7 @@ jobs: name: Test alertmanager frontend runs-on: ubuntu-latest steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 @@ -42,7 +42,7 @@ jobs: matrix: thread: [0, 1, 2] steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v4.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v4.0.0 with: persist-credentials: false - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 @@ -76,7 +76,7 @@ jobs: EMAIL_NO_AUTH_CONFIG: testdata/noauth.yml EMAIL_AUTH_CONFIG: testdata/auth.yml steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 diff --git a/.github/workflows/mixin.yml b/.github/workflows/mixin.yml index a373e7b06a..c724c58bad 100644 --- a/.github/workflows/mixin.yml +++ b/.github/workflows/mixin.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: install Go diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3378b5dc2b..84d18af145 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,7 +20,7 @@ jobs: thread: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] needs: ci steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 diff --git a/.github/workflows/ui-ci.yml b/.github/workflows/ui-ci.yml index 38343ce814..ce434fe4f9 100644 --- a/.github/workflows/ui-ci.yml +++ b/.github/workflows/ui-ci.yml @@ -23,7 +23,7 @@ jobs: run: working-directory: ./ui/mantine-ui steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 From ab843d7d112937c68c099123bd1394921e15c35e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 Aug 2026 07:52:27 +0200 Subject: [PATCH 103/120] build(deps): bump the mantine group in /ui/mantine-ui with 3 updates (#5419) Bumps the mantine group in /ui/mantine-ui with 3 updates: [@mantine/code-highlight](https://github.com/mantinedev/mantine/tree/HEAD/packages/@mantine/code-highlight), [@mantine/core](https://github.com/mantinedev/mantine/tree/HEAD/packages/@mantine/core) and [@mantine/hooks](https://github.com/mantinedev/mantine/tree/HEAD/packages/@mantine/hooks). Updates `@mantine/code-highlight` from 9.4.1 to 9.5.0 - [Release notes](https://github.com/mantinedev/mantine/releases) - [Changelog](https://github.com/mantinedev/mantine/blob/master/CHANGELOG.md) - [Commits](https://github.com/mantinedev/mantine/commits/9.5.0/packages/@mantine/code-highlight) Updates `@mantine/core` from 9.4.1 to 9.5.0 - [Release notes](https://github.com/mantinedev/mantine/releases) - [Changelog](https://github.com/mantinedev/mantine/blob/master/CHANGELOG.md) - [Commits](https://github.com/mantinedev/mantine/commits/9.5.0/packages/@mantine/core) Updates `@mantine/hooks` from 9.4.1 to 9.5.0 - [Release notes](https://github.com/mantinedev/mantine/releases) - [Changelog](https://github.com/mantinedev/mantine/blob/master/CHANGELOG.md) - [Commits](https://github.com/mantinedev/mantine/commits/9.5.0/packages/@mantine/hooks) --- updated-dependencies: - dependency-name: "@mantine/code-highlight" dependency-version: 9.5.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: mantine - dependency-name: "@mantine/core" dependency-version: 9.5.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: mantine - dependency-name: "@mantine/hooks" dependency-version: 9.5.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: mantine ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- ui/mantine-ui/package-lock.json | 34 ++++++++++++++++----------------- ui/mantine-ui/package.json | 2 +- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/ui/mantine-ui/package-lock.json b/ui/mantine-ui/package-lock.json index 48dacb4aa0..0811d6622b 100644 --- a/ui/mantine-ui/package-lock.json +++ b/ui/mantine-ui/package-lock.json @@ -8,7 +8,7 @@ "name": "alertmanager", "version": "0.0.0", "dependencies": { - "@mantine/code-highlight": "^9.4.1", + "@mantine/code-highlight": "^9.5.0", "@mantine/core": "^9.0.0", "@mantine/hooks": "^9.0.0", "@tanstack/react-query": "^5.101.2", @@ -572,42 +572,42 @@ "license": "MIT" }, "node_modules/@mantine/code-highlight": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/@mantine/code-highlight/-/code-highlight-9.4.1.tgz", - "integrity": "sha512-C6cMFf2LV+2r/oyn9hVhlMaeWWQ3vIV7zOti2kjdrXOU3ZQkE88c2tknFw8BLnZjO7teY2s+tL2HVDGOht1Y+Q==", + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/@mantine/code-highlight/-/code-highlight-9.5.0.tgz", + "integrity": "sha512-Soidz7O/AtOGlbKgqfH0OgAei2l4TDFw1L3Ux1DZQ2CqJO4ILPHTjuXXNhtNmeH4iTjsUoTjlReTAUfAw+QIDA==", "license": "MIT", "dependencies": { "clsx": "^2.1.1" }, "peerDependencies": { - "@mantine/core": "9.4.1", - "@mantine/hooks": "9.4.1", + "@mantine/core": "9.5.0", + "@mantine/hooks": "9.5.0", "react": "^19.2.0", "react-dom": "^19.2.0" } }, "node_modules/@mantine/core": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/@mantine/core/-/core-9.4.1.tgz", - "integrity": "sha512-lZWEICrum4+vwKxzh/mk4RB1N6BqZ0Cshdl6lhm7OYLiQzmOaxKtOgyaWz+vr65G8mqTXjalZalB+wmMVBYK2Q==", + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/@mantine/core/-/core-9.5.0.tgz", + "integrity": "sha512-lUZtPfW+ZIXthofPyw+SVgWNiV/OkJ498r3OZeuekPgM23p2AspqHzHjEGhe5DMSTyhma6BhIHF2H10dAp0ibw==", "license": "MIT", "dependencies": { "@floating-ui/react": "^0.27.19", "clsx": "^2.1.1", "react-number-format": "^5.4.5", "react-remove-scroll": "^2.7.2", - "type-fest": "^5.7.0" + "type-fest": "^5.8.0" }, "peerDependencies": { - "@mantine/hooks": "9.4.1", + "@mantine/hooks": "9.5.0", "react": "^19.2.0", "react-dom": "^19.2.0" } }, "node_modules/@mantine/hooks": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/@mantine/hooks/-/hooks-9.4.1.tgz", - "integrity": "sha512-eTI8wmzPx3r98zgKIEuvukmoGTHBhmtI6+9E6o2DbTmEU2eM1bCdjE2vFdf0op2AlRO0KEYEcZhNQi4T/SJk0A==", + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/@mantine/hooks/-/hooks-9.5.0.tgz", + "integrity": "sha512-d67+7dQW0ZJFiWXqZgwcPrZK0KxKMzAX+Sv663g40xhISseiUoK0f8k/Ss2amOA09IhFayeeaqprVRc4Fp5yEw==", "license": "MIT", "peerDependencies": { "react": "^19.2.0" @@ -2704,9 +2704,9 @@ "license": "0BSD" }, "node_modules/type-fest": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.7.0.tgz", - "integrity": "sha512-1URUxUqfHFM1c+zfSPsa3gnkO7Aq21qyH75SIduNYz4SzY964rn1X2vCMQaHSHhktiw+0kPa2iyb6PUpXqB6Vg==", + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.8.0.tgz", + "integrity": "sha512-YGYEVz3Fm5iy/AybuA0oyNFq7H4CgQNfRp/qfe8nurE1kuCeNm3/vfm9X4Mtl+qLyaKJUh5xrFZwogr41SMjYA==", "license": "(MIT OR CC0-1.0)", "dependencies": { "tagged-tag": "^1.0.0" diff --git a/ui/mantine-ui/package.json b/ui/mantine-ui/package.json index 2483517d4b..616f1d9e4d 100644 --- a/ui/mantine-ui/package.json +++ b/ui/mantine-ui/package.json @@ -18,7 +18,7 @@ "test": "npm run typecheck && npm run check && npm run vitest && npm run build" }, "dependencies": { - "@mantine/code-highlight": "^9.4.1", + "@mantine/code-highlight": "^9.5.0", "@mantine/core": "^9.0.0", "@mantine/hooks": "^9.0.0", "@tanstack/react-query": "^5.101.2", From 855ea0e8b973cc6f59b2a767054dc56d9773c0d4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 Aug 2026 07:55:34 +0200 Subject: [PATCH 104/120] build(deps-dev): bump @biomejs/biome from 2.5.2 to 2.5.6 in /ui/mantine-ui (#5427) Bumps [@biomejs/biome](https://github.com/biomejs/biome/tree/HEAD/packages/@biomejs/biome) from 2.5.2 to 2.5.6. - [Release notes](https://github.com/biomejs/biome/releases) - [Changelog](https://github.com/biomejs/biome/blob/main/packages/@biomejs/biome/CHANGELOG.md) - [Commits](https://github.com/biomejs/biome/commits/@biomejs/biome@2.5.6/packages/@biomejs/biome) --- updated-dependencies: - dependency-name: "@biomejs/biome" dependency-version: 2.5.6 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- ui/mantine-ui/package-lock.json | 72 ++++++++++++++++----------------- ui/mantine-ui/package.json | 2 +- 2 files changed, 37 insertions(+), 37 deletions(-) diff --git a/ui/mantine-ui/package-lock.json b/ui/mantine-ui/package-lock.json index 0811d6622b..0cc7b0d7d7 100644 --- a/ui/mantine-ui/package-lock.json +++ b/ui/mantine-ui/package-lock.json @@ -18,7 +18,7 @@ "react-router-dom": "^7.18.1" }, "devDependencies": { - "@biomejs/biome": "^2.5.2", + "@biomejs/biome": "^2.5.6", "@testing-library/dom": "^10.4.0", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", @@ -132,9 +132,9 @@ } }, "node_modules/@biomejs/biome": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.5.2.tgz", - "integrity": "sha512-VQ3RCqr7JmDIX+w6stWYl+g/3bYofN3q2wDBHUKKc/c7i5QWrFKFBZYCYPWTE6agsUPMIZZe6/CMmVUfUAhkKA==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.5.6.tgz", + "integrity": "sha512-lxVNjv7UF6KfhMJfL9gaUHbWdJdHbsAj6OSmwSYNdhRuG67NxNQ4Xdvh3TUxsSK9sBzJBQhEJj3AopmmNJ5pSA==", "dev": true, "license": "MIT OR Apache-2.0", "bin": { @@ -148,20 +148,20 @@ "url": "https://opencollective.com/biome" }, "optionalDependencies": { - "@biomejs/cli-darwin-arm64": "2.5.2", - "@biomejs/cli-darwin-x64": "2.5.2", - "@biomejs/cli-linux-arm64": "2.5.2", - "@biomejs/cli-linux-arm64-musl": "2.5.2", - "@biomejs/cli-linux-x64": "2.5.2", - "@biomejs/cli-linux-x64-musl": "2.5.2", - "@biomejs/cli-win32-arm64": "2.5.2", - "@biomejs/cli-win32-x64": "2.5.2" + "@biomejs/cli-darwin-arm64": "2.5.6", + "@biomejs/cli-darwin-x64": "2.5.6", + "@biomejs/cli-linux-arm64": "2.5.6", + "@biomejs/cli-linux-arm64-musl": "2.5.6", + "@biomejs/cli-linux-x64": "2.5.6", + "@biomejs/cli-linux-x64-musl": "2.5.6", + "@biomejs/cli-win32-arm64": "2.5.6", + "@biomejs/cli-win32-x64": "2.5.6" } }, "node_modules/@biomejs/cli-darwin-arm64": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.5.2.tgz", - "integrity": "sha512-e7P3P7EkwFc/KiX2AHw4YDLIBOMfG9CPCAwy52k5Bp0dfhkozx9hf6wCmIr2QeXy2XeccJ3V/Sg+hDmzYEqxSg==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.5.6.tgz", + "integrity": "sha512-zMOLZP4oMrjh6m1zcSj1ud2awUPgTuMVbmQhYYWL7J8HwCnbHHBvTm7VBTRuY7epT5bez76IpKYQ11ZAqHFlnw==", "cpu": [ "arm64" ], @@ -176,9 +176,9 @@ } }, "node_modules/@biomejs/cli-darwin-x64": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.5.2.tgz", - "integrity": "sha512-ymzMvjC1Jg0b9K0D26ZdARqFQXs7MocfLC5FOCGfkC0Ss+ACUJkX5364ZM5nT4NLZanHRZNVrZEy+Ibwcvux/g==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.5.6.tgz", + "integrity": "sha512-JAC1VqzvO7Th5ZplU0G2uGfkZbxEe9uDDektPAhF0JLusoz1w+T4okp2bkykI0bbaO2vslKiRfj4gU43JaGreA==", "cpu": [ "x64" ], @@ -193,9 +193,9 @@ } }, "node_modules/@biomejs/cli-linux-arm64": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.5.2.tgz", - "integrity": "sha512-t7sseOmqND57uUWTwlawU6BYj+J06T/9EkydzBhkrgw/FK3QVhjU2wsJR0frljrKZ0/I8A/rYw7284QgqjQfIQ==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.5.6.tgz", + "integrity": "sha512-6XsYwCFkp5sMxl85ffhgeGpGgs6A7dRYFnkceZ7WVxvycuTnGdD5xa534Z3xfrBQ0JCMK/mujT6ZNPJoghedwg==", "cpu": [ "arm64" ], @@ -213,9 +213,9 @@ } }, "node_modules/@biomejs/cli-linux-arm64-musl": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.5.2.tgz", - "integrity": "sha512-w+ANG0ZvTu9IeEg9QnstoOnk6L0fpwJifW6aHR18+cb5Z39bkANItYjAfMrnvce5tmMK+IQ6nPX7/kQFdam5iw==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.5.6.tgz", + "integrity": "sha512-eUa3jeeYvfMt19LBeh6E5PUZpxnTC4JqNWo+EDjTtQjAr2xLGnWaxACtVU1DQqmHYbvThlJzLX+ZsYgrqh2qVw==", "cpu": [ "arm64" ], @@ -233,9 +233,9 @@ } }, "node_modules/@biomejs/cli-linux-x64": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.5.2.tgz", - "integrity": "sha512-M/lOZrewzTCRDINbjhQ1gYYru37KlD3kJBQwwKCG0ckz5E9IZwIoJ3X0wBwRXA+yBDIwWUuPBHS67HzJY4dTfA==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.5.6.tgz", + "integrity": "sha512-Pop9VXCFUhFTMfFefZ39S+u2rOPyNp5iHlxbZRwXGACHLy2r0jjiRgJHmaEKJzL3SyxlVeGShXhvvElvWowonA==", "cpu": [ "x64" ], @@ -253,9 +253,9 @@ } }, "node_modules/@biomejs/cli-linux-x64-musl": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.5.2.tgz", - "integrity": "sha512-VArNLAzND063tF+XY0yPyM+DyahpzOMzOAvb7qs259nhjJWRjvjZdssuA+Rfl+l07+NOesKZ0Xu2yFrXyBMtzw==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.5.6.tgz", + "integrity": "sha512-2Vp13QdKysH3HIWLaYLhUUwbK+jbZonJD1K+Lr0d0RO4wH7mkYd43vJixEDm8cUWrowoRz4UUHF1nm9Ae7ym8A==", "cpu": [ "x64" ], @@ -273,9 +273,9 @@ } }, "node_modules/@biomejs/cli-win32-arm64": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.5.2.tgz", - "integrity": "sha512-kbjFFKyZlzYnAuw7sRy5qDoFG6zrP40UK08oPQsWK0ct3NMnGSt+Bs1iviEEyEIP57N5MrykGXdO/wRiaR4lww==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.5.6.tgz", + "integrity": "sha512-tDGshcm6BdkZOCGnTDX0Y8/U4IfBSlnUU7T56nNDuPEfed+aHg+u8G36NB43fJVl0Os6+QURXIE1yuD7AaEofA==", "cpu": [ "arm64" ], @@ -290,9 +290,9 @@ } }, "node_modules/@biomejs/cli-win32-x64": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.5.2.tgz", - "integrity": "sha512-4InchVpdVmdkkkgjQqKpgvyu+VPnoF/7RPSw5YATgEVpt2j72wcCAeV5TwaE9ZGJUZWZn7v2CwSAj6CrMJEx8A==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.5.6.tgz", + "integrity": "sha512-WN05KwXnTO/2J45RQPvzZMXf7tZUIofHoR35xIPfCo7pQ2RFidxI8sfb5mGsaTxdMmEOzHzOPRCdA5/fCpc7xQ==", "cpu": [ "x64" ], diff --git a/ui/mantine-ui/package.json b/ui/mantine-ui/package.json index 616f1d9e4d..1404a1918a 100644 --- a/ui/mantine-ui/package.json +++ b/ui/mantine-ui/package.json @@ -28,7 +28,7 @@ "react-router-dom": "^7.18.1" }, "devDependencies": { - "@biomejs/biome": "^2.5.2", + "@biomejs/biome": "^2.5.6", "@testing-library/dom": "^10.4.0", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", From 70a7d544dacd80d5e0ed9c59b8ad3f302295def7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 Aug 2026 07:56:29 +0200 Subject: [PATCH 105/120] build(deps): bump actions/setup-go from 6.5.0 to 7.0.0 (#5421) Bumps [actions/setup-go](https://github.com/actions/setup-go) from 6.5.0 to 7.0.0. - [Release notes](https://github.com/actions/setup-go/releases) - [Commits](https://github.com/actions/setup-go/compare/924ae3a1cded613372ab5595356fb5720e22ba16...b7ad1dad31e06c5925ef5d2fc7ad053ef454303e) --- updated-dependencies: - dependency-name: actions/setup-go dependency-version: 7.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/mixin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mixin.yml b/.github/workflows/mixin.yml index c724c58bad..6ba7168407 100644 --- a/.github/workflows/mixin.yml +++ b/.github/workflows/mixin.yml @@ -17,7 +17,7 @@ jobs: with: persist-credentials: false - name: install Go - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 + uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: 1.26.x # pin the mixtool version until https://github.com/monitoring-mixins/mixtool/issues/135 is merged. From a1ccf9c201a2086d94d01104515072de5e761a60 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 Aug 2026 07:57:00 +0200 Subject: [PATCH 106/120] build(deps-dev): bump @types/node from 26.1.0 to 26.1.2 in /ui/mantine-ui (#5429) build(deps-dev): bump @types/node in /ui/mantine-ui Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 26.1.0 to 26.1.2. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) --- updated-dependencies: - dependency-name: "@types/node" dependency-version: 26.1.2 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- ui/mantine-ui/package-lock.json | 8 ++++---- ui/mantine-ui/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ui/mantine-ui/package-lock.json b/ui/mantine-ui/package-lock.json index 0cc7b0d7d7..b5ddf63ec5 100644 --- a/ui/mantine-ui/package-lock.json +++ b/ui/mantine-ui/package-lock.json @@ -23,7 +23,7 @@ "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", - "@types/node": "^26.1.0", + "@types/node": "^26.1.2", "@types/react": "^19.2.17", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^6.0.3", @@ -1100,9 +1100,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "26.1.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.0.tgz", - "integrity": "sha512-O0A1G3xPGy4w7AgQdAQYUlQ+BKk2Oovw8eRpofyp5KdBZULnbe+WqaOVNrm705SHphCiG4XHsACrSmPu1f+Kgw==", + "version": "26.1.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.2.tgz", + "integrity": "sha512-Vu4a5UFA9rIIFJ7rB/Vaafh9lrCQszopTCx6KjFboXTGQbPNasehVR5TEiithSDGyd1DEiUByggTZsg8jukeIg==", "dev": true, "license": "MIT", "dependencies": { diff --git a/ui/mantine-ui/package.json b/ui/mantine-ui/package.json index 1404a1918a..5e8f8d0694 100644 --- a/ui/mantine-ui/package.json +++ b/ui/mantine-ui/package.json @@ -33,7 +33,7 @@ "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", - "@types/node": "^26.1.0", + "@types/node": "^26.1.2", "@types/react": "^19.2.17", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^6.0.3", From 87b171e340301704ecc56636a60eaa293d442a65 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 Aug 2026 07:58:53 +0200 Subject: [PATCH 107/120] build(deps): bump the aws group across 1 directory with 14 updates (#5430) Bumps the aws group with 8 updates in the / directory: Updates `github.com/aws/aws-sdk-go-v2` from 1.42.1 to 1.43.2 - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/v1.42.1...v1.43.2) Updates `github.com/aws/aws-sdk-go-v2/config` from 1.32.27 to 1.32.33 - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/config/v1.32.27...config/v1.32.33) Updates `github.com/aws/aws-sdk-go-v2/credentials` from 1.19.26 to 1.19.32 - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/credentials/v1.19.26...credentials/v1.19.32) Updates `github.com/aws/aws-sdk-go-v2/service/sns` from 1.40.3 to 1.42.2 - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/service/sfn/v1.40.3...service/s3/v1.42.2) Updates `github.com/aws/aws-sdk-go-v2/service/sts` from 1.43.5 to 1.45.2 - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/service/sts/v1.43.5...service/kms/v1.45.2) Updates `github.com/aws/smithy-go` from 1.27.3 to 1.27.5 - [Release notes](https://github.com/aws/smithy-go/releases) - [Changelog](https://github.com/aws/smithy-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws/smithy-go/compare/v1.27.3...v1.27.5) Updates `github.com/go-openapi/analysis` from 0.25.3 to 0.25.5 - [Release notes](https://github.com/go-openapi/analysis/releases) - [Commits](https://github.com/go-openapi/analysis/compare/v0.25.3...v0.25.5) Updates `github.com/go-openapi/loads` from 0.24.0 to 0.25.0 - [Release notes](https://github.com/go-openapi/loads/releases) - [Commits](https://github.com/go-openapi/loads/compare/v0.24.0...v0.25.0) Updates `github.com/go-openapi/runtime` from 0.32.4 to 0.33.0 - [Release notes](https://github.com/go-openapi/runtime/releases) - [Commits](https://github.com/go-openapi/runtime/compare/v0.32.4...v0.33.0) Updates `github.com/go-openapi/runtime/server-middleware` from 0.32.4 to 0.33.0 - [Release notes](https://github.com/go-openapi/runtime/releases) - [Commits](https://github.com/go-openapi/runtime/compare/v0.32.4...v0.33.0) Updates `github.com/go-openapi/spec` from 0.22.6 to 0.22.9 - [Release notes](https://github.com/go-openapi/spec/releases) - [Commits](https://github.com/go-openapi/spec/compare/v0.22.6...v0.22.9) Updates `github.com/go-openapi/strfmt` from 0.26.4 to 0.27.0 - [Release notes](https://github.com/go-openapi/strfmt/releases) - [Commits](https://github.com/go-openapi/strfmt/compare/v0.26.4...v0.27.0) Updates `github.com/go-openapi/swag` from 0.27.0 to 0.28.0 - [Release notes](https://github.com/go-openapi/swag/releases) - [Commits](https://github.com/go-openapi/swag/compare/v0.27.0...v0.28.0) Updates `github.com/go-openapi/validate` from 0.26.0 to 0.26.1 - [Release notes](https://github.com/go-openapi/validate/releases) - [Commits](https://github.com/go-openapi/validate/compare/v0.26.0...v0.26.1) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go-v2 dependency-version: 1.43.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: aws - dependency-name: github.com/aws/aws-sdk-go-v2/config dependency-version: 1.32.32 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: aws - dependency-name: github.com/aws/aws-sdk-go-v2/credentials dependency-version: 1.19.31 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: aws - dependency-name: github.com/aws/aws-sdk-go-v2/service/sns dependency-version: 1.42.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: aws - dependency-name: github.com/aws/aws-sdk-go-v2/service/sts dependency-version: 1.45.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: aws - dependency-name: github.com/aws/smithy-go dependency-version: 1.27.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: aws - dependency-name: github.com/go-openapi/analysis dependency-version: 0.25.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: aws - dependency-name: github.com/go-openapi/loads dependency-version: 0.25.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: aws - dependency-name: github.com/go-openapi/runtime dependency-version: 0.33.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: aws - dependency-name: github.com/go-openapi/runtime/server-middleware dependency-version: 0.33.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: aws - dependency-name: github.com/go-openapi/spec dependency-version: 0.22.9 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: aws - dependency-name: github.com/go-openapi/strfmt dependency-version: 0.27.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: aws - dependency-name: github.com/go-openapi/swag dependency-version: 0.28.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: aws - dependency-name: github.com/go-openapi/validate dependency-version: 0.26.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: aws ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 74 ++++++++++++++-------------- go.sum | 152 ++++++++++++++++++++++++++++----------------------------- 2 files changed, 113 insertions(+), 113 deletions(-) diff --git a/go.mod b/go.mod index 16d0e07195..28dc7d3b58 100644 --- a/go.mod +++ b/go.mod @@ -7,26 +7,26 @@ require ( github.com/KimMachineGun/automemlimit v0.7.5 github.com/alecthomas/kingpin/v2 v2.4.0 github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b - github.com/aws/aws-sdk-go-v2 v1.42.1 - github.com/aws/aws-sdk-go-v2/config v1.32.27 - github.com/aws/aws-sdk-go-v2/credentials v1.19.26 - github.com/aws/aws-sdk-go-v2/service/sns v1.40.3 - github.com/aws/aws-sdk-go-v2/service/sts v1.43.5 - github.com/aws/smithy-go v1.27.3 + github.com/aws/aws-sdk-go-v2 v1.43.2 + github.com/aws/aws-sdk-go-v2/config v1.32.33 + github.com/aws/aws-sdk-go-v2/credentials v1.19.32 + github.com/aws/aws-sdk-go-v2/service/sns v1.42.2 + github.com/aws/aws-sdk-go-v2/service/sts v1.45.2 + github.com/aws/smithy-go v1.27.5 github.com/cenkalti/backoff/v5 v5.0.3 github.com/cespare/xxhash/v2 v2.3.0 github.com/coreos/go-systemd/v22 v22.7.0 github.com/emersion/go-smtp v0.24.0 github.com/fsnotify/fsnotify v1.10.1 - github.com/go-openapi/analysis v0.25.3 + github.com/go-openapi/analysis v0.25.5 github.com/go-openapi/errors v0.22.8 - github.com/go-openapi/loads v0.24.0 - github.com/go-openapi/runtime v0.32.4 - github.com/go-openapi/runtime/server-middleware v0.32.4 - github.com/go-openapi/spec v0.22.6 - github.com/go-openapi/strfmt v0.26.4 - github.com/go-openapi/swag v0.27.0 - github.com/go-openapi/validate v0.26.0 + github.com/go-openapi/loads v0.25.0 + github.com/go-openapi/runtime v0.33.0 + github.com/go-openapi/runtime/server-middleware v0.33.0 + github.com/go-openapi/spec v0.22.9 + github.com/go-openapi/strfmt v0.27.0 + github.com/go-openapi/swag v0.28.0 + github.com/go-openapi/validate v0.26.1 github.com/google/uuid v1.6.0 github.com/hashicorp/go-sockaddr v1.0.7 github.com/hashicorp/golang-lru/v2 v2.0.7 @@ -65,35 +65,35 @@ require ( require ( github.com/armon/go-metrics v0.4.1 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.30 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.30 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.30 // indirect - github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.31 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.13 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.30 // indirect - github.com/aws/aws-sdk-go-v2/service/signin v1.2.2 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.31.5 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.8 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.33 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.33 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.33 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.34 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.14 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.33 // indirect + github.com/aws/aws-sdk-go-v2/service/signin v1.5.2 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.33.2 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.38.2 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/go-logr/logr v1.4.3 // indirect + github.com/go-logr/logr v1.4.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-openapi/jsonpointer v0.24.0 // indirect - github.com/go-openapi/jsonreference v0.21.6 // indirect - github.com/go-openapi/swag/cmdutils v0.27.0 // indirect - github.com/go-openapi/swag/conv v0.27.0 // indirect - github.com/go-openapi/swag/fileutils v0.27.0 // indirect - github.com/go-openapi/swag/jsonname v0.26.1 // indirect - github.com/go-openapi/swag/jsonutils v0.27.0 // indirect - github.com/go-openapi/swag/loading v0.27.0 // indirect - github.com/go-openapi/swag/mangling v0.27.0 // indirect - github.com/go-openapi/swag/netutils v0.27.0 // indirect - github.com/go-openapi/swag/stringutils v0.27.0 // indirect - github.com/go-openapi/swag/typeutils v0.27.0 // indirect - github.com/go-openapi/swag/yamlutils v0.27.0 // indirect + github.com/go-openapi/jsonpointer v1.0.0 // indirect + github.com/go-openapi/jsonreference v1.0.0 // indirect + github.com/go-openapi/swag/cmdutils v0.28.0 // indirect + github.com/go-openapi/swag/conv v0.28.0 // indirect + github.com/go-openapi/swag/fileutils v0.28.0 // indirect + github.com/go-openapi/swag/jsonutils v0.28.0 // indirect + github.com/go-openapi/swag/loading v0.28.0 // indirect + github.com/go-openapi/swag/mangling v0.28.0 // indirect + github.com/go-openapi/swag/netutils v0.28.0 // indirect + github.com/go-openapi/swag/pools v0.28.0 // indirect + github.com/go-openapi/swag/stringutils v0.28.0 // indirect + github.com/go-openapi/swag/typeutils v0.28.0 // indirect + github.com/go-openapi/swag/yamlutils v0.28.0 // indirect github.com/go-viper/mapstructure/v2 v2.5.0 // indirect github.com/golang-jwt/jwt/v5 v5.3.1 // indirect github.com/google/btree v1.1.3 // indirect diff --git a/go.sum b/go.sum index bed791d932..dcaf5413b3 100644 --- a/go.sum +++ b/go.sum @@ -79,36 +79,36 @@ github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJ github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/aws/aws-sdk-go-v2 v1.42.1 h1:9eOTgu1z/dVtYpNZ3/8/XbbaX0x/BqE3HUzAzs6K0ek= -github.com/aws/aws-sdk-go-v2 v1.42.1/go.mod h1:5pKeft2eJj+gElQ38Jqg4ibCqh+/AK33/0X3hip7IjM= -github.com/aws/aws-sdk-go-v2/config v1.32.27 h1:SJwJ9Q4kM7v5QVSYYyXj3znRr6lNyZEhSgAXmXXcVbI= -github.com/aws/aws-sdk-go-v2/config v1.32.27/go.mod h1:uBfrzTRedDmB2u+b6+UlaKJy2O6VSH5un2jP24t/KvQ= -github.com/aws/aws-sdk-go-v2/credentials v1.19.26 h1:Si8kk1kyJnuJWCEgiwpBtTdtgSdR7i611596NnC0YIQ= -github.com/aws/aws-sdk-go-v2/credentials v1.19.26/go.mod h1:lBckz+W9SAdNtSDw3pYgQUJDJFcBBWry0GSzw+bK0TY= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.30 h1:/hi1JADLEW9YYryEz1w4GQu0EtP23pP553Cf9KgsDV4= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.30/go.mod h1:/3AOgy4K17Dm4ucMZVC/MJkzy5kmfKUcINRHZyo0koQ= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.30 h1:xM/Is9cKMHa8Jj8zkvWhvrFkZsXJV9E+BB4g0HW0duQ= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.30/go.mod h1:WueJeNDZvK1fMYEWJIkcivBfEzUkTpBhzlrUKKY8EuA= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.30 h1:jn46zC9LdsVR/ZpMIJqMqb8hHv31BlLx3ulVqNspUOk= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.30/go.mod h1:1hTMsAgbdS/AtUi4bw8+gUuh1pceo+eXRLfpSuSQj3M= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.31 h1:3GUprIsfmGcC5SACIyB0e7E0BM1O1b3Erl5CePYIAeQ= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.31/go.mod h1:7PuV1yl5e2xnUbm+RqvVg5i2iBM8EyijZNoI9wsOoOc= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.13 h1:mbRIur/BiHK6SKPjoBIXSE/hJ6g6JGRLuxQy1jGjlN4= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.13/go.mod h1:ITg9em2KbJx1s0y4aqRX5OYWG6HBZ5TVR//OdpEZ2CQ= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.30 h1:/Z5jmNrKsSD7EmDjzAPsm/3L9IuOkzaynklJZ1qX7S4= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.30/go.mod h1:lEzEZnOosE7zi8Z6royW1cFJTD9fpab4Ul1SBrllewk= -github.com/aws/aws-sdk-go-v2/service/signin v1.2.2 h1:69JEZSDTQ+UNbTWQJCZMmbpQb5sfc79KUt0O7Pyfjmo= -github.com/aws/aws-sdk-go-v2/service/signin v1.2.2/go.mod h1:mxC0nT/C8wMMS97DemZPzvUZxvIt+2Iq+eS3JdFZGgg= -github.com/aws/aws-sdk-go-v2/service/sns v1.40.3 h1:ZgC0JhdV3xY7u0nt2Rg91NY6p3SiAzzV8U3Y10DoEEI= -github.com/aws/aws-sdk-go-v2/service/sns v1.40.3/go.mod h1:5EnTxMpMVeiY0vcjjN/a958FFaHrS6XfXcyRBzDKDCE= -github.com/aws/aws-sdk-go-v2/service/sso v1.31.5 h1:xlK3Tdc8FO7Tq1k0+hL+otF33glj+dE+qeM5iINiDvU= -github.com/aws/aws-sdk-go-v2/service/sso v1.31.5/go.mod h1:u8af9Nqkmqnr96f7v9nHqzZT9XBwbXEkTiqT4ROuJSE= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.8 h1:yX1IbiBfC7SdEgDwIGnRaZyPPDRbQPDOJxl8102PcGk= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.8/go.mod h1:DMPWJBjYs6+3+f/qhBFEFPPlQ6NlhWjai3dJNvipJ84= -github.com/aws/aws-sdk-go-v2/service/sts v1.43.5 h1:T3ANO8QWDbzQD8f4+UaX+fvJlyGnOFMKLbW+NGBHg04= -github.com/aws/aws-sdk-go-v2/service/sts v1.43.5/go.mod h1:9gdl4RrflIdpDb2TlXshWgR1F9TeCkvqDx77Vpr4Z/Q= -github.com/aws/smithy-go v1.27.3 h1:F3Zb497UhhskkfpJmfkXswyo+t0sh9OTBnIHjogWbVY= -github.com/aws/smithy-go v1.27.3/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= +github.com/aws/aws-sdk-go-v2 v1.43.2 h1:cl+IXwWb3qazClUcm08tGSsB6OiuV83JVJO9B0jQcPc= +github.com/aws/aws-sdk-go-v2 v1.43.2/go.mod h1:WEzLKBh/mEjXvx1FtQMWgSxMSTVqxQzjkRtk5fa3wkg= +github.com/aws/aws-sdk-go-v2/config v1.32.33 h1:M1m/Q6f0OKDEDGwhiNOqx1OjTdrewe3v+GDbHmKczWk= +github.com/aws/aws-sdk-go-v2/config v1.32.33/go.mod h1:fGj1iQj2QpIZzp7jE4aQQ+71TE8cd4z9K4+xCd6EqmE= +github.com/aws/aws-sdk-go-v2/credentials v1.19.32 h1:eNE0JnIblBo1NCvd3tqEYuZz9XDefn69R74CHd3nT7U= +github.com/aws/aws-sdk-go-v2/credentials v1.19.32/go.mod h1:yYJu+6tqKUYZuJSYcpSGjz/6sV/SUaAaKIufnWKx2OU= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.33 h1:MobhiR6KIerWxmO74Zit5I3379+mSc2DOdZ3DeRFB9w= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.33/go.mod h1:xu02847OdZfNr/jAfZpHtyRk0b3v4d0kaoxNHxZGG/w= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.33 h1:HAp1wLFZzch054uh3FK7rcVYg4v7J2FxVf3h3IGNZas= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.33/go.mod h1:mJk5fmqnF+WUlMdPG37pR2Fh3oh6r8F6ZGUgPKvzu0c= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.33 h1:0YA0aCKgsJyno6xkFfaIgjE3/wK08+Qxo9nQfe1UrWM= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.33/go.mod h1:UZqj4WIdTH+ga8Y/DgpAuy/8cGjM3h7gDCliJYGg2SE= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.34 h1:HQYnjFnXpX8EbPW5M1QT8mXzesRPwly0HEPTcFlS02Y= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.34/go.mod h1:tGzj56niKYZBbDIRhwPGDqrULzmWv5b6uBQGqyNaFZw= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.14 h1:SA43nfaY7+1jjMNIc2ywu99JLJLButtIdLP6j+bT870= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.14/go.mod h1:Du3llKcwbQvHsTXSLzTOGQz0DTDBMEzdg7DAGu7inrY= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.33 h1:mqI7OrxN/DUH85F5OqVn3cIfuZ3+HVcebUm2N8mLlgQ= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.33/go.mod h1:eZ5jdEpvaaOU8nWWE4cTAJETSEA5FZoWxvNRao4piHY= +github.com/aws/aws-sdk-go-v2/service/signin v1.5.2 h1:EjI1CZzDcBxPkTa3j1BdtIrUDbqnOGssFMeyUS+6W0I= +github.com/aws/aws-sdk-go-v2/service/signin v1.5.2/go.mod h1:vN3eb5H8MEAZ4dx0F5Wc9LT8eb3eW7bZZ5BjGJdbw9k= +github.com/aws/aws-sdk-go-v2/service/sns v1.42.2 h1:sG8S3cdfG2+Shse+h8YWTKTrv4sj+48yASyin+YoQHM= +github.com/aws/aws-sdk-go-v2/service/sns v1.42.2/go.mod h1:EHECMrJFM/36j9vLqYP/ksa1js/0Kp5RIzxMEX5Yz1g= +github.com/aws/aws-sdk-go-v2/service/sso v1.33.2 h1:zMP1FDFE08L7sM5f1QqkH/ZgKKg8Uc0Dz7KhSSYqWkw= +github.com/aws/aws-sdk-go-v2/service/sso v1.33.2/go.mod h1:0LoIZSUKjdo2BleHfT1hv/jlD33LQS00IrBlzoUsoUQ= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.38.2 h1:9eTqUYl+SyVmaRPMyBXSO9wwqC6TRwZB82pKENK2hdQ= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.38.2/go.mod h1:DThweuz22kiLc7lGHop5vQ9c3bx5W6Azs/YqSHa2fu8= +github.com/aws/aws-sdk-go-v2/service/sts v1.45.2 h1:EJd8vZO3E8SE6nmPqxuxlQ1NeSb8as50sf6eGdV4Saw= +github.com/aws/aws-sdk-go-v2/service/sts v1.45.2/go.mod h1:OgpPvKzsO2Ranjpli/20djMkg6UrV5mw4W3pZpq1Mqo= +github.com/aws/smithy-go v1.27.5 h1:d1ro7KpYOYwP6m73YFa+Kc/A130VsAdX68SpsJwARMM= +github.com/aws/smithy-go v1.27.5/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -182,60 +182,60 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= -github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8= +github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= 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-openapi/analysis v0.25.3 h1:4zlcg85pd2xq3sEgjW887n1IpwCpCqTmqeT6dP9OxDw= -github.com/go-openapi/analysis v0.25.3/go.mod h1:6PEmUIra9/rn6SPstzbrMkhFAsMB2qm7g6E+4DRFyCU= +github.com/go-openapi/analysis v0.25.5 h1:xPYEvTb90o1y0epuiOPAoG4QqahjP3cdp5xNlHeKJRI= +github.com/go-openapi/analysis v0.25.5/go.mod h1:d3UGtQC5uq5Kqqqis2VH09Km/v3vwsWrYkbp4gdm+Rc= github.com/go-openapi/errors v0.22.8 h1:oP7sW7TWc3wFFjrzzj0nI83H2qMBkNjNfSd+XRejk/I= github.com/go-openapi/errors v0.22.8/go.mod h1:BuUoHcYrU6E7V9gfj1I5wLQqgtIHnup/alXZ8KdgQ0w= -github.com/go-openapi/jsonpointer v0.24.0 h1:AA6mCjHYHmZ+1RU2Js089EaOK/iwXXNwQsTgnsTha2M= -github.com/go-openapi/jsonpointer v0.24.0/go.mod h1:Z3rw7dWu1p9IgitXCFamSlA5lmDiklEB6vkaxcNZW5Y= -github.com/go-openapi/jsonreference v0.21.6 h1:NZ5nGfnaM1n4I43Xjm1e5/M2GjOwQwndQz22uhxwD+Y= -github.com/go-openapi/jsonreference v0.21.6/go.mod h1:xzbgtQ3ZbWxvET3AxdzCJlJt6vkovbf+IfSPJjD0tUY= -github.com/go-openapi/loads v0.24.0 h1:4LLorXRPTzIN9V6ngMUZbAscsBOUBk3Oa8cClu/bFrQ= -github.com/go-openapi/loads v0.24.0/go.mod h1:xQMgX+hw5xRAhGrcDXxeMw78IFqUpIzhleu3HqPhyF4= -github.com/go-openapi/runtime v0.32.4 h1:8ElGj/3goG0itt0nBPP6Cm57ehcYyuHoI3O20nxgvkw= -github.com/go-openapi/runtime v0.32.4/go.mod h1:Bz6keOZw1NX4T6f+m42OoT1MBPDt6Re13dbccHyGH/4= -github.com/go-openapi/runtime/server-middleware v0.32.4 h1:AU6eLMq9CXwh8f6kC1pivtkz+7lfo3TmakMBbUisKME= -github.com/go-openapi/runtime/server-middleware v0.32.4/go.mod h1:fYPep4GdTwg/XqZUjR40uIM/8C12Ba5M+MrGCiwpTHo= -github.com/go-openapi/spec v0.22.6 h1:Tyy1pLaNCM8GBCFLoGYLonjJi6zykqyLCjXLc19ZPic= -github.com/go-openapi/spec v0.22.6/go.mod h1:HZvTHat+iH0PALQRWhrqIHtU/PEqxqd89fu0MxGlMeM= -github.com/go-openapi/strfmt v0.26.4 h1:yI6IAEfcWow459BD5UzFY430KUwXZwBHrYusPFkhWlc= -github.com/go-openapi/strfmt v0.26.4/go.mod h1:hNJi6nb5ETD6i7A1yRo03M9S6ZoTPPoWff1iUexmfUc= -github.com/go-openapi/swag v0.27.0 h1:8ecSuZlh4NXc3GsmAOqECIYqDTApCWaMe3gO4gjJNEE= -github.com/go-openapi/swag v0.27.0/go.mod h1:Kkgz9Ht0+ul9/aVdFmc9xSyPzUwf/aFF5KiFPBXfSY0= -github.com/go-openapi/swag/cmdutils v0.27.0 h1:aIKiqhB29AaP+7xm8/CPg3uOpeHx2SUp6TvMpu/a31Y= -github.com/go-openapi/swag/cmdutils v0.27.0/go.mod h1:Sm1MVFMkF6guJJ+pQqHnQA3N0j9qALV3NxzDSv6bETM= -github.com/go-openapi/swag/conv v0.27.0 h1:EKOH4feXrvdo8DbSsXSAqRT8fz1epEnS5O2IfXUOzE8= -github.com/go-openapi/swag/conv v0.27.0/go.mod h1:pfiv0uKQTbaGApk8Zs/lZV3uSjmSpa2FO1y183YngN8= -github.com/go-openapi/swag/fileutils v0.27.0 h1:ib5jMUqGq5tY1EyO4inlrabsaeDAleFU+XD1FXQcgp8= -github.com/go-openapi/swag/fileutils v0.27.0/go.mod h1:VvJFZLTZS0AI854gEQz5tk7dBESdLjiNUMSZ/th2ry8= -github.com/go-openapi/swag/jsonname v0.26.1 h1:VReupaV6WxlAsCn0e4DUfgV6bPmINnPpyJDLqSfNPcE= -github.com/go-openapi/swag/jsonname v0.26.1/go.mod h1:OvdW6BoWoj33pTfi7x9vFrgmT+fk7aw0BRwvCE0YOuc= -github.com/go-openapi/swag/jsonutils v0.27.0 h1:VYtd9jEQYeU4j8q5vdn5KWotF4vKywhGdMBrALtAsfE= -github.com/go-openapi/swag/jsonutils v0.27.0/go.mod h1:U7pb8AGuwhok3RDicHeHwSG4L3PXSq6PAL98Aon632g= -github.com/go-openapi/swag/jsonutils/fixtures_test v0.27.0 h1:+d7C7Ur/SsGg/UZ9G0JEovnfRqtMNZCJQGKc2h/ojoE= -github.com/go-openapi/swag/jsonutils/fixtures_test v0.27.0/go.mod h1:mofwUWx70wvskwESqRJ//k/9kURmCgyJl5m5Ppoh5kY= -github.com/go-openapi/swag/loading v0.27.0 h1:s8DA9aPEdFH6OluHUYUn3DnIuoTdyWs9RwffXBUfyeI= -github.com/go-openapi/swag/loading v0.27.0/go.mod h1:VOz+Jg6UGGywcmRvYsI4fvtp+bd7NfioseGEPleYdA4= -github.com/go-openapi/swag/mangling v0.27.0 h1:rpPJuqQHa6z2pDiP3iIpXOyNXlSs9cQCxnJSAxzdfOc= -github.com/go-openapi/swag/mangling v0.27.0/go.mod h1:jtBE2+V+3pILxOR7Vgce+Cwp6A2PgZbvVqfNntbVs0w= -github.com/go-openapi/swag/netutils v0.27.0 h1:lEUG+hHvPvLggB3A8snFk0IRKNf9uC0YKc+7WYqvAF8= -github.com/go-openapi/swag/netutils v0.27.0/go.mod h1:J+WYyFMLtvtCGqa6jLv+YNUmIKI3ZRQRrvfNDMoQoEQ= -github.com/go-openapi/swag/stringutils v0.27.0 h1:Of7w/HljWsNZvuxsUAnw3n+hCOyI6HLJOxW2kQRAxio= -github.com/go-openapi/swag/stringutils v0.27.0/go.mod h1:lzRN95CxXmA03XcDWHLOb6nOMcxCqR5rGY0lOgsfRoM= -github.com/go-openapi/swag/typeutils v0.27.0 h1:aCf4MSGo8NLwZP8Q6t32DWLJSvl/WwNqgmEG+xJ6v2o= -github.com/go-openapi/swag/typeutils v0.27.0/go.mod h1:Srm0xFNRZ1Y+vCxJclo5qzx8aj+1pAKda/YfFPrG0dQ= -github.com/go-openapi/swag/yamlutils v0.27.0 h1:bQ6eAMil5X9tdcf7dMn4t15alzG6jddnrKPuKa/zxKM= -github.com/go-openapi/swag/yamlutils v0.27.0/go.mod h1:yRfIo7qqVkmJRQjX8exjA3AfcI8rH1KDNPsTparoCv4= +github.com/go-openapi/jsonpointer v1.0.0 h1:kR9tHqY0CtZaOPVFm622dPVNhrvYpwr4uCxgL3h1H8s= +github.com/go-openapi/jsonpointer v1.0.0/go.mod h1:Z3rw7dWu1p9IgitXCFamSlA5lmDiklEB6vkaxcNZW5Y= +github.com/go-openapi/jsonreference v1.0.0 h1:jlmTr6torcd1YgDQvSfNmRtKzYDO4FGBkrAdlAVWnpY= +github.com/go-openapi/jsonreference v1.0.0/go.mod h1:jtwdyGbJk0Xhe5Y+rwtglQP6Sb1WZST4rT32LWB+sv0= +github.com/go-openapi/loads v0.25.0 h1:74Bc2snfaVlsHzwdQj/3gsA9XJz3daXTJVs+4ZaK7jI= +github.com/go-openapi/loads v0.25.0/go.mod h1:JFBw4SIB9+PTIFHDfcXuSSy5h6aWzjtUCrPYyx3qWU8= +github.com/go-openapi/runtime v0.33.0 h1:Dd3Oj2ig+WH8ckK95l0Wn2V8a4bH/UqWPRZVT0vc8yU= +github.com/go-openapi/runtime v0.33.0/go.mod h1:+rsupH3+TFKqmFysqkmgBOTxpVJV8eV+j9myvvea2Xw= +github.com/go-openapi/runtime/server-middleware v0.33.0 h1:ZFUNyaa2eUs9DhLd6MTe/QRsuxeYn4Lq0C8iAoY13XE= +github.com/go-openapi/runtime/server-middleware v0.33.0/go.mod h1:OQHTBqMGquJShXhPYQ62yAqDMtC1rYpsEwldNWjYKhA= +github.com/go-openapi/spec v0.22.9 h1:/vKIFDcGKp0ktZWGbym/tJEWbk6/XOEmAVU0kqKMH+w= +github.com/go-openapi/spec v0.22.9/go.mod h1:b/mNUYIOQOyIiUzUzXEE8xzyZqf93KvM9hQGP91yfl0= +github.com/go-openapi/strfmt v0.27.0 h1:kbcTeaD9TXuXD0hhMXzuYa1sdTo6+dWGvwjW93E80IM= +github.com/go-openapi/strfmt v0.27.0/go.mod h1:s/qhDqfY72irigXUGJmtgid2Rm+3tnz3k8hZaRmvWYc= +github.com/go-openapi/swag v0.28.0 h1:xkgbOSKj6DZziNpyqRRAOt3GJGtgjgsd2RoyT30VWuw= +github.com/go-openapi/swag v0.28.0/go.mod h1:4qYnT3Cqr1p1VknOdPo70evN4rgQnAg6jwApHyxSGIg= +github.com/go-openapi/swag/cmdutils v0.28.0 h1:7TOeNtkYru1SG8Y34tDh9WBbLsMqGnptuxWiHREPZ4Q= +github.com/go-openapi/swag/cmdutils v0.28.0/go.mod h1:Sm1MVFMkF6guJJ+pQqHnQA3N0j9qALV3NxzDSv6bETM= +github.com/go-openapi/swag/conv v0.28.0 h1:GtqqbyFe7vR5Y7ehxG9W6/OvrSFdf1OLeTGp40TqxH8= +github.com/go-openapi/swag/conv v0.28.0/go.mod h1:mbUE+mzctnhxi864m0Q07SpN8OowD9JhxmxuYvZZD/k= +github.com/go-openapi/swag/fileutils v0.28.0 h1:Z04XWQD7R8Eq+7GnOrjovBxPPmZzsS4gt2H2GPGIViU= +github.com/go-openapi/swag/fileutils v0.28.0/go.mod h1:VvJFZLTZS0AI854gEQz5tk7dBESdLjiNUMSZ/th2ry8= +github.com/go-openapi/swag/jsonutils v0.28.0 h1:YIch6FwO7RXzeAnbO8Tu7dWBZeUEH+4nA0HXltVTnv4= +github.com/go-openapi/swag/jsonutils v0.28.0/go.mod h1:CYM3WlTUcagR2ZoHdz54di/cbBqt82tuxuXgAjxw+mg= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.28.0 h1:qV+VVUAx5Oro8WjVWpZeql7YReTKhT4smR4zhcOQZr0= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.28.0/go.mod h1:mofwUWx70wvskwESqRJ//k/9kURmCgyJl5m5Ppoh5kY= +github.com/go-openapi/swag/loading v0.28.0 h1:td8QZdZC9MIYGGSnSPKShKiK22I2tU5UQvuUhIBPRLU= +github.com/go-openapi/swag/loading v0.28.0/go.mod h1:rXB0QiQX5mMveXEA7ouM4KiiM9jVJe4K6BVbwhD1M4k= +github.com/go-openapi/swag/mangling v0.28.0 h1:pH8eyeNO9SLYsTMWJrurnNfKmDa28XrlA+HePVD53VM= +github.com/go-openapi/swag/mangling v0.28.0/go.mod h1:jtBE2+V+3pILxOR7Vgce+Cwp6A2PgZbvVqfNntbVs0w= +github.com/go-openapi/swag/netutils v0.28.0 h1:YXN6TALEi2pzts8/8GNm6T61HTAZsieukGZidap989k= +github.com/go-openapi/swag/netutils v0.28.0/go.mod h1:J+WYyFMLtvtCGqa6jLv+YNUmIKI3ZRQRrvfNDMoQoEQ= +github.com/go-openapi/swag/pools v0.28.0 h1:HPMZWSAfce3rdVTFcjFiCIBtDg9h4x2QlRrHipwhxeU= +github.com/go-openapi/swag/pools v0.28.0/go.mod h1:kVQefhSK5RWuRe7BXsL8htgBPAMpN7HDGpGEknqugeE= +github.com/go-openapi/swag/stringutils v0.28.0 h1:ixsc9iYgDPubHL/8nSkbnryEHpD2VRlBMLKpQyPXcDU= +github.com/go-openapi/swag/stringutils v0.28.0/go.mod h1:lzRN95CxXmA03XcDWHLOb6nOMcxCqR5rGY0lOgsfRoM= +github.com/go-openapi/swag/typeutils v0.28.0 h1:nRBKSBXjDgf01VDPB3fWeD9nQuhCOVeIYAkUx2tbkyY= +github.com/go-openapi/swag/typeutils v0.28.0/go.mod h1:Srm0xFNRZ1Y+vCxJclo5qzx8aj+1pAKda/YfFPrG0dQ= +github.com/go-openapi/swag/yamlutils v0.28.0 h1:TV3JXH6DS46KUroDtMLAYHGkdWf5VDq3wVWFirmzROY= +github.com/go-openapi/swag/yamlutils v0.28.0/go.mod h1:x0q/yndZHEgk9Rx3DyDqzFUmHy55KTvIZldvF2dTJXs= github.com/go-openapi/testify/enable/yaml/v2 v2.6.0 h1:gGHwAJ0R/5jU8BEGDbfRNR3hL68dAVi84WuOApp29B0= github.com/go-openapi/testify/enable/yaml/v2 v2.6.0/go.mod h1:tY+St1SGq4NFl0QIqdTY4aEdbChAHxhyB77XQi9iJCo= github.com/go-openapi/testify/v2 v2.6.0 h1:5PKH2HE7YJ/LuRPQGvSxBRlFXNQhSetBLlGAgUEu3ug= github.com/go-openapi/testify/v2 v2.6.0/go.mod h1:SgsVHtfooshd0tublTtJ50FPKhujf47YRqauXXOUxfw= -github.com/go-openapi/validate v0.26.0 h1:dxWzQ3F+vb1SajqUxHjwb5T4mTpSHmdrtv5Bi7+ZNhw= -github.com/go-openapi/validate v0.26.0/go.mod h1:b4o00uq7fJeJA+wWhVFCJpKTctzeFwzZImGGmHsl2JA= +github.com/go-openapi/validate v0.26.1 h1:pZSbvtRO8G2R2FpWTYRn3w8LrsNwbtaVhP2dWiBa0Us= +github.com/go-openapi/validate v0.26.1/go.mod h1:B8UMgXiQiwwQWIbmuROlwJZDPGlikPuh7iHV1vPX9Oo= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= From 5f33ec5de796cbabda5741df39e023dc202f69d3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 Aug 2026 07:59:22 +0200 Subject: [PATCH 108/120] build(deps): bump github.com/prometheus/client_golang from 1.23.2 to 1.24.1 (#5432) build(deps): bump github.com/prometheus/client_golang Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.23.2 to 1.24.1. - [Release notes](https://github.com/prometheus/client_golang/releases) - [Changelog](https://github.com/prometheus/client_golang/blob/v1.24.1/CHANGELOG.md) - [Commits](https://github.com/prometheus/client_golang/compare/v1.23.2...v1.24.1) --- updated-dependencies: - dependency-name: github.com/prometheus/client_golang dependency-version: 1.24.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 8 ++++---- go.sum | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index 28dc7d3b58..cdc29650a9 100644 --- a/go.mod +++ b/go.mod @@ -35,9 +35,9 @@ require ( github.com/mdlayher/vsock v1.3.0 github.com/oklog/run v1.2.0 github.com/oklog/ulid/v2 v2.1.1 - github.com/prometheus/client_golang v1.23.2 + github.com/prometheus/client_golang v1.24.1 github.com/prometheus/client_model v0.6.2 - github.com/prometheus/common v0.69.0 + github.com/prometheus/common v0.70.1 github.com/prometheus/exporter-toolkit v0.17.1 github.com/prometheus/sigv4 v0.4.1 github.com/rs/cors v1.11.1 @@ -106,7 +106,7 @@ require ( github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/jpillora/backoff v1.0.0 // indirect github.com/julienschmidt/httprouter v1.3.0 // indirect - github.com/klauspost/compress v1.18.6 // indirect + github.com/klauspost/compress v1.19.1 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/mdlayher/socket v0.6.0 // indirect github.com/miekg/dns v1.1.72 // indirect @@ -115,7 +115,7 @@ require ( github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect github.com/pierrec/lz4/v4 v4.1.26 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/procfs v0.16.1 // indirect + github.com/prometheus/procfs v0.21.1 // indirect github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect github.com/twmb/franz-go/pkg/kmsg v1.13.1 // indirect github.com/xhit/go-str2duration/v2 v2.1.0 // indirect diff --git a/go.sum b/go.sum index dcaf5413b3..6921513260 100644 --- a/go.sum +++ b/go.sum @@ -402,8 +402,8 @@ github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4d github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= 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.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao= -github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= +github.com/klauspost/compress v1.19.1 h1:VsB4HPswih7mmZ8WleSFQ75c/Ui1M4trX5oAsJnhSlk= +github.com/klauspost/compress v1.19.1/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= @@ -486,8 +486,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.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= -github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= +github.com/prometheus/client_golang v1.24.1 h1:JnJkREXzWxUdCuPFpIWZiPispT9xVV59uiuyR2bPlnU= +github.com/prometheus/client_golang v1.24.1/go.mod h1:F+oSRECHg4sse5ucfYpYDeIv/hu68Zo0uoHKetWnzcE= 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= @@ -498,8 +498,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.69.0 h1:OA85nJQS/T/MaYh/Q2CcgDKSGWqNIgrBDvDH85CuiNk= -github.com/prometheus/common v0.69.0/go.mod h1:ZzL3f6u94qUxh9p+tJTrF+FvBS1XXbbRAZCQkytAL0Y= +github.com/prometheus/common v0.70.1 h1:1HvjP4D5oL3t8RsPlwxA9onvvStjtIHYE5XuuwOi/PY= +github.com/prometheus/common v0.70.1/go.mod h1:VdFUQDMZK3VLkurFUVhia6uys/0suUp86TJz5qbJRhc= github.com/prometheus/exporter-toolkit v0.17.1 h1:psKN4wM7shBL/BxZkDHgm6YZJ3fAVG36+r86An/+7q0= github.com/prometheus/exporter-toolkit v0.17.1/go.mod h1:dabwPJvxsC5+tsp2iolQrqBWZh+QlISKlYRpj9Hh5xk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= @@ -507,8 +507,8 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= -github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= +github.com/prometheus/procfs v0.21.1 h1:GljZCt+zSTS+NZq88cyQ1LjZ+RCHp3uVuabBWA5+OJI= +github.com/prometheus/procfs v0.21.1/go.mod h1:aB55Cww9pdSJVHk0hUf0inxWyyjPogFIjmHKYgMKmtY= github.com/prometheus/sigv4 v0.4.1 h1:EIc3j+8NBea9u1iV6O5ZAN8uvPq2xOIUPcqCTivHuXs= github.com/prometheus/sigv4 v0.4.1/go.mod h1:eu+ZbRvsc5TPiHwqh77OWuCnWK73IdkETYY46P4dXOU= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= From a5a81c033018cde9ce7c65f2bde772ab6ece6781 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 Aug 2026 08:04:36 +0200 Subject: [PATCH 109/120] build(deps-dev): bump postcss from 8.5.16 to 8.5.25 in /ui/mantine-ui in the styles group (#5425) build(deps-dev): bump postcss in /ui/mantine-ui in the styles group Bumps the styles group in /ui/mantine-ui with 1 update: [postcss](https://github.com/postcss/postcss). Updates `postcss` from 8.5.16 to 8.5.25 - [Release notes](https://github.com/postcss/postcss/releases) - [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md) - [Commits](https://github.com/postcss/postcss/compare/8.5.16...8.5.25) --- updated-dependencies: - dependency-name: postcss dependency-version: 8.5.25 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: styles ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- ui/mantine-ui/package-lock.json | 16 ++++++++-------- ui/mantine-ui/package.json | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ui/mantine-ui/package-lock.json b/ui/mantine-ui/package-lock.json index b5ddf63ec5..4678f245f1 100644 --- a/ui/mantine-ui/package-lock.json +++ b/ui/mantine-ui/package-lock.json @@ -28,7 +28,7 @@ "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^6.0.3", "jsdom": "^29.1.1", - "postcss": "^8.5.15", + "postcss": "^8.5.25", "postcss-preset-mantine": "1.18.0", "postcss-simple-vars": "^7.0.1", "prop-types": "^15.8.1", @@ -1957,9 +1957,9 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.12", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", - "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "version": "3.3.16", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz", + "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==", "dev": true, "funding": [ { @@ -2024,9 +2024,9 @@ "license": "ISC" }, "node_modules/postcss": { - "version": "8.5.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.16.tgz", - "integrity": "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==", + "version": "8.5.25", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.25.tgz", + "integrity": "sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw==", "dev": true, "funding": [ { @@ -2044,7 +2044,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.12", + "nanoid": "^3.3.16", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, diff --git a/ui/mantine-ui/package.json b/ui/mantine-ui/package.json index 5e8f8d0694..e7f28818d0 100644 --- a/ui/mantine-ui/package.json +++ b/ui/mantine-ui/package.json @@ -38,7 +38,7 @@ "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^6.0.3", "jsdom": "^29.1.1", - "postcss": "^8.5.15", + "postcss": "^8.5.25", "postcss-preset-mantine": "1.18.0", "postcss-simple-vars": "^7.0.1", "prop-types": "^15.8.1", From e9a6078a7f692b4fca5609476232835889eccff0 Mon Sep 17 00:00:00 2001 From: PrometheusBot Date: Wed, 12 Aug 2026 08:05:20 +0200 Subject: [PATCH 110/120] Synchronize common files from prometheus/prometheus (#5439) Update common Prometheus files Signed-off-by: prombot --- .github/workflows/container_description.yml | 4 ++-- .github/workflows/govulncheck.yml | 2 +- .github/workflows/stale.yml | 2 +- Makefile.common | 3 ++- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/container_description.yml b/.github/workflows/container_description.yml index 591a4d687f..ed4ada556d 100644 --- a/.github/workflows/container_description.yml +++ b/.github/workflows/container_description.yml @@ -21,7 +21,7 @@ jobs: if: github.repository_owner == 'prometheus' || github.repository_owner == 'prometheus-community' # Don't run this workflow on forks. steps: - name: git checkout - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Set docker hub repo name @@ -45,7 +45,7 @@ jobs: if: github.repository_owner == 'prometheus' || github.repository_owner == 'prometheus-community' # Don't run this workflow on forks. steps: - name: git checkout - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Set quay.io org name diff --git a/.github/workflows/govulncheck.yml b/.github/workflows/govulncheck.yml index 374df32baf..8c4deff9f8 100644 --- a/.github/workflows/govulncheck.yml +++ b/.github/workflows/govulncheck.yml @@ -28,6 +28,6 @@ jobs: run: sudo apt-get update && sudo apt-get -y install libsnmp-dev if: github.repository == 'prometheus/snmp_exporter' - id: govulncheck - uses: golang/govulncheck-action@3fa7bd9cee2cfdf3499a8803b226e43de7b7cdb4 # master + uses: golang/govulncheck-action@032d45514ae346b1db93c04b0c90b841c370344f # master env: GOOS: ${{ contains(github.repository, 'windows_exporter') && 'windows' || '' }} diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 5df426b096..25a0ad0b4d 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -14,7 +14,7 @@ jobs: if: github.repository_owner == 'prometheus' || github.repository_owner == 'prometheus-community' # Don't run this workflow on forks. runs-on: ubuntu-latest steps: - - uses: actions/stale@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 # v10.3.0 + - uses: actions/stale@1e223db275d687790206a7acac4d1a11bd6fe629 # v10.4.0 with: repo-token: ${{ secrets.GITHUB_TOKEN }} # opt out of defaults to avoid marking issues as stale and closing them diff --git a/Makefile.common b/Makefile.common index 5cfd6b8bff..b336657fb3 100644 --- a/Makefile.common +++ b/Makefile.common @@ -166,7 +166,8 @@ common-deps: update-go-deps: @echo ">> updating Go dependencies" @for m in $$($(GO) list -mod=readonly -m -f '{{ if and (not .Indirect) (not .Main)}}{{.Path}}{{end}}' all); do \ - $(GO) get $$m; \ + # Prevent go get from pulling deps that require a newer Go version. + GOTOOLCHAIN=go$$($(GO) mod edit -json | jq -r .Go) $(GO) get $$m; \ done $(GO) mod tidy From 5cd990f713b27dc43e581f91b1863c01315a633d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 Aug 2026 08:05:47 +0200 Subject: [PATCH 111/120] build(deps): bump @tanstack/react-query from 5.101.2 to 5.101.4 in /ui/mantine-ui (#5428) build(deps): bump @tanstack/react-query in /ui/mantine-ui Bumps [@tanstack/react-query](https://github.com/TanStack/query/tree/HEAD/packages/react-query) from 5.101.2 to 5.101.4. - [Release notes](https://github.com/TanStack/query/releases) - [Changelog](https://github.com/TanStack/query/blob/main/packages/react-query/CHANGELOG.md) - [Commits](https://github.com/TanStack/query/commits/@tanstack/react-query@5.101.4/packages/react-query) --- updated-dependencies: - dependency-name: "@tanstack/react-query" dependency-version: 5.101.4 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- ui/mantine-ui/package-lock.json | 16 ++++++++-------- ui/mantine-ui/package.json | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ui/mantine-ui/package-lock.json b/ui/mantine-ui/package-lock.json index 4678f245f1..e35306fe2d 100644 --- a/ui/mantine-ui/package-lock.json +++ b/ui/mantine-ui/package-lock.json @@ -11,7 +11,7 @@ "@mantine/code-highlight": "^9.5.0", "@mantine/core": "^9.0.0", "@mantine/hooks": "^9.0.0", - "@tanstack/react-query": "^5.101.2", + "@tanstack/react-query": "^5.101.4", "highlight.js": "^11.11.1", "react": "^19.2.7", "react-dom": "^19.2.7", @@ -932,9 +932,9 @@ "license": "MIT" }, "node_modules/@tanstack/query-core": { - "version": "5.101.2", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.101.2.tgz", - "integrity": "sha512-hH5MLoJhF7KaIGd7q3xTXGXvslI+GYlM1Z/35aSHHWaCJWB7XvTSHYuV3eM7tw+aE0mT/xMro4M4Q9rCGHT0lw==", + "version": "5.101.4", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.101.4.tgz", + "integrity": "sha512-gNwcvOJcRbLWPOLG/2OBm+zM+Yv+MKsXKEOWC57USuZDEsI71hEErQsiEGx5wX9rzWWkfwM0fVSPoiIFSsxfiw==", "license": "MIT", "funding": { "type": "github", @@ -942,12 +942,12 @@ } }, "node_modules/@tanstack/react-query": { - "version": "5.101.2", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.101.2.tgz", - "integrity": "sha512-seDkr6kzGzX1okaaTtZPtgA688CDPlXUz1C6xSg0ESqn04Vuc8tlrYms1s3de+znBqhPVxFRfpAfUf+6XvfPWg==", + "version": "5.101.4", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.101.4.tgz", + "integrity": "sha512-yRg2pfOCxIs4ZJW3XYYHU/WgtD04FHSnfHlpRT7h7pR77hwkdRG4wxbKe4aq6P0RvXUTBSQpQeadS1SUYUe+KA==", "license": "MIT", "dependencies": { - "@tanstack/query-core": "5.101.2" + "@tanstack/query-core": "5.101.4" }, "funding": { "type": "github", diff --git a/ui/mantine-ui/package.json b/ui/mantine-ui/package.json index e7f28818d0..fc657a5fff 100644 --- a/ui/mantine-ui/package.json +++ b/ui/mantine-ui/package.json @@ -21,7 +21,7 @@ "@mantine/code-highlight": "^9.5.0", "@mantine/core": "^9.0.0", "@mantine/hooks": "^9.0.0", - "@tanstack/react-query": "^5.101.2", + "@tanstack/react-query": "^5.101.4", "highlight.js": "^11.11.1", "react": "^19.2.7", "react-dom": "^19.2.7", From b9914e66d389b0b6e9ff4ded5ae7b676be9777a3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 Aug 2026 08:06:24 +0200 Subject: [PATCH 112/120] build(deps): bump actions/setup-node from 6.4.0 to 7.0.0 (#5420) Bumps [actions/setup-node](https://github.com/actions/setup-node) from 6.4.0 to 7.0.0. - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](https://github.com/actions/setup-node/compare/48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e...820762786026740c76f36085b0efc47a31fe5020) --- updated-dependencies: - dependency-name: actions/setup-node dependency-version: 7.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- .github/workflows/ui-ci.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2b9cf5c615..07b5064b97 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version-file: './.nvmrc' cache: 'npm' diff --git a/.github/workflows/ui-ci.yml b/.github/workflows/ui-ci.yml index ce434fe4f9..974ae6f639 100644 --- a/.github/workflows/ui-ci.yml +++ b/.github/workflows/ui-ci.yml @@ -26,7 +26,7 @@ jobs: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version-file: './.nvmrc' cache: 'npm' From 5d2efff2cd6aef80c3053866b43832d7ae54c9ee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 Aug 2026 06:10:56 +0000 Subject: [PATCH 113/120] build(deps): bump the react group across 1 directory with 5 updates (#5444) Bumps the react group with 5 updates in the /ui/mantine-ui directory: Updates `react` from 19.2.7 to 19.2.8 - [Release notes](https://github.com/react/react/releases) - [Changelog](https://github.com/react/react/blob/main/CHANGELOG.md) - [Commits](https://github.com/react/react/commits/v19.2.8/packages/react) Updates `@types/react` from 19.2.17 to 19.2.18 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react) Updates `react-dom` from 19.2.7 to 19.2.8 - [Release notes](https://github.com/react/react/releases) - [Changelog](https://github.com/react/react/blob/main/CHANGELOG.md) - [Commits](https://github.com/react/react/commits/v19.2.8/packages/react-dom) Updates `@types/react-dom` from 19.2.3 to 19.2.4 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-dom) Updates `react-router-dom` from 7.18.1 to 7.18.2 - [Release notes](https://github.com/remix-run/react-router/releases) - [Changelog](https://github.com/remix-run/react-router/blob/react-router-dom@7.18.2/packages/react-router-dom/CHANGELOG.md) - [Commits](https://github.com/remix-run/react-router/commits/react-router-dom@7.18.2/packages/react-router-dom) Updates `@types/react` from 19.2.17 to 19.2.18 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react) Updates `@types/react-dom` from 19.2.3 to 19.2.4 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-dom) --- updated-dependencies: - dependency-name: "@types/react" dependency-version: 19.2.18 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: react - dependency-name: "@types/react" dependency-version: 19.2.18 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: react - dependency-name: "@types/react-dom" dependency-version: 19.2.4 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: react - dependency-name: "@types/react-dom" dependency-version: 19.2.4 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: react - dependency-name: react dependency-version: 19.2.8 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: react - dependency-name: react-dom dependency-version: 19.2.8 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: react - dependency-name: react-router-dom dependency-version: 7.18.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: react ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- ui/mantine-ui/package-lock.json | 50 ++++++++++++++++----------------- ui/mantine-ui/package.json | 10 +++---- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/ui/mantine-ui/package-lock.json b/ui/mantine-ui/package-lock.json index e35306fe2d..f498700626 100644 --- a/ui/mantine-ui/package-lock.json +++ b/ui/mantine-ui/package-lock.json @@ -13,9 +13,9 @@ "@mantine/hooks": "^9.0.0", "@tanstack/react-query": "^5.101.4", "highlight.js": "^11.11.1", - "react": "^19.2.7", - "react-dom": "^19.2.7", - "react-router-dom": "^7.18.1" + "react": "^19.2.8", + "react-dom": "^19.2.8", + "react-router-dom": "^7.18.2" }, "devDependencies": { "@biomejs/biome": "^2.5.6", @@ -24,8 +24,8 @@ "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", "@types/node": "^26.1.2", - "@types/react": "^19.2.17", - "@types/react-dom": "^19.2.3", + "@types/react": "^19.2.18", + "@types/react-dom": "^19.2.4", "@vitejs/plugin-react": "^6.0.3", "jsdom": "^29.1.1", "postcss": "^8.5.25", @@ -1110,9 +1110,9 @@ } }, "node_modules/@types/react": { - "version": "19.2.17", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz", - "integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==", + "version": "19.2.18", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.18.tgz", + "integrity": "sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==", "devOptional": true, "license": "MIT", "dependencies": { @@ -1120,9 +1120,9 @@ } }, "node_modules/@types/react-dom": { - "version": "19.2.3", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", - "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.4.tgz", + "integrity": "sha512-Bsc+QHgp+P/F02XDzNCY9jnZNCUuLki36KT7VKrTXXLdHf+vHMNZnW1rVu5DNW/rCK+fya3DATySbLM4yhtKUw==", "dev": true, "license": "MIT", "peerDependencies": { @@ -2236,24 +2236,24 @@ } }, "node_modules/react": { - "version": "19.2.7", - "resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz", - "integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==", + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.8.tgz", + "integrity": "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==", "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/react-dom": { - "version": "19.2.7", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.7.tgz", - "integrity": "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==", + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.8.tgz", + "integrity": "sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==", "license": "MIT", "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { - "react": "^19.2.7" + "react": "^19.2.8" } }, "node_modules/react-is": { @@ -2321,9 +2321,9 @@ } }, "node_modules/react-router": { - "version": "7.18.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.18.1.tgz", - "integrity": "sha512-GDLgg3i3uM0aeJO3Fm+TCS+sDQ7gu12T6x0qdTEzcwqEfleci7JwugVNIF3U//0FWKnJT7ptG+20B2jfDqnZAg==", + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.18.2.tgz", + "integrity": "sha512-aUVMjFm3GAPTTZL7oYr5E7ETiqfQCHRLH+B+5afnICvf0r7kkK4eR6SMuwbSTJw/7t+12khT/Kahij49fqOCIg==", "license": "MIT", "dependencies": { "cookie": "^1.0.1", @@ -2343,12 +2343,12 @@ } }, "node_modules/react-router-dom": { - "version": "7.18.1", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.18.1.tgz", - "integrity": "sha512-KaZh+X/6UtEp28x51AUYZDMg9NGoz2ja3dNHa+ta/tk40vCzKhQ/RypCWBMLbmDr6//E24Vv5uPsrqXFozdkAg==", + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.18.2.tgz", + "integrity": "sha512-AIKJ/jgGlFb3EbfCXk5Gzshiwt+l3mqbCrNjmEWMMjqQxNJ3svBa6bgzFyCC2Sw3RA0VWF1kg3uQf2OFhxb8hw==", "license": "MIT", "dependencies": { - "react-router": "7.18.1" + "react-router": "7.18.2" }, "engines": { "node": ">=20.0.0" diff --git a/ui/mantine-ui/package.json b/ui/mantine-ui/package.json index fc657a5fff..ff4c8eb868 100644 --- a/ui/mantine-ui/package.json +++ b/ui/mantine-ui/package.json @@ -23,9 +23,9 @@ "@mantine/hooks": "^9.0.0", "@tanstack/react-query": "^5.101.4", "highlight.js": "^11.11.1", - "react": "^19.2.7", - "react-dom": "^19.2.7", - "react-router-dom": "^7.18.1" + "react": "^19.2.8", + "react-dom": "^19.2.8", + "react-router-dom": "^7.18.2" }, "devDependencies": { "@biomejs/biome": "^2.5.6", @@ -34,8 +34,8 @@ "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", "@types/node": "^26.1.2", - "@types/react": "^19.2.17", - "@types/react-dom": "^19.2.3", + "@types/react": "^19.2.18", + "@types/react-dom": "^19.2.4", "@vitejs/plugin-react": "^6.0.3", "jsdom": "^29.1.1", "postcss": "^8.5.25", From 0f8855c27a7412c9973330721f2870539df01884 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 Aug 2026 08:14:19 +0200 Subject: [PATCH 114/120] build(deps): bump github.com/oklog/ulid/v2 from 2.1.1 to 2.1.2 (#5434) Bumps [github.com/oklog/ulid/v2](https://github.com/oklog/ulid) from 2.1.1 to 2.1.2. - [Release notes](https://github.com/oklog/ulid/releases) - [Changelog](https://github.com/oklog/ulid/blob/main/CHANGELOG.md) - [Commits](https://github.com/oklog/ulid/compare/v2.1.1...v2.1.2) --- updated-dependencies: - dependency-name: github.com/oklog/ulid/v2 dependency-version: 2.1.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index cdc29650a9..830cfac35a 100644 --- a/go.mod +++ b/go.mod @@ -34,7 +34,7 @@ require ( github.com/jessevdk/go-flags v1.6.1 github.com/mdlayher/vsock v1.3.0 github.com/oklog/run v1.2.0 - github.com/oklog/ulid/v2 v2.1.1 + github.com/oklog/ulid/v2 v2.1.2 github.com/prometheus/client_golang v1.24.1 github.com/prometheus/client_model v0.6.2 github.com/prometheus/common v0.70.1 diff --git a/go.sum b/go.sum index 6921513260..fafd12a0bd 100644 --- a/go.sum +++ b/go.sum @@ -461,8 +461,8 @@ github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+ github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/oklog/run v1.2.0 h1:O8x3yXwah4A73hJdlrwo/2X6J62gE5qTMusH0dvz60E= github.com/oklog/run v1.2.0/go.mod h1:mgDbKRSwPhJfesJ4PntqFUbKQRZ50NgmZTSPlFA0YFk= -github.com/oklog/ulid/v2 v2.1.1 h1:suPZ4ARWLOJLegGFiZZ1dFAkqzhMjL3J1TzI+5wHz8s= -github.com/oklog/ulid/v2 v2.1.1/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ= +github.com/oklog/ulid/v2 v2.1.2 h1:IEclFb9JNvzYA6MW2SCxbLzcHTVsfqm3PrqGQJH5zec= +github.com/oklog/ulid/v2 v2.1.2/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= From 865dcf42c00f06f0e4804f19d4a430ef9520f9e5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 Aug 2026 06:16:54 +0000 Subject: [PATCH 115/120] build(deps-dev): bump the vite group across 1 directory with 2 updates (#5424) Updates `@vitejs/plugin-react` from 6.0.3 to 6.0.5 - [Release notes](https://github.com/vitejs/vite-plugin-react/releases) - [Changelog](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite-plugin-react/commits/plugin-react@6.0.5/packages/plugin-react) Updates `vite` from 8.1.3 to 8.2.1 - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v8.2.1/packages/vite) --- updated-dependencies: - dependency-name: "@vitejs/plugin-react" dependency-version: 6.0.4 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: vite - dependency-name: vite dependency-version: 8.1.5 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: vite ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- ui/mantine-ui/package-lock.json | 394 ++++++++++++-------------------- ui/mantine-ui/package.json | 4 +- 2 files changed, 150 insertions(+), 248 deletions(-) diff --git a/ui/mantine-ui/package-lock.json b/ui/mantine-ui/package-lock.json index f498700626..dccb8c1d81 100644 --- a/ui/mantine-ui/package-lock.json +++ b/ui/mantine-ui/package-lock.json @@ -26,14 +26,14 @@ "@types/node": "^26.1.2", "@types/react": "^19.2.18", "@types/react-dom": "^19.2.4", - "@vitejs/plugin-react": "^6.0.3", + "@vitejs/plugin-react": "^6.0.5", "jsdom": "^29.1.1", "postcss": "^8.5.25", "postcss-preset-mantine": "1.18.0", "postcss-simple-vars": "^7.0.1", "prop-types": "^15.8.1", "typescript": "^5.9.3", - "vite": "^8.1.3", + "vite": "^8.2.1", "vite-tsconfig-paths": "^6.1.1", "vitest": "^4.1.9" } @@ -459,40 +459,6 @@ "node": ">=20.19.0" } }, - "node_modules/@emnapi/core": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", - "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.2", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", - "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", - "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@exodus/bytes": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.0.tgz", @@ -613,29 +579,10 @@ "react": "^19.2.0" } }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", - "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@tybys/wasm-util": "^0.10.3" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - }, - "peerDependencies": { - "@emnapi/core": "^1.7.1", - "@emnapi/runtime": "^1.7.1" - } - }, "node_modules/@oxc-project/types": { - "version": "0.138.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.138.0.tgz", - "integrity": "sha512-1a7ZKmrRTCoN1XMZ4L0PyyqrMnrNlLyPuOkdSX2MZg7IiIGRUyurNhAm73ptDOraoBcIordsIGKNPKUzy3ZmfA==", + "version": "0.143.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.143.0.tgz", + "integrity": "sha512-u6JZdLBTLotrNC9Vd6vPssINdzcCzleKAH6EJKImQb7GtYvX5keN2dxkoK44stCc4tffE6QQRtZTXVSzsLUlWA==", "dev": true, "license": "MIT", "funding": { @@ -643,9 +590,9 @@ } }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.4.tgz", - "integrity": "sha512-EZLpf/8y7GXkkra90ML47kzik/GMP3EMcE9bPyHmRfxLC6z9+aW5A8poCsoxjrT5GfEcNAAvWwUHjvP1pUQkfw==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.3.tgz", + "integrity": "sha512-zrJtHDcaZJ1Fp7xf4hNl+7seH9Cn/N5TwLYkhgXREtBwAd/jaqW3uqeHxpDugJLVICWg4eW44kOQEGJ1r6jCGw==", "cpu": [ "arm64" ], @@ -660,9 +607,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.4.tgz", - "integrity": "sha512-aUi+HBvmYb7j8krl1+qJgkG8C17fO79gk3c+jPw4S8glRFc1DTija9S3EyaTSQUm5GJXYKDAsugBEhFHH2vYiQ==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.3.tgz", + "integrity": "sha512-ieIiibVCp0tX7TLu2cafoNPv8wJyYi01ekXpbf8q2j7F4rGAhhXb/eQh7ge9DRBY78GwmRQtvjZDux7EDbA8kA==", "cpu": [ "arm64" ], @@ -677,9 +624,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.4.tgz", - "integrity": "sha512-F7hHC3gwY11+vByKPRWqwGbeXWVgKmL+pTGCinaEhdihzBV2aQ0fvZOch9cXYUOKuKKq429HeYXOqQLc7wFCEg==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.3.tgz", + "integrity": "sha512-Zh9tCon19eDXJoihx0rqKhMUlMYqzwj3aPsSuHmI4RWZh62dWUL+DJN4C5YQya5TcQBJU/Fe8+rY0jhXTQITqA==", "cpu": [ "x64" ], @@ -694,9 +641,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.4.tgz", - "integrity": "sha512-sI5yw+7s92SK6odiEhD5lKCBlWcpjHS5qyqpVQbZAJ0fIzEUXrmbl3DH2ybR3PZogulNJF+COLtmA8hUfvkCCQ==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.3.tgz", + "integrity": "sha512-nGbJWewA1wrXXZiQhjAT5rhibGfns5ZNkDVqxsO6zJ3f3YvpoDNNmGMSbbhLuXKjNScaBJVOAboztAWVespQMg==", "cpu": [ "x64" ], @@ -711,9 +658,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.4.tgz", - "integrity": "sha512-mCi0OKgEieFircrtVYmQAFGszRtMnZ6fpZAXrxanXAu7lqZcsK1E1RAaZNG0uKAnxox3B1f4EyQNnoyMfN1vAA==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.3.tgz", + "integrity": "sha512-QNniJr5Kml0kDEB98jiDOJjXNroxIIi0IXIbdYzY26Xt1pVbeP62+KnoIZLwirOymX/0jDk/2gI/bNUv7A7OIw==", "cpu": [ "arm" ], @@ -728,9 +675,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.4.tgz", - "integrity": "sha512-B9Ial3Kv5sh0SHnB1g/QWcUQCEvCF6QKGAl4zXypYj65mVI+B4AhFBwPtSN7pDrJeIx8Z7zdy4ntx+wQABom7w==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.3.tgz", + "integrity": "sha512-TkqEAcmmvH3I/q4114NB4RVt6241Dao48pF45uLcFGrwAaIn0iITgTAKP/dLjbN0R4buJjGb91+UHSoFmpgIWw==", "cpu": [ "arm64" ], @@ -748,9 +695,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.4.tgz", - "integrity": "sha512-lZVym0PuHE1KZ22gmFTC15lAkrg9iTszR617oYRB/iPY1A56ywoJzVKOJBKaot5RiikCObmur6pogpse3gRcng==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.3.tgz", + "integrity": "sha512-NHqjnxpsndf4MPymxteFAWHHfkTL8HjWh1KB7z23ofZ6QO2euONuxDXjat69dKZRALnGypg8k8SsK8vZJoXv1Q==", "cpu": [ "arm64" ], @@ -768,9 +715,9 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.4.tgz", - "integrity": "sha512-t2DNiLJWNTbnEHyUzTumldML6ET4/g16467LZoDDJ3tSxGvguL5/NyC2lCsNKuyRycg9XeDQF5SSv+TNOhQEXg==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.3.tgz", + "integrity": "sha512-6tbrbwfz5GB9DQ4Jwo6hy9v+vR31xZlvzZ6n5Xut6Hhx5PvrA9q/HsK8KMaYQp063iqZGXwNvZtYNLD7EM/x0w==", "cpu": [ "ppc64" ], @@ -788,9 +735,9 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.4.tgz", - "integrity": "sha512-0WIRnL1Uw4BvTZRLQt+PVgo6ZKTJadlC2btP+/EOXv2f/DWbY0rEgl+y834mIVwP1FkTlWVTrGGJXf12lru7EQ==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.3.tgz", + "integrity": "sha512-oyuXxXmoZHjXC917IAPFAAv4wWAa0cM9afk8nx1+9/jNNOX1uPf8yDA6p7G0RypOfw/X0PQt5IfoquY1um+zSg==", "cpu": [ "s390x" ], @@ -808,9 +755,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.4.tgz", - "integrity": "sha512-JWtGshGfX+oENAKonoNkqEJX+7hC8yfhi9GUyPX1VX4mdh1y5r+ZiJLR5XzAB0aoP6s/PcILsGjKq8O0mm24bw==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.3.tgz", + "integrity": "sha512-TytMwF2KVGqP2tgd0I1OY0PAv78dZRAYcF5ssDzjM34SUXCED3uXvSd5+lHoC0bTD6eEdFz7LdQNCO1y0oVk9w==", "cpu": [ "x64" ], @@ -828,9 +775,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.4.tgz", - "integrity": "sha512-rT6yQcxUuXs4CnbofqwHRRV0iem349rLMYpTjkgQGLjrY4ado/eDzwPZPTCgTOlF6Nkp8NEv70yLMTn6qkWxsQ==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.3.tgz", + "integrity": "sha512-/E9m3qstrJFVPoULV25mVQblSNExY2+kBsYe4sy0Tn0yOOgJ8wZbZt3KnRbF/XeU2Gl1STKUQnDNTqhIE5MD4A==", "cpu": [ "x64" ], @@ -848,9 +795,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.4.tgz", - "integrity": "sha512-KXMGoboq5cyaCQjDA4GLuRiOwBQ0EyFnJoVViLeZ45/3rFItRODEr+NdsBcVpll40hhNArlm/speWGRvj08LzA==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.3.tgz", + "integrity": "sha512-Kr0OcsoQI816i6HOl3vFHpd1K0eZyh76zgfj4c1nTyaTsd5r2Mj1lwM4R90y/qaCfmTn9eHy0SKwi98eitRxug==", "cpu": [ "arm64" ], @@ -864,29 +811,10 @@ "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.4.tgz", - "integrity": "sha512-5K83rb36oJiY7BCyE9zLZtGcPV4g5wvq+xwdO0XPIwDVZI8cyB/AUjkNXGb92/rnmezEkjMOpgY61rtwjQtFwg==", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "1.11.1", - "@emnapi/runtime": "1.11.1", - "@napi-rs/wasm-runtime": "^1.1.6" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.4.tgz", - "integrity": "sha512-PnWBtw3TV5KOg69HQQDR0mnQuyCmSGR2pAB4DC1rPF808fgKeTUMj2EOEyKATpgiuxuR5APQmiDO7PDgEjTFSA==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.3.tgz", + "integrity": "sha512-hOtMwTqnME+/gJcH/PCZ0wn0zPUjiWOgkHpxbSJpfGKMezHltx1S7/k1SitzVa7Ww2cqrDDaFbZEhcJZO8o+Jw==", "cpu": [ "arm64" ], @@ -901,9 +829,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.4.tgz", - "integrity": "sha512-M1lpniBePobTfsa7Ks9a199e1akxsXn+GYBUKsEzv3YFzOm1HJAMNwKI3qr0Zq+mxwx9gOZoTdP1yXRYsZUocQ==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.3.tgz", + "integrity": "sha512-ekcqMMkI2PlhYnfzQnB/cEdYUVVJViWvoUyLrbzgDoi3Snfc1mVBwdnc306ufA5ejy8JSPjT2RlW1nQSjW7efg==", "cpu": [ "x64" ], @@ -1056,17 +984,6 @@ "@testing-library/dom": ">=7.21.4" } }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", - "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@types/aria-query": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", @@ -1130,9 +1047,9 @@ } }, "node_modules/@vitejs/plugin-react": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.3.tgz", - "integrity": "sha512-vmFvco5/QuC2f9Oj+wTk0+9XeDFkHxSamwZKYc7MxYwKICfvUvlMhqKI0VuICPltGqh1neqBKDvO4kes1ya8vg==", + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.5.tgz", + "integrity": "sha512-BOVzne/NL162sMdResB25mUv+vWMF5NoAjNf09TeGlE7ZpszZWSD3winycicLJw72yeVsoCn/2kOhEuCvEShMA==", "dev": true, "license": "MIT", "dependencies": { @@ -1629,9 +1546,9 @@ } }, "node_modules/lightningcss": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", - "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", "dev": true, "license": "MPL-2.0", "dependencies": { @@ -1645,23 +1562,23 @@ "url": "https://opencollective.com/parcel" }, "optionalDependencies": { - "lightningcss-android-arm64": "1.32.0", - "lightningcss-darwin-arm64": "1.32.0", - "lightningcss-darwin-x64": "1.32.0", - "lightningcss-freebsd-x64": "1.32.0", - "lightningcss-linux-arm-gnueabihf": "1.32.0", - "lightningcss-linux-arm64-gnu": "1.32.0", - "lightningcss-linux-arm64-musl": "1.32.0", - "lightningcss-linux-x64-gnu": "1.32.0", - "lightningcss-linux-x64-musl": "1.32.0", - "lightningcss-win32-arm64-msvc": "1.32.0", - "lightningcss-win32-x64-msvc": "1.32.0" + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" } }, "node_modules/lightningcss-android-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", - "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", "cpu": [ "arm64" ], @@ -1680,9 +1597,9 @@ } }, "node_modules/lightningcss-darwin-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", - "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", "cpu": [ "arm64" ], @@ -1701,9 +1618,9 @@ } }, "node_modules/lightningcss-darwin-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", - "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", "cpu": [ "x64" ], @@ -1722,9 +1639,9 @@ } }, "node_modules/lightningcss-freebsd-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", - "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", "cpu": [ "x64" ], @@ -1743,9 +1660,9 @@ } }, "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", - "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", "cpu": [ "arm" ], @@ -1764,13 +1681,16 @@ } }, "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", - "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -1785,13 +1705,16 @@ } }, "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", - "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -1806,13 +1729,16 @@ } }, "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", - "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -1827,13 +1753,16 @@ } }, "node_modules/lightningcss-linux-x64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", - "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -1848,9 +1777,9 @@ } }, "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", - "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", "cpu": [ "arm64" ], @@ -1869,9 +1798,9 @@ } }, "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", - "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", "cpu": [ "x64" ], @@ -2023,6 +1952,19 @@ "dev": true, "license": "ISC" }, + "node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/postcss": { "version": "8.5.25", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.25.tgz", @@ -2405,13 +2347,13 @@ } }, "node_modules/rolldown": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.4.tgz", - "integrity": "sha512-IjZYiLxZwpnhwhdBH2ugdTGVSdhCQUmLxLoqyjiL0JxYjyRst+5a0P3xfrTxJ5F638j4Mvvw5FAX5XE6eHpXbA==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.3.tgz", + "integrity": "sha512-rn9wpmxplLf7NLNyCk9FyWh3FM43DbY8jOzCdEPzH7uflhTftRbCEpqi6Ly2osgoU8OwObtmavMbWLaWy4LX7A==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.138.0", + "@oxc-project/types": "=0.143.0", "@rolldown/pluginutils": "^1.0.0" }, "bin": { @@ -2421,21 +2363,20 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.1.4", - "@rolldown/binding-darwin-arm64": "1.1.4", - "@rolldown/binding-darwin-x64": "1.1.4", - "@rolldown/binding-freebsd-x64": "1.1.4", - "@rolldown/binding-linux-arm-gnueabihf": "1.1.4", - "@rolldown/binding-linux-arm64-gnu": "1.1.4", - "@rolldown/binding-linux-arm64-musl": "1.1.4", - "@rolldown/binding-linux-ppc64-gnu": "1.1.4", - "@rolldown/binding-linux-s390x-gnu": "1.1.4", - "@rolldown/binding-linux-x64-gnu": "1.1.4", - "@rolldown/binding-linux-x64-musl": "1.1.4", - "@rolldown/binding-openharmony-arm64": "1.1.4", - "@rolldown/binding-wasm32-wasi": "1.1.4", - "@rolldown/binding-win32-arm64-msvc": "1.1.4", - "@rolldown/binding-win32-x64-msvc": "1.1.4" + "@rolldown/binding-android-arm64": "1.2.3", + "@rolldown/binding-darwin-arm64": "1.2.3", + "@rolldown/binding-darwin-x64": "1.2.3", + "@rolldown/binding-freebsd-x64": "1.2.3", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.3", + "@rolldown/binding-linux-arm64-gnu": "1.2.3", + "@rolldown/binding-linux-arm64-musl": "1.2.3", + "@rolldown/binding-linux-ppc64-gnu": "1.2.3", + "@rolldown/binding-linux-s390x-gnu": "1.2.3", + "@rolldown/binding-linux-x64-gnu": "1.2.3", + "@rolldown/binding-linux-x64-musl": "1.2.3", + "@rolldown/binding-openharmony-arm64": "1.2.3", + "@rolldown/binding-win32-arm64-msvc": "1.2.3", + "@rolldown/binding-win32-x64-msvc": "1.2.3" } }, "node_modules/saxes": { @@ -2607,19 +2548,6 @@ } } }, - "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/tinyrainbow": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", @@ -2800,16 +2728,16 @@ "license": "MIT" }, "node_modules/vite": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.3.tgz", - "integrity": "sha512-Ds+gBRbj0lwRO2Y5hwnUBdxSwlAve9LeRyU4sNnAr0ewW0gWF0n5bgXgUzbgZ49MV9BVUAQUFYVcDUcilUExMA==", + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.1.tgz", + "integrity": "sha512-EU/eS7BH3XROHh2YnBefjM6DBKA6ZeMZEYQbj7NLWg5wHYlhB8B/Mayd5XsgWq+NFYccDOTemRpdETWR6Ka/lw==", "dev": true, "license": "MIT", "dependencies": { - "lightningcss": "^1.32.0", - "picomatch": "^4.0.4", - "postcss": "^8.5.16", - "rolldown": "~1.1.3", + "lightningcss": "^1.33.0", + "picomatch": "^4.0.5", + "postcss": "^8.5.25", + "rolldown": "~1.2.1", "tinyglobby": "^0.2.17" }, "bin": { @@ -2826,7 +2754,7 @@ }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.3.0", + "@vitejs/devtools": "^0.4.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", @@ -2892,19 +2820,6 @@ "vite": "*" } }, - "node_modules/vite/node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/vitest": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.9.tgz", @@ -2995,19 +2910,6 @@ } } }, - "node_modules/vitest/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/w3c-xmlserializer": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", diff --git a/ui/mantine-ui/package.json b/ui/mantine-ui/package.json index ff4c8eb868..f2ee937eb5 100644 --- a/ui/mantine-ui/package.json +++ b/ui/mantine-ui/package.json @@ -36,14 +36,14 @@ "@types/node": "^26.1.2", "@types/react": "^19.2.18", "@types/react-dom": "^19.2.4", - "@vitejs/plugin-react": "^6.0.3", + "@vitejs/plugin-react": "^6.0.5", "jsdom": "^29.1.1", "postcss": "^8.5.25", "postcss-preset-mantine": "1.18.0", "postcss-simple-vars": "^7.0.1", "prop-types": "^15.8.1", "typescript": "^5.9.3", - "vite": "^8.1.3", + "vite": "^8.2.1", "vite-tsconfig-paths": "^6.1.1", "vitest": "^4.1.9" } From 55d0d7992a910a44455d224792aa9d3cf653a936 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 Aug 2026 06:23:05 +0000 Subject: [PATCH 116/120] build(deps-dev): bump the testing group across 1 directory with 4 updates (#5445) Updates `@testing-library/jest-dom` from 6.9.1 to 7.0.0 - [Release notes](https://github.com/testing-library/jest-dom/releases) - [Changelog](https://github.com/testing-library/jest-dom/blob/main/CHANGELOG.md) - [Commits](https://github.com/testing-library/jest-dom/compare/v6.9.1...v7.0.0) Updates `@testing-library/user-event` from 14.6.1 to 14.6.3 - [Release notes](https://github.com/testing-library/user-event/releases) - [Changelog](https://github.com/testing-library/user-event/blob/main/CHANGELOG.md) - [Commits](https://github.com/testing-library/user-event/compare/v14.6.1...v14.6.3) Updates `jsdom` from 29.1.1 to 30.0.1 - [Release notes](https://github.com/jsdom/jsdom/releases) - [Commits](https://github.com/jsdom/jsdom/compare/v29.1.1...v30.0.1) Updates `vitest` from 4.1.9 to 4.1.10 - [Release notes](https://github.com/vitest-dev/vitest/releases) - [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md) - [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.10/packages/vitest) --- updated-dependencies: - dependency-name: "@testing-library/jest-dom" dependency-version: 7.0.0 dependency-type: direct:development update-type: version-update:semver-major dependency-group: testing - dependency-name: "@testing-library/user-event" dependency-version: 14.6.3 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: testing - dependency-name: jsdom dependency-version: 30.0.1 dependency-type: direct:development update-type: version-update:semver-major dependency-group: testing - dependency-name: vitest dependency-version: 4.1.10 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: testing ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- ui/mantine-ui/package-lock.json | 274 ++++++++++++++++---------------- ui/mantine-ui/package.json | 8 +- 2 files changed, 141 insertions(+), 141 deletions(-) diff --git a/ui/mantine-ui/package-lock.json b/ui/mantine-ui/package-lock.json index dccb8c1d81..40c4e80351 100644 --- a/ui/mantine-ui/package-lock.json +++ b/ui/mantine-ui/package-lock.json @@ -20,14 +20,14 @@ "devDependencies": { "@biomejs/biome": "^2.5.6", "@testing-library/dom": "^10.4.0", - "@testing-library/jest-dom": "^6.9.1", + "@testing-library/jest-dom": "^7.0.0", "@testing-library/react": "^16.3.2", - "@testing-library/user-event": "^14.6.1", + "@testing-library/user-event": "^14.6.3", "@types/node": "^26.1.2", "@types/react": "^19.2.18", "@types/react-dom": "^19.2.4", "@vitejs/plugin-react": "^6.0.5", - "jsdom": "^29.1.1", + "jsdom": "^30.0.1", "postcss": "^8.5.25", "postcss-preset-mantine": "1.18.0", "postcss-simple-vars": "^7.0.1", @@ -35,7 +35,7 @@ "typescript": "^5.9.3", "vite": "^8.2.1", "vite-tsconfig-paths": "^6.1.1", - "vitest": "^4.1.9" + "vitest": "^4.1.10" } }, "node_modules/@adobe/css-tools": { @@ -46,56 +46,38 @@ "license": "MIT" }, "node_modules/@asamuzakjp/css-color": { - "version": "5.1.11", - "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.1.11.tgz", - "integrity": "sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==", + "version": "6.0.7", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-6.0.7.tgz", + "integrity": "sha512-vC/bk1Lz7Tn/EfU9/apOTBk80/8dyGyWMowPoV1tJ52muDGsDqt2HPT2klrFUiY60MQmQv9q8yIht15JnBgDGw==", "dev": true, "license": "MIT", "dependencies": { - "@asamuzakjp/generational-cache": "^1.0.1", - "@csstools/css-calc": "^3.2.0", - "@csstools/css-color-parser": "^4.1.0", + "@csstools/css-calc": "^3.3.0", + "@csstools/css-color-parser": "^4.1.10", "@csstools/css-parser-algorithms": "^4.0.0", - "@csstools/css-tokenizer": "^4.0.0" + "@csstools/css-tokenizer": "^4.0.0", + "lru-cache": "^11.5.2" }, "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + "node": "^22.13.0 || >=24.0.0" } }, "node_modules/@asamuzakjp/dom-selector": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-7.1.1.tgz", - "integrity": "sha512-67RZDnYRc8H/8MLDgQCDE//zoqVFwajkepHZgmXrbwybzXOEwOWGPYGmALYl9J2DOLfFPPs6kKCqmbzV895hTQ==", + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-8.3.2.tgz", + "integrity": "sha512-93Z1N+BQNXysodoicpOIyNh2drHfz/CTf9nnT0FEx72GJcIiwgydD7tGAr78j41LsYn3hlRn+LdGPuBLn1Bl8Q==", "dev": true, "license": "MIT", "dependencies": { - "@asamuzakjp/generational-cache": "^1.0.1", - "@asamuzakjp/nwsapi": "^2.3.9", "bidi-js": "^1.0.3", "css-tree": "^3.2.1", - "is-potential-custom-element-name": "^1.0.1" + "is-potential-custom-element-name": "^1.0.1", + "lru-cache": "^11.5.2" }, "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" - } - }, - "node_modules/@asamuzakjp/generational-cache": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@asamuzakjp/generational-cache/-/generational-cache-1.0.1.tgz", - "integrity": "sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + "node": "^22.13.0 || >=24.0.0" } }, - "node_modules/@asamuzakjp/nwsapi": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", - "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", - "dev": true, - "license": "MIT" - }, "node_modules/@babel/code-frame": { "version": "7.29.0", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", @@ -320,9 +302,9 @@ } }, "node_modules/@csstools/color-helpers": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.0.2.tgz", - "integrity": "sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.1.0.tgz", + "integrity": "sha512-064IFJdjTfUqnjpCVpMOdbr8FLQBhinbZj6yRv2An2E41O/pLEXqfFRWqGq/SxlE5PEUYTlvWsG2r8MswAVvkg==", "dev": true, "funding": [ { @@ -340,9 +322,9 @@ } }, "node_modules/@csstools/css-calc": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.2.0.tgz", - "integrity": "sha512-bR9e6o2BDB12jzN/gIbjHa5wLJ4UjD1CB9pM7ehlc0ddk6EBz+yYS1EV2MF55/HUxrHcB/hehAyt5vhsA3hx7w==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.3.0.tgz", + "integrity": "sha512-c5ihYsPkdG6JCkU2zTMm4+k6r7RXuGxtWYhu5DHMIiF1FHzrfmHL5so11AoFpUv/tu61xfcmT4AmKoFfMPoqdQ==", "dev": true, "funding": [ { @@ -364,9 +346,9 @@ } }, "node_modules/@csstools/css-color-parser": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.1.0.tgz", - "integrity": "sha512-U0KhLYmy2GVj6q4T3WaAe6NPuFYCPQoE3b0dRGxejWDgcPp8TP7S5rVdM5ZrFaqu4N67X8YaPBw14dQSYx3IyQ==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.1.10.tgz", + "integrity": "sha512-UZhQLIUyJaaMepqehrCODwCg2KW25vFvLWBmqYFaPclYvvxzj/sG8LBOhBFCp11i9uE7t1EyS+RAoV9tztPFyw==", "dev": true, "funding": [ { @@ -380,8 +362,8 @@ ], "license": "MIT", "dependencies": { - "@csstools/color-helpers": "^6.0.2", - "@csstools/css-calc": "^3.2.0" + "@csstools/color-helpers": "^6.1.0", + "@csstools/css-calc": "^3.3.0" }, "engines": { "node": ">=20.19.0" @@ -415,9 +397,9 @@ } }, "node_modules/@csstools/css-syntax-patches-for-csstree": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.3.tgz", - "integrity": "sha512-SH60bMfrRCJF3morcdk57WklujF4Jr/EsQUzqkarfHXEFcAR1gg7fS/chAE922Sehgzc1/+Tz5H3Ypa1HiEKrg==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.7.tgz", + "integrity": "sha512-fQ+05118eQS1cofO3aJpB5efgpBZMvIzwr/sbC8kDLVA5XLG8q1kJV5yzrUAI1f7lvhPnm8fgIjzFB8/O/5Dig==", "dev": true, "funding": [ { @@ -460,9 +442,9 @@ } }, "node_modules/@exodus/bytes": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.0.tgz", - "integrity": "sha512-UY0nlA+feH81UGSHv92sLEPLCeZFjXOuHhrIo0HQydScuQc8s0A7kL/UdgwgDq8g8ilksmuoF35YVTNphV2aBQ==", + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.1.tgz", + "integrity": "sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==", "dev": true, "license": "MIT", "engines": { @@ -916,9 +898,9 @@ } }, "node_modules/@testing-library/jest-dom": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.9.1.tgz", - "integrity": "sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-7.0.0.tgz", + "integrity": "sha512-HKAH9C6mBo5yBG6yRO5i43L2iisencAo5z+o5P/saHUoY+miC5ivXRxHBJcFyB5ypPNxHJdK3BoF/3O4DIptMg==", "dev": true, "license": "MIT", "dependencies": { @@ -930,9 +912,12 @@ "redent": "^3.0.0" }, "engines": { - "node": ">=14", + "node": ">=22", "npm": ">=6", "yarn": ">=1" + }, + "peerDependencies": { + "@testing-library/dom": ">=10 <11" } }, "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": { @@ -971,9 +956,9 @@ } }, "node_modules/@testing-library/user-event": { - "version": "14.6.1", - "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.1.tgz", - "integrity": "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==", + "version": "14.6.3", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.3.tgz", + "integrity": "sha512-6dBq67jT8lE+JTE8Exm02Kt6ze43hz1jdiSpSJwtTZiT1xQQ6b7nZYTTQ9njdArdU8XklOwaDp/AbT/eYSKF4g==", "dev": true, "license": "MIT", "engines": { @@ -1073,16 +1058,16 @@ } }, "node_modules/@vitest/expect": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.9.tgz", - "integrity": "sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.10.tgz", + "integrity": "sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==", "dev": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", - "@vitest/spy": "4.1.9", - "@vitest/utils": "4.1.9", + "@vitest/spy": "4.1.10", + "@vitest/utils": "4.1.10", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" }, @@ -1091,13 +1076,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.9.tgz", - "integrity": "sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.10.tgz", + "integrity": "sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "4.1.9", + "@vitest/spy": "4.1.10", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, @@ -1118,9 +1103,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.9.tgz", - "integrity": "sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.10.tgz", + "integrity": "sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==", "dev": true, "license": "MIT", "dependencies": { @@ -1131,13 +1116,13 @@ } }, "node_modules/@vitest/runner": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.9.tgz", - "integrity": "sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.10.tgz", + "integrity": "sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.1.9", + "@vitest/utils": "4.1.10", "pathe": "^2.0.3" }, "funding": { @@ -1145,14 +1130,14 @@ } }, "node_modules/@vitest/snapshot": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.9.tgz", - "integrity": "sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.10.tgz", + "integrity": "sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.9", - "@vitest/utils": "4.1.9", + "@vitest/pretty-format": "4.1.10", + "@vitest/utils": "4.1.10", "magic-string": "^0.30.21", "pathe": "^2.0.3" }, @@ -1161,9 +1146,9 @@ } }, "node_modules/@vitest/spy": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.9.tgz", - "integrity": "sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.10.tgz", + "integrity": "sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==", "dev": true, "license": "MIT", "funding": { @@ -1171,13 +1156,13 @@ } }, "node_modules/@vitest/utils": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.9.tgz", - "integrity": "sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.10.tgz", + "integrity": "sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.9", + "@vitest/pretty-format": "4.1.10", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" }, @@ -1505,39 +1490,39 @@ "license": "MIT" }, "node_modules/jsdom": { - "version": "29.1.1", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.1.1.tgz", - "integrity": "sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q==", + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-30.0.1.tgz", + "integrity": "sha512-52v7mUVUfNQVYYqE1lcdaymWL0njO7lTLUog6ZvW2U5KsbiLk/GnZlVJ+qx0xfNJZ6Gn+KSpPNE52vurbxZwrA==", "dev": true, "license": "MIT", "dependencies": { - "@asamuzakjp/css-color": "^5.1.11", - "@asamuzakjp/dom-selector": "^7.1.1", + "@asamuzakjp/css-color": "^6.0.5", + "@asamuzakjp/dom-selector": "^8.3.0", "@bramus/specificity": "^2.4.2", - "@csstools/css-syntax-patches-for-csstree": "^1.1.3", - "@exodus/bytes": "^1.15.0", + "@csstools/css-syntax-patches-for-csstree": "^1.1.7", + "@exodus/bytes": "^1.15.1", "css-tree": "^3.2.1", "data-urls": "^7.0.0", "decimal.js": "^10.6.0", "html-encoding-sniffer": "^6.0.0", "is-potential-custom-element-name": "^1.0.1", - "lru-cache": "^11.3.5", + "lru-cache": "^11.5.2", "parse5": "^8.0.1", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", - "tough-cookie": "^6.0.1", - "undici": "^7.25.0", + "tough-cookie": "^6.0.2", + "undici": "^8.9.0", "w3c-xmlserializer": "^5.0.0", "webidl-conversions": "^8.0.1", "whatwg-mimetype": "^5.0.0", - "whatwg-url": "^16.0.1", + "whatwg-url": "^17.1.0", "xml-name-validator": "^5.0.0" }, "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24.0.0" + "node": "^22.22.2 || ^24.15.0 || >=26.0.0" }, "peerDependencies": { - "canvas": "^3.0.0" + "canvas": "^3.2.3" }, "peerDependenciesMeta": { "canvas": { @@ -1545,6 +1530,21 @@ } } }, + "node_modules/jsdom/node_modules/whatwg-url": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-17.1.0.tgz", + "integrity": "sha512-3GeworPmc2ZfEEHP7lEbUfBX/L75wdEsi0rLNhXcXxnoN5jyq0SL5gCy06SGW2cyTIZdTvWIDQNQoza++vKeaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.15.1", + "tr46": "^6.0.0", + "webidl-conversions": "^8.0.1" + }, + "engines": { + "node": "^22.14.0 || >=24.0.0" + } + }, "node_modules/lightningcss": { "version": "1.33.0", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", @@ -1832,9 +1832,9 @@ } }, "node_modules/lru-cache": { - "version": "11.3.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.5.tgz", - "integrity": "sha512-NxVFwLAnrd9i7KUBxC4DrUhmgjzOs+1Qm50D3oF1/oL+r1NpZ4gA7xvG0/zJ8evR7zIKn4vLf7qTNduWFtCrRw==", + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", "dev": true, "license": "BlueOak-1.0.0", "engines": { @@ -2549,9 +2549,9 @@ } }, "node_modules/tinyrainbow": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", - "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.1.tgz", + "integrity": "sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==", "dev": true, "license": "MIT", "engines": { @@ -2559,29 +2559,29 @@ } }, "node_modules/tldts": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.27.tgz", - "integrity": "sha512-I4FZcVFcqCRuT0ph6dCDpPuO4Xgzvh+spkcTr1gK7peIvxWauoloVO0vuy1FQnijT63ss6AsHB6+OIM4aXHbPg==", + "version": "7.4.10", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.4.10.tgz", + "integrity": "sha512-GgouD1B+sWwvkaEq8vXC15DjQitxbvs12oIXELpconwm+Tg3zfcEv4jgzq3vtKverDXsg3VI8aRgNL2Nra0Iog==", "dev": true, "license": "MIT", "dependencies": { - "tldts-core": "^7.0.27" + "tldts-core": "^7.4.10" }, "bin": { "tldts": "bin/cli.js" } }, "node_modules/tldts-core": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.27.tgz", - "integrity": "sha512-YQ7uPjgWUibIK6DW5lrKujGwUKhLevU4hcGbP5O6TcIUb+oTjJYJVWPS4nZsIHrEEEG6myk/oqAJUEQmpZrHsg==", + "version": "7.4.10", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.4.10.tgz", + "integrity": "sha512-KnQjp53ZekKgm/r3l+u8kJGGzYgrWdP8+Mql7a4vijh2WE0IrZWspQj/TpTxDho/YxO+AnOZnIjQcCD+q6iJsw==", "dev": true, "license": "MIT" }, "node_modules/tough-cookie": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.1.tgz", - "integrity": "sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.2.tgz", + "integrity": "sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -2661,13 +2661,13 @@ } }, "node_modules/undici": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.25.0.tgz", - "integrity": "sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-8.10.0.tgz", + "integrity": "sha512-HvltHd7avK13QIw/oLe4qoOLyoVSoafqJ2jYOrtMRBkbYT31eiBQ8O0ehRKZiEZCMEyLFQNIADpgCWC5fALvYQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=20.18.1" + "node": ">=22.19.0" } }, "node_modules/undici-types": { @@ -2821,19 +2821,19 @@ } }, "node_modules/vitest": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.9.tgz", - "integrity": "sha512-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.10.tgz", + "integrity": "sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "4.1.9", - "@vitest/mocker": "4.1.9", - "@vitest/pretty-format": "4.1.9", - "@vitest/runner": "4.1.9", - "@vitest/snapshot": "4.1.9", - "@vitest/spy": "4.1.9", - "@vitest/utils": "4.1.9", + "@vitest/expect": "4.1.10", + "@vitest/mocker": "4.1.10", + "@vitest/pretty-format": "4.1.10", + "@vitest/runner": "4.1.10", + "@vitest/snapshot": "4.1.10", + "@vitest/spy": "4.1.10", + "@vitest/utils": "4.1.10", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", @@ -2861,12 +2861,12 @@ "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.1.9", - "@vitest/browser-preview": "4.1.9", - "@vitest/browser-webdriverio": "4.1.9", - "@vitest/coverage-istanbul": "4.1.9", - "@vitest/coverage-v8": "4.1.9", - "@vitest/ui": "4.1.9", + "@vitest/browser-playwright": "4.1.10", + "@vitest/browser-preview": "4.1.10", + "@vitest/browser-webdriverio": "4.1.10", + "@vitest/coverage-istanbul": "4.1.10", + "@vitest/coverage-v8": "4.1.10", + "@vitest/ui": "4.1.10", "happy-dom": "*", "jsdom": "*", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" diff --git a/ui/mantine-ui/package.json b/ui/mantine-ui/package.json index f2ee937eb5..e8603893b2 100644 --- a/ui/mantine-ui/package.json +++ b/ui/mantine-ui/package.json @@ -30,14 +30,14 @@ "devDependencies": { "@biomejs/biome": "^2.5.6", "@testing-library/dom": "^10.4.0", - "@testing-library/jest-dom": "^6.9.1", + "@testing-library/jest-dom": "^7.0.0", "@testing-library/react": "^16.3.2", - "@testing-library/user-event": "^14.6.1", + "@testing-library/user-event": "^14.6.3", "@types/node": "^26.1.2", "@types/react": "^19.2.18", "@types/react-dom": "^19.2.4", "@vitejs/plugin-react": "^6.0.5", - "jsdom": "^29.1.1", + "jsdom": "^30.0.1", "postcss": "^8.5.25", "postcss-preset-mantine": "1.18.0", "postcss-simple-vars": "^7.0.1", @@ -45,6 +45,6 @@ "typescript": "^5.9.3", "vite": "^8.2.1", "vite-tsconfig-paths": "^6.1.1", - "vitest": "^4.1.9" + "vitest": "^4.1.10" } } From 085f0ef7eb41da24cab8cd000f1345b6250f2edb Mon Sep 17 00:00:00 2001 From: Solomon Jacobs Date: Sun, 16 Aug 2026 17:59:58 +0200 Subject: [PATCH 117/120] Release v0.34.0 (#5453) Signed-off-by: Solomon Jacobs --- CHANGELOG.md | 23 +++++++++++++++++++---- VERSION | 2 +- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f77d126d2..a5182671a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,24 @@ ## main / (unreleased) -* [CHANGE] notify: The `reason` label on `alertmanager_notifications_failed_total` now distinguishes `authError` (HTTP 401/403) and `rateLimited` (HTTP 429) from the generic `clientError`. Dashboards/alerts matching `reason="clientError"` for these codes must be updated. -* [ENHANCEMENT] notify: The discord and webex integrations now report a failure `reason` on `alertmanager_notifications_failed_total`. -* [ENHANCEMENT] eventrecorder: Add optional webhook batching. -* [BUGFIX] webhook: Keep custom `payload` string values verbatim instead of reinterpreting JSON leaves that look like YAML (e.g. values ending with a colon). #5302 +## 0.34.0 / 2026-08-16 + +* [CHANGE] notify: The `reason` label on `alertmanager_notifications_failed_total` now distinguishes `authError` (HTTP 401/403) and `rateLimited` (HTTP 429) from the generic `clientError`. Dashboards/alerts matching `reason="clientError"` for these codes must be updated. #5332 +* [FEATURE] Add optional templatable labels to alert routes. #5328 +* [FEATURE] eventrecorder: Add inhibit rule names to `inhibition_muted_alert` events. #5315 +* [FEATURE] eventrecorder: Add stdout output type. #5311 +* [FEATURE] silences: Add active, expired, and pending boolean filter parameters to GET /api/v2/silences to allow filtering silences by state server-side. #5406 +* [FEATURE] sns: Add aws `external_id` support in sigv4 configuration. #5157 +* [FEATURE] template: Add `toDate` and `mustToDate` functions. #5327 +* [ENHANCEMENT] doc: Add AlertmanagerClusterFailedPeers alertmanager-mixin. #5301 +* [ENHANCEMENT] doc: Add description for Rocketchat parameters in global config. #5181 +* [ENHANCEMENT] doc: Add top level tracing configuration key. #5314 +* [ENHANCEMENT] doc: Fix Alertmanager port in amtool config routes example. #5312 +* [ENHANCEMENT] eventrecorder: Add optional webhook batching. #5392 +* [ENHANCEMENT] notify: The discord and webex integrations now report a failure `reason` on `alertmanager_notifications_failed_total`. #5332 +* [ENHANCEMENT] ui: Improve responsiveness of UI when loading thousands of alerts. #5357 +* [BUGFIX] eventrecorder: Fixed a minor performance regression when the event recorder is enabled. #5307 +* [BUGFIX] msteamsv2: Inherit global `proxy_url` into partial `http_config`. #5379 +* [BUGFIX] webhook: Keep custom `payload` string values verbatim instead of reinterpreting JSON leaves that look like YAML (e.g. values ending with a colon). #5304 ## 0.33.1 / 2026-07-04 diff --git a/VERSION b/VERSION index 8df3f4592f..85e60ed180 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.33.1 +0.34.0 From d03471ceec2fb3c1e81c57d929c52d3285d7129a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 3 Sep 2026 10:10:10 +0000 Subject: [PATCH 118/120] [bot] remove unused upstream configs Signed-off-by: github-actions[bot] --- .github/workflows/approve-workflows.yml | 27 ---------------------- .github/workflows/build-release-branch.yml | 27 ---------------------- 2 files changed, 54 deletions(-) delete mode 100644 .github/workflows/approve-workflows.yml delete mode 100644 .github/workflows/build-release-branch.yml diff --git a/.github/workflows/approve-workflows.yml b/.github/workflows/approve-workflows.yml deleted file mode 100644 index f372012f2b..0000000000 --- a/.github/workflows/approve-workflows.yml +++ /dev/null @@ -1,27 +0,0 @@ ---- -### -# This action is synced from https://github.com/prometheus/prometheus -### -name: Approve pending workflows - -on: - issue_comment: - types: [created] - -permissions: read-all - -jobs: - approve: - if: >- - github.event.issue.pull_request && - github.event.comment.body == '/workflow-approve' && - (github.repository_owner == 'prometheus' || github.repository_owner == 'prometheus-community') - runs-on: ubuntu-latest - permissions: - actions: write - contents: read - pull-requests: write - steps: - - uses: prometheus/promci/approve_workflows@370e8c15dcec50043cbe66f2f34633d9efc0a190 # v0.9.0 - with: - github_token: ${{ github.token }} diff --git a/.github/workflows/build-release-branch.yml b/.github/workflows/build-release-branch.yml deleted file mode 100644 index c1795bbd2e..0000000000 --- a/.github/workflows/build-release-branch.yml +++ /dev/null @@ -1,27 +0,0 @@ ---- -name: Build release branch -on: # yamllint disable-line rule:truthy - push: - branches: - - release-* - workflow_dispatch: -permissions: - contents: read - -jobs: - ci: - name: Run ci - uses: ./.github/workflows/ci.yml - - build: - name: Build Alertmanager for all architectures - runs-on: ubuntu-latest - strategy: - matrix: - thread: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] - needs: ci - steps: - - uses: prometheus/promci/build@370e8c15dcec50043cbe66f2f34633d9efc0a190 # v0.9.0 - with: - parallelism: 12 - thread: ${{ matrix.thread }} From 4c80d548445dd3c2928cea5fa5ed40b4cb28bceb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 3 Sep 2026 10:10:12 +0000 Subject: [PATCH 119/120] [bot] vendor: revendor Signed-off-by: github-actions[bot] --- vendor/connectrpc.com/connect/.gitignore | 6 + vendor/connectrpc.com/connect/.golangci.yml | 173 ++ vendor/connectrpc.com/connect/LICENSE | 201 ++ vendor/connectrpc.com/connect/MAINTAINERS.md | 13 + vendor/connectrpc.com/connect/Makefile | 122 + vendor/connectrpc.com/connect/README.md | 184 ++ vendor/connectrpc.com/connect/RELEASE.md | 44 + vendor/connectrpc.com/connect/SECURITY.md | 5 + vendor/connectrpc.com/connect/buf.gen.yaml | 19 + vendor/connectrpc.com/connect/buf.yaml | 14 + vendor/connectrpc.com/connect/buffer_pool.go | 54 + vendor/connectrpc.com/connect/client.go | 392 +++ .../connectrpc.com/connect/client_stream.go | 453 +++ vendor/connectrpc.com/connect/code.go | 226 ++ vendor/connectrpc.com/connect/codec.go | 259 ++ vendor/connectrpc.com/connect/compression.go | 224 ++ vendor/connectrpc.com/connect/connect.go | 499 +++ vendor/connectrpc.com/connect/context.go | 243 ++ .../connect/duplex_http_call.go | 481 +++ vendor/connectrpc.com/connect/envelope.go | 387 +++ vendor/connectrpc.com/connect/error.go | 471 +++ vendor/connectrpc.com/connect/error_writer.go | 179 ++ vendor/connectrpc.com/connect/handler.go | 427 +++ .../connectrpc.com/connect/handler_stream.go | 205 ++ vendor/connectrpc.com/connect/header.go | 128 + .../connect/idempotency_level.go | 68 + vendor/connectrpc.com/connect/interceptor.go | 140 + .../connectext/grpc/status/v1/status.pb.go | 165 + vendor/connectrpc.com/connect/option.go | 647 ++++ .../connectrpc.com/connect/protobuf_util.go | 42 + vendor/connectrpc.com/connect/protocol.go | 424 +++ .../connect/protocol_connect.go | 1461 +++++++++ .../connectrpc.com/connect/protocol_grpc.go | 1010 ++++++ vendor/connectrpc.com/connect/recover.go | 64 + .../aws/aws-sdk-go-v2/aws/config.go | 12 + .../aws-sdk-go-v2/aws/go_module_metadata.go | 2 +- .../aws/middleware/middleware.go | 47 +- .../aws/restrict_file_permissions.go | 21 + .../aws-sdk-go-v2/aws/retry/jitter_backoff.go | 77 +- .../aws/aws-sdk-go-v2/aws/retry/middleware.go | 137 +- .../aws/aws-sdk-go-v2/aws/retry/retry.go | 13 + .../aws/aws-sdk-go-v2/aws/retry/standard.go | 108 +- .../aws/aws-sdk-go-v2/config/CHANGELOG.md | 69 + .../aws/aws-sdk-go-v2/config/config.go | 4 + .../aws/aws-sdk-go-v2/config/env_config.go | 50 + .../config/go_module_metadata.go | 2 +- .../aws/aws-sdk-go-v2/config/load_options.go | 39 + .../aws/aws-sdk-go-v2/config/provider.go | 33 + .../aws/aws-sdk-go-v2/config/resolve.go | 26 + .../config/resolve_credentials.go | 1 + .../aws/aws-sdk-go-v2/config/shared_config.go | 16 + .../aws-sdk-go-v2/credentials/CHANGELOG.md | 69 + .../credentials/go_module_metadata.go | 2 +- .../credentials/logincreds/file.go | 4 +- .../credentials/logincreds/provider.go | 14 +- .../feature/ec2/imds/CHANGELOG.md | 43 + .../feature/ec2/imds/go_module_metadata.go | 2 +- .../smithy/v4signer_adapter_eventstream.go | 51 + .../internal/configsources/CHANGELOG.md | 43 + .../configsources/go_module_metadata.go | 2 +- .../aws-sdk-go-v2/internal/context/context.go | 13 + .../internal/endpoints/v2/CHANGELOG.md | 43 + .../endpoints/v2/go_module_metadata.go | 2 +- .../aws-sdk-go-v2/internal/v4a/CHANGELOG.md | 43 + .../internal/v4a/go_module_metadata.go | 2 +- .../internal/accept-encoding/CHANGELOG.md | 20 + .../accept-encoding/go_module_metadata.go | 2 +- .../internal/presigned-url/CHANGELOG.md | 43 + .../presigned-url/go_module_metadata.go | 2 +- .../aws-sdk-go-v2/service/signin/CHANGELOG.md | 66 + .../service/signin/api_client.go | 101 +- .../signin/api_op_CreateOAuth2Token.go | 59 +- .../signin/api_op_CreateOAuth2TokenWithIAM.go | 134 + ...DeleteConsoleAuthorizationConfiguration.go | 119 + ...pi_op_DeleteResourcePermissionStatement.go | 148 + ...op_GetConsoleAuthorizationConfiguration.go | 119 + .../signin/api_op_GetResourcePolicy.go | 106 + .../api_op_IntrospectOAuth2TokenWithIAM.go | 184 ++ ...api_op_ListResourcePermissionStatements.go | 213 ++ ...op_PutConsoleAuthorizationConfiguration.go | 119 + .../api_op_PutResourcePermissionStatement.go | 168 + .../signin/api_op_RevokeOAuth2TokenWithIAM.go | 122 + .../aws/aws-sdk-go-v2/service/signin/auth.go | 12 +- .../service/signin/deserializers.go | 2605 +++++++++++++++- .../aws-sdk-go-v2/service/signin/endpoints.go | 694 +++-- .../service/signin/generated.json | 12 + .../service/signin/go_module_metadata.go | 2 +- .../aws-sdk-go-v2/service/signin/options.go | 9 + .../service/signin/serializers.go | 842 +++++ .../service/signin/types/enums.go | 9 + .../service/signin/types/errors.go | 96 + .../service/signin/types/types.go | 51 +- .../service/signin/validators.go | 159 + .../aws-sdk-go-v2/service/sns/CHANGELOG.md | 58 + .../aws-sdk-go-v2/service/sns/api_client.go | 85 +- .../service/sns/api_op_AddPermission.go | 53 +- .../api_op_CheckIfPhoneNumberIsOptedOut.go | 53 +- .../service/sns/api_op_ConfirmSubscription.go | 53 +- .../sns/api_op_CreatePlatformApplication.go | 53 +- .../sns/api_op_CreatePlatformEndpoint.go | 53 +- .../sns/api_op_CreateSMSSandboxPhoneNumber.go | 53 +- .../service/sns/api_op_CreateTopic.go | 53 +- .../service/sns/api_op_DeleteEndpoint.go | 53 +- .../sns/api_op_DeletePlatformApplication.go | 53 +- .../sns/api_op_DeleteSMSSandboxPhoneNumber.go | 53 +- .../service/sns/api_op_DeleteTopic.go | 53 +- .../sns/api_op_GetDataProtectionPolicy.go | 53 +- .../sns/api_op_GetEndpointAttributes.go | 53 +- ...api_op_GetPlatformApplicationAttributes.go | 53 +- .../service/sns/api_op_GetSMSAttributes.go | 53 +- .../sns/api_op_GetSMSSandboxAccountStatus.go | 53 +- .../sns/api_op_GetSubscriptionAttributes.go | 53 +- .../service/sns/api_op_GetTopicAttributes.go | 53 +- ...i_op_ListEndpointsByPlatformApplication.go | 52 +- .../sns/api_op_ListOriginationNumbers.go | 52 +- .../sns/api_op_ListPhoneNumbersOptedOut.go | 52 +- .../sns/api_op_ListPlatformApplications.go | 52 +- .../sns/api_op_ListSMSSandboxPhoneNumbers.go | 52 +- .../service/sns/api_op_ListSubscriptions.go | 52 +- .../sns/api_op_ListSubscriptionsByTopic.go | 52 +- .../service/sns/api_op_ListTagsForResource.go | 53 +- .../service/sns/api_op_ListTopics.go | 52 +- .../service/sns/api_op_OptInPhoneNumber.go | 53 +- .../service/sns/api_op_Publish.go | 53 +- .../service/sns/api_op_PublishBatch.go | 53 +- .../sns/api_op_PutDataProtectionPolicy.go | 53 +- .../service/sns/api_op_RemovePermission.go | 53 +- .../sns/api_op_SetEndpointAttributes.go | 53 +- ...api_op_SetPlatformApplicationAttributes.go | 53 +- .../service/sns/api_op_SetSMSAttributes.go | 53 +- .../sns/api_op_SetSubscriptionAttributes.go | 53 +- .../service/sns/api_op_SetTopicAttributes.go | 53 +- .../service/sns/api_op_Subscribe.go | 53 +- .../service/sns/api_op_TagResource.go | 53 +- .../service/sns/api_op_Unsubscribe.go | 53 +- .../service/sns/api_op_UntagResource.go | 53 +- .../sns/api_op_VerifySMSSandboxPhoneNumber.go | 53 +- .../aws/aws-sdk-go-v2/service/sns/auth.go | 12 +- .../service/sns/deserializers.go | 23 +- .../aws-sdk-go-v2/service/sns/endpoints.go | 320 +- .../aws-sdk-go-v2/service/sns/generated.json | 2 + .../service/sns/go_module_metadata.go | 2 +- .../aws/aws-sdk-go-v2/service/sns/options.go | 5 + .../aws-sdk-go-v2/service/sns/types/errors.go | 2 +- .../aws-sdk-go-v2/service/sso/CHANGELOG.md | 58 + .../aws-sdk-go-v2/service/sso/api_client.go | 85 +- .../service/sso/api_op_GetRoleCredentials.go | 53 +- .../service/sso/api_op_ListAccountRoles.go | 52 +- .../service/sso/api_op_ListAccounts.go | 52 +- .../service/sso/api_op_Logout.go | 53 +- .../aws/aws-sdk-go-v2/service/sso/auth.go | 12 +- .../service/sso/deserializers.go | 3 +- .../aws-sdk-go-v2/service/sso/endpoints.go | 307 +- .../aws-sdk-go-v2/service/sso/generated.json | 2 + .../service/sso/go_module_metadata.go | 2 +- .../aws/aws-sdk-go-v2/service/sso/options.go | 5 + .../service/ssooidc/CHANGELOG.md | 61 + .../service/ssooidc/api_client.go | 85 +- .../service/ssooidc/api_op_CreateToken.go | 53 +- .../ssooidc/api_op_CreateTokenWithIAM.go | 53 +- .../service/ssooidc/api_op_RegisterClient.go | 53 +- .../api_op_StartDeviceAuthorization.go | 53 +- .../aws/aws-sdk-go-v2/service/ssooidc/auth.go | 12 +- .../service/ssooidc/endpoints.go | 307 +- .../service/ssooidc/generated.json | 2 + .../service/ssooidc/go_module_metadata.go | 2 +- .../aws-sdk-go-v2/service/ssooidc/options.go | 5 + .../aws-sdk-go-v2/service/sts/CHANGELOG.md | 58 + .../aws-sdk-go-v2/service/sts/api_client.go | 91 +- .../service/sts/api_op_AssumeRole.go | 53 +- .../service/sts/api_op_AssumeRoleWithSAML.go | 53 +- .../sts/api_op_AssumeRoleWithWebIdentity.go | 53 +- .../service/sts/api_op_AssumeRoot.go | 53 +- .../sts/api_op_DecodeAuthorizationMessage.go | 53 +- .../service/sts/api_op_GetAccessKeyInfo.go | 53 +- .../service/sts/api_op_GetCallerIdentity.go | 53 +- .../sts/api_op_GetDelegatedAccessToken.go | 53 +- .../service/sts/api_op_GetFederationToken.go | 53 +- .../service/sts/api_op_GetSessionToken.go | 53 +- .../service/sts/api_op_GetWebIdentityToken.go | 53 +- .../aws/aws-sdk-go-v2/service/sts/auth.go | 12 +- .../aws-sdk-go-v2/service/sts/endpoints.go | 965 ++---- .../aws-sdk-go-v2/service/sts/generated.json | 2 + .../service/sts/go_module_metadata.go | 2 +- .../aws/aws-sdk-go-v2/service/sts/options.go | 5 + vendor/github.com/aws/smithy-go/AGENTS.md | 5 +- vendor/github.com/aws/smithy-go/CHANGELOG.md | 70 + vendor/github.com/aws/smithy-go/README.md | 39 +- .../aws/smithy-go/document/document.go | 124 +- .../aws/smithy-go/encoding/json/value.go | 5 + .../endpoints/private/bdd/evaluate.go | 35 + .../endpoints/private/rulesfn/string_slice.go | 18 + .../endpoints/private/rulesfn/uri.go | 3 + .../aws/smithy-go/eventstream/const.go | 24 + .../aws/smithy-go/eventstream/debug.go | 144 + .../aws/smithy-go/eventstream/decode.go | 218 ++ .../aws/smithy-go/eventstream/deserializer.go | 294 ++ .../aws/smithy-go/eventstream/encode.go | 167 + .../aws/smithy-go/eventstream/error.go | 23 + .../aws/smithy-go/eventstream/header.go | 175 ++ .../aws/smithy-go/eventstream/header_value.go | 521 ++++ .../aws/smithy-go/eventstream/message.go | 99 + .../aws/smithy-go/eventstream/serializer.go | 228 ++ .../aws/smithy-go/eventstream/signer.go | 82 + .../aws/smithy-go/eventstream/types.go | 26 + .../aws/smithy-go/go_module_metadata.go | 2 +- vendor/github.com/aws/smithy-go/schema.go | 328 ++ vendor/github.com/aws/smithy-go/schema_ext.go | 37 + vendor/github.com/aws/smithy-go/serde.go | 229 ++ vendor/github.com/aws/smithy-go/sync/error.go | 53 + vendor/github.com/aws/smithy-go/trait.go | 21 + .../github.com/aws/smithy-go/traits/http.go | 69 + .../github.com/aws/smithy-go/traits/index.go | 107 + .../github.com/aws/smithy-go/traits/serde.go | 56 + .../github.com/aws/smithy-go/traits/traits.go | 72 + .../aws/smithy-go/transport/http/auth.go | 9 + .../smithy-go/transport/http/eventstream.go | 209 ++ .../transport/http/eventstream_middleware.go | 69 + .../aws/smithy-go/transport/http/host.go | 2 +- .../aws/smithy-go/transport/http/protocol.go | 27 + .../github.com/aws/smithy-go/type_registry.go | 70 + .../github.com/cenkalti/backoff/v4/.gitignore | 25 - vendor/github.com/cenkalti/backoff/v4/LICENSE | 20 - .../github.com/cenkalti/backoff/v4/README.md | 30 - .../github.com/cenkalti/backoff/v4/backoff.go | 66 - .../github.com/cenkalti/backoff/v4/context.go | 62 - .../cenkalti/backoff/v4/exponential.go | 216 -- .../github.com/cenkalti/backoff/v4/retry.go | 146 - .../github.com/cenkalti/backoff/v4/ticker.go | 97 - .../github.com/cenkalti/backoff/v4/timer.go | 35 - .../github.com/cenkalti/backoff/v4/tries.go | 38 - vendor/github.com/coder/quartz/.gitignore | 1 - vendor/github.com/coder/quartz/LICENSE | 18 - vendor/github.com/coder/quartz/README.md | 632 ---- vendor/github.com/coder/quartz/clock.go | 43 - vendor/github.com/coder/quartz/mock.go | 851 ------ vendor/github.com/coder/quartz/real.go | 80 - vendor/github.com/coder/quartz/ticker.go | 151 - vendor/github.com/coder/quartz/timer.go | 118 - .../github.com/fsnotify/fsnotify/CHANGELOG.md | 15 + vendor/github.com/fsnotify/fsnotify/README.md | 32 + .../fsnotify/fsnotify/backend_inotify.go | 11 +- .../fsnotify/fsnotify/backend_windows.go | 9 +- .../github.com/fsnotify/fsnotify/fsnotify.go | 34 +- .../github.com/go-logr/logr/context_noslog.go | 1 - .../github.com/go-logr/logr/context_slog.go | 1 - vendor/github.com/go-logr/logr/funcr/funcr.go | 47 +- .../github.com/go-logr/logr/funcr/slogsink.go | 22 +- vendor/github.com/go-logr/logr/sloghandler.go | 1 - vendor/github.com/go-logr/logr/slogr.go | 1 - vendor/github.com/go-logr/logr/slogsink.go | 1 - .../github.com/go-openapi/analysis/.gitignore | 2 + .../go-openapi/analysis/.golangci.yml | 3 + .../go-openapi/analysis/CONTRIBUTORS.md | 29 +- .../github.com/go-openapi/analysis/README.md | 19 +- .../go-openapi/analysis/analyzer.go | 41 +- .../github.com/go-openapi/analysis/flatten.go | 9 +- .../go-openapi/analysis/flatten_name.go | 4 +- .../go-openapi/analysis/flatten_options.go | 33 +- .../go-openapi/analysis/go.work.sum | 47 - .../github.com/go-openapi/analysis/mixin.go | 78 +- .../github.com/go-openapi/analysis/options.go | 21 + .../github.com/go-openapi/analysis/schema.go | 66 +- .../github.com/go-openapi/errors/.gitignore | 1 - .../go-openapi/errors/CONTRIBUTORS.md | 6 +- vendor/github.com/go-openapi/errors/README.md | 2 +- .../go-openapi/jsonpointer/.cliff.toml | 181 -- .../go-openapi/jsonpointer/.gitignore | 1 - .../go-openapi/jsonpointer/.golangci.yml | 3 + .../go-openapi/jsonpointer/CONTRIBUTORS.md | 7 +- .../github.com/go-openapi/jsonpointer/NOTICE | 2 +- .../go-openapi/jsonpointer/README.md | 63 +- .../go-openapi/jsonpointer/errors.go | 27 +- .../go-openapi/jsonpointer/ifaces.go | 50 + .../{swag => jsonpointer}/jsonname/doc.go | 0 .../jsonname/go_name_provider.go | 36 +- .../{swag => jsonpointer}/jsonname/ifaces.go | 8 +- .../jsonname/name_provider.go | 24 +- .../go-openapi/jsonpointer/options.go | 85 + .../go-openapi/jsonpointer/pointer.go | 358 ++- .../go-openapi/jsonreference/.gitignore | 1 - .../go-openapi/jsonreference/CONTRIBUTORS.md | 22 +- .../go-openapi/jsonreference/README.md | 24 +- vendor/github.com/go-openapi/loads/.gitignore | 1 + .../github.com/go-openapi/loads/.golangci.yml | 2 + .../go-openapi/loads/CONTRIBUTORS.md | 6 +- vendor/github.com/go-openapi/loads/README.md | 55 +- vendor/github.com/go-openapi/loads/doc.go | 68 + vendor/github.com/go-openapi/loads/errors.go | 4 + vendor/github.com/go-openapi/loads/loaders.go | 124 +- vendor/github.com/go-openapi/loads/options.go | 22 +- .../github.com/go-openapi/loads/restricted.go | 185 ++ vendor/github.com/go-openapi/loads/spec.go | 16 + .../go-openapi/runtime/.codecov.yml | 9 + .../github.com/go-openapi/runtime/.gitignore | 2 + .../go-openapi/runtime/.golangci.yml | 32 +- .../go-openapi/runtime/CONTRIBUTORS.md | 12 +- .../github.com/go-openapi/runtime/README.md | 74 +- .../go-openapi/runtime/bytestream.go | 16 +- .../go-openapi/runtime/client/httptrace.go | 520 ++++ .../runtime/client/httptrace_tls.go | 353 +++ .../client/internal/request/request.go | 945 ++++++ .../go-openapi/runtime/client/keepalive.go | 6 +- .../runtime/client/opentelemetry.go | 93 +- .../go-openapi/runtime/client/request.go | 468 --- .../go-openapi/runtime/client/runtime.go | 640 ++-- .../go-openapi/runtime/client/tls.go | 197 ++ .../go-openapi/runtime/client_operation.go | 24 +- .../go-openapi/runtime/client_response.go | 2 +- .../go-openapi/runtime/constants.go | 8 +- vendor/github.com/go-openapi/runtime/csv.go | 18 +- vendor/github.com/go-openapi/runtime/file.go | 6 + vendor/github.com/go-openapi/runtime/form.go | 362 +++ vendor/github.com/go-openapi/runtime/go.work | 2 + .../github.com/go-openapi/runtime/go.work.sum | 119 - .../go-openapi/runtime/interfaces.go | 22 + .../go-openapi/runtime/middleware/context.go | 693 +++-- .../middleware/context_skipauth_disabled.go | 24 + .../middleware/context_skipauth_enabled.go | 61 + .../runtime/middleware/denco/router.go | 19 +- .../runtime/middleware/denco/server.go | 26 +- .../runtime/middleware/negotiate.go | 102 - .../runtime/middleware/parameter.go | 304 +- .../go-openapi/runtime/middleware/rapidoc.go | 83 - .../go-openapi/runtime/middleware/redoc.go | 97 - .../go-openapi/runtime/middleware/request.go | 26 +- .../go-openapi/runtime/middleware/router.go | 119 +- .../go-openapi/runtime/middleware/seam.go | 482 +++ .../go-openapi/runtime/middleware/spec.go | 91 - .../runtime/middleware/swaggerui.go | 178 -- .../runtime/middleware/typeutils.go | 30 + .../runtime/middleware/ui_options.go | 176 -- .../runtime/middleware/validation.go | 98 +- .../go-openapi/runtime/multipart_stream.go | 556 ++++ .../runtime/security/authenticator.go | 47 +- .../server-middleware}/LICENSE | 0 .../runtime/server-middleware/docui/doc.go | 12 + .../server-middleware/docui/options.go | 253 ++ .../server-middleware/docui/rapidoc.go | 67 + .../runtime/server-middleware/docui/redoc.go | 82 + .../runtime/server-middleware/docui/render.go | 33 + .../runtime/server-middleware/docui/spec.go | 50 + .../server-middleware/docui/swaggerui.go | 138 + .../docui}/swaggerui_oauth2.go | 47 +- .../server-middleware/mediatype/doc.go | 30 + .../server-middleware/mediatype/lookup.go | 116 + .../server-middleware/mediatype/match.go | 65 + .../server-middleware/mediatype/mediatype.go | 392 +++ .../server-middleware/mediatype/set.go | 138 + .../server-middleware/negotiate/doc.go | 13 + .../negotiate}/header/header.go | 8 +- .../server-middleware/negotiate/negotiate.go | 215 ++ .../github.com/go-openapi/runtime/statuses.go | 2 +- vendor/github.com/go-openapi/runtime/text.go | 6 +- .../go-openapi/runtime/yamlpc/yaml.go | 3 +- vendor/github.com/go-openapi/spec/.gitignore | 1 - .../github.com/go-openapi/spec/.golangci.yml | 3 + .../go-openapi/spec/CONTRIBUTORS.md | 6 +- vendor/github.com/go-openapi/spec/README.md | 18 +- vendor/github.com/go-openapi/spec/doc.go | 29 + vendor/github.com/go-openapi/spec/errors.go | 7 + vendor/github.com/go-openapi/spec/expander.go | 176 +- vendor/github.com/go-openapi/spec/header.go | 6 +- vendor/github.com/go-openapi/spec/ref.go | 22 +- vendor/github.com/go-openapi/spec/schema.go | 2 +- .../go-openapi/spec/schema_loader.go | 49 +- .../github.com/go-openapi/strfmt/.gitignore | 1 + .../go-openapi/strfmt/.golangci.yml | 3 + .../go-openapi/strfmt/CONTRIBUTORS.md | 5 +- vendor/github.com/go-openapi/strfmt/README.md | 18 +- vendor/github.com/go-openapi/strfmt/bson.go | 5 - .../github.com/go-openapi/strfmt/country.go | 173 ++ .../github.com/go-openapi/strfmt/currency.go | 146 + vendor/github.com/go-openapi/strfmt/date.go | 5 - .../github.com/go-openapi/strfmt/default.go | 159 +- .../github.com/go-openapi/strfmt/duration.go | 8 +- .../go-openapi/strfmt/duration_iso8601.go | 583 ++++ .../strfmt/duration_iso8601_options.go | 80 + vendor/github.com/go-openapi/strfmt/format.go | 120 +- .../strfmt/internal/countries/countries.go | 512 ++++ .../strfmt/internal/countries/country.go | 15 + .../strfmt/internal/countries/iso3166.json | 251 ++ vendor/github.com/go-openapi/strfmt/mongo.go | 85 +- .../github.com/go-openapi/strfmt/register.go | 138 + vendor/github.com/go-openapi/strfmt/time.go | 5 - vendor/github.com/go-openapi/strfmt/ulid.go | 5 - vendor/github.com/go-openapi/swag/.gitignore | 1 + .../github.com/go-openapi/swag/.golangci.yml | 3 + .../go-openapi/swag/CONTRIBUTORS.md | 4 +- vendor/github.com/go-openapi/swag/README.md | 19 +- .../github.com/go-openapi/swag/conv/format.go | 26 +- vendor/github.com/go-openapi/swag/go.work | 1 + .../go-openapi/swag/jsonname_iface.go | 8 +- .../jsonutils/adapters/stdlib/json/adapter.go | 86 +- .../jsonutils/adapters/stdlib/json/lexer.go | 42 +- .../jsonutils/adapters/stdlib/json/options.go | 52 + .../adapters/stdlib/json/ordered_map.go | 49 +- .../jsonutils/adapters/stdlib/json/pool.go | 121 +- .../adapters/stdlib/json/register.go | 18 +- .../jsonutils/adapters/stdlib/json/writer.go | 27 +- .../github.com/go-openapi/swag/loading/doc.go | 24 + .../go-openapi/swag/loading/loading.go | 38 +- .../go-openapi/swag/loading/options.go | 114 +- .../go-openapi/swag/loading_iface.go | 6 +- .../github.com/go-openapi/swag/pools/LICENSE | 202 ++ .../go-openapi/swag/pools/README.md | 1 + .../github.com/go-openapi/swag/pools/debug.go | 17 + .../go-openapi/swag/pools/debug_off.go | 51 + .../go-openapi/swag/pools/debug_on.go | 237 ++ .../github.com/go-openapi/swag/pools/doc.go | 26 + .../github.com/go-openapi/swag/pools/pools.go | 410 +++ .../go-openapi/swag/yamlutils/ordered_map.go | 35 +- .../go-openapi/swag/yamlutils/yaml.go | 151 +- .../go-openapi/validate/CONTRIBUTORS.md | 6 +- .../github.com/go-openapi/validate/README.md | 18 +- .../github.com/go-openapi/validate/helpers.go | 8 +- .../github.com/go-openapi/validate/schema.go | 10 +- .../go-openapi/validate/schema_option.go | 37 + vendor/github.com/go-openapi/validate/spec.go | 37 +- .../go-openapi/validate/spec_messages.go | 20 + .../go-openapi/validate/spec_ref_warnings.go | 209 ++ vendor/github.com/golang-jwt/jwt/v5/README.md | 11 +- .../golang-jwt/jwt/v5/VERSION_HISTORY.md | 2 +- vendor/github.com/golang-jwt/jwt/v5/parser.go | 24 +- .../golang-jwt/jwt/v5/parser_option.go | 12 +- vendor/github.com/golang-jwt/jwt/v5/token.go | 6 +- .../github.com/golang-jwt/jwt/v5/validator.go | 8 +- .../grpc-gateway/v2/runtime/mux.go | 18 +- .../github.com/klauspost/compress/README.md | 12 + .../klauspost/compress/huff0/build_table.go | 168 + .../compress/internal/snapref/decode.go | 2 +- .../klauspost/compress/s2/decode.go | 2 +- .../github.com/klauspost/compress/s2/dict.go | 6 +- .../klauspost/compress/s2/encode_all.go | 2 +- .../klauspost/compress/s2/hashtable_pool.go | 6 +- .../klauspost/compress/s2/reader.go | 19 +- .../klauspost/compress/zstd/README.md | 37 +- .../klauspost/compress/zstd/dict.go | 3 +- .../klauspost/compress/zstd/enc_base.go | 28 + .../klauspost/compress/zstd/enc_best.go | 15 + .../klauspost/compress/zstd/enc_better.go | 18 + .../klauspost/compress/zstd/enc_dfast.go | 16 + .../klauspost/compress/zstd/enc_fast.go | 17 + .../klauspost/compress/zstd/enc_jobs.go | 352 +++ .../klauspost/compress/zstd/encoder.go | 210 +- .../compress/zstd/encoder_options.go | 69 +- .../compress/zstd/fse_decoder_amd64.s | 2 +- .../compress/zstd/fse_decoder_arm64.s | 153 + ...se_decoder_amd64.go => fse_decoder_asm.go} | 8 +- .../compress/zstd/fse_decoder_generic.go | 2 +- .../klauspost/compress/zstd/seqdec_amd64.go | 362 +-- .../klauspost/compress/zstd/seqdec_amd64.s | 2 +- .../klauspost/compress/zstd/seqdec_arm64.go | 70 + .../klauspost/compress/zstd/seqdec_arm64.s | 2705 +++++++++++++++++ .../klauspost/compress/zstd/seqdec_asm.go | 289 ++ .../klauspost/compress/zstd/seqdec_generic.go | 2 +- .../klauspost/compress/zstd/snappy.go | 7 +- .../github.com/mdlayher/socket/.golangci.yml | 16 + .../github.com/mdlayher/socket/CHANGELOG.md | 19 + vendor/github.com/mdlayher/socket/accept4.go | 1 - vendor/github.com/mdlayher/socket/conn.go | 106 +- .../github.com/mdlayher/socket/conn_linux.go | 19 +- .../github.com/mdlayher/socket/netns_linux.go | 3 +- .../mdlayher/socket/setbuffer_linux.go | 1 - .../mdlayher/socket/typ_cloexec_nonblock.go | 1 - .../github.com/mdlayher/vsock/.golangci.yml | 21 + vendor/github.com/mdlayher/vsock/CHANGELOG.md | 10 +- .../github.com/mdlayher/vsock/conn_linux.go | 1 - vendor/github.com/mdlayher/vsock/fd_linux.go | 2 +- .../mdlayher/vsock/listener_linux.go | 1 - vendor/github.com/mdlayher/vsock/vsock.go | 3 +- vendor/github.com/oklog/ulid/v2/ulid.go | 11 +- .../golang/gddo/httputil/header/header.go | 2 +- .../prometheus/collectors/version/version.go | 41 +- .../client_golang/prometheus/counter.go | 11 +- .../client_golang/prometheus/desc.go | 37 +- .../prometheus/expvar_collector.go | 8 +- .../client_golang/prometheus/gauge.go | 11 +- .../prometheus/go_collector_go116.go | 122 - .../prometheus/go_collector_latest.go | 20 +- .../client_golang/prometheus/histogram.go | 29 +- .../prometheus/internal/difflib.go | 4 +- .../client_golang/prometheus/labels.go | 3 +- .../client_golang/prometheus/metric.go | 3 + .../prometheus/process_collector_darwin.go | 13 +- .../prometheus/process_collector_windows.go | 19 +- .../client_golang/prometheus/promhttp/http.go | 197 +- .../prometheus/promhttp/instrument_client.go | 12 +- .../prometheus/promhttp/instrument_server.go | 88 +- .../prometheus/promhttp/option.go | 42 +- .../client_golang/prometheus/registry.go | 63 +- .../client_golang/prometheus/summary.go | 9 +- .../client_golang/prometheus/timer.go | 10 +- .../client_golang/prometheus/vec.go | 10 +- .../client_golang/prometheus/wrap.go | 5 +- .../prometheus/common/config/config.go | 4 +- .../prometheus/common/config/http_config.go | 180 +- .../common/config/oauth_assertion.go | 5 +- .../prometheus/common/expfmt/expfmt.go | 4 +- .../common/expfmt/openmetrics_create.go | 26 +- .../prometheus/common/expfmt/text_create.go | 4 +- .../prometheus/common/expfmt/text_parse.go | 10 + .../common/helpers/templates/time.go | 6 +- .../prometheus/common/model/labels.go | 2 +- .../prometheus/common/model/labelset.go | 13 +- .../prometheus/common/model/metric.go | 14 +- .../prometheus/common/model/time.go | 44 +- .../prometheus/common/model/value.go | 8 +- .../prometheus/common/model/value_float.go | 2 +- .../common/model/value_histogram.go | 8 +- .../prometheus/common/promslog/slog.go | 2 +- .../prometheus/common/route/route.go | 10 + .../prometheus/common/version/info.go | 18 + .../prometheus/exporter-toolkit/web/cache.go | 5 +- .../exporter-toolkit/web/landing_page.go | 1 - .../exporter-toolkit/web/tls_config.go | 46 +- .../prometheus/procfs/.golangci.yml | 36 +- vendor/github.com/prometheus/procfs/Makefile | 2 +- .../prometheus/procfs/Makefile.common | 200 +- vendor/github.com/prometheus/procfs/README.md | 2 +- .../github.com/prometheus/procfs/SECURITY.md | 2 +- vendor/github.com/prometheus/procfs/arp.go | 9 +- .../github.com/prometheus/procfs/buddyinfo.go | 10 +- .../github.com/prometheus/procfs/cmdline.go | 2 +- .../github.com/prometheus/procfs/cpuinfo.go | 5 +- .../prometheus/procfs/cpuinfo_armx.go | 4 +- .../prometheus/procfs/cpuinfo_loong64.go | 3 +- .../prometheus/procfs/cpuinfo_mipsx.go | 4 +- .../prometheus/procfs/cpuinfo_others.go | 3 +- .../prometheus/procfs/cpuinfo_ppcx.go | 4 +- .../prometheus/procfs/cpuinfo_riscvx.go | 4 +- .../prometheus/procfs/cpuinfo_s390x.go | 3 +- .../prometheus/procfs/cpuinfo_x86.go | 4 +- vendor/github.com/prometheus/procfs/crypto.go | 10 +- vendor/github.com/prometheus/procfs/doc.go | 2 +- vendor/github.com/prometheus/procfs/fs.go | 2 +- .../prometheus/procfs/fs_statfs_notype.go | 3 +- .../prometheus/procfs/fs_statfs_type.go | 3 +- .../github.com/prometheus/procfs/fscache.go | 9 +- .../prometheus/procfs/internal/fs/fs.go | 2 +- .../prometheus/procfs/internal/util/parse.go | 2 +- .../procfs/internal/util/readfile.go | 2 +- .../procfs/internal/util/sysreadfile.go | 4 +- .../internal/util/sysreadfile_compat.go | 3 +- .../procfs/internal/util/valueparser.go | 2 +- vendor/github.com/prometheus/procfs/ipvs.go | 2 +- .../prometheus/procfs/kernel_hung.go | 44 + .../prometheus/procfs/kernel_random.go | 3 +- .../github.com/prometheus/procfs/loadavg.go | 2 +- vendor/github.com/prometheus/procfs/mdstat.go | 121 +- .../github.com/prometheus/procfs/meminfo.go | 35 +- .../github.com/prometheus/procfs/mountinfo.go | 41 +- .../prometheus/procfs/mountstats.go | 4 +- .../prometheus/procfs/net_conntrackstat.go | 2 +- .../github.com/prometheus/procfs/net_dev.go | 2 +- .../prometheus/procfs/net_dev_snmp6.go | 7 +- .../prometheus/procfs/net_ip_socket.go | 2 +- .../prometheus/procfs/net_protocols.go | 4 +- .../github.com/prometheus/procfs/net_route.go | 2 +- .../prometheus/procfs/net_sockstat.go | 5 +- .../prometheus/procfs/net_softnet.go | 2 +- .../github.com/prometheus/procfs/net_tcp.go | 6 +- .../prometheus/procfs/net_tls_stat.go | 2 +- .../github.com/prometheus/procfs/net_udp.go | 2 +- .../github.com/prometheus/procfs/net_unix.go | 2 +- .../prometheus/procfs/net_wireless.go | 20 +- .../github.com/prometheus/procfs/net_xfrm.go | 2 +- .../github.com/prometheus/procfs/netstat.go | 2 +- .../prometheus/procfs/nfnetlink_queue.go | 85 + vendor/github.com/prometheus/procfs/proc.go | 4 +- .../prometheus/procfs/proc_cgroup.go | 4 +- .../prometheus/procfs/proc_cgroups.go | 8 +- .../prometheus/procfs/proc_environ.go | 2 +- .../prometheus/procfs/proc_fdinfo.go | 13 +- .../prometheus/procfs/proc_interrupts.go | 4 +- .../github.com/prometheus/procfs/proc_io.go | 2 +- .../prometheus/procfs/proc_limits.go | 7 +- .../github.com/prometheus/procfs/proc_maps.go | 4 +- .../prometheus/procfs/proc_netstat.go | 2 +- .../github.com/prometheus/procfs/proc_ns.go | 2 +- .../github.com/prometheus/procfs/proc_psi.go | 2 +- .../prometheus/procfs/proc_smaps.go | 3 +- .../github.com/prometheus/procfs/proc_snmp.go | 2 +- .../prometheus/procfs/proc_snmp6.go | 2 +- .../github.com/prometheus/procfs/proc_stat.go | 14 +- .../prometheus/procfs/proc_statm.go | 117 + .../prometheus/procfs/proc_status.go | 52 +- .../github.com/prometheus/procfs/proc_sys.go | 2 +- .../github.com/prometheus/procfs/schedstat.go | 2 +- vendor/github.com/prometheus/procfs/slab.go | 2 +- .../github.com/prometheus/procfs/softirqs.go | 2 +- vendor/github.com/prometheus/procfs/stat.go | 5 +- vendor/github.com/prometheus/procfs/swaps.go | 2 +- vendor/github.com/prometheus/procfs/thread.go | 2 +- vendor/github.com/prometheus/procfs/vm.go | 3 +- .../github.com/prometheus/procfs/zoneinfo.go | 9 +- .../franz-go/pkg/kgo/atomic_maybe_work.go | 12 + .../twmb/franz-go/pkg/kgo/broker.go | 366 ++- .../twmb/franz-go/pkg/kgo/client.go | 331 +- .../twmb/franz-go/pkg/kgo/compression.go | 72 +- .../twmb/franz-go/pkg/kgo/config.go | 96 +- .../twmb/franz-go/pkg/kgo/consumer.go | 138 +- .../twmb/franz-go/pkg/kgo/consumer_group.go | 406 ++- .../franz-go/pkg/kgo/consumer_group_848.go | 196 +- .../twmb/franz-go/pkg/kgo/consumer_share.go | 253 +- .../twmb/franz-go/pkg/kgo/errors.go | 30 +- .../twmb/franz-go/pkg/kgo/group_balancer.go | 90 +- .../github.com/twmb/franz-go/pkg/kgo/hooks.go | 8 +- .../franz-go/pkg/kgo/internal/sticky/graph.go | 7 +- .../pkg/kgo/internal/sticky/sticky.go | 38 +- .../twmb/franz-go/pkg/kgo/logger.go | 16 +- .../twmb/franz-go/pkg/kgo/metadata.go | 62 +- .../twmb/franz-go/pkg/kgo/metrics_714.go | 135 +- .../twmb/franz-go/pkg/kgo/partitioner.go | 51 +- .../github.com/twmb/franz-go/pkg/kgo/pools.go | 17 +- .../twmb/franz-go/pkg/kgo/producer.go | 307 +- .../twmb/franz-go/pkg/kgo/record_and_fetch.go | 13 +- .../twmb/franz-go/pkg/kgo/record_formatter.go | 72 +- .../github.com/twmb/franz-go/pkg/kgo/ring.go | 34 +- .../github.com/twmb/franz-go/pkg/kgo/sink.go | 218 +- .../twmb/franz-go/pkg/kgo/source.go | 175 +- .../franz-go/pkg/kgo/topics_and_partitions.go | 62 +- .../github.com/twmb/franz-go/pkg/kgo/txn.go | 208 +- .../httptrace/otelhttptrace/clienttrace.go | 33 +- .../otelhttptrace/internal/semconv/client.go | 45 +- .../otelhttptrace/internal/semconv/server.go | 39 +- .../otelhttptrace/internal/semconv/util.go | 2 +- .../http/httptrace/otelhttptrace/version.go | 2 +- .../net/http/otelhttp/handler.go | 17 +- .../http/otelhttp/internal/semconv/client.go | 45 +- .../http/otelhttp/internal/semconv/server.go | 39 +- .../http/otelhttp/internal/semconv/util.go | 2 +- .../net/http/otelhttp/transport.go | 3 +- .../net/http/otelhttp/version.go | 2 +- vendor/go.opentelemetry.io/otel/.golangci.yml | 18 +- vendor/go.opentelemetry.io/otel/AGENTS.md | 109 + vendor/go.opentelemetry.io/otel/CHANGELOG.md | 97 +- vendor/go.opentelemetry.io/otel/CLAUDE.md | 3 + .../go.opentelemetry.io/otel/CONTRIBUTING.md | 101 +- vendor/go.opentelemetry.io/otel/Makefile | 10 +- .../otel/attribute/encoder.go | 4 +- .../otel/attribute/hash.go | 58 +- .../go.opentelemetry.io/otel/attribute/key.go | 22 + .../go.opentelemetry.io/otel/attribute/kv.go | 10 + .../go.opentelemetry.io/otel/attribute/set.go | 4 +- .../otel/attribute/type_string.go | 6 +- .../otel/attribute/value.go | 742 +++++ .../otel/baggage/baggage.go | 30 +- .../otel/dependencies.Dockerfile | 2 +- .../internal/tracetransform/attribute.go | 18 + .../otlp/otlptrace/otlptracegrpc/client.go | 52 +- .../internal/observ/instrumentation.go | 10 +- .../internal/otlpconfig/options.go | 43 +- .../otlptracegrpc/internal/version.go | 2 +- .../otlp/otlptrace/otlptracegrpc/options.go | 10 + .../otlp/otlptrace/otlptracehttp/client.go | 58 +- .../internal/observ/instrumentation.go | 18 +- .../internal/otlpconfig/options.go | 43 +- .../otlptracehttp/internal/version.go | 2 +- .../otlp/otlptrace/otlptracehttp/options.go | 10 + .../otel/exporters/otlp/otlptrace/version.go | 2 +- .../otel/metric/asyncfloat64.go | 9 + .../otel/metric/asyncint64.go | 9 + .../go.opentelemetry.io/otel/metric/config.go | 7 + vendor/go.opentelemetry.io/otel/metric/doc.go | 51 +- .../otel/metric/instrument.go | 39 +- .../otel/metric/syncfloat64.go | 12 + .../otel/metric/syncint64.go | 12 + .../otel/propagation/baggage.go | 72 +- .../otel/sdk/resource/builtin.go | 4 +- .../otel/sdk/resource/container.go | 2 +- .../otel/sdk/resource/env.go | 2 +- .../otel/sdk/resource/host_id.go | 2 +- .../otel/sdk/resource/host_id_exec.go | 7 +- .../otel/sdk/resource/os.go | 2 +- .../otel/sdk/resource/os_unix.go | 3 +- .../otel/sdk/resource/os_windows.go | 6 +- .../otel/sdk/resource/process.go | 5 +- .../otel/sdk/trace/batch_span_processor.go | 10 +- .../internal/observ/batch_span_processor.go | 4 +- .../internal/observ/simple_span_processor.go | 5 +- .../otel/sdk/trace/internal/observ/tracer.go | 2 +- .../otel/sdk/trace/provider.go | 9 +- .../otel/sdk/trace/sampling.go | 5 +- .../otel/sdk/trace/span.go | 97 +- .../otel/sdk/trace/span_limits.go | 6 +- .../go.opentelemetry.io/otel/sdk/version.go | 2 +- .../otel/semconv/v1.37.0/attribute_group.go | 12 +- .../otel/semconv/v1.40.0/README.md | 3 - .../semconv/{v1.40.0 => v1.41.0}/MIGRATION.md | 16 +- .../otel/semconv/v1.41.0/README.md | 3 + .../{v1.40.0 => v1.41.0}/attribute_group.go | 564 +++- .../otel/semconv/{v1.40.0 => v1.41.0}/doc.go | 6 +- .../{v1.40.0 => v1.41.0}/error_type.go | 23 +- .../semconv/{v1.40.0 => v1.41.0}/exception.go | 4 +- .../{v1.40.0 => v1.41.0}/httpconv/metric.go | 369 +++ .../{v1.40.0 => v1.41.0}/otelconv/metric.go | 1042 ++++++- .../semconv/{v1.40.0 => v1.41.0}/schema.go | 6 +- vendor/go.opentelemetry.io/otel/trace/auto.go | 20 +- .../go.opentelemetry.io/otel/trace/config.go | 16 + .../otel/trace/internal/telemetry/span.go | 8 +- vendor/go.opentelemetry.io/otel/version.go | 2 +- vendor/go.opentelemetry.io/otel/versions.yaml | 12 +- vendor/golang.org/x/text/currency/common.go | 67 + vendor/golang.org/x/text/currency/currency.go | 185 ++ vendor/golang.org/x/text/currency/format.go | 220 ++ vendor/golang.org/x/text/currency/query.go | 152 + vendor/golang.org/x/text/currency/tables.go | 2629 ++++++++++++++++ .../x/text/internal/format/format.go | 41 + .../x/text/internal/format/parser.go | 358 +++ .../x/text/internal/number/common.go | 55 + .../x/text/internal/number/decimal.go | 500 +++ .../x/text/internal/number/format.go | 533 ++++ .../x/text/internal/number/number.go | 152 + .../x/text/internal/number/pattern.go | 485 +++ .../internal/number/roundingmode_string.go | 30 + .../x/text/internal/number/tables.go | 1219 ++++++++ .../x/text/internal/stringset/set.go | 86 + vendor/modules.txt | 178 +- 719 files changed, 52830 insertions(+), 12953 deletions(-) create mode 100644 vendor/connectrpc.com/connect/.gitignore create mode 100644 vendor/connectrpc.com/connect/.golangci.yml create mode 100644 vendor/connectrpc.com/connect/LICENSE create mode 100644 vendor/connectrpc.com/connect/MAINTAINERS.md create mode 100644 vendor/connectrpc.com/connect/Makefile create mode 100644 vendor/connectrpc.com/connect/README.md create mode 100644 vendor/connectrpc.com/connect/RELEASE.md create mode 100644 vendor/connectrpc.com/connect/SECURITY.md create mode 100644 vendor/connectrpc.com/connect/buf.gen.yaml create mode 100644 vendor/connectrpc.com/connect/buf.yaml create mode 100644 vendor/connectrpc.com/connect/buffer_pool.go create mode 100644 vendor/connectrpc.com/connect/client.go create mode 100644 vendor/connectrpc.com/connect/client_stream.go create mode 100644 vendor/connectrpc.com/connect/code.go create mode 100644 vendor/connectrpc.com/connect/codec.go create mode 100644 vendor/connectrpc.com/connect/compression.go create mode 100644 vendor/connectrpc.com/connect/connect.go create mode 100644 vendor/connectrpc.com/connect/context.go create mode 100644 vendor/connectrpc.com/connect/duplex_http_call.go create mode 100644 vendor/connectrpc.com/connect/envelope.go create mode 100644 vendor/connectrpc.com/connect/error.go create mode 100644 vendor/connectrpc.com/connect/error_writer.go create mode 100644 vendor/connectrpc.com/connect/handler.go create mode 100644 vendor/connectrpc.com/connect/handler_stream.go create mode 100644 vendor/connectrpc.com/connect/header.go create mode 100644 vendor/connectrpc.com/connect/idempotency_level.go create mode 100644 vendor/connectrpc.com/connect/interceptor.go create mode 100644 vendor/connectrpc.com/connect/internal/gen/connectext/grpc/status/v1/status.pb.go create mode 100644 vendor/connectrpc.com/connect/option.go create mode 100644 vendor/connectrpc.com/connect/protobuf_util.go create mode 100644 vendor/connectrpc.com/connect/protocol.go create mode 100644 vendor/connectrpc.com/connect/protocol_connect.go create mode 100644 vendor/connectrpc.com/connect/protocol_grpc.go create mode 100644 vendor/connectrpc.com/connect/recover.go create mode 100644 vendor/github.com/aws/aws-sdk-go-v2/aws/restrict_file_permissions.go create mode 100644 vendor/github.com/aws/aws-sdk-go-v2/internal/auth/smithy/v4signer_adapter_eventstream.go create mode 100644 vendor/github.com/aws/aws-sdk-go-v2/service/signin/api_op_CreateOAuth2TokenWithIAM.go create mode 100644 vendor/github.com/aws/aws-sdk-go-v2/service/signin/api_op_DeleteConsoleAuthorizationConfiguration.go create mode 100644 vendor/github.com/aws/aws-sdk-go-v2/service/signin/api_op_DeleteResourcePermissionStatement.go create mode 100644 vendor/github.com/aws/aws-sdk-go-v2/service/signin/api_op_GetConsoleAuthorizationConfiguration.go create mode 100644 vendor/github.com/aws/aws-sdk-go-v2/service/signin/api_op_GetResourcePolicy.go create mode 100644 vendor/github.com/aws/aws-sdk-go-v2/service/signin/api_op_IntrospectOAuth2TokenWithIAM.go create mode 100644 vendor/github.com/aws/aws-sdk-go-v2/service/signin/api_op_ListResourcePermissionStatements.go create mode 100644 vendor/github.com/aws/aws-sdk-go-v2/service/signin/api_op_PutConsoleAuthorizationConfiguration.go create mode 100644 vendor/github.com/aws/aws-sdk-go-v2/service/signin/api_op_PutResourcePermissionStatement.go create mode 100644 vendor/github.com/aws/aws-sdk-go-v2/service/signin/api_op_RevokeOAuth2TokenWithIAM.go create mode 100644 vendor/github.com/aws/smithy-go/endpoints/private/bdd/evaluate.go create mode 100644 vendor/github.com/aws/smithy-go/endpoints/private/rulesfn/string_slice.go create mode 100644 vendor/github.com/aws/smithy-go/eventstream/const.go create mode 100644 vendor/github.com/aws/smithy-go/eventstream/debug.go create mode 100644 vendor/github.com/aws/smithy-go/eventstream/decode.go create mode 100644 vendor/github.com/aws/smithy-go/eventstream/deserializer.go create mode 100644 vendor/github.com/aws/smithy-go/eventstream/encode.go create mode 100644 vendor/github.com/aws/smithy-go/eventstream/error.go create mode 100644 vendor/github.com/aws/smithy-go/eventstream/header.go create mode 100644 vendor/github.com/aws/smithy-go/eventstream/header_value.go create mode 100644 vendor/github.com/aws/smithy-go/eventstream/message.go create mode 100644 vendor/github.com/aws/smithy-go/eventstream/serializer.go create mode 100644 vendor/github.com/aws/smithy-go/eventstream/signer.go create mode 100644 vendor/github.com/aws/smithy-go/eventstream/types.go create mode 100644 vendor/github.com/aws/smithy-go/schema.go create mode 100644 vendor/github.com/aws/smithy-go/schema_ext.go create mode 100644 vendor/github.com/aws/smithy-go/serde.go create mode 100644 vendor/github.com/aws/smithy-go/sync/error.go create mode 100644 vendor/github.com/aws/smithy-go/trait.go create mode 100644 vendor/github.com/aws/smithy-go/traits/http.go create mode 100644 vendor/github.com/aws/smithy-go/traits/index.go create mode 100644 vendor/github.com/aws/smithy-go/traits/serde.go create mode 100644 vendor/github.com/aws/smithy-go/traits/traits.go create mode 100644 vendor/github.com/aws/smithy-go/transport/http/eventstream.go create mode 100644 vendor/github.com/aws/smithy-go/transport/http/eventstream_middleware.go create mode 100644 vendor/github.com/aws/smithy-go/transport/http/protocol.go create mode 100644 vendor/github.com/aws/smithy-go/type_registry.go delete mode 100644 vendor/github.com/cenkalti/backoff/v4/.gitignore delete mode 100644 vendor/github.com/cenkalti/backoff/v4/LICENSE delete mode 100644 vendor/github.com/cenkalti/backoff/v4/README.md delete mode 100644 vendor/github.com/cenkalti/backoff/v4/backoff.go delete mode 100644 vendor/github.com/cenkalti/backoff/v4/context.go delete mode 100644 vendor/github.com/cenkalti/backoff/v4/exponential.go delete mode 100644 vendor/github.com/cenkalti/backoff/v4/retry.go delete mode 100644 vendor/github.com/cenkalti/backoff/v4/ticker.go delete mode 100644 vendor/github.com/cenkalti/backoff/v4/timer.go delete mode 100644 vendor/github.com/cenkalti/backoff/v4/tries.go delete mode 100644 vendor/github.com/coder/quartz/.gitignore delete mode 100644 vendor/github.com/coder/quartz/LICENSE delete mode 100644 vendor/github.com/coder/quartz/README.md delete mode 100644 vendor/github.com/coder/quartz/clock.go delete mode 100644 vendor/github.com/coder/quartz/mock.go delete mode 100644 vendor/github.com/coder/quartz/real.go delete mode 100644 vendor/github.com/coder/quartz/ticker.go delete mode 100644 vendor/github.com/coder/quartz/timer.go delete mode 100644 vendor/github.com/go-openapi/analysis/go.work.sum create mode 100644 vendor/github.com/go-openapi/analysis/options.go delete mode 100644 vendor/github.com/go-openapi/jsonpointer/.cliff.toml create mode 100644 vendor/github.com/go-openapi/jsonpointer/ifaces.go rename vendor/github.com/go-openapi/{swag => jsonpointer}/jsonname/doc.go (100%) rename vendor/github.com/go-openapi/{swag => jsonpointer}/jsonname/go_name_provider.go (88%) rename vendor/github.com/go-openapi/{swag => jsonpointer}/jsonname/ifaces.go (77%) rename vendor/github.com/go-openapi/{swag => jsonpointer}/jsonname/name_provider.go (83%) create mode 100644 vendor/github.com/go-openapi/jsonpointer/options.go create mode 100644 vendor/github.com/go-openapi/loads/restricted.go create mode 100644 vendor/github.com/go-openapi/runtime/.codecov.yml create mode 100644 vendor/github.com/go-openapi/runtime/client/httptrace.go create mode 100644 vendor/github.com/go-openapi/runtime/client/httptrace_tls.go create mode 100644 vendor/github.com/go-openapi/runtime/client/internal/request/request.go delete mode 100644 vendor/github.com/go-openapi/runtime/client/request.go create mode 100644 vendor/github.com/go-openapi/runtime/client/tls.go create mode 100644 vendor/github.com/go-openapi/runtime/form.go delete mode 100644 vendor/github.com/go-openapi/runtime/go.work.sum create mode 100644 vendor/github.com/go-openapi/runtime/middleware/context_skipauth_disabled.go create mode 100644 vendor/github.com/go-openapi/runtime/middleware/context_skipauth_enabled.go delete mode 100644 vendor/github.com/go-openapi/runtime/middleware/negotiate.go delete mode 100644 vendor/github.com/go-openapi/runtime/middleware/rapidoc.go delete mode 100644 vendor/github.com/go-openapi/runtime/middleware/redoc.go create mode 100644 vendor/github.com/go-openapi/runtime/middleware/seam.go delete mode 100644 vendor/github.com/go-openapi/runtime/middleware/spec.go delete mode 100644 vendor/github.com/go-openapi/runtime/middleware/swaggerui.go create mode 100644 vendor/github.com/go-openapi/runtime/middleware/typeutils.go delete mode 100644 vendor/github.com/go-openapi/runtime/middleware/ui_options.go create mode 100644 vendor/github.com/go-openapi/runtime/multipart_stream.go rename vendor/github.com/go-openapi/{swag/jsonname => runtime/server-middleware}/LICENSE (100%) create mode 100644 vendor/github.com/go-openapi/runtime/server-middleware/docui/doc.go create mode 100644 vendor/github.com/go-openapi/runtime/server-middleware/docui/options.go create mode 100644 vendor/github.com/go-openapi/runtime/server-middleware/docui/rapidoc.go create mode 100644 vendor/github.com/go-openapi/runtime/server-middleware/docui/redoc.go create mode 100644 vendor/github.com/go-openapi/runtime/server-middleware/docui/render.go create mode 100644 vendor/github.com/go-openapi/runtime/server-middleware/docui/spec.go create mode 100644 vendor/github.com/go-openapi/runtime/server-middleware/docui/swaggerui.go rename vendor/github.com/go-openapi/runtime/{middleware => server-middleware/docui}/swaggerui_oauth2.go (70%) create mode 100644 vendor/github.com/go-openapi/runtime/server-middleware/mediatype/doc.go create mode 100644 vendor/github.com/go-openapi/runtime/server-middleware/mediatype/lookup.go create mode 100644 vendor/github.com/go-openapi/runtime/server-middleware/mediatype/match.go create mode 100644 vendor/github.com/go-openapi/runtime/server-middleware/mediatype/mediatype.go create mode 100644 vendor/github.com/go-openapi/runtime/server-middleware/mediatype/set.go create mode 100644 vendor/github.com/go-openapi/runtime/server-middleware/negotiate/doc.go rename vendor/github.com/go-openapi/runtime/{middleware => server-middleware/negotiate}/header/header.go (97%) create mode 100644 vendor/github.com/go-openapi/runtime/server-middleware/negotiate/negotiate.go create mode 100644 vendor/github.com/go-openapi/strfmt/country.go create mode 100644 vendor/github.com/go-openapi/strfmt/currency.go create mode 100644 vendor/github.com/go-openapi/strfmt/duration_iso8601.go create mode 100644 vendor/github.com/go-openapi/strfmt/duration_iso8601_options.go create mode 100644 vendor/github.com/go-openapi/strfmt/internal/countries/countries.go create mode 100644 vendor/github.com/go-openapi/strfmt/internal/countries/country.go create mode 100644 vendor/github.com/go-openapi/strfmt/internal/countries/iso3166.json create mode 100644 vendor/github.com/go-openapi/strfmt/register.go create mode 100644 vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/options.go create mode 100644 vendor/github.com/go-openapi/swag/pools/LICENSE create mode 100644 vendor/github.com/go-openapi/swag/pools/README.md create mode 100644 vendor/github.com/go-openapi/swag/pools/debug.go create mode 100644 vendor/github.com/go-openapi/swag/pools/debug_off.go create mode 100644 vendor/github.com/go-openapi/swag/pools/debug_on.go create mode 100644 vendor/github.com/go-openapi/swag/pools/doc.go create mode 100644 vendor/github.com/go-openapi/swag/pools/pools.go create mode 100644 vendor/github.com/go-openapi/validate/spec_ref_warnings.go create mode 100644 vendor/github.com/klauspost/compress/huff0/build_table.go create mode 100644 vendor/github.com/klauspost/compress/zstd/enc_jobs.go create mode 100644 vendor/github.com/klauspost/compress/zstd/fse_decoder_arm64.s rename vendor/github.com/klauspost/compress/zstd/{fse_decoder_amd64.go => fse_decoder_asm.go} (81%) create mode 100644 vendor/github.com/klauspost/compress/zstd/seqdec_arm64.go create mode 100644 vendor/github.com/klauspost/compress/zstd/seqdec_arm64.s create mode 100644 vendor/github.com/klauspost/compress/zstd/seqdec_asm.go create mode 100644 vendor/github.com/mdlayher/socket/.golangci.yml create mode 100644 vendor/github.com/mdlayher/vsock/.golangci.yml delete mode 100644 vendor/github.com/prometheus/client_golang/prometheus/go_collector_go116.go create mode 100644 vendor/github.com/prometheus/procfs/kernel_hung.go create mode 100644 vendor/github.com/prometheus/procfs/nfnetlink_queue.go create mode 100644 vendor/github.com/prometheus/procfs/proc_statm.go create mode 100644 vendor/go.opentelemetry.io/otel/AGENTS.md create mode 100644 vendor/go.opentelemetry.io/otel/CLAUDE.md delete mode 100644 vendor/go.opentelemetry.io/otel/semconv/v1.40.0/README.md rename vendor/go.opentelemetry.io/otel/semconv/{v1.40.0 => v1.41.0}/MIGRATION.md (63%) create mode 100644 vendor/go.opentelemetry.io/otel/semconv/v1.41.0/README.md rename vendor/go.opentelemetry.io/otel/semconv/{v1.40.0 => v1.41.0}/attribute_group.go (96%) rename vendor/go.opentelemetry.io/otel/semconv/{v1.40.0 => v1.41.0}/doc.go (82%) rename vendor/go.opentelemetry.io/otel/semconv/{v1.40.0 => v1.41.0}/error_type.go (75%) rename vendor/go.opentelemetry.io/otel/semconv/{v1.40.0 => v1.41.0}/exception.go (77%) rename vendor/go.opentelemetry.io/otel/semconv/{v1.40.0 => v1.41.0}/httpconv/metric.go (82%) rename vendor/go.opentelemetry.io/otel/semconv/{v1.40.0 => v1.41.0}/otelconv/metric.go (66%) rename vendor/go.opentelemetry.io/otel/semconv/{v1.40.0 => v1.41.0}/schema.go (73%) create mode 100644 vendor/golang.org/x/text/currency/common.go create mode 100644 vendor/golang.org/x/text/currency/currency.go create mode 100644 vendor/golang.org/x/text/currency/format.go create mode 100644 vendor/golang.org/x/text/currency/query.go create mode 100644 vendor/golang.org/x/text/currency/tables.go create mode 100644 vendor/golang.org/x/text/internal/format/format.go create mode 100644 vendor/golang.org/x/text/internal/format/parser.go create mode 100644 vendor/golang.org/x/text/internal/number/common.go create mode 100644 vendor/golang.org/x/text/internal/number/decimal.go create mode 100644 vendor/golang.org/x/text/internal/number/format.go create mode 100644 vendor/golang.org/x/text/internal/number/number.go create mode 100644 vendor/golang.org/x/text/internal/number/pattern.go create mode 100644 vendor/golang.org/x/text/internal/number/roundingmode_string.go create mode 100644 vendor/golang.org/x/text/internal/number/tables.go create mode 100644 vendor/golang.org/x/text/internal/stringset/set.go diff --git a/vendor/connectrpc.com/connect/.gitignore b/vendor/connectrpc.com/connect/.gitignore new file mode 100644 index 0000000000..9f72feebd7 --- /dev/null +++ b/vendor/connectrpc.com/connect/.gitignore @@ -0,0 +1,6 @@ +/.tmp/ +*.pprof +*.svg +.idea +cover.out +connect.test diff --git a/vendor/connectrpc.com/connect/.golangci.yml b/vendor/connectrpc.com/connect/.golangci.yml new file mode 100644 index 0000000000..2a49ad697e --- /dev/null +++ b/vendor/connectrpc.com/connect/.golangci.yml @@ -0,0 +1,173 @@ +version: "2" +linters: + default: all + disable: + - cyclop # covered by gocyclo + - depguard # unnecessary for small libraries + - funcorder # consider enabling in the future + - funlen # rely on code review to limit function length + - gocognit # dubious "cognitive overhead" quantification + - inamedparam # convention is not followed + - ireturn # "accept interfaces, return structs" isn't ironclad + - lll # don't want hard limits for line length + - maintidx # covered by gocyclo + - mnd # status codes are clearer than constants + - nlreturn # generous whitespace violates house style + - noinlineerr # inline is fine + - nonamedreturns # named returns are fine; it's *bare* returns that are bad + - protogetter # too many false positives + - testpackage # internal tests are fine + - wrapcheck # don't _always_ need to wrap errors + - wsl # generous whitespace violates house style + - wsl_v5 # generous whitespace violates house style + settings: + errcheck: + check-type-assertions: true + exhaustruct: + include: + - connectrpc\.com/connect\..*[pP]arams + forbidigo: + forbid: + - pattern: ^fmt\.Print + - pattern: ^log\. + - pattern: ^print$ + - pattern: ^println$ + - pattern: ^panic$ + godox: + keywords: + - FIXME + importas: + alias: + - pkg: connectrpc.com/connect/internal/gen/connect/ping/v1 + alias: pingv1 + no-unaliased: true + varnamelen: + ignore-decls: + - T any + - i int + - wg sync.WaitGroup + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling + rules: + # Loosen requirements on tests + - linters: + - funlen + - gosec + - gosmopolitan + - unparam + - varnamelen + - prealloc + path: _test.go + # If future reflect.Kinds are nil-able, we'll find out when a test fails. + - linters: + - exhaustive + path: internal/assert/assert.go + # We need our duplex HTTP call to have access to the context. + - linters: + - containedctx + path: duplex_http_call.go + # We need to init a global in-mem HTTP server for testable examples. + - linters: + - gochecknoglobals + - gochecknoinits + path: example_init_test.go + # We purposefully do an ineffectual assignment for an example. + - linters: + - ineffassign + path: client_example_test.go + # The generated file is effectively a global receiver. + - linters: + - varnamelen + path: cmd/protoc-gen-connect-go + text: parameter name 'g' is too short + # Thorough error logging and timeout config make this example unreadably long. + - linters: + - errcheck + - gosec + path: error_writer_example_test.go + # It should be crystal clear that Connect uses plain *http.Clients. + - linters: + - revive + - staticcheck + path: client_example_test.go + # Don't complain about timeout management or lack of output assertions in examples. + - linters: + - gosec + - testableexamples + path: handler_example_test.go + # No output assertions needed for these examples. + - linters: + - testableexamples + path: error_writer_example_test.go + - linters: + - testableexamples + path: error_not_modified_example_test.go + - linters: + - testableexamples + path: error_example_test.go + # In examples, it's okay to use http.ListenAndServe. + - linters: + - gosec + path: error_not_modified_example_test.go + # There are many instances where we want to keep unused parameters + # as a matter of style or convention, for example when a context.Context + # is the first parameter, we choose to just globally ignore this. + - linters: + - revive + text: '^unused-parameter: ' + # We want to return explicit nils in protocol_grpc.go + - linters: + - revive + path: protocol_grpc.go + text: '^if-return: ' + # We want to return explicit nils in protocol_connect.go + - linters: + - revive + path: protocol_connect.go + text: '^if-return: ' + # We want to return explicit nils in error_writer.go + - linters: + - revive + path: error_writer.go + text: '^if-return: ' + # We want to set http.Server's logger + - linters: + - forbidigo + path: internal/memhttp + text: use of `log.(New|Logger|Lshortfile)` forbidden by pattern .* + # We want to show examples with http.Get + - linters: + - noctx + path: internal/memhttp/memhttp_test.go + # Allow fmt.Sprintf for cmd/protoc-gen-connect-go for consistency + - linters: + - perfsprint + path: cmd/protoc-gen-connect-go/main.go + # Allow non-canonical headers in tests + - linters: + - canonicalheader + path: .*_test.go + # Allow Code pointer receiver for UnmarshalText method + - linters: + - recvcheck + path: code.go + # Avoid false positives for int overflow in tests + - linters: + - gosec + path: .*_test.go + text: '^G115: integer overflow conversion' + # Don't ban use of fmt.Errorf to create new errors, but the remaining + # checks from err113 are useful. + - path: (.+)\.go$ + text: 'do not define dynamic errors, use wrapped static errors instead: .*' +formatters: + enable: + - gci + - gofmt + exclusions: + generated: lax diff --git a/vendor/connectrpc.com/connect/LICENSE b/vendor/connectrpc.com/connect/LICENSE new file mode 100644 index 0000000000..62b825afba --- /dev/null +++ b/vendor/connectrpc.com/connect/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2021-2025 The Connect Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/connectrpc.com/connect/MAINTAINERS.md b/vendor/connectrpc.com/connect/MAINTAINERS.md new file mode 100644 index 0000000000..237be09894 --- /dev/null +++ b/vendor/connectrpc.com/connect/MAINTAINERS.md @@ -0,0 +1,13 @@ +Maintainers +=========== + +## Current +* [Peter Edge](https://github.com/bufdev), [Buf](https://buf.build) +* [Josh Humphries](https://github.com/jhump), [Buf](https://buf.build) +* [Matt Robenolt](https://github.com/mattrobenolt), [PlanetScale](https://planetscale.com) +* [Edward McFarlane](https://github.com/emcfarlane), [Buf](https://buf.build) +* [Timo Stamm](https://github.com/timostamm), [Buf](https://buf.build) + +## Former +* [Akshay Shah](https://github.com/akshayjshah) +* [Alex McKinney](https://github.com/amckinney) diff --git a/vendor/connectrpc.com/connect/Makefile b/vendor/connectrpc.com/connect/Makefile new file mode 100644 index 0000000000..04720fb37a --- /dev/null +++ b/vendor/connectrpc.com/connect/Makefile @@ -0,0 +1,122 @@ +# See https://tech.davis-hansson.com/p/make/ +SHELL := bash +.DELETE_ON_ERROR: +.SHELLFLAGS := -eu -o pipefail -c +.DEFAULT_GOAL := all +MAKEFLAGS += --warn-undefined-variables +MAKEFLAGS += --no-builtin-rules +MAKEFLAGS += --no-print-directory +BIN := .tmp/bin +export PATH := $(abspath $(BIN)):$(PATH) +export GOBIN := $(abspath $(BIN)) +COPYRIGHT_YEARS := 2021-2026 +LICENSE_IGNORE := --ignore .github/ --ignore ".*\.ya?ml" +BUF_VERSION := 1.69.0 +GOLANGCI_LINT_VERSION ?= v2.11.4 + +.PHONY: help +help: ## Describe useful make targets + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "%-30s %s\n", $$1, $$2}' + +.PHONY: all +all: ## Build, test, and lint (default) + $(MAKE) test + $(MAKE) lint + +.PHONY: clean +clean: ## Delete intermediate build artifacts + @# -X only removes untracked files, -d recurses into directories, -f actually removes files/dirs + git clean -Xdf + +.PHONY: test +test: shorttest slowtest + +.PHONY: shorttest +shorttest: build ## Run unit tests + go test -vet=off -race -cover -short ./... + +.PHONY: slowtest +# Runs all tests, including known long/slow ones. The +# race detector is not used for a few reasons: +# 1. Race coverage of the short tests should be +# adequate to catch race conditions. +# 2. It slows tests down, which is not good if we +# know these are already slow tests. +# 3. Some of the slow tests can't repro issues and +# find regressions as reliably with the race +# detector enabled. +slowtest: build + go test ./... + +.PHONY: runconformance +runconformance: build ## Run conformance test suite + cd internal/conformance && ./runconformance.sh + +.PHONY: bench +bench: BENCH ?= .* +bench: build ## Run benchmarks for root package + go test -vet=off -run '^$$' -bench '$(BENCH)' -benchmem -cpuprofile cpu.pprof -memprofile mem.pprof . + +.PHONY: build +build: generate ## Build all packages + go build ./... + +.PHONY: install +install: ## Install all binaries + go install ./... + +.PHONY: lint +lint: $(BIN)/golangci-lint $(BIN)/buf ## Lint Go and protobuf + go vet ./... + golangci-lint run --modules-download-mode=readonly --timeout=3m0s + buf lint + buf format -d --exit-code + +.PHONY: lintfix +lintfix: $(BIN)/golangci-lint $(BIN)/buf ## Automatically fix some lint errors + golangci-lint run --fix --modules-download-mode=readonly --timeout=3m0s + buf format -w + +.PHONY: generate +generate: $(BIN)/buf $(BIN)/protoc-gen-go $(BIN)/protoc-gen-connect-go $(BIN)/license-header ## Regenerate code and licenses + go mod tidy + cd ./internal/conformance && go mod tidy + buf generate + cd ./cmd/protoc-gen-connect-go/internal && \ + find ./testdata -maxdepth 1 -type d \( ! -name testdata \) | xargs -n 1 -I % bash -c "cd '%' && buf generate" + license-header \ + --license-type apache \ + --copyright-holder "The Connect Authors" \ + --year-range "$(COPYRIGHT_YEARS)" $(LICENSE_IGNORE) + +.PHONY: upgrade +upgrade: ## Upgrade dependencies + go get -u -t ./... && go mod tidy -v + +.PHONY: checkgenerate +checkgenerate: + @# Used in CI to verify that `make generate` doesn't produce a diff. + test -z "$$(git status --porcelain | tee /dev/stderr)" + +.PHONY: $(BIN)/protoc-gen-connect-go +$(BIN)/protoc-gen-connect-go: + @mkdir -p $(@D) + go build -o $(@) ./cmd/protoc-gen-connect-go + +$(BIN)/buf: Makefile + @mkdir -p $(@D) + go install github.com/bufbuild/buf/cmd/buf@v${BUF_VERSION} + +$(BIN)/license-header: Makefile + @mkdir -p $(@D) + go install github.com/bufbuild/buf/private/pkg/licenseheader/cmd/license-header@v${BUF_VERSION} + +$(BIN)/golangci-lint: Makefile + @mkdir -p $(@D) + go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@$(GOLANGCI_LINT_VERSION) + +$(BIN)/protoc-gen-go: Makefile go.mod + @mkdir -p $(@D) + @# The version of protoc-gen-go is determined by the version in go.mod + go install google.golang.org/protobuf/cmd/protoc-gen-go + diff --git a/vendor/connectrpc.com/connect/README.md b/vendor/connectrpc.com/connect/README.md new file mode 100644 index 0000000000..57f4ef6329 --- /dev/null +++ b/vendor/connectrpc.com/connect/README.md @@ -0,0 +1,184 @@ +Connect +======= + +[![Build](https://github.com/connectrpc/connect-go/actions/workflows/ci.yaml/badge.svg?branch=main)](https://github.com/connectrpc/connect-go/actions/workflows/ci.yaml) +[![Report Card](https://goreportcard.com/badge/connectrpc.com/connect)](https://goreportcard.com/report/connectrpc.com/connect) +[![GoDoc](https://pkg.go.dev/badge/connectrpc.com/connect.svg)](https://pkg.go.dev/connectrpc.com/connect) +[![Slack](https://img.shields.io/badge/slack-buf-%23e01563)][slack] +[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/8972/badge)](https://www.bestpractices.dev/projects/8972) + +Connect is a slim library for building browser and gRPC-compatible HTTP APIs. +You write a short [Protocol Buffer][protobuf] schema and implement your +application logic, and Connect generates code to handle marshaling, routing, +compression, and content type negotiation. It also generates an idiomatic, +type-safe client. Handlers and clients support three protocols: gRPC, gRPC-Web, +and Connect's own protocol. + +The [Connect protocol][protocol] is a simple protocol that works over HTTP/1.1 +or HTTP/2. It takes the best portions of gRPC and gRPC-Web, including +streaming, and packages them into a protocol that works equally well in +browsers, monoliths, and microservices. Calling a Connect API is as easy as +using `curl`. Try it with our live demo: + +``` +curl \ + --header "Content-Type: application/json" \ + --data '{"sentence": "I feel happy."}' \ + https://demo.connectrpc.com/connectrpc.eliza.v1.ElizaService/Say +``` + +Handlers and clients also support the gRPC and gRPC-Web protocols, including +streaming, headers, trailers, and error details. gRPC-compatible [server +reflection][grpcreflect] and [health checks][grpchealth] are available as +standalone packages. Instead of cURL, we could call our API with a gRPC client: + +``` +go install github.com/bufbuild/buf/cmd/buf@latest +buf curl --protocol grpc \ + --data '{"sentence": "I feel happy."}' \ + https://demo.connectrpc.com/connectrpc.eliza.v1.ElizaService/Say +``` + +Under the hood, Connect is just [Protocol Buffers][protobuf] and the standard +library: no custom HTTP implementation, no new name resolution or load +balancing APIs, and no surprises. Everything you already know about `net/http` +still applies, and any package that works with an `http.Server`, `http.Client`, +or `http.Handler` also works with Connect. + +For more on Connect, see the [announcement blog post][blog], the documentation +on [connectrpc.com][docs] (especially the [Getting Started] guide for Go), the +[demo service][examples-go], or the [protocol specification][protocol]. + +## A small example + +Curious what all this looks like in practice? From a [Protobuf +schema](internal/proto/connect/ping/v1/ping.proto), we generate [a small RPC +package](internal/gen/simple/connect/ping/v1/pingv1connect/ping.connect.go). Using that +package, we can build a server. This example is available at [internal/example](internal/example): + +```go +package main + +import ( + "context" + "log" + "net/http" + + "connectrpc.com/connect" + pingv1 "connectrpc.com/connect/internal/gen/connect/ping/v1" + "connectrpc.com/connect/internal/gen/simple/connect/ping/v1/pingv1connect" + "connectrpc.com/validate" +) + +type PingServer struct { + pingv1connect.UnimplementedPingServiceHandler // returns errors from all methods +} + +func (ps *PingServer) Ping(ctx context.Context, req *pingv1.PingRequest) (*pingv1.PingResponse, error) { + return &pingv1.PingResponse{ + Number: req.Number, + }, nil +} + +func main() { + mux := http.NewServeMux() + // The generated constructors return a path and a plain net/http + // handler. + mux.Handle( + pingv1connect.NewPingServiceHandler( + &PingServer{}, + // Validation via Protovalidate is almost always recommended + connect.WithInterceptors(validate.NewInterceptor()), + ), + ) + p := new(http.Protocols) + p.SetHTTP1(true) + // For gRPC clients, it's convenient to support HTTP/2 without TLS. + p.SetUnencryptedHTTP2(true) + s := &http.Server{ + Addr: "localhost:8080", + Handler: mux, + Protocols: p, + } + if err := s.ListenAndServe(); err != nil { + log.Fatalf("listen failed: %v", err) + } +} +``` + +With that server running, you can make requests with any gRPC or Connect +client. To write a client using Connect: + +```go +package main + +import ( + "context" + "log" + "net/http" + + pingv1 "connectrpc.com/connect/internal/gen/connect/ping/v1" + "connectrpc.com/connect/internal/gen/simple/connect/ping/v1/pingv1connect" +) + +func main() { + client := pingv1connect.NewPingServiceClient( + http.DefaultClient, + "http://localhost:8080/", + ) + req := &pingv1.PingRequest{Number: 42} + res, err := client.Ping(context.Background(), req) + if err != nil { + log.Fatalln(err) + } + log.Println(res) +} +``` + +Of course, `http.ListenAndServe` and `http.DefaultClient` aren't fit for +production use! See Connect's [deployment docs][docs-deployment] for a guide to +configuring timeouts, connection pools, observability, and h2c. + +## Ecosystem + +* [grpchealth]: gRPC-compatible health checks for connect-go +* [grpcreflect]: gRPC-compatible server reflection for connect-go +* [validate]: [Protovalidate][protovalidate] interceptor for connect-go +* [examples-go]: service powering [demo.connectrpc.com](https://demo.connectrpc.com), including bidi streaming +* [connect-es]: Type-safe APIs with Protobuf and TypeScript +* [Buf Studio]: web UI for ad-hoc RPCs +* [conformance]: Connect, gRPC, and gRPC-Web interoperability tests + +## Status: Stable + +This module is stable. It supports: + +* The two most recent major releases of Go (the same versions of Go that continue + to [receive security patches][go-support-policy]). +* [APIv2] of Protocol Buffers in Go (`google.golang.org/protobuf`). + +Within those parameters, `connect` follows semantic versioning. We will +_not_ make breaking changes in the 1.x series of releases. + +## Legal + +Offered under the [Apache 2 license][license]. + +[APIv2]: https://blog.golang.org/protobuf-apiv2 +[Buf Studio]: https://buf.build/studio +[Getting Started]: https://connectrpc.com/docs/go/getting-started +[blog]: https://buf.build/blog/connect-a-better-grpc +[conformance]: https://github.com/connectrpc/conformance +[grpchealth]: https://github.com/connectrpc/grpchealth-go +[grpcreflect]: https://github.com/connectrpc/grpcreflect-go +[connect-es]: https://github.com/connectrpc/connect-es +[examples-go]: https://github.com/connectrpc/examples-go +[docs-deployment]: https://connectrpc.com/docs/go/deployment +[docs]: https://connectrpc.com +[go-support-policy]: https://golang.org/doc/devel/release#policy +[license]: https://github.com/connectrpc/connect-go/blob/main/LICENSE +[protobuf]: https://developers.google.com/protocol-buffers +[protocol]: https://connectrpc.com/docs/protocol +[slack]: https://buf.build/links/slack +[validate]: https://github.com/connectrpc/validate-go +[protovalidate]: https://protovalidate.com diff --git a/vendor/connectrpc.com/connect/RELEASE.md b/vendor/connectrpc.com/connect/RELEASE.md new file mode 100644 index 0000000000..de7e70a5b4 --- /dev/null +++ b/vendor/connectrpc.com/connect/RELEASE.md @@ -0,0 +1,44 @@ +# Releasing connect-go + +This document outlines how to create a release of connect-go. + +1. Clone the repo, ensuring you have the latest main. + +2. On a new branch, open [connect.go](connect.go) and change the `Version` constant to an appropriate [semantic version](https://semver.org/). To select the correct version, look at the version number of the [latest release] and the changes that are included in this new release. + * If there are only bug fixes and no new features, remove the `-dev` suffix, set MINOR number to be equal to the [latest release], and set the PATCH number to be 1 more than the PATCH number of the [latest release]. + * If there are features being released, remove the `-dev` suffix, set the MINOR number to be 1 more than the MINOR number of the [latest release], and set the PATCH number to `0`. In the common case, the diff here will just be to remove the `-dev` suffix. + + ```patch + -const Version = "1.14.0-dev" + +const Version = "1.14.0" + ``` + +3. Check for any changes in [cmd/protoc-gen-connect-go/main.go](cmd/protoc-gen-connect-go/main.go) that require a version restriction. A constant `IsAtLeastVersionX_Y_Z` should be defined in [connect.go](connect.go) if generated code has begun to use a new API. Make sure the generated code references this constant. If a new constant has been added since the last release, ensure that the name of the constant matches the version being released ([Example PR #496](https://github.com/connectrpc/connect-go/pull/496)). + +4. Open a PR titled "Prepare for vX.Y.Z" ([Example PR #661](https://github.com/connectrpc/connect-go/pull/661)) and a description tagging all current maintainers. Once it's reviewed and CI passes, merge it. + + *Make sure no new commits are merged until the release is complete.* + +5. Review all commits in the new release and for each PR check an appropriate label is used and edit the title to be meaningful to end users. This will help auto-generated release notes match the final notes as closely as possible. + +6. Using the Github UI, create a new release. + - Under “Choose a tag”, type in “vX.Y.Z” to create a new tag for the release upon publish. + - Target the main branch. + - Title the Release “vX.Y.Z”. + - Click “set as latest release”. + - Set the last version as the “Previous tag”. + - Click “Generate release notes” to autogenerate release notes. + - Edit the release notes. A summary and other sub categories may be added if required but should, in most cases, be left as ### Enhancements and ### Bugfixes. Feel free to collect multiple small changes to docs or Github config into one line, but try to tag every contributor. Make especially sure to credit new external contributors! + +7. Publish the release. + +8. On a new branch, open [connect.go](connect.go) and change the `Version` to increment the minor tag and append the `-dev` suffix. Use the next minor release - we never anticipate bugs and patch releases. + + ```patch + -const Version = "1.14.0" + +const Version = "1.15.0-dev" + ``` + +9. Open a PR titled "Back to development" ([Example PR #662](https://github.com/connectrpc/connect-go/pull/662)). Once it's reviewed and CI passes, merge it. + +[latest release]: https://github.com/connectrpc/connect-go/releases/latest diff --git a/vendor/connectrpc.com/connect/SECURITY.md b/vendor/connectrpc.com/connect/SECURITY.md new file mode 100644 index 0000000000..04dcde5210 --- /dev/null +++ b/vendor/connectrpc.com/connect/SECURITY.md @@ -0,0 +1,5 @@ +Security Policy +=============== + +This project follows the [Connect security policy and reporting +process](https://connectrpc.com/docs/governance/security). diff --git a/vendor/connectrpc.com/connect/buf.gen.yaml b/vendor/connectrpc.com/connect/buf.gen.yaml new file mode 100644 index 0000000000..45beb82af6 --- /dev/null +++ b/vendor/connectrpc.com/connect/buf.gen.yaml @@ -0,0 +1,19 @@ +version: v2 +managed: + enabled: true + override: + - file_option: go_package_prefix + value: connectrpc.com/connect/internal/gen +plugins: + - local: protoc-gen-go + out: internal/gen + opt: paths=source_relative + - local: protoc-gen-connect-go + out: internal/gen/generics + opt: paths=source_relative + - local: protoc-gen-connect-go + out: internal/gen/simple + opt: + - paths=source_relative + - simple +clean: true diff --git a/vendor/connectrpc.com/connect/buf.yaml b/vendor/connectrpc.com/connect/buf.yaml new file mode 100644 index 0000000000..0ab5a09449 --- /dev/null +++ b/vendor/connectrpc.com/connect/buf.yaml @@ -0,0 +1,14 @@ +version: v2 +modules: + - path: internal/proto +lint: + use: + - STANDARD + ignore: + - internal/proto/connectext/grpc/health/v1/health.proto + - internal/proto/connectext/grpc/reflection/v1alpha/reflection.proto + - internal/proto/connectext/grpc/status/v1/status.proto + disallow_comment_ignores: true +breaking: + use: + - WIRE_JSON diff --git a/vendor/connectrpc.com/connect/buffer_pool.go b/vendor/connectrpc.com/connect/buffer_pool.go new file mode 100644 index 0000000000..006ad5beda --- /dev/null +++ b/vendor/connectrpc.com/connect/buffer_pool.go @@ -0,0 +1,54 @@ +// Copyright 2021-2026 The Connect Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package connect + +import ( + "bytes" + "sync" +) + +const ( + initialBufferSize = 512 + maxRecycleBufferSize = 8 * 1024 * 1024 // if >8MiB, don't hold onto a buffer +) + +type bufferPool struct { + sync.Pool +} + +func newBufferPool() *bufferPool { + return &bufferPool{ + Pool: sync.Pool{ + New: func() any { + return bytes.NewBuffer(make([]byte, 0, initialBufferSize)) + }, + }, + } +} + +func (b *bufferPool) Get() *bytes.Buffer { + if buf, ok := b.Pool.Get().(*bytes.Buffer); ok { + return buf + } + return bytes.NewBuffer(make([]byte, 0, initialBufferSize)) +} + +func (b *bufferPool) Put(buffer *bytes.Buffer) { + if buffer.Cap() > maxRecycleBufferSize { + return + } + buffer.Reset() + b.Pool.Put(buffer) +} diff --git a/vendor/connectrpc.com/connect/client.go b/vendor/connectrpc.com/connect/client.go new file mode 100644 index 0000000000..d44e6e4a84 --- /dev/null +++ b/vendor/connectrpc.com/connect/client.go @@ -0,0 +1,392 @@ +// Copyright 2021-2026 The Connect Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package connect + +import ( + "context" + "errors" + "fmt" + "io" + "net/http" + "net/url" + "strings" +) + +// Client is a reusable, concurrency-safe client for a single procedure. +// Depending on the procedure's type, use the CallUnary, CallClientStream, +// CallServerStream, or CallBidiStream method. +// +// By default, clients use the Connect protocol with the binary Protobuf Codec, +// ask for gzipped responses, and send uncompressed requests. To use the gRPC +// or gRPC-Web protocols, use the [WithGRPC] or [WithGRPCWeb] options. +type Client[Req, Res any] struct { + config *clientConfig + callUnary func(context.Context, *Request[Req]) (*Response[Res], error) + protocolClient protocolClient + err error +} + +// NewClient constructs a new Client. +func NewClient[Req, Res any](httpClient HTTPClient, url string, options ...ClientOption) *Client[Req, Res] { + client := &Client[Req, Res]{} + config, err := newClientConfig(url, options) + if err != nil { + client.err = err + return client + } + client.config = config + protocolClient, protocolErr := client.config.Protocol.NewClient( + &protocolClientParams{ + CompressionName: config.RequestCompressionName, + CompressionPools: newReadOnlyCompressionPools( + config.CompressionPools, + config.CompressionNames, + ), + Codec: config.Codec, + Protobuf: config.protobuf(), + CompressMinBytes: config.CompressMinBytes, + HTTPClient: httpClient, + URL: config.URL, + BufferPool: config.BufferPool, + ReadMaxBytes: config.ReadMaxBytes, + SendMaxBytes: config.SendMaxBytes, + EnableGet: config.EnableGet, + GetURLMaxBytes: config.GetURLMaxBytes, + GetUseFallback: config.GetUseFallback, + }, + ) + if protocolErr != nil { + client.err = protocolErr + return client + } + client.protocolClient = protocolClient + // Rather than applying unary interceptors along the hot path, we can do it + // once at client creation. + unarySpec := config.newSpec(StreamTypeUnary) + unaryFunc := UnaryFunc(func(ctx context.Context, request AnyRequest) (AnyResponse, error) { + conn := client.protocolClient.NewConn(ctx, unarySpec, request.Header()) + conn.onRequestSend(func(r *http.Request) { + request.setRequestMethod(r.Method) + callInfo, ok := clientCallInfoForContext(ctx) + if ok { + callInfo.method = r.Method + callInfo.responseSource = conn + } + }) + // Send always returns an io.EOF unless the error is from the client-side. + // We want the user to continue to call Receive in those cases to get the + // full error from the server-side. + if err := conn.Send(request.Any()); err != nil && !errors.Is(err, io.EOF) { + _ = conn.CloseRequest() + _ = conn.CloseResponse() + return nil, err + } + if err := conn.CloseRequest(); err != nil { + _ = conn.CloseResponse() + return nil, err + } + response, err := receiveUnaryResponse[Res](conn, config.Initializer) + if err != nil { + _ = conn.CloseResponse() + return nil, err + } + return response, conn.CloseResponse() + }) + if interceptor := config.Interceptor; interceptor != nil { + // interceptor is the full chain of all interceptors provided + unaryFunc = interceptor.WrapUnary(unaryFunc) + } + client.callUnary = func(ctx context.Context, request *Request[Req]) (*Response[Res], error) { + // To make the specification, peer, and RPC headers visible to the full + // interceptor chain (as though they were supplied by the caller), we'll + // add them here. + request.spec = unarySpec + request.peer = client.protocolClient.Peer() + protocolClient.WriteRequestHeader(StreamTypeUnary, request.Header()) + + // Also set them in the context if there's a call info present + callInfo, callInfoOk := clientCallInfoForContext(ctx) + if callInfoOk { + callInfo.peer = request.Peer() + callInfo.spec = request.Spec() + // A client could have set request headers in the call info OR the request wrapper + // So if a callInfo exists in context, merge any headers from there into the request wrapper + // so that all headers are sent in the request + mergeHeaders(request.Header(), callInfo.requestHeader) + + // Copy the call info into a sentinel value. This is so we can compare + // the sentinel value against the call info in context. If they're different, + // we can stop the request. This protects against changing the context in interceptors. + ctx = context.WithValue(ctx, sentinelContextKey{}, callInfo) + } + + response, err := unaryFunc(ctx, request) + if err != nil { + return nil, err + } + typed, ok := response.(*Response[Res]) + if !ok { + return nil, errorf(CodeInternal, "unexpected client response type %T", response) + } + return typed, nil + } + return client +} + +// CallUnary calls a request-response procedure. +func (c *Client[Req, Res]) CallUnary(ctx context.Context, request *Request[Req]) (*Response[Res], error) { + if c.err != nil { + return nil, c.err + } + return c.callUnary(ctx, request) +} + +// CallClientStream calls a client streaming procedure. +// +// Request headers can be sent via the [ClientStreamForClient.RequestHeader] method on the stream. Note that the +// request headers are not sent automatically when this method is invoked and instead require an explicit call to +// [ClientStreamForClient.Send]. +func (c *Client[Req, Res]) CallClientStream(ctx context.Context) *ClientStreamForClient[Req, Res] { + if c.err != nil { + return &ClientStreamForClient[Req, Res]{err: c.err} + } + return &ClientStreamForClient[Req, Res]{ + conn: c.newConn(ctx, StreamTypeClient, nil), + initializer: c.config.Initializer, + } +} + +// CallClientStreamSimple calls a client streaming procedure. +// +// Request headers should be set in a [CallInfo] object inside the context using [NewClientContext]. These headers are +// transmitted when this method is called and do not require an explicit call to [ClientStreamForClientSimple.Send]. +// +// In addition, when calling [ClientStreamForClientSimple.CloseAndReceive] on the returned stream, the returned response +// is the response type defined for the stream and _not_ a Connect [Response] wrapper type. As a result, any response +// headers and trailers should be read from the [CallInfo] object in context. +func (c *Client[Req, Res]) CallClientStreamSimple(ctx context.Context) (*ClientStreamForClientSimple[Req, Res], error) { + if c.err != nil { + return &ClientStreamForClientSimple[Req, Res]{ + stream: &ClientStreamForClient[Req, Res]{err: c.err}, + }, c.err + } + + stream := &ClientStreamForClientSimple[Req, Res]{ + stream: &ClientStreamForClient[Req, Res]{ + conn: c.newConn(ctx, StreamTypeClient, nil), + initializer: c.config.Initializer, + }, + } + if err := stream.Send(nil); err != nil { + return nil, err + } + return stream, nil +} + +// CallServerStream calls a server streaming procedure. +func (c *Client[Req, Res]) CallServerStream(ctx context.Context, request *Request[Req]) (*ServerStreamForClient[Res], error) { + if c.err != nil { + return nil, c.err + } + conn := c.newConn(ctx, StreamTypeServer, func(r *http.Request) { + request.method = r.Method + }) + request.peer = conn.Peer() + request.spec = conn.Spec() + + mergeHeaders(conn.RequestHeader(), request.header) + + // Send always returns an io.EOF unless the error is from the client-side. + // We want the user to continue to call Receive in those cases to get the + // full error from the server-side. + if err := conn.Send(request.Msg); err != nil && !errors.Is(err, io.EOF) { + _ = conn.CloseRequest() + _ = conn.CloseResponse() + return nil, err + } + if err := conn.CloseRequest(); err != nil { + return nil, err + } + return &ServerStreamForClient[Res]{ + conn: conn, + initializer: c.config.Initializer, + }, nil +} + +// CallBidiStream calls a bidirectional streaming procedure. +// +// Request headers can be sent via the [BidiStreamForClient.RequestHeader] method. Note that the +// request headers are not sent automatically when this method is invoked and instead require an explicit call to +// [BidiStreamForClient.Send]. +func (c *Client[Req, Res]) CallBidiStream(ctx context.Context) *BidiStreamForClient[Req, Res] { + if c.err != nil { + return &BidiStreamForClient[Req, Res]{err: c.err} + } + return &BidiStreamForClient[Req, Res]{ + conn: c.newConn(ctx, StreamTypeBidi, nil), + initializer: c.config.Initializer, + } +} + +// CallBidiStreamSimple calls a bidirectional streaming procedure. +// +// Request headers should be set in a [CallInfo] object inside the context using [NewClientContext]. These headers +// are transmitted when this method is called and do not require an explicit call to [BidiStreamForClient.Send]. +// +// Likewise, response headers and trailers should be read from the [CallInfo] object in context. +func (c *Client[Req, Res]) CallBidiStreamSimple(ctx context.Context) (*BidiStreamForClientSimple[Req, Res], error) { + if c.err != nil { + return &BidiStreamForClientSimple[Req, Res]{ + stream: &BidiStreamForClient[Req, Res]{err: c.err}, + }, c.err + } + + stream := &BidiStreamForClientSimple[Req, Res]{ + stream: &BidiStreamForClient[Req, Res]{ + conn: c.newConn(ctx, StreamTypeBidi, nil), + initializer: c.config.Initializer, + }, + } + + if err := stream.Send(nil); err != nil { + return nil, err + } + return stream, nil +} + +func (c *Client[Req, Res]) newConn(ctx context.Context, streamType StreamType, onRequestSend func(r *http.Request)) StreamingClientConn { + callInfo, callInfoOk := clientCallInfoForContext(ctx) + // Set values in the context if there's a call info present + if callInfoOk { + // Copy the call info into a sentinel value. This is so we can compare + // the sentinel value against the call info in context. If they're different, + // we can stop the request. This protects against changing the context in interceptors. + ctx = context.WithValue(ctx, sentinelContextKey{}, callInfo) + } + newConn := func(ctx context.Context, spec Spec) StreamingClientConn { + header := make(http.Header, 8) // arbitrary power of two, prevent immediate resizing + c.protocolClient.WriteRequestHeader(streamType, header) + conn := c.protocolClient.NewConn(ctx, spec, header) + conn.onRequestSend(onRequestSend) + return conn + } + if interceptor := c.config.Interceptor; interceptor != nil { + newConn = interceptor.WrapStreamingClient(newConn) + } + conn := newConn(ctx, c.config.newSpec(streamType)) + + // Set values in the context if there's a call info present + if callInfoOk { + callInfo.peer = conn.Peer() + callInfo.spec = conn.Spec() + callInfo.responseSource = conn + + // Merge any callInfo request headers first, then do the request, + // so that context headers show first in the list of headers. + mergeHeaders(conn.RequestHeader(), callInfo.RequestHeader()) + } + + return conn +} + +type clientConfig struct { + URL *url.URL + Protocol protocol + Procedure string + Schema any + Initializer maybeInitializer + CompressMinBytes int + Interceptor Interceptor + CompressionPools map[string]*compressionPool + CompressionNames []string + Codec Codec + RequestCompressionName string + BufferPool *bufferPool + ReadMaxBytes int + SendMaxBytes int + EnableGet bool + GetURLMaxBytes int + GetUseFallback bool + IdempotencyLevel IdempotencyLevel +} + +func newClientConfig(rawURL string, options []ClientOption) (*clientConfig, *Error) { + url, err := parseRequestURL(rawURL) + if err != nil { + return nil, err + } + protoPath := extractProtoPath(url.Path) + config := clientConfig{ + URL: url, + Protocol: &protocolConnect{}, + Procedure: protoPath, + CompressionPools: make(map[string]*compressionPool), + BufferPool: newBufferPool(), + } + withProtoBinaryCodec().applyToClient(&config) + withGzip().applyToClient(&config) + for _, opt := range options { + opt.applyToClient(&config) + } + if err := config.validate(); err != nil { + return nil, err + } + return &config, nil +} + +func (c *clientConfig) validate() *Error { + if c.Codec == nil || c.Codec.Name() == "" { + return errorf(CodeUnknown, "no codec configured") + } + if c.RequestCompressionName != "" && c.RequestCompressionName != compressionIdentity { + if _, ok := c.CompressionPools[c.RequestCompressionName]; !ok { + return errorf(CodeUnknown, "unknown compression %q", c.RequestCompressionName) + } + } + return nil +} + +func (c *clientConfig) protobuf() Codec { + if c.Codec.Name() == codecNameProto { + return c.Codec + } + return &protoBinaryCodec{} +} + +func (c *clientConfig) newSpec(t StreamType) Spec { + return Spec{ + StreamType: t, + Procedure: c.Procedure, + Schema: c.Schema, + IsClient: true, + IdempotencyLevel: c.IdempotencyLevel, + } +} + +func parseRequestURL(rawURL string) (*url.URL, *Error) { + url, err := url.ParseRequestURI(rawURL) + if err == nil { + return url, nil + } + if !strings.Contains(rawURL, "://") { + // URL doesn't have a scheme, so the user is likely accustomed to + // grpc-go's APIs. + err = fmt.Errorf( + "URL %q missing scheme: use http:// or https:// (unlike grpc-go)", + rawURL, + ) + } + return nil, NewError(CodeUnavailable, err) +} diff --git a/vendor/connectrpc.com/connect/client_stream.go b/vendor/connectrpc.com/connect/client_stream.go new file mode 100644 index 0000000000..83e2af0b82 --- /dev/null +++ b/vendor/connectrpc.com/connect/client_stream.go @@ -0,0 +1,453 @@ +// Copyright 2021-2026 The Connect Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package connect + +import ( + "errors" + "io" + "net/http" +) + +var ( + // errNoStreamInitialized signals that a no stream has been initialized when + // attempting to access stream-related methods. + errNoStreamInitialized = errors.New("no stream initialized") +) + +// ClientStreamForClient is the client's view of a client streaming RPC. +// +// It's returned from [Client].CallClientStream, but doesn't currently have an +// exported constructor function. +// +// When using this stream, request headers should be set via the [ClientStreamForClient.RequestHeader] method. +// +// Send is not safe to call concurrently. +type ClientStreamForClient[Req, Res any] struct { + conn StreamingClientConn + initializer maybeInitializer + // Error from client construction. If non-nil, return for all calls. + err error +} + +// Spec returns the specification for the RPC. +func (c *ClientStreamForClient[_, _]) Spec() Spec { + return c.conn.Spec() +} + +// Peer describes the server for the RPC. +func (c *ClientStreamForClient[_, _]) Peer() Peer { + return c.conn.Peer() +} + +// RequestHeader returns the request headers. Headers are sent to the server with the +// first call to Send. +// +// Headers beginning with "Connect-" and "Grpc-" are reserved for use by the +// Connect and gRPC protocols. Applications shouldn't write them. +func (c *ClientStreamForClient[Req, Res]) RequestHeader() http.Header { + if c.err != nil { + return http.Header{} + } + return c.conn.RequestHeader() +} + +// Send a message to the server. The first call to Send also sends the request +// headers. +// +// If the server returns an error, Send returns an error that wraps [io.EOF]. +// Clients should check for case using the standard library's [errors.Is] and +// unmarshal the error using CloseAndReceive. +func (c *ClientStreamForClient[Req, Res]) Send(request *Req) error { + if c.err != nil { + return c.err + } + if request == nil { + return c.conn.Send(nil) + } + return c.conn.Send(request) +} + +// CloseAndReceive closes the send side of the stream and waits for the +// response. +func (c *ClientStreamForClient[Req, Res]) CloseAndReceive() (*Response[Res], error) { + if c.err != nil { + return nil, c.err + } + if err := c.conn.CloseRequest(); err != nil { + _ = c.conn.CloseResponse() + return nil, err + } + response, err := receiveUnaryResponse[Res](c.conn, c.initializer) + if err != nil { + _ = c.conn.CloseResponse() + return nil, err + } + return response, c.conn.CloseResponse() +} + +// Conn exposes the underlying StreamingClientConn. This may be useful if +// you'd prefer to wrap the connection in a different high-level API. +func (c *ClientStreamForClient[Req, Res]) Conn() (StreamingClientConn, error) { + return c.conn, c.err +} + +// ClientStreamForClientSimple is the client's view of a client streaming RPC. +// +// It's returned from [Client.CallClientStreamSimple], but doesn't currently have an +// exported constructor function. +// +// Usage of this stream requires that request headers be set in a [CallInfo] object in context via [NewClientContext]. +// In addition, the response returned by [ClientStreamForClientSimple.CloseAndReceive] is the response type defined for +// the stream and _not_ a Connect [Response] wrapper type. As a result, response headers/trailers should be read from +// the [CallInfo] object in context. +// +// Send is not safe to call concurrently. +type ClientStreamForClientSimple[Req, Res any] struct { + stream *ClientStreamForClient[Req, Res] +} + +// Spec returns the specification for the RPC. +func (c *ClientStreamForClientSimple[_, _]) Spec() Spec { + if c.stream == nil { + return Spec{} + } + return c.stream.Spec() +} + +// Peer describes the server for the RPC. +func (c *ClientStreamForClientSimple[_, _]) Peer() Peer { + if c.stream == nil { + return Peer{} + } + return c.stream.Peer() +} + +// Send a message to the server. The first call to Send also sends the request +// headers. +// +// If the server returns an error, Send returns an error that wraps [io.EOF]. +// Clients should check for case using the standard library's [errors.Is] and +// unmarshal the error using CloseAndReceive. +func (c *ClientStreamForClientSimple[Req, Res]) Send(request *Req) error { + if c.stream == nil { + return errNoStreamInitialized + } + return c.stream.Send(request) +} + +// CloseAndReceive closes the send side of the stream and waits for the +// response. +func (c *ClientStreamForClientSimple[Req, Res]) CloseAndReceive() (*Res, error) { + if c.stream == nil { + return nil, errNoStreamInitialized + } + res, err := c.stream.CloseAndReceive() + if err != nil { + return nil, err + } + return res.Msg, nil +} + +// ServerStreamForClient is the client's view of a server streaming RPC. +// +// It's returned from [Client].CallServerStream, but doesn't currently have an +// exported constructor function. +// +// Receive is not safe to call concurrently. +type ServerStreamForClient[Res any] struct { + conn StreamingClientConn + initializer maybeInitializer + msg *Res + // Error from client construction. If non-nil, return for all calls. + constructErr error + // Error from conn.Receive(). + receiveErr error +} + +// Receive advances the stream to the next message, which will then be +// available through the Msg method. It returns false when the stream stops, +// either by reaching the end or by encountering an unexpected error. After +// Receive returns false, the Err method will return any unexpected error +// encountered. +func (s *ServerStreamForClient[Res]) Receive() bool { + if s.constructErr != nil || s.receiveErr != nil { + return false + } + s.msg = new(Res) + if err := s.initializer.maybe(s.conn.Spec(), s.msg); err != nil { + s.receiveErr = err + return false + } + s.receiveErr = s.conn.Receive(s.msg) + return s.receiveErr == nil +} + +// Msg returns the most recent message unmarshaled by a call to Receive. +func (s *ServerStreamForClient[Res]) Msg() *Res { + if s.msg == nil { + s.msg = new(Res) + } + return s.msg +} + +// Err returns the first non-EOF error that was encountered by Receive. +func (s *ServerStreamForClient[Res]) Err() error { + if s.constructErr != nil { + return s.constructErr + } + if s.receiveErr != nil && !errors.Is(s.receiveErr, io.EOF) { + return s.receiveErr + } + return nil +} + +// ResponseHeader returns the headers received from the server. It blocks until +// the first call to Receive returns. +func (s *ServerStreamForClient[Res]) ResponseHeader() http.Header { + if s.constructErr != nil { + return http.Header{} + } + return s.conn.ResponseHeader() +} + +// ResponseTrailer returns the trailers received from the server. Trailers +// aren't fully populated until Receive() returns an error wrapping io.EOF. +func (s *ServerStreamForClient[Res]) ResponseTrailer() http.Header { + if s.constructErr != nil { + return http.Header{} + } + return s.conn.ResponseTrailer() +} + +// Close the receive side of the stream. +// +// Close is non-blocking. To gracefully close the stream and allow for +// connection resuse ensure all messages have been received before calling +// Close. All messages are received when Receive returns false. +func (s *ServerStreamForClient[Res]) Close() error { + if s.constructErr != nil { + return s.constructErr + } + return s.conn.CloseResponse() +} + +// Conn exposes the underlying StreamingClientConn. This may be useful if +// you'd prefer to wrap the connection in a different high-level API. +func (s *ServerStreamForClient[Res]) Conn() (StreamingClientConn, error) { + return s.conn, s.constructErr +} + +// BidiStreamForClient is the client's view of a bidirectional streaming RPC. +// +// It's returned from [Client].CallBidiStream, but doesn't currently have an +// exported constructor function. +// +// Send and Receive may be called from separate goroutines concurrently, but +// neither may be called concurrently with itself. +type BidiStreamForClient[Req, Res any] struct { + conn StreamingClientConn + initializer maybeInitializer + // Error from client construction. If non-nil, return for all calls. + err error +} + +// Spec returns the specification for the RPC. +func (b *BidiStreamForClient[_, _]) Spec() Spec { + return b.conn.Spec() +} + +// Peer describes the server for the RPC. +func (b *BidiStreamForClient[_, _]) Peer() Peer { + return b.conn.Peer() +} + +// RequestHeader returns the request headers. Headers are sent with the first +// call to Send. +// +// Headers beginning with "Connect-" and "Grpc-" are reserved for use by the +// Connect and gRPC protocols. Applications shouldn't write them. +func (b *BidiStreamForClient[Req, Res]) RequestHeader() http.Header { + if b.err != nil { + return http.Header{} + } + return b.conn.RequestHeader() +} + +// Send a message to the server. The first call to Send also sends the request +// headers. To send just the request headers, without a body, call Send with a +// nil pointer. +// +// If the server returns an error, Send returns an error that wraps [io.EOF]. +// Clients should check for EOF using the standard library's [errors.Is] and +// call Receive to retrieve the error. +func (b *BidiStreamForClient[Req, Res]) Send(msg *Req) error { + if b.err != nil { + return b.err + } + if msg == nil { + return b.conn.Send(nil) + } + return b.conn.Send(msg) +} + +// CloseRequest closes the send side of the stream. +func (b *BidiStreamForClient[Req, Res]) CloseRequest() error { + if b.err != nil { + return b.err + } + return b.conn.CloseRequest() +} + +// Receive a message. When the server is done sending messages and no other +// errors have occurred, Receive will return an error that wraps [io.EOF]. +func (b *BidiStreamForClient[Req, Res]) Receive() (*Res, error) { + if b.err != nil { + return nil, b.err + } + var msg Res + if err := b.initializer.maybe(b.conn.Spec(), &msg); err != nil { + return nil, err + } + if err := b.conn.Receive(&msg); err != nil { + return nil, err + } + return &msg, nil +} + +// CloseResponse closes the receive side of the stream. +// +// CloseResponse is non-blocking. To gracefully close the stream and allow for +// connection resuse ensure all messages have been received before calling +// CloseResponse. All messages are received when Receive returns an error +// wrapping [io.EOF]. +func (b *BidiStreamForClient[Req, Res]) CloseResponse() error { + if b.err != nil { + return b.err + } + return b.conn.CloseResponse() +} + +// ResponseHeader returns the headers received from the server. It blocks until +// the first call to Receive returns. +func (b *BidiStreamForClient[Req, Res]) ResponseHeader() http.Header { + if b.err != nil { + return http.Header{} + } + return b.conn.ResponseHeader() +} + +// ResponseTrailer returns the trailers received from the server. Trailers +// aren't fully populated until Receive() returns an error wrapping [io.EOF]. +func (b *BidiStreamForClient[Req, Res]) ResponseTrailer() http.Header { + if b.err != nil { + return http.Header{} + } + return b.conn.ResponseTrailer() +} + +// Conn exposes the underlying StreamingClientConn. This may be useful if +// you'd prefer to wrap the connection in a different high-level API. +func (b *BidiStreamForClient[Req, Res]) Conn() (StreamingClientConn, error) { + return b.conn, b.err +} + +// BidiStreamForClientSimple is the client's view of a bidirectional streaming RPC. +// +// It's returned from [Client].CallBidiStream, but doesn't currently have an +// exported constructor function. +// +// Send and Receive may be called from separate goroutines concurrently, but +// neither may be called concurrently with itself. +type BidiStreamForClientSimple[Req, Res any] struct { + stream *BidiStreamForClient[Req, Res] +} + +// Spec returns the specification for the RPC. +func (b *BidiStreamForClientSimple[_, _]) Spec() Spec { + if b.stream == nil { + return Spec{} + } + return b.stream.Spec() +} + +// Peer describes the server for the RPC. +func (b *BidiStreamForClientSimple[_, _]) Peer() Peer { + if b.stream == nil { + return Peer{} + } + return b.stream.Peer() +} + +// Send a message to the server. The first call to Send also sends the request +// headers. To send just the request headers, without a body, call Send with a +// nil pointer. +// +// If the server returns an error, Send returns an error that wraps [io.EOF]. +// Clients should check for EOF using the standard library's [errors.Is] and +// call Receive to retrieve the error. +func (b *BidiStreamForClientSimple[Req, Res]) Send(msg *Req) error { + if b.stream == nil { + return errNoStreamInitialized + } + return b.stream.Send(msg) +} + +// CloseRequest closes the send side of the stream. +func (b *BidiStreamForClientSimple[Req, Res]) CloseRequest() error { + if b.stream == nil { + return errNoStreamInitialized + } + return b.stream.CloseRequest() +} + +// Receive a message. When the server is done sending messages and no other +// errors have occurred, Receive will return an error that wraps [io.EOF]. +func (b *BidiStreamForClientSimple[Req, Res]) Receive() (*Res, error) { + if b.stream == nil { + return nil, errNoStreamInitialized + } + return b.stream.Receive() +} + +// CloseResponse closes the receive side of the stream. +// +// CloseResponse is non-blocking. To gracefully close the stream and allow for +// connection resuse ensure all messages have been received before calling +// CloseResponse. All messages are received when Receive returns an error +// wrapping [io.EOF]. +func (b *BidiStreamForClientSimple[Req, Res]) CloseResponse() error { + if b.stream == nil { + return errNoStreamInitialized + } + return b.stream.CloseResponse() +} + +// ResponseHeader returns the headers received from the server. It blocks until +// the first call to Receive returns. +func (b *BidiStreamForClientSimple[Req, Res]) ResponseHeader() http.Header { + if b.stream == nil { + return make(http.Header) + } + return b.stream.ResponseHeader() +} + +// ResponseTrailer returns the trailers received from the server. Trailers +// aren't fully populated until Receive() returns an error wrapping [io.EOF]. +func (b *BidiStreamForClientSimple[Req, Res]) ResponseTrailer() http.Header { + if b.stream == nil { + return make(http.Header) + } + return b.stream.ResponseTrailer() +} diff --git a/vendor/connectrpc.com/connect/code.go b/vendor/connectrpc.com/connect/code.go new file mode 100644 index 0000000000..6677ae452a --- /dev/null +++ b/vendor/connectrpc.com/connect/code.go @@ -0,0 +1,226 @@ +// Copyright 2021-2026 The Connect Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package connect + +import ( + "fmt" + "strconv" + "strings" +) + +// A Code is one of the Connect protocol's error codes. There are no user-defined +// codes, so only the codes enumerated below are valid. In both name and +// semantics, these codes match the gRPC status codes. +// +// The descriptions below are optimized for brevity rather than completeness. +// See the [Connect protocol specification] for detailed descriptions of each +// code and example usage. +// +// [Connect protocol specification]: https://connectrpc.com/docs/protocol +type Code uint32 + +const ( + // The zero code in gRPC is OK, which indicates that the operation was a + // success. We don't define a constant for it because it overlaps awkwardly + // with Go's error semantics: what does it mean to have a non-nil error with + // an OK status? (Also, the Connect protocol doesn't use a code for + // successes.) + + // CodeCanceled indicates that the operation was canceled, typically by the + // caller. + CodeCanceled Code = 1 + + // CodeUnknown indicates that the operation failed for an unknown reason. + CodeUnknown Code = 2 + + // CodeInvalidArgument indicates that client supplied an invalid argument. + CodeInvalidArgument Code = 3 + + // CodeDeadlineExceeded indicates that deadline expired before the operation + // could complete. + CodeDeadlineExceeded Code = 4 + + // CodeNotFound indicates that some requested entity (for example, a file or + // directory) was not found. + CodeNotFound Code = 5 + + // CodeAlreadyExists indicates that client attempted to create an entity (for + // example, a file or directory) that already exists. + CodeAlreadyExists Code = 6 + + // CodePermissionDenied indicates that the caller doesn't have permission to + // execute the specified operation. + CodePermissionDenied Code = 7 + + // CodeResourceExhausted indicates that some resource has been exhausted. For + // example, a per-user quota may be exhausted or the entire file system may + // be full. + CodeResourceExhausted Code = 8 + + // CodeFailedPrecondition indicates that the system is not in a state + // required for the operation's execution. + CodeFailedPrecondition Code = 9 + + // CodeAborted indicates that operation was aborted by the system, usually + // because of a concurrency issue such as a sequencer check failure or + // transaction abort. + CodeAborted Code = 10 + + // CodeOutOfRange indicates that the operation was attempted past the valid + // range (for example, seeking past end-of-file). + CodeOutOfRange Code = 11 + + // CodeUnimplemented indicates that the operation isn't implemented, + // supported, or enabled in this service. + CodeUnimplemented Code = 12 + + // CodeInternal indicates that some invariants expected by the underlying + // system have been broken. This code is reserved for serious errors. + CodeInternal Code = 13 + + // CodeUnavailable indicates that the service is currently unavailable. This + // is usually temporary, so clients can back off and retry idempotent + // operations. + CodeUnavailable Code = 14 + + // CodeDataLoss indicates that the operation has resulted in unrecoverable + // data loss or corruption. + CodeDataLoss Code = 15 + + // CodeUnauthenticated indicates that the request does not have valid + // authentication credentials for the operation. + CodeUnauthenticated Code = 16 + + minCode = CodeCanceled + maxCode = CodeUnauthenticated +) + +func (c Code) String() string { + switch c { + case CodeCanceled: + return "canceled" + case CodeUnknown: + return "unknown" + case CodeInvalidArgument: + return "invalid_argument" + case CodeDeadlineExceeded: + return "deadline_exceeded" + case CodeNotFound: + return "not_found" + case CodeAlreadyExists: + return "already_exists" + case CodePermissionDenied: + return "permission_denied" + case CodeResourceExhausted: + return "resource_exhausted" + case CodeFailedPrecondition: + return "failed_precondition" + case CodeAborted: + return "aborted" + case CodeOutOfRange: + return "out_of_range" + case CodeUnimplemented: + return "unimplemented" + case CodeInternal: + return "internal" + case CodeUnavailable: + return "unavailable" + case CodeDataLoss: + return "data_loss" + case CodeUnauthenticated: + return "unauthenticated" + } + return fmt.Sprintf("code_%d", c) +} + +// MarshalText implements [encoding.TextMarshaler]. +func (c Code) MarshalText() ([]byte, error) { + return []byte(c.String()), nil +} + +// UnmarshalText implements [encoding.TextUnmarshaler]. +func (c *Code) UnmarshalText(data []byte) error { + dataStr := string(data) + switch dataStr { + case "canceled": + *c = CodeCanceled + return nil + case "unknown": + *c = CodeUnknown + return nil + case "invalid_argument": + *c = CodeInvalidArgument + return nil + case "deadline_exceeded": + *c = CodeDeadlineExceeded + return nil + case "not_found": + *c = CodeNotFound + return nil + case "already_exists": + *c = CodeAlreadyExists + return nil + case "permission_denied": + *c = CodePermissionDenied + return nil + case "resource_exhausted": + *c = CodeResourceExhausted + return nil + case "failed_precondition": + *c = CodeFailedPrecondition + return nil + case "aborted": + *c = CodeAborted + return nil + case "out_of_range": + *c = CodeOutOfRange + return nil + case "unimplemented": + *c = CodeUnimplemented + return nil + case "internal": + *c = CodeInternal + return nil + case "unavailable": + *c = CodeUnavailable + return nil + case "data_loss": + *c = CodeDataLoss + return nil + case "unauthenticated": + *c = CodeUnauthenticated + return nil + } + // Ensure that non-canonical codes round-trip through MarshalText and + // UnmarshalText. + if after, ok := strings.CutPrefix(dataStr, "code_"); ok { + dataStr = after + code, err := strconv.ParseUint(dataStr, 10 /* base */, 32 /* bitsize */) + if err == nil && (code < uint64(minCode) || code > uint64(maxCode)) { + *c = Code(code) + return nil + } + } + return fmt.Errorf("invalid code %q", dataStr) +} + +// CodeOf returns the error's status code if it is or wraps an [*Error] and +// [CodeUnknown] otherwise. +func CodeOf(err error) Code { + if connectErr, ok := asError(err); ok { + return connectErr.Code() + } + return CodeUnknown +} diff --git a/vendor/connectrpc.com/connect/codec.go b/vendor/connectrpc.com/connect/codec.go new file mode 100644 index 0000000000..58af6b8d2a --- /dev/null +++ b/vendor/connectrpc.com/connect/codec.go @@ -0,0 +1,259 @@ +// Copyright 2021-2026 The Connect Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package connect + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/runtime/protoiface" +) + +const ( + codecNameProto = "proto" + codecNameJSON = "json" + codecNameJSONCharsetUTF8 = codecNameJSON + "; charset=utf-8" +) + +// Codec marshals structs (typically generated from a schema) to and from bytes. +type Codec interface { + // Name returns the name of the Codec. + // + // This may be used as part of the Content-Type within HTTP. For example, + // with gRPC this is the content subtype, so "application/grpc+proto" will + // map to the Codec with name "proto". + // + // Names must not be empty. + Name() string + // Marshal marshals the given message. + // + // Marshal may expect a specific type of message, and will error if this type + // is not given. + Marshal(any) ([]byte, error) + // Unmarshal unmarshals the given message. + // + // Unmarshal may expect a specific type of message, and will error if this + // type is not given. + Unmarshal([]byte, any) error +} + +// marshalAppender is an extension to Codec for appending to a byte slice. +type marshalAppender interface { + Codec + + // MarshalAppend marshals the given message and appends it to the given + // byte slice. + // + // MarshalAppend may expect a specific type of message, and will error if + // this type is not given. + MarshalAppend([]byte, any) ([]byte, error) +} + +// stableCodec is an extension to Codec for serializing with stable output. +type stableCodec interface { + Codec + + // MarshalStable marshals the given message with stable field ordering. + // + // MarshalStable should return the same output for a given input. Although + // it is not guaranteed to be canonicalized, the marshalling routine for + // MarshalStable will opt for the most normalized output available for a + // given serialization. + // + // For practical reasons, it is possible for MarshalStable to return two + // different results for two inputs considered to be "equal" in their own + // domain, and it may change in the future with codec updates, but for + // any given concrete value and any given version, it should return the + // same output. + MarshalStable(any) ([]byte, error) + + // IsBinary returns true if the marshalled data is binary for this codec. + // + // If this function returns false, the data returned from Marshal and + // MarshalStable are considered valid text and may be used in contexts + // where text is expected. + IsBinary() bool +} + +type protoBinaryCodec struct{} + +var _ Codec = (*protoBinaryCodec)(nil) + +func (c *protoBinaryCodec) Name() string { return codecNameProto } + +func (c *protoBinaryCodec) Marshal(message any) ([]byte, error) { + protoMessage, ok := message.(proto.Message) + if !ok { + return nil, errNotProto(message) + } + return proto.Marshal(protoMessage) +} + +func (c *protoBinaryCodec) MarshalAppend(dst []byte, message any) ([]byte, error) { + protoMessage, ok := message.(proto.Message) + if !ok { + return nil, errNotProto(message) + } + return proto.MarshalOptions{}.MarshalAppend(dst, protoMessage) +} + +func (c *protoBinaryCodec) Unmarshal(data []byte, message any) error { + protoMessage, ok := message.(proto.Message) + if !ok { + return errNotProto(message) + } + err := proto.Unmarshal(data, protoMessage) + if err != nil { + return fmt.Errorf("unmarshal into %T: %w", message, err) + } + return nil +} + +func (c *protoBinaryCodec) MarshalStable(message any) ([]byte, error) { + protoMessage, ok := message.(proto.Message) + if !ok { + return nil, errNotProto(message) + } + // protobuf does not offer a canonical output today, so this format is not + // guaranteed to match deterministic output from other protobuf libraries. + // In addition, unknown fields may cause inconsistent output for otherwise + // equal messages. + // https://github.com/golang/protobuf/issues/1121 + options := proto.MarshalOptions{Deterministic: true} + return options.Marshal(protoMessage) +} + +func (c *protoBinaryCodec) IsBinary() bool { + return true +} + +type protoJSONCodec struct { + name string +} + +var _ Codec = (*protoJSONCodec)(nil) + +func (c *protoJSONCodec) Name() string { return c.name } + +func (c *protoJSONCodec) Marshal(message any) ([]byte, error) { + protoMessage, ok := message.(proto.Message) + if !ok { + return nil, errNotProto(message) + } + return protojson.MarshalOptions{}.Marshal(protoMessage) +} + +func (c *protoJSONCodec) MarshalAppend(dst []byte, message any) ([]byte, error) { + protoMessage, ok := message.(proto.Message) + if !ok { + return nil, errNotProto(message) + } + return protojson.MarshalOptions{}.MarshalAppend(dst, protoMessage) +} + +func (c *protoJSONCodec) Unmarshal(binary []byte, message any) error { + protoMessage, ok := message.(proto.Message) + if !ok { + return errNotProto(message) + } + if len(binary) == 0 { + return errors.New("zero-length payload is not a valid JSON object") + } + // Discard unknown fields so clients and servers aren't forced to always use + // exactly the same version of the schema. + options := protojson.UnmarshalOptions{DiscardUnknown: true} + err := options.Unmarshal(binary, protoMessage) + if err != nil { + return fmt.Errorf("unmarshal into %T: %w", message, err) + } + return nil +} + +func (c *protoJSONCodec) MarshalStable(message any) ([]byte, error) { + // protojson does not offer a "deterministic" field ordering, but fields + // are still ordered consistently by their index. However, protojson can + // output inconsistent whitespace for some reason, therefore it is + // suggested to use a formatter to ensure consistent formatting. + // https://github.com/golang/protobuf/issues/1373 + messageJSON, err := c.Marshal(message) + if err != nil { + return nil, err + } + compactedJSON := bytes.NewBuffer(messageJSON[:0]) + if err = json.Compact(compactedJSON, messageJSON); err != nil { + return nil, err + } + return compactedJSON.Bytes(), nil +} + +func (c *protoJSONCodec) IsBinary() bool { + return false +} + +// readOnlyCodecs is a read-only interface to a map of named codecs. +type readOnlyCodecs interface { + // Get gets the Codec with the given name. + Get(string) Codec + // Protobuf gets the user-supplied protobuf codec, falling back to the default + // implementation if necessary. + // + // This is helpful in the gRPC protocol, where the wire protocol requires + // marshaling protobuf structs to binary even if the RPC procedures were + // generated from a different IDL. + Protobuf() Codec + // Names returns a copy of the registered codec names. The returned slice is + // safe for the caller to mutate. + Names() []string +} + +func newReadOnlyCodecs(nameToCodec map[string]Codec) readOnlyCodecs { + return &codecMap{ + nameToCodec: nameToCodec, + } +} + +type codecMap struct { + nameToCodec map[string]Codec +} + +func (m *codecMap) Get(name string) Codec { + return m.nameToCodec[name] +} + +func (m *codecMap) Protobuf() Codec { + if pb, ok := m.nameToCodec[codecNameProto]; ok { + return pb + } + return &protoBinaryCodec{} +} + +func (m *codecMap) Names() []string { + names := make([]string, 0, len(m.nameToCodec)) + for name := range m.nameToCodec { + names = append(names, name) + } + return names +} + +func errNotProto(message any) error { + if _, ok := message.(protoiface.MessageV1); ok { + return fmt.Errorf("%T uses github.com/golang/protobuf, but connect-go only supports google.golang.org/protobuf: see https://go.dev/blog/protobuf-apiv2", message) + } + return fmt.Errorf("%T doesn't implement proto.Message", message) +} diff --git a/vendor/connectrpc.com/connect/compression.go b/vendor/connectrpc.com/connect/compression.go new file mode 100644 index 0000000000..588611bc57 --- /dev/null +++ b/vendor/connectrpc.com/connect/compression.go @@ -0,0 +1,224 @@ +// Copyright 2021-2026 The Connect Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package connect + +import ( + "bytes" + "errors" + "io" + "math" + "net/http" + "strings" + "sync" +) + +const ( + compressionGzip = "gzip" + compressionIdentity = "identity" +) + +// A Decompressor is a reusable wrapper that decompresses an underlying data +// source. The standard library's [*gzip.Reader] implements Decompressor. +type Decompressor interface { + io.Reader + + // Close closes the Decompressor, but not the underlying data source. It may + // return an error if the Decompressor wasn't read to EOF. + Close() error + + // Reset discards the Decompressor's internal state, if any, and prepares it + // to read from a new source of compressed data. + Reset(io.Reader) error +} + +// A Compressor is a reusable wrapper that compresses data written to an +// underlying sink. The standard library's [*gzip.Writer] implements Compressor. +type Compressor interface { + io.Writer + + // Close flushes any buffered data to the underlying sink, then closes the + // Compressor. It must not close the underlying sink. + Close() error + + // Reset discards the Compressor's internal state, if any, and prepares it to + // write compressed data to a new sink. + Reset(io.Writer) +} + +type compressionPool struct { + decompressors sync.Pool + compressors sync.Pool +} + +func newCompressionPool( + newDecompressor func() Decompressor, + newCompressor func() Compressor, +) *compressionPool { + if newDecompressor == nil && newCompressor == nil { + return nil + } + return &compressionPool{ + decompressors: sync.Pool{ + New: func() any { return newDecompressor() }, + }, + compressors: sync.Pool{ + New: func() any { return newCompressor() }, + }, + } +} + +func (c *compressionPool) Decompress(dst *bytes.Buffer, src *bytes.Buffer, readMaxBytes int64) *Error { + decompressor, err := c.getDecompressor(src) + if err != nil { + return errorf(CodeInvalidArgument, "get decompressor: %w", err) + } + reader := io.Reader(decompressor) + if readMaxBytes > 0 && readMaxBytes < math.MaxInt64 { + reader = io.LimitReader(decompressor, readMaxBytes+1) + } + bytesRead, err := dst.ReadFrom(reader) + if err != nil { + _ = c.putDecompressor(decompressor) + err = wrapIfContextError(err) + if connectErr, ok := asError(err); ok { + return connectErr + } + return errorf(CodeInvalidArgument, "decompress: %w", err) + } + if readMaxBytes > 0 && bytesRead > readMaxBytes { + discardedBytes, err := io.Copy(io.Discard, decompressor) + _ = c.putDecompressor(decompressor) + if err != nil { + return errorf(CodeResourceExhausted, "message is larger than configured max %d - unable to determine message size: %w", readMaxBytes, err) + } + return errorf(CodeResourceExhausted, "message size %d is larger than configured max %d", bytesRead+discardedBytes, readMaxBytes) + } + if err := c.putDecompressor(decompressor); err != nil { + return errorf(CodeUnknown, "recycle decompressor: %w", err) + } + return nil +} + +func (c *compressionPool) Compress(dst *bytes.Buffer, src *bytes.Buffer) *Error { + compressor, err := c.getCompressor(dst) + if err != nil { + return errorf(CodeUnknown, "get compressor: %w", err) + } + if _, err := src.WriteTo(compressor); err != nil { + _ = c.putCompressor(compressor) + err = wrapIfContextError(err) + if connectErr, ok := asError(err); ok { + return connectErr + } + return errorf(CodeInternal, "compress: %w", err) + } + if err := c.putCompressor(compressor); err != nil { + return errorf(CodeInternal, "recycle compressor: %w", err) + } + return nil +} + +func (c *compressionPool) getDecompressor(reader io.Reader) (Decompressor, error) { + decompressor, ok := c.decompressors.Get().(Decompressor) + if !ok { + return nil, errors.New("expected Decompressor, got incorrect type from pool") + } + return decompressor, decompressor.Reset(reader) +} + +func (c *compressionPool) putDecompressor(decompressor Decompressor) error { + if err := decompressor.Close(); err != nil { + return err + } + // While it's in the pool, we don't want the decompressor to retain a + // reference to the underlying reader. However, most decompressors attempt to + // read some header data from the new data source when Reset; since we don't + // know the compression format, we can't provide a valid header. Since we + // also reset the decompressor when it's pulled out of the pool, we can + // ignore errors here. + _ = decompressor.Reset(http.NoBody) + c.decompressors.Put(decompressor) + return nil +} + +func (c *compressionPool) getCompressor(writer io.Writer) (Compressor, error) { + compressor, ok := c.compressors.Get().(Compressor) + if !ok { + return nil, errors.New("expected Compressor, got incorrect type from pool") + } + compressor.Reset(writer) + return compressor, nil +} + +func (c *compressionPool) putCompressor(compressor Compressor) error { + if err := compressor.Close(); err != nil { + return err + } + compressor.Reset(io.Discard) // don't keep references + c.compressors.Put(compressor) + return nil +} + +// readOnlyCompressionPools is a read-only interface to a map of named +// compressionPools. +type readOnlyCompressionPools interface { + Get(string) *compressionPool + Contains(string) bool + // Wordy, but clarifies how this is different from readOnlyCodecs.Names(). + CommaSeparatedNames() string +} + +func newReadOnlyCompressionPools( + nameToPool map[string]*compressionPool, + reversedNames []string, +) readOnlyCompressionPools { + // Client and handler configs keep compression names in registration order, + // but we want the last registered to be the most preferred. + names := make([]string, 0, len(reversedNames)) + seen := make(map[string]struct{}, len(reversedNames)) + for i := len(reversedNames) - 1; i >= 0; i-- { + name := reversedNames[i] + if _, ok := seen[name]; ok { + continue + } + seen[name] = struct{}{} + names = append(names, name) + } + return &namedCompressionPools{ + nameToPool: nameToPool, + commaSeparatedNames: strings.Join(names, ","), + } +} + +type namedCompressionPools struct { + nameToPool map[string]*compressionPool + commaSeparatedNames string +} + +func (m *namedCompressionPools) Get(name string) *compressionPool { + if name == "" || name == compressionIdentity { + return nil + } + return m.nameToPool[name] +} + +func (m *namedCompressionPools) Contains(name string) bool { + _, ok := m.nameToPool[name] + return ok +} + +func (m *namedCompressionPools) CommaSeparatedNames() string { + return m.commaSeparatedNames +} diff --git a/vendor/connectrpc.com/connect/connect.go b/vendor/connectrpc.com/connect/connect.go new file mode 100644 index 0000000000..2f6dc01a94 --- /dev/null +++ b/vendor/connectrpc.com/connect/connect.go @@ -0,0 +1,499 @@ +// Copyright 2021-2026 The Connect Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package connect is a slim RPC framework built on Protocol Buffers and +// [net/http]. In addition to supporting its own protocol, Connect handlers and +// clients are wire-compatible with gRPC and gRPC-Web, including streaming. +// +// This documentation is intended to explain each type and function in +// isolation. Walkthroughs, FAQs, and other narrative docs are available on the +// [Connect website], and there's a working [demonstration service] on Github. +// +// [Connect website]: https://connectrpc.com +// [demonstration service]: https://github.com/connectrpc/examples-go +package connect + +import ( + "errors" + "fmt" + "io" + "net/http" + "net/url" +) + +// Version is the semantic version of the connect module. +const Version = "1.20.0" + +// These constants are used in compile-time handshakes with connect's generated +// code. +const ( + IsAtLeastVersion0_0_1 = true + IsAtLeastVersion0_1_0 = true + IsAtLeastVersion1_7_0 = true + IsAtLeastVersion1_13_0 = true +) + +// StreamType describes whether the client, server, neither, or both is +// streaming. +type StreamType uint8 + +const ( + StreamTypeUnary StreamType = 0b00 + StreamTypeClient StreamType = 0b01 + StreamTypeServer StreamType = 0b10 + StreamTypeBidi = StreamTypeClient | StreamTypeServer +) + +func (s StreamType) String() string { + switch s { + case StreamTypeUnary: + return "unary" + case StreamTypeClient: + return "client" + case StreamTypeServer: + return "server" + case StreamTypeBidi: + return "bidi" + } + return fmt.Sprintf("stream_%d", s) +} + +// StreamingHandlerConn is the server's view of a bidirectional message +// exchange. Interceptors for streaming RPCs may wrap StreamingHandlerConns. +// +// Like the standard library's [http.ResponseWriter], StreamingHandlerConns write +// response headers to the network with the first call to Send. Any subsequent +// mutations are effectively no-ops. Handlers may mutate response trailers at +// any time before returning. When the client has finished sending data, +// Receive returns an error wrapping [io.EOF]. Handlers should check for this +// using the standard library's [errors.Is]. +// +// Headers and trailers beginning with "Connect-" and "Grpc-" are reserved for +// use by the gRPC and Connect protocols: applications may read them but +// shouldn't write them. +// +// StreamingHandlerConn implementations provided by this module guarantee that +// all returned errors can be cast to [*Error] using the standard library's +// [errors.As]. +// +// StreamingHandlerConn implementations provided by this module support limited +// concurrent use: the read side (Receive, RequestHeader) may be called +// concurrently with the write side (Send, ResponseHeader, ResponseTrailer), but +// the read side must not be called concurrently with itself, and the write side +// must not be called concurrently with itself. +type StreamingHandlerConn interface { + Spec() Spec + Peer() Peer + + // Receive and RequestHeader form the read side of the stream. They are not + // safe to call concurrently with each other, but may be called concurrently + // with Send, ResponseHeader, and ResponseTrailer. + Receive(any) error + RequestHeader() http.Header + + // Send, ResponseHeader, and ResponseTrailer form the write side of the + // stream. They are not safe to call concurrently with each other, but may + // be called concurrently with Receive and RequestHeader. + Send(any) error + ResponseHeader() http.Header + ResponseTrailer() http.Header +} + +// StreamingClientConn is the client's view of a bidirectional message exchange. +// Interceptors for streaming RPCs may wrap StreamingClientConns. +// +// StreamingClientConns write request headers to the network with the first +// call to Send. Any subsequent mutations are effectively no-ops. When the +// server is done sending data, the StreamingClientConn's Receive method +// returns an error wrapping [io.EOF]. Clients should check for this using the +// standard library's [errors.Is]. If the server encounters an error during +// processing, subsequent calls to the StreamingClientConn's Send method will +// return an error wrapping [io.EOF]; clients may then call Receive to unmarshal +// the error. +// +// Headers and trailers beginning with "Connect-" and "Grpc-" are reserved for +// use by the gRPC and Connect protocols: applications may read them but +// shouldn't write them. +// +// StreamingClientConn implementations provided by this module guarantee that +// all returned errors can be cast to [*Error] using the standard library's +// [errors.As]. +// +// StreamingClientConn implementations provided by this module support limited +// concurrent use: the read side (Receive, ResponseHeader, ResponseTrailer, +// CloseResponse) may be called concurrently with the write side (Send, +// RequestHeader, CloseRequest), but the read side must not be called +// concurrently with itself, and the write side must not be called concurrently +// with itself. +type StreamingClientConn interface { + // Spec and Peer are safe to call concurrently with all other methods. + Spec() Spec + Peer() Peer + + // Send, RequestHeader, and CloseRequest form the write side of the stream. + // They are not safe to call concurrently with each other, but may be called + // concurrently with Receive, ResponseHeader, ResponseTrailer, and + // CloseResponse. + Send(any) error + RequestHeader() http.Header + CloseRequest() error + + // Receive, ResponseHeader, ResponseTrailer, and CloseResponse form the read + // side of the stream. They are not safe to call concurrently with each + // other, but may be called concurrently with Send, RequestHeader, and + // CloseRequest. + Receive(any) error + ResponseHeader() http.Header + ResponseTrailer() http.Header + CloseResponse() error +} + +// Request is a wrapper around a generated request message. It provides +// access to metadata like headers and the RPC specification, as well as +// strongly-typed access to the message itself. +type Request[T any] struct { + Msg *T + + spec Spec + peer Peer + header http.Header + method string +} + +// NewRequest wraps a generated request message. +func NewRequest[T any](message *T) *Request[T] { + return &Request[T]{ + Msg: message, + // Initialized lazily so we don't allocate unnecessarily. + header: nil, + } +} + +// Any returns the concrete request message as an empty interface, so that +// *Request implements the [AnyRequest] interface. +func (r *Request[_]) Any() any { + return r.Msg +} + +// Spec returns a description of this RPC. +func (r *Request[_]) Spec() Spec { + return r.spec +} + +// Peer describes the other party for this RPC. +func (r *Request[_]) Peer() Peer { + return r.peer +} + +// Header returns the HTTP headers for this request. Headers beginning with +// "Connect-" and "Grpc-" are reserved for use by the Connect and gRPC +// protocols: applications may read them but shouldn't write them. +func (r *Request[_]) Header() http.Header { + if r.header == nil { + r.header = make(http.Header) + } + return r.header +} + +// HTTPMethod returns the HTTP method for this request. This is nearly always +// POST, but side-effect-free unary RPCs could be made via a GET. +// +// On a newly created request, via NewRequest, this will return the empty +// string until the actual request is actually sent and the HTTP method +// determined. This means that client interceptor functions will see the +// empty string until *after* they delegate to the handler they wrapped. It +// is even possible for this to return the empty string after such delegation, +// if the request was never actually sent to the server (and thus no +// determination ever made about the HTTP method). +func (r *Request[_]) HTTPMethod() string { + return r.method +} + +// internalOnly implements AnyRequest. +func (r *Request[_]) internalOnly() {} + +// setRequestMethod sets the request method to the given value. +func (r *Request[_]) setRequestMethod(method string) { + r.method = method +} + +// AnyRequest is the common method set of every [Request], regardless of type +// parameter. It's used in unary interceptors. +// +// Headers and trailers beginning with "Connect-" and "Grpc-" are reserved for +// use by the gRPC and Connect protocols: applications may read them but +// shouldn't write them. +// +// To preserve our ability to add methods to this interface without breaking +// backward compatibility, only types defined in this package can implement +// AnyRequest. +type AnyRequest interface { + Any() any + Spec() Spec + Peer() Peer + Header() http.Header + HTTPMethod() string + + internalOnly() + setRequestMethod(string) +} + +// Response is a wrapper around a generated response message. It provides +// access to metadata like headers and trailers, as well as strongly-typed +// access to the message itself. +type Response[T any] struct { + Msg *T + + header http.Header + trailer http.Header +} + +// NewResponse wraps a generated response message. +func NewResponse[T any](message *T) *Response[T] { + return &Response[T]{ + Msg: message, + // Initialized lazily so we don't allocate unnecessarily. + header: nil, + trailer: nil, + } +} + +// Any returns the concrete response message as an empty interface, so that +// *Response implements the [AnyResponse] interface. +func (r *Response[_]) Any() any { + return r.Msg +} + +// Header returns the HTTP headers for this response. Headers beginning with +// "Connect-" and "Grpc-" are reserved for use by the Connect and gRPC +// protocols: applications may read them but shouldn't write them. +func (r *Response[_]) Header() http.Header { + if r.header == nil { + r.header = make(http.Header) + } + return r.header +} + +// Trailer returns the trailers for this response. Depending on the underlying +// RPC protocol, trailers may be sent as HTTP trailers or a protocol-specific +// block of in-body metadata. +// +// Trailers beginning with "Connect-" and "Grpc-" are reserved for use by the +// Connect and gRPC protocols: applications may read them but shouldn't write +// them. +func (r *Response[_]) Trailer() http.Header { + if r.trailer == nil { + r.trailer = make(http.Header) + } + return r.trailer +} + +// internalOnly implements AnyResponse. +func (r *Response[_]) internalOnly() {} + +// AnyResponse is the common method set of every [Response], regardless of type +// parameter. It's used in unary interceptors. +// +// Headers and trailers beginning with "Connect-" and "Grpc-" are reserved for +// use by the gRPC and Connect protocols: applications may read them but +// shouldn't write them. +// +// To preserve our ability to add methods to this interface without breaking +// backward compatibility, only types defined in this package can implement +// AnyResponse. +type AnyResponse interface { + Any() any + Header() http.Header + Trailer() http.Header + + internalOnly() +} + +// HTTPClient is the interface connect expects HTTP clients to implement. The +// standard library's *http.Client implements HTTPClient. +type HTTPClient interface { + Do(*http.Request) (*http.Response, error) +} + +// Spec is a description of a client call or a handler invocation. +// +// If you're using Protobuf, protoc-gen-connect-go generates a constant for the +// fully-qualified Procedure corresponding to each RPC in your schema. +type Spec struct { + StreamType StreamType + Schema any // for protobuf RPCs, a protoreflect.MethodDescriptor + Procedure string // for example, "/acme.foo.v1.FooService/Bar" + IsClient bool // otherwise we're in a handler + IdempotencyLevel IdempotencyLevel +} + +// Peer describes the other party to an RPC. +// +// When accessed client-side, Addr contains the host or host:port from the +// server's URL. When accessed server-side, Addr contains the client's address +// in IP:port format. +// +// On both the client and the server, Protocol is the RPC protocol in use. +// Currently, it's either [ProtocolConnect], [ProtocolGRPC], or +// [ProtocolGRPCWeb], but additional protocols may be added in the future. +// +// Query contains the query parameters for the request. For the server, this +// will reflect the actual query parameters sent. For the client, it is unset. +type Peer struct { + Addr string + Protocol string + Query url.Values // server-only +} + +func newPeerForURL(url *url.URL, protocol string) Peer { + return Peer{ + Addr: url.Host, + Protocol: protocol, + } +} + +// handlerConnCloser extends StreamingHandlerConn with a method for handlers to +// terminate the message exchange (and optionally send an error to the client). +type handlerConnCloser interface { + StreamingHandlerConn + + Close(error) error +} + +// receiveConn represents the shared methods of both StreamingClientConn and StreamingHandlerConn +// that the below helper functions use for implementing the rules around a "unary" stream, that +// is expected to have exactly one message (or zero messages followed by a non-EOF error). +type receiveConn interface { + Spec() Spec + Receive(any) error +} + +// hasHTTPMethod is implemented by streaming connections that support HTTP methods other than +// POST. +type hasHTTPMethod interface { + getHTTPMethod() string +} + +// errStreamingClientConn is a sentinel error implementation of StreamingClientConn. +type errStreamingClientConn struct { + err error +} + +func (c *errStreamingClientConn) Receive(msg any) error { + return c.err +} + +func (c *errStreamingClientConn) Spec() Spec { + return Spec{} +} + +func (c *errStreamingClientConn) Peer() Peer { + return Peer{} +} + +func (c *errStreamingClientConn) Send(msg any) error { + return c.err +} + +func (c *errStreamingClientConn) CloseRequest() error { + return c.err +} + +func (c *errStreamingClientConn) CloseResponse() error { + return c.err +} + +func (c *errStreamingClientConn) RequestHeader() http.Header { + return make(http.Header) +} + +func (c *errStreamingClientConn) ResponseHeader() http.Header { + return make(http.Header) +} + +func (c *errStreamingClientConn) ResponseTrailer() http.Header { + return make(http.Header) +} + +// receiveUnaryResponse unmarshals a message from a StreamingClientConn, then +// envelopes the message and attaches headers and trailers. It attempts to +// consume the response stream and isn't appropriate when receiving multiple +// messages. +func receiveUnaryResponse[T any](conn StreamingClientConn, initializer maybeInitializer) (*Response[T], error) { + msg, err := receiveUnaryMessage[T](conn, initializer, "response") + if err != nil { + return nil, err + } + return &Response[T]{ + Msg: msg, + header: conn.ResponseHeader(), + trailer: conn.ResponseTrailer(), + }, nil +} + +// receiveUnaryRequest unmarshals a message from a StreamingClientConn, then +// envelopes the message and attaches headers and other request properties. It +// attempts to consume the request stream and isn't appropriate when receiving +// multiple messages. +func receiveUnaryRequest[T any](conn StreamingHandlerConn, initializer maybeInitializer) (*Request[T], error) { + msg, err := receiveUnaryMessage[T](conn, initializer, "request") + if err != nil { + return nil, err + } + method := http.MethodPost + if hasRequestMethod, ok := conn.(hasHTTPMethod); ok { + method = hasRequestMethod.getHTTPMethod() + } + return &Request[T]{ + Msg: msg, + spec: conn.Spec(), + peer: conn.Peer(), + header: conn.RequestHeader(), + method: method, + }, nil +} + +func receiveUnaryMessage[T any](conn receiveConn, initializer maybeInitializer, what string) (*T, error) { + var msg T + if err := initializer.maybe(conn.Spec(), &msg); err != nil { + return nil, err + } + // Possibly counter-intuitive, but the gRPC specs about error codes state that both clients + // and servers should return "unimplemented" when they encounter a cardinality violation: where + // the number of messages in the stream is wrong. Search for "cardinality violation" in the + // following docs: + // https://grpc.github.io/grpc/core/md_doc_statuscodes.html + if err := conn.Receive(&msg); err != nil { + if errors.Is(err, io.EOF) { + err = NewError(CodeUnimplemented, fmt.Errorf("unary %s has zero messages", what)) + } + return nil, err + } + // In a well-formed stream, the one message must be the only content in the body. + // To verify that it is well-formed, try to read another message from the stream. + // TODO: optimize this second receive: ideally do it w/out allocation, w/out + // fully reading next message (if one is present), and w/out trying to + // actually unmarshal the bytes) + var msg2 T + if err := initializer.maybe(conn.Spec(), &msg2); err != nil { + return nil, err + } + if err := conn.Receive(&msg2); !errors.Is(err, io.EOF) { + if err == nil { + err = NewError(CodeUnimplemented, fmt.Errorf("unary %s has multiple messages", what)) + } + return nil, err + } + return &msg, nil +} diff --git a/vendor/connectrpc.com/connect/context.go b/vendor/connectrpc.com/connect/context.go new file mode 100644 index 0000000000..32f265de18 --- /dev/null +++ b/vendor/connectrpc.com/connect/context.go @@ -0,0 +1,243 @@ +// Copyright 2021-2026 The Connect Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package connect + +import ( + "context" + "net/http" +) + +// CallInfo represents information relevant to an RPC call. +type CallInfo interface { + // Spec returns a description of this call. + Spec() Spec + // Peer describes the other party for this call. + Peer() Peer + // RequestHeader returns the HTTP headers for this request. Headers beginning with + // "Connect-" and "Grpc-" are reserved for use by the Connect and gRPC + // protocols: applications may read them but shouldn't write them. + RequestHeader() http.Header + // ResponseHeader returns the HTTP headers for this response. Headers beginning with + // "Connect-" and "Grpc-" are reserved for use by the Connect and gRPC + // protocols: applications may read them but shouldn't write them. + // On the client side, this method returns nil before + // the call is actually made. After the call is made, for streaming operations, + // this method will block for the server to actually return response headers. + ResponseHeader() http.Header + // ResponseTrailer returns the trailers for this response. Depending on the underlying + // RPC protocol, trailers may be sent as HTTP trailers or a protocol-specific + // block of in-body metadata. + // + // Trailers beginning with "Connect-" and "Grpc-" are reserved for use by the + // Connect and gRPC protocols: applications may read them but shouldn't write + // them. + // + // On the client side, this method returns nil before the call is actually made. + // After the call is made, for streaming operations, this method will block + // for the server to actually return response trailers. + ResponseTrailer() http.Header + // HTTPMethod returns the HTTP method for this request. This is nearly always + // POST, but side-effect-free unary RPCs could be made via a GET. + // + // On a newly created request, via NewRequest, this will return the empty + // string until the actual request is actually sent and the HTTP method + // determined. This means that client interceptor functions will see the + // empty string until *after* they delegate to the handler they wrapped. It + // is even possible for this to return the empty string after such delegation, + // if the request was never actually sent to the server (and thus no + // determination ever made about the HTTP method). + HTTPMethod() string + + internalOnly() +} + +// NewClientContext creates a new client (i.e. outgoing) context for use from a +// client. When the returned context is passed to RPCs, the returned call info +// can be used to set request metadata before the RPC is invoked and to inspect +// response metadata after the RPC completes. +// +// The returned context may be re-used across RPCs as long as they are +// not concurrent. Results of all CallInfo methods other than +// RequestHeader() are undefined if the context is used with concurrent RPCs. +func NewClientContext(ctx context.Context) (context.Context, CallInfo) { + info := &clientCallInfo{} + return context.WithValue(ctx, clientCallInfoContextKey{}, info), info +} + +// CallInfoForHandlerContext returns the CallInfo for the given handler (i.e. incoming) context, if there is one. +func CallInfoForHandlerContext(ctx context.Context) (CallInfo, bool) { + value, ok := ctx.Value(handlerCallInfoContextKey{}).(CallInfo) + return value, ok +} + +// handlerCallInfo is a CallInfo implementation used for unary handlers. +type handlerCallInfo struct { + spec Spec + peer Peer + method string + requestHeader http.Header + responseHeader http.Header + responseTrailer http.Header +} + +func (c *handlerCallInfo) Spec() Spec { + return c.spec +} + +func (c *handlerCallInfo) Peer() Peer { + return c.peer +} + +func (c *handlerCallInfo) RequestHeader() http.Header { + if c.requestHeader == nil { + c.requestHeader = make(http.Header) + } + return c.requestHeader +} + +func (c *handlerCallInfo) ResponseHeader() http.Header { + if c.responseHeader == nil { + c.responseHeader = make(http.Header) + } + return c.responseHeader +} + +func (c *handlerCallInfo) ResponseTrailer() http.Header { + if c.responseTrailer == nil { + c.responseTrailer = make(http.Header) + } + return c.responseTrailer +} + +func (c *handlerCallInfo) HTTPMethod() string { + return c.method +} + +// internalOnly implements CallInfo. +func (c *handlerCallInfo) internalOnly() {} + +// streamingHandlerCallInfo is a CallInfo implementation used for streaming RPC handlers. +type streamingHandlerCallInfo struct { + conn StreamingHandlerConn +} + +func (c *streamingHandlerCallInfo) Spec() Spec { + return c.conn.Spec() +} + +func (c *streamingHandlerCallInfo) Peer() Peer { + return c.conn.Peer() +} + +func (c *streamingHandlerCallInfo) RequestHeader() http.Header { + return c.conn.RequestHeader() +} + +func (c *streamingHandlerCallInfo) ResponseHeader() http.Header { + return c.conn.ResponseHeader() +} + +func (c *streamingHandlerCallInfo) ResponseTrailer() http.Header { + return c.conn.ResponseTrailer() +} + +func (c *streamingHandlerCallInfo) HTTPMethod() string { + // All stream calls are POSTs + return http.MethodPost +} + +// internalOnly implements CallInfo. +func (c *streamingHandlerCallInfo) internalOnly() {} + +// clientCallInfo is a CallInfo implementation used for clients. +type clientCallInfo struct { + responseSource + + spec Spec + peer Peer + method string + requestHeader http.Header +} + +func (c *clientCallInfo) Spec() Spec { + return c.spec +} + +func (c *clientCallInfo) Peer() Peer { + return c.peer +} + +func (c *clientCallInfo) RequestHeader() http.Header { + if c.requestHeader == nil { + c.requestHeader = make(http.Header) + } + return c.requestHeader +} + +func (c *clientCallInfo) ResponseHeader() http.Header { + if c.responseSource == nil { + return nil + } + return c.responseSource.ResponseHeader() +} + +func (c *clientCallInfo) ResponseTrailer() http.Header { + if c.responseSource == nil { + return nil + } + return c.responseSource.ResponseTrailer() +} + +func (c *clientCallInfo) HTTPMethod() string { + return c.method +} + +// internalOnly implements CallInfo. +func (c *clientCallInfo) internalOnly() {} + +// clientCallInfoContextKey is the key used to store client call info in context. +type clientCallInfoContextKey struct{} + +// sentinelContextKey is the key used to store a copy of client call info in context +// when a request is made. +// Each step in an interceptor chain compares the actual call info with the +// sentinel call info. If the two values are different, the request will +// return an error in the interceptor. +// This protects against changing the call info in interceptors, which is prohibited +// as it would allow users to modify call info mid-flight independent of the actual +// request or response. +// Users who wish to modify call info data such as headers and trailers should instead +// use Connect [Request] and [Response] wrapper types. +type sentinelContextKey struct{} + +// handlerCallInfoContextKey is the key used to store handler call info in context. +type handlerCallInfoContextKey struct{} + +// responseSource indicates a type that manages response headers and trailers. +type responseSource interface { + ResponseHeader() http.Header + ResponseTrailer() http.Header +} + +// clientCallInfoForContext gets the call info from a client/outgoing context. +func clientCallInfoForContext(ctx context.Context) (*clientCallInfo, bool) { + info, ok := ctx.Value(clientCallInfoContextKey{}).(*clientCallInfo) + return info, ok +} + +// newHandlerContext creates a new handler/incoming context. +func newHandlerContext(ctx context.Context, info CallInfo) context.Context { + return context.WithValue(ctx, handlerCallInfoContextKey{}, info) +} diff --git a/vendor/connectrpc.com/connect/duplex_http_call.go b/vendor/connectrpc.com/connect/duplex_http_call.go new file mode 100644 index 0000000000..807dcc780d --- /dev/null +++ b/vendor/connectrpc.com/connect/duplex_http_call.go @@ -0,0 +1,481 @@ +// Copyright 2021-2026 The Connect Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package connect + +import ( + "context" + "errors" + "io" + "net/http" + "net/url" + "sync" + "sync/atomic" +) + +// duplexHTTPCall is a full-duplex stream between the client and server. The +// request body is the stream from client to server, and the response body is +// the reverse. +// +// Be warned: we need to use some lesser-known APIs to do this with net/http. +type duplexHTTPCall struct { + ctx context.Context + httpClient HTTPClient + streamType StreamType + onRequestSend func(*http.Request) + validateResponse func(*http.Response) *Error + + // requestBodyWriter streams the request body for client-streaming and bidi + // RPCs. Assigned once in newDuplexHTTPCall and never reassigned, so it is + // safe to read without synchronisation. Nil for unary and server-streaming. + requestBodyWriter *io.PipeWriter + + // requestSent ensures we only send the request once. + requestSent atomic.Bool + request *http.Request + + // responseReady is closed when the response is ready or when the request + // fails. Any error on request initialisation will be set on the + // responseErr. There's always a response if responseErr is nil. + responseReady chan struct{} + response *http.Response + responseErr error +} + +func newDuplexHTTPCall( + ctx context.Context, + httpClient HTTPClient, + url *url.URL, + spec Spec, + header http.Header, +) *duplexHTTPCall { + // ensure we make a copy of the url before we pass along to the + // Request. This ensures if a transport out of our control wants + // to mutate the req.URL, we don't feel the effects of it. + url = cloneURL(url) + + // This is mirroring what http.NewRequestContext did, but + // using an already parsed url.URL object, rather than a string + // and parsing it again. This is a bit funny with HTTP/1.1 + // explicitly, but this is logic copied over from + // NewRequestContext and doesn't effect the actual version + // being transmitted. + request := (&http.Request{ + Method: http.MethodPost, + URL: url, + Header: header, + Proto: "HTTP/1.1", + ProtoMajor: 1, + ProtoMinor: 1, + Body: http.NoBody, + GetBody: getNoBody, + Host: url.Host, + }).WithContext(ctx) + duplex := &duplexHTTPCall{ + ctx: ctx, + httpClient: httpClient, + streamType: spec.StreamType, + request: request, + responseReady: make(chan struct{}), + } + // Client-streaming and bidi RPCs stream the request body through an + // io.Pipe. Set it up here so requestBodyWriter is assigned once at + // construction and safe to read concurrently from Send and CloseWrite. + if spec.StreamType&StreamTypeClient != 0 { + pipeReader, pipeWriter := io.Pipe() + duplex.requestBodyWriter = pipeWriter + duplex.request.Body = pipeReader + duplex.request.GetBody = nil // GetBody is not supported for client streaming. + duplex.request.ContentLength = -1 + } + return duplex +} + +// Send sends a message to the server. +func (d *duplexHTTPCall) Send(payload messagePayload) (int64, error) { + if d.streamType&StreamTypeClient == 0 { + return d.sendUnary(payload) + } + isFirst := d.requestSent.CompareAndSwap(false, true) + if isFirst { + // This is the first time we're sending a message to the server. The + // request body pipe has already been set up by newDuplexHTTPCall, so + // we just kick off the HTTP request here. + go d.makeRequest() // concurrent request + } + if err := d.ctx.Err(); err != nil { + return 0, wrapIfContextError(err) + } + if isFirst && payload.Len() == 0 { + // On first write a nil Send is used to send request headers. Avoid + // writing a zero-length payload to avoid superfluous errors with close. + return 0, nil + } + // It's safe to write to this side of the pipe while net/http concurrently + // reads from the other side. + bytesWritten, err := payload.WriteTo(d.requestBodyWriter) + if err != nil && errors.Is(err, io.ErrClosedPipe) { + // Signal that the stream is closed with the more-typical io.EOF instead of + // io.ErrClosedPipe. This makes it easier for protocol-specific wrappers to + // match grpc-go's behavior. + err = io.EOF + } + return bytesWritten, err +} + +func (d *duplexHTTPCall) sendUnary(payload messagePayload) (int64, error) { + // Unary messages are sent as a single HTTP request. We don't need to use a + // pipe for the request body and we don't need to send headers separately. + if !d.requestSent.CompareAndSwap(false, true) { + return 0, errors.New("request already sent") + } + payloadLength := int64(payload.Len()) + if payloadLength > 0 { + // Build the request body from the payload. + payloadBody := newPayloadCloser(payload) + d.request.Body = payloadBody + d.request.ContentLength = payloadLength + d.request.GetBody = func() (io.ReadCloser, error) { + if !payloadBody.Rewind() { + return nil, errors.New("payload cannot be retried") + } + return payloadBody, nil + } + // Release the payload ensuring that after Send returns the + // payload is safe to be reused. See [http.RoundTripper] for + // more details. + defer payloadBody.Release() + } + d.makeRequest() // synchronous request + if d.responseErr != nil { + // Check on response errors for context errors. Other errors are + // handled on read. + if err := d.ctx.Err(); err != nil { + return 0, wrapIfContextError(err) + } + } + return payloadLength, nil +} + +// CloseWrite closes the request body. Callers *must* call CloseWrite before Read when +// using HTTP/1.x. +func (d *duplexHTTPCall) CloseWrite() error { + // Even if Write was never called, we need to make an HTTP request. This + // ensures that we've sent any headers to the server and that we have an HTTP + // response to read from. + if d.requestSent.CompareAndSwap(false, true) { + go d.makeRequest() + } + // The user calls CloseWrite to indicate that they're done sending data. It's + // safe to close the write side of the pipe while net/http is reading from + // it. + // + // Because connect also supports some RPC types over HTTP/1.1, we need to be + // careful how we expose this method to users. HTTP/1.1 doesn't support + // bidirectional streaming - the write side of the stream (aka request body) + // must be closed before we start reading the response or we'll just block + // forever. To make sure users don't have to worry about this, the generated + // code for unary, client streaming, and server streaming RPCs must call + // CloseWrite automatically rather than requiring the user to do it. + // + // For client-streaming and bidi RPCs requestBodyWriter is always non-nil + // (it is set in newDuplexHTTPCall); for unary and server-streaming RPCs + // it remains nil and we fall back to closing whatever request body was + // attached (typically http.NoBody or a payloadCloser set by sendUnary). + if d.requestBodyWriter != nil { + return d.requestBodyWriter.Close() + } + return d.request.Body.Close() +} + +// Header returns the HTTP request headers. +func (d *duplexHTTPCall) Header() http.Header { + return d.request.Header +} + +// Trailer returns the HTTP request trailers. +func (d *duplexHTTPCall) Trailer() http.Header { + return d.request.Trailer +} + +// URL returns the URL for the request. +func (d *duplexHTTPCall) URL() *url.URL { + return d.request.URL +} + +// Method returns the HTTP method for the request (GET or POST). +func (d *duplexHTTPCall) Method() string { + return d.request.Method +} + +// SetMethod changes the method of the request before it is sent. +func (d *duplexHTTPCall) SetMethod(method string) { + d.request.Method = method +} + +// Read from the response body. Returns the first error passed to SetError. +func (d *duplexHTTPCall) Read(data []byte) (int, error) { + // First, we wait until we've gotten the response headers and established the + // server-to-client side of the stream. + if err := d.BlockUntilResponseReady(); err != nil { + // The stream is already closed or corrupted. + return 0, err + } + // Before we read, check if the context has been canceled. + if err := d.ctx.Err(); err != nil { + return 0, wrapIfContextError(err) + } + n, err := d.response.Body.Read(data) + if err != nil && !errors.Is(err, io.EOF) { + err = wrapIfContextDone(d.ctx, err) + err = wrapIfRSTError(d.ctx, err) + } + return n, err +} + +func (d *duplexHTTPCall) CloseRead() error { + _ = d.BlockUntilResponseReady() + if d.response == nil { + return nil + } + err := d.response.Body.Close() + err = wrapIfContextDone(d.ctx, err) + return wrapIfRSTError(d.ctx, err) +} + +// ResponseStatusCode is the response's HTTP status code. +func (d *duplexHTTPCall) ResponseStatusCode() (int, error) { + if err := d.BlockUntilResponseReady(); err != nil { + return 0, err + } + return d.response.StatusCode, nil +} + +// ResponseHeader returns the response HTTP headers. +func (d *duplexHTTPCall) ResponseHeader() http.Header { + _ = d.BlockUntilResponseReady() + if d.response != nil { + return d.response.Header + } + return make(http.Header) +} + +// ResponseTrailer returns the response HTTP trailers. +func (d *duplexHTTPCall) ResponseTrailer() http.Header { + _ = d.BlockUntilResponseReady() + if d.response != nil { + return d.response.Trailer + } + return make(http.Header) +} + +// SetValidateResponse sets the response validation function. The function runs +// in a background goroutine. +func (d *duplexHTTPCall) SetValidateResponse(validate func(*http.Response) *Error) { + d.validateResponse = validate +} + +// BlockUntilResponseReady returns when the response is ready or reports an +// error from initializing the request. +func (d *duplexHTTPCall) BlockUntilResponseReady() error { + <-d.responseReady + return d.responseErr +} + +func (d *duplexHTTPCall) makeRequest() { + // This runs concurrently with Write and CloseWrite. Read and CloseRead wait + // on d.responseReady, so we can't race with them. + defer close(d.responseReady) + + // Promote the header Host to the request object. + if host := getHeaderCanonical(d.request.Header, headerHost); len(host) > 0 { + d.request.Host = host + } + if d.onRequestSend != nil { + d.onRequestSend(d.request) + } + // Once we send a message to the server, they send a message back and + // establish the receive side of the stream. + // On error, we close the request body using the Write side of the pipe. + // This ensures HTTP2 streams receive an io.EOF from the Read side of the + // pipe. Write's check for io.ErrClosedPipe and will convert this to io.EOF. + response, err := d.httpClient.Do(d.request) //nolint:bodyclose + if err != nil { + if errors.Is(err, io.EOF) { + // We use io.EOF as a sentinel in many places and don't want this + // transport error to be confused for those other situations. + err = io.ErrUnexpectedEOF + } + err = wrapIfContextError(err) + err = wrapIfLikelyH2CNotConfiguredError(d.request, err) + err = wrapIfLikelyWithGRPCNotUsedError(err) + err = wrapIfRSTError(d.ctx, err) + if _, ok := asError(err); !ok { + err = NewError(CodeUnavailable, err) + } + d.responseErr = err + _ = d.CloseWrite() + return + } + // We've got a response. We can now read from the response body. + // Closing the response body is delegated to the caller even on error. + d.response = response + if err := d.validateResponse(response); err != nil { + d.responseErr = err + _ = d.CloseWrite() + return + } + if (d.streamType&StreamTypeBidi) == StreamTypeBidi && response.ProtoMajor < 2 { + // If we somehow dialed an HTTP/1.x server, fail with an explicit message + // rather than returning a more cryptic error later on. + d.responseErr = errorf( + CodeUnimplemented, + "response from %v is HTTP/%d.%d: bidi streams require at least HTTP/2", + d.request.URL, + response.ProtoMajor, + response.ProtoMinor, + ) + _ = d.CloseWrite() + } +} + +// getNoBody is a GetBody function for http.NoBody. +func getNoBody() (io.ReadCloser, error) { + return http.NoBody, nil +} + +// messagePayload is a sized and seekable message payload. The interface is +// implemented by [*bytes.Reader] and *envelope. Reads must be non-blocking. +type messagePayload interface { + io.Reader + io.WriterTo + io.Seeker + Len() int +} + +// nopPayload is a message payload that does nothing. It's used to send headers +// to the server. +type nopPayload struct{} + +var _ messagePayload = nopPayload{} + +func (nopPayload) Read([]byte) (int, error) { + return 0, io.EOF +} + +func (nopPayload) WriteTo(io.Writer) (int64, error) { + return 0, nil +} + +func (nopPayload) Seek(int64, int) (int64, error) { + return 0, nil +} + +func (nopPayload) Len() int { + return 0 +} + +// messageSender sends a message payload. The interface is implemented by +// [*duplexHTTPCall] and writeSender. +type messageSender interface { + Send(messagePayload) (int64, error) +} + +// writeSender is a sender that writes to an [io.Writer]. Useful for wrapping +// [http.ResponseWriter]. +type writeSender struct { + writer io.Writer +} + +var _ messageSender = writeSender{} + +func (w writeSender) Send(payload messagePayload) (int64, error) { + return payload.WriteTo(w.writer) +} + +// See: https://cs.opensource.google/go/go/+/refs/tags/go1.20.1:src/net/http/clone.go;l=22-33 +func cloneURL(oldURL *url.URL) *url.URL { + if oldURL == nil { + return nil + } + newURL := new(url.URL) + *newURL = *oldURL + if oldURL.User != nil { + newURL.User = new(url.Userinfo) + *newURL.User = *oldURL.User + } + return newURL +} + +// payloadCloser is an [io.ReadCloser] that wraps a messagePayload. It's used to +// implement the request body for unary calls. To safely reuse the buffer +// call Release after the response is received to ensure the payload is safe for +// reuse. +type payloadCloser struct { + mu sync.Mutex + payload messagePayload // nil after Release +} + +func newPayloadCloser(payload messagePayload) *payloadCloser { + return &payloadCloser{ + payload: payload, + } +} + +// Read implements [io.Reader]. +func (p *payloadCloser) Read(dst []byte) (readN int, err error) { + p.mu.Lock() + defer p.mu.Unlock() + if p.payload == nil { + return 0, io.EOF + } + return p.payload.Read(dst) +} + +// WriteTo implements [io.WriterTo]. +func (p *payloadCloser) WriteTo(dst io.Writer) (int64, error) { + p.mu.Lock() + defer p.mu.Unlock() + if p.payload == nil { + return 0, nil + } + return p.payload.WriteTo(dst) +} + +// Close implements [io.Closer]. +func (p *payloadCloser) Close() error { + return nil +} + +// Rewind rewinds the payload to the beginning. It returns false if the +// payload has been discarded from a previous call to Release. +func (p *payloadCloser) Rewind() bool { + p.mu.Lock() + defer p.mu.Unlock() + if p.payload == nil { + return false + } + if _, err := p.payload.Seek(0, io.SeekStart); err != nil { + return false + } + return true +} + +// Release discards the payload. After Release is called, the payload cannot be +// rewound and the payload is safe to reuse. +func (p *payloadCloser) Release() { + p.mu.Lock() + p.payload = nil + p.mu.Unlock() +} diff --git a/vendor/connectrpc.com/connect/envelope.go b/vendor/connectrpc.com/connect/envelope.go new file mode 100644 index 0000000000..dd5e5b49ee --- /dev/null +++ b/vendor/connectrpc.com/connect/envelope.go @@ -0,0 +1,387 @@ +// Copyright 2021-2026 The Connect Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package connect + +import ( + "bytes" + "context" + "encoding/binary" + "errors" + "fmt" + "io" + "math" +) + +// flagEnvelopeCompressed indicates that the data is compressed. It has the +// same meaning in the gRPC-Web, gRPC-HTTP2, and Connect protocols. +const flagEnvelopeCompressed = 0b00000001 + +var errSpecialEnvelope = errorf( + CodeUnknown, + "final message has protocol-specific flags: %w", + // User code checks for end of stream with errors.Is(err, io.EOF). + io.EOF, +) + +// envelope is a block of arbitrary bytes wrapped in gRPC and Connect's framing +// protocol. +// +// Each message is preceded by a 5-byte prefix. The first byte is a uint8 used +// as a set of bitwise flags, and the remainder is a uint32 indicating the +// message length. gRPC and Connect interpret the bitwise flags differently, so +// envelope leaves their interpretation up to the caller. +type envelope struct { + Data *bytes.Buffer + Flags uint8 + offset int64 +} + +var _ messagePayload = (*envelope)(nil) + +func (e *envelope) IsSet(flag uint8) bool { + return e.Flags&flag == flag +} + +// Read implements [io.Reader]. +func (e *envelope) Read(data []byte) (readN int, err error) { + if e.offset < 5 { + prefix, err := makeEnvelopePrefix(e.Flags, e.Data.Len()) + if err != nil { + return 0, err + } + readN = copy(data, prefix[e.offset:]) + e.offset += int64(readN) + if e.offset < 5 { + return readN, nil + } + data = data[readN:] + } + n := copy(data, e.Data.Bytes()[e.offset-5:]) + e.offset += int64(n) + readN += n + if readN == 0 && e.offset == int64(e.Data.Len()+5) { + err = io.EOF + } + return readN, err +} + +// WriteTo implements [io.WriterTo]. +func (e *envelope) WriteTo(dst io.Writer) (wroteN int64, err error) { + if e.offset < 5 { + prefix, err := makeEnvelopePrefix(e.Flags, e.Data.Len()) + if err != nil { + return 0, err + } + prefixN, err := dst.Write(prefix[e.offset:]) + e.offset += int64(prefixN) + wroteN += int64(prefixN) + if e.offset < 5 { + return wroteN, err + } + } + n, err := dst.Write(e.Data.Bytes()[e.offset-5:]) + e.offset += int64(n) + wroteN += int64(n) + return wroteN, err +} + +// Seek implements [io.Seeker]. Based on the implementation of [bytes.Reader]. +func (e *envelope) Seek(offset int64, whence int) (int64, error) { + var abs int64 + switch whence { + case io.SeekStart: + abs = offset + case io.SeekCurrent: + abs = e.offset + offset + case io.SeekEnd: + abs = int64(e.Data.Len()) + offset + default: + return 0, errors.New("connect.envelope.Seek: invalid whence") + } + if abs < 0 { + return 0, errors.New("connect.envelope.Seek: negative position") + } + e.offset = abs + return abs, nil +} + +// Len returns the number of bytes of the unread portion of the envelope. +func (e *envelope) Len() int { + if length := int(int64(e.Data.Len()) + 5 - e.offset); length > 0 { + return length + } + return 0 +} + +type envelopeWriter struct { + ctx context.Context //nolint:containedctx + sender messageSender + codec Codec + compressMinBytes int + compressionPool *compressionPool + bufferPool *bufferPool + sendMaxBytes int +} + +func (w *envelopeWriter) Marshal(message any) *Error { + if message == nil { + // Send no-op message to create the request and send headers. + payload := nopPayload{} + if _, err := w.sender.Send(payload); err != nil { + if connectErr, ok := asError(err); ok { + return connectErr + } + return NewError(CodeUnknown, err) + } + return nil + } + if appender, ok := w.codec.(marshalAppender); ok { + return w.marshalAppend(message, appender) + } + return w.marshal(message) +} + +// Write writes the enveloped message, compressing as necessary. It doesn't +// retain any references to the supplied envelope or its underlying data. +func (w *envelopeWriter) Write(env *envelope) *Error { + if env.IsSet(flagEnvelopeCompressed) || + w.compressionPool == nil || + env.Data.Len() < w.compressMinBytes { + if w.sendMaxBytes > 0 && env.Data.Len() > w.sendMaxBytes { + return errorf(CodeResourceExhausted, "message size %d exceeds sendMaxBytes %d", env.Data.Len(), w.sendMaxBytes) + } + return w.write(env) + } + data := w.bufferPool.Get() + defer w.bufferPool.Put(data) + if err := w.compressionPool.Compress(data, env.Data); err != nil { + return err + } + if w.sendMaxBytes > 0 && data.Len() > w.sendMaxBytes { + return errorf(CodeResourceExhausted, "compressed message size %d exceeds sendMaxBytes %d", data.Len(), w.sendMaxBytes) + } + return w.write(&envelope{ + Data: data, + Flags: env.Flags | flagEnvelopeCompressed, + }) +} + +func (w *envelopeWriter) marshalAppend(message any, codec marshalAppender) *Error { + // Codec supports MarshalAppend; try to re-use a []byte from the pool. + buffer := w.bufferPool.Get() + defer w.bufferPool.Put(buffer) + raw, err := codec.MarshalAppend(buffer.Bytes(), message) + if err != nil { + return errorf(CodeInternal, "marshal message: %w", err) + } + if cap(raw) > buffer.Cap() { + // The buffer from the pool was too small, so MarshalAppend grew the slice. + // Pessimistically assume that the too-small buffer is insufficient for the + // application workload, so there's no point in keeping it in the pool. + // Instead, replace it with the larger, newly-allocated slice. This + // allocates, but it's a small, constant-size allocation. + *buffer = *bytes.NewBuffer(raw) + } else { + // MarshalAppend didn't allocate, but we need to fix the internal state of + // the buffer. Compared to replacing the buffer (as above), buffer.Write + // copies but avoids allocating. + buffer.Write(raw) + } + envelope := &envelope{Data: buffer} + return w.Write(envelope) +} + +func (w *envelopeWriter) marshal(message any) *Error { + // Codec doesn't support MarshalAppend; let Marshal allocate a []byte. + raw, err := w.codec.Marshal(message) + if err != nil { + return errorf(CodeInternal, "marshal message: %w", err) + } + buffer := bytes.NewBuffer(raw) + // Put our new []byte into the pool for later reuse. + defer w.bufferPool.Put(buffer) + envelope := &envelope{Data: buffer} + return w.Write(envelope) +} + +func (w *envelopeWriter) write(env *envelope) *Error { + if _, err := w.sender.Send(env); err != nil { + err = wrapIfContextDone(w.ctx, err) + if connectErr, ok := asError(err); ok { + return connectErr + } + return errorf(CodeUnknown, "write envelope: %w", err) + } + return nil +} + +type envelopeReader struct { + ctx context.Context //nolint:containedctx + reader io.Reader + bytesRead int64 // detect trailers-only gRPC responses + codec Codec + last envelope + compressionPool *compressionPool + bufferPool *bufferPool + readMaxBytes int +} + +func (r *envelopeReader) Unmarshal(message any) *Error { + buffer := r.bufferPool.Get() + var dontRelease *bytes.Buffer + defer func() { + if buffer != dontRelease { + r.bufferPool.Put(buffer) + } + }() + + env := &envelope{Data: buffer} + err := r.Read(env) + switch { + case err == nil && env.IsSet(flagEnvelopeCompressed) && r.compressionPool == nil: + return errorf( + CodeInternal, + "protocol error: sent compressed message without compression support", + ) + case err == nil && + (env.Flags == 0 || env.Flags == flagEnvelopeCompressed) && + env.Data.Len() == 0: + // This is a standard message (because none of the top 7 bits are set) and + // there's no data, so the zero value of the message is correct. + return nil + case err != nil && errors.Is(err, io.EOF): + // The stream has ended. Propagate the EOF to the caller. + return err + case err != nil: + // Something's wrong. + return err + } + + data := env.Data + if data.Len() > 0 && env.IsSet(flagEnvelopeCompressed) { + decompressed := r.bufferPool.Get() + defer func() { + if decompressed != dontRelease { + r.bufferPool.Put(decompressed) + } + }() + if err := r.compressionPool.Decompress(decompressed, data, int64(r.readMaxBytes)); err != nil { + return err + } + data = decompressed + } + + if env.Flags != 0 && env.Flags != flagEnvelopeCompressed { + // Drain the rest of the stream to ensure there is no extra data. + numBytes, err := discard(r.reader) + r.bytesRead += numBytes + if err != nil { + err = wrapIfContextError(err) + if connErr, ok := asError(err); ok { + return connErr + } + return errorf(CodeInternal, "corrupt response: I/O error after end-stream message: %w", err) + } else if numBytes > 0 { + return errorf(CodeInternal, "corrupt response: %d extra bytes after end of stream", numBytes) + } + // One of the protocol-specific flags are set, so this is the end of the + // stream. Save the message for protocol-specific code to process and + // return a sentinel error. We alias the buffer with dontRelease as a + // way of marking it so above defers don't release it to the pool. + r.last = envelope{ + Data: data, + Flags: env.Flags, + } + dontRelease = data + return errSpecialEnvelope + } + + if err := r.codec.Unmarshal(data.Bytes(), message); err != nil { + return errorf(CodeInvalidArgument, "unmarshal message: %w", err) + } + return nil +} + +func (r *envelopeReader) Read(env *envelope) *Error { + prefixes := [5]byte{} + // io.ReadFull reads the number of bytes requested, or returns an error. + // io.EOF will only be returned if no bytes were read. + n, err := io.ReadFull(r.reader, prefixes[:]) + r.bytesRead += int64(n) + if err != nil { + if errors.Is(err, io.EOF) { + // The stream ended cleanly. That's expected, but we need to propagate an EOF + // to the user so that they know that the stream has ended. We shouldn't + // add any alarming text about protocol errors, though. + return NewError(CodeUnknown, err) + } + err = wrapIfMaxBytesError(err, "read 5 byte message prefix") + err = wrapIfContextDone(r.ctx, err) + if connectErr, ok := asError(err); ok { + return connectErr + } + // Something else has gone wrong - the stream didn't end cleanly. + return errorf( + CodeInvalidArgument, + "protocol error: incomplete envelope: %w", err, + ) + } + size := int64(binary.BigEndian.Uint32(prefixes[1:5])) + if r.readMaxBytes > 0 && size > int64(r.readMaxBytes) { + n, err := io.CopyN(io.Discard, r.reader, size) + r.bytesRead += n + if err != nil && !errors.Is(err, io.EOF) { + return errorf(CodeResourceExhausted, "message is larger than configured max %d - unable to determine message size: %w", r.readMaxBytes, err) + } + return errorf(CodeResourceExhausted, "message size %d is larger than configured max %d", size, r.readMaxBytes) + } + // We've read the prefix, so we know how many bytes to expect. + // CopyN will return an error if it doesn't read the requested + // number of bytes. + readN, err := io.CopyN(env.Data, r.reader, size) + r.bytesRead += readN + if err != nil { + if errors.Is(err, io.EOF) { + // We've gotten fewer bytes than we expected, so the stream has ended + // unexpectedly. + return errorf( + CodeInvalidArgument, + "protocol error: promised %d bytes in enveloped message, got %d bytes", + size, + readN, + ) + } + err = wrapIfMaxBytesError(err, "read %d byte message", size) + err = wrapIfContextDone(r.ctx, err) + if connectErr, ok := asError(err); ok { + return connectErr + } + return errorf(CodeUnknown, "read enveloped message: %w", err) + } + env.Flags = prefixes[0] + return nil +} + +func makeEnvelopePrefix(flags uint8, size int) ([5]byte, error) { + // Cast as 64-bit to ensure comparison works on all architectures. + size64 := int64(size) + if size64 < 0 || size64 > math.MaxUint32 { + return [5]byte{}, fmt.Errorf("connect.makeEnvelopePrefix: size %d out of bounds", size) + } + prefix := [5]byte{} + prefix[0] = flags + binary.BigEndian.PutUint32(prefix[1:5], uint32(size64)) + return prefix, nil +} diff --git a/vendor/connectrpc.com/connect/error.go b/vendor/connectrpc.com/connect/error.go new file mode 100644 index 0000000000..9f0d356eb6 --- /dev/null +++ b/vendor/connectrpc.com/connect/error.go @@ -0,0 +1,471 @@ +// Copyright 2021-2026 The Connect Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package connect + +import ( + "context" + "errors" + "fmt" + "net/http" + "net/url" + "os" + "strings" + "time" + + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/anypb" +) + +const ( + commonErrorsURL = "https://connectrpc.com/docs/go/common-errors" + defaultAnyResolverPrefix = "type.googleapis.com/" +) + +var ( + // errNotModified signals Connect-protocol responses to GET requests to use the + // 304 Not Modified HTTP error code. + errNotModified = errors.New("not modified") + // errNotModifiedClient wraps ErrNotModified for use client-side. + errNotModifiedClient = fmt.Errorf("HTTP 304: %w", errNotModified) +) + +// An ErrorDetail is a self-describing Protobuf message attached to an [*Error]. +// Error details are sent over the network to clients, which can then work with +// strongly-typed data rather than trying to parse a complex error message. For +// example, you might use details to send a localized error message or retry +// parameters to the client. +// +// The [google.golang.org/genproto/googleapis/rpc/errdetails] package contains a +// variety of Protobuf messages commonly used as error details. +type ErrorDetail struct { + pbAny *anypb.Any + pbInner proto.Message // if nil, must be extracted from pbAny + wireJSON string // preserve human-readable JSON +} + +// NewErrorDetail constructs a new error detail. If msg is an *[anypb.Any] then +// it is used as is. Otherwise, it is first marshalled into an *[anypb.Any] +// value. This returns an error if msg cannot be marshalled. +func NewErrorDetail(msg proto.Message) (*ErrorDetail, error) { + // If it's already an Any, don't wrap it inside another. + if pb, ok := msg.(*anypb.Any); ok { + return &ErrorDetail{pbAny: pb}, nil + } + pb, err := anypb.New(msg) + if err != nil { + return nil, err + } + return &ErrorDetail{pbAny: pb, pbInner: msg}, nil +} + +// Type is the fully-qualified name of the detail's Protobuf message (for +// example, acme.foo.v1.FooDetail). +func (d *ErrorDetail) Type() string { + // proto.Any tries to make messages self-describing by using type URLs rather + // than plain type names, but there aren't any descriptor registries + // deployed. With the current state of the `Any` code, it's not possible to + // build a useful type registry either. To hide this from users, we should + // trim the URL prefix is added to the type name. + // + // If we ever want to support remote registries, we can add an explicit + // `TypeURL` method. + return typeNameForURL(d.pbAny.GetTypeUrl()) +} + +// Bytes returns a copy of the Protobuf-serialized detail. +func (d *ErrorDetail) Bytes() []byte { + out := make([]byte, len(d.pbAny.GetValue())) + copy(out, d.pbAny.GetValue()) + return out +} + +// Value uses the Protobuf runtime's package-global registry to unmarshal the +// Detail into a strongly-typed message. Typically, clients use Go type +// assertions to cast from the proto.Message interface to concrete types. +func (d *ErrorDetail) Value() (proto.Message, error) { + if d.pbInner != nil { + // We clone it so that if the caller mutates the returned value, + // they don't inadvertently corrupt this error detail value. + return proto.Clone(d.pbInner), nil + } + return d.pbAny.UnmarshalNew() +} + +// An Error captures four key pieces of information: a [Code], an underlying Go +// error, a map of metadata, and an optional collection of arbitrary Protobuf +// messages called "details" (more on those below). Servers send the code, the +// underlying error's Error() output, the metadata, and details over the wire +// to clients. Remember that the underlying error's message will be sent to +// clients - take care not to leak sensitive information from public APIs! +// +// Service implementations and interceptors should return errors that can be +// cast to an [*Error] (using the standard library's [errors.As]). If the returned +// error can't be cast to an [*Error], connect will use [CodeUnknown] and the +// returned error's message. +// +// Error details are an optional mechanism for servers, interceptors, and +// proxies to attach arbitrary Protobuf messages to the error code and message. +// They're a clearer and more performant alternative to HTTP header +// microformats. See [the documentation on errors] for more details. +// +// [the documentation on errors]: https://connectrpc.com/docs/go/errors +type Error struct { + code Code + err error + details []*ErrorDetail + meta http.Header + wireErr bool +} + +// NewError annotates any Go error with a status code. +func NewError(c Code, underlying error) *Error { + return &Error{code: c, err: underlying} +} + +// NewWireError is similar to [NewError], but the resulting *Error returns true +// when tested with [IsWireError]. +// +// This is useful for clients trying to propagate partial failures from +// streaming RPCs. Often, these RPCs include error information in their +// response messages (for example, [gRPC server reflection] and +// OpenTelemetry's [OTLP]). Clients propagating these errors up the stack +// should use NewWireError to clarify that the error code, message, and details +// (if any) were explicitly sent by the server rather than inferred from a +// lower-level networking error or timeout. +// +// [gRPC server reflection]: https://github.com/grpc/grpc/blob/v1.49.2/src/proto/grpc/reflection/v1alpha/reflection.proto#L132-L136 +// [OTLP]: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/otlp.md#partial-success +func NewWireError(c Code, underlying error) *Error { + err := NewError(c, underlying) + err.wireErr = true + return err +} + +// IsWireError checks whether the error was returned by the server, as opposed +// to being synthesized by the client. +// +// Clients may find this useful when deciding how to propagate errors. For +// example, an RPC-to-HTTP proxy might expose a server-sent CodeUnknown as an +// HTTP 500 but a client-synthesized CodeUnknown as a 503. +// +// Handlers will strip [Error.Meta] headers propagated from wire errors to avoid +// leaking response headers. To propagate headers recreate the error as a +// non-wire error. +func IsWireError(err error) bool { + se := new(Error) + if !errors.As(err, &se) { + return false + } + return se.wireErr +} + +// NewNotModifiedError indicates that the requested resource hasn't changed. It +// should be used only when handlers wish to respond to conditional HTTP GET +// requests with a 304 Not Modified. In all other circumstances, including all +// RPCs using the gRPC or gRPC-Web protocols, it's equivalent to sending an +// error with [CodeUnknown]. The supplied headers should include Etag, +// Cache-Control, or any other headers required by [RFC 9110 § 15.4.5]. +// +// Clients should check for this error using [IsNotModifiedError]. +// +// [RFC 9110 § 15.4.5]: https://httpwg.org/specs/rfc9110.html#status.304 +func NewNotModifiedError(headers http.Header) *Error { + err := NewError(CodeUnknown, errNotModified) + if headers != nil { + err.meta = headers + } + return err +} + +func (e *Error) Error() string { + message := e.Message() + if message == "" { + return e.code.String() + } + return e.code.String() + ": " + message +} + +// Message returns the underlying error message. It may be empty if the +// original error was created with a status code and a nil error. +func (e *Error) Message() string { + if e.err != nil { + return e.err.Error() + } + return "" +} + +// Unwrap allows [errors.Is] and [errors.As] access to the underlying error. +func (e *Error) Unwrap() error { + return e.err +} + +// Code returns the error's status code. +func (e *Error) Code() Code { + return e.code +} + +// Details returns the error's details. +func (e *Error) Details() []*ErrorDetail { + return e.details +} + +// AddDetail appends to the error's details. +func (e *Error) AddDetail(d *ErrorDetail) { + e.details = append(e.details, d) +} + +// Meta allows the error to carry additional information as key-value pairs. +// +// Protocol-specific headers and trailers may be removed to avoid breaking +// protocol semantics. For example, Content-Length and Content-Type headers +// won't be propagated. See the documentation for each protocol for more +// datails. +// +// When clients receive errors, the metadata contains the union of the HTTP +// headers and the protocol-specific trailers (either HTTP trailers or in-body +// metadata). +func (e *Error) Meta() http.Header { + if e.meta == nil { + e.meta = make(http.Header) + } + return e.meta +} + +func (e *Error) detailsAsAny() []*anypb.Any { + anys := make([]*anypb.Any, 0, len(e.details)) + for _, detail := range e.details { + anys = append(anys, detail.pbAny) + } + return anys +} + +// IsNotModifiedError checks whether the supplied error indicates that the +// requested resource hasn't changed. It only returns true if the server used +// [NewNotModifiedError] in response to a Connect-protocol RPC made with an +// HTTP GET. +func IsNotModifiedError(err error) bool { + return errors.Is(err, errNotModified) +} + +// errorf calls fmt.Errorf with the supplied template and arguments, then wraps +// the resulting error. +func errorf(c Code, template string, args ...any) *Error { + return NewError(c, fmt.Errorf(template, args...)) +} + +// asError uses errors.As to unwrap any error and look for a connect *Error. +func asError(err error) (*Error, bool) { + var connectErr *Error + ok := errors.As(err, &connectErr) + return connectErr, ok +} + +// wrapIfUncoded ensures that all errors are wrapped. It leaves already-wrapped +// errors unchanged, uses wrapIfContextError to apply codes to context.Canceled +// and context.DeadlineExceeded, and falls back to wrapping other errors with +// CodeUnknown. +func wrapIfUncoded(err error) error { + if err == nil { + return nil + } + maybeCodedErr := wrapIfContextError(err) + if _, ok := asError(maybeCodedErr); ok { + return maybeCodedErr + } + return NewError(CodeUnknown, maybeCodedErr) +} + +// wrapIfContextError applies CodeCanceled or CodeDeadlineExceeded to Go's +// context.Canceled and context.DeadlineExceeded errors, but only if they +// haven't already been wrapped. +func wrapIfContextError(err error) error { + if err == nil { + return nil + } + if _, ok := asError(err); ok { + return err + } + if errors.Is(err, context.Canceled) { + return NewError(CodeCanceled, err) + } + if errors.Is(err, context.DeadlineExceeded) { + return NewError(CodeDeadlineExceeded, err) + } + // Ick, some dial errors can be returned as os.ErrDeadlineExceeded + // instead of context.DeadlineExceeded :( + // https://github.com/golang/go/issues/64449 + if errors.Is(err, os.ErrDeadlineExceeded) { + return NewError(CodeDeadlineExceeded, err) + } + return err +} + +// wrapIfContextDone wraps errors with CodeCanceled or CodeDeadlineExceeded +// if the context is done. It leaves already-wrapped errors unchanged. +func wrapIfContextDone(ctx context.Context, err error) error { + if err == nil { + return nil + } + err = wrapIfContextError(err) + if _, ok := asError(err); ok { + return err + } + ctxErr := ctx.Err() + if errors.Is(ctxErr, context.Canceled) { + return NewError(CodeCanceled, err) + } else if errors.Is(ctxErr, context.DeadlineExceeded) { + return NewError(CodeDeadlineExceeded, err) + } + return err +} + +// wrapIfLikelyH2CNotConfiguredError adds a wrapping error that has a message +// telling the caller that they likely need to use h2c but are using a raw http.Client{}. +// +// This happens when running a gRPC-only server. +// This is fragile and may break over time, and this should be considered a best-effort. +func wrapIfLikelyH2CNotConfiguredError(request *http.Request, err error) error { + if err == nil { + return nil + } + if _, ok := asError(err); ok { + return err + } + if url := request.URL; url != nil && url.Scheme != "http" { + // If the scheme is not http, we definitely do not have an h2c error, so just return. + return err + } + // net/http code has been investigated and there is no typing of any of these errors + // they are all created with fmt.Errorf + // grpc-go returns the first error 2/3-3/4 of the time, and the second error 1/4-1/3 of the time + if errString := err.Error(); strings.HasPrefix(errString, `Post "`) && + (strings.Contains(errString, `net/http: HTTP/1.x transport connection broken: malformed HTTP response`) || + strings.HasSuffix(errString, `write: broken pipe`)) { + return fmt.Errorf("possible h2c configuration issue when talking to gRPC server, see %s: %w", commonErrorsURL, err) + } + return err +} + +// wrapIfLikelyWithGRPCNotUsedError adds a wrapping error that has a message +// telling the caller that they likely forgot to use connect.WithGRPC(). +// +// This happens when running a gRPC-only server. +// This is fragile and may break over time, and this should be considered a best-effort. +func wrapIfLikelyWithGRPCNotUsedError(err error) error { + if err == nil { + return nil + } + if _, ok := asError(err); ok { + return err + } + // golang.org/x/net code has been investigated and there is no typing of this error + // it is created with fmt.Errorf + // http2/transport.go:573: return nil, fmt.Errorf("http2: Transport: cannot retry err [%v] after Request.Body was written; define Request.GetBody to avoid this error", err) + if errString := err.Error(); strings.HasPrefix(errString, `Post "`) && + strings.Contains(errString, `http2: Transport: cannot retry err`) && + strings.HasSuffix(errString, `after Request.Body was written; define Request.GetBody to avoid this error`) { + return fmt.Errorf("possible missing connect.WithGPRC() client option when talking to gRPC server, see %s: %w", commonErrorsURL, err) + } + return err +} + +// HTTP/2 has its own set of error codes, which it sends in RST_STREAM frames. +// When the server sends one of these errors, we should map it back into our +// RPC error codes following +// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#http2-transport-mapping. +// +// This would be vastly simpler if we were using x/net/http2 directly, since +// the StreamError type is exported. When x/net/http2 gets vendored into +// net/http, though, all these types become unexported...so we're left with +// string munging. +func wrapIfRSTError(ctx context.Context, err error) error { + const ( + streamErrPrefix = "stream error: " + fromPeerSuffix = "; received from peer" + ) + if err == nil { + return nil + } + if _, ok := asError(err); ok { + return err + } + if urlErr := new(url.Error); errors.As(err, &urlErr) { + // If we get an RST_STREAM error from http.Client.Do, it's wrapped in a + // *url.Error. + err = urlErr.Unwrap() + } + msg := err.Error() + if !strings.HasPrefix(msg, streamErrPrefix) { + return err + } + if !strings.HasSuffix(msg, fromPeerSuffix) { + return err + } + msg = strings.TrimSuffix(msg, fromPeerSuffix) + i := strings.LastIndex(msg, ";") + if i < 0 || i >= len(msg)-1 { + return err + } + msg = msg[i+1:] + msg = strings.TrimSpace(msg) + switch msg { + case "NO_ERROR", "PROTOCOL_ERROR", "INTERNAL_ERROR", "FLOW_CONTROL_ERROR", + "SETTINGS_TIMEOUT", "FRAME_SIZE_ERROR", "COMPRESSION_ERROR", "CONNECT_ERROR": + return NewError(CodeInternal, err) + case "REFUSED_STREAM": + return NewError(CodeUnavailable, err) + case "CANCEL": + if deadline, ok := ctx.Deadline(); ok && time.Now().After(deadline) { + // Some server implementations will cancel the HTTP/2 stream with + // a RST_STREAM frame when they observe that the client's deadline + // has elapsed. + // We don't inspect ctx.Err() because we could be racing with the + // timer goroutine that is setting it. But there is no race when + // directly inspecting the context's deadline. In fact, if we get + // here, we have likely already examined ctx.Err() in a prior call + // to wrapIfContextError but observed a nil error and then fell + // through to here. + return NewError(CodeDeadlineExceeded, err) + } + return NewError(CodeCanceled, err) + case "ENHANCE_YOUR_CALM": + return NewError(CodeResourceExhausted, fmt.Errorf("bandwidth exhausted: %w", err)) + case "INADEQUATE_SECURITY": + return NewError(CodePermissionDenied, fmt.Errorf("transport protocol insecure: %w", err)) + default: + return err + } +} + +// wrapIfMaxBytesError wraps errors returned reading from a http.MaxBytesHandler +// whose limit has been exceeded. +func wrapIfMaxBytesError(err error, tmpl string, args ...any) error { + if err == nil { + return nil + } + if _, ok := asError(err); ok { + return err + } + var maxBytesErr *http.MaxBytesError + if ok := errors.As(err, &maxBytesErr); !ok { + return err + } + prefix := fmt.Sprintf(tmpl, args...) + return errorf(CodeResourceExhausted, "%s: exceeded %d byte http.MaxBytesReader limit", prefix, maxBytesErr.Limit) +} + +func typeNameForURL(url string) string { + return url[strings.LastIndexByte(url, '/')+1:] +} diff --git a/vendor/connectrpc.com/connect/error_writer.go b/vendor/connectrpc.com/connect/error_writer.go new file mode 100644 index 0000000000..92aeed6603 --- /dev/null +++ b/vendor/connectrpc.com/connect/error_writer.go @@ -0,0 +1,179 @@ +// Copyright 2021-2026 The Connect Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package connect + +import ( + "encoding/json" + "fmt" + "net/http" + "strings" +) + +// protocolType is one of the supported RPC protocols. +type protocolType uint8 + +const ( + unknownProtocol protocolType = iota + connectUnaryProtocol + connectStreamProtocol + grpcProtocol + grpcWebProtocol +) + +// An ErrorWriter writes errors to an [http.ResponseWriter] in the format +// expected by an RPC client. This is especially useful in server-side net/http +// middleware, where you may wish to handle requests from RPC and non-RPC +// clients with the same code. +// +// ErrorWriters are safe to use concurrently. +type ErrorWriter struct { + bufferPool *bufferPool + protobuf Codec + requireConnectProtocolHeader bool +} + +// NewErrorWriter constructs an ErrorWriter. Handler options may be passed to +// configure the error writer behaviour to match the handlers. +// [WithRequireConnectProtocolHeader] will assert that Connect protocol +// requests include the version header allowing the error writer to correctly +// classify the request. +// Options supplied via [WithConditionalHandlerOptions] are ignored. +func NewErrorWriter(opts ...HandlerOption) *ErrorWriter { + config := newHandlerConfig("", StreamTypeUnary, opts) + codecs := newReadOnlyCodecs(config.Codecs) + return &ErrorWriter{ + bufferPool: config.BufferPool, + protobuf: codecs.Protobuf(), + requireConnectProtocolHeader: config.RequireConnectProtocolHeader, + } +} + +func (w *ErrorWriter) classifyRequest(request *http.Request) protocolType { + ctype := canonicalizeContentType(getHeaderCanonical(request.Header, headerContentType)) + isPost := request.Method == http.MethodPost + isGet := request.Method == http.MethodGet + switch { + case isPost && (ctype == grpcContentTypeDefault || strings.HasPrefix(ctype, grpcContentTypePrefix)): + return grpcProtocol + case isPost && (ctype == grpcWebContentTypeDefault || strings.HasPrefix(ctype, grpcWebContentTypePrefix)): + return grpcWebProtocol + case isPost && strings.HasPrefix(ctype, connectStreamingContentTypePrefix): + // Streaming ignores the requireConnectProtocolHeader option as the + // Content-Type is enough to determine the protocol. + if err := connectCheckProtocolVersion(request, false /* required */); err != nil { + return unknownProtocol + } + return connectStreamProtocol + case isPost && strings.HasPrefix(ctype, connectUnaryContentTypePrefix): + if err := connectCheckProtocolVersion(request, w.requireConnectProtocolHeader); err != nil { + return unknownProtocol + } + return connectUnaryProtocol + case isGet: + if err := connectCheckProtocolVersion(request, w.requireConnectProtocolHeader); err != nil { + return unknownProtocol + } + return connectUnaryProtocol + default: + return unknownProtocol + } +} + +// IsSupported checks whether a request is using one of the ErrorWriter's +// supported RPC protocols. +func (w *ErrorWriter) IsSupported(request *http.Request) bool { + return w.classifyRequest(request) != unknownProtocol +} + +// Write an error, using the format appropriate for the RPC protocol in use. +// Callers should first use IsSupported to verify that the request is using one +// of the ErrorWriter's supported RPC protocols. If the protocol is unknown, +// Write will send the error as unprefixed, Connect-formatted JSON. +// +// Write does not read or close the request body. +func (w *ErrorWriter) Write(response http.ResponseWriter, request *http.Request, err error) error { + ctype := canonicalizeContentType(getHeaderCanonical(request.Header, headerContentType)) + switch protocolType := w.classifyRequest(request); protocolType { + case connectStreamProtocol: + setHeaderCanonical(response.Header(), headerContentType, ctype) + return w.writeConnectStreaming(response, err) + case grpcProtocol: + setHeaderCanonical(response.Header(), headerContentType, ctype) + return w.writeGRPC(response, err) + case grpcWebProtocol: + setHeaderCanonical(response.Header(), headerContentType, ctype) + return w.writeGRPCWeb(response, err) + case unknownProtocol, connectUnaryProtocol: + fallthrough + default: + // Unary errors are always JSON. Unknown protocols are treated as unary + // because they are likely to be Connect clients and will still be able to + // parse the error as it's in a human-readable format. + setHeaderCanonical(response.Header(), headerContentType, connectUnaryContentTypeJSON) + return w.writeConnectUnary(response, err) + } +} + +func (w *ErrorWriter) writeConnectUnary(response http.ResponseWriter, err error) error { + if connectErr, ok := asError(err); ok && !connectErr.wireErr { + mergeNonProtocolHeaders(response.Header(), connectErr.meta) + } + response.WriteHeader(connectCodeToHTTP(CodeOf(err))) + data, marshalErr := json.Marshal(newConnectWireError(err)) + if marshalErr != nil { + return fmt.Errorf("marshal error: %w", marshalErr) + } + _, writeErr := response.Write(data) + return writeErr +} + +func (w *ErrorWriter) writeConnectStreaming(response http.ResponseWriter, err error) error { + response.WriteHeader(http.StatusOK) + marshaler := &connectStreamingMarshaler{ + envelopeWriter: envelopeWriter{ + sender: writeSender{writer: response}, + bufferPool: w.bufferPool, + }, + } + // MarshalEndStream returns *Error: check return value to avoid typed nils. + if marshalErr := marshaler.MarshalEndStream(err, make(http.Header)); marshalErr != nil { + return marshalErr + } + return nil +} + +func (w *ErrorWriter) writeGRPC(response http.ResponseWriter, err error) error { + trailers := make(http.Header, 2) // need space for at least code & message + grpcErrorToTrailer(trailers, w.protobuf, err) + // To make net/http reliably send trailers without a body, we must set the + // Trailers header rather than using http.TrailerPrefix. See + // https://github.com/golang/go/issues/54723. + keys := make([]string, 0, len(trailers)) + for k := range trailers { + keys = append(keys, k) + } + setHeaderCanonical(response.Header(), headerTrailer, strings.Join(keys, ",")) + response.WriteHeader(http.StatusOK) + mergeHeaders(response.Header(), trailers) + return nil +} + +func (w *ErrorWriter) writeGRPCWeb(response http.ResponseWriter, err error) error { + // This is a trailers-only response. To match the behavior of Envoy and + // protocol_grpc.go, put the trailers in the HTTP headers. + grpcErrorToTrailer(response.Header(), w.protobuf, err) + response.WriteHeader(http.StatusOK) + return nil +} diff --git a/vendor/connectrpc.com/connect/handler.go b/vendor/connectrpc.com/connect/handler.go new file mode 100644 index 0000000000..5355329d3e --- /dev/null +++ b/vendor/connectrpc.com/connect/handler.go @@ -0,0 +1,427 @@ +// Copyright 2021-2026 The Connect Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package connect + +import ( + "context" + "net/http" +) + +// A Handler is the server-side implementation of a single RPC defined by a +// service schema. +// +// By default, Handlers support the Connect, gRPC, and gRPC-Web protocols with +// the binary Protobuf and JSON codecs. They support gzip compression using the +// standard library's [compress/gzip]. +type Handler struct { + spec Spec + implementation StreamingHandlerFunc + protocolHandlers map[string][]protocolHandler // Method to protocol handlers + allowMethod string // Allow header + acceptPost string // Accept-Post header +} + +// NewUnaryHandler constructs a [Handler] for a request-response procedure. +func NewUnaryHandler[Req, Res any]( + procedure string, + unary func(context.Context, *Request[Req]) (*Response[Res], error), + options ...HandlerOption, +) *Handler { + // Wrap the strongly-typed implementation so we can apply interceptors. + untyped := UnaryFunc(func(ctx context.Context, request AnyRequest) (AnyResponse, error) { + if err := ctx.Err(); err != nil { + return nil, err + } + typed, ok := request.(*Request[Req]) + if !ok { + return nil, errorf(CodeInternal, "unexpected handler request type %T", request) + } + res, err := unary(ctx, typed) + if res == nil && err == nil { + // This is going to panic during serialization. Debugging is much easier + // if we panic here instead, so we can include the procedure name. + panic(procedure + " returned nil *connect.Response and nil error") //nolint: forbidigo + } + if res == nil { + // Avoid returning a typed nil (*Response[Res]) as an AnyResponse interface value. + return nil, err + } + return res, err + }) + config := newHandlerConfig(procedure, StreamTypeUnary, options) + if interceptor := config.Interceptor; interceptor != nil { + untyped = interceptor.WrapUnary(untyped) + } + // Given a stream, how should we call the unary function? + implementation := func(ctx context.Context, conn StreamingHandlerConn) error { + request, err := receiveUnaryRequest[Req](conn, config.Initializer) + if err != nil { + return err + } + // Add the request header to the context, and store the response header + // and trailer to propagate back to the caller. + info := &handlerCallInfo{ + peer: request.Peer(), + spec: request.Spec(), + method: request.HTTPMethod(), + requestHeader: request.Header(), + } + ctx = newHandlerContext(ctx, info) + response, err := untyped(ctx, request) + // Add response headers/trailers from the context callinfo into the conn if they exist + if info.responseHeader != nil { + mergeNonProtocolHeaders(conn.ResponseHeader(), info.responseHeader) + } + if info.responseTrailer != nil { + mergeNonProtocolHeaders(conn.ResponseTrailer(), info.responseTrailer) + } + if err != nil { + return err + } + + // Add response headers/trailers from the response into the conn if they exist + if len(response.Header()) != 0 { + mergeNonProtocolHeaders(conn.ResponseHeader(), response.Header()) + } + if len(response.Trailer()) != 0 { + mergeNonProtocolHeaders(conn.ResponseTrailer(), response.Trailer()) + } + return conn.Send(response.Any()) + } + + protocolHandlers := config.newProtocolHandlers() + return &Handler{ + spec: config.newSpec(), + implementation: implementation, + protocolHandlers: mappedMethodHandlers(protocolHandlers), + allowMethod: sortedAllowMethodValue(protocolHandlers), + acceptPost: sortedAcceptPostValue(protocolHandlers), + } +} + +// NewUnaryHandlerSimple constructs a [Handler] for a request-response procedure using the +// function signature associated with the "simple" generation option. +// +// This option eliminates the [Request] and [Response] wrappers, and instead uses the +// context.Context to propagate information such as headers. +func NewUnaryHandlerSimple[Req, Res any]( + procedure string, + unary func(context.Context, *Req) (*Res, error), + options ...HandlerOption, +) *Handler { + return NewUnaryHandler( + procedure, + func(ctx context.Context, request *Request[Req]) (*Response[Res], error) { + responseMsg, err := unary(ctx, request.Msg) + if err != nil { + return nil, err + } + return NewResponse(responseMsg), nil + }, + options..., + ) +} + +// NewClientStreamHandler constructs a [Handler] for a client streaming procedure. +func NewClientStreamHandler[Req, Res any]( + procedure string, + implementation func(context.Context, *ClientStream[Req]) (*Response[Res], error), + options ...HandlerOption, +) *Handler { + config := newHandlerConfig(procedure, StreamTypeClient, options) + return newStreamHandler( + config, + func(ctx context.Context, conn StreamingHandlerConn) error { + stream := &ClientStream[Req]{ + conn: conn, + initializer: config.Initializer, + } + ctx = newHandlerContext(ctx, &streamingHandlerCallInfo{ + conn: conn, + }) + res, err := implementation(ctx, stream) + if err != nil { + return err + } + if res == nil { + // This is going to panic during serialization. Debugging is much easier + // if we panic here instead, so we can include the procedure name. + panic(procedure + " returned nil *connect.Response and nil error") //nolint: forbidigo + } + mergeHeaders(conn.ResponseHeader(), res.header) + mergeHeaders(conn.ResponseTrailer(), res.trailer) + return conn.Send(res.Msg) + }, + ) +} + +// NewClientStreamHandlerSimple constructs a [Handler] for a request-streaming procedure +// using the function signature associated with the "simple" generation option. +// +// This option eliminates the [Response] wrapper, and instead uses the context.Context +// to propagate information such as headers. +func NewClientStreamHandlerSimple[Req, Res any]( + procedure string, + implementation func(context.Context, *ClientStream[Req]) (*Res, error), + options ...HandlerOption, +) *Handler { + return NewClientStreamHandler( + procedure, + func(ctx context.Context, stream *ClientStream[Req]) (*Response[Res], error) { + responseMsg, err := implementation(ctx, stream) + if err != nil { + return nil, err + } + return NewResponse(responseMsg), nil + }, + options..., + ) +} + +// NewServerStreamHandler constructs a [Handler] for a server streaming procedure. +func NewServerStreamHandler[Req, Res any]( + procedure string, + implementation func(context.Context, *Request[Req], *ServerStream[Res]) error, + options ...HandlerOption, +) *Handler { + config := newHandlerConfig(procedure, StreamTypeServer, options) + return newStreamHandler( + config, + func(ctx context.Context, conn StreamingHandlerConn) error { + req, err := receiveUnaryRequest[Req](conn, config.Initializer) + if err != nil { + return err + } + ctx = newHandlerContext(ctx, &streamingHandlerCallInfo{ + conn: conn, + }) + return implementation(ctx, req, &ServerStream[Res]{conn: conn}) + }, + ) +} + +// NewServerStreamHandlerSimple constructs a [Handler] a server streaming procedure using the function +// signature associated with the "simple" generation option. +// +// This option eliminates the [Request] wrapper, and instead uses the context.Context to +// propagate information such as headers. +func NewServerStreamHandlerSimple[Req, Res any]( + procedure string, + implementation func(context.Context, *Req, *ServerStream[Res]) error, + options ...HandlerOption, +) *Handler { + return NewServerStreamHandler( + procedure, + func(ctx context.Context, request *Request[Req], serverStream *ServerStream[Res]) error { + return implementation(ctx, request.Msg, serverStream) + }, + options..., + ) +} + +// NewBidiStreamHandler constructs a [Handler] for a bidirectional streaming procedure. +func NewBidiStreamHandler[Req, Res any]( + procedure string, + implementation func(context.Context, *BidiStream[Req, Res]) error, + options ...HandlerOption, +) *Handler { + config := newHandlerConfig(procedure, StreamTypeBidi, options) + return newStreamHandler( + config, + func(ctx context.Context, conn StreamingHandlerConn) error { + ctx = newHandlerContext(ctx, &streamingHandlerCallInfo{ + conn: conn, + }) + return implementation( + ctx, + &BidiStream[Req, Res]{ + conn: conn, + initializer: config.Initializer, + }, + ) + }, + ) +} + +// ServeHTTP implements [http.Handler]. +func (h *Handler) ServeHTTP(responseWriter http.ResponseWriter, request *http.Request) { + // We don't need to defer functions to close the request body or read to + // EOF: the stream we construct later on already does that, and we only + // return early when dealing with misbehaving clients. In those cases, it's + // okay if we can't re-use the connection. + isBidi := (h.spec.StreamType & StreamTypeBidi) == StreamTypeBidi + if isBidi && request.ProtoMajor < 2 { + // Clients coded to expect full-duplex connections may hang if they've + // mistakenly negotiated HTTP/1.1. To unblock them, we must close the + // underlying TCP connection. + responseWriter.Header().Set("Connection", "close") + responseWriter.WriteHeader(http.StatusHTTPVersionNotSupported) + return + } + + protocolHandlers := h.protocolHandlers[request.Method] + if len(protocolHandlers) == 0 { + responseWriter.Header().Set("Allow", h.allowMethod) + responseWriter.WriteHeader(http.StatusMethodNotAllowed) + return + } + + contentType := canonicalizeContentType(getHeaderCanonical(request.Header, headerContentType)) + + // Find our implementation of the RPC protocol in use. + var protocolHandler protocolHandler + for _, handler := range protocolHandlers { + if handler.CanHandlePayload(request, contentType) { + protocolHandler = handler + break + } + } + if protocolHandler == nil { + responseWriter.Header().Set("Accept-Post", h.acceptPost) + responseWriter.WriteHeader(http.StatusUnsupportedMediaType) + return + } + + if request.Method == http.MethodGet { + // A body must not be present. + hasBody := request.ContentLength > 0 + if request.ContentLength < 0 { + // No content-length header. + // Test if body is empty by trying to read a single byte. + var b [1]byte + n, _ := request.Body.Read(b[:]) + hasBody = n > 0 + } + if hasBody { + responseWriter.WriteHeader(http.StatusUnsupportedMediaType) + return + } + _ = request.Body.Close() + } + + // Establish a stream and serve the RPC. + setHeaderCanonical(request.Header, headerContentType, contentType) + setHeaderCanonical(request.Header, headerHost, request.Host) + ctx, cancel, timeoutErr := protocolHandler.SetTimeout(request) //nolint: contextcheck + if timeoutErr != nil { + ctx = request.Context() + } + if cancel != nil { + defer cancel() + } + connCloser, ok := protocolHandler.NewConn( + responseWriter, + request.WithContext(ctx), + ) + if !ok { + // Failed to create stream, usually because client used an unknown + // compression algorithm. Nothing further to do. + return + } + if timeoutErr != nil { + _ = connCloser.Close(timeoutErr) + return + } + _ = connCloser.Close(h.implementation(ctx, connCloser)) +} + +type handlerConfig struct { + CompressionPools map[string]*compressionPool + CompressionNames []string + Codecs map[string]Codec + CompressMinBytes int + Interceptor Interceptor + Procedure string + Schema any + Initializer maybeInitializer + RequireConnectProtocolHeader bool + IdempotencyLevel IdempotencyLevel + BufferPool *bufferPool + ReadMaxBytes int + SendMaxBytes int + StreamType StreamType +} + +func newHandlerConfig(procedure string, streamType StreamType, options []HandlerOption) *handlerConfig { + protoPath := extractProtoPath(procedure) + config := handlerConfig{ + Procedure: protoPath, + CompressionPools: make(map[string]*compressionPool), + Codecs: make(map[string]Codec), + BufferPool: newBufferPool(), + StreamType: streamType, + } + withProtoBinaryCodec().applyToHandler(&config) + withProtoJSONCodecs().applyToHandler(&config) + withGzip().applyToHandler(&config) + for _, opt := range options { + opt.applyToHandler(&config) + } + return &config +} + +func (c *handlerConfig) newSpec() Spec { + return Spec{ + Procedure: c.Procedure, + Schema: c.Schema, + StreamType: c.StreamType, + IdempotencyLevel: c.IdempotencyLevel, + } +} + +func (c *handlerConfig) newProtocolHandlers() []protocolHandler { + protocols := []protocol{ + &protocolConnect{}, + &protocolGRPC{web: false}, + &protocolGRPC{web: true}, + } + handlers := make([]protocolHandler, 0, len(protocols)) + codecs := newReadOnlyCodecs(c.Codecs) + compressors := newReadOnlyCompressionPools( + c.CompressionPools, + c.CompressionNames, + ) + for _, protocol := range protocols { + handlers = append(handlers, protocol.NewHandler(&protocolHandlerParams{ + Spec: c.newSpec(), + Codecs: codecs, + CompressionPools: compressors, + CompressMinBytes: c.CompressMinBytes, + BufferPool: c.BufferPool, + ReadMaxBytes: c.ReadMaxBytes, + SendMaxBytes: c.SendMaxBytes, + RequireConnectProtocolHeader: c.RequireConnectProtocolHeader, + IdempotencyLevel: c.IdempotencyLevel, + })) + } + return handlers +} + +func newStreamHandler( + config *handlerConfig, + implementation StreamingHandlerFunc, +) *Handler { + if ic := config.Interceptor; ic != nil { + implementation = ic.WrapStreamingHandler(implementation) + } + protocolHandlers := config.newProtocolHandlers() + return &Handler{ + spec: config.newSpec(), + implementation: implementation, + protocolHandlers: mappedMethodHandlers(protocolHandlers), + allowMethod: sortedAllowMethodValue(protocolHandlers), + acceptPost: sortedAcceptPostValue(protocolHandlers), + } +} diff --git a/vendor/connectrpc.com/connect/handler_stream.go b/vendor/connectrpc.com/connect/handler_stream.go new file mode 100644 index 0000000000..9fe9e20bd5 --- /dev/null +++ b/vendor/connectrpc.com/connect/handler_stream.go @@ -0,0 +1,205 @@ +// Copyright 2021-2026 The Connect Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package connect + +import ( + "errors" + "io" + "net/http" +) + +// ClientStream is the handler's view of a client streaming RPC. +// +// It's constructed as part of [Handler] invocation, but doesn't currently have +// an exported constructor. +// +// Receive is not safe to call concurrently. +type ClientStream[Req any] struct { + conn StreamingHandlerConn + initializer maybeInitializer + msg *Req + err error +} + +// Spec returns the specification for the RPC. +func (c *ClientStream[_]) Spec() Spec { + return c.conn.Spec() +} + +// Peer describes the client for this RPC. +func (c *ClientStream[_]) Peer() Peer { + return c.conn.Peer() +} + +// RequestHeader returns the headers received from the client. +func (c *ClientStream[Req]) RequestHeader() http.Header { + return c.conn.RequestHeader() +} + +// Receive advances the stream to the next message, which will then be +// available through the Msg method. It returns false when the stream stops, +// either by reaching the end or by encountering an unexpected error. After +// Receive returns false, the Err method will return any unexpected error +// encountered. +func (c *ClientStream[Req]) Receive() bool { + if c.err != nil { + return false + } + c.msg = new(Req) + if err := c.initializer.maybe(c.Spec(), c.msg); err != nil { + c.err = err + return false + } + c.err = c.conn.Receive(c.msg) + return c.err == nil +} + +// Msg returns the most recent message unmarshaled by a call to Receive. +func (c *ClientStream[Req]) Msg() *Req { + if c.msg == nil { + c.msg = new(Req) + } + return c.msg +} + +// Err returns the first non-EOF error that was encountered by Receive. +func (c *ClientStream[Req]) Err() error { + if c.err == nil || errors.Is(c.err, io.EOF) { + return nil + } + return c.err +} + +// Conn exposes the underlying StreamingHandlerConn. This may be useful if +// you'd prefer to wrap the connection in a different high-level API. +func (c *ClientStream[Req]) Conn() StreamingHandlerConn { + return c.conn +} + +// ServerStream is the handler's view of a server streaming RPC. +// +// It's constructed as part of [Handler] invocation, but doesn't currently have +// an exported constructor. +// +// Send is not safe to call concurrently. +type ServerStream[Res any] struct { + conn StreamingHandlerConn +} + +// ResponseHeader returns the response headers. Headers are sent with the first +// call to Send. +// +// Headers beginning with "Connect-" and "Grpc-" are reserved for use by the +// Connect and gRPC protocols. Applications shouldn't write them. +func (s *ServerStream[Res]) ResponseHeader() http.Header { + return s.conn.ResponseHeader() +} + +// ResponseTrailer returns the response trailers. Handlers may write to the +// response trailers at any time before returning. +// +// Trailers beginning with "Connect-" and "Grpc-" are reserved for use by the +// Connect and gRPC protocols. Applications shouldn't write them. +func (s *ServerStream[Res]) ResponseTrailer() http.Header { + return s.conn.ResponseTrailer() +} + +// Send a message to the client. The first call to Send also sends the response +// headers. +func (s *ServerStream[Res]) Send(msg *Res) error { + if msg == nil { + return s.conn.Send(nil) + } + return s.conn.Send(msg) +} + +// Conn exposes the underlying StreamingHandlerConn. This may be useful if +// you'd prefer to wrap the connection in a different high-level API. +func (s *ServerStream[Res]) Conn() StreamingHandlerConn { + return s.conn +} + +// BidiStream is the handler's view of a bidirectional streaming RPC. +// +// It's constructed as part of [Handler] invocation, but doesn't currently have +// an exported constructor. +// +// Send and Receive may be called from separate goroutines concurrently, but +// neither may be called concurrently with itself. +type BidiStream[Req, Res any] struct { + conn StreamingHandlerConn + initializer maybeInitializer +} + +// Spec returns the specification for the RPC. +func (b *BidiStream[_, _]) Spec() Spec { + return b.conn.Spec() +} + +// Peer describes the client for this RPC. +func (b *BidiStream[_, _]) Peer() Peer { + return b.conn.Peer() +} + +// RequestHeader returns the headers received from the client. +func (b *BidiStream[Req, Res]) RequestHeader() http.Header { + return b.conn.RequestHeader() +} + +// Receive a message. When the client is done sending messages, Receive will +// return an error that wraps [io.EOF]. +func (b *BidiStream[Req, Res]) Receive() (*Req, error) { + var req Req + if err := b.initializer.maybe(b.Spec(), &req); err != nil { + return nil, err + } + if err := b.conn.Receive(&req); err != nil { + return nil, err + } + return &req, nil +} + +// ResponseHeader returns the response headers. Headers are sent with the first +// call to Send. +// +// Headers beginning with "Connect-" and "Grpc-" are reserved for use by the +// Connect and gRPC protocols. Applications shouldn't write them. +func (b *BidiStream[Req, Res]) ResponseHeader() http.Header { + return b.conn.ResponseHeader() +} + +// ResponseTrailer returns the response trailers. Handlers may write to the +// response trailers at any time before returning. +// +// Trailers beginning with "Connect-" and "Grpc-" are reserved for use by the +// Connect and gRPC protocols. Applications shouldn't write them. +func (b *BidiStream[Req, Res]) ResponseTrailer() http.Header { + return b.conn.ResponseTrailer() +} + +// Send a message to the client. The first call to Send also sends the response +// headers. +func (b *BidiStream[Req, Res]) Send(msg *Res) error { + if msg == nil { + return b.conn.Send(nil) + } + return b.conn.Send(msg) +} + +// Conn exposes the underlying StreamingHandlerConn. This may be useful if +// you'd prefer to wrap the connection in a different high-level API. +func (b *BidiStream[Req, Res]) Conn() StreamingHandlerConn { + return b.conn +} diff --git a/vendor/connectrpc.com/connect/header.go b/vendor/connectrpc.com/connect/header.go new file mode 100644 index 0000000000..bcde472a49 --- /dev/null +++ b/vendor/connectrpc.com/connect/header.go @@ -0,0 +1,128 @@ +// Copyright 2021-2026 The Connect Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package connect + +import ( + "encoding/base64" + "net/http" +) + +//nolint:gochecknoglobals +var protocolHeaders = map[string]struct{}{ + // HTTP headers. + headerContentType: {}, + headerContentLength: {}, + headerContentEncoding: {}, + headerHost: {}, + headerUserAgent: {}, + headerTrailer: {}, + headerDate: {}, + // Connect headers. + connectUnaryHeaderAcceptCompression: {}, + connectUnaryTrailerPrefix: {}, + connectStreamingHeaderCompression: {}, + connectStreamingHeaderAcceptCompression: {}, + connectHeaderTimeout: {}, + connectHeaderProtocolVersion: {}, + // gRPC headers. + grpcHeaderCompression: {}, + grpcHeaderAcceptCompression: {}, + grpcHeaderTimeout: {}, + grpcHeaderStatus: {}, + grpcHeaderMessage: {}, + grpcHeaderDetails: {}, +} + +// EncodeBinaryHeader base64-encodes the data. It always emits unpadded values. +// +// In the Connect, gRPC, and gRPC-Web protocols, binary headers must have keys +// ending in "-Bin". +func EncodeBinaryHeader(data []byte) string { + // gRPC specification says that implementations should emit unpadded values. + return base64.RawStdEncoding.EncodeToString(data) +} + +// DecodeBinaryHeader base64-decodes the data. It can decode padded or unpadded +// values. Following usual HTTP semantics, multiple base64-encoded values may +// be joined with a comma. When receiving such comma-separated values, split +// them with [strings.Split] before calling DecodeBinaryHeader. +// +// Binary headers sent using the Connect, gRPC, and gRPC-Web protocols have +// keys ending in "-Bin". +func DecodeBinaryHeader(data string) ([]byte, error) { + if len(data)%4 != 0 { + // Data definitely isn't padded. + return base64.RawStdEncoding.DecodeString(data) + } + // Either the data was padded, or padding wasn't necessary. In both cases, + // the padding-aware decoder works. + return base64.StdEncoding.DecodeString(data) +} + +func mergeHeaders(into, from http.Header) { + for key, vals := range from { + if len(vals) == 0 { + // For response trailers, net/http will pre-populate entries + // with nil values based on the "Trailer" header. But if there + // are no actual values for those keys, we skip them. + continue + } + into[key] = append(into[key], vals...) + } +} + +// mergeNonProtocolHeaders merges headers excluding protocol headers defined in +// protocolHeaders. +func mergeNonProtocolHeaders(into, from http.Header) { + for key, vals := range from { + if len(vals) == 0 { + // For response trailers, net/http will pre-populate entries + // with nil values based on the "Trailer" header. But if there + // are no actual values for those keys, we skip them. + continue + } + if _, isProtocolHeader := protocolHeaders[key]; !isProtocolHeader { + into[key] = append(into[key], vals...) + } + } +} + +// getHeaderCanonical is a shortcut for Header.Get() which +// bypasses the CanonicalMIMEHeaderKey operation when we +// know the key is already in canonical form. +func getHeaderCanonical(h http.Header, key string) string { + if h == nil { + return "" + } + v := h[key] + if len(v) == 0 { + return "" + } + return v[0] +} + +// setHeaderCanonical is a shortcut for Header.Set() which +// bypasses the CanonicalMIMEHeaderKey operation when we +// know the key is already in canonical form. +func setHeaderCanonical(h http.Header, key, value string) { + h[key] = []string{value} +} + +// delHeaderCanonical is a shortcut for Header.Del() which +// bypasses the CanonicalMIMEHeaderKey operation when we +// know the key is already in canonical form. +func delHeaderCanonical(h http.Header, key string) { + delete(h, key) +} diff --git a/vendor/connectrpc.com/connect/idempotency_level.go b/vendor/connectrpc.com/connect/idempotency_level.go new file mode 100644 index 0000000000..316ef12a8f --- /dev/null +++ b/vendor/connectrpc.com/connect/idempotency_level.go @@ -0,0 +1,68 @@ +// Copyright 2021-2026 The Connect Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package connect + +import "fmt" + +// An IdempotencyLevel is a value that declares how "idempotent" an RPC is. This +// value can affect RPC behaviors, such as determining whether it is safe to +// retry a request, or what kinds of request modalities are allowed for a given +// procedure. +type IdempotencyLevel int + +// NOTE: For simplicity, these should be kept in sync with the values of the +// google.protobuf.MethodOptions.IdempotencyLevel enumeration. + +const ( + // IdempotencyUnknown is the default idempotency level. A procedure with + // this idempotency level may not be idempotent. This is appropriate for + // any kind of procedure. + IdempotencyUnknown IdempotencyLevel = 0 + + // IdempotencyNoSideEffects is the idempotency level that specifies that a + // given call has no side-effects. This is equivalent to [RFC 9110 § 9.2.1] + // "safe" methods in terms of semantics. This procedure should not mutate + // any state. This idempotency level is appropriate for queries, or anything + // that would be suitable for an HTTP GET request. In addition, due to the + // lack of side-effects, such a procedure would be suitable to retry and + // expect that the results will not be altered by preceding attempts. + // + // [RFC 9110 § 9.2.1]: https://www.rfc-editor.org/rfc/rfc9110.html#section-9.2.1 + IdempotencyNoSideEffects IdempotencyLevel = 1 + + // IdempotencyIdempotent is the idempotency level that specifies that a + // given call is "idempotent", such that multiple instances of the same + // request to this procedure would have the same side-effects as a single + // request. This is equivalent to [RFC 9110 § 9.2.2] "idempotent" methods. + // This level is a subset of the previous level. This idempotency level is + // appropriate for any procedure that is safe to retry multiple times + // and be guaranteed that the response and side-effects will not be altered + // as a result of multiple attempts, for example, entity deletion requests. + // + // [RFC 9110 § 9.2.2]: https://www.rfc-editor.org/rfc/rfc9110.html#section-9.2.2 + IdempotencyIdempotent IdempotencyLevel = 2 +) + +func (i IdempotencyLevel) String() string { + switch i { + case IdempotencyUnknown: + return "idempotency_unknown" + case IdempotencyNoSideEffects: + return "no_side_effects" + case IdempotencyIdempotent: + return "idempotent" + } + return fmt.Sprintf("idempotency_%d", i) +} diff --git a/vendor/connectrpc.com/connect/interceptor.go b/vendor/connectrpc.com/connect/interceptor.go new file mode 100644 index 0000000000..222247ed79 --- /dev/null +++ b/vendor/connectrpc.com/connect/interceptor.go @@ -0,0 +1,140 @@ +// Copyright 2021-2026 The Connect Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package connect + +import ( + "context" + "errors" +) + +var ( + // errNewClientContextProhibited signals that a new client context was created + // in an interceptor, which is prohibited. + errNewClientContextProhibited = errors.New("creating a new context in an interceptor is prohibited") +) + +// UnaryFunc is the generic signature of a unary RPC. Interceptors may wrap +// Funcs. +// +// The type of the request and response structs depend on the codec being used. +// When using Protobuf, request.Any() and response.Any() will always be +// [proto.Message] implementations. +// +// On return, response is non-nil if and only if err is nil. +type UnaryFunc func(context.Context, AnyRequest) (AnyResponse, error) + +// StreamingClientFunc is the generic signature of a streaming RPC from the client's +// perspective. Interceptors may wrap StreamingClientFuncs. +type StreamingClientFunc func(context.Context, Spec) StreamingClientConn + +// StreamingHandlerFunc is the generic signature of a streaming RPC from the +// handler's perspective. Interceptors may wrap StreamingHandlerFuncs. +type StreamingHandlerFunc func(context.Context, StreamingHandlerConn) error + +// An Interceptor adds logic to a generated handler or client, like the +// decorators or middleware you may have seen in other libraries. Interceptors +// may mutate requests and responses, handle errors, retry, recover from panics, +// emit logs and metrics, or do nearly anything else. +// +// The returned functions must be safe to call concurrently. +type Interceptor interface { + WrapUnary(UnaryFunc) UnaryFunc + WrapStreamingClient(StreamingClientFunc) StreamingClientFunc + WrapStreamingHandler(StreamingHandlerFunc) StreamingHandlerFunc +} + +// UnaryInterceptorFunc is a simple Interceptor implementation that only +// wraps unary RPCs. It has no effect on streaming RPCs. +type UnaryInterceptorFunc func(UnaryFunc) UnaryFunc + +// WrapUnary implements [Interceptor] by applying the interceptor function. +func (f UnaryInterceptorFunc) WrapUnary(next UnaryFunc) UnaryFunc { return f(next) } + +// WrapStreamingClient implements [Interceptor] with a no-op. +func (f UnaryInterceptorFunc) WrapStreamingClient(next StreamingClientFunc) StreamingClientFunc { + return next +} + +// WrapStreamingHandler implements [Interceptor] with a no-op. +func (f UnaryInterceptorFunc) WrapStreamingHandler(next StreamingHandlerFunc) StreamingHandlerFunc { + return next +} + +// A chain composes multiple interceptors into one. +type chain struct { + interceptors []Interceptor +} + +// newChain composes multiple interceptors into one. +func newChain(interceptors []Interceptor) *chain { + // We usually wrap in reverse order to have the first interceptor from + // the slice act first. Rather than doing this dance repeatedly, reverse the + // interceptor order now. + var chain chain + for i := len(interceptors) - 1; i >= 0; i-- { + if interceptor := interceptors[i]; interceptor != nil { + chain.interceptors = append(chain.interceptors, interceptor) + } + } + return &chain +} + +func (c *chain) WrapUnary(next UnaryFunc) UnaryFunc { + for _, interceptor := range c.interceptors { + next = unaryThunk(next) + next = interceptor.WrapUnary(next) + } + return next +} + +func (c *chain) WrapStreamingClient(next StreamingClientFunc) StreamingClientFunc { + for _, interceptor := range c.interceptors { + next = streamingClientThunk(next) + next = interceptor.WrapStreamingClient(next) + } + return next +} + +func (c *chain) WrapStreamingHandler(next StreamingHandlerFunc) StreamingHandlerFunc { + for _, interceptor := range c.interceptors { + next = interceptor.WrapStreamingHandler(next) + } + return next +} + +func unaryThunk(next UnaryFunc) UnaryFunc { + return func(ctx context.Context, req AnyRequest) (AnyResponse, error) { + if err := checkSentinel(ctx); err != nil { + return nil, err + } + return next(ctx, req) + } +} + +func streamingClientThunk(next StreamingClientFunc) StreamingClientFunc { + return func(ctx context.Context, spec Spec) StreamingClientConn { + if err := checkSentinel(ctx); err != nil { + return &errStreamingClientConn{err: err} + } + return next(ctx, spec) + } +} + +func checkSentinel(ctx context.Context) error { + if ctx.Value(clientCallInfoContextKey{}) != ctx.Value(sentinelContextKey{}) { + return errNewClientContextProhibited + } + return nil +} diff --git a/vendor/connectrpc.com/connect/internal/gen/connectext/grpc/status/v1/status.pb.go b/vendor/connectrpc.com/connect/internal/gen/connectext/grpc/status/v1/status.pb.go new file mode 100644 index 0000000000..d7bf60defc --- /dev/null +++ b/vendor/connectrpc.com/connect/internal/gen/connectext/grpc/status/v1/status.pb.go @@ -0,0 +1,165 @@ +// Copyright 2021-2026 The Connect Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.11 +// protoc (unknown) +// source: connectext/grpc/status/v1/status.proto + +// This package is for internal use by Connect, and provides no backward +// compatibility guarantees whatsoever. + +package statusv1 + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + anypb "google.golang.org/protobuf/types/known/anypb" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// See https://cloud.google.com/apis/design/errors. +// +// This struct must remain binary-compatible with +// https://github.com/googleapis/googleapis/blob/master/google/rpc/status.proto. +type Status struct { + state protoimpl.MessageState `protogen:"open.v1"` + Code int32 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"` // a google.rpc.Code + Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` // developer-facing, English (localize in details or client-side) + Details []*anypb.Any `protobuf:"bytes,3,rep,name=details,proto3" json:"details,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Status) Reset() { + *x = Status{} + mi := &file_connectext_grpc_status_v1_status_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Status) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Status) ProtoMessage() {} + +func (x *Status) ProtoReflect() protoreflect.Message { + mi := &file_connectext_grpc_status_v1_status_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Status.ProtoReflect.Descriptor instead. +func (*Status) Descriptor() ([]byte, []int) { + return file_connectext_grpc_status_v1_status_proto_rawDescGZIP(), []int{0} +} + +func (x *Status) GetCode() int32 { + if x != nil { + return x.Code + } + return 0 +} + +func (x *Status) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +func (x *Status) GetDetails() []*anypb.Any { + if x != nil { + return x.Details + } + return nil +} + +var File_connectext_grpc_status_v1_status_proto protoreflect.FileDescriptor + +const file_connectext_grpc_status_v1_status_proto_rawDesc = "" + + "\n" + + "&connectext/grpc/status/v1/status.proto\x12\x0egrpc.status.v1\x1a\x19google/protobuf/any.proto\"f\n" + + "\x06Status\x12\x12\n" + + "\x04code\x18\x01 \x01(\x05R\x04code\x12\x18\n" + + "\amessage\x18\x02 \x01(\tR\amessage\x12.\n" + + "\adetails\x18\x03 \x03(\v2\x14.google.protobuf.AnyR\adetailsB\xc3\x01\n" + + "\x12com.grpc.status.v1B\vStatusProtoP\x01ZFconnectrpc.com/connect/internal/gen/connectext/grpc/status/v1;statusv1\xa2\x02\x03GSX\xaa\x02\x0eGrpc.Status.V1\xca\x02\x0eGrpc\\Status\\V1\xe2\x02\x1aGrpc\\Status\\V1\\GPBMetadata\xea\x02\x10Grpc::Status::V1b\x06proto3" + +var ( + file_connectext_grpc_status_v1_status_proto_rawDescOnce sync.Once + file_connectext_grpc_status_v1_status_proto_rawDescData []byte +) + +func file_connectext_grpc_status_v1_status_proto_rawDescGZIP() []byte { + file_connectext_grpc_status_v1_status_proto_rawDescOnce.Do(func() { + file_connectext_grpc_status_v1_status_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_connectext_grpc_status_v1_status_proto_rawDesc), len(file_connectext_grpc_status_v1_status_proto_rawDesc))) + }) + return file_connectext_grpc_status_v1_status_proto_rawDescData +} + +var file_connectext_grpc_status_v1_status_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_connectext_grpc_status_v1_status_proto_goTypes = []any{ + (*Status)(nil), // 0: grpc.status.v1.Status + (*anypb.Any)(nil), // 1: google.protobuf.Any +} +var file_connectext_grpc_status_v1_status_proto_depIdxs = []int32{ + 1, // 0: grpc.status.v1.Status.details:type_name -> google.protobuf.Any + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_connectext_grpc_status_v1_status_proto_init() } +func file_connectext_grpc_status_v1_status_proto_init() { + if File_connectext_grpc_status_v1_status_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_connectext_grpc_status_v1_status_proto_rawDesc), len(file_connectext_grpc_status_v1_status_proto_rawDesc)), + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_connectext_grpc_status_v1_status_proto_goTypes, + DependencyIndexes: file_connectext_grpc_status_v1_status_proto_depIdxs, + MessageInfos: file_connectext_grpc_status_v1_status_proto_msgTypes, + }.Build() + File_connectext_grpc_status_v1_status_proto = out.File + file_connectext_grpc_status_v1_status_proto_goTypes = nil + file_connectext_grpc_status_v1_status_proto_depIdxs = nil +} diff --git a/vendor/connectrpc.com/connect/option.go b/vendor/connectrpc.com/connect/option.go new file mode 100644 index 0000000000..7945c9b2eb --- /dev/null +++ b/vendor/connectrpc.com/connect/option.go @@ -0,0 +1,647 @@ +// Copyright 2021-2026 The Connect Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package connect + +import ( + "compress/gzip" + "context" + "io" + "net/http" +) + +// A ClientOption configures a [Client]. +// +// In addition to any options grouped in the documentation below, remember that +// any [Option] is also a valid ClientOption. +type ClientOption interface { + applyToClient(*clientConfig) +} + +// WithAcceptCompression makes a compression algorithm available to a client. +// Clients ask servers to compress responses using any of the registered +// algorithms. The first registered algorithm is treated as the least +// preferred, and the last registered algorithm is the most preferred. +// +// It's safe to use this option liberally: servers will ignore any +// compression algorithms they don't support. To compress requests, pair this +// option with [WithSendCompression]. To remove support for a +// previously-registered compression algorithm, use WithAcceptCompression with +// nil decompressor and compressor constructors. +// +// Clients accept gzipped responses by default, using a compressor backed by the +// standard library's [gzip] package with the default compression level. Use +// [WithSendGzip] to compress requests with gzip. +// +// Calling WithAcceptCompression with an empty name is a no-op. +func WithAcceptCompression( + name string, + newDecompressor func() Decompressor, + newCompressor func() Compressor, +) ClientOption { + return &compressionOption{ + Name: name, + CompressionPool: newCompressionPool(newDecompressor, newCompressor), + } +} + +// WithClientOptions composes multiple ClientOptions into one. +func WithClientOptions(options ...ClientOption) ClientOption { + return &clientOptionsOption{options} +} + +// WithGRPC configures clients to use the HTTP/2 gRPC protocol. +func WithGRPC() ClientOption { + return &grpcOption{web: false} +} + +// WithGRPCWeb configures clients to use the gRPC-Web protocol. +func WithGRPCWeb() ClientOption { + return &grpcOption{web: true} +} + +// WithProtoJSON configures a client to send JSON-encoded data instead of +// binary Protobuf. It uses the standard Protobuf JSON mapping as implemented +// by [google.golang.org/protobuf/encoding/protojson]: fields are named using +// lowerCamelCase, zero values are omitted, missing required fields are errors, +// enums are emitted as strings, etc. +func WithProtoJSON() ClientOption { + return WithCodec(&protoJSONCodec{codecNameJSON}) +} + +// WithSendCompression configures the client to use the specified algorithm to +// compress request messages. If the algorithm has not been registered using +// [WithAcceptCompression], the client will return errors at runtime. +// +// Because some servers don't support compression, clients default to sending +// uncompressed requests. +func WithSendCompression(name string) ClientOption { + return &sendCompressionOption{Name: name} +} + +// WithSendGzip configures the client to gzip requests. Since clients have +// access to a gzip compressor by default, WithSendGzip doesn't require +// [WithSendCompression]. +// +// Some servers don't support gzip, so clients default to sending uncompressed +// requests. +func WithSendGzip() ClientOption { + return WithSendCompression(compressionGzip) +} + +// A HandlerOption configures a [Handler]. +// +// In addition to any options grouped in the documentation below, remember that +// any [Option] is also a HandlerOption. +type HandlerOption interface { + applyToHandler(*handlerConfig) +} + +// WithCompression configures handlers to support a compression algorithm. +// Clients may send messages compressed with that algorithm and/or request +// compressed responses. The [Compressor] and [Decompressor] produced by the +// supplied constructors must use the same algorithm. Internally, Connect pools +// compressors and decompressors. +// +// By default, handlers support gzip using the standard library's +// [compress/gzip] package at the default compression level. To remove support for +// a previously-registered compression algorithm, use WithCompression with nil +// decompressor and compressor constructors. +// +// Calling WithCompression with an empty name is a no-op. +func WithCompression( + name string, + newDecompressor func() Decompressor, + newCompressor func() Compressor, +) HandlerOption { + return &compressionOption{ + Name: name, + CompressionPool: newCompressionPool(newDecompressor, newCompressor), + } +} + +// WithHandlerOptions composes multiple HandlerOptions into one. +func WithHandlerOptions(options ...HandlerOption) HandlerOption { + return &handlerOptionsOption{options} +} + +// WithRecover adds an interceptor that recovers from panics. The supplied +// function receives the context, [Spec], request headers, and the recovered +// value (which may be nil). It must return an error to send back to the +// client. It may also log the panic, emit metrics, or execute other +// error-handling logic. Handler functions must be safe to call concurrently. +// +// To preserve compatibility with [net/http]'s semantics, this interceptor +// doesn't handle panics with [http.ErrAbortHandler]. +// +// By default, handlers don't recover from panics. Because the standard +// library's [http.Server] recovers from panics by default, this option isn't +// usually necessary to prevent crashes. Instead, it helps servers collect +// RPC-specific data during panics and send a more detailed error to +// clients. +func WithRecover(handle func(context.Context, Spec, http.Header, any) error) HandlerOption { + return WithInterceptors(&recoverHandlerInterceptor{handle: handle}) +} + +// WithRequireConnectProtocolHeader configures the Handler to require requests +// using the Connect RPC protocol to include the Connect-Protocol-Version +// header. This ensures that HTTP proxies and net/http middleware can easily +// identify valid Connect requests, even if they use a common Content-Type like +// application/json. However, it makes ad-hoc requests with tools like cURL +// more laborious. Streaming requests are not affected by this option. +// +// This option has no effect if the client uses the gRPC or gRPC-Web protocols. +func WithRequireConnectProtocolHeader() HandlerOption { + return &requireConnectProtocolHeaderOption{} +} + +// WithConditionalHandlerOptions allows procedures in the same service to have +// different configurations: for example, one procedure may need a much larger +// WithReadMaxBytes setting than the others. +// +// WithConditionalHandlerOptions takes a function which may inspect each +// procedure's Spec before deciding which options to apply. Returning a nil +// slice is safe. +func WithConditionalHandlerOptions(conditional func(spec Spec) []HandlerOption) HandlerOption { + return &conditionalHandlerOptions{conditional: conditional} +} + +// Option implements both [ClientOption] and [HandlerOption], so it can be +// applied both client-side and server-side. +type Option interface { + ClientOption + HandlerOption +} + +// WithSchema provides a parsed representation of the schema for an RPC to a +// client or handler. The supplied schema is exposed as [Spec].Schema. This +// option is typically added by generated code. +// +// For services using protobuf schemas, the supplied schema should be a +// [google.golang.org/protobuf/reflect/protoreflect.MethodDescriptor]. +func WithSchema(schema any) Option { + return &schemaOption{Schema: schema} +} + +// WithRequestInitializer provides a function that initializes a new message. +// It may be used to dynamically construct request messages. It is called on +// server receives to construct the message to be unmarshaled into. The message +// will be a non nil pointer to the type created by the handler. Use the Schema +// field of the [Spec] to determine the type of the message. +func WithRequestInitializer(initializer func(spec Spec, message any) error) HandlerOption { + return &initializerOption{Initializer: initializer} +} + +// WithResponseInitializer provides a function that initializes a new message. +// It may be used to dynamically construct response messages. It is called on +// client receives to construct the message to be unmarshaled into. The message +// will be a non nil pointer to the type created by the client. Use the Schema +// field of the [Spec] to determine the type of the message. +func WithResponseInitializer(initializer func(spec Spec, message any) error) ClientOption { + return &initializerOption{Initializer: initializer} +} + +// WithCodec registers a serialization method with a client or handler. +// Handlers may have multiple codecs registered, and use whichever the client +// chooses. Clients may only have a single codec. +// +// By default, handlers and clients support binary Protocol Buffer data using +// [google.golang.org/protobuf/proto]. Handlers also support JSON by default, +// using the standard Protobuf JSON mapping. Users with more specialized needs +// may override the default codecs by registering a new codec under the "proto" +// or "json" names. When supplying a custom "proto" codec, keep in mind that +// some unexported, protocol-specific messages are serialized using Protobuf - +// take care to fall back to the standard Protobuf implementation if +// necessary. +// +// Registering a codec with an empty name is a no-op. +func WithCodec(codec Codec) Option { + return &codecOption{Codec: codec} +} + +// WithCompressMinBytes sets a minimum size threshold for compression: +// regardless of compressor configuration, messages smaller than the configured +// minimum are sent uncompressed. +// +// The default minimum is zero. Setting a minimum compression threshold may +// improve overall performance, because the CPU cost of compressing very small +// messages usually isn't worth the small reduction in network I/O. +func WithCompressMinBytes(minBytes int) Option { + return &compressMinBytesOption{Min: minBytes} +} + +// WithReadMaxBytes limits the performance impact of pathologically large +// messages sent by the other party. For handlers, WithReadMaxBytes limits the size +// of a message that the client can send. For clients, WithReadMaxBytes limits the +// size of a message that the server can respond with. Limits apply to each Protobuf +// message, not to the stream as a whole. +// +// Setting WithReadMaxBytes to zero allows any message size. Both clients and +// handlers default to allowing any request size. +// +// Handlers may also use [http.MaxBytesHandler] to limit the total size of the +// HTTP request stream (rather than the per-message size). Connect handles +// [http.MaxBytesError] specially, so clients still receive errors with the +// appropriate error code and informative messages. +func WithReadMaxBytes(maxBytes int) Option { + return &readMaxBytesOption{Max: maxBytes} +} + +// WithSendMaxBytes prevents sending messages too large for the client/handler +// to handle without significant performance overhead. For handlers, WithSendMaxBytes +// limits the size of a message that the handler can respond with. For clients, +// WithSendMaxBytes limits the size of a message that the client can send. Limits +// apply to each message, not to the stream as a whole. +// +// Setting WithSendMaxBytes to zero allows any message size. Both clients and +// handlers default to allowing any message size. +func WithSendMaxBytes(maxBytes int) Option { + return &sendMaxBytesOption{Max: maxBytes} +} + +// WithIdempotency declares the idempotency of the procedure. This can determine +// whether a procedure call can safely be retried, and may affect which request +// modalities are allowed for a given procedure call. +// +// In most cases, you should not need to manually set this. It is normally set +// by the code generator for your schema. For protobuf schemas, it can be set like this: +// +// rpc Ping(PingRequest) returns (PingResponse) { +// option idempotency_level = NO_SIDE_EFFECTS; +// } +func WithIdempotency(idempotencyLevel IdempotencyLevel) Option { + return &idempotencyOption{idempotencyLevel: idempotencyLevel} +} + +// WithHTTPGet allows Connect-protocol clients to use HTTP GET requests for +// side-effect free unary RPC calls. Typically, the service schema indicates +// which procedures are idempotent (see [WithIdempotency] for an example +// protobuf schema). The gRPC and gRPC-Web protocols are POST-only, so this +// option has no effect when combined with [WithGRPC] or [WithGRPCWeb]. +// +// Using HTTP GET requests makes it easier to take advantage of CDNs, caching +// reverse proxies, and browsers' built-in caching. Note, however, that servers +// don't automatically set any cache headers; you can set cache headers using +// interceptors or by adding headers in individual procedure implementations. +// +// By default, all requests are made as HTTP POSTs. +func WithHTTPGet() ClientOption { + return &enableGet{} +} + +// WithInterceptors configures a client or handler's interceptor stack. Repeated +// WithInterceptors options are applied in order, so +// +// WithInterceptors(A) + WithInterceptors(B, C) == WithInterceptors(A, B, C) +// +// Unary interceptors compose like an onion. The first interceptor provided is +// the outermost layer of the onion: it acts first on the context and request, +// and last on the response and error. +// +// Stream interceptors also behave like an onion: the first interceptor +// provided is the outermost wrapper for the [StreamingClientConn] or +// [StreamingHandlerConn]. It's the first to see sent messages and the last to +// see received messages. +// +// Applied to client and handler, WithInterceptors(A, B, ..., Y, Z) produces: +// +// client.Send() client.Receive() +// | ^ +// v | +// A --- --- A +// B --- --- B +// : ... ... : +// Y --- --- Y +// Z --- --- Z +// | ^ +// v | +// = = = = = = = = = = = = = = = = +// network +// = = = = = = = = = = = = = = = = +// | ^ +// v | +// A --- --- A +// B --- --- B +// : ... ... : +// Y --- --- Y +// Z --- --- Z +// | ^ +// v | +// handler.Receive() handler.Send() +// | ^ +// | | +// '-> handler logic >-' +// +// Note that in clients, Send handles the request message(s) and Receive +// handles the response message(s). For handlers, it's the reverse. Depending +// on your interceptor's logic, you may need to wrap one method in clients and +// the other in handlers. +func WithInterceptors(interceptors ...Interceptor) Option { + return &interceptorsOption{interceptors} +} + +// WithOptions composes multiple Options into one. +func WithOptions(options ...Option) Option { + return &optionsOption{options} +} + +type schemaOption struct { + Schema any +} + +func (o *schemaOption) applyToClient(config *clientConfig) { + config.Schema = o.Schema +} + +func (o *schemaOption) applyToHandler(config *handlerConfig) { + config.Schema = o.Schema +} + +type initializerOption struct { + Initializer func(spec Spec, message any) error +} + +func (o *initializerOption) applyToHandler(config *handlerConfig) { + config.Initializer = maybeInitializer{initializer: o.Initializer} +} + +func (o *initializerOption) applyToClient(config *clientConfig) { + config.Initializer = maybeInitializer{initializer: o.Initializer} +} + +type maybeInitializer struct { + initializer func(spec Spec, message any) error +} + +func (o maybeInitializer) maybe(spec Spec, message any) error { + if o.initializer != nil { + return o.initializer(spec, message) + } + return nil +} + +type clientOptionsOption struct { + options []ClientOption +} + +func (o *clientOptionsOption) applyToClient(config *clientConfig) { + for _, option := range o.options { + option.applyToClient(config) + } +} + +type codecOption struct { + Codec Codec +} + +func (o *codecOption) applyToClient(config *clientConfig) { + if o.Codec == nil || o.Codec.Name() == "" { + return + } + config.Codec = o.Codec +} + +func (o *codecOption) applyToHandler(config *handlerConfig) { + if o.Codec == nil || o.Codec.Name() == "" { + return + } + config.Codecs[o.Codec.Name()] = o.Codec +} + +type compressionOption struct { + Name string + CompressionPool *compressionPool +} + +func (o *compressionOption) applyToClient(config *clientConfig) { + o.apply(&config.CompressionNames, config.CompressionPools) +} + +func (o *compressionOption) applyToHandler(config *handlerConfig) { + o.apply(&config.CompressionNames, config.CompressionPools) +} + +func (o *compressionOption) apply(configuredNames *[]string, configuredPools map[string]*compressionPool) { + if o.Name == "" { + return + } + if o.CompressionPool == nil { + delete(configuredPools, o.Name) + var names []string + for _, name := range *configuredNames { + if name == o.Name { + continue + } + names = append(names, name) + } + *configuredNames = names + return + } + configuredPools[o.Name] = o.CompressionPool + *configuredNames = append(*configuredNames, o.Name) +} + +type compressMinBytesOption struct { + Min int +} + +func (o *compressMinBytesOption) applyToClient(config *clientConfig) { + config.CompressMinBytes = o.Min +} + +func (o *compressMinBytesOption) applyToHandler(config *handlerConfig) { + config.CompressMinBytes = o.Min +} + +type readMaxBytesOption struct { + Max int +} + +func (o *readMaxBytesOption) applyToClient(config *clientConfig) { + config.ReadMaxBytes = o.Max +} + +func (o *readMaxBytesOption) applyToHandler(config *handlerConfig) { + config.ReadMaxBytes = o.Max +} + +type sendMaxBytesOption struct { + Max int +} + +func (o *sendMaxBytesOption) applyToClient(config *clientConfig) { + config.SendMaxBytes = o.Max +} + +func (o *sendMaxBytesOption) applyToHandler(config *handlerConfig) { + config.SendMaxBytes = o.Max +} + +type handlerOptionsOption struct { + options []HandlerOption +} + +func (o *handlerOptionsOption) applyToHandler(config *handlerConfig) { + for _, option := range o.options { + option.applyToHandler(config) + } +} + +type requireConnectProtocolHeaderOption struct{} + +func (o *requireConnectProtocolHeaderOption) applyToHandler(config *handlerConfig) { + config.RequireConnectProtocolHeader = true +} + +type idempotencyOption struct { + idempotencyLevel IdempotencyLevel +} + +func (o *idempotencyOption) applyToClient(config *clientConfig) { + config.IdempotencyLevel = o.idempotencyLevel +} + +func (o *idempotencyOption) applyToHandler(config *handlerConfig) { + config.IdempotencyLevel = o.idempotencyLevel +} + +type grpcOption struct { + web bool +} + +func (o *grpcOption) applyToClient(config *clientConfig) { + config.Protocol = &protocolGRPC{web: o.web} +} + +type enableGet struct{} + +func (o *enableGet) applyToClient(config *clientConfig) { + config.EnableGet = true +} + +// WithHTTPGetMaxURLSize sets the maximum allowable URL length for GET requests +// made using the Connect protocol. It has no effect on gRPC or gRPC-Web +// clients, since those protocols are POST-only. +// +// Limiting the URL size is useful as most user agents, proxies, and servers +// have limits on the allowable length of a URL. For example, Apache and Nginx +// limit the size of a request line to around 8 KiB, meaning that maximum +// length of a URL is a bit smaller than this. If you run into URL size +// limitations imposed by your network infrastructure and don't know the +// maximum allowable size, or if you'd prefer to be cautious from the start, a +// 4096 byte (4 KiB) limit works with most common proxies and CDNs. +// +// If fallback is set to true and the URL would be longer than the configured +// maximum value, the request will be sent as an HTTP POST instead. If fallback +// is set to false, the request will fail with [CodeResourceExhausted]. +// +// By default, Connect-protocol clients with GET requests enabled may send a +// URL of any size. +func WithHTTPGetMaxURLSize(bytes int, fallback bool) ClientOption { + return &getURLMaxBytes{Max: bytes, Fallback: fallback} +} + +type getURLMaxBytes struct { + Max int + Fallback bool +} + +func (o *getURLMaxBytes) applyToClient(config *clientConfig) { + config.GetURLMaxBytes = o.Max + config.GetUseFallback = o.Fallback +} + +type interceptorsOption struct { + Interceptors []Interceptor +} + +func (o *interceptorsOption) applyToClient(config *clientConfig) { + config.Interceptor = o.chainWith(config.Interceptor) +} + +func (o *interceptorsOption) applyToHandler(config *handlerConfig) { + config.Interceptor = o.chainWith(config.Interceptor) +} + +func (o *interceptorsOption) chainWith(current Interceptor) Interceptor { + if len(o.Interceptors) == 0 { + return current + } + if current == nil && len(o.Interceptors) == 1 { + return o.Interceptors[0] + } + if current == nil && len(o.Interceptors) > 1 { + return newChain(o.Interceptors) + } + return newChain(append([]Interceptor{current}, o.Interceptors...)) +} + +type optionsOption struct { + options []Option +} + +func (o *optionsOption) applyToClient(config *clientConfig) { + for _, option := range o.options { + option.applyToClient(config) + } +} + +func (o *optionsOption) applyToHandler(config *handlerConfig) { + for _, option := range o.options { + option.applyToHandler(config) + } +} + +type sendCompressionOption struct { + Name string +} + +func (o *sendCompressionOption) applyToClient(config *clientConfig) { + config.RequestCompressionName = o.Name +} + +func withGzip() Option { + return &compressionOption{ + Name: compressionGzip, + CompressionPool: newCompressionPool( + func() Decompressor { return &gzip.Reader{} }, + func() Compressor { return gzip.NewWriter(io.Discard) }, + ), + } +} + +func withProtoBinaryCodec() Option { + return WithCodec(&protoBinaryCodec{}) +} + +func withProtoJSONCodecs() HandlerOption { + return WithHandlerOptions( + WithCodec(&protoJSONCodec{codecNameJSON}), + WithCodec(&protoJSONCodec{codecNameJSONCharsetUTF8}), + ) +} + +type conditionalHandlerOptions struct { + conditional func(spec Spec) []HandlerOption +} + +func (o *conditionalHandlerOptions) applyToHandler(config *handlerConfig) { + spec := config.newSpec() + if spec.Procedure == "" { + return // ignore empty specs + } + for _, option := range o.conditional(spec) { + option.applyToHandler(config) + } +} diff --git a/vendor/connectrpc.com/connect/protobuf_util.go b/vendor/connectrpc.com/connect/protobuf_util.go new file mode 100644 index 0000000000..d2563b6d79 --- /dev/null +++ b/vendor/connectrpc.com/connect/protobuf_util.go @@ -0,0 +1,42 @@ +// Copyright 2021-2026 The Connect Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package connect + +import ( + "strings" +) + +// extractProtoPath returns the trailing portion of the URL's path, +// corresponding to the Protobuf package, service, and method. It always starts +// with a slash. Within connect, we use this as (1) Spec.Procedure and (2) the +// path when mounting handlers on muxes. +func extractProtoPath(path string) string { + segments := strings.Split(path, "/") + var pkg, method string + if len(segments) > 0 { + pkg = segments[0] + } + if len(segments) > 1 { + pkg = segments[len(segments)-2] + method = segments[len(segments)-1] + } + if pkg == "" { + return "/" + } + if method == "" { + return "/" + pkg + } + return "/" + pkg + "/" + method +} diff --git a/vendor/connectrpc.com/connect/protocol.go b/vendor/connectrpc.com/connect/protocol.go new file mode 100644 index 0000000000..4e98b2b1a6 --- /dev/null +++ b/vendor/connectrpc.com/connect/protocol.go @@ -0,0 +1,424 @@ +// Copyright 2021-2026 The Connect Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package connect + +import ( + "context" + "errors" + "fmt" + "io" + "mime" + "net/http" + "net/url" + "sort" + "strings" +) + +// The names of the Connect, gRPC, and gRPC-Web protocols (as exposed by +// [Peer].Protocol). Additional protocols may be added in the future. +const ( + ProtocolConnect = "connect" + ProtocolGRPC = "grpc" + ProtocolGRPCWeb = "grpcweb" +) + +const ( + headerContentType = "Content-Type" + headerContentEncoding = "Content-Encoding" + headerContentLength = "Content-Length" + headerHost = "Host" + headerUserAgent = "User-Agent" + headerTrailer = "Trailer" + headerDate = "Date" + + discardLimit = 1024 * 1024 * 4 // 4MiB +) + +var errNoTimeout = errors.New("no timeout") + +// A Protocol defines the HTTP semantics to use when sending and receiving +// messages. It ties together codecs, compressors, and net/http to produce +// Senders and Receivers. +// +// For example, connect supports the gRPC protocol using this abstraction. Among +// many other things, the protocol implementation is responsible for +// translating timeouts from Go contexts to HTTP and vice versa. For gRPC, it +// converts timeouts to and from strings (for example, 10*time.Second <-> +// "10S"), and puts those strings into the "Grpc-Timeout" HTTP header. Other +// protocols might encode durations differently, put them into a different HTTP +// header, or ignore them entirely. +// +// We don't have any short-term plans to export this interface; it's just here +// to separate the protocol-specific portions of connect from the +// protocol-agnostic plumbing. +type protocol interface { + NewHandler(*protocolHandlerParams) protocolHandler + NewClient(*protocolClientParams) (protocolClient, error) +} + +// HandlerParams are the arguments provided to a Protocol's NewHandler +// method, bundled into a struct to allow backward-compatible argument +// additions. Protocol implementations should take care to use the supplied +// Spec rather than constructing their own, since new fields may have been +// added. +type protocolHandlerParams struct { + Spec Spec + Codecs readOnlyCodecs + CompressionPools readOnlyCompressionPools + CompressMinBytes int + BufferPool *bufferPool + ReadMaxBytes int + SendMaxBytes int + RequireConnectProtocolHeader bool + IdempotencyLevel IdempotencyLevel +} + +// Handler is the server side of a protocol. HTTP handlers typically support +// multiple protocols, codecs, and compressors. +type protocolHandler interface { + // Methods is the set of HTTP methods the protocol can handle. + Methods() map[string]struct{} + + // ContentTypes is the set of HTTP Content-Types that the protocol can + // handle. + ContentTypes() map[string]struct{} + + // SetTimeout runs before NewStream. Implementations may inspect the HTTP + // request, parse any timeout set by the client, and return a modified + // context and cancellation function. + // + // If the client didn't send a timeout, SetTimeout should return the + // request's context, a nil cancellation function, and a nil error. + SetTimeout(*http.Request) (context.Context, context.CancelFunc, error) + + // CanHandlePayload returns true if the protocol can handle an HTTP request. + // This is called after the request method is validated, so we only need to + // be concerned with the content type/payload specifically. + CanHandlePayload(*http.Request, string) bool + + // NewConn constructs a HandlerConn for the message exchange. + NewConn(http.ResponseWriter, *http.Request) (handlerConnCloser, bool) +} + +// ClientParams are the arguments provided to a Protocol's NewClient method, +// bundled into a struct to allow backward-compatible argument additions. +// Protocol implementations should take care to use the supplied Spec rather +// than constructing their own, since new fields may have been added. +type protocolClientParams struct { + CompressionName string + CompressionPools readOnlyCompressionPools + Codec Codec + CompressMinBytes int + HTTPClient HTTPClient + URL *url.URL + BufferPool *bufferPool + ReadMaxBytes int + SendMaxBytes int + EnableGet bool + GetURLMaxBytes int + GetUseFallback bool + // The gRPC family of protocols always needs access to a Protobuf codec to + // marshal and unmarshal errors. + Protobuf Codec +} + +// Client is the client side of a protocol. HTTP clients typically use a single +// protocol, codec, and compressor to send requests. +type protocolClient interface { + // Peer describes the server for the RPC. + Peer() Peer + + // WriteRequestHeader writes any protocol-specific request headers. + WriteRequestHeader(StreamType, http.Header) + + // NewConn constructs a StreamingClientConn for the message exchange. + // + // Implementations should assume that the supplied HTTP headers have already + // been populated by WriteRequestHeader. When constructing a stream for a + // unary call, implementations may assume that the Sender's Send and Close + // methods return before the Receiver's Receive or Close methods are called. + NewConn(context.Context, Spec, http.Header) streamingClientConn +} + +// streamingClientConn extends StreamingClientConn with a method for registering +// a hook when the HTTP request is actually sent. +type streamingClientConn interface { + StreamingClientConn + + onRequestSend(fn func(*http.Request)) +} + +// errorTranslatingHandlerConnCloser wraps a handlerConnCloser to ensure that +// we always return coded errors to users and write coded errors to the +// network. +// +// It's used in protocol implementations. +type errorTranslatingHandlerConnCloser struct { + handlerConnCloser + + toWire func(error) error + fromWire func(error) error +} + +func (hc *errorTranslatingHandlerConnCloser) Send(msg any) error { + return hc.fromWire(hc.handlerConnCloser.Send(msg)) +} + +func (hc *errorTranslatingHandlerConnCloser) Receive(msg any) error { + return hc.fromWire(hc.handlerConnCloser.Receive(msg)) +} + +func (hc *errorTranslatingHandlerConnCloser) Close(err error) error { + closeErr := hc.handlerConnCloser.Close(hc.toWire(err)) + return hc.fromWire(closeErr) +} + +func (hc *errorTranslatingHandlerConnCloser) getHTTPMethod() string { + if methoder, ok := hc.handlerConnCloser.(interface{ getHTTPMethod() string }); ok { + return methoder.getHTTPMethod() + } + return http.MethodPost +} + +// errorTranslatingClientConn wraps a StreamingClientConn to make sure that we always +// return coded errors from clients. +// +// It's used in protocol implementations. +type errorTranslatingClientConn struct { + streamingClientConn + + fromWire func(error) error +} + +func (cc *errorTranslatingClientConn) Send(msg any) error { + return cc.fromWire(cc.streamingClientConn.Send(msg)) +} + +func (cc *errorTranslatingClientConn) Receive(msg any) error { + return cc.fromWire(cc.streamingClientConn.Receive(msg)) +} + +func (cc *errorTranslatingClientConn) CloseRequest() error { + return cc.fromWire(cc.streamingClientConn.CloseRequest()) +} + +func (cc *errorTranslatingClientConn) CloseResponse() error { + return cc.fromWire(cc.streamingClientConn.CloseResponse()) +} + +func (cc *errorTranslatingClientConn) onRequestSend(fn func(*http.Request)) { + cc.streamingClientConn.onRequestSend(fn) +} + +// wrapHandlerConnWithCodedErrors ensures that we (1) automatically code +// context-related errors correctly when writing them to the network, and (2) +// return *Errors from all exported APIs. +func wrapHandlerConnWithCodedErrors(conn handlerConnCloser) handlerConnCloser { + return &errorTranslatingHandlerConnCloser{ + handlerConnCloser: conn, + toWire: wrapIfContextError, + fromWire: wrapIfUncoded, + } +} + +// wrapClientConnWithCodedErrors ensures that we always return *Errors from +// public APIs. +func wrapClientConnWithCodedErrors(conn streamingClientConn) streamingClientConn { + return &errorTranslatingClientConn{ + streamingClientConn: conn, + fromWire: wrapIfUncoded, + } +} + +func mappedMethodHandlers(handlers []protocolHandler) map[string][]protocolHandler { + methodHandlers := make(map[string][]protocolHandler) + for _, handler := range handlers { + for method := range handler.Methods() { + methodHandlers[method] = append(methodHandlers[method], handler) + } + } + return methodHandlers +} + +func sortedAcceptPostValue(handlers []protocolHandler) string { + contentTypes := make(map[string]struct{}) + for _, handler := range handlers { + for contentType := range handler.ContentTypes() { + contentTypes[contentType] = struct{}{} + } + } + accept := make([]string, 0, len(contentTypes)) + for ct := range contentTypes { + accept = append(accept, ct) + } + sort.Strings(accept) + return strings.Join(accept, ", ") +} + +func sortedAllowMethodValue(handlers []protocolHandler) string { + methods := make(map[string]struct{}) + for _, handler := range handlers { + for method := range handler.Methods() { + methods[method] = struct{}{} + } + } + allow := make([]string, 0, len(methods)) + for ct := range methods { + allow = append(allow, ct) + } + sort.Strings(allow) + return strings.Join(allow, ", ") +} + +func isCommaOrSpace(c rune) bool { + return c == ',' || c == ' ' +} + +func discard(reader io.Reader) (int64, error) { + if lr, ok := reader.(*io.LimitedReader); ok { + return io.Copy(io.Discard, lr) + } + // We don't want to get stuck throwing data away forever, so limit how much + // we're willing to do here. + lr := &io.LimitedReader{R: reader, N: discardLimit} + return io.Copy(io.Discard, lr) +} + +// negotiateCompression determines and validates the request compression and +// response compression using the available compressors and protocol-specific +// Content-Encoding and Accept-Encoding headers. +func negotiateCompression( //nolint:nonamedreturns + availableCompressors readOnlyCompressionPools, + sent, accept string, +) (requestCompression, responseCompression string, clientVisibleErr *Error) { + requestCompression = compressionIdentity + if sent != "" && sent != compressionIdentity { + // We default to identity, so we only care if the client sends something + // other than the empty string or compressIdentity. + if availableCompressors.Contains(sent) { + requestCompression = sent + } else { + // To comply with + // https://github.com/grpc/grpc/blob/master/doc/compression.md and the + // Connect protocol, we should return CodeUnimplemented and specify + // acceptable compression(s) (in addition to setting the a + // protocol-specific accept-encoding header). + return "", "", errorf( + CodeUnimplemented, + "unknown compression %q: supported encodings are %v", + sent, availableCompressors.CommaSeparatedNames(), + ) + } + } + // Support asymmetric compression. This logic follows + // https://github.com/grpc/grpc/blob/master/doc/compression.md and common + // sense. + responseCompression = requestCompression + // If we're not already planning to compress the response, check whether the + // client requested a compression algorithm we support. + if responseCompression == compressionIdentity && accept != "" { + for _, name := range strings.FieldsFunc(accept, isCommaOrSpace) { + if availableCompressors.Contains(name) { + // We found a mutually supported compression algorithm. Unlike standard + // HTTP, there's no preference weighting, so can bail out immediately. + responseCompression = name + break + } + } + } + return requestCompression, responseCompression, nil +} + +// checkServerStreamsCanFlush ensures that bidi and server streaming handlers +// have received an http.ResponseWriter that implements http.Flusher, since +// they must flush data after sending each message. +func checkServerStreamsCanFlush(spec Spec, responseWriter http.ResponseWriter) *Error { + requiresFlusher := (spec.StreamType & StreamTypeServer) == StreamTypeServer + if _, flushable := responseWriter.(http.Flusher); requiresFlusher && !flushable { + return NewError(CodeInternal, fmt.Errorf("%T does not implement http.Flusher", responseWriter)) + } + return nil +} + +func flushResponseWriter(w http.ResponseWriter) { + if f, ok := w.(http.Flusher); ok { + f.Flush() + } +} + +func canonicalizeContentType(contentType string) string { + // Typically, clients send Content-Type in canonical form, without + // parameters. In those cases, we'd like to avoid parsing and + // canonicalization overhead. + // + // See https://www.rfc-editor.org/rfc/rfc2045.html#section-5.1 for a full + // grammar. + var slashes int + for _, r := range contentType { + switch { + case r >= 'a' && r <= 'z': + case r == '.' || r == '+' || r == '-': + case r == '/': + slashes++ + default: + return canonicalizeContentTypeSlow(contentType) + } + } + if slashes == 1 { + return contentType + } + return canonicalizeContentTypeSlow(contentType) +} + +func canonicalizeContentTypeSlow(contentType string) string { + base, params, err := mime.ParseMediaType(contentType) + if err != nil { + return contentType + } + // According to RFC 9110 Section 8.3.2, the charset parameter value should be treated as case-insensitive. + // mime.FormatMediaType canonicalizes parameter names, but not parameter values, + // because the case sensitivity of a parameter value depends on its semantics. + // Therefore, the charset parameter value should be canonicalized here. + // ref.) https://httpwg.org/specs/rfc9110.html#rfc.section.8.3.2 + if charset, ok := params["charset"]; ok { + params["charset"] = strings.ToLower(charset) + } + return mime.FormatMediaType(base, params) +} + +func httpToCode(httpCode int) Code { + // https://github.com/grpc/grpc/blob/master/doc/http-grpc-status-mapping.md + // Note that this is NOT the inverse of the gRPC-to-HTTP or Connect-to-HTTP + // mappings. + + // Literals are easier to compare to the specification (vs named + // constants). + switch httpCode { + case 400: + return CodeInternal + case 401: + return CodeUnauthenticated + case 403: + return CodePermissionDenied + case 404: + return CodeUnimplemented + case 429: + return CodeUnavailable + case 502, 503, 504: + return CodeUnavailable + default: + return CodeUnknown + } +} diff --git a/vendor/connectrpc.com/connect/protocol_connect.go b/vendor/connectrpc.com/connect/protocol_connect.go new file mode 100644 index 0000000000..80af427078 --- /dev/null +++ b/vendor/connectrpc.com/connect/protocol_connect.go @@ -0,0 +1,1461 @@ +// Copyright 2021-2026 The Connect Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package connect + +import ( + "bytes" + "context" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "io" + "math" + "net/http" + "net/url" + "runtime" + "strconv" + "strings" + "time" + + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/anypb" +) + +const ( + connectUnaryHeaderCompression = "Content-Encoding" + connectUnaryHeaderAcceptCompression = "Accept-Encoding" + connectUnaryTrailerPrefix = "Trailer-" + connectStreamingHeaderCompression = "Connect-Content-Encoding" + connectStreamingHeaderAcceptCompression = "Connect-Accept-Encoding" + connectHeaderTimeout = "Connect-Timeout-Ms" + connectHeaderProtocolVersion = "Connect-Protocol-Version" + connectProtocolVersion = "1" + headerVary = "Vary" + + connectFlagEnvelopeEndStream = 0b00000010 + + connectUnaryContentTypePrefix = "application/" + connectUnaryContentTypeJSON = connectUnaryContentTypePrefix + codecNameJSON + connectStreamingContentTypePrefix = "application/connect+" + + connectUnaryEncodingQueryParameter = "encoding" + connectUnaryMessageQueryParameter = "message" + connectUnaryBase64QueryParameter = "base64" + connectUnaryCompressionQueryParameter = "compression" + connectUnaryConnectQueryParameter = "connect" + connectUnaryConnectQueryValue = "v" + connectProtocolVersion +) + +// defaultConnectUserAgent returns a User-Agent string similar to those used in gRPC. +// +//nolint:gochecknoglobals +var defaultConnectUserAgent = fmt.Sprintf("connect-go/%s (%s)", Version, runtime.Version()) + +type protocolConnect struct{} + +// NewHandler implements protocol, so it must return an interface. +func (*protocolConnect) NewHandler(params *protocolHandlerParams) protocolHandler { + methods := make(map[string]struct{}) + methods[http.MethodPost] = struct{}{} + + if params.Spec.StreamType == StreamTypeUnary && params.IdempotencyLevel == IdempotencyNoSideEffects { + methods[http.MethodGet] = struct{}{} + } + + contentTypes := make(map[string]struct{}) + for _, name := range params.Codecs.Names() { + if params.Spec.StreamType == StreamTypeUnary { + contentTypes[canonicalizeContentType(connectUnaryContentTypePrefix+name)] = struct{}{} + continue + } + contentTypes[canonicalizeContentType(connectStreamingContentTypePrefix+name)] = struct{}{} + } + + return &connectHandler{ + protocolHandlerParams: *params, + methods: methods, + accept: contentTypes, + } +} + +// NewClient implements protocol, so it must return an interface. +func (*protocolConnect) NewClient(params *protocolClientParams) (protocolClient, error) { + return &connectClient{ + protocolClientParams: *params, + peer: newPeerForURL(params.URL, ProtocolConnect), + }, nil +} + +type connectHandler struct { + protocolHandlerParams + + methods map[string]struct{} + accept map[string]struct{} +} + +func (h *connectHandler) Methods() map[string]struct{} { + return h.methods +} + +func (h *connectHandler) ContentTypes() map[string]struct{} { + return h.accept +} + +func (*connectHandler) SetTimeout(request *http.Request) (context.Context, context.CancelFunc, error) { + timeout := getHeaderCanonical(request.Header, connectHeaderTimeout) + if timeout == "" { + return request.Context(), nil, nil + } + if len(timeout) > 10 { + return nil, nil, errorf(CodeInvalidArgument, "parse timeout: %q has >10 digits", timeout) + } + millis, err := strconv.ParseInt(timeout, 10 /* base */, 64 /* bitsize */) + if err != nil { + return nil, nil, errorf(CodeInvalidArgument, "parse timeout: %w", err) + } + ctx, cancel := context.WithTimeout( + request.Context(), + time.Duration(millis)*time.Millisecond, + ) + return ctx, cancel, nil +} + +func (h *connectHandler) CanHandlePayload(request *http.Request, contentType string) bool { + if request.Method == http.MethodGet { + query := request.URL.Query() + codecName := query.Get(connectUnaryEncodingQueryParameter) + contentType = connectContentTypeForCodecName( + h.Spec.StreamType, + codecName, + ) + } + _, ok := h.accept[contentType] + return ok +} + +func (h *connectHandler) NewConn( + responseWriter http.ResponseWriter, + request *http.Request, +) (handlerConnCloser, bool) { + ctx := request.Context() + query := request.URL.Query() + // We need to parse metadata before entering the interceptor stack; we'll + // send the error to the client later on. + var contentEncoding, acceptEncoding string + if h.Spec.StreamType == StreamTypeUnary { + if request.Method == http.MethodGet { + contentEncoding = query.Get(connectUnaryCompressionQueryParameter) + } else { + contentEncoding = getHeaderCanonical(request.Header, connectUnaryHeaderCompression) + } + acceptEncoding = getHeaderCanonical(request.Header, connectUnaryHeaderAcceptCompression) + } else { + contentEncoding = getHeaderCanonical(request.Header, connectStreamingHeaderCompression) + acceptEncoding = getHeaderCanonical(request.Header, connectStreamingHeaderAcceptCompression) + } + requestCompression, responseCompression, failed := negotiateCompression( + h.CompressionPools, + contentEncoding, + acceptEncoding, + ) + if failed == nil { + failed = checkServerStreamsCanFlush(h.Spec, responseWriter) + } + if failed == nil { + required := h.RequireConnectProtocolHeader && (h.Spec.StreamType == StreamTypeUnary) + failed = connectCheckProtocolVersion(request, required) + } + + var requestBody io.ReadCloser + var contentType, codecName string + if request.Method == http.MethodGet { + if failed == nil && !query.Has(connectUnaryEncodingQueryParameter) { + failed = errorf(CodeInvalidArgument, "missing %s parameter", connectUnaryEncodingQueryParameter) + } else if failed == nil && !query.Has(connectUnaryMessageQueryParameter) { + failed = errorf(CodeInvalidArgument, "missing %s parameter", connectUnaryMessageQueryParameter) + } + msg := query.Get(connectUnaryMessageQueryParameter) + msgReader := queryValueReader(msg, query.Get(connectUnaryBase64QueryParameter) == "1") + requestBody = io.NopCloser(msgReader) + codecName = query.Get(connectUnaryEncodingQueryParameter) + contentType = connectContentTypeForCodecName( + h.Spec.StreamType, + codecName, + ) + } else { + requestBody = request.Body + contentType = getHeaderCanonical(request.Header, headerContentType) + codecName = connectCodecForContentType( + h.Spec.StreamType, + contentType, + ) + } + + codec := h.Codecs.Get(codecName) + // The codec can be nil in the GET request case; that's okay: when failed + // is non-nil, codec is never used. + if failed == nil && codec == nil { + failed = errorf(CodeInvalidArgument, "invalid message encoding: %q", codecName) + } + + // Write any remaining headers here: + // (1) any writes to the stream will implicitly send the headers, so we + // should get all of gRPC's required response headers ready. + // (2) interceptors should be able to see these headers. + // + // Since we know that these header keys are already in canonical form, we can + // skip the normalization in Header.Set. + header := responseWriter.Header() + header[headerContentType] = []string{contentType} + acceptCompressionHeader := connectUnaryHeaderAcceptCompression + if h.Spec.StreamType != StreamTypeUnary { + acceptCompressionHeader = connectStreamingHeaderAcceptCompression + // We only write the request encoding header here for streaming calls, + // since the streaming envelope lets us choose whether to compress each + // message individually. For unary, we won't know whether we're compressing + // the request until we see how large the payload is. + if responseCompression != compressionIdentity { + header[connectStreamingHeaderCompression] = []string{responseCompression} + } + } + header[acceptCompressionHeader] = []string{h.CompressionPools.CommaSeparatedNames()} + + var conn handlerConnCloser + peer := Peer{ + Addr: request.RemoteAddr, + Protocol: ProtocolConnect, + Query: query, + } + if h.Spec.StreamType == StreamTypeUnary { + conn = &connectUnaryHandlerConn{ + spec: h.Spec, + peer: peer, + request: request, + responseWriter: responseWriter, + marshaler: connectUnaryMarshaler{ + ctx: ctx, + sender: writeSender{writer: responseWriter}, + codec: codec, + compressMinBytes: h.CompressMinBytes, + compressionName: responseCompression, + compressionPool: h.CompressionPools.Get(responseCompression), + bufferPool: h.BufferPool, + header: responseWriter.Header(), + sendMaxBytes: h.SendMaxBytes, + }, + unmarshaler: connectUnaryUnmarshaler{ + ctx: ctx, + reader: requestBody, + codec: codec, + compressionPool: h.CompressionPools.Get(requestCompression), + bufferPool: h.BufferPool, + readMaxBytes: h.ReadMaxBytes, + }, + responseTrailer: make(http.Header), + } + } else { + conn = &connectStreamingHandlerConn{ + spec: h.Spec, + peer: peer, + request: request, + responseWriter: responseWriter, + marshaler: connectStreamingMarshaler{ + envelopeWriter: envelopeWriter{ + ctx: ctx, + sender: writeSender{responseWriter}, + codec: codec, + compressMinBytes: h.CompressMinBytes, + compressionPool: h.CompressionPools.Get(responseCompression), + bufferPool: h.BufferPool, + sendMaxBytes: h.SendMaxBytes, + }, + }, + unmarshaler: connectStreamingUnmarshaler{ + envelopeReader: envelopeReader{ + ctx: ctx, + reader: requestBody, + codec: codec, + compressionPool: h.CompressionPools.Get(requestCompression), + bufferPool: h.BufferPool, + readMaxBytes: h.ReadMaxBytes, + }, + }, + responseTrailer: make(http.Header), + } + } + conn = wrapHandlerConnWithCodedErrors(conn) + + if failed != nil { + // Negotiation failed, so we can't establish a stream. + _ = conn.Close(failed) + return nil, false + } + return conn, true +} + +type connectClient struct { + protocolClientParams + + peer Peer +} + +func (c *connectClient) Peer() Peer { + return c.peer +} + +func (c *connectClient) WriteRequestHeader(streamType StreamType, header http.Header) { + // We know these header keys are in canonical form, so we can bypass all the + // checks in Header.Set. + if getHeaderCanonical(header, headerUserAgent) == "" { + header[headerUserAgent] = []string{defaultConnectUserAgent} + } + header[connectHeaderProtocolVersion] = []string{connectProtocolVersion} + header[headerContentType] = []string{ + connectContentTypeForCodecName(streamType, c.Codec.Name()), + } + acceptCompressionHeader := connectUnaryHeaderAcceptCompression + if streamType != StreamTypeUnary { + // If we don't set Accept-Encoding, by default http.Client will ask the + // server to compress the whole stream. Since we're already compressing + // each message, this is a waste. + header[connectUnaryHeaderAcceptCompression] = []string{compressionIdentity} + acceptCompressionHeader = connectStreamingHeaderAcceptCompression + // We only write the request encoding header here for streaming calls, + // since the streaming envelope lets us choose whether to compress each + // message individually. For unary, we won't know whether we're compressing + // the request until we see how large the payload is. + if c.CompressionName != "" && c.CompressionName != compressionIdentity { + header[connectStreamingHeaderCompression] = []string{c.CompressionName} + } + } + if acceptCompression := c.CompressionPools.CommaSeparatedNames(); acceptCompression != "" { + header[acceptCompressionHeader] = []string{acceptCompression} + } +} + +func (c *connectClient) NewConn( + ctx context.Context, + spec Spec, + header http.Header, +) streamingClientConn { + if deadline, ok := ctx.Deadline(); ok { + millis := int64(time.Until(deadline) / time.Millisecond) + if millis > 0 { + encoded := strconv.FormatInt(millis, 10 /* base */) + if len(encoded) <= 10 { + header[connectHeaderTimeout] = []string{encoded} + } // else effectively unbounded + } + } + duplexCall := newDuplexHTTPCall(ctx, c.HTTPClient, c.URL, spec, header) + var conn streamingClientConn + if spec.StreamType == StreamTypeUnary { + unaryConn := &connectUnaryClientConn{ + spec: spec, + peer: c.Peer(), + duplexCall: duplexCall, + compressionPools: c.CompressionPools, + bufferPool: c.BufferPool, + marshaler: connectUnaryRequestMarshaler{ + connectUnaryMarshaler: connectUnaryMarshaler{ + ctx: ctx, + sender: duplexCall, + codec: c.Codec, + compressMinBytes: c.CompressMinBytes, + compressionName: c.CompressionName, + compressionPool: c.CompressionPools.Get(c.CompressionName), + bufferPool: c.BufferPool, + header: duplexCall.Header(), + sendMaxBytes: c.SendMaxBytes, + }, + }, + unmarshaler: connectUnaryUnmarshaler{ + ctx: ctx, + reader: duplexCall, + codec: c.Codec, + bufferPool: c.BufferPool, + readMaxBytes: c.ReadMaxBytes, + }, + responseHeader: make(http.Header), + responseTrailer: make(http.Header), + } + if spec.IdempotencyLevel == IdempotencyNoSideEffects { + unaryConn.marshaler.enableGet = c.EnableGet + unaryConn.marshaler.getURLMaxBytes = c.GetURLMaxBytes + unaryConn.marshaler.getUseFallback = c.GetUseFallback + unaryConn.marshaler.duplexCall = duplexCall + if stableCodec, ok := c.Codec.(stableCodec); ok { + unaryConn.marshaler.stableCodec = stableCodec + } + } + conn = unaryConn + duplexCall.SetValidateResponse(unaryConn.validateResponse) + } else { + streamingConn := &connectStreamingClientConn{ + spec: spec, + peer: c.Peer(), + duplexCall: duplexCall, + compressionPools: c.CompressionPools, + bufferPool: c.BufferPool, + codec: c.Codec, + marshaler: connectStreamingMarshaler{ + envelopeWriter: envelopeWriter{ + ctx: ctx, + sender: duplexCall, + codec: c.Codec, + compressMinBytes: c.CompressMinBytes, + compressionPool: c.CompressionPools.Get(c.CompressionName), + bufferPool: c.BufferPool, + sendMaxBytes: c.SendMaxBytes, + }, + }, + unmarshaler: connectStreamingUnmarshaler{ + envelopeReader: envelopeReader{ + ctx: ctx, + reader: duplexCall, + codec: c.Codec, + bufferPool: c.BufferPool, + readMaxBytes: c.ReadMaxBytes, + }, + }, + responseHeader: make(http.Header), + responseTrailer: make(http.Header), + } + conn = streamingConn + duplexCall.SetValidateResponse(streamingConn.validateResponse) + } + return wrapClientConnWithCodedErrors(conn) +} + +type connectUnaryClientConn struct { + spec Spec + peer Peer + duplexCall *duplexHTTPCall + compressionPools readOnlyCompressionPools + bufferPool *bufferPool + marshaler connectUnaryRequestMarshaler + unmarshaler connectUnaryUnmarshaler + responseHeader http.Header + responseTrailer http.Header +} + +func (cc *connectUnaryClientConn) Spec() Spec { + return cc.spec +} + +func (cc *connectUnaryClientConn) Peer() Peer { + return cc.peer +} + +func (cc *connectUnaryClientConn) Send(msg any) error { + if err := cc.marshaler.Marshal(msg); err != nil { + return err + } + return nil // must be a literal nil: nil *Error is a non-nil error +} + +func (cc *connectUnaryClientConn) RequestHeader() http.Header { + return cc.duplexCall.Header() +} + +func (cc *connectUnaryClientConn) CloseRequest() error { + return cc.duplexCall.CloseWrite() +} + +func (cc *connectUnaryClientConn) Receive(msg any) error { + if err := cc.duplexCall.BlockUntilResponseReady(); err != nil { + return err + } + if err := cc.unmarshaler.Unmarshal(msg); err != nil { + return err + } + return nil // must be a literal nil: nil *Error is a non-nil error +} + +func (cc *connectUnaryClientConn) ResponseHeader() http.Header { + _ = cc.duplexCall.BlockUntilResponseReady() + return cc.responseHeader +} + +func (cc *connectUnaryClientConn) ResponseTrailer() http.Header { + _ = cc.duplexCall.BlockUntilResponseReady() + return cc.responseTrailer +} + +func (cc *connectUnaryClientConn) CloseResponse() error { + return cc.duplexCall.CloseRead() +} + +func (cc *connectUnaryClientConn) onRequestSend(fn func(*http.Request)) { + cc.duplexCall.onRequestSend = fn +} + +func (cc *connectUnaryClientConn) validateResponse(response *http.Response) *Error { + for k, v := range response.Header { + if !strings.HasPrefix(k, connectUnaryTrailerPrefix) { + cc.responseHeader[k] = v + continue + } + cc.responseTrailer[k[len(connectUnaryTrailerPrefix):]] = v + } + if err := connectValidateUnaryResponseContentType( + cc.marshaler.codec.Name(), + cc.duplexCall.Method(), + response.StatusCode, + response.Status, + getHeaderCanonical(response.Header, headerContentType), + ); err != nil { + if IsNotModifiedError(err) { + // Allow access to response headers for this kind of error. + // RFC 9110 doesn't allow trailers on 304s, so we only need to include headers. + err.meta = cc.responseHeader.Clone() + } + return err + } + compression := getHeaderCanonical(response.Header, connectUnaryHeaderCompression) + if compression != "" && + compression != compressionIdentity && + !cc.compressionPools.Contains(compression) { + return errorf( + CodeInternal, + "unknown encoding %q: accepted encodings are %v", + compression, + cc.compressionPools.CommaSeparatedNames(), + ) + } + cc.unmarshaler.compressionPool = cc.compressionPools.Get(compression) + if response.StatusCode != http.StatusOK { + unmarshaler := connectUnaryUnmarshaler{ + ctx: cc.unmarshaler.ctx, + reader: response.Body, + compressionPool: cc.unmarshaler.compressionPool, + bufferPool: cc.bufferPool, + } + var wireErr connectWireError + if err := unmarshaler.UnmarshalFunc(&wireErr, json.Unmarshal); err != nil { + return NewError( + httpToCode(response.StatusCode), + errors.New(response.Status), + ) + } + if wireErr.Code == 0 { + // code not set? default to one implied by HTTP status + wireErr.Code = httpToCode(response.StatusCode) + } + serverErr := wireErr.asError() + if serverErr == nil { + return nil + } + serverErr.meta = cc.responseHeader.Clone() + mergeHeaders(serverErr.meta, cc.responseTrailer) + return serverErr + } + return nil +} + +type connectStreamingClientConn struct { + spec Spec + peer Peer + duplexCall *duplexHTTPCall + compressionPools readOnlyCompressionPools + bufferPool *bufferPool + codec Codec + marshaler connectStreamingMarshaler + unmarshaler connectStreamingUnmarshaler + responseHeader http.Header + responseTrailer http.Header +} + +func (cc *connectStreamingClientConn) Spec() Spec { + return cc.spec +} + +func (cc *connectStreamingClientConn) Peer() Peer { + return cc.peer +} + +func (cc *connectStreamingClientConn) Send(msg any) error { + if err := cc.marshaler.Marshal(msg); err != nil { + return err + } + return nil // must be a literal nil: nil *Error is a non-nil error +} + +func (cc *connectStreamingClientConn) RequestHeader() http.Header { + return cc.duplexCall.Header() +} + +func (cc *connectStreamingClientConn) CloseRequest() error { + return cc.duplexCall.CloseWrite() +} + +func (cc *connectStreamingClientConn) Receive(msg any) error { + if err := cc.duplexCall.BlockUntilResponseReady(); err != nil { + return err + } + err := cc.unmarshaler.Unmarshal(msg) + if err == nil { + return nil + } + // See if the server sent an explicit error in the end-of-stream message. + mergeHeaders(cc.responseTrailer, cc.unmarshaler.Trailer()) + if serverErr := cc.unmarshaler.EndStreamError(); serverErr != nil { + // This is expected from a protocol perspective, but receiving an + // end-of-stream message means that we're _not_ getting a regular message. + // For users to realize that the stream has ended, Receive must return an + // error. + serverErr.meta = cc.responseHeader.Clone() + mergeHeaders(serverErr.meta, cc.responseTrailer) + _ = cc.duplexCall.CloseWrite() + return serverErr + } + // If the error is EOF but not from a last message, we want to return + // io.ErrUnexpectedEOF instead. + if errors.Is(err, io.EOF) && !errors.Is(err, errSpecialEnvelope) { + err = errorf(CodeInternal, "protocol error: %w", io.ErrUnexpectedEOF) + } + // There's no error in the trailers, so this was probably an error + // converting the bytes to a message, an error reading from the network, or + // just an EOF. We're going to return it to the user, but we also want to + // close the writer so Send errors out. + _ = cc.duplexCall.CloseWrite() + return err +} + +func (cc *connectStreamingClientConn) ResponseHeader() http.Header { + _ = cc.duplexCall.BlockUntilResponseReady() + return cc.responseHeader +} + +func (cc *connectStreamingClientConn) ResponseTrailer() http.Header { + _ = cc.duplexCall.BlockUntilResponseReady() + return cc.responseTrailer +} + +func (cc *connectStreamingClientConn) CloseResponse() error { + return cc.duplexCall.CloseRead() +} + +func (cc *connectStreamingClientConn) onRequestSend(fn func(*http.Request)) { + cc.duplexCall.onRequestSend = fn +} + +func (cc *connectStreamingClientConn) validateResponse(response *http.Response) *Error { + if response.StatusCode != http.StatusOK { + return errorf(httpToCode(response.StatusCode), "HTTP status %v", response.Status) + } + if err := connectValidateStreamResponseContentType( + cc.codec.Name(), + cc.spec.StreamType, + getHeaderCanonical(response.Header, headerContentType), + ); err != nil { + return err + } + compression := getHeaderCanonical(response.Header, connectStreamingHeaderCompression) + if compression != "" && + compression != compressionIdentity && + !cc.compressionPools.Contains(compression) { + return errorf( + CodeInternal, + "unknown encoding %q: accepted encodings are %v", + compression, + cc.compressionPools.CommaSeparatedNames(), + ) + } + cc.unmarshaler.compressionPool = cc.compressionPools.Get(compression) + mergeHeaders(cc.responseHeader, response.Header) + return nil +} + +type connectUnaryHandlerConn struct { + spec Spec + peer Peer + request *http.Request + responseWriter http.ResponseWriter + marshaler connectUnaryMarshaler + unmarshaler connectUnaryUnmarshaler + responseTrailer http.Header +} + +func (hc *connectUnaryHandlerConn) Spec() Spec { + return hc.spec +} + +func (hc *connectUnaryHandlerConn) Peer() Peer { + return hc.peer +} + +func (hc *connectUnaryHandlerConn) Receive(msg any) error { + if err := hc.unmarshaler.Unmarshal(msg); err != nil { + return err + } + return nil // must be a literal nil: nil *Error is a non-nil error +} + +func (hc *connectUnaryHandlerConn) RequestHeader() http.Header { + return hc.request.Header +} + +func (hc *connectUnaryHandlerConn) Send(msg any) error { + hc.mergeResponseHeader(nil /* error */) + if err := hc.marshaler.Marshal(msg); err != nil { + return err + } + return nil // must be a literal nil: nil *Error is a non-nil error +} + +func (hc *connectUnaryHandlerConn) ResponseHeader() http.Header { + return hc.responseWriter.Header() +} + +func (hc *connectUnaryHandlerConn) ResponseTrailer() http.Header { + return hc.responseTrailer +} + +func (hc *connectUnaryHandlerConn) Close(err error) error { + if !hc.marshaler.wroteHeader { + hc.mergeResponseHeader(err) + // If the handler received a GET request and the resource hasn't changed, + // return a 304. + if len(hc.peer.Query) > 0 && IsNotModifiedError(err) { + hc.responseWriter.WriteHeader(http.StatusNotModified) + return hc.request.Body.Close() + } + } + if err == nil || hc.marshaler.wroteHeader { + return hc.request.Body.Close() + } + // In unary Connect, errors always use application/json. + setHeaderCanonical(hc.responseWriter.Header(), headerContentType, connectUnaryContentTypeJSON) + hc.responseWriter.WriteHeader(connectCodeToHTTP(CodeOf(err))) + data, marshalErr := json.Marshal(newConnectWireError(err)) + if marshalErr != nil { + _ = hc.request.Body.Close() + return errorf(CodeInternal, "marshal error: %w", err) + } + if _, writeErr := hc.responseWriter.Write(data); writeErr != nil { + _ = hc.request.Body.Close() + return writeErr + } + return hc.request.Body.Close() +} + +func (hc *connectUnaryHandlerConn) getHTTPMethod() string { + return hc.request.Method +} + +func (hc *connectUnaryHandlerConn) mergeResponseHeader(err error) { + header := hc.responseWriter.Header() + if hc.request.Method == http.MethodGet { + // The response content varies depending on the compression that the client + // requested (if any). GETs are potentially cacheable, so we should ensure + // that the Vary header includes at least Accept-Encoding (and not overwrite any values already set). + header[headerVary] = append(header[headerVary], connectUnaryHeaderAcceptCompression) + } + if err != nil { + if connectErr, ok := asError(err); ok && !connectErr.wireErr { + mergeNonProtocolHeaders(header, connectErr.meta) + } + } + for k, v := range hc.responseTrailer { + header[connectUnaryTrailerPrefix+k] = v + } +} + +type connectStreamingHandlerConn struct { + spec Spec + peer Peer + request *http.Request + responseWriter http.ResponseWriter + marshaler connectStreamingMarshaler + unmarshaler connectStreamingUnmarshaler + responseTrailer http.Header +} + +func (hc *connectStreamingHandlerConn) Spec() Spec { + return hc.spec +} + +func (hc *connectStreamingHandlerConn) Peer() Peer { + return hc.peer +} + +func (hc *connectStreamingHandlerConn) Receive(msg any) error { + if err := hc.unmarshaler.Unmarshal(msg); err != nil { + // Clients may not send end-of-stream metadata, so we don't need to handle + // errSpecialEnvelope. + return err + } + return nil // must be a literal nil: nil *Error is a non-nil error +} + +func (hc *connectStreamingHandlerConn) RequestHeader() http.Header { + return hc.request.Header +} + +func (hc *connectStreamingHandlerConn) Send(msg any) error { + defer flushResponseWriter(hc.responseWriter) + if err := hc.marshaler.Marshal(msg); err != nil { + return err + } + return nil // must be a literal nil: nil *Error is a non-nil error +} + +func (hc *connectStreamingHandlerConn) ResponseHeader() http.Header { + return hc.responseWriter.Header() +} + +func (hc *connectStreamingHandlerConn) ResponseTrailer() http.Header { + return hc.responseTrailer +} + +func (hc *connectStreamingHandlerConn) Close(err error) error { + defer flushResponseWriter(hc.responseWriter) + if err := hc.marshaler.MarshalEndStream(err, hc.responseTrailer); err != nil { + _ = hc.request.Body.Close() + return err + } + // We don't want to copy unread portions of the body to /dev/null here: if + // the client hasn't closed the request body, we'll block until the server + // timeout kicks in. This could happen because the client is malicious, but + // a well-intentioned client may just not expect the server to be returning + // an error for a streaming RPC. Better to accept that we can't always reuse + // TCP connections. + if err := hc.request.Body.Close(); err != nil { + if connectErr, ok := asError(err); ok { + return connectErr + } + return NewError(CodeUnknown, err) + } + return nil // must be a literal nil: nil *Error is a non-nil error +} + +type connectStreamingMarshaler struct { + envelopeWriter +} + +func (m *connectStreamingMarshaler) MarshalEndStream(err error, trailer http.Header) *Error { + end := &connectEndStreamMessage{Trailer: trailer} + if err != nil { + end.Error = newConnectWireError(err) + if connectErr, ok := asError(err); ok && !connectErr.wireErr { + mergeNonProtocolHeaders(end.Trailer, connectErr.meta) + } + } + data, marshalErr := json.Marshal(end) + if marshalErr != nil { + return errorf(CodeInternal, "marshal end stream: %w", marshalErr) + } + raw := bytes.NewBuffer(data) + defer m.bufferPool.Put(raw) + return m.Write(&envelope{ + Data: raw, + Flags: connectFlagEnvelopeEndStream, + }) +} + +type connectStreamingUnmarshaler struct { + envelopeReader + + endStreamErr *Error + trailer http.Header +} + +func (u *connectStreamingUnmarshaler) Unmarshal(message any) *Error { + err := u.envelopeReader.Unmarshal(message) + if err == nil { + return nil + } + if !errors.Is(err, errSpecialEnvelope) { + return err + } + env := u.last + data := env.Data + u.last.Data = nil // don't keep a reference to it + defer u.bufferPool.Put(data) + if !env.IsSet(connectFlagEnvelopeEndStream) { + return errorf(CodeInternal, "protocol error: invalid envelope flags %d", env.Flags) + } + var end connectEndStreamMessage + if err := json.Unmarshal(data.Bytes(), &end); err != nil { + return errorf(CodeInternal, "unmarshal end stream message: %w", err) + } + for name, value := range end.Trailer { + canonical := http.CanonicalHeaderKey(name) + if name != canonical { + delHeaderCanonical(end.Trailer, name) + end.Trailer[canonical] = append(end.Trailer[canonical], value...) + } + } + u.trailer = end.Trailer + u.endStreamErr = end.Error.asError() + return errSpecialEnvelope +} + +func (u *connectStreamingUnmarshaler) Trailer() http.Header { + return u.trailer +} + +func (u *connectStreamingUnmarshaler) EndStreamError() *Error { + return u.endStreamErr +} + +type connectUnaryMarshaler struct { + ctx context.Context //nolint:containedctx + sender messageSender + codec Codec + compressMinBytes int + compressionName string + compressionPool *compressionPool + bufferPool *bufferPool + header http.Header + sendMaxBytes int + wroteHeader bool +} + +func (m *connectUnaryMarshaler) Marshal(message any) *Error { + if message == nil { + return m.write(nil) + } + var data []byte + var err error + if appender, ok := m.codec.(marshalAppender); ok { + data, err = appender.MarshalAppend(m.bufferPool.Get().Bytes(), message) + } else { + // Can't avoid allocating the slice, but we'll reuse it. + data, err = m.codec.Marshal(message) + } + if err != nil { + return errorf(CodeInternal, "marshal message: %w", err) + } + uncompressed := bytes.NewBuffer(data) + defer m.bufferPool.Put(uncompressed) + if len(data) < m.compressMinBytes || m.compressionPool == nil { + if m.sendMaxBytes > 0 && len(data) > m.sendMaxBytes { + return NewError(CodeResourceExhausted, fmt.Errorf("message size %d exceeds sendMaxBytes %d", len(data), m.sendMaxBytes)) + } + return m.write(data) + } + compressed := m.bufferPool.Get() + defer m.bufferPool.Put(compressed) + if err := m.compressionPool.Compress(compressed, uncompressed); err != nil { + return err + } + if m.sendMaxBytes > 0 && compressed.Len() > m.sendMaxBytes { + return NewError(CodeResourceExhausted, fmt.Errorf("compressed message size %d exceeds sendMaxBytes %d", compressed.Len(), m.sendMaxBytes)) + } + setHeaderCanonical(m.header, connectUnaryHeaderCompression, m.compressionName) + return m.write(compressed.Bytes()) +} + +func (m *connectUnaryMarshaler) write(data []byte) *Error { + m.wroteHeader = true + payload := bytes.NewReader(data) + if _, err := m.sender.Send(payload); err != nil { + err = wrapIfContextError(err) + if connectErr, ok := asError(err); ok { + return connectErr + } + return errorf(CodeUnknown, "write message: %w", err) + } + return nil +} + +type connectUnaryRequestMarshaler struct { + connectUnaryMarshaler + + enableGet bool + getURLMaxBytes int + getUseFallback bool + stableCodec stableCodec + duplexCall *duplexHTTPCall +} + +func (m *connectUnaryRequestMarshaler) Marshal(message any) *Error { + if m.enableGet { + if m.stableCodec == nil && !m.getUseFallback { + return errorf(CodeInternal, "codec %s doesn't support stable marshal; can't use get", m.codec.Name()) + } + if m.stableCodec != nil { + return m.marshalWithGet(message) + } + } + return m.connectUnaryMarshaler.Marshal(message) +} + +func (m *connectUnaryRequestMarshaler) marshalWithGet(message any) *Error { + // TODO(jchadwick-buf): This function is mostly a superset of + // connectUnaryMarshaler.Marshal. This should be reconciled at some point. + var data []byte + var err error + if message != nil { + data, err = m.stableCodec.MarshalStable(message) + if err != nil { + return errorf(CodeInternal, "marshal message stable: %w", err) + } + } + isTooBig := m.sendMaxBytes > 0 && len(data) > m.sendMaxBytes + if isTooBig && m.compressionPool == nil { + return NewError(CodeResourceExhausted, fmt.Errorf( + "message size %d exceeds sendMaxBytes %d: enabling request compression may help", + len(data), + m.sendMaxBytes, + )) + } + if !isTooBig { + url := m.buildGetURL(data, false /* compressed */) + if m.getURLMaxBytes <= 0 || len(url.String()) < m.getURLMaxBytes { + m.writeWithGet(url) + return nil + } + if m.compressionPool == nil { + if m.getUseFallback { + return m.write(data) + } + return NewError(CodeResourceExhausted, fmt.Errorf( + "url size %d exceeds getURLMaxBytes %d: enabling request compression may help", + len(url.String()), + m.getURLMaxBytes, + )) + } + } + // Compress message to try to make it fit in the URL. + uncompressed := bytes.NewBuffer(data) + defer m.bufferPool.Put(uncompressed) + compressed := m.bufferPool.Get() + defer m.bufferPool.Put(compressed) + if err := m.compressionPool.Compress(compressed, uncompressed); err != nil { + return err + } + if m.sendMaxBytes > 0 && compressed.Len() > m.sendMaxBytes { + return NewError(CodeResourceExhausted, fmt.Errorf("compressed message size %d exceeds sendMaxBytes %d", compressed.Len(), m.sendMaxBytes)) + } + url := m.buildGetURL(compressed.Bytes(), true /* compressed */) + if m.getURLMaxBytes <= 0 || len(url.String()) < m.getURLMaxBytes { + m.writeWithGet(url) + return nil + } + if m.getUseFallback { + setHeaderCanonical(m.header, connectUnaryHeaderCompression, m.compressionName) + return m.write(compressed.Bytes()) + } + return NewError(CodeResourceExhausted, fmt.Errorf("compressed url size %d exceeds getURLMaxBytes %d", len(url.String()), m.getURLMaxBytes)) +} + +func (m *connectUnaryRequestMarshaler) buildGetURL(data []byte, compressed bool) *url.URL { + var query strings.Builder + appendQueryParam(&query, false, connectUnaryConnectQueryParameter, connectUnaryConnectQueryValue) + binary := m.stableCodec.IsBinary() || compressed + if binary { + appendQueryParam(&query, true, connectUnaryBase64QueryParameter, "1") + } + if compressed { + appendQueryParam(&query, true, connectUnaryCompressionQueryParameter, url.QueryEscape(m.compressionName)) + } + appendQueryParam(&query, true, connectUnaryEncodingQueryParameter, url.QueryEscape(m.codec.Name())) + if binary { + appendQueryParam(&query, true, connectUnaryMessageQueryParameter, encodeBinaryQueryValue(data)) + } else { + appendQueryParam(&query, true, connectUnaryMessageQueryParameter, url.QueryEscape(string(data))) + } + target := *m.duplexCall.URL() + target.RawQuery = query.String() + return &target +} + +func appendQueryParam(query *strings.Builder, withSeparator bool, key, escapedValue string) { + if withSeparator { + query.WriteByte('&') + } + query.WriteString(key) + query.WriteByte('=') + query.WriteString(escapedValue) +} + +func (m *connectUnaryRequestMarshaler) writeWithGet(url *url.URL) { + delHeaderCanonical(m.header, connectHeaderProtocolVersion) + delHeaderCanonical(m.header, headerContentType) + delHeaderCanonical(m.header, headerContentEncoding) + delHeaderCanonical(m.header, headerContentLength) + m.duplexCall.SetMethod(http.MethodGet) + *m.duplexCall.URL() = *url +} + +type connectUnaryUnmarshaler struct { + ctx context.Context //nolint:containedctx + reader io.Reader + codec Codec + compressionPool *compressionPool + bufferPool *bufferPool + alreadyRead bool + readMaxBytes int +} + +func (u *connectUnaryUnmarshaler) Unmarshal(message any) *Error { + return u.UnmarshalFunc(message, u.codec.Unmarshal) +} + +func (u *connectUnaryUnmarshaler) UnmarshalFunc(message any, unmarshal func([]byte, any) error) *Error { + if u.alreadyRead { + return NewError(CodeInternal, io.EOF) + } + u.alreadyRead = true + data := u.bufferPool.Get() + defer u.bufferPool.Put(data) + reader := u.reader + if u.readMaxBytes > 0 && int64(u.readMaxBytes) < math.MaxInt64 { + reader = io.LimitReader(u.reader, int64(u.readMaxBytes)+1) + } + // ReadFor ignores io.EOF, so any error here is real. + bytesRead, err := data.ReadFrom(reader) + if err != nil { + err = wrapIfMaxBytesError(err, "read first %d bytes of message", bytesRead) + err = wrapIfContextDone(u.ctx, err) + if connectErr, ok := asError(err); ok { + return connectErr + } + return errorf(CodeUnknown, "read message: %w", err) + } + if u.readMaxBytes > 0 && bytesRead > int64(u.readMaxBytes) { + // Attempt to read to end in order to allow connection re-use + discardedBytes, err := io.Copy(io.Discard, u.reader) + if err != nil { + return errorf(CodeResourceExhausted, "message is larger than configured max %d - unable to determine message size: %w", u.readMaxBytes, err) + } + return errorf(CodeResourceExhausted, "message size %d is larger than configured max %d", bytesRead+discardedBytes, u.readMaxBytes) + } + if data.Len() > 0 && u.compressionPool != nil { + decompressed := u.bufferPool.Get() + defer u.bufferPool.Put(decompressed) + if err := u.compressionPool.Decompress(decompressed, data, int64(u.readMaxBytes)); err != nil { + return err + } + data = decompressed + } + if err := unmarshal(data.Bytes(), message); err != nil { + return errorf(CodeInvalidArgument, "unmarshal message: %w", err) + } + return nil +} + +type connectWireDetail ErrorDetail + +func (d *connectWireDetail) MarshalJSON() ([]byte, error) { + if d.wireJSON != "" { + // If we unmarshaled this detail from JSON, return the original data. This + // lets proxies w/o protobuf descriptors preserve human-readable details. + return []byte(d.wireJSON), nil + } + wire := struct { + Type string `json:"type"` + Value string `json:"value"` + Debug json.RawMessage `json:"debug,omitempty"` + }{ + Type: typeNameForURL(d.pbAny.GetTypeUrl()), + Value: base64.RawStdEncoding.EncodeToString(d.pbAny.GetValue()), + } + // Try to produce debug info, but expect failure when we don't have + // descriptors. + msg, err := d.getInner() + if err == nil { + var codec protoJSONCodec + debug, err := codec.Marshal(msg) + if err == nil { + wire.Debug = debug + } + } + return json.Marshal(wire) +} + +func (d *connectWireDetail) UnmarshalJSON(data []byte) error { + var wire struct { + Type string `json:"type"` + Value string `json:"value"` + } + if err := json.Unmarshal(data, &wire); err != nil { + return err + } + if !strings.Contains(wire.Type, "/") { + wire.Type = defaultAnyResolverPrefix + wire.Type + } + decoded, err := DecodeBinaryHeader(wire.Value) + if err != nil { + return fmt.Errorf("decode base64: %w", err) + } + *d = connectWireDetail{ + pbAny: &anypb.Any{ + TypeUrl: wire.Type, + Value: decoded, + }, + wireJSON: string(data), + } + return nil +} + +func (d *connectWireDetail) getInner() (proto.Message, error) { + if d.pbInner != nil { + return d.pbInner, nil + } + return d.pbAny.UnmarshalNew() +} + +type connectWireError struct { + Code Code `json:"code"` + Message string `json:"message,omitempty"` + Details []*connectWireDetail `json:"details,omitempty"` +} + +func newConnectWireError(err error) *connectWireError { + wire := &connectWireError{ + Code: CodeUnknown, + Message: err.Error(), + } + if connectErr, ok := asError(err); ok { + wire.Code = connectErr.Code() + wire.Message = connectErr.Message() + if len(connectErr.details) > 0 { + wire.Details = make([]*connectWireDetail, len(connectErr.details)) + for i, detail := range connectErr.details { + wire.Details[i] = (*connectWireDetail)(detail) + } + } + } + return wire +} + +func (e *connectWireError) asError() *Error { + if e == nil { + return nil + } + if e.Code < minCode || e.Code > maxCode { + e.Code = CodeUnknown + } + err := NewWireError(e.Code, errors.New(e.Message)) + if len(e.Details) > 0 { + err.details = make([]*ErrorDetail, len(e.Details)) + for i, detail := range e.Details { + err.details[i] = (*ErrorDetail)(detail) + } + } + return err +} + +func (e *connectWireError) UnmarshalJSON(data []byte) error { + // We want to be lenient if the JSON has an unrecognized or invalid code. + // So if that occurs, we leave the code unset but can still de-serialize + // the other fields from the input JSON. + var wireError struct { + Code string `json:"code"` + Message string `json:"message"` + Details []*connectWireDetail `json:"details"` + } + err := json.Unmarshal(data, &wireError) + if err != nil { + return err + } + e.Message = wireError.Message + e.Details = wireError.Details + // This will leave e.Code unset if we can't unmarshal the given string. + _ = e.Code.UnmarshalText([]byte(wireError.Code)) + return nil +} + +type connectEndStreamMessage struct { + Error *connectWireError `json:"error,omitempty"` + Trailer http.Header `json:"metadata,omitempty"` +} + +func connectCodeToHTTP(code Code) int { + // Return literals rather than named constants from the HTTP package to make + // it easier to compare this function to the Connect specification. + switch code { + case CodeCanceled: + return 499 + case CodeUnknown: + return 500 + case CodeInvalidArgument: + return 400 + case CodeDeadlineExceeded: + return 504 + case CodeNotFound: + return 404 + case CodeAlreadyExists: + return 409 + case CodePermissionDenied: + return 403 + case CodeResourceExhausted: + return 429 + case CodeFailedPrecondition: + return 400 + case CodeAborted: + return 409 + case CodeOutOfRange: + return 400 + case CodeUnimplemented: + return 501 + case CodeInternal: + return 500 + case CodeUnavailable: + return 503 + case CodeDataLoss: + return 500 + case CodeUnauthenticated: + return 401 + default: + return 500 // same as CodeUnknown + } +} + +func connectCodecForContentType(streamType StreamType, contentType string) string { + if streamType == StreamTypeUnary { + return strings.TrimPrefix(contentType, connectUnaryContentTypePrefix) + } + return strings.TrimPrefix(contentType, connectStreamingContentTypePrefix) +} + +func connectContentTypeForCodecName(streamType StreamType, name string) string { + if streamType == StreamTypeUnary { + return connectUnaryContentTypePrefix + name + } + return connectStreamingContentTypePrefix + name +} + +// encodeBinaryQueryValue URL-safe base64-encodes data, without padding. +func encodeBinaryQueryValue(data []byte) string { + return base64.RawURLEncoding.EncodeToString(data) +} + +// binaryQueryValueReader creates a reader that can read either padded or +// unpadded URL-safe base64 from a string. +func binaryQueryValueReader(data string) io.Reader { + stringReader := strings.NewReader(data) + if len(data)%4 != 0 { + // Data definitely isn't padded. + return base64.NewDecoder(base64.RawURLEncoding, stringReader) + } + // Data is padded, or no padding was necessary. + return base64.NewDecoder(base64.URLEncoding, stringReader) +} + +// queryValueReader creates a reader for a string that may be URL-safe base64 +// encoded. +func queryValueReader(data string, base64Encoded bool) io.Reader { + if base64Encoded { + return binaryQueryValueReader(data) + } + return strings.NewReader(data) +} + +func connectValidateUnaryResponseContentType( + requestCodecName string, + httpMethod string, + statusCode int, + statusMsg string, + responseContentType string, +) *Error { + if statusCode != http.StatusOK { + if statusCode == http.StatusNotModified && httpMethod == http.MethodGet { + return NewWireError(CodeUnknown, errNotModifiedClient) + } + // Error responses must be JSON-encoded. + if responseContentType == connectUnaryContentTypePrefix+codecNameJSON || + responseContentType == connectUnaryContentTypePrefix+codecNameJSONCharsetUTF8 { + return nil + } + return NewError( + httpToCode(statusCode), + errors.New(statusMsg), + ) + } + // Normal responses must have valid content-type that indicates same codec as the request. + if !strings.HasPrefix(responseContentType, connectUnaryContentTypePrefix) { + // Doesn't even look like a Connect response? Use code "unknown". + return errorf( + CodeUnknown, + "invalid content-type: %q; expecting %q", + responseContentType, + connectUnaryContentTypePrefix+requestCodecName, + ) + } + responseCodecName := connectCodecForContentType( + StreamTypeUnary, + responseContentType, + ) + if responseCodecName == requestCodecName { + return nil + } + // HACK: We likely want a better way to handle the optional "charset" parameter + // for application/json, instead of hard-coding. But this suffices for now. + if (responseCodecName == codecNameJSON && requestCodecName == codecNameJSONCharsetUTF8) || + (responseCodecName == codecNameJSONCharsetUTF8 && requestCodecName == codecNameJSON) { + // Both are JSON + return nil + } + return errorf( + CodeInternal, + "invalid content-type: %q; expecting %q", + responseContentType, + connectUnaryContentTypePrefix+requestCodecName, + ) +} + +func connectValidateStreamResponseContentType(requestCodecName string, streamType StreamType, responseContentType string) *Error { + // Responses must have valid content-type that indicates same codec as the request. + if !strings.HasPrefix(responseContentType, connectStreamingContentTypePrefix) { + // Doesn't even look like a Connect response? Use code "unknown". + return errorf( + CodeUnknown, + "invalid content-type: %q; expecting %q", + responseContentType, + connectStreamingContentTypePrefix+requestCodecName, + ) + } + responseCodecName := connectCodecForContentType( + streamType, + responseContentType, + ) + if responseCodecName != requestCodecName { + return errorf( + CodeInternal, + "invalid content-type: %q; expecting %q", + responseContentType, + connectStreamingContentTypePrefix+requestCodecName, + ) + } + return nil +} + +func connectCheckProtocolVersion(request *http.Request, required bool) *Error { + switch request.Method { + case http.MethodGet: + version := request.URL.Query().Get(connectUnaryConnectQueryParameter) + if version == "" && required { + return errorf(CodeInvalidArgument, "missing required query parameter: set %s to %q", connectUnaryConnectQueryParameter, connectUnaryConnectQueryValue) + } else if version != "" && version != connectUnaryConnectQueryValue { + return errorf(CodeInvalidArgument, "%s must be %q: got %q", connectUnaryConnectQueryParameter, connectUnaryConnectQueryValue, version) + } + case http.MethodPost: + version := getHeaderCanonical(request.Header, connectHeaderProtocolVersion) + if version == "" && required { + return errorf(CodeInvalidArgument, "missing required header: set %s to %q", connectHeaderProtocolVersion, connectProtocolVersion) + } else if version != "" && version != connectProtocolVersion { + return errorf(CodeInvalidArgument, "%s must be %q: got %q", connectHeaderProtocolVersion, connectProtocolVersion, version) + } + default: + return errorf(CodeInvalidArgument, "unsupported method: %q", request.Method) + } + return nil +} diff --git a/vendor/connectrpc.com/connect/protocol_grpc.go b/vendor/connectrpc.com/connect/protocol_grpc.go new file mode 100644 index 0000000000..e40a80d809 --- /dev/null +++ b/vendor/connectrpc.com/connect/protocol_grpc.go @@ -0,0 +1,1010 @@ +// Copyright 2021-2026 The Connect Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package connect + +import ( + "bufio" + "context" + "errors" + "fmt" + "io" + "math" + "net/http" + "net/textproto" + "runtime" + "strconv" + "strings" + "time" + + statusv1 "connectrpc.com/connect/internal/gen/connectext/grpc/status/v1" +) + +const ( + grpcHeaderCompression = "Grpc-Encoding" + grpcHeaderAcceptCompression = "Grpc-Accept-Encoding" + grpcHeaderTimeout = "Grpc-Timeout" + grpcHeaderStatus = "Grpc-Status" + grpcHeaderMessage = "Grpc-Message" + grpcHeaderDetails = "Grpc-Status-Details-Bin" + + grpcFlagEnvelopeTrailer = 0b10000000 + + grpcContentTypeDefault = "application/grpc" + grpcWebContentTypeDefault = "application/grpc-web" + grpcContentTypePrefix = grpcContentTypeDefault + "+" + grpcWebContentTypePrefix = grpcWebContentTypeDefault + "+" + + headerXUserAgent = "X-User-Agent" + + upperhex = "0123456789ABCDEF" +) + +var ( + errTrailersWithoutGRPCStatus = fmt.Errorf("protocol error: no %s trailer: %w", grpcHeaderStatus, io.ErrUnexpectedEOF) + + // defaultGrpcUserAgent follows + // https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#user-agents: + // + // While the protocol does not require a user-agent to function it is recommended + // that clients provide a structured user-agent string that provides a basic + // description of the calling library, version & platform to facilitate issue diagnosis + // in heterogeneous environments. The following structure is recommended to library developers: + // + // User-Agent → "grpc-" Language ?("-" Variant) "/" Version ?( " (" *(AdditionalProperty ";") ")" ) + // + //nolint:gochecknoglobals + defaultGrpcUserAgent = fmt.Sprintf("grpc-go-connect/%s (%s)", Version, runtime.Version()) + //nolint:gochecknoglobals + grpcAllowedMethods = map[string]struct{}{ + http.MethodPost: {}, + } +) + +type protocolGRPC struct { + web bool +} + +// NewHandler implements protocol, so it must return an interface. +func (g *protocolGRPC) NewHandler(params *protocolHandlerParams) protocolHandler { + bare, prefix := grpcContentTypeDefault, grpcContentTypePrefix + if g.web { + bare, prefix = grpcWebContentTypeDefault, grpcWebContentTypePrefix + } + contentTypes := make(map[string]struct{}) + for _, name := range params.Codecs.Names() { + contentTypes[canonicalizeContentType(prefix+name)] = struct{}{} + } + if params.Codecs.Get(codecNameProto) != nil { + contentTypes[bare] = struct{}{} + } + return &grpcHandler{ + protocolHandlerParams: *params, + web: g.web, + accept: contentTypes, + } +} + +// NewClient implements protocol, so it must return an interface. +func (g *protocolGRPC) NewClient(params *protocolClientParams) (protocolClient, error) { + peer := newPeerForURL(params.URL, ProtocolGRPC) + if g.web { + peer = newPeerForURL(params.URL, ProtocolGRPCWeb) + } + return &grpcClient{ + protocolClientParams: *params, + web: g.web, + peer: peer, + }, nil +} + +type grpcHandler struct { + protocolHandlerParams + + web bool + accept map[string]struct{} +} + +func (g *grpcHandler) Methods() map[string]struct{} { + return grpcAllowedMethods +} + +func (g *grpcHandler) ContentTypes() map[string]struct{} { + return g.accept +} + +func (*grpcHandler) SetTimeout(request *http.Request) (context.Context, context.CancelFunc, error) { + timeout, err := grpcParseTimeout(getHeaderCanonical(request.Header, grpcHeaderTimeout)) + if err != nil && !errors.Is(err, errNoTimeout) { + // Errors here indicate that the client sent an invalid timeout header, so + // the error text is safe to send back. + return nil, nil, NewError(CodeInvalidArgument, err) + } else if err != nil { + // err wraps errNoTimeout, nothing to do. + return request.Context(), nil, nil //nolint:nilerr + } + ctx, cancel := context.WithTimeout(request.Context(), timeout) + return ctx, cancel, nil +} + +func (g *grpcHandler) CanHandlePayload(_ *http.Request, contentType string) bool { + _, ok := g.accept[contentType] + return ok +} + +func (g *grpcHandler) NewConn( + responseWriter http.ResponseWriter, + request *http.Request, +) (handlerConnCloser, bool) { + ctx := request.Context() + // We need to parse metadata before entering the interceptor stack; we'll + // send the error to the client later on. + requestCompression, responseCompression, failed := negotiateCompression( + g.CompressionPools, + getHeaderCanonical(request.Header, grpcHeaderCompression), + getHeaderCanonical(request.Header, grpcHeaderAcceptCompression), + ) + if failed == nil { + failed = checkServerStreamsCanFlush(g.Spec, responseWriter) + } + + // Write any remaining headers here: + // (1) any writes to the stream will implicitly send the headers, so we + // should get all of gRPC's required response headers ready. + // (2) interceptors should be able to see these headers. + // + // Since we know that these header keys are already in canonical form, we can + // skip the normalization in Header.Set. + header := responseWriter.Header() + header[headerContentType] = []string{getHeaderCanonical(request.Header, headerContentType)} + header[grpcHeaderAcceptCompression] = []string{g.CompressionPools.CommaSeparatedNames()} + if responseCompression != compressionIdentity { + header[grpcHeaderCompression] = []string{responseCompression} + } + + codecName := grpcCodecForContentType(g.web, getHeaderCanonical(request.Header, headerContentType)) + codec := g.Codecs.Get(codecName) // handler.go guarantees this is not nil + protocolName := ProtocolGRPC + if g.web { + protocolName = ProtocolGRPCWeb + } + conn := wrapHandlerConnWithCodedErrors(&grpcHandlerConn{ + spec: g.Spec, + peer: Peer{ + Addr: request.RemoteAddr, + Protocol: protocolName, + }, + web: g.web, + bufferPool: g.BufferPool, + protobuf: g.Codecs.Protobuf(), // for errors + marshaler: grpcMarshaler{ + envelopeWriter: envelopeWriter{ + ctx: ctx, + sender: writeSender{writer: responseWriter}, + compressionPool: g.CompressionPools.Get(responseCompression), + codec: codec, + compressMinBytes: g.CompressMinBytes, + bufferPool: g.BufferPool, + sendMaxBytes: g.SendMaxBytes, + }, + }, + responseWriter: responseWriter, + responseHeader: make(http.Header), + responseTrailer: make(http.Header), + request: request, + unmarshaler: grpcUnmarshaler{ + envelopeReader: envelopeReader{ + ctx: ctx, + reader: request.Body, + codec: codec, + compressionPool: g.CompressionPools.Get(requestCompression), + bufferPool: g.BufferPool, + readMaxBytes: g.ReadMaxBytes, + }, + web: g.web, + }, + }) + if failed != nil { + // Negotiation failed, so we can't establish a stream. + _ = conn.Close(failed) + return nil, false + } + return conn, true +} + +type grpcClient struct { + protocolClientParams + + web bool + peer Peer +} + +func (g *grpcClient) Peer() Peer { + return g.peer +} + +func (g *grpcClient) WriteRequestHeader(_ StreamType, header http.Header) { + // We know these header keys are in canonical form, so we can bypass all the + // checks in Header.Set. + if getHeaderCanonical(header, headerUserAgent) == "" { + header[headerUserAgent] = []string{defaultGrpcUserAgent} + } + if g.web && getHeaderCanonical(header, headerXUserAgent) == "" { + // The gRPC-Web pseudo-specification seems to require X-User-Agent rather + // than User-Agent for all clients, even if they're not browser-based. This + // is very odd for a backend client, so we'll split the difference and set + // both. + header[headerXUserAgent] = []string{defaultGrpcUserAgent} + } + header[headerContentType] = []string{grpcContentTypeForCodecName(g.web, g.Codec.Name())} + // gRPC handles compression on a per-message basis, so we don't want to + // compress the whole stream. By default, http.Client will ask the server + // to gzip the stream if we don't set Accept-Encoding. + header["Accept-Encoding"] = []string{compressionIdentity} + if g.CompressionName != "" && g.CompressionName != compressionIdentity { + header[grpcHeaderCompression] = []string{g.CompressionName} + } + if acceptCompression := g.CompressionPools.CommaSeparatedNames(); acceptCompression != "" { + header[grpcHeaderAcceptCompression] = []string{acceptCompression} + } + if !g.web { + // The gRPC-HTTP2 specification requires this - it flushes out proxies that + // don't support HTTP trailers. + header["Te"] = []string{"trailers"} + } +} + +func (g *grpcClient) NewConn( + ctx context.Context, + spec Spec, + header http.Header, +) streamingClientConn { + if deadline, ok := ctx.Deadline(); ok { + encodedDeadline := grpcEncodeTimeout(time.Until(deadline)) + header[grpcHeaderTimeout] = []string{encodedDeadline} + } + duplexCall := newDuplexHTTPCall( + ctx, + g.HTTPClient, + g.URL, + spec, + header, + ) + conn := &grpcClientConn{ + spec: spec, + peer: g.Peer(), + duplexCall: duplexCall, + compressionPools: g.CompressionPools, + bufferPool: g.BufferPool, + protobuf: g.Protobuf, + marshaler: grpcMarshaler{ + envelopeWriter: envelopeWriter{ + ctx: ctx, + sender: duplexCall, + compressionPool: g.CompressionPools.Get(g.CompressionName), + codec: g.Codec, + compressMinBytes: g.CompressMinBytes, + bufferPool: g.BufferPool, + sendMaxBytes: g.SendMaxBytes, + }, + }, + unmarshaler: grpcUnmarshaler{ + envelopeReader: envelopeReader{ + ctx: ctx, + reader: duplexCall, + codec: g.Codec, + bufferPool: g.BufferPool, + readMaxBytes: g.ReadMaxBytes, + }, + }, + responseHeader: make(http.Header), + responseTrailer: make(http.Header), + } + duplexCall.SetValidateResponse(conn.validateResponse) + if g.web { + conn.unmarshaler.web = true + conn.readTrailers = func(unmarshaler *grpcUnmarshaler, _ *duplexHTTPCall) http.Header { + return unmarshaler.WebTrailer() + } + } else { + conn.readTrailers = func(_ *grpcUnmarshaler, call *duplexHTTPCall) http.Header { + // To access HTTP trailers, we need to read the body to EOF. + _, _ = discard(call) + return call.ResponseTrailer() + } + } + return wrapClientConnWithCodedErrors(conn) +} + +// grpcClientConn works for both gRPC and gRPC-Web. +type grpcClientConn struct { + spec Spec + peer Peer + duplexCall *duplexHTTPCall + compressionPools readOnlyCompressionPools + bufferPool *bufferPool + protobuf Codec // for errors + marshaler grpcMarshaler + unmarshaler grpcUnmarshaler + responseHeader http.Header + responseTrailer http.Header + readTrailers func(*grpcUnmarshaler, *duplexHTTPCall) http.Header +} + +func (cc *grpcClientConn) Spec() Spec { + return cc.spec +} + +func (cc *grpcClientConn) Peer() Peer { + return cc.peer +} + +func (cc *grpcClientConn) Send(msg any) error { + if err := cc.marshaler.Marshal(msg); err != nil { + return err + } + return nil // must be a literal nil: nil *Error is a non-nil error +} + +func (cc *grpcClientConn) RequestHeader() http.Header { + return cc.duplexCall.Header() +} + +func (cc *grpcClientConn) CloseRequest() error { + return cc.duplexCall.CloseWrite() +} + +func (cc *grpcClientConn) Receive(msg any) error { + if err := cc.duplexCall.BlockUntilResponseReady(); err != nil { + return err + } + err := cc.unmarshaler.Unmarshal(msg) + if err == nil { + return nil + } + mergeHeaders( + cc.responseTrailer, + cc.readTrailers(&cc.unmarshaler, cc.duplexCall), + ) + if errors.Is(err, io.EOF) && cc.unmarshaler.bytesRead == 0 && len(cc.responseTrailer) == 0 { + // No body and no trailers means a trailers-only response. + // Note: per the specification, only the HTTP status code and Content-Type + // should be treated as headers. The rest should be treated as trailing + // metadata. But it would be unsafe to mutate cc.responseHeader at this + // point. So we'll leave cc.responseHeader alone but copy the relevant + // metadata into cc.responseTrailer. + mergeHeaders(cc.responseTrailer, cc.responseHeader) + delHeaderCanonical(cc.responseTrailer, headerContentType) + + // Try to read the status out of the headers. + serverErr := grpcErrorForTrailer(cc.protobuf, cc.responseHeader) + if serverErr == nil { + // Status says "OK". So return original error (io.EOF). + return err + } + serverErr.meta = cc.responseHeader.Clone() + return serverErr + } + + // See if the server sent an explicit error in the HTTP or gRPC-Web trailers. + serverErr := grpcErrorForTrailer(cc.protobuf, cc.responseTrailer) + if serverErr != nil && (errors.Is(err, io.EOF) || !errors.Is(serverErr, errTrailersWithoutGRPCStatus)) { + // We've either: + // - Cleanly read until the end of the response body and *not* received + // gRPC status trailers, which is a protocol error, or + // - Received an explicit error from the server. + // + // This is expected from a protocol perspective, but receiving trailers + // means that we're _not_ getting a message. For users to realize that + // the stream has ended, Receive must return an error. + serverErr.meta = cc.responseHeader.Clone() + mergeHeaders(serverErr.meta, cc.responseTrailer) + _ = cc.duplexCall.CloseWrite() + return serverErr + } + // This was probably an error converting the bytes to a message or an error + // reading from the network. We're going to return it to the + // user, but we also want to close writes so Send errors out. + _ = cc.duplexCall.CloseWrite() + return err +} + +func (cc *grpcClientConn) ResponseHeader() http.Header { + _ = cc.duplexCall.BlockUntilResponseReady() + return cc.responseHeader +} + +func (cc *grpcClientConn) ResponseTrailer() http.Header { + _ = cc.duplexCall.BlockUntilResponseReady() + return cc.responseTrailer +} + +func (cc *grpcClientConn) CloseResponse() error { + return cc.duplexCall.CloseRead() +} + +func (cc *grpcClientConn) onRequestSend(fn func(*http.Request)) { + cc.duplexCall.onRequestSend = fn +} + +func (cc *grpcClientConn) validateResponse(response *http.Response) *Error { + if err := grpcValidateResponse( + response, + cc.responseHeader, + cc.compressionPools, + cc.unmarshaler.web, + cc.marshaler.codec.Name(), + ); err != nil { + return err + } + compression := getHeaderCanonical(response.Header, grpcHeaderCompression) + cc.unmarshaler.compressionPool = cc.compressionPools.Get(compression) + return nil +} + +type grpcHandlerConn struct { + spec Spec + peer Peer + web bool + bufferPool *bufferPool + protobuf Codec // for errors + marshaler grpcMarshaler + responseWriter http.ResponseWriter + responseHeader http.Header + responseTrailer http.Header + wroteToBody bool + request *http.Request + unmarshaler grpcUnmarshaler +} + +func (hc *grpcHandlerConn) Spec() Spec { + return hc.spec +} + +func (hc *grpcHandlerConn) Peer() Peer { + return hc.peer +} + +func (hc *grpcHandlerConn) Receive(msg any) error { + if err := hc.unmarshaler.Unmarshal(msg); err != nil { + return err // already coded + } + return nil // must be a literal nil: nil *Error is a non-nil error +} + +func (hc *grpcHandlerConn) RequestHeader() http.Header { + return hc.request.Header +} + +func (hc *grpcHandlerConn) Send(msg any) error { + defer flushResponseWriter(hc.responseWriter) + if !hc.wroteToBody { + mergeHeaders(hc.responseWriter.Header(), hc.responseHeader) + hc.wroteToBody = true + } + if err := hc.marshaler.Marshal(msg); err != nil { + return err + } + return nil // must be a literal nil: nil *Error is a non-nil error +} + +func (hc *grpcHandlerConn) ResponseHeader() http.Header { + return hc.responseHeader +} + +func (hc *grpcHandlerConn) ResponseTrailer() http.Header { + return hc.responseTrailer +} + +func (hc *grpcHandlerConn) Close(err error) (retErr error) { + defer func() { + // We don't want to copy unread portions of the body to /dev/null here: if + // the client hasn't closed the request body, we'll block until the server + // timeout kicks in. This could happen because the client is malicious, but + // a well-intentioned client may just not expect the server to be returning + // an error for a streaming RPC. Better to accept that we can't always reuse + // TCP connections. + closeErr := hc.request.Body.Close() + if retErr == nil { + retErr = closeErr + } + }() + defer flushResponseWriter(hc.responseWriter) + // If we haven't written the headers yet, do so. + if !hc.wroteToBody { + mergeHeaders(hc.responseWriter.Header(), hc.responseHeader) + } + // gRPC always sends the error's code, message, details, and metadata as + // trailing metadata. The Connect protocol doesn't do this, so we don't want + // to mutate the trailers map that the user sees. + mergedTrailers := make( + http.Header, + len(hc.responseTrailer)+2, // always make space for status & message + ) + mergeHeaders(mergedTrailers, hc.responseTrailer) + grpcErrorToTrailer(mergedTrailers, hc.protobuf, err) + if hc.web && !hc.wroteToBody && len(hc.responseHeader) == 0 { + // We're using gRPC-Web, we haven't yet written to the body, and there are no + // custom headers. That means we can send a "trailers-only" response and send + // trailing metadata as HTTP headers (instead of as trailers). + mergeHeaders(hc.responseWriter.Header(), mergedTrailers) + return nil + } + if hc.web { + // We're using gRPC-Web and we've already sent the headers, so we write + // trailing metadata to the HTTP body. + if err := hc.marshaler.MarshalWebTrailers(mergedTrailers); err != nil { + return err + } + return nil // must be a literal nil: nil *Error is a non-nil error + } + // We're using standard gRPC. Even if we haven't written to the body and + // we're sending a "trailers-only" response, we must send trailing metadata + // as HTTP trailers. (If we had frame-level control of the HTTP/2 layer, we + // could send trailers-only responses as a single HEADER frame and no DATA + // frames, but net/http doesn't expose APIs that low-level.) + // + // In net/http's ResponseWriter API, we send HTTP trailers by writing to the + // headers map with a special prefix. This prefixing is an implementation + // detail, so we should hide it and _not_ mutate the user-visible headers. + // + // Note that this is _very_ finicky and difficult to test with net/http, + // since correctness depends on low-level framing details. Breaking this + // logic breaks Envoy's gRPC-Web translation. + for key, values := range mergedTrailers { + for _, value := range values { + // These are potentially user-supplied, so we can't assume they're in + // canonical form. + hc.responseWriter.Header().Add(http.TrailerPrefix+key, value) + } + } + return nil +} + +type grpcMarshaler struct { + envelopeWriter +} + +func (m *grpcMarshaler) MarshalWebTrailers(trailer http.Header) *Error { + raw := m.bufferPool.Get() + defer m.bufferPool.Put(raw) + for key, values := range trailer { + // Per the Go specification, keys inserted during iteration may be produced + // later in the iteration or may be skipped. For safety, avoid mutating the + // map if the key is already lower-cased. + lower := strings.ToLower(key) + if key == lower { + continue + } + delete(trailer, key) + trailer[lower] = values + } + if err := trailer.Write(raw); err != nil { + return errorf(CodeInternal, "format trailers: %w", err) + } + return m.Write(&envelope{ + Data: raw, + Flags: grpcFlagEnvelopeTrailer, + }) +} + +type grpcUnmarshaler struct { + envelopeReader + + web bool + webTrailer http.Header +} + +func (u *grpcUnmarshaler) Unmarshal(message any) *Error { + err := u.envelopeReader.Unmarshal(message) + if err == nil { + return nil + } + if !errors.Is(err, errSpecialEnvelope) { + return err + } + env := u.last + data := env.Data + u.last.Data = nil // don't keep a reference to it + defer u.bufferPool.Put(data) + if !u.web || !env.IsSet(grpcFlagEnvelopeTrailer) { + return errorf(CodeInternal, "protocol error: invalid envelope flags %d", env.Flags) + } + + // Per the gRPC-Web specification, trailers should be encoded as an HTTP/1 + // headers block _without_ the terminating newline. To make the headers + // parseable by net/textproto, we need to add the newline. + if err := data.WriteByte('\n'); err != nil { + return errorf(CodeInternal, "unmarshal web trailers: %w", err) + } + bufferedReader := bufio.NewReader(data) + mimeReader := textproto.NewReader(bufferedReader) + mimeHeader, mimeErr := mimeReader.ReadMIMEHeader() + if mimeErr != nil { + return errorf( + CodeInternal, + "gRPC-Web protocol error: trailers invalid: %w", + mimeErr, + ) + } + u.webTrailer = http.Header(mimeHeader) + return errSpecialEnvelope +} + +func (u *grpcUnmarshaler) WebTrailer() http.Header { + return u.webTrailer +} + +func grpcValidateResponse( + response *http.Response, + header http.Header, + availableCompressors readOnlyCompressionPools, + web bool, + codecName string, +) *Error { + if response.StatusCode != http.StatusOK { + return errorf(httpToCode(response.StatusCode), "HTTP status %v", response.Status) + } + if err := grpcValidateResponseContentType( + web, + codecName, + getHeaderCanonical(response.Header, headerContentType), + ); err != nil { + return err + } + if compression := getHeaderCanonical(response.Header, grpcHeaderCompression); compression != "" && + compression != compressionIdentity && + !availableCompressors.Contains(compression) { + // Per https://github.com/grpc/grpc/blob/master/doc/compression.md, we + // should return CodeInternal and specify acceptable compression(s) (in + // addition to setting the Grpc-Accept-Encoding header). + return errorf( + CodeInternal, + "unknown encoding %q: accepted encodings are %v", + compression, + availableCompressors.CommaSeparatedNames(), + ) + } + // The response is valid, so we should expose the headers. + mergeHeaders(header, response.Header) + return nil +} + +// The gRPC wire protocol specifies that errors should be serialized using the +// binary Protobuf format, even if the messages in the request/response stream +// use a different codec. Consequently, this function needs a Protobuf codec to +// unmarshal error information in the headers. +// +// A nil error is only returned when a grpc-status key IS present, but it +// indicates a code of zero (no error). If no grpc-status key is present, this +// returns a non-nil *Error that wraps errTrailersWithoutGRPCStatus. +func grpcErrorForTrailer(protobuf Codec, trailer http.Header) *Error { + codeHeader := getHeaderCanonical(trailer, grpcHeaderStatus) + if codeHeader == "" { + // If there are no trailers at all, that's an internal error. + // But if it's an error determining the status code from the + // trailers, it's unknown. + code := CodeUnknown + if len(trailer) == 0 { + code = CodeInternal + } + return NewError(code, errTrailersWithoutGRPCStatus) + } + if codeHeader == "0" { + return nil + } + + code, err := strconv.ParseUint(codeHeader, 10 /* base */, 32 /* bitsize */) + if err != nil { + return errorf(CodeUnknown, "protocol error: invalid error code %q", codeHeader) + } + message, err := grpcPercentDecode(getHeaderCanonical(trailer, grpcHeaderMessage)) + if err != nil { + return errorf(CodeInternal, "protocol error: invalid error message %q", message) + } + retErr := NewWireError(Code(code), errors.New(message)) + + detailsBinaryEncoded := getHeaderCanonical(trailer, grpcHeaderDetails) + if len(detailsBinaryEncoded) > 0 { + detailsBinary, err := DecodeBinaryHeader(detailsBinaryEncoded) + if err != nil { + return errorf(CodeInternal, "server returned invalid grpc-status-details-bin trailer: %w", err) + } + var status statusv1.Status + if err := protobuf.Unmarshal(detailsBinary, &status); err != nil { + return errorf(CodeInternal, "server returned invalid protobuf for error details: %w", err) + } + for _, d := range status.GetDetails() { + retErr.details = append(retErr.details, &ErrorDetail{pbAny: d}) + } + // Prefer the Protobuf-encoded data to the headers (grpc-go does this too). + retErr.code = Code(status.GetCode()) //nolint:gosec // No information loss + retErr.err = errors.New(status.GetMessage()) + } + + return retErr +} + +func grpcParseTimeout(timeout string) (time.Duration, error) { + if timeout == "" { + return 0, errNoTimeout + } + unit, err := grpcTimeoutUnitLookup(timeout[len(timeout)-1]) + if err != nil { + return 0, err + } + num, err := strconv.ParseInt(timeout[:len(timeout)-1], 10 /* base */, 64 /* bitsize */) + if err != nil || num < 0 { + return 0, fmt.Errorf("protocol error: invalid timeout %q", timeout) + } + if num > 99999999 { // timeout must be ASCII string of at most 8 digits + return 0, fmt.Errorf("protocol error: timeout %q is too long", timeout) + } + const grpcTimeoutMaxHours = math.MaxInt64 / int64(time.Hour) // how many hours fit into a time.Duration? + if unit == time.Hour && num > grpcTimeoutMaxHours { + // Timeout is effectively unbounded, so ignore it. The grpc-go + // implementation does the same thing. + return 0, errNoTimeout + } + return time.Duration(num) * unit, nil +} + +func grpcEncodeTimeout(timeout time.Duration) string { + if timeout <= 0 { + return "0n" + } + // The gRPC protocol limits timeouts to 8 characters (not counting the unit), + // so timeouts must be strictly less than 1e8 of the appropriate unit. + const grpcTimeoutMaxValue = 1e8 + var ( + size time.Duration + unit byte + ) + switch { + case timeout < time.Nanosecond*grpcTimeoutMaxValue: + size, unit = time.Nanosecond, 'n' + case timeout < time.Microsecond*grpcTimeoutMaxValue: + size, unit = time.Microsecond, 'u' + case timeout < time.Millisecond*grpcTimeoutMaxValue: + size, unit = time.Millisecond, 'm' + case timeout < time.Second*grpcTimeoutMaxValue: + size, unit = time.Second, 'S' + case timeout < time.Minute*grpcTimeoutMaxValue: + size, unit = time.Minute, 'M' + default: + // time.Duration is an int64 number of nanoseconds, so the largest + // expressible duration is less than 1e8 hours. + size, unit = time.Hour, 'H' + } + buf := make([]byte, 0, 9) + buf = strconv.AppendInt(buf, int64(timeout/size), 10 /* base */) + buf = append(buf, unit) + return string(buf) +} + +func grpcTimeoutUnitLookup(unit byte) (time.Duration, error) { + switch unit { + case 'n': + return time.Nanosecond, nil + case 'u': + return time.Microsecond, nil + case 'm': + return time.Millisecond, nil + case 'S': + return time.Second, nil + case 'M': + return time.Minute, nil + case 'H': + return time.Hour, nil + default: + return 0, fmt.Errorf("protocol error: timeout has invalid unit %q", unit) + } +} + +func grpcCodecForContentType(web bool, contentType string) string { + if (!web && contentType == grpcContentTypeDefault) || (web && contentType == grpcWebContentTypeDefault) { + // implicitly protobuf + return codecNameProto + } + prefix := grpcContentTypePrefix + if web { + prefix = grpcWebContentTypePrefix + } + return strings.TrimPrefix(contentType, prefix) +} + +func grpcContentTypeForCodecName(web bool, name string) string { + if web { + return grpcWebContentTypePrefix + name + } + if name == codecNameProto { + // For compatibility with Google Cloud Platform's frontends, prefer an + // implicit default codec. See + // https://github.com/connectrpc/connect-go/pull/655#issuecomment-1915754523 + // for details. + return grpcContentTypeDefault + } + return grpcContentTypePrefix + name +} + +func grpcErrorToTrailer(trailer http.Header, protobuf Codec, err error) { + if err == nil { + setHeaderCanonical(trailer, grpcHeaderStatus, "0") // zero is the gRPC OK status + return + } + if connectErr, ok := asError(err); ok && !connectErr.wireErr { + mergeNonProtocolHeaders(trailer, connectErr.meta) + } + var ( + status = grpcStatusForError(err) + code = status.GetCode() + message = status.GetMessage() + bin []byte + ) + if len(status.Details) > 0 { + var binErr error + bin, binErr = protobuf.Marshal(status) + if binErr != nil { + code = int32(CodeInternal) + message = fmt.Sprintf("marshal protobuf status: %v", binErr) + } + } + setHeaderCanonical(trailer, grpcHeaderStatus, strconv.Itoa(int(code))) + setHeaderCanonical(trailer, grpcHeaderMessage, grpcPercentEncode(message)) + if len(bin) > 0 { + setHeaderCanonical(trailer, grpcHeaderDetails, EncodeBinaryHeader(bin)) + } +} + +func grpcStatusForError(err error) *statusv1.Status { + status := &statusv1.Status{ + Code: int32(CodeUnknown), + Message: err.Error(), + } + if connectErr, ok := asError(err); ok { + status.Code = int32(connectErr.Code()) //nolint:gosec // No information loss + status.Message = connectErr.Message() + status.Details = connectErr.detailsAsAny() + } + return status +} + +// grpcPercentEncode follows RFC 3986 Section 2.1 and the gRPC HTTP/2 spec. +// It's a variant of URL-encoding with fewer reserved characters. It's intended +// to take UTF-8 encoded text and escape non-ASCII bytes so that they're valid +// HTTP/1 headers, while still maximizing readability of the data on the wire. +// +// The grpc-message trailer (used for human-readable error messages) should be +// percent-encoded. +// +// References: +// +// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#responses +// https://datatracker.ietf.org/doc/html/rfc3986#section-2.1 +func grpcPercentEncode(msg string) string { + var hexCount int + for i := range len(msg) { + if grpcShouldEscape(msg[i]) { + hexCount++ + } + } + if hexCount == 0 { + return msg + } + // We need to escape some characters, so we'll need to allocate a new string. + var out strings.Builder + out.Grow(len(msg) + 2*hexCount) + for i := range len(msg) { + switch char := msg[i]; { + case grpcShouldEscape(char): + out.WriteByte('%') + out.WriteByte(upperhex[char>>4]) + out.WriteByte(upperhex[char&15]) + default: + out.WriteByte(char) + } + } + return out.String() +} + +func grpcPercentDecode(input string) (string, error) { + percentCount := 0 + for i := 0; i < len(input); { + switch input[i] { + case '%': + percentCount++ + if err := validateHex(input[i:]); err != nil { + return "", err + } + i += 3 + default: + i++ + } + } + if percentCount == 0 { + return input, nil + } + // We need to unescape some characters, so we'll need to allocate a new string. + var out strings.Builder + out.Grow(len(input) - 2*percentCount) + for i := 0; i < len(input); i++ { + switch input[i] { + case '%': + out.WriteByte(unhex(input[i+1])<<4 | unhex(input[i+2])) + i += 2 + default: + out.WriteByte(input[i]) + } + } + return out.String(), nil +} + +// Characters that need to be escaped are defined in gRPC's HTTP/2 spec. +// They're different from the generic set defined in RFC 3986. +func grpcShouldEscape(char byte) bool { + return char < ' ' || char > '~' || char == '%' +} + +func unhex(char byte) byte { + switch { + case '0' <= char && char <= '9': + return char - '0' + case 'a' <= char && char <= 'f': + return char - 'a' + 10 + case 'A' <= char && char <= 'F': + return char - 'A' + 10 + } + return 0 +} + +func isHex(char byte) bool { + return ('0' <= char && char <= '9') || ('a' <= char && char <= 'f') || ('A' <= char && char <= 'F') +} + +func validateHex(input string) error { + if len(input) < 3 || input[0] != '%' || !isHex(input[1]) || !isHex(input[2]) { + if len(input) > 3 { + input = input[:3] + } + return fmt.Errorf("invalid percent-encoded string %q", input) + } + return nil +} + +func grpcValidateResponseContentType(web bool, requestCodecName string, responseContentType string) *Error { + // Responses must have valid content-type that indicates same codec as the request. + bare, prefix := grpcContentTypeDefault, grpcContentTypePrefix + if web { + bare, prefix = grpcWebContentTypeDefault, grpcWebContentTypePrefix + } + if responseContentType == prefix+requestCodecName || + (requestCodecName == codecNameProto && responseContentType == bare) { + return nil + } + expectedContentType := bare + if requestCodecName != codecNameProto { + expectedContentType = prefix + requestCodecName + } + code := CodeInternal + if responseContentType != bare && !strings.HasPrefix(responseContentType, prefix) { + // Doesn't even look like a gRPC response? Use code "unknown". + code = CodeUnknown + } + return errorf( + code, + "invalid content-type: %q; expecting %q", + responseContentType, + expectedContentType, + ) +} diff --git a/vendor/connectrpc.com/connect/recover.go b/vendor/connectrpc.com/connect/recover.go new file mode 100644 index 0000000000..d59705d4a1 --- /dev/null +++ b/vendor/connectrpc.com/connect/recover.go @@ -0,0 +1,64 @@ +// Copyright 2021-2026 The Connect Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package connect + +import ( + "context" + "net/http" +) + +// recoverHandlerInterceptor lets handlers trap panics, perform side effects +// (like emitting logs or metrics), and present a friendlier error message to +// clients. +type recoverHandlerInterceptor struct { + Interceptor + + handle func(context.Context, Spec, http.Header, any) error +} + +func (i *recoverHandlerInterceptor) WrapUnary(next UnaryFunc) UnaryFunc { + return func(ctx context.Context, req AnyRequest) (_ AnyResponse, retErr error) { + if req.Spec().IsClient { + return next(ctx, req) + } + defer func() { + if r := recover(); r != nil { + // net/http checks for ErrAbortHandler with ==, so we should too. + if r == http.ErrAbortHandler { //nolint:errorlint,err113 + panic(r) //nolint:forbidigo + } + retErr = i.handle(ctx, req.Spec(), req.Header(), r) + } + }() + res, err := next(ctx, req) + return res, err + } +} + +func (i *recoverHandlerInterceptor) WrapStreamingHandler(next StreamingHandlerFunc) StreamingHandlerFunc { + return func(ctx context.Context, conn StreamingHandlerConn) (retErr error) { + defer func() { + if r := recover(); r != nil { + // net/http checks for ErrAbortHandler with ==, so we should too. + if r == http.ErrAbortHandler { //nolint:errorlint,err113 + panic(r) //nolint:forbidigo + } + retErr = i.handle(ctx, conn.Spec(), conn.RequestHeader(), r) + } + }() + err := next(ctx, conn) + return err + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/aws/config.go b/vendor/github.com/aws/aws-sdk-go-v2/aws/config.go index 3219517dab..0183a1222a 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/aws/config.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/aws/config.go @@ -164,6 +164,14 @@ type Config struct { // the shared config profile attribute request_min_compression_size_bytes RequestMinCompressSizeBytes int64 + // DisableClockSkewCorrection turns off SDK clock skew correction. When set + // the SDK will not adjust request signing timestamps to compensate for + // drift between the client and service clocks. Set to false (enabled) by + // default. This variable is sourced from the environment variable + // AWS_DISABLE_CLOCK_SKEW_CORRECTION or the shared config profile attribute + // disable_clock_skew_correction. + DisableClockSkewCorrection bool + // Controls how a resolved AWS account ID is handled for endpoint routing. AccountIDEndpointMode AccountIDEndpointMode @@ -204,6 +212,10 @@ type Config struct { // when constructing clients for specific services. Each callback function receives the service ID // and the service's Options struct, allowing for dynamic configuration based on the service. ServiceOptions []func(string, any) + + // Controls whether the SDK restricts file permissions on credential + // cache files it creates. + RestrictFilePermissions RestrictFilePermissions } // NewConfig returns a new Config pointer that can be chained with builder diff --git a/vendor/github.com/aws/aws-sdk-go-v2/aws/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/aws/go_module_metadata.go index e589f61561..7222e27d6d 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/aws/go_module_metadata.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/aws/go_module_metadata.go @@ -3,4 +3,4 @@ package aws // goModuleVersion is the tagged release for this module -const goModuleVersion = "1.41.7" +const goModuleVersion = "1.43.2" diff --git a/vendor/github.com/aws/aws-sdk-go-v2/aws/middleware/middleware.go b/vendor/github.com/aws/aws-sdk-go-v2/aws/middleware/middleware.go index 6d5f0079c2..3c4f2caecb 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/aws/middleware/middleware.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/aws/middleware/middleware.go @@ -43,7 +43,12 @@ func (r ClientRequestID) HandleBuild(ctx context.Context, in middleware.BuildInp } // RecordResponseTiming records the response timing for the SDK client requests. -type RecordResponseTiming struct{} +type RecordResponseTiming struct { + // DisableClockSkewCorrection suppresses recording of clock skew observed + // from the response, per the Clock Skew Correction SEP. Response timing is + // still recorded. + DisableClockSkewCorrection bool +} // ID is the middleware identifier func (a *RecordResponseTiming) ID() string { @@ -54,14 +59,17 @@ func (a *RecordResponseTiming) ID() string { func (a RecordResponseTiming) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( out middleware.DeserializeOutput, metadata middleware.Metadata, err error, ) { + requestAt := sdk.NowTime() out, metadata, err = next.HandleDeserialize(ctx, in) responseAt := sdk.NowTime() setResponseAt(&metadata, responseAt) var serverTime time.Time + var hasAgeHeader bool switch resp := out.RawResponse.(type) { case *smithyhttp.Response: + hasAgeHeader = len(resp.Header.Get("Age")) > 0 respDateHeader := resp.Header.Get("Date") if len(respDateHeader) == 0 { break @@ -77,14 +85,45 @@ func (a RecordResponseTiming) HandleDeserialize(ctx context.Context, in middlewa setServerTime(&metadata, serverTime) } - if !serverTime.IsZero() { - attemptSkew := serverTime.Sub(responseAt) - setAttemptSkew(&metadata, attemptSkew) + if !a.DisableClockSkewCorrection { + if skew, ok := computeClockSkew(serverTime, requestAt, responseAt, hasAgeHeader); ok { + setAttemptSkew(&metadata, skew) + } } return out, metadata, err } +// maxTrustedRequestDuration bounds how long a request may take before the SDK +// discards the skew measurement derived from its response. A slower round trip +// could only produce a signing failure if it pushed the timestamp outside the +// SigV4 validity window. See the Clock Skew Correction SEP. +const maxTrustedRequestDuration = 15 * time.Minute + +// computeClockSkew derives a clock skew candidate from a response per the Clock +// Skew Correction SEP. It returns ok=false (no candidate) when the Date header +// was absent/unparseable (serverTime zero), the round trip exceeded the maximum +// trusted request duration, or the response was served from a cache (Age +// header present). Otherwise the skew is the difference between the server's +// Date and the midpoint of the request round trip. +func computeClockSkew(serverTime, requestAt, responseAt time.Time, hasAgeHeader bool) (time.Duration, bool) { + if serverTime.IsZero() { + return 0, false + } + + if hasAgeHeader { + return 0, false + } + + elapsed := responseAt.Sub(requestAt) + if elapsed > maxTrustedRequestDuration { + return 0, false + } + + midpoint := requestAt.Add(elapsed / 2) + return serverTime.Sub(midpoint), true +} + type responseAtKey struct{} // GetResponseAt returns the time response was received at. diff --git a/vendor/github.com/aws/aws-sdk-go-v2/aws/restrict_file_permissions.go b/vendor/github.com/aws/aws-sdk-go-v2/aws/restrict_file_permissions.go new file mode 100644 index 0000000000..6360b657b8 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/aws/restrict_file_permissions.go @@ -0,0 +1,21 @@ +package aws + +// RestrictFilePermissions controls whether the SDK restricts file permissions +// on credential cache files it creates. +type RestrictFilePermissions string + +const ( + // RestrictFilePermissionsUnset indicates the setting has not been + // configured. + RestrictFilePermissionsUnset RestrictFilePermissions = "" + + // RestrictFilePermissionsUserReadWrite sets file permissions to owner + // read/write only (0600) and directory permissions to owner only (0700) + // when creating new cache files and directories on Unix. This is the + // default behavior. + RestrictFilePermissionsUserReadWrite RestrictFilePermissions = "user_read_write" + + // RestrictFilePermissionsUnrestricted does not set any file or directory + // permissions, relying on the system's default umask. + RestrictFilePermissionsUnrestricted RestrictFilePermissions = "unrestricted" +) diff --git a/vendor/github.com/aws/aws-sdk-go-v2/aws/retry/jitter_backoff.go b/vendor/github.com/aws/aws-sdk-go-v2/aws/retry/jitter_backoff.go index c266996dea..14225a53a4 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/aws/retry/jitter_backoff.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/aws/retry/jitter_backoff.go @@ -4,6 +4,7 @@ import ( "math" "time" + "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/internal/rand" "github.com/aws/aws-sdk-go-v2/internal/timeconv" ) @@ -12,9 +13,20 @@ import ( // number of attempts. type ExponentialJitterBackoff struct { maxBackoff time.Duration - // precomputed number of attempts needed to reach max backoff. + // precomputed number of attempts needed to reach max backoff (legacy mode). maxBackoffAttempts float64 + // Base delay for non-throttle errors (x in the formula t_i = b * min(x * r^i, MAX_BACKOFF)). + baseDelay time.Duration + + // Throttle error checker. When set and the error is a throttle, the base + // delay is 1s regardless of the configured baseDelay. + throttle IsErrorThrottle + + // When true, applies MAX_BACKOFF before jitter and uses throttle-aware + // base delay. + retries2026 bool + randFloat64 func() (float64, error) } @@ -25,13 +37,53 @@ func NewExponentialJitterBackoff(maxBackoff time.Duration) *ExponentialJitterBac maxBackoff: maxBackoff, maxBackoffAttempts: math.Log2( float64(maxBackoff) / float64(time.Second)), + baseDelay: time.Second, randFloat64: rand.CryptoRandFloat64, } } +// exponentialJitterBackoffOption is a functional option for ExponentialJitterBackoff. +type exponentialJitterBackoffOption func(*ExponentialJitterBackoff) + +// withBaseDelay sets the base delay for non-throttle errors. +func withBaseDelay(d time.Duration) exponentialJitterBackoffOption { + return func(j *ExponentialJitterBackoff) { + j.baseDelay = d + } +} + +// withThrottleCheck sets the throttle error checker used to determine if the +// backoff should use the throttle base delay (1s) instead of the configured +// base delay. +func withThrottleCheck(t IsErrorThrottle) exponentialJitterBackoffOption { + return func(j *ExponentialJitterBackoff) { + j.throttle = t + } +} + +// newExponentialJitterBackoffWithOptions returns an ExponentialJitterBackoff +// with the given options applied. +func newExponentialJitterBackoffWithOptions(maxBackoff time.Duration, optFns ...exponentialJitterBackoffOption) *ExponentialJitterBackoff { + j := NewExponentialJitterBackoff(maxBackoff) + j.retries2026 = true + for _, fn := range optFns { + fn(j) + } + return j +} + // BackoffDelay returns the duration to wait before the next attempt should be // made. Returns an error if unable get a duration. func (j *ExponentialJitterBackoff) BackoffDelay(attempt int, err error) (time.Duration, error) { + if j.retries2026 { + return j.backoffDelay2026(attempt, err) + } + return j.backoffDelayLegacy(attempt, err) +} + +// backoffDelayLegacy preserves the original backoff formula: b * 2^i, capped +// at maxBackoff. +func (j *ExponentialJitterBackoff) backoffDelayLegacy(attempt int, err error) (time.Duration, error) { if attempt > int(j.maxBackoffAttempts) { return j.maxBackoff, nil } @@ -47,3 +99,26 @@ func (j *ExponentialJitterBackoff) BackoffDelay(attempt int, err error) (time.Du return timeconv.FloatSecondsDur(delaySeconds), nil } + +// backoffDelay2026 uses throttle-aware base delay and applies MAX_BACKOFF +// before jitter: t_i = b * min(x * 2^i, MAX_BACKOFF). +func (j *ExponentialJitterBackoff) backoffDelay2026(attempt int, err error) (time.Duration, error) { + x := j.baseDelay + if j.throttle != nil && j.throttle.IsErrorThrottle(err) == aws.TrueTernary { + x = time.Second + } + + b, randErr := j.randFloat64() + if randErr != nil { + return 0, randErr + } + + ri := math.Pow(2, float64(attempt)) + delaySeconds := float64(x) / float64(time.Second) * ri + maxBackoffSeconds := float64(j.maxBackoff) / float64(time.Second) + if delaySeconds > maxBackoffSeconds { + delaySeconds = maxBackoffSeconds + } + + return timeconv.FloatSecondsDur(b * delaySeconds), nil +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/aws/retry/middleware.go b/vendor/github.com/aws/aws-sdk-go-v2/aws/retry/middleware.go index 52acb62f91..126dcf47bd 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/aws/retry/middleware.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/aws/retry/middleware.go @@ -48,6 +48,12 @@ type Attempt struct { // call. ClientSkew *atomic.Int64 + // DisableClockSkewCorrection disables clock skew correction per the Clock + // Skew Correction SEP: observed skew is not applied to the signing + // timestamp, not recorded into ClientSkew, and clock skew error codes are + // not treated as retry candidates. + DisableClockSkewCorrection bool + retryer aws.RetryerV2 requestCloner RequestCloner } @@ -88,7 +94,7 @@ func (r *Attempt) HandleFinalize(ctx context.Context, in smithymiddle.FinalizeIn out smithymiddle.FinalizeOutput, metadata smithymiddle.Metadata, err error, ) { var attemptClockSkew time.Duration - if r.ClientSkew != nil { + if !r.DisableClockSkewCorrection && r.ClientSkew != nil { attemptClockSkew = time.Duration(r.ClientSkew.Load()) } @@ -159,7 +165,7 @@ func (r *Attempt) HandleFinalize(ctx context.Context, in smithymiddle.FinalizeIn // this guarantees we are staying on top of the persistent skew value // (either to apply it or to heal it back if the clocks realign) - if r.ClientSkew != nil { + if !r.DisableClockSkewCorrection && r.ClientSkew != nil { if resultSkew, ok := awsmiddle.GetAttemptSkew(metadata); ok { r.ClientSkew.Store(resultSkew.Nanoseconds()) } @@ -233,9 +239,11 @@ func (r *Attempt) handleAttempt( "failed to release retry token after request error, %w", err) } // Release the attempt token based on the state of the attempt's error (if any). - if releaseError := releaseAttemptToken(err); releaseError != nil && err != nil { - return out, attemptResult, nopRelease, fmt.Errorf( - "failed to release initial token after request error, %w", err) + if !newRetries2026() || attemptNum == 1 { + if releaseError := releaseAttemptToken(err); releaseError != nil && err != nil { + return out, attemptResult, nopRelease, fmt.Errorf( + "failed to release initial token after request error, %w", err) + } } // If there was no error making the attempt, nothing further to do. There // will be nothing to retry. @@ -243,7 +251,10 @@ func (r *Attempt) handleAttempt( return out, attemptResult, nopRelease, err } - err = wrapAsClockSkew(ctx, err) + if !r.DisableClockSkewCorrection { + candidateSkew, hasCandidateSkew := awsmiddle.GetAttemptSkew(metadata) + err = wrapAsClockSkew(err, candidateSkew, hasCandidateSkew, retryMetadata.AttemptClockSkew) + } //------------------------------ // Is Retryable and Should Retry @@ -276,6 +287,13 @@ func (r *Attempt) handleAttempt( // Get a retry token that will be released after the releaseRetryToken, retryTokenErr := r.retryer.GetRetryToken(ctx, err) if retryTokenErr != nil { + // Long-polling operations must still back off when quota is exceeded. + if newRetries2026() && internalcontext.GetIsLongPolling(ctx) { + if retryDelay, delayErr := r.retryer.RetryDelay(attemptNum-1, err); delayErr == nil { + retryDelay = adjustForRetryAfterHeader(retryDelay, err, logger, r.LogAttempts) + _ = sdk.SleepWithContext(ctx, retryDelay) + } + } return out, attemptResult, nopRelease, errors.Join(err, retryTokenErr) } @@ -285,10 +303,17 @@ func (r *Attempt) handleAttempt( // Get the retry delay before another attempt can be made, and sleep for // that time. Potentially early exist if the sleep is canceled via the // context. - retryDelay, reqErr := r.retryer.RetryDelay(attemptNum, err) + attempt := attemptNum + if newRetries2026() { + attempt = attemptNum - 1 + } + retryDelay, reqErr := r.retryer.RetryDelay(attempt, err) if reqErr != nil { return out, attemptResult, releaseRetryToken, reqErr } + if newRetries2026() { + retryDelay = adjustForRetryAfterHeader(retryDelay, err, logger, r.LogAttempts) + } if reqErr = sdk.SleepWithContext(ctx, retryDelay); reqErr != nil { err = &aws.RequestCanceledError{Err: reqErr} return out, attemptResult, releaseRetryToken, err @@ -300,37 +325,66 @@ func (r *Attempt) handleAttempt( return out, attemptResult, releaseRetryToken, err } -// errors that, if detected when we know there's a clock skew, -// can be retried and have a high chance of success -var possibleSkewCodes = map[string]struct{}{ +// clockSkewCodes are the error codes that may indicate a clock skew problem. +// Per the Clock Skew Correction SEP these are retryable only when the absolute +// skew observed from the response Date header exceeds the detection threshold. +// The SEP does not distinguish "definite" from "possible" skew errors: modern +// services overload a single code (e.g. InvalidSignatureException) for both +// skewed and genuinely malformed signatures, so every code is gated on the +// observed skew. +var clockSkewCodes = map[string]struct{}{ "InvalidSignatureException": {}, "SignatureDoesNotMatch": {}, "AuthFailure": {}, + "RequestTimeTooSkewed": {}, + "AccessDeniedException": {}, } -var definiteSkewCodes = map[string]struct{}{ - "RequestExpired": {}, - "RequestInTheFuture": {}, - "RequestTimeTooSkewed": {}, -} - -// wrapAsClockSkew checks if this error could be related to a clock skew -// error and if so, wrap the error. -func wrapAsClockSkew(ctx context.Context, err error) error { +// wrapAsClockSkew classifies err as a retryable clock skew error when its code +// is a known clock skew code and the signing time diverges from the server +// time by more than the detection threshold. +// +// The signing time is now() + attemptSkew. The server time is now() + +// candidateSkew (derived from the response Date header). The signing error is: +// +// |attemptSkew - candidateSkew| > skewThreshold +// +// This single check covers both fresh skew detection (attemptSkew is zero on +// first attempt, so the error equals |candidateSkew|) and stale offset healing +// (attemptSkew is large but the server and client clocks have realigned, so +// candidateSkew is near zero). +// +// If no candidate was observed (the Date header was absent, unparseable, or +// discarded as untrusted), the error is not treated as clock skew. +func wrapAsClockSkew(err error, candidateSkew time.Duration, hasCandidateSkew bool, attemptSkew time.Duration) error { var v interface{ ErrorCode() string } if !errors.As(err, &v) { return err } - if _, ok := definiteSkewCodes[v.ErrorCode()]; ok { - return &retryableClockSkewError{Err: err} + + if _, ok := clockSkewCodes[v.ErrorCode()]; !ok { + return err + } + + if !hasCandidateSkew { + return err } - _, isPossibleSkewCode := possibleSkewCodes[v.ErrorCode()] - if skew := internalcontext.GetAttemptSkewContext(ctx); skew > skewThreshold && isPossibleSkewCode { + + if absDuration(attemptSkew-candidateSkew) > skewThreshold { return &retryableClockSkewError{Err: err} } + return err } +func absDuration(d time.Duration) time.Duration { + if d < 0 { + return -d + } + + return d +} + // MetricsHeader attaches SDK request metric header for retries to the transport type MetricsHeader struct{} @@ -423,6 +477,43 @@ func AddRetryMiddlewares(stack *smithymiddle.Stack, options AddRetryMiddlewaresO return nil } +// adjustForRetryAfterHeader checks for the x-amz-retry-after response header +// and clamps the backoff duration accordingly. The header value is an integer +// representing milliseconds. The result is clamped to [t_i, 5s + t_i] where +// t_i is the jittered exponential backoff duration. Invalid header values are +// ignored. +func adjustForRetryAfterHeader(backoff time.Duration, err error, logger logging.Logger, logAttempts bool) time.Duration { + var re *http.ResponseError + if !errors.As(err, &re) || re.Response == nil || re.Response.Response == nil { + return backoff + } + + headerVal := re.Response.Header.Get("X-Amz-Retry-After") + if headerVal == "" { + return backoff + } + + ms, parseErr := strconv.ParseInt(headerVal, 10, 64) + if parseErr != nil || ms < 0 { + if logAttempts { + logger.Logf(logging.Debug, "ignoring invalid x-amz-retry-after header value %q", headerVal) + } + return backoff + } + + retryAfter := time.Duration(ms) * time.Millisecond + minDuration := backoff + maxDuration := 5*time.Second + backoff + + if retryAfter < minDuration { + return minDuration + } + if retryAfter > maxDuration { + return maxDuration + } + return retryAfter +} + // Determines the value of exception.type for metrics purposes. We prefer an // API-specific error code, otherwise it's just the Go type for the value. func errorType(err error) string { diff --git a/vendor/github.com/aws/aws-sdk-go-v2/aws/retry/retry.go b/vendor/github.com/aws/aws-sdk-go-v2/aws/retry/retry.go index af81635b3f..c240fb09b6 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/aws/retry/retry.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/aws/retry/retry.go @@ -72,6 +72,19 @@ func (r *withMaxBackoffDelay) RetryDelay(attempt int, err error) (time.Duration, return r.backoff.BackoffDelay(attempt, err) } +// AddWithLongPolling returns a retryer that is marked as long-polling. +// Long-polling operations will back off even when the retry quota is +// exhausted. +func AddWithLongPolling(r aws.Retryer) aws.Retryer { + return &withLongPolling{RetryerV2: wrapAsRetryerV2(r)} +} + +type withLongPolling struct { + aws.RetryerV2 +} + +func (w *withLongPolling) IsLongPolling() bool { return true } + type wrappedAsRetryerV2 struct { aws.Retryer } diff --git a/vendor/github.com/aws/aws-sdk-go-v2/aws/retry/standard.go b/vendor/github.com/aws/aws-sdk-go-v2/aws/retry/standard.go index d5ea93222e..f2f9660da0 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/aws/retry/standard.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/aws/retry/standard.go @@ -3,6 +3,7 @@ package retry import ( "context" "fmt" + "os" "time" "github.com/aws/aws-sdk-go-v2/aws/ratelimit" @@ -35,8 +36,16 @@ const ( const ( DefaultRetryRateTokens uint = 500 DefaultRetryCost uint = 5 - DefaultRetryTimeoutCost uint = 10 DefaultNoRetryIncrement uint = 1 + + // DefaultRetryTimeoutCost is the cost to deduct from the RateLimiter's + // token bucket per retry caused by timeout error. + // + // When AWS_NEW_RETRIES_2026 is set to "true", timeouts are no longer + // treated differently than other transient errors. The discounted cost + // is instead applied to throttling errors via DefaultThrottlingRetryCost. + DefaultRetryTimeoutCost uint = 10 + DefaultThrottlingRetryCost uint = 5 ) // DefaultRetryableHTTPStatusCodes is the default set of HTTP status codes the SDK @@ -121,6 +130,12 @@ type StandardOptions struct { // It is safe to append to this list in NewStandard's functional options. Timeouts []IsErrorTimeout + // Set of strategies to determine if the attempt failed due to a throttle + // error. Used to determine the retry token cost. + // + // It is safe to append to this list in NewStandard's functional options. + Throttles []IsErrorThrottle + // Provides the rate limiting strategy for rate limiting attempt retries // across all attempts the retryer is being used with. // @@ -129,10 +144,14 @@ type StandardOptions struct { // consume more tokens than what's available results in operation failure. // The default implementation is parameterized as follows: // - a capacity of 500 (DefaultRetryRateTokens) - // - a retry caused by a timeout costs 10 tokens (DefaultRetryCost) - // - a retry caused by other errors costs 5 tokens (DefaultRetryTimeoutCost) + // - a retry caused by a timeout costs 10 tokens (DefaultRetryTimeoutCost) + // - a retry caused by other errors costs 5 tokens (DefaultRetryCost) // - an operation that succeeds on the 1st attempt adds 1 token (DefaultNoRetryIncrement) // + // When AWS_NEW_RETRIES_2026 is set to "true", the costs change: + // - a retry costs 14 tokens + // - a retry caused by a throttling error costs 5 tokens (DefaultThrottlingRetryCost) + // // You can disable rate limiting by setting this field to ratelimit.None. RateLimiter RateLimiter @@ -141,11 +160,23 @@ type StandardOptions struct { // The cost to deduct from the RateLimiter's token bucket per retry caused // by timeout error. + // + // When AWS_NEW_RETRIES_2026 is set to "true", this field is unused. + // Throttling errors use ThrottlingRetryCost instead. RetryTimeoutCost uint + // The cost to deduct from the RateLimiter's token bucket per retry caused + // by a throttling error. Only used when AWS_NEW_RETRIES_2026 is "true". + ThrottlingRetryCost uint + // The cost to payback to the RateLimiter's token bucket for successful // attempts. NoRetryIncrement uint + + // BaseDelay is the base backoff delay for non-throttle retryable errors. + // Throttling errors always use 1s. Defaults to 50ms if zero. + // Only used when AWS_NEW_RETRIES_2026 is "true"; ignored in legacy mode. + BaseDelay time.Duration } // RateLimiter provides the interface for limiting the rate of attempt retries @@ -161,6 +192,7 @@ type RateLimiter interface { type Standard struct { options StandardOptions + throttle IsErrorThrottle timeout IsErrorTimeout retryable IsErrorRetryable backoff BackoffDelayer @@ -169,17 +201,7 @@ type Standard struct { // NewStandard initializes a standard retry behavior with defaults that can be // overridden via functional options. func NewStandard(fnOpts ...func(*StandardOptions)) *Standard { - o := StandardOptions{ - MaxAttempts: DefaultMaxAttempts, - MaxBackoff: DefaultMaxBackoff, - Retryables: append([]IsErrorRetryable{}, DefaultRetryables...), - Timeouts: append([]IsErrorTimeout{}, DefaultTimeouts...), - - RateLimiter: ratelimit.NewTokenRateLimit(DefaultRetryRateTokens), - RetryCost: DefaultRetryCost, - RetryTimeoutCost: DefaultRetryTimeoutCost, - NoRetryIncrement: DefaultNoRetryIncrement, - } + o := standardDefaults() for _, fn := range fnOpts { fn(&o) } @@ -189,13 +211,25 @@ func NewStandard(fnOpts ...func(*StandardOptions)) *Standard { backoff := o.Backoff if backoff == nil { - backoff = NewExponentialJitterBackoff(o.MaxBackoff) + if newRetries2026() { + baseDelay := o.BaseDelay + if baseDelay == 0 { + baseDelay = 50 * time.Millisecond + } + backoff = newExponentialJitterBackoffWithOptions(o.MaxBackoff, + withBaseDelay(baseDelay), + withThrottleCheck(IsErrorThrottles(o.Throttles)), + ) + } else { + backoff = NewExponentialJitterBackoff(o.MaxBackoff) + } } return &Standard{ options: o, backoff: backoff, retryable: IsErrorRetryables(o.Retryables), + throttle: IsErrorThrottles(o.Throttles), timeout: IsErrorTimeouts(o.Timeouts), } } @@ -244,8 +278,14 @@ func (s *Standard) noRetryIncrement() error { func (s *Standard) GetRetryToken(ctx context.Context, opErr error) (func(error) error, error) { cost := s.options.RetryCost - if s.timeout.IsErrorTimeout(opErr).Bool() { - cost = s.options.RetryTimeoutCost + if newRetries2026() { + if s.throttle.IsErrorThrottle(opErr).Bool() { + cost = s.options.ThrottlingRetryCost + } + } else { + if s.timeout.IsErrorTimeout(opErr).Bool() { + cost = s.options.RetryTimeoutCost + } } fn, err := s.options.RateLimiter.GetToken(ctx, cost) @@ -267,3 +307,37 @@ func (f releaseToken) release(err error) error { return f() } + +func newRetries2026() bool { + return os.Getenv("AWS_NEW_RETRIES_2026") == "true" +} + +func standardDefaults() StandardOptions { + if newRetries2026() { + return StandardOptions{ + MaxAttempts: DefaultMaxAttempts, + MaxBackoff: DefaultMaxBackoff, + Retryables: append([]IsErrorRetryable{}, DefaultRetryables...), + Timeouts: append([]IsErrorTimeout{}, DefaultTimeouts...), + Throttles: append([]IsErrorThrottle{}, DefaultThrottles...), + + RateLimiter: ratelimit.NewTokenRateLimit(DefaultRetryRateTokens), + RetryCost: 14, + RetryTimeoutCost: DefaultRetryTimeoutCost, + ThrottlingRetryCost: DefaultThrottlingRetryCost, + NoRetryIncrement: DefaultNoRetryIncrement, + } + } + return StandardOptions{ + MaxAttempts: DefaultMaxAttempts, + MaxBackoff: DefaultMaxBackoff, + Retryables: append([]IsErrorRetryable{}, DefaultRetryables...), + Timeouts: append([]IsErrorTimeout{}, DefaultTimeouts...), + Throttles: append([]IsErrorThrottle{}, DefaultThrottles...), + + RateLimiter: ratelimit.NewTokenRateLimit(DefaultRetryRateTokens), + RetryCost: DefaultRetryCost, + RetryTimeoutCost: DefaultRetryTimeoutCost, + NoRetryIncrement: DefaultNoRetryIncrement, + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/config/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/config/CHANGELOG.md index 6f932e910a..34989cc52c 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/config/CHANGELOG.md +++ b/vendor/github.com/aws/aws-sdk-go-v2/config/CHANGELOG.md @@ -1,3 +1,72 @@ +# v1.32.33 (2026-07-29) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.32.32 (2026-07-28) + +* **Dependency Update**: Update to smithy-go v1.27.5. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.32.31 (2026-07-21) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.32.30 (2026-07-13) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.32.29 (2026-07-08.2) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.32.28 (2026-07-06) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.32.27 (2026-07-01) + +* **Bug Fix**: Bump smithy-go to 1.27.3, fix JSON encorder for document.Number, endpoint host label format validation and CBOR union serialization on new serde +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.32.26 (2026-06-29) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.32.25 (2026-06-10) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.32.24 (2026-06-08) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.32.23 (2026-06-04) + +* **Dependency Update**: Update to smithy-go v1.27.1 to fix several union-related deserialization bugs in schema-serde-enabled services. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.32.22 (2026-06-03) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.32.21 (2026-06-02) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.32.20 (2026-05-29) + +* **Dependency Update**: Update to smithy-go v1.26.0. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.32.19 (2026-05-28) + +* **Bug Fix**: Adds support for AWS_RESTRICT_FILE_PERMISSIONS for env and in-code config. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.32.18 (2026-05-22) + +* **Dependency Update**: Updated to the latest SDK module versions + # v1.32.17 (2026-04-29) * **Dependency Update**: Update to smithy-go v1.25.1. diff --git a/vendor/github.com/aws/aws-sdk-go-v2/config/config.go b/vendor/github.com/aws/aws-sdk-go-v2/config/config.go index 498a668a30..f056bf416e 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/config/config.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/config/config.go @@ -77,6 +77,8 @@ var defaultAWSConfigResolvers = []awsConfigResolver{ // Sets the DisableRequestCompression if present in env var or shared config profile resolveDisableRequestCompression, + // Sets the DisableClockSkewCorrection if present in env var or shared config profile + resolveDisableClockSkewCorrection, // Sets the RequestMinCompressSizeBytes if present in env var or shared config profile resolveRequestMinCompressSizeBytes, @@ -96,6 +98,8 @@ var defaultAWSConfigResolvers = []awsConfigResolver{ // Sets the ServiceOptions if present in LoadOptions resolveServiceOptions, + + resolveRestrictFilePermissions, } // A Config represents a generic configuration value or set of values. This type diff --git a/vendor/github.com/aws/aws-sdk-go-v2/config/env_config.go b/vendor/github.com/aws/aws-sdk-go-v2/config/env_config.go index e932c63dfb..886aec4c22 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/config/env_config.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/config/env_config.go @@ -78,6 +78,8 @@ const ( awsDisableRequestCompressionEnv = "AWS_DISABLE_REQUEST_COMPRESSION" awsRequestMinCompressionSizeBytesEnv = "AWS_REQUEST_MIN_COMPRESSION_SIZE_BYTES" + awsDisableClockSkewCorrectionEnv = "AWS_DISABLE_CLOCK_SKEW_CORRECTION" + awsS3DisableExpressSessionAuthEnv = "AWS_S3_DISABLE_EXPRESS_SESSION_AUTH" awsAccountIDEnv = "AWS_ACCOUNT_ID" @@ -87,6 +89,8 @@ const ( awsResponseChecksumValidation = "AWS_RESPONSE_CHECKSUM_VALIDATION" awsAuthSchemePreferenceEnv = "AWS_AUTH_SCHEME_PREFERENCE" + + awsRestrictFilePermissionsEnv = "AWS_RESTRICT_FILE_PERMISSIONS" ) var ( @@ -291,6 +295,10 @@ type EnvConfig struct { // retrieved from env var AWS_REQUEST_MIN_COMPRESSION_SIZE_BYTES RequestMinCompressSizeBytes *int64 + // determine if clock skew correction is disabled, default to false + // retrieved from env var AWS_DISABLE_CLOCK_SKEW_CORRECTION + DisableClockSkewCorrection *bool + // Whether S3Express auth is disabled. // // This will NOT prevent requests from being made to S3Express buckets, it @@ -309,6 +317,10 @@ type EnvConfig struct { // Priority list of preferred auth scheme names (e.g. sigv4a). AuthSchemePreference []string + + // Controls whether the SDK restricts file permissions on credential + // cache files it creates. + RestrictFilePermissions aws.RestrictFilePermissions } // loadEnvConfig reads configuration values from the OS's environment variables. @@ -358,6 +370,9 @@ func NewEnvConfig() (EnvConfig, error) { if err := setInt64PtrFromEnvVal(&cfg.RequestMinCompressSizeBytes, []string{awsRequestMinCompressionSizeBytesEnv}, smithyrequestcompression.MaxRequestMinCompressSizeBytes); err != nil { return cfg, err } + if err := setBoolPtrFromEnvVal(&cfg.DisableClockSkewCorrection, []string{awsDisableClockSkewCorrectionEnv}); err != nil { + return cfg, err + } if err := setEndpointDiscoveryTypeFromEnvVal(&cfg.EnableEndpointDiscovery, []string{awsEnableEndpointDiscoveryEnv}); err != nil { return cfg, err @@ -422,6 +437,10 @@ func NewEnvConfig() (EnvConfig, error) { cfg.AuthSchemePreference = toAuthSchemePreferenceList(os.Getenv(awsAuthSchemePreferenceEnv)) + if err := setRestrictFilePermissionsFromEnvVal(&cfg.RestrictFilePermissions, []string{awsRestrictFilePermissionsEnv}); err != nil { + return cfg, err + } + return cfg, nil } @@ -443,6 +462,13 @@ func (c EnvConfig) getDisableRequestCompression(context.Context) (bool, bool, er return *c.DisableRequestCompression, true, nil } +func (c EnvConfig) getDisableClockSkewCorrection(context.Context) (bool, bool, error) { + if c.DisableClockSkewCorrection == nil { + return false, false, nil + } + return *c.DisableClockSkewCorrection, true, nil +} + func (c EnvConfig) getRequestMinCompressSizeBytes(context.Context) (int64, bool, error) { if c.RequestMinCompressSizeBytes == nil { return 0, false, nil @@ -930,3 +956,27 @@ func (c EnvConfig) getAuthSchemePreference() ([]string, bool) { } return nil, false } + +func (c EnvConfig) getRestrictFilePermissions(context.Context) (aws.RestrictFilePermissions, bool, error) { + return c.RestrictFilePermissions, len(c.RestrictFilePermissions) > 0, nil +} + +func setRestrictFilePermissionsFromEnvVal(m *aws.RestrictFilePermissions, keys []string) error { + for _, k := range keys { + value := os.Getenv(k) + if len(value) == 0 { + continue + } + + switch strings.ToLower(value) { + case "user_read_write": + *m = aws.RestrictFilePermissionsUserReadWrite + case "unrestricted": + *m = aws.RestrictFilePermissionsUnrestricted + default: + return fmt.Errorf("invalid value for environment variable, %s=%s, must be user_read_write/unrestricted", k, value) + } + break + } + return nil +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/config/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/config/go_module_metadata.go index fdbfa78e45..054e37f268 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/config/go_module_metadata.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/config/go_module_metadata.go @@ -3,4 +3,4 @@ package config // goModuleVersion is the tagged release for this module -const goModuleVersion = "1.32.17" +const goModuleVersion = "1.32.33" diff --git a/vendor/github.com/aws/aws-sdk-go-v2/config/load_options.go b/vendor/github.com/aws/aws-sdk-go-v2/config/load_options.go index 7cb5a13658..b7b3d8b205 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/config/load_options.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/config/load_options.go @@ -214,6 +214,9 @@ type LoadOptions struct { // The inclusive min bytes of a request body that could be compressed RequestMinCompressSizeBytes *int64 + // Specifies whether SDK clock skew correction is disabled + DisableClockSkewCorrection *bool + // Whether S3 Express auth is disabled. S3DisableExpressAuth *bool @@ -240,6 +243,10 @@ type LoadOptions struct { // when constructing clients for specific services. Each callback function receives the service ID // and the service's Options struct, allowing for dynamic configuration based on the service. ServiceOptions []func(string, any) + + // Controls whether the SDK restricts file permissions on credential + // cache files it creates. + RestrictFilePermissions aws.RestrictFilePermissions } func (o LoadOptions) getDefaultsMode(ctx context.Context) (aws.DefaultsMode, bool, error) { @@ -295,6 +302,14 @@ func (o LoadOptions) getDisableRequestCompression(ctx context.Context) (bool, bo return *o.DisableRequestCompression, true, nil } +// getDisableClockSkewCorrection returns DisableClockSkewCorrection from config's LoadOptions +func (o LoadOptions) getDisableClockSkewCorrection(ctx context.Context) (bool, bool, error) { + if o.DisableClockSkewCorrection == nil { + return false, false, nil + } + return *o.DisableClockSkewCorrection, true, nil +} + // getRequestMinCompressSizeBytes returns RequestMinCompressSizeBytes from config's LoadOptions func (o LoadOptions) getRequestMinCompressSizeBytes(ctx context.Context) (int64, bool, error) { if o.RequestMinCompressSizeBytes == nil { @@ -365,6 +380,18 @@ func WithDisableRequestCompression(DisableRequestCompression *bool) LoadOptionsF } } +// WithDisableClockSkewCorrection is a helper function to construct functional +// options that sets DisableClockSkewCorrection on config's LoadOptions. +func WithDisableClockSkewCorrection(DisableClockSkewCorrection *bool) LoadOptionsFunc { + return func(o *LoadOptions) error { + if DisableClockSkewCorrection == nil { + return nil + } + o.DisableClockSkewCorrection = DisableClockSkewCorrection + return nil + } +} + // WithRequestMinCompressSizeBytes is a helper function to construct functional options // that sets RequestMinCompressSizeBytes on config's LoadOptions. func WithRequestMinCompressSizeBytes(RequestMinCompressSizeBytes *int64) LoadOptionsFunc { @@ -1353,3 +1380,15 @@ func (o LoadOptions) getAuthSchemePreference() ([]string, bool) { } return nil, false } + +func (o LoadOptions) getRestrictFilePermissions(context.Context) (aws.RestrictFilePermissions, bool, error) { + return o.RestrictFilePermissions, len(o.RestrictFilePermissions) > 0, nil +} + +// WithRestrictFilePermissions sets the RestrictFilePermissions mode on config. +func WithRestrictFilePermissions(m aws.RestrictFilePermissions) LoadOptionsFunc { + return func(o *LoadOptions) error { + o.RestrictFilePermissions = m + return nil + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/config/provider.go b/vendor/github.com/aws/aws-sdk-go-v2/config/provider.go index 5531249710..ae298afb60 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/config/provider.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/config/provider.go @@ -208,6 +208,23 @@ func getDisableRequestCompression(ctx context.Context, configs configs) (value b return } +// disableClockSkewCorrectionProvider provides access to the DisableClockSkewCorrection +type disableClockSkewCorrectionProvider interface { + getDisableClockSkewCorrection(context.Context) (bool, bool, error) +} + +func getDisableClockSkewCorrection(ctx context.Context, configs configs) (value bool, found bool, err error) { + for _, cfg := range configs { + if p, ok := cfg.(disableClockSkewCorrectionProvider); ok { + value, found, err = p.getDisableClockSkewCorrection(ctx) + if err != nil || found { + break + } + } + } + return +} + // requestMinCompressSizeBytesProvider provides access to the MinCompressSizeBytes type requestMinCompressSizeBytesProvider interface { getRequestMinCompressSizeBytes(context.Context) (int64, bool, error) @@ -784,3 +801,19 @@ func getServiceOptions(ctx context.Context, configs configs) (v []func(string, a } return v, found, err } + +type restrictFilePermissionsProvider interface { + getRestrictFilePermissions(context.Context) (aws.RestrictFilePermissions, bool, error) +} + +func getRestrictFilePermissions(ctx context.Context, configs configs) (value aws.RestrictFilePermissions, found bool, err error) { + for _, cfg := range configs { + if p, ok := cfg.(restrictFilePermissionsProvider); ok { + value, found, err = p.getRestrictFilePermissions(ctx) + if err != nil || found { + break + } + } + } + return +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/config/resolve.go b/vendor/github.com/aws/aws-sdk-go-v2/config/resolve.go index a71c105d96..a2c56dc625 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/config/resolve.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/config/resolve.go @@ -151,6 +151,18 @@ func resolveDisableRequestCompression(ctx context.Context, cfg *aws.Config, conf return nil } +// resolveDisableClockSkewCorrection extracts the DisableClockSkewCorrection from +// the configs slice's SharedConfig or EnvConfig +func resolveDisableClockSkewCorrection(ctx context.Context, cfg *aws.Config, configs configs) error { + disable, _, err := getDisableClockSkewCorrection(ctx, configs) + if err != nil { + return err + } + + cfg.DisableClockSkewCorrection = disable + return nil +} + // resolveRequestMinCompressSizeBytes extracts the RequestMinCompressSizeBytes from the configs slice's // SharedConfig or EnvConfig func resolveRequestMinCompressSizeBytes(ctx context.Context, cfg *aws.Config, configs configs) error { @@ -442,3 +454,17 @@ func resolveServiceOptions(ctx context.Context, cfg *aws.Config, configs configs cfg.ServiceOptions = serviceOptions return nil } + +func resolveRestrictFilePermissions(ctx context.Context, cfg *aws.Config, configs configs) error { + m, found, err := getRestrictFilePermissions(ctx, configs) + if err != nil { + return err + } + + if !found { + m = aws.RestrictFilePermissionsUserReadWrite + } + + cfg.RestrictFilePermissions = m + return nil +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/config/resolve_credentials.go b/vendor/github.com/aws/aws-sdk-go-v2/config/resolve_credentials.go index 4f8c324e0d..fc9d47b8ae 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/config/resolve_credentials.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/config/resolve_credentials.go @@ -640,6 +640,7 @@ func resolveLoginCredentials(ctx context.Context, cfg *aws.Config, sharedCfg *Sh svc := signin.NewFromConfig(*cfg) provider := logincreds.New(svc, tokenPath, func(o *logincreds.Options) { o.CredentialSources = getCredentialSources(ctx) + o.RestrictPermissions = cfg.RestrictFilePermissions != aws.RestrictFilePermissionsUnrestricted }) cfg.Credentials, err = wrapWithCredentialsCache(ctx, configs, provider) if err != nil { diff --git a/vendor/github.com/aws/aws-sdk-go-v2/config/shared_config.go b/vendor/github.com/aws/aws-sdk-go-v2/config/shared_config.go index 5b251f54f5..40b6daf3cf 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/config/shared_config.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/config/shared_config.go @@ -113,6 +113,8 @@ const ( disableRequestCompression = "disable_request_compression" requestMinCompressionSizeBytes = "request_min_compression_size_bytes" + disableClockSkewCorrection = "disable_clock_skew_correction" + s3DisableExpressSessionAuthKey = "s3_disable_express_session_auth" accountIDKey = "aws_account_id" @@ -346,6 +348,10 @@ type SharedConfig struct { // retrieved from config file's profile field request_min_compression_size_bytes RequestMinCompressSizeBytes *int64 + // determine if clock skew correction is disabled, default to false + // retrieved from config file's profile field disable_clock_skew_correction + DisableClockSkewCorrection *bool + // Whether S3Express auth is disabled. // // This will NOT prevent requests from being made to S3Express buckets, it @@ -1149,6 +1155,9 @@ func (c *SharedConfig) setFromIniSection(profile string, section ini.Section) er if err := updateDisableRequestCompression(&c.DisableRequestCompression, section, disableRequestCompression); err != nil { return fmt.Errorf("failed to load %s from shared config, %w", disableRequestCompression, err) } + if err := updateDisableRequestCompression(&c.DisableClockSkewCorrection, section, disableClockSkewCorrection); err != nil { + return fmt.Errorf("failed to load %s from shared config, %w", disableClockSkewCorrection, err) + } if err := updateRequestMinCompressSizeBytes(&c.RequestMinCompressSizeBytes, section, requestMinCompressionSizeBytes); err != nil { return fmt.Errorf("failed to load %s from shared config, %w", requestMinCompressionSizeBytes, err) } @@ -1292,6 +1301,13 @@ func (c SharedConfig) getDisableRequestCompression(ctx context.Context) (bool, b return *c.DisableRequestCompression, true, nil } +func (c SharedConfig) getDisableClockSkewCorrection(ctx context.Context) (bool, bool, error) { + if c.DisableClockSkewCorrection == nil { + return false, false, nil + } + return *c.DisableClockSkewCorrection, true, nil +} + func (c SharedConfig) getAccountIDEndpointMode(ctx context.Context) (aws.AccountIDEndpointMode, bool, error) { return c.AccountIDEndpointMode, len(c.AccountIDEndpointMode) > 0, nil } diff --git a/vendor/github.com/aws/aws-sdk-go-v2/credentials/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/credentials/CHANGELOG.md index 0b215e6b83..d69df4c866 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/credentials/CHANGELOG.md +++ b/vendor/github.com/aws/aws-sdk-go-v2/credentials/CHANGELOG.md @@ -1,3 +1,72 @@ +# v1.19.32 (2026-07-29) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.19.31 (2026-07-28) + +* **Dependency Update**: Update to smithy-go v1.27.5. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.19.30 (2026-07-21) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.19.29 (2026-07-13) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.19.28 (2026-07-08.2) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.19.27 (2026-07-06) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.19.26 (2026-07-01) + +* **Bug Fix**: Bump smithy-go to 1.27.3, fix JSON encorder for document.Number, endpoint host label format validation and CBOR union serialization on new serde +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.19.25 (2026-06-29) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.19.24 (2026-06-10) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.19.23 (2026-06-08) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.19.22 (2026-06-04) + +* **Dependency Update**: Update to smithy-go v1.27.1 to fix several union-related deserialization bugs in schema-serde-enabled services. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.19.21 (2026-06-03) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.19.20 (2026-06-02) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.19.19 (2026-05-29) + +* **Dependency Update**: Update to smithy-go v1.26.0. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.19.18 (2026-05-28) + +* **Bug Fix**: Create new login cache files with 0600 on Unix platforms. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.19.17 (2026-05-22) + +* **Dependency Update**: Updated to the latest SDK module versions + # v1.19.16 (2026-04-29) * **Dependency Update**: Update to smithy-go v1.25.1. diff --git a/vendor/github.com/aws/aws-sdk-go-v2/credentials/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/credentials/go_module_metadata.go index 5abad90cd9..fdbe842474 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/credentials/go_module_metadata.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/credentials/go_module_metadata.go @@ -3,4 +3,4 @@ package credentials // goModuleVersion is the tagged release for this module -const goModuleVersion = "1.19.16" +const goModuleVersion = "1.19.32" diff --git a/vendor/github.com/aws/aws-sdk-go-v2/credentials/logincreds/file.go b/vendor/github.com/aws/aws-sdk-go-v2/credentials/logincreds/file.go index 6cd5281d49..a9dbe540ea 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/credentials/logincreds/file.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/credentials/logincreds/file.go @@ -9,6 +9,6 @@ var openFile func(string) (io.ReadCloser, error) = func(name string) (io.ReadClo return os.Open(name) } -var createFile func(string) (io.WriteCloser, error) = func(name string) (io.WriteCloser, error) { - return os.Create(name) +var createFile func(string, os.FileMode) (io.WriteCloser, error) = func(name string, mode os.FileMode) (io.WriteCloser, error) { + return os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, mode) } diff --git a/vendor/github.com/aws/aws-sdk-go-v2/credentials/logincreds/provider.go b/vendor/github.com/aws/aws-sdk-go-v2/credentials/logincreds/provider.go index 3e6357b87c..1ca2a586b0 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/credentials/logincreds/provider.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/credentials/logincreds/provider.go @@ -42,6 +42,10 @@ type Options struct { // The path to the cached login token. CachedTokenFilepath string + // Whether to restrict file permissions on newly-written cache files. + // When true, files are created with 0600 on Unix. + RestrictPermissions bool + // The chain of providers that was used to create this provider. // // These values are for reporting purposes and are not meant to be set up @@ -145,7 +149,15 @@ func (p *Provider) saveToken(token *loginToken) error { return err } - f, err := createFile(p.options.CachedTokenFilepath) + mode := os.FileMode(0666) // matches that used by os.Create + if p.options.RestrictPermissions { + mode = 0600 + } + + // createFile DOES NOT re-create the file with new permissions if it + // already exists, so in that scenario any existing permissions are + // preserved + f, err := createFile(p.options.CachedTokenFilepath, mode) if err != nil { return err } diff --git a/vendor/github.com/aws/aws-sdk-go-v2/feature/ec2/imds/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/feature/ec2/imds/CHANGELOG.md index e17294549f..96bca6c3af 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/feature/ec2/imds/CHANGELOG.md +++ b/vendor/github.com/aws/aws-sdk-go-v2/feature/ec2/imds/CHANGELOG.md @@ -1,3 +1,46 @@ +# v1.18.33 (2026-07-29) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.18.32 (2026-07-28) + +* **Dependency Update**: Update to smithy-go v1.27.5. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.18.31 (2026-07-21) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.18.30 (2026-07-01) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.18.29 (2026-06-08) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.18.28 (2026-06-04) + +* **Dependency Update**: Update to smithy-go v1.27.1 to fix several union-related deserialization bugs in schema-serde-enabled services. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.18.27 (2026-06-03) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.18.26 (2026-06-02) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.18.25 (2026-05-29) + +* **Dependency Update**: Update to smithy-go v1.26.0. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.18.24 (2026-05-28) + +* **Dependency Update**: Updated to the latest SDK module versions + # v1.18.23 (2026-04-29) * **Dependency Update**: Update to smithy-go v1.25.1. diff --git a/vendor/github.com/aws/aws-sdk-go-v2/feature/ec2/imds/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/feature/ec2/imds/go_module_metadata.go index 7f59387edc..83e751954f 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/feature/ec2/imds/go_module_metadata.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/feature/ec2/imds/go_module_metadata.go @@ -3,4 +3,4 @@ package imds // goModuleVersion is the tagged release for this module -const goModuleVersion = "1.18.23" +const goModuleVersion = "1.18.33" diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/auth/smithy/v4signer_adapter_eventstream.go b/vendor/github.com/aws/aws-sdk-go-v2/internal/auth/smithy/v4signer_adapter_eventstream.go new file mode 100644 index 0000000000..320e888587 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/internal/auth/smithy/v4signer_adapter_eventstream.go @@ -0,0 +1,51 @@ +package smithy + +import ( + "context" + "fmt" + "time" + + v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4" + smithygo "github.com/aws/smithy-go" + "github.com/aws/smithy-go/auth" + "github.com/aws/smithy-go/eventstream" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +var _ smithyhttp.EventStreamSigner = (*V4SignerAdapter)(nil) + +// NewMessageSigner implements [smithyhttp.EventStreamSigner]. +func (v *V4SignerAdapter) NewMessageSigner(ctx context.Context, r *smithyhttp.Request, identity auth.Identity, props smithygo.Properties) (eventstream.MessageSigner, error) { + ca, ok := identity.(*CredentialsAdapter) + if !ok { + return nil, fmt.Errorf("unexpected identity type: %T", identity) + } + + name, ok := smithyhttp.GetSigV4SigningName(&props) + if !ok { + return nil, fmt.Errorf("sigv4 signing name is required") + } + + region, ok := smithyhttp.GetSigV4SigningRegion(&props) + if !ok { + return nil, fmt.Errorf("sigv4 signing region is required") + } + + seed, err := v4.GetSignedRequestSignature(r.Request) + if err != nil { + return nil, fmt.Errorf("get seed signature: %w", err) + } + + return &streamSignerAdapter{ + signer: v4.NewStreamSigner(ca.Credentials, name, region, seed), + }, nil +} + +// streamSignerAdapter adapts v4.StreamSigner to eventstream.MessageSigner. +type streamSignerAdapter struct { + signer *v4.StreamSigner +} + +func (s *streamSignerAdapter) SignMessage(headers, payload []byte, signingTime time.Time) ([]byte, error) { + return s.signer.GetSignature(context.Background(), headers, payload, signingTime) +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/configsources/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/internal/configsources/CHANGELOG.md index 0990a4143a..afcd57dd3f 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/internal/configsources/CHANGELOG.md +++ b/vendor/github.com/aws/aws-sdk-go-v2/internal/configsources/CHANGELOG.md @@ -1,3 +1,46 @@ +# v1.4.33 (2026-07-29) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.32 (2026-07-28) + +* **Dependency Update**: Update to smithy-go v1.27.5. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.31 (2026-07-21) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.30 (2026-07-01) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.29 (2026-06-08) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.28 (2026-06-04) + +* **Dependency Update**: Update to smithy-go v1.27.1 to fix several union-related deserialization bugs in schema-serde-enabled services. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.27 (2026-06-03) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.26 (2026-06-02) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.25 (2026-05-29) + +* **Dependency Update**: Update to smithy-go v1.26.0. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.24 (2026-05-28) + +* **Dependency Update**: Updated to the latest SDK module versions + # v1.4.23 (2026-04-29) * **Dependency Update**: Update to smithy-go v1.25.1. diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/configsources/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/internal/configsources/go_module_metadata.go index 05a8d3e7bc..f38ccca23f 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/internal/configsources/go_module_metadata.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/internal/configsources/go_module_metadata.go @@ -3,4 +3,4 @@ package configsources // goModuleVersion is the tagged release for this module -const goModuleVersion = "1.4.23" +const goModuleVersion = "1.4.33" diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/context/context.go b/vendor/github.com/aws/aws-sdk-go-v2/internal/context/context.go index f0c283d394..52f4ebc25c 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/internal/context/context.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/internal/context/context.go @@ -50,3 +50,16 @@ func GetAttemptSkewContext(ctx context.Context) time.Duration { x, _ := middleware.GetStackValue(ctx, clockSkew{}).(time.Duration) return x } + +type longPollingKey struct{} + +// SetIsLongPolling marks the operation as long-polling on the context. +func SetIsLongPolling(ctx context.Context, v bool) context.Context { + return middleware.WithStackValue(ctx, longPollingKey{}, v) +} + +// GetIsLongPolling returns whether the operation is long-polling. +func GetIsLongPolling(ctx context.Context) bool { + v, _ := middleware.GetStackValue(ctx, longPollingKey{}).(bool) + return v +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2/CHANGELOG.md index 49577e3e94..ae09aa2d06 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2/CHANGELOG.md +++ b/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2/CHANGELOG.md @@ -1,3 +1,46 @@ +# v2.7.33 (2026-07-29) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v2.7.32 (2026-07-28) + +* **Dependency Update**: Update to smithy-go v1.27.5. +* **Dependency Update**: Updated to the latest SDK module versions + +# v2.7.31 (2026-07-21) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v2.7.30 (2026-07-01) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v2.7.29 (2026-06-08) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v2.7.28 (2026-06-04) + +* **Dependency Update**: Update to smithy-go v1.27.1 to fix several union-related deserialization bugs in schema-serde-enabled services. +* **Dependency Update**: Updated to the latest SDK module versions + +# v2.7.27 (2026-06-03) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v2.7.26 (2026-06-02) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v2.7.25 (2026-05-29) + +* **Dependency Update**: Update to smithy-go v1.26.0. +* **Dependency Update**: Updated to the latest SDK module versions + +# v2.7.24 (2026-05-28) + +* **Dependency Update**: Updated to the latest SDK module versions + # v2.7.23 (2026-04-29) * **Dependency Update**: Update to smithy-go v1.25.1. diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2/go_module_metadata.go index 1e92900a1e..98755086c5 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2/go_module_metadata.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/internal/endpoints/v2/go_module_metadata.go @@ -3,4 +3,4 @@ package endpoints // goModuleVersion is the tagged release for this module -const goModuleVersion = "2.7.23" +const goModuleVersion = "2.7.33" diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/CHANGELOG.md index e1e3c23a74..19ff9fc681 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/CHANGELOG.md +++ b/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/CHANGELOG.md @@ -1,3 +1,46 @@ +# v1.4.34 (2026-07-29) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.33 (2026-07-28) + +* **Dependency Update**: Update to smithy-go v1.27.5. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.32 (2026-07-21) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.31 (2026-07-01) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.30 (2026-06-08) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.29 (2026-06-04) + +* **Dependency Update**: Update to smithy-go v1.27.1 to fix several union-related deserialization bugs in schema-serde-enabled services. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.28 (2026-06-03) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.27 (2026-06-02) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.26 (2026-05-29) + +* **Dependency Update**: Update to smithy-go v1.26.0. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.25 (2026-05-28) + +* **Dependency Update**: Updated to the latest SDK module versions + # v1.4.24 (2026-04-29) * **Dependency Update**: Update to smithy-go v1.25.1. diff --git a/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/go_module_metadata.go index 455cb74e1a..1eb67f426a 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/go_module_metadata.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/internal/v4a/go_module_metadata.go @@ -3,4 +3,4 @@ package v4a // goModuleVersion is the tagged release for this module -const goModuleVersion = "1.4.24" +const goModuleVersion = "1.4.34" diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding/CHANGELOG.md index cf6c5e0911..44b2089007 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding/CHANGELOG.md +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding/CHANGELOG.md @@ -1,3 +1,23 @@ +# v1.13.14 (2026-07-28) + +* **Dependency Update**: Update to smithy-go v1.27.5. + +# v1.13.13 (2026-07-01) + +* No change notes available for this release. + +# v1.13.12 (2026-06-04) + +* **Dependency Update**: Update to smithy-go v1.27.1 to fix several union-related deserialization bugs in schema-serde-enabled services. + +# v1.13.11 (2026-06-03) + +* No change notes available for this release. + +# v1.13.10 (2026-05-29) + +* **Dependency Update**: Update to smithy-go v1.26.0. + # v1.13.9 (2026-04-29) * **Dependency Update**: Update to smithy-go v1.25.1. diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding/go_module_metadata.go index e145070706..2898c5e525 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding/go_module_metadata.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding/go_module_metadata.go @@ -3,4 +3,4 @@ package acceptencoding // goModuleVersion is the tagged release for this module -const goModuleVersion = "1.13.9" +const goModuleVersion = "1.13.14" diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/internal/presigned-url/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/service/internal/presigned-url/CHANGELOG.md index 96adad5261..90dcc45c99 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/internal/presigned-url/CHANGELOG.md +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/internal/presigned-url/CHANGELOG.md @@ -1,3 +1,46 @@ +# v1.13.33 (2026-07-29) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.13.32 (2026-07-28) + +* **Dependency Update**: Update to smithy-go v1.27.5. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.13.31 (2026-07-21) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.13.30 (2026-07-01) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.13.29 (2026-06-08) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.13.28 (2026-06-04) + +* **Dependency Update**: Update to smithy-go v1.27.1 to fix several union-related deserialization bugs in schema-serde-enabled services. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.13.27 (2026-06-03) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.13.26 (2026-06-02) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.13.25 (2026-05-29) + +* **Dependency Update**: Update to smithy-go v1.26.0. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.13.24 (2026-05-28) + +* **Dependency Update**: Updated to the latest SDK module versions + # v1.13.23 (2026-04-29) * **Dependency Update**: Update to smithy-go v1.25.1. diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/internal/presigned-url/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/service/internal/presigned-url/go_module_metadata.go index 5737e9c0c1..8a073d2487 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/internal/presigned-url/go_module_metadata.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/internal/presigned-url/go_module_metadata.go @@ -3,4 +3,4 @@ package presignedurl // goModuleVersion is the tagged release for this module -const goModuleVersion = "1.13.23" +const goModuleVersion = "1.13.33" diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/signin/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/CHANGELOG.md index 253e035967..561b01cb7c 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/signin/CHANGELOG.md +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/CHANGELOG.md @@ -1,3 +1,69 @@ +# v1.5.2 (2026-07-29) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.5.1 (2026-07-28) + +* **Dependency Update**: Update to smithy-go v1.27.5. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.5.0 (2026-07-21) + +* **Feature**: Add an option to clients to disable clock skew +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.4.1 (2026-07-13) + +* No change notes available for this release. + +# v1.4.0 (2026-07-08.2) + +* **Feature**: Adds support for OAuth 2.0 token operations in AWS Sign-In, CreateOAuth2TokenWithIAM (client credentials flow), IntrospectOAuth2TokenWithIAM (token inspection), and RevokeOAuth2TokenWithIAM (token revocation). + +# v1.3.0 (2026-07-06) + +* **Feature**: Add request serialization snapshot tests. + +# v1.2.2 (2026-07-01) + +* **Bug Fix**: Bump smithy-go to 1.27.3, fix JSON encorder for document.Number, endpoint host label format validation and CBOR union serialization on new serde +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.2.1 (2026-06-29) + +* No change notes available for this release. + +# v1.2.0 (2026-06-10) + +* **Feature**: AWS Sign-In now allows customers to control access to the AWS Management Console using resource-based policies. With this release customers can restrict console access based on network perimeters such as VPC IDs, VPC endpoints, and IP addresses. + +# v1.1.5 (2026-06-08) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.1.4 (2026-06-04) + +* **Dependency Update**: Update to smithy-go v1.27.1 to fix several union-related deserialization bugs in schema-serde-enabled services. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.1.3 (2026-06-03) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.1.2 (2026-06-02) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.1.1 (2026-05-29) + +* **Dependency Update**: Update to smithy-go v1.26.0. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.1.0 (2026-05-28) + +* **Feature**: Adding new BDD representation of endpoint ruleset +* **Dependency Update**: Updated to the latest SDK module versions + # v1.0.11 (2026-04-29) * **Dependency Update**: Update to smithy-go v1.25.1. diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/signin/api_client.go b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/api_client.go index 2c0413c16e..2094f01d27 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/signin/api_client.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/api_client.go @@ -4,6 +4,7 @@ package signin import ( "context" + cryptorand "crypto/rand" "errors" "fmt" "github.com/aws/aws-sdk-go-v2/aws" @@ -20,6 +21,7 @@ import ( "github.com/aws/smithy-go/logging" "github.com/aws/smithy-go/metrics" "github.com/aws/smithy-go/middleware" + smithyrand "github.com/aws/smithy-go/rand" "github.com/aws/smithy-go/tracing" smithyhttp "github.com/aws/smithy-go/transport/http" "net" @@ -201,6 +203,8 @@ func New(options Options, optFns ...func(*Options)) *Client { resolveHTTPSignerV4(&options) + resolveIdempotencyTokenProvider(&options) + resolveEndpointResolverV2(&options) resolveTracerProvider(&options) @@ -259,6 +263,10 @@ func (c *Client) invokeOperation( finalizeClientEndpointResolverOptions(&options) + if err := c.addCommonMiddlewares(stack, options, opID); err != nil { + return nil, metadata, err + } + for _, fn := range stackFns { if err := fn(stack, options); err != nil { return nil, metadata, err @@ -363,6 +371,49 @@ func addProtocolFinalizerMiddlewares(stack *middleware.Stack, options Options, o } return nil } + +func (c *Client) addCommonMiddlewares(stack *middleware.Stack, options Options, operation string) error { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, operation); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + if err := addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err := addClientRequestID(stack); err != nil { + return err + } + if err := addRetry(stack, options, c); err != nil { + return err + } + if err := addRawResponseToMetadata(stack); err != nil { + return err + } + if err := addSpanRetryLoop(stack, options); err != nil { + return err + } + if err := addClientUserAgent(stack, options); err != nil { + return err + } + if err := addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err := addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err := addRecursionDetection(stack); err != nil { + return err + } + if err := addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err := addInterceptAttempt(stack, options); err != nil { + return err + } + return nil +} func resolveAuthSchemeResolver(options *Options) { if options.AuthSchemeResolver == nil { options.AuthSchemeResolver = &defaultAuthSchemeResolver{} @@ -436,16 +487,17 @@ func setResolvedDefaultsMode(o *Options) { // NewFromConfig returns a new client from the provided config. func NewFromConfig(cfg aws.Config, optFns ...func(*Options)) *Client { opts := Options{ - Region: cfg.Region, - DefaultsMode: cfg.DefaultsMode, - RuntimeEnvironment: cfg.RuntimeEnvironment, - HTTPClient: cfg.HTTPClient, - Credentials: cfg.Credentials, - APIOptions: cfg.APIOptions, - Logger: cfg.Logger, - ClientLogMode: cfg.ClientLogMode, - AppID: cfg.AppID, - AuthSchemePreference: cfg.AuthSchemePreference, + Region: cfg.Region, + DefaultsMode: cfg.DefaultsMode, + RuntimeEnvironment: cfg.RuntimeEnvironment, + HTTPClient: cfg.HTTPClient, + Credentials: cfg.Credentials, + APIOptions: cfg.APIOptions, + Logger: cfg.Logger, + ClientLogMode: cfg.ClientLogMode, + AppID: cfg.AppID, + DisableClockSkewCorrection: cfg.DisableClockSkewCorrection, + AuthSchemePreference: cfg.AuthSchemePreference, } resolveAWSRetryerProvider(cfg, &opts) resolveAWSRetryMaxAttempts(cfg, &opts) @@ -635,15 +687,17 @@ func addClientRequestID(stack *middleware.Stack) error { } func addComputeContentLength(stack *middleware.Stack) error { - return stack.Build.Add(&smithyhttp.ComputeContentLength{}, middleware.After) + return stack.Build.Insert(&smithyhttp.ComputeContentLength{}, "ClientRequestID", middleware.After) } func addRawResponseToMetadata(stack *middleware.Stack) error { return stack.Deserialize.Add(&awsmiddleware.AddRawResponse{}, middleware.Before) } -func addRecordResponseTiming(stack *middleware.Stack) error { - return stack.Deserialize.Add(&awsmiddleware.RecordResponseTiming{}, middleware.After) +func addRecordResponseTiming(stack *middleware.Stack, options Options) error { + return stack.Deserialize.Add(&awsmiddleware.RecordResponseTiming{ + DisableClockSkewCorrection: options.DisableClockSkewCorrection, + }, middleware.After) } func addSpanRetryLoop(stack *middleware.Stack, options Options) error { @@ -709,11 +763,19 @@ func addIsPaginatorUserAgent(o *Options) { }) } +func resolveIdempotencyTokenProvider(o *Options) { + if o.IdempotencyTokenProvider != nil { + return + } + o.IdempotencyTokenProvider = smithyrand.NewUUIDIdempotencyToken(cryptorand.Reader) +} + func addRetry(stack *middleware.Stack, o Options, c *Client) error { attempt := retry.NewAttemptMiddleware(o.Retryer, smithyhttp.RequestCloner, func(m *retry.Attempt) { m.LogAttempts = o.ClientLogMode.IsRetries() m.OperationMeter = o.MeterProvider.Meter("github.com/aws/aws-sdk-go-v2/service/signin") m.ClientSkew = c.timeOffset + m.DisableClockSkewCorrection = o.DisableClockSkewCorrection }) if err := stack.Finalize.Insert(attempt, "ResolveAuthScheme", middleware.Before); err != nil { return err @@ -816,6 +878,19 @@ func resolveMeterProvider(options *Options) { } } +// IdempotencyTokenProvider interface for providing idempotency token +type IdempotencyTokenProvider interface { + GetIdempotencyToken() (string, error) +} + +func newServiceMetadataMiddleware(region, operation string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: operation, + } +} + func addRecursionDetection(stack *middleware.Stack) error { return stack.Build.Add(&awsmiddleware.RecursionDetection{}, middleware.After) } diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/signin/api_op_CreateOAuth2Token.go b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/api_op_CreateOAuth2Token.go index dec8656f86..00bec7fb7e 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/signin/api_op_CreateOAuth2Token.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/api_op_CreateOAuth2Token.go @@ -4,10 +4,9 @@ package signin import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/aws-sdk-go-v2/service/signin/types" "github.com/aws/smithy-go/middleware" + "github.com/aws/smithy-go/ptr" smithyhttp "github.com/aws/smithy-go/transport/http" ) @@ -84,6 +83,11 @@ type CreateOAuth2TokenInput struct { noSmithyDocumentSerde } +func (in *CreateOAuth2TokenInput) bindEndpointParams(p *EndpointParameters) { + + p.IsControlPlane = ptr.Bool(false) +} + // Output structure for CreateOAuth2Token operation // // Contains flattened token operation outputs for both authorization code and @@ -104,9 +108,6 @@ type CreateOAuth2TokenOutput struct { } func (c *Client) addOperationCreateOAuth2TokenMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsRestjson1_serializeOpCreateOAuth2Token{}, middleware.After) if err != nil { return err @@ -115,38 +116,17 @@ func (c *Client) addOperationCreateOAuth2TokenMiddlewares(stack *middleware.Stac if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "CreateOAuth2Token"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } if err = addResolveEndpointMiddleware(stack, options); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -155,22 +135,13 @@ func (c *Client) addOperationCreateOAuth2TokenMiddlewares(stack *middleware.Stac if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpCreateOAuth2TokenValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opCreateOAuth2Token(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "CreateOAuth2Token"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -185,22 +156,8 @@ func (c *Client) addOperationCreateOAuth2TokenMiddlewares(stack *middleware.Stac if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opCreateOAuth2Token(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "CreateOAuth2Token", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/signin/api_op_CreateOAuth2TokenWithIAM.go b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/api_op_CreateOAuth2TokenWithIAM.go new file mode 100644 index 0000000000..f66701cd3d --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/api_op_CreateOAuth2TokenWithIAM.go @@ -0,0 +1,134 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package signin + +import ( + "context" + "github.com/aws/smithy-go/middleware" + "github.com/aws/smithy-go/ptr" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Grants permission to exchange client credentials for an OAuth 2.0 access token +// scoped to a resource that can be used to access AWS services from applications +func (c *Client) CreateOAuth2TokenWithIAM(ctx context.Context, params *CreateOAuth2TokenWithIAMInput, optFns ...func(*Options)) (*CreateOAuth2TokenWithIAMOutput, error) { + if params == nil { + params = &CreateOAuth2TokenWithIAMInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "CreateOAuth2TokenWithIAM", params, optFns, c.addOperationCreateOAuth2TokenWithIAMMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*CreateOAuth2TokenWithIAMOutput) + out.ResultMetadata = metadata + return out, nil +} + +// Input structure for CreateOAuth2TokenWithIAM operation +type CreateOAuth2TokenWithIAMInput struct { + + // OAuth 2.0 grant type. Must be "client_credentials". + // + // This member is required. + GrantType *string + + // The OAuth resource for which the access token is requested. Example: + // "aws-mcp.amazonaws.com". + // + // This member is required. + Resource *string + + noSmithyDocumentSerde +} + +func (in *CreateOAuth2TokenWithIAMInput) bindEndpointParams(p *EndpointParameters) { + + p.IsOAuthEndpoint = ptr.Bool(true) +} + +// Output structure for CreateOAuth2TokenWithIAM operation +// +// Contains the JWT access token, token type, and expiration per RFC 6749 §5.1. +type CreateOAuth2TokenWithIAMOutput struct { + + // JWT access token containing principal identity, resource scope, and session + // metadata + // + // This member is required. + AccessToken *string + + // Token lifetime in seconds. Value is the minimum of session validity and 1 hour. + // + // This member is required. + ExpiresIn *int32 + + // Always "Bearer" per OAuth 2.1 specification + // + // This member is required. + TokenType *string + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationCreateOAuth2TokenWithIAMMiddlewares(stack *middleware.Stack, options Options) (err error) { + err = stack.Serialize.Add(&awsRestjson1_serializeOpCreateOAuth2TokenWithIAM{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpCreateOAuth2TokenWithIAM{}, middleware.After) + if err != nil { + return err + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpCreateOAuth2TokenWithIAMValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "CreateOAuth2TokenWithIAM"), middleware.Before); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/signin/api_op_DeleteConsoleAuthorizationConfiguration.go b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/api_op_DeleteConsoleAuthorizationConfiguration.go new file mode 100644 index 0000000000..749157c227 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/api_op_DeleteConsoleAuthorizationConfiguration.go @@ -0,0 +1,119 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package signin + +import ( + "context" + "github.com/aws/smithy-go/middleware" + "github.com/aws/smithy-go/ptr" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Delete console authorization configuration with automatic scope detection +func (c *Client) DeleteConsoleAuthorizationConfiguration(ctx context.Context, params *DeleteConsoleAuthorizationConfigurationInput, optFns ...func(*Options)) (*DeleteConsoleAuthorizationConfigurationOutput, error) { + if params == nil { + params = &DeleteConsoleAuthorizationConfigurationInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "DeleteConsoleAuthorizationConfiguration", params, optFns, c.addOperationDeleteConsoleAuthorizationConfigurationMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*DeleteConsoleAuthorizationConfigurationOutput) + out.ResultMetadata = metadata + return out, nil +} + +// Input for DeleteConsoleAuthorizationConfiguration operation +type DeleteConsoleAuthorizationConfigurationInput struct { + + // Target account identifier + TargetId *string + + noSmithyDocumentSerde +} + +func (in *DeleteConsoleAuthorizationConfigurationInput) bindEndpointParams(p *EndpointParameters) { + + p.IsControlPlane = ptr.Bool(true) +} + +// Output for DeleteConsoleAuthorizationConfiguration operation +type DeleteConsoleAuthorizationConfigurationOutput struct { + + // Whether console authorization is enabled + // + // This member is required. + ConsoleAuthorizationEnabled *bool + + // Authorization scope + // + // This member is required. + Scope *string + + // Target account identifier + // + // This member is required. + TargetId *string + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationDeleteConsoleAuthorizationConfigurationMiddlewares(stack *middleware.Stack, options Options) (err error) { + err = stack.Serialize.Add(&awsRestjson1_serializeOpDeleteConsoleAuthorizationConfiguration{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpDeleteConsoleAuthorizationConfiguration{}, middleware.After) + if err != nil { + return err + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "DeleteConsoleAuthorizationConfiguration"), middleware.Before); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/signin/api_op_DeleteResourcePermissionStatement.go b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/api_op_DeleteResourcePermissionStatement.go new file mode 100644 index 0000000000..d76325ff80 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/api_op_DeleteResourcePermissionStatement.go @@ -0,0 +1,148 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package signin + +import ( + "context" + "fmt" + "github.com/aws/smithy-go/middleware" + "github.com/aws/smithy-go/ptr" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Remove a permission statement from the account's SignIn resource-based policy +func (c *Client) DeleteResourcePermissionStatement(ctx context.Context, params *DeleteResourcePermissionStatementInput, optFns ...func(*Options)) (*DeleteResourcePermissionStatementOutput, error) { + if params == nil { + params = &DeleteResourcePermissionStatementInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "DeleteResourcePermissionStatement", params, optFns, c.addOperationDeleteResourcePermissionStatementMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*DeleteResourcePermissionStatementOutput) + out.ResultMetadata = metadata + return out, nil +} + +// Input for DeleteResourcePermissionStatement operation +type DeleteResourcePermissionStatementInput struct { + + // Unique identifier of the permission statement to delete + // + // This member is required. + StatementId *string + + // Idempotency token for the request + ClientToken *string + + noSmithyDocumentSerde +} + +func (in *DeleteResourcePermissionStatementInput) bindEndpointParams(p *EndpointParameters) { + + p.IsControlPlane = ptr.Bool(true) +} + +// Output for DeleteResourcePermissionStatement operation +type DeleteResourcePermissionStatementOutput struct { + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationDeleteResourcePermissionStatementMiddlewares(stack *middleware.Stack, options Options) (err error) { + err = stack.Serialize.Add(&awsRestjson1_serializeOpDeleteResourcePermissionStatement{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpDeleteResourcePermissionStatement{}, middleware.After) + if err != nil { + return err + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addIdempotencyToken_opDeleteResourcePermissionStatementMiddleware(stack, options); err != nil { + return err + } + if err = addOpDeleteResourcePermissionStatementValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "DeleteResourcePermissionStatement"), middleware.Before); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +type idempotencyToken_initializeOpDeleteResourcePermissionStatement struct { + tokenProvider IdempotencyTokenProvider +} + +func (*idempotencyToken_initializeOpDeleteResourcePermissionStatement) ID() string { + return "OperationIdempotencyTokenAutoFill" +} + +func (m *idempotencyToken_initializeOpDeleteResourcePermissionStatement) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + if m.tokenProvider == nil { + return next.HandleInitialize(ctx, in) + } + + input, ok := in.Parameters.(*DeleteResourcePermissionStatementInput) + if !ok { + return out, metadata, fmt.Errorf("expected middleware input to be of type *DeleteResourcePermissionStatementInput ") + } + + if input.ClientToken == nil { + t, err := m.tokenProvider.GetIdempotencyToken() + if err != nil { + return out, metadata, err + } + input.ClientToken = &t + } + return next.HandleInitialize(ctx, in) +} +func addIdempotencyToken_opDeleteResourcePermissionStatementMiddleware(stack *middleware.Stack, cfg Options) error { + return stack.Initialize.Add(&idempotencyToken_initializeOpDeleteResourcePermissionStatement{tokenProvider: cfg.IdempotencyTokenProvider}, middleware.Before) +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/signin/api_op_GetConsoleAuthorizationConfiguration.go b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/api_op_GetConsoleAuthorizationConfiguration.go new file mode 100644 index 0000000000..74a927da1b --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/api_op_GetConsoleAuthorizationConfiguration.go @@ -0,0 +1,119 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package signin + +import ( + "context" + "github.com/aws/smithy-go/middleware" + "github.com/aws/smithy-go/ptr" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Get console authorization configuration with automatic scope detection +func (c *Client) GetConsoleAuthorizationConfiguration(ctx context.Context, params *GetConsoleAuthorizationConfigurationInput, optFns ...func(*Options)) (*GetConsoleAuthorizationConfigurationOutput, error) { + if params == nil { + params = &GetConsoleAuthorizationConfigurationInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "GetConsoleAuthorizationConfiguration", params, optFns, c.addOperationGetConsoleAuthorizationConfigurationMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*GetConsoleAuthorizationConfigurationOutput) + out.ResultMetadata = metadata + return out, nil +} + +// Input for GetConsoleAuthorizationConfiguration operation +type GetConsoleAuthorizationConfigurationInput struct { + + // Target account identifier + TargetId *string + + noSmithyDocumentSerde +} + +func (in *GetConsoleAuthorizationConfigurationInput) bindEndpointParams(p *EndpointParameters) { + + p.IsControlPlane = ptr.Bool(true) +} + +// Output for GetConsoleAuthorizationConfiguration operation +type GetConsoleAuthorizationConfigurationOutput struct { + + // Whether console authorization is enabled + // + // This member is required. + ConsoleAuthorizationEnabled *bool + + // Authorization scope + // + // This member is required. + Scope *string + + // Target account identifier + // + // This member is required. + TargetId *string + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationGetConsoleAuthorizationConfigurationMiddlewares(stack *middleware.Stack, options Options) (err error) { + err = stack.Serialize.Add(&awsRestjson1_serializeOpGetConsoleAuthorizationConfiguration{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpGetConsoleAuthorizationConfiguration{}, middleware.After) + if err != nil { + return err + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "GetConsoleAuthorizationConfiguration"), middleware.Before); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/signin/api_op_GetResourcePolicy.go b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/api_op_GetResourcePolicy.go new file mode 100644 index 0000000000..4ee0d496ad --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/api_op_GetResourcePolicy.go @@ -0,0 +1,106 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package signin + +import ( + "context" + "github.com/aws/aws-sdk-go-v2/service/signin/types" + "github.com/aws/smithy-go/middleware" + "github.com/aws/smithy-go/ptr" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Retrieve the account's consolidated SignIn resource-based policy +func (c *Client) GetResourcePolicy(ctx context.Context, params *GetResourcePolicyInput, optFns ...func(*Options)) (*GetResourcePolicyOutput, error) { + if params == nil { + params = &GetResourcePolicyInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "GetResourcePolicy", params, optFns, c.addOperationGetResourcePolicyMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*GetResourcePolicyOutput) + out.ResultMetadata = metadata + return out, nil +} + +// Input for GetResourcePolicy operation +type GetResourcePolicyInput struct { + noSmithyDocumentSerde +} + +func (in *GetResourcePolicyInput) bindEndpointParams(p *EndpointParameters) { + + p.IsControlPlane = ptr.Bool(true) +} + +// Output for GetResourcePolicy operation +type GetResourcePolicyOutput struct { + + // The account's SignIn resource-based policy + // + // This member is required. + SigninResourceBasedPolicy *types.SigninResourceBasedPolicy + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationGetResourcePolicyMiddlewares(stack *middleware.Stack, options Options) (err error) { + err = stack.Serialize.Add(&awsRestjson1_serializeOpGetResourcePolicy{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpGetResourcePolicy{}, middleware.After) + if err != nil { + return err + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "GetResourcePolicy"), middleware.Before); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/signin/api_op_IntrospectOAuth2TokenWithIAM.go b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/api_op_IntrospectOAuth2TokenWithIAM.go new file mode 100644 index 0000000000..610390995d --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/api_op_IntrospectOAuth2TokenWithIAM.go @@ -0,0 +1,184 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package signin + +import ( + "context" + "github.com/aws/smithy-go/middleware" + "github.com/aws/smithy-go/ptr" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Grants permission to inspect the metadata and state of an OAuth 2.0 access +// token or refresh token +// +// Implements RFC 7662 OAuth 2.0 Token Introspection over a SigV4-authenticated +// endpoint. Inspects the metadata of an access_token or refresh_token issued by +// AWS Sign-In and returns the claims associated with it. +// +// Inactive token semantics (RFC 7662 §2.2): when the supplied token is unknown, +// expired, revoked, malformed, or owned by a different account, the response body +// is exactly { "active": false } with all other claims omitted. +func (c *Client) IntrospectOAuth2TokenWithIAM(ctx context.Context, params *IntrospectOAuth2TokenWithIAMInput, optFns ...func(*Options)) (*IntrospectOAuth2TokenWithIAMOutput, error) { + if params == nil { + params = &IntrospectOAuth2TokenWithIAMInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "IntrospectOAuth2TokenWithIAM", params, optFns, c.addOperationIntrospectOAuth2TokenWithIAMMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*IntrospectOAuth2TokenWithIAMOutput) + out.ResultMetadata = metadata + return out, nil +} + +// Input structure for IntrospectOAuth2TokenWithIAM operation +// +// RFC 7662 §2.1 introspection request. Contains the token to inspect and an +// optional hint about the token's type. +type IntrospectOAuth2TokenWithIAMInput struct { + + // The string value of the token to introspect. May be either an access_token or a + // refresh_token issued by AWS Sign-In. + // + // This member is required. + Token *string + + // Optional hint about the type of the token submitted for introspection. The + // server uses this hint to optimize lookup, but still falls back to the other + // token type on miss. Allowed values: access_token, refresh_token. + TokenTypeHint *string + + noSmithyDocumentSerde +} + +func (in *IntrospectOAuth2TokenWithIAMInput) bindEndpointParams(p *EndpointParameters) { + + p.IsOAuthEndpoint = ptr.Bool(true) +} + +// Output structure for IntrospectOAuth2TokenWithIAM operation +// +// RFC 7662 §2.2 introspection response. Only active is required; all other claims +// are omitted when the token is inactive. +type IntrospectOAuth2TokenWithIAMOutput struct { + + // Indicates whether the token is currently active. true only when the token is + // valid, has not expired, has not been revoked, and belongs to the caller's + // account. + // + // This member is required. + Active *bool + + // 12-digit AWS account ID of the token's subject principal. + AccountId *string + + // Audience of the token: the OAuth resource the token is scoped to (for example, + // "aws-mcp.amazonaws.com"). Omitted for refresh tokens. + Aud *string + + // Client identifier for the OAuth 2.0 client that requested the token. + ClientId *string + + // Token expiration time as a NumericDate (Unix epoch seconds). + Exp *int64 + + // Token issuance time as a NumericDate (Unix epoch seconds). + Iat *int64 + + // Issuer of the token. Always "signin.amazonaws.com" for AWS Sign-In. + Iss *string + + // Unique identifier for the token. + Jti *string + + // Token "not before" time as a NumericDate (Unix epoch seconds). + Nbf *int64 + + // The OAuth resource the token is scoped to during Human OAuth flow. Only present + // for refresh token introspection. + Resource *string + + // AWS Sign-In session ARN bound to the token, of the form + // arn:aws:signin:{region}:{account}:session/{uuid}. + SigninSession *string + + // Subject of the token: the IAM principal ARN. For assumed-role sessions, this is + // the session ARN (matches sts:GetCallerIdentity's Arn field), e.g. + // arn:aws:sts::123456789012:assumed-role/MyRole/session-name. + Sub *string + + // Indicates which kind of token was introspected. One of "access_token" or + // "refresh_token". + TokenType *string + + // User identifier matching sts:GetCallerIdentity's UserId field for the token's + // subject principal (e.g. "AIDAEXAMPLE" for an IAM user, or + // "AROAEXAMPLE:session-name" for an assumed role). + UserId *string + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationIntrospectOAuth2TokenWithIAMMiddlewares(stack *middleware.Stack, options Options) (err error) { + err = stack.Serialize.Add(&awsRestjson1_serializeOpIntrospectOAuth2TokenWithIAM{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpIntrospectOAuth2TokenWithIAM{}, middleware.After) + if err != nil { + return err + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpIntrospectOAuth2TokenWithIAMValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "IntrospectOAuth2TokenWithIAM"), middleware.Before); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/signin/api_op_ListResourcePermissionStatements.go b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/api_op_ListResourcePermissionStatements.go new file mode 100644 index 0000000000..2cb7c5c67f --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/api_op_ListResourcePermissionStatements.go @@ -0,0 +1,213 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package signin + +import ( + "context" + "fmt" + "github.com/aws/aws-sdk-go-v2/service/signin/types" + "github.com/aws/smithy-go/middleware" + "github.com/aws/smithy-go/ptr" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Retrieve all permission statements in the account's SignIn resource-based policy +func (c *Client) ListResourcePermissionStatements(ctx context.Context, params *ListResourcePermissionStatementsInput, optFns ...func(*Options)) (*ListResourcePermissionStatementsOutput, error) { + if params == nil { + params = &ListResourcePermissionStatementsInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "ListResourcePermissionStatements", params, optFns, c.addOperationListResourcePermissionStatementsMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*ListResourcePermissionStatementsOutput) + out.ResultMetadata = metadata + return out, nil +} + +// Input for ListResourcePermissionStatements operation +type ListResourcePermissionStatementsInput struct { + + // Maximum number of results to return + MaxResults *int32 + + // Token for pagination + NextToken *string + + noSmithyDocumentSerde +} + +func (in *ListResourcePermissionStatementsInput) bindEndpointParams(p *EndpointParameters) { + + p.IsControlPlane = ptr.Bool(true) +} + +// Output for ListResourcePermissionStatements operation +type ListResourcePermissionStatementsOutput struct { + + // List of permission statement summaries + // + // This member is required. + PermissionStatements []types.PermissionStatementSummary + + // Token for next page of results + NextToken *string + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationListResourcePermissionStatementsMiddlewares(stack *middleware.Stack, options Options) (err error) { + err = stack.Serialize.Add(&awsRestjson1_serializeOpListResourcePermissionStatements{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpListResourcePermissionStatements{}, middleware.After) + if err != nil { + return err + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "ListResourcePermissionStatements"), middleware.Before); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +// ListResourcePermissionStatementsPaginatorOptions is the paginator options for +// ListResourcePermissionStatements +type ListResourcePermissionStatementsPaginatorOptions struct { + // Maximum number of results to return + Limit int32 + + // Set to true if pagination should stop if the service returns a pagination token + // that matches the most recent token provided to the service. + StopOnDuplicateToken bool +} + +// ListResourcePermissionStatementsPaginator is a paginator for +// ListResourcePermissionStatements +type ListResourcePermissionStatementsPaginator struct { + options ListResourcePermissionStatementsPaginatorOptions + client ListResourcePermissionStatementsAPIClient + params *ListResourcePermissionStatementsInput + nextToken *string + firstPage bool +} + +// NewListResourcePermissionStatementsPaginator returns a new +// ListResourcePermissionStatementsPaginator +func NewListResourcePermissionStatementsPaginator(client ListResourcePermissionStatementsAPIClient, params *ListResourcePermissionStatementsInput, optFns ...func(*ListResourcePermissionStatementsPaginatorOptions)) *ListResourcePermissionStatementsPaginator { + if params == nil { + params = &ListResourcePermissionStatementsInput{} + } + + options := ListResourcePermissionStatementsPaginatorOptions{} + if params.MaxResults != nil { + options.Limit = *params.MaxResults + } + + for _, fn := range optFns { + fn(&options) + } + + return &ListResourcePermissionStatementsPaginator{ + options: options, + client: client, + params: params, + firstPage: true, + nextToken: params.NextToken, + } +} + +// HasMorePages returns a boolean indicating whether more pages are available +func (p *ListResourcePermissionStatementsPaginator) HasMorePages() bool { + return p.firstPage || (p.nextToken != nil && len(*p.nextToken) != 0) +} + +// NextPage retrieves the next ListResourcePermissionStatements page. +func (p *ListResourcePermissionStatementsPaginator) NextPage(ctx context.Context, optFns ...func(*Options)) (*ListResourcePermissionStatementsOutput, error) { + if !p.HasMorePages() { + return nil, fmt.Errorf("no more pages available") + } + + params := *p.params + params.NextToken = p.nextToken + + var limit *int32 + if p.options.Limit > 0 { + limit = &p.options.Limit + } + params.MaxResults = limit + + optFns = append([]func(*Options){ + addIsPaginatorUserAgent, + }, optFns...) + result, err := p.client.ListResourcePermissionStatements(ctx, ¶ms, optFns...) + if err != nil { + return nil, err + } + p.firstPage = false + + prevToken := p.nextToken + p.nextToken = result.NextToken + + if p.options.StopOnDuplicateToken && + prevToken != nil && + p.nextToken != nil && + *prevToken == *p.nextToken { + p.nextToken = nil + } + + return result, nil +} + +// ListResourcePermissionStatementsAPIClient is a client that implements the +// ListResourcePermissionStatements operation. +type ListResourcePermissionStatementsAPIClient interface { + ListResourcePermissionStatements(context.Context, *ListResourcePermissionStatementsInput, ...func(*Options)) (*ListResourcePermissionStatementsOutput, error) +} + +var _ ListResourcePermissionStatementsAPIClient = (*Client)(nil) diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/signin/api_op_PutConsoleAuthorizationConfiguration.go b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/api_op_PutConsoleAuthorizationConfiguration.go new file mode 100644 index 0000000000..7f2bc68ada --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/api_op_PutConsoleAuthorizationConfiguration.go @@ -0,0 +1,119 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package signin + +import ( + "context" + "github.com/aws/smithy-go/middleware" + "github.com/aws/smithy-go/ptr" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Enable console authorization configuration with automatic scope detection +func (c *Client) PutConsoleAuthorizationConfiguration(ctx context.Context, params *PutConsoleAuthorizationConfigurationInput, optFns ...func(*Options)) (*PutConsoleAuthorizationConfigurationOutput, error) { + if params == nil { + params = &PutConsoleAuthorizationConfigurationInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "PutConsoleAuthorizationConfiguration", params, optFns, c.addOperationPutConsoleAuthorizationConfigurationMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*PutConsoleAuthorizationConfigurationOutput) + out.ResultMetadata = metadata + return out, nil +} + +// Input for PutConsoleAuthorizationConfiguration operation +type PutConsoleAuthorizationConfigurationInput struct { + + // Target account identifier + TargetId *string + + noSmithyDocumentSerde +} + +func (in *PutConsoleAuthorizationConfigurationInput) bindEndpointParams(p *EndpointParameters) { + + p.IsControlPlane = ptr.Bool(true) +} + +// Output for PutConsoleAuthorizationConfiguration operation +type PutConsoleAuthorizationConfigurationOutput struct { + + // Whether console authorization is enabled + // + // This member is required. + ConsoleAuthorizationEnabled *bool + + // Authorization scope + // + // This member is required. + Scope *string + + // Target account identifier + // + // This member is required. + TargetId *string + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationPutConsoleAuthorizationConfigurationMiddlewares(stack *middleware.Stack, options Options) (err error) { + err = stack.Serialize.Add(&awsRestjson1_serializeOpPutConsoleAuthorizationConfiguration{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpPutConsoleAuthorizationConfiguration{}, middleware.After) + if err != nil { + return err + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "PutConsoleAuthorizationConfiguration"), middleware.Before); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/signin/api_op_PutResourcePermissionStatement.go b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/api_op_PutResourcePermissionStatement.go new file mode 100644 index 0000000000..071e1fb506 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/api_op_PutResourcePermissionStatement.go @@ -0,0 +1,168 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package signin + +import ( + "context" + "fmt" + "github.com/aws/smithy-go/middleware" + "github.com/aws/smithy-go/ptr" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Create a permission statement in the account's SignIn resource-based policy +func (c *Client) PutResourcePermissionStatement(ctx context.Context, params *PutResourcePermissionStatementInput, optFns ...func(*Options)) (*PutResourcePermissionStatementOutput, error) { + if params == nil { + params = &PutResourcePermissionStatementInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "PutResourcePermissionStatement", params, optFns, c.addOperationPutResourcePermissionStatementMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*PutResourcePermissionStatementOutput) + out.ResultMetadata = metadata + return out, nil +} + +// Input for PutResourcePermissionStatement operation +type PutResourcePermissionStatementInput struct { + + // Idempotency token for the request + ClientToken *string + + // Console VPC endpoint identifier + ConsoleSourceVpce *string + + // Principal to exclude from the permission statement + ExcludedPrincipal *string + + // AWS region where the VPC and VPC endpoint reside Required when sourceVpc or + // signinSourceVpce/consoleSourceVpce is provided + RequestedRegion *string + + // SignIn VPC endpoint identifier + SigninSourceVpce *string + + // Source IP address + SourceIp *string + + // VPC identifier to restrict console access + SourceVpc *string + + // Source IP address within VPC + VpcSourceIp *string + + noSmithyDocumentSerde +} + +func (in *PutResourcePermissionStatementInput) bindEndpointParams(p *EndpointParameters) { + + p.IsControlPlane = ptr.Bool(true) +} + +// Output for PutResourcePermissionStatement operation +type PutResourcePermissionStatementOutput struct { + + // Unique identifier for the created permission statement + // + // This member is required. + StatementId *string + + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationPutResourcePermissionStatementMiddlewares(stack *middleware.Stack, options Options) (err error) { + err = stack.Serialize.Add(&awsRestjson1_serializeOpPutResourcePermissionStatement{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpPutResourcePermissionStatement{}, middleware.After) + if err != nil { + return err + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addIdempotencyToken_opPutResourcePermissionStatementMiddleware(stack, options); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "PutResourcePermissionStatement"), middleware.Before); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} + +type idempotencyToken_initializeOpPutResourcePermissionStatement struct { + tokenProvider IdempotencyTokenProvider +} + +func (*idempotencyToken_initializeOpPutResourcePermissionStatement) ID() string { + return "OperationIdempotencyTokenAutoFill" +} + +func (m *idempotencyToken_initializeOpPutResourcePermissionStatement) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + if m.tokenProvider == nil { + return next.HandleInitialize(ctx, in) + } + + input, ok := in.Parameters.(*PutResourcePermissionStatementInput) + if !ok { + return out, metadata, fmt.Errorf("expected middleware input to be of type *PutResourcePermissionStatementInput ") + } + + if input.ClientToken == nil { + t, err := m.tokenProvider.GetIdempotencyToken() + if err != nil { + return out, metadata, err + } + input.ClientToken = &t + } + return next.HandleInitialize(ctx, in) +} +func addIdempotencyToken_opPutResourcePermissionStatementMiddleware(stack *middleware.Stack, cfg Options) error { + return stack.Initialize.Add(&idempotencyToken_initializeOpPutResourcePermissionStatement{tokenProvider: cfg.IdempotencyTokenProvider}, middleware.Before) +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/signin/api_op_RevokeOAuth2TokenWithIAM.go b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/api_op_RevokeOAuth2TokenWithIAM.go new file mode 100644 index 0000000000..3524e1eccd --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/api_op_RevokeOAuth2TokenWithIAM.go @@ -0,0 +1,122 @@ +// Code generated by smithy-go-codegen DO NOT EDIT. + +package signin + +import ( + "context" + "github.com/aws/smithy-go/middleware" + "github.com/aws/smithy-go/ptr" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Grants permission to revoke an OAuth 2.0 refresh token and its associated +// refresh tokens +// +// Revokes a refresh_token issued by AWS Sign-In, invalidating the entire token +// chain so that the refresh_token can no longer be used to mint new access_tokens. +// +// Idempotency: revoking an already-revoked, expired, or otherwise invalid token +// still returns 200 OK with an empty body. Only the refresh_token type is +// accepted. +func (c *Client) RevokeOAuth2TokenWithIAM(ctx context.Context, params *RevokeOAuth2TokenWithIAMInput, optFns ...func(*Options)) (*RevokeOAuth2TokenWithIAMOutput, error) { + if params == nil { + params = &RevokeOAuth2TokenWithIAMInput{} + } + + result, metadata, err := c.invokeOperation(ctx, "RevokeOAuth2TokenWithIAM", params, optFns, c.addOperationRevokeOAuth2TokenWithIAMMiddlewares) + if err != nil { + return nil, err + } + + out := result.(*RevokeOAuth2TokenWithIAMOutput) + out.ResultMetadata = metadata + return out, nil +} + +// Input structure for RevokeOAuth2TokenWithIAM operation +// +// RFC 7009 §2.1 revocation request. Contains the refresh_token to revoke. +type RevokeOAuth2TokenWithIAMInput struct { + + // The refresh_token to revoke. Must be a refresh_token issued by AWS Sign-In + // (prefix "ASOR"); access_tokens are not accepted for revocation. + // + // This member is required. + Token *string + + noSmithyDocumentSerde +} + +func (in *RevokeOAuth2TokenWithIAMInput) bindEndpointParams(p *EndpointParameters) { + + p.IsOAuthEndpoint = ptr.Bool(true) +} + +// Output structure for RevokeOAuth2TokenWithIAM operation +// +// RFC 7009 §2.2 revocation response. The endpoint returns 200 OK with an empty +// body on success; there are no response fields. +type RevokeOAuth2TokenWithIAMOutput struct { + // Metadata pertaining to the operation's result. + ResultMetadata middleware.Metadata + + noSmithyDocumentSerde +} + +func (c *Client) addOperationRevokeOAuth2TokenWithIAMMiddlewares(stack *middleware.Stack, options Options) (err error) { + err = stack.Serialize.Add(&awsRestjson1_serializeOpRevokeOAuth2TokenWithIAM{}, middleware.After) + if err != nil { + return err + } + err = stack.Deserialize.Add(&awsRestjson1_deserializeOpRevokeOAuth2TokenWithIAM{}, middleware.After) + if err != nil { + return err + } + + if err = addlegacyEndpointContextSetter(stack, options); err != nil { + return err + } + if err = addComputeContentLength(stack); err != nil { + return err + } + if err = addResolveEndpointMiddleware(stack, options); err != nil { + return err + } + if err = addComputePayloadSHA256(stack); err != nil { + return err + } + if err = addRecordResponseTiming(stack, options); err != nil { + return err + } + if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { + return err + } + if err = addCredentialSource(stack, options); err != nil { + return err + } + if err = addOpRevokeOAuth2TokenWithIAMValidationMiddleware(stack); err != nil { + return err + } + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "RevokeOAuth2TokenWithIAM"), middleware.Before); err != nil { + return err + } + if err = addRequestIDRetrieverMiddleware(stack); err != nil { + return err + } + if err = addResponseErrorMiddleware(stack); err != nil { + return err + } + if err = addRequestResponseLogging(stack, options); err != nil { + return err + } + if err = addDisableHTTPSMiddleware(stack, options); err != nil { + return err + } + if err = addInterceptors(stack, options); err != nil { + return err + } + return nil +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/signin/auth.go b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/auth.go index cf6b365041..c98762629c 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/signin/auth.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/auth.go @@ -190,7 +190,7 @@ func (m *resolveAuthSchemeMiddleware) selectScheme(options []*smithyauth.Option) } for _, scheme := range m.options.AuthSchemes { - if scheme.SchemeID() != option.SchemeID { + if !matchSchemeID(scheme.SchemeID(), option.SchemeID) { continue } @@ -203,6 +203,16 @@ func (m *resolveAuthSchemeMiddleware) selectScheme(options []*smithyauth.Option) return nil, false } +func matchSchemeID(registered, option string) bool { + if registered == option { + return true + } + if i := strings.LastIndex(registered, "#"); i != -1 { + return registered[i+1:] == option + } + return false +} + func sortAuthOptions(options []*smithyauth.Option, preferred []string) []*smithyauth.Option { byPriority := make([]*smithyauth.Option, 0, len(options)) for _, prefName := range preferred { diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/signin/deserializers.go b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/deserializers.go index b74b612e6b..e78be3229e 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/signin/deserializers.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/deserializers.go @@ -180,12 +180,41 @@ func awsRestjson1_deserializeOpDocumentCreateOAuth2TokenOutput(v **CreateOAuth2T return nil } -func awsRestjson1_deserializeErrorAccessDeniedException(response *smithyhttp.Response, errorBody *bytes.Reader) error { - output := &types.AccessDeniedException{} +type awsRestjson1_deserializeOpCreateOAuth2TokenWithIAM struct { +} + +func (*awsRestjson1_deserializeOpCreateOAuth2TokenWithIAM) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpCreateOAuth2TokenWithIAM) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorCreateOAuth2TokenWithIAM(response, &metadata) + } + output := &CreateOAuth2TokenWithIAMOutput{} + out.Result = output + var buff [1024]byte ringBuffer := smithyio.NewRingBuffer(buff[:]) - body := io.TeeReader(errorBody, ringBuffer) + body := io.TeeReader(response.Body, ringBuffer) + decoder := json.NewDecoder(body) decoder.UseNumber() var shape interface{} @@ -196,36 +225,46 @@ func awsRestjson1_deserializeErrorAccessDeniedException(response *smithyhttp.Res Err: fmt.Errorf("failed to decode response body, %w", err), Snapshot: snapshot.Bytes(), } - return err + return out, metadata, err } - err := awsRestjson1_deserializeDocumentAccessDeniedException(&output, shape) - + err = awsRestjson1_deserializeOpDocumentCreateOAuth2TokenWithIAMOutput(&output, shape) if err != nil { var snapshot bytes.Buffer io.Copy(&snapshot, ringBuffer) - err = &smithy.DeserializationError{ - Err: fmt.Errorf("failed to decode response body, %w", err), + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), Snapshot: snapshot.Bytes(), } - return err } - errorBody.Seek(0, io.SeekStart) - - return output + span.End() + return out, metadata, err } -func awsRestjson1_deserializeErrorInternalServerException(response *smithyhttp.Response, errorBody *bytes.Reader) error { - output := &types.InternalServerException{} +func awsRestjson1_deserializeOpErrorCreateOAuth2TokenWithIAM(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + var buff [1024]byte ringBuffer := smithyio.NewRingBuffer(buff[:]) body := io.TeeReader(errorBody, ringBuffer) decoder := json.NewDecoder(body) decoder.UseNumber() - var shape interface{} - if err := decoder.Decode(&shape); err != nil && err != io.EOF { + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { var snapshot bytes.Buffer io.Copy(&snapshot, ringBuffer) err = &smithy.DeserializationError{ @@ -235,29 +274,134 @@ func awsRestjson1_deserializeErrorInternalServerException(response *smithyhttp.R return err } - err := awsRestjson1_deserializeDocumentInternalServerException(&output, shape) + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } - if err != nil { - var snapshot bytes.Buffer - io.Copy(&snapshot, ringBuffer) - err = &smithy.DeserializationError{ - Err: fmt.Errorf("failed to decode response body, %w", err), - Snapshot: snapshot.Bytes(), + switch { + case strings.EqualFold("AccessDeniedException", errorCode): + return awsRestjson1_deserializeErrorAccessDeniedException(response, errorBody) + + case strings.EqualFold("InternalServerException", errorCode): + return awsRestjson1_deserializeErrorInternalServerException(response, errorBody) + + case strings.EqualFold("TooManyRequestsError", errorCode): + return awsRestjson1_deserializeErrorTooManyRequestsError(response, errorBody) + + case strings.EqualFold("ValidationException", errorCode): + return awsRestjson1_deserializeErrorValidationException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, } - return err + return genericError + } +} - errorBody.Seek(0, io.SeekStart) +func awsRestjson1_deserializeOpDocumentCreateOAuth2TokenWithIAMOutput(v **CreateOAuth2TokenWithIAMOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } - return output + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *CreateOAuth2TokenWithIAMOutput + if *v == nil { + sv = &CreateOAuth2TokenWithIAMOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "access_token": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected OAuthAccessToken to be of type string, got %T instead", value) + } + sv.AccessToken = ptr.String(jtv) + } + + case "expires_in": + if value != nil { + jtv, ok := value.(json.Number) + if !ok { + return fmt.Errorf("expected TokenExpiresIn to be json.Number, got %T instead", value) + } + i64, err := jtv.Int64() + if err != nil { + return err + } + sv.ExpiresIn = ptr.Int32(int32(i64)) + } + + case "token_type": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected BearerTokenType to be of type string, got %T instead", value) + } + sv.TokenType = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil } -func awsRestjson1_deserializeErrorTooManyRequestsError(response *smithyhttp.Response, errorBody *bytes.Reader) error { - output := &types.TooManyRequestsError{} +type awsRestjson1_deserializeOpDeleteConsoleAuthorizationConfiguration struct { +} + +func (*awsRestjson1_deserializeOpDeleteConsoleAuthorizationConfiguration) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpDeleteConsoleAuthorizationConfiguration) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorDeleteConsoleAuthorizationConfiguration(response, &metadata) + } + output := &DeleteConsoleAuthorizationConfigurationOutput{} + out.Result = output + var buff [1024]byte ringBuffer := smithyio.NewRingBuffer(buff[:]) - body := io.TeeReader(errorBody, ringBuffer) + body := io.TeeReader(response.Body, ringBuffer) + decoder := json.NewDecoder(body) decoder.UseNumber() var shape interface{} @@ -268,36 +412,46 @@ func awsRestjson1_deserializeErrorTooManyRequestsError(response *smithyhttp.Resp Err: fmt.Errorf("failed to decode response body, %w", err), Snapshot: snapshot.Bytes(), } - return err + return out, metadata, err } - err := awsRestjson1_deserializeDocumentTooManyRequestsError(&output, shape) - + err = awsRestjson1_deserializeOpDocumentDeleteConsoleAuthorizationConfigurationOutput(&output, shape) if err != nil { var snapshot bytes.Buffer io.Copy(&snapshot, ringBuffer) - err = &smithy.DeserializationError{ - Err: fmt.Errorf("failed to decode response body, %w", err), + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), Snapshot: snapshot.Bytes(), } - return err } - errorBody.Seek(0, io.SeekStart) - - return output + span.End() + return out, metadata, err } -func awsRestjson1_deserializeErrorValidationException(response *smithyhttp.Response, errorBody *bytes.Reader) error { - output := &types.ValidationException{} +func awsRestjson1_deserializeOpErrorDeleteConsoleAuthorizationConfiguration(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + var buff [1024]byte ringBuffer := smithyio.NewRingBuffer(buff[:]) body := io.TeeReader(errorBody, ringBuffer) decoder := json.NewDecoder(body) decoder.UseNumber() - var shape interface{} - if err := decoder.Decode(&shape); err != nil && err != io.EOF { + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { var snapshot bytes.Buffer io.Copy(&snapshot, ringBuffer) err = &smithy.DeserializationError{ @@ -307,24 +461,2058 @@ func awsRestjson1_deserializeErrorValidationException(response *smithyhttp.Respo return err } - err := awsRestjson1_deserializeDocumentValidationException(&output, shape) + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("AccessDeniedException", errorCode): + return awsRestjson1_deserializeErrorAccessDeniedException(response, errorBody) + + case strings.EqualFold("InternalServerException", errorCode): + return awsRestjson1_deserializeErrorInternalServerException(response, errorBody) + + case strings.EqualFold("ResourceNotFoundException", errorCode): + return awsRestjson1_deserializeErrorResourceNotFoundException(response, errorBody) + + case strings.EqualFold("TooManyRequestsError", errorCode): + return awsRestjson1_deserializeErrorTooManyRequestsError(response, errorBody) + + case strings.EqualFold("ValidationException", errorCode): + return awsRestjson1_deserializeErrorValidationException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentDeleteConsoleAuthorizationConfigurationOutput(v **DeleteConsoleAuthorizationConfigurationOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *DeleteConsoleAuthorizationConfigurationOutput + if *v == nil { + sv = &DeleteConsoleAuthorizationConfigurationOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "consoleAuthorizationEnabled": + if value != nil { + jtv, ok := value.(bool) + if !ok { + return fmt.Errorf("expected Boolean to be of type *bool, got %T instead", value) + } + sv.ConsoleAuthorizationEnabled = ptr.Bool(jtv) + } + + case "scope": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected String to be of type string, got %T instead", value) + } + sv.Scope = ptr.String(jtv) + } + + case "targetId": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected TargetId to be of type string, got %T instead", value) + } + sv.TargetId = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpDeleteResourcePermissionStatement struct { +} + +func (*awsRestjson1_deserializeOpDeleteResourcePermissionStatement) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpDeleteResourcePermissionStatement) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorDeleteResourcePermissionStatement(response, &metadata) + } + output := &DeleteResourcePermissionStatementOutput{} + out.Result = output + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorDeleteResourcePermissionStatement(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("AccessDeniedException", errorCode): + return awsRestjson1_deserializeErrorAccessDeniedException(response, errorBody) + + case strings.EqualFold("InternalServerException", errorCode): + return awsRestjson1_deserializeErrorInternalServerException(response, errorBody) + + case strings.EqualFold("ResourceNotFoundException", errorCode): + return awsRestjson1_deserializeErrorResourceNotFoundException(response, errorBody) + + case strings.EqualFold("TooManyRequestsError", errorCode): + return awsRestjson1_deserializeErrorTooManyRequestsError(response, errorBody) + + case strings.EqualFold("ValidationException", errorCode): + return awsRestjson1_deserializeErrorValidationException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +type awsRestjson1_deserializeOpGetConsoleAuthorizationConfiguration struct { +} + +func (*awsRestjson1_deserializeOpGetConsoleAuthorizationConfiguration) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpGetConsoleAuthorizationConfiguration) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorGetConsoleAuthorizationConfiguration(response, &metadata) + } + output := &GetConsoleAuthorizationConfigurationOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentGetConsoleAuthorizationConfigurationOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorGetConsoleAuthorizationConfiguration(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("AccessDeniedException", errorCode): + return awsRestjson1_deserializeErrorAccessDeniedException(response, errorBody) + + case strings.EqualFold("InternalServerException", errorCode): + return awsRestjson1_deserializeErrorInternalServerException(response, errorBody) + + case strings.EqualFold("ResourceNotFoundException", errorCode): + return awsRestjson1_deserializeErrorResourceNotFoundException(response, errorBody) + + case strings.EqualFold("TooManyRequestsError", errorCode): + return awsRestjson1_deserializeErrorTooManyRequestsError(response, errorBody) + + case strings.EqualFold("ValidationException", errorCode): + return awsRestjson1_deserializeErrorValidationException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentGetConsoleAuthorizationConfigurationOutput(v **GetConsoleAuthorizationConfigurationOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *GetConsoleAuthorizationConfigurationOutput + if *v == nil { + sv = &GetConsoleAuthorizationConfigurationOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "consoleAuthorizationEnabled": + if value != nil { + jtv, ok := value.(bool) + if !ok { + return fmt.Errorf("expected Boolean to be of type *bool, got %T instead", value) + } + sv.ConsoleAuthorizationEnabled = ptr.Bool(jtv) + } + + case "scope": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected String to be of type string, got %T instead", value) + } + sv.Scope = ptr.String(jtv) + } + + case "targetId": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected TargetId to be of type string, got %T instead", value) + } + sv.TargetId = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpGetResourcePolicy struct { +} + +func (*awsRestjson1_deserializeOpGetResourcePolicy) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpGetResourcePolicy) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorGetResourcePolicy(response, &metadata) + } + output := &GetResourcePolicyOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentGetResourcePolicyOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorGetResourcePolicy(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("AccessDeniedException", errorCode): + return awsRestjson1_deserializeErrorAccessDeniedException(response, errorBody) + + case strings.EqualFold("InternalServerException", errorCode): + return awsRestjson1_deserializeErrorInternalServerException(response, errorBody) + + case strings.EqualFold("ResourceNotFoundException", errorCode): + return awsRestjson1_deserializeErrorResourceNotFoundException(response, errorBody) + + case strings.EqualFold("TooManyRequestsError", errorCode): + return awsRestjson1_deserializeErrorTooManyRequestsError(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentGetResourcePolicyOutput(v **GetResourcePolicyOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *GetResourcePolicyOutput + if *v == nil { + sv = &GetResourcePolicyOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "signinResourceBasedPolicy": + if err := awsRestjson1_deserializeDocumentSigninResourceBasedPolicy(&sv.SigninResourceBasedPolicy, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpIntrospectOAuth2TokenWithIAM struct { +} + +func (*awsRestjson1_deserializeOpIntrospectOAuth2TokenWithIAM) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpIntrospectOAuth2TokenWithIAM) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorIntrospectOAuth2TokenWithIAM(response, &metadata) + } + output := &IntrospectOAuth2TokenWithIAMOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentIntrospectOAuth2TokenWithIAMOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorIntrospectOAuth2TokenWithIAM(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("AccessDeniedException", errorCode): + return awsRestjson1_deserializeErrorAccessDeniedException(response, errorBody) + + case strings.EqualFold("InternalServerException", errorCode): + return awsRestjson1_deserializeErrorInternalServerException(response, errorBody) + + case strings.EqualFold("TooManyRequestsError", errorCode): + return awsRestjson1_deserializeErrorTooManyRequestsError(response, errorBody) + + case strings.EqualFold("ValidationException", errorCode): + return awsRestjson1_deserializeErrorValidationException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentIntrospectOAuth2TokenWithIAMOutput(v **IntrospectOAuth2TokenWithIAMOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *IntrospectOAuth2TokenWithIAMOutput + if *v == nil { + sv = &IntrospectOAuth2TokenWithIAMOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "account_id": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected AccountId to be of type string, got %T instead", value) + } + sv.AccountId = ptr.String(jtv) + } + + case "active": + if value != nil { + jtv, ok := value.(bool) + if !ok { + return fmt.Errorf("expected Boolean to be of type *bool, got %T instead", value) + } + sv.Active = ptr.Bool(jtv) + } + + case "aud": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected String to be of type string, got %T instead", value) + } + sv.Aud = ptr.String(jtv) + } + + case "client_id": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected String to be of type string, got %T instead", value) + } + sv.ClientId = ptr.String(jtv) + } + + case "exp": + if value != nil { + jtv, ok := value.(json.Number) + if !ok { + return fmt.Errorf("expected Long to be json.Number, got %T instead", value) + } + i64, err := jtv.Int64() + if err != nil { + return err + } + sv.Exp = ptr.Int64(i64) + } + + case "iat": + if value != nil { + jtv, ok := value.(json.Number) + if !ok { + return fmt.Errorf("expected Long to be json.Number, got %T instead", value) + } + i64, err := jtv.Int64() + if err != nil { + return err + } + sv.Iat = ptr.Int64(i64) + } + + case "iss": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected String to be of type string, got %T instead", value) + } + sv.Iss = ptr.String(jtv) + } + + case "jti": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected String to be of type string, got %T instead", value) + } + sv.Jti = ptr.String(jtv) + } + + case "nbf": + if value != nil { + jtv, ok := value.(json.Number) + if !ok { + return fmt.Errorf("expected Long to be json.Number, got %T instead", value) + } + i64, err := jtv.Int64() + if err != nil { + return err + } + sv.Nbf = ptr.Int64(i64) + } + + case "resource": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected String to be of type string, got %T instead", value) + } + sv.Resource = ptr.String(jtv) + } + + case "signin_session": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected String to be of type string, got %T instead", value) + } + sv.SigninSession = ptr.String(jtv) + } + + case "sub": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected String to be of type string, got %T instead", value) + } + sv.Sub = ptr.String(jtv) + } + + case "token_type": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected IntrospectedTokenType to be of type string, got %T instead", value) + } + sv.TokenType = ptr.String(jtv) + } + + case "user_id": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected String to be of type string, got %T instead", value) + } + sv.UserId = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpListResourcePermissionStatements struct { +} + +func (*awsRestjson1_deserializeOpListResourcePermissionStatements) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpListResourcePermissionStatements) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorListResourcePermissionStatements(response, &metadata) + } + output := &ListResourcePermissionStatementsOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentListResourcePermissionStatementsOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorListResourcePermissionStatements(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("AccessDeniedException", errorCode): + return awsRestjson1_deserializeErrorAccessDeniedException(response, errorBody) + + case strings.EqualFold("InternalServerException", errorCode): + return awsRestjson1_deserializeErrorInternalServerException(response, errorBody) + + case strings.EqualFold("ResourceNotFoundException", errorCode): + return awsRestjson1_deserializeErrorResourceNotFoundException(response, errorBody) + + case strings.EqualFold("TooManyRequestsError", errorCode): + return awsRestjson1_deserializeErrorTooManyRequestsError(response, errorBody) + + case strings.EqualFold("ValidationException", errorCode): + return awsRestjson1_deserializeErrorValidationException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentListResourcePermissionStatementsOutput(v **ListResourcePermissionStatementsOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *ListResourcePermissionStatementsOutput + if *v == nil { + sv = &ListResourcePermissionStatementsOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "nextToken": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected NextToken to be of type string, got %T instead", value) + } + sv.NextToken = ptr.String(jtv) + } + + case "permissionStatements": + if err := awsRestjson1_deserializeDocumentPermissionStatementSummaries(&sv.PermissionStatements, value); err != nil { + return err + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpPutConsoleAuthorizationConfiguration struct { +} + +func (*awsRestjson1_deserializeOpPutConsoleAuthorizationConfiguration) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpPutConsoleAuthorizationConfiguration) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorPutConsoleAuthorizationConfiguration(response, &metadata) + } + output := &PutConsoleAuthorizationConfigurationOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentPutConsoleAuthorizationConfigurationOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorPutConsoleAuthorizationConfiguration(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("AccessDeniedException", errorCode): + return awsRestjson1_deserializeErrorAccessDeniedException(response, errorBody) + + case strings.EqualFold("ConflictException", errorCode): + return awsRestjson1_deserializeErrorConflictException(response, errorBody) + + case strings.EqualFold("InternalServerException", errorCode): + return awsRestjson1_deserializeErrorInternalServerException(response, errorBody) + + case strings.EqualFold("ResourceNotFoundException", errorCode): + return awsRestjson1_deserializeErrorResourceNotFoundException(response, errorBody) + + case strings.EqualFold("TooManyRequestsError", errorCode): + return awsRestjson1_deserializeErrorTooManyRequestsError(response, errorBody) + + case strings.EqualFold("ValidationException", errorCode): + return awsRestjson1_deserializeErrorValidationException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentPutConsoleAuthorizationConfigurationOutput(v **PutConsoleAuthorizationConfigurationOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *PutConsoleAuthorizationConfigurationOutput + if *v == nil { + sv = &PutConsoleAuthorizationConfigurationOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "consoleAuthorizationEnabled": + if value != nil { + jtv, ok := value.(bool) + if !ok { + return fmt.Errorf("expected Boolean to be of type *bool, got %T instead", value) + } + sv.ConsoleAuthorizationEnabled = ptr.Bool(jtv) + } + + case "scope": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected String to be of type string, got %T instead", value) + } + sv.Scope = ptr.String(jtv) + } + + case "targetId": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected TargetId to be of type string, got %T instead", value) + } + sv.TargetId = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpPutResourcePermissionStatement struct { +} + +func (*awsRestjson1_deserializeOpPutResourcePermissionStatement) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpPutResourcePermissionStatement) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorPutResourcePermissionStatement(response, &metadata) + } + output := &PutResourcePermissionStatementOutput{} + out.Result = output + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(response.Body, ringBuffer) + + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return out, metadata, err + } + + err = awsRestjson1_deserializeOpDocumentPutResourcePermissionStatementOutput(&output, shape) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return out, metadata, &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body with invalid JSON, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorPutResourcePermissionStatement(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("AccessDeniedException", errorCode): + return awsRestjson1_deserializeErrorAccessDeniedException(response, errorBody) + + case strings.EqualFold("ConflictException", errorCode): + return awsRestjson1_deserializeErrorConflictException(response, errorBody) + + case strings.EqualFold("InternalServerException", errorCode): + return awsRestjson1_deserializeErrorInternalServerException(response, errorBody) + + case strings.EqualFold("ServiceQuotaExceededException", errorCode): + return awsRestjson1_deserializeErrorServiceQuotaExceededException(response, errorBody) + + case strings.EqualFold("TooManyRequestsError", errorCode): + return awsRestjson1_deserializeErrorTooManyRequestsError(response, errorBody) + + case strings.EqualFold("ValidationException", errorCode): + return awsRestjson1_deserializeErrorValidationException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeOpDocumentPutResourcePermissionStatementOutput(v **PutResourcePermissionStatementOutput, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *PutResourcePermissionStatementOutput + if *v == nil { + sv = &PutResourcePermissionStatementOutput{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "statementId": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected StatementId to be of type string, got %T instead", value) + } + sv.StatementId = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +type awsRestjson1_deserializeOpRevokeOAuth2TokenWithIAM struct { +} + +func (*awsRestjson1_deserializeOpRevokeOAuth2TokenWithIAM) ID() string { + return "OperationDeserializer" +} + +func (m *awsRestjson1_deserializeOpRevokeOAuth2TokenWithIAM) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) ( + out middleware.DeserializeOutput, metadata middleware.Metadata, err error, +) { + out, metadata, err = next.HandleDeserialize(ctx, in) + if err != nil { + return out, metadata, err + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + defer endTimer() + defer span.End() + response, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + if response.StatusCode < 200 || response.StatusCode >= 300 { + return out, metadata, awsRestjson1_deserializeOpErrorRevokeOAuth2TokenWithIAM(response, &metadata) + } + output := &RevokeOAuth2TokenWithIAMOutput{} + out.Result = output + + span.End() + return out, metadata, err +} + +func awsRestjson1_deserializeOpErrorRevokeOAuth2TokenWithIAM(response *smithyhttp.Response, metadata *middleware.Metadata) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + if len(headerCode) != 0 { + errorCode = restjson.SanitizeErrorCode(headerCode) + } + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + jsonCode, message, err := restjson.GetErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if len(headerCode) == 0 && len(jsonCode) != 0 { + errorCode = restjson.SanitizeErrorCode(jsonCode) + } + if len(message) != 0 { + errorMessage = message + } + + switch { + case strings.EqualFold("AccessDeniedException", errorCode): + return awsRestjson1_deserializeErrorAccessDeniedException(response, errorBody) + + case strings.EqualFold("InternalServerException", errorCode): + return awsRestjson1_deserializeErrorInternalServerException(response, errorBody) + + case strings.EqualFold("TooManyRequestsError", errorCode): + return awsRestjson1_deserializeErrorTooManyRequestsError(response, errorBody) + + case strings.EqualFold("ValidationException", errorCode): + return awsRestjson1_deserializeErrorValidationException(response, errorBody) + + default: + genericError := &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + return genericError + + } +} + +func awsRestjson1_deserializeErrorAccessDeniedException(response *smithyhttp.Response, errorBody *bytes.Reader) error { + output := &types.AccessDeniedException{} + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + err := awsRestjson1_deserializeDocumentAccessDeniedException(&output, shape) + + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + + return output +} + +func awsRestjson1_deserializeErrorConflictException(response *smithyhttp.Response, errorBody *bytes.Reader) error { + output := &types.ConflictException{} + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + err := awsRestjson1_deserializeDocumentConflictException(&output, shape) + + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + + return output +} + +func awsRestjson1_deserializeErrorInternalServerException(response *smithyhttp.Response, errorBody *bytes.Reader) error { + output := &types.InternalServerException{} + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + err := awsRestjson1_deserializeDocumentInternalServerException(&output, shape) + + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + + return output +} + +func awsRestjson1_deserializeErrorResourceNotFoundException(response *smithyhttp.Response, errorBody *bytes.Reader) error { + output := &types.ResourceNotFoundException{} + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + err := awsRestjson1_deserializeDocumentResourceNotFoundException(&output, shape) + + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + + return output +} + +func awsRestjson1_deserializeErrorServiceQuotaExceededException(response *smithyhttp.Response, errorBody *bytes.Reader) error { + output := &types.ServiceQuotaExceededException{} + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + err := awsRestjson1_deserializeDocumentServiceQuotaExceededException(&output, shape) + + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + + return output +} + +func awsRestjson1_deserializeErrorTooManyRequestsError(response *smithyhttp.Response, errorBody *bytes.Reader) error { + output := &types.TooManyRequestsError{} + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + err := awsRestjson1_deserializeDocumentTooManyRequestsError(&output, shape) + + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + + return output +} + +func awsRestjson1_deserializeErrorValidationException(response *smithyhttp.Response, errorBody *bytes.Reader) error { + output := &types.ValidationException{} + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + err := awsRestjson1_deserializeDocumentValidationException(&output, shape) + + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + + return output +} + +func awsRestjson1_deserializeDocumentAccessDeniedException(v **types.AccessDeniedException, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.AccessDeniedException + if *v == nil { + sv = &types.AccessDeniedException{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "error": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected OAuth2ErrorCode to be of type string, got %T instead", value) + } + sv.Error_ = types.OAuth2ErrorCode(jtv) + } + + case "message", "Message": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected String to be of type string, got %T instead", value) + } + sv.Message = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentAccessToken(v **types.AccessToken, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.AccessToken + if *v == nil { + sv = &types.AccessToken{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "accessKeyId": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected String to be of type string, got %T instead", value) + } + sv.AccessKeyId = ptr.String(jtv) + } + + case "secretAccessKey": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected String to be of type string, got %T instead", value) + } + sv.SecretAccessKey = ptr.String(jtv) + } + + case "sessionToken": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected String to be of type string, got %T instead", value) + } + sv.SessionToken = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentCondition(v *map[string][]string, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var mv map[string][]string + if *v == nil { + mv = map[string][]string{} + } else { + mv = *v + } + + for key, value := range shape { + var parsedVal []string + mapVar := parsedVal + if err := awsRestjson1_deserializeDocumentConditionValues(&mapVar, value); err != nil { + return err + } + parsedVal = mapVar + mv[key] = parsedVal + + } + *v = mv + return nil +} + +func awsRestjson1_deserializeDocumentConditionBlock(v *map[string]map[string][]string, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var mv map[string]map[string][]string + if *v == nil { + mv = map[string]map[string][]string{} + } else { + mv = *v + } + + for key, value := range shape { + var parsedVal map[string][]string + mapVar := parsedVal + if err := awsRestjson1_deserializeDocumentCondition(&mapVar, value); err != nil { + return err + } + parsedVal = mapVar + mv[key] = parsedVal + + } + *v = mv + return nil +} + +func awsRestjson1_deserializeDocumentConditionValues(v *[]string, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.([]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var cv []string + if *v == nil { + cv = []string{} + } else { + cv = *v + } + + for _, value := range shape { + var col string + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected String to be of type string, got %T instead", value) + } + col = jtv + } + cv = append(cv, col) + + } + *v = cv + return nil +} + +func awsRestjson1_deserializeDocumentConflictException(v **types.ConflictException, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.ConflictException + if *v == nil { + sv = &types.ConflictException{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "error": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected OAuth2ErrorCode to be of type string, got %T instead", value) + } + sv.Error_ = types.OAuth2ErrorCode(jtv) + } + + case "message", "Message": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected String to be of type string, got %T instead", value) + } + sv.Message = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentCreateOAuth2TokenResponseBody(v **types.CreateOAuth2TokenResponseBody, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.CreateOAuth2TokenResponseBody + if *v == nil { + sv = &types.CreateOAuth2TokenResponseBody{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "accessToken": + if err := awsRestjson1_deserializeDocumentAccessToken(&sv.AccessToken, value); err != nil { + return err + } + + case "expiresIn": + if value != nil { + jtv, ok := value.(json.Number) + if !ok { + return fmt.Errorf("expected ExpiresIn to be json.Number, got %T instead", value) + } + i64, err := jtv.Int64() + if err != nil { + return err + } + sv.ExpiresIn = ptr.Int32(int32(i64)) + } + + case "idToken": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected IdToken to be of type string, got %T instead", value) + } + sv.IdToken = ptr.String(jtv) + } + + case "refreshToken": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected RefreshToken to be of type string, got %T instead", value) + } + sv.RefreshToken = ptr.String(jtv) + } + + case "tokenType": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected TokenType to be of type string, got %T instead", value) + } + sv.TokenType = ptr.String(jtv) + } + + default: + _, _ = key, value - if err != nil { - var snapshot bytes.Buffer - io.Copy(&snapshot, ringBuffer) - err = &smithy.DeserializationError{ - Err: fmt.Errorf("failed to decode response body, %w", err), - Snapshot: snapshot.Bytes(), } - return err } - - errorBody.Seek(0, io.SeekStart) - - return output + *v = sv + return nil } -func awsRestjson1_deserializeDocumentAccessDeniedException(v **types.AccessDeniedException, value interface{}) error { +func awsRestjson1_deserializeDocumentInternalServerException(v **types.InternalServerException, value interface{}) error { if v == nil { return fmt.Errorf("unexpected nil of type %T", v) } @@ -337,9 +2525,9 @@ func awsRestjson1_deserializeDocumentAccessDeniedException(v **types.AccessDenie return fmt.Errorf("unexpected JSON type %v", value) } - var sv *types.AccessDeniedException + var sv *types.InternalServerException if *v == nil { - sv = &types.AccessDeniedException{} + sv = &types.InternalServerException{} } else { sv = *v } @@ -373,7 +2561,41 @@ func awsRestjson1_deserializeDocumentAccessDeniedException(v **types.AccessDenie return nil } -func awsRestjson1_deserializeDocumentAccessToken(v **types.AccessToken, value interface{}) error { +func awsRestjson1_deserializeDocumentPermissionStatementSummaries(v *[]types.PermissionStatementSummary, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.([]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var cv []types.PermissionStatementSummary + if *v == nil { + cv = []types.PermissionStatementSummary{} + } else { + cv = *v + } + + for _, value := range shape { + var col types.PermissionStatementSummary + destAddr := &col + if err := awsRestjson1_deserializeDocumentPermissionStatementSummary(&destAddr, value); err != nil { + return err + } + col = *destAddr + cv = append(cv, col) + + } + *v = cv + return nil +} + +func awsRestjson1_deserializeDocumentPermissionStatementSummary(v **types.PermissionStatementSummary, value interface{}) error { if v == nil { return fmt.Errorf("unexpected nil of type %T", v) } @@ -386,40 +2608,27 @@ func awsRestjson1_deserializeDocumentAccessToken(v **types.AccessToken, value in return fmt.Errorf("unexpected JSON type %v", value) } - var sv *types.AccessToken + var sv *types.PermissionStatementSummary if *v == nil { - sv = &types.AccessToken{} + sv = &types.PermissionStatementSummary{} } else { sv = *v } for key, value := range shape { switch key { - case "accessKeyId": - if value != nil { - jtv, ok := value.(string) - if !ok { - return fmt.Errorf("expected String to be of type string, got %T instead", value) - } - sv.AccessKeyId = ptr.String(jtv) - } - - case "secretAccessKey": - if value != nil { - jtv, ok := value.(string) - if !ok { - return fmt.Errorf("expected String to be of type string, got %T instead", value) - } - sv.SecretAccessKey = ptr.String(jtv) + case "condition": + if err := awsRestjson1_deserializeDocumentConditionBlock(&sv.Condition, value); err != nil { + return err } - case "sessionToken": + case "sid": if value != nil { jtv, ok := value.(string) if !ok { - return fmt.Errorf("expected String to be of type string, got %T instead", value) + return fmt.Errorf("expected StatementId to be of type string, got %T instead", value) } - sv.SessionToken = ptr.String(jtv) + sv.Sid = ptr.String(jtv) } default: @@ -431,7 +2640,43 @@ func awsRestjson1_deserializeDocumentAccessToken(v **types.AccessToken, value in return nil } -func awsRestjson1_deserializeDocumentCreateOAuth2TokenResponseBody(v **types.CreateOAuth2TokenResponseBody, value interface{}) error { +func awsRestjson1_deserializeDocumentPolicyActions(v *[]string, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.([]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var cv []string + if *v == nil { + cv = []string{} + } else { + cv = *v + } + + for _, value := range shape { + var col string + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected String to be of type string, got %T instead", value) + } + col = jtv + } + cv = append(cv, col) + + } + *v = cv + return nil +} + +func awsRestjson1_deserializeDocumentPolicyStatement(v **types.PolicyStatement, value interface{}) error { if v == nil { return fmt.Errorf("unexpected nil of type %T", v) } @@ -444,58 +2689,165 @@ func awsRestjson1_deserializeDocumentCreateOAuth2TokenResponseBody(v **types.Cre return fmt.Errorf("unexpected JSON type %v", value) } - var sv *types.CreateOAuth2TokenResponseBody + var sv *types.PolicyStatement if *v == nil { - sv = &types.CreateOAuth2TokenResponseBody{} + sv = &types.PolicyStatement{} } else { sv = *v } for key, value := range shape { switch key { - case "accessToken": - if err := awsRestjson1_deserializeDocumentAccessToken(&sv.AccessToken, value); err != nil { + case "Action": + if err := awsRestjson1_deserializeDocumentPolicyActions(&sv.Action, value); err != nil { return err } - case "expiresIn": + case "Condition": + if err := awsRestjson1_deserializeDocumentConditionBlock(&sv.Condition, value); err != nil { + return err + } + + case "Effect": if value != nil { - jtv, ok := value.(json.Number) + jtv, ok := value.(string) if !ok { - return fmt.Errorf("expected ExpiresIn to be json.Number, got %T instead", value) - } - i64, err := jtv.Int64() - if err != nil { - return err + return fmt.Errorf("expected String to be of type string, got %T instead", value) } - sv.ExpiresIn = ptr.Int32(int32(i64)) + sv.Effect = ptr.String(jtv) } - case "idToken": + case "Principal": + if err := awsRestjson1_deserializeDocumentPrincipal(&sv.Principal, value); err != nil { + return err + } + + case "Resource": if value != nil { jtv, ok := value.(string) if !ok { - return fmt.Errorf("expected IdToken to be of type string, got %T instead", value) + return fmt.Errorf("expected String to be of type string, got %T instead", value) } - sv.IdToken = ptr.String(jtv) + sv.Resource = ptr.String(jtv) } - case "refreshToken": + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + +func awsRestjson1_deserializeDocumentPolicyStatements(v *[]types.PolicyStatement, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.([]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var cv []types.PolicyStatement + if *v == nil { + cv = []types.PolicyStatement{} + } else { + cv = *v + } + + for _, value := range shape { + var col types.PolicyStatement + destAddr := &col + if err := awsRestjson1_deserializeDocumentPolicyStatement(&destAddr, value); err != nil { + return err + } + col = *destAddr + cv = append(cv, col) + + } + *v = cv + return nil +} + +func awsRestjson1_deserializeDocumentPrincipal(v *map[string]string, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var mv map[string]string + if *v == nil { + mv = map[string]string{} + } else { + mv = *v + } + + for key, value := range shape { + var parsedVal string + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected String to be of type string, got %T instead", value) + } + parsedVal = jtv + } + mv[key] = parsedVal + + } + *v = mv + return nil +} + +func awsRestjson1_deserializeDocumentResourceNotFoundException(v **types.ResourceNotFoundException, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.ResourceNotFoundException + if *v == nil { + sv = &types.ResourceNotFoundException{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "error": if value != nil { jtv, ok := value.(string) if !ok { - return fmt.Errorf("expected RefreshToken to be of type string, got %T instead", value) + return fmt.Errorf("expected OAuth2ErrorCode to be of type string, got %T instead", value) } - sv.RefreshToken = ptr.String(jtv) + sv.Error_ = types.OAuth2ErrorCode(jtv) } - case "tokenType": + case "message", "Message": if value != nil { jtv, ok := value.(string) if !ok { - return fmt.Errorf("expected TokenType to be of type string, got %T instead", value) + return fmt.Errorf("expected String to be of type string, got %T instead", value) } - sv.TokenType = ptr.String(jtv) + sv.Message = ptr.String(jtv) } default: @@ -507,7 +2859,7 @@ func awsRestjson1_deserializeDocumentCreateOAuth2TokenResponseBody(v **types.Cre return nil } -func awsRestjson1_deserializeDocumentInternalServerException(v **types.InternalServerException, value interface{}) error { +func awsRestjson1_deserializeDocumentServiceQuotaExceededException(v **types.ServiceQuotaExceededException, value interface{}) error { if v == nil { return fmt.Errorf("unexpected nil of type %T", v) } @@ -520,9 +2872,9 @@ func awsRestjson1_deserializeDocumentInternalServerException(v **types.InternalS return fmt.Errorf("unexpected JSON type %v", value) } - var sv *types.InternalServerException + var sv *types.ServiceQuotaExceededException if *v == nil { - sv = &types.InternalServerException{} + sv = &types.ServiceQuotaExceededException{} } else { sv = *v } @@ -556,6 +2908,51 @@ func awsRestjson1_deserializeDocumentInternalServerException(v **types.InternalS return nil } +func awsRestjson1_deserializeDocumentSigninResourceBasedPolicy(v **types.SigninResourceBasedPolicy, value interface{}) error { + if v == nil { + return fmt.Errorf("unexpected nil of type %T", v) + } + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + var sv *types.SigninResourceBasedPolicy + if *v == nil { + sv = &types.SigninResourceBasedPolicy{} + } else { + sv = *v + } + + for key, value := range shape { + switch key { + case "Statement": + if err := awsRestjson1_deserializeDocumentPolicyStatements(&sv.Statement, value); err != nil { + return err + } + + case "Version": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected String to be of type string, got %T instead", value) + } + sv.Version = ptr.String(jtv) + } + + default: + _, _ = key, value + + } + } + *v = sv + return nil +} + func awsRestjson1_deserializeDocumentTooManyRequestsError(v **types.TooManyRequestsError, value interface{}) error { if v == nil { return fmt.Errorf("unexpected nil of type %T", v) diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/signin/endpoints.go b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/endpoints.go index db2e6a62a3..52c68b3570 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/signin/endpoints.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/endpoints.go @@ -12,8 +12,10 @@ import ( "github.com/aws/aws-sdk-go-v2/internal/endpoints" "github.com/aws/aws-sdk-go-v2/internal/endpoints/awsrulesfn" internalendpoints "github.com/aws/aws-sdk-go-v2/service/signin/internal/endpoints" + smithy "github.com/aws/smithy-go" smithyauth "github.com/aws/smithy-go/auth" smithyendpoints "github.com/aws/smithy-go/endpoints" + "github.com/aws/smithy-go/endpoints/private/bdd" "github.com/aws/smithy-go/endpoints/private/rulesfn" "github.com/aws/smithy-go/middleware" "github.com/aws/smithy-go/ptr" @@ -229,6 +231,8 @@ func bindRegion(region string) (*string, error) { return aws.String(endpoints.MapFIPSRegion(region)), nil } +var _ = rulesfn.StringSlice(nil) + // EndpointParameters provides the parameters that influence how endpoints are // resolved. type EndpointParameters struct { @@ -266,6 +270,18 @@ type EndpointParameters struct { // // AWS::Region Region *string + + // Indicates if the operation targets the control plane endpoint + // + // Parameter is + // required. + IsControlPlane *bool + + // Indicates if the operation targets the OAuth token endpoint + // + // Parameter is + // required. + IsOAuthEndpoint *bool } // ValidateRequired validates required parameters are set. @@ -294,21 +310,477 @@ func (p EndpointParameters) WithDefaults() EndpointParameters { return p } -type stringSlice []string +const bddRoot int32 = 2 -func (s stringSlice) Get(i int) *string { - if i < 0 || i >= len(s) { - return nil - } +var bddNodes = [120]int32{ + -1, 1, -1, 0, 6, 3, 2, 36, 4, 4, 5, 100000027, 6, 100000004, 100000027, 1, 29, 7, 2, 36, 8, 3, 9, 31, 4, 22, 10, 5, 19, 11, 7, 21, 12, 8, 100000007, 13, 10, 100000008, 14, 12, 100000009, 15, 13, 100000010, 16, 14, 100000011, 17, 15, 100000012, 18, 16, 100000013, 100000016, 6, 100000005, 20, 7, 21, 100000006, 17, 100000024, 100000025, 6, 100000004, 23, 7, 27, 24, 9, 100000014, 25, 10, 100000015, 26, 11, 100000022, 100000023, 11, 28, 100000021, 17, 100000020, 100000021, 2, 35, 30, 3, 39, 31, 4, 32, 100000027, 6, 100000004, 33, 7, 100000027, 34, 9, 100000014, 100000027, 3, 39, 36, 4, 38, 37, 7, 100000018, 100000019, 6, 100000004, 100000017, 5, 100000001, 40, 8, 100000002, 100000003} + +type conditionContext struct { + PartitionResult *awsrulesfn.PartitionConfig +} - v := s[i] - return &v +func evalCondition(idx int, params *EndpointParameters, c *conditionContext) bool { + switch idx { + case 0: + return params.Region != nil + case 1: + return func() bool { + if v := params.IsControlPlane; v != nil { + return *v + } + return false + }() == true + case 2: + return params.Endpoint != nil + case 3: + if v := awsrulesfn.GetPartition(*params.Region); v != nil { + c.PartitionResult = v + return true + } + return false + case 4: + return *params.UseFIPS == true + case 5: + return c.PartitionResult.Name == "aws" + case 6: + return func() bool { + if v := params.IsOAuthEndpoint; v != nil { + return *v + } + return false + }() == true + case 7: + return *params.UseDualStack == true + case 8: + return c.PartitionResult.Name == "aws-cn" + case 9: + return *params.Region == "us-gov-west-1" + case 10: + return c.PartitionResult.Name == "aws-us-gov" + case 11: + return c.PartitionResult.SupportsFIPS == true + case 12: + return c.PartitionResult.Name == "aws-iso" + case 13: + return c.PartitionResult.Name == "aws-iso-b" + case 14: + return c.PartitionResult.Name == "aws-iso-f" + case 15: + return c.PartitionResult.Name == "aws-iso-e" + case 16: + return c.PartitionResult.Name == "aws-eusc" + case 17: + return c.PartitionResult.SupportsDualStack == true + } + return false +} + +func resolveResult(idx int32, params *EndpointParameters, c *conditionContext) (smithyendpoints.Endpoint, error) { + switch idx { + case 0: + return smithyendpoints.Endpoint{}, fmt.Errorf("endpoint resolution failed: no matching rule") + case 1: + uriString := func() string { + var out strings.Builder + out.WriteString("https://signin.") + out.WriteString(*params.Region) + out.WriteString(".api.aws") + return out.String() + }() + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + Properties: func() smithy.Properties { + var out smithy.Properties + smithyauth.SetAuthOptions(&out, []*smithyauth.Option{ + { + SchemeID: "sigv4", + SignerProperties: func() smithy.Properties { + var sp smithy.Properties + smithyhttp.SetSigV4SigningName(&sp, "signin") + smithyhttp.SetSigV4ASigningName(&sp, "signin") + + smithyhttp.SetSigV4SigningRegion(&sp, *params.Region) + return sp + }(), + }, + }) + return out + }(), + }, nil + case 2: + uriString := func() string { + var out strings.Builder + out.WriteString("https://signin.") + out.WriteString(*params.Region) + out.WriteString(".api.amazonwebservices.com.cn") + return out.String() + }() + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + Properties: func() smithy.Properties { + var out smithy.Properties + smithyauth.SetAuthOptions(&out, []*smithyauth.Option{ + { + SchemeID: "sigv4", + SignerProperties: func() smithy.Properties { + var sp smithy.Properties + smithyhttp.SetSigV4SigningName(&sp, "signin") + smithyhttp.SetSigV4ASigningName(&sp, "signin") + + smithyhttp.SetSigV4SigningRegion(&sp, *params.Region) + return sp + }(), + }, + }) + return out + }(), + }, nil + case 3: + uriString := func() string { + var out strings.Builder + out.WriteString("https://signin.") + out.WriteString(*params.Region) + out.WriteString(".") + out.WriteString(c.PartitionResult.DualStackDnsSuffix) + return out.String() + }() + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + Properties: func() smithy.Properties { + var out smithy.Properties + smithyauth.SetAuthOptions(&out, []*smithyauth.Option{ + { + SchemeID: "sigv4", + SignerProperties: func() smithy.Properties { + var sp smithy.Properties + smithyhttp.SetSigV4SigningName(&sp, "signin") + smithyhttp.SetSigV4ASigningName(&sp, "signin") + + smithyhttp.SetSigV4SigningRegion(&sp, *params.Region) + return sp + }(), + }, + }) + return out + }(), + }, nil + case 4: + return smithyendpoints.Endpoint{}, fmt.Errorf("endpoint rule error, %s", "FIPS endpoints are not supported for OAuth operations. Disable FIPS or use a non-OAuth operation.") + case 5: + uriString := func() string { + var out strings.Builder + out.WriteString("https://") + out.WriteString(*params.Region) + out.WriteString(".oauth.signin.aws") + return out.String() + }() + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + Properties: func() smithy.Properties { + var out smithy.Properties + smithyauth.SetAuthOptions(&out, []*smithyauth.Option{ + { + SchemeID: "sigv4", + SignerProperties: func() smithy.Properties { + var sp smithy.Properties + smithyhttp.SetSigV4SigningName(&sp, "signin") + smithyhttp.SetSigV4ASigningName(&sp, "signin") + + smithyhttp.SetSigV4SigningRegion(&sp, *params.Region) + return sp + }(), + }, + }) + return out + }(), + }, nil + case 6: + uriString := func() string { + var out strings.Builder + out.WriteString("https://") + out.WriteString(*params.Region) + out.WriteString(".signin.aws.amazon.com") + return out.String() + }() + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + case 7: + uriString := func() string { + var out strings.Builder + out.WriteString("https://") + out.WriteString(*params.Region) + out.WriteString(".signin.amazonaws.cn") + return out.String() + }() + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + case 8: + uriString := func() string { + var out strings.Builder + out.WriteString("https://") + out.WriteString(*params.Region) + out.WriteString(".signin.amazonaws-us-gov.com") + return out.String() + }() + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + case 9: + uriString := func() string { + var out strings.Builder + out.WriteString("https://") + out.WriteString(*params.Region) + out.WriteString(".signin.c2shome.ic.gov") + return out.String() + }() + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + case 10: + uriString := func() string { + var out strings.Builder + out.WriteString("https://") + out.WriteString(*params.Region) + out.WriteString(".signin.sc2shome.sgov.gov") + return out.String() + }() + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + case 11: + uriString := func() string { + var out strings.Builder + out.WriteString("https://") + out.WriteString(*params.Region) + out.WriteString(".signin.csphome.hci.ic.gov") + return out.String() + }() + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + case 12: + uriString := func() string { + var out strings.Builder + out.WriteString("https://") + out.WriteString(*params.Region) + out.WriteString(".signin.csphome.adc-e.uk") + return out.String() + }() + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + case 13: + uriString := func() string { + var out strings.Builder + out.WriteString("https://") + out.WriteString(*params.Region) + out.WriteString(".signin.amazonaws-eusc.eu") + return out.String() + }() + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + case 14: + uriString := "https://signin-fips.amazonaws-us-gov.com" + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + case 15: + uriString := func() string { + var out strings.Builder + out.WriteString("https://") + out.WriteString(*params.Region) + out.WriteString(".signin-fips.amazonaws-us-gov.com") + return out.String() + }() + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + case 16: + uriString := func() string { + var out strings.Builder + out.WriteString("https://") + out.WriteString(*params.Region) + out.WriteString(".signin.") + out.WriteString(c.PartitionResult.DnsSuffix) + return out.String() + }() + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + case 17: + return smithyendpoints.Endpoint{}, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: FIPS and custom endpoint are not supported") + case 18: + return smithyendpoints.Endpoint{}, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: Dualstack and custom endpoint are not supported") + case 19: + uriString := *params.Endpoint + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + case 20: + uriString := func() string { + var out strings.Builder + out.WriteString("https://signin-fips.") + out.WriteString(*params.Region) + out.WriteString(".") + out.WriteString(c.PartitionResult.DualStackDnsSuffix) + return out.String() + }() + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + case 21: + return smithyendpoints.Endpoint{}, fmt.Errorf("endpoint rule error, %s", "FIPS and DualStack are enabled, but this partition does not support one or both") + case 22: + uriString := func() string { + var out strings.Builder + out.WriteString("https://signin-fips.") + out.WriteString(*params.Region) + out.WriteString(".") + out.WriteString(c.PartitionResult.DnsSuffix) + return out.String() + }() + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + case 23: + return smithyendpoints.Endpoint{}, fmt.Errorf("endpoint rule error, %s", "FIPS is enabled but this partition does not support FIPS") + case 24: + uriString := func() string { + var out strings.Builder + out.WriteString("https://signin.") + out.WriteString(*params.Region) + out.WriteString(".") + out.WriteString(c.PartitionResult.DualStackDnsSuffix) + return out.String() + }() + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + case 25: + return smithyendpoints.Endpoint{}, fmt.Errorf("endpoint rule error, %s", "DualStack is enabled but this partition does not support DualStack") + case 26: + uriString := func() string { + var out strings.Builder + out.WriteString("https://signin.") + out.WriteString(*params.Region) + out.WriteString(".") + out.WriteString(c.PartitionResult.DnsSuffix) + return out.String() + }() + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + case 27: + return smithyendpoints.Endpoint{}, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: Missing Region") + } + return smithyendpoints.Endpoint{}, fmt.Errorf("endpoint rule error, invalid result index: %d", idx) } // EndpointResolverV2 provides the interface for resolving service endpoints. type EndpointResolverV2 interface { - // ResolveEndpoint attempts to resolve the endpoint with the provided options, - // returning the endpoint if found. Otherwise an error is returned. ResolveEndpoint(ctx context.Context, params EndpointParameters) ( smithyendpoints.Endpoint, error, ) @@ -332,206 +804,12 @@ func (r *resolver) ResolveEndpoint( if err = params.ValidateRequired(); err != nil { return endpoint, fmt.Errorf("endpoint parameters are not valid, %w", err) } - _UseDualStack := *params.UseDualStack - _ = _UseDualStack - _UseFIPS := *params.UseFIPS - _ = _UseFIPS - if exprVal := params.Endpoint; exprVal != nil { - _Endpoint := *exprVal - _ = _Endpoint - if _UseFIPS == true { - return endpoint, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: FIPS and custom endpoint are not supported") - } - if _UseDualStack == true { - return endpoint, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: Dualstack and custom endpoint are not supported") - } - uriString := _Endpoint - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - }, nil - } - if exprVal := params.Region; exprVal != nil { - _Region := *exprVal - _ = _Region - if exprVal := awsrulesfn.GetPartition(_Region); exprVal != nil { - _PartitionResult := *exprVal - _ = _PartitionResult - if _PartitionResult.Name == "aws" { - if _UseFIPS == false { - if _UseDualStack == false { - uriString := func() string { - var out strings.Builder - out.WriteString("https://") - out.WriteString(_Region) - out.WriteString(".signin.aws.amazon.com") - return out.String() - }() - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - }, nil - } - } - } - if _PartitionResult.Name == "aws-cn" { - if _UseFIPS == false { - if _UseDualStack == false { - uriString := func() string { - var out strings.Builder - out.WriteString("https://") - out.WriteString(_Region) - out.WriteString(".signin.amazonaws.cn") - return out.String() - }() - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - }, nil - } - } - } - if _PartitionResult.Name == "aws-us-gov" { - if _UseFIPS == false { - if _UseDualStack == false { - uriString := func() string { - var out strings.Builder - out.WriteString("https://") - out.WriteString(_Region) - out.WriteString(".signin.amazonaws-us-gov.com") - return out.String() - }() - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - }, nil - } - } - } - if _UseFIPS == true { - if _UseDualStack == true { - if true == _PartitionResult.SupportsFIPS { - if true == _PartitionResult.SupportsDualStack { - uriString := func() string { - var out strings.Builder - out.WriteString("https://signin-fips.") - out.WriteString(_Region) - out.WriteString(".") - out.WriteString(_PartitionResult.DualStackDnsSuffix) - return out.String() - }() - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - }, nil - } - } - return endpoint, fmt.Errorf("endpoint rule error, %s", "FIPS and DualStack are enabled, but this partition does not support one or both") - } - } - if _UseFIPS == true { - if _UseDualStack == false { - if _PartitionResult.SupportsFIPS == true { - uriString := func() string { - var out strings.Builder - out.WriteString("https://signin-fips.") - out.WriteString(_Region) - out.WriteString(".") - out.WriteString(_PartitionResult.DnsSuffix) - return out.String() - }() - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - }, nil - } - return endpoint, fmt.Errorf("endpoint rule error, %s", "FIPS is enabled but this partition does not support FIPS") - } - } - if _UseFIPS == false { - if _UseDualStack == true { - if true == _PartitionResult.SupportsDualStack { - uriString := func() string { - var out strings.Builder - out.WriteString("https://signin.") - out.WriteString(_Region) - out.WriteString(".") - out.WriteString(_PartitionResult.DualStackDnsSuffix) - return out.String() - }() - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - }, nil - } - return endpoint, fmt.Errorf("endpoint rule error, %s", "DualStack is enabled but this partition does not support DualStack") - } - } - uriString := func() string { - var out strings.Builder - out.WriteString("https://signin.") - out.WriteString(_Region) - out.WriteString(".") - out.WriteString(_PartitionResult.DnsSuffix) - return out.String() - }() - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - }, nil - } - return endpoint, fmt.Errorf("Endpoint resolution failed. Invalid operation or environment input.") - } - return endpoint, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: Missing Region") + c := &conditionContext{} + ref := bdd.Evaluate(bddNodes[:], bddRoot, func(idx int) bool { + return evalCondition(idx, ¶ms, c) + }) + return resolveResult(ref, ¶ms, c) } type endpointParamsBinder interface { diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/signin/generated.json b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/generated.json index 6043ab63f2..762cc709c9 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/signin/generated.json +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/generated.json @@ -9,6 +9,16 @@ "api_client.go", "api_client_test.go", "api_op_CreateOAuth2Token.go", + "api_op_CreateOAuth2TokenWithIAM.go", + "api_op_DeleteConsoleAuthorizationConfiguration.go", + "api_op_DeleteResourcePermissionStatement.go", + "api_op_GetConsoleAuthorizationConfiguration.go", + "api_op_GetResourcePolicy.go", + "api_op_IntrospectOAuth2TokenWithIAM.go", + "api_op_ListResourcePermissionStatements.go", + "api_op_PutConsoleAuthorizationConfiguration.go", + "api_op_PutResourcePermissionStatement.go", + "api_op_RevokeOAuth2TokenWithIAM.go", "auth.go", "deserializers.go", "doc.go", @@ -19,6 +29,8 @@ "internal/endpoints/endpoints.go", "internal/endpoints/endpoints_test.go", "options.go", + "request_snapshot_test.go", + "response_snapshot_test.go", "serializers.go", "snapshot_test.go", "sra_operation_order_test.go", diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/signin/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/go_module_metadata.go index eba7ad7774..d41dad1b6b 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/signin/go_module_metadata.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/go_module_metadata.go @@ -3,4 +3,4 @@ package signin // goModuleVersion is the tagged release for this module -const goModuleVersion = "1.0.11" +const goModuleVersion = "1.5.2" diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/signin/options.go b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/options.go index 88559705f4..9b7248ff30 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/signin/options.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/options.go @@ -44,6 +44,11 @@ type Options struct { // clients initial default settings. DefaultsMode aws.DefaultsMode + // Disables SDK clock skew correction. When set, the SDK will not adjust request + // signing timestamps to compensate for clock drift between the client and the + // service. + DisableClockSkewCorrection bool + // The endpoint options to be used when attempting to resolve an endpoint. EndpointOptions EndpointResolverOptions @@ -64,6 +69,10 @@ type Options struct { // Signature Version 4 (SigV4) Signer HTTPSignerV4 HTTPSignerV4 + // Provides idempotency tokens values that will be automatically populated into + // idempotent API operations. + IdempotencyTokenProvider IdempotencyTokenProvider + // The logger writer interface to write logging messages to. Logger logging.Logger diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/signin/serializers.go b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/serializers.go index 958240275e..d245732ccc 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/signin/serializers.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/serializers.go @@ -97,6 +97,848 @@ func awsRestjson1_serializeOpHttpBindingsCreateOAuth2TokenInput(v *CreateOAuth2T return nil } +type awsRestjson1_serializeOpCreateOAuth2TokenWithIAM struct { +} + +func (*awsRestjson1_serializeOpCreateOAuth2TokenWithIAM) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpCreateOAuth2TokenWithIAM) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*CreateOAuth2TokenWithIAMInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/v1/token?x-amz-client-auth-method=iam") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "POST" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + restEncoder.SetHeader("Content-Type").String("application/json") + + jsonEncoder := smithyjson.NewEncoder() + if err := awsRestjson1_serializeOpDocumentCreateOAuth2TokenWithIAMInput(input, jsonEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(jsonEncoder.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsCreateOAuth2TokenWithIAMInput(v *CreateOAuth2TokenWithIAMInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + return nil +} + +func awsRestjson1_serializeOpDocumentCreateOAuth2TokenWithIAMInput(v *CreateOAuth2TokenWithIAMInput, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.GrantType != nil { + ok := object.Key("grant_type") + ok.String(*v.GrantType) + } + + if v.Resource != nil { + ok := object.Key("resource") + ok.String(*v.Resource) + } + + return nil +} + +type awsRestjson1_serializeOpDeleteConsoleAuthorizationConfiguration struct { +} + +func (*awsRestjson1_serializeOpDeleteConsoleAuthorizationConfiguration) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpDeleteConsoleAuthorizationConfiguration) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*DeleteConsoleAuthorizationConfigurationInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/delete-console-authorization-configuration") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "POST" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + restEncoder.SetHeader("Content-Type").String("application/json") + + jsonEncoder := smithyjson.NewEncoder() + if err := awsRestjson1_serializeOpDocumentDeleteConsoleAuthorizationConfigurationInput(input, jsonEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(jsonEncoder.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsDeleteConsoleAuthorizationConfigurationInput(v *DeleteConsoleAuthorizationConfigurationInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + return nil +} + +func awsRestjson1_serializeOpDocumentDeleteConsoleAuthorizationConfigurationInput(v *DeleteConsoleAuthorizationConfigurationInput, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.TargetId != nil { + ok := object.Key("targetId") + ok.String(*v.TargetId) + } + + return nil +} + +type awsRestjson1_serializeOpDeleteResourcePermissionStatement struct { +} + +func (*awsRestjson1_serializeOpDeleteResourcePermissionStatement) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpDeleteResourcePermissionStatement) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*DeleteResourcePermissionStatementInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/delete-resource-permission-statement") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "POST" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + restEncoder.SetHeader("Content-Type").String("application/json") + + jsonEncoder := smithyjson.NewEncoder() + if err := awsRestjson1_serializeOpDocumentDeleteResourcePermissionStatementInput(input, jsonEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(jsonEncoder.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsDeleteResourcePermissionStatementInput(v *DeleteResourcePermissionStatementInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + return nil +} + +func awsRestjson1_serializeOpDocumentDeleteResourcePermissionStatementInput(v *DeleteResourcePermissionStatementInput, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.ClientToken != nil { + ok := object.Key("clientToken") + ok.String(*v.ClientToken) + } + + if v.StatementId != nil { + ok := object.Key("statementId") + ok.String(*v.StatementId) + } + + return nil +} + +type awsRestjson1_serializeOpGetConsoleAuthorizationConfiguration struct { +} + +func (*awsRestjson1_serializeOpGetConsoleAuthorizationConfiguration) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpGetConsoleAuthorizationConfiguration) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*GetConsoleAuthorizationConfigurationInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/get-console-authorization-configuration") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "POST" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + restEncoder.SetHeader("Content-Type").String("application/json") + + jsonEncoder := smithyjson.NewEncoder() + if err := awsRestjson1_serializeOpDocumentGetConsoleAuthorizationConfigurationInput(input, jsonEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(jsonEncoder.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsGetConsoleAuthorizationConfigurationInput(v *GetConsoleAuthorizationConfigurationInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + return nil +} + +func awsRestjson1_serializeOpDocumentGetConsoleAuthorizationConfigurationInput(v *GetConsoleAuthorizationConfigurationInput, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.TargetId != nil { + ok := object.Key("targetId") + ok.String(*v.TargetId) + } + + return nil +} + +type awsRestjson1_serializeOpGetResourcePolicy struct { +} + +func (*awsRestjson1_serializeOpGetResourcePolicy) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpGetResourcePolicy) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*GetResourcePolicyInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/get-resource-policy") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "POST" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsGetResourcePolicyInput(v *GetResourcePolicyInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + return nil +} + +type awsRestjson1_serializeOpIntrospectOAuth2TokenWithIAM struct { +} + +func (*awsRestjson1_serializeOpIntrospectOAuth2TokenWithIAM) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpIntrospectOAuth2TokenWithIAM) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*IntrospectOAuth2TokenWithIAMInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/v1/introspect?x-amz-client-auth-method=iam") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "POST" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + restEncoder.SetHeader("Content-Type").String("application/json") + + jsonEncoder := smithyjson.NewEncoder() + if err := awsRestjson1_serializeOpDocumentIntrospectOAuth2TokenWithIAMInput(input, jsonEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(jsonEncoder.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsIntrospectOAuth2TokenWithIAMInput(v *IntrospectOAuth2TokenWithIAMInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + return nil +} + +func awsRestjson1_serializeOpDocumentIntrospectOAuth2TokenWithIAMInput(v *IntrospectOAuth2TokenWithIAMInput, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.Token != nil { + ok := object.Key("token") + ok.String(*v.Token) + } + + if v.TokenTypeHint != nil { + ok := object.Key("token_type_hint") + ok.String(*v.TokenTypeHint) + } + + return nil +} + +type awsRestjson1_serializeOpListResourcePermissionStatements struct { +} + +func (*awsRestjson1_serializeOpListResourcePermissionStatements) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpListResourcePermissionStatements) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*ListResourcePermissionStatementsInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/list-resource-permission-statements") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "POST" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + restEncoder.SetHeader("Content-Type").String("application/json") + + jsonEncoder := smithyjson.NewEncoder() + if err := awsRestjson1_serializeOpDocumentListResourcePermissionStatementsInput(input, jsonEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(jsonEncoder.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsListResourcePermissionStatementsInput(v *ListResourcePermissionStatementsInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + return nil +} + +func awsRestjson1_serializeOpDocumentListResourcePermissionStatementsInput(v *ListResourcePermissionStatementsInput, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.MaxResults != nil { + ok := object.Key("maxResults") + ok.Integer(*v.MaxResults) + } + + if v.NextToken != nil { + ok := object.Key("nextToken") + ok.String(*v.NextToken) + } + + return nil +} + +type awsRestjson1_serializeOpPutConsoleAuthorizationConfiguration struct { +} + +func (*awsRestjson1_serializeOpPutConsoleAuthorizationConfiguration) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpPutConsoleAuthorizationConfiguration) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*PutConsoleAuthorizationConfigurationInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/put-console-authorization-configuration") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "POST" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + restEncoder.SetHeader("Content-Type").String("application/json") + + jsonEncoder := smithyjson.NewEncoder() + if err := awsRestjson1_serializeOpDocumentPutConsoleAuthorizationConfigurationInput(input, jsonEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(jsonEncoder.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsPutConsoleAuthorizationConfigurationInput(v *PutConsoleAuthorizationConfigurationInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + return nil +} + +func awsRestjson1_serializeOpDocumentPutConsoleAuthorizationConfigurationInput(v *PutConsoleAuthorizationConfigurationInput, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.TargetId != nil { + ok := object.Key("targetId") + ok.String(*v.TargetId) + } + + return nil +} + +type awsRestjson1_serializeOpPutResourcePermissionStatement struct { +} + +func (*awsRestjson1_serializeOpPutResourcePermissionStatement) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpPutResourcePermissionStatement) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*PutResourcePermissionStatementInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/put-resource-permission-statement") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "POST" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + restEncoder.SetHeader("Content-Type").String("application/json") + + jsonEncoder := smithyjson.NewEncoder() + if err := awsRestjson1_serializeOpDocumentPutResourcePermissionStatementInput(input, jsonEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(jsonEncoder.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsPutResourcePermissionStatementInput(v *PutResourcePermissionStatementInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + return nil +} + +func awsRestjson1_serializeOpDocumentPutResourcePermissionStatementInput(v *PutResourcePermissionStatementInput, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.ClientToken != nil { + ok := object.Key("clientToken") + ok.String(*v.ClientToken) + } + + if v.ConsoleSourceVpce != nil { + ok := object.Key("consoleSourceVpce") + ok.String(*v.ConsoleSourceVpce) + } + + if v.ExcludedPrincipal != nil { + ok := object.Key("excludedPrincipal") + ok.String(*v.ExcludedPrincipal) + } + + if v.RequestedRegion != nil { + ok := object.Key("requestedRegion") + ok.String(*v.RequestedRegion) + } + + if v.SigninSourceVpce != nil { + ok := object.Key("signinSourceVpce") + ok.String(*v.SigninSourceVpce) + } + + if v.SourceIp != nil { + ok := object.Key("sourceIp") + ok.String(*v.SourceIp) + } + + if v.SourceVpc != nil { + ok := object.Key("sourceVpc") + ok.String(*v.SourceVpc) + } + + if v.VpcSourceIp != nil { + ok := object.Key("vpcSourceIp") + ok.String(*v.VpcSourceIp) + } + + return nil +} + +type awsRestjson1_serializeOpRevokeOAuth2TokenWithIAM struct { +} + +func (*awsRestjson1_serializeOpRevokeOAuth2TokenWithIAM) ID() string { + return "OperationSerializer" +} + +func (m *awsRestjson1_serializeOpRevokeOAuth2TokenWithIAM) HandleSerialize(ctx context.Context, in middleware.SerializeInput, next middleware.SerializeHandler) ( + out middleware.SerializeOutput, metadata middleware.Metadata, err error, +) { + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + defer endTimer() + defer span.End() + request, ok := in.Request.(*smithyhttp.Request) + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown transport type %T", in.Request)} + } + + input, ok := in.Parameters.(*RevokeOAuth2TokenWithIAMInput) + _ = input + if !ok { + return out, metadata, &smithy.SerializationError{Err: fmt.Errorf("unknown input parameters type %T", in.Parameters)} + } + + opPath, opQuery := httpbinding.SplitURI("/v1/revoke?x-amz-client-auth-method=iam") + request.URL.Path = smithyhttp.JoinPath(request.URL.Path, opPath) + request.URL.RawQuery = smithyhttp.JoinRawQuery(request.URL.RawQuery, opQuery) + request.Method = "POST" + var restEncoder *httpbinding.Encoder + if request.URL.RawPath == "" { + restEncoder, err = httpbinding.NewEncoder(request.URL.Path, request.URL.RawQuery, request.Header) + } else { + request.URL.RawPath = smithyhttp.JoinPath(request.URL.RawPath, opPath) + restEncoder, err = httpbinding.NewEncoderWithRawPath(request.URL.Path, request.URL.RawPath, request.URL.RawQuery, request.Header) + } + + if err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + restEncoder.SetHeader("Content-Type").String("application/json") + + jsonEncoder := smithyjson.NewEncoder() + if err := awsRestjson1_serializeOpDocumentRevokeOAuth2TokenWithIAMInput(input, jsonEncoder.Value); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request, err = request.SetStream(bytes.NewReader(jsonEncoder.Bytes())); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + + if request.Request, err = restEncoder.Encode(request.Request); err != nil { + return out, metadata, &smithy.SerializationError{Err: err} + } + in.Request = request + + endTimer() + span.End() + return next.HandleSerialize(ctx, in) +} +func awsRestjson1_serializeOpHttpBindingsRevokeOAuth2TokenWithIAMInput(v *RevokeOAuth2TokenWithIAMInput, encoder *httpbinding.Encoder) error { + if v == nil { + return fmt.Errorf("unsupported serialization of nil %T", v) + } + + return nil +} + +func awsRestjson1_serializeOpDocumentRevokeOAuth2TokenWithIAMInput(v *RevokeOAuth2TokenWithIAMInput, value smithyjson.Value) error { + object := value.Object() + defer object.Close() + + if v.Token != nil { + ok := object.Key("token") + ok.String(*v.Token) + } + + return nil +} + func awsRestjson1_serializeDocumentCreateOAuth2TokenRequestBody(v *types.CreateOAuth2TokenRequestBody, value smithyjson.Value) error { object := value.Object() defer object.Close() diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/signin/types/enums.go b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/types/enums.go index ecfabb81f7..2ab46c8f2e 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/signin/types/enums.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/types/enums.go @@ -19,6 +19,12 @@ const ( // The request is missing a required parameter, includes an invalid parameter // value, or is otherwise malformed OAuth2ErrorCodeInvalidRequest OAuth2ErrorCode = "INVALID_REQUEST" + // Requested resource was not found + OAuth2ErrorCodeResourceNotFound OAuth2ErrorCode = "RESOURCE_NOT_FOUND" + // Request conflicts with current state of the resource + OAuth2ErrorCodeConflict OAuth2ErrorCode = "CONFLICT" + // Request would cause a service quota to be exceeded + OAuth2ErrorCodeServiceQuotaExceeded OAuth2ErrorCode = "SERVICE_QUOTA_EXCEEDED" ) // Values returns all known values for OAuth2ErrorCode. Note that this can be @@ -33,5 +39,8 @@ func (OAuth2ErrorCode) Values() []OAuth2ErrorCode { "AUTHCODE_EXPIRED", "server_error", "INVALID_REQUEST", + "RESOURCE_NOT_FOUND", + "CONFLICT", + "SERVICE_QUOTA_EXCEEDED", } } diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/signin/types/errors.go b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/types/errors.go index ca4928a86c..56e1019b07 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/signin/types/errors.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/types/errors.go @@ -44,6 +44,38 @@ func (e *AccessDeniedException) ErrorCode() string { } func (e *AccessDeniedException) ErrorFault() smithy.ErrorFault { return smithy.FaultClient } +// Error thrown when request conflicts with current state +// +// HTTP Status Code: 409 Conflict +// +// Used when the request conflicts with the current state of the resource +type ConflictException struct { + Message *string + + ErrorCodeOverride *string + + Error_ OAuth2ErrorCode + + noSmithyDocumentSerde +} + +func (e *ConflictException) Error() string { + return fmt.Sprintf("%s: %s", e.ErrorCode(), e.ErrorMessage()) +} +func (e *ConflictException) ErrorMessage() string { + if e.Message == nil { + return "" + } + return *e.Message +} +func (e *ConflictException) ErrorCode() string { + if e == nil || e.ErrorCodeOverride == nil { + return "ConflictException" + } + return *e.ErrorCodeOverride +} +func (e *ConflictException) ErrorFault() smithy.ErrorFault { return smithy.FaultClient } + // Error thrown when an internal server error occurs // // HTTP Status Code: 500 Internal Server Error @@ -76,6 +108,70 @@ func (e *InternalServerException) ErrorCode() string { } func (e *InternalServerException) ErrorFault() smithy.ErrorFault { return smithy.FaultServer } +// Error thrown when requested resource is not found +// +// HTTP Status Code: 404 Not Found +// +// Used when the specified resource does not exist +type ResourceNotFoundException struct { + Message *string + + ErrorCodeOverride *string + + Error_ OAuth2ErrorCode + + noSmithyDocumentSerde +} + +func (e *ResourceNotFoundException) Error() string { + return fmt.Sprintf("%s: %s", e.ErrorCode(), e.ErrorMessage()) +} +func (e *ResourceNotFoundException) ErrorMessage() string { + if e.Message == nil { + return "" + } + return *e.Message +} +func (e *ResourceNotFoundException) ErrorCode() string { + if e == nil || e.ErrorCodeOverride == nil { + return "ResourceNotFoundException" + } + return *e.ErrorCodeOverride +} +func (e *ResourceNotFoundException) ErrorFault() smithy.ErrorFault { return smithy.FaultClient } + +// Error thrown when service quota is exceeded +// +// HTTP Status Code: 402 Payment Required (used as quota exceeded indicator) +// +// Used when the request would cause a service quota to be exceeded +type ServiceQuotaExceededException struct { + Message *string + + ErrorCodeOverride *string + + Error_ OAuth2ErrorCode + + noSmithyDocumentSerde +} + +func (e *ServiceQuotaExceededException) Error() string { + return fmt.Sprintf("%s: %s", e.ErrorCode(), e.ErrorMessage()) +} +func (e *ServiceQuotaExceededException) ErrorMessage() string { + if e.Message == nil { + return "" + } + return *e.Message +} +func (e *ServiceQuotaExceededException) ErrorCode() string { + if e == nil || e.ErrorCodeOverride == nil { + return "ServiceQuotaExceededException" + } + return *e.ErrorCodeOverride +} +func (e *ServiceQuotaExceededException) ErrorFault() smithy.ErrorFault { return smithy.FaultClient } + // Error thrown when rate limit is exceeded // // HTTP Status Code: 429 Too Many Requests diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/signin/types/types.go b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/types/types.go index 98afa20bfc..9e8bab7577 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/signin/types/types.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/types/types.go @@ -8,9 +8,7 @@ import ( // AWS credentials structure containing temporary access credentials // -// The scoped-down, 15 minute duration AWS credentials. Scoping down will be based -// on CLI policy (CLI team needs to create it). Similar to cloud shell -// implementation. +// Scoped, temporary AWS credentials with a 15-minute duration. type AccessToken struct { // AWS access key ID for temporary credentials @@ -112,4 +110,51 @@ type CreateOAuth2TokenResponseBody struct { noSmithyDocumentSerde } +// Summary of a permission statement +type PermissionStatementSummary struct { + + // Unique identifier for the permission statement + // + // This member is required. + Sid *string + + // Condition block for the permission statement + Condition map[string]map[string][]string + + noSmithyDocumentSerde +} + +// Individual policy statement within a resource-based policy +type PolicyStatement struct { + + // Actions the statement controls + Action []string + + // Condition block for the statement + Condition map[string]map[string][]string + + // Effect of the policy statement (Allow/Deny) + Effect *string + + // Principal the statement applies to + Principal map[string]string + + // Resource the statement applies to + Resource *string + + noSmithyDocumentSerde +} + +// SignIn resource-based policy document +type SigninResourceBasedPolicy struct { + + // Policy statements + Statement []PolicyStatement + + // Policy version + Version *string + + noSmithyDocumentSerde +} + type noSmithyDocumentSerde = smithydocument.NoSerde diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/signin/validators.go b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/validators.go index f07252341a..8049ca2850 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/signin/validators.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/signin/validators.go @@ -30,10 +30,106 @@ func (m *validateOpCreateOAuth2Token) HandleInitialize(ctx context.Context, in m return next.HandleInitialize(ctx, in) } +type validateOpCreateOAuth2TokenWithIAM struct { +} + +func (*validateOpCreateOAuth2TokenWithIAM) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpCreateOAuth2TokenWithIAM) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*CreateOAuth2TokenWithIAMInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpCreateOAuth2TokenWithIAMInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpDeleteResourcePermissionStatement struct { +} + +func (*validateOpDeleteResourcePermissionStatement) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpDeleteResourcePermissionStatement) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*DeleteResourcePermissionStatementInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpDeleteResourcePermissionStatementInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpIntrospectOAuth2TokenWithIAM struct { +} + +func (*validateOpIntrospectOAuth2TokenWithIAM) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpIntrospectOAuth2TokenWithIAM) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*IntrospectOAuth2TokenWithIAMInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpIntrospectOAuth2TokenWithIAMInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + +type validateOpRevokeOAuth2TokenWithIAM struct { +} + +func (*validateOpRevokeOAuth2TokenWithIAM) ID() string { + return "OperationInputValidation" +} + +func (m *validateOpRevokeOAuth2TokenWithIAM) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) ( + out middleware.InitializeOutput, metadata middleware.Metadata, err error, +) { + input, ok := in.Parameters.(*RevokeOAuth2TokenWithIAMInput) + if !ok { + return out, metadata, fmt.Errorf("unknown input parameters type %T", in.Parameters) + } + if err := validateOpRevokeOAuth2TokenWithIAMInput(input); err != nil { + return out, metadata, err + } + return next.HandleInitialize(ctx, in) +} + func addOpCreateOAuth2TokenValidationMiddleware(stack *middleware.Stack) error { return stack.Initialize.Add(&validateOpCreateOAuth2Token{}, middleware.After) } +func addOpCreateOAuth2TokenWithIAMValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpCreateOAuth2TokenWithIAM{}, middleware.After) +} + +func addOpDeleteResourcePermissionStatementValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpDeleteResourcePermissionStatement{}, middleware.After) +} + +func addOpIntrospectOAuth2TokenWithIAMValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpIntrospectOAuth2TokenWithIAM{}, middleware.After) +} + +func addOpRevokeOAuth2TokenWithIAMValidationMiddleware(stack *middleware.Stack) error { + return stack.Initialize.Add(&validateOpRevokeOAuth2TokenWithIAM{}, middleware.After) +} + func validateCreateOAuth2TokenRequestBody(v *types.CreateOAuth2TokenRequestBody) error { if v == nil { return nil @@ -70,3 +166,66 @@ func validateOpCreateOAuth2TokenInput(v *CreateOAuth2TokenInput) error { return nil } } + +func validateOpCreateOAuth2TokenWithIAMInput(v *CreateOAuth2TokenWithIAMInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "CreateOAuth2TokenWithIAMInput"} + if v.GrantType == nil { + invalidParams.Add(smithy.NewErrParamRequired("GrantType")) + } + if v.Resource == nil { + invalidParams.Add(smithy.NewErrParamRequired("Resource")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpDeleteResourcePermissionStatementInput(v *DeleteResourcePermissionStatementInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "DeleteResourcePermissionStatementInput"} + if v.StatementId == nil { + invalidParams.Add(smithy.NewErrParamRequired("StatementId")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpIntrospectOAuth2TokenWithIAMInput(v *IntrospectOAuth2TokenWithIAMInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "IntrospectOAuth2TokenWithIAMInput"} + if v.Token == nil { + invalidParams.Add(smithy.NewErrParamRequired("Token")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} + +func validateOpRevokeOAuth2TokenWithIAMInput(v *RevokeOAuth2TokenWithIAMInput) error { + if v == nil { + return nil + } + invalidParams := smithy.InvalidParamsError{Context: "RevokeOAuth2TokenWithIAMInput"} + if v.Token == nil { + invalidParams.Add(smithy.NewErrParamRequired("Token")) + } + if invalidParams.Len() > 0 { + return invalidParams + } else { + return nil + } +} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/CHANGELOG.md index b7623b6050..43fee2689c 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/CHANGELOG.md +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/CHANGELOG.md @@ -1,3 +1,61 @@ +# v1.42.2 (2026-07-29) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.42.1 (2026-07-28) + +* **Dependency Update**: Update to smithy-go v1.27.5. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.42.0 (2026-07-21) + +* **Feature**: Add an option to clients to disable clock skew +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.41.1 (2026-07-13) + +* No change notes available for this release. + +# v1.41.0 (2026-07-06) + +* **Feature**: Add request serialization snapshot tests. + +# v1.40.3 (2026-07-01) + +* **Bug Fix**: Bump smithy-go to 1.27.3, fix JSON encorder for document.Number, endpoint host label format validation and CBOR union serialization on new serde +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.40.2 (2026-06-29) + +* No change notes available for this release. + +# v1.40.1 (2026-06-08) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.40.0 (2026-06-04) + +* **Feature**: Adding new BDD representation of endpoint ruleset +* **Dependency Update**: Update to smithy-go v1.27.1 to fix several union-related deserialization bugs in schema-serde-enabled services. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.39.21 (2026-06-03) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.39.20 (2026-06-02) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.39.19 (2026-05-29) + +* **Dependency Update**: Update to smithy-go v1.26.0. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.39.18 (2026-05-28) + +* **Dependency Update**: Updated to the latest SDK module versions + # v1.39.17 (2026-04-29) * **Dependency Update**: Update to smithy-go v1.25.1. diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_client.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_client.go index 1cd8acecce..e24c36846a 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_client.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_client.go @@ -260,6 +260,10 @@ func (c *Client) invokeOperation( finalizeClientEndpointResolverOptions(&options) + if err := c.addCommonMiddlewares(stack, options, opID); err != nil { + return nil, metadata, err + } + for _, fn := range stackFns { if err := fn(stack, options); err != nil { return nil, metadata, err @@ -364,6 +368,49 @@ func addProtocolFinalizerMiddlewares(stack *middleware.Stack, options Options, o } return nil } + +func (c *Client) addCommonMiddlewares(stack *middleware.Stack, options Options, operation string) error { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, operation); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + if err := addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err := addClientRequestID(stack); err != nil { + return err + } + if err := addRetry(stack, options, c); err != nil { + return err + } + if err := addRawResponseToMetadata(stack); err != nil { + return err + } + if err := addSpanRetryLoop(stack, options); err != nil { + return err + } + if err := addClientUserAgent(stack, options); err != nil { + return err + } + if err := addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err := addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err := addRecursionDetection(stack); err != nil { + return err + } + if err := addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err := addInterceptAttempt(stack, options); err != nil { + return err + } + return nil +} func resolveAuthSchemeResolver(options *Options) { if options.AuthSchemeResolver == nil { options.AuthSchemeResolver = &defaultAuthSchemeResolver{} @@ -437,16 +484,17 @@ func setResolvedDefaultsMode(o *Options) { // NewFromConfig returns a new client from the provided config. func NewFromConfig(cfg aws.Config, optFns ...func(*Options)) *Client { opts := Options{ - Region: cfg.Region, - DefaultsMode: cfg.DefaultsMode, - RuntimeEnvironment: cfg.RuntimeEnvironment, - HTTPClient: cfg.HTTPClient, - Credentials: cfg.Credentials, - APIOptions: cfg.APIOptions, - Logger: cfg.Logger, - ClientLogMode: cfg.ClientLogMode, - AppID: cfg.AppID, - AuthSchemePreference: cfg.AuthSchemePreference, + Region: cfg.Region, + DefaultsMode: cfg.DefaultsMode, + RuntimeEnvironment: cfg.RuntimeEnvironment, + HTTPClient: cfg.HTTPClient, + Credentials: cfg.Credentials, + APIOptions: cfg.APIOptions, + Logger: cfg.Logger, + ClientLogMode: cfg.ClientLogMode, + AppID: cfg.AppID, + DisableClockSkewCorrection: cfg.DisableClockSkewCorrection, + AuthSchemePreference: cfg.AuthSchemePreference, } resolveAWSRetryerProvider(cfg, &opts) resolveAWSRetryMaxAttempts(cfg, &opts) @@ -636,15 +684,17 @@ func addClientRequestID(stack *middleware.Stack) error { } func addComputeContentLength(stack *middleware.Stack) error { - return stack.Build.Add(&smithyhttp.ComputeContentLength{}, middleware.After) + return stack.Build.Insert(&smithyhttp.ComputeContentLength{}, "ClientRequestID", middleware.After) } func addRawResponseToMetadata(stack *middleware.Stack) error { return stack.Deserialize.Add(&awsmiddleware.AddRawResponse{}, middleware.Before) } -func addRecordResponseTiming(stack *middleware.Stack) error { - return stack.Deserialize.Add(&awsmiddleware.RecordResponseTiming{}, middleware.After) +func addRecordResponseTiming(stack *middleware.Stack, options Options) error { + return stack.Deserialize.Add(&awsmiddleware.RecordResponseTiming{ + DisableClockSkewCorrection: options.DisableClockSkewCorrection, + }, middleware.After) } func addSpanRetryLoop(stack *middleware.Stack, options Options) error { @@ -715,6 +765,7 @@ func addRetry(stack *middleware.Stack, o Options, c *Client) error { m.LogAttempts = o.ClientLogMode.IsRetries() m.OperationMeter = o.MeterProvider.Meter("github.com/aws/aws-sdk-go-v2/service/sns") m.ClientSkew = c.timeOffset + m.DisableClockSkewCorrection = o.DisableClockSkewCorrection }) if err := stack.Finalize.Insert(attempt, "ResolveAuthScheme", middleware.Before); err != nil { return err @@ -817,6 +868,14 @@ func resolveMeterProvider(options *Options) { } } +func newServiceMetadataMiddleware(region, operation string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: operation, + } +} + func addRecursionDetection(stack *middleware.Stack) error { return stack.Build.Add(&awsmiddleware.RecursionDetection{}, middleware.After) } diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_AddPermission.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_AddPermission.go index 7468af0069..997efd69ac 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_AddPermission.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_AddPermission.go @@ -4,8 +4,6 @@ package sns import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" ) @@ -68,9 +66,6 @@ type AddPermissionOutput struct { } func (c *Client) addOperationAddPermissionMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpAddPermission{}, middleware.After) if err != nil { return err @@ -79,19 +74,10 @@ func (c *Client) addOperationAddPermissionMiddlewares(stack *middleware.Stack, o if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "AddPermission"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -101,19 +87,7 @@ func (c *Client) addOperationAddPermissionMiddlewares(stack *middleware.Stack, o if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -122,22 +96,13 @@ func (c *Client) addOperationAddPermissionMiddlewares(stack *middleware.Stack, o if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpAddPermissionValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opAddPermission(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "AddPermission"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -152,22 +117,8 @@ func (c *Client) addOperationAddPermissionMiddlewares(stack *middleware.Stack, o if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opAddPermission(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "AddPermission", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_CheckIfPhoneNumberIsOptedOut.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_CheckIfPhoneNumberIsOptedOut.go index 65602dc47f..b2651e5148 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_CheckIfPhoneNumberIsOptedOut.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_CheckIfPhoneNumberIsOptedOut.go @@ -4,8 +4,6 @@ package sns import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" ) @@ -61,9 +59,6 @@ type CheckIfPhoneNumberIsOptedOutOutput struct { } func (c *Client) addOperationCheckIfPhoneNumberIsOptedOutMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpCheckIfPhoneNumberIsOptedOut{}, middleware.After) if err != nil { return err @@ -72,19 +67,10 @@ func (c *Client) addOperationCheckIfPhoneNumberIsOptedOutMiddlewares(stack *midd if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "CheckIfPhoneNumberIsOptedOut"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -94,19 +80,7 @@ func (c *Client) addOperationCheckIfPhoneNumberIsOptedOutMiddlewares(stack *midd if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -115,22 +89,13 @@ func (c *Client) addOperationCheckIfPhoneNumberIsOptedOutMiddlewares(stack *midd if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpCheckIfPhoneNumberIsOptedOutValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opCheckIfPhoneNumberIsOptedOut(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "CheckIfPhoneNumberIsOptedOut"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -145,22 +110,8 @@ func (c *Client) addOperationCheckIfPhoneNumberIsOptedOutMiddlewares(stack *midd if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opCheckIfPhoneNumberIsOptedOut(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "CheckIfPhoneNumberIsOptedOut", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_ConfirmSubscription.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_ConfirmSubscription.go index 2c6f999fdf..24e7ba71b7 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_ConfirmSubscription.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_ConfirmSubscription.go @@ -4,8 +4,6 @@ package sns import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" ) @@ -65,9 +63,6 @@ type ConfirmSubscriptionOutput struct { } func (c *Client) addOperationConfirmSubscriptionMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpConfirmSubscription{}, middleware.After) if err != nil { return err @@ -76,19 +71,10 @@ func (c *Client) addOperationConfirmSubscriptionMiddlewares(stack *middleware.St if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "ConfirmSubscription"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -98,19 +84,7 @@ func (c *Client) addOperationConfirmSubscriptionMiddlewares(stack *middleware.St if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -119,22 +93,13 @@ func (c *Client) addOperationConfirmSubscriptionMiddlewares(stack *middleware.St if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpConfirmSubscriptionValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opConfirmSubscription(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "ConfirmSubscription"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -149,22 +114,8 @@ func (c *Client) addOperationConfirmSubscriptionMiddlewares(stack *middleware.St if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opConfirmSubscription(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "ConfirmSubscription", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_CreatePlatformApplication.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_CreatePlatformApplication.go index 8e197226a7..7fa66f9fd7 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_CreatePlatformApplication.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_CreatePlatformApplication.go @@ -4,8 +4,6 @@ package sns import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" ) @@ -103,9 +101,6 @@ type CreatePlatformApplicationOutput struct { } func (c *Client) addOperationCreatePlatformApplicationMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpCreatePlatformApplication{}, middleware.After) if err != nil { return err @@ -114,19 +109,10 @@ func (c *Client) addOperationCreatePlatformApplicationMiddlewares(stack *middlew if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "CreatePlatformApplication"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -136,19 +122,7 @@ func (c *Client) addOperationCreatePlatformApplicationMiddlewares(stack *middlew if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -157,22 +131,13 @@ func (c *Client) addOperationCreatePlatformApplicationMiddlewares(stack *middlew if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpCreatePlatformApplicationValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opCreatePlatformApplication(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "CreatePlatformApplication"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -187,22 +152,8 @@ func (c *Client) addOperationCreatePlatformApplicationMiddlewares(stack *middlew if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opCreatePlatformApplication(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "CreatePlatformApplication", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_CreatePlatformEndpoint.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_CreatePlatformEndpoint.go index 015898cc48..aa635cff50 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_CreatePlatformEndpoint.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_CreatePlatformEndpoint.go @@ -4,8 +4,6 @@ package sns import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" ) @@ -83,9 +81,6 @@ type CreatePlatformEndpointOutput struct { } func (c *Client) addOperationCreatePlatformEndpointMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpCreatePlatformEndpoint{}, middleware.After) if err != nil { return err @@ -94,19 +89,10 @@ func (c *Client) addOperationCreatePlatformEndpointMiddlewares(stack *middleware if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "CreatePlatformEndpoint"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -116,19 +102,7 @@ func (c *Client) addOperationCreatePlatformEndpointMiddlewares(stack *middleware if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -137,22 +111,13 @@ func (c *Client) addOperationCreatePlatformEndpointMiddlewares(stack *middleware if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpCreatePlatformEndpointValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opCreatePlatformEndpoint(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "CreatePlatformEndpoint"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -167,22 +132,8 @@ func (c *Client) addOperationCreatePlatformEndpointMiddlewares(stack *middleware if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opCreatePlatformEndpoint(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "CreatePlatformEndpoint", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_CreateSMSSandboxPhoneNumber.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_CreateSMSSandboxPhoneNumber.go index 51953e6b86..fde7128088 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_CreateSMSSandboxPhoneNumber.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_CreateSMSSandboxPhoneNumber.go @@ -4,8 +4,6 @@ package sns import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/aws-sdk-go-v2/service/sns/types" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" @@ -62,9 +60,6 @@ type CreateSMSSandboxPhoneNumberOutput struct { } func (c *Client) addOperationCreateSMSSandboxPhoneNumberMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpCreateSMSSandboxPhoneNumber{}, middleware.After) if err != nil { return err @@ -73,19 +68,10 @@ func (c *Client) addOperationCreateSMSSandboxPhoneNumberMiddlewares(stack *middl if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "CreateSMSSandboxPhoneNumber"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -95,19 +81,7 @@ func (c *Client) addOperationCreateSMSSandboxPhoneNumberMiddlewares(stack *middl if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -116,22 +90,13 @@ func (c *Client) addOperationCreateSMSSandboxPhoneNumberMiddlewares(stack *middl if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpCreateSMSSandboxPhoneNumberValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opCreateSMSSandboxPhoneNumber(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "CreateSMSSandboxPhoneNumber"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -146,22 +111,8 @@ func (c *Client) addOperationCreateSMSSandboxPhoneNumberMiddlewares(stack *middl if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opCreateSMSSandboxPhoneNumber(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "CreateSMSSandboxPhoneNumber", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_CreateTopic.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_CreateTopic.go index e437db13c2..69867e2f94 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_CreateTopic.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_CreateTopic.go @@ -4,8 +4,6 @@ package sns import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/aws-sdk-go-v2/service/sns/types" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" @@ -218,9 +216,6 @@ type CreateTopicOutput struct { } func (c *Client) addOperationCreateTopicMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpCreateTopic{}, middleware.After) if err != nil { return err @@ -229,19 +224,10 @@ func (c *Client) addOperationCreateTopicMiddlewares(stack *middleware.Stack, opt if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "CreateTopic"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -251,19 +237,7 @@ func (c *Client) addOperationCreateTopicMiddlewares(stack *middleware.Stack, opt if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -272,22 +246,13 @@ func (c *Client) addOperationCreateTopicMiddlewares(stack *middleware.Stack, opt if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpCreateTopicValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opCreateTopic(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "CreateTopic"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -302,22 +267,8 @@ func (c *Client) addOperationCreateTopicMiddlewares(stack *middleware.Stack, opt if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opCreateTopic(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "CreateTopic", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_DeleteEndpoint.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_DeleteEndpoint.go index 6f734a175e..6903d0457a 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_DeleteEndpoint.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_DeleteEndpoint.go @@ -4,8 +4,6 @@ package sns import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" ) @@ -51,9 +49,6 @@ type DeleteEndpointOutput struct { } func (c *Client) addOperationDeleteEndpointMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpDeleteEndpoint{}, middleware.After) if err != nil { return err @@ -62,19 +57,10 @@ func (c *Client) addOperationDeleteEndpointMiddlewares(stack *middleware.Stack, if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "DeleteEndpoint"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -84,19 +70,7 @@ func (c *Client) addOperationDeleteEndpointMiddlewares(stack *middleware.Stack, if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -105,22 +79,13 @@ func (c *Client) addOperationDeleteEndpointMiddlewares(stack *middleware.Stack, if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpDeleteEndpointValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opDeleteEndpoint(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "DeleteEndpoint"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -135,22 +100,8 @@ func (c *Client) addOperationDeleteEndpointMiddlewares(stack *middleware.Stack, if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opDeleteEndpoint(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "DeleteEndpoint", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_DeletePlatformApplication.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_DeletePlatformApplication.go index 391f01b072..b3ce221aa4 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_DeletePlatformApplication.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_DeletePlatformApplication.go @@ -4,8 +4,6 @@ package sns import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" ) @@ -49,9 +47,6 @@ type DeletePlatformApplicationOutput struct { } func (c *Client) addOperationDeletePlatformApplicationMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpDeletePlatformApplication{}, middleware.After) if err != nil { return err @@ -60,19 +55,10 @@ func (c *Client) addOperationDeletePlatformApplicationMiddlewares(stack *middlew if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "DeletePlatformApplication"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -82,19 +68,7 @@ func (c *Client) addOperationDeletePlatformApplicationMiddlewares(stack *middlew if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -103,22 +77,13 @@ func (c *Client) addOperationDeletePlatformApplicationMiddlewares(stack *middlew if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpDeletePlatformApplicationValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opDeletePlatformApplication(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "DeletePlatformApplication"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -133,22 +98,8 @@ func (c *Client) addOperationDeletePlatformApplicationMiddlewares(stack *middlew if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opDeletePlatformApplication(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "DeletePlatformApplication", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_DeleteSMSSandboxPhoneNumber.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_DeleteSMSSandboxPhoneNumber.go index 2e8788814f..d108d4e9a2 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_DeleteSMSSandboxPhoneNumber.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_DeleteSMSSandboxPhoneNumber.go @@ -4,8 +4,6 @@ package sns import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" ) @@ -56,9 +54,6 @@ type DeleteSMSSandboxPhoneNumberOutput struct { } func (c *Client) addOperationDeleteSMSSandboxPhoneNumberMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpDeleteSMSSandboxPhoneNumber{}, middleware.After) if err != nil { return err @@ -67,19 +62,10 @@ func (c *Client) addOperationDeleteSMSSandboxPhoneNumberMiddlewares(stack *middl if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "DeleteSMSSandboxPhoneNumber"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -89,19 +75,7 @@ func (c *Client) addOperationDeleteSMSSandboxPhoneNumberMiddlewares(stack *middl if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -110,22 +84,13 @@ func (c *Client) addOperationDeleteSMSSandboxPhoneNumberMiddlewares(stack *middl if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpDeleteSMSSandboxPhoneNumberValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opDeleteSMSSandboxPhoneNumber(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "DeleteSMSSandboxPhoneNumber"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -140,22 +105,8 @@ func (c *Client) addOperationDeleteSMSSandboxPhoneNumberMiddlewares(stack *middl if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opDeleteSMSSandboxPhoneNumber(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "DeleteSMSSandboxPhoneNumber", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_DeleteTopic.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_DeleteTopic.go index 4e8a4cb8fc..67a5718d30 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_DeleteTopic.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_DeleteTopic.go @@ -4,8 +4,6 @@ package sns import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" ) @@ -47,9 +45,6 @@ type DeleteTopicOutput struct { } func (c *Client) addOperationDeleteTopicMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpDeleteTopic{}, middleware.After) if err != nil { return err @@ -58,19 +53,10 @@ func (c *Client) addOperationDeleteTopicMiddlewares(stack *middleware.Stack, opt if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "DeleteTopic"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -80,19 +66,7 @@ func (c *Client) addOperationDeleteTopicMiddlewares(stack *middleware.Stack, opt if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -101,22 +75,13 @@ func (c *Client) addOperationDeleteTopicMiddlewares(stack *middleware.Stack, opt if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpDeleteTopicValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opDeleteTopic(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "DeleteTopic"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -131,22 +96,8 @@ func (c *Client) addOperationDeleteTopicMiddlewares(stack *middleware.Stack, opt if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opDeleteTopic(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "DeleteTopic", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_GetDataProtectionPolicy.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_GetDataProtectionPolicy.go index bddc5b9c97..17386c8c21 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_GetDataProtectionPolicy.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_GetDataProtectionPolicy.go @@ -4,8 +4,6 @@ package sns import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" ) @@ -54,9 +52,6 @@ type GetDataProtectionPolicyOutput struct { } func (c *Client) addOperationGetDataProtectionPolicyMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpGetDataProtectionPolicy{}, middleware.After) if err != nil { return err @@ -65,19 +60,10 @@ func (c *Client) addOperationGetDataProtectionPolicyMiddlewares(stack *middlewar if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "GetDataProtectionPolicy"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -87,19 +73,7 @@ func (c *Client) addOperationGetDataProtectionPolicyMiddlewares(stack *middlewar if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -108,22 +82,13 @@ func (c *Client) addOperationGetDataProtectionPolicyMiddlewares(stack *middlewar if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpGetDataProtectionPolicyValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opGetDataProtectionPolicy(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "GetDataProtectionPolicy"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -138,22 +103,8 @@ func (c *Client) addOperationGetDataProtectionPolicyMiddlewares(stack *middlewar if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opGetDataProtectionPolicy(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "GetDataProtectionPolicy", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_GetEndpointAttributes.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_GetEndpointAttributes.go index 744ec2240e..fc64f8e84b 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_GetEndpointAttributes.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_GetEndpointAttributes.go @@ -4,8 +4,6 @@ package sns import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" ) @@ -68,9 +66,6 @@ type GetEndpointAttributesOutput struct { } func (c *Client) addOperationGetEndpointAttributesMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpGetEndpointAttributes{}, middleware.After) if err != nil { return err @@ -79,19 +74,10 @@ func (c *Client) addOperationGetEndpointAttributesMiddlewares(stack *middleware. if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "GetEndpointAttributes"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -101,19 +87,7 @@ func (c *Client) addOperationGetEndpointAttributesMiddlewares(stack *middleware. if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -122,22 +96,13 @@ func (c *Client) addOperationGetEndpointAttributesMiddlewares(stack *middleware. if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpGetEndpointAttributesValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opGetEndpointAttributes(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "GetEndpointAttributes"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -152,22 +117,8 @@ func (c *Client) addOperationGetEndpointAttributesMiddlewares(stack *middleware. if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opGetEndpointAttributes(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "GetEndpointAttributes", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_GetPlatformApplicationAttributes.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_GetPlatformApplicationAttributes.go index 0931c7a263..26c42aaba3 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_GetPlatformApplicationAttributes.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_GetPlatformApplicationAttributes.go @@ -4,8 +4,6 @@ package sns import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" ) @@ -83,9 +81,6 @@ type GetPlatformApplicationAttributesOutput struct { } func (c *Client) addOperationGetPlatformApplicationAttributesMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpGetPlatformApplicationAttributes{}, middleware.After) if err != nil { return err @@ -94,19 +89,10 @@ func (c *Client) addOperationGetPlatformApplicationAttributesMiddlewares(stack * if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "GetPlatformApplicationAttributes"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -116,19 +102,7 @@ func (c *Client) addOperationGetPlatformApplicationAttributesMiddlewares(stack * if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -137,22 +111,13 @@ func (c *Client) addOperationGetPlatformApplicationAttributesMiddlewares(stack * if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpGetPlatformApplicationAttributesValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opGetPlatformApplicationAttributes(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "GetPlatformApplicationAttributes"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -167,22 +132,8 @@ func (c *Client) addOperationGetPlatformApplicationAttributesMiddlewares(stack * if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opGetPlatformApplicationAttributes(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "GetPlatformApplicationAttributes", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_GetSMSAttributes.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_GetSMSAttributes.go index be9a4dd703..0c58ba3f15 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_GetSMSAttributes.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_GetSMSAttributes.go @@ -4,8 +4,6 @@ package sns import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" ) @@ -58,9 +56,6 @@ type GetSMSAttributesOutput struct { } func (c *Client) addOperationGetSMSAttributesMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpGetSMSAttributes{}, middleware.After) if err != nil { return err @@ -69,19 +64,10 @@ func (c *Client) addOperationGetSMSAttributesMiddlewares(stack *middleware.Stack if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "GetSMSAttributes"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -91,19 +77,7 @@ func (c *Client) addOperationGetSMSAttributesMiddlewares(stack *middleware.Stack if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -112,19 +86,10 @@ func (c *Client) addOperationGetSMSAttributesMiddlewares(stack *middleware.Stack if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opGetSMSAttributes(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "GetSMSAttributes"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -139,22 +104,8 @@ func (c *Client) addOperationGetSMSAttributesMiddlewares(stack *middleware.Stack if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opGetSMSAttributes(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "GetSMSAttributes", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_GetSMSSandboxAccountStatus.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_GetSMSSandboxAccountStatus.go index 996a280f7f..f036a98471 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_GetSMSSandboxAccountStatus.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_GetSMSSandboxAccountStatus.go @@ -4,8 +4,6 @@ package sns import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" ) @@ -56,9 +54,6 @@ type GetSMSSandboxAccountStatusOutput struct { } func (c *Client) addOperationGetSMSSandboxAccountStatusMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpGetSMSSandboxAccountStatus{}, middleware.After) if err != nil { return err @@ -67,19 +62,10 @@ func (c *Client) addOperationGetSMSSandboxAccountStatusMiddlewares(stack *middle if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "GetSMSSandboxAccountStatus"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -89,19 +75,7 @@ func (c *Client) addOperationGetSMSSandboxAccountStatusMiddlewares(stack *middle if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -110,19 +84,10 @@ func (c *Client) addOperationGetSMSSandboxAccountStatusMiddlewares(stack *middle if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opGetSMSSandboxAccountStatus(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "GetSMSSandboxAccountStatus"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -137,22 +102,8 @@ func (c *Client) addOperationGetSMSSandboxAccountStatusMiddlewares(stack *middle if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opGetSMSSandboxAccountStatus(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "GetSMSSandboxAccountStatus", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_GetSubscriptionAttributes.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_GetSubscriptionAttributes.go index 4987b3004b..e121c7298d 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_GetSubscriptionAttributes.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_GetSubscriptionAttributes.go @@ -4,8 +4,6 @@ package sns import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" ) @@ -109,9 +107,6 @@ type GetSubscriptionAttributesOutput struct { } func (c *Client) addOperationGetSubscriptionAttributesMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpGetSubscriptionAttributes{}, middleware.After) if err != nil { return err @@ -120,19 +115,10 @@ func (c *Client) addOperationGetSubscriptionAttributesMiddlewares(stack *middlew if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "GetSubscriptionAttributes"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -142,19 +128,7 @@ func (c *Client) addOperationGetSubscriptionAttributesMiddlewares(stack *middlew if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -163,22 +137,13 @@ func (c *Client) addOperationGetSubscriptionAttributesMiddlewares(stack *middlew if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpGetSubscriptionAttributesValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opGetSubscriptionAttributes(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "GetSubscriptionAttributes"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -193,22 +158,8 @@ func (c *Client) addOperationGetSubscriptionAttributesMiddlewares(stack *middlew if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opGetSubscriptionAttributes(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "GetSubscriptionAttributes", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_GetTopicAttributes.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_GetTopicAttributes.go index 8e1bf5489d..20fd8b0cb9 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_GetTopicAttributes.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_GetTopicAttributes.go @@ -4,8 +4,6 @@ package sns import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" ) @@ -129,9 +127,6 @@ type GetTopicAttributesOutput struct { } func (c *Client) addOperationGetTopicAttributesMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpGetTopicAttributes{}, middleware.After) if err != nil { return err @@ -140,19 +135,10 @@ func (c *Client) addOperationGetTopicAttributesMiddlewares(stack *middleware.Sta if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "GetTopicAttributes"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -162,19 +148,7 @@ func (c *Client) addOperationGetTopicAttributesMiddlewares(stack *middleware.Sta if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -183,22 +157,13 @@ func (c *Client) addOperationGetTopicAttributesMiddlewares(stack *middleware.Sta if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpGetTopicAttributesValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opGetTopicAttributes(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "GetTopicAttributes"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -213,22 +178,8 @@ func (c *Client) addOperationGetTopicAttributesMiddlewares(stack *middleware.Sta if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opGetTopicAttributes(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "GetTopicAttributes", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_ListEndpointsByPlatformApplication.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_ListEndpointsByPlatformApplication.go index 60d70d2725..2b3830d02d 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_ListEndpointsByPlatformApplication.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_ListEndpointsByPlatformApplication.go @@ -5,7 +5,6 @@ package sns import ( "context" "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/aws-sdk-go-v2/service/sns/types" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" @@ -70,9 +69,6 @@ type ListEndpointsByPlatformApplicationOutput struct { } func (c *Client) addOperationListEndpointsByPlatformApplicationMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpListEndpointsByPlatformApplication{}, middleware.After) if err != nil { return err @@ -81,19 +77,10 @@ func (c *Client) addOperationListEndpointsByPlatformApplicationMiddlewares(stack if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "ListEndpointsByPlatformApplication"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -103,19 +90,7 @@ func (c *Client) addOperationListEndpointsByPlatformApplicationMiddlewares(stack if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -124,22 +99,13 @@ func (c *Client) addOperationListEndpointsByPlatformApplicationMiddlewares(stack if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpListEndpointsByPlatformApplicationValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opListEndpointsByPlatformApplication(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "ListEndpointsByPlatformApplication"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -154,12 +120,6 @@ func (c *Client) addOperationListEndpointsByPlatformApplicationMiddlewares(stack if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } @@ -249,11 +209,3 @@ type ListEndpointsByPlatformApplicationAPIClient interface { } var _ ListEndpointsByPlatformApplicationAPIClient = (*Client)(nil) - -func newServiceMetadataMiddleware_opListEndpointsByPlatformApplication(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "ListEndpointsByPlatformApplication", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_ListOriginationNumbers.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_ListOriginationNumbers.go index b7f5641939..8c9a35c6ba 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_ListOriginationNumbers.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_ListOriginationNumbers.go @@ -5,7 +5,6 @@ package sns import ( "context" "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/aws-sdk-go-v2/service/sns/types" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" @@ -58,9 +57,6 @@ type ListOriginationNumbersOutput struct { } func (c *Client) addOperationListOriginationNumbersMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpListOriginationNumbers{}, middleware.After) if err != nil { return err @@ -69,19 +65,10 @@ func (c *Client) addOperationListOriginationNumbersMiddlewares(stack *middleware if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "ListOriginationNumbers"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -91,19 +78,7 @@ func (c *Client) addOperationListOriginationNumbersMiddlewares(stack *middleware if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -112,19 +87,10 @@ func (c *Client) addOperationListOriginationNumbersMiddlewares(stack *middleware if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opListOriginationNumbers(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "ListOriginationNumbers"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -139,12 +105,6 @@ func (c *Client) addOperationListOriginationNumbersMiddlewares(stack *middleware if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } @@ -244,11 +204,3 @@ type ListOriginationNumbersAPIClient interface { } var _ ListOriginationNumbersAPIClient = (*Client)(nil) - -func newServiceMetadataMiddleware_opListOriginationNumbers(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "ListOriginationNumbers", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_ListPhoneNumbersOptedOut.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_ListPhoneNumbersOptedOut.go index 10f4482648..ae7e506010 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_ListPhoneNumbersOptedOut.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_ListPhoneNumbersOptedOut.go @@ -5,7 +5,6 @@ package sns import ( "context" "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" ) @@ -62,9 +61,6 @@ type ListPhoneNumbersOptedOutOutput struct { } func (c *Client) addOperationListPhoneNumbersOptedOutMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpListPhoneNumbersOptedOut{}, middleware.After) if err != nil { return err @@ -73,19 +69,10 @@ func (c *Client) addOperationListPhoneNumbersOptedOutMiddlewares(stack *middlewa if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "ListPhoneNumbersOptedOut"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -95,19 +82,7 @@ func (c *Client) addOperationListPhoneNumbersOptedOutMiddlewares(stack *middlewa if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -116,19 +91,10 @@ func (c *Client) addOperationListPhoneNumbersOptedOutMiddlewares(stack *middlewa if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opListPhoneNumbersOptedOut(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "ListPhoneNumbersOptedOut"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -143,12 +109,6 @@ func (c *Client) addOperationListPhoneNumbersOptedOutMiddlewares(stack *middlewa if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } @@ -237,11 +197,3 @@ type ListPhoneNumbersOptedOutAPIClient interface { } var _ ListPhoneNumbersOptedOutAPIClient = (*Client)(nil) - -func newServiceMetadataMiddleware_opListPhoneNumbersOptedOut(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "ListPhoneNumbersOptedOut", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_ListPlatformApplications.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_ListPlatformApplications.go index 8c450165dd..2f87c2d8da 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_ListPlatformApplications.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_ListPlatformApplications.go @@ -5,7 +5,6 @@ package sns import ( "context" "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/aws-sdk-go-v2/service/sns/types" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" @@ -65,9 +64,6 @@ type ListPlatformApplicationsOutput struct { } func (c *Client) addOperationListPlatformApplicationsMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpListPlatformApplications{}, middleware.After) if err != nil { return err @@ -76,19 +72,10 @@ func (c *Client) addOperationListPlatformApplicationsMiddlewares(stack *middlewa if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "ListPlatformApplications"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -98,19 +85,7 @@ func (c *Client) addOperationListPlatformApplicationsMiddlewares(stack *middlewa if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -119,19 +94,10 @@ func (c *Client) addOperationListPlatformApplicationsMiddlewares(stack *middlewa if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opListPlatformApplications(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "ListPlatformApplications"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -146,12 +112,6 @@ func (c *Client) addOperationListPlatformApplicationsMiddlewares(stack *middlewa if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } @@ -240,11 +200,3 @@ type ListPlatformApplicationsAPIClient interface { } var _ ListPlatformApplicationsAPIClient = (*Client)(nil) - -func newServiceMetadataMiddleware_opListPlatformApplications(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "ListPlatformApplications", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_ListSMSSandboxPhoneNumbers.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_ListSMSSandboxPhoneNumbers.go index 86b861be4d..0bb58322ad 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_ListSMSSandboxPhoneNumbers.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_ListSMSSandboxPhoneNumbers.go @@ -5,7 +5,6 @@ package sns import ( "context" "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/aws-sdk-go-v2/service/sns/types" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" @@ -68,9 +67,6 @@ type ListSMSSandboxPhoneNumbersOutput struct { } func (c *Client) addOperationListSMSSandboxPhoneNumbersMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpListSMSSandboxPhoneNumbers{}, middleware.After) if err != nil { return err @@ -79,19 +75,10 @@ func (c *Client) addOperationListSMSSandboxPhoneNumbersMiddlewares(stack *middle if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "ListSMSSandboxPhoneNumbers"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -101,19 +88,7 @@ func (c *Client) addOperationListSMSSandboxPhoneNumbersMiddlewares(stack *middle if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -122,19 +97,10 @@ func (c *Client) addOperationListSMSSandboxPhoneNumbersMiddlewares(stack *middle if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opListSMSSandboxPhoneNumbers(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "ListSMSSandboxPhoneNumbers"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -149,12 +115,6 @@ func (c *Client) addOperationListSMSSandboxPhoneNumbersMiddlewares(stack *middle if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } @@ -256,11 +216,3 @@ type ListSMSSandboxPhoneNumbersAPIClient interface { } var _ ListSMSSandboxPhoneNumbersAPIClient = (*Client)(nil) - -func newServiceMetadataMiddleware_opListSMSSandboxPhoneNumbers(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "ListSMSSandboxPhoneNumbers", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_ListSubscriptions.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_ListSubscriptions.go index 62a54231dc..157113f548 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_ListSubscriptions.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_ListSubscriptions.go @@ -5,7 +5,6 @@ package sns import ( "context" "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/aws-sdk-go-v2/service/sns/types" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" @@ -58,9 +57,6 @@ type ListSubscriptionsOutput struct { } func (c *Client) addOperationListSubscriptionsMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpListSubscriptions{}, middleware.After) if err != nil { return err @@ -69,19 +65,10 @@ func (c *Client) addOperationListSubscriptionsMiddlewares(stack *middleware.Stac if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "ListSubscriptions"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -91,19 +78,7 @@ func (c *Client) addOperationListSubscriptionsMiddlewares(stack *middleware.Stac if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -112,19 +87,10 @@ func (c *Client) addOperationListSubscriptionsMiddlewares(stack *middleware.Stac if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opListSubscriptions(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "ListSubscriptions"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -139,12 +105,6 @@ func (c *Client) addOperationListSubscriptionsMiddlewares(stack *middleware.Stac if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } @@ -231,11 +191,3 @@ type ListSubscriptionsAPIClient interface { } var _ ListSubscriptionsAPIClient = (*Client)(nil) - -func newServiceMetadataMiddleware_opListSubscriptions(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "ListSubscriptions", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_ListSubscriptionsByTopic.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_ListSubscriptionsByTopic.go index 64331e986e..af4acf8aa9 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_ListSubscriptionsByTopic.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_ListSubscriptionsByTopic.go @@ -5,7 +5,6 @@ package sns import ( "context" "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/aws-sdk-go-v2/service/sns/types" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" @@ -63,9 +62,6 @@ type ListSubscriptionsByTopicOutput struct { } func (c *Client) addOperationListSubscriptionsByTopicMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpListSubscriptionsByTopic{}, middleware.After) if err != nil { return err @@ -74,19 +70,10 @@ func (c *Client) addOperationListSubscriptionsByTopicMiddlewares(stack *middlewa if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "ListSubscriptionsByTopic"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -96,19 +83,7 @@ func (c *Client) addOperationListSubscriptionsByTopicMiddlewares(stack *middlewa if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -117,22 +92,13 @@ func (c *Client) addOperationListSubscriptionsByTopicMiddlewares(stack *middlewa if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpListSubscriptionsByTopicValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opListSubscriptionsByTopic(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "ListSubscriptionsByTopic"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -147,12 +113,6 @@ func (c *Client) addOperationListSubscriptionsByTopicMiddlewares(stack *middlewa if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } @@ -241,11 +201,3 @@ type ListSubscriptionsByTopicAPIClient interface { } var _ ListSubscriptionsByTopicAPIClient = (*Client)(nil) - -func newServiceMetadataMiddleware_opListSubscriptionsByTopic(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "ListSubscriptionsByTopic", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_ListTagsForResource.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_ListTagsForResource.go index fda5e6d173..5549c0cc78 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_ListTagsForResource.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_ListTagsForResource.go @@ -4,8 +4,6 @@ package sns import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/aws-sdk-go-v2/service/sns/types" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" @@ -52,9 +50,6 @@ type ListTagsForResourceOutput struct { } func (c *Client) addOperationListTagsForResourceMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpListTagsForResource{}, middleware.After) if err != nil { return err @@ -63,19 +58,10 @@ func (c *Client) addOperationListTagsForResourceMiddlewares(stack *middleware.St if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "ListTagsForResource"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -85,19 +71,7 @@ func (c *Client) addOperationListTagsForResourceMiddlewares(stack *middleware.St if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -106,22 +80,13 @@ func (c *Client) addOperationListTagsForResourceMiddlewares(stack *middleware.St if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpListTagsForResourceValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opListTagsForResource(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "ListTagsForResource"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -136,22 +101,8 @@ func (c *Client) addOperationListTagsForResourceMiddlewares(stack *middleware.St if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opListTagsForResource(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "ListTagsForResource", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_ListTopics.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_ListTopics.go index 96101d9cc9..9a1aa250cb 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_ListTopics.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_ListTopics.go @@ -5,7 +5,6 @@ package sns import ( "context" "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/aws-sdk-go-v2/service/sns/types" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" @@ -56,9 +55,6 @@ type ListTopicsOutput struct { } func (c *Client) addOperationListTopicsMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpListTopics{}, middleware.After) if err != nil { return err @@ -67,19 +63,10 @@ func (c *Client) addOperationListTopicsMiddlewares(stack *middleware.Stack, opti if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "ListTopics"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -89,19 +76,7 @@ func (c *Client) addOperationListTopicsMiddlewares(stack *middleware.Stack, opti if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -110,19 +85,10 @@ func (c *Client) addOperationListTopicsMiddlewares(stack *middleware.Stack, opti if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opListTopics(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "ListTopics"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -137,12 +103,6 @@ func (c *Client) addOperationListTopicsMiddlewares(stack *middleware.Stack, opti if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } @@ -228,11 +188,3 @@ type ListTopicsAPIClient interface { } var _ ListTopicsAPIClient = (*Client)(nil) - -func newServiceMetadataMiddleware_opListTopics(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "ListTopics", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_OptInPhoneNumber.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_OptInPhoneNumber.go index f7cb0b03bc..2031ec00c8 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_OptInPhoneNumber.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_OptInPhoneNumber.go @@ -4,8 +4,6 @@ package sns import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" ) @@ -49,9 +47,6 @@ type OptInPhoneNumberOutput struct { } func (c *Client) addOperationOptInPhoneNumberMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpOptInPhoneNumber{}, middleware.After) if err != nil { return err @@ -60,19 +55,10 @@ func (c *Client) addOperationOptInPhoneNumberMiddlewares(stack *middleware.Stack if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "OptInPhoneNumber"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -82,19 +68,7 @@ func (c *Client) addOperationOptInPhoneNumberMiddlewares(stack *middleware.Stack if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -103,22 +77,13 @@ func (c *Client) addOperationOptInPhoneNumberMiddlewares(stack *middleware.Stack if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpOptInPhoneNumberValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opOptInPhoneNumber(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "OptInPhoneNumber"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -133,22 +98,8 @@ func (c *Client) addOperationOptInPhoneNumberMiddlewares(stack *middleware.Stack if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opOptInPhoneNumber(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "OptInPhoneNumber", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_Publish.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_Publish.go index d3a3181607..507c9e3ea5 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_Publish.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_Publish.go @@ -4,8 +4,6 @@ package sns import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/aws-sdk-go-v2/service/sns/types" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" @@ -230,9 +228,6 @@ type PublishOutput struct { } func (c *Client) addOperationPublishMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpPublish{}, middleware.After) if err != nil { return err @@ -241,19 +236,10 @@ func (c *Client) addOperationPublishMiddlewares(stack *middleware.Stack, options if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "Publish"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -263,19 +249,7 @@ func (c *Client) addOperationPublishMiddlewares(stack *middleware.Stack, options if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -284,22 +258,13 @@ func (c *Client) addOperationPublishMiddlewares(stack *middleware.Stack, options if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpPublishValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opPublish(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "Publish"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -314,22 +279,8 @@ func (c *Client) addOperationPublishMiddlewares(stack *middleware.Stack, options if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opPublish(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "Publish", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_PublishBatch.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_PublishBatch.go index 64292891fc..7ecc420699 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_PublishBatch.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_PublishBatch.go @@ -4,8 +4,6 @@ package sns import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/aws-sdk-go-v2/service/sns/types" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" @@ -92,9 +90,6 @@ type PublishBatchOutput struct { } func (c *Client) addOperationPublishBatchMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpPublishBatch{}, middleware.After) if err != nil { return err @@ -103,19 +98,10 @@ func (c *Client) addOperationPublishBatchMiddlewares(stack *middleware.Stack, op if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "PublishBatch"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -125,19 +111,7 @@ func (c *Client) addOperationPublishBatchMiddlewares(stack *middleware.Stack, op if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -146,22 +120,13 @@ func (c *Client) addOperationPublishBatchMiddlewares(stack *middleware.Stack, op if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpPublishBatchValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opPublishBatch(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "PublishBatch"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -176,22 +141,8 @@ func (c *Client) addOperationPublishBatchMiddlewares(stack *middleware.Stack, op if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opPublishBatch(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "PublishBatch", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_PutDataProtectionPolicy.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_PutDataProtectionPolicy.go index 4325abe794..718a7ba36c 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_PutDataProtectionPolicy.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_PutDataProtectionPolicy.go @@ -4,8 +4,6 @@ package sns import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" ) @@ -59,9 +57,6 @@ type PutDataProtectionPolicyOutput struct { } func (c *Client) addOperationPutDataProtectionPolicyMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpPutDataProtectionPolicy{}, middleware.After) if err != nil { return err @@ -70,19 +65,10 @@ func (c *Client) addOperationPutDataProtectionPolicyMiddlewares(stack *middlewar if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "PutDataProtectionPolicy"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -92,19 +78,7 @@ func (c *Client) addOperationPutDataProtectionPolicyMiddlewares(stack *middlewar if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -113,22 +87,13 @@ func (c *Client) addOperationPutDataProtectionPolicyMiddlewares(stack *middlewar if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpPutDataProtectionPolicyValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opPutDataProtectionPolicy(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "PutDataProtectionPolicy"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -143,22 +108,8 @@ func (c *Client) addOperationPutDataProtectionPolicyMiddlewares(stack *middlewar if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opPutDataProtectionPolicy(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "PutDataProtectionPolicy", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_RemovePermission.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_RemovePermission.go index ffb401ec0f..fbe8f4a2b3 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_RemovePermission.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_RemovePermission.go @@ -4,8 +4,6 @@ package sns import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" ) @@ -54,9 +52,6 @@ type RemovePermissionOutput struct { } func (c *Client) addOperationRemovePermissionMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpRemovePermission{}, middleware.After) if err != nil { return err @@ -65,19 +60,10 @@ func (c *Client) addOperationRemovePermissionMiddlewares(stack *middleware.Stack if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "RemovePermission"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -87,19 +73,7 @@ func (c *Client) addOperationRemovePermissionMiddlewares(stack *middleware.Stack if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -108,22 +82,13 @@ func (c *Client) addOperationRemovePermissionMiddlewares(stack *middleware.Stack if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpRemovePermissionValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opRemovePermission(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "RemovePermission"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -138,22 +103,8 @@ func (c *Client) addOperationRemovePermissionMiddlewares(stack *middleware.Stack if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opRemovePermission(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "RemovePermission", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_SetEndpointAttributes.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_SetEndpointAttributes.go index abb7fc6cad..b2a2ce917e 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_SetEndpointAttributes.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_SetEndpointAttributes.go @@ -4,8 +4,6 @@ package sns import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" ) @@ -66,9 +64,6 @@ type SetEndpointAttributesOutput struct { } func (c *Client) addOperationSetEndpointAttributesMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpSetEndpointAttributes{}, middleware.After) if err != nil { return err @@ -77,19 +72,10 @@ func (c *Client) addOperationSetEndpointAttributesMiddlewares(stack *middleware. if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "SetEndpointAttributes"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -99,19 +85,7 @@ func (c *Client) addOperationSetEndpointAttributesMiddlewares(stack *middleware. if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -120,22 +94,13 @@ func (c *Client) addOperationSetEndpointAttributesMiddlewares(stack *middleware. if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpSetEndpointAttributesValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opSetEndpointAttributes(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "SetEndpointAttributes"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -150,22 +115,8 @@ func (c *Client) addOperationSetEndpointAttributesMiddlewares(stack *middleware. if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opSetEndpointAttributes(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "SetEndpointAttributes", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_SetPlatformApplicationAttributes.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_SetPlatformApplicationAttributes.go index 34fd715db2..084be5e2a0 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_SetPlatformApplicationAttributes.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_SetPlatformApplicationAttributes.go @@ -4,8 +4,6 @@ package sns import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" ) @@ -119,9 +117,6 @@ type SetPlatformApplicationAttributesOutput struct { } func (c *Client) addOperationSetPlatformApplicationAttributesMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpSetPlatformApplicationAttributes{}, middleware.After) if err != nil { return err @@ -130,19 +125,10 @@ func (c *Client) addOperationSetPlatformApplicationAttributesMiddlewares(stack * if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "SetPlatformApplicationAttributes"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -152,19 +138,7 @@ func (c *Client) addOperationSetPlatformApplicationAttributesMiddlewares(stack * if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -173,22 +147,13 @@ func (c *Client) addOperationSetPlatformApplicationAttributesMiddlewares(stack * if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpSetPlatformApplicationAttributesValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opSetPlatformApplicationAttributes(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "SetPlatformApplicationAttributes"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -203,22 +168,8 @@ func (c *Client) addOperationSetPlatformApplicationAttributesMiddlewares(stack * if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opSetPlatformApplicationAttributes(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "SetPlatformApplicationAttributes", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_SetSMSAttributes.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_SetSMSAttributes.go index 5c6d4853a8..844b713c6c 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_SetSMSAttributes.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_SetSMSAttributes.go @@ -4,8 +4,6 @@ package sns import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" ) @@ -130,9 +128,6 @@ type SetSMSAttributesOutput struct { } func (c *Client) addOperationSetSMSAttributesMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpSetSMSAttributes{}, middleware.After) if err != nil { return err @@ -141,19 +136,10 @@ func (c *Client) addOperationSetSMSAttributesMiddlewares(stack *middleware.Stack if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "SetSMSAttributes"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -163,19 +149,7 @@ func (c *Client) addOperationSetSMSAttributesMiddlewares(stack *middleware.Stack if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -184,22 +158,13 @@ func (c *Client) addOperationSetSMSAttributesMiddlewares(stack *middleware.Stack if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpSetSMSAttributesValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opSetSMSAttributes(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "SetSMSAttributes"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -214,22 +179,8 @@ func (c *Client) addOperationSetSMSAttributesMiddlewares(stack *middleware.Stack if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opSetSMSAttributes(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "SetSMSAttributes", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_SetSubscriptionAttributes.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_SetSubscriptionAttributes.go index 1781d4fc12..d4e4f16cbe 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_SetSubscriptionAttributes.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_SetSubscriptionAttributes.go @@ -4,8 +4,6 @@ package sns import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" ) @@ -98,9 +96,6 @@ type SetSubscriptionAttributesOutput struct { } func (c *Client) addOperationSetSubscriptionAttributesMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpSetSubscriptionAttributes{}, middleware.After) if err != nil { return err @@ -109,19 +104,10 @@ func (c *Client) addOperationSetSubscriptionAttributesMiddlewares(stack *middlew if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "SetSubscriptionAttributes"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -131,19 +117,7 @@ func (c *Client) addOperationSetSubscriptionAttributesMiddlewares(stack *middlew if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -152,22 +126,13 @@ func (c *Client) addOperationSetSubscriptionAttributesMiddlewares(stack *middlew if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpSetSubscriptionAttributesValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opSetSubscriptionAttributes(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "SetSubscriptionAttributes"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -182,22 +147,8 @@ func (c *Client) addOperationSetSubscriptionAttributesMiddlewares(stack *middlew if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opSetSubscriptionAttributes(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "SetSubscriptionAttributes", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_SetTopicAttributes.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_SetTopicAttributes.go index 4e48daaef6..8ce7265a60 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_SetTopicAttributes.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_SetTopicAttributes.go @@ -4,8 +4,6 @@ package sns import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" ) @@ -199,9 +197,6 @@ type SetTopicAttributesOutput struct { } func (c *Client) addOperationSetTopicAttributesMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpSetTopicAttributes{}, middleware.After) if err != nil { return err @@ -210,19 +205,10 @@ func (c *Client) addOperationSetTopicAttributesMiddlewares(stack *middleware.Sta if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "SetTopicAttributes"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -232,19 +218,7 @@ func (c *Client) addOperationSetTopicAttributesMiddlewares(stack *middleware.Sta if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -253,22 +227,13 @@ func (c *Client) addOperationSetTopicAttributesMiddlewares(stack *middleware.Sta if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpSetTopicAttributesValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opSetTopicAttributes(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "SetTopicAttributes"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -283,22 +248,8 @@ func (c *Client) addOperationSetTopicAttributesMiddlewares(stack *middleware.Sta if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opSetTopicAttributes(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "SetTopicAttributes", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_Subscribe.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_Subscribe.go index 1cbad84892..f3bb628662 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_Subscribe.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_Subscribe.go @@ -4,8 +4,6 @@ package sns import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" ) @@ -192,9 +190,6 @@ type SubscribeOutput struct { } func (c *Client) addOperationSubscribeMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpSubscribe{}, middleware.After) if err != nil { return err @@ -203,19 +198,10 @@ func (c *Client) addOperationSubscribeMiddlewares(stack *middleware.Stack, optio if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "Subscribe"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -225,19 +211,7 @@ func (c *Client) addOperationSubscribeMiddlewares(stack *middleware.Stack, optio if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -246,22 +220,13 @@ func (c *Client) addOperationSubscribeMiddlewares(stack *middleware.Stack, optio if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpSubscribeValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opSubscribe(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "Subscribe"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -276,22 +241,8 @@ func (c *Client) addOperationSubscribeMiddlewares(stack *middleware.Stack, optio if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opSubscribe(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "Subscribe", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_TagResource.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_TagResource.go index b5cb65ad32..70c7647153 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_TagResource.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_TagResource.go @@ -4,8 +4,6 @@ package sns import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/aws-sdk-go-v2/service/sns/types" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" @@ -71,9 +69,6 @@ type TagResourceOutput struct { } func (c *Client) addOperationTagResourceMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpTagResource{}, middleware.After) if err != nil { return err @@ -82,19 +77,10 @@ func (c *Client) addOperationTagResourceMiddlewares(stack *middleware.Stack, opt if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "TagResource"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -104,19 +90,7 @@ func (c *Client) addOperationTagResourceMiddlewares(stack *middleware.Stack, opt if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -125,22 +99,13 @@ func (c *Client) addOperationTagResourceMiddlewares(stack *middleware.Stack, opt if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpTagResourceValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opTagResource(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "TagResource"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -155,22 +120,8 @@ func (c *Client) addOperationTagResourceMiddlewares(stack *middleware.Stack, opt if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opTagResource(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "TagResource", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_Unsubscribe.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_Unsubscribe.go index 9b8125d420..4118e3c661 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_Unsubscribe.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_Unsubscribe.go @@ -4,8 +4,6 @@ package sns import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" ) @@ -53,9 +51,6 @@ type UnsubscribeOutput struct { } func (c *Client) addOperationUnsubscribeMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpUnsubscribe{}, middleware.After) if err != nil { return err @@ -64,19 +59,10 @@ func (c *Client) addOperationUnsubscribeMiddlewares(stack *middleware.Stack, opt if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "Unsubscribe"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -86,19 +72,7 @@ func (c *Client) addOperationUnsubscribeMiddlewares(stack *middleware.Stack, opt if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -107,22 +81,13 @@ func (c *Client) addOperationUnsubscribeMiddlewares(stack *middleware.Stack, opt if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpUnsubscribeValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opUnsubscribe(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "Unsubscribe"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -137,22 +102,8 @@ func (c *Client) addOperationUnsubscribeMiddlewares(stack *middleware.Stack, opt if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opUnsubscribe(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "Unsubscribe", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_UntagResource.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_UntagResource.go index 0764236b4b..a70340dc26 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_UntagResource.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_UntagResource.go @@ -4,8 +4,6 @@ package sns import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" ) @@ -52,9 +50,6 @@ type UntagResourceOutput struct { } func (c *Client) addOperationUntagResourceMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpUntagResource{}, middleware.After) if err != nil { return err @@ -63,19 +58,10 @@ func (c *Client) addOperationUntagResourceMiddlewares(stack *middleware.Stack, o if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "UntagResource"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -85,19 +71,7 @@ func (c *Client) addOperationUntagResourceMiddlewares(stack *middleware.Stack, o if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -106,22 +80,13 @@ func (c *Client) addOperationUntagResourceMiddlewares(stack *middleware.Stack, o if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpUntagResourceValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opUntagResource(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "UntagResource"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -136,22 +101,8 @@ func (c *Client) addOperationUntagResourceMiddlewares(stack *middleware.Stack, o if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opUntagResource(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "UntagResource", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_VerifySMSSandboxPhoneNumber.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_VerifySMSSandboxPhoneNumber.go index bc4cf67d71..8ad5ec567a 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_VerifySMSSandboxPhoneNumber.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/api_op_VerifySMSSandboxPhoneNumber.go @@ -4,8 +4,6 @@ package sns import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" ) @@ -63,9 +61,6 @@ type VerifySMSSandboxPhoneNumberOutput struct { } func (c *Client) addOperationVerifySMSSandboxPhoneNumberMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpVerifySMSSandboxPhoneNumber{}, middleware.After) if err != nil { return err @@ -74,19 +69,10 @@ func (c *Client) addOperationVerifySMSSandboxPhoneNumberMiddlewares(stack *middl if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "VerifySMSSandboxPhoneNumber"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -96,19 +82,7 @@ func (c *Client) addOperationVerifySMSSandboxPhoneNumberMiddlewares(stack *middl if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -117,22 +91,13 @@ func (c *Client) addOperationVerifySMSSandboxPhoneNumberMiddlewares(stack *middl if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpVerifySMSSandboxPhoneNumberValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opVerifySMSSandboxPhoneNumber(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "VerifySMSSandboxPhoneNumber"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -147,22 +112,8 @@ func (c *Client) addOperationVerifySMSSandboxPhoneNumberMiddlewares(stack *middl if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opVerifySMSSandboxPhoneNumber(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "VerifySMSSandboxPhoneNumber", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/auth.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/auth.go index 9f11cc500b..c0832d29cb 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/auth.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/auth.go @@ -184,7 +184,7 @@ func (m *resolveAuthSchemeMiddleware) selectScheme(options []*smithyauth.Option) } for _, scheme := range m.options.AuthSchemes { - if scheme.SchemeID() != option.SchemeID { + if !matchSchemeID(scheme.SchemeID(), option.SchemeID) { continue } @@ -197,6 +197,16 @@ func (m *resolveAuthSchemeMiddleware) selectScheme(options []*smithyauth.Option) return nil, false } +func matchSchemeID(registered, option string) bool { + if registered == option { + return true + } + if i := strings.LastIndex(registered, "#"); i != -1 { + return registered[i+1:] == option + } + return false +} + func sortAuthOptions(options []*smithyauth.Option, preferred []string) []*smithyauth.Option { byPriority := make([]*smithyauth.Option, 0, len(options)) for _, prefName := range preferred { diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/deserializers.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/deserializers.go index 2da54823f5..56617b3a97 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/deserializers.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/deserializers.go @@ -19,7 +19,6 @@ import ( "github.com/aws/smithy-go/tracing" smithyhttp "github.com/aws/smithy-go/transport/http" "io" - "io/ioutil" "strconv" "strings" ) @@ -54,7 +53,7 @@ func (m *awsAwsquery_deserializeOpAddPermission) HandleDeserialize(ctx context.C output := &AddPermissionOutput{} out.Result = output - if _, err = io.Copy(ioutil.Discard, response.Body); err != nil { + if _, err = io.Copy(io.Discard, response.Body); err != nil { return out, metadata, &smithy.DeserializationError{ Err: fmt.Errorf("failed to discard response body, %w", err), } @@ -893,7 +892,7 @@ func (m *awsAwsquery_deserializeOpDeleteEndpoint) HandleDeserialize(ctx context. output := &DeleteEndpointOutput{} out.Result = output - if _, err = io.Copy(ioutil.Discard, response.Body); err != nil { + if _, err = io.Copy(io.Discard, response.Body); err != nil { return out, metadata, &smithy.DeserializationError{ Err: fmt.Errorf("failed to discard response body, %w", err), } @@ -976,7 +975,7 @@ func (m *awsAwsquery_deserializeOpDeletePlatformApplication) HandleDeserialize(c output := &DeletePlatformApplicationOutput{} out.Result = output - if _, err = io.Copy(ioutil.Discard, response.Body); err != nil { + if _, err = io.Copy(io.Discard, response.Body); err != nil { return out, metadata, &smithy.DeserializationError{ Err: fmt.Errorf("failed to discard response body, %w", err), } @@ -1186,7 +1185,7 @@ func (m *awsAwsquery_deserializeOpDeleteTopic) HandleDeserialize(ctx context.Con output := &DeleteTopicOutput{} out.Result = output - if _, err = io.Copy(ioutil.Discard, response.Body); err != nil { + if _, err = io.Copy(io.Discard, response.Body); err != nil { return out, metadata, &smithy.DeserializationError{ Err: fmt.Errorf("failed to discard response body, %w", err), } @@ -3667,7 +3666,7 @@ func (m *awsAwsquery_deserializeOpPutDataProtectionPolicy) HandleDeserialize(ctx output := &PutDataProtectionPolicyOutput{} out.Result = output - if _, err = io.Copy(ioutil.Discard, response.Body); err != nil { + if _, err = io.Copy(io.Discard, response.Body); err != nil { return out, metadata, &smithy.DeserializationError{ Err: fmt.Errorf("failed to discard response body, %w", err), } @@ -3756,7 +3755,7 @@ func (m *awsAwsquery_deserializeOpRemovePermission) HandleDeserialize(ctx contex output := &RemovePermissionOutput{} out.Result = output - if _, err = io.Copy(ioutil.Discard, response.Body); err != nil { + if _, err = io.Copy(io.Discard, response.Body); err != nil { return out, metadata, &smithy.DeserializationError{ Err: fmt.Errorf("failed to discard response body, %w", err), } @@ -3842,7 +3841,7 @@ func (m *awsAwsquery_deserializeOpSetEndpointAttributes) HandleDeserialize(ctx c output := &SetEndpointAttributesOutput{} out.Result = output - if _, err = io.Copy(ioutil.Discard, response.Body); err != nil { + if _, err = io.Copy(io.Discard, response.Body); err != nil { return out, metadata, &smithy.DeserializationError{ Err: fmt.Errorf("failed to discard response body, %w", err), } @@ -3928,7 +3927,7 @@ func (m *awsAwsquery_deserializeOpSetPlatformApplicationAttributes) HandleDeseri output := &SetPlatformApplicationAttributesOutput{} out.Result = output - if _, err = io.Copy(ioutil.Discard, response.Body); err != nil { + if _, err = io.Copy(io.Discard, response.Body); err != nil { return out, metadata, &smithy.DeserializationError{ Err: fmt.Errorf("failed to discard response body, %w", err), } @@ -4135,7 +4134,7 @@ func (m *awsAwsquery_deserializeOpSetSubscriptionAttributes) HandleDeserialize(c output := &SetSubscriptionAttributesOutput{} out.Result = output - if _, err = io.Copy(ioutil.Discard, response.Body); err != nil { + if _, err = io.Copy(io.Discard, response.Body); err != nil { return out, metadata, &smithy.DeserializationError{ Err: fmt.Errorf("failed to discard response body, %w", err), } @@ -4227,7 +4226,7 @@ func (m *awsAwsquery_deserializeOpSetTopicAttributes) HandleDeserialize(ctx cont output := &SetTopicAttributesOutput{} out.Result = output - if _, err = io.Copy(ioutil.Discard, response.Body); err != nil { + if _, err = io.Copy(io.Discard, response.Body); err != nil { return out, metadata, &smithy.DeserializationError{ Err: fmt.Errorf("failed to discard response body, %w", err), } @@ -4579,7 +4578,7 @@ func (m *awsAwsquery_deserializeOpUnsubscribe) HandleDeserialize(ctx context.Con output := &UnsubscribeOutput{} out.Result = output - if _, err = io.Copy(ioutil.Discard, response.Body); err != nil { + if _, err = io.Copy(io.Discard, response.Body); err != nil { return out, metadata, &smithy.DeserializationError{ Err: fmt.Errorf("failed to discard response body, %w", err), } diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/endpoints.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/endpoints.go index c2af00fcc6..964f713fa8 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/endpoints.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/endpoints.go @@ -14,6 +14,7 @@ import ( internalendpoints "github.com/aws/aws-sdk-go-v2/service/sns/internal/endpoints" smithyauth "github.com/aws/smithy-go/auth" smithyendpoints "github.com/aws/smithy-go/endpoints" + "github.com/aws/smithy-go/endpoints/private/bdd" "github.com/aws/smithy-go/endpoints/private/rulesfn" "github.com/aws/smithy-go/middleware" "github.com/aws/smithy-go/ptr" @@ -229,6 +230,8 @@ func bindRegion(region string) (*string, error) { return aws.String(endpoints.MapFIPSRegion(region)), nil } +var _ = rulesfn.StringSlice(nil) + // EndpointParameters provides the parameters that influence how endpoints are // resolved. type EndpointParameters struct { @@ -294,21 +297,163 @@ func (p EndpointParameters) WithDefaults() EndpointParameters { return p } -type stringSlice []string +const bddRoot int32 = 2 -func (s stringSlice) Get(i int) *string { - if i < 0 || i >= len(s) { - return nil - } +var bddNodes = [45]int32{ + -1, 1, -1, 0, 14, 3, 1, 4, 100000013, 2, 5, 100000013, 3, 8, 6, 4, 7, 100000012, 5, 100000010, 100000011, 4, 12, 9, 6, 10, 100000009, 7, 100000006, 11, 8, 100000007, 100000008, 5, 13, 100000005, 6, 100000004, 100000005, 3, 100000001, 15, 4, 100000002, 100000003} + +type conditionContext struct { + PartitionResult *awsrulesfn.PartitionConfig +} + +func evalCondition(idx int, params *EndpointParameters, c *conditionContext) bool { + switch idx { + case 0: + return params.Endpoint != nil + case 1: + return params.Region != nil + case 2: + if v := awsrulesfn.GetPartition(*params.Region); v != nil { + c.PartitionResult = v + return true + } + return false + case 3: + return *params.UseFIPS == true + case 4: + return *params.UseDualStack == true + case 5: + return c.PartitionResult.SupportsDualStack == true + case 6: + return c.PartitionResult.SupportsFIPS == true + case 7: + return *params.Region == "us-gov-east-1" + case 8: + return *params.Region == "us-gov-west-1" + } + return false +} - v := s[i] - return &v +func resolveResult(idx int32, params *EndpointParameters, c *conditionContext) (smithyendpoints.Endpoint, error) { + switch idx { + case 0: + return smithyendpoints.Endpoint{}, fmt.Errorf("endpoint resolution failed: no matching rule") + case 1: + return smithyendpoints.Endpoint{}, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: FIPS and custom endpoint are not supported") + case 2: + return smithyendpoints.Endpoint{}, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: Dualstack and custom endpoint are not supported") + case 3: + uriString := *params.Endpoint + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + case 4: + uriString := func() string { + var out strings.Builder + out.WriteString("https://sns-fips.") + out.WriteString(*params.Region) + out.WriteString(".") + out.WriteString(c.PartitionResult.DualStackDnsSuffix) + return out.String() + }() + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + case 5: + return smithyendpoints.Endpoint{}, fmt.Errorf("endpoint rule error, %s", "FIPS and DualStack are enabled, but this partition does not support one or both") + case 6: + uriString := "https://sns.us-gov-east-1.amazonaws.com" + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + case 7: + uriString := "https://sns.us-gov-west-1.amazonaws.com" + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + case 8: + uriString := func() string { + var out strings.Builder + out.WriteString("https://sns-fips.") + out.WriteString(*params.Region) + out.WriteString(".") + out.WriteString(c.PartitionResult.DnsSuffix) + return out.String() + }() + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + case 9: + return smithyendpoints.Endpoint{}, fmt.Errorf("endpoint rule error, %s", "FIPS is enabled but this partition does not support FIPS") + case 10: + uriString := func() string { + var out strings.Builder + out.WriteString("https://sns.") + out.WriteString(*params.Region) + out.WriteString(".") + out.WriteString(c.PartitionResult.DualStackDnsSuffix) + return out.String() + }() + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + case 11: + return smithyendpoints.Endpoint{}, fmt.Errorf("endpoint rule error, %s", "DualStack is enabled but this partition does not support DualStack") + case 12: + uriString := func() string { + var out strings.Builder + out.WriteString("https://sns.") + out.WriteString(*params.Region) + out.WriteString(".") + out.WriteString(c.PartitionResult.DnsSuffix) + return out.String() + }() + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + case 13: + return smithyendpoints.Endpoint{}, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: Missing Region") + } + return smithyendpoints.Endpoint{}, fmt.Errorf("endpoint rule error, invalid result index: %d", idx) } // EndpointResolverV2 provides the interface for resolving service endpoints. type EndpointResolverV2 interface { - // ResolveEndpoint attempts to resolve the endpoint with the provided options, - // returning the endpoint if found. Otherwise an error is returned. ResolveEndpoint(ctx context.Context, params EndpointParameters) ( smithyendpoints.Endpoint, error, ) @@ -332,159 +477,12 @@ func (r *resolver) ResolveEndpoint( if err = params.ValidateRequired(); err != nil { return endpoint, fmt.Errorf("endpoint parameters are not valid, %w", err) } - _UseDualStack := *params.UseDualStack - _ = _UseDualStack - _UseFIPS := *params.UseFIPS - _ = _UseFIPS - if exprVal := params.Endpoint; exprVal != nil { - _Endpoint := *exprVal - _ = _Endpoint - if _UseFIPS == true { - return endpoint, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: FIPS and custom endpoint are not supported") - } - if _UseDualStack == true { - return endpoint, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: Dualstack and custom endpoint are not supported") - } - uriString := _Endpoint - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - }, nil - } - if exprVal := params.Region; exprVal != nil { - _Region := *exprVal - _ = _Region - if exprVal := awsrulesfn.GetPartition(_Region); exprVal != nil { - _PartitionResult := *exprVal - _ = _PartitionResult - if _UseFIPS == true { - if _UseDualStack == true { - if true == _PartitionResult.SupportsFIPS { - if true == _PartitionResult.SupportsDualStack { - uriString := func() string { - var out strings.Builder - out.WriteString("https://sns-fips.") - out.WriteString(_Region) - out.WriteString(".") - out.WriteString(_PartitionResult.DualStackDnsSuffix) - return out.String() - }() - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - }, nil - } - } - return endpoint, fmt.Errorf("endpoint rule error, %s", "FIPS and DualStack are enabled, but this partition does not support one or both") - } - } - if _UseFIPS == true { - if _PartitionResult.SupportsFIPS == true { - if _Region == "us-gov-east-1" { - uriString := "https://sns.us-gov-east-1.amazonaws.com" - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - }, nil - } - if _Region == "us-gov-west-1" { - uriString := "https://sns.us-gov-west-1.amazonaws.com" - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - }, nil - } - uriString := func() string { - var out strings.Builder - out.WriteString("https://sns-fips.") - out.WriteString(_Region) - out.WriteString(".") - out.WriteString(_PartitionResult.DnsSuffix) - return out.String() - }() - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - }, nil - } - return endpoint, fmt.Errorf("endpoint rule error, %s", "FIPS is enabled but this partition does not support FIPS") - } - if _UseDualStack == true { - if true == _PartitionResult.SupportsDualStack { - uriString := func() string { - var out strings.Builder - out.WriteString("https://sns.") - out.WriteString(_Region) - out.WriteString(".") - out.WriteString(_PartitionResult.DualStackDnsSuffix) - return out.String() - }() - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - }, nil - } - return endpoint, fmt.Errorf("endpoint rule error, %s", "DualStack is enabled but this partition does not support DualStack") - } - uriString := func() string { - var out strings.Builder - out.WriteString("https://sns.") - out.WriteString(_Region) - out.WriteString(".") - out.WriteString(_PartitionResult.DnsSuffix) - return out.String() - }() - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - }, nil - } - return endpoint, fmt.Errorf("Endpoint resolution failed. Invalid operation or environment input.") - } - return endpoint, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: Missing Region") + c := &conditionContext{} + ref := bdd.Evaluate(bddNodes[:], bddRoot, func(idx int) bool { + return evalCondition(idx, ¶ms, c) + }) + return resolveResult(ref, ¶ms, c) } type endpointParamsBinder interface { diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/generated.json b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/generated.json index a57c046a79..af4fe51ffd 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/generated.json +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/generated.json @@ -60,6 +60,8 @@ "internal/endpoints/endpoints.go", "internal/endpoints/endpoints_test.go", "options.go", + "request_snapshot_test.go", + "response_snapshot_test.go", "serializers.go", "snapshot_test.go", "sra_operation_order_test.go", diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/go_module_metadata.go index 88f8840943..a78894bdc1 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/go_module_metadata.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/go_module_metadata.go @@ -3,4 +3,4 @@ package sns // goModuleVersion is the tagged release for this module -const goModuleVersion = "1.39.17" +const goModuleVersion = "1.42.2" diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/options.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/options.go index 690b80c071..5e6cde32e9 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/options.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/options.go @@ -44,6 +44,11 @@ type Options struct { // clients initial default settings. DefaultsMode aws.DefaultsMode + // Disables SDK clock skew correction. When set, the SDK will not adjust request + // signing timestamps to compensate for clock drift between the client and the + // service. + DisableClockSkewCorrection bool + // The endpoint options to be used when attempting to resolve an endpoint. EndpointOptions EndpointResolverOptions diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/types/errors.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/types/errors.go index b52c35306d..79bd763a40 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sns/types/errors.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sns/types/errors.go @@ -166,7 +166,7 @@ func (e *EndpointDisabledException) ErrorFault() smithy.ErrorFault { return smit // Indicates that the number of filter polices in your Amazon Web Services account // exceeds the limit. To add more filter polices, submit an Amazon SNS Limit -// Increase case in the Amazon Web ServicesSupport Center. +// Increase case in the Amazon Web Services Support Center. type FilterPolicyLimitExceededException struct { Message *string diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sso/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/service/sso/CHANGELOG.md index 26c80a2c23..9b0d76107d 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sso/CHANGELOG.md +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sso/CHANGELOG.md @@ -1,3 +1,61 @@ +# v1.33.2 (2026-07-29) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.33.1 (2026-07-28) + +* **Dependency Update**: Update to smithy-go v1.27.5. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.33.0 (2026-07-21) + +* **Feature**: Add an option to clients to disable clock skew +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.32.1 (2026-07-13) + +* No change notes available for this release. + +# v1.32.0 (2026-07-06) + +* **Feature**: Add request serialization snapshot tests. + +# v1.31.5 (2026-07-01) + +* **Bug Fix**: Bump smithy-go to 1.27.3, fix JSON encorder for document.Number, endpoint host label format validation and CBOR union serialization on new serde +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.31.4 (2026-06-29) + +* No change notes available for this release. + +# v1.31.3 (2026-06-08) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.31.2 (2026-06-04) + +* **Dependency Update**: Update to smithy-go v1.27.1 to fix several union-related deserialization bugs in schema-serde-enabled services. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.31.1 (2026-06-03) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.31.0 (2026-06-02) + +* **Feature**: Adding new BDD representation of endpoint ruleset +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.30.19 (2026-05-29) + +* **Dependency Update**: Update to smithy-go v1.26.0. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.30.18 (2026-05-28) + +* **Dependency Update**: Updated to the latest SDK module versions + # v1.30.17 (2026-04-29) * **Dependency Update**: Update to smithy-go v1.25.1. diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sso/api_client.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sso/api_client.go index ca5364792a..76b3527d35 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sso/api_client.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sso/api_client.go @@ -259,6 +259,10 @@ func (c *Client) invokeOperation( finalizeClientEndpointResolverOptions(&options) + if err := c.addCommonMiddlewares(stack, options, opID); err != nil { + return nil, metadata, err + } + for _, fn := range stackFns { if err := fn(stack, options); err != nil { return nil, metadata, err @@ -363,6 +367,49 @@ func addProtocolFinalizerMiddlewares(stack *middleware.Stack, options Options, o } return nil } + +func (c *Client) addCommonMiddlewares(stack *middleware.Stack, options Options, operation string) error { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, operation); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + if err := addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err := addClientRequestID(stack); err != nil { + return err + } + if err := addRetry(stack, options, c); err != nil { + return err + } + if err := addRawResponseToMetadata(stack); err != nil { + return err + } + if err := addSpanRetryLoop(stack, options); err != nil { + return err + } + if err := addClientUserAgent(stack, options); err != nil { + return err + } + if err := addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err := addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err := addRecursionDetection(stack); err != nil { + return err + } + if err := addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err := addInterceptAttempt(stack, options); err != nil { + return err + } + return nil +} func resolveAuthSchemeResolver(options *Options) { if options.AuthSchemeResolver == nil { options.AuthSchemeResolver = &defaultAuthSchemeResolver{} @@ -436,16 +483,17 @@ func setResolvedDefaultsMode(o *Options) { // NewFromConfig returns a new client from the provided config. func NewFromConfig(cfg aws.Config, optFns ...func(*Options)) *Client { opts := Options{ - Region: cfg.Region, - DefaultsMode: cfg.DefaultsMode, - RuntimeEnvironment: cfg.RuntimeEnvironment, - HTTPClient: cfg.HTTPClient, - Credentials: cfg.Credentials, - APIOptions: cfg.APIOptions, - Logger: cfg.Logger, - ClientLogMode: cfg.ClientLogMode, - AppID: cfg.AppID, - AuthSchemePreference: cfg.AuthSchemePreference, + Region: cfg.Region, + DefaultsMode: cfg.DefaultsMode, + RuntimeEnvironment: cfg.RuntimeEnvironment, + HTTPClient: cfg.HTTPClient, + Credentials: cfg.Credentials, + APIOptions: cfg.APIOptions, + Logger: cfg.Logger, + ClientLogMode: cfg.ClientLogMode, + AppID: cfg.AppID, + DisableClockSkewCorrection: cfg.DisableClockSkewCorrection, + AuthSchemePreference: cfg.AuthSchemePreference, } resolveAWSRetryerProvider(cfg, &opts) resolveAWSRetryMaxAttempts(cfg, &opts) @@ -635,15 +683,17 @@ func addClientRequestID(stack *middleware.Stack) error { } func addComputeContentLength(stack *middleware.Stack) error { - return stack.Build.Add(&smithyhttp.ComputeContentLength{}, middleware.After) + return stack.Build.Insert(&smithyhttp.ComputeContentLength{}, "ClientRequestID", middleware.After) } func addRawResponseToMetadata(stack *middleware.Stack) error { return stack.Deserialize.Add(&awsmiddleware.AddRawResponse{}, middleware.Before) } -func addRecordResponseTiming(stack *middleware.Stack) error { - return stack.Deserialize.Add(&awsmiddleware.RecordResponseTiming{}, middleware.After) +func addRecordResponseTiming(stack *middleware.Stack, options Options) error { + return stack.Deserialize.Add(&awsmiddleware.RecordResponseTiming{ + DisableClockSkewCorrection: options.DisableClockSkewCorrection, + }, middleware.After) } func addSpanRetryLoop(stack *middleware.Stack, options Options) error { @@ -714,6 +764,7 @@ func addRetry(stack *middleware.Stack, o Options, c *Client) error { m.LogAttempts = o.ClientLogMode.IsRetries() m.OperationMeter = o.MeterProvider.Meter("github.com/aws/aws-sdk-go-v2/service/sso") m.ClientSkew = c.timeOffset + m.DisableClockSkewCorrection = o.DisableClockSkewCorrection }) if err := stack.Finalize.Insert(attempt, "ResolveAuthScheme", middleware.Before); err != nil { return err @@ -816,6 +867,14 @@ func resolveMeterProvider(options *Options) { } } +func newServiceMetadataMiddleware(region, operation string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: operation, + } +} + func addRecursionDetection(stack *middleware.Stack) error { return stack.Build.Add(&awsmiddleware.RecursionDetection{}, middleware.After) } diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sso/api_op_GetRoleCredentials.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sso/api_op_GetRoleCredentials.go index 5482b7a032..cefc88ad8e 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sso/api_op_GetRoleCredentials.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sso/api_op_GetRoleCredentials.go @@ -4,8 +4,6 @@ package sso import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/aws-sdk-go-v2/service/sso/types" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" @@ -63,9 +61,6 @@ type GetRoleCredentialsOutput struct { } func (c *Client) addOperationGetRoleCredentialsMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsRestjson1_serializeOpGetRoleCredentials{}, middleware.After) if err != nil { return err @@ -74,38 +69,17 @@ func (c *Client) addOperationGetRoleCredentialsMiddlewares(stack *middleware.Sta if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "GetRoleCredentials"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } if err = addResolveEndpointMiddleware(stack, options); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -114,22 +88,13 @@ func (c *Client) addOperationGetRoleCredentialsMiddlewares(stack *middleware.Sta if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpGetRoleCredentialsValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opGetRoleCredentials(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "GetRoleCredentials"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -144,22 +109,8 @@ func (c *Client) addOperationGetRoleCredentialsMiddlewares(stack *middleware.Sta if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opGetRoleCredentials(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "GetRoleCredentials", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sso/api_op_ListAccountRoles.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sso/api_op_ListAccountRoles.go index 8759d52576..c766f1c44e 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sso/api_op_ListAccountRoles.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sso/api_op_ListAccountRoles.go @@ -5,7 +5,6 @@ package sso import ( "context" "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/aws-sdk-go-v2/service/sso/types" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" @@ -68,9 +67,6 @@ type ListAccountRolesOutput struct { } func (c *Client) addOperationListAccountRolesMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsRestjson1_serializeOpListAccountRoles{}, middleware.After) if err != nil { return err @@ -79,38 +75,17 @@ func (c *Client) addOperationListAccountRolesMiddlewares(stack *middleware.Stack if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "ListAccountRoles"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } if err = addResolveEndpointMiddleware(stack, options); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -119,22 +94,13 @@ func (c *Client) addOperationListAccountRolesMiddlewares(stack *middleware.Stack if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpListAccountRolesValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opListAccountRoles(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "ListAccountRoles"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -149,12 +115,6 @@ func (c *Client) addOperationListAccountRolesMiddlewares(stack *middleware.Stack if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } @@ -253,11 +213,3 @@ type ListAccountRolesAPIClient interface { } var _ ListAccountRolesAPIClient = (*Client)(nil) - -func newServiceMetadataMiddleware_opListAccountRoles(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "ListAccountRoles", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sso/api_op_ListAccounts.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sso/api_op_ListAccounts.go index fea5b43912..e8af94746c 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sso/api_op_ListAccounts.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sso/api_op_ListAccounts.go @@ -5,7 +5,6 @@ package sso import ( "context" "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/aws-sdk-go-v2/service/sso/types" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" @@ -67,9 +66,6 @@ type ListAccountsOutput struct { } func (c *Client) addOperationListAccountsMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsRestjson1_serializeOpListAccounts{}, middleware.After) if err != nil { return err @@ -78,38 +74,17 @@ func (c *Client) addOperationListAccountsMiddlewares(stack *middleware.Stack, op if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "ListAccounts"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } if err = addResolveEndpointMiddleware(stack, options); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -118,22 +93,13 @@ func (c *Client) addOperationListAccountsMiddlewares(stack *middleware.Stack, op if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpListAccountsValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opListAccounts(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "ListAccounts"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -148,12 +114,6 @@ func (c *Client) addOperationListAccountsMiddlewares(stack *middleware.Stack, op if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } @@ -251,11 +211,3 @@ type ListAccountsAPIClient interface { } var _ ListAccountsAPIClient = (*Client)(nil) - -func newServiceMetadataMiddleware_opListAccounts(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "ListAccounts", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sso/api_op_Logout.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sso/api_op_Logout.go index 84aef7ce5f..2f38f944e1 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sso/api_op_Logout.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sso/api_op_Logout.go @@ -4,8 +4,6 @@ package sso import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" ) @@ -62,9 +60,6 @@ type LogoutOutput struct { } func (c *Client) addOperationLogoutMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsRestjson1_serializeOpLogout{}, middleware.After) if err != nil { return err @@ -73,38 +68,17 @@ func (c *Client) addOperationLogoutMiddlewares(stack *middleware.Stack, options if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "Logout"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } if err = addResolveEndpointMiddleware(stack, options); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -113,22 +87,13 @@ func (c *Client) addOperationLogoutMiddlewares(stack *middleware.Stack, options if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpLogoutValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opLogout(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "Logout"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -143,22 +108,8 @@ func (c *Client) addOperationLogoutMiddlewares(stack *middleware.Stack, options if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opLogout(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "Logout", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sso/auth.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sso/auth.go index c658615fde..a17cf6ee9c 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sso/auth.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sso/auth.go @@ -208,7 +208,7 @@ func (m *resolveAuthSchemeMiddleware) selectScheme(options []*smithyauth.Option) } for _, scheme := range m.options.AuthSchemes { - if scheme.SchemeID() != option.SchemeID { + if !matchSchemeID(scheme.SchemeID(), option.SchemeID) { continue } @@ -221,6 +221,16 @@ func (m *resolveAuthSchemeMiddleware) selectScheme(options []*smithyauth.Option) return nil, false } +func matchSchemeID(registered, option string) bool { + if registered == option { + return true + } + if i := strings.LastIndex(registered, "#"); i != -1 { + return registered[i+1:] == option + } + return false +} + func sortAuthOptions(options []*smithyauth.Option, preferred []string) []*smithyauth.Option { byPriority := make([]*smithyauth.Option, 0, len(options)) for _, prefName := range preferred { diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sso/deserializers.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sso/deserializers.go index a889f3c7a7..bfa1758c8a 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sso/deserializers.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sso/deserializers.go @@ -16,7 +16,6 @@ import ( "github.com/aws/smithy-go/tracing" smithyhttp "github.com/aws/smithy-go/transport/http" "io" - "io/ioutil" "strings" ) @@ -551,7 +550,7 @@ func (m *awsRestjson1_deserializeOpLogout) HandleDeserialize(ctx context.Context output := &LogoutOutput{} out.Result = output - if _, err = io.Copy(ioutil.Discard, response.Body); err != nil { + if _, err = io.Copy(io.Discard, response.Body); err != nil { return out, metadata, &smithy.DeserializationError{ Err: fmt.Errorf("failed to discard response body, %w", err), } diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sso/endpoints.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sso/endpoints.go index 551f05974e..f67fe538d7 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sso/endpoints.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sso/endpoints.go @@ -14,6 +14,7 @@ import ( internalendpoints "github.com/aws/aws-sdk-go-v2/service/sso/internal/endpoints" smithyauth "github.com/aws/smithy-go/auth" smithyendpoints "github.com/aws/smithy-go/endpoints" + "github.com/aws/smithy-go/endpoints/private/bdd" "github.com/aws/smithy-go/endpoints/private/rulesfn" "github.com/aws/smithy-go/middleware" "github.com/aws/smithy-go/ptr" @@ -229,6 +230,8 @@ func bindRegion(region string) (*string, error) { return aws.String(endpoints.MapFIPSRegion(region)), nil } +var _ = rulesfn.StringSlice(nil) + // EndpointParameters provides the parameters that influence how endpoints are // resolved. type EndpointParameters struct { @@ -294,21 +297,157 @@ func (p EndpointParameters) WithDefaults() EndpointParameters { return p } -type stringSlice []string +const bddRoot int32 = 2 -func (s stringSlice) Get(i int) *string { - if i < 0 || i >= len(s) { - return nil - } +var bddNodes = [42]int32{ + -1, 1, -1, 0, 13, 3, 1, 4, 100000012, 2, 5, 100000012, 3, 8, 6, 4, 7, 100000011, 5, 100000009, 100000010, 4, 11, 9, 6, 10, 100000008, 7, 100000006, 100000007, 5, 12, 100000005, 6, 100000004, 100000005, 3, 100000001, 14, 4, 100000002, 100000003} - v := s[i] - return &v +type conditionContext struct { + PartitionResult *awsrulesfn.PartitionConfig +} + +func evalCondition(idx int, params *EndpointParameters, c *conditionContext) bool { + switch idx { + case 0: + return params.Endpoint != nil + case 1: + return params.Region != nil + case 2: + if v := awsrulesfn.GetPartition(*params.Region); v != nil { + c.PartitionResult = v + return true + } + return false + case 3: + return *params.UseFIPS == true + case 4: + return *params.UseDualStack == true + case 5: + return c.PartitionResult.SupportsDualStack == true + case 6: + return c.PartitionResult.SupportsFIPS == true + case 7: + return c.PartitionResult.Name == "aws-us-gov" + } + return false +} + +func resolveResult(idx int32, params *EndpointParameters, c *conditionContext) (smithyendpoints.Endpoint, error) { + switch idx { + case 0: + return smithyendpoints.Endpoint{}, fmt.Errorf("endpoint resolution failed: no matching rule") + case 1: + return smithyendpoints.Endpoint{}, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: FIPS and custom endpoint are not supported") + case 2: + return smithyendpoints.Endpoint{}, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: Dualstack and custom endpoint are not supported") + case 3: + uriString := *params.Endpoint + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + case 4: + uriString := func() string { + var out strings.Builder + out.WriteString("https://portal.sso-fips.") + out.WriteString(*params.Region) + out.WriteString(".") + out.WriteString(c.PartitionResult.DualStackDnsSuffix) + return out.String() + }() + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + case 5: + return smithyendpoints.Endpoint{}, fmt.Errorf("endpoint rule error, %s", "FIPS and DualStack are enabled, but this partition does not support one or both") + case 6: + uriString := func() string { + var out strings.Builder + out.WriteString("https://portal.sso.") + out.WriteString(*params.Region) + out.WriteString(".amazonaws.com") + return out.String() + }() + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + case 7: + uriString := func() string { + var out strings.Builder + out.WriteString("https://portal.sso-fips.") + out.WriteString(*params.Region) + out.WriteString(".") + out.WriteString(c.PartitionResult.DnsSuffix) + return out.String() + }() + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + case 8: + return smithyendpoints.Endpoint{}, fmt.Errorf("endpoint rule error, %s", "FIPS is enabled but this partition does not support FIPS") + case 9: + uriString := func() string { + var out strings.Builder + out.WriteString("https://portal.sso.") + out.WriteString(*params.Region) + out.WriteString(".") + out.WriteString(c.PartitionResult.DualStackDnsSuffix) + return out.String() + }() + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + case 10: + return smithyendpoints.Endpoint{}, fmt.Errorf("endpoint rule error, %s", "DualStack is enabled but this partition does not support DualStack") + case 11: + uriString := func() string { + var out strings.Builder + out.WriteString("https://portal.sso.") + out.WriteString(*params.Region) + out.WriteString(".") + out.WriteString(c.PartitionResult.DnsSuffix) + return out.String() + }() + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + case 12: + return smithyendpoints.Endpoint{}, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: Missing Region") + } + return smithyendpoints.Endpoint{}, fmt.Errorf("endpoint rule error, invalid result index: %d", idx) } // EndpointResolverV2 provides the interface for resolving service endpoints. type EndpointResolverV2 interface { - // ResolveEndpoint attempts to resolve the endpoint with the provided options, - // returning the endpoint if found. Otherwise an error is returned. ResolveEndpoint(ctx context.Context, params EndpointParameters) ( smithyendpoints.Endpoint, error, ) @@ -332,152 +471,12 @@ func (r *resolver) ResolveEndpoint( if err = params.ValidateRequired(); err != nil { return endpoint, fmt.Errorf("endpoint parameters are not valid, %w", err) } - _UseDualStack := *params.UseDualStack - _ = _UseDualStack - _UseFIPS := *params.UseFIPS - _ = _UseFIPS - - if exprVal := params.Endpoint; exprVal != nil { - _Endpoint := *exprVal - _ = _Endpoint - if _UseFIPS == true { - return endpoint, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: FIPS and custom endpoint are not supported") - } - if _UseDualStack == true { - return endpoint, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: Dualstack and custom endpoint are not supported") - } - uriString := _Endpoint - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - }, nil - } - if exprVal := params.Region; exprVal != nil { - _Region := *exprVal - _ = _Region - if exprVal := awsrulesfn.GetPartition(_Region); exprVal != nil { - _PartitionResult := *exprVal - _ = _PartitionResult - if _UseFIPS == true { - if _UseDualStack == true { - if true == _PartitionResult.SupportsFIPS { - if true == _PartitionResult.SupportsDualStack { - uriString := func() string { - var out strings.Builder - out.WriteString("https://portal.sso-fips.") - out.WriteString(_Region) - out.WriteString(".") - out.WriteString(_PartitionResult.DualStackDnsSuffix) - return out.String() - }() - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - }, nil - } - } - return endpoint, fmt.Errorf("endpoint rule error, %s", "FIPS and DualStack are enabled, but this partition does not support one or both") - } - } - if _UseFIPS == true { - if _PartitionResult.SupportsFIPS == true { - if _PartitionResult.Name == "aws-us-gov" { - uriString := func() string { - var out strings.Builder - out.WriteString("https://portal.sso.") - out.WriteString(_Region) - out.WriteString(".amazonaws.com") - return out.String() - }() - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - }, nil - } - uriString := func() string { - var out strings.Builder - out.WriteString("https://portal.sso-fips.") - out.WriteString(_Region) - out.WriteString(".") - out.WriteString(_PartitionResult.DnsSuffix) - return out.String() - }() - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - }, nil - } - return endpoint, fmt.Errorf("endpoint rule error, %s", "FIPS is enabled but this partition does not support FIPS") - } - if _UseDualStack == true { - if true == _PartitionResult.SupportsDualStack { - uriString := func() string { - var out strings.Builder - out.WriteString("https://portal.sso.") - out.WriteString(_Region) - out.WriteString(".") - out.WriteString(_PartitionResult.DualStackDnsSuffix) - return out.String() - }() - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - }, nil - } - return endpoint, fmt.Errorf("endpoint rule error, %s", "DualStack is enabled but this partition does not support DualStack") - } - uriString := func() string { - var out strings.Builder - out.WriteString("https://portal.sso.") - out.WriteString(_Region) - out.WriteString(".") - out.WriteString(_PartitionResult.DnsSuffix) - return out.String() - }() - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - }, nil - } - return endpoint, fmt.Errorf("Endpoint resolution failed. Invalid operation or environment input.") - } - return endpoint, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: Missing Region") + c := &conditionContext{} + ref := bdd.Evaluate(bddNodes[:], bddRoot, func(idx int) bool { + return evalCondition(idx, ¶ms, c) + }) + return resolveResult(ref, ¶ms, c) } type endpointParamsBinder interface { diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sso/generated.json b/vendor/github.com/aws/aws-sdk-go-v2/service/sso/generated.json index 39a393d441..7aca52b604 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sso/generated.json +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sso/generated.json @@ -22,6 +22,8 @@ "internal/endpoints/endpoints.go", "internal/endpoints/endpoints_test.go", "options.go", + "request_snapshot_test.go", + "response_snapshot_test.go", "serializers.go", "snapshot_test.go", "sra_operation_order_test.go", diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sso/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sso/go_module_metadata.go index 9d12dd55bc..7366a4fae8 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sso/go_module_metadata.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sso/go_module_metadata.go @@ -3,4 +3,4 @@ package sso // goModuleVersion is the tagged release for this module -const goModuleVersion = "1.30.17" +const goModuleVersion = "1.33.2" diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sso/options.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sso/options.go index 8b4e34d064..fbe3ff2a2e 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sso/options.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sso/options.go @@ -44,6 +44,11 @@ type Options struct { // clients initial default settings. DefaultsMode aws.DefaultsMode + // Disables SDK clock skew correction. When set, the SDK will not adjust request + // signing timestamps to compensate for clock drift between the client and the + // service. + DisableClockSkewCorrection bool + // The endpoint options to be used when attempting to resolve an endpoint. EndpointOptions EndpointResolverOptions diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/CHANGELOG.md index e645209405..7879f2345d 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/CHANGELOG.md +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/CHANGELOG.md @@ -1,3 +1,64 @@ +# v1.38.2 (2026-07-29) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.38.1 (2026-07-28) + +* **Dependency Update**: Update to smithy-go v1.27.5. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.38.0 (2026-07-21) + +* **Feature**: Add an option to clients to disable clock skew +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.37.1 (2026-07-13) + +* No change notes available for this release. + +# v1.37.0 (2026-07-06) + +* **Feature**: Add request serialization snapshot tests. + +# v1.36.8 (2026-07-01) + +* **Bug Fix**: Bump smithy-go to 1.27.3, fix JSON encorder for document.Number, endpoint host label format validation and CBOR union serialization on new serde +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.36.7 (2026-06-29) + +* No change notes available for this release. + +# v1.36.6 (2026-06-08) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.36.5 (2026-06-04) + +* **Dependency Update**: Update to smithy-go v1.27.1 to fix several union-related deserialization bugs in schema-serde-enabled services. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.36.4 (2026-06-03) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.36.3 (2026-06-02) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.36.2 (2026-05-29) + +* **Dependency Update**: Update to smithy-go v1.26.0. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.36.1 (2026-05-28) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.36.0 (2026-05-22) + +* **Feature**: Adding new BDD representation of endpoint ruleset + # v1.35.21 (2026-04-29) * **Dependency Update**: Update to smithy-go v1.25.1. diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/api_client.go b/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/api_client.go index 2c0958ade2..0ab277871b 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/api_client.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/api_client.go @@ -259,6 +259,10 @@ func (c *Client) invokeOperation( finalizeClientEndpointResolverOptions(&options) + if err := c.addCommonMiddlewares(stack, options, opID); err != nil { + return nil, metadata, err + } + for _, fn := range stackFns { if err := fn(stack, options); err != nil { return nil, metadata, err @@ -363,6 +367,49 @@ func addProtocolFinalizerMiddlewares(stack *middleware.Stack, options Options, o } return nil } + +func (c *Client) addCommonMiddlewares(stack *middleware.Stack, options Options, operation string) error { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, operation); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + if err := addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err := addClientRequestID(stack); err != nil { + return err + } + if err := addRetry(stack, options, c); err != nil { + return err + } + if err := addRawResponseToMetadata(stack); err != nil { + return err + } + if err := addSpanRetryLoop(stack, options); err != nil { + return err + } + if err := addClientUserAgent(stack, options); err != nil { + return err + } + if err := addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err := addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err := addRecursionDetection(stack); err != nil { + return err + } + if err := addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err := addInterceptAttempt(stack, options); err != nil { + return err + } + return nil +} func resolveAuthSchemeResolver(options *Options) { if options.AuthSchemeResolver == nil { options.AuthSchemeResolver = &defaultAuthSchemeResolver{} @@ -436,16 +483,17 @@ func setResolvedDefaultsMode(o *Options) { // NewFromConfig returns a new client from the provided config. func NewFromConfig(cfg aws.Config, optFns ...func(*Options)) *Client { opts := Options{ - Region: cfg.Region, - DefaultsMode: cfg.DefaultsMode, - RuntimeEnvironment: cfg.RuntimeEnvironment, - HTTPClient: cfg.HTTPClient, - Credentials: cfg.Credentials, - APIOptions: cfg.APIOptions, - Logger: cfg.Logger, - ClientLogMode: cfg.ClientLogMode, - AppID: cfg.AppID, - AuthSchemePreference: cfg.AuthSchemePreference, + Region: cfg.Region, + DefaultsMode: cfg.DefaultsMode, + RuntimeEnvironment: cfg.RuntimeEnvironment, + HTTPClient: cfg.HTTPClient, + Credentials: cfg.Credentials, + APIOptions: cfg.APIOptions, + Logger: cfg.Logger, + ClientLogMode: cfg.ClientLogMode, + AppID: cfg.AppID, + DisableClockSkewCorrection: cfg.DisableClockSkewCorrection, + AuthSchemePreference: cfg.AuthSchemePreference, } resolveAWSRetryerProvider(cfg, &opts) resolveAWSRetryMaxAttempts(cfg, &opts) @@ -635,15 +683,17 @@ func addClientRequestID(stack *middleware.Stack) error { } func addComputeContentLength(stack *middleware.Stack) error { - return stack.Build.Add(&smithyhttp.ComputeContentLength{}, middleware.After) + return stack.Build.Insert(&smithyhttp.ComputeContentLength{}, "ClientRequestID", middleware.After) } func addRawResponseToMetadata(stack *middleware.Stack) error { return stack.Deserialize.Add(&awsmiddleware.AddRawResponse{}, middleware.Before) } -func addRecordResponseTiming(stack *middleware.Stack) error { - return stack.Deserialize.Add(&awsmiddleware.RecordResponseTiming{}, middleware.After) +func addRecordResponseTiming(stack *middleware.Stack, options Options) error { + return stack.Deserialize.Add(&awsmiddleware.RecordResponseTiming{ + DisableClockSkewCorrection: options.DisableClockSkewCorrection, + }, middleware.After) } func addSpanRetryLoop(stack *middleware.Stack, options Options) error { @@ -714,6 +764,7 @@ func addRetry(stack *middleware.Stack, o Options, c *Client) error { m.LogAttempts = o.ClientLogMode.IsRetries() m.OperationMeter = o.MeterProvider.Meter("github.com/aws/aws-sdk-go-v2/service/ssooidc") m.ClientSkew = c.timeOffset + m.DisableClockSkewCorrection = o.DisableClockSkewCorrection }) if err := stack.Finalize.Insert(attempt, "ResolveAuthScheme", middleware.Before); err != nil { return err @@ -816,6 +867,14 @@ func resolveMeterProvider(options *Options) { } } +func newServiceMetadataMiddleware(region, operation string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: operation, + } +} + func addRecursionDetection(stack *middleware.Stack) error { return stack.Build.Add(&awsmiddleware.RecursionDetection{}, middleware.After) } diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/api_op_CreateToken.go b/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/api_op_CreateToken.go index cd739d53f5..bba750bc70 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/api_op_CreateToken.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/api_op_CreateToken.go @@ -4,8 +4,6 @@ package ssooidc import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" ) @@ -133,9 +131,6 @@ type CreateTokenOutput struct { } func (c *Client) addOperationCreateTokenMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsRestjson1_serializeOpCreateToken{}, middleware.After) if err != nil { return err @@ -144,38 +139,17 @@ func (c *Client) addOperationCreateTokenMiddlewares(stack *middleware.Stack, opt if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "CreateToken"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } if err = addResolveEndpointMiddleware(stack, options); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -184,22 +158,13 @@ func (c *Client) addOperationCreateTokenMiddlewares(stack *middleware.Stack, opt if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpCreateTokenValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opCreateToken(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "CreateToken"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -214,22 +179,8 @@ func (c *Client) addOperationCreateTokenMiddlewares(stack *middleware.Stack, opt if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opCreateToken(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "CreateToken", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/api_op_CreateTokenWithIAM.go b/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/api_op_CreateTokenWithIAM.go index a02f62a286..2c6de5a839 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/api_op_CreateTokenWithIAM.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/api_op_CreateTokenWithIAM.go @@ -4,8 +4,6 @@ package ssooidc import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/aws-sdk-go-v2/service/ssooidc/types" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" @@ -177,9 +175,6 @@ type CreateTokenWithIAMOutput struct { } func (c *Client) addOperationCreateTokenWithIAMMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsRestjson1_serializeOpCreateTokenWithIAM{}, middleware.After) if err != nil { return err @@ -188,19 +183,10 @@ func (c *Client) addOperationCreateTokenWithIAMMiddlewares(stack *middleware.Sta if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "CreateTokenWithIAM"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -210,19 +196,7 @@ func (c *Client) addOperationCreateTokenWithIAMMiddlewares(stack *middleware.Sta if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -231,22 +205,13 @@ func (c *Client) addOperationCreateTokenWithIAMMiddlewares(stack *middleware.Sta if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpCreateTokenWithIAMValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opCreateTokenWithIAM(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "CreateTokenWithIAM"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -261,22 +226,8 @@ func (c *Client) addOperationCreateTokenWithIAMMiddlewares(stack *middleware.Sta if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opCreateTokenWithIAM(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "CreateTokenWithIAM", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/api_op_RegisterClient.go b/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/api_op_RegisterClient.go index f32e86be9c..2df6cba224 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/api_op_RegisterClient.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/api_op_RegisterClient.go @@ -4,8 +4,6 @@ package ssooidc import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" ) @@ -104,9 +102,6 @@ type RegisterClientOutput struct { } func (c *Client) addOperationRegisterClientMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsRestjson1_serializeOpRegisterClient{}, middleware.After) if err != nil { return err @@ -115,38 +110,17 @@ func (c *Client) addOperationRegisterClientMiddlewares(stack *middleware.Stack, if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "RegisterClient"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } if err = addResolveEndpointMiddleware(stack, options); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -155,22 +129,13 @@ func (c *Client) addOperationRegisterClientMiddlewares(stack *middleware.Stack, if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpRegisterClientValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opRegisterClient(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "RegisterClient"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -185,22 +150,8 @@ func (c *Client) addOperationRegisterClientMiddlewares(stack *middleware.Stack, if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opRegisterClient(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "RegisterClient", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/api_op_StartDeviceAuthorization.go b/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/api_op_StartDeviceAuthorization.go index a35750b227..232ff802eb 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/api_op_StartDeviceAuthorization.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/api_op_StartDeviceAuthorization.go @@ -4,8 +4,6 @@ package ssooidc import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" ) @@ -86,9 +84,6 @@ type StartDeviceAuthorizationOutput struct { } func (c *Client) addOperationStartDeviceAuthorizationMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsRestjson1_serializeOpStartDeviceAuthorization{}, middleware.After) if err != nil { return err @@ -97,38 +92,17 @@ func (c *Client) addOperationStartDeviceAuthorizationMiddlewares(stack *middlewa if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "StartDeviceAuthorization"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } if err = addResolveEndpointMiddleware(stack, options); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -137,22 +111,13 @@ func (c *Client) addOperationStartDeviceAuthorizationMiddlewares(stack *middlewa if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpStartDeviceAuthorizationValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opStartDeviceAuthorization(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "StartDeviceAuthorization"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -167,22 +132,8 @@ func (c *Client) addOperationStartDeviceAuthorizationMiddlewares(stack *middlewa if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opStartDeviceAuthorization(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "StartDeviceAuthorization", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/auth.go b/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/auth.go index 5f253df305..2ceab3e1bd 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/auth.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/auth.go @@ -202,7 +202,7 @@ func (m *resolveAuthSchemeMiddleware) selectScheme(options []*smithyauth.Option) } for _, scheme := range m.options.AuthSchemes { - if scheme.SchemeID() != option.SchemeID { + if !matchSchemeID(scheme.SchemeID(), option.SchemeID) { continue } @@ -215,6 +215,16 @@ func (m *resolveAuthSchemeMiddleware) selectScheme(options []*smithyauth.Option) return nil, false } +func matchSchemeID(registered, option string) bool { + if registered == option { + return true + } + if i := strings.LastIndex(registered, "#"); i != -1 { + return registered[i+1:] == option + } + return false +} + func sortAuthOptions(options []*smithyauth.Option, preferred []string) []*smithyauth.Option { byPriority := make([]*smithyauth.Option, 0, len(options)) for _, prefName := range preferred { diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/endpoints.go b/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/endpoints.go index 884983eb4d..1bc32925de 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/endpoints.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/endpoints.go @@ -14,6 +14,7 @@ import ( internalendpoints "github.com/aws/aws-sdk-go-v2/service/ssooidc/internal/endpoints" smithyauth "github.com/aws/smithy-go/auth" smithyendpoints "github.com/aws/smithy-go/endpoints" + "github.com/aws/smithy-go/endpoints/private/bdd" "github.com/aws/smithy-go/endpoints/private/rulesfn" "github.com/aws/smithy-go/middleware" "github.com/aws/smithy-go/ptr" @@ -229,6 +230,8 @@ func bindRegion(region string) (*string, error) { return aws.String(endpoints.MapFIPSRegion(region)), nil } +var _ = rulesfn.StringSlice(nil) + // EndpointParameters provides the parameters that influence how endpoints are // resolved. type EndpointParameters struct { @@ -294,21 +297,157 @@ func (p EndpointParameters) WithDefaults() EndpointParameters { return p } -type stringSlice []string +const bddRoot int32 = 2 -func (s stringSlice) Get(i int) *string { - if i < 0 || i >= len(s) { - return nil - } +var bddNodes = [42]int32{ + -1, 1, -1, 0, 13, 3, 1, 4, 100000012, 2, 5, 100000012, 3, 8, 6, 4, 7, 100000011, 5, 100000009, 100000010, 4, 11, 9, 6, 10, 100000008, 7, 100000006, 100000007, 5, 12, 100000005, 6, 100000004, 100000005, 3, 100000001, 14, 4, 100000002, 100000003} - v := s[i] - return &v +type conditionContext struct { + PartitionResult *awsrulesfn.PartitionConfig +} + +func evalCondition(idx int, params *EndpointParameters, c *conditionContext) bool { + switch idx { + case 0: + return params.Endpoint != nil + case 1: + return params.Region != nil + case 2: + if v := awsrulesfn.GetPartition(*params.Region); v != nil { + c.PartitionResult = v + return true + } + return false + case 3: + return *params.UseFIPS == true + case 4: + return *params.UseDualStack == true + case 5: + return c.PartitionResult.SupportsDualStack == true + case 6: + return c.PartitionResult.SupportsFIPS == true + case 7: + return c.PartitionResult.Name == "aws-us-gov" + } + return false +} + +func resolveResult(idx int32, params *EndpointParameters, c *conditionContext) (smithyendpoints.Endpoint, error) { + switch idx { + case 0: + return smithyendpoints.Endpoint{}, fmt.Errorf("endpoint resolution failed: no matching rule") + case 1: + return smithyendpoints.Endpoint{}, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: FIPS and custom endpoint are not supported") + case 2: + return smithyendpoints.Endpoint{}, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: Dualstack and custom endpoint are not supported") + case 3: + uriString := *params.Endpoint + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + case 4: + uriString := func() string { + var out strings.Builder + out.WriteString("https://oidc-fips.") + out.WriteString(*params.Region) + out.WriteString(".") + out.WriteString(c.PartitionResult.DualStackDnsSuffix) + return out.String() + }() + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + case 5: + return smithyendpoints.Endpoint{}, fmt.Errorf("endpoint rule error, %s", "FIPS and DualStack are enabled, but this partition does not support one or both") + case 6: + uriString := func() string { + var out strings.Builder + out.WriteString("https://oidc.") + out.WriteString(*params.Region) + out.WriteString(".amazonaws.com") + return out.String() + }() + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + case 7: + uriString := func() string { + var out strings.Builder + out.WriteString("https://oidc-fips.") + out.WriteString(*params.Region) + out.WriteString(".") + out.WriteString(c.PartitionResult.DnsSuffix) + return out.String() + }() + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + case 8: + return smithyendpoints.Endpoint{}, fmt.Errorf("endpoint rule error, %s", "FIPS is enabled but this partition does not support FIPS") + case 9: + uriString := func() string { + var out strings.Builder + out.WriteString("https://oidc.") + out.WriteString(*params.Region) + out.WriteString(".") + out.WriteString(c.PartitionResult.DualStackDnsSuffix) + return out.String() + }() + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + case 10: + return smithyendpoints.Endpoint{}, fmt.Errorf("endpoint rule error, %s", "DualStack is enabled but this partition does not support DualStack") + case 11: + uriString := func() string { + var out strings.Builder + out.WriteString("https://oidc.") + out.WriteString(*params.Region) + out.WriteString(".") + out.WriteString(c.PartitionResult.DnsSuffix) + return out.String() + }() + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + case 12: + return smithyendpoints.Endpoint{}, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: Missing Region") + } + return smithyendpoints.Endpoint{}, fmt.Errorf("endpoint rule error, invalid result index: %d", idx) } // EndpointResolverV2 provides the interface for resolving service endpoints. type EndpointResolverV2 interface { - // ResolveEndpoint attempts to resolve the endpoint with the provided options, - // returning the endpoint if found. Otherwise an error is returned. ResolveEndpoint(ctx context.Context, params EndpointParameters) ( smithyendpoints.Endpoint, error, ) @@ -332,152 +471,12 @@ func (r *resolver) ResolveEndpoint( if err = params.ValidateRequired(); err != nil { return endpoint, fmt.Errorf("endpoint parameters are not valid, %w", err) } - _UseDualStack := *params.UseDualStack - _ = _UseDualStack - _UseFIPS := *params.UseFIPS - _ = _UseFIPS - - if exprVal := params.Endpoint; exprVal != nil { - _Endpoint := *exprVal - _ = _Endpoint - if _UseFIPS == true { - return endpoint, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: FIPS and custom endpoint are not supported") - } - if _UseDualStack == true { - return endpoint, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: Dualstack and custom endpoint are not supported") - } - uriString := _Endpoint - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - }, nil - } - if exprVal := params.Region; exprVal != nil { - _Region := *exprVal - _ = _Region - if exprVal := awsrulesfn.GetPartition(_Region); exprVal != nil { - _PartitionResult := *exprVal - _ = _PartitionResult - if _UseFIPS == true { - if _UseDualStack == true { - if true == _PartitionResult.SupportsFIPS { - if true == _PartitionResult.SupportsDualStack { - uriString := func() string { - var out strings.Builder - out.WriteString("https://oidc-fips.") - out.WriteString(_Region) - out.WriteString(".") - out.WriteString(_PartitionResult.DualStackDnsSuffix) - return out.String() - }() - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - }, nil - } - } - return endpoint, fmt.Errorf("endpoint rule error, %s", "FIPS and DualStack are enabled, but this partition does not support one or both") - } - } - if _UseFIPS == true { - if _PartitionResult.SupportsFIPS == true { - if _PartitionResult.Name == "aws-us-gov" { - uriString := func() string { - var out strings.Builder - out.WriteString("https://oidc.") - out.WriteString(_Region) - out.WriteString(".amazonaws.com") - return out.String() - }() - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - }, nil - } - uriString := func() string { - var out strings.Builder - out.WriteString("https://oidc-fips.") - out.WriteString(_Region) - out.WriteString(".") - out.WriteString(_PartitionResult.DnsSuffix) - return out.String() - }() - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - }, nil - } - return endpoint, fmt.Errorf("endpoint rule error, %s", "FIPS is enabled but this partition does not support FIPS") - } - if _UseDualStack == true { - if true == _PartitionResult.SupportsDualStack { - uriString := func() string { - var out strings.Builder - out.WriteString("https://oidc.") - out.WriteString(_Region) - out.WriteString(".") - out.WriteString(_PartitionResult.DualStackDnsSuffix) - return out.String() - }() - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - }, nil - } - return endpoint, fmt.Errorf("endpoint rule error, %s", "DualStack is enabled but this partition does not support DualStack") - } - uriString := func() string { - var out strings.Builder - out.WriteString("https://oidc.") - out.WriteString(_Region) - out.WriteString(".") - out.WriteString(_PartitionResult.DnsSuffix) - return out.String() - }() - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - }, nil - } - return endpoint, fmt.Errorf("Endpoint resolution failed. Invalid operation or environment input.") - } - return endpoint, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: Missing Region") + c := &conditionContext{} + ref := bdd.Evaluate(bddNodes[:], bddRoot, func(idx int) bool { + return evalCondition(idx, ¶ms, c) + }) + return resolveResult(ref, ¶ms, c) } type endpointParamsBinder interface { diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/generated.json b/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/generated.json index 1e34b9a9d5..8f46b38d32 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/generated.json +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/generated.json @@ -22,6 +22,8 @@ "internal/endpoints/endpoints.go", "internal/endpoints/endpoints_test.go", "options.go", + "request_snapshot_test.go", + "response_snapshot_test.go", "serializers.go", "snapshot_test.go", "sra_operation_order_test.go", diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/go_module_metadata.go index af00268dfc..7b63f480f7 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/go_module_metadata.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/go_module_metadata.go @@ -3,4 +3,4 @@ package ssooidc // goModuleVersion is the tagged release for this module -const goModuleVersion = "1.35.21" +const goModuleVersion = "1.38.2" diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/options.go b/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/options.go index c2eac09190..73a47ccfb8 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/options.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/ssooidc/options.go @@ -44,6 +44,11 @@ type Options struct { // clients initial default settings. DefaultsMode aws.DefaultsMode + // Disables SDK clock skew correction. When set, the SDK will not adjust request + // signing timestamps to compensate for clock drift between the client and the + // service. + DisableClockSkewCorrection bool + // The endpoint options to be used when attempting to resolve an endpoint. EndpointOptions EndpointResolverOptions diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/CHANGELOG.md b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/CHANGELOG.md index 199f7a79ce..4613abde31 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/CHANGELOG.md +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/CHANGELOG.md @@ -1,3 +1,61 @@ +# v1.45.2 (2026-07-29) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.45.1 (2026-07-28) + +* **Dependency Update**: Update to smithy-go v1.27.5. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.45.0 (2026-07-21) + +* **Feature**: Add an option to clients to disable clock skew +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.44.1 (2026-07-13) + +* No change notes available for this release. + +# v1.44.0 (2026-07-06) + +* **Feature**: Add request serialization snapshot tests. + +# v1.43.5 (2026-07-01) + +* **Bug Fix**: Bump smithy-go to 1.27.3, fix JSON encorder for document.Number, endpoint host label format validation and CBOR union serialization on new serde +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.43.4 (2026-06-29) + +* No change notes available for this release. + +# v1.43.3 (2026-06-08) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.43.2 (2026-06-04) + +* **Dependency Update**: Update to smithy-go v1.27.1 to fix several union-related deserialization bugs in schema-serde-enabled services. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.43.1 (2026-06-03) + +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.43.0 (2026-06-02) + +* **Feature**: Adding new BDD representation of endpoint ruleset +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.42.3 (2026-05-29) + +* **Dependency Update**: Update to smithy-go v1.26.0. +* **Dependency Update**: Updated to the latest SDK module versions + +# v1.42.2 (2026-05-28) + +* **Dependency Update**: Updated to the latest SDK module versions + # v1.42.1 (2026-04-29) * **Dependency Update**: Update to smithy-go v1.25.1. diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_client.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_client.go index 958c83c1a8..da6a5b3060 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_client.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_client.go @@ -224,6 +224,8 @@ func New(options Options, optFns ...func(*Options)) *Client { ignoreAnonymousAuth(&options) + finalizeSTSRetryableErrors(&options) + wrapWithAnonymousAuth(&options) resolveAuthSchemes(&options) @@ -266,6 +268,10 @@ func (c *Client) invokeOperation( finalizeClientEndpointResolverOptions(&options) + if err := c.addCommonMiddlewares(stack, options, opID); err != nil { + return nil, metadata, err + } + for _, fn := range stackFns { if err := fn(stack, options); err != nil { return nil, metadata, err @@ -370,6 +376,49 @@ func addProtocolFinalizerMiddlewares(stack *middleware.Stack, options Options, o } return nil } + +func (c *Client) addCommonMiddlewares(stack *middleware.Stack, options Options, operation string) error { + if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { + return err + } + if err := addProtocolFinalizerMiddlewares(stack, options, operation); err != nil { + return fmt.Errorf("add protocol finalizers: %v", err) + } + if err := addSetLoggerMiddleware(stack, options); err != nil { + return err + } + if err := addClientRequestID(stack); err != nil { + return err + } + if err := addRetry(stack, options, c); err != nil { + return err + } + if err := addRawResponseToMetadata(stack); err != nil { + return err + } + if err := addSpanRetryLoop(stack, options); err != nil { + return err + } + if err := addClientUserAgent(stack, options); err != nil { + return err + } + if err := addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { + return err + } + if err := addUserAgentRetryMode(stack, options); err != nil { + return err + } + if err := addRecursionDetection(stack); err != nil { + return err + } + if err := addInterceptBeforeRetryLoop(stack, options); err != nil { + return err + } + if err := addInterceptAttempt(stack, options); err != nil { + return err + } + return nil +} func resolveAuthSchemeResolver(options *Options) { if options.AuthSchemeResolver == nil { options.AuthSchemeResolver = &defaultAuthSchemeResolver{} @@ -448,16 +497,17 @@ func setResolvedDefaultsMode(o *Options) { // NewFromConfig returns a new client from the provided config. func NewFromConfig(cfg aws.Config, optFns ...func(*Options)) *Client { opts := Options{ - Region: cfg.Region, - DefaultsMode: cfg.DefaultsMode, - RuntimeEnvironment: cfg.RuntimeEnvironment, - HTTPClient: cfg.HTTPClient, - Credentials: cfg.Credentials, - APIOptions: cfg.APIOptions, - Logger: cfg.Logger, - ClientLogMode: cfg.ClientLogMode, - AppID: cfg.AppID, - AuthSchemePreference: cfg.AuthSchemePreference, + Region: cfg.Region, + DefaultsMode: cfg.DefaultsMode, + RuntimeEnvironment: cfg.RuntimeEnvironment, + HTTPClient: cfg.HTTPClient, + Credentials: cfg.Credentials, + APIOptions: cfg.APIOptions, + Logger: cfg.Logger, + ClientLogMode: cfg.ClientLogMode, + AppID: cfg.AppID, + DisableClockSkewCorrection: cfg.DisableClockSkewCorrection, + AuthSchemePreference: cfg.AuthSchemePreference, } resolveAWSRetryerProvider(cfg, &opts) resolveAWSRetryMaxAttempts(cfg, &opts) @@ -647,15 +697,17 @@ func addClientRequestID(stack *middleware.Stack) error { } func addComputeContentLength(stack *middleware.Stack) error { - return stack.Build.Add(&smithyhttp.ComputeContentLength{}, middleware.After) + return stack.Build.Insert(&smithyhttp.ComputeContentLength{}, "ClientRequestID", middleware.After) } func addRawResponseToMetadata(stack *middleware.Stack) error { return stack.Deserialize.Add(&awsmiddleware.AddRawResponse{}, middleware.Before) } -func addRecordResponseTiming(stack *middleware.Stack) error { - return stack.Deserialize.Add(&awsmiddleware.RecordResponseTiming{}, middleware.After) +func addRecordResponseTiming(stack *middleware.Stack, options Options) error { + return stack.Deserialize.Add(&awsmiddleware.RecordResponseTiming{ + DisableClockSkewCorrection: options.DisableClockSkewCorrection, + }, middleware.After) } func addSpanRetryLoop(stack *middleware.Stack, options Options) error { @@ -726,6 +778,7 @@ func addRetry(stack *middleware.Stack, o Options, c *Client) error { m.LogAttempts = o.ClientLogMode.IsRetries() m.OperationMeter = o.MeterProvider.Meter("github.com/aws/aws-sdk-go-v2/service/sts") m.ClientSkew = c.timeOffset + m.DisableClockSkewCorrection = o.DisableClockSkewCorrection }) if err := stack.Finalize.Insert(attempt, "ResolveAuthScheme", middleware.Before); err != nil { return err @@ -836,6 +889,10 @@ func addCredentialSource(stack *middleware.Stack, options Options) error { return stack.Build.Insert(&mw, "UserAgent", middleware.Before) } +func finalizeSTSRetryableErrors(o *Options) { + o.Retryer = retry.AddWithErrorCodes(o.Retryer, "IDPCommunicationError") +} + func resolveTracerProvider(options *Options) { if options.TracerProvider == nil { options.TracerProvider = &tracing.NopTracerProvider{} @@ -848,6 +905,14 @@ func resolveMeterProvider(options *Options) { } } +func newServiceMetadataMiddleware(region, operation string) *awsmiddleware.RegisterServiceMetadata { + return &awsmiddleware.RegisterServiceMetadata{ + Region: region, + ServiceID: ServiceID, + OperationName: operation, + } +} + func addRecursionDetection(stack *middleware.Stack) error { return stack.Build.Add(&awsmiddleware.RecursionDetection{}, middleware.After) } diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_op_AssumeRole.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_op_AssumeRole.go index 83aa65a5a2..d10f11a620 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_op_AssumeRole.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_op_AssumeRole.go @@ -4,8 +4,6 @@ package sts import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/aws-sdk-go-v2/aws/signer/v4" "github.com/aws/aws-sdk-go-v2/service/sts/types" "github.com/aws/smithy-go/middleware" @@ -415,9 +413,6 @@ type AssumeRoleOutput struct { } func (c *Client) addOperationAssumeRoleMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpAssumeRole{}, middleware.After) if err != nil { return err @@ -426,19 +421,10 @@ func (c *Client) addOperationAssumeRoleMiddlewares(stack *middleware.Stack, opti if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "AssumeRole"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -448,19 +434,7 @@ func (c *Client) addOperationAssumeRoleMiddlewares(stack *middleware.Stack, opti if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -469,22 +443,13 @@ func (c *Client) addOperationAssumeRoleMiddlewares(stack *middleware.Stack, opti if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpAssumeRoleValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opAssumeRole(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "AssumeRole"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -499,26 +464,12 @@ func (c *Client) addOperationAssumeRoleMiddlewares(stack *middleware.Stack, opti if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } -func newServiceMetadataMiddleware_opAssumeRole(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "AssumeRole", - } -} - // PresignAssumeRole is used to generate a presigned HTTP Request which contains // presigned URL, signed headers and HTTP method used. func (c *PresignClient) PresignAssumeRole(ctx context.Context, params *AssumeRoleInput, optFns ...func(*PresignOptions)) (*v4.PresignedHTTPRequest, error) { diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_op_AssumeRoleWithSAML.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_op_AssumeRoleWithSAML.go index 520e6e1c61..bd5bfdc224 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_op_AssumeRoleWithSAML.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_op_AssumeRoleWithSAML.go @@ -4,8 +4,6 @@ package sts import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/aws-sdk-go-v2/service/sts/types" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" @@ -353,9 +351,6 @@ type AssumeRoleWithSAMLOutput struct { } func (c *Client) addOperationAssumeRoleWithSAMLMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpAssumeRoleWithSAML{}, middleware.After) if err != nil { return err @@ -364,38 +359,17 @@ func (c *Client) addOperationAssumeRoleWithSAMLMiddlewares(stack *middleware.Sta if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "AssumeRoleWithSAML"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } if err = addResolveEndpointMiddleware(stack, options); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -404,22 +378,13 @@ func (c *Client) addOperationAssumeRoleWithSAMLMiddlewares(stack *middleware.Sta if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpAssumeRoleWithSAMLValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opAssumeRoleWithSAML(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "AssumeRoleWithSAML"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -434,22 +399,8 @@ func (c *Client) addOperationAssumeRoleWithSAMLMiddlewares(stack *middleware.Sta if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opAssumeRoleWithSAML(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "AssumeRoleWithSAML", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_op_AssumeRoleWithWebIdentity.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_op_AssumeRoleWithWebIdentity.go index 8a164be5be..6f6ea05b5e 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_op_AssumeRoleWithWebIdentity.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_op_AssumeRoleWithWebIdentity.go @@ -4,8 +4,6 @@ package sts import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/aws-sdk-go-v2/service/sts/types" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" @@ -370,9 +368,6 @@ type AssumeRoleWithWebIdentityOutput struct { } func (c *Client) addOperationAssumeRoleWithWebIdentityMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpAssumeRoleWithWebIdentity{}, middleware.After) if err != nil { return err @@ -381,38 +376,17 @@ func (c *Client) addOperationAssumeRoleWithWebIdentityMiddlewares(stack *middlew if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "AssumeRoleWithWebIdentity"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } if err = addResolveEndpointMiddleware(stack, options); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -421,22 +395,13 @@ func (c *Client) addOperationAssumeRoleWithWebIdentityMiddlewares(stack *middlew if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpAssumeRoleWithWebIdentityValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opAssumeRoleWithWebIdentity(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "AssumeRoleWithWebIdentity"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -451,22 +416,8 @@ func (c *Client) addOperationAssumeRoleWithWebIdentityMiddlewares(stack *middlew if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opAssumeRoleWithWebIdentity(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "AssumeRoleWithWebIdentity", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_op_AssumeRoot.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_op_AssumeRoot.go index b52a372dba..92aee49080 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_op_AssumeRoot.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_op_AssumeRoot.go @@ -4,8 +4,6 @@ package sts import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/aws-sdk-go-v2/service/sts/types" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" @@ -124,9 +122,6 @@ type AssumeRootOutput struct { } func (c *Client) addOperationAssumeRootMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpAssumeRoot{}, middleware.After) if err != nil { return err @@ -135,19 +130,10 @@ func (c *Client) addOperationAssumeRootMiddlewares(stack *middleware.Stack, opti if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "AssumeRoot"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -157,19 +143,7 @@ func (c *Client) addOperationAssumeRootMiddlewares(stack *middleware.Stack, opti if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -178,22 +152,13 @@ func (c *Client) addOperationAssumeRootMiddlewares(stack *middleware.Stack, opti if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpAssumeRootValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opAssumeRoot(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "AssumeRoot"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -208,22 +173,8 @@ func (c *Client) addOperationAssumeRootMiddlewares(stack *middleware.Stack, opti if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opAssumeRoot(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "AssumeRoot", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_op_DecodeAuthorizationMessage.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_op_DecodeAuthorizationMessage.go index eaeab8a683..e3294e3241 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_op_DecodeAuthorizationMessage.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_op_DecodeAuthorizationMessage.go @@ -4,8 +4,6 @@ package sts import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" ) @@ -84,9 +82,6 @@ type DecodeAuthorizationMessageOutput struct { } func (c *Client) addOperationDecodeAuthorizationMessageMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpDecodeAuthorizationMessage{}, middleware.After) if err != nil { return err @@ -95,19 +90,10 @@ func (c *Client) addOperationDecodeAuthorizationMessageMiddlewares(stack *middle if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "DecodeAuthorizationMessage"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -117,19 +103,7 @@ func (c *Client) addOperationDecodeAuthorizationMessageMiddlewares(stack *middle if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -138,22 +112,13 @@ func (c *Client) addOperationDecodeAuthorizationMessageMiddlewares(stack *middle if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpDecodeAuthorizationMessageValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opDecodeAuthorizationMessage(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "DecodeAuthorizationMessage"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -168,22 +133,8 @@ func (c *Client) addOperationDecodeAuthorizationMessageMiddlewares(stack *middle if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opDecodeAuthorizationMessage(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "DecodeAuthorizationMessage", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_op_GetAccessKeyInfo.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_op_GetAccessKeyInfo.go index 2f7adb2f53..4d1dbed59d 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_op_GetAccessKeyInfo.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_op_GetAccessKeyInfo.go @@ -4,8 +4,6 @@ package sts import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" ) @@ -75,9 +73,6 @@ type GetAccessKeyInfoOutput struct { } func (c *Client) addOperationGetAccessKeyInfoMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpGetAccessKeyInfo{}, middleware.After) if err != nil { return err @@ -86,19 +81,10 @@ func (c *Client) addOperationGetAccessKeyInfoMiddlewares(stack *middleware.Stack if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "GetAccessKeyInfo"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -108,19 +94,7 @@ func (c *Client) addOperationGetAccessKeyInfoMiddlewares(stack *middleware.Stack if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -129,22 +103,13 @@ func (c *Client) addOperationGetAccessKeyInfoMiddlewares(stack *middleware.Stack if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpGetAccessKeyInfoValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opGetAccessKeyInfo(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "GetAccessKeyInfo"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -159,22 +124,8 @@ func (c *Client) addOperationGetAccessKeyInfoMiddlewares(stack *middleware.Stack if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opGetAccessKeyInfo(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "GetAccessKeyInfo", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_op_GetCallerIdentity.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_op_GetCallerIdentity.go index f2d4fbc240..8901bef2c4 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_op_GetCallerIdentity.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_op_GetCallerIdentity.go @@ -4,8 +4,6 @@ package sts import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/aws-sdk-go-v2/aws/signer/v4" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" @@ -66,9 +64,6 @@ type GetCallerIdentityOutput struct { } func (c *Client) addOperationGetCallerIdentityMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpGetCallerIdentity{}, middleware.After) if err != nil { return err @@ -77,19 +72,10 @@ func (c *Client) addOperationGetCallerIdentityMiddlewares(stack *middleware.Stac if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "GetCallerIdentity"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -99,19 +85,7 @@ func (c *Client) addOperationGetCallerIdentityMiddlewares(stack *middleware.Stac if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -120,19 +94,10 @@ func (c *Client) addOperationGetCallerIdentityMiddlewares(stack *middleware.Stac if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opGetCallerIdentity(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "GetCallerIdentity"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -147,26 +112,12 @@ func (c *Client) addOperationGetCallerIdentityMiddlewares(stack *middleware.Stac if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } -func newServiceMetadataMiddleware_opGetCallerIdentity(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "GetCallerIdentity", - } -} - // PresignGetCallerIdentity is used to generate a presigned HTTP Request which // contains presigned URL, signed headers and HTTP method used. func (c *PresignClient) PresignGetCallerIdentity(ctx context.Context, params *GetCallerIdentityInput, optFns ...func(*PresignOptions)) (*v4.PresignedHTTPRequest, error) { diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_op_GetDelegatedAccessToken.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_op_GetDelegatedAccessToken.go index 78d688acc7..f013cb9330 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_op_GetDelegatedAccessToken.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_op_GetDelegatedAccessToken.go @@ -4,8 +4,6 @@ package sts import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/aws-sdk-go-v2/service/sts/types" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" @@ -64,9 +62,6 @@ type GetDelegatedAccessTokenOutput struct { } func (c *Client) addOperationGetDelegatedAccessTokenMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpGetDelegatedAccessToken{}, middleware.After) if err != nil { return err @@ -75,19 +70,10 @@ func (c *Client) addOperationGetDelegatedAccessTokenMiddlewares(stack *middlewar if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "GetDelegatedAccessToken"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -97,19 +83,7 @@ func (c *Client) addOperationGetDelegatedAccessTokenMiddlewares(stack *middlewar if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -118,22 +92,13 @@ func (c *Client) addOperationGetDelegatedAccessTokenMiddlewares(stack *middlewar if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpGetDelegatedAccessTokenValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opGetDelegatedAccessToken(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "GetDelegatedAccessToken"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -148,22 +113,8 @@ func (c *Client) addOperationGetDelegatedAccessTokenMiddlewares(stack *middlewar if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opGetDelegatedAccessToken(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "GetDelegatedAccessToken", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_op_GetFederationToken.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_op_GetFederationToken.go index 57b77ebcc3..fbee53221e 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_op_GetFederationToken.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_op_GetFederationToken.go @@ -4,8 +4,6 @@ package sts import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/aws-sdk-go-v2/service/sts/types" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" @@ -288,9 +286,6 @@ type GetFederationTokenOutput struct { } func (c *Client) addOperationGetFederationTokenMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpGetFederationToken{}, middleware.After) if err != nil { return err @@ -299,19 +294,10 @@ func (c *Client) addOperationGetFederationTokenMiddlewares(stack *middleware.Sta if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "GetFederationToken"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -321,19 +307,7 @@ func (c *Client) addOperationGetFederationTokenMiddlewares(stack *middleware.Sta if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -342,22 +316,13 @@ func (c *Client) addOperationGetFederationTokenMiddlewares(stack *middleware.Sta if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpGetFederationTokenValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opGetFederationToken(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "GetFederationToken"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -372,22 +337,8 @@ func (c *Client) addOperationGetFederationTokenMiddlewares(stack *middleware.Sta if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opGetFederationToken(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "GetFederationToken", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_op_GetSessionToken.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_op_GetSessionToken.go index 4b4083501d..6b5c8ed171 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_op_GetSessionToken.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_op_GetSessionToken.go @@ -4,8 +4,6 @@ package sts import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/aws-sdk-go-v2/service/sts/types" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" @@ -137,9 +135,6 @@ type GetSessionTokenOutput struct { } func (c *Client) addOperationGetSessionTokenMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpGetSessionToken{}, middleware.After) if err != nil { return err @@ -148,19 +143,10 @@ func (c *Client) addOperationGetSessionTokenMiddlewares(stack *middleware.Stack, if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "GetSessionToken"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -170,19 +156,7 @@ func (c *Client) addOperationGetSessionTokenMiddlewares(stack *middleware.Stack, if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -191,19 +165,10 @@ func (c *Client) addOperationGetSessionTokenMiddlewares(stack *middleware.Stack, if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opGetSessionToken(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "GetSessionToken"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -218,22 +183,8 @@ func (c *Client) addOperationGetSessionTokenMiddlewares(stack *middleware.Stack, if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opGetSessionToken(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "GetSessionToken", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_op_GetWebIdentityToken.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_op_GetWebIdentityToken.go index 7738de5f60..1c45a28e73 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_op_GetWebIdentityToken.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/api_op_GetWebIdentityToken.go @@ -4,8 +4,6 @@ package sts import ( "context" - "fmt" - awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/aws-sdk-go-v2/service/sts/types" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" @@ -87,9 +85,6 @@ type GetWebIdentityTokenOutput struct { } func (c *Client) addOperationGetWebIdentityTokenMiddlewares(stack *middleware.Stack, options Options) (err error) { - if err := stack.Serialize.Add(&setOperationInputMiddleware{}, middleware.After); err != nil { - return err - } err = stack.Serialize.Add(&awsAwsquery_serializeOpGetWebIdentityToken{}, middleware.After) if err != nil { return err @@ -98,19 +93,10 @@ func (c *Client) addOperationGetWebIdentityTokenMiddlewares(stack *middleware.St if err != nil { return err } - if err := addProtocolFinalizerMiddlewares(stack, options, "GetWebIdentityToken"); err != nil { - return fmt.Errorf("add protocol finalizers: %v", err) - } if err = addlegacyEndpointContextSetter(stack, options); err != nil { return err } - if err = addSetLoggerMiddleware(stack, options); err != nil { - return err - } - if err = addClientRequestID(stack); err != nil { - return err - } if err = addComputeContentLength(stack); err != nil { return err } @@ -120,19 +106,7 @@ func (c *Client) addOperationGetWebIdentityTokenMiddlewares(stack *middleware.St if err = addComputePayloadSHA256(stack); err != nil { return err } - if err = addRetry(stack, options, c); err != nil { - return err - } - if err = addRawResponseToMetadata(stack); err != nil { - return err - } - if err = addRecordResponseTiming(stack); err != nil { - return err - } - if err = addSpanRetryLoop(stack, options); err != nil { - return err - } - if err = addClientUserAgent(stack, options); err != nil { + if err = addRecordResponseTiming(stack, options); err != nil { return err } if err = smithyhttp.AddErrorCloseResponseBodyMiddleware(stack); err != nil { @@ -141,22 +115,13 @@ func (c *Client) addOperationGetWebIdentityTokenMiddlewares(stack *middleware.St if err = smithyhttp.AddCloseResponseBodyMiddleware(stack); err != nil { return err } - if err = addSetLegacyContextSigningOptionsMiddleware(stack); err != nil { - return err - } - if err = addUserAgentRetryMode(stack, options); err != nil { - return err - } if err = addCredentialSource(stack, options); err != nil { return err } if err = addOpGetWebIdentityTokenValidationMiddleware(stack); err != nil { return err } - if err = stack.Initialize.Add(newServiceMetadataMiddleware_opGetWebIdentityToken(options.Region), middleware.Before); err != nil { - return err - } - if err = addRecursionDetection(stack); err != nil { + if err = stack.Initialize.Add(newServiceMetadataMiddleware(options.Region, "GetWebIdentityToken"), middleware.Before); err != nil { return err } if err = addRequestIDRetrieverMiddleware(stack); err != nil { @@ -171,22 +136,8 @@ func (c *Client) addOperationGetWebIdentityTokenMiddlewares(stack *middleware.St if err = addDisableHTTPSMiddleware(stack, options); err != nil { return err } - if err = addInterceptBeforeRetryLoop(stack, options); err != nil { - return err - } - if err = addInterceptAttempt(stack, options); err != nil { - return err - } if err = addInterceptors(stack, options); err != nil { return err } return nil } - -func newServiceMetadataMiddleware_opGetWebIdentityToken(region string) *awsmiddleware.RegisterServiceMetadata { - return &awsmiddleware.RegisterServiceMetadata{ - Region: region, - ServiceID: ServiceID, - OperationName: "GetWebIdentityToken", - } -} diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/auth.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/auth.go index 71c5db38b7..5fe4c60124 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/auth.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/auth.go @@ -206,7 +206,7 @@ func (m *resolveAuthSchemeMiddleware) selectScheme(options []*smithyauth.Option) } for _, scheme := range m.options.AuthSchemes { - if scheme.SchemeID() != option.SchemeID { + if !matchSchemeID(scheme.SchemeID(), option.SchemeID) { continue } @@ -219,6 +219,16 @@ func (m *resolveAuthSchemeMiddleware) selectScheme(options []*smithyauth.Option) return nil, false } +func matchSchemeID(registered, option string) bool { + if registered == option { + return true + } + if i := strings.LastIndex(registered, "#"); i != -1 { + return registered[i+1:] == option + } + return false +} + func sortAuthOptions(options []*smithyauth.Option, preferred []string) []*smithyauth.Option { byPriority := make([]*smithyauth.Option, 0, len(options)) for _, prefName := range preferred { diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/endpoints.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/endpoints.go index c8f9526c78..52e7a1fd63 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/endpoints.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/endpoints.go @@ -15,6 +15,7 @@ import ( smithy "github.com/aws/smithy-go" smithyauth "github.com/aws/smithy-go/auth" smithyendpoints "github.com/aws/smithy-go/endpoints" + "github.com/aws/smithy-go/endpoints/private/bdd" "github.com/aws/smithy-go/endpoints/private/rulesfn" "github.com/aws/smithy-go/middleware" "github.com/aws/smithy-go/ptr" @@ -230,6 +231,8 @@ func bindRegion(region string) (*string, error) { return aws.String(endpoints.MapFIPSRegion(region)), nil } +var _ = rulesfn.StringSlice(nil) + // EndpointParameters provides the parameters that influence how endpoints are // resolved. type EndpointParameters struct { @@ -312,21 +315,252 @@ func (p EndpointParameters) WithDefaults() EndpointParameters { return p } -type stringSlice []string +const bddRoot int32 = 2 -func (s stringSlice) Get(i int) *string { - if i < 0 || i >= len(s) { - return nil - } +var bddNodes = [93]int32{ + -1, 1, -1, 0, 30, 3, 1, 4, 100000014, 2, 5, 100000014, 3, 25, 6, 4, 24, 7, 5, 100000001, 8, 6, 9, 100000013, 7, 100000001, 10, 10, 100000001, 11, 11, 100000001, 12, 12, 100000001, 13, 13, 100000001, 14, 14, 100000001, 15, 15, 100000001, 16, 16, 100000001, 17, 17, 100000001, 18, 18, 100000001, 19, 19, 100000001, 20, 20, 100000001, 21, 21, 100000001, 22, 22, 100000001, 23, 23, 100000001, 100000002, 8, 100000011, 100000012, 4, 28, 26, 9, 27, 100000010, 24, 100000008, 100000009, 8, 29, 100000007, 9, 100000006, 100000007, 3, 100000003, 31, 4, 100000004, 100000005} + +type conditionContext struct { + PartitionResult *awsrulesfn.PartitionConfig +} - v := s[i] - return &v +func evalCondition(idx int, params *EndpointParameters, c *conditionContext) bool { + switch idx { + case 0: + return params.Endpoint != nil + case 1: + return params.Region != nil + case 2: + if v := awsrulesfn.GetPartition(*params.Region); v != nil { + c.PartitionResult = v + return true + } + return false + case 3: + return *params.UseFIPS == true + case 4: + return *params.UseDualStack == true + case 5: + return *params.Region == "aws-global" + case 6: + return *params.UseGlobalEndpoint == true + case 7: + return *params.Region == "eu-central-1" + case 8: + return c.PartitionResult.SupportsDualStack == true + case 9: + return c.PartitionResult.SupportsFIPS == true + case 10: + return *params.Region == "ap-south-1" + case 11: + return *params.Region == "eu-north-1" + case 12: + return *params.Region == "eu-west-1" + case 13: + return *params.Region == "eu-west-2" + case 14: + return *params.Region == "eu-west-3" + case 15: + return *params.Region == "sa-east-1" + case 16: + return *params.Region == "us-east-1" + case 17: + return *params.Region == "us-east-2" + case 18: + return *params.Region == "us-west-2" + case 19: + return *params.Region == "us-west-1" + case 20: + return *params.Region == "ca-central-1" + case 21: + return *params.Region == "ap-southeast-1" + case 22: + return *params.Region == "ap-northeast-1" + case 23: + return *params.Region == "ap-southeast-2" + case 24: + return c.PartitionResult.Name == "aws-us-gov" + } + return false +} + +func resolveResult(idx int32, params *EndpointParameters, c *conditionContext) (smithyendpoints.Endpoint, error) { + switch idx { + case 0: + return smithyendpoints.Endpoint{}, fmt.Errorf("endpoint resolution failed: no matching rule") + case 1: + uriString := "https://sts.amazonaws.com" + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + Properties: func() smithy.Properties { + var out smithy.Properties + smithyauth.SetAuthOptions(&out, []*smithyauth.Option{ + { + SchemeID: "sigv4", + SignerProperties: func() smithy.Properties { + var sp smithy.Properties + smithyhttp.SetSigV4SigningName(&sp, "sts") + smithyhttp.SetSigV4ASigningName(&sp, "sts") + + smithyhttp.SetSigV4SigningRegion(&sp, "us-east-1") + return sp + }(), + }, + }) + return out + }(), + }, nil + case 2: + uriString := func() string { + var out strings.Builder + out.WriteString("https://sts.") + out.WriteString(*params.Region) + out.WriteString(".") + out.WriteString(c.PartitionResult.DnsSuffix) + return out.String() + }() + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + Properties: func() smithy.Properties { + var out smithy.Properties + smithyauth.SetAuthOptions(&out, []*smithyauth.Option{ + { + SchemeID: "sigv4", + SignerProperties: func() smithy.Properties { + var sp smithy.Properties + smithyhttp.SetSigV4SigningName(&sp, "sts") + smithyhttp.SetSigV4ASigningName(&sp, "sts") + + smithyhttp.SetSigV4SigningRegion(&sp, *params.Region) + return sp + }(), + }, + }) + return out + }(), + }, nil + case 3: + return smithyendpoints.Endpoint{}, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: FIPS and custom endpoint are not supported") + case 4: + return smithyendpoints.Endpoint{}, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: Dualstack and custom endpoint are not supported") + case 5: + uriString := *params.Endpoint + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + case 6: + uriString := func() string { + var out strings.Builder + out.WriteString("https://sts-fips.") + out.WriteString(*params.Region) + out.WriteString(".") + out.WriteString(c.PartitionResult.DualStackDnsSuffix) + return out.String() + }() + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + case 7: + return smithyendpoints.Endpoint{}, fmt.Errorf("endpoint rule error, %s", "FIPS and DualStack are enabled, but this partition does not support one or both") + case 8: + uriString := func() string { + var out strings.Builder + out.WriteString("https://sts.") + out.WriteString(*params.Region) + out.WriteString(".amazonaws.com") + return out.String() + }() + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + case 9: + uriString := func() string { + var out strings.Builder + out.WriteString("https://sts-fips.") + out.WriteString(*params.Region) + out.WriteString(".") + out.WriteString(c.PartitionResult.DnsSuffix) + return out.String() + }() + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + case 10: + return smithyendpoints.Endpoint{}, fmt.Errorf("endpoint rule error, %s", "FIPS is enabled but this partition does not support FIPS") + case 11: + uriString := func() string { + var out strings.Builder + out.WriteString("https://sts.") + out.WriteString(*params.Region) + out.WriteString(".") + out.WriteString(c.PartitionResult.DualStackDnsSuffix) + return out.String() + }() + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + case 12: + return smithyendpoints.Endpoint{}, fmt.Errorf("endpoint rule error, %s", "DualStack is enabled but this partition does not support DualStack") + case 13: + uriString := func() string { + var out strings.Builder + out.WriteString("https://sts.") + out.WriteString(*params.Region) + out.WriteString(".") + out.WriteString(c.PartitionResult.DnsSuffix) + return out.String() + }() + uri, err := url.Parse(uriString) + if err != nil { + return smithyendpoints.Endpoint{}, fmt.Errorf("Failed to parse uri: %s", uriString) + } + return smithyendpoints.Endpoint{ + URI: *uri, + Headers: http.Header{}, + }, nil + case 14: + return smithyendpoints.Endpoint{}, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: Missing Region") + } + return smithyendpoints.Endpoint{}, fmt.Errorf("endpoint rule error, invalid result index: %d", idx) } // EndpointResolverV2 provides the interface for resolving service endpoints. type EndpointResolverV2 interface { - // ResolveEndpoint attempts to resolve the endpoint with the provided options, - // returning the endpoint if found. Otherwise an error is returned. ResolveEndpoint(ctx context.Context, params EndpointParameters) ( smithyendpoints.Endpoint, error, ) @@ -350,715 +584,12 @@ func (r *resolver) ResolveEndpoint( if err = params.ValidateRequired(); err != nil { return endpoint, fmt.Errorf("endpoint parameters are not valid, %w", err) } - _UseDualStack := *params.UseDualStack - _ = _UseDualStack - _UseFIPS := *params.UseFIPS - _ = _UseFIPS - _UseGlobalEndpoint := *params.UseGlobalEndpoint - _ = _UseGlobalEndpoint - - if _UseGlobalEndpoint == true { - if !(params.Endpoint != nil) { - if exprVal := params.Region; exprVal != nil { - _Region := *exprVal - _ = _Region - if exprVal := awsrulesfn.GetPartition(_Region); exprVal != nil { - _PartitionResult := *exprVal - _ = _PartitionResult - if _UseFIPS == false { - if _UseDualStack == false { - if _Region == "ap-northeast-1" { - uriString := "https://sts.amazonaws.com" - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - Properties: func() smithy.Properties { - var out smithy.Properties - smithyauth.SetAuthOptions(&out, []*smithyauth.Option{ - { - SchemeID: "aws.auth#sigv4", - SignerProperties: func() smithy.Properties { - var sp smithy.Properties - smithyhttp.SetSigV4SigningName(&sp, "sts") - smithyhttp.SetSigV4ASigningName(&sp, "sts") - - smithyhttp.SetSigV4SigningRegion(&sp, "us-east-1") - return sp - }(), - }, - }) - return out - }(), - }, nil - } - if _Region == "ap-south-1" { - uriString := "https://sts.amazonaws.com" - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - Properties: func() smithy.Properties { - var out smithy.Properties - smithyauth.SetAuthOptions(&out, []*smithyauth.Option{ - { - SchemeID: "aws.auth#sigv4", - SignerProperties: func() smithy.Properties { - var sp smithy.Properties - smithyhttp.SetSigV4SigningName(&sp, "sts") - smithyhttp.SetSigV4ASigningName(&sp, "sts") - - smithyhttp.SetSigV4SigningRegion(&sp, "us-east-1") - return sp - }(), - }, - }) - return out - }(), - }, nil - } - if _Region == "ap-southeast-1" { - uriString := "https://sts.amazonaws.com" - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - Properties: func() smithy.Properties { - var out smithy.Properties - smithyauth.SetAuthOptions(&out, []*smithyauth.Option{ - { - SchemeID: "aws.auth#sigv4", - SignerProperties: func() smithy.Properties { - var sp smithy.Properties - smithyhttp.SetSigV4SigningName(&sp, "sts") - smithyhttp.SetSigV4ASigningName(&sp, "sts") - - smithyhttp.SetSigV4SigningRegion(&sp, "us-east-1") - return sp - }(), - }, - }) - return out - }(), - }, nil - } - if _Region == "ap-southeast-2" { - uriString := "https://sts.amazonaws.com" - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - Properties: func() smithy.Properties { - var out smithy.Properties - smithyauth.SetAuthOptions(&out, []*smithyauth.Option{ - { - SchemeID: "aws.auth#sigv4", - SignerProperties: func() smithy.Properties { - var sp smithy.Properties - smithyhttp.SetSigV4SigningName(&sp, "sts") - smithyhttp.SetSigV4ASigningName(&sp, "sts") - - smithyhttp.SetSigV4SigningRegion(&sp, "us-east-1") - return sp - }(), - }, - }) - return out - }(), - }, nil - } - if _Region == "aws-global" { - uriString := "https://sts.amazonaws.com" - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - Properties: func() smithy.Properties { - var out smithy.Properties - smithyauth.SetAuthOptions(&out, []*smithyauth.Option{ - { - SchemeID: "aws.auth#sigv4", - SignerProperties: func() smithy.Properties { - var sp smithy.Properties - smithyhttp.SetSigV4SigningName(&sp, "sts") - smithyhttp.SetSigV4ASigningName(&sp, "sts") - - smithyhttp.SetSigV4SigningRegion(&sp, "us-east-1") - return sp - }(), - }, - }) - return out - }(), - }, nil - } - if _Region == "ca-central-1" { - uriString := "https://sts.amazonaws.com" - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - Properties: func() smithy.Properties { - var out smithy.Properties - smithyauth.SetAuthOptions(&out, []*smithyauth.Option{ - { - SchemeID: "aws.auth#sigv4", - SignerProperties: func() smithy.Properties { - var sp smithy.Properties - smithyhttp.SetSigV4SigningName(&sp, "sts") - smithyhttp.SetSigV4ASigningName(&sp, "sts") - - smithyhttp.SetSigV4SigningRegion(&sp, "us-east-1") - return sp - }(), - }, - }) - return out - }(), - }, nil - } - if _Region == "eu-central-1" { - uriString := "https://sts.amazonaws.com" - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - Properties: func() smithy.Properties { - var out smithy.Properties - smithyauth.SetAuthOptions(&out, []*smithyauth.Option{ - { - SchemeID: "aws.auth#sigv4", - SignerProperties: func() smithy.Properties { - var sp smithy.Properties - smithyhttp.SetSigV4SigningName(&sp, "sts") - smithyhttp.SetSigV4ASigningName(&sp, "sts") - - smithyhttp.SetSigV4SigningRegion(&sp, "us-east-1") - return sp - }(), - }, - }) - return out - }(), - }, nil - } - if _Region == "eu-north-1" { - uriString := "https://sts.amazonaws.com" - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - Properties: func() smithy.Properties { - var out smithy.Properties - smithyauth.SetAuthOptions(&out, []*smithyauth.Option{ - { - SchemeID: "aws.auth#sigv4", - SignerProperties: func() smithy.Properties { - var sp smithy.Properties - smithyhttp.SetSigV4SigningName(&sp, "sts") - smithyhttp.SetSigV4ASigningName(&sp, "sts") - - smithyhttp.SetSigV4SigningRegion(&sp, "us-east-1") - return sp - }(), - }, - }) - return out - }(), - }, nil - } - if _Region == "eu-west-1" { - uriString := "https://sts.amazonaws.com" - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - Properties: func() smithy.Properties { - var out smithy.Properties - smithyauth.SetAuthOptions(&out, []*smithyauth.Option{ - { - SchemeID: "aws.auth#sigv4", - SignerProperties: func() smithy.Properties { - var sp smithy.Properties - smithyhttp.SetSigV4SigningName(&sp, "sts") - smithyhttp.SetSigV4ASigningName(&sp, "sts") - - smithyhttp.SetSigV4SigningRegion(&sp, "us-east-1") - return sp - }(), - }, - }) - return out - }(), - }, nil - } - if _Region == "eu-west-2" { - uriString := "https://sts.amazonaws.com" - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - Properties: func() smithy.Properties { - var out smithy.Properties - smithyauth.SetAuthOptions(&out, []*smithyauth.Option{ - { - SchemeID: "aws.auth#sigv4", - SignerProperties: func() smithy.Properties { - var sp smithy.Properties - smithyhttp.SetSigV4SigningName(&sp, "sts") - smithyhttp.SetSigV4ASigningName(&sp, "sts") - - smithyhttp.SetSigV4SigningRegion(&sp, "us-east-1") - return sp - }(), - }, - }) - return out - }(), - }, nil - } - if _Region == "eu-west-3" { - uriString := "https://sts.amazonaws.com" - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - Properties: func() smithy.Properties { - var out smithy.Properties - smithyauth.SetAuthOptions(&out, []*smithyauth.Option{ - { - SchemeID: "aws.auth#sigv4", - SignerProperties: func() smithy.Properties { - var sp smithy.Properties - smithyhttp.SetSigV4SigningName(&sp, "sts") - smithyhttp.SetSigV4ASigningName(&sp, "sts") - - smithyhttp.SetSigV4SigningRegion(&sp, "us-east-1") - return sp - }(), - }, - }) - return out - }(), - }, nil - } - if _Region == "sa-east-1" { - uriString := "https://sts.amazonaws.com" - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - Properties: func() smithy.Properties { - var out smithy.Properties - smithyauth.SetAuthOptions(&out, []*smithyauth.Option{ - { - SchemeID: "aws.auth#sigv4", - SignerProperties: func() smithy.Properties { - var sp smithy.Properties - smithyhttp.SetSigV4SigningName(&sp, "sts") - smithyhttp.SetSigV4ASigningName(&sp, "sts") - - smithyhttp.SetSigV4SigningRegion(&sp, "us-east-1") - return sp - }(), - }, - }) - return out - }(), - }, nil - } - if _Region == "us-east-1" { - uriString := "https://sts.amazonaws.com" - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - Properties: func() smithy.Properties { - var out smithy.Properties - smithyauth.SetAuthOptions(&out, []*smithyauth.Option{ - { - SchemeID: "aws.auth#sigv4", - SignerProperties: func() smithy.Properties { - var sp smithy.Properties - smithyhttp.SetSigV4SigningName(&sp, "sts") - smithyhttp.SetSigV4ASigningName(&sp, "sts") - - smithyhttp.SetSigV4SigningRegion(&sp, "us-east-1") - return sp - }(), - }, - }) - return out - }(), - }, nil - } - if _Region == "us-east-2" { - uriString := "https://sts.amazonaws.com" - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - Properties: func() smithy.Properties { - var out smithy.Properties - smithyauth.SetAuthOptions(&out, []*smithyauth.Option{ - { - SchemeID: "aws.auth#sigv4", - SignerProperties: func() smithy.Properties { - var sp smithy.Properties - smithyhttp.SetSigV4SigningName(&sp, "sts") - smithyhttp.SetSigV4ASigningName(&sp, "sts") - - smithyhttp.SetSigV4SigningRegion(&sp, "us-east-1") - return sp - }(), - }, - }) - return out - }(), - }, nil - } - if _Region == "us-west-1" { - uriString := "https://sts.amazonaws.com" - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - Properties: func() smithy.Properties { - var out smithy.Properties - smithyauth.SetAuthOptions(&out, []*smithyauth.Option{ - { - SchemeID: "aws.auth#sigv4", - SignerProperties: func() smithy.Properties { - var sp smithy.Properties - smithyhttp.SetSigV4SigningName(&sp, "sts") - smithyhttp.SetSigV4ASigningName(&sp, "sts") - - smithyhttp.SetSigV4SigningRegion(&sp, "us-east-1") - return sp - }(), - }, - }) - return out - }(), - }, nil - } - if _Region == "us-west-2" { - uriString := "https://sts.amazonaws.com" - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - Properties: func() smithy.Properties { - var out smithy.Properties - smithyauth.SetAuthOptions(&out, []*smithyauth.Option{ - { - SchemeID: "aws.auth#sigv4", - SignerProperties: func() smithy.Properties { - var sp smithy.Properties - smithyhttp.SetSigV4SigningName(&sp, "sts") - smithyhttp.SetSigV4ASigningName(&sp, "sts") - - smithyhttp.SetSigV4SigningRegion(&sp, "us-east-1") - return sp - }(), - }, - }) - return out - }(), - }, nil - } - uriString := func() string { - var out strings.Builder - out.WriteString("https://sts.") - out.WriteString(_Region) - out.WriteString(".") - out.WriteString(_PartitionResult.DnsSuffix) - return out.String() - }() - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - Properties: func() smithy.Properties { - var out smithy.Properties - smithyauth.SetAuthOptions(&out, []*smithyauth.Option{ - { - SchemeID: "aws.auth#sigv4", - SignerProperties: func() smithy.Properties { - var sp smithy.Properties - smithyhttp.SetSigV4SigningName(&sp, "sts") - smithyhttp.SetSigV4ASigningName(&sp, "sts") - - smithyhttp.SetSigV4SigningRegion(&sp, _Region) - return sp - }(), - }, - }) - return out - }(), - }, nil - } - } - } - } - } - } - if exprVal := params.Endpoint; exprVal != nil { - _Endpoint := *exprVal - _ = _Endpoint - if _UseFIPS == true { - return endpoint, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: FIPS and custom endpoint are not supported") - } - if _UseDualStack == true { - return endpoint, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: Dualstack and custom endpoint are not supported") - } - uriString := _Endpoint - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - }, nil - } - if exprVal := params.Region; exprVal != nil { - _Region := *exprVal - _ = _Region - if exprVal := awsrulesfn.GetPartition(_Region); exprVal != nil { - _PartitionResult := *exprVal - _ = _PartitionResult - if _UseFIPS == true { - if _UseDualStack == true { - if true == _PartitionResult.SupportsFIPS { - if true == _PartitionResult.SupportsDualStack { - uriString := func() string { - var out strings.Builder - out.WriteString("https://sts-fips.") - out.WriteString(_Region) - out.WriteString(".") - out.WriteString(_PartitionResult.DualStackDnsSuffix) - return out.String() - }() - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - }, nil - } - } - return endpoint, fmt.Errorf("endpoint rule error, %s", "FIPS and DualStack are enabled, but this partition does not support one or both") - } - } - if _UseFIPS == true { - if _PartitionResult.SupportsFIPS == true { - if _PartitionResult.Name == "aws-us-gov" { - uriString := func() string { - var out strings.Builder - out.WriteString("https://sts.") - out.WriteString(_Region) - out.WriteString(".amazonaws.com") - return out.String() - }() - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - }, nil - } - uriString := func() string { - var out strings.Builder - out.WriteString("https://sts-fips.") - out.WriteString(_Region) - out.WriteString(".") - out.WriteString(_PartitionResult.DnsSuffix) - return out.String() - }() - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - }, nil - } - return endpoint, fmt.Errorf("endpoint rule error, %s", "FIPS is enabled but this partition does not support FIPS") - } - if _UseDualStack == true { - if true == _PartitionResult.SupportsDualStack { - uriString := func() string { - var out strings.Builder - out.WriteString("https://sts.") - out.WriteString(_Region) - out.WriteString(".") - out.WriteString(_PartitionResult.DualStackDnsSuffix) - return out.String() - }() - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - }, nil - } - return endpoint, fmt.Errorf("endpoint rule error, %s", "DualStack is enabled but this partition does not support DualStack") - } - if _Region == "aws-global" { - uriString := "https://sts.amazonaws.com" - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - Properties: func() smithy.Properties { - var out smithy.Properties - smithyauth.SetAuthOptions(&out, []*smithyauth.Option{ - { - SchemeID: "aws.auth#sigv4", - SignerProperties: func() smithy.Properties { - var sp smithy.Properties - smithyhttp.SetSigV4SigningName(&sp, "sts") - smithyhttp.SetSigV4ASigningName(&sp, "sts") - - smithyhttp.SetSigV4SigningRegion(&sp, "us-east-1") - return sp - }(), - }, - }) - return out - }(), - }, nil - } - uriString := func() string { - var out strings.Builder - out.WriteString("https://sts.") - out.WriteString(_Region) - out.WriteString(".") - out.WriteString(_PartitionResult.DnsSuffix) - return out.String() - }() - - uri, err := url.Parse(uriString) - if err != nil { - return endpoint, fmt.Errorf("Failed to parse uri: %s", uriString) - } - - return smithyendpoints.Endpoint{ - URI: *uri, - Headers: http.Header{}, - }, nil - } - return endpoint, fmt.Errorf("Endpoint resolution failed. Invalid operation or environment input.") - } - return endpoint, fmt.Errorf("endpoint rule error, %s", "Invalid Configuration: Missing Region") + c := &conditionContext{} + ref := bdd.Evaluate(bddNodes[:], bddRoot, func(idx int) bool { + return evalCondition(idx, ¶ms, c) + }) + return resolveResult(ref, ¶ms, c) } type endpointParamsBinder interface { diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/generated.json b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/generated.json index 2fc7b400f7..6a759b4cc2 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/generated.json +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/generated.json @@ -32,6 +32,8 @@ "internal/endpoints/endpoints.go", "internal/endpoints/endpoints_test.go", "options.go", + "request_snapshot_test.go", + "response_snapshot_test.go", "serializers.go", "snapshot_test.go", "sra_operation_order_test.go", diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/go_module_metadata.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/go_module_metadata.go index bdd6a15d8f..6079b28d33 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/go_module_metadata.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/go_module_metadata.go @@ -3,4 +3,4 @@ package sts // goModuleVersion is the tagged release for this module -const goModuleVersion = "1.42.1" +const goModuleVersion = "1.45.2" diff --git a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/options.go b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/options.go index a9f2361fd3..b3c9df17f3 100644 --- a/vendor/github.com/aws/aws-sdk-go-v2/service/sts/options.go +++ b/vendor/github.com/aws/aws-sdk-go-v2/service/sts/options.go @@ -46,6 +46,11 @@ type Options struct { // clients initial default settings. DefaultsMode aws.DefaultsMode + // Disables SDK clock skew correction. When set, the SDK will not adjust request + // signing timestamps to compensate for clock drift between the client and the + // service. + DisableClockSkewCorrection bool + // The endpoint options to be used when attempting to resolve an endpoint. EndpointOptions EndpointResolverOptions diff --git a/vendor/github.com/aws/smithy-go/AGENTS.md b/vendor/github.com/aws/smithy-go/AGENTS.md index e2a75b8ea1..de1e3b2bbe 100644 --- a/vendor/github.com/aws/smithy-go/AGENTS.md +++ b/vendor/github.com/aws/smithy-go/AGENTS.md @@ -68,8 +68,9 @@ cd codegen && ./gradlew build cd codegen && ./gradlew publishToMavenLocal ``` -The codegen artifact version is fixed at `0.1.0` and is not published to -Maven Central — you **MUST** `publishToMavenLocal`. +The codegen artifact version is published to Maven Central and bumped on each +release. For local development against unreleased codegen changes, use +`publishToMavenLocal` and point consumers at `mavenLocal()`. ## Runtime architecture diff --git a/vendor/github.com/aws/smithy-go/CHANGELOG.md b/vendor/github.com/aws/smithy-go/CHANGELOG.md index b9cd114ed1..0140cf10ea 100644 --- a/vendor/github.com/aws/smithy-go/CHANGELOG.md +++ b/vendor/github.com/aws/smithy-go/CHANGELOG.md @@ -1,3 +1,73 @@ +# Release (2026-07-27) + +## General Highlights +* **Dependency Update**: Updated to the latest SDK module versions + +## Module Highlights +* `github.com/aws/smithy-go`: v1.27.5 + * **Bug Fix**: Fix a performance issue in awsQuery with large response payloads. + +# Release (2026-07-16) + +## General Highlights +* **Dependency Update**: Updated to the latest SDK module versions + +## Module Highlights +* `github.com/aws/smithy-go/aws-http-auth`: [v1.2.1](aws-http-auth/CHANGELOG.md#v121-2026-07-16) + * **Bug Fix**: Use r.URL.Host when r.Host is unset. +* `github.com/aws/smithy-go/aws-http-auth-schemes`: [v1.0.0](aws-http-auth-schemes/CHANGELOG.md#v100-2026-07-16) + * **Release**: Module `github.com/aws/smithy-go/aws-http-auth-schemes` adds generic smithy-go client support for AWS Sigv4 and Sigv4a. + +# Release (2026-06-26) + +## General Highlights +* **Dependency Update**: Updated to the latest SDK module versions + +## Module Highlights +* `github.com/aws/smithy-go`: v1.27.3 + * **Bug Fix**: Fix bug in JSON doc encoder and endpoint host label format validation + +# Release (2026-06-05) + +## General Highlights +* **Dependency Update**: Updated to the latest SDK module versions + +## Module Highlights +* `github.com/aws/smithy-go`: v1.27.2 + * **Bug Fix**: Fix incorrect serialization of unions in CBOR-based protocols. + +# Release (2026-06-04) + +## General Highlights +* **Dependency Update**: Updated to the latest SDK module versions + +## Module Highlights +* `github.com/aws/smithy-go`: v1.27.1 + * **Bug Fix**: Fixed a deserialization failure in all protocols when encountering a union with explicit null members. + * **Bug Fix**: Fixed a panic when deserializing nested unions in JSON- and CBOR-based protocols. + +# Release (2026-06-02) + +## General Highlights +* **Dependency Update**: Updated to the latest SDK module versions + +## Module Highlights +* `github.com/aws/smithy-go`: v1.27.0 + * **Feature**: Add APIs for schema-based serialization. + * **Feature**: Add support for all current AWS and Smithy protocols. + * **Bug Fix**: Enforce max nesting depth of 128 on CBOR payloads. +* `github.com/aws/smithy-go/aws-http-auth`: [v1.2.0](aws-http-auth/CHANGELOG.md#v120-2026-06-02) + * **Feature**: Add event stream signer. + +# Release (2026-05-27) + +## General Highlights +* **Dependency Update**: Updated to the latest SDK module versions + +## Module Highlights +* `github.com/aws/smithy-go`: v1.26.0 + * **Feature**: Add StringSlice to endpoint rulesfn. + # Release (2026-04-23) ## General Highlights diff --git a/vendor/github.com/aws/smithy-go/README.md b/vendor/github.com/aws/smithy-go/README.md index a413ff3d87..ac5a0a6139 100644 --- a/vendor/github.com/aws/smithy-go/README.md +++ b/vendor/github.com/aws/smithy-go/README.md @@ -8,22 +8,19 @@ The smithy-go runtime requires a minimum version of Go 1.24. **WARNING: All interfaces are subject to change.** -## :no_entry_sign: DO NOT use the code generators in this repository +## :warning: Client codegen is unstable -**The code generators in this repository do not generate working clients at -this time.** +The client code generator in this repository powers the aws-sdk-go-v2. +Arbitrary client generation, while possible, is in an early stage of +development: -In order to generate a usable smithy client you must provide a [protocol definition](https://github.com/aws/smithy-go/blob/main/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/ProtocolGenerator.java), -such as [AWS restJson1](https://smithy.io/2.0/aws/protocols/aws-restjson1-protocol.html), -in order to generate transport mechanisms and serialization/deserialization -code ("serde") accordingly. +* Generated clients are missing certain features that were originally + implemented SDK-side (e.g. retries) +* There may be bugs +* The public APIs of generated clients may be unstable -The code generator does not currently support any protocols out of the box. -Support for all [AWS protocols](https://smithy.io/2.0/aws/protocols/index.html) -exists in [aws-sdk-go-v2](https://github.com/aws/aws-sdk-go-v2). We are -tracking the movement of those out of the SDK into smithy-go in -[#458](https://github.com/aws/smithy-go/issues/458), but there's currently no -timeline for doing so. +If you are interested in using the client code generators, we encourage you to +experiment and share any feedback with us in an issue. ## Plugins @@ -35,8 +32,6 @@ This repository implements the following Smithy build plugins: | `go-server-codegen` | `software.amazon.smithy.go:smithy-go-codegen` | Implements Go server code generation for Smithy models. | | `go-shape-codegen` | `software.amazon.smithy.go:smithy-go-codegen` | Implements Go shape code generation (types only) for Smithy models. | -**NOTE: Build plugins are not currently published to mavenCentral. You must publish to mavenLocal to make the build plugins visible to the Smithy CLI. The artifact version is currently fixed at 0.1.0.** - ## `go-codegen` ### Configuration @@ -55,9 +50,19 @@ methods and types. The up-to-date list of top-level properties enabled for ### Supported protocols +The protocol a client uses is configured by the `Protocol` field on a client's +`Options`. The SDK will configure a default based on the protocol traits +applied to the modeled service. + | Protocol | Notes | |----------|-------| -| [`smithy.protocols#rpcv2Cbor`](https://smithy.io/2.0/additional-specs/protocols/smithy-rpc-v2.html) | Event streaming not yet implemented. | +| [`smithy.protocols#rpcv2Cbor`](https://smithy.io/2.0/additional-specs/protocols/smithy-rpc-v2.html) | | +| [`aws.protocols#restJson1`](https://smithy.io/2.0/aws/protocols/aws-restjson1-protocol.html) | | +| [`aws.protocols#restXml`](https://smithy.io/2.0/aws/protocols/aws-restxml-protocol.html) | | +| [`aws.protocols#awsJson1_0`](https://smithy.io/2.0/aws/protocols/aws-json-1_0-protocol.html) | | +| [`aws.protocols#awsJson1_1`](https://smithy.io/2.0/aws/protocols/aws-json-1_1-protocol.html) | | +| [`aws.protocols#awsQuery`](https://smithy.io/2.0/aws/protocols/aws-query-protocol.html) | | +| [`aws.protocols#ec2Query`](https://smithy.io/2.0/aws/protocols/aws-ec2-query-protocol.html) | | ### Example @@ -72,7 +77,7 @@ example created from `smithy init`: ], "maven": { "dependencies": [ - "software.amazon.smithy.go:smithy-go-codegen:0.1.0" + "software.amazon.smithy.go:smithy-go-codegen:[0.1.0,2.0)" ] }, "plugins": { diff --git a/vendor/github.com/aws/smithy-go/document/document.go b/vendor/github.com/aws/smithy-go/document/document.go index 8f852d95c6..82b48eb592 100644 --- a/vendor/github.com/aws/smithy-go/document/document.go +++ b/vendor/github.com/aws/smithy-go/document/document.go @@ -4,6 +4,7 @@ import ( "fmt" "math/big" "strconv" + "time" ) // Marshaler is an interface for a type that marshals a document to its protocol-specific byte representation and @@ -15,26 +16,26 @@ import ( // When defining struct types. the `document` struct tag can be used to control how the value will be // marshaled into the resulting protocol document. // -// // Field is ignored -// Field int `document:"-"` +// // Field is ignored +// Field int `document:"-"` // -// // Field object of key "myName" -// Field int `document:"myName"` +// // Field object of key "myName" +// Field int `document:"myName"` // -// // Field object key of key "myName", and -// // Field is omitted if the field is a zero value for the type. -// Field int `document:"myName,omitempty"` +// // Field object key of key "myName", and +// // Field is omitted if the field is a zero value for the type. +// Field int `document:"myName,omitempty"` // -// // Field object key of "Field", and -// // Field is omitted if the field is a zero value for the type. -// Field int `document:",omitempty"` +// // Field object key of "Field", and +// // Field is omitted if the field is a zero value for the type. +// Field int `document:",omitempty"` // // All struct fields, including anonymous fields, are marshaled unless the // any of the following conditions are meet. // -// - the field is not exported -// - document field tag is "-" -// - document field tag specifies "omitempty", and is a zero value. +// - the field is not exported +// - document field tag is "-" +// - document field tag specifies "omitempty", and is a zero value. // // Pointer and interface values are encoded as the value pointed to or // contained in the interface. A nil value encodes as a null @@ -50,6 +51,13 @@ import ( // // Marshal cannot represent cyclic data structures and will not handle them. // Passing cyclic structures to Marshal will result in an infinite recursion. +// +// Marshaler is not used in schema-serde based services (which are currently +// being rolled out) since having an implementation of Marshaler locks a +// document into support for a specific serial format. Existing implementations +// of Marshaler will continue to encode to JSON as that is effectively the only +// serial format supported for Document prior to the introduction of +// schema-serde. In schema-serde services it is replaced by [Value]. type Marshaler interface { MarshalSmithyDocument() ([]byte, error) } @@ -63,18 +71,94 @@ type Marshaler interface { // // Both generic interface{} and concrete types are valid unmarshal destination types. When unmarshaling a document // into an empty interface the Unmarshaler will store one of these values: -// bool, for boolean values -// document.Number, for arbitrary-precision numbers (int64, float64, big.Int, big.Float) -// string, for string values -// []interface{}, for array values -// map[string]interface{}, for objects -// nil, for null values +// +// bool, for boolean values +// document.Number, for arbitrary-precision numbers (int64, float64, big.Int, big.Float) +// string, for string values +// []interface{}, for array values +// map[string]interface{}, for objects +// nil, for null values // // When unmarshaling, any error that occurs will halt the unmarshal and return the error. type Unmarshaler interface { UnmarshalSmithyDocument(v interface{}) error } +// Value is a sealed type representing a Smithy document value. It covers the +// full Smithy data model including blob and timestamp. +// +// The following types implement Value: +// - [Null] +// - [Boolean] +// - [Number] +// - [String] +// - [Blob] +// - [Timestamp] +// - [List] +// - [Map] +// - [Structure] +// - [Opaque] +type Value interface { + isValue() +} + +// Null is a document null value. +type Null struct{} + +func (Null) isValue() {} + +// Boolean is a document boolean value. +type Boolean bool + +func (Boolean) isValue() {} + +// String is a document string value. +type String string + +func (String) isValue() {} + +// Blob is a document blob value. +type Blob []byte + +func (Blob) isValue() {} + +// Timestamp is a document timestamp value. +type Timestamp time.Time + +func (Timestamp) isValue() {} + +// List is a document list value. +type List []Value + +func (List) isValue() {} + +// Map is a document map value with string keys. +type Map map[string]Value + +func (Map) isValue() {} + +// Structure is a document structure value with an optional discriminator +// identifying the shape it represents. +type Structure struct { + // Discriminator is the absolute shape ID (e.g. + // "com.example#MyShape") of the concrete type this structure + // represents. It may be empty if the type is unknown. + Discriminator string + + // Members maps member names to their document values. + Members map[string]Value +} + +func (Structure) isValue() {} + +// Opaque wraps an arbitrary Go value for backward compatibility with the +// legacy reflection-based document serialization path. +type Opaque struct { + Value any +} + +func (Opaque) isValue() {} + type noSerde interface { noSmithyDocumentSerde() } @@ -96,6 +180,8 @@ func IsNoSerde(x interface{}) bool { // Number is an arbitrary precision numerical value type Number string +func (Number) isValue() {} + // Int64 returns the number as a string. func (n Number) String() string { return string(n) diff --git a/vendor/github.com/aws/smithy-go/encoding/json/value.go b/vendor/github.com/aws/smithy-go/encoding/json/value.go index b41ff1e15c..eac49c44c2 100644 --- a/vendor/github.com/aws/smithy-go/encoding/json/value.go +++ b/vendor/github.com/aws/smithy-go/encoding/json/value.go @@ -106,6 +106,11 @@ func (jv Value) BigInteger(v *big.Int) { // BigDecimal encodes v as JSON value func (jv Value) BigDecimal(v *big.Float) { + if v.Sign() == 0 && v.Signbit() { + // Preserve negative zero sign which Int64() would lose. + jv.w.Write([]byte("-0")) + return + } if i, accuracy := v.Int64(); accuracy == big.Exact { jv.Long(i) return diff --git a/vendor/github.com/aws/smithy-go/endpoints/private/bdd/evaluate.go b/vendor/github.com/aws/smithy-go/endpoints/private/bdd/evaluate.go new file mode 100644 index 0000000000..ae0fb7fdad --- /dev/null +++ b/vendor/github.com/aws/smithy-go/endpoints/private/bdd/evaluate.go @@ -0,0 +1,35 @@ +package bdd + +const resultOffset int32 = 100_000_000 +const intsPerNode = 3 + +// Evaluate traverses a compiled BDD node array and returns the result index. +// nodes is a flat array of [condIdx, hi, lo] triples (1-indexed). +// root is the root node reference. evalCond returns true/false for condition index. +func Evaluate(nodes []int32, root int32, evalCond func(int) bool) int32 { + ref := root + for { + if ref >= resultOffset { + return ref - resultOffset + } + if ref == 1 || ref == -1 { + return 0 // NoMatchRule + } + + complement := ref < 0 + nodeIdx := ref + if complement { + nodeIdx = -ref + } + base := (nodeIdx - 1) * intsPerNode + condIdx := nodes[base] + hi := nodes[base+1] + lo := nodes[base+2] + + if complement != evalCond(int(condIdx)) { + ref = hi + } else { + ref = lo + } + } +} diff --git a/vendor/github.com/aws/smithy-go/endpoints/private/rulesfn/string_slice.go b/vendor/github.com/aws/smithy-go/endpoints/private/rulesfn/string_slice.go new file mode 100644 index 0000000000..7a82fcd94e --- /dev/null +++ b/vendor/github.com/aws/smithy-go/endpoints/private/rulesfn/string_slice.go @@ -0,0 +1,18 @@ +package rulesfn + +// StringSlice is a string slice with a negative-index-aware Get method for use +// in endpoint rule evaluation. +type StringSlice []string + +// Get returns a pointer to the string at index i, or nil if the index is out +// of bounds. Negative indices count from the end of the slice. +func (s StringSlice) Get(i int) *string { + if i < 0 { + i = len(s) + i + } + if i < 0 || i >= len(s) { + return nil + } + v := s[i] + return &v +} diff --git a/vendor/github.com/aws/smithy-go/endpoints/private/rulesfn/uri.go b/vendor/github.com/aws/smithy-go/endpoints/private/rulesfn/uri.go index 0c11541276..68828dbfe9 100644 --- a/vendor/github.com/aws/smithy-go/endpoints/private/rulesfn/uri.go +++ b/vendor/github.com/aws/smithy-go/endpoints/private/rulesfn/uri.go @@ -27,6 +27,9 @@ func IsValidHostLabel(input string, allowSubDomains bool) bool { if !smithyhttp.ValidHostLabel(label) { return false } + if label[0] == '-' || label[len(label)-1] == '-' { + return false + } } return true diff --git a/vendor/github.com/aws/smithy-go/eventstream/const.go b/vendor/github.com/aws/smithy-go/eventstream/const.go new file mode 100644 index 0000000000..893156c5d5 --- /dev/null +++ b/vendor/github.com/aws/smithy-go/eventstream/const.go @@ -0,0 +1,24 @@ +package eventstream + +// EventStream headers with specific meaning to async API functionality. +const ( + ChunkSignatureHeader = `:chunk-signature` // chunk signature for message + DateHeader = `:date` // Date header for signature + ContentTypeHeader = ":content-type" // message payload content-type + + // Message header and values + MessageTypeHeader = `:message-type` // Identifies type of message. + EventMessageType = `event` + ErrorMessageType = `error` + ExceptionMessageType = `exception` + + // Message Events + EventTypeHeader = `:event-type` // Identifies message event type e.g. "Stats". + + // Message Error + ErrorCodeHeader = `:error-code` + ErrorMessageHeader = `:error-message` + + // Message Exception + ExceptionTypeHeader = `:exception-type` +) diff --git a/vendor/github.com/aws/smithy-go/eventstream/debug.go b/vendor/github.com/aws/smithy-go/eventstream/debug.go new file mode 100644 index 0000000000..6049402b1f --- /dev/null +++ b/vendor/github.com/aws/smithy-go/eventstream/debug.go @@ -0,0 +1,144 @@ +package eventstream + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "fmt" + "strconv" +) + +type decodedMessage struct { + rawMessage + Headers decodedHeaders `json:"headers"` +} +type jsonMessage struct { + Length json.Number `json:"total_length"` + HeadersLen json.Number `json:"headers_length"` + PreludeCRC json.Number `json:"prelude_crc"` + Headers decodedHeaders `json:"headers"` + Payload []byte `json:"payload"` + CRC json.Number `json:"message_crc"` +} + +func (d *decodedMessage) UnmarshalJSON(b []byte) (err error) { + var jsonMsg jsonMessage + if err = json.Unmarshal(b, &jsonMsg); err != nil { + return err + } + + d.Length, err = numAsUint32(jsonMsg.Length) + if err != nil { + return err + } + d.HeadersLen, err = numAsUint32(jsonMsg.HeadersLen) + if err != nil { + return err + } + d.PreludeCRC, err = numAsUint32(jsonMsg.PreludeCRC) + if err != nil { + return err + } + d.Headers = jsonMsg.Headers + d.Payload = jsonMsg.Payload + d.CRC, err = numAsUint32(jsonMsg.CRC) + if err != nil { + return err + } + + return nil +} + +func (d *decodedMessage) MarshalJSON() ([]byte, error) { + jsonMsg := jsonMessage{ + Length: json.Number(strconv.Itoa(int(d.Length))), + HeadersLen: json.Number(strconv.Itoa(int(d.HeadersLen))), + PreludeCRC: json.Number(strconv.Itoa(int(d.PreludeCRC))), + Headers: d.Headers, + Payload: d.Payload, + CRC: json.Number(strconv.Itoa(int(d.CRC))), + } + + return json.Marshal(jsonMsg) +} + +func numAsUint32(n json.Number) (uint32, error) { + v, err := n.Int64() + if err != nil { + return 0, fmt.Errorf("failed to get int64 json number, %v", err) + } + + return uint32(v), nil +} + +func (d decodedMessage) Message() Message { + return Message{ + Headers: Headers(d.Headers), + Payload: d.Payload, + } +} + +type decodedHeaders Headers + +func (hs *decodedHeaders) UnmarshalJSON(b []byte) error { + var jsonHeaders []struct { + Name string `json:"name"` + Type valueType `json:"type"` + Value any `json:"value"` + } + + decoder := json.NewDecoder(bytes.NewReader(b)) + decoder.UseNumber() + if err := decoder.Decode(&jsonHeaders); err != nil { + return err + } + + var headers Headers + for _, h := range jsonHeaders { + value, err := valueFromType(h.Type, h.Value) + if err != nil { + return err + } + headers.Set(h.Name, value) + } + *hs = decodedHeaders(headers) + + return nil +} + +func valueFromType(typ valueType, val any) (Value, error) { + switch typ { + case trueValueType: + return BoolValue(true), nil + case falseValueType: + return BoolValue(false), nil + case int8ValueType: + v, err := val.(json.Number).Int64() + return Int8Value(int8(v)), err + case int16ValueType: + v, err := val.(json.Number).Int64() + return Int16Value(int16(v)), err + case int32ValueType: + v, err := val.(json.Number).Int64() + return Int32Value(int32(v)), err + case int64ValueType: + v, err := val.(json.Number).Int64() + return Int64Value(v), err + case bytesValueType: + v, err := base64.StdEncoding.DecodeString(val.(string)) + return BytesValue(v), err + case stringValueType: + v, err := base64.StdEncoding.DecodeString(val.(string)) + return StringValue(string(v)), err + case timestampValueType: + v, err := val.(json.Number).Int64() + return TimestampValue(timeFromEpochMilli(v)), err + case uuidValueType: + v, err := base64.StdEncoding.DecodeString(val.(string)) + var tv UUIDValue + copy(tv[:], v) + return tv, err + default: + panic(fmt.Sprintf("unknown type, %s, %T", typ.String(), val)) + } +} diff --git a/vendor/github.com/aws/smithy-go/eventstream/decode.go b/vendor/github.com/aws/smithy-go/eventstream/decode.go new file mode 100644 index 0000000000..d9ab7652f4 --- /dev/null +++ b/vendor/github.com/aws/smithy-go/eventstream/decode.go @@ -0,0 +1,218 @@ +package eventstream + +import ( + "bytes" + "encoding/binary" + "encoding/hex" + "encoding/json" + "fmt" + "github.com/aws/smithy-go/logging" + "hash" + "hash/crc32" + "io" +) + +// DecoderOptions is the Decoder configuration options. +type DecoderOptions struct { + Logger logging.Logger + LogMessages bool +} + +// Decoder provides decoding of an Event Stream messages. +type Decoder struct { + options DecoderOptions +} + +// NewDecoder initializes and returns a Decoder for decoding event +// stream messages from the reader provided. +func NewDecoder(optFns ...func(*DecoderOptions)) *Decoder { + options := DecoderOptions{} + + for _, fn := range optFns { + fn(&options) + } + + return &Decoder{ + options: options, + } +} + +// Decode attempts to decode a single message from the event stream reader. +// Will return the event stream message, or error if decodeMessage fails to read +// the message from the stream. +// +// payloadBuf is a byte slice that will be used in the returned Message.Payload. Callers +// must ensure that the Message.Payload from a previous decode has been consumed before passing in the same underlying +// payloadBuf byte slice. +func (d *Decoder) Decode(reader io.Reader, payloadBuf []byte) (m Message, err error) { + if d.options.Logger != nil && d.options.LogMessages { + debugMsgBuf := bytes.NewBuffer(nil) + reader = io.TeeReader(reader, debugMsgBuf) + defer func() { + logMessageDecode(d.options.Logger, debugMsgBuf, m, err) + }() + } + + m, err = decodeMessage(reader, payloadBuf) + + return m, err +} + +// decodeMessage attempts to decode a single message from the event stream reader. +// Will return the event stream message, or error if decodeMessage fails to read +// the message from the reader. +func decodeMessage(reader io.Reader, payloadBuf []byte) (m Message, err error) { + crc := crc32.New(crc32IEEETable) + hashReader := io.TeeReader(reader, crc) + + prelude, err := decodePrelude(hashReader, crc) + if err != nil { + return Message{}, err + } + + if prelude.HeadersLen > 0 { + lr := io.LimitReader(hashReader, int64(prelude.HeadersLen)) + m.Headers, err = decodeHeaders(lr) + if err != nil { + return Message{}, err + } + } + + if payloadLen := prelude.PayloadLen(); payloadLen > 0 { + buf, err := decodePayload(payloadBuf, io.LimitReader(hashReader, int64(payloadLen))) + if err != nil { + return Message{}, err + } + m.Payload = buf + } + + msgCRC := crc.Sum32() + if err := validateCRC(reader, msgCRC); err != nil { + return Message{}, err + } + + return m, nil +} + +func logMessageDecode(logger logging.Logger, msgBuf *bytes.Buffer, msg Message, decodeErr error) { + w := bytes.NewBuffer(nil) + defer func() { logger.Logf(logging.Debug, w.String()) }() + + fmt.Fprintf(w, "Raw message:\n%s\n", + hex.Dump(msgBuf.Bytes())) + + if decodeErr != nil { + fmt.Fprintf(w, "decodeMessage error: %v\n", decodeErr) + return + } + + rawMsg, err := msg.rawMessage() + if err != nil { + fmt.Fprintf(w, "failed to create raw message, %v\n", err) + return + } + + decodedMsg := decodedMessage{ + rawMessage: rawMsg, + Headers: decodedHeaders(msg.Headers), + } + + fmt.Fprintf(w, "Decoded message:\n") + encoder := json.NewEncoder(w) + if err := encoder.Encode(decodedMsg); err != nil { + fmt.Fprintf(w, "failed to generate decoded message, %v\n", err) + } +} + +func decodePrelude(r io.Reader, crc hash.Hash32) (messagePrelude, error) { + var p messagePrelude + + var err error + p.Length, err = decodeUint32(r) + if err != nil { + return messagePrelude{}, err + } + + p.HeadersLen, err = decodeUint32(r) + if err != nil { + return messagePrelude{}, err + } + + if err := p.ValidateLens(); err != nil { + return messagePrelude{}, err + } + + preludeCRC := crc.Sum32() + if err := validateCRC(r, preludeCRC); err != nil { + return messagePrelude{}, err + } + + p.PreludeCRC = preludeCRC + + return p, nil +} + +func decodePayload(buf []byte, r io.Reader) ([]byte, error) { + w := bytes.NewBuffer(buf[0:0]) + + _, err := io.Copy(w, r) + return w.Bytes(), err +} + +func decodeUint8(r io.Reader) (uint8, error) { + type byteReader interface { + ReadByte() (byte, error) + } + + if br, ok := r.(byteReader); ok { + v, err := br.ReadByte() + return v, err + } + + var b [1]byte + _, err := io.ReadFull(r, b[:]) + return b[0], err +} + +func decodeUint16(r io.Reader) (uint16, error) { + var b [2]byte + bs := b[:] + _, err := io.ReadFull(r, bs) + if err != nil { + return 0, err + } + return binary.BigEndian.Uint16(bs), nil +} + +func decodeUint32(r io.Reader) (uint32, error) { + var b [4]byte + bs := b[:] + _, err := io.ReadFull(r, bs) + if err != nil { + return 0, err + } + return binary.BigEndian.Uint32(bs), nil +} + +func decodeUint64(r io.Reader) (uint64, error) { + var b [8]byte + bs := b[:] + _, err := io.ReadFull(r, bs) + if err != nil { + return 0, err + } + return binary.BigEndian.Uint64(bs), nil +} + +func validateCRC(r io.Reader, expect uint32) error { + msgCRC, err := decodeUint32(r) + if err != nil { + return err + } + + if msgCRC != expect { + return ChecksumError{} + } + + return nil +} diff --git a/vendor/github.com/aws/smithy-go/eventstream/deserializer.go b/vendor/github.com/aws/smithy-go/eventstream/deserializer.go new file mode 100644 index 0000000000..8bc931a324 --- /dev/null +++ b/vendor/github.com/aws/smithy-go/eventstream/deserializer.go @@ -0,0 +1,294 @@ +package eventstream + +import ( + "fmt" + "math/big" + "time" + + "github.com/aws/smithy-go" + "github.com/aws/smithy-go/document" + "github.com/aws/smithy-go/traits" +) + +// ShapeDeserializer wraps a [smithy.ShapeDeserializer] to handle event stream +// message binding traits. +type ShapeDeserializer struct { + Message *Message + + inner smithy.ShapeDeserializer + + depth int + schema *smithy.Schema + + bindings []*smithy.Schema + bindIdx int + inBindings bool + + inBody bool + hasPayload bool + hasBody bool +} + +var _ smithy.ShapeDeserializer = (*ShapeDeserializer)(nil) + +// NewShapeDeserializer returns a deserializer for a Message. +func NewShapeDeserializer(msg *Message, inner smithy.ShapeDeserializer) *ShapeDeserializer { + return &ShapeDeserializer{ + Message: msg, + inner: inner, + } +} + +func (d *ShapeDeserializer) ReadStruct(s *smithy.Schema) error { + d.depth++ + if d.depth > 1 { + return d.inner.ReadStruct(s) + } + d.schema = s + for _, m := range s.Members() { + if _, ok := smithy.SchemaTrait[*traits.EventPayload](m); ok { + d.hasPayload = true + } + if isEventBound(m) { + d.bindings = append(d.bindings, m) + } else { + d.hasBody = true + } + } + return nil +} + +func (d *ShapeDeserializer) ReadStructMember() (*smithy.Schema, error) { + if d.depth > 1 { + ms, err := d.inner.ReadStructMember() + if ms == nil { + d.depth-- + } + return ms, err + } + + // like httpbinding, throw back the bound stuff first before we drop into + // the body + for d.bindIdx < len(d.bindings) { + m := d.bindings[d.bindIdx] + d.bindIdx++ + if isEventHeader(m) && d.Message.Headers.Get(m.MemberName()) == nil { + continue + } + d.inBindings = true + return m, nil + } + d.inBindings = false + + if d.hasPayload { + d.depth-- + return nil, nil + } + + if !d.hasBody { + d.depth-- + return nil, nil + } + + if !d.inBody { + d.inBody = true + if err := d.inner.ReadStruct(d.schema); err != nil { + return nil, err + } + } + + ms, err := d.inner.ReadStructMember() + if ms == nil { + d.depth-- + } + + return ms, err +} + +func (d *ShapeDeserializer) ReadString(s *smithy.Schema, v *string) error { + if d.inBindings { + if isEventHeader(s) { + hv := d.Message.Headers.Get(s.MemberName()) + if hv == nil { + return nil + } + sv, ok := hv.(StringValue) + if !ok { + return fmt.Errorf("event header %q: expected string, got %T", s.MemberName(), hv) + } + *v = string(sv) + return nil + } + if isEventPayload(s) { + *v = string(d.Message.Payload) + return nil + } + } + return d.inner.ReadString(s, v) +} + +func (d *ShapeDeserializer) ReadBool(s *smithy.Schema, v *bool) error { + if d.inBindings && isEventHeader(s) { + hv := d.Message.Headers.Get(s.MemberName()) + if hv == nil { + return nil + } + bv, ok := hv.(BoolValue) + if !ok { + return fmt.Errorf("event header %q: expected bool, got %T", s.MemberName(), hv) + } + *v = bool(bv) + return nil + } + return d.inner.ReadBool(s, v) +} + +func (d *ShapeDeserializer) readHeaderInt64(name string) (int64, bool, error) { + hv := d.Message.Headers.Get(name) + if hv == nil { + return 0, false, nil + } + switch v := hv.(type) { + case Int8Value: + return int64(v), true, nil + case Int16Value: + return int64(v), true, nil + case Int32Value: + return int64(v), true, nil + case Int64Value: + return int64(v), true, nil + default: + return 0, false, fmt.Errorf("event header %q: expected integer, got %T", name, hv) + } +} + +type intn interface { + int8 | int16 | int32 | int64 +} + +func readEventHeaderInt[T intn](d *ShapeDeserializer, s *smithy.Schema, v *T) error { + n, ok, err := d.readHeaderInt64(s.MemberName()) + if err != nil || !ok { + return err + } + *v = T(n) + return nil +} + +func (d *ShapeDeserializer) ReadInt8(s *smithy.Schema, v *int8) error { + if d.inBindings && isEventHeader(s) { + return readEventHeaderInt(d, s, v) + } + return d.inner.ReadInt8(s, v) +} + +func (d *ShapeDeserializer) ReadInt16(s *smithy.Schema, v *int16) error { + if d.inBindings && isEventHeader(s) { + return readEventHeaderInt(d, s, v) + } + return d.inner.ReadInt16(s, v) +} + +func (d *ShapeDeserializer) ReadInt32(s *smithy.Schema, v *int32) error { + if d.inBindings && isEventHeader(s) { + return readEventHeaderInt(d, s, v) + } + return d.inner.ReadInt32(s, v) +} + +func (d *ShapeDeserializer) ReadInt64(s *smithy.Schema, v *int64) error { + if d.inBindings && isEventHeader(s) { + return readEventHeaderInt(d, s, v) + } + return d.inner.ReadInt64(s, v) +} + +func (d *ShapeDeserializer) ReadFloat32(s *smithy.Schema, v *float32) error { + return d.inner.ReadFloat32(s, v) +} + +func (d *ShapeDeserializer) ReadFloat64(s *smithy.Schema, v *float64) error { + return d.inner.ReadFloat64(s, v) +} + +func (d *ShapeDeserializer) ReadBlob(s *smithy.Schema, v *[]byte) error { + if d.inBindings { + if isEventHeader(s) { + hv := d.Message.Headers.Get(s.MemberName()) + if hv == nil { + return nil + } + bv, ok := hv.(BytesValue) + if !ok { + return fmt.Errorf("event header %q: expected bytes, got %T", s.MemberName(), hv) + } + *v = []byte(bv) + return nil + } + if isEventPayload(s) { + *v = d.Message.Payload + return nil + } + } + return d.inner.ReadBlob(s, v) +} + +func (d *ShapeDeserializer) ReadTime(s *smithy.Schema, v *time.Time) error { + if d.inBindings && isEventHeader(s) { + hv := d.Message.Headers.Get(s.MemberName()) + if hv == nil { + return nil + } + tv, ok := hv.(TimestampValue) + if !ok { + return fmt.Errorf("event header %q: expected timestamp, got %T", s.MemberName(), hv) + } + *v = time.Time(tv) + return nil + } + return d.inner.ReadTime(s, v) +} + +func (d *ShapeDeserializer) ReadList(s *smithy.Schema) error { + return d.inner.ReadList(s) +} + +func (d *ShapeDeserializer) ReadListItem(s *smithy.Schema) (bool, error) { + return d.inner.ReadListItem(s) +} + +func (d *ShapeDeserializer) ReadMap(s *smithy.Schema) error { + return d.inner.ReadMap(s) +} + +func (d *ShapeDeserializer) ReadMapKey(s *smithy.Schema) (string, bool, error) { + return d.inner.ReadMapKey(s) +} + +func (d *ShapeDeserializer) ReadUnion(s *smithy.Schema) (*smithy.Schema, error) { + return d.inner.ReadUnion(s) +} + +func (d *ShapeDeserializer) ReadNil(s *smithy.Schema) (bool, error) { + return d.inner.ReadNil(s) +} + +func (d *ShapeDeserializer) ReadDocument(s *smithy.Schema, v *document.Value) error { + return d.inner.ReadDocument(s, v) +} + +func isEventBound(schema *smithy.Schema) bool { + _, h := smithy.SchemaTrait[*traits.EventHeader](schema) + _, p := smithy.SchemaTrait[*traits.EventPayload](schema) + return h || p +} + +// ReadBigInt is unimplemented and will return an error. +func (d *ShapeDeserializer) ReadBigInt(_ *smithy.Schema, _ *big.Int) error { + return fmt.Errorf("unimplemented") +} + +// ReadBigFloat is unimplemented and will return an error. +func (d *ShapeDeserializer) ReadBigFloat(_ *smithy.Schema, _ *big.Float) error { + return fmt.Errorf("unimplemented") +} diff --git a/vendor/github.com/aws/smithy-go/eventstream/encode.go b/vendor/github.com/aws/smithy-go/eventstream/encode.go new file mode 100644 index 0000000000..61cf7238d9 --- /dev/null +++ b/vendor/github.com/aws/smithy-go/eventstream/encode.go @@ -0,0 +1,167 @@ +package eventstream + +import ( + "bytes" + "encoding/binary" + "encoding/hex" + "encoding/json" + "fmt" + "github.com/aws/smithy-go/logging" + "hash" + "hash/crc32" + "io" +) + +// EncoderOptions is the configuration options for Encoder. +type EncoderOptions struct { + Logger logging.Logger + LogMessages bool +} + +// Encoder provides EventStream message encoding. +type Encoder struct { + options EncoderOptions + + headersBuf *bytes.Buffer + messageBuf *bytes.Buffer +} + +// NewEncoder initializes and returns an Encoder to encode Event Stream +// messages. +func NewEncoder(optFns ...func(*EncoderOptions)) *Encoder { + o := EncoderOptions{} + + for _, fn := range optFns { + fn(&o) + } + + return &Encoder{ + options: o, + headersBuf: bytes.NewBuffer(nil), + messageBuf: bytes.NewBuffer(nil), + } +} + +// Encode encodes a single EventStream message to the io.Writer the Encoder +// was created with. An error is returned if writing the message fails. +func (e *Encoder) Encode(w io.Writer, msg Message) (err error) { + e.headersBuf.Reset() + e.messageBuf.Reset() + + var writer io.Writer = e.messageBuf + if e.options.Logger != nil && e.options.LogMessages { + encodeMsgBuf := bytes.NewBuffer(nil) + writer = io.MultiWriter(writer, encodeMsgBuf) + defer func() { + logMessageEncode(e.options.Logger, encodeMsgBuf, msg, err) + }() + } + + if err = EncodeHeaders(e.headersBuf, msg.Headers); err != nil { + return err + } + + crc := crc32.New(crc32IEEETable) + hashWriter := io.MultiWriter(writer, crc) + + headersLen := uint32(e.headersBuf.Len()) + payloadLen := uint32(len(msg.Payload)) + + if err = encodePrelude(hashWriter, crc, headersLen, payloadLen); err != nil { + return err + } + + if headersLen > 0 { + if _, err = io.Copy(hashWriter, e.headersBuf); err != nil { + return err + } + } + + if payloadLen > 0 { + if _, err = hashWriter.Write(msg.Payload); err != nil { + return err + } + } + + msgCRC := crc.Sum32() + if err := binary.Write(writer, binary.BigEndian, msgCRC); err != nil { + return err + } + + _, err = io.Copy(w, e.messageBuf) + + return err +} + +func logMessageEncode(logger logging.Logger, msgBuf *bytes.Buffer, msg Message, encodeErr error) { + w := bytes.NewBuffer(nil) + defer func() { logger.Logf(logging.Debug, w.String()) }() + + fmt.Fprintf(w, "Message to encode:\n") + encoder := json.NewEncoder(w) + if err := encoder.Encode(msg); err != nil { + fmt.Fprintf(w, "Failed to get encoded message, %v\n", err) + } + + if encodeErr != nil { + fmt.Fprintf(w, "Encode error: %v\n", encodeErr) + return + } + + fmt.Fprintf(w, "Raw message:\n%s\n", hex.Dump(msgBuf.Bytes())) +} + +func encodePrelude(w io.Writer, crc hash.Hash32, headersLen, payloadLen uint32) error { + p := messagePrelude{ + Length: minMsgLen + headersLen + payloadLen, + HeadersLen: headersLen, + } + if err := p.ValidateLens(); err != nil { + return err + } + + err := binaryWriteFields(w, binary.BigEndian, + p.Length, + p.HeadersLen, + ) + if err != nil { + return err + } + + p.PreludeCRC = crc.Sum32() + err = binary.Write(w, binary.BigEndian, p.PreludeCRC) + if err != nil { + return err + } + + return nil +} + +// EncodeHeaders writes the header values to the writer encoded in the event +// stream format. Returns an error if a header fails to encode. +func EncodeHeaders(w io.Writer, headers Headers) error { + for _, h := range headers { + hn := headerName{ + Len: uint8(len(h.Name)), + } + copy(hn.Name[:hn.Len], h.Name) + if err := hn.encode(w); err != nil { + return err + } + + if err := h.Value.encode(w); err != nil { + return err + } + } + + return nil +} + +func binaryWriteFields(w io.Writer, order binary.ByteOrder, vs ...any) error { + for _, v := range vs { + if err := binary.Write(w, order, v); err != nil { + return err + } + } + return nil +} diff --git a/vendor/github.com/aws/smithy-go/eventstream/error.go b/vendor/github.com/aws/smithy-go/eventstream/error.go new file mode 100644 index 0000000000..7616214dd6 --- /dev/null +++ b/vendor/github.com/aws/smithy-go/eventstream/error.go @@ -0,0 +1,23 @@ +package eventstream + +import "fmt" + +// LengthError provides the error for items being larger than a maximum length. +type LengthError struct { + Part string + Want int + Have int + Value any +} + +func (e LengthError) Error() string { + return fmt.Sprintf("%s length invalid, %d/%d, %v", + e.Part, e.Want, e.Have, e.Value) +} + +// ChecksumError provides the error for message checksum invalidation errors. +type ChecksumError struct{} + +func (e ChecksumError) Error() string { + return "message checksum mismatch" +} diff --git a/vendor/github.com/aws/smithy-go/eventstream/header.go b/vendor/github.com/aws/smithy-go/eventstream/header.go new file mode 100644 index 0000000000..f580bda4c0 --- /dev/null +++ b/vendor/github.com/aws/smithy-go/eventstream/header.go @@ -0,0 +1,175 @@ +package eventstream + +import ( + "encoding/binary" + "fmt" + "io" +) + +// Headers are a collection of EventStream header values. +type Headers []Header + +// Header is a single EventStream Key Value header pair. +type Header struct { + Name string + Value Value +} + +// Set associates the name with a value. If the header name already exists in +// the Headers the value will be replaced with the new one. +func (hs *Headers) Set(name string, value Value) { + var i int + for ; i < len(*hs); i++ { + if (*hs)[i].Name == name { + (*hs)[i].Value = value + return + } + } + + *hs = append(*hs, Header{ + Name: name, Value: value, + }) +} + +// Get returns the Value associated with the header. Nil is returned if the +// value does not exist. +func (hs Headers) Get(name string) Value { + for i := range hs { + if h := hs[i]; h.Name == name { + return h.Value + } + } + return nil +} + +// Del deletes the value in the Headers if it exists. +func (hs *Headers) Del(name string) { + for i := 0; i < len(*hs); i++ { + if (*hs)[i].Name == name { + copy((*hs)[i:], (*hs)[i+1:]) + (*hs) = (*hs)[:len(*hs)-1] + } + } +} + +// Clone returns a deep copy of the headers +func (hs Headers) Clone() Headers { + o := make(Headers, 0, len(hs)) + for _, h := range hs { + o.Set(h.Name, h.Value) + } + return o +} + +func decodeHeaders(r io.Reader) (Headers, error) { + hs := Headers{} + + for { + name, err := decodeHeaderName(r) + if err != nil { + if err == io.EOF { + // EOF while getting header name means no more headers + break + } + return nil, err + } + + value, err := decodeHeaderValue(r) + if err != nil { + return nil, err + } + + hs.Set(name, value) + } + + return hs, nil +} + +func decodeHeaderName(r io.Reader) (string, error) { + var n headerName + + var err error + n.Len, err = decodeUint8(r) + if err != nil { + return "", err + } + + name := n.Name[:n.Len] + if _, err := io.ReadFull(r, name); err != nil { + return "", err + } + + return string(name), nil +} + +func decodeHeaderValue(r io.Reader) (Value, error) { + var raw rawValue + + typ, err := decodeUint8(r) + if err != nil { + return nil, err + } + raw.Type = valueType(typ) + + var v Value + + switch raw.Type { + case trueValueType: + v = BoolValue(true) + case falseValueType: + v = BoolValue(false) + case int8ValueType: + var tv Int8Value + err = tv.decode(r) + v = tv + case int16ValueType: + var tv Int16Value + err = tv.decode(r) + v = tv + case int32ValueType: + var tv Int32Value + err = tv.decode(r) + v = tv + case int64ValueType: + var tv Int64Value + err = tv.decode(r) + v = tv + case bytesValueType: + var tv BytesValue + err = tv.decode(r) + v = tv + case stringValueType: + var tv StringValue + err = tv.decode(r) + v = tv + case timestampValueType: + var tv TimestampValue + err = tv.decode(r) + v = tv + case uuidValueType: + var tv UUIDValue + err = tv.decode(r) + v = tv + default: + panic(fmt.Sprintf("unknown value type %d", raw.Type)) + } + + // Error could be EOF, let caller deal with it + return v, err +} + +const maxHeaderNameLen = 255 + +type headerName struct { + Len uint8 + Name [maxHeaderNameLen]byte +} + +func (v headerName) encode(w io.Writer) error { + if err := binary.Write(w, binary.BigEndian, v.Len); err != nil { + return err + } + + _, err := w.Write(v.Name[:v.Len]) + return err +} diff --git a/vendor/github.com/aws/smithy-go/eventstream/header_value.go b/vendor/github.com/aws/smithy-go/eventstream/header_value.go new file mode 100644 index 0000000000..61ed35366d --- /dev/null +++ b/vendor/github.com/aws/smithy-go/eventstream/header_value.go @@ -0,0 +1,521 @@ +package eventstream + +import ( + "encoding/base64" + "encoding/binary" + "encoding/hex" + "fmt" + "io" + "strconv" + "time" +) + +const maxHeaderValueLen = 1<<15 - 1 // 2^15-1 or 32KB - 1 + +// valueType is the EventStream header value type. +type valueType uint8 + +// Header value types +const ( + trueValueType valueType = iota + falseValueType + int8ValueType // Byte + int16ValueType // Short + int32ValueType // Integer + int64ValueType // Long + bytesValueType + stringValueType + timestampValueType + uuidValueType +) + +func (t valueType) String() string { + switch t { + case trueValueType: + return "bool" + case falseValueType: + return "bool" + case int8ValueType: + return "int8" + case int16ValueType: + return "int16" + case int32ValueType: + return "int32" + case int64ValueType: + return "int64" + case bytesValueType: + return "byte_array" + case stringValueType: + return "string" + case timestampValueType: + return "timestamp" + case uuidValueType: + return "uuid" + default: + return fmt.Sprintf("unknown value type %d", uint8(t)) + } +} + +type rawValue struct { + Type valueType + Len uint16 // Only set for variable length slices + Value []byte // byte representation of value, BigEndian encoding. +} + +func (r rawValue) encodeScalar(w io.Writer, v any) error { + return binaryWriteFields(w, binary.BigEndian, + r.Type, + v, + ) +} + +func (r rawValue) encodeFixedSlice(w io.Writer, v []byte) error { + binary.Write(w, binary.BigEndian, r.Type) + + _, err := w.Write(v) + return err +} + +func (r rawValue) encodeBytes(w io.Writer, v []byte) error { + if len(v) > maxHeaderValueLen { + return LengthError{ + Part: "header value", + Want: maxHeaderValueLen, Have: len(v), + Value: v, + } + } + r.Len = uint16(len(v)) + + err := binaryWriteFields(w, binary.BigEndian, + r.Type, + r.Len, + ) + if err != nil { + return err + } + + _, err = w.Write(v) + return err +} + +func (r rawValue) encodeString(w io.Writer, v string) error { + if len(v) > maxHeaderValueLen { + return LengthError{ + Part: "header value", + Want: maxHeaderValueLen, Have: len(v), + Value: v, + } + } + r.Len = uint16(len(v)) + + type stringWriter interface { + WriteString(string) (int, error) + } + + err := binaryWriteFields(w, binary.BigEndian, + r.Type, + r.Len, + ) + if err != nil { + return err + } + + if sw, ok := w.(stringWriter); ok { + _, err = sw.WriteString(v) + } else { + _, err = w.Write([]byte(v)) + } + + return err +} + +func decodeFixedBytesValue(r io.Reader, buf []byte) error { + _, err := io.ReadFull(r, buf) + return err +} + +func decodeBytesValue(r io.Reader) ([]byte, error) { + var raw rawValue + var err error + raw.Len, err = decodeUint16(r) + if err != nil { + return nil, err + } + + buf := make([]byte, raw.Len) + _, err = io.ReadFull(r, buf) + if err != nil { + return nil, err + } + + return buf, nil +} + +func decodeStringValue(r io.Reader) (string, error) { + v, err := decodeBytesValue(r) + return string(v), err +} + +// Value represents the abstract header value. +type Value interface { + Get() any + String() string + valueType() valueType + encode(io.Writer) error +} + +// An BoolValue provides eventstream encoding, and representation +// of a Go bool value. +type BoolValue bool + +// Get returns the underlying type +func (v BoolValue) Get() any { + return bool(v) +} + +// valueType returns the EventStream header value type value. +func (v BoolValue) valueType() valueType { + if v { + return trueValueType + } + return falseValueType +} + +func (v BoolValue) String() string { + return strconv.FormatBool(bool(v)) +} + +// encode encodes the BoolValue into an eventstream binary value +// representation. +func (v BoolValue) encode(w io.Writer) error { + return binary.Write(w, binary.BigEndian, v.valueType()) +} + +// An Int8Value provides eventstream encoding, and representation of a Go +// int8 value. +type Int8Value int8 + +// Get returns the underlying value. +func (v Int8Value) Get() any { + return int8(v) +} + +// valueType returns the EventStream header value type value. +func (Int8Value) valueType() valueType { + return int8ValueType +} + +func (v Int8Value) String() string { + return fmt.Sprintf("0x%02x", int8(v)) +} + +// encode encodes the Int8Value into an eventstream binary value +// representation. +func (v Int8Value) encode(w io.Writer) error { + raw := rawValue{ + Type: v.valueType(), + } + + return raw.encodeScalar(w, v) +} + +func (v *Int8Value) decode(r io.Reader) error { + n, err := decodeUint8(r) + if err != nil { + return err + } + + *v = Int8Value(n) + return nil +} + +// An Int16Value provides eventstream encoding, and representation of a Go +// int16 value. +type Int16Value int16 + +// Get returns the underlying value. +func (v Int16Value) Get() any { + return int16(v) +} + +// valueType returns the EventStream header value type value. +func (Int16Value) valueType() valueType { + return int16ValueType +} + +func (v Int16Value) String() string { + return fmt.Sprintf("0x%04x", int16(v)) +} + +// encode encodes the Int16Value into an eventstream binary value +// representation. +func (v Int16Value) encode(w io.Writer) error { + raw := rawValue{ + Type: v.valueType(), + } + return raw.encodeScalar(w, v) +} + +func (v *Int16Value) decode(r io.Reader) error { + n, err := decodeUint16(r) + if err != nil { + return err + } + + *v = Int16Value(n) + return nil +} + +// An Int32Value provides eventstream encoding, and representation of a Go +// int32 value. +type Int32Value int32 + +// Get returns the underlying value. +func (v Int32Value) Get() any { + return int32(v) +} + +// valueType returns the EventStream header value type value. +func (Int32Value) valueType() valueType { + return int32ValueType +} + +func (v Int32Value) String() string { + return fmt.Sprintf("0x%08x", int32(v)) +} + +// encode encodes the Int32Value into an eventstream binary value +// representation. +func (v Int32Value) encode(w io.Writer) error { + raw := rawValue{ + Type: v.valueType(), + } + return raw.encodeScalar(w, v) +} + +func (v *Int32Value) decode(r io.Reader) error { + n, err := decodeUint32(r) + if err != nil { + return err + } + + *v = Int32Value(n) + return nil +} + +// An Int64Value provides eventstream encoding, and representation of a Go +// int64 value. +type Int64Value int64 + +// Get returns the underlying value. +func (v Int64Value) Get() any { + return int64(v) +} + +// valueType returns the EventStream header value type value. +func (Int64Value) valueType() valueType { + return int64ValueType +} + +func (v Int64Value) String() string { + return fmt.Sprintf("0x%016x", int64(v)) +} + +// encode encodes the Int64Value into an eventstream binary value +// representation. +func (v Int64Value) encode(w io.Writer) error { + raw := rawValue{ + Type: v.valueType(), + } + return raw.encodeScalar(w, v) +} + +func (v *Int64Value) decode(r io.Reader) error { + n, err := decodeUint64(r) + if err != nil { + return err + } + + *v = Int64Value(n) + return nil +} + +// An BytesValue provides eventstream encoding, and representation of a Go +// byte slice. +type BytesValue []byte + +// Get returns the underlying value. +func (v BytesValue) Get() any { + return []byte(v) +} + +// valueType returns the EventStream header value type value. +func (BytesValue) valueType() valueType { + return bytesValueType +} + +func (v BytesValue) String() string { + return base64.StdEncoding.EncodeToString([]byte(v)) +} + +// encode encodes the BytesValue into an eventstream binary value +// representation. +func (v BytesValue) encode(w io.Writer) error { + raw := rawValue{ + Type: v.valueType(), + } + + return raw.encodeBytes(w, []byte(v)) +} + +func (v *BytesValue) decode(r io.Reader) error { + buf, err := decodeBytesValue(r) + if err != nil { + return err + } + + *v = BytesValue(buf) + return nil +} + +// An StringValue provides eventstream encoding, and representation of a Go +// string. +type StringValue string + +// Get returns the underlying value. +func (v StringValue) Get() any { + return string(v) +} + +// valueType returns the EventStream header value type value. +func (StringValue) valueType() valueType { + return stringValueType +} + +func (v StringValue) String() string { + return string(v) +} + +// encode encodes the StringValue into an eventstream binary value +// representation. +func (v StringValue) encode(w io.Writer) error { + raw := rawValue{ + Type: v.valueType(), + } + + return raw.encodeString(w, string(v)) +} + +func (v *StringValue) decode(r io.Reader) error { + s, err := decodeStringValue(r) + if err != nil { + return err + } + + *v = StringValue(s) + return nil +} + +// An TimestampValue provides eventstream encoding, and representation of a Go +// timestamp. +type TimestampValue time.Time + +// Get returns the underlying value. +func (v TimestampValue) Get() any { + return time.Time(v) +} + +// valueType returns the EventStream header value type value. +func (TimestampValue) valueType() valueType { + return timestampValueType +} + +func (v TimestampValue) epochMilli() int64 { + nano := time.Time(v).UnixNano() + msec := nano / int64(time.Millisecond) + return msec +} + +func (v TimestampValue) String() string { + msec := v.epochMilli() + return strconv.FormatInt(msec, 10) +} + +// encode encodes the TimestampValue into an eventstream binary value +// representation. +func (v TimestampValue) encode(w io.Writer) error { + raw := rawValue{ + Type: v.valueType(), + } + + msec := v.epochMilli() + return raw.encodeScalar(w, msec) +} + +func (v *TimestampValue) decode(r io.Reader) error { + n, err := decodeUint64(r) + if err != nil { + return err + } + + *v = TimestampValue(timeFromEpochMilli(int64(n))) + return nil +} + +// MarshalJSON implements the json.Marshaler interface +func (v TimestampValue) MarshalJSON() ([]byte, error) { + return []byte(v.String()), nil +} + +func timeFromEpochMilli(t int64) time.Time { + secs := t / 1e3 + msec := t % 1e3 + return time.Unix(secs, msec*int64(time.Millisecond)).UTC() +} + +// An UUIDValue provides eventstream encoding, and representation of a UUID +// value. +type UUIDValue [16]byte + +// Get returns the underlying value. +func (v UUIDValue) Get() any { + return v[:] +} + +// valueType returns the EventStream header value type value. +func (UUIDValue) valueType() valueType { + return uuidValueType +} + +func (v UUIDValue) String() string { + var scratch [36]byte + + const dash = '-' + + hex.Encode(scratch[:8], v[0:4]) + scratch[8] = dash + hex.Encode(scratch[9:13], v[4:6]) + scratch[13] = dash + hex.Encode(scratch[14:18], v[6:8]) + scratch[18] = dash + hex.Encode(scratch[19:23], v[8:10]) + scratch[23] = dash + hex.Encode(scratch[24:], v[10:]) + + return string(scratch[:]) +} + +// encode encodes the UUIDValue into an eventstream binary value +// representation. +func (v UUIDValue) encode(w io.Writer) error { + raw := rawValue{ + Type: v.valueType(), + } + + return raw.encodeFixedSlice(w, v[:]) +} + +func (v *UUIDValue) decode(r io.Reader) error { + tv := (*v)[:] + return decodeFixedBytesValue(r, tv) +} diff --git a/vendor/github.com/aws/smithy-go/eventstream/message.go b/vendor/github.com/aws/smithy-go/eventstream/message.go new file mode 100644 index 0000000000..1a77654f7e --- /dev/null +++ b/vendor/github.com/aws/smithy-go/eventstream/message.go @@ -0,0 +1,99 @@ +package eventstream + +import ( + "bytes" + "encoding/binary" + "hash/crc32" +) + +const preludeLen = 8 +const preludeCRCLen = 4 +const msgCRCLen = 4 +const minMsgLen = preludeLen + preludeCRCLen + msgCRCLen + +var crc32IEEETable = crc32.MakeTable(crc32.IEEE) + +// A Message provides the eventstream message representation. +type Message struct { + Headers Headers + Payload []byte +} + +func (m *Message) rawMessage() (rawMessage, error) { + var raw rawMessage + + if len(m.Headers) > 0 { + var headers bytes.Buffer + if err := EncodeHeaders(&headers, m.Headers); err != nil { + return rawMessage{}, err + } + raw.Headers = headers.Bytes() + raw.HeadersLen = uint32(len(raw.Headers)) + } + + raw.Length = raw.HeadersLen + uint32(len(m.Payload)) + minMsgLen + + hash := crc32.New(crc32IEEETable) + binaryWriteFields(hash, binary.BigEndian, raw.Length, raw.HeadersLen) + raw.PreludeCRC = hash.Sum32() + + binaryWriteFields(hash, binary.BigEndian, raw.PreludeCRC) + + if raw.HeadersLen > 0 { + hash.Write(raw.Headers) + } + + // Read payload bytes and update hash for it as well. + if len(m.Payload) > 0 { + raw.Payload = m.Payload + hash.Write(raw.Payload) + } + + raw.CRC = hash.Sum32() + + return raw, nil +} + +// Clone returns a deep copy of the message. +func (m Message) Clone() Message { + var payload []byte + if m.Payload != nil { + payload = make([]byte, len(m.Payload)) + copy(payload, m.Payload) + } + + return Message{ + Headers: m.Headers.Clone(), + Payload: payload, + } +} + +type messagePrelude struct { + Length uint32 + HeadersLen uint32 + PreludeCRC uint32 +} + +func (p messagePrelude) PayloadLen() uint32 { + return p.Length - p.HeadersLen - minMsgLen +} + +func (p messagePrelude) ValidateLens() error { + if p.Length == 0 { + return LengthError{ + Part: "message prelude", + Want: minMsgLen, + Have: int(p.Length), + } + } + return nil +} + +type rawMessage struct { + messagePrelude + + Headers []byte + Payload []byte + + CRC uint32 +} diff --git a/vendor/github.com/aws/smithy-go/eventstream/serializer.go b/vendor/github.com/aws/smithy-go/eventstream/serializer.go new file mode 100644 index 0000000000..018481e938 --- /dev/null +++ b/vendor/github.com/aws/smithy-go/eventstream/serializer.go @@ -0,0 +1,228 @@ +package eventstream + +import ( + "math/big" + "time" + + "github.com/aws/smithy-go" + "github.com/aws/smithy-go/document" + "github.com/aws/smithy-go/traits" +) + +// ShapeSerializer wraps a [smithy.ShapeSerializer], much like the internal +// httpbinding serializer, to handle event stream message binding traits. +type ShapeSerializer struct { + Message *Message + + inner smithy.ShapeSerializer + contentType string // may be inflenced by bindings + depth int + hasBody bool +} + +var _ smithy.ShapeSerializer = (*ShapeSerializer)(nil) + +// NewShapeSerializer returns a serializer for a single Message. +func NewShapeSerializer(msg *Message, inner smithy.ShapeSerializer) *ShapeSerializer { + return &ShapeSerializer{ + Message: msg, + inner: inner, + } +} + +// ContentType returns the resolved content type for the event message payload +// after serialization, which may be affected by bindings. +func (s *ShapeSerializer) ContentType() string { + return s.contentType +} + +// Bytes returns the serialized body bytes. +func (s *ShapeSerializer) Bytes() []byte { + return s.inner.Bytes() +} + +// WriteBool implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteBool(schema *smithy.Schema, v bool) { + if isEventHeader(schema) { + s.Message.Headers.Set(schema.MemberName(), BoolValue(v)) + return + } + s.inner.WriteBool(schema, v) +} + +// WriteInt8 implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt8(schema *smithy.Schema, v int8) { + if isEventHeader(schema) { + s.Message.Headers.Set(schema.MemberName(), Int8Value(v)) + return + } + s.inner.WriteInt8(schema, v) +} + +// WriteInt16 implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt16(schema *smithy.Schema, v int16) { + if isEventHeader(schema) { + s.Message.Headers.Set(schema.MemberName(), Int16Value(v)) + return + } + s.inner.WriteInt16(schema, v) +} + +// WriteInt32 implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt32(schema *smithy.Schema, v int32) { + if isEventHeader(schema) { + s.Message.Headers.Set(schema.MemberName(), Int32Value(v)) + return + } + s.inner.WriteInt32(schema, v) +} + +// WriteInt64 implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt64(schema *smithy.Schema, v int64) { + if isEventHeader(schema) { + s.Message.Headers.Set(schema.MemberName(), Int64Value(v)) + return + } + s.inner.WriteInt64(schema, v) +} + +// WriteFloat32 implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteFloat32(schema *smithy.Schema, v float32) { + s.inner.WriteFloat32(schema, v) +} + +// WriteFloat64 implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteFloat64(schema *smithy.Schema, v float64) { + s.inner.WriteFloat64(schema, v) +} + +// WriteString implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteString(schema *smithy.Schema, v string) { + if isEventHeader(schema) { + s.Message.Headers.Set(schema.MemberName(), StringValue(v)) + return + } + if isEventPayload(schema) { + s.Message.Payload = []byte(v) + s.contentType = "text/plain" + return + } + s.inner.WriteString(schema, v) +} + +// WriteBlob implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteBlob(schema *smithy.Schema, v []byte) { + if isEventHeader(schema) { + s.Message.Headers.Set(schema.MemberName(), BytesValue(v)) + return + } + if isEventPayload(schema) { + s.Message.Payload = v + s.contentType = "application/octet-stream" + return + } + s.inner.WriteBlob(schema, v) +} + +// WriteTime implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteTime(schema *smithy.Schema, v time.Time) { + if isEventHeader(schema) { + s.Message.Headers.Set(schema.MemberName(), TimestampValue(v)) + return + } + s.inner.WriteTime(schema, v) +} + +// WriteBigInt implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteBigInt(schema *smithy.Schema, v *big.Int) { + s.inner.WriteBigInt(schema, v) +} + +// WriteBigFloat implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteBigFloat(schema *smithy.Schema, v *big.Float) { + s.inner.WriteBigFloat(schema, v) +} + +// WriteStruct implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteStruct(schema *smithy.Schema) { + s.depth++ + if s.depth > 1 { + s.inner.WriteStruct(schema) + return + } + // At depth 1 (the event struct itself), start a JSON body if there are + // implicit body members (members without @eventHeader or @eventPayload). + for _, m := range schema.Members() { + if !isEventBound(m) { + s.inner.WriteStruct(schema) + s.hasBody = true + return + } + } +} + +// CloseStruct implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) CloseStruct() { + if s.depth > 1 || s.hasBody { + s.inner.CloseStruct() + } + if s.depth == 1 { + s.hasBody = false + } + s.depth-- +} + +// WriteUnion implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteUnion(schema, variant *smithy.Schema) { + s.inner.WriteUnion(schema, variant) +} + +// CloseUnion implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) CloseUnion() { + s.inner.CloseUnion() +} + +// WriteNil implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteNil(schema *smithy.Schema) { + s.inner.WriteNil(schema) +} + +// WriteList implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteList(schema *smithy.Schema) { + s.inner.WriteList(schema) +} + +// CloseList implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) CloseList() { + s.inner.CloseList() +} + +// WriteMap implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteMap(schema *smithy.Schema) { + s.inner.WriteMap(schema) +} + +// WriteKey implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteKey(schema *smithy.Schema, key string) { + s.inner.WriteKey(schema, key) +} + +// CloseMap implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) CloseMap() { + s.inner.CloseMap() +} + +// WriteDocument implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteDocument(schema *smithy.Schema, v document.Value) { + s.inner.WriteDocument(schema, v) +} + +func isEventHeader(schema *smithy.Schema) bool { + _, ok := smithy.SchemaTrait[*traits.EventHeader](schema) + return ok +} + +func isEventPayload(schema *smithy.Schema) bool { + _, ok := smithy.SchemaTrait[*traits.EventPayload](schema) + return ok +} diff --git a/vendor/github.com/aws/smithy-go/eventstream/signer.go b/vendor/github.com/aws/smithy-go/eventstream/signer.go new file mode 100644 index 0000000000..69f7779d80 --- /dev/null +++ b/vendor/github.com/aws/smithy-go/eventstream/signer.go @@ -0,0 +1,82 @@ +package eventstream + +import ( + "bytes" + "io" + "time" +) + +// MessageSigner signs event stream message header and payload byte pairs. +// Each invocation chains off the previous signature. +type MessageSigner interface { + SignMessage(headers, payload []byte, signingTime time.Time) ([]byte, error) +} + +// SigningWriter wraps an io.WriteCloser and signs each event stream message +// frame written to it. Each Write call MUST contain exactly one complete +// encoded event stream message frame. +// +// The signing writer wraps each incoming frame in an outer event stream +// message with :date and :chunk-signature headers, then encodes the outer +// message to the underlying writer. +// +// Close sends a signed empty message to signal end-of-stream, then closes +// the underlying writer. +type SigningWriter struct { + writer io.WriteCloser + signer MessageSigner + encoder *Encoder + + headersBuf bytes.Buffer +} + +// NewSigningWriter returns a SigningWriter that signs frames and writes them +// to w. +func NewSigningWriter(w io.WriteCloser, signer MessageSigner) *SigningWriter { + return &SigningWriter{ + writer: w, + signer: signer, + encoder: NewEncoder(), + } +} + +// Write signs a complete event stream message frame and writes the signed +// outer envelope to the underlying writer. +func (s *SigningWriter) Write(frame []byte) (int, error) { + if err := s.signAndWrite(frame); err != nil { + return 0, err + } + return len(frame), nil +} + +// Close sends a signed empty message to signal end-of-stream, then closes +// the underlying writer. +func (s *SigningWriter) Close() error { + if err := s.signAndWrite([]byte{}); err != nil { + _ = s.writer.Close() + return err + } + return s.writer.Close() +} + +func (s *SigningWriter) signAndWrite(payload []byte) error { + now := time.Now().UTC() + + var msg Message + msg.Headers.Set(DateHeader, TimestampValue(now)) + msg.Payload = payload + + s.headersBuf.Reset() + if err := EncodeHeaders(&s.headersBuf, msg.Headers); err != nil { + return err + } + + sig, err := s.signer.SignMessage(s.headersBuf.Bytes(), payload, now) + if err != nil { + return err + } + + msg.Headers.Set(ChunkSignatureHeader, BytesValue(sig)) + + return s.encoder.Encode(s.writer, msg) +} diff --git a/vendor/github.com/aws/smithy-go/eventstream/types.go b/vendor/github.com/aws/smithy-go/eventstream/types.go new file mode 100644 index 0000000000..4627bb2091 --- /dev/null +++ b/vendor/github.com/aws/smithy-go/eventstream/types.go @@ -0,0 +1,26 @@ +package eventstream + +import "github.com/aws/smithy-go" + +// UnknownUnionMember is returned when a union member is returned over the +// wire, but has an unknown tag. +type UnknownUnionMember struct { + Tag string + Value []byte +} + +// Deserialize is a no-op. The raw bytes are already captured in Value. +func (*UnknownUnionMember) Deserialize(smithy.ShapeDeserializer) error { + return nil +} + +// UnknownMessageError provides an error when a message is received from the +// stream, but the reader is unable to determine what kind of message it is. +type UnknownMessageError struct { + Type string + Message *Message +} + +func (e *UnknownMessageError) Error() string { + return "unknown event stream message type, " + e.Type +} diff --git a/vendor/github.com/aws/smithy-go/go_module_metadata.go b/vendor/github.com/aws/smithy-go/go_module_metadata.go index a1e928754a..4277e6869a 100644 --- a/vendor/github.com/aws/smithy-go/go_module_metadata.go +++ b/vendor/github.com/aws/smithy-go/go_module_metadata.go @@ -3,4 +3,4 @@ package smithy // goModuleVersion is the tagged release for this module -const goModuleVersion = "1.25.1" +const goModuleVersion = "1.27.5" diff --git a/vendor/github.com/aws/smithy-go/schema.go b/vendor/github.com/aws/smithy-go/schema.go new file mode 100644 index 0000000000..6293d34b1e --- /dev/null +++ b/vendor/github.com/aws/smithy-go/schema.go @@ -0,0 +1,328 @@ +package smithy + +import ( + "fmt" + "strings" + "sync/atomic" + "unsafe" +) + +// ShapeType is a type of Smithy shape. +// See https://smithy.io/2.0/spec/idl.html#defining-shapes. +type ShapeType int + +// Enumerates ShapeType per the Smithy IDL. +const ( + ShapeTypeBlob ShapeType = iota + ShapeTypeBoolean + ShapeTypeString + ShapeTypeTimestamp + ShapeTypeByte + ShapeTypeShort + ShapeTypeInteger + ShapeTypeLong + ShapeTypeFloat + ShapeTypeDocument + ShapeTypeDouble + ShapeTypeBigDecimal + ShapeTypeBigInteger + ShapeTypeEnum + ShapeTypeIntEnum + ShapeTypeList + ShapeTypeSet + ShapeTypeMap + ShapeTypeStructure + ShapeTypeUnion + ShapeTypeMember + ShapeTypeService + ShapeTypeResource + ShapeTypeOperation +) + +// ShapeID fields of a Smithy shape ID. +type ShapeID struct { + Namespace, Name, Member string +} + +// String returns the IDL microformat for the shape ID. +func (s ShapeID) String() string { + if s.Member == "" { + return fmt.Sprintf("%s#%s", s.Namespace, s.Name) + } + return fmt.Sprintf("%s#%s$%s", s.Namespace, s.Name, s.Member) +} + +func stoid(s string) ShapeID { + ns, n, _ := strings.Cut(s, "#") + n, m, _ := strings.Cut(n, "$") + return ShapeID{ns, n, m} +} + +// Schema encodes information about a shape from a Smithy model. +// +// Generated clients use schemas at runtime to dynamically (de)serialize +// request/responses. +type Schema struct { + id ShapeID + typ ShapeType + members map[string]*Schema // member name -> schema + traits map[ShapeID]Trait // trait ID -> non-indexed traits only + indexed []Trait // indexed trait slots, sized to max index present + directMask uint64 // bitmask: bit i set means indexed[i] was declared directly on this schema + targetID ShapeID // for member schemas, the target's shape ID + + listMember *Schema + mapKey, mapValue *Schema + + ext [numExtensionSlots]unsafe.Pointer // lazily-computed codec extensions, accessed atomically +} + +// NewSchema creates a new Schema with the given shape ID and traits. +func NewSchema(id ShapeID, typ ShapeType, numMembers int, ts ...Trait) *Schema { + s := &Schema{ + id: id, + typ: typ, + members: make(map[string]*Schema, numMembers), + } + for _, t := range ts { + s.addTrait(t, true) + } + return s +} + +func (s *Schema) addTrait(t Trait, direct bool) { + if it, ok := t.(IndexableTrait); ok { + idx := it.TraitIndex() + if idx >= len(s.indexed) { + s.indexed = append(s.indexed, make([]Trait, idx-len(s.indexed)+1)...) + } + s.indexed[idx] = t + if direct { + s.directMask |= 1 << uint(idx) + } + return + } + + if s.traits == nil { + s.traits = map[ShapeID]Trait{} + } + s.traits[t.TraitID()] = t +} + +// AddMember adds a member to the schema derived from the target, with +// optional trait overrides. The member schema is returned for caller +// reference. +// +// The member schema's effective trait view (accessed via [SchemaTrait]) +// inherits all of the target's traits, then applies the overrides. The +// member's direct trait view (accessed via [SchemaDirectTrait]) contains +// only the overrides, i.e. the traits declared directly on the member. +func (s *Schema) AddMember(name string, target *Schema, ts ...Trait) *Schema { + m := &Schema{ + id: ShapeID{Member: name}, + typ: target.typ, + members: target.members, + indexed: cloneIndexed(target.indexed), + traits: cloneTraits(target.traits), + directMask: 0, // inherited traits are not direct + targetID: target.id, + listMember: target.listMember, + mapKey: target.mapKey, + mapValue: target.mapValue, + } + + // member-declared traits override and are direct + for _, t := range ts { + m.addTrait(t, true) + } + + s.members[name] = m + + // Invalidate cached extensions, schema structure changed. + for i := range s.ext { + atomic.StorePointer(&s.ext[i], nil) + } + + switch name { + case "member": + s.listMember = m + case "key": + s.mapKey = m + case "value": + s.mapValue = m + } + return m +} + +func cloneIndexed(src []Trait) []Trait { + if src == nil { + return nil + } + dst := make([]Trait, len(src)) + copy(dst, src) + return dst +} + +func cloneTraits(src map[ShapeID]Trait) map[ShapeID]Trait { + if src == nil { + return nil + } + dst := make(map[ShapeID]Trait, len(src)) + for k, v := range src { + dst[k] = v + } + return dst +} + +// ListMember returns the "member" schema for list types. +func (s *Schema) ListMember() *Schema { + return s.listMember +} + +// MapKey returns the "key" schema for map types. +func (s *Schema) MapKey() *Schema { + return s.mapKey +} + +// MapValue returns the "value" schema for map types. +func (s *Schema) MapValue() *Schema { + return s.mapValue +} + +// MemberName returns the member component of the schema's shape ID. +func (s *Schema) MemberName() string { + return s.id.Member +} + +// ID returns the shape ID of the schema. +func (s *Schema) ID() ShapeID { + return s.id +} + +// TargetID returns the shape ID of the member's target shape. +func (s *Schema) TargetID() ShapeID { + return s.targetID +} + +// Type returns the shape type of the schema. +func (s *Schema) Type() ShapeType { + return s.typ +} + +// Member returns the member schema for the given name, or nil. +func (s *Schema) Member(name string) *Schema { + return s.members[name] +} + +// Members returns the schema's members as a map of name to schema. +func (s *Schema) Members() map[string]*Schema { + return s.members +} + +// OperationSchema describes an operation, which is essentially its own schema +// with additional pointers to its input and output. +type OperationSchema struct { + *Schema + Input, Output *Schema + + inputStream, outputStream bool +} + +// NewOperationSchema returns an OperationSchema for (input, output). +func NewOperationSchema(op, input, output *Schema) *OperationSchema { + return &OperationSchema{ + Schema: op, + Input: input, + Output: output, + inputStream: isEventStream(input), + outputStream: isEventStream(output), + } +} + +// IsInputEventStream reports whether this is an input event stream. +func (s *OperationSchema) IsInputEventStream() bool { + return s.inputStream +} + +// IsOutputEventStream reports whether this is an output event stream. +func (s *OperationSchema) IsOutputEventStream() bool { + return s.outputStream +} + +// ServiceSchema describes a service shape. +type ServiceSchema struct { + *Schema + Version string +} + +// NewServiceSchema returns a ServiceSchema for the given service shape. +func NewServiceSchema(schema *Schema, version string) *ServiceSchema { + return &ServiceSchema{Schema: schema, Version: version} +} + +// SchemaTrait returns the target trait on the schema if it exists. +// +// For member schemas this returns the effective trait, which is the trait +// declared directly on the member if present, else the trait inherited from +// the target shape. +func SchemaTrait[T Trait](s *Schema) (T, bool) { + return schemaTrait[T](s, false) +} + +// SchemaDirectTrait returns the target trait on the schema if it was +// declared directly on the schema. +// +// For member schemas this returns the trait only if it was declared on the +// member itself, ignoring any trait inherited from the target shape. For +// non-member schemas this is equivalent to [SchemaTrait]. +func SchemaDirectTrait[T Trait](s *Schema) (T, bool) { + return schemaTrait[T](s, true) +} + +func schemaTrait[T Trait](s *Schema, directOnly bool) (T, bool) { + var zero T + + if s == nil { + return zero, false + } + + if it, ok := Trait(zero).(IndexableTrait); ok { + idx := it.TraitIndex() + if idx >= len(s.indexed) { + return zero, false + } + if directOnly && s.directMask&(1< indexStreaming && m.indexed[indexStreaming] != nil { + return true + } + } + return false +} diff --git a/vendor/github.com/aws/smithy-go/schema_ext.go b/vendor/github.com/aws/smithy-go/schema_ext.go new file mode 100644 index 0000000000..7503b30b80 --- /dev/null +++ b/vendor/github.com/aws/smithy-go/schema_ext.go @@ -0,0 +1,37 @@ +package smithy + +import ( + "sync/atomic" + "unsafe" +) + +// ExtensionID identifies a schema extension slot. Each codec family +// (JSON, CBOR, etc.) uses a distinct slot to cache precomputed data. +type ExtensionID int + +const numExtensionSlots = 4 + +const ( + ExtJSON ExtensionID = iota // transport/http/protocol/internal/json + ExtCBOR // transport/http/protocol/internal/cbor + ExtXML // transport/http/protocol/internal/xml + ExtQuery // transport/http/protocol/internal/query +) + +// SchemaExtension retrieves or lazily computes the extension for the given +// slot. build is called on first access for a schema and the result is cached. +// The build function must return a pointer to an immutable value. +func SchemaExtension[T any](s *Schema, id ExtensionID, build func(*Schema) *T) *T { + p := atomic.LoadPointer(&s.ext[id]) + if p != nil { + return (*T)(p) + } + return computeSchemaExtension(s, id, build) +} + +//go:noinline +func computeSchemaExtension[T any](s *Schema, id ExtensionID, build func(*Schema) *T) *T { + v := build(s) + atomic.StorePointer(&s.ext[id], unsafe.Pointer(v)) + return v +} diff --git a/vendor/github.com/aws/smithy-go/serde.go b/vendor/github.com/aws/smithy-go/serde.go new file mode 100644 index 0000000000..a9effc5655 --- /dev/null +++ b/vendor/github.com/aws/smithy-go/serde.go @@ -0,0 +1,229 @@ +package smithy + +import ( + "fmt" + "io" + "math/big" + "time" + + "github.com/aws/smithy-go/document" +) + +// ShapeSerializer implements the marshaling of an in-code representation of a +// shape to an unspecified data format, which is determined by the +// implementation. +// +// A ShapeSerializer is consumed by the **code-generated** Serialize() method +// of a modeled structure. For example: +// +// func (v *PutItemInput) Serialize(s smithy.ShapeSerializer) { +// s.WriteStruct(schemas.PutItemInput) +// v.SerializeMembers(s) +// s.CloseStruct() +// } +// +// func (v *PutItemInput) SerializeMembers(s smithy.ShapeSerializer) { +// if v.TableName != nil { +// s.WriteString(schemas.PutItemInput_TableName, *v.TableName) +// } +// if v.Item != nil { +// serializeAttributeMap(s, schemas.PutItemInput_Item, v.Item) +// } +// // ... +// } +type ShapeSerializer interface { + Bytes() []byte + + WriteInt8(*Schema, int8) + WriteInt16(*Schema, int16) + WriteInt32(*Schema, int32) + WriteInt64(*Schema, int64) + WriteFloat32(*Schema, float32) + WriteFloat64(*Schema, float64) + WriteBool(*Schema, bool) + WriteString(*Schema, string) + WriteBigInt(*Schema, *big.Int) + WriteBigFloat(*Schema, *big.Float) + WriteBlob(*Schema, []byte) + WriteTime(*Schema, time.Time) + + WriteUnion(schema, variant *Schema) + CloseUnion() + WriteDocument(*Schema, document.Value) + WriteNil(*Schema) + + WriteStruct(*Schema) + CloseStruct() + + WriteList(*Schema) + CloseList() + + WriteMap(*Schema) + WriteKey(*Schema, string) + CloseMap() +} + +// ShapeDeserializer implements the unmarshaling from some unspecified data +// format to an in-code representation of a shape, which is determined by the +// implementation. +type ShapeDeserializer interface { + ReadInt8(*Schema, *int8) error + ReadInt16(*Schema, *int16) error + ReadInt32(*Schema, *int32) error + ReadInt64(*Schema, *int64) error + ReadFloat32(*Schema, *float32) error + ReadFloat64(*Schema, *float64) error + ReadBool(*Schema, *bool) error + ReadString(*Schema, *string) error + ReadBlob(*Schema, *[]byte) error + ReadTime(*Schema, *time.Time) error + ReadBigInt(*Schema, *big.Int) error + ReadBigFloat(*Schema, *big.Float) error + ReadNil(*Schema) (bool, error) + + ReadStruct(*Schema) error + ReadStructMember() (*Schema, error) + + ReadUnion(*Schema) (*Schema, error) + ReadDocument(*Schema, *document.Value) error + + ReadList(*Schema) error + ReadListItem(*Schema) (hasMoreElements bool, err error) + + ReadMap(*Schema) error + ReadMapKey(*Schema) (key string, hasMoreElements bool, err error) +} + +// Serializable is an entity that can describe itself to a ShapeSerializer to +// be encoded to some format. +// +// Unlike the standard library marshaler interfaces, which idiomatically encode +// to []byte, the output format and data type here is not specified at all. +// This is because Smithy shapes need to encode to a variety of formats or data +// carriers. For example, HTTP-binding JSON protocols need to serialize some +// members to bytes (the HTTP request body) and others directly to fields on +// the HTTP request itself (e.g. headers). +type Serializable interface { + Serialize(ShapeSerializer) +} + +// StreamingInput is implemented by input types that have a streaming blob +// payload (an io.Reader member with @httpPayload + @streaming). +type StreamingInput interface { + GetPayloadStream() io.Reader +} + +// StreamingOutput is implemented by output types that have a streaming blob +// payload (an io.ReadCloser member with @httpPayload + @streaming). +type StreamingOutput interface { + SetPayloadStream(io.ReadCloser) +} + +// Deserializable is an entity that can unmarshal itself from a +// ShapeDeserializer. +type Deserializable interface { + Deserialize(ShapeDeserializer) error +} + +// DeserializableError is implemented by modeled error types for a service. +type DeserializableError interface { + Deserializable + error +} + +// ReadUnion is a utility API for generated clients. +func ReadUnion(d ShapeDeserializer, schema *Schema, memberFn func(*Schema) error) error { + ms, err := d.ReadUnion(schema) + if ms == nil || err != nil { + return err + } + + if err := memberFn(ms); err != nil { + return err + } + + for { + ms, err = d.ReadUnion(schema) + if err != nil { + return err + } + if ms == nil { + return nil + } + return fmt.Errorf("union has more than one non-nil member: %s", ms.MemberName()) + } +} + +// ReadStruct is a utility API for generated clients. +func ReadStruct(d ShapeDeserializer, schema *Schema, memberFn func(*Schema) error) error { + if err := d.ReadStruct(schema); err != nil { + return err + } + + for { + ms, err := d.ReadStructMember() + if err != nil { + return err + } + + if ms == nil { + return nil + } + + if err := memberFn(ms); err != nil { + return err + } + } +} + +// ReadList is a utility API for generated clients. +func ReadList(d ShapeDeserializer, schema *Schema, memberFn func() error) error { + if err := d.ReadList(schema); err != nil { + return err + } + + var memberSchema *Schema + if schema != nil { + memberSchema = schema.ListMember() + } + + for { + ok, err := d.ReadListItem(memberSchema) + if !ok { + return nil + } + if err != nil { + return err + } + + if err := memberFn(); err != nil { + return err + } + } +} + +// ReadMap is a utility API for generated clients. +func ReadMap(d ShapeDeserializer, schema *Schema, memberFn func(string) error) error { + if err := d.ReadMap(schema); err != nil { + return err + } + + var keySchema *Schema + if schema != nil { + keySchema = schema.MapKey() + } + + for { + k, ok, err := d.ReadMapKey(keySchema) + if !ok { + return nil + } + if err != nil { + return err + } + + if err := memberFn(k); err != nil { + return err + } + } +} diff --git a/vendor/github.com/aws/smithy-go/sync/error.go b/vendor/github.com/aws/smithy-go/sync/error.go new file mode 100644 index 0000000000..629207672b --- /dev/null +++ b/vendor/github.com/aws/smithy-go/sync/error.go @@ -0,0 +1,53 @@ +package sync + +import "sync" + +// OnceErr wraps the behavior of recording an error +// once and signal on a channel when this has occurred. +// Signaling is done by closing of the channel. +// +// Type is safe for concurrent usage. +type OnceErr struct { + mu sync.RWMutex + err error + ch chan struct{} +} + +// NewOnceErr return a new OnceErr +func NewOnceErr() *OnceErr { + return &OnceErr{ + ch: make(chan struct{}, 1), + } +} + +// Err acquires a read-lock and returns an +// error if one has been set. +func (e *OnceErr) Err() error { + e.mu.RLock() + err := e.err + e.mu.RUnlock() + + return err +} + +// SetError acquires a write-lock and will set +// the underlying error value if one has not been set. +func (e *OnceErr) SetError(err error) { + if err == nil { + return + } + + e.mu.Lock() + if e.err == nil { + e.err = err + close(e.ch) + } + e.mu.Unlock() +} + +// ErrorSet returns a channel that will be used to signal +// that an error has been set. This channel will be closed +// when the error value has been set for OnceErr. +func (e *OnceErr) ErrorSet() <-chan struct{} { + return e.ch +} diff --git a/vendor/github.com/aws/smithy-go/trait.go b/vendor/github.com/aws/smithy-go/trait.go new file mode 100644 index 0000000000..a45db96c07 --- /dev/null +++ b/vendor/github.com/aws/smithy-go/trait.go @@ -0,0 +1,21 @@ +package smithy + +// Trait represents a trait applied to a shape in a Smithy model. Traits +// related to (de)serialization are included in code-generated Schemas for the +// client. +type Trait interface { + TraitID() ShapeID +} + +// IndexableTrait is optionally implemented by Trait values that have a +// reserved index in Schema's indexed trait slice. All traits defined in the +// traits package implement this interface. +// +// You SHOULD NOT implement this outside of a smithy-go trait unless you know +// what you are doing. If you implement this and return a value that collides +// with one of the primary serde-based indexed traits (see index.go) you will +// probably break something. +type IndexableTrait interface { + Trait + TraitIndex() int +} diff --git a/vendor/github.com/aws/smithy-go/traits/http.go b/vendor/github.com/aws/smithy-go/traits/http.go new file mode 100644 index 0000000000..b06e9fed14 --- /dev/null +++ b/vendor/github.com/aws/smithy-go/traits/http.go @@ -0,0 +1,69 @@ +package traits + +import smithy "github.com/aws/smithy-go" + +// HTTPHeader represents smithy.api#httpHeader. +type HTTPHeader struct { + Name string +} + +// TraitID identifies the trait. +func (*HTTPHeader) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "httpHeader"} } + +// HTTPLabel represents smithy.api#httpLabel. +type HTTPLabel struct{} + +// TraitID identifies the trait. +func (*HTTPLabel) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "httpLabel"} } + +// HTTPPayload represents smithy.api#httpPayload. +type HTTPPayload struct{} + +// TraitID identifies the trait. +func (*HTTPPayload) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "httpPayload"} } + +// HTTPPrefixHeaders represents smithy.api#httpPrefixHeaders. +type HTTPPrefixHeaders struct { + Prefix string +} + +// TraitID identifies the trait. +func (*HTTPPrefixHeaders) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "httpPrefixHeaders"} } + +// HTTPQuery represents smithy.api#httpQuery. +type HTTPQuery struct { + Name string +} + +// TraitID identifies the trait. +func (*HTTPQuery) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "httpQuery"} } + +// HTTPQueryParams represents smithy.api#httpQueryParams. +type HTTPQueryParams struct{} + +// TraitID identifies the trait. +func (*HTTPQueryParams) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "httpQueryParams"} } + +// HTTPResponseCode represents smithy.api#httpResponseCode. +type HTTPResponseCode struct{} + +// TraitID identifies the trait. +func (*HTTPResponseCode) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "httpResponseCode"} } + +// HTTP represents smithy.api#http. +type HTTP struct { + Method string + URI string + Code int +} + +// TraitID identifies the trait. +func (*HTTP) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "http"} } + +// HTTPError represents smithy.api#httpError. +type HTTPError struct { + Code int +} + +// TraitID identifies the trait. +func (*HTTPError) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "httpError"} } diff --git a/vendor/github.com/aws/smithy-go/traits/index.go b/vendor/github.com/aws/smithy-go/traits/index.go new file mode 100644 index 0000000000..47733afc6b --- /dev/null +++ b/vendor/github.com/aws/smithy-go/traits/index.go @@ -0,0 +1,107 @@ +package traits + +// Trait index constants, ordered by frequency of occurrence across AWS API +// models. Lower indices are assigned to more common traits so that the +// per-schema indexed slice stays small. +const ( + indexJSONName = iota + indexHTTP + indexHTTPLabel + indexXMLName + indexHTTPQuery + indexEC2QueryName + indexHTTPError + indexHTTPHeader + indexSensitive + indexAWSQueryError + indexTimestampFormat + indexHTTPPayload + indexContextParam + indexHTTPResponseCode + indexHostLabel + indexXMLNamespace + indexXMLFlattened + indexStreaming + indexMediaType + indexHTTPQueryParams + indexEventPayload + indexHTTPPrefixHeaders + indexEventHeader + indexXMLAttribute + indexUnitShape +) + +// TraitIndex implements [smithy.IndexableTrait]. +func (*JSONName) TraitIndex() int { return indexJSONName } + +// TraitIndex implements [smithy.IndexableTrait]. +func (*HTTP) TraitIndex() int { return indexHTTP } + +// TraitIndex implements [smithy.IndexableTrait]. +func (*HTTPLabel) TraitIndex() int { return indexHTTPLabel } + +// TraitIndex implements [smithy.IndexableTrait]. +func (*XMLName) TraitIndex() int { return indexXMLName } + +// TraitIndex implements [smithy.IndexableTrait]. +func (*HTTPQuery) TraitIndex() int { return indexHTTPQuery } + +// TraitIndex implements [smithy.IndexableTrait]. +func (*EC2QueryName) TraitIndex() int { return indexEC2QueryName } + +// TraitIndex implements [smithy.IndexableTrait]. +func (*HTTPError) TraitIndex() int { return indexHTTPError } + +// TraitIndex implements [smithy.IndexableTrait]. +func (*HTTPHeader) TraitIndex() int { return indexHTTPHeader } + +// TraitIndex implements [smithy.IndexableTrait]. +func (*Sensitive) TraitIndex() int { return indexSensitive } + +// TraitIndex implements [smithy.IndexableTrait]. +func (*AWSQueryError) TraitIndex() int { return indexAWSQueryError } + +// TraitIndex implements [smithy.IndexableTrait]. +func (*TimestampFormat) TraitIndex() int { return indexTimestampFormat } + +// TraitIndex implements [smithy.IndexableTrait]. +func (*HTTPPayload) TraitIndex() int { return indexHTTPPayload } + +// TraitIndex implements [smithy.IndexableTrait]. +func (*ContextParam) TraitIndex() int { return indexContextParam } + +// TraitIndex implements [smithy.IndexableTrait]. +func (*HTTPResponseCode) TraitIndex() int { return indexHTTPResponseCode } + +// TraitIndex implements [smithy.IndexableTrait]. +func (*HostLabel) TraitIndex() int { return indexHostLabel } + +// TraitIndex implements [smithy.IndexableTrait]. +func (*XMLNamespace) TraitIndex() int { return indexXMLNamespace } + +// TraitIndex implements [smithy.IndexableTrait]. +func (*XMLFlattened) TraitIndex() int { return indexXMLFlattened } + +// TraitIndex implements [smithy.IndexableTrait]. +func (*Streaming) TraitIndex() int { return indexStreaming } + +// TraitIndex implements [smithy.IndexableTrait]. +func (*MediaType) TraitIndex() int { return indexMediaType } + +// TraitIndex implements [smithy.IndexableTrait]. +func (*HTTPQueryParams) TraitIndex() int { return indexHTTPQueryParams } + +// TraitIndex implements [smithy.IndexableTrait]. +func (*EventPayload) TraitIndex() int { return indexEventPayload } + +// TraitIndex implements [smithy.IndexableTrait]. +func (*HTTPPrefixHeaders) TraitIndex() int { return indexHTTPPrefixHeaders } + +// TraitIndex implements [smithy.IndexableTrait]. +func (*EventHeader) TraitIndex() int { return indexEventHeader } + +// TraitIndex implements [smithy.IndexableTrait]. +func (*XMLAttribute) TraitIndex() int { return indexXMLAttribute } + +// TraitIndex implements [smithy.IndexableTrait]. +func (*UnitShape) TraitIndex() int { return indexUnitShape } diff --git a/vendor/github.com/aws/smithy-go/traits/serde.go b/vendor/github.com/aws/smithy-go/traits/serde.go new file mode 100644 index 0000000000..25b7f0dd33 --- /dev/null +++ b/vendor/github.com/aws/smithy-go/traits/serde.go @@ -0,0 +1,56 @@ +package traits + +import smithy "github.com/aws/smithy-go" + +// JSONName represents smithy.api#jsonName. +type JSONName struct { + Name string +} + +// TraitID identifies the trait. +func (*JSONName) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "jsonName"} } + +// MediaType represents smithy.api#mediaType. +type MediaType struct { + Type string +} + +// TraitID identifies the trait. +func (*MediaType) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "mediaType"} } + +// TimestampFormat represents smithy.api#timestampFormat. +type TimestampFormat struct { + Format string +} + +// TraitID identifies the trait. +func (*TimestampFormat) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "timestampFormat"} } + +// XMLAttribute represents smithy.api#xmlAttribute. +type XMLAttribute struct{} + +// TraitID identifies the trait. +func (*XMLAttribute) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "xmlAttribute"} } + +// XMLFlattened represents smithy.api#xmlFlattened. +type XMLFlattened struct{} + +// TraitID identifies the trait. +func (*XMLFlattened) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "xmlFlattened"} } + +// XMLName represents smithy.api#xmlName. +type XMLName struct { + Name string +} + +// TraitID identifies the trait. +func (*XMLName) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "xmlName"} } + +// XMLNamespace represents smithy.api#xmlNamespace. +type XMLNamespace struct { + URI string + Prefix string +} + +// TraitID identifies the trait. +func (*XMLNamespace) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "xmlNamespace"} } diff --git a/vendor/github.com/aws/smithy-go/traits/traits.go b/vendor/github.com/aws/smithy-go/traits/traits.go new file mode 100644 index 0000000000..599be4e54e --- /dev/null +++ b/vendor/github.com/aws/smithy-go/traits/traits.go @@ -0,0 +1,72 @@ +// Package traits defines representations of Smithy IDL traits that appear in +// code-generated schemas. +package traits + +import smithy "github.com/aws/smithy-go" + +// Sensitive represents smithy.api#sensitive. +type Sensitive struct{} + +// TraitID identifies the trait. +func (*Sensitive) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "sensitive"} } + +// EventHeader represents smithy.api#eventHeader. +type EventHeader struct{} + +// TraitID identifies the trait. +func (*EventHeader) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "eventHeader"} } + +// EventPayload represents smithy.api#eventPayload. +type EventPayload struct{} + +// TraitID identifies the trait. +func (*EventPayload) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "eventPayload"} } + +// Streaming represents smithy.api#streaming. +type Streaming struct{} + +// TraitID identifies the trait. +func (*Streaming) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "streaming"} } + +// HostLabel represents smithy.api#hostLabel. +type HostLabel struct{} + +// TraitID identifies the trait. +func (*HostLabel) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "hostLabel"} } + +// ContextParam represents smithy.rules#contextParam. +type ContextParam struct{} + +// TraitID identifies the trait. +func (*ContextParam) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.rules", Name: "contextParam"} } + +// AWSQueryError represents aws.protocols#awsQueryError. +type AWSQueryError struct { + ErrorCode string + StatusCode int +} + +// TraitID identifies the trait. +func (*AWSQueryError) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "aws.protocols", Name: "awsQueryError"} } + +// EC2QueryName represents aws.protocols#ec2QueryName. +type EC2QueryName struct { + Name string +} + +// TraitID identifies the trait. +func (*EC2QueryName) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "aws.protocols", Name: "ec2QueryName"} } + +// AWSQueryCompatible represents aws.protocols#awsQueryCompatible. +type AWSQueryCompatible struct{} + +// TraitID identifies the trait. +func (*AWSQueryCompatible) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "aws.protocols", Name: "awsQueryCompatible"} } + +// UnitShape is a synthetic trait applied to input/output shapes that were +// backfilled from Unit. It indicates the shape has no defined members and +// should be treated as absent for protocol serialization purposes. +type UnitShape struct{} + +// TraitID identifies the trait. +func (*UnitShape) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.go", Name: "unitShape"} } diff --git a/vendor/github.com/aws/smithy-go/transport/http/auth.go b/vendor/github.com/aws/smithy-go/transport/http/auth.go index 58e1ab5ef8..5b5adad0b4 100644 --- a/vendor/github.com/aws/smithy-go/transport/http/auth.go +++ b/vendor/github.com/aws/smithy-go/transport/http/auth.go @@ -5,6 +5,7 @@ import ( smithy "github.com/aws/smithy-go" "github.com/aws/smithy-go/auth" + "github.com/aws/smithy-go/eventstream" ) // AuthScheme defines an HTTP authentication scheme. @@ -19,3 +20,11 @@ type AuthScheme interface { type Signer interface { SignRequest(context.Context, *Request, auth.Identity, smithy.Properties) error } + +// EventStreamSigner is an optional interface that a [Signer] can implement to +// support signing of event stream messages. If the resolved auth scheme's +// signer implements this interface, the event stream middleware will use it to +// wrap the outbound message stream with a signing layer. +type EventStreamSigner interface { + NewMessageSigner(ctx context.Context, r *Request, identity auth.Identity, props smithy.Properties) (eventstream.MessageSigner, error) +} diff --git a/vendor/github.com/aws/smithy-go/transport/http/eventstream.go b/vendor/github.com/aws/smithy-go/transport/http/eventstream.go new file mode 100644 index 0000000000..251db8ac35 --- /dev/null +++ b/vendor/github.com/aws/smithy-go/transport/http/eventstream.go @@ -0,0 +1,209 @@ +package http + +import ( + "context" + "fmt" + "io" + "sync" + + "github.com/aws/smithy-go" + smithysync "github.com/aws/smithy-go/sync" +) + +// EventStreamWriter writes events to a stream using a ClientProtocol. +// +// The writer manages a background goroutine that facilitates the write loop. +// Calls to Send() on a writer will block until the message has been written. +// +// The writer doesn't know anything about signing. If event stream messages are +// getting signed by the client then the underlying io.Writer has already been +// wrapped to handle that at this point. +type EventStreamWriter struct { + protocol ClientProtocol + schema *smithy.Schema + + eventStream io.WriteCloser + stream chan singleflight + done chan struct{} + err *smithysync.OnceErr + + closeOnce sync.Once +} + +// we send one message at a time, the underlying write loop marshals these into +// the writer and reports back any error to the error channel +type singleflight struct { + variant *smithy.Schema + event smithy.Serializable + errCh chan<- error +} + +// NewEventStreamWriter returns an EventStreamWriter for the given schema. +func NewEventStreamWriter(protocol ClientProtocol, schema *smithy.Schema, stream io.WriteCloser) *EventStreamWriter { + w := &EventStreamWriter{ + protocol: protocol, + schema: schema, + + eventStream: stream, + stream: make(chan singleflight), + done: make(chan struct{}), + err: smithysync.NewOnceErr(), + } + + go w.writeStream() + + return w +} + +func (w *EventStreamWriter) writeStream() { + defer w.Close() + + for { + select { + case ev := <-w.stream: + err := w.protocol.SerializeEventMessage(w.schema, ev.variant, ev.event, w.eventStream) + if err != nil { + w.err.SetError(err) + } + ev.errCh <- err + case <-w.done: + return + } + } +} + +// Send writes a single event to the stream. +func (w *EventStreamWriter) Send(ctx context.Context, variant *smithy.Schema, event smithy.Serializable) error { + if err := w.err.Err(); err != nil { + return err + } + + errCh := make(chan error, 1) + select { + case w.stream <- singleflight{variant, event, errCh}: + case <-ctx.Done(): + return ctx.Err() + case <-w.done: + return fmt.Errorf("stream closed, unable to send event") + } + + select { + case err := <-errCh: + return err + case <-ctx.Done(): + return ctx.Err() + case <-w.done: + return fmt.Errorf("stream closed, unable to send event") + } +} + +// Close signals end-of-stream and closes the underlying writer. Close is +// safe for concurrent calls. +func (w *EventStreamWriter) Close() error { + w.closeOnce.Do(func() { + close(w.done) + w.err.SetError(w.eventStream.Close()) + }) + return w.err.Err() +} + +// Err returns the first error encountered during writing. +func (w *EventStreamWriter) Err() error { + return w.err.Err() +} + +// ErrorSet returns a channel that is closed when an error occurs. +func (w *EventStreamWriter) ErrorSet() <-chan struct{} { + return w.err.ErrorSet() +} + +// EventStreamReader reads events from a stream using a ClientProtocol. +type EventStreamReader struct { + protocol ClientProtocol + schema *smithy.Schema + types *smithy.TypeRegistry + + eventStream io.ReadCloser + stream chan smithy.Deserializable + done chan struct{} + err *smithysync.OnceErr + + closeOnce sync.Once +} + +// NewEventStreamReader returns an EventStreamReader that deserializes events +// through the given protocol from r. The schema is the event stream union +// schema. +func NewEventStreamReader(protocol ClientProtocol, schema *smithy.Schema, types *smithy.TypeRegistry, stream io.ReadCloser) *EventStreamReader { + r := &EventStreamReader{ + protocol: protocol, + schema: schema, + types: types, + + eventStream: stream, + stream: make(chan smithy.Deserializable), + done: make(chan struct{}), + err: smithysync.NewOnceErr(), + } + + go r.readEventStream() + + return r +} + +func (r *EventStreamReader) readEventStream() { + defer r.Close() + defer close(r.stream) + + for { + event, err := r.protocol.DeserializeEventMessage(r.schema, r.types, r.eventStream) + if err != nil { + if err == io.EOF { + return + } + select { + case <-r.done: + return + default: + r.err.SetError(err) + return + } + } + + select { + case r.stream <- event: + case <-r.done: + return + } + } +} + +// Events returns the channel from which deserialized events can be read. +func (r *EventStreamReader) Events() <-chan smithy.Deserializable { + return r.stream +} + +// Close stops the reader and releases the underlying stream. Close is safe +// for concurrent calls. +func (r *EventStreamReader) Close() error { + r.closeOnce.Do(func() { + close(r.done) + r.eventStream.Close() + }) + return r.err.Err() +} + +// Err returns the first error encountered during reading. +func (r *EventStreamReader) Err() error { + return r.err.Err() +} + +// ErrorSet returns a channel that is closed when an error occurs. +func (r *EventStreamReader) ErrorSet() <-chan struct{} { + return r.err.ErrorSet() +} + +// Closed returns a channel that is closed when the reader is closed. +func (r *EventStreamReader) Closed() <-chan struct{} { + return r.done +} diff --git a/vendor/github.com/aws/smithy-go/transport/http/eventstream_middleware.go b/vendor/github.com/aws/smithy-go/transport/http/eventstream_middleware.go new file mode 100644 index 0000000000..f7d60dc76a --- /dev/null +++ b/vendor/github.com/aws/smithy-go/transport/http/eventstream_middleware.go @@ -0,0 +1,69 @@ +package http + +import ( + "context" + "fmt" + "io" + + "github.com/aws/smithy-go/middleware" +) + +type eventStreamWriterKey struct{} + +// GetInputStreamWriter returns the io.WriteCloser pipe used for the +// operation's input event stream. +func GetInputStreamWriter(ctx context.Context) io.WriteCloser { + writeCloser, _ := middleware.GetStackValue(ctx, eventStreamWriterKey{}).(io.WriteCloser) + return writeCloser +} + +func setInputStreamWriter(ctx context.Context, writeCloser io.WriteCloser) context.Context { + return middleware.WithStackValue(ctx, eventStreamWriterKey{}, writeCloser) +} + +// InitializeStreamWriter is a Finalize middleware that creates an in-memory +// pipe and sets it as the HTTP request body so event stream messages can be +// written after the request is sent. +type InitializeStreamWriter struct{} + +// AddInitializeStreamWriter adds the InitializeStreamWriter middleware to the +// provided stack. +func AddInitializeStreamWriter(stack *middleware.Stack) error { + return stack.Finalize.Add(&InitializeStreamWriter{}, middleware.After) +} + +// ID returns the identifier for the middleware. +func (i *InitializeStreamWriter) ID() string { + return "InitializeStreamWriter" +} + +// HandleFinalize is the middleware implementation. +func (i *InitializeStreamWriter) HandleFinalize( + ctx context.Context, in middleware.FinalizeInput, next middleware.FinalizeHandler, +) ( + out middleware.FinalizeOutput, metadata middleware.Metadata, err error, +) { + request, ok := in.Request.(*Request) + if !ok { + return out, metadata, fmt.Errorf("unknown transport type: %T", in.Request) + } + + inputReader, inputWriter := io.Pipe() + defer func() { + if err == nil { + return + } + _ = inputReader.Close() + _ = inputWriter.Close() + }() + + request, err = request.SetStream(inputReader) + if err != nil { + return out, metadata, err + } + in.Request = request + + ctx = setInputStreamWriter(ctx, inputWriter) + + return next.HandleFinalize(ctx, in) +} diff --git a/vendor/github.com/aws/smithy-go/transport/http/host.go b/vendor/github.com/aws/smithy-go/transport/http/host.go index db9801bea5..b504a455da 100644 --- a/vendor/github.com/aws/smithy-go/transport/http/host.go +++ b/vendor/github.com/aws/smithy-go/transport/http/host.go @@ -69,7 +69,7 @@ func ValidPortNumber(port string) bool { return true } -// ValidHostLabel returns whether the label is a valid RFC 3986 host label. +// ValidHostLabel returns whether the label is a valid RFC 952/1123 host label. func ValidHostLabel(label string) bool { if l := len(label); l == 0 || l > 63 { return false diff --git a/vendor/github.com/aws/smithy-go/transport/http/protocol.go b/vendor/github.com/aws/smithy-go/transport/http/protocol.go new file mode 100644 index 0000000000..80fc9e6f99 --- /dev/null +++ b/vendor/github.com/aws/smithy-go/transport/http/protocol.go @@ -0,0 +1,27 @@ +package http + +import ( + "context" + "io" + + "github.com/aws/smithy-go" +) + +// ClientProtocol defines the interface through which client-side operation +// request/responses are (de)serialized across the wire. +// +// While a caller CAN define their own protocol, it is almost never necessary +// to do so. In practice, a generated client will utilize one of the predefined +// protocols implemented as part of the Smithy client runtime. +type ClientProtocol interface { + ID() smithy.ShapeID + SerializeRequest(context.Context, *smithy.OperationSchema, smithy.Serializable, *Request) error + DeserializeResponse(ctx context.Context, schema *smithy.OperationSchema, types *smithy.TypeRegistry, resp *Response, out smithy.Deserializable) error + + // event stream APIs + HasInitialEventMessage() bool + SerializeEventMessage(schema, variant *smithy.Schema, v smithy.Serializable, w io.Writer) error + DeserializeEventMessage(schema *smithy.Schema, types *smithy.TypeRegistry, r io.Reader) (smithy.Deserializable, error) + SerializeInitialRequest(schema *smithy.Schema, v smithy.Serializable, w io.Writer) error + DeserializeInitialResponse(schema *smithy.Schema, r io.Reader, out smithy.Deserializable) error +} diff --git a/vendor/github.com/aws/smithy-go/type_registry.go b/vendor/github.com/aws/smithy-go/type_registry.go new file mode 100644 index 0000000000..3c4e02a185 --- /dev/null +++ b/vendor/github.com/aws/smithy-go/type_registry.go @@ -0,0 +1,70 @@ +package smithy + +import ( + "strings" +) + +// TypeRegistry creates an instance of a type based on its Smithy IDL shape ID. +// +// Generated clients have an exported package-level registry (named +// TypeRegistry) that holds all structure types for the service. +type TypeRegistry struct { + Entries map[string]*TypeRegistryEntry +} + +// RegistryEntry creates a type registry entry. +func RegistryEntry[T any](schema *Schema) *TypeRegistryEntry { + return &TypeRegistryEntry{ + Schema: schema, + New: func() any { + return new(T) + }, + } +} + +// DeserializableError provides an instance of a deserializable error structure +// for a given shape ID. +// +// The ID is given as a string here since this will be called in a context where +// a shape ID is a discriminator read in from some wire payload. +func (t *TypeRegistry) DeserializableError(id string) (DeserializableError, bool) { + return typeRegistryLookup[DeserializableError](t, id) +} + +// LookupEntry returns the registry entry for the given shape ID. +func (t *TypeRegistry) LookupEntry(id string) (*TypeRegistryEntry, bool) { + entry, ok := t.Entries[id] + if !ok { + entry, ok = t.lookupShortName(id) + } + return entry, ok +} + +// TypeRegistryEntry holds the schema and constructor for a registered shape. +type TypeRegistryEntry struct { + Schema *Schema + New func() any +} + +func (t *TypeRegistry) lookupShortName(id string) (*TypeRegistryEntry, bool) { + for key, e := range t.Entries { + if idx := strings.Index(key, "#"); idx != -1 && key[idx+1:] == id { + return e, true + } + } + return nil, false +} + +func typeRegistryLookup[T any](t *TypeRegistry, id string) (T, bool) { + entry, ok := t.Entries[id] + if !ok { + entry, ok = t.lookupShortName(id) + } + if !ok { + var v T + return v, false + } + + v, ok := entry.New().(T) + return v, ok +} diff --git a/vendor/github.com/cenkalti/backoff/v4/.gitignore b/vendor/github.com/cenkalti/backoff/v4/.gitignore deleted file mode 100644 index 50d95c548b..0000000000 --- a/vendor/github.com/cenkalti/backoff/v4/.gitignore +++ /dev/null @@ -1,25 +0,0 @@ -# Compiled Object files, Static and Dynamic libs (Shared Objects) -*.o -*.a -*.so - -# Folders -_obj -_test - -# Architecture specific extensions/prefixes -*.[568vq] -[568vq].out - -*.cgo1.go -*.cgo2.c -_cgo_defun.c -_cgo_gotypes.go -_cgo_export.* - -_testmain.go - -*.exe - -# IDEs -.idea/ diff --git a/vendor/github.com/cenkalti/backoff/v4/LICENSE b/vendor/github.com/cenkalti/backoff/v4/LICENSE deleted file mode 100644 index 89b8179965..0000000000 --- a/vendor/github.com/cenkalti/backoff/v4/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 Cenk Altı - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/cenkalti/backoff/v4/README.md b/vendor/github.com/cenkalti/backoff/v4/README.md deleted file mode 100644 index 9433004a28..0000000000 --- a/vendor/github.com/cenkalti/backoff/v4/README.md +++ /dev/null @@ -1,30 +0,0 @@ -# Exponential Backoff [![GoDoc][godoc image]][godoc] [![Coverage Status][coveralls image]][coveralls] - -This is a Go port of the exponential backoff algorithm from [Google's HTTP Client Library for Java][google-http-java-client]. - -[Exponential backoff][exponential backoff wiki] -is an algorithm that uses feedback to multiplicatively decrease the rate of some process, -in order to gradually find an acceptable rate. -The retries exponentially increase and stop increasing when a certain threshold is met. - -## Usage - -Import path is `github.com/cenkalti/backoff/v4`. Please note the version part at the end. - -Use https://pkg.go.dev/github.com/cenkalti/backoff/v4 to view the documentation. - -## Contributing - -* I would like to keep this library as small as possible. -* Please don't send a PR without opening an issue and discussing it first. -* If proposed change is not a common use case, I will probably not accept it. - -[godoc]: https://pkg.go.dev/github.com/cenkalti/backoff/v4 -[godoc image]: https://godoc.org/github.com/cenkalti/backoff?status.png -[coveralls]: https://coveralls.io/github/cenkalti/backoff?branch=master -[coveralls image]: https://coveralls.io/repos/github/cenkalti/backoff/badge.svg?branch=master - -[google-http-java-client]: https://github.com/google/google-http-java-client/blob/da1aa993e90285ec18579f1553339b00e19b3ab5/google-http-client/src/main/java/com/google/api/client/util/ExponentialBackOff.java -[exponential backoff wiki]: http://en.wikipedia.org/wiki/Exponential_backoff - -[advanced example]: https://pkg.go.dev/github.com/cenkalti/backoff/v4?tab=doc#pkg-examples diff --git a/vendor/github.com/cenkalti/backoff/v4/backoff.go b/vendor/github.com/cenkalti/backoff/v4/backoff.go deleted file mode 100644 index 3676ee405d..0000000000 --- a/vendor/github.com/cenkalti/backoff/v4/backoff.go +++ /dev/null @@ -1,66 +0,0 @@ -// Package backoff implements backoff algorithms for retrying operations. -// -// Use Retry function for retrying operations that may fail. -// If Retry does not meet your needs, -// copy/paste the function into your project and modify as you wish. -// -// There is also Ticker type similar to time.Ticker. -// You can use it if you need to work with channels. -// -// See Examples section below for usage examples. -package backoff - -import "time" - -// BackOff is a backoff policy for retrying an operation. -type BackOff interface { - // NextBackOff returns the duration to wait before retrying the operation, - // or backoff. Stop to indicate that no more retries should be made. - // - // Example usage: - // - // duration := backoff.NextBackOff(); - // if (duration == backoff.Stop) { - // // Do not retry operation. - // } else { - // // Sleep for duration and retry operation. - // } - // - NextBackOff() time.Duration - - // Reset to initial state. - Reset() -} - -// Stop indicates that no more retries should be made for use in NextBackOff(). -const Stop time.Duration = -1 - -// ZeroBackOff is a fixed backoff policy whose backoff time is always zero, -// meaning that the operation is retried immediately without waiting, indefinitely. -type ZeroBackOff struct{} - -func (b *ZeroBackOff) Reset() {} - -func (b *ZeroBackOff) NextBackOff() time.Duration { return 0 } - -// StopBackOff is a fixed backoff policy that always returns backoff.Stop for -// NextBackOff(), meaning that the operation should never be retried. -type StopBackOff struct{} - -func (b *StopBackOff) Reset() {} - -func (b *StopBackOff) NextBackOff() time.Duration { return Stop } - -// ConstantBackOff is a backoff policy that always returns the same backoff delay. -// This is in contrast to an exponential backoff policy, -// which returns a delay that grows longer as you call NextBackOff() over and over again. -type ConstantBackOff struct { - Interval time.Duration -} - -func (b *ConstantBackOff) Reset() {} -func (b *ConstantBackOff) NextBackOff() time.Duration { return b.Interval } - -func NewConstantBackOff(d time.Duration) *ConstantBackOff { - return &ConstantBackOff{Interval: d} -} diff --git a/vendor/github.com/cenkalti/backoff/v4/context.go b/vendor/github.com/cenkalti/backoff/v4/context.go deleted file mode 100644 index 48482330eb..0000000000 --- a/vendor/github.com/cenkalti/backoff/v4/context.go +++ /dev/null @@ -1,62 +0,0 @@ -package backoff - -import ( - "context" - "time" -) - -// BackOffContext is a backoff policy that stops retrying after the context -// is canceled. -type BackOffContext interface { // nolint: golint - BackOff - Context() context.Context -} - -type backOffContext struct { - BackOff - ctx context.Context -} - -// WithContext returns a BackOffContext with context ctx -// -// ctx must not be nil -func WithContext(b BackOff, ctx context.Context) BackOffContext { // nolint: golint - if ctx == nil { - panic("nil context") - } - - if b, ok := b.(*backOffContext); ok { - return &backOffContext{ - BackOff: b.BackOff, - ctx: ctx, - } - } - - return &backOffContext{ - BackOff: b, - ctx: ctx, - } -} - -func getContext(b BackOff) context.Context { - if cb, ok := b.(BackOffContext); ok { - return cb.Context() - } - if tb, ok := b.(*backOffTries); ok { - return getContext(tb.delegate) - } - return context.Background() -} - -func (b *backOffContext) Context() context.Context { - return b.ctx -} - -func (b *backOffContext) NextBackOff() time.Duration { - select { - case <-b.ctx.Done(): - return Stop - default: - return b.BackOff.NextBackOff() - } -} diff --git a/vendor/github.com/cenkalti/backoff/v4/exponential.go b/vendor/github.com/cenkalti/backoff/v4/exponential.go deleted file mode 100644 index aac99f196a..0000000000 --- a/vendor/github.com/cenkalti/backoff/v4/exponential.go +++ /dev/null @@ -1,216 +0,0 @@ -package backoff - -import ( - "math/rand" - "time" -) - -/* -ExponentialBackOff is a backoff implementation that increases the backoff -period for each retry attempt using a randomization function that grows exponentially. - -NextBackOff() is calculated using the following formula: - - randomized interval = - RetryInterval * (random value in range [1 - RandomizationFactor, 1 + RandomizationFactor]) - -In other words NextBackOff() will range between the randomization factor -percentage below and above the retry interval. - -For example, given the following parameters: - - RetryInterval = 2 - RandomizationFactor = 0.5 - Multiplier = 2 - -the actual backoff period used in the next retry attempt will range between 1 and 3 seconds, -multiplied by the exponential, that is, between 2 and 6 seconds. - -Note: MaxInterval caps the RetryInterval and not the randomized interval. - -If the time elapsed since an ExponentialBackOff instance is created goes past the -MaxElapsedTime, then the method NextBackOff() starts returning backoff.Stop. - -The elapsed time can be reset by calling Reset(). - -Example: Given the following default arguments, for 10 tries the sequence will be, -and assuming we go over the MaxElapsedTime on the 10th try: - - Request # RetryInterval (seconds) Randomized Interval (seconds) - - 1 0.5 [0.25, 0.75] - 2 0.75 [0.375, 1.125] - 3 1.125 [0.562, 1.687] - 4 1.687 [0.8435, 2.53] - 5 2.53 [1.265, 3.795] - 6 3.795 [1.897, 5.692] - 7 5.692 [2.846, 8.538] - 8 8.538 [4.269, 12.807] - 9 12.807 [6.403, 19.210] - 10 19.210 backoff.Stop - -Note: Implementation is not thread-safe. -*/ -type ExponentialBackOff struct { - InitialInterval time.Duration - RandomizationFactor float64 - Multiplier float64 - MaxInterval time.Duration - // After MaxElapsedTime the ExponentialBackOff returns Stop. - // It never stops if MaxElapsedTime == 0. - MaxElapsedTime time.Duration - Stop time.Duration - Clock Clock - - currentInterval time.Duration - startTime time.Time -} - -// Clock is an interface that returns current time for BackOff. -type Clock interface { - Now() time.Time -} - -// ExponentialBackOffOpts is a function type used to configure ExponentialBackOff options. -type ExponentialBackOffOpts func(*ExponentialBackOff) - -// Default values for ExponentialBackOff. -const ( - DefaultInitialInterval = 500 * time.Millisecond - DefaultRandomizationFactor = 0.5 - DefaultMultiplier = 1.5 - DefaultMaxInterval = 60 * time.Second - DefaultMaxElapsedTime = 15 * time.Minute -) - -// NewExponentialBackOff creates an instance of ExponentialBackOff using default values. -func NewExponentialBackOff(opts ...ExponentialBackOffOpts) *ExponentialBackOff { - b := &ExponentialBackOff{ - InitialInterval: DefaultInitialInterval, - RandomizationFactor: DefaultRandomizationFactor, - Multiplier: DefaultMultiplier, - MaxInterval: DefaultMaxInterval, - MaxElapsedTime: DefaultMaxElapsedTime, - Stop: Stop, - Clock: SystemClock, - } - for _, fn := range opts { - fn(b) - } - b.Reset() - return b -} - -// WithInitialInterval sets the initial interval between retries. -func WithInitialInterval(duration time.Duration) ExponentialBackOffOpts { - return func(ebo *ExponentialBackOff) { - ebo.InitialInterval = duration - } -} - -// WithRandomizationFactor sets the randomization factor to add jitter to intervals. -func WithRandomizationFactor(randomizationFactor float64) ExponentialBackOffOpts { - return func(ebo *ExponentialBackOff) { - ebo.RandomizationFactor = randomizationFactor - } -} - -// WithMultiplier sets the multiplier for increasing the interval after each retry. -func WithMultiplier(multiplier float64) ExponentialBackOffOpts { - return func(ebo *ExponentialBackOff) { - ebo.Multiplier = multiplier - } -} - -// WithMaxInterval sets the maximum interval between retries. -func WithMaxInterval(duration time.Duration) ExponentialBackOffOpts { - return func(ebo *ExponentialBackOff) { - ebo.MaxInterval = duration - } -} - -// WithMaxElapsedTime sets the maximum total time for retries. -func WithMaxElapsedTime(duration time.Duration) ExponentialBackOffOpts { - return func(ebo *ExponentialBackOff) { - ebo.MaxElapsedTime = duration - } -} - -// WithRetryStopDuration sets the duration after which retries should stop. -func WithRetryStopDuration(duration time.Duration) ExponentialBackOffOpts { - return func(ebo *ExponentialBackOff) { - ebo.Stop = duration - } -} - -// WithClockProvider sets the clock used to measure time. -func WithClockProvider(clock Clock) ExponentialBackOffOpts { - return func(ebo *ExponentialBackOff) { - ebo.Clock = clock - } -} - -type systemClock struct{} - -func (t systemClock) Now() time.Time { - return time.Now() -} - -// SystemClock implements Clock interface that uses time.Now(). -var SystemClock = systemClock{} - -// Reset the interval back to the initial retry interval and restarts the timer. -// Reset must be called before using b. -func (b *ExponentialBackOff) Reset() { - b.currentInterval = b.InitialInterval - b.startTime = b.Clock.Now() -} - -// NextBackOff calculates the next backoff interval using the formula: -// Randomized interval = RetryInterval * (1 ± RandomizationFactor) -func (b *ExponentialBackOff) NextBackOff() time.Duration { - // Make sure we have not gone over the maximum elapsed time. - elapsed := b.GetElapsedTime() - next := getRandomValueFromInterval(b.RandomizationFactor, rand.Float64(), b.currentInterval) - b.incrementCurrentInterval() - if b.MaxElapsedTime != 0 && elapsed+next > b.MaxElapsedTime { - return b.Stop - } - return next -} - -// GetElapsedTime returns the elapsed time since an ExponentialBackOff instance -// is created and is reset when Reset() is called. -// -// The elapsed time is computed using time.Now().UnixNano(). It is -// safe to call even while the backoff policy is used by a running -// ticker. -func (b *ExponentialBackOff) GetElapsedTime() time.Duration { - return b.Clock.Now().Sub(b.startTime) -} - -// Increments the current interval by multiplying it with the multiplier. -func (b *ExponentialBackOff) incrementCurrentInterval() { - // Check for overflow, if overflow is detected set the current interval to the max interval. - if float64(b.currentInterval) >= float64(b.MaxInterval)/b.Multiplier { - b.currentInterval = b.MaxInterval - } else { - b.currentInterval = time.Duration(float64(b.currentInterval) * b.Multiplier) - } -} - -// Returns a random value from the following interval: -// [currentInterval - randomizationFactor * currentInterval, currentInterval + randomizationFactor * currentInterval]. -func getRandomValueFromInterval(randomizationFactor, random float64, currentInterval time.Duration) time.Duration { - if randomizationFactor == 0 { - return currentInterval // make sure no randomness is used when randomizationFactor is 0. - } - var delta = randomizationFactor * float64(currentInterval) - var minInterval = float64(currentInterval) - delta - var maxInterval = float64(currentInterval) + delta - - // Get a random value from the range [minInterval, maxInterval]. - // The formula used below has a +1 because if the minInterval is 1 and the maxInterval is 3 then - // we want a 33% chance for selecting either 1, 2 or 3. - return time.Duration(minInterval + (random * (maxInterval - minInterval + 1))) -} diff --git a/vendor/github.com/cenkalti/backoff/v4/retry.go b/vendor/github.com/cenkalti/backoff/v4/retry.go deleted file mode 100644 index b9c0c51cd7..0000000000 --- a/vendor/github.com/cenkalti/backoff/v4/retry.go +++ /dev/null @@ -1,146 +0,0 @@ -package backoff - -import ( - "errors" - "time" -) - -// An OperationWithData is executing by RetryWithData() or RetryNotifyWithData(). -// The operation will be retried using a backoff policy if it returns an error. -type OperationWithData[T any] func() (T, error) - -// An Operation is executing by Retry() or RetryNotify(). -// The operation will be retried using a backoff policy if it returns an error. -type Operation func() error - -func (o Operation) withEmptyData() OperationWithData[struct{}] { - return func() (struct{}, error) { - return struct{}{}, o() - } -} - -// Notify is a notify-on-error function. It receives an operation error and -// backoff delay if the operation failed (with an error). -// -// NOTE that if the backoff policy stated to stop retrying, -// the notify function isn't called. -type Notify func(error, time.Duration) - -// Retry the operation o until it does not return error or BackOff stops. -// o is guaranteed to be run at least once. -// -// If o returns a *PermanentError, the operation is not retried, and the -// wrapped error is returned. -// -// Retry sleeps the goroutine for the duration returned by BackOff after a -// failed operation returns. -func Retry(o Operation, b BackOff) error { - return RetryNotify(o, b, nil) -} - -// RetryWithData is like Retry but returns data in the response too. -func RetryWithData[T any](o OperationWithData[T], b BackOff) (T, error) { - return RetryNotifyWithData(o, b, nil) -} - -// RetryNotify calls notify function with the error and wait duration -// for each failed attempt before sleep. -func RetryNotify(operation Operation, b BackOff, notify Notify) error { - return RetryNotifyWithTimer(operation, b, notify, nil) -} - -// RetryNotifyWithData is like RetryNotify but returns data in the response too. -func RetryNotifyWithData[T any](operation OperationWithData[T], b BackOff, notify Notify) (T, error) { - return doRetryNotify(operation, b, notify, nil) -} - -// RetryNotifyWithTimer calls notify function with the error and wait duration using the given Timer -// for each failed attempt before sleep. -// A default timer that uses system timer is used when nil is passed. -func RetryNotifyWithTimer(operation Operation, b BackOff, notify Notify, t Timer) error { - _, err := doRetryNotify(operation.withEmptyData(), b, notify, t) - return err -} - -// RetryNotifyWithTimerAndData is like RetryNotifyWithTimer but returns data in the response too. -func RetryNotifyWithTimerAndData[T any](operation OperationWithData[T], b BackOff, notify Notify, t Timer) (T, error) { - return doRetryNotify(operation, b, notify, t) -} - -func doRetryNotify[T any](operation OperationWithData[T], b BackOff, notify Notify, t Timer) (T, error) { - var ( - err error - next time.Duration - res T - ) - if t == nil { - t = &defaultTimer{} - } - - defer func() { - t.Stop() - }() - - ctx := getContext(b) - - b.Reset() - for { - res, err = operation() - if err == nil { - return res, nil - } - - var permanent *PermanentError - if errors.As(err, &permanent) { - return res, permanent.Err - } - - if next = b.NextBackOff(); next == Stop { - if cerr := ctx.Err(); cerr != nil { - return res, cerr - } - - return res, err - } - - if notify != nil { - notify(err, next) - } - - t.Start(next) - - select { - case <-ctx.Done(): - return res, ctx.Err() - case <-t.C(): - } - } -} - -// PermanentError signals that the operation should not be retried. -type PermanentError struct { - Err error -} - -func (e *PermanentError) Error() string { - return e.Err.Error() -} - -func (e *PermanentError) Unwrap() error { - return e.Err -} - -func (e *PermanentError) Is(target error) bool { - _, ok := target.(*PermanentError) - return ok -} - -// Permanent wraps the given err in a *PermanentError. -func Permanent(err error) error { - if err == nil { - return nil - } - return &PermanentError{ - Err: err, - } -} diff --git a/vendor/github.com/cenkalti/backoff/v4/ticker.go b/vendor/github.com/cenkalti/backoff/v4/ticker.go deleted file mode 100644 index df9d68bce5..0000000000 --- a/vendor/github.com/cenkalti/backoff/v4/ticker.go +++ /dev/null @@ -1,97 +0,0 @@ -package backoff - -import ( - "context" - "sync" - "time" -) - -// Ticker holds a channel that delivers `ticks' of a clock at times reported by a BackOff. -// -// Ticks will continue to arrive when the previous operation is still running, -// so operations that take a while to fail could run in quick succession. -type Ticker struct { - C <-chan time.Time - c chan time.Time - b BackOff - ctx context.Context - timer Timer - stop chan struct{} - stopOnce sync.Once -} - -// NewTicker returns a new Ticker containing a channel that will send -// the time at times specified by the BackOff argument. Ticker is -// guaranteed to tick at least once. The channel is closed when Stop -// method is called or BackOff stops. It is not safe to manipulate the -// provided backoff policy (notably calling NextBackOff or Reset) -// while the ticker is running. -func NewTicker(b BackOff) *Ticker { - return NewTickerWithTimer(b, &defaultTimer{}) -} - -// NewTickerWithTimer returns a new Ticker with a custom timer. -// A default timer that uses system timer is used when nil is passed. -func NewTickerWithTimer(b BackOff, timer Timer) *Ticker { - if timer == nil { - timer = &defaultTimer{} - } - c := make(chan time.Time) - t := &Ticker{ - C: c, - c: c, - b: b, - ctx: getContext(b), - timer: timer, - stop: make(chan struct{}), - } - t.b.Reset() - go t.run() - return t -} - -// Stop turns off a ticker. After Stop, no more ticks will be sent. -func (t *Ticker) Stop() { - t.stopOnce.Do(func() { close(t.stop) }) -} - -func (t *Ticker) run() { - c := t.c - defer close(c) - - // Ticker is guaranteed to tick at least once. - afterC := t.send(time.Now()) - - for { - if afterC == nil { - return - } - - select { - case tick := <-afterC: - afterC = t.send(tick) - case <-t.stop: - t.c = nil // Prevent future ticks from being sent to the channel. - return - case <-t.ctx.Done(): - return - } - } -} - -func (t *Ticker) send(tick time.Time) <-chan time.Time { - select { - case t.c <- tick: - case <-t.stop: - return nil - } - - next := t.b.NextBackOff() - if next == Stop { - t.Stop() - return nil - } - - t.timer.Start(next) - return t.timer.C() -} diff --git a/vendor/github.com/cenkalti/backoff/v4/timer.go b/vendor/github.com/cenkalti/backoff/v4/timer.go deleted file mode 100644 index 8120d0213c..0000000000 --- a/vendor/github.com/cenkalti/backoff/v4/timer.go +++ /dev/null @@ -1,35 +0,0 @@ -package backoff - -import "time" - -type Timer interface { - Start(duration time.Duration) - Stop() - C() <-chan time.Time -} - -// defaultTimer implements Timer interface using time.Timer -type defaultTimer struct { - timer *time.Timer -} - -// C returns the timers channel which receives the current time when the timer fires. -func (t *defaultTimer) C() <-chan time.Time { - return t.timer.C -} - -// Start starts the timer to fire after the given duration -func (t *defaultTimer) Start(duration time.Duration) { - if t.timer == nil { - t.timer = time.NewTimer(duration) - } else { - t.timer.Reset(duration) - } -} - -// Stop is called when the timer is not used anymore and resources may be freed. -func (t *defaultTimer) Stop() { - if t.timer != nil { - t.timer.Stop() - } -} diff --git a/vendor/github.com/cenkalti/backoff/v4/tries.go b/vendor/github.com/cenkalti/backoff/v4/tries.go deleted file mode 100644 index 28d58ca37c..0000000000 --- a/vendor/github.com/cenkalti/backoff/v4/tries.go +++ /dev/null @@ -1,38 +0,0 @@ -package backoff - -import "time" - -/* -WithMaxRetries creates a wrapper around another BackOff, which will -return Stop if NextBackOff() has been called too many times since -the last time Reset() was called - -Note: Implementation is not thread-safe. -*/ -func WithMaxRetries(b BackOff, max uint64) BackOff { - return &backOffTries{delegate: b, maxTries: max} -} - -type backOffTries struct { - delegate BackOff - maxTries uint64 - numTries uint64 -} - -func (b *backOffTries) NextBackOff() time.Duration { - if b.maxTries == 0 { - return Stop - } - if b.maxTries > 0 { - if b.maxTries <= b.numTries { - return Stop - } - b.numTries++ - } - return b.delegate.NextBackOff() -} - -func (b *backOffTries) Reset() { - b.numTries = 0 - b.delegate.Reset() -} diff --git a/vendor/github.com/coder/quartz/.gitignore b/vendor/github.com/coder/quartz/.gitignore deleted file mode 100644 index 62c893550a..0000000000 --- a/vendor/github.com/coder/quartz/.gitignore +++ /dev/null @@ -1 +0,0 @@ -.idea/ \ No newline at end of file diff --git a/vendor/github.com/coder/quartz/LICENSE b/vendor/github.com/coder/quartz/LICENSE deleted file mode 100644 index aee8c1d627..0000000000 --- a/vendor/github.com/coder/quartz/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -MIT No Attribution - -Copyright (c) Coder Technologies, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/vendor/github.com/coder/quartz/README.md b/vendor/github.com/coder/quartz/README.md deleted file mode 100644 index b0da401ec4..0000000000 --- a/vendor/github.com/coder/quartz/README.md +++ /dev/null @@ -1,632 +0,0 @@ -# Quartz - -A Go time testing library for writing deterministic unit tests - -Our high level goal is to write unit tests that - -1. execute quickly -2. don't flake -3. are straightforward to write and understand - -For tests to execute quickly without flakes, we want to focus on _determinism_: the test should run -the same each time, and it should be easy to force the system into a known state (no races) before -executing test assertions. `time.Sleep`, `runtime.Gosched()`, and -polling/[Eventually](https://pkg.go.dev/github.com/stretchr/testify/assert#Eventually) are all -symptoms of an inability to do this easily. - -## Usage - -### `Clock` interface - -In your application code, maintain a reference to a `quartz.Clock` instance to start timers and -tickers, instead of the bare `time` standard library. - -```go -import "github.com/coder/quartz" - -type Component struct { - ... - - // for testing - clock quartz.Clock -} -``` - -Whenever you would call into `time` to start a timer or ticker, call `Component`'s `clock` instead. - -In production, set this clock to `quartz.NewReal()` to create a clock that just transparently passes -through to the standard `time` library. - -### Mocking - -In your tests, you can use a `*Mock` to control the tickers and timers your code under test gets. - -```go -import ( - "testing" - "github.com/coder/quartz" -) - -func TestComponent(t *testing.T) { - mClock := quartz.NewMock(t) - comp := &Component{ - ... - clock: mClock, - } -} -``` - -The `*Mock` clock starts at Jan 1, 2024, 00:00 UTC by default, but you can set any start time you'd like prior to your test. - -```go -mClock := quartz.NewMock(t) -mClock.Set(time.Date(2021, 6, 18, 12, 0, 0, 0, time.UTC)) // June 18, 2021 @ 12pm UTC -``` - -#### Advancing the clock - -Once you begin setting timers or tickers, you cannot change the time backward, only advance it -forward. You may continue to use `Set()`, but it is often easier and clearer to use `Advance()`. - -For example, with a timer: - -```go -fired := false - -tmr := mClock.AfterFunc(time.Second, func() { - fired = true -}) -mClock.Advance(time.Second) -``` - -When you call `Advance()` it immediately moves the clock forward the given amount, and triggers any -tickers or timers that are scheduled to happen at that time. Any triggered events happen on separate -goroutines, so _do not_ immediately assert the results: - -```go -fired := false - -tmr := mClock.AfterFunc(time.Second, func() { - fired = true -}) -mClock.Advance(time.Second) - -// RACE CONDITION, DO NOT DO THIS! -if !fired { - t.Fatal("didn't fire") -} -``` - -`Advance()` (and `Set()` for that matter) return an `AdvanceWaiter` object you can use to wait for -all triggered events to complete. - -```go -fired := false -// set a test timeout so we don't wait the default `go test` timeout for a failure -ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - -tmr := mClock.AfterFunc(time.Second, func() { - fired = true -}) - -w := mClock.Advance(time.Second) -err := w.Wait(ctx) -if err != nil { - t.Fatal("AfterFunc f never completed") -} -if !fired { - t.Fatal("didn't fire") -} -``` - -The construction of waiting for the triggered events and failing the test if they don't complete is -very common, so there is a shorthand: - -```go -w := mClock.Advance(time.Second) -err := w.Wait(ctx) -if err != nil { - t.Fatal("AfterFunc f never completed") -} -``` - -is equivalent to: - -```go -w := mClock.Advance(time.Second) -w.MustWait(ctx) -``` - -or even more briefly: - -```go -mClock.Advance(time.Second).MustWait(ctx) -``` - -### Advance only to the next event - -One important restriction on advancing the clock is that you may only advance forward to the next -timer or ticker event and no further. The following will result in a test failure: - -```go -func TestAdvanceTooFar(t *testing.T) { - ctx, cancel := context.WithTimeout(10*time.Second) - defer cancel() - mClock := quartz.NewMock(t) - var firedAt time.Time - mClock.AfterFunc(time.Second, func() { - firedAt := mClock.Now() - }) - mClock.Advance(2*time.Second).MustWait(ctx) -} -``` - -This is a deliberate design decision to allow `Advance()` to immediately and synchronously move the -clock forward (even without calling `Wait()` on returned waiter). This helps meet Quartz's design -goals of writing deterministic and easy to understand unit tests. It also allows the clock to be -advanced, deterministically _during_ the execution of a tick or timer function, as explained in the -next sections on Traps. - -Advancing multiple events can be accomplished via looping. E.g. if you have a 1-second ticker - -```go -for i := 0; i < 10; i++ { - mClock.Advance(time.Second).MustWait(ctx) -} -``` - -will advance 10 ticks. - -If you don't know or don't want to compute the time to the next event, you can use `AdvanceNext()`. - -```go -d, w := mClock.AdvanceNext() -w.MustWait(ctx) -// d contains the duration we advanced -``` - -`d, ok := Peek()` returns the duration until the next event, if any (`ok` is `true`). You can use -this to advance a specific time, regardless of the tickers and timer events: - -```go -desired := time.Minute // time to advance -for desired > 0 { - p, ok := mClock.Peek() - if !ok || p > desired { - mClock.Advance(desired).MustWait(ctx) - break - } - mClock.Advance(p).MustWait(ctx) - desired -= p -} -``` - -### Traps - -A trap allows you to match specific calls into the library while mocking, block their return, -inspect their arguments, then release them to allow them to return. They help you write -deterministic unit tests even when the code under test executes asynchronously from the test. - -You set your traps prior to executing code under test, and then wait for them to be triggered. - -```go -func TestTrap(t *testing.T) { - ctx, cancel := context.WithTimeout(10*time.Second) - defer cancel() - mClock := quartz.NewMock(t) - trap := mClock.Trap().AfterFunc() - defer trap.Close() // stop trapping AfterFunc calls - - count := 0 - go mClock.AfterFunc(time.Hour, func(){ - count++ - }) - call := trap.MustWait(ctx) - call.MustRelease(ctx) - if call.Duration != time.Hour { - t.Fatal("wrong duration") - } - - // Now that the async call to AfterFunc has occurred, we can advance the clock to trigger it - mClock.Advance(call.Duration).MustWait(ctx) - if count != 1 { - t.Fatal("wrong count") - } -} -``` - -In this test, the trap serves 2 purposes. Firstly, it allows us to capture and assert the duration -passed to the `AfterFunc` call. Secondly, it prevents a race between setting the timer and advancing -it. Since these things happen on different goroutines, if `Advance()` completes before -`AfterFunc()` is called, then the timer never pops in this test. - -Any untrapped calls immediately complete using the current time, and calling `Close()` on a trap -causes the mock clock to stop trapping those calls. - -You may also `Advance()` the clock between trapping a call and releasing it. The call uses the -current (mocked) time at the moment it is released. - -```go -func TestTrap2(t *testing.T) { - ctx, cancel := context.WithTimeout(10*time.Second) - defer cancel() - mClock := quartz.NewMock(t) - trap := mClock.Trap().Now() - defer trap.Close() // stop trapping AfterFunc calls - - var logs []string - done := make(chan struct{}) - go func(clk quartz.Clock){ - defer close(done) - start := clk.Now() - phase1() - p1end := clk.Now() - logs = append(fmt.Sprintf("Phase 1 took %s", p1end.Sub(start).String())) - phase2() - p2end := clk.Now() - logs = append(fmt.Sprintf("Phase 2 took %s", p2end.Sub(p1end).String())) - }(mClock) - - // start - trap.MustWait(ctx).MustRelease(ctx) - // phase 1 - call := trap.MustWait(ctx) - mClock.Advance(3*time.Second).MustWait(ctx) - call.MustRelease(ctx) - // phase 2 - call = trap.MustWait(ctx) - mClock.Advance(5*time.Second).MustWait(ctx) - call.MustRelease(ctx) - - <-done - // Now logs contains []string{"Phase 1 took 3s", "Phase 2 took 5s"} -} -``` - -### Tags - -When multiple goroutines in the code under test call into the Clock, you can use `tags` to -distinguish them in your traps. - -```go -trap := mClock.Trap.Now("foo") // traps any calls that contain "foo" -defer trap.Close() - -foo := make(chan time.Time) -go func(){ - foo <- mClock.Now("foo", "bar") -}() -baz := make(chan time.Time) -go func(){ - baz <- mClock.Now("baz") -}() -call := trap.MustWait(ctx) -mClock.Advance(time.Second).MustWait(ctx) -call.MustRelease(ctx) -// call.Tags contains []string{"foo", "bar"} - -gotFoo := <-foo // 1s after start -gotBaz := <-baz // ?? never trapped, so races with Advance() -``` - -Tags appear as an optional suffix on all `Clock` methods (type `...string`) and are ignored entirely -by the real clock. They also appear on all methods on returned timers and tickers. - -## Recommended Patterns - -### Options - -We use the Option pattern to inject the mock clock for testing, keeping the call signature in -production clean. The option pattern is compatible with other optional fields as well. - -```go -type Option func(*Thing) - -// WithTestClock is used in tests to inject a mock Clock -func WithTestClock(clk quartz.Clock) Option { - return func(t *Thing) { - t.clock = clk - } -} - -func NewThing(, opts ...Option) *Thing { - t := &Thing{ - ... - clock: quartz.NewReal() - } - for _, o := range opts { - o(t) - } - return t -} -``` - -In tests, this becomes - -```go -func TestThing(t *testing.T) { - mClock := quartz.NewMock(t) - thing := NewThing(, WithTestClock(mClock)) - ... -} -``` - -### Tagging convention - -Tag your `Clock` method calls as: - -```go -func (c *Component) Method() { - now := c.clock.Now("Component", "Method") -} -``` - -or - -```go -func (c *Component) Method() { - start := c.clock.Now("Component", "Method", "start") - ... - end := c.clock.Now("Component", "Method", "end") -} -``` - -This makes it much less likely that code changes that introduce new components or methods will spoil -existing unit tests. - -## Why another time testing library? - -Writing good unit tests for components and functions that use the `time` package is difficult, even -though several open source libraries exist. In building Quartz, we took some inspiration from - -- [github.com/benbjohnson/clock](https://github.com/benbjohnson/clock) -- Tailscale's [tstest.Clock](https://github.com/coder/tailscale/blob/main/tstest/clock.go) -- [github.com/aspenmesh/tock](https://github.com/aspenmesh/tock) - -Quartz shares the high level design of a `Clock` interface that closely resembles the functions in -the `time` standard library, and a "real" clock passes thru to the standard library in production, -while a mock clock gives precise control in testing. - -As mentioned in our introduction, our high level goal is to write unit tests that - -1. execute quickly -2. don't flake -3. are straightforward to write and understand - -For several reasons, this is a tall order when it comes to code that depends on time, and we found -the existing libraries insufficient for our goals. - -### Preventing test flakes - -The following example comes from the README from benbjohnson/clock: - -```go -mock := clock.NewMock() -count := 0 - -// Kick off a timer to increment every 1 mock second. -go func() { - ticker := mock.Ticker(1 * time.Second) - for { - <-ticker.C - count++ - } -}() -runtime.Gosched() - -// Move the clock forward 10 seconds. -mock.Add(10 * time.Second) - -// This prints 10. -fmt.Println(count) -``` - -The first race condition is fairly obvious: moving the clock forward 10 seconds may generate 10 -ticks on the `ticker.C` channel, but there is no guarantee that `count++` executes before -`fmt.Println(count)`. - -The second race condition is more subtle, but `runtime.Gosched()` is the tell. Since the ticker -is started on a separate goroutine, there is no guarantee that `mock.Ticker()` executes before -`mock.Add()`. `runtime.Gosched()` is an attempt to get this to happen, but it makes no hard -promises. On a busy system, especially when running tests in parallel, this can flake, advance the -time 10 seconds first, then start the ticker and never generate a tick. - -Let's talk about how Quartz tackles these problems. - -In our experience, an extremely common use case is creating a ticker then doing a 2-arm `select` -with ticks in one and context expiring in another, i.e. - -```go -t := time.NewTicker(duration) -for { - select { - case <-ctx.Done(): - return ctx.Err() - case <-t.C: - err := do() - if err != nil { - return err - } - } -} -``` - -In Quartz, we refactor this to be more compact and testing friendly: - -```go -t := clock.TickerFunc(ctx, duration, do) -return t.Wait() -``` - -This affords the mock `Clock` the ability to explicitly know when processing of a tick is finished -because it's wrapped in the function passed to `TickerFunc` (`do()` in this example). - -In Quartz, when you advance the clock, you are returned an object you can `Wait()` on to ensure all -ticks and timers triggered are finished. This solves the first race condition in the example. - -(As an aside, we still support a traditional standard library-style `Ticker`. You may find it useful -if you want to keep your code as close as possible to the standard library, or if you need to use -the channel in a larger `select` block. In that case, you'll have to find some other mechanism to -sync tick processing to your test code.) - -To prevent race conditions related to the starting of the ticker, Quartz allows you to set "traps" -for calls that access the clock. - -```go -func TestTicker(t *testing.T) { - mClock := quartz.NewMock(t) - trap := mClock.Trap().TickerFunc() - defer trap.Close() // stop trapping at end - go runMyTicker(mClock) // async calls TickerFunc() - call := trap.MustWait(context.Background()) // waits for a call and blocks its return - call.MustRelease(ctx) // allow the TickerFunc() call to return - // optionally check the duration using call.Duration - // Move the clock forward 1 tick - mClock.Advance(time.Second).MustWait(context.Background()) - // assert results of the tick -} -``` - -Trapping and then releasing the call to `TickerFunc()` ensures the ticker is started at a -deterministic time, so our calls to `Advance()` will have a predictable effect. - -Take a look at `TestExampleTickerFunc` in `example_test.go` for a complete worked example. - -### Complex time dependence - -Another difficult issue to handle when unit testing is when some code under test makes multiple -calls that depend on the time, and you want to simulate some time passing between them. - -A very basic example is measuring how long something took: - -```go -var measurement time.Duration -go func(clock quartz.Clock) { - start := clock.Now() - doSomething() - measurement = clock.Since(start) -}(mClock) - -// how to get measurement to be, say, 5 seconds? -``` - -The two calls into the clock happen asynchronously, so we need to be able to advance the clock after -the first call to `Now()` but before the call to `Since()`. Doing this with the libraries we -mentioned above means that you have to be able to mock out or otherwise block the completion of -`doSomething()`. - -But, with the trap functionality we mentioned in the previous section, you can deterministically -control the time each call sees. - -```go -trap := mClock.Trap().Since() -var measurement time.Duration -go func(clock quartz.Clock) { - start := clock.Now() - doSomething() - measurement = clock.Since(start) -}(mClock) - -c := trap.MustWait(ctx) -mClock.Advance(5*time.Second) -c.MustRelease(ctx) -``` - -We wait until we trap the `clock.Since()` call, which implies that `clock.Now()` has completed, then -advance the mock clock 5 seconds. Finally, we release the `clock.Since()` call. Any changes to the -clock that happen _before_ we release the call will be included in the time used for the -`clock.Since()` call. - -As a more involved example, consider an inactivity timeout: we want something to happen if there is -no activity recorded for some period, say 10 minutes in the following example: - -```go -type InactivityTimer struct { - mu sync.Mutex - activity time.Time - clock quartz.Clock -} - -func (i *InactivityTimer) Start() { - i.mu.Lock() - defer i.mu.Unlock() - next := i.clock.Until(i.activity.Add(10*time.Minute)) - t := i.clock.AfterFunc(next, func() { - i.mu.Lock() - defer i.mu.Unlock() - next := i.clock.Until(i.activity.Add(10*time.Minute)) - if next == 0 { - i.timeoutLocked() - return - } - t.Reset(next) - }) -} -``` - -The actual contents of `timeoutLocked()` doesn't matter for this example, and assume there are other -functions that record the latest `activity`. - -We found that some time testing libraries hold a lock on the mock clock while calling the function -passed to `AfterFunc`, resulting in a deadlock if you made clock calls from within. - -Others allow this sort of thing, but don't have the flexibility to test edge cases. There is a -subtle bug in our `Start()` function. The timer may pop a little late, and/or some measurable real -time may elapse before `Until()` gets called inside the `AfterFunc`. If there hasn't been activity, -`next` might be negative. - -To test this in Quartz, we'll use a trap. We only want to trap the inner `Until()` call, not the -initial one, so to make testing easier we can "tag" the call we want. Like this: - -```go -func (i *InactivityTimer) Start() { - i.mu.Lock() - defer i.mu.Unlock() - next := i.clock.Until(i.activity.Add(10*time.Minute)) - t := i.clock.AfterFunc(next, func() { - i.mu.Lock() - defer i.mu.Unlock() - next := i.clock.Until(i.activity.Add(10*time.Minute), "inner") - if next == 0 { - i.timeoutLocked() - return - } - t.Reset(next) - }) -} -``` - -All Quartz `Clock` functions, and functions on returned timers and tickers support zero or more -string tags that allow traps to match on them. - -```go -func TestInactivityTimer_Late(t *testing.T) { - // set a timeout on the test itself, so that if Wait functions get blocked, we don't have to - // wait for the default test timeout of 10 minutes. - ctx, cancel := context.WithTimeout(10*time.Second) - defer cancel() - mClock := quartz.NewMock(t) - trap := mClock.Trap.Until("inner") - defer trap.Close() - - it := &InactivityTimer{ - activity: mClock.Now(), - clock: mClock, - } - it.Start() - - // Trigger the AfterFunc - w := mClock.Advance(10*time.Minute) - c := trap.MustWait(ctx) - // Advance the clock a few ms to simulate a busy system - mClock.Advance(3*time.Millisecond) - c.MustRelease(ctx) // Until() returns - w.MustWait(ctx) // Wait for the AfterFunc to wrap up - - // Assert that the timeoutLocked() function was called -} -``` - -This test case will fail with our bugged implementation, since the triggered AfterFunc won't call -`timeoutLocked()` and instead will reset the timer with a negative number. The fix is easy, use -`next <= 0` as the comparison. diff --git a/vendor/github.com/coder/quartz/clock.go b/vendor/github.com/coder/quartz/clock.go deleted file mode 100644 index 729edfa562..0000000000 --- a/vendor/github.com/coder/quartz/clock.go +++ /dev/null @@ -1,43 +0,0 @@ -// Package quartz is a library for testing time related code. It exports an interface Clock that -// mimics the standard library time package functions. In production, an implementation that calls -// thru to the standard library is used. In testing, a Mock clock is used to precisely control and -// intercept time functions. -package quartz - -import ( - "context" - "time" -) - -type Clock interface { - // NewTicker returns a new Ticker containing a channel that will send the current time on the - // channel after each tick. The period of the ticks is specified by the duration argument. The - // ticker will adjust the time interval or drop ticks to make up for slow receivers. The - // duration d must be greater than zero; if not, NewTicker will panic. Stop the ticker to - // release associated resources. - NewTicker(d time.Duration, tags ...string) *Ticker - // TickerFunc is a convenience function that calls f on the interval d until either the given - // context expires or f returns an error. Callers may call Wait() on the returned Waiter to - // wait until this happens and obtain the error. The duration d must be greater than zero; if - // not, TickerFunc will panic. - TickerFunc(ctx context.Context, d time.Duration, f func() error, tags ...string) Waiter - // NewTimer creates a new Timer that will send the current time on its channel after at least - // duration d. - NewTimer(d time.Duration, tags ...string) *Timer - // AfterFunc waits for the duration to elapse and then calls f in its own goroutine. It returns - // a Timer that can be used to cancel the call using its Stop method. The returned Timer's C - // field is not used and will be nil. - AfterFunc(d time.Duration, f func(), tags ...string) *Timer - - // Now returns the current local time. - Now(tags ...string) time.Time - // Since returns the time elapsed since t. It is shorthand for Clock.Now().Sub(t). - Since(t time.Time, tags ...string) time.Duration - // Until returns the duration until t. It is shorthand for t.Sub(Clock.Now()). - Until(t time.Time, tags ...string) time.Duration -} - -// Waiter can be waited on for an error. -type Waiter interface { - Wait(tags ...string) error -} diff --git a/vendor/github.com/coder/quartz/mock.go b/vendor/github.com/coder/quartz/mock.go deleted file mode 100644 index 7255aff99b..0000000000 --- a/vendor/github.com/coder/quartz/mock.go +++ /dev/null @@ -1,851 +0,0 @@ -package quartz - -import ( - "context" - "errors" - "fmt" - "slices" - "sync" - "time" -) - -// TestingT is the minimal interface required from a testing framework for the Mock. -type TestingT interface { - Helper() - - Log(...any) - Logf(string, ...any) - Error(...any) - Errorf(string, ...any) - Fatal(...any) - Fatalf(string, ...any) - - Cleanup(func()) -} - -// Mock is the testing implementation of Clock. It tracks a time that monotonically increases -// during a test, triggering any timers or tickers automatically. -type Mock struct { - tb TestingT - logger Logger - mu sync.Mutex - testOver bool - - // cur is the current time - cur time.Time - - all []event - nextTime time.Time - nextEvents []event - traps []*Trap -} - -type event interface { - next() time.Time - fire(t time.Time) -} - -func (m *Mock) TickerFunc(ctx context.Context, d time.Duration, f func() error, tags ...string) Waiter { - if d <= 0 { - panic("TickerFunc called with negative or zero duration") - } - m.mu.Lock() - defer m.mu.Unlock() - c := newCall(clockFunctionTickerFunc, tags, withDuration(d)) - m.matchCallLocked(c) - defer close(c.complete) - t := &mockTickerFunc{ - ctx: ctx, - d: d, - f: f, - nxt: m.cur.Add(d), - mock: m, - cond: sync.NewCond(&m.mu), - } - m.all = append(m.all, t) - m.recomputeNextLocked() - go t.waitForCtx() - return t -} - -// NewTicker creates a mocked ticker attached to this Mock. Note that it will cease sending ticks on its channel at the -// end of the test, to avoid leaking any goroutines. Ticks are suppressed even if the mock clock is advanced after the -// test completes. Best practice is to only manipulate the mock time in the main goroutine of the test. -func (m *Mock) NewTicker(d time.Duration, tags ...string) *Ticker { - if d <= 0 { - panic("NewTicker called with negative or zero duration") - } - m.mu.Lock() - defer m.mu.Unlock() - c := newCall(clockFunctionNewTicker, tags, withDuration(d)) - m.matchCallLocked(c) - defer close(c.complete) - return newMockTickerLocked(m, d) -} - -func (m *Mock) NewTimer(d time.Duration, tags ...string) *Timer { - m.mu.Lock() - defer m.mu.Unlock() - c := newCall(clockFunctionNewTimer, tags, withDuration(d)) - defer close(c.complete) - m.matchCallLocked(c) - ch := make(chan time.Time) - t := &Timer{ - C: ch, - c: ch, - nxt: m.cur.Add(d), - mock: m, - } - if d <= 0 { - // zero or negative duration timer means we should immediately fire - // it, rather than add it. - go t.fire(t.mock.cur) - return t - } - m.addEventLocked(t) - return t -} - -func (m *Mock) AfterFunc(d time.Duration, f func(), tags ...string) *Timer { - m.mu.Lock() - defer m.mu.Unlock() - c := newCall(clockFunctionAfterFunc, tags, withDuration(d)) - defer close(c.complete) - m.matchCallLocked(c) - t := &Timer{ - nxt: m.cur.Add(d), - fn: f, - mock: m, - } - if d <= 0 { - // zero or negative duration timer means we should immediately fire - // it, rather than add it. - go t.fire(t.mock.cur) - return t - } - m.addEventLocked(t) - return t -} - -func (m *Mock) Now(tags ...string) time.Time { - m.mu.Lock() - defer m.mu.Unlock() - c := newCall(clockFunctionNow, tags) - defer close(c.complete) - m.matchCallLocked(c) - return m.cur -} - -func (m *Mock) Since(t time.Time, tags ...string) time.Duration { - m.mu.Lock() - defer m.mu.Unlock() - c := newCall(clockFunctionSince, tags, withTime(t)) - defer close(c.complete) - m.matchCallLocked(c) - return m.cur.Sub(t) -} - -func (m *Mock) Until(t time.Time, tags ...string) time.Duration { - m.mu.Lock() - defer m.mu.Unlock() - c := newCall(clockFunctionUntil, tags, withTime(t)) - defer close(c.complete) - m.matchCallLocked(c) - return t.Sub(m.cur) -} - -func (m *Mock) addEventLocked(e event) { - m.all = append(m.all, e) - m.recomputeNextLocked() -} - -func (m *Mock) recomputeNextLocked() { - var best time.Time - var events []event - for _, e := range m.all { - if best.IsZero() || e.next().Before(best) { - best = e.next() - events = []event{e} - continue - } - if e.next().Equal(best) { - events = append(events, e) - continue - } - } - m.nextTime = best - m.nextEvents = events -} - -func (m *Mock) removeTimer(t *Timer) { - m.mu.Lock() - defer m.mu.Unlock() - m.removeTimerLocked(t) -} - -func (m *Mock) removeTimerLocked(t *Timer) { - t.stopped = true - m.removeEventLocked(t) -} - -func (m *Mock) removeEventLocked(e event) { - defer m.recomputeNextLocked() - for i := range m.all { - if m.all[i] == e { - m.all = append(m.all[:i], m.all[i+1:]...) - return - } - } -} - -func (m *Mock) matchCallLocked(c *apiCall) { - var traps []*Trap - for _, t := range m.traps { - if t.matches(c) { - traps = append(traps, t) - } - } - if !m.testOver { - m.logger.Logf("Mock Clock - %s call, matched %d traps", c, len(traps)) - } - if len(traps) == 0 { - return - } - c.releases.Add(len(traps)) - m.mu.Unlock() - for _, t := range traps { - go t.catch(c) - } - c.releases.Wait() - m.mu.Lock() -} - -// AdvanceWaiter is returned from Advance and Set calls and allows you to wait for ticks and timers -// to complete. In the case of functions passed to AfterFunc or TickerFunc, it waits for the -// functions to return. For other ticks & timers, it just waits for the tick to be delivered to -// the channel. -// -// If multiple timers or tickers trigger simultaneously, they are all run on separate -// go routines. -type AdvanceWaiter struct { - tb TestingT - ch chan struct{} -} - -// Wait for all timers and ticks to complete, or until context expires. -func (w AdvanceWaiter) Wait(ctx context.Context) error { - select { - case <-w.ch: - return nil - case <-ctx.Done(): - return ctx.Err() - } -} - -// MustWait waits for all timers and ticks to complete, and fails the test immediately if the -// context completes first. MustWait must be called from the goroutine running the test or -// benchmark, similar to `t.FailNow()`. -func (w AdvanceWaiter) MustWait(ctx context.Context) { - w.tb.Helper() - select { - case <-w.ch: - return - case <-ctx.Done(): - w.tb.Fatalf("context expired while waiting for clock to advance: %s", ctx.Err()) - } -} - -// Done returns a channel that is closed when all timers and ticks complete. -func (w AdvanceWaiter) Done() <-chan struct{} { - return w.ch -} - -// Advance moves the clock forward by d, triggering any timers or tickers. The returned value can -// be used to wait for all timers and ticks to complete. Advance sets the clock forward before -// returning, and can only advance up to the next timer or tick event. It will fail the test if you -// attempt to advance beyond. -// -// If you need to advance exactly to the next event, and don't know or don't wish to calculate it, -// consider AdvanceNext(). -func (m *Mock) Advance(d time.Duration) AdvanceWaiter { - m.tb.Helper() - w := AdvanceWaiter{tb: m.tb, ch: make(chan struct{})} - m.mu.Lock() - if !m.testOver { - m.logger.Logf("Mock Clock - Advance(%s)", d) - } - fin := m.cur.Add(d) - // nextTime.IsZero implies no events scheduled. - if m.nextTime.IsZero() || fin.Before(m.nextTime) { - m.cur = fin - m.mu.Unlock() - close(w.ch) - return w - } - if fin.After(m.nextTime) { - m.tb.Errorf("cannot advance %s which is beyond next timer/ticker event in %s", - d.String(), m.nextTime.Sub(m.cur)) - m.mu.Unlock() - close(w.ch) - return w - } - - m.cur = m.nextTime - go m.advanceLocked(w) - return w -} - -func (m *Mock) advanceLocked(w AdvanceWaiter) { - defer close(w.ch) - wg := sync.WaitGroup{} - for i := range m.nextEvents { - e := m.nextEvents[i] - t := m.cur - wg.Add(1) - go func() { - e.fire(t) - wg.Done() - }() - } - // release the lock and let the events resolve. This allows them to call back into the - // Mock to query the time or set new timers. Each event should remove or reschedule - // itself from nextEvents. - m.mu.Unlock() - wg.Wait() -} - -// Set the time to t. If the time is after the current mocked time, then this is equivalent to -// Advance() with the difference. You may only Set the time earlier than the current time before -// starting tickers and timers (e.g. at the start of your test case). -func (m *Mock) Set(t time.Time) AdvanceWaiter { - m.tb.Helper() - w := AdvanceWaiter{tb: m.tb, ch: make(chan struct{})} - m.mu.Lock() - if !m.testOver { - m.logger.Logf("Mock Clock - Set(%s)", t) - } - if t.Before(m.cur) { - defer close(w.ch) - defer m.mu.Unlock() - // past - if !m.nextTime.IsZero() { - m.tb.Error("Set mock clock to the past after timers/tickers started") - } - m.cur = t - return w - } - // future - // nextTime.IsZero implies no events scheduled. - if m.nextTime.IsZero() || t.Before(m.nextTime) { - defer close(w.ch) - defer m.mu.Unlock() - m.cur = t - return w - } - if t.After(m.nextTime) { - defer close(w.ch) - defer m.mu.Unlock() - m.tb.Errorf("cannot Set time to %s which is beyond next timer/ticker event at %s", - t.String(), m.nextTime) - return w - } - - m.cur = m.nextTime - go m.advanceLocked(w) - return w -} - -// AdvanceNext advances the clock to the next timer or tick event. It fails the test if there are -// none scheduled. It returns the duration the clock was advanced and a waiter that can be used to -// wait for the timer/tick event(s) to finish. -func (m *Mock) AdvanceNext() (time.Duration, AdvanceWaiter) { - m.mu.Lock() - if !m.testOver { - m.logger.Logf("Mock Clock - AdvanceNext()") - } - m.tb.Helper() - w := AdvanceWaiter{tb: m.tb, ch: make(chan struct{})} - if m.nextTime.IsZero() { - defer close(w.ch) - defer m.mu.Unlock() - m.tb.Error("cannot AdvanceNext because there are no timers or tickers running") - return 0, w - } - d := m.nextTime.Sub(m.cur) - m.cur = m.nextTime - go m.advanceLocked(w) - return d, w -} - -// Peek returns the duration until the next ticker or timer event and the value -// true, or, if there are no running tickers or timers, it returns zero and -// false. -func (m *Mock) Peek() (d time.Duration, ok bool) { - m.mu.Lock() - defer m.mu.Unlock() - if m.nextTime.IsZero() { - return 0, false - } - return m.nextTime.Sub(m.cur), true -} - -// Trapper allows the creation of Traps -type Trapper struct { - // mock is the underlying Mock. This is a thin wrapper around Mock so that - // we can have our interface look like mClock.Trap().NewTimer("foo") - mock *Mock -} - -func (t Trapper) NewTimer(tags ...string) *Trap { - return t.mock.newTrap(clockFunctionNewTimer, tags) -} - -func (t Trapper) AfterFunc(tags ...string) *Trap { - return t.mock.newTrap(clockFunctionAfterFunc, tags) -} - -func (t Trapper) TimerStop(tags ...string) *Trap { - return t.mock.newTrap(clockFunctionTimerStop, tags) -} - -func (t Trapper) TimerReset(tags ...string) *Trap { - return t.mock.newTrap(clockFunctionTimerReset, tags) -} - -func (t Trapper) TickerFunc(tags ...string) *Trap { - return t.mock.newTrap(clockFunctionTickerFunc, tags) -} - -func (t Trapper) TickerFuncWait(tags ...string) *Trap { - return t.mock.newTrap(clockFunctionTickerFuncWait, tags) -} - -func (t Trapper) NewTicker(tags ...string) *Trap { - return t.mock.newTrap(clockFunctionNewTicker, tags) -} - -func (t Trapper) TickerStop(tags ...string) *Trap { - return t.mock.newTrap(clockFunctionTickerStop, tags) -} - -func (t Trapper) TickerReset(tags ...string) *Trap { - return t.mock.newTrap(clockFunctionTickerReset, tags) -} - -func (t Trapper) Now(tags ...string) *Trap { - return t.mock.newTrap(clockFunctionNow, tags) -} - -func (t Trapper) Since(tags ...string) *Trap { - return t.mock.newTrap(clockFunctionSince, tags) -} - -func (t Trapper) Until(tags ...string) *Trap { - return t.mock.newTrap(clockFunctionUntil, tags) -} - -func (m *Mock) Trap() Trapper { - return Trapper{m} -} - -func (m *Mock) newTrap(fn clockFunction, tags []string) *Trap { - m.mu.Lock() - defer m.mu.Unlock() - if !m.testOver { - m.logger.Logf("Mock Clock - Trap %s(..., %v)", fn, tags) - } - tr := &Trap{ - fn: fn, - tags: tags, - mock: m, - calls: make(chan *apiCall), - done: make(chan struct{}), - } - m.traps = append(m.traps, tr) - return tr -} - -// WithLogger replaces the default testing logger with a custom one. -// -// This can be used to discard log messages with: -// -// quartz.NewMock(t).WithLogger(quartz.NoOpLogger) -func (m *Mock) WithLogger(l Logger) *Mock { - m.mu.Lock() - defer m.mu.Unlock() - m.logger = l - return m -} - -// NewMock creates a new Mock with the time set to midnight UTC on Jan 1, 2024. -// You may re-set the time earlier than this, but only before timers or tickers -// are created. -func NewMock(tb TestingT) *Mock { - cur, err := time.Parse(time.RFC3339, "2024-01-01T00:00:00Z") - if err != nil { - panic(err) - } - m := &Mock{ - tb: tb, - logger: tb, - cur: cur, - } - tb.Cleanup(func() { - m.mu.Lock() - defer m.mu.Unlock() - m.testOver = true - m.logger.Logf("Mock Clock - test cleanup; will no longer log clock events") - }) - return m -} - -var _ Clock = &Mock{} - -type mockTickerFunc struct { - ctx context.Context - d time.Duration - f func() error - nxt time.Time - mock *Mock - - // cond is a condition Locked on the main Mock.mu - cond *sync.Cond - // inProgress is true when we are actively calling f - inProgress bool - // done is true when the ticker exits - done bool - // err holds the error when the ticker exits - err error -} - -func (m *mockTickerFunc) next() time.Time { - return m.nxt -} - -func (m *mockTickerFunc) fire(_ time.Time) { - m.mock.mu.Lock() - if m.done { - m.mock.mu.Unlock() - return - } - m.nxt = m.nxt.Add(m.d) - m.mock.recomputeNextLocked() - // we need this check to happen after we've computed the next tick, - // otherwise it will be immediately rescheduled. - if m.inProgress { - m.mock.mu.Unlock() - return - } - - m.inProgress = true - m.mock.mu.Unlock() - err := m.f() - m.mock.mu.Lock() - defer m.mock.mu.Unlock() - m.inProgress = false - m.cond.Broadcast() // wake up anything waiting for f to finish - if err != nil { - m.exitLocked(err) - } -} - -func (m *mockTickerFunc) exitLocked(err error) { - if m.done { - return - } - m.done = true - m.err = err - m.mock.removeEventLocked(m) - m.cond.Broadcast() -} - -func (m *mockTickerFunc) waitForCtx() { - <-m.ctx.Done() - m.mock.mu.Lock() - defer m.mock.mu.Unlock() - for m.inProgress { - m.cond.Wait() - } - m.exitLocked(m.ctx.Err()) -} - -func (m *mockTickerFunc) Wait(tags ...string) error { - m.mock.mu.Lock() - defer m.mock.mu.Unlock() - c := newCall(clockFunctionTickerFuncWait, tags) - m.mock.matchCallLocked(c) - defer close(c.complete) - for !m.done { - m.cond.Wait() - } - return m.err -} - -var _ Waiter = &mockTickerFunc{} - -type clockFunction int - -const ( - clockFunctionNewTimer clockFunction = iota - clockFunctionAfterFunc - clockFunctionTimerStop - clockFunctionTimerReset - clockFunctionTickerFunc - clockFunctionTickerFuncWait - clockFunctionNewTicker - clockFunctionTickerReset - clockFunctionTickerStop - clockFunctionNow - clockFunctionSince - clockFunctionUntil -) - -func (c clockFunction) String() string { - switch c { - case clockFunctionNewTimer: - return "NewTimer" - case clockFunctionAfterFunc: - return "AfterFunc" - case clockFunctionTimerStop: - return "Timer.Stop" - case clockFunctionTimerReset: - return "Timer.Reset" - case clockFunctionTickerFunc: - return "TickerFunc" - case clockFunctionTickerFuncWait: - return "TickerFunc.Wait" - case clockFunctionNewTicker: - return "NewTicker" - case clockFunctionTickerReset: - return "Ticker.Reset" - case clockFunctionTickerStop: - return "Ticker.Stop" - case clockFunctionNow: - return "Now" - case clockFunctionSince: - return "Since" - case clockFunctionUntil: - return "Until" - default: - return fmt.Sprintf("Unknown clockFunction(%d)", c) - } -} - -type callArg func(c *apiCall) - -// apiCall represents a single call to one of the Clock APIs. -type apiCall struct { - Time time.Time - Duration time.Duration - Tags []string - - fn clockFunction - releases sync.WaitGroup - complete chan struct{} -} - -func (a *apiCall) String() string { - switch a.fn { - case clockFunctionNewTimer: - return fmt.Sprintf("NewTimer(%s, %v)", a.Duration, a.Tags) - case clockFunctionAfterFunc: - return fmt.Sprintf("AfterFunc(%s, , %v)", a.Duration, a.Tags) - case clockFunctionTimerStop: - return fmt.Sprintf("Timer.Stop(%v)", a.Tags) - case clockFunctionTimerReset: - return fmt.Sprintf("Timer.Reset(%s, %v)", a.Duration, a.Tags) - case clockFunctionTickerFunc: - return fmt.Sprintf("TickerFunc(, %s, , %s)", a.Duration, a.Tags) - case clockFunctionTickerFuncWait: - return fmt.Sprintf("TickerFunc.Wait(%v)", a.Tags) - case clockFunctionNewTicker: - return fmt.Sprintf("NewTicker(%s, %v)", a.Duration, a.Tags) - case clockFunctionTickerReset: - return fmt.Sprintf("Ticker.Reset(%s, %v)", a.Duration, a.Tags) - case clockFunctionTickerStop: - return fmt.Sprintf("Ticker.Stop(%v)", a.Tags) - case clockFunctionNow: - return fmt.Sprintf("Now(%v)", a.Tags) - case clockFunctionSince: - return fmt.Sprintf("Since(%s, %v)", a.Time, a.Tags) - case clockFunctionUntil: - return fmt.Sprintf("Until(%s, %v)", a.Time, a.Tags) - default: - return fmt.Sprintf("Unknown clockFunction(%d)", a.fn) - } -} - -// Call represents an apiCall that has been trapped. -type Call struct { - Time time.Time - Duration time.Duration - Tags []string - - tb TestingT - apiCall *apiCall - trap *Trap -} - -// Release the call and wait for it to complete. If the provided context expires before the call completes, it returns -// an error. -// -// IMPORTANT: If a call is trapped by more than one trap, they all must release the call before it can complete, and -// they must do so from different goroutines. -func (c *Call) Release(ctx context.Context) error { - c.apiCall.releases.Done() - select { - case <-ctx.Done(): - return fmt.Errorf("timed out waiting for release; did more than one trap capture the call?: %w", ctx.Err()) - case <-c.apiCall.complete: - // OK - } - c.trap.callReleased() - return nil -} - -// MustRelease releases the call and waits for it to complete. If the provided context expires before the call -// completes, it fails the test. -// -// IMPORTANT: If a call is trapped by more than one trap, they all must release the call before it can complete, and -// they must do so from different goroutines. -func (c *Call) MustRelease(ctx context.Context) { - if err := c.Release(ctx); err != nil { - c.tb.Helper() - c.tb.Fatal(err.Error()) - } -} - -func withTime(t time.Time) callArg { - return func(c *apiCall) { - c.Time = t - } -} - -func withDuration(d time.Duration) callArg { - return func(c *apiCall) { - c.Duration = d - } -} - -func newCall(fn clockFunction, tags []string, args ...callArg) *apiCall { - c := &apiCall{ - fn: fn, - Tags: tags, - complete: make(chan struct{}), - } - for _, a := range args { - a(c) - } - return c -} - -type Trap struct { - fn clockFunction - tags []string - mock *Mock - calls chan *apiCall - done chan struct{} - - // mu protects the unreleasedCalls count - mu sync.Mutex - unreleasedCalls int -} - -func (t *Trap) String() string { - return fmt.Sprintf("Trap %s(..., %v)", t.fn.String(), t.tags) -} - -func (t *Trap) catch(c *apiCall) { - select { - case t.calls <- c: - case <-t.done: - c.releases.Done() - } -} - -func (t *Trap) matches(c *apiCall) bool { - if t.fn != c.fn { - return false - } - for _, tag := range t.tags { - if !slices.Contains(c.Tags, tag) { - return false - } - } - return true -} - -func (t *Trap) Close() { - t.mock.mu.Lock() - defer t.mock.mu.Unlock() - select { - case <-t.done: - t.mock.tb.Logf("%s already Closed()", t) - return // already closed - default: - } - if t.unreleasedCalls != 0 { - t.mock.tb.Helper() - t.mock.tb.Errorf("%s Closed() with %d unreleased calls", t, t.unreleasedCalls) - } - for i, tr := range t.mock.traps { - if t == tr { - t.mock.traps = append(t.mock.traps[:i], t.mock.traps[i+1:]...) - } - } - close(t.done) -} - -func (t *Trap) callReleased() { - t.mu.Lock() - defer t.mu.Unlock() - t.unreleasedCalls-- -} - -var ErrTrapClosed = errors.New("trap closed") - -func (t *Trap) Wait(ctx context.Context) (*Call, error) { - select { - case <-ctx.Done(): - return nil, ctx.Err() - case <-t.done: - return nil, ErrTrapClosed - case a := <-t.calls: - c := &Call{ - Time: a.Time, - Duration: a.Duration, - Tags: a.Tags, - apiCall: a, - trap: t, - tb: t.mock.tb, - } - t.mu.Lock() - defer t.mu.Unlock() - t.unreleasedCalls++ - return c, nil - } -} - -// MustWait calls Wait() and then if there is an error, immediately fails the -// test via tb.Fatalf() -func (t *Trap) MustWait(ctx context.Context) *Call { - t.mock.tb.Helper() - c, err := t.Wait(ctx) - if err != nil { - t.mock.tb.Fatalf("context expired while waiting for %s: %s", t, err.Error()) - } - return c -} - -type Logger interface { - Log(args ...any) - Logf(format string, args ...any) -} - -// NoOpLogger is a Logger that discards all log messages. -var NoOpLogger Logger = noOpLogger{} - -type noOpLogger struct{} - -func (noOpLogger) Log(args ...any) {} -func (noOpLogger) Logf(format string, args ...any) {} diff --git a/vendor/github.com/coder/quartz/real.go b/vendor/github.com/coder/quartz/real.go deleted file mode 100644 index f39fb1163e..0000000000 --- a/vendor/github.com/coder/quartz/real.go +++ /dev/null @@ -1,80 +0,0 @@ -package quartz - -import ( - "context" - "time" -) - -type realClock struct{} - -func NewReal() Clock { - return realClock{} -} - -func (realClock) NewTicker(d time.Duration, _ ...string) *Ticker { - tkr := time.NewTicker(d) - return &Ticker{ticker: tkr, C: tkr.C} -} - -func (realClock) TickerFunc(ctx context.Context, d time.Duration, f func() error, _ ...string) Waiter { - ct := &realContextTicker{ - ctx: ctx, - tkr: time.NewTicker(d), - f: f, - err: make(chan error, 1), - } - go ct.run() - return ct -} - -type realContextTicker struct { - ctx context.Context - tkr *time.Ticker - f func() error - err chan error -} - -func (t *realContextTicker) Wait(_ ...string) error { - return <-t.err -} - -func (t *realContextTicker) run() { - defer t.tkr.Stop() - for { - select { - case <-t.ctx.Done(): - t.err <- t.ctx.Err() - return - case <-t.tkr.C: - err := t.f() - if err != nil { - t.err <- err - return - } - } - } -} - -func (realClock) NewTimer(d time.Duration, _ ...string) *Timer { - rt := time.NewTimer(d) - return &Timer{C: rt.C, timer: rt} -} - -func (realClock) AfterFunc(d time.Duration, f func(), _ ...string) *Timer { - rt := time.AfterFunc(d, f) - return &Timer{C: rt.C, timer: rt} -} - -func (realClock) Now(_ ...string) time.Time { - return time.Now() -} - -func (realClock) Since(t time.Time, _ ...string) time.Duration { - return time.Since(t) -} - -func (realClock) Until(t time.Time, _ ...string) time.Duration { - return time.Until(t) -} - -var _ Clock = realClock{} diff --git a/vendor/github.com/coder/quartz/ticker.go b/vendor/github.com/coder/quartz/ticker.go deleted file mode 100644 index f4a4b06650..0000000000 --- a/vendor/github.com/coder/quartz/ticker.go +++ /dev/null @@ -1,151 +0,0 @@ -package quartz - -import "time" - -// A Ticker holds a channel that delivers “ticks” of a clock at intervals. -type Ticker struct { - C <-chan time.Time - //nolint: revive - c chan time.Time - ticker *time.Ticker // realtime impl, if set - d time.Duration // period, if set - nxt time.Time // next tick time - mock *Mock // mock clock, if set - stopped bool // true if the ticker is not running - internalTicks chan time.Time // used to deliver ticks to the runLoop goroutine - - // As of Go 1.23, ticker channels are unbuffered and guaranteed to block forever after a call to stop. - // - // When a mocked ticker fires, we don't want to block on a channel write, because it's fine for the code under test - // not to be reading. That means we need to start a new goroutine to do the channel write (runLoop) if we are a - // channel-based ticker. - // - // They also are not supposed to leak even if they are never read or stopped (Go runtime can garbage collect them). - // We can't garbage-collect because we can't check if any other code besides the mock references, but we can ensure - // that we don't leak goroutines so that the garbage collector can do its job when the mock is no longer - // referenced. The channels below allow us to interrupt the runLoop goroutine. - interrupt chan struct{} -} - -func (t *Ticker) fire(tt time.Time) { - t.mock.mu.Lock() - defer t.mock.mu.Unlock() - if t.stopped { - return - } - for !t.nxt.After(t.mock.cur) { - t.nxt = t.nxt.Add(t.d) - } - t.mock.recomputeNextLocked() - if t.interrupt != nil { // implies runLoop is still going. - t.internalTicks <- tt - } -} - -func (t *Ticker) next() time.Time { - return t.nxt -} - -// Stop turns off a ticker. After Stop, no more ticks will be sent. Stop does -// not close the channel, to prevent a concurrent goroutine reading from the -// channel from seeing an erroneous "tick". -func (t *Ticker) Stop(tags ...string) { - if t.ticker != nil { - t.ticker.Stop() - return - } - t.mock.mu.Lock() - defer t.mock.mu.Unlock() - c := newCall(clockFunctionTickerStop, tags) - t.mock.matchCallLocked(c) - defer close(c.complete) - t.mock.removeEventLocked(t) - t.stopped = true - // check if we've already fired, and if so, interrupt it. - if t.interrupt != nil { - <-t.interrupt - t.interrupt = nil - } -} - -// Reset stops a ticker and resets its period to the specified duration. The -// next tick will arrive after the new period elapses. The duration d must be -// greater than zero; if not, Reset will panic. -func (t *Ticker) Reset(d time.Duration, tags ...string) { - if t.ticker != nil { - t.ticker.Reset(d) - return - } - t.mock.mu.Lock() - defer t.mock.mu.Unlock() - c := newCall(clockFunctionTickerReset, tags, withDuration(d)) - t.mock.matchCallLocked(c) - defer close(c.complete) - t.nxt = t.mock.cur.Add(d) - t.d = d - if t.stopped { - t.stopped = false - t.mock.addEventLocked(t) - } else { - t.mock.recomputeNextLocked() - } - if t.interrupt == nil { - t.startRunLoopLocked() - } -} - -func (t *Ticker) runLoop(interrupt chan struct{}) { - defer close(interrupt) -outer: - for { - select { - case tt := <-t.internalTicks: - for { - select { - case t.c <- tt: - continue outer - case <-t.internalTicks: - // Discard future ticks until we can send this one. - case interrupt <- struct{}{}: - return - } - } - case interrupt <- struct{}{}: - return - } - } -} - -func (t *Ticker) startRunLoopLocked() { - // assert some assumptions. If these fire, it is a bug in Quartz itself. - if t.interrupt != nil { - t.mock.tb.Error("called startRunLoopLocked when interrupt suggests we are already running") - } - interrupt := make(chan struct{}) - t.interrupt = interrupt - go t.runLoop(interrupt) -} - -func newMockTickerLocked(m *Mock, d time.Duration) *Ticker { - // no buffer follows Go 1.23+ behavior - ticks := make(chan time.Time) - t := &Ticker{ - C: ticks, - c: ticks, - d: d, - nxt: m.cur.Add(d), - mock: m, - internalTicks: make(chan time.Time), - } - m.addEventLocked(t) - m.tb.Cleanup(func() { - m.mu.Lock() - defer m.mu.Unlock() - if t.interrupt != nil { - <-t.interrupt - t.interrupt = nil - } - }) - t.startRunLoopLocked() - return t -} diff --git a/vendor/github.com/coder/quartz/timer.go b/vendor/github.com/coder/quartz/timer.go deleted file mode 100644 index 8f571eb0dc..0000000000 --- a/vendor/github.com/coder/quartz/timer.go +++ /dev/null @@ -1,118 +0,0 @@ -package quartz - -import ( - "time" -) - -// The Timer type represents a single event. When the Timer expires, the current time will be sent -// on C, unless the Timer was created by AfterFunc. A Timer must be created with NewTimer or -// AfterFunc. -type Timer struct { - C <-chan time.Time - //nolint: revive - c chan time.Time - timer *time.Timer // realtime impl, if set - nxt time.Time // next tick time - mock *Mock // mock clock, if set - fn func() // AfterFunc function, if set - stopped bool // True if stopped, false if running - - // As of Go 1.23, timer channels are unbuffered and guaranteed to block forever after a call to stop. - // - // When a mocked timer fires, we don't want to block on a channel write, because it's fine for the code under test - // not to be reading. That means we need to start a new goroutine to do the channel write if we are a channel-based - // timer. - // - // They also are not supposed to leak even if they are never read or stopped (Go runtime can garbage collect them). - // We can't garbage-collect because we can't check if any other code besides the mock references, but we can ensure - // that we don't leak goroutines so that the garbage collector can do its job when the mock is no longer - // referenced. The channels below allow us to interrupt the channel write goroutine. - interrupt chan struct{} -} - -func (t *Timer) fire(tt time.Time) { - t.mock.mu.Lock() - t.mock.removeTimerLocked(t) - if t.fn != nil { - t.mock.mu.Unlock() - t.fn() - return - } else { - interrupt := make(chan struct{}) - // Prevents the goroutine from leaking beyond the test. Side effect is that timer channels cannot be read - // after the test exits. - t.mock.tb.Cleanup(func() { - <-interrupt - }) - t.interrupt = interrupt - t.mock.mu.Unlock() - go func() { - defer close(interrupt) - select { - case t.c <- tt: - case interrupt <- struct{}{}: - } - }() - } -} - -func (t *Timer) next() time.Time { - return t.nxt -} - -// Stop prevents the Timer from firing. It returns true if the call stops the timer, false if the -// timer has already expired or been stopped. Stop does not close the channel, to prevent a read -// from the channel succeeding incorrectly. -// -// See https://pkg.go.dev/time#Timer.Stop for more information. -func (t *Timer) Stop(tags ...string) bool { - if t.timer != nil { - return t.timer.Stop() - } - t.mock.mu.Lock() - defer t.mock.mu.Unlock() - c := newCall(clockFunctionTimerStop, tags) - t.mock.matchCallLocked(c) - defer close(c.complete) - result := !t.stopped - t.mock.removeTimerLocked(t) - // check if we've already fired, and if so, interrupt it. - if t.interrupt != nil { - <-t.interrupt - t.interrupt = nil - } - return result -} - -// Reset changes the timer to expire after duration d. It returns true if the timer had been active, -// false if the timer had expired or been stopped. -// -// See https://pkg.go.dev/time#Timer.Reset for more information. -func (t *Timer) Reset(d time.Duration, tags ...string) bool { - if t.timer != nil { - return t.timer.Reset(d) - } - t.mock.mu.Lock() - defer t.mock.mu.Unlock() - c := newCall(clockFunctionTimerReset, tags, withDuration(d)) - t.mock.matchCallLocked(c) - defer close(c.complete) - result := !t.stopped - // check if we've already fired, and if so, interrupt it. - if t.interrupt != nil { - <-t.interrupt - t.interrupt = nil - } - if d <= 0 { - // zero or negative duration timer means we should immediately re-fire - // it, rather than remove and re-add it. - t.stopped = false - go t.fire(t.mock.cur) - return result - } - t.mock.removeTimerLocked(t) - t.stopped = false - t.nxt = t.mock.cur.Add(d) - t.mock.addEventLocked(t) - return result -} diff --git a/vendor/github.com/fsnotify/fsnotify/CHANGELOG.md b/vendor/github.com/fsnotify/fsnotify/CHANGELOG.md index 4ed4864f47..3027f3c67a 100644 --- a/vendor/github.com/fsnotify/fsnotify/CHANGELOG.md +++ b/vendor/github.com/fsnotify/fsnotify/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +1.10.1 2026-05-04 +----------------- + +### Changes and fixes + +- inotify: don't remove sibling watches sharing a path prefix ([#754]) + +- inotify, windows: don't rename sibling watches sharing a path prefix + ([#755]) + + +[#754]: https://github.com/fsnotify/fsnotify/pull/754 +[#755]: https://github.com/fsnotify/fsnotify/pull/755 + + 1.10.0 2026-04-30 ----------------- This version of fsnotify needs Go 1.23. diff --git a/vendor/github.com/fsnotify/fsnotify/README.md b/vendor/github.com/fsnotify/fsnotify/README.md index d8441aa061..2e56ef4c9a 100644 --- a/vendor/github.com/fsnotify/fsnotify/README.md +++ b/vendor/github.com/fsnotify/fsnotify/README.md @@ -171,6 +171,38 @@ distro's documentation): fs.inotify.max_user_watches=200000 fs.inotify.max_user_instances=256 +### Windows +Recursive watching is not currently enabled through fsnotify's public API +(see the FAQ "Are subdirectories watched?" above). The notes below +describe Windows backend behavior observed when recursive watching is +enabled internally (for example, in fsnotify's own tests). They are kept +here as a reference for maintainers and contributors who encounter the +behavior, since the recursive code path still exists in the backend. + +When recursive watching is enabled and you watch a directory, you may +receive a `Write` event for an intermediate directory whenever a child +entry inside it is created, renamed, or removed. For example, with a +recursive watch on `/a` and a new file `/a/b/c`, you will receive +`Create /a/b/c` and may also receive `Write /a/b`. + +This happens because, on NTFS-backed volumes, modifying the entries of a +directory updates that directory's last-write time, and the Windows +backend requests `FILE_NOTIFY_CHANGE_LAST_WRITE` to support `Write` events +on files. The same `Write` filter therefore picks up the directory's +metadata update. + +kqueue has the same "directory `Write` = directory contents changed" +semantics, so portable code that treats `Write` on a directory as +"something inside it changed" works on Windows and BSD/macOS, but not on +Linux (inotify uses `Write` only for file-content changes). If you only +care about file content, filter out `Write` events whose path refers to a +directory. + +Whether the directory `Write` is actually delivered alongside the child +events is not guaranteed: it depends on `ReadDirectoryChangesW` buffering, +NTFS metadata update timing, and event coalescing, none of which fsnotify +controls. + ### kqueue (macOS, all BSD systems) kqueue requires opening a file descriptor for every file that's being watched; diff --git a/vendor/github.com/fsnotify/fsnotify/backend_inotify.go b/vendor/github.com/fsnotify/fsnotify/backend_inotify.go index cdb7812ac1..4c3f6f7c28 100644 --- a/vendor/github.com/fsnotify/fsnotify/backend_inotify.go +++ b/vendor/github.com/fsnotify/fsnotify/backend_inotify.go @@ -82,6 +82,13 @@ func (w *watches) len() int { return len(w.wd) } func (w *watches) add(ww *watch) { w.wd[ww.wd] = ww; w.path[ww.path] = ww.wd } func (w *watches) remove(watch *watch) { delete(w.path, watch.path); delete(w.wd, watch.wd) } +func isSameOrDescendantPath(path, root string) bool { + if path == root { + return true + } + return strings.HasPrefix(path, root+string(os.PathSeparator)) +} + func (w *watches) removePath(path string) ([]uint32, error) { path, recurse := recursivePath(path) wd, ok := w.path[path] @@ -103,7 +110,7 @@ func (w *watches) removePath(path string) ([]uint32, error) { wds := make([]uint32, 0, 8) wds = append(wds, wd) for p, rwd := range w.path { - if strings.HasPrefix(p, path) { + if isSameOrDescendantPath(p, path) { delete(w.path, p) delete(w.wd, rwd) wds = append(wds, rwd) @@ -501,7 +508,7 @@ func (w *inotify) handleEvent(inEvent *unix.InotifyEvent, buf *[65536]byte, offs if k == watch.wd || ww.path == ev.Name { continue } - if strings.HasPrefix(ww.path, ev.renamedFrom) { + if isSameOrDescendantPath(ww.path, ev.renamedFrom) { ww.path = strings.Replace(ww.path, ev.renamedFrom, ev.Name, 1) w.watches.wd[k] = ww } diff --git a/vendor/github.com/fsnotify/fsnotify/backend_windows.go b/vendor/github.com/fsnotify/fsnotify/backend_windows.go index 8ef0eb0f62..fb9210f24e 100644 --- a/vendor/github.com/fsnotify/fsnotify/backend_windows.go +++ b/vendor/github.com/fsnotify/fsnotify/backend_windows.go @@ -36,6 +36,13 @@ type readDirChangesW struct { var defaultBufferSize = 50 +func isSameOrDescendantPath(path, root string) bool { + if path == root { + return true + } + return strings.HasPrefix(path, root+string(os.PathSeparator)) +} + func newBackend(ev chan Event, errs chan error) (backend, error) { port, err := windows.CreateIoCompletionPort(windows.InvalidHandle, 0, 0, 0) if err != nil { @@ -605,7 +612,7 @@ func (w *readDirChangesW) readEvents() { w.mu.Lock() for _, watchMap := range w.watches { for _, ww := range watchMap { - if strings.HasPrefix(ww.path, old) { + if isSameOrDescendantPath(ww.path, old) { ww.path = filepath.Join(fullname, strings.TrimPrefix(ww.path, old)) } } diff --git a/vendor/github.com/fsnotify/fsnotify/fsnotify.go b/vendor/github.com/fsnotify/fsnotify/fsnotify.go index c7a4cb309c..38cb4dd481 100644 --- a/vendor/github.com/fsnotify/fsnotify/fsnotify.go +++ b/vendor/github.com/fsnotify/fsnotify/fsnotify.go @@ -92,6 +92,28 @@ import ( // Sometimes it will send events for all files, sometimes it will send no // events, and often only for some files. // +// Recursive watching is not currently enabled through fsnotify's public +// API; the recursive code path is gated and only exercised by fsnotify's +// own tests. The note below describes backend behavior observed when +// recursive watching is enabled internally, and is kept here as a +// reference for maintainers and contributors who encounter it. +// +// When recursive watching is enabled and you watch a directory, you may +// receive a Write event for an intermediate directory whenever a child +// entry inside it is created, renamed, or removed. For example, with a +// recursive watch on /a and a new file /a/b/c, you will receive +// Create /a/b/c and may also receive Write /a/b. +// +// This happens because, on NTFS-backed volumes, modifying the entries of a +// directory updates that directory's last-write time, and the Windows +// backend requests FILE_NOTIFY_CHANGE_LAST_WRITE to support Write events +// on files. The same Write filter therefore picks up the directory's +// metadata update. +// +// Whether the directory Write is actually delivered alongside the child +// events is not guaranteed; it depends on ReadDirectoryChangesW buffering, +// NTFS metadata update timing, and event coalescing. +// // The default ReadDirectoryChangesW() buffer size is 64K, which is the largest // value that is guaranteed to work with SMB filesystems. If you have many // events in quick succession this may not be enough, and you will have to use @@ -128,8 +150,12 @@ type Watcher struct { // want to wait until you've stopped receiving them // (see the dedup example in cmd/fsnotify). // - // Some systems may send Write event for directories - // when the directory content changes. + // Some systems also send Write events for directories + // when the directory contents change. This is the + // case for kqueue, and on Windows for the directory + // that contains a created, renamed, or removed child + // entry. It does not happen on inotify. See the + // per-platform notes on [Watcher]. // // fsnotify.Chmod Attributes were changed. On Linux this is also sent // when a file is removed (or more accurately, when a @@ -178,7 +204,9 @@ const ( Create Op = 1 << iota // The pathname was written to; this does *not* mean the write has finished, - // and a write can be followed by more writes. + // and a write can be followed by more writes. On Windows and kqueue, a + // Write on a directory can also indicate that its contents changed; see + // the per-platform notes on [Watcher]. Write // The path was removed; any watches on it will be removed. Some "remove" diff --git a/vendor/github.com/go-logr/logr/context_noslog.go b/vendor/github.com/go-logr/logr/context_noslog.go index f012f9a18e..0a3d1a125e 100644 --- a/vendor/github.com/go-logr/logr/context_noslog.go +++ b/vendor/github.com/go-logr/logr/context_noslog.go @@ -1,5 +1,4 @@ //go:build !go1.21 -// +build !go1.21 /* Copyright 2019 The logr Authors. diff --git a/vendor/github.com/go-logr/logr/context_slog.go b/vendor/github.com/go-logr/logr/context_slog.go index 065ef0b828..c69eb01ba1 100644 --- a/vendor/github.com/go-logr/logr/context_slog.go +++ b/vendor/github.com/go-logr/logr/context_slog.go @@ -1,5 +1,4 @@ //go:build go1.21 -// +build go1.21 /* Copyright 2019 The logr Authors. diff --git a/vendor/github.com/go-logr/logr/funcr/funcr.go b/vendor/github.com/go-logr/logr/funcr/funcr.go index b22c57d713..7f4996e9e4 100644 --- a/vendor/github.com/go-logr/logr/funcr/funcr.go +++ b/vendor/github.com/go-logr/logr/funcr/funcr.go @@ -426,7 +426,7 @@ func (f Formatter) colon() byte { } func (f Formatter) pretty(value any) string { - return f.prettyWithFlags(value, 0, 0) + return f.prettyWithFlags(value, 0, 0, 0, nil) } const ( @@ -434,7 +434,13 @@ const ( ) // TODO: This is not fast. Most of the overhead goes here. -func (f Formatter) prettyWithFlags(value any, flags uint32, depth int) string { +// value: The value to render +// flags: Bitmask of flags (see above) +// depth: The current depth of nested structs, slices, arrays, and maps +// ptrDepth: The current depth of including pointer dereferences +// ptrMap: A map of pointers already seen, to avoid infinite recursion (usually +// nil unless ptrDepth is large) +func (f Formatter) prettyWithFlags(value any, flags uint32, depth int, ptrDepth int, ptrMap map[uintptr]bool) string { if depth > f.opts.MaxLogDepth { return `""` } @@ -504,7 +510,7 @@ func (f Formatter) prettyWithFlags(value any, flags uint32, depth int) string { // arbitrary keys might need escaping buf.WriteString(prettyString(k)) buf.WriteByte(f.colon()) - buf.WriteString(f.prettyWithFlags(v[i+1], 0, depth+1)) + buf.WriteString(f.prettyWithFlags(v[i+1], 0, depth+1, ptrDepth+1, ptrMap)) } if flags&flagRawStruct == 0 { buf.WriteByte('}') @@ -576,7 +582,7 @@ func (f Formatter) prettyWithFlags(value any, flags uint32, depth int) string { } printComma = true // if we got here, we are rendering a field if fld.Anonymous && fld.Type.Kind() == reflect.Struct && name == "" { - buf.WriteString(f.prettyWithFlags(v.Field(i).Interface(), flags|flagRawStruct, depth+1)) + buf.WriteString(f.prettyWithFlags(v.Field(i).Interface(), flags|flagRawStruct, depth+1, ptrDepth+1, ptrMap)) continue } if name == "" { @@ -585,7 +591,7 @@ func (f Formatter) prettyWithFlags(value any, flags uint32, depth int) string { // field names can't contain characters which need escaping buf.WriteString(f.quoted(name, false)) buf.WriteByte(f.colon()) - buf.WriteString(f.prettyWithFlags(v.Field(i).Interface(), 0, depth+1)) + buf.WriteString(f.prettyWithFlags(v.Field(i).Interface(), 0, depth+1, ptrDepth+1, ptrMap)) } if flags&flagRawStruct == 0 { buf.WriteByte('}') @@ -612,7 +618,7 @@ func (f Formatter) prettyWithFlags(value any, flags uint32, depth int) string { buf.WriteByte(f.comma()) } e := v.Index(i) - buf.WriteString(f.prettyWithFlags(e.Interface(), 0, depth+1)) + buf.WriteString(f.prettyWithFlags(e.Interface(), 0, depth+1, ptrDepth+1, ptrMap)) } buf.WriteByte(']') return buf.String() @@ -637,7 +643,8 @@ func (f Formatter) prettyWithFlags(value any, flags uint32, depth int) string { keystr = prettyString(keystr) } else { // prettyWithFlags will produce already-escaped values - keystr = f.prettyWithFlags(it.Key().Interface(), 0, depth+1) + // key depth is unrelated to overall depth + keystr = f.prettyWithFlags(it.Key().Interface(), 0, 0, ptrDepth, ptrMap) if t.Key().Kind() != reflect.String { // JSON only does string keys. Unlike Go's standard JSON, we'll // convert just about anything to a string. @@ -646,16 +653,34 @@ func (f Formatter) prettyWithFlags(value any, flags uint32, depth int) string { } buf.WriteString(keystr) buf.WriteByte(f.colon()) - buf.WriteString(f.prettyWithFlags(it.Value().Interface(), 0, depth+1)) + buf.WriteString(f.prettyWithFlags(it.Value().Interface(), 0, depth+1, ptrDepth+1, ptrMap)) i++ } buf.WriteByte('}') return buf.String() - case reflect.Ptr, reflect.Interface: + case reflect.Pointer, reflect.Interface: if v.IsNil() { return "null" } - return f.prettyWithFlags(v.Elem().Interface(), 0, depth) + // Special case: recursive pointers. For normal use we do not want to + // count pointer dereferences as depth, but if we see the same pointer + // again we have a recursion and need to stop. After a large number of + // pointer dereferences we will start tracking pointers to avoid the + // perf hit of doing it in the normal path. + // + // This should not happen accidentally (e.g. json decoding should never + // do this) but we can handle it gracefully. + if ptrMap != nil && ptrMap[uintptr(v.Pointer())] { + depth = f.opts.MaxLogDepth + 1 // force a depth error + } + const maxDepthFactor = 4 // arbitrary, but we want it large enough to not false-alert + if ptrDepth > f.opts.MaxLogDepth*maxDepthFactor && ptrMap == nil { + ptrMap = map[uintptr]bool{} + } + if ptrMap != nil { + ptrMap[(uintptr)(v.Pointer())] = true + } + return f.prettyWithFlags(v.Elem().Interface(), 0, depth, ptrDepth+1, ptrMap) } return fmt.Sprintf(`""`, t.Kind().String()) } @@ -697,7 +722,7 @@ func isEmpty(v reflect.Value) bool { return v.Float() == 0 case reflect.Complex64, reflect.Complex128: return v.Complex() == 0 - case reflect.Interface, reflect.Ptr: + case reflect.Interface, reflect.Pointer: return v.IsNil() } return false diff --git a/vendor/github.com/go-logr/logr/funcr/slogsink.go b/vendor/github.com/go-logr/logr/funcr/slogsink.go index 7bd84761e2..8b519c91e1 100644 --- a/vendor/github.com/go-logr/logr/funcr/slogsink.go +++ b/vendor/github.com/go-logr/logr/funcr/slogsink.go @@ -1,5 +1,4 @@ //go:build go1.21 -// +build go1.21 /* Copyright 2023 The logr Authors. @@ -33,7 +32,7 @@ const extraSlogSinkDepth = 3 // 2 for slog, 1 for SlogSink func (l fnlogger) Handle(_ context.Context, record slog.Record) error { kvList := make([]any, 0, 2*record.NumAttrs()) record.Attrs(func(attr slog.Attr) bool { - kvList = attrToKVs(attr, kvList) + kvList = attrToKVs(attr, kvList, l.opts.MaxLogDepth) return true }) @@ -49,7 +48,7 @@ func (l fnlogger) Handle(_ context.Context, record slog.Record) error { func (l fnlogger) WithAttrs(attrs []slog.Attr) logr.SlogSink { kvList := make([]any, 0, 2*len(attrs)) for _, attr := range attrs { - kvList = attrToKVs(attr, kvList) + kvList = attrToKVs(attr, kvList, l.opts.MaxLogDepth) } l.AddValues(kvList) return &l @@ -61,14 +60,25 @@ func (l fnlogger) WithGroup(name string) logr.SlogSink { } // attrToKVs appends a slog.Attr to a logr-style kvList. It handle slog Groups -// and other details of slog. -func attrToKVs(attr slog.Attr, kvList []any) []any { +// and other details of slog. maxDepth bounds recursion into nested groups so a +// deeply-nested slog.Group cannot exhaust the stack; it is decremented per group +// level and starts at the Formatter's MaxLogDepth (past which the formatter would +// truncate the rendering anyway). +func attrToKVs(attr slog.Attr, kvList []any, maxDepth int) []any { attrVal := attr.Value.Resolve() if attrVal.Kind() == slog.KindGroup { + if maxDepth <= 0 { + // Nesting is too deep to build without risking a stack overflow. + // Stop here; the formatter truncates below MaxLogDepth regardless. + if attr.Key != "" { + kvList = append(kvList, attr.Key, "") + } + return kvList + } groupVal := attrVal.Group() grpKVs := make([]any, 0, 2*len(groupVal)) for _, attr := range groupVal { - grpKVs = attrToKVs(attr, grpKVs) + grpKVs = attrToKVs(attr, grpKVs, maxDepth-1) } if attr.Key == "" { // slog says we have to inline these diff --git a/vendor/github.com/go-logr/logr/sloghandler.go b/vendor/github.com/go-logr/logr/sloghandler.go index 82d1ba4948..befaf5510f 100644 --- a/vendor/github.com/go-logr/logr/sloghandler.go +++ b/vendor/github.com/go-logr/logr/sloghandler.go @@ -1,5 +1,4 @@ //go:build go1.21 -// +build go1.21 /* Copyright 2023 The logr Authors. diff --git a/vendor/github.com/go-logr/logr/slogr.go b/vendor/github.com/go-logr/logr/slogr.go index 28a83d0243..bfe80eb8d6 100644 --- a/vendor/github.com/go-logr/logr/slogr.go +++ b/vendor/github.com/go-logr/logr/slogr.go @@ -1,5 +1,4 @@ //go:build go1.21 -// +build go1.21 /* Copyright 2023 The logr Authors. diff --git a/vendor/github.com/go-logr/logr/slogsink.go b/vendor/github.com/go-logr/logr/slogsink.go index 4060fcbc2b..ab76ea99fe 100644 --- a/vendor/github.com/go-logr/logr/slogsink.go +++ b/vendor/github.com/go-logr/logr/slogsink.go @@ -1,5 +1,4 @@ //go:build go1.21 -// +build go1.21 /* Copyright 2023 The logr Authors. diff --git a/vendor/github.com/go-openapi/analysis/.gitignore b/vendor/github.com/go-openapi/analysis/.gitignore index d8f4186fe5..20c4e0fa04 100644 --- a/vendor/github.com/go-openapi/analysis/.gitignore +++ b/vendor/github.com/go-openapi/analysis/.gitignore @@ -3,3 +3,5 @@ .idea .env .mcp.json +go.work.sum +.worktrees diff --git a/vendor/github.com/go-openapi/analysis/.golangci.yml b/vendor/github.com/go-openapi/analysis/.golangci.yml index b97d68077f..0d7baa1d7f 100644 --- a/vendor/github.com/go-openapi/analysis/.golangci.yml +++ b/vendor/github.com/go-openapi/analysis/.golangci.yml @@ -4,8 +4,11 @@ linters: disable: - depguard - funlen + - goconst - godox - gomoddirectives + - gomodguard + - gomodguard_v2 - exhaustruct - nlreturn - nonamedreturns diff --git a/vendor/github.com/go-openapi/analysis/CONTRIBUTORS.md b/vendor/github.com/go-openapi/analysis/CONTRIBUTORS.md index 2f85f1c050..a89274cdbc 100644 --- a/vendor/github.com/go-openapi/analysis/CONTRIBUTORS.md +++ b/vendor/github.com/go-openapi/analysis/CONTRIBUTORS.md @@ -4,24 +4,31 @@ | Total Contributors | Total Contributions | | --- | --- | -| 15 | 207 | +| 22 | 273 | | Username | All Time Contribution Count | All Commits | | --- | --- | --- | -| @fredbi | 104 | | -| @casualjim | 70 | | +| @fredbi | 133 | | +| @casualjim | 91 | | | @keramix | 9 | | | @youyuanwu | 8 | | -| @msample | 3 | | +| @wjase | 7 | | | @kul-amr | 3 | | +| @schafle | 3 | | +| @msample | 3 | | | @mbohlool | 2 | | -| @Copilot | 1 | | -| @danielfbm | 1 | | -| @gregmarr | 1 | | -| @guillemj | 1 | | -| @knweiss | 1 | | -| @tklauser | 1 | | -| @cuishuang | 1 | | +| @zmay2030 | 2 | | | @ujjwalsh | 1 | | +| @itengfei | 1 | | +| @nrnrk | 1 | | +| @cuishuang | 1 | | +| @tklauser | 1 | | +| @Shimizu1111 | 1 | | +| @thaJeztah | 1 | | +| @knweiss | 1 | | +| @guillemj | 1 | | +| @gregmarr | 1 | | +| @danielfbm | 1 | | +| @Copilot | 1 | | _this file was generated by the [Contributors GitHub Action](https://github.com/github-community-projects/contributors)_ diff --git a/vendor/github.com/go-openapi/analysis/README.md b/vendor/github.com/go-openapi/analysis/README.md index 82c782fcdd..2a90462974 100644 --- a/vendor/github.com/go-openapi/analysis/README.md +++ b/vendor/github.com/go-openapi/analysis/README.md @@ -12,18 +12,15 @@ --- -A foundational library to analyze an OAI specification document for easier reasoning about the content. +A foundational library to analyze, diff, flatten, merge, and fix OAI specification documents for easier reasoning about the content. ## Announcements * **2025-12-19** : new community chat on discord * a new discord community channel is available to be notified of changes and support users - * our venerable Slack channel remains open, and will be eventually discontinued on **2026-03-31** You may join the discord community by clicking the invite link on the discord badge (also above). [![Discord Channel][discord-badge]][discord-url] -Or join our Slack channel: [![Slack Channel][slack-logo]![slack-badge]][slack-url] - ## Status API is stable. @@ -38,6 +35,7 @@ go get github.com/go-openapi/analysis * An analyzer providing methods to walk the functional content of a specification * A spec flattener producing a self-contained document bundle, while preserving `$ref`s +* A spec differ ("diff") to compare two specs and report structural and compatibility changes * A spec merger ("mixin") to merge several spec documents into a primary spec * A spec "fixer" ensuring that response descriptions are non empty @@ -78,9 +76,9 @@ on top of which it has been built. ## Other documentation * [All-time contributors](./CONTRIBUTORS.md) -* [Contributing guidelines](.github/CONTRIBUTING.md) -* [Maintainers documentation](docs/MAINTAINERS.md) -* [Code style](docs/STYLE.md) +* [Contributing guidelines][contributing-doc-site] +* [Maintainers documentation][maintainers-doc-site] +* [Code style][style-doc-site] ## Cutting a new release @@ -111,9 +109,6 @@ Maintainers can cut a new release by either: [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/analysis [godoc-url]: http://pkg.go.dev/github.com/go-openapi/analysis -[slack-logo]: https://a.slack-edge.com/e6a93c1/img/icons/favicon-32.png -[slack-badge]: https://img.shields.io/badge/slack-blue?link=https%3A%2F%2Fgoswagger.slack.com%2Farchives%2FC04R30YM -[slack-url]: https://goswagger.slack.com/archives/C04R30YMU [discord-badge]: https://img.shields.io/discord/1446918742398341256?logo=discord&label=discord&color=blue [discord-url]: https://discord.gg/FfnFYaC3k5 @@ -125,3 +120,7 @@ Maintainers can cut a new release by either: [goversion-url]: https://github.com/go-openapi/analysis/blob/master/go.mod [top-badge]: https://img.shields.io/github/languages/top/go-openapi/analysis [commits-badge]: https://img.shields.io/github/commits-since/go-openapi/analysis/latest + +[contributing-doc-site]: https://go-openapi.github.io/doc-site/contributing/contributing/index.html +[maintainers-doc-site]: https://go-openapi.github.io/doc-site/maintainers/index.html +[style-doc-site]: https://go-openapi.github.io/doc-site/contributing/style/index.html diff --git a/vendor/github.com/go-openapi/analysis/analyzer.go b/vendor/github.com/go-openapi/analysis/analyzer.go index 1c91b8c550..c24811aaeb 100644 --- a/vendor/github.com/go-openapi/analysis/analyzer.go +++ b/vendor/github.com/go-openapi/analysis/analyzer.go @@ -145,19 +145,27 @@ type Spec struct { enums enumAnalysis allSchemas map[string]SchemaRef allOfs map[string]SchemaRef + mangler mangling.NameMangler } // New takes a swagger spec object and returns an analyzed spec document. // The analyzed document contains a number of indices that make it easier to // reason about semantics of a swagger specification for use in code generation // or validation etc. -func New(doc *spec.Swagger) *Spec { +func New(doc *spec.Swagger, opts ...Option) *Spec { + o := &analyzerOptions{} + for _, opt := range opts { + opt(o) + } + a := &Spec{ spec: doc, references: referenceAnalysis{}, patterns: patternAnalysis{}, enums: enumAnalysis{}, + mangler: mangling.NewNameMangler(o.manglerOpts...), } + a.reset() a.initialize() @@ -288,20 +296,6 @@ func (s *Spec) ProducesFor(operation *spec.Operation) []string { return s.structMapKeys(prod) } -func mapKeyFromParam(param *spec.Parameter) string { - return fmt.Sprintf("%s#%s", param.In, fieldNameFromParam(param)) -} - -func fieldNameFromParam(param *spec.Parameter) string { - // TODO: this should be x-go-name - if nm, ok := param.Extensions.GetString("go-name"); ok { - return nm - } - mangler := mangling.NewNameMangler() - - return mangler.ToGoName(param.Name) -} - // ErrorOnParamFunc is a callback function to be invoked // whenever an error is encountered while resolving references // on parameters. @@ -651,6 +645,19 @@ func (s *Spec) AllEnums() map[string][]any { return cloneEnumMap(s.enums.allEnums) } +func (s *Spec) mapKeyFromParam(param *spec.Parameter) string { + return fmt.Sprintf("%s#%s", param.In, s.fieldNameFromParam(param)) +} + +func (s *Spec) fieldNameFromParam(param *spec.Parameter) string { + // TODO: this should be x-go-name + if nm, ok := param.Extensions.GetString("go-name"); ok { + return nm + } + + return s.mangler.ToGoName(param.Name) +} + func (s *Spec) structMapKeys(mp map[string]struct{}) []string { if len(mp) == 0 { return nil @@ -668,7 +675,7 @@ func (s *Spec) paramsAsMap(parameters []spec.Parameter, res map[string]spec.Para for _, param := range parameters { pr := param if pr.Ref.String() == "" { - res[mapKeyFromParam(&pr)] = pr + res[s.mapKeyFromParam(&pr)] = pr continue } @@ -699,7 +706,7 @@ func (s *Spec) paramsAsMap(parameters []spec.Parameter, res map[string]spec.Para } pr = objAsParam - res[mapKeyFromParam(&pr)] = pr + res[s.mapKeyFromParam(&pr)] = pr } } diff --git a/vendor/github.com/go-openapi/analysis/flatten.go b/vendor/github.com/go-openapi/analysis/flatten.go index d7ee0064b6..c90456f6a0 100644 --- a/vendor/github.com/go-openapi/analysis/flatten.go +++ b/vendor/github.com/go-openapi/analysis/flatten.go @@ -243,7 +243,7 @@ func nameInlinedSchemas(opts *FlattenOpts) error { continue } - asch, err := Schema(SchemaOpts{Schema: sch.Schema, Root: opts.Swagger(), BasePath: opts.BasePath}) + asch, err := Schema(SchemaOpts{Schema: sch.Schema, Root: opts.Swagger(), BasePath: opts.BasePath, PathLoaderWithOptions: opts.PathLoaderWithOptions}) if err != nil { return ErrAtKey(key, err) } @@ -554,6 +554,7 @@ func updateRefParents(allRefs map[string]spec.Ref, r *newRef) { } } +//nolint:gocognit,gocyclo,cyclop // legacy from a lot of design choices that led to concentrate the complexity just here. func stripOAIGenForRef(opts *FlattenOpts, k string, r *newRef) (bool, error) { replacedWithComplex := false @@ -574,7 +575,7 @@ func stripOAIGenForRef(opts *FlattenOpts, k string, r *newRef) (bool, error) { } // rewrite other parents to point to first parent - if len(pr) > 1 { + if len(pr) > 1 { //nolint:nestif // should be refactored at a later time for _, p := range pr[1:] { replacingRef := spec.MustCreateRef(pr[0]) @@ -656,7 +657,7 @@ func stripOAIGenForRef(opts *FlattenOpts, k string, r *newRef) (bool, error) { // determine if the previous substitution did inline a complex schema if r.schema != nil && r.schema.Ref.String() == "" { // inline schema - asch, err := Schema(SchemaOpts{Schema: r.schema, Root: opts.Swagger(), BasePath: opts.BasePath}) + asch, err := Schema(SchemaOpts{Schema: r.schema, Root: opts.Swagger(), BasePath: opts.BasePath, PathLoaderWithOptions: opts.PathLoaderWithOptions}) if err != nil { return false, err } @@ -760,7 +761,7 @@ func flattenAnonPointer(key string, v SchemaRef, refsToReplace map[string]Schema debugLog("namePointers at %s for %s", key, v.Ref.String()) // qualify the expanded schema - asch, ers := Schema(SchemaOpts{Schema: v.Schema, Root: opts.Swagger(), BasePath: opts.BasePath}) + asch, ers := Schema(SchemaOpts{Schema: v.Schema, Root: opts.Swagger(), BasePath: opts.BasePath, PathLoaderWithOptions: opts.PathLoaderWithOptions}) if ers != nil { return ErrAtKey(key, ers) } diff --git a/vendor/github.com/go-openapi/analysis/flatten_name.go b/vendor/github.com/go-openapi/analysis/flatten_name.go index 922cae55c5..9d73217184 100644 --- a/vendor/github.com/go-openapi/analysis/flatten_name.go +++ b/vendor/github.com/go-openapi/analysis/flatten_name.go @@ -273,9 +273,9 @@ func mangler(o *FlattenOpts) func(string) string { if o.KeepNames { return func(in string) string { return in } } - mangler := mangling.NewNameMangler() + m := mangling.NewNameMangler(o.ManglerOpts...) - return mangler.ToJSONName + return m.ToJSONName } func nameFromRef(ref spec.Ref, o *FlattenOpts) string { diff --git a/vendor/github.com/go-openapi/analysis/flatten_options.go b/vendor/github.com/go-openapi/analysis/flatten_options.go index 23a57ea1ac..1e182ad435 100644 --- a/vendor/github.com/go-openapi/analysis/flatten_options.go +++ b/vendor/github.com/go-openapi/analysis/flatten_options.go @@ -4,9 +4,12 @@ package analysis import ( + "encoding/json" "log" "github.com/go-openapi/spec" + "github.com/go-openapi/swag/loading" + "github.com/go-openapi/swag/mangling" ) // FlattenOpts configuration for flattening a swagger specification. @@ -24,12 +27,23 @@ type FlattenOpts struct { BasePath string // The location of the root document for this spec to resolve relative $ref // Flattening options - Expand bool // When true, skip flattening the spec and expand it instead (if Minimal is false) - Minimal bool // When true, do not decompose complex structures such as allOf - Verbose bool // enable some reporting on possible name conflicts detected - RemoveUnused bool // When true, remove unused parameters, responses and definitions after expansion/flattening - ContinueOnError bool // Continue when spec expansion issues are found - KeepNames bool // Do not attempt to jsonify names from references when flattening + Expand bool // When true, skip flattening the spec and expand it instead (if Minimal is false) + Minimal bool // When true, do not decompose complex structures such as allOf + Verbose bool // enable some reporting on possible name conflicts detected + RemoveUnused bool // When true, remove unused parameters, responses and definitions after expansion/flattening + ContinueOnError bool // Continue when spec expansion issues are found + KeepNames bool // Do not attempt to jsonify names from references when flattening + ManglerOpts []mangling.Option `json:"-"` // Options for the name mangler used to jsonify names + + // PathLoaderWithOptions injects the document loader used to resolve remote and relative $ref + // while flattening or expanding the specification. It matches the option-aware loader signature + // of github.com/go-openapi/swag/loading (and go-openapi/loads). + // + // Security: when flattening a specification obtained from an untrusted source, set this to a + // confined loader — for example one built with loading.WithRoot (to confine local reads) and + // loading.WithHTTPClient (to restrict remote fetches), or a restricted loader from + // go-openapi/loads. Left nil, the spec package default (unsandboxed) loader is used. + PathLoaderWithOptions func(string, ...loading.Option) (json.RawMessage, error) `json:"-"` /* Extra keys */ _ struct{} // require keys @@ -38,9 +52,10 @@ type FlattenOpts struct { // ExpandOpts creates a spec.[spec.ExpandOptions] to configure expanding a specification document. func (f *FlattenOpts) ExpandOpts(skipSchemas bool) *spec.ExpandOptions { return &spec.ExpandOptions{ - RelativeBase: f.BasePath, - SkipSchemas: skipSchemas, - ContinueOnError: f.ContinueOnError, + RelativeBase: f.BasePath, + SkipSchemas: skipSchemas, + ContinueOnError: f.ContinueOnError, + PathLoaderWithOptions: f.PathLoaderWithOptions, } } diff --git a/vendor/github.com/go-openapi/analysis/go.work.sum b/vendor/github.com/go-openapi/analysis/go.work.sum deleted file mode 100644 index 899a68976e..0000000000 --- a/vendor/github.com/go-openapi/analysis/go.work.sum +++ /dev/null @@ -1,47 +0,0 @@ -github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -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/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= -github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= -github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30 h1:BHT1/DKsYDGkUgQ2jmMaozVcdk+sVfz0+1ZJq4zkWgw= -github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= -github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= -github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= -github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI= -github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.mongodb.org/mongo-driver v1.17.6 h1:87JUG1wZfWsr6rIz3ZmpH90rL5tea7O3IHuSwHUpsss= -go.mongodb.org/mongo-driver v1.17.6/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ= -golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= -golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= -golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= -golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= -golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= -golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc= -golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c= -golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU= -golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= -golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= -golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= -golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= -golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= -golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= -golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= -golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= -golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -golang.org/x/telemetry v0.0.0-20260209163413-e7419c687ee4 h1:bTLqdHv7xrGlFbvf5/TXNxy/iUwwdkjhqQTJDjW7aj0= -golang.org/x/telemetry v0.0.0-20260209163413-e7419c687ee4/go.mod h1:g5NllXBEermZrmR51cJDQxmJUHUOfRAaNyWBM+R+548= -golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg= -golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg= -golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM= -golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU= -golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A= -golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ= -golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc= -golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg= -golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= -golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= diff --git a/vendor/github.com/go-openapi/analysis/mixin.go b/vendor/github.com/go-openapi/analysis/mixin.go index a7a9306cb3..635b1b0ad4 100644 --- a/vendor/github.com/go-openapi/analysis/mixin.go +++ b/vendor/github.com/go-openapi/analysis/mixin.go @@ -11,37 +11,66 @@ import ( "github.com/go-openapi/spec" ) -// Mixin modifies the primary swagger spec by adding the paths and -// definitions from the mixin specs. Top level parameters and -// responses from the mixins are also carried over. Operation id -// collisions are avoided by appending "Mixin" but only if -// needed. +// Mixin merges one or more Swagger 2.0 documents into a primary document. // -// The following parts of primary are subject to merge, filling empty details +// # Argument order and precedence // -// - Info +// The first argument is the primary spec, which Mixin modifies in place. +// Subsequent arguments are mixins, listed in decreasing order of priority. +// On any collision, the primary always wins; among mixins, the earliest one +// wins. +// +// Example: given a primary spec with host "a.example.com" and a mixin with +// host "b.example.com", the merged result keeps "a.example.com" (primary +// wins, the mixin value is dropped). Given a primary without a host and a +// mixin with host "b.example.com", the merged result uses "b.example.com" +// (the mixin fills in the empty field on the primary). +// +// # What gets merged +// +// Top-level scalar fields on the primary are filled from the first mixin +// that provides them, but only if the primary's value is the zero value: +// +// - Info (including the nested Contact and License) // - BasePath // - Host // - ExternalDocs // -// Consider calling [FixEmptyResponseDescriptions]() on the modified primary -// if you read them from storage and they are valid to start with. +// Map and slice fields are merged entry by entry. This covers: +// +// - paths, definitions, parameters, responses +// - securityDefinitions, security, tags +// - top-level and Info extensions +// +// Duplicate keys (or equal security requirements, or equal tag names) are +// skipped with a warning; warnings are returned as a slice and intended to +// be inspected by the caller (e.g. compared to an expected collision count +// in build scripts). +// +// Schemes, consumes and produces are merged as the union of distinct +// values. Duplicates there are silently dropped, no warning is emitted. +// +// Operation id collisions are auto-resolved by appending "Mixin" to the +// mixin operation id (N is the mixin index), so the merged spec keeps +// unique operation ids. +// +// # Notes and limitations // -// Entries in "paths", "definitions", "parameters" and "responses" are -// added to the primary in the order of the given mixins. If the entry -// already exists in primary it is skipped with a warning message. +// Consider calling [FixEmptyResponseDescriptions] on the modified primary +// if you read responses from storage and they are valid to start with. // -// The count of skipped entries (from collisions) is returned so any -// deviation from the number expected can flag a warning in your build -// scripts. Carefully review the collisions before accepting them; -// consider renaming things if possible. +// No key normalization takes place. Ensure paths, type names, etc. are +// canonical if your downstream tools rely on normalized forms. // -// No key normalization takes place (paths, type defs, -// etc). Ensure they are canonical if your downstream tools do -// key normalization of any form. +// YAML anchors (& / *) are resolved by the YAML parser before Mixin sees +// the document, so they are not preserved in the merged output, and they +// cannot be shared across input files. Use $ref for cross-file reuse. See +// https://goswagger.io/go-swagger/faq/faq_swagger/#does-swagger-mixin-preserve-yaml-anchors // -// Merging schemes ([http], https), and consumers/producers do not account for -// collisions. +// The order of paths and definitions in the merged output is alphabetical: +// the underlying spec model stores them as Go maps, which serialize with +// sorted keys. Source-file order is not preserved. See +// https://goswagger.io/go-swagger/faq/faq_swagger/#can-i-control-the-path-or-operation-order-in-swagger-mixin-output func Mixin(primary *spec.Swagger, mixins ...*spec.Swagger) []string { skipped := make([]string, 0, len(mixins)) opIDs := getOpIDs(primary) @@ -101,6 +130,7 @@ func pathItemOps(p spec.PathItem) []*spec.Operation { rv = appendOp(rv, p.Post) rv = appendOp(rv, p.Delete) rv = appendOp(rv, p.Head) + rv = appendOp(rv, p.Options) rv = appendOp(rv, p.Patch) return rv @@ -184,9 +214,9 @@ func mergePaths(primary *spec.Swagger, m *spec.Swagger, opIDs map[string]bool, m // Swagger requires that operationIds be // unique within a spec. If we find a // collision we append "Mixin0" to the - // operatoinId we are adding, where 0 is mixin + // operationId we are adding, where 0 is mixin // index. We assume that operationIds with - // all the proivded specs are already unique. + // all the provided specs are already unique. piops := pathItemOps(v) for _, piop := range piops { if opIDs[piop.ID] { @@ -329,7 +359,7 @@ func mergeSwaggerProps(primary *spec.Swagger, m *spec.Swagger) []string { if primary.ExternalDocs == nil { primary.ExternalDocs = m.ExternalDocs - } else if m != nil { + } else if m.ExternalDocs != nil { skippedDocs = mergeExternalDocs(primary.ExternalDocs, m.ExternalDocs) skipped = append(skipped, skippedDocs...) } diff --git a/vendor/github.com/go-openapi/analysis/options.go b/vendor/github.com/go-openapi/analysis/options.go new file mode 100644 index 0000000000..b46cd2ca69 --- /dev/null +++ b/vendor/github.com/go-openapi/analysis/options.go @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package analysis + +import "github.com/go-openapi/swag/mangling" + +// Option configures the behavior of a new [Spec] analyzer. +type Option func(*analyzerOptions) + +type analyzerOptions struct { + manglerOpts []mangling.Option +} + +// WithManglerOptions sets the name mangler options used when building +// Go identifiers from specification names (e.g. parameter names). +func WithManglerOptions(opts ...mangling.Option) Option { + return func(o *analyzerOptions) { + o.manglerOpts = append(o.manglerOpts, opts...) + } +} diff --git a/vendor/github.com/go-openapi/analysis/schema.go b/vendor/github.com/go-openapi/analysis/schema.go index bedea652ac..2c250461eb 100644 --- a/vendor/github.com/go-openapi/analysis/schema.go +++ b/vendor/github.com/go-openapi/analysis/schema.go @@ -4,8 +4,11 @@ package analysis import ( + "encoding/json" + "github.com/go-openapi/spec" "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag/loading" ) // SchemaOpts configures the schema analyzer. @@ -13,7 +16,17 @@ type SchemaOpts struct { Schema *spec.Schema Root any BasePath string - _ struct{} + + // PathLoaderWithOptions injects the document loader (with loading options) used to resolve + // remote and relative $ref while analyzing a schema. + // + // Security: set this to a confined loader — for example one built with loading.WithRoot and + // loading.WithHTTPClient, or a restricted loader from go-openapi/loads — when the schema may + // derive from an untrusted source. Left nil, the spec package default (unsandboxed) loader is + // used. + PathLoaderWithOptions func(string, ...loading.Option) (json.RawMessage, error) + + _ struct{} } // Schema analysis, will classify the schema according to known @@ -24,9 +37,10 @@ func Schema(opts SchemaOpts) (*AnalyzedSchema, error) { } a := &AnalyzedSchema{ - schema: opts.Schema, - root: opts.Root, - basePath: opts.BasePath, + schema: opts.Schema, + root: opts.Root, + basePath: opts.BasePath, + pathLoaderWithOptions: opts.PathLoaderWithOptions, } a.initializeFlags() @@ -54,9 +68,10 @@ func Schema(opts SchemaOpts) (*AnalyzedSchema, error) { // AnalyzedSchema indicates what the schema represents. type AnalyzedSchema struct { - schema *spec.Schema - root any - basePath string + schema *spec.Schema + root any + basePath string + pathLoaderWithOptions func(string, ...loading.Option) (json.RawMessage, error) hasProps bool hasAllOf bool @@ -103,19 +118,32 @@ func (a *AnalyzedSchema) inherits(other *AnalyzedSchema) { a.IsEnum = other.IsEnum } +// subSchemaOpts builds SchemaOpts for a nested schema, propagating the root, base path and the +// injected document loader so that confinement applies throughout the recursive analysis. +func (a *AnalyzedSchema) subSchemaOpts(sch *spec.Schema) SchemaOpts { + return SchemaOpts{ + Schema: sch, + Root: a.root, + BasePath: a.basePath, + PathLoaderWithOptions: a.pathLoaderWithOptions, + } +} + +// expandOpts builds the spec expand options for this analysis, carrying the injected loader so +// remote/relative $ref are resolved through it (rather than the unsandboxed package default). +func (a *AnalyzedSchema) expandOpts() *spec.ExpandOptions { + return &spec.ExpandOptions{PathLoaderWithOptions: a.pathLoaderWithOptions} +} + func (a *AnalyzedSchema) inferFromRef() error { if a.hasRef { sch := new(spec.Schema) sch.Ref = a.schema.Ref - err := spec.ExpandSchema(sch, a.root, nil) + err := spec.ExpandSchemaWithOptions(sch, a.root, nil, a.expandOpts()) if err != nil { return err } - rsch, err := Schema(SchemaOpts{ - Schema: sch, - Root: a.root, - BasePath: a.basePath, - }) + rsch, err := Schema(a.subSchemaOpts(sch)) if err != nil { // NOTE(fredbi): currently the only cause for errors is // unresolved ref. Since spec.ExpandSchema() expands the @@ -159,11 +187,7 @@ func (a *AnalyzedSchema) inferMap() error { // maps if a.schema.AdditionalProperties.Schema != nil { - msch, err := Schema(SchemaOpts{ - Schema: a.schema.AdditionalProperties.Schema, - Root: a.root, - BasePath: a.basePath, - }) + msch, err := Schema(a.subSchemaOpts(a.schema.AdditionalProperties.Schema)) if err != nil { return err } @@ -186,11 +210,7 @@ func (a *AnalyzedSchema) inferArray() error { a.IsArray = a.isArrayType() && (a.schema.Items == nil || a.schema.Items.Schemas == nil) if a.IsArray && a.hasItems { if a.schema.Items.Schema != nil { - itsch, err := Schema(SchemaOpts{ - Schema: a.schema.Items.Schema, - Root: a.root, - BasePath: a.basePath, - }) + itsch, err := Schema(a.subSchemaOpts(a.schema.Items.Schema)) if err != nil { return err } diff --git a/vendor/github.com/go-openapi/errors/.gitignore b/vendor/github.com/go-openapi/errors/.gitignore index 9364443a6f..96c4149a0b 100644 --- a/vendor/github.com/go-openapi/errors/.gitignore +++ b/vendor/github.com/go-openapi/errors/.gitignore @@ -3,5 +3,4 @@ .idea .env .mcp.json -.claude/ settings.local.json diff --git a/vendor/github.com/go-openapi/errors/CONTRIBUTORS.md b/vendor/github.com/go-openapi/errors/CONTRIBUTORS.md index d49e377a13..68b716b568 100644 --- a/vendor/github.com/go-openapi/errors/CONTRIBUTORS.md +++ b/vendor/github.com/go-openapi/errors/CONTRIBUTORS.md @@ -4,12 +4,12 @@ | Total Contributors | Total Contributions | | --- | --- | -| 13 | 110 | +| 13 | 115 | | Username | All Time Contribution Count | All Commits | | --- | --- | --- | | @casualjim | 58 | | -| @fredbi | 36 | | +| @fredbi | 41 | | | @youyuanwu | 5 | | | @alexandear | 2 | | | @fiorix | 1 | | @@ -22,4 +22,4 @@ | @aokumasan | 1 | | | @ujjwalsh | 1 | | - _this file was generated by the [Contributors GitHub Action](https://github.com/github/contributors)_ + _this file was generated by the [Contributors GitHub Action](https://github.com/github-community-projects/contributors)_ diff --git a/vendor/github.com/go-openapi/errors/README.md b/vendor/github.com/go-openapi/errors/README.md index d9f4a3f151..35e6b69ba8 100644 --- a/vendor/github.com/go-openapi/errors/README.md +++ b/vendor/github.com/go-openapi/errors/README.md @@ -106,7 +106,7 @@ Maintainers can cut a new release by either: [slack-badge]: https://img.shields.io/badge/slack-blue?link=https%3A%2F%2Fgoswagger.slack.com%2Farchives%2FC04R30YM [slack-url]: https://goswagger.slack.com/archives/C04R30YMU [discord-badge]: https://img.shields.io/discord/1446918742398341256?logo=discord&label=discord&color=blue -[discord-url]: https://discord.gg/twZ9BwT3 +[discord-url]: https://discord.gg/FfnFYaC3k5 [license-badge]: http://img.shields.io/badge/license-Apache%20v2-orange.svg diff --git a/vendor/github.com/go-openapi/jsonpointer/.cliff.toml b/vendor/github.com/go-openapi/jsonpointer/.cliff.toml deleted file mode 100644 index 702629f5dc..0000000000 --- a/vendor/github.com/go-openapi/jsonpointer/.cliff.toml +++ /dev/null @@ -1,181 +0,0 @@ -# git-cliff ~ configuration file -# https://git-cliff.org/docs/configuration - -[changelog] -header = """ -""" - -footer = """ - ------ - -**[{{ remote.github.repo }}]({{ self::remote_url() }}) license terms** - -[![License][license-badge]][license-url] - -[license-badge]: http://img.shields.io/badge/license-Apache%20v2-orange.svg -[license-url]: {{ self::remote_url() }}/?tab=Apache-2.0-1-ov-file#readme - -{%- macro remote_url() -%} - https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }} -{%- endmacro -%} -""" - -body = """ -{%- if version %} -## [{{ version | trim_start_matches(pat="v") }}]({{ self::remote_url() }}/tree/{{ version }}) - {{ timestamp | date(format="%Y-%m-%d") }} -{%- else %} -## [unreleased] -{%- endif %} -{%- if message %} - {%- raw %}\n{% endraw %} -{{ message }} - {%- raw %}\n{% endraw %} -{%- endif %} -{%- if version %} - {%- if previous.version %} - -**Full Changelog**: <{{ self::remote_url() }}/compare/{{ previous.version }}...{{ version }}> - {%- endif %} -{%- else %} - {%- raw %}\n{% endraw %} -{%- endif %} - -{%- if statistics %}{% if statistics.commit_count %} - {%- raw %}\n{% endraw %} -{{ statistics.commit_count }} commits in this release. - {%- raw %}\n{% endraw %} -{%- endif %}{% endif %} ------ - -{%- for group, commits in commits | group_by(attribute="group") %} - {%- raw %}\n{% endraw %} -### {{ group | upper_first }} - {%- raw %}\n{% endraw %} - {%- for commit in commits %} - {%- if commit.remote.pr_title %} - {%- set commit_message = commit.remote.pr_title %} - {%- else %} - {%- set commit_message = commit.message %} - {%- endif %} -* {{ commit_message | split(pat="\n") | first | trim }} - {%- if commit.remote.username %} -{%- raw %} {% endraw %}by [@{{ commit.remote.username }}](https://github.com/{{ commit.remote.username }}) - {%- endif %} - {%- if commit.remote.pr_number %} -{%- raw %} {% endraw %}in [#{{ commit.remote.pr_number }}]({{ self::remote_url() }}/pull/{{ commit.remote.pr_number }}) - {%- endif %} -{%- raw %} {% endraw %}[...]({{ self::remote_url() }}/commit/{{ commit.id }}) - {%- endfor %} -{%- endfor %} - -{%- if github %} -{%- raw %}\n{% endraw -%} - {%- set all_contributors = github.contributors | length %} - {%- if github.contributors | filter(attribute="username", value="dependabot[bot]") | length < all_contributors %} ------ - -### People who contributed to this release - {% endif %} - {%- for contributor in github.contributors | filter(attribute="username") | sort(attribute="username") %} - {%- if contributor.username != "dependabot[bot]" and contributor.username != "github-actions[bot]" %} -* [@{{ contributor.username }}](https://github.com/{{ contributor.username }}) - {%- endif %} - {%- endfor %} - - {% if github.contributors | filter(attribute="is_first_time", value=true) | length != 0 %} ------ - {%- raw %}\n{% endraw %} - -### New Contributors - {%- endif %} - - {%- for contributor in github.contributors | filter(attribute="is_first_time", value=true) %} - {%- if contributor.username != "dependabot[bot]" and contributor.username != "github-actions[bot]" %} -* @{{ contributor.username }} made their first contribution - {%- if contributor.pr_number %} - in [#{{ contributor.pr_number }}]({{ self::remote_url() }}/pull/{{ contributor.pr_number }}) \ - {%- endif %} - {%- endif %} - {%- endfor %} -{%- endif %} - -{%- raw %}\n{% endraw %} - -{%- macro remote_url() -%} - https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }} -{%- endmacro -%} -""" -# Remove leading and trailing whitespaces from the changelog's body. -trim = true -# Render body even when there are no releases to process. -render_always = true -# An array of regex based postprocessors to modify the changelog. -postprocessors = [ - # Replace the placeholder with a URL. - #{ pattern = '', replace = "https://github.com/orhun/git-cliff" }, -] -# output file path -# output = "test.md" - -[git] -# Parse commits according to the conventional commits specification. -# See https://www.conventionalcommits.org -conventional_commits = false -# Exclude commits that do not match the conventional commits specification. -filter_unconventional = false -# Require all commits to be conventional. -# Takes precedence over filter_unconventional. -require_conventional = false -# Split commits on newlines, treating each line as an individual commit. -split_commits = false -# An array of regex based parsers to modify commit messages prior to further processing. -commit_preprocessors = [ - # Replace issue numbers with link templates to be updated in `changelog.postprocessors`. - #{ pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](/issues/${2}))"}, - # Check spelling of the commit message using https://github.com/crate-ci/typos. - # If the spelling is incorrect, it will be fixed automatically. - #{ pattern = '.*', replace_command = 'typos --write-changes -' } -] -# Prevent commits that are breaking from being excluded by commit parsers. -protect_breaking_commits = false -# An array of regex based parsers for extracting data from the commit message. -# Assigns commits to groups. -# Optionally sets the commit's scope and can decide to exclude commits from further processing. -commit_parsers = [ - { message = "^[Cc]hore\\([Rr]elease\\): prepare for", skip = true }, - { message = "(^[Mm]erge)|([Mm]erge conflict)", skip = true }, - { field = "author.name", pattern = "dependabot*", group = "Updates" }, - { message = "([Ss]ecurity)|([Vv]uln)", group = "Security" }, - { body = "(.*[Ss]ecurity)|([Vv]uln)", group = "Security" }, - { message = "([Cc]hore\\(lint\\))|(style)|(lint)|(codeql)|(golangci)", group = "Code quality" }, - { message = "(^[Dd]oc)|((?i)readme)|(badge)|(typo)|(documentation)", group = "Documentation" }, - { message = "(^[Ff]eat)|(^[Ee]nhancement)", group = "Implemented enhancements" }, - { message = "(^ci)|(\\(ci\\))|(fixup\\s+ci)|(fix\\s+ci)|(license)|(example)", group = "Miscellaneous tasks" }, - { message = "^test", group = "Testing" }, - { message = "(^fix)|(panic)", group = "Fixed bugs" }, - { message = "(^refact)|(rework)", group = "Refactor" }, - { message = "(^[Pp]erf)|(performance)", group = "Performance" }, - { message = "(^[Cc]hore)", group = "Miscellaneous tasks" }, - { message = "^[Rr]evert", group = "Reverted changes" }, - { message = "(upgrade.*?go)|(go\\s+version)", group = "Updates" }, - { message = ".*", group = "Other" }, -] -# Exclude commits that are not matched by any commit parser. -filter_commits = false -# An array of link parsers for extracting external references, and turning them into URLs, using regex. -link_parsers = [] -# Include only the tags that belong to the current branch. -use_branch_tags = false -# Order releases topologically instead of chronologically. -topo_order = false -# Order releases topologically instead of chronologically. -topo_order_commits = true -# Order of commits in each group/release within the changelog. -# Allowed values: newest, oldest -sort_commits = "newest" -# Process submodules commits -recurse_submodules = false - -#[remote.github] -#owner = "go-openapi" diff --git a/vendor/github.com/go-openapi/jsonpointer/.gitignore b/vendor/github.com/go-openapi/jsonpointer/.gitignore index 885dc27ab0..d8f4186fe5 100644 --- a/vendor/github.com/go-openapi/jsonpointer/.gitignore +++ b/vendor/github.com/go-openapi/jsonpointer/.gitignore @@ -3,4 +3,3 @@ .idea .env .mcp.json -.claude/ diff --git a/vendor/github.com/go-openapi/jsonpointer/.golangci.yml b/vendor/github.com/go-openapi/jsonpointer/.golangci.yml index dc7c96053d..9d2733176e 100644 --- a/vendor/github.com/go-openapi/jsonpointer/.golangci.yml +++ b/vendor/github.com/go-openapi/jsonpointer/.golangci.yml @@ -4,7 +4,10 @@ linters: disable: - depguard - funlen + - goconst - godox + - gomodguard + - gomodguard_v2 - exhaustruct - nlreturn - nonamedreturns diff --git a/vendor/github.com/go-openapi/jsonpointer/CONTRIBUTORS.md b/vendor/github.com/go-openapi/jsonpointer/CONTRIBUTORS.md index 2ebebedc15..de0bf5c04b 100644 --- a/vendor/github.com/go-openapi/jsonpointer/CONTRIBUTORS.md +++ b/vendor/github.com/go-openapi/jsonpointer/CONTRIBUTORS.md @@ -4,14 +4,15 @@ | Total Contributors | Total Contributions | | --- | --- | -| 12 | 101 | +| 13 | 132 | | Username | All Time Contribution Count | All Commits | | --- | --- | --- | -| @fredbi | 54 | | +| @fredbi | 83 | | | @casualjim | 33 | | | @magodo | 3 | | | @youyuanwu | 3 | | +| @alexandear | 2 | | | @gaiaz-iusipov | 1 | | | @gbjk | 1 | | | @gordallott | 1 | | @@ -21,4 +22,4 @@ | @olivierlemasle | 1 | | | @testwill | 1 | | - _this file was generated by the [Contributors GitHub Action](https://github.com/github/contributors)_ + _this file was generated by the [Contributors GitHub Action](https://github.com/github-community-projects/contributors)_ diff --git a/vendor/github.com/go-openapi/jsonpointer/NOTICE b/vendor/github.com/go-openapi/jsonpointer/NOTICE index f3b51939a9..201908d2f0 100644 --- a/vendor/github.com/go-openapi/jsonpointer/NOTICE +++ b/vendor/github.com/go-openapi/jsonpointer/NOTICE @@ -18,7 +18,7 @@ It ships with copies of other software which license terms are recalled below. The original software was authored on 25-02-2013 by sigu-399 (https://github.com/sigu-399, sigu.399@gmail.com). -github.com/sigh-399/jsonpointer +github.com/sigu-399/jsonpointer =========================== // SPDX-FileCopyrightText: Copyright 2013 sigu-399 ( https://github.com/sigu-399 ) diff --git a/vendor/github.com/go-openapi/jsonpointer/README.md b/vendor/github.com/go-openapi/jsonpointer/README.md index c52803e2e8..6e7929c64b 100644 --- a/vendor/github.com/go-openapi/jsonpointer/README.md +++ b/vendor/github.com/go-openapi/jsonpointer/README.md @@ -16,17 +16,34 @@ An implementation of JSON Pointer for golang, which supports go `struct`. ## Announcements -* **2025-12-19** : new community chat on discord - * a new discord community channel is available to be notified of changes and support users - * our venerable Slack channel remains open, and will be eventually discontinued on **2026-03-31** - -You may join the discord community by clicking the invite link on the discord badge (also above). [![Discord Channel][discord-badge]][discord-url] - -Or join our Slack channel: [![Slack Channel][slack-logo]![slack-badge]][slack-url] +* **2026-07-07** : landing v1.0.0 + * stable API pledge + +* **2026-06-29** : reinsourced external dependency to swag (v0.24.0) + * module `github.com/go-openapi/swag/jsonname` is source directly here, so we no longer have any external dependency + * `jsonname` was never really used by any other package, so it makes sense to deprecate it away from the `swag` family + and retrofit its functionality here. `jsonpointer` no longer get external dependencies, besides test dependencies. + +* **2026-04-15** : added support for trailing "-" for arrays (v0.23.0) + * this brings full support of [RFC6901][RFC6901] + * this is supported for types relying on the reflection-based implemented + * API semantics remain essentially unaltered. Exception: `Pointer.Set(document any,value any) (document any, err error)` + can only perform a best-effort to mutate the input document in place. In the case of adding elements to an array with a + trailing "-", either pass a mutable array (`*[]T`) as the input document, or use the returned updated document instead. + * types that implement the `JSONSetable` interface may not implement the mutation implied by the trailing "-" + +* **2026-04-15** : added support for optional alternate JSON name providers + * for struct support the defaults might not suit all situations: there are known limitations + when it comes to handle untagged fields or embedded types. + * the default name provider in use is not fully aligned with go JSON stdlib + * exposed an option (or global setting) to change the provider that resolves a struct into json keys + * the default behavior is not altered ## Status -API is stable. +API is stable and feature-complete. + +The project continues to receive regular updates, bug fixes and hygiene maintenance (CI, linting, etc). ## Import this library in your project @@ -88,7 +105,7 @@ See -also known as [RFC6901](https://www.rfc-editor.org/rfc/rfc6901) +also known as [RFC6901][RFC6901]. ## Licensing @@ -99,19 +116,19 @@ on top of which it has been built. ## Limitations -The 4.Evaluation part of the previous reference, starting with 'If the currently referenced value is a JSON array, -the reference token MUST contain either...' is not implemented. - -That is because our implementation of the JSON pointer only supports explicit references to array elements: -the provision in the spec to resolve non-existent members as "the last element in the array", -using the special trailing character "-" is not implemented. +* [RFC6901][RFC6901] is now fully supported, including trailing "-" semantics for arrays (for `Set` operations). +* Default behavior: JSON name detection in go `struct`s + - Unlike go standard marshaling, untagged fields do not default to the go field name and are ignored. + - anonymous fields are not traversed if untagged + - the above limitations may be overcome by calling `UseGoNameProvider()` at initialization time. + - alternatively, users may inject the desired custom behavior for naming fields as an option. ## Other documentation * [All-time contributors](./CONTRIBUTORS.md) -* [Contributing guidelines](.github/CONTRIBUTING.md) -* [Maintainers documentation](docs/MAINTAINERS.md) -* [Code style](docs/STYLE.md) +* [Contributing guidelines][contributing-doc-site] +* [Maintainers documentation][maintainers-doc-site] +* [Code style][style-doc-site] ## Cutting a new release @@ -142,11 +159,8 @@ Maintainers can cut a new release by either: [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/jsonpointer [godoc-url]: http://pkg.go.dev/github.com/go-openapi/jsonpointer -[slack-logo]: https://a.slack-edge.com/e6a93c1/img/icons/favicon-32.png -[slack-badge]: https://img.shields.io/badge/slack-blue?link=https%3A%2F%2Fgoswagger.slack.com%2Farchives%2FC04R30YM -[slack-url]: https://goswagger.slack.com/archives/C04R30YMU [discord-badge]: https://img.shields.io/discord/1446918742398341256?logo=discord&label=discord&color=blue -[discord-url]: https://discord.gg/twZ9BwT3 +[discord-url]: https://discord.gg/FfnFYaC3k5 [license-badge]: http://img.shields.io/badge/license-Apache%20v2-orange.svg @@ -156,3 +170,8 @@ Maintainers can cut a new release by either: [goversion-url]: https://github.com/go-openapi/jsonpointer/blob/master/go.mod [top-badge]: https://img.shields.io/github/languages/top/go-openapi/jsonpointer [commits-badge]: https://img.shields.io/github/commits-since/go-openapi/jsonpointer/latest +[RFC6901]: https://www.rfc-editor.org/rfc/rfc6901 + +[contributing-doc-site]: https://go-openapi.github.io/doc-site/contributing/contributing/index.html +[maintainers-doc-site]: https://go-openapi.github.io/doc-site/maintainers/index.html +[style-doc-site]: https://go-openapi.github.io/doc-site/contributing/style/index.html diff --git a/vendor/github.com/go-openapi/jsonpointer/errors.go b/vendor/github.com/go-openapi/jsonpointer/errors.go index 8c50dde8bc..2ae6e3cfb3 100644 --- a/vendor/github.com/go-openapi/jsonpointer/errors.go +++ b/vendor/github.com/go-openapi/jsonpointer/errors.go @@ -16,12 +16,25 @@ const ( ErrPointer pointerError = "JSON pointer error" // ErrInvalidStart states that a JSON pointer must start with a separator ("/"). - ErrInvalidStart pointerError = `JSON pointer must be empty or start with a "` + pointerSeparator + ErrInvalidStart pointerError = `JSON pointer must be empty or start with a "` + pointerSeparator + `"` // ErrUnsupportedValueType indicates that a value of the wrong type is being set. ErrUnsupportedValueType pointerError = "only structs, pointers, maps and slices are supported for setting values" + + // ErrDashToken indicates use of the RFC 6901 "-" reference token in a context where it cannot be + // resolved. + // + // Per RFC 6901 §4 the "-" token refers to the (nonexistent) element after the last array element. + // It may only be used as the terminal token of a [Pointer.Set] against a slice, where it means + // "append". + // + // Any other use (get, offset, intermediate traversal, non-slice target) is an error condition that + // wraps this sentinel. + ErrDashToken pointerError = `the "-" array token cannot be resolved here` //nolint:gosec // G101 false positive: this is a JSON Pointer reference token, not a credential. ) +const dashToken = "-" + func errNoKey(key string) error { return fmt.Errorf("object has no key %q: %w", key, ErrPointer) } @@ -33,3 +46,15 @@ func errOutOfBounds(length, idx int) error { func errInvalidReference(token string) error { return fmt.Errorf("invalid token reference %q: %w", token, ErrPointer) } + +func errDashOnGet() error { + return fmt.Errorf("cannot resolve %q token on get: %w: %w", dashToken, ErrDashToken, ErrPointer) +} + +func errDashIntermediate() error { + return fmt.Errorf("the %q token may only appear as the terminal token of a pointer: %w: %w", dashToken, ErrDashToken, ErrPointer) +} + +func errDashOnOffset() error { + return fmt.Errorf("cannot compute offset for %q token (nonexistent element): %w: %w", dashToken, ErrDashToken, ErrPointer) +} diff --git a/vendor/github.com/go-openapi/jsonpointer/ifaces.go b/vendor/github.com/go-openapi/jsonpointer/ifaces.go new file mode 100644 index 0000000000..31359c48fa --- /dev/null +++ b/vendor/github.com/go-openapi/jsonpointer/ifaces.go @@ -0,0 +1,50 @@ +// SPDX-FileCopyrightText: Copyright (c) 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package jsonpointer + +import "reflect" + +// JSONPointable is an interface for structs to implement, when they need to customize the json +// pointer process or want to avoid the use of reflection. +type JSONPointable interface { + // JSONLookup returns a value pointed at this (unescaped) key. + JSONLookup(key string) (any, error) +} + +// JSONSetable is an interface for structs to implement, when they need to customize the json +// pointer process or want to avoid the use of reflection. +// +// # Handling of the RFC 6901 "-" token +// +// When a type implementing JSONSetable is the terminal parent of a [Pointer.Set] call, the library +// passes the raw reference token to JSONSet without interpretation. +// +// In particular, the RFC 6901 "-" token (which conventionally means "append" for arrays, per RFC +// 6902) is forwarded verbatim as the key argument. +// +// Implementations that model an array-like container are expected to give "-" the append semantics; +// implementations that do not should return an error wrapping [ErrDashToken] (or [ErrPointer]) for +// clarity. +// +// Implementations are responsible for any in-place mutation: the library does not attempt to rebind +// the result of JSONSet into a parent container. +type JSONSetable interface { + // JSONSet sets the value pointed at the (unescaped) key. + // + // The key may be the RFC 6901 "-" token when the pointer targets a slice-like member; see the + // interface documentation for details. + JSONSet(key string, value any) error +} + +// NameProvider knows how to resolve go struct fields into json names. +// +// The default provider is brought by +// [github.com/go-openapi/jsonpointer/jsonname.DefaultJSONNameProvider]. +type NameProvider interface { + // GetGoName gets the go name for a json property name + GetGoName(subject any, name string) (string, bool) + + // GetGoNameForType gets the go name for a given type for a json property name + GetGoNameForType(tpe reflect.Type, name string) (string, bool) +} diff --git a/vendor/github.com/go-openapi/swag/jsonname/doc.go b/vendor/github.com/go-openapi/jsonpointer/jsonname/doc.go similarity index 100% rename from vendor/github.com/go-openapi/swag/jsonname/doc.go rename to vendor/github.com/go-openapi/jsonpointer/jsonname/doc.go diff --git a/vendor/github.com/go-openapi/swag/jsonname/go_name_provider.go b/vendor/github.com/go-openapi/jsonpointer/jsonname/go_name_provider.go similarity index 88% rename from vendor/github.com/go-openapi/swag/jsonname/go_name_provider.go rename to vendor/github.com/go-openapi/jsonpointer/jsonname/go_name_provider.go index adc4426873..5eec18fbfd 100644 --- a/vendor/github.com/go-openapi/swag/jsonname/go_name_provider.go +++ b/vendor/github.com/go-openapi/jsonpointer/jsonname/go_name_provider.go @@ -11,11 +11,11 @@ import ( var _ providerIface = (*GoNameProvider)(nil) -// GoNameProvider resolves json property names to go struct field names following -// the same rules as the standard library's [encoding/json] package. +// GoNameProvider resolves json property names to go struct field names following the same rules as +// the standard library's [encoding/json] package. // -// Contrary to [NameProvider], it considers exported fields without a json tag, -// and promotes fields from anonymous embedded struct types. +// Contrary to [NameProvider], it considers exported fields without a json tag, and promotes fields +// from anonymous embedded struct types. // // Rules (aligned with encoding/json): // @@ -104,9 +104,9 @@ func (n *GoNameProvider) nameIndexFor(tpe reflect.Type) nameIndex { return names } -// fieldEntry captures a candidate field discovered while walking a struct -// along with the indirection path from the root type (used to resolve conflicts -// by depth in the same way encoding/json does). +// fieldEntry captures a candidate field discovered while walking a struct along with the +// indirection path from the root type (used to resolve conflicts by depth in the same way +// encoding/json does). type fieldEntry struct { goName string jsonName string @@ -129,6 +129,8 @@ func buildGoNameIndex(tpe reflect.Type) nameIndex { // collectGoFields walks tpe breadth-first along anonymous struct fields, // reproducing the field selection performed by encoding/json.typeFields. +// +//nolint:gocognit // everything is inlined to help the compiler determine what escapes and what doesn't func collectGoFields(tpe reflect.Type) []fieldEntry { if tpe.Kind() != reflect.Struct { return nil @@ -157,12 +159,12 @@ func collectGoFields(tpe reflect.Type) []fieldEntry { } for _, q := range current { - for i := 0; i < q.typ.NumField(); i++ { + for i := range q.typ.NumField() { sf := q.typ.Field(i) if sf.Anonymous { ft := sf.Type - if ft.Kind() == reflect.Ptr { + if ft.Kind() == reflect.Pointer { ft = ft.Elem() } if !sf.IsExported() && ft.Kind() != reflect.Struct { @@ -180,7 +182,7 @@ func collectGoFields(tpe reflect.Type) []fieldEntry { tagged := jsonName != "" ft := sf.Type - if ft.Kind() == reflect.Ptr { + if ft.Kind() == reflect.Pointer { ft = ft.Elem() } @@ -221,9 +223,9 @@ func collectGoFields(tpe reflect.Type) []fieldEntry { return dominantFields(candidates) } -// dominantFields applies the Go encoding/json conflict resolution rules: -// at each JSON name, the shallowest field wins; at equal depth, a uniquely -// tagged candidate wins; otherwise all candidates for that name are dropped. +// dominantFields applies the Go encoding/json conflict resolution rules: at each JSON name, the +// shallowest field wins; at equal depth, a uniquely tagged candidate wins; otherwise all candidates +// for that name are dropped. func dominantFields(candidates []fieldEntry) []fieldEntry { byName := make(map[string][]fieldEntry, len(candidates)) for _, c := range candidates { @@ -272,14 +274,14 @@ func dominantFields(candidates []fieldEntry) []fieldEntry { return out } -// parseJSONTag returns the name component of a json struct tag and whether -// it carried any non-name option (kept for future-proofing, e.g. "omitempty"). +// parseJSONTag returns the name component of a json struct tag and whether it carried any non-name +// option (kept for future-proofing, e.g. "omitempty"). func parseJSONTag(tag string) (string, string) { if tag == "" { return "", "" } - if idx := strings.IndexByte(tag, ','); idx >= 0 { - return tag[:idx], tag[idx+1:] + if before, after, ok := strings.Cut(tag, ","); ok { + return before, after } return tag, "" diff --git a/vendor/github.com/go-openapi/swag/jsonname/ifaces.go b/vendor/github.com/go-openapi/jsonpointer/jsonname/ifaces.go similarity index 77% rename from vendor/github.com/go-openapi/swag/jsonname/ifaces.go rename to vendor/github.com/go-openapi/jsonpointer/jsonname/ifaces.go index 812ace5639..64871f0d27 100644 --- a/vendor/github.com/go-openapi/swag/jsonname/ifaces.go +++ b/vendor/github.com/go-openapi/jsonpointer/jsonname/ifaces.go @@ -5,9 +5,11 @@ package jsonname import "reflect" -// providerIface is an unexported compile-time contract that every name provider -// in this package is expected to satisfy. -// It mirrors the interface declared by the main consumer of this module: [github.com/go-openapi/jsonpointer.NameProvider]. +// providerIface is an unexported compile-time contract that every name provider in this package is +// expected to satisfy. +// +// It mirrors the interface declared by the main consumer of this module: +// [github.com/go-openapi/jsonpointer.NameProvider]. type providerIface interface { GetGoName(subject any, name string) (string, bool) GetGoNameForType(tpe reflect.Type, name string) (string, bool) diff --git a/vendor/github.com/go-openapi/swag/jsonname/name_provider.go b/vendor/github.com/go-openapi/jsonpointer/jsonname/name_provider.go similarity index 83% rename from vendor/github.com/go-openapi/swag/jsonname/name_provider.go rename to vendor/github.com/go-openapi/jsonpointer/jsonname/name_provider.go index 9f5da7a016..1bec2406b5 100644 --- a/vendor/github.com/go-openapi/swag/jsonname/name_provider.go +++ b/vendor/github.com/go-openapi/jsonpointer/jsonname/name_provider.go @@ -10,12 +10,12 @@ import ( ) // DefaultJSONNameProvider is the default cache for types. -var DefaultJSONNameProvider = NewNameProvider() +var DefaultJSONNameProvider = NewNameProvider() //nolint:gochecknoglobals // default settings, for backward compatible package-level settings var _ providerIface = (*NameProvider)(nil) -// NameProvider represents an object capable of translating from go property names -// to json property names. +// NameProvider represents an object capable of translating from go property names to json property +// names. // // This type is thread-safe. // @@ -30,7 +30,7 @@ type nameIndex struct { goNames map[string]string } -// NewNameProvider creates a new name provider +// NewNameProvider creates a new name provider. func NewNameProvider() *NameProvider { return &NameProvider{ lock: &sync.Mutex{}, @@ -39,7 +39,7 @@ func NewNameProvider() *NameProvider { } func buildnameIndex(tpe reflect.Type, idx, reverseIdx map[string]string) { - for i := 0; i < tpe.NumField(); i++ { + for i := range tpe.NumField() { targetDes := tpe.Field(i) if targetDes.PkgPath != "" { // unexported @@ -73,14 +73,14 @@ func buildnameIndex(tpe reflect.Type, idx, reverseIdx map[string]string) { } func newNameIndex(tpe reflect.Type) nameIndex { - var idx = make(map[string]string, tpe.NumField()) - var reverseIdx = make(map[string]string, tpe.NumField()) + idx := make(map[string]string, tpe.NumField()) + reverseIdx := make(map[string]string, tpe.NumField()) buildnameIndex(tpe, idx, reverseIdx) return nameIndex{jsonNames: idx, goNames: reverseIdx} } -// GetJSONNames gets all the json property names for a type +// GetJSONNames gets all the json property names for a type. func (n *NameProvider) GetJSONNames(subject any) []string { n.lock.Lock() defer n.lock.Unlock() @@ -97,13 +97,13 @@ func (n *NameProvider) GetJSONNames(subject any) []string { return res } -// GetJSONName gets the json name for a go property name +// GetJSONName gets the json name for a go property name. func (n *NameProvider) GetJSONName(subject any, name string) (string, bool) { tpe := reflect.Indirect(reflect.ValueOf(subject)).Type() return n.GetJSONNameForType(tpe, name) } -// GetJSONNameForType gets the json name for a go property name on a given type +// GetJSONNameForType gets the json name for a go property name on a given type. func (n *NameProvider) GetJSONNameForType(tpe reflect.Type, name string) (string, bool) { n.lock.Lock() defer n.lock.Unlock() @@ -115,13 +115,13 @@ func (n *NameProvider) GetJSONNameForType(tpe reflect.Type, name string) (string return nme, ok } -// GetGoName gets the go name for a json property name +// GetGoName gets the go name for a json property name. func (n *NameProvider) GetGoName(subject any, name string) (string, bool) { tpe := reflect.Indirect(reflect.ValueOf(subject)).Type() return n.GetGoNameForType(tpe, name) } -// GetGoNameForType gets the go name for a given type for a json property name +// GetGoNameForType gets the go name for a given type for a json property name. func (n *NameProvider) GetGoNameForType(tpe reflect.Type, name string) (string, bool) { n.lock.Lock() defer n.lock.Unlock() diff --git a/vendor/github.com/go-openapi/jsonpointer/options.go b/vendor/github.com/go-openapi/jsonpointer/options.go new file mode 100644 index 0000000000..223c1e5ff0 --- /dev/null +++ b/vendor/github.com/go-openapi/jsonpointer/options.go @@ -0,0 +1,85 @@ +// SPDX-FileCopyrightText: Copyright (c) 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package jsonpointer + +import ( + "sync" + + "github.com/go-openapi/jsonpointer/jsonname" +) + +// Option to tune the behavior of a JSON [Pointer]. +type Option func(*options) + +var ( + //nolint:gochecknoglobals // package level defaults are provided as a convenient, backward-compatible way to adopt options. + defaultOptions = options{ + provider: jsonname.DefaultJSONNameProvider, + } + //nolint:gochecknoglobals // guards defaultOptions against concurrent SetDefaultNameProvider / read races (testing) + defaultOptionsMu sync.RWMutex +) + +// SetDefaultNameProvider sets the [NameProvider] as a package-level default. +// +// By default, the default provider is [jsonname.DefaultJSONNameProvider]. +// +// It is safe to call concurrently with [Pointer.Get], [Pointer.Set], [GetForToken] and +// [SetForToken]. +// The typical usage is to call it once at initialization time. +// +// A nil provider is ignored. +func SetDefaultNameProvider(provider NameProvider) { + if provider == nil { + return + } + + defaultOptionsMu.Lock() + defer defaultOptionsMu.Unlock() + + defaultOptions.provider = provider +} + +// UseGoNameProvider sets the [NameProvider] as a package-level default to the alternative provider +// [jsonname.GoNameProvider], that covers a few areas not supported by the default name provider. +// +// This implementation supports untagged exported fields and embedded types in go struct. +// It follows strictly the behavior of the JSON standard library regarding field naming conventions. +// +// It is safe to call concurrently with [Pointer.Get], [Pointer.Set], [GetForToken] and +// [SetForToken]. +// The typical usage is to call it once at initialization time. +func UseGoNameProvider() { + SetDefaultNameProvider(jsonname.NewGoNameProvider()) +} + +// DefaultNameProvider returns the current package-level [NameProvider]. +func DefaultNameProvider() NameProvider { //nolint:ireturn // returning the interface is the point — callers pick their own implementation. + defaultOptionsMu.RLock() + defer defaultOptionsMu.RUnlock() + + return defaultOptions.provider +} + +// WithNameProvider injects a custom [NameProvider] to resolve json names from go struct types. +func WithNameProvider(provider NameProvider) Option { + return func(o *options) { + o.provider = provider + } +} + +type options struct { + provider NameProvider +} + +func optionsWithDefaults(opts []Option) options { + var o options + o.provider = DefaultNameProvider() + + for _, apply := range opts { + apply(&o) + } + + return o +} diff --git a/vendor/github.com/go-openapi/jsonpointer/pointer.go b/vendor/github.com/go-openapi/jsonpointer/pointer.go index 7df49af3b9..05fc863ee6 100644 --- a/vendor/github.com/go-openapi/jsonpointer/pointer.go +++ b/vendor/github.com/go-openapi/jsonpointer/pointer.go @@ -11,8 +11,6 @@ import ( "reflect" "strconv" "strings" - - "github.com/go-openapi/swag/jsonname" ) const ( @@ -20,20 +18,6 @@ const ( pointerSeparator = `/` ) -// JSONPointable is an interface for structs to implement, -// when they need to customize the json pointer process or want to avoid the use of reflection. -type JSONPointable interface { - // JSONLookup returns a value pointed at this (unescaped) key. - JSONLookup(key string) (any, error) -} - -// JSONSetable is an interface for structs to implement, -// when they need to customize the json pointer process or want to avoid the use of reflection. -type JSONSetable interface { - // JSONSet sets the value pointed at the (unescaped) key. - JSONSet(key string, value any) error -} - // Pointer is a representation of a json pointer. // // Use [Pointer.Get] to retrieve a value or [Pointer.Set] to set a value. @@ -41,7 +25,7 @@ type JSONSetable interface { // It works with any go type interpreted as a JSON document, which means: // // - if a type implements [JSONPointable], its [JSONPointable.JSONLookup] method is used to resolve [Pointer.Get] -// - if a type implements [JSONSetable], its [JSONPointable.JSONSet] method is used to resolve [Pointer.Set] +// - if a type implements [JSONSetable], its [JSONSetable.JSONSet] method is used to resolve [Pointer.Set] // - a go map[K]V is interpreted as an object, with type K assignable to a string // - a go slice []T is interpreted as an array // - a go struct is interpreted as an object, with exported fields interpreted as keys @@ -50,7 +34,8 @@ type JSONSetable interface { // // For struct s resolved by reflection, key mappings honor the conventional struct tag `json`. // -// Fields that do not specify a `json` tag, or specify an empty one, or are tagged as `json:"-"` are ignored. +// Fields that do not specify a `json` tag, or specify an empty one, or are tagged as `json:"-"` are +// ignored. // // # Limitations // @@ -71,16 +56,36 @@ func New(jsonPointerString string) (Pointer, error) { // Get uses the pointer to retrieve a value from a JSON document. // // It returns the value with its type as a [reflect.Kind] or an error. -func (p *Pointer) Get(document any) (any, reflect.Kind, error) { - return p.get(document, jsonname.DefaultJSONNameProvider) +func (p *Pointer) Get(document any, opts ...Option) (any, reflect.Kind, error) { + o := optionsWithDefaults(opts) + + return p.get(document, o.provider) } -// Set uses the pointer to set a value from a data type -// that represent a JSON document. +// Set uses the pointer to set a value from a data type that represent a JSON document. +// +// # Mutation contract // -// It returns the updated document. -func (p *Pointer) Set(document any, value any) (any, error) { - return document, p.set(document, value, jsonname.DefaultJSONNameProvider) +// Set mutates the provided document in place whenever Go's type system allows it: when document is +// a map, a pointer, or when the targeted value is reached through an addressable ancestor (e.g. a +// struct field traversed via a pointer, a slice element). +// +// Callers that rely on this in-place behavior may continue to ignore the returned document. +// +// The returned document is only load-bearing when Set cannot mutate in place. +// +// This happens in one specific case: appending to a top-level slice passed by value (e.g. document +// of type []T rather than *[]T) via the RFC 6901 "-" terminal token. reflect.Append produces a new +// slice header that the library cannot rebind into the caller's variable; the updated document is +// returned instead. +// +// Pass *[]T if you want in-place rebind for that case as well. +// +// See [ErrDashToken] for the semantics of the "-" token. +func (p *Pointer) Set(document any, value any, opts ...Option) (any, error) { + o := optionsWithDefaults(opts) + + return p.set(document, value, o.provider) } // DecodedTokens returns the decoded (unescaped) tokens of this JSON pointer. @@ -109,6 +114,46 @@ func (p *Pointer) String() string { return pointerSeparator + strings.Join(p.referenceTokens, pointerSeparator) } +// Offset returns the byte offset, in the raw JSON text of document, of the location referenced by +// this pointer's terminal token. +// +// Unlike [Pointer.Get] and [Pointer.Set], which operate on a decoded Go value, Offset operates +// directly on the textual JSON source. +// +// It drives an [encoding/json.Decoder] over the string and stops at the terminal token, returning +// the position at which the decoder was about to read that token. +// +// It is primarily intended for tooling that needs to map a pointer back to a region of the original +// source: reporting line/column for validation or parse diagnostics, extracting a sub-document by +// slicing the raw bytes, or highlighting the referenced span in an editor. +// +// # Offset semantics +// +// The meaning of the returned offset depends on whether the terminal token addresses an object +// property or an array element: +// +// - Object property: the offset points to the first byte of the key (its +// opening quote character), not to the associated value. For example, +// pointer "/foo/bar" against {"foo": {"bar": 21}} returns 9, the index of +// the opening quote of "bar". +// - Array element: the offset points to the first byte of the value at that +// index. For example, pointer "/0/1" against [[1,2], [3,4]] returns 4, +// the index of the digit 2. +// +// # Errors +// +// Offset returns an error in any of these cases: +// +// - document is not syntactically valid JSON; +// - the structure of document does not match the pointer (e.g. traversing +// into a scalar, or a token that is neither a valid key nor a valid +// numeric index); +// - a referenced key or index does not exist in document; +// - the pointer's terminal token is the RFC 6901 "-" array token, which +// designates a nonexistent element and therefore has no offset in the +// source. The returned error wraps [ErrDashToken]. +// +// All errors wrap [ErrPointer]. func (p *Pointer) Offset(document string) (int64, error) { dec := json.NewDecoder(strings.NewReader(document)) var offset int64 @@ -137,7 +182,34 @@ func (p *Pointer) Offset(document string) (int64, error) { return 0, fmt.Errorf("invalid token %#v: %w", tk, ErrPointer) } } - return offset, nil + return skipJSONSeparator(document, offset), nil +} + +// skipJSONSeparator advances offset past trailing JSON whitespace and at most one value separator +// (comma) in document, so the result points at the first byte of the next JSON token. +// +// The streaming decoder's InputOffset sits right after the most recently consumed token, which +// between values is the comma (or whitespace) — not the following token. +// +// Normalizing here keeps Offset's contract uniform: for both object keys and array elements, and +// regardless of position within the parent container, the returned offset always points at the +// first byte of the addressed token. +func skipJSONSeparator(document string, offset int64) int64 { + n := int64(len(document)) + for offset < n && isJSONWhitespace(document[offset]) { + offset++ + } + if offset < n && document[offset] == ',' { + offset++ + } + for offset < n && isJSONWhitespace(document[offset]) { + offset++ + } + return offset +} + +func isJSONWhitespace(c byte) bool { + return c == ' ' || c == '\t' || c == '\n' || c == '\r' } // "Constructor", parses the given string JSON pointer. @@ -157,9 +229,9 @@ func (p *Pointer) parse(jsonPointerString string) error { return nil } -func (p *Pointer) get(node any, nameProvider *jsonname.NameProvider) (any, reflect.Kind, error) { +func (p *Pointer) get(node any, nameProvider NameProvider) (any, reflect.Kind, error) { if nameProvider == nil { - nameProvider = jsonname.DefaultJSONNameProvider + nameProvider = defaultOptions.provider } kind := reflect.Invalid @@ -185,50 +257,128 @@ func (p *Pointer) get(node any, nameProvider *jsonname.NameProvider) (any, refle return node, kind, nil } -func (p *Pointer) set(node, data any, nameProvider *jsonname.NameProvider) error { +func (p *Pointer) set(node, data any, nameProvider NameProvider) (any, error) { knd := reflect.ValueOf(node).Kind() if knd != reflect.Pointer && knd != reflect.Struct && knd != reflect.Map && knd != reflect.Slice && knd != reflect.Array { - return errors.Join( + return node, errors.Join( fmt.Errorf("unexpected type: %T", node), //nolint:err113 // err wrapping is carried out by errors.Join, not fmt.Errorf. ErrUnsupportedValueType, ErrPointer, ) } - l := len(p.referenceTokens) - // full document when empty - if l == 0 { - return nil + if len(p.referenceTokens) == 0 { + return node, nil } if nameProvider == nil { - nameProvider = jsonname.DefaultJSONNameProvider + nameProvider = defaultOptions.provider } - var decodedToken string - lastIndex := l - 1 + return p.setAt(node, p.referenceTokens, data, nameProvider) +} - if lastIndex > 0 { // skip if we only have one token in pointer - for _, token := range p.referenceTokens[:lastIndex] { - decodedToken = Unescape(token) - next, err := p.resolveNodeForToken(node, decodedToken, nameProvider) - if err != nil { - return err - } +// setAt recursively walks the token list, setting the data at the terminal token and rebinding any +// new child reference (e.g. a slice header returned by an "-" append) into its parent on the way +// back up. +// +// Returning the (possibly new) node at each level is what makes append work at any depth without +// requiring the caller to pass a pointer to the containing slice: the new slice header propagates +// up and each parent rebinds it via the appropriate kind-specific setter. +func (p *Pointer) setAt(node any, tokens []string, data any, nameProvider NameProvider) (any, error) { + decodedToken := Unescape(tokens[0]) + + if len(tokens) == 1 { + return setSingleImpl(node, data, decodedToken, nameProvider) + } - node = next - } + child, err := p.resolveNodeForToken(node, decodedToken, nameProvider) + if err != nil { + return node, err } - // last token - decodedToken = Unescape(p.referenceTokens[lastIndex]) + newChild, err := p.setAt(child, tokens[1:], data, nameProvider) + if err != nil { + return node, err + } - return setSingleImpl(node, data, decodedToken, nameProvider) + return rebindChild(node, decodedToken, newChild, nameProvider) } -func (p *Pointer) resolveNodeForToken(node any, decodedToken string, nameProvider *jsonname.NameProvider) (next any, err error) { +// rebindChild writes newChild back into node at decodedToken. +// +// For cases where the child was already mutated in place (pointer aliasing, addressable slice +// elements) the rebind is a safe no-op. +// +// For cases where the child was returned by value (map entries holding a slice, slices reached +// through a non-addressable ancestor), the rebind propagates the new value into the parent. +// +// Parents implementing [JSONPointable] are left alone: they took ownership of the child via +// JSONLookup and did not opt into a JSONSet-based rebind on intermediate tokens. +func rebindChild(node any, decodedToken string, newChild any, nameProvider NameProvider) (any, error) { + if _, ok := node.(JSONPointable); ok { + return node, nil + } + + rValue := reflect.Indirect(reflect.ValueOf(node)) + + switch rValue.Kind() { + case reflect.Struct: + nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken) + if !ok { + return node, fmt.Errorf("object has no field %q: %w", decodedToken, ErrPointer) + } + fld := rValue.FieldByName(nm) + if !fld.CanSet() { + return node, nil + } + assignReflectValue(fld, newChild) + return node, nil + + case reflect.Map: + rValue.SetMapIndex(reflect.ValueOf(decodedToken), reflect.ValueOf(newChild)) + return node, nil + + case reflect.Slice: + if decodedToken == dashToken { + return node, errDashIntermediate() + } + idx, err := strconv.Atoi(decodedToken) + if err != nil { + return node, errors.Join(err, ErrPointer) + } + elem := rValue.Index(idx) + if !elem.CanSet() { + return node, nil + } + assignReflectValue(elem, newChild) + return node, nil + + default: + return node, errInvalidReference(decodedToken) + } +} + +// assignReflectValue assigns src into dst, unwrapping a pointer when dst expects the pointee type. +// +// This tolerates the pointer-wrapping performed by [typeFromValue] for addressable fields. +func assignReflectValue(dst reflect.Value, src any) { + nv := reflect.ValueOf(src) + if !nv.IsValid() { + return + } + if nv.Type().AssignableTo(dst.Type()) { + dst.Set(nv) + return + } + if nv.Kind() == reflect.Pointer && nv.Elem().Type().AssignableTo(dst.Type()) { + dst.Set(nv.Elem()) + } +} + +func (p *Pointer) resolveNodeForToken(node any, decodedToken string, nameProvider NameProvider) (next any, err error) { // check for nil during traversal if isNil(node) { return nil, fmt.Errorf("cannot traverse through nil value at %q: %w", decodedToken, ErrPointer) @@ -272,6 +422,9 @@ func (p *Pointer) resolveNodeForToken(node any, decodedToken string, nameProvide return typeFromValue(mv), nil case reflect.Slice: + if decodedToken == dashToken { + return nil, errDashIntermediate() + } tokenIndex, err := strconv.Atoi(decodedToken) if err != nil { return nil, errors.Join(err, ErrPointer) @@ -312,16 +465,23 @@ func typeFromValue(v reflect.Value) any { } // GetForToken gets a value for a json pointer token 1 level deep. -func GetForToken(document any, decodedToken string) (any, reflect.Kind, error) { - return getSingleImpl(document, decodedToken, jsonname.DefaultJSONNameProvider) +func GetForToken(document any, decodedToken string, opts ...Option) (any, reflect.Kind, error) { + o := optionsWithDefaults(opts) + + return getSingleImpl(document, decodedToken, o.provider) } // SetForToken sets a value for a json pointer token 1 level deep. -func SetForToken(document any, decodedToken string, value any) (any, error) { - return document, setSingleImpl(document, value, decodedToken, jsonname.DefaultJSONNameProvider) +// +// See [Pointer.Set] for the mutation contract, in particular the handling of the RFC 6901 "-" token +// on slices. +func SetForToken(document any, decodedToken string, value any, opts ...Option) (any, error) { + o := optionsWithDefaults(opts) + + return setSingleImpl(document, value, decodedToken, o.provider) } -func getSingleImpl(node any, decodedToken string, nameProvider *jsonname.NameProvider) (any, reflect.Kind, error) { +func getSingleImpl(node any, decodedToken string, nameProvider NameProvider) (any, reflect.Kind, error) { rValue := reflect.Indirect(reflect.ValueOf(node)) kind := rValue.Kind() if isNil(node) { @@ -361,6 +521,9 @@ func getSingleImpl(node any, decodedToken string, nameProvider *jsonname.NamePro return nil, kind, errNoKey(decodedToken) case reflect.Slice: + if decodedToken == dashToken { + return nil, kind, errDashOnGet() + } tokenIndex, err := strconv.Atoi(decodedToken) if err != nil { return nil, kind, errors.Join(err, ErrPointer) @@ -378,14 +541,14 @@ func getSingleImpl(node any, decodedToken string, nameProvider *jsonname.NamePro } } -func setSingleImpl(node, data any, decodedToken string, nameProvider *jsonname.NameProvider) error { +func setSingleImpl(node, data any, decodedToken string, nameProvider NameProvider) (any, error) { // check for nil to prevent panic when calling rValue.Type() if isNil(node) { - return fmt.Errorf("cannot set field %q on nil value: %w", decodedToken, ErrPointer) + return node, fmt.Errorf("cannot set field %q on nil value: %w", decodedToken, ErrPointer) } if ns, ok := node.(JSONSetable); ok { - return ns.JSONSet(decodedToken, data) + return node, ns.JSONSet(decodedToken, data) } rValue := reflect.Indirect(reflect.ValueOf(node)) @@ -394,12 +557,12 @@ func setSingleImpl(node, data any, decodedToken string, nameProvider *jsonname.N case reflect.Struct: nm, ok := nameProvider.GetGoNameForType(rValue.Type(), decodedToken) if !ok { - return fmt.Errorf("object has no field %q: %w", decodedToken, ErrPointer) + return node, fmt.Errorf("object has no field %q: %w", decodedToken, ErrPointer) } fld := rValue.FieldByName(nm) if !fld.CanSet() { - return fmt.Errorf("can't set struct field %s to %v: %w", nm, data, ErrPointer) + return node, fmt.Errorf("can't set struct field %s to %v: %w", nm, data, ErrPointer) } value := reflect.ValueOf(data) @@ -407,33 +570,51 @@ func setSingleImpl(node, data any, decodedToken string, nameProvider *jsonname.N assignedType := fld.Type() if !valueType.AssignableTo(assignedType) { - return fmt.Errorf("can't set value with type %T to field %s with type %v: %w", data, nm, assignedType, ErrPointer) + return node, fmt.Errorf("can't set value with type %T to field %s with type %v: %w", data, nm, assignedType, ErrPointer) } fld.Set(value) - return nil + return node, nil case reflect.Map: kv := reflect.ValueOf(decodedToken) rValue.SetMapIndex(kv, reflect.ValueOf(data)) - return nil + return node, nil case reflect.Slice: + if decodedToken == dashToken { + // RFC 6901 §4 / RFC 6902 append semantics: terminal "-" appends the value to the slice. + // + // We rebind in place when the slice is reachable via an addressable ancestor; otherwise we + // return the new slice header for the parent (or the public Set) to rebind. + value := reflect.ValueOf(data) + elemType := rValue.Type().Elem() + if !value.Type().AssignableTo(elemType) { + return node, fmt.Errorf("can't append value of type %T to slice of %v: %w", data, elemType, ErrPointer) + } + newSlice := reflect.Append(rValue, value) + if rValue.CanSet() { + rValue.Set(newSlice) + return node, nil + } + return newSlice.Interface(), nil + } + tokenIndex, err := strconv.Atoi(decodedToken) if err != nil { - return errors.Join(err, ErrPointer) + return node, errors.Join(err, ErrPointer) } sLength := rValue.Len() if tokenIndex < 0 || tokenIndex >= sLength { - return errOutOfBounds(sLength, tokenIndex) + return node, errOutOfBounds(sLength, tokenIndex) } elem := rValue.Index(tokenIndex) if !elem.CanSet() { - return fmt.Errorf("can't set slice index %s to %v: %w", decodedToken, data, ErrPointer) + return node, fmt.Errorf("can't set slice index %s to %v: %w", decodedToken, data, ErrPointer) } value := reflect.ValueOf(data) @@ -441,15 +622,15 @@ func setSingleImpl(node, data any, decodedToken string, nameProvider *jsonname.N assignedType := elem.Type() if !valueType.AssignableTo(assignedType) { - return fmt.Errorf("can't set value with type %T to slice element %d with type %v: %w", data, tokenIndex, assignedType, ErrPointer) + return node, fmt.Errorf("can't set value with type %T to slice element %d with type %v: %w", data, tokenIndex, assignedType, ErrPointer) } elem.Set(value) - return nil + return node, nil default: - return errInvalidReference(decodedToken) + return node, errInvalidReference(decodedToken) } } @@ -460,24 +641,27 @@ func offsetSingleObject(dec *json.Decoder, decodedToken string) (int64, error) { if err != nil { return 0, err } - switch tk := tk.(type) { - case json.Delim: - switch tk { - case '{': - if err = drainSingle(dec); err != nil { - return 0, err - } - case '[': + key, ok := tk.(string) + if !ok { + return 0, fmt.Errorf("invalid key token %#v: %w", tk, ErrPointer) + } + if key == decodedToken { + return offset, nil + } + + // Consume the associated value. + // Scalars are fully read by a single Token() call; composite values must be drained. + tk, err = dec.Token() + if err != nil { + return 0, err + } + if delim, isDelim := tk.(json.Delim); isDelim { + switch delim { + case '{', '[': if err = drainSingle(dec); err != nil { return 0, err } } - case string: - if tk == decodedToken { - return offset, nil - } - default: - return 0, fmt.Errorf("invalid token %#v: %w", tk, ErrPointer) } } @@ -485,6 +669,9 @@ func offsetSingleObject(dec *json.Decoder, decodedToken string) (int64, error) { } func offsetSingleArray(dec *json.Decoder, decodedToken string) (int64, error) { + if decodedToken == dashToken { + return 0, errDashOnOffset() + } idx, err := strconv.Atoi(decodedToken) if err != nil { return 0, fmt.Errorf("token reference %q is not a number: %w: %w", decodedToken, err, ErrPointer) @@ -548,10 +735,7 @@ func drainSingle(dec *json.Decoder) error { return nil } -// JSON pointer encoding: -// ~0 => ~ -// ~1 => / -// ... and vice versa +// JSON pointer encoding: ~0 => ~ ~1 => / ... and vice versa. const ( encRefTok0 = `~0` diff --git a/vendor/github.com/go-openapi/jsonreference/.gitignore b/vendor/github.com/go-openapi/jsonreference/.gitignore index 885dc27ab0..d8f4186fe5 100644 --- a/vendor/github.com/go-openapi/jsonreference/.gitignore +++ b/vendor/github.com/go-openapi/jsonreference/.gitignore @@ -3,4 +3,3 @@ .idea .env .mcp.json -.claude/ diff --git a/vendor/github.com/go-openapi/jsonreference/CONTRIBUTORS.md b/vendor/github.com/go-openapi/jsonreference/CONTRIBUTORS.md index 7faeb83a77..d20737c946 100644 --- a/vendor/github.com/go-openapi/jsonreference/CONTRIBUTORS.md +++ b/vendor/github.com/go-openapi/jsonreference/CONTRIBUTORS.md @@ -4,18 +4,18 @@ | Total Contributors | Total Contributions | | --- | --- | -| 9 | 73 | +| 9 | 83 | | Username | All Time Contribution Count | All Commits | | --- | --- | --- | -| @fredbi | 36 | https://github.com/go-openapi/jsonreference/commits?author=fredbi | -| @casualjim | 25 | https://github.com/go-openapi/jsonreference/commits?author=casualjim | -| @youyuanwu | 5 | https://github.com/go-openapi/jsonreference/commits?author=youyuanwu | -| @olivierlemasle | 2 | https://github.com/go-openapi/jsonreference/commits?author=olivierlemasle | -| @apelisse | 1 | https://github.com/go-openapi/jsonreference/commits?author=apelisse | -| @gbjk | 1 | https://github.com/go-openapi/jsonreference/commits?author=gbjk | -| @honza | 1 | https://github.com/go-openapi/jsonreference/commits?author=honza | -| @Neo2308 | 1 | https://github.com/go-openapi/jsonreference/commits?author=Neo2308 | -| @erraggy | 1 | https://github.com/go-openapi/jsonreference/commits?author=erraggy | +| @fredbi | 46 | | +| @casualjim | 25 | | +| @youyuanwu | 5 | | +| @olivierlemasle | 2 | | +| @apelisse | 1 | | +| @gbjk | 1 | | +| @honza | 1 | | +| @Neo2308 | 1 | | +| @erraggy | 1 | | - _this file was generated by the [Contributors GitHub Action](https://github.com/github/contributors)_ + _this file was generated by the [Contributors GitHub Action](https://github.com/github-community-projects/contributors)_ diff --git a/vendor/github.com/go-openapi/jsonreference/README.md b/vendor/github.com/go-openapi/jsonreference/README.md index adea160619..fbd16cf892 100644 --- a/vendor/github.com/go-openapi/jsonreference/README.md +++ b/vendor/github.com/go-openapi/jsonreference/README.md @@ -16,13 +16,8 @@ An implementation of JSON Reference for golang. ## Announcements -* **2025-12-19** : new community chat on discord - * a new discord community channel is available to be notified of changes and support users - * our venerable Slack channel remains open, and will be eventually discontinued on **2026-03-31** - -You may join the discord community by clicking the invite link on the discord badge (also above). [![Discord Channel][discord-badge]][discord-url] - -Or join our Slack channel: [![Slack Channel][slack-logo]![slack-badge]][slack-url] +* **2026-07-07** : landing v1.0.0 + * stable API pledge ## Status @@ -74,9 +69,9 @@ on top of which it has been built. ## Other documentation * [All-time contributors](./CONTRIBUTORS.md) -* [Contributing guidelines](.github/CONTRIBUTING.md) -* [Maintainers documentation](docs/MAINTAINERS.md) -* [Code style](docs/STYLE.md) +* [Contributing guidelines][contributing-doc-site] +* [Maintainers documentation][maintainers-doc-site] +* [Code style][style-doc-site] ## Cutting a new release @@ -111,11 +106,8 @@ Maintainers can cut a new release by either: [doc-url]: https://goswagger.io/go-openapi [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/jsonreference [godoc-url]: http://pkg.go.dev/github.com/go-openapi/jsonreference -[slack-logo]: https://a.slack-edge.com/e6a93c1/img/icons/favicon-32.png -[slack-badge]: https://img.shields.io/badge/slack-blue?link=https%3A%2F%2Fgoswagger.slack.com%2Farchives%2FC04R30YM -[slack-url]: https://goswagger.slack.com/archives/C04R30YMU [discord-badge]: https://img.shields.io/discord/1446918742398341256?logo=discord&label=discord&color=blue -[discord-url]: https://discord.gg/twZ9BwT3 +[discord-url]: https://discord.gg/FfnFYaC3k5 [license-badge]: http://img.shields.io/badge/license-Apache%20v2-orange.svg @@ -125,3 +117,7 @@ Maintainers can cut a new release by either: [goversion-url]: https://github.com/go-openapi/jsonreference/blob/master/go.mod [top-badge]: https://img.shields.io/github/languages/top/go-openapi/jsonreference [commits-badge]: https://img.shields.io/github/commits-since/go-openapi/jsonreference/latest + +[contributing-doc-site]: https://go-openapi.github.io/doc-site/contributing/contributing/index.html +[maintainers-doc-site]: https://go-openapi.github.io/doc-site/maintainers/index.html +[style-doc-site]: https://go-openapi.github.io/doc-site/contributing/style/index.html diff --git a/vendor/github.com/go-openapi/loads/.gitignore b/vendor/github.com/go-openapi/loads/.gitignore index d8f4186fe5..fbb78de2c3 100644 --- a/vendor/github.com/go-openapi/loads/.gitignore +++ b/vendor/github.com/go-openapi/loads/.gitignore @@ -3,3 +3,4 @@ .idea .env .mcp.json +.worktrees diff --git a/vendor/github.com/go-openapi/loads/.golangci.yml b/vendor/github.com/go-openapi/loads/.golangci.yml index 83968f3fae..272b14e545 100644 --- a/vendor/github.com/go-openapi/loads/.golangci.yml +++ b/vendor/github.com/go-openapi/loads/.golangci.yml @@ -7,6 +7,8 @@ linters: - gochecknoglobals # on this repo, it is hard to refactor without globals/inits and no breaking change - gochecknoinits - godox + - gomodguard + - gomodguard_v2 - exhaustruct - nlreturn - nonamedreturns diff --git a/vendor/github.com/go-openapi/loads/CONTRIBUTORS.md b/vendor/github.com/go-openapi/loads/CONTRIBUTORS.md index 36b836a3d5..ff3fd35081 100644 --- a/vendor/github.com/go-openapi/loads/CONTRIBUTORS.md +++ b/vendor/github.com/go-openapi/loads/CONTRIBUTORS.md @@ -4,12 +4,12 @@ | Total Contributors | Total Contributions | | --- | --- | -| 14 | 123 | +| 14 | 136 | | Username | All Time Contribution Count | All Commits | | --- | --- | --- | +| @fredbi | 58 | | | @casualjim | 48 | | -| @fredbi | 45 | | | @youyuanwu | 6 | | | @vburenin | 4 | | | @keramix | 4 | | @@ -23,4 +23,4 @@ | @kreativka | 1 | | | @petrkotas | 1 | | - _this file was generated by the [Contributors GitHub Action](https://github.com/github/contributors)_ + _this file was generated by the [Contributors GitHub Action](https://github.com/github-community-projects/contributors)_ diff --git a/vendor/github.com/go-openapi/loads/README.md b/vendor/github.com/go-openapi/loads/README.md index d92e62a040..293f79bb6a 100644 --- a/vendor/github.com/go-openapi/loads/README.md +++ b/vendor/github.com/go-openapi/loads/README.md @@ -20,12 +20,9 @@ Supports JSON and YAML documents. * **2025-12-19** : new community chat on discord * a new discord community channel is available to be notified of changes and support users - * our venerable Slack channel remains open, and will be eventually discontinued on **2026-03-31** You may join the discord community by clicking the invite link on the discord badge (also above). [![Discord Channel][discord-badge]][discord-url] -Or join our Slack channel: [![Slack Channel][slack-logo]![slack-badge]][slack-url] - ## Status API is stable. @@ -58,6 +55,41 @@ go get github.com/go-openapi/loads See also the provided [examples](https://pkg.go.dev/github.com/go-openapi/loads#pkg-examples). +## Security + +This library does not enforce a security policy of its own: it reads whatever the configured +loader is allowed to read. + +This is deliberate — like `go-openapi/swag/loading`, it is a base utility, +and sanitizing or containing untrusted input is the caller's responsibility, +just as sanitizing a file name before passing it to `os.ReadFile` is not that function's job. + +When a spec — its path or its `$ref` contents — may come from an untrusted source, confine +loading explicitly (e.g. `loading.WithRoot` for local files and a restricted +`loading.WithHTTPClient` for remote URLs, passed via `loads.WithLoadingOptions`). + +For the common case, the pre-baked `loads.SpecRestricted` / `loads.JSONSpecRestricted` loaders +bundle a trusted root with a network-restricted client (`loads.RestrictedHTTPClient`) and apply +the confinement to `$ref` resolution as well: + +```go +doc, err := loads.SpecRestricted(path, trustedRoot) +``` + +To harden the package-level default in one call — so even callers that rely on the global +loader (including cross-package `$ref` resolution via `spec.PathLoader`) are confined, with no +unconfined fallback left — use `loads.SetRestrictedLoaders` at startup: + +```go +loads.SetRestrictedLoaders(trustedRoot) +``` + +Note that `loads.AddLoader` only *prepends* to the default chain, leaving the unconfined loader +reachable; use `loads.SetLoaders` / `loads.SetRestrictedLoaders` to replace it. + +See the [Security section of the package documentation][security-doc] for the threat model and +runnable examples. For the project's vulnerability reporting policy, see [SECURITY.md](./SECURITY.md). + ## Change log See @@ -69,9 +101,9 @@ This library ships under the [SPDX-License-Identifier: Apache-2.0](./LICENSE). ## Other documentation * [All-time contributors](./CONTRIBUTORS.md) -* [Contributing guidelines](.github/CONTRIBUTING.md) -* [Maintainers documentation](docs/MAINTAINERS.md) -* [Code style](docs/STYLE.md) +* [Contributing guidelines][contributing-doc-site] +* [Maintainers documentation][maintainers-doc-site] +* [Code style][style-doc-site] ## Cutting a new release @@ -102,11 +134,8 @@ Maintainers can cut a new release by either: [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/loads [godoc-url]: http://pkg.go.dev/github.com/go-openapi/loads -[slack-logo]: https://a.slack-edge.com/e6a93c1/img/icons/favicon-32.png -[slack-badge]: https://img.shields.io/badge/slack-blue?link=https%3A%2F%2Fgoswagger.slack.com%2Farchives%2FC04R30YM -[slack-url]: https://goswagger.slack.com/archives/C04R30YMU [discord-badge]: https://img.shields.io/discord/1446918742398341256?logo=discord&label=discord&color=blue -[discord-url]: https://discord.gg/twZ9BwT3 +[discord-url]: https://discord.gg/FfnFYaC3k5 [license-badge]: http://img.shields.io/badge/license-Apache%20v2-orange.svg @@ -116,3 +145,9 @@ Maintainers can cut a new release by either: [goversion-url]: https://github.com/go-openapi/loads/blob/master/go.mod [top-badge]: https://img.shields.io/github/languages/top/go-openapi/loads [commits-badge]: https://img.shields.io/github/commits-since/go-openapi/loads/latest + +[security-doc]: https://pkg.go.dev/github.com/go-openapi/loads#hdr-Security + +[contributing-doc-site]: https://go-openapi.github.io/doc-site/contributing/contributing/index.html +[maintainers-doc-site]: https://go-openapi.github.io/doc-site/maintainers/index.html +[style-doc-site]: https://go-openapi.github.io/doc-site/contributing/style/index.html diff --git a/vendor/github.com/go-openapi/loads/doc.go b/vendor/github.com/go-openapi/loads/doc.go index 67a5e2f8d9..0fafe4f468 100644 --- a/vendor/github.com/go-openapi/loads/doc.go +++ b/vendor/github.com/go-openapi/loads/doc.go @@ -6,4 +6,72 @@ // It is used by other go-openapi packages to load and run analysis on local or remote spec documents. // // Loaders support JSON and YAML documents. +// +// # Security +// +// This package does not enforce a security policy of its own: like the underlying +// [github.com/go-openapi/swag/loading] utilities, it reads whatever the configured loader is +// allowed to read. +// +// When a spec — its path or its contents — may derive from untrusted input, the caller must confine loading explicitly. +// +// This is a deliberate design choice. +// Both this package and the [github.com/go-openapi/swag/loading] utilities are base building blocks: +// deciding which sources are legitimate, and containing access to them, +// requires application context that a general-purpose loader does not have. +// +// Just as sanitizing a file name before handing it to [os.ReadFile] is the caller's +// responsibility and not that function's, sanitizing and containing the path and references +// resolved here is the responsibility of the code that may feed them untrusted input. +// +// There are two distinct attack surfaces: +// +// - The path passed to [Spec], [JSONSpec], or [Embedded]. By default a local path is read +// with no confinement, so a caller-controlled path (including an absolute path or a +// "file:///etc/passwd" URI) may read any file the process can access. A remote path is +// fetched with [net/http.DefaultClient], which follows redirects and performs no +// destination filtering, so a caller-controlled URL may reach internal services or cloud +// metadata endpoints (server-side request forgery). +// +// - The contents of the spec, when references are resolved. [Document.Expanded] follows the +// "$ref" pointers found inside the document by calling the same loader recursively. A spec +// obtained even from a trusted path can therefore drive arbitrary local reads +// ("$ref": "file:///etc/passwd") or SSRF ("$ref": "http://169.254.169.254/...") through +// its own contents. This amplification is specific to reference resolution and does not +// exist in the raw loading utilities. +// +// Mitigation. Pass [github.com/go-openapi/swag/loading] options through [WithLoadingOptions]; +// they are attached to the document's loader and so apply both to the initial load and to +// every "$ref" resolved during expansion: +// +// - [github.com/go-openapi/swag/loading.WithRoot] confines local reads to a trusted +// directory, rejecting absolute paths, ".." traversal, and symlinks that escape it. Prefer +// it over a [github.com/go-openapi/swag/loading.WithFS] built from [os.DirFS], which does +// not block symlink escapes. +// +// - [github.com/go-openapi/swag/loading.WithHTTPClient] allows to supply a restricted HTTP client. +// Enforce the network policy at dial time (a [net.Dialer] Control hook), so it also covers +// redirects and DNS rebinding, which a URL-string allowlist cannot. See the example on +// [Spec]. +// +// Pre-baked loaders. When the opinionated defaults fit, [SpecRestricted], [JSONSpecRestricted] +// and [JSONDocRestricted] bundle a trusted root with a network-restricted client +// ([RestrictedHTTPClient]), and apply the confinement to "$ref" resolution as well — so the +// common case needs no manual wiring. To harden the global default in one call (so even callers +// that rely on the package-level loader are confined), use [SetRestrictedLoaders]. Reach for the +// options above when you need a custom policy; [IsForbiddenAddress] exposes the default network +// policy so you can reuse it as the base of your own HTTP client. +// +// Caveats: +// +// - The package-level default loader (also installed as [github.com/go-openapi/spec.PathLoader]) +// carries no loading options and is therefore unconfined. It is used as a fallback when +// expansion runs without a document loader, and by other go-openapi packages that resolve +// references on their own. [AddLoader] does not fix this — it only prepends, leaving the +// unconfined fallback reachable. Either build a confined loader per call, or replace the +// global default outright with [SetLoaders] / [SetRestrictedLoaders]. +// +// - A custom loader installed via [WithDocLoader] or [AddLoader] only honors these +// protections if its loading function actually applies the [github.com/go-openapi/swag/loading] +// options it is given. package loads diff --git a/vendor/github.com/go-openapi/loads/errors.go b/vendor/github.com/go-openapi/loads/errors.go index 14a8186b6c..e94f038f94 100644 --- a/vendor/github.com/go-openapi/loads/errors.go +++ b/vendor/github.com/go-openapi/loads/errors.go @@ -15,4 +15,8 @@ const ( // ErrNoLoader indicates that no configured loader matched the input. ErrNoLoader loaderError = "no loader matched" + + // ErrForbiddenAddress is returned by [RestrictedHTTPClient] when a connection is attempted + // to a non-public address (loopback, private, link-local, or unspecified). + ErrForbiddenAddress loaderError = "blocked dial to a non-public address" ) diff --git a/vendor/github.com/go-openapi/loads/loaders.go b/vendor/github.com/go-openapi/loads/loaders.go index ac8adfe8b2..ba5c48b65f 100644 --- a/vendor/github.com/go-openapi/loads/loaders.go +++ b/vendor/github.com/go-openapi/loads/loaders.go @@ -21,6 +21,15 @@ import ( var loaders *loader func init() { + loaders = defaultLoaders() + + // sets the global default loader for go-openapi/spec + spec.PathLoader = loaders.Load +} + +// defaultLoaders builds the built-in loader chain: a YAML matcher first, with a JSON loader as +// the catch-all fallback. +func defaultLoaders() *loader { jsonLoader := &loader{ DocLoaderWithMatch: DocLoaderWithMatch{ Match: func(_ string) bool { @@ -30,20 +39,85 @@ func init() { }, } - loaders = jsonLoader.WithHead(&loader{ + return jsonLoader.WithHead(&loader{ DocLoaderWithMatch: DocLoaderWithMatch{ Match: loading.YAMLMatcher, Fn: loading.YAMLDoc, }, }) +} - // sets the global default loader for go-openapi/spec - spec.PathLoader = loaders.Load +// LoaderChain links a list of [DocLoaderWithMatch] into a single [DocLoader], preserving order. +// Entries with a nil Fn are skipped. Loading options passed at call time are forwarded to the +// matched loader. +// +// Combined with [LoaderWithOptions], it composes a self-contained loader (for example a +// format-dispatching YAML/JSON chain, each entry carrying its own options) that a caller can inject +// through [WithDocLoader] instead of relying on the package-level global loaders. +// +// The returned DocLoader is never nil: when no usable loader is provided, it yields [ErrNoLoader] +// on every call. This fails closed rather than returning nil (which every caller would have to +// guard against, at the risk of a nil-func panic) or silently falling back to an unconfined +// loader. +func LoaderChain(ldrs ...DocLoaderWithMatch) DocLoader { + loader := buildLoaderChain(ldrs...) + + return func(pth string, opts ...loading.Option) (json.RawMessage, error) { + l := loader.clone() + if l != nil { + l.loadingOptions = opts + } + + return l.Load(pth) // nil-safe: yields ErrNoLoader when the chain is empty + } +} + +// buildLoaderChain links a list of [DocLoaderWithMatch] into a loader chain, preserving order. +// Entries with a nil Fn are skipped. Returns nil when no usable loader is provided. +func buildLoaderChain(ldrs ...DocLoaderWithMatch) *loader { + var final, prev *loader + for _, ldr := range ldrs { + if ldr.Fn == nil { + continue + } + + node := &loader{DocLoaderWithMatch: ldr} + if prev == nil { + final = node + prev = node + + continue + } + + prev = prev.WithNext(node) + } + + return final } // DocLoader represents a doc loader type. type DocLoader func(string, ...loading.Option) (json.RawMessage, error) +// LoaderWithOptions returns a [DocLoader] that always applies opts. +// +// Use it to bind a set of [loading.Option] to a loader so they apply to every load — for example a +// custom HTTP client or timeout, authentication or custom headers, an embedded or rooted file +// system, or a remote-address restriction. This is the building block for a document loader that +// carries its own options, avoiding reliance on the package-level global loaders. +// +// opts are appended after any options passed at call time, so they take precedence (loading +// options are last-wins). This also makes confinement options (e.g. [loading.WithRoot]) win over +// any caller-supplied options. +func LoaderWithOptions(fn DocLoader, opts ...loading.Option) DocLoader { + return func(path string, callOpts ...loading.Option) (json.RawMessage, error) { + all := make([]loading.Option, 0, len(callOpts)+len(opts)) + all = append(all, callOpts...) + all = append(all, opts...) + + return fn(path, all...) + } +} + // DocMatcher represents a predicate to check if a loader matches. type DocMatcher func(string) bool @@ -141,6 +215,17 @@ func JSONDoc(path string, opts ...loading.Option) (json.RawMessage, error) { // // This function updates the default loader used by [github.com/go-openapi/spec]. // Since this sets package level globals, you shouldn't call this concurrently. +// +// # Security +// +// AddLoader only *prepends* to the default chain: the previous loaders — including the +// unconfined JSON fallback — remain reachable, both here and via cross-package "$ref" +// resolution. It is therefore the wrong tool for hardening the global default. To replace the +// chain entirely (leaving no unconfined fallback) use [SetLoaders], or [SetRestrictedLoaders] +// for a one-call confined setup. For a single load, prefer a confined per-call loader via +// [WithLoadingOptions] or [WithDocLoaderMatches]. A custom loader registered here only honors +// the protections if its loading function applies the [github.com/go-openapi/swag/loading] +// options it is given. See the package documentation on Security. func AddLoader(predicate DocMatcher, load DocLoader) { loaders = loaders.WithHead(&loader{ DocLoaderWithMatch: DocLoaderWithMatch{ @@ -152,3 +237,36 @@ func AddLoader(predicate DocMatcher, load DocLoader) { // sets the global default loader for go-openapi/spec spec.PathLoader = loaders.Load } + +// SetLoaders replaces the package-level default loader chain with the given loaders, tried in +// order, and re-points [github.com/go-openapi/spec.PathLoader] at it. +// +// Unlike [AddLoader], nothing of the previous default survives — so when the replacement is +// confined, no unconfined fallback remains for any caller relying on the global default +// (including cross-package "$ref" resolution). An entry with a nil Match is a catch-all; you +// are responsible for providing a suitable fallback. Calling SetLoaders with no usable loader +// restores the built-in default (a YAML matcher with a JSON fallback). +// +// # Concurrency +// +// This sets package-level globals and the [github.com/go-openapi/spec] global loader. It is +// not safe to call concurrently with other loads or with [AddLoader]; configure it once at +// startup, before serving. +// +// # Security +// +// This is the way to harden the global default in one place. For a ready-made confined setup, +// see [SetRestrictedLoaders]. As with [AddLoader], a custom loader only honors the protections +// if its loading function applies the [github.com/go-openapi/swag/loading] options it is given. +// See the package documentation on Security. +func SetLoaders(ldrs ...DocLoaderWithMatch) { + chain := buildLoaderChain(ldrs...) + if chain == nil { + chain = defaultLoaders() + } + + loaders = chain + + // sets the global default loader for go-openapi/spec + spec.PathLoader = loaders.Load +} diff --git a/vendor/github.com/go-openapi/loads/options.go b/vendor/github.com/go-openapi/loads/options.go index 045ece5e09..6a4bc6983b 100644 --- a/vendor/github.com/go-openapi/loads/options.go +++ b/vendor/github.com/go-openapi/loads/options.go @@ -51,25 +51,17 @@ func WithDocLoader(l DocLoader) LoaderOption { // Loaders are executed in the order of provided [DocLoaderWithMatch] 'es. func WithDocLoaderMatches(l ...DocLoaderWithMatch) LoaderOption { return func(opt *options) { - var final, prev *loader - for _, ldr := range l { - if ldr.Fn == nil { - continue - } - - if prev == nil { - final = &loader{DocLoaderWithMatch: ldr} - prev = final - continue - } - - prev = prev.WithNext(&loader{DocLoaderWithMatch: ldr}) - } - opt.loader = final + opt.loader = buildLoaderChain(l...) } } // WithLoadingOptions adds some [loading.Option] to be added when calling a registered loader. +// +// The options are attached to the document's loader, so they apply both to the initial load +// and to every "$ref" resolved during [Document.Expanded]. +// +// This is the recommended place to confine loading of untrusted input, for example with [loading.WithRoot] (local) and +// [loading.WithHTTPClient] (remote). See the package documentation on Security. func WithLoadingOptions(loadingOptions ...loading.Option) LoaderOption { return func(opt *options) { opt.loadingOptions = loadingOptions diff --git a/vendor/github.com/go-openapi/loads/restricted.go b/vendor/github.com/go-openapi/loads/restricted.go new file mode 100644 index 0000000000..022a9a8572 --- /dev/null +++ b/vendor/github.com/go-openapi/loads/restricted.go @@ -0,0 +1,185 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package loads + +import ( + "encoding/json" + "net" + "net/http" + "net/netip" + "syscall" + "time" + + "github.com/go-openapi/swag/loading" +) + +const ( + // numConfinementOptions is the count of loading options appended to enforce confinement + // (WithRoot + WithHTTPClient), used to size the bundled option slice. + numConfinementOptions = 2 + + defaultTLSHandshakeTimeout = 10 * time.Second +) + +// RestrictedHTTPClient returns an [http.Client] that refuses, at dial time, to connect to +// loopback, private, link-local (including cloud-metadata endpoints such as 169.254.169.254), +// or unspecified addresses. A blocked connection fails with an error wrapping +// [ErrForbiddenAddress]. +// +// The check runs in the dialer Control hook, after DNS resolution and before connect, so it +// also covers HTTP redirects and DNS rebinding — which a URL-string allowlist cannot. The +// client does not honor proxy environment variables, so the guard always inspects the real +// destination rather than a proxy address. +// +// This is the network half of the restricted loaders ([JSONDocRestricted], +// [JSONSpecRestricted], [SpecRestricted]). It may also be used directly with +// [github.com/go-openapi/swag/loading.WithHTTPClient]. +// +// The policy is opinionated and deliberately simple. For a different one (a custom allow/deny +// list, an explicit proxy, mutual TLS, ...), build your own client and pass it with +// [github.com/go-openapi/swag/loading.WithHTTPClient]. To keep the default address policy as a +// base, reuse [IsForbiddenAddress] in your own dialer Control hook — see the package examples +// for the pattern. +func RestrictedHTTPClient() *http.Client { + control := func(_, address string, _ syscall.RawConn) error { + host, _, err := net.SplitHostPort(address) + if err != nil { + return err + } + addr, err := netip.ParseAddr(host) + if err != nil { + return err + } + if IsForbiddenAddress(addr) { + return ErrForbiddenAddress + } + + return nil + } + + return &http.Client{ + Transport: &http.Transport{ + Proxy: nil, // dial the real destination so the guard inspects it + DialContext: (&net.Dialer{Control: control}).DialContext, + ForceAttemptHTTP2: true, + TLSHandshakeTimeout: defaultTLSHandshakeTimeout, + }, + } +} + +// IsForbiddenAddress reports whether addr is one that [RestrictedHTTPClient] refuses to dial: +// a loopback, private, link-local (including cloud-metadata endpoints such as 169.254.169.254), +// or unspecified address. IPv4-mapped IPv6 addresses are unmapped before the check. +// +// It is exported so callers can reuse or extend the default policy when building their own +// dialer Control hook, for example to also reject a CGNAT range or to carve out a single +// trusted internal host: +// +// control := func(_, address string, _ syscall.RawConn) error { +// host, _, err := net.SplitHostPort(address) +// if err != nil { +// return err +// } +// addr, err := netip.ParseAddr(host) +// if err != nil { +// return err +// } +// if loads.IsForbiddenAddress(addr) && host != allowedInternalHost { +// return loads.ErrForbiddenAddress +// } +// return nil +// } +func IsForbiddenAddress(addr netip.Addr) bool { + a := addr.Unmap() + + return a.IsLoopback() || a.IsPrivate() || a.IsLinkLocalUnicast() || a.IsUnspecified() +} + +// restrictedLoadingOptions bundles caller-supplied options with the confinement options, +// appended last so that local rooting and the restricted client always take precedence +// (the loading options are last-wins). +func restrictedLoadingOptions(root string, extra []loading.Option) []loading.Option { + out := make([]loading.Option, 0, len(extra)+numConfinementOptions) + out = append(out, extra...) + out = append(out, loading.WithRoot(root), loading.WithHTTPClient(RestrictedHTTPClient())) + + return out +} + +// JSONDocRestricted returns a JSON [DocLoader] that confines local reads to root (via +// [github.com/go-openapi/swag/loading.WithRoot]) and restricts remote fetches with +// [RestrictedHTTPClient]. +// +// The returned loader may be registered with [WithDocLoader] or [AddLoader]. The confinement +// always takes precedence over any option passed here or at call time, so a caller cannot +// loosen it through [WithLoadingOptions]. +// +// Like [JSONDoc], it loads JSON only: it does not convert YAML. For specs whose references may +// point at YAML documents, prefer [SpecRestricted], which keeps the default JSON/YAML chain. +func JSONDocRestricted(root string, opts ...loading.Option) DocLoader { + // one restricted client, reused for every path and $ref + return restrictedDocLoader(JSONDoc, restrictedLoadingOptions(root, opts)) +} + +// restrictedDocLoader wraps a [DocLoader] so that the confinement options in base are always +// applied, appended after any call-time options so they take precedence (loading options are +// last-wins). +func restrictedDocLoader(fn DocLoader, base []loading.Option) DocLoader { + return func(path string, callOpts ...loading.Option) (json.RawMessage, error) { + if len(callOpts) == 0 { + return fn(path, base...) + } + + all := make([]loading.Option, 0, len(callOpts)+len(base)) + all = append(all, callOpts...) + all = append(all, base...) // confinement (tail of base) still wins + + return fn(path, all...) + } +} + +// JSONSpecRestricted loads a JSON spec like [JSONSpec], but confines local reads to root and +// restricts remote fetches with [RestrictedHTTPClient]. +// +// The confinement is attached to the document's loader, so it also applies to every "$ref" +// resolved by [Document.Expanded]. Extra [github.com/go-openapi/swag/loading] options (custom +// headers, basic auth, timeout, ...) may be supplied; the confinement always wins over them. +func JSONSpecRestricted(path, root string, opts ...loading.Option) (*Document, error) { + return JSONSpec(path, WithLoadingOptions(restrictedLoadingOptions(root, opts)...)) +} + +// SpecRestricted loads a spec like [Spec] — with JSON/YAML auto-detection — but confines local +// reads to root and restricts remote fetches with [RestrictedHTTPClient]. +// +// The confinement is attached to the document's loader, so it also applies to every "$ref" +// resolved by [Document.Expanded]. Extra [github.com/go-openapi/swag/loading] options (custom +// headers, basic auth, timeout, ...) may be supplied; the confinement always wins over them. +func SpecRestricted(path, root string, opts ...loading.Option) (*Document, error) { + return Spec(path, WithLoadingOptions(restrictedLoadingOptions(root, opts)...)) +} + +// SetRestrictedLoaders hardens the package-level default in a single call: it installs a +// confined JSON/YAML loader chain — local reads rooted at root, remote fetches through +// [RestrictedHTTPClient] — as the global default and as +// [github.com/go-openapi/spec.PathLoader]. +// +// After this call, every load that relies on the package default ([Spec], [JSONSpec], and any +// cross-package "$ref" resolution) is confined, with no unconfined fallback left behind. It is +// the global counterpart of [SpecRestricted]; a single restricted client is shared across the +// chain. Extra [github.com/go-openapi/swag/loading] options may be supplied; the confinement +// always wins over them. +// +// # Concurrency +// +// Like [SetLoaders], this mutates package-level and [github.com/go-openapi/spec] globals and is +// not safe to call concurrently. Configure it once at startup, before serving. To revert, call +// [SetLoaders] with no arguments. +func SetRestrictedLoaders(root string, opts ...loading.Option) { + base := restrictedLoadingOptions(root, opts) // one restricted client shared by the whole chain + + SetLoaders( + NewDocLoaderWithMatch(restrictedDocLoader(loading.YAMLDoc, base), loading.YAMLMatcher), + NewDocLoaderWithMatch(restrictedDocLoader(JSONDoc, base), nil), // nil matcher: JSON catch-all fallback + ) +} diff --git a/vendor/github.com/go-openapi/loads/spec.go b/vendor/github.com/go-openapi/loads/spec.go index 606a01d8e9..40eaff2c73 100644 --- a/vendor/github.com/go-openapi/loads/spec.go +++ b/vendor/github.com/go-openapi/loads/spec.go @@ -77,6 +77,14 @@ func Embedded(orig, flat json.RawMessage, opts ...LoaderOption) (*Document, erro // Spec loads a new spec document from a local or remote path. // // By default it uses a JSON or YAML loader, with auto-detection based on the resource extension. +// +// Security: by default the path is read with no confinement (local) and fetched with +// [net/http.DefaultClient] (remote), and any "$ref" later resolved by [Document.Expanded] is +// loaded the same way. When the path or the spec contents may derive from untrusted input, +// confine loading with [WithLoadingOptions] (for example +// [github.com/go-openapi/swag/loading.WithRoot] and +// [github.com/go-openapi/swag/loading.WithHTTPClient]). See the package documentation on +// Security. func Spec(path string, opts ...LoaderOption) (*Document, error) { ldr := loaderFromOptions(opts) @@ -157,6 +165,14 @@ func trimData(in json.RawMessage) (json.RawMessage, error) { } // Expanded expands the $ref fields in the spec [Document] and returns a new expanded [Document]. +// +// Security: expansion resolves every "$ref" by calling the document's loader recursively, so +// the spec contents drive further loads. A spec from an untrusted source can thus trigger +// arbitrary local reads or SSRF through its references. The loader carries the +// [github.com/go-openapi/swag/loading] options supplied via [WithLoadingOptions] at load time; +// configure confinement there so it applies to expansion as well. When no document loader is +// set, expansion falls back to the unconfined package-level loader. See the package +// documentation on Security. func (d *Document) Expanded(options ...*spec.ExpandOptions) (*Document, error) { swspec := new(spec.Swagger) if err := json.Unmarshal(d.raw, swspec); err != nil { diff --git a/vendor/github.com/go-openapi/runtime/.codecov.yml b/vendor/github.com/go-openapi/runtime/.codecov.yml new file mode 100644 index 0000000000..a5ba8e96d8 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/.codecov.yml @@ -0,0 +1,9 @@ +codecov: + notify: + after_n_builds: 2 + +coverage: + status: + patch: + default: + target: 80% diff --git a/vendor/github.com/go-openapi/runtime/.gitignore b/vendor/github.com/go-openapi/runtime/.gitignore index d8f4186fe5..c0bc15bebe 100644 --- a/vendor/github.com/go-openapi/runtime/.gitignore +++ b/vendor/github.com/go-openapi/runtime/.gitignore @@ -3,3 +3,5 @@ .idea .env .mcp.json +go.work.sum +.worktrees/ diff --git a/vendor/github.com/go-openapi/runtime/.golangci.yml b/vendor/github.com/go-openapi/runtime/.golangci.yml index 0087ed3113..affd69c88a 100644 --- a/vendor/github.com/go-openapi/runtime/.golangci.yml +++ b/vendor/github.com/go-openapi/runtime/.golangci.yml @@ -2,13 +2,9 @@ version: "2" linters: default: all disable: - - cyclop - depguard - err113 # disabled temporarily: there are just too many issues to address - - errchkjson - - errorlint - exhaustruct - - forcetypeassert - funlen - gochecknoglobals - gochecknoinits @@ -16,12 +12,12 @@ linters: - godot - godox - gomoddirectives # moved to mono-repo, multi-modules, so replace directives are needed + - gomodguard + - gomodguard_v2 - gosmopolitan - inamedparam - - ireturn - - lll + - ireturn # this repo adopted a pattern where there are quite many returned interfaces. To be challenged with v2 - musttag - - nestif - nilerr # nilerr crashes on this repo - nlreturn - noinlineerr @@ -31,7 +27,6 @@ linters: - testpackage - thelper - tparallel - - unparam - varnamelen - whitespace - wrapcheck @@ -41,10 +36,19 @@ linters: dupl: threshold: 200 goconst: - min-len: 2 + min-len: 9 min-occurrences: 3 + cyclop: + max-complexity: 25 gocyclo: - min-complexity: 45 + min-complexity: 25 + gocognit: + min-complexity: 35 + exhaustive: + default-signifies-exhaustive: true + default-case-required: true + lll: + line-length: 180 exclusions: generated: lax presets: @@ -53,6 +57,7 @@ linters: - legacy - std-error-handling paths: + - .worktrees - third_party$ - builtin$ - examples$ @@ -60,12 +65,17 @@ formatters: enable: - gofmt - goimports + settings: + # local prefixes regroup imports from these packages + goimports: + local-prefixes: + - github.com/go-openapi exclusions: generated: lax paths: + - .worktrees - third_party$ - builtin$ - - examples$ issues: # Maximum issues count per one linter. # Set to 0 to disable. diff --git a/vendor/github.com/go-openapi/runtime/CONTRIBUTORS.md b/vendor/github.com/go-openapi/runtime/CONTRIBUTORS.md index 541fd575e0..9c959ef482 100644 --- a/vendor/github.com/go-openapi/runtime/CONTRIBUTORS.md +++ b/vendor/github.com/go-openapi/runtime/CONTRIBUTORS.md @@ -4,20 +4,21 @@ | Total Contributors | Total Contributions | | --- | --- | -| 69 | 490 | +| 71 | 570 | | Username | All Time Contribution Count | All Commits | | --- | --- | --- | | @casualjim | 268 | | -| @fredbi | 69 | | +| @fredbi | 144 | | | @youyuanwu | 19 | | | @josephwoodward | 13 | | | @kenjones-cisco | 12 | | | @GlenDC | 7 | | -| @elakito | 6 | | | @moenning | 6 | | | @mstoykov | 6 | | +| @elakito | 6 | | | @ifraixedes | 5 | | +| @Copilot | 4 | | | @zeitlinger | 4 | | | @jkawamoto | 3 | | | @stoyanr | 3 | | @@ -44,7 +45,7 @@ | @petrkotas | 1 | | | @maxatome | 1 | | | @maxkarelov | 1 | | -| @aleksandr-vin | 1 | | +| @tooolbox | 1 | | | @akutz | 1 | | | @yabberyabber | 1 | | | @elv-gilles | 1 | | @@ -56,6 +57,7 @@ | @tte | 1 | | | @martian4202 | 1 | | | @yan-zhuang | 1 | | +| @aleksandr-vin | 1 | | | @azylman | 1 | | | @anasmuhmd | 1 | | | @ArFe | 1 | | @@ -74,8 +76,8 @@ | @JoakimSoderberg | 1 | | | @robbert229 | 1 | | | @jonathaningram | 1 | | +| @KuaaMU | 1 | | | @germanhs | 1 | | | @pracucci | 1 | | -| @tooolbox | 1 | | _this file was generated by the [Contributors GitHub Action](https://github.com/github-community-projects/contributors)_ diff --git a/vendor/github.com/go-openapi/runtime/README.md b/vendor/github.com/go-openapi/runtime/README.md index fa749062b3..134d930cd9 100644 --- a/vendor/github.com/go-openapi/runtime/README.md +++ b/vendor/github.com/go-openapi/runtime/README.md @@ -8,8 +8,7 @@ [![Release][release-badge]][release-url] [![Go Report Card][gocard-badge]][gocard-url] [![CodeFactor Grade][codefactor-badge]][codefactor-url] [![License][license-badge]][license-url] -[![GoDoc][godoc-badge]][godoc-url] [![Discord Channel][discord-badge]][discord-url] [![go version][goversion-badge]][goversion-url] ![Top language][top-badge] ![Commits since latest release][commits-badge] - +[![Doc][doc-badge]][doc-url] [![GoDoc][godoc-badge]][godoc-url] [![Discord Channel][discord-badge]][discord-url] [![go version][goversion-badge]][goversion-url] ![Top language][top-badge] ![Commits since latest release][commits-badge] --- A runtime for go OpenAPI toolkit. @@ -18,13 +17,44 @@ The runtime component for use in code generation or as untyped usage. ## Announcements -* **2025-12-19** : new community chat on discord - * a new discord community channel is available to be notified of changes and support users - * our venerable Slack channel remains open, and will be eventually discontinued on **2026-03-31** +[**Complete documentation as github pages**][doc-url] + +**Changes to the API surface in `v0.30.0`**: + +* utility package `header` has now moved to `github.com/go-openapi/runtime/server-middleware/negotiate/header` + +> A shim is provided to support existing programs, with a deprecation notice. + +**Changes in semantics in `v0.30.0`**: + +Function `negotiate.NegotiateContentType` (available as an alias for backward compatibility as `middleware.NegotiateContentType` +now performs a full match considering MIME parameters. + +The previous behavior (matching in order of appearance after stripping parameters) may be enabled explicitly with +option `negotiate.WithIgnoreParameters`. + +* **2026-05-07** : exposed UI and Spec middleware as a separate, dependency-free module. + +> Newly available package: `github.com/go-openapi/runtime/server-middleware/docui` that now holds our +> UI and spec serve middleware. +> +> A shim is available in `github.com/go-openapi/runtime/middleware` to bridge the older UI options to the new ones, +> with a deprecation notice. +> +> Methods that were unduly exported and purely used to manipulate options (e.g. `SwaggerUIOpts.EnsureDefaults`) have been +> removed. New options in `docui` should be used instead. + +> Users may reuse this middleware to serve a Redoc, Rapidoc or SwaggerUI documentation without +> importing the complete go-openapi scaffolding. -You may join the discord community by clicking the invite link on the discord badge (also above). [![Discord Channel][discord-badge]][discord-url] +* **2026-05-05** : exposed content negotiation methods as a separate, dependency-free module -Or join our Slack channel: [![Slack Channel][slack-logo]![slack-badge]][slack-url] +> Users may reuse these utilities to support content-negotiation without extra dependencies. +> +> Newly available module: `github.com/go-openapi/runtime/server-middleware` +> +> Newly available packages: `github.com/go-openapi/runtime/server-middleware/negotiate` and +> `github.com/go-openapi/runtime/server-middleware/mediatype`. ## Status @@ -40,18 +70,21 @@ go get github.com/go-openapi/runtime See -For pre-v0.30.0 releases see [release notes](docs/NOTES.md). +For v0.29.0 release see [release notes](docs/NOTES.md). +From that release onwards, changes are tracked in the github release notes. **What coming next?** Moving forward, we want to : -* [ ] continue narrowing down the scope of dependencies: - * yaml support in an independent module +* [x] fix a few known issues with some file upload requests (e.g. #286) +* [] continue narrowing down the scope of dependencies: + * [x] split middleware and other useful utilities as a separate dependency-free module + * yaml support in an independent module (v2) * introduce more up-to-date support for opentelemetry as a separate module that evolves independently from the main package (to avoid breaking changes, the existing API - will remain maintained, but evolve at a slower pace than opentelemetry). -* [ ] fix a few known issues with some file upload requests (e.g. #286) + will remain maintained, but evolve at a slower pace than opentelemetry). (v2) +* [] publish proper documentation and examples ## Licensing @@ -62,11 +95,11 @@ on top of which it has been built. ## Other documentation -* [FAQ](docs/FAQ.md) +* [FAQ](https://go-openapi.github.io/runtime/tutorials/faq/) · [Media-type selection](https://go-openapi.github.io/runtime/tutorials/media-types/) · [Client keep-alive](https://go-openapi.github.io/runtime/tutorials/keep-alive/) * [All-time contributors](./CONTRIBUTORS.md) -* [Contributing guidelines](.github/CONTRIBUTING.md) -* [Maintainers documentation](docs/MAINTAINERS.md) -* [Code style](docs/STYLE.md) +* [Contributing guidelines][contributing-doc-site] +* [Maintainers documentation][maintainers-doc-site] +* [Code style][style-doc-site] ## Cutting a new release @@ -95,11 +128,10 @@ Maintainers can cut a new release by either: [codefactor-badge]: https://img.shields.io/codefactor/grade/github/go-openapi/runtime [codefactor-url]: https://www.codefactor.io/repository/github/go-openapi/runtime +[doc-badge]: https://img.shields.io/badge/doc-site-blue?link=https%3A%2F%2Fgo-openapi.github.io%2Fruntime%2F +[doc-url]: https://go-openapi.github.io/runtime [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/runtime [godoc-url]: http://pkg.go.dev/github.com/go-openapi/runtime -[slack-logo]: https://a.slack-edge.com/e6a93c1/img/icons/favicon-32.png -[slack-badge]: https://img.shields.io/badge/slack-blue?link=https%3A%2F%2Fgoswagger.slack.com%2Farchives%2FC04R30YM -[slack-url]: https://goswagger.slack.com/archives/C04R30YMU [discord-badge]: https://img.shields.io/discord/1446918742398341256?logo=discord&label=discord&color=blue [discord-url]: https://discord.gg/FfnFYaC3k5 @@ -111,3 +143,7 @@ Maintainers can cut a new release by either: [goversion-url]: https://github.com/go-openapi/runtime/blob/master/go.mod [top-badge]: https://img.shields.io/github/languages/top/go-openapi/runtime [commits-badge]: https://img.shields.io/github/commits-since/go-openapi/runtime/latest + +[contributing-doc-site]: https://go-openapi.github.io/doc-site/contributing/contributing/index.html +[maintainers-doc-site]: https://go-openapi.github.io/doc-site/maintainers/index.html +[style-doc-site]: https://go-openapi.github.io/doc-site/contributing/style/index.html diff --git a/vendor/github.com/go-openapi/runtime/bytestream.go b/vendor/github.com/go-openapi/runtime/bytestream.go index 8701c8e3d6..9371ea4ea1 100644 --- a/vendor/github.com/go-openapi/runtime/bytestream.go +++ b/vendor/github.com/go-openapi/runtime/bytestream.go @@ -97,7 +97,7 @@ func ByteStreamConsumer(opts ...byteStreamOpt) Consumer { } default: // check for the underlying type to be pointer to []byte or string, - if ptr := reflect.TypeOf(data); ptr.Kind() != reflect.Ptr { + if ptr := reflect.TypeOf(data); ptr.Kind() != reflect.Pointer { return errors.New("destination must be a pointer") } @@ -126,13 +126,13 @@ func ByteStreamConsumer(opts ...byteStreamOpt) Consumer { // // Supported input underlying types and interfaces, prioritized in this order: // -// - [io.WriterTo] (for maximum control) -// - [io.Reader] (performs [io.Copy]). A ReadCloser is closed before exiting. -// - [encoding.BinaryMarshaler] -// - error (writes as a string) -// - []byte -// - string -// - struct, other slices: writes as JSON. +// - [io.WriterTo] (for maximum control) +// - [io.Reader] (performs [io.Copy]). A ReadCloser is closed before exiting. +// - [encoding.BinaryMarshaler] +// - error (writes as a string) +// - []byte +// - string +// - struct, other slices: writes as JSON. func ByteStreamProducer(opts ...byteStreamOpt) Producer { var vals byteStreamOpts for _, opt := range opts { diff --git a/vendor/github.com/go-openapi/runtime/client/httptrace.go b/vendor/github.com/go-openapi/runtime/client/httptrace.go new file mode 100644 index 0000000000..5bdea4e241 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/client/httptrace.go @@ -0,0 +1,520 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package client + +import ( + "context" + "crypto/tls" + "fmt" + "io" + "net/http/httptrace" + "strings" + "sync" + "time" + + "github.com/go-openapi/runtime/logger" +) + +// traceSession owns the per-request state for [Runtime.Trace]. +// +// It tracks the t=0 anchor for the connection phase, accumulates +// per-phase timestamps (for the trailing summary), and emits each +// event to the runtime logger as it fires. One session per +// SubmitContext call. +type traceSession struct { + logger logger.Logger + method string + url string + + // tlsCfg points at the *tls.Config of the http.Transport that + // will run the request, when introspectable (i.e. the transport + // is an *http.Transport). Used by the TLS diagnostic mode to + // cross-check user configuration against what the handshake + // actually attempted. Nil when the transport is custom and + // the config cannot be reached. + tlsCfg *tls.Config + + mu sync.Mutex + start time.Time + last time.Time // last printed event, for relative-dt rendering + phases phaseTimings + gotConn httptrace.GotConnInfo + tlsDone tlsResult + + dnsStartAt time.Time + connectStartAt time.Time + tlsHandshakeStartAt time.Time + wait100StartAt time.Time + gotConnAt time.Time + wroteHeadersAt time.Time + wroteRequestAt time.Time + ttfbAt time.Time + + statusCode int + rtError error +} + +// phaseTimings holds the per-phase durations for the trailing +// summary line. Zero values mean "phase did not occur" (e.g. no +// DNS lookup on a reused conn, no TLS on http://). +type phaseTimings struct { + dns time.Duration + dial time.Duration + tls time.Duration + ttfb time.Duration // time from GotConn to first response byte +} + +// tlsResult captures whatever we learned from TLSHandshakeDone. +// On the happy path err is nil and state is fully populated; on +// failure state may be partial (and is what the TLS diagnostic +// mode in httptrace_tls.go works from). +type tlsResult struct { + state tls.ConnectionState + err error + done bool +} + +const tracePrefix = "[trace] " + +// staleIdleThreshold is the idle duration above which a reused +// pooled connection earns a HEADS-UP annotation. Per-runtime +// configurability is deferred to v2; 30s matches the issue #336 +// territory (typical NAT idle timeouts start in the 60–350s +// range, so a 30s reuse is already in "could be stale" zone). +const staleIdleThreshold = 30 * time.Second + +// newTraceSession allocates a session and pre-renders the opening +// line (method + url). The session is not yet attached to a +// context — that's the caller's responsibility via session.attach. +// +// tlsCfg may be nil; when non-nil it is used by the TLS diagnostic +// mode to cross-check user-configured constraints (MinVersion, +// CipherSuites, custom RootCAs) against handshake failures. +func newTraceSession(log logger.Logger, method, url string, tlsCfg *tls.Config) *traceSession { + s := &traceSession{ + logger: log, + method: method, + url: url, + tlsCfg: tlsCfg, + start: time.Now(), + } + s.last = s.start + s.emitf("%s %s", method, url) + return s +} + +// attach installs the session's ClientTrace on ctx and returns the +// derived context. Callers pass the returned context to +// http.Client.Do (typically by setting it on req via +// req.WithContext) so the transport fires the hooks. +func (s *traceSession) attach(ctx context.Context) context.Context { + return httptrace.WithClientTrace(ctx, s.clientTrace()) +} + +// clientTrace wires every httptrace hook to the corresponding +// session method. Each callback is responsible for its own +// locking; the stdlib does not serialize trace callbacks. +func (s *traceSession) clientTrace() *httptrace.ClientTrace { + return &httptrace.ClientTrace{ + GetConn: s.onGetConn, + GotConn: s.onGotConn, + PutIdleConn: s.onPutIdleConn, + GotFirstResponseByte: s.onGotFirstResponseByte, + Got100Continue: s.onGot100Continue, + DNSStart: s.onDNSStart, + DNSDone: s.onDNSDone, + ConnectStart: s.onConnectStart, + ConnectDone: s.onConnectDone, + TLSHandshakeStart: s.onTLSHandshakeStart, + TLSHandshakeDone: s.onTLSHandshakeDone, + WroteHeaders: s.onWroteHeaders, + Wait100Continue: s.onWait100Continue, + WroteRequest: s.onWroteRequest, + } +} + +// --------------------------------------------------------------- +// Phase callbacks (stdlib httptrace hooks) +// --------------------------------------------------------------- + +func (s *traceSession) onGetConn(hostPort string) { + s.emitTf("GetConn(%s)", hostPort) +} + +func (s *traceSession) onGotConn(info httptrace.GotConnInfo) { + s.mu.Lock() + s.gotConn = info + s.gotConnAt = time.Now() + s.mu.Unlock() + + if info.Reused { + s.emitTf("GotConn(reused=true, idle=%t, idle-time=%s)", + info.WasIdle, info.IdleTime.Round(time.Millisecond)) + } else { + s.emitTf("GotConn(reused=false)") + } + + if isStaleIdleReuse(info) { + s.emitf("# HEADS-UP: reused idle connection (idle for %s).", + info.IdleTime.Round(time.Second)) + s.emitf("# If this request fails with EOF/connection reset, the server") + s.emitf("# or an in-path NAT may have dropped the conn silently.") + } +} + +// isStaleIdleReuse reports whether a GotConn info indicates the +// connection came from the idle pool after sitting idle for +// longer than [staleIdleThreshold]. This is the issue #336 +// pattern: long-idle pooled conns are the ones most likely to be +// dead by the time the next request tries to use them. +func isStaleIdleReuse(info httptrace.GotConnInfo) bool { + return info.Reused && info.WasIdle && info.IdleTime > staleIdleThreshold +} + +func (s *traceSession) onPutIdleConn(err error) { + if err != nil { + s.emitTf("PutIdleConn(err=%v)", err) + return + } + s.emitTf("PutIdleConn") +} + +func (s *traceSession) onGotFirstResponseByte() { + s.mu.Lock() + s.ttfbAt = time.Now() + if !s.gotConnAt.IsZero() { + s.phases.ttfb = s.ttfbAt.Sub(s.gotConnAt) + } + s.mu.Unlock() + s.emitTf("GotFirstResponseByte (TTFB)") +} + +func (s *traceSession) onGot100Continue() { + s.emitTf("Got100Continue") +} + +func (s *traceSession) onDNSStart(info httptrace.DNSStartInfo) { + s.mu.Lock() + s.dnsStartAt = time.Now() + s.mu.Unlock() + s.emitTf("DNSStart(host=%s)", info.Host) +} + +func (s *traceSession) onDNSDone(info httptrace.DNSDoneInfo) { + s.mu.Lock() + if !s.dnsStartAt.IsZero() { + s.phases.dns = time.Since(s.dnsStartAt) + } + s.mu.Unlock() + + addrs := make([]string, 0, len(info.Addrs)) + for _, a := range info.Addrs { + addrs = append(addrs, a.String()) + } + if info.Err != nil { + s.emitTf("DNSDone(err=%v, addrs=[%s], coalesced=%t)", + info.Err, strings.Join(addrs, " "), info.Coalesced) + return + } + s.emitTf("DNSDone(addrs=[%s], coalesced=%t)", + strings.Join(addrs, " "), info.Coalesced) +} + +func (s *traceSession) onConnectStart(network, addr string) { + s.mu.Lock() + s.connectStartAt = time.Now() + s.mu.Unlock() + s.emitTf("ConnectStart(%s %s)", network, addr) +} + +func (s *traceSession) onConnectDone(network, addr string, err error) { + s.mu.Lock() + if !s.connectStartAt.IsZero() { + s.phases.dial = time.Since(s.connectStartAt) + } + s.mu.Unlock() + + if err != nil { + s.emitTf("ConnectDone(%s %s, err=%v)", network, addr, err) + return + } + s.emitTf("ConnectDone(%s %s)", network, addr) +} + +func (s *traceSession) onTLSHandshakeStart() { + s.mu.Lock() + s.tlsHandshakeStartAt = time.Now() + s.mu.Unlock() + s.emitTf("TLSHandshakeStart") +} + +func (s *traceSession) onTLSHandshakeDone(state tls.ConnectionState, err error) { + s.mu.Lock() + if !s.tlsHandshakeStartAt.IsZero() { + s.phases.tls = time.Since(s.tlsHandshakeStartAt) + } + s.tlsDone = tlsResult{state: state, err: err, done: true} + s.mu.Unlock() + + if err != nil { + s.emitTf("TLSHandshakeDone(err=%v)", err) + s.emitTLSDiagnostic(state, err) + return + } + s.emitTf("TLSHandshakeDone(tls=%s, cipher=%s, server=%s%s)", + tlsVersionName(state.Version), + tls.CipherSuiteName(state.CipherSuite), + state.ServerName, + certExpiryFragment(state), + ) +} + +func (s *traceSession) onWroteHeaders() { + s.mu.Lock() + s.wroteHeadersAt = time.Now() + s.mu.Unlock() + s.emitTf("WroteHeaders") +} + +func (s *traceSession) onWait100Continue() { + s.mu.Lock() + s.wait100StartAt = time.Now() + s.mu.Unlock() + s.emitTf("Wait100Continue") +} + +func (s *traceSession) onWroteRequest(info httptrace.WroteRequestInfo) { + s.mu.Lock() + s.wroteRequestAt = time.Now() + s.mu.Unlock() + + if info.Err != nil { + s.emitTf("WroteRequest(err=%v)", info.Err) + return + } + s.emitTf("WroteRequest") +} + +// --------------------------------------------------------------- +// Body wrapping +// --------------------------------------------------------------- + +// bodySide identifies which direction an instrumented body is on. +type bodySide string + +const ( + bodySend bodySide = "Sent" + bodyRecv bodySide = "Received" +) + +// instrumentedBody wraps an [io.ReadCloser] and emits a +// BodyChunk{Sent,Received} trace event per Read call. Tracks the +// inter-read delay in `dt` so users can see streaming-body +// cadence. +// +// Read granularity: bytes returned by the underlying body, not +// HTTP/1.1 chunked-framing units. For wire-level chunking, use +// [Runtime.Debug] instead. +// +// Concurrency: a single body is read from a single goroutine in +// practice (http.Transport for request bodies, the application +// for response bodies), so no internal locking is needed beyond +// what the underlying ReadCloser provides. +type instrumentedBody struct { + wrapped io.ReadCloser + sess *traceSession + side bodySide + last time.Time +} + +func (b *instrumentedBody) Read(p []byte) (int, error) { + n, err := b.wrapped.Read(p) + if n > 0 { + first := b.last.IsZero() + var dt time.Duration + if !first { + dt = time.Since(b.last) + } + b.last = time.Now() + b.sess.onBodyChunk(b.side, n, dt, first) + } + return n, err +} + +func (b *instrumentedBody) Close() error { + return b.wrapped.Close() +} + +// wrapRequestBody returns an instrumented wrapper around the +// outgoing request body, or the original body if nil (which is +// the common case for GET requests). The wrapper observes +// Transport-side reads, so BodyChunkSent events appear between +// WroteHeaders and WroteRequest in the trace timeline. +func (s *traceSession) wrapRequestBody(body io.ReadCloser) io.ReadCloser { + if body == nil { + return nil + } + return &instrumentedBody{wrapped: body, sess: s, side: bodySend} +} + +// wrapResponseBody returns an instrumented wrapper around the +// incoming response body. Stacks cleanly above +// [KeepAliveTransport]'s drain-on-close behavior. +func (s *traceSession) wrapResponseBody(body io.ReadCloser) io.ReadCloser { + if body == nil { + return nil + } + return &instrumentedBody{wrapped: body, sess: s, side: bodyRecv} +} + +// onBodyChunk renders a single BodyChunk{Sent,Received} event. +// dt is the duration since the previous Read on the same body and +// is meaningful only when `first` is false. The first chunk has no +// preceding read, so the dt= field is suppressed; every subsequent +// chunk emits dt= unconditionally — even when the measured value +// rounds to zero (common on Windows, where the system clock +// resolution is coarser than a fast loopback read loop). +func (s *traceSession) onBodyChunk(side bodySide, n int, dt time.Duration, first bool) { + if first { + s.emitTf("BodyChunk%s(n=%d)", side, n) + return + } + s.emitTf("BodyChunk%s(n=%d, dt=%s)", side, n, round(dt)) +} + +// --------------------------------------------------------------- +// Submit-level lifecycle hooks (called from SubmitContext) +// --------------------------------------------------------------- + +// onRoundTripError is called by SubmitContext when http.Client.Do +// returns an error. It records the error for the summary line. +func (s *traceSession) onRoundTripError(err error) { + s.mu.Lock() + s.rtError = err + s.mu.Unlock() + s.emitTf("! error: %v", err) +} + +// onResponse is called when http.Client.Do returns successfully. +// It records the status code for the summary line. +func (s *traceSession) onResponse(statusCode int) { + s.mu.Lock() + s.statusCode = statusCode + s.mu.Unlock() +} + +// finish renders the trailing single-line summary and is called +// by SubmitContext after the response body has been consumed (or +// on error path, after the error was recorded). When a round-trip +// error happened on a stale-idle reused connection, a tail block +// flags the issue #336 pattern explicitly. +func (s *traceSession) finish() { + s.mu.Lock() + defer s.mu.Unlock() + + total := time.Since(s.start) + var b strings.Builder + fmt.Fprintf(&b, "Summary: %s — ", s.method) + if s.rtError != nil { + fmt.Fprintf(&b, "FAILED (%v)", s.rtError) + } else { + fmt.Fprintf(&b, "%d", s.statusCode) + } + if s.phases.dns > 0 { + fmt.Fprintf(&b, ", dns=%s", round(s.phases.dns)) + } + if s.phases.dial > 0 { + fmt.Fprintf(&b, ", dial=%s", round(s.phases.dial)) + } + if s.phases.tls > 0 { + fmt.Fprintf(&b, ", tls=%s", round(s.phases.tls)) + } + if s.phases.ttfb > 0 { + fmt.Fprintf(&b, ", ttfb=%s", round(s.phases.ttfb)) + } + fmt.Fprintf(&b, ", total=%s", round(total)) + + s.emitRaw(b.String()) + + // issue #336 tail annotation: a round-trip failure on a + // stale-idle reused conn is the canonical pattern. + if s.rtError != nil && isStaleIdleReuse(s.gotConn) { + s.emitf("# FAILED on a reused idle conn (%s idle).", + s.gotConn.IdleTime.Round(time.Second)) + s.emitf("# Silently closed the conn while it sat in the idle pool.") + s.emitf("# Consider lowering http.Transport.IdleConnTimeout to evict") + s.emitf("# pooled conns before the NAT/server side does.") + } +} + +// --------------------------------------------------------------- +// Emission helpers +// --------------------------------------------------------------- + +// emitf prints a plain event line (no t= timestamp). Used for the +// opening line and the summary. +func (s *traceSession) emitf(format string, args ...any) { + s.logger.Debugf(tracePrefix+format, args...) +} + +// emitRaw is like emitf but takes an already-rendered string. Used +// by finish() which builds its line via strings.Builder. +func (s *traceSession) emitRaw(line string) { + s.logger.Debugf("%s", tracePrefix+line) +} + +// emitTf prints a phase event with a cumulative t=... offset from +// the session start. +func (s *traceSession) emitTf(format string, args ...any) { + t := round(time.Since(s.start)) + msg := fmt.Sprintf(format, args...) + s.logger.Debugf(tracePrefix+"%s (t=%s)", msg, t) +} + +// traceRoundUnit is the rounding granularity for >=1ms durations +// rendered in trace output. 100µs keeps lines readable while +// preserving enough resolution to spot millisecond-scale phase +// differences. +const traceRoundUnit = 100 * time.Microsecond + +// round trims durations for human-readable trace output. +// Sub-millisecond durations round to 1µs (preserves visibility on +// fast loopback servers); >=1ms durations round to [traceRoundUnit]. +func round(d time.Duration) time.Duration { + if d <= 0 { + return 0 + } + if d < time.Millisecond { + return d.Round(time.Microsecond) + } + return d.Round(traceRoundUnit) +} + +// --------------------------------------------------------------- +// TLS rendering helpers +// --------------------------------------------------------------- + +func tlsVersionName(v uint16) string { + switch v { + case tls.VersionTLS10: + return "1.0" + case tls.VersionTLS11: + return "1.1" + case tls.VersionTLS12: + return "1.2" + case tls.VersionTLS13: + return "1.3" + default: + return fmt.Sprintf("0x%04x", v) + } +} + +// certExpiryFragment renders ", expires=YYYY-MM-DD" for the leaf +// cert when available, or an empty string otherwise. +func certExpiryFragment(state tls.ConnectionState) string { + if len(state.PeerCertificates) == 0 { + return "" + } + return ", expires=" + state.PeerCertificates[0].NotAfter.UTC().Format("2006-01-02") +} diff --git a/vendor/github.com/go-openapi/runtime/client/httptrace_tls.go b/vendor/github.com/go-openapi/runtime/client/httptrace_tls.go new file mode 100644 index 0000000000..063fb25927 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/client/httptrace_tls.go @@ -0,0 +1,353 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package client + +import ( + "crypto/tls" + "crypto/x509" + "errors" + "fmt" + "net/http" + "strings" + "time" +) + +// TLS alert codes used by the diagnostic to classify handshake +// failures. The crypto/tls package does not export named constants +// for individual alerts, so we declare the ones we care about. +// Values are from RFC 8446 §6 (the TLS 1.3 alert protocol; the +// numbering is shared with earlier TLS versions for these alerts). +// +// The `err`-prefixed names satisfy the errname linter — tls.AlertError +// implements error, so these are sentinel errors. +const ( + errTLSAlertHandshakeFailure tls.AlertError = 40 + errTLSAlertProtocolVersion tls.AlertError = 70 +) + +// introspectTLSConfig returns the *tls.Config of the http.Transport +// that will run a request, when reachable, or nil otherwise. +// +// Reachable means the client's Transport is an *http.Transport +// (the default and most common case). Custom transports — wrappers +// around the default, or entirely user-provided — break introspection; +// the TLS diagnostic falls back to "configured: not introspectable" +// in that case. +// +// A nil client (zero value) or nil Transport falls through to +// [http.DefaultTransport], whose TLSClientConfig is also nil; the +// function returns nil and the diagnostic reports defaults. +func introspectTLSConfig(client *http.Client) *tls.Config { + if client == nil { + return nil + } + transport := client.Transport + if transport == nil { + transport = http.DefaultTransport + } + t, ok := transport.(*http.Transport) + if !ok { + return nil + } + return t.TLSClientConfig +} + +// emitTLSDiagnostic renders the failure-mode TLS diagnostic block. +// Called from [traceSession.onTLSHandshakeDone] when err != nil. +// +// The block covers three axes (per the plan): +// +// 1. Protocol-version negotiation — detected from +// [errTLSAlertProtocolVersion] or a "protocol version" substring. +// 2. Cipher-suite negotiation — detected from +// [errTLSAlertHandshakeFailure] when the user pinned CipherSuites. +// 3. Certificate-chain validity — detected from +// [x509.CertificateInvalidError], [x509.UnknownAuthorityError] +// or [x509.HostnameError]. +// +// When none of the specific axes match, a generic fallback emits +// the raw error and whatever inspectable config the session holds. +func (s *traceSession) emitTLSDiagnostic(state tls.ConnectionState, err error) { + s.emitf("# TLS DIAGNOSTIC") + + // tlsAxisGeneric is handled by the default branch. + switch axis := classifyTLSError(err); axis { + case tlsAxisProtocolVersion: + s.diagnoseProtocolVersion(state, err) + case tlsAxisCipher: + s.diagnoseCipher(err) + case tlsAxisCertChain: + s.diagnoseCertChain(err) + default: + s.diagnoseTLSGeneric(err) + } +} + +// tlsAxis is the diagnostic dimension a TLS handshake error maps +// to. Axes are mutually exclusive at classification time. +type tlsAxis int + +const ( + tlsAxisGeneric tlsAxis = iota + tlsAxisProtocolVersion + tlsAxisCipher + tlsAxisCertChain +) + +// classifyTLSError maps a TLS handshake error to one of the +// diagnostic axes. The ordering matters: cert-chain errors win +// over the generic handshake_failure alert because the alert is +// what the server sends back, but the local error type carries +// the more specific reason. +func classifyTLSError(err error) tlsAxis { + if err == nil { + return tlsAxisGeneric + } + + // Cert-chain errors are the most specific local diagnostic + // and should be reported even if a generic alert is also + // present in the chain. + var certInvalid x509.CertificateInvalidError + if errors.As(err, &certInvalid) { + return tlsAxisCertChain + } + var unknownAuth x509.UnknownAuthorityError + if errors.As(err, &unknownAuth) { + return tlsAxisCertChain + } + var hostnameErr x509.HostnameError + if errors.As(err, &hostnameErr) { + return tlsAxisCertChain + } + + // TLS alert classification. + var alert tls.AlertError + if errors.As(err, &alert) { + switch alert { + case errTLSAlertProtocolVersion: + return tlsAxisProtocolVersion + case errTLSAlertHandshakeFailure: + return tlsAxisCipher + } + } + + // Fall back on substring detection for protocol-version + // failures that arrive via the local error path rather than + // a server-side alert (e.g. when the client refuses the + // server's offered version). + msg := err.Error() + if strings.Contains(msg, "protocol version") || strings.Contains(msg, "unsupported protocol") { + return tlsAxisProtocolVersion + } + + return tlsAxisGeneric +} + +// --------------------------------------------------------------- +// Axis renderers +// --------------------------------------------------------------- + +func (s *traceSession) diagnoseProtocolVersion(state tls.ConnectionState, err error) { + s.emitf("# axis: protocol-version") + s.emitf("# error: %v", err) + + configuredMin, configuredMax := configuredVersionRange(s.tlsCfg) + s.emitf("# client offered: TLS %s — TLS %s", + tlsVersionName(configuredMin), tlsVersionName(configuredMax)) + + if state.Version != 0 { + s.emitf("# negotiated up to: TLS %s", tlsVersionName(state.Version)) + } + s.emitf("# suggested: widen TLSClientOptions.MinVersion/MaxVersion,") + s.emitf("# or pin to a version the server speaks.") +} + +func (s *traceSession) diagnoseCipher(err error) { + s.emitf("# axis: cipher-suite") + s.emitf("# error: %v", err) + + if s.tlsCfg != nil && len(s.tlsCfg.CipherSuites) > 0 { + s.emitf("# client configured: [%s]", + strings.Join(cipherSuiteNames(s.tlsCfg.CipherSuites), ", ")) + s.emitf("# server set: not exposed by Go stdlib") + s.emitf("# (capture with: openssl s_client -cipher ALL)") + s.emitf("# suggested: drop the explicit CipherSuites restriction,") + s.emitf("# or align it with the server's policy.") + return + } + // No client-side restriction. The handshake_failure alert + // is generic; without more info we can only surface the + // fact and suggest investigation. + s.emitf("# client configured: defaults (no CipherSuites restriction)") + s.emitf("# note: alert 40 is generic; the server may have rejected") + s.emitf("# the handshake for a non-cipher reason. Try") + s.emitf("# openssl s_client to capture details.") +} + +func (s *traceSession) diagnoseCertChain(err error) { + s.emitf("# axis: cert-chain") + + var certInvalid x509.CertificateInvalidError + if errors.As(err, &certInvalid) { + s.diagnoseCertInvalid(certInvalid) + return + } + + var unknownAuth x509.UnknownAuthorityError + if errors.As(err, &unknownAuth) { + s.diagnoseUnknownAuthority(unknownAuth) + return + } + + var hostnameErr x509.HostnameError + if errors.As(err, &hostnameErr) { + s.diagnoseHostnameMismatch(hostnameErr) + return + } + + // Defensive: should not happen — classifyTLSError already + // matched one of the three. + s.emitf("# error: %v", err) +} + +func (s *traceSession) diagnoseCertInvalid(certInvalid x509.CertificateInvalidError) { + cert := certInvalid.Cert + s.emitf("# reason: %s", certInvalidReasonName(certInvalid.Reason)) + + switch certInvalid.Reason { + case x509.Expired: + s.emitf("# leaf: subject=%s", cert.Subject) + s.emitf("# NotBefore=%s", cert.NotBefore.UTC().Format(time.RFC3339)) + s.emitf("# NotAfter=%s", cert.NotAfter.UTC().Format(time.RFC3339)) + s.emitf("# now=%s", time.Now().UTC().Format(time.RFC3339)) + delta := time.Since(cert.NotAfter).Round(time.Hour) + s.emitf("# expired %s ago", delta) + s.emitf("# suggested: renew the server cert.") + case x509.NameMismatch, x509.CANotAuthorizedForThisName: + s.emitf("# leaf: subject=%s", cert.Subject) + s.emitf("# DNS SANs=%v", cert.DNSNames) + s.emitf("# suggested: set TLSClientOptions.ServerName to match") + s.emitf("# one of the cert SANs, or fix the cert.") + default: + // Less-common reasons render via the default branch (issuer + NotAfter dump). + s.emitf("# leaf: subject=%s, issuer=%s", cert.Subject, cert.Issuer) + s.emitf("# NotBefore=%s", cert.NotBefore.UTC().Format(time.RFC3339)) + s.emitf("# NotAfter=%s", cert.NotAfter.UTC().Format(time.RFC3339)) + s.emitf("# error: %v", certInvalid) + } +} + +func (s *traceSession) diagnoseUnknownAuthority(unknownAuth x509.UnknownAuthorityError) { + s.emitf("# reason: chain root not in trust store (unknown-CA)") + if cert := unknownAuth.Cert; cert != nil { + s.emitf("# offending: subject=%s", cert.Subject) + s.emitf("# issuer=%s", cert.Issuer) + s.emitf("# NotAfter=%s", cert.NotAfter.UTC().Format(time.RFC3339)) + } + + trust := "SystemCertPool" + if s.tlsCfg != nil && s.tlsCfg.RootCAs != nil { + trust = "TLSClientOptions.CA (custom RootCAs)" + } + s.emitf("# trust store in use: %s", trust) + + s.emitf("# suggested: set TLSClientOptions.CA to a bundle that") + s.emitf("# includes the issuing CA, or add it to the") + s.emitf("# OS trust store.") +} + +func (s *traceSession) diagnoseHostnameMismatch(hostnameErr x509.HostnameError) { + s.emitf("# reason: hostname mismatch") + s.emitf("# dialed: %s", hostnameErr.Host) + if cert := hostnameErr.Certificate; cert != nil { + s.emitf("# leaf: subject=%s", cert.Subject) + s.emitf("# DNS SANs=%v", cert.DNSNames) + s.emitf("# IP SANs=%v", cert.IPAddresses) + } + if s.tlsCfg != nil && s.tlsCfg.ServerName != "" { + s.emitf("# TLSClientOptions.ServerName=%q", s.tlsCfg.ServerName) + } + s.emitf("# suggested: dial the hostname listed in the cert SANs,") + s.emitf("# or set TLSClientOptions.ServerName to match.") +} + +func (s *traceSession) diagnoseTLSGeneric(err error) { + s.emitf("# axis: unclassified") + s.emitf("# error: %v", err) + if s.tlsCfg != nil { + minV, maxV := configuredVersionRange(s.tlsCfg) + s.emitf("# configured: MinVersion=TLS %s, MaxVersion=TLS %s", + tlsVersionName(minV), tlsVersionName(maxV)) + if s.tlsCfg.InsecureSkipVerify { + s.emitf("# note: TLSClientOptions.InsecureSkipVerify=true — yet") + s.emitf("# a TLS error still surfaced. Something deeper than") + s.emitf("# certificate verification is failing.") + } + } +} + +// --------------------------------------------------------------- +// Helpers +// --------------------------------------------------------------- + +// configuredVersionRange returns the effective (Min, Max) TLS +// version range a client config negotiates. Zero values in the +// stdlib config mean "use Go default", which is TLS 1.2 .. 1.3 in +// modern Go. We materialize those defaults for display. +func configuredVersionRange(cfg *tls.Config) (uint16, uint16) { + const ( + defaultMin = tls.VersionTLS12 + defaultMax = tls.VersionTLS13 + ) + if cfg == nil { + return defaultMin, defaultMax + } + minV := cfg.MinVersion + if minV == 0 { + minV = defaultMin + } + maxV := cfg.MaxVersion + if maxV == 0 { + maxV = defaultMax + } + return minV, maxV +} + +func cipherSuiteNames(ids []uint16) []string { + out := make([]string, 0, len(ids)) + for _, id := range ids { + out = append(out, tls.CipherSuiteName(id)) + } + return out +} + +// certInvalidReasonName renders an x509.InvalidReason as a short +// human-readable label. The stdlib does not expose a String() +// method for these, so we keep a small table. +// +// Anything outside the listed cases falls through to the numeric default. +func certInvalidReasonName(r x509.InvalidReason) string { + switch r { + case x509.NotAuthorizedToSign: + return "not-authorized-to-sign" + case x509.Expired: + return "expired" + case x509.CANotAuthorizedForThisName: + return "ca-not-authorized-for-this-name" + case x509.TooManyIntermediates: + return "too-many-intermediates" + case x509.IncompatibleUsage: + return "incompatible-usage" + case x509.NameMismatch: + return "name-mismatch" + case x509.NameConstraintsWithoutSANs: + return "name-constraints-without-sans" + case x509.TooManyConstraints: + return "too-many-constraints" + case x509.CANotAuthorizedForExtKeyUsage: + return "ca-not-authorized-for-ext-key-usage" + default: + return fmt.Sprintf("invalid-reason-%d", r) + } +} diff --git a/vendor/github.com/go-openapi/runtime/client/internal/request/request.go b/vendor/github.com/go-openapi/runtime/client/internal/request/request.go new file mode 100644 index 0000000000..22d3f64c01 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/client/internal/request/request.go @@ -0,0 +1,945 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package request + +import ( + "bytes" + "context" + "errors" + "fmt" + "io" + "log" + "mime" + "mime/multipart" + "net/http" + "net/textproto" + "net/url" + "os" + "path" + "path/filepath" + "strings" + "time" + + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" +) + +var _ runtime.ClientRequest = new(Request) // ensure compliance to the interface + +// Request represents a swagger client request. +// It binds parameters to a HTTP request. +// +// The main purpose of this struct is to hide the machinery of adding OpenAPI v2 parameters to a transport request. +// +// A generated client only implements what is necessary to turn a parameter into a valid value for these methods. +// +// There is no parameter validation here, it is assumed to be used after a spec has been validated. +// +// # Request binding +// +// The binding of parameters is carried out by method [Request.BuildHTTPContext]. +// +// It analyzes parameters, which may come in different flavors: +// +// - a file or multipart form containing a file +// - a body which is a [io.Reader] +// - a buffered body (regular schema body, including urlencoded form) +// +// In all cases, we may also have query or path parameters encoded in the URL, or header parameters. +// +// The result is a [http.Request], with the following properties: +// +// - file, multipart form or [io.Reader] body: a streaming request with an attached go routine that consumes the [io.Reader]. +// - buffered body: a simple request +// +// The caller passes the parent [context.Context] to [Request.BuildHTTPContext] and receives back a cancel +// function to release the resources held by the derived request context once the response is consumed. +// +// # Authentication +// +// Authentication is built in the request by using a [runtime.ClientAuthInfoWriter]. +// This helper may need to inspect the body of the request before sending authentication info. +// To cover that case, streaming bodies use a copy of the body [io.Reader] for the [runtime.ClientAuthInfoWriter] +// to consume if it wants to. +// +// # Content negotiation +// +// The [Request] detects `multipart/form-data` to switch to streamed request. +// +// `application/x-www-form-urlencoded` is also honored, even for file parameters, which are not streamed in this case. +// File parameters default behavior is `multipart/form-data`. +// +// The natural way to define the `Content-Type` header is to use the `contentType` parameter to switch to the map of +// available body producers. +// +// For buffered requests, this setting override any `Content-Type` header possibly set by calling [Request.SetHeaderParam]. +// +// For streamed requests, users may want more flexibility, as we enter custom territory, with use-cases not supported by OpenAPI v2. +// +// The `Content-Type` header of a streamed request is defined using the following sequence: +// +// 1. if the caller sets an explicit value already in header — the user set it via +// [Request.SetHeaderParam] during WriteToRequest, and we treat that as an intentional escape hatch +// 2. use payload's [runtime.ContentTyper] declaration (in this case, the produced payload knows its content type) +// 3. use `application/octet-stream` if it is available in the registered producers +// 4. otherwise set the picker's mediaType +// +// For multi-part requests, the content type of each part is auto-detected using the following sequence: +// +// 1. use [runtime.ContentTyper] declaration (in this case, the file payload knows its content type) +// 2. use [http.DetectContentType] on the first 512 bytes of the file +// +// # Concurrency +// +// A [Request] is a disposable object that is NOT intended to be reused or called concurrently. +// +// # Future evolutions +// +// There might be other similar structs that convert to other transports. +type Request struct { + pathPattern string + method string + writer runtime.ClientRequestWriter + + pathParams map[string]string + header http.Header + query url.Values + formFields url.Values + fileFields map[string][]runtime.NamedReadCloser + payload any + // consumes carries the operation's full ConsumesMediaTypes list so + // that buildHTTP — which runs after the writer populates the payload + // — can apply payload-aware fallback rules (see streamFallbackMime). + // + // This is set by Runtime.createHttpRequest. + consumes []string + timeout time.Duration + buf *bytes.Buffer + + getBody func(r *Request) []byte +} + +// New creates a new http client [Request] to handle OpenAPI v2 parameters. +func New(method, pathPattern string, writer runtime.ClientRequestWriter) *Request { + return &Request{ + pathPattern: pathPattern, + method: method, + writer: writer, + header: make(http.Header), + query: make(url.Values), + timeout: 0, + getBody: getRequestBuffer, + } +} + +// GetMethod yields the method being used. +func (r *Request) GetMethod() string { + return r.method +} + +// GetPath yields the URL path being used. +func (r *Request) GetPath() string { + pth := r.pathPattern + for k, v := range r.pathParams { + pth = strings.ReplaceAll(pth, "{"+k+"}", v) + } + + return pth +} + +// GetBody returns the request body, if any. +// +// For streaming requests, this is a copy of the original [io.Reader]. +func (r *Request) GetBody() []byte { + return r.getBody(r) +} + +// SetHeaderParam adds a header parameter to the request. +// +// The header key is always canonicalized. +// +// - when there is only 1 value provided, it will set it. +// - when there are several values provided, it will add all of those (no overriding). +func (r *Request) SetHeaderParam(name string, values ...string) error { + if r.header == nil { + r.header = make(http.Header) + } + r.header[http.CanonicalHeaderKey(name)] = values + + return nil +} + +// GetHeaderParams returns all headers currently set for the request. +func (r *Request) GetHeaderParams() http.Header { + return r.header +} + +// SetQueryParam adds a query parameter to the request. +// +// - when there is only 1 value provided, it will set it. +// - when there are several values provided, it will add all of those (no overriding). +func (r *Request) SetQueryParam(name string, values ...string) error { + if r.query == nil { + r.query = make(url.Values) + } + r.query[name] = values + + return nil +} + +// GetQueryParams returns a copy of all query params currently set for the request. +func (r *Request) GetQueryParams() url.Values { + result := make(url.Values, len(r.query)) + for key, values := range r.query { + result[key] = append([]string{}, values...) + } + + return result +} + +// SetFormParam adds a form param to the request. +// +// - when there is only 1 value provided, it will set it. +// - when there are several values provided, it will add all of those (no overriding). +func (r *Request) SetFormParam(name string, values ...string) error { + if r.formFields == nil { + r.formFields = make(url.Values) + } + r.formFields[name] = values + + return nil +} + +// SetPathParam adds a path param to the request. +func (r *Request) SetPathParam(name string, value string) error { + if r.pathParams == nil { + r.pathParams = make(map[string]string) + } + + r.pathParams[name] = value + + return nil +} + +// SetFileParam adds a file parameter to the request. +// +// Files must implement [runtime.NamedReadCloser]. +// +// [runtime.File] is proposed as the default concrete implementation. +func (r *Request) SetFileParam(name string, files ...runtime.NamedReadCloser) error { + for _, file := range files { + if actualFile, ok := file.(*os.File); ok { + fi, err := os.Stat(actualFile.Name()) + if err != nil { + return err + } + + if fi.IsDir() { + return fmt.Errorf("%q is a directory, only files are supported", file.Name()) + } + } + } + + if r.fileFields == nil { + r.fileFields = make(map[string][]runtime.NamedReadCloser) + } + + if r.formFields == nil { + r.formFields = make(url.Values) + } + + r.fileFields[name] = files + + return nil +} + +// GetFileParam yields all file parameters. +func (r *Request) GetFileParam() map[string][]runtime.NamedReadCloser { + return r.fileFields +} + +// SetBodyParam sets a body parameter on the request. +// +// This does not yet serialize the object: actual serialization happens as late as possible. +func (r *Request) SetBodyParam(payload any) error { + r.payload = payload + + return nil +} + +// GetBodyParam returns the body payload. +func (r *Request) GetBodyParam() any { + return r.payload +} + +// GetTimeout sets the timeout for a request. +func (r *Request) GetTimeout() time.Duration { + return r.timeout +} + +// SetTimeout sets the timeout for a request. +func (r *Request) SetTimeout(timeout time.Duration) error { + r.timeout = timeout + + return nil +} + +// SetConsumes sets the list of registered consumed content for a request. +func (r *Request) SetConsumes(consumers []string) { + r.consumes = consumers +} + +// BuildHTTPContext binds the request parameters and returns a ready-to-send [http.Request]. +// +// Dispatch picks one of two end-to-end builders based on whether: +// +// - the body source is a stream (multipart pipe or stream payload) +// - or a buffer (urlencoded form, producer output, or no body) +// +// It starts by writing the request, then proceed with adding authentication, +// then finally assembling URL or header parameters. +// +// The split mirrors the auth question: streaming bodies require a lazy body-copy closure during [AuthenticateRequest], +// whereas buffered bodies do not. +// +// The returned [http.Request] carries a context derived from parentCtx that: +// +// - inherits any deadline or cancellation already set on parentCtx; +// - additionally honors the per-request timeout set via [Request.SetTimeout] +// (the [runtime.ClientRequestWriter] may override the runtime default during +// WriteToRequest, which is why the derivation happens here rather than +// at the call site). +// +// The returned cancel must be invoked by the caller (typically deferred) +// once the response has been fully read; otherwise resources held by the +// derived context — including any timeout timer — are leaked. +// +// On error the cancel is invoked internally and a no-op cancel is returned, +// so callers can defer cancel unconditionally. +func (r *Request) BuildHTTPContext(parentCtx context.Context, mediaType, basePath string, + producers map[string]runtime.Producer, registry strfmt.Registry, auth runtime.ClientAuthInfoWriter, +) (*http.Request, context.CancelFunc, error) { + if err := r.writer.WriteToRequest(r, registry); err != nil { + return nil, noop, err + } + + ctx, cancel := deriveRequestContext(parentCtx, r.timeout) + r.buf = bytes.NewBuffer(nil) + + var ( + httpReq *http.Request + err error + ) + if r.usesStreamingBody(mediaType) { + httpReq, err = r.buildStreamingRequest(ctx, mediaType, basePath, producers, registry, auth) + } else { + httpReq, err = r.buildBufferedRequest(ctx, mediaType, basePath, producers, registry, auth) + } + if err != nil { + cancel() + return nil, noop, err + } + return httpReq, cancel, nil +} + +func noop() {} + +// deriveRequestContext returns a child of parent bounded by timeout. +// If timeout == 0 the child is only canceled when the caller invokes +// cancel; any deadline already on parent is preserved. If timeout > 0 +// the child uses the shortest of timeout and parent's existing deadline. +func deriveRequestContext(parent context.Context, timeout time.Duration) (context.Context, context.CancelFunc) { + if timeout == 0 { + return context.WithCancel(parent) + } + return context.WithTimeout(parent, timeout) +} + +// usesStreamingBody reports whether the request body must be assembled +// as a stream (an io.Pipe for multipart, or the payload's own reader +// for stream payloads). +// +// The complementary case is a fully buffered body in r.buf — urlencoded form, producer output, or no body at all. +func (r *Request) usesStreamingBody(mediaType string) bool { + if (len(r.formFields) > 0 || len(r.fileFields) > 0) && r.isMultipart(mediaType) { + return true + } + + if r.payload != nil { + if _, ok := r.payload.(io.Reader); ok { + return true + } + } + + return false +} + +func (r *Request) isMultipart(mediaType string) bool { + // Strip media-type parameters before comparing: callers may legally + // pass `multipart/form-data; boundary=…` or + // `application/x-www-form-urlencoded; charset=utf-8` per RFC 7231, + // and a bare-string compare would route those to the wrong flow. + // + // mime.ParseMediaType lowercases the type/subtype and is + // case-insensitive on input, so plain == against our (lowercase) + // constants is sufficient on the happy path. + base, _, err := mime.ParseMediaType(mediaType) + if err != nil { + // Malformed mediaType: only the file-presence shortcut can + // fire — by definition we cannot recognize either canonical + // form mime in unparseable input. + return len(r.fileFields) > 0 + } + + // An explicit application/x-www-form-urlencoded choice is honored even when + // file fields are present: the spec allows files to travel as URL-encoded + // form values, although it does not stream and is discouraged. Without this + // short-circuit, picking urlencoded with files would silently fall back to + // multipart and emit an inconsistent Content-Type. + if base == runtime.URLencodedFormMime { + return false + } + + if len(r.fileFields) > 0 { + return true + } + + return base == runtime.MultipartFormMime +} + +// buildBufferedRequest assembles a request whose body is fully +// buffered in r.buf before AuthenticateRequest runs — urlencoded form, +// producer-serialized payload, or no body. +// +// Auth is trivial in this flow because the buffer is already populated when the auth helper +// asks for the body via r.GetBody(). +func (r *Request) buildBufferedRequest(ctx context.Context, mediaType, basePath string, + producers map[string]runtime.Producer, registry strfmt.Registry, auth runtime.ClientAuthInfoWriter, +) (*http.Request, error) { + var body io.Reader + var err error + + switch { + case len(r.formFields) > 0 || len(r.fileFields) > 0: + body, err = r.writeURLEncodedBody(mediaType) + case r.payload != nil: + body, err = r.writeNonStreamPayload(mediaType, producers) + } + if err != nil { + return nil, err + } + + if runtime.CanHaveBody(r.method) && body != nil && r.header.Get(runtime.HeaderContentType) == "" { + r.header.Set(runtime.HeaderContentType, mediaType) + } + + if auth != nil { + if err := auth.AuthenticateRequest(r, registry); err != nil { + return nil, err + } + } + + return r.assembleRequest(ctx, basePath, body) +} + +// buildStreamingRequest assembles a request whose body is a stream — +// either an io.Pipe filled by the multipart goroutine, or the +// payload's own io.Reader. +// +// AuthenticateRequest consumes the body lazily through the getBody closure installed by +// applyAuthWithBodyCopy, which buffers the stream into r.buf so the http.Request can use the buffered copy. +// +// On any error path before the http.Request takes ownership of body, we close the body to release +// the underlying resource. +// +// For multipart this unblocks the spawned writer goroutine +// (it would otherwise park forever on pw.Write with no reader). +// +// For stream payloads it closes the user-provided io.ReadCloser. +func (r *Request) buildStreamingRequest(ctx context.Context, mediaType, basePath string, + producers map[string]runtime.Producer, registry strfmt.Registry, auth runtime.ClientAuthInfoWriter, +) (req *http.Request, retErr error) { + var body io.Reader + if len(r.formFields) > 0 || len(r.fileFields) > 0 { + body = r.writeMultipartBody(ctx, mediaType) + } else { + body = r.writeStreamPayload(mediaType, producers) + } + + defer func() { + if retErr == nil { + return + } + if c, ok := body.(io.Closer); ok { + _ = c.Close() + } + }() + + if runtime.CanHaveBody(r.method) && body != nil && r.header.Get(runtime.HeaderContentType) == "" { + r.header.Set(runtime.HeaderContentType, mediaType) + } + + body, err := r.applyAuthWithBodyCopy(auth, body, registry) + if err != nil { + return nil, err + } + + return r.assembleRequest(ctx, basePath, body) +} + +// assembleRequest is the shared tail of both flows: build the URL +// path, create the http.Request, merge static query parameters, and +// finalize headers/query. +func (r *Request) assembleRequest(ctx context.Context, basePath string, body io.Reader) (*http.Request, error) { + urlPath, staticQueryParams, err := r.resolveURLPath(basePath) + if err != nil { + return nil, err + } + + req, err := http.NewRequestWithContext(ctx, r.method, urlPath, body) + if err != nil { + return nil, err + } + + if err := r.mergeStaticQuery(staticQueryParams); err != nil { + return nil, err + } + + req.URL.RawQuery = r.query.Encode() + req.Header = r.header + + return req, nil +} + +// resolveURLPath builds the final url path string and returns the static +// query parameters extracted from basePath and r.pathPattern. +// +// Static query parameters from the path pattern take precedence over those +// from the base path; merging with r.query is the caller's responsibility +// (see [request.mergeStaticQuery]). +// +// The path is assembled from basePath + pathPattern with path-param +// substitution and trailing-slash preservation when the original +// pathPattern carried one. +func (r *Request) resolveURLPath(basePath string) (string, url.Values, error) { + basePathURL, err := url.Parse(basePath) + if err != nil { + return "", nil, err + } + staticQueryParams := basePathURL.Query() + + pathPatternURL, err := url.Parse(r.pathPattern) + if err != nil { + return "", nil, err + } + for name, values := range pathPatternURL.Query() { + if _, present := staticQueryParams[name]; present { + staticQueryParams.Del(name) + } + for _, value := range values { + staticQueryParams.Add(name, value) + } + } + + // path.Join strips trailing slashes; reinstate one whenever the + // pathPattern carried it, including the bare-root case ("/" under a + // non-empty basePath, which path.Join would collapse to "/basepath"). + // The HasSuffix check on urlPath keeps the rewrite idempotent and + // avoids producing "//" when basePath is "/" or empty. + reinstateSlash := strings.HasSuffix(pathPatternURL.Path, "/") + + urlPath := path.Join(basePathURL.Path, pathPatternURL.Path) + for k, v := range r.pathParams { + urlPath = strings.ReplaceAll(urlPath, "{"+k+"}", url.PathEscape(v)) + } + if reinstateSlash && !strings.HasSuffix(urlPath, "/") { + urlPath += "/" + } + + return urlPath, staticQueryParams, nil +} + +// applyAuthWithBodyCopy runs auth.AuthenticateRequest for the +// streaming flow, where the http.Request body is a pipe or a payload +// reader rather than r.buf. If AuthenticateRequest asks for the body +// via r.GetBody(), the lazy closure copies the stream into r.buf on +// demand and reassigns body to r.buf so the post-auth source passed +// to http.NewRequestWithContext is the buffered copy. +// +// The closure is registered lazily because there is no way to know +// ahead of time whether AuthenticateRequest will read the body. +// +// On error precedence: a copy error is reported in preference to the +// AuthenticateRequest error, because a mis-read body may have +// interfered with auth. +// +// No-op when auth is nil; returns body unchanged. +func (r *Request) applyAuthWithBodyCopy(auth runtime.ClientAuthInfoWriter, body io.Reader, registry strfmt.Registry) (io.Reader, error) { + if auth == nil { + return body, nil + } + + var copyErr error + var copied bool + r.getBody = func(r *Request) []byte { + if copied { + return getRequestBuffer(r) + } + + defer func() { + copied = true + }() + + if _, copyErr = io.Copy(r.buf, body); copyErr != nil { + return nil + } + + if closer, ok := body.(io.ReadCloser); ok { + if copyErr = closer.Close(); copyErr != nil { + return nil + } + } + + body = r.buf + return getRequestBuffer(r) + } + + authErr := auth.AuthenticateRequest(r, registry) + + // On error we return body alongside the error so the caller's + // cleanup defer (in buildStreamingRequest) can close the + // underlying pipe/stream. Caller treats body as ignorable when + // err != nil per Go convention; the defer reads it via closure. + if copyErr != nil { + return body, fmt.Errorf("error copying the request body: %w", copyErr) + } + + if authErr != nil { + return body, authErr + } + + return body, nil +} + +// mergeStaticQuery overlays staticQuery onto r.query. On conflict r.query +// wins — the parameters set by the client take precedence over the ones +// extracted from basePath / pathPattern. +func (r *Request) mergeStaticQuery(staticQuery url.Values) error { + originalParams := r.GetQueryParams() + for k, v := range staticQuery { + if _, present := originalParams[k]; present { + continue + } + if err := r.SetQueryParam(k, v...); err != nil { + return err + } + } + return nil +} + +// writeURLEncodedBody serializes form fields (and any file fields, per +// Swagger 2.0 fallback semantics) into r.buf as +// application/x-www-form-urlencoded. Sets Content-Type to mediaType and +// returns r.buf as the body source. +// +// Per Swagger 2.0, file form parameters can be sent under +// application/x-www-form-urlencoded by including the file content as a +// regular form-field value. The whole form is then percent-encoded as +// usual. This buffers the entire payload and does not preserve a +// per-file Content-Type — multipart/form-data is preferred when both +// are advertised by the operation. +func (r *Request) writeURLEncodedBody(mediaType string) (io.Reader, error) { + r.header.Set(runtime.HeaderContentType, mediaType) + values := url.Values{} + for k, vs := range r.formFields { + values[k] = append(values[k], vs...) + } + for fn, ff := range r.fileFields { + for _, fi := range ff { + data, ferr := io.ReadAll(fi) + if cerr := fi.Close(); cerr != nil && ferr == nil { + ferr = cerr + } + if ferr != nil { + return nil, ferr + } + values.Add(fn, string(data)) + } + } + r.buf.WriteString(values.Encode()) + return r.buf, nil +} + +// writeMultipartBody assembles a multipart/form-data body via an +// io.Pipe. A goroutine streams form fields and files into the pipe +// writer; the pipe reader is returned as the body. Sets Content-Type to +// the multipart media type with the writer's boundary parameter. +// +// The goroutine owns the pipe writer's lifecycle: it closes the +// multipart writer (flushing the closing boundary) and the pipe writer +// when it finishes or hits an error. +func (r *Request) writeMultipartBody(ctx context.Context, mediaType string) io.Reader { + pr, pw := io.Pipe() + mp := multipart.NewWriter(pw) + r.header.Set(runtime.HeaderContentType, mangleContentType(mediaType, mp.Boundary())) + + go r.streamMultipartParts(ctx, mp, pw) + + return pr +} + +// streamMultipartParts writes form fields then file fields to mp, +// closing mp and pw when done. +// +// Errors are reported by closing pw with the error so the consumer of pr observes them on its next Read. +// +// Context cancellation is observed at iteration boundaries (between +// fields and between files) and during file copy via a context-aware +// reader. When ctx is canceled the pipe writer is closed with ctx.Err() +// so the body consumer surfaces the cancellation as the read error. +func (r *Request) streamMultipartParts(ctx context.Context, mp *multipart.Writer, pw *io.PipeWriter) { + defer func() { + mp.Close() + pw.Close() + }() + + for fn, v := range r.formFields { + for _, vi := range v { + if err := ctx.Err(); err != nil { + _ = pw.CloseWithError(err) + return + } + if err := mp.WriteField(fn, vi); err != nil { + logClose(err, pw) + return + } + } + } + + defer func() { + for _, ff := range r.fileFields { + for _, ffi := range ff { + ffi.Close() + } + } + }() + + for fn, f := range r.fileFields { + for _, fi := range f { + if err := ctx.Err(); err != nil { + _ = pw.CloseWithError(err) + return + } + + var fileContentType string + if p, ok := fi.(runtime.ContentTyper); ok { + fileContentType = p.ContentType() + } else { + // Need to read the data so that we can detect the content type + const contentTypeBufferSize = 512 + buf := make([]byte, contentTypeBufferSize) + size, err := fi.Read(buf) + if err != nil && !errors.Is(err, io.EOF) { + logClose(err, pw) + return + } + fileContentType = http.DetectContentType(buf) + fi = runtime.NamedReader(fi.Name(), io.MultiReader(bytes.NewReader(buf[:size]), fi)) + } + + // Create the MIME headers for the new part + h := make(textproto.MIMEHeader) + h.Set("Content-Disposition", + fmt.Sprintf(`form-data; name="%s"; filename="%s"`, + escapeQuotes(fn), escapeQuotes(filepath.Base(fi.Name())))) + h.Set("Content-Type", fileContentType) + + wrtr, err := mp.CreatePart(h) + if err != nil { + logClose(err, pw) + return + } + if _, err := io.Copy(wrtr, &ctxReader{ctx: ctx, r: fi}); err != nil { + logClose(err, pw) + return + } + } + } +} + +// ctxReader wraps an [io.Reader] with a context check on each Read. Once +// ctx is done, subsequent Reads return ctx.Err() instead of delegating +// to the underlying reader. It does not preempt a Read already in flight +// — that is the source's responsibility (e.g. *os.File honors Close from +// another goroutine, network sources honor SetDeadline). +type ctxReader struct { + ctx context.Context //nolint:containedctx // io.Reader's Read method has no ctx parameter, so the wrapper must carry it on the struct + r io.Reader +} + +func (cr *ctxReader) Read(p []byte) (int, error) { + if err := cr.ctx.Err(); err != nil { + return 0, err + } + return cr.r.Read(p) +} + +// writeStreamPayload handles a stream payload (io.Reader / +// io.ReadCloser). The bytes flow through verbatim — no producer is +// invoked. The wire Content-Type is resolved via setStreamContentType +// (priority: existing header, payload's ContentTyper, +// streamFallbackMime, mediaType). +// +// Caller must ensure r.payload satisfies io.Reader (see +// [request.usesStreamingBody]). +func (r *Request) writeStreamPayload(mediaType string, producers map[string]runtime.Producer) io.Reader { + setStreamContentType(r.header, r.payload, mediaType, r.consumes, producers) + if rdr, ok := r.payload.(io.ReadCloser); ok { + return rdr + } + + rdr, ok := r.payload.(io.Reader) + if !ok { + panic("internal error: payload expected to be an io.Reader") // guaranteed by earlier checks + } + + return rdr +} + +// writeNonStreamPayload runs the producer registered for mediaType +// against r.payload, writing into r.buf. The Content-Type header +// reflects the picker. +// +// SetHeaderParam("Content-Type", …) is intentionally NOT honored on +// the producer path because the producer is dispatched off mediaType — +// the wire header would otherwise misrepresent the body. +// +// The same reasoning applies to the form/multipart branch. +func (r *Request) writeNonStreamPayload(mediaType string, producers map[string]runtime.Producer) (io.Reader, error) { + r.header.Set(runtime.HeaderContentType, mediaType) + producer, ok := producers[mediaType] + if !ok { + return nil, fmt.Errorf("no producer registered for content type %q (register one with Runtime.Producers)", mediaType) + } + + if err := producer.Produce(r.buf, r.payload); err != nil { + return nil, err + } + return r.buf, nil +} + +var quoter = strings.NewReplacer( + "\\", "\\\\", + `"`, "\\\"", + "\r", "_", + "\n", "_", +) + +// escapeQuotes escapes backslash and double-quote for embedding in a +// quoted-string Content-Disposition parameter value, and rewrites +// CR / LF to '_' to prevent header-injection through attacker-influenced +// field names or filenames. +// +// RFC 7578 §4.2 limits parameter values to printable characters; this +// is the conservative subset relevant to security (control characters +// that would split the header line into a forged header or part). +// Mirrors the known stdlib gap golang/go#19038. +func escapeQuotes(s string) string { + return quoter.Replace(s) +} + +// setStreamContentType resolves and writes the wire Content-Type for a +// stream payload (io.Reader / io.ReadCloser). Priority: +// +// 1. an explicit value already in header — the user set it via +// SetHeaderParam during [ClientRequestWriter.WriteToRequest], and we treat that as an +// intentional escape hatch; +// 2. payload's [runtime.ContentTyper] declaration; +// 3. [streamFallbackMime] (Stage-2 octet-stream upgrade); +// 4. the picker's mediaType (passed in as the chain's terminal +// fallback). +// +// Does not apply to non-stream payloads or to form/multipart bodies — +// see the comment above the call site in [request.buildHTTP]. +func setStreamContentType( + header http.Header, + payload any, + mediaType string, + candidates []string, + producers map[string]runtime.Producer, +) { + if header.Get(runtime.HeaderContentType) != "" { + return + } + fallback := streamFallbackMime(mediaType, candidates, producers) + header.Set(runtime.HeaderContentType, payloadContentType(payload, fallback)) +} + +// payloadContentType returns the payload's declared content type when +// it implements [runtime.ContentTyper] with a non-empty result, and +// fallback otherwise. Mirrors the per-file convention already used for +// multipart upload parts (see [request.buildHTTP] file-fields branch). +func payloadContentType(payload any, fallback string) string { + if t, ok := payload.(runtime.ContentTyper); ok { + if ct := t.ContentType(); ct != "" { + return ct + } + } + + return fallback +} + +// streamFallbackMime selects a wire content-type for a stream payload +// (io.Reader / io.ReadCloser) that has neither implemented +// `ContentType() string` nor declared an explicit value. +// +// The picker (Stage 1) ran without seeing the payload, so its choice +// may be wildly wrong for raw bytes — e.g. picking application/json +// for a payload that is just a stream of opaque data. When the +// candidate consumes list also offers application/octet-stream and +// the runtime has an octet-stream producer registered, that's a +// safer wire type than the picker's choice: it advertises "raw bytes" +// rather than making a structural claim about the body. +// +// If octet-stream is unavailable in either the candidate list or the +// producer set, the picker's choice is preserved. The wire header +// then continues to misrepresent the body — but no correct +// alternative exists and we cannot infer one without more +// information from the caller. +func streamFallbackMime(picked string, candidates []string, producers map[string]runtime.Producer) string { + if strings.EqualFold(picked, runtime.DefaultMime) { + return picked + } + + for _, c := range candidates { + if strings.EqualFold(c, runtime.DefaultMime) { + if _, ok := producers[runtime.DefaultMime]; ok { + return runtime.DefaultMime + } + } + } + + return picked +} + +func getRequestBuffer(r *Request) []byte { + if r.buf == nil { + return nil + } + return r.buf.Bytes() +} + +func logClose(err error, pw *io.PipeWriter) { + log.Println(err) + closeErr := pw.CloseWithError(err) + if closeErr != nil { + log.Println(closeErr) + } +} + +func mangleContentType(mediaType, boundary string) string { + _ = mediaType // reserved for future enhancement: honor caller-provided media type + // Proposal for enhancement: honor caller's boundary if specified + return "multipart/form-data; boundary=" + boundary +} diff --git a/vendor/github.com/go-openapi/runtime/client/keepalive.go b/vendor/github.com/go-openapi/runtime/client/keepalive.go index 3bac5e272c..6b6097d206 100644 --- a/vendor/github.com/go-openapi/runtime/client/keepalive.go +++ b/vendor/github.com/go-openapi/runtime/client/keepalive.go @@ -34,20 +34,20 @@ func (k *keepAliveTransport) RoundTrip(r *http.Request) (*http.Response, error) type drainingReadCloser struct { rdr io.ReadCloser - seenEOF uint32 + seenEOF atomic.Uint32 } func (d *drainingReadCloser) Read(p []byte) (n int, err error) { n, err = d.rdr.Read(p) if err == io.EOF || n == 0 { - atomic.StoreUint32(&d.seenEOF, 1) + d.seenEOF.Store(1) } return } func (d *drainingReadCloser) Close() error { // drain buffer - if atomic.LoadUint32(&d.seenEOF) != 1 { + if d.seenEOF.Load() != 1 { // If the reader side (a HTTP server) is misbehaving, it still may send // some bytes, but the closer ignores them to keep the underling // connection open. diff --git a/vendor/github.com/go-openapi/runtime/client/opentelemetry.go b/vendor/github.com/go-openapi/runtime/client/opentelemetry.go index 5054878c06..e422f83cb1 100644 --- a/vendor/github.com/go-openapi/runtime/client/opentelemetry.go +++ b/vendor/github.com/go-openapi/runtime/client/opentelemetry.go @@ -4,18 +4,20 @@ package client import ( + "context" "fmt" "net/http" "strings" - "github.com/go-openapi/runtime" - "github.com/go-openapi/strfmt" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/propagation" semconv "go.opentelemetry.io/otel/semconv/v1.37.0" "go.opentelemetry.io/otel/trace" + + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" ) const ( @@ -23,6 +25,52 @@ const ( tracerName = "go-openapi" ) +// WithOpenTelemetry adds opentelemetry support to the provided runtime. +// A new client span is created for each request. +// The provided opts are applied to each spans - for example to add global tags. +// +// The returned transport satisfies [runtime.ContextualTransport]: callers +// should prefer [openTelemetryTransport.SubmitContext] over the +// legacy [runtime.ClientOperation.Context] field. Setting that +// field is still honored on the [openTelemetryTransport.Submit] +// compatibility path. +func (r *Runtime) WithOpenTelemetry(opts ...OpenTelemetryOpt) runtime.ContextualTransport { + return newOpenTelemetryTransport(r, r.Host, opts) +} + +// WithOpenTracing adds opentracing support to the provided runtime. +// A new client span is created for each request. +// If the context of the client operation does not contain an active span, no span is created. +// The provided opts are applied to each spans - for example to add global tags. +// +// Deprecated: use [WithOpenTelemetry] instead, as opentracing is now archived and superseded by opentelemetry. +// +// # Deprecation notice +// +// The [Runtime.WithOpenTracing] method has been deprecated in favor of [Runtime.WithOpenTelemetry]. +// +// The method is still around so programs calling it will still build. However, it will return +// an opentelemetry transport. +// +// If you have a strict requirement on using opentracing, you may still do so by importing +// module [github.com/go-openapi/runtime/client-[middleware]/opentracing] and using +// [github.com/go-openapi/runtime/client-[middleware]/opentracing.WithOpenTracing] with your +// usual opentracing options and opentracing-enabled transport. +// +// Passed options are ignored unless they are of type [OpenTelemetryOpt]. +func (r *Runtime) WithOpenTracing(opts ...any) runtime.ContextualTransport { + otelOpts := make([]OpenTelemetryOpt, 0, len(opts)) + for _, o := range opts { + otelOpt, ok := o.(OpenTelemetryOpt) + if !ok { + continue + } + otelOpts = append(otelOpts, otelOpt) + } + + return r.WithOpenTelemetry(otelOpts...) +} + type config struct { Tracer trace.Tracer Propagator propagation.TextMapPropagator @@ -113,11 +161,31 @@ func newOpenTelemetryTransport(transport runtime.ClientTransport, host string, o return tr } +// Submit implements [runtime.ClientTransport]. It honors the legacy +// [runtime.ClientOperation.Context] field for backward compatibility +// — that field is being phased out; new code should call +// [openTelemetryTransport.SubmitContext] directly with an explicit +// context. func (t *openTelemetryTransport) Submit(op *runtime.ClientOperation) (any, error) { - if op.Context == nil { - return t.transport.Submit(op) + ctx := op.Context + if ctx == nil { + ctx = context.Background() } + return t.SubmitContext(ctx, op) +} +// SubmitContext submits an operation with an explicit context that +// drives both the tracing span and (when supported) the wrapped +// transport's SubmitContext call. The legacy +// [runtime.ClientOperation.Context] field is not consulted. +// +// When the wrapped transport implements [runtime.ContextualTransport], ctx is +// forwarded directly via its SubmitContext. Otherwise, the legacy +// Submit path is used: ctx is stamped onto op.Context for the +// duration of that call and restored afterwards, so the wrapped +// transport still receives a usable context. The legacy fallback +// disappears once SubmitContext is universal (v2). +func (t *openTelemetryTransport) SubmitContext(ctx context.Context, op *runtime.ClientOperation) (any, error) { params := op.Params reader := op.Reader @@ -129,7 +197,7 @@ func (t *openTelemetryTransport) Submit(op *runtime.ClientOperation) (any, error }() op.Params = runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, reg strfmt.Registry) error { - span = t.newOpenTelemetrySpan(op, req.GetHeaderParams()) + span = t.newOpenTelemetrySpan(ctx, op, req.GetHeaderParams()) return params.WriteToRequest(req, reg) }) @@ -149,7 +217,7 @@ func (t *openTelemetryTransport) Submit(op *runtime.ClientOperation) (any, error return reader.ReadResponse(response, consumer) }) - submit, err := t.transport.Submit(op) + submit, err := t.submitWrapped(ctx, op) if err != nil && span != nil { span.RecordError(err) span.SetStatus(codes.Error, err.Error()) @@ -158,9 +226,18 @@ func (t *openTelemetryTransport) Submit(op *runtime.ClientOperation) (any, error return submit, err } -func (t *openTelemetryTransport) newOpenTelemetrySpan(op *runtime.ClientOperation, header http.Header) trace.Span { - ctx := op.Context +//nolint:contextcheck // ctx is forwarded verbatim; the legacy Submit branch only stamps it onto op.Context for the wrapped transport. +func (t *openTelemetryTransport) submitWrapped(ctx context.Context, op *runtime.ClientOperation) (any, error) { + if sc, ok := t.transport.(runtime.ContextualTransport); ok { + return sc.SubmitContext(ctx, op) + } + prev := op.Context + op.Context = ctx + defer func() { op.Context = prev }() + return t.transport.Submit(op) +} +func (t *openTelemetryTransport) newOpenTelemetrySpan(ctx context.Context, op *runtime.ClientOperation, header http.Header) trace.Span { tracer := t.tracer if tracer == nil { if span := trace.SpanFromContext(ctx); span.SpanContext().IsValid() { diff --git a/vendor/github.com/go-openapi/runtime/client/request.go b/vendor/github.com/go-openapi/runtime/client/request.go deleted file mode 100644 index f16ee487ba..0000000000 --- a/vendor/github.com/go-openapi/runtime/client/request.go +++ /dev/null @@ -1,468 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers -// SPDX-License-Identifier: Apache-2.0 - -package client - -import ( - "bytes" - "context" - "fmt" - "io" - "log" - "mime/multipart" - "net/http" - "net/textproto" - "net/url" - "os" - "path" - "path/filepath" - "strings" - "time" - - "github.com/go-openapi/runtime" - "github.com/go-openapi/strfmt" -) - -var _ runtime.ClientRequest = new(request) // ensure compliance to the interface - -// Request represents a swagger client request. -// -// This Request struct converts to a HTTP request. -// There might be others that convert to other transports. -// There is no error checking here, it is assumed to be used after a spec has been validated. -// so impossible combinations should not arise (hopefully). -// -// The main purpose of this struct is to hide the machinery of adding params to a transport request. -// The generated code only implements what is necessary to turn a param into a valid value for these methods. -type request struct { - pathPattern string - method string - writer runtime.ClientRequestWriter - - pathParams map[string]string - header http.Header - query url.Values - formFields url.Values - fileFields map[string][]runtime.NamedReadCloser - payload any - timeout time.Duration - buf *bytes.Buffer - - getBody func(r *request) []byte -} - -// NewRequest creates a new swagger http client request. -func newRequest(method, pathPattern string, writer runtime.ClientRequestWriter) *request { - return &request{ - pathPattern: pathPattern, - method: method, - writer: writer, - header: make(http.Header), - query: make(url.Values), - timeout: DefaultTimeout, - getBody: getRequestBuffer, - } -} - -// BuildHTTP creates a new http request based on the data from the params. -func (r *request) BuildHTTP(mediaType, basePath string, producers map[string]runtime.Producer, registry strfmt.Registry) (*http.Request, error) { - return r.buildHTTP(mediaType, basePath, producers, registry, nil) -} - -func (r *request) GetMethod() string { - return r.method -} - -func (r *request) GetPath() string { - path := r.pathPattern - for k, v := range r.pathParams { - path = strings.ReplaceAll(path, "{"+k+"}", v) - } - return path -} - -func (r *request) GetBody() []byte { - return r.getBody(r) -} - -// SetHeaderParam adds a header param to the request -// when there is only 1 value provided for the varargs, it will set it. -// when there are several values provided for the varargs it will add it (no overriding). -func (r *request) SetHeaderParam(name string, values ...string) error { - if r.header == nil { - r.header = make(http.Header) - } - r.header[http.CanonicalHeaderKey(name)] = values - return nil -} - -// GetHeaderParams returns the all headers currently set for the request. -func (r *request) GetHeaderParams() http.Header { - return r.header -} - -// SetQueryParam adds a query param to the request -// when there is only 1 value provided for the varargs, it will set it. -// when there are several values provided for the varargs it will add it (no overriding). -func (r *request) SetQueryParam(name string, values ...string) error { - if r.query == nil { - r.query = make(url.Values) - } - r.query[name] = values - return nil -} - -// GetQueryParams returns a copy of all query params currently set for the request. -func (r *request) GetQueryParams() url.Values { - var result = make(url.Values) - for key, value := range r.query { - result[key] = append([]string{}, value...) - } - return result -} - -// SetFormParam adds a forn param to the request -// when there is only 1 value provided for the varargs, it will set it. -// when there are several values provided for the varargs it will add it (no overriding). -func (r *request) SetFormParam(name string, values ...string) error { - if r.formFields == nil { - r.formFields = make(url.Values) - } - r.formFields[name] = values - return nil -} - -// SetPathParam adds a path param to the request. -func (r *request) SetPathParam(name string, value string) error { - if r.pathParams == nil { - r.pathParams = make(map[string]string) - } - - r.pathParams[name] = value - return nil -} - -// SetFileParam adds a file param to the request. -func (r *request) SetFileParam(name string, files ...runtime.NamedReadCloser) error { - for _, file := range files { - if actualFile, ok := file.(*os.File); ok { - fi, err := os.Stat(actualFile.Name()) - if err != nil { - return err - } - if fi.IsDir() { - return fmt.Errorf("%q is a directory, only files are supported", file.Name()) - } - } - } - - if r.fileFields == nil { - r.fileFields = make(map[string][]runtime.NamedReadCloser) - } - if r.formFields == nil { - r.formFields = make(url.Values) - } - - r.fileFields[name] = files - return nil -} - -func (r *request) GetFileParam() map[string][]runtime.NamedReadCloser { - return r.fileFields -} - -// SetBodyParam sets a body parameter on the request. -// This does not yet serialze the object, this happens as late as possible. -func (r *request) SetBodyParam(payload any) error { - r.payload = payload - return nil -} - -func (r *request) GetBodyParam() any { - return r.payload -} - -// SetTimeout sets the timeout for a request. -func (r *request) SetTimeout(timeout time.Duration) error { - r.timeout = timeout - return nil -} - -func (r *request) isMultipart(mediaType string) bool { - if len(r.fileFields) > 0 { - return true - } - - return runtime.MultipartFormMime == mediaType -} - -func (r *request) buildHTTP(mediaType, basePath string, producers map[string]runtime.Producer, registry strfmt.Registry, auth runtime.ClientAuthInfoWriter) (*http.Request, error) { //nolint:gocyclo,maintidx - // build the data - if err := r.writer.WriteToRequest(r, registry); err != nil { - return nil, err - } - - // Our body must be an io.Reader. - // When we create the http.Request, if we pass it a - // bytes.Buffer then it will wrap it in an io.ReadCloser - // and set the content length automatically. - var body io.Reader - var pr *io.PipeReader - var pw *io.PipeWriter - - r.buf = bytes.NewBuffer(nil) - if r.payload != nil || len(r.formFields) > 0 || len(r.fileFields) > 0 { - body = r.buf - if r.isMultipart(mediaType) { - pr, pw = io.Pipe() - body = pr - } - } - - // check if this is a form type request - if len(r.formFields) > 0 || len(r.fileFields) > 0 { - if !r.isMultipart(mediaType) { - r.header.Set(runtime.HeaderContentType, mediaType) - formString := r.formFields.Encode() - r.buf.WriteString(formString) - goto DoneChoosingBodySource - } - - mp := multipart.NewWriter(pw) - r.header.Set(runtime.HeaderContentType, mangleContentType(mediaType, mp.Boundary())) - - go func() { - defer func() { - mp.Close() - pw.Close() - }() - - for fn, v := range r.formFields { - for _, vi := range v { - if err := mp.WriteField(fn, vi); err != nil { - logClose(err, pw) - return - } - } - } - - defer func() { - for _, ff := range r.fileFields { - for _, ffi := range ff { - ffi.Close() - } - } - }() - for fn, f := range r.fileFields { - for _, fi := range f { - var fileContentType string - if p, ok := fi.(interface { - ContentType() string - }); ok { - fileContentType = p.ContentType() - } else { - // Need to read the data so that we can detect the content type - const contentTypeBufferSize = 512 - buf := make([]byte, contentTypeBufferSize) - size, err := fi.Read(buf) - if err != nil && err != io.EOF { - logClose(err, pw) - return - } - fileContentType = http.DetectContentType(buf) - fi = runtime.NamedReader(fi.Name(), io.MultiReader(bytes.NewReader(buf[:size]), fi)) - } - - // Create the MIME headers for the new part - h := make(textproto.MIMEHeader) - h.Set("Content-Disposition", - fmt.Sprintf(`form-data; name="%s"; filename="%s"`, - escapeQuotes(fn), escapeQuotes(filepath.Base(fi.Name())))) - h.Set("Content-Type", fileContentType) - - wrtr, err := mp.CreatePart(h) - if err != nil { - logClose(err, pw) - return - } - if _, err := io.Copy(wrtr, fi); err != nil { - logClose(err, pw) - } - } - } - }() - - goto DoneChoosingBodySource - } - - // if there is payload, use the producer to write the payload, and then - // set the header to the content-type appropriate for the payload produced - if r.payload != nil { - // Enhancement proposal: https://github.com/go-openapi/runtime/issues/387 - r.header.Set(runtime.HeaderContentType, mediaType) - if rdr, ok := r.payload.(io.ReadCloser); ok { - body = rdr - goto DoneChoosingBodySource - } - - if rdr, ok := r.payload.(io.Reader); ok { - body = rdr - goto DoneChoosingBodySource - } - - producer := producers[mediaType] - if err := producer.Produce(r.buf, r.payload); err != nil { - return nil, err - } - } - -DoneChoosingBodySource: - - if runtime.CanHaveBody(r.method) && body != nil && r.header.Get(runtime.HeaderContentType) == "" { - r.header.Set(runtime.HeaderContentType, mediaType) - } - - if auth != nil { - // If we're not using r.buf as our http.Request's body, - // either the payload is an io.Reader or io.ReadCloser, - // or we're doing a multipart form/file. - // - // In those cases, if the AuthenticateRequest call asks for the body, - // we must read it into a buffer and provide that, then use that buffer - // as the body of our http.Request. - // - // This is done in-line with the GetBody() request rather than ahead - // of time, because there's no way to know if the AuthenticateRequest - // will even ask for the body of the request. - // - // If for some reason the copy fails, there's no way to return that - // error to the GetBody() call, so return it afterwards. - // - // An error from the copy action is prioritized over any error - // from the AuthenticateRequest call, because the mis-read - // body may have interfered with the auth. - // - var copyErr error - if buf, ok := body.(*bytes.Buffer); body != nil && (!ok || buf != r.buf) { - var copied bool - r.getBody = func(r *request) []byte { - if copied { - return getRequestBuffer(r) - } - - defer func() { - copied = true - }() - - if _, copyErr = io.Copy(r.buf, body); copyErr != nil { - return nil - } - - if closer, ok := body.(io.ReadCloser); ok { - if copyErr = closer.Close(); copyErr != nil { - return nil - } - } - - body = r.buf - return getRequestBuffer(r) - } - } - - authErr := auth.AuthenticateRequest(r, registry) - - if copyErr != nil { - return nil, fmt.Errorf("error retrieving the response body: %v", copyErr) - } - - if authErr != nil { - return nil, authErr - } - } - - // In case the basePath or the request pathPattern include static query parameters, - // parse those out before constructing the final path. The parameters themselves - // will be merged with the ones set by the client, with the priority given first to - // the ones set by the client, then the path pattern, and lastly the base path. - basePathURL, err := url.Parse(basePath) - if err != nil { - return nil, err - } - staticQueryParams := basePathURL.Query() - - pathPatternURL, err := url.Parse(r.pathPattern) - if err != nil { - return nil, err - } - for name, values := range pathPatternURL.Query() { - if _, present := staticQueryParams[name]; present { - staticQueryParams.Del(name) - } - for _, value := range values { - staticQueryParams.Add(name, value) - } - } - - // create http request - var reinstateSlash bool - if pathPatternURL.Path != "" && pathPatternURL.Path != "/" && pathPatternURL.Path[len(pathPatternURL.Path)-1] == '/' { - reinstateSlash = true - } - - urlPath := path.Join(basePathURL.Path, pathPatternURL.Path) - for k, v := range r.pathParams { - urlPath = strings.ReplaceAll(urlPath, "{"+k+"}", url.PathEscape(v)) - } - if reinstateSlash { - urlPath += "/" - } - - req, err := http.NewRequestWithContext(context.Background(), r.method, urlPath, body) - if err != nil { - return nil, err - } - - originalParams := r.GetQueryParams() - - // Merge the query parameters extracted from the basePath with the ones set by - // the client in this struct. In case of conflict, the client wins. - for k, v := range staticQueryParams { - _, present := originalParams[k] - if !present { - if err = r.SetQueryParam(k, v...); err != nil { - return nil, err - } - } - } - - req.URL.RawQuery = r.query.Encode() - req.Header = r.header - - return req, nil -} - -func escapeQuotes(s string) string { - return strings.NewReplacer("\\", "\\\\", `"`, "\\\"").Replace(s) -} - -func getRequestBuffer(r *request) []byte { - if r.buf == nil { - return nil - } - return r.buf.Bytes() -} - -func logClose(err error, pw *io.PipeWriter) { - log.Println(err) - closeErr := pw.CloseWithError(err) - if closeErr != nil { - log.Println(closeErr) - } -} - -func mangleContentType(mediaType, boundary string) string { - if strings.ToLower(mediaType) == runtime.URLencodedFormMime { - return fmt.Sprintf("%s; boundary=%s", mediaType, boundary) - } - return "multipart/form-data; boundary=" + boundary -} diff --git a/vendor/github.com/go-openapi/runtime/client/runtime.go b/vendor/github.com/go-openapi/runtime/client/runtime.go index eeb17dfb24..b890f9f413 100644 --- a/vendor/github.com/go-openapi/runtime/client/runtime.go +++ b/vendor/github.com/go-openapi/runtime/client/runtime.go @@ -5,25 +5,19 @@ package client import ( "context" - "crypto" - "crypto/ecdsa" - "crypto/rsa" - "crypto/tls" - "crypto/x509" - "encoding/pem" - "errors" "fmt" "mime" "net/http" "net/http/httputil" - "os" "strings" "sync" "time" "github.com/go-openapi/runtime" + "github.com/go-openapi/runtime/client/internal/request" "github.com/go-openapi/runtime/logger" "github.com/go-openapi/runtime/middleware" + "github.com/go-openapi/runtime/server-middleware/mediatype" "github.com/go-openapi/runtime/yamlpc" "github.com/go-openapi/strfmt" ) @@ -36,184 +30,6 @@ const ( // DefaultTimeout the default request timeout. var DefaultTimeout = 30 * time.Second -// TLSClientOptions to configure client authentication with mutual TLS. -type TLSClientOptions struct { - // Certificate is the path to a PEM-encoded certificate to be used for - // client authentication. If set then Key must also be set. - Certificate string - - // LoadedCertificate is the certificate to be used for client authentication. - // This field is ignored if Certificate is set. If this field is set, LoadedKey - // is also required. - LoadedCertificate *x509.Certificate - - // Key is the path to an unencrypted PEM-encoded private key for client - // authentication. This field is required if Certificate is set. - Key string - - // LoadedKey is the key for client authentication. This field is required if - // LoadedCertificate is set. - LoadedKey crypto.PrivateKey - - // CA is a path to a PEM-encoded certificate that specifies the root certificate - // to use when validating the TLS certificate presented by the server. If this field - // (and LoadedCA) is not set, the system certificate pool is used. This field is ignored if LoadedCA - // is set. - CA string - - // LoadedCA specifies the root certificate to use when validating the server's TLS certificate. - // If this field (and CA) is not set, the system certificate pool is used. - LoadedCA *x509.Certificate - - // LoadedCAPool specifies a pool of RootCAs to use when validating the server's TLS certificate. - // If set, it will be combined with the other loaded certificates (see LoadedCA and CA). - // If neither LoadedCA or CA is set, the provided pool with override the system - // certificate pool. - // The caller must not use the supplied pool after calling TLSClientAuth. - LoadedCAPool *x509.CertPool - - // ServerName specifies the hostname to use when verifying the server certificate. - // If this field is set then InsecureSkipVerify will be ignored and treated as - // false. - ServerName string - - // InsecureSkipVerify controls whether the certificate chain and hostname presented - // by the server are validated. If true, any certificate is accepted. - InsecureSkipVerify bool - - // VerifyPeerCertificate, if not nil, is called after normal - // certificate verification. It receives the raw ASN.1 certificates - // provided by the peer and also any verified chains that normal processing found. - // If it returns a non-nil error, the handshake is aborted and that error results. - // - // If normal verification fails then the handshake will abort before - // considering this callback. If normal verification is disabled by - // setting InsecureSkipVerify then this callback will be considered but - // the verifiedChains argument will always be nil. - VerifyPeerCertificate func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error - - // VerifyConnection, if not nil, is called after normal certificate - // verification and after [TLSClientOptions.VerifyPeerCertificate] by either a TLS client or - // server. It receives the [tls.ConnectionState] which may be inspected. - // - // Unlike VerifyPeerCertificate, this callback is invoked on every - // connection, including resumed ones, making it suitable for checks - // that must always apply (e.g. certificate pinning). - // - // If it returns a non-nil error, the handshake is aborted and that error results. - VerifyConnection func(tls.ConnectionState) error - - // SessionTicketsDisabled may be set to true to disable session ticket and - // PSK (resumption) support. Note that on clients, session ticket support is - // also disabled if ClientSessionCache is nil. - SessionTicketsDisabled bool - - // ClientSessionCache is a cache of ClientSessionState entries for TLS - // session resumption. It is only used by clients. - ClientSessionCache tls.ClientSessionCache - - // Prevents callers using unkeyed fields. - _ struct{} -} - -// TLSClientAuth creates a [tls.Config] for mutual auth. -func TLSClientAuth(opts TLSClientOptions) (*tls.Config, error) { - // create client tls config - cfg := &tls.Config{ - MinVersion: tls.VersionTLS12, - } - - // load client cert if specified - if opts.Certificate != "" { - cert, err := tls.LoadX509KeyPair(opts.Certificate, opts.Key) - if err != nil { - return nil, fmt.Errorf("tls client cert: %v", err) - } - cfg.Certificates = []tls.Certificate{cert} - } else if opts.LoadedCertificate != nil { - block := pem.Block{Type: "CERTIFICATE", Bytes: opts.LoadedCertificate.Raw} - certPem := pem.EncodeToMemory(&block) - - var keyBytes []byte - switch k := opts.LoadedKey.(type) { - case *rsa.PrivateKey: - keyBytes = x509.MarshalPKCS1PrivateKey(k) - case *ecdsa.PrivateKey: - var err error - keyBytes, err = x509.MarshalECPrivateKey(k) - if err != nil { - return nil, fmt.Errorf("tls client priv key: %v", err) - } - default: - return nil, errors.New("tls client priv key: unsupported key type") - } - - block = pem.Block{Type: "PRIVATE KEY", Bytes: keyBytes} - keyPem := pem.EncodeToMemory(&block) - - cert, err := tls.X509KeyPair(certPem, keyPem) - if err != nil { - return nil, fmt.Errorf("tls client cert: %v", err) - } - cfg.Certificates = []tls.Certificate{cert} - } - - cfg.InsecureSkipVerify = opts.InsecureSkipVerify - - cfg.VerifyPeerCertificate = opts.VerifyPeerCertificate - cfg.VerifyConnection = opts.VerifyConnection - cfg.SessionTicketsDisabled = opts.SessionTicketsDisabled - cfg.ClientSessionCache = opts.ClientSessionCache - - // When no CA certificate is provided, default to the system cert pool - // that way when a request is made to a server known by the system trust store, - // the name is still verified - switch { - case opts.LoadedCA != nil: - caCertPool := basePool(opts.LoadedCAPool) - caCertPool.AddCert(opts.LoadedCA) - cfg.RootCAs = caCertPool - case opts.CA != "": - // load ca cert - caCert, err := os.ReadFile(opts.CA) - if err != nil { - return nil, fmt.Errorf("tls client ca: %v", err) - } - caCertPool := basePool(opts.LoadedCAPool) - caCertPool.AppendCertsFromPEM(caCert) - cfg.RootCAs = caCertPool - case opts.LoadedCAPool != nil: - cfg.RootCAs = opts.LoadedCAPool - } - - // apply servername overrride - if opts.ServerName != "" { - cfg.InsecureSkipVerify = false - cfg.ServerName = opts.ServerName - } - - return cfg, nil -} - -// TLSTransport creates a [http] client transport suitable for mutual [tls] auth. -func TLSTransport(opts TLSClientOptions) (http.RoundTripper, error) { - cfg, err := TLSClientAuth(opts) - if err != nil { - return nil, err - } - - return &http.Transport{TLSClientConfig: cfg}, nil -} - -// TLSClient creates a [http.Client] for mutual auth. -func TLSClient(opts TLSClientOptions) (*http.Client, error) { - transport, err := TLSTransport(opts) - if err != nil { - return nil, err - } - return &http.Client{Transport: transport}, nil -} - // Runtime represents an API client that uses the transport // to make [http] requests based on a swagger specification. type Runtime struct { @@ -228,17 +44,50 @@ type Runtime struct { Host string BasePath string Formats strfmt.Registry - Context context.Context //nolint:containedctx // we precisely want this type to contain the request context + // Deprecated: prefer [runtime.ContextualTransport.SubmitContext] to pass the request context explicitly. + Context context.Context //nolint:containedctx // we precisely want this type to contain the request context + + Debug bool + + // Trace enables connection-level diagnostic output via + // [net/http/httptrace]. When true, the runtime narrates the + // connection lifecycle of every request through r.logger.Debugf: + // DNS, dial, TLS handshake, idle-pool reuse, request body + // transfer, time-to-first-byte, response body transfer, and a + // trailing per-request summary line. + // + // Trace is orthogonal to Debug: Debug dumps wire bytes (request + // and response headers and body), Trace narrates how the + // connection got there. Both may be enabled independently. + // + // Trace is not coupled to the SWAGGER_DEBUG / DEBUG environment + // variables: it defaults to false and is only enabled by + // explicit assignment. + // + // Trace is primarily intended as a problem-investigation tool + // (the local equivalent of curl -vvv), not an always-on tracer. + // For distributed-trace correlation, use the OpenTelemetry + // integration ([Runtime.WithOpenTelemetry]). + Trace bool - Debug bool logger logger.Logger + // MatchSuffix enables RFC 6839 structured-syntax suffix tolerance + // for codec lookup. When true, a response with Content-Type + // "application/problem+json" finds the JSON consumer registered + // under "application/json"; with the default false, the lookup + // is strict and falls through to the "*/*" wildcard if present. + // See [mediatype.AllowSuffix] for the semantics. + MatchSuffix bool + clientOnce *sync.Once client *http.Client schemes []string response ClientResponseFunc } +var _ runtime.ContextualTransport = &Runtime{} + // New creates a new default runtime for a swagger api runtime.Client. func New(host, basePath string, schemes []string) *Runtime { var rt Runtime @@ -246,13 +95,15 @@ func New(host, basePath string, schemes []string) *Runtime { // Enhancement proposal: https://github.com/go-openapi/runtime/issues/385 rt.Consumers = map[string]runtime.Consumer{ - runtime.YAMLMime: yamlpc.YAMLConsumer(), - runtime.JSONMime: runtime.JSONConsumer(), - runtime.XMLMime: runtime.XMLConsumer(), - runtime.TextMime: runtime.TextConsumer(), - runtime.HTMLMime: runtime.TextConsumer(), - runtime.CSVMime: runtime.CSVConsumer(), - runtime.DefaultMime: runtime.ByteStreamConsumer(), + runtime.YAMLMime: yamlpc.YAMLConsumer(), + runtime.JSONMime: runtime.JSONConsumer(), + runtime.XMLMime: runtime.XMLConsumer(), + runtime.TextMime: runtime.TextConsumer(), + runtime.HTMLMime: runtime.TextConsumer(), + runtime.CSVMime: runtime.CSVConsumer(), + runtime.MultipartFormMime: runtime.ByteStreamConsumer(), + runtime.URLencodedFormMime: runtime.ByteStreamConsumer(), + runtime.DefaultMime: runtime.ByteStreamConsumer(), } rt.Producers = map[string]runtime.Producer{ runtime.YAMLMime: yamlpc.YAMLProducer(), @@ -294,47 +145,6 @@ func NewWithClient(host, basePath string, schemes []string, client *http.Client) return rt } -// WithOpenTracing adds opentracing support to the provided runtime. -// A new client span is created for each request. -// If the context of the client operation does not contain an active span, no span is created. -// The provided opts are applied to each spans - for example to add global tags. -// -// Deprecated: use [WithOpenTelemetry] instead, as opentracing is now archived and superseded by opentelemetry. -// -// # Deprecation notice -// -// The [Runtime.WithOpenTracing] method has been deprecated in favor of [Runtime.WithOpenTelemetry]. -// -// The method is still around so programs calling it will still build. However, it will return -// an opentelemetry transport. -// -// If you have a strict requirement on using opentracing, you may still do so by importing -// module [github.com/go-openapi/runtime/client-[middleware]/opentracing] and using -// [github.com/go-openapi/runtime/client-[middleware]/opentracing.WithOpenTracing] with your -// usual opentracing options and opentracing-enabled transport. -// -// Passed options are ignored unless they are of type [OpenTelemetryOpt]. -func (r *Runtime) WithOpenTracing(opts ...any) runtime.ClientTransport { - otelOpts := make([]OpenTelemetryOpt, 0, len(opts)) - for _, o := range opts { - otelOpt, ok := o.(OpenTelemetryOpt) - if !ok { - continue - } - otelOpts = append(otelOpts, otelOpt) - } - - return r.WithOpenTelemetry(otelOpts...) -} - -// WithOpenTelemetry adds opentelemetry support to the provided runtime. -// A new client span is created for each request. -// If the context of the client operation does not contain an active span, no span is created. -// The provided opts are applied to each spans - for example to add global tags. -func (r *Runtime) WithOpenTelemetry(opts ...OpenTelemetryOpt) runtime.ClientTransport { - return newOpenTelemetryTransport(r, r.Host, opts) -} - // EnableConnectionReuse drains the remaining body from a response // so that go will reuse the TCP connections. // @@ -357,105 +167,109 @@ func (r *Runtime) EnableConnectionReuse() { ) } +// CreateHTTPRequestContext creates the requests and bind the parameters, but does not send it over the wire +// like [Runtime.SubmitContext]. +// +// The [http.Request] is complete with authentication, headers and body (including streamed body) and ready for callers +// to submit it to a [http.Client] of their choice, then consume the [http.Response]. +// +// Most users would simply use [Runtime.SubmitContext], which wraps all these operations in one call. +func (r *Runtime) CreateHTTPRequestContext(ctx context.Context, operation *runtime.ClientOperation) (req *http.Request, cancel context.CancelFunc, err error) { + req, cancel, err = r.createHTTPRequestContext(ctx, operation) + return +} + +// CreateHttpRequest builds the [http.Request] for the given operation, using +// [context.Background] as the request context. +// +// Any per-operation timeout declared by the operation's [runtime.ClientRequestWriter] +// is silently ignored here, which can leak a context-cancellation channel if the +// caller relies on it. +// +// Deprecated: use [Runtime.CreateHTTPRequestContext] instead, with explicit +// control over the request context and its cancellation. func (r *Runtime) CreateHttpRequest(operation *runtime.ClientOperation) (req *http.Request, err error) { //nolint:revive - _, req, err = r.createHttpRequest(operation) + req, _, err = r.createHTTPRequestContext(context.Background(), operation) return } // Submit a request and when there is a body on success it will turn that into the result // all other things are turned into an api error for swagger which retains the status code. +// +// This call inherits the context possibly put in the operation, otherwise the one possibly put in the [Runtime]. +// If none are set, use [context.Background]. +// +// Any timeout set by parameters is honored. func (r *Runtime) Submit(operation *runtime.ClientOperation) (any, error) { - _, readResponse, _ := operation.Params, operation.Reader, operation.AuthInfo + return r.SubmitContext(r.ensureContext(operation), operation) +} - request, req, err := r.createHttpRequest(operation) +// SubmitContext submits a request and returns the result. +// +// Errors are turned into an api error for swagger which retains the status code. +// +// Unlike [Submit], [SubmitContext] only injects the context provided by the caller: +// contexts possibly cached in operation or runtime are ignored. +// +// On the other hand, a timeout set by parameters is honored. +func (r *Runtime) SubmitContext(parentCtx context.Context, operation *runtime.ClientOperation) (any, error) { + req, cancel, err := r.createHTTPRequestContext(parentCtx, operation) if err != nil { return nil, err } + defer cancel() - r.clientOnce.Do(func() { - r.client = &http.Client{ - Transport: r.Transport, - Jar: r.Jar, - } - }) - - if r.Debug { - b, err2 := httputil.DumpRequestOut(req, true) - if err2 != nil { - return nil, err2 - } - r.logger.Debugf("%s\n", string(b)) - } + r.ensureClient() - var parentCtx context.Context - switch { - case operation.Context != nil: - parentCtx = operation.Context - case r.Context != nil: - parentCtx = r.Context - default: - parentCtx = context.Background() + if err := r.dumpRequest(req); err != nil { + return nil, err } - var ( - ctx context.Context - cancel context.CancelFunc - ) - if request.timeout == 0 { - // There may be a deadline in the context passed to the operation. - // Otherwise, there is no timeout set. - ctx, cancel = context.WithCancel(parentCtx) - } else { - // Sets the timeout passed from request params (by default runtime.DefaultTimeout). - // If there is already a deadline in the parent context, the shortest will - // apply. - ctx, cancel = context.WithTimeout(parentCtx, request.timeout) + // Attach the trace session before Do so the httptrace hooks + // fire during the round-trip. The session emits its trailing + // summary on finish; the response body is consumed by + // ReadResponse downstream, after which finish is called. + var trace *traceSession + if r.Trace { + trace = newTraceSession(r.logger, req.Method, req.URL.String(), + introspectTLSConfig(r.pickClient(operation))) + //nolint:contextcheck // We intentionally derive from req.Context() to layer the trace hooks onto the existing request context. + req = req.WithContext(trace.attach(req.Context())) + if req.Body != nil { + req.Body = trace.wrapRequestBody(req.Body) + } + defer trace.finish() } - defer cancel() - var client *http.Client - if operation.Client != nil { - client = operation.Client - } else { - client = r.client - } - req = req.WithContext(ctx) - res, err := client.Do(req) // make requests, by default follows 10 redirects before failing + res, err := r.pickClient(operation).Do(req) if err != nil { + if trace != nil { + trace.onRoundTripError(err) + } return nil, err } defer res.Body.Close() + if trace != nil { + trace.onResponse(res.StatusCode) + res.Body = trace.wrapResponseBody(res.Body) + } + ct := res.Header.Get(runtime.HeaderContentType) if ct == "" { // this should really never occur ct = r.DefaultMediaType } - if r.Debug { - printBody := true - if ct == runtime.DefaultMime { - printBody = false // Spare the terminal from a binary blob. - } - b, err2 := httputil.DumpResponse(res, printBody) - if err2 != nil { - return nil, err2 - } - r.logger.Debugf("%s\n", string(b)) + if err := r.dumpResponse(res, ct); err != nil { + return nil, err } - mt, _, err := mime.ParseMediaType(ct) + cons, err := r.resolveConsumer(ct) if err != nil { - return nil, fmt.Errorf("parse content type: %s", err) + return nil, err } - cons, ok := r.Consumers[mt] - if !ok { - if cons, ok = r.Consumers["*/*"]; !ok { - // scream about not knowing what to do - return nil, fmt.Errorf("no consumer: %q", ct) - } - } - return readResponse.ReadResponse(r.response(res), cons) + return operation.Reader.ReadResponse(r.response(res), cons) } // SetDebug changes the debug flag. @@ -482,6 +296,17 @@ func (r *Runtime) SetResponseReader(f ClientResponseFunc) { r.response = f } +func (r *Runtime) ensureContext(operation *runtime.ClientOperation) context.Context { + switch { + case operation.Context != nil: //nolint:staticcheck // kept for backward compatibility + return operation.Context + case r.Context != nil: + return r.Context + default: + return context.Background() + } +} + func (r *Runtime) pickScheme(schemes []string) string { if v := r.selectScheme(r.schemes); v != "" { return v @@ -518,16 +343,121 @@ func transportOrDefault(left, right http.RoundTripper) http.RoundTripper { return left } -// takes a client operation and creates equivalent http.Request. -func (r *Runtime) createHttpRequest(operation *runtime.ClientOperation) (*request, *http.Request, error) { //nolint:revive +// ensureClient lazily initializes r.client from r.Transport and r.Jar +// on first use. Safe under concurrent calls via sync.Once. +func (r *Runtime) ensureClient() { + r.clientOnce.Do(func() { + r.client = &http.Client{ + Transport: r.Transport, + Jar: r.Jar, + } + }) +} + +// pickClient returns the http.Client to use for this operation: the +// per-operation override if set, else the runtime's shared client. +func (r *Runtime) pickClient(operation *runtime.ClientOperation) *http.Client { + if operation.Client != nil { + return operation.Client + } + return r.client +} + +// dumpRequest writes the outgoing request to the debug logger when +// r.Debug is enabled. No-op otherwise. Returns the dump error so the +// caller can decide whether to abort the submit. +func (r *Runtime) dumpRequest(req *http.Request) error { + if !r.Debug { + return nil + } + b, err := httputil.DumpRequestOut(req, true) + if err != nil { + return err + } + r.logger.Debugf("%s\n", string(b)) + return nil +} + +// dumpResponse writes the incoming response to the debug logger when +// r.Debug is enabled. The body is omitted for runtime.DefaultMime +// (binary blob). No-op otherwise. +func (r *Runtime) dumpResponse(res *http.Response, ct string) error { + if !r.Debug { + return nil + } + printBody := ct != runtime.DefaultMime // Spare the terminal from a binary blob. + b, err := httputil.DumpResponse(res, printBody) + if err != nil { + return err + } + r.logger.Debugf("%s\n", string(b)) + return nil +} + +// resolveConsumer parses ct and returns the registered Consumer for +// that media type. Lookup is alias-aware (RFC 9512 §2.1 — yaml +// aliases) and, when [Runtime.MatchSuffix] is true, also tolerates +// RFC 6839 structured-syntax suffix media types (+json, +xml, +yaml). +// Falls back to the "*/*" entry if no match found. +func (r *Runtime) resolveConsumer(ct string) (runtime.Consumer, error) { + if _, _, err := mime.ParseMediaType(ct); err != nil { + return nil, fmt.Errorf("parse content type: %w", err) + } + if cons, ok := mediatype.Lookup(r.Consumers, ct, r.matchOpts()...); ok { + return cons, nil + } + if cons, ok := r.Consumers["*/*"]; ok { + return cons, nil + } + // scream about not knowing what to do + return nil, fmt.Errorf("no consumer: %q", ct) +} + +// matchOpts builds the mediatype.MatchOption slice for codec +// lookups on the Runtime, currently just the AllowSuffix opt-in. +func (r *Runtime) matchOpts() []mediatype.MatchOption { + if !r.MatchSuffix { + return nil + } + + return []mediatype.MatchOption{mediatype.AllowSuffix()} +} + +// createHTTPRequestContext is the context-aware builder of a [http.Request]. +// +// The returned [http.Request] carries a context derived from parentCtx that +// honors the per-request timeout set during WriteToRequest. Callers must +// invoke cancel once the response is fully read. +func (r *Runtime) createHTTPRequestContext(parentCtx context.Context, operation *runtime.ClientOperation) (*http.Request, context.CancelFunc, error) { + req, cmt, auth, err := r.prepareRequest(operation) + if err != nil { + return nil, nil, err + } + + httpReq, cancel, err := req.BuildHTTPContext(parentCtx, cmt, r.BasePath, r.Producers, r.Formats, auth) + if err != nil { + return nil, nil, err + } + + r.applyHostScheme(httpReq, operation) + + return httpReq, cancel, nil +} + +// prepareRequest performs the operation-to-request setup that is +// independent of how the http.Request is finally assembled: parameters, +// headers, default authentication, and consumes-media-type selection. +func (r *Runtime) prepareRequest(operation *runtime.ClientOperation) (*request.Request, string, runtime.ClientAuthInfoWriter, error) { params, _, auth := operation.Params, operation.Reader, operation.AuthInfo - request := newRequest(operation.Method, operation.PathPattern, params) + req := request.New(operation.Method, operation.PathPattern, params) + _ = req.SetTimeout(DefaultTimeout) // the timeout may be overridden by ClientRequestWriter + req.SetConsumes(operation.ConsumesMediaTypes) accept := make([]string, 0, len(operation.ProducesMediaTypes)) accept = append(accept, operation.ProducesMediaTypes...) - if err := request.SetHeaderParam(runtime.HeaderAccept, accept...); err != nil { - return nil, nil, err + if err := req.SetHeaderParam(runtime.HeaderAccept, accept...); err != nil { + return nil, "", nil, err } if auth == nil && r.DefaultAuthentication != nil { @@ -538,39 +468,75 @@ func (r *Runtime) createHttpRequest(operation *runtime.ClientOperation) (*reques return r.DefaultAuthentication.AuthenticateRequest(req, reg) }) } - // if auth != nil { - // if err := auth.AuthenticateRequest(request, r.Formats); err != nil { - // return nil, err - // } - //} - - // Enhancement proposal: https://github.com/go-openapi/runtime/issues/386 - cmt := r.DefaultMediaType - for _, mediaType := range operation.ConsumesMediaTypes { - // Pick first non-empty media type - if mediaType != "" { - cmt = mediaType - break - } - } - if _, ok := r.Producers[cmt]; !ok && cmt != runtime.MultipartFormMime && cmt != runtime.URLencodedFormMime { - return nil, nil, fmt.Errorf("none of producers: %v registered. try %s", r.Producers, cmt) + cmt := pickConsumesMediaType(operation.ConsumesMediaTypes, r.Producers, r.DefaultMediaType, r.matchOpts()...) + if _, ok := mediatype.Lookup(r.Producers, cmt, r.matchOpts()...); !ok && cmt != runtime.MultipartFormMime && cmt != runtime.URLencodedFormMime { + return nil, "", nil, fmt.Errorf("none of producers: %v registered. try %s", r.Producers, cmt) } - req, err := request.buildHTTP(cmt, r.BasePath, r.Producers, r.Formats, auth) - if err != nil { - return nil, nil, err - } - req.URL.Scheme = r.pickScheme(operation.Schemes) - req.URL.Host = r.Host - req.Host = r.Host - return request, req, nil + return req, cmt, auth, nil } -func basePool(pool *x509.CertPool) *x509.CertPool { - if pool == nil { - return x509.NewCertPool() +// applyHostScheme stamps the runtime's host and the operation-selected +// scheme onto the freshly built http.Request. +func (r *Runtime) applyHostScheme(httpReq *http.Request, operation *runtime.ClientOperation) { + httpReq.URL.Scheme = r.pickScheme(operation.Schemes) + httpReq.URL.Host = r.Host + httpReq.Host = r.Host +} + +// pickConsumesMediaType selects which Content-Type the client will send. +// +// Selection rules, in priority order: +// +// 1. multipart/form-data if any consumes entry advertises it (it streams +// and preserves per-file Content-Type, regardless of codegen ordering; +// resolves issue #286); +// 2. the first non-empty entry whose mime is either structural +// (multipart/form-data or application/x-www-form-urlencoded — these +// do not need a producer in the map) or has a producer registered in +// producers — this lets the client gracefully skip unregistered +// spec entries instead of erroring at the gate that follows; +// 3. the first non-empty entry overall (preserves the historical error +// path: the gate at the call site reports "none of producers" with +// the unregistered mime, so the diagnostic is unchanged when nothing +// in consumes is registered); +// 4. def, if consumes is empty or all empty strings. +// +// Step 2 closes part of issues #32 and #386: an operation declaring +// `consumes: [application/x-vendor, application/json]` with no vendor +// producer registered now silently uses JSON instead of erroring. +func pickConsumesMediaType(consumes []string, producers map[string]runtime.Producer, def string, opts ...mediatype.MatchOption) string { + for _, mt := range consumes { + if strings.EqualFold(mt, runtime.MultipartFormMime) { + return mt + } + } + var firstNonEmpty string + for _, mt := range consumes { + if mt == "" { + continue + } + if firstNonEmpty == "" { + firstNonEmpty = mt + } + if isStructuralMime(mt) { + return mt + } + if _, ok := mediatype.Lookup(producers, mt, opts...); ok { + return mt + } + } + if firstNonEmpty != "" { + return firstNonEmpty } - return pool + return def +} + +// isStructuralMime reports whether mt is a media type whose body shape +// is owned by the runtime (multipart envelope, urlencoded form). These +// do not require an entry in the producers map. +func isStructuralMime(mt string) bool { + return strings.EqualFold(mt, runtime.MultipartFormMime) || + strings.EqualFold(mt, runtime.URLencodedFormMime) } diff --git a/vendor/github.com/go-openapi/runtime/client/tls.go b/vendor/github.com/go-openapi/runtime/client/tls.go new file mode 100644 index 0000000000..017694fae0 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/client/tls.go @@ -0,0 +1,197 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package client + +import ( + "crypto" + "crypto/tls" + "crypto/x509" + "encoding/pem" + "fmt" + "net/http" + "os" +) + +// TLSClientOptions to configure client authentication with mutual TLS. +type TLSClientOptions struct { + // Certificate is the path to a PEM-encoded certificate to be used for + // client authentication. If set then Key must also be set. + Certificate string + + // LoadedCertificate is the certificate to be used for client authentication. + // This field is ignored if Certificate is set. If this field is set, LoadedKey + // is also required. + LoadedCertificate *x509.Certificate + + // Key is the path to an unencrypted PEM-encoded private key for client + // authentication. This field is required if Certificate is set. + Key string + + // LoadedKey is the key for client authentication. This field is required if + // LoadedCertificate is set. + LoadedKey crypto.PrivateKey + + // CA is a path to a PEM-encoded certificate that specifies the root certificate + // to use when validating the TLS certificate presented by the server. If this field + // (and LoadedCA) is not set, the system certificate pool is used. This field is ignored if LoadedCA + // is set. + CA string + + // LoadedCA specifies the root certificate to use when validating the server's TLS certificate. + // If this field (and CA) is not set, the system certificate pool is used. + LoadedCA *x509.Certificate + + // LoadedCAPool specifies a pool of RootCAs to use when validating the server's TLS certificate. + // If set, it will be combined with the other loaded certificates (see LoadedCA and CA). + // If neither LoadedCA or CA is set, the provided pool will override the system + // certificate pool. + // + // The caller must not use the supplied pool after calling TLSClientAuth. + LoadedCAPool *x509.CertPool + + // ServerName specifies the hostname to use when verifying the server certificate. + // If this field is set then InsecureSkipVerify will be ignored and treated as + // false. + ServerName string + + // InsecureSkipVerify controls whether the certificate chain and hostname presented + // by the server are validated. If true, any certificate is accepted. + InsecureSkipVerify bool + + // VerifyPeerCertificate, if not nil, is called after normal + // certificate verification. It receives the raw ASN.1 certificates + // provided by the peer and also any verified chains that normal processing found. + // If it returns a non-nil error, the handshake is aborted and that error results. + // + // If normal verification fails then the handshake will abort before + // considering this callback. If normal verification is disabled by + // setting InsecureSkipVerify then this callback will be considered but + // the verifiedChains argument will always be nil. + VerifyPeerCertificate func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error + + // VerifyConnection, if not nil, is called after normal certificate + // verification and after [TLSClientOptions.VerifyPeerCertificate] by either a TLS client or + // server. It receives the [tls.ConnectionState] which may be inspected. + // + // Unlike VerifyPeerCertificate, this callback is invoked on every + // connection, including resumed ones, making it suitable for checks + // that must always apply (e.g. certificate pinning). + // + // If it returns a non-nil error, the handshake is aborted and that error results. + VerifyConnection func(tls.ConnectionState) error + + // SessionTicketsDisabled may be set to true to disable session ticket and + // PSK (resumption) support. Note that on clients, session ticket support is + // also disabled if ClientSessionCache is nil. + SessionTicketsDisabled bool + + // ClientSessionCache is a cache of ClientSessionState entries for TLS + // session resumption. It is only used by clients. + ClientSessionCache tls.ClientSessionCache + + // Prevents callers using unkeyed fields. + _ struct{} +} + +// TLSClientAuth creates a [tls.Config] for mutual auth. +func TLSClientAuth(opts TLSClientOptions) (*tls.Config, error) { + // create client tls config + cfg := &tls.Config{ + MinVersion: tls.VersionTLS12, + } + + // load client cert if specified + if opts.Certificate != "" { + cert, err := tls.LoadX509KeyPair(opts.Certificate, opts.Key) + if err != nil { + return nil, fmt.Errorf("tls client cert: %w", err) + } + cfg.Certificates = []tls.Certificate{cert} + } else if opts.LoadedCertificate != nil { + block := pem.Block{Type: "CERTIFICATE", Bytes: opts.LoadedCertificate.Raw} + certPem := pem.EncodeToMemory(&block) + + // PKCS#8 covers RSA, ECDSA, Ed25519, X25519 (the key types tls.X509KeyPair + // understands) and pairs with the canonical "PRIVATE KEY" PEM label. + keyBytes, err := x509.MarshalPKCS8PrivateKey(opts.LoadedKey) + if err != nil { + return nil, fmt.Errorf("tls client priv key: %w", err) + } + + block = pem.Block{Type: "PRIVATE KEY", Bytes: keyBytes} + keyPem := pem.EncodeToMemory(&block) + + cert, err := tls.X509KeyPair(certPem, keyPem) + if err != nil { + return nil, fmt.Errorf("tls client cert: %w", err) + } + cfg.Certificates = []tls.Certificate{cert} + } + + cfg.InsecureSkipVerify = opts.InsecureSkipVerify + + cfg.VerifyPeerCertificate = opts.VerifyPeerCertificate + cfg.VerifyConnection = opts.VerifyConnection + cfg.SessionTicketsDisabled = opts.SessionTicketsDisabled + cfg.ClientSessionCache = opts.ClientSessionCache + + // When no CA certificate is provided, default to the system cert pool + // that way when a request is made to a server known by the system trust store, + // the name is still verified + switch { + case opts.LoadedCA != nil: + caCertPool := basePool(opts.LoadedCAPool) + caCertPool.AddCert(opts.LoadedCA) + cfg.RootCAs = caCertPool + case opts.CA != "": + // load ca cert + caCert, err := os.ReadFile(opts.CA) + if err != nil { + return nil, fmt.Errorf("tls client ca: %w", err) + } + caCertPool := basePool(opts.LoadedCAPool) + caCertPool.AppendCertsFromPEM(caCert) + cfg.RootCAs = caCertPool + case opts.LoadedCAPool != nil: + cfg.RootCAs = opts.LoadedCAPool + } + + // apply servername override + if opts.ServerName != "" { + cfg.InsecureSkipVerify = false + cfg.ServerName = opts.ServerName + } + + return cfg, nil +} + +// TLSTransport creates a [http.RoundTripper] for a client transport,suitable for mutual TLS auth. +func TLSTransport(opts TLSClientOptions) (http.RoundTripper, error) { + cfg, err := TLSClientAuth(opts) + if err != nil { + return nil, err + } + + return &http.Transport{TLSClientConfig: cfg}, nil +} + +// TLSClient creates a [http.Client] for mutual auth. +func TLSClient(opts TLSClientOptions) (*http.Client, error) { + transport, err := TLSTransport(opts) + if err != nil { + return nil, err + } + return &http.Client{Transport: transport}, nil +} + +// basePool returns pool if non-nil; otherwise it returns a new empty cert pool. +// +// Clones the pool provided up front by the caller. +func basePool(pool *x509.CertPool) *x509.CertPool { + if pool == nil { + return x509.NewCertPool() + } + + return pool.Clone() +} diff --git a/vendor/github.com/go-openapi/runtime/client_operation.go b/vendor/github.com/go-openapi/runtime/client_operation.go index ad7277e091..61f6ead34a 100644 --- a/vendor/github.com/go-openapi/runtime/client_operation.go +++ b/vendor/github.com/go-openapi/runtime/client_operation.go @@ -19,12 +19,30 @@ type ClientOperation struct { AuthInfo ClientAuthInfoWriter Params ClientRequestWriter Reader ClientResponseReader - Context context.Context //nolint:containedctx // we precisely want this type to contain the request context - Client *http.Client + // Deprecated: prefer [ContextualTransport.SubmitContext] to pass the request context explicitly. + Context context.Context //nolint:containedctx // we precisely want this type to contain the request context + Client *http.Client } // A ClientTransport implementor knows how to submit Request objects to some destination. type ClientTransport interface { - // Submit(string, RequestWriter, ResponseReader, AuthInfoWriter) (interface{}, error) + // Submit the operation and return the deserialized response or an error. Submit(*ClientOperation) (any, error) } + +// ContextualTransport extends [ClientTransport] with an explicit +// context-aware submission method. +// +// Wrappers such as the OpenTelemetry transport type-assert to this +// interface so they can forward an explicit context to the underlying +// transport without setting the cached [ClientOperation.Context] field. +// +// In v2, SubmitContext will be folded into [ClientTransport] itself +// and the cached [ClientOperation.Context] field removed; this interface +// is the v0.x bridge. +type ContextualTransport interface { + ClientTransport + + // SubmitContext submits the operation using ctx as the request context. + SubmitContext(ctx context.Context, operation *ClientOperation) (any, error) +} diff --git a/vendor/github.com/go-openapi/runtime/client_response.go b/vendor/github.com/go-openapi/runtime/client_response.go index 92668db4ec..7b4b7e40df 100644 --- a/vendor/github.com/go-openapi/runtime/client_response.go +++ b/vendor/github.com/go-openapi/runtime/client_response.go @@ -59,7 +59,7 @@ func (o *APIError) Error() string { if err, ok := o.Response.(error); ok { resp = []byte("'" + sanitizer.Replace(err.Error()) + "'") } else { - resp, _ = json.Marshal(o.Response) + resp, _ = json.Marshal(o.Response) //nolint:errchkjson // error swallowed as this is our last best effort attempt } return fmt.Sprintf("%s (status %d): %s", o.OperationName, o.Code, resp) diff --git a/vendor/github.com/go-openapi/runtime/constants.go b/vendor/github.com/go-openapi/runtime/constants.go index 80de6c8086..ea86cfadbc 100644 --- a/vendor/github.com/go-openapi/runtime/constants.go +++ b/vendor/github.com/go-openapi/runtime/constants.go @@ -21,8 +21,12 @@ const ( DefaultMime = "application/octet-stream" // JSONMime the json mime type. JSONMime = "application/json" - // YAMLMime the [yaml] mime type. - YAMLMime = "application/x-yaml" + // YAMLMime the [yaml] mime type. Set to the canonical RFC 9512 + // name (application/yaml). Legacy forms application/x-yaml, + // text/yaml, and text/x-yaml — per RFC 9512 §2.1 "Deprecated + // alias names for this type" — resolve to the same codec via + // the mediatype alias bridge. + YAMLMime = "application/yaml" // XMLMime the [xml] mime type. XMLMime = "application/xml" // TextMime the text mime type. diff --git a/vendor/github.com/go-openapi/runtime/csv.go b/vendor/github.com/go-openapi/runtime/csv.go index 558d0cb99a..11d60872c3 100644 --- a/vendor/github.com/go-openapi/runtime/csv.go +++ b/vendor/github.com/go-openapi/runtime/csv.go @@ -100,7 +100,7 @@ func CSVConsumer(opts ...CSVOpt) Consumer { default: // support *[][]string, *[]byte, *string - if ptr := reflect.TypeOf(data); ptr.Kind() != reflect.Ptr { + if ptr := reflect.TypeOf(data); ptr.Kind() != reflect.Pointer { return errors.New("destination must be a pointer") } @@ -159,14 +159,14 @@ func CSVConsumer(opts ...CSVOpt) Consumer { // // Supported input underlying types and interfaces, prioritized in this order: // -// - *[csv.Reader] -// - [CSVReader] (reader options are ignored) -// - [io.Reader] -// - [io.WriterTo] -// - [encoding.BinaryMarshaler] -// - [][]string -// - []byte -// - string +// - *[csv.Reader] +// - [CSVReader] (reader options are ignored) +// - [io.Reader] +// - [io.WriterTo] +// - [encoding.BinaryMarshaler] +// - [][]string +// - []byte +// - string // // The producer prioritizes situations where buffering the input is not required. func CSVProducer(opts ...CSVOpt) Producer { diff --git a/vendor/github.com/go-openapi/runtime/file.go b/vendor/github.com/go-openapi/runtime/file.go index 2a85379a74..0420db9440 100644 --- a/vendor/github.com/go-openapi/runtime/file.go +++ b/vendor/github.com/go-openapi/runtime/file.go @@ -5,4 +5,10 @@ package runtime import "github.com/go-openapi/swag/fileutils" +// File represents an uploaded file. Re-exported from +// [fileutils.File] for backwards compatibility. +// +// See [BindForm] (in form.go) for the orchestrator that parses +// multipart / urlencoded request bodies and binds declared file +// fields onto handler-side targets. type File = fileutils.File diff --git a/vendor/github.com/go-openapi/runtime/form.go b/vendor/github.com/go-openapi/runtime/form.go new file mode 100644 index 0000000000..213757f1f4 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/form.go @@ -0,0 +1,362 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package runtime + +import ( + stderrors "errors" + "fmt" + "mime/multipart" + "net/http" + "strings" + + "github.com/go-openapi/errors" +) + +// DefaultMaxUploadFilenameLength is the default cap applied to +// FileHeader.Filename for each declared file when [BindForm] is invoked +// without an explicit [BindFormMaxFilenameLen] option. +// +// Multipart headers are allocated per part; an attacker submitting +// multi-MB filenames inflates the parser's memory footprint. 1 KiB +// matches the IETF guidance for sane filename length and is enough +// for realistic uploads. +const DefaultMaxUploadFilenameLength = 1024 + +// DefaultMaxUploadBodySize limits the size of the body to upload forms to 32MB. +// +// Use an explicit [BindFormMaxBody] option to change this limit. +const DefaultMaxUploadBodySize = int64(32) << 20 + +// filenamePreviewLen caps the byte length of the FileHeader.Filename +// preview embedded as the ParseError.Value field when the helper +// rejects a too-long filename. +const filenamePreviewLen = 32 + +// ValidateFilenameLength enforces the FileHeader.Filename length cap +// that [BindForm] applies via [BindFormFile] declarations. Untyped +// binder paths that fetch the file via [http.Request.FormFile] +// directly (rather than declaring the file through [BindFormFile]) call +// this to opt into the same protection. +// +// Returns nil if filename length is within maxLen or maxLen <= 0. +// Otherwise returns a [*errors.ParseError] suitable for direct return +// from a parameter binder. The error embeds a truncated preview of +// the offending filename to keep the error message bounded. +func ValidateFilenameLength(paramName, paramIn, filename string, maxLen int) error { + if maxLen <= 0 || len(filename) <= maxLen { + return nil + } + preview := filename[:min(len(filename), filenamePreviewLen)] + return errors.NewParseError(paramName, paramIn, preview, + fmt.Errorf("filename length %d exceeds limit %d", len(filename), maxLen)) +} + +// FileBinder is the per-file callback invoked by [BindForm] when a +// declared file field is present. +// +// The callback is responsible for BOTH validating the file (size, MIME, etc.) AND assigning the bound +// file to its destination — typically using: +// +// o.FieldName = &runtime.File{Data: file, Header: header} +// +// Returning a non-nil error surfaces the error in [BindForm]'s per-field +// accumulator. Errors from the binder flow through verbatim — the +// binder is expected to produce HTTP-aware errors (e.g. +// [errors.ExceedsMaximum] from go-openapi/validate). +type FileBinder func(file multipart.File, header *multipart.FileHeader) error + +// BindOption configures [BindForm]. The variadic style keeps simple +// call sites simple and lets new knobs (security caps, additional +// behaviour) be added without breaking the signature. +type BindOption func(*bindConfig) + +type multipartFormLimits struct { + maxBody int64 + maxFiles int + maxFilenameLen int +} + +type bindConfig struct { + multipartFormLimits + + maxParseMemory int64 + files []formFileSpec +} + +type formFileSpec struct { + name string + required bool + bind FileBinder +} + +// BindFormMaxParseMemory caps the in-memory portion of a multipart +// body. Bytes beyond this are spilled to temporary files on disk by +// the stdlib parser. 0 (the default) defers to the stdlib's 32 MB. +// +// This option does NOT cap total body bytes — see [BindFormMaxBody] +// for that. The default body cap ([DefaultMaxUploadBodySize] = 32 MB) +// is applied even when this option is not supplied, so out of the box +// [BindForm] is bounded; callers with stricter or looser requirements +// adjust via [BindFormMaxBody]. +func BindFormMaxParseMemory(n int64) BindOption { + return func(c *bindConfig) { c.maxParseMemory = n } +} + +// BindFormMaxBody caps the size of the body read from a http form before parsing. +// +// The limit is set to 32MB by default. This default limit is applied for any n=0. +// +// The limit is disabled for n<0, assuming the caller has already capped the body size upstream. +func BindFormMaxBody(n int64) BindOption { + return func(c *bindConfig) { c.maxBody = n } +} + +// BindFormMaxFiles rejects parses where the total number of file +// parts across all field names exceeds n. 0 (the default) means no +// cap. Exceeding the cap is a fatal error — [BindForm] returns +// fatal=true and no per-file binders run. +func BindFormMaxFiles(n int) BindOption { + return func(c *bindConfig) { c.maxFiles = n } +} + +// BindFormMaxFilenameLen rejects per-file headers whose Filename +// length exceeds n. 0 means no cap; the default applied when this +// option is not supplied is [DefaultMaxUploadFilenameLength]. The +// cap is a per-field bind error (non-fatal); other declared files +// still run. +func BindFormMaxFilenameLen(n int) BindOption { + return func(c *bindConfig) { c.maxFilenameLen = n } +} + +// BindFormFile declares a file field to bind under the given form +// name. If required is true and the field is absent, [BindForm] +// produces the per-field error. +// +// errors.NewParseError(name, "formData", "", http.ErrMissingFile) +// +// If required is false, absence is silent (no error, no bind). +// +// The bind callback runs only when the field is present. It is the +// site where both validation and assignment happen — see [FileBinder]. +// +// FileHeader.Filename is attacker-controlled text; the binder MUST +// NOT use it directly as a filesystem path. The helper does not +// touch the filesystem. +func BindFormFile(name string, required bool, bind FileBinder) BindOption { + return func(c *bindConfig) { + c.files = append(c.files, formFileSpec{ + name: name, + required: required, + bind: bind, + }) + } +} + +// BindForm parses r as multipart/form-data, falling back to +// application/x-www-form-urlencoded when the request is not +// multipart. On success, r.MultipartForm and r.PostForm are populated; +// the caller can read non-file form values via [Values](r.Form) after +// the call returns. +// +// All errors produced by BindForm itself (parse failure, missing +// required field, cap exceeded) are [*errors.ParseError] values built +// via [errors.NewParseError], matching the untyped +// middleware/parameter.go path. Errors returned by per-file binders +// flow through verbatim — binders own their HTTP-aware error shape. +// +// Per-file binders declared via [BindFormFile] run in declaration +// order after a successful parse. Their errors are accumulated and +// returned wrapped in [errors.CompositeValidationError]; the caller +// typically appends the returned err to its own []error and continues +// with non-file parameter binding. +// +// Return semantics: +// +// - fatal=true, err!=nil: parse failure or a hard cap (e.g. +// [BindFormMaxFiles]) was exceeded. No per-file binders ran; the +// caller MUST return err immediately. +// - fatal=false, err!=nil: one or more per-file binders produced +// errors. The form parsed successfully; r.Form is populated. The +// caller appends err to its accumulator and continues. +// - fatal=false, err==nil: full success. +// +// fatal==true implies err!=nil. +// +// Defaults applied out of the box: +// +// - Total body bytes capped at [DefaultMaxUploadBodySize] (32 MB) +// via [http.MaxBytesReader]. Adjust with [BindFormMaxBody] +// (negative n disables, when the caller has already capped the +// body upstream). +// - FileHeader.Filename length capped at +// [DefaultMaxUploadFilenameLength]. Adjust with +// [BindFormMaxFilenameLen]. +// +// Caller responsibilities the helper does NOT cover: +// +// - Set [http.Server.ReadTimeout] / [http.Server.IdleTimeout] to defend +// against slow-read attacks. +// - Decompress Content-Encoding: gzip request bodies upstream if +// the API accepts them, using a size-limited reader. +// - Treat FileHeader.Filename as untrusted user input; never use +// it directly as a filesystem path. +func BindForm(r *http.Request, opts ...BindOption) (fatal bool, err error) { + cfg := bindConfig{ + multipartFormLimits: multipartFormLimits{ + maxFilenameLen: DefaultMaxUploadFilenameLength, + }, + } + for _, opt := range opts { + opt(&cfg) + } + + if perr := parseFormBody(r, cfg.maxParseMemory, cfg.maxBody); perr != nil { + // Body-cap hit gets the 413 status; everything else maps to a + // 400 ParseError. parseFormBody returns the raw stdlib error + // in both cases — the HTTP-aware wrapping happens here. + var maxBytesErr *http.MaxBytesError + if stderrors.As(perr, &maxBytesErr) { + return true, errors.New(http.StatusRequestEntityTooLarge, "formData: %v", perr) + } + return true, errors.NewParseError("body", "formData", "", perr) + } + + if cfg.maxFiles > 0 { + if got := countFileParts(r); got > cfg.maxFiles { + return true, errors.NewParseError("body", "formData", "", + fmt.Errorf("multipart form contains %d file parts, exceeds limit %d", got, cfg.maxFiles)) + } + } + + var bindErrs []error + for _, spec := range cfg.files { + if e := bindFormFile(r, spec, cfg.maxFilenameLen); e != nil { + bindErrs = append(bindErrs, e) + } + } + if len(bindErrs) > 0 { + return false, errors.CompositeValidationError(bindErrs...) + } + return false, nil +} + +// parseFormBody parses the request body. Content-Type drives the +// parser: multipart/form-data → r.ParseMultipartForm, everything else +// → r.ParseForm (stdlib's parsePostForm only actually reads the body +// when Content-Type is application/x-www-form-urlencoded, so calling +// ParseForm is safe for unrecognised types). +// +// Caveat: ParseMultipartForm calls ParseForm internally and discards its error +// when the body turns out not to be multipart, returning ErrNotMultipart instead +// — the subsequent retry then short-circuits because r.PostForm is already +// set. Content-type-based routing avoids the lossy detour. +// +// Returns the raw stdlib error on failure; the caller (BindForm) +// handles HTTP-aware wrapping (413 for MaxBytesError, 400 ParseError +// otherwise). +// +// maxMemory == 0 falls through to the stdlib default (32 MB). +// maxBody == 0 defaults to DefaultMaxUploadBodySize; maxBody < 0 +// disables the body cap (caller has capped upstream). +func parseFormBody(r *http.Request, maxMemory, maxBody int64) error { + if r.Body != nil && maxBody >= 0 { + if maxBody == 0 { + maxBody = DefaultMaxUploadBodySize + } + r.Body = http.MaxBytesReader(nil, r.Body, maxBody) + } + + mt, _, _ := ContentType(r.Header) + if mt == MultipartFormMime { + //nolint:gosec // G120: false positive -- see below + // gosec doesn't track the Body. + // See https://github.com/securego/gosec/blob/de65614d10a6b84029e3e1215567b8ce7e490f23/testutils/g120_samples.go#L57 + return r.ParseMultipartForm(maxMemory) + } + return r.ParseForm() +} + +func countFileParts(r *http.Request) int { + if r.MultipartForm == nil { + return 0 + } + var n int + for _, fhs := range r.MultipartForm.File { + n += len(fhs) + } + + return n +} + +// FormFile resolves a file field from a parsed form body, transparently +// handling both content types accepted for `type: file` parameters by +// the OpenAPI 2.0 spec: +// +// - multipart/form-data — delegates to [http.Request.FormFile]. +// - application/x-www-form-urlencoded — looks up the field in +// r.PostForm and synthesizes a [multipart.File] backed by the +// value bytes plus a [multipart.FileHeader] with Filename equal +// to the field name and Size set to the byte length. +// +// Returns [http.ErrMissingFile] when the field is absent under either +// content type. Callers must have parsed the body upstream (e.g. via +// [BindForm] or [http.Request.ParseForm]) before reading from the +// urlencoded path — [http.Request.FormFile] takes care of parsing on +// the multipart path. +// +// Presence is the only criterion for binding a urlencoded file: an +// empty value (e.g. `file=`) is bound as a zero-byte file. +func FormFile(r *http.Request, name string) (multipart.File, *multipart.FileHeader, error) { + file, header, err := r.FormFile(name) + if err == nil { + return file, header, nil + } + if !stderrors.Is(err, http.ErrNotMultipart) { + return nil, nil, err + } + + values, present := r.PostForm[name] + if !present { + return nil, nil, http.ErrMissingFile + } + value := values[0] + return urlencodedFile{Reader: strings.NewReader(value)}, + &multipart.FileHeader{Filename: name, Size: int64(len(value))}, + nil +} + +// urlencodedFile adapts a urlencoded form value (already buffered in +// memory by [http.Request.ParseForm]) to the [multipart.File] +// interface. The embedded [strings.Reader] supplies Read/ReadAt/Seek; +// Close is a no-op since there is no resource to release. +type urlencodedFile struct { + *strings.Reader +} + +func (urlencodedFile) Close() error { return nil } + +func bindFormFile(r *http.Request, spec formFileSpec, maxFilenameLen int) error { + file, header, err := FormFile(r, spec.name) + if err != nil { + if stderrors.Is(err, http.ErrMissingFile) { + if spec.required { + return errors.New(http.StatusBadRequest, "formData: %v", http.ErrMissingFile) + } + + return nil + } + + return errors.NewParseError(spec.name, "formData", "", err) + } + + if err := ValidateFilenameLength(spec.name, "formData", header.Filename, maxFilenameLen); err != nil { + return err + } + + if spec.bind == nil { + return nil + } + + return spec.bind(file, header) +} diff --git a/vendor/github.com/go-openapi/runtime/go.work b/vendor/github.com/go-openapi/runtime/go.work index efa29dcac2..73479f9ad8 100644 --- a/vendor/github.com/go-openapi/runtime/go.work +++ b/vendor/github.com/go-openapi/runtime/go.work @@ -1,6 +1,8 @@ use ( . ./client-middleware/opentracing + ./docs/examples + ./server-middleware ) go 1.25.0 diff --git a/vendor/github.com/go-openapi/runtime/go.work.sum b/vendor/github.com/go-openapi/runtime/go.work.sum deleted file mode 100644 index eaf8fddf6b..0000000000 --- a/vendor/github.com/go-openapi/runtime/go.work.sum +++ /dev/null @@ -1,119 +0,0 @@ -github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/go-openapi/errors v0.22.2/go.mod h1:+n/5UdIqdVnLIJ6Q9Se8HNGUXYaY6CN8ImWzfi/Gzp0= -github.com/go-openapi/jsonpointer v0.22.0/go.mod h1:xt3jV88UtExdIkkL7NloURjRQjbeUgcxFblMjq2iaiU= -github.com/go-openapi/jsonreference v0.21.1/go.mod h1:PWs8rO4xxTUqKGu+lEvvCxD5k2X7QYkKAepJyCmSTT8= -github.com/go-openapi/swag v0.24.1/go.mod h1:sm8I3lCPlspsBBwUm1t5oZeWZS0s7m/A+Psg0ooRU0A= -github.com/go-openapi/swag/cmdutils v0.24.0/go.mod h1:uxib2FAeQMByyHomTlsP8h1TtPd54Msu2ZDU/H5Vuf8= -github.com/go-openapi/swag/conv v0.24.0/go.mod h1:jbn140mZd7EW2g8a8Y5bwm8/Wy1slLySQQ0ND6DPc2c= -github.com/go-openapi/swag/fileutils v0.24.0/go.mod h1:3SCrCSBHyP1/N+3oErQ1gP+OX1GV2QYFSnrTbzwli90= -github.com/go-openapi/swag/jsonname v0.24.0/go.mod h1:GXqrPzGJe611P7LG4QB9JKPtUZ7flE4DOVechNaDd7Q= -github.com/go-openapi/swag/jsonutils v0.24.0/go.mod h1:vBowZtF5Z4DDApIoxcIVfR8v0l9oq5PpYRUuteVu6f0= -github.com/go-openapi/swag/loading v0.24.0/go.mod h1:gShCN4woKZYIxPxbfbyHgjXAhO61m88tmjy0lp/LkJk= -github.com/go-openapi/swag/mangling v0.24.0/go.mod h1:Jm5Go9LHkycsz0wfoaBDkdc4CkpuSnIEf62brzyCbhc= -github.com/go-openapi/swag/netutils v0.24.0/go.mod h1:WRgiHcYTnx+IqfMCtu0hy9oOaPR0HnPbmArSRN1SkZM= -github.com/go-openapi/swag/stringutils v0.24.0/go.mod h1:5nUXB4xA0kw2df5PRipZDslPJgJut+NjL7D25zPZ/4w= -github.com/go-openapi/swag/typeutils v0.24.0/go.mod h1:q8C3Kmk/vh2VhpCLaoR2MVWOGP8y7Jc8l82qCTd1DYI= -github.com/go-openapi/swag/yamlutils v0.24.0/go.mod h1:DpKv5aYuaGm/sULePoeiG8uwMpZSfReo1HR3Ik0yaG8= -github.com/go-openapi/testify/enable/yaml/v2 v2.4.0/go.mod h1:14iV8jyyQlinc9StD7w1xVPW3CO3q1Gj04Jy//Kw4VM= -github.com/go-openapi/testify/v2 v2.4.0/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= -github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= -github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30 h1:BHT1/DKsYDGkUgQ2jmMaozVcdk+sVfz0+1ZJq4zkWgw= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= -github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= -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.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= -github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= -github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= -github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= -github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.mongodb.org/mongo-driver v1.17.6/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ= -go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= -golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= -golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8= -golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= -golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= -golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= -golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= -golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= -golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= -golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI= -golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c= -golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU= -golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= -golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= -golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= -golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/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.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0= -golang.org/x/telemetry v0.0.0-20250807160809-1a19826ec488/go.mod h1:fGb/2+tgXXjhjHsTNdVEEMZNWA0quBnfrO+AfoDSAKw= -golang.org/x/telemetry v0.0.0-20260109210033-bd525da824e2/go.mod h1:b7fPSJ0pKZ3ccUh8gnTONJxhn3c/PS6tyzQvyqw4iA8= -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.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= -golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA= -golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss= -golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg= -golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM= -golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU= -golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/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.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= -golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= -golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw= -golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= -golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w= -golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc= -golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg= -golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= -golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/vendor/github.com/go-openapi/runtime/interfaces.go b/vendor/github.com/go-openapi/runtime/interfaces.go index a8b4b318d9..8521198907 100644 --- a/vendor/github.com/go-openapi/runtime/interfaces.go +++ b/vendor/github.com/go-openapi/runtime/interfaces.go @@ -99,3 +99,25 @@ type Validatable interface { type ContextValidatable interface { ContextValidate(context.Context, strfmt.Registry) error } + +// ContentTyper is implemented by values that declare their own MIME +// content type. The client runtime consults it in two places: +// +// - on a body payload set via [SetBodyParam]: when the payload is a +// stream (io.Reader, io.ReadCloser) and ContentType returns a +// non-empty value, that value becomes the wire Content-Type +// header instead of the operation's picked consumes entry. +// +// - on individual file values inside a multipart upload: their per- +// part Content-Type header is taken from ContentType() rather +// than sniffed via http.DetectContentType. +// +// An empty string return is treated as "no opinion" and the runtime +// falls back to its default selection. Values that have no content +// type to declare may simply not implement the interface. +// +// See docs/MEDIA_TYPES.md for the full client-side selection +// algorithm. +type ContentTyper interface { + ContentType() string +} diff --git a/vendor/github.com/go-openapi/runtime/middleware/context.go b/vendor/github.com/go-openapi/runtime/middleware/context.go index 1f85e86b53..12abfd4829 100644 --- a/vendor/github.com/go-openapi/runtime/middleware/context.go +++ b/vendor/github.com/go-openapi/runtime/middleware/context.go @@ -5,10 +5,9 @@ package middleware import ( stdContext "context" + stderrors "errors" "fmt" "net/http" - "net/url" - "path" "strings" "sync" @@ -17,19 +16,21 @@ import ( "github.com/go-openapi/loads" "github.com/go-openapi/spec" "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag/typeutils" "github.com/go-openapi/runtime" "github.com/go-openapi/runtime/logger" "github.com/go-openapi/runtime/middleware/untyped" "github.com/go-openapi/runtime/security" + "github.com/go-openapi/runtime/server-middleware/docui" + "github.com/go-openapi/runtime/server-middleware/mediatype" + "github.com/go-openapi/runtime/server-middleware/negotiate" ) // Debug when true turns on verbose logging. var Debug = logger.DebugEnabled() // Logger is the standard library logger used for printing debug messages. -// -// (Note: The correct spelling is "library", not "libra". "Libra" is a zodiac sign/constellation and wouldn't make sense in this context.) var Logger logger.Logger = logger.StandardLogger{} func debugLogfFunc(lg logger.Logger) func(string, ...any) { @@ -75,11 +76,103 @@ func (fn ResponderFunc) WriteResponse(rw http.ResponseWriter, pr runtime.Produce // used throughout to store request context with the standard context attached // to the [http.Request]. type Context struct { - spec *loads.Document - analyzer *analysis.Spec - api RoutableAPI - router Router - debugLogf func(string, ...any) // a logging function to debug context and all components using it + spec *loads.Document + analyzer *analysis.Spec + api RoutableAPI + router Router + debugLogf func(string, ...any) // a logging function to debug context and all components using it + ignoreParameters bool // see SetIgnoreParameters / WithIgnoreParameters + matchSuffix bool // see SetMatchSuffix / WithMatchSuffix +} + +// NewRoutableContext creates a new context for a routable API. +// +// If a nil Router is provided, the [DefaultRouter] ([denco]-based) will be used. +func NewRoutableContext(spec *loads.Document, routableAPI RoutableAPI, routes Router) *Context { + var an *analysis.Spec + if spec != nil { + an = analysis.New(spec.Spec()) + } + + return NewRoutableContextWithAnalyzedSpec(spec, an, routableAPI, routes) +} + +// NewRoutableContextWithAnalyzedSpec is like [NewRoutableContext] but takes as input an already analysed spec. +// +// If a nil Router is provided, the [DefaultRouter] ([denco]-based) will be used. +func NewRoutableContextWithAnalyzedSpec(spec *loads.Document, an *analysis.Spec, routableAPI RoutableAPI, routes Router) *Context { + // Either there are no spec doc and analysis, or both of them. + if (spec != nil || an != nil) && (spec == nil || an == nil) { + panic(fmt.Errorf("%d: %s", http.StatusInternalServerError, "routable context requires either both spec doc and analysis, or none of them")) + } + + return &Context{ + spec: spec, + api: routableAPI, + analyzer: an, + router: routes, + debugLogf: debugLogfFunc(nil), + } +} + +// NewContext creates a new context wrapper. +// +// If a nil Router is provided, the [DefaultRouter] ([denco]-based) will be used. +func NewContext(spec *loads.Document, api *untyped.API, routes Router) *Context { + var an *analysis.Spec + if spec != nil { + an = analysis.New(spec.Spec()) + } + ctx := &Context{ + spec: spec, + analyzer: an, + router: routes, + debugLogf: debugLogfFunc(nil), + } + ctx.api = newRoutableUntypedAPI(spec, api, ctx) + + return ctx +} + +// Serve serves the specified spec with the specified api registrations as a [http.Handler]. +func Serve(spec *loads.Document, api *untyped.API) http.Handler { + return ServeWithBuilder(spec, api, PassthroughBuilder) +} + +// SetIgnoreParameters toggles the legacy parameter-stripping behaviour for +// Accept negotiation server-wide. When set, every internal call to +// [NegotiateContentType] from this Context applies [WithIgnoreParameters]. +// +// Returns the receiver for fluent configuration: +// +// ctx := middleware.NewContext(spec, api, nil).SetIgnoreParameters(true) +// +// See [WithIgnoreParameters] for the rationale and an example. +func (c *Context) SetIgnoreParameters(ignore bool) *Context { + c.ignoreParameters = ignore + + return c +} + +// SetMatchSuffix toggles RFC 6839 structured-syntax suffix tolerance +// server-wide. When enabled, both Accept negotiation and codec lookup +// fall back through the suffix base for the recognised suffixes +// (+json, +xml, +yaml) — so an operation declaring +// consumes: [application/json] also accepts request bodies sent with +// Content-Type: application/vnd.api+json (or any other +json variant). +// +// Default: strict (false). Use only when interoperating with clients +// that do not strictly abide by the spec. +// +// Returns the receiver for fluent configuration: +// +// ctx := middleware.NewContext(spec, api, nil).SetMatchSuffix(true) +// +// See [negotiate.WithMatchSuffix] for the per-call form and rationale. +func (c *Context) SetMatchSuffix(enable bool) *Context { + c.matchSuffix = enable + + return c } type routableUntypedAPI struct { @@ -95,53 +188,57 @@ func newRoutableUntypedAPI(spec *loads.Document, api *untyped.API, context *Cont if spec == nil || api == nil { return nil } + analyzer := analysis.New(spec.Spec()) for method, hls := range analyzer.Operations() { um := strings.ToUpper(method) for path, op := range hls { schemes := analyzer.SecurityRequirementsFor(op) - if oh, ok := api.OperationHandlerFor(method, path); ok { - if handlers == nil { - handlers = make(map[string]map[string]http.Handler) + oh, ok := api.OperationHandlerFor(method, path) + if !ok { + continue + } + + if handlers == nil { + handlers = make(map[string]map[string]http.Handler) + } + if b, ok := handlers[um]; !ok || b == nil { + handlers[um] = make(map[string]http.Handler) + } + + var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // lookup route info in the context + route, rCtx, _ := context.RouteInfo(r) + if rCtx != nil { + r = rCtx } - if b, ok := handlers[um]; !ok || b == nil { - handlers[um] = make(map[string]http.Handler) + + // bind and validate the request using reflection + var bound any + var validation error + bound, r, validation = context.BindAndValidate(r, route) + if validation != nil { + context.Respond(w, r, route.Produces, route, validation) + return } - var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // lookup route info in the context - route, rCtx, _ := context.RouteInfo(r) - if rCtx != nil { - r = rCtx - } - - // bind and validate the request using reflection - var bound any - var validation error - bound, r, validation = context.BindAndValidate(r, route) - if validation != nil { - context.Respond(w, r, route.Produces, route, validation) - return - } - - // actually handle the request - result, err := oh.Handle(bound) - if err != nil { - // respond with failure - context.Respond(w, r, route.Produces, route, err) - return - } - - // respond with success - context.Respond(w, r, route.Produces, route, result) - }) - - if len(schemes) > 0 { - handler = newSecureAPI(context, handler) + // actually handle the request + result, err := oh.Handle(bound) + if err != nil { + // respond with failure + context.Respond(w, r, route.Produces, route, err) + return } - handlers[um][path] = handler + + // respond with success + context.Respond(w, r, route.Produces, route, result) + }) + + if len(schemes) > 0 { + handler = newSecureAPI(context, handler) } + handlers[um][path] = handler } } @@ -192,60 +289,6 @@ func (r *routableUntypedAPI) DefaultConsumes() string { return r.defaultConsumes } -// NewRoutableContext creates a new context for a routable API. -// -// If a nil Router is provided, the [DefaultRouter] ([denco]-based) will be used. -func NewRoutableContext(spec *loads.Document, routableAPI RoutableAPI, routes Router) *Context { - var an *analysis.Spec - if spec != nil { - an = analysis.New(spec.Spec()) - } - - return NewRoutableContextWithAnalyzedSpec(spec, an, routableAPI, routes) -} - -// NewRoutableContextWithAnalyzedSpec is like [NewRoutableContext] but takes as input an already analysed spec. -// -// If a nil Router is provided, the [DefaultRouter] ([denco]-based) will be used. -func NewRoutableContextWithAnalyzedSpec(spec *loads.Document, an *analysis.Spec, routableAPI RoutableAPI, routes Router) *Context { - // Either there are no spec doc and analysis, or both of them. - if (spec != nil || an != nil) && (spec == nil || an == nil) { - panic(fmt.Errorf("%d: %s", http.StatusInternalServerError, "routable context requires either both spec doc and analysis, or none of them")) - } - - return &Context{ - spec: spec, - api: routableAPI, - analyzer: an, - router: routes, - debugLogf: debugLogfFunc(nil), - } -} - -// NewContext creates a new context wrapper. -// -// If a nil Router is provided, the [DefaultRouter] ([denco]-based) will be used. -func NewContext(spec *loads.Document, api *untyped.API, routes Router) *Context { - var an *analysis.Spec - if spec != nil { - an = analysis.New(spec.Spec()) - } - ctx := &Context{ - spec: spec, - analyzer: an, - router: routes, - debugLogf: debugLogfFunc(nil), - } - ctx.api = newRoutableUntypedAPI(spec, api, ctx) - - return ctx -} - -// Serve serves the specified spec with the specified api registrations as a [http.Handler]. -func Serve(spec *loads.Document, api *untyped.API) http.Handler { - return ServeWithBuilder(spec, api, PassthroughBuilder) -} - // ServeWithBuilder serves the specified spec with the specified api registrations as a [http.Handler] that is decorated // by the Builder. func ServeWithBuilder(spec *loads.Document, api *untyped.API, builder Builder) http.Handler { @@ -319,57 +362,42 @@ func (c *Context) RequiredProduces() []string { // BindValidRequest binds a params object to a request but only when the request is valid // if the request is not valid an error will be returned. func (c *Context) BindValidRequest(request *http.Request, route *MatchedRoute, binder RequestBinder) error { - var res []error var requestContentType string // check and validate content type, select consumer if runtime.HasBody(request) { - ct, _, err := runtime.ContentType(request.Header) + ct, cons, err := c.bindRequestBody(request, route) if err != nil { - res = append(res, err) - } else { - c.debugLogf("validating content type for %q against [%s]", ct, strings.Join(route.Consumes, ", ")) - if err := validateContentType(route.Consumes, ct); err != nil { - res = append(res, err) - } - if len(res) == 0 { - cons, ok := route.Consumers[ct] - if !ok { - res = append(res, errors.New(http.StatusInternalServerError, "no consumer registered for %s", ct)) - } else { - route.Consumer = cons - requestContentType = ct - } - } + return errors.CompositeValidationError(err) } + + // happy path + requestContentType = ct + route.Consumer = cons } // check and validate the response format - if len(res) == 0 { - // if the route does not provide Produces and a default contentType could not be identified - // based on a body, typical for GET and DELETE requests, then default contentType to. - if len(route.Produces) == 0 && requestContentType == "" { - requestContentType = "*/*" - } + // if the route does not provide Produces and a default contentType could not be identified + // based on a body, typical for GET and DELETE requests, then default contentType to. + if len(route.Produces) == 0 && requestContentType == "" { + requestContentType = "*/*" + } - if str := NegotiateContentType(request, route.Produces, requestContentType); str == "" { - res = append(res, errors.InvalidResponseFormat(request.Header.Get(runtime.HeaderAccept), route.Produces)) - } + str := negotiate.ContentType(request, route.Produces, requestContentType, c.negotiateOpts()...) + if str == "" { + return errors.CompositeValidationError( + errors.InvalidResponseFormat(request.Header.Get(runtime.HeaderAccept), route.Produces), + ) + } + + if binder == nil { + return nil } // now bind the request with the provided binder // it's assumed the binder will also validate the request and return an error if the // request is invalid - if binder != nil && len(res) == 0 { - if err := binder.BindRequest(request, route); err != nil { - return err - } - } - - if len(res) > 0 { - return errors.CompositeValidationError(res...) - } - return nil + return binder.BindRequest(request, route) } // ContentType gets the parsed value of a content type @@ -414,6 +442,7 @@ func (c *Context) RouteInfo(request *http.Request) (*MatchedRoute, *http.Request if route, ok := c.LookupRoute(request); ok { rCtx = stdContext.WithValue(rCtx, ctxMatchedRoute, route) + request.Pattern = route.BasePath + route.PathPattern return route, request.WithContext(rCtx), ok } @@ -431,7 +460,7 @@ func (c *Context) ResponseFormat(r *http.Request, offers []string) (string, *htt return v, r } - format := NegotiateContentType(r, offers, "") + format := negotiate.ContentType(r, offers, "", c.negotiateOpts()...) if format != "" { c.debugLogf("[%s %s] set response format %q in context", r.Method, r.URL.Path, format) r = r.WithContext(stdContext.WithValue(rCtx, ctxResponseFormat, format)) @@ -453,44 +482,6 @@ func (c *Context) ResetAuth(request *http.Request) *http.Request { return request.WithContext(rctx) } -// Authorize authorizes the request -// Returns the principal object and a shallow copy of the request when its -// context doesn't contain the principal, otherwise the same request or an error -// (the last) if one of the authenticators returns one or an Unauthenticated error. -func (c *Context) Authorize(request *http.Request, route *MatchedRoute) (any, *http.Request, error) { - if route == nil || !route.HasAuth() { - return nil, nil, nil - } - - var rCtx = request.Context() - if v := rCtx.Value(ctxSecurityPrincipal); v != nil { - return v, request, nil - } - - applies, usr, err := route.Authenticators.Authenticate(request, route) - if !applies || err != nil || !route.Authenticators.AllowsAnonymous() && usr == nil { - if err != nil { - return nil, nil, err - } - return nil, nil, errors.Unauthenticated("invalid credentials") - } - if route.Authorizer != nil { - if err := route.Authorizer.Authorize(request, usr); err != nil { - if _, ok := err.(errors.Error); ok { - return nil, nil, err - } - - return nil, nil, errors.New(http.StatusForbidden, "%v", err) - } - } - - rCtx = request.Context() - - rCtx = stdContext.WithValue(rCtx, ctxSecurityPrincipal, usr) - rCtx = stdContext.WithValue(rCtx, ctxSecurityScopes, route.Authenticator.AllScopes()) - return usr, request.WithContext(rCtx), nil -} - // BindAndValidate binds and validates the request // Returns the validation map and a shallow copy of the request when its context // doesn't contain the validation, otherwise it returns the same request or an @@ -523,91 +514,29 @@ func (c *Context) NotFound(rw http.ResponseWriter, r *http.Request) { // Respond renders the response after doing some content negotiation. func (c *Context) Respond(rw http.ResponseWriter, r *http.Request, produces []string, route *MatchedRoute, data any) { c.debugLogf("responding to %s %s with produces: %v", r.Method, r.URL.Path, produces) - offers := []string{} - for _, mt := range produces { - if mt != c.api.DefaultProduces() { - offers = append(offers, mt) - } - } - // the default producer is last so more specific producers take precedence - offers = append(offers, c.api.DefaultProduces()) - c.debugLogf("offers: %v", offers) + offers := c.buildOffers(produces) var format string format, r = c.ResponseFormat(r, offers) rw.Header().Set(runtime.HeaderContentType, format) if resp, ok := data.(Responder); ok { - producers := route.Producers - // producers contains keys with normalized format, if a format has MIME type parameter such as `text/plain; charset=utf-8` - // then you must provide `text/plain` to get the correct producer. HOWEVER, format here is not normalized. - prod, ok := producers[normalizeOffer(format)] - if !ok { - prods := c.api.ProducersFor(normalizeOffers([]string{c.api.DefaultProduces()})) - pr, ok := prods[c.api.DefaultProduces()] - if !ok { - panic(fmt.Errorf("%d: %s", http.StatusInternalServerError, cantFindProducer(format))) - } - prod = pr - } - resp.WriteResponse(rw, prod) + c.respondWithResponder(rw, r, route, resp, format) return } if err, ok := data.(error); ok { - if format == "" { - rw.Header().Set(runtime.HeaderContentType, runtime.JSONMime) - } - - if realm := security.FailedBasicAuth(r); realm != "" { - rw.Header().Set("WWW-Authenticate", fmt.Sprintf("Basic realm=%q", realm)) - } - - if route == nil || route.Operation == nil { - c.api.ServeErrorFor("")(rw, r, err) - return - } - c.api.ServeErrorFor(route.Operation.ID)(rw, r, err) + c.respondWithError(rw, r, produces, route, err, format) return } if route == nil || route.Operation == nil { - rw.WriteHeader(http.StatusOK) - if r.Method == http.MethodHead { - return - } - producers := c.api.ProducersFor(normalizeOffers(offers)) - prod, ok := producers[format] - if !ok { - panic(fmt.Errorf("%d: %s", http.StatusInternalServerError, cantFindProducer(format))) - } - if err := prod.Produce(rw, data); err != nil { - panic(err) // let the recovery middleware deal with this - } + c.respondWithoutCode(rw, r, data, format, offers) return } if _, code, ok := route.Operation.SuccessResponse(); ok { - rw.WriteHeader(code) - if code == http.StatusNoContent || r.Method == http.MethodHead { - return - } - - producers := route.Producers - prod, ok := producers[format] - if !ok { - if !ok { - prods := c.api.ProducersFor(normalizeOffers([]string{c.api.DefaultProduces()})) - pr, ok := prods[c.api.DefaultProduces()] - if !ok { - panic(fmt.Errorf("%d: %s", http.StatusInternalServerError, cantFindProducer(format))) - } - prod = pr - } - } - if err := prod.Produce(rw, data); err != nil { - panic(err) // let the recovery middleware deal with this - } + c.respondWithCode(rw, r, route, code, data, format) return } @@ -618,57 +547,76 @@ func (c *Context) Respond(rw http.ResponseWriter, r *http.Request, produces []st // // This handler includes a swagger spec, router and the contract defined in the swagger spec. // -// A spec UI ([SwaggerUI]) is served at {API base path}/docs and the spec document at /swagger.json -// (these can be modified with uiOptions). +// A spec UI ([docui.SwaggerUI]) is served at {API base path}/docs and the spec document at /swagger.json +// (these can be modified with combined [UIOption]). +// +// Deprecated: use [Context.APIHandlerWithUI] with [docui.SwaggerUI] middleware instead. func (c *Context) APIHandlerSwaggerUI(builder Builder, opts ...UIOption) http.Handler { - b := builder - if b == nil { - b = PassthroughBuilder - } - - specPath, uiOpts, specOpts := c.uiOptionsForHandler(opts) - var swaggerUIOpts SwaggerUIOpts - fromCommonToAnyOptions(uiOpts, &swaggerUIOpts) - - return Spec(specPath, c.spec.Raw(), SwaggerUI(swaggerUIOpts, c.RoutesHandler(b)), specOpts...) + return c.APIHandlerWithUI(builder, docui.UseSwaggerUI, c.uiOptionsForHandler(opts)...) } // APIHandlerRapiDoc returns a handler to serve the API. // // This handler includes a swagger spec, router and the contract defined in the swagger spec. // -// A spec UI ([RapiDoc]) is served at {API base path}/docs and the spec document at /swagger.json -// (these can be modified with uiOptions). +// A spec UI ([docui.RapiDoc]) is served at {API base path}/docs and the spec document at /swagger.json +// (these can be modified with combined [UIOption]). +// +// Deprecated: use [Context.APIHandlerWithUI] with [docui.UseRapiDoc] middleware instead. func (c *Context) APIHandlerRapiDoc(builder Builder, opts ...UIOption) http.Handler { - b := builder - if b == nil { - b = PassthroughBuilder - } - - specPath, uiOpts, specOpts := c.uiOptionsForHandler(opts) - var rapidocUIOpts RapiDocOpts - fromCommonToAnyOptions(uiOpts, &rapidocUIOpts) - - return Spec(specPath, c.spec.Raw(), RapiDoc(rapidocUIOpts, c.RoutesHandler(b)), specOpts...) + return c.APIHandlerWithUI(builder, docui.UseRapiDoc, c.uiOptionsForHandler(opts)...) } // APIHandler returns a handler to serve the API. // // This handler includes a swagger spec, router and the contract defined in the swagger spec. // -// A spec UI ([Redoc]) is served at {API base path}/docs and the spec document at /swagger.json -// (these can be modified with uiOptions). +// A spec UI ([docui.Redoc]) is served at {API base path}/docs and the spec document at /swagger.json +// (these can be modified with combined [UIOption]). +// +// Notice that you may use [Context.APIHandlerWithUI] to use an alternate UI-serving middleware. func (c *Context) APIHandler(builder Builder, opts ...UIOption) http.Handler { + return c.APIHandlerWithUI(builder, docui.UseRedoc, c.uiOptionsForHandler(opts)...) +} + +// APIHandlerWithUI returns a handler to serve the API with a swagger spec and a UI. +// +// This handler includes a swagger spec, router and the contract defined in the swagger spec. +// +// A spec UI is served at {API base path}/docs and the spec document at /swagger.json +// (these can be modified with combined [UIOption]). +// +// Notice that any function that accepts the [docui.Option] set and returns a valid middleware may be injected here. +// +// [Context.APIHandlerWithUI] extends [Context.APIHandler], and supersedes [Context.APIHandlerRapiDoc] and [Context.APIHandlerSwaggerUI]. +func (c *Context) APIHandlerWithUI(builder Builder, uiMiddleware docui.UIMiddleware, opts ...docui.Option) http.Handler { b := builder if b == nil { b = PassthroughBuilder } - specPath, uiOpts, specOpts := c.uiOptionsForHandler(opts) - var redocOpts RedocOpts - fromCommonToAnyOptions(uiOpts, &redocOpts) + // the UI titles defaults to the title in the spec + const extraOptions = 2 + prepend := make([]docui.Option, 0, len(opts)+extraOptions) + var title string - return Spec(specPath, c.spec.Raw(), Redoc(redocOpts, c.RoutesHandler(b)), specOpts...) + sp := c.spec.Spec() + if sp != nil && sp.Info != nil && sp.Info.Title != "" { + title = sp.Info.Title + } + if title != "" { + prepend = append(prepend, docui.WithUITitle(title)) + } + + prepend = append(prepend, docui.WithUIBasePath(c.BasePath())) + prepend = append(prepend, opts...) + + // aligns spec serve path with UI setting to fetch spec document. + return docui.UseSpec(c.spec.Raw(), docui.WithSpecPathFromOptions(prepend...))( + uiMiddleware(prepend...)( + c.RoutesHandler(b), + ), + ) } // RoutesHandler returns a handler to serve the API, just the routes and the contract defined in the swagger spec. @@ -680,37 +628,192 @@ func (c *Context) RoutesHandler(builder Builder) http.Handler { return NewRouter(c, b(NewOperationExecutor(c))) } -func (c Context) uiOptionsForHandler(opts []UIOption) (string, uiOptions, []SpecOption) { - var title string - sp := c.spec.Spec() - if sp != nil && sp.Info != nil && sp.Info.Title != "" { - title = sp.Info.Title +// authorizeImpl is the real authentication+authorization body shared +// between the production and dev-only variants of [Context.Authorize]. +// See context_skipauth_disabled.go (default build) and +// context_skipauth_enabled.go (the `openapi_unsafe_skipauth` build tag). +// +// The doc on the exported Authorize describes the user-facing +// contract; this function MUST NOT change semantics for the +// production path. +func (c *Context) authorizeImpl(request *http.Request, route *MatchedRoute) (any, *http.Request, error) { + if route == nil || !route.HasAuth() { + return nil, nil, nil } - // default options (may be overridden) - const baseOptions = 2 - optsForContext := make([]UIOption, 0, len(opts)+baseOptions) - optsForContext = append(optsForContext, - WithUIBasePath(c.BasePath()), - WithUITitle(title), - ) - optsForContext = append(optsForContext, opts...) - uiOpts := uiOptionsWithDefaults(optsForContext) + var rCtx = request.Context() + if v := rCtx.Value(ctxSecurityPrincipal); v != nil { + return v, request, nil + } + + applies, usr, err := route.Authenticators.Authenticate(request, route) + if !applies || err != nil || !route.Authenticators.AllowsAnonymous() && typeutils.IsZero(usr) { + if err != nil { + return nil, nil, err + } + return nil, nil, errors.Unauthenticated("invalid credentials") + } + if route.Authorizer != nil { + if err := route.Authorizer.Authorize(request, usr); err != nil { + var apiError errors.Error + if stderrors.As(err, &apiError) { + return nil, nil, err + } + + return nil, nil, errors.New(http.StatusForbidden, "%v", err) + } + } + + rCtx = request.Context() + + rCtx = stdContext.WithValue(rCtx, ctxSecurityPrincipal, usr) + rCtx = stdContext.WithValue(rCtx, ctxSecurityScopes, route.Authenticator.AllScopes()) + return usr, request.WithContext(rCtx), nil +} + +func (c *Context) bindRequestBody(request *http.Request, route *MatchedRoute) (string, runtime.Consumer, error) { + ct, _, err := runtime.ContentType(request.Header) + if err != nil { + return "", nil, err + } + + c.debugLogf("validating content type for %q against [%s]", ct, strings.Join(route.Consumes, ", ")) + if err := validateContentType(route.Consumes, ct); err != nil { + return "", nil, err + } + + cons, ok := mediatype.Lookup(route.Consumers, ct, c.matchOpts()...) + if !ok { + return "", nil, errors.New(http.StatusInternalServerError, "no consumer registered for %s", ct) + } + + return ct, cons, nil +} + +func (c *Context) respondWithResponder(rw http.ResponseWriter, r *http.Request, route *MatchedRoute, resp Responder, format string) { + _ = r + producers := route.Producers + + // producers contains keys with normalized format, if a format has MIME type parameter such as `text/plain; charset=utf-8` + // then you must provide `text/plain` to get the correct producer. HOWEVER, format here is not normalized. + prod, ok := producers[normalizeOffer(format)] + if !ok { + prods := c.api.ProducersFor(normalizeOffers([]string{c.api.DefaultProduces()})) + pr, ok := prods[c.api.DefaultProduces()] + if !ok { + panic(fmt.Errorf("%d: %s", http.StatusInternalServerError, cantFindProducer(format))) + } + prod = pr + } + + resp.WriteResponse(rw, prod) +} + +func (c *Context) respondWithError(rw http.ResponseWriter, r *http.Request, produces []string, route *MatchedRoute, err error, format string) { + _ = produces + + if format == "" { + rw.Header().Set(runtime.HeaderContentType, runtime.JSONMime) + } + + if realm := security.FailedBasicAuth(r); realm != "" { + rw.Header().Set("WWW-Authenticate", fmt.Sprintf("Basic realm=%q", realm)) + } + + if route == nil || route.Operation == nil { + c.api.ServeErrorFor("")(rw, r, err) + return + } + + c.api.ServeErrorFor(route.Operation.ID)(rw, r, err) +} + +func (c *Context) respondWithoutCode(rw http.ResponseWriter, r *http.Request, data any, format string, offers []string) { + rw.WriteHeader(http.StatusOK) + if r.Method == http.MethodHead { + return + } + + producers := c.api.ProducersFor(normalizeOffers(offers)) + prod, ok := producers[format] + if !ok { + panic(fmt.Errorf("%d: %s", http.StatusInternalServerError, cantFindProducer(format))) + } + + if err := prod.Produce(rw, data); err != nil { + panic(err) // let the recovery middleware deal with this + } +} + +func (c *Context) buildOffers(produces []string) []string { + offers := make([]string, 0, len(produces)+1) + + for _, mt := range produces { + if mt != c.api.DefaultProduces() { + offers = append(offers, mt) + } + } + + // the default producer is last so more specific producers take precedence + offers = append(offers, c.api.DefaultProduces()) + c.debugLogf("offers: %v", offers) + + return offers +} + +func (c *Context) respondWithCode(rw http.ResponseWriter, r *http.Request, route *MatchedRoute, code int, data any, format string) { + rw.WriteHeader(code) + if code == http.StatusNoContent || r.Method == http.MethodHead { + return + } + + producers := route.Producers + prod, ok := producers[format] + if !ok { + if !ok { + prods := c.api.ProducersFor(normalizeOffers([]string{c.api.DefaultProduces()})) + pr, ok := prods[c.api.DefaultProduces()] + if !ok { + panic(fmt.Errorf("%d: %s", http.StatusInternalServerError, cantFindProducer(format))) + } + prod = pr + } + } - // If spec URL is provided, there is a non-default path to serve the spec. - // This makes sure that the UI middleware is aligned with the Spec middleware. - u, _ := url.Parse(uiOpts.SpecURL) - var specPath string - if u != nil { - specPath = u.Path + if err := prod.Produce(rw, data); err != nil { + panic(err) // let the recovery middleware deal with this } +} + +// uiOptionsForHandler bridges the deprecated [UIOption] set to the new [docui.Option] set. +func (c Context) uiOptionsForHandler(opts []UIOption) []docui.Option { + uiOpts := uiOptionsWithDefaults(opts) - pth, doc := path.Split(specPath) - if pth == "." { - pth = "" + return uiOpts.toFuncOptions() +} + +func (c *Context) negotiateOpts() []negotiate.Option { + var opts []negotiate.Option + if c.ignoreParameters { + opts = append(opts, negotiate.WithIgnoreParameters(true)) + } + if c.matchSuffix { + opts = append(opts, negotiate.WithMatchSuffix(true)) + } + + return opts +} + +// matchOpts builds the mediatype.MatchOption slice that the +// codec-lookup and Content-Type validation paths apply server-wide. +// Mirrors negotiateOpts but at the mediatype level (without going +// through the negotiate.Option wrapper). +func (c *Context) matchOpts() []mediatype.MatchOption { + if !c.matchSuffix { + return nil } - return pth, uiOpts, []SpecOption{WithSpecDocument(doc)} + return []mediatype.MatchOption{mediatype.AllowSuffix()} } func cantFindProducer(format string) string { diff --git a/vendor/github.com/go-openapi/runtime/middleware/context_skipauth_disabled.go b/vendor/github.com/go-openapi/runtime/middleware/context_skipauth_disabled.go new file mode 100644 index 0000000000..c8cd01a434 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/middleware/context_skipauth_disabled.go @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +//go:build !openapi_unsafe_skipauth + +package middleware + +import "net/http" + +// Authorize authorizes the request. +// +// Returns the principal object and a shallow copy of the request when its +// context doesn't contain the principal, otherwise the same request or an error +// (the last) if one of the authenticators returns one or an Unauthenticated error. +// +// This is the production variant — compiled when the build tag +// `openapi_unsafe_skipauth` is NOT set. There is no skip-auth check +// in this codepath; the field, setter, and storage for the bypass +// flag are entirely absent from the binary. See the alternate +// implementation in context_skipauth_enabled.go for the dev-only +// bypass mechanism. +func (c *Context) Authorize(request *http.Request, route *MatchedRoute) (any, *http.Request, error) { + return c.authorizeImpl(request, route) +} diff --git a/vendor/github.com/go-openapi/runtime/middleware/context_skipauth_enabled.go b/vendor/github.com/go-openapi/runtime/middleware/context_skipauth_enabled.go new file mode 100644 index 0000000000..2ac8706818 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/middleware/context_skipauth_enabled.go @@ -0,0 +1,61 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +//go:build openapi_unsafe_skipauth + +package middleware + +import ( + "log" + "net/http" + "sync/atomic" +) + +// skipAuthEnabled holds the process-wide skip-auth flag. It only +// exists in binaries built with the `openapi_unsafe_skipauth` tag — +// production binaries (built without the tag) have no field, no +// setter, no storage, and no skip-checking branch in [Context.Authorize]. +// Reflection, unsafe-pointer arithmetic, or a debugger cannot flip +// what is not in the binary. +var skipAuthEnabled atomic.Bool + +// SetSkipAuth toggles a PROCESS-WIDE bypass of authentication AND +// authorization for every operation served by every Context in the +// running program. +// +// DANGER: this disables ALL authentication and ALL authorization. +// Every request to every secured endpoint runs as if it had been +// authorized with a nil principal. Use ONLY on developer +// workstations during early prototyping (e.g. while +// authentication is not yet wired up). +// +// This function exists only when the build tag +// `openapi_unsafe_skipauth` is set: +// +// go build -tags openapi_unsafe_skipauth ./... +// +// Production CI MUST NOT pass this tag. Calls compile to a symbol +// that does not exist in production binaries. +// +// Calling with true emits a one-line WARNING via the stdlib `log` +// package (stderr by default) so the bypass is visible at startup. +// Calling with false silently disables it. +func SetSkipAuth(skip bool) { + skipAuthEnabled.Store(skip) + if skip { + log.Println("WARNING: go-openapi/runtime: SetSkipAuth(true) — authentication and authorization are bypassed for ALL operations. This MUST NOT run in production.") + } +} + +// Authorize is the dev-build variant of the production +// [Context.Authorize] (see context_skipauth_disabled.go for the +// production path). When [SetSkipAuth] has enabled the bypass, this +// returns a nil principal with the original request and no error — +// handlers downstream receive a nil-value principal. Otherwise it +// delegates to the standard authentication+authorization body. +func (c *Context) Authorize(request *http.Request, route *MatchedRoute) (any, *http.Request, error) { + if skipAuthEnabled.Load() { + return nil, request, nil + } + return c.authorizeImpl(request, route) +} diff --git a/vendor/github.com/go-openapi/runtime/middleware/denco/router.go b/vendor/github.com/go-openapi/runtime/middleware/denco/router.go index f89d761cf2..e380a138d5 100644 --- a/vendor/github.com/go-openapi/runtime/middleware/denco/router.go +++ b/vendor/github.com/go-openapi/runtime/middleware/denco/router.go @@ -9,6 +9,7 @@ package denco import ( "errors" "fmt" + "slices" "sort" "strings" ) @@ -29,8 +30,8 @@ const ( // PathParamCharacter indicates a RESTCONF path param. PathParamCharacter = '=' - // MaxSize is max size of records and internal slice. - MaxSize = (1 << 22) - 1 //nolint:mnd + // MaxSize is the maximum size of records and internal slice (encoded over 22 bits). + MaxSize = (1 << baseBits) - 1 ) // Router represents a URL router. @@ -53,9 +54,12 @@ func New() *Router { } } -// Lookup returns data and path parameters that associated with path. +// Lookup returns data and path parameters which are associated to the path. +// // params is a slice of the [Param] that arranged in the order in which parameters appeared. -// e.g. when built routing path is "/path/to/:id/:name" and given path is "/path/to/1/alice". params order is [{"id": "1"}, {"name": "alice"}], not [{"name": "alice"}, {"id": "1"}]. +// +// e.g. when built routing path is "/path/to/:id/:name" and given path is "/path/to/1/alice", +// params order is [{"id": "1"}, {"name": "alice"}], not [{"name": "alice"}, {"id": "1"}]. func (rt *Router) Lookup(path string) (data any, params Params, found bool) { if data, found = rt.static[path]; found { return data, nil, true @@ -144,6 +148,7 @@ func newDoubleArray() *doubleArray { type baseCheck uint32 const ( + baseBits = 22 flagsBits = 10 checkBits = 8 ) @@ -157,7 +162,7 @@ func (bc *baseCheck) SetBase(base int) { } func (bc baseCheck) Check() byte { - return byte(bc) //nolint:gosec // integer conversion is ok + return byte(bc) //nolint:gosec // integer conversion is ok: we pick the last 8 bits } func (bc *baseCheck) SetCheck(check byte) { @@ -213,8 +218,8 @@ func (da *doubleArray) lookup(path string, params []Param, idx int) (*node, []Pa } BACKTRACKING: - for j := len(indices) - 1; j >= 0; j-- { - i, idx := int(indices[j]>>indexOffset), int(indices[j]&indexMask) + for _, j := range slices.Backward(indices) { + i, idx := int(j>>indexOffset), int(j&indexMask) if da.bc[idx].IsSingleParam() { nextIdx := nextIndex(da.bc[idx].Base(), ParamCharacter) if nextIdx >= len(da.bc) { diff --git a/vendor/github.com/go-openapi/runtime/middleware/denco/server.go b/vendor/github.com/go-openapi/runtime/middleware/denco/server.go index e6c0976d8b..3bbbc679d9 100644 --- a/vendor/github.com/go-openapi/runtime/middleware/denco/server.go +++ b/vendor/github.com/go-openapi/runtime/middleware/denco/server.go @@ -9,7 +9,7 @@ import ( "net/http" ) -// Mux represents a multiplexer for HTTP request. +// Mux represents a multiplexer for HTTP requests. type Mux struct{} // NewMux returns a new [Mux]. @@ -17,27 +17,27 @@ func NewMux() *Mux { return &Mux{} } -// GET is shorthand of [Mux].Handler("GET", path, handler). +// GET is shorthand for [Mux.Handler] ("GET", path, handler). func (m *Mux) GET(path string, handler HandlerFunc) Handler { return m.Handler("GET", path, handler) } -// POST is shorthand of [Mux].Handler("POST", path, handler). +// POST is shorthand for [Mux.Handler] ("POST", path, handler). func (m *Mux) POST(path string, handler HandlerFunc) Handler { return m.Handler("POST", path, handler) } -// PUT is shorthand of [Mux].Handler("PUT", path, handler). +// PUT is shorthand for [Mux.Handler] ("PUT", path, handler). func (m *Mux) PUT(path string, handler HandlerFunc) Handler { return m.Handler("PUT", path, handler) } -// HEAD is shorthand of [Mux].Handler("HEAD", path, handler). +// HEAD is shorthand for [Mux.Handler]("HEAD", path, handler). func (m *Mux) HEAD(path string, handler HandlerFunc) Handler { return m.Handler("HEAD", path, handler) } -// Handler returns a handler for HTTP method. +// Handler returns a [Handler] for a HTTP method. func (m *Mux) Handler(method, path string, handler HandlerFunc) Handler { return Handler{ Method: method, @@ -63,7 +63,7 @@ func (m *Mux) Build(handlers []Handler) (http.Handler, error) { return mux, nil } -// Handler represents a handler of HTTP request. +// Handler represents a handler of HTTP requests. type Handler struct { // Method is an HTTP method. Method string @@ -75,7 +75,7 @@ type Handler struct { Func HandlerFunc } -// HandlerFunc is aliased to type of handler function. +// HandlerFunc is an alias to the handler function, similar to [http.HandlerFunc]. type HandlerFunc func(w http.ResponseWriter, r *http.Request, params Params) type serveMux struct { @@ -88,7 +88,7 @@ func newServeMux() *serveMux { } } -// ServeHTTP implements http.Handler interface. +// ServeHTTP implements the [http.Handler] interface. func (mux *serveMux) ServeHTTP(w http.ResponseWriter, r *http.Request) { handler, params := mux.handler(r.Method, r.URL.Path) handler(w, r, params) @@ -97,15 +97,17 @@ func (mux *serveMux) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (mux *serveMux) handler(method, path string) (HandlerFunc, []Param) { if router, found := mux.routers[method]; found { if handler, params, found := router.Lookup(path); found { - return handler.(HandlerFunc), params + return handler.(HandlerFunc), params //nolint:forcetypeassert // type is guaranteed when the path is found } } return NotFound, nil } // NotFound replies to the request with an HTTP 404 not found error. -// NotFound is called when unknown HTTP method or a handler not found. -// If you want to use the your own NotFound handler, please overwrite this variable. +// +// NotFound is called when unknown HTTP methods are being user or a handler not found. +// +// If you want to use your own NotFound handler, please overwrite this variable. var NotFound = func(w http.ResponseWriter, r *http.Request, _ Params) { http.NotFound(w, r) } diff --git a/vendor/github.com/go-openapi/runtime/middleware/negotiate.go b/vendor/github.com/go-openapi/runtime/middleware/negotiate.go deleted file mode 100644 index cb0a85283c..0000000000 --- a/vendor/github.com/go-openapi/runtime/middleware/negotiate.go +++ /dev/null @@ -1,102 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers -// SPDX-License-Identifier: Apache-2.0 - -// Copyright 2013 The Go Authors. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file or at -// https://developers.google.com/open-source/licenses/bsd. - -// this file was taken from the github.com/golang/gddo repository - -package middleware - -import ( - "net/http" - "strings" - - "github.com/go-openapi/runtime/middleware/header" -) - -// NegotiateContentEncoding returns the best offered content encoding for the -// request's Accept-Encoding header. If two offers match with equal weight and -// then the offer earlier in the list is preferred. If no offers are -// acceptable, then "" is returned. -func NegotiateContentEncoding(r *http.Request, offers []string) string { - bestOffer := "identity" - bestQ := -1.0 - specs := header.ParseAccept(r.Header, "Accept-Encoding") - for _, offer := range offers { - for _, spec := range specs { - if spec.Q > bestQ && - (spec.Value == "*" || spec.Value == offer) { - bestQ = spec.Q - bestOffer = offer - } - } - } - if bestQ == 0 { - bestOffer = "" - } - return bestOffer -} - -// NegotiateContentType returns the best offered content type for the request's -// Accept header. If two offers match with equal weight, then the more specific -// offer is preferred. For example, text/* trumps */*. If two offers match -// with equal weight and specificity, then the offer earlier in the list is -// preferred. If no offers match, then defaultOffer is returned. -func NegotiateContentType(r *http.Request, offers []string, defaultOffer string) string { - bestOffer := defaultOffer - bestQ := -1.0 - bestWild := 3 - specs := header.ParseAccept(r.Header, "Accept") - for _, rawOffer := range offers { - offer := normalizeOffer(rawOffer) - // No Accept header: just return the first offer. - if len(specs) == 0 { - return rawOffer - } - for _, spec := range specs { - switch { - case spec.Q == 0.0: - // ignore - case spec.Q < bestQ: - // better match found - case spec.Value == "*/*": - if spec.Q > bestQ || bestWild > 2 { - bestQ = spec.Q - bestWild = 2 - bestOffer = rawOffer - } - case strings.HasSuffix(spec.Value, "/*"): - if strings.HasPrefix(offer, spec.Value[:len(spec.Value)-1]) && - (spec.Q > bestQ || bestWild > 1) { - bestQ = spec.Q - bestWild = 1 - bestOffer = rawOffer - } - default: - if spec.Value == offer && - (spec.Q > bestQ || bestWild > 0) { - bestQ = spec.Q - bestWild = 0 - bestOffer = rawOffer - } - } - } - } - return bestOffer -} - -func normalizeOffers(orig []string) (norm []string) { - for _, o := range orig { - norm = append(norm, normalizeOffer(o)) - } - return -} - -func normalizeOffer(orig string) string { - const maxParts = 2 - return strings.SplitN(orig, ";", maxParts)[0] -} diff --git a/vendor/github.com/go-openapi/runtime/middleware/parameter.go b/vendor/github.com/go-openapi/runtime/middleware/parameter.go index a9d2a36460..4ae8e3d62d 100644 --- a/vendor/github.com/go-openapi/runtime/middleware/parameter.go +++ b/vendor/github.com/go-openapi/runtime/middleware/parameter.go @@ -6,7 +6,7 @@ package middleware import ( "encoding" "encoding/base64" - "fmt" + stderrors "errors" "io" "net/http" "reflect" @@ -56,126 +56,153 @@ func (p *untypedParamBinder) Type() reflect.Type { } func (p *untypedParamBinder) Bind(request *http.Request, routeParams RouteParams, consumer runtime.Consumer, target reflect.Value) error { - // fmt.Println("binding", p.name, "as", p.Type()) switch p.parameter.In { case "query": - data, custom, hasKey, err := p.readValue(runtime.Values(request.URL.Query()), target) - if err != nil { - return err - } - if custom { - return nil - } - - return p.bindValue(data, hasKey, target) + return p.bindQuery(request, routeParams, consumer, target) case "header": - data, custom, hasKey, err := p.readValue(runtime.Values(request.Header), target) - if err != nil { - return err - } - if custom { - return nil - } - return p.bindValue(data, hasKey, target) + return p.bindHeader(request, routeParams, consumer, target) case "path": - data, custom, hasKey, err := p.readValue(routeParams, target) - if err != nil { - return err - } - if custom { - return nil - } - return p.bindValue(data, hasKey, target) + return p.bindPath(request, routeParams, consumer, target) case "formData": - var err error - var mt string + return p.bindFormData(request, routeParams, consumer, target) - mt, _, e := runtime.ContentType(request.Header) - if e != nil { - // because of the interface conversion go thinks the error is not nil - // so we first check for nil and then set the err var if it's not nil - err = e - } + case "body": + return p.bindBody(request, routeParams, consumer, target) + default: + return errors.New(http.StatusInternalServerError, "invalid parameter location: %q", p.parameter.In) + } +} - if err != nil { - return errors.InvalidContentType("", []string{"multipart/form-data", "application/x-www-form-urlencoded"}) - } +func (p *untypedParamBinder) bindQuery(request *http.Request, _ RouteParams, _ runtime.Consumer, target reflect.Value) error { + data, custom, hasKey, err := p.readValue(runtime.Values(request.URL.Query()), target) + if err != nil { + return err + } + if custom { + return nil + } - if mt != "multipart/form-data" && mt != "application/x-www-form-urlencoded" { - return errors.InvalidContentType(mt, []string{"multipart/form-data", "application/x-www-form-urlencoded"}) - } + return p.bindValue(data, hasKey, target) +} - if mt == "multipart/form-data" { - if err = request.ParseMultipartForm(defaultMaxMemory); err != nil { - return errors.NewParseError(p.Name, p.parameter.In, "", err) - } - } +func (p *untypedParamBinder) bindHeader(request *http.Request, _ RouteParams, _ runtime.Consumer, target reflect.Value) error { + data, custom, hasKey, err := p.readValue(runtime.Values(request.Header), target) + if err != nil { + return err + } + if custom { + return nil + } + return p.bindValue(data, hasKey, target) +} - if err = request.ParseForm(); err != nil { - return errors.NewParseError(p.Name, p.parameter.In, "", err) - } +func (p *untypedParamBinder) bindPath(_ *http.Request, routeParams RouteParams, _ runtime.Consumer, target reflect.Value) error { + data, custom, hasKey, err := p.readValue(routeParams, target) + if err != nil { + return err + } + if custom { + return nil + } + return p.bindValue(data, hasKey, target) +} - if p.parameter.Type == "file" { - file, header, ffErr := request.FormFile(p.parameter.Name) - if ffErr != nil { - if p.parameter.Required { - return errors.NewParseError(p.Name, p.parameter.In, "", ffErr) - } +func (p *untypedParamBinder) bindFormData(request *http.Request, _ RouteParams, _ runtime.Consumer, target reflect.Value) error { + mt, _, ctErr := runtime.ContentType(request.Header) + if ctErr != nil { + return errors.InvalidContentType("", []string{runtime.MultipartFormMime, runtime.URLencodedFormMime}) + } - return nil - } + if mt != runtime.MultipartFormMime && mt != runtime.URLencodedFormMime { + return errors.InvalidContentType(mt, []string{runtime.MultipartFormMime, runtime.URLencodedFormMime}) + } - target.Set(reflect.ValueOf(runtime.File{Data: file, Header: header})) - return nil - } + // Parse via the shared helper. The helper routes on Content-Type + // (multipart/form-data → ParseMultipartForm; all non-multipart types, + // including application/x-www-form-urlencoded, → ParseForm) + // and applies the default 32 MiB body cap via http.MaxBytesReader. + // Idempotent across the per-parameter loop: stdlib short-circuits + // when r.MultipartForm / r.PostForm are already populated. + if _, perr := runtime.BindForm(request, runtime.BindFormMaxParseMemory(defaultMaxMemory)); perr != nil { + return perr + } - if request.MultipartForm != nil { - data, custom, hasKey, rvErr := p.readValue(runtime.Values(request.MultipartForm.Value), target) - if rvErr != nil { - return rvErr - } - if custom { + if p.parameter.Type == "file" { + // runtime.FormFile handles both multipart/form-data and + // application/x-www-form-urlencoded (OpenAPI 2.0 permits + // either consumes for `type: file`), and surfaces a + // missing field as http.ErrMissingFile under both. + file, header, ffErr := runtime.FormFile(request, p.parameter.Name) + if ffErr != nil { + if stderrors.Is(ffErr, http.ErrMissingFile) { + if p.parameter.Required { + return errors.NewParseError(p.Name, p.parameter.In, "", http.ErrMissingFile) + } return nil } - return p.bindValue(data, hasKey, target) + return errors.NewParseError(p.Name, p.parameter.In, "", ffErr) } - data, custom, hasKey, err := p.readValue(runtime.Values(request.PostForm), target) - if err != nil { + + // Mirror the FileHeader.Filename length cap that BindForm + // applies to typed (codegen) paths through BindFormFile, so + // untyped formData bindings get the same protection. + if err := runtime.ValidateFilenameLength(p.Name, p.parameter.In, header.Filename, + runtime.DefaultMaxUploadFilenameLength); err != nil { return err } + + target.Set(reflect.ValueOf(runtime.File{Data: file, Header: header})) + return nil + } + + if request.MultipartForm != nil { + data, custom, hasKey, rvErr := p.readValue(runtime.Values(request.MultipartForm.Value), target) + if rvErr != nil { + return rvErr + } if custom { return nil } return p.bindValue(data, hasKey, target) + } + data, custom, hasKey, err := p.readValue(runtime.Values(request.PostForm), target) + if err != nil { + return err + } + if custom { + return nil + } + return p.bindValue(data, hasKey, target) +} - case "body": - newValue := reflect.New(target.Type()) - if !runtime.HasBody(request) { - if p.parameter.Default != nil { - target.Set(reflect.ValueOf(p.parameter.Default)) - } +func (p *untypedParamBinder) bindBody(request *http.Request, _ RouteParams, consumer runtime.Consumer, target reflect.Value) error { + newValue := reflect.New(target.Type()) + if !runtime.HasBody(request) { + if p.parameter.Default != nil { + target.Set(reflect.ValueOf(p.parameter.Default)) + } + return nil + } + + if err := consumer.Consume(request.Body, newValue.Interface()); err != nil { + if stderrors.Is(err, io.EOF) && p.parameter.Default != nil { + target.Set(reflect.ValueOf(p.parameter.Default)) return nil } - if err := consumer.Consume(request.Body, newValue.Interface()); err != nil { - if err == io.EOF && p.parameter.Default != nil { - target.Set(reflect.ValueOf(p.parameter.Default)) - return nil - } - tpe := p.parameter.Type - if p.parameter.Format != "" { - tpe = p.parameter.Format - } - return errors.InvalidType(p.Name, p.parameter.In, tpe, nil) + tpe := p.parameter.Type + if p.parameter.Format != "" { + tpe = p.parameter.Format } - target.Set(reflect.Indirect(newValue)) - return nil - default: - return fmt.Errorf("%d: invalid parameter location %q", http.StatusInternalServerError, p.parameter.In) + return errors.InvalidType(p.Name, p.parameter.In, tpe, nil) } + + target.Set(reflect.Indirect(newValue)) + + return nil } func (p *untypedParamBinder) typeForSchema(tpe, format string, items *spec.Items) reflect.Type { @@ -261,20 +288,51 @@ func (p *untypedParamBinder) bindValue(data []string, hasKey bool, target reflec if p.parameter.Type == typeArray { return p.setSliceFieldValue(target, p.parameter.Default, data, hasKey) } + var d string if len(data) > 0 { d = data[len(data)-1] } + return p.setFieldValue(target, p.parameter.Default, d, hasKey) } -func (p *untypedParamBinder) setFieldValue(target reflect.Value, defaultValue any, data string, hasKey bool) error { //nolint:gocyclo +func (p *untypedParamBinder) isMissingAndRequired(hasKey bool, data string) bool { + return p.parameter.Required && + p.parameter.Default == nil && + (!hasKey || (!p.parameter.AllowEmptyValue && data == "")) +} + +func (p *untypedParamBinder) setByte(target, defVal reflect.Value, tpe, data string) error { + if data == "" { + if target.CanSet() { + target.SetBytes(defVal.Bytes()) + } + + return nil + } + + b, err := base64.StdEncoding.DecodeString(data) + if err != nil { + b, err = base64.URLEncoding.DecodeString(data) + if err != nil { + return errors.InvalidType(p.Name, p.parameter.In, tpe, data) + } + } + if target.CanSet() { + target.SetBytes(b) + } + + return nil +} + +func (p *untypedParamBinder) setFieldValue(target reflect.Value, defaultValue any, data string, hasKey bool) error { tpe := p.parameter.Type if p.parameter.Format != "" { tpe = p.parameter.Format } - if (!hasKey || (!p.parameter.AllowEmptyValue && data == "")) && p.parameter.Required && p.parameter.Default == nil { + if p.isMissingAndRequired(hasKey, data) { return errors.Required(p.Name, p.parameter.In, data) } @@ -292,27 +350,15 @@ func (p *untypedParamBinder) setFieldValue(target reflect.Value, defaultValue an } if tpe == "byte" { - if data == "" { - if target.CanSet() { - target.SetBytes(defVal.Bytes()) - } - return nil - } - - b, err := base64.StdEncoding.DecodeString(data) - if err != nil { - b, err = base64.URLEncoding.DecodeString(data) - if err != nil { - return errors.InvalidType(p.Name, p.parameter.In, tpe, data) - } - } - if target.CanSet() { - target.SetBytes(b) - } - return nil + return p.setByte(target, defVal, tpe, data) } - switch target.Kind() { //nolint:exhaustive // we want to check only types that map from a swagger parameter + return p.setReflectFieldValue(target, defVal, tpe, data, hasKey) +} + +//nolint:gocyclo,cyclop // not much we can simplify further significantly: the big case with all types is unavoidable. +func (p *untypedParamBinder) setReflectFieldValue(target, defVal reflect.Value, tpe, data string, hasKey bool) error { + switch target.Kind() { // we want to check only types that map from a swagger parameter case reflect.Bool: if data == "" { if target.CanSet() { @@ -327,6 +373,7 @@ func (p *untypedParamBinder) setFieldValue(target reflect.Value, defaultValue an if target.CanSet() { target.SetBool(b) } + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: if data == "" { if target.CanSet() { @@ -394,8 +441,8 @@ func (p *untypedParamBinder) setFieldValue(target reflect.Value, defaultValue an target.SetString(value) } - case reflect.Ptr: - if data == "" && defVal.Kind() == reflect.Ptr { + case reflect.Pointer: + if data == "" && defVal.Kind() == reflect.Pointer { if target.CanSet() { target.Set(defVal) } @@ -412,6 +459,7 @@ func (p *untypedParamBinder) setFieldValue(target reflect.Value, defaultValue an default: return errors.InvalidType(p.Name, p.parameter.In, tpe, data) } + return nil } @@ -419,20 +467,30 @@ func (p *untypedParamBinder) tryUnmarshaler(target reflect.Value, defaultValue a if !target.CanSet() { return false, nil } + // When a type implements encoding.TextUnmarshaler we'll use that instead of reflecting some more - if reflect.PointerTo(target.Type()).Implements(textUnmarshalType) { - if defaultValue != nil && len(data) == 0 { - target.Set(reflect.ValueOf(defaultValue)) - return true, nil - } - value := reflect.New(target.Type()) - if err := value.Interface().(encoding.TextUnmarshaler).UnmarshalText([]byte(data)); err != nil { - return true, err - } - target.Set(reflect.Indirect(value)) + ttyp := target.Type() + if !reflect.PointerTo(ttyp).Implements(textUnmarshalType) { + return false, nil + } + + if defaultValue != nil && len(data) == 0 { + target.Set(reflect.ValueOf(defaultValue)) return true, nil } - return false, nil + + value := reflect.New(ttyp) + if !value.CanInterface() { + return false, nil + } + + if err := value.Interface().(encoding.TextUnmarshaler).UnmarshalText([]byte(data)); err != nil { //nolint:forcetypeassert // this is guaranteed by the reflect check above + return true, err + } + + target.Set(reflect.Indirect(value)) + + return true, nil } func (p *untypedParamBinder) readFormattedSliceFieldValue(data string, target reflect.Value) ([]string, bool, error) { diff --git a/vendor/github.com/go-openapi/runtime/middleware/rapidoc.go b/vendor/github.com/go-openapi/runtime/middleware/rapidoc.go deleted file mode 100644 index 1574defb41..0000000000 --- a/vendor/github.com/go-openapi/runtime/middleware/rapidoc.go +++ /dev/null @@ -1,83 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers -// SPDX-License-Identifier: Apache-2.0 - -package middleware - -import ( - "bytes" - "fmt" - "html/template" - "net/http" - "path" -) - -// RapiDocOpts configures the [RapiDoc] middlewares. -type RapiDocOpts struct { - // BasePath for the UI, defaults to: / - BasePath string - - // Path combines with BasePath to construct the path to the UI, defaults to: "docs". - Path string - - // SpecURL is the URL of the spec document. - // - // Defaults to: /swagger.json - SpecURL string - - // Title for the documentation site, default to: API documentation - Title string - - // Template specifies a custom template to serve the UI - Template string - - // RapiDocURL points to the js asset that generates the rapidoc site. - // - // Defaults to https://unpkg.com/rapidoc/dist/rapidoc-min.js - RapiDocURL string -} - -func (r *RapiDocOpts) EnsureDefaults() { - common := toCommonUIOptions(r) - common.EnsureDefaults() - fromCommonToAnyOptions(common, r) - - // rapidoc-specifics - if r.RapiDocURL == "" { - r.RapiDocURL = rapidocLatest - } - if r.Template == "" { - r.Template = rapidocTemplate - } -} - -// RapiDoc creates a [middleware] to serve a documentation site for a swagger spec. -// -// This allows for altering the spec before starting the [http] listener. -func RapiDoc(opts RapiDocOpts, next http.Handler) http.Handler { - opts.EnsureDefaults() - - pth := path.Join(opts.BasePath, opts.Path) - tmpl := template.Must(template.New("rapidoc").Parse(opts.Template)) - assets := bytes.NewBuffer(nil) - if err := tmpl.Execute(assets, opts); err != nil { - panic(fmt.Errorf("cannot execute template: %w", err)) - } - - return serveUI(pth, assets.Bytes(), next) -} - -const ( - rapidocLatest = "https://unpkg.com/rapidoc/dist/rapidoc-min.js" - rapidocTemplate = ` - - - {{ .Title }} - - - - - - - -` -) diff --git a/vendor/github.com/go-openapi/runtime/middleware/redoc.go b/vendor/github.com/go-openapi/runtime/middleware/redoc.go deleted file mode 100644 index 1007409a30..0000000000 --- a/vendor/github.com/go-openapi/runtime/middleware/redoc.go +++ /dev/null @@ -1,97 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers -// SPDX-License-Identifier: Apache-2.0 - -package middleware - -import ( - "bytes" - "fmt" - "html/template" - "net/http" - "path" -) - -// RedocOpts configures the [Redoc] middlewares. -type RedocOpts struct { - // BasePath for the UI, defaults to: / - BasePath string - - // Path combines with BasePath to construct the path to the UI, defaults to: "docs". - Path string - - // SpecURL is the URL of the spec document. - // - // Defaults to: /swagger.json - SpecURL string - - // Title for the documentation site, default to: API documentation - Title string - - // Template specifies a custom template to serve the UI - Template string - - // RedocURL points to the js that generates the redoc site. - // - // Defaults to: https://cdn.jsdelivr.net/npm/redoc/bundles/redoc.standalone.js - RedocURL string -} - -// EnsureDefaults in case some options are missing. -func (r *RedocOpts) EnsureDefaults() { - common := toCommonUIOptions(r) - common.EnsureDefaults() - fromCommonToAnyOptions(common, r) - - // redoc-specifics - if r.RedocURL == "" { - r.RedocURL = redocLatest - } - if r.Template == "" { - r.Template = redocTemplate - } -} - -// Redoc creates a [middleware] to serve a documentation site for a swagger spec. -// -// This allows for altering the spec before starting the [http] listener. -func Redoc(opts RedocOpts, next http.Handler) http.Handler { - opts.EnsureDefaults() - - pth := path.Join(opts.BasePath, opts.Path) - tmpl := template.Must(template.New("redoc").Parse(opts.Template)) - assets := bytes.NewBuffer(nil) - if err := tmpl.Execute(assets, opts); err != nil { - panic(fmt.Errorf("cannot execute template: %w", err)) - } - - return serveUI(pth, assets.Bytes(), next) -} - -const ( - redocLatest = "https://cdn.jsdelivr.net/npm/redoc/bundles/redoc.standalone.js" - redocTemplate = ` - - - {{ .Title }} - - - - - - - - - - - - - -` -) diff --git a/vendor/github.com/go-openapi/runtime/middleware/request.go b/vendor/github.com/go-openapi/runtime/middleware/request.go index ad781663b8..2b8aab08d2 100644 --- a/vendor/github.com/go-openapi/runtime/middleware/request.go +++ b/vendor/github.com/go-openapi/runtime/middleware/request.go @@ -40,8 +40,25 @@ func NewUntypedRequestBinder(parameters map[string]spec.Parameter, spec *spec.Sw // Bind perform the databinding and validation. func (o *UntypedRequestBinder) Bind(request *http.Request, routeParams RouteParams, consumer runtime.Consumer, data any) error { + err := o.bind(request, routeParams, consumer, data) + if err == nil { + return nil // avoids returning a nil-interface + } + + return err +} + +// SetLogger allows for injecting a logger to catch debug entries. +// +// The logger is enabled in DEBUG mode only. +func (o *UntypedRequestBinder) SetLogger(lg logger.Logger) { + o.debugLogf = debugLogfFunc(lg) +} + +func (o *UntypedRequestBinder) bind(request *http.Request, routeParams RouteParams, consumer runtime.Consumer, data any) *errors.CompositeError { val := reflect.Indirect(reflect.ValueOf(data)) isMap := val.Kind() == reflect.Map + var result []error o.debugLogf("binding %d parameters for %s %s", len(o.Parameters), request.Method, request.URL.EscapedPath()) for fieldName, param := range o.Parameters { @@ -56,7 +73,7 @@ func (o *UntypedRequestBinder) Bind(request *http.Request, routeParams RoutePara if isMap { tpe := binder.Type() if tpe == nil { - if param.Schema.Type.Contains(typeArray) { + if param.Schema != nil && param.Schema.Type.Contains(typeArray) { tpe = reflect.TypeFor[[]any]() } else { tpe = reflect.TypeFor[map[string]any]() @@ -94,13 +111,6 @@ func (o *UntypedRequestBinder) Bind(request *http.Request, routeParams RoutePara return nil } -// SetLogger allows for injecting a logger to catch debug entries. -// -// The logger is enabled in DEBUG mode only. -func (o *UntypedRequestBinder) SetLogger(lg logger.Logger) { - o.debugLogf = debugLogfFunc(lg) -} - func (o *UntypedRequestBinder) setDebugLogf(fn func(string, ...any)) { o.debugLogf = fn } diff --git a/vendor/github.com/go-openapi/runtime/middleware/router.go b/vendor/github.com/go-openapi/runtime/middleware/router.go index e828653be7..939cf7337a 100644 --- a/vendor/github.com/go-openapi/runtime/middleware/router.go +++ b/vendor/github.com/go-openapi/runtime/middleware/router.go @@ -21,6 +21,7 @@ import ( "github.com/go-openapi/spec" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag/stringutils" + "github.com/go-openapi/swag/typeutils" ) // RouteParam is a object to capture route params in a framework agnostic way. @@ -292,7 +293,7 @@ func (ras RouteAuthenticators) Authenticate(req *http.Request, route *MatchedRou continue } applies, usr, err := ra.Authenticate(req, route) - if !applies || err != nil || usr == nil { + if !applies || err != nil || typeutils.IsZero(usr) { if err != nil { lastError = err } @@ -348,49 +349,62 @@ func (m *MatchedRoute) NeedsAuth() bool { func (d *defaultRouter) Lookup(method, path string) (*MatchedRoute, bool) { mth := strings.ToUpper(method) d.debugLogf("looking up route for %s %s", method, path) - if Debug { - if len(d.routers) == 0 { + if len(d.routers) == 0 { + if Debug { d.debugLogf("there are no known routers") } + panic("internal error: no router is configured") + } + + if Debug { for meth := range d.routers { d.debugLogf("got a router for %s", meth) } } - if router, ok := d.routers[mth]; ok { - if m, rp, ok := router.Lookup(fpath.Clean(path)); ok && m != nil { - if entry, ok := m.(*routeEntry); ok { - d.debugLogf("found a route for %s %s with %d parameters", method, path, len(entry.Parameters)) - var params RouteParams - for _, p := range rp { - v, err := url.PathUnescape(p.Value) - if err != nil { - d.debugLogf("failed to escape %q: %v", p.Value, err) - v = p.Value - } - // a workaround to handle fragment/composing parameters until they are supported in denco router - // check if this parameter is a fragment within a path segment - const enclosureSize = 2 - if xpos := strings.Index(entry.PathPattern, fmt.Sprintf("{%s}", p.Name)) + len(p.Name) + enclosureSize; xpos < len(entry.PathPattern) && entry.PathPattern[xpos] != '/' { - // extract fragment parameters - ep := strings.Split(entry.PathPattern[xpos:], "/")[0] - pnames, pvalues := decodeCompositParams(p.Name, v, ep, nil, nil) - for i, pname := range pnames { - params = append(params, RouteParam{Name: pname, Value: pvalues[i]}) - } - } else { - // use the parameter directly - params = append(params, RouteParam{Name: p.Name, Value: v}) - } - } - return &MatchedRoute{routeEntry: *entry, Params: params}, true + + router, ok := d.routers[mth] + if !ok { + d.debugLogf("couldn't find a route by method for %s %s", method, path) + return nil, false + } + + m, rp, ok := router.Lookup(fpath.Clean(escapeLiteralColons(path))) + if !ok || m == nil { + d.debugLogf("couldn't find a route by path for %s %s", method, path) + return nil, false + } + + entry, ok := m.(*routeEntry) + if !ok { + return nil, false + } + + d.debugLogf("found a route for %s %s with %d parameters", method, path, len(entry.Parameters)) + var params RouteParams + for _, p := range rp { + v, err := url.PathUnescape(p.Value) + if err != nil { + d.debugLogf("failed to escape %q: %v", p.Value, err) + v = p.Value + } + + // a workaround to handle fragment/composing parameters until they are supported in denco router + // check if this parameter is a fragment within a path segment + const enclosureSize = 2 + if xpos := strings.Index(entry.PathPattern, fmt.Sprintf("{%s}", p.Name)) + len(p.Name) + enclosureSize; xpos < len(entry.PathPattern) && entry.PathPattern[xpos] != '/' { + // extract fragment parameters + ep := strings.Split(entry.PathPattern[xpos:], "/")[0] + pnames, pvalues := decodeCompositParams(p.Name, v, ep, nil, nil) + for i, pname := range pnames { + params = append(params, RouteParam{Name: pname, Value: pvalues[i]}) } } else { - d.debugLogf("couldn't find a route by path for %s %s", method, path) + // use the parameter directly + params = append(params, RouteParam{Name: p.Name, Value: v}) } - } else { - d.debugLogf("couldn't find a route by method for %s %s", method, path) } - return nil, false + + return &MatchedRoute{routeEntry: *entry, Params: params}, true } func (d *defaultRouter) OtherMethods(method, path string) []string { @@ -398,7 +412,7 @@ func (d *defaultRouter) OtherMethods(method, path string) []string { var methods []string for k, v := range d.routers { if k != mn { - if _, _, ok := v.Lookup(fpath.Clean(path)); ok { + if _, _, ok := v.Lookup(fpath.Clean(escapeLiteralColons(path))); ok { methods = append(methods, k) continue } @@ -414,28 +428,39 @@ func (d *defaultRouter) SetLogger(lg logger.Logger) { // convert swagger parameters per path segment into a denco parameter as multiple parameters per segment are not supported in denco. var pathConverter = regexp.MustCompile(`{(.+?)}([^/]*)`) +// escapeLiteralColons replaces literal ':' characters with their URL-encoded +// equivalent "%3A". This prevents the denco router from misinterpreting ':' +// in URL path segments as parameter delimiters. The ':' character is valid in +// URL paths per RFC 3986 section 3.3. +func escapeLiteralColons(path string) string { + return strings.ReplaceAll(path, ":", "%3A") +} + func decodeCompositParams(name string, value string, pattern string, names []string, values []string) ([]string, []string) { pleft := strings.Index(pattern, "{") names = append(names, name) + if pleft < 0 { if strings.HasSuffix(value, pattern) { values = append(values, value[:len(value)-len(pattern)]) } else { values = append(values, "") } + + return names, values + } + + toskip := pattern[:pleft] + pright := strings.Index(pattern, "}") + vright := strings.Index(value, toskip) + if vright >= 0 { + values = append(values, value[:vright]) } else { - toskip := pattern[:pleft] - pright := strings.Index(pattern, "}") - vright := strings.Index(value, toskip) - if vright >= 0 { - values = append(values, value[:vright]) - } else { - values = append(values, "") - value = "" - } - return decodeCompositParams(pattern[pleft+1:pright], value[vright+len(toskip):], pattern[pright+1:], names, values) + values = append(values, "") + value = "" } - return names, values + + return decodeCompositParams(pattern[pleft+1:pright], value[vright+len(toskip):], pattern[pright+1:], names, values) } func (d *defaultRouteBuilder) AddRoute(method, path string, operation *spec.Operation) { @@ -463,7 +488,7 @@ func (d *defaultRouteBuilder) AddRoute(method, path string, operation *spec.Oper requestBinder := NewUntypedRequestBinder(parameters, d.spec.Spec(), d.api.Formats()) requestBinder.setDebugLogf(d.debugLogf) - record := denco.NewRecord(pathConverter.ReplaceAllString(path, ":$1"), &routeEntry{ + record := denco.NewRecord(pathConverter.ReplaceAllString(escapeLiteralColons(path), ":$1"), &routeEntry{ BasePath: bp, PathPattern: path, Operation: operation, diff --git a/vendor/github.com/go-openapi/runtime/middleware/seam.go b/vendor/github.com/go-openapi/runtime/middleware/seam.go new file mode 100644 index 0000000000..b234395f19 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/middleware/seam.go @@ -0,0 +1,482 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package middleware + +import ( + "net/http" + "path" + "strings" + + "github.com/go-openapi/runtime/server-middleware/docui" + "github.com/go-openapi/runtime/server-middleware/negotiate" +) + +/////////////////////////////////////////////////////////: +// Seam to the negotiate options introduced in v0.29.5 +/////////////////////////////////////////////////////////: + +// NegotiateOption configures [NegotiateContentType] behaviour. +// +// Deprecated: moved to the [negotiate] package. Use [negotiate.Option] instead. +type NegotiateOption = negotiate.Option + +// NegotiateContentType returns the best offered content type for the +// request's Accept header. +// +// Deprecated: moved to the [negotiate] package. Use [negotiate.ContentType] instead. +func NegotiateContentType(r *http.Request, offers []string, defaultOffer string, opts ...NegotiateOption) string { + return negotiate.ContentType(r, offers, defaultOffer, opts...) +} + +// NegotiateContentEncoding returns the best offered content encoding for +// the request's Accept-Encoding header. +// +// Deprecated: moved to the [negotiate] package. Use [negotiate.ContentEncoding] instead. +func NegotiateContentEncoding(r *http.Request, offers []string) string { + return negotiate.ContentEncoding(r, offers) +} + +// WithIgnoreParameters returns a [NegotiateOption] that strips MIME-type +// parameters from both Accept entries and offers before matching, +// restoring the pre-v0.30 behaviour. +// +// Deprecated: moved to the [negotiate] package. Use [negotiate.WithIgnoreParameters] instead. +func WithIgnoreParameters(ignore bool) NegotiateOption { + return negotiate.WithIgnoreParameters(ignore) +} + +/////////////////////////////////////////////////////////: +// Seam to the UI options +/////////////////////////////////////////////////////////: + +// RapiDoc creates a [http.Handler] to serve a documentation site for a swagger spec. +// +// This allows for altering the spec before starting the [http] listener. +// +// Deprecated: moved to the [docui] package. Use [docui.RapiDoc] instead. +func RapiDoc(opts RapiDocOpts, next http.Handler) http.Handler { + return docui.RapiDoc(next, opts.toFuncOptions()...) +} + +// Redoc creates a [http.Handler] to serve a documentation site for a swagger spec. +// +// This allows for altering the spec before starting the [http] listener. +// +// Deprecated: moved to the [docui] package. Use [docui.Redoc] instead. +func Redoc(opts RedocOpts, next http.Handler) http.Handler { + return docui.Redoc(next, opts.toFuncOptions()...) +} + +// SwaggerUI creates a [http.Handler] to serve a documentation site for a swagger spec. +// +// This allows for altering the spec before starting the [http] listener. +// +// Deprecated: moved to the [docui] package. Use [docui.SwaggerUI] instead. +func SwaggerUI(opts SwaggerUIOpts, next http.Handler) http.Handler { + return docui.SwaggerUI(next, opts.toFuncOptions()...) +} + +// SwaggerUIOAuth2Callback creates a middleware that serves the OAuth2 callback page used by Swagger UI. +// +// Deprecated: moved to the [docui] package. Use [docui.SwaggerUIOAuth2Callback] instead. +func SwaggerUIOAuth2Callback(opts SwaggerUIOpts, next http.Handler) http.Handler { + return docui.SwaggerUIOAuth2Callback(next, opts.toFuncOptions()...) +} + +/////////////////////////////////////////////////////////: +// Seam to the spec middleware options +/////////////////////////////////////////////////////////: + +// SpecOption can be applied to the [Spec] serving [middleware]. +// +// Deprecated: moved to the [docui] package. Use [docui.SpecOption] instead. +type SpecOption func(*specOptions) + +type specOptions struct { + BasePath string + Path string + Document string +} + +func (o specOptions) fullPath() string { + return path.Join(o.BasePath, o.Path, o.Document) +} + +func specOptionsWithDefaults(basePath string, opts []SpecOption) specOptions { + o := specOptions{ + BasePath: "/", + Path: "", + Document: "swagger.json", + } + + for _, apply := range opts { + apply(&o) + } + if basePath != "" { + o.BasePath = basePath + } + + return o +} + +// Spec creates a [middleware] to serve a swagger spec as a JSON document. +// +// This allows for altering the spec before starting the [http] listener. +// +// The basePath argument indicates the path of the spec document (defaults to "/"). +// Additional [SpecOption] can be used to change the name of the document (defaults to "swagger.json"). +// +// Deprecated: moved to the [docui] package as [docui.ServeSpec]. +func Spec(basePath string, spec []byte, next http.Handler, opts ...SpecOption) http.Handler { + o := specOptionsWithDefaults(basePath, opts) + + return docui.ServeSpec(spec, next, docui.WithSpecPath(o.fullPath())) + +} + +// WithSpecPath sets the path to be joined to the base path of the +// spec-serving middleware (see [docui.ServeSpec]). +// +// This is empty by default. +func WithSpecPath(pth string) SpecOption { + return func(o *specOptions) { + o.Path = pth + } +} + +// WithSpecDocument sets the name of the JSON document served as a spec. +// +// By default, this is "swagger.json". +func WithSpecDocument(doc string) SpecOption { + return func(o *specOptions) { + if doc == "" { + return + } + + o.Document = doc + } +} + +// UIOptions defines common options for UI serving middlewares. +// +// Deprecated: use instead the function options provided by [docui]. +type UIOptions struct { + // BasePath for the UI, defaults to: / + BasePath string + + // Path combines with BasePath to construct the path to the UI, defaults to: "docs". + Path string + + // SpecURL is the URL of the spec document. + // + // Defaults to: /swagger.json + SpecURL string + + // Title for the documentation site, default to: API documentation + Title string + + // Template specifies a custom template to serve the UI + Template string +} + +// toFuncOptions bridges the deprecated options struct with the newer function options in [docui]. +func (o UIOptions) toFuncOptions() []docui.Option { + const structMembers = 5 + opts := make([]docui.Option, 0, structMembers) + + if o.BasePath != "" { + opts = append(opts, docui.WithUIBasePath(o.BasePath)) + } + + if o.Path != "" { + opts = append(opts, docui.WithUIPath(o.Path)) + } + + if o.SpecURL != "" { + opts = append(opts, docui.WithSpecURL(o.SpecURL)) + } + + if o.Title != "" { + opts = append(opts, docui.WithUITitle(o.Title)) + } + + if o.Template != "" { + opts = append(opts, docui.WithUITemplate(o.Template)) + } + + return opts +} + +// RapiDocOpts configures the [RapiDoc] middlewares. +// +// Deprecated: use instead the function options provided by [docui]. +type RapiDocOpts struct { + // BasePath for the UI, defaults to: / + BasePath string + + // Path combines with BasePath to construct the path to the UI, defaults to: "docs". + Path string + + // SpecURL is the URL of the spec document. + // + // Defaults to: /swagger.json + SpecURL string + + // Title for the documentation site, default to: API documentation + Title string + + // Template specifies a custom template to serve the UI + Template string + + // RapiDocURL points to the js asset that generates the rapidoc site. + // + // Defaults to https://unpkg.com/rapidoc/dist/rapidoc-min.js + RapiDocURL string +} + +func (o RapiDocOpts) toFuncOptions() []docui.Option { + const structMembers = 6 + opts := make([]docui.Option, 0, structMembers) + + if o.BasePath != "" { + opts = append(opts, docui.WithUIBasePath(o.BasePath)) + } + + if o.Path != "" { + opts = append(opts, docui.WithUIPath(o.Path)) + } + + if o.SpecURL != "" { + opts = append(opts, docui.WithSpecURL(o.SpecURL)) + } + + if o.Title != "" { + opts = append(opts, docui.WithUITitle(o.Title)) + } + + if o.Template != "" { + opts = append(opts, docui.WithUITemplate(o.Template)) + } + + if o.RapiDocURL != "" { + opts = append(opts, docui.WithUIAssetsURL(o.RapiDocURL)) + } + + return opts +} + +// RedocOpts configures the [Redoc] middlewares. +// +// Deprecated: use instead the function options provided by [docui]. +type RedocOpts struct { + // BasePath for the UI, defaults to: / + BasePath string + + // Path combines with BasePath to construct the path to the UI, defaults to: "docs". + Path string + + // SpecURL is the URL of the spec document. + // + // Defaults to: /swagger.json + SpecURL string + + // Title for the documentation site, default to: API documentation + Title string + + // Template specifies a custom template to serve the UI + Template string + + // RedocURL points to the js that generates the redoc site. + // + // Defaults to: https://cdn.jsdelivr.net/npm/redoc/bundles/redoc.standalone.js + RedocURL string +} + +func (o RedocOpts) toFuncOptions() []docui.Option { + const structMembers = 6 + opts := make([]docui.Option, 0, structMembers) + + if o.BasePath != "" { + opts = append(opts, docui.WithUIBasePath(o.BasePath)) + } + + if o.Path != "" { + opts = append(opts, docui.WithUIPath(o.Path)) + } + + if o.SpecURL != "" { + opts = append(opts, docui.WithSpecURL(o.SpecURL)) + } + + if o.Title != "" { + opts = append(opts, docui.WithUITitle(o.Title)) + } + + if o.Template != "" { + opts = append(opts, docui.WithUITemplate(o.Template)) + } + + if o.RedocURL != "" { + opts = append(opts, docui.WithUIAssetsURL(o.RedocURL)) + } + + return opts +} + +// SwaggerUIOpts configures the [SwaggerUI] [middleware]. +// +// Deprecated: use instead the function options provided by [docui]. +type SwaggerUIOpts struct { + // BasePath for the API, defaults to: / + BasePath string + + // Path combines with BasePath to construct the path to the UI, defaults to: "docs". + Path string + + // SpecURL is the URL of the spec document. + // + // Defaults to: /swagger.json + SpecURL string + + // Title for the documentation site, default to: API documentation + Title string + + // Template specifies a custom template to serve the UI + Template string + + // OAuthCallbackURL the url called after OAuth2 login + // + // NOTE: in the new [docui.SwaggerUIOptions] type, this field is named `OAuth2CallbackURL`, + // which is more appropriate. + OAuthCallbackURL string + + // The three components needed to embed swagger-ui + + // SwaggerURL points to the js that generates the SwaggerUI site. + // + // Defaults to: https://unpkg.com/swagger-ui-dist/swagger-ui-bundle.js + SwaggerURL string + + SwaggerPresetURL string + SwaggerStylesURL string + + Favicon32 string + Favicon16 string +} + +func (o SwaggerUIOpts) toFuncOptions() []docui.Option { + const structMembers = 6 + opts := make([]docui.Option, 0, structMembers) + + if o.BasePath != "" { + opts = append(opts, docui.WithUIBasePath(o.BasePath)) + } + + if o.Path != "" { + opts = append(opts, docui.WithUIPath(o.Path)) + } + + if o.SpecURL != "" { + opts = append(opts, docui.WithSpecURL(o.SpecURL)) + } + + if o.Title != "" { + opts = append(opts, docui.WithUITitle(o.Title)) + } + + if o.Template != "" { + opts = append(opts, docui.WithUITemplate(o.Template)) + } + + if o.SwaggerURL != "" { + opts = append(opts, docui.WithUIAssetsURL(o.SwaggerURL)) + } + + var empty SwaggerUIOpts + if o != empty { + swaggeruiOpts := docui.SwaggerUIOptions{ + OAuth2CallbackURL: o.OAuthCallbackURL, + SwaggerPresetURL: o.SwaggerPresetURL, + SwaggerStylesURL: o.SwaggerStylesURL, + Favicon32: o.Favicon32, + Favicon16: o.Favicon16, + } + opts = append(opts, docui.WithSwaggerUIOptions(swaggeruiOpts)) + } + + return opts +} + +// UIOption can be applied to UI serving [middleware] to alter the default +// behavior. +// +// Deprecated: use instead the function options provided by [docui]. +type UIOption func(*UIOptions) + +// uiOptionsWithDefaults applies the given options on top of an empty +// [UIOptions]. Per-flavor handlers ([SwaggerUI], [Redoc], [RapiDoc]) +// fill in the remaining defaults via [UIOptions.EnsureDefaults] when +// the option struct is used. +func uiOptionsWithDefaults(opts []UIOption) UIOptions { + var o UIOptions + for _, apply := range opts { + apply(&o) + } + + return o +} + +// WithUIBasePath sets the base path from where to serve the UI assets. +// +// Deprecated: use instead the function options provided by [docui]. +func WithUIBasePath(base string) UIOption { + return func(o *UIOptions) { + if !strings.HasPrefix(base, "/") { + base = "/" + base + } + o.BasePath = base + } +} + +// WithUIPath sets the path from where to serve the UI assets (i.e. /{basepath}/{path}. +// +// Deprecated: use instead the function options provided by [docui]. +func WithUIPath(pth string) UIOption { + return func(o *UIOptions) { + o.Path = pth + } +} + +// WithUISpecURL sets the path from where to serve swagger spec document. +// +// This may be specified as a full URL or a path. +// +// By default, this is "/swagger.json". +// +// Deprecated: use instead the function options provided by [docui]. +func WithUISpecURL(specURL string) UIOption { + return func(o *UIOptions) { + o.SpecURL = specURL + } +} + +// WithUITitle sets the title of the UI. +// +// Deprecated: use instead the function options provided by [docui]. +func WithUITitle(title string) UIOption { + return func(o *UIOptions) { + o.Title = title + } +} + +// WithTemplate allows to set a custom template for the UI. +// +// UI [middleware] will panic if the template does not parse or execute properly. +// +// Deprecated: use instead the function options provided by [docui]. +func WithTemplate(tpl string) UIOption { + return func(o *UIOptions) { + o.Template = tpl + } +} diff --git a/vendor/github.com/go-openapi/runtime/middleware/spec.go b/vendor/github.com/go-openapi/runtime/middleware/spec.go deleted file mode 100644 index 0a64a9572b..0000000000 --- a/vendor/github.com/go-openapi/runtime/middleware/spec.go +++ /dev/null @@ -1,91 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers -// SPDX-License-Identifier: Apache-2.0 - -package middleware - -import ( - "net/http" - "path" -) - -const ( - contentTypeHeader = "Content-Type" - applicationJSON = "application/json" -) - -// SpecOption can be applied to the Spec serving [middleware]. -type SpecOption func(*specOptions) - -var defaultSpecOptions = specOptions{ - Path: "", - Document: "swagger.json", -} - -type specOptions struct { - Path string - Document string -} - -func specOptionsWithDefaults(opts []SpecOption) specOptions { - o := defaultSpecOptions - for _, apply := range opts { - apply(&o) - } - - return o -} - -// Spec creates a [middleware] to serve a swagger spec as a JSON document. -// -// This allows for altering the spec before starting the [http] listener. -// -// The basePath argument indicates the path of the spec document (defaults to "/"). -// Additional [SpecOption] can be used to change the name of the document (defaults to "swagger.json"). -func Spec(basePath string, b []byte, next http.Handler, opts ...SpecOption) http.Handler { - if basePath == "" { - basePath = "/" - } - o := specOptionsWithDefaults(opts) - pth := path.Join(basePath, o.Path, o.Document) - - return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { - if path.Clean(r.URL.Path) == pth { - rw.Header().Set(contentTypeHeader, applicationJSON) - rw.WriteHeader(http.StatusOK) - _, _ = rw.Write(b) - - return - } - - if next != nil { - next.ServeHTTP(rw, r) - - return - } - - rw.Header().Set(contentTypeHeader, applicationJSON) - rw.WriteHeader(http.StatusNotFound) - }) -} - -// WithSpecPath sets the path to be joined to the base path of the Spec [middleware]. -// -// This is empty by default. -func WithSpecPath(pth string) SpecOption { - return func(o *specOptions) { - o.Path = pth - } -} - -// WithSpecDocument sets the name of the JSON document served as a spec. -// -// By default, this is "swagger.json". -func WithSpecDocument(doc string) SpecOption { - return func(o *specOptions) { - if doc == "" { - return - } - - o.Document = doc - } -} diff --git a/vendor/github.com/go-openapi/runtime/middleware/swaggerui.go b/vendor/github.com/go-openapi/runtime/middleware/swaggerui.go deleted file mode 100644 index 14ed37ced6..0000000000 --- a/vendor/github.com/go-openapi/runtime/middleware/swaggerui.go +++ /dev/null @@ -1,178 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers -// SPDX-License-Identifier: Apache-2.0 - -package middleware - -import ( - "bytes" - "fmt" - "html/template" - "net/http" - "path" -) - -// SwaggerUIOpts configures the [SwaggerUI] [middleware]. -type SwaggerUIOpts struct { - // BasePath for the API, defaults to: / - BasePath string - - // Path combines with BasePath to construct the path to the UI, defaults to: "docs". - Path string - - // SpecURL is the URL of the spec document. - // - // Defaults to: /swagger.json - SpecURL string - - // Title for the documentation site, default to: API documentation - Title string - - // Template specifies a custom template to serve the UI - Template string - - // OAuthCallbackURL the url called after OAuth2 login - OAuthCallbackURL string - - // The three components needed to embed swagger-ui - - // SwaggerURL points to the js that generates the SwaggerUI site. - // - // Defaults to: https://unpkg.com/swagger-ui-dist/swagger-ui-bundle.js - SwaggerURL string - - SwaggerPresetURL string - SwaggerStylesURL string - - Favicon32 string - Favicon16 string -} - -// EnsureDefaults in case some options are missing. -func (r *SwaggerUIOpts) EnsureDefaults() { - r.ensureDefaults() - - if r.Template == "" { - r.Template = swaggeruiTemplate - } -} - -func (r *SwaggerUIOpts) EnsureDefaultsOauth2() { - r.ensureDefaults() - - if r.Template == "" { - r.Template = swaggerOAuthTemplate - } -} - -func (r *SwaggerUIOpts) ensureDefaults() { - common := toCommonUIOptions(r) - common.EnsureDefaults() - fromCommonToAnyOptions(common, r) - - // swaggerui-specifics - if r.OAuthCallbackURL == "" { - r.OAuthCallbackURL = path.Join(r.BasePath, r.Path, "oauth2-callback") - } - if r.SwaggerURL == "" { - r.SwaggerURL = swaggerLatest - } - if r.SwaggerPresetURL == "" { - r.SwaggerPresetURL = swaggerPresetLatest - } - if r.SwaggerStylesURL == "" { - r.SwaggerStylesURL = swaggerStylesLatest - } - if r.Favicon16 == "" { - r.Favicon16 = swaggerFavicon16Latest - } - if r.Favicon32 == "" { - r.Favicon32 = swaggerFavicon32Latest - } -} - -// SwaggerUI creates a [middleware] to serve a documentation site for a swagger spec. -// -// This allows for altering the spec before starting the [http] listener. -func SwaggerUI(opts SwaggerUIOpts, next http.Handler) http.Handler { - opts.EnsureDefaults() - - pth := path.Join(opts.BasePath, opts.Path) - tmpl := template.Must(template.New("swaggerui").Parse(opts.Template)) - assets := bytes.NewBuffer(nil) - if err := tmpl.Execute(assets, opts); err != nil { - panic(fmt.Errorf("cannot execute template: %w", err)) - } - - return serveUI(pth, assets.Bytes(), next) -} - -const ( - swaggerLatest = "https://unpkg.com/swagger-ui-dist/swagger-ui-bundle.js" - swaggerPresetLatest = "https://unpkg.com/swagger-ui-dist/swagger-ui-standalone-preset.js" - swaggerStylesLatest = "https://unpkg.com/swagger-ui-dist/swagger-ui.css" - swaggerFavicon32Latest = "https://unpkg.com/swagger-ui-dist/favicon-32x32.png" - swaggerFavicon16Latest = "https://unpkg.com/swagger-ui-dist/favicon-16x16.png" - swaggeruiTemplate = ` - - - - - {{ .Title }} - - - - - - - - -
- - - - - - -` -) diff --git a/vendor/github.com/go-openapi/runtime/middleware/typeutils.go b/vendor/github.com/go-openapi/runtime/middleware/typeutils.go new file mode 100644 index 0000000000..3f7d7976a1 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/middleware/typeutils.go @@ -0,0 +1,30 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package middleware + +import "strings" + +// normalizeOffer strips the parameter section (";...") from a media-type +// string. +func normalizeOffer(orig string) string { + // NOTE(maintainers): Despite its name (kept for historical reasons), this helper is + // not about Accept negotiation — it is used to derive the bare type that + // keys the producer/consumer maps registered on a [RoutableAPI]. + // Those maps are looked up by the bare media type, so an entry registered as + // "application/json" satisfies a route that declares "application/json; + // charset=utf-8" and vice-versa. + const maxParts = 2 + + return strings.SplitN(orig, ";", maxParts)[0] +} + +// normalizeOffers is the slice form of [normalizeOffer]. +func normalizeOffers(orig []string) []string { + norm := make([]string, 0, len(orig)) + for _, o := range orig { + norm = append(norm, normalizeOffer(o)) + } + + return norm +} diff --git a/vendor/github.com/go-openapi/runtime/middleware/ui_options.go b/vendor/github.com/go-openapi/runtime/middleware/ui_options.go deleted file mode 100644 index ed255426ad..0000000000 --- a/vendor/github.com/go-openapi/runtime/middleware/ui_options.go +++ /dev/null @@ -1,176 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers -// SPDX-License-Identifier: Apache-2.0 - -package middleware - -import ( - "bytes" - "encoding/gob" - "fmt" - "net/http" - "path" - "strings" -) - -const ( - // constants that are common to all UI-serving middlewares. - defaultDocsPath = "docs" - defaultDocsURL = "/swagger.json" - defaultDocsTitle = "API Documentation" -) - -// uiOptions defines common options for UI serving middlewares. -type uiOptions struct { - // BasePath for the UI, defaults to: / - BasePath string - - // Path combines with BasePath to construct the path to the UI, defaults to: "docs". - Path string - - // SpecURL is the URL of the spec document. - // - // Defaults to: /swagger.json - SpecURL string - - // Title for the documentation site, default to: API documentation - Title string - - // Template specifies a custom template to serve the UI - Template string -} - -// toCommonUIOptions converts any UI option type to retain the common options. -// -// This uses gob encoding/decoding to convert common fields from one struct to another. -func toCommonUIOptions(opts any) uiOptions { - var buf bytes.Buffer - enc := gob.NewEncoder(&buf) - dec := gob.NewDecoder(&buf) - var o uiOptions - err := enc.Encode(opts) - if err != nil { - panic(err) - } - - err = dec.Decode(&o) - if err != nil { - panic(err) - } - - return o -} - -func fromCommonToAnyOptions[T any](source uiOptions, target *T) { - var buf bytes.Buffer - enc := gob.NewEncoder(&buf) - dec := gob.NewDecoder(&buf) - err := enc.Encode(source) - if err != nil { - panic(err) - } - - err = dec.Decode(target) - if err != nil { - panic(err) - } -} - -// UIOption can be applied to UI serving [middleware], such as Context.[APIHandler] or -// Context.[APIHandlerSwaggerUI] to alter the default behavior. -type UIOption func(*uiOptions) - -func uiOptionsWithDefaults(opts []UIOption) uiOptions { - var o uiOptions - for _, apply := range opts { - apply(&o) - } - - return o -} - -// WithUIBasePath sets the base path from where to serve the UI assets. -// -// By default, Context [middleware] sets this value to the API base path. -func WithUIBasePath(base string) UIOption { - return func(o *uiOptions) { - if !strings.HasPrefix(base, "/") { - base = "/" + base - } - o.BasePath = base - } -} - -// WithUIPath sets the path from where to serve the UI assets (i.e. /{basepath}/{path}. -func WithUIPath(pth string) UIOption { - return func(o *uiOptions) { - o.Path = pth - } -} - -// WithUISpecURL sets the path from where to serve swagger spec document. -// -// This may be specified as a full URL or a path. -// -// By default, this is "/swagger.json". -func WithUISpecURL(specURL string) UIOption { - return func(o *uiOptions) { - o.SpecURL = specURL - } -} - -// WithUITitle sets the title of the UI. -// -// By default, Context [middleware] sets this value to the title found in the API spec. -func WithUITitle(title string) UIOption { - return func(o *uiOptions) { - o.Title = title - } -} - -// WithTemplate allows to set a custom template for the UI. -// -// UI [middleware] will panic if the template does not parse or execute properly. -func WithTemplate(tpl string) UIOption { - return func(o *uiOptions) { - o.Template = tpl - } -} - -// EnsureDefaults in case some options are missing. -func (r *uiOptions) EnsureDefaults() { - if r.BasePath == "" { - r.BasePath = "/" - } - if r.Path == "" { - r.Path = defaultDocsPath - } - if r.SpecURL == "" { - r.SpecURL = defaultDocsURL - } - if r.Title == "" { - r.Title = defaultDocsTitle - } -} - -// serveUI creates a middleware that serves a templated asset as text/html. -func serveUI(pth string, assets []byte, next http.Handler) http.Handler { - return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { - if path.Clean(r.URL.Path) == pth { - rw.Header().Set(contentTypeHeader, "text/html; charset=utf-8") - rw.WriteHeader(http.StatusOK) - _, _ = rw.Write(assets) - - return - } - - if next != nil { - next.ServeHTTP(rw, r) - - return - } - - rw.Header().Set(contentTypeHeader, "text/plain") - rw.WriteHeader(http.StatusNotFound) - _, _ = fmt.Fprintf(rw, "%q not found", pth) - }) -} diff --git a/vendor/github.com/go-openapi/runtime/middleware/validation.go b/vendor/github.com/go-openapi/runtime/middleware/validation.go index 8a56490639..63a78d482a 100644 --- a/vendor/github.com/go-openapi/runtime/middleware/validation.go +++ b/vendor/github.com/go-openapi/runtime/middleware/validation.go @@ -4,13 +4,14 @@ package middleware import ( - "mime" + stderrors "errors" "net/http" "strings" "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" - "github.com/go-openapi/swag/stringutils" + "github.com/go-openapi/runtime/server-middleware/mediatype" ) type validation struct { @@ -21,24 +22,28 @@ type validation struct { bound map[string]any } -// ContentType validates the content type of a request. -func validateContentType(allowed []string, actual string) error { +// validateContentType maps [mediatype.MatchFirst] to the runtime's +// validation errors: +// +// - actual fails to parse → HTTP 400 ([errors.NewParseError]). +// - actual is well-formed but +// no allowed entry accepts it → HTTP 415 ([errors.InvalidContentType]). +// +// In the standard runtime flow, malformed Content-Type headers are +// already caught upstream by [runtime.ContentType] (which itself returns +// a 400 [errors.ParseError]). This function therefore only sees the +// malformed case when invoked directly by callers that have bypassed +// that step. +func validateContentType(allowed []string, actual string, opts ...mediatype.MatchOption) error { if len(allowed) == 0 { return nil } - mt, _, err := mime.ParseMediaType(actual) - if err != nil { - return errors.InvalidContentType(actual, allowed) - } - if stringutils.ContainsStringsCI(allowed, mt) { - return nil - } - if stringutils.ContainsStringsCI(allowed, "*/*") { + _, ok, err := mediatype.MatchFirst(allowed, actual, opts...) + if ok { return nil } - parts := strings.Split(actual, "/") - if len(parts) == 2 && stringutils.ContainsStringsCI(allowed, parts[0]+"/*") { - return nil + if err != nil { + return errors.NewParseError(runtime.HeaderContentType, "header", actual, err) } return errors.InvalidContentType(actual, allowed) } @@ -69,46 +74,53 @@ func (v *validation) debugLogf(format string, args ...any) { func (v *validation) parameters() { v.debugLogf("validating request parameters for %s %s", v.request.Method, v.request.URL.EscapedPath()) - if result := v.route.Binder.Bind(v.request, v.route.Params, v.route.Consumer, v.bound); result != nil { - if result.Error() == "validation failure list" { - for _, e := range result.(*errors.Validation).Value.([]any) { - v.result = append(v.result, e.(error)) - } - return + result := v.route.Binder.bind(v.request, v.route.Params, v.route.Consumer, v.bound) + if result == nil { + return + } + + for _, e := range result.Errors { + var validationErr *errors.Validation + if stderrors.As(e, &validationErr) { + v.result = append(v.result, validationErr) } - v.result = append(v.result, result) } } func (v *validation) contentType() { - if len(v.result) == 0 && runtime.HasBody(v.request) { - v.debugLogf("validating body content type for %s %s", v.request.Method, v.request.URL.EscapedPath()) - ct, _, req, err := v.context.ContentType(v.request) - if err != nil { + if len(v.result) > 0 || !runtime.HasBody(v.request) { + return + } + + v.debugLogf("validating body content type for %s %s", v.request.Method, v.request.URL.EscapedPath()) + ct, _, req, err := v.context.ContentType(v.request) + if err != nil { + v.result = append(v.result, err) + } else { + v.request = req + } + + if len(v.result) == 0 { + v.debugLogf("validating content type for %q against [%s]", ct, strings.Join(v.route.Consumes, ", ")) + if err := validateContentType(v.route.Consumes, ct, v.context.matchOpts()...); err != nil { v.result = append(v.result, err) - } else { - v.request = req } + } - if len(v.result) == 0 { - v.debugLogf("validating content type for %q against [%s]", ct, strings.Join(v.route.Consumes, ", ")) - if err := validateContentType(v.route.Consumes, ct); err != nil { - v.result = append(v.result, err) - } - } - if ct != "" && v.route.Consumer == nil { - cons, ok := v.route.Consumers[ct] - if !ok { - v.result = append(v.result, errors.New(http.StatusInternalServerError, "no consumer registered for %s", ct)) - } else { - v.route.Consumer = cons - } - } + if ct == "" || v.route.Consumer != nil { + return + } + + cons, ok := mediatype.Lookup(v.route.Consumers, ct, v.context.matchOpts()...) + if !ok { + v.result = append(v.result, errors.New(http.StatusInternalServerError, "no consumer registered for %s", ct)) + } else { + v.route.Consumer = cons } } func (v *validation) responseFormat() { - // if the route provides values for Produces and no format could be identify then return an error. + // if the route provides values for Produces and no format could be identified then return an error. // if the route does not specify values for Produces then treat request as valid since the API designer // choose not to specify the format for responses. if str, rCtx := v.context.ResponseFormat(v.request, v.route.Produces); str == "" && len(v.route.Produces) > 0 { diff --git a/vendor/github.com/go-openapi/runtime/multipart_stream.go b/vendor/github.com/go-openapi/runtime/multipart_stream.go new file mode 100644 index 0000000000..76e1bf4607 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/multipart_stream.go @@ -0,0 +1,556 @@ +// SPDX-FileCopyrightText: Copyright 2015-2026 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package runtime + +import ( + "context" + stderrors "errors" + "fmt" + "io" + "mime" + "mime/multipart" + "net/http" + "net/textproto" + "net/url" + + "github.com/go-openapi/errors" +) + +// MultipartFormStreamOption configures [NewMultipartFormStream]. +type MultipartFormStreamOption func(*multipartFormStreamConfig) + +const defaultMultipartFormStreamMaxParts = 1000 + +type multipartFormStreamConfig struct { + multipartFormLimits + + maxParts int +} + +// MultipartFormStreamMaxBody caps the total number of request-body bytes read +// by a [MultipartFormStream]. +// +// A value of 0 applies [DefaultMaxUploadBodySize]. A negative value disables +// the cap when the caller has already limited the request body upstream. +func MultipartFormStreamMaxBody(n int64) MultipartFormStreamOption { + return func(c *multipartFormStreamConfig) { c.maxBody = n } +} + +// MultipartFormStreamMaxFiles rejects a multipart stream after more than n +// file parts have been encountered. A value of 0 means no file-count cap. +func MultipartFormStreamMaxFiles(n int) MultipartFormStreamOption { + return func(c *multipartFormStreamConfig) { c.maxFiles = n } +} + +// MultipartFormStreamMaxParts rejects a multipart stream after more than n +// total parts have been encountered. The default is 1000, matching +// [multipart.Reader.ReadForm]. A value of 0 disables the limit. +func MultipartFormStreamMaxParts(n int) MultipartFormStreamOption { + return func(c *multipartFormStreamConfig) { c.maxParts = n } +} + +// MultipartFormStreamMaxFilenameLen rejects file parts whose filename exceeds +// n bytes. A value of 0 disables the limit. When this option is not supplied, +// [DefaultMaxUploadFilenameLength] is used. +func MultipartFormStreamMaxFilenameLen(n int) MultipartFormStreamOption { + return func(c *multipartFormStreamConfig) { c.maxFilenameLen = n } +} + +// StreamedFile exposes a file part directly from the multipart request body. +// +// Reads block until bytes arrive from the client. StreamedFile is not seekable +// and is not safe for concurrent use. Its form name, filename and MIME headers +// are available before the payload is consumed. The underlying [multipart.Part] +// remains private so callers cannot bypass Close and its error-preserving drain +// semantics; Header exposes the part metadata without exposing that lifecycle. +// +// Closing a StreamedFile drains only the unread remainder of that file part. +// Close may therefore block while the client is still uploading the current +// part. The owning [MultipartFormStream] may then advance to the next part. +type StreamedFile struct { + FieldName string + Filename string + Header textproto.MIMEHeader + part *multipart.Part + closeErr error +} + +// MultipartFileInfo describes a file part discovered by [MultipartFormStream]. +// +// The payload reader is intentionally omitted. File parts remain sequential and +// are consumed through [MultipartFormStream.NextFile]. Header is a snapshot of +// the client-supplied MIME headers and must be treated as untrusted input. +type MultipartFileInfo struct { + FieldName string + Filename string + Header textproto.MIMEHeader +} + +// Read reads file payload bytes directly from the request body. +func (f *StreamedFile) Read(p []byte) (int, error) { + if f == nil || f.part == nil { + return 0, io.ErrClosedPipe + } + + return f.part.Read(p) +} + +// Close discards the unread remainder of this file part. +// +// Close does not close the underlying HTTP request body and does not consume +// subsequent multipart parts. After Close returns successfully, the parent +// MultipartFormStream may advance to the next part. +// +// Any error encountered while discarding the unread payload is returned. +func (f *StreamedFile) Close() error { + if f == nil { + return nil + } + if f.part == nil { + return f.closeErr + } + + part := f.part + f.part = nil + // multipart.Part.Close drains with io.Copy but intentionally discards the + // resulting error, so drain explicitly to preserve error propagation. + _, f.closeErr = io.Copy(io.Discard, part) + + return f.closeErr +} + +// MultipartFormStream reads multipart/form-data sequentially without parsing +// the complete request body before exposing file payloads. +// +// [MultipartFormStream.NextFile] consumes ordinary form fields until it reaches +// the next file part. Fields are appended to request.PostForm and request.Form +// as they are encountered. Consequently, fields after a file become visible +// only after the caller consumes or closes that file and advances the stream. +// +// A stream and its returned files are not safe for concurrent use. There is at +// most one active file part. Calling [MultipartFormStream.NextFile] closes and +// drains an unread active file before advancing, and may therefore block until +// the current part finishes arriving. No background goroutines are started. +// +// The caller owns the stream. Call [MultipartFormStream.Drain] to consume the +// remaining body, collect trailing fields and allow HTTP connection reuse when +// possible. Call [MultipartFormStream.Close] to stop multipart processing +// without explicitly draining the remaining parts. +// +// [MultipartFormStream.Fields] and [MultipartFormStream.Files] expose snapshots +// of the multipart fields and file metadata discovered so far. They never read +// ahead: fields or files after the active file become visible only after the +// stream advances. +type MultipartFormStream struct { + multipartFormStreamConfig + + request *http.Request + reader *multipart.Reader + current *StreamedFile + + fields url.Values + fileInfos []MultipartFileInfo + parts int + closed bool + done bool +} + +// NewMultipartFormStream creates a sequential multipart/form-data stream over +// r.Body. +// +// For POST, PUT and PATCH requests, the constructor accepts only +// multipart/form-data, validates that a non-empty boundary is present, but does +// not consume multipart parts. For other methods, it returns an empty stream +// whose NextFile method reports io.EOF without reading the request body. +// +// The constructor initializes request.Form and request.PostForm in the same way +// as [http.Request.ParseForm], then populates multipart values incrementally as +// [MultipartFormStream.NextFile] advances. +// +// For POST, PUT and PATCH requests, NewMultipartFormStream marks the request +// as handled by MultipartReader. Callers must not subsequently call +// [http.Request.ParseMultipartForm] or [BindForm] for the same request. +// +// File payloads are exposed directly from the request body and are not buffered +// in memory or temporary files. Ordinary form values are read into memory as +// they are encountered. Parts are processed in wire order. +// +// At most one StreamedFile may be active at a time. Calling +// [MultipartFormStream.NextFile] automatically closes and drains an unread +// current file before advancing. No background goroutines are started. +// +// MultipartFormStream is not safe for concurrent use. +// +// File names and MIME headers are supplied by the client and remain untrusted. +func NewMultipartFormStream(r *http.Request, opts ...MultipartFormStreamOption) (*MultipartFormStream, error) { + cfg := multipartFormStreamConfig{ + multipartFormLimits: multipartFormLimits{ + maxFilenameLen: DefaultMaxUploadFilenameLength, + }, + maxParts: defaultMultipartFormStreamMaxParts, + } + for _, opt := range opts { + opt(&cfg) + } + + if r == nil { + return nil, errors.NewParseError("body", "formData", "", stderrors.New("nil request")) + } + + if !supportsMultipartFormStream(r.Method) { + if err := r.ParseForm(); err != nil { + return nil, errors.NewParseError("body", "formData", "", err) + } + + return &MultipartFormStream{ + request: r, + multipartFormStreamConfig: cfg, + fields: make(url.Values), + done: true, + }, nil + } + + if r.Body == nil { + return nil, errors.NewParseError("body", "formData", "", stderrors.New("nil request body")) + } + + contentType := r.Header.Get(HeaderContentType) + mediaType, params, err := mime.ParseMediaType(contentType) + if err != nil { + return nil, errors.NewParseError(HeaderContentType, "header", contentType, err) + } + if mediaType != MultipartFormMime { + return nil, errors.NewParseError(HeaderContentType, "header", mediaType, http.ErrNotMultipart) + } + if params["boundary"] == "" { + return nil, errors.NewParseError(HeaderContentType, "header", contentType, http.ErrMissingBoundary) + } + if err = r.ParseForm(); err != nil { + return nil, errors.NewParseError("body", "formData", "", err) + } + + body := r.Body + if cfg.maxBody >= 0 { + maxBody := cfg.maxBody + if maxBody == 0 { + maxBody = DefaultMaxUploadBodySize + } + body = http.MaxBytesReader(nil, body, maxBody) + } + body = &contextReadCloser{ctx: r.Context(), ReadCloser: body} + r.Body = body + + reader, err := r.MultipartReader() + if err != nil { + return nil, errors.NewParseError("body", "formData", "", err) + } + + return &MultipartFormStream{ + request: r, + reader: reader, + multipartFormStreamConfig: cfg, + fields: make(url.Values), + }, nil +} + +// Fields returns a snapshot of ordinary multipart form fields discovered so far. +// +// URL query values are not included. Repeated multipart fields preserve their +// encounter order. Fields after the active file are not visible until that file +// is consumed or closed and the stream advances. Mutating the returned values +// does not affect the stream or the request. +func (s *MultipartFormStream) Fields() url.Values { + if s == nil { + return nil + } + + return cloneMultipartValues(s.fields) +} + +// Files returns snapshots of file metadata discovered so far in wire order. +// +// The currently active file is included as soon as NextFile returns it. Payload +// readers are not retained in the index. Mutating the returned slice or MIME +// headers does not affect the stream. +func (s *MultipartFormStream) Files() []MultipartFileInfo { + if s == nil { + return nil + } + + files := make([]MultipartFileInfo, len(s.fileInfos)) + for i, file := range s.fileInfos { + files[i] = MultipartFileInfo{ + FieldName: file.FieldName, + Filename: file.Filename, + Header: cloneMultipartMIMEHeader(file.Header), + } + } + + return files +} + +// NextFile advances through the multipart body and returns the next file part. +// Ordinary form fields encountered before that file are added to request.Form +// and request.PostForm. +// +// If the previously returned file is still open, NextFile closes and drains it +// before advancing. This may block while the client is still uploading that +// part. Any drain error is returned and the stream is aborted. +// +// NextFile returns io.EOF when no file parts remain. At that point all trailing +// ordinary fields have been collected. +func (s *MultipartFormStream) NextFile() (*StreamedFile, error) { + if err := s.prepareNextFile(); err != nil { + return nil, err + } + + return s.readNextFile() +} + +// Drain consumes the rest of the multipart body. +// +// Unread file payloads are discarded. Non-file form fields encountered while +// draining are collected in the request form values. +// +// Drain closes the underlying request body after reaching EOF. Subsequent calls +// to NextFile return io.EOF. Drain returns any multipart parsing, payload drain +// or request-body close error. +func (s *MultipartFormStream) Drain() error { + if s == nil || s.closed { + return nil + } + + for { + file, err := s.NextFile() + if stderrors.Is(err, io.EOF) { + return s.Close() + } + if err != nil { + return stderrors.Join(err, s.Close()) + } + if err = file.Close(); err != nil { + return stderrors.Join(err, s.Close()) + } + } +} + +// Close stops multipart processing and closes the underlying HTTP request body +// without explicitly draining the remaining multipart parts. +// +// The concrete request body may perform its own work during Close. In +// particular, a net/http server request body may discard a limited amount of +// unread data to allow connection reuse, so Close is not guaranteed to return +// immediately. Call Drain when trailing form fields must be collected. +func (s *MultipartFormStream) Close() error { + if s == nil || s.closed { + return nil + } + + s.closed = true + if s.current != nil { + s.current.part = nil + s.current = nil + } + + if s.request == nil || s.request.Body == nil { + return nil + } + + return s.request.Body.Close() +} + +func supportsMultipartFormStream(method string) bool { + switch method { + case http.MethodPost, http.MethodPut, http.MethodPatch: + return true + default: + return false + } +} + +func (s *MultipartFormStream) abort(err error) error { + return stderrors.Join(err, s.Close()) +} + +func (s *MultipartFormStream) closeCurrent() error { + if s.current == nil { + return nil + } + + err := s.current.Close() + s.current = nil + + return err +} + +func (s *MultipartFormStream) bindValue(part *multipart.Part, name string) error { + value, err := io.ReadAll(part) + if err != nil { + return err + } + + s.fields.Add(name, string(value)) + s.request.PostForm.Add(name, string(value)) + // Match net/http.ParseMultipartForm: query values already present in Form + // keep precedence over multipart body values, which are appended. + s.request.Form.Add(name, string(value)) + + return nil +} + +func discardPart(part *multipart.Part) error { + _, err := io.Copy(io.Discard, part) + + return err +} + +func cloneMultipartValues(values url.Values) url.Values { + cloned := make(url.Values, len(values)) + for name, entries := range values { + cloned[name] = append([]string(nil), entries...) + } + + return cloned +} + +func cloneMultipartMIMEHeader(header textproto.MIMEHeader) textproto.MIMEHeader { + cloned := make(textproto.MIMEHeader, len(header)) + for name, entries := range header { + cloned[name] = append([]string(nil), entries...) + } + + return cloned +} + +type contextReadCloser struct { + io.ReadCloser + + ctx context.Context //nolint:containedctx // Read has no context parameter, so the wrapper must retain it +} + +func (r *contextReadCloser) Read(p []byte) (int, error) { + if err := r.ctx.Err(); err != nil { + return 0, err + } + + return r.ReadCloser.Read(p) +} + +func (s *MultipartFormStream) prepareNextFile() error { + if s == nil { + return io.ErrClosedPipe + } + if s.done { + return io.EOF + } + if s.closed { + return io.ErrClosedPipe + } + if err := s.closeCurrent(); err != nil { + return s.abort(err) + } + + return nil +} + +func (s *MultipartFormStream) readNextFile() (*StreamedFile, error) { + for { + part, err := s.reader.NextPart() + if err != nil { + return nil, s.handleNextPartError(err) + } + + s.parts++ + if s.maxParts > 0 && s.parts > s.maxParts { + err := errors.NewParseError( + "body", + "formData", + "", + fmt.Errorf( + "multipart form contains %d parts, exceeds limit %d", + s.parts, + s.maxParts, + ), + ) + + return nil, s.abort(err) + } + + fieldName := part.FormName() + if fieldName == "" { + if err := discardPart(part); err != nil { + return nil, s.abort(err) + } + + continue + } + + filename := part.FileName() + if filename == "" { + if err := s.bindValue(part, fieldName); err != nil { + return nil, s.abort(err) + } + + continue + } + + return s.openFile(part, fieldName, filename) + } +} + +func (s *MultipartFormStream) handleNextPartError(err error) error { + if stderrors.Is(err, io.EOF) { + s.done = true + + return io.EOF + } + + return s.abort(err) +} + +func (s *MultipartFormStream) openFile( + part *multipart.Part, + fieldName string, + filename string, +) (*StreamedFile, error) { + fileCount := len(s.fileInfos) + 1 + if s.maxFiles > 0 && fileCount > s.maxFiles { + err := errors.NewParseError( + "body", + "formData", + "", + fmt.Errorf( + "multipart form contains %d file parts, exceeds limit %d", + fileCount, + s.maxFiles, + ), + ) + + return nil, s.abort(err) + } + + if err := ValidateFilenameLength( + fieldName, + "formData", + filename, + s.maxFilenameLen, + ); err != nil { + return nil, s.abort(err) + } + + file := &StreamedFile{ + FieldName: fieldName, + Filename: filename, + Header: part.Header, + part: part, + } + s.fileInfos = append(s.fileInfos, MultipartFileInfo{ + FieldName: fieldName, + Filename: filename, + Header: cloneMultipartMIMEHeader(part.Header), + }) + s.current = file + + return file, nil +} diff --git a/vendor/github.com/go-openapi/runtime/security/authenticator.go b/vendor/github.com/go-openapi/runtime/security/authenticator.go index 4c09101826..e521d95ef1 100644 --- a/vendor/github.com/go-openapi/runtime/security/authenticator.go +++ b/vendor/github.com/go-openapi/runtime/security/authenticator.go @@ -19,8 +19,8 @@ const ( accessTokenParam = "access_token" ) -// HttpAuthenticator is a function that authenticates a HTTP request. -func HttpAuthenticator(handler func(*http.Request) (bool, any, error)) runtime.Authenticator { //nolint:revive +// HTTPAuthenticator is a function that authenticates a HTTP request. +func HTTPAuthenticator(handler func(*http.Request) (bool, any, error)) runtime.Authenticator { return runtime.AuthenticatorFunc(func(params any) (bool, any, error) { if request, ok := params.(*http.Request); ok { return handler(request) @@ -32,7 +32,14 @@ func HttpAuthenticator(handler func(*http.Request) (bool, any, error)) runtime.A }) } -// ScopedAuthenticator is a function that authenticates a HTTP request against a list of valid scopes. +// HttpAuthenticator aliases [HTTPAuthenticator] for backward-compatibility. +// +// Deprecated: use [HTTPAuthenticator] instead. +func HttpAuthenticator(handler func(*http.Request) (bool, any, error)) runtime.Authenticator { //nolint:revive + return HTTPAuthenticator(handler) +} + +// ScopedAuthenticator is a function that authenticates an [http.Request] against a list of valid scopes. func ScopedAuthenticator(handler func(*ScopedAuthRequest) (bool, any, error)) runtime.Authenticator { return runtime.AuthenticatorFunc(func(params any) (bool, any, error) { if request, ok := params.(*ScopedAuthRequest); ok { @@ -42,22 +49,42 @@ func ScopedAuthenticator(handler func(*ScopedAuthRequest) (bool, any, error)) ru }) } -// UserPassAuthentication authentication function. +// UserPassAuthentication validates a basic-auth credential. +// +// Implementations comparing the password (or any derived secret) against a +// known value MUST use [crypto/subtle.ConstantTimeCompare]: the runtime +// extracts the credential from the request and delegates the comparison +// here, and does not enforce a constant-time posture on the caller's behalf. type UserPassAuthentication func(string, string) (any, error) -// UserPassAuthenticationCtx authentication function with [context.Context]. +// UserPassAuthenticationCtx is the [context.Context]-aware variant of +// [UserPassAuthentication]. The same constant-time-comparison guidance +// applies. type UserPassAuthenticationCtx func(context.Context, string, string) (context.Context, any, error) -// TokenAuthentication authentication function. +// TokenAuthentication validates an API-key token. +// +// Implementations comparing the token against a known value MUST use +// [crypto/subtle.ConstantTimeCompare]; the runtime delegates the comparison +// here and does not enforce a constant-time posture on the caller's behalf. type TokenAuthentication func(string) (any, error) -// TokenAuthenticationCtx authentication function with [context.Context]. +// TokenAuthenticationCtx is the [context.Context]-aware variant of +// [TokenAuthentication]. The same constant-time-comparison guidance +// applies. type TokenAuthenticationCtx func(context.Context, string) (context.Context, any, error) -// ScopedTokenAuthentication authentication function. +// ScopedTokenAuthentication validates a bearer/OAuth2 token along with the +// scopes required for the operation. +// +// Implementations comparing the token against a known value MUST use +// [crypto/subtle.ConstantTimeCompare]; the runtime delegates the comparison +// here and does not enforce a constant-time posture on the caller's behalf. type ScopedTokenAuthentication func(string, []string) (any, error) -// ScopedTokenAuthenticationCtx authentication function with [context.Context]. +// ScopedTokenAuthenticationCtx is the [context.Context]-aware variant of +// [ScopedTokenAuthentication]. The same constant-time-comparison guidance +// applies. type ScopedTokenAuthenticationCtx func(context.Context, string, []string) (context.Context, any, error) var DefaultRealmName = "API" @@ -199,7 +226,7 @@ func APIKeyAuthCtx(name, in string, authenticate TokenAuthenticationCtx) runtime }) } -// ScopedAuthRequest contains both a [http] request and the required scopes for a particular operation. +// ScopedAuthRequest contains both the [http.Request] and the required scopes for a particular operation. type ScopedAuthRequest struct { Request *http.Request RequiredScopes []string diff --git a/vendor/github.com/go-openapi/swag/jsonname/LICENSE b/vendor/github.com/go-openapi/runtime/server-middleware/LICENSE similarity index 100% rename from vendor/github.com/go-openapi/swag/jsonname/LICENSE rename to vendor/github.com/go-openapi/runtime/server-middleware/LICENSE diff --git a/vendor/github.com/go-openapi/runtime/server-middleware/docui/doc.go b/vendor/github.com/go-openapi/runtime/server-middleware/docui/doc.go new file mode 100644 index 0000000000..809296d5c4 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/server-middleware/docui/doc.go @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +// Package docui provides standalone HTTP middlewares that serve OpenAPI +// documentation UIs (Swagger UI, ReDoc, RapiDoc) and the spec document +// itself. +// +// The package is stdlib-only and has no transitive dependency on any +// OpenAPI spec, loading or validation library, so it may be imported by +// any net/http application that simply wants to mount a documentation +// site. +package docui diff --git a/vendor/github.com/go-openapi/runtime/server-middleware/docui/options.go b/vendor/github.com/go-openapi/runtime/server-middleware/docui/options.go new file mode 100644 index 0000000000..c9e45f7794 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/server-middleware/docui/options.go @@ -0,0 +1,253 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package docui + +import ( + "net/http" + "net/url" + "strings" +) + +const ( + // constants that are common to all UI-serving middlewares. + defaultDocsPath = "docs" + defaultDocsURL = "/swagger.json" + defaultDocsTitle = "API Documentation" + + contentTypeHeader = "Content-Type" + applicationJSON = "application/json" +) + +// UIMiddleware is a function returning a http middleware which accepts UI [Option]. +type UIMiddleware func(...Option) func(http.Handler) http.Handler + +// Option to tune your swagger documentation UI middleware. +// +// Options may be combined to alter the route at which the UI asset is served, +// the URL of the spec document, the source URL of the UI asset and the title of the UI page. +// +// The embedded js scriptlet served may be modified using [WithUITemplate]. +type Option func(*options) + +// SpecOption can be applied to the [ServeSpec] middleware. +type SpecOption func(*specOptions) + +// SwaggerUIOptions define a group of extra options specific to the SwaggerUI component. +type SwaggerUIOptions struct { + // OAuth2CallbackURL sets the URL called after OAuth2 login + OAuth2CallbackURL string + + // Defines the URL of the swagger UI assets with presets. + // + // Default: https://unpkg.com/swagger-ui-dist/swagger-ui-standalone-preset.js + SwaggerPresetURL string + + // Defines style sheet URL. + // + // Default: https://unpkg.com/swagger-ui-dist/swagger-ui.css + SwaggerStylesURL string + + // Define the favicons URLs. + // + // Defaults: + // + // - 16x16: https://unpkg.com/swagger-ui-dist/favicon-16x16.png + // - 32x32: https://unpkg.com/swagger-ui-dist/favicon-32x32.png + Favicon32 string + Favicon16 string +} + +func (o *SwaggerUIOptions) applySwaggerUIDefaults() { + if o.SwaggerPresetURL == "" { + o.SwaggerPresetURL = swaggerPresetLatest + } + if o.SwaggerStylesURL == "" { + o.SwaggerStylesURL = swaggerStylesLatest + } + if o.Favicon16 == "" || o.Favicon32 == "" { + o.Favicon16 = swaggerFavicon16Latest + o.Favicon32 = swaggerFavicon32Latest + } +} + +type ( + options struct { + SwaggerUIOptions + + // BasePath for the UI, defaults to: / + BasePath string + + // Path combines with BasePath to construct the path to the UI, defaults to: "docs". + Path string + + // SpecURL is the URL of the spec document. + SpecURL string + + // Title for the documentation site, default to: API documentation + Title string + + // Template specifies a custom template to serve the UI + Template string + + // AssetsURL points to the js asset that generates the documentation page. + AssetsURL string + } + + specOptions struct { + Path string + Document string + } +) + +//////////////////////////////////////////////////////////// +// Common UI options +//////////////////////////////////////////////////////////// + +// WithUIBasePath sets the base path from where to serve the UI assets. +// +// Default: "/" +func WithUIBasePath(base string) Option { + return func(o *options) { + if !strings.HasPrefix(base, "/") { + base = "/" + base + } + o.BasePath = base + } +} + +// WithUIPath sets the path from where to serve the UI assets (i.e. /{basepath}/{path}). +// +// Default: "docs" +func WithUIPath(pth string) Option { + return func(o *options) { + o.Path = pth + } +} + +// WithUITitle sets the title of the UI. +// +// Default: "API documentation" +func WithUITitle(title string) Option { + return func(o *options) { + o.Title = title + } +} + +// WithUIAssetsURL sets the URL from where to fetch the js assets. +// +// Defaults: +// +// - for Redoc: https://cdn.jsdelivr.net/npm/redoc/bundles/redoc.standalone.js +// - for RapiDoc, this defaults to: https://unpkg.com/rapidoc/dist/rapidoc-min.js +// - for SwaggerUI: https://unpkg.com/swagger-ui-dist/swagger-ui-bundle.js +func WithUIAssetsURL(assets string) Option { + return func(o *options) { + o.AssetsURL = assets + } +} + +// WithUITemplate allows to set a custom template for the UI. +// +// This allows the caller to fully customize the rendered UI, using the advanced options +// provided by any UI. +// +// The UI [middleware] will panic if the template does not parse or execute properly. +// +// Reference documentations to customize your js scriptlet: +// +// - for Redoc: https://github.com/Redocly/redoc/blob/main/docs/deployment/html.md +// - for RapiDoc: https://github.com/rapi-doc/RapiDoc +// - for SwaggerUI: https://github.com/swagger-api/swagger-ui +func WithUITemplate[StringOrBytes ~string | ~[]byte](tpl StringOrBytes) Option { + return func(o *options) { + o.Template = string(tpl) + } +} + +// WithSpecURL sets the URL of the spec document. +// +// Defaults to: /swagger.json +func WithSpecURL(u string) Option { + return func(o *options) { + o.SpecURL = u + } +} + +//////////////////////////////////////////////////////////// +// SwaggerUI UI options +//////////////////////////////////////////////////////////// + +func WithSwaggerUIOptions(opts SwaggerUIOptions) Option { + return func(o *options) { + o.SwaggerUIOptions = opts + } +} + +//////////////////////////////////////////////////////////// +// Spec options +//////////////////////////////////////////////////////////// + +// WithSpecPath sets the path of the spec document. +// +// This is "/swagger.json" by default. +func WithSpecPath(pth string) SpecOption { + return func(o *specOptions) { + if pth == "" { + return + } + + o.Path = pth + } +} + +// WithSpecPathFromOptions reuses the same SpecPath as the one specified in +// a set of UI [Option] (extract the path from the URL provided by [WithSpecURL]). +func WithSpecPathFromOptions(opts ...Option) SpecOption { + return func(o *specOptions) { + uiOpts := optionsWithDefaults(opts) + + // If the spec URL is provided, there is a non-default path to serve the spec. + // + // This makes sure that the UI middleware is aligned with the Spec middleware. + u, _ := url.Parse(uiOpts.SpecURL) + + if u.Path == "" { + return + } + + o.Path = u.Path + } +} + +func optionsWithDefaults(opts []Option, prepend ...Option) options { + o := options{ + BasePath: "/", + Path: defaultDocsPath, + SpecURL: defaultDocsURL, + Title: defaultDocsTitle, + } + + prepend = append(prepend, opts...) + for _, apply := range prepend { + apply(&o) + } + + return o +} + +func specOptionsWithDefaults(opts []SpecOption) specOptions { + o := specOptions{ + Path: defaultDocsURL, + } + + for _, apply := range opts { + apply(&o) + } + + if !strings.HasPrefix(o.Path, "/") { + o.Path = "/" + o.Path + } + + return o +} diff --git a/vendor/github.com/go-openapi/runtime/server-middleware/docui/rapidoc.go b/vendor/github.com/go-openapi/runtime/server-middleware/docui/rapidoc.go new file mode 100644 index 0000000000..c050331b4b --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/server-middleware/docui/rapidoc.go @@ -0,0 +1,67 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package docui + +import ( + "bytes" + "fmt" + "html/template" + "net/http" + "path" +) + +// UseRapiDoc creates a middleware to serve a documentation site for a swagger spec using [RapidDoc]. +// +// [RapiDoc]: https://github.com/rapi-doc/RapiDoc +func UseRapiDoc(opts ...Option) func(next http.Handler) http.Handler { + pth, assets := rapiDocSetup(opts) + return func(next http.Handler) http.Handler { + return serveUI(pth, assets, next) + } +} + +// RapiDoc creates a [http.Handler] to serve a documentation site for a swagger spec using [RapidDoc]. +// +// By default, the UI is served at route "/docs" +// +// This allows for altering the spec before starting the [http] listener. +// +// [RapiDoc]: https://github.com/rapi-doc/RapiDoc +func RapiDoc(next http.Handler, opts ...Option) http.Handler { + pth, assets := rapiDocSetup(opts) + + return serveUI(pth, assets, next) +} + +func rapiDocSetup(opts []Option) (pth string, assets []byte) { + o := optionsWithDefaults(opts, + // defaults for rapiDoc + WithUITemplate(rapidocTemplate), + WithUIAssetsURL(rapidocLatest), + ) + pth = path.Join(o.BasePath, o.Path) + tmpl := template.Must(template.New("rapidoc").Parse(o.Template)) + buf := bytes.NewBuffer(nil) + if err := tmpl.Execute(buf, o); err != nil { + panic(fmt.Errorf("cannot execute template: %w", err)) + } + + return pth, buf.Bytes() +} + +const ( + rapidocLatest = "https://unpkg.com/rapidoc/dist/rapidoc-min.js" + rapidocTemplate = ` + + + {{ .Title }} + + + + + + + +` +) diff --git a/vendor/github.com/go-openapi/runtime/server-middleware/docui/redoc.go b/vendor/github.com/go-openapi/runtime/server-middleware/docui/redoc.go new file mode 100644 index 0000000000..31054a2476 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/server-middleware/docui/redoc.go @@ -0,0 +1,82 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package docui + +import ( + "bytes" + "fmt" + "html/template" + "net/http" + "path" +) + +// UseRedoc creates a middleware to serve a documentation site for a swagger spec using [Redoc]. +// +// [Redoc]: https://redocly.com/docs/redoc +func UseRedoc(opts ...Option) func(next http.Handler) http.Handler { + pth, assets := redocSetup(opts) + + return func(next http.Handler) http.Handler { + return serveUI(pth, assets, next) + } +} + +// Redoc creates a [http.Handler] to serve a documentation site for a swagger spec using [Redoc]. +// +// By default, the UI is served at route "/docs" +// +// This allows for altering the spec before starting the [http] listener. +// +// [Redoc]: https://redocly.com/docs/redoc +func Redoc(next http.Handler, opts ...Option) http.Handler { + pth, assets := redocSetup(opts) + + return serveUI(pth, assets, next) +} + +func redocSetup(opts []Option) (pth string, assets []byte) { + o := optionsWithDefaults(opts, + // defaults for redoc + WithUITemplate(redocTemplate), + WithUIAssetsURL(redocLatest), + ) + + pth = path.Join(o.BasePath, o.Path) + tmpl := template.Must(template.New("redoc").Parse(o.Template)) + buf := bytes.NewBuffer(nil) + if err := tmpl.Execute(buf, o); err != nil { + panic(fmt.Errorf("cannot execute template: %w", err)) + } + + return pth, buf.Bytes() +} + +const ( + redocLatest = "https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js" // "https://cdn.jsdelivr.net/npm/redoc/bundles/redoc.standalone.js" + redocTemplate = ` + + + {{ .Title }} + + + + + + + + + + + + + +` +) diff --git a/vendor/github.com/go-openapi/runtime/server-middleware/docui/render.go b/vendor/github.com/go-openapi/runtime/server-middleware/docui/render.go new file mode 100644 index 0000000000..1fb744fd00 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/server-middleware/docui/render.go @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package docui + +import ( + "fmt" + "net/http" + "path" +) + +// serveUI creates a [http.Handler] that serves a templated asset as text/html. +func serveUI(pth string, assets []byte, next http.Handler) http.Handler { + return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + if path.Clean(r.URL.Path) == pth { + rw.Header().Set(contentTypeHeader, "text/html; charset=utf-8") + rw.WriteHeader(http.StatusOK) + _, _ = rw.Write(assets) + + return + } + + if next != nil { + next.ServeHTTP(rw, r) + + return + } + + rw.Header().Set(contentTypeHeader, "text/plain") + rw.WriteHeader(http.StatusNotFound) + _, _ = fmt.Fprintf(rw, "%q not found", pth) + }) +} diff --git a/vendor/github.com/go-openapi/runtime/server-middleware/docui/spec.go b/vendor/github.com/go-openapi/runtime/server-middleware/docui/spec.go new file mode 100644 index 0000000000..59780199d5 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/server-middleware/docui/spec.go @@ -0,0 +1,50 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package docui + +import ( + "net/http" + "path" +) + +// UseSpec creates a middleware to serve a swagger spec as a JSON document. +func UseSpec(spec []byte, opts ...SpecOption) func(next http.Handler) http.Handler { + o := specOptionsWithDefaults(opts) + + return func(next http.Handler) http.Handler { + return handleSpec(o.Path, spec, next) + } +} + +// ServeSpec creates a [http.Handler] to serve a swagger spec as a JSON document. +// +// This allows for altering the spec before starting the [http] listener. +// +// Additional [SpecOption] can be used to change the path and the name of the document (defaults to "/swagger.json"). +func ServeSpec(spec []byte, next http.Handler, opts ...SpecOption) http.Handler { + o := specOptionsWithDefaults(opts) + + return handleSpec(o.Path, spec, next) +} + +func handleSpec(pth string, spec []byte, next http.Handler) http.Handler { + return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + if path.Clean(r.URL.Path) == pth { + rw.Header().Set(contentTypeHeader, applicationJSON) + rw.WriteHeader(http.StatusOK) + _, _ = rw.Write(spec) + + return + } + + if next != nil { + next.ServeHTTP(rw, r) + + return + } + + rw.Header().Set(contentTypeHeader, applicationJSON) + rw.WriteHeader(http.StatusNotFound) + }) +} diff --git a/vendor/github.com/go-openapi/runtime/server-middleware/docui/swaggerui.go b/vendor/github.com/go-openapi/runtime/server-middleware/docui/swaggerui.go new file mode 100644 index 0000000000..db0aa05e6a --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/server-middleware/docui/swaggerui.go @@ -0,0 +1,138 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package docui + +import ( + "bytes" + "fmt" + "html/template" + "net/http" + "path" +) + +// UseSwaggerUI creates a middleware to serve a documentation site for a swagger spec using [SwaggerUI]. +// +// [SwaggerUI]: https://swagger.io/tools/swagger-ui +func UseSwaggerUI(opts ...Option) func(next http.Handler) http.Handler { + pth, assets := swaggeruiSetup(opts) + + return func(next http.Handler) http.Handler { + return serveUI(pth, assets, next) + } +} + +// SwaggerUI creates a [http.Handler] to serve a documentation site for a swagger spec using [SwaggerUI]. +// +// By default, the UI is served at route "/docs" +// +// This allows for altering the spec before starting the [http] listener. +// +// [SwaggerUI]: https://swagger.io/tools/swagger-ui +func SwaggerUI(next http.Handler, opts ...Option) http.Handler { + pth, assets := swaggeruiSetup(opts) + + return serveUI(pth, assets, next) +} + +func swaggeruiSetup(opts []Option) (pth string, assets []byte) { + o := optionsWithDefaults(opts, + // defaults for SwaggerUI + WithUITemplate(swaggeruiTemplate), + WithUIAssetsURL(swaggerLatest), + ) + o.applySwaggerUIDefaults() + if o.OAuth2CallbackURL == "" { + o.OAuth2CallbackURL = path.Join(o.BasePath, o.Path, "oauth2-callback") + } + + pth = path.Join(o.BasePath, o.Path) + tmpl := template.Must(template.New("swaggerui").Parse(o.Template)) + buf := bytes.NewBuffer(nil) + if err := tmpl.Execute(buf, o); err != nil { + panic(fmt.Errorf("cannot execute template: %w", err)) + } + + return pth, buf.Bytes() +} + +const ( + swaggerLatest = "https://unpkg.com/swagger-ui-dist/swagger-ui-bundle.js" + swaggerPresetLatest = "https://unpkg.com/swagger-ui-dist/swagger-ui-standalone-preset.js" + swaggerStylesLatest = "https://unpkg.com/swagger-ui-dist/swagger-ui.css" + swaggerFavicon32Latest = "https://unpkg.com/swagger-ui-dist/favicon-32x32.png" + swaggerFavicon16Latest = "https://unpkg.com/swagger-ui-dist/favicon-16x16.png" + swaggeruiTemplate = ` + + + + + {{ .Title }} + + {{- if .SwaggerStylesURL }} + + {{- end }} + {{- if .Favicon32 }} + + {{- end }} + {{- if .Favicon16 }} + + {{- end }} + + + + +
+ + + {{- if .SwaggerPresetURL }} + + {{- end }} + + + +` +) diff --git a/vendor/github.com/go-openapi/runtime/middleware/swaggerui_oauth2.go b/vendor/github.com/go-openapi/runtime/server-middleware/docui/swaggerui_oauth2.go similarity index 70% rename from vendor/github.com/go-openapi/runtime/middleware/swaggerui_oauth2.go rename to vendor/github.com/go-openapi/runtime/server-middleware/docui/swaggerui_oauth2.go index 879bdbaade..a38e408f15 100644 --- a/vendor/github.com/go-openapi/runtime/middleware/swaggerui_oauth2.go +++ b/vendor/github.com/go-openapi/runtime/server-middleware/docui/swaggerui_oauth2.go @@ -1,30 +1,56 @@ // SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 -package middleware +package docui import ( "bytes" "fmt" "net/http" + "path" "text/template" ) -func SwaggerUIOAuth2Callback(opts SwaggerUIOpts, next http.Handler) http.Handler { - opts.EnsureDefaultsOauth2() +// UseSwaggerUIOAuth2Callback creates a middleware that serves a callback URL to complete +// a OAuth2 token handshake. +func UseSwaggerUIOAuth2Callback(opts ...Option) func(next http.Handler) http.Handler { + pth, assets := swaggeruiOAuth2Setup(opts) - pth := opts.OAuthCallbackURL - tmpl := template.Must(template.New("swaggeroauth").Parse(opts.Template)) - assets := bytes.NewBuffer(nil) - if err := tmpl.Execute(assets, opts); err != nil { + return func(next http.Handler) http.Handler { + return serveUI(pth, assets, next) + } +} + +// SwaggerUIOAuth2Callback creates a [http.Handler] that serves a callback URL to complete +// a OAuth2 token handshake. +func SwaggerUIOAuth2Callback(next http.Handler, opts ...Option) http.Handler { + pth, assets := swaggeruiOAuth2Setup(opts) + + return serveUI(pth, assets, next) +} + +func swaggeruiOAuth2Setup(opts []Option) (pth string, assets []byte) { + o := optionsWithDefaults(opts, + // defaults for SwaggerUI OAuth2 callback endpoint + WithUITemplate(swaggerOAuth2Template), + WithUIAssetsURL(swaggerLatest), + ) + o.applySwaggerUIDefaults() + if o.OAuth2CallbackURL == "" { + o.OAuth2CallbackURL = path.Join(o.BasePath, o.Path, "oauth2-callback") + } + + pth = o.OAuth2CallbackURL + tmpl := template.Must(template.New("swaggeroauth2").Parse(o.Template)) + buf := bytes.NewBuffer(nil) + if err := tmpl.Execute(buf, o); err != nil { panic(fmt.Errorf("cannot execute template: %w", err)) } - return serveUI(pth, assets.Bytes(), next) + return pth, buf.Bytes() } -const ( - swaggerOAuthTemplate = ` +const swaggerOAuth2Template = ` @@ -105,4 +131,3 @@ const ( ` -) diff --git a/vendor/github.com/go-openapi/runtime/server-middleware/mediatype/doc.go b/vendor/github.com/go-openapi/runtime/server-middleware/mediatype/doc.go new file mode 100644 index 0000000000..6f8aa31352 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/server-middleware/mediatype/doc.go @@ -0,0 +1,30 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +// Package mediatype provides a typed value for media types +// defined by RFC 7231 and RFC 2045. +// +// The matching/selection primitives used by both server-side +// validation and Accept-header negotiation. +// +// The package is stdlib-only. +// +// # The matching rule +// +// [MediaType.Matches] is asymmetric. The receiver acts as the "bound" +// (an allowed entry on the server side, or a candidate offer when +// matching against an Accept entry); the argument is the constraint +// (the actual incoming request, or the Accept entry being satisfied). +// +// - bare type/subtype must agree, with wildcard handling on either +// side ("*/*" matches anything; "type/*" matches any subtype); +// - if the receiver carries no parameters, any constraint is +// accepted regardless of its parameters; +// - otherwise every (key,value) pair on the constraint must be +// present on the receiver, with case-insensitive value +// comparison. The receiver may carry additional parameters the +// constraint does not list. +// +// q-values are NOT considered by [MediaType.Matches] — they are the +// negotiator's concern, handled inside [Set.BestMatch]. +package mediatype diff --git a/vendor/github.com/go-openapi/runtime/server-middleware/mediatype/lookup.go b/vendor/github.com/go-openapi/runtime/server-middleware/mediatype/lookup.go new file mode 100644 index 0000000000..598b60aca3 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/server-middleware/mediatype/lookup.go @@ -0,0 +1,116 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package mediatype + +// Lookup finds the entry in m matching mediaType, with alias-aware +// fallback. It is the canonical seam for codec-map lookups in both +// the client and server runtimes — placing the fallback policy here +// keeps alias definitions (and any future lookup tolerances) in one +// place. +// +// Lookup tries the following, in order, returning the first hit: +// +// 1. mediaType verbatim (fast path for callers that already pass a +// canonical, parameter-free string and store map keys in the +// same form). +// 2. An alias-aware walk against the parsed "type/subtype" form: +// a direct map hit on the parsed key, on its alias canonical +// if any, and finally an O(len(m)) scan returning any map +// entry whose key alias-canonicalizes to the same target. +// Catches both "map keyed by canonical, query uses alias" and +// "map keyed by one alias, query uses another alias of the +// same canonical". +// 3. When [AllowSuffix] is passed in opts: the same alias-aware +// walk against the RFC 6839 structured-syntax suffix base. +// Catches the "spec/traffic divergence" case (request for +// application/vnd.api+json finds a JSON consumer registered +// under application/json). Query-side suffix fold only — no +// map-side suffix folding. +// +// Lookup does NOT fall back to "*/*". Callers that want wildcard +// behavior (the historical resolveConsumer pattern in the client +// runtime) chain that themselves after a Lookup miss — keeping +// wildcard semantics explicit at each call site. +// +// Map keys are expected in canonical "type/subtype" form (no +// parameters). The runtime's default Consumers / Producers maps +// follow this convention. +// +// Returns (zero, false) when: +// +// - m is empty; +// - mediaType fails to parse and is not present verbatim; +// - none of the active steps hits. +// +// The malformed-vs-not-found distinction is intentionally elided: +// codec-lookup callers treat both as the same "no codec" error path. +func Lookup[T any](m map[string]T, mediaType string, opts ...MatchOption) (T, bool) { + var zero T + if len(m) == 0 { + return zero, false + } + o := applyMatchOptions(opts) + // Fast path: raw key (preserves any caller behaviour that stored + // non-canonical strings as map keys, and skips parsing in the + // common already-canonical case). + if v, ok := m[mediaType]; ok { + return v, true + } + mt, err := Parse(mediaType) + if err != nil { + return zero, false + } + key := mt.Type + "/" + mt.Subtype + if v, ok := findByCanonical(m, key); ok { + return v, true + } + if o.allowSuffix && mt.Suffix != "" { + base := mt.Base() + if baseKey := base.Type + "/" + base.Subtype; baseKey != key { + if v, ok := findByCanonical(m, baseKey); ok { + return v, true + } + } + } + return zero, false +} + +// findByCanonical returns the first entry in m whose key +// alias-canonicalizes to the same value as target. +// +// Tries direct hits before the O(len(m)) walk: +// +// 1. m[target] — map keyed by the same string. +// 2. m[aliases[target]] — map keyed by the canonical when target +// is an alias. +// 3. Walk m: return any entry where canonical(k) == canonical(target). +// Catches the "map keyed by an alias different from the query +// side" case (e.g. registered under text/yaml, queried as +// application/x-yaml — both canonicalize to application/yaml). +// +// Map size is single-digit for the runtime's codec maps, so the +// walk is negligible. +func findByCanonical[T any](m map[string]T, target string) (T, bool) { + if v, ok := m[target]; ok { + return v, true + } + canonTarget := target + if canon, ok := aliases[target]; ok { + canonTarget = canon + if v, ok := m[canonTarget]; ok { + return v, true + } + } + for k, v := range m { + kCanon := k + if c, ok := aliases[k]; ok { + kCanon = c + } + if kCanon == canonTarget { + return v, true + } + } + var zero T + return zero, false +} diff --git a/vendor/github.com/go-openapi/runtime/server-middleware/mediatype/match.go b/vendor/github.com/go-openapi/runtime/server-middleware/mediatype/match.go new file mode 100644 index 0000000000..6a16d0b6f2 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/server-middleware/mediatype/match.go @@ -0,0 +1,65 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package mediatype + +// MatchFirst reports whether actual matches any entry in allowed, +// using [MediaType.Match] — the param-aware RFC 7231 rule plus the +// alias bridge from the package-internal alias table. +// +// The scan is multi-pass and tier-ordered: the first pass returns +// the first allowed entry that matches under [MatchExact] (RFC 7231 +// semantics); the second pass looks for a [MatchAlias] match; when +// [AllowSuffix] is in opts a third pass looks for a [MatchSuffix] +// match. This preserves the "stronger tier wins" ordering from +// [Set.BestMatch] while keeping the "first match wins" semantics +// within each tier. +// +// Return values: +// +// - (matched, true, nil) — the first allowed entry that +// matches, with exact matches preferred over alias matches. +// - (zero, false, nil) — actual is well-formed but no +// allowed entry accepts it. Maps to an HTTP 415 outcome. +// - (zero, false, err) — actual fails to parse. err +// wraps [ErrMalformed], so callers can use [errors.Is] to +// distinguish this case. Maps to an HTTP 400 outcome. +// +// Allowed entries that themselves fail to parse are skipped (they +// cannot match any well-formed actual), and no error is surfaced +// for them. +// +// An empty allowed list returns (zero, false, nil). MatchFirst is +// the primitive; callers decide what no-constraints means in their +// context. +func MatchFirst(allowed []string, actual string, opts ...MatchOption) (MediaType, bool, error) { + if len(allowed) == 0 { + return MediaType{}, false, nil + } + actualMT, err := Parse(actual) + if err != nil { + return MediaType{}, false, err + } + o := applyMatchOptions(opts) + // Tier-ordered passes over the allowed list. The list is + // typically short (an operation's Consumes set), so re-parsing + // each entry on every pass is cheaper than caching parses across + // passes. + tiers := []MatchKind{MatchExact, MatchAlias} + if o.allowSuffix { + tiers = append(tiers, MatchSuffix) + } + for _, want := range tiers { + for _, a := range allowed { + allowedMT, perr := Parse(a) + if perr != nil { + continue + } + if allowedMT.Match(actualMT) == want { + return allowedMT, true, nil + } + } + } + + return MediaType{}, false, nil +} diff --git a/vendor/github.com/go-openapi/runtime/server-middleware/mediatype/mediatype.go b/vendor/github.com/go-openapi/runtime/server-middleware/mediatype/mediatype.go new file mode 100644 index 0000000000..41a32a160a --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/server-middleware/mediatype/mediatype.go @@ -0,0 +1,392 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package mediatype + +import ( + "fmt" + "mime" + "strconv" + "strings" +) + +const wildcard = "*" + +// Internal constants for the suffixBase table and any future +// in-package references to the well-known base media types. +const ( + typeApplication = "application" + subtypeJSON = "json" + subtypeXML = "xml" + subtypeYAML = "yaml" + + mtYAML = typeApplication + "/" + subtypeYAML +) + +// Specificity scores returned by [MediaType.Specificity], ordered from +// least to most specific. +const ( + SpecificityAny = iota // "*/*" + SpecificityType // "type/*" + SpecificityExact // "type/subtype" (no params) + SpecificityExactWithParams // "type/subtype;k=v" +) + +// MatchKind classifies the strength of a match between two media +// types. Larger values represent stronger matches and win in +// negotiation tie-breaks. +// +// MatchExact covers direct subtype or wildcard agreement under RFC +// 7231 rules; MatchAlias is returned when the strict comparison +// fails but the two values agree after canonicalization through the +// internal alias table (see [MediaType.Canonical]); MatchSuffix is +// returned only when both alias and exact comparisons fail but the +// two values agree after folding the RFC 6839 structured-syntax +// suffix (see [MediaType.Base]). +// +// MatchSuffix matches are off by default at the negotiation / +// lookup callers — they count only when [AllowSuffix] is passed to +// [Set.BestMatch], [MatchFirst], or [Lookup]. The opt-in is the +// single user-visible knob; [MediaType.Match] itself always returns +// the strongest tier that succeeds. +type MatchKind int + +// MatchKind values. Returned by [MediaType.Match]. +const ( + MatchNone MatchKind = iota // no match + MatchSuffix // matched via the RFC 6839 suffix base + MatchAlias // matched via the alias table + MatchExact // matched directly (RFC 7231 semantics) +) + +// MatchOption configures the matching tolerances used by +// [Set.BestMatch], [MatchFirst], and [Lookup]. The zero behaviour +// is strict: only [MatchAlias] and [MatchExact] count. +type MatchOption func(*matchOptions) + +type matchOptions struct { + allowSuffix bool +} + +func applyMatchOptions(opts []MatchOption) matchOptions { + var o matchOptions + for _, opt := range opts { + opt(&o) + } + return o +} + +// AllowSuffix returns a [MatchOption] that lets the caller count +// [MatchSuffix] results as valid matches. Use this to opt into +// RFC 6839 structured-syntax suffix tolerance for situations where +// the client/server traffic does not strictly abide by the spec +// (typical example: server returning application/problem+json +// against operations that only declare application/json in +// produces). +func AllowSuffix() MatchOption { + return func(o *matchOptions) { + o.allowSuffix = true + } +} + +type mediaTypeError string + +func (e mediaTypeError) Error() string { + return string(e) +} + +// ErrMalformed is the sentinel returned (wrapped) by [Parse] when its input +// cannot be parsed as an RFC 7231 media type. +// +// Callers can test for it with [errors.Is] to distinguish a client-side +// malformed Content-Type header (an HTTP 400 outcome) from a well-formed +// value that simply matches no allowed entry (an HTTP 415 outcome). +const ErrMalformed mediaTypeError = "mediatype: malformed" + +// MediaType is a parsed RFC 7231 media type with optional parameters and +// an optional q-value (used by Accept negotiation). +// +// Type, Subtype and the keys of Params are lowercased. Parameter values +// are preserved verbatim; comparisons are case-insensitive (matching the +// pre-v0.30 behaviour and the common convention for charset, version, etc.). +// +// Suffix exposes the RFC 6839 structured syntax suffix (the token after +// the final '+' in Subtype) as a parallel hint. Subtype itself retains +// the full wire value, so existing callers comparing Subtype against a +// string see no change. +type MediaType struct { + Type string + Subtype string + Suffix string + Params map[string]string + Q float64 +} + +// suffixBase maps a known RFC 6839 / RFC 9512 structured syntax +// suffix (without the leading '+', lowercased) to its base media +// type. It is the authoritative table consulted by [MediaType.Base]. +// +// The table is intentionally small: only suffixes whose base type +// has a codec in the default runtime maps are listed. CBOR, zip, +// BER, DER, FastInfoset and WBXML are registered by IANA but have +// no default codec in this runtime; adding them is gated on having +// something to do with them. +// +// Package-internal by design: the external API is [MediaType.Base]. +// If users ever need to extend the table, a Register-style function +// is the right answer, not an exported mutable map. +var suffixBase = map[string]MediaType{ + subtypeJSON: {Type: typeApplication, Subtype: subtypeJSON}, + subtypeXML: {Type: typeApplication, Subtype: subtypeXML}, + subtypeYAML: {Type: typeApplication, Subtype: subtypeYAML}, +} + +// aliases maps a deprecated or legacy media-type name to its +// canonical registered equivalent. Keys are the lowercased +// "type/subtype" form with no parameters; values are the canonical +// "type/subtype" form, also without parameters. +// +// Entries are limited to media types whose authoritative RFC +// explicitly names the alias. The seed entries cite RFC 9512 §2.1, +// which enumerates "Deprecated alias names for this type: +// application/x-yaml, text/yaml, and text/x-yaml" as part of the +// IANA registration template for application/yaml. +// +// Pull requests adding entries need an analogous citation in the +// commit message; entries without authoritative backing belong in +// caller-side canonicalization, not here. +// +// Package-internal by design: the external API is +// [MediaType.Canonical] and [MediaType.Match]. If users ever need +// to register their own aliases, a Register-style function is the +// right answer, not an exported mutable map. +var aliases = map[string]string{ + "application/x-yaml": mtYAML, // RFC 9512 §2.1 + "text/yaml": mtYAML, // RFC 9512 §2.1 + "text/x-yaml": mtYAML, // RFC 9512 §2.1 +} + +// Parse parses a single media type. The input may carry parameters and a +// q-value; the q-value is extracted into [MediaType.Q] and removed from +// [MediaType.Params]. +// +// An empty input returns an error. +func Parse(s string) (MediaType, error) { + s = strings.TrimSpace(s) + if s == "" { + return MediaType{}, fmt.Errorf("%w: empty value", ErrMalformed) + } + full, params, err := mime.ParseMediaType(s) + if err != nil { + return MediaType{}, fmt.Errorf("%w: %w", ErrMalformed, err) + } + slash := strings.IndexByte(full, '/') + if slash <= 0 || slash == len(full)-1 { + return MediaType{}, fmt.Errorf("%w: %q has no subtype", ErrMalformed, s) + } + mt := MediaType{ + Type: full[:slash], + Subtype: full[slash+1:], + Q: 1.0, + } + // RFC 6839: structured syntax suffix is the trailing '+'-delimited + // token of the subtype. Only the last '+' counts ("foo+bar+json" → + // suffix "json"). A trailing '+' with nothing after it is not a + // valid suffix and is ignored. mime.ParseMediaType has already + // lowercased the subtype, so no further ToLower is needed. + if plus := strings.LastIndexByte(mt.Subtype, '+'); plus >= 0 && plus < len(mt.Subtype)-1 { + mt.Suffix = mt.Subtype[plus+1:] + } + + if q, ok := params["q"]; ok { + if qf, isFloat := boundedQ(q); isFloat { + mt.Q = qf + } + delete(params, "q") + } + + if len(params) > 0 { + mt.Params = params + } + + return mt, nil +} + +// String renders the canonical "type/subtype;k=v;k=v" form. Parameters are +// emitted in lexicographic key order (the standard library guarantees this) +// so the result is stable. The q-value is NOT emitted — it is meta, not +// part of the media type identity. +func (m MediaType) String() string { + if m.Type == "" && m.Subtype == "" { + return "" + } + + return mime.FormatMediaType(m.Type+"/"+m.Subtype, m.Params) +} + +// Matches reports whether the receiver accepts other, per the package +// documentation: the receiver is the bound, other is the constraint. +func (m MediaType) Matches(other MediaType) bool { + if !typeAgrees(m.Type, other.Type) { + return false + } + if !subtypeAgrees(m.Type, m.Subtype, other.Type, other.Subtype) { + return false + } + if len(m.Params) == 0 { + return true + } + for k, v := range other.Params { + sv, ok := m.Params[k] + if !ok || !strings.EqualFold(sv, v) { + return false + } + } + + return true +} + +// Specificity returns a numeric score for ordering matches. Higher is more +// specific. The returned value is one of [SpecificityAny], +// [SpecificityType], [SpecificityExact] or [SpecificityExactWithParams]. +func (m MediaType) Specificity() int { + if m.Type == wildcard && m.Subtype == wildcard { + return SpecificityAny + } + if m.Subtype == wildcard { + return SpecificityType + } + if len(m.Params) == 0 { + return SpecificityExact + } + + return SpecificityExactWithParams +} + +func boundedQ(q string) (float64, bool) { + qf, err := strconv.ParseFloat(q, 64) + if err != nil { + return 0, false + } + + if qf < 0 { + qf = 0 + } + + if qf > 1 { + qf = 1 + } + + return qf, true +} + +// typeAgrees reports whether two top-level types match, allowing "*" on +// either side. A type of "*" without a "*" subtype is rejected per RFC +// 7231 §5.3.2 ("*/sub" is not valid), but Parse never produces such a +// shape — it goes through mime.ParseMediaType. +func typeAgrees(a, b string) bool { + return a == wildcard || b == wildcard || a == b +} + +// subtypeAgrees handles the "type/*" wildcard: the bare type must match +// (a "*/*" pair has already been accepted by typeAgrees above). +func subtypeAgrees(at, asub, bt, bsub string) bool { + if at == wildcard || bt == wildcard { + // at least one side is "*/*" or "*/sub". With typeAgrees having + // returned true, we accept. + return true + } + if asub == wildcard || bsub == wildcard { + return true + } + + return asub == bsub +} + +// StripParams returns a copy of m with no parameters. Q is preserved +// because it drives negotiation ordering, not media-type identity. +// +// Useful for the legacy "ignore parameters" negotiation mode. +func (m MediaType) StripParams() MediaType { + return MediaType{Type: m.Type, Subtype: m.Subtype, Suffix: m.Suffix, Q: m.Q} +} + +// Base returns the base media type implied by the RFC 6839 structured +// syntax suffix, or m unchanged when: +// +// - Suffix is empty; +// - Suffix is not present in the package-internal suffix→base table. +// +// The returned value represents the structural base only: it carries +// no parameters and no q-value. Use it to find a codec for the +// underlying wire format — for example, "application/vnd.api+json" +// resolves to "application/json". +// +// Base does not mutate the receiver. +func (m MediaType) Base() MediaType { + if m.Suffix == "" { + return m + } + base, ok := suffixBase[m.Suffix] + if !ok { + return m + } + return base +} + +// Canonical returns m rewritten to its canonical media type via +// the package-internal alias table, or m unchanged when +// (Type, Subtype) is not a known alias. Params and Q are preserved on the returned value; Suffix +// is recomputed from the canonical Subtype (none of the current +// entries carry a suffix, but the contract is forward-safe). +// +// Canonical does not mutate the receiver. +func (m MediaType) Canonical() MediaType { + key := m.Type + "/" + m.Subtype + canon, ok := aliases[key] + if !ok { + return m + } + slash := strings.IndexByte(canon, '/') + out := m + out.Type = canon[:slash] + out.Subtype = canon[slash+1:] + out.Suffix = "" + if plus := strings.LastIndexByte(out.Subtype, '+'); plus >= 0 && plus < len(out.Subtype)-1 { + out.Suffix = out.Subtype[plus+1:] + } + return out +} + +// Match reports how m matches other, classifying the result by +// [MatchKind]. Used by negotiation to rank candidate offers: +// stronger tiers win when both apply. +// +// Returns, strongest first: +// +// - MatchExact when m.Matches(other) is true under the strict +// RFC 7231 rules (including wildcards and the param subset +// rule). +// - MatchAlias when m.Canonical().Matches(other.Canonical()) +// is true but the strict comparison failed. +// - MatchSuffix when m.Base().Canonical().Matches( +// other.Base().Canonical()) is true but the alias comparison +// failed (RFC 6839 structured-syntax suffix fold). +// - MatchNone otherwise. +// +// The asymmetric "bound vs constraint" rule of [MediaType.Matches] +// is preserved at every tier. Match itself is always lenient — the +// opt-in to count MatchSuffix lives one level up at [Set.BestMatch], +// [MatchFirst], and [Lookup] via [AllowSuffix]. +func (m MediaType) Match(other MediaType) MatchKind { + if m.Matches(other) { + return MatchExact + } + if m.Canonical().Matches(other.Canonical()) { + return MatchAlias + } + if m.Base().Canonical().Matches(other.Base().Canonical()) { + return MatchSuffix + } + return MatchNone +} diff --git a/vendor/github.com/go-openapi/runtime/server-middleware/mediatype/set.go b/vendor/github.com/go-openapi/runtime/server-middleware/mediatype/set.go new file mode 100644 index 0000000000..70f62a18d4 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/server-middleware/mediatype/set.go @@ -0,0 +1,138 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package mediatype + +import ( + "strings" +) + +// Set is a list of media types — typically the parsed value of an Accept +// header, or a list of server-side offers. +type Set []MediaType + +// ParseAccept parses a comma-separated list of media types, as found in +// the Accept, Accept-Charset (etc.) HTTP headers. Malformed entries are +// skipped silently — be liberal in what you accept. +// +// An empty input returns nil. +func ParseAccept(s string) Set { + parts := splitTopLevel(s) + if len(parts) == 0 { + return nil + } + out := make(Set, 0, len(parts)) + for _, p := range parts { + mt, err := Parse(p) + if err != nil { + continue + } + out = append(out, mt) + } + + return out +} + +// BestMatch picks the offer most acceptable to the receiver's Accept +// entries. Selection follows RFC 7231 §5.3.2 plus tier-aware +// ranking: +// +// - highest q-value wins; +// - ties on q broken by the highest [MediaType.Specificity] of the +// matching Accept entry; +// - ties on specificity broken by [MatchKind] (MatchExact beats +// MatchAlias beats MatchSuffix); +// - ties on match kind broken by earliest position in offered. +// +// Accept entries with q=0 are treated as exclusions and never match. +// MatchSuffix results are only counted when [AllowSuffix] is passed. +// Returns ok=false if no offer matched any non-zero-q entry. +func (s Set) BestMatch(offered Set, opts ...MatchOption) (best MediaType, ok bool) { + if len(s) == 0 || len(offered) == 0 { + return MediaType{}, false + } + o := applyMatchOptions(opts) + bestQ := -1.0 + bestSpec := -1 + bestKind := MatchNone + bestIdx := -1 + for i, offer := range offered { + for _, entry := range s { + if entry.Q == 0 { + continue + } + kind := offer.Match(entry) + if kind == MatchNone { + continue + } + if kind == MatchSuffix && !o.allowSuffix { + continue + } + spec := entry.Specificity() + switch { + case entry.Q > bestQ: + best, ok = offer, true + bestQ = entry.Q + bestSpec = spec + bestKind = kind + bestIdx = i + case entry.Q < bestQ: + // not better + case spec > bestSpec: + best, ok = offer, true + bestSpec = spec + bestKind = kind + bestIdx = i + case spec < bestSpec: + // not better + case kind > bestKind: + best, ok = offer, true + bestKind = kind + bestIdx = i + case kind < bestKind: + // not better + case bestIdx < 0 || i < bestIdx: + best, ok = offer, true + bestIdx = i + } + } + } + + return best, ok +} + +// splitTopLevel splits s on top-level commas, respecting double-quoted +// strings (RFC 7230 §3.2.6 — quoted-string). +func splitTopLevel(s string) []string { + if strings.IndexByte(s, ',') < 0 { + if t := strings.TrimSpace(s); t != "" { + return []string{t} + } + return nil + } + var out []string + start := 0 + inQuote := false + escape := false + for i := range len(s) { + c := s[i] + switch { + case escape: + escape = false + case inQuote && c == '\\': + escape = true + case c == '"': + inQuote = !inQuote + case c == ',' && !inQuote: + if t := strings.TrimSpace(s[start:i]); t != "" { + out = append(out, t) + } + start = i + 1 + } + } + if t := strings.TrimSpace(s[start:]); t != "" { + out = append(out, t) + } + + return out +} diff --git a/vendor/github.com/go-openapi/runtime/server-middleware/negotiate/doc.go b/vendor/github.com/go-openapi/runtime/server-middleware/negotiate/doc.go new file mode 100644 index 0000000000..a9f278c31b --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/server-middleware/negotiate/doc.go @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +// Package negotiate provides server-side HTTP content negotiation +// helpers — selecting the response Content-Type from an Accept header +// and the response Content-Encoding from an Accept-Encoding header. +// +// The package is stdlib-only (modulo the typed [mediatype.MediaType] +// values it consumes). +// +// The exported [ContentType] honours MIME-type parameters by default; +// use [WithIgnoreParameters] to restore the pre-v0.30 looser match. +package negotiate diff --git a/vendor/github.com/go-openapi/runtime/middleware/header/header.go b/vendor/github.com/go-openapi/runtime/server-middleware/negotiate/header/header.go similarity index 97% rename from vendor/github.com/go-openapi/runtime/middleware/header/header.go rename to vendor/github.com/go-openapi/runtime/server-middleware/negotiate/header/header.go index 6ce870d893..6f3c3f0038 100644 --- a/vendor/github.com/go-openapi/runtime/middleware/header/header.go +++ b/vendor/github.com/go-openapi/runtime/server-middleware/negotiate/header/header.go @@ -300,7 +300,13 @@ func expectQuality(s string) (q float64, rest string) { n = n*10 + int(b) - '0' d *= 10 } - return q + float64(n)/float64(d), s[i:] + result := q + float64(n)/float64(d) + // RFC 7231 §5.3.1: qvalue is in [0, 1]. Inputs like "1.1" + // would otherwise yield > 1; reject as malformed. + if result > 1 { + return -1, s[i:] + } + return result, s[i:] } func expectTokenOrQuoted(s string) (value string, rest string) { diff --git a/vendor/github.com/go-openapi/runtime/server-middleware/negotiate/negotiate.go b/vendor/github.com/go-openapi/runtime/server-middleware/negotiate/negotiate.go new file mode 100644 index 0000000000..3c932a1969 --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/server-middleware/negotiate/negotiate.go @@ -0,0 +1,215 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package negotiate + +import ( + "net/http" + "strings" + + "github.com/go-openapi/runtime/server-middleware/mediatype" + "github.com/go-openapi/runtime/server-middleware/negotiate/header" +) + +// Option configures [ContentType] behaviour. +type Option func(*options) + +type options struct { + ignoreParameters bool + matchSuffix bool +} + +func optionsWithDefaults(opts []Option) options { + var o options + for _, apply := range opts { + apply(&o) + } + + return o +} + +// WithIgnoreParameters returns an [Option] that strips MIME-type +// parameters from both Accept entries and offers before matching, restoring +// the behaviour the runtime had before v0.30. +// +// New code should leave parameters honoured (the default). This option +// exists for applications that depend on the looser pre-v0.30 match — +// most often because their producers and Accept clients use mismatched +// charset or version params that they treat as informational. +// +// Example — per-call opt-out: +// +// chosen := negotiate.ContentType(r, offers, "", +// negotiate.WithIgnoreParameters(true), +// ) +// +// Example — server-wide opt-out (via [middleware.Context]): +// +// ctx := middleware.NewContext(spec, api, nil).SetIgnoreParameters(true) +func WithIgnoreParameters(ignore bool) Option { + return func(o *options) { + o.ignoreParameters = ignore + } +} + +// WithMatchSuffix returns an [Option] that extends content +// negotiation to tolerate RFC 6839 structured-syntax suffix media +// types. When enabled, an Accept entry of "application/json" +// matches an offer of "application/vnd.api+json" and vice versa, +// for the suffixes recognised by the runtime (+json, +xml, +yaml). +// +// Default: strict (false). Use only when interoperating with +// clients or servers that do not strictly abide by the spec — for +// example, servers returning application/problem+json error +// responses against operations that only declare application/json +// in produces. +// +// Suffix matches always lose to exact and alias matches when those +// are on offer; see [mediatype.MatchKind] for the tier ordering. +// +// Example — per-call opt-in: +// +// chosen := negotiate.ContentType(r, offers, "", +// negotiate.WithMatchSuffix(true), +// ) +// +// Example — server-wide opt-in (via [middleware.Context]): +// +// ctx := middleware.NewContext(spec, api, nil).SetMatchSuffix(true) +func WithMatchSuffix(enable bool) Option { + return func(o *options) { + o.matchSuffix = enable + } +} + +// ContentEncoding returns the best offered content encoding for the +// request's Accept-Encoding header. If two offers match with equal +// weight then the offer earlier in the list is preferred. If no offers +// are acceptable, then "" is returned. +// +// Encoding tokens have no parameters, so this function is unaffected by +// the v0.30 parameter-honouring change to [ContentType]. +// +// Deprecated: ContentEncoding negotiation is not used by the components +// of this project. +// +// This historical addition has never been associated with proper +// compression middleware and is thus half a feature. +// The runtime does not ship compression. +// Use github.com/CAFxX/httpcompression or github.com/klauspost/compress/gzhttp +// at the http.Handler level, or github.com/klauspost/compress/* for client +// transport wrapping. See docs/examples/middleware for a recipe. +func ContentEncoding(r *http.Request, offers []string) string { + bestOffer := "identity" + bestQ := -1.0 + specs := header.ParseAccept(r.Header, "Accept-Encoding") + for _, offer := range offers { + for _, spec := range specs { + if spec.Q > bestQ && + (spec.Value == "*" || spec.Value == offer) { + bestQ = spec.Q + bestOffer = offer + } + } + } + if bestQ == 0 { + bestOffer = "" + } + + return bestOffer +} + +// ContentType returns the best offered content type for the request's +// Accept header. If two offers match with equal weight, then the more +// specific offer is preferred (text/* trumps */*; type/subtype trumps +// type/*). If two offers match with equal weight and specificity, then +// the offer earlier in the list is preferred. If no offers match, then +// defaultOffer is returned. +// +// As of v0.30 the matching rule honours MIME-type parameters: an Accept +// entry of "text/plain;charset=utf-8" matches an offer of bare +// "text/plain" (offer carries no constraint), but it does NOT match an +// offer of "text/plain;charset=ascii" (charset values disagree). Pass +// [WithIgnoreParameters](true) to restore the pre-v0.30 behaviour where +// parameters were stripped before matching — see [WithIgnoreParameters] +// for details and an example. +// +// When the Accept header is absent, the first offer is returned +// unchanged (param-stripping is irrelevant in that case). +func ContentType(r *http.Request, offers []string, defaultOffer string, opts ...Option) string { + if len(offers) == 0 { + return defaultOffer + } + o := optionsWithDefaults(opts) + + // Per RFC 7230 §3.2.2, multiple Accept headers are equivalent to a + // single comma-joined value. Join before parsing so we don't drop + // later entries. + acceptValues := r.Header.Values("Accept") + if len(acceptValues) == 0 { + return offers[0] + } + acceptSet := mediatype.ParseAccept(strings.Join(acceptValues, ", ")) + if len(acceptSet) == 0 { + return defaultOffer + } + + offerSet := make(mediatype.Set, 0, len(offers)) + rawByIdx := make([]string, 0, len(offers)) + for _, raw := range offers { + mt, err := mediatype.Parse(raw) + if err != nil { + continue + } + offerSet = append(offerSet, mt) + rawByIdx = append(rawByIdx, raw) + } + if len(offerSet) == 0 { + return defaultOffer + } + + if o.ignoreParameters { + acceptSet = stripSet(acceptSet) + offerSet = stripSet(offerSet) + } + + var matchOpts []mediatype.MatchOption + if o.matchSuffix { + matchOpts = append(matchOpts, mediatype.AllowSuffix()) + } + best, ok := acceptSet.BestMatch(offerSet, matchOpts...) + if !ok { + return defaultOffer + } + // Return the original raw offer string so callers receive the value + // they declared, with its parameters preserved. + for i, mt := range offerSet { + if mt.Type == best.Type && mt.Subtype == best.Subtype && sameParams(mt.Params, best.Params) { + return rawByIdx[i] + } + } + + return best.String() +} + +func stripSet(s mediatype.Set) mediatype.Set { + out := make(mediatype.Set, len(s)) + for i, m := range s { + out[i] = m.StripParams() + } + + return out +} + +func sameParams(a, b map[string]string) bool { + if len(a) != len(b) { + return false + } + for k, v := range a { + if b[k] != v { + return false + } + } + + return true +} diff --git a/vendor/github.com/go-openapi/runtime/statuses.go b/vendor/github.com/go-openapi/runtime/statuses.go index c0f3e6b447..273d9c6ced 100644 --- a/vendor/github.com/go-openapi/runtime/statuses.go +++ b/vendor/github.com/go-openapi/runtime/statuses.go @@ -60,7 +60,7 @@ var Statuses = map[int]string{ 444: "No Response", 449: "Retry With", 450: "Blocked by Windows Parental Controls", - 451: "Wrong Exchange Server", + 451: "Unavailable For Legal Reasons", 499: "Client Closed Request", 500: "Internal Server Error", 501: "Not Implemented", diff --git a/vendor/github.com/go-openapi/runtime/text.go b/vendor/github.com/go-openapi/runtime/text.go index 1252ac88c7..3764a87fe5 100644 --- a/vendor/github.com/go-openapi/runtime/text.go +++ b/vendor/github.com/go-openapi/runtime/text.go @@ -36,14 +36,14 @@ func TextConsumer() Consumer { if tu, ok := data.(encoding.TextUnmarshaler); ok { err := tu.UnmarshalText(b) if err != nil { - return fmt.Errorf("text consumer: %v", err) + return fmt.Errorf("text consumer: %w", err) } return nil } t := reflect.TypeOf(data) - if data != nil && t.Kind() == reflect.Ptr { + if data != nil && t.Kind() == reflect.Pointer { v := reflect.Indirect(reflect.ValueOf(data)) if t.Elem().Kind() == reflect.String { v.SetString(string(b)) @@ -70,7 +70,7 @@ func TextProducer() Producer { if tm, ok := data.(encoding.TextMarshaler); ok { txt, err := tm.MarshalText() if err != nil { - return fmt.Errorf("text producer: %v", err) + return fmt.Errorf("text producer: %w", err) } _, err = writer.Write(txt) return err diff --git a/vendor/github.com/go-openapi/runtime/yamlpc/yaml.go b/vendor/github.com/go-openapi/runtime/yamlpc/yaml.go index ca71edbb1b..b7fab88906 100644 --- a/vendor/github.com/go-openapi/runtime/yamlpc/yaml.go +++ b/vendor/github.com/go-openapi/runtime/yamlpc/yaml.go @@ -6,8 +6,9 @@ package yamlpc import ( "io" - "github.com/go-openapi/runtime" yaml "go.yaml.in/yaml/v3" + + "github.com/go-openapi/runtime" ) // YAMLConsumer creates a consumer for [yaml] data. diff --git a/vendor/github.com/go-openapi/spec/.gitignore b/vendor/github.com/go-openapi/spec/.gitignore index 885dc27ab0..d8f4186fe5 100644 --- a/vendor/github.com/go-openapi/spec/.gitignore +++ b/vendor/github.com/go-openapi/spec/.gitignore @@ -3,4 +3,3 @@ .idea .env .mcp.json -.claude/ diff --git a/vendor/github.com/go-openapi/spec/.golangci.yml b/vendor/github.com/go-openapi/spec/.golangci.yml index dc7c96053d..9d2733176e 100644 --- a/vendor/github.com/go-openapi/spec/.golangci.yml +++ b/vendor/github.com/go-openapi/spec/.golangci.yml @@ -4,7 +4,10 @@ linters: disable: - depguard - funlen + - goconst - godox + - gomodguard + - gomodguard_v2 - exhaustruct - nlreturn - nonamedreturns diff --git a/vendor/github.com/go-openapi/spec/CONTRIBUTORS.md b/vendor/github.com/go-openapi/spec/CONTRIBUTORS.md index 2967e3cedd..12fd069b0f 100644 --- a/vendor/github.com/go-openapi/spec/CONTRIBUTORS.md +++ b/vendor/github.com/go-openapi/spec/CONTRIBUTORS.md @@ -4,12 +4,12 @@ | Total Contributors | Total Contributions | | --- | --- | -| 38 | 392 | +| 38 | 403 | | Username | All Time Contribution Count | All Commits | | --- | --- | --- | | @casualjim | 191 | | -| @fredbi | 90 | | +| @fredbi | 101 | | | @pytlesk4 | 26 | | | @kul-amr | 10 | | | @keramix | 10 | | @@ -47,4 +47,4 @@ | @ChandanChainani | 1 | | | @bvwells | 1 | | - _this file was generated by the [Contributors GitHub Action](https://github.com/github/contributors)_ + _this file was generated by the [Contributors GitHub Action](https://github.com/github-community-projects/contributors)_ diff --git a/vendor/github.com/go-openapi/spec/README.md b/vendor/github.com/go-openapi/spec/README.md index 134809fd77..7c96eb9a58 100644 --- a/vendor/github.com/go-openapi/spec/README.md +++ b/vendor/github.com/go-openapi/spec/README.md @@ -18,12 +18,9 @@ The object model for OpenAPI v2 specification documents. * **2025-12-19** : new community chat on discord * a new discord community channel is available to be notified of changes and support users - * our venerable Slack channel remains open, and will be eventually discontinued on **2026-03-31** You may join the discord community by clicking the invite link on the discord badge (also above). [![Discord Channel][discord-badge]][discord-url] -Or join our Slack channel: [![Slack Channel][slack-logo]![slack-badge]][slack-url] - ## Status API is stable. @@ -95,9 +92,9 @@ This library ships under the [SPDX-License-Identifier: Apache-2.0](./LICENSE). ## Other documentation * [All-time contributors](./CONTRIBUTORS.md) -* [Contributing guidelines](.github/CONTRIBUTING.md) -* [Maintainers documentation](docs/MAINTAINERS.md) -* [Code style](docs/STYLE.md) +* [Contributing guidelines][contributing-doc-site] +* [Maintainers documentation][maintainers-doc-site] +* [Code style][style-doc-site] ## Cutting a new release @@ -132,11 +129,8 @@ Maintainers can cut a new release by either: [doc-url]: https://goswagger.io/go-openapi [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/spec [godoc-url]: http://pkg.go.dev/github.com/go-openapi/spec -[slack-logo]: https://a.slack-edge.com/e6a93c1/img/icons/favicon-32.png -[slack-badge]: https://img.shields.io/badge/slack-blue?link=https%3A%2F%2Fgoswagger.slack.com%2Farchives%2FC04R30YM -[slack-url]: https://goswagger.slack.com/archives/C04R30YMU [discord-badge]: https://img.shields.io/discord/1446918742398341256?logo=discord&label=discord&color=blue -[discord-url]: https://discord.gg/twZ9BwT3 +[discord-url]: https://discord.gg/FfnFYaC3k5 [license-badge]: http://img.shields.io/badge/license-Apache%20v2-orange.svg @@ -146,3 +140,7 @@ Maintainers can cut a new release by either: [goversion-url]: https://github.com/go-openapi/spec/blob/master/go.mod [top-badge]: https://img.shields.io/github/languages/top/go-openapi/spec [commits-badge]: https://img.shields.io/github/commits-since/go-openapi/spec/latest + +[contributing-doc-site]: https://go-openapi.github.io/doc-site/contributing/contributing/index.html +[maintainers-doc-site]: https://go-openapi.github.io/doc-site/maintainers/index.html +[style-doc-site]: https://go-openapi.github.io/doc-site/contributing/style/index.html diff --git a/vendor/github.com/go-openapi/spec/doc.go b/vendor/github.com/go-openapi/spec/doc.go index 04eea35758..8b589781a6 100644 --- a/vendor/github.com/go-openapi/spec/doc.go +++ b/vendor/github.com/go-openapi/spec/doc.go @@ -4,4 +4,33 @@ // Package spec exposes an object model for OpenAPIv2 specifications (swagger). // // The exposed data structures know how to serialize to and deserialize from JSON. +// +// # Security +// +// Resolving and expanding "$ref" pointers loads documents through a pluggable loader (see +// [ExpandOptions.PathLoader] and [ExpandOptions.PathLoaderWithOptions]). By default, that +// loader is NOT sandboxed, so a specification obtained from an untrusted source can abuse it: +// +// - A local "$ref" such as "file:///etc/passwd" or a relative "../../secret.json" is read +// straight off disk. A malicious specification can therefore read any file the process can +// access (arbitrary file read / path traversal, CWE-22). +// - A remote "$ref" such as "http://169.254.169.254/..." is fetched with no restriction. A +// malicious specification can therefore probe or reach internal addresses (SSRF, CWE-918). +// +// Do NOT expand or resolve an untrusted specification with the default options. To process +// untrusted specifications safely, inject a confined loader: +// +// - Recommended: use the restricted loaders from github.com/go-openapi/loads, for example +// loads.SpecRestricted(path, root) or loads.SetRestrictedLoaders(root). They confine local +// reads to root and route remote fetches through a client that rejects loopback, private and +// link-local addresses, and the confinement applies to every "$ref" resolved during +// expansion. +// - Or directly: set [ExpandOptions.PathLoaderWithOptions] to a loader built with +// github.com/go-openapi/swag/loading options such as loading.WithRoot (to confine local +// reads to a directory) and loading.WithHTTPClient (to restrict remote fetches). A "$ref" +// that resolves outside root is then rejected, including one reached through a "file://" +// URI or a "../" traversal. +// +// Expanding an untrusted specification also has a resource-exhaustion vector ("$ref" +// amplification); see [ExpandOptions.MaxExpansionNodes], which is bounded by default. package spec diff --git a/vendor/github.com/go-openapi/spec/errors.go b/vendor/github.com/go-openapi/spec/errors.go index eaca01cc83..740b773c2c 100644 --- a/vendor/github.com/go-openapi/spec/errors.go +++ b/vendor/github.com/go-openapi/spec/errors.go @@ -20,6 +20,13 @@ var ( // ErrExpandUnsupportedType indicates that $ref expansion is attempted on some invalid type. ErrExpandUnsupportedType = errors.New("expand: unsupported type. Input should be of type *Parameter or *Response") + // ErrExpandTooManyNodes indicates that $ref expansion exceeded the maximum number of schema nodes + // allowed for a single expansion (see ExpandOptions.MaxExpansionNodes). + // + // This is a safeguard against maliciously crafted specifications that expand to an exponential + // number of nodes from a small input (a $ref amplification / "billion laughs" style attack). + ErrExpandTooManyNodes = errors.New("expand: too many schema nodes: expansion budget exceeded (see ExpandOptions.MaxExpansionNodes)") + // ErrSpec is an error raised by the spec package. ErrSpec = errors.New("spec error") ) diff --git a/vendor/github.com/go-openapi/spec/expander.go b/vendor/github.com/go-openapi/spec/expander.go index f9c2fa327a..00eb5b53a8 100644 --- a/vendor/github.com/go-openapi/spec/expander.go +++ b/vendor/github.com/go-openapi/spec/expander.go @@ -6,10 +6,23 @@ package spec import ( "encoding/json" "fmt" + + "github.com/go-openapi/swag/loading" ) const smallPrealloc = 10 +// DefaultMaxExpansionNodes is the default upper bound on the number of schema nodes +// expanded during a single ExpandSpec / ExpandSchema* call. +// +// It guards against maliciously crafted specifications whose $ref graph expands to an +// exponential number of nodes from a few kilobytes of input. For reference, expanding the +// full Kubernetes API specification (the largest real-world spec we test against) visits +// roughly 47,000 nodes, so this default leaves ample headroom for legitimate documents. +// +// See ExpandOptions.MaxExpansionNodes to tune or disable this budget. +const DefaultMaxExpansionNodes = 500_000 + // ExpandOptions provides options for the spec expander. // // RelativeBase is the path to the root document. This can be a remote URL or a path to a local file. @@ -17,13 +30,59 @@ const smallPrealloc = 10 // If left empty, the root document is assumed to be located in the current working directory: // all relative $ref's will be resolved from there. // -// PathLoader injects a document loading method. By default, this resolves to the function provided by the SpecLoader package variable. +// PathLoader injects a document loading method. By default, this resolves to the function provided by the PathLoader package variable. +// +// PathLoaderWithOptions is an alternative document loader that accepts [loading.Option] values, matching the +// signature used by the go-openapi/swag/loading and go-openapi/loads loaders. When set, it takes precedence over +// PathLoader. This lets a caller inject an options-aware (e.g. path-confined) loader without an adapter closure. +// +// Security: the default loader is not sandboxed. When expanding an untrusted specification, inject a confined +// loader (for example one built with loading.WithRoot) — see the package "Security" section. type ExpandOptions struct { RelativeBase string // the path to the root document to expand. This is a file, not a directory SkipSchemas bool // do not expand schemas, just paths, parameters and responses ContinueOnError bool // continue expanding even after and error is found PathLoader func(string) (json.RawMessage, error) `json:"-"` // the document loading method that takes a path as input and yields a json document AbsoluteCircularRef bool // circular $ref remaining after expansion remain absolute URLs + + // PathLoaderWithOptions injects a document loading method that accepts loading options. + // + // It has the same role as PathLoader but matches the option-aware loader signature exposed by + // github.com/go-openapi/swag/loading (and github.com/go-openapi/loads), so such a loader can be + // injected directly, without wrapping it in an adapter closure. + // + // When set, PathLoaderWithOptions takes precedence over PathLoader. The provided loader is expected + // to carry its own loading options (for example a path confinement built with loading.WithRoot); + // the expander itself invokes it without adding options. + PathLoaderWithOptions func(string, ...loading.Option) (json.RawMessage, error) `json:"-"` + + // MaxExpansionNodes caps the number of schema nodes expanded during a single expansion call, + // as a safeguard against $ref amplification attacks (see ErrExpandTooManyNodes). + // + // The value is interpreted as follows: + // + // 0 (the zero value): use DefaultMaxExpansionNodes. Every caller is protected by default. + // <0: no limit (unbounded expansion). Use only with fully trusted specifications. + // >0: cap the expansion at this number of nodes. + // + // When the budget is exceeded, expansion stops and ErrExpandTooManyNodes is returned. + // Because this is a resource-exhaustion safeguard, the error is always returned, even when + // ContinueOnError is set. + MaxExpansionNodes int +} + +// maxExpansionNodes resolves the tri-state MaxExpansionNodes option into an effective budget. +// +// A returned value of 0 means "unbounded". +func (o *ExpandOptions) maxExpansionNodes() int { + switch { + case o.MaxExpansionNodes == 0: + return DefaultMaxExpansionNodes + case o.MaxExpansionNodes < 0: + return 0 // unbounded + default: + return o.MaxExpansionNodes + } } func optionsOrDefault(opts *ExpandOptions) *ExpandOptions { @@ -39,6 +98,10 @@ func optionsOrDefault(opts *ExpandOptions) *ExpandOptions { } // ExpandSpec expands the references in a swagger spec. +// +// Security: with default options the document loader is not sandboxed, so a "$ref" in an +// untrusted spec can read local files or reach internal addresses. See the package "Security" +// section before expanding untrusted input. func ExpandSpec(spec *Swagger, options *ExpandOptions) error { options = optionsOrDefault(options) resolver := defaultSchemaLoader(spec, options, nil, nil) @@ -121,25 +184,50 @@ func baseForRoot(root any, cache ResolutionCache) string { // (use ExpandSchemaWithBasePath to resolve external references). // // Setting the cache is optional and this parameter may safely be left to nil. +// +// ExpandSchema uses the package default document loader, which is not sandboxed. To expand a +// schema whose $ref may derive from untrusted input, use [ExpandSchemaWithOptions] with a confined +// loader — see the package "Security" section. func ExpandSchema(schema *Schema, root any, cache ResolutionCache) error { + return ExpandSchemaWithOptions(schema, root, cache, nil) +} + +// ExpandSchemaWithOptions expands the refs in the schema object with reference to the root object, +// honoring the provided expand options. It is the option-aware form of [ExpandSchema]. +// +// In particular, set opts.PathLoaderWithOptions (or opts.PathLoader) to inject a confined document +// loader when expanding a schema whose $ref may derive from an untrusted source (see the package +// "Security" section). opts.ContinueOnError, opts.AbsoluteCircularRef and opts.MaxExpansionNodes +// are honored as well. +// +// The base path is always derived from root (as with [ExpandSchema]), so opts.RelativeBase and +// opts.SkipSchemas are ignored. Passing nil opts is equivalent to [ExpandSchema]. +// +// Setting the cache is optional and this parameter may safely be left to nil. +func ExpandSchemaWithOptions(schema *Schema, root any, cache ResolutionCache, opts *ExpandOptions) error { cache = cacheOrDefault(cache) if root == nil { root = schema } - opts := &ExpandOptions{ - // when a root is specified, cache the root as an in-memory document for $ref retrieval - RelativeBase: baseForRoot(root, cache), - SkipSchemas: false, - ContinueOnError: false, + effective := ExpandOptions{} + if opts != nil { + effective = *opts // preserve caller options (loader, ContinueOnError, budget, ...) } + // when a root is specified, cache the root as an in-memory document for $ref retrieval + effective.RelativeBase = baseForRoot(root, cache) + effective.SkipSchemas = false - return ExpandSchemaWithBasePath(schema, cache, opts) + return ExpandSchemaWithBasePath(schema, cache, &effective) } // ExpandSchemaWithBasePath expands the refs in the schema object, base path configured through expand options. // // Setting the cache is optional and this parameter may safely be left to nil. +// +// Security: with default options the document loader is not sandboxed, so a "$ref" in an +// untrusted schema can read local files or reach internal addresses. See the package "Security" +// section before expanding untrusted input. func ExpandSchemaWithBasePath(schema *Schema, cache ResolutionCache, opts *ExpandOptions) error { if schema == nil { return nil @@ -192,6 +280,10 @@ func expandItems(target Schema, parentRefs []string, resolver *schemaLoader, bas //nolint:gocognit,gocyclo,cyclop // complex but well-tested $ref expansion logic; refactoring deferred to dedicated PR func expandSchema(target Schema, parentRefs []string, resolver *schemaLoader, basePath string) (*Schema, error) { + if err := resolver.context.countNode(); err != nil { + return &target, err + } + if target.Ref.String() == "" && target.Ref.IsRoot() { newRef := normalizeRef(&target.Ref, basePath) target.Ref = *newRef @@ -454,25 +546,28 @@ func expandOperation(op *Operation, resolver *schemaLoader, basePath string) err // // Setting the cache is optional and this parameter may safely be left to nil. func ExpandResponseWithRoot(response *Response, root any, cache ResolutionCache) error { - cache = cacheOrDefault(cache) - opts := &ExpandOptions{ - RelativeBase: baseForRoot(root, cache), - } - resolver := defaultSchemaLoader(root, opts, cache, nil) - - return expandParameterOrResponse(response, resolver, opts.RelativeBase) + return ExpandResponseWithOptions(response, root, cache, nil) } // ExpandResponse expands a response based on a basepath // // All refs inside response will be resolved relative to basePath. func ExpandResponse(response *Response, basePath string) error { - opts := optionsOrDefault(&ExpandOptions{ - RelativeBase: basePath, - }) - resolver := defaultSchemaLoader(nil, opts, nil, nil) + return ExpandResponseWithOptions(response, nil, nil, &ExpandOptions{RelativeBase: basePath}) +} - return expandParameterOrResponse(response, resolver, opts.RelativeBase) +// ExpandResponseWithOptions expands a response, honoring the provided expand options. +// +// It is the option-aware form of [ExpandResponse] and [ExpandResponseWithRoot]. When root is +// non-nil, refs resolve against the in-memory root document; otherwise they resolve relative to +// opts.RelativeBase. +// +// Set opts.PathLoaderWithOptions (or opts.PathLoader) to inject a confined document loader when +// the response's $ref may derive from an untrusted source — see the package "Security" section. +// +// Setting the cache is optional and this parameter may safely be left to nil. +func ExpandResponseWithOptions(response *Response, root any, cache ResolutionCache, opts *ExpandOptions) error { + return expandRefableWithOptions(response, root, cache, opts) } // ExpandParameterWithRoot expands a parameter based on a root document, not a fetchable document. @@ -480,26 +575,43 @@ func ExpandResponse(response *Response, basePath string) error { // Notice that it is impossible to reference a json schema in a different document other than root // (use ExpandParameter to resolve external references). func ExpandParameterWithRoot(parameter *Parameter, root any, cache ResolutionCache) error { - cache = cacheOrDefault(cache) - - opts := &ExpandOptions{ - RelativeBase: baseForRoot(root, cache), - } - resolver := defaultSchemaLoader(root, opts, cache, nil) - - return expandParameterOrResponse(parameter, resolver, opts.RelativeBase) + return ExpandParameterWithOptions(parameter, root, cache, nil) } // ExpandParameter expands a parameter based on a basepath. // This is the exported version of expandParameter // all refs inside parameter will be resolved relative to basePath. func ExpandParameter(parameter *Parameter, basePath string) error { - opts := optionsOrDefault(&ExpandOptions{ - RelativeBase: basePath, - }) - resolver := defaultSchemaLoader(nil, opts, nil, nil) + return ExpandParameterWithOptions(parameter, nil, nil, &ExpandOptions{RelativeBase: basePath}) +} + +// ExpandParameterWithOptions expands a parameter, honoring the provided expand options. +// +// It is the option-aware form of [ExpandParameter] and [ExpandParameterWithRoot]. When root is +// non-nil, refs resolve against the in-memory root document; otherwise they resolve relative to +// opts.RelativeBase. +// +// Set opts.PathLoaderWithOptions (or opts.PathLoader) to inject a confined document loader when +// the parameter's $ref may derive from an untrusted source — see the package "Security" section. +// +// Setting the cache is optional and this parameter may safely be left to nil. +func ExpandParameterWithOptions(parameter *Parameter, root any, cache ResolutionCache, opts *ExpandOptions) error { + return expandRefableWithOptions(parameter, root, cache, opts) +} + +// expandRefableWithOptions is the shared implementation for the option-aware parameter/response +// expanders. When root is non-nil, refs resolve against the in-memory root (base derived from +// root); otherwise they resolve relative to opts.RelativeBase. opts carries the loader and other +// expand options. +func expandRefableWithOptions(input any, root any, cache ResolutionCache, opts *ExpandOptions) error { + cache = cacheOrDefault(cache) + effective := optionsOrDefault(opts) // clones and normalizes RelativeBase; preserves the loader + if root != nil { + effective.RelativeBase = baseForRoot(root, cache) + } + resolver := defaultSchemaLoader(root, effective, cache, nil) - return expandParameterOrResponse(parameter, resolver, opts.RelativeBase) + return expandParameterOrResponse(input, resolver, effective.RelativeBase) } func getRefAndSchema(input any) (*Ref, *Schema, error) { diff --git a/vendor/github.com/go-openapi/spec/header.go b/vendor/github.com/go-openapi/spec/header.go index 599ba2c5d7..f656e0789a 100644 --- a/vendor/github.com/go-openapi/spec/header.go +++ b/vendor/github.com/go-openapi/spec/header.go @@ -150,7 +150,11 @@ func (h Header) MarshalJSON() ([]byte, error) { if err != nil { return nil, err } - return jsonutils.ConcatJSON(b1, b2, b3), nil + b4, err := json.Marshal(h.VendorExtensible) + if err != nil { + return nil, err + } + return jsonutils.ConcatJSON(b1, b2, b3, b4), nil } // UnmarshalJSON unmarshals this header from JSON. diff --git a/vendor/github.com/go-openapi/spec/ref.go b/vendor/github.com/go-openapi/spec/ref.go index 40b7d486c9..d1a7ab9b94 100644 --- a/vendor/github.com/go-openapi/spec/ref.go +++ b/vendor/github.com/go-openapi/spec/ref.go @@ -7,7 +7,6 @@ import ( "bytes" "encoding/gob" "encoding/json" - "net/http" "os" "path/filepath" @@ -62,7 +61,15 @@ func (r *Ref) RemoteURI() string { return u.String() } -// IsValidURI returns true when the url the ref points to can be found. +// IsValidURI returns true when the ref points to a valid URI. +// +// For an absolute URL, it only checks that the reference is a well-formed URI. It deliberately +// does NOT perform a network request to verify that the remote target is reachable: doing so +// would make validation depend on network availability and expose callers to denial-of-service +// and SSRF when processing untrusted specifications. Resolving and fetching remote references is +// the responsibility of the expander, through its configurable (and confinable) document loader. +// +// For a local file reference, it checks that the file exists. func (r *Ref) IsValidURI(basepaths ...string) bool { if r.String() == "" { return true @@ -74,15 +81,8 @@ func (r *Ref) IsValidURI(basepaths ...string) bool { } if r.HasFullURL { - //nolint:noctx,gosec - rr, err := http.Get(v) - if err != nil { - return false - } - defer rr.Body.Close() - - // true if the response is >= 200 and < 300 - return rr.StatusCode/100 == 2 //nolint:mnd + // a well-formed absolute URL is a valid URI; remote reachability is not checked here (see above). + return true } if !r.HasFileScheme && !r.HasFullFilePath && !r.HasURLPathOnly { diff --git a/vendor/github.com/go-openapi/spec/schema.go b/vendor/github.com/go-openapi/spec/schema.go index d7a481bf1a..c71a2e5c5a 100644 --- a/vendor/github.com/go-openapi/spec/schema.go +++ b/vendor/github.com/go-openapi/spec/schema.go @@ -9,7 +9,7 @@ import ( "strings" "github.com/go-openapi/jsonpointer" - "github.com/go-openapi/swag/jsonname" + "github.com/go-openapi/jsonpointer/jsonname" "github.com/go-openapi/swag/jsonutils" ) diff --git a/vendor/github.com/go-openapi/spec/schema_loader.go b/vendor/github.com/go-openapi/spec/schema_loader.go index 0894c932c6..491ed020fd 100644 --- a/vendor/github.com/go-openapi/spec/schema_loader.go +++ b/vendor/github.com/go-openapi/spec/schema_loader.go @@ -5,6 +5,7 @@ package spec import ( "encoding/json" + "errors" "fmt" "log" "net/url" @@ -43,24 +44,48 @@ type resolverContext struct { basePath string loadDoc func(string) (json.RawMessage, error) rootID string + + // nodes counts the schema nodes expanded so far, capped by maxNodes to guard against + // $ref amplification. maxNodes == 0 means unbounded. Shared, single-threaded: no locking needed. + nodes int + maxNodes int } func newResolverContext(options *ExpandOptions) *resolverContext { expandOptions := optionsOrDefault(options) - // path loader may be overridden by options + // path loader may be overridden by options. An option-aware loader takes precedence over a + // plain one, which in turn takes precedence over the package-level default. var loader func(string) (json.RawMessage, error) - if expandOptions.PathLoader == nil { - loader = PathLoader - } else { + switch { + case expandOptions.PathLoaderWithOptions != nil: + withOptions := expandOptions.PathLoaderWithOptions + loader = func(pth string) (json.RawMessage, error) { + // the injected loader carries its own loading options: none are added here. + return withOptions(pth) + } + case expandOptions.PathLoader != nil: loader = expandOptions.PathLoader + default: + loader = PathLoader } return &resolverContext{ circulars: make(map[string]bool), basePath: expandOptions.RelativeBase, // keep the root base path in context loadDoc: loader, + maxNodes: expandOptions.maxExpansionNodes(), + } +} + +// countNode accounts for one expanded schema node and reports whether the expansion budget +// has been exceeded. A maxNodes of 0 disables the budget (unbounded expansion). +func (c *resolverContext) countNode() error { + c.nodes++ + if c.maxNodes > 0 && c.nodes > c.maxNodes { + return ErrExpandTooManyNodes } + return nil } type schemaLoader struct { @@ -117,7 +142,7 @@ func (r *schemaLoader) updateBasePath(transitive *schemaLoader, basePath string) func (r *schemaLoader) resolveRef(ref *Ref, target any, basePath string) error { tgt := reflect.ValueOf(target) - if tgt.Kind() != reflect.Ptr { + if tgt.Kind() != reflect.Pointer { return ErrResolveRefNeedsAPointer } @@ -246,14 +271,22 @@ func (r *schemaLoader) deref(input any, parentRefs []string, basePath string) er } func (r *schemaLoader) shouldStopOnError(err error) bool { - if err != nil && !r.options.ContinueOnError { + if err == nil { + return false + } + + if errors.Is(err, ErrExpandTooManyNodes) { + // a blown expansion budget is a hard, document-level failure: it is a safeguard against + // resource exhaustion and is never suppressed by ContinueOnError. return true } - if err != nil { - log.Println(err) + if !r.options.ContinueOnError { + return true } + log.Println(err) + return false } diff --git a/vendor/github.com/go-openapi/strfmt/.gitignore b/vendor/github.com/go-openapi/strfmt/.gitignore index bbdffea78a..20c4e0fa04 100644 --- a/vendor/github.com/go-openapi/strfmt/.gitignore +++ b/vendor/github.com/go-openapi/strfmt/.gitignore @@ -4,3 +4,4 @@ .env .mcp.json go.work.sum +.worktrees diff --git a/vendor/github.com/go-openapi/strfmt/.golangci.yml b/vendor/github.com/go-openapi/strfmt/.golangci.yml index 3c4cd489a1..31480c7138 100644 --- a/vendor/github.com/go-openapi/strfmt/.golangci.yml +++ b/vendor/github.com/go-openapi/strfmt/.golangci.yml @@ -2,9 +2,12 @@ version: "2" linters: default: all disable: + - goconst # has become too noisy. Disabled - depguard - funlen - gomoddirectives + - gomodguard + - gomodguard_v2 - godox - exhaustruct - nlreturn diff --git a/vendor/github.com/go-openapi/strfmt/CONTRIBUTORS.md b/vendor/github.com/go-openapi/strfmt/CONTRIBUTORS.md index a5d5ed6e62..fa17ff3111 100644 --- a/vendor/github.com/go-openapi/strfmt/CONTRIBUTORS.md +++ b/vendor/github.com/go-openapi/strfmt/CONTRIBUTORS.md @@ -4,12 +4,12 @@ | Total Contributors | Total Contributions | | --- | --- | -| 40 | 231 | +| 41 | 243 | | Username | All Time Contribution Count | All Commits | | --- | --- | --- | | @casualjim | 88 | | -| @fredbi | 63 | | +| @fredbi | 74 | | | @youyuanwu | 13 | | | @jlambatl | 9 | | | @GlenDC | 5 | | @@ -26,6 +26,7 @@ | @bg451 | 2 | | | @aleksandr-vin | 2 | | | @ujjwalsh | 1 | | +| @patchwright | 1 | | | @kenjones-cisco | 1 | | | @jwalter1-quest | 1 | | | @ccoVeille | 1 | | diff --git a/vendor/github.com/go-openapi/strfmt/README.md b/vendor/github.com/go-openapi/strfmt/README.md index 4afef43733..15ee0e5826 100644 --- a/vendor/github.com/go-openapi/strfmt/README.md +++ b/vendor/github.com/go-openapi/strfmt/README.md @@ -67,7 +67,7 @@ It also provides convenient extensions to go-openapi users. - [x] go-openapi custom format extensions - bsonobjectid (BSON objectID) - creditcard - - duration (e.g. "3 weeks", "1ms") + - duration (e.g. "3 weeks", "1ms") (aka "duration-human") - hexcolor (e.g. "#FFFFFF") - isbn, isbn10, isbn13 - mac (e.g "01:02:03:04:05:06") @@ -76,11 +76,26 @@ It also provides convenient extensions to go-openapi users. - uuid, uuid3, uuid4, uuid5, uuid7 - cidr (e.g. "192.0.2.1/24", "2001:db8:a0b:12f0::1/32") - ulid (e.g. "00000PP9HGSBSSDZ1JTEXBJ0PW", [spec](https://github.com/ulid/spec)) +- [x] JSON-schema draft 2020 formats + - duration-iso8601 B(e.g. "P2W") > NOTE: as the name stands for, this package is intended to support string formatting only. > It does not provide validation for numerical values with swagger format extension for JSON types "number" or > "integer" (e.g. float, double, int32...). +## Durations + +We have 2 very different definitions of the "duration" format: the "human-readable" duration that used to be just "duration", +and the new "duration-iso8601". There is no "dual" parser that accepts both formats: types are specialized. + +To clarify the situation, a new alias for the duration format is introduced "duration-human" (e.g. "1 ms"), as opposed to +"duration-iso8601". + +The `Default` format registry wires "duration-human" as the default mapping for "duration" +(preexisting behavior, no breaking change - aligned with Swagger 2.0 which did not define "duration"). + +A new `JSONSchema2020` registry wires "duration-iso8601" as the default mapping for "duration". + ### Type conversion All types defined here are stringers and may be converted to strings with `.String()`. @@ -104,6 +119,7 @@ List of defined types: - Date - DateTime - Duration +- DurationISO8601 and `ISODuration[P ISODurationPolicy]` (for optional behavior) - Email - HexColor - Hostname diff --git a/vendor/github.com/go-openapi/strfmt/bson.go b/vendor/github.com/go-openapi/strfmt/bson.go index 16a83f6408..b0c05d83bc 100644 --- a/vendor/github.com/go-openapi/strfmt/bson.go +++ b/vendor/github.com/go-openapi/strfmt/bson.go @@ -10,11 +10,6 @@ import ( "fmt" ) -func init() { //nolint:gochecknoinits // registers bsonobjectid format in the default registry - var id ObjectId - Default.Add("bsonobjectid", &id, IsBSONObjectID) -} - // IsBSONObjectID returns true when the string is a valid BSON [ObjectId]. func IsBSONObjectID(str string) bool { _, err := objectIDFromHex(str) diff --git a/vendor/github.com/go-openapi/strfmt/country.go b/vendor/github.com/go-openapi/strfmt/country.go new file mode 100644 index 0000000000..39aeb3d786 --- /dev/null +++ b/vendor/github.com/go-openapi/strfmt/country.go @@ -0,0 +1,173 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package strfmt + +import ( + "database/sql/driver" + "encoding/json" + "fmt" + + "github.com/go-openapi/strfmt/internal/countries" +) + +// Country represents an ISO 3166 country alpha-3 or alpha-2 code as a string format. +// +// swagger:strfmt country. +type Country struct { + countries.Country + + l int +} + +const ( + alpha2Len = 2 + alpha3Len = 3 +) + +// IsCountry checks if a string is a valid ISO 3166 country format. +func IsCountry(str string) bool { + _, err := ParseCountry(str) + + return err == nil +} + +// ParseCountry parses a string that represents a valid [Country]. +func ParseCountry(str string) (Country, error) { + l := len(str) + switch l { + case alpha3Len: + c, ok := countries.CountriesISO3[str] + if !ok { + return Country{}, fmt.Errorf("unrecognized strfmt.Country %q: %w", str, ErrFormat) + } + + return Country{Country: c, l: alpha3Len}, nil + case alpha2Len: + c, ok := countries.CountriesISO2[str] + if !ok { + return Country{}, fmt.Errorf("unrecognized strfmt.Country %q: %w", str, ErrFormat) + } + + return Country{Country: c, l: alpha2Len}, nil + default: + return Country{}, fmt.Errorf("invalid length for strfmt.Country in: %q: %w", str, ErrFormat) + } +} + +// MarshalText returns this instance into text. +func (u Country) MarshalText() ([]byte, error) { + return []byte(u.String()), nil +} + +// UnmarshalText hydrates this instance from text. +func (u *Country) UnmarshalText(data []byte) error { // validation is performed later on + c, err := ParseCountry(string(data)) + if err != nil { + return err + } + + *u = c + + return nil +} + +// Scan read a value from a database driver. +func (u *Country) Scan(raw any) error { + switch v := raw.(type) { + case []byte: + c, err := ParseCountry(string(v)) + if err != nil { + return err + } + *u = c + case string: + c, err := ParseCountry(v) + if err != nil { + return err + } + *u = c + default: + return fmt.Errorf("cannot sql.Scan() strfmt.Country from: %#v: %w", v, ErrFormat) + } + + return nil +} + +// Value converts a value to a database driver value. +func (u Country) Value() (driver.Value, error) { + return driver.Value(u.String()), nil +} + +func (u Country) String() string { + switch u.l { + case alpha3Len: + return u.ISOAlpha3 + case alpha2Len: + return u.ISOAlpha2 + default: + return "" + } +} + +// MarshalJSON returns the [Country] as JSON. +func (u Country) MarshalJSON() ([]byte, error) { + return json.Marshal(u.String()) +} + +// UnmarshalJSON sets the [Country] from JSON. +func (u *Country) UnmarshalJSON(data []byte) error { + if string(data) == jsonNull { + return nil + } + var ustr string + if err := json.Unmarshal(data, &ustr); err != nil { + return err + } + + c, err := ParseCountry(ustr) + if err != nil { + return err + } + + *u = c + + return nil +} + +// DeepCopyInto copies the receiver and writes its value into out. +func (u *Country) DeepCopyInto(out *Country) { + *out = *u +} + +// DeepCopy copies the receiver into a new [Country]. +func (u *Country) DeepCopy() *Country { + if u == nil { + return nil + } + + out := new(Country) + u.DeepCopyInto(out) + + return out +} + +// GobEncode implements the gob.GobEncoder interface. +func (u Country) GobEncode() ([]byte, error) { + return u.MarshalText() +} + +// GobDecode implements the gob.GobDecoder interface. +func (u *Country) GobDecode(data []byte) error { + return u.UnmarshalText(data) +} + +// MarshalBinary implements the encoding.[encoding.BinaryMarshaler] interface. +func (u Country) MarshalBinary() ([]byte, error) { + return u.MarshalText() +} + +// UnmarshalBinary implements the encoding.[encoding.BinaryUnmarshaler] interface. +func (u *Country) UnmarshalBinary(data []byte) error { + return u.UnmarshalText(data) +} diff --git a/vendor/github.com/go-openapi/strfmt/currency.go b/vendor/github.com/go-openapi/strfmt/currency.go new file mode 100644 index 0000000000..7af115663f --- /dev/null +++ b/vendor/github.com/go-openapi/strfmt/currency.go @@ -0,0 +1,146 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package strfmt + +import ( + "database/sql/driver" + "encoding/json" + "fmt" + + "golang.org/x/text/currency" +) + +// Currency represents an ISO 4217 currency alpha-3 code as a string format. +// +// # Implementation +// +// golang.org/x/text/currency +// +// swagger:strfmt currency. +type Currency struct { + currency.Unit +} + +// IsCurrency checks if a string is a valid currency format. +func IsCurrency(str string) bool { + _, err := ParseCurrency(str) + + return err == nil +} + +// ParseCurrency parses a string that represents a valid [Currency]. +func ParseCurrency(str string) (Currency, error) { + cur, err := currency.ParseISO(str) + + return Currency{Unit: cur}, err +} + +// MarshalText returns this instance into text. +func (u Currency) MarshalText() ([]byte, error) { + return []byte(u.Unit.String()), nil +} + +// UnmarshalText hydrates this instance from text. +func (u *Currency) UnmarshalText(data []byte) error { // validation is performed later on + cur, err := ParseCurrency(string(data)) + if err != nil { + return err + } + *u = cur + + return nil +} + +// Scan read a value from a database driver. +func (u *Currency) Scan(raw any) error { + switch v := raw.(type) { + case []byte: + cur, err := ParseCurrency(string(v)) + if err != nil { + return err + } + *u = cur + case string: + cur, err := ParseCurrency(v) + if err != nil { + return err + } + *u = cur + default: + return fmt.Errorf("cannot sql.Scan() strfmt.Currency from: %#v: %w", v, ErrFormat) + } + + return nil +} + +// Value converts a value to a database driver value. +func (u Currency) Value() (driver.Value, error) { + return driver.Value(u.String()), nil +} + +func (u Currency) String() string { + return u.Unit.String() +} + +// MarshalJSON returns the [Currency] as JSON. +func (u Currency) MarshalJSON() ([]byte, error) { + return json.Marshal(u.String()) +} + +// UnmarshalJSON sets the [Currency] from JSON. +func (u *Currency) UnmarshalJSON(data []byte) error { + if string(data) == jsonNull { + return nil + } + var ustr string + if err := json.Unmarshal(data, &ustr); err != nil { + return err + } + + cur, err := ParseCurrency(ustr) + if err != nil { + return err + } + + *u = cur + + return nil +} + +// DeepCopyInto copies the receiver and writes its value into out. +func (u *Currency) DeepCopyInto(out *Currency) { + *out = *u +} + +// DeepCopy copies the receiver into a new [Currency]. +func (u *Currency) DeepCopy() *Currency { + if u == nil { + return nil + } + + out := new(Currency) + u.DeepCopyInto(out) + + return out +} + +// GobEncode implements the gob.GobEncoder interface. +func (u Currency) GobEncode() ([]byte, error) { + return u.MarshalText() +} + +// GobDecode implements the gob.GobDecoder interface. +func (u *Currency) GobDecode(data []byte) error { + return u.UnmarshalText(data) +} + +// MarshalBinary implements the encoding.[encoding.BinaryMarshaler] interface. +func (u Currency) MarshalBinary() ([]byte, error) { + return u.MarshalText() +} + +// UnmarshalBinary implements the encoding.[encoding.BinaryUnmarshaler] interface. +func (u *Currency) UnmarshalBinary(data []byte) error { + return u.UnmarshalText(data) +} diff --git a/vendor/github.com/go-openapi/strfmt/date.go b/vendor/github.com/go-openapi/strfmt/date.go index 59ee1f1121..6d0e8a01ac 100644 --- a/vendor/github.com/go-openapi/strfmt/date.go +++ b/vendor/github.com/go-openapi/strfmt/date.go @@ -10,11 +10,6 @@ import ( "time" ) -func init() { //nolint:gochecknoinits // registers date format in the default registry - d := Date{} - Default.Add("date", &d, IsDate) -} - // IsDate returns true when the string is a valid date. func IsDate(str string) bool { _, err := time.Parse(RFC3339FullDate, str) diff --git a/vendor/github.com/go-openapi/strfmt/default.go b/vendor/github.com/go-openapi/strfmt/default.go index 87d3856ad2..bcc57e6fad 100644 --- a/vendor/github.com/go-openapi/strfmt/default.go +++ b/vendor/github.com/go-openapi/strfmt/default.go @@ -32,22 +32,22 @@ const ( ) const ( - // UUIDPattern Regex for [UUID] that allows uppercase + // UUIDPattern Regex for [UUID] that allows uppercase. // // Deprecated: [strfmt] no longer uses regular expressions to validate UUIDs. UUIDPattern = `(?i)(^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$)|(^[0-9a-f]{32}$)` - // UUID3Pattern Regex for [UUID3] that allows uppercase + // UUID3Pattern Regex for [UUID3] that allows uppercase. // // Deprecated: [strfmt] no longer uses regular expressions to validate UUIDs. UUID3Pattern = `(?i)(^[0-9a-f]{8}-[0-9a-f]{4}-3[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$)|(^[0-9a-f]{12}3[0-9a-f]{3}?[0-9a-f]{16}$)` - // UUID4Pattern Regex for [UUID4] that allows uppercase + // UUID4Pattern Regex for [UUID4] that allows uppercase. // // Deprecated: [strfmt] no longer uses regular expressions to validate UUIDs. UUID4Pattern = `(?i)(^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$)|(^[0-9a-f]{12}4[0-9a-f]{3}[89ab][0-9a-f]{15}$)` - // UUID5Pattern Regex for [UUID]5 that allows uppercase + // UUID5Pattern Regex for [UUID]5 that allows uppercase. // // Deprecated: [strfmt] no longer uses regular expressions to validate UUIDs. UUID5Pattern = `(?i)(^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$)|(^[0-9a-f]{12}5[0-9a-f]{3}[89ab][0-9a-f]{15}$)` @@ -88,8 +88,8 @@ var ( // IsHostname returns true when the string is a valid hostname. // -// It follows the rules detailed at https://url.spec.whatwg.org/#concept-host-parser -// and implemented by most modern web browsers. +// It follows the rules detailed at https://url.spec.whatwg.org/#concept-host-parser and implemented by most modern web +// browsers. // // It supports IDNA rules regarding internationalized names with unicode. // @@ -142,13 +142,13 @@ func IsHostname(str string) bool { return true } -// domainEndsAsNumber determines if a domain name ends with a decimal, octal or hex digit, -// accounting for a possible trailing dot (the last part being empty in that case). +// domainEndsAsNumber determines if a domain name ends with a decimal, octal or hex digit, accounting for a possible +// trailing dot (the last part being empty in that case). // // It returns the last non-trailing dot part and if that part consists only of (dec/hex/oct) digits. func domainEndsAsNumber(parts []string) (lastPart string, lastIndex int, ok bool) { - // NOTE: using ParseUint(x, 0, 32) is not an option, as the IPv4 format supported why WHATWG - // doesn't support notations such as "0b1001" (binary digits) or "0o666" (alternate notation for octal digits). + // NOTE: using ParseUint(x, 0, 32) is not an option, as the IPv4 format supported why WHATWG doesn't support notations + // such as "0b1001" (binary digits) or "0o666" (alternate notation for octal digits). lastIndex = len(parts) - 1 lastPart = parts[lastIndex] if len(lastPart) == 0 { @@ -233,8 +233,9 @@ func isValidIPv6(str string) bool { // "0o07.2.3.4" func isValidIPv4(parts []string) bool { // NOTE: using ParseUint(x, 0, 32) is not an option, even though it would simplify this code a lot. - // The IPv4 format supported why WHATWG doesn't support notations such as "0b1001" (binary digits) - // or "0o666" (alternate notation for octal digits). + // + // The IPv4 format supported why WHATWG doesn't support notations such as "0b1001" (binary digits) or "0o666" + // (alternate notation for octal digits). const ( maxPartsInIPv4 = 4 maxDigitsInPart = 11 // max size of a 4-bytes hex or octal digit @@ -376,101 +377,22 @@ func IsEmail(str string) bool { return e == nil && addr.Address != "" } -func init() { //nolint:gochecknoinits // registers all default string formats in the registry - // register formats in the default registry: - // - byte - // - creditcard - // - email - // - hexcolor - // - hostname - // - ipv4 - // - ipv6 - // - cidr - // - isbn - // - isbn10 - // - isbn13 - // - mac - // - password - // - rgbcolor - // - ssn - // - uri - // - uuid - // - uuid3 - // - uuid4 - // - uuid5 - // - uuid7 - u := URI("") - Default.Add("uri", &u, isRequestURI) - - eml := Email("") - Default.Add("email", &eml, IsEmail) - - hn := Hostname("") - Default.Add("hostname", &hn, IsHostname) - - ip4 := IPv4("") - Default.Add("ipv4", &ip4, isIPv4) - - ip6 := IPv6("") - Default.Add("ipv6", &ip6, isIPv6) - - cidr := CIDR("") - Default.Add("cidr", &cidr, isCIDR) - - mac := MAC("") - Default.Add("mac", &mac, isMAC) - - uid := UUID("") - Default.Add("uuid", &uid, IsUUID) - - uid3 := UUID3("") - Default.Add("uuid3", &uid3, IsUUID3) - - uid4 := UUID4("") - Default.Add("uuid4", &uid4, IsUUID4) - - uid5 := UUID5("") - Default.Add("uuid5", &uid5, IsUUID5) - - uid7 := UUID7("") - Default.Add("uuid7", &uid7, IsUUID7) - - isbn := ISBN("") - Default.Add("isbn", &isbn, func(str string) bool { return isISBN10(str) || isISBN13(str) }) - - isbn10 := ISBN10("") - Default.Add("isbn10", &isbn10, isISBN10) - - isbn13 := ISBN13("") - Default.Add("isbn13", &isbn13, isISBN13) - - cc := CreditCard("") - Default.Add("creditcard", &cc, isCreditCard) - - ssn := SSN("") - Default.Add("ssn", &ssn, isSSN) - - hc := HexColor("") - Default.Add("hexcolor", &hc, isHexcolor) - - rc := RGBColor("") - Default.Add("rgbcolor", &rc, isRGBcolor) - - b64 := Base64([]byte(nil)) - Default.Add("byte", &b64, isBase64) - - pw := Password("") - Default.Add("password", &pw, func(_ string) bool { return true }) -} +// base64Encoding is the canonical alphabet for the [Base64] format. +// +// OpenAPI `format: byte` means standard base64 (RFC 4648 §4, the `+/` alphabet), not base64url. +// This is the single seam every [Base64] path encodes and decodes through, so a future URL-safe variant only swaps the +// encoding here. +// See go-openapi/strfmt#87. +var base64Encoding = base64.StdEncoding //nolint:gochecknoglobals // canonical alphabet seam for the Base64 format -// Base64 represents a base64 encoded string, using URLEncoding alphabet. +// Base64 represents a base64 encoded string, using the standard RFC 4648 alphabet. // // swagger:strfmt byte. type Base64 []byte // MarshalText turns this instance into text. func (b Base64) MarshalText() ([]byte, error) { - enc := base64.URLEncoding + enc := base64Encoding src := []byte(b) buf := make([]byte, enc.EncodedLen(len(src))) enc.Encode(buf, src) @@ -479,7 +401,7 @@ func (b Base64) MarshalText() ([]byte, error) { // UnmarshalText hydrates this instance from text. func (b *Base64) UnmarshalText(data []byte) error { // validation is performed later on - enc := base64.URLEncoding + enc := base64Encoding dbuf := make([]byte, enc.DecodedLen(len(data))) n, err := enc.Decode(dbuf, data) @@ -495,14 +417,14 @@ func (b *Base64) UnmarshalText(data []byte) error { // validation is performed l func (b *Base64) Scan(raw any) error { switch v := raw.(type) { case []byte: - dbuf := make([]byte, base64.StdEncoding.DecodedLen(len(v))) - n, err := base64.StdEncoding.Decode(dbuf, v) + dbuf := make([]byte, base64Encoding.DecodedLen(len(v))) + n, err := base64Encoding.Decode(dbuf, v) if err != nil { return err } *b = dbuf[:n] case string: - vv, err := base64.StdEncoding.DecodeString(v) + vv, err := base64Encoding.DecodeString(v) if err != nil { return err } @@ -520,7 +442,7 @@ func (b Base64) Value() (driver.Value, error) { } func (b Base64) String() string { - return base64.StdEncoding.EncodeToString([]byte(b)) + return base64Encoding.EncodeToString([]byte(b)) } // MarshalJSON returns the Base64 as JSON. @@ -534,7 +456,7 @@ func (b *Base64) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, &b64str); err != nil { return err } - vb, err := base64.StdEncoding.DecodeString(b64str) + vb, err := base64Encoding.DecodeString(b64str) if err != nil { return err } @@ -1040,7 +962,7 @@ func (u *MAC) DeepCopy() *MAC { return out } -// UUID represents a [uuid] string format +// UUID represents a [uuid] string format. // // swagger:strfmt uuid. type UUID string @@ -1905,6 +1827,7 @@ func (r *RGBColor) DeepCopy() *RGBColor { } // Password represents a password. +// // This has no validations and is mainly used as a marker for UI components. // // swagger:strfmt password. @@ -1978,6 +1901,13 @@ func (r *Password) DeepCopy() *Password { } func isRequestURI(rawurl string) bool { + // url.ParseRequestURI assumes the input contains no "#fragment" (RFC 3986 §3.5). + // A URI with a fragment and an empty path, such as "https://host#@frag", is therefore misread as userinfo and rejected + // as "invalid userinfo". + // Strip the fragment first so the absolute request URI validates, matching url.Parse's RFC 3986 handling. + if i := strings.IndexByte(rawurl, '#'); i >= 0 { + rawurl = rawurl[:i] + } _, err := url.ParseRequestURI(rawurl) return err == nil } @@ -2001,19 +1931,20 @@ func isCIDR(str string) bool { } // isMAC checks if a string is valid MAC address. +// // Possible MAC formats: -// 01:23:45:67:89:ab -// 01:23:45:67:89:ab:cd:ef -// 01-23-45-67-89-ab -// 01-23-45-67-89-ab-cd-ef -// 0123.4567.89ab -// 0123.4567.89ab.cdef. +// - 01:23:45:67:89:ab +// - 01:23:45:67:89:ab:cd:ef +// - 01-23-45-67-89-ab +// - 01-23-45-67-89-ab-cd-ef +// - 0123.4567.89ab 0123.4567.89ab.cdef func isMAC(str string) bool { _, err := net.ParseMAC(str) return err == nil } // isISBN checks if the string is an ISBN (version 10 or 13). +// // If version value is not equal to 10 or 13, it will be checks both variants. func isISBN(str string, version int) bool { sanitized := whiteSpacesAndMinus.ReplaceAllString(str, "") @@ -2112,7 +2043,7 @@ func isRGBcolor(str string) bool { // isBase64 checks if a string is base64 encoded. func isBase64(str string) bool { - _, err := base64.StdEncoding.DecodeString(str) + _, err := base64Encoding.DecodeString(str) return err == nil } diff --git a/vendor/github.com/go-openapi/strfmt/duration.go b/vendor/github.com/go-openapi/strfmt/duration.go index f2ab7ff834..fc4de0d14c 100644 --- a/vendor/github.com/go-openapi/strfmt/duration.go +++ b/vendor/github.com/go-openapi/strfmt/duration.go @@ -12,11 +12,6 @@ import ( "unicode" ) -func init() { //nolint:gochecknoinits // registers duration format in the default registry - d := Duration(0) - Default.Add("duration", &d, IsDuration) -} - const ( hoursInDay = 24 daysInWeek = 7 @@ -174,6 +169,9 @@ func ParseDuration(s string) (time.Duration, error) { scale float64 = 1 // value = v + f/scale ) s = strings.TrimLeftFunc(s, unicode.IsSpace) + if s == "" { + break + } // The next character must be 0-9.] if s[0] != '.' && ('0' > s[0] || s[0] > '9') { diff --git a/vendor/github.com/go-openapi/strfmt/duration_iso8601.go b/vendor/github.com/go-openapi/strfmt/duration_iso8601.go new file mode 100644 index 0000000000..9fded035ae --- /dev/null +++ b/vendor/github.com/go-openapi/strfmt/duration_iso8601.go @@ -0,0 +1,583 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package strfmt + +import ( + "database/sql/driver" + "encoding/json" + "fmt" + "math" + "strconv" + "strings" + "time" +) + +// ISO 8601 duration unit lengths, expressed in nanoseconds. +// +// Calendar units are collapsed to fixed lengths (a year is 365 days, a month is 30 days): a [time.Duration] cannot +// carry calendar context, so this conversion is inherently lossy and not calendar-correct. +// It matches the historical behaviour of ISO duration libraries in this space. +const ( + isoSecond = uint64(time.Second) + isoMinute = uint64(time.Minute) + isoHour = uint64(time.Hour) + isoDay = 24 * isoHour + isoWeek = 7 * isoDay + isoMonth = 30 * isoDay + isoYear = 365 * isoDay + + // maxDurationMagnitude is 1<<63: the magnitude of [math.MinInt64], and one more than [math.MaxInt64]. + // + // A parsed magnitude may reach exactly this value only for a negative duration (yielding [math.MinInt64]). + maxDurationMagnitude = uint64(1) << 63 +) + +// Base-10 parsing / formatting constants (decimalBase lives in default.go). +const ( + // decimalAccMax bounds a uint64 accumulator so that acc*decimalBase + 9 cannot overflow. + decimalAccMax = (math.MaxUint64 - (decimalBase - 1)) / decimalBase + // nanoDigits is the number of fractional digits in one whole second (nanosecond precision). + nanoDigits = 9 +) + +// ISODuration is an ISO 8601 / RFC 3339 duration (e.g. "P1Y2M3DT4H5M6S", "P2W"). +// +// The type parameter P selects the parsing policy at compile time (see [ISODurationPolicy]). +// Every instantiation is a distinct type that round-trips through JSON, text, SQL and BSON. +// Use the [DurationISO8601] alias for the strict, spec-compliant default. +// +// Like [time.Duration], it stores a nanosecond count; the largest representable duration is approximately 290 years. +// Calendar units are collapsed to fixed lengths (year = 365 days, month = 30 days), which is lossy by nature. +// +// This generic type carries no swagger:strfmt annotation: go-swagger binds a concrete format name to the +// [DurationISO8601] alias, not to a generic declaration. +type ISODuration[P ISODurationPolicy] time.Duration + +// DurationISO8601 is the strict, spec-compliant ISO 8601 duration: the JSON Schema draft 2020 "duration" format +// (RFC 3339 Appendix A). +// +// It binds to the explicit, unambiguous "duration-iso8601" handle. Plain "duration" is a context-dependent +// default resolved by the registry ([Default] → human, [JSONSchema2020Registry] → ISO), not by this static type. +// +// swagger:strfmt duration-iso8601. +type DurationISO8601 = ISODuration[DurationStrict] + +// ParseISO8601Duration parses an ISO 8601 / RFC 3339 duration string. +// +// With no options it enforces the strict RFC 3339 Appendix A grammar. +// Options relax individual rules for programmatic callers. +func ParseISO8601Duration(s string, opts ...ISODurationOption) (time.Duration, error) { + cfg := DurationStrict{}.isoDurationConfig() + for _, o := range opts { + o(&cfg) + } + return parseISO8601Duration(s, cfg) +} + +// --- Format / encoding methods --- + +// String renders the duration in canonical ISO 8601 form. +// +// Unlike [ISODuration.MarshalText], String is a best-effort display form: it always renders the true value (lossless), +// even under a strict policy that could not serialize it (e.g. a sign or sub-second precision). +func (d ISODuration[P]) String() string { return isoFormat(time.Duration(d)) } + +// MarshalText implements [encoding.TextMarshaler], applying the policy P: a strict policy errors on a value it cannot +// represent (see [isoEmit]). +func (d ISODuration[P]) MarshalText() ([]byte, error) { + var p P + s, err := isoEmit(time.Duration(d), p.isoDurationConfig()) + if err != nil { + return nil, err + } + return []byte(s), nil +} + +// UnmarshalText implements [encoding.TextUnmarshaler], applying the policy P. +func (d *ISODuration[P]) UnmarshalText(text []byte) error { + var p P + dd, err := parseISO8601Duration(string(text), p.isoDurationConfig()) + if err != nil { + return err + } + *d = ISODuration[P](dd) + return nil +} + +// MarshalJSON returns the duration as a JSON string, applying the policy P. +func (d ISODuration[P]) MarshalJSON() ([]byte, error) { + var p P + s, err := isoEmit(time.Duration(d), p.isoDurationConfig()) + if err != nil { + return nil, err + } + return json.Marshal(s) +} + +// UnmarshalJSON sets the duration from a JSON string, applying the policy P. +func (d *ISODuration[P]) UnmarshalJSON(data []byte) error { + if string(data) == jsonNull { + return nil + } + var str string + if err := json.Unmarshal(data, &str); err != nil { + return err + } + var p P + dd, err := parseISO8601Duration(str, p.isoDurationConfig()) + if err != nil { + return err + } + *d = ISODuration[P](dd) + return nil +} + +// Scan reads a duration (nanoseconds) from a database driver value. +func (d *ISODuration[P]) Scan(raw any) error { + switch v := raw.(type) { + case int64: + *d = ISODuration[P](v) + case float64: + *d = ISODuration[P](int64(v)) + case nil: + *d = ISODuration[P](0) + default: + return fmt.Errorf("cannot sql.Scan() strfmt.ISODuration from: %#v: %w", v, ErrFormat) + } + return nil +} + +// Value writes the duration as a nanosecond count. +func (d ISODuration[P]) Value() (driver.Value, error) { + return driver.Value(int64(d)), nil +} + +// Equal reports whether two durations are equal. +func (d ISODuration[P]) Equal(other ISODuration[P]) bool { return d == other } + +// DeepCopyInto copies the receiver into out. +func (d *ISODuration[P]) DeepCopyInto(out *ISODuration[P]) { *out = *d } + +// DeepCopy copies the receiver into a new value. +func (d *ISODuration[P]) DeepCopy() *ISODuration[P] { + if d == nil { + return nil + } + out := new(ISODuration[P]) + d.DeepCopyInto(out) + return out +} + +// IsDurationISO8601 returns true if the string is a valid strict ISO 8601 duration. +func IsDurationISO8601(s string) bool { + _, err := parseISO8601Duration(s, DurationStrict{}.isoDurationConfig()) + return err == nil +} + +func isoDurationError(input, msg string) error { + return fmt.Errorf("invalid ISO 8601 duration %q: %s: %w", input, msg, ErrFormat) +} + +// section positions used to enforce component ordering (strictly increasing). +// +//nolint:gochecknoglobals,mnd // immutable ordinal lookup tables; the integers are sequence positions, not magic constants +var ( + isoDatePos = map[byte]int{'Y': 1, 'M': 2, 'W': 3, 'D': 4} + isoTimePos = map[byte]int{'H': 1, 'M': 2, 'S': 3} +) + +// isoDateSlot maps the year/month/day designators to contiguous slots for the strict anchoring (gap) check. +// +// 'W' is deliberately excluded: in strict mode it is exclusive, so it never coexists with Y/M/D. +func isoDateSlot(des byte) (int, bool) { + switch des { + case 'Y': + return 0, true + case 'M': + return 1, true + case 'D': + return 2, true //nolint:mnd // contiguous slot index (Y=0, M=1, D=2), not a magic constant + default: + return 0, false + } +} + +//nolint:gocognit,gocyclo,cyclop // a single-pass grammar scanner; the branches mirror the ABNF. +func parseISO8601Duration(input string, cfg isoDurationConfig) (time.Duration, error) { + s := input + if cfg.allowSpace { + s = strings.TrimSpace(s) + } + + neg := false + if cfg.allowSign && s != "" && (s[0] == '+' || s[0] == '-') { + neg = s[0] == '-' + s = s[1:] + } + + if s == "" || s[0] != 'P' { + return 0, isoDurationError(input, `must start with "P"`) + } + s = s[1:] + if s == "" { + return 0, isoDurationError(input, "no components after P") + } + + var ( + total uint64 + inTime bool + seenAny bool + weekSeen bool + fractionUsed bool + lastDatePos int + lastTimePos int + datePresent [3]bool // Y, M, D — for the anchoring (gap) check + timePresent [3]bool // H, M, S + ) + + for s != "" { + c := s[0] + + if c == 'T' { + if inTime { + return 0, isoDurationError(input, `duplicate "T" separator`) + } + if weekSeen && !cfg.weekCombinable { + return 0, isoDurationError(input, `"W" cannot be combined with other components`) + } + inTime = true + s = s[1:] + if s == "" { + return 0, isoDurationError(input, `no components after "T"`) + } + continue + } + + // A fraction, if any, must be on the least significant component: nothing may follow it. + if fractionUsed { + return 0, isoDurationError(input, "a fraction is only allowed on the least significant component") + } + + // Scan the integer part (ASCII digits only). + i := 0 + for i < len(s) && s[i] >= '0' && s[i] <= '9' { + i++ + } + if i == 0 { + return 0, isoDurationError(input, fmt.Sprintf("expected a digit, got %q", s[0])) + } + intPart := s[:i] + + // Optional decimal fraction. + var fracPart string + hasFrac := false + if i < len(s) && (s[i] == '.' || s[i] == ',') { + if !cfg.allowFraction { + return 0, isoDurationError(input, "decimal fraction is not allowed") + } + hasFrac = true + j := i + 1 + for j < len(s) && s[j] >= '0' && s[j] <= '9' { + j++ + } + fracPart = s[i+1 : j] + i = j + } + + if i >= len(s) { + return 0, isoDurationError(input, "value without a unit designator") + } + des := s[i] + s = s[i+1:] + + unit, err := isoResolveUnit(input, des, inTime, cfg, + &lastDatePos, &lastTimePos, &weekSeen, seenAny, &datePresent, &timePresent) + if err != nil { + return 0, err + } + seenAny = true + if hasFrac { + fractionUsed = true + } + + val, err := isoScaleValue(input, intPart, fracPart, unit) + if err != nil { + return 0, err + } + + // Accumulate with the stdlib overflow discipline: the magnitude may reach 1<<63 only for a negative duration. + if total > maxDurationMagnitude-val { + return 0, isoDurationError(input, "value out of range") + } + total += val + } + + if inTime && !timePresent[0] && !timePresent[1] && !timePresent[2] { + return 0, isoDurationError(input, `no components after "T"`) + } + if !seenAny { + return 0, isoDurationError(input, "no components") + } + + // Strict anchoring: the present Y/M/D and H/M/S components must each form a contiguous run (e.g. P1Y2D and PT1H2S are + // rejected). + if !cfg.relaxAnchoring { + if err := isoCheckContiguous(input, datePresent, "date"); err != nil { + return 0, err + } + if err := isoCheckContiguous(input, timePresent, "time"); err != nil { + return 0, err + } + } + + if total > maxDurationMagnitude-1 { + if neg && total == maxDurationMagnitude { + return math.MinInt64, nil + } + return 0, isoDurationError(input, "value out of range") + } + if neg { + return -time.Duration(total), nil + } + return time.Duration(total), nil +} + +// isoResolveUnit validates a designator in context (section, ordering, week +// exclusivity) and returns its unit length in nanoseconds. +func isoResolveUnit( + input string, des byte, inTime bool, cfg isoDurationConfig, + lastDatePos, lastTimePos *int, weekSeen *bool, seenAny bool, + datePresent, timePresent *[3]bool, +) (uint64, error) { + if inTime { + pos, ok := isoTimePos[des] + if !ok { + return 0, isoDurationError(input, fmt.Sprintf("%q is not a valid time-section designator", des)) + } + if pos <= *lastTimePos { + return 0, isoDurationError(input, fmt.Sprintf("designator %q is out of order", des)) + } + *lastTimePos = pos + timePresent[pos-1] = true + switch des { + case 'H': + return isoHour, nil + case 'M': + return isoMinute, nil + default: // 'S' + return isoSecond, nil + } + } + + pos, ok := isoDatePos[des] + if !ok { + return 0, isoDurationError(input, fmt.Sprintf("%q is not a valid date-section designator", des)) + } + if pos <= *lastDatePos { + return 0, isoDurationError(input, fmt.Sprintf("designator %q is out of order", des)) + } + + if des == 'W' { + if !cfg.weekCombinable && seenAny { + return 0, isoDurationError(input, `"W" cannot be combined with other components`) + } + *lastDatePos = pos + *weekSeen = true + return isoWeek, nil + } + + if *weekSeen && !cfg.weekCombinable { + return 0, isoDurationError(input, `"W" cannot be combined with other components`) + } + *lastDatePos = pos + if slot, ok := isoDateSlot(des); ok { + datePresent[slot] = true + } + switch des { + case 'Y': + return isoYear, nil + case 'M': + return isoMonth, nil + default: // 'D' + return isoDay, nil + } +} + +// isoCheckContiguous rejects gaps in a section's component chain. +func isoCheckContiguous(input string, present [3]bool, section string) error { + first, last := -1, -1 + for i, p := range present { + if p { + if first < 0 { + first = i + } + last = i + } + } + if first < 0 { + return nil + } + for i := first; i <= last; i++ { + if !present[i] { + return isoDurationError(input, "non-contiguous "+section+" components (a gap in the unit chain)") + } + } + return nil +} + +// isoScaleValue converts an integer (and optional fractional) component to nanoseconds, checking every overflow +// boundary. +func isoScaleValue(input, intPart, fracPart string, unit uint64) (uint64, error) { + v, ok := isoParseUint(intPart) + if !ok { + return 0, isoDurationError(input, "value out of range") + } + // Bound v*unit to the duration magnitude *before* multiplying, so the later fractional addition cannot wrap a uint64. + if v > maxDurationMagnitude/unit { + return 0, isoDurationError(input, "value out of range") + } + val := v * unit + + if fracPart != "" { + f, scale := isoParseFraction(fracPart) + if f > 0 { + // float64 is accurate enough for a sub-unit fraction (matches the standard library technique); the magnitude stays + // below the uint64 ceiling because val <= 1<<63 and the addend is < unit. + val += uint64(float64(f) * (float64(unit) / float64(scale))) + if val > maxDurationMagnitude { + return 0, isoDurationError(input, "value out of range") + } + } + } + + return val, nil +} + +// isoParseUint parses digits into a uint64, reporting overflow. +func isoParseUint(s string) (uint64, bool) { + var v uint64 + for i := range len(s) { + if v > decimalAccMax { + return 0, false + } + v = v*decimalBase + uint64(s[i]-'0') + } + + return v, true +} + +// isoParseFraction parses fractional digits into (value, scale=10^len), capping precision at 18 digits so the value +// stays exact in the subsequent float maths. +func isoParseFraction(s string) (uint64, uint64) { + const maxFracDigits = 18 + if len(s) > maxFracDigits { + s = s[:maxFracDigits] + } + var f, scale uint64 = 0, 1 + for i := range len(s) { + f = f*decimalBase + uint64(s[i]-'0') + scale *= decimalBase + } + + return f, scale +} + +// isoFormat renders a duration in canonical ISO 8601 form (P[n]DT[n]H[n]M[n]S). +// +// Intermediate zero time components are emitted when needed to keep the output contiguous (e.g. 1h5s → "PT1H0M5S"), +// so the structure is always syntactically valid ISO 8601. Years, months and weeks are not reconstructed (a +// [time.Duration] does not carry calendar context); a fractional second is emitted losslessly, and a negative duration +// is prefixed with "-". +func isoFormat(d time.Duration) string { + if d == 0 { + return "PT0S" + } + neg := d < 0 + var u uint64 + if neg { + u = uint64(-d) + } else { + u = uint64(d) + } + + days := u / isoDay + u %= isoDay + hours := u / isoHour + u %= isoHour + minutes := u / isoMinute + u %= isoMinute + seconds := u / isoSecond + frac := u % isoSecond + + var b strings.Builder + if neg { + b.WriteByte('-') + } + + b.WriteByte('P') + if days > 0 { + b.WriteString(strconv.FormatUint(days, decimalBase)) + b.WriteByte('D') + } + isoWriteTimeSection(&b, hours, minutes, seconds, frac) + + return b.String() +} + +// isoWriteTimeSection appends the "T…" part of a canonical ISO 8601 duration, emitting a zero-minute filler when it +// must bridge hours and seconds so the output stays contiguous (e.g. 1h5s → "T1H0M5S"). +func isoWriteTimeSection(b *strings.Builder, hours, minutes, seconds, frac uint64) { + hasH := hours > 0 + hasM := minutes > 0 + hasS := seconds > 0 || frac > 0 + if !hasH && !hasM && !hasS { + return + } + + b.WriteByte('T') + if hasH { + b.WriteString(strconv.FormatUint(hours, decimalBase)) + b.WriteByte('H') + } + + // Emit minutes if non-zero, or as a zero filler bridging hours and seconds. + if hasM || (hasH && hasS) { + b.WriteString(strconv.FormatUint(minutes, decimalBase)) + b.WriteByte('M') + } + + if hasS { + b.WriteString(strconv.FormatUint(seconds, decimalBase)) + if frac > 0 { + b.WriteByte('.') + b.WriteString(isoFormatFraction(frac)) + } + b.WriteByte('S') + } +} + +// isoEmit renders d under the given policy. +// +// A policy that forbids a feature the value requires (a sign, or sub-second precision) cannot represent it, and returns +// an error rather than emitting output a matching parser would reject. +func isoEmit(d time.Duration, cfg isoDurationConfig) (string, error) { + if !cfg.allowSign && d < 0 { + return "", fmt.Errorf("a negative duration cannot be represented under this ISO 8601 policy: %w", ErrFormat) + } + + if !cfg.allowFraction && d%time.Second != 0 { + return "", fmt.Errorf("sub-second precision cannot be represented under this ISO 8601 policy: %w", ErrFormat) + } + + return isoFormat(d), nil +} + +// isoFormatFraction renders a nanosecond remainder (0..1e9) as its 9-digit fractional part with trailing zeros trimmed. +func isoFormatFraction(frac uint64) string { + s := strconv.FormatUint(frac, decimalBase) + if len(s) < nanoDigits { + s = strings.Repeat("0", nanoDigits-len(s)) + s + } + return strings.TrimRight(s, "0") +} diff --git a/vendor/github.com/go-openapi/strfmt/duration_iso8601_options.go b/vendor/github.com/go-openapi/strfmt/duration_iso8601_options.go new file mode 100644 index 0000000000..4f90a5c5a5 --- /dev/null +++ b/vendor/github.com/go-openapi/strfmt/duration_iso8601_options.go @@ -0,0 +1,80 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package strfmt + +// ISODurationPolicy is a compile-time policy selecting the parsing behaviour of an [ISODuration]. +// +// It is a phantom type parameter: a zero-size value whose single method yields the parser configuration. +// See [DurationStrict] and [DurationLenient]. +type ISODurationPolicy interface { + isoDurationConfig() isoDurationConfig +} + +// isoDurationConfig holds the leniency knobs of the ISO 8601 duration parser. +// +// The zero value is the strict RFC 3339 Appendix A grammar (the JSON Schema "duration" format): every field false. +type isoDurationConfig struct { + allowFraction bool // accept a decimal fraction on the least significant component + allowSign bool // accept a leading '+' or '-' + allowSpace bool // tolerate surrounding whitespace + weekCombinable bool // allow the 'W' designator to combine with other components + relaxAnchoring bool // allow gaps in the component chain (e.g. P1Y2D, PT1H2S) +} + +// DurationStrict is the strict RFC 3339 Appendix A policy. +// +// This is the grammar that the JSON Schema "duration" format is defined against (draft 2020): +// no fraction, no sign, no whitespace, strict ordering and anchoring, and an exclusive 'W' designator. +type DurationStrict struct{} + +func (DurationStrict) isoDurationConfig() isoDurationConfig { return isoDurationConfig{} } + +// DurationLenient relaxes every strictness knob. +// +// It accepts a decimal fraction on the least significant component, a leading sign, surrounding whitespace, +// component chains with gaps (e.g. P1Y2D), and a 'W' designator combined with other components. +// +// It remains ordered (P2D1Y is still rejected). +type DurationLenient struct{} + +func (DurationLenient) isoDurationConfig() isoDurationConfig { + return isoDurationConfig{ + allowFraction: true, + allowSign: true, + allowSpace: true, + weekCombinable: true, + relaxAnchoring: true, + } +} + +// ISODurationOption relaxes the strict parser on the explicit [ParseISO8601Duration] path, for programmatic callers. +// +// It has no effect on the registry / struct-field decode path, which is governed by the type's policy. +type ISODurationOption func(*isoDurationConfig) + +// WithISOFractions accepts a decimal fraction on the least significant component. +func WithISOFractions() ISODurationOption { + return func(c *isoDurationConfig) { c.allowFraction = true } +} + +// WithISOSign accepts a leading '+' or '-'. +func WithISOSign() ISODurationOption { return func(c *isoDurationConfig) { c.allowSign = true } } + +// WithISOSpace tolerates surrounding whitespace. +func WithISOSpace() ISODurationOption { return func(c *isoDurationConfig) { c.allowSpace = true } } + +// WithISOWeekCombinable allows the 'W' designator to combine with other components. +func WithISOWeekCombinable() ISODurationOption { + return func(c *isoDurationConfig) { c.weekCombinable = true } +} + +// WithISORelaxedAnchoring allows gaps in the component chain (e.g. P1Y2D, PT1H2S). +func WithISORelaxedAnchoring() ISODurationOption { + return func(c *isoDurationConfig) { c.relaxAnchoring = true } +} + +// WithISOLenient relaxes every strictness knob (see [DurationLenient]). +func WithISOLenient() ISODurationOption { + return func(c *isoDurationConfig) { *c = DurationLenient{}.isoDurationConfig() } +} diff --git a/vendor/github.com/go-openapi/strfmt/format.go b/vendor/github.com/go-openapi/strfmt/format.go index e494dd7b83..09c3754254 100644 --- a/vendor/github.com/go-openapi/strfmt/format.go +++ b/vendor/github.com/go-openapi/strfmt/format.go @@ -10,15 +10,11 @@ import ( "slices" "strings" "sync" - "time" "github.com/go-openapi/errors" "github.com/go-viper/mapstructure/v2" ) -// Default is the default formats registry. -var Default = NewSeededFormats(nil, nil) //nolint:gochecknoglobals // package-level default registry, by design - // Validator represents a validator for a string format. type Validator func(string) bool @@ -48,11 +44,28 @@ type knownFormat struct { } // NameNormalizer is a function that normalizes a format name. +// +// The default duration format corresponds to "duration-human". type NameNormalizer func(string) string +var dashReplacer = strings.NewReplacer("-", "") //nolint:gochecknoglobals // it's okay to use a global private replacer + // DefaultNameNormalizer removes all dashes. func DefaultNameNormalizer(name string) string { - return strings.ReplaceAll(name, "-", "") + if name == "duration" { + name = "duration-human" + } + + return dashReplacer.Replace(name) +} + +// JSONSchema2020Normalizer is like [NameNormalizer] but adopts "duration-iso8601" as the default "duration" format. +func JSONSchema2020Normalizer(name string) string { + if name == "duration" { + name = "duration-iso8601" + } + + return dashReplacer.Replace(name) } type defaultFormats struct { @@ -63,6 +76,11 @@ type defaultFormats struct { } // MapStructureHookFunc is a decode hook function for mapstructure. +// +// A registered format is decoded by delegating to the destination type's own +// [encoding.TextUnmarshaler], so the mapstructure path is identical to the JSON +// and [defaultFormats.Parse] paths. New formats are picked up automatically as +// soon as they are registered — no per-type wiring here. func (f *defaultFormats) MapStructureHookFunc() mapstructure.DecodeHookFunc { //nolint:ireturn // returns interface required by mapstructure return func(from reflect.Type, to reflect.Type, obj any) (any, error) { if from.Kind() != reflect.String { @@ -74,85 +92,25 @@ func (f *defaultFormats) MapStructureHookFunc() mapstructure.DecodeHookFunc { // } for _, v := range f.data { - tpe, _ := f.GetType(v.Name) - if to == tpe { - return decodeFormatFromString(v.Name, data) + if to != v.Type { + continue } - } - return data, nil - } -} -// decodeFormatFromString decodes a string into the appropriate format type by name. -func decodeFormatFromString(name, data string) (any, error) { //nolint:gocyclo,cyclop // flat switch over format names, no real complexity - switch name { - case "date": - d, err := time.ParseInLocation(RFC3339FullDate, data, DefaultTimeLocation) - if err != nil { - return nil, err - } - return Date(d), nil - case "datetime": - if len(data) == 0 { - return nil, fmt.Errorf("empty string is an invalid datetime format: %w", ErrFormat) - } - return ParseDateTime(data) - case "duration": - dur, err := ParseDuration(data) - if err != nil { - return nil, err - } - return Duration(dur), nil - case "uri": - return URI(data), nil - case "email": - return Email(data), nil - case "uuid": - return UUID(data), nil - case "uuid3": - return UUID3(data), nil - case "uuid4": - return UUID4(data), nil - case "uuid5": - return UUID5(data), nil - case "uuid7": - return UUID7(data), nil - case "hostname": - return Hostname(data), nil - case "ipv4": - return IPv4(data), nil - case "ipv6": - return IPv6(data), nil - case "cidr": - return CIDR(data), nil - case "mac": - return MAC(data), nil - case "isbn": - return ISBN(data), nil - case "isbn10": - return ISBN10(data), nil - case "isbn13": - return ISBN13(data), nil - case "creditcard": - return CreditCard(data), nil - case "ssn": - return SSN(data), nil - case "hexcolor": - return HexColor(data), nil - case "rgbcolor": - return RGBColor(data), nil - case "byte": - return Base64(data), nil - case "password": - return Password(data), nil - case "ulid": - ulid, err := ParseULID(data) - if err != nil { - return nil, err + // reflect.New yields an addressable *T so the (pointer-receiver) + // TextUnmarshaler can hydrate it; we hand back the dereferenced + // value to match the destination field's (value) type. + nw := reflect.New(v.Type).Interface() + dec, isText := nw.(encoding.TextUnmarshaler) + if !isText { + return nil, errors.InvalidTypeName(v.Name) + } + if err := dec.UnmarshalText([]byte(data)); err != nil { + return nil, err + } + return reflect.ValueOf(nw).Elem().Interface(), nil } - return ulid, nil - default: - return nil, errors.InvalidTypeName(name) + + return data, nil } } diff --git a/vendor/github.com/go-openapi/strfmt/internal/countries/countries.go b/vendor/github.com/go-openapi/strfmt/internal/countries/countries.go new file mode 100644 index 0000000000..7f13c0cfe3 --- /dev/null +++ b/vendor/github.com/go-openapi/strfmt/internal/countries/countries.go @@ -0,0 +1,512 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +// Code generated by gen.go; DO NOT EDIT. + +package countries + +// CountriesISO3 indexes ISO3166 countries by their alpha-3 code. +var CountriesISO3 = map[string]Country{ + "ABW": {Name: "Aruba", ISOAlpha3: "ABW", ISOAlpha2: "AW", Code: "533"}, + "AFG": {Name: "Afghanistan", ISOAlpha3: "AFG", ISOAlpha2: "AF", Code: "004"}, + "AGO": {Name: "Angola", ISOAlpha3: "AGO", ISOAlpha2: "AO", Code: "024"}, + "AIA": {Name: "Anguilla", ISOAlpha3: "AIA", ISOAlpha2: "AI", Code: "660"}, + "ALA": {Name: "Åland Islands", ISOAlpha3: "ALA", ISOAlpha2: "AX", Code: "248"}, + "ALB": {Name: "Albania", ISOAlpha3: "ALB", ISOAlpha2: "AL", Code: "008"}, + "AND": {Name: "Andorra", ISOAlpha3: "AND", ISOAlpha2: "AD", Code: "020"}, + "ARE": {Name: "United Arab Emirates", ISOAlpha3: "ARE", ISOAlpha2: "AE", Code: "784"}, + "ARG": {Name: "Argentina", ISOAlpha3: "ARG", ISOAlpha2: "AR", Code: "032"}, + "ARM": {Name: "Armenia", ISOAlpha3: "ARM", ISOAlpha2: "AM", Code: "051"}, + "ASM": {Name: "American Samoa", ISOAlpha3: "ASM", ISOAlpha2: "AS", Code: "016"}, + "ATA": {Name: "Antarctica", ISOAlpha3: "ATA", ISOAlpha2: "AQ", Code: "010"}, + "ATF": {Name: "French Southern Territories", ISOAlpha3: "ATF", ISOAlpha2: "TF", Code: "260"}, + "ATG": {Name: "Antigua and Barbuda", ISOAlpha3: "ATG", ISOAlpha2: "AG", Code: "028"}, + "AUS": {Name: "Australia", ISOAlpha3: "AUS", ISOAlpha2: "AU", Code: "036"}, + "AUT": {Name: "Austria", ISOAlpha3: "AUT", ISOAlpha2: "AT", Code: "040"}, + "AZE": {Name: "Azerbaijan", ISOAlpha3: "AZE", ISOAlpha2: "AZ", Code: "031"}, + "BDI": {Name: "Burundi", ISOAlpha3: "BDI", ISOAlpha2: "BI", Code: "108"}, + "BEL": {Name: "Belgium", ISOAlpha3: "BEL", ISOAlpha2: "BE", Code: "056"}, + "BEN": {Name: "Benin", ISOAlpha3: "BEN", ISOAlpha2: "BJ", Code: "204"}, + "BES": {Name: "Bonaire, Sint Eustatius and Saba", ISOAlpha3: "BES", ISOAlpha2: "BQ", Code: "535"}, + "BFA": {Name: "Burkina Faso", ISOAlpha3: "BFA", ISOAlpha2: "BF", Code: "854"}, + "BGD": {Name: "Bangladesh", ISOAlpha3: "BGD", ISOAlpha2: "BD", Code: "050"}, + "BGR": {Name: "Bulgaria", ISOAlpha3: "BGR", ISOAlpha2: "BG", Code: "100"}, + "BHR": {Name: "Bahrain", ISOAlpha3: "BHR", ISOAlpha2: "BH", Code: "048"}, + "BHS": {Name: "Bahamas", ISOAlpha3: "BHS", ISOAlpha2: "BS", Code: "044"}, + "BIH": {Name: "Bosnia and Herzegovina", ISOAlpha3: "BIH", ISOAlpha2: "BA", Code: "070"}, + "BLM": {Name: "Saint Barthélemy", ISOAlpha3: "BLM", ISOAlpha2: "BL", Code: "652"}, + "BLR": {Name: "Belarus", ISOAlpha3: "BLR", ISOAlpha2: "BY", Code: "112"}, + "BLZ": {Name: "Belize", ISOAlpha3: "BLZ", ISOAlpha2: "BZ", Code: "084"}, + "BMU": {Name: "Bermuda", ISOAlpha3: "BMU", ISOAlpha2: "BM", Code: "060"}, + "BOL": {Name: "Bolivia (Plurinational State of)", ISOAlpha3: "BOL", ISOAlpha2: "BO", Code: "068"}, + "BRA": {Name: "Brazil", ISOAlpha3: "BRA", ISOAlpha2: "BR", Code: "076"}, + "BRB": {Name: "Barbados", ISOAlpha3: "BRB", ISOAlpha2: "BB", Code: "052"}, + "BRN": {Name: "Brunei Darussalam", ISOAlpha3: "BRN", ISOAlpha2: "BN", Code: "096"}, + "BTN": {Name: "Bhutan", ISOAlpha3: "BTN", ISOAlpha2: "BT", Code: "064"}, + "BVT": {Name: "Bouvet Island", ISOAlpha3: "BVT", ISOAlpha2: "BV", Code: "074"}, + "BWA": {Name: "Botswana", ISOAlpha3: "BWA", ISOAlpha2: "BW", Code: "072"}, + "CAF": {Name: "Central African Republic", ISOAlpha3: "CAF", ISOAlpha2: "CF", Code: "140"}, + "CAN": {Name: "Canada", ISOAlpha3: "CAN", ISOAlpha2: "CA", Code: "124"}, + "CCK": {Name: "Cocos (Keeling) Islands", ISOAlpha3: "CCK", ISOAlpha2: "CC", Code: "166"}, + "CHE": {Name: "Switzerland", ISOAlpha3: "CHE", ISOAlpha2: "CH", Code: "756"}, + "CHL": {Name: "Chile", ISOAlpha3: "CHL", ISOAlpha2: "CL", Code: "152"}, + "CHN": {Name: "China", ISOAlpha3: "CHN", ISOAlpha2: "CN", Code: "156"}, + "CIV": {Name: "Côte d'Ivoire", ISOAlpha3: "CIV", ISOAlpha2: "CI", Code: "384"}, + "CMR": {Name: "Cameroon", ISOAlpha3: "CMR", ISOAlpha2: "CM", Code: "120"}, + "COD": {Name: "Congo, Democratic Republic of the", ISOAlpha3: "COD", ISOAlpha2: "CD", Code: "180"}, + "COG": {Name: "Congo", ISOAlpha3: "COG", ISOAlpha2: "CG", Code: "178"}, + "COK": {Name: "Cook Islands", ISOAlpha3: "COK", ISOAlpha2: "CK", Code: "184"}, + "COL": {Name: "Colombia", ISOAlpha3: "COL", ISOAlpha2: "CO", Code: "170"}, + "COM": {Name: "Comoros", ISOAlpha3: "COM", ISOAlpha2: "KM", Code: "174"}, + "CPV": {Name: "Cabo Verde", ISOAlpha3: "CPV", ISOAlpha2: "CV", Code: "132"}, + "CRI": {Name: "Costa Rica", ISOAlpha3: "CRI", ISOAlpha2: "CR", Code: "188"}, + "CUB": {Name: "Cuba", ISOAlpha3: "CUB", ISOAlpha2: "CU", Code: "192"}, + "CUW": {Name: "Curaçao", ISOAlpha3: "CUW", ISOAlpha2: "CW", Code: "531"}, + "CXR": {Name: "Christmas Island", ISOAlpha3: "CXR", ISOAlpha2: "CX", Code: "162"}, + "CYM": {Name: "Cayman Islands", ISOAlpha3: "CYM", ISOAlpha2: "KY", Code: "136"}, + "CYP": {Name: "Cyprus", ISOAlpha3: "CYP", ISOAlpha2: "CY", Code: "196"}, + "CZE": {Name: "Czechia", ISOAlpha3: "CZE", ISOAlpha2: "CZ", Code: "203"}, + "DEU": {Name: "Germany", ISOAlpha3: "DEU", ISOAlpha2: "DE", Code: "276"}, + "DJI": {Name: "Djibouti", ISOAlpha3: "DJI", ISOAlpha2: "DJ", Code: "262"}, + "DMA": {Name: "Dominica", ISOAlpha3: "DMA", ISOAlpha2: "DM", Code: "212"}, + "DNK": {Name: "Denmark", ISOAlpha3: "DNK", ISOAlpha2: "DK", Code: "208"}, + "DOM": {Name: "Dominican Republic", ISOAlpha3: "DOM", ISOAlpha2: "DO", Code: "214"}, + "DZA": {Name: "Algeria", ISOAlpha3: "DZA", ISOAlpha2: "DZ", Code: "012"}, + "ECU": {Name: "Ecuador", ISOAlpha3: "ECU", ISOAlpha2: "EC", Code: "218"}, + "EGY": {Name: "Egypt", ISOAlpha3: "EGY", ISOAlpha2: "EG", Code: "818"}, + "ERI": {Name: "Eritrea", ISOAlpha3: "ERI", ISOAlpha2: "ER", Code: "232"}, + "ESH": {Name: "Western Sahara", ISOAlpha3: "ESH", ISOAlpha2: "EH", Code: "732"}, + "ESP": {Name: "Spain", ISOAlpha3: "ESP", ISOAlpha2: "ES", Code: "724"}, + "EST": {Name: "Estonia", ISOAlpha3: "EST", ISOAlpha2: "EE", Code: "233"}, + "ETH": {Name: "Ethiopia", ISOAlpha3: "ETH", ISOAlpha2: "ET", Code: "231"}, + "FIN": {Name: "Finland", ISOAlpha3: "FIN", ISOAlpha2: "FI", Code: "246"}, + "FJI": {Name: "Fiji", ISOAlpha3: "FJI", ISOAlpha2: "FJ", Code: "242"}, + "FLK": {Name: "Falkland Islands (Malvinas)", ISOAlpha3: "FLK", ISOAlpha2: "FK", Code: "238"}, + "FRA": {Name: "France", ISOAlpha3: "FRA", ISOAlpha2: "FR", Code: "250"}, + "FRO": {Name: "Faroe Islands", ISOAlpha3: "FRO", ISOAlpha2: "FO", Code: "234"}, + "FSM": {Name: "Micronesia (Federated States of)", ISOAlpha3: "FSM", ISOAlpha2: "FM", Code: "583"}, + "GAB": {Name: "Gabon", ISOAlpha3: "GAB", ISOAlpha2: "GA", Code: "266"}, + "GBR": {Name: "United Kingdom of Great Britain and Northern Ireland", ISOAlpha3: "GBR", ISOAlpha2: "GB", Code: "826"}, + "GEO": {Name: "Georgia", ISOAlpha3: "GEO", ISOAlpha2: "GE", Code: "268"}, + "GGY": {Name: "Guernsey", ISOAlpha3: "GGY", ISOAlpha2: "GG", Code: "831"}, + "GHA": {Name: "Ghana", ISOAlpha3: "GHA", ISOAlpha2: "GH", Code: "288"}, + "GIB": {Name: "Gibraltar", ISOAlpha3: "GIB", ISOAlpha2: "GI", Code: "292"}, + "GIN": {Name: "Guinea", ISOAlpha3: "GIN", ISOAlpha2: "GN", Code: "324"}, + "GLP": {Name: "Guadeloupe", ISOAlpha3: "GLP", ISOAlpha2: "GP", Code: "312"}, + "GMB": {Name: "Gambia", ISOAlpha3: "GMB", ISOAlpha2: "GM", Code: "270"}, + "GNB": {Name: "Guinea-Bissau", ISOAlpha3: "GNB", ISOAlpha2: "GW", Code: "624"}, + "GNQ": {Name: "Equatorial Guinea", ISOAlpha3: "GNQ", ISOAlpha2: "GQ", Code: "226"}, + "GRC": {Name: "Greece", ISOAlpha3: "GRC", ISOAlpha2: "GR", Code: "300"}, + "GRD": {Name: "Grenada", ISOAlpha3: "GRD", ISOAlpha2: "GD", Code: "308"}, + "GRL": {Name: "Greenland", ISOAlpha3: "GRL", ISOAlpha2: "GL", Code: "304"}, + "GTM": {Name: "Guatemala", ISOAlpha3: "GTM", ISOAlpha2: "GT", Code: "320"}, + "GUF": {Name: "French Guiana", ISOAlpha3: "GUF", ISOAlpha2: "GF", Code: "254"}, + "GUM": {Name: "Guam", ISOAlpha3: "GUM", ISOAlpha2: "GU", Code: "316"}, + "GUY": {Name: "Guyana", ISOAlpha3: "GUY", ISOAlpha2: "GY", Code: "328"}, + "HKG": {Name: "Hong Kong", ISOAlpha3: "HKG", ISOAlpha2: "HK", Code: "344"}, + "HMD": {Name: "Heard Island and McDonald Islands", ISOAlpha3: "HMD", ISOAlpha2: "HM", Code: "334"}, + "HND": {Name: "Honduras", ISOAlpha3: "HND", ISOAlpha2: "HN", Code: "340"}, + "HRV": {Name: "Croatia", ISOAlpha3: "HRV", ISOAlpha2: "HR", Code: "191"}, + "HTI": {Name: "Haiti", ISOAlpha3: "HTI", ISOAlpha2: "HT", Code: "332"}, + "HUN": {Name: "Hungary", ISOAlpha3: "HUN", ISOAlpha2: "HU", Code: "348"}, + "IDN": {Name: "Indonesia", ISOAlpha3: "IDN", ISOAlpha2: "ID", Code: "360"}, + "IMN": {Name: "Isle of Man", ISOAlpha3: "IMN", ISOAlpha2: "IM", Code: "833"}, + "IND": {Name: "India", ISOAlpha3: "IND", ISOAlpha2: "IN", Code: "356"}, + "IOT": {Name: "British Indian Ocean Territory", ISOAlpha3: "IOT", ISOAlpha2: "IO", Code: "086"}, + "IRL": {Name: "Ireland", ISOAlpha3: "IRL", ISOAlpha2: "IE", Code: "372"}, + "IRN": {Name: "Iran (Islamic Republic of)", ISOAlpha3: "IRN", ISOAlpha2: "IR", Code: "364"}, + "IRQ": {Name: "Iraq", ISOAlpha3: "IRQ", ISOAlpha2: "IQ", Code: "368"}, + "ISL": {Name: "Iceland", ISOAlpha3: "ISL", ISOAlpha2: "IS", Code: "352"}, + "ISR": {Name: "Israel", ISOAlpha3: "ISR", ISOAlpha2: "IL", Code: "376"}, + "ITA": {Name: "Italy", ISOAlpha3: "ITA", ISOAlpha2: "IT", Code: "380"}, + "JAM": {Name: "Jamaica", ISOAlpha3: "JAM", ISOAlpha2: "JM", Code: "388"}, + "JEY": {Name: "Jersey", ISOAlpha3: "JEY", ISOAlpha2: "JE", Code: "832"}, + "JOR": {Name: "Jordan", ISOAlpha3: "JOR", ISOAlpha2: "JO", Code: "400"}, + "JPN": {Name: "Japan", ISOAlpha3: "JPN", ISOAlpha2: "JP", Code: "392"}, + "KAZ": {Name: "Kazakhstan", ISOAlpha3: "KAZ", ISOAlpha2: "KZ", Code: "398"}, + "KEN": {Name: "Kenya", ISOAlpha3: "KEN", ISOAlpha2: "KE", Code: "404"}, + "KGZ": {Name: "Kyrgyzstan", ISOAlpha3: "KGZ", ISOAlpha2: "KG", Code: "417"}, + "KHM": {Name: "Cambodia", ISOAlpha3: "KHM", ISOAlpha2: "KH", Code: "116"}, + "KIR": {Name: "Kiribati", ISOAlpha3: "KIR", ISOAlpha2: "KI", Code: "296"}, + "KNA": {Name: "Saint Kitts and Nevis", ISOAlpha3: "KNA", ISOAlpha2: "KN", Code: "659"}, + "KOR": {Name: "Korea, Republic of", ISOAlpha3: "KOR", ISOAlpha2: "KR", Code: "410"}, + "KWT": {Name: "Kuwait", ISOAlpha3: "KWT", ISOAlpha2: "KW", Code: "414"}, + "LAO": {Name: "Lao People's Democratic Republic", ISOAlpha3: "LAO", ISOAlpha2: "LA", Code: "418"}, + "LBN": {Name: "Lebanon", ISOAlpha3: "LBN", ISOAlpha2: "LB", Code: "422"}, + "LBR": {Name: "Liberia", ISOAlpha3: "LBR", ISOAlpha2: "LR", Code: "430"}, + "LBY": {Name: "Libya", ISOAlpha3: "LBY", ISOAlpha2: "LY", Code: "434"}, + "LCA": {Name: "Saint Lucia", ISOAlpha3: "LCA", ISOAlpha2: "LC", Code: "662"}, + "LIE": {Name: "Liechtenstein", ISOAlpha3: "LIE", ISOAlpha2: "LI", Code: "438"}, + "LKA": {Name: "Sri Lanka", ISOAlpha3: "LKA", ISOAlpha2: "LK", Code: "144"}, + "LSO": {Name: "Lesotho", ISOAlpha3: "LSO", ISOAlpha2: "LS", Code: "426"}, + "LTU": {Name: "Lithuania", ISOAlpha3: "LTU", ISOAlpha2: "LT", Code: "440"}, + "LUX": {Name: "Luxembourg", ISOAlpha3: "LUX", ISOAlpha2: "LU", Code: "442"}, + "LVA": {Name: "Latvia", ISOAlpha3: "LVA", ISOAlpha2: "LV", Code: "428"}, + "MAC": {Name: "Macao", ISOAlpha3: "MAC", ISOAlpha2: "MO", Code: "446"}, + "MAF": {Name: "Saint Martin (French part)", ISOAlpha3: "MAF", ISOAlpha2: "MF", Code: "663"}, + "MAR": {Name: "Morocco", ISOAlpha3: "MAR", ISOAlpha2: "MA", Code: "504"}, + "MCO": {Name: "Monaco", ISOAlpha3: "MCO", ISOAlpha2: "MC", Code: "492"}, + "MDA": {Name: "Moldova, Republic of", ISOAlpha3: "MDA", ISOAlpha2: "MD", Code: "498"}, + "MDG": {Name: "Madagascar", ISOAlpha3: "MDG", ISOAlpha2: "MG", Code: "450"}, + "MDV": {Name: "Maldives", ISOAlpha3: "MDV", ISOAlpha2: "MV", Code: "462"}, + "MEX": {Name: "Mexico", ISOAlpha3: "MEX", ISOAlpha2: "MX", Code: "484"}, + "MHL": {Name: "Marshall Islands", ISOAlpha3: "MHL", ISOAlpha2: "MH", Code: "584"}, + "MKD": {Name: "North Macedonia", ISOAlpha3: "MKD", ISOAlpha2: "MK", Code: "807"}, + "MLI": {Name: "Mali", ISOAlpha3: "MLI", ISOAlpha2: "ML", Code: "466"}, + "MLT": {Name: "Malta", ISOAlpha3: "MLT", ISOAlpha2: "MT", Code: "470"}, + "MMR": {Name: "Myanmar", ISOAlpha3: "MMR", ISOAlpha2: "MM", Code: "104"}, + "MNE": {Name: "Montenegro", ISOAlpha3: "MNE", ISOAlpha2: "ME", Code: "499"}, + "MNG": {Name: "Mongolia", ISOAlpha3: "MNG", ISOAlpha2: "MN", Code: "496"}, + "MNP": {Name: "Northern Mariana Islands", ISOAlpha3: "MNP", ISOAlpha2: "MP", Code: "580"}, + "MOZ": {Name: "Mozambique", ISOAlpha3: "MOZ", ISOAlpha2: "MZ", Code: "508"}, + "MRT": {Name: "Mauritania", ISOAlpha3: "MRT", ISOAlpha2: "MR", Code: "478"}, + "MSR": {Name: "Montserrat", ISOAlpha3: "MSR", ISOAlpha2: "MS", Code: "500"}, + "MTQ": {Name: "Martinique", ISOAlpha3: "MTQ", ISOAlpha2: "MQ", Code: "474"}, + "MUS": {Name: "Mauritius", ISOAlpha3: "MUS", ISOAlpha2: "MU", Code: "480"}, + "MWI": {Name: "Malawi", ISOAlpha3: "MWI", ISOAlpha2: "MW", Code: "454"}, + "MYS": {Name: "Malaysia", ISOAlpha3: "MYS", ISOAlpha2: "MY", Code: "458"}, + "MYT": {Name: "Mayotte", ISOAlpha3: "MYT", ISOAlpha2: "YT", Code: "175"}, + "NAM": {Name: "Namibia", ISOAlpha3: "NAM", ISOAlpha2: "NA", Code: "516"}, + "NCL": {Name: "New Caledonia", ISOAlpha3: "NCL", ISOAlpha2: "NC", Code: "540"}, + "NER": {Name: "Niger", ISOAlpha3: "NER", ISOAlpha2: "NE", Code: "562"}, + "NFK": {Name: "Norfolk Island", ISOAlpha3: "NFK", ISOAlpha2: "NF", Code: "574"}, + "NGA": {Name: "Nigeria", ISOAlpha3: "NGA", ISOAlpha2: "NG", Code: "566"}, + "NIC": {Name: "Nicaragua", ISOAlpha3: "NIC", ISOAlpha2: "NI", Code: "558"}, + "NIU": {Name: "Niue", ISOAlpha3: "NIU", ISOAlpha2: "NU", Code: "570"}, + "NLD": {Name: "Netherlands", ISOAlpha3: "NLD", ISOAlpha2: "NL", Code: "528"}, + "NOR": {Name: "Norway", ISOAlpha3: "NOR", ISOAlpha2: "NO", Code: "578"}, + "NPL": {Name: "Nepal", ISOAlpha3: "NPL", ISOAlpha2: "NP", Code: "524"}, + "NRU": {Name: "Nauru", ISOAlpha3: "NRU", ISOAlpha2: "NR", Code: "520"}, + "NZL": {Name: "New Zealand", ISOAlpha3: "NZL", ISOAlpha2: "NZ", Code: "554"}, + "OMN": {Name: "Oman", ISOAlpha3: "OMN", ISOAlpha2: "OM", Code: "512"}, + "PAK": {Name: "Pakistan", ISOAlpha3: "PAK", ISOAlpha2: "PK", Code: "586"}, + "PAN": {Name: "Panama", ISOAlpha3: "PAN", ISOAlpha2: "PA", Code: "591"}, + "PCN": {Name: "Pitcairn", ISOAlpha3: "PCN", ISOAlpha2: "PN", Code: "612"}, + "PER": {Name: "Peru", ISOAlpha3: "PER", ISOAlpha2: "PE", Code: "604"}, + "PHL": {Name: "Philippines", ISOAlpha3: "PHL", ISOAlpha2: "PH", Code: "608"}, + "PLW": {Name: "Palau", ISOAlpha3: "PLW", ISOAlpha2: "PW", Code: "585"}, + "PNG": {Name: "Papua New Guinea", ISOAlpha3: "PNG", ISOAlpha2: "PG", Code: "598"}, + "POL": {Name: "Poland", ISOAlpha3: "POL", ISOAlpha2: "PL", Code: "616"}, + "PRI": {Name: "Puerto Rico", ISOAlpha3: "PRI", ISOAlpha2: "PR", Code: "630"}, + "PRK": {Name: "Korea (Democratic People's Republic of)", ISOAlpha3: "PRK", ISOAlpha2: "KP", Code: "408"}, + "PRT": {Name: "Portugal", ISOAlpha3: "PRT", ISOAlpha2: "PT", Code: "620"}, + "PRY": {Name: "Paraguay", ISOAlpha3: "PRY", ISOAlpha2: "PY", Code: "600"}, + "PSE": {Name: "Palestine, State of", ISOAlpha3: "PSE", ISOAlpha2: "PS", Code: "275"}, + "PYF": {Name: "French Polynesia", ISOAlpha3: "PYF", ISOAlpha2: "PF", Code: "258"}, + "QAT": {Name: "Qatar", ISOAlpha3: "QAT", ISOAlpha2: "QA", Code: "634"}, + "REU": {Name: "Réunion", ISOAlpha3: "REU", ISOAlpha2: "RE", Code: "638"}, + "ROU": {Name: "Romania", ISOAlpha3: "ROU", ISOAlpha2: "RO", Code: "642"}, + "RUS": {Name: "Russian Federation", ISOAlpha3: "RUS", ISOAlpha2: "RU", Code: "643"}, + "RWA": {Name: "Rwanda", ISOAlpha3: "RWA", ISOAlpha2: "RW", Code: "646"}, + "SAU": {Name: "Saudi Arabia", ISOAlpha3: "SAU", ISOAlpha2: "SA", Code: "682"}, + "SDN": {Name: "Sudan", ISOAlpha3: "SDN", ISOAlpha2: "SD", Code: "729"}, + "SEN": {Name: "Senegal", ISOAlpha3: "SEN", ISOAlpha2: "SN", Code: "686"}, + "SGP": {Name: "Singapore", ISOAlpha3: "SGP", ISOAlpha2: "SG", Code: "702"}, + "SGS": {Name: "South Georgia and the South Sandwich Islands", ISOAlpha3: "SGS", ISOAlpha2: "GS", Code: "239"}, + "SHN": {Name: "Saint Helena, Ascension and Tristan da Cunha", ISOAlpha3: "SHN", ISOAlpha2: "SH", Code: "654"}, + "SJM": {Name: "Svalbard and Jan Mayen", ISOAlpha3: "SJM", ISOAlpha2: "SJ", Code: "744"}, + "SLB": {Name: "Solomon Islands", ISOAlpha3: "SLB", ISOAlpha2: "SB", Code: "090"}, + "SLE": {Name: "Sierra Leone", ISOAlpha3: "SLE", ISOAlpha2: "SL", Code: "694"}, + "SLV": {Name: "El Salvador", ISOAlpha3: "SLV", ISOAlpha2: "SV", Code: "222"}, + "SMR": {Name: "San Marino", ISOAlpha3: "SMR", ISOAlpha2: "SM", Code: "674"}, + "SOM": {Name: "Somalia", ISOAlpha3: "SOM", ISOAlpha2: "SO", Code: "706"}, + "SPM": {Name: "Saint Pierre and Miquelon", ISOAlpha3: "SPM", ISOAlpha2: "PM", Code: "666"}, + "SRB": {Name: "Serbia", ISOAlpha3: "SRB", ISOAlpha2: "RS", Code: "688"}, + "SSD": {Name: "South Sudan", ISOAlpha3: "SSD", ISOAlpha2: "SS", Code: "728"}, + "STP": {Name: "Sao Tome and Principe", ISOAlpha3: "STP", ISOAlpha2: "ST", Code: "678"}, + "SUR": {Name: "Suriname", ISOAlpha3: "SUR", ISOAlpha2: "SR", Code: "740"}, + "SVK": {Name: "Slovakia", ISOAlpha3: "SVK", ISOAlpha2: "SK", Code: "703"}, + "SVN": {Name: "Slovenia", ISOAlpha3: "SVN", ISOAlpha2: "SI", Code: "705"}, + "SWE": {Name: "Sweden", ISOAlpha3: "SWE", ISOAlpha2: "SE", Code: "752"}, + "SWZ": {Name: "Eswatini", ISOAlpha3: "SWZ", ISOAlpha2: "SZ", Code: "748"}, + "SXM": {Name: "Sint Maarten (Dutch part)", ISOAlpha3: "SXM", ISOAlpha2: "SX", Code: "534"}, + "SYC": {Name: "Seychelles", ISOAlpha3: "SYC", ISOAlpha2: "SC", Code: "690"}, + "SYR": {Name: "Syrian Arab Republic", ISOAlpha3: "SYR", ISOAlpha2: "SY", Code: "760"}, + "TCA": {Name: "Turks and Caicos Islands", ISOAlpha3: "TCA", ISOAlpha2: "TC", Code: "796"}, + "TCD": {Name: "Chad", ISOAlpha3: "TCD", ISOAlpha2: "TD", Code: "148"}, + "TGO": {Name: "Togo", ISOAlpha3: "TGO", ISOAlpha2: "TG", Code: "768"}, + "THA": {Name: "Thailand", ISOAlpha3: "THA", ISOAlpha2: "TH", Code: "764"}, + "TJK": {Name: "Tajikistan", ISOAlpha3: "TJK", ISOAlpha2: "TJ", Code: "762"}, + "TKL": {Name: "Tokelau", ISOAlpha3: "TKL", ISOAlpha2: "TK", Code: "772"}, + "TKM": {Name: "Turkmenistan", ISOAlpha3: "TKM", ISOAlpha2: "TM", Code: "795"}, + "TLS": {Name: "Timor-Leste", ISOAlpha3: "TLS", ISOAlpha2: "TL", Code: "626"}, + "TON": {Name: "Tonga", ISOAlpha3: "TON", ISOAlpha2: "TO", Code: "776"}, + "TTO": {Name: "Trinidad and Tobago", ISOAlpha3: "TTO", ISOAlpha2: "TT", Code: "780"}, + "TUN": {Name: "Tunisia", ISOAlpha3: "TUN", ISOAlpha2: "TN", Code: "788"}, + "TUR": {Name: "Turkey", ISOAlpha3: "TUR", ISOAlpha2: "TR", Code: "792"}, + "TUV": {Name: "Tuvalu", ISOAlpha3: "TUV", ISOAlpha2: "TV", Code: "798"}, + "TWN": {Name: "Taiwan, Province of China", ISOAlpha3: "TWN", ISOAlpha2: "TW", Code: "158"}, + "TZA": {Name: "Tanzania, United Republic of", ISOAlpha3: "TZA", ISOAlpha2: "TZ", Code: "834"}, + "UGA": {Name: "Uganda", ISOAlpha3: "UGA", ISOAlpha2: "UG", Code: "800"}, + "UKR": {Name: "Ukraine", ISOAlpha3: "UKR", ISOAlpha2: "UA", Code: "804"}, + "UMI": {Name: "United States Minor Outlying Islands", ISOAlpha3: "UMI", ISOAlpha2: "UM", Code: "581"}, + "URY": {Name: "Uruguay", ISOAlpha3: "URY", ISOAlpha2: "UY", Code: "858"}, + "USA": {Name: "United States of America", ISOAlpha3: "USA", ISOAlpha2: "US", Code: "840"}, + "UZB": {Name: "Uzbekistan", ISOAlpha3: "UZB", ISOAlpha2: "UZ", Code: "860"}, + "VAT": {Name: "Holy See", ISOAlpha3: "VAT", ISOAlpha2: "VA", Code: "336"}, + "VCT": {Name: "Saint Vincent and the Grenadines", ISOAlpha3: "VCT", ISOAlpha2: "VC", Code: "670"}, + "VEN": {Name: "Venezuela (Bolivarian Republic of)", ISOAlpha3: "VEN", ISOAlpha2: "VE", Code: "862"}, + "VGB": {Name: "Virgin Islands (British)", ISOAlpha3: "VGB", ISOAlpha2: "VG", Code: "092"}, + "VIR": {Name: "Virgin Islands (U.S.)", ISOAlpha3: "VIR", ISOAlpha2: "VI", Code: "850"}, + "VNM": {Name: "Viet Nam", ISOAlpha3: "VNM", ISOAlpha2: "VN", Code: "704"}, + "VUT": {Name: "Vanuatu", ISOAlpha3: "VUT", ISOAlpha2: "VU", Code: "548"}, + "WLF": {Name: "Wallis and Futuna", ISOAlpha3: "WLF", ISOAlpha2: "WF", Code: "876"}, + "WSM": {Name: "Samoa", ISOAlpha3: "WSM", ISOAlpha2: "WS", Code: "882"}, + "YEM": {Name: "Yemen", ISOAlpha3: "YEM", ISOAlpha2: "YE", Code: "887"}, + "ZAF": {Name: "South Africa", ISOAlpha3: "ZAF", ISOAlpha2: "ZA", Code: "710"}, + "ZMB": {Name: "Zambia", ISOAlpha3: "ZMB", ISOAlpha2: "ZM", Code: "894"}, + "ZWE": {Name: "Zimbabwe", ISOAlpha3: "ZWE", ISOAlpha2: "ZW", Code: "716"}, +} + +// CountriesISO2 indexes ISO3166 countries by their alpha-2 code. +var CountriesISO2 = map[string]Country{ + "AD": {Name: "Andorra", ISOAlpha3: "AND", ISOAlpha2: "AD", Code: "020"}, + "AE": {Name: "United Arab Emirates", ISOAlpha3: "ARE", ISOAlpha2: "AE", Code: "784"}, + "AF": {Name: "Afghanistan", ISOAlpha3: "AFG", ISOAlpha2: "AF", Code: "004"}, + "AG": {Name: "Antigua and Barbuda", ISOAlpha3: "ATG", ISOAlpha2: "AG", Code: "028"}, + "AI": {Name: "Anguilla", ISOAlpha3: "AIA", ISOAlpha2: "AI", Code: "660"}, + "AL": {Name: "Albania", ISOAlpha3: "ALB", ISOAlpha2: "AL", Code: "008"}, + "AM": {Name: "Armenia", ISOAlpha3: "ARM", ISOAlpha2: "AM", Code: "051"}, + "AO": {Name: "Angola", ISOAlpha3: "AGO", ISOAlpha2: "AO", Code: "024"}, + "AQ": {Name: "Antarctica", ISOAlpha3: "ATA", ISOAlpha2: "AQ", Code: "010"}, + "AR": {Name: "Argentina", ISOAlpha3: "ARG", ISOAlpha2: "AR", Code: "032"}, + "AS": {Name: "American Samoa", ISOAlpha3: "ASM", ISOAlpha2: "AS", Code: "016"}, + "AT": {Name: "Austria", ISOAlpha3: "AUT", ISOAlpha2: "AT", Code: "040"}, + "AU": {Name: "Australia", ISOAlpha3: "AUS", ISOAlpha2: "AU", Code: "036"}, + "AW": {Name: "Aruba", ISOAlpha3: "ABW", ISOAlpha2: "AW", Code: "533"}, + "AX": {Name: "Åland Islands", ISOAlpha3: "ALA", ISOAlpha2: "AX", Code: "248"}, + "AZ": {Name: "Azerbaijan", ISOAlpha3: "AZE", ISOAlpha2: "AZ", Code: "031"}, + "BA": {Name: "Bosnia and Herzegovina", ISOAlpha3: "BIH", ISOAlpha2: "BA", Code: "070"}, + "BB": {Name: "Barbados", ISOAlpha3: "BRB", ISOAlpha2: "BB", Code: "052"}, + "BD": {Name: "Bangladesh", ISOAlpha3: "BGD", ISOAlpha2: "BD", Code: "050"}, + "BE": {Name: "Belgium", ISOAlpha3: "BEL", ISOAlpha2: "BE", Code: "056"}, + "BF": {Name: "Burkina Faso", ISOAlpha3: "BFA", ISOAlpha2: "BF", Code: "854"}, + "BG": {Name: "Bulgaria", ISOAlpha3: "BGR", ISOAlpha2: "BG", Code: "100"}, + "BH": {Name: "Bahrain", ISOAlpha3: "BHR", ISOAlpha2: "BH", Code: "048"}, + "BI": {Name: "Burundi", ISOAlpha3: "BDI", ISOAlpha2: "BI", Code: "108"}, + "BJ": {Name: "Benin", ISOAlpha3: "BEN", ISOAlpha2: "BJ", Code: "204"}, + "BL": {Name: "Saint Barthélemy", ISOAlpha3: "BLM", ISOAlpha2: "BL", Code: "652"}, + "BM": {Name: "Bermuda", ISOAlpha3: "BMU", ISOAlpha2: "BM", Code: "060"}, + "BN": {Name: "Brunei Darussalam", ISOAlpha3: "BRN", ISOAlpha2: "BN", Code: "096"}, + "BO": {Name: "Bolivia (Plurinational State of)", ISOAlpha3: "BOL", ISOAlpha2: "BO", Code: "068"}, + "BQ": {Name: "Bonaire, Sint Eustatius and Saba", ISOAlpha3: "BES", ISOAlpha2: "BQ", Code: "535"}, + "BR": {Name: "Brazil", ISOAlpha3: "BRA", ISOAlpha2: "BR", Code: "076"}, + "BS": {Name: "Bahamas", ISOAlpha3: "BHS", ISOAlpha2: "BS", Code: "044"}, + "BT": {Name: "Bhutan", ISOAlpha3: "BTN", ISOAlpha2: "BT", Code: "064"}, + "BV": {Name: "Bouvet Island", ISOAlpha3: "BVT", ISOAlpha2: "BV", Code: "074"}, + "BW": {Name: "Botswana", ISOAlpha3: "BWA", ISOAlpha2: "BW", Code: "072"}, + "BY": {Name: "Belarus", ISOAlpha3: "BLR", ISOAlpha2: "BY", Code: "112"}, + "BZ": {Name: "Belize", ISOAlpha3: "BLZ", ISOAlpha2: "BZ", Code: "084"}, + "CA": {Name: "Canada", ISOAlpha3: "CAN", ISOAlpha2: "CA", Code: "124"}, + "CC": {Name: "Cocos (Keeling) Islands", ISOAlpha3: "CCK", ISOAlpha2: "CC", Code: "166"}, + "CD": {Name: "Congo, Democratic Republic of the", ISOAlpha3: "COD", ISOAlpha2: "CD", Code: "180"}, + "CF": {Name: "Central African Republic", ISOAlpha3: "CAF", ISOAlpha2: "CF", Code: "140"}, + "CG": {Name: "Congo", ISOAlpha3: "COG", ISOAlpha2: "CG", Code: "178"}, + "CH": {Name: "Switzerland", ISOAlpha3: "CHE", ISOAlpha2: "CH", Code: "756"}, + "CI": {Name: "Côte d'Ivoire", ISOAlpha3: "CIV", ISOAlpha2: "CI", Code: "384"}, + "CK": {Name: "Cook Islands", ISOAlpha3: "COK", ISOAlpha2: "CK", Code: "184"}, + "CL": {Name: "Chile", ISOAlpha3: "CHL", ISOAlpha2: "CL", Code: "152"}, + "CM": {Name: "Cameroon", ISOAlpha3: "CMR", ISOAlpha2: "CM", Code: "120"}, + "CN": {Name: "China", ISOAlpha3: "CHN", ISOAlpha2: "CN", Code: "156"}, + "CO": {Name: "Colombia", ISOAlpha3: "COL", ISOAlpha2: "CO", Code: "170"}, + "CR": {Name: "Costa Rica", ISOAlpha3: "CRI", ISOAlpha2: "CR", Code: "188"}, + "CU": {Name: "Cuba", ISOAlpha3: "CUB", ISOAlpha2: "CU", Code: "192"}, + "CV": {Name: "Cabo Verde", ISOAlpha3: "CPV", ISOAlpha2: "CV", Code: "132"}, + "CW": {Name: "Curaçao", ISOAlpha3: "CUW", ISOAlpha2: "CW", Code: "531"}, + "CX": {Name: "Christmas Island", ISOAlpha3: "CXR", ISOAlpha2: "CX", Code: "162"}, + "CY": {Name: "Cyprus", ISOAlpha3: "CYP", ISOAlpha2: "CY", Code: "196"}, + "CZ": {Name: "Czechia", ISOAlpha3: "CZE", ISOAlpha2: "CZ", Code: "203"}, + "DE": {Name: "Germany", ISOAlpha3: "DEU", ISOAlpha2: "DE", Code: "276"}, + "DJ": {Name: "Djibouti", ISOAlpha3: "DJI", ISOAlpha2: "DJ", Code: "262"}, + "DK": {Name: "Denmark", ISOAlpha3: "DNK", ISOAlpha2: "DK", Code: "208"}, + "DM": {Name: "Dominica", ISOAlpha3: "DMA", ISOAlpha2: "DM", Code: "212"}, + "DO": {Name: "Dominican Republic", ISOAlpha3: "DOM", ISOAlpha2: "DO", Code: "214"}, + "DZ": {Name: "Algeria", ISOAlpha3: "DZA", ISOAlpha2: "DZ", Code: "012"}, + "EC": {Name: "Ecuador", ISOAlpha3: "ECU", ISOAlpha2: "EC", Code: "218"}, + "EE": {Name: "Estonia", ISOAlpha3: "EST", ISOAlpha2: "EE", Code: "233"}, + "EG": {Name: "Egypt", ISOAlpha3: "EGY", ISOAlpha2: "EG", Code: "818"}, + "EH": {Name: "Western Sahara", ISOAlpha3: "ESH", ISOAlpha2: "EH", Code: "732"}, + "ER": {Name: "Eritrea", ISOAlpha3: "ERI", ISOAlpha2: "ER", Code: "232"}, + "ES": {Name: "Spain", ISOAlpha3: "ESP", ISOAlpha2: "ES", Code: "724"}, + "ET": {Name: "Ethiopia", ISOAlpha3: "ETH", ISOAlpha2: "ET", Code: "231"}, + "FI": {Name: "Finland", ISOAlpha3: "FIN", ISOAlpha2: "FI", Code: "246"}, + "FJ": {Name: "Fiji", ISOAlpha3: "FJI", ISOAlpha2: "FJ", Code: "242"}, + "FK": {Name: "Falkland Islands (Malvinas)", ISOAlpha3: "FLK", ISOAlpha2: "FK", Code: "238"}, + "FM": {Name: "Micronesia (Federated States of)", ISOAlpha3: "FSM", ISOAlpha2: "FM", Code: "583"}, + "FO": {Name: "Faroe Islands", ISOAlpha3: "FRO", ISOAlpha2: "FO", Code: "234"}, + "FR": {Name: "France", ISOAlpha3: "FRA", ISOAlpha2: "FR", Code: "250"}, + "GA": {Name: "Gabon", ISOAlpha3: "GAB", ISOAlpha2: "GA", Code: "266"}, + "GB": {Name: "United Kingdom of Great Britain and Northern Ireland", ISOAlpha3: "GBR", ISOAlpha2: "GB", Code: "826"}, + "GD": {Name: "Grenada", ISOAlpha3: "GRD", ISOAlpha2: "GD", Code: "308"}, + "GE": {Name: "Georgia", ISOAlpha3: "GEO", ISOAlpha2: "GE", Code: "268"}, + "GF": {Name: "French Guiana", ISOAlpha3: "GUF", ISOAlpha2: "GF", Code: "254"}, + "GG": {Name: "Guernsey", ISOAlpha3: "GGY", ISOAlpha2: "GG", Code: "831"}, + "GH": {Name: "Ghana", ISOAlpha3: "GHA", ISOAlpha2: "GH", Code: "288"}, + "GI": {Name: "Gibraltar", ISOAlpha3: "GIB", ISOAlpha2: "GI", Code: "292"}, + "GL": {Name: "Greenland", ISOAlpha3: "GRL", ISOAlpha2: "GL", Code: "304"}, + "GM": {Name: "Gambia", ISOAlpha3: "GMB", ISOAlpha2: "GM", Code: "270"}, + "GN": {Name: "Guinea", ISOAlpha3: "GIN", ISOAlpha2: "GN", Code: "324"}, + "GP": {Name: "Guadeloupe", ISOAlpha3: "GLP", ISOAlpha2: "GP", Code: "312"}, + "GQ": {Name: "Equatorial Guinea", ISOAlpha3: "GNQ", ISOAlpha2: "GQ", Code: "226"}, + "GR": {Name: "Greece", ISOAlpha3: "GRC", ISOAlpha2: "GR", Code: "300"}, + "GS": {Name: "South Georgia and the South Sandwich Islands", ISOAlpha3: "SGS", ISOAlpha2: "GS", Code: "239"}, + "GT": {Name: "Guatemala", ISOAlpha3: "GTM", ISOAlpha2: "GT", Code: "320"}, + "GU": {Name: "Guam", ISOAlpha3: "GUM", ISOAlpha2: "GU", Code: "316"}, + "GW": {Name: "Guinea-Bissau", ISOAlpha3: "GNB", ISOAlpha2: "GW", Code: "624"}, + "GY": {Name: "Guyana", ISOAlpha3: "GUY", ISOAlpha2: "GY", Code: "328"}, + "HK": {Name: "Hong Kong", ISOAlpha3: "HKG", ISOAlpha2: "HK", Code: "344"}, + "HM": {Name: "Heard Island and McDonald Islands", ISOAlpha3: "HMD", ISOAlpha2: "HM", Code: "334"}, + "HN": {Name: "Honduras", ISOAlpha3: "HND", ISOAlpha2: "HN", Code: "340"}, + "HR": {Name: "Croatia", ISOAlpha3: "HRV", ISOAlpha2: "HR", Code: "191"}, + "HT": {Name: "Haiti", ISOAlpha3: "HTI", ISOAlpha2: "HT", Code: "332"}, + "HU": {Name: "Hungary", ISOAlpha3: "HUN", ISOAlpha2: "HU", Code: "348"}, + "ID": {Name: "Indonesia", ISOAlpha3: "IDN", ISOAlpha2: "ID", Code: "360"}, + "IE": {Name: "Ireland", ISOAlpha3: "IRL", ISOAlpha2: "IE", Code: "372"}, + "IL": {Name: "Israel", ISOAlpha3: "ISR", ISOAlpha2: "IL", Code: "376"}, + "IM": {Name: "Isle of Man", ISOAlpha3: "IMN", ISOAlpha2: "IM", Code: "833"}, + "IN": {Name: "India", ISOAlpha3: "IND", ISOAlpha2: "IN", Code: "356"}, + "IO": {Name: "British Indian Ocean Territory", ISOAlpha3: "IOT", ISOAlpha2: "IO", Code: "086"}, + "IQ": {Name: "Iraq", ISOAlpha3: "IRQ", ISOAlpha2: "IQ", Code: "368"}, + "IR": {Name: "Iran (Islamic Republic of)", ISOAlpha3: "IRN", ISOAlpha2: "IR", Code: "364"}, + "IS": {Name: "Iceland", ISOAlpha3: "ISL", ISOAlpha2: "IS", Code: "352"}, + "IT": {Name: "Italy", ISOAlpha3: "ITA", ISOAlpha2: "IT", Code: "380"}, + "JE": {Name: "Jersey", ISOAlpha3: "JEY", ISOAlpha2: "JE", Code: "832"}, + "JM": {Name: "Jamaica", ISOAlpha3: "JAM", ISOAlpha2: "JM", Code: "388"}, + "JO": {Name: "Jordan", ISOAlpha3: "JOR", ISOAlpha2: "JO", Code: "400"}, + "JP": {Name: "Japan", ISOAlpha3: "JPN", ISOAlpha2: "JP", Code: "392"}, + "KE": {Name: "Kenya", ISOAlpha3: "KEN", ISOAlpha2: "KE", Code: "404"}, + "KG": {Name: "Kyrgyzstan", ISOAlpha3: "KGZ", ISOAlpha2: "KG", Code: "417"}, + "KH": {Name: "Cambodia", ISOAlpha3: "KHM", ISOAlpha2: "KH", Code: "116"}, + "KI": {Name: "Kiribati", ISOAlpha3: "KIR", ISOAlpha2: "KI", Code: "296"}, + "KM": {Name: "Comoros", ISOAlpha3: "COM", ISOAlpha2: "KM", Code: "174"}, + "KN": {Name: "Saint Kitts and Nevis", ISOAlpha3: "KNA", ISOAlpha2: "KN", Code: "659"}, + "KP": {Name: "Korea (Democratic People's Republic of)", ISOAlpha3: "PRK", ISOAlpha2: "KP", Code: "408"}, + "KR": {Name: "Korea, Republic of", ISOAlpha3: "KOR", ISOAlpha2: "KR", Code: "410"}, + "KW": {Name: "Kuwait", ISOAlpha3: "KWT", ISOAlpha2: "KW", Code: "414"}, + "KY": {Name: "Cayman Islands", ISOAlpha3: "CYM", ISOAlpha2: "KY", Code: "136"}, + "KZ": {Name: "Kazakhstan", ISOAlpha3: "KAZ", ISOAlpha2: "KZ", Code: "398"}, + "LA": {Name: "Lao People's Democratic Republic", ISOAlpha3: "LAO", ISOAlpha2: "LA", Code: "418"}, + "LB": {Name: "Lebanon", ISOAlpha3: "LBN", ISOAlpha2: "LB", Code: "422"}, + "LC": {Name: "Saint Lucia", ISOAlpha3: "LCA", ISOAlpha2: "LC", Code: "662"}, + "LI": {Name: "Liechtenstein", ISOAlpha3: "LIE", ISOAlpha2: "LI", Code: "438"}, + "LK": {Name: "Sri Lanka", ISOAlpha3: "LKA", ISOAlpha2: "LK", Code: "144"}, + "LR": {Name: "Liberia", ISOAlpha3: "LBR", ISOAlpha2: "LR", Code: "430"}, + "LS": {Name: "Lesotho", ISOAlpha3: "LSO", ISOAlpha2: "LS", Code: "426"}, + "LT": {Name: "Lithuania", ISOAlpha3: "LTU", ISOAlpha2: "LT", Code: "440"}, + "LU": {Name: "Luxembourg", ISOAlpha3: "LUX", ISOAlpha2: "LU", Code: "442"}, + "LV": {Name: "Latvia", ISOAlpha3: "LVA", ISOAlpha2: "LV", Code: "428"}, + "LY": {Name: "Libya", ISOAlpha3: "LBY", ISOAlpha2: "LY", Code: "434"}, + "MA": {Name: "Morocco", ISOAlpha3: "MAR", ISOAlpha2: "MA", Code: "504"}, + "MC": {Name: "Monaco", ISOAlpha3: "MCO", ISOAlpha2: "MC", Code: "492"}, + "MD": {Name: "Moldova, Republic of", ISOAlpha3: "MDA", ISOAlpha2: "MD", Code: "498"}, + "ME": {Name: "Montenegro", ISOAlpha3: "MNE", ISOAlpha2: "ME", Code: "499"}, + "MF": {Name: "Saint Martin (French part)", ISOAlpha3: "MAF", ISOAlpha2: "MF", Code: "663"}, + "MG": {Name: "Madagascar", ISOAlpha3: "MDG", ISOAlpha2: "MG", Code: "450"}, + "MH": {Name: "Marshall Islands", ISOAlpha3: "MHL", ISOAlpha2: "MH", Code: "584"}, + "MK": {Name: "North Macedonia", ISOAlpha3: "MKD", ISOAlpha2: "MK", Code: "807"}, + "ML": {Name: "Mali", ISOAlpha3: "MLI", ISOAlpha2: "ML", Code: "466"}, + "MM": {Name: "Myanmar", ISOAlpha3: "MMR", ISOAlpha2: "MM", Code: "104"}, + "MN": {Name: "Mongolia", ISOAlpha3: "MNG", ISOAlpha2: "MN", Code: "496"}, + "MO": {Name: "Macao", ISOAlpha3: "MAC", ISOAlpha2: "MO", Code: "446"}, + "MP": {Name: "Northern Mariana Islands", ISOAlpha3: "MNP", ISOAlpha2: "MP", Code: "580"}, + "MQ": {Name: "Martinique", ISOAlpha3: "MTQ", ISOAlpha2: "MQ", Code: "474"}, + "MR": {Name: "Mauritania", ISOAlpha3: "MRT", ISOAlpha2: "MR", Code: "478"}, + "MS": {Name: "Montserrat", ISOAlpha3: "MSR", ISOAlpha2: "MS", Code: "500"}, + "MT": {Name: "Malta", ISOAlpha3: "MLT", ISOAlpha2: "MT", Code: "470"}, + "MU": {Name: "Mauritius", ISOAlpha3: "MUS", ISOAlpha2: "MU", Code: "480"}, + "MV": {Name: "Maldives", ISOAlpha3: "MDV", ISOAlpha2: "MV", Code: "462"}, + "MW": {Name: "Malawi", ISOAlpha3: "MWI", ISOAlpha2: "MW", Code: "454"}, + "MX": {Name: "Mexico", ISOAlpha3: "MEX", ISOAlpha2: "MX", Code: "484"}, + "MY": {Name: "Malaysia", ISOAlpha3: "MYS", ISOAlpha2: "MY", Code: "458"}, + "MZ": {Name: "Mozambique", ISOAlpha3: "MOZ", ISOAlpha2: "MZ", Code: "508"}, + "NA": {Name: "Namibia", ISOAlpha3: "NAM", ISOAlpha2: "NA", Code: "516"}, + "NC": {Name: "New Caledonia", ISOAlpha3: "NCL", ISOAlpha2: "NC", Code: "540"}, + "NE": {Name: "Niger", ISOAlpha3: "NER", ISOAlpha2: "NE", Code: "562"}, + "NF": {Name: "Norfolk Island", ISOAlpha3: "NFK", ISOAlpha2: "NF", Code: "574"}, + "NG": {Name: "Nigeria", ISOAlpha3: "NGA", ISOAlpha2: "NG", Code: "566"}, + "NI": {Name: "Nicaragua", ISOAlpha3: "NIC", ISOAlpha2: "NI", Code: "558"}, + "NL": {Name: "Netherlands", ISOAlpha3: "NLD", ISOAlpha2: "NL", Code: "528"}, + "NO": {Name: "Norway", ISOAlpha3: "NOR", ISOAlpha2: "NO", Code: "578"}, + "NP": {Name: "Nepal", ISOAlpha3: "NPL", ISOAlpha2: "NP", Code: "524"}, + "NR": {Name: "Nauru", ISOAlpha3: "NRU", ISOAlpha2: "NR", Code: "520"}, + "NU": {Name: "Niue", ISOAlpha3: "NIU", ISOAlpha2: "NU", Code: "570"}, + "NZ": {Name: "New Zealand", ISOAlpha3: "NZL", ISOAlpha2: "NZ", Code: "554"}, + "OM": {Name: "Oman", ISOAlpha3: "OMN", ISOAlpha2: "OM", Code: "512"}, + "PA": {Name: "Panama", ISOAlpha3: "PAN", ISOAlpha2: "PA", Code: "591"}, + "PE": {Name: "Peru", ISOAlpha3: "PER", ISOAlpha2: "PE", Code: "604"}, + "PF": {Name: "French Polynesia", ISOAlpha3: "PYF", ISOAlpha2: "PF", Code: "258"}, + "PG": {Name: "Papua New Guinea", ISOAlpha3: "PNG", ISOAlpha2: "PG", Code: "598"}, + "PH": {Name: "Philippines", ISOAlpha3: "PHL", ISOAlpha2: "PH", Code: "608"}, + "PK": {Name: "Pakistan", ISOAlpha3: "PAK", ISOAlpha2: "PK", Code: "586"}, + "PL": {Name: "Poland", ISOAlpha3: "POL", ISOAlpha2: "PL", Code: "616"}, + "PM": {Name: "Saint Pierre and Miquelon", ISOAlpha3: "SPM", ISOAlpha2: "PM", Code: "666"}, + "PN": {Name: "Pitcairn", ISOAlpha3: "PCN", ISOAlpha2: "PN", Code: "612"}, + "PR": {Name: "Puerto Rico", ISOAlpha3: "PRI", ISOAlpha2: "PR", Code: "630"}, + "PS": {Name: "Palestine, State of", ISOAlpha3: "PSE", ISOAlpha2: "PS", Code: "275"}, + "PT": {Name: "Portugal", ISOAlpha3: "PRT", ISOAlpha2: "PT", Code: "620"}, + "PW": {Name: "Palau", ISOAlpha3: "PLW", ISOAlpha2: "PW", Code: "585"}, + "PY": {Name: "Paraguay", ISOAlpha3: "PRY", ISOAlpha2: "PY", Code: "600"}, + "QA": {Name: "Qatar", ISOAlpha3: "QAT", ISOAlpha2: "QA", Code: "634"}, + "RE": {Name: "Réunion", ISOAlpha3: "REU", ISOAlpha2: "RE", Code: "638"}, + "RO": {Name: "Romania", ISOAlpha3: "ROU", ISOAlpha2: "RO", Code: "642"}, + "RS": {Name: "Serbia", ISOAlpha3: "SRB", ISOAlpha2: "RS", Code: "688"}, + "RU": {Name: "Russian Federation", ISOAlpha3: "RUS", ISOAlpha2: "RU", Code: "643"}, + "RW": {Name: "Rwanda", ISOAlpha3: "RWA", ISOAlpha2: "RW", Code: "646"}, + "SA": {Name: "Saudi Arabia", ISOAlpha3: "SAU", ISOAlpha2: "SA", Code: "682"}, + "SB": {Name: "Solomon Islands", ISOAlpha3: "SLB", ISOAlpha2: "SB", Code: "090"}, + "SC": {Name: "Seychelles", ISOAlpha3: "SYC", ISOAlpha2: "SC", Code: "690"}, + "SD": {Name: "Sudan", ISOAlpha3: "SDN", ISOAlpha2: "SD", Code: "729"}, + "SE": {Name: "Sweden", ISOAlpha3: "SWE", ISOAlpha2: "SE", Code: "752"}, + "SG": {Name: "Singapore", ISOAlpha3: "SGP", ISOAlpha2: "SG", Code: "702"}, + "SH": {Name: "Saint Helena, Ascension and Tristan da Cunha", ISOAlpha3: "SHN", ISOAlpha2: "SH", Code: "654"}, + "SI": {Name: "Slovenia", ISOAlpha3: "SVN", ISOAlpha2: "SI", Code: "705"}, + "SJ": {Name: "Svalbard and Jan Mayen", ISOAlpha3: "SJM", ISOAlpha2: "SJ", Code: "744"}, + "SK": {Name: "Slovakia", ISOAlpha3: "SVK", ISOAlpha2: "SK", Code: "703"}, + "SL": {Name: "Sierra Leone", ISOAlpha3: "SLE", ISOAlpha2: "SL", Code: "694"}, + "SM": {Name: "San Marino", ISOAlpha3: "SMR", ISOAlpha2: "SM", Code: "674"}, + "SN": {Name: "Senegal", ISOAlpha3: "SEN", ISOAlpha2: "SN", Code: "686"}, + "SO": {Name: "Somalia", ISOAlpha3: "SOM", ISOAlpha2: "SO", Code: "706"}, + "SR": {Name: "Suriname", ISOAlpha3: "SUR", ISOAlpha2: "SR", Code: "740"}, + "SS": {Name: "South Sudan", ISOAlpha3: "SSD", ISOAlpha2: "SS", Code: "728"}, + "ST": {Name: "Sao Tome and Principe", ISOAlpha3: "STP", ISOAlpha2: "ST", Code: "678"}, + "SV": {Name: "El Salvador", ISOAlpha3: "SLV", ISOAlpha2: "SV", Code: "222"}, + "SX": {Name: "Sint Maarten (Dutch part)", ISOAlpha3: "SXM", ISOAlpha2: "SX", Code: "534"}, + "SY": {Name: "Syrian Arab Republic", ISOAlpha3: "SYR", ISOAlpha2: "SY", Code: "760"}, + "SZ": {Name: "Eswatini", ISOAlpha3: "SWZ", ISOAlpha2: "SZ", Code: "748"}, + "TC": {Name: "Turks and Caicos Islands", ISOAlpha3: "TCA", ISOAlpha2: "TC", Code: "796"}, + "TD": {Name: "Chad", ISOAlpha3: "TCD", ISOAlpha2: "TD", Code: "148"}, + "TF": {Name: "French Southern Territories", ISOAlpha3: "ATF", ISOAlpha2: "TF", Code: "260"}, + "TG": {Name: "Togo", ISOAlpha3: "TGO", ISOAlpha2: "TG", Code: "768"}, + "TH": {Name: "Thailand", ISOAlpha3: "THA", ISOAlpha2: "TH", Code: "764"}, + "TJ": {Name: "Tajikistan", ISOAlpha3: "TJK", ISOAlpha2: "TJ", Code: "762"}, + "TK": {Name: "Tokelau", ISOAlpha3: "TKL", ISOAlpha2: "TK", Code: "772"}, + "TL": {Name: "Timor-Leste", ISOAlpha3: "TLS", ISOAlpha2: "TL", Code: "626"}, + "TM": {Name: "Turkmenistan", ISOAlpha3: "TKM", ISOAlpha2: "TM", Code: "795"}, + "TN": {Name: "Tunisia", ISOAlpha3: "TUN", ISOAlpha2: "TN", Code: "788"}, + "TO": {Name: "Tonga", ISOAlpha3: "TON", ISOAlpha2: "TO", Code: "776"}, + "TR": {Name: "Turkey", ISOAlpha3: "TUR", ISOAlpha2: "TR", Code: "792"}, + "TT": {Name: "Trinidad and Tobago", ISOAlpha3: "TTO", ISOAlpha2: "TT", Code: "780"}, + "TV": {Name: "Tuvalu", ISOAlpha3: "TUV", ISOAlpha2: "TV", Code: "798"}, + "TW": {Name: "Taiwan, Province of China", ISOAlpha3: "TWN", ISOAlpha2: "TW", Code: "158"}, + "TZ": {Name: "Tanzania, United Republic of", ISOAlpha3: "TZA", ISOAlpha2: "TZ", Code: "834"}, + "UA": {Name: "Ukraine", ISOAlpha3: "UKR", ISOAlpha2: "UA", Code: "804"}, + "UG": {Name: "Uganda", ISOAlpha3: "UGA", ISOAlpha2: "UG", Code: "800"}, + "UM": {Name: "United States Minor Outlying Islands", ISOAlpha3: "UMI", ISOAlpha2: "UM", Code: "581"}, + "US": {Name: "United States of America", ISOAlpha3: "USA", ISOAlpha2: "US", Code: "840"}, + "UY": {Name: "Uruguay", ISOAlpha3: "URY", ISOAlpha2: "UY", Code: "858"}, + "UZ": {Name: "Uzbekistan", ISOAlpha3: "UZB", ISOAlpha2: "UZ", Code: "860"}, + "VA": {Name: "Holy See", ISOAlpha3: "VAT", ISOAlpha2: "VA", Code: "336"}, + "VC": {Name: "Saint Vincent and the Grenadines", ISOAlpha3: "VCT", ISOAlpha2: "VC", Code: "670"}, + "VE": {Name: "Venezuela (Bolivarian Republic of)", ISOAlpha3: "VEN", ISOAlpha2: "VE", Code: "862"}, + "VG": {Name: "Virgin Islands (British)", ISOAlpha3: "VGB", ISOAlpha2: "VG", Code: "092"}, + "VI": {Name: "Virgin Islands (U.S.)", ISOAlpha3: "VIR", ISOAlpha2: "VI", Code: "850"}, + "VN": {Name: "Viet Nam", ISOAlpha3: "VNM", ISOAlpha2: "VN", Code: "704"}, + "VU": {Name: "Vanuatu", ISOAlpha3: "VUT", ISOAlpha2: "VU", Code: "548"}, + "WF": {Name: "Wallis and Futuna", ISOAlpha3: "WLF", ISOAlpha2: "WF", Code: "876"}, + "WS": {Name: "Samoa", ISOAlpha3: "WSM", ISOAlpha2: "WS", Code: "882"}, + "YE": {Name: "Yemen", ISOAlpha3: "YEM", ISOAlpha2: "YE", Code: "887"}, + "YT": {Name: "Mayotte", ISOAlpha3: "MYT", ISOAlpha2: "YT", Code: "175"}, + "ZA": {Name: "South Africa", ISOAlpha3: "ZAF", ISOAlpha2: "ZA", Code: "710"}, + "ZM": {Name: "Zambia", ISOAlpha3: "ZMB", ISOAlpha2: "ZM", Code: "894"}, + "ZW": {Name: "Zimbabwe", ISOAlpha3: "ZWE", ISOAlpha2: "ZW", Code: "716"}, +} diff --git a/vendor/github.com/go-openapi/strfmt/internal/countries/country.go b/vendor/github.com/go-openapi/strfmt/internal/countries/country.go new file mode 100644 index 0000000000..774051430e --- /dev/null +++ b/vendor/github.com/go-openapi/strfmt/internal/countries/country.go @@ -0,0 +1,15 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +//go:generate go run gen.go -- countries.go +package countries + +// Country identifies a country with its english name, and ISO3166 codes. +// +//nolint:tagliatelle // JSON tags mirror the iso3166.json source keys (alpha-3, alpha-2, country-code); renaming them breaks the embedded-JSON unmarshal. +type Country struct { + Name string `json:"name"` + ISOAlpha3 string `json:"alpha-3"` + ISOAlpha2 string `json:"alpha-2"` + Code string `json:"country-code"` +} diff --git a/vendor/github.com/go-openapi/strfmt/internal/countries/iso3166.json b/vendor/github.com/go-openapi/strfmt/internal/countries/iso3166.json new file mode 100644 index 0000000000..b2c0b5c501 --- /dev/null +++ b/vendor/github.com/go-openapi/strfmt/internal/countries/iso3166.json @@ -0,0 +1,251 @@ +[ +{"name":"Afghanistan","alpha-2":"AF","alpha-3":"AFG","country-code":"004","iso_3166-2":"ISO 3166-2:AF","region":"Asia","sub-region":"Southern Asia","intermediate-region":"","region-code":"142","sub-region-code":"034","intermediate-region-code":""}, +{"name":"Åland Islands","alpha-2":"AX","alpha-3":"ALA","country-code":"248","iso_3166-2":"ISO 3166-2:AX","region":"Europe","sub-region":"Northern Europe","intermediate-region":"","region-code":"150","sub-region-code":"154","intermediate-region-code":""}, +{"name":"Albania","alpha-2":"AL","alpha-3":"ALB","country-code":"008","iso_3166-2":"ISO 3166-2:AL","region":"Europe","sub-region":"Southern Europe","intermediate-region":"","region-code":"150","sub-region-code":"039","intermediate-region-code":""}, +{"name":"Algeria","alpha-2":"DZ","alpha-3":"DZA","country-code":"012","iso_3166-2":"ISO 3166-2:DZ","region":"Africa","sub-region":"Northern Africa","intermediate-region":"","region-code":"002","sub-region-code":"015","intermediate-region-code":""}, +{"name":"American Samoa","alpha-2":"AS","alpha-3":"ASM","country-code":"016","iso_3166-2":"ISO 3166-2:AS","region":"Oceania","sub-region":"Polynesia","intermediate-region":"","region-code":"009","sub-region-code":"061","intermediate-region-code":""}, +{"name":"Andorra","alpha-2":"AD","alpha-3":"AND","country-code":"020","iso_3166-2":"ISO 3166-2:AD","region":"Europe","sub-region":"Southern Europe","intermediate-region":"","region-code":"150","sub-region-code":"039","intermediate-region-code":""}, +{"name":"Angola","alpha-2":"AO","alpha-3":"AGO","country-code":"024","iso_3166-2":"ISO 3166-2:AO","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Middle Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"017"}, +{"name":"Anguilla","alpha-2":"AI","alpha-3":"AIA","country-code":"660","iso_3166-2":"ISO 3166-2:AI","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"Caribbean","region-code":"019","sub-region-code":"419","intermediate-region-code":"029"}, +{"name":"Antarctica","alpha-2":"AQ","alpha-3":"ATA","country-code":"010","iso_3166-2":"ISO 3166-2:AQ","region":"","sub-region":"","intermediate-region":"","region-code":"","sub-region-code":"","intermediate-region-code":""}, +{"name":"Antigua and Barbuda","alpha-2":"AG","alpha-3":"ATG","country-code":"028","iso_3166-2":"ISO 3166-2:AG","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"Caribbean","region-code":"019","sub-region-code":"419","intermediate-region-code":"029"}, +{"name":"Argentina","alpha-2":"AR","alpha-3":"ARG","country-code":"032","iso_3166-2":"ISO 3166-2:AR","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"South America","region-code":"019","sub-region-code":"419","intermediate-region-code":"005"}, +{"name":"Armenia","alpha-2":"AM","alpha-3":"ARM","country-code":"051","iso_3166-2":"ISO 3166-2:AM","region":"Asia","sub-region":"Western Asia","intermediate-region":"","region-code":"142","sub-region-code":"145","intermediate-region-code":""}, +{"name":"Aruba","alpha-2":"AW","alpha-3":"ABW","country-code":"533","iso_3166-2":"ISO 3166-2:AW","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"Caribbean","region-code":"019","sub-region-code":"419","intermediate-region-code":"029"}, +{"name":"Australia","alpha-2":"AU","alpha-3":"AUS","country-code":"036","iso_3166-2":"ISO 3166-2:AU","region":"Oceania","sub-region":"Australia and New Zealand","intermediate-region":"","region-code":"009","sub-region-code":"053","intermediate-region-code":""}, +{"name":"Austria","alpha-2":"AT","alpha-3":"AUT","country-code":"040","iso_3166-2":"ISO 3166-2:AT","region":"Europe","sub-region":"Western Europe","intermediate-region":"","region-code":"150","sub-region-code":"155","intermediate-region-code":""}, +{"name":"Azerbaijan","alpha-2":"AZ","alpha-3":"AZE","country-code":"031","iso_3166-2":"ISO 3166-2:AZ","region":"Asia","sub-region":"Western Asia","intermediate-region":"","region-code":"142","sub-region-code":"145","intermediate-region-code":""}, +{"name":"Bahamas","alpha-2":"BS","alpha-3":"BHS","country-code":"044","iso_3166-2":"ISO 3166-2:BS","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"Caribbean","region-code":"019","sub-region-code":"419","intermediate-region-code":"029"}, +{"name":"Bahrain","alpha-2":"BH","alpha-3":"BHR","country-code":"048","iso_3166-2":"ISO 3166-2:BH","region":"Asia","sub-region":"Western Asia","intermediate-region":"","region-code":"142","sub-region-code":"145","intermediate-region-code":""}, +{"name":"Bangladesh","alpha-2":"BD","alpha-3":"BGD","country-code":"050","iso_3166-2":"ISO 3166-2:BD","region":"Asia","sub-region":"Southern Asia","intermediate-region":"","region-code":"142","sub-region-code":"034","intermediate-region-code":""}, +{"name":"Barbados","alpha-2":"BB","alpha-3":"BRB","country-code":"052","iso_3166-2":"ISO 3166-2:BB","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"Caribbean","region-code":"019","sub-region-code":"419","intermediate-region-code":"029"}, +{"name":"Belarus","alpha-2":"BY","alpha-3":"BLR","country-code":"112","iso_3166-2":"ISO 3166-2:BY","region":"Europe","sub-region":"Eastern Europe","intermediate-region":"","region-code":"150","sub-region-code":"151","intermediate-region-code":""}, +{"name":"Belgium","alpha-2":"BE","alpha-3":"BEL","country-code":"056","iso_3166-2":"ISO 3166-2:BE","region":"Europe","sub-region":"Western Europe","intermediate-region":"","region-code":"150","sub-region-code":"155","intermediate-region-code":""}, +{"name":"Belize","alpha-2":"BZ","alpha-3":"BLZ","country-code":"084","iso_3166-2":"ISO 3166-2:BZ","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"Central America","region-code":"019","sub-region-code":"419","intermediate-region-code":"013"}, +{"name":"Benin","alpha-2":"BJ","alpha-3":"BEN","country-code":"204","iso_3166-2":"ISO 3166-2:BJ","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Western Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"011"}, +{"name":"Bermuda","alpha-2":"BM","alpha-3":"BMU","country-code":"060","iso_3166-2":"ISO 3166-2:BM","region":"Americas","sub-region":"Northern America","intermediate-region":"","region-code":"019","sub-region-code":"021","intermediate-region-code":""}, +{"name":"Bhutan","alpha-2":"BT","alpha-3":"BTN","country-code":"064","iso_3166-2":"ISO 3166-2:BT","region":"Asia","sub-region":"Southern Asia","intermediate-region":"","region-code":"142","sub-region-code":"034","intermediate-region-code":""}, +{"name":"Bolivia (Plurinational State of)","alpha-2":"BO","alpha-3":"BOL","country-code":"068","iso_3166-2":"ISO 3166-2:BO","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"South America","region-code":"019","sub-region-code":"419","intermediate-region-code":"005"}, +{"name":"Bonaire, Sint Eustatius and Saba","alpha-2":"BQ","alpha-3":"BES","country-code":"535","iso_3166-2":"ISO 3166-2:BQ","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"Caribbean","region-code":"019","sub-region-code":"419","intermediate-region-code":"029"}, +{"name":"Bosnia and Herzegovina","alpha-2":"BA","alpha-3":"BIH","country-code":"070","iso_3166-2":"ISO 3166-2:BA","region":"Europe","sub-region":"Southern Europe","intermediate-region":"","region-code":"150","sub-region-code":"039","intermediate-region-code":""}, +{"name":"Botswana","alpha-2":"BW","alpha-3":"BWA","country-code":"072","iso_3166-2":"ISO 3166-2:BW","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Southern Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"018"}, +{"name":"Bouvet Island","alpha-2":"BV","alpha-3":"BVT","country-code":"074","iso_3166-2":"ISO 3166-2:BV","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"South America","region-code":"019","sub-region-code":"419","intermediate-region-code":"005"}, +{"name":"Brazil","alpha-2":"BR","alpha-3":"BRA","country-code":"076","iso_3166-2":"ISO 3166-2:BR","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"South America","region-code":"019","sub-region-code":"419","intermediate-region-code":"005"}, +{"name":"British Indian Ocean Territory","alpha-2":"IO","alpha-3":"IOT","country-code":"086","iso_3166-2":"ISO 3166-2:IO","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Eastern Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"014"}, +{"name":"Brunei Darussalam","alpha-2":"BN","alpha-3":"BRN","country-code":"096","iso_3166-2":"ISO 3166-2:BN","region":"Asia","sub-region":"South-eastern Asia","intermediate-region":"","region-code":"142","sub-region-code":"035","intermediate-region-code":""}, +{"name":"Bulgaria","alpha-2":"BG","alpha-3":"BGR","country-code":"100","iso_3166-2":"ISO 3166-2:BG","region":"Europe","sub-region":"Eastern Europe","intermediate-region":"","region-code":"150","sub-region-code":"151","intermediate-region-code":""}, +{"name":"Burkina Faso","alpha-2":"BF","alpha-3":"BFA","country-code":"854","iso_3166-2":"ISO 3166-2:BF","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Western Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"011"}, +{"name":"Burundi","alpha-2":"BI","alpha-3":"BDI","country-code":"108","iso_3166-2":"ISO 3166-2:BI","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Eastern Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"014"}, +{"name":"Cabo Verde","alpha-2":"CV","alpha-3":"CPV","country-code":"132","iso_3166-2":"ISO 3166-2:CV","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Western Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"011"}, +{"name":"Cambodia","alpha-2":"KH","alpha-3":"KHM","country-code":"116","iso_3166-2":"ISO 3166-2:KH","region":"Asia","sub-region":"South-eastern Asia","intermediate-region":"","region-code":"142","sub-region-code":"035","intermediate-region-code":""}, +{"name":"Cameroon","alpha-2":"CM","alpha-3":"CMR","country-code":"120","iso_3166-2":"ISO 3166-2:CM","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Middle Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"017"}, +{"name":"Canada","alpha-2":"CA","alpha-3":"CAN","country-code":"124","iso_3166-2":"ISO 3166-2:CA","region":"Americas","sub-region":"Northern America","intermediate-region":"","region-code":"019","sub-region-code":"021","intermediate-region-code":""}, +{"name":"Cayman Islands","alpha-2":"KY","alpha-3":"CYM","country-code":"136","iso_3166-2":"ISO 3166-2:KY","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"Caribbean","region-code":"019","sub-region-code":"419","intermediate-region-code":"029"}, +{"name":"Central African Republic","alpha-2":"CF","alpha-3":"CAF","country-code":"140","iso_3166-2":"ISO 3166-2:CF","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Middle Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"017"}, +{"name":"Chad","alpha-2":"TD","alpha-3":"TCD","country-code":"148","iso_3166-2":"ISO 3166-2:TD","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Middle Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"017"}, +{"name":"Chile","alpha-2":"CL","alpha-3":"CHL","country-code":"152","iso_3166-2":"ISO 3166-2:CL","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"South America","region-code":"019","sub-region-code":"419","intermediate-region-code":"005"}, +{"name":"China","alpha-2":"CN","alpha-3":"CHN","country-code":"156","iso_3166-2":"ISO 3166-2:CN","region":"Asia","sub-region":"Eastern Asia","intermediate-region":"","region-code":"142","sub-region-code":"030","intermediate-region-code":""}, +{"name":"Christmas Island","alpha-2":"CX","alpha-3":"CXR","country-code":"162","iso_3166-2":"ISO 3166-2:CX","region":"Oceania","sub-region":"Australia and New Zealand","intermediate-region":"","region-code":"009","sub-region-code":"053","intermediate-region-code":""}, +{"name":"Cocos (Keeling) Islands","alpha-2":"CC","alpha-3":"CCK","country-code":"166","iso_3166-2":"ISO 3166-2:CC","region":"Oceania","sub-region":"Australia and New Zealand","intermediate-region":"","region-code":"009","sub-region-code":"053","intermediate-region-code":""}, +{"name":"Colombia","alpha-2":"CO","alpha-3":"COL","country-code":"170","iso_3166-2":"ISO 3166-2:CO","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"South America","region-code":"019","sub-region-code":"419","intermediate-region-code":"005"}, +{"name":"Comoros","alpha-2":"KM","alpha-3":"COM","country-code":"174","iso_3166-2":"ISO 3166-2:KM","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Eastern Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"014"}, +{"name":"Congo","alpha-2":"CG","alpha-3":"COG","country-code":"178","iso_3166-2":"ISO 3166-2:CG","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Middle Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"017"}, +{"name":"Congo, Democratic Republic of the","alpha-2":"CD","alpha-3":"COD","country-code":"180","iso_3166-2":"ISO 3166-2:CD","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Middle Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"017"}, +{"name":"Cook Islands","alpha-2":"CK","alpha-3":"COK","country-code":"184","iso_3166-2":"ISO 3166-2:CK","region":"Oceania","sub-region":"Polynesia","intermediate-region":"","region-code":"009","sub-region-code":"061","intermediate-region-code":""}, +{"name":"Costa Rica","alpha-2":"CR","alpha-3":"CRI","country-code":"188","iso_3166-2":"ISO 3166-2:CR","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"Central America","region-code":"019","sub-region-code":"419","intermediate-region-code":"013"}, +{"name":"Côte d'Ivoire","alpha-2":"CI","alpha-3":"CIV","country-code":"384","iso_3166-2":"ISO 3166-2:CI","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Western Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"011"}, +{"name":"Croatia","alpha-2":"HR","alpha-3":"HRV","country-code":"191","iso_3166-2":"ISO 3166-2:HR","region":"Europe","sub-region":"Southern Europe","intermediate-region":"","region-code":"150","sub-region-code":"039","intermediate-region-code":""}, +{"name":"Cuba","alpha-2":"CU","alpha-3":"CUB","country-code":"192","iso_3166-2":"ISO 3166-2:CU","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"Caribbean","region-code":"019","sub-region-code":"419","intermediate-region-code":"029"}, +{"name":"Curaçao","alpha-2":"CW","alpha-3":"CUW","country-code":"531","iso_3166-2":"ISO 3166-2:CW","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"Caribbean","region-code":"019","sub-region-code":"419","intermediate-region-code":"029"}, +{"name":"Cyprus","alpha-2":"CY","alpha-3":"CYP","country-code":"196","iso_3166-2":"ISO 3166-2:CY","region":"Asia","sub-region":"Western Asia","intermediate-region":"","region-code":"142","sub-region-code":"145","intermediate-region-code":""}, +{"name":"Czechia","alpha-2":"CZ","alpha-3":"CZE","country-code":"203","iso_3166-2":"ISO 3166-2:CZ","region":"Europe","sub-region":"Eastern Europe","intermediate-region":"","region-code":"150","sub-region-code":"151","intermediate-region-code":""}, +{"name":"Denmark","alpha-2":"DK","alpha-3":"DNK","country-code":"208","iso_3166-2":"ISO 3166-2:DK","region":"Europe","sub-region":"Northern Europe","intermediate-region":"","region-code":"150","sub-region-code":"154","intermediate-region-code":""}, +{"name":"Djibouti","alpha-2":"DJ","alpha-3":"DJI","country-code":"262","iso_3166-2":"ISO 3166-2:DJ","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Eastern Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"014"}, +{"name":"Dominica","alpha-2":"DM","alpha-3":"DMA","country-code":"212","iso_3166-2":"ISO 3166-2:DM","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"Caribbean","region-code":"019","sub-region-code":"419","intermediate-region-code":"029"}, +{"name":"Dominican Republic","alpha-2":"DO","alpha-3":"DOM","country-code":"214","iso_3166-2":"ISO 3166-2:DO","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"Caribbean","region-code":"019","sub-region-code":"419","intermediate-region-code":"029"}, +{"name":"Ecuador","alpha-2":"EC","alpha-3":"ECU","country-code":"218","iso_3166-2":"ISO 3166-2:EC","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"South America","region-code":"019","sub-region-code":"419","intermediate-region-code":"005"}, +{"name":"Egypt","alpha-2":"EG","alpha-3":"EGY","country-code":"818","iso_3166-2":"ISO 3166-2:EG","region":"Africa","sub-region":"Northern Africa","intermediate-region":"","region-code":"002","sub-region-code":"015","intermediate-region-code":""}, +{"name":"El Salvador","alpha-2":"SV","alpha-3":"SLV","country-code":"222","iso_3166-2":"ISO 3166-2:SV","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"Central America","region-code":"019","sub-region-code":"419","intermediate-region-code":"013"}, +{"name":"Equatorial Guinea","alpha-2":"GQ","alpha-3":"GNQ","country-code":"226","iso_3166-2":"ISO 3166-2:GQ","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Middle Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"017"}, +{"name":"Eritrea","alpha-2":"ER","alpha-3":"ERI","country-code":"232","iso_3166-2":"ISO 3166-2:ER","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Eastern Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"014"}, +{"name":"Estonia","alpha-2":"EE","alpha-3":"EST","country-code":"233","iso_3166-2":"ISO 3166-2:EE","region":"Europe","sub-region":"Northern Europe","intermediate-region":"","region-code":"150","sub-region-code":"154","intermediate-region-code":""}, +{"name":"Eswatini","alpha-2":"SZ","alpha-3":"SWZ","country-code":"748","iso_3166-2":"ISO 3166-2:SZ","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Southern Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"018"}, +{"name":"Ethiopia","alpha-2":"ET","alpha-3":"ETH","country-code":"231","iso_3166-2":"ISO 3166-2:ET","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Eastern Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"014"}, +{"name":"Falkland Islands (Malvinas)","alpha-2":"FK","alpha-3":"FLK","country-code":"238","iso_3166-2":"ISO 3166-2:FK","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"South America","region-code":"019","sub-region-code":"419","intermediate-region-code":"005"}, +{"name":"Faroe Islands","alpha-2":"FO","alpha-3":"FRO","country-code":"234","iso_3166-2":"ISO 3166-2:FO","region":"Europe","sub-region":"Northern Europe","intermediate-region":"","region-code":"150","sub-region-code":"154","intermediate-region-code":""}, +{"name":"Fiji","alpha-2":"FJ","alpha-3":"FJI","country-code":"242","iso_3166-2":"ISO 3166-2:FJ","region":"Oceania","sub-region":"Melanesia","intermediate-region":"","region-code":"009","sub-region-code":"054","intermediate-region-code":""}, +{"name":"Finland","alpha-2":"FI","alpha-3":"FIN","country-code":"246","iso_3166-2":"ISO 3166-2:FI","region":"Europe","sub-region":"Northern Europe","intermediate-region":"","region-code":"150","sub-region-code":"154","intermediate-region-code":""}, +{"name":"France","alpha-2":"FR","alpha-3":"FRA","country-code":"250","iso_3166-2":"ISO 3166-2:FR","region":"Europe","sub-region":"Western Europe","intermediate-region":"","region-code":"150","sub-region-code":"155","intermediate-region-code":""}, +{"name":"French Guiana","alpha-2":"GF","alpha-3":"GUF","country-code":"254","iso_3166-2":"ISO 3166-2:GF","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"South America","region-code":"019","sub-region-code":"419","intermediate-region-code":"005"}, +{"name":"French Polynesia","alpha-2":"PF","alpha-3":"PYF","country-code":"258","iso_3166-2":"ISO 3166-2:PF","region":"Oceania","sub-region":"Polynesia","intermediate-region":"","region-code":"009","sub-region-code":"061","intermediate-region-code":""}, +{"name":"French Southern Territories","alpha-2":"TF","alpha-3":"ATF","country-code":"260","iso_3166-2":"ISO 3166-2:TF","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Eastern Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"014"}, +{"name":"Gabon","alpha-2":"GA","alpha-3":"GAB","country-code":"266","iso_3166-2":"ISO 3166-2:GA","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Middle Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"017"}, +{"name":"Gambia","alpha-2":"GM","alpha-3":"GMB","country-code":"270","iso_3166-2":"ISO 3166-2:GM","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Western Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"011"}, +{"name":"Georgia","alpha-2":"GE","alpha-3":"GEO","country-code":"268","iso_3166-2":"ISO 3166-2:GE","region":"Asia","sub-region":"Western Asia","intermediate-region":"","region-code":"142","sub-region-code":"145","intermediate-region-code":""}, +{"name":"Germany","alpha-2":"DE","alpha-3":"DEU","country-code":"276","iso_3166-2":"ISO 3166-2:DE","region":"Europe","sub-region":"Western Europe","intermediate-region":"","region-code":"150","sub-region-code":"155","intermediate-region-code":""}, +{"name":"Ghana","alpha-2":"GH","alpha-3":"GHA","country-code":"288","iso_3166-2":"ISO 3166-2:GH","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Western Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"011"}, +{"name":"Gibraltar","alpha-2":"GI","alpha-3":"GIB","country-code":"292","iso_3166-2":"ISO 3166-2:GI","region":"Europe","sub-region":"Southern Europe","intermediate-region":"","region-code":"150","sub-region-code":"039","intermediate-region-code":""}, +{"name":"Greece","alpha-2":"GR","alpha-3":"GRC","country-code":"300","iso_3166-2":"ISO 3166-2:GR","region":"Europe","sub-region":"Southern Europe","intermediate-region":"","region-code":"150","sub-region-code":"039","intermediate-region-code":""}, +{"name":"Greenland","alpha-2":"GL","alpha-3":"GRL","country-code":"304","iso_3166-2":"ISO 3166-2:GL","region":"Americas","sub-region":"Northern America","intermediate-region":"","region-code":"019","sub-region-code":"021","intermediate-region-code":""}, +{"name":"Grenada","alpha-2":"GD","alpha-3":"GRD","country-code":"308","iso_3166-2":"ISO 3166-2:GD","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"Caribbean","region-code":"019","sub-region-code":"419","intermediate-region-code":"029"}, +{"name":"Guadeloupe","alpha-2":"GP","alpha-3":"GLP","country-code":"312","iso_3166-2":"ISO 3166-2:GP","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"Caribbean","region-code":"019","sub-region-code":"419","intermediate-region-code":"029"}, +{"name":"Guam","alpha-2":"GU","alpha-3":"GUM","country-code":"316","iso_3166-2":"ISO 3166-2:GU","region":"Oceania","sub-region":"Micronesia","intermediate-region":"","region-code":"009","sub-region-code":"057","intermediate-region-code":""}, +{"name":"Guatemala","alpha-2":"GT","alpha-3":"GTM","country-code":"320","iso_3166-2":"ISO 3166-2:GT","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"Central America","region-code":"019","sub-region-code":"419","intermediate-region-code":"013"}, +{"name":"Guernsey","alpha-2":"GG","alpha-3":"GGY","country-code":"831","iso_3166-2":"ISO 3166-2:GG","region":"Europe","sub-region":"Northern Europe","intermediate-region":"Channel Islands","region-code":"150","sub-region-code":"154","intermediate-region-code":"830"}, +{"name":"Guinea","alpha-2":"GN","alpha-3":"GIN","country-code":"324","iso_3166-2":"ISO 3166-2:GN","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Western Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"011"}, +{"name":"Guinea-Bissau","alpha-2":"GW","alpha-3":"GNB","country-code":"624","iso_3166-2":"ISO 3166-2:GW","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Western Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"011"}, +{"name":"Guyana","alpha-2":"GY","alpha-3":"GUY","country-code":"328","iso_3166-2":"ISO 3166-2:GY","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"South America","region-code":"019","sub-region-code":"419","intermediate-region-code":"005"}, +{"name":"Haiti","alpha-2":"HT","alpha-3":"HTI","country-code":"332","iso_3166-2":"ISO 3166-2:HT","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"Caribbean","region-code":"019","sub-region-code":"419","intermediate-region-code":"029"}, +{"name":"Heard Island and McDonald Islands","alpha-2":"HM","alpha-3":"HMD","country-code":"334","iso_3166-2":"ISO 3166-2:HM","region":"Oceania","sub-region":"Australia and New Zealand","intermediate-region":"","region-code":"009","sub-region-code":"053","intermediate-region-code":""}, +{"name":"Holy See","alpha-2":"VA","alpha-3":"VAT","country-code":"336","iso_3166-2":"ISO 3166-2:VA","region":"Europe","sub-region":"Southern Europe","intermediate-region":"","region-code":"150","sub-region-code":"039","intermediate-region-code":""}, +{"name":"Honduras","alpha-2":"HN","alpha-3":"HND","country-code":"340","iso_3166-2":"ISO 3166-2:HN","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"Central America","region-code":"019","sub-region-code":"419","intermediate-region-code":"013"}, +{"name":"Hong Kong","alpha-2":"HK","alpha-3":"HKG","country-code":"344","iso_3166-2":"ISO 3166-2:HK","region":"Asia","sub-region":"Eastern Asia","intermediate-region":"","region-code":"142","sub-region-code":"030","intermediate-region-code":""}, +{"name":"Hungary","alpha-2":"HU","alpha-3":"HUN","country-code":"348","iso_3166-2":"ISO 3166-2:HU","region":"Europe","sub-region":"Eastern Europe","intermediate-region":"","region-code":"150","sub-region-code":"151","intermediate-region-code":""}, +{"name":"Iceland","alpha-2":"IS","alpha-3":"ISL","country-code":"352","iso_3166-2":"ISO 3166-2:IS","region":"Europe","sub-region":"Northern Europe","intermediate-region":"","region-code":"150","sub-region-code":"154","intermediate-region-code":""}, +{"name":"India","alpha-2":"IN","alpha-3":"IND","country-code":"356","iso_3166-2":"ISO 3166-2:IN","region":"Asia","sub-region":"Southern Asia","intermediate-region":"","region-code":"142","sub-region-code":"034","intermediate-region-code":""}, +{"name":"Indonesia","alpha-2":"ID","alpha-3":"IDN","country-code":"360","iso_3166-2":"ISO 3166-2:ID","region":"Asia","sub-region":"South-eastern Asia","intermediate-region":"","region-code":"142","sub-region-code":"035","intermediate-region-code":""}, +{"name":"Iran (Islamic Republic of)","alpha-2":"IR","alpha-3":"IRN","country-code":"364","iso_3166-2":"ISO 3166-2:IR","region":"Asia","sub-region":"Southern Asia","intermediate-region":"","region-code":"142","sub-region-code":"034","intermediate-region-code":""}, +{"name":"Iraq","alpha-2":"IQ","alpha-3":"IRQ","country-code":"368","iso_3166-2":"ISO 3166-2:IQ","region":"Asia","sub-region":"Western Asia","intermediate-region":"","region-code":"142","sub-region-code":"145","intermediate-region-code":""}, +{"name":"Ireland","alpha-2":"IE","alpha-3":"IRL","country-code":"372","iso_3166-2":"ISO 3166-2:IE","region":"Europe","sub-region":"Northern Europe","intermediate-region":"","region-code":"150","sub-region-code":"154","intermediate-region-code":""}, +{"name":"Isle of Man","alpha-2":"IM","alpha-3":"IMN","country-code":"833","iso_3166-2":"ISO 3166-2:IM","region":"Europe","sub-region":"Northern Europe","intermediate-region":"","region-code":"150","sub-region-code":"154","intermediate-region-code":""}, +{"name":"Israel","alpha-2":"IL","alpha-3":"ISR","country-code":"376","iso_3166-2":"ISO 3166-2:IL","region":"Asia","sub-region":"Western Asia","intermediate-region":"","region-code":"142","sub-region-code":"145","intermediate-region-code":""}, +{"name":"Italy","alpha-2":"IT","alpha-3":"ITA","country-code":"380","iso_3166-2":"ISO 3166-2:IT","region":"Europe","sub-region":"Southern Europe","intermediate-region":"","region-code":"150","sub-region-code":"039","intermediate-region-code":""}, +{"name":"Jamaica","alpha-2":"JM","alpha-3":"JAM","country-code":"388","iso_3166-2":"ISO 3166-2:JM","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"Caribbean","region-code":"019","sub-region-code":"419","intermediate-region-code":"029"}, +{"name":"Japan","alpha-2":"JP","alpha-3":"JPN","country-code":"392","iso_3166-2":"ISO 3166-2:JP","region":"Asia","sub-region":"Eastern Asia","intermediate-region":"","region-code":"142","sub-region-code":"030","intermediate-region-code":""}, +{"name":"Jersey","alpha-2":"JE","alpha-3":"JEY","country-code":"832","iso_3166-2":"ISO 3166-2:JE","region":"Europe","sub-region":"Northern Europe","intermediate-region":"Channel Islands","region-code":"150","sub-region-code":"154","intermediate-region-code":"830"}, +{"name":"Jordan","alpha-2":"JO","alpha-3":"JOR","country-code":"400","iso_3166-2":"ISO 3166-2:JO","region":"Asia","sub-region":"Western Asia","intermediate-region":"","region-code":"142","sub-region-code":"145","intermediate-region-code":""}, +{"name":"Kazakhstan","alpha-2":"KZ","alpha-3":"KAZ","country-code":"398","iso_3166-2":"ISO 3166-2:KZ","region":"Asia","sub-region":"Central Asia","intermediate-region":"","region-code":"142","sub-region-code":"143","intermediate-region-code":""}, +{"name":"Kenya","alpha-2":"KE","alpha-3":"KEN","country-code":"404","iso_3166-2":"ISO 3166-2:KE","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Eastern Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"014"}, +{"name":"Kiribati","alpha-2":"KI","alpha-3":"KIR","country-code":"296","iso_3166-2":"ISO 3166-2:KI","region":"Oceania","sub-region":"Micronesia","intermediate-region":"","region-code":"009","sub-region-code":"057","intermediate-region-code":""}, +{"name":"Korea (Democratic People's Republic of)","alpha-2":"KP","alpha-3":"PRK","country-code":"408","iso_3166-2":"ISO 3166-2:KP","region":"Asia","sub-region":"Eastern Asia","intermediate-region":"","region-code":"142","sub-region-code":"030","intermediate-region-code":""}, +{"name":"Korea, Republic of","alpha-2":"KR","alpha-3":"KOR","country-code":"410","iso_3166-2":"ISO 3166-2:KR","region":"Asia","sub-region":"Eastern Asia","intermediate-region":"","region-code":"142","sub-region-code":"030","intermediate-region-code":""}, +{"name":"Kuwait","alpha-2":"KW","alpha-3":"KWT","country-code":"414","iso_3166-2":"ISO 3166-2:KW","region":"Asia","sub-region":"Western Asia","intermediate-region":"","region-code":"142","sub-region-code":"145","intermediate-region-code":""}, +{"name":"Kyrgyzstan","alpha-2":"KG","alpha-3":"KGZ","country-code":"417","iso_3166-2":"ISO 3166-2:KG","region":"Asia","sub-region":"Central Asia","intermediate-region":"","region-code":"142","sub-region-code":"143","intermediate-region-code":""}, +{"name":"Lao People's Democratic Republic","alpha-2":"LA","alpha-3":"LAO","country-code":"418","iso_3166-2":"ISO 3166-2:LA","region":"Asia","sub-region":"South-eastern Asia","intermediate-region":"","region-code":"142","sub-region-code":"035","intermediate-region-code":""}, +{"name":"Latvia","alpha-2":"LV","alpha-3":"LVA","country-code":"428","iso_3166-2":"ISO 3166-2:LV","region":"Europe","sub-region":"Northern Europe","intermediate-region":"","region-code":"150","sub-region-code":"154","intermediate-region-code":""}, +{"name":"Lebanon","alpha-2":"LB","alpha-3":"LBN","country-code":"422","iso_3166-2":"ISO 3166-2:LB","region":"Asia","sub-region":"Western Asia","intermediate-region":"","region-code":"142","sub-region-code":"145","intermediate-region-code":""}, +{"name":"Lesotho","alpha-2":"LS","alpha-3":"LSO","country-code":"426","iso_3166-2":"ISO 3166-2:LS","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Southern Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"018"}, +{"name":"Liberia","alpha-2":"LR","alpha-3":"LBR","country-code":"430","iso_3166-2":"ISO 3166-2:LR","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Western Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"011"}, +{"name":"Libya","alpha-2":"LY","alpha-3":"LBY","country-code":"434","iso_3166-2":"ISO 3166-2:LY","region":"Africa","sub-region":"Northern Africa","intermediate-region":"","region-code":"002","sub-region-code":"015","intermediate-region-code":""}, +{"name":"Liechtenstein","alpha-2":"LI","alpha-3":"LIE","country-code":"438","iso_3166-2":"ISO 3166-2:LI","region":"Europe","sub-region":"Western Europe","intermediate-region":"","region-code":"150","sub-region-code":"155","intermediate-region-code":""}, +{"name":"Lithuania","alpha-2":"LT","alpha-3":"LTU","country-code":"440","iso_3166-2":"ISO 3166-2:LT","region":"Europe","sub-region":"Northern Europe","intermediate-region":"","region-code":"150","sub-region-code":"154","intermediate-region-code":""}, +{"name":"Luxembourg","alpha-2":"LU","alpha-3":"LUX","country-code":"442","iso_3166-2":"ISO 3166-2:LU","region":"Europe","sub-region":"Western Europe","intermediate-region":"","region-code":"150","sub-region-code":"155","intermediate-region-code":""}, +{"name":"Macao","alpha-2":"MO","alpha-3":"MAC","country-code":"446","iso_3166-2":"ISO 3166-2:MO","region":"Asia","sub-region":"Eastern Asia","intermediate-region":"","region-code":"142","sub-region-code":"030","intermediate-region-code":""}, +{"name":"Madagascar","alpha-2":"MG","alpha-3":"MDG","country-code":"450","iso_3166-2":"ISO 3166-2:MG","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Eastern Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"014"}, +{"name":"Malawi","alpha-2":"MW","alpha-3":"MWI","country-code":"454","iso_3166-2":"ISO 3166-2:MW","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Eastern Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"014"}, +{"name":"Malaysia","alpha-2":"MY","alpha-3":"MYS","country-code":"458","iso_3166-2":"ISO 3166-2:MY","region":"Asia","sub-region":"South-eastern Asia","intermediate-region":"","region-code":"142","sub-region-code":"035","intermediate-region-code":""}, +{"name":"Maldives","alpha-2":"MV","alpha-3":"MDV","country-code":"462","iso_3166-2":"ISO 3166-2:MV","region":"Asia","sub-region":"Southern Asia","intermediate-region":"","region-code":"142","sub-region-code":"034","intermediate-region-code":""}, +{"name":"Mali","alpha-2":"ML","alpha-3":"MLI","country-code":"466","iso_3166-2":"ISO 3166-2:ML","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Western Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"011"}, +{"name":"Malta","alpha-2":"MT","alpha-3":"MLT","country-code":"470","iso_3166-2":"ISO 3166-2:MT","region":"Europe","sub-region":"Southern Europe","intermediate-region":"","region-code":"150","sub-region-code":"039","intermediate-region-code":""}, +{"name":"Marshall Islands","alpha-2":"MH","alpha-3":"MHL","country-code":"584","iso_3166-2":"ISO 3166-2:MH","region":"Oceania","sub-region":"Micronesia","intermediate-region":"","region-code":"009","sub-region-code":"057","intermediate-region-code":""}, +{"name":"Martinique","alpha-2":"MQ","alpha-3":"MTQ","country-code":"474","iso_3166-2":"ISO 3166-2:MQ","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"Caribbean","region-code":"019","sub-region-code":"419","intermediate-region-code":"029"}, +{"name":"Mauritania","alpha-2":"MR","alpha-3":"MRT","country-code":"478","iso_3166-2":"ISO 3166-2:MR","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Western Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"011"}, +{"name":"Mauritius","alpha-2":"MU","alpha-3":"MUS","country-code":"480","iso_3166-2":"ISO 3166-2:MU","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Eastern Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"014"}, +{"name":"Mayotte","alpha-2":"YT","alpha-3":"MYT","country-code":"175","iso_3166-2":"ISO 3166-2:YT","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Eastern Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"014"}, +{"name":"Mexico","alpha-2":"MX","alpha-3":"MEX","country-code":"484","iso_3166-2":"ISO 3166-2:MX","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"Central America","region-code":"019","sub-region-code":"419","intermediate-region-code":"013"}, +{"name":"Micronesia (Federated States of)","alpha-2":"FM","alpha-3":"FSM","country-code":"583","iso_3166-2":"ISO 3166-2:FM","region":"Oceania","sub-region":"Micronesia","intermediate-region":"","region-code":"009","sub-region-code":"057","intermediate-region-code":""}, +{"name":"Moldova, Republic of","alpha-2":"MD","alpha-3":"MDA","country-code":"498","iso_3166-2":"ISO 3166-2:MD","region":"Europe","sub-region":"Eastern Europe","intermediate-region":"","region-code":"150","sub-region-code":"151","intermediate-region-code":""}, +{"name":"Monaco","alpha-2":"MC","alpha-3":"MCO","country-code":"492","iso_3166-2":"ISO 3166-2:MC","region":"Europe","sub-region":"Western Europe","intermediate-region":"","region-code":"150","sub-region-code":"155","intermediate-region-code":""}, +{"name":"Mongolia","alpha-2":"MN","alpha-3":"MNG","country-code":"496","iso_3166-2":"ISO 3166-2:MN","region":"Asia","sub-region":"Eastern Asia","intermediate-region":"","region-code":"142","sub-region-code":"030","intermediate-region-code":""}, +{"name":"Montenegro","alpha-2":"ME","alpha-3":"MNE","country-code":"499","iso_3166-2":"ISO 3166-2:ME","region":"Europe","sub-region":"Southern Europe","intermediate-region":"","region-code":"150","sub-region-code":"039","intermediate-region-code":""}, +{"name":"Montserrat","alpha-2":"MS","alpha-3":"MSR","country-code":"500","iso_3166-2":"ISO 3166-2:MS","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"Caribbean","region-code":"019","sub-region-code":"419","intermediate-region-code":"029"}, +{"name":"Morocco","alpha-2":"MA","alpha-3":"MAR","country-code":"504","iso_3166-2":"ISO 3166-2:MA","region":"Africa","sub-region":"Northern Africa","intermediate-region":"","region-code":"002","sub-region-code":"015","intermediate-region-code":""}, +{"name":"Mozambique","alpha-2":"MZ","alpha-3":"MOZ","country-code":"508","iso_3166-2":"ISO 3166-2:MZ","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Eastern Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"014"}, +{"name":"Myanmar","alpha-2":"MM","alpha-3":"MMR","country-code":"104","iso_3166-2":"ISO 3166-2:MM","region":"Asia","sub-region":"South-eastern Asia","intermediate-region":"","region-code":"142","sub-region-code":"035","intermediate-region-code":""}, +{"name":"Namibia","alpha-2":"NA","alpha-3":"NAM","country-code":"516","iso_3166-2":"ISO 3166-2:NA","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Southern Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"018"}, +{"name":"Nauru","alpha-2":"NR","alpha-3":"NRU","country-code":"520","iso_3166-2":"ISO 3166-2:NR","region":"Oceania","sub-region":"Micronesia","intermediate-region":"","region-code":"009","sub-region-code":"057","intermediate-region-code":""}, +{"name":"Nepal","alpha-2":"NP","alpha-3":"NPL","country-code":"524","iso_3166-2":"ISO 3166-2:NP","region":"Asia","sub-region":"Southern Asia","intermediate-region":"","region-code":"142","sub-region-code":"034","intermediate-region-code":""}, +{"name":"Netherlands","alpha-2":"NL","alpha-3":"NLD","country-code":"528","iso_3166-2":"ISO 3166-2:NL","region":"Europe","sub-region":"Western Europe","intermediate-region":"","region-code":"150","sub-region-code":"155","intermediate-region-code":""}, +{"name":"New Caledonia","alpha-2":"NC","alpha-3":"NCL","country-code":"540","iso_3166-2":"ISO 3166-2:NC","region":"Oceania","sub-region":"Melanesia","intermediate-region":"","region-code":"009","sub-region-code":"054","intermediate-region-code":""}, +{"name":"New Zealand","alpha-2":"NZ","alpha-3":"NZL","country-code":"554","iso_3166-2":"ISO 3166-2:NZ","region":"Oceania","sub-region":"Australia and New Zealand","intermediate-region":"","region-code":"009","sub-region-code":"053","intermediate-region-code":""}, +{"name":"Nicaragua","alpha-2":"NI","alpha-3":"NIC","country-code":"558","iso_3166-2":"ISO 3166-2:NI","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"Central America","region-code":"019","sub-region-code":"419","intermediate-region-code":"013"}, +{"name":"Niger","alpha-2":"NE","alpha-3":"NER","country-code":"562","iso_3166-2":"ISO 3166-2:NE","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Western Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"011"}, +{"name":"Nigeria","alpha-2":"NG","alpha-3":"NGA","country-code":"566","iso_3166-2":"ISO 3166-2:NG","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Western Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"011"}, +{"name":"Niue","alpha-2":"NU","alpha-3":"NIU","country-code":"570","iso_3166-2":"ISO 3166-2:NU","region":"Oceania","sub-region":"Polynesia","intermediate-region":"","region-code":"009","sub-region-code":"061","intermediate-region-code":""}, +{"name":"Norfolk Island","alpha-2":"NF","alpha-3":"NFK","country-code":"574","iso_3166-2":"ISO 3166-2:NF","region":"Oceania","sub-region":"Australia and New Zealand","intermediate-region":"","region-code":"009","sub-region-code":"053","intermediate-region-code":""}, +{"name":"North Macedonia","alpha-2":"MK","alpha-3":"MKD","country-code":"807","iso_3166-2":"ISO 3166-2:MK","region":"Europe","sub-region":"Southern Europe","intermediate-region":"","region-code":"150","sub-region-code":"039","intermediate-region-code":""}, +{"name":"Northern Mariana Islands","alpha-2":"MP","alpha-3":"MNP","country-code":"580","iso_3166-2":"ISO 3166-2:MP","region":"Oceania","sub-region":"Micronesia","intermediate-region":"","region-code":"009","sub-region-code":"057","intermediate-region-code":""}, +{"name":"Norway","alpha-2":"NO","alpha-3":"NOR","country-code":"578","iso_3166-2":"ISO 3166-2:NO","region":"Europe","sub-region":"Northern Europe","intermediate-region":"","region-code":"150","sub-region-code":"154","intermediate-region-code":""}, +{"name":"Oman","alpha-2":"OM","alpha-3":"OMN","country-code":"512","iso_3166-2":"ISO 3166-2:OM","region":"Asia","sub-region":"Western Asia","intermediate-region":"","region-code":"142","sub-region-code":"145","intermediate-region-code":""}, +{"name":"Pakistan","alpha-2":"PK","alpha-3":"PAK","country-code":"586","iso_3166-2":"ISO 3166-2:PK","region":"Asia","sub-region":"Southern Asia","intermediate-region":"","region-code":"142","sub-region-code":"034","intermediate-region-code":""}, +{"name":"Palau","alpha-2":"PW","alpha-3":"PLW","country-code":"585","iso_3166-2":"ISO 3166-2:PW","region":"Oceania","sub-region":"Micronesia","intermediate-region":"","region-code":"009","sub-region-code":"057","intermediate-region-code":""}, +{"name":"Palestine, State of","alpha-2":"PS","alpha-3":"PSE","country-code":"275","iso_3166-2":"ISO 3166-2:PS","region":"Asia","sub-region":"Western Asia","intermediate-region":"","region-code":"142","sub-region-code":"145","intermediate-region-code":""}, +{"name":"Panama","alpha-2":"PA","alpha-3":"PAN","country-code":"591","iso_3166-2":"ISO 3166-2:PA","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"Central America","region-code":"019","sub-region-code":"419","intermediate-region-code":"013"}, +{"name":"Papua New Guinea","alpha-2":"PG","alpha-3":"PNG","country-code":"598","iso_3166-2":"ISO 3166-2:PG","region":"Oceania","sub-region":"Melanesia","intermediate-region":"","region-code":"009","sub-region-code":"054","intermediate-region-code":""}, +{"name":"Paraguay","alpha-2":"PY","alpha-3":"PRY","country-code":"600","iso_3166-2":"ISO 3166-2:PY","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"South America","region-code":"019","sub-region-code":"419","intermediate-region-code":"005"}, +{"name":"Peru","alpha-2":"PE","alpha-3":"PER","country-code":"604","iso_3166-2":"ISO 3166-2:PE","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"South America","region-code":"019","sub-region-code":"419","intermediate-region-code":"005"}, +{"name":"Philippines","alpha-2":"PH","alpha-3":"PHL","country-code":"608","iso_3166-2":"ISO 3166-2:PH","region":"Asia","sub-region":"South-eastern Asia","intermediate-region":"","region-code":"142","sub-region-code":"035","intermediate-region-code":""}, +{"name":"Pitcairn","alpha-2":"PN","alpha-3":"PCN","country-code":"612","iso_3166-2":"ISO 3166-2:PN","region":"Oceania","sub-region":"Polynesia","intermediate-region":"","region-code":"009","sub-region-code":"061","intermediate-region-code":""}, +{"name":"Poland","alpha-2":"PL","alpha-3":"POL","country-code":"616","iso_3166-2":"ISO 3166-2:PL","region":"Europe","sub-region":"Eastern Europe","intermediate-region":"","region-code":"150","sub-region-code":"151","intermediate-region-code":""}, +{"name":"Portugal","alpha-2":"PT","alpha-3":"PRT","country-code":"620","iso_3166-2":"ISO 3166-2:PT","region":"Europe","sub-region":"Southern Europe","intermediate-region":"","region-code":"150","sub-region-code":"039","intermediate-region-code":""}, +{"name":"Puerto Rico","alpha-2":"PR","alpha-3":"PRI","country-code":"630","iso_3166-2":"ISO 3166-2:PR","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"Caribbean","region-code":"019","sub-region-code":"419","intermediate-region-code":"029"}, +{"name":"Qatar","alpha-2":"QA","alpha-3":"QAT","country-code":"634","iso_3166-2":"ISO 3166-2:QA","region":"Asia","sub-region":"Western Asia","intermediate-region":"","region-code":"142","sub-region-code":"145","intermediate-region-code":""}, +{"name":"Réunion","alpha-2":"RE","alpha-3":"REU","country-code":"638","iso_3166-2":"ISO 3166-2:RE","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Eastern Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"014"}, +{"name":"Romania","alpha-2":"RO","alpha-3":"ROU","country-code":"642","iso_3166-2":"ISO 3166-2:RO","region":"Europe","sub-region":"Eastern Europe","intermediate-region":"","region-code":"150","sub-region-code":"151","intermediate-region-code":""}, +{"name":"Russian Federation","alpha-2":"RU","alpha-3":"RUS","country-code":"643","iso_3166-2":"ISO 3166-2:RU","region":"Europe","sub-region":"Eastern Europe","intermediate-region":"","region-code":"150","sub-region-code":"151","intermediate-region-code":""}, +{"name":"Rwanda","alpha-2":"RW","alpha-3":"RWA","country-code":"646","iso_3166-2":"ISO 3166-2:RW","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Eastern Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"014"}, +{"name":"Saint Barthélemy","alpha-2":"BL","alpha-3":"BLM","country-code":"652","iso_3166-2":"ISO 3166-2:BL","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"Caribbean","region-code":"019","sub-region-code":"419","intermediate-region-code":"029"}, +{"name":"Saint Helena, Ascension and Tristan da Cunha","alpha-2":"SH","alpha-3":"SHN","country-code":"654","iso_3166-2":"ISO 3166-2:SH","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Western Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"011"}, +{"name":"Saint Kitts and Nevis","alpha-2":"KN","alpha-3":"KNA","country-code":"659","iso_3166-2":"ISO 3166-2:KN","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"Caribbean","region-code":"019","sub-region-code":"419","intermediate-region-code":"029"}, +{"name":"Saint Lucia","alpha-2":"LC","alpha-3":"LCA","country-code":"662","iso_3166-2":"ISO 3166-2:LC","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"Caribbean","region-code":"019","sub-region-code":"419","intermediate-region-code":"029"}, +{"name":"Saint Martin (French part)","alpha-2":"MF","alpha-3":"MAF","country-code":"663","iso_3166-2":"ISO 3166-2:MF","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"Caribbean","region-code":"019","sub-region-code":"419","intermediate-region-code":"029"}, +{"name":"Saint Pierre and Miquelon","alpha-2":"PM","alpha-3":"SPM","country-code":"666","iso_3166-2":"ISO 3166-2:PM","region":"Americas","sub-region":"Northern America","intermediate-region":"","region-code":"019","sub-region-code":"021","intermediate-region-code":""}, +{"name":"Saint Vincent and the Grenadines","alpha-2":"VC","alpha-3":"VCT","country-code":"670","iso_3166-2":"ISO 3166-2:VC","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"Caribbean","region-code":"019","sub-region-code":"419","intermediate-region-code":"029"}, +{"name":"Samoa","alpha-2":"WS","alpha-3":"WSM","country-code":"882","iso_3166-2":"ISO 3166-2:WS","region":"Oceania","sub-region":"Polynesia","intermediate-region":"","region-code":"009","sub-region-code":"061","intermediate-region-code":""}, +{"name":"San Marino","alpha-2":"SM","alpha-3":"SMR","country-code":"674","iso_3166-2":"ISO 3166-2:SM","region":"Europe","sub-region":"Southern Europe","intermediate-region":"","region-code":"150","sub-region-code":"039","intermediate-region-code":""}, +{"name":"Sao Tome and Principe","alpha-2":"ST","alpha-3":"STP","country-code":"678","iso_3166-2":"ISO 3166-2:ST","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Middle Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"017"}, +{"name":"Saudi Arabia","alpha-2":"SA","alpha-3":"SAU","country-code":"682","iso_3166-2":"ISO 3166-2:SA","region":"Asia","sub-region":"Western Asia","intermediate-region":"","region-code":"142","sub-region-code":"145","intermediate-region-code":""}, +{"name":"Senegal","alpha-2":"SN","alpha-3":"SEN","country-code":"686","iso_3166-2":"ISO 3166-2:SN","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Western Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"011"}, +{"name":"Serbia","alpha-2":"RS","alpha-3":"SRB","country-code":"688","iso_3166-2":"ISO 3166-2:RS","region":"Europe","sub-region":"Southern Europe","intermediate-region":"","region-code":"150","sub-region-code":"039","intermediate-region-code":""}, +{"name":"Seychelles","alpha-2":"SC","alpha-3":"SYC","country-code":"690","iso_3166-2":"ISO 3166-2:SC","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Eastern Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"014"}, +{"name":"Sierra Leone","alpha-2":"SL","alpha-3":"SLE","country-code":"694","iso_3166-2":"ISO 3166-2:SL","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Western Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"011"}, +{"name":"Singapore","alpha-2":"SG","alpha-3":"SGP","country-code":"702","iso_3166-2":"ISO 3166-2:SG","region":"Asia","sub-region":"South-eastern Asia","intermediate-region":"","region-code":"142","sub-region-code":"035","intermediate-region-code":""}, +{"name":"Sint Maarten (Dutch part)","alpha-2":"SX","alpha-3":"SXM","country-code":"534","iso_3166-2":"ISO 3166-2:SX","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"Caribbean","region-code":"019","sub-region-code":"419","intermediate-region-code":"029"}, +{"name":"Slovakia","alpha-2":"SK","alpha-3":"SVK","country-code":"703","iso_3166-2":"ISO 3166-2:SK","region":"Europe","sub-region":"Eastern Europe","intermediate-region":"","region-code":"150","sub-region-code":"151","intermediate-region-code":""}, +{"name":"Slovenia","alpha-2":"SI","alpha-3":"SVN","country-code":"705","iso_3166-2":"ISO 3166-2:SI","region":"Europe","sub-region":"Southern Europe","intermediate-region":"","region-code":"150","sub-region-code":"039","intermediate-region-code":""}, +{"name":"Solomon Islands","alpha-2":"SB","alpha-3":"SLB","country-code":"090","iso_3166-2":"ISO 3166-2:SB","region":"Oceania","sub-region":"Melanesia","intermediate-region":"","region-code":"009","sub-region-code":"054","intermediate-region-code":""}, +{"name":"Somalia","alpha-2":"SO","alpha-3":"SOM","country-code":"706","iso_3166-2":"ISO 3166-2:SO","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Eastern Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"014"}, +{"name":"South Africa","alpha-2":"ZA","alpha-3":"ZAF","country-code":"710","iso_3166-2":"ISO 3166-2:ZA","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Southern Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"018"}, +{"name":"South Georgia and the South Sandwich Islands","alpha-2":"GS","alpha-3":"SGS","country-code":"239","iso_3166-2":"ISO 3166-2:GS","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"South America","region-code":"019","sub-region-code":"419","intermediate-region-code":"005"}, +{"name":"South Sudan","alpha-2":"SS","alpha-3":"SSD","country-code":"728","iso_3166-2":"ISO 3166-2:SS","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Eastern Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"014"}, +{"name":"Spain","alpha-2":"ES","alpha-3":"ESP","country-code":"724","iso_3166-2":"ISO 3166-2:ES","region":"Europe","sub-region":"Southern Europe","intermediate-region":"","region-code":"150","sub-region-code":"039","intermediate-region-code":""}, +{"name":"Sri Lanka","alpha-2":"LK","alpha-3":"LKA","country-code":"144","iso_3166-2":"ISO 3166-2:LK","region":"Asia","sub-region":"Southern Asia","intermediate-region":"","region-code":"142","sub-region-code":"034","intermediate-region-code":""}, +{"name":"Sudan","alpha-2":"SD","alpha-3":"SDN","country-code":"729","iso_3166-2":"ISO 3166-2:SD","region":"Africa","sub-region":"Northern Africa","intermediate-region":"","region-code":"002","sub-region-code":"015","intermediate-region-code":""}, +{"name":"Suriname","alpha-2":"SR","alpha-3":"SUR","country-code":"740","iso_3166-2":"ISO 3166-2:SR","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"South America","region-code":"019","sub-region-code":"419","intermediate-region-code":"005"}, +{"name":"Svalbard and Jan Mayen","alpha-2":"SJ","alpha-3":"SJM","country-code":"744","iso_3166-2":"ISO 3166-2:SJ","region":"Europe","sub-region":"Northern Europe","intermediate-region":"","region-code":"150","sub-region-code":"154","intermediate-region-code":""}, +{"name":"Sweden","alpha-2":"SE","alpha-3":"SWE","country-code":"752","iso_3166-2":"ISO 3166-2:SE","region":"Europe","sub-region":"Northern Europe","intermediate-region":"","region-code":"150","sub-region-code":"154","intermediate-region-code":""}, +{"name":"Switzerland","alpha-2":"CH","alpha-3":"CHE","country-code":"756","iso_3166-2":"ISO 3166-2:CH","region":"Europe","sub-region":"Western Europe","intermediate-region":"","region-code":"150","sub-region-code":"155","intermediate-region-code":""}, +{"name":"Syrian Arab Republic","alpha-2":"SY","alpha-3":"SYR","country-code":"760","iso_3166-2":"ISO 3166-2:SY","region":"Asia","sub-region":"Western Asia","intermediate-region":"","region-code":"142","sub-region-code":"145","intermediate-region-code":""}, +{"name":"Taiwan, Province of China","alpha-2":"TW","alpha-3":"TWN","country-code":"158","iso_3166-2":"ISO 3166-2:TW","region":"Asia","sub-region":"Eastern Asia","intermediate-region":"","region-code":"142","sub-region-code":"030","intermediate-region-code":""}, +{"name":"Tajikistan","alpha-2":"TJ","alpha-3":"TJK","country-code":"762","iso_3166-2":"ISO 3166-2:TJ","region":"Asia","sub-region":"Central Asia","intermediate-region":"","region-code":"142","sub-region-code":"143","intermediate-region-code":""}, +{"name":"Tanzania, United Republic of","alpha-2":"TZ","alpha-3":"TZA","country-code":"834","iso_3166-2":"ISO 3166-2:TZ","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Eastern Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"014"}, +{"name":"Thailand","alpha-2":"TH","alpha-3":"THA","country-code":"764","iso_3166-2":"ISO 3166-2:TH","region":"Asia","sub-region":"South-eastern Asia","intermediate-region":"","region-code":"142","sub-region-code":"035","intermediate-region-code":""}, +{"name":"Timor-Leste","alpha-2":"TL","alpha-3":"TLS","country-code":"626","iso_3166-2":"ISO 3166-2:TL","region":"Asia","sub-region":"South-eastern Asia","intermediate-region":"","region-code":"142","sub-region-code":"035","intermediate-region-code":""}, +{"name":"Togo","alpha-2":"TG","alpha-3":"TGO","country-code":"768","iso_3166-2":"ISO 3166-2:TG","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Western Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"011"}, +{"name":"Tokelau","alpha-2":"TK","alpha-3":"TKL","country-code":"772","iso_3166-2":"ISO 3166-2:TK","region":"Oceania","sub-region":"Polynesia","intermediate-region":"","region-code":"009","sub-region-code":"061","intermediate-region-code":""}, +{"name":"Tonga","alpha-2":"TO","alpha-3":"TON","country-code":"776","iso_3166-2":"ISO 3166-2:TO","region":"Oceania","sub-region":"Polynesia","intermediate-region":"","region-code":"009","sub-region-code":"061","intermediate-region-code":""}, +{"name":"Trinidad and Tobago","alpha-2":"TT","alpha-3":"TTO","country-code":"780","iso_3166-2":"ISO 3166-2:TT","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"Caribbean","region-code":"019","sub-region-code":"419","intermediate-region-code":"029"}, +{"name":"Tunisia","alpha-2":"TN","alpha-3":"TUN","country-code":"788","iso_3166-2":"ISO 3166-2:TN","region":"Africa","sub-region":"Northern Africa","intermediate-region":"","region-code":"002","sub-region-code":"015","intermediate-region-code":""}, +{"name":"Turkey","alpha-2":"TR","alpha-3":"TUR","country-code":"792","iso_3166-2":"ISO 3166-2:TR","region":"Asia","sub-region":"Western Asia","intermediate-region":"","region-code":"142","sub-region-code":"145","intermediate-region-code":""}, +{"name":"Turkmenistan","alpha-2":"TM","alpha-3":"TKM","country-code":"795","iso_3166-2":"ISO 3166-2:TM","region":"Asia","sub-region":"Central Asia","intermediate-region":"","region-code":"142","sub-region-code":"143","intermediate-region-code":""}, +{"name":"Turks and Caicos Islands","alpha-2":"TC","alpha-3":"TCA","country-code":"796","iso_3166-2":"ISO 3166-2:TC","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"Caribbean","region-code":"019","sub-region-code":"419","intermediate-region-code":"029"}, +{"name":"Tuvalu","alpha-2":"TV","alpha-3":"TUV","country-code":"798","iso_3166-2":"ISO 3166-2:TV","region":"Oceania","sub-region":"Polynesia","intermediate-region":"","region-code":"009","sub-region-code":"061","intermediate-region-code":""}, +{"name":"Uganda","alpha-2":"UG","alpha-3":"UGA","country-code":"800","iso_3166-2":"ISO 3166-2:UG","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Eastern Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"014"}, +{"name":"Ukraine","alpha-2":"UA","alpha-3":"UKR","country-code":"804","iso_3166-2":"ISO 3166-2:UA","region":"Europe","sub-region":"Eastern Europe","intermediate-region":"","region-code":"150","sub-region-code":"151","intermediate-region-code":""}, +{"name":"United Arab Emirates","alpha-2":"AE","alpha-3":"ARE","country-code":"784","iso_3166-2":"ISO 3166-2:AE","region":"Asia","sub-region":"Western Asia","intermediate-region":"","region-code":"142","sub-region-code":"145","intermediate-region-code":""}, +{"name":"United Kingdom of Great Britain and Northern Ireland","alpha-2":"GB","alpha-3":"GBR","country-code":"826","iso_3166-2":"ISO 3166-2:GB","region":"Europe","sub-region":"Northern Europe","intermediate-region":"","region-code":"150","sub-region-code":"154","intermediate-region-code":""}, +{"name":"United States of America","alpha-2":"US","alpha-3":"USA","country-code":"840","iso_3166-2":"ISO 3166-2:US","region":"Americas","sub-region":"Northern America","intermediate-region":"","region-code":"019","sub-region-code":"021","intermediate-region-code":""}, +{"name":"United States Minor Outlying Islands","alpha-2":"UM","alpha-3":"UMI","country-code":"581","iso_3166-2":"ISO 3166-2:UM","region":"Oceania","sub-region":"Micronesia","intermediate-region":"","region-code":"009","sub-region-code":"057","intermediate-region-code":""}, +{"name":"Uruguay","alpha-2":"UY","alpha-3":"URY","country-code":"858","iso_3166-2":"ISO 3166-2:UY","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"South America","region-code":"019","sub-region-code":"419","intermediate-region-code":"005"}, +{"name":"Uzbekistan","alpha-2":"UZ","alpha-3":"UZB","country-code":"860","iso_3166-2":"ISO 3166-2:UZ","region":"Asia","sub-region":"Central Asia","intermediate-region":"","region-code":"142","sub-region-code":"143","intermediate-region-code":""}, +{"name":"Vanuatu","alpha-2":"VU","alpha-3":"VUT","country-code":"548","iso_3166-2":"ISO 3166-2:VU","region":"Oceania","sub-region":"Melanesia","intermediate-region":"","region-code":"009","sub-region-code":"054","intermediate-region-code":""}, +{"name":"Venezuela (Bolivarian Republic of)","alpha-2":"VE","alpha-3":"VEN","country-code":"862","iso_3166-2":"ISO 3166-2:VE","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"South America","region-code":"019","sub-region-code":"419","intermediate-region-code":"005"}, +{"name":"Viet Nam","alpha-2":"VN","alpha-3":"VNM","country-code":"704","iso_3166-2":"ISO 3166-2:VN","region":"Asia","sub-region":"South-eastern Asia","intermediate-region":"","region-code":"142","sub-region-code":"035","intermediate-region-code":""}, +{"name":"Virgin Islands (British)","alpha-2":"VG","alpha-3":"VGB","country-code":"092","iso_3166-2":"ISO 3166-2:VG","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"Caribbean","region-code":"019","sub-region-code":"419","intermediate-region-code":"029"}, +{"name":"Virgin Islands (U.S.)","alpha-2":"VI","alpha-3":"VIR","country-code":"850","iso_3166-2":"ISO 3166-2:VI","region":"Americas","sub-region":"Latin America and the Caribbean","intermediate-region":"Caribbean","region-code":"019","sub-region-code":"419","intermediate-region-code":"029"}, +{"name":"Wallis and Futuna","alpha-2":"WF","alpha-3":"WLF","country-code":"876","iso_3166-2":"ISO 3166-2:WF","region":"Oceania","sub-region":"Polynesia","intermediate-region":"","region-code":"009","sub-region-code":"061","intermediate-region-code":""}, +{"name":"Western Sahara","alpha-2":"EH","alpha-3":"ESH","country-code":"732","iso_3166-2":"ISO 3166-2:EH","region":"Africa","sub-region":"Northern Africa","intermediate-region":"","region-code":"002","sub-region-code":"015","intermediate-region-code":""}, +{"name":"Yemen","alpha-2":"YE","alpha-3":"YEM","country-code":"887","iso_3166-2":"ISO 3166-2:YE","region":"Asia","sub-region":"Western Asia","intermediate-region":"","region-code":"142","sub-region-code":"145","intermediate-region-code":""}, +{"name":"Zambia","alpha-2":"ZM","alpha-3":"ZMB","country-code":"894","iso_3166-2":"ISO 3166-2:ZM","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Eastern Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"014"}, +{"name":"Zimbabwe","alpha-2":"ZW","alpha-3":"ZWE","country-code":"716","iso_3166-2":"ISO 3166-2:ZW","region":"Africa","sub-region":"Sub-Saharan Africa","intermediate-region":"Eastern Africa","region-code":"002","sub-region-code":"202","intermediate-region-code":"014"} +] diff --git a/vendor/github.com/go-openapi/strfmt/mongo.go b/vendor/github.com/go-openapi/strfmt/mongo.go index be904ffa5d..15bfbed192 100644 --- a/vendor/github.com/go-openapi/strfmt/mongo.go +++ b/vendor/github.com/go-openapi/strfmt/mongo.go @@ -4,7 +4,6 @@ package strfmt import ( - "encoding/base64" "encoding/binary" "fmt" "time" @@ -41,6 +40,8 @@ var ( _ bsonUnmarshaler = &Base64{} _ bsonMarshaler = Duration(0) _ bsonUnmarshaler = (*Duration)(nil) + _ bsonMarshaler = DurationISO8601(0) + _ bsonUnmarshaler = (*DurationISO8601)(nil) _ bsonMarshaler = DateTime{} _ bsonUnmarshaler = &DateTime{} _ bsonMarshaler = ULID{} @@ -92,6 +93,10 @@ var ( _ bsonValueUnmarshaler = &DateTime{} _ bsonValueMarshaler = ObjectId{} _ bsonValueUnmarshaler = &ObjectId{} + _ bsonMarshaler = Currency{} + _ bsonUnmarshaler = (*Currency)(nil) + _ bsonMarshaler = Country{} + _ bsonUnmarshaler = (*Country)(nil) ) const ( @@ -140,7 +145,7 @@ func (b *Base64) UnmarshalBSON(data []byte) error { return fmt.Errorf("couldn't unmarshal bson bytes as base64: %w", ErrFormat) } - vb, err := base64.StdEncoding.DecodeString(s) + vb, err := base64Encoding.DecodeString(s) if err != nil { return err } @@ -171,6 +176,38 @@ func (d *Duration) UnmarshalBSON(data []byte) error { return nil } +// MarshalBSON renders the [ISODuration] as a BSON document. +// +// BSON is a storage boundary (like SQL): the value is emitted losslessly with [ISODuration.String], regardless of the +// policy P — a strict policy that could not serialize a sign or sub-second precision on the interchange path must still +// be persistable. +func (d ISODuration[P]) MarshalBSON() ([]byte, error) { + return bsonlite.C.MarshalDoc(d.String()) +} + +// UnmarshalBSON reads an [ISODuration] from a BSON document. +// +// The stored value is our own canonical output, so it is parsed leniently: BSON is trusted storage, not external +// interchange, and must round-trip whatever [ISODuration.MarshalBSON] emitted even under a strict policy P. +func (d *ISODuration[P]) UnmarshalBSON(data []byte) error { + v, err := bsonlite.C.UnmarshalDoc(data) + if err != nil { + return err + } + + s, ok := v.(string) + if !ok { + return fmt.Errorf("couldn't unmarshal bson bytes value as ISODuration: %w", ErrFormat) + } + + rd, err := parseISO8601Duration(s, DurationLenient{}.isoDurationConfig()) + if err != nil { + return err + } + *d = ISODuration[P](rd) + return nil +} + // MarshalBSON renders the [DateTime] as a BSON document. func (t DateTime) MarshalBSON() ([]byte, error) { tNorm := NormalizeTimeForMarshal(time.Time(t)) @@ -599,3 +636,47 @@ func (id *ObjectId) UnmarshalBSONValue(_ byte, data []byte) error { *id = ObjectId(oid) return nil } + +// MarshalBSON document from this value. +func (u Currency) MarshalBSON() ([]byte, error) { + return bsonlite.C.MarshalDoc(u.String()) +} + +// UnmarshalBSON document into this value. +func (u *Currency) UnmarshalBSON(data []byte) error { + s, err := unmarshalBSONString(data, "Currency") + if err != nil { + return err + } + + cur, err := ParseCurrency(s) + if err != nil { + return err + } + + *u = cur + + return nil +} + +// MarshalBSON document from this value. +func (u Country) MarshalBSON() ([]byte, error) { + return bsonlite.C.MarshalDoc(u.String()) +} + +// UnmarshalBSON document into this value. +func (u *Country) UnmarshalBSON(data []byte) error { + s, err := unmarshalBSONString(data, "Country") + if err != nil { + return err + } + + cur, err := ParseCountry(s) + if err != nil { + return err + } + + *u = cur + + return nil +} diff --git a/vendor/github.com/go-openapi/strfmt/register.go b/vendor/github.com/go-openapi/strfmt/register.go new file mode 100644 index 0000000000..4a4c185b53 --- /dev/null +++ b/vendor/github.com/go-openapi/strfmt/register.go @@ -0,0 +1,138 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package strfmt + +// Default is the default formats registry. +// +// NOTE: format "duration" is wire by default onto "duration-human". +var Default Registry //nolint:gochecknoglobals // package-level default registry, by design + +// JSONSchema2020Registry is the format registry with JSONSchema draft 2020 formats (e.g. duration is an iso8601 duration). +var JSONSchema2020Registry Registry //nolint:gochecknoglobals // package-level default registry, by design + +func init() { //nolint:gochecknoinits // registers all default string formats in the registry + // register formats in the default registry: + // - byte + // - creditcard + // - email + // - hexcolor + // - hostname + // - ipv4 + // - ipv6 + // - cidr + // - isbn + // - isbn10 + // - isbn13 + // - mac + // - password + // - rgbcolor + // - ssn + // - uri + // - uuid + // - uuid3 + // - uuid4 + // - uuid5 + // - uuid7 + // - ulid + // - date, date-time + // - duration, duration-human, duration-iso8601 + // - objectid + // - currency, country + Default = NewSeededFormats(nil, nil) + + u := URI("") + Default.Add("uri", &u, isRequestURI) + + eml := Email("") + Default.Add("email", &eml, IsEmail) + + hn := Hostname("") + Default.Add("hostname", &hn, IsHostname) + + ip4 := IPv4("") + Default.Add("ipv4", &ip4, isIPv4) + + ip6 := IPv6("") + Default.Add("ipv6", &ip6, isIPv6) + + cidr := CIDR("") + Default.Add("cidr", &cidr, isCIDR) + + mac := MAC("") + Default.Add("mac", &mac, isMAC) + + uid := UUID("") + Default.Add("uuid", &uid, IsUUID) + + uid3 := UUID3("") + Default.Add("uuid3", &uid3, IsUUID3) + + uid4 := UUID4("") + Default.Add("uuid4", &uid4, IsUUID4) + + uid5 := UUID5("") + Default.Add("uuid5", &uid5, IsUUID5) + + uid7 := UUID7("") + Default.Add("uuid7", &uid7, IsUUID7) + + isbn := ISBN("") + Default.Add("isbn", &isbn, func(str string) bool { return isISBN10(str) || isISBN13(str) }) + + isbn10 := ISBN10("") + Default.Add("isbn10", &isbn10, isISBN10) + + isbn13 := ISBN13("") + Default.Add("isbn13", &isbn13, isISBN13) + + cc := CreditCard("") + Default.Add("creditcard", &cc, isCreditCard) + + ssn := SSN("") + Default.Add("ssn", &ssn, isSSN) + + hc := HexColor("") + Default.Add("hexcolor", &hc, isHexcolor) + + rc := RGBColor("") + Default.Add("rgbcolor", &rc, isRGBcolor) + + b64 := Base64([]byte(nil)) + Default.Add("byte", &b64, isBase64) + + pw := Password("") + Default.Add("password", &pw, func(_ string) bool { return true }) + + d := Date{} + Default.Add("date", &d, IsDate) + + dt := DateTime{} + Default.Add("datetime", &dt, IsDateTime) + + du := Duration(0) + Default.Add("duration", &du, IsDuration) + Default.Add("duration-human", &du, IsDuration) + + di := DurationISO8601(0) + Default.Add("duration-iso8601", &di, IsDurationISO8601) + + var id ObjectId + Default.Add("bsonobjectid", &id, IsBSONObjectID) + + ulid := ULID{} + Default.Add("ulid", &ulid, IsULID) + + cur := Currency{} + Default.Add("currency", &cur, IsCurrency) + + co := Country{} + Default.Add("country", &co, IsCountry) + + def, ok := Default.(*defaultFormats) + if !ok { + panic("internal error: can't initialize") + } + + JSONSchema2020Registry = NewSeededFormats(def.data, JSONSchema2020Normalizer) +} diff --git a/vendor/github.com/go-openapi/strfmt/time.go b/vendor/github.com/go-openapi/strfmt/time.go index 1fde8c6b11..94e409e063 100644 --- a/vendor/github.com/go-openapi/strfmt/time.go +++ b/vendor/github.com/go-openapi/strfmt/time.go @@ -17,11 +17,6 @@ import ( // Unix 0 for an EST timezone is not equivalent to a UTC timezone. var UnixZero = time.Unix(0, 0).UTC() //nolint:gochecknoglobals // package-level sentinel value for unix epoch -func init() { //nolint:gochecknoinits // registers datetime format in the default registry - dt := DateTime{} - Default.Add("datetime", &dt, IsDateTime) -} - // IsDateTime returns true when the string is a valid date-time. // // JSON datetime format consist of a date and a time separated by a "T", e.g. 2012-04-23T18:25:43.511Z. diff --git a/vendor/github.com/go-openapi/strfmt/ulid.go b/vendor/github.com/go-openapi/strfmt/ulid.go index f05d22c518..38809adba4 100644 --- a/vendor/github.com/go-openapi/strfmt/ulid.go +++ b/vendor/github.com/go-openapi/strfmt/ulid.go @@ -69,11 +69,6 @@ var ( ULIDValueOverrideFunc = ULIDValueDefaultFunc ) -func init() { //nolint:gochecknoinits // registers ulid format in the default registry - ulid := ULID{} - Default.Add("ulid", &ulid, IsULID) -} - // IsULID checks if provided string is [ULID] format // Be noticed that this function considers overflowed [ULID] as non-[ulid]. // For more details see https://github.com/[ulid]/spec diff --git a/vendor/github.com/go-openapi/swag/.gitignore b/vendor/github.com/go-openapi/swag/.gitignore index 1680db44c0..3ceb596fa2 100644 --- a/vendor/github.com/go-openapi/swag/.gitignore +++ b/vendor/github.com/go-openapi/swag/.gitignore @@ -4,3 +4,4 @@ Godeps .idea *.out .mcp.json +.worktrees diff --git a/vendor/github.com/go-openapi/swag/.golangci.yml b/vendor/github.com/go-openapi/swag/.golangci.yml index 126264a6b8..099c0a78c6 100644 --- a/vendor/github.com/go-openapi/swag/.golangci.yml +++ b/vendor/github.com/go-openapi/swag/.golangci.yml @@ -14,7 +14,10 @@ linters: - gocognit - godot - godox + - goconst - gomoddirectives + - gomodguard + - gomodguard_v2 - gosmopolitan - inamedparam - intrange diff --git a/vendor/github.com/go-openapi/swag/CONTRIBUTORS.md b/vendor/github.com/go-openapi/swag/CONTRIBUTORS.md index 286878acff..0f0711cd5f 100644 --- a/vendor/github.com/go-openapi/swag/CONTRIBUTORS.md +++ b/vendor/github.com/go-openapi/swag/CONTRIBUTORS.md @@ -4,11 +4,11 @@ | Total Contributors | Total Contributions | | --- | --- | -| 24 | 242 | +| 24 | 251 | | Username | All Time Contribution Count | All Commits | | --- | --- | --- | -| @fredbi | 112 | | +| @fredbi | 121 | | | @casualjim | 98 | | | @alexandear | 4 | | | @orisano | 3 | | diff --git a/vendor/github.com/go-openapi/swag/README.md b/vendor/github.com/go-openapi/swag/README.md index 64f6671039..c6c2d21e94 100644 --- a/vendor/github.com/go-openapi/swag/README.md +++ b/vendor/github.com/go-openapi/swag/README.md @@ -34,12 +34,9 @@ You may also use it standalone for your projects. * **2025-12-19** : new community chat on discord * a new discord community channel is available to be notified of changes and support users - * our venerable Slack channel remains open, and will be eventually discontinued on **2026-03-31** You may join the discord community by clicking the invite link on the discord badge (also above). [![Discord Channel][discord-badge]][discord-url] -Or join our Slack channel: [![Slack Channel][slack-logo]![slack-badge]][slack-url] - ## Status API is stable. @@ -70,11 +67,12 @@ Child modules will continue to evolve and some new ones may be added in the futu | `cmdutils` | utilities to work with CLIs || | `conv` | type conversion utilities | convert between values and pointers for any types
convert from string to builtin types (wraps `strconv`)
require `./typeutils` (test dependency)
| | `fileutils` | file utilities | | -| `jsonname` | JSON utilities | infer JSON names from `go` properties
| +| `jsonname` | JSON utilities (deprecated) | infer JSON names from `go` properties
use `github.com/go-openapi/jsonpointer/jsonname` instead | | `jsonutils` | JSON utilities | fast json concatenation
read and write JSON from and to dynamic `go` data structures
~require `github.com/mailru/easyjson`~
| | `loading` | file loading | load from file or http
require `./yamlutils`
| | `mangling` | safe name generation | name mangling for `go`
| | `netutils` | networking utilities | host, port from address
| +| `pools` | utilities to work with sync.Pools | | | `stringutils` | `string` utilities | search in slice (with case-insensitive)
split/join query parameters as arrays
| | `typeutils` | `go` types utilities | check the zero value for any type
safe check for a nil value
| | `yamlutils` | YAML utilities | converting YAML to JSON
loading YAML into a dynamic YAML document
maintaining the original order of keys in YAML objects
require `./jsonutils`
~require `github.com/mailru/easyjson`~
require `go.yaml.in/yaml/v3`
| @@ -171,9 +169,9 @@ on top of which it has been built. ## Other documentation * [All-time contributors](./CONTRIBUTORS.md) -* [Contributing guidelines](.github/CONTRIBUTING.md) -* [Maintainers documentation](docs/MAINTAINERS.md) -* [Code style](docs/STYLE.md) +* [Contributing guidelines][contributing-doc-site] +* [Maintainers documentation][maintainers-doc-site] +* [Code style][style-doc-site] ## Cutting a new release @@ -208,9 +206,6 @@ Maintainers can cut a new release by either: [doc-url]: https://goswagger.io/go-openapi [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/swag [godoc-url]: http://pkg.go.dev/github.com/go-openapi/swag -[slack-logo]: https://a.slack-edge.com/e6a93c1/img/icons/favicon-32.png -[slack-badge]: https://img.shields.io/badge/slack-blue?link=https%3A%2F%2Fgoswagger.slack.com%2Farchives%2FC04R30YM -[slack-url]: https://goswagger.slack.com/archives/C04R30YMU [discord-badge]: https://img.shields.io/discord/1446918742398341256?logo=discord&label=discord&color=blue [discord-url]: https://discord.gg/FfnFYaC3k5 @@ -222,3 +217,7 @@ Maintainers can cut a new release by either: [goversion-url]: https://github.com/go-openapi/swag/blob/master/go.mod [top-badge]: https://img.shields.io/github/languages/top/go-openapi/swag [commits-badge]: https://img.shields.io/github/commits-since/go-openapi/swag/latest + +[contributing-doc-site]: https://go-openapi.github.io/doc-site/contributing/contributing/index.html +[maintainers-doc-site]: https://go-openapi.github.io/doc-site/maintainers/index.html +[style-doc-site]: https://go-openapi.github.io/doc-site/contributing/style/index.html diff --git a/vendor/github.com/go-openapi/swag/conv/format.go b/vendor/github.com/go-openapi/swag/conv/format.go index 5b87b8e146..e14e5bfcfb 100644 --- a/vendor/github.com/go-openapi/swag/conv/format.go +++ b/vendor/github.com/go-openapi/swag/conv/format.go @@ -7,14 +7,16 @@ import ( "strconv" ) +const baseDecimal = 10 + // FormatInteger turns an integer type into a string. func FormatInteger[T Signed](value T) string { - return strconv.FormatInt(int64(value), 10) + return strconv.FormatInt(int64(value), baseDecimal) } // FormatUinteger turns an unsigned integer type into a string. func FormatUinteger[T Unsigned](value T) string { - return strconv.FormatUint(uint64(value), 10) + return strconv.FormatUint(uint64(value), baseDecimal) } // FormatFloat turns a floating point numerical value into a string. @@ -26,3 +28,23 @@ func FormatFloat[T Float](value T) string { func FormatBool(value bool) string { return strconv.FormatBool(value) } + +// AppendInteger appends the decimal representation of an integer to a slice of bytes. +func AppendInteger[T Signed](dst []byte, value T) []byte { + return strconv.AppendInt(dst, int64(value), baseDecimal) +} + +// AppendUinteger appends the decimal representation of an unsigned integer to a slice of bytes. +func AppendUinteger[T Unsigned](dst []byte, value T) []byte { + return strconv.AppendUint(dst, uint64(value), baseDecimal) +} + +// AppendFloat appends the decimal representation of a floating point number to a slice of bytes. +func AppendFloat[T Float](dst []byte, value T) []byte { + return strconv.AppendFloat(dst, float64(value), 'g', -1, bitsize(value)) +} + +// AppendBool appends the text representation of a boolean to a slice of bytes. +func AppendBool(dst []byte, value bool) []byte { + return strconv.AppendBool(dst, value) +} diff --git a/vendor/github.com/go-openapi/swag/go.work b/vendor/github.com/go-openapi/swag/go.work index 8537cb2a76..f1dddc436e 100644 --- a/vendor/github.com/go-openapi/swag/go.work +++ b/vendor/github.com/go-openapi/swag/go.work @@ -12,6 +12,7 @@ use ( ./loading ./mangling ./netutils + ./pools ./stringutils ./typeutils ./yamlutils diff --git a/vendor/github.com/go-openapi/swag/jsonname_iface.go b/vendor/github.com/go-openapi/swag/jsonname_iface.go index 303a007f6f..443560caad 100644 --- a/vendor/github.com/go-openapi/swag/jsonname_iface.go +++ b/vendor/github.com/go-openapi/swag/jsonname_iface.go @@ -4,21 +4,21 @@ package swag import ( - "github.com/go-openapi/swag/jsonname" + "github.com/go-openapi/jsonpointer/jsonname" ) // DefaultJSONNameProvider is the default cache for types // -// Deprecated: use [jsonname.DefaultJSONNameProvider] instead. +// Deprecated: use [github.com/go-openapi/jsonpointer/jsonname.DefaultJSONNameProvider] instead. var DefaultJSONNameProvider = jsonname.DefaultJSONNameProvider // NameProvider represents an object capable of translating from go property names // to json property names. // -// Deprecated: use [jsonname.NameProvider] instead. +// Deprecated: use [github.com/go-openapi/jsonpointer/jsonname.NameProvider] instead. type NameProvider = jsonname.NameProvider // NewNameProvider creates a new name provider // -// Deprecated: use [jsonname.NewNameProvider] instead. +// Deprecated: use [github.com/go-openapi/jsonpointer/jsonname.NewNameProvider] instead. func NewNameProvider() *NameProvider { return jsonname.NewNameProvider() } diff --git a/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/adapter.go b/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/adapter.go index 0213ff5c29..94185f79c1 100644 --- a/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/adapter.go +++ b/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/adapter.go @@ -5,6 +5,7 @@ package json import ( stdjson "encoding/json" + "fmt" "github.com/go-openapi/swag/jsonutils/adapters/ifaces" "github.com/go-openapi/swag/typeutils" @@ -24,11 +25,16 @@ var ErrStdlib jsonError = "error from the JSON adapter stdlib" var _ ifaces.Adapter = &Adapter{} type Adapter struct { + options } // NewAdapter yields an [ifaces.Adapter] using the standard library. -func NewAdapter() *Adapter { - return &Adapter{} +func NewAdapter(opts ...Option) *Adapter { + var o options + + return &Adapter{ + options: buildOptions(o, opts), + } } func (a *Adapter) Marshal(value any) ([]byte, error) { @@ -40,45 +46,18 @@ func (a *Adapter) Unmarshal(data []byte, value any) error { } func (a *Adapter) OrderedMarshal(value ifaces.Ordered) ([]byte, error) { - w := poolOfWriters.Borrow() - defer func() { - poolOfWriters.Redeem(w) - }() - - if typeutils.IsNil(value) { - w.RawString("null") - - return w.BuildBytes() - } - - w.RawByte('{') - first := true - for k, v := range value.OrderedItems() { - if first { - first = false - } else { - w.RawByte(',') - } + w, redeem := poolOfWriters.BorrowWithRedeem() + defer redeem() + w.setBuf() - w.String(k) - w.RawByte(':') - - switch val := v.(type) { - case ifaces.Ordered: - w.Raw(a.OrderedMarshal(val)) - default: - w.Raw(stdjson.Marshal(v)) - } - } - - w.RawByte('}') + a.orderedMarshal(w, value, 1) return w.BuildBytes() } func (a *Adapter) OrderedUnmarshal(data []byte, value ifaces.SetOrdered) error { var m MapSlice - if err := m.OrderedUnmarshalJSON(data); err != nil { + if err := m.orderedUnmarshalJSON(data, a.maxDepth()); err != nil { return err } @@ -112,4 +91,43 @@ func (a *Adapter) Redeem() { } func (a *Adapter) Reset() { + a.options = options{} +} + +// orderedMarshal writes value to w, tracking the container nesting depth to guard +// against stack overflow on deeply nested structures. +func (a *Adapter) orderedMarshal(w *jwriter, value ifaces.Ordered, depth int) { + if typeutils.IsNil(value) { + w.RawString("null") + + return + } + + if maxDepth := a.maxDepth(); depth > maxDepth { + w.SetErr(fmt.Errorf("maximum nesting depth of %d exceeded: %w", maxDepth, ErrStdlib)) + + return + } + + w.RawByte('{') + first := true + for k, v := range value.OrderedItems() { + if first { + first = false + } else { + w.RawByte(',') + } + + w.String(k) + w.RawByte(':') + + switch val := v.(type) { + case ifaces.Ordered: + a.orderedMarshal(w, val, depth+1) + default: + w.Raw(stdjson.Marshal(v)) + } + } + + w.RawByte('}') } diff --git a/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/lexer.go b/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/lexer.go index b5aa1c7972..ac81cbc75f 100644 --- a/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/lexer.go +++ b/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/lexer.go @@ -54,7 +54,7 @@ func (t token) Delim() byte { return 0 } - return byte(r) + return byte(r) //nolint:gosec // delimiter runes are single byte } type tokenKind uint8 @@ -91,6 +91,13 @@ type jlexer struct { // current token next token // started bool + + // depth tracks the current JSON container nesting level, and maxDepth caps it + // to guard against stack-overflow on adversarially deep documents. The standard + // library's streaming [encoding/json.Decoder.Token] API (used here) does not + // enforce the max-depth guard that [encoding/json.Unmarshal] provides, so we do. + depth int + maxDepth int } type bytesReader struct { @@ -130,7 +137,8 @@ var _ io.Reader = &bytesReader{} func newLexer(data []byte) *jlexer { l := &jlexer{ // current: undefToken, - next: undefToken, + next: undefToken, + maxDepth: defaultMaxNestingDepth, } l.buf = &bytesReader{ buf: data, @@ -143,7 +151,11 @@ func newLexer(data []byte) *jlexer { func (l *jlexer) Reset() { l.err = nil l.next = undefToken - // leave l.dec and l.buf alone, since they are replaced at every Borrow + l.depth = 0 + l.maxDepth = defaultMaxNestingDepth + l.dec = nil + // leave l.buf alone, since they are replaced at every Borrow + l.buf = nil } func (l *jlexer) Error() error { @@ -228,6 +240,21 @@ func (l *jlexer) Delim(c byte) { if tok.Delim() != c { l.err = fmt.Errorf("expected delimiter '%q' but got '%q': %w", c, tok.Delim(), ErrStdlib) + + return + } + + // Track container nesting depth centrally: every '{' or '[' opens a level and + // every '}' or ']' closes one. This guards the mutually-recursive unmarshal + // routines (unmarshalObject/unmarshalArray/asInterface) against stack overflow. + switch c { + case '{', '[': + l.depth++ + if l.maxDepth > 0 && l.depth > l.maxDepth { + l.err = fmt.Errorf("maximum nesting depth of %d exceeded: %w", l.maxDepth, ErrStdlib) + } + case '}', ']': + l.depth-- } } @@ -318,3 +345,12 @@ func (l *jlexer) fetchToken() token { return token{Token: jtok} } + +func (l *jlexer) setBuf(data []byte) func() { + rdr, redeemBuf := poolOfReaders.BorrowWithRedeem() + l.buf = rdr + l.buf.buf = data + l.dec = stdjson.NewDecoder(l.buf) // cannot pool, not exposed by the encoding/json API + + return redeemBuf +} diff --git a/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/options.go b/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/options.go new file mode 100644 index 0000000000..f114f1f1a1 --- /dev/null +++ b/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/options.go @@ -0,0 +1,52 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package json + +// defaultMaxNestingDepth is the default maximum number of nested JSON containers +// ('{' or '[') that the ordered-JSON marshaler and unmarshaler will process before +// returning an error. +// +// It mirrors the limit enforced by the standard library's [encoding/json] decoder +// (see encoding/json's internal maxNestingDepth), which this adapter would otherwise +// not benefit from since it drives [encoding/json.Decoder.Token] directly. +const defaultMaxNestingDepth = 10000 + +// Option selects options for the stdlib adapter. +type Option func(o options) options + +type options struct { + maxNestingDepth int +} + +func buildOptions(o options, opts []Option) options { + for _, apply := range opts { + o = apply(o) + } + + return o +} + +// maxDepth returns the configured maximum nesting depth, or the default when unset. +func (o options) maxDepth() int { + if o.maxNestingDepth <= 0 { + return defaultMaxNestingDepth + } + + return o.maxNestingDepth +} + +// WithMaxNestingDepth sets the maximum number of nested JSON containers accepted +// when marshaling or unmarshaling ordered JSON. +// +// A value <= 0 selects the default (10,000). +// +// This guards against stack-overflow crashes on deeply nested (possibly adversarial) +// JSON documents or in-memory structures. +func WithMaxNestingDepth(depth int) Option { + return func(o options) options { + o.maxNestingDepth = depth + + return o + } +} diff --git a/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/ordered_map.go b/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/ordered_map.go index 54deef406f..a5a8f4b631 100644 --- a/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/ordered_map.go +++ b/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/ordered_map.go @@ -70,12 +70,11 @@ func (s MapSlice) MarshalJSON() ([]byte, error) { } func (s MapSlice) OrderedMarshalJSON() ([]byte, error) { - w := poolOfWriters.Borrow() - defer func() { - poolOfWriters.Redeem(w) - }() + w, redeem := poolOfWriters.BorrowWithRedeem() + defer redeem() + w.setBuf() - s.marshalObject(w) + s.marshalObject(w, 1) return w.BuildBytes() // this clones data, so it's okay to redeem the writer and its buffer } @@ -88,23 +87,38 @@ func (s *MapSlice) UnmarshalJSON(data []byte) error { } func (s *MapSlice) OrderedUnmarshalJSON(data []byte) error { - l := poolOfLexers.Borrow(data) - defer func() { - poolOfLexers.Redeem(l) - }() + return s.orderedUnmarshalJSON(data, defaultMaxNestingDepth) +} + +func (s *MapSlice) orderedUnmarshalJSON(data []byte, maxDepth int) error { + l, redeem := poolOfLexers.BorrowWithRedeem() + defer redeem() + + redeemBuf := l.setBuf(data) + defer redeemBuf() + + if maxDepth > 0 { + l.maxDepth = maxDepth + } s.unmarshalObject(l) return l.Error() } -func (s MapSlice) marshalObject(w *jwriter) { +func (s MapSlice) marshalObject(w *jwriter, depth int) { if s == nil { w.RawString("null") return } + if depth > defaultMaxNestingDepth { + w.SetErr(fmt.Errorf("maximum nesting depth of %d exceeded: %w", defaultMaxNestingDepth, ErrStdlib)) + + return + } + w.RawByte('{') if len(s) == 0 { @@ -113,11 +127,11 @@ func (s MapSlice) marshalObject(w *jwriter) { return } - s[0].marshalJSON(w) + s[0].marshalJSON(w, depth) for i := 1; i < len(s); i++ { w.RawByte(',') - s[i].marshalJSON(w) + s[i].marshalJSON(w, depth) } w.RawByte('}') @@ -162,9 +176,18 @@ type MapItem struct { Value any } -func (s MapItem) marshalJSON(w *jwriter) { +func (s MapItem) marshalJSON(w *jwriter, depth int) { w.String(s.Key) w.RawByte(':') + + // Recurse internally for nested ordered maps so the depth guard is not lost across + // the stdjson.Marshal boundary (which would reset it and re-enable stack overflow). + if nested, ok := s.Value.(MapSlice); ok { + nested.marshalObject(w, depth+1) + + return + } + w.Raw(stdjson.Marshal(s.Value)) } diff --git a/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/pool.go b/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/pool.go index 709b97c304..2f06b88e80 100644 --- a/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/pool.go +++ b/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/pool.go @@ -4,118 +4,15 @@ package json import ( - "encoding/json" - "sync" - "github.com/go-openapi/swag/jsonutils/adapters/ifaces" + "github.com/go-openapi/swag/pools" ) -type adaptersPool struct { - sync.Pool -} - -func (p *adaptersPool) Borrow() *Adapter { - return p.Get().(*Adapter) -} - -func (p *adaptersPool) BorrowIface() ifaces.Adapter { - return p.Get().(*Adapter) -} - -func (p *adaptersPool) Redeem(a *Adapter) { - p.Put(a) -} - -type writersPool struct { - sync.Pool -} - -func (p *writersPool) Borrow() *jwriter { - ptr := p.Get() - - jw := ptr.(*jwriter) - jw.Reset() - - return jw -} - -func (p *writersPool) Redeem(w *jwriter) { - p.Put(w) -} - -type lexersPool struct { - sync.Pool -} - -func (p *lexersPool) Borrow(data []byte) *jlexer { - ptr := p.Get() - - l := ptr.(*jlexer) - l.buf = poolOfReaders.Borrow(data) - l.dec = json.NewDecoder(l.buf) // cannot pool, not exposed by the encoding/json API - l.Reset() - - return l -} - -func (p *lexersPool) Redeem(l *jlexer) { - l.dec = nil - discard := l.buf - l.buf = nil - poolOfReaders.Redeem(discard) - p.Put(l) -} - -type readersPool struct { - sync.Pool -} - -func (p *readersPool) Borrow(data []byte) *bytesReader { - ptr := p.Get() - - b := ptr.(*bytesReader) - b.Reset() - b.buf = data - - return b -} - -func (p *readersPool) Redeem(b *bytesReader) { - p.Put(b) -} - var ( - poolOfAdapters = &adaptersPool{ - Pool: sync.Pool{ - New: func() any { - return NewAdapter() - }, - }, - } - - poolOfWriters = &writersPool{ - Pool: sync.Pool{ - New: func() any { - return newJWriter() - }, - }, - } - - poolOfLexers = &lexersPool{ - Pool: sync.Pool{ - New: func() any { - return newLexer(nil) - }, - }, - } - - poolOfReaders = &readersPool{ - Pool: sync.Pool{ - New: func() any { - return &bytesReader{} - }, - }, - } + poolOfAdapters = pools.New[Adapter]() + poolOfWriters = pools.NewRedeemable[jwriter]() + poolOfLexers = pools.NewRedeemable[jlexer]() + poolOfReaders = pools.NewRedeemable[bytesReader]() ) // BorrowAdapter borrows an [Adapter] from the pool, recycling already allocated instances. @@ -124,10 +21,12 @@ func BorrowAdapter() *Adapter { } // BorrowAdapterIface borrows a stdlib [Adapter] and converts it directly -// to [ifaces.Adapter]. This is useful to avoid further allocations when -// translating the concrete type into an interface. +// to [ifaces.Adapter]. +// +// This is useful to avoid further allocations when translating the concrete type into +// an interface. func BorrowAdapterIface() ifaces.Adapter { - return poolOfAdapters.BorrowIface() + return poolOfAdapters.Borrow() } // RedeemAdapter redeems an [Adapter] to the pool, so it may be recycled. diff --git a/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/register.go b/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/register.go index fc8818694e..0dec85425b 100644 --- a/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/register.go +++ b/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/register.go @@ -10,14 +10,22 @@ import ( "github.com/go-openapi/swag/jsonutils/adapters/ifaces" ) -func Register(dispatcher ifaces.Registrar) { +func Register(dispatcher ifaces.Registrar, opts ...Option) { t := reflect.TypeOf(Adapter{}) + var o options + o = buildOptions(o, opts) + dispatcher.RegisterFor( ifaces.RegistryEntry{ - Who: fmt.Sprintf("%s.%s", t.PkgPath(), t.Name()), - What: ifaces.AllCapabilities, - Constructor: BorrowAdapterIface, - Support: support, + Who: fmt.Sprintf("%s.%s", t.PkgPath(), t.Name()), + What: ifaces.AllCapabilities, + Constructor: func() ifaces.Adapter { + a := BorrowAdapter() + a.options = o + + return a + }, + Support: support, }) } diff --git a/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/writer.go b/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/writer.go index dc2325c1a3..c84e02cd83 100644 --- a/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/writer.go +++ b/vendor/github.com/go-openapi/swag/jsonutils/adapters/stdlib/json/writer.go @@ -14,17 +14,21 @@ type jwriter struct { err error } -func newJWriter() *jwriter { - buf := make([]byte, 0, sensibleBufferSize) - - return &jwriter{buf: bytes.NewBuffer(buf)} -} - func (w *jwriter) Reset() { - w.buf.Reset() + if w.buf != nil { + w.buf.Reset() + } w.err = nil } +// SetErr records the first error encountered while building the JSON output. +func (w *jwriter) SetErr(err error) { + if w.err != nil { + return + } + w.err = err +} + func (w *jwriter) RawString(s string) { if w.err != nil { return @@ -73,3 +77,12 @@ func (w *jwriter) BuildBytes() ([]byte, error) { return bytes.Clone(w.buf.Bytes()), nil } + +func (w *jwriter) setBuf() { + if w.buf != nil { + return + } + + buf := make([]byte, 0, sensibleBufferSize) + w.buf = bytes.NewBuffer(buf) +} diff --git a/vendor/github.com/go-openapi/swag/loading/doc.go b/vendor/github.com/go-openapi/swag/loading/doc.go index 8cf7bcb8b9..112c49968b 100644 --- a/vendor/github.com/go-openapi/swag/loading/doc.go +++ b/vendor/github.com/go-openapi/swag/loading/doc.go @@ -2,4 +2,28 @@ // SPDX-License-Identifier: Apache-2.0 // Package loading provides tools to load a file from http or from a local file system. +// +// # Security +// +// By default, the local loader reads any path the process can access, including absolute +// paths and "file://" URIs (for example "file:///etc/passwd"). Applications that pass +// untrusted input to [LoadFromFileOrHTTP], [JSONDoc] (or to downstream consumers such as +// go-openapi/loads) must confine local loading to a trusted directory. +// +// Use [WithRoot] to do so: it resolves every requested path relative to a chosen directory +// and rejects anything that escapes it, including via symlink. It is built on [os.Root] +// and is therefore safer than passing an [os.DirFS] to [WithFS], which does not block +// symlink escapes. +// +// Remote loading uses a standard [net/http] client. +// By default it follows redirects and performs no destination filtering — exactly like [net/http.DefaultClient]. +// +// A caller-controlled URL may therefore reach internal services or cloud metadata endpoints +// (server-side request forgery). +// +// This package does not, and should not, embed a network policy: +// when the URL may derive from untrusted input, supply a restricted client with +// [WithHTTPClient] whose transport rejects unwanted destinations at dial time — which also +// covers redirects and DNS rebinding. +// See the example on [LoadFromFileOrHTTP]. package loading diff --git a/vendor/github.com/go-openapi/swag/loading/loading.go b/vendor/github.com/go-openapi/swag/loading/loading.go index 269fb74d16..b06450c64d 100644 --- a/vendor/github.com/go-openapi/swag/loading/loading.go +++ b/vendor/github.com/go-openapi/swag/loading/loading.go @@ -17,7 +17,11 @@ import ( "strings" ) -// LoadFromFileOrHTTP loads the bytes from a file or a remote http server based on the path passed in +// LoadFromFileOrHTTP loads the bytes from a file or a remote http server based on the path passed in. +// +// Security: by default a local path is read with no confinement, so a caller-controlled path +// (including a "file://" URI or an absolute path) may read any file the process can access. +// When the path may derive from untrusted input, confine local loading with [WithRoot]. func LoadFromFileOrHTTP(pth string, opts ...Option) ([]byte, error) { o := optionsWithDefaults(opts) return LoadStrategy(pth, o.ReadFileFunc(), loadHTTPBytes(opts...), opts...)(pth) @@ -54,11 +58,14 @@ func LoadFromFileOrHTTP(pth string, opts ...Option) ([]byte, error) { // - `file:///c:/folder/file` becomes `C:\folder\file` // - `file://c:/folder/file` is tolerated (without leading `/`) and becomes `c:\folder\file` func LoadStrategy(pth string, local, remote func(string) ([]byte, error), opts ...Option) func(string) ([]byte, error) { - if strings.HasPrefix(pth, "http") { + if hasHTTPScheme(pth) { return remote } o := optionsWithDefaults(opts) _, isEmbedFS := o.fs.(embed.FS) + // any loader backed by an fs.FS or an os.Root consumes forward-slash paths on every + // platform, so it must not go through the windows-native file:// preprocessing below. + isFSBacked := o.fs != nil || o.root != "" return func(p string) ([]byte, error) { upth, err := url.PathUnescape(p) @@ -67,14 +74,22 @@ func LoadStrategy(pth string, local, remote func(string) ([]byte, error), opts . } cpth, hasPrefix := strings.CutPrefix(upth, "file://") - if !hasPrefix || isEmbedFS || runtime.GOOS != "windows" { + if !hasPrefix || isFSBacked || runtime.GOOS != "windows" { // crude processing: trim the file:// prefix. This leaves full URIs with a host with a (mostly) unexpected result // regular file path provided: just normalize slashes if isEmbedFS { - // on windows, we need to slash the path if FS is an embed FS. + // embed.FS always uses "/" as separator, even on windows, and rejects leading "./" or "/". return local(strings.TrimLeft(filepath.ToSlash(cpth), "./")) // remove invalid leading characters for embed FS } + if isFSBacked { + // other fs.FS (e.g. os.DirFS) and os.Root loaders also use "/" on every platform. + // Path confinement is enforced by the loader, not here: the os.Root loader rebases + // absolute in-root paths and rejects escaping paths ("..", out-of-root absolute, + // escaping symlinks); an fs.FS loader rejects what its file system does not allow. + return local(filepath.ToSlash(cpth)) + } + return local(filepath.FromSlash(cpth)) } @@ -113,6 +128,21 @@ func LoadStrategy(pth string, local, remote func(string) ([]byte, error), opts . } } +// hasHTTPScheme reports whether pth is an absolute URL with an http or https scheme, +// selecting the remote loader. The comparison is case-insensitive, as URL schemes are. +// +// Requiring the "://" separator (rather than a bare "http" prefix) avoids misrouting a +// local file whose name merely starts with "http" (e.g. "httpbin.json") to the remote loader. +func hasHTTPScheme(pth string) bool { + for _, scheme := range [...]string{"http://", "https://"} { + if len(pth) >= len(scheme) && strings.EqualFold(pth[:len(scheme)], scheme) { + return true + } + } + + return false +} + func loadHTTPBytes(opts ...Option) func(path string) ([]byte, error) { o := optionsWithDefaults(opts) diff --git a/vendor/github.com/go-openapi/swag/loading/options.go b/vendor/github.com/go-openapi/swag/loading/options.go index 6674ac69e6..539987547d 100644 --- a/vendor/github.com/go-openapi/swag/loading/options.go +++ b/vendor/github.com/go-openapi/swag/loading/options.go @@ -4,9 +4,12 @@ package loading import ( + "errors" "io/fs" "net/http" "os" + "path/filepath" + "runtime" "time" ) @@ -23,7 +26,8 @@ type ( } fileOptions struct { - fs fs.ReadFileFS + fs fs.ReadFileFS + root string // when non-empty, local reads are confined to this directory via os.Root } options struct { @@ -33,6 +37,28 @@ type ( ) func (fo fileOptions) ReadFileFunc() func(string) ([]byte, error) { + if fo.root != "" { + root := fo.root + + return func(name string) ([]byte, error) { + // os.Root only accepts paths relative to the root, but callers (and this package's + // own file:// handling) routinely produce absolute paths. Rebase an absolute path + // onto the root before handing it to os.Root. + rel, err := rootRelative(root, name) + if err != nil { + return nil, errors.Join(err, ErrLoader) + } + + r, err := os.OpenRoot(root) + if err != nil { + return nil, errors.Join(err, ErrLoader) + } + defer func() { _ = r.Close() }() + + return r.ReadFile(rel) + } + } + if fo.fs == nil { return os.ReadFile } @@ -40,6 +66,61 @@ func (fo fileOptions) ReadFileFunc() func(string) ([]byte, error) { return fo.fs.ReadFile } +// rootRelative expresses name as a path relative to root, so that it can be resolved by os.Root. +// +// A relative name is returned unchanged: os.Root confines it directly (including "../" traversal +// and symlink escapes, which it rejects at open time). +// +// An absolute name is rebased onto root. If it cannot be expressed relative to root — for +// example because it lives on a different volume on Windows — filepath.Rel returns an error, +// which is propagated so the read is rejected rather than silently escaping the root. An +// absolute path that lexically escapes root yields a "../" prefix here and is then rejected by +// os.Root. +func rootRelative(root, name string) (string, error) { + osName := toOSPath(name) + if !filepath.IsAbs(osName) { + return name, nil + } + + absRoot, err := filepath.Abs(toOSPath(root)) + if err != nil { + return "", err + } + + return filepath.Rel(absRoot, osName) +} + +// toOSPath converts a slash-separated path to an OS-native path. +// +// On Windows it additionally normalizes the "/C:/dir" form — a leading separator before a drive +// letter — that file URIs and URL-style path normalization (as performed by +// github.com/go-openapi/spec) produce. Without this, filepath.IsAbs does not recognize such a +// path as absolute and os.Root rejects an otherwise in-root target. This mirrors the file:// +// drive-letter handling in LoadStrategy, which the os.Root loader bypasses. +func toOSPath(p string) string { + p = filepath.FromSlash(p) + if runtime.GOOS == "windows" { + p = stripLeadingDriveSlash(p) + } + + return p +} + +// stripLeadingDriveSlash removes a leading separator that precedes a Windows drive letter, +// turning "\C:\dir" (from a "/C:/dir" URL path) into "C:\dir". Any other path is returned +// unchanged. It is pure (no OS dependency) so that its logic can be tested on any platform. +func stripLeadingDriveSlash(p string) string { + if len(p) >= 3 && (p[0] == '/' || p[0] == '\\') && p[2] == ':' && isASCIILetter(p[1]) { + return p[1:] + } + + return p +} + +func isASCIILetter(b byte) bool { + return ('a' <= b && b <= 'z') || ('A' <= b && b <= 'Z') +} + // WithTimeout sets a timeout for the remote file loader. // // The default timeout is 30s. @@ -87,8 +168,15 @@ func WithHTTPClient(client *http.Client) Option { // By default, the file system is the one provided by the os package. // // For example, this may be set to consume from an embedded file system, or a rooted FS. +// +// WithFS and [WithRoot] are mutually exclusive: the last one applied wins. +// +// Security note: a file system built from [os.DirFS] confines paths but does NOT protect +// against symlinks that escape the root. To load from a directory derived from untrusted +// input, prefer [WithRoot], which is symlink-escape resistant. func WithFS(filesystem fs.FS) Option { return func(o *options) { + o.root = "" // last-wins vs WithRoot if rfs, ok := filesystem.(fs.ReadFileFS); ok { o.fs = rfs @@ -98,6 +186,30 @@ func WithFS(filesystem fs.FS) Option { } } +// WithRoot confines local file loading to dir. +// +// Every requested path is resolved within dir. A relative path is resolved against dir; an +// absolute path is rebased onto dir (so a caller that normalizes references to absolute paths, +// such as github.com/go-openapi/spec, still resolves correctly). Any path that would escape dir +// — through ".." traversal, an absolute path pointing outside dir, or a symlink pointing outside +// dir — is rejected. This is built on [os.Root] and is therefore resistant to the symlink +// escapes that a plain [os.DirFS] does not prevent. +// +// WithRoot is the recommended option when loading specs from a location derived from +// untrusted input. It applies to local loading only and has no effect on remote +// (http/https) loading. WithRoot and [WithFS] are mutually exclusive: the last one applied +// wins. +// +// Note: [os.Root] confines path resolution but does not, by itself, protect against +// traversal of mount/bind boundaries, /proc special files, or device files. Point WithRoot +// at a directory that holds only the documents you intend to expose. +func WithRoot(dir string) Option { + return func(o *options) { + o.root = dir + o.fs = nil // last-wins vs WithFS + } +} + type readFileFS struct { fs.FS } diff --git a/vendor/github.com/go-openapi/swag/loading_iface.go b/vendor/github.com/go-openapi/swag/loading_iface.go index 27ec3fb8c3..78dadfccdf 100644 --- a/vendor/github.com/go-openapi/swag/loading_iface.go +++ b/vendor/github.com/go-openapi/swag/loading_iface.go @@ -80,11 +80,13 @@ func YAMLData(path string) (any, error) { // loadingOptionsWithDefaults bridges deprecated default settings that use package-level variables, // with the recommended use of loading.Option. func loadingOptionsWithDefaults(opts []loading.Option) []loading.Option { - o := []loading.Option{ + const defaultOptions = 3 + o := make([]loading.Option, 0, defaultOptions+len(opts)) + o = append(o, []loading.Option{ loading.WithTimeout(LoadHTTPTimeout), loading.WithBasicAuth(LoadHTTPBasicAuthUsername, LoadHTTPBasicAuthPassword), loading.WithCustomHeaders(LoadHTTPCustomHeaders), - } + }...) o = append(o, opts...) return o diff --git a/vendor/github.com/go-openapi/swag/pools/LICENSE b/vendor/github.com/go-openapi/swag/pools/LICENSE new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/vendor/github.com/go-openapi/swag/pools/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/go-openapi/swag/pools/README.md b/vendor/github.com/go-openapi/swag/pools/README.md new file mode 100644 index 0000000000..1966461cb5 --- /dev/null +++ b/vendor/github.com/go-openapi/swag/pools/README.md @@ -0,0 +1 @@ +# pools diff --git a/vendor/github.com/go-openapi/swag/pools/debug.go b/vendor/github.com/go-openapi/swag/pools/debug.go new file mode 100644 index 0000000000..94415e989e --- /dev/null +++ b/vendor/github.com/go-openapi/swag/pools/debug.go @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package pools + +// TB is the subset of [testing.TB] used by [AssertNoLeaks]. +// +// It is satisfied by *[testing.T] and *[testing.B]. +// +// A local interface is used (rather than importing "testing") so that the +// release build does not pull the testing package — and its flags — into +// production binaries. +type TB interface { + Helper() + Errorf(format string, args ...any) + Logf(format string, args ...any) +} diff --git a/vendor/github.com/go-openapi/swag/pools/debug_off.go b/vendor/github.com/go-openapi/swag/pools/debug_off.go new file mode 100644 index 0000000000..9163d6d60e --- /dev/null +++ b/vendor/github.com/go-openapi/swag/pools/debug_off.go @@ -0,0 +1,51 @@ +//go:build !poolsdebug + +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package pools + +// This is the release implementation of the pool instrumentation: it does +// nothing. +// +// tracker is an empty struct, so it adds no field to the pool types and its +// methods inline away to nothing. +// +// Build with -tags poolsdebug to get the instrumented variant (see +// debug_on.go). + +// debugBuild reports whether the pool instrumentation is compiled in (the +// poolsdebug tag). +const debugBuild = false + +// DebugBuild reports whether the pool instrumentation is compiled in (the +// poolsdebug build tag). +// +// It lets a test that must run in both modes skip the parts that are invalid +// under instrumentation — e.g. an allocation-count assertion, since the +// instrumented build allocates a per-borrow tracker. +const DebugBuild = debugBuild + +type tracker[T any] struct{} + +func (tracker[T]) register() {} + +func (tracker[T]) onBorrow(*T) {} + +func (tracker[T]) onRedeem(*T) {} + +func (tracker[T]) borrowRedeemer(_ *T, cached func()) func() { return cached } + +// AssertNoLeaks reports whether every borrowed object has been redeemed across +// all pools. +// +// It is only meaningful in the instrumented build (-tags poolsdebug). +// +// In a release build it is a no-op that always reports true, so the same test +// can run in both modes. +func AssertNoLeaks(TB) bool { return true } + +// ResetTracking clears all recorded borrow/redeem tracking. +// +// This is a no-op in a release build. +func ResetTracking() {} diff --git a/vendor/github.com/go-openapi/swag/pools/debug_on.go b/vendor/github.com/go-openapi/swag/pools/debug_on.go new file mode 100644 index 0000000000..64ccc57d38 --- /dev/null +++ b/vendor/github.com/go-openapi/swag/pools/debug_on.go @@ -0,0 +1,237 @@ +//go:build poolsdebug + +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package pools + +import ( + "fmt" + "runtime" + "sync" +) + +// This is the instrumented implementation of the pool tracking, enabled with +// -tags poolsdebug. +// +// Each pool carries a tracker that records, per recycled pointer, whether it is +// currently borrowed or redeemed, together with the call sites of the last +// borrow and redeem. +// It panics loudly (with those call sites) when it detects misuse: +// +// - a double redeem (the same object returned to the pool twice — corrupts sync.Pool); +// - for the redeemable pools, a redeem of a stale borrow (the slot was re-borrowed since — the +// ABA case the production atomic guard cannot catch), thanks to a per-borrow generation; +// - a redeem of an object the pool never handed out; +// - a borrow of an object still checked out (a symptom of an earlier double-Put). +// +// Borrowed-but-never-redeemed objects (leaks) are reported by [AssertNoLeaks]. + +// debugBuild reports whether the pool instrumentation is compiled in (the +// poolsdebug tag). +const debugBuild = true + +// DebugBuild reports whether the pool instrumentation is compiled in (the +// poolsdebug build tag). +// +// See the release-build doc for usage. +const DebugBuild = debugBuild + +type trackStatus uint8 + +const ( + trackBorrowed trackStatus = iota + 1 + trackRedeemed +) + +type trackEntry struct { + status trackStatus + gen uint64 // identifies the current borrow, to detect a redeem racing a re-borrow (ABA) + borrowedAt string + redeemedAt string +} + +type tracker[T any] struct { + mu sync.Mutex + entries map[*T]*trackEntry + nextGen uint64 +} + +func (t *tracker[T]) register() { + t.mu.Lock() + if t.entries == nil { + t.entries = make(map[*T]*trackEntry) + } + t.mu.Unlock() + + registerLeakChecker(t) +} + +// markBorrow records a borrow of ptr and returns its generation. +// +// Caller must hold no lock. +func (t *tracker[T]) markBorrow(ptr *T, site string) uint64 { + t.mu.Lock() + defer t.mu.Unlock() + + e := t.entries[ptr] + if e == nil { + e = &trackEntry{} + t.entries[ptr] = e + } else if e.status == trackBorrowed { + panic(fmt.Sprintf( + "pools: borrow of an object still checked out (borrowed at %s); "+ + "this usually means it was redeemed twice earlier", e.borrowedAt)) + } + + t.nextGen++ + e.status = trackBorrowed + e.gen = t.nextGen + e.borrowedAt = site + + return t.nextGen +} + +// markRedeem validates and records a redeem of ptr. gen is the borrow +// generation the caller is redeeming, or 0 to skip the ABA check (plain +// Pool[T], which has no per-borrow token). +func (t *tracker[T]) markRedeem(ptr *T, gen uint64, site string) { + t.mu.Lock() + defer t.mu.Unlock() + + e := t.entries[ptr] + switch { + case e == nil: + panic("pools: redeem of an object this pool never handed out") + case e.status != trackBorrowed: + panic(fmt.Sprintf("pools: double redeem (first redeemed at %s)", e.redeemedAt)) + case gen != 0 && e.gen != gen: + panic(fmt.Sprintf( + "pools: redeem of a stale borrow (the slot was re-borrowed at %s since this borrow); "+ + "a redeem is racing a re-borrow of the same slot (ABA)", e.borrowedAt)) + } + + e.status = trackRedeemed + e.redeemedAt = site +} + +const stackOffset = 3 + +func (t *tracker[T]) onBorrow(ptr *T) { + t.markBorrow(ptr, caller(stackOffset)) +} + +func (t *tracker[T]) onRedeem(ptr *T) { + t.markRedeem(ptr, 0, caller(stackOffset)) +} + +// borrowRedeemer records the borrow and returns a generation-stamped redeemer +// that validates the redeem (catching double-redeem and ABA) before delegating +// to the cached redeemer. +func (t *tracker[T]) borrowRedeemer(ptr *T, cached func()) func() { + gen := t.markBorrow(ptr, caller(stackOffset)) + + return func() { + t.markRedeem(ptr, gen, caller(stackOffset-1)) + cached() + } +} + +func (t *tracker[T]) checkLeaks(tb TB) bool { + t.mu.Lock() + defer t.mu.Unlock() + + ok := true + for _, e := range t.entries { + if e.status != trackRedeemed { + tb.Logf("pools: object borrowed but never redeemed (borrowed at %s)", e.borrowedAt) + ok = false + } + } + + return ok +} + +func (t *tracker[T]) resetTracking() { + t.mu.Lock() + t.entries = make(map[*T]*trackEntry) + t.nextGen = 0 + t.mu.Unlock() +} + +// leakChecker is the build-erased view of a tracker that the global registry +// holds, so trackers of different element types can be checked uniformly. +type leakChecker interface { + checkLeaks(tb TB) bool + resetTracking() +} + +var ( + registryMu sync.Mutex + registry []leakChecker +) + +func registerLeakChecker(c leakChecker) { + registryMu.Lock() + registry = append(registry, c) + registryMu.Unlock() +} + +// AssertNoLeaks reports whether every borrowed object has been redeemed across +// all pools created so far. +// +// It logs the borrow call site of each leaked object and fails tb when any are +// found. +// +// Typical use, with [ResetTracking] to isolate the test from earlier ones: +// +// func TestX(t *testing.T) { +// pools.ResetTracking() +// t.Cleanup(func() { pools.AssertNoLeaks(t) }) +// // ... exercise code that borrows/redeems ... +// } +func AssertNoLeaks(tb TB) bool { + tb.Helper() + registryMu.Lock() + defer registryMu.Unlock() + + ok := true + for _, c := range registry { + if !c.checkLeaks(tb) { + ok = false + } + } + if !ok { + tb.Errorf("pools: leaked pooled objects detected (borrowed but never redeemed)") + } + + return ok +} + +// ResetTracking clears all recorded borrow/redeem tracking across every pool. +// +// Call it at the start of a test so leaks from earlier tests are not attributed +// to it. +func ResetTracking() { + registryMu.Lock() + defer registryMu.Unlock() + + for _, c := range registry { + c.resetTracking() + } +} + +// caller returns "file:line" of the frame skip levels above caller itself. +func caller(skip int) string { + pc, _, _, ok := runtime.Caller(skip) + if !ok { + return "unknown" + } + fn := runtime.FuncForPC(pc) + if fn == nil { + return "unknown" + } + file, line := fn.FileLine(pc) + + return fmt.Sprintf("%s:%d", file, line) +} diff --git a/vendor/github.com/go-openapi/swag/pools/doc.go b/vendor/github.com/go-openapi/swag/pools/doc.go new file mode 100644 index 0000000000..395c24d29a --- /dev/null +++ b/vendor/github.com/go-openapi/swag/pools/doc.go @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +// Package pools provide utilities to recycle allocated objects. +// +// This package provides: +// +// - a generic [Pool] type that wraps [sync.Pool], +// - a [PoolRedeemable] variant that hands out a cached redeem closure, +// - a [PoolSlice] for recycling slices without juggling pointers. +// +// # Debug build +// +// Building with the "poolsdebug" tag (go test -tags poolsdebug ./...) turns on +// instrumentation that tracks every borrow and redeem and panics on misuse: +// +// - double redeem (including the A -> B -> A case for the redeemable pools), +// - redeem of a foreign object, +// - borrow of an object still checked out +// +// It reports the offending call sites. +// +// [AssertNoLeaks] then reports any object borrowed but never redeemed. +// +// The instrumentation is a no-op with zero overhead when the tag is absent. +package pools diff --git a/vendor/github.com/go-openapi/swag/pools/pools.go b/vendor/github.com/go-openapi/swag/pools/pools.go new file mode 100644 index 0000000000..d78ea5790a --- /dev/null +++ b/vendor/github.com/go-openapi/swag/pools/pools.go @@ -0,0 +1,410 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package pools + +import ( + "iter" + "slices" + "sync" + "sync/atomic" +) + +// Resettable is an interface for types that want to recycle a clean instance +// from a [Pool]. +// +// When T (or rather *T) implements [Resettable], the pool calls Reset on an +// instance both when it is redeemed and when it is borrowed: +// +// - on redeem, so that no references held by the instance are retained while it sits idle in the +// pool (which would pin a reference graph alive across a GC cycle); +// - on borrow, so that the next borrower receives a clean object regardless of how the instance +// reached the pool. +// +// Reset must be safe to call more than once on the same instance (it runs at +// least twice per cycle). +type Resettable interface { + Reset() +} + +// resetIfResettable calls Reset on v when *T implements [Resettable]. +func resetIfResettable[T any](v *T) { + if r, ok := any(v).(Resettable); ok { + r.Reset() + } +} + +// borrow state of a [redeemable] wrapper, used to detect double-redeem. +const ( + stateIdle uint32 = iota // sitting in the pool (or freshly created), not checked out + stateBorrowed // checked out by a borrower +) + +type redeemable[T any] struct { + inner *T + redeemer func() + // state guards against a double-redeem (the same wrapper Put into the pool + // twice, which would let one object be handed to two borrowers). + // + // It is set to stateBorrowed on borrow and atomically flipped back to + // stateIdle on redeem; a redeem that finds it already idle panics. + state atomic.Uint32 +} + +// redeemPanic is the message raised when a slot is redeemed while already idle. +const redeemPanic = "pools: " + + "double redeem detected (object already returned to the pool); " + + "a borrowed object must be redeemed exactly once" + +// Pool wraps a [sync.Pool] to make it available for any type. +// +// T must be the value type of the pooled object (e.g. Pool[bytes.Buffer]): +// [Pool.Borrow] returns a *T. Using a pointer type as T (e.g. +// Pool[*bytes.Buffer]) would yield a **T and is almost certainly a mistake. +type Pool[T any] struct { + pool sync.Pool + tracker tracker[T] // empty (zero-cost) unless built with the poolsdebug tag +} + +// PoolRedeemable wraps a [sync.Pool] to make it available for any type. +// +// It differs from [Pool] in the way objects are redeemed to the pool: borrowing +// also yields a cached redeem closure, so no closure is allocated at redeem +// time. +type PoolRedeemable[T any] struct { + pool sync.Pool + tracker tracker[redeemable[T]] // empty (zero-cost) unless built with the poolsdebug tag +} + +// New builds a new [Pool] to recycle allocations of type T explicitly using +// [Pool.Redeem] and the allocated pointer. +// +// Freshly allocated instances of type T are set to their zero value; like +// recycled instances they are reset (if [Resettable]) when borrowed, so +// [Pool.Borrow] always yields a clean object. +func New[T any]() *Pool[T] { + p := &Pool[T]{} + p.pool = sync.Pool{ + New: func() any { + return new(T) + }, + } + p.tracker.register() + + return p +} + +// NewRedeemable builds a new redeemable [Pool] to recycle allocations of type +// T, and use the inner redeemer to relinquish objects to the pool. +func NewRedeemable[T any]() *PoolRedeemable[T] { + p := &PoolRedeemable[T]{} + p.pool = sync.Pool{ + New: func() any { + r := &redeemable[T]{inner: new(T)} + r.redeemer = func() { + if !r.state.CompareAndSwap(stateBorrowed, stateIdle) { + panic(redeemPanic) + } + resetIfResettable(r.inner) + p.pool.Put(r) + } + + return r + }, + } + p.tracker.register() + + return p +} + +// Borrow an instance from the pool. +// +// If the type implements [Resettable], the returned instance is reset before +// being handed out, so it is always clean. +func (p *Pool[T]) Borrow() *T { + target := p.pool.Get().(*T) + resetIfResettable(target) + p.tracker.onBorrow(target) + + return target +} + +// Redeem a borrowed instance to the pool. +// +// A nil pointer is ignored (it would otherwise corrupt the pool: a typed-nil +// boxed into an interface is not the nil interface that [sync.Pool.Put] skips). +// +// The instance is reset (if it implements [Resettable]) before being returned +// to the pool. +// After calling Redeem, the caller must drop its reference to ptr: continuing +// to use it is a use-after-redeem bug. +// +// Unlike [PoolRedeemable], this plain pool holds no per-object state, so it +// cannot detect a double-redeem of the same pointer (which corrupts the pool). +// +// Prefer [PoolRedeemable] when you want that guard, or the debug build for full +// tracking. +func (p *Pool[T]) Redeem(ptr *T) { + if ptr == nil { + return + } + p.tracker.onRedeem(ptr) + resetIfResettable(ptr) + p.pool.Put(ptr) +} + +// BorrowWithRedeem borrows an instance from the pool and provides the +// corresponding redeem function. +// +// This is useful for instance to use with defer. +// +// The instance is reset (if it implements [Resettable]) both when borrowed and +// when the returned redeem closure is called. +// After calling the redeem closure, the caller must drop its reference to the +// returned instance. +// +// Calling the redeem closure more than once panics (see [redeemable.state]): a +// borrowed instance must be redeemed exactly once. +func (p *PoolRedeemable[T]) BorrowWithRedeem() (*T, func()) { + container := p.pool.Get().(*redeemable[T]) + container.state.Store(stateBorrowed) + resetIfResettable(container.inner) + + // In release builds borrowRedeemer returns container.redeemer unchanged (zero + // cost). + // Under the poolsdebug tag it returns a generation-stamped wrapper that tracks + // the borrow and detects double-redeem (incl. + // + // ABA), foreign-redeem and leaks. + return container.inner, p.tracker.borrowRedeemer(container, container.redeemer) +} + +// Slice is a struct that wraps a slice []T. +// +// This is useful to borrow and redeem slices from a pool, without having to +// constantly manipulate pointers to the slice. +// +// The wrapper holds the authoritative slice header. +// +// Its mutating methods ([Slice.Append], [Slice.Concat], [Slice.Grow]) return +// the current backing slice for convenience, so it reads as an idiomatic []T. +// +// But the returned slice is only a snapshot of the wrapper's state at that +// moment: if you keep it and grow it yourself with the builtin append and it +// reallocates, the new backing array lives only in your local copy and is NOT +// tracked by the wrapper — it will not be recycled when the wrapper is +// redeemed (and a later borrower would get the old, smaller array). +// +// Rule of thumb: it is fine to read or pass the returned []T to a consumer; but +// if you plan to grow the slice, keep calling the wrapper's methods so the +// growth is tracked and recycled. +type Slice[T any] struct { + length int + inner []T +} + +// Slice returns the inner slice. +// +// Treat the result as a read-only view (for ranging or passing to a consumer), +// valid until the next mutation or redeem. +// To grow or append, use the wrapper methods so the new backing array is +// tracked and recycled (see [Slice]). +func (s *Slice[T]) Slice() []T { + return s.inner +} + +// Grow the inner slice so it can accommodate at least size more elements +// without reallocating, and return the current backing slice. +// +// Growth is tracked by the wrapper, so the enlarged backing array is recycled +// on redeem. +// See [Slice] for the caveat about growing the returned slice yourself. +func (s *Slice[T]) Grow(size int) []T { + s.inner = slices.Grow(s.inner, size) + + return s.inner +} + +func (s *Slice[T]) Len() int { + return len(s.inner) +} + +func (s *Slice[T]) Cap() int { + return cap(s.inner) +} + +// Append elements to the inner slice and return the current backing slice. +// +// This should be preferred to the append builtin if you plan that the slice will +// grow and you want the newly allocated space to be tracked and recycled. +// See [Slice] for the caveat about growing the returned slice yourself. +func (s *Slice[T]) Append(elems ...T) []T { + s.inner = append(s.inner, elems...) + + return s.inner +} + +// Concat another slice to the inner slice and return the current backing slice. +// +// Unlike [slices.Concat], this reuses the inner slice's capacity instead of +// always allocating a fresh backing array. +// See [Slice] for the caveat about growing the returned slice yourself. +func (s *Slice[T]) Concat(slice []T) []T { + s.inner = append(s.inner, slice...) + + return s.inner +} + +// IndexedElems iterates over the inner slice. +func (s *Slice[T]) IndexedElems() iter.Seq2[int, T] { + return func(yield func(int, T) bool) { + for i, elem := range s.inner { + if !yield(i, elem) { + return + } + } + } +} + +// Reset the inner slice to its configured initial length, keeping allocated +// capacity. +// +// All elements are zeroed, so the pool never retains stale element references +// (which would keep a referenced graph alive for slices of pointers) and so a +// [WithLength] slice is handed out clean rather than carrying data from a +// previous borrower. +func (s *Slice[T]) Reset() { + clear(s.inner) + if s.length > cap(s.inner) { + s.inner = slices.Grow(s.inner[:0], s.length) + } + s.inner = s.inner[:s.length] +} + +// Clip removes unused capacity from the inner slice. +func (s *Slice[T]) Clip() { + s.inner = slices.Clip(s.inner) +} + +// resetWithCapacity discards the current backing array and replaces it with a +// fresh one of the configured length and the given capacity. +// +// It is used by a capacity-capped pool to stop recycling an oversized backing +// array (the old array is left for the GC). +func (s *Slice[T]) resetWithCapacity(capacity int) { + s.inner = make([]T, s.length, max(s.length, capacity)) +} + +// PoolSlice is a pool of [Slice[T]]. +// +// [PoolSlice.BorrowWithRedeem] will return an empty inner slice by default. +// This default may be altered using [WithMinimumCapacity]. +// +// Use [PoolSlice.BorrowWithSizeAndRedeem] or [Slice.Grow] to grow the capacity +// of the inner slice. +type PoolSlice[T any] struct { + // redeemable is held as an unexported field rather than embedded, so the + // underlying [PoolRedeemable] and its [sync.Pool] are not part of PoolSlice's + // public surface. + redeemable *PoolRedeemable[Slice[T]] +} + +// PoolSliceOption alters the default settings to allocate new pooled slices +type PoolSliceOption func(*poolSliceOptions) + +type poolSliceOptions struct { + minCapacity int + length int + maxCapacity int +} + +func WithMinimumCapacity(size int) PoolSliceOption { + return func(o *poolSliceOptions) { + o.minCapacity = size + } +} + +// WithMaxCapacity bounds the capacity of recycled slices. +// +// When a borrowed slice has grown past size at redeem time, its (oversized) +// backing array is discarded and replaced with a fresh one sized to the minimum +// capacity, instead of being recycled. +// +// This stops the pool from accumulating large backing arrays after an +// occasional large request, keeping the steady-state memory bounded. +// +// The trade-off: a workload that genuinely needs slices larger than size will +// reallocate on every cycle. +// Set size from the high-water mark you actually expect, not below it. +// A size of 0 (the default) means no cap: grown slices are recycled as-is. +func WithMaxCapacity(size int) PoolSliceOption { + return func(o *poolSliceOptions) { + o.maxCapacity = size + } +} + +// WithLength ensures that the borrowed slices have a fixed given initial +// length. +// +// By default, the borrowed slices are reset to length 0. +func WithLength(size int) PoolSliceOption { + return func(o *poolSliceOptions) { + o.length = size + } +} + +// NewPoolSlice builds a pool to recycle slices of type []T. +func NewPoolSlice[T any](opts ...PoolSliceOption) *PoolSlice[T] { + var o poolSliceOptions + for _, apply := range opts { + apply(&o) + } + + rp := &PoolRedeemable[Slice[T]]{} + rp.pool = sync.Pool{ + New: func() any { + s := &redeemable[Slice[T]]{ + inner: &Slice[T]{ + length: o.length, + inner: make([]T, o.length, max(o.length, o.minCapacity)), + }, + } + + s.redeemer = func() { + if !s.state.CompareAndSwap(stateBorrowed, stateIdle) { + panic(redeemPanic) + } + if o.maxCapacity > 0 && s.inner.Cap() > o.maxCapacity { + s.inner.resetWithCapacity(o.minCapacity) + } else { + s.inner.Reset() + } + rp.pool.Put(s) + } + + return s + }, + } + rp.tracker.register() + + return &PoolSlice[T]{redeemable: rp} +} + +// BorrowWithRedeem returns the slice wrapper and the redeem closure to +// relinquish the allocated wrapper. +// +// The wrapper is reset (elements zeroed, length restored) both on borrow and +// when the redeem closure is called. +// Calling the redeem closure more than once panics. +func (p *PoolSlice[T]) BorrowWithRedeem() (*Slice[T], func()) { + return p.redeemable.BorrowWithRedeem() +} + +// BorrowWithSizeAndRedeem borrows a slice []T from the pool and ensures that +// its capacity is at least the provided size. +func (p *PoolSlice[T]) BorrowWithSizeAndRedeem(size int) (*Slice[T], func()) { + s, redeem := p.BorrowWithRedeem() + s.Grow(size) + + return s, redeem +} diff --git a/vendor/github.com/go-openapi/swag/yamlutils/ordered_map.go b/vendor/github.com/go-openapi/swag/yamlutils/ordered_map.go index 3daf68dbba..24d951f8de 100644 --- a/vendor/github.com/go-openapi/swag/yamlutils/ordered_map.go +++ b/vendor/github.com/go-openapi/swag/yamlutils/ordered_map.go @@ -123,7 +123,7 @@ func (s YAMLMapSlice) MarshalYAML() (any, error) { var nodes []*yaml.Node for _, item := range s { - nn, err := json2yaml(item.Value) + nn, err := json2yaml(item.Value, 1) if err != nil { return nil, err } @@ -153,6 +153,17 @@ func (s YAMLMapSlice) MarshalYAML() (any, error) { // // It implements [yaml.Unmarshaler]. func (s *YAMLMapSlice) UnmarshalYAML(node *yaml.Node) error { + return s.unmarshalYAML(newYAMLWalker(), node, 0) +} + +// unmarshalYAML builds the slice from a [yaml.Node], tracking the recursion depth (against +// stack-overflow) and threading the [yamlWalker] so anchor/alias expansion stays bounded +// across the whole document. +func (s *YAMLMapSlice) unmarshalYAML(w *yamlWalker, node *yaml.Node, depth int) error { + if depth > defaultMaxNestingDepth { + return errMaxNestingDepth + } + if typeutils.IsNil(*s) { // allow to unmarshal with a simple var declaration (nil slice) *s = YAMLMapSlice{} @@ -167,13 +178,17 @@ func (s *YAMLMapSlice) UnmarshalYAML(node *yaml.Node) error { m = m[:0] for i := 0; i < len(node.Content); i += 2 { + if err := w.account(); err != nil { // account the key node + return err + } + var nmi YAMLMapItem k, err := yamlStringScalarC(node.Content[i]) if err != nil { return fmt.Errorf("unable to decode YAML map key: %w: %w", err, ErrYAML) } nmi.Key = k - v, err := yamlNode(node.Content[i+1]) + v, err := w.node(node.Content[i+1], depth+1) if err != nil { return fmt.Errorf("unable to process YAML map value for key %q: %w: %w", k, err, ErrYAML) } @@ -186,7 +201,11 @@ func (s *YAMLMapSlice) UnmarshalYAML(node *yaml.Node) error { return nil } -func json2yaml(item any) (*yaml.Node, error) { +func json2yaml(item any, depth int) (*yaml.Node, error) { + if depth > defaultMaxNestingDepth { + return nil, errMaxNestingDepth + } + if typeutils.IsNil(item) { return &yaml.Node{ Kind: yaml.ScalarNode, @@ -196,7 +215,7 @@ func json2yaml(item any) (*yaml.Node, error) { switch val := item.(type) { case ifaces.Ordered: - return orderedYAML(val) + return orderedYAML(val, depth) case map[string]any: var n yaml.Node @@ -209,7 +228,7 @@ func json2yaml(item any) (*yaml.Node, error) { for _, k := range keys { v := val[k] - childNode, err := json2yaml(v) + childNode, err := json2yaml(v, depth+1) if err != nil { return nil, err } @@ -225,7 +244,7 @@ func json2yaml(item any) (*yaml.Node, error) { var n yaml.Node n.Kind = yaml.SequenceNode for i := range val { - childNode, err := json2yaml(val[i]) + childNode, err := json2yaml(val[i], depth+1) if err != nil { return nil, err } @@ -297,11 +316,11 @@ func uintegerNode[T conv.Unsigned](val T) (*yaml.Node, error) { }, nil } -func orderedYAML[T ifaces.Ordered](val T) (*yaml.Node, error) { +func orderedYAML[T ifaces.Ordered](val T, depth int) (*yaml.Node, error) { var n yaml.Node n.Kind = yaml.MappingNode for key, value := range val.OrderedItems() { - childNode, err := json2yaml(value) + childNode, err := json2yaml(value, depth+1) if err != nil { return nil, err } diff --git a/vendor/github.com/go-openapi/swag/yamlutils/yaml.go b/vendor/github.com/go-openapi/swag/yamlutils/yaml.go index e3aff3c2fd..d4b5335f6f 100644 --- a/vendor/github.com/go-openapi/swag/yamlutils/yaml.go +++ b/vendor/github.com/go-openapi/swag/yamlutils/yaml.go @@ -12,13 +12,99 @@ import ( yaml "go.yaml.in/yaml/v3" ) +// defaultMaxNestingDepth caps the recursion depth of the YAML<->JSON transforms to +// guard against stack-overflow on deeply nested (possibly adversarial) input. +// +// It matches the limit enforced by go.yaml.in/yaml/v3's own parser and by +// encoding/json's decoder. +const defaultMaxNestingDepth = 10000 + +// Bounds on YAML anchor/alias expansion. +// +// go.yaml.in/yaml/v3 enforces these when decoding into Go values, but that guard is +// coupled to the library's own tree walk: when we decode into a low-level [yaml.Node] +// (to preserve key order) and expand aliases ourselves in [yamlWalker.node], we bypass +// it. We therefore reproduce it here, with the same constants and ratio schedule as the +// library's decoder (see go.yaml.in/yaml/v3 decode.go, "excessive aliasing"). +const ( + aliasCountThreshold = 100 + decodeCountThreshold = 1000 + + // 400,000 decode operations is ~500kb of dense object declarations, or + // ~5kb of dense object declarations with 10000% alias expansion. + aliasRatioRangeLow = 400000 + // 4,000,000 decode operations is ~5MB of dense object declarations. + aliasRatioRangeHigh = 4000000 + aliasRatioRange = float64(aliasRatioRangeHigh - aliasRatioRangeLow) + + // tolerated share of alias-driven decodes: from aliasRatioSmall (small/medium documents) + // down to aliasRatioLarge (very large ones), interpolated with slope aliasRatioSlope. + aliasRatioSmall = 0.99 + aliasRatioLarge = 0.10 + aliasRatioSlope = aliasRatioSmall - aliasRatioLarge +) + +var ( + // errMaxNestingDepth is returned when a document nests deeper than [defaultMaxNestingDepth]. + errMaxNestingDepth = fmt.Errorf("maximum nesting depth of %d exceeded: %w", defaultMaxNestingDepth, ErrYAML) + + // errExcessiveAliasing is returned when anchor/alias expansion is disproportionate to the + // size of the document, i.e. an "alias bomb". + errExcessiveAliasing = fmt.Errorf("document contains excessive aliasing: %w", ErrYAML) +) + +// allowedAliasRatio scales the tolerated share of alias-driven decode operations from 99% +// for small-to-medium documents down to 10% for very large ones, mirroring go.yaml.in/yaml/v3. +func allowedAliasRatio(decodeCount int) float64 { + switch { + case decodeCount <= aliasRatioRangeLow: + return aliasRatioSmall + case decodeCount >= aliasRatioRangeHigh: + return aliasRatioLarge + default: + return aliasRatioSmall - aliasRatioSlope*(float64(decodeCount-aliasRatioRangeLow)/aliasRatioRange) + } +} + +// yamlWalker carries the state needed to bound a single YAML-tree traversal: +// anchor/alias expansion accounting and cycle detection. +// +// A fresh walker is created per top-level conversion; it is threaded (not copied) through +// the whole recursive walk so its counters accumulate across the entire document. +type yamlWalker struct { + decodeCount int + aliasCount int + aliasDepth int + aliases map[*yaml.Node]bool // anchors currently being expanded, for cycle detection +} + +func newYAMLWalker() *yamlWalker { + return &yamlWalker{aliases: make(map[*yaml.Node]bool)} +} + +// account records one processed node and fails if alias expansion has become excessive. +func (w *yamlWalker) account() error { + w.decodeCount++ + if w.aliasDepth > 0 { + w.aliasCount++ + } + + if w.aliasCount > aliasCountThreshold && + w.decodeCount > decodeCountThreshold && + float64(w.aliasCount)/float64(w.decodeCount) > allowedAliasRatio(w.decodeCount) { + return errExcessiveAliasing + } + + return nil +} + // YAMLToJSON converts a YAML document into JSON bytes. // // Note: a YAML document is the output from a [yaml.Marshaler], e.g a pointer to a [yaml.Node]. // // [YAMLToJSON] is typically called after [BytesToYAMLDoc]. func YAMLToJSON(value any) (json.RawMessage, error) { - jm, err := transformData(value) + jm, err := transformData(value, 0) if err != nil { return nil, err } @@ -44,46 +130,73 @@ func BytesToYAMLDoc(data []byte) (any, error) { return &document, nil } -func yamlNode(root *yaml.Node) (any, error) { +func (w *yamlWalker) node(root *yaml.Node, depth int) (any, error) { + if depth > defaultMaxNestingDepth { + return nil, errMaxNestingDepth + } + if err := w.account(); err != nil { + return nil, err + } + switch root.Kind { case yaml.DocumentNode: - return yamlDocument(root) + return w.document(root, depth) case yaml.SequenceNode: - return yamlSequence(root) + return w.sequence(root, depth) case yaml.MappingNode: - return yamlMapping(root) + return w.mapping(root, depth) case yaml.ScalarNode: return yamlScalar(root) case yaml.AliasNode: - return yamlNode(root.Alias) + return w.alias(root, depth) default: return nil, fmt.Errorf("unsupported YAML node type: %v: %w", root.Kind, ErrYAML) } } -func yamlDocument(node *yaml.Node) (any, error) { +// alias resolves an anchor reference, expanding the anchored subtree. It detects cycles +// (an anchor whose expansion transitively references itself) and accounts the expansion +// against the alias-bomb budget via [yamlWalker.aliasDepth]. +func (w *yamlWalker) alias(node *yaml.Node, depth int) (any, error) { + if node.Alias == nil { + return nil, fmt.Errorf("invalid YAML alias node %q: %w", node.Value, ErrYAML) + } + if w.aliases[node.Alias] { + return nil, fmt.Errorf("anchor %q contains itself: %w", node.Value, ErrYAML) + } + + w.aliases[node.Alias] = true + w.aliasDepth++ + out, err := w.node(node.Alias, depth+1) + w.aliasDepth-- + delete(w.aliases, node.Alias) + + return out, err +} + +func (w *yamlWalker) document(node *yaml.Node, depth int) (any, error) { if len(node.Content) != 1 { return nil, fmt.Errorf("unexpected YAML Document node content length: %d: %w", len(node.Content), ErrYAML) } - return yamlNode(node.Content[0]) + return w.node(node.Content[0], depth+1) } -func yamlMapping(node *yaml.Node) (any, error) { +func (w *yamlWalker) mapping(node *yaml.Node, depth int) (any, error) { const sensibleAllocDivider = 2 // nodes concatenate (key,value) sequences m := make(YAMLMapSlice, len(node.Content)/sensibleAllocDivider) - if err := m.UnmarshalYAML(node); err != nil { + if err := m.unmarshalYAML(w, node, depth); err != nil { return nil, err } return m, nil } -func yamlSequence(node *yaml.Node) (any, error) { +func (w *yamlWalker) sequence(node *yaml.Node, depth int) (any, error) { s := make([]any, 0) for i := range len(node.Content) { - v, err := yamlNode(node.Content[i]) + v, err := w.node(node.Content[i], depth+1) if err != nil { return nil, fmt.Errorf("unable to decode YAML sequence value: %w: %w", err, ErrYAML) } @@ -174,12 +287,16 @@ func format(t any) (string, error) { } } -func transformData(input any) (out any, err error) { +func transformData(input any, depth int) (out any, err error) { + if depth > defaultMaxNestingDepth { + return nil, errMaxNestingDepth + } + switch in := input.(type) { case yaml.Node: - return yamlNode(&in) + return newYAMLWalker().node(&in, depth) case *yaml.Node: - return yamlNode(in) + return newYAMLWalker().node(in, depth) case map[any]any: o := make(YAMLMapSlice, 0, len(in)) for ke, va := range in { @@ -188,7 +305,7 @@ func transformData(input any) (out any, err error) { return nil, err } - v, ert := transformData(va) + v, ert := transformData(va, depth+1) if ert != nil { return nil, ert } @@ -200,7 +317,7 @@ func transformData(input any) (out any, err error) { len1 := len(in) o := make([]any, len1) for i := range len1 { - o[i], err = transformData(in[i]) + o[i], err = transformData(in[i], depth+1) if err != nil { return nil, err } diff --git a/vendor/github.com/go-openapi/validate/CONTRIBUTORS.md b/vendor/github.com/go-openapi/validate/CONTRIBUTORS.md index 7b79b765dc..40d877a117 100644 --- a/vendor/github.com/go-openapi/validate/CONTRIBUTORS.md +++ b/vendor/github.com/go-openapi/validate/CONTRIBUTORS.md @@ -4,12 +4,12 @@ | Total Contributors | Total Contributions | | --- | --- | -| 31 | 295 | +| 31 | 310 | | Username | All Time Contribution Count | All Commits | | --- | --- | --- | | @casualjim | 169 | | -| @fredbi | 58 | | +| @fredbi | 73 | | | @sttts | 11 | | | @youyuanwu | 9 | | | @keramix | 8 | | @@ -40,4 +40,4 @@ | @dadgar | 1 | | | @elakito | 1 | | - _this file was generated by the [Contributors GitHub Action](https://github.com/github/contributors)_ + _this file was generated by the [Contributors GitHub Action](https://github.com/github-community-projects/contributors)_ diff --git a/vendor/github.com/go-openapi/validate/README.md b/vendor/github.com/go-openapi/validate/README.md index fec42b7c6e..b814a2ef12 100644 --- a/vendor/github.com/go-openapi/validate/README.md +++ b/vendor/github.com/go-openapi/validate/README.md @@ -18,12 +18,9 @@ A validator for OpenAPI v2 specifications and JSON schema draft 4. * **2025-12-19** : new community chat on discord * a new discord community channel is available to be notified of changes and support users - * our venerable Slack channel remains open, and will be eventually discontinued on **2026-03-31** You may join the discord community by clicking the invite link on the discord badge (also above). [![Discord Channel][discord-badge]][discord-url] -Or join our Slack channel: [![Slack Channel][slack-logo]![slack-badge]][slack-url] - ## Status API is stable. @@ -75,9 +72,9 @@ This library ships under the [SPDX-License-Identifier: Apache-2.0](./LICENSE). ## Other documentation * [All-time contributors](./CONTRIBUTORS.md) -* [Contributing guidelines](.github/CONTRIBUTING.md) -* [Maintainers documentation](docs/MAINTAINERS.md) -* [Code style](docs/STYLE.md) +* [Contributing guidelines][contributing-doc-site] +* [Maintainers documentation][maintainers-doc-site] +* [Code style][style-doc-site] ## Cutting a new release @@ -108,11 +105,8 @@ Maintainers can cut a new release by either: [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/validate [godoc-url]: http://pkg.go.dev/github.com/go-openapi/validate -[slack-logo]: https://a.slack-edge.com/e6a93c1/img/icons/favicon-32.png -[slack-badge]: https://img.shields.io/badge/slack-blue?link=https%3A%2F%2Fgoswagger.slack.com%2Farchives%2FC04R30YM -[slack-url]: https://goswagger.slack.com/archives/C04R30YMU [discord-badge]: https://img.shields.io/discord/1446918742398341256?logo=discord&label=discord&color=blue -[discord-url]: https://discord.gg/twZ9BwT3 +[discord-url]: https://discord.gg/FfnFYaC3k5 [license-badge]: http://img.shields.io/badge/license-Apache%20v2-orange.svg @@ -122,3 +116,7 @@ Maintainers can cut a new release by either: [goversion-url]: https://github.com/go-openapi/validate/blob/master/go.mod [top-badge]: https://img.shields.io/github/languages/top/go-openapi/validate [commits-badge]: https://img.shields.io/github/commits-since/go-openapi/validate/latest + +[contributing-doc-site]: https://go-openapi.github.io/doc-site/contributing/contributing/index.html +[maintainers-doc-site]: https://go-openapi.github.io/doc-site/maintainers/index.html +[style-doc-site]: https://go-openapi.github.io/doc-site/contributing/style/index.html diff --git a/vendor/github.com/go-openapi/validate/helpers.go b/vendor/github.com/go-openapi/validate/helpers.go index 8a1a231283..7cc254e02e 100644 --- a/vendor/github.com/go-openapi/validate/helpers.go +++ b/vendor/github.com/go-openapi/validate/helpers.go @@ -242,9 +242,9 @@ func (h *paramHelper) resolveParam(path, method, operationID string, param *spec res := new(Result) isRef := param.Ref.String() != "" if s.spec.SpecFilePath() == "" { - err = spec.ExpandParameterWithRoot(param, s.spec.Spec(), nil) + err = spec.ExpandParameterWithOptions(param, s.spec.Spec(), nil, s.schemaOptions.expandOptions("")) } else { - err = spec.ExpandParameter(param, s.spec.SpecFilePath()) + err = spec.ExpandParameterWithOptions(param, nil, nil, s.schemaOptions.expandOptions(s.spec.SpecFilePath())) } if err != nil { // Safeguard // NOTE: we may enter here when the whole parameter is an unresolved $ref @@ -295,9 +295,9 @@ func (r *responseHelper) expandResponseRef( res := new(Result) if s.spec.SpecFilePath() == "" { // there is no physical document to resolve $ref in response - err = spec.ExpandResponseWithRoot(response, s.spec.Spec(), nil) + err = spec.ExpandResponseWithOptions(response, s.spec.Spec(), nil, s.schemaOptions.expandOptions("")) } else { - err = spec.ExpandResponse(response, s.spec.SpecFilePath()) + err = spec.ExpandResponseWithOptions(response, nil, nil, s.schemaOptions.expandOptions(s.spec.SpecFilePath())) } if err != nil { // Safeguard // NOTE: we may enter here when the whole response is an unresolved $ref. diff --git a/vendor/github.com/go-openapi/validate/schema.go b/vendor/github.com/go-openapi/validate/schema.go index b72a47bc33..706b7f5ef6 100644 --- a/vendor/github.com/go-openapi/validate/schema.go +++ b/vendor/github.com/go-openapi/validate/schema.go @@ -63,18 +63,18 @@ func newSchemaValidator(schema *spec.Schema, rootSchema any, root string, format rootSchema = schema } + if opts == nil { + opts = new(SchemaValidatorOptions) + } + if schema.ID != "" || schema.Ref.String() != "" || schema.Ref.IsRoot() { - err := spec.ExpandSchema(schema, rootSchema, nil) + err := spec.ExpandSchemaWithOptions(schema, rootSchema, nil, opts.expandOptions("")) if err != nil { msg := invalidSchemaProvidedMsg(err).Error() panic(msg) } } - if opts == nil { - opts = new(SchemaValidatorOptions) - } - var s *SchemaValidator if opts.recycleValidators { s = pools.poolOfSchemaValidators.BorrowValidator() diff --git a/vendor/github.com/go-openapi/validate/schema_option.go b/vendor/github.com/go-openapi/validate/schema_option.go index 3e1b882ed3..3ca489c0f5 100644 --- a/vendor/github.com/go-openapi/validate/schema_option.go +++ b/vendor/github.com/go-openapi/validate/schema_option.go @@ -3,6 +3,13 @@ package validate +import ( + "encoding/json" + + "github.com/go-openapi/spec" + "github.com/go-openapi/swag/loading" +) + // SchemaValidatorOptions defines optional rules for schema validation. type SchemaValidatorOptions struct { EnableObjectArrayTypeCheck bool @@ -10,6 +17,7 @@ type SchemaValidatorOptions struct { recycleValidators bool recycleResult bool skipSchemataResult bool + pathLoaderWithOptions func(string, ...loading.Option) (json.RawMessage, error) } // Option sets optional rules for schema validation. @@ -60,6 +68,23 @@ func WithSkipSchemataResult(enable bool) Option { } } +// WithPathLoader injects the document loader used to resolve remote and relative $ref while +// validating a schema or specification. It matches the option-aware loader signature of +// github.com/go-openapi/swag/loading (and go-openapi/loads). +// +// This lets validation resolve references through a caller-provided loader instead of the spec +// package's global default. The loader may carry any loading options — a custom HTTP client or +// timeout, authentication or custom headers, an embedded or rooted file system, and so on. +// +// One important use is confining loading of untrusted input: build the loader with loading.WithRoot +// (to confine local reads) and loading.WithHTTPClient (to restrict remote fetches), or use a +// restricted loader from go-openapi/loads. Left unset, the spec package default loader is used. +func WithPathLoader(loader func(string, ...loading.Option) (json.RawMessage, error)) Option { + return func(svo *SchemaValidatorOptions) { + svo.pathLoaderWithOptions = loader + } +} + // Options returns the current set of options. func (svo SchemaValidatorOptions) Options() []Option { return []Option{ @@ -68,5 +93,17 @@ func (svo SchemaValidatorOptions) Options() []Option { WithRecycleValidators(svo.recycleValidators), withRecycleResults(svo.recycleResult), WithSkipSchemataResult(svo.skipSchemataResult), + WithPathLoader(svo.pathLoaderWithOptions), + } +} + +// expandOptions builds the spec expand options for schema/$ref expansion during validation, +// carrying the injected loader (when set) so resolution can be confined. relativeBase is used for +// base-path-relative resolution; it is ignored by [spec.ExpandSchemaWithOptions], which derives the +// base from the root. +func (svo *SchemaValidatorOptions) expandOptions(relativeBase string) *spec.ExpandOptions { + return &spec.ExpandOptions{ + RelativeBase: relativeBase, + PathLoaderWithOptions: svo.pathLoaderWithOptions, } } diff --git a/vendor/github.com/go-openapi/validate/spec.go b/vendor/github.com/go-openapi/validate/spec.go index b85432f92b..d6a61eae29 100644 --- a/vendor/github.com/go-openapi/validate/spec.go +++ b/vendor/github.com/go-openapi/validate/spec.go @@ -25,6 +25,9 @@ import ( // // Returns an error flattening in a single standard error, all validation messages. // +// Options are forwarded to the underlying [SpecValidator]; in particular [WithPathLoader] injects a +// confined document loader for validating a specification from an untrusted source. +// // - Proposal for enhancement: $ref should not have siblings // - Proposal for enhancement: make sure documentation reflects all checks and warnings // - Proposal for enhancement: check on discriminators @@ -35,8 +38,8 @@ import ( // - Proposal for enhancement: check on required properties to support anyOf, allOf, oneOf // // NOTE: SecurityScopes are maps: no need to check uniqueness. -func Spec(doc *loads.Document, formats strfmt.Registry) error { - errs, _ /*warns*/ := NewSpecValidator(doc.Schema(), formats).Validate(doc) +func Spec(doc *loads.Document, formats strfmt.Registry, options ...Option) error { + errs, _ /*warns*/ := NewSpecValidator(doc.Schema(), formats, options...).Validate(doc) if errs.HasErrors() { return errors.CompositeValidationError(errs.Errors...) } @@ -55,14 +58,20 @@ type SpecValidator struct { } // NewSpecValidator creates a new swagger spec validator instance. -func NewSpecValidator(schema *spec.Schema, formats strfmt.Registry) *SpecValidator { - // schema options that apply to all called validators +// +// Options apply to the schema validators used internally. In particular, [WithPathLoader] injects +// the document loader used to resolve $ref while validating the specification — set a confined +// loader when validating a specification from an untrusted source (see the package "Security" +// notes on [WithPathLoader]). +func NewSpecValidator(schema *spec.Schema, formats strfmt.Registry, options ...Option) *SpecValidator { + // schema options that apply to all called validators: built-in defaults first, then + // caller-supplied options (which may add a loader or override a default). schemaOptions := new(SchemaValidatorOptions) - for _, o := range []Option{ + for _, o := range append([]Option{ SwaggerSchema(true), WithRecycleValidators(true), // withRecycleResults(true), - } { + }, options...) { o(schemaOptions) } @@ -146,7 +155,8 @@ func (s *SpecValidator) Validate(data any) (*Result, *Result) { errs.Merge(s.validateNonEmptyPathParamNames()) // errs.Merge(s.validateRefNoSibling()) // warning only - errs.Merge(s.validateReferenced()) // warning only + errs.Merge(s.validateReferenced()) // warning only + errs.Merge(s.validateDubiousRefs()) // warning only return errs, warnings } @@ -251,7 +261,7 @@ func (s *SpecValidator) validateDuplicatePropertyNames() *Result { func (s *SpecValidator) resolveRef(ref *spec.Ref) (*spec.Schema, error) { if s.spec.SpecFilePath() != "" { - return spec.ResolveRefWithBase(s.spec.Spec(), ref, &spec.ExpandOptions{RelativeBase: s.spec.SpecFilePath()}) + return spec.ResolveRefWithBase(s.spec.Spec(), ref, s.schemaOptions.expandOptions(s.spec.SpecFilePath())) } // NOTE: it looks like with the new spec resolver, this code is now unrecheable return spec.ResolveRef(s.spec.Spec(), ref) @@ -553,8 +563,12 @@ DEFINITIONS: if schema.Required != nil { // Safeguard for _, pn := range schema.Required { red := s.validateRequiredProperties(pn, d, &schema) //#nosec + // NOTE: capture validity before merging: Merge may redeem `red` to the + // pool (wantsRedeemOnMerge), after which reading it races with a concurrent + // BorrowResult().cleared() in another goroutine sharing the global pool. + isValid := red.IsValid() res.Merge(red) - if !red.IsValid() && !s.Options.ContinueOnErrors { + if !isValid && !s.Options.ContinueOnErrors { break DEFINITIONS // there is an error, let's stop that bleeding } } @@ -784,7 +798,10 @@ func (s *SpecValidator) validateReferencesValid() *Result { // NOTE: with default settings, loads.Document.Expanded() // stops on first error. Anyhow, the expand option to continue // on errors fails to report errors at all. - exp, err := s.spec.Expanded() + // + // Pass the injected loader (if any) so whole-spec expansion is confined too. When no loader + // is set, this is a no-op: loads falls back to the document's own loader. + exp, err := s.spec.Expanded(s.schemaOptions.expandOptions("")) if err != nil { res.AddErrors(unresolvedReferencesMsg(err)) } diff --git a/vendor/github.com/go-openapi/validate/spec_messages.go b/vendor/github.com/go-openapi/validate/spec_messages.go index 42ce360285..eeb8a86951 100644 --- a/vendor/github.com/go-openapi/validate/spec_messages.go +++ b/vendor/github.com/go-openapi/validate/spec_messages.go @@ -177,6 +177,18 @@ const ( // UnusedResponseWarning ... UnusedResponseWarning = "response %q is not used anywhere" + // DubiousAbsoluteRefWarning flags a $ref pointing to an absolute local file location that escapes the + // spec's base path. Absolute local references are legitimate when they stay beneath the base path + // (flattening/expansion introduces such anchors for cyclical $refs), but an absolute reference that + // escapes the base path - or a file:// reference in a spec with no known base - may indicate an + // unsafe or adversarial spec. + DubiousAbsoluteRefWarning = "$ref %q points to an absolute or local file location that escapes the spec's base path: this may be unsafe with adversarial specs" + + // DubiousMultipleHostsWarning flags a spec whose remote $refs resolve to several distinct hosts. + // A single consistent remote host is common and legitimate; references spread across multiple hosts + // may indicate an unsafe or adversarial spec. + DubiousMultipleHostsWarning = "$ref values point to %d distinct remote hosts (%s): a spec referencing multiple hosts may be unsafe" + InvalidObject = "expected an object in %q.%s" ) @@ -404,3 +416,11 @@ func someParametersBrokenMsg(path, method, operationID string) errors.Error { func refShouldNotHaveSiblingsMsg(path, operationID string) errors.Error { return errors.New(errors.CompositeErrorCode, RefShouldNotHaveSiblingsWarning, operationID, path) } + +func dubiousAbsoluteRefMsg(ref string) errors.Error { + return errors.New(errors.CompositeErrorCode, DubiousAbsoluteRefWarning, ref) +} + +func dubiousMultipleHostsMsg(count int, hosts string) errors.Error { + return errors.New(errors.CompositeErrorCode, DubiousMultipleHostsWarning, count, hosts) +} diff --git a/vendor/github.com/go-openapi/validate/spec_ref_warnings.go b/vendor/github.com/go-openapi/validate/spec_ref_warnings.go new file mode 100644 index 0000000000..49c72314c9 --- /dev/null +++ b/vendor/github.com/go-openapi/validate/spec_ref_warnings.go @@ -0,0 +1,209 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package validate + +import ( + "net/url" + "path" + "sort" + "strings" + + "github.com/go-openapi/spec" +) + +// minDistinctHostsToWarn is the number of distinct remote hosts among $refs at or above which +// Rule 2 emits a host-spread warning. A single consistent remote host is legitimate. +const minDistinctHostsToWarn = 2 + +// validateDubiousRefs emits warnings (never errors) when $ref locations match patterns +// that may indicate an unsafe or adversarial spec. It inspects refs as authored, on the +// UNEXPANDED spec, so it must run before expansion flattens them away. +// +// Two rules are applied over s.analyzer.AllRefs(): +// +// - Rule 1 (absolute local escape): a $ref pointing to an absolute local file location +// (file:// scheme, a Unix absolute path, or a Windows drive path such as C:\) is dubious +// UNLESS it stays beneath the spec's base path. Absolute refs beneath the base are +// legitimate: flattening/expansion in go-openapi/spec and analysis introduces absolute +// anchors to resolve cyclical $refs. Relative and fragment-only refs are always exempt. +// +// - Rule 2 (host spread): when remote (http/https, or protocol-relative) refs resolve to +// two or more distinct hosts, a single aggregate warning lists them. A single consistent +// remote host is common and legitimate, so it is not flagged. +// +// All findings are warnings: they do not affect validity (see Result.IsValid). +func (s *SpecValidator) validateDubiousRefs() *Result { + res := pools.poolOfResults.BorrowResult() + + baseDir, hasBase := s.localBaseDir() + + remoteHosts := make(map[string]struct{}) + for _, r := range s.analyzer.AllRefs() { + u := r.GetURL() + if u == nil { // Safeguard: a valid spec always yields parseable refs + continue + } + + // Rule 1: absolute local reference escaping the base path. + if refPath, isLocalAbs := absoluteLocalRefPath(r, u); isLocalAbs { + if !hasBase || !isBeneathBase(refPath, baseDir) { + res.AddWarnings(dubiousAbsoluteRefMsg(r.String())) + } + continue + } + + // Rule 2: gather remote hosts (http/https and protocol-relative //host/...). + if host := remoteRefHost(u); host != "" { + remoteHosts[host] = struct{}{} + } + } + + if len(remoteHosts) >= minDistinctHostsToWarn { + hosts := make([]string, 0, len(remoteHosts)) + for h := range remoteHosts { + hosts = append(hosts, h) + } + sort.Strings(hosts) + res.AddWarnings(dubiousMultipleHostsMsg(len(hosts), strings.Join(hosts, ", "))) + } + + return res +} + +// absoluteLocalRefPath reports whether r is an absolute LOCAL file reference and, if so, +// returns the cleaned path it points to (without scheme/fragment, drive letter lower-cased). +// +// Classification order matters (see the empirical jsonreference flag behavior): +// - file:// scheme is local, including UNC file://host/share (inherently dubious). +// - a non-empty Host with no file scheme means remote (http/https or protocol-relative +// //host/path) - NOT local; handled by Rule 2. This must be checked before the Unix +// branch, because protocol-relative refs also set HasFullFilePath. +// - len(u.Scheme) == 1 is a Windows drive path (C:\ or C:/), whose drive+path land in +// Scheme/Opaque/Path rather than Path. Checked before the Unix branch because C:/x also +// sets HasFullFilePath, and reconstructed from the authored ref string to keep the drive. +// - !r.HasFullURL && r.HasFullFilePath is a plain Unix absolute path (/abs/models.json). +// +// Relative (./x.json) and fragment-only (#/definitions/X) refs return false. +func absoluteLocalRefPath(r spec.Ref, u *url.URL) (string, bool) { + switch { + case r.HasFileScheme: + return fileRefPath(u), true + case u.Host != "": + // Remote (http/https) or protocol-relative //host/path: handled by Rule 2. + return "", false + case len(u.Scheme) == 1: + // Windows drive letter: reconstruct from the authored ref string. + return cleanRefPath(r.String()), true + case !r.HasFullURL && r.HasFullFilePath: + return cleanRefPath(u.Path), true + default: + return "", false + } +} + +// remoteRefHost returns the host of a remote reference (http/https), or of a protocol-relative +// reference (//host/path). It returns "" for local and fragment-only refs. file:// hosts (UNC) +// are deliberately excluded: those are handled as local-absolute refs by Rule 1. +func remoteRefHost(u *url.URL) string { + switch u.Scheme { + case "http", "https": + return u.Host + case "": + // Protocol-relative //host/path: empty scheme but a host is present. + return u.Host + default: + return "" + } +} + +// localBaseDir returns the directory of the spec file, slash-normalized, when the spec was +// loaded from a local path. It returns ok=false when the base is unknown (in-memory spec) or +// remote (http/https), in which case absolute-local refs cannot be proven beneath a base and +// are treated as dubious. +func (s *SpecValidator) localBaseDir() (string, bool) { + specPath := s.spec.SpecFilePath() + if specPath == "" { + return "", false + } + + // Strip a file:// scheme if present; reject remote bases. + if u, err := url.Parse(specPath); err == nil && u.Scheme != "" { + switch { + case u.Scheme == "file": + specPath = u.Path + case len(u.Scheme) == 1: // Windows drive letter, treat as local + // keep specPath as-is (authored path) + default: // http, https, ... : no local base + return "", false + } + } + + return path.Dir(cleanRefPath(specPath)), true +} + +// isBeneathBase reports whether the cleaned target path is located within baseDir, i.e. it does +// not escape baseDir via "..". Comparison is purely lexical on cleaned paths, which is sufficient +// (and cross-platform safe) for a non-fatal warning. Both sides are expected to already be +// cleanRefPath-normalized (slashes, drive-letter case). +func isBeneathBase(target, baseDir string) bool { + if baseDir == "" { + return false + } + if target == baseDir { + return true + } + if !strings.HasSuffix(baseDir, "/") { + baseDir += "/" + } + return strings.HasPrefix(target, baseDir) +} + +// fileRefPath extracts the local path a file:// reference points to, accounting for the way +// Windows file URLs parse: +// - file:///abs/x -> /abs/x (empty host) +// - file:///C:/dir/x -> /c:/dir/x (empty host; drive sits in the path) +// - file://D:/a/x -> d:/a/x (drive letter lands in Host, rejoin it) +// - file://host/share -> /host/share/x (real UNC host kept visible so it cannot match a +// local base and stays flagged as dubious) +func fileRefPath(u *url.URL) string { + switch { + case u.Host == "": + return cleanRefPath(u.Path) + case isDriveHost(u.Host): + // Windows path authored as file://D:/... : the drive landed in Host (e.g. "d:"). + return cleanRefPath(u.Host + u.Path) + default: + // Real remote/UNC host: keep it in the path so it never matches a local base. + return cleanRefPath("//" + u.Host + u.Path) + } +} + +// isDriveHost reports whether a URL host is actually a Windows drive letter (e.g. "d:"), which +// happens when a Windows path is authored as a two-slash file URL: file://D:/path. +func isDriveHost(host string) bool { + h := strings.TrimSuffix(host, ":") + if len(h) != 1 { + return false + } + c := h[0] + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') +} + +// cleanRefPath normalizes a ref or base path for lexical comparison: backslashes to forward +// slashes, path.Clean, and a lower-cased leading Windows drive letter (matching the behavior of +// go-openapi/spec's normalizer). Plain Unix paths are unaffected, preserving case-sensitivity. +func cleanRefPath(p string) string { + p = path.Clean(strings.ReplaceAll(p, `\`, `/`)) + switch { + case len(p) >= 2 && p[1] == ':': + // drive-letter form: C:/dir -> c:/dir + p = strings.ToLower(p[:1]) + p[1:] + case len(p) >= 3 && p[0] == '/' && p[2] == ':': + // slash-prefixed drive form from canonical file:// URLs: /C:/dir -> c:/dir. + // The leading slash is dropped so this matches the base path derived from + // SpecFilePath (which has no leading slash), and the bare-drive form. + p = strings.ToLower(p[1:2]) + p[2:] + } + return p +} diff --git a/vendor/github.com/golang-jwt/jwt/v5/README.md b/vendor/github.com/golang-jwt/jwt/v5/README.md index 0bb636f222..17e7ea766e 100644 --- a/vendor/github.com/golang-jwt/jwt/v5/README.md +++ b/vendor/github.com/golang-jwt/jwt/v5/README.md @@ -140,11 +140,12 @@ A common use case would be integrating with different 3rd party signature providers, like key management services from various cloud providers or Hardware Security Modules (HSMs) or to implement additional standards. -| Extension | Purpose | Repo | -| --------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------------ | -| GCP | Integrates with multiple Google Cloud Platform signing tools (AppEngine, IAM API, Cloud KMS) | https://github.com/someone1/gcp-jwt-go | -| AWS | Integrates with AWS Key Management Service, KMS | https://github.com/matelang/jwt-go-aws-kms | -| JWKS | Provides support for JWKS ([RFC 7517](https://datatracker.ietf.org/doc/html/rfc7517)) as a `jwt.Keyfunc` | https://github.com/MicahParks/keyfunc | +| Extension | Purpose | Repo | +| --------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------- | +| GCP | Integrates with multiple Google Cloud Platform signing tools (AppEngine, IAM API, Cloud KMS) | https://github.com/someone1/gcp-jwt-go | +| AWS | Integrates with AWS Key Management Service, KMS | https://github.com/matelang/jwt-go-aws-kms | +| JWKS | Provides support for JWKS ([RFC 7517](https://datatracker.ietf.org/doc/html/rfc7517)) as a `jwt.Keyfunc` | https://github.com/MicahParks/keyfunc | +| TPM | Integrates with Trusted Platform Module (TPM) | https://github.com/salrashid123/golang-jwt-tpm | *Disclaimer*: Unless otherwise specified, these integrations are maintained by third parties and should not be considered as a primary offer by any of the diff --git a/vendor/github.com/golang-jwt/jwt/v5/VERSION_HISTORY.md b/vendor/github.com/golang-jwt/jwt/v5/VERSION_HISTORY.md index b5039e49c1..e39ca8efcb 100644 --- a/vendor/github.com/golang-jwt/jwt/v5/VERSION_HISTORY.md +++ b/vendor/github.com/golang-jwt/jwt/v5/VERSION_HISTORY.md @@ -97,7 +97,7 @@ Backwards compatible API change that was missed in 2.0.0. There were two major reasons for breaking backwards compatibility with this update. The first was a refactor required to expand the width of the RSA and HMAC-SHA signing implementations. There will likely be no required code changes to support this change. -The second update, while unfortunately requiring a small change in integration, is required to open up this library to other signing methods. Not all keys used for all signing methods have a single standard on-disk representation. Requiring `[]byte` as the type for all keys proved too limiting. Additionally, this implementation allows for pre-parsed tokens to be reused, which might matter in an application that parses a high volume of tokens with a small set of keys. Backwards compatibilty has been maintained for passing `[]byte` to the RSA signing methods, but they will also accept `*rsa.PublicKey` and `*rsa.PrivateKey`. +The second update, while unfortunately requiring a small change in integration, is required to open up this library to other signing methods. Not all keys used for all signing methods have a single standard on-disk representation. Requiring `[]byte` as the type for all keys proved too limiting. Additionally, this implementation allows for pre-parsed tokens to be reused, which might matter in an application that parses a high volume of tokens with a small set of keys. Backwards compatibility has been maintained for passing `[]byte` to the RSA signing methods, but they will also accept `*rsa.PublicKey` and `*rsa.PrivateKey`. It is likely the only integration change required here will be to change `func(t *jwt.Token) ([]byte, error)` to `func(t *jwt.Token) (interface{}, error)` when calling `Parse`. diff --git a/vendor/github.com/golang-jwt/jwt/v5/parser.go b/vendor/github.com/golang-jwt/jwt/v5/parser.go index 054c7eb6ff..5f803965c3 100644 --- a/vendor/github.com/golang-jwt/jwt/v5/parser.go +++ b/vendor/github.com/golang-jwt/jwt/v5/parser.go @@ -76,13 +76,6 @@ func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyf } } - // Decode signature - token.Signature, err = p.DecodeSegment(parts[2]) - if err != nil { - return token, newError("could not base64 decode signature", ErrTokenMalformed, err) - } - text := strings.Join(parts[0:2], ".") - // Lookup key(s) if keyFunc == nil { // keyFunc was not provided. short circuiting validation @@ -94,11 +87,14 @@ func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyf return token, newError("error while executing keyfunc", ErrTokenUnverifiable, err) } + // Join together header and claims in order to verify them with the signature + text := strings.Join(parts[0:2], ".") switch have := got.(type) { case VerificationKeySet: if len(have.Keys) == 0 { return token, newError("keyfunc returned empty verification key set", ErrTokenUnverifiable) } + // Iterate through keys and verify signature, skipping the rest when a match is found. // Return the last error if no match is found. for _, key := range have.Keys { @@ -131,7 +127,7 @@ func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyf return token, nil } -// ParseUnverified parses the token but doesn't validate the signature. +// ParseUnverified parses the token but does not validate the signature. // // WARNING: Don't use this method unless you know what you're doing. // @@ -146,7 +142,7 @@ func (p *Parser) ParseUnverified(tokenString string, claims Claims) (token *Toke token = &Token{Raw: tokenString} - // parse Header + // Parse Header var headerBytes []byte if headerBytes, err = p.DecodeSegment(parts[0]); err != nil { return token, parts, newError("could not base64 decode header", ErrTokenMalformed, err) @@ -155,7 +151,7 @@ func (p *Parser) ParseUnverified(tokenString string, claims Claims) (token *Toke return token, parts, newError("could not JSON decode header", ErrTokenMalformed, err) } - // parse Claims + // Parse Claims token.Claims = claims claimBytes, err := p.DecodeSegment(parts[1]) @@ -196,6 +192,12 @@ func (p *Parser) ParseUnverified(tokenString string, claims Claims) (token *Toke return token, parts, newError("signing method (alg) is unspecified", ErrTokenUnverifiable) } + // Parse token signature + token.Signature, err = p.DecodeSegment(parts[2]) + if err != nil { + return token, parts, newError("could not base64 decode signature", ErrTokenMalformed, err) + } + return token, parts, nil } @@ -216,7 +218,7 @@ func splitToken(token string) ([]string, bool) { parts[1] = claims // One more cut to ensure the signature is the last part of the token and there are no more // delimiters. This avoids an issue where malicious input could contain additional delimiters - // causing unecessary overhead parsing tokens. + // causing unnecessary overhead parsing tokens. signature, _, unexpected := strings.Cut(remain, tokenDelimiter) if unexpected { return nil, false diff --git a/vendor/github.com/golang-jwt/jwt/v5/parser_option.go b/vendor/github.com/golang-jwt/jwt/v5/parser_option.go index 431573557b..af42fd3a73 100644 --- a/vendor/github.com/golang-jwt/jwt/v5/parser_option.go +++ b/vendor/github.com/golang-jwt/jwt/v5/parser_option.go @@ -3,9 +3,7 @@ package jwt import "time" // ParserOption is used to implement functional-style options that modify the -// behavior of the parser. To add new options, just create a function (ideally -// beginning with With or Without) that returns an anonymous function that takes -// a *Parser type as input and manipulates its configuration accordingly. +// behavior of the parser. type ParserOption func(*Parser) // WithValidMethods is an option to supply algorithm methods that the parser @@ -66,6 +64,14 @@ func WithExpirationRequired() ParserOption { } } +// WithNotBeforeRequired returns the ParserOption to make nbf claim required. +// By default nbf claim is optional. +func WithNotBeforeRequired() ParserOption { + return func(p *Parser) { + p.validator.requireNbf = true + } +} + // WithAudience configures the validator to require any of the specified // audiences in the `aud` claim. Validation will fail if the audience is not // listed in the token or the `aud` claim is missing. diff --git a/vendor/github.com/golang-jwt/jwt/v5/token.go b/vendor/github.com/golang-jwt/jwt/v5/token.go index 3f71558888..d9f6c9d25f 100644 --- a/vendor/github.com/golang-jwt/jwt/v5/token.go +++ b/vendor/github.com/golang-jwt/jwt/v5/token.go @@ -32,8 +32,8 @@ type Token struct { Method SigningMethod // Method is the signing method used or to be used Header map[string]any // Header is the first segment of the token in decoded form Claims Claims // Claims is the second segment of the token in decoded form - Signature []byte // Signature is the third segment of the token in decoded form. Populated when you Parse a token - Valid bool // Valid specifies if the token is valid. Populated when you Parse/Verify a token + Signature []byte // Signature is the third segment of the token in decoded form. Populated when you [Parse] or sign a token + Valid bool // Valid specifies if the token is valid. Populated when you [Parse] a token } // New creates a new [Token] with the specified signing method and an empty map @@ -71,6 +71,8 @@ func (t *Token) SignedString(key any) (string, error) { return "", err } + t.Signature = sig + return sstr + "." + t.EncodeSegment(sig), nil } diff --git a/vendor/github.com/golang-jwt/jwt/v5/validator.go b/vendor/github.com/golang-jwt/jwt/v5/validator.go index 92b5c057cd..c82dfcae6a 100644 --- a/vendor/github.com/golang-jwt/jwt/v5/validator.go +++ b/vendor/github.com/golang-jwt/jwt/v5/validator.go @@ -44,6 +44,9 @@ type Validator struct { // requireExp specifies whether the exp claim is required requireExp bool + // requireNbf specifies whether the nbf claim is required + requireNbf bool + // verifyIat specifies whether the iat (Issued At) claim will be verified. // According to https://www.rfc-editor.org/rfc/rfc7519#section-4.1.6 this // only specifies the age of the token, but no validation check is @@ -111,8 +114,9 @@ func (v *Validator) Validate(claims Claims) error { } // We always need to check not-before, but usage of the claim itself is - // OPTIONAL. - if err = v.verifyNotBefore(claims, now, false); err != nil { + // OPTIONAL by default. requireNbf overrides this behavior and makes + // the nbf claim mandatory. + if err = v.verifyNotBefore(claims, now, v.requireNbf); err != nil { errs = append(errs, err) } diff --git a/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/mux.go b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/mux.go index 4e684c7de6..69edf5eff8 100644 --- a/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/mux.go +++ b/vendor/github.com/grpc-ecosystem/grpc-gateway/v2/runtime/mux.go @@ -71,6 +71,7 @@ type ServeMux struct { streamErrorHandler StreamErrorHandlerFunc routingErrorHandler RoutingErrorHandlerFunc disablePathLengthFallback bool + disableHTTPMethodOverride bool unescapingMode UnescapingMode writeContentLength bool disableChunkedEncoding bool @@ -271,6 +272,19 @@ func WithDisablePathLengthFallback() ServeMuxOption { } } +// WithDisableHTTPMethodOverride returns a ServeMuxOption that disables the +// X-HTTP-Method-Override header handling. +// +// When this option is used, the mux will no longer allow POST requests with +// the X-HTTP-Method-Override header to override the HTTP method. The path +// length fallback (POST with application/x-www-form-urlencoded falling back +// to a matching GET handler) is not affected by this option. +func WithDisableHTTPMethodOverride() ServeMuxOption { + return func(serveMux *ServeMux) { + serveMux.disableHTTPMethodOverride = true + } +} + // WithWriteContentLength returns a ServeMuxOption to enable writing content length on non-streaming responses func WithWriteContentLength() ServeMuxOption { return func(serveMux *ServeMux) { @@ -405,7 +419,7 @@ func (s *ServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) { path = r.URL.RawPath } - if override := r.Header.Get("X-HTTP-Method-Override"); override != "" && s.isPathLengthFallback(r) { + if override := r.Header.Get("X-HTTP-Method-Override"); override != "" && !s.disableHTTPMethodOverride && s.isPathLengthFallback(r) { if err := r.ParseForm(); err != nil { _, outboundMarshaler := MarshalerForRequest(s, r) sterr := status.Error(codes.InvalidArgument, err.Error()) @@ -467,6 +481,7 @@ func (s *ServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) { HTTPStatus: http.StatusBadRequest, Err: mse, }) + return } continue } @@ -509,6 +524,7 @@ func (s *ServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) { HTTPStatus: http.StatusBadRequest, Err: mse, }) + return } continue } diff --git a/vendor/github.com/klauspost/compress/README.md b/vendor/github.com/klauspost/compress/README.md index fb023f2cf2..0e9f170d01 100644 --- a/vendor/github.com/klauspost/compress/README.md +++ b/vendor/github.com/klauspost/compress/README.md @@ -27,6 +27,18 @@ Use the links above for more information on each. # changelog +* Jul 1st, 2026 [1.19.0](https://github.com/klauspost/compress/releases/tag/v1.19.0) + * zstd: Add true concurrent stream encodingin https://github.com/klauspost/compress/pull/1136 + * zstd: arm64 decoder asm by @lizthegrey in https://github.com/klauspost/compress/pull/1160 + * flate: Add inflate checkpoints in https://github.com/klauspost/compress/pull/1154 + * zstd: avoid unused BuildDict encoder allocation by @snissn in https://github.com/klauspost/compress/pull/1147 + * snappy/s2: Limit length of varint in `decodedLen` by @eustas in https://github.com/klauspost/compress/pull/1148 + * gzhttp: match qvalue parameter case-insensitively (RFC 7231) by @z9z in https://github.com/klauspost/compress/pull/1149 + * zip: add NameDecoder callback for legacy encoding rewrite by @SAY-5 in https://github.com/klauspost/compress/pull/1150 + * huff0: Allow building tables from histogram in https://github.com/klauspost/compress/pull/1155 + * huff0: Allow building table from oversized histogram in https://github.com/klauspost/compress/pull/1156 + * s2sx: Clean symlink targets in https://github.com/klauspost/compress/pull/1163 + * Feb 9th, 2026 [1.18.4](https://github.com/klauspost/compress/releases/tag/v1.18.4) * gzhttp: Add zstandard to server handler wrapper https://github.com/klauspost/compress/pull/1121 * zstd: Add ResetWithOptions to encoder/decoder https://github.com/klauspost/compress/pull/1122 diff --git a/vendor/github.com/klauspost/compress/huff0/build_table.go b/vendor/github.com/klauspost/compress/huff0/build_table.go new file mode 100644 index 0000000000..e3757c87e0 --- /dev/null +++ b/vendor/github.com/klauspost/compress/huff0/build_table.go @@ -0,0 +1,168 @@ +package huff0 + +import "errors" + +// BuildCTable builds a Huffman compression table from a precomputed symbol +// histogram and installs it as the previous (reuse) table on s. +// +// After this call: +// - EstimateSize/CanUseTable can probe the table against other histograms. +// - Compress1X/Compress4X with Reuse = ReusePolicyMust will encode without +// emitting a new table header. +// - TransferCTable can hand the table to a sibling Scratch. +// +// count[i] is the number of occurrences of symbol i. The histogram must have +// at least 2 distinct non-zero symbols; ErrUseRLE is returned for a single +// symbol and an error is returned for an empty histogram. +func (s *Scratch) BuildCTable(count *[256]uint32) error { + if s == nil { + return errors.New("huff0: BuildCTable on nil Scratch") + } + if count == nil { + return errors.New("huff0: nil count passed to BuildCTable") + } + var err error + s, err = s.prepare(nil) + if err != nil { + return err + } + s.count = *count + var total, maxCount int + var symLen uint16 + for i, v := range s.count { + total += int(v) + if int(v) > maxCount { + maxCount = int(v) + } + if v != 0 { + symLen = uint16(i) + 1 + } + } + if total == 0 { + return errors.New("huff0: empty histogram") + } + if symLen < 2 || maxCount == total { + return ErrUseRLE + } + // huff0's internal rank table assumes total ≤ BlockSizeMax (it uses + // highBit32(count+1) + 1 as a rank index into a fixed-size array). + // Histograms summed across multiple blocks can exceed that; scale the + // counts down preserving the distribution. Non-zero entries round up so + // rare symbols stay representable. + if total > BlockSizeMax { + shift := uint(0) + for total>>shift > BlockSizeMax { + shift++ + } + round := uint32(1<> shift + if scaled == 0 { + scaled = 1 + } + s.count[i] = scaled + newTotal += int(scaled) + if int(scaled) > newMax { + newMax = int(scaled) + } + } + total = newTotal + maxCount = newMax + if maxCount == total { + return ErrUseRLE + } + } + s.symbolLen = symLen + s.maxCount = maxCount + s.srcLen = total + if err := s.buildCTable(); err != nil { + return err + } + if cap(s.prevTable) < len(s.cTable) { + s.prevTable = make(cTable, 0, maxSymbolValue+1) + } + s.prevTable = s.prevTable[:len(s.cTable)] + copy(s.prevTable, s.cTable) + s.prevTableLog = s.actualTableLog + // Force the next Compress* to recount from real input. + s.clearCount = true + s.maxCount = 0 + return nil +} + +// EstimateSize returns an estimated compressed payload size in bytes for the +// supplied histogram using the table currently stored in prevTable. It returns +// -1 when the table cannot encode every non-zero symbol of hist (i.e. when +// CanUseTable would return false). The estimate excludes the table header. +func (s *Scratch) EstimateSize(hist *[256]uint32) int { + if s == nil || hist == nil || len(s.prevTable) == 0 { + return -1 + } + pt := s.prevTable + nbBits := uint32(7) + for i, v := range hist { + if v == 0 { + continue + } + if i >= len(pt) || pt[i].nBits == 0 { + return -1 + } + nbBits += uint32(pt[i].nBits) * v + } + return int(nbBits >> 3) +} + +// CanUseTable reports whether the table in prevTable can encode every +// non-zero symbol present in hist. +func (s *Scratch) CanUseTable(hist *[256]uint32) bool { + if s == nil || hist == nil || len(s.prevTable) == 0 { + return false + } + pt := s.prevTable + for i, v := range hist { + if v == 0 { + continue + } + if i >= len(pt) || pt[i].nBits == 0 { + return false + } + } + return true +} + +// AppendTable serializes the table currently stored in prevTable (e.g. as +// installed by BuildCTable or carried over from a previous Compress call) +// into a self-delimiting zstd-style header and appends it to dst. The +// returned slice can be parsed back by ReadTable. +func (s *Scratch) AppendTable(dst []byte) ([]byte, error) { + if s == nil || len(s.prevTable) == 0 { + return dst, errors.New("huff0: AppendTable with empty table") + } + // cTable.write reads s.actualTableLog, s.symbolLen, s.huffWeight, s.fse + // and writes into s.Out. Save/restore Out so we don't disturb in-flight + // compression buffers. + saveOut := s.Out + saveTL := s.actualTableLog + saveSL := s.symbolLen + if s.fse == nil { + // Lazily init in case AppendTable is called on a fresh Scratch. + if _, err := s.prepare(nil); err != nil { + return dst, err + } + saveOut = s.Out + } + s.Out = s.Out[:0] + s.actualTableLog = s.prevTableLog + s.symbolLen = uint16(len(s.prevTable)) + if err := s.prevTable.write(s); err != nil { + s.Out, s.actualTableLog, s.symbolLen = saveOut, saveTL, saveSL + return dst, err + } + dst = append(dst, s.Out...) + s.Out, s.actualTableLog, s.symbolLen = saveOut, saveTL, saveSL + return dst, nil +} diff --git a/vendor/github.com/klauspost/compress/internal/snapref/decode.go b/vendor/github.com/klauspost/compress/internal/snapref/decode.go index a2c82fcd22..584b7574b2 100644 --- a/vendor/github.com/klauspost/compress/internal/snapref/decode.go +++ b/vendor/github.com/klauspost/compress/internal/snapref/decode.go @@ -31,7 +31,7 @@ func DecodedLen(src []byte) (int, error) { // that the length header occupied. func decodedLen(src []byte) (blockLen, headerLen int, err error) { v, n := binary.Uvarint(src) - if n <= 0 || v > 0xffffffff { + if n <= 0 || n > 5 || v > 0xffffffff { return 0, 0, ErrCorrupt } diff --git a/vendor/github.com/klauspost/compress/s2/decode.go b/vendor/github.com/klauspost/compress/s2/decode.go index 264ffd0a9b..17abb515a5 100644 --- a/vendor/github.com/klauspost/compress/s2/decode.go +++ b/vendor/github.com/klauspost/compress/s2/decode.go @@ -35,7 +35,7 @@ func DecodedLen(src []byte) (int, error) { // that the length header occupied. func decodedLen(src []byte) (blockLen, headerLen int, err error) { v, n := binary.Uvarint(src) - if n <= 0 || v > 0xffffffff { + if n <= 0 || n > 5 || v > 0xffffffff { return 0, 0, ErrCorrupt } diff --git a/vendor/github.com/klauspost/compress/s2/dict.go b/vendor/github.com/klauspost/compress/s2/dict.go index f125ad0963..f8dc652800 100644 --- a/vendor/github.com/klauspost/compress/s2/dict.go +++ b/vendor/github.com/klauspost/compress/s2/dict.go @@ -56,10 +56,12 @@ func NewDict(dict []byte) *Dict { if len(dict) < MinDictSize || len(dict) > MaxDictSize { return nil } - d.repeat = int(r) - if d.repeat > len(dict) { + // Compare as uint64: int(r) would wrap negative for r > MaxInt64, + // slipping past the bounds check and causing an OOB read in encode. + if r > uint64(len(dict)) { return nil } + d.repeat = int(r) return &d } diff --git a/vendor/github.com/klauspost/compress/s2/encode_all.go b/vendor/github.com/klauspost/compress/s2/encode_all.go index 9d12c44f38..794ec8a687 100644 --- a/vendor/github.com/klauspost/compress/s2/encode_all.go +++ b/vendor/github.com/klauspost/compress/s2/encode_all.go @@ -981,7 +981,7 @@ searchDict: cv = load64(src, s) continue } - } else if uint32(cv>>(checkRep*8)) == load32(src, s-repeat+checkRep) { + } else if repeat > 0 && uint32(cv>>(checkRep*8)) == load32(src, s-repeat+checkRep) { base := s + checkRep // Extend back for i := base - repeat; base > nextEmit && i > 0 && src[i-1] == src[base-1]; { diff --git a/vendor/github.com/klauspost/compress/s2/hashtable_pool.go b/vendor/github.com/klauspost/compress/s2/hashtable_pool.go index bc7cabd5c5..ec972132b5 100644 --- a/vendor/github.com/klauspost/compress/s2/hashtable_pool.go +++ b/vendor/github.com/klauspost/compress/s2/hashtable_pool.go @@ -25,7 +25,7 @@ type betterTables struct { sTable [betterShortTableSize]uint32 } -var betterTablePool = sync.Pool{New: func() interface{} { return &betterTables{} }} +var betterTablePool = sync.Pool{New: func() any { return &betterTables{} }} // betterSnappyTables holds better-snappy compression hash tables. type betterSnappyTables struct { @@ -33,7 +33,7 @@ type betterSnappyTables struct { sTable [betterShortTableSize]uint32 } -var betterSnappyTablePool = sync.Pool{New: func() interface{} { return &betterSnappyTables{} }} +var betterSnappyTablePool = sync.Pool{New: func() any { return &betterSnappyTables{} }} // bestTables holds best compression hash tables. type bestTables struct { @@ -41,7 +41,7 @@ type bestTables struct { sTable [bestShortTableSize]uint64 } -var bestTablePool = sync.Pool{New: func() interface{} { return &bestTables{} }} +var bestTablePool = sync.Pool{New: func() any { return &bestTables{} }} // getBetterTables gets a zeroed betterTables from the pool. func getBetterTables() *betterTables { diff --git a/vendor/github.com/klauspost/compress/s2/reader.go b/vendor/github.com/klauspost/compress/s2/reader.go index 4d01c4190c..17443e2c14 100644 --- a/vendor/github.com/klauspost/compress/s2/reader.go +++ b/vendor/github.com/klauspost/compress/s2/reader.go @@ -216,13 +216,18 @@ func (r *Reader) skippable(tmp []byte, n int, allowEOF bool, id uint8) (ok bool) return r.err == nil } if rs, ok := r.r.(io.ReadSeeker); ok { - _, err := rs.Seek(int64(n), io.SeekCurrent) - if err == nil { - return true - } - if err == io.ErrUnexpectedEOF || (r.err == io.EOF && !allowEOF) { - r.err = ErrCorrupt - return false + if cur, err := rs.Seek(0, io.SeekCurrent); err == nil { + if end, err := rs.Seek(0, io.SeekEnd); err == nil { + if cur+int64(n) <= end { + if _, err := rs.Seek(cur+int64(n), io.SeekStart); err == nil { + return true + } + } + if _, err := rs.Seek(cur, io.SeekStart); err != nil { + r.err = ErrCorrupt + return false + } + } } } for n > 0 { diff --git a/vendor/github.com/klauspost/compress/zstd/README.md b/vendor/github.com/klauspost/compress/zstd/README.md index c11d7fa28e..a5aeeaed06 100644 --- a/vendor/github.com/klauspost/compress/zstd/README.md +++ b/vendor/github.com/klauspost/compress/zstd/README.md @@ -75,14 +75,47 @@ The above is fine for big encodes. However, whenever possible try to *reuse* the To reuse the encoder, you can use the `Reset(io.Writer)` function to change to another output. This will allow the encoder to reuse all resources and avoid wasteful allocations. -Currently stream encoding has 'light' concurrency, meaning up to 2 goroutines can be working on part -of a stream. This is independent of the `WithEncoderConcurrency(n)`, but that is likely to change +By default, stream encoding has 'light' concurrency, meaning up to 2 goroutines can be working on part +of a stream. This is independent of the `WithEncoderConcurrency(n)`, but that is likely to change in the future. So if you want to limit concurrency for future updates, specify the concurrency you would like. If you would like stream encoding to be done without spawning async goroutines, use `WithEncoderConcurrency(1)` which will compress input as each block is completed, blocking on writes until each has completed. +#### Parallel Stream Compression + +For maximum throughput on large streams, use `WithConcurrentBlocks(true)` together with +`WithEncoderConcurrency(n)` where n is the number of CPU cores you want to use. +This splits the input into large sections (jobs) that are compressed simultaneously by multiple goroutines, +similar to how the C zstd library does multithreaded compression. + +```Go +enc, err := zstd.NewWriter(out, + zstd.WithEncoderLevel(zstd.SpeedDefault), + zstd.WithEncoderConcurrency(runtime.GOMAXPROCS(0)), + zstd.WithConcurrentBlocks(true), +) +``` + +Each non-first job receives an overlap prefix from the previous job for match context, +so compression ratio is only marginally affected. Output is flushed in order, +producing a valid single-frame zstd stream. + +Benchmark on 1.8GB GOB stream (AMD Ryzen 9 9950X): + +| Level | 1 thread | 4 threads | 16 threads | 1T ratio | 16T ratio | +|---------|:----------:|:------------------:|:-------------------:|:--------:|:---------:| +| fastest | 783 MB/s | 2950 MB/s (3.8×) | 6939 MB/s (8.9×) | 12.24% | 12.26% | +| default | 728 MB/s | 2533 MB/s (3.5×) | 5340 MB/s (7.3×) | 10.67% | 10.68% | +| better | 434 MB/s | 1105 MB/s (2.5×) | 2206 MB/s (5.1×) | 9.14% | 9.21% | +| best | 129 MB/s | 367 MB/s (2.8×) | 884 MB/s (6.8×) | 8.48% | 8.63% | + +Notes: +* Not compatible with dictionary encoding. +* `Flush()` dispatches the current partial job, so latency-sensitive callers can force output. +* `EncodeAll` is unaffected — it uses its own concurrency via the encoder pool. + You can specify your desired compression level using `WithEncoderLevel()` option. Currently only pre-defined compression settings can be specified. diff --git a/vendor/github.com/klauspost/compress/zstd/dict.go b/vendor/github.com/klauspost/compress/zstd/dict.go index 2ffbfdf379..4f1c4938cd 100644 --- a/vendor/github.com/klauspost/compress/zstd/dict.go +++ b/vendor/github.com/klauspost/compress/zstd/dict.go @@ -230,7 +230,7 @@ func BuildDict(o BuildDictOptions) ([]byte, error) { } block := blockEnc{lowMem: false} block.init() - enc := encoder(&bestFastEncoder{fastBase: fastBase{maxMatchOff: int32(maxMatchLen), bufferReset: math.MaxInt32 - int32(maxMatchLen*2), lowMem: false}}) + var enc encoder if o.Level != 0 { eOpts := encoderOptions{ level: o.Level, @@ -242,6 +242,7 @@ func BuildDict(o BuildDictOptions) ([]byte, error) { enc = eOpts.encoder() } else { o.Level = SpeedBestCompression + enc = encoder(&bestFastEncoder{fastBase: fastBase{maxMatchOff: int32(maxMatchLen), bufferReset: math.MaxInt32 - int32(maxMatchLen*2), lowMem: false}}) } var ( remain [256]int diff --git a/vendor/github.com/klauspost/compress/zstd/enc_base.go b/vendor/github.com/klauspost/compress/zstd/enc_base.go index c4de134a7a..c4fea575d6 100644 --- a/vendor/github.com/klauspost/compress/zstd/enc_base.go +++ b/vendor/github.com/klauspost/compress/zstd/enc_base.go @@ -128,6 +128,34 @@ func (e *fastBase) matchlen(s, t int32, src []byte) int32 { return int32(matchLen(src[s:], src[t:])) } +// resetBasePrefix resets the encoder state and loads prefix as initial history. +// This is used for parallel job encoding where non-first jobs need overlap context. +// Rep offsets are set to defaults [1,4,8] (invalidated, matching C behavior). +func (e *fastBase) resetBasePrefix(prefix []byte) { + if e.blk == nil { + e.blk = &blockEnc{lowMem: e.lowMem} + e.blk.init() + } else { + e.blk.reset(nil) + } + e.blk.initNewEncode() + if e.crc == nil { + e.crc = xxhash.New() + } else { + e.crc.Reset() + } + e.blk.dictLitEnc = nil + e.ensureHist(len(prefix) + maxCompressedBlockSize) + // Bump cur so old table entries fall outside the window. + // When cur >= bufferReset, leave it; the first Encode call + // will shift/clear tables, preserving valid prefix entries. + if e.cur < e.bufferReset { + e.cur += e.maxMatchOff + int32(len(e.hist)) + } + e.hist = e.hist[:0] + e.hist = append(e.hist, prefix...) +} + // Reset the encoding table. func (e *fastBase) resetBase(d *dict, singleBlock bool) { if e.blk == nil { diff --git a/vendor/github.com/klauspost/compress/zstd/enc_best.go b/vendor/github.com/klauspost/compress/zstd/enc_best.go index 851799322b..c71382dde6 100644 --- a/vendor/github.com/klauspost/compress/zstd/enc_best.go +++ b/vendor/github.com/klauspost/compress/zstd/enc_best.go @@ -551,3 +551,18 @@ func (e *bestFastEncoder) Reset(d *dict, singleBlock bool) { // Reset table to initial state copy(e.table[:], e.dictTable) } + +func (e *bestFastEncoder) ResetPrefix(prefix []byte) { + e.resetBasePrefix(prefix) + if len(prefix) < 8 { + return + } + end := e.cur + int32(len(prefix)) - 8 + for i := e.cur; i < end; i++ { + cv := load6432(prefix, i-e.cur) + h := hashLen(cv, bestLongTableBits, bestLongLen) + e.longTable[h] = prevEntry{offset: i, prev: e.longTable[h].offset} + h0 := hashLen(cv, bestShortTableBits, bestShortLen) + e.table[h0] = prevEntry{offset: i, prev: e.table[h0].offset} + } +} diff --git a/vendor/github.com/klauspost/compress/zstd/enc_better.go b/vendor/github.com/klauspost/compress/zstd/enc_better.go index 3305f09248..523d57f3ad 100644 --- a/vendor/github.com/klauspost/compress/zstd/enc_better.go +++ b/vendor/github.com/klauspost/compress/zstd/enc_better.go @@ -1096,6 +1096,20 @@ func (e *betterFastEncoder) Reset(d *dict, singleBlock bool) { } } +func (e *betterFastEncoder) ResetPrefix(prefix []byte) { + e.resetBasePrefix(prefix) + if len(prefix) < 8 { + return + } + end := e.cur + int32(len(prefix)) - 8 + for i := e.cur; i < end; i += 2 { + cv := load6432(prefix, i-e.cur) + h := hashLen(cv, betterLongTableBits, betterLongLen) + e.longTable[h] = prevEntry{offset: i, prev: e.longTable[h].offset} + e.table[hashLen(cv>>8, betterShortTableBits, betterShortLen)] = tableEntry{val: uint32(cv >> 8), offset: i + 1} + } +} + // ResetDict will reset and set a dictionary if not nil func (e *betterFastEncoderDict) Reset(d *dict, singleBlock bool) { e.resetBase(d, singleBlock) @@ -1229,6 +1243,10 @@ func (e *betterFastEncoderDict) Reset(d *dict, singleBlock bool) { e.allDirty = false } +func (e *betterFastEncoderDict) ResetPrefix([]byte) { + panic("ResetPrefix not supported for dict encoders") +} + func (e *betterFastEncoderDict) markLongShardDirty(entryNum uint32) { e.longTableShardDirty[entryNum/betterLongTableShardSize] = true } diff --git a/vendor/github.com/klauspost/compress/zstd/enc_dfast.go b/vendor/github.com/klauspost/compress/zstd/enc_dfast.go index 2fb6da112b..712ba7ab58 100644 --- a/vendor/github.com/klauspost/compress/zstd/enc_dfast.go +++ b/vendor/github.com/klauspost/compress/zstd/enc_dfast.go @@ -1037,6 +1037,18 @@ func (e *doubleFastEncoder) Reset(d *dict, singleBlock bool) { } } +func (e *doubleFastEncoder) ResetPrefix(prefix []byte) { + e.fastEncoder.ResetPrefix(prefix) + if len(prefix) < 8 { + return + } + end := e.cur + int32(len(prefix)) - 8 + for i := e.cur + 1; i < end; i += 2 { + cv := load6432(prefix, i-e.cur) + e.longTable[hashLen(cv, dFastLongTableBits, dFastLongLen)] = tableEntry{val: uint32(cv), offset: i} + } +} + // ResetDict will reset and set a dictionary if not nil func (e *doubleFastEncoderDict) Reset(d *dict, singleBlock bool) { allDirty := e.allDirty @@ -1102,6 +1114,10 @@ func (e *doubleFastEncoderDict) Reset(d *dict, singleBlock bool) { } } +func (e *doubleFastEncoderDict) ResetPrefix([]byte) { + panic("ResetPrefix not supported for dict encoders") +} + func (e *doubleFastEncoderDict) markLongShardDirty(entryNum uint32) { e.longTableShardDirty[entryNum/dLongTableShardSize] = true } diff --git a/vendor/github.com/klauspost/compress/zstd/enc_fast.go b/vendor/github.com/klauspost/compress/zstd/enc_fast.go index 5e104f1a48..06045e2463 100644 --- a/vendor/github.com/klauspost/compress/zstd/enc_fast.go +++ b/vendor/github.com/klauspost/compress/zstd/enc_fast.go @@ -797,6 +797,19 @@ func (e *fastEncoder) Reset(d *dict, singleBlock bool) { } } +func (e *fastEncoder) ResetPrefix(prefix []byte) { + e.resetBasePrefix(prefix) + if len(prefix) < 8 { + return + } + end := e.cur + int32(len(prefix)) - 8 + // Index every 4th + for i := e.cur + 1; i < end; i += 4 { + cv := load6432(prefix, i-e.cur) + e.table[hashLen(cv, tableBits, tableFastHashLen)] = tableEntry{val: uint32(cv), offset: i} + } +} + // ResetDict will reset and set a dictionary if not nil func (e *fastEncoderDict) Reset(d *dict, singleBlock bool) { e.resetBase(d, singleBlock) @@ -866,6 +879,10 @@ func (e *fastEncoderDict) Reset(d *dict, singleBlock bool) { e.allDirty = false } +func (e *fastEncoderDict) ResetPrefix([]byte) { + panic("ResetPrefix not supported for dict encoders") +} + func (e *fastEncoderDict) markAllShardsDirty() { e.allDirty = true } diff --git a/vendor/github.com/klauspost/compress/zstd/enc_jobs.go b/vendor/github.com/klauspost/compress/zstd/enc_jobs.go new file mode 100644 index 0000000000..95ce67ac05 --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/enc_jobs.go @@ -0,0 +1,352 @@ +// Copyright 2019+ Klaus Post. All rights reserved. +// License information can be found in the LICENSE file. +// Based on work by Yann Collet, released under BSD License. + +package zstd + +import ( + "fmt" + rdebug "runtime/debug" + "sync" +) + +type encJob struct { + prefix []byte // overlap from previous job (nil for first) + input []byte // job's own input data (swapped from filling) + last bool // last block of last job gets last=true + output []byte // compressed blocks (filled by worker) + err error // encoding error + done chan struct{} // closed when complete +} + +type jobState struct { + jobSize int + overlapSize int + filling []byte // accumulates input up to jobSize + nextPrefix []byte // overlap prefix prepared for the next dispatched job + + jobSeq int // next job sequence number + + jobCh chan *encJob // dispatch to workers + resultCh chan *encJob // ordered results to flusher + + workerWg sync.WaitGroup + flusherWg sync.WaitGroup + + mu sync.Mutex + flushedSeq int // last flushed sequence number + cond *sync.Cond + + flusherErr error + started bool + + inputPool sync.Pool // *[]byte buffers of jobSize cap + outputPool sync.Pool // *[]byte buffers for compressed output + overlapPool sync.Pool // *[]byte buffers for overlap prefixes +} + +func (e *Encoder) startJobWorkers() { + js := &e.state.jobs + n := e.o.concurrent + js.jobCh = make(chan *encJob, n) + js.resultCh = make(chan *encJob, n) + js.flushedSeq = 0 + js.cond = sync.NewCond(&js.mu) + + // Workers borrow encoders from the shared e.encoders pool per-job. + // Ensure the pool is initialized before any worker tries to borrow. + e.init.Do(e.initialize) + + for range n { + js.workerWg.Add(1) + go e.jobWorker() + } + js.flusherWg.Add(1) + go e.jobFlusher() + js.started = true +} + +func (e *Encoder) jobWorker() { + js := &e.state.jobs + defer js.workerWg.Done() + for job := range js.jobCh { + enc := <-e.encoders + e.compressJob(enc, job) + e.encoders <- enc + close(job.done) + } +} + +func (e *Encoder) compressJob(enc encoder, job *encJob) { + defer func() { + if r := recover(); r != nil { + job.err = fmt.Errorf("panic in parallel job: %v", r) + rdebug.PrintStack() + } + }() + + if len(job.prefix) > 0 { + enc.ResetPrefix(job.prefix) + } else { + enc.Reset(nil, false) + } + + data := job.input + if len(data) == 0 && job.last { + blk := enc.Block() + blk.reset(nil) + blk.last = true + blk.encodeRaw(nil) + job.output = append(job.output, blk.output...) + return + } + + blk := enc.Block() + for len(data) > 0 { + todo := data + if len(todo) > e.o.blockSize { + todo = todo[:e.o.blockSize] + } + data = data[len(todo):] + + blk.pushOffsets() + enc.Encode(blk, todo) + blk.last = len(data) == 0 && job.last + + err := blk.encode(todo, e.o.noEntropy, !e.o.allLitEntropy) + if err != nil { + job.err = err + return + } + job.output = append(job.output, blk.output...) + blk.reset(nil) + } +} + +func (js *jobState) getInputBuf(size int) []byte { + if v := js.inputPool.Get(); v != nil { + bp := v.(*[]byte) + b := *bp + if cap(b) >= size { + return b[:0] + } + } + return make([]byte, 0, size) +} + +func (js *jobState) putInputBuf(b []byte) { + if cap(b) > 0 { + b = b[:0] + js.inputPool.Put(&b) + } +} + +func (js *jobState) getOutputBuf(size int) []byte { + if v := js.outputPool.Get(); v != nil { + bp := v.(*[]byte) + b := *bp + if cap(b) >= size { + return b[:0] + } + } + return make([]byte, 0, size) +} + +func (js *jobState) putOutputBuf(b []byte) { + if cap(b) > 0 { + b = b[:0] + js.outputPool.Put(&b) + } +} + +func (js *jobState) getOverlapBuf(size int) []byte { + if v := js.overlapPool.Get(); v != nil { + bp := v.(*[]byte) + b := *bp + if cap(b) >= size { + return b[:size] + } + } + return make([]byte, size) +} + +func (js *jobState) putOverlapBuf(b []byte) { + if cap(b) > 0 { + b = b[:0] + js.overlapPool.Put(&b) + } +} + +func (e *Encoder) jobFlusher() { + js := &e.state.jobs + defer js.flusherWg.Done() + for job := range js.resultCh { + <-job.done + // Worker has fully exited compressJob, so the prefix is no longer + // in use. Return it to the pool regardless of outcome. + if job.prefix != nil { + js.putOverlapBuf(job.prefix) + job.prefix = nil + } + if job.err != nil { + js.mu.Lock() + js.flusherErr = job.err + js.cond.Broadcast() + js.mu.Unlock() + for range js.resultCh { + } + return + } + if len(job.output) > 0 { + _, err := e.state.w.Write(job.output) + if err != nil { + js.mu.Lock() + js.flusherErr = err + js.cond.Broadcast() + js.mu.Unlock() + for range js.resultCh { + } + return + } + e.state.nWritten += int64(len(job.output)) + } + // Return buffers to pools. + js.putInputBuf(job.input) + js.putOutputBuf(job.output) + job.input = nil + job.output = nil + + js.mu.Lock() + js.flushedSeq++ + js.cond.Broadcast() + js.mu.Unlock() + } +} + +func (e *Encoder) shutdownJobWorkers() { + js := &e.state.jobs + if !js.started { + return + } + close(js.jobCh) + js.workerWg.Wait() + close(js.resultCh) + js.flusherWg.Wait() + js.started = false +} + +// waitAllJobs blocks until all dispatched jobs have been flushed. +func (e *Encoder) waitAllJobs() { + js := &e.state.jobs + if !js.started { + return + } + js.mu.Lock() + for js.flushedSeq < js.jobSeq && js.flusherErr == nil { + js.cond.Wait() + } + js.mu.Unlock() +} + +func (e *Encoder) dispatchJob(final bool) error { + s := &e.state + js := &s.jobs + + js.mu.Lock() + fErr := js.flusherErr + js.mu.Unlock() + if fErr != nil { + return fErr + } + + if !s.headerWritten { + // Single-block optimization: fall through to encodeAll path. + if final && len(js.filling) > 0 && len(js.filling) <= e.o.blockSize { + s.current = e.encodeAll(s.encoder, js.filling, s.current[:0]) + var n2 int + n2, s.err = s.w.Write(s.current) + if s.err != nil { + return s.err + } + s.nWritten += int64(n2) + s.nInput += int64(len(js.filling)) + s.current = s.current[:0] + js.filling = js.filling[:0] + s.headerWritten = true + s.fullFrameWritten = true + s.eofWritten = true + return nil + } + if final && len(js.filling) == 0 && !e.o.fullZero { + s.headerWritten = true + s.fullFrameWritten = true + s.eofWritten = true + return nil + } + + var tmp [maxHeaderSize]byte + fh := frameHeader{ + ContentSize: uint64(s.frameContentSize), + WindowSize: uint32(s.encoder.WindowSize(s.frameContentSize)), + SingleSegment: false, + Checksum: e.o.crc, + DictID: 0, + } + dst := fh.appendTo(tmp[:0]) + var n2 int + n2, s.err = s.w.Write(dst) + if s.err != nil { + return s.err + } + s.nWritten += int64(n2) + s.headerWritten = true + } + + if len(js.filling) == 0 && !final { + return nil + } + + if !js.started { + e.startJobWorkers() + } + + // Estimate output size for pooled buffer. + outputEst := max(len(js.filling)/2, 512) + + job := &encJob{ + last: final, + done: make(chan struct{}), + output: js.getOutputBuf(outputEst), + } + + // Each job owns its prefix slice; the flusher returns it to the pool + // after <-job.done, so workers and dispatch never share a buffer. + if js.nextPrefix != nil { + job.prefix = js.nextPrefix + js.nextPrefix = nil + } + + // Build the next job's prefix from the tail of this job's input. + if !final && len(js.filling) > 0 { + overlapLen := min(js.overlapSize, len(js.filling)) + np := js.getOverlapBuf(overlapLen) + copy(np, js.filling[len(js.filling)-overlapLen:]) + js.nextPrefix = np + } + + // Swap filling buffer into job — zero-copy for the input data. + job.input = js.filling + js.filling = js.getInputBuf(js.jobSize) + + s.nInput += int64(len(job.input)) + js.jobSeq++ + + if final { + s.eofWritten = true + } + + js.resultCh <- job + js.jobCh <- job + + return nil +} diff --git a/vendor/github.com/klauspost/compress/zstd/encoder.go b/vendor/github.com/klauspost/compress/zstd/encoder.go index 0f2a00a003..6ee96d8730 100644 --- a/vendor/github.com/klauspost/compress/zstd/encoder.go +++ b/vendor/github.com/klauspost/compress/zstd/encoder.go @@ -38,6 +38,7 @@ type encoder interface { WindowSize(size int64) int32 UseBlock(*blockEnc) Reset(d *dict, singleBlock bool) + ResetPrefix(prefix []byte) } type encoderState struct { @@ -60,6 +61,9 @@ type encoderState struct { wg sync.WaitGroup // This waitgroup indicates we have a block encoding/writing. wWg sync.WaitGroup + + // Parallel job state (used when concurrentBlocks is enabled). + jobs jobState } // NewWriter will create a new Zstandard encoder. @@ -74,6 +78,9 @@ func NewWriter(w io.Writer, opts ...EOption) (*Encoder, error) { return nil, err } } + if e.o.concurrentBlocks && (e.o.dict != nil || e.o.concurrent <= 1) { + e.o.concurrentBlocks = false + } if w != nil { e.Reset(w) } @@ -95,12 +102,31 @@ func (e *Encoder) initialize() { // as a new, independent stream. func (e *Encoder) Reset(w io.Writer) { s := &e.state + + if e.o.concurrentBlocks { + e.shutdownJobWorkers() + js := &s.jobs + js.jobSize = e.o.jobSize() + js.overlapSize = e.o.overlapSize() + // js.filling is allocated lazily on first Write/ReadFrom so callers + // that only use EncodeAll don't pay the (up to ~32 MB) jobSize cost. + js.filling = js.filling[:0] + if js.nextPrefix != nil { + js.putOverlapBuf(js.nextPrefix) + js.nextPrefix = nil + } + js.jobSeq = 0 + js.flushedSeq = 0 + js.flusherErr = nil + js.started = false + } + s.wg.Wait() s.wWg.Wait() if cap(s.filling) == 0 { s.filling = make([]byte, 0, e.o.blockSize) } - if e.o.concurrent > 1 { + if e.o.concurrent > 1 && !e.o.concurrentBlocks { if cap(s.current) == 0 { s.current = make([]byte, 0, e.o.blockSize) } @@ -145,6 +171,9 @@ func (e *Encoder) ResetWithOptions(w io.Writer, opts ...EOption) error { } } hasDict := e.o.dict != nil + if e.o.concurrentBlocks && hasDict { + e.o.concurrentBlocks = false + } if hadDict != hasDict { // Dict presence changed — encoder type must be recreated. e.state.encoder = nil @@ -176,6 +205,49 @@ func (e *Encoder) Write(p []byte) (n int, err error) { if s.eofWritten { return 0, ErrEncoderClosed } + if e.o.concurrentBlocks { + return e.writeJobs(p) + } + return e.writeBlocks(p) +} + +func (e *Encoder) writeJobs(p []byte) (n int, err error) { + s := &e.state + js := &s.jobs + jobSize := js.jobSize + if cap(js.filling) == 0 && len(p) > 0 { + js.filling = make([]byte, 0, jobSize) + } + for len(p) > 0 { + if len(p)+len(js.filling) < jobSize { + if e.o.crc { + _, _ = s.encoder.CRC().Write(p) + } + js.filling = append(js.filling, p...) + return n + len(p), nil + } + add := p + if len(p)+len(js.filling) > jobSize { + add = add[:jobSize-len(js.filling)] + } + if e.o.crc { + _, _ = s.encoder.CRC().Write(add) + } + js.filling = append(js.filling, add...) + p = p[len(add):] + n += len(add) + if len(js.filling) < jobSize { + return n, nil + } + if err := e.dispatchJob(false); err != nil { + return n, err + } + } + return n, nil +} + +func (e *Encoder) writeBlocks(p []byte) (n int, err error) { + s := &e.state for len(p) > 0 { if len(p)+len(s.filling) < e.o.blockSize { if e.o.crc { @@ -374,6 +446,10 @@ func (e *Encoder) ReadFrom(r io.Reader) (n int64, err error) { println("Using ReadFrom") } + if e.o.concurrentBlocks { + return e.readFromJobs(r) + } + // Flush any current writes. if len(e.state.filling) > 0 { if err := e.nextBlock(false); err != nil { @@ -387,7 +463,6 @@ func (e *Encoder) ReadFrom(r io.Reader) (n int64, err error) { if e.o.crc { _, _ = e.state.encoder.CRC().Write(src[:n2]) } - // src is now the unfilled part... src = src[n2:] n += int64(n2) switch err { @@ -420,15 +495,63 @@ func (e *Encoder) ReadFrom(r io.Reader) (n int64, err error) { } } +func (e *Encoder) readFromJobs(r io.Reader) (n int64, err error) { + js := &e.state.jobs + jobSize := js.jobSize + + // Flush any current filling. + if len(js.filling) > 0 { + if err := e.dispatchJob(false); err != nil { + return 0, err + } + } + + if cap(js.filling) < jobSize { + js.filling = make([]byte, 0, jobSize) + } + js.filling = js.filling[:jobSize] + src := js.filling + for { + n2, err := r.Read(src) + if e.o.crc { + _, _ = e.state.encoder.CRC().Write(src[:n2]) + } + src = src[n2:] + n += int64(n2) + switch err { + case io.EOF: + js.filling = js.filling[:len(js.filling)-len(src)] + return n, nil + case nil: + default: + e.state.err = err + return n, err + } + if len(src) > 0 { + continue + } + if err = e.dispatchJob(false); err != nil { + return n, err + } + if cap(js.filling) < jobSize { + js.filling = make([]byte, 0, jobSize) + } + js.filling = js.filling[:jobSize] + src = js.filling + } +} + // Flush will send the currently written data to output // and block until everything has been written. // This should only be used on rare occasions where pushing the currently queued data is critical. func (e *Encoder) Flush() error { s := &e.state + if e.o.concurrentBlocks { + return e.flushJobs() + } if len(s.filling) > 0 { err := e.nextBlock(false) if err != nil { - // Ignore Flush after Close. if errors.Is(s.err, ErrEncoderClosed) { return nil } @@ -438,7 +561,6 @@ func (e *Encoder) Flush() error { s.wg.Wait() s.wWg.Wait() if s.err != nil { - // Ignore Flush after Close. if errors.Is(s.err, ErrEncoderClosed) { return nil } @@ -447,6 +569,20 @@ func (e *Encoder) Flush() error { return s.writeErr } +func (e *Encoder) flushJobs() error { + js := &e.state.jobs + if len(js.filling) > 0 { + if err := e.dispatchJob(false); err != nil { + return err + } + } + e.waitAllJobs() + js.mu.Lock() + fErr := js.flusherErr + js.mu.Unlock() + return fErr +} + // Close will flush the final output and close the stream. // The function will block until everything has been written. // The Encoder can still be re-used after calling this. @@ -455,12 +591,16 @@ func (e *Encoder) Close() error { if s.encoder == nil { return nil } + if e.o.concurrentBlocks { + return e.closeJobs() + } if s.w == nil { if len(s.filling) == 0 && !s.headerWritten && !s.eofWritten && s.nInput == 0 { return nil } return errors.New("zstd: encoder has no writer") } + err := e.nextBlock(true) if err != nil { if errors.Is(s.err, ErrEncoderClosed) { @@ -511,6 +651,68 @@ func (e *Encoder) Close() error { return s.err } +func (e *Encoder) closeJobs() error { + s := &e.state + js := &s.jobs + + if errors.Is(s.err, ErrEncoderClosed) { + return nil + } + + if s.w == nil { + if len(js.filling) == 0 && !s.headerWritten && !s.eofWritten && s.nInput == 0 { + return nil + } + return errors.New("zstd: encoder has no writer") + } + + if err := e.dispatchJob(true); err != nil { + e.shutdownJobWorkers() + if errors.Is(s.err, ErrEncoderClosed) { + return nil + } + return err + } + + if s.frameContentSize > 0 && s.nInput != s.frameContentSize { + e.shutdownJobWorkers() + return fmt.Errorf("frame content size %d given, but %d bytes was written", s.frameContentSize, s.nInput) + } + + if s.fullFrameWritten { + e.shutdownJobWorkers() + s.err = ErrEncoderClosed + return nil + } + + e.shutdownJobWorkers() + if js.flusherErr != nil { + return js.flusherErr + } + + // Write CRC + if e.o.crc { + var tmp [4]byte + _, s.err = s.w.Write(s.encoder.AppendCRC(tmp[:0])) + s.nWritten += 4 + } + + // Add padding + if s.err == nil && e.o.pad > 0 { + add := calcSkippableFrame(s.nWritten, int64(e.o.pad)) + frame, err := skippableFrame(js.filling[:0], add, rand.Reader) + if err != nil { + return err + } + _, s.err = s.w.Write(frame) + } + if s.err == nil { + s.err = ErrEncoderClosed + return nil + } + return s.err +} + // EncodeAll will encode all input in src and append it to dst. // This function can be called concurrently, but each call will only run on a single goroutine. // If empty input is given, nothing is returned, unless WithZeroFrames is specified. diff --git a/vendor/github.com/klauspost/compress/zstd/encoder_options.go b/vendor/github.com/klauspost/compress/zstd/encoder_options.go index e217be0a17..a808149673 100644 --- a/vendor/github.com/klauspost/compress/zstd/encoder_options.go +++ b/vendor/github.com/klauspost/compress/zstd/encoder_options.go @@ -14,22 +14,23 @@ type EOption func(*encoderOptions) error // options retains accumulated state of multiple options. type encoderOptions struct { - resetOpt bool - concurrent int - level EncoderLevel - single *bool - pad int - blockSize int - windowSize int - crc bool - fullZero bool - noEntropy bool - allLitEntropy bool - customWindow bool - customALEntropy bool - customBlockSize bool - lowMem bool - dict *dict + resetOpt bool + concurrent int + level EncoderLevel + single *bool + pad int + blockSize int + windowSize int + crc bool + fullZero bool + noEntropy bool + allLitEntropy bool + customWindow bool + customALEntropy bool + customBlockSize bool + lowMem bool + dict *dict + concurrentBlocks bool } func (o *encoderOptions) setDefault() { @@ -333,6 +334,42 @@ func WithLowerEncoderMem(b bool) EOption { } } +// WithConcurrentBlocks enables job-based parallel compression for streams. +// When enabled and concurrent > 1, input is split into large sections (jobs) +// that are compressed simultaneously by multiple goroutines. +// Each non-first job receives an overlap prefix from the previous job for match context. +// Output is flushed in order, producing a valid single-frame zstd stream. +// +// Currently disabled when used with dictionary encoding. +// Cannot be changed with ResetWithOptions. +func WithConcurrentBlocks(b bool) EOption { + return func(o *encoderOptions) error { + if o.resetOpt && b != o.concurrentBlocks { + return errors.New("WithConcurrentBlocks cannot be changed on Reset") + } + o.concurrentBlocks = b + return nil + } +} + +// jobSize returns the input section size per parallel job. +func (o *encoderOptions) jobSize() int { + s := max(o.windowSize*4, 512<<10) + return s +} + +// overlapSize returns the overlap prefix size for parallel jobs. +func (o *encoderOptions) overlapSize() int { + switch o.level { + case SpeedBestCompression: + return o.windowSize / 2 + case SpeedBetterCompression: + return o.windowSize / 4 + default: + return o.windowSize / 8 + } +} + // WithEncoderDict allows to register a dictionary that will be used for the encode. // // The slice dict must be in the [dictionary format] produced by diff --git a/vendor/github.com/klauspost/compress/zstd/fse_decoder_amd64.s b/vendor/github.com/klauspost/compress/zstd/fse_decoder_amd64.s index bcde398695..deeadc49eb 100644 --- a/vendor/github.com/klauspost/compress/zstd/fse_decoder_amd64.s +++ b/vendor/github.com/klauspost/compress/zstd/fse_decoder_amd64.s @@ -1,4 +1,4 @@ -// Code generated by command: go run gen_fse.go -out ../fse_decoder_amd64.s -pkg=zstd. DO NOT EDIT. +// Code generated by command: go run gen_fse.go -out ../fse_decoder.s -arch amd64,arm64 -pkg=zstd. DO NOT EDIT. //go:build !appengine && !noasm && gc && !noasm diff --git a/vendor/github.com/klauspost/compress/zstd/fse_decoder_arm64.s b/vendor/github.com/klauspost/compress/zstd/fse_decoder_arm64.s new file mode 100644 index 0000000000..77ee3913f0 --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/fse_decoder_arm64.s @@ -0,0 +1,153 @@ +// Code generated by command: go run gen_fse.go -out ../fse_decoder.s -arch amd64,arm64 -pkg=zstd. DO NOT EDIT. +// EXPERIMENTAL arm64 output lowered from an amd64 avo program. + +//go:build arm64 && !appengine && !noasm && gc && !noasm + +// func buildDtable_asm(s *fseDecoder, ctx *buildDtableAsmContext) int +TEXT ·buildDtable_asm(SB), $0-24 + MOVD ctx+8(FP), R1 + MOVD s+0(FP), R6 + + // Load values + MOVBU 4098(R6), R2 + MOVD $0, R0 + MOVD $1, R16 + LSL R2, R16, R16 + ORR R16, R0, R0 + MOVD (R1), R3 + MOVD 16(R1), R5 + SUB $1, R0, R7 + MOVD 8(R1), R1 + MOVHU 4096(R6), R6 + + // End load values + // Init, lay down lowprob symbols + MOVD $0, R8 + JMP init_main_loop_condition + +init_main_loop: + ADD R8<<1, R1, R15 + MOVH (R15), R9 + AND $0xffff, R9, R15 + MOVD $-1, R16 + AND $0xffff, R16, R16 + CMP R16, R15 + BNE do_not_update_high_threshold + ADD R7<<3, R5, R15 + MOVB R8, 1(R15) + SUB $1, R7, R7 + MOVD $0x0000000000000001, R9 + +do_not_update_high_threshold: + ADD R8<<1, R3, R15 + MOVH R9, (R15) + ADD $1, R8, R8 + +init_main_loop_condition: + CMP R6, R8 + BLT init_main_loop + + // Spread symbols + // Calculate table step + MOVD R0, R8 + LSR $0x01, R8, R8 + MOVD R0, R9 + LSR $0x03, R9, R9 + ADD R9, R8, R8 + ADD $3, R8, R8 + + // Fill add bits values + SUB $1, R0, R9 + MOVD $0, R10 + MOVD $0, R11 + JMP spread_main_loop_condition + +spread_main_loop: + MOVD $0, R12 + ADD R11<<1, R1, R15 + MOVH (R15), R13 + JMP spread_inner_loop_condition + +spread_inner_loop: + ADD R10<<3, R5, R15 + MOVB R11, 1(R15) + +adjust_position: + ADD R8, R10, R10 + AND R9, R10, R10 + CMP R7, R10 + BGT adjust_position + ADD $1, R12, R12 + +spread_inner_loop_condition: + CMP R13, R12 + BLT spread_inner_loop + ADD $1, R11, R11 + +spread_main_loop_condition: + CMP R6, R11 + BLT spread_main_loop + TST R10, R10 + BEQ spread_check_ok + MOVD ctx+8(FP), R0 + MOVD R10, 24(R0) + MOVD $+1, R16 + MOVD R16, ret+16(FP) + RET + +spread_check_ok: + // Build Decoding table + MOVD $0, R6 + +build_table_main_table: + ADD R6<<3, R5, R15 + MOVBU 1(R15), R1 + ADD R1<<1, R3, R15 + MOVHU (R15), R7 + ADD $1, R7, R8 + ADD R1<<1, R3, R15 + MOVH R8, (R15) + MOVD R7, R8 + CLZ R8, R16 + MOVD $63, R8 + SUB R16, R8, R8 + MOVD R2, R1 + SUB R8, R1, R1 + LSL R1, R7, R7 + SUB R0, R7, R7 + ADD R6<<3, R5, R15 + MOVB R1, (R15) + ADD R6<<3, R5, R15 + MOVH R7, 2(R15) + CMP R0, R7 + BLE build_table_check1_ok + MOVD ctx+8(FP), R1 + MOVD R7, 24(R1) + MOVD R0, 32(R1) + MOVD $+2, R16 + MOVD R16, ret+16(FP) + RET + +build_table_check1_ok: + AND $0xff, R1, R15 + AND $0xff, R1, R16 + TST R16, R15 + BNE build_table_check2_ok + AND $0xffff, R7, R15 + AND $0xffff, R6, R16 + CMP R16, R15 + BNE build_table_check2_ok + MOVD ctx+8(FP), R0 + MOVD R7, 24(R0) + MOVD R6, 32(R0) + MOVD $+3, R16 + MOVD R16, ret+16(FP) + RET + +build_table_check2_ok: + ADD $1, R6, R6 + CMP R0, R6 + BLT build_table_main_table + MOVD $+0, R16 + MOVD R16, ret+16(FP) + RET diff --git a/vendor/github.com/klauspost/compress/zstd/fse_decoder_amd64.go b/vendor/github.com/klauspost/compress/zstd/fse_decoder_asm.go similarity index 81% rename from vendor/github.com/klauspost/compress/zstd/fse_decoder_amd64.go rename to vendor/github.com/klauspost/compress/zstd/fse_decoder_asm.go index b8c8607b5d..4ffc7e3c9f 100644 --- a/vendor/github.com/klauspost/compress/zstd/fse_decoder_amd64.go +++ b/vendor/github.com/klauspost/compress/zstd/fse_decoder_asm.go @@ -1,4 +1,4 @@ -//go:build amd64 && !appengine && !noasm && gc +//go:build (amd64 || arm64) && !appengine && !noasm && gc package zstd @@ -6,6 +6,10 @@ import ( "fmt" ) +// buildDtable_asm is generated by _generate/gen_fse.go and lowered to each +// architecture (amd64 by goasm, arm64 by the avo arm64 lowering printer). The +// Go side is identical across architectures, so it lives here. + type buildDtableAsmContext struct { // inputs stateTable *uint16 @@ -18,7 +22,7 @@ type buildDtableAsmContext struct { errParam2 uint64 } -// buildDtable_asm is an x86 assembly implementation of fseDecoder.buildDtable. +// buildDtable_asm is an assembly implementation of fseDecoder.buildDtable. // Function returns non-zero exit code on error. // //go:noescape diff --git a/vendor/github.com/klauspost/compress/zstd/fse_decoder_generic.go b/vendor/github.com/klauspost/compress/zstd/fse_decoder_generic.go index 2138f8091a..38fd2ccb2a 100644 --- a/vendor/github.com/klauspost/compress/zstd/fse_decoder_generic.go +++ b/vendor/github.com/klauspost/compress/zstd/fse_decoder_generic.go @@ -1,4 +1,4 @@ -//go:build !amd64 || appengine || !gc || noasm +//go:build (!amd64 && !arm64) || appengine || !gc || noasm package zstd diff --git a/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.go b/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.go index 18c3703ddc..1281da885c 100644 --- a/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.go +++ b/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.go @@ -3,321 +3,83 @@ package zstd import ( - "fmt" - "io" - "github.com/klauspost/compress/internal/cpuinfo" ) -type decodeSyncAsmContext struct { - llTable []decSymbol - mlTable []decSymbol - ofTable []decSymbol - llState uint64 - mlState uint64 - ofState uint64 - iteration int - litRemain int - out []byte - outPosition int - literals []byte - litPosition int - history []byte - windowSize int - ll int // set on error (not for all errors, please refer to _generate/gen.go) - ml int // set on error (not for all errors, please refer to _generate/gen.go) - mo int // set on error (not for all errors, please refer to _generate/gen.go) -} +// The shared decode/decodeSync/executeSimple wrappers and context structs live +// in seqdec_asm.go; this file only declares the amd64 asm routines and the +// dispatch helpers that pick the BMI2 / non-BMI2 (and 56-bit / safe) variant. -// sequenceDecs_decodeSync_amd64 implements the main loop of sequenceDecs.decodeSync in x86 asm. +// sequenceDecs_decode implements the main loop of sequenceDecs in x86 asm. // // Please refer to seqdec_generic.go for the reference implementation. // //go:noescape -func sequenceDecs_decodeSync_amd64(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int +func sequenceDecs_decode_amd64(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int -// sequenceDecs_decodeSync_bmi2 implements the main loop of sequenceDecs.decodeSync in x86 asm with BMI2 extensions. +// sequenceDecs_decode_56_amd64 implements the main loop of sequenceDecs in x86 asm. // //go:noescape -func sequenceDecs_decodeSync_bmi2(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int +func sequenceDecs_decode_56_amd64(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int -// sequenceDecs_decodeSync_safe_amd64 does the same as above, but does not write more than output buffer. +// sequenceDecs_decode_bmi2 implements the main loop of sequenceDecs in x86 asm with BMI2 extensions. // //go:noescape -func sequenceDecs_decodeSync_safe_amd64(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int +func sequenceDecs_decode_bmi2(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int -// sequenceDecs_decodeSync_safe_bmi2 does the same as above, but does not write more than output buffer. +// sequenceDecs_decode_56_bmi2 implements the main loop of sequenceDecs in x86 asm with BMI2 extensions. // //go:noescape -func sequenceDecs_decodeSync_safe_bmi2(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int - -// decode sequences from the stream with the provided history but without a dictionary. -func (s *sequenceDecs) decodeSyncSimple(hist []byte) (bool, error) { - if len(s.dict) > 0 { - return false, nil - } - if s.maxSyncLen == 0 && cap(s.out)-len(s.out) < maxCompressedBlockSize { - return false, nil - } - - // FIXME: Using unsafe memory copies leads to rare, random crashes - // with fuzz testing. It is therefore disabled for now. - const useSafe = true - /* - useSafe := false - if s.maxSyncLen == 0 && cap(s.out)-len(s.out) < maxCompressedBlockSizeAlloc { - useSafe = true - } - if s.maxSyncLen > 0 && cap(s.out)-len(s.out)-compressedBlockOverAlloc < int(s.maxSyncLen) { - useSafe = true - } - if cap(s.literals) < len(s.literals)+compressedBlockOverAlloc { - useSafe = true - } - */ - - br := s.br - - maxBlockSize := min(s.windowSize, maxCompressedBlockSize) - - ctx := decodeSyncAsmContext{ - llTable: s.litLengths.fse.dt[:maxTablesize], - mlTable: s.matchLengths.fse.dt[:maxTablesize], - ofTable: s.offsets.fse.dt[:maxTablesize], - llState: uint64(s.litLengths.state.state), - mlState: uint64(s.matchLengths.state.state), - ofState: uint64(s.offsets.state.state), - iteration: s.nSeqs - 1, - litRemain: len(s.literals), - out: s.out, - outPosition: len(s.out), - literals: s.literals, - windowSize: s.windowSize, - history: hist, - } - - s.seqSize = 0 - startSize := len(s.out) +func sequenceDecs_decode_56_bmi2(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int - var errCode int +// decodeAsm runs the sequenceDecs decode loop, choosing the BMI2 / 56-bit variant. +func decodeAsm(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext, lte56bits bool) int { if cpuinfo.HasBMI2() { - if useSafe { - errCode = sequenceDecs_decodeSync_safe_bmi2(s, br, &ctx) - } else { - errCode = sequenceDecs_decodeSync_bmi2(s, br, &ctx) - } - } else { - if useSafe { - errCode = sequenceDecs_decodeSync_safe_amd64(s, br, &ctx) - } else { - errCode = sequenceDecs_decodeSync_amd64(s, br, &ctx) - } - } - switch errCode { - case noError: - break - - case errorMatchLenOfsMismatch: - return true, fmt.Errorf("zero matchoff and matchlen (%d) > 0", ctx.ml) - - case errorMatchLenTooBig: - return true, fmt.Errorf("match len (%d) bigger than max allowed length", ctx.ml) - - case errorMatchOffTooBig: - return true, fmt.Errorf("match offset (%d) bigger than current history (%d)", - ctx.mo, ctx.outPosition+len(hist)-startSize) - - case errorNotEnoughLiterals: - return true, fmt.Errorf("unexpected literal count, want %d bytes, but only %d is available", - ctx.ll, ctx.litRemain+ctx.ll) - - case errorOverread: - return true, io.ErrUnexpectedEOF - - case errorNotEnoughSpace: - size := ctx.outPosition + ctx.ll + ctx.ml - if debugDecoder { - println("msl:", s.maxSyncLen, "cap", cap(s.out), "bef:", startSize, "sz:", size-startSize, "mbs:", maxBlockSize, "outsz:", cap(s.out)-startSize) + if lte56bits { + return sequenceDecs_decode_56_bmi2(s, br, ctx) } - return true, fmt.Errorf("output bigger than max block size (%d)", maxBlockSize) - - default: - return true, fmt.Errorf("sequenceDecs_decode returned erroneous code %d", errCode) - } - - s.seqSize += ctx.litRemain - if s.seqSize > maxBlockSize { - return true, fmt.Errorf("output bigger than max block size (%d)", maxBlockSize) + return sequenceDecs_decode_bmi2(s, br, ctx) } - err := br.close() - if err != nil { - printf("Closing sequences: %v, %+v\n", err, *br) - return true, err + if lte56bits { + return sequenceDecs_decode_56_amd64(s, br, ctx) } - - s.literals = s.literals[ctx.litPosition:] - t := ctx.outPosition - s.out = s.out[:t] - - // Add final literals - s.out = append(s.out, s.literals...) - if debugDecoder { - t += len(s.literals) - if t != len(s.out) { - panic(fmt.Errorf("length mismatch, want %d, got %d", len(s.out), t)) - } - } - - return true, nil + return sequenceDecs_decode_amd64(s, br, ctx) } -// -------------------------------------------------------------------------------- - -type decodeAsmContext struct { - llTable []decSymbol - mlTable []decSymbol - ofTable []decSymbol - llState uint64 - mlState uint64 - ofState uint64 - iteration int - seqs []seqVals - litRemain int -} - -const noError = 0 - -// error reported when mo == 0 && ml > 0 -const errorMatchLenOfsMismatch = 1 - -// error reported when ml > maxMatchLen -const errorMatchLenTooBig = 2 - -// error reported when mo > available history or mo > s.windowSize -const errorMatchOffTooBig = 3 - -// error reported when the sum of literal lengths exeeceds the literal buffer size -const errorNotEnoughLiterals = 4 - -// error reported when capacity of `out` is too small -const errorNotEnoughSpace = 5 - -// error reported when bits are overread. -const errorOverread = 6 - -// sequenceDecs_decode implements the main loop of sequenceDecs in x86 asm. +// sequenceDecs_decodeSync_amd64 implements the main loop of sequenceDecs.decodeSync in x86 asm. // // Please refer to seqdec_generic.go for the reference implementation. // //go:noescape -func sequenceDecs_decode_amd64(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int +func sequenceDecs_decodeSync_amd64(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int -// sequenceDecs_decode implements the main loop of sequenceDecs in x86 asm. -// -// Please refer to seqdec_generic.go for the reference implementation. +// sequenceDecs_decodeSync_bmi2 implements the main loop of sequenceDecs.decodeSync in x86 asm with BMI2 extensions. // //go:noescape -func sequenceDecs_decode_56_amd64(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int +func sequenceDecs_decodeSync_bmi2(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int -// sequenceDecs_decode implements the main loop of sequenceDecs in x86 asm with BMI2 extensions. +// sequenceDecs_decodeSync_safe_amd64 does the same as above, but does not write more than output buffer. // //go:noescape -func sequenceDecs_decode_bmi2(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int +func sequenceDecs_decodeSync_safe_amd64(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int -// sequenceDecs_decode implements the main loop of sequenceDecs in x86 asm with BMI2 extensions. +// sequenceDecs_decodeSync_safe_bmi2 does the same as above, but does not write more than output buffer. // //go:noescape -func sequenceDecs_decode_56_bmi2(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int - -// decode sequences from the stream without the provided history. -func (s *sequenceDecs) decode(seqs []seqVals) error { - br := s.br - - maxBlockSize := min(s.windowSize, maxCompressedBlockSize) - - ctx := decodeAsmContext{ - llTable: s.litLengths.fse.dt[:maxTablesize], - mlTable: s.matchLengths.fse.dt[:maxTablesize], - ofTable: s.offsets.fse.dt[:maxTablesize], - llState: uint64(s.litLengths.state.state), - mlState: uint64(s.matchLengths.state.state), - ofState: uint64(s.offsets.state.state), - seqs: seqs, - iteration: len(seqs) - 1, - litRemain: len(s.literals), - } - - if debugDecoder { - println("decode: decoding", len(seqs), "sequences", br.remain(), "bits remain on stream") - } +func sequenceDecs_decodeSync_safe_bmi2(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int - s.seqSize = 0 - lte56bits := s.maxBits+s.offsets.fse.actualTableLog+s.matchLengths.fse.actualTableLog+s.litLengths.fse.actualTableLog <= 56 - var errCode int +// decodeSyncAsm runs the decodeSync loop, choosing the BMI2 / safe variant. +func decodeSyncAsm(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext, safe bool) int { if cpuinfo.HasBMI2() { - if lte56bits { - errCode = sequenceDecs_decode_56_bmi2(s, br, &ctx) - } else { - errCode = sequenceDecs_decode_bmi2(s, br, &ctx) - } - } else { - if lte56bits { - errCode = sequenceDecs_decode_56_amd64(s, br, &ctx) - } else { - errCode = sequenceDecs_decode_amd64(s, br, &ctx) + if safe { + return sequenceDecs_decodeSync_safe_bmi2(s, br, ctx) } + return sequenceDecs_decodeSync_bmi2(s, br, ctx) } - if errCode != 0 { - i := len(seqs) - ctx.iteration - 1 - switch errCode { - case errorMatchLenOfsMismatch: - ml := ctx.seqs[i].ml - return fmt.Errorf("zero matchoff and matchlen (%d) > 0", ml) - - case errorMatchLenTooBig: - ml := ctx.seqs[i].ml - return fmt.Errorf("match len (%d) bigger than max allowed length", ml) - - case errorNotEnoughLiterals: - ll := ctx.seqs[i].ll - return fmt.Errorf("unexpected literal count, want %d bytes, but only %d is available", ll, ctx.litRemain+ll) - case errorOverread: - return io.ErrUnexpectedEOF - } - - return fmt.Errorf("sequenceDecs_decode_amd64 returned erroneous code %d", errCode) + if safe { + return sequenceDecs_decodeSync_safe_amd64(s, br, ctx) } - - if ctx.litRemain < 0 { - return fmt.Errorf("literal count is too big: total available %d, total requested %d", - len(s.literals), len(s.literals)-ctx.litRemain) - } - - s.seqSize += ctx.litRemain - if s.seqSize > maxBlockSize { - return fmt.Errorf("output bigger than max block size (%d)", maxBlockSize) - } - if debugDecoder { - println("decode: ", br.remain(), "bits remain on stream. code:", errCode) - } - err := br.close() - if err != nil { - printf("Closing sequences: %v, %+v\n", err, *br) - } - return err -} - -// -------------------------------------------------------------------------------- - -type executeAsmContext struct { - seqs []seqVals - seqIndex int - out []byte - history []byte - literals []byte - outPosition int - litPosition int - windowSize int + return sequenceDecs_decodeSync_amd64(s, br, ctx) } // sequenceDecs_executeSimple_amd64 implements the main loop of sequenceDecs.executeSimple in x86 asm. @@ -334,54 +96,10 @@ func sequenceDecs_executeSimple_amd64(ctx *executeAsmContext) bool //go:noescape func sequenceDecs_executeSimple_safe_amd64(ctx *executeAsmContext) bool -// executeSimple handles cases when dictionary is not used. -func (s *sequenceDecs) executeSimple(seqs []seqVals, hist []byte) error { - // Ensure we have enough output size... - if len(s.out)+s.seqSize+compressedBlockOverAlloc > cap(s.out) { - addBytes := s.seqSize + len(s.out) + compressedBlockOverAlloc - s.out = append(s.out, make([]byte, addBytes)...) - s.out = s.out[:len(s.out)-addBytes] - } - - if debugDecoder { - printf("Execute %d seqs with literals: %d into %d bytes\n", len(seqs), len(s.literals), s.seqSize) - } - - var t = len(s.out) - out := s.out[:t+s.seqSize] - - ctx := executeAsmContext{ - seqs: seqs, - seqIndex: 0, - out: out, - history: hist, - outPosition: t, - litPosition: 0, - literals: s.literals, - windowSize: s.windowSize, +// executeSimpleAsm runs the executeSimple loop, choosing the safe variant. +func executeSimpleAsm(ctx *executeAsmContext, safe bool) bool { + if safe { + return sequenceDecs_executeSimple_safe_amd64(ctx) } - var ok bool - if cap(s.literals) < len(s.literals)+compressedBlockOverAlloc { - ok = sequenceDecs_executeSimple_safe_amd64(&ctx) - } else { - ok = sequenceDecs_executeSimple_amd64(&ctx) - } - if !ok { - return fmt.Errorf("match offset (%d) bigger than current history (%d)", - seqs[ctx.seqIndex].mo, ctx.outPosition+len(hist)) - } - s.literals = s.literals[ctx.litPosition:] - t = ctx.outPosition - - // Add final literals - copy(out[t:], s.literals) - if debugDecoder { - t += len(s.literals) - if t != len(out) { - panic(fmt.Errorf("length mismatch, want %d, got %d, ss: %d", len(out), t, s.seqSize)) - } - } - s.out = out - - return nil + return sequenceDecs_executeSimple_amd64(ctx) } diff --git a/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.s b/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.s index a708ca6d3d..3fc381c7a7 100644 --- a/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.s +++ b/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.s @@ -1,4 +1,4 @@ -// Code generated by command: go run gen.go -out ../seqdec_amd64.s -pkg=zstd. DO NOT EDIT. +// Code generated by command: go run gen.go -out ../seqdec.s -arch amd64,arm64 -pkg=zstd. DO NOT EDIT. //go:build !appengine && !noasm && gc && !noasm diff --git a/vendor/github.com/klauspost/compress/zstd/seqdec_arm64.go b/vendor/github.com/klauspost/compress/zstd/seqdec_arm64.go new file mode 100644 index 0000000000..5ad262acff --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/seqdec_arm64.go @@ -0,0 +1,70 @@ +//go:build arm64 && !appengine && !noasm && gc + +package zstd + +// The shared decode/decodeSync/executeSimple wrappers and context structs live +// in seqdec_asm.go; this file only declares the arm64 asm routines (generated +// by the avo arm64 lowering printer) and the dispatch helpers. arm64 has no +// BMI2, so each helper selects only between the 56-bit / safe variants. + +// sequenceDecs_decode_arm64 implements the main loop of sequenceDecs in arm64 asm. +// +// Please refer to seqdec_generic.go for the reference implementation. +// +//go:noescape +func sequenceDecs_decode_arm64(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int + +// sequenceDecs_decode_56_arm64 implements the main loop of sequenceDecs in arm64 asm. +// +//go:noescape +func sequenceDecs_decode_56_arm64(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int + +// decodeAsm runs the sequenceDecs decode loop, choosing the 56-bit variant. +func decodeAsm(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext, lte56bits bool) int { + if lte56bits { + return sequenceDecs_decode_56_arm64(s, br, ctx) + } + return sequenceDecs_decode_arm64(s, br, ctx) +} + +// sequenceDecs_decodeSync_arm64 implements the main loop of sequenceDecs.decodeSync in arm64 asm. +// +// Please refer to seqdec_generic.go for the reference implementation. +// +//go:noescape +func sequenceDecs_decodeSync_arm64(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int + +// sequenceDecs_decodeSync_safe_arm64 does the same as above, but does not write more than output buffer. +// +//go:noescape +func sequenceDecs_decodeSync_safe_arm64(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int + +// decodeSyncAsm runs the decodeSync loop, choosing the safe variant. +func decodeSyncAsm(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext, safe bool) int { + if safe { + return sequenceDecs_decodeSync_safe_arm64(s, br, ctx) + } + return sequenceDecs_decodeSync_arm64(s, br, ctx) +} + +// sequenceDecs_executeSimple_arm64 implements the main loop of sequenceDecs.executeSimple in arm64 asm. +// +// Returns false if a match offset is too big. +// +// Please refer to seqdec_generic.go for the reference implementation. +// +//go:noescape +func sequenceDecs_executeSimple_arm64(ctx *executeAsmContext) bool + +// Same as above, but with safe memcopies +// +//go:noescape +func sequenceDecs_executeSimple_safe_arm64(ctx *executeAsmContext) bool + +// executeSimpleAsm runs the executeSimple loop, choosing the safe variant. +func executeSimpleAsm(ctx *executeAsmContext, safe bool) bool { + if safe { + return sequenceDecs_executeSimple_safe_arm64(ctx) + } + return sequenceDecs_executeSimple_arm64(ctx) +} diff --git a/vendor/github.com/klauspost/compress/zstd/seqdec_arm64.s b/vendor/github.com/klauspost/compress/zstd/seqdec_arm64.s new file mode 100644 index 0000000000..a468e5fc2c --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/seqdec_arm64.s @@ -0,0 +1,2705 @@ +// Code generated by command: go run gen.go -out ../seqdec.s -arch amd64,arm64 -pkg=zstd. DO NOT EDIT. +// EXPERIMENTAL arm64 output lowered from an amd64 avo program. + +//go:build arm64 && !appengine && !noasm && gc && !noasm + +// func sequenceDecs_decode_amd64(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int +// Requires: CMOV +TEXT ·sequenceDecs_decode_arm64(SB), $8-32 + MOVD br+8(FP), R1 + MOVD 24(R1), R2 + MOVBU 40(R1), R3 + MOVD (R1), R0 + MOVD 32(R1), R5 + ADD R5, R0, R0 + MOVD R0, (RSP) + MOVD ctx+16(FP), R0 + MOVD 72(R0), R6 + MOVD 80(R0), R7 + MOVD 88(R0), R8 + MOVD 104(R0), R9 + MOVD s+0(FP), R0 + MOVD 144(R0), R10 + MOVD 152(R0), R11 + MOVD 160(R0), R12 + +sequenceDecs_decode_amd64_main_loop: + MOVD (RSP), R13 + + // Fill bitreader to have enough for the offset and match length. + CMP $0x08, R5 + BLT sequenceDecs_decode_amd64_fill_byte_by_byte + MOVD R3, R0 + LSR $0x03, R0, R0 + SUB R0, R13, R13 + MOVD (R13), R2 + SUB R0, R5, R5 + AND $0x07, R3, R3 + JMP sequenceDecs_decode_amd64_fill_end + +sequenceDecs_decode_amd64_fill_byte_by_byte: + CMP $0x00, R5 + BLE sequenceDecs_decode_amd64_fill_check_overread + CMP $0x07, R3 + BLE sequenceDecs_decode_amd64_fill_end + LSL $0x08, R2, R2 + SUB $0x01, R13, R13 + SUB $0x01, R5, R5 + SUB $0x08, R3, R3 + MOVBU (R13), R0 + ORR R0, R2, R2 + JMP sequenceDecs_decode_amd64_fill_byte_by_byte + +sequenceDecs_decode_amd64_fill_check_overread: + CMP $0x40, R3 + BHI error_overread + +sequenceDecs_decode_amd64_fill_end: + // Update offset + MOVD R8, R0 + MOVD R3, R1 + MOVD R2, R14 + LSL R1, R14, R14 + UBFX $8, R0, $8, R1 + LSR $0x20, R0, R0 + TST R1, R1 + BEQ sequenceDecs_decode_amd64_of_update_zero + ADD R1, R3, R3 + CMP $0x40, R3 + BHI sequenceDecs_decode_amd64_of_update_zero + CMP $0x40, R1 + BHS sequenceDecs_decode_amd64_of_update_zero + NEG R1, R1 + LSR R1, R14, R14 + ADD R14, R0, R0 + +sequenceDecs_decode_amd64_of_update_zero: + MOVD R0, 16(R9) + + // Update match length + MOVD R7, R0 + MOVD R3, R1 + MOVD R2, R14 + LSL R1, R14, R14 + UBFX $8, R0, $8, R1 + LSR $0x20, R0, R0 + TST R1, R1 + BEQ sequenceDecs_decode_amd64_ml_update_zero + ADD R1, R3, R3 + CMP $0x40, R3 + BHI sequenceDecs_decode_amd64_ml_update_zero + CMP $0x40, R1 + BHS sequenceDecs_decode_amd64_ml_update_zero + NEG R1, R1 + LSR R1, R14, R14 + ADD R14, R0, R0 + +sequenceDecs_decode_amd64_ml_update_zero: + MOVD R0, 8(R9) + + // Fill bitreader to have enough for the remaining + CMP $0x08, R5 + BLT sequenceDecs_decode_amd64_fill_2_byte_by_byte + MOVD R3, R0 + LSR $0x03, R0, R0 + SUB R0, R13, R13 + MOVD (R13), R2 + SUB R0, R5, R5 + AND $0x07, R3, R3 + JMP sequenceDecs_decode_amd64_fill_2_end + +sequenceDecs_decode_amd64_fill_2_byte_by_byte: + CMP $0x00, R5 + BLE sequenceDecs_decode_amd64_fill_2_check_overread + CMP $0x07, R3 + BLE sequenceDecs_decode_amd64_fill_2_end + LSL $0x08, R2, R2 + SUB $0x01, R13, R13 + SUB $0x01, R5, R5 + SUB $0x08, R3, R3 + MOVBU (R13), R0 + ORR R0, R2, R2 + JMP sequenceDecs_decode_amd64_fill_2_byte_by_byte + +sequenceDecs_decode_amd64_fill_2_check_overread: + CMP $0x40, R3 + BHI error_overread + +sequenceDecs_decode_amd64_fill_2_end: + // Update literal length + MOVD R6, R0 + MOVD R3, R1 + MOVD R2, R14 + LSL R1, R14, R14 + UBFX $8, R0, $8, R1 + LSR $0x20, R0, R0 + TST R1, R1 + BEQ sequenceDecs_decode_amd64_ll_update_zero + ADD R1, R3, R3 + CMP $0x40, R3 + BHI sequenceDecs_decode_amd64_ll_update_zero + CMP $0x40, R1 + BHS sequenceDecs_decode_amd64_ll_update_zero + NEG R1, R1 + LSR R1, R14, R14 + ADD R14, R0, R0 + +sequenceDecs_decode_amd64_ll_update_zero: + MOVD R0, (R9) + + // Fill bitreader for state updates + MOVD R13, (RSP) + MOVD R8, R0 + LSR $0x08, R0, R0 + MOVBU R0, R0 + MOVD ctx+16(FP), R1 + MOVD 96(R1), R16 + CMP $0x00, R16 + BEQ sequenceDecs_decode_amd64_skip_update + + // Update Literal Length State + MOVBU R6, R13 + LSRW $0x10, R6, R6 + ADD R13, R3, R1 + MOVD R2, R14 + MOVD R1, R3 + NEG R1, R16 + ROR R16, R14, R14 + MOVD $0x00000001, R4 + MOVB R13, R1 + LSLW R1, R4, R4 + SUBW $1, R4, R4 + AND R4, R14, R14 + ADD R14, R6, R6 + + // Load ctx.llTable + MOVD ctx+16(FP), R1 + MOVD (R1), R1 + ADD R6<<3, R1, R15 + MOVD (R15), R6 + + // Update Match Length State + MOVBU R7, R13 + LSRW $0x10, R7, R7 + ADD R13, R3, R1 + MOVD R2, R14 + MOVD R1, R3 + NEG R1, R16 + ROR R16, R14, R14 + MOVD $0x00000001, R4 + MOVB R13, R1 + LSLW R1, R4, R4 + SUBW $1, R4, R4 + AND R4, R14, R14 + ADD R14, R7, R7 + + // Load ctx.mlTable + MOVD ctx+16(FP), R1 + MOVD 24(R1), R1 + ADD R7<<3, R1, R15 + MOVD (R15), R7 + + // Update Offset State + MOVBU R8, R13 + LSRW $0x10, R8, R8 + ADD R13, R3, R1 + MOVD R2, R14 + MOVD R1, R3 + NEG R1, R16 + ROR R16, R14, R14 + MOVD $0x00000001, R4 + MOVB R13, R1 + LSLW R1, R4, R4 + SUBW $1, R4, R4 + AND R4, R14, R14 + ADD R14, R8, R8 + + // Load ctx.ofTable + MOVD ctx+16(FP), R1 + MOVD 48(R1), R1 + ADD R8<<3, R1, R15 + MOVD (R15), R8 + +sequenceDecs_decode_amd64_skip_update: + // Adjust offset + MOVD 16(R9), R1 + CMP $0x01, R0 + BLS sequenceDecs_decode_amd64_adjust_offsetB_1_or_0 + MOVD R11, R12 + MOVD R10, R11 + MOVD R1, R10 + JMP sequenceDecs_decode_amd64_after_adjust + +sequenceDecs_decode_amd64_adjust_offsetB_1_or_0: + MOVD (R9), R16 + CMP $0x00000000, R16 + BNE sequenceDecs_decode_amd64_adjust_offset_maybezero + ADD $1, R1, R1 + JMP sequenceDecs_decode_amd64_adjust_offset_nonzero + +sequenceDecs_decode_amd64_adjust_offset_maybezero: + TST R1, R1 + BNE sequenceDecs_decode_amd64_adjust_offset_nonzero + MOVD R10, R1 + JMP sequenceDecs_decode_amd64_after_adjust + +sequenceDecs_decode_amd64_adjust_offset_nonzero: + CMP $0x01, R1 + BLO sequenceDecs_decode_amd64_adjust_zero + BEQ sequenceDecs_decode_amd64_adjust_one + CMP $0x02, R1 + BHI sequenceDecs_decode_amd64_adjust_three + JMP sequenceDecs_decode_amd64_adjust_two + +sequenceDecs_decode_amd64_adjust_zero: + MOVD R10, R0 + JMP sequenceDecs_decode_amd64_adjust_test_temp_valid + +sequenceDecs_decode_amd64_adjust_one: + MOVD R11, R0 + JMP sequenceDecs_decode_amd64_adjust_test_temp_valid + +sequenceDecs_decode_amd64_adjust_two: + MOVD R12, R0 + JMP sequenceDecs_decode_amd64_adjust_test_temp_valid + +sequenceDecs_decode_amd64_adjust_three: + SUB $1, R10, R0 + +sequenceDecs_decode_amd64_adjust_test_temp_valid: + TST R0, R0 + BNE sequenceDecs_decode_amd64_adjust_temp_valid + MOVD $0x00000001, R0 + +sequenceDecs_decode_amd64_adjust_temp_valid: + CMP $0x01, R1 + CSEL NE, R11, R12, R12 + MOVD R10, R11 + MOVD R0, R10 + MOVD R0, R1 + +sequenceDecs_decode_amd64_after_adjust: + MOVD R1, 16(R9) + + // Check values + MOVD 8(R9), R0 + MOVD (R9), R13 + ADD R13, R0, R14 + MOVD s+0(FP), R4 + MOVD 256(R4), R16 + ADD R14, R16, R16 + MOVD R16, 256(R4) + MOVD ctx+16(FP), R14 + MOVD 128(R14), R16 + SUBS R13, R16, R16 + MOVD R16, 128(R14) + BMI error_not_enough_literals + CMP $0x00020002, R0 + BHI sequenceDecs_decode_amd64_error_match_len_too_big + TST R1, R1 + BNE sequenceDecs_decode_amd64_match_len_ofs_ok + TST R0, R0 + BNE sequenceDecs_decode_amd64_error_match_len_ofs_mismatch + +sequenceDecs_decode_amd64_match_len_ofs_ok: + ADD $0x18, R9, R9 + MOVD ctx+16(FP), R0 + MOVD 96(R0), R16 + SUBS $1, R16, R16 + MOVD R16, 96(R0) + BPL sequenceDecs_decode_amd64_main_loop + MOVD s+0(FP), R0 + MOVD R10, 144(R0) + MOVD R11, 152(R0) + MOVD R12, 160(R0) + MOVD br+8(FP), R0 + MOVD R2, 24(R0) + MOVB R3, 40(R0) + MOVD R5, 32(R0) + + // Return success + MOVD $0x00000000, R16 + MOVD R16, ret+24(FP) + RET + + // Return with match length error +sequenceDecs_decode_amd64_error_match_len_ofs_mismatch: + MOVD $0x00000001, R16 + MOVD R16, ret+24(FP) + RET + + // Return with match too long error +sequenceDecs_decode_amd64_error_match_len_too_big: + MOVD $0x00000002, R16 + MOVD R16, ret+24(FP) + RET + + // Return with match offset too long error + MOVD $0x00000003, R16 + MOVD R16, ret+24(FP) + RET + + // Return with not enough literals error +error_not_enough_literals: + MOVD $0x00000004, R16 + MOVD R16, ret+24(FP) + RET + + // Return with overread error +error_overread: + MOVD $0x00000006, R16 + MOVD R16, ret+24(FP) + RET + +// func sequenceDecs_decode_56_amd64(s *sequenceDecs, br *bitReader, ctx *decodeAsmContext) int +// Requires: CMOV +TEXT ·sequenceDecs_decode_56_arm64(SB), $8-32 + MOVD br+8(FP), R1 + MOVD 24(R1), R2 + MOVBU 40(R1), R3 + MOVD (R1), R0 + MOVD 32(R1), R5 + ADD R5, R0, R0 + MOVD R0, (RSP) + MOVD ctx+16(FP), R0 + MOVD 72(R0), R6 + MOVD 80(R0), R7 + MOVD 88(R0), R8 + MOVD 104(R0), R9 + MOVD s+0(FP), R0 + MOVD 144(R0), R10 + MOVD 152(R0), R11 + MOVD 160(R0), R12 + +sequenceDecs_decode_56_amd64_main_loop: + MOVD (RSP), R13 + + // Fill bitreader to have enough for the offset and match length. + CMP $0x08, R5 + BLT sequenceDecs_decode_56_amd64_fill_byte_by_byte + MOVD R3, R0 + LSR $0x03, R0, R0 + SUB R0, R13, R13 + MOVD (R13), R2 + SUB R0, R5, R5 + AND $0x07, R3, R3 + JMP sequenceDecs_decode_56_amd64_fill_end + +sequenceDecs_decode_56_amd64_fill_byte_by_byte: + CMP $0x00, R5 + BLE sequenceDecs_decode_56_amd64_fill_check_overread + CMP $0x07, R3 + BLE sequenceDecs_decode_56_amd64_fill_end + LSL $0x08, R2, R2 + SUB $0x01, R13, R13 + SUB $0x01, R5, R5 + SUB $0x08, R3, R3 + MOVBU (R13), R0 + ORR R0, R2, R2 + JMP sequenceDecs_decode_56_amd64_fill_byte_by_byte + +sequenceDecs_decode_56_amd64_fill_check_overread: + CMP $0x40, R3 + BHI error_overread + +sequenceDecs_decode_56_amd64_fill_end: + // Update offset + MOVD R8, R0 + MOVD R3, R1 + MOVD R2, R14 + LSL R1, R14, R14 + UBFX $8, R0, $8, R1 + LSR $0x20, R0, R0 + TST R1, R1 + BEQ sequenceDecs_decode_56_amd64_of_update_zero + ADD R1, R3, R3 + CMP $0x40, R3 + BHI sequenceDecs_decode_56_amd64_of_update_zero + CMP $0x40, R1 + BHS sequenceDecs_decode_56_amd64_of_update_zero + NEG R1, R1 + LSR R1, R14, R14 + ADD R14, R0, R0 + +sequenceDecs_decode_56_amd64_of_update_zero: + MOVD R0, 16(R9) + + // Update match length + MOVD R7, R0 + MOVD R3, R1 + MOVD R2, R14 + LSL R1, R14, R14 + UBFX $8, R0, $8, R1 + LSR $0x20, R0, R0 + TST R1, R1 + BEQ sequenceDecs_decode_56_amd64_ml_update_zero + ADD R1, R3, R3 + CMP $0x40, R3 + BHI sequenceDecs_decode_56_amd64_ml_update_zero + CMP $0x40, R1 + BHS sequenceDecs_decode_56_amd64_ml_update_zero + NEG R1, R1 + LSR R1, R14, R14 + ADD R14, R0, R0 + +sequenceDecs_decode_56_amd64_ml_update_zero: + MOVD R0, 8(R9) + + // Update literal length + MOVD R6, R0 + MOVD R3, R1 + MOVD R2, R14 + LSL R1, R14, R14 + UBFX $8, R0, $8, R1 + LSR $0x20, R0, R0 + TST R1, R1 + BEQ sequenceDecs_decode_56_amd64_ll_update_zero + ADD R1, R3, R3 + CMP $0x40, R3 + BHI sequenceDecs_decode_56_amd64_ll_update_zero + CMP $0x40, R1 + BHS sequenceDecs_decode_56_amd64_ll_update_zero + NEG R1, R1 + LSR R1, R14, R14 + ADD R14, R0, R0 + +sequenceDecs_decode_56_amd64_ll_update_zero: + MOVD R0, (R9) + + // Fill bitreader for state updates + MOVD R13, (RSP) + MOVD R8, R0 + LSR $0x08, R0, R0 + MOVBU R0, R0 + MOVD ctx+16(FP), R1 + MOVD 96(R1), R16 + CMP $0x00, R16 + BEQ sequenceDecs_decode_56_amd64_skip_update + + // Update Literal Length State + MOVBU R6, R13 + LSRW $0x10, R6, R6 + ADD R13, R3, R1 + MOVD R2, R14 + MOVD R1, R3 + NEG R1, R16 + ROR R16, R14, R14 + MOVD $0x00000001, R4 + MOVB R13, R1 + LSLW R1, R4, R4 + SUBW $1, R4, R4 + AND R4, R14, R14 + ADD R14, R6, R6 + + // Load ctx.llTable + MOVD ctx+16(FP), R1 + MOVD (R1), R1 + ADD R6<<3, R1, R15 + MOVD (R15), R6 + + // Update Match Length State + MOVBU R7, R13 + LSRW $0x10, R7, R7 + ADD R13, R3, R1 + MOVD R2, R14 + MOVD R1, R3 + NEG R1, R16 + ROR R16, R14, R14 + MOVD $0x00000001, R4 + MOVB R13, R1 + LSLW R1, R4, R4 + SUBW $1, R4, R4 + AND R4, R14, R14 + ADD R14, R7, R7 + + // Load ctx.mlTable + MOVD ctx+16(FP), R1 + MOVD 24(R1), R1 + ADD R7<<3, R1, R15 + MOVD (R15), R7 + + // Update Offset State + MOVBU R8, R13 + LSRW $0x10, R8, R8 + ADD R13, R3, R1 + MOVD R2, R14 + MOVD R1, R3 + NEG R1, R16 + ROR R16, R14, R14 + MOVD $0x00000001, R4 + MOVB R13, R1 + LSLW R1, R4, R4 + SUBW $1, R4, R4 + AND R4, R14, R14 + ADD R14, R8, R8 + + // Load ctx.ofTable + MOVD ctx+16(FP), R1 + MOVD 48(R1), R1 + ADD R8<<3, R1, R15 + MOVD (R15), R8 + +sequenceDecs_decode_56_amd64_skip_update: + // Adjust offset + MOVD 16(R9), R1 + CMP $0x01, R0 + BLS sequenceDecs_decode_56_amd64_adjust_offsetB_1_or_0 + MOVD R11, R12 + MOVD R10, R11 + MOVD R1, R10 + JMP sequenceDecs_decode_56_amd64_after_adjust + +sequenceDecs_decode_56_amd64_adjust_offsetB_1_or_0: + MOVD (R9), R16 + CMP $0x00000000, R16 + BNE sequenceDecs_decode_56_amd64_adjust_offset_maybezero + ADD $1, R1, R1 + JMP sequenceDecs_decode_56_amd64_adjust_offset_nonzero + +sequenceDecs_decode_56_amd64_adjust_offset_maybezero: + TST R1, R1 + BNE sequenceDecs_decode_56_amd64_adjust_offset_nonzero + MOVD R10, R1 + JMP sequenceDecs_decode_56_amd64_after_adjust + +sequenceDecs_decode_56_amd64_adjust_offset_nonzero: + CMP $0x01, R1 + BLO sequenceDecs_decode_56_amd64_adjust_zero + BEQ sequenceDecs_decode_56_amd64_adjust_one + CMP $0x02, R1 + BHI sequenceDecs_decode_56_amd64_adjust_three + JMP sequenceDecs_decode_56_amd64_adjust_two + +sequenceDecs_decode_56_amd64_adjust_zero: + MOVD R10, R0 + JMP sequenceDecs_decode_56_amd64_adjust_test_temp_valid + +sequenceDecs_decode_56_amd64_adjust_one: + MOVD R11, R0 + JMP sequenceDecs_decode_56_amd64_adjust_test_temp_valid + +sequenceDecs_decode_56_amd64_adjust_two: + MOVD R12, R0 + JMP sequenceDecs_decode_56_amd64_adjust_test_temp_valid + +sequenceDecs_decode_56_amd64_adjust_three: + SUB $1, R10, R0 + +sequenceDecs_decode_56_amd64_adjust_test_temp_valid: + TST R0, R0 + BNE sequenceDecs_decode_56_amd64_adjust_temp_valid + MOVD $0x00000001, R0 + +sequenceDecs_decode_56_amd64_adjust_temp_valid: + CMP $0x01, R1 + CSEL NE, R11, R12, R12 + MOVD R10, R11 + MOVD R0, R10 + MOVD R0, R1 + +sequenceDecs_decode_56_amd64_after_adjust: + MOVD R1, 16(R9) + + // Check values + MOVD 8(R9), R0 + MOVD (R9), R13 + ADD R13, R0, R14 + MOVD s+0(FP), R4 + MOVD 256(R4), R16 + ADD R14, R16, R16 + MOVD R16, 256(R4) + MOVD ctx+16(FP), R14 + MOVD 128(R14), R16 + SUBS R13, R16, R16 + MOVD R16, 128(R14) + BMI error_not_enough_literals + CMP $0x00020002, R0 + BHI sequenceDecs_decode_56_amd64_error_match_len_too_big + TST R1, R1 + BNE sequenceDecs_decode_56_amd64_match_len_ofs_ok + TST R0, R0 + BNE sequenceDecs_decode_56_amd64_error_match_len_ofs_mismatch + +sequenceDecs_decode_56_amd64_match_len_ofs_ok: + ADD $0x18, R9, R9 + MOVD ctx+16(FP), R0 + MOVD 96(R0), R16 + SUBS $1, R16, R16 + MOVD R16, 96(R0) + BPL sequenceDecs_decode_56_amd64_main_loop + MOVD s+0(FP), R0 + MOVD R10, 144(R0) + MOVD R11, 152(R0) + MOVD R12, 160(R0) + MOVD br+8(FP), R0 + MOVD R2, 24(R0) + MOVB R3, 40(R0) + MOVD R5, 32(R0) + + // Return success + MOVD $0x00000000, R16 + MOVD R16, ret+24(FP) + RET + + // Return with match length error +sequenceDecs_decode_56_amd64_error_match_len_ofs_mismatch: + MOVD $0x00000001, R16 + MOVD R16, ret+24(FP) + RET + + // Return with match too long error +sequenceDecs_decode_56_amd64_error_match_len_too_big: + MOVD $0x00000002, R16 + MOVD R16, ret+24(FP) + RET + + // Return with match offset too long error + MOVD $0x00000003, R16 + MOVD R16, ret+24(FP) + RET + + // Return with not enough literals error +error_not_enough_literals: + MOVD $0x00000004, R16 + MOVD R16, ret+24(FP) + RET + + // Return with overread error +error_overread: + MOVD $0x00000006, R16 + MOVD R16, ret+24(FP) + RET + +// skipped sequenceDecs_decode_bmi2 (generic twin preferred on arm64) + +// skipped sequenceDecs_decode_56_bmi2 (generic twin preferred on arm64) + +// func sequenceDecs_executeSimple_amd64(ctx *executeAsmContext) bool +// Requires: SSE +TEXT ·sequenceDecs_executeSimple_arm64(SB), $8-9 + MOVD ctx+0(FP), R9 + MOVD 8(R9), R1 + TST R1, R1 + BEQ empty_seqs + MOVD (R9), R0 + MOVD 24(R9), R2 + MOVD 32(R9), R3 + MOVD 80(R9), R5 + MOVD 104(R9), R6 + MOVD 120(R9), R7 + MOVD 56(R9), R8 + MOVD 64(R9), R9 + ADD R9, R8, R8 + + // seqsBase += 24 * seqIndex + ADD R2<<1, R2, R10 + LSL $0x03, R10, R10 + ADD R10, R0, R0 + + // outBase += outPosition + ADD R6, R3, R3 + +main_loop: + MOVD (R0), R10 + MOVD 16(R0), R11 + MOVD 8(R0), R12 + + // Copy literals + TST R10, R10 + BEQ check_offset + MOVD $0, R13 + +copy_1: + ADD R13, R5, R15 + VLD1 (R15), [V0.B16] + ADD R13, R3, R15 + VST1 [V0.B16], (R15) + ADD $0x10, R13, R13 + CMP R10, R13 + BLO copy_1 + ADD R10, R5, R5 + ADD R10, R3, R3 + ADD R10, R6, R6 + + // Malformed input if seq.mo > t+len(hist) || seq.mo > s.windowSize) +check_offset: + ADD R9, R6, R10 + CMP R10, R11 + BGT error_match_off_too_big + CMP R7, R11 + BGT error_match_off_too_big + + // Copy match from history + MOVD R11, R10 + SUBS R6, R10, R10 + BLS copy_match + MOVD R8, R13 + SUB R10, R13, R13 + CMP R10, R12 + BGT copy_all_from_history + MOVD R12, R10 + SUBS $0x10, R10, R10 + BLO copy_4_small + +copy_4_loop: + VLD1 (R13), [V0.B16] + VST1 [V0.B16], (R3) + ADD $0x10, R13, R13 + ADD $0x10, R3, R3 + SUBS $0x10, R10, R10 + BHS copy_4_loop + ADD R10, R13, R13 + ADD $16, R13, R13 + ADD R10, R3, R3 + ADD $16, R3, R3 + ADD $-16, R13, R15 + VLD1 (R15), [V0.B16] + ADD $-16, R3, R15 + VST1 [V0.B16], (R15) + JMP copy_4_end + +copy_4_small: + CMP $0x03, R12 + BEQ copy_4_move_3 + CMP $0x08, R12 + BLO copy_4_move_4through7 + JMP copy_4_move_8through16 + +copy_4_move_3: + MOVH (R13), R10 + MOVB 2(R13), R11 + MOVH R10, (R3) + MOVB R11, 2(R3) + ADD R12, R13, R13 + ADD R12, R3, R3 + JMP copy_4_end + +copy_4_move_4through7: + MOVWU (R13), R10 + ADD R12, R13, R15 + MOVWU -4(R15), R11 + MOVW R10, (R3) + ADD R12, R3, R15 + MOVW R11, -4(R15) + ADD R12, R13, R13 + ADD R12, R3, R3 + JMP copy_4_end + +copy_4_move_8through16: + MOVD (R13), R10 + ADD R12, R13, R15 + MOVD -8(R15), R11 + MOVD R10, (R3) + ADD R12, R3, R15 + MOVD R11, -8(R15) + ADD R12, R13, R13 + ADD R12, R3, R3 + +copy_4_end: + ADD R12, R6, R6 + ADD $0x18, R0, R0 + ADD $1, R2, R2 + CMP R1, R2 + BLO main_loop + JMP loop_finished + +copy_all_from_history: + MOVD R10, R14 + SUBS $0x10, R14, R14 + BLO copy_5_small + +copy_5_loop: + VLD1 (R13), [V0.B16] + VST1 [V0.B16], (R3) + ADD $0x10, R13, R13 + ADD $0x10, R3, R3 + SUBS $0x10, R14, R14 + BHS copy_5_loop + ADD R14, R13, R13 + ADD $16, R13, R13 + ADD R14, R3, R3 + ADD $16, R3, R3 + ADD $-16, R13, R15 + VLD1 (R15), [V0.B16] + ADD $-16, R3, R15 + VST1 [V0.B16], (R15) + JMP copy_5_end + +copy_5_small: + CMP $0x03, R10 + BEQ copy_5_move_3 + BLO copy_5_move_1or2 + CMP $0x08, R10 + BLO copy_5_move_4through7 + JMP copy_5_move_8through16 + +copy_5_move_1or2: + MOVB (R13), R14 + ADD R10, R13, R15 + MOVB -1(R15), R4 + MOVB R14, (R3) + ADD R10, R3, R15 + MOVB R4, -1(R15) + ADD R10, R13, R13 + ADD R10, R3, R3 + JMP copy_5_end + +copy_5_move_3: + MOVH (R13), R14 + MOVB 2(R13), R4 + MOVH R14, (R3) + MOVB R4, 2(R3) + ADD R10, R13, R13 + ADD R10, R3, R3 + JMP copy_5_end + +copy_5_move_4through7: + MOVWU (R13), R14 + ADD R10, R13, R15 + MOVWU -4(R15), R4 + MOVW R14, (R3) + ADD R10, R3, R15 + MOVW R4, -4(R15) + ADD R10, R13, R13 + ADD R10, R3, R3 + JMP copy_5_end + +copy_5_move_8through16: + MOVD (R13), R14 + ADD R10, R13, R15 + MOVD -8(R15), R4 + MOVD R14, (R3) + ADD R10, R3, R15 + MOVD R4, -8(R15) + ADD R10, R13, R13 + ADD R10, R3, R3 + +copy_5_end: + ADD R10, R6, R6 + SUB R10, R12, R12 + + // Copy match from the current buffer +copy_match: + MOVD R3, R10 + SUB R11, R10, R10 + + // ml <= mo + CMP R11, R12 + BHI copy_overlapping_match + + // Copy non-overlapping match + ADD R12, R6, R6 + MOVD R3, R11 + ADD R12, R3, R3 + +copy_2: + VLD1 (R10), [V0.B16] + VST1 [V0.B16], (R11) + ADD $0x10, R10, R10 + ADD $0x10, R11, R11 + SUBS $0x10, R12, R12 + BHI copy_2 + JMP handle_loop + + // Copy overlapping match +copy_overlapping_match: + ADD R12, R6, R6 + +copy_slow_3: + MOVB (R10), R11 + MOVB R11, (R3) + ADD $1, R10, R10 + ADD $1, R3, R3 + SUBS $1, R12, R12 + BNE copy_slow_3 + +handle_loop: + ADD $0x18, R0, R0 + ADD $1, R2, R2 + CMP R1, R2 + BLO main_loop + +loop_finished: + // Return value + MOVD $0x01, R16 + MOVB R16, ret+8(FP) + + // Update the context + MOVD ctx+0(FP), R0 + MOVD R2, 24(R0) + MOVD R6, 104(R0) + MOVD 80(R0), R16 + SUB R16, R5, R5 + MOVD R5, 112(R0) + RET + +error_match_off_too_big: + // Return value + MOVD $0x00, R16 + MOVB R16, ret+8(FP) + + // Update the context + MOVD ctx+0(FP), R0 + MOVD R2, 24(R0) + MOVD R6, 104(R0) + MOVD 80(R0), R16 + SUB R16, R5, R5 + MOVD R5, 112(R0) + RET + +empty_seqs: + // Return value + MOVD $0x01, R16 + MOVB R16, ret+8(FP) + RET + +// func sequenceDecs_executeSimple_safe_amd64(ctx *executeAsmContext) bool +// Requires: SSE +TEXT ·sequenceDecs_executeSimple_safe_arm64(SB), $8-9 + MOVD ctx+0(FP), R9 + MOVD 8(R9), R1 + TST R1, R1 + BEQ empty_seqs + MOVD (R9), R0 + MOVD 24(R9), R2 + MOVD 32(R9), R3 + MOVD 80(R9), R5 + MOVD 104(R9), R6 + MOVD 120(R9), R7 + MOVD 56(R9), R8 + MOVD 64(R9), R9 + ADD R9, R8, R8 + + // seqsBase += 24 * seqIndex + ADD R2<<1, R2, R10 + LSL $0x03, R10, R10 + ADD R10, R0, R0 + + // outBase += outPosition + ADD R6, R3, R3 + +main_loop: + MOVD (R0), R10 + MOVD 16(R0), R11 + MOVD 8(R0), R12 + + // Copy literals + TST R10, R10 + BEQ check_offset + MOVD R10, R13 + SUBS $0x10, R13, R13 + BLO copy_1_small + +copy_1_loop: + VLD1 (R5), [V0.B16] + VST1 [V0.B16], (R3) + ADD $0x10, R5, R5 + ADD $0x10, R3, R3 + SUBS $0x10, R13, R13 + BHS copy_1_loop + ADD R13, R5, R5 + ADD $16, R5, R5 + ADD R13, R3, R3 + ADD $16, R3, R3 + ADD $-16, R5, R15 + VLD1 (R15), [V0.B16] + ADD $-16, R3, R15 + VST1 [V0.B16], (R15) + JMP copy_1_end + +copy_1_small: + CMP $0x03, R10 + BEQ copy_1_move_3 + BLO copy_1_move_1or2 + CMP $0x08, R10 + BLO copy_1_move_4through7 + JMP copy_1_move_8through16 + +copy_1_move_1or2: + MOVB (R5), R13 + ADD R10, R5, R15 + MOVB -1(R15), R14 + MOVB R13, (R3) + ADD R10, R3, R15 + MOVB R14, -1(R15) + ADD R10, R5, R5 + ADD R10, R3, R3 + JMP copy_1_end + +copy_1_move_3: + MOVH (R5), R13 + MOVB 2(R5), R14 + MOVH R13, (R3) + MOVB R14, 2(R3) + ADD R10, R5, R5 + ADD R10, R3, R3 + JMP copy_1_end + +copy_1_move_4through7: + MOVWU (R5), R13 + ADD R10, R5, R15 + MOVWU -4(R15), R14 + MOVW R13, (R3) + ADD R10, R3, R15 + MOVW R14, -4(R15) + ADD R10, R5, R5 + ADD R10, R3, R3 + JMP copy_1_end + +copy_1_move_8through16: + MOVD (R5), R13 + ADD R10, R5, R15 + MOVD -8(R15), R14 + MOVD R13, (R3) + ADD R10, R3, R15 + MOVD R14, -8(R15) + ADD R10, R5, R5 + ADD R10, R3, R3 + +copy_1_end: + ADD R10, R6, R6 + + // Malformed input if seq.mo > t+len(hist) || seq.mo > s.windowSize) +check_offset: + ADD R9, R6, R10 + CMP R10, R11 + BGT error_match_off_too_big + CMP R7, R11 + BGT error_match_off_too_big + + // Copy match from history + MOVD R11, R10 + SUBS R6, R10, R10 + BLS copy_match + MOVD R8, R13 + SUB R10, R13, R13 + CMP R10, R12 + BGT copy_all_from_history + MOVD R12, R10 + SUBS $0x10, R10, R10 + BLO copy_4_small + +copy_4_loop: + VLD1 (R13), [V0.B16] + VST1 [V0.B16], (R3) + ADD $0x10, R13, R13 + ADD $0x10, R3, R3 + SUBS $0x10, R10, R10 + BHS copy_4_loop + ADD R10, R13, R13 + ADD $16, R13, R13 + ADD R10, R3, R3 + ADD $16, R3, R3 + ADD $-16, R13, R15 + VLD1 (R15), [V0.B16] + ADD $-16, R3, R15 + VST1 [V0.B16], (R15) + JMP copy_4_end + +copy_4_small: + CMP $0x03, R12 + BEQ copy_4_move_3 + CMP $0x08, R12 + BLO copy_4_move_4through7 + JMP copy_4_move_8through16 + +copy_4_move_3: + MOVH (R13), R10 + MOVB 2(R13), R11 + MOVH R10, (R3) + MOVB R11, 2(R3) + ADD R12, R13, R13 + ADD R12, R3, R3 + JMP copy_4_end + +copy_4_move_4through7: + MOVWU (R13), R10 + ADD R12, R13, R15 + MOVWU -4(R15), R11 + MOVW R10, (R3) + ADD R12, R3, R15 + MOVW R11, -4(R15) + ADD R12, R13, R13 + ADD R12, R3, R3 + JMP copy_4_end + +copy_4_move_8through16: + MOVD (R13), R10 + ADD R12, R13, R15 + MOVD -8(R15), R11 + MOVD R10, (R3) + ADD R12, R3, R15 + MOVD R11, -8(R15) + ADD R12, R13, R13 + ADD R12, R3, R3 + +copy_4_end: + ADD R12, R6, R6 + ADD $0x18, R0, R0 + ADD $1, R2, R2 + CMP R1, R2 + BLO main_loop + JMP loop_finished + +copy_all_from_history: + MOVD R10, R14 + SUBS $0x10, R14, R14 + BLO copy_5_small + +copy_5_loop: + VLD1 (R13), [V0.B16] + VST1 [V0.B16], (R3) + ADD $0x10, R13, R13 + ADD $0x10, R3, R3 + SUBS $0x10, R14, R14 + BHS copy_5_loop + ADD R14, R13, R13 + ADD $16, R13, R13 + ADD R14, R3, R3 + ADD $16, R3, R3 + ADD $-16, R13, R15 + VLD1 (R15), [V0.B16] + ADD $-16, R3, R15 + VST1 [V0.B16], (R15) + JMP copy_5_end + +copy_5_small: + CMP $0x03, R10 + BEQ copy_5_move_3 + BLO copy_5_move_1or2 + CMP $0x08, R10 + BLO copy_5_move_4through7 + JMP copy_5_move_8through16 + +copy_5_move_1or2: + MOVB (R13), R14 + ADD R10, R13, R15 + MOVB -1(R15), R4 + MOVB R14, (R3) + ADD R10, R3, R15 + MOVB R4, -1(R15) + ADD R10, R13, R13 + ADD R10, R3, R3 + JMP copy_5_end + +copy_5_move_3: + MOVH (R13), R14 + MOVB 2(R13), R4 + MOVH R14, (R3) + MOVB R4, 2(R3) + ADD R10, R13, R13 + ADD R10, R3, R3 + JMP copy_5_end + +copy_5_move_4through7: + MOVWU (R13), R14 + ADD R10, R13, R15 + MOVWU -4(R15), R4 + MOVW R14, (R3) + ADD R10, R3, R15 + MOVW R4, -4(R15) + ADD R10, R13, R13 + ADD R10, R3, R3 + JMP copy_5_end + +copy_5_move_8through16: + MOVD (R13), R14 + ADD R10, R13, R15 + MOVD -8(R15), R4 + MOVD R14, (R3) + ADD R10, R3, R15 + MOVD R4, -8(R15) + ADD R10, R13, R13 + ADD R10, R3, R3 + +copy_5_end: + ADD R10, R6, R6 + SUB R10, R12, R12 + + // Copy match from the current buffer +copy_match: + MOVD R3, R10 + SUB R11, R10, R10 + + // ml <= mo + CMP R11, R12 + BHI copy_overlapping_match + + // Copy non-overlapping match + ADD R12, R6, R6 + MOVD R12, R11 + SUBS $0x10, R11, R11 + BLO copy_2_small + +copy_2_loop: + VLD1 (R10), [V0.B16] + VST1 [V0.B16], (R3) + ADD $0x10, R10, R10 + ADD $0x10, R3, R3 + SUBS $0x10, R11, R11 + BHS copy_2_loop + ADD R11, R10, R10 + ADD $16, R10, R10 + ADD R11, R3, R3 + ADD $16, R3, R3 + ADD $-16, R10, R15 + VLD1 (R15), [V0.B16] + ADD $-16, R3, R15 + VST1 [V0.B16], (R15) + JMP copy_2_end + +copy_2_small: + CMP $0x03, R12 + BEQ copy_2_move_3 + BLO copy_2_move_1or2 + CMP $0x08, R12 + BLO copy_2_move_4through7 + JMP copy_2_move_8through16 + +copy_2_move_1or2: + MOVB (R10), R11 + ADD R12, R10, R15 + MOVB -1(R15), R13 + MOVB R11, (R3) + ADD R12, R3, R15 + MOVB R13, -1(R15) + ADD R12, R10, R10 + ADD R12, R3, R3 + JMP copy_2_end + +copy_2_move_3: + MOVH (R10), R11 + MOVB 2(R10), R13 + MOVH R11, (R3) + MOVB R13, 2(R3) + ADD R12, R10, R10 + ADD R12, R3, R3 + JMP copy_2_end + +copy_2_move_4through7: + MOVWU (R10), R11 + ADD R12, R10, R15 + MOVWU -4(R15), R13 + MOVW R11, (R3) + ADD R12, R3, R15 + MOVW R13, -4(R15) + ADD R12, R10, R10 + ADD R12, R3, R3 + JMP copy_2_end + +copy_2_move_8through16: + MOVD (R10), R11 + ADD R12, R10, R15 + MOVD -8(R15), R13 + MOVD R11, (R3) + ADD R12, R3, R15 + MOVD R13, -8(R15) + ADD R12, R10, R10 + ADD R12, R3, R3 + +copy_2_end: + JMP handle_loop + + // Copy overlapping match +copy_overlapping_match: + ADD R12, R6, R6 + +copy_slow_3: + MOVB (R10), R11 + MOVB R11, (R3) + ADD $1, R10, R10 + ADD $1, R3, R3 + SUBS $1, R12, R12 + BNE copy_slow_3 + +handle_loop: + ADD $0x18, R0, R0 + ADD $1, R2, R2 + CMP R1, R2 + BLO main_loop + +loop_finished: + // Return value + MOVD $0x01, R16 + MOVB R16, ret+8(FP) + + // Update the context + MOVD ctx+0(FP), R0 + MOVD R2, 24(R0) + MOVD R6, 104(R0) + MOVD 80(R0), R16 + SUB R16, R5, R5 + MOVD R5, 112(R0) + RET + +error_match_off_too_big: + // Return value + MOVD $0x00, R16 + MOVB R16, ret+8(FP) + + // Update the context + MOVD ctx+0(FP), R0 + MOVD R2, 24(R0) + MOVD R6, 104(R0) + MOVD 80(R0), R16 + SUB R16, R5, R5 + MOVD R5, 112(R0) + RET + +empty_seqs: + // Return value + MOVD $0x01, R16 + MOVB R16, ret+8(FP) + RET + +// func sequenceDecs_decodeSync_amd64(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int +// Requires: CMOV, SSE +TEXT ·sequenceDecs_decodeSync_arm64(SB), $64-32 + MOVD br+8(FP), R1 + MOVD 24(R1), R2 + MOVBU 40(R1), R3 + MOVD (R1), R0 + MOVD 32(R1), R5 + ADD R5, R0, R0 + MOVD R0, (RSP) + MOVD ctx+16(FP), R0 + MOVD 72(R0), R6 + MOVD 80(R0), R7 + MOVD 88(R0), R8 + MOVD $0, R1 + MOVD R1, 8(RSP) + MOVD R1, 16(RSP) + MOVD R1, 24(RSP) + MOVD 112(R0), R9 + MOVD 128(R0), R1 + MOVD R1, 32(RSP) + MOVD 144(R0), R10 + MOVD 136(R0), R11 + MOVD 200(R0), R1 + MOVD R1, 56(RSP) + MOVD 176(R0), R1 + MOVD R1, 48(RSP) + MOVD 184(R0), R0 + MOVD R0, 40(RSP) + MOVD 40(RSP), R0 + MOVD 48(RSP), R16 + ADD R0, R16, R16 + MOVD R16, 48(RSP) + + // Calculate pointer to s.out[cap(s.out)] (a past-end pointer) + MOVD 32(RSP), R16 + ADD R9, R16, R16 + MOVD R16, 32(RSP) + + // outBase += outPosition + ADD R11, R9, R9 + +sequenceDecs_decodeSync_amd64_main_loop: + MOVD (RSP), R12 + + // Fill bitreader to have enough for the offset and match length. + CMP $0x08, R5 + BLT sequenceDecs_decodeSync_amd64_fill_byte_by_byte + MOVD R3, R0 + LSR $0x03, R0, R0 + SUB R0, R12, R12 + MOVD (R12), R2 + SUB R0, R5, R5 + AND $0x07, R3, R3 + JMP sequenceDecs_decodeSync_amd64_fill_end + +sequenceDecs_decodeSync_amd64_fill_byte_by_byte: + CMP $0x00, R5 + BLE sequenceDecs_decodeSync_amd64_fill_check_overread + CMP $0x07, R3 + BLE sequenceDecs_decodeSync_amd64_fill_end + LSL $0x08, R2, R2 + SUB $0x01, R12, R12 + SUB $0x01, R5, R5 + SUB $0x08, R3, R3 + MOVBU (R12), R0 + ORR R0, R2, R2 + JMP sequenceDecs_decodeSync_amd64_fill_byte_by_byte + +sequenceDecs_decodeSync_amd64_fill_check_overread: + CMP $0x40, R3 + BHI error_overread + +sequenceDecs_decodeSync_amd64_fill_end: + // Update offset + MOVD R8, R0 + MOVD R3, R1 + MOVD R2, R13 + LSL R1, R13, R13 + UBFX $8, R0, $8, R1 + LSR $0x20, R0, R0 + TST R1, R1 + BEQ sequenceDecs_decodeSync_amd64_of_update_zero + ADD R1, R3, R3 + CMP $0x40, R3 + BHI sequenceDecs_decodeSync_amd64_of_update_zero + CMP $0x40, R1 + BHS sequenceDecs_decodeSync_amd64_of_update_zero + NEG R1, R1 + LSR R1, R13, R13 + ADD R13, R0, R0 + +sequenceDecs_decodeSync_amd64_of_update_zero: + MOVD R0, 8(RSP) + + // Update match length + MOVD R7, R0 + MOVD R3, R1 + MOVD R2, R13 + LSL R1, R13, R13 + UBFX $8, R0, $8, R1 + LSR $0x20, R0, R0 + TST R1, R1 + BEQ sequenceDecs_decodeSync_amd64_ml_update_zero + ADD R1, R3, R3 + CMP $0x40, R3 + BHI sequenceDecs_decodeSync_amd64_ml_update_zero + CMP $0x40, R1 + BHS sequenceDecs_decodeSync_amd64_ml_update_zero + NEG R1, R1 + LSR R1, R13, R13 + ADD R13, R0, R0 + +sequenceDecs_decodeSync_amd64_ml_update_zero: + MOVD R0, 16(RSP) + + // Fill bitreader to have enough for the remaining + CMP $0x08, R5 + BLT sequenceDecs_decodeSync_amd64_fill_2_byte_by_byte + MOVD R3, R0 + LSR $0x03, R0, R0 + SUB R0, R12, R12 + MOVD (R12), R2 + SUB R0, R5, R5 + AND $0x07, R3, R3 + JMP sequenceDecs_decodeSync_amd64_fill_2_end + +sequenceDecs_decodeSync_amd64_fill_2_byte_by_byte: + CMP $0x00, R5 + BLE sequenceDecs_decodeSync_amd64_fill_2_check_overread + CMP $0x07, R3 + BLE sequenceDecs_decodeSync_amd64_fill_2_end + LSL $0x08, R2, R2 + SUB $0x01, R12, R12 + SUB $0x01, R5, R5 + SUB $0x08, R3, R3 + MOVBU (R12), R0 + ORR R0, R2, R2 + JMP sequenceDecs_decodeSync_amd64_fill_2_byte_by_byte + +sequenceDecs_decodeSync_amd64_fill_2_check_overread: + CMP $0x40, R3 + BHI error_overread + +sequenceDecs_decodeSync_amd64_fill_2_end: + // Update literal length + MOVD R6, R0 + MOVD R3, R1 + MOVD R2, R13 + LSL R1, R13, R13 + UBFX $8, R0, $8, R1 + LSR $0x20, R0, R0 + TST R1, R1 + BEQ sequenceDecs_decodeSync_amd64_ll_update_zero + ADD R1, R3, R3 + CMP $0x40, R3 + BHI sequenceDecs_decodeSync_amd64_ll_update_zero + CMP $0x40, R1 + BHS sequenceDecs_decodeSync_amd64_ll_update_zero + NEG R1, R1 + LSR R1, R13, R13 + ADD R13, R0, R0 + +sequenceDecs_decodeSync_amd64_ll_update_zero: + MOVD R0, 24(RSP) + + // Fill bitreader for state updates + MOVD R12, (RSP) + MOVD R8, R0 + LSR $0x08, R0, R0 + MOVBU R0, R0 + MOVD ctx+16(FP), R1 + MOVD 96(R1), R16 + CMP $0x00, R16 + BEQ sequenceDecs_decodeSync_amd64_skip_update + + // Update Literal Length State + MOVBU R6, R12 + LSRW $0x10, R6, R6 + ADD R12, R3, R1 + MOVD R2, R13 + MOVD R1, R3 + NEG R1, R16 + ROR R16, R13, R13 + MOVD $0x00000001, R14 + MOVB R12, R1 + LSLW R1, R14, R14 + SUBW $1, R14, R14 + AND R14, R13, R13 + ADD R13, R6, R6 + + // Load ctx.llTable + MOVD ctx+16(FP), R1 + MOVD (R1), R1 + ADD R6<<3, R1, R15 + MOVD (R15), R6 + + // Update Match Length State + MOVBU R7, R12 + LSRW $0x10, R7, R7 + ADD R12, R3, R1 + MOVD R2, R13 + MOVD R1, R3 + NEG R1, R16 + ROR R16, R13, R13 + MOVD $0x00000001, R14 + MOVB R12, R1 + LSLW R1, R14, R14 + SUBW $1, R14, R14 + AND R14, R13, R13 + ADD R13, R7, R7 + + // Load ctx.mlTable + MOVD ctx+16(FP), R1 + MOVD 24(R1), R1 + ADD R7<<3, R1, R15 + MOVD (R15), R7 + + // Update Offset State + MOVBU R8, R12 + LSRW $0x10, R8, R8 + ADD R12, R3, R1 + MOVD R2, R13 + MOVD R1, R3 + NEG R1, R16 + ROR R16, R13, R13 + MOVD $0x00000001, R14 + MOVB R12, R1 + LSLW R1, R14, R14 + SUBW $1, R14, R14 + AND R14, R13, R13 + ADD R13, R8, R8 + + // Load ctx.ofTable + MOVD ctx+16(FP), R1 + MOVD 48(R1), R1 + ADD R8<<3, R1, R15 + MOVD (R15), R8 + +sequenceDecs_decodeSync_amd64_skip_update: + // Adjust offset + MOVD s+0(FP), R1 + MOVD 8(RSP), R12 + CMP $0x01, R0 + BLS sequenceDecs_decodeSync_amd64_adjust_offsetB_1_or_0 + ADD $144, R1, R15 + VLD1 (R15), [V0.B16] + MOVD R12, 144(R1) + ADD $152, R1, R15 + VST1 [V0.B16], (R15) + JMP sequenceDecs_decodeSync_amd64_after_adjust + +sequenceDecs_decodeSync_amd64_adjust_offsetB_1_or_0: + MOVD 24(RSP), R16 + CMP $0x00000000, R16 + BNE sequenceDecs_decodeSync_amd64_adjust_offset_maybezero + ADD $1, R12, R12 + JMP sequenceDecs_decodeSync_amd64_adjust_offset_nonzero + +sequenceDecs_decodeSync_amd64_adjust_offset_maybezero: + TST R12, R12 + BNE sequenceDecs_decodeSync_amd64_adjust_offset_nonzero + MOVD 144(R1), R12 + JMP sequenceDecs_decodeSync_amd64_after_adjust + +sequenceDecs_decodeSync_amd64_adjust_offset_nonzero: + MOVD R12, R0 + MOVD $0, R13 + MOVD $-1, R14 + CMP $0x03, R12 + CSEL EQ, R13, R0, R0 + CSEL EQ, R14, R13, R13 + ADD R0<<3, R1, R15 + MOVD 144(R15), R16 + ADDS R16, R13, R13 + BNE sequenceDecs_decodeSync_amd64_adjust_temp_valid + MOVD $0x00000001, R13 + +sequenceDecs_decodeSync_amd64_adjust_temp_valid: + CMP $0x01, R12 + BEQ sequenceDecs_decodeSync_amd64_adjust_skip + MOVD 152(R1), R0 + MOVD R0, 160(R1) + +sequenceDecs_decodeSync_amd64_adjust_skip: + MOVD 144(R1), R0 + MOVD R0, 152(R1) + MOVD R13, 144(R1) + MOVD R13, R12 + +sequenceDecs_decodeSync_amd64_after_adjust: + MOVD R12, 8(RSP) + + // Check values + MOVD 16(RSP), R0 + MOVD 24(RSP), R1 + ADD R1, R0, R13 + MOVD s+0(FP), R14 + MOVD 256(R14), R16 + ADD R13, R16, R16 + MOVD R16, 256(R14) + MOVD ctx+16(FP), R13 + MOVD 104(R13), R16 + SUBS R1, R16, R16 + MOVD R16, 104(R13) + BMI error_not_enough_literals + CMP $0x00020002, R0 + BHI sequenceDecs_decodeSync_amd64_error_match_len_too_big + TST R12, R12 + BNE sequenceDecs_decodeSync_amd64_match_len_ofs_ok + TST R0, R0 + BNE sequenceDecs_decodeSync_amd64_error_match_len_ofs_mismatch + +sequenceDecs_decodeSync_amd64_match_len_ofs_ok: + MOVD 24(RSP), R0 + MOVD 8(RSP), R1 + MOVD 16(RSP), R12 + + // Check if we have enough space in s.out + ADD R12, R0, R13 + ADD R9, R13, R13 + MOVD 32(RSP), R16 + CMP R16, R13 + BHI error_not_enough_space + + // Copy literals + TST R0, R0 + BEQ check_offset + MOVD $0, R13 + +copy_1: + ADD R13, R10, R15 + VLD1 (R15), [V0.B16] + ADD R13, R9, R15 + VST1 [V0.B16], (R15) + ADD $0x10, R13, R13 + CMP R0, R13 + BLO copy_1 + ADD R0, R10, R10 + ADD R0, R9, R9 + ADD R0, R11, R11 + + // Malformed input if seq.mo > t+len(hist) || seq.mo > s.windowSize) +check_offset: + MOVD R11, R0 + MOVD 40(RSP), R16 + ADD R16, R0, R0 + CMP R0, R1 + BGT error_match_off_too_big + MOVD 56(RSP), R16 + CMP R16, R1 + BGT error_match_off_too_big + + // Copy match from history + MOVD R1, R0 + SUBS R11, R0, R0 + BLS copy_match + MOVD 48(RSP), R13 + SUB R0, R13, R13 + CMP R0, R12 + BGT copy_all_from_history + MOVD R12, R0 + SUBS $0x10, R0, R0 + BLO copy_4_small + +copy_4_loop: + VLD1 (R13), [V0.B16] + VST1 [V0.B16], (R9) + ADD $0x10, R13, R13 + ADD $0x10, R9, R9 + SUBS $0x10, R0, R0 + BHS copy_4_loop + ADD R0, R13, R13 + ADD $16, R13, R13 + ADD R0, R9, R9 + ADD $16, R9, R9 + ADD $-16, R13, R15 + VLD1 (R15), [V0.B16] + ADD $-16, R9, R15 + VST1 [V0.B16], (R15) + JMP copy_4_end + +copy_4_small: + CMP $0x03, R12 + BEQ copy_4_move_3 + CMP $0x08, R12 + BLO copy_4_move_4through7 + JMP copy_4_move_8through16 + +copy_4_move_3: + MOVH (R13), R0 + MOVB 2(R13), R1 + MOVH R0, (R9) + MOVB R1, 2(R9) + ADD R12, R13, R13 + ADD R12, R9, R9 + JMP copy_4_end + +copy_4_move_4through7: + MOVWU (R13), R0 + ADD R12, R13, R15 + MOVWU -4(R15), R1 + MOVW R0, (R9) + ADD R12, R9, R15 + MOVW R1, -4(R15) + ADD R12, R13, R13 + ADD R12, R9, R9 + JMP copy_4_end + +copy_4_move_8through16: + MOVD (R13), R0 + ADD R12, R13, R15 + MOVD -8(R15), R1 + MOVD R0, (R9) + ADD R12, R9, R15 + MOVD R1, -8(R15) + ADD R12, R13, R13 + ADD R12, R9, R9 + +copy_4_end: + ADD R12, R11, R11 + JMP handle_loop + JMP loop_finished + +copy_all_from_history: + MOVD R0, R14 + SUBS $0x10, R14, R14 + BLO copy_5_small + +copy_5_loop: + VLD1 (R13), [V0.B16] + VST1 [V0.B16], (R9) + ADD $0x10, R13, R13 + ADD $0x10, R9, R9 + SUBS $0x10, R14, R14 + BHS copy_5_loop + ADD R14, R13, R13 + ADD $16, R13, R13 + ADD R14, R9, R9 + ADD $16, R9, R9 + ADD $-16, R13, R15 + VLD1 (R15), [V0.B16] + ADD $-16, R9, R15 + VST1 [V0.B16], (R15) + JMP copy_5_end + +copy_5_small: + CMP $0x03, R0 + BEQ copy_5_move_3 + BLO copy_5_move_1or2 + CMP $0x08, R0 + BLO copy_5_move_4through7 + JMP copy_5_move_8through16 + +copy_5_move_1or2: + MOVB (R13), R14 + ADD R0, R13, R15 + MOVB -1(R15), R4 + MOVB R14, (R9) + ADD R0, R9, R15 + MOVB R4, -1(R15) + ADD R0, R13, R13 + ADD R0, R9, R9 + JMP copy_5_end + +copy_5_move_3: + MOVH (R13), R14 + MOVB 2(R13), R4 + MOVH R14, (R9) + MOVB R4, 2(R9) + ADD R0, R13, R13 + ADD R0, R9, R9 + JMP copy_5_end + +copy_5_move_4through7: + MOVWU (R13), R14 + ADD R0, R13, R15 + MOVWU -4(R15), R4 + MOVW R14, (R9) + ADD R0, R9, R15 + MOVW R4, -4(R15) + ADD R0, R13, R13 + ADD R0, R9, R9 + JMP copy_5_end + +copy_5_move_8through16: + MOVD (R13), R14 + ADD R0, R13, R15 + MOVD -8(R15), R4 + MOVD R14, (R9) + ADD R0, R9, R15 + MOVD R4, -8(R15) + ADD R0, R13, R13 + ADD R0, R9, R9 + +copy_5_end: + ADD R0, R11, R11 + SUB R0, R12, R12 + + // Copy match from the current buffer +copy_match: + MOVD R9, R0 + SUB R1, R0, R0 + + // ml <= mo + CMP R1, R12 + BHI copy_overlapping_match + + // Copy non-overlapping match + ADD R12, R11, R11 + MOVD R9, R1 + ADD R12, R9, R9 + +copy_2: + VLD1 (R0), [V0.B16] + VST1 [V0.B16], (R1) + ADD $0x10, R0, R0 + ADD $0x10, R1, R1 + SUBS $0x10, R12, R12 + BHI copy_2 + JMP handle_loop + + // Copy overlapping match +copy_overlapping_match: + ADD R12, R11, R11 + +copy_slow_3: + MOVB (R0), R1 + MOVB R1, (R9) + ADD $1, R0, R0 + ADD $1, R9, R9 + SUBS $1, R12, R12 + BNE copy_slow_3 + +handle_loop: + MOVD ctx+16(FP), R0 + MOVD 96(R0), R16 + SUBS $1, R16, R16 + MOVD R16, 96(R0) + BPL sequenceDecs_decodeSync_amd64_main_loop + +loop_finished: + MOVD br+8(FP), R0 + MOVD R2, 24(R0) + MOVB R3, 40(R0) + MOVD R5, 32(R0) + + // Update the context + MOVD ctx+16(FP), R0 + MOVD R11, 136(R0) + MOVD 144(R0), R1 + SUB R1, R10, R10 + MOVD R10, 168(R0) + + // Return success + MOVD $0x00000000, R16 + MOVD R16, ret+24(FP) + RET + + // Return with match length error +sequenceDecs_decodeSync_amd64_error_match_len_ofs_mismatch: + MOVD 16(RSP), R0 + MOVD ctx+16(FP), R1 + MOVD R0, 216(R1) + MOVD $0x00000001, R16 + MOVD R16, ret+24(FP) + RET + + // Return with match too long error +sequenceDecs_decodeSync_amd64_error_match_len_too_big: + MOVD ctx+16(FP), R0 + MOVD 16(RSP), R1 + MOVD R1, 216(R0) + MOVD $0x00000002, R16 + MOVD R16, ret+24(FP) + RET + + // Return with match offset too long error +error_match_off_too_big: + MOVD ctx+16(FP), R0 + MOVD 8(RSP), R1 + MOVD R1, 224(R0) + MOVD R11, 136(R0) + MOVD $0x00000003, R16 + MOVD R16, ret+24(FP) + RET + + // Return with not enough literals error +error_not_enough_literals: + MOVD ctx+16(FP), R0 + MOVD 24(RSP), R1 + MOVD R1, 208(R0) + MOVD $0x00000004, R16 + MOVD R16, ret+24(FP) + RET + + // Return with overread error +error_overread: + MOVD $0x00000006, R16 + MOVD R16, ret+24(FP) + RET + + // Return with not enough output space error +error_not_enough_space: + MOVD ctx+16(FP), R0 + MOVD 24(RSP), R1 + MOVD R1, 208(R0) + MOVD 16(RSP), R1 + MOVD R1, 216(R0) + MOVD R11, 136(R0) + MOVD $0x00000005, R16 + MOVD R16, ret+24(FP) + RET + +// skipped sequenceDecs_decodeSync_bmi2 (generic twin preferred on arm64) + +// func sequenceDecs_decodeSync_safe_amd64(s *sequenceDecs, br *bitReader, ctx *decodeSyncAsmContext) int +// Requires: CMOV, SSE +TEXT ·sequenceDecs_decodeSync_safe_arm64(SB), $64-32 + MOVD br+8(FP), R1 + MOVD 24(R1), R2 + MOVBU 40(R1), R3 + MOVD (R1), R0 + MOVD 32(R1), R5 + ADD R5, R0, R0 + MOVD R0, (RSP) + MOVD ctx+16(FP), R0 + MOVD 72(R0), R6 + MOVD 80(R0), R7 + MOVD 88(R0), R8 + MOVD $0, R1 + MOVD R1, 8(RSP) + MOVD R1, 16(RSP) + MOVD R1, 24(RSP) + MOVD 112(R0), R9 + MOVD 128(R0), R1 + MOVD R1, 32(RSP) + MOVD 144(R0), R10 + MOVD 136(R0), R11 + MOVD 200(R0), R1 + MOVD R1, 56(RSP) + MOVD 176(R0), R1 + MOVD R1, 48(RSP) + MOVD 184(R0), R0 + MOVD R0, 40(RSP) + MOVD 40(RSP), R0 + MOVD 48(RSP), R16 + ADD R0, R16, R16 + MOVD R16, 48(RSP) + + // Calculate pointer to s.out[cap(s.out)] (a past-end pointer) + MOVD 32(RSP), R16 + ADD R9, R16, R16 + MOVD R16, 32(RSP) + + // outBase += outPosition + ADD R11, R9, R9 + +sequenceDecs_decodeSync_safe_amd64_main_loop: + MOVD (RSP), R12 + + // Fill bitreader to have enough for the offset and match length. + CMP $0x08, R5 + BLT sequenceDecs_decodeSync_safe_amd64_fill_byte_by_byte + MOVD R3, R0 + LSR $0x03, R0, R0 + SUB R0, R12, R12 + MOVD (R12), R2 + SUB R0, R5, R5 + AND $0x07, R3, R3 + JMP sequenceDecs_decodeSync_safe_amd64_fill_end + +sequenceDecs_decodeSync_safe_amd64_fill_byte_by_byte: + CMP $0x00, R5 + BLE sequenceDecs_decodeSync_safe_amd64_fill_check_overread + CMP $0x07, R3 + BLE sequenceDecs_decodeSync_safe_amd64_fill_end + LSL $0x08, R2, R2 + SUB $0x01, R12, R12 + SUB $0x01, R5, R5 + SUB $0x08, R3, R3 + MOVBU (R12), R0 + ORR R0, R2, R2 + JMP sequenceDecs_decodeSync_safe_amd64_fill_byte_by_byte + +sequenceDecs_decodeSync_safe_amd64_fill_check_overread: + CMP $0x40, R3 + BHI error_overread + +sequenceDecs_decodeSync_safe_amd64_fill_end: + // Update offset + MOVD R8, R0 + MOVD R3, R1 + MOVD R2, R13 + LSL R1, R13, R13 + UBFX $8, R0, $8, R1 + LSR $0x20, R0, R0 + TST R1, R1 + BEQ sequenceDecs_decodeSync_safe_amd64_of_update_zero + ADD R1, R3, R3 + CMP $0x40, R3 + BHI sequenceDecs_decodeSync_safe_amd64_of_update_zero + CMP $0x40, R1 + BHS sequenceDecs_decodeSync_safe_amd64_of_update_zero + NEG R1, R1 + LSR R1, R13, R13 + ADD R13, R0, R0 + +sequenceDecs_decodeSync_safe_amd64_of_update_zero: + MOVD R0, 8(RSP) + + // Update match length + MOVD R7, R0 + MOVD R3, R1 + MOVD R2, R13 + LSL R1, R13, R13 + UBFX $8, R0, $8, R1 + LSR $0x20, R0, R0 + TST R1, R1 + BEQ sequenceDecs_decodeSync_safe_amd64_ml_update_zero + ADD R1, R3, R3 + CMP $0x40, R3 + BHI sequenceDecs_decodeSync_safe_amd64_ml_update_zero + CMP $0x40, R1 + BHS sequenceDecs_decodeSync_safe_amd64_ml_update_zero + NEG R1, R1 + LSR R1, R13, R13 + ADD R13, R0, R0 + +sequenceDecs_decodeSync_safe_amd64_ml_update_zero: + MOVD R0, 16(RSP) + + // Fill bitreader to have enough for the remaining + CMP $0x08, R5 + BLT sequenceDecs_decodeSync_safe_amd64_fill_2_byte_by_byte + MOVD R3, R0 + LSR $0x03, R0, R0 + SUB R0, R12, R12 + MOVD (R12), R2 + SUB R0, R5, R5 + AND $0x07, R3, R3 + JMP sequenceDecs_decodeSync_safe_amd64_fill_2_end + +sequenceDecs_decodeSync_safe_amd64_fill_2_byte_by_byte: + CMP $0x00, R5 + BLE sequenceDecs_decodeSync_safe_amd64_fill_2_check_overread + CMP $0x07, R3 + BLE sequenceDecs_decodeSync_safe_amd64_fill_2_end + LSL $0x08, R2, R2 + SUB $0x01, R12, R12 + SUB $0x01, R5, R5 + SUB $0x08, R3, R3 + MOVBU (R12), R0 + ORR R0, R2, R2 + JMP sequenceDecs_decodeSync_safe_amd64_fill_2_byte_by_byte + +sequenceDecs_decodeSync_safe_amd64_fill_2_check_overread: + CMP $0x40, R3 + BHI error_overread + +sequenceDecs_decodeSync_safe_amd64_fill_2_end: + // Update literal length + MOVD R6, R0 + MOVD R3, R1 + MOVD R2, R13 + LSL R1, R13, R13 + UBFX $8, R0, $8, R1 + LSR $0x20, R0, R0 + TST R1, R1 + BEQ sequenceDecs_decodeSync_safe_amd64_ll_update_zero + ADD R1, R3, R3 + CMP $0x40, R3 + BHI sequenceDecs_decodeSync_safe_amd64_ll_update_zero + CMP $0x40, R1 + BHS sequenceDecs_decodeSync_safe_amd64_ll_update_zero + NEG R1, R1 + LSR R1, R13, R13 + ADD R13, R0, R0 + +sequenceDecs_decodeSync_safe_amd64_ll_update_zero: + MOVD R0, 24(RSP) + + // Fill bitreader for state updates + MOVD R12, (RSP) + MOVD R8, R0 + LSR $0x08, R0, R0 + MOVBU R0, R0 + MOVD ctx+16(FP), R1 + MOVD 96(R1), R16 + CMP $0x00, R16 + BEQ sequenceDecs_decodeSync_safe_amd64_skip_update + + // Update Literal Length State + MOVBU R6, R12 + LSRW $0x10, R6, R6 + ADD R12, R3, R1 + MOVD R2, R13 + MOVD R1, R3 + NEG R1, R16 + ROR R16, R13, R13 + MOVD $0x00000001, R14 + MOVB R12, R1 + LSLW R1, R14, R14 + SUBW $1, R14, R14 + AND R14, R13, R13 + ADD R13, R6, R6 + + // Load ctx.llTable + MOVD ctx+16(FP), R1 + MOVD (R1), R1 + ADD R6<<3, R1, R15 + MOVD (R15), R6 + + // Update Match Length State + MOVBU R7, R12 + LSRW $0x10, R7, R7 + ADD R12, R3, R1 + MOVD R2, R13 + MOVD R1, R3 + NEG R1, R16 + ROR R16, R13, R13 + MOVD $0x00000001, R14 + MOVB R12, R1 + LSLW R1, R14, R14 + SUBW $1, R14, R14 + AND R14, R13, R13 + ADD R13, R7, R7 + + // Load ctx.mlTable + MOVD ctx+16(FP), R1 + MOVD 24(R1), R1 + ADD R7<<3, R1, R15 + MOVD (R15), R7 + + // Update Offset State + MOVBU R8, R12 + LSRW $0x10, R8, R8 + ADD R12, R3, R1 + MOVD R2, R13 + MOVD R1, R3 + NEG R1, R16 + ROR R16, R13, R13 + MOVD $0x00000001, R14 + MOVB R12, R1 + LSLW R1, R14, R14 + SUBW $1, R14, R14 + AND R14, R13, R13 + ADD R13, R8, R8 + + // Load ctx.ofTable + MOVD ctx+16(FP), R1 + MOVD 48(R1), R1 + ADD R8<<3, R1, R15 + MOVD (R15), R8 + +sequenceDecs_decodeSync_safe_amd64_skip_update: + // Adjust offset + MOVD s+0(FP), R1 + MOVD 8(RSP), R12 + CMP $0x01, R0 + BLS sequenceDecs_decodeSync_safe_amd64_adjust_offsetB_1_or_0 + ADD $144, R1, R15 + VLD1 (R15), [V0.B16] + MOVD R12, 144(R1) + ADD $152, R1, R15 + VST1 [V0.B16], (R15) + JMP sequenceDecs_decodeSync_safe_amd64_after_adjust + +sequenceDecs_decodeSync_safe_amd64_adjust_offsetB_1_or_0: + MOVD 24(RSP), R16 + CMP $0x00000000, R16 + BNE sequenceDecs_decodeSync_safe_amd64_adjust_offset_maybezero + ADD $1, R12, R12 + JMP sequenceDecs_decodeSync_safe_amd64_adjust_offset_nonzero + +sequenceDecs_decodeSync_safe_amd64_adjust_offset_maybezero: + TST R12, R12 + BNE sequenceDecs_decodeSync_safe_amd64_adjust_offset_nonzero + MOVD 144(R1), R12 + JMP sequenceDecs_decodeSync_safe_amd64_after_adjust + +sequenceDecs_decodeSync_safe_amd64_adjust_offset_nonzero: + MOVD R12, R0 + MOVD $0, R13 + MOVD $-1, R14 + CMP $0x03, R12 + CSEL EQ, R13, R0, R0 + CSEL EQ, R14, R13, R13 + ADD R0<<3, R1, R15 + MOVD 144(R15), R16 + ADDS R16, R13, R13 + BNE sequenceDecs_decodeSync_safe_amd64_adjust_temp_valid + MOVD $0x00000001, R13 + +sequenceDecs_decodeSync_safe_amd64_adjust_temp_valid: + CMP $0x01, R12 + BEQ sequenceDecs_decodeSync_safe_amd64_adjust_skip + MOVD 152(R1), R0 + MOVD R0, 160(R1) + +sequenceDecs_decodeSync_safe_amd64_adjust_skip: + MOVD 144(R1), R0 + MOVD R0, 152(R1) + MOVD R13, 144(R1) + MOVD R13, R12 + +sequenceDecs_decodeSync_safe_amd64_after_adjust: + MOVD R12, 8(RSP) + + // Check values + MOVD 16(RSP), R0 + MOVD 24(RSP), R1 + ADD R1, R0, R13 + MOVD s+0(FP), R14 + MOVD 256(R14), R16 + ADD R13, R16, R16 + MOVD R16, 256(R14) + MOVD ctx+16(FP), R13 + MOVD 104(R13), R16 + SUBS R1, R16, R16 + MOVD R16, 104(R13) + BMI error_not_enough_literals + CMP $0x00020002, R0 + BHI sequenceDecs_decodeSync_safe_amd64_error_match_len_too_big + TST R12, R12 + BNE sequenceDecs_decodeSync_safe_amd64_match_len_ofs_ok + TST R0, R0 + BNE sequenceDecs_decodeSync_safe_amd64_error_match_len_ofs_mismatch + +sequenceDecs_decodeSync_safe_amd64_match_len_ofs_ok: + MOVD 24(RSP), R0 + MOVD 8(RSP), R1 + MOVD 16(RSP), R12 + + // Check if we have enough space in s.out + ADD R12, R0, R13 + ADD R9, R13, R13 + MOVD 32(RSP), R16 + CMP R16, R13 + BHI error_not_enough_space + + // Copy literals + TST R0, R0 + BEQ check_offset + MOVD R0, R13 + SUBS $0x10, R13, R13 + BLO copy_1_small + +copy_1_loop: + VLD1 (R10), [V0.B16] + VST1 [V0.B16], (R9) + ADD $0x10, R10, R10 + ADD $0x10, R9, R9 + SUBS $0x10, R13, R13 + BHS copy_1_loop + ADD R13, R10, R10 + ADD $16, R10, R10 + ADD R13, R9, R9 + ADD $16, R9, R9 + ADD $-16, R10, R15 + VLD1 (R15), [V0.B16] + ADD $-16, R9, R15 + VST1 [V0.B16], (R15) + JMP copy_1_end + +copy_1_small: + CMP $0x03, R0 + BEQ copy_1_move_3 + BLO copy_1_move_1or2 + CMP $0x08, R0 + BLO copy_1_move_4through7 + JMP copy_1_move_8through16 + +copy_1_move_1or2: + MOVB (R10), R13 + ADD R0, R10, R15 + MOVB -1(R15), R14 + MOVB R13, (R9) + ADD R0, R9, R15 + MOVB R14, -1(R15) + ADD R0, R10, R10 + ADD R0, R9, R9 + JMP copy_1_end + +copy_1_move_3: + MOVH (R10), R13 + MOVB 2(R10), R14 + MOVH R13, (R9) + MOVB R14, 2(R9) + ADD R0, R10, R10 + ADD R0, R9, R9 + JMP copy_1_end + +copy_1_move_4through7: + MOVWU (R10), R13 + ADD R0, R10, R15 + MOVWU -4(R15), R14 + MOVW R13, (R9) + ADD R0, R9, R15 + MOVW R14, -4(R15) + ADD R0, R10, R10 + ADD R0, R9, R9 + JMP copy_1_end + +copy_1_move_8through16: + MOVD (R10), R13 + ADD R0, R10, R15 + MOVD -8(R15), R14 + MOVD R13, (R9) + ADD R0, R9, R15 + MOVD R14, -8(R15) + ADD R0, R10, R10 + ADD R0, R9, R9 + +copy_1_end: + ADD R0, R11, R11 + + // Malformed input if seq.mo > t+len(hist) || seq.mo > s.windowSize) +check_offset: + MOVD R11, R0 + MOVD 40(RSP), R16 + ADD R16, R0, R0 + CMP R0, R1 + BGT error_match_off_too_big + MOVD 56(RSP), R16 + CMP R16, R1 + BGT error_match_off_too_big + + // Copy match from history + MOVD R1, R0 + SUBS R11, R0, R0 + BLS copy_match + MOVD 48(RSP), R13 + SUB R0, R13, R13 + CMP R0, R12 + BGT copy_all_from_history + MOVD R12, R0 + SUBS $0x10, R0, R0 + BLO copy_4_small + +copy_4_loop: + VLD1 (R13), [V0.B16] + VST1 [V0.B16], (R9) + ADD $0x10, R13, R13 + ADD $0x10, R9, R9 + SUBS $0x10, R0, R0 + BHS copy_4_loop + ADD R0, R13, R13 + ADD $16, R13, R13 + ADD R0, R9, R9 + ADD $16, R9, R9 + ADD $-16, R13, R15 + VLD1 (R15), [V0.B16] + ADD $-16, R9, R15 + VST1 [V0.B16], (R15) + JMP copy_4_end + +copy_4_small: + CMP $0x03, R12 + BEQ copy_4_move_3 + CMP $0x08, R12 + BLO copy_4_move_4through7 + JMP copy_4_move_8through16 + +copy_4_move_3: + MOVH (R13), R0 + MOVB 2(R13), R1 + MOVH R0, (R9) + MOVB R1, 2(R9) + ADD R12, R13, R13 + ADD R12, R9, R9 + JMP copy_4_end + +copy_4_move_4through7: + MOVWU (R13), R0 + ADD R12, R13, R15 + MOVWU -4(R15), R1 + MOVW R0, (R9) + ADD R12, R9, R15 + MOVW R1, -4(R15) + ADD R12, R13, R13 + ADD R12, R9, R9 + JMP copy_4_end + +copy_4_move_8through16: + MOVD (R13), R0 + ADD R12, R13, R15 + MOVD -8(R15), R1 + MOVD R0, (R9) + ADD R12, R9, R15 + MOVD R1, -8(R15) + ADD R12, R13, R13 + ADD R12, R9, R9 + +copy_4_end: + ADD R12, R11, R11 + JMP handle_loop + JMP loop_finished + +copy_all_from_history: + MOVD R0, R14 + SUBS $0x10, R14, R14 + BLO copy_5_small + +copy_5_loop: + VLD1 (R13), [V0.B16] + VST1 [V0.B16], (R9) + ADD $0x10, R13, R13 + ADD $0x10, R9, R9 + SUBS $0x10, R14, R14 + BHS copy_5_loop + ADD R14, R13, R13 + ADD $16, R13, R13 + ADD R14, R9, R9 + ADD $16, R9, R9 + ADD $-16, R13, R15 + VLD1 (R15), [V0.B16] + ADD $-16, R9, R15 + VST1 [V0.B16], (R15) + JMP copy_5_end + +copy_5_small: + CMP $0x03, R0 + BEQ copy_5_move_3 + BLO copy_5_move_1or2 + CMP $0x08, R0 + BLO copy_5_move_4through7 + JMP copy_5_move_8through16 + +copy_5_move_1or2: + MOVB (R13), R14 + ADD R0, R13, R15 + MOVB -1(R15), R4 + MOVB R14, (R9) + ADD R0, R9, R15 + MOVB R4, -1(R15) + ADD R0, R13, R13 + ADD R0, R9, R9 + JMP copy_5_end + +copy_5_move_3: + MOVH (R13), R14 + MOVB 2(R13), R4 + MOVH R14, (R9) + MOVB R4, 2(R9) + ADD R0, R13, R13 + ADD R0, R9, R9 + JMP copy_5_end + +copy_5_move_4through7: + MOVWU (R13), R14 + ADD R0, R13, R15 + MOVWU -4(R15), R4 + MOVW R14, (R9) + ADD R0, R9, R15 + MOVW R4, -4(R15) + ADD R0, R13, R13 + ADD R0, R9, R9 + JMP copy_5_end + +copy_5_move_8through16: + MOVD (R13), R14 + ADD R0, R13, R15 + MOVD -8(R15), R4 + MOVD R14, (R9) + ADD R0, R9, R15 + MOVD R4, -8(R15) + ADD R0, R13, R13 + ADD R0, R9, R9 + +copy_5_end: + ADD R0, R11, R11 + SUB R0, R12, R12 + + // Copy match from the current buffer +copy_match: + MOVD R9, R0 + SUB R1, R0, R0 + + // ml <= mo + CMP R1, R12 + BHI copy_overlapping_match + + // Copy non-overlapping match + ADD R12, R11, R11 + MOVD R12, R1 + SUBS $0x10, R1, R1 + BLO copy_2_small + +copy_2_loop: + VLD1 (R0), [V0.B16] + VST1 [V0.B16], (R9) + ADD $0x10, R0, R0 + ADD $0x10, R9, R9 + SUBS $0x10, R1, R1 + BHS copy_2_loop + ADD R1, R0, R0 + ADD $16, R0, R0 + ADD R1, R9, R9 + ADD $16, R9, R9 + ADD $-16, R0, R15 + VLD1 (R15), [V0.B16] + ADD $-16, R9, R15 + VST1 [V0.B16], (R15) + JMP copy_2_end + +copy_2_small: + CMP $0x03, R12 + BEQ copy_2_move_3 + BLO copy_2_move_1or2 + CMP $0x08, R12 + BLO copy_2_move_4through7 + JMP copy_2_move_8through16 + +copy_2_move_1or2: + MOVB (R0), R1 + ADD R12, R0, R15 + MOVB -1(R15), R13 + MOVB R1, (R9) + ADD R12, R9, R15 + MOVB R13, -1(R15) + ADD R12, R0, R0 + ADD R12, R9, R9 + JMP copy_2_end + +copy_2_move_3: + MOVH (R0), R1 + MOVB 2(R0), R13 + MOVH R1, (R9) + MOVB R13, 2(R9) + ADD R12, R0, R0 + ADD R12, R9, R9 + JMP copy_2_end + +copy_2_move_4through7: + MOVWU (R0), R1 + ADD R12, R0, R15 + MOVWU -4(R15), R13 + MOVW R1, (R9) + ADD R12, R9, R15 + MOVW R13, -4(R15) + ADD R12, R0, R0 + ADD R12, R9, R9 + JMP copy_2_end + +copy_2_move_8through16: + MOVD (R0), R1 + ADD R12, R0, R15 + MOVD -8(R15), R13 + MOVD R1, (R9) + ADD R12, R9, R15 + MOVD R13, -8(R15) + ADD R12, R0, R0 + ADD R12, R9, R9 + +copy_2_end: + JMP handle_loop + + // Copy overlapping match +copy_overlapping_match: + ADD R12, R11, R11 + +copy_slow_3: + MOVB (R0), R1 + MOVB R1, (R9) + ADD $1, R0, R0 + ADD $1, R9, R9 + SUBS $1, R12, R12 + BNE copy_slow_3 + +handle_loop: + MOVD ctx+16(FP), R0 + MOVD 96(R0), R16 + SUBS $1, R16, R16 + MOVD R16, 96(R0) + BPL sequenceDecs_decodeSync_safe_amd64_main_loop + +loop_finished: + MOVD br+8(FP), R0 + MOVD R2, 24(R0) + MOVB R3, 40(R0) + MOVD R5, 32(R0) + + // Update the context + MOVD ctx+16(FP), R0 + MOVD R11, 136(R0) + MOVD 144(R0), R1 + SUB R1, R10, R10 + MOVD R10, 168(R0) + + // Return success + MOVD $0x00000000, R16 + MOVD R16, ret+24(FP) + RET + + // Return with match length error +sequenceDecs_decodeSync_safe_amd64_error_match_len_ofs_mismatch: + MOVD 16(RSP), R0 + MOVD ctx+16(FP), R1 + MOVD R0, 216(R1) + MOVD $0x00000001, R16 + MOVD R16, ret+24(FP) + RET + + // Return with match too long error +sequenceDecs_decodeSync_safe_amd64_error_match_len_too_big: + MOVD ctx+16(FP), R0 + MOVD 16(RSP), R1 + MOVD R1, 216(R0) + MOVD $0x00000002, R16 + MOVD R16, ret+24(FP) + RET + + // Return with match offset too long error +error_match_off_too_big: + MOVD ctx+16(FP), R0 + MOVD 8(RSP), R1 + MOVD R1, 224(R0) + MOVD R11, 136(R0) + MOVD $0x00000003, R16 + MOVD R16, ret+24(FP) + RET + + // Return with not enough literals error +error_not_enough_literals: + MOVD ctx+16(FP), R0 + MOVD 24(RSP), R1 + MOVD R1, 208(R0) + MOVD $0x00000004, R16 + MOVD R16, ret+24(FP) + RET + + // Return with overread error +error_overread: + MOVD $0x00000006, R16 + MOVD R16, ret+24(FP) + RET + + // Return with not enough output space error +error_not_enough_space: + MOVD ctx+16(FP), R0 + MOVD 24(RSP), R1 + MOVD R1, 208(R0) + MOVD 16(RSP), R1 + MOVD R1, 216(R0) + MOVD R11, 136(R0) + MOVD $0x00000005, R16 + MOVD R16, ret+24(FP) + RET + +// skipped sequenceDecs_decodeSync_safe_bmi2 (generic twin preferred on arm64) diff --git a/vendor/github.com/klauspost/compress/zstd/seqdec_asm.go b/vendor/github.com/klauspost/compress/zstd/seqdec_asm.go new file mode 100644 index 0000000000..55405f3914 --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/seqdec_asm.go @@ -0,0 +1,289 @@ +//go:build (amd64 || arm64) && !appengine && !noasm && gc + +package zstd + +import ( + "fmt" + "io" +) + +// This file holds the parts of the assembly sequence decoder that are identical +// across architectures: the context structs exchanged with the asm, the error +// codes, and the decode/decodeSync/executeSimple wrappers. Each architecture +// supplies the small dispatch helpers (decodeAsm, decodeSyncAsm, +// executeSimpleAsm) that select the concrete asm routine — amd64 also chooses a +// BMI2 variant, arm64 has a single implementation. + +type decodeSyncAsmContext struct { + llTable []decSymbol + mlTable []decSymbol + ofTable []decSymbol + llState uint64 + mlState uint64 + ofState uint64 + iteration int + litRemain int + out []byte + outPosition int + literals []byte + litPosition int + history []byte + windowSize int + ll int // set on error (not for all errors, please refer to _generate/gen.go) + ml int // set on error (not for all errors, please refer to _generate/gen.go) + mo int // set on error (not for all errors, please refer to _generate/gen.go) +} + +type decodeAsmContext struct { + llTable []decSymbol + mlTable []decSymbol + ofTable []decSymbol + llState uint64 + mlState uint64 + ofState uint64 + iteration int + seqs []seqVals + litRemain int +} + +type executeAsmContext struct { + seqs []seqVals + seqIndex int + out []byte + history []byte + literals []byte + outPosition int + litPosition int + windowSize int +} + +const noError = 0 + +// error reported when mo == 0 && ml > 0 +const errorMatchLenOfsMismatch = 1 + +// error reported when ml > maxMatchLen +const errorMatchLenTooBig = 2 + +// error reported when mo > available history or mo > s.windowSize +const errorMatchOffTooBig = 3 + +// error reported when the sum of literal lengths exeeceds the literal buffer size +const errorNotEnoughLiterals = 4 + +// error reported when capacity of `out` is too small +const errorNotEnoughSpace = 5 + +// error reported when bits are overread. +const errorOverread = 6 + +// decode sequences from the stream with the provided history but without a dictionary. +func (s *sequenceDecs) decodeSyncSimple(hist []byte) (bool, error) { + if len(s.dict) > 0 { + return false, nil + } + if s.maxSyncLen == 0 && cap(s.out)-len(s.out) < maxCompressedBlockSize { + return false, nil + } + + // FIXME: Using unsafe memory copies leads to rare, random crashes + // with fuzz testing. It is therefore disabled for now. + const useSafe = true + + br := s.br + + maxBlockSize := min(s.windowSize, maxCompressedBlockSize) + + ctx := decodeSyncAsmContext{ + llTable: s.litLengths.fse.dt[:maxTablesize], + mlTable: s.matchLengths.fse.dt[:maxTablesize], + ofTable: s.offsets.fse.dt[:maxTablesize], + llState: uint64(s.litLengths.state.state), + mlState: uint64(s.matchLengths.state.state), + ofState: uint64(s.offsets.state.state), + iteration: s.nSeqs - 1, + litRemain: len(s.literals), + out: s.out, + outPosition: len(s.out), + literals: s.literals, + windowSize: s.windowSize, + history: hist, + } + + s.seqSize = 0 + startSize := len(s.out) + + errCode := decodeSyncAsm(s, br, &ctx, useSafe) + switch errCode { + case noError: + break + + case errorMatchLenOfsMismatch: + return true, fmt.Errorf("zero matchoff and matchlen (%d) > 0", ctx.ml) + + case errorMatchLenTooBig: + return true, fmt.Errorf("match len (%d) bigger than max allowed length", ctx.ml) + + case errorMatchOffTooBig: + return true, fmt.Errorf("match offset (%d) bigger than current history (%d)", + ctx.mo, ctx.outPosition+len(hist)-startSize) + + case errorNotEnoughLiterals: + return true, fmt.Errorf("unexpected literal count, want %d bytes, but only %d is available", + ctx.ll, ctx.litRemain+ctx.ll) + + case errorOverread: + return true, io.ErrUnexpectedEOF + + case errorNotEnoughSpace: + size := ctx.outPosition + ctx.ll + ctx.ml + if debugDecoder { + println("msl:", s.maxSyncLen, "cap", cap(s.out), "bef:", startSize, "sz:", size-startSize, "mbs:", maxBlockSize, "outsz:", cap(s.out)-startSize) + } + return true, fmt.Errorf("output bigger than max block size (%d)", maxBlockSize) + + default: + return true, fmt.Errorf("sequenceDecs_decode returned erroneous code %d", errCode) + } + + s.seqSize += ctx.litRemain + if s.seqSize > maxBlockSize { + return true, fmt.Errorf("output bigger than max block size (%d)", maxBlockSize) + } + err := br.close() + if err != nil { + printf("Closing sequences: %v, %+v\n", err, *br) + return true, err + } + + s.literals = s.literals[ctx.litPosition:] + t := ctx.outPosition + s.out = s.out[:t] + + // Add final literals + s.out = append(s.out, s.literals...) + if debugDecoder { + t += len(s.literals) + if t != len(s.out) { + panic(fmt.Errorf("length mismatch, want %d, got %d", len(s.out), t)) + } + } + + return true, nil +} + +// decode sequences from the stream without the provided history. +func (s *sequenceDecs) decode(seqs []seqVals) error { + br := s.br + + maxBlockSize := min(s.windowSize, maxCompressedBlockSize) + + ctx := decodeAsmContext{ + llTable: s.litLengths.fse.dt[:maxTablesize], + mlTable: s.matchLengths.fse.dt[:maxTablesize], + ofTable: s.offsets.fse.dt[:maxTablesize], + llState: uint64(s.litLengths.state.state), + mlState: uint64(s.matchLengths.state.state), + ofState: uint64(s.offsets.state.state), + seqs: seqs, + iteration: len(seqs) - 1, + litRemain: len(s.literals), + } + + if debugDecoder { + println("decode: decoding", len(seqs), "sequences", br.remain(), "bits remain on stream") + } + + s.seqSize = 0 + lte56bits := s.maxBits+s.offsets.fse.actualTableLog+s.matchLengths.fse.actualTableLog+s.litLengths.fse.actualTableLog <= 56 + errCode := decodeAsm(s, br, &ctx, lte56bits) + if errCode != 0 { + i := len(seqs) - ctx.iteration - 1 + switch errCode { + case errorMatchLenOfsMismatch: + ml := ctx.seqs[i].ml + return fmt.Errorf("zero matchoff and matchlen (%d) > 0", ml) + + case errorMatchLenTooBig: + ml := ctx.seqs[i].ml + return fmt.Errorf("match len (%d) bigger than max allowed length", ml) + + case errorNotEnoughLiterals: + ll := ctx.seqs[i].ll + return fmt.Errorf("unexpected literal count, want %d bytes, but only %d is available", ll, ctx.litRemain+ll) + case errorOverread: + return io.ErrUnexpectedEOF + } + + return fmt.Errorf("sequenceDecs_decode_amd64 returned erroneous code %d", errCode) + } + + if ctx.litRemain < 0 { + return fmt.Errorf("literal count is too big: total available %d, total requested %d", + len(s.literals), len(s.literals)-ctx.litRemain) + } + + s.seqSize += ctx.litRemain + if s.seqSize > maxBlockSize { + return fmt.Errorf("output bigger than max block size (%d)", maxBlockSize) + } + if debugDecoder { + println("decode: ", br.remain(), "bits remain on stream. code:", errCode) + } + err := br.close() + if err != nil { + printf("Closing sequences: %v, %+v\n", err, *br) + } + return err +} + +// executeSimple handles cases when dictionary is not used. +func (s *sequenceDecs) executeSimple(seqs []seqVals, hist []byte) error { + // Ensure we have enough output size... + if len(s.out)+s.seqSize+compressedBlockOverAlloc > cap(s.out) { + addBytes := s.seqSize + len(s.out) + compressedBlockOverAlloc + s.out = append(s.out, make([]byte, addBytes)...) + s.out = s.out[:len(s.out)-addBytes] + } + + if debugDecoder { + printf("Execute %d seqs with literals: %d into %d bytes\n", len(seqs), len(s.literals), s.seqSize) + } + + var t = len(s.out) + out := s.out[:t+s.seqSize] + + ctx := executeAsmContext{ + seqs: seqs, + seqIndex: 0, + out: out, + history: hist, + outPosition: t, + litPosition: 0, + literals: s.literals, + windowSize: s.windowSize, + } + // useSafe avoids overwriting the output buffer when the literals slice has + // not been allocated with the required over-allocation slack. + useSafe := cap(s.literals) < len(s.literals)+compressedBlockOverAlloc + + ok := executeSimpleAsm(&ctx, useSafe) + if !ok { + return fmt.Errorf("match offset (%d) bigger than current history (%d)", + seqs[ctx.seqIndex].mo, ctx.outPosition+len(hist)) + } + s.literals = s.literals[ctx.litPosition:] + t = ctx.outPosition + + // Add final literals + copy(out[t:], s.literals) + if debugDecoder { + t += len(s.literals) + if t != len(out) { + panic(fmt.Errorf("length mismatch, want %d, got %d, ss: %d", len(out), t, s.seqSize)) + } + } + s.out = out + + return nil +} diff --git a/vendor/github.com/klauspost/compress/zstd/seqdec_generic.go b/vendor/github.com/klauspost/compress/zstd/seqdec_generic.go index 516cd9b070..8a3db6ba22 100644 --- a/vendor/github.com/klauspost/compress/zstd/seqdec_generic.go +++ b/vendor/github.com/klauspost/compress/zstd/seqdec_generic.go @@ -1,4 +1,4 @@ -//go:build !amd64 || appengine || !gc || noasm +//go:build (!amd64 && !arm64) || appengine || !gc || noasm package zstd diff --git a/vendor/github.com/klauspost/compress/zstd/snappy.go b/vendor/github.com/klauspost/compress/zstd/snappy.go index 336c288930..36c56f36d7 100644 --- a/vendor/github.com/klauspost/compress/zstd/snappy.go +++ b/vendor/github.com/klauspost/compress/zstd/snappy.go @@ -334,9 +334,10 @@ func decodeSnappy(blk *blockEnc, src []byte) error { return errUnsupportedLiteralLength } - //if length > snappyMaxBlockSize-d || uint32(length) > len(src)-s { - // return ErrSnappyCorrupt - //} + if length > len(src)-s { + println("length > len(src)-s", length, len(src)-s) + return ErrSnappyCorrupt + } blk.literals = append(blk.literals, src[s:s+length]...) //println(length, "litLen") diff --git a/vendor/github.com/mdlayher/socket/.golangci.yml b/vendor/github.com/mdlayher/socket/.golangci.yml new file mode 100644 index 0000000000..1f10166aab --- /dev/null +++ b/vendor/github.com/mdlayher/socket/.golangci.yml @@ -0,0 +1,16 @@ +version: "2" +linters: + enable: + - misspell + - modernize + - revive + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling +formatters: + exclusions: + generated: lax diff --git a/vendor/github.com/mdlayher/socket/CHANGELOG.md b/vendor/github.com/mdlayher/socket/CHANGELOG.md index f0d01641a2..b94818e7ca 100644 --- a/vendor/github.com/mdlayher/socket/CHANGELOG.md +++ b/vendor/github.com/mdlayher/socket/CHANGELOG.md @@ -1,5 +1,24 @@ # CHANGELOG +## v0.5.2 + +- [Improvement]: Bump build to Go 1.23.0. Note this is required for the latest + Go extended library versions. + +## v0.5.1 + +- [Improvement]: revert `go.mod` to Go 1.20 to [resolve an issue around Go + module version upgrades](https://github.com/mdlayher/socket/issues/13). + +## v0.5.0 + +**This is the first release of package socket that only supports Go 1.21+. +Users on older versions of Go must use v0.4.1.** + +- [Improvement]: drop support for older versions of Go. +- [New API]: add `socket.Conn` wrappers for various `Getsockopt` and + `Setsockopt` system calls. + ## v0.4.1 - [Bug Fix] [commit](https://github.com/mdlayher/socket/commit/2a14ceef4da279de1f957c5761fffcc6c87bbd3b): diff --git a/vendor/github.com/mdlayher/socket/accept4.go b/vendor/github.com/mdlayher/socket/accept4.go index e1016b2063..48be812ed8 100644 --- a/vendor/github.com/mdlayher/socket/accept4.go +++ b/vendor/github.com/mdlayher/socket/accept4.go @@ -1,5 +1,4 @@ //go:build dragonfly || freebsd || illumos || linux -// +build dragonfly freebsd illumos linux package socket diff --git a/vendor/github.com/mdlayher/socket/conn.go b/vendor/github.com/mdlayher/socket/conn.go index 7b3cc7a6e7..6057a2e0a8 100644 --- a/vendor/github.com/mdlayher/socket/conn.go +++ b/vendor/github.com/mdlayher/socket/conn.go @@ -120,7 +120,7 @@ func (c *Conn) ReadContext(ctx context.Context, b []byte) (int, error) { b = b[:maxRW] } - n, err := readT(c, ctx, "read", func(fd int) (int, error) { + n, err := readT(ctx, c, "read", func(fd int) (int, error) { return unix.Read(fd, b) }) if n == 0 && err == nil && c.facts.zeroReadIsEOF { @@ -142,12 +142,12 @@ func (c *Conn) WriteContext(ctx context.Context, b []byte) (int, error) { ) doErr := c.write(ctx, "write", func(fd int) error { - max := len(b) - if c.facts.isStream && max-nn > maxRW { - max = nn + maxRW + lenb := len(b) + if c.facts.isStream && lenb-nn > maxRW { + lenb = nn + maxRW } - n, err = unix.Write(fd, b[nn:max]) + n, err = unix.Write(fd, b[nn:lenb]) if n > 0 { nn += n } @@ -418,7 +418,7 @@ func (c *Conn) Accept(ctx context.Context, flags int) (*Conn, unix.Sockaddr, err sa unix.Sockaddr } - r, err := readT(c, ctx, sysAccept, func(fd int) (ret, error) { + r, err := readT(ctx, c, sysAccept, func(fd int) (ret, error) { // Either accept(2) or accept4(2) depending on the OS. nfd, sa, err := accept(fd, flags|socketFlags) return ret{nfd, sa}, err @@ -440,9 +440,7 @@ func (c *Conn) Accept(ctx context.Context, flags int) (*Conn, unix.Sockaddr, err // Bind wraps bind(2). func (c *Conn) Bind(sa unix.Sockaddr) error { - return c.control(context.Background(), "bind", func(fd int) error { - return unix.Bind(fd, sa) - }) + return c.control("bind", func(fd int) error { return unix.Bind(fd, sa) }) } // Connect wraps connect(2). In order to verify that the underlying socket is @@ -466,7 +464,7 @@ func (c *Conn) Connect(ctx context.Context, sa unix.Sockaddr) (unix.Sockaddr, er // have an explicit WaitWrite call like internal/poll does, so we have // to wait until the runtime calls the closure again to indicate we can // write. - progress uint32 + progress atomic.Uint32 // Capture closure sockaddr and error. rsa unix.Sockaddr @@ -474,7 +472,7 @@ func (c *Conn) Connect(ctx context.Context, sa unix.Sockaddr) (unix.Sockaddr, er ) doErr := c.write(ctx, op, func(fd int) error { - if atomic.AddUint32(&progress, 1) == 1 { + if progress.Add(1) == 1 { // First call: initiate connect. return unix.Connect(fd, sa) } @@ -530,26 +528,38 @@ func (c *Conn) Connect(ctx context.Context, sa unix.Sockaddr) (unix.Sockaddr, er // Getsockname wraps getsockname(2). func (c *Conn) Getsockname() (unix.Sockaddr, error) { - return controlT(c, context.Background(), "getsockname", unix.Getsockname) + return controlT(c, "getsockname", unix.Getsockname) } // Getpeername wraps getpeername(2). func (c *Conn) Getpeername() (unix.Sockaddr, error) { - return controlT(c, context.Background(), "getpeername", unix.Getpeername) + return controlT(c, "getpeername", unix.Getpeername) +} + +// GetsockoptICMPv6Filter wraps getsockopt(2) for *unix.ICMPv6Filter values. +func (c *Conn) GetsockoptICMPv6Filter(level, opt int) (*unix.ICMPv6Filter, error) { + return controlT(c, "getsockopt", func(fd int) (*unix.ICMPv6Filter, error) { + return unix.GetsockoptICMPv6Filter(fd, level, opt) + }) } // GetsockoptInt wraps getsockopt(2) for integer values. func (c *Conn) GetsockoptInt(level, opt int) (int, error) { - return controlT(c, context.Background(), "getsockopt", func(fd int) (int, error) { + return controlT(c, "getsockopt", func(fd int) (int, error) { return unix.GetsockoptInt(fd, level, opt) }) } +// GetsockoptString wraps getsockopt(2) for string values. +func (c *Conn) GetsockoptString(level, opt int) (string, error) { + return controlT(c, "getsockopt", func(fd int) (string, error) { + return unix.GetsockoptString(fd, level, opt) + }) +} + // Listen wraps listen(2). func (c *Conn) Listen(n int) error { - return c.control(context.Background(), "listen", func(fd int) error { - return unix.Listen(fd, n) - }) + return c.control("listen", func(fd int) error { return unix.Listen(fd, n) }) } // Recvmsg wraps recvmsg(2). @@ -559,7 +569,7 @@ func (c *Conn) Recvmsg(ctx context.Context, p, oob []byte, flags int) (int, int, from unix.Sockaddr } - r, err := readT(c, ctx, "recvmsg", func(fd int) (ret, error) { + r, err := readT(ctx, c, "recvmsg", func(fd int) (ret, error) { n, oobn, recvflags, from, err := unix.Recvmsg(fd, p, oob, flags) return ret{n, oobn, recvflags, from}, err }) @@ -577,7 +587,7 @@ func (c *Conn) Recvfrom(ctx context.Context, p []byte, flags int) (int, unix.Soc addr unix.Sockaddr } - out, err := readT(c, ctx, "recvfrom", func(fd int) (ret, error) { + out, err := readT(ctx, c, "recvfrom", func(fd int) (ret, error) { n, addr, err := unix.Recvfrom(fd, p, flags) return ret{n, addr}, err }) @@ -590,7 +600,7 @@ func (c *Conn) Recvfrom(ctx context.Context, p []byte, flags int) (int, unix.Soc // Sendmsg wraps sendmsg(2). func (c *Conn) Sendmsg(ctx context.Context, p, oob []byte, to unix.Sockaddr, flags int) (int, error) { - return writeT(c, ctx, "sendmsg", func(fd int) (int, error) { + return writeT(ctx, c, "sendmsg", func(fd int) (int, error) { return unix.SendmsgN(fd, p, oob, to, flags) }) } @@ -602,18 +612,30 @@ func (c *Conn) Sendto(ctx context.Context, p []byte, flags int, to unix.Sockaddr }) } +// SetsockoptICMPv6Filter wraps setsockopt(2) for *unix.ICMPv6Filter values. +func (c *Conn) SetsockoptICMPv6Filter(level, opt int, filter *unix.ICMPv6Filter) error { + return c.control("setsockopt", func(fd int) error { + return unix.SetsockoptICMPv6Filter(fd, level, opt, filter) + }) +} + // SetsockoptInt wraps setsockopt(2) for integer values. func (c *Conn) SetsockoptInt(level, opt, value int) error { - return c.control(context.Background(), "setsockopt", func(fd int) error { + return c.control("setsockopt", func(fd int) error { return unix.SetsockoptInt(fd, level, opt, value) }) } +// SetsockoptString wraps setsockopt(2) for string values. +func (c *Conn) SetsockoptString(level, opt int, value string) error { + return c.control("setsockopt", func(fd int) error { + return unix.SetsockoptString(fd, level, opt, value) + }) +} + // Shutdown wraps shutdown(2). func (c *Conn) Shutdown(how int) error { - return c.control(context.Background(), "shutdown", func(fd int) error { - return unix.Shutdown(fd, how) - }) + return c.control("shutdown", func(fd int) error { return unix.Shutdown(fd, how) }) } // Conn low-level read/write/control functions. These functions mirror the @@ -623,7 +645,7 @@ func (c *Conn) Shutdown(how int) error { // read wraps readT to execute a function and capture its error result. This is // a convenience wrapper for functions which don't return any extra values. func (c *Conn) read(ctx context.Context, op string, f func(fd int) error) error { - _, err := readT(c, ctx, op, func(fd int) (struct{}, error) { + _, err := readT(ctx, c, op, func(fd int) (struct{}, error) { return struct{}{}, f(fd) }) return err @@ -632,7 +654,7 @@ func (c *Conn) read(ctx context.Context, op string, f func(fd int) error) error // write executes f, a write function, against the associated file descriptor. // op is used to create an *os.SyscallError if the file descriptor is closed. func (c *Conn) write(ctx context.Context, op string, f func(fd int) error) error { - _, err := writeT(c, ctx, op, func(fd int) (struct{}, error) { + _, err := writeT(ctx, c, op, func(fd int) (struct{}, error) { return struct{}{}, f(fd) }) return err @@ -640,7 +662,7 @@ func (c *Conn) write(ctx context.Context, op string, f func(fd int) error) error // readT executes c.rc.Read for op using the input function, returning a newly // allocated result T. -func readT[T any](c *Conn, ctx context.Context, op string, f func(fd int) (T, error)) (T, error) { +func readT[T any](ctx context.Context, c *Conn, op string, f func(fd int) (T, error)) (T, error) { return rwT(c, rwContext[T]{ Context: ctx, Type: read, @@ -651,7 +673,7 @@ func readT[T any](c *Conn, ctx context.Context, op string, f func(fd int) (T, er // writeT executes c.rc.Write for op using the input function, returning a newly // allocated result T. -func writeT[T any](c *Conn, ctx context.Context, op string, f func(fd int) (T, error)) (T, error) { +func writeT[T any](ctx context.Context, c *Conn, op string, f func(fd int) (T, error)) (T, error) { return rwT(c, rwContext[T]{ Context: ctx, Type: write, @@ -725,10 +747,7 @@ func rwT[T any](c *Conn, rw rwContext[T]) (T, error) { doneC = make(chan struct{}) // Atomic: reports whether we have to disarm the deadline. - // - // TODO(mdlayher): switch back to atomic.Bool when we drop support for - // Go 1.18. - needDisarm int64 + needDisarm atomic.Bool ) // On cancel, clean up the watcher. @@ -744,7 +763,7 @@ func rwT[T any](c *Conn, rw rwContext[T]) (T, error) { return *new(T), err } setDeadline = true - atomic.AddInt64(&needDisarm, 1) + needDisarm.Store(true) } else { // The context does not have an explicit deadline. We have to watch for // cancelation so we can propagate that signal to immediately unblock @@ -752,20 +771,18 @@ func rwT[T any](c *Conn, rw rwContext[T]) (T, error) { // // TODO(mdlayher): is it possible to detect a background context vs a // context with possible future cancel? - wg.Add(1) - go func() { - defer wg.Done() + wg.Go(func() { select { case <-rw.Context.Done(): // Cancel the operation. Make the caller disarm after poll // returns. - atomic.AddInt64(&needDisarm, 1) + needDisarm.Store(true) _ = deadline(time.Unix(0, 1)) case <-doneC: // Nothing to do. } - }() + }) } var ( @@ -778,7 +795,7 @@ func rwT[T any](c *Conn, rw rwContext[T]) (T, error) { return ready(err) }) - if atomic.LoadInt64(&needDisarm) > 0 { + if needDisarm.Load() { _ = deadline(time.Time{}) } @@ -805,8 +822,8 @@ func rwT[T any](c *Conn, rw rwContext[T]) (T, error) { } // control executes Conn.control for op using the input function. -func (c *Conn) control(ctx context.Context, op string, f func(fd int) error) error { - _, err := controlT(c, ctx, op, func(fd int) (struct{}, error) { +func (c *Conn) control(op string, f func(fd int) error) error { + _, err := controlT(c, op, func(fd int) (struct{}, error) { return struct{}{}, f(fd) }) return err @@ -814,7 +831,7 @@ func (c *Conn) control(ctx context.Context, op string, f func(fd int) error) err // controlT executes c.rc.Control for op using the input function, returning a // newly allocated result T. -func controlT[T any](c *Conn, ctx context.Context, op string, f func(fd int) (T, error)) (T, error) { +func controlT[T any](c *Conn, op string, f func(fd int) (T, error)) (T, error) { if atomic.LoadUint32(&c.closed) != 0 { // If the file descriptor is already closed, do nothing. return *new(T), os.NewSyscallError(op, unix.EBADF) @@ -832,11 +849,6 @@ func controlT[T any](c *Conn, ctx context.Context, op string, f func(fd int) (T, // The last values for t and err are captured outside of the closure for // use when the loop breaks. for { - if err = ctx.Err(); err != nil { - // Early exit due to context cancel. - return - } - t, err = f(int(fd)) if ready(err) { return diff --git a/vendor/github.com/mdlayher/socket/conn_linux.go b/vendor/github.com/mdlayher/socket/conn_linux.go index 37579d4a0c..b1cdffbdc6 100644 --- a/vendor/github.com/mdlayher/socket/conn_linux.go +++ b/vendor/github.com/mdlayher/socket/conn_linux.go @@ -1,5 +1,4 @@ //go:build linux -// +build linux package socket @@ -15,7 +14,7 @@ import ( // IoctlKCMClone wraps ioctl(2) for unix.KCMClone values, but returns a Conn // rather than a raw file descriptor. func (c *Conn) IoctlKCMClone() (*Conn, error) { - info, err := controlT(c, context.Background(), "ioctl", unix.IoctlKCMClone) + info, err := controlT(c, "ioctl", unix.IoctlKCMClone) if err != nil { return nil, err } @@ -26,14 +25,14 @@ func (c *Conn) IoctlKCMClone() (*Conn, error) { // IoctlKCMAttach wraps ioctl(2) for unix.KCMAttach values. func (c *Conn) IoctlKCMAttach(info unix.KCMAttach) error { - return c.control(context.Background(), "ioctl", func(fd int) error { + return c.control("ioctl", func(fd int) error { return unix.IoctlKCMAttach(fd, info) }) } // IoctlKCMUnattach wraps ioctl(2) for unix.KCMUnattach values. func (c *Conn) IoctlKCMUnattach(info unix.KCMUnattach) error { - return c.control(context.Background(), "ioctl", func(fd int) error { + return c.control("ioctl", func(fd int) error { return unix.IoctlKCMUnattach(fd, info) }) } @@ -41,7 +40,7 @@ func (c *Conn) IoctlKCMUnattach(info unix.KCMUnattach) error { // PidfdGetfd wraps pidfd_getfd(2) for a Conn which wraps a pidfd, but returns a // Conn rather than a raw file descriptor. func (c *Conn) PidfdGetfd(targetFD, flags int) (*Conn, error) { - outFD, err := controlT(c, context.Background(), "pidfd_getfd", func(fd int) (int, error) { + outFD, err := controlT(c, "pidfd_getfd", func(fd int) (int, error) { return unix.PidfdGetfd(fd, targetFD, flags) }) if err != nil { @@ -55,7 +54,7 @@ func (c *Conn) PidfdGetfd(targetFD, flags int) (*Conn, error) { // PidfdSendSignal wraps pidfd_send_signal(2) for a Conn which wraps a Linux // pidfd. func (c *Conn) PidfdSendSignal(sig unix.Signal, info *unix.Siginfo, flags int) error { - return c.control(context.Background(), "pidfd_send_signal", func(fd int) error { + return c.control("pidfd_send_signal", func(fd int) error { return unix.PidfdSendSignal(fd, sig, info, flags) }) } @@ -84,28 +83,28 @@ func (c *Conn) RemoveBPF() error { // SetsockoptPacketMreq wraps setsockopt(2) for unix.PacketMreq values. func (c *Conn) SetsockoptPacketMreq(level, opt int, mreq *unix.PacketMreq) error { - return c.control(context.Background(), "setsockopt", func(fd int) error { + return c.control("setsockopt", func(fd int) error { return unix.SetsockoptPacketMreq(fd, level, opt, mreq) }) } // SetsockoptSockFprog wraps setsockopt(2) for unix.SockFprog values. func (c *Conn) SetsockoptSockFprog(level, opt int, fprog *unix.SockFprog) error { - return c.control(context.Background(), "setsockopt", func(fd int) error { + return c.control("setsockopt", func(fd int) error { return unix.SetsockoptSockFprog(fd, level, opt, fprog) }) } // GetsockoptTpacketStats wraps getsockopt(2) for unix.TpacketStats values. func (c *Conn) GetsockoptTpacketStats(level, name int) (*unix.TpacketStats, error) { - return controlT(c, context.Background(), "getsockopt", func(fd int) (*unix.TpacketStats, error) { + return controlT(c, "getsockopt", func(fd int) (*unix.TpacketStats, error) { return unix.GetsockoptTpacketStats(fd, level, name) }) } // GetsockoptTpacketStatsV3 wraps getsockopt(2) for unix.TpacketStatsV3 values. func (c *Conn) GetsockoptTpacketStatsV3(level, name int) (*unix.TpacketStatsV3, error) { - return controlT(c, context.Background(), "getsockopt", func(fd int) (*unix.TpacketStatsV3, error) { + return controlT(c, "getsockopt", func(fd int) (*unix.TpacketStatsV3, error) { return unix.GetsockoptTpacketStatsV3(fd, level, name) }) } diff --git a/vendor/github.com/mdlayher/socket/netns_linux.go b/vendor/github.com/mdlayher/socket/netns_linux.go index b29115ad1c..9f37b77029 100644 --- a/vendor/github.com/mdlayher/socket/netns_linux.go +++ b/vendor/github.com/mdlayher/socket/netns_linux.go @@ -1,5 +1,4 @@ //go:build linux -// +build linux package socket @@ -65,7 +64,7 @@ func withNetNS(fd int, fn func() (*Conn, error)) (*Conn, error) { // No more thread-local state manipulation; return the new Conn. runtime.UnlockOSThread() conn = c - return nil + return err }) if err := eg.Wait(); err != nil { diff --git a/vendor/github.com/mdlayher/socket/setbuffer_linux.go b/vendor/github.com/mdlayher/socket/setbuffer_linux.go index 0d4aa4417c..ae631893a6 100644 --- a/vendor/github.com/mdlayher/socket/setbuffer_linux.go +++ b/vendor/github.com/mdlayher/socket/setbuffer_linux.go @@ -1,5 +1,4 @@ //go:build linux -// +build linux package socket diff --git a/vendor/github.com/mdlayher/socket/typ_cloexec_nonblock.go b/vendor/github.com/mdlayher/socket/typ_cloexec_nonblock.go index 40e834310b..f4a7e559b3 100644 --- a/vendor/github.com/mdlayher/socket/typ_cloexec_nonblock.go +++ b/vendor/github.com/mdlayher/socket/typ_cloexec_nonblock.go @@ -1,5 +1,4 @@ //go:build !darwin -// +build !darwin package socket diff --git a/vendor/github.com/mdlayher/vsock/.golangci.yml b/vendor/github.com/mdlayher/vsock/.golangci.yml new file mode 100644 index 0000000000..7237e2714d --- /dev/null +++ b/vendor/github.com/mdlayher/vsock/.golangci.yml @@ -0,0 +1,21 @@ +version: "2" +linters: + enable: + # - errorlint + - misspell + - modernize + - revive + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling + rules: + - linters: + - errcheck + path: _test.go +formatters: + exclusions: + generated: lax diff --git a/vendor/github.com/mdlayher/vsock/CHANGELOG.md b/vendor/github.com/mdlayher/vsock/CHANGELOG.md index c64a797bc2..aae7486351 100644 --- a/vendor/github.com/mdlayher/vsock/CHANGELOG.md +++ b/vendor/github.com/mdlayher/vsock/CHANGELOG.md @@ -1,10 +1,16 @@ # CHANGELOG -# v1.2.1 +## v1.3.0 + +- [Improvement]: Updated dependencies and now requires Go 1.25. (#63) +- [Improvement]: Update to use net.ErrClosed error (#57) +- [Tests]: Check for ENETUNREACH and ETIMEDOUT in tests (#54) + +## v1.2.1 - [Improvement]: updated dependencies, test with Go 1.20. -# v1.2.0 +## v1.2.0 **This is the first release of package vsock that only supports Go 1.18+. Users on older versions of Go must use v1.1.1.** diff --git a/vendor/github.com/mdlayher/vsock/conn_linux.go b/vendor/github.com/mdlayher/vsock/conn_linux.go index 6029d547e5..46902d4fca 100644 --- a/vendor/github.com/mdlayher/vsock/conn_linux.go +++ b/vendor/github.com/mdlayher/vsock/conn_linux.go @@ -1,5 +1,4 @@ //go:build linux -// +build linux package vsock diff --git a/vendor/github.com/mdlayher/vsock/fd_linux.go b/vendor/github.com/mdlayher/vsock/fd_linux.go index 531e53f928..25c6e6761a 100644 --- a/vendor/github.com/mdlayher/vsock/fd_linux.go +++ b/vendor/github.com/mdlayher/vsock/fd_linux.go @@ -31,6 +31,6 @@ func isErrno(err error, errno int) bool { } } -func panicf(format string, a ...interface{}) { +func panicf(format string, a ...any) { panic(fmt.Sprintf(format, a...)) } diff --git a/vendor/github.com/mdlayher/vsock/listener_linux.go b/vendor/github.com/mdlayher/vsock/listener_linux.go index 50fa1b7a49..3416368f3f 100644 --- a/vendor/github.com/mdlayher/vsock/listener_linux.go +++ b/vendor/github.com/mdlayher/vsock/listener_linux.go @@ -1,5 +1,4 @@ //go:build linux -// +build linux package vsock diff --git a/vendor/github.com/mdlayher/vsock/vsock.go b/vendor/github.com/mdlayher/vsock/vsock.go index 78763936ae..1cc05202e5 100644 --- a/vendor/github.com/mdlayher/vsock/vsock.go +++ b/vendor/github.com/mdlayher/vsock/vsock.go @@ -1,7 +1,6 @@ package vsock import ( - "errors" "fmt" "io" "net" @@ -403,7 +402,7 @@ func opError(op string, err error, local, remote net.Addr) error { // // To rectify the differences, net.TCPConn uses an error with this text // from internal/poll for the backing file already being closed. - err = errors.New("use of closed network connection") + err = net.ErrClosed default: // Nothing to do, return this directly. } diff --git a/vendor/github.com/oklog/ulid/v2/ulid.go b/vendor/github.com/oklog/ulid/v2/ulid.go index 77e9ddd634..e07e1a6818 100644 --- a/vendor/github.com/oklog/ulid/v2/ulid.go +++ b/vendor/github.com/oklog/ulid/v2/ulid.go @@ -505,7 +505,16 @@ func (id *ULID) Scan(src interface{}) error { case string: return id.UnmarshalText([]byte(x)) case []byte: - return id.UnmarshalBinary(x) + // Drivers often return text/varchar columns as []byte. Accept both + // the 16-byte binary form and the 26-character text encoding. + switch len(x) { + case len(*id): + return id.UnmarshalBinary(x) + case EncodedSize: + return id.UnmarshalText(x) + default: + return ErrDataSize + } } return ErrScanValue diff --git a/vendor/github.com/prometheus/client_golang/internal/github.com/golang/gddo/httputil/header/header.go b/vendor/github.com/prometheus/client_golang/internal/github.com/golang/gddo/httputil/header/header.go index 8547c8dfd1..820bf436ab 100644 --- a/vendor/github.com/prometheus/client_golang/internal/github.com/golang/gddo/httputil/header/header.go +++ b/vendor/github.com/prometheus/client_golang/internal/github.com/golang/gddo/httputil/header/header.go @@ -90,7 +90,7 @@ loop: s = skipSpace(s[1:]) } } - return + return specs } func skipSpace(s string) (rest string) { diff --git a/vendor/github.com/prometheus/client_golang/prometheus/collectors/version/version.go b/vendor/github.com/prometheus/client_golang/prometheus/collectors/version/version.go index c96e187126..0306b5878c 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/collectors/version/version.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/collectors/version/version.go @@ -15,15 +15,44 @@ package version import ( "fmt" + "maps" "github.com/prometheus/common/version" "github.com/prometheus/client_golang/prometheus" ) +type Option func(*options) + +type options struct { + extraConstLabels prometheus.Labels +} + +func WithExtraConstLabels(l prometheus.Labels) Option { + return func(o *options) { + o.extraConstLabels = l + } +} + // NewCollector returns a collector that exports metrics about current version // information. -func NewCollector(program string) prometheus.Collector { +func NewCollector(program string, opts ...Option) prometheus.Collector { + o := options{} + for _, opt := range opts { + opt(&o) + } + + constLabels := prometheus.Labels{ + "version": version.Version, + "revision": version.GetRevision(), + "branch": version.Branch, + "goversion": version.GoVersion, + "goos": version.GoOS, + "goarch": version.GoArch, + "tags": version.GetTags(), + } + maps.Copy(constLabels, o.extraConstLabels) + return prometheus.NewGaugeFunc( prometheus.GaugeOpts{ Namespace: program, @@ -32,15 +61,7 @@ func NewCollector(program string) prometheus.Collector { "A metric with a constant '1' value labeled by version, revision, branch, goversion from which %s was built, and the goos and goarch for the build.", program, ), - ConstLabels: prometheus.Labels{ - "version": version.Version, - "revision": version.GetRevision(), - "branch": version.Branch, - "goversion": version.GoVersion, - "goos": version.GoOS, - "goarch": version.GoArch, - "tags": version.GetTags(), - }, + ConstLabels: constLabels, }, func() float64 { return 1 }, ) diff --git a/vendor/github.com/prometheus/client_golang/prometheus/counter.go b/vendor/github.com/prometheus/client_golang/prometheus/counter.go index 4ce84e7a80..7d963d3afb 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/counter.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/counter.go @@ -85,11 +85,12 @@ type CounterVecOpts struct { // Both internal tracking values are added up in the Write method. This has to // be taken into account when it comes to precision and overflow behavior. func NewCounter(opts CounterOpts) Counter { - desc := NewDesc( + desc := V2.NewDesc( BuildFQName(opts.Namespace, opts.Subsystem, opts.Name), opts.Help, - nil, + UnconstrainedLabels(nil), opts.ConstLabels, + WithUnit(opts.Unit), ) if opts.now == nil { opts.now = time.Now @@ -205,6 +206,7 @@ func (v2) NewCounterVec(opts CounterVecOpts) *CounterVec { opts.Help, opts.VariableLabels, opts.ConstLabels, + WithUnit(opts.Unit), ) if opts.now == nil { opts.now = time.Now @@ -349,10 +351,11 @@ type CounterFunc interface { // // Check out the ExampleGaugeFunc examples for the similar GaugeFunc. func NewCounterFunc(opts CounterOpts, function func() float64) CounterFunc { - return newValueFunc(NewDesc( + return newValueFunc(V2.NewDesc( BuildFQName(opts.Namespace, opts.Subsystem, opts.Name), opts.Help, - nil, + UnconstrainedLabels(nil), opts.ConstLabels, + WithUnit(opts.Unit), ), CounterValue, function) } diff --git a/vendor/github.com/prometheus/client_golang/prometheus/desc.go b/vendor/github.com/prometheus/client_golang/prometheus/desc.go index 2331b8b4f3..a3c92e7a4c 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/desc.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/desc.go @@ -47,6 +47,8 @@ type Desc struct { fqName string // help provides some helpful information about this metric. help string + // unit provides the unit of this metric. + unit string // constLabelPairs contains precalculated DTO label pairs based on // the constant labels. constLabelPairs []*dto.LabelPair @@ -66,6 +68,16 @@ type Desc struct { err error } +// DescOpt allows setting optional fields for NewDesc. +type DescOpt func(*Desc) + +// WithUnit sets the unit for a Desc. +func WithUnit(unit string) DescOpt { + return func(d *Desc) { + d.unit = unit + } +} + // NewDesc allocates and initializes a new Desc. Errors are recorded in the Desc // and will be reported on registration time. variableLabels and constLabels can // be nil if no such labels should be set. fqName must not be empty. @@ -89,14 +101,17 @@ func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) * // // For constLabels, the label values are constant. Therefore, they are fully // specified in the Desc. See the Collector example for a usage pattern. -func (v2) NewDesc(fqName, help string, variableLabels ConstrainableLabels, constLabels Labels) *Desc { +func (v2) NewDesc(fqName, help string, variableLabels ConstrainableLabels, constLabels Labels, opts ...DescOpt) *Desc { d := &Desc{ fqName: fqName, help: help, variableLabels: variableLabels.compile(), } - //nolint:staticcheck // TODO: Don't use deprecated model.NameValidationScheme. - if !model.NameValidationScheme.IsValidMetricName(fqName) { + + for _, opt := range opts { + opt(d) + } + if !model.UTF8Validation.IsValidMetricName(fqName) { d.err = fmt.Errorf("%q is not a valid metric name", fqName) return d } @@ -150,11 +165,13 @@ func (v2) NewDesc(fqName, help string, variableLabels ConstrainableLabels, const d.id = xxh.Sum64() // Sort labelNames so that order doesn't matter for the hash. sort.Strings(labelNames) - // Now hash together (in this order) the help string and the sorted + // Now hash together (in this order) the help string, the unit string and the sorted // label names. xxh.Reset() xxh.WriteString(help) xxh.Write(separatorByteSlice) + xxh.WriteString(d.unit) + xxh.Write(separatorByteSlice) for _, labelName := range labelNames { xxh.WriteString(labelName) xxh.Write(separatorByteSlice) @@ -182,6 +199,15 @@ func NewInvalidDesc(err error) *Desc { } } +// Err returns an error that occurred during construction, if any. +// +// Calling this method is optional. It can be used to detect construction +// errors early, before invoking other methods on the Desc. If an error is +// present, later operations may not behave as expected. +func (d *Desc) Err() error { + return d.err +} + func (d *Desc) String() string { lpStrings := make([]string, 0, len(d.constLabelPairs)) for _, lp := range d.constLabelPairs { @@ -202,9 +228,10 @@ func (d *Desc) String() string { } } return fmt.Sprintf( - "Desc{fqName: %q, help: %q, constLabels: {%s}, variableLabels: {%s}}", + "Desc{fqName: %q, help: %q, unit: %q, constLabels: {%s}, variableLabels: {%s}}", d.fqName, d.help, + d.unit, strings.Join(lpStrings, ","), strings.Join(vlStrings, ","), ) diff --git a/vendor/github.com/prometheus/client_golang/prometheus/expvar_collector.go b/vendor/github.com/prometheus/client_golang/prometheus/expvar_collector.go index de5a856293..327746f433 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/expvar_collector.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/expvar_collector.go @@ -47,14 +47,14 @@ func (e *expvarCollector) Collect(ch chan<- Metric) { if expVar == nil { continue } - var v interface{} + var v any labels := make([]string, len(desc.variableLabels.names)) if err := json.Unmarshal([]byte(expVar.String()), &v); err != nil { ch <- NewInvalidMetric(desc, err) continue } - var processValue func(v interface{}, i int) - processValue = func(v interface{}, i int) { + var processValue func(v any, i int) + processValue = func(v any, i int) { if i >= len(labels) { copiedLabels := append(make([]string, 0, len(labels)), labels...) switch v := v.(type) { @@ -72,7 +72,7 @@ func (e *expvarCollector) Collect(ch chan<- Metric) { ch <- m return } - vm, ok := v.(map[string]interface{}) + vm, ok := v.(map[string]any) if !ok { return } diff --git a/vendor/github.com/prometheus/client_golang/prometheus/gauge.go b/vendor/github.com/prometheus/client_golang/prometheus/gauge.go index dd2eac9406..41e54bf270 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/gauge.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/gauge.go @@ -76,11 +76,12 @@ type GaugeVecOpts struct { // scenarios for Gauges and Counters, where the former tends to be Set-heavy and // the latter Inc-heavy. func NewGauge(opts GaugeOpts) Gauge { - desc := NewDesc( + desc := V2.NewDesc( BuildFQName(opts.Namespace, opts.Subsystem, opts.Name), opts.Help, - nil, + UnconstrainedLabels(nil), opts.ConstLabels, + WithUnit(opts.Unit), ) result := &gauge{desc: desc, labelPairs: desc.constLabelPairs} result.init(result) // Init self-collection. @@ -163,6 +164,7 @@ func (v2) NewGaugeVec(opts GaugeVecOpts) *GaugeVec { opts.Help, opts.VariableLabels, opts.ConstLabels, + WithUnit(opts.Unit), ) return &GaugeVec{ MetricVec: NewMetricVec(desc, func(lvs ...string) Metric { @@ -302,10 +304,11 @@ type GaugeFunc interface { // value of 1. Example: // https://github.com/prometheus/common/blob/8558a5b7db3c84fa38b4766966059a7bd5bfa2ee/version/info.go#L36-L56 func NewGaugeFunc(opts GaugeOpts, function func() float64) GaugeFunc { - return newValueFunc(NewDesc( + return newValueFunc(V2.NewDesc( BuildFQName(opts.Namespace, opts.Subsystem, opts.Name), opts.Help, - nil, + UnconstrainedLabels(nil), opts.ConstLabels, + WithUnit(opts.Unit), ), GaugeValue, function) } diff --git a/vendor/github.com/prometheus/client_golang/prometheus/go_collector_go116.go b/vendor/github.com/prometheus/client_golang/prometheus/go_collector_go116.go deleted file mode 100644 index 897a6e906b..0000000000 --- a/vendor/github.com/prometheus/client_golang/prometheus/go_collector_go116.go +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2021 The Prometheus Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//go:build !go1.17 -// +build !go1.17 - -package prometheus - -import ( - "runtime" - "sync" - "time" -) - -type goCollector struct { - base baseGoCollector - - // ms... are memstats related. - msLast *runtime.MemStats // Previously collected memstats. - msLastTimestamp time.Time - msMtx sync.Mutex // Protects msLast and msLastTimestamp. - msMetrics memStatsMetrics - msRead func(*runtime.MemStats) // For mocking in tests. - msMaxWait time.Duration // Wait time for fresh memstats. - msMaxAge time.Duration // Maximum allowed age of old memstats. -} - -// NewGoCollector is the obsolete version of collectors.NewGoCollector. -// See there for documentation. -// -// Deprecated: Use collectors.NewGoCollector instead. -func NewGoCollector() Collector { - msMetrics := goRuntimeMemStats() - msMetrics = append(msMetrics, struct { - desc *Desc - eval func(*runtime.MemStats) float64 - valType ValueType - }{ - // This metric is omitted in Go1.17+, see https://github.com/prometheus/client_golang/issues/842#issuecomment-861812034 - desc: NewDesc( - memstatNamespace("gc_cpu_fraction"), - "The fraction of this program's available CPU time used by the GC since the program started.", - nil, nil, - ), - eval: func(ms *runtime.MemStats) float64 { return ms.GCCPUFraction }, - valType: GaugeValue, - }) - return &goCollector{ - base: newBaseGoCollector(), - msLast: &runtime.MemStats{}, - msRead: runtime.ReadMemStats, - msMaxWait: time.Second, - msMaxAge: 5 * time.Minute, - msMetrics: msMetrics, - } -} - -// Describe returns all descriptions of the collector. -func (c *goCollector) Describe(ch chan<- *Desc) { - c.base.Describe(ch) - for _, i := range c.msMetrics { - ch <- i.desc - } -} - -// Collect returns the current state of all metrics of the collector. -func (c *goCollector) Collect(ch chan<- Metric) { - var ( - ms = &runtime.MemStats{} - done = make(chan struct{}) - ) - // Start reading memstats first as it might take a while. - go func() { - c.msRead(ms) - c.msMtx.Lock() - c.msLast = ms - c.msLastTimestamp = time.Now() - c.msMtx.Unlock() - close(done) - }() - - // Collect base non-memory metrics. - c.base.Collect(ch) - - timer := time.NewTimer(c.msMaxWait) - select { - case <-done: // Our own ReadMemStats succeeded in time. Use it. - timer.Stop() // Important for high collection frequencies to not pile up timers. - c.msCollect(ch, ms) - return - case <-timer.C: // Time out, use last memstats if possible. Continue below. - } - c.msMtx.Lock() - if time.Since(c.msLastTimestamp) < c.msMaxAge { - // Last memstats are recent enough. Collect from them under the lock. - c.msCollect(ch, c.msLast) - c.msMtx.Unlock() - return - } - // If we are here, the last memstats are too old or don't exist. We have - // to wait until our own ReadMemStats finally completes. For that to - // happen, we have to release the lock. - c.msMtx.Unlock() - <-done - c.msCollect(ch, ms) -} - -func (c *goCollector) msCollect(ch chan<- Metric, ms *runtime.MemStats) { - for _, i := range c.msMetrics { - ch <- MustNewConstMetric(i.desc, i.valType, i.eval(ms)) - } -} diff --git a/vendor/github.com/prometheus/client_golang/prometheus/go_collector_latest.go b/vendor/github.com/prometheus/client_golang/prometheus/go_collector_latest.go index 6b8684731c..1db1c4be09 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/go_collector_latest.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/go_collector_latest.go @@ -98,7 +98,7 @@ type goCollector struct { // snapshot is always produced by Collect. mu sync.Mutex - // Contains all samples that has to retrieved from runtime/metrics (not all of them will be exposed). + // Contains all samples that have to be retrieved from runtime/metrics (not all of them will be exposed). sampleBuf []metrics.Sample // sampleMap allows lookup for MemStats metrics and runtime/metrics histograms for exact sums. sampleMap map[string]*metrics.Sample @@ -210,16 +210,26 @@ func NewGoCollector(opts ...func(o *internal.GoCollectorOptions)) Collector { sampleBuf = append(sampleBuf, metrics.Sample{Name: d.Name}) sampleMap[d.Name] = &sampleBuf[len(sampleBuf)-1] + // Extract unit from the runtime/metrics name (e.g., "/gc/heap/allocs:bytes" -> "bytes") + // and sanitize to match Prometheus naming conventions (e.g., "cpu-seconds" -> "cpu_seconds") + var unit string + if idx := strings.IndexRune(d.Name, ':'); idx >= 0 { + unit = d.Name[idx+1:] + unit = strings.ReplaceAll(unit, "-", "_") + unit = strings.ReplaceAll(unit, "*", "_") + unit = strings.ReplaceAll(unit, "/", "_per_") + } + var m collectorMetric if d.Kind == metrics.KindFloat64Histogram { _, hasSum := opt.RuntimeMetricSumForHist[d.Name] - unit := d.Name[strings.IndexRune(d.Name, ':')+1:] m = newBatchHistogram( - NewDesc( + V2.NewDesc( BuildFQName(namespace, subsystem, name), help, + UnconstrainedLabels(nil), nil, - nil, + WithUnit(unit), ), internal.RuntimeMetricsBucketsForUnit(bucketsMap[d.Name], unit), hasSum, @@ -230,6 +240,7 @@ func NewGoCollector(opts ...func(o *internal.GoCollectorOptions)) Collector { Subsystem: subsystem, Name: name, Help: help, + Unit: unit, }, ) } else { @@ -238,6 +249,7 @@ func NewGoCollector(opts ...func(o *internal.GoCollectorOptions)) Collector { Subsystem: subsystem, Name: name, Help: help, + Unit: unit, }) } metricSet = append(metricSet, m) diff --git a/vendor/github.com/prometheus/client_golang/prometheus/histogram.go b/vendor/github.com/prometheus/client_golang/prometheus/histogram.go index c453b754a7..88bae3b32c 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/histogram.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/histogram.go @@ -378,6 +378,9 @@ type HistogramOpts struct { // string. Help string + // Unit provides the unit of this Histogram. + Unit string + // ConstLabels are used to attach fixed labels to this metric. Metrics // with the same fully-qualified name must have the same label names in // their ConstLabels. @@ -522,11 +525,12 @@ type HistogramVecOpts struct { // for each bucket. func NewHistogram(opts HistogramOpts) Histogram { return newHistogram( - NewDesc( + V2.NewDesc( BuildFQName(opts.Namespace, opts.Subsystem, opts.Name), opts.Help, - nil, + UnconstrainedLabels(nil), opts.ConstLabels, + WithUnit(opts.Unit), ), opts, ) @@ -966,7 +970,7 @@ func (h *histogram) maybeReset( // We are using the possibly mocked h.now() rather than // time.Since(h.lastResetTime) to enable testing. if h.nativeHistogramMinResetDuration == 0 || // No reset configured. - h.resetScheduled || // Do not interefere if a reset is already scheduled. + h.resetScheduled || // Do not interfere if a reset is already scheduled. h.now().Sub(h.lastResetTime) < h.nativeHistogramMinResetDuration { return false } @@ -1053,8 +1057,8 @@ func (h *histogram) maybeWidenZeroBucket(hot, cold *histogramCounts) bool { atomic.StoreUint64(&cold.nativeHistogramZeroThresholdBits, math.Float64bits(newZeroThreshold)) // ...and then merge the newly deleted buckets into the wider zero // bucket. - mergeAndDeleteOrAddAndReset := func(hotBuckets, coldBuckets *sync.Map) func(k, v interface{}) bool { - return func(k, v interface{}) bool { + mergeAndDeleteOrAddAndReset := func(hotBuckets, coldBuckets *sync.Map) func(k, v any) bool { + return func(k, v any) bool { key := k.(int) bucket := v.(*int64) if key == smallestKey { @@ -1107,8 +1111,8 @@ func (h *histogram) doubleBucketWidth(hot, cold *histogramCounts) { // ...adjust the schema in the cold counts, too... atomic.StoreInt32(&cold.nativeHistogramSchema, coldSchema) // ...and then merge the cold buckets into the wider hot buckets. - merge := func(hotBuckets *sync.Map) func(k, v interface{}) bool { - return func(k, v interface{}) bool { + merge := func(hotBuckets *sync.Map) func(k, v any) bool { + return func(k, v any) bool { key := k.(int) bucket := v.(*int64) // Adjust key to match the bucket to merge into. @@ -1190,6 +1194,7 @@ func (v2) NewHistogramVec(opts HistogramVecOpts) *HistogramVec { opts.Help, opts.VariableLabels, opts.ConstLabels, + WithUnit(opts.Unit), ) return &HistogramVec{ MetricVec: NewMetricVec(desc, func(lvs ...string) Metric { @@ -1476,7 +1481,7 @@ func pickSchema(bucketFactor float64) int32 { func makeBuckets(buckets *sync.Map) ([]*dto.BucketSpan, []int64) { var ii []int - buckets.Range(func(k, v interface{}) bool { + buckets.Range(func(k, v any) bool { ii = append(ii, k.(int)) return true }) @@ -1553,8 +1558,8 @@ func addToBucket(buckets *sync.Map, key int, increment int64) bool { // according to the buckets ranged through. It then resets all buckets ranged // through to 0 (but leaves them in place so that they don't need to get // recreated on the next scrape). -func addAndReset(hotBuckets *sync.Map, bucketNumber *uint32) func(k, v interface{}) bool { - return func(k, v interface{}) bool { +func addAndReset(hotBuckets *sync.Map, bucketNumber *uint32) func(k, v any) bool { + return func(k, v any) bool { bucket := v.(*int64) if addToBucket(hotBuckets, k.(int), atomic.LoadInt64(bucket)) { atomic.AddUint32(bucketNumber, 1) @@ -1565,7 +1570,7 @@ func addAndReset(hotBuckets *sync.Map, bucketNumber *uint32) func(k, v interface } func deleteSyncMap(m *sync.Map) { - m.Range(func(k, v interface{}) bool { + m.Range(func(k, v any) bool { m.Delete(k) return true }) @@ -1573,7 +1578,7 @@ func deleteSyncMap(m *sync.Map) { func findSmallestKey(m *sync.Map) int { result := math.MaxInt32 - m.Range(func(k, v interface{}) bool { + m.Range(func(k, v any) bool { key := k.(int) if key < result { result = key diff --git a/vendor/github.com/prometheus/client_golang/prometheus/internal/difflib.go b/vendor/github.com/prometheus/client_golang/prometheus/internal/difflib.go index 7bac0da33d..2db270f216 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/internal/difflib.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/internal/difflib.go @@ -78,7 +78,7 @@ type OpCode struct { // notion, pairing up elements that appear uniquely in each sequence. // That, and the method here, appear to yield more intuitive difference // reports than does diff. This method appears to be the least vulnerable -// to synching up on blocks of "junk lines", though (like blank lines in +// to syncing up on blocks of "junk lines", though (like blank lines in // ordinary text files, or maybe "

" lines in HTML files). That may be // because this is the only method of the 3 that has a *concept* of // "junk" . @@ -567,7 +567,7 @@ type UnifiedDiff struct { func WriteUnifiedDiff(writer io.Writer, diff UnifiedDiff) error { buf := bufio.NewWriter(writer) defer buf.Flush() - wf := func(format string, args ...interface{}) error { + wf := func(format string, args ...any) error { _, err := fmt.Fprintf(buf, format, args...) return err } diff --git a/vendor/github.com/prometheus/client_golang/prometheus/labels.go b/vendor/github.com/prometheus/client_golang/prometheus/labels.go index 5fe8d3b4d2..a0285489a0 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/labels.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/labels.go @@ -184,6 +184,5 @@ func validateLabelValues(vals []string, expectedNumberOfValues int) error { } func checkLabelName(l string) bool { - //nolint:staticcheck // TODO: Don't use deprecated model.NameValidationScheme. - return model.NameValidationScheme.IsValidLabelName(l) && !strings.HasPrefix(l, reservedLabelPrefix) + return model.UTF8Validation.IsValidLabelName(l) && !strings.HasPrefix(l, reservedLabelPrefix) } diff --git a/vendor/github.com/prometheus/client_golang/prometheus/metric.go b/vendor/github.com/prometheus/client_golang/prometheus/metric.go index 76e59f1288..c5cb90adf8 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/metric.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/metric.go @@ -81,6 +81,9 @@ type Opts struct { // string. Help string + // Unit provides the unit of this metric as per https://prometheus.io/docs/specs/om + Unit string + // ConstLabels are used to attach fixed labels to this metric. Metrics // with the same fully-qualified name must have the same label names in // their ConstLabels. diff --git a/vendor/github.com/prometheus/client_golang/prometheus/process_collector_darwin.go b/vendor/github.com/prometheus/client_golang/prometheus/process_collector_darwin.go index b32c95fa3f..2b16298f40 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/process_collector_darwin.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/process_collector_darwin.go @@ -72,7 +72,13 @@ func getOpenFileCount() (float64, error) { } func (c *processCollector) processCollect(ch chan<- Metric) { - if procs, err := unix.SysctlKinfoProcSlice("kern.proc.pid", os.Getpid()); err == nil { + pid, err := c.pidFn() + if err != nil { + c.reportError(ch, nil, err) + return + } + + if procs, err := unix.SysctlKinfoProcSlice("kern.proc.pid", pid); err == nil { if len(procs) == 1 { startTime := float64(procs[0].Proc.P_starttime.Nano() / 1e9) ch <- MustNewConstMetric(c.startTime, GaugeValue, startTime) @@ -84,6 +90,11 @@ func (c *processCollector) processCollect(ch chan<- Metric) { c.reportError(ch, c.startTime, err) } + if pid != os.Getpid() { + c.reportError(ch, nil, fmt.Errorf("collecting metrics for pid %d is not supported on darwin: process metrics collection is limited to the current process (pid %d)", pid, os.Getpid())) + return + } + // The proc structure returned by kern.proc.pid above has an Rusage member, // but it is not filled in, so it needs to be fetched by getrusage(2). For // that call, the UTime, STime, and Maxrss members are filled out, but not diff --git a/vendor/github.com/prometheus/client_golang/prometheus/process_collector_windows.go b/vendor/github.com/prometheus/client_golang/prometheus/process_collector_windows.go index fa474289ef..c08dd05f03 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/process_collector_windows.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/process_collector_windows.go @@ -30,6 +30,10 @@ var ( procGetProcessMemoryInfo = modpsapi.NewProc("GetProcessMemoryInfo") procGetProcessHandleCount = modkernel32.NewProc("GetProcessHandleCount") + + openProcess = windows.OpenProcess + closeHandle = windows.CloseHandle + getProcessTimes = windows.GetProcessTimes ) type processMemoryCounters struct { @@ -79,10 +83,21 @@ func getProcessHandleCount(handle windows.Handle) (uint32, error) { } func (c *processCollector) processCollect(ch chan<- Metric) { - h := windows.CurrentProcess() + pid, err := c.pidFn() + if err != nil { + c.reportError(ch, nil, err) + return + } + + h, err := openProcess(windows.PROCESS_QUERY_INFORMATION|windows.PROCESS_VM_READ, false, uint32(pid)) + if err != nil { + c.reportError(ch, nil, err) + return + } + defer closeHandle(h) var startTime, exitTime, kernelTime, userTime windows.Filetime - err := windows.GetProcessTimes(h, &startTime, &exitTime, &kernelTime, &userTime) + err = getProcessTimes(h, &startTime, &exitTime, &kernelTime, &userTime) if err != nil { c.reportError(ch, nil, err) return diff --git a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/http.go b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/http.go index 763d99e362..c28af5ce2b 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/http.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/http.go @@ -37,10 +37,12 @@ import ( "fmt" "io" "net/http" + "slices" "strconv" "sync" "time" + dto "github.com/prometheus/client_model/go" "github.com/prometheus/common/expfmt" "github.com/prometheus/client_golang/internal/github.com/golang/gddo/httputil" @@ -74,11 +76,118 @@ func defaultCompressionFormats() []Compression { } var gzipPool = sync.Pool{ - New: func() interface{} { + New: func() any { return gzip.NewWriter(nil) }, } +// coalescingGatherer wraps a TransactionalGatherer to deduplicate concurrent +// Gather calls. When a Gather is already in flight, new callers join the +// existing cycle and receive the same result once it completes. The underlying +// done function is called exactly once, when the last joined caller releases. +// +// This prevents goroutine pile-up when the scrape rate is faster than the +// time collectors need to produce metrics. +type coalescingGatherer struct { + g prometheus.TransactionalGatherer + mu sync.Mutex + cycle *gatherCycle +} + +// gatherCycle tracks a single in-flight Gather and all HTTP handlers sharing it. +type gatherCycle struct { + ready chan struct{} // closed when Gather completes; happens-before reads of mfs/err/done + mfs []*dto.MetricFamily // canonical result, set before ready is closed; callers get a slices.Clone, the element values stay shared and must not be mutated + err error // set before ready is closed + done func() // underlying done callback; set before ready is closed + refs int // number of handlers using this cycle; protected by coalescingGatherer.mu +} + +var _ prometheus.TransactionalGatherer = (*coalescingGatherer)(nil) // compile-time interface check + +// errGatherPanicked is returned to callers that joined an in-flight coalesced +// Gather whose underlying gatherer panicked. See the panic guard in Gather for +// why joiners receive this error instead of the panic itself. +var errGatherPanicked = errors.New("coalesced gather panicked") + +func (c *coalescingGatherer) Gather() ([]*dto.MetricFamily, func(), error) { + c.mu.Lock() + if cy := c.cycle; cy != nil { + // c.cycle is non-nil while Gather runs or handlers are still consuming its results. + cy.refs++ + c.mu.Unlock() + <-cy.ready + // Each caller gets its own slice header so it can filter or reorder + // without racing other callers sharing this cycle. The *dto.MetricFamily + // values remain shared and must not be mutated in place. + return slices.Clone(cy.mfs), c.releaseFunc(cy), cy.err + } + cy := &gatherCycle{ + ready: make(chan struct{}), + done: func() {}, + refs: 1, + } + c.cycle = cy + c.mu.Unlock() + + // Guard against a panic in c.g.Gather. The common case, a panicking + // Collector, never reaches here: Registry.Gather recovers Collector panics + // and returns them as an error. This guard only covers the rare case where + // the wrapped gatherer itself panics. + // + // We deliberately do not recover: the leader's panic propagates and is + // handled by net/http exactly as it would be without coalescing. We only + // set cy.err before closing cy.ready so joiners waiting on <-cy.ready fail + // with that error instead of silently returning an empty, successful + // response, and we clear c.cycle so the next Gather starts a fresh cycle. + // + // The leader never runs its own releaseFunc on this path, so its ref is + // not decremented; that is harmless because the cycle is detached (c.cycle + // = nil) and cy.done is still the no-op set at construction (c.g.Gather + // panicked before assigning a real done). If cy.done is ever made non-nil + // before c.g.Gather runs, this path would need to release it. + panicked := true + defer func() { + if panicked { + c.mu.Lock() + if c.cycle == cy { + c.cycle = nil + } + c.mu.Unlock() + cy.err = errGatherPanicked // set before close: happens-before joiners' reads + close(cy.ready) + } + }() + cy.mfs, cy.done, cy.err = c.g.Gather() + panicked = false + close(cy.ready) // happens-before joiners' reads of cy.mfs/err/done + + // Clone here too so cy.mfs stays the write-once canonical slice: joiners + // read it concurrently via slices.Clone, so the leader must not hand out + // (and potentially reorder) the same backing array. + return slices.Clone(cy.mfs), c.releaseFunc(cy), cy.err +} + +// releaseFunc returns the done callback for one caller sharing cy. +// When the last caller releases, the underlying done is invoked and the +// cycle is cleared so the next Gather starts fresh. +func (c *coalescingGatherer) releaseFunc(cy *gatherCycle) func() { + return func() { + c.mu.Lock() + cy.refs-- + if cy.refs > 0 { + c.mu.Unlock() + return + } + // Last caller. + if c.cycle == cy { + c.cycle = nil + } + c.mu.Unlock() + cy.done() // called outside the lock to avoid holding it during done + } +} + // Handler returns an http.Handler for the prometheus.DefaultGatherer, using // default HandlerOpts, i.e. it reports the first error as an HTTP error, it has // no error logging, and it applies compression if requested by the client. @@ -89,6 +198,10 @@ var gzipPool = sync.Pool{ // metrics used for instrumentation will be shared between them, providing // global scrape counts. // +// The handler supports filtering metrics by name using the `name[]` query parameter. +// Multiple metric names can be specified by providing the parameter multiple times. +// When no name[] parameters are provided, all metrics are returned. +// // This function is meant to cover the bulk of basic use cases. If you are doing // anything that requires more customization (including using a non-default // Gatherer, different instrumentation, and non-default HandlerOpts), use the @@ -105,6 +218,10 @@ func Handler() http.Handler { // Gatherers, with non-default HandlerOpts, and/or with custom (or no) // instrumentation. Use the InstrumentMetricHandler function to apply the same // kind of instrumentation as it is used by the Handler function. +// +// The handler supports filtering metrics by name using the `name[]` query parameter. +// Multiple metric names can be specified by providing the parameter multiple times. +// When no name[] parameters are provided, all metrics are returned. func HandlerFor(reg prometheus.Gatherer, opts HandlerOpts) http.Handler { return HandlerForTransactional(prometheus.ToTransactionalGatherer(reg), opts) } @@ -112,7 +229,15 @@ func HandlerFor(reg prometheus.Gatherer, opts HandlerOpts) http.Handler { // HandlerForTransactional is like HandlerFor, but it uses transactional gather, which // can safely change in-place returned *dto.MetricFamily before call to `Gather` and after // call to `done` of that `Gather`. +// +// The handler supports filtering metrics by name using the `name[]` query parameter. +// Multiple metric names can be specified by providing the parameter multiple times. +// When no name[] parameters are provided, all metrics are returned. func HandlerForTransactional(reg prometheus.TransactionalGatherer, opts HandlerOpts) http.Handler { + if opts.CoalesceGather { + reg = &coalescingGatherer{g: reg} + } + var ( inFlightSem chan struct{} errCnt = prometheus.NewCounterVec( @@ -214,12 +339,14 @@ func HandlerForTransactional(reg prometheus.TransactionalGatherer, opts HandlerO rsp.Header().Set(contentEncodingHeader, encodingHeader) } - var enc expfmt.Encoder + var ( + enc expfmt.Encoder + encOpts []expfmt.EncoderOption + ) if opts.EnableOpenMetricsTextCreatedSamples { - enc = expfmt.NewEncoder(w, contentType, expfmt.WithCreatedLines()) - } else { - enc = expfmt.NewEncoder(w, contentType) + encOpts = append(encOpts, expfmt.WithCreatedLines()) } + enc = expfmt.NewEncoder(w, contentType, encOpts...) // handleError handles the error according to opts.ErrorHandling // and returns true if we have to abort after the handling. @@ -245,7 +372,24 @@ func HandlerForTransactional(reg prometheus.TransactionalGatherer, opts HandlerO return false } + // Build metric name filter set from query params (if any). The URL + // can be nil on hand-constructed requests. + var metricFilter map[string]struct{} + if req.URL != nil { + if metricNames := req.URL.Query()["name[]"]; len(metricNames) > 0 { + metricFilter = make(map[string]struct{}, len(metricNames)) + for _, name := range metricNames { + metricFilter[name] = struct{}{} + } + } + } + for _, mf := range mfs { + if metricFilter != nil { + if _, ok := metricFilter[mf.GetName()]; !ok { + continue + } + } if handleError(enc.Encode(mf)) { return } @@ -353,7 +497,7 @@ const ( // log.Logger from the standard library implements this interface, and it is // easy to implement by custom loggers, if they don't do so already anyway. type Logger interface { - Println(v ...interface{}) + Println(v ...any) } // HandlerOpts specifies options how to serve metrics via an http.Handler. The @@ -400,6 +544,40 @@ type HandlerOpts struct { // Service Unavailable and a suitable message in the body. If // MaxRequestsInFlight is 0 or negative, no limit is applied. MaxRequestsInFlight int + // CoalesceGather, if true, deduplicates concurrent Gather calls so that + // only one collection runs at a time. Additional requests that arrive + // while a Gather is in flight will receive the same result once it + // completes. This prevents goroutine pile-up when the scrape rate is + // faster than the time collectors need to produce metrics. + // + // When enabled, concurrent scrapers share a single metric snapshot per + // collection cycle. Each request receives its own copy of the returned + // slice, so filtering or reordering it (for example via name[] query + // parameters) is safe. The pointed-to MetricFamily values are still + // shared: the built-in handler only reads them, so this is safe in + // practice, but a custom TransactionalGatherer that mutates the returned + // families in place after Gather returns must not use this option. + // + // Because the snapshot is shared, a request that arrives while a cycle is + // in flight receives that cycle's result even though collection began + // before the request; two scrapers joined to one cycle observe the same + // timestamps rather than independently gathered data. + // + // Consider using CoalesceGather together with Timeout. Timeout bounds the + // client-facing response time and keeps at most one collection running at + // a time, but it does not cancel the underlying Gather: a joined request + // that times out still holds a MaxRequestsInFlight slot until the shared + // collection completes. + // + // Panic handling: a panicking Collector is already turned into an error by + // the registry, so joiners receive that error like any other. In the rare + // case where the wrapped gatherer itself panics, the panicking request's + // panic propagates as usual (handled by net/http), while requests that + // joined the same cycle receive an error rather than an empty response. + // + // NOTE: This option is experimental and may change or be removed in a + // future release. + CoalesceGather bool // If handling a request takes longer than Timeout, it is responded to // with 503 ServiceUnavailable and a suitable Message. No timeout is // applied if Timeout is 0 or negative. Note that with the current @@ -407,8 +585,9 @@ type HandlerOpts struct { // described above (and even that only if sending of the body hasn't // started yet), while the bulk work of gathering all the metrics keeps // running in the background (with the eventual result to be thrown - // away). Until the implementation is improved, it is recommended to - // implement a separate timeout in potentially slow Collectors. + // away). When CoalesceGather is enabled, only one such background Gather + // can be in flight at a time. It is also recommended to implement a + // separate timeout in potentially slow Collectors. Timeout time.Duration // If true, the experimental OpenMetrics encoding is added to the // possible options during content negotiation. Note that Prometheus @@ -460,7 +639,7 @@ func httpError(rsp http.ResponseWriter, err error) { // negotiateEncodingWriter reads the Accept-Encoding header from a request and // selects the right compression based on an allow-list of supported -// compressions. It returns a writer implementing the compression and an the +// compressions. It returns a writer implementing the compression and the // correct value that the caller can set in the response header. func negotiateEncodingWriter(r *http.Request, rw io.Writer, compressions []string) (_ io.Writer, encodingHeaderValue string, closeWriter func(), _ error) { if len(compressions) == 0 { diff --git a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client.go b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client.go index d3482c40ca..0248579742 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_client.go @@ -75,10 +75,10 @@ func InstrumentRoundTripperCounter(counter *prometheus.CounterVec, next http.Rou resp, err := next.RoundTrip(r) if err == nil { l := labels(code, method, r.Method, resp.StatusCode, rtOpts.extraMethods...) - for label, resolve := range rtOpts.extraLabelsFromCtx { - l[label] = resolve(resp.Request.Context()) + for label, resolve := range rtOpts.extraLabelsFromRequest { + l[label] = resolve(resp.Request) } - addWithExemplar(counter.With(l), 1, rtOpts.getExemplarFn(r.Context())) + addWithExemplar(counter.With(l), 1, rtOpts.getExemplarFn(r)) } return resp, err } @@ -119,10 +119,10 @@ func InstrumentRoundTripperDuration(obs prometheus.ObserverVec, next http.RoundT resp, err := next.RoundTrip(r) if err == nil { l := labels(code, method, r.Method, resp.StatusCode, rtOpts.extraMethods...) - for label, resolve := range rtOpts.extraLabelsFromCtx { - l[label] = resolve(resp.Request.Context()) + for label, resolve := range rtOpts.extraLabelsFromRequest { + l[label] = resolve(resp.Request) } - observeWithExemplar(obs.With(l), time.Since(start).Seconds(), rtOpts.getExemplarFn(r.Context())) + observeWithExemplar(obs.With(l), time.Since(start).Seconds(), rtOpts.getExemplarFn(r)) } return resp, err } diff --git a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_server.go b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_server.go index 9332b0249a..9dec091ac7 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_server.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/instrument_server.go @@ -28,24 +28,36 @@ import ( // magicString is used for the hacky label test in checkLabels. Remove once fixed. const magicString = "zZgWfBxLqvG8kc8IMv3POi2Bb0tZI3vAnBx+gBaFi9FyPzB/CzKUer1yufDa" -// observeWithExemplar is a wrapper for [prometheus.ExemplarAdder.ExemplarObserver], -// which falls back to [prometheus.Observer.Observe] if no labels are provided. +// observeWithExemplar records val on obs. If labels is non-nil and obs +// implements [prometheus.ExemplarObserver], the exemplar is attached via +// ObserveWithExemplar; otherwise the exemplar is dropped and the value is +// recorded with a plain [prometheus.Observer.Observe]. This mirrors the +// safe-cast pattern in [prometheus.Timer.ObserveDurationWithExemplar] and +// ensures we never panic when callers pass an ObserverVec backed by a +// summary, which cannot carry exemplars in the Prometheus exposition format. func observeWithExemplar(obs prometheus.Observer, val float64, labels map[string]string) { - if labels == nil { - obs.Observe(val) - return + if labels != nil { + if eo, ok := obs.(prometheus.ExemplarObserver); ok { + eo.ObserveWithExemplar(val, labels) + return + } } - obs.(prometheus.ExemplarObserver).ObserveWithExemplar(val, labels) + obs.Observe(val) } -// addWithExemplar is a wrapper for [prometheus.ExemplarAdder.AddWithExemplar], -// which falls back to [prometheus.Counter.Add] if no labels are provided. -func addWithExemplar(obs prometheus.Counter, val float64, labels map[string]string) { - if labels == nil { - obs.Add(val) - return +// addWithExemplar records val on c. If labels is non-nil and c implements +// [prometheus.ExemplarAdder], the exemplar is attached via AddWithExemplar; +// otherwise the exemplar is dropped and the value is recorded with a plain +// [prometheus.Counter.Add]. The safe-cast keeps the helper robust against +// custom Counter implementations that do not advertise exemplar support. +func addWithExemplar(c prometheus.Counter, val float64, labels map[string]string) { + if labels != nil { + if ea, ok := c.(prometheus.ExemplarAdder); ok { + ea.AddWithExemplar(val, labels) + return + } } - obs.(prometheus.ExemplarAdder).AddWithExemplar(val, labels) + c.Add(val) } // InstrumentHandlerInFlight is a middleware that wraps the provided @@ -97,10 +109,10 @@ func InstrumentHandlerDuration(obs prometheus.ObserverVec, next http.Handler, op next.ServeHTTP(d, r) l := labels(code, method, r.Method, d.Status(), hOpts.extraMethods...) - for label, resolve := range hOpts.extraLabelsFromCtx { - l[label] = resolve(r.Context()) + for label, resolve := range hOpts.extraLabelsFromRequest { + l[label] = resolve(r) } - observeWithExemplar(obs.With(l), time.Since(now).Seconds(), hOpts.getExemplarFn(r.Context())) + observeWithExemplar(obs.With(l), time.Since(now).Seconds(), hOpts.getExemplarFn(r)) } } @@ -108,10 +120,10 @@ func InstrumentHandlerDuration(obs prometheus.ObserverVec, next http.Handler, op now := time.Now() next.ServeHTTP(w, r) l := labels(code, method, r.Method, 0, hOpts.extraMethods...) - for label, resolve := range hOpts.extraLabelsFromCtx { - l[label] = resolve(r.Context()) + for label, resolve := range hOpts.extraLabelsFromRequest { + l[label] = resolve(r) } - observeWithExemplar(obs.With(l), time.Since(now).Seconds(), hOpts.getExemplarFn(r.Context())) + observeWithExemplar(obs.With(l), time.Since(now).Seconds(), hOpts.getExemplarFn(r)) } } @@ -147,10 +159,10 @@ func InstrumentHandlerCounter(counter *prometheus.CounterVec, next http.Handler, next.ServeHTTP(d, r) l := labels(code, method, r.Method, d.Status(), hOpts.extraMethods...) - for label, resolve := range hOpts.extraLabelsFromCtx { - l[label] = resolve(r.Context()) + for label, resolve := range hOpts.extraLabelsFromRequest { + l[label] = resolve(r) } - addWithExemplar(counter.With(l), 1, hOpts.getExemplarFn(r.Context())) + addWithExemplar(counter.With(l), 1, hOpts.getExemplarFn(r)) } } @@ -158,10 +170,10 @@ func InstrumentHandlerCounter(counter *prometheus.CounterVec, next http.Handler, next.ServeHTTP(w, r) l := labels(code, method, r.Method, 0, hOpts.extraMethods...) - for label, resolve := range hOpts.extraLabelsFromCtx { - l[label] = resolve(r.Context()) + for label, resolve := range hOpts.extraLabelsFromRequest { + l[label] = resolve(r) } - addWithExemplar(counter.With(l), 1, hOpts.getExemplarFn(r.Context())) + addWithExemplar(counter.With(l), 1, hOpts.getExemplarFn(r)) } } @@ -200,10 +212,10 @@ func InstrumentHandlerTimeToWriteHeader(obs prometheus.ObserverVec, next http.Ha now := time.Now() d := newDelegator(w, func(status int) { l := labels(code, method, r.Method, status, hOpts.extraMethods...) - for label, resolve := range hOpts.extraLabelsFromCtx { - l[label] = resolve(r.Context()) + for label, resolve := range hOpts.extraLabelsFromRequest { + l[label] = resolve(r) } - observeWithExemplar(obs.With(l), time.Since(now).Seconds(), hOpts.getExemplarFn(r.Context())) + observeWithExemplar(obs.With(l), time.Since(now).Seconds(), hOpts.getExemplarFn(r)) }) next.ServeHTTP(d, r) } @@ -244,10 +256,10 @@ func InstrumentHandlerRequestSize(obs prometheus.ObserverVec, next http.Handler, size := computeApproximateRequestSize(r) l := labels(code, method, r.Method, d.Status(), hOpts.extraMethods...) - for label, resolve := range hOpts.extraLabelsFromCtx { - l[label] = resolve(r.Context()) + for label, resolve := range hOpts.extraLabelsFromRequest { + l[label] = resolve(r) } - observeWithExemplar(obs.With(l), float64(size), hOpts.getExemplarFn(r.Context())) + observeWithExemplar(obs.With(l), float64(size), hOpts.getExemplarFn(r)) } } @@ -256,10 +268,10 @@ func InstrumentHandlerRequestSize(obs prometheus.ObserverVec, next http.Handler, size := computeApproximateRequestSize(r) l := labels(code, method, r.Method, 0, hOpts.extraMethods...) - for label, resolve := range hOpts.extraLabelsFromCtx { - l[label] = resolve(r.Context()) + for label, resolve := range hOpts.extraLabelsFromRequest { + l[label] = resolve(r) } - observeWithExemplar(obs.With(l), float64(size), hOpts.getExemplarFn(r.Context())) + observeWithExemplar(obs.With(l), float64(size), hOpts.getExemplarFn(r)) } } @@ -296,10 +308,10 @@ func InstrumentHandlerResponseSize(obs prometheus.ObserverVec, next http.Handler next.ServeHTTP(d, r) l := labels(code, method, r.Method, d.Status(), hOpts.extraMethods...) - for label, resolve := range hOpts.extraLabelsFromCtx { - l[label] = resolve(r.Context()) + for label, resolve := range hOpts.extraLabelsFromRequest { + l[label] = resolve(r) } - observeWithExemplar(obs.With(l), float64(d.Written()), hOpts.getExemplarFn(r.Context())) + observeWithExemplar(obs.With(l), float64(d.Written()), hOpts.getExemplarFn(r)) }) } @@ -366,7 +378,7 @@ func checkLabels(c prometheus.Collector) (code, method bool) { panic("metric partitioned with non-supported labels") } } - return + return code, method } func isLabelCurried(c prometheus.Collector, label string) bool { diff --git a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/option.go b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/option.go index 5d4383aa14..d4c0954f36 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/option.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/option.go @@ -15,6 +15,7 @@ package promhttp import ( "context" + "net/http" "github.com/prometheus/client_golang/prometheus" ) @@ -24,28 +25,31 @@ type Option interface { apply(*options) } +// LabelValueFromRequest is used to compute the label value from request. +type LabelValueFromRequest func(request *http.Request) string + // LabelValueFromCtx are used to compute the label value from request context. // Context can be filled with values from request through middleware. type LabelValueFromCtx func(ctx context.Context) string // options store options for both a handler or round tripper. type options struct { - extraMethods []string - getExemplarFn func(requestCtx context.Context) prometheus.Labels - extraLabelsFromCtx map[string]LabelValueFromCtx + extraMethods []string + getExemplarFn func(req *http.Request) prometheus.Labels + extraLabelsFromRequest map[string]LabelValueFromRequest } func defaultOptions() *options { return &options{ - getExemplarFn: func(ctx context.Context) prometheus.Labels { return nil }, - extraLabelsFromCtx: map[string]LabelValueFromCtx{}, + getExemplarFn: func(req *http.Request) prometheus.Labels { return nil }, + extraLabelsFromRequest: map[string]LabelValueFromRequest{}, } } func (o *options) emptyDynamicLabels() prometheus.Labels { labels := prometheus.Labels{} - for label := range o.extraLabelsFromCtx { + for label := range o.extraLabelsFromRequest { labels[label] = "" } @@ -66,19 +70,39 @@ func WithExtraMethods(methods ...string) Option { }) } -// WithExemplarFromContext allows to inject function that will get exemplar from context that will be put to counter and histogram metrics. +// WithExemplarFromRequest allows you to inject a function that will get exemplar from request that will be put to counter and histogram metrics. // If the function returns nil labels or the metric does not support exemplars, no exemplar will be added (noop), but // metric will continue to observe/increment. -func WithExemplarFromContext(getExemplarFn func(requestCtx context.Context) prometheus.Labels) Option { +func WithExemplarFromRequest(getExemplarFn func(req *http.Request) prometheus.Labels) Option { return optionApplyFunc(func(o *options) { o.getExemplarFn = getExemplarFn }) } +// WithExemplarFromContext allows you to inject a function that will get exemplar from context that will be put to counter and histogram metrics. +// If the function returns nil labels or the metric does not support exemplars, no exemplar will be added (noop), but +// metric will continue to observe/increment. +func WithExemplarFromContext(getExemplarFn func(requestCtx context.Context) prometheus.Labels) Option { + return optionApplyFunc(func(o *options) { + o.getExemplarFn = func(req *http.Request) prometheus.Labels { + return getExemplarFn(req.Context()) + } + }) +} + +// WithLabelFromRequest registers a label for dynamic resolution with access to the request. +func WithLabelFromRequest(name string, valueFn LabelValueFromRequest) Option { + return optionApplyFunc(func(o *options) { + o.extraLabelsFromRequest[name] = valueFn + }) +} + // WithLabelFromCtx registers a label for dynamic resolution with access to context. // See the example for ExampleInstrumentHandlerWithLabelResolver for example usage func WithLabelFromCtx(name string, valueFn LabelValueFromCtx) Option { return optionApplyFunc(func(o *options) { - o.extraLabelsFromCtx[name] = valueFn + o.extraLabelsFromRequest[name] = func(req *http.Request) string { + return valueFn(req.Context()) + } }) } diff --git a/vendor/github.com/prometheus/client_golang/prometheus/registry.go b/vendor/github.com/prometheus/client_golang/prometheus/registry.go index c6fd2f58b7..ed0681c8b4 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/registry.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/registry.go @@ -214,6 +214,19 @@ func (err AlreadyRegisteredError) Error() string { // by a Gatherer to report multiple errors during MetricFamily gathering. type MultiError []error +// SafeMultiError is a thread-safe wrapper around MultiError using a mutex. +type SafeMultiError struct { + mu sync.Mutex + errs MultiError +} + +// Appends the provided error to the contained MultiError in a thread-safe way. +func (s *SafeMultiError) Append(err error) { + s.mu.Lock() + s.errs.Append(err) + s.mu.Unlock() +} + // Error formats the contained errors as a bullet point list, preceded by the // total number of errors. Note that this results in a multi-line string. func (errs MultiError) Error() string { @@ -408,6 +421,16 @@ func (r *Registry) MustRegister(cs ...Collector) { } } +// MustGather implements Gatherer. +// Wraps around Gather and panics if Gather fails for any reason. +func (r *Registry) MustGather() []*dto.MetricFamily { + mfs, err := r.Gather() + if err != nil { + panic(err) + } + return mfs +} + // Gather implements Gatherer. func (r *Registry) Gather() ([]*dto.MetricFamily, error) { r.mtx.RLock() @@ -423,7 +446,7 @@ func (r *Registry) Gather() ([]*dto.MetricFamily, error) { uncheckedMetricChan = make(chan Metric, capMetricChan) metricHashes = map[uint64]struct{}{} wg sync.WaitGroup - errs MultiError // The collected errors to return in the end. + safeErrs = &SafeMultiError{} // To collect errors in a threadsafe way registeredDescIDs map[uint64]struct{} // Only used for pedantic checks ) @@ -453,9 +476,9 @@ func (r *Registry) Gather() ([]*dto.MetricFamily, error) { for { select { case collector := <-checkedCollectors: - collector.Collect(checkedMetricChan) + safeErrs.Append((safeCollect(collector, checkedMetricChan))) case collector := <-uncheckedCollectors: - collector.Collect(uncheckedMetricChan) + safeErrs.Append(safeCollect(collector, uncheckedMetricChan)) default: return } @@ -499,7 +522,7 @@ func (r *Registry) Gather() ([]*dto.MetricFamily, error) { cmc = nil break } - errs.Append(processMetric( + safeErrs.Append(processMetric( metric, metricFamiliesByName, metricHashes, registeredDescIDs, @@ -509,7 +532,7 @@ func (r *Registry) Gather() ([]*dto.MetricFamily, error) { umc = nil break } - errs.Append(processMetric( + safeErrs.Append(processMetric( metric, metricFamiliesByName, metricHashes, nil, @@ -526,7 +549,7 @@ func (r *Registry) Gather() ([]*dto.MetricFamily, error) { cmc = nil break } - errs.Append(processMetric( + safeErrs.Append(processMetric( metric, metricFamiliesByName, metricHashes, registeredDescIDs, @@ -536,7 +559,7 @@ func (r *Registry) Gather() ([]*dto.MetricFamily, error) { umc = nil break } - errs.Append(processMetric( + safeErrs.Append(processMetric( metric, metricFamiliesByName, metricHashes, nil, @@ -556,7 +579,8 @@ func (r *Registry) Gather() ([]*dto.MetricFamily, error) { break } } - return internal.NormalizeMetricFamilies(metricFamiliesByName), errs.MaybeUnwrap() + + return internal.NormalizeMetricFamilies(metricFamiliesByName), safeErrs.errs.MaybeUnwrap() } // Describe implements Collector. @@ -571,6 +595,24 @@ func (r *Registry) Describe(ch chan<- *Desc) { } } +// Helper wrapper around Collector.Collect. +// It tries to collect from the channel, recovers on panic and +// if it has recovered from a panic, then it sends an InvalidMetric into +// the channel with an InvalidDesc, and an error that includes a stack trace. +func safeCollect(c Collector, ch chan<- Metric) (err error) { + defer func() { + if r := recover(); r != nil { + buf := make([]byte, 64<<10) // 64 KB + n := runtime.Stack(buf, false) + err = fmt.Errorf("prometheus collector panic recovered: type=%T: error=%v\nstack trace=%s", c, r, buf[:n]) + ch <- NewInvalidMetric(NewInvalidDesc(err), err) + } + }() + c.Collect(ch) + + return err +} + // Collect implements Collector. func (r *Registry) Collect(ch chan<- Metric) { r.mtx.RLock() @@ -599,10 +641,12 @@ func WriteToTextfile(filename string, g Gatherer) error { mfs, err := g.Gather() if err != nil { + tmp.Close() return err } for _, mf := range mfs { if _, err := expfmt.MetricFamilyToText(tmp, mf); err != nil { + tmp.Close() return err } } @@ -685,6 +729,9 @@ func processMetric( metricFamily = &dto.MetricFamily{} metricFamily.Name = proto.String(desc.fqName) metricFamily.Help = proto.String(desc.help) + if desc.unit != "" { + metricFamily.Unit = proto.String(desc.unit) + } // TODO(beorn7): Simplify switch once Desc has type. switch { case dtoMetric.Gauge != nil: diff --git a/vendor/github.com/prometheus/client_golang/prometheus/summary.go b/vendor/github.com/prometheus/client_golang/prometheus/summary.go index ac5203c6fa..c12b8d13d4 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/summary.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/summary.go @@ -101,6 +101,9 @@ type SummaryOpts struct { // string. Help string + // Unit provides the unit of this Summary. + Unit string + // ConstLabels are used to attach fixed labels to this metric. Metrics // with the same fully-qualified name must have the same label names in // their ConstLabels. @@ -181,11 +184,12 @@ type SummaryVecOpts struct { // NewSummary creates a new Summary based on the provided SummaryOpts. func NewSummary(opts SummaryOpts) Summary { return newSummary( - NewDesc( + V2.NewDesc( BuildFQName(opts.Namespace, opts.Subsystem, opts.Name), opts.Help, - nil, + UnconstrainedLabels(nil), opts.ConstLabels, + WithUnit(opts.Unit), ), opts, ) @@ -578,6 +582,7 @@ func (v2) NewSummaryVec(opts SummaryVecOpts) *SummaryVec { opts.Help, opts.VariableLabels, opts.ConstLabels, + WithUnit(opts.Unit), ) return &SummaryVec{ MetricVec: NewMetricVec(desc, func(lvs ...string) Metric { diff --git a/vendor/github.com/prometheus/client_golang/prometheus/timer.go b/vendor/github.com/prometheus/client_golang/prometheus/timer.go index 52344fef53..c1318ffb51 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/timer.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/timer.go @@ -37,10 +37,10 @@ type Timer struct { // or // // func TimeMeWithExemplar() { -// timer := NewTimer(myHistogram) -// defer timer.ObserveDurationWithExemplar(exemplar) -// // Do actual work. -// } +// timer := NewTimer(myHistogram) +// defer timer.ObserveDurationWithExemplar(exemplar) +// // Do actual work. +// } func NewTimer(o Observer) *Timer { return &Timer{ begin: time.Now(), @@ -66,7 +66,7 @@ func (t *Timer) ObserveDuration() time.Duration { // ObserveDurationWithExemplar is like ObserveDuration, but it will also // observe exemplar with the duration unless exemplar is nil or provided Observer can't -// be casted to ExemplarObserver. +// be cast to ExemplarObserver. func (t *Timer) ObserveDurationWithExemplar(exemplar Labels) time.Duration { d := time.Since(t.begin) eo, ok := t.observer.(ExemplarObserver) diff --git a/vendor/github.com/prometheus/client_golang/prometheus/vec.go b/vendor/github.com/prometheus/client_golang/prometheus/vec.go index 487b466563..121d2a9639 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/vec.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/vec.go @@ -193,9 +193,11 @@ func (m *MetricVec) CurryWith(labels Labels) (*MetricVec, error) { // // Keeping the Metric for later use is possible (and should be considered if // performance is critical), but keep in mind that Reset, DeleteLabelValues and -// Delete can be used to delete the Metric from the MetricVec. In that case, the -// Metric will still exist, but it will not be exported anymore, even if a -// Metric with the same label values is created later. +// Delete can be used to delete the Metric from the MetricVec. In that case, if +// you have previously kept a reference to that Metric, the Metric object still +// exists and can be used, but it will not be exported anymore. If a Metric with +// the same label values is created later, updates to the old Metric reference +// will not be exported. // // An error is returned if the number of label values is not the same as the // number of variable labels in Desc (minus any curried labels). @@ -657,7 +659,7 @@ func inlineLabelValues(lvs []string, curry []curriedLabelValue) []string { } var labelsPool = &sync.Pool{ - New: func() interface{} { + New: func() any { return make(Labels) }, } diff --git a/vendor/github.com/prometheus/client_golang/prometheus/wrap.go b/vendor/github.com/prometheus/client_golang/prometheus/wrap.go index 2ed1285068..697f55558b 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/wrap.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/wrap.go @@ -230,6 +230,7 @@ func wrapDesc(desc *Desc, prefix string, labels Labels) *Desc { return &Desc{ fqName: desc.fqName, help: desc.help, + unit: desc.unit, variableLabels: desc.variableLabels, constLabelPairs: desc.constLabelPairs, err: fmt.Errorf("attempted wrapping with already existing label name %q", ln), @@ -238,8 +239,8 @@ func wrapDesc(desc *Desc, prefix string, labels Labels) *Desc { constLabels[ln] = lv } // NewDesc will do remaining validations. - newDesc := V2.NewDesc(prefix+desc.fqName, desc.help, desc.variableLabels, constLabels) - // Propagate errors if there was any. This will override any errer + newDesc := V2.NewDesc(prefix+desc.fqName, desc.help, desc.variableLabels, constLabels, WithUnit(desc.unit)) + // Propagate errors if there was any. This will override any error // created by NewDesc above, i.e. earlier errors get precedence. if desc.err != nil { newDesc.err = desc.err diff --git a/vendor/github.com/prometheus/common/config/config.go b/vendor/github.com/prometheus/common/config/config.go index ff54cdd82c..d0040763ec 100644 --- a/vendor/github.com/prometheus/common/config/config.go +++ b/vendor/github.com/prometheus/common/config/config.go @@ -33,7 +33,7 @@ type Secret string var MarshalSecretValue = false // MarshalYAML implements the yaml.Marshaler interface for Secrets. -func (s Secret) MarshalYAML() (interface{}, error) { +func (s Secret) MarshalYAML() (any, error) { if MarshalSecretValue { return string(s), nil } @@ -44,7 +44,7 @@ func (s Secret) MarshalYAML() (interface{}, error) { } // UnmarshalYAML implements the yaml.Unmarshaler interface for Secrets. -func (s *Secret) UnmarshalYAML(unmarshal func(interface{}) error) error { +func (s *Secret) UnmarshalYAML(unmarshal func(any) error) error { type plain Secret return unmarshal((*plain)(s)) } diff --git a/vendor/github.com/prometheus/common/config/http_config.go b/vendor/github.com/prometheus/common/config/http_config.go index 55cc5b0770..d633479c69 100644 --- a/vendor/github.com/prometheus/common/config/http_config.go +++ b/vendor/github.com/prometheus/common/config/http_config.go @@ -22,6 +22,7 @@ import ( "encoding/json" "errors" "fmt" + "maps" "net" "net/http" "net/url" @@ -76,7 +77,7 @@ var TLSVersions = map[string]TLSVersion{ "TLS10": (TLSVersion)(tls.VersionTLS10), } -func (tv *TLSVersion) UnmarshalYAML(unmarshal func(interface{}) error) error { +func (tv *TLSVersion) UnmarshalYAML(unmarshal func(any) error) error { var s string err := unmarshal(&s) if err != nil { @@ -89,7 +90,7 @@ func (tv *TLSVersion) UnmarshalYAML(unmarshal func(interface{}) error) error { return fmt.Errorf("unknown TLS version: %s", s) } -func (tv TLSVersion) MarshalYAML() (interface{}, error) { +func (tv TLSVersion) MarshalYAML() (any, error) { for s, v := range TLSVersions { if tv == v { return s, nil @@ -131,7 +132,7 @@ func (tv *TLSVersion) String() string { return s } } - return fmt.Sprintf("%d", tv) + return fmt.Sprintf("%d", *tv) } // BasicAuth contains basic HTTP authentication credentials. @@ -178,7 +179,7 @@ type URL struct { } // UnmarshalYAML implements the yaml.Unmarshaler interface for URLs. -func (u *URL) UnmarshalYAML(unmarshal func(interface{}) error) error { +func (u *URL) UnmarshalYAML(unmarshal func(any) error) error { var s string if err := unmarshal(&s); err != nil { return err @@ -193,7 +194,7 @@ func (u *URL) UnmarshalYAML(unmarshal func(interface{}) error) error { } // MarshalYAML implements the yaml.Marshaler interface for URLs. -func (u URL) MarshalYAML() (interface{}, error) { +func (u URL) MarshalYAML() (any, error) { if u.URL != nil { return u.Redacted(), nil } @@ -269,16 +270,16 @@ type OAuth2 struct { Audience string `yaml:"audience,omitempty" json:"audience,omitempty"` // Claims is a map of claims to be added to the JWT token. Only used if // GrantType is set to "urn:ietf:params:oauth:grant-type:jwt-bearer". - Claims map[string]interface{} `yaml:"claims,omitempty" json:"claims,omitempty"` - Scopes []string `yaml:"scopes,omitempty" json:"scopes,omitempty"` - TokenURL string `yaml:"token_url,omitempty" json:"token_url,omitempty"` - EndpointParams map[string]string `yaml:"endpoint_params,omitempty" json:"endpoint_params,omitempty"` - TLSConfig TLSConfig `yaml:"tls_config,omitempty"` + Claims map[string]any `yaml:"claims,omitempty" json:"claims,omitempty"` + Scopes []string `yaml:"scopes,omitempty" json:"scopes,omitempty"` + TokenURL string `yaml:"token_url,omitempty" json:"token_url,omitempty"` + EndpointParams map[string]string `yaml:"endpoint_params,omitempty" json:"endpoint_params,omitempty"` + TLSConfig TLSConfig `yaml:"tls_config,omitempty"` ProxyConfig `yaml:",inline"` } // UnmarshalYAML implements the yaml.Unmarshaler interface. -func (o *OAuth2) UnmarshalYAML(unmarshal func(interface{}) error) error { +func (o *OAuth2) UnmarshalYAML(unmarshal func(any) error) error { type plain OAuth2 if err := unmarshal((*plain)(o)); err != nil { return err @@ -324,7 +325,7 @@ func LoadHTTPConfigFile(filename string) (*HTTPClientConfig, []byte, error) { if err != nil { return nil, nil, err } - cfg.SetDirectory(filepath.Dir(filepath.Dir(filename))) + cfg.SetDirectory(filepath.Dir(filename)) return cfg, content, nil } @@ -463,7 +464,7 @@ func (c *HTTPClientConfig) Validate() error { } // UnmarshalYAML implements the yaml.Unmarshaler interface. -func (c *HTTPClientConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { +func (c *HTTPClientConfig) UnmarshalYAML(unmarshal func(any) error) error { type plain HTTPClientConfig *c = DefaultHTTPClientConfig if err := unmarshal((*plain)(c)); err != nil { @@ -483,7 +484,7 @@ func (c *HTTPClientConfig) UnmarshalJSON(data []byte) error { } // UnmarshalYAML implements the yaml.Unmarshaler interface. -func (a *BasicAuth) UnmarshalYAML(unmarshal func(interface{}) error) error { +func (a *BasicAuth) UnmarshalYAML(unmarshal func(any) error) error { type plain BasicAuth return unmarshal((*plain)(a)) } @@ -717,10 +718,18 @@ func NewRoundTripperFromConfigWithContext(ctx context.Context, cfg HTTPClientCon return nil, fmt.Errorf("unable to use client secret: %w", err) } } - rt = NewOAuth2RoundTripper(oauthCredential, cfg.OAuth2, rt, &opts) + rt = NewOAuth2RoundTripper(oauthCredential, cfg.OAuth2, rt, optFuncs...) } if cfg.HTTPHeaders != nil { + // Strip sensitive headers added by headersRoundTripper on cross-host + // redirects before they reach the transport. Only needed when + // redirects are actually followed; when FollowRedirects is false + // CheckRedirect returns ErrUseLastResponse immediately so there are + // no subsequent requests. + if cfg.FollowRedirects { + rt = &sensitiveHeadersStripRT{next: rt} + } rt = NewHeadersRoundTripper(cfg.HTTPHeaders, rt) } @@ -862,7 +871,7 @@ func NewAuthorizationCredentialsRoundTripper(authType string, authCredentials Se } func (rt *authorizationCredentialsRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { - if len(req.Header.Get("Authorization")) != 0 { + if len(req.Header.Get("Authorization")) != 0 || isCrossHostRedirect(req) { return rt.rt.RoundTrip(req) } @@ -900,7 +909,7 @@ func NewBasicAuthRoundTripper(username, password SecretReader, rt http.RoundTrip } func (rt *basicAuthRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { - if len(req.Header.Get("Authorization")) != 0 { + if len(req.Header.Get("Authorization")) != 0 || isCrossHostRedirect(req) { return rt.rt.RoundTrip(req) } var username string @@ -942,16 +951,26 @@ type oauth2RoundTripper struct { client *http.Client } -func NewOAuth2RoundTripper(oauthCredential SecretReader, config *OAuth2, next http.RoundTripper, opts *httpClientOptions) http.RoundTripper { +// NewOAuth2RoundTripper returns a round tripper that performs OAuth2 +// authentication. The opts variadic parameter accepts any HTTPClientOption +// (e.g. WithDialContextFunc, WithKeepAlivesDisabled) so that callers outside +// this package can fully configure the transport without needing access to the +// unexported *httpClientOptions type. +func NewOAuth2RoundTripper(oauthCredential SecretReader, config *OAuth2, next http.RoundTripper, optFuncs ...HTTPClientOption) http.RoundTripper { if oauthCredential == nil { oauthCredential = NewInlineSecret("") } + opts := defaultHTTPClientOptions + for _, opt := range optFuncs { + opt.applyToHTTPClientOptions(&opts) + } + return &oauth2RoundTripper{ config: config, // A correct tokenSource will be added later on. lastRT: &oauth2.Transport{Base: next}, - opts: opts, + opts: &opts, oauthCredential: oauthCredential, } } @@ -977,6 +996,7 @@ func (rt *oauth2RoundTripper) newOauth2TokenSource(req *http.Request, clientCred IdleConnTimeout: 10 * time.Second, TLSHandshakeTimeout: 10 * time.Second, ExpectContinueTimeout: 1 * time.Second, + DialContext: rt.opts.dialContextFunc, }, nil } @@ -1048,11 +1068,29 @@ func (rt *oauth2RoundTripper) newOauth2TokenSource(req *http.Request, clientCred } func (rt *oauth2RoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { + if isCrossHostRedirect(req) { + // Bypass the OAuth2 transport so no token is attached. Read Base under + // the lock to avoid a data race with concurrent reconfigurations. + rt.mtx.RLock() + base := rt.lastRT.Base + rt.mtx.RUnlock() + if base == nil { + base = http.DefaultTransport + } + return base.RoundTrip(req) + } + var ( secret string needsInit bool ) + // This should not happen when config goes through the normal Prometheus + // validation path, but guard against a nil credential to avoid a panic. + if rt.oauthCredential == nil { + return nil, errors.New("oauth2 client secret is required") + } + rt.mtx.RLock() secret = rt.lastSecret needsInit = rt.lastRT.Source == nil @@ -1106,6 +1144,96 @@ func mapToValues(m map[string]string) url.Values { return v } +// isCrossHostRedirect reports whether req is a redirect that has left the +// original request's host at any point in the chain. It detects this by walking +// the req.Response chain (which Go's HTTP client populates on every redirect +// hop) to find the original request's hostname, then checking every hop in the +// chain against it. +// +// The decision is sticky, mirroring net/http: once any hop leaves the original +// host's domain, credentials and sensitive headers stay stripped for the rest +// of the chain, even if a later hop redirects back to the original host. +// +// This works regardless of whether the caller uses NewClientFromConfig or a +// custom http.Client built from NewRoundTripperFromConfigWithContext directly. +func isCrossHostRedirect(req *http.Request) bool { + if req.Response == nil { + return false + } + originalHost := strings.ToLower(originalRequestHost(req)) + for r := req; r.Response != nil && r.Response.Request != nil; r = r.Response.Request { + if !isDomainOrSubdomain(strings.ToLower(r.URL.Hostname()), originalHost) { + return true + } + } + return false +} + +func originalRequestHost(req *http.Request) string { + r := req + for r.Response != nil && r.Response.Request != nil { + r = r.Response.Request + } + return r.URL.Hostname() +} + +// sensitiveHeadersOnRedirect lists the headers that must not be forwarded when +// following a redirect to a different host. The list matches the one stripped +// by makeHeadersCopier in net/http/client.go. +var sensitiveHeadersOnRedirect = map[string]struct{}{ + "Authorization": {}, + // "Www-Authenticate" is the canonical form produced by + // textproto.CanonicalMIMEHeaderKey; it is not a typo of "WWW-Authenticate". + "Www-Authenticate": {}, + "Cookie": {}, + "Cookie2": {}, + "Proxy-Authorization": {}, + "Proxy-Authenticate": {}, +} + +// sensitiveHeadersStripRT strips sensitive headers from requests marked as +// cross-host redirects before passing them to the underlying transport. +type sensitiveHeadersStripRT struct { + next http.RoundTripper +} + +func (rt *sensitiveHeadersStripRT) RoundTrip(req *http.Request) (*http.Response, error) { + if isCrossHostRedirect(req) { + req = cloneRequest(req) + for h := range sensitiveHeadersOnRedirect { + req.Header.Del(h) + } + } + return rt.next.RoundTrip(req) +} + +func (rt *sensitiveHeadersStripRT) CloseIdleConnections() { + if ci, ok := rt.next.(closeIdler); ok { + ci.CloseIdleConnections() + } +} + +// isDomainOrSubdomain reports whether sub is a subdomain (or exact match) of +// parent. It mirrors isDomainOrSubdomain from net/http/client.go. +func isDomainOrSubdomain(sub, parent string) bool { + if parent == "" { + return false + } + if sub == parent { + return true + } + // A colon means sub is an IPv6 address; a percent sign introduces an IPv6 + // zone ID. Neither can be a hostname, and both could otherwise pass the + // suffix check below (e.g. "::1%.www.example.com" ends with "example.com"). + if strings.ContainsAny(sub, ":%") { + return false + } + if !strings.HasSuffix(sub, parent) { + return false + } + return sub[len(sub)-len(parent)-1] == '.' +} + // cloneRequest returns a clone of the provided *http.Request. // The clone is a shallow copy of the struct and its Header map. func cloneRequest(r *http.Request) *http.Request { @@ -1113,10 +1241,7 @@ func cloneRequest(r *http.Request) *http.Request { r2 := new(http.Request) *r2 = *r // Deep copy of the Header. - r2.Header = make(http.Header) - for k, s := range r.Header { - r2.Header[k] = s - } + maps.Copy(r.Header, r2.Header) return r2 } @@ -1239,7 +1364,7 @@ func (c *TLSConfig) SetDirectory(dir string) { } // UnmarshalYAML implements the yaml.Unmarshaler interface. -func (c *TLSConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { +func (c *TLSConfig) UnmarshalYAML(unmarshal func(any) error) error { type plain TLSConfig if err := unmarshal((*plain)(c)); err != nil { return err @@ -1460,7 +1585,7 @@ func (t *tlsRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { rt := t.rt t.mtx.RUnlock() if equal { - // The CA cert hasn't changed, use the existing RoundTripper. + // The TLS materials (CA, cert, key) haven't changed, use the existing RoundTripper. return rt.RoundTrip(req) } @@ -1468,10 +1593,7 @@ func (t *tlsRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { // The cert and key files are read separately by the client // using GetClientCertificate. tlsConfig := t.tlsConfig.Clone() - if !updateRootCA(tlsConfig, caData) { - if t.settings.CA == nil { - return nil, errors.New("unable to use specified CA cert: none configured") - } + if t.settings.CA != nil && !updateRootCA(tlsConfig, caData) { return nil, fmt.Errorf("unable to use specified CA cert %s", t.settings.CA.Description()) } rt, err = t.newRT(tlsConfig) diff --git a/vendor/github.com/prometheus/common/config/oauth_assertion.go b/vendor/github.com/prometheus/common/config/oauth_assertion.go index bf4bcb949b..ba5ffb4dc4 100644 --- a/vendor/github.com/prometheus/common/config/oauth_assertion.go +++ b/vendor/github.com/prometheus/common/config/oauth_assertion.go @@ -18,6 +18,7 @@ import ( "encoding/json" "fmt" "io" + "maps" "net/http" "net/url" "strings" @@ -133,9 +134,7 @@ func (js jwtSource) Token() (*oauth2.Token, error) { claims["scope"] = scopes } - for k, v := range js.conf.PrivateClaims { - claims[k] = v - } + maps.Copy(claims, js.conf.PrivateClaims) assertion := jwt.NewWithClaims(js.conf.SigningAlgorithm, claims) if js.conf.PrivateKeyID != "" { diff --git a/vendor/github.com/prometheus/common/expfmt/expfmt.go b/vendor/github.com/prometheus/common/expfmt/expfmt.go index 4e4c13e724..10bf35708c 100644 --- a/vendor/github.com/prometheus/common/expfmt/expfmt.go +++ b/vendor/github.com/prometheus/common/expfmt/expfmt.go @@ -122,7 +122,7 @@ func NewOpenMetricsFormat(version string) (Format, error) { // removed. func (f Format) WithEscapingScheme(s model.EscapingScheme) Format { var terms []string - for _, p := range strings.Split(string(f), ";") { + for p := range strings.SplitSeq(string(f), ";") { toks := strings.Split(p, "=") if len(toks) != 2 { trimmed := strings.TrimSpace(p) @@ -194,7 +194,7 @@ func (f Format) FormatType() FormatType { // "escaping" term exists, that will be used. Otherwise, the global default will // be returned. func (f Format) ToEscapingScheme() model.EscapingScheme { - for _, p := range strings.Split(string(f), ";") { + for p := range strings.SplitSeq(string(f), ";") { toks := strings.Split(p, "=") if len(toks) != 2 { continue diff --git a/vendor/github.com/prometheus/common/expfmt/openmetrics_create.go b/vendor/github.com/prometheus/common/expfmt/openmetrics_create.go index 21b93bca36..0480e7af5b 100644 --- a/vendor/github.com/prometheus/common/expfmt/openmetrics_create.go +++ b/vendor/github.com/prometheus/common/expfmt/openmetrics_create.go @@ -30,7 +30,6 @@ import ( type encoderOption struct { withCreatedLines bool - withUnit bool } type EncoderOption func(*encoderOption) @@ -51,17 +50,6 @@ func WithCreatedLines() EncoderOption { } } -// WithUnit is an EncoderOption enabling a set unit to be written to the output -// and to be added to the metric name, if it's not there already, as a suffix. -// Without opting in this way, the unit will not be added to the metric name and, -// on top of that, the unit will not be passed onto the output, even if it -// were declared in the *dto.MetricFamily struct, i.e. even if in.Unit !=nil. -func WithUnit() EncoderOption { - return func(t *encoderOption) { - t.withUnit = true - } -} - // MetricFamilyToOpenMetrics converts a MetricFamily proto message into the // OpenMetrics text format and writes the resulting lines to 'out'. It returns // the number of bytes written and any error encountered. The output will have @@ -99,15 +87,6 @@ func WithUnit() EncoderOption { // its type will be set to `unknown` in that case to avoid invalid OpenMetrics // output. // -// - According to the OM specs, the `# UNIT` line is optional, but if populated, -// the unit has to be present in the metric name as its suffix: -// (see https://github.com/prometheus/OpenMetrics/blob/v1.0.0/specification/OpenMetrics.md#unit). -// However, in order to accommodate any potential scenario where such a change in the -// metric name is not desirable, the users are here given the choice of either explicitly -// opt in, in case they wish for the unit to be included in the output AND in the metric name -// as a suffix (see the description of the WithUnit function above), -// or not to opt in, in case they don't want for any of that to happen. -// // - No support for the following (optional) features: info type, // stateset type, gaugehistogram type. // @@ -151,9 +130,6 @@ func MetricFamilyToOpenMetrics(out io.Writer, in *dto.MetricFamily, options ...E if metricType == dto.MetricType_COUNTER && strings.HasSuffix(compliantName, "_total") { compliantName = name[:len(name)-6] } - if toOM.withUnit && in.Unit != nil && !strings.HasSuffix(compliantName, "_"+*in.Unit) { - compliantName = compliantName + "_" + *in.Unit - } // Comments, first HELP, then TYPE. if in.Help != nil { @@ -217,7 +193,7 @@ func MetricFamilyToOpenMetrics(out io.Writer, in *dto.MetricFamily, options ...E if err != nil { return written, err } - if toOM.withUnit && in.Unit != nil { + if in.Unit != nil { n, err = w.WriteString("# UNIT ") written += n if err != nil { diff --git a/vendor/github.com/prometheus/common/expfmt/text_create.go b/vendor/github.com/prometheus/common/expfmt/text_create.go index 6b89781456..f4074ae9a3 100644 --- a/vendor/github.com/prometheus/common/expfmt/text_create.go +++ b/vendor/github.com/prometheus/common/expfmt/text_create.go @@ -42,12 +42,12 @@ const ( var ( bufPool = sync.Pool{ - New: func() interface{} { + New: func() any { return bufio.NewWriter(io.Discard) }, } numBufPool = sync.Pool{ - New: func() interface{} { + New: func() any { b := make([]byte, 0, initialNumBufSize) return &b }, diff --git a/vendor/github.com/prometheus/common/expfmt/text_parse.go b/vendor/github.com/prometheus/common/expfmt/text_parse.go index 00c8841a10..4ce1f40b81 100644 --- a/vendor/github.com/prometheus/common/expfmt/text_parse.go +++ b/vendor/github.com/prometheus/common/expfmt/text_parse.go @@ -339,6 +339,16 @@ func (p *TextParser) startLabelName() stateFn { return nil // Unexpected end of input. } if p.currentByte == '}' { + if p.currentMF == nil { + // The closing brace was reached before any metric name was read, + // e.g. for the input "{}". There is no metric to attach labels to, + // so this is a malformed exposition. This mirrors the guard in + // startLabelValue. currentMF (not currentMetric) is checked because + // reset only clears currentMF between parses. + p.parseError("invalid metric name") + p.currentLabelPairs = nil + return nil + } p.currentMetric.Label = append(p.currentMetric.Label, p.currentLabelPairs...) p.currentLabelPairs = nil if p.skipBlankTab(); p.err != nil { diff --git a/vendor/github.com/prometheus/common/helpers/templates/time.go b/vendor/github.com/prometheus/common/helpers/templates/time.go index b7dc655f67..d9fcaa0ab7 100644 --- a/vendor/github.com/prometheus/common/helpers/templates/time.go +++ b/vendor/github.com/prometheus/common/helpers/templates/time.go @@ -25,7 +25,7 @@ import ( var errNaNOrInf = errors.New("value is NaN or Inf") -func ConvertToFloat(i interface{}) (float64, error) { +func ConvertToFloat(i any) (float64, error) { switch v := i.(type) { case float64: return v, nil @@ -58,7 +58,7 @@ func FloatToTime(v float64) (*time.Time, error) { return &t, nil } -func HumanizeDuration(i interface{}) (string, error) { +func HumanizeDuration(i any) (string, error) { v, err := ConvertToFloat(i) if err != nil { return "", err @@ -105,7 +105,7 @@ func HumanizeDuration(i interface{}) (string, error) { return fmt.Sprintf("%.4g%ss", v, prefix), nil } -func HumanizeTimestamp(i interface{}) (string, error) { +func HumanizeTimestamp(i any) (string, error) { v, err := ConvertToFloat(i) if err != nil { return "", err diff --git a/vendor/github.com/prometheus/common/model/labels.go b/vendor/github.com/prometheus/common/model/labels.go index dfeb34be5f..29688a13c8 100644 --- a/vendor/github.com/prometheus/common/model/labels.go +++ b/vendor/github.com/prometheus/common/model/labels.go @@ -124,7 +124,7 @@ func (ln LabelName) IsValidLegacy() bool { } // UnmarshalYAML implements the yaml.Unmarshaler interface. -func (ln *LabelName) UnmarshalYAML(unmarshal func(interface{}) error) error { +func (ln *LabelName) UnmarshalYAML(unmarshal func(any) error) error { var s string if err := unmarshal(&s); err != nil { return err diff --git a/vendor/github.com/prometheus/common/model/labelset.go b/vendor/github.com/prometheus/common/model/labelset.go index 9de47b2568..6010b26a88 100644 --- a/vendor/github.com/prometheus/common/model/labelset.go +++ b/vendor/github.com/prometheus/common/model/labelset.go @@ -16,6 +16,7 @@ package model import ( "encoding/json" "fmt" + "maps" "sort" ) @@ -107,9 +108,7 @@ func (ls LabelSet) Before(o LabelSet) bool { // Clone returns a copy of the label set. func (ls LabelSet) Clone() LabelSet { lsn := make(LabelSet, len(ls)) - for ln, lv := range ls { - lsn[ln] = lv - } + maps.Copy(lsn, ls) return lsn } @@ -117,13 +116,9 @@ func (ls LabelSet) Clone() LabelSet { func (ls LabelSet) Merge(other LabelSet) LabelSet { result := make(LabelSet, len(ls)) - for k, v := range ls { - result[k] = v - } + maps.Copy(result, ls) - for k, v := range other { - result[k] = v - } + maps.Copy(result, other) return result } diff --git a/vendor/github.com/prometheus/common/model/metric.go b/vendor/github.com/prometheus/common/model/metric.go index 3feebf328a..2fe461511d 100644 --- a/vendor/github.com/prometheus/common/model/metric.go +++ b/vendor/github.com/prometheus/common/model/metric.go @@ -17,6 +17,7 @@ import ( "encoding/json" "errors" "fmt" + "maps" "regexp" "sort" "strconv" @@ -24,7 +25,6 @@ import ( "unicode/utf8" dto "github.com/prometheus/client_model/go" - "go.yaml.in/yaml/v2" "google.golang.org/protobuf/proto" ) @@ -78,14 +78,6 @@ const ( UTF8Validation ) -var _ interface { - yaml.Marshaler - yaml.Unmarshaler - json.Marshaler - json.Unmarshaler - fmt.Stringer -} = new(ValidationScheme) - // String returns the string representation of s. func (s ValidationScheme) String() string { switch s { @@ -267,9 +259,7 @@ func (m Metric) Before(o Metric) bool { // Clone returns a copy of the Metric. func (m Metric) Clone() Metric { clone := make(Metric, len(m)) - for k, v := range m { - clone[k] = v - } + maps.Copy(clone, m) return clone } diff --git a/vendor/github.com/prometheus/common/model/time.go b/vendor/github.com/prometheus/common/model/time.go index 1730b0fdc1..0854753f4a 100644 --- a/vendor/github.com/prometheus/common/model/time.go +++ b/vendor/github.com/prometheus/common/model/time.go @@ -123,44 +123,38 @@ func (t Time) MarshalJSON() ([]byte, error) { // UnmarshalJSON implements the json.Unmarshaler interface. func (t *Time) UnmarshalJSON(b []byte) error { - p := strings.Split(string(b), ".") - switch len(p) { - case 1: - v, err := strconv.ParseInt(p[0], 10, 64) + base, frac, found := strings.Cut(string(b), ".") + if !found { + v, err := strconv.ParseInt(base, 10, 64) if err != nil { return err } *t = Time(v * second) - - case 2: - v, err := strconv.ParseInt(p[0], 10, 64) + } else { + v, err := strconv.ParseInt(base, 10, 64) if err != nil { return err } - v *= second - prec := dotPrecision - len(p[1]) + prec := dotPrecision - len(frac) if prec < 0 { - p[1] = p[1][:dotPrecision] - } else if prec > 0 { - p[1] += strings.Repeat("0", prec) + frac = frac[:dotPrecision] } - - va, err := strconv.ParseInt(p[1], 10, 32) + va, err := strconv.ParseInt(frac, 10, 32) if err != nil { return err } - - // If the value was something like -0.1 the negative is lost in the - // parsing because of the leading zero, this ensures that we capture it. - if len(p[0]) > 0 && p[0][0] == '-' && v+va > 0 { - *t = Time(v+va) * -1 - } else { - *t = Time(v + va) + switch prec { + case 1: + va *= 10 + case 2: + va *= 100 } - default: - return fmt.Errorf("invalid time %q", string(b)) + if len(base) > 0 && base[0] == '-' { + va = -va + } + *t = Time(v*second + va) } return nil } @@ -340,12 +334,12 @@ func (d *Duration) UnmarshalText(text []byte) error { } // MarshalYAML implements the yaml.Marshaler interface. -func (d Duration) MarshalYAML() (interface{}, error) { +func (d Duration) MarshalYAML() (any, error) { return d.String(), nil } // UnmarshalYAML implements the yaml.Unmarshaler interface. -func (d *Duration) UnmarshalYAML(unmarshal func(interface{}) error) error { +func (d *Duration) UnmarshalYAML(unmarshal func(any) error) error { var s string if err := unmarshal(&s); err != nil { return err diff --git a/vendor/github.com/prometheus/common/model/value.go b/vendor/github.com/prometheus/common/model/value.go index a9995a37ee..8dffd9c4a5 100644 --- a/vendor/github.com/prometheus/common/model/value.go +++ b/vendor/github.com/prometheus/common/model/value.go @@ -259,13 +259,13 @@ func (s Scalar) String() string { // MarshalJSON implements json.Marshaler. func (s Scalar) MarshalJSON() ([]byte, error) { v := strconv.FormatFloat(float64(s.Value), 'f', -1, 64) - return json.Marshal([...]interface{}{s.Timestamp, v}) + return json.Marshal([...]any{s.Timestamp, v}) } // UnmarshalJSON implements json.Unmarshaler. func (s *Scalar) UnmarshalJSON(b []byte) error { var f string - v := [...]interface{}{&s.Timestamp, &f} + v := [...]any{&s.Timestamp, &f} if err := json.Unmarshal(b, &v); err != nil { return err @@ -291,12 +291,12 @@ func (s *String) String() string { // MarshalJSON implements json.Marshaler. func (s String) MarshalJSON() ([]byte, error) { - return json.Marshal([]interface{}{s.Timestamp, s.Value}) + return json.Marshal([]any{s.Timestamp, s.Value}) } // UnmarshalJSON implements json.Unmarshaler. func (s *String) UnmarshalJSON(b []byte) error { - v := [...]interface{}{&s.Timestamp, &s.Value} + v := [...]any{&s.Timestamp, &s.Value} return json.Unmarshal(b, &v) } diff --git a/vendor/github.com/prometheus/common/model/value_float.go b/vendor/github.com/prometheus/common/model/value_float.go index 6bfc757d18..b7d93615e2 100644 --- a/vendor/github.com/prometheus/common/model/value_float.go +++ b/vendor/github.com/prometheus/common/model/value_float.go @@ -79,7 +79,7 @@ func (s SamplePair) MarshalJSON() ([]byte, error) { if err != nil { return nil, err } - return []byte(fmt.Sprintf("[%s,%s]", t, v)), nil + return fmt.Appendf(nil, "[%s,%s]", t, v), nil } // UnmarshalJSON implements json.Unmarshaler. diff --git a/vendor/github.com/prometheus/common/model/value_histogram.go b/vendor/github.com/prometheus/common/model/value_histogram.go index 91ce5b7a45..f27856ccc4 100644 --- a/vendor/github.com/prometheus/common/model/value_histogram.go +++ b/vendor/github.com/prometheus/common/model/value_histogram.go @@ -67,11 +67,11 @@ func (s HistogramBucket) MarshalJSON() ([]byte, error) { if err != nil { return nil, err } - return []byte(fmt.Sprintf("[%s,%s,%s,%s]", b, l, u, c)), nil + return fmt.Appendf(nil, "[%s,%s,%s,%s]", b, l, u, c), nil } func (s *HistogramBucket) UnmarshalJSON(buf []byte) error { - tmp := []interface{}{&s.Boundaries, &s.Lower, &s.Upper, &s.Count} + tmp := []any{&s.Boundaries, &s.Lower, &s.Upper, &s.Count} wantLen := len(tmp) if err := json.Unmarshal(buf, &tmp); err != nil { return err @@ -152,11 +152,11 @@ func (s SampleHistogramPair) MarshalJSON() ([]byte, error) { if err != nil { return nil, err } - return []byte(fmt.Sprintf("[%s,%s]", t, v)), nil + return fmt.Appendf(nil, "[%s,%s]", t, v), nil } func (s *SampleHistogramPair) UnmarshalJSON(buf []byte) error { - tmp := []interface{}{&s.Timestamp, &s.Histogram} + tmp := []any{&s.Timestamp, &s.Histogram} wantLen := len(tmp) if err := json.Unmarshal(buf, &tmp); err != nil { return err diff --git a/vendor/github.com/prometheus/common/promslog/slog.go b/vendor/github.com/prometheus/common/promslog/slog.go index f5b9e98ba2..f8f77165a6 100644 --- a/vendor/github.com/prometheus/common/promslog/slog.go +++ b/vendor/github.com/prometheus/common/promslog/slog.go @@ -61,7 +61,7 @@ func NewLevel() *Level { } } -func (l *Level) UnmarshalYAML(unmarshal func(interface{}) error) error { +func (l *Level) UnmarshalYAML(unmarshal func(any) error) error { var s string type plain string if err := unmarshal((*plain)(&s)); err != nil { diff --git a/vendor/github.com/prometheus/common/route/route.go b/vendor/github.com/prometheus/common/route/route.go index 18039fac0d..b1f274c15d 100644 --- a/vendor/github.com/prometheus/common/route/route.go +++ b/vendor/github.com/prometheus/common/route/route.go @@ -20,6 +20,11 @@ import ( "github.com/julienschmidt/httprouter" ) +// MethodQuery is the QUERY HTTP method. It is a safe, idempotent method that +// carries a request body, see https://datatracker.ietf.org/doc/rfc10008/. +// It is not yet part of net/http, so it is defined here. +const MethodQuery = "QUERY" + type param string // Param returns param p for the context, or the empty string when @@ -114,6 +119,11 @@ func (r *Router) Head(path string, h http.HandlerFunc) { r.rtr.HEAD(r.prefix+path, r.handle(path, h)) } +// Query registers a new QUERY route. +func (r *Router) Query(path string, h http.HandlerFunc) { + r.rtr.Handle(MethodQuery, r.prefix+path, r.handle(path, h)) +} + // Redirect takes an absolute path and sends an internal HTTP redirect for it, // prefixed by the router's path prefix. Note that this method does not include // functionality for handling relative paths or full URL redirects. diff --git a/vendor/github.com/prometheus/common/version/info.go b/vendor/github.com/prometheus/common/version/info.go index 61ed1ba314..112f355237 100644 --- a/vendor/github.com/prometheus/common/version/info.go +++ b/vendor/github.com/prometheus/common/version/info.go @@ -79,6 +79,24 @@ func BuildContext() string { return fmt.Sprintf("(go=%s, platform=%s, user=%s, date=%s, tags=%s)", GoVersion, GoOS+"/"+GoArch, BuildUser, BuildDate, GetTags()) } +// Slog returns a slice of strings for use with structured logging. +// +// Example: +// logger := promslog.New(promslog.Config{}) +// logger.Info("Starting Prometheus Server", version.Slog()...) +func Slog() []any { + return []any{ + "version", Version, + "revision", Revision, + "branch", Branch, + "builduser", BuildUser, + "builddate", BuildDate, + "goversion", GoVersion, + "goos", GoOS, + "goarch", GoArch, + } +} + func GetRevision() string { if Revision != "" { return Revision diff --git a/vendor/github.com/prometheus/exporter-toolkit/web/cache.go b/vendor/github.com/prometheus/exporter-toolkit/web/cache.go index 252928eeaa..b638afd978 100644 --- a/vendor/github.com/prometheus/exporter-toolkit/web/cache.go +++ b/vendor/github.com/prometheus/exporter-toolkit/web/cache.go @@ -63,10 +63,7 @@ func (c *cache) makeRoom() { // the cache is on a long tail, we can save a lot of CPU // time by doing a whole bunch of deletions now and then // we won't have to do them again for a while. - numToDelete := len(c.cache) / 10 - if numToDelete < 1 { - numToDelete = 1 - } + numToDelete := max(len(c.cache)/10, 1) for deleted := 0; deleted <= numToDelete; deleted++ { // Go maps are "nondeterministic" not actually random, // so although we could just chop off the "front" of the diff --git a/vendor/github.com/prometheus/exporter-toolkit/web/landing_page.go b/vendor/github.com/prometheus/exporter-toolkit/web/landing_page.go index 240337ca77..f863e9d8c3 100644 --- a/vendor/github.com/prometheus/exporter-toolkit/web/landing_page.go +++ b/vendor/github.com/prometheus/exporter-toolkit/web/landing_page.go @@ -12,7 +12,6 @@ // limitations under the License. //go:build !genassets -// +build !genassets //go:generate go run -tags genassets gen_assets.go diff --git a/vendor/github.com/prometheus/exporter-toolkit/web/tls_config.go b/vendor/github.com/prometheus/exporter-toolkit/web/tls_config.go index c760d88ca2..7245f74145 100644 --- a/vendor/github.com/prometheus/exporter-toolkit/web/tls_config.go +++ b/vendor/github.com/prometheus/exporter-toolkit/web/tls_config.go @@ -24,6 +24,7 @@ import ( "net/url" "os" "path/filepath" + "slices" "strconv" "strings" "time" @@ -38,6 +39,7 @@ import ( var ( errNoTLSConfig = errors.New("TLS config is not present") + ErrMissingFlag = errors.New("missing required flag configuration") ErrNoListeners = errors.New("no web listen address or systemd socket flag specified") ) @@ -65,9 +67,27 @@ type TLSConfig struct { } type FlagConfig struct { + // WebListenAddresses contains the listen addresses for the HTTP server. WebListenAddresses *[]string - WebSystemdSocket *bool - WebConfigFile *string + // WebSystemdSocket enables systemd socket activation listeners. + WebSystemdSocket *bool + // WebConfigFile points to the TLS and authentication configuration file. + WebConfigFile *string +} + +// checkFlags validates that the flag configuration contains the required +// listener and web config fields needed by the web package. +func (c *FlagConfig) checkFlags() error { + if c == nil { + return ErrMissingFlag + } + if c.WebConfigFile == nil { + return ErrMissingFlag + } + if c.WebSystemdSocket == nil && (c.WebListenAddresses == nil || len(*c.WebListenAddresses) == 0) { + return ErrNoListeners + } + return nil } // SetDirectory joins any relative file paths with dir. @@ -97,10 +117,8 @@ func (t *TLSConfig) VerifyPeerCertificate(rawCerts [][]byte, _ [][]*x509.Certifi } for _, sanValue := range sanValues { - for _, allowedSan := range t.ClientAllowedSans { - if sanValue == allowedSan { - return nil - } + if slices.Contains(t.ClientAllowedSans, sanValue) { + return nil } } @@ -291,8 +309,8 @@ func ServeMultiple(listeners []net.Listener, server *http.Server, flags *FlagCon // FlagConfig is true. // The FlagConfig is also passed on to ServeMultiple. func ListenAndServe(server *http.Server, flags *FlagConfig, logger *slog.Logger) error { - if flags.WebSystemdSocket == nil && (flags.WebListenAddresses == nil || len(*flags.WebListenAddresses) == 0) { - return ErrNoListeners + if err := flags.checkFlags(); err != nil { + return err } if flags.WebSystemdSocket != nil && *flags.WebSystemdSocket { @@ -440,7 +458,7 @@ func Validate(tlsConfigPath string) error { type Cipher uint16 -func (c *Cipher) UnmarshalYAML(unmarshal func(interface{}) error) error { +func (c *Cipher) UnmarshalYAML(unmarshal func(any) error) error { var s string err := unmarshal(&s) if err != nil { @@ -455,7 +473,7 @@ func (c *Cipher) UnmarshalYAML(unmarshal func(interface{}) error) error { return errors.New("unknown cipher: " + s) } -func (c Cipher) MarshalYAML() (interface{}, error) { +func (c Cipher) MarshalYAML() (any, error) { return tls.CipherSuiteName((uint16)(c)), nil } @@ -468,7 +486,7 @@ var curves = map[string]Curve{ "X25519": (Curve)(tls.X25519), } -func (c *Curve) UnmarshalYAML(unmarshal func(interface{}) error) error { +func (c *Curve) UnmarshalYAML(unmarshal func(any) error) error { var s string err := unmarshal(&s) if err != nil { @@ -481,7 +499,7 @@ func (c *Curve) UnmarshalYAML(unmarshal func(interface{}) error) error { return errors.New("unknown curve: " + s) } -func (c *Curve) MarshalYAML() (interface{}, error) { +func (c *Curve) MarshalYAML() (any, error) { for s, curveid := range curves { if *c == curveid { return s, nil @@ -499,7 +517,7 @@ var tlsVersions = map[string]TLSVersion{ "TLS10": (TLSVersion)(tls.VersionTLS10), } -func (tv *TLSVersion) UnmarshalYAML(unmarshal func(interface{}) error) error { +func (tv *TLSVersion) UnmarshalYAML(unmarshal func(any) error) error { var s string err := unmarshal(&s) if err != nil { @@ -512,7 +530,7 @@ func (tv *TLSVersion) UnmarshalYAML(unmarshal func(interface{}) error) error { return errors.New("unknown TLS version: " + s) } -func (tv *TLSVersion) MarshalYAML() (interface{}, error) { +func (tv *TLSVersion) MarshalYAML() (any, error) { for s, v := range tlsVersions { if *tv == v { return s, nil diff --git a/vendor/github.com/prometheus/procfs/.golangci.yml b/vendor/github.com/prometheus/procfs/.golangci.yml index 3c3bf910fd..eac920ba80 100644 --- a/vendor/github.com/prometheus/procfs/.golangci.yml +++ b/vendor/github.com/prometheus/procfs/.golangci.yml @@ -1,7 +1,9 @@ version: "2" linters: enable: + - errorlint - forbidigo + - gocritic - godot - misspell - revive @@ -11,6 +13,20 @@ linters: forbid: - pattern: ^fmt\.Print.*$ msg: Do not commit print statements. + gocritic: + enable-all: true + disabled-checks: + - commentFormatting + - commentedOutCode + - deferInLoop + - filepathJoin + - hugeParam + - importShadow + - paramTypeCombine + - rangeValCopy + - tooManyResultsChecker + - unnamedResult + - whyNoLint godot: exclude: # Ignore "See: URL". @@ -18,17 +34,21 @@ linters: capital: true misspell: locale: US + revive: + rules: + - name: var-naming + # TODO(SuperQ): See: https://github.com/prometheus/prometheus/issues/17766 + arguments: + - [] + - [] + - - skip-package-name-checks: true exclusions: - generated: lax presets: - comments - common-false-positives - legacy - std-error-handling - paths: - - third_party$ - - builtin$ - - examples$ + warn-unused: true formatters: enable: - gofmt @@ -37,9 +57,3 @@ formatters: goimports: local-prefixes: - github.com/prometheus/procfs - exclusions: - generated: lax - paths: - - third_party$ - - builtin$ - - examples$ diff --git a/vendor/github.com/prometheus/procfs/Makefile b/vendor/github.com/prometheus/procfs/Makefile index 7edfe4d093..bce50a19c5 100644 --- a/vendor/github.com/prometheus/procfs/Makefile +++ b/vendor/github.com/prometheus/procfs/Makefile @@ -1,4 +1,4 @@ -# Copyright 2018 The Prometheus Authors +# Copyright The Prometheus Authors # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/procfs/Makefile.common b/vendor/github.com/prometheus/procfs/Makefile.common index 0ed55c2ba2..a7c5f553e1 100644 --- a/vendor/github.com/prometheus/procfs/Makefile.common +++ b/vendor/github.com/prometheus/procfs/Makefile.common @@ -1,4 +1,4 @@ -# Copyright 2018 The Prometheus Authors +# Copyright The Prometheus Authors # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at @@ -33,7 +33,7 @@ GOHOSTOS ?= $(shell $(GO) env GOHOSTOS) GOHOSTARCH ?= $(shell $(GO) env GOHOSTARCH) GO_VERSION ?= $(shell $(GO) version) -GO_VERSION_NUMBER ?= $(word 3, $(GO_VERSION))Error Parsing File +GO_VERSION_NUMBER ?= $(word 3, $(GO_VERSION)) PRE_GO_111 ?= $(shell echo $(GO_VERSION_NUMBER) | grep -E 'go1\.(10|[0-9])\.') PROMU := $(FIRST_GOPATH)/bin/promu @@ -55,13 +55,14 @@ ifneq ($(shell command -v gotestsum 2> /dev/null),) endif endif -PROMU_VERSION ?= 0.17.0 +PROMU_VERSION ?= 0.20.0 PROMU_URL := https://github.com/prometheus/promu/releases/download/v$(PROMU_VERSION)/promu-$(PROMU_VERSION).$(GO_BUILD_PLATFORM).tar.gz SKIP_GOLANGCI_LINT := GOLANGCI_LINT := GOLANGCI_LINT_OPTS ?= -GOLANGCI_LINT_VERSION ?= v2.0.2 +GOLANGCI_LINT_VERSION ?= v2.11.4 +GOLANGCI_FMT_OPTS ?= # golangci-lint only supports linux, darwin and windows platforms on i386/amd64/arm64. # windows isn't included here because of the path separator being different. ifeq ($(GOHOSTOS),$(filter $(GOHOSTOS),linux darwin)) @@ -81,11 +82,32 @@ endif PREFIX ?= $(shell pwd) BIN_DIR ?= $(shell pwd) DOCKER_IMAGE_TAG ?= $(subst /,-,$(shell git rev-parse --abbrev-ref HEAD)) -DOCKERFILE_PATH ?= ./Dockerfile DOCKERBUILD_CONTEXT ?= ./ DOCKER_REPO ?= prom -DOCKER_ARCHS ?= amd64 +# Check if deprecated DOCKERFILE_PATH is set +ifdef DOCKERFILE_PATH +$(error DOCKERFILE_PATH is deprecated. Use DOCKERFILE_VARIANTS ?= $(DOCKERFILE_PATH) in the Makefile) +endif + +DOCKER_ARCHS ?= amd64 arm64 armv7 ppc64le riscv64 s390x +DOCKERFILE_VARIANTS ?= $(wildcard Dockerfile Dockerfile.*) + +# Function to extract variant from Dockerfile label. +# Returns the variant name from io.prometheus.image.variant label, or "default" if not found. +define dockerfile_variant +$(strip $(or $(shell sed -n 's/.*io\.prometheus\.image\.variant="\([^"]*\)".*/\1/p' $(1)),default)) +endef + +# Check for duplicate variant names (including default for Dockerfiles without labels). +DOCKERFILE_VARIANT_NAMES := $(foreach df,$(DOCKERFILE_VARIANTS),$(call dockerfile_variant,$(df))) +DOCKERFILE_VARIANT_NAMES_SORTED := $(sort $(DOCKERFILE_VARIANT_NAMES)) +ifneq ($(words $(DOCKERFILE_VARIANT_NAMES)),$(words $(DOCKERFILE_VARIANT_NAMES_SORTED))) +$(error Duplicate variant names found. Each Dockerfile must have a unique io.prometheus.image.variant label, and only one can be without a label (default)) +endif + +# Build variant:dockerfile pairs for shell iteration. +DOCKERFILE_VARIANTS_WITH_NAMES := $(foreach df,$(DOCKERFILE_VARIANTS),$(call dockerfile_variant,$(df)):$(df)) BUILD_DOCKER_ARCHS = $(addprefix common-docker-,$(DOCKER_ARCHS)) PUBLISH_DOCKER_ARCHS = $(addprefix common-docker-publish-,$(DOCKER_ARCHS)) @@ -111,7 +133,7 @@ common-all: precheck style check_license lint yamllint unused build test .PHONY: common-style common-style: @echo ">> checking code style" - @fmtRes=$$($(GOFMT) -d $$(find . -path ./vendor -prune -o -name '*.go' -print)); \ + @fmtRes=$$($(GOFMT) -d $$(git ls-files '*.go' ':!:vendor/*' || find . -path ./vendor -prune -o -name '*.go' -print)); \ if [ -n "$${fmtRes}" ]; then \ echo "gofmt checking failed!"; echo "$${fmtRes}"; echo; \ echo "Please ensure you are using $$($(GO) version) for formatting code."; \ @@ -121,13 +143,19 @@ common-style: .PHONY: common-check_license common-check_license: @echo ">> checking license header" - @licRes=$$(for file in $$(find . -type f -iname '*.go' ! -path './vendor/*') ; do \ + @licRes=$$(for file in $$(git ls-files '*.go' ':!:vendor/*' || find . -path ./vendor -prune -o -type f -iname '*.go' -print) ; do \ awk 'NR<=3' $$file | grep -Eq "(Copyright|generated|GENERATED)" || echo $$file; \ done); \ if [ -n "$${licRes}" ]; then \ echo "license header checking failed:"; echo "$${licRes}"; \ exit 1; \ fi + @echo ">> checking for copyright years 2026 or later" + @futureYearRes=$$(git grep -E 'Copyright (202[6-9]|20[3-9][0-9])' -- '*.go' ':!:vendor/*' || true); \ + if [ -n "$${futureYearRes}" ]; then \ + echo "Files with copyright year 2026 or later found (should use 'Copyright The Prometheus Authors'):"; echo "$${futureYearRes}"; \ + exit 1; \ + fi .PHONY: common-deps common-deps: @@ -138,7 +166,7 @@ common-deps: update-go-deps: @echo ">> updating Go dependencies" @for m in $$($(GO) list -mod=readonly -m -f '{{ if and (not .Indirect) (not .Main)}}{{.Path}}{{end}}' all); do \ - $(GO) get -d $$m; \ + $(GO) get $$m; \ done $(GO) mod tidy @@ -156,9 +184,13 @@ $(GOTEST_DIR): @mkdir -p $@ .PHONY: common-format -common-format: +common-format: $(GOLANGCI_LINT) @echo ">> formatting code" $(GO) fmt $(pkgs) +ifdef GOLANGCI_LINT + @echo ">> formatting code with golangci-lint" + $(GOLANGCI_LINT) fmt $(GOLANGCI_FMT_OPTS) +endif .PHONY: common-vet common-vet: @@ -215,28 +247,142 @@ common-docker-repo-name: .PHONY: common-docker $(BUILD_DOCKER_ARCHS) common-docker: $(BUILD_DOCKER_ARCHS) $(BUILD_DOCKER_ARCHS): common-docker-%: - docker build -t "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(SANITIZED_DOCKER_IMAGE_TAG)" \ - -f $(DOCKERFILE_PATH) \ - --build-arg ARCH="$*" \ - --build-arg OS="linux" \ - $(DOCKERBUILD_CONTEXT) + @for variant in $(DOCKERFILE_VARIANTS_WITH_NAMES); do \ + dockerfile=$${variant#*:}; \ + variant_name=$${variant%%:*}; \ + distroless_arch="$*"; \ + if [ "$*" = "armv7" ]; then \ + distroless_arch="arm"; \ + fi; \ + if [ "$$dockerfile" = "Dockerfile" ]; then \ + echo "Building default variant ($$variant_name) for linux-$* using $$dockerfile"; \ + docker build -t "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(SANITIZED_DOCKER_IMAGE_TAG)" \ + -f $$dockerfile \ + --build-arg ARCH="$*" \ + --build-arg OS="linux" \ + --build-arg DISTROLESS_ARCH="$$distroless_arch" \ + $(DOCKERBUILD_CONTEXT); \ + if [ "$$variant_name" != "default" ]; then \ + echo "Tagging default variant with $$variant_name suffix"; \ + docker tag "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(SANITIZED_DOCKER_IMAGE_TAG)" \ + "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(SANITIZED_DOCKER_IMAGE_TAG)-$$variant_name"; \ + fi; \ + else \ + echo "Building $$variant_name variant for linux-$* using $$dockerfile"; \ + docker build -t "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(SANITIZED_DOCKER_IMAGE_TAG)-$$variant_name" \ + -f $$dockerfile \ + --build-arg ARCH="$*" \ + --build-arg OS="linux" \ + --build-arg DISTROLESS_ARCH="$$distroless_arch" \ + $(DOCKERBUILD_CONTEXT); \ + fi; \ + done .PHONY: common-docker-publish $(PUBLISH_DOCKER_ARCHS) common-docker-publish: $(PUBLISH_DOCKER_ARCHS) $(PUBLISH_DOCKER_ARCHS): common-docker-publish-%: - docker push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(SANITIZED_DOCKER_IMAGE_TAG)" + @for variant in $(DOCKERFILE_VARIANTS_WITH_NAMES); do \ + dockerfile=$${variant#*:}; \ + variant_name=$${variant%%:*}; \ + if [ "$$dockerfile" != "Dockerfile" ] || [ "$$variant_name" != "default" ]; then \ + echo "Pushing $$variant_name variant for linux-$*"; \ + docker push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(SANITIZED_DOCKER_IMAGE_TAG)-$$variant_name"; \ + fi; \ + if [ "$$dockerfile" = "Dockerfile" ]; then \ + echo "Pushing default variant ($$variant_name) for linux-$*"; \ + docker push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(SANITIZED_DOCKER_IMAGE_TAG)"; \ + fi; \ + if [ "$(DOCKER_IMAGE_TAG)" = "latest" ]; then \ + if [ "$$dockerfile" != "Dockerfile" ] || [ "$$variant_name" != "default" ]; then \ + echo "Pushing $$variant_name variant version tags for linux-$*"; \ + docker push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:v$(DOCKER_MAJOR_VERSION_TAG)-$$variant_name"; \ + fi; \ + if [ "$$dockerfile" = "Dockerfile" ]; then \ + echo "Pushing default variant version tag for linux-$*"; \ + docker push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:v$(DOCKER_MAJOR_VERSION_TAG)"; \ + fi; \ + fi; \ + done DOCKER_MAJOR_VERSION_TAG = $(firstword $(subst ., ,$(shell cat VERSION))) .PHONY: common-docker-tag-latest $(TAG_DOCKER_ARCHS) common-docker-tag-latest: $(TAG_DOCKER_ARCHS) $(TAG_DOCKER_ARCHS): common-docker-tag-latest-%: - docker tag "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(SANITIZED_DOCKER_IMAGE_TAG)" "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:latest" - docker tag "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(SANITIZED_DOCKER_IMAGE_TAG)" "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:v$(DOCKER_MAJOR_VERSION_TAG)" + @for variant in $(DOCKERFILE_VARIANTS_WITH_NAMES); do \ + dockerfile=$${variant#*:}; \ + variant_name=$${variant%%:*}; \ + if [ "$$dockerfile" != "Dockerfile" ] || [ "$$variant_name" != "default" ]; then \ + echo "Tagging $$variant_name variant for linux-$* as latest"; \ + docker tag "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(SANITIZED_DOCKER_IMAGE_TAG)-$$variant_name" "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:latest-$$variant_name"; \ + docker tag "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(SANITIZED_DOCKER_IMAGE_TAG)-$$variant_name" "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:v$(DOCKER_MAJOR_VERSION_TAG)-$$variant_name"; \ + fi; \ + if [ "$$dockerfile" = "Dockerfile" ]; then \ + echo "Tagging default variant ($$variant_name) for linux-$* as latest"; \ + docker tag "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(SANITIZED_DOCKER_IMAGE_TAG)" "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:latest"; \ + docker tag "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(SANITIZED_DOCKER_IMAGE_TAG)" "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:v$(DOCKER_MAJOR_VERSION_TAG)"; \ + fi; \ + done .PHONY: common-docker-manifest common-docker-manifest: - DOCKER_CLI_EXPERIMENTAL=enabled docker manifest create -a "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):$(SANITIZED_DOCKER_IMAGE_TAG)" $(foreach ARCH,$(DOCKER_ARCHS),$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$(ARCH):$(SANITIZED_DOCKER_IMAGE_TAG)) - DOCKER_CLI_EXPERIMENTAL=enabled docker manifest push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):$(SANITIZED_DOCKER_IMAGE_TAG)" + @for variant in $(DOCKERFILE_VARIANTS_WITH_NAMES); do \ + dockerfile=$${variant#*:}; \ + variant_name=$${variant%%:*}; \ + if [ "$$dockerfile" != "Dockerfile" ] || [ "$$variant_name" != "default" ]; then \ + echo "Creating manifest for $$variant_name variant"; \ + refs=""; \ + for arch in $(DOCKER_ARCHS); do \ + refs="$$refs $(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$$arch:$(SANITIZED_DOCKER_IMAGE_TAG)-$$variant_name"; \ + done; \ + if [ -z "$$refs" ]; then \ + echo "Skipping manifest for $$variant_name variant (no supported architectures)"; \ + continue; \ + fi; \ + DOCKER_CLI_EXPERIMENTAL=enabled docker manifest create -a "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):$(SANITIZED_DOCKER_IMAGE_TAG)-$$variant_name" $$refs; \ + DOCKER_CLI_EXPERIMENTAL=enabled docker manifest push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):$(SANITIZED_DOCKER_IMAGE_TAG)-$$variant_name"; \ + fi; \ + if [ "$$dockerfile" = "Dockerfile" ]; then \ + echo "Creating default variant ($$variant_name) manifest"; \ + refs=""; \ + for arch in $(DOCKER_ARCHS); do \ + refs="$$refs $(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$$arch:$(SANITIZED_DOCKER_IMAGE_TAG)"; \ + done; \ + if [ -z "$$refs" ]; then \ + echo "Skipping default variant manifest (no supported architectures)"; \ + continue; \ + fi; \ + DOCKER_CLI_EXPERIMENTAL=enabled docker manifest create -a "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):$(SANITIZED_DOCKER_IMAGE_TAG)" $$refs; \ + DOCKER_CLI_EXPERIMENTAL=enabled docker manifest push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):$(SANITIZED_DOCKER_IMAGE_TAG)"; \ + fi; \ + if [ "$(DOCKER_IMAGE_TAG)" = "latest" ]; then \ + if [ "$$dockerfile" != "Dockerfile" ] || [ "$$variant_name" != "default" ]; then \ + echo "Creating manifest for $$variant_name variant version tag"; \ + refs=""; \ + for arch in $(DOCKER_ARCHS); do \ + refs="$$refs $(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$$arch:v$(DOCKER_MAJOR_VERSION_TAG)-$$variant_name"; \ + done; \ + if [ -z "$$refs" ]; then \ + echo "Skipping version-tag manifest for $$variant_name variant (no supported architectures)"; \ + continue; \ + fi; \ + DOCKER_CLI_EXPERIMENTAL=enabled docker manifest create -a "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):v$(DOCKER_MAJOR_VERSION_TAG)-$$variant_name" $$refs; \ + DOCKER_CLI_EXPERIMENTAL=enabled docker manifest push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):v$(DOCKER_MAJOR_VERSION_TAG)-$$variant_name"; \ + fi; \ + if [ "$$dockerfile" = "Dockerfile" ]; then \ + echo "Creating default variant version tag manifest"; \ + refs=""; \ + for arch in $(DOCKER_ARCHS); do \ + refs="$$refs $(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$$arch:v$(DOCKER_MAJOR_VERSION_TAG)"; \ + done; \ + if [ -z "$$refs" ]; then \ + echo "Skipping default variant version-tag manifest (no supported architectures)"; \ + continue; \ + fi; \ + DOCKER_CLI_EXPERIMENTAL=enabled docker manifest create -a "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):v$(DOCKER_MAJOR_VERSION_TAG)" $$refs; \ + DOCKER_CLI_EXPERIMENTAL=enabled docker manifest push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):v$(DOCKER_MAJOR_VERSION_TAG)"; \ + fi; \ + fi; \ + done .PHONY: promu promu: $(PROMU) @@ -248,8 +394,8 @@ $(PROMU): cp $(PROMU_TMP)/promu-$(PROMU_VERSION).$(GO_BUILD_PLATFORM)/promu $(FIRST_GOPATH)/bin/promu rm -r $(PROMU_TMP) -.PHONY: proto -proto: +.PHONY: common-proto +common-proto: @echo ">> generating code from proto files" @./scripts/genproto.sh @@ -261,6 +407,10 @@ $(GOLANGCI_LINT): | sh -s -- -b $(FIRST_GOPATH)/bin $(GOLANGCI_LINT_VERSION) endif +.PHONY: common-print-golangci-lint-version +common-print-golangci-lint-version: + @echo $(GOLANGCI_LINT_VERSION) + .PHONY: precheck precheck:: @@ -275,9 +425,3 @@ $(1)_precheck: exit 1; \ fi endef - -govulncheck: install-govulncheck - govulncheck ./... - -install-govulncheck: - command -v govulncheck > /dev/null || go install golang.org/x/vuln/cmd/govulncheck@latest diff --git a/vendor/github.com/prometheus/procfs/README.md b/vendor/github.com/prometheus/procfs/README.md index 0718239cf1..363524094b 100644 --- a/vendor/github.com/prometheus/procfs/README.md +++ b/vendor/github.com/prometheus/procfs/README.md @@ -7,7 +7,7 @@ metrics from the pseudo-filesystems /proc and /sys. backwards-incompatible ways without warnings. Use it at your own risk. [![Go Reference](https://pkg.go.dev/badge/github.com/prometheus/procfs.svg)](https://pkg.go.dev/github.com/prometheus/procfs) -[![CircleCI](https://circleci.com/gh/prometheus/procfs/tree/master.svg?style=svg)](https://circleci.com/gh/prometheus/procfs/tree/master) +[![Build Status](https://github.com/prometheus/procfs/actions/workflows/ci.yml/badge.svg)](https://github.com/prometheus/procfs/actions/workflows/ci.yml) [![Go Report Card](https://goreportcard.com/badge/github.com/prometheus/procfs)](https://goreportcard.com/report/github.com/prometheus/procfs) ## Usage diff --git a/vendor/github.com/prometheus/procfs/SECURITY.md b/vendor/github.com/prometheus/procfs/SECURITY.md index fed02d85c7..5e6f976dbf 100644 --- a/vendor/github.com/prometheus/procfs/SECURITY.md +++ b/vendor/github.com/prometheus/procfs/SECURITY.md @@ -3,4 +3,4 @@ The Prometheus security policy, including how to report vulnerabilities, can be found here: - +[https://prometheus.io/docs/operating/security/](https://prometheus.io/docs/operating/security/) diff --git a/vendor/github.com/prometheus/procfs/arp.go b/vendor/github.com/prometheus/procfs/arp.go index 2e53344151..716bdef109 100644 --- a/vendor/github.com/prometheus/procfs/arp.go +++ b/vendor/github.com/prometheus/procfs/arp.go @@ -1,4 +1,4 @@ -// Copyright 2019 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -73,15 +73,16 @@ func parseARPEntries(data []byte) ([]ARPEntry, error) { columns := strings.Fields(line) width := len(columns) - if width == expectedHeaderWidth || width == 0 { + switch width { + case expectedHeaderWidth, 0: continue - } else if width == expectedDataWidth { + case expectedDataWidth: entry, err := parseARPEntry(columns) if err != nil { return []ARPEntry{}, fmt.Errorf("%w: Failed to parse ARP entry: %v: %w", ErrFileParse, entry, err) } entries = append(entries, entry) - } else { + default: return []ARPEntry{}, fmt.Errorf("%w: %d columns found, but expected %d: %w", ErrFileParse, width, expectedDataWidth, err) } diff --git a/vendor/github.com/prometheus/procfs/buddyinfo.go b/vendor/github.com/prometheus/procfs/buddyinfo.go index 8380750090..53243e6875 100644 --- a/vendor/github.com/prometheus/procfs/buddyinfo.go +++ b/vendor/github.com/prometheus/procfs/buddyinfo.go @@ -1,4 +1,4 @@ -// Copyright 2017 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -64,14 +64,12 @@ func parseBuddyInfo(r io.Reader) ([]BuddyInfo, error) { if bucketCount == -1 { bucketCount = arraySize - } else { - if bucketCount != arraySize { - return nil, fmt.Errorf("%w: mismatch in number of buddyinfo buckets, previous count %d, new count %d", ErrFileParse, bucketCount, arraySize) - } + } else if bucketCount != arraySize { + return nil, fmt.Errorf("%w: mismatch in number of buddyinfo buckets, previous count %d, new count %d", ErrFileParse, bucketCount, arraySize) } sizes := make([]float64, arraySize) - for i := 0; i < arraySize; i++ { + for i := range arraySize { sizes[i], err = strconv.ParseFloat(parts[i+4], 64) if err != nil { return nil, fmt.Errorf("%w: Invalid valid in buddyinfo: %f: %w", ErrFileParse, sizes[i], err) diff --git a/vendor/github.com/prometheus/procfs/cmdline.go b/vendor/github.com/prometheus/procfs/cmdline.go index bf4f3b48c0..4f1cac1f0a 100644 --- a/vendor/github.com/prometheus/procfs/cmdline.go +++ b/vendor/github.com/prometheus/procfs/cmdline.go @@ -1,4 +1,4 @@ -// Copyright 2021 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/procfs/cpuinfo.go b/vendor/github.com/prometheus/procfs/cpuinfo.go index f0950bb495..4b23d8d6b5 100644 --- a/vendor/github.com/prometheus/procfs/cpuinfo.go +++ b/vendor/github.com/prometheus/procfs/cpuinfo.go @@ -1,4 +1,4 @@ -// Copyright 2019 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -12,7 +12,6 @@ // limitations under the License. //go:build linux -// +build linux package procfs @@ -502,7 +501,7 @@ func parseCPUInfoRISCV(info []byte) ([]CPUInfo, error) { return cpuinfo, nil } -func parseCPUInfoDummy(_ []byte) ([]CPUInfo, error) { // nolint:unused,deadcode +func parseCPUInfoDummy(_ []byte) ([]CPUInfo, error) { //nolint:unused return nil, errors.New("not implemented") } diff --git a/vendor/github.com/prometheus/procfs/cpuinfo_armx.go b/vendor/github.com/prometheus/procfs/cpuinfo_armx.go index 64cfd534c1..b09035ff38 100644 --- a/vendor/github.com/prometheus/procfs/cpuinfo_armx.go +++ b/vendor/github.com/prometheus/procfs/cpuinfo_armx.go @@ -1,4 +1,4 @@ -// Copyright 2020 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -12,8 +12,6 @@ // limitations under the License. //go:build linux && (arm || arm64) -// +build linux -// +build arm arm64 package procfs diff --git a/vendor/github.com/prometheus/procfs/cpuinfo_loong64.go b/vendor/github.com/prometheus/procfs/cpuinfo_loong64.go index d88442f0ed..7bb20211f9 100644 --- a/vendor/github.com/prometheus/procfs/cpuinfo_loong64.go +++ b/vendor/github.com/prometheus/procfs/cpuinfo_loong64.go @@ -1,4 +1,4 @@ -// Copyright 2022 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -12,7 +12,6 @@ // limitations under the License. //go:build linux -// +build linux package procfs diff --git a/vendor/github.com/prometheus/procfs/cpuinfo_mipsx.go b/vendor/github.com/prometheus/procfs/cpuinfo_mipsx.go index c11207f3ab..fd75d0f79d 100644 --- a/vendor/github.com/prometheus/procfs/cpuinfo_mipsx.go +++ b/vendor/github.com/prometheus/procfs/cpuinfo_mipsx.go @@ -1,4 +1,4 @@ -// Copyright 2020 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -12,8 +12,6 @@ // limitations under the License. //go:build linux && (mips || mipsle || mips64 || mips64le) -// +build linux -// +build mips mipsle mips64 mips64le package procfs diff --git a/vendor/github.com/prometheus/procfs/cpuinfo_others.go b/vendor/github.com/prometheus/procfs/cpuinfo_others.go index a6b2b3127c..3d36ba0e6b 100644 --- a/vendor/github.com/prometheus/procfs/cpuinfo_others.go +++ b/vendor/github.com/prometheus/procfs/cpuinfo_others.go @@ -1,4 +1,4 @@ -// Copyright 2020 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -12,7 +12,6 @@ // limitations under the License. //go:build linux && !386 && !amd64 && !arm && !arm64 && !loong64 && !mips && !mips64 && !mips64le && !mipsle && !ppc64 && !ppc64le && !riscv64 && !s390x -// +build linux,!386,!amd64,!arm,!arm64,!loong64,!mips,!mips64,!mips64le,!mipsle,!ppc64,!ppc64le,!riscv64,!s390x package procfs diff --git a/vendor/github.com/prometheus/procfs/cpuinfo_ppcx.go b/vendor/github.com/prometheus/procfs/cpuinfo_ppcx.go index 003bc2ad4a..b3425051ef 100644 --- a/vendor/github.com/prometheus/procfs/cpuinfo_ppcx.go +++ b/vendor/github.com/prometheus/procfs/cpuinfo_ppcx.go @@ -1,4 +1,4 @@ -// Copyright 2020 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -12,8 +12,6 @@ // limitations under the License. //go:build linux && (ppc64 || ppc64le) -// +build linux -// +build ppc64 ppc64le package procfs diff --git a/vendor/github.com/prometheus/procfs/cpuinfo_riscvx.go b/vendor/github.com/prometheus/procfs/cpuinfo_riscvx.go index 1c9b7313b6..72598230c3 100644 --- a/vendor/github.com/prometheus/procfs/cpuinfo_riscvx.go +++ b/vendor/github.com/prometheus/procfs/cpuinfo_riscvx.go @@ -1,4 +1,4 @@ -// Copyright 2020 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -12,8 +12,6 @@ // limitations under the License. //go:build linux && (riscv || riscv64) -// +build linux -// +build riscv riscv64 package procfs diff --git a/vendor/github.com/prometheus/procfs/cpuinfo_s390x.go b/vendor/github.com/prometheus/procfs/cpuinfo_s390x.go index fa3686bc00..50a8239cbc 100644 --- a/vendor/github.com/prometheus/procfs/cpuinfo_s390x.go +++ b/vendor/github.com/prometheus/procfs/cpuinfo_s390x.go @@ -1,4 +1,4 @@ -// Copyright 2020 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -12,7 +12,6 @@ // limitations under the License. //go:build linux -// +build linux package procfs diff --git a/vendor/github.com/prometheus/procfs/cpuinfo_x86.go b/vendor/github.com/prometheus/procfs/cpuinfo_x86.go index a0ef55562e..00edb30a5c 100644 --- a/vendor/github.com/prometheus/procfs/cpuinfo_x86.go +++ b/vendor/github.com/prometheus/procfs/cpuinfo_x86.go @@ -1,4 +1,4 @@ -// Copyright 2020 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -12,8 +12,6 @@ // limitations under the License. //go:build linux && (386 || amd64) -// +build linux -// +build 386 amd64 package procfs diff --git a/vendor/github.com/prometheus/procfs/crypto.go b/vendor/github.com/prometheus/procfs/crypto.go index 5f2a37a78b..d93b712e05 100644 --- a/vendor/github.com/prometheus/procfs/crypto.go +++ b/vendor/github.com/prometheus/procfs/crypto.go @@ -1,4 +1,4 @@ -// Copyright 2019 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -48,11 +48,13 @@ type Crypto struct { Walksize *uint64 } +var cryptoFile = "crypto" + // Crypto parses an crypto-file (/proc/crypto) and returns a slice of // structs containing the relevant info. More information available here: // https://kernel.readthedocs.io/en/sphinx-samples/crypto-API.html func (fs FS) Crypto() ([]Crypto, error) { - path := fs.proc.Path("crypto") + path := fs.proc.Path(cryptoFile) b, err := util.ReadFileNoStat(path) if err != nil { return nil, fmt.Errorf("%w: Cannot read file %v: %w", ErrFileRead, b, err) @@ -82,6 +84,10 @@ func parseCrypto(r io.Reader) ([]Crypto, error) { continue } + if len(out) == 0 { + return nil, fmt.Errorf("%w: parsed invalid line before name parsed: %q", ErrFileParse, text) + } + kv := strings.Split(text, ":") if len(kv) != 2 { return nil, fmt.Errorf("%w: Cannot parse line: %q", ErrFileParse, text) diff --git a/vendor/github.com/prometheus/procfs/doc.go b/vendor/github.com/prometheus/procfs/doc.go index f9d961e441..26bfea071b 100644 --- a/vendor/github.com/prometheus/procfs/doc.go +++ b/vendor/github.com/prometheus/procfs/doc.go @@ -1,4 +1,4 @@ -// Copyright 2014 Prometheus Team +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/procfs/fs.go b/vendor/github.com/prometheus/procfs/fs.go index 9bdaccc7c8..8f27912a13 100644 --- a/vendor/github.com/prometheus/procfs/fs.go +++ b/vendor/github.com/prometheus/procfs/fs.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/procfs/fs_statfs_notype.go b/vendor/github.com/prometheus/procfs/fs_statfs_notype.go index 1b5bdbdf84..0bef25bdd9 100644 --- a/vendor/github.com/prometheus/procfs/fs_statfs_notype.go +++ b/vendor/github.com/prometheus/procfs/fs_statfs_notype.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -12,7 +12,6 @@ // limitations under the License. //go:build !freebsd && !linux -// +build !freebsd,!linux package procfs diff --git a/vendor/github.com/prometheus/procfs/fs_statfs_type.go b/vendor/github.com/prometheus/procfs/fs_statfs_type.go index 80df79c319..d183330390 100644 --- a/vendor/github.com/prometheus/procfs/fs_statfs_type.go +++ b/vendor/github.com/prometheus/procfs/fs_statfs_type.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -12,7 +12,6 @@ // limitations under the License. //go:build freebsd || linux -// +build freebsd linux package procfs diff --git a/vendor/github.com/prometheus/procfs/fscache.go b/vendor/github.com/prometheus/procfs/fscache.go index 7db8633077..9dde857073 100644 --- a/vendor/github.com/prometheus/procfs/fscache.go +++ b/vendor/github.com/prometheus/procfs/fscache.go @@ -1,4 +1,4 @@ -// Copyright 2019 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -388,20 +388,21 @@ func parseFscacheinfo(r io.Reader) (*Fscacheinfo, error) { } } case "CacheOp:": - if strings.Split(fields[1], "=")[0] == "alo" { + switch strings.Split(fields[1], "=")[0] { + case "alo": err := setFSCacheFields(fields[1:], &m.CacheopAllocationsinProgress, &m.CacheopLookupObjectInProgress, &m.CacheopLookupCompleteInPorgress, &m.CacheopGrabObjectInProgress) if err != nil { return &m, err } - } else if strings.Split(fields[1], "=")[0] == "inv" { + case "inv": err := setFSCacheFields(fields[1:], &m.CacheopInvalidations, &m.CacheopUpdateObjectInProgress, &m.CacheopDropObjectInProgress, &m.CacheopPutObjectInProgress, &m.CacheopAttributeChangeInProgress, &m.CacheopSyncCacheInProgress) if err != nil { return &m, err } - } else { + default: err := setFSCacheFields(fields[1:], &m.CacheopReadOrAllocPageInProgress, &m.CacheopReadOrAllocPagesInProgress, &m.CacheopAllocatePageInProgress, &m.CacheopAllocatePagesInProgress, &m.CacheopWritePagesInProgress, &m.CacheopUncachePagesInProgress, &m.CacheopDissociatePagesInProgress) diff --git a/vendor/github.com/prometheus/procfs/internal/fs/fs.go b/vendor/github.com/prometheus/procfs/internal/fs/fs.go index 3a43e83915..e7ccad66b2 100644 --- a/vendor/github.com/prometheus/procfs/internal/fs/fs.go +++ b/vendor/github.com/prometheus/procfs/internal/fs/fs.go @@ -1,4 +1,4 @@ -// Copyright 2019 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/procfs/internal/util/parse.go b/vendor/github.com/prometheus/procfs/internal/util/parse.go index 5a7d2df06a..30c5872019 100644 --- a/vendor/github.com/prometheus/procfs/internal/util/parse.go +++ b/vendor/github.com/prometheus/procfs/internal/util/parse.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/procfs/internal/util/readfile.go b/vendor/github.com/prometheus/procfs/internal/util/readfile.go index 71b7a70ebd..0e41f71af1 100644 --- a/vendor/github.com/prometheus/procfs/internal/util/readfile.go +++ b/vendor/github.com/prometheus/procfs/internal/util/readfile.go @@ -1,4 +1,4 @@ -// Copyright 2019 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/procfs/internal/util/sysreadfile.go b/vendor/github.com/prometheus/procfs/internal/util/sysreadfile.go index d5404a6d72..f6a4a4de62 100644 --- a/vendor/github.com/prometheus/procfs/internal/util/sysreadfile.go +++ b/vendor/github.com/prometheus/procfs/internal/util/sysreadfile.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -12,8 +12,6 @@ // limitations under the License. //go:build (linux || darwin) && !appengine -// +build linux darwin -// +build !appengine package util diff --git a/vendor/github.com/prometheus/procfs/internal/util/sysreadfile_compat.go b/vendor/github.com/prometheus/procfs/internal/util/sysreadfile_compat.go index 1d86f5e63f..c80e082cb9 100644 --- a/vendor/github.com/prometheus/procfs/internal/util/sysreadfile_compat.go +++ b/vendor/github.com/prometheus/procfs/internal/util/sysreadfile_compat.go @@ -1,4 +1,4 @@ -// Copyright 2019 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -12,7 +12,6 @@ // limitations under the License. //go:build (linux && appengine) || (!linux && !darwin) -// +build linux,appengine !linux,!darwin package util diff --git a/vendor/github.com/prometheus/procfs/internal/util/valueparser.go b/vendor/github.com/prometheus/procfs/internal/util/valueparser.go index fe2355d3c6..e0ed671ea0 100644 --- a/vendor/github.com/prometheus/procfs/internal/util/valueparser.go +++ b/vendor/github.com/prometheus/procfs/internal/util/valueparser.go @@ -1,4 +1,4 @@ -// Copyright 2019 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/procfs/ipvs.go b/vendor/github.com/prometheus/procfs/ipvs.go index bc3a20c932..5374da9fa8 100644 --- a/vendor/github.com/prometheus/procfs/ipvs.go +++ b/vendor/github.com/prometheus/procfs/ipvs.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/procfs/kernel_hung.go b/vendor/github.com/prometheus/procfs/kernel_hung.go new file mode 100644 index 0000000000..0c7a69f99f --- /dev/null +++ b/vendor/github.com/prometheus/procfs/kernel_hung.go @@ -0,0 +1,44 @@ +// Copyright The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !windows + +package procfs + +import ( + "os" + "strconv" + "strings" +) + +// KernelHung contains information about to the kernel's hung_task_detect_count number. +type KernelHung struct { + // Indicates the total number of tasks that have been detected as hung since the system boot. + // This file shows up if `CONFIG_DETECT_HUNG_TASK` is enabled. + HungTaskDetectCount *uint64 +} + +// KernelHung returns values from /proc/sys/kernel/hung_task_detect_count. +func (fs FS) KernelHung() (KernelHung, error) { + data, err := os.ReadFile(fs.proc.Path("sys", "kernel", "hung_task_detect_count")) + if err != nil { + return KernelHung{}, err + } + val, err := strconv.ParseUint(strings.TrimSpace(string(data)), 10, 64) + if err != nil { + return KernelHung{}, err + } + return KernelHung{ + HungTaskDetectCount: &val, + }, nil +} diff --git a/vendor/github.com/prometheus/procfs/kernel_random.go b/vendor/github.com/prometheus/procfs/kernel_random.go index db88566bdf..e7c5b8cf2b 100644 --- a/vendor/github.com/prometheus/procfs/kernel_random.go +++ b/vendor/github.com/prometheus/procfs/kernel_random.go @@ -1,4 +1,4 @@ -// Copyright 2020 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -12,7 +12,6 @@ // limitations under the License. //go:build !windows -// +build !windows package procfs diff --git a/vendor/github.com/prometheus/procfs/loadavg.go b/vendor/github.com/prometheus/procfs/loadavg.go index 332e76c17f..c8c78a65ed 100644 --- a/vendor/github.com/prometheus/procfs/loadavg.go +++ b/vendor/github.com/prometheus/procfs/loadavg.go @@ -1,4 +1,4 @@ -// Copyright 2019 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/procfs/mdstat.go b/vendor/github.com/prometheus/procfs/mdstat.go index 67a9d2b448..d66eeda82a 100644 --- a/vendor/github.com/prometheus/procfs/mdstat.go +++ b/vendor/github.com/prometheus/procfs/mdstat.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -27,13 +27,34 @@ var ( recoveryLinePctRE = regexp.MustCompile(`= (.+)%`) recoveryLineFinishRE = regexp.MustCompile(`finish=(.+)min`) recoveryLineSpeedRE = regexp.MustCompile(`speed=(.+)[A-Z]`) - componentDeviceRE = regexp.MustCompile(`(.*)\[\d+\]`) + componentDeviceRE = regexp.MustCompile(`(.*)\[(\d+)\](\([SF]+\))?`) + personalitiesPrefix = "Personalities : " ) +type MDStatComponent struct { + // Name of the component device. + Name string + // DescriptorIndex number of component device, e.g. the order in the superblock. + DescriptorIndex int32 + // Flags per Linux drivers/md/md.[ch] as of v6.12-rc1 + // Subset that are exposed in mdstat + WriteMostly bool + Journal bool + Faulty bool // "Faulty" is what kernel source uses for "(F)" + Spare bool + Replacement bool + // Some additional flags that are NOT exposed in procfs today; they may + // be available via sysfs. + // In_sync, Bitmap_sync, Blocked, WriteErrorSeen, FaultRecorded, + // BlockedBadBlocks, WantReplacement, Candidate, ... +} + // MDStat holds info parsed from /proc/mdstat. type MDStat struct { // Name of the device. Name string + // raid type of the device. + Type string // activity-state of the device. ActivityState string // Number of active disks. @@ -58,8 +79,8 @@ type MDStat struct { BlocksSyncedFinishTime float64 // current sync speed (in Kilobytes/sec) BlocksSyncedSpeed float64 - // Name of md component devices - Devices []string + // component devices + Devices []MDStatComponent } // MDStat parses an mdstat-file (/proc/mdstat) and returns a slice of @@ -80,28 +101,52 @@ func (fs FS) MDStat() ([]MDStat, error) { // parseMDStat parses data from mdstat file (/proc/mdstat) and returns a slice of // structs containing the relevant info. func parseMDStat(mdStatData []byte) ([]MDStat, error) { + // TODO: + // - parse global hotspares from the "unused devices" line. mdStats := []MDStat{} lines := strings.Split(string(mdStatData), "\n") + knownRaidTypes := make(map[string]bool) for i, line := range lines { if strings.TrimSpace(line) == "" || line[0] == ' ' || - strings.HasPrefix(line, "Personalities") || strings.HasPrefix(line, "unused") { continue } + // Personalities : [linear] [multipath] [raid0] [raid1] [raid6] [raid5] [raid4] [raid10] + if len(knownRaidTypes) == 0 && strings.HasPrefix(line, personalitiesPrefix) { + personalities := strings.Fields(line[len(personalitiesPrefix):]) + for _, word := range personalities { + word := word[1 : len(word)-1] + knownRaidTypes[word] = true + } + continue + } deviceFields := strings.Fields(line) if len(deviceFields) < 3 { return nil, fmt.Errorf("%w: Expected 3+ lines, got %q", ErrFileParse, line) } mdName := deviceFields[0] // mdx - state := deviceFields[2] // active or inactive + state := deviceFields[2] // active, inactive, broken + + mdType := "unknown" // raid1, raid5, etc. + var deviceStartIndex int + if len(deviceFields) > 3 { // mdType may be in the 3rd or 4th field + if isRaidType(deviceFields[3], knownRaidTypes) { + mdType = deviceFields[3] + deviceStartIndex = 4 + } else if len(deviceFields) > 4 && isRaidType(deviceFields[4], knownRaidTypes) { + // if the 3rd field is (...), the 4th field is the mdType + mdType = deviceFields[4] + deviceStartIndex = 5 + } + } if len(lines) <= i+3 { return nil, fmt.Errorf("%w: Too few lines for md device: %q", ErrFileParse, mdName) } - // Failed disks have the suffix (F) & Spare disks have the suffix (S). + // Failed (Faulty) disks have the suffix (F) & Spare disks have the suffix (S). fail := int64(strings.Count(line, "(F)")) spare := int64(strings.Count(line, "(S)")) active, total, down, size, err := evalStatusLine(lines[i], lines[i+1]) @@ -123,16 +168,20 @@ func parseMDStat(mdStatData []byte) ([]MDStat, error) { finish := float64(0) pct := float64(0) recovering := strings.Contains(lines[syncLineIdx], "recovery") + reshaping := strings.Contains(lines[syncLineIdx], "reshape") resyncing := strings.Contains(lines[syncLineIdx], "resync") checking := strings.Contains(lines[syncLineIdx], "check") // Append recovery and resyncing state info. - if recovering || resyncing || checking { - if recovering { + if recovering || resyncing || checking || reshaping { + switch { + case recovering: state = "recovering" - } else if checking { + case reshaping: + state = "reshaping" + case checking: state = "checking" - } else { + default: state = "resyncing" } @@ -148,8 +197,14 @@ func parseMDStat(mdStatData []byte) ([]MDStat, error) { } } + devices, err := evalComponentDevices(deviceFields[deviceStartIndex:]) + if err != nil { + return nil, fmt.Errorf("error parsing components in md device %q: %w", mdName, err) + } + mdStats = append(mdStats, MDStat{ Name: mdName, + Type: mdType, ActivityState: state, DisksActive: active, DisksFailed: fail, @@ -162,14 +217,24 @@ func parseMDStat(mdStatData []byte) ([]MDStat, error) { BlocksSyncedPct: pct, BlocksSyncedFinishTime: finish, BlocksSyncedSpeed: speed, - Devices: evalComponentDevices(deviceFields), + Devices: devices, }) } return mdStats, nil } +// check if a string's format is like the mdType +// Rule 1: mdType should not be like (...) +// Rule 2: mdType should not be like sda[0] +// . +func isRaidType(mdType string, knownRaidTypes map[string]bool) bool { + _, ok := knownRaidTypes[mdType] + return !strings.ContainsAny(mdType, "([") && ok +} + func evalStatusLine(deviceLine, statusLine string) (active, total, down, size int64, err error) { + // e.g. 523968 blocks super 1.2 [4/4] [UUUU] statusFields := strings.Fields(statusLine) if len(statusFields) < 1 { return 0, 0, 0, 0, fmt.Errorf("%w: Unexpected statusline %q: %w", ErrFileParse, statusLine, err) @@ -260,17 +325,29 @@ func evalRecoveryLine(recoveryLine string) (blocksSynced int64, blocksToBeSynced return blocksSynced, blocksToBeSynced, pct, finish, speed, nil } -func evalComponentDevices(deviceFields []string) []string { - mdComponentDevices := make([]string, 0) - if len(deviceFields) > 3 { - for _, field := range deviceFields[4:] { - match := componentDeviceRE.FindStringSubmatch(field) - if match == nil { - continue - } - mdComponentDevices = append(mdComponentDevices, match[1]) +func evalComponentDevices(deviceFields []string) ([]MDStatComponent, error) { + mdComponentDevices := make([]MDStatComponent, 0) + for _, field := range deviceFields { + match := componentDeviceRE.FindStringSubmatch(field) + if match == nil { + continue + } + descriptorIndex, err := strconv.ParseInt(match[2], 10, 32) + if err != nil { + return mdComponentDevices, fmt.Errorf("error parsing int from device %q: %w", match[2], err) } + mdComponentDevices = append(mdComponentDevices, MDStatComponent{ + Name: match[1], + DescriptorIndex: int32(descriptorIndex), + // match may contain one or more of these + // https://github.com/torvalds/linux/blob/7ec462100ef9142344ddbf86f2c3008b97acddbe/drivers/md/md.c#L8376-L8392 + Faulty: strings.Contains(match[3], "(F)"), + Spare: strings.Contains(match[3], "(S)"), + Journal: strings.Contains(match[3], "(J)"), + Replacement: strings.Contains(match[3], "(R)"), + WriteMostly: strings.Contains(match[3], "(W)"), + }) } - return mdComponentDevices + return mdComponentDevices, nil } diff --git a/vendor/github.com/prometheus/procfs/meminfo.go b/vendor/github.com/prometheus/procfs/meminfo.go index 4b2c4050a3..3420383187 100644 --- a/vendor/github.com/prometheus/procfs/meminfo.go +++ b/vendor/github.com/prometheus/procfs/meminfo.go @@ -1,4 +1,4 @@ -// Copyright 2019 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -66,6 +66,10 @@ type Meminfo struct { // Memory which has been evicted from RAM, and is temporarily // on the disk SwapFree *uint64 + // Memory consumed by the zswap backend (compressed size) + Zswap *uint64 + // Amount of anonymous memory stored in zswap (original size) + Zswapped *uint64 // Memory which is waiting to get written back to the disk Dirty *uint64 // Memory which is actively being written back to the disk @@ -85,6 +89,8 @@ type Meminfo struct { // amount of memory dedicated to the lowest level of page // tables. PageTables *uint64 + // secondary page tables. + SecPageTables *uint64 // NFS pages sent to the server, but not yet committed to // stable storage NFSUnstable *uint64 @@ -129,15 +135,18 @@ type Meminfo struct { Percpu *uint64 HardwareCorrupted *uint64 AnonHugePages *uint64 + FileHugePages *uint64 ShmemHugePages *uint64 ShmemPmdMapped *uint64 CmaTotal *uint64 CmaFree *uint64 + Unaccepted *uint64 HugePagesTotal *uint64 HugePagesFree *uint64 HugePagesRsvd *uint64 HugePagesSurp *uint64 Hugepagesize *uint64 + Hugetlb *uint64 DirectMap4k *uint64 DirectMap2M *uint64 DirectMap1G *uint64 @@ -161,6 +170,8 @@ type Meminfo struct { MlockedBytes *uint64 SwapTotalBytes *uint64 SwapFreeBytes *uint64 + ZswapBytes *uint64 + ZswappedBytes *uint64 DirtyBytes *uint64 WritebackBytes *uint64 AnonPagesBytes *uint64 @@ -171,6 +182,7 @@ type Meminfo struct { SUnreclaimBytes *uint64 KernelStackBytes *uint64 PageTablesBytes *uint64 + SecPageTablesBytes *uint64 NFSUnstableBytes *uint64 BounceBytes *uint64 WritebackTmpBytes *uint64 @@ -182,11 +194,14 @@ type Meminfo struct { PercpuBytes *uint64 HardwareCorruptedBytes *uint64 AnonHugePagesBytes *uint64 + FileHugePagesBytes *uint64 ShmemHugePagesBytes *uint64 ShmemPmdMappedBytes *uint64 CmaTotalBytes *uint64 CmaFreeBytes *uint64 + UnacceptedBytes *uint64 HugepagesizeBytes *uint64 + HugetlbBytes *uint64 DirectMap4kBytes *uint64 DirectMap2MBytes *uint64 DirectMap1GBytes *uint64 @@ -287,6 +302,12 @@ func parseMemInfo(r io.Reader) (*Meminfo, error) { case "SwapFree:": m.SwapFree = &val m.SwapFreeBytes = &valBytes + case "Zswap:": + m.Zswap = &val + m.ZswapBytes = &valBytes + case "Zswapped:": + m.Zswapped = &val + m.ZswappedBytes = &valBytes case "Dirty:": m.Dirty = &val m.DirtyBytes = &valBytes @@ -317,6 +338,9 @@ func parseMemInfo(r io.Reader) (*Meminfo, error) { case "PageTables:": m.PageTables = &val m.PageTablesBytes = &valBytes + case "SecPageTables:": + m.SecPageTables = &val + m.SecPageTablesBytes = &valBytes case "NFS_Unstable:": m.NFSUnstable = &val m.NFSUnstableBytes = &valBytes @@ -350,6 +374,9 @@ func parseMemInfo(r io.Reader) (*Meminfo, error) { case "AnonHugePages:": m.AnonHugePages = &val m.AnonHugePagesBytes = &valBytes + case "FileHugePages:": + m.FileHugePages = &val + m.FileHugePagesBytes = &valBytes case "ShmemHugePages:": m.ShmemHugePages = &val m.ShmemHugePagesBytes = &valBytes @@ -362,6 +389,9 @@ func parseMemInfo(r io.Reader) (*Meminfo, error) { case "CmaFree:": m.CmaFree = &val m.CmaFreeBytes = &valBytes + case "Unaccepted:": + m.Unaccepted = &val + m.UnacceptedBytes = &valBytes case "HugePages_Total:": m.HugePagesTotal = &val case "HugePages_Free:": @@ -373,6 +403,9 @@ func parseMemInfo(r io.Reader) (*Meminfo, error) { case "Hugepagesize:": m.Hugepagesize = &val m.HugepagesizeBytes = &valBytes + case "Hugetlb:": + m.Hugetlb = &val + m.HugetlbBytes = &valBytes case "DirectMap4k:": m.DirectMap4k = &val m.DirectMap4kBytes = &valBytes diff --git a/vendor/github.com/prometheus/procfs/mountinfo.go b/vendor/github.com/prometheus/procfs/mountinfo.go index a704c5e735..8594ae7f1e 100644 --- a/vendor/github.com/prometheus/procfs/mountinfo.go +++ b/vendor/github.com/prometheus/procfs/mountinfo.go @@ -1,4 +1,4 @@ -// Copyright 2019 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -17,10 +17,10 @@ import ( "bufio" "bytes" "fmt" + "io" + "os" "strconv" "strings" - - "github.com/prometheus/procfs/internal/util" ) // A MountInfo is a type that describes the details, options @@ -147,8 +147,7 @@ func mountOptionsParseOptionalFields(o []string) (map[string]string, error) { // mountOptionsParser parses the mount options, superblock options. func mountOptionsParser(mountOptions string) map[string]string { opts := make(map[string]string) - options := strings.Split(mountOptions, ",") - for _, opt := range options { + for opt := range strings.SplitSeq(mountOptions, ",") { splitOption := strings.Split(opt, "=") if len(splitOption) < 2 { key := splitOption[0] @@ -161,9 +160,19 @@ func mountOptionsParser(mountOptions string) map[string]string { return opts } +// readMountInfo reads a full mountinfo file (no 1 MiB cap, unlike util.ReadFileNoStat). +func readMountInfo(path string) ([]byte, error) { + f, err := os.Open(path) + if err != nil { + return nil, err + } + defer f.Close() + return io.ReadAll(f) +} + // GetMounts retrieves mountinfo information from `/proc/self/mountinfo`. func GetMounts() ([]*MountInfo, error) { - data, err := util.ReadFileNoStat("/proc/self/mountinfo") + data, err := readMountInfo("/proc/self/mountinfo") if err != nil { return nil, err } @@ -172,7 +181,25 @@ func GetMounts() ([]*MountInfo, error) { // GetProcMounts retrieves mountinfo information from a processes' `/proc//mountinfo`. func GetProcMounts(pid int) ([]*MountInfo, error) { - data, err := util.ReadFileNoStat(fmt.Sprintf("/proc/%d/mountinfo", pid)) + data, err := readMountInfo(fmt.Sprintf("/proc/%d/mountinfo", pid)) + if err != nil { + return nil, err + } + return parseMountInfo(data) +} + +// GetMounts retrieves mountinfo information from `/proc/self/mountinfo`. +func (fs FS) GetMounts() ([]*MountInfo, error) { + data, err := readMountInfo(fs.proc.Path("self/mountinfo")) + if err != nil { + return nil, err + } + return parseMountInfo(data) +} + +// GetProcMounts retrieves mountinfo information from a processes' `/proc//mountinfo`. +func (fs FS) GetProcMounts(pid int) ([]*MountInfo, error) { + data, err := readMountInfo(fs.proc.Path(fmt.Sprintf("%d/mountinfo", pid))) if err != nil { return nil, err } diff --git a/vendor/github.com/prometheus/procfs/mountstats.go b/vendor/github.com/prometheus/procfs/mountstats.go index 50caa73274..e503cb3a6c 100644 --- a/vendor/github.com/prometheus/procfs/mountstats.go +++ b/vendor/github.com/prometheus/procfs/mountstats.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -383,7 +383,7 @@ func parseMountStatsNFS(s *bufio.Scanner, statVersion string) (*MountStatsNFS, e if stats.Opts == nil { stats.Opts = map[string]string{} } - for _, opt := range strings.Split(ss[1], ",") { + for opt := range strings.SplitSeq(ss[1], ",") { split := strings.Split(opt, "=") if len(split) == 2 { stats.Opts[split[0]] = split[1] diff --git a/vendor/github.com/prometheus/procfs/net_conntrackstat.go b/vendor/github.com/prometheus/procfs/net_conntrackstat.go index 316df5fbb7..e9ca357079 100644 --- a/vendor/github.com/prometheus/procfs/net_conntrackstat.go +++ b/vendor/github.com/prometheus/procfs/net_conntrackstat.go @@ -1,4 +1,4 @@ -// Copyright 2020 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/procfs/net_dev.go b/vendor/github.com/prometheus/procfs/net_dev.go index e66208aa05..7b3e1d61c9 100644 --- a/vendor/github.com/prometheus/procfs/net_dev.go +++ b/vendor/github.com/prometheus/procfs/net_dev.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/procfs/net_dev_snmp6.go b/vendor/github.com/prometheus/procfs/net_dev_snmp6.go index f50b38e352..2a0f60f29f 100644 --- a/vendor/github.com/prometheus/procfs/net_dev_snmp6.go +++ b/vendor/github.com/prometheus/procfs/net_dev_snmp6.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -18,6 +18,7 @@ import ( "errors" "io" "os" + "path/filepath" "strconv" "strings" ) @@ -56,7 +57,9 @@ func newNetDevSNMP6(dir string) (NetDevSNMP6, error) { } for _, iFaceFile := range ifaceFiles { - f, err := os.Open(dir + "/" + iFaceFile.Name()) + filePath := filepath.Join(dir, iFaceFile.Name()) + + f, err := os.Open(filePath) if err != nil { return netDevSNMP6, err } diff --git a/vendor/github.com/prometheus/procfs/net_ip_socket.go b/vendor/github.com/prometheus/procfs/net_ip_socket.go index 19e3378f72..9291f8cd4c 100644 --- a/vendor/github.com/prometheus/procfs/net_ip_socket.go +++ b/vendor/github.com/prometheus/procfs/net_ip_socket.go @@ -1,4 +1,4 @@ -// Copyright 2020 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/procfs/net_protocols.go b/vendor/github.com/prometheus/procfs/net_protocols.go index 8d4b1ac05b..eaa996cbcf 100644 --- a/vendor/github.com/prometheus/procfs/net_protocols.go +++ b/vendor/github.com/prometheus/procfs/net_protocols.go @@ -1,4 +1,4 @@ -// Copyright 2020 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -169,7 +169,7 @@ func (pc *NetProtocolCapabilities) parseCapabilities(capabilities []string) erro &pc.EnterMemoryPressure, } - for i := 0; i < len(capabilities); i++ { + for i := range capabilities { switch capabilities[i] { case "y": *capabilityFields[i] = true diff --git a/vendor/github.com/prometheus/procfs/net_route.go b/vendor/github.com/prometheus/procfs/net_route.go index deb7029fe1..fa3812d9d0 100644 --- a/vendor/github.com/prometheus/procfs/net_route.go +++ b/vendor/github.com/prometheus/procfs/net_route.go @@ -1,4 +1,4 @@ -// Copyright 2023 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/procfs/net_sockstat.go b/vendor/github.com/prometheus/procfs/net_sockstat.go index fae62b13d9..8b221ebfff 100644 --- a/vendor/github.com/prometheus/procfs/net_sockstat.go +++ b/vendor/github.com/prometheus/procfs/net_sockstat.go @@ -1,4 +1,4 @@ -// Copyright 2019 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -139,9 +139,6 @@ func parseSockstatKVs(kvs []string) (map[string]int, error) { func parseSockstatProtocol(kvs map[string]int) NetSockstatProtocol { var nsp NetSockstatProtocol for k, v := range kvs { - // Capture the range variable to ensure we get unique pointers for - // each of the optional fields. - v := v switch k { case "inuse": nsp.InUse = v diff --git a/vendor/github.com/prometheus/procfs/net_softnet.go b/vendor/github.com/prometheus/procfs/net_softnet.go index 71c8059f4d..4a2dfa18fd 100644 --- a/vendor/github.com/prometheus/procfs/net_softnet.go +++ b/vendor/github.com/prometheus/procfs/net_softnet.go @@ -1,4 +1,4 @@ -// Copyright 2019 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/procfs/net_tcp.go b/vendor/github.com/prometheus/procfs/net_tcp.go index 0396d72015..2c7f9bc7c3 100644 --- a/vendor/github.com/prometheus/procfs/net_tcp.go +++ b/vendor/github.com/prometheus/procfs/net_tcp.go @@ -1,4 +1,4 @@ -// Copyright 2020 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -25,6 +25,7 @@ type ( // NetTCP returns the IPv4 kernel/networking statistics for TCP datagrams // read from /proc/net/tcp. +// // Deprecated: Use github.com/mdlayher/netlink#Conn (with syscall.AF_INET) instead. func (fs FS) NetTCP() (NetTCP, error) { return newNetTCP(fs.proc.Path("net/tcp")) @@ -32,6 +33,7 @@ func (fs FS) NetTCP() (NetTCP, error) { // NetTCP6 returns the IPv6 kernel/networking statistics for TCP datagrams // read from /proc/net/tcp6. +// // Deprecated: Use github.com/mdlayher/netlink#Conn (with syscall.AF_INET6) instead. func (fs FS) NetTCP6() (NetTCP, error) { return newNetTCP(fs.proc.Path("net/tcp6")) @@ -39,6 +41,7 @@ func (fs FS) NetTCP6() (NetTCP, error) { // NetTCPSummary returns already computed statistics like the total queue lengths // for TCP datagrams read from /proc/net/tcp. +// // Deprecated: Use github.com/mdlayher/netlink#Conn (with syscall.AF_INET) instead. func (fs FS) NetTCPSummary() (*NetTCPSummary, error) { return newNetTCPSummary(fs.proc.Path("net/tcp")) @@ -46,6 +49,7 @@ func (fs FS) NetTCPSummary() (*NetTCPSummary, error) { // NetTCP6Summary returns already computed statistics like the total queue lengths // for TCP datagrams read from /proc/net/tcp6. +// // Deprecated: Use github.com/mdlayher/netlink#Conn (with syscall.AF_INET6) instead. func (fs FS) NetTCP6Summary() (*NetTCPSummary, error) { return newNetTCPSummary(fs.proc.Path("net/tcp6")) diff --git a/vendor/github.com/prometheus/procfs/net_tls_stat.go b/vendor/github.com/prometheus/procfs/net_tls_stat.go index 13994c1782..b1b3f6a6a2 100644 --- a/vendor/github.com/prometheus/procfs/net_tls_stat.go +++ b/vendor/github.com/prometheus/procfs/net_tls_stat.go @@ -1,4 +1,4 @@ -// Copyright 2023 Prometheus Team +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/procfs/net_udp.go b/vendor/github.com/prometheus/procfs/net_udp.go index 9ac3daf2d4..8a32779102 100644 --- a/vendor/github.com/prometheus/procfs/net_udp.go +++ b/vendor/github.com/prometheus/procfs/net_udp.go @@ -1,4 +1,4 @@ -// Copyright 2020 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/procfs/net_unix.go b/vendor/github.com/prometheus/procfs/net_unix.go index d7e0cacb4c..e4d6359236 100644 --- a/vendor/github.com/prometheus/procfs/net_unix.go +++ b/vendor/github.com/prometheus/procfs/net_unix.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/procfs/net_wireless.go b/vendor/github.com/prometheus/procfs/net_wireless.go index 7c597bc870..f74dd3bed0 100644 --- a/vendor/github.com/prometheus/procfs/net_wireless.go +++ b/vendor/github.com/prometheus/procfs/net_wireless.go @@ -1,4 +1,4 @@ -// Copyright 2023 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -114,47 +114,47 @@ func parseWireless(r io.Reader) ([]*Wireless, error) { qlink, err := strconv.Atoi(strings.TrimSuffix(stats[1], ".")) if err != nil { - return nil, fmt.Errorf("%w: parse Quality:link as integer %q: %w", ErrFileParse, qlink, err) + return nil, fmt.Errorf("%w: parse Quality:link as integer %q: %w", ErrFileParse, stats[1], err) } qlevel, err := strconv.Atoi(strings.TrimSuffix(stats[2], ".")) if err != nil { - return nil, fmt.Errorf("%w: Quality:level as integer %q: %w", ErrFileParse, qlevel, err) + return nil, fmt.Errorf("%w: Quality:level as integer %q: %w", ErrFileParse, stats[2], err) } qnoise, err := strconv.Atoi(strings.TrimSuffix(stats[3], ".")) if err != nil { - return nil, fmt.Errorf("%w: Quality:noise as integer %q: %w", ErrFileParse, qnoise, err) + return nil, fmt.Errorf("%w: Quality:noise as integer %q: %w", ErrFileParse, stats[3], err) } dnwid, err := strconv.Atoi(stats[4]) if err != nil { - return nil, fmt.Errorf("%w: Discarded:nwid as integer %q: %w", ErrFileParse, dnwid, err) + return nil, fmt.Errorf("%w: Discarded:nwid as integer %q: %w", ErrFileParse, stats[4], err) } dcrypt, err := strconv.Atoi(stats[5]) if err != nil { - return nil, fmt.Errorf("%w: Discarded:crypt as integer %q: %w", ErrFileParse, dcrypt, err) + return nil, fmt.Errorf("%w: Discarded:crypt as integer %q: %w", ErrFileParse, stats[5], err) } dfrag, err := strconv.Atoi(stats[6]) if err != nil { - return nil, fmt.Errorf("%w: Discarded:frag as integer %q: %w", ErrFileParse, dfrag, err) + return nil, fmt.Errorf("%w: Discarded:frag as integer %q: %w", ErrFileParse, stats[6], err) } dretry, err := strconv.Atoi(stats[7]) if err != nil { - return nil, fmt.Errorf("%w: Discarded:retry as integer %q: %w", ErrFileParse, dretry, err) + return nil, fmt.Errorf("%w: Discarded:retry as integer %q: %w", ErrFileParse, stats[7], err) } dmisc, err := strconv.Atoi(stats[8]) if err != nil { - return nil, fmt.Errorf("%w: Discarded:misc as integer %q: %w", ErrFileParse, dmisc, err) + return nil, fmt.Errorf("%w: Discarded:misc as integer %q: %w", ErrFileParse, stats[8], err) } mbeacon, err := strconv.Atoi(stats[9]) if err != nil { - return nil, fmt.Errorf("%w: Missed:beacon as integer %q: %w", ErrFileParse, mbeacon, err) + return nil, fmt.Errorf("%w: Missed:beacon as integer %q: %w", ErrFileParse, stats[9], err) } w := &Wireless{ diff --git a/vendor/github.com/prometheus/procfs/net_xfrm.go b/vendor/github.com/prometheus/procfs/net_xfrm.go index 932ef20468..5a9f497d19 100644 --- a/vendor/github.com/prometheus/procfs/net_xfrm.go +++ b/vendor/github.com/prometheus/procfs/net_xfrm.go @@ -1,4 +1,4 @@ -// Copyright 2017 Prometheus Team +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/procfs/netstat.go b/vendor/github.com/prometheus/procfs/netstat.go index 742dff453b..dbdae47392 100644 --- a/vendor/github.com/prometheus/procfs/netstat.go +++ b/vendor/github.com/prometheus/procfs/netstat.go @@ -1,4 +1,4 @@ -// Copyright 2020 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/procfs/nfnetlink_queue.go b/vendor/github.com/prometheus/procfs/nfnetlink_queue.go new file mode 100644 index 0000000000..b0a73b11e9 --- /dev/null +++ b/vendor/github.com/prometheus/procfs/nfnetlink_queue.go @@ -0,0 +1,85 @@ +// Copyright The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package procfs + +import ( + "bufio" + "bytes" + "fmt" + + "github.com/prometheus/procfs/internal/util" +) + +const nfNetLinkQueueFormat = "%d %d %d %d %d %d %d %d %d" + +// NFNetLinkQueue contains general information about netfilter queues found in /proc/net/netfilter/nfnetlink_queue. +type NFNetLinkQueue struct { + // id of the queue + QueueID uint + // pid of process handling the queue + PeerPID uint + // number of packets waiting for a decision + QueueTotal uint + // indicate how userspace receive packets + CopyMode uint + // size of copy + CopyRange uint + // number of items dropped by the kernel because too many packets were waiting a decision. + // It queue_total is superior to queue_max_len (1024 per default) the packets are dropped. + QueueDropped uint + // number of packets dropped by userspace (due to kernel send failure on the netlink socket) + QueueUserDropped uint + // sequence number of packets queued. It gives a correct approximation of the number of queued packets. + SequenceID uint + // internal value (number of entity using the queue) + Use uint +} + +// NFNetLinkQueue returns information about current state of netfilter queues. +func (fs FS) NFNetLinkQueue() ([]NFNetLinkQueue, error) { + data, err := util.ReadFileNoStat(fs.proc.Path("net/netfilter/nfnetlink_queue")) + if err != nil { + return nil, err + } + + queue := []NFNetLinkQueue{} + if len(data) == 0 { + return queue, nil + } + + scanner := bufio.NewScanner(bytes.NewReader(data)) + for scanner.Scan() { + line := scanner.Text() + nFNetLinkQueue, err := parseNFNetLinkQueueLine(line) + if err != nil { + return nil, err + } + queue = append(queue, *nFNetLinkQueue) + } + return queue, nil +} + +// parseNFNetLinkQueueLine parses each line of the /proc/net/netfilter/nfnetlink_queue file. +func parseNFNetLinkQueueLine(line string) (*NFNetLinkQueue, error) { + nFNetLinkQueue := NFNetLinkQueue{} + _, err := fmt.Sscanf( + line, nfNetLinkQueueFormat, + &nFNetLinkQueue.QueueID, &nFNetLinkQueue.PeerPID, &nFNetLinkQueue.QueueTotal, &nFNetLinkQueue.CopyMode, + &nFNetLinkQueue.CopyRange, &nFNetLinkQueue.QueueDropped, &nFNetLinkQueue.QueueUserDropped, &nFNetLinkQueue.SequenceID, &nFNetLinkQueue.Use, + ) + if err != nil { + return nil, err + } + return &nFNetLinkQueue, nil +} diff --git a/vendor/github.com/prometheus/procfs/proc.go b/vendor/github.com/prometheus/procfs/proc.go index 368187fa88..39c14aa55e 100644 --- a/vendor/github.com/prometheus/procfs/proc.go +++ b/vendor/github.com/prometheus/procfs/proc.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -49,7 +49,7 @@ func (p Procs) Less(i, j int) bool { return p[i].PID < p[j].PID } // Self returns a process for the current process read via /proc/self. func Self() (Proc, error) { fs, err := NewFS(DefaultMountPoint) - if err != nil || errors.Unwrap(err) == ErrMountPoint { + if err != nil || errors.Is(err, ErrMountPoint) { return Proc{}, err } return fs.Self() diff --git a/vendor/github.com/prometheus/procfs/proc_cgroup.go b/vendor/github.com/prometheus/procfs/proc_cgroup.go index 4a64347c03..7e8a122978 100644 --- a/vendor/github.com/prometheus/procfs/proc_cgroup.go +++ b/vendor/github.com/prometheus/procfs/proc_cgroup.go @@ -1,4 +1,4 @@ -// Copyright 2020 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -60,7 +60,7 @@ func parseCgroupString(cgroupStr string) (*Cgroup, error) { } cgroup.HierarchyID, err = strconv.Atoi(fields[0]) if err != nil { - return nil, fmt.Errorf("%w: hierarchy ID: %q", ErrFileParse, cgroup.HierarchyID) + return nil, fmt.Errorf("%w: hierarchy ID: %q", ErrFileParse, fields[0]) } if fields[1] != "" { ssNames := strings.Split(fields[1], ",") diff --git a/vendor/github.com/prometheus/procfs/proc_cgroups.go b/vendor/github.com/prometheus/procfs/proc_cgroups.go index 5dd4938999..0b275c3b1f 100644 --- a/vendor/github.com/prometheus/procfs/proc_cgroups.go +++ b/vendor/github.com/prometheus/procfs/proc_cgroups.go @@ -1,4 +1,4 @@ -// Copyright 2021 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -40,13 +40,13 @@ type CgroupSummary struct { // parseCgroupSummary parses each line of the /proc/cgroup file // Line format is `subsys_name hierarchy num_cgroups enabled`. -func parseCgroupSummaryString(CgroupSummaryStr string) (*CgroupSummary, error) { +func parseCgroupSummaryString(cgroupSummaryStr string) (*CgroupSummary, error) { var err error - fields := strings.Fields(CgroupSummaryStr) + fields := strings.Fields(cgroupSummaryStr) // require at least 4 fields if len(fields) < 4 { - return nil, fmt.Errorf("%w: 4+ fields required, found %d fields in cgroup info string: %s", ErrFileParse, len(fields), CgroupSummaryStr) + return nil, fmt.Errorf("%w: 4+ fields required, found %d fields in cgroup info string: %s", ErrFileParse, len(fields), cgroupSummaryStr) } CgroupSummary := &CgroupSummary{ diff --git a/vendor/github.com/prometheus/procfs/proc_environ.go b/vendor/github.com/prometheus/procfs/proc_environ.go index 57a89895d6..5b941de047 100644 --- a/vendor/github.com/prometheus/procfs/proc_environ.go +++ b/vendor/github.com/prometheus/procfs/proc_environ.go @@ -1,4 +1,4 @@ -// Copyright 2019 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/procfs/proc_fdinfo.go b/vendor/github.com/prometheus/procfs/proc_fdinfo.go index fa761b3529..fa57761dbe 100644 --- a/vendor/github.com/prometheus/procfs/proc_fdinfo.go +++ b/vendor/github.com/prometheus/procfs/proc_fdinfo.go @@ -1,4 +1,4 @@ -// Copyright 2019 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -60,15 +60,16 @@ func (p Proc) FDInfo(fd string) (*ProcFDInfo, error) { scanner := bufio.NewScanner(bytes.NewReader(data)) for scanner.Scan() { text = scanner.Text() - if rPos.MatchString(text) { + switch { + case rPos.MatchString(text): pos = rPos.FindStringSubmatch(text)[1] - } else if rFlags.MatchString(text) { + case rFlags.MatchString(text): flags = rFlags.FindStringSubmatch(text)[1] - } else if rMntID.MatchString(text) { + case rMntID.MatchString(text): mntid = rMntID.FindStringSubmatch(text)[1] - } else if rIno.MatchString(text) { + case rIno.MatchString(text): ino = rIno.FindStringSubmatch(text)[1] - } else if rInotify.MatchString(text) { + case rInotify.MatchString(text): newInotify, err := parseInotifyInfo(text) if err != nil { return nil, err diff --git a/vendor/github.com/prometheus/procfs/proc_interrupts.go b/vendor/github.com/prometheus/procfs/proc_interrupts.go index 86b4b45246..643b500d5d 100644 --- a/vendor/github.com/prometheus/procfs/proc_interrupts.go +++ b/vendor/github.com/prometheus/procfs/proc_interrupts.go @@ -1,4 +1,4 @@ -// Copyright 2022 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -42,7 +42,7 @@ type Interrupts map[string]Interrupt // Interrupts creates a new instance from a given Proc instance. func (p Proc) Interrupts() (Interrupts, error) { - data, err := util.ReadFileNoStat(p.path("interrupts")) + data, err := util.ReadFileNoStat(p.fs.proc.Path("interrupts")) if err != nil { return nil, err } diff --git a/vendor/github.com/prometheus/procfs/proc_io.go b/vendor/github.com/prometheus/procfs/proc_io.go index d15b66ddb6..dd8086ba2e 100644 --- a/vendor/github.com/prometheus/procfs/proc_io.go +++ b/vendor/github.com/prometheus/procfs/proc_io.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/procfs/proc_limits.go b/vendor/github.com/prometheus/procfs/proc_limits.go index 9530b14bc6..4b7d337847 100644 --- a/vendor/github.com/prometheus/procfs/proc_limits.go +++ b/vendor/github.com/prometheus/procfs/proc_limits.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -19,6 +19,7 @@ import ( "os" "regexp" "strconv" + "strings" ) // ProcLimits represents the soft limits for each of the process's resource @@ -74,7 +75,7 @@ const ( ) var ( - limitsMatch = regexp.MustCompile(`(Max \w+\s{0,1}?\w*\s{0,1}\w*)\s{2,}(\w+)\s+(\w+)`) + limitsMatch = regexp.MustCompile(`(Max \w+\s??\w*\s?\w*)\s{2,}(\w+)\s+(\w+)`) ) // NewLimits returns the current soft limits of the process. @@ -106,7 +107,7 @@ func (p Proc) Limits() (ProcLimits, error) { return ProcLimits{}, fmt.Errorf("%w: couldn't parse %q line %q", ErrFileParse, f.Name(), s.Text()) } - switch fields[1] { + switch strings.TrimSpace(fields[1]) { case "Max cpu time": l.CPUTime, err = parseUint(fields[2]) case "Max file size": diff --git a/vendor/github.com/prometheus/procfs/proc_maps.go b/vendor/github.com/prometheus/procfs/proc_maps.go index 7e75c286b5..08b89a6eb9 100644 --- a/vendor/github.com/prometheus/procfs/proc_maps.go +++ b/vendor/github.com/prometheus/procfs/proc_maps.go @@ -1,4 +1,4 @@ -// Copyright 2019 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -12,8 +12,6 @@ // limitations under the License. //go:build (aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris) && !js -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris -// +build !js package procfs diff --git a/vendor/github.com/prometheus/procfs/proc_netstat.go b/vendor/github.com/prometheus/procfs/proc_netstat.go index 4248c1716e..7f94cc8914 100644 --- a/vendor/github.com/prometheus/procfs/proc_netstat.go +++ b/vendor/github.com/prometheus/procfs/proc_netstat.go @@ -1,4 +1,4 @@ -// Copyright 2022 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/procfs/proc_ns.go b/vendor/github.com/prometheus/procfs/proc_ns.go index 0f8f847f95..5fc0eb9e2f 100644 --- a/vendor/github.com/prometheus/procfs/proc_ns.go +++ b/vendor/github.com/prometheus/procfs/proc_ns.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/procfs/proc_psi.go b/vendor/github.com/prometheus/procfs/proc_psi.go index ccd35f153a..cc2c5de873 100644 --- a/vendor/github.com/prometheus/procfs/proc_psi.go +++ b/vendor/github.com/prometheus/procfs/proc_psi.go @@ -1,4 +1,4 @@ -// Copyright 2019 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/procfs/proc_smaps.go b/vendor/github.com/prometheus/procfs/proc_smaps.go index 9a297afcf8..f637309b3d 100644 --- a/vendor/github.com/prometheus/procfs/proc_smaps.go +++ b/vendor/github.com/prometheus/procfs/proc_smaps.go @@ -1,4 +1,4 @@ -// Copyright 2020 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -12,7 +12,6 @@ // limitations under the License. //go:build !windows -// +build !windows package procfs diff --git a/vendor/github.com/prometheus/procfs/proc_snmp.go b/vendor/github.com/prometheus/procfs/proc_snmp.go index 4bdc90b07e..8d9a9bcd67 100644 --- a/vendor/github.com/prometheus/procfs/proc_snmp.go +++ b/vendor/github.com/prometheus/procfs/proc_snmp.go @@ -1,4 +1,4 @@ -// Copyright 2022 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/procfs/proc_snmp6.go b/vendor/github.com/prometheus/procfs/proc_snmp6.go index fb7fd3995b..841fef4649 100644 --- a/vendor/github.com/prometheus/procfs/proc_snmp6.go +++ b/vendor/github.com/prometheus/procfs/proc_snmp6.go @@ -1,4 +1,4 @@ -// Copyright 2022 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/procfs/proc_stat.go b/vendor/github.com/prometheus/procfs/proc_stat.go index 06a8d931c9..02e3f9e316 100644 --- a/vendor/github.com/prometheus/procfs/proc_stat.go +++ b/vendor/github.com/prometheus/procfs/proc_stat.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -101,6 +101,12 @@ type ProcStat struct { RSS int // Soft limit in bytes on the rss of the process. RSSLimit uint64 + // The address above which program text can run. + StartCode uint64 + // The address below which program text can run. + EndCode uint64 + // The address of the start (i.e., bottom) of the stack. + StartStack uint64 // CPU number last executed on. Processor uint // Real-time scheduling priority, a number in the range 1 to 99 for processes @@ -177,9 +183,9 @@ func (p Proc) Stat() (ProcStat, error) { &s.VSize, &s.RSS, &s.RSSLimit, - &ignoreUint64, - &ignoreUint64, - &ignoreUint64, + &s.StartCode, + &s.EndCode, + &s.StartStack, &ignoreUint64, &ignoreUint64, &ignoreUint64, diff --git a/vendor/github.com/prometheus/procfs/proc_statm.go b/vendor/github.com/prometheus/procfs/proc_statm.go new file mode 100644 index 0000000000..6bcc97ec9c --- /dev/null +++ b/vendor/github.com/prometheus/procfs/proc_statm.go @@ -0,0 +1,117 @@ +// Copyright The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package procfs + +import ( + "os" + "strconv" + "strings" + + "github.com/prometheus/procfs/internal/util" +) + +// - https://man7.org/linux/man-pages/man5/proc_pid_statm.5.html + +// ProcStatm Provides memory usage information for a process, measured in memory pages. +// Read from /proc/[pid]/statm. +type ProcStatm struct { + // The process ID. + PID int + // total program size (same as VmSize in status) + Size uint64 + // resident set size (same as VmRSS in status) + Resident uint64 + // number of resident shared pages (i.e., backed by a file) + Shared uint64 + // text (code) + Text uint64 + // library (unused since Linux 2.6; always 0) + Lib uint64 + // data + stack + Data uint64 + // dirty pages (unused since Linux 2.6; always 0) + Dt uint64 +} + +// NewStatm returns the current status information of the process. +// +// Deprecated: Use p.Statm() instead. +func (p Proc) NewStatm() (ProcStatm, error) { + return p.Statm() +} + +// Statm returns the current memory usage information of the process. +func (p Proc) Statm() (ProcStatm, error) { + data, err := util.ReadFileNoStat(p.path("statm")) + if err != nil { + return ProcStatm{}, err + } + + statmSlice, err := parseStatm(data) + if err != nil { + return ProcStatm{}, err + } + + procStatm := ProcStatm{ + PID: p.PID, + Size: statmSlice[0], + Resident: statmSlice[1], + Shared: statmSlice[2], + Text: statmSlice[3], + Lib: statmSlice[4], + Data: statmSlice[5], + Dt: statmSlice[6], + } + + return procStatm, nil +} + +// parseStatm return /proc/[pid]/statm data to uint64 slice. +func parseStatm(data []byte) ([]uint64, error) { + var statmSlice []uint64 + statmItems := strings.Fields(string(data)) + for i := range statmItems { + statmItem, err := strconv.ParseUint(statmItems[i], 10, 64) + if err != nil { + return nil, err + } + statmSlice = append(statmSlice, statmItem) + } + return statmSlice, nil +} + +// SizeBytes returns the process of total program size in bytes. +func (s ProcStatm) SizeBytes() uint64 { + return s.Size * uint64(os.Getpagesize()) +} + +// ResidentBytes returns the process of resident set size in bytes. +func (s ProcStatm) ResidentBytes() uint64 { + return s.Resident * uint64(os.Getpagesize()) +} + +// SHRBytes returns the process of share memory size in bytes. +func (s ProcStatm) SHRBytes() uint64 { + return s.Shared * uint64(os.Getpagesize()) +} + +// TextBytes returns the process of text (code) size in bytes. +func (s ProcStatm) TextBytes() uint64 { + return s.Text * uint64(os.Getpagesize()) +} + +// DataBytes returns the process of data + stack size in bytes. +func (s ProcStatm) DataBytes() uint64 { + return s.Data * uint64(os.Getpagesize()) +} diff --git a/vendor/github.com/prometheus/procfs/proc_status.go b/vendor/github.com/prometheus/procfs/proc_status.go index dd8aa56885..12d65581c8 100644 --- a/vendor/github.com/prometheus/procfs/proc_status.go +++ b/vendor/github.com/prometheus/procfs/proc_status.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -16,7 +16,7 @@ package procfs import ( "bytes" "math/bits" - "sort" + "slices" "strconv" "strings" @@ -83,6 +83,19 @@ type ProcStatus struct { // CpusAllowedList: List of cpu cores processes are allowed to run on. CpusAllowedList []uint64 + + // CapInh is the bitmap of inheritable capabilities + // + // See: https://www.kernel.org/doc/man-pages/online/pages/man7/capabilities.7.html + CapInh uint64 + // CapPrm is the bitmap of permitted capabilities + CapPrm uint64 + // CapEff is the bitmap of effective capabilities + CapEff uint64 + // CapBnd is the bitmap of bounding capabilities + CapBnd uint64 + // CapAmb is the bitmap of ambient capabilities + CapAmb uint64 } // NewStatus returns the current status information of the process. @@ -94,8 +107,7 @@ func (p Proc) NewStatus() (ProcStatus, error) { s := ProcStatus{PID: p.PID} - lines := strings.Split(string(data), "\n") - for _, line := range lines { + for line := range strings.SplitSeq(string(data), "\n") { if !bytes.Contains([]byte(line), []byte(":")) { continue } @@ -191,6 +203,36 @@ func (s *ProcStatus) fillStatus(k string, vString string, vUint uint64, vUintByt s.NonVoluntaryCtxtSwitches = vUint case "Cpus_allowed_list": s.CpusAllowedList = calcCpusAllowedList(vString) + case "CapInh": + var err error + s.CapInh, err = strconv.ParseUint(vString, 16, 64) + if err != nil { + return err + } + case "CapPrm": + var err error + s.CapPrm, err = strconv.ParseUint(vString, 16, 64) + if err != nil { + return err + } + case "CapEff": + var err error + s.CapEff, err = strconv.ParseUint(vString, 16, 64) + if err != nil { + return err + } + case "CapBnd": + var err error + s.CapBnd, err = strconv.ParseUint(vString, 16, 64) + if err != nil { + return err + } + case "CapAmb": + var err error + s.CapAmb, err = strconv.ParseUint(vString, 16, 64) + if err != nil { + return err + } } return nil @@ -222,7 +264,7 @@ func calcCpusAllowedList(cpuString string) []uint64 { } - sort.Slice(g, func(i, j int) bool { return g[i] < g[j] }) + slices.Sort(g) return g } diff --git a/vendor/github.com/prometheus/procfs/proc_sys.go b/vendor/github.com/prometheus/procfs/proc_sys.go index 3810d1ac99..52658a4d52 100644 --- a/vendor/github.com/prometheus/procfs/proc_sys.go +++ b/vendor/github.com/prometheus/procfs/proc_sys.go @@ -1,4 +1,4 @@ -// Copyright 2022 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/procfs/schedstat.go b/vendor/github.com/prometheus/procfs/schedstat.go index 5f7f32dc83..fafd8dff74 100644 --- a/vendor/github.com/prometheus/procfs/schedstat.go +++ b/vendor/github.com/prometheus/procfs/schedstat.go @@ -1,4 +1,4 @@ -// Copyright 2019 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/procfs/slab.go b/vendor/github.com/prometheus/procfs/slab.go index 8611c90177..32a04678ad 100644 --- a/vendor/github.com/prometheus/procfs/slab.go +++ b/vendor/github.com/prometheus/procfs/slab.go @@ -1,4 +1,4 @@ -// Copyright 2020 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/procfs/softirqs.go b/vendor/github.com/prometheus/procfs/softirqs.go index 403e6ae708..47b73a7297 100644 --- a/vendor/github.com/prometheus/procfs/softirqs.go +++ b/vendor/github.com/prometheus/procfs/softirqs.go @@ -1,4 +1,4 @@ -// Copyright 2022 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/procfs/stat.go b/vendor/github.com/prometheus/procfs/stat.go index e36b41c18a..593ad0f62f 100644 --- a/vendor/github.com/prometheus/procfs/stat.go +++ b/vendor/github.com/prometheus/procfs/stat.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -16,6 +16,7 @@ package procfs import ( "bufio" "bytes" + "errors" "fmt" "io" "strconv" @@ -92,7 +93,7 @@ func parseCPUStat(line string) (CPUStat, int64, error) { &cpuStat.Iowait, &cpuStat.IRQ, &cpuStat.SoftIRQ, &cpuStat.Steal, &cpuStat.Guest, &cpuStat.GuestNice) - if err != nil && err != io.EOF { + if err != nil && !errors.Is(err, io.EOF) { return CPUStat{}, -1, fmt.Errorf("%w: couldn't parse %q (cpu): %w", ErrFileParse, line, err) } if count == 0 { diff --git a/vendor/github.com/prometheus/procfs/swaps.go b/vendor/github.com/prometheus/procfs/swaps.go index 65fec834bf..ee17bf4888 100644 --- a/vendor/github.com/prometheus/procfs/swaps.go +++ b/vendor/github.com/prometheus/procfs/swaps.go @@ -1,4 +1,4 @@ -// Copyright 2019 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/procfs/thread.go b/vendor/github.com/prometheus/procfs/thread.go index 80e0e947be..0cfbb54184 100644 --- a/vendor/github.com/prometheus/procfs/thread.go +++ b/vendor/github.com/prometheus/procfs/thread.go @@ -1,4 +1,4 @@ -// Copyright 2022 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/prometheus/procfs/vm.go b/vendor/github.com/prometheus/procfs/vm.go index 51c49d89e8..52180c03e2 100644 --- a/vendor/github.com/prometheus/procfs/vm.go +++ b/vendor/github.com/prometheus/procfs/vm.go @@ -1,4 +1,4 @@ -// Copyright 2019 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -12,7 +12,6 @@ // limitations under the License. //go:build !windows -// +build !windows package procfs diff --git a/vendor/github.com/prometheus/procfs/zoneinfo.go b/vendor/github.com/prometheus/procfs/zoneinfo.go index e54d94b090..63d1898bc8 100644 --- a/vendor/github.com/prometheus/procfs/zoneinfo.go +++ b/vendor/github.com/prometheus/procfs/zoneinfo.go @@ -1,4 +1,4 @@ -// Copyright 2019 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -12,7 +12,6 @@ // limitations under the License. //go:build !windows -// +build !windows package procfs @@ -88,11 +87,9 @@ func parseZoneinfo(zoneinfoData []byte) ([]Zoneinfo, error) { zoneinfo := []Zoneinfo{} - zoneinfoBlocks := bytes.Split(zoneinfoData, []byte("\nNode")) - for _, block := range zoneinfoBlocks { + for block := range bytes.SplitSeq(zoneinfoData, []byte("\nNode")) { var zoneinfoElement Zoneinfo - lines := strings.Split(string(block), "\n") - for _, line := range lines { + for line := range strings.SplitSeq(string(block), "\n") { if nodeZone := nodeZoneRE.FindStringSubmatch(line); nodeZone != nil { zoneinfoElement.Node = nodeZone[1] diff --git a/vendor/github.com/twmb/franz-go/pkg/kgo/atomic_maybe_work.go b/vendor/github.com/twmb/franz-go/pkg/kgo/atomic_maybe_work.go index 1cf93ad68e..63c360fb96 100644 --- a/vendor/github.com/twmb/franz-go/pkg/kgo/atomic_maybe_work.go +++ b/vendor/github.com/twmb/franz-go/pkg/kgo/atomic_maybe_work.go @@ -61,6 +61,18 @@ func (l *workLoop) maybeFinish(again bool) bool { return again } +// hardFinish forces the loop back to unstarted, discarding any pending +// continue-work bump that a concurrent maybeBegin may have set. Unlike +// maybeFinish, it does not observe or preserve that bump, so work a racing +// pusher enqueued can be stranded with no worker running. +// +// This is safe only where the strand is moot or compensated. Most callers +// hardFinish because their session or client context just died, so any +// stranded work is being torn down anyway. The one transient caller is +// loopFetch hitting noConsumerSession: it compensates by reloading the +// session after hardFinish and re-triggering if a new one appeared (the +// race the comment there walks through). A new transient caller must do +// the same. func (l *workLoop) hardFinish() { l.state.Store(stateUnstarted) } diff --git a/vendor/github.com/twmb/franz-go/pkg/kgo/broker.go b/vendor/github.com/twmb/franz-go/pkg/kgo/broker.go index 2386eb24ba..8a7051aeb4 100644 --- a/vendor/github.com/twmb/franz-go/pkg/kgo/broker.go +++ b/vendor/github.com/twmb/franz-go/pkg/kgo/broker.go @@ -36,6 +36,17 @@ var ctxPinReq = func() *string { v := "pin_req"; return &v }() type forceOpenReq struct{ kmsg.Request } +// reauthDrainReq is an internal sentinel pushed through the broker's request +// ring by a connection's handleResps worker as it exits with a sasl +// reauthentication pending. It runs handleReauthDrain on the broker worker +// goroutine: reauthenticate now that no reads are in flight, then replay the +// requests that parked while the pipeline drained. The embedded kmsg.Request +// is nil and never used; handleReq dispatches on the type before touching it. +type reauthDrainReq struct { + kmsg.Request + cxn *brokerCxn +} + type promisedReq struct { ctx context.Context req kmsg.Request @@ -137,10 +148,12 @@ type broker struct { addr string // net.JoinHostPort(meta.Host, meta.Port) meta BrokerMetadata - // versions tracks the first load of an ApiVersions. We store this - // after the first connect, which helps speed things up on future - // reconnects (across any of the three broker connections) because we - // will never look up API versions for this broker again. + // versions tracks the broker's last loaded ApiVersions. Every new + // connection re-issues ApiVersions and re-stores the result (see + // brokerCxn.init), but the stored value serves request version + // resolution across all five broker connections and client-level + // capability checks (supportsKeyVersion, supportsFeature) without + // opening a connection. versions atomic.Value // *brokerVersions // The cxn fields each manage a single tcp connection to one broker. @@ -222,6 +235,17 @@ func unknownSeedID(seedNum int) int32 { } func (cl *Client) newBroker(nodeID int32, host string, port int32, rack *string) *broker { + // Clone the Rack *string so the broker owns its own pointer. A + // user-issued cl.Request(MetadataRequest) is hijacked through + // fetchMetadata, which builds brokers from the response and then + // returns that same response to the user: a user writing *Rack + // through their response would race every internal deref of + // b.meta.Rack (brokerRacks, dups in RequestCachedMetadata, + // meta.equals on a later metadata update). + if rack != nil { + r := *rack + rack = &r + } return &broker{ cl: cl, @@ -243,14 +267,20 @@ func (b *broker) stopForever() { b.reqs.die() // no more pushing + // Snapshot the connections under reapMu but die outside of it: die + // runs the user's OnBrokerDisconnect hook, and a hook must not run + // under reapMu (a hook that waits on anything that itself needs + // reapMu, e.g. a concurrent request's loadConnection, would + // deadlock). b.dead is already set above and loadConnection re-checks + // it under reapMu before storing, so no new connection can be stored + // after this snapshot. b.reapMu.Lock() - defer b.reapMu.Unlock() + cxns := []*brokerCxn{b.cxnNormal, b.cxnProduce, b.cxnFetch, b.cxnGroup, b.cxnSlow} + b.reapMu.Unlock() - b.cxnNormal.die() - b.cxnProduce.die() - b.cxnFetch.die() - b.cxnGroup.die() - b.cxnSlow.die() + for _, cxn := range cxns { + cxn.die() + } } // do issues a request to the broker, eventually calling the response @@ -304,6 +334,11 @@ start: func (b *broker) handleReq(pr promisedReq) { req := pr.req + if r, ok := req.(*reauthDrainReq); ok { + b.handleReauthDrain(r.cxn) + pr.promise(nil, nil) // internal sentinel; the promise is a no-op + return + } var cxn *brokerCxn var retriedOnNewConnection bool start: @@ -400,17 +435,61 @@ start: } req.SetVersion(ourMax) - if !cxn.expiry.IsZero() && time.Now().After(cxn.expiry) { - // If we are after the reauth time, try to reauth. We - // can only have an expiry if we went the authenticate - // flow, so we know we are authenticating again. + if !cxn.expiry.IsZero() && time.Now().After(cxn.expiry) && !cxn.hasDiscard { + // If we are after the reauth time, reauthenticate, for KIP-368. + // We can only have an expiry if we went the authenticate flow, + // so we know we are authenticating again. // + // Reauthenticating reads the handshake and authenticate + // responses on this goroutine, so it requires that nothing + // else can be reading the connection: + // + // * If any response is in flight (resps is non-empty -- an + // element stays in the ring while handleResps processes + // it), handleResps is reading this connection and our read + // would race it byte-by-byte, interleaving the two + // responses' bytes between the two readers. We park this + // request (and every following request for this connection) + // so the pipeline drains -- under sustained pipelining the + // pipeline never goes empty on its own, so we must stop + // feeding it. Our expiry is deliberately pessimistic (2-5% + // early, 1s floor; see doSasl), so the in-flight responses + // are on a still-valid session and the drain (bounded by + // their read timeouts) fits the margin. handleResps pushes + // a reauthDrainReq as it exits; handleReauthDrain then + // reauthenticates and replays the parked requests in + // order. Only this connection parks: the broker worker + // keeps serving its other connections. The Java client + // similarly holds the channel's queued send while + // reauthenticating, and it too only begins with no write + // in progress. + // + // Ordering of the pending store vs the empty check below + // matters: we store before checking, and handleResps reads + // the flag after its final dropPeek. empty() and dropPeek + // both take the ring mutex, so if we observe a non-empty + // ring, our store happens-before the drain's flag read and + // a parked request can never miss its drain signal. + // + // * acks=0 produce connections run a discard goroutine that + // owns all reads forever (hasDiscard, checked above), so + // in-place reauth is never possible: loadConnection + // recreates expired discard connections instead, and a + // fresh connection authenticates in init. We can still get + // here if the connection's lifetime is shorter than our + // pessimism (the fresh connection is already "expired"); + // issuing the request on the just-authenticated connection + // is correct, and the next request picks up a new one. + cxn.reauthPending.Store(true) + if cxn.anyParked() || !cxn.resps.empty() { + cxn.park(pr) + return + } + cxn.reauthPending.Store(false) // Some implementations (AWS) occasionally fail for // unclear reasons (principals change, somehow). If // we receive SASL_AUTHENTICATION_FAILED, we retry // once on a new connection. See #249. - // - // For KIP-368. cxn.cl.cfg.logger.Log(LogLevelDebug, "sasl expiry limit reached, reauthenticating", "broker", logID(cxn.b.meta.NodeID)) if err := cxn.sasl(); err != nil { cxn.die() @@ -438,6 +517,20 @@ start: } if _, isForceOpen := req.(*forceOpenReq); isForceOpen { + // The connection is already fully open: loadConnection ran init + // (dial, ApiVersions, SASL). On an acks=0 produce connection the + // discard goroutine owns all reads (hasDiscard), so issuing a + // response-expecting request here would start a second reader + // (handleResps, via waitResp below) that races the discard read on + // the same socket -- the two io.ReadFulls split one byte stream, + // the same concurrent-reader hazard the expiry arm and + // loadConnection already avoid for reauthentication. The connection + // is warm; report success without probing it (init already proved + // the connection works end to end). + if cxn.hasDiscard { + pr.promise(nil, nil) + return + } // We issue ApiVersions with v0; we could try to bound the // version by going to the start above, but it really does // not matter much. @@ -565,12 +658,19 @@ func (b *broker) loadConnection(ctx context.Context, req kmsg.Request) (*brokerC pcxn = &b.cxnSlow } - // Do not reuse a connection that has been idle for longer than idle timeout. - // Kill it instead. - if *pcxn != nil && !(*pcxn).dead.Load() && (*pcxn).isIdleTimeout(b.cl.cfg.connIdleTimeout) { - // die() in a goroutine to avoid blocking - go (*pcxn).die() - reuse = false + // Do not reuse a connection that has been idle for longer than idle + // timeout; kill it instead. Same for an acks=0 produce connection + // whose sasl session expired: the discard goroutine owns all reads on + // it, so in-place reauthentication (which reads handshake responses) + // is impossible -- a fresh connection authenticates in init. + if *pcxn != nil && !(*pcxn).dead.Load() { + cxn := *pcxn + expired := cxn.hasDiscard && !cxn.expiry.IsZero() && time.Now().After(cxn.expiry) + if cxn.isIdleTimeout(b.cl.cfg.connIdleTimeout) || expired { + // die() in a goroutine to avoid blocking + go cxn.die() + reuse = false + } } if reuse && *pcxn != nil && !(*pcxn).dead.Load() { @@ -578,9 +678,13 @@ func (b *broker) loadConnection(ctx context.Context, req kmsg.Request) (*brokerC } var tries int - start := time.Now() doConnect: tries++ + // start, conn, and err are declared per attempt so that each attempt's + // deferred OnBrokerConnect hook reports that attempt's connection, + // error, and duration (a backward goto over := re-executes the + // declaration; prior attempts' defers keep their own variables). + start := time.Now() conn, err := b.connect(ctx) defer func() { since := time.Since(start) @@ -606,12 +710,10 @@ doConnect: // EventHubs does not handle v4 and resets the connection. We // retry twice. On the first and second attempt, we try our max // version possible (as should be allowed). On the third try, - // we downgrade to v0. - if er := (*errApiVersionsReset)(nil); errors.As(err, &er) { - if tries < 3 { - tries++ - goto doConnect - } + // we downgrade to v0 (see requestAPIVersions). + if er := (*errApiVersionsReset)(nil); errors.As(err, &er) && tries < 3 { + cxn.closeConn() + goto doConnect } b.cl.cfg.logger.Log(LogLevelDebug, "connection initialization failed", "addr", b.addr, "broker", logID(b.meta.NodeID), "err", err) cxn.closeConn() @@ -626,24 +728,28 @@ doConnect: } b.reapMu.Lock() - defer b.reapMu.Unlock() // If stopForever ran while we were connecting, the broker is - // dead and we must not store the connection. stopForever kills - // cxnProduce/etc under reapMu, but if the connection was nil at - // that time (we were mid-connect), stopForever's die() was a - // no-op. Without this check, the connection escapes destruction + // dead and we must not store the connection. stopForever reads + // cxnProduce/etc under reapMu before killing them, but if the + // connection was nil at that time (we were mid-connect), it was + // not seen. Without this check, the connection escapes destruction // and a produce request succeeds on a connection that will never // be reused, which -- combined with other connections from other // broker objects for the same nodeID -- breaks the single- // connection-per-broker ordering guarantee that Kafka requires // for idempotent produce. + // + // closeConn runs the user's OnBrokerDisconnect hook, so it runs + // after the unlock (see stopForever for why). if b.dead.Load() { + b.reapMu.Unlock() cxn.closeConn() return nil, errChosenBrokerDead } *pcxn = cxn + b.reapMu.Unlock() return cxn, nil } @@ -687,16 +793,20 @@ func (cl *Client) reapConnections(idleTimeout time.Duration) (total int) { } func (b *broker) reapConnections(idleTimeout time.Duration) (total int) { + // Snapshot under reapMu, evaluate and die outside: die runs the + // user's OnBrokerDisconnect hook, which must not run under reapMu + // (see stopForever). b.reapMu.Lock() - defer b.reapMu.Unlock() - - for _, cxn := range []*brokerCxn{ + cxns := []*brokerCxn{ b.cxnNormal, b.cxnProduce, b.cxnFetch, b.cxnGroup, b.cxnSlow, - } { + } + b.reapMu.Unlock() + + for _, cxn := range cxns { if cxn == nil || cxn.dead.Load() { continue } @@ -766,8 +876,75 @@ type brokerCxn struct { resps ring[promisedResp] // dead is an atomic so that a backed up resps cannot block cxn death. dead atomic.Bool - // closed in cloneConn; allows throttle waiting to quit + // closed in closeConn; allows throttle waiting to quit deadCh chan struct{} + // hasDiscard is set during init (before the connection is shared) if + // this is an acks=0 produce connection running the discard goroutine. + // The discard goroutine owns all reads on the connection, so sasl + // reauthentication can never happen in place; see handleReq and + // loadConnection. + hasDiscard bool + + // reauthPending is set by the broker worker when the sasl expiry has + // passed but responses are still in flight (handleResps owns reads, + // so reauthenticating would race it). While pending, requests for + // this connection park instead of being written, so the pipeline + // drains; handleResps pushes a reauthDrainReq as it exits to + // reauthenticate and replay them. See handleReq's expiry arm for the + // store/load ordering argument. + reauthPending atomic.Bool + + // parkMu guards parked/parkFailed. parked holds requests, in arrival + // order, that are waiting for a pending reauthentication; order + // matters for idempotent produce, whose sequence numbers were + // assigned at request build. die fails everything parked (and marks + // parkFailed) so a dead connection cannot strand requests. + parkMu xsync.Mutex + parked []promisedReq + parkFailed bool +} + +// park holds a request destined for this connection while a sasl +// reauthentication is pending; handleReauthDrain replays parked requests in +// order once the pipeline drains. If the connection already died (die fails +// everything parked), the request fails immediately with the same retryable +// error that ring-queued requests receive on broker death. +func (cxn *brokerCxn) park(pr promisedReq) { + cxn.parkMu.Lock() + if cxn.parkFailed { + cxn.parkMu.Unlock() + pr.promise(nil, errChosenBrokerDead) + return + } + cxn.parked = append(cxn.parked, pr) + n := len(cxn.parked) + cxn.parkMu.Unlock() + cxn.cl.cfg.logger.Log(LogLevelDebug, "sasl expiry limit reached but responses are in flight, parking request until the pipeline drains", "broker", logID(cxn.b.meta.NodeID), "parked_reqs", n) +} + +func (cxn *brokerCxn) anyParked() bool { + cxn.parkMu.Lock() + defer cxn.parkMu.Unlock() + return len(cxn.parked) > 0 +} + +func (cxn *brokerCxn) takeParked() []promisedReq { + cxn.parkMu.Lock() + defer cxn.parkMu.Unlock() + parked := cxn.parked + cxn.parked = nil + return parked +} + +func (cxn *brokerCxn) failParked() { + cxn.parkMu.Lock() + parked := cxn.parked + cxn.parked = nil + cxn.parkFailed = true + cxn.parkMu.Unlock() + for _, pr := range parked { + pr.promise(nil, errChosenBrokerDead) + } } func (cxn *brokerCxn) init(isProduceCxn bool, tries int) error { @@ -800,6 +977,7 @@ func (cxn *brokerCxn) init(isProduceCxn bool, tries int) error { } if isProduceCxn && cxn.cl.cfg.acks.val == 0 { + cxn.hasDiscard = true go cxn.discard() // see docs on discard for why we do this } return nil @@ -871,9 +1049,19 @@ start: cxn.cl.cfg.logger.Log(LogLevelDebug, "broker does not know our ApiVersions version, downgrading to version 0 and retrying", "broker", logID(cxn.b.meta.NodeID)) goto start case len(resp.ApiKeys) == 1 && resp.ApiKeys[0].ApiKey == 18: - maxVersion = resp.ApiKeys[0].MaxVersion - cxn.cl.cfg.logger.Log(LogLevelDebug, fmt.Sprintf("broker does not know our ApiVersions version but replied version %[1]d, downgrading to version %[1]d and retrying", maxVersion), "broker", logID(cxn.b.meta.NodeID)) - goto start + // KIP-511: the broker replies with the version we should + // retry with. Only accept a strictly lower, non-negative + // version: a real broker advertises less than what it + // just rejected, and accepting anything else would let a + // buggy/hostile broker keep us in this downgrade loop + // forever (the init path runs on no request context, so + // only client close would stop it). + if v := resp.ApiKeys[0].MaxVersion; v >= 0 && v < maxVersion { + maxVersion = v + cxn.cl.cfg.logger.Log(LogLevelDebug, fmt.Sprintf("broker does not know our ApiVersions version but replied version %[1]d, downgrading to version %[1]d and retrying", maxVersion), "broker", logID(cxn.b.meta.NodeID)) + goto start + } + return fmt.Errorf("broker replied with UNSUPPORTED_VERSION to our v%d ApiVersions request but advertised non-downgrade version %d", maxVersion, resp.ApiKeys[0].MaxVersion) default: // Should not hit this case, but we hope the broker replied with all keys } @@ -1055,6 +1243,16 @@ func (cxn *brokerCxn) doSasl(authenticate bool) error { } if lifetimeMillis > 0 { + // A hostile/buggy broker can reply with a lifetime so large + // that converting it to a time.Duration overflows, wrapping + // the expiry into the past and forcing a reauth before every + // request. Anything beyond a year never matters for a real + // connection; clamp. + const maxLifetimeMillis = 365 * 24 * int64(time.Hour/time.Millisecond) + if lifetimeMillis > maxLifetimeMillis { + lifetimeMillis = maxLifetimeMillis + } + // Lifetime is problematic. We need to be a bit pessimistic. // // We want a lowerbound: we use 1s (arbitrary), but if 1.1x our @@ -1093,6 +1291,14 @@ func (cxn *brokerCxn) doSasl(authenticate bool) error { "lifetime_pessimism", time.Duration(usePessimismMillis)*time.Millisecond, "reauthenticate_in", cxn.expiry.Sub(now), ) + } else { + // No (or zero) lifetime means the broker does not require + // reauthentication, KIP-368. If a previous authenticate on + // this connection set an expiry (reauth was enabled then, e.g. + // before a dynamic broker config change), clear it: keeping + // the old, already-passed expiry would re-run the full sasl + // flow before every subsequent request on this connection. + cxn.expiry = time.Time{} } return nil } @@ -1384,9 +1590,11 @@ func (cxn *brokerCxn) readResponse( return buf[4:], nil } -// closeConn is the one place we close broker connections. This is always done -// in either die, which is called when handleResps returns, or if init fails, -// which means we did not succeed enough to start handleResps. +// closeConn is the one place we close broker connections. It is reached via +// die (callable from anywhere once the connection is live: read/write errors, +// reaping, broker stoppage) or directly when the connection was never shared: +// init failure, the ApiVersions-reset retry, and loadConnection's dead-broker +// arm. func (cxn *brokerCxn) closeConn() { cxn.cl.cfg.hooks.each(func(h Hook) { if h, ok := h.(HookBrokerDisconnect); ok { @@ -1398,13 +1606,15 @@ func (cxn *brokerCxn) closeConn() { } // die kills a broker connection (which could be dead already) and replies to -// all requests awaiting responses appropriately. +// all requests awaiting responses appropriately, including requests parked +// for a pending reauthentication. func (cxn *brokerCxn) die() { if cxn == nil || cxn.dead.Swap(true) { return } cxn.closeConn() cxn.resps.die() + cxn.failParked() } // waitResp, called serially by a broker's handleReqs, manages handling a @@ -1529,7 +1739,7 @@ func (cxn *brokerCxn) discard() { } nread2, err = cxn.conn.Read(discard) nread += nread2 - size -= int32(nread2) // nread2 max is 128 + size -= int32(nread2) // nread2 max is len(discardBuf), 256 } }() @@ -1570,6 +1780,60 @@ start: if more { goto start } + + // The ring is empty and this worker is exiting: no read is in flight + // and none can start (only the broker worker pushes responses, and it + // parks requests for this connection while a reauth is pending). If a + // reauth is pending, signal the broker worker to perform it and + // replay the parked requests. If the broker is stopping, the failed + // push is fine: stopForever dies every connection, and die fails + // everything parked. + if cxn.reauthPending.Load() { + cxn.b.do(cxn.cl.ctx, &reauthDrainReq{cxn: cxn}, func(kmsg.Response, error) {}) + } +} + +// handleReauthDrain runs on the broker worker goroutine when a connection +// with a pending reauthentication has drained its in-flight responses (its +// handleResps worker pushed this sentinel as it exited). No reads can be in +// flight: handleResps exited, only this goroutine starts another (by writing +// a request), and every request for this connection since the expiry passed +// was parked. Reauthenticate and replay the parked requests in their +// original order -- order matters for idempotent produce, whose sequence +// numbers were assigned at request build. +func (b *broker) handleReauthDrain(cxn *brokerCxn) { + // A sentinel can be spurious: the quiet-connection reauth arm stores + // the pending flag and clears it just after, and a concurrently + // exiting handleResps can observe the transient true. That alone is + // a harmless no-op below -- but if the broker also hands out + // immediately-expiring lifetimes, a request queued behind this + // sentinel's push may have re-parked by now with a NEW response in + // flight (the inline arm's own request), so "drained" no longer + // holds. If anything is in flight, touch nothing: that response's + // handleResps exit re-signals (the parker stored the pending flag + // before observing the non-empty ring, so the exit cannot miss it) + // and the re-signal finds the connection actually quiet. + if !cxn.resps.empty() { + return + } + + // Take the parked requests before a possible die below: a failed + // reauth must not fail them, it replays them through the normal path, + // which builds a fresh connection with a fresh authentication -- the + // same recovery the in-place reauth arm provides via its + // retry-on-new-connection. (If the connection died earlier, die + // already failed everything parked and this take returns nil.) + parked := cxn.takeParked() + if cxn.reauthPending.Swap(false) && !cxn.dead.Load() { + cxn.cl.cfg.logger.Log(LogLevelDebug, "sasl expiry limit reached and responses have drained, reauthenticating", "broker", logID(cxn.b.meta.NodeID), "parked_reqs", len(parked)) + if err := cxn.sasl(); err != nil { + cxn.cl.cfg.logger.Log(LogLevelDebug, "sasl reauth failed, killing connection; any parked requests retry on a new connection", "broker", logID(cxn.b.meta.NodeID), "err", err) + cxn.die() + } + } + for _, pr := range parked { + b.handleReq(pr) + } } func (cxn *brokerCxn) handleResp(pr promisedResp) { @@ -1618,6 +1882,14 @@ func (cxn *brokerCxn) handleResp(pr promisedResp) { if readErr == nil { if throttleResponse, ok := pr.resp.(kmsg.ThrottleResponse); ok { millis, throttlesAfterResp := throttleResponse.Throttle() + // A throttle is honored in full with NO upper cap (KIP-219), + // matching Java's NetworkClient. millis <= 0 is ignored; a + // positive value - even a hostile near-MaxInt32 (~24.8 days) - + // delays the next write on this connection (writeRequest), which + // selects on the request ctx and client Close, so the wait is + // always interruptible and holds no lock. Capping it would break + // the quota mechanism; an absurd throttle is equivalent to a slow + // broker, which is always possible. Do not add a cap. if millis > 0 { if pr.resp.Key() == 0 { cxn.b.cl.metrics.observeTime(&cxn.b.cl.metrics.pThrottle, int64(millis)) diff --git a/vendor/github.com/twmb/franz-go/pkg/kgo/client.go b/vendor/github.com/twmb/franz-go/pkg/kgo/client.go index f82e59838b..eaba2c3c45 100644 --- a/vendor/github.com/twmb/franz-go/pkg/kgo/client.go +++ b/vendor/github.com/twmb/franz-go/pkg/kgo/client.go @@ -100,7 +100,6 @@ type Client struct { topics map[string]cachedMetaTopic byID map[[16]byte]string // TopicID => topic name allAt time.Time // when last all-topics fetch completed - anyAt time.Time // when any cached metadata was last stored } } @@ -372,7 +371,7 @@ func (cl *Client) OptValues(opt any) []any { return []any{cfg.recordTimeout} case namefn(TransactionalID): if cfg.txnID != nil { - return []any{cfg.txnID, true} + return []any{*cfg.txnID, true} } return []any{"", false} case namefn(TransactionTimeout): @@ -462,6 +461,14 @@ func (cl *Client) OptValues(opt any) []any { return []any{true} case namefn(SessionTimeout): return []any{cfg.sessionTimeout} + case namefn(ShareGroup): + return []any{cfg.shareGroup} + case namefn(ShareAckCallback): + return []any{cfg.shareAckCallback} + case namefn(ShareMaxRecords): + return []any{cfg.shareMaxRecords} + case namefn(ShareMaxRecordsStrict): + return []any{cfg.shareMaxRecordsStrict} default: return nil @@ -681,7 +688,7 @@ func (cl *Client) Ping(ctx context.Context) error { // // For admin requests, this deletes the topic from the cached metadata map for // sharded requests. Metadata for sharded admin requests is only cached for -// MetadataMinAge anyway, but the map is not cleaned up one the metadata +// MetadataMinAge anyway, but the map is not cleaned up once the metadata // expires. This function ensures the map is purged. func (cl *Client) PurgeTopicsFromClient(topics ...string) { if len(topics) == 0 { @@ -935,7 +942,21 @@ func (cl *Client) supportsOffsetForLeaderEpoch() bool { // v1 introduces support for regex and requires the client to generate // the member ID, and fully stabilizes KIP-848. func (cl *Client) supportsKIP848v1() bool { - return cl.supportsKeyVersion(int16(kmsg.ConsumerGroupHeartbeat), 1) + if !cl.supportsKeyVersion(int16(kmsg.ConsumerGroupHeartbeat), 1) { + return false + } + // The 848 manage loop runs v1 semantics (client-generated member ID, + // regex subscriptions). If the user's MaxVersions caps the heartbeat + // below v1, the wire request would be v0 while we behave as v1: v0 + // has no SubscribedTopicRegex field at all, so a regex consumer's + // join would carry no subscription and silently consume nothing. + // Only opt in if the cap allows v1, mirroring supportsKIP890p2. + if mv := cl.cfg.maxVersions; mv != nil { + if v, ok := mv.LookupMaxKeyVersion(int16(kmsg.ConsumerGroupHeartbeat)); !ok || v < 1 { + return false + } + } + return true } // Called after the first metric observed, which is always after a response. @@ -964,7 +985,28 @@ func (cl *Client) supportsKeyVersion(key, version int16) bool { } func (cl *Client) supportsKIP890p2() bool { - return cl.supportsFeature("transaction.version", 2) + if !cl.supportsFeature("transaction.version", 2) { + return false + } + // The KIP-890 part 2 paths change what goes on the wire: produce v12+ + // skips AddPartitionsToTxn, TxnOffsetCommit v5+ skips AddOffsetsToTxn, + // and EndTxn v5+ bumps the producer epoch. A user MaxVersions cap + // below any of those keeps the OLD wire versions while skipping the + // explicit add requests, and the broker then rejects the produces + // with INVALID_TXN_STATE because the partitions were never added to + // the transaction. Only opt in if the cap allows the new versions. + if mv := cl.cfg.maxVersions; mv != nil { + if v, ok := mv.LookupMaxKeyVersion(int16(kmsg.Produce)); !ok || v < 12 { + return false + } + if v, ok := mv.LookupMaxKeyVersion(int16(kmsg.EndTxn)); !ok || v < 5 { + return false + } + if v, ok := mv.LookupMaxKeyVersion(int16(kmsg.TxnOffsetCommit)); !ok || v < 5 { + return false + } + } + return true } // Same as above. A cluster returns a max version for a feature only once the @@ -988,32 +1030,42 @@ func (cl *Client) supportsFeature(name string, version int16) bool { } // fetchBrokerMetadata issues a metadata request solely for broker information. +// +// Concurrent callers collapse onto one in-flight request. The request itself +// runs on the client context: if it ran on the initiating caller's context, +// that caller canceling would fail every collapsed waiter with the canceled +// error even though their own contexts are live (the same reasoning as +// doLoadCoordinators). Each caller's context governs only its own wait; an +// abandoned fetch still completes and updates broker state, which is fine. func (cl *Client) fetchBrokerMetadata(ctx context.Context) error { cl.fetchingBrokersMu.Lock() wait := cl.fetchingBrokers - if wait != nil { - cl.fetchingBrokersMu.Unlock() - <-wait.done - return wait.err + if wait == nil { + wait = &struct { + done chan struct{} + err error + }{done: make(chan struct{})} + cl.fetchingBrokers = wait + go func() { + defer func() { + cl.fetchingBrokersMu.Lock() + defer cl.fetchingBrokersMu.Unlock() + cl.fetchingBrokers = nil + close(wait.done) + }() + req := kmsg.NewPtrMetadataRequest() + req.Topics = []kmsg.MetadataRequestTopic{} + _, _, wait.err = cl.fetchMetadata(cl.ctx, req, true, nil) + }() } - wait = &struct { - done chan struct{} - err error - }{done: make(chan struct{})} - cl.fetchingBrokers = wait cl.fetchingBrokersMu.Unlock() - defer func() { - cl.fetchingBrokersMu.Lock() - defer cl.fetchingBrokersMu.Unlock() - cl.fetchingBrokers = nil - close(wait.done) - }() - - req := kmsg.NewPtrMetadataRequest() - req.Topics = []kmsg.MetadataRequestTopic{} - _, _, wait.err = cl.fetchMetadata(ctx, req, true, nil) - return wait.err + select { + case <-wait.done: + return wait.err + case <-ctx.Done(): + return ctx.Err() + } } func (cl *Client) fetchMetadataByName(ctx context.Context, all bool, topics []string, results map[string]cachedMetaTopic) (*broker, *kmsg.MetadataResponse, error) { @@ -1106,14 +1158,32 @@ func (cl *Client) updateMetadataBrokers(resp *kmsg.MetadataResponse) { if resp.ControllerID >= 0 { cl.controllerID = resp.ControllerID } - cl.clusterID = resp.ClusterID + // Clone ClusterID so cl.clusterID owns its own *string, independent + // of the broker response. Readers (dups in RequestCachedMetadata) + // would otherwise race a user mutating *resp.ClusterID on a + // previously-returned cl.Request(MetadataRequest) response. + cl.clusterID = nil + if resp.ClusterID != nil { + s := *resp.ClusterID + cl.clusterID = &s + } cl.controllerIDMu.Unlock() cl.updateBrokers(resp.Brokers) } // updateBrokers is called with the broker portion of every metadata response. -// All metadata responses contain all known live brokers, so we can always -// use the response. +// All metadata responses contain all known live brokers, so the response list +// is authoritative and we diff-merge it: brokers absent from it are stopped. +// +// An EMPTY list therefore wipes all discovered brokers and falls back to +// seeds. This is intentional and long-standing: if a broker ever answers +// metadata with no brokers, falling back to the seeds is safer than being +// left with none, and it re-covers every seed should the cluster have somehow +// split. KIP-1102's rebootstrap rides the same path, calling updateBrokers(nil) +// explicitly. A buggy broker sending a transient subset only kills those +// connections (in-flight requests fail with retriable errChosenBrokerDead) and +// the next good response heals. Do not special-case the empty list back into a +// no-op. func (cl *Client) updateBrokers(brokers []kmsg.MetadataResponseBroker) { sort.Slice(brokers, func(i, j int) bool { return brokers[i].NodeID < brokers[j].NodeID }) newBrokers := make([]*broker, 0, len(brokers)) @@ -1263,23 +1333,28 @@ func (cl *Client) close(ctx context.Context) (rerr error) { wg.Wait() sessCloseCancel() - // Now we kill the client context and all brokers, ensuring all - // requests fail. This will finish all producer callbacks and - // stop the metadata loop and metrics loop. - cl.ctxCancel() - - // Before killing brokers, give metrics 1s to push any final - // terminating message. The client context cancelation awakens - // the push-period-wait loop. + // Tell the metrics loop to send its final Terminating=true push and + // give it 1s to do so. This must happen BEFORE we cancel the client + // context: the entire request path (shardedRequest's cancel watchdog, + // Broker.request's select, and the broker connection write/read + // loops) aborts requests once cl.ctx is dead, so a terminating push + // started after cancelation could never be delivered. The metrics + // loop cancels metrics.ctx when it exits; clients with metrics + // disabled, unsupported, or never observed pass through instantly. + cl.metrics.quit() after := time.NewTimer(time.Second) select { case <-cl.metrics.ctx.Done(): + after.Stop() case <-after.C: cl.metrics.ctxCancel() - case <-ctx.Done(): - cl.metrics.ctxCancel() } + // Now we kill the client context and all brokers, ensuring all + // requests fail. This will finish all producer callbacks and + // stop the metadata loop and metrics loop. + cl.ctxCancel() + cl.brokersMu.Lock() cl.stopBrokers = true for _, broker := range cl.brokers { @@ -1349,6 +1424,8 @@ func (cl *Client) close(ctx context.Context) (rerr error) { // ListGroups // DeleteRecords // OffsetForLeaderEpoch +// AddPartitionsToTxn +// WriteTxnMarkers // DescribeConfigs // AlterConfigs // AlterReplicaLogDirs @@ -1511,7 +1588,7 @@ func (cl *Client) RequestCachedMetadata(ctx context.Context, req *kmsg.MetadataR NodeID: b.meta.NodeID, Host: b.meta.Host, Port: b.meta.Port, - Rack: b.meta.Rack, + Rack: dups(b.meta.Rack), }) } cl.brokersMu.RUnlock() @@ -1657,7 +1734,7 @@ type ResponseShard struct { // unknown (node ID -1) metadata if the request could not be issued. // // Requests can fail to even be issued if an appropriate broker cannot - // be loaded of if the client cannot understand the request. + // be loaded or if the client cannot understand the request. Meta BrokerMetadata // Req is the request that was issued to this broker. @@ -1683,9 +1760,9 @@ type ResponseShard struct { // // If, in the process of splitting a request, some topics or partitions are // found to not exist, or Kafka replies that a request should go to a broker -// that does not exist, all those non-existent pieces are grouped into one -// request to the first seed broker. This will show up as a seed broker node ID -// (min int32) and the response will likely contain purely errors. +// that does not exist, all those non-existent pieces are not issued; they are +// returned as error shards with an unknown broker metadata (node ID -1) and +// the error that prevented the piece from being mapped to a broker. // // The response shards are ordered by broker metadata. func (cl *Client) RequestSharded(ctx context.Context, req kmsg.Request) []ResponseShard { @@ -1827,20 +1904,23 @@ func findBroker(candidates []*broker, node int32) *broker { // that error. func (cl *Client) brokerOrErr(ctx context.Context, id int32, err error) (*broker, error) { if id < 0 { + // Negative IDs are seed brokers (unknownSeedID), exposed to + // users via SeedBrokers(). Any other negative ID (-1 unknown + // controller / coordinator sentinels) never exists, and + // metadata responses cannot introduce negative IDs, so we + // look up seeds directly and never try a metadata load. + if b := findBroker(cl.loadSeeds(), id); b != nil { + return b, nil + } return nil, err } tryLoad := ctx != nil tries := 0 start: - var broker *broker - if id < 0 { - broker = findBroker(cl.loadSeeds(), id) - } else { - cl.brokersMu.RLock() - broker = findBroker(cl.brokers, id) - cl.brokersMu.RUnlock() - } + cl.brokersMu.RLock() + broker := findBroker(cl.brokers, id) + cl.brokersMu.RUnlock() if broker == nil { if tryLoad { @@ -1897,6 +1977,11 @@ func (cl *Client) forgetControllerID(id int32) { } } +// Coordinator types match Kafka's FindCoordinator key-type enum. +// coordinatorTypeShare (the share-state coordinator) is keyed by a +// groupId:topicId:partition SharePartitionKey and serves only the +// broker-internal persister RPCs (keys 83-87), which kgo never issues; share +// groups go entirely through the GROUP coordinator (see #1330). const ( coordinatorTypeGroup int8 = 0 coordinatorTypeTxn int8 = 1 @@ -1934,13 +2019,13 @@ func (cl *Client) loadCoordinators(ctx context.Context, typ int8, keys ...string } } -// doLoadCoordinators uses the caller context to cancel loading metadata -// (brokerOrErr), but we use the client context to actually issue the request. -// There should be only one direct call to doLoadCoordinators, just above in -// loadCoordinator. It is possible for two requests to be loading the same -// coordinator (in fact, that's the point of this function -- collapse these -// requests). We do not want the first request canceling it's context to cause -// errors for the second request. +// doLoadCoordinators issues the FindCoordinator request - and any broker +// metadata load needed to resolve the response - on the client context. It is +// possible for two requests to be loading the same coordinator (in fact, +// that's the point of this function -- collapse these requests). We do not +// want the first request canceling its context to cause errors for the +// second request. The caller context only cancels the caller's wait, via +// loadCoordinators above. // // It is ok to leave FindCoordinator running even if the caller quits. Worst // case, we just cache things for some time in the future; yay. @@ -2140,13 +2225,35 @@ func (cl *Client) deleteStaleCoordinatorsByNode(node int32) { cl.coordinatorsMu.Lock() defer cl.coordinatorsMu.Unlock() for k, v := range cl.coordinators { - if v == nil || v.node != node { + if v == nil { continue } + // v.node is written by doLoadCoordinators outside of + // coordinatorsMu; that write is published only by the + // close(loadWait) ending the load. We must observe the close + // before reading v.node, so the v.node check lives inside the + // loadWait arm -- not in the range filter above. + // + // Race walkthrough if we checked v.node before the select: + // 1) doLoadCoordinators inserts v with an open loadWait, then + // releases coordinatorsMu and issues FindCoordinator. + // 2) The response arrives and the loader writes v.node = + // rc.NodeID, lock-free (doLoadCoordinators, above). + // 3) Concurrently a broker disconnect calls us here. We hold + // coordinatorsMu, but the writer in step 2 never takes it, + // so the mutex does not order us against that write -- + // reading v.node now is a data race (go test -race flags + // it). close(loadWait) in step 2 has not happened yet, so + // the default arm is what we would take anyway. + // Reading inside the loadWait arm makes the read happen-after + // the publishing close, so v.node is safe to observe. select { case <-v.loadWait: - delete(cl.coordinators, k) + if v.node == node { + delete(cl.coordinators, k) + } default: + // Still loading: v.node is not yet published; skip. } } } @@ -2285,8 +2392,18 @@ func (cl *Client) handleCoordinatorReq(ctx context.Context, req kmsg.Request) Re case *kmsg.OffsetDeleteRequest: return cl.handleCoordinatorReqSimple(ctx, coordinatorTypeGroup, t.Group, req) - // ConsumerGroupHeartbeat cannot be retried at all + // ConsumerGroupHeartbeat carries reconciliation state and cannot be + // blindly retried; the 848 heartbeat loop owns retries with full + // state knowledge. The exception is leaving (MemberEpoch -1, or -2 + // for static members): a leave carries no reconcilable state and is + // idempotent, and the coordinator moving is precisely when leaves + // are issued against a stale cached coordinator - firing it once + // would lose the leave to a single NOT_COORDINATOR and ghost the + // member until the session timeout. case *kmsg.ConsumerGroupHeartbeatRequest: + if t.MemberEpoch < 0 { + return cl.handleCoordinatorReqSimple(ctx, coordinatorTypeGroup, t.Group, req) + } br, err := cl.loadCoordinator(ctx, coordinatorTypeGroup, t.Group) var resp kmsg.Response if err == nil { @@ -2298,10 +2415,14 @@ func (cl *Client) handleCoordinatorReq(ctx context.Context, req kmsg.Request) Re // SHARE // /////////// - // ShareGroupHeartbeat cannot be retried (like ConsumerGroupHeartbeat). - // Like ConsumerGroupHeartbeat, share membership is managed by the - // GROUP coordinator, not the share-state coordinator. + // ShareGroupHeartbeat cannot be retried (like ConsumerGroupHeartbeat), + // except for the leave, which is retried for the same reason as + // above. Share membership is managed by the GROUP coordinator, not + // the share-state coordinator. case *kmsg.ShareGroupHeartbeatRequest: + if t.MemberEpoch < 0 { + return cl.handleCoordinatorReqSimple(ctx, coordinatorTypeGroup, t.GroupID, req) + } br, err := cl.loadCoordinator(ctx, coordinatorTypeGroup, t.GroupID) var resp kmsg.Response if err == nil { @@ -2309,9 +2430,9 @@ func (cl *Client) handleCoordinatorReq(ctx context.Context, req kmsg.Request) Re } return shard(br, req, resp, err) case *kmsg.AlterShareGroupOffsetsRequest: - return cl.handleCoordinatorReqSimple(ctx, coordinatorTypeShare, t.GroupID, req) + return cl.handleCoordinatorReqSimple(ctx, coordinatorTypeGroup, t.GroupID, req) case *kmsg.DeleteShareGroupOffsetsRequest: - return cl.handleCoordinatorReqSimple(ctx, coordinatorTypeShare, t.GroupID, req) + return cl.handleCoordinatorReqSimple(ctx, coordinatorTypeGroup, t.GroupID, req) } } @@ -2376,6 +2497,8 @@ func (cl *Client) handleReqWithCoordinator( code = t.ErrorCode case *kmsg.ConsumerGroupHeartbeatResponse: code = t.ErrorCode + case *kmsg.ShareGroupHeartbeatResponse: + code = t.ErrorCode } // ListGroups, OffsetFetch, DeleteGroups, DescribeGroups, and @@ -2730,13 +2853,18 @@ func (cl *Client) handleShardedReq(ctx context.Context, req kmsg.Request) ([]Res start: tries++ - br := cl.broker() + var br *broker var err error if !myIssue.any { br, err = cl.brokerOrErr(ctx, myIssue.broker, errUnknownBroker) - } else if avoidBroker != -1 { - for i := 0; i < 3 && br.meta.NodeID == avoidBroker; i++ { - br = cl.broker() + } else { + // Only consume an any-broker rotation slot for + // shards that actually go to any broker. + br = cl.broker() + if avoidBroker != -1 { + for i := 0; i < 3 && br.meta.NodeID == avoidBroker; i++ { + br = cl.broker() + } } } if err != nil { @@ -3016,7 +3144,6 @@ func (cl *Client) storeCachedMeta(meta *kmsg.MetadataResponse, all bool, results cl.metaCache.byID = make(map[[16]byte]string) } when := time.Now() - cl.metaCache.anyAt = when var zeroID [16]byte var stored int for _, topic := range meta.Topics { @@ -3026,22 +3153,49 @@ func (cl *Client) storeCachedMeta(meta *kmsg.MetadataResponse, all bool, results continue } stored++ + // Deep-clone the topic name, Partitions, and each partition's + // inner slices so the cache owns fully independent state. The + // broker response is shared with whoever consumed it: + // fetchTopicMetadata sort.Slice's the outer Partitions slice + // in place to validate ordering (issue #1328), and + // cl.Request(MetadataRequest) hands the response back to user + // code that may mutate the inner slices or write through the + // Topic *string. Without these clones, readers via + // RequestCachedMetadata or sharded request paths (which read + // ps[part].Replicas) would race those writers. dupt on the + // GET side clones separately: that isolates returned responses + // from the cache, this isolates the cache from the response. + topicName := *topic.Topic + topic.Topic = &topicName + topic.Partitions = slices.Clone(topic.Partitions) + for i := range topic.Partitions { + p := &topic.Partitions[i] + p.Replicas = slices.Clone(p.Replicas) + p.ISR = slices.Clone(p.ISR) + p.OfflineReplicas = slices.Clone(p.OfflineReplicas) + } t := cachedMetaTopic{ id: topic.TopicID, t: topic, ps: make(map[int32]kmsg.MetadataResponseTopicPartition), when: when, } - cl.metaCache.topics[*topic.Topic] = t + // A recreated topic comes back under a new ID. Delete the old + // ID's mapping when overwriting the entry, else byID accumulates + // stale IDs forever and resolves IDs that no longer exist. + if old, ok := cl.metaCache.topics[topicName]; ok && old.id != topic.TopicID && old.id != zeroID { + delete(cl.metaCache.byID, old.id) + } + cl.metaCache.topics[topicName] = t for _, partition := range topic.Partitions { t.ps[partition.Partition] = partition } if topic.TopicID != zeroID { - cl.metaCache.byID[topic.TopicID] = *topic.Topic + cl.metaCache.byID[topic.TopicID] = topicName } if results != nil { - results[*t.t.Topic] = t + results[topicName] = t } } @@ -4202,15 +4356,26 @@ func (cl *addPartitionsToTxnSharder) shard(ctx context.Context, kreq kmsg.Reques var issues []issueShard for id, req := range brokerReqs { - if len(req.Transactions) <= 1 || len(req.Transactions) == 1 && !req.Transactions[0].VerifyOnly { + if len(req.Transactions) == 1 && !req.Transactions[0].VerifyOnly { + // Clients must use v3 and below; v4+ requires CLUSTER_ACTION + // authorization because it is the broker-to-broker side of + // KIP-890. issues = append(issues, issueShard{ req: req, pin: &pinReq{pinMax: true, max: 3}, broker: id, }) } else { + // Batched transactions and VerifyOnly only exist in v4+: + // the v3 body has no Transactions array and no VerifyOnly + // field, so serializing this request any lower silently + // drops everything that matters - VerifyOnly in particular + // would turn a verification into a real partition add. Pin + // min 4 so brokers that cannot express the request fail + // loudly with errBrokerTooOld instead. issues = append(issues, issueShard{ req: req, + pin: &pinReq{pinMin: true, min: 4}, broker: id, }) } @@ -4315,10 +4480,11 @@ func (cl *writeTxnMarkersSharder) shard(ctx context.Context, kreq kmsg.Request, } type pidEpochCommit struct { - pid int64 - epoch int16 - commit bool - txnVersion int8 + pid int64 + epoch int16 + commit bool + coordinatorEpoch int32 + txnVersion int8 } brokerReqs := make(map[int32]map[pidEpochCommit]map[string][]int32) @@ -4356,6 +4522,7 @@ func (cl *writeTxnMarkersSharder) shard(ctx context.Context, kreq kmsg.Request, marker.ProducerID, marker.ProducerEpoch, marker.Committed, + marker.CoordinatorEpoch, marker.TransactionVersion, } for _, topic := range marker.Topics { @@ -4392,6 +4559,7 @@ func (cl *writeTxnMarkersSharder) shard(ctx context.Context, kreq kmsg.Request, rm.ProducerID = pec.pid rm.ProducerEpoch = pec.epoch rm.Committed = pec.commit + rm.CoordinatorEpoch = pec.coordinatorEpoch rm.TransactionVersion = pec.txnVersion for topic, parts := range topics { rt := kmsg.NewWriteTxnMarkersRequestMarkerTopic() @@ -4414,6 +4582,7 @@ func (cl *writeTxnMarkersSharder) shard(ctx context.Context, kreq kmsg.Request, rm.ProducerID = pec.pid rm.ProducerEpoch = pec.epoch rm.Committed = pec.commit + rm.CoordinatorEpoch = pec.coordinatorEpoch rm.TransactionVersion = pec.txnVersion for topic, parts := range topics { rt := kmsg.NewWriteTxnMarkersRequestMarkerTopic() @@ -5450,7 +5619,7 @@ func (cl *describeShareGroupOffsetsSharder) shard(ctx context.Context, kreq kmsg for _, g := range req.Groups { groupIDs = append(groupIDs, g.GroupID) } - coordinators := cl.loadCoordinators(ctx, coordinatorTypeShare, groupIDs...) + coordinators := cl.loadCoordinators(ctx, coordinatorTypeGroup, groupIDs...) type unkerr struct { err error groupID string @@ -5510,7 +5679,7 @@ func (cl *describeShareGroupOffsetsSharder) onResp(_ kmsg.Request, kresp kmsg.Re for i := range resp.Groups { group := &resp.Groups[i] err := kerr.ErrorForCode(group.ErrorCode) - cl.maybeDeleteStaleCoordinator(group.GroupID, coordinatorTypeShare, err) + cl.maybeDeleteStaleCoordinator(group.GroupID, coordinatorTypeGroup, err) onRespShardErr(&retErr, err) } return retErr diff --git a/vendor/github.com/twmb/franz-go/pkg/kgo/compression.go b/vendor/github.com/twmb/franz-go/pkg/kgo/compression.go index ff114664a9..216939bd8c 100644 --- a/vendor/github.com/twmb/franz-go/pkg/kgo/compression.go +++ b/vendor/github.com/twmb/franz-go/pkg/kgo/compression.go @@ -6,6 +6,7 @@ import ( "encoding/binary" "errors" "io" + "math" "runtime" "slices" "sync" @@ -17,6 +18,24 @@ import ( var byteBuffers = sync.Pool{New: func() any { return bytes.NewBuffer(make([]byte, 8<<10)) }} +// maxDecompressedSize caps how much one batch may decompress to. Fetch +// limits bound only the COMPRESSED bytes on the wire; nothing in the +// protocol bounds the decompressed size, and the whole batch is +// materialized contiguously while decompressing. Without a cap, a few-KB +// malicious or corrupt batch can demand tens of GiB: zstd frames declare a +// content size that is honored up to the decoder's configured limit (the +// library default is 64 GiB), gzip expands up to ~1032x, lz4 up to ~255x, +// and snappy headers claim up to 4 GiB. No legitimate batch can exceed +// math.MaxInt32 decompressed: every known producer serializes a batch's +// records into an int32-indexed buffer before compressing (this client's +// own appendTo, Java's MemoryRecordsBuilder over a ByteBuffer, librdkafka), +// so a batch claiming more is corrupt or hostile and is rejected like any +// other corrupt batch: a loud, repeated fetch error with no offset advance. +// A var only so tests can shrink it. +var maxDecompressedSize = int64(math.MaxInt32) + +var errDecompressedTooLarge = errors.New("decompressed data exceeds the maximum allowed decompressed batch size (corrupt or malicious batch)") + // CompressionCodecType is a bitfield specifying a Kafka-defined compression // codec. Per spec, only four compression codecs are supported. However, if // you control both the producer and consumer, you can technically override the @@ -35,8 +54,9 @@ const ( // CodecZstd is a compression codec signifying zstd compression. CodecZstd - // CodecError is returned from compressing or decompressing if an error - // occurred. + // CodecError is returned as the used-codec from Compress if an error + // occurred while compressing (Decompress reports errors via its error + // return instead). CodecError = -1 ) @@ -176,7 +196,7 @@ out: case CodecGzip: level := gzip.DefaultCompression if codec.level != 0 { - if _, err := gzip.NewWriterLevel(nil, codec.level); err != nil { + if _, err := gzip.NewWriterLevel(nil, codec.level); err == nil { level = codec.level } } @@ -322,6 +342,7 @@ func DefaultDecompressor(pools ...Pool) Decompressor { zstdDec, _ := zstd.NewReader(nil, zstd.WithDecoderLowmem(true), zstd.WithDecoderConcurrency(1), + zstd.WithDecoderMaxMemory(uint64(maxDecompressedSize)), ) r := &zstdDecoder{zstdDec} runtime.SetFinalizer(r, func(r *zstdDecoder) { @@ -352,7 +373,12 @@ func (d *decompressor) Decompress(src []byte, codecType CompressionCodecType) ([ d.pools.each(func(p Pool) bool { if pdecompressBytes, ok := p.(PoolDecompressBytes); ok { s := pdecompressBytes.GetDecompressBytes(src, codecType) - out = bytes.NewBuffer(s) + // Only the slice's capacity is used: decompressed data + // must start at index 0, while a buffer initialized with + // len(s) > 0 (a pool returning make([]byte, sizeGuess)) + // would have the copy/append based codecs write AFTER the + // existing length, prefixing the output with stale bytes. + out = bytes.NewBuffer(s[:0]) rfn = out.Bytes userPooled = true return true @@ -382,13 +408,30 @@ func (d *decompressor) Decompress(src []byte, codecType CompressionCodecType) ([ if err := ungz.Reset(bytes.NewReader(src)); err != nil { return nil, err } - if _, err := io.Copy(out, ungz); err != nil { + if n, err := io.Copy(out, io.LimitReader(ungz, maxDecompressedSize+1)); err != nil { return nil, err + } else if n > maxDecompressedSize { + return nil, errDecompressedTooLarge } return rfn(), nil case CodecSnappy: if len(src) > 16 && bytes.HasPrefix(src, xerialPfx) { - return xerialDecode(src) + // Decode into the pooled destination when one exists; + // this path previously ignored the pool's Get entirely + // (fresh allocation every batch, and the Get'd slice was + // orphaned: never used, never put back). + var xdst []byte + if userPooled { + xdst = out.Bytes() + } + return xerialDecode(xdst, src) + } + // The decoded length is read from the header and allocated up + // front; check the claim before decoding. + if l, err := s2.DecodedLen(src); err != nil { + return nil, err + } else if int64(l) > maxDecompressedSize { + return nil, errDecompressedTooLarge } decoded, err := s2.Decode(out.Bytes(), src) if err != nil { @@ -402,8 +445,10 @@ func (d *decompressor) Decompress(src []byte, codecType CompressionCodecType) ([ unlz4 := d.unlz4Pool.Get().(*lz4.Reader) defer d.unlz4Pool.Put(unlz4) unlz4.Reset(bytes.NewReader(src)) - if _, err := io.Copy(out, unlz4); err != nil { + if n, err := io.Copy(out, io.LimitReader(unlz4, maxDecompressedSize+1)); err != nil { return nil, err + } else if n > maxDecompressedSize { + return nil, errDecompressedTooLarge } return rfn(), nil case CodecZstd: @@ -426,13 +471,15 @@ var xerialPfx = []byte{130, 83, 78, 65, 80, 80, 89, 0} var errMalformedXerial = errors.New("malformed xerial framing") -func xerialDecode(src []byte) ([]byte, error) { +// xerialDecode appends the decoded chunks to dst (commonly a len-0 pooled +// slice, or nil) and returns the result. +func xerialDecode(dst, src []byte) ([]byte, error) { // bytes 0-8: xerial header // bytes 8-16: xerial version // everything after: uint32 chunk size, snappy chunk // we come into this function knowing src is at least 16 src = src[16:] - var dst, chunk []byte + var chunk []byte var err error for len(src) > 0 { if len(src) < 4 { @@ -443,6 +490,13 @@ func xerialDecode(src []byte) ([]byte, error) { if size < 0 || len(src) < int(size) { return nil, errMalformedXerial } + // Chunks accumulate; bound the cumulative claimed output before + // decoding each chunk. + if l, err := s2.DecodedLen(src[:size]); err != nil { + return nil, err + } else if int64(l) > maxDecompressedSize-int64(len(dst)) { + return nil, errDecompressedTooLarge + } if chunk, err = s2.Decode(chunk[:cap(chunk)], src[:size]); err != nil { return nil, err } diff --git a/vendor/github.com/twmb/franz-go/pkg/kgo/config.go b/vendor/github.com/twmb/franz-go/pkg/kgo/config.go index ee361a1c05..72d5d82ae1 100644 --- a/vendor/github.com/twmb/franz-go/pkg/kgo/config.go +++ b/vendor/github.com/twmb/franz-go/pkg/kgo/config.go @@ -286,6 +286,18 @@ func (cfg *cfg) validate() error { i64lt := func(l, r int64) (bool, string) { return l < r, "less" } i64gt := func(l, r int64) (bool, string) { return l > r, "larger" } + + // Several Durations are serialized to int32-millisecond wire fields + // (JoinGroup SessionTimeoutMs / RebalanceTimeoutMs, ProduceRequest + // TimeoutMs, InitProducerId TransactionTimeoutMs). A Duration whose + // millisecond value exceeds an int32 silently overflows that field when + // cast - e.g. a 30 day session timeout wraps to a negative wire value, + // and a ~50 day one wraps to a small positive value the broker quietly + // accepts as a completely different timeout. Java cannot hit this because + // its equivalent configs are int32-millisecond typed at the source; kgo + // takes a Duration, so the bound must be enforced here. The wire field's + // capacity is the only principled cap. + const maxWireTimeout = time.Duration(math.MaxInt32) * time.Millisecond for _, limit := range []struct { name string v int64 @@ -343,6 +355,11 @@ func (cfg *cfg) validate() error { {name: "max buffered bytes", v: cfg.maxBufferedBytes, allowed: 0, badcmp: i64lt}, {name: "linger", v: int64(cfg.linger), allowed: int64(time.Minute), badcmp: i64gt, durs: true}, {name: "produce timeout", v: int64(cfg.produceTimeout), allowed: int64(100 * time.Millisecond), badcmp: i64lt, durs: true}, + {name: "produce timeout", v: int64(cfg.produceTimeout), allowed: int64(maxWireTimeout), badcmp: i64gt, durs: true}, + + // The transaction timeout is serialized to an int32-millisecond wire + // field and is otherwise unvalidated; bound it so it cannot overflow. + {name: "transaction timeout", v: int64(cfg.txnTimeout), allowed: int64(maxWireTimeout), badcmp: i64gt, durs: true}, {name: "record timeout", v: int64(cfg.recordTimeout), allowed: int64(time.Second), badcmp: func(l, r int64) (bool, string) { if l == 0 { return false, "" // we print nothing when things are good @@ -360,10 +377,12 @@ func (cfg *cfg) validate() error { {name: "consumer protocol length", v: int64(len(cfg.protocol)), allowed: 1, badcmp: i64lt}, {name: "session timeout", v: int64(cfg.sessionTimeout), allowed: int64(100 * time.Millisecond), badcmp: i64lt, durs: true}, + {name: "session timeout", v: int64(cfg.sessionTimeout), allowed: int64(maxWireTimeout), badcmp: i64gt, durs: true}, {name: "rebalance timeout", v: int64(cfg.rebalanceTimeout), allowed: int64(100 * time.Millisecond), badcmp: i64lt, durs: true}, + {name: "rebalance timeout", v: int64(cfg.rebalanceTimeout), allowed: int64(maxWireTimeout), badcmp: i64gt, durs: true}, {name: "autocommit interval", v: int64(cfg.autocommitInterval), allowed: int64(100 * time.Millisecond), badcmp: i64lt, durs: true}, - {v: int64(cfg.heartbeatInterval), allowed: int64(cfg.rebalanceTimeout) * int64(time.Millisecond), badcmp: i64gt, durs: true, fmt: "heartbeat interval %v is erroneously larger than the session timeout %v"}, + {v: int64(cfg.heartbeatInterval), allowed: int64(cfg.sessionTimeout), badcmp: i64gt, durs: true, fmt: "heartbeat interval %v is erroneously larger than the session timeout %v"}, } { bad, cmp := limit.badcmp(limit.v, limit.allowed) if bad { @@ -443,6 +462,40 @@ func (cfg *cfg) validate() error { return errors.New("invalid use of ConsumeExcludeTopics when not using ConsumeRegex") } + // A topic literally named "" cannot exist; consuming it would spin on + // UNKNOWN_TOPIC metadata forever with no surfaced error. In regex mode the + // ConsumeTopics values are patterns ("" is a valid match-all regex), so we + // only reject empty names when not consuming via regex. + if !cfg.regex { + for topic := range cfg.topics { + if topic == "" { + return errors.New("invalid empty topic name in ConsumeTopics") + } + } + } + for topic, partitions := range cfg.partitions { + if topic == "" { + return errors.New("invalid empty topic name in ConsumePartitions") + } + for p := range partitions { + if p < 0 { + return fmt.Errorf("invalid negative partition %d for topic %q in ConsumePartitions", p, topic) + } + } + } + + // These options take a value; if explicitly set to "" it is a mistake (an + // empty transactional or instance ID is not valid). Both are *string so an + // explicit "" is distinguishable from unset (nil). ConsumerGroup and + // ShareGroup are plain strings, indistinguishable from unset, so they are + // not checked here. + if cfg.txnID != nil && *cfg.txnID == "" { + return errors.New("invalid empty TransactionalID") + } + if cfg.instanceID != nil && *cfg.instanceID == "" { + return errors.New("invalid empty InstanceID") + } + if cfg.topics != nil && cfg.partitions != nil { for topic := range cfg.partitions { if _, exists := cfg.topics[topic]; exists { @@ -686,6 +739,12 @@ func WithLogger(l Logger) Opt { // WithContext sets the client to use a custom context. // // By default, the client uses context.Background. +// +// Canceling this context stops the client's background goroutines and fails +// in-flight requests with ErrClientClosed, but it does not replace Close: +// records still buffered for producing are reliably failed only by Close, and +// a group leave is sent only by Close. Always call Close for a clean shutdown; +// canceling this context first (to interrupt blocking calls) is fine. func WithContext(ctx context.Context) Opt { return clientOpt{func(cfg *cfg) { cfg.ctx = ctx }} } @@ -813,7 +872,7 @@ func MinVersions(versions *kversion.Versions) Opt { // RetryBackoffFn sets the backoff strategy for how long to backoff for a given // amount of retries, overriding the default jittery exponential backoff that -// ranges from 250ms min to 2.5s max. +// ranges from 250ms min to 5s max. // // This (roughly) corresponds to Kafka's retry.backoff.ms setting and // retry.backoff.max.ms (which is being introduced with KIP-500). @@ -1296,16 +1355,18 @@ func RecordRetries(n int) ProducerOpt { } // UnknownTopicRetries sets the number of times a record can fail with -// UNKNOWN_TOPIC_OR_PARTITION, overriding the default 4. +// UNKNOWN_TOPIC_OR_PARTITION or UNKNOWN_TOPIC_ID, overriding the default 4. // // This is a separate limit from RecordRetries because unknown topic or // partition errors should only happen if the topic does not exist. It is // pointless for the client to continue producing to a topic that does not // exist, and if we repeatedly see that the topic does not exist across // multiple metadata queries (which are going to different brokers), then we -// may as well stop trying and fail the records. +// may as well stop trying and fail the records. The count is reset whenever +// a produce to the partition succeeds; errors other than the two unknown +// topic errors leave it unchanged. // -// If this is -1, the client never fails records with this error. +// If this is -1, the client never fails records with these errors. func UnknownTopicRetries(n int) ProducerOpt { return producerOpt{func(cfg *cfg) { cfg.maxUnknownFailures = int64(n) }} } @@ -1409,9 +1470,9 @@ func TransactionalID(id string) ProducerOpt { // default 40s. It is a good idea to keep this less than a group's session // timeout, so that a group member will always be alive for the duration of a // transaction even if connectivity dies. This helps prevent a transaction -// finishing after a rebalance, which is problematic pre-Kafka 2.5. If you -// are on Kafka 2.5+, then you can use the RequireStableFetchOffsets option -// when assigning the group, and you can set this to whatever you would like. +// finishing after a rebalance, which is problematic pre-Kafka 2.5. On Kafka +// 2.5+, the client always requires stable fetch offsets (KIP-447), so you +// can set this to whatever you would like. // // Transaction timeouts begin when the first record is produced within a // transaction, not when a transaction begins. @@ -1877,9 +1938,9 @@ func Balancers(balancers ...GroupBalancer) GroupOpt { // If you are using a [GroupTransactSession] for EOS, wish to lower this, and are // talking to a Kafka cluster pre 2.5, consider lowering the // TransactionTimeout. If you do not, you risk a transaction finishing after a -// group has rebalanced, which could lead to duplicate processing. If you are -// talking to a Kafka 2.5+ cluster, you can safely use the -// RequireStableFetchOffsets group option and prevent any problems. +// group has rebalanced, which could lead to duplicate processing. On a Kafka +// 2.5+ cluster there is no problem: the client always requires stable fetch +// offsets (KIP-447). // // This option corresponds to Kafka's session.timeout.ms setting and must be // within the broker's group.min.session.timeout.ms and @@ -2002,6 +2063,13 @@ func AdjustFetchOffsetsFn(adjustOffsetsBeforeAssign func(context.Context, map[st // This function can be called at any time you are polling or processing // records. If you want to ensure this function is called serially with // processing, consider the BlockRebalanceOnPoll option. +// +// Do not call Close or LeaveGroup synchronously from within this callback +// (nor from OnPartitionsRevoked or OnPartitionsLost): leaving the group +// waits for the group management loop to finish, and the loop is waiting +// for your callback to return - a permanent deadlock. To leave from within +// a callback, use LeaveGroupContext with a nil context (it triggers the +// leave without waiting), or call LeaveGroup from a separate goroutine. func OnPartitionsAssigned(onAssigned func(context.Context, *Client, map[string][]int32)) GroupOpt { return groupOpt{func(cfg *cfg) { cfg.onAssigned = onAssigned }} } @@ -2034,6 +2102,9 @@ func OnPartitionsAssigned(onAssigned func(context.Context, *Client, map[string][ // // This function is called if a "fatal" group error is encountered and you have // not set [OnPartitionsLost]. See OnPartitionsLost for more details. +// +// Do not call Close or LeaveGroup synchronously from within this callback; +// see the warning on [OnPartitionsAssigned]. func OnPartitionsRevoked(onRevoked func(context.Context, *Client, map[string][]int32)) GroupOpt { return groupOpt{func(cfg *cfg) { cfg.onRevoked = onRevoked }} } @@ -2053,6 +2124,9 @@ func OnPartitionsRevoked(onRevoked func(context.Context, *Client, map[string][]i // This function can be called at any time you are polling or processing // records. If you want to ensure this function is called serially with // processing, consider the BlockRebalanceOnPoll option. +// +// Do not call Close or LeaveGroup synchronously from within this callback; +// see the warning on [OnPartitionsAssigned]. func OnPartitionsLost(onLost func(context.Context, *Client, map[string][]int32)) GroupOpt { return groupOpt{func(cfg *cfg) { cfg.onLost = onLost }} } diff --git a/vendor/github.com/twmb/franz-go/pkg/kgo/consumer.go b/vendor/github.com/twmb/franz-go/pkg/kgo/consumer.go index b5e5443123..4970f96574 100644 --- a/vendor/github.com/twmb/franz-go/pkg/kgo/consumer.go +++ b/vendor/github.com/twmb/franz-go/pkg/kgo/consumer.go @@ -263,7 +263,16 @@ func (c *consumer) unaddPoller() { } c.pollWaitMu.Lock() defer c.pollWaitMu.Unlock() - c.pollWaitState-- + // AllowRebalance zeroes the poller count outright. If the user calls it + // while another goroutine's poll is still in flight (a contract + // violation: AllowRebalance means "all pollers are done"), that poll's + // release lands here after the mask and must not decrement: the low 32 + // bits would underflow and borrow into the rebalance count, permanently + // blocking both polls and rebalances. A poller whose accounting was + // force-cleared simply no-ops its release. + if c.pollWaitState&math.MaxUint32 > 0 { + c.pollWaitState-- + } c.pollWaitC.Broadcast() } @@ -401,7 +410,7 @@ func (c *consumer) addFakeReadyForDraining(topic string, partition int32, err er } // NewErrFetch returns a fake fetch containing a single empty topic with a -// single zero partition with the given error. +// single partition of -1 with the given error. func NewErrFetch(err error) Fetches { return []Fetch{{ Topics: []FetchTopic{{ @@ -420,7 +429,7 @@ func NewErrFetch(err error) Fetches { // equivalent to calling PollRecords(ctx, 0). // // If the client is closed, a fake fetch will be injected that has no topic, a -// partition of 0, and a partition error of ErrClientClosed. If the context is +// partition of -1, and a partition error of ErrClientClosed. If the context is // canceled, a fake fetch will be injected with ctx.Err. These injected errors // can be used to break out of a poll loop. // @@ -816,7 +825,12 @@ func (c *consumer) purgeTopics(topics []string) { delete(c.g.using, topic) delete(c.g.reSeen, topic) } - c.g.rejoin("rejoin from PurgeFetchTopics") + // Our subscription shrank; reconcile per protocol. This MUST NOT + // feed rejoinCh in 848 mode (signalSubscriptionChange forces a + // heartbeat instead): a rejoinCh bounce there runs the session-end + // revoke's nowAssigned read-modify-write concurrently with live + // heartbeats, losing a heartbeat's nowAssigned store. + c.g.signalSubscriptionChange("topics purged from consuming") } else { c.assignPartitions(purgeAssignments, assignPurgeMatching, c.d.tps, fmt.Sprintf("purge of %v requested", topics)) for _, topic := range topics { @@ -838,7 +852,7 @@ func (c *consumer) purgeTopics(topics []string) { // entire topic is purged. func (cl *Client) AddConsumeTopics(topics ...string) { c := &cl.consumer - if len(topics) == 0 || c.g == nil && c.d == nil || cl.cfg.regex { + if len(topics) == 0 || !c.consuming() || cl.cfg.regex { return } @@ -863,7 +877,7 @@ func (cl *Client) AddConsumeTopics(topics ...string) { // GetConsumeTopics retrieves a list of current topics being consumed. func (cl *Client) GetConsumeTopics() []string { c := &cl.consumer - if c.g == nil && c.d == nil { + if !c.consuming() { return nil } var m map[string]*topicPartitions @@ -1045,10 +1059,9 @@ func (f fmtAssignment) String() string { // assignPartitions, called under the consumer's mu, is used to set new cursors // or add to the existing cursors. // -// We do not need to pass tps when we are bumping the session or when we are -// invalidating all. All other cases, we want the tps -- the logic below does -// not fully differentiate needing to start a new session vs. just reusing the -// old (third if case below) +// We do not need to pass tps when we are invalidating all. All other cases, +// we want the tps: guarding the session may create a new session needing it, +// and stopping the session needs it for the restart. func (c *consumer) assignPartitions(assignments map[string]map[int32]Offset, how assignHow, tps *topicsPartitions, why string) { if c.mu.TryLock() { c.mu.Unlock() @@ -1115,6 +1128,23 @@ func (c *consumer) assignPartitions(assignments map[string]map[int32]Offset, how offset: assignPart.at, lastConsumedEpoch: assignPart.epoch, }) + // This partition can have a pending list or epoch + // load: an OffsetOutOfRange reload, or an epoch + // validation from a leader move. The cursor is then + // unusable and the load's completion is its only + // re-enabler -- and that completion would also + // overwrite the offset we just set with the load's + // now-stale result. A transact session resetting to + // committed offsets after an abort would be undone: + // consumption would resume at the pre-abort position, + // never re-consuming the aborted records. The set + // offset is the new truth: drop the load and + // re-enable the cursor ourselves. Safe here because + // the session is stopped (no source can use the + // cursor until the new session starts). + if loadOffsets.removeLoad(usedCursor.topic, usedCursor.partition) { + usedCursor.allowUsable() + } } } } @@ -1140,8 +1170,12 @@ func (c *consumer) assignPartitions(assignments map[string]map[int32]Offset, how case assignInvalidateAll: loadOffsets = listOrEpochLoads{} case assignSetMatching: - // We had not yet loaded this partition, so there is - // nothing to set, and we keep everything. + // Loads for partitions that were being consumed were + // handled in the cursor walk above (offset set directly, + // pending load dropped). Anything remaining is a load for + // a partition that never finished loading; SetOffsets + // documents those are skipped, so we keep their loads + // untouched. case assignInvalidateMatching: loadOffsets.keepFilter(func(t string, p int32) bool { if assignTopic, ok := assignments[t]; ok { @@ -1466,7 +1500,7 @@ func (l *listOrEpochLoads) addLoad(t string, p int32, loadType listOrEpochLoadTy ps[p] = load } -func (l *listOrEpochLoads) removeLoad(t string, p int32) { +func (l *listOrEpochLoads) removeLoad(t string, p int32) (removed bool) { for _, m := range []offsetLoadMap{ l.List, l.Epoch, @@ -1478,11 +1512,16 @@ func (l *listOrEpochLoads) removeLoad(t string, p int32) { if ps == nil { continue } + if _, exists := ps[p]; !exists { + continue + } delete(ps, p) + removed = true if len(ps) == 0 { delete(m, t) } } + return removed } func (l listOrEpochLoads) each(fn func(string, int32)) { @@ -1679,7 +1718,14 @@ func (fm *fetchManager) manageFetchConcurrency() { continue } - if wantQuit && activeFetches == 0 { + // We cannot return while sources are still registered in + // wantFetch: each of them is (or is about to be) blocked + // sending on cancelFetchCh, which only we drain. Returning + // early orphans those sends once the channel's small buffer + // fills; the sources then hold session workers forever and + // stopSession never finishes (consumer deadlock). Keep + // looping until every registered source has canceled. + if wantQuit && activeFetches == 0 && len(wantFetch) == 0 { return } } @@ -1937,6 +1983,19 @@ func (s *consumerSession) listOrEpoch(waiting listOrEpochLoads, immediate bool, } } + // If the session is dying, park: the loads were stored in waiting above, + // and stopSession returns waiting loads to the caller for the next + // session. Without this check, a dying session whose metadata is fresh + // busy-loops until metadataMinAge elapses: wait is false above, so we + // would issue requests on the dead context, every one fails instantly, + // the reload timer below is canceled by the same dead context, and its + // loadWithSession re-enters this function - burning CPU and holding the + // session-stop (every revoke, leave, close, or seek) for the full + // metadataMinAge. + if s.ctx.Err() != nil { + return + } + s.listOrEpochMu.Lock() loading := s.listOrEpochLoadsWaiting s.listOrEpochLoadsLoading.mergeFrom(loading) @@ -2395,8 +2454,23 @@ func (cl *Client) listOffsetsForBrokerLoad(ctx context.Context, broker *broker, offset = start } } + // Every arm above yields a non-negative offset from a + // well-behaved broker: by-time listings exist only for + // afterMilli, whose -1 is replaced by the end listing, + // and the start/end/exact arms bound within the + // responses. A negative offset here means the broker + // violated the protocol; clamping to 0 (the old + // behavior) would silently re-consume the partition + // from the start. Surface the misbehavior and retry + // the load instead. if offset < 0 { - offset = 0 // sanity + loaded.add(loadedOffset{ + topic: topic, + partition: partition, + err: errNegativeListedOffset, + request: loadPart, + }) + continue } loaded.add(loadedOffset{ @@ -2422,10 +2496,14 @@ func (*Client) loadEpochsForBrokerLoad(ctx context.Context, broker *broker, load return } - // If the version is < 2, we are speaking to an old broker. We should - // not have an old version, but we could have spoken to a new broker - // first then an old broker in the middle of a broker roll. For now, we - // will just loop retrying until the broker is upgraded. + // If the negotiated version is < 2, we are speaking to an old broker: + // possible mid-roll when metadata (with leader epochs) came from an + // upgraded broker while the partition leader is not yet upgraded. The + // request then goes out without CurrentLeaderEpoch fencing, and a v0 + // response carries no LeaderEpoch (kmsg defaults it to -1). Validation + // still compares EndOffset and completes, just unfenced and without + // epoch information - a degraded but correct fallback for a window + // that only exists rolling from pre-KIP-320 brokers. topics := tps.load() resp := kresp.(*kmsg.OffsetForLeaderEpochResponse) @@ -2469,7 +2547,27 @@ func (*Client) loadEpochsForBrokerLoad(ctx context.Context, broker *broker, load // validating. offset := loadPart.at var err error - if rPartition.EndOffset < offset { + switch { + case rPartition.EndOffset < 0: + // KIP-320 UNDEFINED_EPOCH_OFFSET: a conformant broker + // answers endOffset -1 (and leaderEpoch -1) when its + // leader-epoch cache holds no record of the requested + // epoch - an empty or freshly-truncated cache, an unclean + // election to a replica with no epoch history, or an epoch + // newer than anything in the log. This is NOT data loss; the + // broker simply cannot tell us a truncation point. The old + // `EndOffset < offset` arm treated the -1 sentinel as + // "truncated to offset -1", surfacing a spurious ErrDataLoss + // and pinning the cursor at -1. Instead carry the sentinel + // through so the next fetch hits OFFSET_OUT_OF_RANGE and + // resets via the configured ConsumeResetOffset (or, under + // NoResetOffset, surfaces OOOR rather than a bogus data-loss + // error) - the same reset path the cursor already took, + // minus the false alarm. Matches the Java client, which + // resets per policy on this sentinel rather than comparing + // -1 against the validating position. + offset = rPartition.EndOffset + case rPartition.EndOffset < offset: err = &ErrDataLoss{topic, partition, offset, loadPart.epoch, rPartition.EndOffset, rPartition.LeaderEpoch} offset = rPartition.EndOffset } diff --git a/vendor/github.com/twmb/franz-go/pkg/kgo/consumer_group.go b/vendor/github.com/twmb/franz-go/pkg/kgo/consumer_group.go index 7ccc5d195e..04fa0f07f0 100644 --- a/vendor/github.com/twmb/franz-go/pkg/kgo/consumer_group.go +++ b/vendor/github.com/twmb/franz-go/pkg/kgo/consumer_group.go @@ -153,10 +153,17 @@ type groupConsumer struct { // done, and then starts its own. commitDone chan struct{} - // blockAuto is set and cleared in CommitOffsets{,Sync} to block - // autocommitting if autocommitting is active. This ensures that an - // autocommit does not cancel the user's manual commit. - blockAuto bool + // blockAuto counts outstanding CommitOffsets{,Sync} commits and + // blocks autocommitting while any are in flight. This ensures that + // an autocommit does not trample a user's manual commit: the + // autocommit snapshot is taken when the commit is enqueued, but the + // request is issued only after all prior commits complete, so a + // snapshot taken while a higher manual commit is in flight would be + // committed after it and rewind the broker's offset. This must be a + // count, not a bool: with two overlapping async commits, the first + // completion would otherwise re-enable autocommit while the second + // is still in flight, reopening exactly that window. + blockAuto int // We set this once to manage the group lifecycle once. // If we detect we should run in 848 mode, we set is848 true. @@ -237,6 +244,12 @@ func (cl *Client) LeaveGroup() { // group. If you have configured the group with an InstanceID, this // does not leave the group. // +// For next-gen (KIP-848) consumer groups, the leave is a final +// heartbeat with MemberEpoch -1; with an InstanceID configured the +// epoch is -2 instead, a temporary "static" departure where the +// member's partitions stay reserved until the session timeout or +// until a replacement with the same InstanceID rejoins. +// // For share groups: this drains any pending acks, releases records that // were acquired but never finalized, closes each per-broker share // session, and sends the final ShareGroupHeartbeat with MemberEpoch=-1 @@ -250,6 +263,13 @@ func (cl *Client) LeaveGroup() { // failed with an internal "consumer left" error). // // LeaveGroupContext is a no-op for direct (non-group) consumers. +// +// Do not call this function with a non-nil context synchronously from +// within an OnPartitions callback: the leave waits for the group +// management loop to finish, and the loop is waiting for your callback to +// return. With the client's own context (LeaveGroup), that is a permanent +// deadlock. A nil context is safe anywhere: it triggers the leave and +// returns immediately. func (cl *Client) LeaveGroupContext(ctx context.Context) error { c := &cl.consumer if c.g == nil && c.s == nil { @@ -465,7 +485,7 @@ func (g *groupConsumer) manageFailWait(consecutiveErrors int, err error) (ctxCan g.resetExternal() } - // Unblock bolling now that we have called onLost and + // Unblock polling now that we have called onLost and // re-assigned. g.c.unaddRebalance() @@ -811,7 +831,25 @@ func (g *groupConsumer) revoke(stage revokeStage, lost map[string][]int32, leavi } if stage != revokeThisSession { // cooperative consumers rejoin after revoking what they lost - defer g.rejoin("after revoking what we lost from a rebalance") + // The rejoin triggers the second join of the classic + // cooperative two-phase rebalance: after giving up lost + // partitions, the member rejoins so the group can reassign + // them. 848 has no second join, the server reconciles + // through heartbeats. There, this signal would only tear + // down and rebuild the heartbeat session: the session that + // called us already handled both our lost and added + // partitions, so the rebuilt session re-enters with an empty + // diff, and because rebuilding re-arms the heartbeat timer + // at a full interval, the bounce also DELAYS the heartbeat + // that acks our revocation to the server. For 848, prerevoke + // instead forces an immediate heartbeat to ack the + // revocation. + g.mu.Lock() + is848 := g.is848 + g.mu.Unlock() + if !is848 { + defer g.rejoin("after revoking what we lost from a rebalance") + } } // The block below deletes everything lost from our uncommitted map. @@ -863,6 +901,7 @@ func (s *assignRevokeSession) prerevoke(g *groupConsumer, lost map[string][]int3 // very first concurrent heartbeat sends keepalive. g.mu.Lock() g848 := g.g848 + is848 := g.is848 // g848 stays non-nil after a fallback to classic; is848 is what tracks the protocol g.mu.Unlock() if g848 != nil { g848.prerevoking.Store(true) @@ -877,6 +916,40 @@ func (s *assignRevokeSession) prerevoke(g *groupConsumer, lost map[string][]int3 if g848 != nil { g848.prerevoking.Store(false) } + // If we revoked, ack the revocation to the server right away + // rather than waiting out the heartbeat timer: the server + // cannot give the revoked partitions to other members until + // it sees a heartbeat without them, so an immediate full + // heartbeat (prerevoking was cleared above, so it will not + // be a keepalive) directly speeds group-wide reconciliation. + // The Java client acks the same way the moment revocation + // callbacks complete. + // + // The send must be best effort, NOT blocking. If the + // heartbeat loop exits on a fatal error before consuming our + // send (its first heartbeat can fail while we are still + // revoking), nothing reads heartbeatForceCh again until the + // next session begins, but the next session cannot begin + // until we return: setupAssignedAndHeartbeat waits on + // assignDone, which waits on prerevokeDone, which closes + // only when this goroutine exits. A blocking send would + // deadlock the manage loop. If the send is missed (loop + // mid-heartbeat or already gone), the regular heartbeat + // timer acks within one interval, which is no worse than + // what the session bounce this replaced provided. + // + // We force even when nothing was lost: a session (re)entry + // with only added partitions also owes the server an ack - + // the next full heartbeat's Topics is what reports the new + // assignment as owned. The Java client likewise heartbeats + // the moment reconciliation completes rather than waiting + // out the interval. + if is848 { + select { + case g.heartbeatForceCh <- func(error) {}: + default: + } + } }() return s.prerevokeDone } @@ -1076,6 +1149,7 @@ func (g *groupConsumer) heartbeat(initialHb time.Duration, fetchErrCh <-chan err for { var err error var force func(error) + var fetchErr bool heartbeat = false select { case <-cooperativeFastCheck: @@ -1091,6 +1165,7 @@ func (g *groupConsumer) heartbeat(initialHb time.Duration, fetchErrCh <-chan err err = kerr.RebalanceInProgress case err = <-fetchErrCh: fetchErrCh = nil + fetchErr = true case <-revoked: revoked = nil didRevoke = true @@ -1147,7 +1222,22 @@ func (g *groupConsumer) heartbeat(initialHb time.Duration, fetchErrCh <-chan err // If cfg.retries consecutive failures occur without any // success, the error propagates to manage848 which // rebuilds the session. - if is848 && (isRetryableBrokerErr(err) || isAnyDialErr(err) || g.cl.maybeDeleteStaleCoordinator(g.cfg.group, coordinatorTypeGroup, err)) { + // + // This arm must only see errors from the heartbeat itself, + // never from fetchErrCh, even though both feed the same err + // variable. Walkthrough of what would go wrong: the + // coordinator moves, OffsetFetch exhausts its internal + // retries, fetchOffsets returns the retryable error here, we + // "retry" by heartbeating in place, the next heartbeat + // succeeds and resets the counter - and the session lives on + // with partitions that were never handed to assignPartitions + // (that only happens after a successful fetch). The fetch + // goroutine is already gone and nothing inside a live session + // re-runs it, so those partitions silently never consume. + // Only re-entering setupAssignedAndHeartbeat re-fetches (via + // g.fetching), so a fetch error must propagate to manage848, + // whose transient arm restarts the session. + if is848 && !fetchErr && (isRetryableBrokerErr(err) || isAnyDialErr(err) || g.cl.maybeDeleteStaleCoordinator(g.cfg.group, coordinatorTypeGroup, err)) { if int64(hbBrokerRetries) < g.cfg.retries { hbBrokerRetries++ backoff := g.cfg.retryBackoff(hbBrokerRetries) @@ -1234,9 +1324,23 @@ func (g *groupConsumer) heartbeat(initialHb time.Duration, fetchErrCh <-chan err // If neither of the cases above are true (this member is not a leader, and the // join group metadata has not changed), then Kafka will not actually trigger a // rebalance and will instead reply to the member with its current assignment. +// +// For next-gen (KIP-848) consumer groups, assignment is entirely server +// driven and there is no client-side join to redo: this instead forces an +// immediate full heartbeat (best effort), which re-syncs the subscription +// and assignment with the broker. func (cl *Client) ForceRebalance() { if g := cl.consumer.g; g != nil { - g.rejoin("from ForceRebalance") + // Classic rejoins; 848 forces a heartbeat (848 must never feed + // rejoinCh - see signalSubscriptionChange). Routing every + // rejoinCh feeder through the one helper is what keeps "nothing + // feeds rejoinCh in 848 mode" true: every remaining session-end + // path stops heartbeating (or has a dead context) before revoke + // runs, so revoke's nowAssigned read-modify-write never races a + // live heartbeat's store. + g.mu.Lock() + g.signalSubscriptionChange("from ForceRebalance") + g.mu.Unlock() } } @@ -1250,6 +1354,36 @@ func (g *groupConsumer) rejoin(why string) { } } +// signalSubscriptionChange tells the manage loop that our local set of +// subscribed topics changed (AddConsumeTopics growth, PurgeConsumeTopics +// shrink) and a reconcile is owed. The caller must hold g.mu (we read +// g.is848). +// +// Classic and 848 reconcile differently, and routing every caller through +// here is what keeps the difference from being re-derived (and forgotten) +// at each site: +// +// - Classic: bounce the heartbeat session via rejoinCh so the member +// re-joins and the group re-balances with the new subscription. +// - 848: the server reconciles through heartbeats, so feeding rejoinCh +// would only bounce the session pointlessly - AND that bounce runs the +// session-end revoke concurrently with live heartbeats, the one +// interleaving where a completing heartbeat's nowAssigned store is lost +// to revoke's read-modify-write. Instead force an immediate heartbeat +// (best effort, must not block; see the walkthrough in prerevoke): the +// next request rebuilds the subscription from live state, and if the +// force is missed the heartbeat timer sends within one interval. +func (g *groupConsumer) signalSubscriptionChange(why string) { + if g.is848 { + select { + case g.heartbeatForceCh <- func(error) {}: + default: + } + return + } + g.rejoin(why) +} + // Joins and then syncs, issuing the two slow requests in goroutines to allow // for group cancelation to return early. func (g *groupConsumer) joinAndSync(joinWhy string) error { @@ -1571,6 +1705,13 @@ func (g *groupExternal) eachTopic(fn func(string)) { } func (g *groupExternal) updateLatest(meta map[string]*metadataTopic) { + // These are topics the leader balances but does not itself consume, so + // there is no kept-partition floor like the leader's own topics have + // (metadata.go). We rewrite the cached count and trigger a rejoin on + // ANY change, INCLUDING a one-response stale shrink - that is one churn + // cycle (revoke + re-assign) that self-heals on the next refresh. This + // is the same stale-snapshot exposure Java's leader carries; it is + // intentional, not a bug to silence with a shrink filter. g.cloned(func(tps map[string]int32) { var rejoin bool for t, ps := range tps { @@ -1744,6 +1885,16 @@ func (g *groupConsumer) fetchOffsets(ctx context.Context, added map[string][]int // Groups format, and resp.Topics is a copy that may lose TopicID. var staleRetries int var unknownTopicIDRetries int + var omittedRetries int + // injected tracks partitions whose non-retryable error we have already + // surfaced via a fake fetch. It is declared BEFORE the start label so it + // survives the goto-start retries below. addFakeReadyForDraining is not + // idempotent: a non-conformant broker that omits a requested partition + // (driving the omitted retry) or returns a non-retryable partition error + // ordered ahead of an UNSTABLE_OFFSET_COMMIT partition (driving the + // retryable goto-start) would otherwise re-run the injection arm every + // pass and emit a duplicate fake fetch for the same partition each retry. + var injected mtmps start: member, gen := g.memberGen.load() req := kmsg.NewPtrOffsetFetchRequest() @@ -1757,9 +1908,28 @@ start: groupTopics := g.tps.load() pinV9 := false for topic, partitions := range added { + // Skip partitions we have already surfaced a non-retryable error + // for and dropped (injected, below). On the first pass injected is + // nil and nothing is filtered; on a goto-start retry (driven by an + // UNSTABLE_OFFSET_COMMIT or UNKNOWN_TOPIC_ID partition) this avoids + // re-fetching partitions we will never re-add to the assignment. + if inj := injected[topic]; inj != nil { + kept := partitions[:0:0] // fresh backing array; do not mutate added + for _, p := range partitions { + if _, ok := inj[p]; !ok { + kept = append(kept, p) + } + } + partitions = kept + } + if len(partitions) == 0 { + continue + } reqTopic := kmsg.NewOffsetFetchRequestGroupTopic() reqTopic.Topic = topic - reqTopic.TopicID = groupTopics.loadTopic(topic).id + if td := groupTopics.loadTopic(topic); td != nil { + reqTopic.TopicID = td.id + } if reqTopic.TopicID == ([16]byte{}) { pinV9 = true } @@ -1872,7 +2042,18 @@ start: // Some partition errors are retryable: // // - UnstableOffsetCommit (KIP-447): a pending - // transaction should be committing soon. + // transaction should be committing soon. This + // 1s retry is deliberately UNBOUNDED and has no + // counter - the block is protocol-mandated: + // require_stable hides pending txnal offsets + // until the commit marker lands, so a bound + // would convert a mandated wait into a spurious + // error. The worst legal wait is the blocking + // producer's transaction timeout; an adversarial + // producer extending its txn extends the block + // server-side, exactly as it does for Java. Only + // session teardown (ctx below) interrupts it. + // Do not add a retry cap. // // - UnknownTopicID: the broker has not yet // propagated the topic ID for a newly created @@ -1915,6 +2096,15 @@ start: // Instead we surface the error to the user via a // fake fetch and drop the partition from this // assignment; the rest of the session proceeds. + // + // Surface each partition's error exactly once across + // the goto-start retries: injected persists (declared + // before the start label), so a partition already + // surfaced on an earlier pass is skipped here rather + // than re-injected. + if _, ok := injected[topic][rPartition.Partition]; ok { + continue + } g.cfg.logger.Log(LogLevelError, "fetch offsets failed for partition; injecting error and continuing with remaining partitions", "group", g.cfg.group, "topic", topic, @@ -1922,6 +2112,7 @@ start: "err", err, ) g.c.addFakeReadyForDraining(topic, rPartition.Partition, err, "fetch offsets returned a non-retryable partition error") + injected.add(topic, rPartition.Partition) continue } offset := Offset{ @@ -1931,7 +2122,12 @@ start: if resp.Version >= 5 && kip320 { // KIP-320 offset.epoch = rPartition.LeaderEpoch } - if rPartition.Offset == -1 { + // The coordinator's "no committed offset" sentinel is -1. + // We treat ANY negative offset as no-commit, matching the + // Java client's `offset >= 0` test: a buggy broker's -5 + // must not flow into partition assignment as a literal + // negative offset. + if rPartition.Offset < 0 { offset = g.cfg.startOffset } topicOffsets[rPartition.Partition] = offset @@ -1969,6 +2165,64 @@ start: } } + // The dual of the validation above: every partition we requested must + // be present in the response. The group coordinator answers every + // requested partition (filling -1 for those with no commit), so an + // omission is a buggy/hostile broker - as is a duplicated topic entry, + // which overwrites and discards the earlier entry's partitions in the + // response loop above. A partition silently absent from `offsets` is + // never assigned a cursor, and a successful return here clears + // g.fetching: nothing inside a live session ever re-fetches it, so for + // cooperative and 848 sessions (whose unchanged assignment diffs to an + // empty "added" on the next session) the partition would silently + // never consume. Retry a few times in case the omission is transient, + // then surface a loud error fetch and drop the partition from this + // assignment, exactly like the non-retryable partition error path + // above. + var omitted mtmps + for topic, partitions := range added { + if !groupTopics.hasTopic(topic) { + continue // already warned and skipped above + } + topicOffsets := offsets[topic] + for _, partition := range partitions { + if injected != nil { + if _, ok := injected[topic][partition]; ok { + continue // deliberately dropped, not omitted + } + } + if _, ok := topicOffsets[partition]; !ok { + omitted.add(topic, partition) + } + } + } + if len(omitted) > 0 { + if omittedRetries < 3 { + omittedRetries++ + g.cfg.logger.Log(LogLevelError, "fetch offsets response omitted requested partitions, waiting 1s and retrying", + "group", g.cfg.group, + "omitted", omitted, + "attempt", omittedRetries, + ) + select { + case <-ctx.Done(): + return ctx.Err() + case <-time.After(time.Second): + goto start + } + } + for topic, partitions := range omitted { + for partition := range partitions { + g.cfg.logger.Log(LogLevelError, "fetch offsets response repeatedly omitted a requested partition; injecting error and continuing with remaining partitions", + "group", g.cfg.group, + "topic", topic, + "partition", partition, + ) + g.c.addFakeReadyForDraining(topic, partition, errOffsetFetchOmitted, "fetch offsets response omitted the partition") + } + } + } + if g.cfg.onFetched != nil { g.onFetchedMu.Lock() err = g.cfg.onFetched(ctx, g.cl, resp) @@ -2112,7 +2366,9 @@ func (g *groupConsumer) findNewAssignments() { } if numNewTopics > 0 { - g.rejoin("rejoining because there are more topics to consume, our interests have changed") + // Our subscription grew; reconcile per protocol (848 forces a + // heartbeat, classic rejoins). See signalSubscriptionChange. + g.signalSubscriptionChange("rejoining because there are more topics to consume, our interests have changed") } else if g.leader.Load() { if len(toChange) > 0 { g.rejoin("rejoining because we are the leader and noticed some topics have new partitions") @@ -2165,6 +2421,18 @@ func (g *groupConsumer) updateUncommitted(fetches Fetches) { // We set the head offset if autocommitting is disabled (because we // only use head / committed in that case), or if we are greedily // autocommitting (so that the latest head is available to autocommit). + // + // Under DEFAULT autocommit (neither disabled nor greedy) we set only + // dirty here, and undirtyUncommitted promotes dirty->head at the START + // of the next poll. This one-poll lag is INTENTIONAL and is what makes + // default autocommit at-least-once: defaultRevoke commits head, so a + // revoke between poll N and N+1 commits none of poll N's records and + // the new owner re-reads them. Committing dirty at revoke instead would + // mark records consumed while the app may still be mid-processing them - + // a loss window. Do not "fix" the duplicate re-read by committing dirty + // on revoke; users wanting eager semantics use AutoCommitGreedy or their + // own OnPartitionsRevoked -> CommitUncommittedOffsets (user decision + // 2026-04-24). setHead := g.cfg.autocommitDisable || g.cfg.autocommitGreedy g.mu.Lock() @@ -2476,7 +2744,7 @@ func (g *groupConsumer) loopCommit() { // offsets. g.noCommitDuringJoinAndSync.RLock() g.mu.Lock() - if !g.blockAuto { + if g.blockAuto == 0 { uncommitted := g.getUncommittedLocked(true, false) if len(uncommitted) == 0 { g.cfg.logger.Log(LogLevelDebug, "skipping autocommit due to no offsets to commit", "group", g.cfg.group) @@ -2878,10 +3146,11 @@ func (cl *Client) commitOffsets(ctx context.Context, offsets map[string]map[int3 return rerr } -// CommitOffsetsSync cancels any active CommitOffsets, begins a commit that -// cannot be canceled, and waits for that commit to complete. This function -// will not return until the commit is done and the onDone callback is -// complete. +// CommitOffsetsSync waits for any active CommitOffsets to complete, begins a +// commit that cannot be canceled by other commits or by rebalancing, and +// waits for that commit to complete. The commit is canceled only if the +// passed context is canceled. This function will not return until the commit +// is done and the onDone callback is complete. // // The purpose of this function is for use in OnPartitionsRevoked or committing // before leaving a group, because you do not want to have a commit issued in @@ -2984,12 +3253,12 @@ func (g *groupConsumer) commitOffsetsSync( g.mu.Lock() defer g.mu.Unlock() - g.blockAuto = true + g.blockAuto++ unblockAuto := func(cl *Client, req *kmsg.OffsetCommitRequest, resp *kmsg.OffsetCommitResponse, err error) { unblockCommits(cl, req, resp, err) g.mu.Lock() defer g.mu.Unlock() - g.blockAuto = false + g.blockAuto-- } g.commit(ctx, uncommitted, unblockAuto) @@ -3013,12 +3282,14 @@ func (g *groupConsumer) commitOffsetsSync( // It is invalid to use this function to commit offsets for a transaction. // // Note that this function ensures absolute ordering of commit requests by -// canceling prior requests and ensuring they are done before executing a new -// one. This means, for absolute control, you can use this function to -// periodically commit async and then issue a final sync commit before quitting -// (this is the behavior of autocommiting and using the default revoke). This -// differs from the Java async commit, which does not retry requests to avoid -// trampling on future commits. +// waiting for any prior in-flight commit to complete before issuing a new +// one. Prior commits are never canceled: canceling kills the connection, +// and the broker could then process a replacement commit issued on a new +// connection before the original. This means, for absolute control, you can +// use this function to periodically commit async and then issue a final sync +// commit before quitting (this is the behavior of autocommiting and using +// the default revoke). This differs from the Java async commit, which does +// not retry requests to avoid trampling on future commits. // // It is highly recommended to check the response's partition's error codes if // the response is non-nil. While unlikely, individual partitions can error. @@ -3065,12 +3336,12 @@ func (cl *Client) CommitOffsets( g.mu.Lock() defer g.mu.Unlock() - g.blockAuto = true + g.blockAuto++ unblockAuto := func(cl *Client, req *kmsg.OffsetCommitRequest, resp *kmsg.OffsetCommitResponse, err error) { unblockJoinSync(cl, req, resp, err) g.mu.Lock() defer g.mu.Unlock() - g.blockAuto = false + g.blockAuto-- } g.commit(ctx, uncommitted, unblockAuto) @@ -3159,16 +3430,7 @@ func (g *groupConsumer) commit( req.Generation = generation req.MemberID = memberID req.InstanceID = g.cfg.instanceID - - if ctx.Done() != nil { - go func() { - select { - case <-ctx.Done(): - commitCancel() - case <-commitCtx.Done(): - } - }() - } + is848 := g.is848 // g.mu is held, per the function comment above go func() { defer close(commitDone) // allow future commits to continue when we are done @@ -3377,8 +3639,18 @@ func (g *groupConsumer) commit( for _, d := range dropped { var rt *kmsg.OffsetCommitResponseTopic for i := range resp.Topics { - if resp.Topics[i].Topic == d.name && resp.Topics[i].TopicID == d.id { - rt = &resp.Topics[i] + // v10+ responses carry only the TopicID (Topic is + // v0-v9 on the wire, so kept topics arrive with an + // empty name); v9 and below carry only the name (and + // pinV9 above guarantees an id-less topic never goes + // out v10+, so a zero d.id implies a v9 response). + // Match whichever side the wire carried; requiring + // both duplicated the topic on v10 and the length + // mismatch then made updateCommitted skip the whole + // response. + t := &resp.Topics[i] + if d.id != ([16]byte{}) && t.TopicID == d.id || t.Topic != "" && t.Topic == d.name { + rt = t break } } @@ -3401,11 +3673,67 @@ func (g *groupConsumer) commit( // original request, not the wire-filtered one. req.Topics = origReqTopics + // If the broker no longer recognizes our member (the session + // expired during a network blip, or the group rebalanced + // without us), every commit from here on fails identically + // while we keep consuming as a zombie, until the heartbeat + // loop sees the same error up to a full heartbeat interval + // later. Rejoin immediately instead; the join itself repairs + // the session (joinAndSync clears the member id and retries + // if the broker rejects the join with UnknownMemberID). + // + // Classic protocol only: in 848 mode, a forced "rejoin" does + // not actually rejoin. The heartbeat loop treats the signal + // as RebalanceInProgress and the manage loop restarts the + // session with the same member id and epoch; only a + // heartbeat error resets the member to epoch 0. Worse, with + // autocommit the restart livelocks: ending a session runs + // the default revoke, which sync-commits uncommitted + // offsets; that commit fails with the same fatal error and + // re-queues the rejoin signal; the restarted session then + // consumes the queued signal before its first heartbeat + // timer can fire, and the cycle repeats forever without a + // single heartbeat reaching the broker. Classic does not + // loop because joinAndSync drains rejoinCh before joining + // and the join re-registers us. For 848 we leave fatal + // member errors to the heartbeat loop, matching the Java + // clients: the classic Java consumer rejoins from the commit + // path, while the next-gen one leaves fencing detection to + // the heartbeat. + if !is848 { + if fatalErr := commitHasFatalMemberError(resp); fatalErr != nil { + g.cfg.logger.Log(LogLevelInfo, "offset commit returned a fatal group member error, triggering rejoin", + "group", g.cfg.group, + "err", fatalErr, + ) + g.rejoin(fmt.Sprintf("offset commit error: %s", fatalErr)) + } + } + g.updateCommitted(req, resp) onDone(g.cl, req, resp, nil) }() } +// commitHasFatalMemberError returns the first per-partition error that +// means the broker no longer recognizes this member's session. The +// broker validates membership before any per-partition handling, so +// these errors arrive on every partition or none. FencedInstanceID is +// deliberately not included: it means another instance with our +// instance id has taken over, and rejoining would fight that instance +// for the group slot rather than repair anything. +func commitHasFatalMemberError(resp *kmsg.OffsetCommitResponse) error { + for i := range resp.Topics { + for _, p := range resp.Topics[i].Partitions { + switch p.ErrorCode { + case kerr.UnknownMemberID.Code, kerr.IllegalGeneration.Code: + return kerr.ErrorForCode(p.ErrorCode) + } + } + } + return nil +} + type reNews struct { added map[string][]string skipped []string diff --git a/vendor/github.com/twmb/franz-go/pkg/kgo/consumer_group_848.go b/vendor/github.com/twmb/franz-go/pkg/kgo/consumer_group_848.go index 756d45122e..a6c34f1cc6 100644 --- a/vendor/github.com/twmb/franz-go/pkg/kgo/consumer_group_848.go +++ b/vendor/github.com/twmb/franz-go/pkg/kgo/consumer_group_848.go @@ -38,6 +38,27 @@ func (g *groupConsumer) should848() bool { return true } +// manage848 drives the KIP-848 heartbeat session: it restarts the session on +// transient errors and re-fetches outstanding partitions via g.fetching. +// +// Constraints any change to coordinator/leader-churn recovery (here, in +// heartbeat, and in fetchOffsets) must preserve — established by the +// rebalance-churn audit: +// +// 1. Heartbeat-originated transport and coordinator errors retry in place +// (the stale-connection cycle 90bcc2bb fixed is real). Fetch errors do +// NOT take that arm — they propagate here so the session restarts and +// re-fetches; do not collapse the two error sources back together. +// 2. Session restart is the designed heal for fetch failures: the g.fetching +// carryover exists precisely so a torn-down session re-fetches. Route +// fixes through it rather than inventing a second retry inside fetchOffsets. +// 3. Member-identity resets are the minimum the error implies: a fresh UUID +// for UnknownMemberID, the SAME UUID for epoch problems (FENCED and STALE +// both keep it). Anything stronger strands server-side state for a full +// session timeout. +// 4. Leaves (MemberEpoch -1/-2) are idempotent and stateless — safe to retry +// anywhere. The CGHB no-retry rule applies only to reconciliation-carrying +// heartbeats, never to leaves. func (g *groupConsumer) manage848() { var serverAssignor string switch g.cfg.balancers[0].(type) { @@ -91,19 +112,28 @@ outer: // Even if Kafka replies that the API is available, if we use it // and the broker is not configured to support it, we receive - // UnsupportedVersion. On the first loop + // UnsupportedVersion. Until a join attempt settles the question + // (any other kerr, or success, means the API is really served), + // an UnsupportedVersion error falls back to classic group + // management. if !known848Support { if err != nil { var ke *kerr.Error if errors.As(err, &ke) { if ke.Code == kerr.UnsupportedVersion.Code { // It's okay to update is848 here. This is used while leaving - // and while heartbeating. We have not yet entered heartbeating, - // and if the user is concurrently leaving, the lack of a memberID - // means both 848 and old group mgmt leaves return early. + // and while heartbeating. We have not yet entered heartbeating. g.mu.Lock() g.is848 = false g.mu.Unlock() + // We pre-stored a self-generated member id (v1 semantics) + // that the server never admitted - the join just failed. + // Clear it so a concurrent leave returns early (there is + // no member to remove; both the 848 and classic leave + // paths check for an empty member id) and so the classic + // join below starts with an empty member id rather than + // burning an UNKNOWN_MEMBER_ID round trip on our UUID. + g.memberGen.store("", -1) g.cfg.logger.Log(LogLevelInfo, "falling back to standard consumer group management due to lack of broker support", "group", g.cfg.group) fallbackToClassic = true go g.manage() @@ -231,7 +261,12 @@ outer: sleep := g.cfg.heartbeatInterval if err == nil { err = errCodeMessage(resp.ErrorCode, resp.ErrorMessage) - sleep = time.Duration(resp.HeartbeatIntervalMillis) * time.Millisecond + // A zero or negative server interval (buggy or + // hostile broker) would hot-loop heartbeats at + // round-trip pace; keep the configured cadence. + if hb := time.Duration(resp.HeartbeatIntervalMillis) * time.Millisecond; hb > 0 { + sleep = hb + } } if err != nil { // Reset last-sent state so the next attempt @@ -268,21 +303,27 @@ outer: isAnyDialErr(err), g.cl.maybeDeleteStaleCoordinator(g.cfg.group, coordinatorTypeGroup, err): consecutiveTransientRestarts++ - if int64(consecutiveTransientRestarts) >= g.cfg.retries && int64(consecutiveTransientRestarts)%g.cfg.retries == 0 { + if shouldNotify848Restart(int64(consecutiveTransientRestarts), g.cfg.retries) { g.c.addFakeReadyForDraining("", 0, &ErrGroupSession{ Err: fmt.Errorf("consumer group %s heartbeat has been failing for %d consecutive attempts, still retrying: %w", g.cfg.group, consecutiveTransientRestarts, err), }, "consumer group heartbeat persistently failing") } err = nil + // Continue directly: we nil err only to keep the + // session loop going, not because anything + // succeeded. Falling into the err == nil reset + // below would zero the counter we just + // incremented, capping it at 1 forever and making + // the every-cfg.retries notification above + // unreachable. The reset is for the other arms, + // whose nil means a processed response. + continue - case errors.Is(err, kerr.UnknownMemberID), - errors.Is(err, kerr.StaleMemberEpoch): - // UnknownMemberID: server forgot us. - // StaleMemberEpoch: our epoch drifted (e.g. a - // heartbeat response was lost). Either way, the - // fix is identical: abandon the assignment and - // re-initialJoin with a fresh member id so the - // server hands us back a current epoch. + case errors.Is(err, kerr.UnknownMemberID): + // The server forgot us (session expired during an + // outage, or we were administratively removed). + // Abandon the assignment and re-initialJoin with a + // fresh member id. member, gen := g.memberGen.load() g.cfg.logger.Log(LogLevelInfo, "consumer group heartbeat error, abandoning assignment and rejoining with new member id", "group", g.cfg.group, @@ -294,7 +335,17 @@ outer: g.memberGen.store(newStringUUID(), 0) continue outer + // StaleMemberEpoch means our epoch drifted from the + // server's; it reaches us via OffsetFetch (the heartbeat + // itself fences with FencedMemberEpoch), so the server + // still has this member. We must KEEP our member id: + // rejoining at epoch 0 with the same id is the protocol's + // lost-response recovery - the server re-admits the member + // in place and re-delivers its assignment. Rejoining with + // a fresh id would strand the old member server-side, + // parking its partitions until the session timeout. case errors.Is(err, kerr.FencedMemberEpoch), + errors.Is(err, kerr.StaleMemberEpoch), errors.Is(err, kerr.GroupMaxSizeReached), errors.Is(err, kerr.UnsupportedAssignor): lvl := LogLevelInfo @@ -332,8 +383,9 @@ outer: } // The errors we have to handle are: - // * UnknownMemberID: abandon partitions, rejoin - // * FencedMemberEpoch: abandon partitions, rejoin + // * UnknownMemberID: abandon partitions, rejoin w/ new member id + // * FencedMemberEpoch / StaleMemberEpoch: abandon partitions, + // rejoin with the SAME member id // * UnreleasedInstanceID: fatal error, do not rejoin // * General error: fatal error, do not rejoin // @@ -353,6 +405,25 @@ outer: } } +// shouldNotify848Restart reports whether a transient-restart count warrants +// surfacing the "heartbeat persistently failing" notification - the only +// user-visible signal that an 848 group is unreachable: once we have +// restarted at least `retries` times, on every `retries`-th restart. +// +// retries <= 0 means the user disabled retries (RequestRetries(0)). That also +// disables in-session heartbeat retries - heartbeat() propagates the first +// transient error immediately rather than retrying in place - so every +// transient error is its own restart and each one warrants the notification. +// We must therefore notify on every restart, NOT divide restarts by zero +// (an integer divide-by-zero panic that would crash the manage goroutine on +// the first transient heartbeat error). +func shouldNotify848Restart(restarts, retries int64) bool { + if retries < 1 { + return true + } + return restarts >= retries && restarts%retries == 0 +} + func (g *groupConsumer) leave848(ctx context.Context) { memberID := g.memberGen.memberID() g.cfg.logger.Log(LogLevelInfo, "leaving next-gen group", @@ -375,7 +446,16 @@ func (g *groupConsumer) leave848(ctx context.Context) { g.leaveErr = err return } - g.leaveErr = errCodeMessage(resp.ErrorCode, resp.ErrorMessage) + err = errCodeMessage(resp.ErrorCode, resp.ErrorMessage) + // The leave rides the coordinator retry wrapper: if a prior attempt + // succeeded but its response was lost (the connection died), the + // retry finds the member already gone. Same if the session expired + // before we could leave. Either way the member is out of the group, + // which is the goal state of leaving, not an error. + if errors.Is(err, kerr.UnknownMemberID) { + err = nil + } + g.leaveErr = err } type g848 struct { @@ -402,6 +482,23 @@ type g848 struct { prerevoking atomic.Bool } +// sanitizePartitions returns the broker-provided partitions sorted, with +// duplicates and negatives dropped. A duplicated partition is not just +// redundant: it survives into nowAssigned, makes the assignment compare as +// changed, and diffAssigned then re-"adds" the partition we already own - +// re-fetching its committed offset and rewinding the live cursor into +// duplicate consumption. Negative numbers can only come from a buggy or +// hostile broker and would otherwise flow into the offset-load machinery. +func sanitizePartitions(ps []int32) []int32 { + ps = slices.Clone(ps) + slices.Sort(ps) + ps = slices.Compact(ps) + for len(ps) > 0 && ps[0] < 0 { + ps = ps[1:] + } + return ps +} + // v1+ requires the end user to generate their own MemberID, with the // recommendation being v4 uuid base64 encoded so it can be put in URLs. We // roughly do that (no version nor variant bits). crypto/rand does not fail @@ -424,7 +521,32 @@ func (g *g848) initialJoin() (time.Duration, error) { g.g.memberGen.storeGeneration(0) g.lastSubscribedTopics = nil g.lastTopics = nil + // A (re)join must carry an EMPTY owned-partitions list: the broker + // rejects any epoch-0 heartbeat whose Topics is non-empty (or null) + // with INVALID_REQUEST, "TopicPartitions must be empty when + // (re-)joining." unresolvedAssigned holds the OLD member's + // server-side assignment, and mkreq folds it into Topics - so + // carrying it across a member reset would poison every join, and + // permanently: the only other thing that clears unresolvedAssigned + // is a successful assignment-carrying response, which a rejected + // join never produces. Dropping it loses nothing - the join + // response always re-delivers the member's full assignment. The + // Java client likewise clears its unresolved-IDs cache on every + // transition to joining. + g.unresolvedAssigned = nil g.prerevoking.Store(false) + // Drain any stale rejoin signal, mirroring joinAndSync. Nothing + // else on the 848 path consumes the channel across a member reset: + // if a signal was queued (e.g. metadata found new matching topics) + // and the heartbeat loop then exited on a fatal error without + // reading it, the signal would survive into the session we are + // about to build and immediately bounce it. The join below already + // carries our current subscription (mkreq reads live state), which + // is everything a queued signal could ask for. + select { + case <-g.g.rejoinCh: + default: + } req := g.mkreq() resp, err := req.RequestWith(g.g.ctx, g.g.cl) if err == nil { @@ -442,10 +564,30 @@ func (g *g848) initialJoin() (time.Duration, error) { "now_assigned", nowAssigned, ) - return time.Duration(resp.HeartbeatIntervalMillis) * time.Millisecond, nil + // As in the heartbeat closure: never adopt a zero/negative server + // interval, it would hot-loop the heartbeat timer. + if hb := time.Duration(resp.HeartbeatIntervalMillis) * time.Millisecond; hb > 0 { + return hb, nil + } + return g.g.cfg.heartbeatInterval, nil } func (g *g848) handleResp(req *kmsg.ConsumerGroupHeartbeatRequest, resp *kmsg.ConsumerGroupHeartbeatResponse) map[string][]int32 { + // A success response can only legitimately carry a negative member + // epoch as the echo of a leave (-1, or -2 static), which this loop + // never sends; the broker rejects requests below -2 outright. If a + // buggy or hostile broker hands us a negative epoch here and we + // store it, our next heartbeat would BE a leave: the member silently + // exits the group while fetches continue. Ignore the response + // entirely, like the Java client does. + if resp.MemberEpoch < 0 { + g.g.cfg.logger.Log(LogLevelWarn, "ignoring consumer group heartbeat response with an invalid negative member epoch", + "group", g.g.cfg.group, + "epoch", resp.MemberEpoch, + ) + return nil + } + id2t := g.g.cl.id2tMap() newAssigned := make(map[string][]int32) @@ -469,16 +611,16 @@ func (g *g848) handleResp(req *kmsg.ConsumerGroupHeartbeatRequest, resp *kmsg.Co // Fresh assignment from server - replace unresolved state. g.unresolvedAssigned = nil for _, t := range resp.Assignment.Topics { + ps := sanitizePartitions(t.Partitions) name := id2t[t.TopicID] if name == "" { if g.unresolvedAssigned == nil { g.unresolvedAssigned = make(map[topicID][]int32) } - g.unresolvedAssigned[topicID(t.TopicID)] = slices.Clone(t.Partitions) + g.unresolvedAssigned[topicID(t.TopicID)] = ps continue } - slices.Sort(t.Partitions) - newAssigned[name] = t.Partitions + newAssigned[name] = ps } } @@ -489,7 +631,6 @@ func (g *g848) handleResp(req *kmsg.ConsumerGroupHeartbeatRequest, resp *kmsg.Co // simpler and only costs one heartbeat interval (~5s). for id, ps := range g.unresolvedAssigned { if name := id2t[[16]byte(id)]; name != "" { - slices.Sort(ps) newAssigned[name] = ps delete(g.unresolvedAssigned, id) } @@ -590,8 +731,17 @@ func (g *g848) mkreq() *kmsg.ConsumerGroupHeartbeatRequest { // the already-resolved topic names from g.tps (which // filterMetadataAllTopics populates with excludes applied). // New topics are picked up on the next metadata refresh. + // + // Skip internal topics: classic regex consuming never uses them + // (findNewAssignments skips isInternal), and the broker honors + // explicit name subscriptions to internal topics, so emulating + // the regex with names would otherwise consume e.g. + // __consumer_offsets whenever the regex matches it. subscribedTopics := make([]string, 0, len(tps)) - for t := range tps { + for t, tp := range tps { + if tp.load().isInternal { + continue + } subscribedTopics = append(subscribedTopics, t) } slices.Sort(subscribedTopics) diff --git a/vendor/github.com/twmb/franz-go/pkg/kgo/consumer_share.go b/vendor/github.com/twmb/franz-go/pkg/kgo/consumer_share.go index f97bf9797a..f61bb25d05 100644 --- a/vendor/github.com/twmb/franz-go/pkg/kgo/consumer_share.go +++ b/vendor/github.com/twmb/franz-go/pkg/kgo/consumer_share.go @@ -43,6 +43,15 @@ type ( // names. Owned by the manage goroutine. unresolvedAssigns map[topicID][]int32 + // Whether the last assignPartitions pass skipped an assigned + // partition our metadata does not know yet (the broker assigned + // newly added partitions before our metadata refreshed). We ack + // the member epoch regardless, so the broker never re-sends the + // assignment; while true, handleHeartbeatResp re-returns the + // current assignment so we retry once metadata catches up. Owned + // by the manage goroutine. + pendingAssigns bool + lastSentSubscribedTopics []string lastSentRack bool @@ -80,7 +89,7 @@ type ( mu xsync.Mutex cond *sync.Cond dying bool // single-shot leave guard + incWorker gate - workers int // active goroutines (manage + source loops) + workers int // active goroutines (manage + source loops + in-flight cursor migrations) left chan struct{} leaveErr error @@ -96,6 +105,13 @@ type ( // sources concurrent with user acking. source atomic.Pointer[source] + // unknownIDFails counts consecutive UnknownTopicID fetch + // errors, mirroring cursor.unknownIDFails: the error is + // transient on a just-created topic while brokers sync, so we + // strip it for a few fetches, but persistent means the topic + // was recreated and we surface it forever (stall loudly). + unknownIDFails atomic.Int32 + cursorsIdx int // assigned is true when the cursor's partition is currently @@ -110,6 +126,19 @@ type ( // a similar flow actually makes the code worse. assigned atomic.Bool + // moving is true while a leader-move (applyMoves) is queued and + // in flight for this cursor. createShareReq skips a moving cursor + // (it then falls into the forget set, dropping it from the old + // broker's session like the classic strip), so the old source does + // not keep re-fetching the partition - getting NOT_LEADER and + // re-queuing the move - until the async migration lands. This is + // the share analog of the classic cursor going unusable for the + // duration of a move (source.go use() + strip, never re-enabled + // until the move replaces the cursor). applyMoves sets it before + // spawning the migration; applyMovesBlocking clears it and + // re-signals the cursor's source once the move has run. + moving atomic.Bool + ackMu xsync.Mutex pendingAcks []*shareAckState // user acks (r.Ack, finalizePreviousPoll, batchAckRecords) pendingGaps []shareAckRange // internal acks (gap acks, release-undeliverable) @@ -145,6 +174,7 @@ type ( topicID [16]byte partition int32 leaderID int32 + cursor *shareCursor // cursor to re-enable (clear moving) after the migration runs; see shareCursor.moving } // shareAckRange is a contiguous offset range with a fixed ack @@ -620,7 +650,15 @@ func (sc *shareConsumer) leave(ctx context.Context) { sc.leaveErr = err return } - sc.leaveErr = errCodeMessage(resp.ErrorCode, resp.ErrorMessage) + err = errCodeMessage(resp.ErrorCode, resp.ErrorMessage) + // As with the 848 leave: the leave is retried, so a retry can find + // the member already gone (prior attempt's response lost, or the + // session expired first). The member being out of the group is the + // goal state of leaving, not an error. + if errors.Is(err, kerr.UnknownMemberID) { + err = nil + } + sc.leaveErr = err } // closeShareSession releases any buffered records on this source, @@ -969,9 +1007,12 @@ func (sc *shareConsumer) manage() { consecutiveErrors = 0 continue + // Evict with coordinatorTypeGroup to match how the heartbeat loads it: + // the cache is keyed by {name, type}, so a share-typed evict never + // matches and we would retry the stale coordinator forever (#1330). case isRetryableBrokerErr(err), isAnyDialErr(err), - sc.cl.maybeDeleteStaleCoordinator(sc.cfg.shareGroup, coordinatorTypeShare, err): + sc.cl.maybeDeleteStaleCoordinator(sc.cfg.shareGroup, coordinatorTypeGroup, err): // Retryable -- fall through to shared backoff below. default: @@ -1084,14 +1125,17 @@ func (sc *shareConsumer) handleHeartbeatResp(resp *kmsg.ShareGroupHeartbeatRespo sc.memberGen.storeGeneration(resp.MemberEpoch) if resp.Assignment == nil { - if len(sc.unresolvedAssigns) == 0 { + if len(sc.unresolvedAssigns) == 0 && !sc.pendingAssigns { return nil } - // No assignment: try to resolve prior un-resolvable - // topics. If we can, that updates our current assignment - // and we return the new update. + // No assignment: try to resolve prior un-resolvable topics, + // and if a prior assignPartitions could not activate some + // partitions (pendingAssigns), re-return the current + // assignment so activation is retried: the broker considers + // the assignment delivered (we acked the epoch) and will not + // re-send it on its own. resolved := sc.resolveUnresolvedTopicIDs() - if len(resolved) == 0 { + if len(resolved) == 0 && !sc.pendingAssigns { return nil } current := sc.nowAssigned.read() @@ -1176,7 +1220,7 @@ func (sc *shareConsumer) assignPartitions(assignments map[string][]int32) { if slices.Contains(newPs, p) { // linear, *usually* fast... continue } - if int(p) >= len(td.partitions) { + if p < 0 || int(p) >= len(td.partitions) { continue } cursor := td.partitions[p].shareCursor @@ -1185,26 +1229,52 @@ func (sc *shareConsumer) assignPartitions(assignments map[string][]int32) { } } - // Add what is new. + // Add what is new. We skip based on the cursor's own activation + // state, not on what nowAssigned previously contained: a partition + // can be in nowAssigned but never activated (skipped below because + // our metadata did not know it yet), and it must be re-attempted on + // a later pass. var needsMetaUpdate bool + sc.pendingAssigns = false for t, newPs := range assignments { - oldPs := old[t] tp, ok := tps[t] if !ok { + // Not subscribed (e.g. purged): the broker revokes once + // our next heartbeat updates SubscribedTopicNames. We + // deliberately do not set pendingAssigns: the topic will + // never appear in tps, and the broker is guaranteed to + // send a new assignment in response to the subscription + // change. needsMetaUpdate = true - continue // if we don't know the tps data, we can't assign it; force a meta refresh + continue } td := tp.load() for _, p := range newPs { - if slices.Contains(oldPs, p) { - continue // already was assigned, no-op + if p < 0 { + // A sane broker never assigns a negative partition; + // guard a buggy/hostile one. Unlike the too-large + // case below, a negative index can never become + // valid, so do not set pendingAssigns for it. + sc.cfg.logger.Log(LogLevelWarn, "share assignment contains a negative partition, ignoring it", + "topic", t, + "partition", p, + ) + continue } if int(p) >= len(td.partitions) { + // Assigned a partition our metadata does not know + // yet (partitions were just added and the + // coordinator is ahead of our metadata). We cannot + // activate it now; retry after the metadata + // refresh below. + sc.pendingAssigns = true needsMetaUpdate = true continue } cursor := td.partitions[p].shareCursor - cursor.assigned.Store(true) + if cursor.assigned.Swap(true) { + continue // already active from a prior pass + } sourcesToWake[cursor.source.Load()] = struct{}{} } } @@ -1226,7 +1296,37 @@ func (sc *shareConsumer) applyMoves(moves []shareMove, endpoints []BrokerMetadat if len(moves) == 0 { return } - go sc.cl.blockingMetadataFn(func() { + // Mark each migrating cursor unusable BEFORE spawning, on this (the + // fetch) goroutine, so the very next createShareReq already skips it. + // Setting it inside the spawned goroutine would race the next fetch, + // which could rebuild a request that re-fetches the partition (getting + // NOT_LEADER again) before the goroutine runs. applyMovesBlocking clears + // the flag once the migration has run. See shareCursor.moving. + for i := range moves { + moves[i].cursor.moving.Store(true) + } + go sc.applyMovesBlocking(moves, endpoints) +} + +// applyMovesBlocking performs the cursor migration on the metadata loop. +// It registers as a share-consumer worker (incWorker/decWorker) so leave's +// barrier waits for an in-flight migration before it drains and closes the +// per-source sessions: the move runs via blockingMetadataFn and can relocate +// a cursor (or create a brand-new source) concurrently with leave. Without +// the worker registration, a CurrentLeader-hint move racing LeaveGroup/Close +// can land a cursor on a source whose closeShareSession already drained (or +// on a source created after leave snapshotted the source list), stranding +// that cursor's pending acks: sc.pendingAcks never returns to 0 (FlushAcks +// hangs) and the held records release only via the broker's acquisition-lock +// timeout. If the consumer is already dying, incWorker returns false and the +// move is skipped; the cursor stays on its current source, which the leave's +// closeShareSession drains. +func (sc *shareConsumer) applyMovesBlocking(moves []shareMove, endpoints []BrokerMetadata) { + if !sc.incWorker() { + return + } + defer sc.decWorker() + sc.cl.blockingMetadataFn(func() { // Seed any brokers from the response's NodeEndpoints that we // do not yet know about. Same merge-without-remove invariant // as kip951move.ensureBrokers. @@ -1286,7 +1386,7 @@ func (sc *shareConsumer) applyMoves(moves []shareMove, endpoints []BrokerMetadat continue } td := tp.load() - if int(m.partition) >= len(td.partitions) { + if m.partition < 0 || int(m.partition) >= len(td.partitions) { continue } cursor := td.partitions[m.partition].shareCursor @@ -1306,6 +1406,22 @@ func (sc *shareConsumer) applyMoves(moves []shareMove, endpoints []BrokerMetadat "applied", moved, ) } + + // Re-enable each cursor for fetching now that its move has run, and + // re-signal its (current) source. addShareCursor above woke the new + // source's loop while moving was still set, so that loop may have + // skipped this cursor and exited via maybeFinish before we got here; + // maybeShareConsume re-fetches it. Cursors whose move was skipped + // (topic unresolved, out of range, already on the leader) are + // re-enabled on their current source so they retry. This clear runs + // before decWorker, so it cannot race leave's barrier; the dying + // bail and the cl.ctx escape skip it, but those are shutdown paths + // where closeShareSession drains the cursor regardless of moving. + for i := range moves { + c := moves[i].cursor + c.moving.Store(false) + c.source.Load().maybeShareConsume() + } }) } @@ -1737,6 +1853,7 @@ func (s *source) shareAck(predrained []cursorAckDrain) { topicID: rt.TopicID, partition: rp.Partition, leaderID: rp.CurrentLeader.LeaderID, + cursor: drained.cursor, }) } if isShareAckRetryable(partErr) { @@ -2328,12 +2445,18 @@ func (s *source) shareFetch(doneFetch chan<- bool) (fetched bool) { } var didBackoff bool - backoff := func() { + backoff := func(why any) { // Release fetch slot before sleeping so other sources // can fetch during our backoff. doneFetch <- false alreadySentToDoneFetch = true didBackoff = true + + // Like the classic source backoff: a fetch failure is the + // only signal we get when a broker dies (no response means no + // CurrentLeader hint), so opportunistically refresh metadata + // to migrate our cursors to the new leader. + s.cl.triggerUpdateMetadata(false, fmt.Sprintf("opportunistic load during share source backoff: %v", why)) s.consecutiveFailures++ after := time.NewTimer(sc.cfg.retryBackoff(s.consecutiveFailures)) defer after.Stop() @@ -2350,7 +2473,7 @@ func (s *source) shareFetch(doneFetch chan<- bool) (fetched bool) { if err != nil { s.resetShareSession() - backoff() + backoff(err) sc.enqueueAckErrors(piggybackAcks, err, nAcks) return fetched } @@ -2360,7 +2483,13 @@ func (s *source) shareFetch(doneFetch chan<- bool) (fetched bool) { res := s.handleShareReqResp(req, resp, usable, piggybackAcks, sentPiggyback) if res.discardErr != nil { + // Top-level errors are not necessarily transient (e.g. group + // auth revoked mid-run answers GROUP_AUTHORIZATION_FAILED + // top-level on every fetch); without a backoff this loops at + // round-trip pace. Transport errors and all-errors-stripped + // responses already back off; treat top-level errors the same. sc.enqueueAckErrors(piggybackAcks, res.discardErr, nAcks) + backoff(res.discardErr) return fetched } @@ -2380,7 +2509,7 @@ func (s *source) shareFetch(doneFetch chan<- bool) (fetched bool) { s.hook(&res.fetch, true, false) sc.c.addSourceReadyForDraining(s) } else if res.allErrsStripped { - backoff() + backoff("empty share fetch response due to all partitions having retryable errors") } return fetched } @@ -2410,14 +2539,17 @@ func (s *source) handleShareReqResp(req *kmsg.ShareFetchRequest, resp *kmsg.Shar sessionStale := s.share.sessionEpoch != epoch if !sessionStale { s.share.sessionEpoch++ - // Only add cursors that were in the WANT set (usable) to - // sessionParts. req.Topics may also contain piggyback-only - // partitions for cursors that got revoked after we drained - // their acks: adding those to sessionParts would force us - // to forget them on the next request, generating an extra - // round trip of ForgottenTopicsData. - for _, c := range usable { - s.share.sessionParts[tidp{c.topicID, c.partition}] = struct{}{} + // Mirror the broker's session bookkeeping exactly: the broker + // adds EVERY partition we list in the request topics to its + // share session, whether the partition carries a fetch or only + // piggybacked acks (a cursor that was revoked, paused, or + // migrated after we drained its acks). It removes a partition + // from the session only when we forget it. + for i := range req.Topics { + t := &req.Topics[i] + for j := range t.Partitions { + s.share.sessionParts[tidp{t.TopicID, t.Partitions[j].Partition}] = struct{}{} + } } for _, ft := range req.ForgottenTopicsData { for _, p := range ft.Partitions { @@ -2464,6 +2596,7 @@ func (s *source) handleShareReqResp(req *kmsg.ShareFetchRequest, resp *kmsg.Shar ackRequeued int64 partitionsWithErrs int seen = make(map[tidp]struct{}, len(usable)+len(piggybackAcks)) + updateWhy multiUpdateWhy ) for i := range resp.Topics { @@ -2529,16 +2662,48 @@ func (s *source) handleShareReqResp(req *kmsg.ShareFetchRequest, resp *kmsg.Shar topicID: rt.TopicID, partition: rp.Partition, leaderID: rp.CurrentLeader.LeaderID, + cursor: cursor, }) continue } + // No leader hint: classify like the classic fetch + // path (source.go handleReqResp). Retriable errors + // are stripped and heal via the metadata update + // triggered below (the broker only fills + // CurrentLeader for NotLeader/FencedLeaderEpoch and + // only when it knows the new leader, so hint-less + // errors are common: leaderless windows, storage + // errors, topic ID propagation). Non-retriable + // errors surface: share sessions give the user no + // other signal. partErr := kerr.ErrorForCode(rp.ErrorCode) - partitions = append(partitions, FetchPartition{ - Partition: rp.Partition, - Err: partErr, - }) + updateWhy.add(topicName, rp.Partition, partErr) + keep := true + switch { + case errors.Is(partErr, kerr.UnknownTopicID): + // Transient on just-created topics while + // brokers sync; persistent means recreation. + // Strip a few, then surface forever, exactly + // like the classic cursor's grace counter. + if fails := cursor.unknownIDFails.Add(1); fails > 5 { + cursor.unknownIDFails.Add(-1) + } else if !sc.cfg.keepRetryableFetchErrors { + keep = false + } + default: + if kerr.IsRetriable(partErr) && !sc.cfg.keepRetryableFetchErrors { + keep = false + } + } + if keep { + partitions = append(partitions, FetchPartition{ + Partition: rp.Partition, + Err: partErr, + }) + } continue } + cursor.unknownIDFails.Store(0) if len(rp.Records) == 0 && len(rp.AcquiredRecords) == 0 { continue @@ -2591,6 +2756,22 @@ func (s *source) handleShareReqResp(req *kmsg.ShareFetchRequest, resp *kmsg.Shar } } + // Like the classic fetch path: per-partition errors trigger an + // immediate metadata update so the cursor can migrate (this is the + // only heal when the response carries no CurrentLeader hint), except + // pure unknown-topic reasons, which likely mean the topic does not + // exist yet and reloading is wasteful - those ride the debounced + // trigger. Hinted moves are handled via applyMoves and do not land + // in updateWhy. + if updateWhy != nil { + why := updateWhy.reason(fmt.Sprintf("share fetch had inner topic errors from broker %d", s.nodeID)) + if updateWhy.isOnly(kerr.UnknownTopicOrPartition) || updateWhy.isOnly(kerr.UnknownTopicID) { + s.cl.triggerUpdateMetadata(false, why) + } else { + s.cl.triggerUpdateMetadataNow(why) + } + } + var moveBrokers []BrokerMetadata if len(moves) > 0 && len(resp.NodeEndpoints) > 0 { moveBrokers = make([]BrokerMetadata, 0, len(resp.NodeEndpoints)) @@ -2834,7 +3015,13 @@ func (s *source) createShareReq(skipAckDrain bool) ( for range s.share.cursors { c := s.share.cursors[ci] ci = (ci + 1) % nShareCursors - if !c.assigned.Load() || paused.has(c.topic, c.partition) { + // Skip a cursor whose leader move is in flight (moving): leaving it + // in the want-set would re-fetch the partition on the old leader, + // getting NOT_LEADER and re-queuing the move until the migration + // lands. Skipping drops it into the forget set below, releasing it + // from the old broker's session (the share analog of the classic + // strip). applyMovesBlocking re-enables it once the move has run. + if !c.assigned.Load() || c.moving.Load() || paused.has(c.topic, c.partition) { continue } wantSet[tidp{c.topicID, c.partition}] = struct{}{} diff --git a/vendor/github.com/twmb/franz-go/pkg/kgo/errors.go b/vendor/github.com/twmb/franz-go/pkg/kgo/errors.go index 131c47485b..0a987427b1 100644 --- a/vendor/github.com/twmb/franz-go/pkg/kgo/errors.go +++ b/vendor/github.com/twmb/franz-go/pkg/kgo/errors.go @@ -13,13 +13,13 @@ import ( ) // IsRetryableBrokerErr returns whether the client considers an error from a -// broker retrayble. This returns true specifically if the client thinks it can +// broker retryable. This returns true specifically if the client thinks it can // retry whatever it was just trying to do with a broker. It returns false in // all other cases. // -// This can used external to the library to help filter errors if use kgo -// hooks: errors may be sent to hooks before the client retries whatever it was -// just attempting. +// This can be used external to the library to help filter errors when using +// kgo hooks: errors may be sent to hooks before the client retries whatever it +// was just attempting. func IsRetryableBrokerErr(err error) bool { return isRetryableBrokerErr(err) } @@ -99,11 +99,6 @@ func isRetryableBrokerErr(err error) bool { if errors.Is(err, errChosenBrokerDead) { return true } - // A broker kept giving us short sasl lifetimes, so we killed the - // connection ourselves. We can retry on a new connection. - if errors.Is(err, errSaslReauthLoop) { - return true - } // We really should not get correlation mismatch, but if we do, we can // retry. if errors.Is(err, errCorrelationIDMismatch) { @@ -199,11 +194,6 @@ var ( // while a request was in-flight. errChosenBrokerDead = errors.New("the broker connection has died and the request will be retried on a new connection") - // If a broker repeatedly gives us tiny sasl lifetimes, we fail a - // request after a few tries to forcefully kill the connection and - // restart a new connection ourselves. - errSaslReauthLoop = errors.New("the broker is repeatedly giving us sasl lifetimes that are too short to write a request") - // A temporary error returned when Kafka replies with a different // correlation ID than we were expecting for the request the client // issued. @@ -238,6 +228,18 @@ var ( errNoCommittedOffset = errors.New("partition has no prior committed offset") + // Returned when a ListOffsets success response carries a negative + // offset, which no legitimate listing produces. Non-retryable so the + // broker misbehavior surfaces in polls; the load is still retried. + errNegativeListedOffset = errors.New("broker replied to a ListOffsets request with an invalid negative offset") + + // Injected as a fake errored fetch when an OffsetFetch response + // repeatedly omits a partition we requested; the group coordinator + // answers every requested partition, so an omission is a broker bug + // that would otherwise leave the partition silently unconsumed for + // the rest of the group session. + errOffsetFetchOmitted = errors.New("broker repeatedly omitted a requested partition from an OffsetFetch response") + // Returned by the 848 heartbeat closure when it detects an assignment // change. The heartbeat loop treats this like RebalanceInProgress but // suppresses further heartbeat requests so that a second heartbeat diff --git a/vendor/github.com/twmb/franz-go/pkg/kgo/group_balancer.go b/vendor/github.com/twmb/franz-go/pkg/kgo/group_balancer.go index a754ee6797..1b9d5abba6 100644 --- a/vendor/github.com/twmb/franz-go/pkg/kgo/group_balancer.go +++ b/vendor/github.com/twmb/franz-go/pkg/kgo/group_balancer.go @@ -205,6 +205,28 @@ func ParseConsumerSyncAssignment(assignment []byte) (map[string][]int32, error) // // If any metadata parsing fails, this returns an error. func NewConsumerBalancer(balance ConsumerBalancerBalance, members []kmsg.JoinGroupResponseMember) (*ConsumerBalancer, error) { + // A buggy or hostile broker can list the same member ID twice in one + // JoinGroup response. Balancers key plans by member ID, so a duplicate + // either merges (range, roundrobin) or, worse, overwrites: the sticky + // engine balances the duplicates as two members and then loses one + // side's partitions when keying its returned plan -- partitions + // assigned to nobody. Keep the first occurrence. + seen := make(map[string]struct{}, len(members)) + for _, member := range members { + seen[member.MemberID] = struct{}{} + } + if len(seen) != len(members) { + dedup := make([]kmsg.JoinGroupResponseMember, 0, len(seen)) + clear(seen) + for _, member := range members { + if _, exists := seen[member.MemberID]; !exists { + seen[member.MemberID] = struct{}{} + dedup = append(dedup, member) + } + } + members = dedup + } + b := &ConsumerBalancer{ b: balance, members: members, @@ -225,12 +247,13 @@ func NewConsumerBalancer(balance ConsumerBalancerBalance, members []kmsg.JoinGro // claiming higher and higher version support and not // actually supporting them. Sarama has a similarish // workaround. See #493. - if bytes.HasPrefix(memberMeta, []byte{0, 1}) { - memberMeta[0] = 0 - memberMeta[1] = 0 - if err = meta.ReadFrom(memberMeta); err != nil { - return nil, fmt.Errorf("unable to read member metadata: %v", err) - } + if !bytes.HasPrefix(memberMeta, []byte{0, 1}) { + return nil, fmt.Errorf("unable to read member metadata: %v", err) + } + memberMeta[0] = 0 + memberMeta[1] = 0 + if err = meta.ReadFrom(memberMeta); err != nil { + return nil, fmt.Errorf("unable to read member metadata: %v", err) } } for _, topic := range meta.Topics { @@ -495,6 +518,13 @@ func (g *groupConsumer) balanceGroup(proto string, members []kmsg.JoinGroupRespo into = memberBalancer.Balance(topicPartitionCount) } + // A custom balancer that fails is documented to SetError and return + // nil; if it returns nil without setting an error, fail loudly rather + // than dereferencing the nil interface below. + if into == nil { + return nil, fmt.Errorf("balancer %s returned a nil plan with no error", proto) + } + if p, ok := into.(*BalancePlan); ok { g.cl.cfg.logger.Log(LogLevelInfo, "balanced", "plan", p.String()) } else { @@ -767,13 +797,13 @@ func (*rangeBalancer) Balance(b *ConsumerBalancer, topics map[string]int32) Into // StickyBalancer returns a group balancer that ensures minimal partition // movement on group changes while also ensuring optimal balancing. // -// Suppose there are three members M0, M1, and M2, and two topics t0 and t1 -// each with three partitions p0, p1, and p2. If the initial balance plan looks -// like +// Suppose there are three members M0, M1, and M2, and three topics t0, t1, +// and t2 each with three partitions p0, p1, and p2. If the initial balance +// plan looks like // // M0: [t0p0, t0p1, t0p2] // M1: [t1p0, t1p1, t1p2] -// M2: [t2p0, t2p2, t2p2] +// M2: [t2p0, t2p1, t2p2] // // If M2 disappears, both roundrobin and range would have mostly destructive // reassignments. @@ -972,6 +1002,37 @@ func (p *BalancePlan) AdjustCooperative(b *ConsumerBalancer) { tmap := make(map[string]struct{}) // reusable topic existence map pmap := make(map[int32]struct{}) // reusable partitions existence map + // KIP-792 / KAFKA-12983: an OwnedPartitions claim only proves current + // ownership when no other member claims the same partition at a + // strictly higher generation. A member that missed rebalances can + // rejoin still claiming a partition that has since been assigned to + // (and is actively consumed by) another member. The balance plan may + // deliberately move the partition back to that stale claimant (sticky + // re-sticking); if the claimant's own stale claim then masks the move + // as "already owned", we skip the revoke round and two members consume + // the partition until the current owner's next sync. We track the + // highest claimed generation per partition and ignore strictly lower + // claims when computing what was added. Same-generation claims all + // count: each claimant keeps what the plan gave it and revokes the + // rest at its own sync, which creates no new overlap. The revoked side + // below stays unfiltered on purpose -- any claimant might still be + // consuming, and revoking more is always safe. + maxClaim := make(map[string]map[int32]int32, 8) + b.EachMember(func(_ *kmsg.JoinGroupResponseMember, meta *kmsg.ConsumerMemberMetadata) { + for _, otopic := range meta.OwnedPartitions { + claimT := maxClaim[otopic.Topic] + if claimT == nil { + claimT = make(map[int32]int32, 20) + maxClaim[otopic.Topic] = claimT + } + for _, opartition := range otopic.Partitions { + if gen, ok := claimT[opartition]; !ok || meta.Generation > gen { + claimT[opartition] = meta.Generation + } + } + } + }) + plan := p.plan // First, on all members, we find what was added and what was removed @@ -997,12 +1058,17 @@ func (p *BalancePlan) AdjustCooperative(b *ConsumerBalancer) { continue } // calculate what was added by creating a planned existence map, - // then removing what was owned, and anything that remains is new, + // then removing what was owned, and anything that remains is new. + // A claim beaten by a strictly higher-generation claim elsewhere + // is stale and does not count as owned: the planned partition is + // then a real transfer that must wait for the owner's revoke. for _, ppartition := range ppartitions { pmap[ppartition] = struct{}{} } for _, opartition := range otopic.Partitions { - delete(pmap, opartition) + if meta.Generation >= maxClaim[topic][opartition] { + delete(pmap, opartition) + } } if len(pmap) > 0 { allAddedT := addT(topic) diff --git a/vendor/github.com/twmb/franz-go/pkg/kgo/hooks.go b/vendor/github.com/twmb/franz-go/pkg/kgo/hooks.go index 3ba13db0ea..0442b544b4 100644 --- a/vendor/github.com/twmb/franz-go/pkg/kgo/hooks.go +++ b/vendor/github.com/twmb/franz-go/pkg/kgo/hooks.go @@ -42,7 +42,7 @@ type HookNewClient interface { OnNewClient(*Client) } -// HookClientClosed is called in Close or CloseAfterRebalance after a client +// HookClientClosed is called in Close or CloseAllowingRebalance after a client // has been closed. This hook can be used to perform final cleanup work. type HookClientClosed interface { // OnClientClosed is passed the client that has been closed, after @@ -176,7 +176,7 @@ type HookBrokerE2E interface { type HookBrokerThrottle interface { // OnBrokerThrottle is passed the broker metadata, the imposed // throttling interval, and whether the throttle was applied before - // Kafka responded to them request or after. + // Kafka responded to the request or after. // // For Kafka < 2.0, the throttle is applied before issuing a response. // For Kafka >= 2.0, the throttle is applied after issuing a response. @@ -380,8 +380,8 @@ type HookFetchRecordBuffered interface { // HookFetchRecordUnbuffered is called when a fetched record is unbuffered. // -// A record can be internally discarded after being in some scenarios without -// being polled, such as when the internal assignment changes. +// A record can be internally discarded in some scenarios without being +// polled, such as when the internal assignment changes. // // As an example, if using HookFetchRecordBuffered for a gauge of how many // record bytes are buffered ready to be polled, this hook can be used to diff --git a/vendor/github.com/twmb/franz-go/pkg/kgo/internal/sticky/graph.go b/vendor/github.com/twmb/franz-go/pkg/kgo/internal/sticky/graph.go index d6bbb587ed..947b55d57d 100644 --- a/vendor/github.com/twmb/franz-go/pkg/kgo/internal/sticky/graph.go +++ b/vendor/github.com/twmb/franz-go/pkg/kgo/internal/sticky/graph.go @@ -42,9 +42,10 @@ func (b *balancer) newGraph( for memberNum := range b.plan { out := outBufs[:0:len(topicPotentials)] outBufs = outBufs[len(topicPotentials):] - // In the worst case, if every node is linked to each other, - // each node will have nparts edges. We preallocate the worst - // case. It is common for the graph to be highly connected. + // Out edges are per topic, not per partition: in the worst + // case a member subscribes to every topic, so we preallocate + // one topic slot per member. The partition edges themselves + // are enumerated from topicInfos during findSteal. g.out[memberNum] = out } for topicNum, potentials := range topicPotentials { diff --git a/vendor/github.com/twmb/franz-go/pkg/kgo/internal/sticky/sticky.go b/vendor/github.com/twmb/franz-go/pkg/kgo/internal/sticky/sticky.go index 06ac816937..351636005f 100644 --- a/vendor/github.com/twmb/franz-go/pkg/kgo/internal/sticky/sticky.go +++ b/vendor/github.com/twmb/franz-go/pkg/kgo/internal/sticky/sticky.go @@ -17,7 +17,8 @@ import ( // bug. The second version introduced generations with the default generation // from the first generation's consumers defaulting to -1. -// We can support up to 65533 members; two slots are reserved. +// We can support up to 65533 members; the unassignedPart sentinel and one +// spare slot are reserved. // We can support up to 2,147,483,647 partitions. // I expect a server to fall over before reaching either of these numbers. @@ -246,7 +247,10 @@ func (b *balancer) partNumByTopic(topic string, partition int32) (int32, bool) { return 0, false } topicInfo := b.topicInfos[topicNum] - if partition >= topicInfo.partitions { + // Claimed partitions are arbitrary input from other group members; a + // negative partition would index our flat partition state at a + // negative offset (or alias into the preceding topic's range). + if partition < 0 || partition >= topicInfo.partitions { return 0, false } return topicInfo.partNum + partition, true @@ -671,22 +675,36 @@ func (b *balancer) tryRestickyStales( } } if !canTake { - return + continue } - // The part cannot be unassigned here; a stale member - // would just have it. The part also cannot be deleted; - // if it is, there are no potential consumers and the - // logic above continues before getting here. The part - // must be on a different owner (cannot be lastOwner), - // otherwise it would not be a lastOwner in the stales - // map; it would just be the current owner. + // The part cannot be deleted; if it is, there are no + // potential consumers and canTake is false above. The part + // CAN be unassigned: the member that won the claim (the + // higher generation) may have dropped its subscription to + // the topic, in which case our caller un-mapped the + // partition from the winner's plan and left it unassigned. + // We give the partition straight back to the stale member. currentOwner := partitionConsumers[staleNum].memberNum + if currentOwner == unassignedPart { + b.plan[lastOwnerNum].add(staleNum) + partitionConsumers[staleNum] = partitionConsumer{lastOwnerNum, lastOwnerNum} + continue + } lastOwnerPartitions := &b.plan[lastOwnerNum] currentOwnerPartitions := &b.plan[currentOwner] if len(*lastOwnerPartitions)+1 < len(*currentOwnerPartitions) { currentOwnerPartitions.remove(staleNum) lastOwnerPartitions.add(staleNum) + // partitionConsumers seeds the steal graph's edge + // ownership (cxns) on the complex path. If we move the + // partition in the plan but not here, a later steal of + // this partition resolves to the old owner and remove() + // runs against a plan that does not contain the + // partition -- which swap-removes an unrelated + // partition: the final plan then has this partition on + // two members and the wrongly removed one on none. + partitionConsumers[staleNum] = partitionConsumer{lastOwnerNum, lastOwnerNum} } } } diff --git a/vendor/github.com/twmb/franz-go/pkg/kgo/logger.go b/vendor/github.com/twmb/franz-go/pkg/kgo/logger.go index b9f1cd2465..8d8fa61c8c 100644 --- a/vendor/github.com/twmb/franz-go/pkg/kgo/logger.go +++ b/vendor/github.com/twmb/franz-go/pkg/kgo/logger.go @@ -125,16 +125,14 @@ func (w *wrappedLogger) Log(level LogLevel, msg string, keyvals ...any) { w.inner.Log(level, msg, keyvals...) } -// LoggerFn returns an anonymous function that can be used in other packages -// that support their own anonymous logger functions. +// The following is a small helper you can copy into your own code to bridge a +// kgo.Logger to packages that accept an anonymous logger function of the form +// func(int8, string, ...any) - notably the sister 'sr' and 'kfake' packages, +// which are initialized with a 'LogFn' option. It is intentionally not +// exported (it would force a dependency edge); copy it where you need it: // -// Notably, this was added so that you can easily use a kgo.Logger in the -// sister 'sr' and 'kfake' packages. Both clients can be initialized with a -// 'LogFn' option. This function makes it easy to use the same kgo.Logger -// across the other packages. -// -// func LoggerFn(l Logger) func(int8, string, ...any) { +// func loggerFn(l kgo.Logger) func(int8, string, ...any) { // return func(lvl int8, msg string, keyvals ...any) { -// l.Log(LogLevel(lvl), msg, keyvals...) +// l.Log(kgo.LogLevel(lvl), msg, keyvals...) // } // } diff --git a/vendor/github.com/twmb/franz-go/pkg/kgo/metadata.go b/vendor/github.com/twmb/franz-go/pkg/kgo/metadata.go index c5615c1ff7..4c2542b618 100644 --- a/vendor/github.com/twmb/franz-go/pkg/kgo/metadata.go +++ b/vendor/github.com/twmb/franz-go/pkg/kgo/metadata.go @@ -835,8 +835,11 @@ func (cl *Client) mergeTopicPartitions( // // 2) a topic was deleted and recreated with fewer partitions // - // Both of these scenarios should be rare to non-existent, and we do - // nothing if we encounter them. + // Case 1 is temporary and heals on a later refresh; case 2 is + // permanent. Below, we keep the missing partition around either way. + // For producers we bump its load error, which fails buffered records + // only once the unknown fail limit trips (so case 1 does not fail + // records); consumers keep consuming through the existing cursor. // Migrating topicPartitions is a little tricky because we have to // worry about underlying pointers that may currently be loaded. @@ -851,9 +854,11 @@ func (cl *Client) mergeTopicPartitions( // consuming, the partition is part of a group or part // of what was loaded for direct consuming. // - // We only clear a partition if it is purged from the - // client (which can happen automatically for consumers - // if the user opted into ConsumeRecreatedTopics). + // We only clear a partition if the topic is purged from + // the client, either manually via PurgeTopicsFromClient + // or automatically for regex consumers when the topic + // has been missing from metadata for longer than + // ConsiderMissingTopicDeletedAfter. dup := *oldTP newTP := &dup newTP.loadErr = errMissingMetadataPartition @@ -895,11 +900,47 @@ func (cl *Client) mergeTopicPartitions( // fetched from an out of date broker. We just keep the old // information. if newTP.leaderEpoch < oldTP.leaderEpoch { - // If we repeatedly rewind, then perhaps the cluster - // entered some bad state and lost forward progress. - // We will log & allow the rewind to allow the client - // to continue; other requests may encounter fenced - // epoch errors (and respectively recover). + // A negative leader epoch is the "no leader" sentinel + // (Kafka uses -1): the partition is momentarily + // leaderless, e.g. mid-election after every replica + // restarted in a full cluster bounce. This is not a + // genuinely older epoch, so we must not treat it as a + // rewind. If we counted it toward maxEpochRewinds, then + // after enough leaderless refreshes we would fall through + // below and accept -1 as the partition's leader epoch -- + // which is unsafe. A cursor at leader epoch -1 opts out of + // KIP-320 fencing: migrateCursorTo skips + // OffsetForLeaderEpoch validation for a negative new epoch, + // so a genuine log truncation during the leaderless window + // would go undetected (no ErrDataLoss), and the consumer + // would then fetch at a stale offset with currentLeaderEpoch + // -1 (which brokers never fence) and stall at the high + // watermark. Instead we keep our last known real leader and + // epoch and signal a retry; once a real epoch (>= old) + // reappears, normal validation runs and detects any + // truncation. + if newTP.leaderEpoch < 0 { + cl.cfg.logger.Log(LogLevelDebug, "metadata has a leader epoch of -1 (no leader); keeping our last known leader and epoch until a leader is elected", + "topic", topic, + "partition", part, + "old_leader_epoch", oldTP.leaderEpoch, + ) + *newTP = *oldTP + retryWhy.add(topic, int32(part), errNoLeaderEpoch) + continue + } + + // Otherwise newTP.leaderEpoch is a real (>= 0) epoch that + // is merely lower than ours. That can be the current + // reality: issue #119 saw an unclean leader election + // briefly surface a higher epoch from a broker that then + // died, leaving the surviving cluster on a real, lower + // epoch. Permanently refusing it stranded the client in an + // unrecoverable metadata loop, so if we repeatedly rewind we + // accept it to allow the client to continue. Unlike the -1 + // sentinel handled above, a real lower epoch self-corrects + // downstream: consume sees FENCED_LEADER_EPOCH (and reports + // data loss) and produce sees NOT_LEADER_FOR_PARTITION. // // Five is a pretty low amount of retries, but since // we iterate through known brokers, this basically @@ -1048,6 +1089,7 @@ func (cl *Client) mergeTopicPartitions( var ( errEpochRewind = errors.New("epoch rewind") errMissingTopicID = errors.New("missing topic ID") + errNoLeaderEpoch = errors.New("no leader epoch") ) type multiUpdateWhy map[kerrOrString]map[string]map[int32]struct{} diff --git a/vendor/github.com/twmb/franz-go/pkg/kgo/metrics_714.go b/vendor/github.com/twmb/franz-go/pkg/kgo/metrics_714.go index 927f0f9fdb..7d7220b977 100644 --- a/vendor/github.com/twmb/franz-go/pkg/kgo/metrics_714.go +++ b/vendor/github.com/twmb/franz-go/pkg/kgo/metrics_714.go @@ -29,6 +29,8 @@ func (cl *Client) pushMetrics() { select { case <-cl.ctx.Done(): return + case <-m.quitting: + return case <-m.firstObserve: } @@ -58,6 +60,9 @@ func (cl *Client) pushMetrics() { case <-cl.ctx.Done(): after.Stop() return + case <-m.quitting: + after.Stop() + return case <-after.C: } continue @@ -65,6 +70,21 @@ func (cl *Client) pushMetrics() { clientInstanceID = gresp.ClientInstanceID + // A broker must advertise a positive push interval; a <=0 value is + // invalid. We substitute Kafka's documented default rather than honor + // it, matching the Java client's validateIntervalMs. Without this, the + // no-requested-metrics arm below times on the raw interval (no floor, + // unlike the push loop's max(..., time.Second)), so a broker returning + // an empty RequestedMetrics list with a non-positive interval would + // re-issue GetTelemetrySubscriptions at round-trip pace forever. + if v := validatePushIntervalMillis(gresp.PushIntervalMillis); v != gresp.PushIntervalMillis { + cl.cfg.logger.Log(LogLevelWarn, "broker advertised a non-positive telemetry push interval, substituting the default", + "advertised", gresp.PushIntervalMillis, + "substituted", v, + ) + gresp.PushIntervalMillis = v + } + // If there are no requested metrics, we wait the push interval // and re-get. if len(gresp.RequestedMetrics) == 0 { @@ -74,6 +94,10 @@ func (cl *Client) pushMetrics() { select { case <-cl.ctx.Done(): terminating = true + after.Stop() + case <-m.quitting: + terminating = true + after.Stop() case <-after.C: } continue @@ -120,12 +144,24 @@ func (cl *Client) pushMetrics() { } // Wait until our push interval; if the client is quitting, - // we immediately send a push with Terminating=true. + // we immediately send a push with Terminating=true. The + // quitting signal fires during Close BEFORE the client + // context is canceled - once cl.ctx is dead, the request + // path aborts everything and the push could not be + // delivered. This assigns the outer terminating so that + // after the terminating push is handled, both loops exit + // and our deferred ctxCancel releases Close's bounded + // wait; a shadowing variable here would re-enter the loop + // and send a second terminating push (which brokers + // reject). after := time.NewTimer(wait) - var terminating bool select { case <-cl.ctx.Done(): terminating = true + after.Stop() + case <-m.quitting: + terminating = true + after.Stop() case <-after.C: } @@ -199,6 +235,22 @@ func (cl *Client) pushMetrics() { } } +// defaultPushIntervalMillis is Kafka's documented telemetry push interval +// default (5m), substituted when a broker advertises a non-positive interval +// (Java's ClientTelemetryReporter.DEFAULT_PUSH_INTERVAL_MS). +const defaultPushIntervalMillis = 5 * 60 * 1000 + +// validatePushIntervalMillis returns the broker-advertised telemetry push +// interval, substituting the default for a non-positive (invalid) value so the +// re-get / push loops always pace on a sane interval. Mirrors the Java client's +// ClientTelemetryUtils.validateIntervalMs. +func validatePushIntervalMillis(advertised int32) int32 { + if advertised <= 0 { + return defaultPushIntervalMillis + } + return advertised +} + func buildNameFilter(requested []string) func(string) bool { if len(requested) == 0 { return func(string) bool { return false } @@ -289,16 +341,17 @@ type ( // metricRate is a count per second and a total. metricRate struct { - count atomic.Int64 // Sum of events this period; rate == float64(count/time) at rollup + count atomic.Int64 // Events this period; rate == count/elapsed-seconds at rollup. tot atomic.Int64 // Total events over all time. lastTot int64 // Updated when encoding; the last value for tot in case broker requests DELTA. } - // metricTime reports average latency, max latency, and total latency. - // The unit is in milliseconds. + // metricTime reports average latency and max latency. The unit is in + // milliseconds. metricTime struct { - sum atomic.Int64 // With separate aggDur field, avg = sum/aggDur at rollup. - max atomic.Int64 // Max latency seen during this window. + sum atomic.Int64 // Sum of all observations this period; avg = sum/count at rollup. + count atomic.Int64 // Number of observations this period; the avg denominator. + max atomic.Int64 // Max latency seen during this window. } // We skip: @@ -331,6 +384,12 @@ type ( firstObserve chan struct{} + // quitting is closed by Close before the client context is + // canceled, asking pushMetrics to send its final terminating + // push while requests can still complete. + quitting chan struct{} + closedQuitting atomic.Bool + ctx context.Context ctxCancel func() } @@ -340,9 +399,18 @@ func (m *metrics) init(cl *Client) { m.cl = cl m.initNano = time.Now().UnixNano() m.firstObserve = make(chan struct{}) + m.quitting = make(chan struct{}) m.ctx, m.ctxCancel = context.WithCancel(context.Background()) // for graceful shutdown } +// quit asks pushMetrics to send its final terminating push and exit; it is +// safe to call multiple times (Close can run more than once). +func (m *metrics) quit() { + if !m.closedQuitting.Swap(true) { + close(m.quitting) + } +} + func safeDiv[T ~int64 | ~float64](num, denom T) T { if denom == 0 { return 0 @@ -357,13 +425,17 @@ func (t *metricRate) observe() { t.tot.Add(1) } -func (t *metricRate) rollNums() (rate float64, tot, lastTot int64) { +func (t *metricRate) rollNums(aggDur time.Duration) (rate float64, tot, lastTot int64) { count := t.count.Swap(0) lastTot = t.lastTot tot = t.tot.Load() t.lastTot = tot - rate = safeDiv(float64(count), float64(tot-lastTot)) + // A rate is events-per-second over the aggregation window, matching + // Kafka's Rate stat (value / elapsed-time). The previous divisor was + // tot-lastTot, which equals count by construction (observe increments + // both count and tot), so the rate was a constant ~1.0. + rate = safeDiv(float64(count), aggDur.Seconds()) return rate, tot, lastTot } @@ -371,6 +443,7 @@ func (t *metricRate) rollNums() (rate float64, tot, lastTot int64) { // triggerFirstObserve is always called. func (t *metricTime) observe(millis int64) { t.sum.Add(millis) + t.count.Add(1) for { max := t.max.Load() if millis < max { @@ -382,10 +455,14 @@ func (t *metricTime) observe(millis int64) { } } -func (t *metricTime) rollNums(aggDur time.Duration) (avg float64, max int64) { +func (t *metricTime) rollNums() (avg float64, max int64) { sum := t.sum.Swap(0) + count := t.count.Swap(0) max = t.max.Swap(0) - avg = safeDiv(float64(sum), float64(aggDur.Milliseconds())) + // An avg is the mean observation, matching Kafka's Avg stat + // (total / count). The previous divisor was the aggregation window + // duration in millis, which is not the number of observations. + avg = safeDiv(float64(sum), float64(count)) return avg, max } @@ -512,6 +589,7 @@ func (m *metrics) appendTo(b []byte, useDeltaSums bool, maxBytes int32, allowedN timeNano: nowNano, }, aggregationTemporality: otelTempDelta, + isMonotonic: true, }, }) } else { @@ -525,6 +603,7 @@ func (m *metrics) appendTo(b []byte, useDeltaSums bool, maxBytes int32, allowedN timeNano: nowNano, }, aggregationTemporality: otelTempCumulative, + isMonotonic: true, }, }) } @@ -548,18 +627,18 @@ func (m *metrics) appendTo(b []byte, useDeltaSums bool, maxBytes int32, allowedN } { switch t := s.v.(type) { case *metricRate: - rate, tot, lastTot := t.rollNums() + rate, tot, lastTot := t.rollNums(aggDur) appendGauge(s.name+".rate", 0, rate, nil) appendSum(s.name+".total", tot, lastTot, nil) case *metricTime: - avg, max := t.rollNums(aggDur) + avg, max := t.rollNums() appendGauge(s.name+".avg", 0, avg, nil) appendGauge(s.name+".max", max, 0, nil) case *map[int32]*metricTime: for broker, m := range *t { - avg, max := m.rollNums(aggDur) + avg, max := m.rollNums() attrs := map[string]any{"node_id": broker} appendGauge(s.name+".avg", 0, avg, attrs) appendGauge(s.name+".max", max, 0, attrs) @@ -897,18 +976,13 @@ func (d *otelNumDataPoint) appendTo(b []byte) []byte { } func appendOtelAttributesTo(b []byte, fieldNumber int, attrs map[string]any) []byte { -outer: for key, value := range attrs { - b = appendProtoTag(b, fieldNumber, protoTypeLength) - - kvBytes := []byte{} - // Field 1: key (string) - kvBytes = appendProtoTag(kvBytes, 1, protoTypeLength) - kvBytes = appendProtoString(kvBytes, key) - - // Field 2: value (AnyValue) - kvBytes = appendProtoTag(kvBytes, 2, protoTypeLength) - + // We must determine that the value is serializable BEFORE we + // write anything to b: an unsupported type continues the loop, + // and if we had already appended the field tag we would leave a + // dangling tag with no length+payload, corrupting the whole + // protobuf (the documented Attrs contract is to silently skip + // unsupported types). var anyValueBytes []byte switch t := value.(type) { case *string, string: @@ -973,9 +1047,18 @@ outer: anyValueBytes = binary.AppendUvarint(anyValueBytes, uint64(len(t))) anyValueBytes = append(anyValueBytes, t...) default: - continue outer + continue } + b = appendProtoTag(b, fieldNumber, protoTypeLength) + + kvBytes := []byte{} + // Field 1: key (string) + kvBytes = appendProtoTag(kvBytes, 1, protoTypeLength) + kvBytes = appendProtoString(kvBytes, key) + + // Field 2: value (AnyValue) + kvBytes = appendProtoTag(kvBytes, 2, protoTypeLength) kvBytes = binary.AppendUvarint(kvBytes, uint64(len(anyValueBytes))) kvBytes = append(kvBytes, anyValueBytes...) diff --git a/vendor/github.com/twmb/franz-go/pkg/kgo/partitioner.go b/vendor/github.com/twmb/franz-go/pkg/kgo/partitioner.go index 46e7d11d12..da463b378e 100644 --- a/vendor/github.com/twmb/franz-go/pkg/kgo/partitioner.go +++ b/vendor/github.com/twmb/franz-go/pkg/kgo/partitioner.go @@ -15,8 +15,8 @@ import ( // from producing through partitioning, so you can set fields in the record // struct before producing to aid in partitioning with a custom partitioner. type Partitioner interface { - // forTopic returns a partitioner for an individual topic. It is - // guaranteed that only one record will use the an individual topic's + // ForTopic returns a partitioner for an individual topic. It is + // guaranteed that only one record will use an individual topic's // topicPartitioner at a time, meaning partitioning within a topic does // not require locks. ForTopic(string) TopicPartitioner @@ -229,7 +229,7 @@ func (p *leastBackupTopicPartitioner) PartitionByBackup(_ *Record, n int, backup leastBackup = backup p.onPart = pick npicked = 1 - } else { + } else if backup == leastBackup { npicked++ // reservoir sampling with k = 1 if p.rng.Intn(npicked) == 0 { p.onPart = pick @@ -245,11 +245,11 @@ func (p *leastBackupTopicPartitioner) PartitionByBackup(_ *Record, n int, backup /////////////////// // UniformBytesPartitioner is a redux of the StickyPartitioner, proposed in -// KIP-794 and release with the Java client in Kafka 3.3. This partitioner +// KIP-794 and released with the Java client in Kafka 3.3. This partitioner // returns the same partition until 'bytes' is hit. At that point, a // re-partitioning happens. If adaptive is false, this chooses a new random -// partition, otherwise this chooses a broker based on the inverse of the -// backlog currently buffered for that broker. If keys is true, this uses +// partition, otherwise this chooses a partition based on the inverse of the +// backlog currently buffered for that partition. If keys is true, this uses // standard hashing based on record key for records with non-nil keys. hasher // is optional; if nil, the default hasher murmur2 (Kafka's default). // @@ -355,8 +355,8 @@ func (p *uniformBytesTopicPartitioner) PartitionByBackup(r *Record, n int, backu } else { p.calc = p.calc[:0] - // For adaptive, the logic is that we pick by broker according - // to the inverse of the queue size. Presumably this means + // For adaptive, the logic is that we pick a partition according + // to the inverse of its queue size. Presumably this means // bytes, but we use records for simplicity. // // We calculate 1/recs for all brokers and choose the first one @@ -382,14 +382,28 @@ func (p *uniformBytesTopicPartitioner) PartitionByBackup(r *Record, n int, backu } r := p.rng.Float64() pick := r * t + // The loop below selects nothing in two cases: floating rounding + // can leave pick just above 0 after subtracting every weight (the + // guarded case the original code handled), and we may have entered + // this re-pick with a stale p.onPart that is not the -1 byte-reset + // sentinel but rather a previously-pinned index that is now >= n + // because the writable partition count shrank under us (e.g. a + // leader election dropped partitions from writablePartitions). In + // both cases p.onPart is unchanged by the loop, so we must fall + // back to a valid index rather than return the stale value, which + // the caller would reject as an out-of-range partitioning choice + // (failing the record). The non-adaptive branch above re-picks + // unconditionally via Intn(n) and so has never had this hole. + picked := false for _, c := range p.calc { pick -= c.f if pick <= 0 { p.onPart = c.n + picked = true break } } - if p.onPart == -1 { + if !picked { p.onPart = p.calc[len(p.calc)-1].n } } @@ -475,7 +489,7 @@ type PartitionerHasher func([]byte, int) int // KafkaHasher returns a PartitionerHasher using hashFn that mirrors how Kafka // partitions after hashing data. In Kafka, after hashing into a uint32, the // hash is converted to an int32 and the high bit is stripped. Kafka by default -// uses murmur2 hashing, and the StickyKeyPartiitoner uses this by default. +// uses murmur2 hashing, and the StickyKeyPartitioner uses this by default. // Using this KafkaHasher function is only necessary if you want to change the // underlying hashing algorithm. func KafkaHasher(hashFn func([]byte) uint32) PartitionerHasher { @@ -497,8 +511,25 @@ func KafkaHasher(hashFn func([]byte) uint32) PartitionerHasher { // In particular, using this function with a crc32.ChecksumIEEE hasher makes // this partitioner match librdkafka's consistent partitioner, or the // zendesk/ruby-kafka partitioner. +// +// Note that the arithmetic depends on Go's int width: on 32-bit platforms, +// hashes with the high bit set choose a different partition than on 64-bit +// platforms (only the 64-bit behavior matches librdkafka). This is +// deliberately left alone: the function exists to preserve a historical +// placement, and changing either platform's placement would remap keys for +// deployments relying on it. func SaramaHasher(hashFn func([]byte) uint32) PartitionerHasher { return func(key []byte, n int) int { + // The int conversion makes this arithmetic int-width + // dependent: on 64-bit platforms int(uint32) is always + // non-negative (the negation below is dead and the result + // matches librdkafka's unsigned modulo), while on 32-bit + // platforms a hash with the high bit set wraps negative and + // is negated, choosing a different partition. This stays + // as-is deliberately: this function's entire purpose is + // preserving a historical placement (see the doc above), and + // existing deployments on either width depend on the + // placement they have been writing with. p := int(hashFn(key)) % n if p < 0 { p = -p diff --git a/vendor/github.com/twmb/franz-go/pkg/kgo/pools.go b/vendor/github.com/twmb/franz-go/pkg/kgo/pools.go index f4449c3db4..63b258f7e6 100644 --- a/vendor/github.com/twmb/franz-go/pkg/kgo/pools.go +++ b/vendor/github.com/twmb/franz-go/pkg/kgo/pools.go @@ -11,7 +11,7 @@ import ( //////////////////////////////////////////////////////////////// // NOTE: // -// NOTE: Make sure new hooks are checked in implementsAnyPool // +// NOTE: Make sure new pools are checked in implementsAnyPool // // NOTE: // //////////////////////////////////////////////////////////////// @@ -42,7 +42,9 @@ func (ps pools) each(fn func(Pool) bool) { type PoolDecompressBytes interface { // GetDecompressBytes returns a slice to decompress into. This // interface is given the compressed data and the codec that will be - // used for decompressing. + // used for decompressing. Only the returned slice's capacity is used; + // data is written starting at index 0 regardless of the slice's + // length. // // For many decompression algorithms, it is not possible to accurately // know the size that data will be once decompressed. You can guess a @@ -143,6 +145,13 @@ func (r *Record) Recycle() { return } + ps.release() +} + +// release zeroes and puts the pooled slices back. Called by a batch's last +// Recycle, or directly by the fetch processor when a batch kept no records +// (so no Recycle will ever run). +func (ps *recordPools) release() { // Reset the length of slices to max to ensure we zero things and so // that users can avoid this resetting. ps.decompressBytes = ps.decompressBytes[:cap(ps.decompressBytes)] @@ -192,6 +201,10 @@ func implementsAnyPool(p Pool) bool { return false } +// ensureLen returns s reset to exactly length n, growing it if needed. n +// must be >= 0: a negative n panics on the s[:n] slice expression. Callers +// pass a record count read off the wire, which is rejected for < 0 before +// reaching here (see processRecordBatch's negative-count guard). func ensureLen[S ~[]E, E any](s S, n int) S { s = s[:cap(s)] if len(s) >= n { diff --git a/vendor/github.com/twmb/franz-go/pkg/kgo/producer.go b/vendor/github.com/twmb/franz-go/pkg/kgo/producer.go index c4a13618f9..7ca99bc427 100644 --- a/vendor/github.com/twmb/franz-go/pkg/kgo/producer.go +++ b/vendor/github.com/twmb/franz-go/pkg/kgo/producer.go @@ -59,9 +59,27 @@ type producer struct { batchPromises ring[batchPromise] // we never call die() on it + // onBatchPromiseBroadcast, if non-nil, is invoked from finishPromises + // immediately before the per-batch p.c.Broadcast() fires, with moreQueued + // reporting whether further promise elements are still queued in the ring + // (i.e. the broadcast is firing mid-drain rather than at ring exit). It + // exists purely so tests can observe the broadcast deterministically at + // its source instead of racing a woken goroutine; it is always nil in + // production. + onBatchPromiseBroadcast func(moreQueued bool) + txnMu xsync.Mutex inTxn bool tx890p2 atomic.Bool + + // producedInTxn is set when a record is buffered within the current + // transaction and reset by BeginTransaction. EndTransaction consults + // it under KIP-890 part 2, where produce requests implicitly add + // partitions broker-side before the data append: a transaction whose + // every produce FAILED still needs an EndTxn abort even though no + // partition was marked addedToTxn client-side (marking happens only + // on produce success). + producedInTxn atomic.Bool } // BufferedProduceRecords returns the number of records currently buffered for @@ -87,6 +105,7 @@ func (cl *Client) BufferedProduceBytes() int64 { // EnsureProduceConnectionIsOpen attempts to open a produce connection to all // specified brokers, or all brokers if `brokers` is empty or contains -1. +// Broker IDs less than -1 are ignored. // // This can be used in an attempt to reduce the latency when producing if your // application produces infrequently: you can force open a produce connection a @@ -127,7 +146,7 @@ func (cl *Client) EnsureProduceConnectionIsOpen(ctx context.Context, brokers ... } cl.brokersMu.RUnlock() } else { - for _, b := range brokers { + for _, b := range keep { wg.Add(1) go func() { defer wg.Done() @@ -211,6 +230,20 @@ func (p *producer) purgeTopics(topics []string) { p.topicsMu.Lock() defer p.topicsMu.Unlock() + // We sweep unknown-topic waiters AND store the cleaned topics map + // while unknownTopicsMu is held. The store must not happen after the + // mu is released: partitionsForTopicProduce re-checks the topic's + // presence in p.topics under this mu before (re)creating an + // unknown-topic waiter, so holding the mu across both steps means a + // concurrent produce either sees the topic still present - and its + // just-added waiter is then swept by us - or sees it gone and + // re-registers the topic properly. If we instead released the mu + // between the sweep and the store (old behavior), a produce could + // land in that window: it saw the topic in p.topics, re-created a + // waiter, and we then removed the topic from the map - orphaning the + // waiter, which no metadata update would ever notify (the request set + // is built from p.topics), silently hanging the record forever. This + // mirrors storePartitionsUpdate's store-before-unlock requirement. p.unknownTopicsMu.Lock() for _, topic := range topics { if unknown, exists := p.unknownTopics[topic]; exists { @@ -222,17 +255,20 @@ func (p *producer) purgeTopics(topics []string) { }) } } - p.unknownTopicsMu.Unlock() - toStore := p.topics.clone() - defer p.topics.storeData(toStore) - + var purged []*topicPartitionsData for _, topic := range topics { d := toStore.loadTopic(topic) if d == nil { continue } delete(toStore, topic) + purged = append(purged, d) + } + p.topics.storeData(toStore) + p.unknownTopicsMu.Unlock() + + for _, d := range purged { for _, p := range d.partitions { r := p.records @@ -296,7 +332,7 @@ func (rs ProduceResults) FirstErr() error { return nil } -// First the first record and error in the produce results. +// First returns the first record and error in the produce results. // // This function is useful if you only passed one record to ProduceSync. func (rs ProduceResults) First() (*Record, error) { @@ -476,16 +512,26 @@ func (cl *Client) TryProduce( // Kafka replies. For a synchronous produce, see ProduceSync. Records are // produced in order per partition if the record is produced successfully. // Successfully produced records will have their attributes, offset, and -// partition set before the promise is called. All promises are called serially -// (and should be relatively fast). If a record's timestamp is unset, this -// sets the timestamp to time.Now. +// partition set before the promise is called. All promises are called +// serially, so they should be relatively fast and must not block: a promise +// that blocks on the client itself -- for example, a blocking Produce while +// the client is at its max-buffered limits, or a Flush -- waits on progress +// that only later promises can deliver and can deadlock the client. To +// produce from within a promise, spawn a goroutine (as +// AbortingFirstErrPromise does for aborting): a goroutine'd produce that +// blocks simply waits for the promise worker to make space. A direct +// TryProduce inside a promise is safe only while promise delivery is keeping +// up; if the record fails before buffering while the client is saturated +// with an extreme backlog of failing records, delivering that failure blocks +// the promise worker on itself. If a record's timestamp is unset, this sets +// the timestamp to time.Now. // // If the topic field is empty, the client will use the DefaultProduceTopic; if // that is also empty, the record is failed immediately. If the record is too // large to fit in a batch on its own in a produce request, the record will be -// failed with immediately kerr.MessageTooLarge. +// failed immediately with kerr.MessageTooLarge. // -// If the client is configured to automatically flush the client currently has +// If the client is configured to automatically flush and the client currently has // the configured maximum amount of records buffered, Produce will block. The // context can be used to cancel waiting while records flush to make space. In // contrast, if manual flushing is configured, the record will be failed @@ -625,6 +671,18 @@ func (cl *Client) produce( }() <-wait // we wait for the goroutine to exit, then unlock again (since the goroutine leaves the mutex locked) p.mu.Unlock() + // The goroutine above decremented p.blocked, but this is the + // cancel path: the record is failed, not buffered, so there is + // no compensating bufferedRecords++ (the success path below + // has one, keeping the sum unchanged). The bufferedRecords + + // blocked sum that Flush waits on therefore just dropped, and + // the only broadcast on this path - the one that woke the + // goroutine above - fired BEFORE its decrement, so a Flush that + // re-checked its predicate in between observed the stale + // pre-decrement sum and went back to waiting. Broadcast now, + // after the decrement is visible, so a Flush whose sum reached + // zero is woken; without this it can hang forever. + p.c.Broadcast() p.promiseRecordBeforeBuf(promisedRec{ctx, promise, r}, err) } @@ -645,6 +703,16 @@ func (cl *Client) produce( p.bufferedBytes += userSize p.mu.Unlock() + // Set at buffer time, before any produce reaches the broker, so this can + // over-set: a record counted here may still be failed locally (client + // close, unknown-topic timeout) before it is ever sent. The only + // consequence is an unnecessary EndTxn abort, which is always legal under + // KIP-890p2 -- an empty abort succeeds, bumps the epoch, and has nothing to + // commit. See the producedInTxn field doc and EndTransaction. + if cl.cfg.txnID != nil && !p.producedInTxn.Load() { + p.producedInTxn.Store(true) + } + cl.loadPartsAndPartition(promisedRec{ctx, promise, r}) } @@ -658,8 +726,18 @@ type batchPromise struct { err error } +// promiseBatch finishes a batch of records. This never parks on the ring's +// maxLen: every caller either carries records already admitted under the +// max-buffered accounting (sink responses, fail/purge paths) or holds a +// client lock (purgeTopics and failBufferedRecords under topicsMu and +// unknownTopicsMu, storePartitionsUpdate under unknownTopicsMu, recBuf +// failure paths under recBuf.mu). A parked lock-holder can deadlock: the +// promise worker is the only goroutine that frees ring space, and a user +// promise may re-enter the client (TryProduce) and need the very lock the +// parked pusher holds, so the worker would wait on the lock while the lock +// holder waits on the worker. func (p *producer) promiseBatch(b batchPromise) { - if first, _ := p.batchPromises.push(b); first { + if first, _ := p.batchPromises.pushForce(b); first { go p.finishPromises(b) } } @@ -668,32 +746,54 @@ func (p *producer) promiseRecord(pr promisedRec, err error) { p.promiseBatch(batchPromise{recs: []promisedRec{pr}, err: err}) } +// promiseRecordBeforeBuf finishes a record that failed before it was ever +// buffered (and before it counted toward bufferedRecords). Only this entry +// applies the ring's maxLen backpressure: pre-buffer failures are the one +// promise source not bounded by the max-buffered admission, so a spin loop +// of failing produces - blocking Produce or TryProduce alike - could +// otherwise grow the ring without bound (#1194). Parking here is the +// deliberate trade: every accepted record costs memory until its promise +// runs and the promise is the only completion channel, so a caller outpacing +// the promise worker must be blocked, not absorbed. The one caller that can +// never park is the promise worker itself (a promise calling TryProduce with +// a record that fails pre-buffer); produce-from-promise is documented to +// spawn a goroutine, whose park is safe: the worker keeps draining and the +// goroutine proceeds when space frees. func (p *producer) promiseRecordBeforeBuf(pr promisedRec, err error) { - p.promiseBatch(batchPromise{recs: []promisedRec{pr}, beforeBuf: true, err: err}) + b := batchPromise{recs: []promisedRec{pr}, beforeBuf: true, err: err} + if first, _ := p.batchPromises.push(b); first { + go p.finishPromises(b) + } } func (p *producer) finishPromises(b batchPromise) { cl := p.cl var more bool var broadcast bool - defer func() { - if broadcast { - p.c.Broadcast() - } - }() start: for i, pr := range b.recs { pr.LeaderEpoch = -1 - if b.baseOffset == -1 { - // if the base offset is invalid/unknown (-1), all record offsets should - // be treated as unknown + if b.err != nil { + // Failed records carry unknown-sentinels, not the + // batchPromise zero values: without this, a failed + // record's offset was its index within the failed + // batch and its producer id/epoch were 0 - + // plausible-looking garbage. pr.Offset = -1 + pr.ProducerID = -1 + pr.ProducerEpoch = -1 } else { - pr.Offset = b.baseOffset + int64(i) + if b.baseOffset == -1 { + // if the base offset is invalid/unknown (-1), all record offsets should + // be treated as unknown + pr.Offset = -1 + } else { + pr.Offset = b.baseOffset + int64(i) + } + pr.ProducerID = b.pid + pr.ProducerEpoch = b.epoch + pr.Attrs = b.attrs } - pr.ProducerID = b.pid - pr.ProducerEpoch = b.epoch - pr.Attrs = b.attrs recBroadcast := cl.finishRecordPromise(pr, b.err, b.beforeBuf) broadcast = broadcast || recBroadcast } @@ -702,6 +802,28 @@ start: cl.prsPool.put(b.recs) } + // We broadcast per batch, not per record (waking blocked producers on + // every record forces tiny one-record batches; see ead18d3c) - but + // also not once per ring drain: while pre-buffer failure promises keep + // arriving from other goroutines, this loop never observes an empty + // ring and never exits, and a deferred-to-exit broadcast would starve + // a Flush whose condition (bufferedRecords == 0) became true mid-drain + // and blocked Produce calls whose space opened. + if broadcast { + if p.onBatchPromiseBroadcast != nil { + // The current (just-processed) element is still in the ring + // here, since dropPeek runs below; l > 1 thus means more + // elements are queued behind it and the broadcast is firing + // mid-drain rather than at ring exit. + p.batchPromises.mu.Lock() + moreQueued := p.batchPromises.l > 1 + p.batchPromises.mu.Unlock() + p.onBatchPromiseBroadcast(moreQueued) + } + p.c.Broadcast() + broadcast = false + } + b, more, _ = p.batchPromises.dropPeek() if more { goto start @@ -767,6 +889,18 @@ func (cl *Client) doPartition(parts *topicPartitions, partsData *topicPartitions mapping := partsData.writablePartitions if parts.partitioner.RequiresConsistency(pr.Record) { mapping = partsData.partitions + } else if len(mapping) == 0 && len(partsData.partitions) > 0 { + // Every partition has a retriable load error, e.g. a rolling + // restart of an RF=1 broker briefly left all partitions + // leaderless. Rather than failing the record up front with + // the synthetic error below, fall back to the full set: the + // record buffers and rides the normal metadata-refresh retry + // path, the same as records on consistency-requiring + // partitioners via the branch above. If the outage outlasts + // the delivery timeout or retry limits, the record fails + // with the partition's actual load error. The Java client + // falls back identically when no partition is available. + mapping = partsData.partitions } if len(mapping) == 0 { cl.producer.promiseRecord(pr, errors.New("unable to partition record due to no usable partitions")) @@ -846,7 +980,7 @@ type producerID struct { var errReloadProducerID = errors.New("producer id needs reloading") -// initProducerID initializes the client's producer ID for idempotent +// producerID returns, loading if necessary, the client's producer ID for idempotent // producing only (no transactions, which are more special). After the first // load, this clears all buffered unknown topics. func (cl *Client) producerID(ctxFn func() context.Context) (int64, int16, error) { @@ -997,7 +1131,21 @@ func (cl *Client) doInitProducerID(ctxFn func() context.Context, lastID int64, l if err != nil { return err } - return nil // resp.ErrorCode handled below + // We return ConcurrentTransactions so that our wrapping + // doWithConcurrentTransactions retries in place: the + // coordinator replies with it while still completing (or + // fence-aborting) a previous transaction for this + // transactional ID. Notably, taking over a crashed + // incarnation's ongoing transaction ALWAYS receives it at + // least once: the broker internally aborts the old + // transaction and tells us to retry. Surfacing the error + // instead would bubble a routine, transient condition up as a + // BeginTransaction failure. All other response error codes + // are classified below. + if err := kerr.ErrorForCode(resp.ErrorCode); errors.Is(err, kerr.ConcurrentTransactions) { + return err + } + return nil }) if err != nil { if errors.Is(err, errUnknownRequestKey) || errors.Is(err, errBrokerTooOld) { @@ -1046,47 +1194,68 @@ func (cl *Client) partitionsForTopicProduce(pr promisedRec) (*topicPartitions, * p := &cl.producer topic := pr.Topic - topics := p.topics.load() - parts, exists := topics[topic] - if exists { - if v := parts.load(); len(v.partitions) > 0 { - return parts, v + for { + topics := p.topics.load() + parts, exists := topics[topic] + if exists { + if v := parts.load(); len(v.partitions) > 0 { + return parts, v + } } - } - - if !exists { // topic did not exist: check again under mu and potentially create it - p.topicsMu.Lock() - defer p.topicsMu.Unlock() - if parts, exists = p.topics.load()[topic]; !exists { // update parts for below - // Before we store the new topic, we lock unknown - // topics to prevent a concurrent metadata update - // seeing our new topic before we are waiting from the - // addUnknownTopicRecord fn. Otherwise, we would wait - // and never be re-notified. - p.unknownTopicsMu.Lock() - defer p.unknownTopicsMu.Unlock() - - p.topics.storeTopics([]string{topic}) - cl.addUnknownTopicRecord(pr) - cl.triggerUpdateMetadataNow("forced load because we are producing to a topic for the first time") - return nil, nil + if !exists { // topic did not exist: check again under mu and potentially create it + p.topicsMu.Lock() + if _, exists = p.topics.load()[topic]; !exists { + // Before we store the new topic, we lock unknown + // topics to prevent a concurrent metadata update + // seeing our new topic before we are waiting from the + // addUnknownTopicRecord fn. Otherwise, we would wait + // and never be re-notified. + p.unknownTopicsMu.Lock() + p.topics.storeTopics([]string{topic}) + cl.addUnknownTopicRecord(pr) + cl.triggerUpdateMetadataNow("forced load because we are producing to a topic for the first time") + p.unknownTopicsMu.Unlock() + p.topicsMu.Unlock() + return nil, nil + } + p.topicsMu.Unlock() } - } - // Here, the topic existed, but maybe has not loaded partitions yet. We - // have to lock unknown topics first to ensure ordering just in case a - // load has not happened. - p.unknownTopicsMu.Lock() - defer p.unknownTopicsMu.Unlock() + // Here, the topic existed, but maybe has not loaded partitions + // yet. We have to lock unknown topics first to ensure ordering + // just in case a load has not happened. + p.unknownTopicsMu.Lock() + + // Re-resolve the topic now that we hold the mu. Both sides of + // the entry's lifecycle change only under unknownTopicsMu: + // storePartitionsUpdate stores loaded partitions before + // releasing it, and purgeTopics stores the topic's removal + // from p.topics before releasing it. Without the re-check we + // could add a waiter for a topic a concurrent purge already + // swept: the purge then removes the topic from p.topics, no + // metadata update ever requests it (the request set is built + // from p.topics), and the waiter - and its records - silently + // hang forever. Re-resolving from the current map (not the + // `parts` pointer loaded above) also covers a purge+recreate + // swapping the topicPartitions object in between. + parts, exists = p.topics.load()[topic] + if !exists { + // Purged between our load and the lock; retry from the + // top, which re-creates the topic properly. + p.unknownTopicsMu.Unlock() + continue + } + if v := parts.load(); len(v.partitions) > 0 { + p.unknownTopicsMu.Unlock() + return parts, v + } + cl.addUnknownTopicRecord(pr) + cl.triggerUpdateMetadata(false, "reload trigger due to produce topic still not known") + p.unknownTopicsMu.Unlock() - if v := parts.load(); len(v.partitions) > 0 { - return parts, v + return nil, nil // our record is buffered waiting for metadata update; nothing to return } - cl.addUnknownTopicRecord(pr) - cl.triggerUpdateMetadata(false, "reload trigger due to produce topic still not known") - - return nil, nil // our record is buffered waiting for metadata update; nothing to return } // addUnknownTopicRecord adds a record to a topic whose partitions are @@ -1107,7 +1276,11 @@ func (cl *Client) addUnknownTopicRecord(pr promisedRec) { } } -// waitUnknownTopic waits for a notification +// waitUnknownTopic waits for the topic to be resolved by a metadata update +// (the wait channel is closed, or receives retryable load errors that count +// toward the retry limits), or for the record/produce contexts, the client, +// the record timeout, or an abort (the fatal channel) to end the wait and +// fail all buffered records. func (cl *Client) waitUnknownTopic( pctx context.Context, // context passed to Produce rctx context.Context, // context on the record itself @@ -1154,7 +1327,11 @@ func (cl *Client) waitUnknownTopic( case err = <-unknown.fatal: case retryableErr, ok := <-unknown.wait: if !ok { - cl.cfg.logger.Log(LogLevelInfo, "done waiting for metadata for new topic", "topic", topic) + // The channel is closed when the topic's partitions + // load, but also when the topic is purged or all + // buffered records are failed; whoever closed it + // already handled the buffered records. + cl.cfg.logger.Log(LogLevelInfo, "done waiting on metadata for new topic", "topic", topic) return // metadata was successful! } cl.cfg.logger.Log(LogLevelInfo, "new topic metadata wait failed, retrying wait", "topic", topic, "err", retryableErr) @@ -1214,7 +1391,7 @@ func (cl *Client) unlingerDueToMaxRecsBuffered() { // If the context finishes (Done), this returns the context's error. // // This function is safe to call multiple times concurrently, and safe to call -// concurrent with Flush. +// concurrent with AbortBufferedRecords. func (cl *Client) Flush(ctx context.Context) error { p := &cl.producer diff --git a/vendor/github.com/twmb/franz-go/pkg/kgo/record_and_fetch.go b/vendor/github.com/twmb/franz-go/pkg/kgo/record_and_fetch.go index 2c0275b16d..a1bb8e485e 100644 --- a/vendor/github.com/twmb/franz-go/pkg/kgo/record_and_fetch.go +++ b/vendor/github.com/twmb/franz-go/pkg/kgo/record_and_fetch.go @@ -655,17 +655,28 @@ func (fs Fetches) EachTopic(fn func(FetchTopic)) { return } + // A topic's partitions are led by different brokers, so the same topic + // is spread across multiple Fetch entries (one per broker response); we + // must carry the TopicID across them rather than zeroing it. The broker + // returns the same ID in every fetch response for the topic, so the + // first non-zero copy is authoritative. Without this, EachTopic returns + // a zero TopicID whenever more than one broker replied -- i.e. nearly + // always in a real cluster, yet never in a single-broker test. topics := make(map[string][]FetchPartition) + ids := make(map[string][16]byte) for _, fetch := range fs { for _, topic := range fetch.Topics { topics[topic.Topic] = append(topics[topic.Topic], topic.Partitions...) + if topic.TopicID != ([16]byte{}) { + ids[topic.Topic] = topic.TopicID + } } } for topic, partitions := range topics { fn(FetchTopic{ topic, - [16]byte{}, + ids[topic], partitions, }) } diff --git a/vendor/github.com/twmb/franz-go/pkg/kgo/record_formatter.go b/vendor/github.com/twmb/franz-go/pkg/kgo/record_formatter.go index 59f9d75d82..3747dd4dc3 100644 --- a/vendor/github.com/twmb/franz-go/pkg/kgo/record_formatter.go +++ b/vendor/github.com/twmb/franz-go/pkg/kgo/record_formatter.go @@ -84,7 +84,7 @@ func (f *RecordFormatter) AppendPartitionRecord(b []byte, p *FetchPartition, r * // %D share group delivery count (0 if not from a share group) // %A share group acquisition deadline (timestamp, 0 if not from a share group) // -// For AppendPartitionRecord, the formatter also undersands the following three +// For AppendPartitionRecord, the formatter also understands the following three // formatting options: // // %[ partition log start offset @@ -238,9 +238,7 @@ func NewRecordFormatter(layout string) (*RecordFormatter, error) { var f RecordFormatter var literal []byte // non-formatted raw text to output - var i int for len(layout) > 0 { - i++ c, size := utf8.DecodeRuneInString(layout) rawc := layout[:size] layout = layout[size:] @@ -777,7 +775,7 @@ func parseUnpack(layout string) (func([]byte, []byte) []byte, error) { islittle := little fns = append(fns, func(dst, src []byte) ([]byte, int) { if len(src) < need { - return append(dst, fmt.Sprintf("%%!%%s(have %d bytes, need %d)", len(src), need)...), len(src) + return append(dst, fmt.Sprintf("%%!(have %d bytes, need %d)", len(src), need)...), len(src) } var ul, ub uint64 @@ -1226,7 +1224,11 @@ func (r *RecordReader) parseReadLayout(layout string) error { switch escaped { default: - return fmt.Errorf("unknown percent escape sequence %q", layout[:1]) + // escaped is the verb byte; layout has already advanced past + // it (and past any '{'), so it can be empty here - slicing + // layout[:1] would panic for an unknown verb at the end of + // the layout (e.g. "%q"). + return fmt.Errorf("unknown percent escape sequence %q", string(escaped)) case 'T', 'K', 'V', 'H': var dst *uint64 @@ -1391,7 +1393,7 @@ func (r *RecordReader) parseReadLayout(layout string) error { inner(b, r) return nil }} - bit.set(bit) + bits.set(bit) if bits.has(bitSize) { if re != nil { return errors.New("cannot specify exact size and regular expression") @@ -1481,6 +1483,13 @@ func (r *RecordReader) parseReadLayout(layout string) error { reads = append(reads, fn) } } + // A layout consisting solely of noread verbs (fixed sizes/numbers like + // "%p{3}") never consumes input, so next() never reaches an EOF, never + // sets r.done, and ReadRecord returns identical records forever. Reject + // it at construction rather than looping at read time. + if len(reads) == 0 { + return errors.New("RecordReader: layout reads nothing from the input") + } r.fns = make([]readParse, 0, len(noreads)+len(reads)) r.fns = append(r.fns, noreads...) r.fns = append(r.fns, reads...) @@ -1767,6 +1776,20 @@ func (r *RecordReader) next(rec *Record) error { continue } + // If the input EOF'd before a fixed-size read accumulated its full + // byte count, r.buf is short. This happens on the fall-through + // above: a zero-byte read surfaces as plain io.EOF (readSize only + // reports io.EOF when it read nothing; a partial read is already + // io.ErrUnexpectedEOF), and if that read is the last fn after an + // earlier real read it is not the clean record boundary, so it + // reaches here with an empty buffer. The fixed-width number parsers + // index r.buf at constant offsets (binary.*.Uint64 etc.) and panic + // on a short slice, so surface the truncation as the unexpected EOF + // that ReadRecord's doc already promises for a mid-record EOF. + if fn.read.size > 0 && len(r.buf) < fn.read.size { + return io.ErrUnexpectedEOF + } + if err := fn.parse(r.buf, rec); err != nil { return err } @@ -1837,10 +1860,34 @@ func (r *RecordReader) readRe(re *regexp.Regexp) error { } func (r *RecordReader) readSize(n int) error { - r.buf = append(r.buf, make([]byte, n)...) - n, err := io.ReadFull(r.r, r.buf) - r.buf = r.buf[:n] - return err + if n < 0 { + // A size verb (%T/%K/%V) reads its value from the input as a + // uint64; converting to int can wrap negative for values above + // math.MaxInt64. Reject rather than panicking in make below. + return fmt.Errorf("invalid negative read size %d", n) + } + // Read in bounded chunks rather than pre-allocating n bytes up front: + // a hostile or corrupt size verb can claim a huge length that would + // OOM the process before io.ReadFull notices the input is short. The + // caller resets r.buf to empty before invoking us, so we accumulate + // from index 0. + const chunk = 64 << 10 + for len(r.buf) < n { + start := len(r.buf) + r.buf = append(r.buf, make([]byte, min(n-start, chunk))...) + nn, err := io.ReadFull(r.r, r.buf[start:]) + r.buf = r.buf[:start+nn] + if err != nil { + // io.ReadFull reports EOF only when it read zero bytes of + // this chunk; if we have already accumulated bytes for this + // field the value is truncated, which is an unexpected EOF. + if err == io.EOF && len(r.buf) > 0 { + err = io.ErrUnexpectedEOF + } + return err + } + } + return nil } func (r *RecordReader) readExact(d []byte) error { @@ -2263,7 +2310,10 @@ func parseLayoutSlash(layout string) (byte, int, error) { return 0, 0, errors.New("invalid non-terminated hex escape sequence at end of delim string") } hex := layout[1:3] - n, err := strconv.ParseInt(hex, 16, 8) + // A \xNN escape names the byte with hex value NN, i.e. the full + // [0, 255] range. ParseInt with bitSize 8 caps at 0x7f and + // rejected 0x80-0xff; ParseUint with bitSize 8 accepts [0, 255]. + n, err := strconv.ParseUint(hex, 16, 8) if err != nil { return 0, 0, fmt.Errorf("unable to parse hex escape sequence %q: %v", hex, err) } diff --git a/vendor/github.com/twmb/franz-go/pkg/kgo/ring.go b/vendor/github.com/twmb/franz-go/pkg/kgo/ring.go index 9aad112547..31a4d4485d 100644 --- a/vendor/github.com/twmb/franz-go/pkg/kgo/ring.go +++ b/vendor/github.com/twmb/franz-go/pkg/kgo/ring.go @@ -65,13 +65,43 @@ func (r *ring[T]) die() { } } +// empty returns whether the ring currently holds no elements. Because an +// element being processed stays in the ring until dropPeek removes it, +// empty also means no worker goroutine is mid-element. +func (r *ring[T]) empty() bool { + r.mu.Lock() + defer r.mu.Unlock() + return r.l == 0 +} + func (r *ring[T]) push(elem T) (first, dead bool) { + return r.doPush(elem, true) +} + +// pushForce pushes even when the ring is at maxLen. The maxLen wait can only +// be used by pushers that do not deadlock against the ring's worker: the +// worker is the only goroutine that signals space (dropPeek), and a push +// made while holding a client lock (purge/fail paths, storePartitionsUpdate, +// recBuf failure paths) would park holding a lock the worker can need +// through a user promise re-entering the client - the worker then waits on +// the lock while the lock holder waits on the worker. Those internal pushers +// force. Forcing does not unbound the ring: every record an internal push +// carries was already admitted under the max-buffered-records accounting, so +// forced volume is capped by that admission; only produce-entry failure +// pushes (the unbounded source) take the blocking push. +func (r *ring[T]) pushForce(elem T) (first, dead bool) { + return r.doPush(elem, false) +} + +func (r *ring[T]) doPush(elem T, wait bool) (first, dead bool) { r.mu.Lock() defer r.mu.Unlock() // If a max length is set, block until there's space. - for r.maxLen > 0 && r.l >= r.maxLen && !r.dead { - r.cond.Wait() + if wait { + for r.maxLen > 0 && r.l >= r.maxLen && !r.dead { + r.cond.Wait() + } } if r.dead { diff --git a/vendor/github.com/twmb/franz-go/pkg/kgo/sink.go b/vendor/github.com/twmb/franz-go/pkg/kgo/sink.go index 29099f6ce1..df3a1543a7 100644 --- a/vendor/github.com/twmb/franz-go/pkg/kgo/sink.go +++ b/vendor/github.com/twmb/franz-go/pkg/kgo/sink.go @@ -31,9 +31,9 @@ type sink struct { drainState workLoop - // seqRespsMu, guarded by seqRespsMu, contains responses that must - // be handled sequentially. These responses are handled asynchronously, - // but sequentially. + // seqResps contains responses that must be handled sequentially: the + // ring preserves issue order, and its single transient worker handles + // responses asynchronously but in that order. seqResps ring[*seqResp] // we never call die() on it backoffMu xsync.Mutex // guards the following @@ -461,10 +461,14 @@ func (s *sink) produce(sem <-chan struct{}) bool { // records for a new transaction. Any records in the request are // from the current transaction at epoch N, which is correct. // - // This undo is safe: resetBatchDrainIdx + decInflight rewind - // each recBuf to its pre-drain state, the existing defer releases - // the semaphore (produced is still false), and returning true - // retries produce() with the correct epoch. + // This undo is safe: undoStagedBatches rewinds each recBuf to its + // pre-drain state -- drain index, inflight, and (for any partition + // this createReq newly added to the transaction) the addedToTxn flag, + // so the next drain re-issues its AddPartitionsToTxn instead of + // producing to a partition the broker never learned is in the txn (see + // undoStagedBatches). The existing defer releases the semaphore + // (produced is still false), and returning true retries produce() with + // the correct epoch. // // We use a raw atomic load rather than producerID() to avoid // side effects (blocking on idMu, triggering InitProducerID @@ -491,10 +495,7 @@ func (s *sink) produce(sem <-chan struct{}) bool { // unchanged, so we bail and the next drain pulls the reloaded // (id, epoch) with seq=0 consistently. if cur := s.cl.producer.id.Load().(*producerID); cur.id != id || cur.epoch != epoch || cur.err != nil { - req.batches.sliced().eachOwnerLocked(func(b *recBatch) { - b.owner.resetBatchDrainIdx() - b.decInflight() - }) + req.undoStagedBatches(txnReq) return true } @@ -510,18 +511,30 @@ func (s *sink) produce(sem <-chan struct{}) bool { batchesStripped, err := s.doTxnReq(req, txnReq) if err != nil { switch { - case errors.Is(err, kerr.TransactionAbortable): - // If we get TransactionAbortable, we continue into producing. - // The produce will fail with the same error, and this is the - // only way to notify the user to abort the txn. case isRetryableBrokerErr(err) || isDialNonTimeoutErr(err): s.cl.bumpRepeatedLoadErr(err) s.cl.cfg.logger.Log(LogLevelWarn, "unable to AddPartitionsToTxn due to retryable broker err, bumping client's buffered record load errors by 1 and retrying", "err", err) s.cl.triggerUpdateMetadata(false, "attempting to refresh broker list due to failed AddPartitionsToTxn requests") return moreToDrain || len(req.batches.bs) > 0 // nothing stripped if request-issuing error default: - // Note that err can be InvalidProducerEpoch, which is - // potentially recoverable in EndTransaction. + // This includes TransactionAbortable. We used to + // continue into producing on TransactionAbortable so + // the produce failure would carry the error to the + // user, but doTxnReq's error path has already + // requeued every batch in the request (reset drain + // indexes, decremented inflight, and un-marked + // addedToTxn for the partitions whose add actually + // failed): producing those batches anyway would + // decrement inflight a second time (wrapping the + // counter and permanently wedging the recBuf's + // drain gate) and could re-drain batches that are + // already in flight. Failing the producer ID delivers + // the same error to all buffered records on the next + // drain, and TransactionAbortable remains recoverable + // via EndTransaction. + // + // Note that err can also be InvalidProducerEpoch, + // which is potentially recoverable in EndTransaction. // // We do not fail all buffered records here, // because that can lead to undesirable behavior @@ -574,9 +587,10 @@ func (s *sink) doSequenced( // We can NOT use any record context. If we do, we force the request to // fail while also force the batch to be unfailable (due to no - // response). If and only if the user has disabled idempotency, we - // allow the user to cancel the request via some random record with a - // canceling context. + // response). Only if the user has disabled idempotency or opted into + // AllowIdempotentProduceCancellation do we allow canceling the request + // via some random record with a canceling context (createReq only + // sets firstCancelingCtx under those options). ctx := req.firstCancelingCtx if ctx == nil { ctx = s.cl.ctx @@ -618,14 +632,20 @@ func (s *sink) doTxnReq( req *produceRequest, txnReq *kmsg.AddPartitionsToTxnRequest, ) (stripped bool, err error) { - // If we return an unretryable error, then we have to reset everything - // to not be in the transaction and begin draining at the start. + // If we return an unretryable error, every batch in this request must + // be requeued and any partition this request newly added to the + // transaction must be un-marked, since we will not issue the produce + // request. undoStagedBatches scopes the un-marking to txnReq so a + // partition added by an EARLIER AddPartitionsToTxn of this transaction + // (a broker-acked fact, deliberately absent here) keeps its membership; + // clearing it would make EndTransaction's anyAdded walk skip EndTxn and + // strand the broker-side transaction until its timeout abort. // // These batches must be the first in their recBuf, because we would // not be trying to add them to a partition if they were not. defer func() { if err != nil { - req.batches.eachOwnerLocked(seqRecBatch.removeFromTxn) + req.undoStagedBatches(txnReq) } }() // We do NOT let record context cancelations fail this request: doing @@ -633,6 +653,12 @@ func (s *sink) doTxnReq( // similar to the warning we give in the txn.go file, but the // difference there is the user knows explicitly at the function call // that canceling the context will opt them into invalid state. + // + // Note that the concurrent-transactions wrapper is defensive only: + // AddPartitionsToTxn is pinned at most v3 (no top-level error code) + // and a per-partition CONCURRENT_TRANSACTIONS is retriable, so it is + // stripped in issueTxnReq and healed by requeue+backoff rather than + // ever surfacing to the wrapper. err = s.cl.doWithConcurrentTransactions(s.cl.ctx, fmt.Sprintf("AddPartitionsToTxn-sink%d", s.nodeID), func() error { stripped, err = s.issueTxnReq(req, txnReq) return err @@ -640,6 +666,54 @@ func (s *sink) doTxnReq( return stripped, err } +// txnReqContains returns whether the AddPartitionsToTxn request contains the +// topic and partition. Partitions already in the transaction from an earlier +// request are deliberately absent (see txnReqBuilder.add); failure handling +// and response processing must not touch their addedToTxn state. +func txnReqContains(txnReq *kmsg.AddPartitionsToTxnRequest, topic string, partition int32) bool { + for i := range txnReq.Topics { + t := &txnReq.Topics[i] + if t.Topic != topic { + continue + } + for _, p := range t.Partitions { + if p == partition { + return true + } + } + } + return false +} + +// undoStagedBatches rewinds every batch that createReq staged into this request +// back to its pre-drain state, for the early-return arms that decide not to +// issue the request after staging it: the producer-ID/epoch recheck in +// produce() and doTxnReq's failure defer. It resets each recBuf's drain index +// and decrements inflight, and -- for the partitions THIS request newly added +// to the transaction -- clears addedToTxn so the next drain re-issues their +// AddPartitionsToTxn. +// +// txnReq holds exactly the partitions createReq newly added: txnReqBuilder.add +// only records a partition whose addedToTxn flipped false->true, so partitions +// added to the transaction by an EARLIER request are deliberately absent and +// keep their broker-acked membership. Leaving a newly-added partition's +// addedToTxn set after rewinding would suppress its AddPartitionsToTxn on the +// next drain (txnReqBuilder.add skips already-added partitions); the broker +// then rejects the produce to that unverified partition with INVALID_TXN_STATE +// (or, on a non-verifying broker, the records hang in a transaction the +// coordinator never learned the partition belongs to). txnReq is nil for +// non-transactional and pv12+ (KIP-890p2) producers, which never stage +// addedToTxn in createReq, so the clear is correctly skipped for them. +func (p *produceRequest) undoStagedBatches(txnReq *kmsg.AddPartitionsToTxnRequest) { + p.batches.eachOwnerLocked(func(batch seqRecBatch) { + if txnReq != nil && txnReqContains(txnReq, batch.owner.topic, batch.owner.partition) { + batch.owner.addedToTxn.Store(false) + } + batch.owner.resetBatchDrainIdx() + batch.decInflight() + }) +} + // Removing a batch from the transaction means we will not be issuing it // inflight, and that it was not added to the txn and that we need to reset the // drain index. @@ -665,7 +739,24 @@ func (s *sink) issueTxnReq( continue } for _, partition := range topic.Partitions { - if err := kerr.ErrorForCode(partition.ErrorCode); err != nil && err != kerr.TransactionAbortable { // see below for txn abortable + // TransactionAbortable partitions are deliberately NOT + // handled as errors here: the batch stays in the request, + // the subsequent produce fails with the same abortable + // error, and the record promises carry it to the user (who + // then aborts; recovery happens via EndTransaction). + if err := kerr.ErrorForCode(partition.ErrorCode); err != nil && err != kerr.TransactionAbortable { + // An errored partition that we did not ask to add must + // not strip a batch nor fail the producer ID: it could + // name a partition added by an earlier request of this + // transaction (deliberately absent from this txnReq), + // and un-marking that would break EndTransaction's + // anyAdded accounting -- see doTxnReq's deferred + // failure handling. + if !txnReqContains(txnReq, topic.Topic, partition.Partition) { + s.cl.cfg.logger.Log(LogLevelError, "broker replied with errored partition in AddPartitionsToTxnResponse that was not in the request", "topic", topic.Topic, "partition", partition.Partition) + continue + } + // OperationNotAttempted is set for all partitions that are authorized // if any partition is unauthorized _or_ does not exist. We simply remove // unattempted partitions and treat them as retryable. @@ -821,11 +912,18 @@ func (s *sink) handleReqResp(br *broker, req *produceRequest, resp kmsg.Response rt := &kresp.Topics[i] topic := rt.Topic tid := rt.TopicID + // For topics (and partitions below) that we did not produce to, + // we deliberately do NOT touch req.metrics: metrics entries only + // exist for batches that were actually appended to the request, + // so a genuinely invented entry has nothing to remove -- and a + // DUPLICATED reply entry lands here too (the first occurrence + // empties req.batches), where deleting would erase the metrics + // of a batch the first occurrence legitimately processed and + // silently skip its OnProduceBatchWritten hook. if req.version >= 13 { var ok bool if topic, ok = req.batches.id2t[rt.TopicID]; !ok { - s.cl.cfg.logger.Log(LogLevelError, "broker erroneously replied with topic id in produce request that we did not produce to", "broker", logID(s.nodeID), "topic", topic, "topic_id", strtid(rt.TopicID)) - delete(req.metrics, topic) + s.cl.cfg.logger.Log(LogLevelError, "broker erroneously replied with topic id in produce request that we did not produce to", "broker", logID(s.nodeID), "topic_id", strtid(rt.TopicID)) continue } } else { @@ -834,7 +932,6 @@ func (s *sink) handleReqResp(br *broker, req *produceRequest, resp kmsg.Response partitions, ok := req.batches.bs[topic] if !ok { s.cl.cfg.logger.Log(LogLevelError, "broker erroneously replied with topic in produce request that we did not produce to", "broker", logID(s.nodeID), "topic", topic) - delete(req.metrics, topic) continue } @@ -849,8 +946,7 @@ func (s *sink) handleReqResp(br *broker, req *produceRequest, resp kmsg.Response batch, ok := partitions[partition] if !ok { s.cl.cfg.logger.Log(LogLevelError, "broker erroneously replied with partition in produce request that we did not produce to", "broker", logID(s.nodeID), "topic", rt.Topic, "partition", partition) - delete(tmetrics, partition) - continue // should not hit this + continue // should not hit this; see the topic-level comment above for why tmetrics is left alone } delete(partitions, partition) @@ -1420,7 +1516,13 @@ type recBuf struct { // this recBuf. Every time this hits zero, if the batchDrainIdx is not // at the end, we clear inflightOnSink and trigger the *current* sink // to drain. - inflight uint8 + // + // This is bounded by the sink's inflight sem: 1 or 4 when idempotent, + // or the user's MaxProduceRequestsInflightPerBroker (no upper bound) + // when idempotency is disabled -- which is why this is an int32 and + // not a small type that a large user value could wrap, breaking the + // != 0 drain gates here and in createReq. + inflight int32 lastAckedOffset int64 // last ProduceResponse's BaseOffset + how many records we produced @@ -1509,6 +1611,27 @@ func (recBuf *recBuf) bufferRecord(pr promisedRec, abortOnNewBatch bool) bool { return true } + // If the client is closing, fail the record rather than buffering it + // into a recBuf whose sink drain loop has already exited. close() cancels + // cl.ctx and then sweeps every recBuf exactly once via + // failBufferedRecords; a record buffered after that sweep would never be + // failed - its promise would never fire, BufferedProduceRecords would + // never return to zero, and a later Flush would hang - contradicting the + // documented ErrClientClosed contract ("for producing, records are failed + // with this error"). Checking cl.ctx under recBuf.mu (held here and by + // failAllRecords) is race-free given the close ordering (ctxCancel then + // sweep): we either observe the cancel and fail here, or we buffer before + // the sweep and the sweep fails us. The unknown-topic sibling path already + // honors this via waitUnknownTopic's cl.ctx.Done arm; this is the missing + // guard on the known-topic sibling. We select on Done rather than calling + // Err to keep this per-record hot path free of the context's per-call mutex. + select { + case <-recBuf.cl.ctx.Done(): + recBuf.cl.producer.promiseRecord(pr, ErrClientClosed) + return true + default: + } + var ( mkNewBatch = true produceVersion = recBuf.sink.produceVersion.Load() @@ -1604,9 +1727,11 @@ func (recBuf *recBuf) unlingerAndManuallyDrain() { // load errors during metadata updates. // // Partition load errors are generally temporary (leader/listener/replica not -// available), and this try bump is not expected to do much. If for some reason -// a partition errors for a long time and we are not idempotent, this function -// drops all buffered records. +// available, or a metadata response from an out of date broker that is +// missing a partition we know about), and this try bump is not expected to do +// much. Records are failed only once a bound trips: the record timeout or +// retry limit, or for unknown-partition style errors (including a metadata +// response missing the partition), the unknown fail limit. func (recBuf *recBuf) bumpRepeatedLoadErr(err error) { recBuf.mu.Lock() defer recBuf.mu.Unlock() @@ -1624,10 +1749,10 @@ func (recBuf *recBuf) bumpRepeatedLoadErr(err error) { canFail = !recBuf.cl.idempotent() || recBuf.cl.cfg.allowIdempotentProduceCancellation || (batch0.canFailFromLoadErrs && !batch0.unsureIfProduced) // we can only fail if we are not idempotent, cancellation is allowed, or if we have no outstanding requests batch0Fail = batch0.maybeFailErr(&recBuf.cl.cfg) != nil // timeout, retries, or aborting netErr = isRetryableBrokerErr(err) || isDialNonTimeoutErr(err) // we can fail if this is *not* a network error - retryableKerr = kerr.IsRetriable(err) // we fail if this is not a retryable kerr, - isUnknownLimit = recBuf.checkUnknownFailLimit(err) // or if it is, but it is UnknownTopicOrPartition and we are at our limit + retryableErr = kerr.IsRetriable(err) || errors.Is(err, errMissingMetadataPartition) // we fail if this is not a retryable error (missing-metadata-partition retries like unknown topic), + isUnknownLimit = recBuf.checkUnknownFailLimit(err) // or if it is, but it is an unknown topic error and we are at our limit - willFail = canFail && (batch0Fail || !netErr && (!retryableKerr || retryableKerr && isUnknownLimit)) + willFail = canFail && (batch0Fail || !netErr && (!retryableErr || retryableErr && isUnknownLimit)) ) batch0.isFailingFromLoadErr = willFail batch0.mu.Unlock() @@ -1640,7 +1765,7 @@ func (recBuf *recBuf) bumpRepeatedLoadErr(err error) { "can_fail", canFail, "batch0_should_fail", batch0Fail, "is_network_err", netErr, - "is_retryable_kerr", retryableKerr, + "is_retryable_err", retryableErr, "is_unknown_limit", isUnknownLimit, "will_fail", willFail, ) @@ -1650,13 +1775,20 @@ func (recBuf *recBuf) bumpRepeatedLoadErr(err error) { } } -// Called locked, if err is an unknown error, bumps our limit, otherwise resets -// it. This returns if we have reached or exceeded the limit. +// Called locked. A successful produce (nil err) resets the count; an unknown +// topic error bumps it; any other error leaves it unchanged -- resetting only +// on success is what keeps interleaved errors (e.g. an alternating +// NOT_LEADER_FOR_PARTITION) from holding the count below the limit forever. +// Three errors count: UNKNOWN_TOPIC_OR_PARTITION, UNKNOWN_TOPIC_ID (a deleted- +// and-recreated topic returns it until the user purges and re-adds, since +// produce v13+ keys topics by ID), and the metadata-side +// errMissingMetadataPartition twin. Returns whether we have exceeded the limit. func (recBuf *recBuf) checkUnknownFailLimit(err error) bool { - if errors.Is(err, kerr.UnknownTopicOrPartition) { - recBuf.unknownFailures++ - } else { + switch { + case err == nil: recBuf.unknownFailures = 0 + case errors.Is(err, kerr.UnknownTopicOrPartition) || errors.Is(err, kerr.UnknownTopicID) || errors.Is(err, errMissingMetadataPartition): + recBuf.unknownFailures++ } return recBuf.cl.cfg.maxUnknownFailures >= 0 && recBuf.unknownFailures > recBuf.cl.cfg.maxUnknownFailures } @@ -1918,7 +2050,7 @@ type produceRequest struct { timeout int32 batches seqRecBatches - firstCancelingCtx context.Context // of all batches added, the first one with a record that has a canceling context; only used with disableIdempotency + firstCancelingCtx context.Context // of all batches added, the first one with a record that has a canceling context; only used with disableIdempotency or allowIdempotentProduceCancellation producerID int64 producerEpoch int16 diff --git a/vendor/github.com/twmb/franz-go/pkg/kgo/source.go b/vendor/github.com/twmb/franz-go/pkg/kgo/source.go index 4266cbea2d..458247cc03 100644 --- a/vendor/github.com/twmb/franz-go/pkg/kgo/source.go +++ b/vendor/github.com/twmb/franz-go/pkg/kgo/source.go @@ -29,7 +29,7 @@ type readerFrom interface { // another fetch in the background. type source struct { cl *Client // our owning client, for cfg, metadata triggering, context, etc. - nodeID int32 // the node ID of the broker this sink belongs to + nodeID int32 // the node ID of the broker this source belongs to // Tracks how many _failed_ fetch requests we have in a row (unable to // receive a response). Any response, even responses with an ErrorCode @@ -117,7 +117,17 @@ func (s *source) removeCursor(rm *cursor) { // cursor is where we are consuming from for an individual partition. type cursor struct { - topic string + topic string + // topicID is written once at cursor creation and is deliberately + // never re-adopted if a delete+recreate hands back a new ID for the + // same name: a recreated topic stalls loudly (UNKNOWN_TOPIC_ID, see + // the UnknownTopicID arm below) and the user must purge+re-add. This + // is the principled alternative to librdkafka/Java's adopt-and-gamble; + // issue #908 records why auto-adoption was backed out (PR #391/#377: + // OffsetForLeaderEpoch has no TopicID field, so an adopted ID cannot + // be validated against truncation). The metadata merge copies this + // pointer over rather than swapping the ID; do not "fix" the stall + // into an adopt without solving #908. topicID [16]byte partition int32 @@ -297,7 +307,25 @@ func (p *cursorOffsetPreferred) move() { c.source.cl.sinksAndSourcesMu.Unlock() if !exists { + // The preferred replica is a broker we have not yet learned from + // metadata - e.g. a freshly added replica that the broker we + // fetched from already knows about, before our periodic refresh + // caught up. We force a metadata update so the source comes to + // exist for a future move, but we must ALSO re-enable the cursor + // on its current (leader) source. The caller (source.fetch) deletes + // this cursor from the request's used offsets right after we return, + // so neither the fetch's used-offset finishing nor a later buffered + // poll will re-enable it: leaving it unusable here would strand the + // partition. The leader is unchanged, so no cursor + // migration re-enables it, and a metadata refresh that merely + // learns the new broker never touches cursor usability - the + // partition would silently never be consumed again until an + // unrelated session restart (rebalance, assign, leader change). + // + // Re-enabling on the current source keeps us consuming from the + // leader until a later fetch's preferred replica can be honored. c.source.cl.triggerUpdateMetadataNow("cursor moving to a different broker that is not yet known") + c.allowUsable() return } @@ -1011,13 +1039,21 @@ func (s *source) fetch(consumerSession *consumerSession, doneFetch chan<- bool) return fetched default: - // Any other top-level error is unexpected: current brokers - // only emit session-related codes here. Rather than bumping - // the session epoch against a failed request, reset defensively - // so the next request re-establishes state the broker agrees - // with. - s.cl.cfg.logger.Log(LogLevelWarn, "fetch response has unexpected top-level error, resetting session", "broker", logID(s.nodeID), "err", err) - s.session.reset() + // Any other top-level error is unexpected: current brokers only + // emit session-related codes here, and every one of those arms + // above self-heals in a single round-trip (a reset re-establishes + // the session at epoch 0, which the broker then accepts). An + // unexpected code has no such bounded heal: a non-conformant or + // future broker that returns one persistently would spin this + // fetch at round-trip pace, re-logging on every iteration, because + // nothing here paces the loop. Back off rather than bumping the + // session epoch against a failed request; backoff also resets the + // session defensively so the next request re-establishes state the + // broker agrees with. This mirrors the transport-error and + // all-partitions-stripped paths, and the share fetch loop's + // top-level-error arm. + s.cl.cfg.logger.Log(LogLevelWarn, "fetch response has unexpected top-level error, resetting session and backing off", "broker", logID(s.nodeID), "err", err) + backoff(err) return fetched } @@ -1096,6 +1132,15 @@ func (s *source) handleReqResp(br *broker, req *fetchRequest, resp *kmsg.FetchRe numErrsStripped int kip320 = s.cl.supportsOffsetForLeaderEpoch() kmove kip951move + // seen guards against a broker returning the same partition (or the + // same topic) more than once in a single fetch response. Each + // requested partition maps to one stable *cursorOffsetNext for the + // life of the request; processing one twice would double-advance its + // offset / double-append its records, or enqueue two move()s for one + // cursor (the #1167 concurrent-source hazard). We must not dedup by + // deleting from req.usedOffsets - that map re-enables the cursor + // after the response - so we track seen pointers separately. + seen map[*cursorOffsetNext]struct{} ) defer kmove.maybeBeginMove(s.cl) @@ -1145,6 +1190,18 @@ func (s *source) handleReqResp(br *broker, req *fetchRequest, resp *kmsg.FetchRe ) continue } + if _, dup := seen[partOffset]; dup { + s.cl.cfg.logger.Log(LogLevelWarn, "broker returned a duplicate partition in a fetch response, ignoring the duplicate", + "broker", logID(s.nodeID), + "topic", topic, + "partition", partition, + ) + continue + } + if seen == nil { + seen = make(map[*cursorOffsetNext]struct{}, req.numOffsets) + } + seen[partOffset] = struct{}{} c := partOffset.from // If we are fetching from the replica already, Kafka replies with a -1 @@ -1496,7 +1553,13 @@ func ProcessFetchPartition(o ProcessFetchPartitionOpts, rp *kmsg.FetchResponseTo offset := int64(binary.BigEndian.Uint64(in)) length = int32(binary.BigEndian.Uint32(in[8:])) length += 12 // for the int64 offset we skipped and int32 length field itself - if len(in) < int(length) { + // length is read as a signed int32: a high-bit-set length field (or a + // near-MaxInt32 one, which overflows negative once we add 12) is + // negative and would slip past the truncation check below, panicking + // check()'s in[:length] with a negative bound. Treat a negative length + // as an untrustworthy/truncated batch and stop, matching the negative + // guards already in parseReadSize and the xerial decoder. + if length < 0 || len(in) < int(length) { break } @@ -1593,6 +1656,25 @@ func buildAborter(rp *kmsg.FetchResponseTopicPartition) aborter { for _, abort := range rp.AbortedTransactions { a[abort.ProducerID] = append(a[abort.ProducerID], abort.FirstOffset) } + // shouldAbortBatch and trackAbortedPID below both treat a[pid][0] as the + // smallest remaining aborted first offset for that producer: a batch is + // aborted once its FirstOffset reaches a[pid][0], and each abort marker + // pops a[pid][0]. The broker is NOT required to return a producer's aborted + // transactions sorted by first offset, though. Apache Kafka happens to (its + // transaction index is appended in last-offset order, which for one + // producer's strictly sequential transactions coincides with first-offset + // order), but Redpanda concatenates its newest in-memory aborted ranges + // (highest offsets) ahead of older on-disk snapshot ranges, so one + // producer's entries can arrive highest-first-offset first. With a[pid][0] + // not the smallest, a lower aborted transaction slips past the + // `FirstOffset < pidAborts[0]` guard and its records are surfaced to the + // application as committed - a read_committed violation. Sort each + // producer's first offsets ascending to restore the invariant the rest of + // the filtering relies on (the Java client makes the same guarantee with a + // first-offset-ordered priority queue). + for pid := range a { + slices.Sort(a[pid]) + } return a } @@ -1613,7 +1695,16 @@ func (a aborter) shouldAbortBatch(b *kmsg.RecordBatch) bool { } func (a aborter) trackAbortedPID(producerID int64) { - remaining := a[producerID][1:] + pidAborts := a[producerID] + if len(pidAborts) == 0 { + // Already exhausted for this PID. A well-formed response pops once + // per aborted transaction (one abort marker each), so this is only + // reachable from a buggy/hostile broker; reslicing a[producerID][1:] + // on the nil/empty slice would panic. Java removes the PID from a Set + // here, which is idempotent - mirror that. + return + } + remaining := pidAborts[1:] if len(remaining) == 0 { delete(a, producerID) } else { @@ -1691,6 +1782,24 @@ func (o *ProcessFetchPartitionOpts) processRecordBatch( uncompressedBytes := len(rawRecords) numRecords := int(batch.NumRecords) + // NumRecords is decoded straight off the wire and is therefore + // attacker/bug controlled. A negative count would panic the slice + // sizing below (ensureLen does s[:n], which panics for n<0); reject it + // as a corrupt batch, matching the Java client's InvalidRecordException + // guard (DefaultRecordBatch: "Found invalid record count"). A count + // larger than the available record bytes is likewise impossible for a + // well-formed batch - every record needs at least one byte - so clamp + // the up-front allocation to the byte count to keep a bogus huge count + // from driving a massive allocation. The true decodable count is + // recomputed by readRawRecordsInto, and the truncation defer below + // leaves the offset unadvanced whenever it disagrees with numRecords. + if numRecords < 0 { + fp.Err = fmt.Errorf("invalid record batch: negative record count %d", numRecords) + return 0, 0 + } + if numRecords > len(rawRecords) { + numRecords = len(rawRecords) + } var krecords []kmsg.Record var krecordsPool PoolKRecords pools(o.Pools).each(func(p Pool) bool { @@ -1746,8 +1855,10 @@ func (o *ProcessFetchPartitionOpts) processRecordBatch( } recordCtx := poolsCtx + var slabbed bool if o.shareAckSlab != nil && len(rrecords) > 0 { if slab := o.shareAckSlab(numRecords, &rrecords[0]); slab != nil { + slabbed = true parent := poolsCtx if parent == nil { parent = context.Background() @@ -1757,11 +1868,36 @@ func (o *ProcessFetchPartitionOpts) processRecordBatch( } var nkept int defer func() { - if p != nil && nkept > 0 { + if p == nil { + return + } + if nkept > 0 { p.n.Add(int64(nkept)) + return + } + // No record from this batch was kept: an aborted transaction's + // data batch, a control/marker batch, or a batch wholly below + // the requested offset. Nothing will ever call Recycle, so the + // recordPools put-back would never run and abort-heavy + // read_committed workloads would leak every pool Get. Release + // the pooled slices now, zeroing the written-then-discarded + // records like Recycle would have. Skip when a share-ack slab + // was created: the slab indexes rrecords by pointer, and putting + // the slice back would let the pool hand out memory the slab + // still references. + if slabbed { + return } + clear(p.recs) + p.release() }() + // A control batch ends at most one aborted transaction for its producer, + // so we pop the aborter at most once per batch. A well-formed control + // batch holds a single marker record; a buggy/hostile broker can pack many + // (and Java inspects only a control batch's first record), so without this + // guard a second abort marker would pop an already-empty aborter slice. + abortMarkerHandled := false for i := range krecords { record := &rrecords[i] recordToRecord( @@ -1777,12 +1913,13 @@ func (o *ProcessFetchPartitionOpts) processRecordBatch( nkept++ } - if abortBatch && record.Attrs.IsControl() { + if abortBatch && !abortMarkerHandled && record.Attrs.IsControl() { // A control record has a key and a value where the key // is int16 version and int16 type. Aborted records // have a type of 0. if key := record.Key; len(key) >= 4 && key[2] == 0 && key[3] == 0 { aborter.trackAbortedPID(batch.ProducerID) + abortMarkerHandled = true } } } @@ -1818,7 +1955,10 @@ out: for len(rawInner) > 17 { // magic at byte 17 length := int32(binary.BigEndian.Uint32(rawInner[8:])) length += 12 // offset and length fields - if len(rawInner) < int(length) { + // A negative length (high-bit-set length field, signed int32) would + // slip past the truncation check and panic rawInner[:length]; treat it + // as a truncated message set. See the record-batch loop's guard. + if length < 0 || len(rawInner) < int(length) { break } @@ -1931,7 +2071,10 @@ func (o *ProcessFetchPartitionOpts) processV0OuterMessage( for len(rawInner) > 17 { // magic at byte 17 length := int32(binary.BigEndian.Uint32(rawInner[8:])) length += 12 // offset and length fields - if len(rawInner) < int(length) { + // A negative length (high-bit-set length field, signed int32) would + // slip past the truncation check and panic rawInner[:length]; treat it + // as a truncated message set. See the record-batch loop's guard. + if length < 0 || len(rawInner) < int(length) { break // truncated batch } var m kmsg.MessageV0 @@ -2730,7 +2873,7 @@ func (s *source) removeShareCursor(c *shareCursor) { s.share.cursorsStart = 0 } s.share.mu.Unlock() - // We don't ned to wake the source to send this is a forgotten + // We don't need to wake the source to send this as a forgotten // partition, but it doesn't hurt. s.maybeShareConsume() } diff --git a/vendor/github.com/twmb/franz-go/pkg/kgo/topics_and_partitions.go b/vendor/github.com/twmb/franz-go/pkg/kgo/topics_and_partitions.go index 1fb5941996..0eefc9be3f 100644 --- a/vendor/github.com/twmb/franz-go/pkg/kgo/topics_and_partitions.go +++ b/vendor/github.com/twmb/franz-go/pkg/kgo/topics_and_partitions.go @@ -657,9 +657,13 @@ func (old *topicPartition) migrateCursorTo( //nolint:revive // old/new naming ma // leader epoch on the new broker to see if we experienced data loss // before we can use this cursor. // - // Metadata ensures that leaderEpoch is non-negative only if the broker - // supports KIP-320. - if new.leaderEpoch != -1 && old.cursor.lastConsumedEpoch >= 0 { + // We can only validate against a real (non-negative) leader epoch. + // Metadata reports a non-negative epoch only if the broker supports + // KIP-320, and the metadata merge keeps our last known real epoch + // rather than ever migrating us to the -1 "no leader" sentinel (see + // mergeTopicPartitions), so a negative epoch here means we genuinely + // have nothing to validate against and must skip validation. + if new.leaderEpoch >= 0 && old.cursor.lastConsumedEpoch >= 0 { // Since the cursor consumed messages, it is definitely usable. // We use it so that the epoch load can finish using it // properly. @@ -681,6 +685,32 @@ func (old *topicPartition) migrateCursorTo( //nolint:revive // old/new naming ma func (tp *topicPartition) migrateShareCursorTo(cl *Client, new *topicPartition) { c := tp.shareCursor + new.shareCursor = c + + // Relocating the cursor between sources races the share consumer's + // leave. leave waits for every share worker to exit (sc.workers == 0), + // then drains each source's cursors via closeShareSession, iterating a + // snapshot of the source list taken after that barrier. A relocation that + // removes the cursor from its old source before that source is drained, + // and lands it on an already-drained source (or on one created after the + // snapshot), leaves the cursor's pending acks on a source nothing will + // drain: sc.pendingAcks never returns to 0 (FlushAcks hangs) and the held + // records release only via the broker's acquisition-lock timeout. + // + // Register the migration as a share worker so leave's barrier waits for an + // in-flight migration before it snapshots and drains. This mirrors + // applyMovesBlocking, the CurrentLeader-hint sibling that already does + // this; the metadata-merge path (this function) was the only share-cursor + // relocation not covered by the barrier. If the consumer is already dying, + // incWorker returns false: skip the swap and leave the cursor on its + // current source, which closeShareSession then drains. new.shareCursor is + // assigned above either way, so the stored partition data is always valid. + sc := cl.consumer.s + if !sc.incWorker() { + return + } + defer sc.decWorker() + cl.sinksAndSourcesMu.Lock() sns := cl.sinksAndSources[new.leader] cl.sinksAndSourcesMu.Unlock() @@ -689,7 +719,6 @@ func (tp *topicPartition) migrateShareCursorTo(cl *Client, new *topicPartition) } c.source.Store(sns.source) sns.source.addShareCursor(c) - new.shareCursor = c } type kip951move struct { @@ -907,8 +936,27 @@ func (k *kip951move) doMove(cl *Client) { // same (this allows easier injection of failures in local testing). A // higher epoch can come from a concurrent metadata update that // actually performed the move first. - modifyP := func(d *topicPartitionsData, partition int32, td topicPartitionData) (old, new *topicPartition, modified bool) { + // + // The hint was staged from a produce/fetch response and we are applied + // asynchronously: between staging and now, the user can purge the + // topic and re-add it (a produce or AddConsumeTopics recreates the + // topic with zero partitions until metadata loads), so the partition + // index can be out of range, and even in range the object at this + // index can belong to the new topic incarnation rather than the one + // whose response produced the hint. The owns check pins object + // identity: the records/cursor pointer is preserved across legitimate + // metadata updates and prior moves (the migrate functions copy it into + // the new topicPartition), so a mismatch can only mean purge+re-add - + // skip, and let the live incarnation resolve its own leader via + // metadata. + modifyP := func(d *topicPartitionsData, partition int32, td topicPartitionData, owns func(*topicPartition) bool) (old, new *topicPartition, modified bool) { + if int(partition) < 0 || int(partition) >= len(d.partitions) { + return nil, nil, false + } old = d.partitions[partition] + if !owns(old) { + return nil, nil, false + } if old.leaderEpoch > td.leaderEpoch { return nil, nil, false } @@ -959,7 +1007,7 @@ func (k *kip951move) doMove(cl *Client) { if !ok { continue // perhaps concurrently purged } - old, new, modified := modifyP(lr.r, recBuf.partition, td) + old, new, modified := modifyP(lr.r, recBuf.partition, td, func(tp *topicPartition) bool { return tp.records == recBuf }) if modified { cl.cfg.logger.Log(LogLevelInfo, "moving producing partition due to kip-951 not_leader_for_partition", "topic", recBuf.topic, @@ -990,7 +1038,7 @@ func (k *kip951move) doMove(cl *Client) { if !ok { continue // perhaps concurrently purged } - old, new, modified := modifyP(lr.r, cursor.partition, td) + old, new, modified := modifyP(lr.r, cursor.partition, td, func(tp *topicPartition) bool { return tp.cursor == cursor }) if modified { cl.cfg.logger.Log(LogLevelInfo, "moving consuming partition due to kip-951 not_leader_for_partition", "topic", cursor.topic, diff --git a/vendor/github.com/twmb/franz-go/pkg/kgo/txn.go b/vendor/github.com/twmb/franz-go/pkg/kgo/txn.go index 289777e25b..b08f9ff603 100644 --- a/vendor/github.com/twmb/franz-go/pkg/kgo/txn.go +++ b/vendor/github.com/twmb/franz-go/pkg/kgo/txn.go @@ -27,10 +27,6 @@ const ( // GroupTransactSession abstracts away the proper way to begin and end a // transaction when consuming in a group, modifying records, and producing // (EOS). -// -// If you are running Kafka 2.5+, it is strongly recommended that you also use -// RequireStableFetchOffsets. See that config option's documentation for more -// details. type GroupTransactSession struct { cl *Client @@ -65,9 +61,9 @@ type GroupTransactSession struct { // proper rebalance timeout, this single request will not fail and the commit // will succeed properly. // -// If this client detects you are talking to a pre-2.5 cluster, OR if you have -// not enabled RequireStableFetchOffsets, the client will sleep for 200ms after -// a successful commit to allow Kafka's txn markers to propagate. This is not +// If this client detects you are talking to a pre-2.5 cluster (one too old +// for KIP-447 stable fetch offsets), the client sleeps for 500ms after a +// successful commit to allow Kafka's txn markers to propagate. This is not // foolproof in the event of some extremely unlikely communication patterns and // **potentially** could allow duplicates. See this repo's transaction's doc // for more details. @@ -436,10 +432,11 @@ func (s *GroupTransactSession) End(ctx context.Context, commit TransactionEndTry // We have a few potential retryable errors from EndTransaction. // OperationNotAttempted will be returned at most once. // - // UnknownServerError should not be returned, but some brokers do: - // technically this is fatal, but there is no downside to retrying - // (even retrying a commit) and seeing if we are successful or if we - // get a better error. + // UnknownServerError should not be returned, but some brokers do + // (e.g. Redpanda in certain versions). It leaves the commit/abort + // unconfirmed: the broker may or may not have completed it. We + // retry as an abort (see the arm below) rather than reporting a + // commit we cannot confirm. var tries int retry: endTxnErr := s.cl.EndTransaction(ctx, TransactionEndTry(willTryCommit)) @@ -457,13 +454,20 @@ retry: goto retry case errors.Is(endTxnErr, kerr.UnknownServerError): - s.cl.cfg.logger.Log(LogLevelInfo, "end transaction with commit unknown server error; retrying") - after := time.NewTimer(s.cl.cfg.retryBackoff(tries)) - select { - case <-after.C: // context canceled; we will see when we retry - case <-s.cl.ctx.Done(): - after.Stop() - } + // We must downgrade to an abort exactly like the two arms + // above. EndTransaction already consumed inTxn on the + // erroring call, so re-calling it returns nil at its !inTxn + // guard without issuing another EndTxn: we cannot actually + // re-commit. If willTryCommit stayed true, that manufactured + // nil would fall into the "willTryCommit && endTxnErr == nil" + // success tail below and report a committed transaction, + // advancing the consumer offsets to postcommit even though the + // broker's UNKNOWN_SERVER_ERROR may have aborted it: a silent + // EOS data loss. Retrying as an abort reports not-committed and + // rewinds to the last committed offsets, so the caller + // reprocesses (at-least-once). + s.cl.cfg.logger.Log(LogLevelInfo, "end transaction returned an unknown server error; the commit is unconfirmed, retrying as abort to avoid reporting a false commit") + willTryCommit = false goto retry } } @@ -525,6 +529,7 @@ func (cl *Client) BeginTransaction() error { } cl.producer.inTxn = true + cl.producer.producedInTxn.Store(false) if !cl.producer.tx890p2.Load() && cl.supportsKIP890p2() { cl.producer.tx890p2.Store(true) } @@ -588,7 +593,7 @@ func (cl *Client) EndAndBeginTransaction( return cl.EndTransaction(ctx, commit) } -// AbortBufferedRecords fails all unflushed records with ErrAborted and waits +// AbortBufferedRecords fails all unflushed records with ErrAborting and waits // for there to be no buffered records. // // This accepts a context to quit the wait early, but quitting the wait may @@ -625,7 +630,7 @@ func (cl *Client) AbortBufferedRecords(ctx context.Context) error { return cl.Flush(ctx) } -// UnsafeAbortBufferedRecords fails all unflushed records with ErrAborted and +// UnsafeAbortBufferedRecords fails all unflushed records with ErrAborting and // waits for there to be no buffered records. This function does NOT wait for // any inflight produce requests to finish, meaning topics in the client may be // in an invalid state and producing to an invalid-state topic may cause the @@ -664,8 +669,9 @@ func (cl *Client) UnsafeAbortBufferedRecords() { // It may be possible for the client to recover in a new transaction via // BeginTransaction if an error is returned from this function: // -// - Before Kafka 4.0, InvalidProducerIDMapping and InvalidProducerEpoch -// are recoverable +// - When transactions are not running under KIP-890 part 2 (the cluster's +// transaction.version feature is below 2), InvalidProducerIDMapping and +// InvalidProducerEpoch are recoverable // - UnknownProducerID is recoverable for Kafka 2.5+ // - TransactionAbortable is always recoverable (after aborting) // @@ -692,12 +698,18 @@ func (cl *Client) EndTransaction(ctx context.Context, commit TransactionEndTry) // issues AddOffsetsToTxn, which internally adds a __consumer_offsets // partition to the transaction. Thus, if we added offsets, then we // also produced. - var anyAdded bool - if g := cl.consumer.g; g != nil { + var ( + anyAdded bool + addedSwapped []*recBuf // every addedToTxn we consume, restored if the commit is not attempted + offsetsWereAdded bool + ) + g := cl.consumer.g + if g != nil { // We do not lock because we expect commitTransactionOffsets to // be called *before* ending a transaction. if g.offsetsAddedToTxn { g.offsetsAddedToTxn = false + offsetsWereAdded = true anyAdded = true } } else { @@ -708,7 +720,10 @@ func (cl *Client) EndTransaction(ctx context.Context, commit TransactionEndTry) // addedToTxn to false outside of any mutex. for _, parts := range cl.producer.topics.load() { for _, part := range parts.load().partitions { - anyAdded = part.records.addedToTxn.Swap(false) || anyAdded + if part.records.addedToTxn.Swap(false) { + addedSwapped = append(addedSwapped, part.records) + anyAdded = true + } } } @@ -717,13 +732,52 @@ func (cl *Client) EndTransaction(ctx context.Context, commit TransactionEndTry) // Note that anyAdded is true if the producer ID was failed, meaning we will // get to the potential recovery logic below if necessary. if !anyAdded { - cl.cfg.logger.Log(LogLevelDebug, "no records were produced during the commit; thus no transaction was began; ending without doing anything") - return nil + // Under KIP-890 part 2, produce requests implicitly add their + // partition to the transaction on the broker BEFORE the data + // append; the registration is durable even if the append then + // fails, and we mark a partition added client-side only on a + // SUCCESSFUL produce response. If produces were attempted but + // none succeeded, the broker can have an ongoing transaction + // with registered partitions that only the transaction timeout + // would clear, and the next transaction's produces (same + // epoch, since no EndTxn bumped it) would silently join that + // ongoing transaction. Aborting is always legal under 890p2 + // (aborting an empty transaction succeeds and bumps the + // epoch), so abort regardless of what the caller asked: with + // zero successful produces and no committed offsets, there is + // nothing to commit anyway. + if !cl.producer.tx890p2.Load() || !cl.producer.producedInTxn.Load() { + cl.cfg.logger.Log(LogLevelDebug, "no records were produced during the commit; thus no transaction was began; ending without doing anything") + return nil + } + cl.cfg.logger.Log(LogLevelInfo, "no produce succeeded in this transaction but produces were attempted; issuing an abort to clear any server-side partition registrations", + "transactional_id", *cl.cfg.txnID, + ) + commit = TryAbort } id, epoch, err := cl.producerID(ctx2fn(ctx)) if err != nil { if commit { + // We are NOT attempting the commit: restore everything this + // call consumed (inTxn, addedToTxn, offsetsAddedToTxn). We + // document that the caller should retry with TryAbort, and + // that retry must still see the transaction state to issue + // the EndTxn abort; consuming the state here would turn the + // retry into a silent no-op that leaves the broker-side + // transaction ongoing (stalling read_committed consumers on + // the LSO) until the transaction timeout aborts it. + // producingTxn deliberately stays false: produces between + // the failed commit and the abort retry fail fast with + // errNotInTransaction rather than buffering against a + // failed producer ID. + for _, rb := range addedSwapped { + rb.addedToTxn.Store(true) + } + if offsetsWereAdded { + g.offsetsAddedToTxn = true + } + cl.producer.inTxn = true return kerr.OperationNotAttempted } @@ -847,11 +901,37 @@ func (cl *Client) maybeRecoverProducerID(ctx context.Context) (necessary, did bo return true, false, err } + if ke.Retriable { + // The stored failure is a retriable broker code, e.g. + // COORDINATOR_LOAD_IN_PROGRESS or NOT_COORDINATOR that + // outlived its internal retries, or CONCURRENT_TRANSACTIONS + // that outlived doWithConcurrentTransactions. These are + // transient load failures, not a fatal producer state: flag + // the ID for reload exactly like the transport-level failures + // above, rather than reporting "fatal, unrecoverable" for a + // condition that clears on its own. + cl.producer.id.Store(&producerID{ + id: id, + epoch: epoch, + err: errReloadProducerID, + }) + return true, true, nil + } + var recoverable bool - if cl.supportsKeyVersion(int16(kmsg.EndTxn), 5) { - // As of KIP-890 / Kafka 4.0, InvalidProducerIDMapping and + if cl.producer.tx890p2.Load() { + // Under KIP-890 part 2 (transaction.version=2 in effect for + // this client's transactions), InvalidProducerIDMapping and // InvalidProducerEpoch are NOT recoverable. Only // UnknownProducerID and TransactionAbortable are. + // + // We gate on the mode our transactions actually ran in, NOT + // on broker-advertised EndTxn versions: a 4.0+ broker + // advertises EndTxn v5 even while the cluster's + // transaction.version is still 0 or 1, and under those the + // cluster still operates the pre-890p2 semantics where the + // KIP-360/KIP-588 re-init below is the designed recovery + // (e.g. after a transaction-timeout abort bumped our epoch). recoverable = errors.Is(ke, kerr.UnknownProducerID) || errors.Is(ke, kerr.TransactionAbortable) } else { kip360 := cl.producer.idVersion >= 3 && (errors.Is(ke, kerr.UnknownProducerID) || errors.Is(ke, kerr.InvalidProducerIDMapping)) @@ -876,6 +956,31 @@ func (cl *Client) maybeRecoverProducerID(ctx context.Context) (necessary, did bo // If a transaction is begun too quickly after finishing an old transaction, // Kafka may still be finalizing its commit / abort and will return a // concurrent transactions error. We handle that by retrying for a bit. +// +// Constraints any change to EOS-under-coordinator-churn (here and in +// maybeRecoverProducerID / EndTransaction) must preserve — established by the +// txn-churn audit: +// +// 1. This wrapper / coordinator-retry division stays: the coordinator +// wrapper retries only coordinator-move codes; CONCURRENT_TRANSACTIONS +// loops belong to callers, ctx-bounded, because CT can legitimately last +// as long as a marker drain. Route CT recovery through this loop, not a +// second one. +// 2. anyAdded gating stays for TV1 — EndTxn on an EMPTY TV1 transaction is +// INVALID_TXN_STATE. The TV2 forced-abort-when-every-produce-failed path +// must stay TV2-only and attempted-only; idle Begin/End cycles stay +// wireless. +// 3. Producer-fenced means dead: no fix may add recovery for PRODUCER_FENCED +// from coordinator paths. Only the timeout-abort IPE-on-produce path is +// recoverable, and only under TV1 semantics. +// +// Two design-sized items remain deliberately NOT taken (would need their own +// round; until then docs + AbortingFirstErrPromise carry them): (a) +// commit-after-failed-produce — kgo commits whatever succeeded if the caller +// asks, unlike Java's abortable-state block, so changing it means tracking +// per-txn batch failures and changing End's contract; (b) TV2 feature +// downgrade mid-session (transaction.version 2=>1 while a client lives) — +// Java's per-EndTxn re-check is the reference shape if it ever bites. func (cl *Client) doWithConcurrentTransactions(ctx context.Context, name string, fn func() error) error { start := time.Now() tries := 0 @@ -885,20 +990,21 @@ start: err := fn() if errors.Is(err, kerr.ConcurrentTransactions) { // The longer we are stalled, the more we enforce a minimum - // backoff. + // backoff. Checks are ordered longest first; switch takes the + // first true case. since := time.Since(start) switch { - case since > time.Second: - if backoff < 200*time.Millisecond { - backoff = 200 * time.Millisecond + case since > 5*time.Second: + if backoff < time.Second { + backoff = time.Second } case since > 5*time.Second/2: if backoff < 500*time.Millisecond { backoff = 500 * time.Millisecond } - case since > 5*time.Second: - if backoff < time.Second { - backoff = time.Second + case since > time.Second: + if backoff < 200*time.Millisecond { + backoff = 200 * time.Millisecond } } @@ -1069,31 +1175,12 @@ func (g *groupConsumer) commitTxn(ctx context.Context, tx890p2 bool, req *kmsg.T } priorDone := g.commitDone - - // Unlike the non-txn consumer, we use the group context for - // transaction offset committing. We want to quit when the group is - // left, and we are not committing when leaving. We rely on proper - // usage of the GroupTransactSession API to issue commits, so there is - // no reason not to use the group context here. - commitCtx, commitCancel := context.WithCancel(g.ctx) // enable ours to be canceled and waited for commitDone := make(chan struct{}) - g.commitDone = commitDone - if ctx.Done() != nil { - go func() { - select { - case <-ctx.Done(): - commitCancel() - case <-commitCtx.Done(): - } - }() - } - go func() { defer close(commitDone) // allow future commits to continue when we are done - defer commitCancel() - if priorDone != nil { // wait for any prior request to finish + if priorDone != nil { // wait for any prior request to finish // Same as commit(): we must NOT cancel the prior commit // because canceling kills the TCP connection, and our // subsequent request on a new connection can be processed @@ -1108,7 +1195,14 @@ func (g *groupConsumer) commitTxn(ctx context.Context, tx890p2 bool, req *kmsg.T g.cl.cfg.logger.Log(LogLevelDebug, "issuing txn offset commit", "uncommitted", req) start := time.Now() - ctx := ctx // capture a local ctx variable; do not use the shared one that is concurrently read above + // The request rides the caller's context (the End context), + // not the group context: a transactional offset commit must + // not be canceled by group teardown midway, because canceling + // an in-flight request kills the connection and a replacement + // commit on a new connection can be reordered behind the + // canceled one by the broker. End documents that canceling + // ITS context risks an invalid state. + ctx := ctx if !tx890p2 { ctx = context.WithValue(ctx, ctxPinReq, &pinReq{pinMax: true, max: 4}) // v5 is only supported with KIP-890 part 2 } diff --git a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace/clienttrace.go b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace/clienttrace.go index 7b62464916..2ad6c7cc88 100644 --- a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace/clienttrace.go +++ b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace/clienttrace.go @@ -63,8 +63,20 @@ func (fn clientTraceOptionFunc) apply(c *clientTracer) { } // WithoutSubSpans will modify the httptrace.ClientTrace to only collect data -// as Events and Attributes on a span found in the context. By default +// as Events and Attributes on a span found in the context. By default // sub-spans will be generated. +// +// This option is recommended for services that make a large number of +// outbound HTTP requests per incoming request (e.g., API gateways, +// fan-out proxies, or GraphQL servers). Each outbound request creates +// up to 7 sub-spans (http.getconn, http.dns, http.connect, http.tls, +// http.headers, http.send, http.receive) as separate heap-allocated +// trace.Span objects. In high fan-out services this multiplied span +// volume can overwhelm the span processor queue and increase GC +// pressure, resulting in elevated and continuously growing memory usage. +// Using WithoutSubSpans replaces those spans with lightweight events on +// the parent span, preserving the diagnostic information at a fraction +// of the cost. func WithoutSubSpans() ClientTraceOption { return clientTraceOptionFunc(func(ct *clientTracer) { ct.useSpans = false @@ -189,10 +201,11 @@ func NewClientTrace(ctx context.Context, opts ...ClientTraceOption) *httptrace.C } func (ct *clientTracer) start(hook, spanName string, attrs ...attribute.KeyValue) { + if ct.root == nil { + ct.root = trace.SpanFromContext(ct.Context) + } + if !ct.useSpans { - if ct.root == nil { - ct.root = trace.SpanFromContext(ct.Context) - } ct.root.AddEvent(hook+".start", trace.WithAttributes(attrs...)) return } @@ -201,11 +214,7 @@ func (ct *clientTracer) start(hook, spanName string, attrs ...attribute.KeyValue defer ct.mtx.Unlock() if hookCtx, found := ct.activeHooks[hook]; !found { - var sp trace.Span - ct.activeHooks[hook], sp = ct.tr.Start(ct.getParentContext(hook), spanName, trace.WithAttributes(attrs...), trace.WithSpanKind(trace.SpanKindClient)) - if ct.root == nil { - ct.root = sp - } + ct.activeHooks[hook], _ = ct.tr.Start(ct.getParentContext(hook), spanName, trace.WithAttributes(attrs...), trace.WithSpanKind(trace.SpanKindClient)) } else { // end was called before start finished, add the start attributes and end the span here span := trace.SpanFromContext(hookCtx) @@ -306,14 +315,16 @@ func (ct *clientTracer) dnsDone(info httptrace.DNSDoneInfo) { } func (ct *clientTracer) connectStart(network, addr string) { - ct.start("http.connect."+addr, "http.connect", + ct.start( + "http.connect."+addr, "http.connect", HTTPRemoteAddr.String(addr), HTTPConnectionStartNetwork.String(network), ) } func (ct *clientTracer) connectDone(network, addr string, err error) { - ct.end("http.connect."+addr, err, + ct.end( + "http.connect."+addr, err, HTTPConnectionDoneAddr.String(addr), HTTPConnectionDoneNetwork.String(network), ) diff --git a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace/internal/semconv/client.go b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace/internal/semconv/client.go index 353ed2ff53..d88cd079d1 100644 --- a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace/internal/semconv/client.go +++ b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace/internal/semconv/client.go @@ -12,6 +12,7 @@ import ( "context" "fmt" "net/http" + "reflect" "slices" "strconv" "strings" @@ -19,8 +20,8 @@ import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/metric" - "go.opentelemetry.io/otel/semconv/v1.40.0" - "go.opentelemetry.io/otel/semconv/v1.40.0/httpconv" + "go.opentelemetry.io/otel/semconv/v1.41.0" + "go.opentelemetry.io/otel/semconv/v1.41.0/httpconv" ) type HTTPClient struct { @@ -166,7 +167,7 @@ func (n HTTPClient) ResponseTraceAttrs(resp *http.Response) []attribute.KeyValue func (n HTTPClient) method(method string) (attribute.KeyValue, attribute.KeyValue) { if method == "" { - return semconv.HTTPRequestMethodGet, attribute.KeyValue{} + return semconv.HTTPRequestMethodOther, attribute.KeyValue{} } if attr, ok := methodLookup[method]; ok { return attr, attribute.KeyValue{} @@ -176,7 +177,7 @@ func (n HTTPClient) method(method string) (attribute.KeyValue, attribute.KeyValu if attr, ok := methodLookup[strings.ToUpper(method)]; ok { return attr, orig } - return semconv.HTTPRequestMethodGet, orig + return semconv.HTTPRequestMethodOther, orig } func (n HTTPClient) MetricAttributes(req *http.Request, statusCode int, additionalAttributes []attribute.KeyValue) []attribute.KeyValue { @@ -249,6 +250,9 @@ func (o MetricOpts) AddOptions() metric.AddOption { func (n HTTPClient) MetricOptions(ma MetricAttributes) MetricOpts { attributes := n.MetricAttributes(ma.Req, ma.StatusCode, ma.AdditionalAttributes) + if ma.StatusCode == 0 && ma.Err != nil { + attributes = append(attributes, n.ErrorType(ma.Err)) + } set := metric.WithAttributeSet(attribute.NewSet(attributes...)) return MetricOpts{ @@ -257,6 +261,39 @@ func (n HTTPClient) MetricOptions(ma MetricAttributes) MetricOpts { } } +// ErrorType returns an error.type attribute for the given error. The otelhttp +// Transport calls the underlying RoundTripper directly, so transport failures +// arrive as *net.OpError (connection refused, timeout, etc.) rather than +// *url.Error (which http.Client.Do adds above the Transport layer). This +// function intentionally does not unwrap further: reporting a single concrete +// type per failure keeps attribute cardinality bounded, as required by the +// OTel spec (error.type SHOULD have low cardinality). Callers that need +// finer-grained error distinctions should inspect the error themselves. +func (n HTTPClient) ErrorType(err error) attribute.KeyValue { + t := reflect.TypeOf(err) + if t == nil { + return semconv.ErrorTypeOther + } + if t.Kind() == reflect.Ptr { + t = t.Elem() + } + var value string + if t.PkgPath() == "" || t.Name() == "" { + // t.PkgPath() == "" covers builtin and unnamed types. + // t.Name() == "" covers anonymous struct types that implement error, + // which are uncommon but possible. Fall back to t.String() for both. + value = t.String() + } else { + value = fmt.Sprintf("%s.%s", t.PkgPath(), t.Name()) + } + + if value == "" { + return semconv.ErrorTypeOther + } + + return semconv.ErrorTypeKey.String(value) +} + func (n HTTPClient) RecordMetrics(ctx context.Context, md MetricData, opts MetricOpts) { recordOpts := metricRecordOptionPool.Get().(*[]metric.RecordOption) defer func() { diff --git a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace/internal/semconv/server.go b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace/internal/semconv/server.go index 332057bafc..a50124078d 100644 --- a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace/internal/semconv/server.go +++ b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace/internal/semconv/server.go @@ -20,8 +20,8 @@ import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/metric" - "go.opentelemetry.io/otel/semconv/v1.40.0" - "go.opentelemetry.io/otel/semconv/v1.40.0/httpconv" + "go.opentelemetry.io/otel/semconv/v1.41.0" + "go.opentelemetry.io/otel/semconv/v1.41.0/httpconv" ) type RequestTraceAttrsOpts struct { @@ -243,6 +243,7 @@ type MetricAttributes struct { StatusCode int Route string AdditionalAttributes []attribute.KeyValue + Err error } type MetricData struct { @@ -250,13 +251,11 @@ type MetricData struct { RequestDuration time.Duration } -var ( - metricRecordOptionPool = &sync.Pool{ - New: func() any { - return &[]metric.RecordOption{} - }, - } -) +var metricRecordOptionPool = &sync.Pool{ + New: func() any { + return &[]metric.RecordOption{} + }, +} func (n HTTPServer) RecordMetrics(ctx context.Context, md ServerMetricData) { attributes := n.MetricAttributes(md.ServerName, md.Req, md.StatusCode, md.Route, md.AdditionalAttributes) @@ -270,9 +269,27 @@ func (n HTTPServer) RecordMetrics(ctx context.Context, md ServerMetricData) { metricRecordOptionPool.Put(recordOpts) } +// SpanName returns the span name for an HTTP request following the +// OpenTelemetry HTTP semantic conventions. +// It returns "{method} {route}" when the request has a pattern, +// or just "{method}" when no route is available. +// Non-standard HTTP methods are replaced by "HTTP". +func (n HTTPServer) SpanName(r *http.Request) string { + method := strings.ToUpper(r.Method) + if _, ok := methodLookup[method]; !ok { + method = "HTTP" + } + + route := httpRoute(r.Pattern) + if route != "" { + return method + " " + route + } + return method +} + func (n HTTPServer) method(method string) (attribute.KeyValue, attribute.KeyValue) { if method == "" { - return semconv.HTTPRequestMethodGet, attribute.KeyValue{} + return semconv.HTTPRequestMethodOther, attribute.KeyValue{} } if attr, ok := methodLookup[method]; ok { return attr, attribute.KeyValue{} @@ -282,7 +299,7 @@ func (n HTTPServer) method(method string) (attribute.KeyValue, attribute.KeyValu if attr, ok := methodLookup[strings.ToUpper(method)]; ok { return attr, orig } - return semconv.HTTPRequestMethodGet, orig + return semconv.HTTPRequestMethodOther, orig } func (n HTTPServer) scheme(https bool) attribute.KeyValue { //nolint:revive // ignore linter diff --git a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace/internal/semconv/util.go b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace/internal/semconv/util.go index b2dc8548f4..88b67003cc 100644 --- a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace/internal/semconv/util.go +++ b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace/internal/semconv/util.go @@ -15,7 +15,7 @@ import ( "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" - semconvNew "go.opentelemetry.io/otel/semconv/v1.40.0" + semconvNew "go.opentelemetry.io/otel/semconv/v1.41.0" ) // SplitHostPort splits a network address hostport of the form "host", diff --git a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace/version.go b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace/version.go index 5473bb8841..f38ce8da37 100644 --- a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace/version.go +++ b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace/version.go @@ -4,4 +4,4 @@ package otelhttptrace // import "go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace" // Version is the current release version of the httptrace instrumentation. -const Version = "0.68.0" +const Version = "0.69.0" diff --git a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/handler.go b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/handler.go index a269fce0f7..204588b849 100644 --- a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/handler.go +++ b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/handler.go @@ -35,10 +35,6 @@ type middleware struct { semconv semconv.HTTPServer } -func defaultHandlerFormatter(operation string, _ *http.Request) string { - return operation -} - // NewHandler wraps the passed handler in a span named after the operation and // enriches it with metrics. func NewHandler(handler http.Handler, operation string, opts ...Option) http.Handler { @@ -55,12 +51,17 @@ func NewMiddleware(operation string, opts ...Option) func(http.Handler) http.Han defaultOpts := []Option{ WithSpanOptions(trace.WithSpanKind(trace.SpanKindServer)), - WithSpanNameFormatter(defaultHandlerFormatter), } c := newConfig(append(defaultOpts, opts...)...) h.configure(c) + if h.spanNameFormatter == nil { + h.spanNameFormatter = func(_ string, r *http.Request) string { + return h.semconv.SpanName(r) + } + } + return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { h.serveHTTP(w, r, next) @@ -138,7 +139,13 @@ func (h *middleware) serveHTTP(w http.ResponseWriter, r *http.Request, next http // ReadCloser fulfills a certain interface and it is indeed nil or NoBody. bw := request.NewBodyWrapper(r.Body, readRecordFunc) if r.Body != nil && r.Body != http.NoBody { + origReq := r + prevBody := r.Body r.Body = bw + + // Restore the original body after the request is processed to avoid issues + // with extra wrapper since `http/server.go` later checks type of `r.Body`. + defer func() { origReq.Body = prevBody }() } writeRecordFunc := func(int64) {} diff --git a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/client.go b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/client.go index 1398d85c2e..0241b08e85 100644 --- a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/client.go +++ b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/client.go @@ -12,6 +12,7 @@ import ( "context" "fmt" "net/http" + "reflect" "slices" "strconv" "strings" @@ -19,8 +20,8 @@ import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/metric" - "go.opentelemetry.io/otel/semconv/v1.40.0" - "go.opentelemetry.io/otel/semconv/v1.40.0/httpconv" + "go.opentelemetry.io/otel/semconv/v1.41.0" + "go.opentelemetry.io/otel/semconv/v1.41.0/httpconv" ) type HTTPClient struct { @@ -166,7 +167,7 @@ func (n HTTPClient) ResponseTraceAttrs(resp *http.Response) []attribute.KeyValue func (n HTTPClient) method(method string) (attribute.KeyValue, attribute.KeyValue) { if method == "" { - return semconv.HTTPRequestMethodGet, attribute.KeyValue{} + return semconv.HTTPRequestMethodOther, attribute.KeyValue{} } if attr, ok := methodLookup[method]; ok { return attr, attribute.KeyValue{} @@ -176,7 +177,7 @@ func (n HTTPClient) method(method string) (attribute.KeyValue, attribute.KeyValu if attr, ok := methodLookup[strings.ToUpper(method)]; ok { return attr, orig } - return semconv.HTTPRequestMethodGet, orig + return semconv.HTTPRequestMethodOther, orig } func (n HTTPClient) MetricAttributes(req *http.Request, statusCode int, additionalAttributes []attribute.KeyValue) []attribute.KeyValue { @@ -249,6 +250,9 @@ func (o MetricOpts) AddOptions() metric.AddOption { func (n HTTPClient) MetricOptions(ma MetricAttributes) MetricOpts { attributes := n.MetricAttributes(ma.Req, ma.StatusCode, ma.AdditionalAttributes) + if ma.StatusCode == 0 && ma.Err != nil { + attributes = append(attributes, n.ErrorType(ma.Err)) + } set := metric.WithAttributeSet(attribute.NewSet(attributes...)) return MetricOpts{ @@ -257,6 +261,39 @@ func (n HTTPClient) MetricOptions(ma MetricAttributes) MetricOpts { } } +// ErrorType returns an error.type attribute for the given error. The otelhttp +// Transport calls the underlying RoundTripper directly, so transport failures +// arrive as *net.OpError (connection refused, timeout, etc.) rather than +// *url.Error (which http.Client.Do adds above the Transport layer). This +// function intentionally does not unwrap further: reporting a single concrete +// type per failure keeps attribute cardinality bounded, as required by the +// OTel spec (error.type SHOULD have low cardinality). Callers that need +// finer-grained error distinctions should inspect the error themselves. +func (n HTTPClient) ErrorType(err error) attribute.KeyValue { + t := reflect.TypeOf(err) + if t == nil { + return semconv.ErrorTypeOther + } + if t.Kind() == reflect.Ptr { + t = t.Elem() + } + var value string + if t.PkgPath() == "" || t.Name() == "" { + // t.PkgPath() == "" covers builtin and unnamed types. + // t.Name() == "" covers anonymous struct types that implement error, + // which are uncommon but possible. Fall back to t.String() for both. + value = t.String() + } else { + value = fmt.Sprintf("%s.%s", t.PkgPath(), t.Name()) + } + + if value == "" { + return semconv.ErrorTypeOther + } + + return semconv.ErrorTypeKey.String(value) +} + func (n HTTPClient) RecordMetrics(ctx context.Context, md MetricData, opts MetricOpts) { recordOpts := metricRecordOptionPool.Get().(*[]metric.RecordOption) defer func() { diff --git a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/server.go b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/server.go index 83c6ae2465..8e0430863d 100644 --- a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/server.go +++ b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/server.go @@ -20,8 +20,8 @@ import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/metric" - "go.opentelemetry.io/otel/semconv/v1.40.0" - "go.opentelemetry.io/otel/semconv/v1.40.0/httpconv" + "go.opentelemetry.io/otel/semconv/v1.41.0" + "go.opentelemetry.io/otel/semconv/v1.41.0/httpconv" ) type RequestTraceAttrsOpts struct { @@ -243,6 +243,7 @@ type MetricAttributes struct { StatusCode int Route string AdditionalAttributes []attribute.KeyValue + Err error } type MetricData struct { @@ -250,13 +251,11 @@ type MetricData struct { RequestDuration time.Duration } -var ( - metricRecordOptionPool = &sync.Pool{ - New: func() any { - return &[]metric.RecordOption{} - }, - } -) +var metricRecordOptionPool = &sync.Pool{ + New: func() any { + return &[]metric.RecordOption{} + }, +} func (n HTTPServer) RecordMetrics(ctx context.Context, md ServerMetricData) { attributes := n.MetricAttributes(md.ServerName, md.Req, md.StatusCode, md.Route, md.AdditionalAttributes) @@ -270,9 +269,27 @@ func (n HTTPServer) RecordMetrics(ctx context.Context, md ServerMetricData) { metricRecordOptionPool.Put(recordOpts) } +// SpanName returns the span name for an HTTP request following the +// OpenTelemetry HTTP semantic conventions. +// It returns "{method} {route}" when the request has a pattern, +// or just "{method}" when no route is available. +// Non-standard HTTP methods are replaced by "HTTP". +func (n HTTPServer) SpanName(r *http.Request) string { + method := strings.ToUpper(r.Method) + if _, ok := methodLookup[method]; !ok { + method = "HTTP" + } + + route := httpRoute(r.Pattern) + if route != "" { + return method + " " + route + } + return method +} + func (n HTTPServer) method(method string) (attribute.KeyValue, attribute.KeyValue) { if method == "" { - return semconv.HTTPRequestMethodGet, attribute.KeyValue{} + return semconv.HTTPRequestMethodOther, attribute.KeyValue{} } if attr, ok := methodLookup[method]; ok { return attr, attribute.KeyValue{} @@ -282,7 +299,7 @@ func (n HTTPServer) method(method string) (attribute.KeyValue, attribute.KeyValu if attr, ok := methodLookup[strings.ToUpper(method)]; ok { return attr, orig } - return semconv.HTTPRequestMethodGet, orig + return semconv.HTTPRequestMethodOther, orig } func (n HTTPServer) scheme(https bool) attribute.KeyValue { //nolint:revive // ignore linter diff --git a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/util.go b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/util.go index 2eab2ecabd..d6842ed2ab 100644 --- a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/util.go +++ b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/util.go @@ -15,7 +15,7 @@ import ( "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" - semconvNew "go.opentelemetry.io/otel/semconv/v1.40.0" + semconvNew "go.opentelemetry.io/otel/semconv/v1.41.0" ) // SplitHostPort splits a network address hostport of the form "host", diff --git a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/transport.go b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/transport.go index d8d204d1f8..b15895cee2 100644 --- a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/transport.go +++ b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/transport.go @@ -15,7 +15,7 @@ import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/propagation" - otelsemconv "go.opentelemetry.io/otel/semconv/v1.40.0" + otelsemconv "go.opentelemetry.io/otel/semconv/v1.41.0" "go.opentelemetry.io/otel/trace" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/request" @@ -161,6 +161,7 @@ func (t *Transport) RoundTrip(r *http.Request) (*http.Response, error) { t.semconv.MetricOptions(semconv.MetricAttributes{ Req: r, StatusCode: statusCode, + Err: err, AdditionalAttributes: append(labeler.Get(), t.metricAttributesFromRequest(r)...), }), ) diff --git a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/version.go b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/version.go index 835ec5aa7e..2feb885000 100644 --- a/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/version.go +++ b/vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/version.go @@ -4,4 +4,4 @@ package otelhttp // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" // Version is the current release version of the otelhttp instrumentation. -const Version = "0.68.0" +const Version = "0.69.0" diff --git a/vendor/go.opentelemetry.io/otel/.golangci.yml b/vendor/go.opentelemetry.io/otel/.golangci.yml index db1f55101c..645c7e6af1 100644 --- a/vendor/go.opentelemetry.io/otel/.golangci.yml +++ b/vendor/go.opentelemetry.io/otel/.golangci.yml @@ -96,9 +96,9 @@ linters: - "!**/exporters/zipkin/**" deny: - pkg: go.opentelemetry.io/otel/semconv - desc: "Use go.opentelemetry.io/otel/semconv/v1.40.0 instead. If a newer semconv version has been released, update the depguard rule." + desc: "Use go.opentelemetry.io/otel/semconv/v1.41.0 instead. If a newer semconv version has been released, update the depguard rule." allow: - - go.opentelemetry.io/otel/semconv/v1.40.0 + - go.opentelemetry.io/otel/semconv/v1.41.0 gocritic: disabled-checks: - appendAssign @@ -134,13 +134,16 @@ linters: strconcat: true revive: confidence: 0.01 + enable-all-rules: false + enable-default-rules: true + max-open-files: 2048 rules: - name: blank-imports - name: bool-literal-in-expr - name: constant-logical-expr - name: context-as-argument arguments: - - allowTypesBefore: '*testing.T' + - allow-types-before: '*testing.T' disabled: true - name: context-keys-type - name: deep-exit @@ -152,7 +155,7 @@ linters: - name: duplicated-imports - name: early-return arguments: - - preserveScope + - preserve-scope - name: empty-block - name: empty-lines - name: error-naming @@ -161,7 +164,7 @@ linters: - name: errorf - name: exported arguments: - - sayRepetitiveInsteadOfStutters + - say-repetitive-instead-of-stutters - name: flag-parameter - name: identical-branches - name: if-return @@ -169,11 +172,12 @@ linters: - name: increment-decrement - name: indent-error-flow arguments: - - preserveScope + - preserve-scope - name: package-comments - name: range - name: range-val-in-closure - name: range-val-address + - name: receiver-naming - name: redefines-builtin-id - name: string-format arguments: @@ -183,7 +187,7 @@ linters: - name: struct-tag - name: superfluous-else arguments: - - preserveScope + - preserve-scope - name: time-equal - name: unconditional-recursion - name: unexported-return diff --git a/vendor/go.opentelemetry.io/otel/AGENTS.md b/vendor/go.opentelemetry.io/otel/AGENTS.md new file mode 100644 index 0000000000..26c0fc4ddb --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/AGENTS.md @@ -0,0 +1,109 @@ +# Agent Guide for opentelemetry-go + +This file contains active, task-oriented instructions for autonomous and semi-autonomous coding agents working in this repository. + +Before starting any task, read `.github/copilot-instructions.md`, `CONTRIBUTING.md`, and this file. +Treat `.github/copilot-instructions.md` as global passive guidance for every task, including docs-only and review-only work. + +## Core expectations + +- Preserve OpenTelemetry specification compliance, API stability, and idiomatic Go. +- Prefer minimal, surgical changes over broad refactors or speculative cleanup. +- Read the package you are editing and match its existing naming, option types, error handling, comments, tests, and concurrency patterns. +- Keep public APIs backward compatible unless the task explicitly requires a breaking change. +- Keep telemetry resilient and loosely coupled. Do not introduce behavior that can unexpectedly interfere with host applications. +- Inspect boundaries carefully: input validation, resource limits, cancellation, shutdown, error propagation, concurrency, and memory growth. +- Prefer fail-safe behavior and explicit invariants over implicit assumptions. +- Keep dependencies minimal and justified. +- Preserve host-application safety: telemetry should not panic, block indefinitely, or amplify attacker-controlled input. +- Be conservative on hot paths. Avoid unnecessary allocations, reflection, interface churn, blocking, global state, and high-cardinality telemetry. +- Write comments only for intent, invariants, and non-obvious constraints. Do not add comments that restate the code. + +## Default workflow + +For new features and behavior changes, use this order unless the task explicitly says otherwise: + +1. Read the relevant package, its tests, and any package docs or `README.md`. +2. Add or update a failing unit test that captures the required behavior or regression. +3. Implement the smallest change that makes the test pass. +4. Refactor only after the behavior is locked in, and only if the refactor keeps the diff focused. +5. If the changed code is on a hot path or performance-sensitive, inspect existing benchmarks and run them. Add a benchmark if coverage is missing. +6. Update documentation artifacts as needed while the context is fresh. Follow the documentation and changelog conventions below for the specific updates required. +7. Run `make precommit` each time before considering the work complete. + +For docs-only, test-only, or review-only tasks, still start with the required repository guidance above, then skip the workflow steps that do not apply while keeping the same discipline around scope, verification, and repository conventions. + +## Verification + +- Use `make` as the canonical repository verification command. The default target is `precommit`. +- `make precommit` is the expected final verification step for linting, generation, README checks, module checks, and tests. +- During iteration, targeted commands are fine for fast feedback, but do not stop there if the task changes code. +- If you touch performance-sensitive code, run focused benchmarks and compare the results using `benchstat` in addition to `make`. + +## Documentation and changelog + +- Non-internal, non-test packages should have Go doc comments, usually in `doc.go`. +- Non-internal, non-test, non-documentation packages should also have a `README.md` with at least a title and a `pkg.go.dev` badge. +- Prefer examples over long code snippets in GoDoc when practical. +- Keep docs aligned with actual behavior. Do not leave stale comments, stale examples, or stale package documentation behind. +- For user-visible changes, update `CHANGELOG.md` under the appropriate `Added`, `Changed`, `Deprecated`, `Fixed`, or `Removed` section within `## [Unreleased]`. + +## Repository habits + +- Prefer focused diffs. Avoid drive-by cleanup. +- Follow existing option patterns and exported API conventions instead of inventing new abstractions. +- Generated files are checked in. If your change affects generation, keep generated output up to date. +- Prefer fast local search tools such as `rg` when exploring the repository. +- When changing behavior, make the invariants explicit in tests. + +## Personas + +### Feature Agent + +Use this persona for new behavior, new API surface, or spec-driven feature work. + +- Start with a failing unit test. +- Confirm the expected behavior against the spec, existing package behavior, and public API compatibility. +- Implement the smallest viable change. +- Update GoDoc, examples, `README.md`, and `CHANGELOG.md` when the change is user-visible. +- If the feature touches a hot path, check benchmarks and add one if the coverage is missing. + +### Refactoring Agent + +Use this persona when improving structure without intentionally changing behavior. + +- Treat behavior preservation as the default contract. +- Add or tighten tests before moving code if current behavior is not already pinned down. +- Avoid broad rewrites, clever abstractions, or package-wide cleanup unless explicitly requested. +- If a refactor touches a hot path, benchmark before and after. +- Keep API shape, semantics, concurrency guarantees, and failure modes unchanged unless the task says otherwise. + +### Test Agent + +Use this persona when adding missing coverage, reproducing bugs, or hardening regressions. + +- Reproduce the bug or missing behavior with the smallest failing test you can. +- Prefer testing public behavior and externally visible invariants. +- Add targeted regression tests before changing production code. +- Only change production code when it is required to make the tested behavior correct or testable. +- Keep tests deterministic, readable, and aligned with package patterns. + +### Performance Agent + +Use this persona for hot-path work, allocation reduction, or throughput and latency improvements. + +- Benchmark first to establish a baseline. +- Prefer changes that reduce allocations, copying, interface churn, and unnecessary synchronization. +- Do not trade away correctness, spec compliance, or API stability for micro-optimizations. +- Add or update benchmarks when performance-sensitive coverage is missing. +- If you materially change a hot path, capture before-and-after results, preferably with `benchstat`. + +### Review Agent + +Use this persona when asked to review code, patches, or pull requests. + +- Lead with findings, not summaries. +- Order findings by severity and include precise file and line references when available. +- Focus on correctness, spec compliance, API compatibility, concurrency safety, resilience, performance regressions, missing tests, missing benchmarks, documentation gaps, and changelog gaps. +- Call out when a diff is broader than necessary. +- If you find no issues, say that explicitly and note any residual risks or verification gaps. diff --git a/vendor/go.opentelemetry.io/otel/CHANGELOG.md b/vendor/go.opentelemetry.io/otel/CHANGELOG.md index 20edda4418..6a90451f52 100644 --- a/vendor/go.opentelemetry.io/otel/CHANGELOG.md +++ b/vendor/go.opentelemetry.io/otel/CHANGELOG.md @@ -11,6 +11,100 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm +## [1.44.0/0.66.0/0.20.0/0.0.17] 2026-05-27 + +### Added + +- Add `ByteSlice` and `ByteSliceValue` functions for new `BYTESLICE` attribute type in `go.opentelemetry.io/otel/attribute`. (#7948) +- Apply attribute value limit to the `KindBytes` attribute type in `go.opentelemetry.io/otel/sdk/log`. (#7990) +- Apply attribute value limit to the `BYTESLICE` attribute type in `go.opentelemetry.io/otel/sdk/trace`. (#7990) +- Support `BYTESLICE` attributes in `go.opentelemetry.io/otel/trace`. (#8153) +- Support `BYTESLICE` attributes in `go.opentelemetry.io/otel/exporters/otlp/otlptrace`. (#8153) +- Support `BYTESLICE` attributes in `go.opentelemetry.io/otel/exporters/otlp/otlplog`. (#8153) +- Support `BYTESLICE` attributes in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric`. (#8153) +- Support `BYTESLICE` attributes in `go.opentelemetry.io/otel/exporters/zipkin`. (#8153) +- Add `String` method for `Value` type in `go.opentelemetry.io/otel/attribute`. (#8142) +- Add `Slice` and `SliceValue` functions for new `SLICE` attribute type in `go.opentelemetry.io/otel/attribute`. (#8166) +- Support `SLICE` attributes in `go.opentelemetry.io/otel/exporters/otlp/otlptrace`. (#8216) +- Support `SLICE` attributes in `go.opentelemetry.io/otel/exporters/otlp/otlplog`. (#8216) +- Support `SLICE` attributes in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric`. (#8216) +- Support `SLICE` attributes in `go.opentelemetry.io/otel/exporters/zipkin`. (#8216) +- Apply `AttributeValueLengthLimit` to `attribute.SLICE` type attribute values in `go.opentelemetry.io/otel/sdk/trace`, recursively truncating contained string values. (#8217) +- Add `Error` field on `Record` type in `go.opentelemetry.io/otel/log/logtest`. (#8148) +- Add `WithMaxRequestSize` option in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc`. (#8157) +- Add `WithMaxRequestSize` option in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp`. (#8157) +- Add `WithMaxRequestSize` option in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc`. (#8157) +- Add `WithMaxRequestSize` option in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp`. (#8157) +- Add `WithMaxRequestSize` option in `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc`. (#8157) +- Add `WithMaxRequestSize` option in `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp`. (#8157) +- Add `Settable` to `go.opentelemetry.io/otel/metric/x` to allow reusing attribute options. (#8178) +- Add experimental support for splitting metric data across multiple batches in `go.opentelemetry.io/otel/sdk/metric`. + Set `OTEL_GO_X_METRIC_EXPORT_BATCH_SIZE=` to enable for all periodic readers. + See `go.opentelemetry.io/otel/sdk/metric/internal/x` for feature documentation. (#8071) +- Add experimental self-observability metrics in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc`. + Enable with `OTEL_GO_X_SELF_OBSERVABILITY=true` environment variable. + See `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/x` for feature documentation. (#8192) +- Add experimental self-observability metrics in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp`. + Enable with `OTEL_GO_X_SELF_OBSERVABILITY=true` environment variable. + See `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/x` for feature documentation. (#8194) +- Add experimental self-observability metrics in `go.opentelemetry.io/otel/exporters/stdout/stdoutlog`. + Enable with `OTEL_GO_X_SELF_OBSERVABILITY=true` environment variable. + See `go.opentelemetry.io/otel/stdout/stdoutlog/internal/x` for feature documentation. (#8263) +- Add `WithDefaultAttributes` to `go.opentelemetry.io/otel/metric/x` to support setting default attributes on instruments. (#8135) +- Add `go.opentelemetry.io/otel/semconv/v1.41.0` package. + The package contains semantic conventions from the `v1.41.0` version of the OpenTelemetry Semantic Conventions. + See the [migration documentation](./semconv/v1.41.0/MIGRATION.md) for information on how to upgrade from `go.opentelemetry.io/otel/semconv/v1.40.0`. (#8324) +- Add Observable variants of instruments to `go.opentelemetry.io/otel/semconv/v1.41.0` package. (#8350) +- Generate explicit histogram bucket boundaries from weaver configuration for HTTP and RPC duration instruments in `go.opentelemetry.io/otel/semconv/v1.41.0`. (#8002) + +### Changed + +- ⚠️ **Breaking Change:** `go.opentelemetry.io/otel/sdk/metric` now applies a default cardinality limit of 2000 to comply with the Metrics SDK specification recommendation. + New attribute sets are dropped when the cardinality limit is reached. The measurement of these sets are aggregated into a special attribute set containing `attribute.Bool("otel.metric.overflow", true)`. + This can break users who relied on the previous unlimited default. + Set `WithCardinalityLimit(0)` or the deprecated `OTEL_GO_X_CARDINALITY_LIMIT=0` environment variable to preserve unlimited cardinality. + Note that support for `OTEL_GO_X_CARDINALITY_LIMIT` may be removed in a future release. (#8247) +- `ErrorType` in `go.opentelemetry.io/otel/semconv` now unwraps errors created with `fmt.Errorf` when deriving the `error.type` attribute. (#8133) +- `go.opentelemetry.io/otel/sdk/log` now unwraps error chains created with `fmt.Errorf` when deriving the `error.type` attribute from errors on log records. (#8133) +- `Set.MarshalLog` method in `go.opentelemetry.io/otel/attribute` now uses `Value.String` formatting following the [OpenTelemetry AnyValue representation for non-OTLP protocols](https://opentelemetry.io/docs/specs/otel/common/#anyvalue). (#8169) +- Optimize `go.opentelemetry.io/otel/sdk/metric` to return a drop reservoir and short-circuit `Offer` calls to the exemplar reservoir when `exemplar.AlwaysOffFilter` is configured. (#8211) (#8267) +- Optimize `go.opentelemetry.io/otel/sdk/metric` to return a drop reservoir for asynchronous instruments when `exemplar.TraceBasedFilter` is configured. (#8286) + +### Deprecated + +- Deprecate `Value.Emit` method in `go.opentelemetry.io/otel/attribute`. + Use `Value.String` instead. (#8176) + +### Fixed + +- Limit OTLP request size to 64 MiB by default in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc`. + The limit applies before compression, oversized requests are treated as non-retryable errors, and the limit can be configured with the new `WithMaxRequestSize` option. (#8157, #8365) +- Limit OTLP request size to 64 MiB by default in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp`. + The limit applies before compression, oversized requests are treated as non-retryable errors, and the limit can be configured with the new `WithMaxRequestSize` option. (#8157, #8365) +- Limit OTLP request size to 64 MiB by default in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc`. + The limit applies before compression, oversized requests are treated as non-retryable errors, and the limit can be configured with the new `WithMaxRequestSize` option. (#8157, #8365) +- Limit OTLP request size to 64 MiB by default in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp`. + The limit applies before compression, oversized requests are treated as non-retryable errors, and the limit can be configured with the new `WithMaxRequestSize` option. (#8157, #8365) +- Limit OTLP request size to 64 MiB by default in `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc`. + The limit applies before compression, oversized requests are treated as non-retryable errors, and the limit can be configured with the new `WithMaxRequestSize` option. (#8157, #8365) +- Limit OTLP request size to 64 MiB by default in `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp`. + The limit applies before compression, oversized requests are treated as non-retryable errors, and the limit can be configured with the new `WithMaxRequestSize` option. (#8157, #8365) +- Fix gzipped request body replay on redirect in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp`. (#8135) +- Fix gzipped request body replay on redirect in `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp`. (#8152) +- `go.opentelemetry.io/otel/exporters/prometheus` now uses `Value.String` formatting for label values following the [OpenTelemetry AnyValue representation for non-OTLP protocols](https://opentelemetry.io/docs/specs/otel/common/#anyvalue). (#8170) +- Propagate errors from the exporter when calling `Shutdown` on `BatchSpanProcessor` in `go.opentelemetry.io/otel/sdk/trace`. (#8197) +- Fix stale status code reporting on self-observability metrics in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp` and `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp`. (#8226) +- Fix a concurrent `Collect` data race and potential panic in `go.opentelemetry.io/otel/exporters/prometheus` when `WithResourceAsConstantLabels` option is used. (#8227) +- Fix race condition in `FixedSizeReservoir` in `go.opentelemetry.io/otel/sdk/metric/exemplar` by reverting #7447. (#8249) +- Fix `FixedSizeReservoir` in `go.opentelemetry.io/otel/sdk/metric/exemplar` to safely handle zero size. + A capacity check in the constructor initializes the reservoir safely and skips initialization for zero-cap; early returns in `Offer()` and `Collect()` ensure no-op behavior. (#8295) +- Fix counting of spans and logs in self-observability metrics in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc`, `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp`, `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc`, and `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp`. (#8254) +- Drop conflicting scope attributes named `name`, `version`, or `schema_url` from metric labels in `go.opentelemetry.io/otel/exporters/prometheus`, preserving the dedicated `otel_scope_name`, `otel_scope_version`, and `otel_scope_schema_url` labels. (#8264) +- Close schema files opened by `ParseFile` in `go.opentelemetry.io/otel/schema/v1.0` and `go.opentelemetry.io/otel/schema/v1.1`. ([GHSA-995v-fvrw-c78m](https://github.com/open-telemetry/opentelemetry-go/security/advisories/GHSA-995v-fvrw-c78m)) +- Enforce the 8192-byte baggage size limit during extraction/parsing, changing behavior when the limit is exceeded in `go.opentelemetry.io/otel/baggage` and `go.opentelemetry.io/otel/propagation`. (#8222) +- Fix `go.opentelemetry.io/otel/semconv/v1.41.0` to include `Attr*` helper methods for required attributes on observable instruments. (#8361) +- Limit baggage extraction error reporting in `go.opentelemetry.io/otel/propagation` to prevent malformed or oversized baggage headers from flooding logs. ([GHSA-5wrp-cwcj-q835](https://github.com/open-telemetry/opentelemetry-go/security/advisories/GHSA-5wrp-cwcj-q835)) + ## [1.43.0/0.65.0/0.19.0] 2026-04-02 ### Added @@ -3619,7 +3713,8 @@ It contains api and sdk for trace and meter. - CircleCI build CI manifest files. - CODEOWNERS file to track owners of this project. -[Unreleased]: https://github.com/open-telemetry/opentelemetry-go/compare/v1.43.0...HEAD +[Unreleased]: https://github.com/open-telemetry/opentelemetry-go/compare/v1.44.0...HEAD +[1.44.0/0.66.0/0.20.0/0.0.17]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.44.0 [1.43.0/0.65.0/0.19.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.43.0 [1.42.0/0.64.0/0.18.0/0.0.16]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.42.0 [1.41.0/0.63.0/0.17.0/0.0.15]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.41.0 diff --git a/vendor/go.opentelemetry.io/otel/CLAUDE.md b/vendor/go.opentelemetry.io/otel/CLAUDE.md new file mode 100644 index 0000000000..dd3c4594fc --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/CLAUDE.md @@ -0,0 +1,3 @@ +# Instructions for Claude Code + +@AGENTS.md diff --git a/vendor/go.opentelemetry.io/otel/CONTRIBUTING.md b/vendor/go.opentelemetry.io/otel/CONTRIBUTING.md index 12de3607a3..3ec17d6832 100644 --- a/vendor/go.opentelemetry.io/otel/CONTRIBUTING.md +++ b/vendor/go.opentelemetry.io/otel/CONTRIBUTING.md @@ -11,6 +11,12 @@ for a summary description of past meetings. To request edit access, join the meeting or get in touch on [Slack](https://cloud-native.slack.com/archives/C01NPAXACKT). +The meeting is open for all to join. We invite everyone to join our +meeting, regardless of your experience level. Whether you're a +seasoned OpenTelemetry developer, just starting your journey, or +simply curious about the work we do, you're more than welcome to +participate! + ## Development You can view and edit the source code by cloning this repository: @@ -746,8 +752,8 @@ Encapsulate setup in constructor functions, ensuring clear ownership and scope: import ( "errors" - semconv "go.opentelemetry.io/otel/semconv/v1.40.0" - "go.opentelemetry.io/otel/semconv/v1.40.0/otelconv" + semconv "go.opentelemetry.io/otel/semconv/v1.41.0" + "go.opentelemetry.io/otel/semconv/v1.41.0/otelconv" ) type SDKComponent struct { @@ -808,11 +814,11 @@ func (c *Component) initObservability() { #### Performance -When observability is disabled there should be little to no overhead. +When observability is disabled or the instrument is not `Enabled`, there should be little to no overhead. ```go func (e *Exporter) ExportSpans(ctx context.Context, spans []trace.ReadOnlySpan) error { - if e.inst != nil { + if e.inst != nil && e.inst.Enabled(ctx) { attrs := expensiveOperation() e.inst.recordSpanInflight(ctx, int64(len(spans)), attrs...) } @@ -829,7 +835,7 @@ func (e *Exporter) ExportSpans(ctx context.Context, spans []trace.ReadOnlySpan) } func (i *instrumentation) recordSpanInflight(ctx context.Context, count int64, attrs ...attribute.KeyValue) { - if i == nil || i.inflight == nil { + if i == nil || i.inflight == nil || !i.inflight.Enabled(ctx) { return } i.inflight.Add(ctx, count, metric.WithAttributes(attrs...)) @@ -865,8 +871,12 @@ var ( ) func (i *instrumentation) record(ctx context.Context, value int64, baseAttrs ...attribute.KeyValue) { + if !i.counter.Enabled(ctx) { + return + } attrs := attrPool.Get().(*[]attribute.KeyValue) defer func() { + clear(*attrs) // Clear references to strings/etc to let GC collect them. *attrs = (*attrs)[:0] // Reset. attrPool.Put(attrs) }() @@ -877,6 +887,7 @@ func (i *instrumentation) record(ctx context.Context, value int64, baseAttrs ... addOpt := addOptPool.Get().(*[]metric.AddOption) defer func() { + clear(*addOpt) *addOpt = (*addOpt)[:0] addOptPool.Put(addOpt) }() @@ -1007,16 +1018,20 @@ Ensure observability measurements receive the correct context, especially for tr ```go func (e *Exporter) ExportSpans(ctx context.Context, spans []trace.ReadOnlySpan) error { // Use the provided context for observability measurements - e.inst.recordSpanExportStarted(ctx, len(spans)) + if e.inst.Enabled(ctx) { + e.inst.recordSpanExportStarted(ctx, len(spans)) + } err := e.doExport(ctx, spans) - if err != nil { - e.inst.recordSpanExportFailed(ctx, len(spans), err) - } else { - e.inst.recordSpanExportSucceeded(ctx, len(spans)) + if e.inst.Enabled(ctx) { + if err != nil { + e.inst.recordSpanExportFailed(ctx, len(spans), err) + } else { + e.inst.recordSpanExportSucceeded(ctx, len(spans)) + } } - + return err } ``` @@ -1039,7 +1054,7 @@ func (e *Exporter) ExportSpans(ctx context.Context, spans []trace.ReadOnlySpan) All observability metrics should follow the [OpenTelemetry Semantic Conventions for SDK metrics](https://github.com/open-telemetry/semantic-conventions/blob/1cf2476ae5e518225a766990a28a6d5602bd5a30/docs/otel/sdk-metrics.md). -Use the metric semantic conventions convenience package [otelconv](./semconv/v1.40.0/otelconv/metric.go). +Use the metric semantic conventions convenience package [otelconv](./semconv/v1.41.0/otelconv/metric.go). ##### Component Identification @@ -1109,6 +1124,68 @@ func TestObservability(t *testing.T) { Test order should not affect results. Ensure that any global state (e.g. component ID counters) is reset between tests. +### Experimental Features + +To support the development of new features in the specification, we use the following patterns to implement in-development features without adding new public artifacts in stable modules. + +#### Experimental behavior with no API artifacts + +Features that change behavior without changing the API (e.g., exemplar collection, auto-generation of identifiers) are implemented behind a feature gate. +The implementation resides in an `/internal/x` package and is activated through environment variables with the `OTEL_GO_X_` prefix (e.g., `OTEL_GO_X_OBSERVABILITY`). +The feature must be documented in a `README.md` file in the `/internal/x` package. + +#### Experimental methods on SDK-only interfaces + +Features that require new methods on SDK interfaces are defined as a new interface in an experimental module (e.g., `go.opentelemetry.io/otel/sdk/x`). +The SDK uses type assertions (without importing the unstable package) to check if passing types implement these experimental interfaces. +The SDK must not depend on the experimental module. + +#### Experimental structs, functions, or interfaces + +Features that don't need any changes to the existing stable package are implemented in an experimental module (e.g., `go.opentelemetry.io/otel/sdk/x`). + +#### Experimental signals and components + +New telemetry signals (e.g., Logs before stabilization) and components (e.g. bridges) are hosted in new, unstable modules (e.g., `go.opentelemetry.io/otel/log` before 1.0.0). +The package should have the final name it will use once stabilized (i.e. not `/x`), and is released at a v0.x.y version to indicate it is not stable. +Most new components are hosted in [opentelemetry-go-contrib](https://github.com/open-telemetry/opentelemetry-go-contrib). + +#### Experimental options for API or SDK functions + +Experimental Options functions are implemented in an experimental module (e.g., `go.opentelemetry.io/otel/sdk/x`). +The return type of the Option function must embed the option's type (e.g. `metric.InstrumentOption`), and have an `Experimental()` method to prevent the API from panicking when the option is used. +The SDK uses type assertions (without importing the unstable package) to check if passing types implement these experimental interfaces. +The SDK must not depend on the experimental module. + +For example: + +```go +type myOption struct { + // Embed the stable option type. + metric.InstrumentOption + value string +} + +// Experimental prevents the API from panicking when the option is used. +func (o myOption) Experimental() {} + +// The SDK can use type assertions to use this function. +func (o myOption) Value() string { return o.value } + +func WithMyOption(value string) metric.InstrumentOption { + return myOption{value: value} +} +``` + +#### Not Supported + +The following kinds of experimental features are **not currently supported** on stable interfaces: + +- Experimental methods on API interfaces +- Experimental fields for API or SDK exported structs + +In some cases forks or long-lived branches may be used for prototyping these features. + ## Approvers and Maintainers ### Maintainers diff --git a/vendor/go.opentelemetry.io/otel/Makefile b/vendor/go.opentelemetry.io/otel/Makefile index 42466f2d6a..de63a5e9bc 100644 --- a/vendor/go.opentelemetry.io/otel/Makefile +++ b/vendor/go.opentelemetry.io/otel/Makefile @@ -191,8 +191,16 @@ benchmark: $(OTEL_GO_MOD_DIRS:%=benchmark/%) benchmark/%: cd $* && $(GO) test -run='^$$' -bench=. $(ARGS) ./... +# sdk/metric is split into two shards to work around CodSpeed limitations. +# See https://github.com/CodSpeedHQ/codspeed-go/issues/56 +BENCHMARK_SHARDS := $(filter-out ./sdk/metric,$(OTEL_GO_MOD_DIRS)) ./sdk/metric/root ./sdk/metric/internal +benchmark/./sdk/metric/root: + cd ./sdk/metric && $(GO) test -run='^$$' -bench=. $(ARGS) . ./exemplar/... +benchmark/./sdk/metric/internal: + cd ./sdk/metric && $(GO) test -run='^$$' -bench=. $(ARGS) ./internal/... + print-sharded-benchmarks: - @echo $(OTEL_GO_MOD_DIRS) | jq -cR 'split(" ")' + @echo $(BENCHMARK_SHARDS) | jq -cR 'split(" ")' .PHONY: golangci-lint golangci-lint-fix golangci-lint-fix: ARGS=--fix diff --git a/vendor/go.opentelemetry.io/otel/attribute/encoder.go b/vendor/go.opentelemetry.io/otel/attribute/encoder.go index 771dd69c55..ca186d8ac2 100644 --- a/vendor/go.opentelemetry.io/otel/attribute/encoder.go +++ b/vendor/go.opentelemetry.io/otel/attribute/encoder.go @@ -105,7 +105,9 @@ func (d *defaultAttrEncoder) Encode(iter Iterator) string { if keyValue.Value.Type() == STRING { copyAndEscape(buf, keyValue.Value.AsString()) } else { - _, _ = buf.WriteString(keyValue.Value.Emit()) + _, _ = buf.WriteString( + keyValue.Value.Emit(), + ) //nolint:staticcheck // Preserve the existing default encoder output. } } return buf.String() diff --git a/vendor/go.opentelemetry.io/otel/attribute/hash.go b/vendor/go.opentelemetry.io/otel/attribute/hash.go index b09caaa6d7..92f39ffe7b 100644 --- a/vendor/go.opentelemetry.io/otel/attribute/hash.go +++ b/vendor/go.opentelemetry.io/otel/attribute/hash.go @@ -27,6 +27,8 @@ const ( int64SliceID uint64 = 3762322556277578591 // "_[]int64" (little endian) float64SliceID uint64 = 7308324551835016539 // "[]double" (little endian) stringSliceID uint64 = 7453010373645655387 // "[]string" (little endian) + byteSliceID uint64 = 6874028470941080415 // "_[]byte_" (little endian) + sliceID uint64 = 7883494272577650031 // "__slice_" (little endian) emptyID uint64 = 7305809155345288421 // "__empty_" (little endian) ) @@ -42,53 +44,87 @@ func hashKVs(kvs []KeyValue) uint64 { // hashKV returns the xxHash64 hash of kv with h as the base. func hashKV(h xxhash.Hash, kv KeyValue) xxhash.Hash { h = h.String(string(kv.Key)) + return hashValue(h, kv.Value) +} - switch kv.Value.Type() { +func hashValue(h xxhash.Hash, v Value) xxhash.Hash { + switch v.Type() { case BOOL: h = h.Uint64(boolID) - h = h.Uint64(kv.Value.numeric) + h = h.Uint64(v.numeric) case INT64: h = h.Uint64(int64ID) - h = h.Uint64(kv.Value.numeric) + h = h.Uint64(v.numeric) case FLOAT64: h = h.Uint64(float64ID) // Assumes numeric stored with math.Float64bits. - h = h.Uint64(kv.Value.numeric) + h = h.Uint64(v.numeric) case STRING: h = h.Uint64(stringID) - h = h.String(kv.Value.stringly) + h = h.String(v.stringly) case BOOLSLICE: h = h.Uint64(boolSliceID) - rv := reflect.ValueOf(kv.Value.slice) + rv := reflect.ValueOf(v.slice) for i := 0; i < rv.Len(); i++ { h = h.Bool(rv.Index(i).Bool()) } case INT64SLICE: h = h.Uint64(int64SliceID) - rv := reflect.ValueOf(kv.Value.slice) + rv := reflect.ValueOf(v.slice) for i := 0; i < rv.Len(); i++ { h = h.Int64(rv.Index(i).Int()) } case FLOAT64SLICE: h = h.Uint64(float64SliceID) - rv := reflect.ValueOf(kv.Value.slice) + rv := reflect.ValueOf(v.slice) for i := 0; i < rv.Len(); i++ { h = h.Float64(rv.Index(i).Float()) } case STRINGSLICE: h = h.Uint64(stringSliceID) - rv := reflect.ValueOf(kv.Value.slice) + rv := reflect.ValueOf(v.slice) for i := 0; i < rv.Len(); i++ { h = h.String(rv.Index(i).String()) } + case BYTESLICE: + h = h.Uint64(byteSliceID) + h = h.String(v.stringly) + case SLICE: + h = h.Uint64(sliceID) + switch vals := v.slice.(type) { + case [0]Value: + // No values to hash, but the type identifier is still hashed above. + case [1]Value: + h = hashValueSlice(h, vals[:]) + case [2]Value: + h = hashValueSlice(h, vals[:]) + case [3]Value: + h = hashValueSlice(h, vals[:]) + case [4]Value: + h = hashValueSlice(h, vals[:]) + case [5]Value: + h = hashValueSlice(h, vals[:]) + default: + rv := reflect.ValueOf(v.slice) + for i := 0; i < rv.Len(); i++ { + h = hashValue(h, rv.Index(i).Interface().(Value)) + } + } case EMPTY: h = h.Uint64(emptyID) default: // Logging is an alternative, but using the internal logger here // causes an import cycle so it is not done. - v := kv.Value.AsInterface() - msg := fmt.Sprintf("unknown value type: %[1]v (%[1]T)", v) + val := v.AsInterface() + msg := fmt.Sprintf("unknown value type: %[1]v (%[1]T)", val) panic(msg) } return h } + +func hashValueSlice(h xxhash.Hash, vals []Value) xxhash.Hash { + for _, v := range vals { + h = hashValue(h, v) + } + return h +} diff --git a/vendor/go.opentelemetry.io/otel/attribute/key.go b/vendor/go.opentelemetry.io/otel/attribute/key.go index 80a9e5643f..cdc7089e82 100644 --- a/vendor/go.opentelemetry.io/otel/attribute/key.go +++ b/vendor/go.opentelemetry.io/otel/attribute/key.go @@ -117,6 +117,28 @@ func (k Key) StringSlice(v []string) KeyValue { } } +// ByteSlice creates a KeyValue instance with a BYTESLICE Value. +// +// If creating both a key and value at the same time, use the provided +// convenience function instead -- ByteSlice(name, value). +func (k Key) ByteSlice(v []byte) KeyValue { + return KeyValue{ + Key: k, + Value: ByteSliceValue(v), + } +} + +// Slice creates a KeyValue instance with a SLICE Value. +// +// If creating both a key and value at the same time, use the provided +// convenience function instead -- Slice(name, values...). +func (k Key) Slice(v ...Value) KeyValue { + return KeyValue{ + Key: k, + Value: SliceValue(v...), + } +} + // Defined reports whether the key is not empty. func (k Key) Defined() bool { return len(k) != 0 diff --git a/vendor/go.opentelemetry.io/otel/attribute/kv.go b/vendor/go.opentelemetry.io/otel/attribute/kv.go index 0cc368018b..eeb76a1348 100644 --- a/vendor/go.opentelemetry.io/otel/attribute/kv.go +++ b/vendor/go.opentelemetry.io/otel/attribute/kv.go @@ -68,6 +68,16 @@ func StringSlice(k string, v []string) KeyValue { return Key(k).StringSlice(v) } +// ByteSlice creates a KeyValue with a BYTESLICE Value type. +func ByteSlice(k string, v []byte) KeyValue { + return Key(k).ByteSlice(v) +} + +// Slice creates a KeyValue with a SLICE Value type. +func Slice(k string, v ...Value) KeyValue { + return Key(k).Slice(v...) +} + // Stringer creates a new key-value pair with a passed name and a string // value generated by the passed Stringer interface. func Stringer(k string, v fmt.Stringer) KeyValue { diff --git a/vendor/go.opentelemetry.io/otel/attribute/set.go b/vendor/go.opentelemetry.io/otel/attribute/set.go index 6572c98b12..a4b6ce81de 100644 --- a/vendor/go.opentelemetry.io/otel/attribute/set.go +++ b/vendor/go.opentelemetry.io/otel/attribute/set.go @@ -401,7 +401,7 @@ func computeDataFixed(kvs []KeyValue) any { func computeDataReflect(kvs []KeyValue) any { at := reflect.New(reflect.ArrayOf(len(kvs), keyValueType)).Elem() for i, keyValue := range kvs { - *(at.Index(i).Addr().Interface().(*KeyValue)) = keyValue + *at.Index(i).Addr().Interface().(*KeyValue) = keyValue } return at.Interface() } @@ -415,7 +415,7 @@ func (l *Set) MarshalJSON() ([]byte, error) { func (l Set) MarshalLog() any { kvs := make(map[string]string) for _, kv := range l.ToSlice() { - kvs[string(kv.Key)] = kv.Value.Emit() + kvs[string(kv.Key)] = kv.Value.String() } return kvs } diff --git a/vendor/go.opentelemetry.io/otel/attribute/type_string.go b/vendor/go.opentelemetry.io/otel/attribute/type_string.go index 6c04448d6f..dbc01d3247 100644 --- a/vendor/go.opentelemetry.io/otel/attribute/type_string.go +++ b/vendor/go.opentelemetry.io/otel/attribute/type_string.go @@ -17,11 +17,13 @@ func _() { _ = x[INT64SLICE-6] _ = x[FLOAT64SLICE-7] _ = x[STRINGSLICE-8] + _ = x[BYTESLICE-9] + _ = x[SLICE-10] } -const _Type_name = "EMPTYBOOLINT64FLOAT64STRINGBOOLSLICEINT64SLICEFLOAT64SLICESTRINGSLICE" +const _Type_name = "EMPTYBOOLINT64FLOAT64STRINGBOOLSLICEINT64SLICEFLOAT64SLICESTRINGSLICEBYTESLICESLICE" -var _Type_index = [...]uint8{0, 5, 9, 14, 21, 27, 36, 46, 58, 69} +var _Type_index = [...]uint8{0, 5, 9, 14, 21, 27, 36, 46, 58, 69, 78, 83} func (i Type) String() string { idx := int(i) - 0 diff --git a/vendor/go.opentelemetry.io/otel/attribute/value.go b/vendor/go.opentelemetry.io/otel/attribute/value.go index db04b1326c..0529fefae2 100644 --- a/vendor/go.opentelemetry.io/otel/attribute/value.go +++ b/vendor/go.opentelemetry.io/otel/attribute/value.go @@ -4,9 +4,14 @@ package attribute // import "go.opentelemetry.io/otel/attribute" import ( + "encoding/base64" "encoding/json" "fmt" + "math" + "reflect" "strconv" + "strings" + "unicode/utf8" attribute "go.opentelemetry.io/otel/attribute/internal" ) @@ -45,6 +50,10 @@ const ( FLOAT64SLICE // STRINGSLICE is a slice of strings Type Value. STRINGSLICE + // BYTESLICE is a slice of bytes Type Value. + BYTESLICE + // SLICE is a slice of Value Type values. + SLICE // INVALID is used for a Value with no value set. // // Deprecated: Use EMPTY instead as an empty value is a valid value. @@ -134,6 +143,19 @@ func StringSliceValue(v []string) Value { return Value{vtype: STRINGSLICE, slice: attribute.SliceValue(v)} } +// ByteSliceValue creates a BYTESLICE Value. +func ByteSliceValue(v []byte) Value { + return Value{ + vtype: BYTESLICE, + stringly: string(v), + } +} + +// SliceValue creates a SLICE Value. +func SliceValue(v ...Value) Value { + return Value{vtype: SLICE, slice: sliceValue(v)} +} + // Type returns a type of the Value. func (v Value) Type() Type { return v.vtype @@ -215,6 +237,59 @@ func (v Value) asStringSlice() []string { return attribute.AsSlice[string](v.slice) } +// AsSlice returns the []Value value. Make sure that the Value's type is +// SLICE. +func (v Value) AsSlice() []Value { + if v.vtype != SLICE { + return nil + } + return v.asSlice() +} + +func (v Value) asSlice() []Value { + switch vals := v.slice.(type) { + case [0]Value: + return []Value{} + case [1]Value: + return []Value{vals[0]} + case [2]Value: + return []Value{vals[0], vals[1]} + case [3]Value: + return []Value{vals[0], vals[1], vals[2]} + case [4]Value: + return []Value{vals[0], vals[1], vals[2], vals[3]} + case [5]Value: + return []Value{vals[0], vals[1], vals[2], vals[3], vals[4]} + default: + return asValueSliceReflect(v.slice) + } +} + +func asValueSliceReflect(v any) []Value { + rv := reflect.ValueOf(v) + if !rv.IsValid() || rv.Kind() != reflect.Array || rv.Type().Elem() != reflect.TypeFor[Value]() { + return nil + } + cpy := make([]Value, rv.Len()) + if len(cpy) > 0 { + _ = reflect.Copy(reflect.ValueOf(cpy), rv) + } + return cpy +} + +// AsByteSlice returns the bytes value. Make sure that the Value's type +// is BYTESLICE. +func (v Value) AsByteSlice() []byte { + if v.vtype != BYTESLICE { + return nil + } + return v.asByteSlice() +} + +func (v Value) asByteSlice() []byte { + return []byte(v.stringly) +} + type unknownValueType struct{} // AsInterface returns Value's data as any. @@ -236,13 +311,60 @@ func (v Value) AsInterface() any { return v.stringly case STRINGSLICE: return v.asStringSlice() + case BYTESLICE: + return v.asByteSlice() + case SLICE: + return v.asSlice() case EMPTY: return nil } return unknownValueType{} } +// String returns a string representation of Value using the +// [OpenTelemetry AnyValue representation for non-OTLP protocols] rules. +// +// Strings are returned as-is without JSON quoting, booleans and integers use +// JSON literals, floating-point values use JSON numbers except that NaN and +// ±Inf are rendered as NaN, Infinity, and -Infinity, byte slices are +// base64-encoded, empty values are the empty string, and slices are encoded as +// JSON arrays. String, byte, and special floating-point values inside arrays +// are encoded as JSON strings, and empty values inside arrays are encoded as +// null. +// +// [OpenTelemetry AnyValue representation for non-OTLP protocols]: https://opentelemetry.io/docs/specs/otel/common/#anyvalue-representation-for-non-otlp-protocols +func (v Value) String() string { + switch v.Type() { + case BOOL: + return strconv.FormatBool(v.AsBool()) + case BOOLSLICE: + return formatBoolSliceValue(v.slice) + case INT64: + return strconv.FormatInt(v.AsInt64(), 10) + case INT64SLICE: + return formatInt64SliceValue(v.slice) + case FLOAT64: + return formatFloat64(v.AsFloat64()) + case FLOAT64SLICE: + return formatFloat64SliceValue(v.slice) + case STRING: + return v.stringly + case STRINGSLICE: + return formatStringSliceValue(v.slice) + case BYTESLICE: + return formatByteSlice(v.stringly) + case SLICE: + return formatValueSliceValue(v.slice) + case EMPTY: + return "" + default: + return "unknown" + } +} + // Emit returns a string representation of Value's data. +// +// Deprecated: Use [Value.String] instead. func (v Value) Emit() string { switch v.Type() { case BOOLSLICE: @@ -273,6 +395,10 @@ func (v Value) Emit() string { return string(j) case STRING: return v.stringly + case BYTESLICE: + return formatByteSlice(v.stringly) + case SLICE: + return formatValueSliceValue(v.slice) case EMPTY: return "" default: @@ -280,6 +406,622 @@ func (v Value) Emit() string { } } +const ( + jsonArrayBracketsLen = len("[]") + boolArrayElemMaxLen = len("false") + int64ArrayElemMaxLen = len("-9223372036854775808") + float64ArrayElemMaxLen = len("-1.7976931348623157e+308") + commaLen = len(",") +) + +func sliceValue(v []Value) any { + switch len(v) { + case 0: + return [0]Value{} + case 1: + return [1]Value{v[0]} + case 2: + return [2]Value{v[0], v[1]} + case 3: + return [3]Value{v[0], v[1], v[2]} + case 4: + return [4]Value{v[0], v[1], v[2], v[3]} + case 5: + return [5]Value{v[0], v[1], v[2], v[3], v[4]} + default: + return sliceValueReflect(v) + } +} + +func sliceValueReflect(v []Value) any { + cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeFor[Value]())).Elem() + reflect.Copy(cp, reflect.ValueOf(v)) + return cp.Interface() +} + +func formatBoolSliceValue(v any) string { + switch vals := v.(type) { + case [0]bool: + return "[]" + case [1]bool: + return formatBoolSlice(vals[:]) + case [2]bool: + return formatBoolSlice(vals[:]) + case [3]bool: + return formatBoolSlice(vals[:]) + default: + return formatBoolSliceReflect(v) + } +} + +func formatBoolSlice(vals []bool) string { + var b strings.Builder + appendBoolSlice(&b, vals) + return b.String() +} + +func formatBoolSliceReflect(v any) string { + var b strings.Builder + appendBoolSliceReflect(&b, reflect.ValueOf(v)) + return b.String() +} + +func appendBoolSliceValue(dst *strings.Builder, v any) { + switch vals := v.(type) { + case [0]bool: + _, _ = dst.WriteString("[]") + case [1]bool: + appendBoolSlice(dst, vals[:]) + case [2]bool: + appendBoolSlice(dst, vals[:]) + case [3]bool: + appendBoolSlice(dst, vals[:]) + default: + appendBoolSliceReflect(dst, reflect.ValueOf(v)) + } +} + +func appendBoolSlice(dst *strings.Builder, vals []bool) { + dst.Grow(jsonArrayBracketsLen + len(vals)*(boolArrayElemMaxLen+commaLen)) + _ = dst.WriteByte('[') + for i, val := range vals { + if i > 0 { + _ = dst.WriteByte(',') + } + if val { + _, _ = dst.WriteString("true") + } else { + _, _ = dst.WriteString("false") + } + } + _ = dst.WriteByte(']') +} + +func appendBoolSliceReflect(dst *strings.Builder, rv reflect.Value) { + dst.Grow(jsonArrayBracketsLen + rv.Len()*(boolArrayElemMaxLen+commaLen)) + _ = dst.WriteByte('[') + for i := 0; i < rv.Len(); i++ { + if i > 0 { + _ = dst.WriteByte(',') + } + if rv.Index(i).Bool() { + _, _ = dst.WriteString("true") + } else { + _, _ = dst.WriteString("false") + } + } + _ = dst.WriteByte(']') +} + +func formatInt64SliceValue(v any) string { + switch vals := v.(type) { + case [0]int64: + return "[]" + case [1]int64: + return formatInt64Slice(vals[:]) + case [2]int64: + return formatInt64Slice(vals[:]) + case [3]int64: + return formatInt64Slice(vals[:]) + default: + return formatInt64SliceReflect(v) + } +} + +func formatInt64Slice(vals []int64) string { + var b strings.Builder + appendInt64Slice(&b, vals) + return b.String() +} + +func formatInt64SliceReflect(v any) string { + var b strings.Builder + appendInt64SliceReflect(&b, reflect.ValueOf(v)) + return b.String() +} + +func appendInt64SliceValue(dst *strings.Builder, v any) { + switch vals := v.(type) { + case [0]int64: + _, _ = dst.WriteString("[]") + case [1]int64: + appendInt64Slice(dst, vals[:]) + case [2]int64: + appendInt64Slice(dst, vals[:]) + case [3]int64: + appendInt64Slice(dst, vals[:]) + default: + appendInt64SliceReflect(dst, reflect.ValueOf(v)) + } +} + +func appendInt64Slice(dst *strings.Builder, vals []int64) { + dst.Grow(jsonArrayBracketsLen + len(vals)*(int64ArrayElemMaxLen+commaLen)) + _ = dst.WriteByte('[') + + var buf [int64ArrayElemMaxLen]byte + for i, val := range vals { + if i > 0 { + _ = dst.WriteByte(',') + } + out := strconv.AppendInt(buf[:0], val, 10) + _, _ = dst.Write(out) + } + + _ = dst.WriteByte(']') +} + +func appendInt64SliceReflect(dst *strings.Builder, rv reflect.Value) { + dst.Grow(jsonArrayBracketsLen + rv.Len()*(int64ArrayElemMaxLen+commaLen)) + _ = dst.WriteByte('[') + + var scratch [int64ArrayElemMaxLen]byte + for i := 0; i < rv.Len(); i++ { + if i > 0 { + _ = dst.WriteByte(',') + } + out := strconv.AppendInt(scratch[:0], rv.Index(i).Int(), 10) + _, _ = dst.Write(out) + } + + _ = dst.WriteByte(']') +} + +func formatFloat64(v float64) string { + switch { + case math.IsNaN(v): + return "NaN" + case math.IsInf(v, 1): + return "Infinity" + case math.IsInf(v, -1): + return "-Infinity" + default: + return strconv.FormatFloat(v, 'g', -1, 64) + } +} + +func formatFloat64SliceValue(v any) string { + switch vals := v.(type) { + case [0]float64: + return "[]" + case [1]float64: + return formatFloat64Slice(vals[:]) + case [2]float64: + return formatFloat64Slice(vals[:]) + case [3]float64: + return formatFloat64Slice(vals[:]) + default: + return formatFloat64SliceReflect(v) + } +} + +func formatFloat64Slice(vals []float64) string { + var b strings.Builder + appendFloat64Slice(&b, vals) + return b.String() +} + +func formatFloat64SliceReflect(v any) string { + var b strings.Builder + appendFloat64SliceReflect(&b, reflect.ValueOf(v)) + return b.String() +} + +func appendFloat64SliceValue(dst *strings.Builder, v any) { + switch vals := v.(type) { + case [0]float64: + _, _ = dst.WriteString("[]") + case [1]float64: + appendFloat64Slice(dst, vals[:]) + case [2]float64: + appendFloat64Slice(dst, vals[:]) + case [3]float64: + appendFloat64Slice(dst, vals[:]) + default: + appendFloat64SliceReflect(dst, reflect.ValueOf(v)) + } +} + +func appendFloat64Slice(dst *strings.Builder, vals []float64) { + dst.Grow(jsonArrayBracketsLen + len(vals)*(float64ArrayElemMaxLen+commaLen)) + _ = dst.WriteByte('[') + + var buf [float64ArrayElemMaxLen]byte + for i, val := range vals { + if i > 0 { + _ = dst.WriteByte(',') + } + + switch { + case math.IsNaN(val): + _, _ = dst.WriteString(`"NaN"`) + case math.IsInf(val, 1): + _, _ = dst.WriteString(`"Infinity"`) + case math.IsInf(val, -1): + _, _ = dst.WriteString(`"-Infinity"`) + default: + out := strconv.AppendFloat(buf[:0], val, 'g', -1, 64) + _, _ = dst.Write(out) + } + } + + _ = dst.WriteByte(']') +} + +func appendFloat64SliceReflect(dst *strings.Builder, rv reflect.Value) { + dst.Grow(jsonArrayBracketsLen + rv.Len()*(float64ArrayElemMaxLen+commaLen)) + _ = dst.WriteByte('[') + + var scratch [float64ArrayElemMaxLen]byte + for i := 0; i < rv.Len(); i++ { + if i > 0 { + _ = dst.WriteByte(',') + } + val := rv.Index(i).Float() + switch { + case math.IsNaN(val): + _, _ = dst.WriteString(`"NaN"`) + case math.IsInf(val, 1): + _, _ = dst.WriteString(`"Infinity"`) + case math.IsInf(val, -1): + _, _ = dst.WriteString(`"-Infinity"`) + default: + out := strconv.AppendFloat(scratch[:0], val, 'g', -1, 64) + _, _ = dst.Write(out) + } + } + + _ = dst.WriteByte(']') +} + +func formatStringSliceValue(v any) string { + switch vals := v.(type) { + case [0]string: + return "[]" + case [1]string: + return formatStringSlice(vals[:]) + case [2]string: + return formatStringSlice(vals[:]) + case [3]string: + return formatStringSlice(vals[:]) + default: + return formatStringSliceReflect(v) + } +} + +func formatStringSlice(vals []string) string { + var b strings.Builder + appendStringSlice(&b, vals) + return b.String() +} + +func formatStringSliceReflect(v any) string { + var b strings.Builder + appendStringSliceReflect(&b, reflect.ValueOf(v)) + return b.String() +} + +func appendStringSliceValue(dst *strings.Builder, v any) { + switch vals := v.(type) { + case [0]string: + _, _ = dst.WriteString("[]") + case [1]string: + appendStringSlice(dst, vals[:]) + case [2]string: + appendStringSlice(dst, vals[:]) + case [3]string: + appendStringSlice(dst, vals[:]) + default: + appendStringSliceReflect(dst, reflect.ValueOf(v)) + } +} + +func appendStringSlice(dst *strings.Builder, vals []string) { + size := jsonArrayBracketsLen + for _, val := range vals { + size += len(val) + commaLen + 2 // Account for JSON string quotes and comma. + } + + dst.Grow(size) + _ = dst.WriteByte('[') + for i, val := range vals { + if i > 0 { + _ = dst.WriteByte(',') + } + appendJSONString(dst, val) + } + _ = dst.WriteByte(']') +} + +func appendStringSliceReflect(dst *strings.Builder, rv reflect.Value) { + size := jsonArrayBracketsLen + for i := 0; i < rv.Len(); i++ { + size += len(rv.Index(i).String()) + commaLen + 2 // Account for JSON string quotes and comma. + } + + dst.Grow(size) + _ = dst.WriteByte('[') + for i := 0; i < rv.Len(); i++ { + if i > 0 { + _ = dst.WriteByte(',') + } + appendJSONString(dst, rv.Index(i).String()) + } + _ = dst.WriteByte(']') +} + +func formatByteSlice(v string) string { + var b strings.Builder + appendBase64(&b, v) + return b.String() +} + +func formatValueSliceValue(v any) string { + switch vals := v.(type) { + case [0]Value: + return "[]" + case [1]Value: + return formatValueSlice(vals[:]) + case [2]Value: + return formatValueSlice(vals[:]) + case [3]Value: + return formatValueSlice(vals[:]) + case [4]Value: + return formatValueSlice(vals[:]) + case [5]Value: + return formatValueSlice(vals[:]) + default: + return formatValueSliceReflect(v) + } +} + +func formatValueSlice(vals []Value) string { + var b strings.Builder + appendValueSlice(&b, vals) + return b.String() +} + +func formatValueSliceReflect(v any) string { + var b strings.Builder + appendValueSliceReflect(&b, reflect.ValueOf(v)) + return b.String() +} + +func appendValueSliceValue(dst *strings.Builder, v any) { + switch vals := v.(type) { + case [0]Value: + _, _ = dst.WriteString("[]") + case [1]Value: + appendValueSlice(dst, vals[:]) + case [2]Value: + appendValueSlice(dst, vals[:]) + case [3]Value: + appendValueSlice(dst, vals[:]) + case [4]Value: + appendValueSlice(dst, vals[:]) + case [5]Value: + appendValueSlice(dst, vals[:]) + default: + appendValueSliceReflect(dst, reflect.ValueOf(v)) + } +} + +func appendValueSlice(dst *strings.Builder, vals []Value) { + // Estimate 10 bytes per value for small values and commas. + dst.Grow(jsonArrayBracketsLen + len(vals)*commaLen + len(vals)*10) + _ = dst.WriteByte('[') + for i, val := range vals { + if i > 0 { + _ = dst.WriteByte(',') + } + appendJSONValue(dst, val) + } + _ = dst.WriteByte(']') +} + +func appendValueSliceReflect(dst *strings.Builder, rv reflect.Value) { + // Estimate 10 bytes per value for small values and commas. + dst.Grow(jsonArrayBracketsLen + rv.Len()*commaLen + rv.Len()*10) + _ = dst.WriteByte('[') + for i := 0; i < rv.Len(); i++ { + if i > 0 { + _ = dst.WriteByte(',') + } + appendJSONValue(dst, rv.Index(i).Interface().(Value)) + } + _ = dst.WriteByte(']') +} + +func appendJSONValue(dst *strings.Builder, v Value) { + switch v.Type() { + case BOOL: + if v.AsBool() { + _, _ = dst.WriteString("true") + } else { + _, _ = dst.WriteString("false") + } + case BOOLSLICE: + appendBoolSliceValue(dst, v.slice) + case INT64: + var buf [int64ArrayElemMaxLen]byte + out := strconv.AppendInt(buf[:0], v.AsInt64(), 10) + _, _ = dst.Write(out) + case INT64SLICE: + appendInt64SliceValue(dst, v.slice) + case FLOAT64: + val := v.AsFloat64() + switch { + case math.IsNaN(val): + appendJSONString(dst, "NaN") + case math.IsInf(val, 1): + appendJSONString(dst, "Infinity") + case math.IsInf(val, -1): + appendJSONString(dst, "-Infinity") + default: + var buf [float64ArrayElemMaxLen]byte + out := strconv.AppendFloat(buf[:0], val, 'g', -1, 64) + _, _ = dst.Write(out) + } + case FLOAT64SLICE: + appendFloat64SliceValue(dst, v.slice) + case STRING: + appendJSONString(dst, v.stringly) + case STRINGSLICE: + appendStringSliceValue(dst, v.slice) + case BYTESLICE: + _ = dst.WriteByte('"') + appendBase64(dst, v.stringly) + _ = dst.WriteByte('"') + case SLICE: + appendValueSliceValue(dst, v.slice) + case EMPTY: + _, _ = dst.WriteString("null") + default: + appendJSONString(dst, "unknown") + } +} + +// appendJSONString appends s to dst as a JSON string literal. +// +// This is adapted from the Go standard library's encoding/json +// [appendString implementation]. It keeps the same escaping behavior we need +// here, but writes directly into a strings.Builder and intentionally does not +// apply HTML escaping because the OpenTelemetry non-OTLP AnyValue representation +// only requires JSON array string encoding. We inline this instead of using +// encoding/json so slice formatting avoids allocations and reflection. +// +// [appendString implementation]: https://github.com/golang/go/blob/3b5954c6349d31465dca409b45ab6597e0942d9f/src/encoding/json/encode.go#L998-L1064 +func appendJSONString(dst *strings.Builder, s string) { + const hex = "0123456789abcdef" // For escaping bytes to hex. + + _ = dst.WriteByte('"') + start := 0 + + for i := 0; i < len(s); { + if c := s[i]; c < utf8.RuneSelf { + if c >= 0x20 && c != '\\' && c != '"' { + i++ + continue + } + + if start < i { + _, _ = dst.WriteString(s[start:i]) + } + + switch c { + case '\\', '"': + _ = dst.WriteByte('\\') + _ = dst.WriteByte(c) + case '\b': + _, _ = dst.WriteString(`\b`) + case '\f': + _, _ = dst.WriteString(`\f`) + case '\n': + _, _ = dst.WriteString(`\n`) + case '\r': + _, _ = dst.WriteString(`\r`) + case '\t': + _, _ = dst.WriteString(`\t`) + default: + _, _ = dst.WriteString(`\u00`) + _ = dst.WriteByte(hex[c>>4]) + _ = dst.WriteByte(hex[c&0x0f]) + } + + i++ + start = i + continue + } + + r, size := utf8.DecodeRuneInString(s[i:]) + if r == utf8.RuneError && size == 1 { + if start < i { + _, _ = dst.WriteString(s[start:i]) + } + // Match encoding/json by replacing invalid UTF-8 with U+FFFD. + _, _ = dst.WriteString(`\ufffd`) + i++ + start = i + continue + } + + if r == '\u2028' || r == '\u2029' { + if start < i { + _, _ = dst.WriteString(s[start:i]) + } + // Escape JSONP-sensitive separators unconditionally, like encoding/json. + _, _ = dst.WriteString(`\u202`) + _ = dst.WriteByte(hex[r&0x0f]) + i += size + start = i + continue + } + + i += size + } + + if start < len(s) { + _, _ = dst.WriteString(s[start:]) + } + _ = dst.WriteByte('"') +} + +// This is adapted from the Go standard library's encoding/base64 +// [Encoding.Encode implementation]. It keeps the same encoding behavior we need +// here, but writes directly into a strings.Builder. We inline this instead of using +// encoding/base64 to avoid allocations. +// +// [Encoding.Encode implementation]: https://github.com/golang/go/blob/3b5954c6349d31465dca409b45ab6597e0942d9f/src/encoding/base64/base64.go#L139-L189 +func appendBase64(dst *strings.Builder, s string) { + const encode = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" + + dst.Grow(base64.StdEncoding.EncodedLen(len(s))) + + i := 0 + for ; i+2 < len(s); i += 3 { + n := uint32(s[i])<<16 | uint32(s[i+1])<<8 | uint32(s[i+2]) + _ = dst.WriteByte(encode[n>>18&0x3f]) + _ = dst.WriteByte(encode[n>>12&0x3f]) + _ = dst.WriteByte(encode[n>>6&0x3f]) + _ = dst.WriteByte(encode[n&0x3f]) + } + + switch len(s) - i { + case 1: + n := uint32(s[i]) << 16 + _ = dst.WriteByte(encode[n>>18&0x3f]) + _ = dst.WriteByte(encode[n>>12&0x3f]) + _ = dst.WriteByte('=') + _ = dst.WriteByte('=') + case 2: + n := uint32(s[i])<<16 | uint32(s[i+1])<<8 + _ = dst.WriteByte(encode[n>>18&0x3f]) + _ = dst.WriteByte(encode[n>>12&0x3f]) + _ = dst.WriteByte(encode[n>>6&0x3f]) + _ = dst.WriteByte('=') + } +} + // MarshalJSON returns the JSON encoding of the Value. func (v Value) MarshalJSON() ([]byte, error) { var jsonVal struct { diff --git a/vendor/go.opentelemetry.io/otel/baggage/baggage.go b/vendor/go.opentelemetry.io/otel/baggage/baggage.go index 878ffbe43a..b290c6d6cc 100644 --- a/vendor/go.opentelemetry.io/otel/baggage/baggage.go +++ b/vendor/go.opentelemetry.io/otel/baggage/baggage.go @@ -14,6 +14,10 @@ import ( ) const ( + maxParseErrors = 5 + + // W3C Baggage specification limits. + // https://www.w3.org/TR/baggage/#limits maxMembers = 64 maxBytesPerBaggageString = 8192 @@ -493,9 +497,15 @@ func New(members ...Member) (Baggage, error) { // from the W3C Baggage specification which allows duplicate list-members, but // conforms to the OpenTelemetry Baggage specification. // -// If the baggage-string exceeds the maximum allowed members (64) or bytes -// (8192), members are dropped until the limits are satisfied and an error is -// returned along with the partial result. +// If the raw baggage-string exceeds the maximum allowed bytes (8192), an +// empty Baggage and an error are returned. +// +// Otherwise, members are parsed left-to-right and accumulated until one of +// the following conditions is reached, at which point parsing stops and an +// error is returned alongside the partial result: +// - accepting the next member would cause the encoded baggage to exceed +// 8192 bytes, or +// - the baggage already contains 64 distinct keys. // // Invalid members are skipped and the error is returned along with the // partial result containing the valid members. @@ -504,9 +514,14 @@ func Parse(bStr string) (Baggage, error) { return Baggage{}, nil } + if n := len(bStr); n > maxBytesPerBaggageString { + return Baggage{}, fmt.Errorf("%w: %d", errBaggageBytes, n) + } + b := make(baggage.List) sizes := make(map[string]int) // Track per-key byte sizes var totalBytes int + var parseErrors int var truncateErr error for memberStr := range strings.SplitSeq(bStr, listDelimiter) { // Check member count limit. @@ -517,7 +532,10 @@ func Parse(bStr string) (Baggage, error) { m, err := parseMember(memberStr) if err != nil { - truncateErr = errors.Join(truncateErr, err) + parseErrors++ + if parseErrors <= maxParseErrors { + truncateErr = errors.Join(truncateErr, err) + } continue // skip invalid member, keep processing } @@ -553,6 +571,10 @@ func Parse(bStr string) (Baggage, error) { totalBytes = newTotalBytes } + if dropped := parseErrors - maxParseErrors; dropped > 0 { + truncateErr = errors.Join(truncateErr, fmt.Errorf("and %d more invalid member(s)", dropped)) + } + if len(b) == 0 { return Baggage{}, truncateErr } diff --git a/vendor/go.opentelemetry.io/otel/dependencies.Dockerfile b/vendor/go.opentelemetry.io/otel/dependencies.Dockerfile index 7a9b3c0559..74fa510bc8 100644 --- a/vendor/go.opentelemetry.io/otel/dependencies.Dockerfile +++ b/vendor/go.opentelemetry.io/otel/dependencies.Dockerfile @@ -1,4 +1,4 @@ # This is a renovate-friendly source of Docker images. FROM python:3.13.6-slim-bullseye@sha256:e98b521460ee75bca92175c16247bdf7275637a8faaeb2bcfa19d879ae5c4b9a AS python -FROM otel/weaver:v0.22.1@sha256:33ae522ae4b71c1c562563c1d81f46aa0f79f088a0873199143a1f11ac30e5c9 AS weaver +FROM otel/weaver:v0.23.0@sha256:7984ecb55b859eb3034ae9d836c4eeda137e2bdd0873b7ba2bb6c3d24d6ff457 AS weaver FROM avtodev/markdown-lint:v1@sha256:6aeedc2f49138ce7a1cd0adffc1b1c0321b841dc2102408967d9301c031949ee AS markdown diff --git a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform/attribute.go b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform/attribute.go index 12e243e042..0d43a5dc5b 100644 --- a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform/attribute.go +++ b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform/attribute.go @@ -87,6 +87,16 @@ func Value(v attribute.Value) *commonpb.AnyValue { av.Value = &commonpb.AnyValue_StringValue{ StringValue: v.AsString(), } + case attribute.BYTESLICE: + av.Value = &commonpb.AnyValue_BytesValue{ + BytesValue: v.AsByteSlice(), + } + case attribute.SLICE: + av.Value = &commonpb.AnyValue_ArrayValue{ + ArrayValue: &commonpb.ArrayValue{ + Values: values(v.AsSlice()), + }, + } case attribute.STRINGSLICE: av.Value = &commonpb.AnyValue_ArrayValue{ ArrayValue: &commonpb.ArrayValue{ @@ -149,3 +159,11 @@ func stringSliceValues(vals []string) []*commonpb.AnyValue { } return converted } + +func values(vals []attribute.Value) []*commonpb.AnyValue { + converted := make([]*commonpb.AnyValue, len(vals)) + for i, v := range vals { + converted[i] = Value(v) + } + return converted +} diff --git a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/client.go b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/client.go index 258d0ca6a5..e8b33d3fa5 100644 --- a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/client.go +++ b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/client.go @@ -6,6 +6,7 @@ package otlptracegrpc // import "go.opentelemetry.io/otel/exporters/otlp/otlptra import ( "context" "errors" + "fmt" "sync" "time" @@ -16,6 +17,7 @@ import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" "go.opentelemetry.io/otel/exporters/otlp/otlptrace" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal" @@ -26,11 +28,12 @@ import ( ) type client struct { - endpoint string - dialOpts []grpc.DialOption - metadata metadata.MD - exportTimeout time.Duration - requestFunc retry.RequestFunc + endpoint string + dialOpts []grpc.DialOption + metadata metadata.MD + exportTimeout time.Duration + maxRequestSize int + requestFunc retry.RequestFunc // stopCtx is used as a parent context for all exports. Therefore, when it // is canceled with the stopFunc all exports are canceled. @@ -65,14 +68,15 @@ func newClient(opts ...Option) *client { ctx, cancel := context.WithCancel(context.Background()) //nolint:gosec // cancel called in client shutdown. c := &client{ - endpoint: cfg.Traces.Endpoint, - exportTimeout: cfg.Traces.Timeout, - requestFunc: cfg.RetryConfig.RequestFunc(retryable), - dialOpts: cfg.DialOptions, - stopCtx: ctx, - stopFunc: cancel, - conn: cfg.GRPCConn, - instID: counter.NextExporterID(), + endpoint: cfg.Traces.Endpoint, + exportTimeout: cfg.Traces.Timeout, + maxRequestSize: cfg.Traces.MaxRequestSize, + requestFunc: cfg.RetryConfig.RequestFunc(retryable), + dialOpts: cfg.DialOptions, + stopCtx: ctx, + stopFunc: cancel, + conn: cfg.GRPCConn, + instID: counter.NextExporterID(), } if len(cfg.Traces.Headers) > 0 { @@ -205,16 +209,28 @@ func (c *client) UploadTraces(ctx context.Context, protoSpans []*tracepb.Resourc ctx, cancel := c.exportContext(ctx) defer cancel() - var code codes.Code + pbRequest := &coltracepb.ExportTraceServiceRequest{ + ResourceSpans: protoSpans, + } + + code := codes.Unknown if c.inst != nil { - op := c.inst.ExportSpans(ctx, len(protoSpans)) + var spanCount int + for _, rs := range protoSpans { + for _, ss := range rs.ScopeSpans { + spanCount += len(ss.Spans) + } + } + op := c.inst.ExportSpans(ctx, spanCount) defer func() { op.End(uploadErr, code) }() } + if maxSize := c.maxRequestSize; maxSize > 0 && proto.Size(pbRequest) > maxSize { + return fmt.Errorf("request message too large: exceeded %d bytes", maxSize) + } + return c.requestFunc(ctx, func(iCtx context.Context) error { - resp, err := c.tsc.Export(iCtx, &coltracepb.ExportTraceServiceRequest{ - ResourceSpans: protoSpans, - }) + resp, err := c.tsc.Export(iCtx, pbRequest) if resp != nil && resp.PartialSuccess != nil { msg := resp.PartialSuccess.GetErrorMessage() n := resp.PartialSuccess.GetRejectedSpans() diff --git a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/observ/instrumentation.go b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/observ/instrumentation.go index a84733174e..676a93514b 100644 --- a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/observ/instrumentation.go +++ b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/observ/instrumentation.go @@ -18,8 +18,8 @@ import ( "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/x" "go.opentelemetry.io/otel/internal/global" "go.opentelemetry.io/otel/metric" - semconv "go.opentelemetry.io/otel/semconv/v1.40.0" - "go.opentelemetry.io/otel/semconv/v1.40.0/otelconv" + semconv "go.opentelemetry.io/otel/semconv/v1.41.0" + "go.opentelemetry.io/otel/semconv/v1.41.0/otelconv" ) const ( @@ -72,6 +72,7 @@ var ( func get[T any](p *sync.Pool) *[]T { return p.Get().(*[]T) } func put[T any](p *sync.Pool, s *[]T) { + clear(*s) // erase elements to allow GC to collect what they refer to. *s = (*s)[:0] // Reset. p.Put(s) } @@ -339,7 +340,10 @@ var errPartialPool = &sync.Pool{ // the provided non-nil err. func rejected(n int64, err error) int64 { ps := errPartialPool.Get().(*internal.PartialSuccess) - defer errPartialPool.Put(ps) + defer func() { + *ps = internal.PartialSuccess{} // erase fields to allow GC to collect them. + errPartialPool.Put(ps) + }() // Check for partial success. if errors.As(err, ps) { // Bound RejectedItems to [0, n]. This should not be needed, diff --git a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/otlpconfig/options.go b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/otlpconfig/options.go index 4f47117a58..d940b67625 100644 --- a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/otlpconfig/options.go +++ b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/otlpconfig/options.go @@ -31,6 +31,9 @@ const ( // DefaultTracesPath is a default URL path for endpoint that // receives spans. DefaultTracesPath string = "/v1/traces" + // DefaultMaxRequestSize is the default maximum size of a serialized export + // request, before compression. + DefaultMaxRequestSize int = 64 * 1024 * 1024 // DefaultTimeout is a default max waiting time for the backend to process // each span batch. DefaultTimeout time.Duration = 10 * time.Second @@ -42,13 +45,14 @@ type ( HTTPTransportProxyFunc func(*http.Request) (*url.URL, error) SignalConfig struct { - Endpoint string - Insecure bool - TLSCfg *tls.Config - Headers map[string]string - Compression Compression - Timeout time.Duration - URLPath string + Endpoint string + Insecure bool + TLSCfg *tls.Config + Headers map[string]string + Compression Compression + MaxRequestSize int + Timeout time.Duration + URLPath string // gRPC configurations GRPCCredentials credentials.TransportCredentials @@ -77,10 +81,11 @@ type ( func NewHTTPConfig(opts ...HTTPOption) Config { cfg := Config{ Traces: SignalConfig{ - Endpoint: fmt.Sprintf("%s:%d", DefaultCollectorHost, DefaultCollectorHTTPPort), - URLPath: DefaultTracesPath, - Compression: NoCompression, - Timeout: DefaultTimeout, + Endpoint: fmt.Sprintf("%s:%d", DefaultCollectorHost, DefaultCollectorHTTPPort), + URLPath: DefaultTracesPath, + Compression: NoCompression, + MaxRequestSize: DefaultMaxRequestSize, + Timeout: DefaultTimeout, }, RetryConfig: retry.DefaultConfig, } @@ -111,10 +116,11 @@ func NewGRPCConfig(opts ...GRPCOption) Config { userAgent := "OTel OTLP Exporter Go/" + otlptrace.Version() cfg := Config{ Traces: SignalConfig{ - Endpoint: fmt.Sprintf("%s:%d", DefaultCollectorHost, DefaultCollectorGRPCPort), - URLPath: DefaultTracesPath, - Compression: NoCompression, - Timeout: DefaultTimeout, + Endpoint: fmt.Sprintf("%s:%d", DefaultCollectorHost, DefaultCollectorGRPCPort), + URLPath: DefaultTracesPath, + Compression: NoCompression, + MaxRequestSize: DefaultMaxRequestSize, + Timeout: DefaultTimeout, }, RetryConfig: retry.DefaultConfig, DialOptions: []grpc.DialOption{grpc.WithUserAgent(userAgent)}, @@ -345,6 +351,13 @@ func WithTimeout(duration time.Duration) GenericOption { }) } +func WithMaxRequestSize(size int) GenericOption { + return newGenericOption(func(cfg Config) Config { + cfg.Traces.MaxRequestSize = size + return cfg + }) +} + func WithProxy(pf HTTPTransportProxyFunc) GenericOption { return newGenericOption(func(cfg Config) Config { cfg.Traces.Proxy = pf diff --git a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/version.go b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/version.go index 7a1c420ecb..28e51e443f 100644 --- a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/version.go +++ b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/version.go @@ -5,4 +5,4 @@ package internal // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/ot // Version is the current release version of the OpenTelemetry OTLP gRPC trace // exporter in use. -const Version = "1.43.0" +const Version = "1.44.0" diff --git a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/options.go b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/options.go index 2da2298701..b320060525 100644 --- a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/options.go +++ b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/options.go @@ -192,6 +192,16 @@ func WithTimeout(duration time.Duration) Option { return wrappedOption{otlpconfig.WithTimeout(duration)} } +// WithMaxRequestSize sets the maximum size, in bytes, of a serialized export +// request, before compression, that the exporter will send. +// +// If size is less than or equal to zero, no request-size limit is applied. +// Disabling the limit is not recommended because it can lead to excessive +// resource consumption or abuse. +func WithMaxRequestSize(size int) Option { + return wrappedOption{otlpconfig.WithMaxRequestSize(size)} +} + // WithRetry sets the retry policy for transient retryable errors that may be // returned by the target endpoint when exporting a batch of spans. // diff --git a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/client.go b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/client.go index 4ae569ff4b..f81098b381 100644 --- a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/client.go +++ b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/client.go @@ -143,9 +143,9 @@ func (c *client) Start(ctx context.Context) error { } // Stop shuts down the client and interrupt any in-flight request. -func (d *client) Stop(ctx context.Context) error { - d.stopOnce.Do(func() { - close(d.stopCh) +func (c *client) Stop(ctx context.Context) error { + c.stopOnce.Do(func() { + close(c.stopCh) }) select { case <-ctx.Done(): @@ -156,7 +156,7 @@ func (d *client) Stop(ctx context.Context) error { } // UploadTraces sends a batch of spans to the collector. -func (d *client) UploadTraces(ctx context.Context, protoSpans []*tracepb.ResourceSpans) (uploadErr error) { +func (c *client) UploadTraces(ctx context.Context, protoSpans []*tracepb.ResourceSpans) (uploadErr error) { pbRequest := &coltracepb.ExportTraceServiceRequest{ ResourceSpans: protoSpans, } @@ -165,30 +165,41 @@ func (d *client) UploadTraces(ctx context.Context, protoSpans []*tracepb.Resourc return err } - ctx, cancel := d.contextWithStop(ctx) + ctx, cancel := c.contextWithStop(ctx) defer cancel() - request, err := d.newRequest(rawRequest) + if maxSize := c.cfg.MaxRequestSize; maxSize > 0 && len(rawRequest) > maxSize { + return fmt.Errorf("request body too large: exceeded %d bytes", maxSize) + } + + request, err := c.newRequest(rawRequest) if err != nil { return err } var statusCode int - if d.inst != nil { - op := d.inst.ExportSpans(ctx, len(protoSpans)) + if c.inst != nil { + var spanCount int + for _, rs := range protoSpans { + for _, ss := range rs.ScopeSpans { + spanCount += len(ss.Spans) + } + } + op := c.inst.ExportSpans(ctx, spanCount) defer func() { op.End(uploadErr, statusCode) }() } - return errors.Join(uploadErr, d.requestFunc(ctx, func(ctx context.Context) error { + return errors.Join(uploadErr, c.requestFunc(ctx, func(ctx context.Context) error { select { case <-ctx.Done(): return ctx.Err() default: } + statusCode = 0 request.reset(ctx) // nolint:gosec // URL is constructed from validated OTLP endpoint configuration - resp, err := d.client.Do(request.Request) + resp, err := c.client.Do(request.Request) var urlErr *url.Error if errors.As(err, &urlErr) && urlErr.Temporary() { return newResponseError(http.Header{}, err) @@ -272,8 +283,8 @@ func (d *client) UploadTraces(ctx context.Context, protoSpans []*tracepb.Resourc })) } -func (d *client) newRequest(body []byte) (request, error) { - u := url.URL{Scheme: d.getScheme(), Host: d.cfg.Endpoint, Path: d.cfg.URLPath} +func (c *client) newRequest(body []byte) (request, error) { + u := url.URL{Scheme: c.getScheme(), Host: c.cfg.Endpoint, Path: c.cfg.URLPath} r, err := http.NewRequestWithContext(context.Background(), http.MethodPost, u.String(), http.NoBody) if err != nil { return request{Request: r}, err @@ -282,13 +293,13 @@ func (d *client) newRequest(body []byte) (request, error) { userAgent := "OTel OTLP Exporter Go/" + otlptrace.Version() r.Header.Set("User-Agent", userAgent) - for k, v := range d.cfg.Headers { + for k, v := range c.cfg.Headers { r.Header.Set(k, v) } r.Header.Set("Content-Type", contentTypeProto) req := request{Request: r} - switch Compression(d.cfg.Compression) { + switch Compression(c.cfg.Compression) { case NoCompression: r.ContentLength = int64(len(body)) req.bodyReader = bodyReader(body) @@ -299,7 +310,10 @@ func (d *client) newRequest(body []byte) (request, error) { r.Header.Set("Content-Encoding", "gzip") gz := gzPool.Get().(*gzip.Writer) - defer gzPool.Put(gz) + defer func() { + gz.Reset(io.Discard) + gzPool.Put(gz) + }() var b bytes.Buffer gz.Reset(&b) @@ -320,15 +334,15 @@ func (d *client) newRequest(body []byte) (request, error) { } // MarshalLog is the marshaling function used by the logging system to represent this Client. -func (d *client) MarshalLog() any { +func (c *client) MarshalLog() any { return struct { Type string Endpoint string Insecure bool }{ Type: "otlptracehttp", - Endpoint: d.cfg.Endpoint, - Insecure: d.cfg.Insecure, + Endpoint: c.cfg.Endpoint, + Insecure: c.cfg.Insecure, } } @@ -425,14 +439,14 @@ func evaluate(err error) (bool, time.Duration) { return true, time.Duration(rErr.throttle) } -func (d *client) getScheme() string { - if d.cfg.Insecure { +func (c *client) getScheme() string { + if c.cfg.Insecure { return "http" } return "https" } -func (d *client) contextWithStop(ctx context.Context) (context.Context, context.CancelFunc) { +func (c *client) contextWithStop(ctx context.Context) (context.Context, context.CancelFunc) { // Unify the parent context Done signal with the client's stop // channel. ctx, cancel := context.WithCancel(ctx) @@ -441,7 +455,7 @@ func (d *client) contextWithStop(ctx context.Context) (context.Context, context. case <-ctx.Done(): // Nothing to do, either cancelled or deadline // happened. - case <-d.stopCh: + case <-c.stopCh: cancel() } }(ctx, cancel) diff --git a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal/observ/instrumentation.go b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal/observ/instrumentation.go index 3f2556e7a6..1f4fc55c6f 100644 --- a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal/observ/instrumentation.go +++ b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal/observ/instrumentation.go @@ -23,8 +23,8 @@ import ( "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal/x" "go.opentelemetry.io/otel/internal/global" "go.opentelemetry.io/otel/metric" - semconv "go.opentelemetry.io/otel/semconv/v1.40.0" - "go.opentelemetry.io/otel/semconv/v1.40.0/otelconv" + semconv "go.opentelemetry.io/otel/semconv/v1.41.0" + "go.opentelemetry.io/otel/semconv/v1.41.0/otelconv" ) const ( @@ -77,6 +77,7 @@ var ( func get[T any](p *sync.Pool) *[]T { return p.Get().(*[]T) } func put[T any](p *sync.Pool, s *[]T) { + clear(*s) // erase elements to allow GC to collect what they refer to. *s = (*s)[:0] // Reset. p.Put(s) } @@ -167,7 +168,7 @@ func NewInstrumentation(id int64, endpoint string) (*Instrumentation, error) { // to set the "component.name" attribute. // // The endpoint is the HTTP endpoint the exporter is exporting to. It should be -// in the format "host:port" or a full URL. +// in the format "host[:port]". func BaseAttrs(id int64, endpoint string) []attribute.KeyValue { host, port, err := parseEndpoint(endpoint) if err != nil || (host == "" && port < 0) { @@ -345,7 +346,7 @@ func (e ExportOp) End(err error, status int) { // // Otherwise, a new RecordOption is returned with the base attributes of the // Instrumentation plus the http.response.status_code attribute set to the -// provided status, and if err is not nil, the error.type attribute set +// provided status (if non-zero), and if err is not nil, the error.type attribute set // to the type of the error. func (i *Instrumentation) recordOption(err error, status int) metric.RecordOption { if err == nil && status == http.StatusOK { @@ -356,7 +357,9 @@ func (i *Instrumentation) recordOption(err error, status int) metric.RecordOptio defer put(measureAttrsPool, attrs) *attrs = append(*attrs, i.attrs...) - *attrs = append(*attrs, semconv.HTTPResponseStatusCode(status)) + if status != 0 { + *attrs = append(*attrs, semconv.HTTPResponseStatusCode(status)) + } if err != nil { *attrs = append(*attrs, semconv.ErrorType(err)) } @@ -394,7 +397,10 @@ var errPartialPool = &sync.Pool{ // the provided non-nil err. func rejected(n int64, err error) int64 { ps := errPartialPool.Get().(*internal.PartialSuccess) - defer errPartialPool.Put(ps) + defer func() { + *ps = internal.PartialSuccess{} // erase fields to allow GC to collect them. + errPartialPool.Put(ps) + }() // Check for partial success. if errors.As(err, ps) { // Bound RejectedItems to [0, n]. This should not be needed, diff --git a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal/otlpconfig/options.go b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal/otlpconfig/options.go index e415feea6e..2abde66150 100644 --- a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal/otlpconfig/options.go +++ b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal/otlpconfig/options.go @@ -31,6 +31,9 @@ const ( // DefaultTracesPath is a default URL path for endpoint that // receives spans. DefaultTracesPath string = "/v1/traces" + // DefaultMaxRequestSize is the default maximum size of a serialized export + // request, before compression. + DefaultMaxRequestSize int = 64 * 1024 * 1024 // DefaultTimeout is a default max waiting time for the backend to process // each span batch. DefaultTimeout time.Duration = 10 * time.Second @@ -42,13 +45,14 @@ type ( HTTPTransportProxyFunc func(*http.Request) (*url.URL, error) SignalConfig struct { - Endpoint string - Insecure bool - TLSCfg *tls.Config - Headers map[string]string - Compression Compression - Timeout time.Duration - URLPath string + Endpoint string + Insecure bool + TLSCfg *tls.Config + Headers map[string]string + Compression Compression + MaxRequestSize int + Timeout time.Duration + URLPath string // gRPC configurations GRPCCredentials credentials.TransportCredentials @@ -77,10 +81,11 @@ type ( func NewHTTPConfig(opts ...HTTPOption) Config { cfg := Config{ Traces: SignalConfig{ - Endpoint: fmt.Sprintf("%s:%d", DefaultCollectorHost, DefaultCollectorHTTPPort), - URLPath: DefaultTracesPath, - Compression: NoCompression, - Timeout: DefaultTimeout, + Endpoint: fmt.Sprintf("%s:%d", DefaultCollectorHost, DefaultCollectorHTTPPort), + URLPath: DefaultTracesPath, + Compression: NoCompression, + MaxRequestSize: DefaultMaxRequestSize, + Timeout: DefaultTimeout, }, RetryConfig: retry.DefaultConfig, } @@ -111,10 +116,11 @@ func NewGRPCConfig(opts ...GRPCOption) Config { userAgent := "OTel OTLP Exporter Go/" + otlptrace.Version() cfg := Config{ Traces: SignalConfig{ - Endpoint: fmt.Sprintf("%s:%d", DefaultCollectorHost, DefaultCollectorGRPCPort), - URLPath: DefaultTracesPath, - Compression: NoCompression, - Timeout: DefaultTimeout, + Endpoint: fmt.Sprintf("%s:%d", DefaultCollectorHost, DefaultCollectorGRPCPort), + URLPath: DefaultTracesPath, + Compression: NoCompression, + MaxRequestSize: DefaultMaxRequestSize, + Timeout: DefaultTimeout, }, RetryConfig: retry.DefaultConfig, DialOptions: []grpc.DialOption{grpc.WithUserAgent(userAgent)}, @@ -345,6 +351,13 @@ func WithTimeout(duration time.Duration) GenericOption { }) } +func WithMaxRequestSize(size int) GenericOption { + return newGenericOption(func(cfg Config) Config { + cfg.Traces.MaxRequestSize = size + return cfg + }) +} + func WithProxy(pf HTTPTransportProxyFunc) GenericOption { return newGenericOption(func(cfg Config) Config { cfg.Traces.Proxy = pf diff --git a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal/version.go b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal/version.go index 3e43f77113..882b671380 100644 --- a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal/version.go +++ b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal/version.go @@ -5,4 +5,4 @@ package internal // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/ot // Version is the current release version of the OpenTelemetry OTLP HTTP trace // exporter in use. -const Version = "1.43.0" +const Version = "1.44.0" diff --git a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/options.go b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/options.go index cfe21dbfb0..291b5f9b64 100644 --- a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/options.go +++ b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/options.go @@ -138,6 +138,16 @@ func WithTimeout(duration time.Duration) Option { return wrappedOption{otlpconfig.WithTimeout(duration)} } +// WithMaxRequestSize sets the maximum size, in bytes, of a serialized export +// request, before compression, that the exporter will send. +// +// If size is less than or equal to zero, no request-size limit is applied. +// Disabling the limit is not recommended because it can lead to excessive +// resource consumption or abuse. +func WithMaxRequestSize(size int) Option { + return wrappedOption{otlpconfig.WithMaxRequestSize(size)} +} + // WithRetry configures the retry policy for transient errors that may occurs // when exporting traces. An exponential back-off algorithm is used to ensure // endpoints are not overwhelmed with retries. If unset, the default retry diff --git a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/version.go b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/version.go index 087e95f7b8..d847210dba 100644 --- a/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/version.go +++ b/vendor/go.opentelemetry.io/otel/exporters/otlp/otlptrace/version.go @@ -5,5 +5,5 @@ package otlptrace // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace" // Version is the current release version of the OpenTelemetry OTLP trace exporter in use. func Version() string { - return "1.43.0" + return "1.44.0" } diff --git a/vendor/go.opentelemetry.io/otel/metric/asyncfloat64.go b/vendor/go.opentelemetry.io/otel/metric/asyncfloat64.go index 466812d343..1d21e2eb75 100644 --- a/vendor/go.opentelemetry.io/otel/metric/asyncfloat64.go +++ b/vendor/go.opentelemetry.io/otel/metric/asyncfloat64.go @@ -51,6 +51,9 @@ type Float64ObservableCounterConfig struct { func NewFloat64ObservableCounterConfig(opts ...Float64ObservableCounterOption) Float64ObservableCounterConfig { var config Float64ObservableCounterConfig for _, o := range opts { + if _, ok := o.(experimentalOption); ok { + continue + } config = o.applyFloat64ObservableCounter(config) } return config @@ -111,6 +114,9 @@ func NewFloat64ObservableUpDownCounterConfig( ) Float64ObservableUpDownCounterConfig { var config Float64ObservableUpDownCounterConfig for _, o := range opts { + if _, ok := o.(experimentalOption); ok { + continue + } config = o.applyFloat64ObservableUpDownCounter(config) } return config @@ -168,6 +174,9 @@ type Float64ObservableGaugeConfig struct { func NewFloat64ObservableGaugeConfig(opts ...Float64ObservableGaugeOption) Float64ObservableGaugeConfig { var config Float64ObservableGaugeConfig for _, o := range opts { + if _, ok := o.(experimentalOption); ok { + continue + } config = o.applyFloat64ObservableGauge(config) } return config diff --git a/vendor/go.opentelemetry.io/otel/metric/asyncint64.go b/vendor/go.opentelemetry.io/otel/metric/asyncint64.go index 66c971bd8a..9d45a4d416 100644 --- a/vendor/go.opentelemetry.io/otel/metric/asyncint64.go +++ b/vendor/go.opentelemetry.io/otel/metric/asyncint64.go @@ -50,6 +50,9 @@ type Int64ObservableCounterConfig struct { func NewInt64ObservableCounterConfig(opts ...Int64ObservableCounterOption) Int64ObservableCounterConfig { var config Int64ObservableCounterConfig for _, o := range opts { + if _, ok := o.(experimentalOption); ok { + continue + } config = o.applyInt64ObservableCounter(config) } return config @@ -110,6 +113,9 @@ func NewInt64ObservableUpDownCounterConfig( ) Int64ObservableUpDownCounterConfig { var config Int64ObservableUpDownCounterConfig for _, o := range opts { + if _, ok := o.(experimentalOption); ok { + continue + } config = o.applyInt64ObservableUpDownCounter(config) } return config @@ -167,6 +173,9 @@ type Int64ObservableGaugeConfig struct { func NewInt64ObservableGaugeConfig(opts ...Int64ObservableGaugeOption) Int64ObservableGaugeConfig { var config Int64ObservableGaugeConfig for _, o := range opts { + if _, ok := o.(experimentalOption); ok { + continue + } config = o.applyInt64ObservableGauge(config) } return config diff --git a/vendor/go.opentelemetry.io/otel/metric/config.go b/vendor/go.opentelemetry.io/otel/metric/config.go index e42dd6e70a..889545e235 100644 --- a/vendor/go.opentelemetry.io/otel/metric/config.go +++ b/vendor/go.opentelemetry.io/otel/metric/config.go @@ -42,11 +42,18 @@ type MeterOption interface { applyMeter(MeterConfig) MeterConfig } +type experimentalOption interface { + Experimental() +} + // NewMeterConfig creates a new MeterConfig and applies // all the given options. func NewMeterConfig(opts ...MeterOption) MeterConfig { var config MeterConfig for _, o := range opts { + if _, ok := o.(experimentalOption); ok { + continue + } config = o.applyMeter(config) } return config diff --git a/vendor/go.opentelemetry.io/otel/metric/doc.go b/vendor/go.opentelemetry.io/otel/metric/doc.go index f153745b00..794e1a8bae 100644 --- a/vendor/go.opentelemetry.io/otel/metric/doc.go +++ b/vendor/go.opentelemetry.io/otel/metric/doc.go @@ -24,10 +24,10 @@ all instruments fall into two overlapping logical categories: asynchronous or synchronous, and int64 or float64. All synchronous instruments ([Int64Counter], [Int64UpDownCounter], -[Int64Histogram], [Float64Counter], [Float64UpDownCounter], and -[Float64Histogram]) are used to measure the operation and performance of source -code during the source code execution. These instruments only make measurements -when the source code they instrument is run. +[Int64Histogram], [Int64Gauge], [Float64Counter], [Float64UpDownCounter], +[Float64Histogram], and [Float64Gauge]) are used to measure the operation and +performance of source code during the source code execution. These instruments +only make measurements when the source code they instrument is run. All asynchronous instruments ([Int64ObservableCounter], [Int64ObservableUpDownCounter], [Int64ObservableGauge], @@ -50,9 +50,11 @@ incrementally increase in value. UpDownCounters ([Int64UpDownCounter], values that can increase and decrease. When more information needs to be conveyed about all the synchronous measurements made during a collection cycle, a Histogram ([Int64Histogram] and [Float64Histogram]) should be used. Finally, -when just the most recent measurement needs to be conveyed about an -asynchronous measurement, a Gauge ([Int64ObservableGauge] and -[Float64ObservableGauge]) should be used. +when just the most recent measurement needs to be conveyed, a Gauge +([Int64Gauge], [Float64Gauge], [Int64ObservableGauge], and +[Float64ObservableGauge]) should be used: the synchronous variants record an +instantaneous value at a specific point in code, while the observable variants +sample the value via a callback once per collection cycle. See the [OpenTelemetry documentation] for more information about instruments and their intended use. @@ -80,11 +82,11 @@ Measurements are made by recording values and information about the values with an instrument. How these measurements are recorded depends on the instrument. Measurements for synchronous instruments ([Int64Counter], [Int64UpDownCounter], -[Int64Histogram], [Float64Counter], [Float64UpDownCounter], and -[Float64Histogram]) are recorded using the instrument methods directly. All -counter instruments have an Add method that is used to measure an increment -value, and all histogram instruments have a Record method to measure a data -point. +[Int64Histogram], [Int64Gauge], [Float64Counter], [Float64UpDownCounter], +[Float64Histogram], and [Float64Gauge]) are recorded using the instrument +methods directly. All counter instruments have an Add method that is used to +measure an increment value, and all histogram and synchronous gauge +instruments have a Record method to measure a data point. Asynchronous instruments ([Int64ObservableCounter], [Int64ObservableUpDownCounter], [Int64ObservableGauge], @@ -107,6 +109,31 @@ respectively): If the criteria are not met, use the RegisterCallback method of the [Meter] that created the instrument to register a [Callback]. +# Avoiding Expensive Computations + +All synchronous instruments provide an Enabled method that reports whether the +instrument will process measurements for the given context. When no SDK is +registered or the instrument is otherwise disabled, Enabled returns false. This +can be used to avoid expensive measurement work when a measurement will not be +recorded: + + if counter.Enabled(ctx) { + counter.Add(ctx, 1, metric.WithAttributes(expensiveAttributes()...)) + } + +This is especially valuable when computing attributes is expensive. +[WithAttributes] performs non-trivial work on every call to build an +[attribute.Set] from the provided attributes, and that work is wasted if the +measurement is not recorded. + +For performance sensitive code where the same attribute set is used repeatedly, +prefer [WithAttributeSet]. It accepts a pre-built [attribute.Set], letting you +pay the construction cost once and reuse it across many measurements: + + attrs := attribute.NewSet(attribute.String("key", "val")) + // ... later, on each call: + counter.Add(ctx, 1, metric.WithAttributeSet(attrs)) + # API Implementations This package does not conform to the standard Go versioning policy, all of its diff --git a/vendor/go.opentelemetry.io/otel/metric/instrument.go b/vendor/go.opentelemetry.io/otel/metric/instrument.go index 9f48d5f117..2e79ab5683 100644 --- a/vendor/go.opentelemetry.io/otel/metric/instrument.go +++ b/vendor/go.opentelemetry.io/otel/metric/instrument.go @@ -3,7 +3,9 @@ package metric // import "go.opentelemetry.io/otel/metric" -import "go.opentelemetry.io/otel/attribute" +import ( + "go.opentelemetry.io/otel/attribute" +) // Observable is used as a grouping mechanism for all instruments that are // updated within a Callback. @@ -228,6 +230,9 @@ type AddConfig struct { func NewAddConfig(opts []AddOption) AddConfig { config := AddConfig{attrs: *attribute.EmptySet()} for _, o := range opts { + if _, ok := o.(experimentalOption); ok { + continue + } config = o.applyAdd(config) } return config @@ -253,6 +258,9 @@ type RecordConfig struct { func NewRecordConfig(opts []RecordOption) RecordConfig { config := RecordConfig{attrs: *attribute.EmptySet()} for _, o := range opts { + if _, ok := o.(experimentalOption); ok { + continue + } config = o.applyRecord(config) } return config @@ -278,6 +286,9 @@ type ObserveConfig struct { func NewObserveConfig(opts []ObserveOption) ObserveConfig { config := ObserveConfig{attrs: *attribute.EmptySet()} for _, o := range opts { + if _, ok := o.(experimentalOption); ok { + continue + } config = o.applyObserve(config) } return config @@ -299,6 +310,10 @@ type attrOpt struct { set attribute.Set } +func (o *attrOpt) Set(set attribute.Set) { + o.set = set +} + // mergeSets returns the union of keys between a and b. Any duplicate keys will // use the value associated with b. func mergeSets(a, b attribute.Set) attribute.Set { @@ -311,7 +326,7 @@ func mergeSets(a, b attribute.Set) attribute.Set { return attribute.NewSet(merged...) } -func (o attrOpt) applyAdd(c AddConfig) AddConfig { +func (o *attrOpt) applyAdd(c AddConfig) AddConfig { switch { case o.set.Len() == 0: case c.attrs.Len() == 0: @@ -322,7 +337,7 @@ func (o attrOpt) applyAdd(c AddConfig) AddConfig { return c } -func (o attrOpt) applyRecord(c RecordConfig) RecordConfig { +func (o *attrOpt) applyRecord(c RecordConfig) RecordConfig { switch { case o.set.Len() == 0: case c.attrs.Len() == 0: @@ -333,7 +348,7 @@ func (o attrOpt) applyRecord(c RecordConfig) RecordConfig { return c } -func (o attrOpt) applyObserve(c ObserveConfig) ObserveConfig { +func (o *attrOpt) applyObserve(c ObserveConfig) ObserveConfig { switch { case o.set.Len() == 0: case c.attrs.Len() == 0: @@ -350,8 +365,14 @@ func (o attrOpt) applyObserve(c ObserveConfig) ObserveConfig { // If multiple WithAttributeSet or WithAttributes options are passed the // attributes will be merged together in the order they are passed. Attributes // with duplicate keys will use the last value passed. +// +// Experimental: The returned option may implement +// [go.opentelemetry.io/otel/metric/x.Settable][attribute.Set], which can be +// used to replace the option's attribute set and reuse the option without +// additional allocations. This behavior is experimental and may be changed or +// removed in a future release without notice. func WithAttributeSet(attributes attribute.Set) MeasurementOption { - return attrOpt{set: attributes} + return &attrOpt{set: attributes} } // WithAttributes converts attributes into an attribute Set and sets the Set to @@ -369,8 +390,14 @@ func WithAttributeSet(attributes attribute.Set) MeasurementOption { // // See [WithAttributeSet] for information about how multiple WithAttributes are // merged. +// +// Experimental: The returned option may implement +// [go.opentelemetry.io/otel/metric/x.Settable][[]attribute.KeyValue], which can be +// used to replace the option's attributes and reuse the option without +// additional allocations. This behavior is experimental and may be changed or +// removed in a future release without notice. func WithAttributes(attributes ...attribute.KeyValue) MeasurementOption { cp := make([]attribute.KeyValue, len(attributes)) copy(cp, attributes) - return attrOpt{set: attribute.NewSet(cp...)} + return &attrOpt{set: attribute.NewSet(cp...)} } diff --git a/vendor/go.opentelemetry.io/otel/metric/syncfloat64.go b/vendor/go.opentelemetry.io/otel/metric/syncfloat64.go index abb3051d7f..2101f686ae 100644 --- a/vendor/go.opentelemetry.io/otel/metric/syncfloat64.go +++ b/vendor/go.opentelemetry.io/otel/metric/syncfloat64.go @@ -51,6 +51,9 @@ type Float64CounterConfig struct { func NewFloat64CounterConfig(opts ...Float64CounterOption) Float64CounterConfig { var config Float64CounterConfig for _, o := range opts { + if _, ok := o.(experimentalOption); ok { + continue + } config = o.applyFloat64Counter(config) } return config @@ -116,6 +119,9 @@ type Float64UpDownCounterConfig struct { func NewFloat64UpDownCounterConfig(opts ...Float64UpDownCounterOption) Float64UpDownCounterConfig { var config Float64UpDownCounterConfig for _, o := range opts { + if _, ok := o.(experimentalOption); ok { + continue + } config = o.applyFloat64UpDownCounter(config) } return config @@ -182,6 +188,9 @@ type Float64HistogramConfig struct { func NewFloat64HistogramConfig(opts ...Float64HistogramOption) Float64HistogramConfig { var config Float64HistogramConfig for _, o := range opts { + if _, ok := o.(experimentalOption); ok { + continue + } config = o.applyFloat64Histogram(config) } return config @@ -251,6 +260,9 @@ type Float64GaugeConfig struct { func NewFloat64GaugeConfig(opts ...Float64GaugeOption) Float64GaugeConfig { var config Float64GaugeConfig for _, o := range opts { + if _, ok := o.(experimentalOption); ok { + continue + } config = o.applyFloat64Gauge(config) } return config diff --git a/vendor/go.opentelemetry.io/otel/metric/syncint64.go b/vendor/go.opentelemetry.io/otel/metric/syncint64.go index 5bbfaf0397..425c1a0d51 100644 --- a/vendor/go.opentelemetry.io/otel/metric/syncint64.go +++ b/vendor/go.opentelemetry.io/otel/metric/syncint64.go @@ -51,6 +51,9 @@ type Int64CounterConfig struct { func NewInt64CounterConfig(opts ...Int64CounterOption) Int64CounterConfig { var config Int64CounterConfig for _, o := range opts { + if _, ok := o.(experimentalOption); ok { + continue + } config = o.applyInt64Counter(config) } return config @@ -116,6 +119,9 @@ type Int64UpDownCounterConfig struct { func NewInt64UpDownCounterConfig(opts ...Int64UpDownCounterOption) Int64UpDownCounterConfig { var config Int64UpDownCounterConfig for _, o := range opts { + if _, ok := o.(experimentalOption); ok { + continue + } config = o.applyInt64UpDownCounter(config) } return config @@ -182,6 +188,9 @@ type Int64HistogramConfig struct { func NewInt64HistogramConfig(opts ...Int64HistogramOption) Int64HistogramConfig { var config Int64HistogramConfig for _, o := range opts { + if _, ok := o.(experimentalOption); ok { + continue + } config = o.applyInt64Histogram(config) } return config @@ -251,6 +260,9 @@ type Int64GaugeConfig struct { func NewInt64GaugeConfig(opts ...Int64GaugeOption) Int64GaugeConfig { var config Int64GaugeConfig for _, o := range opts { + if _, ok := o.(experimentalOption); ok { + continue + } config = o.applyInt64Gauge(config) } return config diff --git a/vendor/go.opentelemetry.io/otel/propagation/baggage.go b/vendor/go.opentelemetry.io/otel/propagation/baggage.go index 2ecca3fed1..d81b709a2c 100644 --- a/vendor/go.opentelemetry.io/otel/propagation/baggage.go +++ b/vendor/go.opentelemetry.io/otel/propagation/baggage.go @@ -5,6 +5,9 @@ package propagation // import "go.opentelemetry.io/otel/propagation" import ( "context" + "errors" + "fmt" + "sync" "go.opentelemetry.io/otel/baggage" "go.opentelemetry.io/otel/internal/errorhandler" @@ -13,11 +16,18 @@ import ( const ( baggageHeader = "baggage" + maxParseErrors = 5 + // W3C Baggage specification limits. // https://www.w3.org/TR/baggage/#limits - maxMembers = 64 + maxMembers = 64 + maxBytesPerBaggageString = 8192 ) +// handleExtractErrOnce limits error reporting for attacker-controlled baggage headers +// to one process-wide emission, preventing repeated extraction from flooding logs. +var handleExtractErrOnce sync.Once + // Baggage is a propagator that supports the W3C Baggage format. // // This propagates user-defined baggage associated with a trace. The complete @@ -57,7 +67,9 @@ func extractSingleBaggage(parent context.Context, carrier TextMapCarrier) contex bag, err := baggage.Parse(bStr) if err != nil { - errorhandler.GetErrorHandler().Handle(err) + handleExtractErrOnce.Do(func() { + errorhandler.GetErrorHandler().Handle(err) + }) } if bag.Len() == 0 { return parent @@ -72,24 +84,60 @@ func extractMultiBaggage(parent context.Context, carrier ValuesGetter) context.C } var members []baggage.Member - for _, bStr := range bVals { - currBag, err := baggage.Parse(bStr) - if err != nil { - errorhandler.GetErrorHandler().Handle(err) + var totalBytes int + var parseErrors int + var truncateErr error + for i, bStr := range bVals { + if i > 0 { + totalBytes++ // comma separator between combined header values } - if currBag.Len() == 0 { - continue + totalBytes += len(bStr) + if totalBytes > maxBytesPerBaggageString { + // Per the W3C Baggage spec, the byte limit applies to the + // combination of all baggage headers, not each header + // individually. Mirror the single-header behavior of + // reporting the error and returning the parent context + // with no baggage attached. + handleExtractErrOnce.Do(func() { + errorhandler.GetErrorHandler().Handle(fmt.Errorf( + "baggage: aggregate header size %d exceeds %d byte limit", + totalBytes, + maxBytesPerBaggageString, + )) + }) + return parent } - members = append(members, currBag.Members()...) - if len(members) >= maxMembers { - break + + // If members exceed the limit, stop parsing baggage. + if len(members) <= maxMembers { + currBag, err := baggage.Parse(bStr) + if err != nil { + parseErrors++ + if parseErrors <= maxParseErrors { + truncateErr = errors.Join(truncateErr, err) + } + } + if currBag.Len() == 0 { + continue + } + members = append(members, currBag.Members()...) } } + if dropped := parseErrors - maxParseErrors; dropped > 0 { + truncateErr = errors.Join(truncateErr, fmt.Errorf("and %d more error(s)", dropped)) + } + b, err := baggage.New(members...) if err != nil { - errorhandler.GetErrorHandler().Handle(err) + truncateErr = errors.Join(truncateErr, err) } + if truncateErr != nil { + handleExtractErrOnce.Do(func() { + errorhandler.GetErrorHandler().Handle(truncateErr) + }) + } + if b.Len() == 0 { return parent } diff --git a/vendor/go.opentelemetry.io/otel/sdk/resource/builtin.go b/vendor/go.opentelemetry.io/otel/sdk/resource/builtin.go index 04f15fcd21..28823edd53 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/resource/builtin.go +++ b/vendor/go.opentelemetry.io/otel/sdk/resource/builtin.go @@ -13,7 +13,7 @@ import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/sdk" - semconv "go.opentelemetry.io/otel/semconv/v1.40.0" + semconv "go.opentelemetry.io/otel/semconv/v1.41.0" ) type ( @@ -79,7 +79,7 @@ func (sd stringDetector) Detect(context.Context) (*Resource, error) { } a := sd.K.String(value) if !a.Valid() { - return nil, fmt.Errorf("invalid attribute: %q -> %q", a.Key, a.Value.Emit()) + return nil, fmt.Errorf("invalid attribute: %q -> %q", a.Key, a.Value.String()) } return NewWithAttributes(sd.schemaURL, sd.K.String(value)), nil } diff --git a/vendor/go.opentelemetry.io/otel/sdk/resource/container.go b/vendor/go.opentelemetry.io/otel/sdk/resource/container.go index e977ff1c48..ce03e24c41 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/resource/container.go +++ b/vendor/go.opentelemetry.io/otel/sdk/resource/container.go @@ -11,7 +11,7 @@ import ( "os" "regexp" - semconv "go.opentelemetry.io/otel/semconv/v1.40.0" + semconv "go.opentelemetry.io/otel/semconv/v1.41.0" ) type containerIDProvider func() (string, error) diff --git a/vendor/go.opentelemetry.io/otel/sdk/resource/env.go b/vendor/go.opentelemetry.io/otel/sdk/resource/env.go index bc0e5c19e3..ac5691c08d 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/resource/env.go +++ b/vendor/go.opentelemetry.io/otel/sdk/resource/env.go @@ -12,7 +12,7 @@ import ( "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" - semconv "go.opentelemetry.io/otel/semconv/v1.40.0" + semconv "go.opentelemetry.io/otel/semconv/v1.41.0" ) const ( diff --git a/vendor/go.opentelemetry.io/otel/sdk/resource/host_id.go b/vendor/go.opentelemetry.io/otel/sdk/resource/host_id.go index 755c082427..cb38fa1a8b 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/resource/host_id.go +++ b/vendor/go.opentelemetry.io/otel/sdk/resource/host_id.go @@ -8,7 +8,7 @@ import ( "errors" "strings" - semconv "go.opentelemetry.io/otel/semconv/v1.40.0" + semconv "go.opentelemetry.io/otel/semconv/v1.41.0" ) type hostIDProvider func() (string, error) diff --git a/vendor/go.opentelemetry.io/otel/sdk/resource/host_id_exec.go b/vendor/go.opentelemetry.io/otel/sdk/resource/host_id_exec.go index d9e5d1a8ff..e239ead028 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/resource/host_id_exec.go +++ b/vendor/go.opentelemetry.io/otel/sdk/resource/host_id_exec.go @@ -5,10 +5,13 @@ package resource // import "go.opentelemetry.io/otel/sdk/resource" -import "os/exec" +import ( + "context" + "os/exec" +) func execCommand(name string, arg ...string) (string, error) { - cmd := exec.Command(name, arg...) + cmd := exec.CommandContext(context.Background(), name, arg...) b, err := cmd.Output() if err != nil { return "", err diff --git a/vendor/go.opentelemetry.io/otel/sdk/resource/os.go b/vendor/go.opentelemetry.io/otel/sdk/resource/os.go index f5682cad41..4c0def4148 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/resource/os.go +++ b/vendor/go.opentelemetry.io/otel/sdk/resource/os.go @@ -8,7 +8,7 @@ import ( "strings" "go.opentelemetry.io/otel/attribute" - semconv "go.opentelemetry.io/otel/semconv/v1.40.0" + semconv "go.opentelemetry.io/otel/semconv/v1.41.0" ) type osDescriptionProvider func() (string, error) diff --git a/vendor/go.opentelemetry.io/otel/sdk/resource/os_unix.go b/vendor/go.opentelemetry.io/otel/sdk/resource/os_unix.go index 6c50ab6867..1cd87c3983 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/resource/os_unix.go +++ b/vendor/go.opentelemetry.io/otel/sdk/resource/os_unix.go @@ -55,7 +55,8 @@ func uname() (string, error) { return "", err } - return fmt.Sprintf("%s %s %s %s %s", + return fmt.Sprintf( + "%s %s %s %s %s", unix.ByteSliceToString(utsName.Sysname[:]), unix.ByteSliceToString(utsName.Nodename[:]), unix.ByteSliceToString(utsName.Release[:]), diff --git a/vendor/go.opentelemetry.io/otel/sdk/resource/os_windows.go b/vendor/go.opentelemetry.io/otel/sdk/resource/os_windows.go index a6a5a53c0e..ebd50826d6 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/resource/os_windows.go +++ b/vendor/go.opentelemetry.io/otel/sdk/resource/os_windows.go @@ -16,7 +16,8 @@ import ( // resembles the one displayed by the Version Reporter Applet (winver.exe). func platformOSDescription() (string, error) { k, err := registry.OpenKey( - registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE) + registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE, + ) if err != nil { return "", err } @@ -37,7 +38,8 @@ func platformOSDescription() (string, error) { displayVersion += " " } - return fmt.Sprintf("%s %s(%s) [Version %s.%s.%s.%s]", + return fmt.Sprintf( + "%s %s(%s) [Version %s.%s.%s.%s]", productName, displayVersion, releaseID, diff --git a/vendor/go.opentelemetry.io/otel/sdk/resource/process.go b/vendor/go.opentelemetry.io/otel/sdk/resource/process.go index 99dce64f6d..b015f8233c 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/resource/process.go +++ b/vendor/go.opentelemetry.io/otel/sdk/resource/process.go @@ -11,7 +11,7 @@ import ( "path/filepath" "runtime" - semconv "go.opentelemetry.io/otel/semconv/v1.40.0" + semconv "go.opentelemetry.io/otel/semconv/v1.41.0" ) type ( @@ -164,7 +164,8 @@ func (processRuntimeVersionDetector) Detect(context.Context) (*Resource, error) // Detect returns a *Resource that describes the runtime of this process. func (processRuntimeDescriptionDetector) Detect(context.Context) (*Resource, error) { runtimeDescription := fmt.Sprintf( - "go version %s %s/%s", runtimeVersion(), runtimeOS(), runtimeArch()) + "go version %s %s/%s", runtimeVersion(), runtimeOS(), runtimeArch(), + ) return NewWithAttributes( semconv.SchemaURL, diff --git a/vendor/go.opentelemetry.io/otel/sdk/trace/batch_span_processor.go b/vendor/go.opentelemetry.io/otel/sdk/trace/batch_span_processor.go index 32854b14a3..f9f2a6c2c8 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/trace/batch_span_processor.go +++ b/vendor/go.opentelemetry.io/otel/sdk/trace/batch_span_processor.go @@ -163,19 +163,21 @@ func (bsp *batchSpanProcessor) Shutdown(ctx context.Context) error { bsp.stopOnce.Do(func() { bsp.stopped.Store(true) wait := make(chan struct{}) + // exportErr is written by the goroutine before closing wait. + // It is only read in the <-wait case, so there is no race. + var exportErr error go func() { close(bsp.stopCh) bsp.stopWait.Wait() if bsp.e != nil { - if err := bsp.e.Shutdown(ctx); err != nil { - otel.Handle(err) - } + exportErr = bsp.e.Shutdown(ctx) } close(wait) }() - // Wait until the wait group is done or the context is cancelled + // Wait until the channel is ready or the context is canceled. select { case <-wait: + err = exportErr case <-ctx.Done(): err = ctx.Err() } diff --git a/vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/batch_span_processor.go b/vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/batch_span_processor.go index c31e03aa0a..c725ebf372 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/batch_span_processor.go +++ b/vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/batch_span_processor.go @@ -13,8 +13,8 @@ import ( "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/sdk" "go.opentelemetry.io/otel/sdk/internal/x" - semconv "go.opentelemetry.io/otel/semconv/v1.40.0" - "go.opentelemetry.io/otel/semconv/v1.40.0/otelconv" + semconv "go.opentelemetry.io/otel/semconv/v1.41.0" + "go.opentelemetry.io/otel/semconv/v1.41.0/otelconv" ) const ( diff --git a/vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/simple_span_processor.go b/vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/simple_span_processor.go index 0e77cd9537..cf4b5d481f 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/simple_span_processor.go +++ b/vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/simple_span_processor.go @@ -13,8 +13,8 @@ import ( "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/sdk" "go.opentelemetry.io/otel/sdk/internal/x" - semconv "go.opentelemetry.io/otel/semconv/v1.40.0" - "go.opentelemetry.io/otel/semconv/v1.40.0/otelconv" + semconv "go.opentelemetry.io/otel/semconv/v1.41.0" + "go.opentelemetry.io/otel/semconv/v1.41.0/otelconv" ) var measureAttrsPool = sync.Pool{ @@ -86,6 +86,7 @@ func (ssp *SSP) addOption(err error) []metric.AddOption { } attrs := measureAttrsPool.Get().(*[]attribute.KeyValue) defer func() { + clear(*attrs) *attrs = (*attrs)[:0] // reset the slice for reuse measureAttrsPool.Put(attrs) }() diff --git a/vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/tracer.go b/vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/tracer.go index 560d316f2f..5aae89e883 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/tracer.go +++ b/vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/tracer.go @@ -13,7 +13,7 @@ import ( "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/sdk" "go.opentelemetry.io/otel/sdk/internal/x" - "go.opentelemetry.io/otel/semconv/v1.40.0/otelconv" + "go.opentelemetry.io/otel/semconv/v1.41.0/otelconv" "go.opentelemetry.io/otel/trace" ) diff --git a/vendor/go.opentelemetry.io/otel/sdk/trace/provider.go b/vendor/go.opentelemetry.io/otel/sdk/trace/provider.go index cd40d299d6..9d90c2eda5 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/trace/provider.go +++ b/vendor/go.opentelemetry.io/otel/sdk/trace/provider.go @@ -82,6 +82,10 @@ type TracerProvider struct { var _ trace.TracerProvider = &TracerProvider{} +type experimentalOption interface { + Experimental() +} + // NewTracerProvider returns a new and configured TracerProvider. // // By default the returned TracerProvider is configured with: @@ -99,6 +103,9 @@ func NewTracerProvider(opts ...TracerProviderOption) *TracerProvider { o = applyTracerProviderEnvConfigs(o) for _, opt := range opts { + if _, ok := opt.(experimentalOption); ok { + continue + } o = opt.apply(o) } @@ -310,7 +317,7 @@ func (p *TracerProvider) Shutdown(ctx context.Context) error { } func (p *TracerProvider) getSpanProcessors() spanProcessorStates { - return *(p.spanProcessors.Load()) + return *p.spanProcessors.Load() } // TracerProviderOption configures a TracerProvider. diff --git a/vendor/go.opentelemetry.io/otel/sdk/trace/sampling.go b/vendor/go.opentelemetry.io/otel/sdk/trace/sampling.go index 845e292c2b..5a4c74318b 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/trace/sampling.go +++ b/vendor/go.opentelemetry.io/otel/sdk/trace/sampling.go @@ -297,8 +297,9 @@ func (pb parentBased) ShouldSample(p SamplingParameters) SamplingResult { } func (pb parentBased) Description() string { - return fmt.Sprintf("ParentBased{root:%s,remoteParentSampled:%s,"+ - "remoteParentNotSampled:%s,localParentSampled:%s,localParentNotSampled:%s}", + return fmt.Sprintf( + "ParentBased{root:%s,remoteParentSampled:%s,"+ + "remoteParentNotSampled:%s,localParentSampled:%s,localParentNotSampled:%s}", pb.root.Description(), pb.config.remoteParentSampled.Description(), pb.config.remoteParentNotSampled.Description(), diff --git a/vendor/go.opentelemetry.io/otel/sdk/trace/span.go b/vendor/go.opentelemetry.io/otel/sdk/trace/span.go index 7d55ce1dc2..d1d9af29d7 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/trace/span.go +++ b/vendor/go.opentelemetry.io/otel/sdk/trace/span.go @@ -20,7 +20,7 @@ import ( "go.opentelemetry.io/otel/internal/global" "go.opentelemetry.io/otel/sdk/instrumentation" "go.opentelemetry.io/otel/sdk/resource" - semconv "go.opentelemetry.io/otel/semconv/v1.40.0" + semconv "go.opentelemetry.io/otel/semconv/v1.41.0" "go.opentelemetry.io/otel/trace" "go.opentelemetry.io/otel/trace/embedded" ) @@ -346,10 +346,12 @@ func (s *recordingSpan) addOverCapAttrs(limit int, attrs []attribute.KeyValue) { } } -// truncateAttr returns a truncated version of attr. Only string and string -// slice attribute values are truncated. String values are truncated to at -// most a length of limit. Each string slice value is truncated in this fashion -// (the slice length itself is unaffected). +// truncateAttr returns a truncated version of attr. Only string, string +// slice, byte slice, and slice attribute values are truncated. String values are truncated +// to at most a length of limit. Each string slice value is truncated in this +// fashion (the slice length itself is unaffected), and byte slice values are truncated to at most +// limit bytes. For slice attribute values, the limit is applied to each +// element recursively. // // No truncation is performed for a negative limit. func truncateAttr(limit int, attr attribute.KeyValue) attribute.KeyValue { @@ -366,10 +368,95 @@ func truncateAttr(limit int, attr attribute.KeyValue) attribute.KeyValue { v[i] = truncate(limit, v[i]) } return attr.Key.StringSlice(v) + case attribute.BYTESLICE: + v := attr.Value.AsString() + if len(v) > limit { + return attr.Key.ByteSlice([]byte(v[:limit])) + } + return attr + case attribute.SLICE: + v := attr.Value.AsSlice() + if !slices.ContainsFunc(v, func(e attribute.Value) bool { return needsTruncation(limit, e) }) { + return attr + } + newV := make([]attribute.Value, len(v)) + for i, elem := range v { + newV[i] = truncateValue(limit, elem) + } + return attr.Key.Slice(newV...) } return attr } +// truncateValue returns a truncated version of v. Only string, string slice, +// byte slice, and (recursively) slice values are modified. +// +// No truncation is performed for a negative limit. +func truncateValue(limit int, v attribute.Value) attribute.Value { + switch v.Type() { + case attribute.STRING: + return attribute.StringValue(truncate(limit, v.AsString())) + case attribute.STRINGSLICE: + ss := v.AsStringSlice() + for i := range ss { + ss[i] = truncate(limit, ss[i]) + } + return attribute.StringSliceValue(ss) + + case attribute.BYTESLICE: + // len(v.AsString()) is identical to len(v.AsByteSlice()) but + // avoids allocating the full slice before truncation. + s := v.AsString() + if limit >= 0 && len(s) > limit { + return attribute.ByteSliceValue([]byte(s[:limit])) + } + case attribute.SLICE: + sl := v.AsSlice() + if !slices.ContainsFunc(sl, func(e attribute.Value) bool { return needsTruncation(limit, e) }) { + return v + } + newSl := make([]attribute.Value, len(sl)) + for i, elem := range sl { + newSl[i] = truncateValue(limit, elem) + } + return attribute.SliceValue(newSl...) + } + return v +} + +// stringNeedsTruncation reports whether s would be modified by truncate for the +// given limit. +func stringNeedsTruncation(limit int, s string) bool { + if limit < 0 || len(s) <= limit { + return false + } + return utf8.RuneCountInString(s) > limit || !utf8.ValidString(s) +} + +// needsTruncation reports whether v would be modified by truncateValue for the +// given limit. +func needsTruncation(limit int, v attribute.Value) bool { + switch v.Type() { + case attribute.STRING: + return stringNeedsTruncation(limit, v.AsString()) + case attribute.BYTESLICE: + // len(v.AsString()) is identical to len(v.AsByteSlice()) but + // avoids memory allocation. + if limit >= 0 && len(v.AsString()) > limit { + return true + } + case attribute.STRINGSLICE: + for _, s := range v.AsStringSlice() { + if stringNeedsTruncation(limit, s) { + return true + } + } + case attribute.SLICE: + return slices.ContainsFunc(v.AsSlice(), func(e attribute.Value) bool { return needsTruncation(limit, e) }) + } + return false +} + // truncate returns a truncated version of s such that it contains less than // the limit number of characters. Truncation is applied by returning the limit // number of valid characters contained in s. diff --git a/vendor/go.opentelemetry.io/otel/sdk/trace/span_limits.go b/vendor/go.opentelemetry.io/otel/sdk/trace/span_limits.go index 321d974305..348ee0e808 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/trace/span_limits.go +++ b/vendor/go.opentelemetry.io/otel/sdk/trace/span_limits.go @@ -35,8 +35,10 @@ const ( type SpanLimits struct { // AttributeValueLengthLimit is the maximum allowed attribute value length. // - // This limit only applies to string and string slice attribute values. - // Any string longer than this value will be truncated to this length. + // This limit only applies to string, string slice, byte slice, and slice attribute + // values. Any string and byte slice longer than this value will be truncated to this + // length. For slice attribute values, the limit is applied to each string and byte slice + // element recursively. // // Setting this to a negative value means no limit is applied. AttributeValueLengthLimit int diff --git a/vendor/go.opentelemetry.io/otel/sdk/version.go b/vendor/go.opentelemetry.io/otel/sdk/version.go index 766731dd25..218dce1f56 100644 --- a/vendor/go.opentelemetry.io/otel/sdk/version.go +++ b/vendor/go.opentelemetry.io/otel/sdk/version.go @@ -6,5 +6,5 @@ package sdk // import "go.opentelemetry.io/otel/sdk" // Version is the current release version of the OpenTelemetry SDK in use. func Version() string { - return "1.43.0" + return "1.44.0" } diff --git a/vendor/go.opentelemetry.io/otel/semconv/v1.37.0/attribute_group.go b/vendor/go.opentelemetry.io/otel/semconv/v1.37.0/attribute_group.go index b6b27498f2..2fcab24352 100644 --- a/vendor/go.opentelemetry.io/otel/semconv/v1.37.0/attribute_group.go +++ b/vendor/go.opentelemetry.io/otel/semconv/v1.37.0/attribute_group.go @@ -1447,9 +1447,11 @@ func AWSExtendedRequestID(val string) attribute.KeyValue { // AWSKinesisStreamName returns an attribute KeyValue conforming to the // "aws.kinesis.stream_name" semantic conventions. It represents the name of the // AWS Kinesis [stream] the request refers to. Corresponds to the `--stream-name` -// parameter of the Kinesis [describe-stream] operation. +// +// parameter of the Kinesis [describe-stream] operation. // // [stream]: https://docs.aws.amazon.com/streams/latest/dev/introduction.html +// // [describe-stream]: https://docs.aws.amazon.com/cli/latest/reference/kinesis/describe-stream.html func AWSKinesisStreamName(val string) attribute.KeyValue { return AWSKinesisStreamNameKey.String(val) @@ -1459,7 +1461,8 @@ func AWSKinesisStreamName(val string) attribute.KeyValue { // "aws.lambda.invoked_arn" semantic conventions. It represents the full invoked // ARN as provided on the `Context` passed to the function ( // `Lambda-Runtime-Invoked-Function-Arn` header on the `/runtime/invocation/next` -// applicable). +// +// applicable). func AWSLambdaInvokedARN(val string) attribute.KeyValue { return AWSLambdaInvokedARNKey.String(val) } @@ -2635,7 +2638,8 @@ func CloudRegion(val string) attribute.KeyValue { // "cloud.resource_id" semantic conventions. It represents the cloud // provider-specific native identifier of the monitored cloud resource (e.g. an // [ARN] on AWS, a [fully qualified resource ID] on Azure, a [full resource name] -// on GCP). +// +// on GCP). // // [ARN]: https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html // [fully qualified resource ID]: https://learn.microsoft.com/rest/api/resources/resources/get-by-id @@ -15190,4 +15194,4 @@ func ZOSSmfID(val string) attribute.KeyValue { // to which the z/OS system belongs too. func ZOSSysplexName(val string) attribute.KeyValue { return ZOSSysplexNameKey.String(val) -} \ No newline at end of file +} diff --git a/vendor/go.opentelemetry.io/otel/semconv/v1.40.0/README.md b/vendor/go.opentelemetry.io/otel/semconv/v1.40.0/README.md deleted file mode 100644 index c51b7fb7b0..0000000000 --- a/vendor/go.opentelemetry.io/otel/semconv/v1.40.0/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Semconv v1.40.0 - -[![PkgGoDev](https://pkg.go.dev/badge/go.opentelemetry.io/otel/semconv/v1.40.0)](https://pkg.go.dev/go.opentelemetry.io/otel/semconv/v1.40.0) diff --git a/vendor/go.opentelemetry.io/otel/semconv/v1.40.0/MIGRATION.md b/vendor/go.opentelemetry.io/otel/semconv/v1.41.0/MIGRATION.md similarity index 63% rename from vendor/go.opentelemetry.io/otel/semconv/v1.40.0/MIGRATION.md rename to vendor/go.opentelemetry.io/otel/semconv/v1.41.0/MIGRATION.md index e246b1692d..ba52cadf71 100644 --- a/vendor/go.opentelemetry.io/otel/semconv/v1.40.0/MIGRATION.md +++ b/vendor/go.opentelemetry.io/otel/semconv/v1.41.0/MIGRATION.md @@ -1,7 +1,7 @@ -# Migration from v1.39.0 to v1.40.0 +# Migration from v1.40.0 to v1.41.0 -The `go.opentelemetry.io/otel/semconv/v1.40.0` package should be a drop-in replacement for `go.opentelemetry.io/otel/semconv/v1.39.0` with the following exceptions. +The `go.opentelemetry.io/otel/semconv/v1.41.0` package should be a drop-in replacement for `go.opentelemetry.io/otel/semconv/v1.40.0` with the following exceptions. ## Removed @@ -11,17 +11,7 @@ Refer to the [OpenTelemetry Semantic Conventions documentation] for deprecation If the type is not listed in the documentation as deprecated, it has been removed in this version due to lack of applicability or use. If you use any of these non-deprecated declarations in your Go application, please [open an issue] describing your use-case. -- `ErrorMessage` -- `ErrorMessageKey` -- `RPCMessageCompressedSize` -- `RPCMessageCompressedSizeKey` -- `RPCMessageID` -- `RPCMessageIDKey` -- `RPCMessageTypeKey` -- `RPCMessageTypeReceived` -- `RPCMessageTypeSent` -- `RPCMessageUncompressedSize` -- `RPCMessageUncompressedSizeKey` +- `DeploymentEnvironmentName` [OpenTelemetry Semantic Conventions documentation]: https://github.com/open-telemetry/semantic-conventions [open an issue]: https://github.com/open-telemetry/opentelemetry-go/issues/new?template=Blank+issue diff --git a/vendor/go.opentelemetry.io/otel/semconv/v1.41.0/README.md b/vendor/go.opentelemetry.io/otel/semconv/v1.41.0/README.md new file mode 100644 index 0000000000..8353bb7152 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/semconv/v1.41.0/README.md @@ -0,0 +1,3 @@ +# Semconv v1.41.0 + +[![PkgGoDev](https://pkg.go.dev/badge/go.opentelemetry.io/otel/semconv/v1.41.0)](https://pkg.go.dev/go.opentelemetry.io/otel/semconv/v1.41.0) diff --git a/vendor/go.opentelemetry.io/otel/semconv/v1.40.0/attribute_group.go b/vendor/go.opentelemetry.io/otel/semconv/v1.41.0/attribute_group.go similarity index 96% rename from vendor/go.opentelemetry.io/otel/semconv/v1.40.0/attribute_group.go rename to vendor/go.opentelemetry.io/otel/semconv/v1.41.0/attribute_group.go index ee6b1f79d6..7cee086802 100644 --- a/vendor/go.opentelemetry.io/otel/semconv/v1.40.0/attribute_group.go +++ b/vendor/go.opentelemetry.io/otel/semconv/v1.41.0/attribute_group.go @@ -3,7 +3,7 @@ // Code generated from semantic convention specification. DO NOT EDIT. -package semconv // import "go.opentelemetry.io/otel/semconv/v1.40.0" +package semconv // import "go.opentelemetry.io/otel/semconv/v1.41.0" import "go.opentelemetry.io/otel/attribute" @@ -950,7 +950,7 @@ const ( // of the [AWS Lambda EvenSource Mapping]. An event source is mapped to a lambda // function. It's contents are read by Lambda and used to trigger a function. // This isn't available in the lambda execution context or the lambda runtime - // environtment. This is going to be populated by the AWS SDK for each language + // environment. This is going to be populated by the AWS SDK for each language // when that UUID is present. Some of these operations are // Create/Delete/Get/List/Update EventSourceMapping. // @@ -1186,7 +1186,7 @@ const ( // AWSSecretsmanagerSecretARNKey is the attribute Key conforming to the // "aws.secretsmanager.secret.arn" semantic conventions. It represents the ARN - // of the Secret stored in the Secrets Mangger. + // of the Secret stored in the Secrets Manager. // // Type: string // RequirementLevel: Recommended @@ -1515,7 +1515,7 @@ func AWSLambdaInvokedARN(val string) attribute.KeyValue { // of the [AWS Lambda EvenSource Mapping]. An event source is mapped to a lambda // function. It's contents are read by Lambda and used to trigger a function. // This isn't available in the lambda execution context or the lambda runtime -// environtment. This is going to be populated by the AWS SDK for each language +// environment. This is going to be populated by the AWS SDK for each language // when that UUID is present. Some of these operations are // Create/Delete/Get/List/Update EventSourceMapping. // @@ -1609,7 +1609,7 @@ func AWSS3UploadID(val string) attribute.KeyValue { // AWSSecretsmanagerSecretARN returns an attribute KeyValue conforming to the // "aws.secretsmanager.secret.arn" semantic conventions. It represents the ARN of -// the Secret stored in the Secrets Mangger. +// the Secret stored in the Secrets Manager. func AWSSecretsmanagerSecretARN(val string) attribute.KeyValue { return AWSSecretsmanagerSecretARNKey.String(val) } @@ -2196,6 +2196,11 @@ const ( // Stability: Development // // Examples: "12097" + // Note: For a given pipeline run and task, the `cicd.pipeline.task.run.id` MUST + // be unique within that run. For the same task across different runs of the + // same pipeline, the `cicd.pipeline.task.run.id` MAY remain the same, enabling + // correlation of `cicd.pipeline.task.run.result` values across multiple + // pipeline runs. CICDPipelineTaskRunIDKey = attribute.Key("cicd.pipeline.task.run.id") // CICDPipelineTaskRunResultKey is the attribute Key conforming to the @@ -3431,7 +3436,7 @@ const ( // // Type: string // RequirementLevel: Recommended - // Stability: Beta + // Stability: Release_Candidate // // Examples: "a3bf90e006b2" // @@ -3467,7 +3472,7 @@ const ( // // Type: string // RequirementLevel: Recommended - // Stability: Beta + // Stability: Release_Candidate // // Examples: "gcr.io/opentelemetry/operator" ContainerImageNameKey = attribute.Key("container.image.name") @@ -3478,7 +3483,7 @@ const ( // // Type: string[] // RequirementLevel: Recommended - // Stability: Beta + // Stability: Release_Candidate // // Examples: // "example@sha256:afcc7f1ac1b49db317a7196c902e61c6c3c4607d63599ee1a82d702d249a0ccb", @@ -3497,7 +3502,7 @@ const ( // // Type: string[] // RequirementLevel: Recommended - // Stability: Beta + // Stability: Release_Candidate // // Examples: "v1.27.1", "3.5.7-0" // @@ -3518,7 +3523,7 @@ const ( // ContainerRuntimeDescriptionKey is the attribute Key conforming to the // "container.runtime.description" semantic conventions. It represents a // description about the runtime which could include, for example details about - // the CRI/API version being used or other customisations. + // the CRI/API version being used or other customizations. // // Type: string // RequirementLevel: Recommended @@ -3649,7 +3654,7 @@ func ContainerName(val string) attribute.KeyValue { // ContainerRuntimeDescription returns an attribute KeyValue conforming to the // "container.runtime.description" semantic conventions. It represents a // description about the runtime which could include, for example details about -// the CRI/API version being used or other customisations. +// the CRI/API version being used or other customizations. func ContainerRuntimeDescription(val string) attribute.KeyValue { return ContainerRuntimeDescriptionKey.String(val) } @@ -4260,9 +4265,9 @@ const ( // "deployment.environment.name" semantic conventions. It represents the name of // the [deployment environment] (aka deployment tier). // - // Type: string + // Type: Enum // RequirementLevel: Recommended - // Stability: Development + // Stability: Stable // // Examples: "staging", "production" // Note: `deployment.environment.name` does not affect the uniqueness @@ -4312,15 +4317,6 @@ const ( DeploymentStatusKey = attribute.Key("deployment.status") ) -// DeploymentEnvironmentName returns an attribute KeyValue conforming to the -// "deployment.environment.name" semantic conventions. It represents the name of -// the [deployment environment] (aka deployment tier). -// -// [deployment environment]: https://wikipedia.org/wiki/Deployment_environment -func DeploymentEnvironmentName(val string) attribute.KeyValue { - return DeploymentEnvironmentNameKey.String(val) -} - // DeploymentID returns an attribute KeyValue conforming to the "deployment.id" // semantic conventions. It represents the id of the deployment. func DeploymentID(val string) attribute.KeyValue { @@ -4334,6 +4330,22 @@ func DeploymentName(val string) attribute.KeyValue { return DeploymentNameKey.String(val) } +// Enum values for deployment.environment.name +var ( + // Production environment + // Stability: stable + DeploymentEnvironmentNameProduction = DeploymentEnvironmentNameKey.String("production") + // Staging environment + // Stability: stable + DeploymentEnvironmentNameStaging = DeploymentEnvironmentNameKey.String("staging") + // Testing environment + // Stability: stable + DeploymentEnvironmentNameTest = DeploymentEnvironmentNameKey.String("test") + // Development environment + // Stability: stable + DeploymentEnvironmentNameDevelopment = DeploymentEnvironmentNameKey.String("development") +) + // Enum values for deployment.status var ( // failed @@ -4645,6 +4657,12 @@ const ( // When `error.type` is set to a type (e.g., an exception type), its // canonical class name identifying the type within the artifact SHOULD be used. // + // If the recorded error type is a wrapper that is not meaningful for + // failure classification, instrumentation MAY use the type of the inner + // error instead. For example, in Go, errors created with `fmt.Errorf` + // using `%w` MAY be unwrapped when the wrapper type does not help + // classify the failure. + // // Instrumentations SHOULD document the list of errors they report. // // The cardinality of `error.type` within one instrumentation library SHOULD be @@ -4718,6 +4736,11 @@ const ( // Stability: Stable // // Examples: "java.net.ConnectException", "OSError" + // Note: If the recorded exception type is a wrapper that is not meaningful for + // failure classification, instrumentation MAY use the type of the inner + // exception instead. For example, in Go, errors created with `fmt.Errorf` + // using `%w` MAY be unwrapped when the wrapper type does not help + // classify the failure. ExceptionTypeKey = attribute.Key("exception.type") ) @@ -6667,6 +6690,17 @@ const ( // Examples: "forest", "lived" GenAIRequestStopSequencesKey = attribute.Key("gen_ai.request.stop_sequences") + // GenAIRequestStreamKey is the attribute Key conforming to the + // "gen_ai.request.stream" semantic conventions. It represents the indicates + // whether the GenAI request was made in streaming mode. + // + // Type: boolean + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: + GenAIRequestStreamKey = attribute.Key("gen_ai.request.stream") + // GenAIRequestTemperatureKey is the attribute Key conforming to the // "gen_ai.request.temperature" semantic conventions. It represents the // temperature setting for the GenAI request. @@ -6734,6 +6768,19 @@ const ( // Examples: "gpt-4-0613" GenAIResponseModelKey = attribute.Key("gen_ai.response.model") + // GenAIResponseTimeToFirstChunkKey is the attribute Key conforming to the + // "gen_ai.response.time_to_first_chunk" semantic conventions. It represents the + // time to first chunk in a streaming response, measured from request issuance, + // in seconds. The value is measured from when the client issues the generation + // request to when the first chunk is received in the response stream. + // + // Type: double + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 0.5, 1.2 + GenAIResponseTimeToFirstChunkKey = attribute.Key("gen_ai.response.time_to_first_chunk") + // GenAIRetrievalDocumentsKey is the attribute Key conforming to the // "gen_ai.retrieval.documents" semantic conventions. It represents the // documents retrieved. @@ -6875,7 +6922,7 @@ const ( // GenAIToolDefinitionsKey is the attribute Key conforming to the // "gen_ai.tool.definitions" semantic conventions. It represents the list of - // source system tool definitions available to the GenAI agent or model. + // tool definitions available to the GenAI agent or model. // // Type: any // RequirementLevel: Recommended @@ -6887,19 +6934,18 @@ const ( // "description": "The city and state, e.g. San Francisco, CA"\n },\n "unit": // {\n "type": "string",\n "enum": [\n "celsius",\n "fahrenheit"\n ]\n }\n },\n // "required": [\n "location",\n "unit"\n ]\n }\n }\n]\n" - // Note: The value of this attribute matches source system tool definition - // format. + // Note: Instrumentations MUST follow [Tool Definitions JSON Schema]. // - // It's expected to be an array of objects where each object represents a tool - // definition. In case a serialized string is available - // to the instrumentation, the instrumentation SHOULD do the best effort to - // deserialize it to an array. When recorded on spans, it MAY be recorded as a - // JSON string if structured format is not supported and SHOULD be recorded in - // structured form otherwise. + // When the attribute is recorded on events, it MUST be recorded in structured + // form. When recorded on spans, it MAY be recorded as a JSON string if + // structured + // format is not supported and SHOULD be recorded in structured form otherwise. // // Since this attribute could be large, it's NOT RECOMMENDED to populate - // it by default. Instrumentations MAY provide a way to enable - // populating this attribute. + // non-required properties by default. Instrumentations MAY provide a way + // to enable populating optional properties. + // + // [Tool Definitions JSON Schema]: /docs/gen-ai/gen-ai-tool-definitions.json GenAIToolDefinitionsKey = attribute.Key("gen_ai.tool.definitions") // GenAIToolDescriptionKey is the attribute Key conforming to the @@ -6997,6 +7043,32 @@ const ( // // Examples: 180 GenAIUsageOutputTokensKey = attribute.Key("gen_ai.usage.output_tokens") + + // GenAIUsageReasoningOutputTokensKey is the attribute Key conforming to the + // "gen_ai.usage.reasoning.output_tokens" semantic conventions. It represents + // the number of output tokens used for reasoning (e.g. chain-of-thought, + // extended thinking). + // + // Type: int + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: 50 + // Note: The value SHOULD be included in `gen_ai.usage.output_tokens`. + GenAIUsageReasoningOutputTokensKey = attribute.Key("gen_ai.usage.reasoning.output_tokens") + + // GenAIWorkflowNameKey is the attribute Key conforming to the + // "gen_ai.workflow.name" semantic conventions. It represents the human-readable + // name of the GenAI workflow provided by the application. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "multi_agent_rag", "customer_support_pipeline" + // Note: This attribute can be populated in different frameworks eg: name of the + // first chain in LangChain OR name of the crew in CrewAI. + GenAIWorkflowNameKey = attribute.Key("gen_ai.workflow.name") ) // GenAIAgentDescription returns an attribute KeyValue conforming to the @@ -7139,6 +7211,13 @@ func GenAIRequestStopSequences(val ...string) attribute.KeyValue { return GenAIRequestStopSequencesKey.StringSlice(val) } +// GenAIRequestStream returns an attribute KeyValue conforming to the +// "gen_ai.request.stream" semantic conventions. It represents the indicates +// whether the GenAI request was made in streaming mode. +func GenAIRequestStream(val bool) attribute.KeyValue { + return GenAIRequestStreamKey.Bool(val) +} + // GenAIRequestTemperature returns an attribute KeyValue conforming to the // "gen_ai.request.temperature" semantic conventions. It represents the // temperature setting for the GenAI request. @@ -7182,6 +7261,15 @@ func GenAIResponseModel(val string) attribute.KeyValue { return GenAIResponseModelKey.String(val) } +// GenAIResponseTimeToFirstChunk returns an attribute KeyValue conforming to the +// "gen_ai.response.time_to_first_chunk" semantic conventions. It represents the +// time to first chunk in a streaming response, measured from request issuance, +// in seconds. The value is measured from when the client issues the generation +// request to when the first chunk is received in the response stream. +func GenAIResponseTimeToFirstChunk(val float64) attribute.KeyValue { + return GenAIResponseTimeToFirstChunkKey.Float64(val) +} + // GenAIRetrievalQueryText returns an attribute KeyValue conforming to the // "gen_ai.retrieval.query.text" semantic conventions. It represents the query // text used for retrieval. @@ -7245,6 +7333,21 @@ func GenAIUsageOutputTokens(val int) attribute.KeyValue { return GenAIUsageOutputTokensKey.Int(val) } +// GenAIUsageReasoningOutputTokens returns an attribute KeyValue conforming to +// the "gen_ai.usage.reasoning.output_tokens" semantic conventions. It represents +// the number of output tokens used for reasoning (e.g. chain-of-thought, +// extended thinking). +func GenAIUsageReasoningOutputTokens(val int) attribute.KeyValue { + return GenAIUsageReasoningOutputTokensKey.Int(val) +} + +// GenAIWorkflowName returns an attribute KeyValue conforming to the +// "gen_ai.workflow.name" semantic conventions. It represents the human-readable +// name of the GenAI workflow provided by the application. +func GenAIWorkflowName(val string) attribute.KeyValue { + return GenAIWorkflowNameKey.String(val) +} + // Enum values for gen_ai.operation.name var ( // Chat completion operation such as [OpenAI Chat API] @@ -7281,6 +7384,9 @@ var ( // Execute a tool // Stability: development GenAIOperationNameExecuteTool = GenAIOperationNameKey.String("execute_tool") + // Invoke GenAI workflow + // Stability: development + GenAIOperationNameInvokeWorkflow = GenAIOperationNameKey.String("invoke_workflow") ) // Enum values for gen_ai.output.type @@ -7335,7 +7441,7 @@ var ( // [Azure OpenAI] // Stability: development // - // [Azure OpenAI]: https://azure.microsoft.com/products/ai-services/openai-service/ + // [Azure OpenAI]: https://learn.microsoft.com/en-us/azure/ai-services/openai/overview GenAIProviderNameAzureAIOpenAI = GenAIProviderNameKey.String("azure.ai.openai") // [IBM Watsonx AI] // Stability: development @@ -7551,6 +7657,44 @@ var ( // Namespace: go const ( + // GoCPUDetailedStateKey is the attribute Key conforming to the + // "go.cpu.detailed_state" semantic conventions. It represents the detailed + // state of the CPU. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "gc/pause", "gc/mark/assist" + // Note: Value SHOULD match the specific CPU class reported by the Go runtime + // under `/cpu/classes/...`. The list of possible values is subject to change + // with the Go version used. + GoCPUDetailedStateKey = attribute.Key("go.cpu.detailed_state") + + // GoCPUStateKey is the attribute Key conforming to the "go.cpu.state" semantic + // conventions. It represents the state of the CPU. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "user", "gc" + GoCPUStateKey = attribute.Key("go.cpu.state") + + // GoMemoryDetailedTypeKey is the attribute Key conforming to the + // "go.memory.detailed_type" semantic conventions. It represents the detailed + // type of memory. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "heap/objects", "heap/free" + // Note: Value SHOULD match the specific memory class reported by the Go runtime + // under `/memory/classes/...`. The list of possible values is subject to change + // with the Go version used. + GoMemoryDetailedTypeKey = attribute.Key("go.memory.detailed_type") + // GoMemoryTypeKey is the attribute Key conforming to the "go.memory.type" // semantic conventions. It represents the type of memory. // @@ -7562,6 +7706,36 @@ const ( GoMemoryTypeKey = attribute.Key("go.memory.type") ) +// GoCPUDetailedState returns an attribute KeyValue conforming to the +// "go.cpu.detailed_state" semantic conventions. It represents the detailed state +// of the CPU. +func GoCPUDetailedState(val string) attribute.KeyValue { + return GoCPUDetailedStateKey.String(val) +} + +// GoMemoryDetailedType returns an attribute KeyValue conforming to the +// "go.memory.detailed_type" semantic conventions. It represents the detailed +// type of memory. +func GoMemoryDetailedType(val string) attribute.KeyValue { + return GoMemoryDetailedTypeKey.String(val) +} + +// Enum values for go.cpu.state +var ( + // CPU time spent running user Go code. + // Stability: development + GoCPUStateUser = GoCPUStateKey.String("user") + // CPU time spent performing garbage collection tasks. + // Stability: development + GoCPUStateGC = GoCPUStateKey.String("gc") + // CPU time spent returning unused memory to the underlying platform. + // Stability: development + GoCPUStateScavenge = GoCPUStateKey.String("scavenge") + // Available CPU time not spent executing any Go or Go runtime code. + // Stability: development + GoCPUStateIdle = GoCPUStateKey.String("idle") +) + // Enum values for go.memory.type var ( // Memory allocated from the heap that is reserved for stack space, whether or @@ -7584,7 +7758,8 @@ const ( // Stability: Development // // Examples: query findBookById { bookById(id: ?) { name } } - // Note: The value may be sanitized to exclude sensitive information. + // Note: If instrumentation can reliably identify and redact sensitive + // information it SHOULD do it. GraphQLDocumentKey = attribute.Key("graphql.document") // GraphQLOperationNameKey is the attribute Key conforming to the @@ -8335,7 +8510,7 @@ var ( const ( // HwBatteryCapacityKey is the attribute Key conforming to the // "hw.battery.capacity" semantic conventions. It represents the design capacity - // in Watts-hours or Amper-hours. + // in Watts-hours or Ampere-hours. // // Type: string // RequirementLevel: Recommended @@ -8637,7 +8812,7 @@ const ( // HwBatteryCapacity returns an attribute KeyValue conforming to the // "hw.battery.capacity" semantic conventions. It represents the design capacity -// in Watts-hours or Amper-hours. +// in Watts-hours or Ampere-hours. func HwBatteryCapacity(val string) attribute.KeyValue { return HwBatteryCapacityKey.String(val) } @@ -9026,7 +9201,7 @@ const ( // // Type: string // RequirementLevel: Recommended - // Stability: Beta + // Stability: Release_Candidate // // Examples: "opentelemetry-cluster" K8SClusterNameKey = attribute.Key("k8s.cluster.name") @@ -9037,7 +9212,7 @@ const ( // // Type: string // RequirementLevel: Recommended - // Stability: Beta + // Stability: Release_Candidate // // Examples: "218fc5a9-a5f1-4b54-aa05-46717d0ab26d" // Note: K8s doesn't have support for obtaining a cluster ID. If this is ever @@ -9073,7 +9248,7 @@ const ( // // Type: string // RequirementLevel: Recommended - // Stability: Beta + // Stability: Release_Candidate // // Examples: "redis" K8SContainerNameKey = attribute.Key("k8s.container.name") @@ -9085,7 +9260,7 @@ const ( // // Type: int // RequirementLevel: Recommended - // Stability: Beta + // Stability: Release_Candidate // // Examples: K8SContainerRestartCountKey = attribute.Key("k8s.container.restart_count") @@ -9136,7 +9311,7 @@ const ( // // Type: string // RequirementLevel: Recommended - // Stability: Beta + // Stability: Release_Candidate // // Examples: "opentelemetry" K8SCronJobNameKey = attribute.Key("k8s.cronjob.name") @@ -9146,7 +9321,7 @@ const ( // // Type: string // RequirementLevel: Recommended - // Stability: Beta + // Stability: Release_Candidate // // Examples: "275ecb36-5aa8-4c2a-9c47-d8bb681b9aff" K8SCronJobUIDKey = attribute.Key("k8s.cronjob.uid") @@ -9157,7 +9332,7 @@ const ( // // Type: string // RequirementLevel: Recommended - // Stability: Beta + // Stability: Release_Candidate // // Examples: "opentelemetry" K8SDaemonSetNameKey = attribute.Key("k8s.daemonset.name") @@ -9167,7 +9342,7 @@ const ( // // Type: string // RequirementLevel: Recommended - // Stability: Beta + // Stability: Release_Candidate // // Examples: "275ecb36-5aa8-4c2a-9c47-d8bb681b9aff" K8SDaemonSetUIDKey = attribute.Key("k8s.daemonset.uid") @@ -9178,7 +9353,7 @@ const ( // // Type: string // RequirementLevel: Recommended - // Stability: Beta + // Stability: Release_Candidate // // Examples: "opentelemetry" K8SDeploymentNameKey = attribute.Key("k8s.deployment.name") @@ -9189,7 +9364,7 @@ const ( // // Type: string // RequirementLevel: Recommended - // Stability: Beta + // Stability: Release_Candidate // // Examples: "275ecb36-5aa8-4c2a-9c47-d8bb681b9aff" K8SDeploymentUIDKey = attribute.Key("k8s.deployment.uid") @@ -9279,7 +9454,7 @@ const ( // // Type: string // RequirementLevel: Recommended - // Stability: Beta + // Stability: Release_Candidate // // Examples: "opentelemetry" K8SJobNameKey = attribute.Key("k8s.job.name") @@ -9289,7 +9464,7 @@ const ( // // Type: string // RequirementLevel: Recommended - // Stability: Beta + // Stability: Release_Candidate // // Examples: "275ecb36-5aa8-4c2a-9c47-d8bb681b9aff" K8SJobUIDKey = attribute.Key("k8s.job.uid") @@ -9300,7 +9475,7 @@ const ( // // Type: string // RequirementLevel: Recommended - // Stability: Beta + // Stability: Release_Candidate // // Examples: "default" K8SNamespaceNameKey = attribute.Key("k8s.namespace.name") @@ -9365,27 +9540,128 @@ const ( // // Type: string // RequirementLevel: Recommended - // Stability: Beta + // Stability: Release_Candidate // // Examples: "node-1" K8SNodeNameKey = attribute.Key("k8s.node.name") + // K8SNodeSystemContainerNameKey is the attribute Key conforming to the + // "k8s.node.system_container.name" semantic conventions. It represents the name + // of the system container running on the K8s Node. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "kubelet", "runtime", "pods", "misc" + K8SNodeSystemContainerNameKey = attribute.Key("k8s.node.system_container.name") + // K8SNodeUIDKey is the attribute Key conforming to the "k8s.node.uid" semantic // conventions. It represents the UID of the Node. // // Type: string // RequirementLevel: Recommended - // Stability: Beta + // Stability: Release_Candidate // // Examples: "1eb3a0c6-0477-4080-a9cb-0cb7db65c6a2" K8SNodeUIDKey = attribute.Key("k8s.node.uid") + // K8SPersistentvolumeNameKey is the attribute Key conforming to the + // "k8s.persistentvolume.name" semantic conventions. It represents the name of + // the PersistentVolume. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "pv-data-01" + K8SPersistentvolumeNameKey = attribute.Key("k8s.persistentvolume.name") + + // K8SPersistentvolumeReclaimPolicyKey is the attribute Key conforming to the + // "k8s.persistentvolume.reclaim_policy" semantic conventions. It represents the + // reclaim policy of the PersistentVolume. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "Delete", "Retain", "Recycle" + // Note: This attribute aligns with the `persistentVolumeReclaimPolicy` field of + // the + // [K8s PersistentVolumeSpec]. + // + // [K8s PersistentVolumeSpec]: https://kubernetes.io/docs/reference/kubernetes-api/config-and-storage-resources/persistent-volume-v1/#PersistentVolumeSpec + K8SPersistentvolumeReclaimPolicyKey = attribute.Key("k8s.persistentvolume.reclaim_policy") + + // K8SPersistentvolumeStatusPhaseKey is the attribute Key conforming to the + // "k8s.persistentvolume.status.phase" semantic conventions. It represents the + // phase of the PersistentVolume. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "Pending", "Available", "Bound", "Released", "Failed" + // Note: This attribute aligns with the `phase` field of the + // [K8s PersistentVolumeStatus]. + // + // [K8s PersistentVolumeStatus]: https://kubernetes.io/docs/reference/kubernetes-api/config-and-storage-resources/persistent-volume-v1/#PersistentVolumeStatus + K8SPersistentvolumeStatusPhaseKey = attribute.Key("k8s.persistentvolume.status.phase") + + // K8SPersistentvolumeUIDKey is the attribute Key conforming to the + // "k8s.persistentvolume.uid" semantic conventions. It represents the UID of the + // PersistentVolume. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "275ecb36-5aa8-4c2a-9c47-d8bb681b9aff" + K8SPersistentvolumeUIDKey = attribute.Key("k8s.persistentvolume.uid") + + // K8SPersistentvolumeclaimNameKey is the attribute Key conforming to the + // "k8s.persistentvolumeclaim.name" semantic conventions. It represents the name + // of the PersistentVolumeClaim. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "pvc-data-01" + K8SPersistentvolumeclaimNameKey = attribute.Key("k8s.persistentvolumeclaim.name") + + // K8SPersistentvolumeclaimStatusPhaseKey is the attribute Key conforming to the + // "k8s.persistentvolumeclaim.status.phase" semantic conventions. It represents + // the phase of the PersistentVolumeClaim. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "Pending", "Bound", "Lost" + // Note: This attribute aligns with the `phase` field of the + // [K8s PersistentVolumeClaimStatus]. + // + // [K8s PersistentVolumeClaimStatus]: https://kubernetes.io/docs/reference/kubernetes-api/config-and-storage-resources/persistent-volume-claim-v1/#PersistentVolumeClaimStatus + K8SPersistentvolumeclaimStatusPhaseKey = attribute.Key("k8s.persistentvolumeclaim.status.phase") + + // K8SPersistentvolumeclaimUIDKey is the attribute Key conforming to the + // "k8s.persistentvolumeclaim.uid" semantic conventions. It represents the UID + // of the PersistentVolumeClaim. + // + // Type: string + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "275ecb36-5aa8-4c2a-9c47-d8bb681b9aff" + K8SPersistentvolumeclaimUIDKey = attribute.Key("k8s.persistentvolumeclaim.uid") + // K8SPodHostnameKey is the attribute Key conforming to the "k8s.pod.hostname" // semantic conventions. It represents the specifies the hostname of the Pod. // // Type: string // RequirementLevel: Recommended - // Stability: Beta + // Stability: Release_Candidate // // Examples: "collector-gateway" // Note: The K8s Pod spec has an optional hostname field, which can be used to @@ -9405,7 +9681,7 @@ const ( // // Type: string // RequirementLevel: Recommended - // Stability: Beta + // Stability: Release_Candidate // // Examples: "172.18.0.2" // Note: This attribute aligns with the `podIP` field of the @@ -9419,7 +9695,7 @@ const ( // // Type: string // RequirementLevel: Recommended - // Stability: Beta + // Stability: Release_Candidate // // Examples: "opentelemetry-pod-autoconf" K8SPodNameKey = attribute.Key("k8s.pod.name") @@ -9430,7 +9706,7 @@ const ( // // Type: string // RequirementLevel: Recommended - // Stability: Beta + // Stability: Release_Candidate // // Examples: "2025-12-04T08:41:03Z" // Note: Date and time at which the object was acknowledged by the Kubelet. @@ -9474,7 +9750,7 @@ const ( // // Type: string // RequirementLevel: Recommended - // Stability: Beta + // Stability: Release_Candidate // // Examples: "275ecb36-5aa8-4c2a-9c47-d8bb681b9aff" K8SPodUIDKey = attribute.Key("k8s.pod.uid") @@ -9485,7 +9761,7 @@ const ( // // Type: string // RequirementLevel: Recommended - // Stability: Beta + // Stability: Release_Candidate // // Examples: "opentelemetry" K8SReplicaSetNameKey = attribute.Key("k8s.replicaset.name") @@ -9496,7 +9772,7 @@ const ( // // Type: string // RequirementLevel: Recommended - // Stability: Beta + // Stability: Release_Candidate // // Examples: "275ecb36-5aa8-4c2a-9c47-d8bb681b9aff" K8SReplicaSetUIDKey = attribute.Key("k8s.replicaset.uid") @@ -9709,7 +9985,7 @@ const ( // // Type: string // RequirementLevel: Recommended - // Stability: Beta + // Stability: Release_Candidate // // Examples: "opentelemetry" K8SStatefulSetNameKey = attribute.Key("k8s.statefulset.name") @@ -9720,7 +9996,7 @@ const ( // // Type: string // RequirementLevel: Recommended - // Stability: Beta + // Stability: Release_Candidate // // Examples: "275ecb36-5aa8-4c2a-9c47-d8bb681b9aff" K8SStatefulSetUIDKey = attribute.Key("k8s.statefulset.uid") @@ -10005,12 +10281,80 @@ func K8SNodeName(val string) attribute.KeyValue { return K8SNodeNameKey.String(val) } +// K8SNodeSystemContainerName returns an attribute KeyValue conforming to the +// "k8s.node.system_container.name" semantic conventions. It represents the name +// of the system container running on the K8s Node. +func K8SNodeSystemContainerName(val string) attribute.KeyValue { + return K8SNodeSystemContainerNameKey.String(val) +} + // K8SNodeUID returns an attribute KeyValue conforming to the "k8s.node.uid" // semantic conventions. It represents the UID of the Node. func K8SNodeUID(val string) attribute.KeyValue { return K8SNodeUIDKey.String(val) } +// K8SPersistentvolumeAnnotation returns an attribute KeyValue conforming to the +// "k8s.persistentvolume.annotation" semantic conventions. It represents the +// annotation placed on the PersistentVolume, the `` being the annotation +// name, the value being the annotation value, even if the value is empty. +func K8SPersistentvolumeAnnotation(key string, val string) attribute.KeyValue { + return attribute.String("k8s.persistentvolume.annotation."+key, val) +} + +// K8SPersistentvolumeLabel returns an attribute KeyValue conforming to the +// "k8s.persistentvolume.label" semantic conventions. It represents the label +// placed on the PersistentVolume, the `` being the label name, the value +// being the label value, even if the value is empty. +func K8SPersistentvolumeLabel(key string, val string) attribute.KeyValue { + return attribute.String("k8s.persistentvolume.label."+key, val) +} + +// K8SPersistentvolumeName returns an attribute KeyValue conforming to the +// "k8s.persistentvolume.name" semantic conventions. It represents the name of +// the PersistentVolume. +func K8SPersistentvolumeName(val string) attribute.KeyValue { + return K8SPersistentvolumeNameKey.String(val) +} + +// K8SPersistentvolumeUID returns an attribute KeyValue conforming to the +// "k8s.persistentvolume.uid" semantic conventions. It represents the UID of the +// PersistentVolume. +func K8SPersistentvolumeUID(val string) attribute.KeyValue { + return K8SPersistentvolumeUIDKey.String(val) +} + +// K8SPersistentvolumeclaimAnnotation returns an attribute KeyValue conforming to +// the "k8s.persistentvolumeclaim.annotation" semantic conventions. It represents +// the annotation placed on the PersistentVolumeClaim, the `` being the +// annotation name, the value being the annotation value, even if the value is +// empty. +func K8SPersistentvolumeclaimAnnotation(key string, val string) attribute.KeyValue { + return attribute.String("k8s.persistentvolumeclaim.annotation."+key, val) +} + +// K8SPersistentvolumeclaimLabel returns an attribute KeyValue conforming to the +// "k8s.persistentvolumeclaim.label" semantic conventions. It represents the +// label placed on the PersistentVolumeClaim, the `` being the label name, +// the value being the label value, even if the value is empty. +func K8SPersistentvolumeclaimLabel(key string, val string) attribute.KeyValue { + return attribute.String("k8s.persistentvolumeclaim.label."+key, val) +} + +// K8SPersistentvolumeclaimName returns an attribute KeyValue conforming to the +// "k8s.persistentvolumeclaim.name" semantic conventions. It represents the name +// of the PersistentVolumeClaim. +func K8SPersistentvolumeclaimName(val string) attribute.KeyValue { + return K8SPersistentvolumeclaimNameKey.String(val) +} + +// K8SPersistentvolumeclaimUID returns an attribute KeyValue conforming to the +// "k8s.persistentvolumeclaim.uid" semantic conventions. It represents the UID of +// the PersistentVolumeClaim. +func K8SPersistentvolumeclaimUID(val string) attribute.KeyValue { + return K8SPersistentvolumeclaimUIDKey.String(val) +} + // K8SPodAnnotation returns an attribute KeyValue conforming to the // "k8s.pod.annotation" semantic conventions. It represents the annotation placed // on the Pod, the `` being the annotation name, the value being the @@ -10318,6 +10662,51 @@ var ( K8SNodeConditionTypeNetworkUnavailable = K8SNodeConditionTypeKey.String("NetworkUnavailable") ) +// Enum values for k8s.persistentvolume.reclaim_policy +var ( + // The volume will be deleted when released from its claim. + // Stability: development + K8SPersistentvolumeReclaimPolicyDelete = K8SPersistentvolumeReclaimPolicyKey.String("Delete") + // The volume will be recycled (basic scrub) when released from its claim. + // Stability: development + K8SPersistentvolumeReclaimPolicyRecycle = K8SPersistentvolumeReclaimPolicyKey.String("Recycle") + // The volume will be retained when released from its claim. + // Stability: development + K8SPersistentvolumeReclaimPolicyRetain = K8SPersistentvolumeReclaimPolicyKey.String("Retain") +) + +// Enum values for k8s.persistentvolume.status.phase +var ( + // The volume is available and not yet bound to a claim. + // Stability: development + K8SPersistentvolumeStatusPhaseAvailable = K8SPersistentvolumeStatusPhaseKey.String("Available") + // The volume is bound to a claim. + // Stability: development + K8SPersistentvolumeStatusPhaseBound = K8SPersistentvolumeStatusPhaseKey.String("Bound") + // The volume has failed its automatic reclamation. + // Stability: development + K8SPersistentvolumeStatusPhaseFailed = K8SPersistentvolumeStatusPhaseKey.String("Failed") + // The volume is being provisioned. + // Stability: development + K8SPersistentvolumeStatusPhasePending = K8SPersistentvolumeStatusPhaseKey.String("Pending") + // The claim has been deleted but the volume is not yet available. + // Stability: development + K8SPersistentvolumeStatusPhaseReleased = K8SPersistentvolumeStatusPhaseKey.String("Released") +) + +// Enum values for k8s.persistentvolumeclaim.status.phase +var ( + // The claim is bound to a volume. + // Stability: development + K8SPersistentvolumeclaimStatusPhaseBound = K8SPersistentvolumeclaimStatusPhaseKey.String("Bound") + // The claim has lost its underlying volume (the volume does not exist anymore). + // Stability: development + K8SPersistentvolumeclaimStatusPhaseLost = K8SPersistentvolumeclaimStatusPhaseKey.String("Lost") + // The claim has not yet been bound to a volume. + // Stability: development + K8SPersistentvolumeclaimStatusPhasePending = K8SPersistentvolumeclaimStatusPhaseKey.String("Pending") +) + // Enum values for k8s.pod.status.phase var ( // The pod has been accepted by the system, but one or more of the containers @@ -12669,7 +13058,7 @@ const ( // // Type: string // RequirementLevel: Recommended - // Stability: Development + // Stability: Stable // // Examples: "browser.mouse.click", "device.app.lifecycle" // Note: This attribute SHOULD be used by non-OTLP exporters when destination @@ -13240,14 +13629,27 @@ const ( // ProcessExecutableBuildIDHtlhashKey is the attribute Key conforming to the // "process.executable.build_id.htlhash" semantic conventions. It represents the - // profiling specific build ID for executables. See the OTel specification for - // Profiles for more information. + // deterministic build ID for executables. // // Type: string // RequirementLevel: Recommended // Stability: Development // // Examples: "600DCAFE4A110000F2BF38C493F5FB92" + // Note: GNU and Go build IDs may be stripped or unavailable in some + // environments + // (e.g., Alpine Linux, Docker images). This attribute provides a deterministic + // build ID computed by hashing the first and last 4096 bytes of the file + // along with its length: + // + // ``` + // Input ← Concat(File[:4096], File[-4096:], BigEndianUInt64(Len(File))) + // Digest ← SHA256(Input) + // BuildID ← Digest[:16] + // ``` + // + // The result is the first 16 bytes (128 bits) of the SHA256 digest, + // represented as a hex string. ProcessExecutableBuildIDHtlhashKey = attribute.Key("process.executable.build_id.htlhash") // ProcessExecutableNameKey is the attribute Key conforming to the @@ -13603,8 +14005,7 @@ func ProcessExecutableBuildIDGo(val string) attribute.KeyValue { // ProcessExecutableBuildIDHtlhash returns an attribute KeyValue conforming to // the "process.executable.build_id.htlhash" semantic conventions. It represents -// the profiling specific build ID for executables. See the OTel specification -// for Profiles for more information. +// the deterministic build ID for executables. func ProcessExecutableBuildIDHtlhash(val string) attribute.KeyValue { return ProcessExecutableBuildIDHtlhashKey.String(val) } @@ -14317,9 +14718,11 @@ const ( // Examples: "shoppingcart" // Note: MUST be the same for all instances of horizontally scaled services. If // the value was not specified, SDKs MUST fallback to `unknown_service:` - // concatenated with [`process.executable.name`], e.g. `unknown_service:bash`. - // If `process.executable.name` is not available, the value MUST be set to + // concatenated with the process executable name, e.g. `unknown_service:bash`. + // If the process executable name is not available, the value MUST be set to // `unknown_service`. + // The process executable name is the name of the process executable, the same + // value as described by the [`process.executable.name`] resource attribute. // // [`process.executable.name`]: process.md ServiceNameKey = attribute.Key("service.name") @@ -14643,6 +15046,17 @@ const ( // Examples: "ext4" SystemFilesystemTypeKey = attribute.Key("system.filesystem.type") + // SystemMemoryLinuxHugepagesStateKey is the attribute Key conforming to the + // "system.memory.linux.hugepages.state" semantic conventions. It represents the + // Linux HugePages memory state. + // + // Type: Enum + // RequirementLevel: Recommended + // Stability: Development + // + // Examples: "free", "used" + SystemMemoryLinuxHugepagesStateKey = attribute.Key("system.memory.linux.hugepages.state") + // SystemMemoryLinuxSlabStateKey is the attribute Key conforming to the // "system.memory.linux.slab.state" semantic conventions. It represents the // Linux Slab memory state. @@ -14753,6 +15167,16 @@ var ( SystemFilesystemTypeExt4 = SystemFilesystemTypeKey.String("ext4") ) +// Enum values for system.memory.linux.hugepages.state +var ( + // free + // Stability: development + SystemMemoryLinuxHugepagesStateFree = SystemMemoryLinuxHugepagesStateKey.String("free") + // used + // Stability: development + SystemMemoryLinuxHugepagesStateUsed = SystemMemoryLinuxHugepagesStateKey.String("used") +) + // Enum values for system.memory.linux.slab.state var ( // reclaimable @@ -14817,7 +15241,7 @@ const ( // // Type: string // RequirementLevel: Recommended - // Stability: Development + // Stability: Stable // // Examples: "parts-unlimited-java" // Note: Official auto instrumentation agents and distributions SHOULD set the @@ -14832,7 +15256,7 @@ const ( // // Type: string // RequirementLevel: Recommended - // Stability: Development + // Stability: Stable // // Examples: "1.2.3" TelemetryDistroVersionKey = attribute.Key("telemetry.distro.version") diff --git a/vendor/go.opentelemetry.io/otel/semconv/v1.40.0/doc.go b/vendor/go.opentelemetry.io/otel/semconv/v1.41.0/doc.go similarity index 82% rename from vendor/go.opentelemetry.io/otel/semconv/v1.40.0/doc.go rename to vendor/go.opentelemetry.io/otel/semconv/v1.41.0/doc.go index c5c41e4d27..a45d424d88 100644 --- a/vendor/go.opentelemetry.io/otel/semconv/v1.40.0/doc.go +++ b/vendor/go.opentelemetry.io/otel/semconv/v1.41.0/doc.go @@ -1,9 +1,11 @@ +// Code generated from semantic convention specification. DO NOT EDIT. + // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 // Package semconv implements OpenTelemetry semantic conventions. // // OpenTelemetry semantic conventions are agreed standardized naming -// patterns for OpenTelemetry things. This package represents the v1.40.0 +// patterns for OpenTelemetry things. This package represents the v1.41.0 // version of the OpenTelemetry semantic conventions. -package semconv // import "go.opentelemetry.io/otel/semconv/v1.40.0" +package semconv // import "go.opentelemetry.io/otel/semconv/v1.41.0" diff --git a/vendor/go.opentelemetry.io/otel/semconv/v1.40.0/error_type.go b/vendor/go.opentelemetry.io/otel/semconv/v1.41.0/error_type.go similarity index 75% rename from vendor/go.opentelemetry.io/otel/semconv/v1.40.0/error_type.go rename to vendor/go.opentelemetry.io/otel/semconv/v1.41.0/error_type.go index 6d26e52821..0b13f0de8e 100644 --- a/vendor/go.opentelemetry.io/otel/semconv/v1.40.0/error_type.go +++ b/vendor/go.opentelemetry.io/otel/semconv/v1.41.0/error_type.go @@ -1,10 +1,13 @@ +// Code generated from semantic convention specification. DO NOT EDIT. + // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -package semconv // import "go.opentelemetry.io/otel/semconv/v1.40.0" +package semconv // import "go.opentelemetry.io/otel/semconv/v1.41.0" import ( "errors" + "fmt" "reflect" "go.opentelemetry.io/otel/attribute" @@ -22,7 +25,8 @@ import ( // the returned attribute has that method's return value. If multiple errors in // the chain implement this method, the value from the first match found by // [errors.As] is used. Otherwise, the returned attribute has a value derived -// from the concrete type of err. +// from the concrete type of err after unwrapping any wrappers created with +// [fmt.Errorf]. // // The key of the returned attribute is [ErrorTypeKey]. func ErrorType(err error) attribute.KeyValue { @@ -50,7 +54,7 @@ func errorType(err error) string { // Fallback to reflection if the ErrorType method is not supported or // returns an empty value. - t := reflect.TypeOf(err) + t := reflect.TypeOf(unwrapFmtWrapped(err)) pkg, name := t.PkgPath(), t.Name() if pkg != "" && name != "" { s = pkg + "." + name @@ -64,3 +68,16 @@ func errorType(err error) string { } return s } + +var fmtWrapErrorType = reflect.TypeOf(fmt.Errorf("wrapped: %w", errors.New("err"))) + +func unwrapFmtWrapped(err error) error { + for reflect.TypeOf(err) == fmtWrapErrorType { + u := errors.Unwrap(err) + if u == nil { + return err // When the wrapped error is nil, use the concrete type of the wrapper. + } + err = u + } + return err +} diff --git a/vendor/go.opentelemetry.io/otel/semconv/v1.40.0/exception.go b/vendor/go.opentelemetry.io/otel/semconv/v1.41.0/exception.go similarity index 77% rename from vendor/go.opentelemetry.io/otel/semconv/v1.40.0/exception.go rename to vendor/go.opentelemetry.io/otel/semconv/v1.41.0/exception.go index 6a26231a1a..5f0151affa 100644 --- a/vendor/go.opentelemetry.io/otel/semconv/v1.40.0/exception.go +++ b/vendor/go.opentelemetry.io/otel/semconv/v1.41.0/exception.go @@ -1,7 +1,9 @@ +// Code generated from semantic convention specification. DO NOT EDIT. + // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -package semconv // import "go.opentelemetry.io/otel/semconv/v1.40.0" +package semconv // import "go.opentelemetry.io/otel/semconv/v1.41.0" const ( // ExceptionEventName is the name of the Span event representing an exception. diff --git a/vendor/go.opentelemetry.io/otel/semconv/v1.40.0/httpconv/metric.go b/vendor/go.opentelemetry.io/otel/semconv/v1.41.0/httpconv/metric.go similarity index 82% rename from vendor/go.opentelemetry.io/otel/semconv/v1.40.0/httpconv/metric.go rename to vendor/go.opentelemetry.io/otel/semconv/v1.41.0/httpconv/metric.go index 7264925ba9..1b811e2d72 100644 --- a/vendor/go.opentelemetry.io/otel/semconv/v1.40.0/httpconv/metric.go +++ b/vendor/go.opentelemetry.io/otel/semconv/v1.41.0/httpconv/metric.go @@ -159,6 +159,9 @@ func (m ClientActiveRequests) Add( serverPort int, attrs ...attribute.KeyValue, ) { + if !m.Int64UpDownCounter.Enabled(ctx) { + return + } if len(attrs) == 0 { m.Int64UpDownCounter.Add(ctx, incr, metric.WithAttributes( attribute.String("server.address", serverAddress), @@ -169,6 +172,7 @@ func (m ClientActiveRequests) Add( o := addOptPool.Get().(*[]metric.AddOption) defer func() { + clear(*o) *o = (*o)[:0] addOptPool.Put(o) }() @@ -189,6 +193,9 @@ func (m ClientActiveRequests) Add( // AddSet adds incr to the existing count for set. func (m ClientActiveRequests) AddSet(ctx context.Context, incr int64, set attribute.Set) { + if !m.Int64UpDownCounter.Enabled(ctx) { + return + } if set.Len() == 0 { m.Int64UpDownCounter.Add(ctx, incr) return @@ -196,6 +203,7 @@ func (m ClientActiveRequests) AddSet(ctx context.Context, incr int64, set attrib o := addOptPool.Get().(*[]metric.AddOption) defer func() { + clear(*o) *o = (*o)[:0] addOptPool.Put(o) }() @@ -228,6 +236,102 @@ func (ClientActiveRequests) AttrURLScheme(val string) attribute.KeyValue { return attribute.String("url.scheme", val) } +// ClientActiveRequestsObservable is an instrument used to record metric values +// conforming to the "http.client.active_requests" semantic conventions. It +// represents the number of active HTTP requests. +type ClientActiveRequestsObservable struct { + metric.Int64ObservableUpDownCounter +} + +var newClientActiveRequestsObservableOpts = []metric.Int64ObservableUpDownCounterOption{ + metric.WithDescription("Number of active HTTP requests."), + metric.WithUnit("{request}"), +} + +// NewClientActiveRequestsObservable returns a new ClientActiveRequestsObservable +// instrument. +func NewClientActiveRequestsObservable( + m metric.Meter, + opt ...metric.Int64ObservableUpDownCounterOption, +) (ClientActiveRequestsObservable, error) { + // Check if the meter is nil. + if m == nil { + return ClientActiveRequestsObservable{noop.Int64ObservableUpDownCounter{}}, nil + } + + if len(opt) == 0 { + opt = newClientActiveRequestsObservableOpts + } else { + opt = append(opt, newClientActiveRequestsObservableOpts...) + } + + i, err := m.Int64ObservableUpDownCounter( + "http.client.active_requests", + opt..., + ) + if err != nil { + return ClientActiveRequestsObservable{noop.Int64ObservableUpDownCounter{}}, err + } + return ClientActiveRequestsObservable{i}, nil +} + +// Inst returns the underlying metric instrument. +func (m ClientActiveRequestsObservable) Inst() metric.Int64ObservableUpDownCounter { + return m.Int64ObservableUpDownCounter +} + +// Name returns the semantic convention name of the instrument. +func (ClientActiveRequestsObservable) Name() string { + return "http.client.active_requests" +} + +// Unit returns the semantic convention unit of the instrument +func (ClientActiveRequestsObservable) Unit() string { + return "{request}" +} + +// Description returns the semantic convention description of the instrument +func (ClientActiveRequestsObservable) Description() string { + return "Number of active HTTP requests." +} + +// AttrServerAddress returns a required attribute for the "server.address" +// semantic convention. It represents the server domain name if available without +// reverse DNS lookup; otherwise, IP address or Unix domain socket name. +func (ClientActiveRequestsObservable) AttrServerAddress(val string) attribute.KeyValue { + return attribute.String("server.address", val) +} + +// AttrServerPort returns a required attribute for the "server.port" semantic +// convention. It represents the server port number. +func (ClientActiveRequestsObservable) AttrServerPort(val int) attribute.KeyValue { + return attribute.Int("server.port", val) +} + +// AttrURLTemplate returns an optional attribute for the "url.template" semantic +// convention. It represents the low-cardinality template of an +// [absolute path reference]. +// +// [absolute path reference]: https://www.rfc-editor.org/rfc/rfc3986#section-4.2 +func (ClientActiveRequestsObservable) AttrURLTemplate(val string) attribute.KeyValue { + return attribute.String("url.template", val) +} + +// AttrRequestMethod returns an optional attribute for the "http.request.method" +// semantic convention. It represents the HTTP request method. +func (ClientActiveRequestsObservable) AttrRequestMethod(val RequestMethodAttr) attribute.KeyValue { + return attribute.String("http.request.method", string(val)) +} + +// AttrURLScheme returns an optional attribute for the "url.scheme" semantic +// convention. It represents the [URI scheme] component identifying the used +// protocol. +// +// [URI scheme]: https://www.rfc-editor.org/rfc/rfc3986#section-3.1 +func (ClientActiveRequestsObservable) AttrURLScheme(val string) attribute.KeyValue { + return attribute.String("url.scheme", val) +} + // ClientConnectionDuration is an instrument used to record metric values // conforming to the "http.client.connection.duration" semantic conventions. It // represents the duration of the successfully established outbound HTTP @@ -302,6 +406,9 @@ func (m ClientConnectionDuration) Record( serverPort int, attrs ...attribute.KeyValue, ) { + if !m.Float64Histogram.Enabled(ctx) { + return + } if len(attrs) == 0 { m.Float64Histogram.Record(ctx, val, metric.WithAttributes( attribute.String("server.address", serverAddress), @@ -312,6 +419,7 @@ func (m ClientConnectionDuration) Record( o := recOptPool.Get().(*[]metric.RecordOption) defer func() { + clear(*o) *o = (*o)[:0] recOptPool.Put(o) }() @@ -332,6 +440,9 @@ func (m ClientConnectionDuration) Record( // RecordSet records val to the current distribution for set. func (m ClientConnectionDuration) RecordSet(ctx context.Context, val float64, set attribute.Set) { + if !m.Float64Histogram.Enabled(ctx) { + return + } if set.Len() == 0 { m.Float64Histogram.Record(ctx, val) return @@ -339,6 +450,7 @@ func (m ClientConnectionDuration) RecordSet(ctx context.Context, val float64, se o := recOptPool.Get().(*[]metric.RecordOption) defer func() { + clear(*o) *o = (*o)[:0] recOptPool.Put(o) }() @@ -448,6 +560,9 @@ func (m ClientOpenConnections) Add( serverPort int, attrs ...attribute.KeyValue, ) { + if !m.Int64UpDownCounter.Enabled(ctx) { + return + } if len(attrs) == 0 { m.Int64UpDownCounter.Add(ctx, incr, metric.WithAttributes( attribute.String("http.connection.state", string(connectionState)), @@ -459,6 +574,7 @@ func (m ClientOpenConnections) Add( o := addOptPool.Get().(*[]metric.AddOption) defer func() { + clear(*o) *o = (*o)[:0] addOptPool.Put(o) }() @@ -480,6 +596,9 @@ func (m ClientOpenConnections) Add( // AddSet adds incr to the existing count for set. func (m ClientOpenConnections) AddSet(ctx context.Context, incr int64, set attribute.Set) { + if !m.Int64UpDownCounter.Enabled(ctx) { + return + } if set.Len() == 0 { m.Int64UpDownCounter.Add(ctx, incr) return @@ -487,6 +606,7 @@ func (m ClientOpenConnections) AddSet(ctx context.Context, incr int64, set attri o := addOptPool.Get().(*[]metric.AddOption) defer func() { + clear(*o) *o = (*o)[:0] addOptPool.Put(o) }() @@ -518,6 +638,109 @@ func (ClientOpenConnections) AttrURLScheme(val string) attribute.KeyValue { return attribute.String("url.scheme", val) } +// ClientOpenConnectionsObservable is an instrument used to record metric values +// conforming to the "http.client.open_connections" semantic conventions. It +// represents the number of outbound HTTP connections that are currently active +// or idle on the client. +type ClientOpenConnectionsObservable struct { + metric.Int64ObservableUpDownCounter +} + +var newClientOpenConnectionsObservableOpts = []metric.Int64ObservableUpDownCounterOption{ + metric.WithDescription("Number of outbound HTTP connections that are currently active or idle on the client."), + metric.WithUnit("{connection}"), +} + +// NewClientOpenConnectionsObservable returns a new +// ClientOpenConnectionsObservable instrument. +func NewClientOpenConnectionsObservable( + m metric.Meter, + opt ...metric.Int64ObservableUpDownCounterOption, +) (ClientOpenConnectionsObservable, error) { + // Check if the meter is nil. + if m == nil { + return ClientOpenConnectionsObservable{noop.Int64ObservableUpDownCounter{}}, nil + } + + if len(opt) == 0 { + opt = newClientOpenConnectionsObservableOpts + } else { + opt = append(opt, newClientOpenConnectionsObservableOpts...) + } + + i, err := m.Int64ObservableUpDownCounter( + "http.client.open_connections", + opt..., + ) + if err != nil { + return ClientOpenConnectionsObservable{noop.Int64ObservableUpDownCounter{}}, err + } + return ClientOpenConnectionsObservable{i}, nil +} + +// Inst returns the underlying metric instrument. +func (m ClientOpenConnectionsObservable) Inst() metric.Int64ObservableUpDownCounter { + return m.Int64ObservableUpDownCounter +} + +// Name returns the semantic convention name of the instrument. +func (ClientOpenConnectionsObservable) Name() string { + return "http.client.open_connections" +} + +// Unit returns the semantic convention unit of the instrument +func (ClientOpenConnectionsObservable) Unit() string { + return "{connection}" +} + +// Description returns the semantic convention description of the instrument +func (ClientOpenConnectionsObservable) Description() string { + return "Number of outbound HTTP connections that are currently active or idle on the client." +} + +// AttrConnectionState returns a required attribute for the +// "http.connection.state" semantic convention. It represents the state of the +// HTTP connection in the HTTP connection pool. +func (ClientOpenConnectionsObservable) AttrConnectionState(val ConnectionStateAttr) attribute.KeyValue { + return attribute.String("http.connection.state", string(val)) +} + +// AttrServerAddress returns a required attribute for the "server.address" +// semantic convention. It represents the server domain name if available without +// reverse DNS lookup; otherwise, IP address or Unix domain socket name. +func (ClientOpenConnectionsObservable) AttrServerAddress(val string) attribute.KeyValue { + return attribute.String("server.address", val) +} + +// AttrServerPort returns a required attribute for the "server.port" semantic +// convention. It represents the server port number. +func (ClientOpenConnectionsObservable) AttrServerPort(val int) attribute.KeyValue { + return attribute.Int("server.port", val) +} + +// AttrNetworkPeerAddress returns an optional attribute for the +// "network.peer.address" semantic convention. It represents the peer address of +// the network connection - IP address or Unix domain socket name. +func (ClientOpenConnectionsObservable) AttrNetworkPeerAddress(val string) attribute.KeyValue { + return attribute.String("network.peer.address", val) +} + +// AttrNetworkProtocolVersion returns an optional attribute for the +// "network.protocol.version" semantic convention. It represents the actual +// version of the protocol used for network communication. +func (ClientOpenConnectionsObservable) AttrNetworkProtocolVersion(val string) attribute.KeyValue { + return attribute.String("network.protocol.version", val) +} + +// AttrURLScheme returns an optional attribute for the "url.scheme" semantic +// convention. It represents the [URI scheme] component identifying the used +// protocol. +// +// [URI scheme]: https://www.rfc-editor.org/rfc/rfc3986#section-3.1 +func (ClientOpenConnectionsObservable) AttrURLScheme(val string) attribute.KeyValue { + return attribute.String("url.scheme", val) +} + // ClientRequestBodySize is an instrument used to record metric values conforming // to the "http.client.request.body.size" semantic conventions. It represents the // size of HTTP client request bodies. @@ -601,6 +824,9 @@ func (m ClientRequestBodySize) Record( serverPort int, attrs ...attribute.KeyValue, ) { + if !m.Int64Histogram.Enabled(ctx) { + return + } if len(attrs) == 0 { m.Int64Histogram.Record(ctx, val, metric.WithAttributes( attribute.String("http.request.method", string(requestMethod)), @@ -612,6 +838,7 @@ func (m ClientRequestBodySize) Record( o := recOptPool.Get().(*[]metric.RecordOption) defer func() { + clear(*o) *o = (*o)[:0] recOptPool.Put(o) }() @@ -640,6 +867,9 @@ func (m ClientRequestBodySize) Record( // // [Content-Length]: https://www.rfc-editor.org/rfc/rfc9110.html#field.content-length func (m ClientRequestBodySize) RecordSet(ctx context.Context, val int64, set attribute.Set) { + if !m.Int64Histogram.Enabled(ctx) { + return + } if set.Len() == 0 { m.Int64Histogram.Record(ctx, val) return @@ -647,6 +877,7 @@ func (m ClientRequestBodySize) RecordSet(ctx context.Context, val int64, set att o := recOptPool.Get().(*[]metric.RecordOption) defer func() { + clear(*o) *o = (*o)[:0] recOptPool.Put(o) }() @@ -715,6 +946,7 @@ type ClientRequestDuration struct { var newClientRequestDurationOpts = []metric.Float64HistogramOption{ metric.WithDescription("Duration of HTTP client requests."), metric.WithUnit("s"), + metric.WithExplicitBucketBoundaries([]float64{0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10}...), } // NewClientRequestDuration returns a new ClientRequestDuration instrument. @@ -781,6 +1013,9 @@ func (m ClientRequestDuration) Record( serverPort int, attrs ...attribute.KeyValue, ) { + if !m.Float64Histogram.Enabled(ctx) { + return + } if len(attrs) == 0 { m.Float64Histogram.Record(ctx, val, metric.WithAttributes( attribute.String("http.request.method", string(requestMethod)), @@ -792,6 +1027,7 @@ func (m ClientRequestDuration) Record( o := recOptPool.Get().(*[]metric.RecordOption) defer func() { + clear(*o) *o = (*o)[:0] recOptPool.Put(o) }() @@ -813,6 +1049,9 @@ func (m ClientRequestDuration) Record( // RecordSet records val to the current distribution for set. func (m ClientRequestDuration) RecordSet(ctx context.Context, val float64, set attribute.Set) { + if !m.Float64Histogram.Enabled(ctx) { + return + } if set.Len() == 0 { m.Float64Histogram.Record(ctx, val) return @@ -820,6 +1059,7 @@ func (m ClientRequestDuration) RecordSet(ctx context.Context, val float64, set a o := recOptPool.Get().(*[]metric.RecordOption) defer func() { + clear(*o) *o = (*o)[:0] recOptPool.Put(o) }() @@ -961,6 +1201,9 @@ func (m ClientResponseBodySize) Record( serverPort int, attrs ...attribute.KeyValue, ) { + if !m.Int64Histogram.Enabled(ctx) { + return + } if len(attrs) == 0 { m.Int64Histogram.Record(ctx, val, metric.WithAttributes( attribute.String("http.request.method", string(requestMethod)), @@ -972,6 +1215,7 @@ func (m ClientResponseBodySize) Record( o := recOptPool.Get().(*[]metric.RecordOption) defer func() { + clear(*o) *o = (*o)[:0] recOptPool.Put(o) }() @@ -1000,6 +1244,9 @@ func (m ClientResponseBodySize) Record( // // [Content-Length]: https://www.rfc-editor.org/rfc/rfc9110.html#field.content-length func (m ClientResponseBodySize) RecordSet(ctx context.Context, val int64, set attribute.Set) { + if !m.Int64Histogram.Enabled(ctx) { + return + } if set.Len() == 0 { m.Int64Histogram.Record(ctx, val) return @@ -1007,6 +1254,7 @@ func (m ClientResponseBodySize) RecordSet(ctx context.Context, val int64, set at o := recOptPool.Get().(*[]metric.RecordOption) defer func() { + clear(*o) *o = (*o)[:0] recOptPool.Put(o) }() @@ -1139,6 +1387,9 @@ func (m ServerActiveRequests) Add( urlScheme string, attrs ...attribute.KeyValue, ) { + if !m.Int64UpDownCounter.Enabled(ctx) { + return + } if len(attrs) == 0 { m.Int64UpDownCounter.Add(ctx, incr, metric.WithAttributes( attribute.String("http.request.method", string(requestMethod)), @@ -1149,6 +1400,7 @@ func (m ServerActiveRequests) Add( o := addOptPool.Get().(*[]metric.AddOption) defer func() { + clear(*o) *o = (*o)[:0] addOptPool.Put(o) }() @@ -1169,6 +1421,9 @@ func (m ServerActiveRequests) Add( // AddSet adds incr to the existing count for set. func (m ServerActiveRequests) AddSet(ctx context.Context, incr int64, set attribute.Set) { + if !m.Int64UpDownCounter.Enabled(ctx) { + return + } if set.Len() == 0 { m.Int64UpDownCounter.Add(ctx, incr) return @@ -1176,6 +1431,7 @@ func (m ServerActiveRequests) AddSet(ctx context.Context, incr int64, set attrib o := addOptPool.Get().(*[]metric.AddOption) defer func() { + clear(*o) *o = (*o)[:0] addOptPool.Put(o) }() @@ -1198,6 +1454,94 @@ func (ServerActiveRequests) AttrServerPort(val int) attribute.KeyValue { return attribute.Int("server.port", val) } +// ServerActiveRequestsObservable is an instrument used to record metric values +// conforming to the "http.server.active_requests" semantic conventions. It +// represents the number of active HTTP server requests. +type ServerActiveRequestsObservable struct { + metric.Int64ObservableUpDownCounter +} + +var newServerActiveRequestsObservableOpts = []metric.Int64ObservableUpDownCounterOption{ + metric.WithDescription("Number of active HTTP server requests."), + metric.WithUnit("{request}"), +} + +// NewServerActiveRequestsObservable returns a new ServerActiveRequestsObservable +// instrument. +func NewServerActiveRequestsObservable( + m metric.Meter, + opt ...metric.Int64ObservableUpDownCounterOption, +) (ServerActiveRequestsObservable, error) { + // Check if the meter is nil. + if m == nil { + return ServerActiveRequestsObservable{noop.Int64ObservableUpDownCounter{}}, nil + } + + if len(opt) == 0 { + opt = newServerActiveRequestsObservableOpts + } else { + opt = append(opt, newServerActiveRequestsObservableOpts...) + } + + i, err := m.Int64ObservableUpDownCounter( + "http.server.active_requests", + opt..., + ) + if err != nil { + return ServerActiveRequestsObservable{noop.Int64ObservableUpDownCounter{}}, err + } + return ServerActiveRequestsObservable{i}, nil +} + +// Inst returns the underlying metric instrument. +func (m ServerActiveRequestsObservable) Inst() metric.Int64ObservableUpDownCounter { + return m.Int64ObservableUpDownCounter +} + +// Name returns the semantic convention name of the instrument. +func (ServerActiveRequestsObservable) Name() string { + return "http.server.active_requests" +} + +// Unit returns the semantic convention unit of the instrument +func (ServerActiveRequestsObservable) Unit() string { + return "{request}" +} + +// Description returns the semantic convention description of the instrument +func (ServerActiveRequestsObservable) Description() string { + return "Number of active HTTP server requests." +} + +// AttrRequestMethod returns a required attribute for the "http.request.method" +// semantic convention. It represents the HTTP request method. +func (ServerActiveRequestsObservable) AttrRequestMethod(val RequestMethodAttr) attribute.KeyValue { + return attribute.String("http.request.method", string(val)) +} + +// AttrURLScheme returns a required attribute for the "url.scheme" semantic +// convention. It represents the [URI scheme] component identifying the used +// protocol. +// +// [URI scheme]: https://www.rfc-editor.org/rfc/rfc3986#section-3.1 +func (ServerActiveRequestsObservable) AttrURLScheme(val string) attribute.KeyValue { + return attribute.String("url.scheme", val) +} + +// AttrServerAddress returns an optional attribute for the "server.address" +// semantic convention. It represents the name of the local HTTP server that +// received the request. +func (ServerActiveRequestsObservable) AttrServerAddress(val string) attribute.KeyValue { + return attribute.String("server.address", val) +} + +// AttrServerPort returns an optional attribute for the "server.port" semantic +// convention. It represents the port of the local HTTP server that received the +// request. +func (ServerActiveRequestsObservable) AttrServerPort(val int) attribute.KeyValue { + return attribute.Int("server.port", val) +} + // ServerRequestBodySize is an instrument used to record metric values conforming // to the "http.server.request.body.size" semantic conventions. It represents the // size of HTTP server request bodies. @@ -1279,6 +1623,9 @@ func (m ServerRequestBodySize) Record( urlScheme string, attrs ...attribute.KeyValue, ) { + if !m.Int64Histogram.Enabled(ctx) { + return + } if len(attrs) == 0 { m.Int64Histogram.Record(ctx, val, metric.WithAttributes( attribute.String("http.request.method", string(requestMethod)), @@ -1289,6 +1636,7 @@ func (m ServerRequestBodySize) Record( o := recOptPool.Get().(*[]metric.RecordOption) defer func() { + clear(*o) *o = (*o)[:0] recOptPool.Put(o) }() @@ -1316,6 +1664,9 @@ func (m ServerRequestBodySize) Record( // // [Content-Length]: https://www.rfc-editor.org/rfc/rfc9110.html#field.content-length func (m ServerRequestBodySize) RecordSet(ctx context.Context, val int64, set attribute.Set) { + if !m.Int64Histogram.Enabled(ctx) { + return + } if set.Len() == 0 { m.Int64Histogram.Record(ctx, val) return @@ -1323,6 +1674,7 @@ func (m ServerRequestBodySize) RecordSet(ctx context.Context, val int64, set att o := recOptPool.Get().(*[]metric.RecordOption) defer func() { + clear(*o) *o = (*o)[:0] recOptPool.Put(o) }() @@ -1402,6 +1754,7 @@ type ServerRequestDuration struct { var newServerRequestDurationOpts = []metric.Float64HistogramOption{ metric.WithDescription("Duration of HTTP server requests."), metric.WithUnit("s"), + metric.WithExplicitBucketBoundaries([]float64{0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10}...), } // NewServerRequestDuration returns a new ServerRequestDuration instrument. @@ -1466,6 +1819,9 @@ func (m ServerRequestDuration) Record( urlScheme string, attrs ...attribute.KeyValue, ) { + if !m.Float64Histogram.Enabled(ctx) { + return + } if len(attrs) == 0 { m.Float64Histogram.Record(ctx, val, metric.WithAttributes( attribute.String("http.request.method", string(requestMethod)), @@ -1476,6 +1832,7 @@ func (m ServerRequestDuration) Record( o := recOptPool.Get().(*[]metric.RecordOption) defer func() { + clear(*o) *o = (*o)[:0] recOptPool.Put(o) }() @@ -1496,6 +1853,9 @@ func (m ServerRequestDuration) Record( // RecordSet records val to the current distribution for set. func (m ServerRequestDuration) RecordSet(ctx context.Context, val float64, set attribute.Set) { + if !m.Float64Histogram.Enabled(ctx) { + return + } if set.Len() == 0 { m.Float64Histogram.Record(ctx, val) return @@ -1503,6 +1863,7 @@ func (m ServerRequestDuration) RecordSet(ctx context.Context, val float64, set a o := recOptPool.Get().(*[]metric.RecordOption) defer func() { + clear(*o) *o = (*o)[:0] recOptPool.Put(o) }() @@ -1653,6 +2014,9 @@ func (m ServerResponseBodySize) Record( urlScheme string, attrs ...attribute.KeyValue, ) { + if !m.Int64Histogram.Enabled(ctx) { + return + } if len(attrs) == 0 { m.Int64Histogram.Record(ctx, val, metric.WithAttributes( attribute.String("http.request.method", string(requestMethod)), @@ -1663,6 +2027,7 @@ func (m ServerResponseBodySize) Record( o := recOptPool.Get().(*[]metric.RecordOption) defer func() { + clear(*o) *o = (*o)[:0] recOptPool.Put(o) }() @@ -1690,6 +2055,9 @@ func (m ServerResponseBodySize) Record( // // [Content-Length]: https://www.rfc-editor.org/rfc/rfc9110.html#field.content-length func (m ServerResponseBodySize) RecordSet(ctx context.Context, val int64, set attribute.Set) { + if !m.Int64Histogram.Enabled(ctx) { + return + } if set.Len() == 0 { m.Int64Histogram.Record(ctx, val) return @@ -1697,6 +2065,7 @@ func (m ServerResponseBodySize) RecordSet(ctx context.Context, val int64, set at o := recOptPool.Get().(*[]metric.RecordOption) defer func() { + clear(*o) *o = (*o)[:0] recOptPool.Put(o) }() diff --git a/vendor/go.opentelemetry.io/otel/semconv/v1.40.0/otelconv/metric.go b/vendor/go.opentelemetry.io/otel/semconv/v1.41.0/otelconv/metric.go similarity index 66% rename from vendor/go.opentelemetry.io/otel/semconv/v1.40.0/otelconv/metric.go rename to vendor/go.opentelemetry.io/otel/semconv/v1.41.0/otelconv/metric.go index 901da86985..d50e198493 100644 --- a/vendor/go.opentelemetry.io/otel/semconv/v1.40.0/otelconv/metric.go +++ b/vendor/go.opentelemetry.io/otel/semconv/v1.41.0/otelconv/metric.go @@ -197,6 +197,9 @@ func (m SDKExporterLogExported) Add( incr int64, attrs ...attribute.KeyValue, ) { + if !m.Int64Counter.Enabled(ctx) { + return + } if len(attrs) == 0 { m.Int64Counter.Add(ctx, incr) return @@ -204,6 +207,7 @@ func (m SDKExporterLogExported) Add( o := addOptPool.Get().(*[]metric.AddOption) defer func() { + clear(*o) *o = (*o)[:0] addOptPool.Put(o) }() @@ -228,6 +232,9 @@ func (m SDKExporterLogExported) Add( // If no rejection reason is available, `rejected` SHOULD be used as value for // `error.type`. func (m SDKExporterLogExported) AddSet(ctx context.Context, incr int64, set attribute.Set) { + if !m.Int64Counter.Enabled(ctx) { + return + } if set.Len() == 0 { m.Int64Counter.Add(ctx, incr) return @@ -235,6 +242,7 @@ func (m SDKExporterLogExported) AddSet(ctx context.Context, incr int64, set attr o := addOptPool.Get().(*[]metric.AddOption) defer func() { + clear(*o) *o = (*o)[:0] addOptPool.Put(o) }() @@ -277,6 +285,100 @@ func (SDKExporterLogExported) AttrServerPort(val int) attribute.KeyValue { return attribute.Int("server.port", val) } +// SDKExporterLogExportedObservable is an instrument used to record metric values +// conforming to the "otel.sdk.exporter.log.exported" semantic conventions. It +// represents the number of log records for which the export has finished, either +// successful or failed. +type SDKExporterLogExportedObservable struct { + metric.Int64ObservableCounter +} + +var newSDKExporterLogExportedObservableOpts = []metric.Int64ObservableCounterOption{ + metric.WithDescription("The number of log records for which the export has finished, either successful or failed."), + metric.WithUnit("{log_record}"), +} + +// NewSDKExporterLogExportedObservable returns a new +// SDKExporterLogExportedObservable instrument. +func NewSDKExporterLogExportedObservable( + m metric.Meter, + opt ...metric.Int64ObservableCounterOption, +) (SDKExporterLogExportedObservable, error) { + // Check if the meter is nil. + if m == nil { + return SDKExporterLogExportedObservable{noop.Int64ObservableCounter{}}, nil + } + + if len(opt) == 0 { + opt = newSDKExporterLogExportedObservableOpts + } else { + opt = append(opt, newSDKExporterLogExportedObservableOpts...) + } + + i, err := m.Int64ObservableCounter( + "otel.sdk.exporter.log.exported", + opt..., + ) + if err != nil { + return SDKExporterLogExportedObservable{noop.Int64ObservableCounter{}}, err + } + return SDKExporterLogExportedObservable{i}, nil +} + +// Inst returns the underlying metric instrument. +func (m SDKExporterLogExportedObservable) Inst() metric.Int64ObservableCounter { + return m.Int64ObservableCounter +} + +// Name returns the semantic convention name of the instrument. +func (SDKExporterLogExportedObservable) Name() string { + return "otel.sdk.exporter.log.exported" +} + +// Unit returns the semantic convention unit of the instrument +func (SDKExporterLogExportedObservable) Unit() string { + return "{log_record}" +} + +// Description returns the semantic convention description of the instrument +func (SDKExporterLogExportedObservable) Description() string { + return "The number of log records for which the export has finished, either successful or failed." +} + +// AttrErrorType returns an optional attribute for the "error.type" semantic +// convention. It represents the describes a class of error the operation ended +// with. +func (SDKExporterLogExportedObservable) AttrErrorType(val ErrorTypeAttr) attribute.KeyValue { + return attribute.String("error.type", string(val)) +} + +// AttrComponentName returns an optional attribute for the "otel.component.name" +// semantic convention. It represents a name uniquely identifying the instance of +// the OpenTelemetry component within its containing SDK instance. +func (SDKExporterLogExportedObservable) AttrComponentName(val string) attribute.KeyValue { + return attribute.String("otel.component.name", val) +} + +// AttrComponentType returns an optional attribute for the "otel.component.type" +// semantic convention. It represents a name identifying the type of the +// OpenTelemetry component. +func (SDKExporterLogExportedObservable) AttrComponentType(val ComponentTypeAttr) attribute.KeyValue { + return attribute.String("otel.component.type", string(val)) +} + +// AttrServerAddress returns an optional attribute for the "server.address" +// semantic convention. It represents the server domain name if available without +// reverse DNS lookup; otherwise, IP address or Unix domain socket name. +func (SDKExporterLogExportedObservable) AttrServerAddress(val string) attribute.KeyValue { + return attribute.String("server.address", val) +} + +// AttrServerPort returns an optional attribute for the "server.port" semantic +// convention. It represents the server port number. +func (SDKExporterLogExportedObservable) AttrServerPort(val int) attribute.KeyValue { + return attribute.Int("server.port", val) +} + // SDKExporterLogInflight is an instrument used to record metric values // conforming to the "otel.sdk.exporter.log.inflight" semantic conventions. It // represents the number of log records which were passed to the exporter, but @@ -347,6 +449,9 @@ func (m SDKExporterLogInflight) Add( incr int64, attrs ...attribute.KeyValue, ) { + if !m.Int64UpDownCounter.Enabled(ctx) { + return + } if len(attrs) == 0 { m.Int64UpDownCounter.Add(ctx, incr) return @@ -354,6 +459,7 @@ func (m SDKExporterLogInflight) Add( o := addOptPool.Get().(*[]metric.AddOption) defer func() { + clear(*o) *o = (*o)[:0] addOptPool.Put(o) }() @@ -373,6 +479,9 @@ func (m SDKExporterLogInflight) Add( // For successful exports, `error.type` MUST NOT be set. For failed exports, // `error.type` MUST contain the failure cause. func (m SDKExporterLogInflight) AddSet(ctx context.Context, incr int64, set attribute.Set) { + if !m.Int64UpDownCounter.Enabled(ctx) { + return + } if set.Len() == 0 { m.Int64UpDownCounter.Add(ctx, incr) return @@ -380,6 +489,7 @@ func (m SDKExporterLogInflight) AddSet(ctx context.Context, incr int64, set attr o := addOptPool.Get().(*[]metric.AddOption) defer func() { + clear(*o) *o = (*o)[:0] addOptPool.Put(o) }() @@ -415,6 +525,93 @@ func (SDKExporterLogInflight) AttrServerPort(val int) attribute.KeyValue { return attribute.Int("server.port", val) } +// SDKExporterLogInflightObservable is an instrument used to record metric values +// conforming to the "otel.sdk.exporter.log.inflight" semantic conventions. It +// represents the number of log records which were passed to the exporter, but +// that have not been exported yet (neither successful, nor failed). +type SDKExporterLogInflightObservable struct { + metric.Int64ObservableUpDownCounter +} + +var newSDKExporterLogInflightObservableOpts = []metric.Int64ObservableUpDownCounterOption{ + metric.WithDescription("The number of log records which were passed to the exporter, but that have not been exported yet (neither successful, nor failed)."), + metric.WithUnit("{log_record}"), +} + +// NewSDKExporterLogInflightObservable returns a new +// SDKExporterLogInflightObservable instrument. +func NewSDKExporterLogInflightObservable( + m metric.Meter, + opt ...metric.Int64ObservableUpDownCounterOption, +) (SDKExporterLogInflightObservable, error) { + // Check if the meter is nil. + if m == nil { + return SDKExporterLogInflightObservable{noop.Int64ObservableUpDownCounter{}}, nil + } + + if len(opt) == 0 { + opt = newSDKExporterLogInflightObservableOpts + } else { + opt = append(opt, newSDKExporterLogInflightObservableOpts...) + } + + i, err := m.Int64ObservableUpDownCounter( + "otel.sdk.exporter.log.inflight", + opt..., + ) + if err != nil { + return SDKExporterLogInflightObservable{noop.Int64ObservableUpDownCounter{}}, err + } + return SDKExporterLogInflightObservable{i}, nil +} + +// Inst returns the underlying metric instrument. +func (m SDKExporterLogInflightObservable) Inst() metric.Int64ObservableUpDownCounter { + return m.Int64ObservableUpDownCounter +} + +// Name returns the semantic convention name of the instrument. +func (SDKExporterLogInflightObservable) Name() string { + return "otel.sdk.exporter.log.inflight" +} + +// Unit returns the semantic convention unit of the instrument +func (SDKExporterLogInflightObservable) Unit() string { + return "{log_record}" +} + +// Description returns the semantic convention description of the instrument +func (SDKExporterLogInflightObservable) Description() string { + return "The number of log records which were passed to the exporter, but that have not been exported yet (neither successful, nor failed)." +} + +// AttrComponentName returns an optional attribute for the "otel.component.name" +// semantic convention. It represents a name uniquely identifying the instance of +// the OpenTelemetry component within its containing SDK instance. +func (SDKExporterLogInflightObservable) AttrComponentName(val string) attribute.KeyValue { + return attribute.String("otel.component.name", val) +} + +// AttrComponentType returns an optional attribute for the "otel.component.type" +// semantic convention. It represents a name identifying the type of the +// OpenTelemetry component. +func (SDKExporterLogInflightObservable) AttrComponentType(val ComponentTypeAttr) attribute.KeyValue { + return attribute.String("otel.component.type", string(val)) +} + +// AttrServerAddress returns an optional attribute for the "server.address" +// semantic convention. It represents the server domain name if available without +// reverse DNS lookup; otherwise, IP address or Unix domain socket name. +func (SDKExporterLogInflightObservable) AttrServerAddress(val string) attribute.KeyValue { + return attribute.String("server.address", val) +} + +// AttrServerPort returns an optional attribute for the "server.port" semantic +// convention. It represents the server port number. +func (SDKExporterLogInflightObservable) AttrServerPort(val int) attribute.KeyValue { + return attribute.Int("server.port", val) +} + // SDKExporterMetricDataPointExported is an instrument used to record metric // values conforming to the "otel.sdk.exporter.metric_data_point.exported" // semantic conventions. It represents the number of metric data points for which @@ -491,6 +688,9 @@ func (m SDKExporterMetricDataPointExported) Add( incr int64, attrs ...attribute.KeyValue, ) { + if !m.Int64Counter.Enabled(ctx) { + return + } if len(attrs) == 0 { m.Int64Counter.Add(ctx, incr) return @@ -498,6 +698,7 @@ func (m SDKExporterMetricDataPointExported) Add( o := addOptPool.Get().(*[]metric.AddOption) defer func() { + clear(*o) *o = (*o)[:0] addOptPool.Put(o) }() @@ -522,6 +723,9 @@ func (m SDKExporterMetricDataPointExported) Add( // If no rejection reason is available, `rejected` SHOULD be used as value for // `error.type`. func (m SDKExporterMetricDataPointExported) AddSet(ctx context.Context, incr int64, set attribute.Set) { + if !m.Int64Counter.Enabled(ctx) { + return + } if set.Len() == 0 { m.Int64Counter.Add(ctx, incr) return @@ -529,6 +733,7 @@ func (m SDKExporterMetricDataPointExported) AddSet(ctx context.Context, incr int o := addOptPool.Get().(*[]metric.AddOption) defer func() { + clear(*o) *o = (*o)[:0] addOptPool.Put(o) }() @@ -571,6 +776,100 @@ func (SDKExporterMetricDataPointExported) AttrServerPort(val int) attribute.KeyV return attribute.Int("server.port", val) } +// SDKExporterMetricDataPointExportedObservable is an instrument used to record +// metric values conforming to the "otel.sdk.exporter.metric_data_point.exported" +// semantic conventions. It represents the number of metric data points for which +// the export has finished, either successful or failed. +type SDKExporterMetricDataPointExportedObservable struct { + metric.Int64ObservableCounter +} + +var newSDKExporterMetricDataPointExportedObservableOpts = []metric.Int64ObservableCounterOption{ + metric.WithDescription("The number of metric data points for which the export has finished, either successful or failed."), + metric.WithUnit("{data_point}"), +} + +// NewSDKExporterMetricDataPointExportedObservable returns a new +// SDKExporterMetricDataPointExportedObservable instrument. +func NewSDKExporterMetricDataPointExportedObservable( + m metric.Meter, + opt ...metric.Int64ObservableCounterOption, +) (SDKExporterMetricDataPointExportedObservable, error) { + // Check if the meter is nil. + if m == nil { + return SDKExporterMetricDataPointExportedObservable{noop.Int64ObservableCounter{}}, nil + } + + if len(opt) == 0 { + opt = newSDKExporterMetricDataPointExportedObservableOpts + } else { + opt = append(opt, newSDKExporterMetricDataPointExportedObservableOpts...) + } + + i, err := m.Int64ObservableCounter( + "otel.sdk.exporter.metric_data_point.exported", + opt..., + ) + if err != nil { + return SDKExporterMetricDataPointExportedObservable{noop.Int64ObservableCounter{}}, err + } + return SDKExporterMetricDataPointExportedObservable{i}, nil +} + +// Inst returns the underlying metric instrument. +func (m SDKExporterMetricDataPointExportedObservable) Inst() metric.Int64ObservableCounter { + return m.Int64ObservableCounter +} + +// Name returns the semantic convention name of the instrument. +func (SDKExporterMetricDataPointExportedObservable) Name() string { + return "otel.sdk.exporter.metric_data_point.exported" +} + +// Unit returns the semantic convention unit of the instrument +func (SDKExporterMetricDataPointExportedObservable) Unit() string { + return "{data_point}" +} + +// Description returns the semantic convention description of the instrument +func (SDKExporterMetricDataPointExportedObservable) Description() string { + return "The number of metric data points for which the export has finished, either successful or failed." +} + +// AttrErrorType returns an optional attribute for the "error.type" semantic +// convention. It represents the describes a class of error the operation ended +// with. +func (SDKExporterMetricDataPointExportedObservable) AttrErrorType(val ErrorTypeAttr) attribute.KeyValue { + return attribute.String("error.type", string(val)) +} + +// AttrComponentName returns an optional attribute for the "otel.component.name" +// semantic convention. It represents a name uniquely identifying the instance of +// the OpenTelemetry component within its containing SDK instance. +func (SDKExporterMetricDataPointExportedObservable) AttrComponentName(val string) attribute.KeyValue { + return attribute.String("otel.component.name", val) +} + +// AttrComponentType returns an optional attribute for the "otel.component.type" +// semantic convention. It represents a name identifying the type of the +// OpenTelemetry component. +func (SDKExporterMetricDataPointExportedObservable) AttrComponentType(val ComponentTypeAttr) attribute.KeyValue { + return attribute.String("otel.component.type", string(val)) +} + +// AttrServerAddress returns an optional attribute for the "server.address" +// semantic convention. It represents the server domain name if available without +// reverse DNS lookup; otherwise, IP address or Unix domain socket name. +func (SDKExporterMetricDataPointExportedObservable) AttrServerAddress(val string) attribute.KeyValue { + return attribute.String("server.address", val) +} + +// AttrServerPort returns an optional attribute for the "server.port" semantic +// convention. It represents the server port number. +func (SDKExporterMetricDataPointExportedObservable) AttrServerPort(val int) attribute.KeyValue { + return attribute.Int("server.port", val) +} + // SDKExporterMetricDataPointInflight is an instrument used to record metric // values conforming to the "otel.sdk.exporter.metric_data_point.inflight" // semantic conventions. It represents the number of metric data points which @@ -643,6 +942,9 @@ func (m SDKExporterMetricDataPointInflight) Add( incr int64, attrs ...attribute.KeyValue, ) { + if !m.Int64UpDownCounter.Enabled(ctx) { + return + } if len(attrs) == 0 { m.Int64UpDownCounter.Add(ctx, incr) return @@ -650,6 +952,7 @@ func (m SDKExporterMetricDataPointInflight) Add( o := addOptPool.Get().(*[]metric.AddOption) defer func() { + clear(*o) *o = (*o)[:0] addOptPool.Put(o) }() @@ -669,6 +972,9 @@ func (m SDKExporterMetricDataPointInflight) Add( // For successful exports, `error.type` MUST NOT be set. For failed exports, // `error.type` MUST contain the failure cause. func (m SDKExporterMetricDataPointInflight) AddSet(ctx context.Context, incr int64, set attribute.Set) { + if !m.Int64UpDownCounter.Enabled(ctx) { + return + } if set.Len() == 0 { m.Int64UpDownCounter.Add(ctx, incr) return @@ -676,6 +982,7 @@ func (m SDKExporterMetricDataPointInflight) AddSet(ctx context.Context, incr int o := addOptPool.Get().(*[]metric.AddOption) defer func() { + clear(*o) *o = (*o)[:0] addOptPool.Put(o) }() @@ -711,6 +1018,94 @@ func (SDKExporterMetricDataPointInflight) AttrServerPort(val int) attribute.KeyV return attribute.Int("server.port", val) } +// SDKExporterMetricDataPointInflightObservable is an instrument used to record +// metric values conforming to the "otel.sdk.exporter.metric_data_point.inflight" +// semantic conventions. It represents the number of metric data points which +// were passed to the exporter, but that have not been exported yet (neither +// successful, nor failed). +type SDKExporterMetricDataPointInflightObservable struct { + metric.Int64ObservableUpDownCounter +} + +var newSDKExporterMetricDataPointInflightObservableOpts = []metric.Int64ObservableUpDownCounterOption{ + metric.WithDescription("The number of metric data points which were passed to the exporter, but that have not been exported yet (neither successful, nor failed)."), + metric.WithUnit("{data_point}"), +} + +// NewSDKExporterMetricDataPointInflightObservable returns a new +// SDKExporterMetricDataPointInflightObservable instrument. +func NewSDKExporterMetricDataPointInflightObservable( + m metric.Meter, + opt ...metric.Int64ObservableUpDownCounterOption, +) (SDKExporterMetricDataPointInflightObservable, error) { + // Check if the meter is nil. + if m == nil { + return SDKExporterMetricDataPointInflightObservable{noop.Int64ObservableUpDownCounter{}}, nil + } + + if len(opt) == 0 { + opt = newSDKExporterMetricDataPointInflightObservableOpts + } else { + opt = append(opt, newSDKExporterMetricDataPointInflightObservableOpts...) + } + + i, err := m.Int64ObservableUpDownCounter( + "otel.sdk.exporter.metric_data_point.inflight", + opt..., + ) + if err != nil { + return SDKExporterMetricDataPointInflightObservable{noop.Int64ObservableUpDownCounter{}}, err + } + return SDKExporterMetricDataPointInflightObservable{i}, nil +} + +// Inst returns the underlying metric instrument. +func (m SDKExporterMetricDataPointInflightObservable) Inst() metric.Int64ObservableUpDownCounter { + return m.Int64ObservableUpDownCounter +} + +// Name returns the semantic convention name of the instrument. +func (SDKExporterMetricDataPointInflightObservable) Name() string { + return "otel.sdk.exporter.metric_data_point.inflight" +} + +// Unit returns the semantic convention unit of the instrument +func (SDKExporterMetricDataPointInflightObservable) Unit() string { + return "{data_point}" +} + +// Description returns the semantic convention description of the instrument +func (SDKExporterMetricDataPointInflightObservable) Description() string { + return "The number of metric data points which were passed to the exporter, but that have not been exported yet (neither successful, nor failed)." +} + +// AttrComponentName returns an optional attribute for the "otel.component.name" +// semantic convention. It represents a name uniquely identifying the instance of +// the OpenTelemetry component within its containing SDK instance. +func (SDKExporterMetricDataPointInflightObservable) AttrComponentName(val string) attribute.KeyValue { + return attribute.String("otel.component.name", val) +} + +// AttrComponentType returns an optional attribute for the "otel.component.type" +// semantic convention. It represents a name identifying the type of the +// OpenTelemetry component. +func (SDKExporterMetricDataPointInflightObservable) AttrComponentType(val ComponentTypeAttr) attribute.KeyValue { + return attribute.String("otel.component.type", string(val)) +} + +// AttrServerAddress returns an optional attribute for the "server.address" +// semantic convention. It represents the server domain name if available without +// reverse DNS lookup; otherwise, IP address or Unix domain socket name. +func (SDKExporterMetricDataPointInflightObservable) AttrServerAddress(val string) attribute.KeyValue { + return attribute.String("server.address", val) +} + +// AttrServerPort returns an optional attribute for the "server.port" semantic +// convention. It represents the server port number. +func (SDKExporterMetricDataPointInflightObservable) AttrServerPort(val int) attribute.KeyValue { + return attribute.Int("server.port", val) +} + // SDKExporterOperationDuration is an instrument used to record metric values // conforming to the "otel.sdk.exporter.operation.duration" semantic conventions. // It represents the duration of exporting a batch of telemetry records. @@ -788,6 +1183,9 @@ func (m SDKExporterOperationDuration) Record( val float64, attrs ...attribute.KeyValue, ) { + if !m.Float64Histogram.Enabled(ctx) { + return + } if len(attrs) == 0 { m.Float64Histogram.Record(ctx, val) return @@ -795,6 +1193,7 @@ func (m SDKExporterOperationDuration) Record( o := recOptPool.Get().(*[]metric.RecordOption) defer func() { + clear(*o) *o = (*o)[:0] recOptPool.Put(o) }() @@ -821,6 +1220,9 @@ func (m SDKExporterOperationDuration) Record( // [http]: https://github.com/open-telemetry/opentelemetry-proto/blob/v1.5.0/docs/specification.md#full-success-1 // [grpc]: https://github.com/open-telemetry/opentelemetry-proto/blob/v1.5.0/docs/specification.md#full-success func (m SDKExporterOperationDuration) RecordSet(ctx context.Context, val float64, set attribute.Set) { + if !m.Float64Histogram.Enabled(ctx) { + return + } if set.Len() == 0 { m.Float64Histogram.Record(ctx, val) return @@ -828,6 +1230,7 @@ func (m SDKExporterOperationDuration) RecordSet(ctx context.Context, val float64 o := recOptPool.Get().(*[]metric.RecordOption) defer func() { + clear(*o) *o = (*o)[:0] recOptPool.Put(o) }() @@ -959,6 +1362,9 @@ func (m SDKExporterSpanExported) Add( incr int64, attrs ...attribute.KeyValue, ) { + if !m.Int64Counter.Enabled(ctx) { + return + } if len(attrs) == 0 { m.Int64Counter.Add(ctx, incr) return @@ -966,6 +1372,7 @@ func (m SDKExporterSpanExported) Add( o := addOptPool.Get().(*[]metric.AddOption) defer func() { + clear(*o) *o = (*o)[:0] addOptPool.Put(o) }() @@ -990,6 +1397,9 @@ func (m SDKExporterSpanExported) Add( // If no rejection reason is available, `rejected` SHOULD be used as value for // `error.type`. func (m SDKExporterSpanExported) AddSet(ctx context.Context, incr int64, set attribute.Set) { + if !m.Int64Counter.Enabled(ctx) { + return + } if set.Len() == 0 { m.Int64Counter.Add(ctx, incr) return @@ -997,6 +1407,7 @@ func (m SDKExporterSpanExported) AddSet(ctx context.Context, incr int64, set att o := addOptPool.Get().(*[]metric.AddOption) defer func() { + clear(*o) *o = (*o)[:0] addOptPool.Put(o) }() @@ -1039,6 +1450,100 @@ func (SDKExporterSpanExported) AttrServerPort(val int) attribute.KeyValue { return attribute.Int("server.port", val) } +// SDKExporterSpanExportedObservable is an instrument used to record metric +// values conforming to the "otel.sdk.exporter.span.exported" semantic +// conventions. It represents the number of spans for which the export has +// finished, either successful or failed. +type SDKExporterSpanExportedObservable struct { + metric.Int64ObservableCounter +} + +var newSDKExporterSpanExportedObservableOpts = []metric.Int64ObservableCounterOption{ + metric.WithDescription("The number of spans for which the export has finished, either successful or failed."), + metric.WithUnit("{span}"), +} + +// NewSDKExporterSpanExportedObservable returns a new +// SDKExporterSpanExportedObservable instrument. +func NewSDKExporterSpanExportedObservable( + m metric.Meter, + opt ...metric.Int64ObservableCounterOption, +) (SDKExporterSpanExportedObservable, error) { + // Check if the meter is nil. + if m == nil { + return SDKExporterSpanExportedObservable{noop.Int64ObservableCounter{}}, nil + } + + if len(opt) == 0 { + opt = newSDKExporterSpanExportedObservableOpts + } else { + opt = append(opt, newSDKExporterSpanExportedObservableOpts...) + } + + i, err := m.Int64ObservableCounter( + "otel.sdk.exporter.span.exported", + opt..., + ) + if err != nil { + return SDKExporterSpanExportedObservable{noop.Int64ObservableCounter{}}, err + } + return SDKExporterSpanExportedObservable{i}, nil +} + +// Inst returns the underlying metric instrument. +func (m SDKExporterSpanExportedObservable) Inst() metric.Int64ObservableCounter { + return m.Int64ObservableCounter +} + +// Name returns the semantic convention name of the instrument. +func (SDKExporterSpanExportedObservable) Name() string { + return "otel.sdk.exporter.span.exported" +} + +// Unit returns the semantic convention unit of the instrument +func (SDKExporterSpanExportedObservable) Unit() string { + return "{span}" +} + +// Description returns the semantic convention description of the instrument +func (SDKExporterSpanExportedObservable) Description() string { + return "The number of spans for which the export has finished, either successful or failed." +} + +// AttrErrorType returns an optional attribute for the "error.type" semantic +// convention. It represents the describes a class of error the operation ended +// with. +func (SDKExporterSpanExportedObservable) AttrErrorType(val ErrorTypeAttr) attribute.KeyValue { + return attribute.String("error.type", string(val)) +} + +// AttrComponentName returns an optional attribute for the "otel.component.name" +// semantic convention. It represents a name uniquely identifying the instance of +// the OpenTelemetry component within its containing SDK instance. +func (SDKExporterSpanExportedObservable) AttrComponentName(val string) attribute.KeyValue { + return attribute.String("otel.component.name", val) +} + +// AttrComponentType returns an optional attribute for the "otel.component.type" +// semantic convention. It represents a name identifying the type of the +// OpenTelemetry component. +func (SDKExporterSpanExportedObservable) AttrComponentType(val ComponentTypeAttr) attribute.KeyValue { + return attribute.String("otel.component.type", string(val)) +} + +// AttrServerAddress returns an optional attribute for the "server.address" +// semantic convention. It represents the server domain name if available without +// reverse DNS lookup; otherwise, IP address or Unix domain socket name. +func (SDKExporterSpanExportedObservable) AttrServerAddress(val string) attribute.KeyValue { + return attribute.String("server.address", val) +} + +// AttrServerPort returns an optional attribute for the "server.port" semantic +// convention. It represents the server port number. +func (SDKExporterSpanExportedObservable) AttrServerPort(val int) attribute.KeyValue { + return attribute.Int("server.port", val) +} + // SDKExporterSpanInflight is an instrument used to record metric values // conforming to the "otel.sdk.exporter.span.inflight" semantic conventions. It // represents the number of spans which were passed to the exporter, but that @@ -1109,6 +1614,9 @@ func (m SDKExporterSpanInflight) Add( incr int64, attrs ...attribute.KeyValue, ) { + if !m.Int64UpDownCounter.Enabled(ctx) { + return + } if len(attrs) == 0 { m.Int64UpDownCounter.Add(ctx, incr) return @@ -1116,6 +1624,7 @@ func (m SDKExporterSpanInflight) Add( o := addOptPool.Get().(*[]metric.AddOption) defer func() { + clear(*o) *o = (*o)[:0] addOptPool.Put(o) }() @@ -1135,6 +1644,9 @@ func (m SDKExporterSpanInflight) Add( // For successful exports, `error.type` MUST NOT be set. For failed exports, // `error.type` MUST contain the failure cause. func (m SDKExporterSpanInflight) AddSet(ctx context.Context, incr int64, set attribute.Set) { + if !m.Int64UpDownCounter.Enabled(ctx) { + return + } if set.Len() == 0 { m.Int64UpDownCounter.Add(ctx, incr) return @@ -1142,6 +1654,7 @@ func (m SDKExporterSpanInflight) AddSet(ctx context.Context, incr int64, set att o := addOptPool.Get().(*[]metric.AddOption) defer func() { + clear(*o) *o = (*o)[:0] addOptPool.Put(o) }() @@ -1177,6 +1690,94 @@ func (SDKExporterSpanInflight) AttrServerPort(val int) attribute.KeyValue { return attribute.Int("server.port", val) } +// SDKExporterSpanInflightObservable is an instrument used to record metric +// values conforming to the "otel.sdk.exporter.span.inflight" semantic +// conventions. It represents the number of spans which were passed to the +// exporter, but that have not been exported yet (neither successful, nor +// failed). +type SDKExporterSpanInflightObservable struct { + metric.Int64ObservableUpDownCounter +} + +var newSDKExporterSpanInflightObservableOpts = []metric.Int64ObservableUpDownCounterOption{ + metric.WithDescription("The number of spans which were passed to the exporter, but that have not been exported yet (neither successful, nor failed)."), + metric.WithUnit("{span}"), +} + +// NewSDKExporterSpanInflightObservable returns a new +// SDKExporterSpanInflightObservable instrument. +func NewSDKExporterSpanInflightObservable( + m metric.Meter, + opt ...metric.Int64ObservableUpDownCounterOption, +) (SDKExporterSpanInflightObservable, error) { + // Check if the meter is nil. + if m == nil { + return SDKExporterSpanInflightObservable{noop.Int64ObservableUpDownCounter{}}, nil + } + + if len(opt) == 0 { + opt = newSDKExporterSpanInflightObservableOpts + } else { + opt = append(opt, newSDKExporterSpanInflightObservableOpts...) + } + + i, err := m.Int64ObservableUpDownCounter( + "otel.sdk.exporter.span.inflight", + opt..., + ) + if err != nil { + return SDKExporterSpanInflightObservable{noop.Int64ObservableUpDownCounter{}}, err + } + return SDKExporterSpanInflightObservable{i}, nil +} + +// Inst returns the underlying metric instrument. +func (m SDKExporterSpanInflightObservable) Inst() metric.Int64ObservableUpDownCounter { + return m.Int64ObservableUpDownCounter +} + +// Name returns the semantic convention name of the instrument. +func (SDKExporterSpanInflightObservable) Name() string { + return "otel.sdk.exporter.span.inflight" +} + +// Unit returns the semantic convention unit of the instrument +func (SDKExporterSpanInflightObservable) Unit() string { + return "{span}" +} + +// Description returns the semantic convention description of the instrument +func (SDKExporterSpanInflightObservable) Description() string { + return "The number of spans which were passed to the exporter, but that have not been exported yet (neither successful, nor failed)." +} + +// AttrComponentName returns an optional attribute for the "otel.component.name" +// semantic convention. It represents a name uniquely identifying the instance of +// the OpenTelemetry component within its containing SDK instance. +func (SDKExporterSpanInflightObservable) AttrComponentName(val string) attribute.KeyValue { + return attribute.String("otel.component.name", val) +} + +// AttrComponentType returns an optional attribute for the "otel.component.type" +// semantic convention. It represents a name identifying the type of the +// OpenTelemetry component. +func (SDKExporterSpanInflightObservable) AttrComponentType(val ComponentTypeAttr) attribute.KeyValue { + return attribute.String("otel.component.type", string(val)) +} + +// AttrServerAddress returns an optional attribute for the "server.address" +// semantic convention. It represents the server domain name if available without +// reverse DNS lookup; otherwise, IP address or Unix domain socket name. +func (SDKExporterSpanInflightObservable) AttrServerAddress(val string) attribute.KeyValue { + return attribute.String("server.address", val) +} + +// AttrServerPort returns an optional attribute for the "server.port" semantic +// convention. It represents the server port number. +func (SDKExporterSpanInflightObservable) AttrServerPort(val int) attribute.KeyValue { + return attribute.Int("server.port", val) +} + // SDKLogCreated is an instrument used to record metric values conforming to the // "otel.sdk.log.created" semantic conventions. It represents the number of logs // submitted to enabled SDK Loggers. @@ -1237,6 +1838,9 @@ func (SDKLogCreated) Description() string { // Add adds incr to the existing count for attrs. func (m SDKLogCreated) Add(ctx context.Context, incr int64, attrs ...attribute.KeyValue) { + if !m.Int64Counter.Enabled(ctx) { + return + } if len(attrs) == 0 { m.Int64Counter.Add(ctx, incr) return @@ -1244,29 +1848,92 @@ func (m SDKLogCreated) Add(ctx context.Context, incr int64, attrs ...attribute.K o := addOptPool.Get().(*[]metric.AddOption) defer func() { + clear(*o) *o = (*o)[:0] addOptPool.Put(o) }() - *o = append(*o, metric.WithAttributes(attrs...)) - m.Int64Counter.Add(ctx, incr, *o...) + *o = append(*o, metric.WithAttributes(attrs...)) + m.Int64Counter.Add(ctx, incr, *o...) +} + +// AddSet adds incr to the existing count for set. +func (m SDKLogCreated) AddSet(ctx context.Context, incr int64, set attribute.Set) { + if !m.Int64Counter.Enabled(ctx) { + return + } + if set.Len() == 0 { + m.Int64Counter.Add(ctx, incr) + return + } + + o := addOptPool.Get().(*[]metric.AddOption) + defer func() { + clear(*o) + *o = (*o)[:0] + addOptPool.Put(o) + }() + + *o = append(*o, metric.WithAttributeSet(set)) + m.Int64Counter.Add(ctx, incr, *o...) +} + +// SDKLogCreatedObservable is an instrument used to record metric values +// conforming to the "otel.sdk.log.created" semantic conventions. It represents +// the number of logs submitted to enabled SDK Loggers. +type SDKLogCreatedObservable struct { + metric.Int64ObservableCounter +} + +var newSDKLogCreatedObservableOpts = []metric.Int64ObservableCounterOption{ + metric.WithDescription("The number of logs submitted to enabled SDK Loggers."), + metric.WithUnit("{log_record}"), +} + +// NewSDKLogCreatedObservable returns a new SDKLogCreatedObservable instrument. +func NewSDKLogCreatedObservable( + m metric.Meter, + opt ...metric.Int64ObservableCounterOption, +) (SDKLogCreatedObservable, error) { + // Check if the meter is nil. + if m == nil { + return SDKLogCreatedObservable{noop.Int64ObservableCounter{}}, nil + } + + if len(opt) == 0 { + opt = newSDKLogCreatedObservableOpts + } else { + opt = append(opt, newSDKLogCreatedObservableOpts...) + } + + i, err := m.Int64ObservableCounter( + "otel.sdk.log.created", + opt..., + ) + if err != nil { + return SDKLogCreatedObservable{noop.Int64ObservableCounter{}}, err + } + return SDKLogCreatedObservable{i}, nil +} + +// Inst returns the underlying metric instrument. +func (m SDKLogCreatedObservable) Inst() metric.Int64ObservableCounter { + return m.Int64ObservableCounter } -// AddSet adds incr to the existing count for set. -func (m SDKLogCreated) AddSet(ctx context.Context, incr int64, set attribute.Set) { - if set.Len() == 0 { - m.Int64Counter.Add(ctx, incr) - return - } +// Name returns the semantic convention name of the instrument. +func (SDKLogCreatedObservable) Name() string { + return "otel.sdk.log.created" +} - o := addOptPool.Get().(*[]metric.AddOption) - defer func() { - *o = (*o)[:0] - addOptPool.Put(o) - }() +// Unit returns the semantic convention unit of the instrument +func (SDKLogCreatedObservable) Unit() string { + return "{log_record}" +} - *o = append(*o, metric.WithAttributeSet(set)) - m.Int64Counter.Add(ctx, incr, *o...) +// Description returns the semantic convention description of the instrument +func (SDKLogCreatedObservable) Description() string { + return "The number of logs submitted to enabled SDK Loggers." } // SDKMetricReaderCollectionDuration is an instrument used to record metric @@ -1343,6 +2010,9 @@ func (m SDKMetricReaderCollectionDuration) Record( val float64, attrs ...attribute.KeyValue, ) { + if !m.Float64Histogram.Enabled(ctx) { + return + } if len(attrs) == 0 { m.Float64Histogram.Record(ctx, val) return @@ -1350,6 +2020,7 @@ func (m SDKMetricReaderCollectionDuration) Record( o := recOptPool.Get().(*[]metric.RecordOption) defer func() { + clear(*o) *o = (*o)[:0] recOptPool.Put(o) }() @@ -1372,6 +2043,9 @@ func (m SDKMetricReaderCollectionDuration) Record( // while others fail. In that case `error.type` SHOULD be set to any of the // failure causes. func (m SDKMetricReaderCollectionDuration) RecordSet(ctx context.Context, val float64, set attribute.Set) { + if !m.Float64Histogram.Enabled(ctx) { + return + } if set.Len() == 0 { m.Float64Histogram.Record(ctx, val) return @@ -1379,6 +2053,7 @@ func (m SDKMetricReaderCollectionDuration) RecordSet(ctx context.Context, val fl o := recOptPool.Get().(*[]metric.RecordOption) defer func() { + clear(*o) *o = (*o)[:0] recOptPool.Put(o) }() @@ -1481,6 +2156,9 @@ func (m SDKProcessorLogProcessed) Add( incr int64, attrs ...attribute.KeyValue, ) { + if !m.Int64Counter.Enabled(ctx) { + return + } if len(attrs) == 0 { m.Int64Counter.Add(ctx, incr) return @@ -1488,6 +2166,7 @@ func (m SDKProcessorLogProcessed) Add( o := addOptPool.Get().(*[]metric.AddOption) defer func() { + clear(*o) *o = (*o)[:0] addOptPool.Put(o) }() @@ -1510,6 +2189,9 @@ func (m SDKProcessorLogProcessed) Add( // considered to be processed already when it has been submitted to the exporter, // not when the corresponding export call has finished. func (m SDKProcessorLogProcessed) AddSet(ctx context.Context, incr int64, set attribute.Set) { + if !m.Int64Counter.Enabled(ctx) { + return + } if set.Len() == 0 { m.Int64Counter.Add(ctx, incr) return @@ -1517,6 +2199,7 @@ func (m SDKProcessorLogProcessed) AddSet(ctx context.Context, incr int64, set at o := addOptPool.Get().(*[]metric.AddOption) defer func() { + clear(*o) *o = (*o)[:0] addOptPool.Put(o) }() @@ -1547,6 +2230,88 @@ func (SDKProcessorLogProcessed) AttrComponentType(val ComponentTypeAttr) attribu return attribute.String("otel.component.type", string(val)) } +// SDKProcessorLogProcessedObservable is an instrument used to record metric +// values conforming to the "otel.sdk.processor.log.processed" semantic +// conventions. It represents the number of log records for which the processing +// has finished, either successful or failed. +type SDKProcessorLogProcessedObservable struct { + metric.Int64ObservableCounter +} + +var newSDKProcessorLogProcessedObservableOpts = []metric.Int64ObservableCounterOption{ + metric.WithDescription("The number of log records for which the processing has finished, either successful or failed."), + metric.WithUnit("{log_record}"), +} + +// NewSDKProcessorLogProcessedObservable returns a new +// SDKProcessorLogProcessedObservable instrument. +func NewSDKProcessorLogProcessedObservable( + m metric.Meter, + opt ...metric.Int64ObservableCounterOption, +) (SDKProcessorLogProcessedObservable, error) { + // Check if the meter is nil. + if m == nil { + return SDKProcessorLogProcessedObservable{noop.Int64ObservableCounter{}}, nil + } + + if len(opt) == 0 { + opt = newSDKProcessorLogProcessedObservableOpts + } else { + opt = append(opt, newSDKProcessorLogProcessedObservableOpts...) + } + + i, err := m.Int64ObservableCounter( + "otel.sdk.processor.log.processed", + opt..., + ) + if err != nil { + return SDKProcessorLogProcessedObservable{noop.Int64ObservableCounter{}}, err + } + return SDKProcessorLogProcessedObservable{i}, nil +} + +// Inst returns the underlying metric instrument. +func (m SDKProcessorLogProcessedObservable) Inst() metric.Int64ObservableCounter { + return m.Int64ObservableCounter +} + +// Name returns the semantic convention name of the instrument. +func (SDKProcessorLogProcessedObservable) Name() string { + return "otel.sdk.processor.log.processed" +} + +// Unit returns the semantic convention unit of the instrument +func (SDKProcessorLogProcessedObservable) Unit() string { + return "{log_record}" +} + +// Description returns the semantic convention description of the instrument +func (SDKProcessorLogProcessedObservable) Description() string { + return "The number of log records for which the processing has finished, either successful or failed." +} + +// AttrErrorType returns an optional attribute for the "error.type" semantic +// convention. It represents a low-cardinality description of the failure reason. +// SDK Batching Log Record Processors MUST use `queue_full` for log records +// dropped due to a full queue. +func (SDKProcessorLogProcessedObservable) AttrErrorType(val ErrorTypeAttr) attribute.KeyValue { + return attribute.String("error.type", string(val)) +} + +// AttrComponentName returns an optional attribute for the "otel.component.name" +// semantic convention. It represents a name uniquely identifying the instance of +// the OpenTelemetry component within its containing SDK instance. +func (SDKProcessorLogProcessedObservable) AttrComponentName(val string) attribute.KeyValue { + return attribute.String("otel.component.name", val) +} + +// AttrComponentType returns an optional attribute for the "otel.component.type" +// semantic convention. It represents a name identifying the type of the +// OpenTelemetry component. +func (SDKProcessorLogProcessedObservable) AttrComponentType(val ComponentTypeAttr) attribute.KeyValue { + return attribute.String("otel.component.type", string(val)) +} + // SDKProcessorLogQueueCapacity is an instrument used to record metric values // conforming to the "otel.sdk.processor.log.queue.capacity" semantic // conventions. It represents the maximum number of log records the queue of a @@ -1768,6 +2533,9 @@ func (m SDKProcessorSpanProcessed) Add( incr int64, attrs ...attribute.KeyValue, ) { + if !m.Int64Counter.Enabled(ctx) { + return + } if len(attrs) == 0 { m.Int64Counter.Add(ctx, incr) return @@ -1775,6 +2543,7 @@ func (m SDKProcessorSpanProcessed) Add( o := addOptPool.Get().(*[]metric.AddOption) defer func() { + clear(*o) *o = (*o)[:0] addOptPool.Put(o) }() @@ -1797,6 +2566,9 @@ func (m SDKProcessorSpanProcessed) Add( // processed already when it has been submitted to the exporter, not when the // corresponding export call has finished. func (m SDKProcessorSpanProcessed) AddSet(ctx context.Context, incr int64, set attribute.Set) { + if !m.Int64Counter.Enabled(ctx) { + return + } if set.Len() == 0 { m.Int64Counter.Add(ctx, incr) return @@ -1804,6 +2576,7 @@ func (m SDKProcessorSpanProcessed) AddSet(ctx context.Context, incr int64, set a o := addOptPool.Get().(*[]metric.AddOption) defer func() { + clear(*o) *o = (*o)[:0] addOptPool.Put(o) }() @@ -1834,6 +2607,88 @@ func (SDKProcessorSpanProcessed) AttrComponentType(val ComponentTypeAttr) attrib return attribute.String("otel.component.type", string(val)) } +// SDKProcessorSpanProcessedObservable is an instrument used to record metric +// values conforming to the "otel.sdk.processor.span.processed" semantic +// conventions. It represents the number of spans for which the processing has +// finished, either successful or failed. +type SDKProcessorSpanProcessedObservable struct { + metric.Int64ObservableCounter +} + +var newSDKProcessorSpanProcessedObservableOpts = []metric.Int64ObservableCounterOption{ + metric.WithDescription("The number of spans for which the processing has finished, either successful or failed."), + metric.WithUnit("{span}"), +} + +// NewSDKProcessorSpanProcessedObservable returns a new +// SDKProcessorSpanProcessedObservable instrument. +func NewSDKProcessorSpanProcessedObservable( + m metric.Meter, + opt ...metric.Int64ObservableCounterOption, +) (SDKProcessorSpanProcessedObservable, error) { + // Check if the meter is nil. + if m == nil { + return SDKProcessorSpanProcessedObservable{noop.Int64ObservableCounter{}}, nil + } + + if len(opt) == 0 { + opt = newSDKProcessorSpanProcessedObservableOpts + } else { + opt = append(opt, newSDKProcessorSpanProcessedObservableOpts...) + } + + i, err := m.Int64ObservableCounter( + "otel.sdk.processor.span.processed", + opt..., + ) + if err != nil { + return SDKProcessorSpanProcessedObservable{noop.Int64ObservableCounter{}}, err + } + return SDKProcessorSpanProcessedObservable{i}, nil +} + +// Inst returns the underlying metric instrument. +func (m SDKProcessorSpanProcessedObservable) Inst() metric.Int64ObservableCounter { + return m.Int64ObservableCounter +} + +// Name returns the semantic convention name of the instrument. +func (SDKProcessorSpanProcessedObservable) Name() string { + return "otel.sdk.processor.span.processed" +} + +// Unit returns the semantic convention unit of the instrument +func (SDKProcessorSpanProcessedObservable) Unit() string { + return "{span}" +} + +// Description returns the semantic convention description of the instrument +func (SDKProcessorSpanProcessedObservable) Description() string { + return "The number of spans for which the processing has finished, either successful or failed." +} + +// AttrErrorType returns an optional attribute for the "error.type" semantic +// convention. It represents a low-cardinality description of the failure reason. +// SDK Batching Span Processors MUST use `queue_full` for spans dropped due to a +// full queue. +func (SDKProcessorSpanProcessedObservable) AttrErrorType(val ErrorTypeAttr) attribute.KeyValue { + return attribute.String("error.type", string(val)) +} + +// AttrComponentName returns an optional attribute for the "otel.component.name" +// semantic convention. It represents a name uniquely identifying the instance of +// the OpenTelemetry component within its containing SDK instance. +func (SDKProcessorSpanProcessedObservable) AttrComponentName(val string) attribute.KeyValue { + return attribute.String("otel.component.name", val) +} + +// AttrComponentType returns an optional attribute for the "otel.component.type" +// semantic convention. It represents a name identifying the type of the +// OpenTelemetry component. +func (SDKProcessorSpanProcessedObservable) AttrComponentType(val ComponentTypeAttr) attribute.KeyValue { + return attribute.String("otel.component.type", string(val)) +} + // SDKProcessorSpanQueueCapacity is an instrument used to record metric values // conforming to the "otel.sdk.processor.span.queue.capacity" semantic // conventions. It represents the maximum number of spans the queue of a given @@ -2049,6 +2904,9 @@ func (m SDKSpanLive) Add( incr int64, attrs ...attribute.KeyValue, ) { + if !m.Int64UpDownCounter.Enabled(ctx) { + return + } if len(attrs) == 0 { m.Int64UpDownCounter.Add(ctx, incr) return @@ -2056,6 +2914,7 @@ func (m SDKSpanLive) Add( o := addOptPool.Get().(*[]metric.AddOption) defer func() { + clear(*o) *o = (*o)[:0] addOptPool.Put(o) }() @@ -2072,6 +2931,9 @@ func (m SDKSpanLive) Add( // AddSet adds incr to the existing count for set. func (m SDKSpanLive) AddSet(ctx context.Context, incr int64, set attribute.Set) { + if !m.Int64UpDownCounter.Enabled(ctx) { + return + } if set.Len() == 0 { m.Int64UpDownCounter.Add(ctx, incr) return @@ -2079,6 +2941,7 @@ func (m SDKSpanLive) AddSet(ctx context.Context, incr int64, set attribute.Set) o := addOptPool.Get().(*[]metric.AddOption) defer func() { + clear(*o) *o = (*o)[:0] addOptPool.Put(o) }() @@ -2094,6 +2957,72 @@ func (SDKSpanLive) AttrSpanSamplingResult(val SpanSamplingResultAttr) attribute. return attribute.String("otel.span.sampling_result", string(val)) } +// SDKSpanLiveObservable is an instrument used to record metric values conforming +// to the "otel.sdk.span.live" semantic conventions. It represents the number of +// created spans with `recording=true` for which the end operation has not been +// called yet. +type SDKSpanLiveObservable struct { + metric.Int64ObservableUpDownCounter +} + +var newSDKSpanLiveObservableOpts = []metric.Int64ObservableUpDownCounterOption{ + metric.WithDescription("The number of created spans with `recording=true` for which the end operation has not been called yet."), + metric.WithUnit("{span}"), +} + +// NewSDKSpanLiveObservable returns a new SDKSpanLiveObservable instrument. +func NewSDKSpanLiveObservable( + m metric.Meter, + opt ...metric.Int64ObservableUpDownCounterOption, +) (SDKSpanLiveObservable, error) { + // Check if the meter is nil. + if m == nil { + return SDKSpanLiveObservable{noop.Int64ObservableUpDownCounter{}}, nil + } + + if len(opt) == 0 { + opt = newSDKSpanLiveObservableOpts + } else { + opt = append(opt, newSDKSpanLiveObservableOpts...) + } + + i, err := m.Int64ObservableUpDownCounter( + "otel.sdk.span.live", + opt..., + ) + if err != nil { + return SDKSpanLiveObservable{noop.Int64ObservableUpDownCounter{}}, err + } + return SDKSpanLiveObservable{i}, nil +} + +// Inst returns the underlying metric instrument. +func (m SDKSpanLiveObservable) Inst() metric.Int64ObservableUpDownCounter { + return m.Int64ObservableUpDownCounter +} + +// Name returns the semantic convention name of the instrument. +func (SDKSpanLiveObservable) Name() string { + return "otel.sdk.span.live" +} + +// Unit returns the semantic convention unit of the instrument +func (SDKSpanLiveObservable) Unit() string { + return "{span}" +} + +// Description returns the semantic convention description of the instrument +func (SDKSpanLiveObservable) Description() string { + return "The number of created spans with `recording=true` for which the end operation has not been called yet." +} + +// AttrSpanSamplingResult returns an optional attribute for the +// "otel.span.sampling_result" semantic convention. It represents the result +// value of the sampler for this span. +func (SDKSpanLiveObservable) AttrSpanSamplingResult(val SpanSamplingResultAttr) attribute.KeyValue { + return attribute.String("otel.span.sampling_result", string(val)) +} + // SDKSpanStarted is an instrument used to record metric values conforming to the // "otel.sdk.span.started" semantic conventions. It represents the number of // created spans. @@ -2163,6 +3092,9 @@ func (m SDKSpanStarted) Add( incr int64, attrs ...attribute.KeyValue, ) { + if !m.Int64Counter.Enabled(ctx) { + return + } if len(attrs) == 0 { m.Int64Counter.Add(ctx, incr) return @@ -2170,6 +3102,7 @@ func (m SDKSpanStarted) Add( o := addOptPool.Get().(*[]metric.AddOption) defer func() { + clear(*o) *o = (*o)[:0] addOptPool.Put(o) }() @@ -2189,6 +3122,9 @@ func (m SDKSpanStarted) Add( // Implementations MUST record this metric for all spans, even for non-recording // ones. func (m SDKSpanStarted) AddSet(ctx context.Context, incr int64, set attribute.Set) { + if !m.Int64Counter.Enabled(ctx) { + return + } if set.Len() == 0 { m.Int64Counter.Add(ctx, incr) return @@ -2196,6 +3132,7 @@ func (m SDKSpanStarted) AddSet(ctx context.Context, incr int64, set attribute.Se o := addOptPool.Get().(*[]metric.AddOption) defer func() { + clear(*o) *o = (*o)[:0] addOptPool.Put(o) }() @@ -2220,3 +3157,78 @@ func (SDKSpanStarted) AttrSpanParentOrigin(val SpanParentOriginAttr) attribute.K func (SDKSpanStarted) AttrSpanSamplingResult(val SpanSamplingResultAttr) attribute.KeyValue { return attribute.String("otel.span.sampling_result", string(val)) } + +// SDKSpanStartedObservable is an instrument used to record metric values +// conforming to the "otel.sdk.span.started" semantic conventions. It represents +// the number of created spans. +type SDKSpanStartedObservable struct { + metric.Int64ObservableCounter +} + +var newSDKSpanStartedObservableOpts = []metric.Int64ObservableCounterOption{ + metric.WithDescription("The number of created spans."), + metric.WithUnit("{span}"), +} + +// NewSDKSpanStartedObservable returns a new SDKSpanStartedObservable instrument. +func NewSDKSpanStartedObservable( + m metric.Meter, + opt ...metric.Int64ObservableCounterOption, +) (SDKSpanStartedObservable, error) { + // Check if the meter is nil. + if m == nil { + return SDKSpanStartedObservable{noop.Int64ObservableCounter{}}, nil + } + + if len(opt) == 0 { + opt = newSDKSpanStartedObservableOpts + } else { + opt = append(opt, newSDKSpanStartedObservableOpts...) + } + + i, err := m.Int64ObservableCounter( + "otel.sdk.span.started", + opt..., + ) + if err != nil { + return SDKSpanStartedObservable{noop.Int64ObservableCounter{}}, err + } + return SDKSpanStartedObservable{i}, nil +} + +// Inst returns the underlying metric instrument. +func (m SDKSpanStartedObservable) Inst() metric.Int64ObservableCounter { + return m.Int64ObservableCounter +} + +// Name returns the semantic convention name of the instrument. +func (SDKSpanStartedObservable) Name() string { + return "otel.sdk.span.started" +} + +// Unit returns the semantic convention unit of the instrument +func (SDKSpanStartedObservable) Unit() string { + return "{span}" +} + +// Description returns the semantic convention description of the instrument +func (SDKSpanStartedObservable) Description() string { + return "The number of created spans." +} + +// AttrSpanParentOrigin returns an optional attribute for the +// "otel.span.parent.origin" semantic convention. It represents the determines +// whether the span has a parent span, and if so, [whether it is a remote parent] +// . +// +// [whether it is a remote parent]: https://opentelemetry.io/docs/specs/otel/trace/api/#isremote +func (SDKSpanStartedObservable) AttrSpanParentOrigin(val SpanParentOriginAttr) attribute.KeyValue { + return attribute.String("otel.span.parent.origin", string(val)) +} + +// AttrSpanSamplingResult returns an optional attribute for the +// "otel.span.sampling_result" semantic convention. It represents the result +// value of the sampler for this span. +func (SDKSpanStartedObservable) AttrSpanSamplingResult(val SpanSamplingResultAttr) attribute.KeyValue { + return attribute.String("otel.span.sampling_result", string(val)) +} diff --git a/vendor/go.opentelemetry.io/otel/semconv/v1.40.0/schema.go b/vendor/go.opentelemetry.io/otel/semconv/v1.41.0/schema.go similarity index 73% rename from vendor/go.opentelemetry.io/otel/semconv/v1.40.0/schema.go rename to vendor/go.opentelemetry.io/otel/semconv/v1.41.0/schema.go index a07ffa3361..24948a48f8 100644 --- a/vendor/go.opentelemetry.io/otel/semconv/v1.40.0/schema.go +++ b/vendor/go.opentelemetry.io/otel/semconv/v1.41.0/schema.go @@ -1,9 +1,11 @@ +// Code generated from semantic convention specification. DO NOT EDIT. + // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -package semconv // import "go.opentelemetry.io/otel/semconv/v1.40.0" +package semconv // import "go.opentelemetry.io/otel/semconv/v1.41.0" // SchemaURL is the schema URL that matches the version of the semantic conventions // that this package defines. Semconv packages starting from v1.4.0 must declare // non-empty schema URL in the form https://opentelemetry.io/schemas/ -const SchemaURL = "https://opentelemetry.io/schemas/1.40.0" +const SchemaURL = "https://opentelemetry.io/schemas/1.41.0" diff --git a/vendor/go.opentelemetry.io/otel/trace/auto.go b/vendor/go.opentelemetry.io/otel/trace/auto.go index 9316fd0ac4..a75cf047d5 100644 --- a/vendor/go.opentelemetry.io/otel/trace/auto.go +++ b/vendor/go.opentelemetry.io/otel/trace/auto.go @@ -20,7 +20,7 @@ import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" - semconv "go.opentelemetry.io/otel/semconv/v1.40.0" + semconv "go.opentelemetry.io/otel/semconv/v1.41.0" "go.opentelemetry.io/otel/trace/embedded" "go.opentelemetry.io/otel/trace/internal/telemetry" ) @@ -314,6 +314,14 @@ func convAttrValue(value attribute.Value) telemetry.Value { case attribute.STRING: v := truncate(maxSpan.AttrValueLen, value.AsString()) return telemetry.StringValue(v) + case attribute.BYTESLICE: + // len(v.AsString()) is identical to len(v.AsByteSlice()) but + // avoids allocating the full slice before truncation. + s := value.AsString() + if maxSpan.AttrValueLen >= 0 && len(s) > maxSpan.AttrValueLen { + return telemetry.BytesValue([]byte(s[:maxSpan.AttrValueLen])) + } + return telemetry.BytesValue([]byte(s)) case attribute.BOOLSLICE: slice := value.AsBoolSlice() out := make([]telemetry.Value, 0, len(slice)) @@ -343,6 +351,13 @@ func convAttrValue(value attribute.Value) telemetry.Value { out = append(out, telemetry.StringValue(v)) } return telemetry.SliceValue(out...) + case attribute.SLICE: + slice := value.AsSlice() + out := make([]telemetry.Value, 0, len(slice)) + for _, v := range slice { + out = append(out, convAttrValue(v)) + } + return telemetry.SliceValue(out...) } return telemetry.Value{} } @@ -463,7 +478,8 @@ func (s *autoSpan) RecordError(err error, opts ...EventOption) { cfg := NewEventConfig(opts...) attrs := cfg.Attributes() - attrs = append(attrs, + attrs = append( + attrs, semconv.ExceptionType(typeStr(err)), semconv.ExceptionMessage(err.Error()), ) diff --git a/vendor/go.opentelemetry.io/otel/trace/config.go b/vendor/go.opentelemetry.io/otel/trace/config.go index d9ecef1cad..4cedba5ac7 100644 --- a/vendor/go.opentelemetry.io/otel/trace/config.go +++ b/vendor/go.opentelemetry.io/otel/trace/config.go @@ -34,10 +34,17 @@ func (t *TracerConfig) SchemaURL() string { return t.schemaURL } +type experimentalOption interface { + Experimental() +} + // NewTracerConfig applies all the options to a returned TracerConfig. func NewTracerConfig(options ...TracerOption) TracerConfig { var config TracerConfig for _, option := range options { + if _, ok := option.(experimentalOption); ok { + continue + } config = option.apply(config) } return config @@ -103,6 +110,9 @@ func (cfg *SpanConfig) SpanKind() SpanKind { func NewSpanStartConfig(options ...SpanStartOption) SpanConfig { var c SpanConfig for _, option := range options { + if _, ok := option.(experimentalOption); ok { + continue + } c = option.applySpanStart(c) } return c @@ -115,6 +125,9 @@ func NewSpanStartConfig(options ...SpanStartOption) SpanConfig { func NewSpanEndConfig(options ...SpanEndOption) SpanConfig { var c SpanConfig for _, option := range options { + if _, ok := option.(experimentalOption); ok { + continue + } c = option.applySpanEnd(c) } return c @@ -167,6 +180,9 @@ func (cfg *EventConfig) StackTrace() bool { func NewEventConfig(options ...EventOption) EventConfig { var c EventConfig for _, option := range options { + if _, ok := option.(experimentalOption); ok { + continue + } c = option.applyEvent(c) } if c.timestamp.IsZero() { diff --git a/vendor/go.opentelemetry.io/otel/trace/internal/telemetry/span.go b/vendor/go.opentelemetry.io/otel/trace/internal/telemetry/span.go index e7ca62c660..61c7819a23 100644 --- a/vendor/go.opentelemetry.io/otel/trace/internal/telemetry/span.go +++ b/vendor/go.opentelemetry.io/otel/trace/internal/telemetry/span.go @@ -314,9 +314,9 @@ type SpanEvent struct { } // MarshalJSON encodes e into OTLP formatted JSON. -func (e SpanEvent) MarshalJSON() ([]byte, error) { - t := e.Time.UnixNano() - if e.Time.IsZero() || t < 0 { +func (se SpanEvent) MarshalJSON() ([]byte, error) { + t := se.Time.UnixNano() + if se.Time.IsZero() || t < 0 { t = 0 } @@ -325,7 +325,7 @@ func (e SpanEvent) MarshalJSON() ([]byte, error) { Alias Time uint64 `json:"timeUnixNano,omitempty"` }{ - Alias: Alias(e), + Alias: Alias(se), Time: uint64(t), // nolint: gosec // >0 checked above }) } diff --git a/vendor/go.opentelemetry.io/otel/version.go b/vendor/go.opentelemetry.io/otel/version.go index 1db4f47e43..72746acfdb 100644 --- a/vendor/go.opentelemetry.io/otel/version.go +++ b/vendor/go.opentelemetry.io/otel/version.go @@ -5,5 +5,5 @@ package otel // import "go.opentelemetry.io/otel" // Version is the current release version of OpenTelemetry in use. func Version() string { - return "1.43.0" + return "1.44.0" } diff --git a/vendor/go.opentelemetry.io/otel/versions.yaml b/vendor/go.opentelemetry.io/otel/versions.yaml index bcc6ee78a4..d6dbf803ef 100644 --- a/vendor/go.opentelemetry.io/otel/versions.yaml +++ b/vendor/go.opentelemetry.io/otel/versions.yaml @@ -3,7 +3,7 @@ module-sets: stable-v1: - version: v1.43.0 + version: v1.44.0 modules: - go.opentelemetry.io/otel - go.opentelemetry.io/otel/bridge/opencensus @@ -22,11 +22,12 @@ module-sets: - go.opentelemetry.io/otel/sdk/metric - go.opentelemetry.io/otel/trace experimental-metrics: - version: v0.65.0 + version: v0.66.0 modules: - go.opentelemetry.io/otel/exporters/prometheus + - go.opentelemetry.io/otel/metric/x experimental-logs: - version: v0.19.0 + version: v0.20.0 modules: - go.opentelemetry.io/otel/log - go.opentelemetry.io/otel/log/logtest @@ -36,7 +37,7 @@ module-sets: - go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp - go.opentelemetry.io/otel/exporters/stdout/stdoutlog experimental-schema: - version: v0.0.16 + version: v0.0.17 modules: - go.opentelemetry.io/otel/schema excluded-modules: @@ -55,6 +56,9 @@ modules: go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc: version-refs: - ./internal/version.go + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp: + version-refs: + - ./internal/version.go go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc: version-refs: - ./internal/version.go diff --git a/vendor/golang.org/x/text/currency/common.go b/vendor/golang.org/x/text/currency/common.go new file mode 100644 index 0000000000..fef15be554 --- /dev/null +++ b/vendor/golang.org/x/text/currency/common.go @@ -0,0 +1,67 @@ +// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. + +package currency + +import ( + "time" + + "golang.org/x/text/language" +) + +// This file contains code common to gen.go and the package code. + +const ( + cashShift = 3 + roundMask = 0x7 + + nonTenderBit = 0x8000 +) + +// currencyInfo contains information about a currency. +// bits 0..2: index into roundings for standard rounding +// bits 3..5: index into roundings for cash rounding +type currencyInfo byte + +// roundingType defines the scale (number of fractional decimals) and increments +// in terms of units of size 10^-scale. For example, for scale == 2 and +// increment == 1, the currency is rounded to units of 0.01. +type roundingType struct { + scale, increment uint8 +} + +// roundings contains rounding data for currencies. This struct is +// created by hand as it is very unlikely to change much. +var roundings = [...]roundingType{ + {2, 1}, // default + {0, 1}, + {1, 1}, + {3, 1}, + {4, 1}, + {2, 5}, // cash rounding alternative + {2, 50}, +} + +// regionToCode returns a 16-bit region code. Only two-letter codes are +// supported. (Three-letter codes are not needed.) +func regionToCode(r language.Region) uint16 { + if s := r.String(); len(s) == 2 { + return uint16(s[0])<<8 | uint16(s[1]) + } + return 0 +} + +func toDate(t time.Time) uint32 { + y := t.Year() + if y == 1 { + return 0 + } + date := uint32(y) << 4 + date |= uint32(t.Month()) + date <<= 5 + date |= uint32(t.Day()) + return date +} + +func fromDate(date uint32) time.Time { + return time.Date(int(date>>9), time.Month((date>>5)&0xf), int(date&0x1f), 0, 0, 0, 0, time.UTC) +} diff --git a/vendor/golang.org/x/text/currency/currency.go b/vendor/golang.org/x/text/currency/currency.go new file mode 100644 index 0000000000..598ddeff42 --- /dev/null +++ b/vendor/golang.org/x/text/currency/currency.go @@ -0,0 +1,185 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:generate go run gen.go gen_common.go -output tables.go + +// Package currency contains currency-related functionality. +// +// NOTE: the formatting functionality is currently under development and may +// change without notice. +package currency // import "golang.org/x/text/currency" + +import ( + "errors" + "sort" + + "golang.org/x/text/internal/tag" + "golang.org/x/text/language" +) + +// TODO: +// - language-specific currency names. +// - currency formatting. +// - currency information per region +// - register currency code (there are no private use area) + +// TODO: remove Currency type from package language. + +// Kind determines the rounding and rendering properties of a currency value. +type Kind struct { + rounding rounding + // TODO: formatting type: standard, accounting. See CLDR. +} + +type rounding byte + +const ( + standard rounding = iota + cash +) + +var ( + // Standard defines standard rounding and formatting for currencies. + Standard Kind = Kind{rounding: standard} + + // Cash defines rounding and formatting standards for cash transactions. + Cash Kind = Kind{rounding: cash} + + // Accounting defines rounding and formatting standards for accounting. + Accounting Kind = Kind{rounding: standard} +) + +// Rounding reports the rounding characteristics for the given currency, where +// scale is the number of fractional decimals and increment is the number of +// units in terms of 10^(-scale) to which to round to. +func (k Kind) Rounding(cur Unit) (scale, increment int) { + info := currency.Elem(int(cur.index))[3] + switch k.rounding { + case standard: + info &= roundMask + case cash: + info >>= cashShift + } + return int(roundings[info].scale), int(roundings[info].increment) +} + +// Unit is an ISO 4217 currency designator. +type Unit struct { + index uint16 +} + +// String returns the ISO code of u. +func (u Unit) String() string { + if u.index == 0 { + return "XXX" + } + return currency.Elem(int(u.index))[:3] +} + +// Amount creates an Amount for the given currency unit and amount. +func (u Unit) Amount(amount interface{}) Amount { + // TODO: verify amount is a supported number type + return Amount{amount: amount, currency: u} +} + +var ( + errSyntax = errors.New("currency: tag is not well-formed") + errValue = errors.New("currency: tag is not a recognized currency") +) + +// ParseISO parses a 3-letter ISO 4217 currency code. It returns an error if s +// is not well-formed or not a recognized currency code. +func ParseISO(s string) (Unit, error) { + var buf [4]byte // Take one byte more to detect oversize keys. + key := buf[:copy(buf[:], s)] + if !tag.FixCase("XXX", key) { + return Unit{}, errSyntax + } + if i := currency.Index(key); i >= 0 { + if i == xxx { + return Unit{}, nil + } + return Unit{uint16(i)}, nil + } + return Unit{}, errValue +} + +// MustParseISO is like ParseISO, but panics if the given currency unit +// cannot be parsed. It simplifies safe initialization of Unit values. +func MustParseISO(s string) Unit { + c, err := ParseISO(s) + if err != nil { + panic(err) + } + return c +} + +// FromRegion reports the currency unit that is currently legal tender in the +// given region according to CLDR. It will return false if region currently does +// not have a legal tender. +func FromRegion(r language.Region) (currency Unit, ok bool) { + x := regionToCode(r) + i := sort.Search(len(regionToCurrency), func(i int) bool { + return regionToCurrency[i].region >= x + }) + if i < len(regionToCurrency) && regionToCurrency[i].region == x { + return Unit{regionToCurrency[i].code}, true + } + return Unit{}, false +} + +// FromTag reports the most likely currency for the given tag. It considers the +// currency defined in the -u extension and infers the region if necessary. +func FromTag(t language.Tag) (Unit, language.Confidence) { + if cur := t.TypeForKey("cu"); len(cur) == 3 { + c, _ := ParseISO(cur) + return c, language.Exact + } + r, conf := t.Region() + if cur, ok := FromRegion(r); ok { + return cur, conf + } + return Unit{}, language.No +} + +var ( + // Undefined and testing. + XXX Unit = Unit{} + XTS Unit = Unit{xts} + + // G10 currencies https://en.wikipedia.org/wiki/G10_currencies. + USD Unit = Unit{usd} + EUR Unit = Unit{eur} + JPY Unit = Unit{jpy} + GBP Unit = Unit{gbp} + CHF Unit = Unit{chf} + AUD Unit = Unit{aud} + NZD Unit = Unit{nzd} + CAD Unit = Unit{cad} + SEK Unit = Unit{sek} + NOK Unit = Unit{nok} + + // Additional common currencies as defined by CLDR. + BRL Unit = Unit{brl} + CNY Unit = Unit{cny} + DKK Unit = Unit{dkk} + INR Unit = Unit{inr} + RUB Unit = Unit{rub} + HKD Unit = Unit{hkd} + IDR Unit = Unit{idr} + KRW Unit = Unit{krw} + MXN Unit = Unit{mxn} + PLN Unit = Unit{pln} + SAR Unit = Unit{sar} + THB Unit = Unit{thb} + TRY Unit = Unit{try} + TWD Unit = Unit{twd} + ZAR Unit = Unit{zar} + + // Precious metals. + XAG Unit = Unit{xag} + XAU Unit = Unit{xau} + XPT Unit = Unit{xpt} + XPD Unit = Unit{xpd} +) diff --git a/vendor/golang.org/x/text/currency/format.go b/vendor/golang.org/x/text/currency/format.go new file mode 100644 index 0000000000..cc4570d3b6 --- /dev/null +++ b/vendor/golang.org/x/text/currency/format.go @@ -0,0 +1,220 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package currency + +import ( + "fmt" + "sort" + + "golang.org/x/text/internal/format" + "golang.org/x/text/internal/language/compact" + "golang.org/x/text/internal/number" + + "golang.org/x/text/language" +) + +// Amount is an amount-currency unit pair. +type Amount struct { + amount interface{} // Change to decimal(64|128). + currency Unit +} + +// Currency reports the currency unit of this amount. +func (a Amount) Currency() Unit { return a.currency } + +// TODO: based on decimal type, but may make sense to customize a bit. +// func (a Amount) Decimal() +// func (a Amount) Int() (int64, error) +// func (a Amount) Fraction() (int64, error) +// func (a Amount) Rat() *big.Rat +// func (a Amount) Float() (float64, error) +// func (a Amount) Scale() uint +// func (a Amount) Precision() uint +// func (a Amount) Sign() int +// +// Add/Sub/Div/Mul/Round. + +// Format implements fmt.Formatter. It accepts format.State for +// language-specific rendering. +func (a Amount) Format(s fmt.State, verb rune) { + v := formattedValue{ + currency: a.currency, + amount: a.amount, + format: defaultFormat, + } + v.Format(s, verb) +} + +// formattedValue is currency amount or unit that implements language-sensitive +// formatting. +type formattedValue struct { + currency Unit + amount interface{} // Amount, Unit, or number. + format *options +} + +// Format implements fmt.Formatter. It accepts format.State for +// language-specific rendering. +func (v formattedValue) Format(s fmt.State, verb rune) { + var tag language.Tag + var lang compact.ID + if state, ok := s.(format.State); ok { + tag = state.Language() + lang, _ = compact.RegionalID(compact.Tag(tag)) + } + + // Get the options. Use DefaultFormat if not present. + opt := v.format + if opt == nil { + opt = defaultFormat + } + cur := v.currency + if cur.index == 0 { + cur = opt.currency + } + + sym := opt.symbol(lang, cur) + if v.amount != nil { + var f number.Formatter + f.InitDecimal(tag) + + scale, increment := opt.kind.Rounding(cur) + f.RoundingContext.SetScale(scale) + f.RoundingContext.Increment = uint32(increment) + f.RoundingContext.IncrementScale = uint8(scale) + f.RoundingContext.Mode = number.ToNearestAway + + d := f.Append(nil, v.amount) + + fmt.Fprint(s, sym, " ", string(d)) + } else { + fmt.Fprint(s, sym) + } +} + +// Formatter decorates a given number, Unit or Amount with formatting options. +type Formatter func(amount interface{}) formattedValue + +// func (f Formatter) Options(opts ...Option) Formatter + +// TODO: call this a Formatter or FormatFunc? + +var dummy = USD.Amount(0) + +// adjust creates a new Formatter based on the adjustments of fn on f. +func (f Formatter) adjust(fn func(*options)) Formatter { + var o options = *(f(dummy).format) + fn(&o) + return o.format +} + +// Default creates a new Formatter that defaults to currency unit c if a numeric +// value is passed that is not associated with a currency. +func (f Formatter) Default(currency Unit) Formatter { + return f.adjust(func(o *options) { o.currency = currency }) +} + +// Kind sets the kind of the underlying currency unit. +func (f Formatter) Kind(k Kind) Formatter { + return f.adjust(func(o *options) { o.kind = k }) +} + +var defaultFormat *options = ISO(dummy).format + +var ( + // Uses Narrow symbols. Overrides Symbol, if present. + NarrowSymbol Formatter = Formatter(formNarrow) + + // Use Symbols instead of ISO codes, when available. + Symbol Formatter = Formatter(formSymbol) + + // Use ISO code as symbol. + ISO Formatter = Formatter(formISO) + + // TODO: + // // Use full name as symbol. + // Name Formatter +) + +// options configures rendering and rounding options for an Amount. +type options struct { + currency Unit + kind Kind + + symbol func(compactIndex compact.ID, c Unit) string +} + +func (o *options) format(amount interface{}) formattedValue { + v := formattedValue{format: o} + switch x := amount.(type) { + case Amount: + v.amount = x.amount + v.currency = x.currency + case *Amount: + v.amount = x.amount + v.currency = x.currency + case Unit: + v.currency = x + case *Unit: + v.currency = *x + default: + if o.currency.index == 0 { + panic("cannot format number without a currency being set") + } + // TODO: Must be a number. + v.amount = x + v.currency = o.currency + } + return v +} + +var ( + optISO = options{symbol: lookupISO} + optSymbol = options{symbol: lookupSymbol} + optNarrow = options{symbol: lookupNarrow} +) + +// These need to be functions, rather than curried methods, as curried methods +// are evaluated at init time, causing tables to be included unconditionally. +func formISO(x interface{}) formattedValue { return optISO.format(x) } +func formSymbol(x interface{}) formattedValue { return optSymbol.format(x) } +func formNarrow(x interface{}) formattedValue { return optNarrow.format(x) } + +func lookupISO(x compact.ID, c Unit) string { return c.String() } +func lookupSymbol(x compact.ID, c Unit) string { return normalSymbol.lookup(x, c) } +func lookupNarrow(x compact.ID, c Unit) string { return narrowSymbol.lookup(x, c) } + +type symbolIndex struct { + index []uint16 // position corresponds with compact index of language. + data []curToIndex +} + +var ( + normalSymbol = symbolIndex{normalLangIndex, normalSymIndex} + narrowSymbol = symbolIndex{narrowLangIndex, narrowSymIndex} +) + +func (x *symbolIndex) lookup(lang compact.ID, c Unit) string { + for { + index := x.data[x.index[lang]:x.index[lang+1]] + i := sort.Search(len(index), func(i int) bool { + return index[i].cur >= c.index + }) + if i < len(index) && index[i].cur == c.index { + x := index[i].idx + start := x + 1 + end := start + uint16(symbols[x]) + if start == end { + return c.String() + } + return symbols[start:end] + } + if lang == 0 { + break + } + lang = lang.Parent() + } + return c.String() +} diff --git a/vendor/golang.org/x/text/currency/query.go b/vendor/golang.org/x/text/currency/query.go new file mode 100644 index 0000000000..7bf9430a62 --- /dev/null +++ b/vendor/golang.org/x/text/currency/query.go @@ -0,0 +1,152 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package currency + +import ( + "sort" + "time" + + "golang.org/x/text/language" +) + +// QueryIter represents a set of Units. The default set includes all Units that +// are currently in use as legal tender in any Region. +type QueryIter interface { + // Next returns true if there is a next element available. + // It must be called before any of the other methods are called. + Next() bool + + // Unit returns the unit of the current iteration. + Unit() Unit + + // Region returns the Region for the current iteration. + Region() language.Region + + // From returns the date from which the unit was used in the region. + // It returns false if this date is unknown. + From() (time.Time, bool) + + // To returns the date up till which the unit was used in the region. + // It returns false if this date is unknown or if the unit is still in use. + To() (time.Time, bool) + + // IsTender reports whether the unit is a legal tender in the region during + // the specified date range. + IsTender() bool +} + +// Query represents a set of Units. The default set includes all Units that are +// currently in use as legal tender in any Region. +func Query(options ...QueryOption) QueryIter { + it := &iter{ + end: len(regionData), + date: 0xFFFFFFFF, + } + for _, fn := range options { + fn(it) + } + return it +} + +// NonTender returns a new query that also includes matching Units that are not +// legal tender. +var NonTender QueryOption = nonTender + +func nonTender(i *iter) { + i.nonTender = true +} + +// Historical selects the units for all dates. +var Historical QueryOption = historical + +func historical(i *iter) { + i.date = hist +} + +// A QueryOption can be used to change the set of unit information returned by +// a query. +type QueryOption func(*iter) + +// Date queries the units that were in use at the given point in history. +func Date(t time.Time) QueryOption { + d := toDate(t) + return func(i *iter) { + i.date = d + } +} + +// Region limits the query to only return entries for the given region. +func Region(r language.Region) QueryOption { + p, end := len(regionData), len(regionData) + x := regionToCode(r) + i := sort.Search(len(regionData), func(i int) bool { + return regionData[i].region >= x + }) + if i < len(regionData) && regionData[i].region == x { + p = i + for i++; i < len(regionData) && regionData[i].region == x; i++ { + } + end = i + } + return func(i *iter) { + i.p, i.end = p, end + } +} + +const ( + hist = 0x00 + now = 0xFFFFFFFF +) + +type iter struct { + *regionInfo + p, end int + date uint32 + nonTender bool +} + +func (i *iter) Next() bool { + for ; i.p < i.end; i.p++ { + i.regionInfo = ®ionData[i.p] + if !i.nonTender && !i.IsTender() { + continue + } + if i.date == hist || (i.from <= i.date && (i.to == 0 || i.date <= i.to)) { + i.p++ + return true + } + } + return false +} + +func (r *regionInfo) Region() language.Region { + // TODO: this could be much faster. + var buf [2]byte + buf[0] = uint8(r.region >> 8) + buf[1] = uint8(r.region) + return language.MustParseRegion(string(buf[:])) +} + +func (r *regionInfo) Unit() Unit { + return Unit{r.code &^ nonTenderBit} +} + +func (r *regionInfo) IsTender() bool { + return r.code&nonTenderBit == 0 +} + +func (r *regionInfo) From() (time.Time, bool) { + if r.from == 0 { + return time.Time{}, false + } + return fromDate(r.from), true +} + +func (r *regionInfo) To() (time.Time, bool) { + if r.to == 0 { + return time.Time{}, false + } + return fromDate(r.to), true +} diff --git a/vendor/golang.org/x/text/currency/tables.go b/vendor/golang.org/x/text/currency/tables.go new file mode 100644 index 0000000000..d1a7440a6b --- /dev/null +++ b/vendor/golang.org/x/text/currency/tables.go @@ -0,0 +1,2629 @@ +// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. + +package currency + +import "golang.org/x/text/internal/tag" + +// CLDRVersion is the CLDR version from which the tables in this package are derived. +const CLDRVersion = "32" + +const ( + xxx = 285 + xts = 283 + usd = 252 + eur = 94 + jpy = 133 + gbp = 99 + chf = 61 + aud = 19 + nzd = 192 + cad = 58 + sek = 219 + nok = 190 + dkk = 82 + xag = 266 + xau = 267 + xpt = 280 + xpd = 278 + brl = 46 + cny = 68 + inr = 125 + rub = 210 + hkd = 114 + idr = 120 + krw = 141 + mxn = 178 + pln = 201 + sar = 213 + thb = 235 + try = 244 + twd = 246 + zar = 293 +) + +// currency holds an alphabetically sorted list of canonical 3-letter currency +// identifiers. Each identifier is followed by a byte of type currencyInfo, +// defined in gen_common.go. +const currency tag.Index = "" + // Size: 1208 bytes + "\x00\x00\x00\x00ADP\x09AED\x00AFA\x00AFN\x09ALK\x00ALL\x09AMD\x09ANG\x00" + + "AOA\x00AOK\x00AON\x00AOR\x00ARA\x00ARL\x00ARM\x00ARP\x00ARS\x00ATS\x00AU" + + "D\x00AWG\x00AZM\x00AZN\x00BAD\x00BAM\x00BAN\x00BBD\x00BDT\x00BEC\x00BEF" + + "\x00BEL\x00BGL\x00BGM\x00BGN\x00BGO\x00BHD\x1bBIF\x09BMD\x00BND\x00BOB" + + "\x00BOL\x00BOP\x00BOV\x00BRB\x00BRC\x00BRE\x00BRL\x00BRN\x00BRR\x00BRZ" + + "\x00BSD\x00BTN\x00BUK\x00BWP\x00BYB\x00BYN\x00BYR\x09BZD\x00CAD(CDF\x00C" + + "HE\x00CHF(CHW\x00CLE\x00CLF$CLP\x09CNH\x00CNX\x00CNY\x00COP\x09COU\x00CR" + + "C\x08CSD\x00CSK\x00CUC\x00CUP\x00CVE\x00CYP\x00CZK\x08DDM\x00DEM\x00DJF" + + "\x09DKK0DOP\x00DZD\x00ECS\x00ECV\x00EEK\x00EGP\x00ERN\x00ESA\x00ESB\x00E" + + "SP\x09ETB\x00EUR\x00FIM\x00FJD\x00FKP\x00FRF\x00GBP\x00GEK\x00GEL\x00GHC" + + "\x00GHS\x00GIP\x00GMD\x00GNF\x09GNS\x00GQE\x00GRD\x00GTQ\x00GWE\x00GWP" + + "\x00GYD\x09HKD\x00HNL\x00HRD\x00HRK\x00HTG\x00HUF\x08IDR\x09IEP\x00ILP" + + "\x00ILR\x00ILS\x00INR\x00IQD\x09IRR\x09ISJ\x00ISK\x09ITL\x09JMD\x00JOD" + + "\x1bJPY\x09KES\x00KGS\x00KHR\x00KMF\x09KPW\x09KRH\x00KRO\x00KRW\x09KWD" + + "\x1bKYD\x00KZT\x00LAK\x09LBP\x09LKR\x00LRD\x00LSL\x00LTL\x00LTT\x00LUC" + + "\x00LUF\x09LUL\x00LVL\x00LVR\x00LYD\x1bMAD\x00MAF\x00MCF\x00MDC\x00MDL" + + "\x00MGA\x09MGF\x09MKD\x00MKN\x00MLF\x00MMK\x09MNT\x09MOP\x00MRO\x09MTL" + + "\x00MTP\x00MUR\x09MVP\x00MVR\x00MWK\x00MXN\x00MXP\x00MXV\x00MYR\x00MZE" + + "\x00MZM\x00MZN\x00NAD\x00NGN\x00NIC\x00NIO\x00NLG\x00NOK\x08NPR\x00NZD" + + "\x00OMR\x1bPAB\x00PEI\x00PEN\x00PES\x00PGK\x00PHP\x00PKR\x09PLN\x00PLZ" + + "\x00PTE\x00PYG\x09QAR\x00RHD\x00ROL\x00RON\x00RSD\x09RUB\x00RUR\x00RWF" + + "\x09SAR\x00SBD\x00SCR\x00SDD\x00SDG\x00SDP\x00SEK\x08SGD\x00SHP\x00SIT" + + "\x00SKK\x00SLL\x09SOS\x09SRD\x00SRG\x00SSP\x00STD\x09STN\x00SUR\x00SVC" + + "\x00SYP\x09SZL\x00THB\x00TJR\x00TJS\x00TMM\x09TMT\x00TND\x1bTOP\x00TPE" + + "\x00TRL\x09TRY\x00TTD\x00TWD\x08TZS\x09UAH\x00UAK\x00UGS\x00UGX\x09USD" + + "\x00USN\x00USS\x00UYI\x09UYP\x00UYU\x00UZS\x09VEB\x00VEF\x00VND\x09VNN" + + "\x00VUV\x09WST\x00XAF\x09XAG\x00XAU\x00XBA\x00XBB\x00XBC\x00XBD\x00XCD" + + "\x00XDR\x00XEU\x00XFO\x00XFU\x00XOF\x09XPD\x00XPF\x09XPT\x00XRE\x00XSU" + + "\x00XTS\x00XUA\x00XXX\x00YDD\x00YER\x09YUD\x00YUM\x00YUN\x00YUR\x00ZAL" + + "\x00ZAR\x00ZMK\x09ZMW\x00ZRN\x00ZRZ\x00ZWD\x09ZWL\x00ZWR\x00\xff\xff\xff" + + "\xff" + +const numCurrencies = 300 + +type toCurrency struct { + region uint16 + code uint16 +} + +var regionToCurrency = []toCurrency{ // 255 elements + 0: {region: 0x4143, code: 0xdd}, + 1: {region: 0x4144, code: 0x5e}, + 2: {region: 0x4145, code: 0x2}, + 3: {region: 0x4146, code: 0x4}, + 4: {region: 0x4147, code: 0x110}, + 5: {region: 0x4149, code: 0x110}, + 6: {region: 0x414c, code: 0x6}, + 7: {region: 0x414d, code: 0x7}, + 8: {region: 0x414f, code: 0x9}, + 9: {region: 0x4152, code: 0x11}, + 10: {region: 0x4153, code: 0xfc}, + 11: {region: 0x4154, code: 0x5e}, + 12: {region: 0x4155, code: 0x13}, + 13: {region: 0x4157, code: 0x14}, + 14: {region: 0x4158, code: 0x5e}, + 15: {region: 0x415a, code: 0x16}, + 16: {region: 0x4241, code: 0x18}, + 17: {region: 0x4242, code: 0x1a}, + 18: {region: 0x4244, code: 0x1b}, + 19: {region: 0x4245, code: 0x5e}, + 20: {region: 0x4246, code: 0x115}, + 21: {region: 0x4247, code: 0x21}, + 22: {region: 0x4248, code: 0x23}, + 23: {region: 0x4249, code: 0x24}, + 24: {region: 0x424a, code: 0x115}, + 25: {region: 0x424c, code: 0x5e}, + 26: {region: 0x424d, code: 0x25}, + 27: {region: 0x424e, code: 0x26}, + 28: {region: 0x424f, code: 0x27}, + 29: {region: 0x4251, code: 0xfc}, + 30: {region: 0x4252, code: 0x2e}, + 31: {region: 0x4253, code: 0x32}, + 32: {region: 0x4254, code: 0x33}, + 33: {region: 0x4256, code: 0xbe}, + 34: {region: 0x4257, code: 0x35}, + 35: {region: 0x4259, code: 0x37}, + 36: {region: 0x425a, code: 0x39}, + 37: {region: 0x4341, code: 0x3a}, + 38: {region: 0x4343, code: 0x13}, + 39: {region: 0x4344, code: 0x3b}, + 40: {region: 0x4346, code: 0x109}, + 41: {region: 0x4347, code: 0x109}, + 42: {region: 0x4348, code: 0x3d}, + 43: {region: 0x4349, code: 0x115}, + 44: {region: 0x434b, code: 0xc0}, + 45: {region: 0x434c, code: 0x41}, + 46: {region: 0x434d, code: 0x109}, + 47: {region: 0x434e, code: 0x44}, + 48: {region: 0x434f, code: 0x45}, + 49: {region: 0x4352, code: 0x47}, + 50: {region: 0x4355, code: 0x4b}, + 51: {region: 0x4356, code: 0x4c}, + 52: {region: 0x4357, code: 0x8}, + 53: {region: 0x4358, code: 0x13}, + 54: {region: 0x4359, code: 0x5e}, + 55: {region: 0x435a, code: 0x4e}, + 56: {region: 0x4445, code: 0x5e}, + 57: {region: 0x4447, code: 0xfc}, + 58: {region: 0x444a, code: 0x51}, + 59: {region: 0x444b, code: 0x52}, + 60: {region: 0x444d, code: 0x110}, + 61: {region: 0x444f, code: 0x53}, + 62: {region: 0x445a, code: 0x54}, + 63: {region: 0x4541, code: 0x5e}, + 64: {region: 0x4543, code: 0xfc}, + 65: {region: 0x4545, code: 0x5e}, + 66: {region: 0x4547, code: 0x58}, + 67: {region: 0x4548, code: 0x9e}, + 68: {region: 0x4552, code: 0x59}, + 69: {region: 0x4553, code: 0x5e}, + 70: {region: 0x4554, code: 0x5d}, + 71: {region: 0x4555, code: 0x5e}, + 72: {region: 0x4649, code: 0x5e}, + 73: {region: 0x464a, code: 0x60}, + 74: {region: 0x464b, code: 0x61}, + 75: {region: 0x464d, code: 0xfc}, + 76: {region: 0x464f, code: 0x52}, + 77: {region: 0x4652, code: 0x5e}, + 78: {region: 0x4741, code: 0x109}, + 79: {region: 0x4742, code: 0x63}, + 80: {region: 0x4744, code: 0x110}, + 81: {region: 0x4745, code: 0x65}, + 82: {region: 0x4746, code: 0x5e}, + 83: {region: 0x4747, code: 0x63}, + 84: {region: 0x4748, code: 0x67}, + 85: {region: 0x4749, code: 0x68}, + 86: {region: 0x474c, code: 0x52}, + 87: {region: 0x474d, code: 0x69}, + 88: {region: 0x474e, code: 0x6a}, + 89: {region: 0x4750, code: 0x5e}, + 90: {region: 0x4751, code: 0x109}, + 91: {region: 0x4752, code: 0x5e}, + 92: {region: 0x4753, code: 0x63}, + 93: {region: 0x4754, code: 0x6e}, + 94: {region: 0x4755, code: 0xfc}, + 95: {region: 0x4757, code: 0x115}, + 96: {region: 0x4759, code: 0x71}, + 97: {region: 0x484b, code: 0x72}, + 98: {region: 0x484d, code: 0x13}, + 99: {region: 0x484e, code: 0x73}, + 100: {region: 0x4852, code: 0x75}, + 101: {region: 0x4854, code: 0x76}, + 102: {region: 0x4855, code: 0x77}, + 103: {region: 0x4943, code: 0x5e}, + 104: {region: 0x4944, code: 0x78}, + 105: {region: 0x4945, code: 0x5e}, + 106: {region: 0x494c, code: 0x7c}, + 107: {region: 0x494d, code: 0x63}, + 108: {region: 0x494e, code: 0x7d}, + 109: {region: 0x494f, code: 0xfc}, + 110: {region: 0x4951, code: 0x7e}, + 111: {region: 0x4952, code: 0x7f}, + 112: {region: 0x4953, code: 0x81}, + 113: {region: 0x4954, code: 0x5e}, + 114: {region: 0x4a45, code: 0x63}, + 115: {region: 0x4a4d, code: 0x83}, + 116: {region: 0x4a4f, code: 0x84}, + 117: {region: 0x4a50, code: 0x85}, + 118: {region: 0x4b45, code: 0x86}, + 119: {region: 0x4b47, code: 0x87}, + 120: {region: 0x4b48, code: 0x88}, + 121: {region: 0x4b49, code: 0x13}, + 122: {region: 0x4b4d, code: 0x89}, + 123: {region: 0x4b4e, code: 0x110}, + 124: {region: 0x4b50, code: 0x8a}, + 125: {region: 0x4b52, code: 0x8d}, + 126: {region: 0x4b57, code: 0x8e}, + 127: {region: 0x4b59, code: 0x8f}, + 128: {region: 0x4b5a, code: 0x90}, + 129: {region: 0x4c41, code: 0x91}, + 130: {region: 0x4c42, code: 0x92}, + 131: {region: 0x4c43, code: 0x110}, + 132: {region: 0x4c49, code: 0x3d}, + 133: {region: 0x4c4b, code: 0x93}, + 134: {region: 0x4c52, code: 0x94}, + 135: {region: 0x4c53, code: 0x125}, + 136: {region: 0x4c54, code: 0x5e}, + 137: {region: 0x4c55, code: 0x5e}, + 138: {region: 0x4c56, code: 0x5e}, + 139: {region: 0x4c59, code: 0x9d}, + 140: {region: 0x4d41, code: 0x9e}, + 141: {region: 0x4d43, code: 0x5e}, + 142: {region: 0x4d44, code: 0xa2}, + 143: {region: 0x4d45, code: 0x5e}, + 144: {region: 0x4d46, code: 0x5e}, + 145: {region: 0x4d47, code: 0xa3}, + 146: {region: 0x4d48, code: 0xfc}, + 147: {region: 0x4d4b, code: 0xa5}, + 148: {region: 0x4d4c, code: 0x115}, + 149: {region: 0x4d4d, code: 0xa8}, + 150: {region: 0x4d4e, code: 0xa9}, + 151: {region: 0x4d4f, code: 0xaa}, + 152: {region: 0x4d50, code: 0xfc}, + 153: {region: 0x4d51, code: 0x5e}, + 154: {region: 0x4d52, code: 0xab}, + 155: {region: 0x4d53, code: 0x110}, + 156: {region: 0x4d54, code: 0x5e}, + 157: {region: 0x4d55, code: 0xae}, + 158: {region: 0x4d56, code: 0xb0}, + 159: {region: 0x4d57, code: 0xb1}, + 160: {region: 0x4d58, code: 0xb2}, + 161: {region: 0x4d59, code: 0xb5}, + 162: {region: 0x4d5a, code: 0xb8}, + 163: {region: 0x4e41, code: 0xb9}, + 164: {region: 0x4e43, code: 0x117}, + 165: {region: 0x4e45, code: 0x115}, + 166: {region: 0x4e46, code: 0x13}, + 167: {region: 0x4e47, code: 0xba}, + 168: {region: 0x4e49, code: 0xbc}, + 169: {region: 0x4e4c, code: 0x5e}, + 170: {region: 0x4e4f, code: 0xbe}, + 171: {region: 0x4e50, code: 0xbf}, + 172: {region: 0x4e52, code: 0x13}, + 173: {region: 0x4e55, code: 0xc0}, + 174: {region: 0x4e5a, code: 0xc0}, + 175: {region: 0x4f4d, code: 0xc1}, + 176: {region: 0x5041, code: 0xc2}, + 177: {region: 0x5045, code: 0xc4}, + 178: {region: 0x5046, code: 0x117}, + 179: {region: 0x5047, code: 0xc6}, + 180: {region: 0x5048, code: 0xc7}, + 181: {region: 0x504b, code: 0xc8}, + 182: {region: 0x504c, code: 0xc9}, + 183: {region: 0x504d, code: 0x5e}, + 184: {region: 0x504e, code: 0xc0}, + 185: {region: 0x5052, code: 0xfc}, + 186: {region: 0x5053, code: 0x7c}, + 187: {region: 0x5054, code: 0x5e}, + 188: {region: 0x5057, code: 0xfc}, + 189: {region: 0x5059, code: 0xcc}, + 190: {region: 0x5141, code: 0xcd}, + 191: {region: 0x5245, code: 0x5e}, + 192: {region: 0x524f, code: 0xd0}, + 193: {region: 0x5253, code: 0xd1}, + 194: {region: 0x5255, code: 0xd2}, + 195: {region: 0x5257, code: 0xd4}, + 196: {region: 0x5341, code: 0xd5}, + 197: {region: 0x5342, code: 0xd6}, + 198: {region: 0x5343, code: 0xd7}, + 199: {region: 0x5344, code: 0xd9}, + 200: {region: 0x5345, code: 0xdb}, + 201: {region: 0x5347, code: 0xdc}, + 202: {region: 0x5348, code: 0xdd}, + 203: {region: 0x5349, code: 0x5e}, + 204: {region: 0x534a, code: 0xbe}, + 205: {region: 0x534b, code: 0x5e}, + 206: {region: 0x534c, code: 0xe0}, + 207: {region: 0x534d, code: 0x5e}, + 208: {region: 0x534e, code: 0x115}, + 209: {region: 0x534f, code: 0xe1}, + 210: {region: 0x5352, code: 0xe2}, + 211: {region: 0x5353, code: 0xe4}, + 212: {region: 0x5354, code: 0xe6}, + 213: {region: 0x5356, code: 0xfc}, + 214: {region: 0x5358, code: 0x8}, + 215: {region: 0x5359, code: 0xe9}, + 216: {region: 0x535a, code: 0xea}, + 217: {region: 0x5441, code: 0x63}, + 218: {region: 0x5443, code: 0xfc}, + 219: {region: 0x5444, code: 0x109}, + 220: {region: 0x5446, code: 0x5e}, + 221: {region: 0x5447, code: 0x115}, + 222: {region: 0x5448, code: 0xeb}, + 223: {region: 0x544a, code: 0xed}, + 224: {region: 0x544b, code: 0xc0}, + 225: {region: 0x544c, code: 0xfc}, + 226: {region: 0x544d, code: 0xef}, + 227: {region: 0x544e, code: 0xf0}, + 228: {region: 0x544f, code: 0xf1}, + 229: {region: 0x5452, code: 0xf4}, + 230: {region: 0x5454, code: 0xf5}, + 231: {region: 0x5456, code: 0x13}, + 232: {region: 0x5457, code: 0xf6}, + 233: {region: 0x545a, code: 0xf7}, + 234: {region: 0x5541, code: 0xf8}, + 235: {region: 0x5547, code: 0xfb}, + 236: {region: 0x554d, code: 0xfc}, + 237: {region: 0x5553, code: 0xfc}, + 238: {region: 0x5559, code: 0x101}, + 239: {region: 0x555a, code: 0x102}, + 240: {region: 0x5641, code: 0x5e}, + 241: {region: 0x5643, code: 0x110}, + 242: {region: 0x5645, code: 0x104}, + 243: {region: 0x5647, code: 0xfc}, + 244: {region: 0x5649, code: 0xfc}, + 245: {region: 0x564e, code: 0x105}, + 246: {region: 0x5655, code: 0x107}, + 247: {region: 0x5746, code: 0x117}, + 248: {region: 0x5753, code: 0x108}, + 249: {region: 0x584b, code: 0x5e}, + 250: {region: 0x5945, code: 0x11f}, + 251: {region: 0x5954, code: 0x5e}, + 252: {region: 0x5a41, code: 0x125}, + 253: {region: 0x5a4d, code: 0x127}, + 254: {region: 0x5a57, code: 0xfc}, +} // Size: 1044 bytes + +type regionInfo struct { + region uint16 + code uint16 + from uint32 + to uint32 +} + +var regionData = []regionInfo{ // 495 elements + 0: {region: 0x4143, code: 0xdd, from: 0xf7021, to: 0x0}, + 1: {region: 0x4144, code: 0x5e, from: 0xf9e21, to: 0x0}, + 2: {region: 0x4144, code: 0x5c, from: 0xea221, to: 0xfa45c}, + 3: {region: 0x4144, code: 0x62, from: 0xf5021, to: 0xfa451}, + 4: {region: 0x4144, code: 0x1, from: 0xf2021, to: 0xfa39f}, + 5: {region: 0x4145, code: 0x2, from: 0xf6ab3, to: 0x0}, + 6: {region: 0x4146, code: 0x4, from: 0xfa547, to: 0x0}, + 7: {region: 0x4146, code: 0x3, from: 0xf0e6e, to: 0xfa59f}, + 8: {region: 0x4147, code: 0x110, from: 0xf5b46, to: 0x0}, + 9: {region: 0x4149, code: 0x110, from: 0xf5b46, to: 0x0}, + 10: {region: 0x414c, code: 0x6, from: 0xf5b10, to: 0x0}, + 11: {region: 0x414c, code: 0x5, from: 0xf3561, to: 0xf5b10}, + 12: {region: 0x414d, code: 0x7, from: 0xf9376, to: 0x0}, + 13: {region: 0x414d, code: 0xd3, from: 0xf8f99, to: 0xf9376}, + 14: {region: 0x414d, code: 0xe7, from: 0xf5221, to: 0xf8f99}, + 15: {region: 0x414f, code: 0x9, from: 0xf9f8d, to: 0x0}, + 16: {region: 0x414f, code: 0xc, from: 0xf96e1, to: 0xfa041}, + 17: {region: 0x414f, code: 0xb, from: 0xf8d39, to: 0xfa041}, + 18: {region: 0x414f, code: 0xa, from: 0xf7228, to: 0xf8e61}, + 19: {region: 0x4151, code: 0x811d, from: 0x0, to: 0x0}, + 20: {region: 0x4152, code: 0x11, from: 0xf9021, to: 0x0}, + 21: {region: 0x4152, code: 0xd, from: 0xf82ce, to: 0xf9021}, + 22: {region: 0x4152, code: 0x10, from: 0xf7ec1, to: 0xf82ce}, + 23: {region: 0x4152, code: 0xe, from: 0xf6421, to: 0xf7ec1}, + 24: {region: 0x4152, code: 0xf, from: 0xeb365, to: 0xf6421}, + 25: {region: 0x4153, code: 0xfc, from: 0xee0f0, to: 0x0}, + 26: {region: 0x4154, code: 0x5e, from: 0xf9e21, to: 0x0}, + 27: {region: 0x4154, code: 0x12, from: 0xf3784, to: 0xfa45c}, + 28: {region: 0x4155, code: 0x13, from: 0xf5c4e, to: 0x0}, + 29: {region: 0x4157, code: 0x14, from: 0xf8421, to: 0x0}, + 30: {region: 0x4157, code: 0x8, from: 0xf28aa, to: 0xf8421}, + 31: {region: 0x4158, code: 0x5e, from: 0xf9e21, to: 0x0}, + 32: {region: 0x415a, code: 0x16, from: 0xfac21, to: 0x0}, + 33: {region: 0x415a, code: 0x15, from: 0xf9376, to: 0xfad9f}, + 34: {region: 0x415a, code: 0xd3, from: 0xf8f99, to: 0xf9421}, + 35: {region: 0x415a, code: 0xe7, from: 0xf5221, to: 0xf8f99}, + 36: {region: 0x4241, code: 0x18, from: 0xf9621, to: 0x0}, + 37: {region: 0x4241, code: 0x19, from: 0xf950f, to: 0xf9ae1}, + 38: {region: 0x4241, code: 0x17, from: 0xf90e1, to: 0xf950f}, + 39: {region: 0x4241, code: 0x123, from: 0xf90e1, to: 0xf9341}, + 40: {region: 0x4241, code: 0x122, from: 0xf8c21, to: 0xf90e1}, + 41: {region: 0x4241, code: 0x120, from: 0xf5c21, to: 0xf8c21}, + 42: {region: 0x4242, code: 0x1a, from: 0xf6b83, to: 0x0}, + 43: {region: 0x4242, code: 0x110, from: 0xf5b46, to: 0xf6b83}, + 44: {region: 0x4244, code: 0x1b, from: 0xf6821, to: 0x0}, + 45: {region: 0x4244, code: 0xc8, from: 0xf3881, to: 0xf6821}, + 46: {region: 0x4244, code: 0x7d, from: 0xe5711, to: 0xf3881}, + 47: {region: 0x4245, code: 0x5e, from: 0xf9e21, to: 0x0}, + 48: {region: 0x4245, code: 0x1d, from: 0xe4e47, to: 0xfa45c}, + 49: {region: 0x4245, code: 0xbd, from: 0xe318f, to: 0xe4e47}, + 50: {region: 0x4245, code: 0x801e, from: 0xf6421, to: 0xf8c65}, + 51: {region: 0x4245, code: 0x801c, from: 0xf6421, to: 0xf8c65}, + 52: {region: 0x4246, code: 0x115, from: 0xf8104, to: 0x0}, + 53: {region: 0x4247, code: 0x21, from: 0xf9ee5, to: 0x0}, + 54: {region: 0x4247, code: 0x1f, from: 0xf5421, to: 0xf9ee5}, + 55: {region: 0x4247, code: 0x20, from: 0xf40ac, to: 0xf5421}, + 56: {region: 0x4247, code: 0x22, from: 0xeaee8, to: 0xf40ac}, + 57: {region: 0x4248, code: 0x23, from: 0xf5b50, to: 0x0}, + 58: {region: 0x4249, code: 0x24, from: 0xf58b3, to: 0x0}, + 59: {region: 0x424a, code: 0x115, from: 0xf6f7e, to: 0x0}, + 60: {region: 0x424c, code: 0x5e, from: 0xf9e21, to: 0x0}, + 61: {region: 0x424c, code: 0x62, from: 0xf5021, to: 0xfa451}, + 62: {region: 0x424d, code: 0x25, from: 0xf6446, to: 0x0}, + 63: {region: 0x424e, code: 0x26, from: 0xf5ecc, to: 0x0}, + 64: {region: 0x424e, code: 0xb5, from: 0xf5730, to: 0xf5ecc}, + 65: {region: 0x424f, code: 0x27, from: 0xf8621, to: 0x0}, + 66: {region: 0x424f, code: 0x29, from: 0xf5621, to: 0xf859f}, + 67: {region: 0x424f, code: 0x28, from: 0xe8ed7, to: 0xf5621}, + 68: {region: 0x424f, code: 0x802a, from: 0x0, to: 0x0}, + 69: {region: 0x4251, code: 0xfc, from: 0xfb621, to: 0x0}, + 70: {region: 0x4251, code: 0x8, from: 0xfb54a, to: 0xfb621}, + 71: {region: 0x4252, code: 0x2e, from: 0xf94e1, to: 0x0}, + 72: {region: 0x4252, code: 0x30, from: 0xf9301, to: 0xf94e1}, + 73: {region: 0x4252, code: 0x2d, from: 0xf8c70, to: 0xf9301}, + 74: {region: 0x4252, code: 0x2f, from: 0xf8a2f, to: 0xf8c70}, + 75: {region: 0x4252, code: 0x2c, from: 0xf845c, to: 0xf8a2f}, + 76: {region: 0x4252, code: 0x2b, from: 0xf5e4d, to: 0xf845c}, + 77: {region: 0x4252, code: 0x31, from: 0xf2d61, to: 0xf5e4d}, + 78: {region: 0x4253, code: 0x32, from: 0xf5cb9, to: 0x0}, + 79: {region: 0x4254, code: 0x33, from: 0xf6c90, to: 0x0}, + 80: {region: 0x4254, code: 0x7d, from: 0xee621, to: 0x0}, + 81: {region: 0x4255, code: 0x34, from: 0xf40e1, to: 0xf8ad2}, + 82: {region: 0x4256, code: 0xbe, from: 0xee2c7, to: 0x0}, + 83: {region: 0x4257, code: 0x35, from: 0xf7117, to: 0x0}, + 84: {region: 0x4257, code: 0x125, from: 0xf524e, to: 0xf7117}, + 85: {region: 0x4259, code: 0x37, from: 0xfc0e1, to: 0x0}, + 86: {region: 0x4259, code: 0x38, from: 0xfa021, to: 0xfc221}, + 87: {region: 0x4259, code: 0x36, from: 0xf9501, to: 0xfa19f}, + 88: {region: 0x4259, code: 0xd3, from: 0xf8f99, to: 0xf9568}, + 89: {region: 0x4259, code: 0xe7, from: 0xf5221, to: 0xf8f99}, + 90: {region: 0x425a, code: 0x39, from: 0xf6c21, to: 0x0}, + 91: {region: 0x4341, code: 0x3a, from: 0xe8421, to: 0x0}, + 92: {region: 0x4343, code: 0x13, from: 0xf5c4e, to: 0x0}, + 93: {region: 0x4344, code: 0x3b, from: 0xf9ce1, to: 0x0}, + 94: {region: 0x4344, code: 0x128, from: 0xf9361, to: 0xf9ce1}, + 95: {region: 0x4344, code: 0x129, from: 0xf675b, to: 0xf9361}, + 96: {region: 0x4346, code: 0x109, from: 0xf9221, to: 0x0}, + 97: {region: 0x4347, code: 0x109, from: 0xf9221, to: 0x0}, + 98: {region: 0x4348, code: 0x3d, from: 0xe0e71, to: 0x0}, + 99: {region: 0x4348, code: 0x803c, from: 0x0, to: 0x0}, + 100: {region: 0x4348, code: 0x803e, from: 0x0, to: 0x0}, + 101: {region: 0x4349, code: 0x115, from: 0xf4d84, to: 0x0}, + 102: {region: 0x434b, code: 0xc0, from: 0xf5eea, to: 0x0}, + 103: {region: 0x434c, code: 0x41, from: 0xf6f3d, to: 0x0}, + 104: {region: 0x434c, code: 0x3f, from: 0xf5021, to: 0xf6f3d}, + 105: {region: 0x434c, code: 0x8040, from: 0x0, to: 0x0}, + 106: {region: 0x434d, code: 0x109, from: 0xf6a81, to: 0x0}, + 107: {region: 0x434e, code: 0x44, from: 0xf4261, to: 0x0}, + 108: {region: 0x434e, code: 0x8043, from: 0xf7621, to: 0xf9d9f}, + 109: {region: 0x434e, code: 0x8042, from: 0xfb4f3, to: 0x0}, + 110: {region: 0x434f, code: 0x45, from: 0xee221, to: 0x0}, + 111: {region: 0x434f, code: 0x8046, from: 0x0, to: 0x0}, + 112: {region: 0x4350, code: 0x811d, from: 0x0, to: 0x0}, + 113: {region: 0x4352, code: 0x47, from: 0xed15a, to: 0x0}, + 114: {region: 0x4353, code: 0x48, from: 0xfa4af, to: 0xfacc3}, + 115: {region: 0x4353, code: 0x5e, from: 0xfa644, to: 0xfacc3}, + 116: {region: 0x4353, code: 0x121, from: 0xf9438, to: 0xfa4af}, + 117: {region: 0x4355, code: 0x4b, from: 0xe8621, to: 0x0}, + 118: {region: 0x4355, code: 0x4a, from: 0xf9421, to: 0x0}, + 119: {region: 0x4355, code: 0xfc, from: 0xed621, to: 0xf4e21}, + 120: {region: 0x4356, code: 0x4c, from: 0xef421, to: 0x0}, + 121: {region: 0x4356, code: 0xcb, from: 0xeeeb6, to: 0xf6ee5}, + 122: {region: 0x4357, code: 0x8, from: 0xfb54a, to: 0x0}, + 123: {region: 0x4358, code: 0x13, from: 0xf5c4e, to: 0x0}, + 124: {region: 0x4359, code: 0x5e, from: 0xfb021, to: 0x0}, + 125: {region: 0x4359, code: 0x4d, from: 0xef52a, to: 0xfb03f}, + 126: {region: 0x435a, code: 0x4e, from: 0xf9221, to: 0x0}, + 127: {region: 0x435a, code: 0x49, from: 0xf42c1, to: 0xf9261}, + 128: {region: 0x4444, code: 0x4f, from: 0xf38f4, to: 0xf8d42}, + 129: {region: 0x4445, code: 0x5e, from: 0xf9e21, to: 0x0}, + 130: {region: 0x4445, code: 0x50, from: 0xf38d4, to: 0xfa45c}, + 131: {region: 0x4447, code: 0xfc, from: 0xf5b68, to: 0x0}, + 132: {region: 0x444a, code: 0x51, from: 0xf72db, to: 0x0}, + 133: {region: 0x444b, code: 0x52, from: 0xea2bb, to: 0x0}, + 134: {region: 0x444d, code: 0x110, from: 0xf5b46, to: 0x0}, + 135: {region: 0x444f, code: 0x53, from: 0xf3741, to: 0x0}, + 136: {region: 0x444f, code: 0xfc, from: 0xee2d5, to: 0xf3741}, + 137: {region: 0x445a, code: 0x54, from: 0xf5881, to: 0x0}, + 138: {region: 0x4541, code: 0x5e, from: 0xf9e21, to: 0x0}, + 139: {region: 0x4543, code: 0xfc, from: 0xfa142, to: 0x0}, + 140: {region: 0x4543, code: 0x55, from: 0xeb881, to: 0xfa142}, + 141: {region: 0x4543, code: 0x8056, from: 0xf92b7, to: 0xfa029}, + 142: {region: 0x4545, code: 0x5e, from: 0xfb621, to: 0x0}, + 143: {region: 0x4545, code: 0x57, from: 0xf90d5, to: 0xfb59f}, + 144: {region: 0x4545, code: 0xe7, from: 0xf5221, to: 0xf90d4}, + 145: {region: 0x4547, code: 0x58, from: 0xebb6e, to: 0x0}, + 146: {region: 0x4548, code: 0x9e, from: 0xf705a, to: 0x0}, + 147: {region: 0x4552, code: 0x59, from: 0xf9b68, to: 0x0}, + 148: {region: 0x4552, code: 0x5d, from: 0xf92b8, to: 0xf9b68}, + 149: {region: 0x4553, code: 0x5e, from: 0xf9e21, to: 0x0}, + 150: {region: 0x4553, code: 0x5c, from: 0xe9953, to: 0xfa45c}, + 151: {region: 0x4553, code: 0x805a, from: 0xf7421, to: 0xf7b9f}, + 152: {region: 0x4553, code: 0x805b, from: 0xf6e21, to: 0xf959f}, + 153: {region: 0x4554, code: 0x5d, from: 0xf712f, to: 0x0}, + 154: {region: 0x4555, code: 0x5e, from: 0xf9e21, to: 0x0}, + 155: {region: 0x4555, code: 0x8112, from: 0xf7621, to: 0xf9d9f}, + 156: {region: 0x4649, code: 0x5e, from: 0xf9e21, to: 0x0}, + 157: {region: 0x4649, code: 0x5f, from: 0xf5621, to: 0xfa45c}, + 158: {region: 0x464a, code: 0x60, from: 0xf622d, to: 0x0}, + 159: {region: 0x464b, code: 0x61, from: 0xeda21, to: 0x0}, + 160: {region: 0x464d, code: 0xfc, from: 0xf3021, to: 0x0}, + 161: {region: 0x464d, code: 0x85, from: 0xef543, to: 0xf3021}, + 162: {region: 0x464f, code: 0x52, from: 0xf3821, to: 0x0}, + 163: {region: 0x4652, code: 0x5e, from: 0xf9e21, to: 0x0}, + 164: {region: 0x4652, code: 0x62, from: 0xf5021, to: 0xfa451}, + 165: {region: 0x4741, code: 0x109, from: 0xf9221, to: 0x0}, + 166: {region: 0x4742, code: 0x63, from: 0xd3cfb, to: 0x0}, + 167: {region: 0x4744, code: 0x110, from: 0xf5e5b, to: 0x0}, + 168: {region: 0x4745, code: 0x65, from: 0xf9737, to: 0x0}, + 169: {region: 0x4745, code: 0x64, from: 0xf9285, to: 0xf9739}, + 170: {region: 0x4745, code: 0xd3, from: 0xf8f99, to: 0xf92cb}, + 171: {region: 0x4745, code: 0xe7, from: 0xf5221, to: 0xf8f99}, + 172: {region: 0x4746, code: 0x5e, from: 0xf9e21, to: 0x0}, + 173: {region: 0x4746, code: 0x62, from: 0xf5021, to: 0xfa451}, + 174: {region: 0x4747, code: 0x63, from: 0xe4c21, to: 0x0}, + 175: {region: 0x4748, code: 0x67, from: 0xfaee3, to: 0x0}, + 176: {region: 0x4748, code: 0x66, from: 0xf7669, to: 0xfaf9f}, + 177: {region: 0x4749, code: 0x68, from: 0xd6221, to: 0x0}, + 178: {region: 0x474c, code: 0x52, from: 0xea2bb, to: 0x0}, + 179: {region: 0x474d, code: 0x69, from: 0xf66e1, to: 0x0}, + 180: {region: 0x474e, code: 0x6a, from: 0xf8426, to: 0x0}, + 181: {region: 0x474e, code: 0x6b, from: 0xf6942, to: 0xf8426}, + 182: {region: 0x4750, code: 0x5e, from: 0xf9e21, to: 0x0}, + 183: {region: 0x4750, code: 0x62, from: 0xf5021, to: 0xfa451}, + 184: {region: 0x4751, code: 0x109, from: 0xf9221, to: 0x0}, + 185: {region: 0x4751, code: 0x6c, from: 0xf6ee7, to: 0xf84c1}, + 186: {region: 0x4752, code: 0x5e, from: 0xfa221, to: 0x0}, + 187: {region: 0x4752, code: 0x6d, from: 0xf44a1, to: 0xfa45c}, + 188: {region: 0x4753, code: 0x63, from: 0xee821, to: 0x0}, + 189: {region: 0x4754, code: 0x6e, from: 0xf0abb, to: 0x0}, + 190: {region: 0x4755, code: 0xfc, from: 0xf3115, to: 0x0}, + 191: {region: 0x4757, code: 0x115, from: 0xf9a7f, to: 0x0}, + 192: {region: 0x4757, code: 0x70, from: 0xf705c, to: 0xf9a7f}, + 193: {region: 0x4757, code: 0x6f, from: 0xef421, to: 0xf705c}, + 194: {region: 0x4759, code: 0x71, from: 0xf5cba, to: 0x0}, + 195: {region: 0x484b, code: 0x72, from: 0xece42, to: 0x0}, + 196: {region: 0x484d, code: 0x13, from: 0xf5e50, to: 0x0}, + 197: {region: 0x484e, code: 0x73, from: 0xf0c83, to: 0x0}, + 198: {region: 0x4852, code: 0x75, from: 0xf94be, to: 0x0}, + 199: {region: 0x4852, code: 0x74, from: 0xf8f97, to: 0xf9621}, + 200: {region: 0x4852, code: 0x122, from: 0xf8c21, to: 0xf8f97}, + 201: {region: 0x4852, code: 0x120, from: 0xf5c21, to: 0xf8c21}, + 202: {region: 0x4854, code: 0x76, from: 0xea11a, to: 0x0}, + 203: {region: 0x4854, code: 0xfc, from: 0xef621, to: 0x0}, + 204: {region: 0x4855, code: 0x77, from: 0xf34f7, to: 0x0}, + 205: {region: 0x4943, code: 0x5e, from: 0xf9e21, to: 0x0}, + 206: {region: 0x4944, code: 0x78, from: 0xf5b8d, to: 0x0}, + 207: {region: 0x4945, code: 0x5e, from: 0xf9e21, to: 0x0}, + 208: {region: 0x4945, code: 0x79, from: 0xf0421, to: 0xfa449}, + 209: {region: 0x4945, code: 0x63, from: 0xe1021, to: 0xf0421}, + 210: {region: 0x494c, code: 0x7c, from: 0xf8324, to: 0x0}, + 211: {region: 0x494c, code: 0x7b, from: 0xf7856, to: 0xf8324}, + 212: {region: 0x494c, code: 0x7a, from: 0xf3910, to: 0xf7856}, + 213: {region: 0x494d, code: 0x63, from: 0xe6023, to: 0x0}, + 214: {region: 0x494e, code: 0x7d, from: 0xe5711, to: 0x0}, + 215: {region: 0x494f, code: 0xfc, from: 0xf5b68, to: 0x0}, + 216: {region: 0x4951, code: 0x7e, from: 0xf1693, to: 0x0}, + 217: {region: 0x4951, code: 0x58, from: 0xf016b, to: 0xf1693}, + 218: {region: 0x4951, code: 0x7d, from: 0xf016b, to: 0xf1693}, + 219: {region: 0x4952, code: 0x7f, from: 0xf18ad, to: 0x0}, + 220: {region: 0x4953, code: 0x81, from: 0xf7a21, to: 0x0}, + 221: {region: 0x4953, code: 0x80, from: 0xefd81, to: 0xf7a21}, + 222: {region: 0x4953, code: 0x52, from: 0xea2bb, to: 0xefd81}, + 223: {region: 0x4954, code: 0x5e, from: 0xf9e21, to: 0x0}, + 224: {region: 0x4954, code: 0x82, from: 0xe8d18, to: 0xfa45c}, + 225: {region: 0x4a45, code: 0x63, from: 0xe5a21, to: 0x0}, + 226: {region: 0x4a4d, code: 0x83, from: 0xf6328, to: 0x0}, + 227: {region: 0x4a4f, code: 0x84, from: 0xf3ce1, to: 0x0}, + 228: {region: 0x4a50, code: 0x85, from: 0xe9ec1, to: 0x0}, + 229: {region: 0x4b45, code: 0x86, from: 0xf5d2e, to: 0x0}, + 230: {region: 0x4b47, code: 0x87, from: 0xf92aa, to: 0x0}, + 231: {region: 0x4b47, code: 0xd3, from: 0xf8f99, to: 0xf92aa}, + 232: {region: 0x4b47, code: 0xe7, from: 0xf5221, to: 0xf8f99}, + 233: {region: 0x4b48, code: 0x88, from: 0xf7874, to: 0x0}, + 234: {region: 0x4b49, code: 0x13, from: 0xf5c4e, to: 0x0}, + 235: {region: 0x4b4d, code: 0x89, from: 0xf6ee6, to: 0x0}, + 236: {region: 0x4b4e, code: 0x110, from: 0xf5b46, to: 0x0}, + 237: {region: 0x4b50, code: 0x8a, from: 0xf4e91, to: 0x0}, + 238: {region: 0x4b52, code: 0x8d, from: 0xf54ca, to: 0x0}, + 239: {region: 0x4b52, code: 0x8b, from: 0xf424f, to: 0xf54ca}, + 240: {region: 0x4b52, code: 0x8c, from: 0xf330f, to: 0xf424f}, + 241: {region: 0x4b57, code: 0x8e, from: 0xf5281, to: 0x0}, + 242: {region: 0x4b59, code: 0x8f, from: 0xf6621, to: 0x0}, + 243: {region: 0x4b59, code: 0x83, from: 0xf6328, to: 0xf6621}, + 244: {region: 0x4b5a, code: 0x90, from: 0xf9365, to: 0x0}, + 245: {region: 0x4c41, code: 0x91, from: 0xf778a, to: 0x0}, + 246: {region: 0x4c42, code: 0x92, from: 0xf3842, to: 0x0}, + 247: {region: 0x4c43, code: 0x110, from: 0xf5b46, to: 0x0}, + 248: {region: 0x4c49, code: 0x3d, from: 0xf0241, to: 0x0}, + 249: {region: 0x4c4b, code: 0x93, from: 0xf74b6, to: 0x0}, + 250: {region: 0x4c52, code: 0x94, from: 0xf3021, to: 0x0}, + 251: {region: 0x4c53, code: 0x125, from: 0xf524e, to: 0x0}, + 252: {region: 0x4c53, code: 0x95, from: 0xf7836, to: 0x0}, + 253: {region: 0x4c54, code: 0x5e, from: 0xfbe21, to: 0x0}, + 254: {region: 0x4c54, code: 0x96, from: 0xf92d9, to: 0xfbd9f}, + 255: {region: 0x4c54, code: 0x97, from: 0xf9141, to: 0xf92d9}, + 256: {region: 0x4c54, code: 0xe7, from: 0xf5221, to: 0xf9141}, + 257: {region: 0x4c55, code: 0x5e, from: 0xf9e21, to: 0x0}, + 258: {region: 0x4c55, code: 0x99, from: 0xf3124, to: 0xfa45c}, + 259: {region: 0x4c55, code: 0x8098, from: 0xf6421, to: 0xf8c65}, + 260: {region: 0x4c55, code: 0x809a, from: 0xf6421, to: 0xf8c65}, + 261: {region: 0x4c56, code: 0x5e, from: 0xfbc21, to: 0x0}, + 262: {region: 0x4c56, code: 0x9b, from: 0xf92dc, to: 0xfbb9f}, + 263: {region: 0x4c56, code: 0x9c, from: 0xf90a7, to: 0xf9351}, + 264: {region: 0x4c56, code: 0xe7, from: 0xf5221, to: 0xf90f4}, + 265: {region: 0x4c59, code: 0x9d, from: 0xf6721, to: 0x0}, + 266: {region: 0x4d41, code: 0x9e, from: 0xf4f51, to: 0x0}, + 267: {region: 0x4d41, code: 0x9f, from: 0xeb221, to: 0xf4f51}, + 268: {region: 0x4d43, code: 0x5e, from: 0xf9e21, to: 0x0}, + 269: {region: 0x4d43, code: 0x62, from: 0xf5021, to: 0xfa451}, + 270: {region: 0x4d43, code: 0xa0, from: 0xf5021, to: 0xfa451}, + 271: {region: 0x4d44, code: 0xa2, from: 0xf937d, to: 0x0}, + 272: {region: 0x4d44, code: 0xa1, from: 0xf90c1, to: 0xf937d}, + 273: {region: 0x4d45, code: 0x5e, from: 0xfa421, to: 0x0}, + 274: {region: 0x4d45, code: 0x50, from: 0xf9f42, to: 0xfa4af}, + 275: {region: 0x4d45, code: 0x121, from: 0xf9438, to: 0xfa4af}, + 276: {region: 0x4d46, code: 0x5e, from: 0xf9e21, to: 0x0}, + 277: {region: 0x4d46, code: 0x62, from: 0xf5021, to: 0xfa451}, + 278: {region: 0x4d47, code: 0xa3, from: 0xf7f61, to: 0x0}, + 279: {region: 0x4d47, code: 0xa4, from: 0xf56e1, to: 0xfa99f}, + 280: {region: 0x4d48, code: 0xfc, from: 0xf3021, to: 0x0}, + 281: {region: 0x4d4b, code: 0xa5, from: 0xf92b4, to: 0x0}, + 282: {region: 0x4d4b, code: 0xa6, from: 0xf909a, to: 0xf92b4}, + 283: {region: 0x4d4c, code: 0x115, from: 0xf80c1, to: 0x0}, + 284: {region: 0x4d4c, code: 0xa7, from: 0xf54e2, to: 0xf811f}, + 285: {region: 0x4d4c, code: 0x115, from: 0xf4d78, to: 0xf54e2}, + 286: {region: 0x4d4d, code: 0xa8, from: 0xf8ad2, to: 0x0}, + 287: {region: 0x4d4d, code: 0x34, from: 0xf40e1, to: 0xf8ad2}, + 288: {region: 0x4d4e, code: 0xa9, from: 0xef661, to: 0x0}, + 289: {region: 0x4d4f, code: 0xaa, from: 0xeda21, to: 0x0}, + 290: {region: 0x4d50, code: 0xfc, from: 0xf3021, to: 0x0}, + 291: {region: 0x4d51, code: 0x5e, from: 0xf9e21, to: 0x0}, + 292: {region: 0x4d51, code: 0x62, from: 0xf5021, to: 0xfa451}, + 293: {region: 0x4d52, code: 0xab, from: 0xf6add, to: 0x0}, + 294: {region: 0x4d52, code: 0x115, from: 0xf4d7c, to: 0xf6add}, + 295: {region: 0x4d53, code: 0x110, from: 0xf5e5b, to: 0x0}, + 296: {region: 0x4d54, code: 0x5e, from: 0xfb021, to: 0x0}, + 297: {region: 0x4d54, code: 0xac, from: 0xf60c7, to: 0xfb03f}, + 298: {region: 0x4d54, code: 0xad, from: 0xef50d, to: 0xf60c7}, + 299: {region: 0x4d55, code: 0xae, from: 0xf1c81, to: 0x0}, + 300: {region: 0x4d56, code: 0xb0, from: 0xf7ae1, to: 0x0}, + 301: {region: 0x4d57, code: 0xb1, from: 0xf664f, to: 0x0}, + 302: {region: 0x4d58, code: 0xb2, from: 0xf9221, to: 0x0}, + 303: {region: 0x4d58, code: 0xb3, from: 0xe3c21, to: 0xf919f}, + 304: {region: 0x4d58, code: 0x80b4, from: 0x0, to: 0x0}, + 305: {region: 0x4d59, code: 0xb5, from: 0xf5730, to: 0x0}, + 306: {region: 0x4d5a, code: 0xb8, from: 0xface1, to: 0x0}, + 307: {region: 0x4d5a, code: 0xb7, from: 0xf78d0, to: 0xfad9f}, + 308: {region: 0x4d5a, code: 0xb6, from: 0xf6ed9, to: 0xf78d0}, + 309: {region: 0x4e41, code: 0xb9, from: 0xf9221, to: 0x0}, + 310: {region: 0x4e41, code: 0x125, from: 0xf524e, to: 0x0}, + 311: {region: 0x4e43, code: 0x117, from: 0xf8221, to: 0x0}, + 312: {region: 0x4e45, code: 0x115, from: 0xf4d93, to: 0x0}, + 313: {region: 0x4e46, code: 0x13, from: 0xf5c4e, to: 0x0}, + 314: {region: 0x4e47, code: 0xba, from: 0xf6a21, to: 0x0}, + 315: {region: 0x4e49, code: 0xbc, from: 0xf8e9e, to: 0x0}, + 316: {region: 0x4e49, code: 0xbb, from: 0xf884f, to: 0xf8e9e}, + 317: {region: 0x4e4c, code: 0x5e, from: 0xf9e21, to: 0x0}, + 318: {region: 0x4e4c, code: 0xbd, from: 0xe2a21, to: 0xfa45c}, + 319: {region: 0x4e4f, code: 0xbe, from: 0xee2c7, to: 0x0}, + 320: {region: 0x4e4f, code: 0xdb, from: 0xea2bb, to: 0xee2c7}, + 321: {region: 0x4e50, code: 0xbf, from: 0xf1a21, to: 0x0}, + 322: {region: 0x4e50, code: 0x7d, from: 0xe9c21, to: 0xf5d51}, + 323: {region: 0x4e52, code: 0x13, from: 0xf5c4e, to: 0x0}, + 324: {region: 0x4e55, code: 0xc0, from: 0xf5eea, to: 0x0}, + 325: {region: 0x4e5a, code: 0xc0, from: 0xf5eea, to: 0x0}, + 326: {region: 0x4f4d, code: 0xc1, from: 0xf696b, to: 0x0}, + 327: {region: 0x5041, code: 0xc2, from: 0xedf64, to: 0x0}, + 328: {region: 0x5041, code: 0xfc, from: 0xedf72, to: 0x0}, + 329: {region: 0x5045, code: 0xc4, from: 0xf8ee1, to: 0x0}, + 330: {region: 0x5045, code: 0xc3, from: 0xf8241, to: 0xf8ee1}, + 331: {region: 0x5045, code: 0xc5, from: 0xe8e4e, to: 0xf8241}, + 332: {region: 0x5046, code: 0x117, from: 0xf339a, to: 0x0}, + 333: {region: 0x5047, code: 0xc6, from: 0xf6f30, to: 0x0}, + 334: {region: 0x5047, code: 0x13, from: 0xf5c4e, to: 0xf6f30}, + 335: {region: 0x5048, code: 0xc7, from: 0xf34e4, to: 0x0}, + 336: {region: 0x504b, code: 0xc8, from: 0xf3881, to: 0x0}, + 337: {region: 0x504b, code: 0x7d, from: 0xe5711, to: 0xf370f}, + 338: {region: 0x504c, code: 0xc9, from: 0xf9621, to: 0x0}, + 339: {region: 0x504c, code: 0xca, from: 0xf3d5c, to: 0xf959f}, + 340: {region: 0x504d, code: 0x5e, from: 0xf9e21, to: 0x0}, + 341: {region: 0x504d, code: 0x62, from: 0xf6995, to: 0xfa451}, + 342: {region: 0x504e, code: 0xc0, from: 0xf622d, to: 0x0}, + 343: {region: 0x5052, code: 0xfc, from: 0xed58a, to: 0x0}, + 344: {region: 0x5052, code: 0x5c, from: 0xe1021, to: 0xed58a}, + 345: {region: 0x5053, code: 0x7c, from: 0xf8324, to: 0x0}, + 346: {region: 0x5053, code: 0x84, from: 0xf984c, to: 0x0}, + 347: {region: 0x5053, code: 0x7a, from: 0xf5ec1, to: 0xf7856}, + 348: {region: 0x5053, code: 0x84, from: 0xf3ce1, to: 0xf5ec1}, + 349: {region: 0x5054, code: 0x5e, from: 0xf9e21, to: 0x0}, + 350: {region: 0x5054, code: 0xcb, from: 0xeeeb6, to: 0xfa45c}, + 351: {region: 0x5057, code: 0xfc, from: 0xf3021, to: 0x0}, + 352: {region: 0x5059, code: 0xcc, from: 0xf2f61, to: 0x0}, + 353: {region: 0x5141, code: 0xcd, from: 0xf6ab3, to: 0x0}, + 354: {region: 0x5245, code: 0x5e, from: 0xf9e21, to: 0x0}, + 355: {region: 0x5245, code: 0x62, from: 0xf6e21, to: 0xfa451}, + 356: {region: 0x524f, code: 0xd0, from: 0xfaae1, to: 0x0}, + 357: {region: 0x524f, code: 0xcf, from: 0xf403c, to: 0xfad9f}, + 358: {region: 0x5253, code: 0xd1, from: 0xfad59, to: 0x0}, + 359: {region: 0x5253, code: 0x48, from: 0xfa4af, to: 0xfad59}, + 360: {region: 0x5253, code: 0x121, from: 0xf9438, to: 0xfa4af}, + 361: {region: 0x5255, code: 0xd2, from: 0xf9e21, to: 0x0}, + 362: {region: 0x5255, code: 0xd3, from: 0xf8f99, to: 0xf9d9f}, + 363: {region: 0x5257, code: 0xd4, from: 0xf58b3, to: 0x0}, + 364: {region: 0x5341, code: 0xd5, from: 0xf4156, to: 0x0}, + 365: {region: 0x5342, code: 0xd6, from: 0xf7358, to: 0x0}, + 366: {region: 0x5342, code: 0x13, from: 0xf5c4e, to: 0xf74de}, + 367: {region: 0x5343, code: 0xd7, from: 0xedf61, to: 0x0}, + 368: {region: 0x5344, code: 0xd9, from: 0xfae2a, to: 0x0}, + 369: {region: 0x5344, code: 0xd8, from: 0xf90c8, to: 0xfaede}, + 370: {region: 0x5344, code: 0xda, from: 0xf4a88, to: 0xf9cc1}, + 371: {region: 0x5344, code: 0x58, from: 0xec233, to: 0xf4c21}, + 372: {region: 0x5344, code: 0x63, from: 0xec233, to: 0xf4c21}, + 373: {region: 0x5345, code: 0xdb, from: 0xea2bb, to: 0x0}, + 374: {region: 0x5347, code: 0xdc, from: 0xf5ecc, to: 0x0}, + 375: {region: 0x5347, code: 0xb5, from: 0xf5730, to: 0xf5ecc}, + 376: {region: 0x5348, code: 0xdd, from: 0xefa4f, to: 0x0}, + 377: {region: 0x5349, code: 0x5e, from: 0xfae21, to: 0x0}, + 378: {region: 0x5349, code: 0xde, from: 0xf9147, to: 0xfae2e}, + 379: {region: 0x534a, code: 0xbe, from: 0xee2c7, to: 0x0}, + 380: {region: 0x534b, code: 0x5e, from: 0xfb221, to: 0x0}, + 381: {region: 0x534b, code: 0xdf, from: 0xf919f, to: 0xfb221}, + 382: {region: 0x534b, code: 0x49, from: 0xf42c1, to: 0xf919f}, + 383: {region: 0x534c, code: 0xe0, from: 0xf5904, to: 0x0}, + 384: {region: 0x534c, code: 0x63, from: 0xe217e, to: 0xf5c44}, + 385: {region: 0x534d, code: 0x5e, from: 0xf9e21, to: 0x0}, + 386: {region: 0x534d, code: 0x82, from: 0xe9397, to: 0xfa25c}, + 387: {region: 0x534e, code: 0x115, from: 0xf4e84, to: 0x0}, + 388: {region: 0x534f, code: 0xe1, from: 0xf50e1, to: 0x0}, + 389: {region: 0x5352, code: 0xe2, from: 0xfa821, to: 0x0}, + 390: {region: 0x5352, code: 0xe3, from: 0xf28aa, to: 0xfa79f}, + 391: {region: 0x5352, code: 0xbd, from: 0xe2f74, to: 0xf28aa}, + 392: {region: 0x5353, code: 0xe4, from: 0xfb6f2, to: 0x0}, + 393: {region: 0x5353, code: 0xd9, from: 0xfae2a, to: 0xfb721}, + 394: {region: 0x5354, code: 0xe6, from: 0xfc421, to: 0x0}, + 395: {region: 0x5354, code: 0xe5, from: 0xf7328, to: 0xfc39f}, + 396: {region: 0x5355, code: 0xe7, from: 0xf5221, to: 0xf8f99}, + 397: {region: 0x5356, code: 0xfc, from: 0xfa221, to: 0x0}, + 398: {region: 0x5356, code: 0xe8, from: 0xeff6b, to: 0xfa221}, + 399: {region: 0x5358, code: 0x8, from: 0xfb54a, to: 0x0}, + 400: {region: 0x5359, code: 0xe9, from: 0xf3821, to: 0x0}, + 401: {region: 0x535a, code: 0xea, from: 0xf6d26, to: 0x0}, + 402: {region: 0x5441, code: 0x63, from: 0xf242c, to: 0x0}, + 403: {region: 0x5443, code: 0xfc, from: 0xf6328, to: 0x0}, + 404: {region: 0x5444, code: 0x109, from: 0xf9221, to: 0x0}, + 405: {region: 0x5446, code: 0x5e, from: 0xf9e21, to: 0x0}, + 406: {region: 0x5446, code: 0x62, from: 0xf4e21, to: 0xfa451}, + 407: {region: 0x5447, code: 0x115, from: 0xf4d7c, to: 0x0}, + 408: {region: 0x5448, code: 0xeb, from: 0xf108f, to: 0x0}, + 409: {region: 0x544a, code: 0xed, from: 0xfa15a, to: 0x0}, + 410: {region: 0x544a, code: 0xec, from: 0xf96aa, to: 0xfa159}, + 411: {region: 0x544a, code: 0xd3, from: 0xf8f99, to: 0xf96aa}, + 412: {region: 0x544b, code: 0xc0, from: 0xf5eea, to: 0x0}, + 413: {region: 0x544c, code: 0xfc, from: 0xf9f54, to: 0x0}, + 414: {region: 0x544c, code: 0xf2, from: 0xf4e22, to: 0xfa4b4}, + 415: {region: 0x544c, code: 0x78, from: 0xf6f87, to: 0xfa4b4}, + 416: {region: 0x544d, code: 0xef, from: 0xfb221, to: 0x0}, + 417: {region: 0x544d, code: 0xee, from: 0xf9361, to: 0xfb221}, + 418: {region: 0x544d, code: 0xd3, from: 0xf8f99, to: 0xf9361}, + 419: {region: 0x544d, code: 0xe7, from: 0xf5221, to: 0xf8f99}, + 420: {region: 0x544e, code: 0xf0, from: 0xf4d61, to: 0x0}, + 421: {region: 0x544f, code: 0xf1, from: 0xf5c4e, to: 0x0}, + 422: {region: 0x5450, code: 0xf2, from: 0xf4e22, to: 0xfa4b4}, + 423: {region: 0x5450, code: 0x78, from: 0xf6f87, to: 0xfa4b4}, + 424: {region: 0x5452, code: 0xf4, from: 0xfaa21, to: 0x0}, + 425: {region: 0x5452, code: 0xf3, from: 0xf0561, to: 0xfab9f}, + 426: {region: 0x5454, code: 0xf5, from: 0xf5821, to: 0x0}, + 427: {region: 0x5456, code: 0x13, from: 0xf5c4e, to: 0x0}, + 428: {region: 0x5457, code: 0xf6, from: 0xf3acf, to: 0x0}, + 429: {region: 0x545a, code: 0xf7, from: 0xf5cce, to: 0x0}, + 430: {region: 0x5541, code: 0xf8, from: 0xf9922, to: 0x0}, + 431: {region: 0x5541, code: 0xf9, from: 0xf916d, to: 0xf9351}, + 432: {region: 0x5541, code: 0xd3, from: 0xf8f99, to: 0xf916d}, + 433: {region: 0x5541, code: 0xe7, from: 0xf5221, to: 0xf8f99}, + 434: {region: 0x5547, code: 0xfb, from: 0xf86af, to: 0x0}, + 435: {region: 0x5547, code: 0xfa, from: 0xf5d0f, to: 0xf86af}, + 436: {region: 0x554d, code: 0xfc, from: 0xf3021, to: 0x0}, + 437: {region: 0x5553, code: 0xfc, from: 0xe0021, to: 0x0}, + 438: {region: 0x5553, code: 0x80fd, from: 0x0, to: 0x0}, + 439: {region: 0x5553, code: 0x80fe, from: 0x0, to: 0xfbc61}, + 440: {region: 0x5559, code: 0x101, from: 0xf9261, to: 0x0}, + 441: {region: 0x5559, code: 0x100, from: 0xf6ee1, to: 0xf9261}, + 442: {region: 0x5559, code: 0x80ff, from: 0x0, to: 0x0}, + 443: {region: 0x555a, code: 0x102, from: 0xf94e1, to: 0x0}, + 444: {region: 0x5641, code: 0x5e, from: 0xf9e21, to: 0x0}, + 445: {region: 0x5641, code: 0x82, from: 0xe9d53, to: 0xfa45c}, + 446: {region: 0x5643, code: 0x110, from: 0xf5b46, to: 0x0}, + 447: {region: 0x5645, code: 0x104, from: 0xfb021, to: 0x0}, + 448: {region: 0x5645, code: 0x103, from: 0xe9eab, to: 0xfb0de}, + 449: {region: 0x5647, code: 0xfc, from: 0xe5221, to: 0x0}, + 450: {region: 0x5647, code: 0x63, from: 0xe5221, to: 0xf4e21}, + 451: {region: 0x5649, code: 0xfc, from: 0xe5a21, to: 0x0}, + 452: {region: 0x564e, code: 0x105, from: 0xf832e, to: 0x0}, + 453: {region: 0x564e, code: 0x106, from: 0xf74a3, to: 0xf832e}, + 454: {region: 0x5655, code: 0x107, from: 0xf7a21, to: 0x0}, + 455: {region: 0x5746, code: 0x117, from: 0xf52fe, to: 0x0}, + 456: {region: 0x5753, code: 0x108, from: 0xf5eea, to: 0x0}, + 457: {region: 0x584b, code: 0x5e, from: 0xfa421, to: 0x0}, + 458: {region: 0x584b, code: 0x50, from: 0xf9f21, to: 0xfa469}, + 459: {region: 0x584b, code: 0x121, from: 0xf9438, to: 0xf9f3e}, + 460: {region: 0x5944, code: 0x11e, from: 0xf5a81, to: 0xf9821}, + 461: {region: 0x5945, code: 0x11f, from: 0xf8cb6, to: 0x0}, + 462: {region: 0x5954, code: 0x5e, from: 0xf9e21, to: 0x0}, + 463: {region: 0x5954, code: 0x62, from: 0xf7057, to: 0xfa451}, + 464: {region: 0x5954, code: 0x89, from: 0xf6e21, to: 0xf7057}, + 465: {region: 0x5955, code: 0x121, from: 0xf9438, to: 0xfa4af}, + 466: {region: 0x5955, code: 0x122, from: 0xf8c21, to: 0xf90f8}, + 467: {region: 0x5955, code: 0x120, from: 0xf5c21, to: 0xf8c21}, + 468: {region: 0x5a41, code: 0x125, from: 0xf524e, to: 0x0}, + 469: {region: 0x5a41, code: 0x8124, from: 0xf8321, to: 0xf966d}, + 470: {region: 0x5a4d, code: 0x127, from: 0xfba21, to: 0x0}, + 471: {region: 0x5a4d, code: 0x126, from: 0xf6030, to: 0xfba21}, + 472: {region: 0x5a52, code: 0x128, from: 0xf9361, to: 0xf9cff}, + 473: {region: 0x5a52, code: 0x129, from: 0xf675b, to: 0xf9361}, + 474: {region: 0x5a57, code: 0xfc, from: 0xfb28c, to: 0x0}, + 475: {region: 0x5a57, code: 0x12b, from: 0xfb242, to: 0xfb28c}, + 476: {region: 0x5a57, code: 0x12c, from: 0xfb101, to: 0xfb242}, + 477: {region: 0x5a57, code: 0x12a, from: 0xf7892, to: 0xfb101}, + 478: {region: 0x5a57, code: 0xce, from: 0xf6451, to: 0xf7892}, + 479: {region: 0x5a5a, code: 0x810a, from: 0x0, to: 0x0}, + 480: {region: 0x5a5a, code: 0x810b, from: 0x0, to: 0x0}, + 481: {region: 0x5a5a, code: 0x810c, from: 0x0, to: 0x0}, + 482: {region: 0x5a5a, code: 0x810d, from: 0x0, to: 0x0}, + 483: {region: 0x5a5a, code: 0x810e, from: 0x0, to: 0x0}, + 484: {region: 0x5a5a, code: 0x810f, from: 0x0, to: 0x0}, + 485: {region: 0x5a5a, code: 0x8111, from: 0x0, to: 0x0}, + 486: {region: 0x5a5a, code: 0x8113, from: 0xf1421, to: 0xfa681}, + 487: {region: 0x5a5a, code: 0x8114, from: 0x0, to: 0xfbb7e}, + 488: {region: 0x5a5a, code: 0x8116, from: 0x0, to: 0x0}, + 489: {region: 0x5a5a, code: 0x8118, from: 0x0, to: 0x0}, + 490: {region: 0x5a5a, code: 0x8119, from: 0x0, to: 0xf9f7e}, + 491: {region: 0x5a5a, code: 0x811a, from: 0x0, to: 0x0}, + 492: {region: 0x5a5a, code: 0x811b, from: 0x0, to: 0x0}, + 493: {region: 0x5a5a, code: 0x811c, from: 0x0, to: 0x0}, + 494: {region: 0x5a5a, code: 0x811d, from: 0x0, to: 0x0}, +} // Size: 5964 bytes + +// symbols holds symbol data of the form , where n is the length of +// the symbol string str. +const symbols string = "" + // Size: 1445 bytes + "\x00\x02Kz\x01$\x02A$\x02KM\x03৳\x02Bs\x02R$\x01P\x03р.\x03CA$\x04CN¥" + + "\x02¥\x03₡\x03Kč\x02kr\x03E£\x03₧\x03€\x02£\x03₾\x02FG\x01Q\x03HK$\x01L" + + "\x02kn\x02Ft\x02Rp\x03₪\x03₹\x04JP¥\x03៛\x02CF\x03₩\x03₸\x03₭\x03L£\x02R" + + "s\x02Lt\x02Ls\x02Ar\x01K\x03₮\x03MX$\x02RM\x03₦\x02C$\x03NZ$\x03₱\x03zł" + + "\x03₲\x03lei\x03₽\x02RF\x02Db\x03฿\x02T$\x03₺\x03NT$\x03₴\x03US$\x03₫" + + "\x04FCFA\x03EC$\x03CFA\x04CFPF\x01R\x02ZK\x03leu\x05GH₵\x03AU$\x16የቻይና ዩ" + + "ዋን\x06ብር\x03***\x09د.إ.\u200f\x03AR$\x03BB$\x09د.ب.\u200f\x03BM$\x03BN" + + "$\x03BS$\x03BZ$\x03CL$\x03CO$\x03CU$\x03DO$\x09د.ج.\u200f\x09ج.م.\u200f" + + "\x03FJ$\x04UK£\x03GY$\x09د.ع.\u200f\x06ر.إ.\x03JM$\x09د.أ.\u200f\x09د.ك." + + "\u200f\x03KY$\x09ل.ل.\u200f\x09د.ل.\u200f\x09د.م.\u200f\x09أ.م.\u200f" + + "\x09ر.ع.\u200f\x09ر.ق.\u200f\x09ر.س.\u200f\x03SB$\x09د.س.\u200f\x06ج.س." + + "\x03SR$\x09ل.س.\u200f\x09د.ت.\u200f\x03TT$\x03UY$\x09ر.ي.\u200f\x03Fdj" + + "\x03Nfk\x01S\x04GB£\x03TSh\x03₼\x03ley\x03S£\x04Bds$\x03BD$\x02B$\x02Br" + + "\x04CUC$\x03$MN\x03RD$\x04FK£\x02G$\x04Íkr\x02J$\x03CI$\x02L$\x02N$\x07р" + + "уб.\x03SI$\x02S$\x02$U\x05лв.\x06щ.д.\x02$A\x03$CA\x04£ E\x05£ RU\x04$ " + + "HK\x03£L\x04$ ZN\x03$ T\x04$ SU\x04din.\x04КМ\x04Кч\x04зл\x07дин.\x04Тл" + + "\x01F\x06лей\x03USh\x04Kčs\x03ECU\x02TK\x03kr.\x03Ksh\x03öS\x03BGK\x03BG" + + "J\x04Cub$\x02DM\x04Fl£\x04F.G.\x02FC\x04F.Rw\x03Nu.\x05KR₩\x05TH฿\x06Δρχ" + + "\x02Tk\x02$b\x02Kr\x02Gs\x03CFP\x03FBu\x01D\x04MOP$\x02MK\x02SR\x02Le" + + "\x04NAf.\x01E\x02VT\x03WS$\x04SD£\x03BsF\x02p.\x03B/.\x02S/\x03Gs.\x03Bs" + + ".\x02؋\x04¥CN\x03$HK\x08ریال\x03$MX\x03$NZ\x03$EC\x02UM\x02mk\x03$AR\x03" + + "$AU\x02FB\x03$BM\x03$BN\x03$BS\x03$BZ\x03$CL\x03$CO\x04£CY\x03£E\x03$FJ" + + "\x04£FK\x04£GB\x04£GI\x04£IE\x04£IL\x05₤IT\x04£LB\x04£MT\x03$NA\x02$C" + + "\x03$RH\x02FR\x03$SB\x03$SG\x03$SR\x03$TT\x03$US\x03$UY\x04FCFP\x02Kw" + + "\x05$\u00a0AU\x05$\u00a0HK\x05$\u00a0NZ\x05$\u00a0SG\x05$\u00a0US\x02DA" + + "\x01G\x02LS\x02DT\x06руб\x07રૂ.\x0a\u200eCN¥\u200e\x06ל״י\x09लेई\x02֏" + + "\x03NKr\x03元\x03¥\x06レイ\x03\u200b\x06ಲೀ\x02LE\x02Kn\x06сом\x02zl\x02rb" + + "\x03MTn\x06ден\x04кр\x03NAf\x03Afl\x0cनेरू\x06रू\x04Afl.\x02ر\x03lej\x04" + + "Esc.\x06\u200bPTE\x04XXXX\x03ლ\x06ТМТ\x03Dkr\x03Skr\x03Nkr\x07රු.\x0fසිෆ" + + "්එ\x03NIS\x05Lekë\x03den\x02r.\x03BR$\x03Ekr\x04EG£\x04IE£\x03Ikr\x03R" + + "s.\x07сом.\x04AUD$\x04NZD$\x07крб.\x05soʻm\x06сўм\x03₩\x03ILS\x02P.\x03Z" + + "ł" + +type curToIndex struct { + cur uint16 + idx uint16 +} + +var normalLangIndex = []uint16{ // 776 elements + // Entry 0 - 3F + 0x0000, 0x0014, 0x0017, 0x0018, 0x0018, 0x0018, 0x0018, 0x0019, + 0x0019, 0x001d, 0x001d, 0x0034, 0x0034, 0x0034, 0x0034, 0x0035, + 0x0035, 0x0035, 0x0035, 0x0036, 0x0036, 0x0036, 0x0036, 0x0037, + 0x0037, 0x0038, 0x0038, 0x0038, 0x0038, 0x0038, 0x0038, 0x0038, + 0x0038, 0x0038, 0x0039, 0x003b, 0x003b, 0x003b, 0x003b, 0x003b, + 0x003b, 0x003b, 0x003b, 0x003c, 0x003c, 0x003f, 0x003f, 0x0041, + 0x0042, 0x0042, 0x0042, 0x0042, 0x0042, 0x0042, 0x0049, 0x0049, + 0x004a, 0x004a, 0x004b, 0x004b, 0x005c, 0x005c, 0x005c, 0x005c, + // Entry 40 - 7F + 0x005c, 0x005e, 0x005e, 0x005e, 0x005f, 0x005f, 0x0060, 0x006e, + 0x006e, 0x006e, 0x006e, 0x007f, 0x0085, 0x0085, 0x0085, 0x0085, + 0x008e, 0x008e, 0x008e, 0x008f, 0x008f, 0x0091, 0x0091, 0x0091, + 0x0092, 0x0092, 0x0093, 0x0093, 0x0094, 0x0094, 0x0095, 0x0095, + 0x0095, 0x009c, 0x009c, 0x009d, 0x009d, 0x009f, 0x009f, 0x00a3, + 0x00a3, 0x00a3, 0x00a4, 0x00a4, 0x00ac, 0x00ac, 0x00ac, 0x00ad, + 0x00ad, 0x00ad, 0x00ae, 0x00af, 0x00af, 0x00af, 0x00b4, 0x00b4, + 0x00b4, 0x00b4, 0x00b4, 0x00b4, 0x00b4, 0x00ba, 0x00ba, 0x00bb, + // Entry 80 - BF + 0x00bb, 0x00be, 0x00be, 0x00be, 0x00c1, 0x00c1, 0x00c1, 0x00c3, + 0x00c5, 0x00c5, 0x00c6, 0x00c7, 0x00c7, 0x00c7, 0x00dc, 0x00dd, + 0x00dd, 0x00de, 0x00df, 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, + 0x00e4, 0x00e5, 0x00e5, 0x00e6, 0x00e6, 0x00e6, 0x00e6, 0x00e7, + 0x00e8, 0x00e9, 0x00e9, 0x00ea, 0x00ec, 0x00ec, 0x00ec, 0x00ed, + 0x00ed, 0x00ee, 0x00f0, 0x00f1, 0x00f1, 0x00f2, 0x00f2, 0x00f2, + 0x00f2, 0x00f2, 0x00f2, 0x00f2, 0x00f2, 0x00f3, 0x00f4, 0x00f5, + 0x00f6, 0x00f7, 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fb, 0x00fc, + // Entry C0 - FF + 0x00fc, 0x00fd, 0x00fe, 0x00ff, 0x0100, 0x0101, 0x0102, 0x0103, + 0x0104, 0x0104, 0x0105, 0x0106, 0x0107, 0x0108, 0x0109, 0x010a, + 0x010b, 0x010b, 0x010b, 0x010c, 0x010d, 0x010e, 0x010e, 0x010f, + 0x0110, 0x0112, 0x0112, 0x0113, 0x0115, 0x0116, 0x0117, 0x0117, + 0x0118, 0x0119, 0x011a, 0x011b, 0x011c, 0x011d, 0x011d, 0x011d, + 0x011e, 0x011e, 0x011e, 0x011f, 0x0120, 0x0121, 0x0122, 0x0122, + 0x0122, 0x0122, 0x0133, 0x0138, 0x013a, 0x013b, 0x013c, 0x013d, + 0x013f, 0x0141, 0x0142, 0x0144, 0x0146, 0x0146, 0x0147, 0x0147, + // Entry 100 - 13F + 0x0148, 0x0149, 0x014a, 0x014a, 0x014b, 0x014c, 0x014d, 0x014e, + 0x014f, 0x0150, 0x0151, 0x0152, 0x0154, 0x0156, 0x0157, 0x015c, + 0x015c, 0x015e, 0x015e, 0x015e, 0x015e, 0x0169, 0x0169, 0x0169, + 0x0169, 0x0169, 0x016a, 0x016b, 0x016b, 0x017c, 0x017c, 0x0180, + 0x0180, 0x0181, 0x0182, 0x0182, 0x01a8, 0x01a8, 0x01a8, 0x01a9, + 0x01a9, 0x01a9, 0x01ca, 0x01cb, 0x01cb, 0x01cb, 0x01cb, 0x01cb, + 0x01cb, 0x01cc, 0x01cd, 0x01cd, 0x01cd, 0x01cd, 0x01ce, 0x01ce, + 0x01ce, 0x01cf, 0x01d0, 0x01d2, 0x01d2, 0x01d2, 0x01d2, 0x01d3, + // Entry 140 - 17F + 0x01d3, 0x01d3, 0x01d4, 0x01d5, 0x01d5, 0x01d5, 0x01d5, 0x01d5, + 0x01d5, 0x01d6, 0x01d7, 0x01d7, 0x01d8, 0x01d8, 0x01d8, 0x01d9, + 0x01da, 0x01da, 0x01da, 0x01da, 0x01da, 0x01e0, 0x01e0, 0x01e3, + 0x01e3, 0x01e5, 0x01e5, 0x01e9, 0x01e9, 0x01ec, 0x01ec, 0x01ec, + 0x01ec, 0x01ed, 0x01ed, 0x01ed, 0x01ee, 0x01ee, 0x01ee, 0x01ee, + 0x01ef, 0x01f0, 0x01f0, 0x01f0, 0x01f1, 0x01f1, 0x01f6, 0x01f6, + 0x01f8, 0x01f8, 0x020a, 0x020b, 0x020b, 0x0210, 0x0210, 0x0222, + 0x0222, 0x0225, 0x0225, 0x0229, 0x0229, 0x022a, 0x022a, 0x022b, + // Entry 180 - 1BF + 0x022b, 0x022b, 0x022b, 0x0237, 0x0237, 0x023f, 0x023f, 0x023f, + 0x023f, 0x023f, 0x023f, 0x023f, 0x0242, 0x0242, 0x0242, 0x0242, + 0x0242, 0x0242, 0x0243, 0x0243, 0x0243, 0x0243, 0x024d, 0x024d, + 0x024e, 0x024e, 0x024e, 0x024f, 0x024f, 0x024f, 0x0250, 0x0250, + 0x0253, 0x0253, 0x0253, 0x0253, 0x0254, 0x0254, 0x0258, 0x0258, + 0x0258, 0x0258, 0x0259, 0x0259, 0x025a, 0x025a, 0x025d, 0x025d, + 0x025f, 0x025f, 0x0260, 0x0260, 0x0260, 0x0260, 0x0260, 0x0260, + 0x0260, 0x0261, 0x0261, 0x0261, 0x0261, 0x0261, 0x0261, 0x0261, + // Entry 1C0 - 1FF + 0x0261, 0x0261, 0x0270, 0x0270, 0x0271, 0x0271, 0x0276, 0x0276, + 0x0277, 0x0277, 0x0278, 0x0278, 0x0279, 0x027a, 0x027a, 0x027a, + 0x027a, 0x027c, 0x027c, 0x027d, 0x027d, 0x027d, 0x0290, 0x0290, + 0x0291, 0x0291, 0x0292, 0x0292, 0x0293, 0x0293, 0x0298, 0x0298, + 0x0299, 0x0299, 0x029a, 0x029b, 0x029b, 0x029c, 0x029c, 0x029d, + 0x029d, 0x029e, 0x029e, 0x029e, 0x029e, 0x02aa, 0x02aa, 0x02ad, + 0x02ad, 0x02b0, 0x02b0, 0x02b0, 0x02b2, 0x02b2, 0x02b6, 0x02b7, + 0x02b7, 0x02b8, 0x02b8, 0x02b8, 0x02b8, 0x02b8, 0x02bf, 0x02bf, + // Entry 200 - 23F + 0x02c0, 0x02c0, 0x02c0, 0x02c1, 0x02c1, 0x02d3, 0x02d3, 0x02d3, + 0x02d3, 0x02d3, 0x02d3, 0x02d3, 0x02d3, 0x02d5, 0x02d5, 0x02d5, + 0x02db, 0x02dc, 0x02dc, 0x02dd, 0x02de, 0x02de, 0x02df, 0x02e0, + 0x02e0, 0x02e0, 0x02f3, 0x02f3, 0x02f3, 0x02f3, 0x02f3, 0x02f3, + 0x02f3, 0x02f3, 0x02f5, 0x02f5, 0x02f5, 0x02f6, 0x02f6, 0x02f7, + 0x02f7, 0x02f8, 0x02fa, 0x02fa, 0x02fc, 0x02fc, 0x02fe, 0x02ff, + 0x0300, 0x0300, 0x0300, 0x0300, 0x0300, 0x030f, 0x030f, 0x030f, + 0x030f, 0x0310, 0x0310, 0x0313, 0x0314, 0x0314, 0x0314, 0x0316, + // Entry 240 - 27F + 0x0316, 0x0316, 0x0317, 0x0318, 0x0319, 0x031a, 0x031b, 0x031b, + 0x031c, 0x031e, 0x0320, 0x0320, 0x0320, 0x0320, 0x0321, 0x0321, + 0x0332, 0x0333, 0x0333, 0x0334, 0x0334, 0x033c, 0x033e, 0x033f, + 0x0340, 0x0341, 0x0341, 0x0341, 0x0342, 0x0342, 0x0343, 0x0343, + 0x0344, 0x0344, 0x0345, 0x0345, 0x0346, 0x0346, 0x0347, 0x0347, + 0x0347, 0x034b, 0x034b, 0x034b, 0x034d, 0x034e, 0x034e, 0x034e, + 0x034e, 0x034e, 0x034e, 0x034e, 0x034e, 0x034e, 0x034e, 0x034e, + 0x034e, 0x0351, 0x0351, 0x035f, 0x035f, 0x0369, 0x0369, 0x0369, + // Entry 280 - 2BF + 0x0369, 0x0369, 0x0369, 0x0369, 0x0369, 0x0369, 0x0369, 0x036a, + 0x036b, 0x036c, 0x036d, 0x036d, 0x036f, 0x036f, 0x0370, 0x0370, + 0x0376, 0x0376, 0x0376, 0x0376, 0x0376, 0x0376, 0x037c, 0x037c, + 0x037c, 0x037c, 0x037c, 0x037c, 0x037c, 0x037c, 0x0394, 0x0394, + 0x0394, 0x0394, 0x0397, 0x0398, 0x0398, 0x0398, 0x0399, 0x0399, + 0x039c, 0x039c, 0x039d, 0x039f, 0x03a2, 0x03a4, 0x03a4, 0x03a5, + 0x03a6, 0x03a6, 0x03a8, 0x03a8, 0x03aa, 0x03aa, 0x03ab, 0x03ac, + 0x03ac, 0x03ac, 0x03ae, 0x03ae, 0x03ae, 0x03ae, 0x03b1, 0x03b1, + // Entry 2C0 - 2FF + 0x03b6, 0x03b6, 0x03b6, 0x03b6, 0x03b8, 0x03b8, 0x03b8, 0x03b8, + 0x03b8, 0x03b8, 0x03ba, 0x03ba, 0x03cd, 0x03cd, 0x03d0, 0x03d1, + 0x03d1, 0x03d2, 0x03d3, 0x03d3, 0x03d5, 0x03d5, 0x03d5, 0x03d5, + 0x03d6, 0x03d7, 0x03d7, 0x03d7, 0x03d7, 0x03d7, 0x03d9, 0x03d9, + 0x03d9, 0x03d9, 0x03da, 0x03da, 0x03da, 0x03dc, 0x03dc, 0x03dd, + 0x03dd, 0x03dd, 0x03de, 0x03de, 0x03de, 0x03de, 0x03de, 0x03de, + 0x03df, 0x03df, 0x03df, 0x03e2, 0x03e5, 0x03e5, 0x03e5, 0x03e5, + 0x03e5, 0x03e5, 0x03e9, 0x03e9, 0x03e9, 0x03ea, 0x03ec, 0x03ee, + // Entry 300 - 33F + 0x03f2, 0x03f4, 0x03f5, 0x03f5, 0x03f7, 0x03f7, 0x03f7, 0x03f7, +} // Size: 1576 bytes + +var normalSymIndex = []curToIndex{ // 1015 elements + 0: {cur: 0x13, idx: 0x6}, + 1: {cur: 0x2e, idx: 0x13}, + 2: {cur: 0x3a, idx: 0x1c}, + 3: {cur: 0x44, idx: 0x20}, + 4: {cur: 0x5e, idx: 0x3b}, + 5: {cur: 0x63, idx: 0x3f}, + 6: {cur: 0x72, idx: 0x4b}, + 7: {cur: 0x7c, idx: 0x5a}, + 8: {cur: 0x7d, idx: 0x5e}, + 9: {cur: 0x85, idx: 0x62}, + 10: {cur: 0x8d, idx: 0x6e}, + 11: {cur: 0xb2, idx: 0x90}, + 12: {cur: 0xc0, idx: 0x9e}, + 13: {cur: 0xf6, idx: 0xc7}, + 14: {cur: 0xfc, idx: 0xcf}, + 15: {cur: 0x105, idx: 0xd3}, + 16: {cur: 0x109, idx: 0xd7}, + 17: {cur: 0x110, idx: 0xdc}, + 18: {cur: 0x115, idx: 0xe0}, + 19: {cur: 0x117, idx: 0xe4}, + 20: {cur: 0xb2, idx: 0x0}, + 21: {cur: 0xeb, idx: 0xbc}, + 22: {cur: 0x125, idx: 0xe9}, + 23: {cur: 0xb9, idx: 0x4}, + 24: {cur: 0x67, idx: 0xf2}, + 25: {cur: 0x13, idx: 0xf8}, + 26: {cur: 0x42, idx: 0xfc}, + 27: {cur: 0x5d, idx: 0x113}, + 28: {cur: 0xeb, idx: 0xbc}, + 29: {cur: 0x0, idx: 0x11a}, + 30: {cur: 0x2, idx: 0x11e}, + 31: {cur: 0x13, idx: 0xf8}, + 32: {cur: 0x23, idx: 0x130}, + 33: {cur: 0x54, idx: 0x15a}, + 34: {cur: 0x58, idx: 0x164}, + 35: {cur: 0x7e, idx: 0x17b}, + 36: {cur: 0x7f, idx: 0x185}, + 37: {cur: 0x84, idx: 0x190}, + 38: {cur: 0x8e, idx: 0x19a}, + 39: {cur: 0x92, idx: 0x1a8}, + 40: {cur: 0x9d, idx: 0x1b2}, + 41: {cur: 0x9e, idx: 0x1bc}, + 42: {cur: 0xab, idx: 0x1c6}, + 43: {cur: 0xc1, idx: 0x1d0}, + 44: {cur: 0xcd, idx: 0x1da}, + 45: {cur: 0xd5, idx: 0x1e4}, + 46: {cur: 0xd8, idx: 0x1f2}, + 47: {cur: 0xd9, idx: 0x1fc}, + 48: {cur: 0xe9, idx: 0x207}, + 49: {cur: 0xeb, idx: 0xbc}, + 50: {cur: 0xf0, idx: 0x211}, + 51: {cur: 0x11f, idx: 0x223}, + 52: {cur: 0x51, idx: 0x22d}, + 53: {cur: 0x59, idx: 0x231}, + 54: {cur: 0x89, idx: 0x6b}, + 55: {cur: 0xd9, idx: 0x0}, + 56: {cur: 0xe1, idx: 0x235}, + 57: {cur: 0x63, idx: 0x237}, + 58: {cur: 0xe4, idx: 0x3f}, + 59: {cur: 0xf7, idx: 0x23c}, + 60: {cur: 0x85, idx: 0x25}, + 61: {cur: 0xeb, idx: 0xbc}, + 62: {cur: 0xfc, idx: 0x4}, + 63: {cur: 0x16, idx: 0x240}, + 64: {cur: 0xeb, idx: 0xbc}, + 65: {cur: 0x16, idx: 0x240}, + 66: {cur: 0x2e, idx: 0x0}, + 67: {cur: 0x37, idx: 0x258}, + 68: {cur: 0x3a, idx: 0x0}, + 69: {cur: 0x85, idx: 0x25}, + 70: {cur: 0xc0, idx: 0x0}, + 71: {cur: 0xd2, idx: 0xb2}, + 72: {cur: 0xfc, idx: 0x4}, + 73: {cur: 0x127, idx: 0x8a}, + 74: {cur: 0xf7, idx: 0x23c}, + 75: {cur: 0x13, idx: 0x0}, + 76: {cur: 0x21, idx: 0x294}, + 77: {cur: 0x2e, idx: 0x0}, + 78: {cur: 0x3a, idx: 0x0}, + 79: {cur: 0x44, idx: 0x0}, + 80: {cur: 0x63, idx: 0x0}, + 81: {cur: 0x72, idx: 0x0}, + 82: {cur: 0x7c, idx: 0x0}, + 83: {cur: 0x7d, idx: 0x0}, + 84: {cur: 0x85, idx: 0x0}, + 85: {cur: 0x8d, idx: 0x0}, + 86: {cur: 0xb2, idx: 0x0}, + 87: {cur: 0xc0, idx: 0x0}, + 88: {cur: 0xf6, idx: 0x0}, + 89: {cur: 0xfc, idx: 0x29a}, + 90: {cur: 0x105, idx: 0x0}, + 91: {cur: 0x110, idx: 0x0}, + 92: {cur: 0x1b, idx: 0xc}, + 93: {cur: 0xeb, idx: 0xbc}, + 94: {cur: 0x44, idx: 0x25}, + 95: {cur: 0x44, idx: 0x20}, + 96: {cur: 0x13, idx: 0x2a1}, + 97: {cur: 0x2e, idx: 0x0}, + 98: {cur: 0x3a, idx: 0x2a4}, + 99: {cur: 0x44, idx: 0x0}, + 100: {cur: 0x63, idx: 0x2ad}, + 101: {cur: 0x72, idx: 0x2b3}, + 102: {cur: 0x7c, idx: 0x0}, + 103: {cur: 0x85, idx: 0x0}, + 104: {cur: 0x8d, idx: 0x0}, + 105: {cur: 0xc0, idx: 0x2bc}, + 106: {cur: 0xf6, idx: 0x0}, + 107: {cur: 0xfc, idx: 0x2c5}, + 108: {cur: 0x105, idx: 0x0}, + 109: {cur: 0x110, idx: 0x0}, + 110: {cur: 0x13, idx: 0x0}, + 111: {cur: 0x18, idx: 0x9}, + 112: {cur: 0x2e, idx: 0x0}, + 113: {cur: 0x3a, idx: 0x0}, + 114: {cur: 0x44, idx: 0x0}, + 115: {cur: 0x63, idx: 0x0}, + 116: {cur: 0x72, idx: 0x0}, + 117: {cur: 0x75, idx: 0x51}, + 118: {cur: 0x7c, idx: 0x0}, + 119: {cur: 0x85, idx: 0x25}, + 120: {cur: 0xb2, idx: 0x0}, + 121: {cur: 0xc0, idx: 0x0}, + 122: {cur: 0xd1, idx: 0x2ca}, + 123: {cur: 0xeb, idx: 0xbc}, + 124: {cur: 0xfc, idx: 0x0}, + 125: {cur: 0x110, idx: 0x0}, + 126: {cur: 0x117, idx: 0x0}, + 127: {cur: 0x18, idx: 0x2cf}, + 128: {cur: 0x4e, idx: 0x2d4}, + 129: {cur: 0x85, idx: 0x25}, + 130: {cur: 0xc9, idx: 0x2d9}, + 131: {cur: 0xd1, idx: 0x2de}, + 132: {cur: 0xf4, idx: 0x2e6}, + 133: {cur: 0x13, idx: 0xf8}, + 134: {cur: 0x2e, idx: 0x0}, + 135: {cur: 0x3a, idx: 0x0}, + 136: {cur: 0x44, idx: 0x25}, + 137: {cur: 0x5c, idx: 0x37}, + 138: {cur: 0xb2, idx: 0x0}, + 139: {cur: 0xeb, idx: 0xbc}, + 140: {cur: 0xfc, idx: 0x0}, + 141: {cur: 0x110, idx: 0x0}, + 142: {cur: 0x62, idx: 0x2eb}, + 143: {cur: 0x1b, idx: 0xc}, + 144: {cur: 0xeb, idx: 0xbc}, + 145: {cur: 0xd2, idx: 0xb2}, + 146: {cur: 0xfb, idx: 0x2f4}, + 147: {cur: 0xfc, idx: 0x4}, + 148: {cur: 0x7e, idx: 0x17b}, + 149: {cur: 0x13, idx: 0xf8}, + 150: {cur: 0x49, idx: 0x2f8}, + 151: {cur: 0x4e, idx: 0x2c}, + 152: {cur: 0x7c, idx: 0x0}, + 153: {cur: 0x7d, idx: 0x0}, + 154: {cur: 0x105, idx: 0x0}, + 155: {cur: 0x112, idx: 0x2fd}, + 156: {cur: 0xd2, idx: 0xb2}, + 157: {cur: 0x8d, idx: 0x0}, + 158: {cur: 0xeb, idx: 0xbc}, + 159: {cur: 0x13, idx: 0xf8}, + 160: {cur: 0x52, idx: 0x304}, + 161: {cur: 0xeb, idx: 0xbc}, + 162: {cur: 0xfc, idx: 0x4}, + 163: {cur: 0x86, idx: 0x308}, + 164: {cur: 0x12, idx: 0x30c}, + 165: {cur: 0x13, idx: 0xf8}, + 166: {cur: 0x20, idx: 0x310}, + 167: {cur: 0x22, idx: 0x314}, + 168: {cur: 0x50, idx: 0x31d}, + 169: {cur: 0x85, idx: 0x25}, + 170: {cur: 0xeb, idx: 0xbc}, + 171: {cur: 0xfc, idx: 0x4}, + 172: {cur: 0x5e, idx: 0x0}, + 173: {cur: 0x5e, idx: 0x0}, + 174: {cur: 0x99, idx: 0x2eb}, + 175: {cur: 0x13, idx: 0x0}, + 176: {cur: 0x85, idx: 0x25}, + 177: {cur: 0xc9, idx: 0xa6}, + 178: {cur: 0xeb, idx: 0xbc}, + 179: {cur: 0xfc, idx: 0x4}, + 180: {cur: 0x13, idx: 0xf8}, + 181: {cur: 0x33, idx: 0x332}, + 182: {cur: 0x7c, idx: 0x0}, + 183: {cur: 0x8d, idx: 0x336}, + 184: {cur: 0xeb, idx: 0x33c}, + 185: {cur: 0x109, idx: 0x0}, + 186: {cur: 0x86, idx: 0x308}, + 187: {cur: 0x13, idx: 0xf8}, + 188: {cur: 0x67, idx: 0xf2}, + 189: {cur: 0xeb, idx: 0xbc}, + 190: {cur: 0x6d, idx: 0x342}, + 191: {cur: 0xeb, idx: 0xbc}, + 192: {cur: 0xfc, idx: 0x4}, + 193: {cur: 0x85, idx: 0x25}, + 194: {cur: 0xfc, idx: 0x4}, + 195: {cur: 0x85, idx: 0x62}, + 196: {cur: 0xfc, idx: 0xcf}, + 197: {cur: 0x110, idx: 0x4}, + 198: {cur: 0x110, idx: 0x4}, + 199: {cur: 0x13, idx: 0x4}, + 200: {cur: 0x2e, idx: 0x0}, + 201: {cur: 0x3a, idx: 0x0}, + 202: {cur: 0x44, idx: 0x0}, + 203: {cur: 0x5e, idx: 0x0}, + 204: {cur: 0x63, idx: 0x0}, + 205: {cur: 0x72, idx: 0x0}, + 206: {cur: 0x7c, idx: 0x0}, + 207: {cur: 0x7d, idx: 0x0}, + 208: {cur: 0x85, idx: 0x0}, + 209: {cur: 0x8d, idx: 0x0}, + 210: {cur: 0xb2, idx: 0x0}, + 211: {cur: 0xc0, idx: 0x0}, + 212: {cur: 0xd7, idx: 0x7e}, + 213: {cur: 0xf6, idx: 0x0}, + 214: {cur: 0xfc, idx: 0x0}, + 215: {cur: 0x105, idx: 0x0}, + 216: {cur: 0x109, idx: 0x0}, + 217: {cur: 0x110, idx: 0x0}, + 218: {cur: 0x115, idx: 0x0}, + 219: {cur: 0x117, idx: 0x355}, + 220: {cur: 0x1a, idx: 0x4}, + 221: {cur: 0x24, idx: 0x359}, + 222: {cur: 0x25, idx: 0x4}, + 223: {cur: 0x32, idx: 0x4}, + 224: {cur: 0x35, idx: 0x16}, + 225: {cur: 0x39, idx: 0x4}, + 226: {cur: 0x3a, idx: 0x4}, + 227: {cur: 0x13, idx: 0x4}, + 228: {cur: 0xc0, idx: 0x4}, + 229: {cur: 0x13, idx: 0x4}, + 230: {cur: 0x52, idx: 0x304}, + 231: {cur: 0x110, idx: 0x4}, + 232: {cur: 0x59, idx: 0x231}, + 233: {cur: 0x60, idx: 0x4}, + 234: {cur: 0x61, idx: 0x3f}, + 235: {cur: 0x63, idx: 0x237}, + 236: {cur: 0x110, idx: 0x4}, + 237: {cur: 0x67, idx: 0xf2}, + 238: {cur: 0x63, idx: 0x237}, + 239: {cur: 0x68, idx: 0x3f}, + 240: {cur: 0x69, idx: 0x35d}, + 241: {cur: 0x71, idx: 0x4}, + 242: {cur: 0x83, idx: 0x4}, + 243: {cur: 0x86, idx: 0x308}, + 244: {cur: 0x13, idx: 0x4}, + 245: {cur: 0x110, idx: 0x4}, + 246: {cur: 0x8f, idx: 0x4}, + 247: {cur: 0x110, idx: 0x4}, + 248: {cur: 0x94, idx: 0x4}, + 249: {cur: 0x125, idx: 0xe9}, + 250: {cur: 0xa3, idx: 0x87}, + 251: {cur: 0xaa, idx: 0x35f}, + 252: {cur: 0x110, idx: 0x4}, + 253: {cur: 0x63, idx: 0x237}, + 254: {cur: 0xae, idx: 0x7e}, + 255: {cur: 0xb1, idx: 0x364}, + 256: {cur: 0xb5, idx: 0x94}, + 257: {cur: 0xb9, idx: 0x4}, + 258: {cur: 0x13, idx: 0x4}, + 259: {cur: 0xba, idx: 0x97}, + 260: {cur: 0x13, idx: 0x4}, + 261: {cur: 0xc0, idx: 0x4}, + 262: {cur: 0xc0, idx: 0x4}, + 263: {cur: 0xc6, idx: 0x8a}, + 264: {cur: 0xc7, idx: 0xa2}, + 265: {cur: 0xc8, idx: 0x7e}, + 266: {cur: 0xc0, idx: 0x4}, + 267: {cur: 0xd4, idx: 0xb6}, + 268: {cur: 0xd6, idx: 0x4}, + 269: {cur: 0xd7, idx: 0x367}, + 270: {cur: 0xdb, idx: 0x30}, + 271: {cur: 0xdc, idx: 0x4}, + 272: {cur: 0x63, idx: 0x237}, + 273: {cur: 0xdd, idx: 0x3f}, + 274: {cur: 0xe0, idx: 0x36a}, + 275: {cur: 0x63, idx: 0x237}, + 276: {cur: 0xe4, idx: 0x3f}, + 277: {cur: 0x8, idx: 0x36d}, + 278: {cur: 0xea, idx: 0x372}, + 279: {cur: 0xc0, idx: 0x4}, + 280: {cur: 0xf1, idx: 0xc0}, + 281: {cur: 0xf5, idx: 0x4}, + 282: {cur: 0x13, idx: 0x4}, + 283: {cur: 0xf7, idx: 0x23c}, + 284: {cur: 0xfb, idx: 0x2f4}, + 285: {cur: 0x110, idx: 0x4}, + 286: {cur: 0x107, idx: 0x374}, + 287: {cur: 0x108, idx: 0x377}, + 288: {cur: 0x125, idx: 0xe9}, + 289: {cur: 0x127, idx: 0x8a}, + 290: {cur: 0x13, idx: 0x0}, + 291: {cur: 0x2e, idx: 0x0}, + 292: {cur: 0x44, idx: 0x0}, + 293: {cur: 0x5c, idx: 0x37}, + 294: {cur: 0x63, idx: 0x0}, + 295: {cur: 0x72, idx: 0x0}, + 296: {cur: 0x7c, idx: 0x0}, + 297: {cur: 0x7d, idx: 0x0}, + 298: {cur: 0x85, idx: 0x0}, + 299: {cur: 0x8d, idx: 0x0}, + 300: {cur: 0xb2, idx: 0x0}, + 301: {cur: 0xc0, idx: 0x0}, + 302: {cur: 0xeb, idx: 0xbc}, + 303: {cur: 0xf6, idx: 0x0}, + 304: {cur: 0x109, idx: 0x0}, + 305: {cur: 0x110, idx: 0x0}, + 306: {cur: 0x115, idx: 0x0}, + 307: {cur: 0x3a, idx: 0x0}, + 308: {cur: 0x5e, idx: 0x0}, + 309: {cur: 0xeb, idx: 0x0}, + 310: {cur: 0xfc, idx: 0x0}, + 311: {cur: 0x105, idx: 0x0}, + 312: {cur: 0x11, idx: 0x4}, + 313: {cur: 0xfc, idx: 0xcf}, + 314: {cur: 0x27, idx: 0x10}, + 315: {cur: 0x2e, idx: 0x13}, + 316: {cur: 0x39, idx: 0x4}, + 317: {cur: 0x41, idx: 0x4}, + 318: {cur: 0xfc, idx: 0xcf}, + 319: {cur: 0x45, idx: 0x4}, + 320: {cur: 0xfc, idx: 0xcf}, + 321: {cur: 0x47, idx: 0x28}, + 322: {cur: 0x4b, idx: 0x4}, + 323: {cur: 0xfc, idx: 0xcf}, + 324: {cur: 0x53, idx: 0x264}, + 325: {cur: 0xfc, idx: 0xcf}, + 326: {cur: 0xfc, idx: 0x4}, + 327: {cur: 0x109, idx: 0xd7}, + 328: {cur: 0x6e, idx: 0x49}, + 329: {cur: 0x73, idx: 0x4f}, + 330: {cur: 0xb2, idx: 0x4}, + 331: {cur: 0xbc, idx: 0x9b}, + 332: {cur: 0xc2, idx: 0x387}, + 333: {cur: 0xc4, idx: 0x38b}, + 334: {cur: 0xc7, idx: 0xa2}, + 335: {cur: 0xfc, idx: 0x4}, + 336: {cur: 0xcc, idx: 0x38e}, + 337: {cur: 0xfc, idx: 0x4}, + 338: {cur: 0x85, idx: 0x25}, + 339: {cur: 0xfc, idx: 0x4}, + 340: {cur: 0xfc, idx: 0xcf}, + 341: {cur: 0x101, idx: 0x4}, + 342: {cur: 0x104, idx: 0x392}, + 343: {cur: 0x13, idx: 0xf8}, + 344: {cur: 0x57, idx: 0x30}, + 345: {cur: 0x85, idx: 0x25}, + 346: {cur: 0xeb, idx: 0xbc}, + 347: {cur: 0xfc, idx: 0x4}, + 348: {cur: 0x5c, idx: 0x37}, + 349: {cur: 0xeb, idx: 0xbc}, + 350: {cur: 0x4, idx: 0x396}, + 351: {cur: 0x3a, idx: 0x2a4}, + 352: {cur: 0x44, idx: 0x399}, + 353: {cur: 0x72, idx: 0x39e}, + 354: {cur: 0x7f, idx: 0x3a2}, + 355: {cur: 0x85, idx: 0x25}, + 356: {cur: 0xb2, idx: 0x3ab}, + 357: {cur: 0xc0, idx: 0x3af}, + 358: {cur: 0xeb, idx: 0xbc}, + 359: {cur: 0xfc, idx: 0x4}, + 360: {cur: 0x110, idx: 0x3b3}, + 361: {cur: 0x6a, idx: 0x46}, + 362: {cur: 0xab, idx: 0x3b7}, + 363: {cur: 0x13, idx: 0x0}, + 364: {cur: 0x2e, idx: 0x0}, + 365: {cur: 0x3a, idx: 0x0}, + 366: {cur: 0x44, idx: 0x0}, + 367: {cur: 0x5f, idx: 0x3ba}, + 368: {cur: 0x72, idx: 0x0}, + 369: {cur: 0x7c, idx: 0x0}, + 370: {cur: 0x7d, idx: 0x0}, + 371: {cur: 0x85, idx: 0x25}, + 372: {cur: 0x8d, idx: 0x0}, + 373: {cur: 0xb2, idx: 0x0}, + 374: {cur: 0xc0, idx: 0x0}, + 375: {cur: 0xf6, idx: 0x0}, + 376: {cur: 0xfc, idx: 0x4}, + 377: {cur: 0x105, idx: 0x0}, + 378: {cur: 0x110, idx: 0x0}, + 379: {cur: 0x117, idx: 0x0}, + 380: {cur: 0x85, idx: 0x25}, + 381: {cur: 0xc7, idx: 0xa2}, + 382: {cur: 0xeb, idx: 0xbc}, + 383: {cur: 0xfc, idx: 0x4}, + 384: {cur: 0x52, idx: 0x30}, + 385: {cur: 0x52, idx: 0x304}, + 386: {cur: 0x11, idx: 0x3bd}, + 387: {cur: 0x13, idx: 0x3c1}, + 388: {cur: 0x1d, idx: 0x3c5}, + 389: {cur: 0x25, idx: 0x3c8}, + 390: {cur: 0x26, idx: 0x3cc}, + 391: {cur: 0x32, idx: 0x3d0}, + 392: {cur: 0x39, idx: 0x3d4}, + 393: {cur: 0x3a, idx: 0x2a4}, + 394: {cur: 0x41, idx: 0x3d8}, + 395: {cur: 0x44, idx: 0x0}, + 396: {cur: 0x45, idx: 0x3dc}, + 397: {cur: 0x4d, idx: 0x3e0}, + 398: {cur: 0x60, idx: 0x3e9}, + 399: {cur: 0x61, idx: 0x3ed}, + 400: {cur: 0x62, idx: 0x2eb}, + 401: {cur: 0x63, idx: 0x3f2}, + 402: {cur: 0x68, idx: 0x3f7}, + 403: {cur: 0x72, idx: 0x0}, + 404: {cur: 0x79, idx: 0x3fc}, + 405: {cur: 0x7a, idx: 0x401}, + 406: {cur: 0x82, idx: 0x406}, + 407: {cur: 0x85, idx: 0x0}, + 408: {cur: 0x92, idx: 0x40c}, + 409: {cur: 0xad, idx: 0x411}, + 410: {cur: 0xb2, idx: 0x3ab}, + 411: {cur: 0xb9, idx: 0x416}, + 412: {cur: 0xc0, idx: 0x3af}, + 413: {cur: 0xce, idx: 0x41d}, + 414: {cur: 0xd6, idx: 0x424}, + 415: {cur: 0xdc, idx: 0x428}, + 416: {cur: 0xe2, idx: 0x42c}, + 417: {cur: 0xf5, idx: 0x430}, + 418: {cur: 0xf6, idx: 0x0}, + 419: {cur: 0xfc, idx: 0x434}, + 420: {cur: 0x101, idx: 0x438}, + 421: {cur: 0x108, idx: 0x377}, + 422: {cur: 0x110, idx: 0x0}, + 423: {cur: 0x117, idx: 0x43c}, + 424: {cur: 0x24, idx: 0x359}, + 425: {cur: 0x11, idx: 0x0}, + 426: {cur: 0x13, idx: 0x444}, + 427: {cur: 0x25, idx: 0x0}, + 428: {cur: 0x26, idx: 0x0}, + 429: {cur: 0x32, idx: 0x0}, + 430: {cur: 0x39, idx: 0x0}, + 431: {cur: 0x3a, idx: 0x4}, + 432: {cur: 0x41, idx: 0x0}, + 433: {cur: 0x44, idx: 0x20}, + 434: {cur: 0x45, idx: 0x0}, + 435: {cur: 0x60, idx: 0x0}, + 436: {cur: 0x61, idx: 0x0}, + 437: {cur: 0x63, idx: 0x3f}, + 438: {cur: 0x68, idx: 0x0}, + 439: {cur: 0x72, idx: 0x44a}, + 440: {cur: 0x7c, idx: 0x0}, + 441: {cur: 0x7d, idx: 0x0}, + 442: {cur: 0x85, idx: 0x25}, + 443: {cur: 0x8d, idx: 0x0}, + 444: {cur: 0x92, idx: 0x0}, + 445: {cur: 0xb2, idx: 0x0}, + 446: {cur: 0xb9, idx: 0x0}, + 447: {cur: 0xc0, idx: 0x450}, + 448: {cur: 0xd6, idx: 0x0}, + 449: {cur: 0xdc, idx: 0x456}, + 450: {cur: 0xe2, idx: 0x0}, + 451: {cur: 0xf5, idx: 0x0}, + 452: {cur: 0xfc, idx: 0x45c}, + 453: {cur: 0x101, idx: 0x0}, + 454: {cur: 0x105, idx: 0x0}, + 455: {cur: 0x109, idx: 0x0}, + 456: {cur: 0x115, idx: 0x0}, + 457: {cur: 0x117, idx: 0x0}, + 458: {cur: 0x3b, idx: 0x32a}, + 459: {cur: 0x51, idx: 0x22d}, + 460: {cur: 0x54, idx: 0x462}, + 461: {cur: 0x6a, idx: 0x46}, + 462: {cur: 0x76, idx: 0x465}, + 463: {cur: 0x89, idx: 0x6b}, + 464: {cur: 0x62, idx: 0x0}, + 465: {cur: 0x99, idx: 0x2eb}, + 466: {cur: 0xa3, idx: 0x87}, + 467: {cur: 0xab, idx: 0x3b7}, + 468: {cur: 0xae, idx: 0x7e}, + 469: {cur: 0xd4, idx: 0xb6}, + 470: {cur: 0xd7, idx: 0x367}, + 471: {cur: 0xe9, idx: 0x467}, + 472: {cur: 0xf0, idx: 0x46a}, + 473: {cur: 0x107, idx: 0x374}, + 474: {cur: 0x13, idx: 0xf8}, + 475: {cur: 0x3a, idx: 0x9b}, + 476: {cur: 0x60, idx: 0x16e}, + 477: {cur: 0xd6, idx: 0x28a}, + 478: {cur: 0xeb, idx: 0xbc}, + 479: {cur: 0x117, idx: 0x0}, + 480: {cur: 0x85, idx: 0x25}, + 481: {cur: 0xeb, idx: 0xbc}, + 482: {cur: 0xfc, idx: 0x4}, + 483: {cur: 0xeb, idx: 0xbc}, + 484: {cur: 0xfc, idx: 0x4}, + 485: {cur: 0x5c, idx: 0x37}, + 486: {cur: 0xb2, idx: 0x3ab}, + 487: {cur: 0xeb, idx: 0xbc}, + 488: {cur: 0xfc, idx: 0x4}, + 489: {cur: 0x12, idx: 0x30c}, + 490: {cur: 0x85, idx: 0x25}, + 491: {cur: 0xfc, idx: 0x4}, + 492: {cur: 0xeb, idx: 0xbc}, + 493: {cur: 0x86, idx: 0x308}, + 494: {cur: 0xba, idx: 0x97}, + 495: {cur: 0x67, idx: 0xf2}, + 496: {cur: 0xfc, idx: 0x4}, + 497: {cur: 0x44, idx: 0x47c}, + 498: {cur: 0x7a, idx: 0x487}, + 499: {cur: 0x85, idx: 0x25}, + 500: {cur: 0xeb, idx: 0xbc}, + 501: {cur: 0xfc, idx: 0x4}, + 502: {cur: 0xeb, idx: 0xbc}, + 503: {cur: 0xfc, idx: 0x4}, + 504: {cur: 0x13, idx: 0x0}, + 505: {cur: 0x2e, idx: 0x0}, + 506: {cur: 0x3a, idx: 0x0}, + 507: {cur: 0x44, idx: 0x0}, + 508: {cur: 0x5e, idx: 0x0}, + 509: {cur: 0x63, idx: 0x0}, + 510: {cur: 0x72, idx: 0x0}, + 511: {cur: 0x7c, idx: 0x0}, + 512: {cur: 0x7d, idx: 0x0}, + 513: {cur: 0x85, idx: 0x0}, + 514: {cur: 0x8d, idx: 0x0}, + 515: {cur: 0xb2, idx: 0x0}, + 516: {cur: 0xc0, idx: 0x0}, + 517: {cur: 0xf6, idx: 0x0}, + 518: {cur: 0xfc, idx: 0x0}, + 519: {cur: 0x105, idx: 0x0}, + 520: {cur: 0x110, idx: 0x0}, + 521: {cur: 0x117, idx: 0x0}, + 522: {cur: 0x18, idx: 0x9}, + 523: {cur: 0x13, idx: 0x0}, + 524: {cur: 0x85, idx: 0x25}, + 525: {cur: 0xc9, idx: 0xa6}, + 526: {cur: 0xeb, idx: 0xbc}, + 527: {cur: 0xfc, idx: 0x4}, + 528: {cur: 0x13, idx: 0x0}, + 529: {cur: 0x2e, idx: 0x0}, + 530: {cur: 0x3a, idx: 0x0}, + 531: {cur: 0x44, idx: 0x0}, + 532: {cur: 0x5e, idx: 0x0}, + 533: {cur: 0x63, idx: 0x0}, + 534: {cur: 0x72, idx: 0x0}, + 535: {cur: 0x77, idx: 0x54}, + 536: {cur: 0x7c, idx: 0x0}, + 537: {cur: 0x7d, idx: 0x0}, + 538: {cur: 0x85, idx: 0x25}, + 539: {cur: 0x8d, idx: 0x0}, + 540: {cur: 0xb2, idx: 0x0}, + 541: {cur: 0xc0, idx: 0x0}, + 542: {cur: 0xf6, idx: 0x0}, + 543: {cur: 0xfc, idx: 0x0}, + 544: {cur: 0x105, idx: 0x0}, + 545: {cur: 0x110, idx: 0x0}, + 546: {cur: 0x7, idx: 0x498}, + 547: {cur: 0xeb, idx: 0xbc}, + 548: {cur: 0xfc, idx: 0x4}, + 549: {cur: 0x13, idx: 0xf8}, + 550: {cur: 0x78, idx: 0x57}, + 551: {cur: 0x7d, idx: 0x7e}, + 552: {cur: 0xeb, idx: 0xbc}, + 553: {cur: 0xba, idx: 0x97}, + 554: {cur: 0x44, idx: 0x25}, + 555: {cur: 0x13, idx: 0x0}, + 556: {cur: 0x2e, idx: 0x0}, + 557: {cur: 0x3a, idx: 0x0}, + 558: {cur: 0x5e, idx: 0x0}, + 559: {cur: 0x63, idx: 0x0}, + 560: {cur: 0x7d, idx: 0x0}, + 561: {cur: 0x8d, idx: 0x0}, + 562: {cur: 0xb2, idx: 0x0}, + 563: {cur: 0xc0, idx: 0x0}, + 564: {cur: 0xf6, idx: 0x0}, + 565: {cur: 0xfc, idx: 0x0}, + 566: {cur: 0x105, idx: 0x0}, + 567: {cur: 0x2e, idx: 0x0}, + 568: {cur: 0x72, idx: 0x0}, + 569: {cur: 0x85, idx: 0x0}, + 570: {cur: 0x8d, idx: 0x0}, + 571: {cur: 0xb2, idx: 0x0}, + 572: {cur: 0xeb, idx: 0xbc}, + 573: {cur: 0xf6, idx: 0x0}, + 574: {cur: 0xfc, idx: 0x0}, + 575: {cur: 0x44, idx: 0x49f}, + 576: {cur: 0x85, idx: 0x4a3}, + 577: {cur: 0xfc, idx: 0x4}, + 578: {cur: 0xf7, idx: 0x23c}, + 579: {cur: 0x13, idx: 0x0}, + 580: {cur: 0x44, idx: 0x0}, + 581: {cur: 0x65, idx: 0x42}, + 582: {cur: 0x72, idx: 0x0}, + 583: {cur: 0x7c, idx: 0x0}, + 584: {cur: 0x7d, idx: 0x0}, + 585: {cur: 0x85, idx: 0x0}, + 586: {cur: 0x8d, idx: 0x0}, + 587: {cur: 0xc0, idx: 0x0}, + 588: {cur: 0x105, idx: 0x0}, + 589: {cur: 0x54, idx: 0x462}, + 590: {cur: 0x86, idx: 0x308}, + 591: {cur: 0xf7, idx: 0x23c}, + 592: {cur: 0x13, idx: 0xf8}, + 593: {cur: 0x4c, idx: 0x4ae}, + 594: {cur: 0xeb, idx: 0xbc}, + 595: {cur: 0x86, idx: 0x308}, + 596: {cur: 0x90, idx: 0x72}, + 597: {cur: 0xd2, idx: 0xb2}, + 598: {cur: 0xeb, idx: 0xbc}, + 599: {cur: 0xfc, idx: 0x4}, + 600: {cur: 0x52, idx: 0x304}, + 601: {cur: 0x86, idx: 0x308}, + 602: {cur: 0x88, idx: 0x67}, + 603: {cur: 0xeb, idx: 0xbc}, + 604: {cur: 0xfc, idx: 0x4}, + 605: {cur: 0xeb, idx: 0xbc}, + 606: {cur: 0xfc, idx: 0x4}, + 607: {cur: 0x13, idx: 0xf8}, + 608: {cur: 0xf7, idx: 0x23c}, + 609: {cur: 0x13, idx: 0x0}, + 610: {cur: 0x2e, idx: 0x0}, + 611: {cur: 0x3a, idx: 0x0}, + 612: {cur: 0x63, idx: 0x0}, + 613: {cur: 0x72, idx: 0x0}, + 614: {cur: 0x7c, idx: 0x0}, + 615: {cur: 0x7d, idx: 0x0}, + 616: {cur: 0x87, idx: 0x4bf}, + 617: {cur: 0x8d, idx: 0x0}, + 618: {cur: 0xb2, idx: 0x0}, + 619: {cur: 0xc0, idx: 0x0}, + 620: {cur: 0xeb, idx: 0xbc}, + 621: {cur: 0xf6, idx: 0x0}, + 622: {cur: 0xfc, idx: 0x0}, + 623: {cur: 0x110, idx: 0x0}, + 624: {cur: 0xf7, idx: 0x23c}, + 625: {cur: 0x12, idx: 0x30c}, + 626: {cur: 0x13, idx: 0xf8}, + 627: {cur: 0x85, idx: 0x25}, + 628: {cur: 0xeb, idx: 0xbc}, + 629: {cur: 0xfc, idx: 0x4}, + 630: {cur: 0xfb, idx: 0x2f4}, + 631: {cur: 0xfc, idx: 0x4}, + 632: {cur: 0x3b, idx: 0x32a}, + 633: {cur: 0x9, idx: 0x1}, + 634: {cur: 0x91, idx: 0x76}, + 635: {cur: 0xeb, idx: 0xbc}, + 636: {cur: 0x7e, idx: 0x17b}, + 637: {cur: 0x13, idx: 0x0}, + 638: {cur: 0x2e, idx: 0x0}, + 639: {cur: 0x3a, idx: 0x0}, + 640: {cur: 0x44, idx: 0x0}, + 641: {cur: 0x63, idx: 0x0}, + 642: {cur: 0x72, idx: 0x0}, + 643: {cur: 0x7c, idx: 0x0}, + 644: {cur: 0x7d, idx: 0x0}, + 645: {cur: 0x85, idx: 0x0}, + 646: {cur: 0x8d, idx: 0x0}, + 647: {cur: 0xb2, idx: 0x0}, + 648: {cur: 0xc0, idx: 0x0}, + 649: {cur: 0xf6, idx: 0x0}, + 650: {cur: 0xfc, idx: 0x0}, + 651: {cur: 0x105, idx: 0x0}, + 652: {cur: 0x109, idx: 0x0}, + 653: {cur: 0x110, idx: 0x0}, + 654: {cur: 0x115, idx: 0x0}, + 655: {cur: 0x117, idx: 0x0}, + 656: {cur: 0x3b, idx: 0x32a}, + 657: {cur: 0x86, idx: 0x308}, + 658: {cur: 0x86, idx: 0x308}, + 659: {cur: 0x13, idx: 0xf8}, + 660: {cur: 0x85, idx: 0x25}, + 661: {cur: 0x9b, idx: 0x84}, + 662: {cur: 0xeb, idx: 0xbc}, + 663: {cur: 0xfc, idx: 0x4}, + 664: {cur: 0x86, idx: 0x308}, + 665: {cur: 0xf7, idx: 0x23c}, + 666: {cur: 0x86, idx: 0x308}, + 667: {cur: 0xae, idx: 0x7e}, + 668: {cur: 0xa3, idx: 0x87}, + 669: {cur: 0xb8, idx: 0x4cc}, + 670: {cur: 0x13, idx: 0x0}, + 671: {cur: 0x44, idx: 0x0}, + 672: {cur: 0x63, idx: 0x0}, + 673: {cur: 0x72, idx: 0x0}, + 674: {cur: 0x7c, idx: 0x0}, + 675: {cur: 0x7d, idx: 0x0}, + 676: {cur: 0x85, idx: 0x0}, + 677: {cur: 0x8d, idx: 0x0}, + 678: {cur: 0xa5, idx: 0x4d0}, + 679: {cur: 0xc0, idx: 0x0}, + 680: {cur: 0xf6, idx: 0x0}, + 681: {cur: 0x105, idx: 0x0}, + 682: {cur: 0x85, idx: 0x25}, + 683: {cur: 0xeb, idx: 0xbc}, + 684: {cur: 0xfc, idx: 0x4}, + 685: {cur: 0xa9, idx: 0x8c}, + 686: {cur: 0xeb, idx: 0xbc}, + 687: {cur: 0xfc, idx: 0x4}, + 688: {cur: 0xeb, idx: 0xbc}, + 689: {cur: 0xfc, idx: 0x4}, + 690: {cur: 0x3a, idx: 0x0}, + 691: {cur: 0xb2, idx: 0x0}, + 692: {cur: 0xb5, idx: 0x94}, + 693: {cur: 0xfc, idx: 0x0}, + 694: {cur: 0x26, idx: 0x4}, + 695: {cur: 0xdc, idx: 0x4}, + 696: {cur: 0x8, idx: 0x4dc}, + 697: {cur: 0x14, idx: 0x4e0}, + 698: {cur: 0x76, idx: 0x465}, + 699: {cur: 0xa8, idx: 0x8a}, + 700: {cur: 0xc2, idx: 0x387}, + 701: {cur: 0xeb, idx: 0xbc}, + 702: {cur: 0xf5, idx: 0x21b}, + 703: {cur: 0xfc, idx: 0x4}, + 704: {cur: 0xb9, idx: 0x4}, + 705: {cur: 0x13, idx: 0x0}, + 706: {cur: 0x2e, idx: 0x0}, + 707: {cur: 0x3a, idx: 0x0}, + 708: {cur: 0x44, idx: 0x0}, + 709: {cur: 0x72, idx: 0x0}, + 710: {cur: 0x7c, idx: 0x0}, + 711: {cur: 0x7d, idx: 0x0}, + 712: {cur: 0x85, idx: 0x0}, + 713: {cur: 0x8d, idx: 0x0}, + 714: {cur: 0xb2, idx: 0x0}, + 715: {cur: 0xbe, idx: 0x30}, + 716: {cur: 0xc0, idx: 0x0}, + 717: {cur: 0xf6, idx: 0x0}, + 718: {cur: 0xfc, idx: 0x0}, + 719: {cur: 0x105, idx: 0x0}, + 720: {cur: 0x109, idx: 0x0}, + 721: {cur: 0x110, idx: 0x0}, + 722: {cur: 0x117, idx: 0x0}, + 723: {cur: 0xbf, idx: 0x4e4}, + 724: {cur: 0xeb, idx: 0xbc}, + 725: {cur: 0x13, idx: 0xf8}, + 726: {cur: 0x3a, idx: 0x9b}, + 727: {cur: 0x60, idx: 0x16e}, + 728: {cur: 0xd6, idx: 0x28a}, + 729: {cur: 0xeb, idx: 0xbc}, + 730: {cur: 0x117, idx: 0x0}, + 731: {cur: 0x14, idx: 0x4f8}, + 732: {cur: 0xfc, idx: 0x4}, + 733: {cur: 0x8, idx: 0x36d}, + 734: {cur: 0xe2, idx: 0x4}, + 735: {cur: 0x8, idx: 0x36d}, + 736: {cur: 0x13, idx: 0x0}, + 737: {cur: 0x2e, idx: 0x0}, + 738: {cur: 0x3a, idx: 0x0}, + 739: {cur: 0x44, idx: 0x0}, + 740: {cur: 0x63, idx: 0x0}, + 741: {cur: 0x72, idx: 0x0}, + 742: {cur: 0x7c, idx: 0x0}, + 743: {cur: 0x7d, idx: 0x0}, + 744: {cur: 0x85, idx: 0x0}, + 745: {cur: 0x8d, idx: 0x0}, + 746: {cur: 0xb2, idx: 0x0}, + 747: {cur: 0xbe, idx: 0x30}, + 748: {cur: 0xc0, idx: 0x0}, + 749: {cur: 0xf6, idx: 0x0}, + 750: {cur: 0xfc, idx: 0x0}, + 751: {cur: 0x105, idx: 0x0}, + 752: {cur: 0x109, idx: 0x0}, + 753: {cur: 0x110, idx: 0x0}, + 754: {cur: 0x117, idx: 0x0}, + 755: {cur: 0x63, idx: 0x237}, + 756: {cur: 0xe4, idx: 0x3f}, + 757: {cur: 0xfb, idx: 0x2f4}, + 758: {cur: 0x5d, idx: 0x258}, + 759: {cur: 0x86, idx: 0x308}, + 760: {cur: 0x85, idx: 0x25}, + 761: {cur: 0xfc, idx: 0x4}, + 762: {cur: 0x65, idx: 0x42}, + 763: {cur: 0xfc, idx: 0x4}, + 764: {cur: 0x65, idx: 0x0}, + 765: {cur: 0xd2, idx: 0xb2}, + 766: {cur: 0xeb, idx: 0xbc}, + 767: {cur: 0xc8, idx: 0x4fd}, + 768: {cur: 0x13, idx: 0x0}, + 769: {cur: 0x3a, idx: 0x0}, + 770: {cur: 0x44, idx: 0x0}, + 771: {cur: 0x63, idx: 0x0}, + 772: {cur: 0x72, idx: 0x0}, + 773: {cur: 0x7c, idx: 0x0}, + 774: {cur: 0x7d, idx: 0x0}, + 775: {cur: 0x85, idx: 0x0}, + 776: {cur: 0x8d, idx: 0x0}, + 777: {cur: 0xb2, idx: 0x0}, + 778: {cur: 0xc0, idx: 0x0}, + 779: {cur: 0xc9, idx: 0xa6}, + 780: {cur: 0xf6, idx: 0x0}, + 781: {cur: 0xfc, idx: 0x0}, + 782: {cur: 0x105, idx: 0x0}, + 783: {cur: 0x4, idx: 0x396}, + 784: {cur: 0x13, idx: 0xf8}, + 785: {cur: 0xcb, idx: 0x504}, + 786: {cur: 0xeb, idx: 0xbc}, + 787: {cur: 0x9, idx: 0x1}, + 788: {cur: 0x4c, idx: 0x4ae}, + 789: {cur: 0xcb, idx: 0x509}, + 790: {cur: 0x99, idx: 0x2eb}, + 791: {cur: 0xaa, idx: 0x35f}, + 792: {cur: 0xb8, idx: 0x4cc}, + 793: {cur: 0xcb, idx: 0x4ae}, + 794: {cur: 0xe5, idx: 0xb9}, + 795: {cur: 0xc4, idx: 0x38b}, + 796: {cur: 0x27, idx: 0x10}, + 797: {cur: 0xc4, idx: 0x0}, + 798: {cur: 0xc4, idx: 0x0}, + 799: {cur: 0xfc, idx: 0x4}, + 800: {cur: 0x24, idx: 0x359}, + 801: {cur: 0x13, idx: 0x0}, + 802: {cur: 0x2e, idx: 0x0}, + 803: {cur: 0x3a, idx: 0x0}, + 804: {cur: 0x44, idx: 0x0}, + 805: {cur: 0x5e, idx: 0x0}, + 806: {cur: 0x63, idx: 0x0}, + 807: {cur: 0x72, idx: 0x0}, + 808: {cur: 0x7c, idx: 0x0}, + 809: {cur: 0x7d, idx: 0x0}, + 810: {cur: 0x85, idx: 0x0}, + 811: {cur: 0x8d, idx: 0x0}, + 812: {cur: 0xb2, idx: 0x0}, + 813: {cur: 0xc0, idx: 0x0}, + 814: {cur: 0xf6, idx: 0x0}, + 815: {cur: 0xfc, idx: 0x0}, + 816: {cur: 0x105, idx: 0x0}, + 817: {cur: 0x110, idx: 0x0}, + 818: {cur: 0xa2, idx: 0x4f}, + 819: {cur: 0xf7, idx: 0x23c}, + 820: {cur: 0x0, idx: 0x510}, + 821: {cur: 0x85, idx: 0x25}, + 822: {cur: 0xd2, idx: 0xb2}, + 823: {cur: 0xd3, idx: 0x18}, + 824: {cur: 0xeb, idx: 0xbc}, + 825: {cur: 0xef, idx: 0x519}, + 826: {cur: 0xf8, idx: 0xcb}, + 827: {cur: 0xfc, idx: 0x4}, + 828: {cur: 0x37, idx: 0x258}, + 829: {cur: 0xd3, idx: 0x0}, + 830: {cur: 0x87, idx: 0x4bf}, + 831: {cur: 0x90, idx: 0x72}, + 832: {cur: 0xa2, idx: 0x4f}, + 833: {cur: 0xd4, idx: 0xb6}, + 834: {cur: 0xf7, idx: 0x23c}, + 835: {cur: 0xd2, idx: 0xb2}, + 836: {cur: 0x86, idx: 0x308}, + 837: {cur: 0xf7, idx: 0x23c}, + 838: {cur: 0xc8, idx: 0x7e}, + 839: {cur: 0x52, idx: 0x520}, + 840: {cur: 0xbe, idx: 0x30}, + 841: {cur: 0xdb, idx: 0x524}, + 842: {cur: 0xeb, idx: 0xbc}, + 843: {cur: 0xbe, idx: 0x528}, + 844: {cur: 0xdb, idx: 0x30}, + 845: {cur: 0xb8, idx: 0x4cc}, + 846: {cur: 0x93, idx: 0x52c}, + 847: {cur: 0xeb, idx: 0xbc}, + 848: {cur: 0x115, idx: 0x534}, + 849: {cur: 0x13, idx: 0x0}, + 850: {cur: 0x2e, idx: 0x0}, + 851: {cur: 0x3a, idx: 0x0}, + 852: {cur: 0x44, idx: 0x0}, + 853: {cur: 0x63, idx: 0x0}, + 854: {cur: 0x72, idx: 0x0}, + 855: {cur: 0x7c, idx: 0x544}, + 856: {cur: 0x7d, idx: 0x0}, + 857: {cur: 0x85, idx: 0x0}, + 858: {cur: 0x8d, idx: 0x0}, + 859: {cur: 0xc0, idx: 0x0}, + 860: {cur: 0xf6, idx: 0x0}, + 861: {cur: 0xfc, idx: 0x0}, + 862: {cur: 0x105, idx: 0x0}, + 863: {cur: 0x13, idx: 0x0}, + 864: {cur: 0x2e, idx: 0x0}, + 865: {cur: 0x3a, idx: 0x0}, + 866: {cur: 0x63, idx: 0x0}, + 867: {cur: 0x85, idx: 0x25}, + 868: {cur: 0xb2, idx: 0x0}, + 869: {cur: 0xc0, idx: 0x0}, + 870: {cur: 0xf6, idx: 0x0}, + 871: {cur: 0xfc, idx: 0x4}, + 872: {cur: 0x110, idx: 0x0}, + 873: {cur: 0xe1, idx: 0x235}, + 874: {cur: 0x51, idx: 0x22d}, + 875: {cur: 0x5d, idx: 0x258}, + 876: {cur: 0x86, idx: 0x308}, + 877: {cur: 0x6, idx: 0x548}, + 878: {cur: 0xeb, idx: 0xbc}, + 879: {cur: 0xa5, idx: 0x54e}, + 880: {cur: 0x13, idx: 0x0}, + 881: {cur: 0x18, idx: 0x2cf}, + 882: {cur: 0x85, idx: 0x25}, + 883: {cur: 0x8d, idx: 0x0}, + 884: {cur: 0xc0, idx: 0x0}, + 885: {cur: 0x105, idx: 0x0}, + 886: {cur: 0x13, idx: 0x0}, + 887: {cur: 0x18, idx: 0x9}, + 888: {cur: 0x85, idx: 0x25}, + 889: {cur: 0x8d, idx: 0x0}, + 890: {cur: 0xc0, idx: 0x0}, + 891: {cur: 0x105, idx: 0x0}, + 892: {cur: 0x13, idx: 0x0}, + 893: {cur: 0x1a, idx: 0x24c}, + 894: {cur: 0x25, idx: 0x13a}, + 895: {cur: 0x2e, idx: 0x555}, + 896: {cur: 0x32, idx: 0x142}, + 897: {cur: 0x39, idx: 0x146}, + 898: {cur: 0x44, idx: 0x0}, + 899: {cur: 0x52, idx: 0x520}, + 900: {cur: 0x53, idx: 0x264}, + 901: {cur: 0x57, idx: 0x559}, + 902: {cur: 0x58, idx: 0x55d}, + 903: {cur: 0x63, idx: 0x0}, + 904: {cur: 0x72, idx: 0x0}, + 905: {cur: 0x79, idx: 0x562}, + 906: {cur: 0x7d, idx: 0x0}, + 907: {cur: 0x81, idx: 0x567}, + 908: {cur: 0x83, idx: 0x18c}, + 909: {cur: 0x85, idx: 0x0}, + 910: {cur: 0x8d, idx: 0x0}, + 911: {cur: 0xbe, idx: 0x528}, + 912: {cur: 0xc0, idx: 0x0}, + 913: {cur: 0xdb, idx: 0x30}, + 914: {cur: 0xf6, idx: 0x0}, + 915: {cur: 0x105, idx: 0x0}, + 916: {cur: 0x86, idx: 0x308}, + 917: {cur: 0xeb, idx: 0xbc}, + 918: {cur: 0xf7, idx: 0x23c}, + 919: {cur: 0x3b, idx: 0x32a}, + 920: {cur: 0xfb, idx: 0x2f4}, + 921: {cur: 0x85, idx: 0x25}, + 922: {cur: 0xeb, idx: 0xbc}, + 923: {cur: 0xfc, idx: 0x4}, + 924: {cur: 0x93, idx: 0x56b}, + 925: {cur: 0xb5, idx: 0x94}, + 926: {cur: 0xdc, idx: 0x28e}, + 927: {cur: 0xb5, idx: 0x94}, + 928: {cur: 0xdc, idx: 0x4}, + 929: {cur: 0xfc, idx: 0xcf}, + 930: {cur: 0xeb, idx: 0xbc}, + 931: {cur: 0xfc, idx: 0x4}, + 932: {cur: 0xfb, idx: 0x2f4}, + 933: {cur: 0x86, idx: 0x308}, + 934: {cur: 0xed, idx: 0x56f}, + 935: {cur: 0xfc, idx: 0x4}, + 936: {cur: 0x13, idx: 0xf8}, + 937: {cur: 0x85, idx: 0x25}, + 938: {cur: 0x5d, idx: 0x258}, + 939: {cur: 0x59, idx: 0x231}, + 940: {cur: 0x5e, idx: 0x0}, + 941: {cur: 0x63, idx: 0x0}, + 942: {cur: 0x13, idx: 0x577}, + 943: {cur: 0xc0, idx: 0x57c}, + 944: {cur: 0xf1, idx: 0xc0}, + 945: {cur: 0x13, idx: 0xf8}, + 946: {cur: 0x85, idx: 0x25}, + 947: {cur: 0xeb, idx: 0xbc}, + 948: {cur: 0xf4, idx: 0xc3}, + 949: {cur: 0xfc, idx: 0x4}, + 950: {cur: 0xd2, idx: 0xb2}, + 951: {cur: 0xfc, idx: 0x4}, + 952: {cur: 0x44, idx: 0x4a3}, + 953: {cur: 0xfc, idx: 0x4}, + 954: {cur: 0x13, idx: 0x0}, + 955: {cur: 0x2e, idx: 0x0}, + 956: {cur: 0x3a, idx: 0x0}, + 957: {cur: 0x44, idx: 0x0}, + 958: {cur: 0x5e, idx: 0x0}, + 959: {cur: 0x63, idx: 0x0}, + 960: {cur: 0x72, idx: 0x0}, + 961: {cur: 0x7c, idx: 0x0}, + 962: {cur: 0x7d, idx: 0x0}, + 963: {cur: 0x85, idx: 0x25}, + 964: {cur: 0x8d, idx: 0x0}, + 965: {cur: 0xb2, idx: 0x0}, + 966: {cur: 0xc0, idx: 0x0}, + 967: {cur: 0xf6, idx: 0x0}, + 968: {cur: 0xf8, idx: 0xcb}, + 969: {cur: 0xf9, idx: 0x581}, + 970: {cur: 0xfc, idx: 0x0}, + 971: {cur: 0x105, idx: 0x0}, + 972: {cur: 0x110, idx: 0x0}, + 973: {cur: 0xc8, idx: 0x7e}, + 974: {cur: 0xeb, idx: 0xbc}, + 975: {cur: 0xfc, idx: 0x4}, + 976: {cur: 0xc8, idx: 0x0}, + 977: {cur: 0x102, idx: 0x589}, + 978: {cur: 0x4, idx: 0x396}, + 979: {cur: 0xeb, idx: 0xbc}, + 980: {cur: 0x102, idx: 0x58f}, + 981: {cur: 0x94, idx: 0x4}, + 982: {cur: 0x94, idx: 0x4}, + 983: {cur: 0x13, idx: 0xf8}, + 984: {cur: 0xeb, idx: 0xbc}, + 985: {cur: 0xf7, idx: 0x23c}, + 986: {cur: 0x85, idx: 0x25}, + 987: {cur: 0xfc, idx: 0x4}, + 988: {cur: 0xfc, idx: 0x4}, + 989: {cur: 0xfb, idx: 0x2f4}, + 990: {cur: 0xba, idx: 0x97}, + 991: {cur: 0x13, idx: 0xf8}, + 992: {cur: 0x85, idx: 0x25}, + 993: {cur: 0x8d, idx: 0x596}, + 994: {cur: 0x13, idx: 0xf8}, + 995: {cur: 0x44, idx: 0x4a3}, + 996: {cur: 0x8d, idx: 0x596}, + 997: {cur: 0x13, idx: 0xf8}, + 998: {cur: 0x44, idx: 0x4a3}, + 999: {cur: 0x7b, idx: 0x59a}, + 1000: {cur: 0x8d, idx: 0x596}, + 1001: {cur: 0x44, idx: 0x20}, + 1002: {cur: 0x44, idx: 0x20}, + 1003: {cur: 0xaa, idx: 0x35f}, + 1004: {cur: 0x44, idx: 0x20}, + 1005: {cur: 0xdc, idx: 0x4}, + 1006: {cur: 0x13, idx: 0xf8}, + 1007: {cur: 0x85, idx: 0x25}, + 1008: {cur: 0x8d, idx: 0x596}, + 1009: {cur: 0xf6, idx: 0x4}, + 1010: {cur: 0x8d, idx: 0x6e}, + 1011: {cur: 0xf6, idx: 0xc7}, + 1012: {cur: 0xaa, idx: 0x35f}, + 1013: {cur: 0xeb, idx: 0xbc}, + 1014: {cur: 0x125, idx: 0xe9}, +} // Size: 4084 bytes + +var narrowLangIndex = []uint16{ // 776 elements + // Entry 0 - 3F + 0x0000, 0x0062, 0x0064, 0x0064, 0x0064, 0x0064, 0x0064, 0x0064, + 0x0064, 0x0065, 0x0065, 0x0081, 0x0081, 0x0082, 0x0082, 0x0082, + 0x0082, 0x0082, 0x0082, 0x0082, 0x0082, 0x0082, 0x0082, 0x0082, + 0x0082, 0x0082, 0x0082, 0x0082, 0x0082, 0x0082, 0x0082, 0x0082, + 0x0082, 0x0082, 0x0082, 0x0082, 0x0082, 0x0082, 0x0082, 0x0082, + 0x0082, 0x0082, 0x0082, 0x0082, 0x0082, 0x008b, 0x008b, 0x008e, + 0x008e, 0x008e, 0x008e, 0x008e, 0x008e, 0x008e, 0x00a8, 0x00a8, + 0x00a8, 0x00a8, 0x00a8, 0x00a8, 0x00d8, 0x00d8, 0x00d8, 0x00d8, + // Entry 40 - 7F + 0x00d8, 0x00d9, 0x00d9, 0x00d9, 0x00d9, 0x00d9, 0x00d9, 0x00dc, + 0x00dc, 0x00dc, 0x00dc, 0x00dd, 0x00dd, 0x00dd, 0x00dd, 0x00dd, + 0x00de, 0x00de, 0x00de, 0x00de, 0x00de, 0x00df, 0x00df, 0x00df, + 0x00e0, 0x00e0, 0x00e0, 0x00e0, 0x00e0, 0x00e0, 0x00e0, 0x00e0, + 0x00e0, 0x00e2, 0x00e2, 0x00e2, 0x00e2, 0x00e8, 0x00e8, 0x00ee, + 0x00ee, 0x00ee, 0x00ee, 0x00ee, 0x00f7, 0x00f7, 0x00f7, 0x00f8, + 0x00f8, 0x00f8, 0x00f8, 0x00f8, 0x00f8, 0x00f8, 0x00f8, 0x00f8, + 0x00f8, 0x00f8, 0x00f8, 0x00f8, 0x00f8, 0x00f8, 0x00f8, 0x00f8, + // Entry 80 - BF + 0x00f8, 0x00f8, 0x00f8, 0x00f8, 0x00f8, 0x00f8, 0x00f8, 0x00f8, + 0x00f8, 0x00f8, 0x00f8, 0x00f8, 0x00f8, 0x00f8, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + // Entry C0 - FF + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0103, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, + 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, + // Entry 100 - 13F + 0x0108, 0x0108, 0x0108, 0x0108, 0x010d, 0x010d, 0x010d, 0x010d, + 0x010d, 0x010d, 0x010d, 0x010d, 0x0111, 0x0111, 0x0112, 0x0113, + 0x0113, 0x0114, 0x0114, 0x0114, 0x0114, 0x0114, 0x0114, 0x0114, + 0x0114, 0x0114, 0x0114, 0x0114, 0x0114, 0x0171, 0x0171, 0x0172, + 0x0172, 0x0172, 0x0172, 0x0172, 0x017a, 0x017a, 0x017a, 0x017a, + 0x017a, 0x017a, 0x017f, 0x017f, 0x017f, 0x017f, 0x017f, 0x017f, + 0x017f, 0x017f, 0x017f, 0x017f, 0x017f, 0x017f, 0x017f, 0x017f, + 0x017f, 0x017f, 0x017f, 0x017f, 0x017f, 0x017f, 0x017f, 0x017f, + // Entry 140 - 17F + 0x017f, 0x017f, 0x017f, 0x017f, 0x017f, 0x017f, 0x017f, 0x017f, + 0x017f, 0x017f, 0x017f, 0x017f, 0x017f, 0x017f, 0x017f, 0x017f, + 0x017f, 0x017f, 0x017f, 0x017f, 0x017f, 0x017f, 0x017f, 0x0180, + 0x0180, 0x0182, 0x0182, 0x0185, 0x0185, 0x0185, 0x0185, 0x0185, + 0x0185, 0x0187, 0x0187, 0x0187, 0x0187, 0x0187, 0x0187, 0x0187, + 0x0187, 0x0187, 0x0187, 0x0187, 0x0187, 0x0187, 0x0188, 0x0188, + 0x018a, 0x018a, 0x018b, 0x018b, 0x018b, 0x018b, 0x018b, 0x018c, + 0x018c, 0x018d, 0x018d, 0x018e, 0x018e, 0x018e, 0x018e, 0x018e, + // Entry 180 - 1BF + 0x018e, 0x018e, 0x018e, 0x018f, 0x018f, 0x0193, 0x0193, 0x0193, + 0x0193, 0x0193, 0x0193, 0x0193, 0x0196, 0x0196, 0x0196, 0x0196, + 0x0196, 0x0196, 0x0196, 0x0196, 0x0196, 0x0196, 0x0197, 0x0197, + 0x0197, 0x0197, 0x0197, 0x0197, 0x0197, 0x0197, 0x0197, 0x0197, + 0x0197, 0x0197, 0x0197, 0x0197, 0x0197, 0x0197, 0x0198, 0x0198, + 0x0198, 0x0198, 0x0198, 0x0198, 0x0198, 0x0198, 0x0199, 0x0199, + 0x019b, 0x019b, 0x019d, 0x019d, 0x019d, 0x019d, 0x019d, 0x019d, + 0x019d, 0x019d, 0x019d, 0x019d, 0x019d, 0x019d, 0x019d, 0x019d, + // Entry 1C0 - 1FF + 0x019d, 0x019d, 0x01a8, 0x01a8, 0x01a8, 0x01a8, 0x01a9, 0x01a9, + 0x01a9, 0x01a9, 0x01a9, 0x01a9, 0x01a9, 0x01a9, 0x01a9, 0x01a9, + 0x01a9, 0x01aa, 0x01aa, 0x01aa, 0x01aa, 0x01aa, 0x01b5, 0x01b5, + 0x01b5, 0x01b5, 0x01b5, 0x01b5, 0x01b5, 0x01b5, 0x01b6, 0x01b6, + 0x01b6, 0x01b6, 0x01b6, 0x01b6, 0x01b6, 0x01b6, 0x01b6, 0x01b6, + 0x01b6, 0x01b6, 0x01b6, 0x01b6, 0x01b6, 0x01b7, 0x01b7, 0x01b8, + 0x01b8, 0x01ba, 0x01ba, 0x01ba, 0x01bb, 0x01bb, 0x01bc, 0x01bc, + 0x01bc, 0x01bc, 0x01bc, 0x01bc, 0x01bc, 0x01bc, 0x01be, 0x01be, + // Entry 200 - 23F + 0x01be, 0x01be, 0x01be, 0x01be, 0x01be, 0x01c0, 0x01c0, 0x01c0, + 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c0, 0x01c1, 0x01c1, 0x01c1, + 0x01c2, 0x01c2, 0x01c2, 0x01c2, 0x01c2, 0x01c2, 0x01c2, 0x01c2, + 0x01c2, 0x01c2, 0x01c2, 0x01c2, 0x01c2, 0x01c2, 0x01c2, 0x01c2, + 0x01c2, 0x01c2, 0x01c2, 0x01c2, 0x01c2, 0x01c2, 0x01c2, 0x01c2, + 0x01c2, 0x01c2, 0x01c2, 0x01c2, 0x01c2, 0x01c2, 0x01c2, 0x01c3, + 0x01c3, 0x01c3, 0x01c3, 0x01c3, 0x01c3, 0x01c5, 0x01c5, 0x01c5, + 0x01c5, 0x01c5, 0x01c5, 0x01c7, 0x01c7, 0x01c7, 0x01c7, 0x01c7, + // Entry 240 - 27F + 0x01c7, 0x01c7, 0x01c7, 0x01c7, 0x01c7, 0x01c7, 0x01c7, 0x01c7, + 0x01c7, 0x01c7, 0x01c7, 0x01c7, 0x01c7, 0x01c7, 0x01c7, 0x01c7, + 0x01c8, 0x01c8, 0x01c8, 0x01c8, 0x01c8, 0x01cb, 0x01cc, 0x01cc, + 0x01cc, 0x01cc, 0x01cc, 0x01cc, 0x01cc, 0x01cc, 0x01cc, 0x01cc, + 0x01cc, 0x01cc, 0x01cc, 0x01cc, 0x01cc, 0x01cc, 0x01cc, 0x01cc, + 0x01cc, 0x01cc, 0x01cc, 0x01cc, 0x01cc, 0x01cc, 0x01cc, 0x01cc, + 0x01cc, 0x01cc, 0x01cc, 0x01cc, 0x01cc, 0x01cc, 0x01cc, 0x01cc, + 0x01cc, 0x01ce, 0x01ce, 0x01cf, 0x01cf, 0x01d0, 0x01d0, 0x01d0, + // Entry 280 - 2BF + 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, + 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, 0x01d0, + 0x01d2, 0x01d2, 0x01d2, 0x01d2, 0x01d2, 0x01d2, 0x01d5, 0x01d5, + 0x01d5, 0x01d5, 0x01d5, 0x01d5, 0x01d5, 0x01d5, 0x01d8, 0x01d8, + 0x01d8, 0x01d8, 0x01d9, 0x01d9, 0x01d9, 0x01d9, 0x01d9, 0x01d9, + 0x01da, 0x01da, 0x01da, 0x01da, 0x01da, 0x01db, 0x01db, 0x01db, + 0x01db, 0x01db, 0x01db, 0x01db, 0x01dc, 0x01dc, 0x01dc, 0x01dc, + 0x01dc, 0x01dc, 0x01dc, 0x01dc, 0x01dc, 0x01dc, 0x01dc, 0x01dc, + // Entry 2C0 - 2FF + 0x01de, 0x01de, 0x01de, 0x01de, 0x01de, 0x01de, 0x01de, 0x01de, + 0x01de, 0x01de, 0x01de, 0x01de, 0x01df, 0x01df, 0x01e0, 0x01e0, + 0x01e0, 0x01e0, 0x01e0, 0x01e0, 0x01e0, 0x01e0, 0x01e0, 0x01e0, + 0x01e0, 0x01e0, 0x01e0, 0x01e0, 0x01e0, 0x01e0, 0x01e1, 0x01e1, + 0x01e1, 0x01e1, 0x01e1, 0x01e1, 0x01e1, 0x01e1, 0x01e1, 0x01e1, + 0x01e1, 0x01e1, 0x01e1, 0x01e1, 0x01e1, 0x01e1, 0x01e1, 0x01e1, + 0x01e1, 0x01e1, 0x01e1, 0x01e1, 0x01e1, 0x01e1, 0x01e1, 0x01e1, + 0x01e1, 0x01e1, 0x01e2, 0x01e2, 0x01e2, 0x01e2, 0x01e2, 0x01e2, + // Entry 300 - 33F + 0x01e3, 0x01e3, 0x01e3, 0x01e3, 0x01eb, 0x01eb, 0x01eb, 0x01eb, +} // Size: 1576 bytes + +var narrowSymIndex = []curToIndex{ // 491 elements + 0: {cur: 0x9, idx: 0x1}, + 1: {cur: 0x11, idx: 0x4}, + 2: {cur: 0x13, idx: 0x4}, + 3: {cur: 0x18, idx: 0x9}, + 4: {cur: 0x1a, idx: 0x4}, + 5: {cur: 0x1b, idx: 0xc}, + 6: {cur: 0x25, idx: 0x4}, + 7: {cur: 0x26, idx: 0x4}, + 8: {cur: 0x27, idx: 0x10}, + 9: {cur: 0x2e, idx: 0x13}, + 10: {cur: 0x32, idx: 0x4}, + 11: {cur: 0x35, idx: 0x16}, + 12: {cur: 0x37, idx: 0x18}, + 13: {cur: 0x39, idx: 0x4}, + 14: {cur: 0x3a, idx: 0x4}, + 15: {cur: 0x41, idx: 0x4}, + 16: {cur: 0x44, idx: 0x25}, + 17: {cur: 0x45, idx: 0x4}, + 18: {cur: 0x47, idx: 0x28}, + 19: {cur: 0x4a, idx: 0x4}, + 20: {cur: 0x4b, idx: 0x4}, + 21: {cur: 0x4e, idx: 0x2c}, + 22: {cur: 0x52, idx: 0x30}, + 23: {cur: 0x53, idx: 0x4}, + 24: {cur: 0x58, idx: 0x33}, + 25: {cur: 0x5c, idx: 0x37}, + 26: {cur: 0x5e, idx: 0x3b}, + 27: {cur: 0x60, idx: 0x4}, + 28: {cur: 0x61, idx: 0x3f}, + 29: {cur: 0x63, idx: 0x3f}, + 30: {cur: 0x65, idx: 0x42}, + 31: {cur: 0x68, idx: 0x3f}, + 32: {cur: 0x6a, idx: 0x46}, + 33: {cur: 0x6e, idx: 0x49}, + 34: {cur: 0x71, idx: 0x4}, + 35: {cur: 0x72, idx: 0x4}, + 36: {cur: 0x73, idx: 0x4f}, + 37: {cur: 0x75, idx: 0x51}, + 38: {cur: 0x77, idx: 0x54}, + 39: {cur: 0x78, idx: 0x57}, + 40: {cur: 0x7c, idx: 0x5a}, + 41: {cur: 0x7d, idx: 0x5e}, + 42: {cur: 0x81, idx: 0x30}, + 43: {cur: 0x83, idx: 0x4}, + 44: {cur: 0x85, idx: 0x25}, + 45: {cur: 0x88, idx: 0x67}, + 46: {cur: 0x89, idx: 0x6b}, + 47: {cur: 0x8a, idx: 0x6e}, + 48: {cur: 0x8d, idx: 0x6e}, + 49: {cur: 0x8f, idx: 0x4}, + 50: {cur: 0x90, idx: 0x72}, + 51: {cur: 0x91, idx: 0x76}, + 52: {cur: 0x92, idx: 0x7a}, + 53: {cur: 0x93, idx: 0x7e}, + 54: {cur: 0x94, idx: 0x4}, + 55: {cur: 0x96, idx: 0x81}, + 56: {cur: 0x9b, idx: 0x84}, + 57: {cur: 0xa3, idx: 0x87}, + 58: {cur: 0xa8, idx: 0x8a}, + 59: {cur: 0xa9, idx: 0x8c}, + 60: {cur: 0xae, idx: 0x7e}, + 61: {cur: 0xb2, idx: 0x4}, + 62: {cur: 0xb5, idx: 0x94}, + 63: {cur: 0xb9, idx: 0x4}, + 64: {cur: 0xba, idx: 0x97}, + 65: {cur: 0xbc, idx: 0x9b}, + 66: {cur: 0xbe, idx: 0x30}, + 67: {cur: 0xbf, idx: 0x7e}, + 68: {cur: 0xc0, idx: 0x4}, + 69: {cur: 0xc7, idx: 0xa2}, + 70: {cur: 0xc8, idx: 0x7e}, + 71: {cur: 0xc9, idx: 0xa6}, + 72: {cur: 0xcc, idx: 0xaa}, + 73: {cur: 0xd0, idx: 0xae}, + 74: {cur: 0xd2, idx: 0xb2}, + 75: {cur: 0xd3, idx: 0x18}, + 76: {cur: 0xd4, idx: 0xb6}, + 77: {cur: 0xd6, idx: 0x4}, + 78: {cur: 0xdb, idx: 0x30}, + 79: {cur: 0xdc, idx: 0x4}, + 80: {cur: 0xdd, idx: 0x3f}, + 81: {cur: 0xe2, idx: 0x4}, + 82: {cur: 0xe4, idx: 0x3f}, + 83: {cur: 0xe5, idx: 0xb9}, + 84: {cur: 0xe9, idx: 0x3f}, + 85: {cur: 0xeb, idx: 0xbc}, + 86: {cur: 0xf1, idx: 0xc0}, + 87: {cur: 0xf4, idx: 0xc3}, + 88: {cur: 0xf5, idx: 0x4}, + 89: {cur: 0xf6, idx: 0x4}, + 90: {cur: 0xf8, idx: 0xcb}, + 91: {cur: 0xfc, idx: 0x4}, + 92: {cur: 0x101, idx: 0x4}, + 93: {cur: 0x104, idx: 0x10}, + 94: {cur: 0x105, idx: 0xd3}, + 95: {cur: 0x110, idx: 0x4}, + 96: {cur: 0x125, idx: 0xe9}, + 97: {cur: 0x127, idx: 0xeb}, + 98: {cur: 0xd0, idx: 0xee}, + 99: {cur: 0xf6, idx: 0xc7}, + 100: {cur: 0xf6, idx: 0xc7}, + 101: {cur: 0x11, idx: 0x128}, + 102: {cur: 0x13, idx: 0xf8}, + 103: {cur: 0x1a, idx: 0x12c}, + 104: {cur: 0x25, idx: 0x13a}, + 105: {cur: 0x26, idx: 0x13e}, + 106: {cur: 0x32, idx: 0x142}, + 107: {cur: 0x39, idx: 0x146}, + 108: {cur: 0x3a, idx: 0x1c}, + 109: {cur: 0x41, idx: 0x14a}, + 110: {cur: 0x44, idx: 0x20}, + 111: {cur: 0x45, idx: 0x14e}, + 112: {cur: 0x4b, idx: 0x152}, + 113: {cur: 0x53, idx: 0x156}, + 114: {cur: 0x60, idx: 0x16e}, + 115: {cur: 0x63, idx: 0x172}, + 116: {cur: 0x71, idx: 0x177}, + 117: {cur: 0x72, idx: 0x4b}, + 118: {cur: 0x83, idx: 0x18c}, + 119: {cur: 0x85, idx: 0x62}, + 120: {cur: 0x8f, idx: 0x1a4}, + 121: {cur: 0xb2, idx: 0x90}, + 122: {cur: 0xc0, idx: 0x9e}, + 123: {cur: 0xd6, idx: 0x1ee}, + 124: {cur: 0xe2, idx: 0x203}, + 125: {cur: 0xf5, idx: 0x21b}, + 126: {cur: 0xf6, idx: 0xc7}, + 127: {cur: 0xfc, idx: 0xcf}, + 128: {cur: 0x101, idx: 0x21f}, + 129: {cur: 0x26, idx: 0x4}, + 130: {cur: 0x37, idx: 0x0}, + 131: {cur: 0x52, idx: 0x0}, + 132: {cur: 0x75, idx: 0x0}, + 133: {cur: 0x81, idx: 0x0}, + 134: {cur: 0xbe, idx: 0x0}, + 135: {cur: 0xc9, idx: 0x0}, + 136: {cur: 0xd3, idx: 0x0}, + 137: {cur: 0xdb, idx: 0x0}, + 138: {cur: 0xf6, idx: 0xc7}, + 139: {cur: 0xd0, idx: 0x244}, + 140: {cur: 0xe9, idx: 0x248}, + 141: {cur: 0xf6, idx: 0xc7}, + 142: {cur: 0x13, idx: 0x6}, + 143: {cur: 0x1a, idx: 0x24c}, + 144: {cur: 0x25, idx: 0x251}, + 145: {cur: 0x32, idx: 0x255}, + 146: {cur: 0x37, idx: 0x258}, + 147: {cur: 0x39, idx: 0x146}, + 148: {cur: 0x3a, idx: 0x1c}, + 149: {cur: 0x4a, idx: 0x25b}, + 150: {cur: 0x4b, idx: 0x260}, + 151: {cur: 0x53, idx: 0x264}, + 152: {cur: 0x60, idx: 0x16e}, + 153: {cur: 0x61, idx: 0x268}, + 154: {cur: 0x71, idx: 0x26d}, + 155: {cur: 0x81, idx: 0x270}, + 156: {cur: 0x83, idx: 0x275}, + 157: {cur: 0x8f, idx: 0x278}, + 158: {cur: 0x94, idx: 0x27c}, + 159: {cur: 0xb2, idx: 0x90}, + 160: {cur: 0xb9, idx: 0x27f}, + 161: {cur: 0xc0, idx: 0x9e}, + 162: {cur: 0xd2, idx: 0x282}, + 163: {cur: 0xd6, idx: 0x28a}, + 164: {cur: 0xdc, idx: 0x28e}, + 165: {cur: 0xf5, idx: 0x21b}, + 166: {cur: 0x101, idx: 0x291}, + 167: {cur: 0x110, idx: 0xdc}, + 168: {cur: 0x11, idx: 0x0}, + 169: {cur: 0x13, idx: 0x0}, + 170: {cur: 0x1a, idx: 0x0}, + 171: {cur: 0x1b, idx: 0x0}, + 172: {cur: 0x25, idx: 0x0}, + 173: {cur: 0x26, idx: 0x0}, + 174: {cur: 0x2e, idx: 0x0}, + 175: {cur: 0x32, idx: 0x0}, + 176: {cur: 0x37, idx: 0x0}, + 177: {cur: 0x39, idx: 0x0}, + 178: {cur: 0x3a, idx: 0x0}, + 179: {cur: 0x41, idx: 0x0}, + 180: {cur: 0x44, idx: 0x0}, + 181: {cur: 0x45, idx: 0x0}, + 182: {cur: 0x47, idx: 0x0}, + 183: {cur: 0x4b, idx: 0x0}, + 184: {cur: 0x53, idx: 0x0}, + 185: {cur: 0x60, idx: 0x0}, + 186: {cur: 0x68, idx: 0x0}, + 187: {cur: 0x71, idx: 0x0}, + 188: {cur: 0x72, idx: 0x0}, + 189: {cur: 0x7c, idx: 0x0}, + 190: {cur: 0x7d, idx: 0x0}, + 191: {cur: 0x83, idx: 0x0}, + 192: {cur: 0x88, idx: 0x0}, + 193: {cur: 0x8d, idx: 0x0}, + 194: {cur: 0x8f, idx: 0x0}, + 195: {cur: 0x90, idx: 0x0}, + 196: {cur: 0x91, idx: 0x0}, + 197: {cur: 0x94, idx: 0x0}, + 198: {cur: 0xa9, idx: 0x0}, + 199: {cur: 0xb2, idx: 0x0}, + 200: {cur: 0xb9, idx: 0x0}, + 201: {cur: 0xba, idx: 0x0}, + 202: {cur: 0xc0, idx: 0x0}, + 203: {cur: 0xc7, idx: 0x0}, + 204: {cur: 0xcc, idx: 0x0}, + 205: {cur: 0xd0, idx: 0x0}, + 206: {cur: 0xd6, idx: 0x0}, + 207: {cur: 0xdc, idx: 0x0}, + 208: {cur: 0xe2, idx: 0x0}, + 209: {cur: 0xe4, idx: 0x0}, + 210: {cur: 0xf4, idx: 0x0}, + 211: {cur: 0xf5, idx: 0x0}, + 212: {cur: 0xf6, idx: 0x0}, + 213: {cur: 0xf8, idx: 0x0}, + 214: {cur: 0x101, idx: 0x0}, + 215: {cur: 0x105, idx: 0x0}, + 216: {cur: 0xf6, idx: 0xc7}, + 217: {cur: 0x58, idx: 0x2a8}, + 218: {cur: 0x92, idx: 0x2b8}, + 219: {cur: 0xf1, idx: 0x2c1}, + 220: {cur: 0xf6, idx: 0xc7}, + 221: {cur: 0x104, idx: 0x0}, + 222: {cur: 0xf6, idx: 0xc7}, + 223: {cur: 0xd0, idx: 0x2ed}, + 224: {cur: 0xd0, idx: 0x4f}, + 225: {cur: 0xf6, idx: 0xc7}, + 226: {cur: 0x1b, idx: 0x301}, + 227: {cur: 0x35, idx: 0x0}, + 228: {cur: 0x72, idx: 0x4b}, + 229: {cur: 0xf6, idx: 0xc7}, + 230: {cur: 0x125, idx: 0x0}, + 231: {cur: 0x127, idx: 0x0}, + 232: {cur: 0x52, idx: 0x304}, + 233: {cur: 0x81, idx: 0x304}, + 234: {cur: 0xbe, idx: 0x304}, + 235: {cur: 0xd0, idx: 0x4f}, + 236: {cur: 0xdb, idx: 0x304}, + 237: {cur: 0xf6, idx: 0xc7}, + 238: {cur: 0x4a, idx: 0x318}, + 239: {cur: 0x61, idx: 0x320}, + 240: {cur: 0x6a, idx: 0x325}, + 241: {cur: 0x89, idx: 0x32a}, + 242: {cur: 0xd0, idx: 0x4f}, + 243: {cur: 0xd4, idx: 0x32d}, + 244: {cur: 0xe9, idx: 0x0}, + 245: {cur: 0xf6, idx: 0xc7}, + 246: {cur: 0x127, idx: 0x8a}, + 247: {cur: 0x5e, idx: 0x0}, + 248: {cur: 0x1b, idx: 0x349}, + 249: {cur: 0x27, idx: 0x34c}, + 250: {cur: 0x4b, idx: 0xa2}, + 251: {cur: 0x58, idx: 0x3f}, + 252: {cur: 0x81, idx: 0x34f}, + 253: {cur: 0xcc, idx: 0x352}, + 254: {cur: 0xdb, idx: 0x34f}, + 255: {cur: 0x101, idx: 0x291}, + 256: {cur: 0x58, idx: 0x0}, + 257: {cur: 0xd0, idx: 0x4f}, + 258: {cur: 0xf6, idx: 0xc7}, + 259: {cur: 0x58, idx: 0x33}, + 260: {cur: 0x61, idx: 0x268}, + 261: {cur: 0xe4, idx: 0x37b}, + 262: {cur: 0xe9, idx: 0x248}, + 263: {cur: 0x104, idx: 0x380}, + 264: {cur: 0x37, idx: 0x384}, + 265: {cur: 0x61, idx: 0x3f}, + 266: {cur: 0xd0, idx: 0xae}, + 267: {cur: 0xe4, idx: 0x3f}, + 268: {cur: 0xe9, idx: 0x3f}, + 269: {cur: 0x61, idx: 0x3f}, + 270: {cur: 0xd0, idx: 0xae}, + 271: {cur: 0xe4, idx: 0x3f}, + 272: {cur: 0xe9, idx: 0x3f}, + 273: {cur: 0x104, idx: 0x392}, + 274: {cur: 0xf6, idx: 0xc7}, + 275: {cur: 0xf6, idx: 0xc7}, + 276: {cur: 0x9, idx: 0x0}, + 277: {cur: 0x11, idx: 0x0}, + 278: {cur: 0x13, idx: 0x0}, + 279: {cur: 0x18, idx: 0x0}, + 280: {cur: 0x1a, idx: 0x0}, + 281: {cur: 0x1b, idx: 0x0}, + 282: {cur: 0x25, idx: 0x0}, + 283: {cur: 0x26, idx: 0x0}, + 284: {cur: 0x27, idx: 0x0}, + 285: {cur: 0x2e, idx: 0x0}, + 286: {cur: 0x32, idx: 0x0}, + 287: {cur: 0x35, idx: 0x0}, + 288: {cur: 0x37, idx: 0x0}, + 289: {cur: 0x39, idx: 0x0}, + 290: {cur: 0x3a, idx: 0x0}, + 291: {cur: 0x41, idx: 0x0}, + 292: {cur: 0x44, idx: 0x0}, + 293: {cur: 0x45, idx: 0x0}, + 294: {cur: 0x47, idx: 0x0}, + 295: {cur: 0x4a, idx: 0x0}, + 296: {cur: 0x4b, idx: 0x0}, + 297: {cur: 0x4e, idx: 0x0}, + 298: {cur: 0x52, idx: 0x0}, + 299: {cur: 0x53, idx: 0x0}, + 300: {cur: 0x58, idx: 0x0}, + 301: {cur: 0x5c, idx: 0x0}, + 302: {cur: 0x60, idx: 0x0}, + 303: {cur: 0x61, idx: 0x0}, + 304: {cur: 0x65, idx: 0x0}, + 305: {cur: 0x68, idx: 0x0}, + 306: {cur: 0x6a, idx: 0x0}, + 307: {cur: 0x6e, idx: 0x0}, + 308: {cur: 0x71, idx: 0x0}, + 309: {cur: 0x72, idx: 0x0}, + 310: {cur: 0x73, idx: 0x0}, + 311: {cur: 0x75, idx: 0x0}, + 312: {cur: 0x77, idx: 0x0}, + 313: {cur: 0x78, idx: 0x0}, + 314: {cur: 0x7c, idx: 0x0}, + 315: {cur: 0x7d, idx: 0x0}, + 316: {cur: 0x81, idx: 0x0}, + 317: {cur: 0x83, idx: 0x0}, + 318: {cur: 0x88, idx: 0x0}, + 319: {cur: 0x89, idx: 0x0}, + 320: {cur: 0x8a, idx: 0x0}, + 321: {cur: 0x8d, idx: 0x0}, + 322: {cur: 0x8f, idx: 0x0}, + 323: {cur: 0x90, idx: 0x0}, + 324: {cur: 0x91, idx: 0x0}, + 325: {cur: 0x92, idx: 0x0}, + 326: {cur: 0x93, idx: 0x0}, + 327: {cur: 0x94, idx: 0x0}, + 328: {cur: 0x96, idx: 0x0}, + 329: {cur: 0x9b, idx: 0x0}, + 330: {cur: 0xa3, idx: 0x0}, + 331: {cur: 0xa8, idx: 0x0}, + 332: {cur: 0xa9, idx: 0x0}, + 333: {cur: 0xae, idx: 0x0}, + 334: {cur: 0xb2, idx: 0x0}, + 335: {cur: 0xb5, idx: 0x0}, + 336: {cur: 0xb9, idx: 0x0}, + 337: {cur: 0xba, idx: 0x0}, + 338: {cur: 0xbc, idx: 0x0}, + 339: {cur: 0xbe, idx: 0x0}, + 340: {cur: 0xbf, idx: 0x0}, + 341: {cur: 0xc0, idx: 0x0}, + 342: {cur: 0xc7, idx: 0x0}, + 343: {cur: 0xc8, idx: 0x0}, + 344: {cur: 0xc9, idx: 0x0}, + 345: {cur: 0xcc, idx: 0x0}, + 346: {cur: 0xd0, idx: 0x0}, + 347: {cur: 0xd3, idx: 0x0}, + 348: {cur: 0xd4, idx: 0x0}, + 349: {cur: 0xd6, idx: 0x0}, + 350: {cur: 0xdb, idx: 0x0}, + 351: {cur: 0xdc, idx: 0x0}, + 352: {cur: 0xdd, idx: 0x0}, + 353: {cur: 0xe2, idx: 0x0}, + 354: {cur: 0xe4, idx: 0x0}, + 355: {cur: 0xe5, idx: 0x0}, + 356: {cur: 0xe9, idx: 0x0}, + 357: {cur: 0xeb, idx: 0x0}, + 358: {cur: 0xf1, idx: 0x0}, + 359: {cur: 0xf4, idx: 0x0}, + 360: {cur: 0xf5, idx: 0x0}, + 361: {cur: 0xf6, idx: 0x0}, + 362: {cur: 0xf8, idx: 0x0}, + 363: {cur: 0x101, idx: 0x0}, + 364: {cur: 0x104, idx: 0x0}, + 365: {cur: 0x105, idx: 0x0}, + 366: {cur: 0x110, idx: 0x0}, + 367: {cur: 0x125, idx: 0x0}, + 368: {cur: 0x127, idx: 0x0}, + 369: {cur: 0xf6, idx: 0xc7}, + 370: {cur: 0x58, idx: 0x3e5}, + 371: {cur: 0x89, idx: 0x32a}, + 372: {cur: 0x92, idx: 0x2b8}, + 373: {cur: 0xbc, idx: 0x41a}, + 374: {cur: 0xd0, idx: 0x4f}, + 375: {cur: 0xd4, idx: 0x421}, + 376: {cur: 0xf6, idx: 0xc7}, + 377: {cur: 0x127, idx: 0x441}, + 378: {cur: 0x37, idx: 0x258}, + 379: {cur: 0x65, idx: 0x0}, + 380: {cur: 0x89, idx: 0x6b}, + 381: {cur: 0xbc, idx: 0x9b}, + 382: {cur: 0x127, idx: 0xeb}, + 383: {cur: 0xf6, idx: 0xc7}, + 384: {cur: 0xd0, idx: 0xee}, + 385: {cur: 0xf6, idx: 0xc7}, + 386: {cur: 0x89, idx: 0x32a}, + 387: {cur: 0xd2, idx: 0x46d}, + 388: {cur: 0xf6, idx: 0xc7}, + 389: {cur: 0xae, idx: 0x474}, + 390: {cur: 0xf6, idx: 0xc7}, + 391: {cur: 0xf6, idx: 0xc7}, + 392: {cur: 0xd0, idx: 0x48e}, + 393: {cur: 0xf6, idx: 0xc7}, + 394: {cur: 0xf6, idx: 0xc7}, + 395: {cur: 0xf6, idx: 0xc7}, + 396: {cur: 0xf6, idx: 0xc7}, + 397: {cur: 0xf6, idx: 0xc7}, + 398: {cur: 0xf6, idx: 0xc7}, + 399: {cur: 0x37, idx: 0x258}, + 400: {cur: 0x58, idx: 0x3e5}, + 401: {cur: 0xbe, idx: 0x49b}, + 402: {cur: 0xf6, idx: 0xc7}, + 403: {cur: 0x44, idx: 0x4a3}, + 404: {cur: 0x85, idx: 0x4a3}, + 405: {cur: 0xd0, idx: 0x4a7}, + 406: {cur: 0xf6, idx: 0xc7}, + 407: {cur: 0xf6, idx: 0xc7}, + 408: {cur: 0xf6, idx: 0xc7}, + 409: {cur: 0xd0, idx: 0x4b2}, + 410: {cur: 0xf6, idx: 0xc7}, + 411: {cur: 0xd0, idx: 0x4f}, + 412: {cur: 0xf6, idx: 0xc7}, + 413: {cur: 0x25, idx: 0x251}, + 414: {cur: 0x32, idx: 0x255}, + 415: {cur: 0x39, idx: 0x146}, + 416: {cur: 0x3a, idx: 0x9b}, + 417: {cur: 0x53, idx: 0x264}, + 418: {cur: 0x58, idx: 0x4b9}, + 419: {cur: 0x72, idx: 0x4b}, + 420: {cur: 0x75, idx: 0x4bc}, + 421: {cur: 0x83, idx: 0x275}, + 422: {cur: 0xf5, idx: 0x21b}, + 423: {cur: 0xf6, idx: 0xc7}, + 424: {cur: 0xf6, idx: 0xc7}, + 425: {cur: 0xf6, idx: 0xc7}, + 426: {cur: 0x1b, idx: 0x0}, + 427: {cur: 0x37, idx: 0x258}, + 428: {cur: 0x7c, idx: 0x0}, + 429: {cur: 0x7d, idx: 0x0}, + 430: {cur: 0x88, idx: 0x0}, + 431: {cur: 0x91, idx: 0x0}, + 432: {cur: 0xa9, idx: 0x0}, + 433: {cur: 0xc9, idx: 0x4c6}, + 434: {cur: 0xcc, idx: 0x352}, + 435: {cur: 0xd2, idx: 0x4c9}, + 436: {cur: 0x105, idx: 0x0}, + 437: {cur: 0xf6, idx: 0xc7}, + 438: {cur: 0xf6, idx: 0xc7}, + 439: {cur: 0xf6, idx: 0xc7}, + 440: {cur: 0xdb, idx: 0x4d7}, + 441: {cur: 0xf6, idx: 0xc7}, + 442: {cur: 0xf6, idx: 0xc7}, + 443: {cur: 0xf6, idx: 0xc7}, + 444: {cur: 0x1a, idx: 0x24c}, + 445: {cur: 0x32, idx: 0x255}, + 446: {cur: 0xd0, idx: 0x4f}, + 447: {cur: 0xf6, idx: 0xc7}, + 448: {cur: 0xbf, idx: 0x4f1}, + 449: {cur: 0xf6, idx: 0xc7}, + 450: {cur: 0xf6, idx: 0xc7}, + 451: {cur: 0xd0, idx: 0x500}, + 452: {cur: 0xf6, idx: 0xc7}, + 453: {cur: 0xd0, idx: 0x4f}, + 454: {cur: 0xf6, idx: 0xc7}, + 455: {cur: 0xf6, idx: 0xc7}, + 456: {cur: 0x65, idx: 0x515}, + 457: {cur: 0xd0, idx: 0x4f}, + 458: {cur: 0xf6, idx: 0xc7}, + 459: {cur: 0x37, idx: 0x258}, + 460: {cur: 0x93, idx: 0x52c}, + 461: {cur: 0xf6, idx: 0xc7}, + 462: {cur: 0xf6, idx: 0xc7}, + 463: {cur: 0xf6, idx: 0xc7}, + 464: {cur: 0x65, idx: 0x515}, + 465: {cur: 0xf6, idx: 0xc7}, + 466: {cur: 0x37, idx: 0x552}, + 467: {cur: 0x65, idx: 0x515}, + 468: {cur: 0xf6, idx: 0xc7}, + 469: {cur: 0x5c, idx: 0x0}, + 470: {cur: 0xd0, idx: 0x4f}, + 471: {cur: 0xf6, idx: 0xc7}, + 472: {cur: 0xf6, idx: 0xc7}, + 473: {cur: 0xf6, idx: 0xc7}, + 474: {cur: 0xf6, idx: 0xc7}, + 475: {cur: 0xf6, idx: 0xc7}, + 476: {cur: 0xd0, idx: 0x4f}, + 477: {cur: 0xf6, idx: 0xc7}, + 478: {cur: 0xf6, idx: 0xc7}, + 479: {cur: 0xf6, idx: 0xc7}, + 480: {cur: 0xf6, idx: 0xc7}, + 481: {cur: 0xf6, idx: 0xc7}, + 482: {cur: 0xd0, idx: 0x4f}, + 483: {cur: 0x37, idx: 0x59e}, + 484: {cur: 0x52, idx: 0x34f}, + 485: {cur: 0x75, idx: 0x4bc}, + 486: {cur: 0x81, idx: 0x34f}, + 487: {cur: 0xbe, idx: 0x34f}, + 488: {cur: 0xc9, idx: 0x5a1}, + 489: {cur: 0xdb, idx: 0x34f}, + 490: {cur: 0xf6, idx: 0xc7}, +} // Size: 1988 bytes + +// Total table size 18885 bytes (18KiB); checksum: BE08FD0B diff --git a/vendor/golang.org/x/text/internal/format/format.go b/vendor/golang.org/x/text/internal/format/format.go new file mode 100644 index 0000000000..ee1c57a3c5 --- /dev/null +++ b/vendor/golang.org/x/text/internal/format/format.go @@ -0,0 +1,41 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package format contains types for defining language-specific formatting of +// values. +// +// This package is internal now, but will eventually be exposed after the API +// settles. +package format // import "golang.org/x/text/internal/format" + +import ( + "fmt" + + "golang.org/x/text/language" +) + +// State represents the printer state passed to custom formatters. It provides +// access to the fmt.State interface and the sentence and language-related +// context. +type State interface { + fmt.State + + // Language reports the requested language in which to render a message. + Language() language.Tag + + // TODO: consider this and removing rune from the Format method in the + // Formatter interface. + // + // Verb returns the format variant to render, analogous to the types used + // in fmt. Use 'v' for the default or only variant. + // Verb() rune + + // TODO: more info: + // - sentence context such as linguistic features passed by the translator. +} + +// Formatter is analogous to fmt.Formatter. +type Formatter interface { + Format(state State, verb rune) +} diff --git a/vendor/golang.org/x/text/internal/format/parser.go b/vendor/golang.org/x/text/internal/format/parser.go new file mode 100644 index 0000000000..855aed71db --- /dev/null +++ b/vendor/golang.org/x/text/internal/format/parser.go @@ -0,0 +1,358 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package format + +import ( + "reflect" + "unicode/utf8" +) + +// A Parser parses a format string. The result from the parse are set in the +// struct fields. +type Parser struct { + Verb rune + + WidthPresent bool + PrecPresent bool + Minus bool + Plus bool + Sharp bool + Space bool + Zero bool + + // For the formats %+v %#v, we set the plusV/sharpV flags + // and clear the plus/sharp flags since %+v and %#v are in effect + // different, flagless formats set at the top level. + PlusV bool + SharpV bool + + HasIndex bool + + Width int + Prec int // precision + + // retain arguments across calls. + Args []interface{} + // retain current argument number across calls + ArgNum int + + // reordered records whether the format string used argument reordering. + Reordered bool + // goodArgNum records whether the most recent reordering directive was valid. + goodArgNum bool + + // position info + format string + startPos int + endPos int + Status Status +} + +// Reset initializes a parser to scan format strings for the given args. +func (p *Parser) Reset(args []interface{}) { + p.Args = args + p.ArgNum = 0 + p.startPos = 0 + p.Reordered = false +} + +// Text returns the part of the format string that was parsed by the last call +// to Scan. It returns the original substitution clause if the current scan +// parsed a substitution. +func (p *Parser) Text() string { return p.format[p.startPos:p.endPos] } + +// SetFormat sets a new format string to parse. It does not reset the argument +// count. +func (p *Parser) SetFormat(format string) { + p.format = format + p.startPos = 0 + p.endPos = 0 +} + +// Status indicates the result type of a call to Scan. +type Status int + +const ( + StatusText Status = iota + StatusSubstitution + StatusBadWidthSubstitution + StatusBadPrecSubstitution + StatusNoVerb + StatusBadArgNum + StatusMissingArg +) + +// ClearFlags reset the parser to default behavior. +func (p *Parser) ClearFlags() { + p.WidthPresent = false + p.PrecPresent = false + p.Minus = false + p.Plus = false + p.Sharp = false + p.Space = false + p.Zero = false + + p.PlusV = false + p.SharpV = false + + p.HasIndex = false +} + +// Scan scans the next part of the format string and sets the status to +// indicate whether it scanned a string literal, substitution or error. +func (p *Parser) Scan() bool { + p.Status = StatusText + format := p.format + end := len(format) + if p.endPos >= end { + return false + } + afterIndex := false // previous item in format was an index like [3]. + + p.startPos = p.endPos + p.goodArgNum = true + i := p.startPos + for i < end && format[i] != '%' { + i++ + } + if i > p.startPos { + p.endPos = i + return true + } + // Process one verb + i++ + + p.Status = StatusSubstitution + + // Do we have flags? + p.ClearFlags() + +simpleFormat: + for ; i < end; i++ { + c := p.format[i] + switch c { + case '#': + p.Sharp = true + case '0': + p.Zero = !p.Minus // Only allow zero padding to the left. + case '+': + p.Plus = true + case '-': + p.Minus = true + p.Zero = false // Do not pad with zeros to the right. + case ' ': + p.Space = true + default: + // Fast path for common case of ascii lower case simple verbs + // without precision or width or argument indices. + if 'a' <= c && c <= 'z' && p.ArgNum < len(p.Args) { + if c == 'v' { + // Go syntax + p.SharpV = p.Sharp + p.Sharp = false + // Struct-field syntax + p.PlusV = p.Plus + p.Plus = false + } + p.Verb = rune(c) + p.ArgNum++ + p.endPos = i + 1 + return true + } + // Format is more complex than simple flags and a verb or is malformed. + break simpleFormat + } + } + + // Do we have an explicit argument index? + i, afterIndex = p.updateArgNumber(format, i) + + // Do we have width? + if i < end && format[i] == '*' { + i++ + p.Width, p.WidthPresent = p.intFromArg() + + if !p.WidthPresent { + p.Status = StatusBadWidthSubstitution + } + + // We have a negative width, so take its value and ensure + // that the minus flag is set + if p.Width < 0 { + p.Width = -p.Width + p.Minus = true + p.Zero = false // Do not pad with zeros to the right. + } + afterIndex = false + } else { + p.Width, p.WidthPresent, i = parsenum(format, i, end) + if afterIndex && p.WidthPresent { // "%[3]2d" + p.goodArgNum = false + } + } + + // Do we have precision? + if i+1 < end && format[i] == '.' { + i++ + if afterIndex { // "%[3].2d" + p.goodArgNum = false + } + i, afterIndex = p.updateArgNumber(format, i) + if i < end && format[i] == '*' { + i++ + p.Prec, p.PrecPresent = p.intFromArg() + // Negative precision arguments don't make sense + if p.Prec < 0 { + p.Prec = 0 + p.PrecPresent = false + } + if !p.PrecPresent { + p.Status = StatusBadPrecSubstitution + } + afterIndex = false + } else { + p.Prec, p.PrecPresent, i = parsenum(format, i, end) + if !p.PrecPresent { + p.Prec = 0 + p.PrecPresent = true + } + } + } + + if !afterIndex { + i, afterIndex = p.updateArgNumber(format, i) + } + p.HasIndex = afterIndex + + if i >= end { + p.endPos = i + p.Status = StatusNoVerb + return true + } + + verb, w := utf8.DecodeRuneInString(format[i:]) + p.endPos = i + w + p.Verb = verb + + switch { + case verb == '%': // Percent does not absorb operands and ignores f.wid and f.prec. + p.startPos = p.endPos - 1 + p.Status = StatusText + case !p.goodArgNum: + p.Status = StatusBadArgNum + case p.ArgNum >= len(p.Args): // No argument left over to print for the current verb. + p.Status = StatusMissingArg + p.ArgNum++ + case verb == 'v': + // Go syntax + p.SharpV = p.Sharp + p.Sharp = false + // Struct-field syntax + p.PlusV = p.Plus + p.Plus = false + fallthrough + default: + p.ArgNum++ + } + return true +} + +// intFromArg gets the ArgNumth element of Args. On return, isInt reports +// whether the argument has integer type. +func (p *Parser) intFromArg() (num int, isInt bool) { + if p.ArgNum < len(p.Args) { + arg := p.Args[p.ArgNum] + num, isInt = arg.(int) // Almost always OK. + if !isInt { + // Work harder. + switch v := reflect.ValueOf(arg); v.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + n := v.Int() + if int64(int(n)) == n { + num = int(n) + isInt = true + } + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + n := v.Uint() + if int64(n) >= 0 && uint64(int(n)) == n { + num = int(n) + isInt = true + } + default: + // Already 0, false. + } + } + p.ArgNum++ + if tooLarge(num) { + num = 0 + isInt = false + } + } + return +} + +// parseArgNumber returns the value of the bracketed number, minus 1 +// (explicit argument numbers are one-indexed but we want zero-indexed). +// The opening bracket is known to be present at format[0]. +// The returned values are the index, the number of bytes to consume +// up to the closing paren, if present, and whether the number parsed +// ok. The bytes to consume will be 1 if no closing paren is present. +func parseArgNumber(format string) (index int, wid int, ok bool) { + // There must be at least 3 bytes: [n]. + if len(format) < 3 { + return 0, 1, false + } + + // Find closing bracket. + for i := 1; i < len(format); i++ { + if format[i] == ']' { + width, ok, newi := parsenum(format, 1, i) + if !ok || newi != i { + return 0, i + 1, false + } + return width - 1, i + 1, true // arg numbers are one-indexed and skip paren. + } + } + return 0, 1, false +} + +// updateArgNumber returns the next argument to evaluate, which is either the value of the passed-in +// argNum or the value of the bracketed integer that begins format[i:]. It also returns +// the new value of i, that is, the index of the next byte of the format to process. +func (p *Parser) updateArgNumber(format string, i int) (newi int, found bool) { + if len(format) <= i || format[i] != '[' { + return i, false + } + p.Reordered = true + index, wid, ok := parseArgNumber(format[i:]) + if ok && 0 <= index && index < len(p.Args) { + p.ArgNum = index + return i + wid, true + } + p.goodArgNum = false + return i + wid, ok +} + +// tooLarge reports whether the magnitude of the integer is +// too large to be used as a formatting width or precision. +func tooLarge(x int) bool { + const max int = 1e6 + return x > max || x < -max +} + +// parsenum converts ASCII to integer. num is 0 (and isnum is false) if no number present. +func parsenum(s string, start, end int) (num int, isnum bool, newi int) { + if start >= end { + return 0, false, end + } + for newi = start; newi < end && '0' <= s[newi] && s[newi] <= '9'; newi++ { + if tooLarge(num) { + return 0, false, end // Overflow; crazy long number most likely. + } + num = num*10 + int(s[newi]-'0') + isnum = true + } + return +} diff --git a/vendor/golang.org/x/text/internal/number/common.go b/vendor/golang.org/x/text/internal/number/common.go new file mode 100644 index 0000000000..a6e9c8e0d5 --- /dev/null +++ b/vendor/golang.org/x/text/internal/number/common.go @@ -0,0 +1,55 @@ +// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. + +package number + +import ( + "unicode/utf8" + + "golang.org/x/text/internal/language/compact" +) + +// A system identifies a CLDR numbering system. +type system byte + +type systemData struct { + id system + digitSize byte // number of UTF-8 bytes per digit + zero [utf8.UTFMax]byte // UTF-8 sequence of zero digit. +} + +// A SymbolType identifies a symbol of a specific kind. +type SymbolType int + +const ( + SymDecimal SymbolType = iota + SymGroup + SymList + SymPercentSign + SymPlusSign + SymMinusSign + SymExponential + SymSuperscriptingExponent + SymPerMille + SymInfinity + SymNan + SymTimeSeparator + + NumSymbolTypes +) + +const hasNonLatnMask = 0x8000 + +// symOffset is an offset into altSymData if the bit indicated by hasNonLatnMask +// is not 0 (with this bit masked out), and an offset into symIndex otherwise. +// +// TODO: this type can be a byte again if we use an indirection into altsymData +// and introduce an alt -> offset slice (the length of this will be number of +// alternatives plus 1). This also allows getting rid of the compactTag field +// in altSymData. In total this will save about 1K. +type symOffset uint16 + +type altSymData struct { + compactTag compact.ID + symIndex symOffset + system system +} diff --git a/vendor/golang.org/x/text/internal/number/decimal.go b/vendor/golang.org/x/text/internal/number/decimal.go new file mode 100644 index 0000000000..e128cf3437 --- /dev/null +++ b/vendor/golang.org/x/text/internal/number/decimal.go @@ -0,0 +1,500 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:generate stringer -type RoundingMode + +package number + +import ( + "math" + "strconv" +) + +// RoundingMode determines how a number is rounded to the desired precision. +type RoundingMode byte + +const ( + ToNearestEven RoundingMode = iota // towards the nearest integer, or towards an even number if equidistant. + ToNearestZero // towards the nearest integer, or towards zero if equidistant. + ToNearestAway // towards the nearest integer, or away from zero if equidistant. + ToPositiveInf // towards infinity + ToNegativeInf // towards negative infinity + ToZero // towards zero + AwayFromZero // away from zero + numModes +) + +const maxIntDigits = 20 + +// A Decimal represents a floating point number in decimal format. +// Digits represents a number [0, 1.0), and the absolute value represented by +// Decimal is Digits * 10^Exp. Leading and trailing zeros may be omitted and Exp +// may point outside a valid position in Digits. +// +// Examples: +// +// Number Decimal +// 12345 Digits: [1, 2, 3, 4, 5], Exp: 5 +// 12.345 Digits: [1, 2, 3, 4, 5], Exp: 2 +// 12000 Digits: [1, 2], Exp: 5 +// 12000.00 Digits: [1, 2], Exp: 5 +// 0.00123 Digits: [1, 2, 3], Exp: -2 +// 0 Digits: [], Exp: 0 +type Decimal struct { + digits + + buf [maxIntDigits]byte +} + +type digits struct { + Digits []byte // mantissa digits, big-endian + Exp int32 // exponent + Neg bool + Inf bool // Takes precedence over Digits and Exp. + NaN bool // Takes precedence over Inf. +} + +// Digits represents a floating point number represented in digits of the +// base in which a number is to be displayed. It is similar to Decimal, but +// keeps track of trailing fraction zeros and the comma placement for +// engineering notation. Digits must have at least one digit. +// +// Examples: +// +// Number Decimal +// decimal +// 12345 Digits: [1, 2, 3, 4, 5], Exp: 5 End: 5 +// 12.345 Digits: [1, 2, 3, 4, 5], Exp: 2 End: 5 +// 12000 Digits: [1, 2], Exp: 5 End: 5 +// 12000.00 Digits: [1, 2], Exp: 5 End: 7 +// 0.00123 Digits: [1, 2, 3], Exp: -2 End: 3 +// 0 Digits: [], Exp: 0 End: 1 +// scientific (actual exp is Exp - Comma) +// 0e0 Digits: [0], Exp: 1, End: 1, Comma: 1 +// .0e0 Digits: [0], Exp: 0, End: 1, Comma: 0 +// 0.0e0 Digits: [0], Exp: 1, End: 2, Comma: 1 +// 1.23e4 Digits: [1, 2, 3], Exp: 5, End: 3, Comma: 1 +// .123e5 Digits: [1, 2, 3], Exp: 5, End: 3, Comma: 0 +// engineering +// 12.3e3 Digits: [1, 2, 3], Exp: 5, End: 3, Comma: 2 +type Digits struct { + digits + // End indicates the end position of the number. + End int32 // For decimals Exp <= End. For scientific len(Digits) <= End. + // Comma is used for the comma position for scientific (always 0 or 1) and + // engineering notation (always 0, 1, 2, or 3). + Comma uint8 + // IsScientific indicates whether this number is to be rendered as a + // scientific number. + IsScientific bool +} + +func (d *Digits) NumFracDigits() int { + if d.Exp >= d.End { + return 0 + } + return int(d.End - d.Exp) +} + +// normalize returns a new Decimal with leading and trailing zeros removed. +func (d *Decimal) normalize() (n Decimal) { + n = *d + b := n.Digits + // Strip leading zeros. Resulting number of digits is significant digits. + for len(b) > 0 && b[0] == 0 { + b = b[1:] + n.Exp-- + } + // Strip trailing zeros + for len(b) > 0 && b[len(b)-1] == 0 { + b = b[:len(b)-1] + } + if len(b) == 0 { + n.Exp = 0 + } + n.Digits = b + return n +} + +func (d *Decimal) clear() { + b := d.Digits + if b == nil { + b = d.buf[:0] + } + *d = Decimal{} + d.Digits = b[:0] +} + +func (x *Decimal) String() string { + if x.NaN { + return "NaN" + } + var buf []byte + if x.Neg { + buf = append(buf, '-') + } + if x.Inf { + buf = append(buf, "Inf"...) + return string(buf) + } + switch { + case len(x.Digits) == 0: + buf = append(buf, '0') + case x.Exp <= 0: + // 0.00ddd + buf = append(buf, "0."...) + buf = appendZeros(buf, -int(x.Exp)) + buf = appendDigits(buf, x.Digits) + + case /* 0 < */ int(x.Exp) < len(x.Digits): + // dd.ddd + buf = appendDigits(buf, x.Digits[:x.Exp]) + buf = append(buf, '.') + buf = appendDigits(buf, x.Digits[x.Exp:]) + + default: // len(x.Digits) <= x.Exp + // ddd00 + buf = appendDigits(buf, x.Digits) + buf = appendZeros(buf, int(x.Exp)-len(x.Digits)) + } + return string(buf) +} + +func appendDigits(buf []byte, digits []byte) []byte { + for _, c := range digits { + buf = append(buf, c+'0') + } + return buf +} + +// appendZeros appends n 0 digits to buf and returns buf. +func appendZeros(buf []byte, n int) []byte { + for ; n > 0; n-- { + buf = append(buf, '0') + } + return buf +} + +func (d *digits) round(mode RoundingMode, n int) { + if n >= len(d.Digits) { + return + } + // Make rounding decision: The result mantissa is truncated ("rounded down") + // by default. Decide if we need to increment, or "round up", the (unsigned) + // mantissa. + inc := false + switch mode { + case ToNegativeInf: + inc = d.Neg + case ToPositiveInf: + inc = !d.Neg + case ToZero: + // nothing to do + case AwayFromZero: + inc = true + case ToNearestEven: + inc = d.Digits[n] > 5 || d.Digits[n] == 5 && + (len(d.Digits) > n+1 || n == 0 || d.Digits[n-1]&1 != 0) + case ToNearestAway: + inc = d.Digits[n] >= 5 + case ToNearestZero: + inc = d.Digits[n] > 5 || d.Digits[n] == 5 && len(d.Digits) > n+1 + default: + panic("unreachable") + } + if inc { + d.roundUp(n) + } else { + d.roundDown(n) + } +} + +// roundFloat rounds a floating point number. +func (r RoundingMode) roundFloat(x float64) float64 { + // Make rounding decision: The result mantissa is truncated ("rounded down") + // by default. Decide if we need to increment, or "round up", the (unsigned) + // mantissa. + abs := x + if x < 0 { + abs = -x + } + i, f := math.Modf(abs) + if f == 0.0 { + return x + } + inc := false + switch r { + case ToNegativeInf: + inc = x < 0 + case ToPositiveInf: + inc = x >= 0 + case ToZero: + // nothing to do + case AwayFromZero: + inc = true + case ToNearestEven: + // TODO: check overflow + inc = f > 0.5 || f == 0.5 && int64(i)&1 != 0 + case ToNearestAway: + inc = f >= 0.5 + case ToNearestZero: + inc = f > 0.5 + default: + panic("unreachable") + } + if inc { + i += 1 + } + if abs != x { + i = -i + } + return i +} + +func (x *digits) roundUp(n int) { + if n < 0 || n >= len(x.Digits) { + return // nothing to do + } + // find first digit < 9 + for n > 0 && x.Digits[n-1] >= 9 { + n-- + } + + if n == 0 { + // all digits are 9s => round up to 1 and update exponent + x.Digits[0] = 1 // ok since len(x.Digits) > n + x.Digits = x.Digits[:1] + x.Exp++ + return + } + x.Digits[n-1]++ + x.Digits = x.Digits[:n] + // x already trimmed +} + +func (x *digits) roundDown(n int) { + if n < 0 || n >= len(x.Digits) { + return // nothing to do + } + x.Digits = x.Digits[:n] + trim(x) +} + +// trim cuts off any trailing zeros from x's mantissa; +// they are meaningless for the value of x. +func trim(x *digits) { + i := len(x.Digits) + for i > 0 && x.Digits[i-1] == 0 { + i-- + } + x.Digits = x.Digits[:i] + if i == 0 { + x.Exp = 0 + } +} + +// A Converter converts a number into decimals according to the given rounding +// criteria. +type Converter interface { + Convert(d *Decimal, r RoundingContext) +} + +const ( + signed = true + unsigned = false +) + +// Convert converts the given number to the decimal representation using the +// supplied RoundingContext. +func (d *Decimal) Convert(r RoundingContext, number interface{}) { + switch f := number.(type) { + case Converter: + d.clear() + f.Convert(d, r) + case float32: + d.ConvertFloat(r, float64(f), 32) + case float64: + d.ConvertFloat(r, f, 64) + case int: + d.ConvertInt(r, signed, uint64(f)) + case int8: + d.ConvertInt(r, signed, uint64(f)) + case int16: + d.ConvertInt(r, signed, uint64(f)) + case int32: + d.ConvertInt(r, signed, uint64(f)) + case int64: + d.ConvertInt(r, signed, uint64(f)) + case uint: + d.ConvertInt(r, unsigned, uint64(f)) + case uint8: + d.ConvertInt(r, unsigned, uint64(f)) + case uint16: + d.ConvertInt(r, unsigned, uint64(f)) + case uint32: + d.ConvertInt(r, unsigned, uint64(f)) + case uint64: + d.ConvertInt(r, unsigned, f) + + default: + d.NaN = true + // TODO: + // case string: if produced by strconv, allows for easy arbitrary pos. + // case reflect.Value: + // case big.Float + // case big.Int + // case big.Rat? + // catch underlyings using reflect or will this already be done by the + // message package? + } +} + +// ConvertInt converts an integer to decimals. +func (d *Decimal) ConvertInt(r RoundingContext, signed bool, x uint64) { + if r.Increment > 0 { + // TODO: if uint64 is too large, fall back to float64 + if signed { + d.ConvertFloat(r, float64(int64(x)), 64) + } else { + d.ConvertFloat(r, float64(x), 64) + } + return + } + d.clear() + if signed && int64(x) < 0 { + x = uint64(-int64(x)) + d.Neg = true + } + d.fillIntDigits(x) + d.Exp = int32(len(d.Digits)) +} + +// ConvertFloat converts a floating point number to decimals. +func (d *Decimal) ConvertFloat(r RoundingContext, x float64, size int) { + d.clear() + if math.IsNaN(x) { + d.NaN = true + return + } + // Simple case: decimal notation + if r.Increment > 0 { + scale := int(r.IncrementScale) + mult := 1.0 + if scale >= len(scales) { + mult = math.Pow(10, float64(scale)) + } else { + mult = scales[scale] + } + // We multiply x instead of dividing inc as it gives less rounding + // issues. + x *= mult + x /= float64(r.Increment) + x = r.Mode.roundFloat(x) + x *= float64(r.Increment) + x /= mult + } + + abs := x + if x < 0 { + d.Neg = true + abs = -x + } + if math.IsInf(abs, 1) { + d.Inf = true + return + } + + // By default we get the exact decimal representation. + verb := byte('g') + prec := -1 + // As the strconv API does not return the rounding accuracy, we can only + // round using ToNearestEven. + if r.Mode == ToNearestEven { + if n := r.RoundSignificantDigits(); n >= 0 { + prec = n + } else if n = r.RoundFractionDigits(); n >= 0 { + prec = n + verb = 'f' + } + } else { + // TODO: At this point strconv's rounding is imprecise to the point that + // it is not usable for this purpose. + // See https://github.com/golang/go/issues/21714 + // If rounding is requested, we ask for a large number of digits and + // round from there to simulate rounding only once. + // Ideally we would have strconv export an AppendDigits that would take + // a rounding mode and/or return an accuracy. Something like this would + // work: + // AppendDigits(dst []byte, x float64, base, size, prec int) (digits []byte, exp, accuracy int) + hasPrec := r.RoundSignificantDigits() >= 0 + hasScale := r.RoundFractionDigits() >= 0 + if hasPrec || hasScale { + // prec is the number of mantissa bits plus some extra for safety. + // We need at least the number of mantissa bits as decimals to + // accurately represent the floating point without rounding, as each + // bit requires one more decimal to represent: 0.5, 0.25, 0.125, ... + prec = 60 + } + } + + b := strconv.AppendFloat(d.Digits[:0], abs, verb, prec, size) + i := 0 + k := 0 + beforeDot := 1 + for i < len(b) { + if c := b[i]; '0' <= c && c <= '9' { + b[k] = c - '0' + k++ + d.Exp += int32(beforeDot) + } else if c == '.' { + beforeDot = 0 + d.Exp = int32(k) + } else { + break + } + i++ + } + d.Digits = b[:k] + if i != len(b) { + i += len("e") + pSign := i + exp := 0 + for i++; i < len(b); i++ { + exp *= 10 + exp += int(b[i] - '0') + } + if b[pSign] == '-' { + exp = -exp + } + d.Exp = int32(exp) + 1 + } +} + +func (d *Decimal) fillIntDigits(x uint64) { + if cap(d.Digits) < maxIntDigits { + d.Digits = d.buf[:] + } else { + d.Digits = d.buf[:maxIntDigits] + } + i := 0 + for ; x > 0; x /= 10 { + d.Digits[i] = byte(x % 10) + i++ + } + d.Digits = d.Digits[:i] + for p := 0; p < i; p++ { + i-- + d.Digits[p], d.Digits[i] = d.Digits[i], d.Digits[p] + } +} + +var scales [70]float64 + +func init() { + x := 1.0 + for i := range scales { + scales[i] = x + x *= 10 + } +} diff --git a/vendor/golang.org/x/text/internal/number/format.go b/vendor/golang.org/x/text/internal/number/format.go new file mode 100644 index 0000000000..1aadcf4077 --- /dev/null +++ b/vendor/golang.org/x/text/internal/number/format.go @@ -0,0 +1,533 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package number + +import ( + "strconv" + "unicode/utf8" + + "golang.org/x/text/language" +) + +// TODO: +// - grouping of fractions +// - allow user-defined superscript notation (such as 4) +// - same for non-breaking spaces, like   + +// A VisibleDigits computes digits, comma placement and trailing zeros as they +// will be shown to the user. +type VisibleDigits interface { + Digits(buf []byte, t language.Tag, scale int) Digits + // TODO: Do we also need to add the verb or pass a format.State? +} + +// Formatting proceeds along the following lines: +// 0) Compose rounding information from format and context. +// 1) Convert a number into a Decimal. +// 2) Sanitize Decimal by adding trailing zeros, removing leading digits, and +// (non-increment) rounding. The Decimal that results from this is suitable +// for determining the plural form. +// 3) Render the Decimal in the localized form. + +// Formatter contains all the information needed to render a number. +type Formatter struct { + Pattern + Info +} + +func (f *Formatter) init(t language.Tag, index []uint8) { + f.Info = InfoFromTag(t) + f.Pattern = formats[index[tagToID(t)]] +} + +// InitPattern initializes a Formatter for the given Pattern. +func (f *Formatter) InitPattern(t language.Tag, pat *Pattern) { + f.Info = InfoFromTag(t) + f.Pattern = *pat +} + +// InitDecimal initializes a Formatter using the default Pattern for the given +// language. +func (f *Formatter) InitDecimal(t language.Tag) { + f.init(t, tagToDecimal) +} + +// InitScientific initializes a Formatter using the default Pattern for the +// given language. +func (f *Formatter) InitScientific(t language.Tag) { + f.init(t, tagToScientific) + f.Pattern.MinFractionDigits = 0 + f.Pattern.MaxFractionDigits = -1 +} + +// InitEngineering initializes a Formatter using the default Pattern for the +// given language. +func (f *Formatter) InitEngineering(t language.Tag) { + f.init(t, tagToScientific) + f.Pattern.MinFractionDigits = 0 + f.Pattern.MaxFractionDigits = -1 + f.Pattern.MaxIntegerDigits = 3 + f.Pattern.MinIntegerDigits = 1 +} + +// InitPercent initializes a Formatter using the default Pattern for the given +// language. +func (f *Formatter) InitPercent(t language.Tag) { + f.init(t, tagToPercent) +} + +// InitPerMille initializes a Formatter using the default Pattern for the given +// language. +func (f *Formatter) InitPerMille(t language.Tag) { + f.init(t, tagToPercent) + f.Pattern.DigitShift = 3 +} + +func (f *Formatter) Append(dst []byte, x interface{}) []byte { + var d Decimal + r := f.RoundingContext + d.Convert(r, x) + return f.Render(dst, FormatDigits(&d, r)) +} + +func FormatDigits(d *Decimal, r RoundingContext) Digits { + if r.isScientific() { + return scientificVisibleDigits(r, d) + } + return decimalVisibleDigits(r, d) +} + +func (f *Formatter) Format(dst []byte, d *Decimal) []byte { + return f.Render(dst, FormatDigits(d, f.RoundingContext)) +} + +func (f *Formatter) Render(dst []byte, d Digits) []byte { + var result []byte + var postPrefix, preSuffix int + if d.IsScientific { + result, postPrefix, preSuffix = appendScientific(dst, f, &d) + } else { + result, postPrefix, preSuffix = appendDecimal(dst, f, &d) + } + if f.PadRune == 0 { + return result + } + width := int(f.FormatWidth) + if count := utf8.RuneCount(result); count < width { + insertPos := 0 + switch f.Flags & PadMask { + case PadAfterPrefix: + insertPos = postPrefix + case PadBeforeSuffix: + insertPos = preSuffix + case PadAfterSuffix: + insertPos = len(result) + } + num := width - count + pad := [utf8.UTFMax]byte{' '} + sz := 1 + if r := f.PadRune; r != 0 { + sz = utf8.EncodeRune(pad[:], r) + } + extra := sz * num + if n := len(result) + extra; n < cap(result) { + result = result[:n] + copy(result[insertPos+extra:], result[insertPos:]) + } else { + buf := make([]byte, n) + copy(buf, result[:insertPos]) + copy(buf[insertPos+extra:], result[insertPos:]) + result = buf + } + for ; num > 0; num-- { + insertPos += copy(result[insertPos:], pad[:sz]) + } + } + return result +} + +// decimalVisibleDigits converts d according to the RoundingContext. Note that +// the exponent may change as a result of this operation. +func decimalVisibleDigits(r RoundingContext, d *Decimal) Digits { + if d.NaN || d.Inf { + return Digits{digits: digits{Neg: d.Neg, NaN: d.NaN, Inf: d.Inf}} + } + n := Digits{digits: d.normalize().digits} + + exp := n.Exp + exp += int32(r.DigitShift) + + // Cap integer digits. Remove *most-significant* digits. + if r.MaxIntegerDigits > 0 { + if p := int(exp) - int(r.MaxIntegerDigits); p > 0 { + if p > len(n.Digits) { + p = len(n.Digits) + } + if n.Digits = n.Digits[p:]; len(n.Digits) == 0 { + exp = 0 + } else { + exp -= int32(p) + } + // Strip leading zeros. + for len(n.Digits) > 0 && n.Digits[0] == 0 { + n.Digits = n.Digits[1:] + exp-- + } + } + } + + // Rounding if not already done by Convert. + p := len(n.Digits) + if maxSig := int(r.MaxSignificantDigits); maxSig > 0 { + p = maxSig + } + if maxFrac := int(r.MaxFractionDigits); maxFrac >= 0 { + if cap := int(exp) + maxFrac; cap < p { + p = int(exp) + maxFrac + } + if p < 0 { + p = 0 + } + } + n.round(r.Mode, p) + + // set End (trailing zeros) + n.End = int32(len(n.Digits)) + if n.End == 0 { + exp = 0 + if r.MinFractionDigits > 0 { + n.End = int32(r.MinFractionDigits) + } + if p := int32(r.MinSignificantDigits) - 1; p > n.End { + n.End = p + } + } else { + if end := exp + int32(r.MinFractionDigits); end > n.End { + n.End = end + } + if n.End < int32(r.MinSignificantDigits) { + n.End = int32(r.MinSignificantDigits) + } + } + n.Exp = exp + return n +} + +// appendDecimal appends a formatted number to dst. It returns two possible +// insertion points for padding. +func appendDecimal(dst []byte, f *Formatter, n *Digits) (b []byte, postPre, preSuf int) { + if dst, ok := f.renderSpecial(dst, n); ok { + return dst, 0, len(dst) + } + digits := n.Digits + exp := n.Exp + + // Split in integer and fraction part. + var intDigits, fracDigits []byte + numInt := 0 + numFrac := int(n.End - n.Exp) + if exp > 0 { + numInt = int(exp) + if int(exp) >= len(digits) { // ddddd | ddddd00 + intDigits = digits + } else { // ddd.dd + intDigits = digits[:exp] + fracDigits = digits[exp:] + } + } else { + fracDigits = digits + } + + neg := n.Neg + affix, suffix := f.getAffixes(neg) + dst = appendAffix(dst, f, affix, neg) + savedLen := len(dst) + + minInt := int(f.MinIntegerDigits) + if minInt == 0 && f.MinSignificantDigits > 0 { + minInt = 1 + } + // add leading zeros + for i := minInt; i > numInt; i-- { + dst = f.AppendDigit(dst, 0) + if f.needsSep(i) { + dst = append(dst, f.Symbol(SymGroup)...) + } + } + i := 0 + for ; i < len(intDigits); i++ { + dst = f.AppendDigit(dst, intDigits[i]) + if f.needsSep(numInt - i) { + dst = append(dst, f.Symbol(SymGroup)...) + } + } + for ; i < numInt; i++ { + dst = f.AppendDigit(dst, 0) + if f.needsSep(numInt - i) { + dst = append(dst, f.Symbol(SymGroup)...) + } + } + + if numFrac > 0 || f.Flags&AlwaysDecimalSeparator != 0 { + dst = append(dst, f.Symbol(SymDecimal)...) + } + // Add trailing zeros + i = 0 + for n := -int(n.Exp); i < n; i++ { + dst = f.AppendDigit(dst, 0) + } + for _, d := range fracDigits { + i++ + dst = f.AppendDigit(dst, d) + } + for ; i < numFrac; i++ { + dst = f.AppendDigit(dst, 0) + } + return appendAffix(dst, f, suffix, neg), savedLen, len(dst) +} + +func scientificVisibleDigits(r RoundingContext, d *Decimal) Digits { + if d.NaN || d.Inf { + return Digits{digits: digits{Neg: d.Neg, NaN: d.NaN, Inf: d.Inf}} + } + n := Digits{digits: d.normalize().digits, IsScientific: true} + + // Normalize to have at least one digit. This simplifies engineering + // notation. + if len(n.Digits) == 0 { + n.Digits = append(n.Digits, 0) + n.Exp = 1 + } + + // Significant digits are transformed by the parser for scientific notation + // and do not need to be handled here. + maxInt, numInt := int(r.MaxIntegerDigits), int(r.MinIntegerDigits) + if numInt == 0 { + numInt = 1 + } + + // If a maximum number of integers is specified, the minimum must be 1 + // and the exponent is grouped by this number (e.g. for engineering) + if maxInt > numInt { + // Correct the exponent to reflect a single integer digit. + numInt = 1 + // engineering + // 0.01234 ([12345]e-1) -> 1.2345e-2 12.345e-3 + // 12345 ([12345]e+5) -> 1.2345e4 12.345e3 + d := int(n.Exp-1) % maxInt + if d < 0 { + d += maxInt + } + numInt += d + } + + p := len(n.Digits) + if maxSig := int(r.MaxSignificantDigits); maxSig > 0 { + p = maxSig + } + if maxFrac := int(r.MaxFractionDigits); maxFrac >= 0 && numInt+maxFrac < p { + p = numInt + maxFrac + } + n.round(r.Mode, p) + + n.Comma = uint8(numInt) + n.End = int32(len(n.Digits)) + if minSig := int32(r.MinFractionDigits) + int32(numInt); n.End < minSig { + n.End = minSig + } + return n +} + +// appendScientific appends a formatted number to dst. It returns two possible +// insertion points for padding. +func appendScientific(dst []byte, f *Formatter, n *Digits) (b []byte, postPre, preSuf int) { + if dst, ok := f.renderSpecial(dst, n); ok { + return dst, 0, 0 + } + digits := n.Digits + numInt := int(n.Comma) + numFrac := int(n.End) - int(n.Comma) + + var intDigits, fracDigits []byte + if numInt <= len(digits) { + intDigits = digits[:numInt] + fracDigits = digits[numInt:] + } else { + intDigits = digits + } + neg := n.Neg + affix, suffix := f.getAffixes(neg) + dst = appendAffix(dst, f, affix, neg) + savedLen := len(dst) + + i := 0 + for ; i < len(intDigits); i++ { + dst = f.AppendDigit(dst, intDigits[i]) + if f.needsSep(numInt - i) { + dst = append(dst, f.Symbol(SymGroup)...) + } + } + for ; i < numInt; i++ { + dst = f.AppendDigit(dst, 0) + if f.needsSep(numInt - i) { + dst = append(dst, f.Symbol(SymGroup)...) + } + } + + if numFrac > 0 || f.Flags&AlwaysDecimalSeparator != 0 { + dst = append(dst, f.Symbol(SymDecimal)...) + } + i = 0 + for ; i < len(fracDigits); i++ { + dst = f.AppendDigit(dst, fracDigits[i]) + } + for ; i < numFrac; i++ { + dst = f.AppendDigit(dst, 0) + } + + // exp + buf := [12]byte{} + // TODO: use exponential if superscripting is not available (no Latin + // numbers or no tags) and use exponential in all other cases. + exp := n.Exp - int32(n.Comma) + exponential := f.Symbol(SymExponential) + if exponential == "E" { + dst = append(dst, f.Symbol(SymSuperscriptingExponent)...) + dst = f.AppendDigit(dst, 1) + dst = f.AppendDigit(dst, 0) + switch { + case exp < 0: + dst = append(dst, superMinus...) + exp = -exp + case f.Flags&AlwaysExpSign != 0: + dst = append(dst, superPlus...) + } + b = strconv.AppendUint(buf[:0], uint64(exp), 10) + for i := len(b); i < int(f.MinExponentDigits); i++ { + dst = append(dst, superDigits[0]...) + } + for _, c := range b { + dst = append(dst, superDigits[c-'0']...) + } + } else { + dst = append(dst, exponential...) + switch { + case exp < 0: + dst = append(dst, f.Symbol(SymMinusSign)...) + exp = -exp + case f.Flags&AlwaysExpSign != 0: + dst = append(dst, f.Symbol(SymPlusSign)...) + } + b = strconv.AppendUint(buf[:0], uint64(exp), 10) + for i := len(b); i < int(f.MinExponentDigits); i++ { + dst = f.AppendDigit(dst, 0) + } + for _, c := range b { + dst = f.AppendDigit(dst, c-'0') + } + } + return appendAffix(dst, f, suffix, neg), savedLen, len(dst) +} + +const ( + superMinus = "\u207B" // SUPERSCRIPT HYPHEN-MINUS + superPlus = "\u207A" // SUPERSCRIPT PLUS SIGN +) + +var ( + // Note: the digits are not sequential!!! + superDigits = []string{ + "\u2070", // SUPERSCRIPT DIGIT ZERO + "\u00B9", // SUPERSCRIPT DIGIT ONE + "\u00B2", // SUPERSCRIPT DIGIT TWO + "\u00B3", // SUPERSCRIPT DIGIT THREE + "\u2074", // SUPERSCRIPT DIGIT FOUR + "\u2075", // SUPERSCRIPT DIGIT FIVE + "\u2076", // SUPERSCRIPT DIGIT SIX + "\u2077", // SUPERSCRIPT DIGIT SEVEN + "\u2078", // SUPERSCRIPT DIGIT EIGHT + "\u2079", // SUPERSCRIPT DIGIT NINE + } +) + +func (f *Formatter) getAffixes(neg bool) (affix, suffix string) { + str := f.Affix + if str != "" { + if f.NegOffset > 0 { + if neg { + str = str[f.NegOffset:] + } else { + str = str[:f.NegOffset] + } + } + sufStart := 1 + str[0] + affix = str[1:sufStart] + suffix = str[sufStart+1:] + } + // TODO: introduce a NeedNeg sign to indicate if the left pattern already + // has a sign marked? + if f.NegOffset == 0 && (neg || f.Flags&AlwaysSign != 0) { + affix = "-" + affix + } + return affix, suffix +} + +func (f *Formatter) renderSpecial(dst []byte, d *Digits) (b []byte, ok bool) { + if d.NaN { + return fmtNaN(dst, f), true + } + if d.Inf { + return fmtInfinite(dst, f, d), true + } + return dst, false +} + +func fmtNaN(dst []byte, f *Formatter) []byte { + return append(dst, f.Symbol(SymNan)...) +} + +func fmtInfinite(dst []byte, f *Formatter, d *Digits) []byte { + affix, suffix := f.getAffixes(d.Neg) + dst = appendAffix(dst, f, affix, d.Neg) + dst = append(dst, f.Symbol(SymInfinity)...) + dst = appendAffix(dst, f, suffix, d.Neg) + return dst +} + +func appendAffix(dst []byte, f *Formatter, affix string, neg bool) []byte { + quoting := false + escaping := false + for _, r := range affix { + switch { + case escaping: + // escaping occurs both inside and outside of quotes + dst = append(dst, string(r)...) + escaping = false + case r == '\\': + escaping = true + case r == '\'': + quoting = !quoting + case quoting: + dst = append(dst, string(r)...) + case r == '%': + if f.DigitShift == 3 { + dst = append(dst, f.Symbol(SymPerMille)...) + } else { + dst = append(dst, f.Symbol(SymPercentSign)...) + } + case r == '-' || r == '+': + if neg { + dst = append(dst, f.Symbol(SymMinusSign)...) + } else if f.Flags&ElideSign == 0 { + dst = append(dst, f.Symbol(SymPlusSign)...) + } else { + dst = append(dst, ' ') + } + default: + dst = append(dst, string(r)...) + } + } + return dst +} diff --git a/vendor/golang.org/x/text/internal/number/number.go b/vendor/golang.org/x/text/internal/number/number.go new file mode 100644 index 0000000000..e1d933c3f7 --- /dev/null +++ b/vendor/golang.org/x/text/internal/number/number.go @@ -0,0 +1,152 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:generate go run gen.go gen_common.go + +// Package number contains tools and data for formatting numbers. +package number + +import ( + "unicode/utf8" + + "golang.org/x/text/internal/language/compact" + "golang.org/x/text/language" +) + +// Info holds number formatting configuration data. +type Info struct { + system systemData // numbering system information + symIndex symOffset // index to symbols +} + +// InfoFromLangID returns a Info for the given compact language identifier and +// numbering system identifier. If system is the empty string, the default +// numbering system will be taken for that language. +func InfoFromLangID(compactIndex compact.ID, numberSystem string) Info { + p := langToDefaults[compactIndex] + // Lookup the entry for the language. + pSymIndex := symOffset(0) // Default: Latin, default symbols + system, ok := systemMap[numberSystem] + if !ok { + // Take the value for the default numbering system. This is by far the + // most common case as an alternative numbering system is hardly used. + if p&hasNonLatnMask == 0 { // Latn digits. + pSymIndex = p + } else { // Non-Latn or multiple numbering systems. + // Take the first entry from the alternatives list. + data := langToAlt[p&^hasNonLatnMask] + pSymIndex = data.symIndex + system = data.system + } + } else { + langIndex := compactIndex + ns := system + outerLoop: + for ; ; p = langToDefaults[langIndex] { + if p&hasNonLatnMask == 0 { + if ns == 0 { + // The index directly points to the symbol data. + pSymIndex = p + break + } + // Move to the parent and retry. + langIndex = langIndex.Parent() + } else { + // The index points to a list of symbol data indexes. + for _, e := range langToAlt[p&^hasNonLatnMask:] { + if e.compactTag != langIndex { + if langIndex == 0 { + // The CLDR root defines full symbol information for + // all numbering systems (even though mostly by + // means of aliases). Fall back to the default entry + // for Latn if there is no data for the numbering + // system of this language. + if ns == 0 { + break + } + // Fall back to Latin and start from the original + // language. See + // https://unicode.org/reports/tr35/#Locale_Inheritance. + ns = numLatn + langIndex = compactIndex + continue outerLoop + } + // Fall back to parent. + langIndex = langIndex.Parent() + } else if e.system == ns { + pSymIndex = e.symIndex + break outerLoop + } + } + } + } + } + if int(system) >= len(numSysData) { // algorithmic + // Will generate ASCII digits in case the user inadvertently calls + // WriteDigit or Digit on it. + d := numSysData[0] + d.id = system + return Info{ + system: d, + symIndex: pSymIndex, + } + } + return Info{ + system: numSysData[system], + symIndex: pSymIndex, + } +} + +// InfoFromTag returns a Info for the given language tag. +func InfoFromTag(t language.Tag) Info { + return InfoFromLangID(tagToID(t), t.TypeForKey("nu")) +} + +// IsDecimal reports if the numbering system can convert decimal to native +// symbols one-to-one. +func (n Info) IsDecimal() bool { + return int(n.system.id) < len(numSysData) +} + +// WriteDigit writes the UTF-8 sequence for n corresponding to the given ASCII +// digit to dst and reports the number of bytes written. dst must be large +// enough to hold the rune (can be up to utf8.UTFMax bytes). +func (n Info) WriteDigit(dst []byte, asciiDigit rune) int { + copy(dst, n.system.zero[:n.system.digitSize]) + dst[n.system.digitSize-1] += byte(asciiDigit - '0') + return int(n.system.digitSize) +} + +// AppendDigit appends the UTF-8 sequence for n corresponding to the given digit +// to dst and reports the number of bytes written. dst must be large enough to +// hold the rune (can be up to utf8.UTFMax bytes). +func (n Info) AppendDigit(dst []byte, digit byte) []byte { + dst = append(dst, n.system.zero[:n.system.digitSize]...) + dst[len(dst)-1] += digit + return dst +} + +// Digit returns the digit for the numbering system for the corresponding ASCII +// value. For example, ni.Digit('3') could return '三'. Note that the argument +// is the rune constant '3', which equals 51, not the integer constant 3. +func (n Info) Digit(asciiDigit rune) rune { + var x [utf8.UTFMax]byte + n.WriteDigit(x[:], asciiDigit) + r, _ := utf8.DecodeRune(x[:]) + return r +} + +// Symbol returns the string for the given symbol type. +func (n Info) Symbol(t SymbolType) string { + return symData.Elem(int(symIndex[n.symIndex][t])) +} + +func formatForLang(t language.Tag, index []byte) *Pattern { + return &formats[index[tagToID(t)]] +} + +func tagToID(t language.Tag) compact.ID { + id, _ := compact.RegionalID(compact.Tag(t)) + return id +} diff --git a/vendor/golang.org/x/text/internal/number/pattern.go b/vendor/golang.org/x/text/internal/number/pattern.go new file mode 100644 index 0000000000..06e59559a9 --- /dev/null +++ b/vendor/golang.org/x/text/internal/number/pattern.go @@ -0,0 +1,485 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package number + +import ( + "errors" + "unicode/utf8" +) + +// This file contains a parser for the CLDR number patterns as described in +// https://unicode.org/reports/tr35/tr35-numbers.html#Number_Format_Patterns. +// +// The following BNF is derived from this standard. +// +// pattern := subpattern (';' subpattern)? +// subpattern := affix? number exponent? affix? +// number := decimal | sigDigits +// decimal := '#'* '0'* ('.' fraction)? | '#' | '0' +// fraction := '0'* '#'* +// sigDigits := '#'* '@' '@'* '#'* +// exponent := 'E' '+'? '0'* '0' +// padSpec := '*' \L +// +// Notes: +// - An affix pattern may contain any runes, but runes with special meaning +// should be escaped. +// - Sequences of digits, '#', and '@' in decimal and sigDigits may have +// interstitial commas. + +// TODO: replace special characters in affixes (-, +, ¤) with control codes. + +// Pattern holds information for formatting numbers. It is designed to hold +// information from CLDR number patterns. +// +// This pattern is precompiled for all patterns for all languages. Even though +// the number of patterns is not very large, we want to keep this small. +// +// This type is only intended for internal use. +type Pattern struct { + RoundingContext + + Affix string // includes prefix and suffix. First byte is prefix length. + Offset uint16 // Offset into Affix for prefix and suffix + NegOffset uint16 // Offset into Affix for negative prefix and suffix or 0. + PadRune rune + FormatWidth uint16 + + GroupingSize [2]uint8 + Flags PatternFlag +} + +// A RoundingContext indicates how a number should be converted to digits. +// It contains all information needed to determine the "visible digits" as +// required by the pluralization rules. +type RoundingContext struct { + // TODO: unify these two fields so that there is a more unambiguous meaning + // of how precision is handled. + MaxSignificantDigits int16 // -1 is unlimited + MaxFractionDigits int16 // -1 is unlimited + + Increment uint32 + IncrementScale uint8 // May differ from printed scale. + + Mode RoundingMode + + DigitShift uint8 // Number of decimals to shift. Used for % and ‰. + + // Number of digits. + MinIntegerDigits uint8 + + MaxIntegerDigits uint8 + MinFractionDigits uint8 + MinSignificantDigits uint8 + + MinExponentDigits uint8 +} + +// RoundSignificantDigits returns the number of significant digits an +// implementation of Convert may round to or n < 0 if there is no maximum or +// a maximum is not recommended. +func (r *RoundingContext) RoundSignificantDigits() (n int) { + if r.MaxFractionDigits == 0 && r.MaxSignificantDigits > 0 { + return int(r.MaxSignificantDigits) + } else if r.isScientific() && r.MaxIntegerDigits == 1 { + if r.MaxSignificantDigits == 0 || + int(r.MaxFractionDigits+1) == int(r.MaxSignificantDigits) { + // Note: don't add DigitShift: it is only used for decimals. + return int(r.MaxFractionDigits) + 1 + } + } + return -1 +} + +// RoundFractionDigits returns the number of fraction digits an implementation +// of Convert may round to or n < 0 if there is no maximum or a maximum is not +// recommended. +func (r *RoundingContext) RoundFractionDigits() (n int) { + if r.MinExponentDigits == 0 && + r.MaxSignificantDigits == 0 && + r.MaxFractionDigits >= 0 { + return int(r.MaxFractionDigits) + int(r.DigitShift) + } + return -1 +} + +// SetScale fixes the RoundingContext to a fixed number of fraction digits. +func (r *RoundingContext) SetScale(scale int) { + r.MinFractionDigits = uint8(scale) + r.MaxFractionDigits = int16(scale) +} + +func (r *RoundingContext) SetPrecision(prec int) { + r.MaxSignificantDigits = int16(prec) +} + +func (r *RoundingContext) isScientific() bool { + return r.MinExponentDigits > 0 +} + +func (f *Pattern) needsSep(pos int) bool { + p := pos - 1 + size := int(f.GroupingSize[0]) + if size == 0 || p == 0 { + return false + } + if p == size { + return true + } + if p -= size; p < 0 { + return false + } + // TODO: make second groupingsize the same as first if 0 so that we can + // avoid this check. + if x := int(f.GroupingSize[1]); x != 0 { + size = x + } + return p%size == 0 +} + +// A PatternFlag is a bit mask for the flag field of a Pattern. +type PatternFlag uint8 + +const ( + AlwaysSign PatternFlag = 1 << iota + ElideSign // Use space instead of plus sign. AlwaysSign must be true. + AlwaysExpSign + AlwaysDecimalSeparator + ParenthesisForNegative // Common pattern. Saves space. + + PadAfterNumber + PadAfterAffix + + PadBeforePrefix = 0 // Default + PadAfterPrefix = PadAfterAffix + PadBeforeSuffix = PadAfterNumber + PadAfterSuffix = PadAfterNumber | PadAfterAffix + PadMask = PadAfterNumber | PadAfterAffix +) + +type parser struct { + *Pattern + + leadingSharps int + + pos int + err error + doNotTerminate bool + groupingCount uint + hasGroup bool + buf []byte +} + +func (p *parser) setError(err error) { + if p.err == nil { + p.err = err + } +} + +func (p *parser) updateGrouping() { + if p.hasGroup && + 0 < p.groupingCount && p.groupingCount < 255 { + p.GroupingSize[1] = p.GroupingSize[0] + p.GroupingSize[0] = uint8(p.groupingCount) + } + p.groupingCount = 0 + p.hasGroup = true +} + +var ( + // TODO: more sensible and localizeable error messages. + errMultiplePadSpecifiers = errors.New("format: pattern has multiple pad specifiers") + errInvalidPadSpecifier = errors.New("format: invalid pad specifier") + errInvalidQuote = errors.New("format: invalid quote") + errAffixTooLarge = errors.New("format: prefix or suffix exceeds maximum UTF-8 length of 256 bytes") + errDuplicatePercentSign = errors.New("format: duplicate percent sign") + errDuplicatePermilleSign = errors.New("format: duplicate permille sign") + errUnexpectedEnd = errors.New("format: unexpected end of pattern") +) + +// ParsePattern extracts formatting information from a CLDR number pattern. +// +// See https://unicode.org/reports/tr35/tr35-numbers.html#Number_Format_Patterns. +func ParsePattern(s string) (f *Pattern, err error) { + p := parser{Pattern: &Pattern{}} + + s = p.parseSubPattern(s) + + if s != "" { + // Parse negative sub pattern. + if s[0] != ';' { + p.setError(errors.New("format: error parsing first sub pattern")) + return nil, p.err + } + neg := parser{Pattern: &Pattern{}} // just for extracting the affixes. + s = neg.parseSubPattern(s[len(";"):]) + p.NegOffset = uint16(len(p.buf)) + p.buf = append(p.buf, neg.buf...) + } + if s != "" { + p.setError(errors.New("format: spurious characters at end of pattern")) + } + if p.err != nil { + return nil, p.err + } + if affix := string(p.buf); affix == "\x00\x00" || affix == "\x00\x00\x00\x00" { + // No prefix or suffixes. + p.NegOffset = 0 + } else { + p.Affix = affix + } + if p.Increment == 0 { + p.IncrementScale = 0 + } + return p.Pattern, nil +} + +func (p *parser) parseSubPattern(s string) string { + s = p.parsePad(s, PadBeforePrefix) + s = p.parseAffix(s) + s = p.parsePad(s, PadAfterPrefix) + + s = p.parse(p.number, s) + p.updateGrouping() + + s = p.parsePad(s, PadBeforeSuffix) + s = p.parseAffix(s) + s = p.parsePad(s, PadAfterSuffix) + return s +} + +func (p *parser) parsePad(s string, f PatternFlag) (tail string) { + if len(s) >= 2 && s[0] == '*' { + r, sz := utf8.DecodeRuneInString(s[1:]) + if p.PadRune != 0 { + p.err = errMultiplePadSpecifiers + } else { + p.Flags |= f + p.PadRune = r + } + return s[1+sz:] + } + return s +} + +func (p *parser) parseAffix(s string) string { + x := len(p.buf) + p.buf = append(p.buf, 0) // placeholder for affix length + + s = p.parse(p.affix, s) + + n := len(p.buf) - x - 1 + if n > 0xFF { + p.setError(errAffixTooLarge) + } + p.buf[x] = uint8(n) + return s +} + +// state implements a state transition. It returns the new state. A state +// function may set an error on the parser or may simply return on an incorrect +// token and let the next phase fail. +type state func(r rune) state + +// parse repeatedly applies a state function on the given string until a +// termination condition is reached. +func (p *parser) parse(fn state, s string) (tail string) { + for i, r := range s { + p.doNotTerminate = false + if fn = fn(r); fn == nil || p.err != nil { + return s[i:] + } + p.FormatWidth++ + } + if p.doNotTerminate { + p.setError(errUnexpectedEnd) + } + return "" +} + +func (p *parser) affix(r rune) state { + switch r { + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '#', '@', '.', '*', ',', ';': + return nil + case '\'': + p.FormatWidth-- + return p.escapeFirst + case '%': + if p.DigitShift != 0 { + p.setError(errDuplicatePercentSign) + } + p.DigitShift = 2 + case '\u2030': // ‰ Per mille + if p.DigitShift != 0 { + p.setError(errDuplicatePermilleSign) + } + p.DigitShift = 3 + // TODO: handle currency somehow: ¤, ¤¤, ¤¤¤, ¤¤¤¤ + } + p.buf = append(p.buf, string(r)...) + return p.affix +} + +func (p *parser) escapeFirst(r rune) state { + switch r { + case '\'': + p.buf = append(p.buf, "\\'"...) + return p.affix + default: + p.buf = append(p.buf, '\'') + p.buf = append(p.buf, string(r)...) + } + return p.escape +} + +func (p *parser) escape(r rune) state { + switch r { + case '\'': + p.FormatWidth-- + p.buf = append(p.buf, '\'') + return p.affix + default: + p.buf = append(p.buf, string(r)...) + } + return p.escape +} + +// number parses a number. The BNF says the integer part should always have +// a '0', but that does not appear to be the case according to the rest of the +// documentation. We will allow having only '#' numbers. +func (p *parser) number(r rune) state { + switch r { + case '#': + p.groupingCount++ + p.leadingSharps++ + case '@': + p.groupingCount++ + p.leadingSharps = 0 + p.MaxFractionDigits = -1 + return p.sigDigits(r) + case ',': + if p.leadingSharps == 0 { // no leading commas + return nil + } + p.updateGrouping() + case 'E': + p.MaxIntegerDigits = uint8(p.leadingSharps) + return p.exponent + case '.': // allow ".##" etc. + p.updateGrouping() + return p.fraction + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + return p.integer(r) + default: + return nil + } + return p.number +} + +func (p *parser) integer(r rune) state { + if !('0' <= r && r <= '9') { + var next state + switch r { + case 'E': + if p.leadingSharps > 0 { + p.MaxIntegerDigits = uint8(p.leadingSharps) + p.MinIntegerDigits + } + next = p.exponent + case '.': + next = p.fraction + case ',': + next = p.integer + } + p.updateGrouping() + return next + } + p.Increment = p.Increment*10 + uint32(r-'0') + p.groupingCount++ + p.MinIntegerDigits++ + return p.integer +} + +func (p *parser) sigDigits(r rune) state { + switch r { + case '@': + p.groupingCount++ + p.MaxSignificantDigits++ + p.MinSignificantDigits++ + case '#': + return p.sigDigitsFinal(r) + case 'E': + p.updateGrouping() + return p.normalizeSigDigitsWithExponent() + default: + p.updateGrouping() + return nil + } + return p.sigDigits +} + +func (p *parser) sigDigitsFinal(r rune) state { + switch r { + case '#': + p.groupingCount++ + p.MaxSignificantDigits++ + case 'E': + p.updateGrouping() + return p.normalizeSigDigitsWithExponent() + default: + p.updateGrouping() + return nil + } + return p.sigDigitsFinal +} + +func (p *parser) normalizeSigDigitsWithExponent() state { + p.MinIntegerDigits, p.MaxIntegerDigits = 1, 1 + p.MinFractionDigits = p.MinSignificantDigits - 1 + p.MaxFractionDigits = p.MaxSignificantDigits - 1 + p.MinSignificantDigits, p.MaxSignificantDigits = 0, 0 + return p.exponent +} + +func (p *parser) fraction(r rune) state { + switch r { + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + p.Increment = p.Increment*10 + uint32(r-'0') + p.IncrementScale++ + p.MinFractionDigits++ + p.MaxFractionDigits++ + case '#': + p.MaxFractionDigits++ + case 'E': + if p.leadingSharps > 0 { + p.MaxIntegerDigits = uint8(p.leadingSharps) + p.MinIntegerDigits + } + return p.exponent + default: + return nil + } + return p.fraction +} + +func (p *parser) exponent(r rune) state { + switch r { + case '+': + // Set mode and check it wasn't already set. + if p.Flags&AlwaysExpSign != 0 || p.MinExponentDigits > 0 { + break + } + p.Flags |= AlwaysExpSign + p.doNotTerminate = true + return p.exponent + case '0': + p.MinExponentDigits++ + return p.exponent + } + // termination condition + if p.MinExponentDigits == 0 { + p.setError(errors.New("format: need at least one digit")) + } + return nil +} diff --git a/vendor/golang.org/x/text/internal/number/roundingmode_string.go b/vendor/golang.org/x/text/internal/number/roundingmode_string.go new file mode 100644 index 0000000000..bcc22471db --- /dev/null +++ b/vendor/golang.org/x/text/internal/number/roundingmode_string.go @@ -0,0 +1,30 @@ +// Code generated by "stringer -type RoundingMode"; DO NOT EDIT. + +package number + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[ToNearestEven-0] + _ = x[ToNearestZero-1] + _ = x[ToNearestAway-2] + _ = x[ToPositiveInf-3] + _ = x[ToNegativeInf-4] + _ = x[ToZero-5] + _ = x[AwayFromZero-6] + _ = x[numModes-7] +} + +const _RoundingMode_name = "ToNearestEvenToNearestZeroToNearestAwayToPositiveInfToNegativeInfToZeroAwayFromZeronumModes" + +var _RoundingMode_index = [...]uint8{0, 13, 26, 39, 52, 65, 71, 83, 91} + +func (i RoundingMode) String() string { + if i >= RoundingMode(len(_RoundingMode_index)-1) { + return "RoundingMode(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _RoundingMode_name[_RoundingMode_index[i]:_RoundingMode_index[i+1]] +} diff --git a/vendor/golang.org/x/text/internal/number/tables.go b/vendor/golang.org/x/text/internal/number/tables.go new file mode 100644 index 0000000000..8efce81b56 --- /dev/null +++ b/vendor/golang.org/x/text/internal/number/tables.go @@ -0,0 +1,1219 @@ +// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. + +package number + +import "golang.org/x/text/internal/stringset" + +// CLDRVersion is the CLDR version from which the tables in this package are derived. +const CLDRVersion = "32" + +var numSysData = []systemData{ // 59 elements + 0: {id: 0x0, digitSize: 0x1, zero: [4]uint8{0x30, 0x0, 0x0, 0x0}}, + 1: {id: 0x1, digitSize: 0x4, zero: [4]uint8{0xf0, 0x9e, 0xa5, 0x90}}, + 2: {id: 0x2, digitSize: 0x4, zero: [4]uint8{0xf0, 0x91, 0x9c, 0xb0}}, + 3: {id: 0x3, digitSize: 0x2, zero: [4]uint8{0xd9, 0xa0, 0x0, 0x0}}, + 4: {id: 0x4, digitSize: 0x2, zero: [4]uint8{0xdb, 0xb0, 0x0, 0x0}}, + 5: {id: 0x5, digitSize: 0x3, zero: [4]uint8{0xe1, 0xad, 0x90, 0x0}}, + 6: {id: 0x6, digitSize: 0x3, zero: [4]uint8{0xe0, 0xa7, 0xa6, 0x0}}, + 7: {id: 0x7, digitSize: 0x4, zero: [4]uint8{0xf0, 0x91, 0xb1, 0x90}}, + 8: {id: 0x8, digitSize: 0x4, zero: [4]uint8{0xf0, 0x91, 0x81, 0xa6}}, + 9: {id: 0x9, digitSize: 0x4, zero: [4]uint8{0xf0, 0x91, 0x84, 0xb6}}, + 10: {id: 0xa, digitSize: 0x3, zero: [4]uint8{0xea, 0xa9, 0x90, 0x0}}, + 11: {id: 0xb, digitSize: 0x3, zero: [4]uint8{0xe0, 0xa5, 0xa6, 0x0}}, + 12: {id: 0xc, digitSize: 0x3, zero: [4]uint8{0xef, 0xbc, 0x90, 0x0}}, + 13: {id: 0xd, digitSize: 0x4, zero: [4]uint8{0xf0, 0x91, 0xb5, 0x90}}, + 14: {id: 0xe, digitSize: 0x3, zero: [4]uint8{0xe0, 0xab, 0xa6, 0x0}}, + 15: {id: 0xf, digitSize: 0x3, zero: [4]uint8{0xe0, 0xa9, 0xa6, 0x0}}, + 16: {id: 0x10, digitSize: 0x4, zero: [4]uint8{0xf0, 0x96, 0xad, 0x90}}, + 17: {id: 0x11, digitSize: 0x3, zero: [4]uint8{0xea, 0xa7, 0x90, 0x0}}, + 18: {id: 0x12, digitSize: 0x3, zero: [4]uint8{0xea, 0xa4, 0x80, 0x0}}, + 19: {id: 0x13, digitSize: 0x3, zero: [4]uint8{0xe1, 0x9f, 0xa0, 0x0}}, + 20: {id: 0x14, digitSize: 0x3, zero: [4]uint8{0xe0, 0xb3, 0xa6, 0x0}}, + 21: {id: 0x15, digitSize: 0x3, zero: [4]uint8{0xe1, 0xaa, 0x80, 0x0}}, + 22: {id: 0x16, digitSize: 0x3, zero: [4]uint8{0xe1, 0xaa, 0x90, 0x0}}, + 23: {id: 0x17, digitSize: 0x3, zero: [4]uint8{0xe0, 0xbb, 0x90, 0x0}}, + 24: {id: 0x18, digitSize: 0x3, zero: [4]uint8{0xe1, 0xb1, 0x80, 0x0}}, + 25: {id: 0x19, digitSize: 0x3, zero: [4]uint8{0xe1, 0xa5, 0x86, 0x0}}, + 26: {id: 0x1a, digitSize: 0x4, zero: [4]uint8{0xf0, 0x9d, 0x9f, 0x8e}}, + 27: {id: 0x1b, digitSize: 0x4, zero: [4]uint8{0xf0, 0x9d, 0x9f, 0x98}}, + 28: {id: 0x1c, digitSize: 0x4, zero: [4]uint8{0xf0, 0x9d, 0x9f, 0xb6}}, + 29: {id: 0x1d, digitSize: 0x4, zero: [4]uint8{0xf0, 0x9d, 0x9f, 0xac}}, + 30: {id: 0x1e, digitSize: 0x4, zero: [4]uint8{0xf0, 0x9d, 0x9f, 0xa2}}, + 31: {id: 0x1f, digitSize: 0x3, zero: [4]uint8{0xe0, 0xb5, 0xa6, 0x0}}, + 32: {id: 0x20, digitSize: 0x4, zero: [4]uint8{0xf0, 0x91, 0x99, 0x90}}, + 33: {id: 0x21, digitSize: 0x3, zero: [4]uint8{0xe1, 0xa0, 0x90, 0x0}}, + 34: {id: 0x22, digitSize: 0x4, zero: [4]uint8{0xf0, 0x96, 0xa9, 0xa0}}, + 35: {id: 0x23, digitSize: 0x3, zero: [4]uint8{0xea, 0xaf, 0xb0, 0x0}}, + 36: {id: 0x24, digitSize: 0x3, zero: [4]uint8{0xe1, 0x81, 0x80, 0x0}}, + 37: {id: 0x25, digitSize: 0x3, zero: [4]uint8{0xe1, 0x82, 0x90, 0x0}}, + 38: {id: 0x26, digitSize: 0x3, zero: [4]uint8{0xea, 0xa7, 0xb0, 0x0}}, + 39: {id: 0x27, digitSize: 0x4, zero: [4]uint8{0xf0, 0x91, 0x91, 0x90}}, + 40: {id: 0x28, digitSize: 0x2, zero: [4]uint8{0xdf, 0x80, 0x0, 0x0}}, + 41: {id: 0x29, digitSize: 0x3, zero: [4]uint8{0xe1, 0xb1, 0x90, 0x0}}, + 42: {id: 0x2a, digitSize: 0x3, zero: [4]uint8{0xe0, 0xad, 0xa6, 0x0}}, + 43: {id: 0x2b, digitSize: 0x4, zero: [4]uint8{0xf0, 0x90, 0x92, 0xa0}}, + 44: {id: 0x2c, digitSize: 0x3, zero: [4]uint8{0xea, 0xa3, 0x90, 0x0}}, + 45: {id: 0x2d, digitSize: 0x4, zero: [4]uint8{0xf0, 0x91, 0x87, 0x90}}, + 46: {id: 0x2e, digitSize: 0x4, zero: [4]uint8{0xf0, 0x91, 0x8b, 0xb0}}, + 47: {id: 0x2f, digitSize: 0x3, zero: [4]uint8{0xe0, 0xb7, 0xa6, 0x0}}, + 48: {id: 0x30, digitSize: 0x4, zero: [4]uint8{0xf0, 0x91, 0x83, 0xb0}}, + 49: {id: 0x31, digitSize: 0x3, zero: [4]uint8{0xe1, 0xae, 0xb0, 0x0}}, + 50: {id: 0x32, digitSize: 0x4, zero: [4]uint8{0xf0, 0x91, 0x9b, 0x80}}, + 51: {id: 0x33, digitSize: 0x3, zero: [4]uint8{0xe1, 0xa7, 0x90, 0x0}}, + 52: {id: 0x34, digitSize: 0x3, zero: [4]uint8{0xe0, 0xaf, 0xa6, 0x0}}, + 53: {id: 0x35, digitSize: 0x3, zero: [4]uint8{0xe0, 0xb1, 0xa6, 0x0}}, + 54: {id: 0x36, digitSize: 0x3, zero: [4]uint8{0xe0, 0xb9, 0x90, 0x0}}, + 55: {id: 0x37, digitSize: 0x3, zero: [4]uint8{0xe0, 0xbc, 0xa0, 0x0}}, + 56: {id: 0x38, digitSize: 0x4, zero: [4]uint8{0xf0, 0x91, 0x93, 0x90}}, + 57: {id: 0x39, digitSize: 0x3, zero: [4]uint8{0xea, 0x98, 0xa0, 0x0}}, + 58: {id: 0x3a, digitSize: 0x4, zero: [4]uint8{0xf0, 0x91, 0xa3, 0xa0}}, +} // Size: 378 bytes + +const ( + numAdlm = 0x1 + numAhom = 0x2 + numArab = 0x3 + numArabext = 0x4 + numArmn = 0x3b + numArmnlow = 0x3c + numBali = 0x5 + numBeng = 0x6 + numBhks = 0x7 + numBrah = 0x8 + numCakm = 0x9 + numCham = 0xa + numCyrl = 0x3d + numDeva = 0xb + numEthi = 0x3e + numFullwide = 0xc + numGeor = 0x3f + numGonm = 0xd + numGrek = 0x40 + numGreklow = 0x41 + numGujr = 0xe + numGuru = 0xf + numHanidays = 0x42 + numHanidec = 0x43 + numHans = 0x44 + numHansfin = 0x45 + numHant = 0x46 + numHantfin = 0x47 + numHebr = 0x48 + numHmng = 0x10 + numJava = 0x11 + numJpan = 0x49 + numJpanfin = 0x4a + numKali = 0x12 + numKhmr = 0x13 + numKnda = 0x14 + numLana = 0x15 + numLanatham = 0x16 + numLaoo = 0x17 + numLatn = 0x0 + numLepc = 0x18 + numLimb = 0x19 + numMathbold = 0x1a + numMathdbl = 0x1b + numMathmono = 0x1c + numMathsanb = 0x1d + numMathsans = 0x1e + numMlym = 0x1f + numModi = 0x20 + numMong = 0x21 + numMroo = 0x22 + numMtei = 0x23 + numMymr = 0x24 + numMymrshan = 0x25 + numMymrtlng = 0x26 + numNewa = 0x27 + numNkoo = 0x28 + numOlck = 0x29 + numOrya = 0x2a + numOsma = 0x2b + numRoman = 0x4b + numRomanlow = 0x4c + numSaur = 0x2c + numShrd = 0x2d + numSind = 0x2e + numSinh = 0x2f + numSora = 0x30 + numSund = 0x31 + numTakr = 0x32 + numTalu = 0x33 + numTaml = 0x4d + numTamldec = 0x34 + numTelu = 0x35 + numThai = 0x36 + numTibt = 0x37 + numTirh = 0x38 + numVaii = 0x39 + numWara = 0x3a + numNumberSystems +) + +var systemMap = map[string]system{ + "adlm": numAdlm, + "ahom": numAhom, + "arab": numArab, + "arabext": numArabext, + "armn": numArmn, + "armnlow": numArmnlow, + "bali": numBali, + "beng": numBeng, + "bhks": numBhks, + "brah": numBrah, + "cakm": numCakm, + "cham": numCham, + "cyrl": numCyrl, + "deva": numDeva, + "ethi": numEthi, + "fullwide": numFullwide, + "geor": numGeor, + "gonm": numGonm, + "grek": numGrek, + "greklow": numGreklow, + "gujr": numGujr, + "guru": numGuru, + "hanidays": numHanidays, + "hanidec": numHanidec, + "hans": numHans, + "hansfin": numHansfin, + "hant": numHant, + "hantfin": numHantfin, + "hebr": numHebr, + "hmng": numHmng, + "java": numJava, + "jpan": numJpan, + "jpanfin": numJpanfin, + "kali": numKali, + "khmr": numKhmr, + "knda": numKnda, + "lana": numLana, + "lanatham": numLanatham, + "laoo": numLaoo, + "latn": numLatn, + "lepc": numLepc, + "limb": numLimb, + "mathbold": numMathbold, + "mathdbl": numMathdbl, + "mathmono": numMathmono, + "mathsanb": numMathsanb, + "mathsans": numMathsans, + "mlym": numMlym, + "modi": numModi, + "mong": numMong, + "mroo": numMroo, + "mtei": numMtei, + "mymr": numMymr, + "mymrshan": numMymrshan, + "mymrtlng": numMymrtlng, + "newa": numNewa, + "nkoo": numNkoo, + "olck": numOlck, + "orya": numOrya, + "osma": numOsma, + "roman": numRoman, + "romanlow": numRomanlow, + "saur": numSaur, + "shrd": numShrd, + "sind": numSind, + "sinh": numSinh, + "sora": numSora, + "sund": numSund, + "takr": numTakr, + "talu": numTalu, + "taml": numTaml, + "tamldec": numTamldec, + "telu": numTelu, + "thai": numThai, + "tibt": numTibt, + "tirh": numTirh, + "vaii": numVaii, + "wara": numWara, +} + +var symIndex = [][12]uint8{ // 81 elements + 0: [12]uint8{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb}, + 1: [12]uint8{0x1, 0xc, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb}, + 2: [12]uint8{0x0, 0x1, 0x2, 0xd, 0xe, 0xf, 0x6, 0x7, 0x8, 0x9, 0x10, 0xb}, + 3: [12]uint8{0x1, 0x0, 0x2, 0xd, 0xe, 0xf, 0x6, 0x7, 0x8, 0x9, 0x10, 0xb}, + 4: [12]uint8{0x0, 0x1, 0x2, 0x11, 0xe, 0xf, 0x6, 0x7, 0x8, 0x9, 0x10, 0xb}, + 5: [12]uint8{0x1, 0x0, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x12, 0xb}, + 6: [12]uint8{0x1, 0x0, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb}, + 7: [12]uint8{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x13, 0xb}, + 8: [12]uint8{0x0, 0x1, 0x2, 0x3, 0xe, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb}, + 9: [12]uint8{0x1, 0x0, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0x0}, + 10: [12]uint8{0x1, 0x0, 0x2, 0x3, 0x4, 0x5, 0x6, 0x14, 0x8, 0x9, 0xa, 0xb}, + 11: [12]uint8{0x1, 0xc, 0x2, 0x3, 0x4, 0x5, 0x6, 0x14, 0x8, 0x9, 0xa, 0xb}, + 12: [12]uint8{0x0, 0x15, 0x2, 0x3, 0x4, 0x5, 0x6, 0x14, 0x8, 0x9, 0xa, 0xb}, + 13: [12]uint8{0x0, 0xc, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb}, + 14: [12]uint8{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x16, 0xb}, + 15: [12]uint8{0x1, 0x0, 0x2, 0x3, 0x4, 0x5, 0x17, 0x7, 0x8, 0x9, 0xa, 0xb}, + 16: [12]uint8{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x17, 0x7, 0x8, 0x9, 0xa, 0x0}, + 17: [12]uint8{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x17, 0x7, 0x8, 0x9, 0xa, 0xb}, + 18: [12]uint8{0x1, 0xc, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0x0}, + 19: [12]uint8{0x1, 0xc, 0x2, 0x3, 0x4, 0x5, 0x18, 0x7, 0x8, 0x9, 0xa, 0xb}, + 20: [12]uint8{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x19, 0x1a, 0xa, 0xb}, + 21: [12]uint8{0x1, 0xc, 0x2, 0x3, 0x4, 0x1b, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb}, + 22: [12]uint8{0x1, 0xc, 0x2, 0x3, 0x4, 0x1b, 0x18, 0x7, 0x8, 0x9, 0xa, 0xb}, + 23: [12]uint8{0x1, 0x0, 0x2, 0x3, 0x4, 0x1b, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb}, + 24: [12]uint8{0x0, 0x1, 0x2, 0x3, 0xe, 0x1c, 0x6, 0x7, 0x8, 0x9, 0x1d, 0xb}, + 25: [12]uint8{0x1, 0xc, 0x2, 0x3, 0x4, 0x1b, 0x6, 0x7, 0x8, 0x9, 0x1e, 0x0}, + 26: [12]uint8{0x0, 0x15, 0x2, 0x3, 0x4, 0x1b, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb}, + 27: [12]uint8{0x0, 0x1, 0x2, 0x3, 0xe, 0xf, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb}, + 28: [12]uint8{0x1, 0xc, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x1f, 0xb}, + 29: [12]uint8{0x0, 0x15, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb}, + 30: [12]uint8{0x1, 0xc, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x20, 0xb}, + 31: [12]uint8{0x1, 0xc, 0x2, 0x3, 0x4, 0x5, 0x21, 0x7, 0x8, 0x9, 0x22, 0xb}, + 32: [12]uint8{0x1, 0xc, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x23, 0xb}, + 33: [12]uint8{0x1, 0x0, 0x2, 0x3, 0x4, 0x1b, 0x18, 0x14, 0x8, 0x9, 0x24, 0xb}, + 34: [12]uint8{0x1, 0xc, 0x2, 0x3, 0x4, 0x1b, 0x18, 0x7, 0x8, 0x9, 0x24, 0xb}, + 35: [12]uint8{0x1, 0xc, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x25, 0xb}, + 36: [12]uint8{0x1, 0x0, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x26, 0xb}, + 37: [12]uint8{0x1, 0xc, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x27, 0xb}, + 38: [12]uint8{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x28, 0xb}, + 39: [12]uint8{0x1, 0xc, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x29, 0xb}, + 40: [12]uint8{0x1, 0x0, 0x2, 0x3, 0xe, 0x1c, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb}, + 41: [12]uint8{0x1, 0xc, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x2a, 0xb}, + 42: [12]uint8{0x1, 0xc, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x2b, 0xb}, + 43: [12]uint8{0x1, 0xc, 0x2, 0x3, 0x4, 0x1b, 0x2c, 0x14, 0x8, 0x9, 0x24, 0xb}, + 44: [12]uint8{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0x0}, + 45: [12]uint8{0x1, 0xc, 0x2, 0x3, 0x4, 0x5, 0x17, 0x7, 0x8, 0x9, 0xa, 0xb}, + 46: [12]uint8{0x1, 0x0, 0x2, 0x3, 0x4, 0x1b, 0x17, 0x7, 0x8, 0x9, 0xa, 0xb}, + 47: [12]uint8{0x1, 0xc, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x2d, 0x0}, + 48: [12]uint8{0x1, 0xc, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x2e, 0xb}, + 49: [12]uint8{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x2f, 0xb}, + 50: [12]uint8{0x1, 0xc, 0x2, 0x3, 0x4, 0x5, 0x30, 0x7, 0x8, 0x9, 0xa, 0xb}, + 51: [12]uint8{0x1, 0xc, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x31, 0xb}, + 52: [12]uint8{0x1, 0xc, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x32, 0xb}, + 53: [12]uint8{0x1, 0x15, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb}, + 54: [12]uint8{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x33, 0xb}, + 55: [12]uint8{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x34, 0xb}, + 56: [12]uint8{0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x7, 0x3c, 0x9, 0xa, 0xb}, + 57: [12]uint8{0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x7, 0x3c, 0x9, 0x3d, 0xb}, + 58: [12]uint8{0x35, 0x36, 0x37, 0x11, 0x3e, 0x3f, 0x3b, 0x7, 0x3c, 0x9, 0xa, 0xb}, + 59: [12]uint8{0x35, 0x36, 0x37, 0x11, 0x39, 0x3a, 0x3b, 0x7, 0x3c, 0x9, 0xa, 0xb}, + 60: [12]uint8{0x35, 0x36, 0x37, 0x11, 0x39, 0x40, 0x3b, 0x7, 0x3c, 0x9, 0xa, 0xb}, + 61: [12]uint8{0x35, 0x36, 0x37, 0x41, 0x3e, 0x3f, 0x3b, 0x7, 0x3c, 0x9, 0xa, 0xb}, + 62: [12]uint8{0x35, 0x36, 0x37, 0x38, 0x3e, 0x3f, 0x3b, 0x7, 0x3c, 0x9, 0xa, 0xb}, + 63: [12]uint8{0x35, 0xc, 0x37, 0x38, 0x39, 0x42, 0x3b, 0x7, 0x3c, 0x9, 0xa, 0x0}, + 64: [12]uint8{0x35, 0xc, 0x37, 0x38, 0x39, 0x42, 0x43, 0x7, 0x44, 0x9, 0x24, 0xb}, + 65: [12]uint8{0x35, 0x36, 0x37, 0x38, 0x39, 0x5, 0x3b, 0x7, 0x3c, 0x9, 0x33, 0xb}, + 66: [12]uint8{0x35, 0x36, 0x37, 0x11, 0x45, 0x46, 0x43, 0x7, 0x3c, 0x9, 0xa, 0x35}, + 67: [12]uint8{0x35, 0x36, 0x37, 0x11, 0xe, 0x1c, 0x43, 0x7, 0x3c, 0x9, 0x1d, 0xb}, + 68: [12]uint8{0x35, 0x36, 0x37, 0x11, 0xe, 0x1c, 0x43, 0x7, 0x3c, 0x9, 0xa, 0x35}, + 69: [12]uint8{0x35, 0x36, 0x37, 0x11, 0x45, 0x5, 0x43, 0x7, 0x3c, 0x9, 0xa, 0x35}, + 70: [12]uint8{0x1, 0xc, 0x37, 0x11, 0x45, 0x47, 0x43, 0x7, 0x3c, 0x9, 0xa, 0x0}, + 71: [12]uint8{0x35, 0x1, 0x37, 0x11, 0x4, 0x5, 0x43, 0x7, 0x3c, 0x9, 0xa, 0x35}, + 72: [12]uint8{0x1, 0xc, 0x37, 0x11, 0x45, 0x47, 0x43, 0x7, 0x3c, 0x9, 0x24, 0xb}, + 73: [12]uint8{0x35, 0x36, 0x2, 0x3, 0x45, 0x46, 0x43, 0x7, 0x8, 0x9, 0xa, 0x35}, + 74: [12]uint8{0x35, 0x36, 0x37, 0x11, 0x4, 0x5, 0x43, 0x7, 0x3c, 0x9, 0x31, 0x35}, + 75: [12]uint8{0x35, 0x36, 0x37, 0x11, 0x4, 0x5, 0x43, 0x7, 0x3c, 0x9, 0x32, 0x35}, + 76: [12]uint8{0x35, 0x36, 0x37, 0x11, 0x48, 0x46, 0x43, 0x7, 0x3c, 0x9, 0x33, 0x35}, + 77: [12]uint8{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0x49}, + 78: [12]uint8{0x0, 0x1, 0x4a, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x28, 0xb}, + 79: [12]uint8{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x4b, 0xb}, + 80: [12]uint8{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x4c, 0x4d, 0xb}, +} // Size: 996 bytes + +var symData = stringset.Set{ + Data: "" + // Size: 599 bytes + ".,;%+-E׉∞NaN:\u00a0\u200e%\u200e\u200e+\u200e-ليس\u00a0رقمًا٪NDТерхьаш" + + "\u00a0дац·’mnne×10^0/00INF−\u200e−ناعددepälukuՈչԹარ\u00a0არის\u00a0რიცხვ" + + "იZMdMсан\u00a0емес¤¤¤сан\u00a0эмесບໍ່\u200bແມ່ນ\u200bໂຕ\u200bເລກNSဂဏန်" + + "းမဟုတ်သောННне\u00a0числочыыһыла\u00a0буотах·10^epilohosan\u00a0dälTFЕs" + + "on\u00a0emasҳақиқий\u00a0сон\u00a0эмас非數值非数值٫٬؛٪\u061c\u061c+\u061c-اس؉ل" + + "يس\u00a0رقم\u200f+\u200f-\u200f−٪\u200f\u061c−×۱۰^؉\u200f\u200e+\u200e" + + "\u200e-\u200e\u200e−\u200e+\u200e:၊ཨང་མེན་གྲངས་མེདཨང་མད", + Index: []uint16{ // 79 elements + // Entry 0 - 3F + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, + 0x0009, 0x000c, 0x000f, 0x0012, 0x0013, 0x0015, 0x001c, 0x0020, + 0x0024, 0x0036, 0x0038, 0x003a, 0x0050, 0x0052, 0x0055, 0x0058, + 0x0059, 0x005e, 0x0062, 0x0065, 0x0068, 0x006e, 0x0078, 0x0080, + 0x0086, 0x00ae, 0x00af, 0x00b2, 0x00c2, 0x00c8, 0x00d8, 0x0105, + 0x0107, 0x012e, 0x0132, 0x0142, 0x015e, 0x0163, 0x016a, 0x0173, + 0x0175, 0x0177, 0x0180, 0x01a0, 0x01a9, 0x01b2, 0x01b4, 0x01b6, + 0x01b8, 0x01bc, 0x01bf, 0x01c2, 0x01c6, 0x01c8, 0x01d6, 0x01da, + // Entry 40 - 7F + 0x01de, 0x01e4, 0x01e9, 0x01ee, 0x01f5, 0x01fa, 0x0201, 0x0208, + 0x0211, 0x0215, 0x0218, 0x021b, 0x0230, 0x0248, 0x0257, + }, +} // Size: 797 bytes + +// langToDefaults maps a compact language index to the default numbering system +// and default symbol set +var langToDefaults = [775]symOffset{ + // Entry 0 - 3F + 0x8000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, + 0x0000, 0x0000, 0x8003, 0x0002, 0x0002, 0x0002, 0x0002, 0x0003, + 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, + 0x0003, 0x0003, 0x0003, 0x0003, 0x0002, 0x0002, 0x0002, 0x0004, + 0x0002, 0x0004, 0x0002, 0x0002, 0x0002, 0x0003, 0x0002, 0x0000, + 0x8005, 0x0000, 0x0000, 0x0000, 0x8006, 0x0005, 0x0006, 0x0006, + 0x0006, 0x0006, 0x0006, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0000, + // Entry 40 - 7F + 0x8009, 0x0000, 0x0000, 0x800a, 0x0000, 0x0000, 0x800c, 0x0001, + 0x0000, 0x0000, 0x0006, 0x0006, 0x0006, 0x0006, 0x0006, 0x0006, + 0x0006, 0x0006, 0x0006, 0x0006, 0x800e, 0x0000, 0x0000, 0x0007, + 0x0007, 0x0000, 0x0000, 0x0000, 0x0000, 0x800f, 0x0008, 0x0008, + 0x8011, 0x0001, 0x0001, 0x0001, 0x803c, 0x0000, 0x0009, 0x0009, + 0x0009, 0x0000, 0x0000, 0x000a, 0x000b, 0x000a, 0x000c, 0x000a, + 0x000a, 0x000c, 0x000a, 0x000d, 0x000d, 0x000a, 0x000a, 0x0001, + 0x0001, 0x0000, 0x0001, 0x0001, 0x803f, 0x0000, 0x0000, 0x0000, + // Entry 80 - BF + 0x000e, 0x000e, 0x000e, 0x000f, 0x000f, 0x000f, 0x0000, 0x0000, + 0x0006, 0x0000, 0x0000, 0x0000, 0x000a, 0x0010, 0x0000, 0x0006, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0011, 0x0000, 0x000a, + 0x0000, 0x0000, 0x0000, 0x0000, 0x000a, 0x0000, 0x0009, 0x0000, + 0x0000, 0x0012, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + // Entry C0 - FF + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0006, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0013, 0x0000, + 0x0000, 0x000f, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0015, + 0x0015, 0x0006, 0x0000, 0x0006, 0x0006, 0x0000, 0x0000, 0x0006, + 0x0006, 0x0001, 0x0000, 0x0000, 0x0006, 0x0006, 0x0006, 0x0006, + // Entry 100 - 13F + 0x0000, 0x0000, 0x0006, 0x0000, 0x0000, 0x0000, 0x0000, 0x0006, + 0x0000, 0x0006, 0x0000, 0x0000, 0x0006, 0x0006, 0x0016, 0x0016, + 0x0017, 0x0017, 0x0001, 0x0001, 0x8041, 0x0018, 0x0018, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0019, 0x0019, 0x0000, 0x0000, + 0x0017, 0x0017, 0x0017, 0x8044, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0006, 0x0006, 0x0001, 0x0001, 0x0001, 0x0001, + // Entry 140 - 17F + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0006, 0x0006, 0x0006, 0x0006, 0x0000, 0x0000, + 0x8047, 0x0000, 0x0006, 0x0006, 0x001a, 0x001a, 0x001a, 0x001a, + 0x804a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x804c, 0x001b, 0x0000, + 0x0000, 0x0006, 0x0006, 0x0006, 0x000a, 0x000a, 0x0001, 0x0001, + 0x001c, 0x001c, 0x0009, 0x0009, 0x804f, 0x0000, 0x0000, 0x0000, + // Entry 180 - 1BF + 0x0000, 0x0000, 0x8052, 0x0006, 0x0006, 0x001d, 0x0006, 0x0006, + 0x0006, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0006, 0x0006, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x001e, 0x001e, 0x001f, + 0x001f, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, + 0x0001, 0x000d, 0x000d, 0x0000, 0x0000, 0x0020, 0x0020, 0x0006, + 0x0006, 0x0021, 0x0021, 0x0000, 0x0000, 0x0006, 0x0006, 0x0000, + 0x0000, 0x8054, 0x0000, 0x0000, 0x0000, 0x0000, 0x8056, 0x001b, + 0x0000, 0x0000, 0x0001, 0x0001, 0x0022, 0x0022, 0x0000, 0x0000, + // Entry 1C0 - 1FF + 0x0000, 0x0023, 0x0023, 0x0000, 0x0000, 0x0006, 0x0006, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0006, 0x0006, 0x0006, 0x0006, 0x0006, + 0x0024, 0x0024, 0x8058, 0x0000, 0x0000, 0x0016, 0x0016, 0x0006, + 0x0006, 0x0000, 0x0000, 0x0000, 0x0000, 0x0025, 0x0025, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x000d, 0x000d, 0x0000, 0x0000, + 0x0006, 0x0006, 0x0000, 0x0000, 0x0006, 0x0006, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x805a, 0x0000, 0x0000, 0x0006, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0006, 0x0006, 0x805b, 0x0026, 0x805d, + // Entry 200 - 23F + 0x0000, 0x0000, 0x0000, 0x0000, 0x805e, 0x0015, 0x0015, 0x0000, + 0x0000, 0x0006, 0x0006, 0x0006, 0x8061, 0x0000, 0x0000, 0x8062, + 0x0006, 0x0006, 0x0006, 0x0006, 0x0006, 0x0006, 0x0006, 0x0001, + 0x0001, 0x0015, 0x0015, 0x0006, 0x0006, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0027, 0x0027, 0x0027, 0x8065, 0x8067, + 0x001b, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, + 0x8069, 0x0028, 0x0006, 0x0001, 0x0006, 0x0001, 0x0001, 0x0001, + // Entry 240 - 27F + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, + 0x0006, 0x0000, 0x0000, 0x001a, 0x001a, 0x0006, 0x0006, 0x0006, + 0x0006, 0x0006, 0x0000, 0x0000, 0x0029, 0x0029, 0x0029, 0x0029, + 0x0029, 0x0029, 0x0029, 0x0006, 0x0006, 0x0000, 0x0000, 0x002a, + 0x002a, 0x0000, 0x0000, 0x0000, 0x0000, 0x806b, 0x0000, 0x0000, + 0x002b, 0x002b, 0x002b, 0x002b, 0x0006, 0x0006, 0x000d, 0x000d, + 0x0006, 0x0006, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x002c, 0x002c, 0x002d, 0x002d, 0x002e, 0x002e, 0x0000, 0x0000, + // Entry 280 - 2BF + 0x0000, 0x002f, 0x002f, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0006, + 0x0006, 0x0006, 0x0006, 0x0006, 0x0006, 0x0006, 0x0006, 0x0006, + 0x0006, 0x0006, 0x0000, 0x0000, 0x0000, 0x806d, 0x0022, 0x0022, + 0x0022, 0x0000, 0x0006, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0030, 0x0030, 0x0000, 0x0000, 0x8071, 0x0031, 0x0006, + // Entry 2C0 - 2FF + 0x0006, 0x0006, 0x0000, 0x0001, 0x0001, 0x000d, 0x000d, 0x0001, + 0x0001, 0x0000, 0x0000, 0x0032, 0x0032, 0x8074, 0x8076, 0x001b, + 0x8077, 0x8079, 0x0028, 0x807b, 0x0034, 0x0033, 0x0033, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0006, 0x0006, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0035, 0x0035, 0x0006, 0x0006, + 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0036, 0x0037, 0x0037, 0x0036, 0x0036, 0x0001, + 0x0001, 0x807d, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x8080, + // Entry 300 - 33F + 0x0036, 0x0036, 0x0036, 0x0000, 0x0000, 0x0006, 0x0014, +} // Size: 1550 bytes + +// langToAlt is a list of numbering system and symbol set pairs, sorted and +// marked by compact language index. +var langToAlt = []altSymData{ // 131 elements + 1: {compactTag: 0x0, symIndex: 0x38, system: 0x3}, + 2: {compactTag: 0x0, symIndex: 0x42, system: 0x4}, + 3: {compactTag: 0xa, symIndex: 0x39, system: 0x3}, + 4: {compactTag: 0xa, symIndex: 0x2, system: 0x0}, + 5: {compactTag: 0x28, symIndex: 0x0, system: 0x6}, + 6: {compactTag: 0x2c, symIndex: 0x5, system: 0x0}, + 7: {compactTag: 0x2c, symIndex: 0x3a, system: 0x3}, + 8: {compactTag: 0x2c, symIndex: 0x42, system: 0x4}, + 9: {compactTag: 0x40, symIndex: 0x0, system: 0x6}, + 10: {compactTag: 0x43, symIndex: 0x0, system: 0x0}, + 11: {compactTag: 0x43, symIndex: 0x4f, system: 0x37}, + 12: {compactTag: 0x46, symIndex: 0x1, system: 0x0}, + 13: {compactTag: 0x46, symIndex: 0x38, system: 0x3}, + 14: {compactTag: 0x54, symIndex: 0x0, system: 0x9}, + 15: {compactTag: 0x5d, symIndex: 0x3a, system: 0x3}, + 16: {compactTag: 0x5d, symIndex: 0x8, system: 0x0}, + 17: {compactTag: 0x60, symIndex: 0x1, system: 0x0}, + 18: {compactTag: 0x60, symIndex: 0x38, system: 0x3}, + 19: {compactTag: 0x60, symIndex: 0x42, system: 0x4}, + 20: {compactTag: 0x60, symIndex: 0x0, system: 0x5}, + 21: {compactTag: 0x60, symIndex: 0x0, system: 0x6}, + 22: {compactTag: 0x60, symIndex: 0x0, system: 0x8}, + 23: {compactTag: 0x60, symIndex: 0x0, system: 0x9}, + 24: {compactTag: 0x60, symIndex: 0x0, system: 0xa}, + 25: {compactTag: 0x60, symIndex: 0x0, system: 0xb}, + 26: {compactTag: 0x60, symIndex: 0x0, system: 0xc}, + 27: {compactTag: 0x60, symIndex: 0x0, system: 0xd}, + 28: {compactTag: 0x60, symIndex: 0x0, system: 0xe}, + 29: {compactTag: 0x60, symIndex: 0x0, system: 0xf}, + 30: {compactTag: 0x60, symIndex: 0x0, system: 0x11}, + 31: {compactTag: 0x60, symIndex: 0x0, system: 0x12}, + 32: {compactTag: 0x60, symIndex: 0x0, system: 0x13}, + 33: {compactTag: 0x60, symIndex: 0x0, system: 0x14}, + 34: {compactTag: 0x60, symIndex: 0x0, system: 0x15}, + 35: {compactTag: 0x60, symIndex: 0x0, system: 0x16}, + 36: {compactTag: 0x60, symIndex: 0x0, system: 0x17}, + 37: {compactTag: 0x60, symIndex: 0x0, system: 0x18}, + 38: {compactTag: 0x60, symIndex: 0x0, system: 0x19}, + 39: {compactTag: 0x60, symIndex: 0x0, system: 0x1f}, + 40: {compactTag: 0x60, symIndex: 0x0, system: 0x21}, + 41: {compactTag: 0x60, symIndex: 0x0, system: 0x23}, + 42: {compactTag: 0x60, symIndex: 0x0, system: 0x24}, + 43: {compactTag: 0x60, symIndex: 0x0, system: 0x25}, + 44: {compactTag: 0x60, symIndex: 0x0, system: 0x28}, + 45: {compactTag: 0x60, symIndex: 0x0, system: 0x29}, + 46: {compactTag: 0x60, symIndex: 0x0, system: 0x2a}, + 47: {compactTag: 0x60, symIndex: 0x0, system: 0x2b}, + 48: {compactTag: 0x60, symIndex: 0x0, system: 0x2c}, + 49: {compactTag: 0x60, symIndex: 0x0, system: 0x2d}, + 50: {compactTag: 0x60, symIndex: 0x0, system: 0x30}, + 51: {compactTag: 0x60, symIndex: 0x0, system: 0x31}, + 52: {compactTag: 0x60, symIndex: 0x0, system: 0x32}, + 53: {compactTag: 0x60, symIndex: 0x0, system: 0x33}, + 54: {compactTag: 0x60, symIndex: 0x0, system: 0x34}, + 55: {compactTag: 0x60, symIndex: 0x0, system: 0x35}, + 56: {compactTag: 0x60, symIndex: 0x0, system: 0x36}, + 57: {compactTag: 0x60, symIndex: 0x0, system: 0x37}, + 58: {compactTag: 0x60, symIndex: 0x0, system: 0x39}, + 59: {compactTag: 0x60, symIndex: 0x0, system: 0x43}, + 60: {compactTag: 0x64, symIndex: 0x0, system: 0x0}, + 61: {compactTag: 0x64, symIndex: 0x38, system: 0x3}, + 62: {compactTag: 0x64, symIndex: 0x42, system: 0x4}, + 63: {compactTag: 0x7c, symIndex: 0x50, system: 0x37}, + 64: {compactTag: 0x7c, symIndex: 0x0, system: 0x0}, + 65: {compactTag: 0x114, symIndex: 0x43, system: 0x4}, + 66: {compactTag: 0x114, symIndex: 0x18, system: 0x0}, + 67: {compactTag: 0x114, symIndex: 0x3b, system: 0x3}, + 68: {compactTag: 0x123, symIndex: 0x1, system: 0x0}, + 69: {compactTag: 0x123, symIndex: 0x3c, system: 0x3}, + 70: {compactTag: 0x123, symIndex: 0x44, system: 0x4}, + 71: {compactTag: 0x158, symIndex: 0x0, system: 0x0}, + 72: {compactTag: 0x158, symIndex: 0x3b, system: 0x3}, + 73: {compactTag: 0x158, symIndex: 0x45, system: 0x4}, + 74: {compactTag: 0x160, symIndex: 0x0, system: 0x0}, + 75: {compactTag: 0x160, symIndex: 0x38, system: 0x3}, + 76: {compactTag: 0x16d, symIndex: 0x1b, system: 0x0}, + 77: {compactTag: 0x16d, symIndex: 0x0, system: 0x9}, + 78: {compactTag: 0x16d, symIndex: 0x0, system: 0xa}, + 79: {compactTag: 0x17c, symIndex: 0x0, system: 0x0}, + 80: {compactTag: 0x17c, symIndex: 0x3d, system: 0x3}, + 81: {compactTag: 0x17c, symIndex: 0x42, system: 0x4}, + 82: {compactTag: 0x182, symIndex: 0x6, system: 0x0}, + 83: {compactTag: 0x182, symIndex: 0x38, system: 0x3}, + 84: {compactTag: 0x1b1, symIndex: 0x0, system: 0x0}, + 85: {compactTag: 0x1b1, symIndex: 0x3e, system: 0x3}, + 86: {compactTag: 0x1b6, symIndex: 0x42, system: 0x4}, + 87: {compactTag: 0x1b6, symIndex: 0x1b, system: 0x0}, + 88: {compactTag: 0x1d2, symIndex: 0x42, system: 0x4}, + 89: {compactTag: 0x1d2, symIndex: 0x0, system: 0x0}, + 90: {compactTag: 0x1f3, symIndex: 0x0, system: 0xb}, + 91: {compactTag: 0x1fd, symIndex: 0x4e, system: 0x24}, + 92: {compactTag: 0x1fd, symIndex: 0x26, system: 0x0}, + 93: {compactTag: 0x1ff, symIndex: 0x42, system: 0x4}, + 94: {compactTag: 0x204, symIndex: 0x15, system: 0x0}, + 95: {compactTag: 0x204, symIndex: 0x3f, system: 0x3}, + 96: {compactTag: 0x204, symIndex: 0x46, system: 0x4}, + 97: {compactTag: 0x20c, symIndex: 0x0, system: 0xb}, + 98: {compactTag: 0x20f, symIndex: 0x6, system: 0x0}, + 99: {compactTag: 0x20f, symIndex: 0x38, system: 0x3}, + 100: {compactTag: 0x20f, symIndex: 0x42, system: 0x4}, + 101: {compactTag: 0x22e, symIndex: 0x0, system: 0x0}, + 102: {compactTag: 0x22e, symIndex: 0x47, system: 0x4}, + 103: {compactTag: 0x22f, symIndex: 0x42, system: 0x4}, + 104: {compactTag: 0x22f, symIndex: 0x1b, system: 0x0}, + 105: {compactTag: 0x238, symIndex: 0x42, system: 0x4}, + 106: {compactTag: 0x238, symIndex: 0x28, system: 0x0}, + 107: {compactTag: 0x265, symIndex: 0x38, system: 0x3}, + 108: {compactTag: 0x265, symIndex: 0x0, system: 0x0}, + 109: {compactTag: 0x29d, symIndex: 0x22, system: 0x0}, + 110: {compactTag: 0x29d, symIndex: 0x40, system: 0x3}, + 111: {compactTag: 0x29d, symIndex: 0x48, system: 0x4}, + 112: {compactTag: 0x29d, symIndex: 0x4d, system: 0xc}, + 113: {compactTag: 0x2bd, symIndex: 0x31, system: 0x0}, + 114: {compactTag: 0x2bd, symIndex: 0x3e, system: 0x3}, + 115: {compactTag: 0x2bd, symIndex: 0x42, system: 0x4}, + 116: {compactTag: 0x2cd, symIndex: 0x1b, system: 0x0}, + 117: {compactTag: 0x2cd, symIndex: 0x49, system: 0x4}, + 118: {compactTag: 0x2ce, symIndex: 0x49, system: 0x4}, + 119: {compactTag: 0x2d0, symIndex: 0x33, system: 0x0}, + 120: {compactTag: 0x2d0, symIndex: 0x4a, system: 0x4}, + 121: {compactTag: 0x2d1, symIndex: 0x42, system: 0x4}, + 122: {compactTag: 0x2d1, symIndex: 0x28, system: 0x0}, + 123: {compactTag: 0x2d3, symIndex: 0x34, system: 0x0}, + 124: {compactTag: 0x2d3, symIndex: 0x4b, system: 0x4}, + 125: {compactTag: 0x2f9, symIndex: 0x0, system: 0x0}, + 126: {compactTag: 0x2f9, symIndex: 0x38, system: 0x3}, + 127: {compactTag: 0x2f9, symIndex: 0x42, system: 0x4}, + 128: {compactTag: 0x2ff, symIndex: 0x36, system: 0x0}, + 129: {compactTag: 0x2ff, symIndex: 0x41, system: 0x3}, + 130: {compactTag: 0x2ff, symIndex: 0x4c, system: 0x4}, +} // Size: 810 bytes + +var tagToDecimal = []uint8{ // 775 elements + // Entry 0 - 3F + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x05, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + // Entry 40 - 7F + 0x05, 0x05, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x05, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x05, 0x05, 0x05, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x05, 0x05, 0x01, 0x01, + // Entry 80 - BF + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + // Entry C0 - FF + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + // Entry 100 - 13F + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + // Entry 140 - 17F + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x05, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x05, + 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + // Entry 180 - 1BF + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x05, 0x05, 0x05, 0x05, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + // Entry 1C0 - 1FF + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x05, 0x05, + 0x01, 0x01, 0x01, 0x05, 0x05, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + // Entry 200 - 23F + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x05, 0x05, 0x01, 0x01, 0x01, 0x05, 0x01, + 0x01, 0x05, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + // Entry 240 - 27F + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + // Entry 280 - 2BF + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x05, + 0x05, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + // Entry 2C0 - 2FF + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + // Entry 300 - 33F + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x08, +} // Size: 799 bytes + +var tagToScientific = []uint8{ // 775 elements + // Entry 0 - 3F + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + // Entry 40 - 7F + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + // Entry 80 - BF + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + // Entry C0 - FF + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + // Entry 100 - 13F + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + // Entry 140 - 17F + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x0c, 0x0c, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x0c, + 0x0c, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + // Entry 180 - 1BF + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + // Entry 1C0 - 1FF + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x0d, 0x0d, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x0c, 0x0c, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + // Entry 200 - 23F + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x0c, 0x02, + 0x02, 0x0c, 0x0c, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + // Entry 240 - 27F + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x0d, 0x0d, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + // Entry 280 - 2BF + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + // Entry 2C0 - 2FF + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + // Entry 300 - 33F + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x09, +} // Size: 799 bytes + +var tagToPercent = []uint8{ // 775 elements + // Entry 0 - 3F + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x06, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + // Entry 40 - 7F + 0x06, 0x06, 0x06, 0x04, 0x04, 0x04, 0x03, 0x03, + 0x06, 0x06, 0x03, 0x04, 0x04, 0x03, 0x03, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x06, 0x06, 0x06, 0x03, + 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, + 0x03, 0x04, 0x04, 0x03, 0x03, 0x03, 0x04, 0x03, + 0x03, 0x04, 0x03, 0x04, 0x04, 0x03, 0x03, 0x03, + 0x03, 0x04, 0x04, 0x04, 0x07, 0x07, 0x04, 0x04, + // Entry 80 - BF + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x03, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x03, 0x04, 0x03, 0x04, + 0x04, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + // Entry C0 - FF + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + // Entry 100 - 13F + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, + 0x0b, 0x0b, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x04, 0x04, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + // Entry 140 - 17F + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x06, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x06, + 0x06, 0x04, 0x04, 0x04, 0x03, 0x03, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + // Entry 180 - 1BF + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x06, 0x06, 0x06, 0x06, + 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x04, 0x04, + // Entry 1C0 - 1FF + 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + // Entry 200 - 23F + 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x06, 0x06, 0x04, 0x04, 0x04, 0x06, 0x04, + 0x04, 0x06, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + // Entry 240 - 27F + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, + // Entry 280 - 2BF + 0x04, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, + 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x06, + 0x06, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x0e, + // Entry 2C0 - 2FF + 0x0e, 0x0e, 0x04, 0x03, 0x03, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, + 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + // Entry 300 - 33F + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x0a, +} // Size: 799 bytes + +var formats = []Pattern{Pattern{RoundingContext: RoundingContext{MaxSignificantDigits: 0, + MaxFractionDigits: 0, + Increment: 0x0, + IncrementScale: 0x0, + Mode: 0x0, + DigitShift: 0x0, + MinIntegerDigits: 0x0, + MaxIntegerDigits: 0x0, + MinFractionDigits: 0x0, + MinSignificantDigits: 0x0, + MinExponentDigits: 0x0}, + Affix: "", + Offset: 0x0, + NegOffset: 0x0, + PadRune: 0, + FormatWidth: 0x0, + GroupingSize: [2]uint8{0x0, + 0x0}, + Flags: 0x0}, + Pattern{RoundingContext: RoundingContext{MaxSignificantDigits: 0, + MaxFractionDigits: 3, + Increment: 0x0, + IncrementScale: 0x0, + Mode: 0x0, + DigitShift: 0x0, + MinIntegerDigits: 0x1, + MaxIntegerDigits: 0x0, + MinFractionDigits: 0x0, + MinSignificantDigits: 0x0, + MinExponentDigits: 0x0}, + Affix: "", + Offset: 0x0, + NegOffset: 0x0, + PadRune: 0, + FormatWidth: 0x9, + GroupingSize: [2]uint8{0x3, + 0x0}, + Flags: 0x0}, + Pattern{RoundingContext: RoundingContext{MaxSignificantDigits: 0, + MaxFractionDigits: 0, + Increment: 0x0, + IncrementScale: 0x0, + Mode: 0x0, + DigitShift: 0x0, + MinIntegerDigits: 0x0, + MaxIntegerDigits: 0x1, + MinFractionDigits: 0x0, + MinSignificantDigits: 0x0, + MinExponentDigits: 0x1}, + Affix: "", + Offset: 0x0, + NegOffset: 0x0, + PadRune: 0, + FormatWidth: 0x3, + GroupingSize: [2]uint8{0x0, + 0x0}, + Flags: 0x0}, + Pattern{RoundingContext: RoundingContext{MaxSignificantDigits: 0, + MaxFractionDigits: 0, + Increment: 0x0, + IncrementScale: 0x0, + Mode: 0x0, + DigitShift: 0x2, + MinIntegerDigits: 0x1, + MaxIntegerDigits: 0x0, + MinFractionDigits: 0x0, + MinSignificantDigits: 0x0, + MinExponentDigits: 0x0}, + Affix: "\x00\x03\u00a0%", + Offset: 0x0, + NegOffset: 0x0, + PadRune: 0, + FormatWidth: 0x7, + GroupingSize: [2]uint8{0x3, + 0x0}, + Flags: 0x0}, + Pattern{RoundingContext: RoundingContext{MaxSignificantDigits: 0, + MaxFractionDigits: 0, + Increment: 0x0, + IncrementScale: 0x0, + Mode: 0x0, + DigitShift: 0x2, + MinIntegerDigits: 0x1, + MaxIntegerDigits: 0x0, + MinFractionDigits: 0x0, + MinSignificantDigits: 0x0, + MinExponentDigits: 0x0}, + Affix: "\x00\x01%", + Offset: 0x0, + NegOffset: 0x0, + PadRune: 0, + FormatWidth: 0x6, + GroupingSize: [2]uint8{0x3, + 0x0}, + Flags: 0x0}, + Pattern{RoundingContext: RoundingContext{MaxSignificantDigits: 0, + MaxFractionDigits: 3, + Increment: 0x0, + IncrementScale: 0x0, + Mode: 0x0, + DigitShift: 0x0, + MinIntegerDigits: 0x1, + MaxIntegerDigits: 0x0, + MinFractionDigits: 0x0, + MinSignificantDigits: 0x0, + MinExponentDigits: 0x0}, + Affix: "", + Offset: 0x0, + NegOffset: 0x0, + PadRune: 0, + FormatWidth: 0xc, + GroupingSize: [2]uint8{0x3, + 0x2}, + Flags: 0x0}, + Pattern{RoundingContext: RoundingContext{MaxSignificantDigits: 0, + MaxFractionDigits: 0, + Increment: 0x0, + IncrementScale: 0x0, + Mode: 0x0, + DigitShift: 0x2, + MinIntegerDigits: 0x1, + MaxIntegerDigits: 0x0, + MinFractionDigits: 0x0, + MinSignificantDigits: 0x0, + MinExponentDigits: 0x0}, + Affix: "\x00\x01%", + Offset: 0x0, + NegOffset: 0x0, + PadRune: 0, + FormatWidth: 0x9, + GroupingSize: [2]uint8{0x3, + 0x2}, + Flags: 0x0}, + Pattern{RoundingContext: RoundingContext{MaxSignificantDigits: 0, + MaxFractionDigits: 0, + Increment: 0x0, + IncrementScale: 0x0, + Mode: 0x0, + DigitShift: 0x2, + MinIntegerDigits: 0x1, + MaxIntegerDigits: 0x0, + MinFractionDigits: 0x0, + MinSignificantDigits: 0x0, + MinExponentDigits: 0x0}, + Affix: "\x00\x03\u00a0%", + Offset: 0x0, + NegOffset: 0x0, + PadRune: 0, + FormatWidth: 0xa, + GroupingSize: [2]uint8{0x3, + 0x2}, + Flags: 0x0}, + Pattern{RoundingContext: RoundingContext{MaxSignificantDigits: 0, + MaxFractionDigits: 6, + Increment: 0x0, + IncrementScale: 0x0, + Mode: 0x0, + DigitShift: 0x0, + MinIntegerDigits: 0x1, + MaxIntegerDigits: 0x0, + MinFractionDigits: 0x0, + MinSignificantDigits: 0x0, + MinExponentDigits: 0x0}, + Affix: "", + Offset: 0x0, + NegOffset: 0x0, + PadRune: 0, + FormatWidth: 0x8, + GroupingSize: [2]uint8{0x0, + 0x0}, + Flags: 0x0}, + Pattern{RoundingContext: RoundingContext{MaxSignificantDigits: 0, + MaxFractionDigits: 6, + Increment: 0x0, + IncrementScale: 0x0, + Mode: 0x0, + DigitShift: 0x0, + MinIntegerDigits: 0x1, + MaxIntegerDigits: 0x0, + MinFractionDigits: 0x6, + MinSignificantDigits: 0x0, + MinExponentDigits: 0x3}, + Affix: "", + Offset: 0x0, + NegOffset: 0x0, + PadRune: 0, + FormatWidth: 0xd, + GroupingSize: [2]uint8{0x0, + 0x0}, + Flags: 0x4}, + Pattern{RoundingContext: RoundingContext{MaxSignificantDigits: 0, + MaxFractionDigits: 0, + Increment: 0x0, + IncrementScale: 0x0, + Mode: 0x0, + DigitShift: 0x2, + MinIntegerDigits: 0x1, + MaxIntegerDigits: 0x0, + MinFractionDigits: 0x0, + MinSignificantDigits: 0x0, + MinExponentDigits: 0x0}, + Affix: "\x00\x01%", + Offset: 0x0, + NegOffset: 0x0, + PadRune: 0, + FormatWidth: 0x2, + GroupingSize: [2]uint8{0x0, + 0x0}, + Flags: 0x0}, + Pattern{RoundingContext: RoundingContext{MaxSignificantDigits: 0, + MaxFractionDigits: 0, + Increment: 0x0, + IncrementScale: 0x0, + Mode: 0x0, + DigitShift: 0x2, + MinIntegerDigits: 0x1, + MaxIntegerDigits: 0x0, + MinFractionDigits: 0x0, + MinSignificantDigits: 0x0, + MinExponentDigits: 0x0}, + Affix: "\x03%\u00a0\x00", + Offset: 0x0, + NegOffset: 0x0, + PadRune: 0, + FormatWidth: 0x7, + GroupingSize: [2]uint8{0x3, + 0x0}, + Flags: 0x0}, + Pattern{RoundingContext: RoundingContext{MaxSignificantDigits: 0, + MaxFractionDigits: 0, + Increment: 0x0, + IncrementScale: 0x0, + Mode: 0x0, + DigitShift: 0x0, + MinIntegerDigits: 0x0, + MaxIntegerDigits: 0x1, + MinFractionDigits: 0x0, + MinSignificantDigits: 0x0, + MinExponentDigits: 0x1}, + Affix: "\x01[\x01]", + Offset: 0x0, + NegOffset: 0x0, + PadRune: 0, + FormatWidth: 0x5, + GroupingSize: [2]uint8{0x0, + 0x0}, + Flags: 0x0}, + Pattern{RoundingContext: RoundingContext{MaxSignificantDigits: 0, + MaxFractionDigits: 0, + Increment: 0x0, + IncrementScale: 0x0, + Mode: 0x0, + DigitShift: 0x0, + MinIntegerDigits: 0x0, + MaxIntegerDigits: 0x0, + MinFractionDigits: 0x0, + MinSignificantDigits: 0x0, + MinExponentDigits: 0x0}, + Affix: "", + Offset: 0x0, + NegOffset: 0x0, + PadRune: 0, + FormatWidth: 0x1, + GroupingSize: [2]uint8{0x0, + 0x0}, + Flags: 0x0}, + Pattern{RoundingContext: RoundingContext{MaxSignificantDigits: 0, + MaxFractionDigits: 0, + Increment: 0x0, + IncrementScale: 0x0, + Mode: 0x0, + DigitShift: 0x2, + MinIntegerDigits: 0x1, + MaxIntegerDigits: 0x0, + MinFractionDigits: 0x0, + MinSignificantDigits: 0x0, + MinExponentDigits: 0x0}, + Affix: "\x01%\x00", + Offset: 0x0, + NegOffset: 0x0, + PadRune: 0, + FormatWidth: 0x6, + GroupingSize: [2]uint8{0x3, + 0x0}, + Flags: 0x0}} + +// Total table size 8634 bytes (8KiB); checksum: 8F23386D diff --git a/vendor/golang.org/x/text/internal/stringset/set.go b/vendor/golang.org/x/text/internal/stringset/set.go new file mode 100644 index 0000000000..bb2fffbc75 --- /dev/null +++ b/vendor/golang.org/x/text/internal/stringset/set.go @@ -0,0 +1,86 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package stringset provides a way to represent a collection of strings +// compactly. +package stringset + +import "sort" + +// A Set holds a collection of strings that can be looked up by an index number. +type Set struct { + // These fields are exported to allow for code generation. + + Data string + Index []uint16 +} + +// Elem returns the string with index i. It panics if i is out of range. +func (s *Set) Elem(i int) string { + return s.Data[s.Index[i]:s.Index[i+1]] +} + +// Len returns the number of strings in the set. +func (s *Set) Len() int { + return len(s.Index) - 1 +} + +// Search returns the index of the given string or -1 if it is not in the set. +// The Set must have been created with strings in sorted order. +func Search(s *Set, str string) int { + // TODO: optimize this if it gets used a lot. + n := len(s.Index) - 1 + p := sort.Search(n, func(i int) bool { + return s.Elem(i) >= str + }) + if p == n || str != s.Elem(p) { + return -1 + } + return p +} + +// A Builder constructs Sets. +type Builder struct { + set Set + index map[string]int +} + +// NewBuilder returns a new and initialized Builder. +func NewBuilder() *Builder { + return &Builder{ + set: Set{ + Index: []uint16{0}, + }, + index: map[string]int{}, + } +} + +// Set creates the set created so far. +func (b *Builder) Set() Set { + return b.set +} + +// Index returns the index for the given string, which must have been added +// before. +func (b *Builder) Index(s string) int { + return b.index[s] +} + +// Add adds a string to the index. Strings that are added by a single Add will +// be stored together, unless they match an existing string. +func (b *Builder) Add(ss ...string) { + // First check if the string already exists. + for _, s := range ss { + if _, ok := b.index[s]; ok { + continue + } + b.index[s] = len(b.set.Index) - 1 + b.set.Data += s + x := len(b.set.Data) + if x > 0xFFFF { + panic("Index too > 0xFFFF") + } + b.set.Index = append(b.set.Index, uint16(x)) + } +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 44dd70b2c5..a17150d472 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,3 +1,7 @@ +# connectrpc.com/connect v1.20.0 +## explicit; go 1.25.0 +connectrpc.com/connect +connectrpc.com/connect/internal/gen/connectext/grpc/status/v1 # github.com/KimMachineGun/automemlimit v0.7.5 ## explicit; go 1.22.0 github.com/KimMachineGun/automemlimit/memlimit @@ -10,7 +14,7 @@ github.com/alecthomas/units # github.com/armon/go-metrics v0.4.1 ## explicit; go 1.12 github.com/armon/go-metrics -# github.com/aws/aws-sdk-go-v2 v1.41.7 +# github.com/aws/aws-sdk-go-v2 v1.43.2 ## explicit; go 1.24 github.com/aws/aws-sdk-go-v2/aws github.com/aws/aws-sdk-go-v2/aws/defaults @@ -35,11 +39,11 @@ github.com/aws/aws-sdk-go-v2/internal/shareddefaults github.com/aws/aws-sdk-go-v2/internal/strings github.com/aws/aws-sdk-go-v2/internal/sync/singleflight github.com/aws/aws-sdk-go-v2/internal/timeconv -# github.com/aws/aws-sdk-go-v2/config v1.32.17 +# github.com/aws/aws-sdk-go-v2/config v1.32.33 ## explicit; go 1.24 github.com/aws/aws-sdk-go-v2/config github.com/aws/aws-sdk-go-v2/config/internal/ini -# github.com/aws/aws-sdk-go-v2/credentials v1.19.16 +# github.com/aws/aws-sdk-go-v2/credentials v1.19.32 ## explicit; go 1.24 github.com/aws/aws-sdk-go-v2/credentials github.com/aws/aws-sdk-go-v2/credentials/ec2rolecreds @@ -49,53 +53,53 @@ github.com/aws/aws-sdk-go-v2/credentials/logincreds github.com/aws/aws-sdk-go-v2/credentials/processcreds github.com/aws/aws-sdk-go-v2/credentials/ssocreds github.com/aws/aws-sdk-go-v2/credentials/stscreds -# github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.23 +# github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.33 ## explicit; go 1.24 github.com/aws/aws-sdk-go-v2/feature/ec2/imds github.com/aws/aws-sdk-go-v2/feature/ec2/imds/internal/config -# github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.23 +# github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.33 ## explicit; go 1.24 github.com/aws/aws-sdk-go-v2/internal/configsources -# github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.23 +# github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.33 ## explicit; go 1.24 github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 -# github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.24 +# github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.34 ## explicit; go 1.24 github.com/aws/aws-sdk-go-v2/internal/v4a github.com/aws/aws-sdk-go-v2/internal/v4a/internal/crypto github.com/aws/aws-sdk-go-v2/internal/v4a/internal/v4 -# github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.9 +# github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.14 ## explicit; go 1.24 github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding -# github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.23 +# github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.33 ## explicit; go 1.24 github.com/aws/aws-sdk-go-v2/service/internal/presigned-url -# github.com/aws/aws-sdk-go-v2/service/signin v1.0.11 +# github.com/aws/aws-sdk-go-v2/service/signin v1.5.2 ## explicit; go 1.24 github.com/aws/aws-sdk-go-v2/service/signin github.com/aws/aws-sdk-go-v2/service/signin/internal/endpoints github.com/aws/aws-sdk-go-v2/service/signin/types -# github.com/aws/aws-sdk-go-v2/service/sns v1.39.17 +# github.com/aws/aws-sdk-go-v2/service/sns v1.42.2 ## explicit; go 1.24 github.com/aws/aws-sdk-go-v2/service/sns github.com/aws/aws-sdk-go-v2/service/sns/internal/endpoints github.com/aws/aws-sdk-go-v2/service/sns/types -# github.com/aws/aws-sdk-go-v2/service/sso v1.30.17 +# github.com/aws/aws-sdk-go-v2/service/sso v1.33.2 ## explicit; go 1.24 github.com/aws/aws-sdk-go-v2/service/sso github.com/aws/aws-sdk-go-v2/service/sso/internal/endpoints github.com/aws/aws-sdk-go-v2/service/sso/types -# github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.21 +# github.com/aws/aws-sdk-go-v2/service/ssooidc v1.38.2 ## explicit; go 1.24 github.com/aws/aws-sdk-go-v2/service/ssooidc github.com/aws/aws-sdk-go-v2/service/ssooidc/internal/endpoints github.com/aws/aws-sdk-go-v2/service/ssooidc/types -# github.com/aws/aws-sdk-go-v2/service/sts v1.42.1 +# github.com/aws/aws-sdk-go-v2/service/sts v1.45.2 ## explicit; go 1.24 github.com/aws/aws-sdk-go-v2/service/sts github.com/aws/aws-sdk-go-v2/service/sts/internal/endpoints github.com/aws/aws-sdk-go-v2/service/sts/types -# github.com/aws/smithy-go v1.25.1 +# github.com/aws/smithy-go v1.27.5 ## explicit; go 1.24 github.com/aws/smithy-go github.com/aws/smithy-go/auth @@ -107,7 +111,9 @@ github.com/aws/smithy-go/encoding/httpbinding github.com/aws/smithy-go/encoding/json github.com/aws/smithy-go/encoding/xml github.com/aws/smithy-go/endpoints +github.com/aws/smithy-go/endpoints/private/bdd github.com/aws/smithy-go/endpoints/private/rulesfn +github.com/aws/smithy-go/eventstream github.com/aws/smithy-go/internal/sync/singleflight github.com/aws/smithy-go/io github.com/aws/smithy-go/logging @@ -116,25 +122,21 @@ github.com/aws/smithy-go/middleware github.com/aws/smithy-go/private/requestcompression github.com/aws/smithy-go/ptr github.com/aws/smithy-go/rand +github.com/aws/smithy-go/sync github.com/aws/smithy-go/time github.com/aws/smithy-go/tracing +github.com/aws/smithy-go/traits github.com/aws/smithy-go/transport/http github.com/aws/smithy-go/transport/http/internal/io # github.com/beorn7/perks v1.0.1 ## explicit; go 1.11 github.com/beorn7/perks/quantile -# github.com/cenkalti/backoff/v4 v4.3.0 -## explicit; go 1.18 -github.com/cenkalti/backoff/v4 # github.com/cenkalti/backoff/v5 v5.0.3 ## explicit; go 1.23 github.com/cenkalti/backoff/v5 # github.com/cespare/xxhash/v2 v2.3.0 ## explicit; go 1.11 github.com/cespare/xxhash/v2 -# github.com/coder/quartz v0.3.1 -## explicit; go 1.23.9 -github.com/coder/quartz # github.com/coreos/go-systemd/v22 v22.7.0 ## explicit; go 1.23 github.com/coreos/go-systemd/v22/activation @@ -153,18 +155,18 @@ github.com/emersion/go-smtp # github.com/felixge/httpsnoop v1.0.4 ## explicit; go 1.13 github.com/felixge/httpsnoop -# github.com/fsnotify/fsnotify v1.10.0 +# github.com/fsnotify/fsnotify v1.10.1 ## explicit; go 1.23 github.com/fsnotify/fsnotify github.com/fsnotify/fsnotify/internal -# github.com/go-logr/logr v1.4.3 +# github.com/go-logr/logr v1.4.4 ## explicit; go 1.18 github.com/go-logr/logr github.com/go-logr/logr/funcr # github.com/go-logr/stdr v1.2.2 ## explicit; go 1.16 github.com/go-logr/stdr -# github.com/go-openapi/analysis v0.25.0 +# github.com/go-openapi/analysis v0.25.5 ## explicit; go 1.25.0 github.com/go-openapi/analysis github.com/go-openapi/analysis/internal/debug @@ -173,85 +175,93 @@ github.com/go-openapi/analysis/internal/flatten/operations github.com/go-openapi/analysis/internal/flatten/replace github.com/go-openapi/analysis/internal/flatten/schutils github.com/go-openapi/analysis/internal/flatten/sortref -# github.com/go-openapi/errors v0.22.7 -## explicit; go 1.24.0 +# github.com/go-openapi/errors v0.22.8 +## explicit; go 1.25.0 github.com/go-openapi/errors -# github.com/go-openapi/jsonpointer v0.22.5 -## explicit; go 1.24.0 +# github.com/go-openapi/jsonpointer v1.0.0 +## explicit; go 1.25.0 github.com/go-openapi/jsonpointer -# github.com/go-openapi/jsonreference v0.21.5 -## explicit; go 1.24.0 +github.com/go-openapi/jsonpointer/jsonname +# github.com/go-openapi/jsonreference v1.0.0 +## explicit; go 1.25.0 github.com/go-openapi/jsonreference github.com/go-openapi/jsonreference/internal -# github.com/go-openapi/loads v0.23.3 -## explicit; go 1.24.0 +# github.com/go-openapi/loads v0.25.0 +## explicit; go 1.25.0 github.com/go-openapi/loads -# github.com/go-openapi/runtime v0.29.4 +# github.com/go-openapi/runtime v0.33.0 ## explicit; go 1.25.0 github.com/go-openapi/runtime github.com/go-openapi/runtime/client +github.com/go-openapi/runtime/client/internal/request github.com/go-openapi/runtime/flagext github.com/go-openapi/runtime/logger github.com/go-openapi/runtime/middleware github.com/go-openapi/runtime/middleware/denco -github.com/go-openapi/runtime/middleware/header github.com/go-openapi/runtime/middleware/untyped github.com/go-openapi/runtime/security github.com/go-openapi/runtime/yamlpc -# github.com/go-openapi/spec v0.22.4 -## explicit; go 1.24.0 +# github.com/go-openapi/runtime/server-middleware v0.33.0 +## explicit; go 1.25.0 +github.com/go-openapi/runtime/server-middleware/docui +github.com/go-openapi/runtime/server-middleware/mediatype +github.com/go-openapi/runtime/server-middleware/negotiate +github.com/go-openapi/runtime/server-middleware/negotiate/header +# github.com/go-openapi/spec v0.22.9 +## explicit; go 1.25.0 github.com/go-openapi/spec -# github.com/go-openapi/strfmt v0.26.2 +# github.com/go-openapi/strfmt v0.27.0 ## explicit; go 1.25.0 github.com/go-openapi/strfmt github.com/go-openapi/strfmt/internal/bsonlite -# github.com/go-openapi/swag v0.26.0 +github.com/go-openapi/strfmt/internal/countries +# github.com/go-openapi/swag v0.28.0 ## explicit; go 1.25.0 github.com/go-openapi/swag -# github.com/go-openapi/swag/cmdutils v0.26.0 +# github.com/go-openapi/swag/cmdutils v0.28.0 ## explicit; go 1.25.0 github.com/go-openapi/swag/cmdutils -# github.com/go-openapi/swag/conv v0.26.0 +# github.com/go-openapi/swag/conv v0.28.0 ## explicit; go 1.25.0 github.com/go-openapi/swag/conv -# github.com/go-openapi/swag/fileutils v0.26.0 +# github.com/go-openapi/swag/fileutils v0.28.0 ## explicit; go 1.25.0 github.com/go-openapi/swag/fileutils -# github.com/go-openapi/swag/jsonname v0.26.0 -## explicit; go 1.25.0 -github.com/go-openapi/swag/jsonname -# github.com/go-openapi/swag/jsonutils v0.26.0 +# github.com/go-openapi/swag/jsonutils v0.28.0 ## explicit; go 1.25.0 github.com/go-openapi/swag/jsonutils github.com/go-openapi/swag/jsonutils/adapters github.com/go-openapi/swag/jsonutils/adapters/ifaces github.com/go-openapi/swag/jsonutils/adapters/stdlib/json -# github.com/go-openapi/swag/loading v0.26.0 +# github.com/go-openapi/swag/loading v0.28.0 ## explicit; go 1.25.0 github.com/go-openapi/swag/loading -# github.com/go-openapi/swag/mangling v0.26.0 +# github.com/go-openapi/swag/mangling v0.28.0 ## explicit; go 1.25.0 github.com/go-openapi/swag/mangling -# github.com/go-openapi/swag/netutils v0.26.0 +# github.com/go-openapi/swag/netutils v0.28.0 ## explicit; go 1.25.0 github.com/go-openapi/swag/netutils -# github.com/go-openapi/swag/stringutils v0.26.0 +# github.com/go-openapi/swag/pools v0.28.0 +## explicit; go 1.25.0 +github.com/go-openapi/swag/pools +# github.com/go-openapi/swag/stringutils v0.28.0 ## explicit; go 1.25.0 github.com/go-openapi/swag/stringutils -# github.com/go-openapi/swag/typeutils v0.26.0 +# github.com/go-openapi/swag/typeutils v0.28.0 ## explicit; go 1.25.0 github.com/go-openapi/swag/typeutils -# github.com/go-openapi/swag/yamlutils v0.26.0 +# github.com/go-openapi/swag/yamlutils v0.28.0 ## explicit; go 1.25.0 github.com/go-openapi/swag/yamlutils -# github.com/go-openapi/validate v0.25.2 -## explicit; go 1.24.0 +# github.com/go-openapi/validate v0.26.1 +## explicit; go 1.25.0 github.com/go-openapi/validate # github.com/go-viper/mapstructure/v2 v2.5.0 ## explicit; go 1.18 github.com/go-viper/mapstructure/v2 github.com/go-viper/mapstructure/v2/internal/errors -# github.com/golang-jwt/jwt/v5 v5.3.0 +# github.com/golang-jwt/jwt/v5 v5.3.1 ## explicit; go 1.21 github.com/golang-jwt/jwt/v5 # github.com/google/btree v1.1.3 @@ -260,8 +270,8 @@ github.com/google/btree # github.com/google/uuid v1.6.0 ## explicit github.com/google/uuid -# github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 -## explicit; go 1.24.0 +# github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 +## explicit; go 1.25.0 github.com/grpc-ecosystem/grpc-gateway/v2/internal/httprule github.com/grpc-ecosystem/grpc-gateway/v2/runtime github.com/grpc-ecosystem/grpc-gateway/v2/utilities @@ -304,7 +314,7 @@ github.com/jpillora/backoff # github.com/julienschmidt/httprouter v1.3.0 ## explicit; go 1.7 github.com/julienschmidt/httprouter -# github.com/klauspost/compress v1.18.6 +# github.com/klauspost/compress v1.19.1 ## explicit; go 1.24 github.com/klauspost/compress github.com/klauspost/compress/fse @@ -319,11 +329,11 @@ github.com/klauspost/compress/zstd/internal/xxhash # github.com/kylelemons/godebug v1.1.0 ## explicit; go 1.11 github.com/kylelemons/godebug/diff -# github.com/mdlayher/socket v0.4.1 -## explicit; go 1.20 +# github.com/mdlayher/socket v0.6.0 +## explicit; go 1.25.0 github.com/mdlayher/socket -# github.com/mdlayher/vsock v1.2.1 -## explicit; go 1.20 +# github.com/mdlayher/vsock v1.3.0 +## explicit; go 1.25.0 github.com/mdlayher/vsock # github.com/miekg/dns v1.1.72 ## explicit; go 1.24.0 @@ -337,7 +347,7 @@ github.com/mwitkow/go-conntrack # github.com/oklog/run v1.2.0 ## explicit; go 1.20 github.com/oklog/run -# github.com/oklog/ulid/v2 v2.1.1 +# github.com/oklog/ulid/v2 v2.1.2 ## explicit; go 1.15 github.com/oklog/ulid/v2 # github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 @@ -353,8 +363,8 @@ github.com/pierrec/lz4/v4/internal/xxh32 # github.com/pmezard/go-difflib v1.0.0 ## explicit github.com/pmezard/go-difflib/difflib -# github.com/prometheus/client_golang v1.23.2 -## explicit; go 1.23.0 +# github.com/prometheus/client_golang v1.24.1 +## explicit; go 1.25.0 github.com/prometheus/client_golang/internal/github.com/golang/gddo/httputil github.com/prometheus/client_golang/internal/github.com/golang/gddo/httputil/header github.com/prometheus/client_golang/prometheus @@ -369,8 +379,8 @@ github.com/prometheus/client_golang/prometheus/testutil/promlint/validations # github.com/prometheus/client_model v0.6.2 ## explicit; go 1.22.0 github.com/prometheus/client_model/go -# github.com/prometheus/common v0.67.5 -## explicit; go 1.24.0 +# github.com/prometheus/common v0.70.1 +## explicit; go 1.25.0 github.com/prometheus/common/config github.com/prometheus/common/expfmt github.com/prometheus/common/helpers/templates @@ -379,12 +389,12 @@ github.com/prometheus/common/promslog github.com/prometheus/common/promslog/flag github.com/prometheus/common/route github.com/prometheus/common/version -# github.com/prometheus/exporter-toolkit v0.16.0 +# github.com/prometheus/exporter-toolkit v0.17.1 ## explicit; go 1.25.0 github.com/prometheus/exporter-toolkit/web github.com/prometheus/exporter-toolkit/web/kingpinflag -# github.com/prometheus/procfs v0.16.1 -## explicit; go 1.23.0 +# github.com/prometheus/procfs v0.21.1 +## explicit; go 1.25.0 github.com/prometheus/procfs github.com/prometheus/procfs/internal/fs github.com/prometheus/procfs/internal/util @@ -403,7 +413,7 @@ github.com/sean-/seed github.com/stretchr/testify/assert github.com/stretchr/testify/assert/yaml github.com/stretchr/testify/require -# github.com/twmb/franz-go v1.21.2 +# github.com/twmb/franz-go v1.21.5 ## explicit; go 1.25.0 github.com/twmb/franz-go/pkg/kbin github.com/twmb/franz-go/pkg/kerr @@ -432,16 +442,16 @@ github.com/xlab/treeprint ## explicit; go 1.24.0 go.opentelemetry.io/auto/sdk go.opentelemetry.io/auto/sdk/internal/telemetry -# go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.68.0 +# go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.69.0 ## explicit; go 1.25.0 go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace/internal/semconv -# go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 +# go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0 ## explicit; go 1.25.0 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/request go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv -# go.opentelemetry.io/otel v1.43.0 +# go.opentelemetry.io/otel v1.44.0 ## explicit; go 1.25.0 go.opentelemetry.io/otel go.opentelemetry.io/otel/attribute @@ -454,14 +464,14 @@ go.opentelemetry.io/otel/internal/errorhandler go.opentelemetry.io/otel/internal/global go.opentelemetry.io/otel/propagation go.opentelemetry.io/otel/semconv/v1.37.0 -go.opentelemetry.io/otel/semconv/v1.40.0 -go.opentelemetry.io/otel/semconv/v1.40.0/httpconv -go.opentelemetry.io/otel/semconv/v1.40.0/otelconv -# go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 +go.opentelemetry.io/otel/semconv/v1.41.0 +go.opentelemetry.io/otel/semconv/v1.41.0/httpconv +go.opentelemetry.io/otel/semconv/v1.41.0/otelconv +# go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0 ## explicit; go 1.25.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform -# go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0 +# go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.44.0 ## explicit; go 1.25.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal @@ -471,7 +481,7 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/observ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/otlpconfig go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/retry go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/x -# go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 +# go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.44.0 ## explicit; go 1.25.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal @@ -481,12 +491,12 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal/observ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal/otlpconfig go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal/retry go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/internal/x -# go.opentelemetry.io/otel/metric v1.43.0 +# go.opentelemetry.io/otel/metric v1.44.0 ## explicit; go 1.25.0 go.opentelemetry.io/otel/metric go.opentelemetry.io/otel/metric/embedded go.opentelemetry.io/otel/metric/noop -# go.opentelemetry.io/otel/sdk v1.43.0 +# go.opentelemetry.io/otel/sdk v1.44.0 ## explicit; go 1.25.0 go.opentelemetry.io/otel/sdk go.opentelemetry.io/otel/sdk/instrumentation @@ -495,7 +505,7 @@ go.opentelemetry.io/otel/sdk/resource go.opentelemetry.io/otel/sdk/trace go.opentelemetry.io/otel/sdk/trace/internal/env go.opentelemetry.io/otel/sdk/trace/internal/observ -# go.opentelemetry.io/otel/trace v1.43.0 +# go.opentelemetry.io/otel/trace v1.44.0 ## explicit; go 1.25.0 go.opentelemetry.io/otel/trace go.opentelemetry.io/otel/trace/embedded @@ -554,9 +564,13 @@ golang.org/x/sys/windows/registry # golang.org/x/text v0.40.0 ## explicit; go 1.25.0 golang.org/x/text/cases +golang.org/x/text/currency golang.org/x/text/internal +golang.org/x/text/internal/format golang.org/x/text/internal/language golang.org/x/text/internal/language/compact +golang.org/x/text/internal/number +golang.org/x/text/internal/stringset golang.org/x/text/internal/tag golang.org/x/text/language golang.org/x/text/secure/bidirule From 26c75b6918571525a1ebf5191fe38b76b9971eea Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 3 Sep 2026 10:10:21 +0000 Subject: [PATCH 120/120] [bot] assets: generate Signed-off-by: github-actions[bot] --- ui/app/dist/assets/index-BUvG_Zbo.js.br | Bin 34011 -> 0 bytes ui/app/dist/assets/index-BUvG_Zbo.js.gz | Bin 38564 -> 0 bytes ui/app/dist/assets/index-aNRKuvMz.js.br | Bin 0 -> 34091 bytes ui/app/dist/assets/index-aNRKuvMz.js.gz | Bin 0 -> 38661 bytes ui/app/dist/index.html.br | Bin 184 -> 186 bytes ui/app/dist/index.html.gz | Bin 312 -> 311 bytes 6 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 ui/app/dist/assets/index-BUvG_Zbo.js.br delete mode 100644 ui/app/dist/assets/index-BUvG_Zbo.js.gz create mode 100644 ui/app/dist/assets/index-aNRKuvMz.js.br create mode 100644 ui/app/dist/assets/index-aNRKuvMz.js.gz diff --git a/ui/app/dist/assets/index-BUvG_Zbo.js.br b/ui/app/dist/assets/index-BUvG_Zbo.js.br deleted file mode 100644 index b86353762a5c7ea19aa7722260191d9f82b75f7a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34011 zcmV(nK=Qv^y5TW6uEWLwfLZ=eX+|NialioCe`o;pSlk#Qc)-fm3mJlv4z*yG!;8hM z1sR*po;IwJAlt>Q&VtHR=F6h#OHxAjL#{FH7uf#s`+t1u;m6{~4;E#^G3FT)LpTH6 z$*~CW&)%Q^{r{hzugM7MBpR)IZ1P%5=jKWf-r*H61hQvjm)m7i(5^PsWnQ@({`<1| zwc3gv+%L;Fp@&rmj&cp-5P;IQ`U}C45@VXL|IKFJb2-6eo<@55fWB>D##7a^ZKMmd zdzvpm1|_!aCyOkN-TS3r)LP`s=og+@eqGW7yN3k-`Ki6_XR~q?>h?P-hi<1LOLSaK z3>*T@>HmaJj7-s_oJF3GY?e`KbN`zstpTfdwj)OzBswHxWd#RIT%_jUGJRbTgzoT#6O%zc|0c?oNfOpKT@I*RpQzn}K?y%Jj8D^9$S zv?V3-qLAm4gg(6(GwQg54N`)`~7-?KiGjb;%~0AoBkcbw@MV z#52)kKM2yXX`@mFXw_ZU0S0sV-!@GDoAkwY)AO$n5JFr+2<#?nV`#O_aM}Cs5n*g7 zTiU+Z?0d`*prsx5m&EaP$g-q3T~B#mk*&4C2s825q7dg}?RApQUej54VAdOs=ewpv zt$i%`sSvRPv-x$DrX!7eXjZH#UX4c&{!d|}e;NFy?+rD%o}0<()^5b3 zRh={n^Zy-M61oJ_tF!dQJT%@kRTlHW>JVTYoy(c&y)B;7Ge3`yQIOeU7g%hD?-)LP z-t@0xk-bfI&izHIqLCkUi|tOqf4@z3#c*b2*2ts+i~X?r&)=Q02bV7;^=e(c+%T@c z+dXzV!B>aCANdLS6`RAH4^v`a;*V6tyz~$9w3WPDOIwE8ZTcyS=$q=E^oRbDr>175 zPWM>zZC9r%g5@rD?jc74bT5Ylv)DB4ZD}WLyXkjokjG`$5MP|vx_c5{MZ_$uwtgfC z@GuHb0R9GfCK2|Y3h!lJRyJl$YZ}qx!i6>u@uULvO5$?)fqA4uh-er^+!JzTzdg}0 zl=N1i#S?OA|3632F#^yy0C_?#?Dv<#MaM{o(ZP{7Wc;q!ac|)%Mg7AAs z$53_`(Bnr2hGj=cbRd1+FV$IQFW#U>HydI6^D+j7!vCc z7!VD94rrx;BE_p7hTQ`L!hs~Z;sn(kQdsXYc`e{DnkW(OxA1CL$vSOL`TdLM>BMIlcdeC;c#Sly+eR!fagbp<3 z!k?ET$kLPW7#cc3I&#A};Tua6^NHY691bpk<~6VReSh}86agjS9BHbS5YvWylxt!l zxdWDtWt&g2W`>q$KpFTbK~^p!+Gec&=ZH@N4?mQyqxYOhp9fD zwr*+DUiPbM@W05__Hx*+@IgJO$Ky2AX+_c&nr>c&MGH$C&gmxk*MCsTH2XIcKs4#V zIH&mjdelQE!%}u&mr1T>k}YHa48y4usLp2ZzjuuBc)r$1l$F1Rp`d0T~|>kJX}9nCw; zvD31Zlei#6u*PuUvzA1bgYJ?FSW>~uOV?A)l}m&6&!CYdEQ}jGR% z%5yfM+u+jz7leiT?%jP+-<1)K@R+#a<7dj!KahPee=Fnem$E_4VX6nU(y`RD?+URc z4c%LE$|~8mVuD$rmWOO9uHzlt_|MYo&s-wW%{6!}jh57z2DiuG_K4^gz3$gKR) zo7Vtc$p)RI9vSDXXX%=cla~+w0qE27;72n0lo5LW`cDR||I7{hqD9w%cQFbxK-g_& zz1wt>CY($VE)xei&EMe2?Mjk-nEdWRgl7?32?-i3io2#)da~+1h%SxZIO>5YAF!23 zr*Z=gIm&$`ddg^Z+!hV=FNYh6x}UHT{WXn7j{w1{^mA!xY3gUskD}`6Bwd zKw-lIwhS689jGfTj<*@ZTT9K7*!-uuSFr)VvCMpI1@~|<_1J(&rZ7h#1)d6iLtDkf z;M^5?Uo6b#_SZA}v~KKNHjpVU(%PFr?boNKuL@O)PhVV^ZG*G-?N;ejZ5A7hI9Gl- zx6SZx8xfKkSms3I63%`?YS**efS*!i`90uA0+~8*SYIE7W=F6HR3ro6umT(SbDD(v(>vUbIp~kP#JcQmdAlkwUw^e;uI)KnFZZ{2V6lCh7uP=5sH?CKo;KTu zPk|Ha!%a!W%TW-%15%TJWH9sdM$duY!E^Ziz!hkW&N%~|E6EQO4|(oWBEsSs(mv|j-W(#Z+Npq~5l9{YdGfiqv3eh?o;)h? zUK>*NZVpdA8ceGPmG#uj}jPp(oNLiX3S69YT#+I%oEnPZ~z&J zCnWw;##RI4KQx#^??M_u4QSCOq{mKcqV7)BlJ0N(?yfcPLfbr>GjHzpa*6i%nhCie zgSC=Rmo&$EAf>r5t3{8s;nE(>)|~+J%8mDuh+RY;tc>a8)kUOeKZ*3V^V~u2EnV!O zuMCx%hVlS)1CpF?nVOc6mES(yHpbapEO?f+xYKD8Gdp4U5M^5rLM&5(O6 z=9o2;a60*0=vKSEsoFY;p`Atlnx?Z9vp{>Xv!DI-@9wAV;rE*xIrN~^@Jx;TYpS9t zD`ZVAAW62?r!%@6WV$8BEtby25~{D`2nuSJ zF}(i!<55PYkH{HI1hV5AvSmqz%p((o6k*W6j?3E zb1?2-IuuWj$lQo`Gw|SIs=qgd3|^vdgarp$hJyis#Q~P#-9Wg-!7YXO0r882UkWn= zW0q;mPy>78wx@Nyd?Yfajf78^*pw@#lUa=L-J zZt0FXIwUgdYT6UEAVZV+=}$PfdS++v}k(n<0wVP*r(TV%}(fIU5J4 zrp@7+TtVpL2V?wKqN%3l1zGp3O4?UsH1w{Dup8fdw|y|pT`f|H6))F!QR428o15Vm z-xc=cMRsfNvoBjV^S_X6i{C2RDZF-g82*+WoGwIA`Rl(RP3b>|2)WoZiJm*#CmBya zJ1vEvIFx?HCydycxAo(sTIEd&J4P1+_BUKfbC#GK{`)IHV~1A%{d5q?`$o z(b^Kb+EgtsYpIyzryh?6uE)_k2Aw^qGZ=Mic(gawD&uiYrmqJ&FOtQlw|B|Gig&C} z3XiBrj)$xXz!Ov@ZzCDS`OG8B=W3ISqf3coyry#JuDUm~% zKpGN5k>VcWeQoRk^ zvRv=b69;XiM^@6O$O&FLOF%qvP%r!|g?-=Dd8pjt$#pj@R@-1PkU5U}14R(1T{B+H zqaaVj3*p)B;UaFuq7Pn0)fr{J1y26$VR5d3q0)%jm5piUR0HvT(IJ>8td@4(YD6nJou-!^5c{=ExxR`KM(?7Qt3W(CoxGu#4xS zqs>ET=C1PGR+}0OC?2e8Uo(hlbMK=hoNyC4%vf=_Q<5_%WhJIjD0}Uf=W>(wX;Gk{ z_sT>IJ0OaG4T~U_suwO%5h4xa6Ht`LcJ|vVxni~B24!*n{S(Fy4bXAOS;%HQPgo0A z-40q&hV?QsYNBBAB^6O!p#rS0>jkG`b>*ngFaC>g=pN>jy_Rbp|7DG5>^%^D$A`(S$4$V7FZgGxpD|L z9-TgqUQB{+@?CYOpa?~;Y2OFtyM+}~k&|o|= z&yFSjBVu)`tan7b3U9?l*rVs;VYQ|2eBYc&Y=wk}oMNC*W{N&s#Ya+OaTU*0I$~-* zXE7NJ(>fO1GD@Ob%rLzEqX$0|?!;SodXNB4X=;OG`9T@W)7UgW`LwI%Bj>c3n`Xis zW#sx?9&7>XdC#-Ey>I@#AQ~vuc@YxP<6yweF7=>3dT+ih`(aicQ^|7$(&NQ)59f>2Y zxtFAYq8s<#E3~7HxO}}>uDA*~W}LsU2_}n3Qo$hd<_is`I)e}R-;SM;T!G~QXhI#d zUZL&bu#_~yD_6%mC453Ndzoigtt=mk>mYFz?U(-2@qr0XoSjl<9w=8ukl?HdFSfOS z&~{VJ9%a>EYa(yd&4y#<+tA_*ZcS)OXM3Qa4Ke;e*94Z%+;X?wRHFvHCT* zJZb9ur?&Scp_kqJQ|G&$x*0jFV-)6_#zQ>A#@+By7_$**w zw5Y6{QQxu@qjsTE%tq%@FXL$&nragJlcin&g1eJ#3Ac~)t?WG01ByB-Ya@c&ys(P* z&f+weLNkjZ!b|Ene>bn@w=o@nPZjto)XfEce02U8&=7gK$KId6)uTzQ_)L&wFF+`> z1&UCDB`(3+0hnr0|LOe#tC_6h{;hR4p= z=HRJ({WWnfZz>L?(TKj^A%?dpI+Jx2_o`F3A{2voAmC>Ine+T@ z8wf^nGIat|-LS`3Gx>}S4RqUF2-!^@f5LQF>v|bg%rB|b;&bZ>{}V6ym@?ENeCX$I zi5piB_$g{NM&{(H{7$XbN@>5!(368=%nCzc*F8Wjk{!~J3M6+J`6O>>A}-0nCv>KL zoQ)IC^V@KkgnYxZ=<^hYo!sflHt*TD%Kk?w68Io8T{V~y%L)QGwJV2?*nmVm7 zo9;{4pp~LJ>J4c~7+zLvAt8uE3nUZv zF)(18$c5BWOS(PuX4Pl|auzSlv=Q9wzvNXakRci0-*qf80i>zEJj=++FjM0^78=zx z^3lURN_`^%F>88Si!!-H zwMtD)BXC)V5yV#u)3wkpA}qBmvm|$yRLwnB>13rWk&F2;1u2JIEGPGg60$op%1oK1 z0pHrm2qSmkvh!q_*g<}0&Mvdd-HCKobzpmM9a?BzZbqiCxbdjE@xQY&a#d!bEHY@# zzZJPGq>>$s6eP+*5a=`vm$9Zj7BdQ?8^UN;r}_+JAg7scReOx~B6P>!2Vyh>a~-5m zS@~Zs@X?;iMHeV0^paZ4%dsi#c_fDBX=hXGly3+IYZzPH%#FPS8FoeJ*Tzi%Wj~gw z0nCc0ej@ii)O)<{zg)su!Yo16;btooByftO9UCHQOf%Ij_Vd+Ic= zkSqh_8K6pOveq70+13p{^QHSf%d>tBrq2gOmJYE#I-KHzf>lj(v)@;dZ~96cRgtie zPGE(oq$>Ir^HF5wi{wQ{1oP_1qJmGfK8g_o=v)nl!-uVx&r6VRwu zwP3v|m&C+!#xB1cSg1IqU|C-Rhyuy)m*cdm@?oT$dLX^st`p>)%$P&m5dk+HdtBvj z1ZWsEP$lO>PBw)%GAS4Uvs^T)~+(M8be0y^lM$KhM7PWS6 z536H)Q&M0B#E!~`k9l_3ZfXGt-sgtSp9mi3N3Dz|=_w~a<M~Z+3XbSW^CgNZu>$4IMN^NT=L{0RCnN2^2>DhNG4H2{<7?BP`ED$7vm- z?H3LZK;7aqd?c|pzHQ&%aFI#x#|GK5i(He|Juht8K;X`u;VZnKa8(Fl;u+87@P1CT zsY^O`w8*`{Zw&`35V@x&=x9L0yhvp1>m@QBnZKO7LV6p{rH(@QDiWR zwk$5qqJ%3`Ceetql8@>>X!i(_H4U$nL7#I(~ksLXD|B3NPsTv-6I{AH)bgZiugu| z$omZw%VoYIGBz{0X7Oa-(|*~F?|BJ-WhMl^s^jy${n-RBdm`MHU)itKI-y~nSA=9| z8gk(kh_472the+j%;ThvIpJc~3SA;5`g&hr+W4O^3A5HQFEyTSZLb5S1oO`g>@NYj zOp?em-hnSngE{A?jGW_s3%vAit>H6n#nt(wNg&Sk_d9V_NZ?$)Pmdj{kns5p3J*sJ zI``S+AhmfG@k2uA{zd{J8f5v7BNmvbZDBv?(U7yLD$N4czT??bAbQa4nGXFZ@7BwL zfz2F-Dsw7X!m|55VoxNkUNbIw{*>zTC{`yy=%RhQaCXjw6k{i;kxmZeoRIX5$( zfIg_JvU4U(1pPp@I_6Oft=`V+_QHnIN>IaNGf_1Z$Q?|#aBmg@EwOkZx~na`_kBrf zE$izZ<&+YmMu+MMV9Y!nm%0i*FY_hFAS3+rx2KYnMk%b1SBkpp!oT_R#G&-$=F|(VqObc9@N2 zH02M3gvaB)prA-43-QH`ngczL6FeN89bL;++`CmG#tF|$#@fQh$x=)LIUBKDQ==YeC`kvhUiQjGcGekxHXC%ki?5c+Z1pR_A>IZ&yHWdfgL?YSR zt~@f#){%pVpsm zSp21%S~nb59iO(JQQy(KuxQ6Q@x?ELhW^FV!MzeZ!PH!-k5?gS^b2yi8$~+Ppouwh zR*!$-Z@PiFB3bp?_;JadZJg5mM02X|P8Y;a-;Mby#%(#ZQl6t2x|Gzi(Nb2!d@4P< z=F!7TB)GlMTectMW(ffs=?voe=0a(-;h)azKrzz6x*t0*mJYRXrIN*Oyft(FM%Ph1 zusw`|Nnl6UDgwP%UQL- zdbF1x;+|bIYK+Q-E>U6xSt1LEu#7sf>++a(W!1uW*Tq?5PmpN4;7LPJG8oU+Y@qJg zRqV+cI4=uVcNkT;_d(GTJwa+kzJNA2awIV?Q2Cc7q%5;k}4sW91`X+y4y@e8prH!BW91cjBI zyEn3`eeT(sN|P_Qx{9Y~<6bGvW3pmNqMdJN19w!jR#|h!Gta1-B%uXKm%id;8L#07 zcdcy~TyxbJEG9u5(g^I)$40aygNja?$8mo1tdZKvGguVVFf6V8$Sv%ivi56I(Y;{X zHz(lmS!+pr2*sH>xs-+pTmsUBiVoa2tQ@dK_qT9hVKAuW@;<||`x5XmU~ywFSa zr=A6}oLxtyMv8C!4_O#>L}B+?z}9VbZYjN&9joL%-v!QCKB)Rn76Td&<>MI8&;+l196k ztLj!zUtNM+)jLl~^g%iQJB(EiV-w3!sqGvt4t1QLFS+IV&yC!+|DS3ZeZRDT4?YheG zm~79c+lp>tQ5U(G8k4zz4U0g7)x9Q*ter22*S*9R(1L{C3!l->PyPOz&q)7n1(<|Q zXE48g&tNZ~nV;>j3R$m1Z}X2+7;_ zn-E}#T6>NBALQ>daQXsRCv!b}!2lqB^=B4JB2%-r_6=8Wy6GN=i%_d81f-TeG{UQ? z8g;l|YaB4-rjba;>=-%Zo0E~^33aF65UM$g{RO9+kE9aIXUV?&f2RLuFe!R|%aX(M zOZ5Dq68};~Q~fNU=e!DB=ggE@*2(1o-$e>%$3b~)2rh(^s1J!91t&2oNdA|CeJkUV zYT;sh{z4cT89N7v#nB5Ue=0kYrmMO))wiuR9kECU#W$2|klR;SO?%@0;pT3XA0Dgw zUz7QAd)ZxEuA2P+LxuYE(L1IOo|xG5sX%m5E8&f$Fqc-3x?@+Q`3KX0JIPRVl05T*aPBr#7+!Xp>lQ8T}cWus*B8%bQJ<1MhL!9Kv$@7{hCP&2OK$@N-xwEK@s4p+ zm7lk~YtQ%tyv1j{sv}RHDe;=wm*rVMuNzyA>!4l%~4N7*D&i`StH+&){ zYeNL0wnc#`PGJ)oOpm646M@ZYj5g;{V%J)|I6{q-7WOpLO{-8is*jHek)c{!w+_^3 z__f;}WdH`B{t&D3Ccer8^wkrU$^;DLLs2ThF;IdlTAYIp;tHdnR01^6(?M+&=LCgr za>5ub+oggy-)=8AukiUCV#{8m>cn0C*I;Rc90xdgR?*4h$kYeH+`3ec(5hWhn7qW` ziI2enexk)6Akt0N^6|MPXQQ8E0&6@a9D-)bAT%YZ+`i4>0u~8KvxK(Cnk{`TpB`#3 z0Zj*|PbhYeyCReB9ss7eJ{V1zthtq+SV1JeRyX;s;_Z*jaoP2)$!;)M%oq+Sp=I=l zEcs~qswSqONpOPoNk6X>G>&J&q*=>PbCqa{Yt?G5xPiH00GOmYp-`JpZ~X`|Z=#xb zZ#_|TV6qE{T6}(HOTN=r=>F{mnNVc+xU0g!%-&|NeL3YAU6=aqnb%vhR``zpXxG}u zETTbqmYH3z*kva;dE~D@&dwB)Pk{wx=xO9o=#Z3#K+iKj$9GHOQzV^UV9CR+Q*7R#_vkn;`XgSTtQo zAIV?;O_Fn=XO|BJNJ)#CnURqWrHHI{Qu~^~NxQfItjXjrAKk%gBi$b`qc^phw;HET z?!6g5ALgrdFyY0^x84Ajj$7{L^5p{!($)MBtz(Ul5O#L%sV0p~y#OjFWCqtDC_;Wo zC1VaOOrYT?UhCWupssdS^>SX3lJ$ zy-&<6yTN|tX`+@~XPK&<;OJJvp*-XE;M2;3Sg(TlgAfRgg#k_nko4^2%iKo!f0ZU{ zsCGw2=UkH_6+^?w27dl6mr2%i;t7?qaqrhkN&NUjx=j9PSh7*(Th{L<0(8hbd8ZsEKha`M=Hd4f z(We;9M3Jr5f+-NpKUbDPj~T;|$qvn(HcC?%KaW#o!OFG6 z)#b;i9+L|unJmtX0y3Q^#gAoh8YHuFQ=+&6HTE(5#|*Y_s7}qgrYyGJB|IW@IfF;v zmc1xPpXdRG+9VP+L`5DW9xR~1=x>F;v1OVYJs$-d^L{LWa4#%Sq74g`XDdwwOk=0{ zSm!8!>9Cbl9;1S#QZgA@8HoFfs8&2 zX!J<{W8QazyKu&+jYh21rR?p!ZuIp-{V1Ob?8=D-26ByPnhbzHU||JykjVaz(pi2K z>DpJuBgrcQI?)Hq)Jm?L$a$xT;zrU|w=(s~6fr54&|Fc(La-Xnp@;N~+fmkF#1#bD zX!i^teeY=8H!dA$)=bgrjEl^lkolI5+<#%>r4+#|M6zgr9w$}UwW9z{Kr|OKNkdtU zY#zZ@v`zit@{wbek5dAL&T08R8X1{2pkwT=7y7{z!SZfTz} z(chg*pUyK?%rm9T<#D&Fgf-piW=RG9_gfCGiPdSweL}fV8C~^0cJ5s$I}!~35M!&c zXg%IvSza+WA^7eRi{CG0~bAt+_NA8z z0nC{k=A4CjMKE7fFe+!5UXCyWigXrk`=7taUi)`pg6^$1Oo3sJ=O=dbejojnC=5T7 zJ!Z^4WERYH{UUJd4v{)b2vT3a3TpTk7#wSoMFby{%m;wSUc{J5+sd(NunDDS<=t0n zLqj;+GPid3vB*Vw4Iz08>mbtaDxA7G3TF+^fJ<`*t`MW^5(Nw`(&%hz?3OyXYmFoA zJ#IQTs?3-H#q`xBgv%Gk=9^0;2Ns)mL%?d6xxOV1Vfv<5djb8W3mY(LkFvvmm`8F{ z;hLl`_oB~ANPzj)Ar)JD|&=J0pS+R8#lDyjLHxWU~7|bp>G!U)}3>UZP#15GqH3+k4pptKY%(pK6Uh98NSaV!=Z zDll~pCzXpMhEvZv!DAtidIT4|O7#eQNHx1LZrmSRNS%6uJXljz&P%-tNzN)a;CDAW z^zTp6O^jYQr*P1(@Dg*uUep!m&HgVjY4BagCHVGcX+FBS$)za@-p%WdbAaSt;i?{L zz7fQpPTfSLFQl6ay6MobGNX7m2VmW_2zh_@-An>%_^Few-V2%6!!~u(b0KmV14%55 z>|0|-n(~>LF`hAb^`SM_#9KhEj*asoo@ev7;G?!u_`@~J!(Yk+_7LDRjSlWH$<0kD z>YE1X*Yf_7qiq7@YFfo6b7|Xr00z$}clE32GcYVonRfeQSLU$c z>lL5DLf_od$`BLrc_vQn-y1R2Mh<$mhd{pUxH>2KU@DX@no2eRO_d<&(3dH>tY`Mz z1v3@6-zRq)2Os7&wJE7%E$usBz}hqp_Fsc*pSo>#>>9eJFT?K6`*JPo8$Glw{ip8e z#Dv%Ta%n8u^zC6}&GFIOe86Ay&pu((N*#j!N#oqvRNc2l7~@mDrJ#noi+Z z$T+U8wJ5OU4iP{{$7MU1+CpPP=f6XGd_IIWj{~rY{T9sY!~S>y?jHHJkb=$EMjIB& zG-PWtH9$V^2?O&YH^U3pa1nr3AWl$sUm!+dMzIM7Qa$XTAVH^u6uesSUPSNOW{6Dw zt=X(hfx3t;1ki-}Pcb&7u#g3}617YRKqN*47P`9re~-HP&E1CCQlL({1FuRfT%g1F zTSOW>yDt?dUVb0mF10Wl$F=wxq~1!@l)XhV^7%&EwdD{NWkdBQQy(R&&EJF4AtRY+ z$ZCFnOT66JZT=M$n_K%Xh8W?GABDPodokTieYYu54BDrEi7_Pmyh%eXtfc+@5-7YH&+{L{s5 z@|(&jCE_j{j??A?-jBxHb}&9H_+Y46#ef#i=QMa?pJUwtLLil;8lwJ8TV7jRx z+1GTyYlZKDfu|c68+Yudx8gDfs z#Xa&RTfd(;$CYHk6_?l?C|!Umm(8>&vobjU^U)o8lAoT=@M2{)JPHngCB-C*RYw|< zcdaO?sWiWwJh#q3nTQ);D+>Zifu~aziDI{N<(c~swkll9Qu1nEM;#NOxdgJB*fkG7 zZ0v)4QEQU5F6??V@)>tB{M93koLcgRbTd0#EIfEF4Rg3cZ&#=c?Jr+cHXq#b6R_Wr zeYd9>l2Cc$H|+`GMUQD-siToTLpsV%B_}+Aq-&d={Z`u|{dY;n+%5f4vzKF`o$&_` z$OiUFeNqGe;XWl@(R>1S*)8ATT2~p6rA7Q7!aa{4&wl(lrCgz62sb`XDNo;0lT1nU zKN*WFhJDW0GSAc|zVMiQtrLeMMF@tLA`|mp^|KG-S-hz$cb06N*etcBV{R{+p+5P{ zUW>VG8RF<`mDD0yK|LALOMA<MpsC&A!FtMg28ZVKju$;me2k}CtQQNb>6cK(CugvB7v zsVA{dB@+^l>?VG&^|nXdYP$P{QqXt^2 z17oj;A`+ik4Vg#zg;4SE{#2x_zW&cf-f5{IVuhH;?>a~{G{MKd(pL)oU?JnKJI-L4 z*Uz~?0_r!Ff$duk1$R)<% z-M=jmt!jVzMACKj6GRe|(%)PvYm+W6ULzwfv&M*3bXtQ5{yQ%r>+kE++E;Q6E2_(T ztMM@Mm)fm9uFL6b@IAF{5}Hr-<9Bbz;GpWy0&ZEe5I=S3GHVOkqCu=t7LYrXyGT%S z-E-JpxCsSAgZV_iQA(jPu!?;fb-h~QTX*3Yqmh_enH<2 zOPl(%k5f5J3~A)bTV7&)vbK$jkA&UO=me2|48g@eFV*=T^kq{SSE->-U2nDrW>I3C zmC%K<+`hk1O}7SAAR7>kj+RYbw%ABa6Hbs8`k5ARj|qo7hZl^A0g0Xen#9I7cZ z4L{5x5`b$!&Q!*mL>@g&3Lm)d8`!p;-oD+|=)|?)dLI0ObUk z&N#Ke)Q@43E(iCQngX2BILIhBBv%4_%3=KeFP{b2qxQreUL!79d9sIFNdKuTJ%d-_ z%N@L}GK9w^1RMi6{k*na^tuV26uVQ2Jh>9yD~Um2RIYJE64U{{p2h0GtvKeDQAU#` z{rN+mly{0k?sdSEk!v+mO`Ke6qTleZyQ0{Pn3Rws`{wR@uf~_rDLo$lf~5t^1skEN zrmr;ZVovubgl#c1^M_g?=9~e99*V%a%U$*+KX(GX+o0_i9#C^zpHlbfw!&(zQ?Y5b zX}8B@GyW9!EFbc8zpd3r)pVD(oc#)gr{{sH(6PHt1_r?`yUFQ!*EOJNA(O>YyGaCf zM!DE_Q{QF|kfKGlKRlT8Cq%g_i-N54zX_fTx1cUG$*5?7DA@} z{I5A#%M<6Wy}GyW`FZEo-3@j!C|#)qaL=&_GI~lp=!Ed`_Wk<$yz3g_wuyHemaanH;LuPM6F&1SRyGU$x8>mHscxT8XO+;YT zZ6p5an>EO=>mAX z@X+DX#t?i4lb&%Q(9OQG0*UXeu{|yv;>V${A&i3bonl#gtico;9X4j54e4$AV-aKH zEoq@*r#cYb@IjEahwE>tml;zNvp6Z3f5WoPZC?@v!UD>etMG$Q%wDVLqVRQkUDWxh z%n>_ve%S8yLByQx4b_j5vrd@p?R%EjjBfQz%b|J-N(tDVqX#%N1^yd?$Q&dqfGpwU zF0T`Kij0FGn|}V&g@ZS(+ZpoY6SngxzD7ne-5SyyF*-aopqguJyO&$~Xtw0g<-KN+% zrkSexYUbJ8->VA87cc1hQt9Bl!;`q`UTLBNficokPjMH^s1s^Y^76%s1++W@-W?_{ zR~V9w17%1r@wB0@W&as3g&nJ48y8n;c5*h^KLo;+-t_xALvbcceM$%{ebKscaA0m|bOOW|ZF7uJRNwx1CVCiHEs#hE&GjV#d)gVNoI5OU zu$oN@?+P<%>0ig=Y2r%yp3i#}Ko*zI6-K%r|H1|*pe2`b`X)4)5nSq2j4^bO5t#V| z4c=Axdcd>OYRGw7N2Gl{*3;E4G3Y8ZVJ7;%#B+mU#Aro(uFZJyWskUbbjTdO@Bkj? zs6!h(XOV;-51l9!8$bUIg2NGPxf!udZrJs`N7RMe^d@1zJX0Tlw4G|)(T>)(H)%y% zG^F%|cRlN=2aQPwVI=M&rgcy^n#p9+q%yg)z(MR}HZq0Ge!y7oHdZK`193k!Q*E^4 z*3YQ|achacFZ9#UNk8c){iK~7w9`1+$w50+(N1hNQxPk0^~X?Z-YjQ7-hkU5^E`Eb z)M7Q|nBtG_L|{>-qHZ)~0jpQ3DpSU)v>TbA$W+9tfV+nPu7{z5r%AV*5ofZ*F5Fgr za?nnCW?gS1S2Xpje$}sCJcQXhp(SJ7d_QCMs^u7jLr@)?U>N}t>!5=UA%K(3#Nyuw z7;1w!m&doebCOvMxdV}gjR&NBL+s+m=}e3cIN)Ngf@}P{Y!E#8RW>VTJSrPE@Ww2= z`(Y$j7p&e(*dlo?rb2OPjU0LI6(XzF5S~1W@jy6vs3PVsEv!CO^^kUh1kaFk5*A0M zk|w0gc4}x6ErSeINEs1U_js2Y54DZ8jf)a$BO=q1^vHp<66vIa?!@q)Agcq3ab*+Q z-e0@Qvx$URUCQ$q{Aw+Y6$CIx#^F@2^-QnyUETucN(CKu-4vws$48+iXf6)Eb4fiV z@nMxH#)8n~f!67(r-nT3ohB;BWTYl~ED0Z}@w)c4dTTPM zg!Qb|aM{_n^zud#^AU|WXm8uK5&l`Tt+ZcGS6|=+Rz>_TJ7WUV*+50iWK)=H2=l>& z%x8?u*d4BP^tIdJ7sZ#7eEb&$M1*)(&=h>IdO)L>g_>cogsF`~OA(I2>9nQ7avVC{ zUkLu+mC}|r%5fB5+zbQd+^>0Zvp!9s!kMQT)KMe3#=OGUQXr_X(c9vv35`iSo`caD z;|djwU@lhN9}3u1J$rR*2<;$+N))zczp`UGx|MmB(z2)UwgmR{&ecWjFE1!;vQC`CF&?G zF_$ztBT0n6|-tX%~PLq~Z(U`;xowUEqKw z#<(?oB%vj`&jf|Sd!wH=SBb8zl6eZlV}jzdb93i$rnP_RpR4kF`UQ3gtt%T~RDA3_#J(1~t%r0$E7&I|Y|k!`yp1PH5^F|Cm5 zDSpD!(rfgxQ*!KCyM&MiMvL^+$yaMdF~W7m1As25&=EZzHxWU~^=YlScBYBZ-V(q+ zqY0J@XCFJAVsedMvyiuX%#4sY6wxz|AwF5drOCZORemjxEA%c2n^)Ro{V@T4W|{zW z3US*(Zw~9e6mB`6E|x}-Zsi2G%aNGS085AWfe-uc^Gg?ULNiIFqc0-Iu_A&wMhyVK zOKay?Ip$V>6@|UKSR^GTFefa*SlUn=*I=ReoTd2GheuwlUqnov&D)&iR~`$|X&4d3 zy_u{#AOfgJ3ESF+GD;JcWqD8#H-S4yB$|O$(~W$%t{+nUs?1?g5PRfd0+2eOZyh+H zZ9cjN*q`%;Y@E8i60ei>o>(;1)O8*fY>J$@g`p^0yd1Tm?4@uBPsX={8Idj6p7fjz zuM2_hvYSD0qsSa)L{Mwz5Eay#QUXEfWc0P-C6$Cn0PaUBlgu!Dw(K>~Ja48}TzlO| zvngahT78mrD)V&aF@PV?hQhQ|uH|=CGS?~&8niNLYK*ZT<(*_Tl>02;=a0t zh&ab=WfL^W#4teB-=)!S#nK@neG5yGBRh5nn-dXxTc|3Do?Hj}9@sseBiqh?PJ-+8 znYte=bJYhJ#KxrCo;9e1MHdFBY3M{pkE8MIXKYI&dK_r?xWfwh+vFOM%>^5&=y9FS zO5Yi;RLJQkR7Ez@eWQI;-K5&kIom7`!EEl*Z%`{o`QJ%XrlC-tDYh-GvK1m+;53Ze!f^f978&0Vg+&r~l*VPJB#v z?)$s`i7QyKh>alzbF3!O*X)^IDBXLgT6hqad2XYdgf$2TmCeMvzD3ov@IVrw zE=rJL)`5Ul)cvLy+z(oo+D^P%es7xOorgF74TD^5^{r9U8XOZK;s)Xet4k;>U%bvM z!aEQ@X=`2!1%q0fV+eT|@tvbqP%!^#hD4EmkFK17TN#m8`HvJ-GjX^qNY604lJ!)^ zy2zbzR9F@$#roQP*LpI>Rv-o%UP*RG-sliEDqA@3_52&7Z5S%~Ldb&*faBIU123s- z;toJ0X^LPl{OzJMiAajwn!|ByLtcD$w;t@YpEP&kyumT>D!6(x#83o5&8EQ&dAVWz zX}IhVe3S(S+0dUObI{-SOs+o;TW!2m{nKA^T>Z4bZmHhSVuYdo2dHg*d?#W^b|NeRGpQrsfn(BT$6c72UA1u!0fQ|H) z9T6M`!BXcN;$q>7#);N(jP$oanZlePgy1gZ5YvHDQnH{h*@v;XIaIy_@MA7w zmf5!Ix;^4r#DO>W)+O?JWd-M@XR6cBtZ@#Ik@}P=`2$EHs?6Z8T$WtK0S#R9ts}0; z$&iC0UWZzO_e?0&$OCMAad9Lo+&eSXz!)7xSp4JDF);(~9uH13o*JO_c)AU)SA7Cp zDUcdN8*+N{xNEdj#dGYAs!b$zeLaKn4j{jn1#F9n?%&D(*l)@p&Nd=^l-ompFmpE!`dQRxFlHS4iStp^gO~Ep|)Lm*@ zi#pP~{<}+YS6k%>{UH=lB~88SbdGEuXMyowCoa-b8Uc%EYW*UBTfI~GeSsl@C0qZ3 zy-lq3k}OGy+NB=msI5DD&ll?Y>9UEjzb9oZdKj+!8~_Z0Kr-ifd1gJMzEi~{(>~m* z%noWk+{Q`~M+oboy>KAsv2>O@v;;F5#oImNeE)t&qP!FRPh=hpKL*a^^oBmBj_69g zb85`V;>x0e(`h`5YoK1&$&<{gnE7ecQFkcmI!#*oe5V@p+SuvLXfA%rElB@(l!&LfCq+FUQWrDNEcdKfmP>8L)yRWo}=-kwF@+ z?P5Y>A$4R5?16fE-+XFVLOe@apWJ$?%Ws}TdGTB9d-@jV-NlMFpL5mV1NjbK z>H3!%Ns|x5s67?Nlp(`}3S^j)d zi*0+;)q14dd)7-^yo(Pgy&M^lowd3eu7Zj`85m(olx?X!l?HD2I@vknpcPYLFLLwq zi_}v=npWSfS_W$OQn{Ew>Z-+XE-l$;uG+FAl&a)V@dcyMT8iNlyDOYXv!>`L!?pYB zW2PjM-IJY(q<@QryK%^w#;+H`G%CK!NTJb=g$q#meMfa~teSH?Ogu}{KjD0&du0mu z<2RcafpqJL?SDynTcn9I7c1Uu2P8mSYbI}|T_?Se0a*)AH$2jyLFMc!tmfrs|n|`P<5jeT~ z5@%>A;SSC_a43t}G4*P``($Vw6kgo9fxne6ET#f$Sc+-9mZDU}?U|97Kar?cn1=iP z98&7#l;GbUF^;=?-FpWdn+4hG>k=TM1%S`(iC7L}>j+?B<8L~WZ*!YiI&JS?%4LH^ zXTOfkwyarBUPvozn4W5n=>t+7mHy{u?IvG{wj2|3G`&x5Dp~s=758}5GLE$T<{{O^ z;3=?9ez7UQVl6HLdmqYYi5%HPTPh{z8|-6yyNye6py>5?zLNho&czY08&CbzORLOV z`X7UnXt;*aL}7-@YA2P|^d8ZQ>j%I+CMZNAC7wh$OEC#p)+1LPutQ zEj^p~cJ>xmh@epClc>&?CSlk%G*HrX3#A2){=+l%7ssu|@pjqG8-S%hr=|IoI>#!o zQ9`uBG+lZZc9@x}pBKhvim`4giANe@0~!_Hj$RzjEwP7Fc7Tr&b+}wg=fQ$?&TYq(@iM(cEYg4$urtxZI_i(0f`E&!H zqO^4C-2z^i+A)t`MPVbS^A&I`p8ON)1EOcpu}tg|F3|wzGA`q}U*lVKZbaDu6j%l? z0;BM!rYn5<))GM;mX3lcicEEh1Tbx>8i8Yv^Qm-(I0~EM24k)aXm?jSNI#VMw(q3<>iuObxtd<% zXwM)<5jA6!KKzPP*C<`U3lof%cMuM#quy+ z`Y;z#l~?mxsILr--Lg|1eCmg(?~whEOT#!acO%CO`oDq@$fm)wtK5h;rr>GYdsi$E zI%ZtEP9-OzmRTwf@1D@CdwKp*Y%XfKmvX{~qoDAOYiPkMjirCoLN&QQnotXgMh#e+ zLMi3|Y&S2x?$W@ucW$}wEzMivtHt0#uE3FKjH^gJ^6jRxTvtJ(O4PN;z7ln>;HQTf zz4*cg_`$2`5WQ&^U;DKz?Y|8!=y?X*^ z&wFYoyTu~nGCCHkyomU?x?xXMWsWd;0i?UHNHa@q)9lh)BFl?s&P;*wkjBY!z7GK( z*O;TsX-8LOVi}IwPx_?!(MxsUh~R(^)m!uFP?*n#n)!U_F<%Tl<;$Vxe#k|8QKo8a zX+$nBB%#SyaUgHmCJJkki37)c-&0*Ox+g5jKp6pVbUdQ7=iYdCBUmKMk^MWp!*Sx{ z7|s~k-B?wG*r}(bb}cUVs?nF+Iwr(bU3UyNJ1yH6AKB*3AfhVB?g|~`hh{^qcW>ijny zPK7}sZ-&BM#lUjDlio-~GV3n(UhvH7&~GC5HlpB#R!M#D#msN0q!9tWvhUui~mDfeAPNeP)2|Rf;6LN0a@%}hNG4TprTh3@2|YZ-0Z=$hs9QG1T#cL z0D}nC$&EcWN)Z=eRnI$+=m27Ko%v$2&VtOcquS}KY(M91VZuly^ni;ce+Il+ewOXU z4U&lj!WnD2X^65|nB%LG>G3(fnt#~}b&mKBh49Ib^rx(CD9|V~{Ttcc>t@{~U?N+m zxsVxtG|2OeueiP9ncz&*ki2UXS?Hfxi?OLPc!SY)p@%VmUfBTa6=6i)1^_JPNRuP= z&z%p3rv6L#gn8rx@4GQEZ{e6PAYhBV)m9$8O}(Y;^Yr~P+2c;hLfxyi{c@$~gAE-I z@7^;oTQ>1!=RrYiE4(bdTVh`>CLDa_SeM)FRE3+Xr_otSw9NN^3vMY40wrJjJSqe zGqQH~(4WPoJX{dpy=}{TJOBk0dBgRxAwWRRKf4#eF0r85|E{D2c%{EXfUwHr>_<*ga~bq2mydv zTpqA(k?`x_OJ3`!eSH!FV1bX9e@)Bf=);3!9DerpR)>2s@=ie_} zuZ*it%67TO2W`N*!rHJ@Fs9*E3qSO4{80xQNJu;&-zIsOBMSDTUFMSUlu@n%r$c~< z7#W2QdHhFgaXjT3LJd~z*{MxOhY8WjV;i!}R08bTTk>PS{q9Ft4yYk^SXGa2_1_`Pl;xn%OWt(h9;AQ_yrQq`WHN^Q6`n4|ejbPDQ zN3M2{39t37E@VnJ8*{Ro<>S!I??xG^Et10-6%7-nYk$fqq+HuZoYHMwwE7?R{1%kM*1+uWLhG!v@eN1^6dK!_5C%Kb28wG9* zwb3uI3;v(xXe#eUc}yfAu1P7L#teYG%o2N3nM{+`qN-LZ6pinVYbj}|5V=9xjlo9F zZ|F&&{0%@g3uFR7M@s)RC=A{{nR@gfd145YWhJXgLltS6oJz}aeImc`OZC{8M(Ie? zL&pqyf=Qe^8%z~-2|6dyx@63&{_x3_K6E}y@|sl6_i~6I}323vTpKVsFXErLRTc}!Dl&SmqgnL zZDQV#Cw5CowTbBX=J zqKi_+_*3J~CAa)BF|=_DlqaBBBUrV)yko-i4Y--F5Uec|QNSWE-{;{~wRK-@m(bt$ zHUG5h9sDu7xuLR_D)SxK=Ibr%XXO20u!0<{YSo2N4{NJxQx8!ydmlFt z>3PM8UeYR%#dpKYI)*7~*LfHxC(XXYBm14_x7zF$_E|Dcp(#iS?F6@#q(W)k5P(ct zXOc4pZVgb4URqn+GG23bRdu?mGZJktHNFqBu{Yc|2g_2Z8_xk_n;dde4QsswP(OEM z;VO*}OsVywv^F!r(B+o@yyd?Sg8#gZ{}k5zBA4?l#Q%A0&o~4F$fpH_UA952nTPZE zzK^jREPCg;z@qKb_FM-ZPO#{6J1_cE!UM#3?NfvKqM2UiZ3(qPSoAi^g{UAzWEv-5 z64}s8p4Xul@1OE-Do1|f&|MX`+C2VfyPk)XOB0}PxSOSBiCFt1iOR z)xEvE=HVm!rB%WM!vaHUp~nNEx|pJiFGL#wmgqXWwLTX_pI;Dga^a-}yAT zfH5?ZlB6VP&N}01#J-Kxp}b+Eui3f-3Nbo510*!#8yT|{LfVcI2LZShZLclkNCbitk3d#8~ZxaF(q?* z$fsVGJ)hF{xKCtO#ViFxuA6Ak;Rr?njX0~Gm;TR%;dhR#Lrp!_!=TK@3;^Y z2b^rj-n`^1**9fm1oiZksl+9SXqj!m=x!2am=n0qlk*vF0?rMRD%;)V_8Je4VE0a6 zN+rxH>-_?LOy6Z0!q3D64^4Raz)-BOEMZh zF~BFb8)dRYhjSS&RW$!X9nGKPYwAA{gpghmnIJY;Tpqj=BDQEQ@%z1Plb`MZs9SJrO1=w?|Tv_J*Z-tOpKRx^tJ@8f2sE7A<8om>*`SZ z8l!wD3)!mYC|iYHQu}VVI|jQm`ckniY?E#|YNvFfK)Ek36YHq}v>FkrP9PY+Jl>nM z{m9ZO8WtAJyMeV44!Z4Ph(g&-zcC0O!Fd4j->^%?NAGb-B8KC$@qg!GG?1_!*~f5& zi^pxpC@lZvZAzMJa!T4(H_ds!T!64ya zcIBxi-$6GQjn?syrXD+$VNeNOiDBJu&fRjWXBMlx5cPV;&|0hL^`}0=A({xDLOtNr zuZY*1k&v9P$8WRLXdyktJbezK)5Uu=Fx^OXn&Fi3c={s2T`_lR)U>StUIhJtHW2AZ zy)l{lb|+jUW^YODM>36!lqhypY2@&$ayWLPkit(&?q2C%uO&p?ek2Xi8nRrYY_4i) zBx~5wm0R>RC|Oz2FFxsS0e!hje!Y0C+gr0>yE$TaHS-1-%5H+?$$Qc{eXWCiW)er# zqyiviY7Q2wVn|7nEqYi0byAU|4%bz8s-y{CZ%~(tXOiAU&jg%@Ps0A0&E9S&|F7zt zf$<_r@3@cA7n`8G@}qGd@nuEuJ*7yYs8p648gs1#9k`%K)!!7JA>ehN$|X6hd5`~W z%;Vl>0>&7{sZErLAzo6-)igR}&&)_!F|&GpnAm~3$=WWMcD=`vsJ+~%*pl=^z5b9o zTWjA9k9T9L_s-2y&pcB4LTk%7-7Z_3tVgLPG;+tInmeB)51Q@4ct=7mFwwWvZeJdJ zV*Ta0{5kpB-x_58;P9`7q9oAM3uV`1|GGZ)N0j4oDAUHUh<+%;Evnl1%j9 z;T{7XP~vf4VPKvIVtRn?#D|z}!qWpXPY=#-9<=fjd~YJ46$q->peOQ5Lqw^Y2XG$B zEtMsfDi7c81yAkC7={x!oyFB8kIOdKg+R~kgzrtA$>cg=i}dT4KY`xtUpB~5-~Xq<@O%=Gdq58crO{HQeA zwb-@o8s;>ypf(-vq9P7U$P+iHi98TgGJ1yw5$5fpd$k$XN%>(jP}nNRMzLu_`$J*I zWTF=J@p+JnxQ6sjv7s0!5aXm5?1oO%k~HOrhj^}f-&Iv86|Jw0BQSAM-5waeOS7dh6pKk5mL z7wdZEf5gk?%P<@$uG-IQ7pCvE(+-ttbR?H58GBuGLaV@gj!$=AgW80gT z?kzbM&Ej{s?(&I_Q`#YANSV zzl;T~gK-)R&>*yngFa50LxI$A5!2r*v_PZ`z7MIm9OS) z8Hv|xm|9GUT+dud*esi42a7&Bub0h8arNT&wNsC#w1=DQ+0@@Gnnh)Yq$}!&b~d&v zZkO0Do=bHb`U@*GpY=x~nU`^BOl-Pd%kJ-!_fn0!AgvQz1Z=U!iyui%EaU3SE>2Sj zSgu3mS7Ro18rFM-SQ7`g_HHj;hL1;JJ!j7ZwBL}~ev&x;M>|8twPnWc^ASb;*K_JZ z$|{nDuS~O569dwuW1jM+v)#WV?CGzJox*aCZby%4C02fTHI_abka=Hlug^}}w z9t(x{YH&oK(oshpbtL9+-q)NXB{IO!gZ4V5aPl>OSIsXB!EW?{xWjt4+54)`JB7=^ z#~taa^;qCNppArI1tZe#$tgULm-AV^r1=`-3-8RARFu%KIC`~3@cBrtLV39l4vnAm zDs1#gR`qP|JsqUFAO3K7+?CY2Hdu5KO!ykkOV$+WPJQ9Zh&K)Ug()$|73+u_EIaP;i&0A@&qhLP(O14t z7VAI}XM!?b%G-1#vl`Xhvz1ruUf4-cLCbo3@G)u24!!FaC^)l~ILD`iKJLZ56=s6cH!9J8sQ4u@SE{)|-o_&f~hjRB$hg zP6sieJH+IF>!Ho^n(zI035k&{7Y04qMk(HgXJ4?XwmYAKR#_T1IQry0T+>@9(`zWw zA8<;KV3%Hjrf<<*7xJ2s)m;zQ{@r~(`K-7R0frZ_^xX-$avhkn1&PpUh5a6^1+Ps) zlzIyp^Ov3&(LRGw`4_2PK_0@)oG%u zJ8Xt|nxQHhH9lQ2d!Pyx!!)y4f6O6}6zM?@?AWLYDB$#SmAp+^SEgUVtKgOIX{>Xz zwNOO^)pdTxXlCmeiJGlZ$0sL@AQ93mnf_xNu-6H4fTT$jBui!lZqJYdD}UNMj;Gyt zZkd>%Jq%quwh1d)uUO;HXAQ&iM1}O)f%Fy12ru#5sty>ix$&U(orfgUxhGR$*y$vh zY#8!caS%K>F{!Wi=`dCM!|snH5tqC3#O@boE!K#|oU#4!6iY`9!@o_eHFvu3Fg!M# znk)xL#^b!V=HY-@>vhd<$HO&iAKm57nt9#Q%t8O*b+(cKZ(M+%5mn6dZL;>~k0f^b z8H_U_1k&r9)@iMWC0Wm5cZ5LXpBK_WH}zciU$7 zkghOoS68F=7VYh+yL=R0M9E74i_6avWN^XE3LCen@y=>zfB>Uz$gj2oU?3SCM z_`G>c%e8h(TZ*U{t)1$3QyV1zk9T%oOsSw_kM~qsd);!$f@3WerL2(m^6Ryma~=*+ z@PY@ryB;l_(C!yIoH$B~1 zIzr&>?@(h0^hZ!v8@twGf>s8^47hz(&F>h22Wif}yJ{7_uwMag0&nZ2`hbo3gQ;aF z55Sz?prURE)Y37fSkow5ax>1{e#RAgS6a3VYvVLgQ#H<~)R$wGX>_rB2q4$7p^q)& zV(-Q{p;G6%8BSB=o1?80Dejox0sl*WLfHt(b~7~TJ>+?6U)Y_V9J}%mkhcEjtm=@PV8Kx-Sy2U4cDs*^y{8Pg4RU2J zindRfi{Dk7eh(LhJ!vE=^88}1ST|+}l>bP>uBkF+P?|pkCq*!XJuR3v8V4d0^Sc0! zK&!HSq}sT@PI%id+O6*UmTVl#-N%r7FP5GI?&X69=bc_#-B%60z4m_fdEq0jBH!H6 zuga?l`KQ+$69zvn{o)CuAP7yZ52lK_kw~;*u}-n(Aj&=ASSvX{f>baFX^!XGM@>f7 zHtAJkSyc14TAlpXI{0jTOx%GsqkOMyG7ZnK(+8hmU&#_p?Nh#o#mdT7^}^i_lIqM6 z_Ci?AU>U8Iwm5GyUq=Uf=?rAobf5>4$qdr~193pH+!jHg4fVal&DWjBV#TvrBU>PxK}I z+x!s8FY4PpuH?!u-e94bCdP<8IjNEkgb9P8@)J%arb-?~7Lts25*ZZuQI#rJ*;_Ra zM-^cL*!Lj1k0MXxgAtc(p-)<76agD(cB<$0B(C2Iri(%~^i{+weankD*9D}6^m4z8 zOW4}JUwT&l58MigcH}Rw2g%=9TjP!ml%utTU|pG*-Ix&yT*xC|zwjUqUd-EbVDlm= z+IZqmjhCpvkG{h<)=|ieJP0_u^6{l+stDI&Q1#8tT@1JAAENJ@?9yU9p>G9dW|!Vl z0d5q1^3|YG{y;NTOj@(hVnNdqf&e?Gt_W;^JPSw`+sQBhxu*C>2Gqt5$aZ0-(ujK7 z*tkAb<%=)M9VxltBuBHnwDypv{h9j$=+*abR{>Q1|Qjn>AF&9agN<-A02JxJ-htthP#uJ}t#?K*3z zar9|XwTMLOD&J@P^g))GM9~0Z%UZ_LpLEcO!h?6NkBiNHVOWsH7NPtMLFq5OV_T9MxMASP1?RP(yjIoxkt?d6y4QE__bw zBLi?%>Zrow@lMiZ>cZ+t65{${6q_>A30Ho70m?EN*Hf*1HI132mu8M!LL*|;B)S}T zft)u2?2Sdfh9rGZ*uGXfyH=EsV>hBQLwxY_p~yrbIZm8LUbMP)C}J~xEbT_e)ljE;=x zGaw!#ptp0Gp0z<|0u>c=0V1Z-%jpRGnp@yt%*W!;3ObUnBDr|O>*s>Wrx>f&n^eP+ z4)?fno?Umb%9yKhX>Tji*gAL<-;Unm2+LN5PL=FAV@r|q476&t=fnju4k1;!8kV|x z`)FhMsQK=Uk$RIT(iH4{hSRj{7ebgG%tc5QZ`sJ;5lg@wRI89?6ujv2%z_*Fj$4Ky zu<49w(iYX}2P?Wb%dY8>XXZSID#BjDBAB_)F(E&>gJH4)Tw~WUm9b@U=4w)p&})^f zxtFAP?aohQl9iAa$9H^XXG&$<9wswBq6eQazDi=-XaDC}-Me|fLj1>Q`wg>1V=!-t zs!ol#ydUOcmrR0oJL$E^dzM;P0WTIKV`=fXld&o$uavv#Yfu1d3fp{U5xhXcRJo+ zOEkp1N66DtIa*@9;s|~%>|CgP1Gmm1O9Z=S4)$L8zpDx@fAm?wFesRL8A2oVqU2<@1^8Y_F$xT58Wv?|3ghS&g z{y2N{6(!I!Y1MYr_qE%{w<2P-KC^e{6j836ea(Htc6b`rt&tH~+u$G76o`c^qtm(& z-J#Se&N4i3L+RKuz5ESqpUOcSvrAQ>lz(&yfFbY%v1mE5Q^`7sP*D+RS$*3Oed3T&;YCn6`Gpeegy~_C!d3mayeRthJ0& z!}H*oPzkf9h|bfAtUEjF&2YYEj|T9B&N=LCJ6!RCJ zPLx#%IQxqcc(M_aGK}9mF|(NB_>6f6kCif_E2^`$Y0af7L59l)+j+|V0;Wtc`|zKp z5Qd_h`KA*8*aV*B3yg<~NSyEQ^x)t$4!iox+ZI(IH2xoIc+9f2@pQv)Sq0eJ3MOas z(`g`rw>2&^rkkDvfTj}3d)A7Rm_bb@By!z7q`r@WmbHR5pi`wBPAoox+6dF3E5FOv z_hs{3!1z~c^l%#=lV=fa-r<=U`}thk;pA8LR7U=ifj?v8wTpOBS0B!=1*f)smU=2C8pBz5-2@M^LHM(|4m(adv7_O(;YIV$RzWn-L!h%*bO~Vv9D|zRccD ztD?7Za_epB(prbG0Ir7q$}ua0I^_Wyw(gW!6nN$Gh?44%)Op^HEPJrHEwGvixcidu zv)smD&Re%KmT_3YvdwyY1&0?$d+@S=qn0?ikCs0&fj8>;J#}&x>F1Mq1+PUYg}d{w zLBOXaRR{HvGCo~VsOip!>AkzeGkJ5)DAx=vt9W|Yxx|M>87K3O7>HAv+8Hhmg<>)6;T-s8C6Px z0vF%^C(eqi$a5uCLQL&EyePIk#CDI@Mw#${vBnxzTnX^NFmRguLAPT~+34D3=4`9c zV)YfPu31qs3e_WQoDzp1z*u!rqm)6A3YLYF;_oS^)H?Bxxhr^hKkc|y>Z2@xCEPLk zS>ksA1Vn`Avxt-W@EHrak~v0Dvn&ogNe;#)BRv6Del#3|)Z{yxH5{djYWFY$N-?y! zoLzDDnY0^wG$P|fxYPrVJT%rM(_?-&G3GGWg3DUiw#OI<=uaG2T%9(TpMNMPioGv| zuva{cHue?^^KdH(-7FMH{;8Gd8=j*L$)V#6jyW31RKuSYUVWgpjmaCve$-#$`|!!I{)m_z5Uh! zV>SW8UL6lvIj%JJIU%EcML?D$`1~2LwmXTKnKU&X}RP&LrqQB3J-&vaoT|1{5vGaAxGJpwDS)XX?)eB54GQ$KCSVd zn5SD4LVDZr##mUmyj8fTlpayHEZRQ-OI0{}3WHa@i*Hf3 zg_L@vGvdTv{UyR{zh#^{&bw2k6BdS8dM@1A5us-rj%}mipzMRS!g)5AczC|GWF-Ne z3l~<4onj~P|8WC6H=uYnsQ^}dFt_#f2A-iW zMpD}!Pe^cVVSJ!FN;={$%wJ)f`oH8;?87%jfH7(`hO0=~rF3rArl;oqw6*n5A9N`Ar?A-@d+`od z_Oe$|2EJmA#U2^%@QFU5ei_EL4lMG43-xZ3b$OMZ1yD^NMddwoR4_=QRu4<~^h}z3 zO!-F=6&K-KzhNUHYMOmSV7S(ugOJdF`a~=g(ZgSo)2OYVRPs4A35kvkFGz`xaH))e zIhAze_Uq9!jJ3{5ICo3lz!OjqN~Qjjox@J?p8$~6x544av-t$nQjpKc31Sd(hRdjR zKj6cP*Xrk$17tNRItb$|wRVw?597oNlt4&%bgD82AtH9pBgj;6AeV+7bHTAx((G}0 z0K+2;lQ%fKCfqs`iat?ye-ylEd-51(YoC1dT#3odiyUK5m`9{0>3 z$h!V+!s`P;+Z?KnS8^IIm134A)~*FX+tSRB!QWuD!nqG$G%< z&N#MICz;tBuRy59q@j7OeONDgpdUe&QcWw*t>7`bvZfrE&%Kq2QbzWq$QYI{LXpWm zelk zorWDv8$cA-tBXd9sL=?kUnQO?LF!0Om1Q-AhvcG!*U793z=bl<}78uvsh`twWu_Ys=K;41rhV)Sl@zvTGX34awL9`HB5L|^d$Po>vWYj> zNsIvz`Z>9X0E0d6OW;kV-b~3Nm<#tq%DX;Vd-6;Px_*QRNd^cV)j?;Rt04Q&05m>p z(K?76>zc5*A~uxwiYo_V*Jq>?T!JzY_WROvN=Nd zlmb!QIajZBXylcMK3vTf)R~Or+$D+gj7rCBx>I6bw8Y`$*3`Byuj-NKR^OyWoSh;m zTKj-*Zo((M|865s)>W6j!-&(n9K&gZ5!6r1vyMR%mLW$V|IATYpikFOa z#67-YX{P0ZXsrYtQ9u~lIZ#8~nDJ)`^-2(qC2eh{JREpHa|&WODumn~53$x!6NtXs zZBxz}N=F3x!|u!$y|1fZ3xnm8y%9*t#{`1eK(5$@pB1K+59f5a|B(#$zx~)Muyrm% zc0YzJwfn>}|Mblr6)bC{qnnh}cu9R8xFA2_oB53e0f-Ki3Ewv(X;vf%AKpqR*Q&&{ zd8i)$BURjEPpT0#p53VCku$e-m(Ww5ce}~iR)==>X^3jsyGm%AwN0D zoBl-~69^vuXbxnucV9c78!^ucVb_65yPt4V5GCq5R_YtiF%}^aaOiC>rp|=Q{WXlf z-acXM;r(!EVzPwdw$H9*I8=MEt_Y!9H-|X?VFQ-adQ0E(uq%Ds{k2rXr8`&)cU|iL z8AGGCFr+a zHr!&heBU6I5`4WbD<_j5XKlN!mHO7}D+^(H>&%u&q=vcGVo-7Qx4sAOqeGD|1$vV3 zOT7<&y7}lTf@2F)!52hLDyVF`nqbp{$TQ zgtrrK0UiY4*9VYXY!41xZNwh45k#;xoXFhh5l zN+SVwtC|}ej8pk`J;nTJ^r z9_KLEdJWp!W5w56{`Fe-YF1P70}KFXbgI4GF@4iF`N`K7U+wtMKJTHGMDWVpmL> z9TGX*Roe>($RCLtjWbHQ;vMVcekEc3%z4R)Gsy|(Bh^Fgn$dv=)IPwi13!k zqs`-4#jY{N<~y^T2)~;5H{z^na`9ljb^iCnJqhp$;^s0FWXWWlYs*Fak`*N47nH8e z1W`!LN6Gt7iZ~4Og6il7s?t?M(A7rH0*$tk#W)^R0XZE+=gxXA%EdbCXt2Wh*!^*O z(7d>h+@%zmzrI(D36z_Mb3)WE&o4`o-xLH4j%V<%2>-X^>BssuEtMHJ*MpgjchuYT zST;eiw&Gew!phHKi65G_L>Hyf*t$*0xaBINAUnnAng8FgI3Z2>iqcq1W0f|$|55(L zW-zRggd9J@3OY(0Zu__L$#yeXTB=>$D)?Zd%XxD}b04=?e+`a449()5VD`g)=OTWn z>*?3`%<*FjY3928ha4a;Q!`!=uHiRd9D~g9DYTZ3-SUszex%jctb}t6qEuoGZ}Rpw z5eS1usTcw0VRRSvY6%b7|1_D9bYo~mGx2VRqSK$G; z-dk+q=WtG~?;V2_^aXzG)`8U-MIrsAadJ(TY({a1lA2U^U>OYToy|lr$DcnibufSm zs>(*u%SV@@AM|&bIyBkaS4)frN3nFtT{1jc@Lmx70ciph813OV4xAq}t`?YEQ;x<$ z+%&~u$96K%z7SgVzJ=OZ$2n8bh!NExRf)`?v8G5`Bx$$GG9fVrn8v2~+ttBV=L z7j#gZStHLLE<&`k=4D3Zx;JG7u#YbC=OoP1ne2$M-hL=fA-@(_XBm`qV-LcK|0Eow*8L8}k8;1akX) zJ&3R31a-?F*-B?Yott|Ha{`g^Z5=Q6sn+tvGHM>TNl&U}wFMf4+Di$7MeQ()t6hG# zf;pe2m0L%yHPMUeNuc~@(oGlG17|B3`_mXr-sx9koaEL)a4~Y=hIH;}xDhbG!H!zB z1_}q=;D`@l4z-}___`v7-S+5!y%?W5VQLfm-dqPiOb$xuhB3g(Oi0b(m=K?o#@WDP(tK_UVt^tvT z3r5HHTigMF!MUcT`=gHc<~P059Hd9?FO=~2{dP^evv98N}UhjVBucW;Z| zM&9Tw_A!i!dZigjqkE#^^PQ->%12IoQb+hdzS7~7LS7i+r`1>_d7 zD@m(z!*F0hM6M5AmGRvAaXxJ6pVWjDS)LQV%I$m(J)i$#d$gW{ zGJ^YaZmuB!*Gjxn5BuAnjPJKH?*GTBN~aX`biuv9ZTtqJNav&5f(#jLO)ymwfZsL(MUx z4Fzo1$ZN%`^EX!JF-6TyadzJ?C8LHlvR&v*AX{^MXmtkiZX}udO7iuDsNOo5<+?f5C~iCBC@LiecGTB2CHWSEAw$^Q*#yW@M+gxAj# zJ~#ec$H$9|$NbFyOb!|<_?BX>j>R5=R+#v%tk+BpR26l?i3|VBZbQstEB|8K>!ycZt>Qo)nFB+F$wyoAI*0ftzxpFg9n zMY19)c=utUL2R;kujL-`Fc<6nU6H-zAFeB0W#vX2E#Lwy2o{4`_-~-?4*OD{%4u58 zVYLstDTc6M=5NHQ$F*1C=RGUk*DHog_=3%j1^#heT#=el*t6L#Gq*_)f3)*i&YjM6 zxl)X{u;mT=dCGjN?wkU67w<*4FmJ@d2Sax z^}9x|x2KUI`xe#URQqE(L{D2KtX%qwt>~?2`r@Ti}p-HWiTb$aV`ghI;g8!n16Gd;h6!2dy7-q^>ddOpSRk z?E0bonWJ+bQ>CO9|3I7BbL^DWiP^XFWZDc5q>@q8D(s5DLT?->4afYbO&S#p^Xvn2 zdB0xz;m{VFH2u?(5Dqnj6Uo)~!xC->yHgMdrrpj~9ujS|zr8ZBdp^E}N`RP)h!Ei= zhV80Qc##l9qG{1=#XfeNbf`vMEPwN4{114CepT5$cdr?7PL||yUCDXxf62uK* Xa2nKBBb(`ekj$-L0)&MykqgIx};Zokv&qPMIN79#$3Zot0*KM6d(&v9x0R4Gt8%DsS(F^4D(m|pGVtzP? zWppJYCi%0wtObQFNW&X4WvbhWAnwL85`16V)R~&smA`^ z`*$|>3~}LnrdzER;xaYNP-DQd9>HDSl(pP}I2ou&N^7|@HG0{2FDWMVkSl&S?dbtp zlBO3ZIf^`W+iEdYJn_;O0}!F1j3OC*2z}4dWvj3FY#FTOL}45sM&8H^uscaCSDi?@ zGEkoIW4lk2o;`Bg3miXjrESgC6hRo=j>BZWPB`|IZOy!l#aOO}RNDqM&Bp-8?+#az zDJR()!QXtj)5D<&IF3?pD3x>0qC5~>{;B|{nAB~S(Jq5p|7}qDtEdVcB4b)L`op3=h#3f*We6Z1z zjrhrFaloCGRw}|gVk+@~j0FQTi-nQ&{?Y0q}c&9vI zs_hvW&k;=xkdmtkr~*(>0E&307j4T{OPPaS)NUKWNp#AUDL`?0l^wtuM;^fu3qToD zqc;mZTl^>TbZWq&Ze-BFOA(0-5WLe9+fh%n+lF@{PI+)5PP68{6M4!NcvAzK`=m0c z**FF5ZZ=XuF;eQp%i%^R!6}yz{XV`3EFxfv;rpLDpUyOLptD=Xu_JvBtP+5&Kja>!mSI~C*gz{Mvp!Qrhvd4P=V4E7woJoP9kr}f@j@M$H>;0 zIZ5Jkc0^4Zu-qZZl5?)o68a6wa469&b2}VdNJrWC_sjDBUUANY#P>6BSvk(_ta+5v zB-=D#xtmX}x8xx$UD@umT1@g^5t28B{~{73H!dUAv|;g2(C%XAQk1&A3^$^~9#YSN z0b-ttrsW3dVlJlmCR@z4oy)pI1JUnTa$EIRyH*#6Vp*Ci>30_J8zzzdn!+&d@6+c2 zD05&K{lLN}2bAgPZP1hEfT(w?wOp;+lbq@y9nYsQxW7ke{jR0V0k+@K$5YtUPu(kH zyVFeZ(-Ph0J)%oI>pWP3cTxfx&*P(so-UYoXS>t?8d*XAb7Z|i+U7{!DQ4uT+{T@- zBn&JyEiqs-f@W3(94jalOJw=DF5bT=BX&2(YX{*6>TqiE}$vkr#}L<|k>$qW9 z03-_xV`?nmSr>rP>m`%aayE){{wC^oEd22`B0{cP!18B}ILo$b#zL-U?Bi84fWH<2 zc(i~|k&o29CcplCD`f5A34*Pd=| zbJc38ZN9m+-hJ9XVt4?ZbWYpyq|sfzlYVKtdm6lHJL;A zPH>cnezPYnaSHec&GVW7Mqk<)4^`rMx8jOIn? z1l^E>7K>I_LDy(|C0x82Twteh_b4--_?{wb3|uTIMx#un4(d8pD}Wa3|9y4+tH>L( zlKAwY{P`8j3Z%F_`a5nv8X2YXyR+r9g3;f>{om33-$Awf-c0l1et0dT7b2F-_>Ent z8K_KnMi$DeF62P(K0QTVtK}6_6s)d-uGEgwg&Y~VIMkVDa#6Zs_7|y>{^#*oo<*oT z%`r3J(}MNj1+Kg~95QJB$;g1H&4VffS*-Cll||OJODc!Y zGt6~|xbw>41Q8E7-&nuD$A32aT?=uFQ2m&p-)?&u&64x9Uu9nvbTm90aFL;^k~X zh)$a&#QDPDoi38=>MDSsDq(GvlHF#e5`W5sN-^!)H}-$u0w)mp2m6(r+8_jK&5#8NC+$q)Cs;QOgwRA1DL>o zDT1ZL=Wk!VI9|H;)cKNF8hX-qmjW@C?vnjyJ81uD|A|d$Zo*B`ISC58%}5y07p>Iea${kv>77XzNz0)I|w=Q_W zsD*lJ$T#g=#D~|xhbWxLNZqo~$Qa@r+3=JGXqhlIN<`;-1YwG(dCR(15XcZ4!UFFnN4Qz<Mc!&U^p2~P6=$b zd(&d)Dz}N{R5XX+5(Kx^nlDwtmHKSTx>D;!sUejbwpt@BH3DUhiXD-cI;T?Sb+Gco zrUan0{zj@%AYA6N^s{8`)P5TQxoqI-!-mOs;3PV2vT2?*@+ZU(=>yms-ZJ7;01r)& z2Fi;ip5;o_+rn-e-VYSZUZ0#6uWyRicbbttTEHR)=>L&oRYvATxzPqAK&qHS{AdFp z1ZwbF@hip1(p*v=tA#?bj+&dcI&? zXz~_Ib?l90n3O)IYlbEhWX@HTzB|4YQC3g8Z7&d#zbK{vSpeq+{&|9b)}aJ$z5#?- z+DKniRX*0Iz0VQ_`V{RebLfjvY+Z$(yVNm^9-aO7_W{~uwEF_O1B5(ac>dEH2hV>< zxL`wY3~>2p#bOwi+8yeZk4;4&u~5QvEoHzrEFIzmFvrMQq3T<3`*#$qVYyQ)rK)cz z6rs@fm5-aMTf!TaDf-`#Yl3GA)XLu9^SdeerE$*`EFza4JZkuB#k`U_zvP$yRn03T z?ldV>;93DlD}tfrl@`zI$~ky9A|;F--zh|>bwi&p(*;y9N(gcF^-9jqW$x)vK@22Fl#O4LQP3VQ_$EIiLS5u>8XDiluF$ zM}nqk?|zq}Npr+z30jkR*Jry5qI_4{vM`78(HW z@9#Y87%r^iivRQvRc!y1J5@P%nu)H;yzHS_jIUtnkNC%f2$bm?s$OXn9OBk18G%7mypr^=<;-?|G zGAFDcigKE-1lfP1PSmL>E&cBhJ5?H*6*bF3;YQ8|5oA9wJru1J1g2+Lfq9WVT^QvM z8%h55Z_ufieD2n9{TJHb8QU@z1}e<&q!k1ZSqi5xn8MHev=0K_>}6Tf^9BWe>Do zUn~;a(F;p^UZqEQWQv|>^UxgJ-*+Ijp@lh37j5ok!Z#63#|pr;jsx-~vOMCH2bLhV zfu%eE_`&}P1b+|3wckTA2^Pr|7nX!eOTobEwwa=ajmq4(vvoGXw#te~H;?;Zr(>-`sjbsfMi1s9mYuzdE` z19E3I^g;Eak-a%W4HPNJ%>gd`>&CU~_DX?cg}gKUE}ZgG z9_l1Iy}xJp55Gfhmmsd&tyTe%poKatFhodVI`88BY9H>MAt)(7mck%ShHxxzh*!}*uvN=GSmn3^$ z^deeS8$Basr#zk;kouV`j(K5N9zQ{=)5R&dKwqZTqPq*rgNdc45b~D+i=gmX4^8Qh z&*YU1)EP2UXOSFo0pT>oXFcN#(8huqq^#m04lW9Xt^wgRD>Llp-Ai*lygx!66Oci4 z_8XZ=dm=!km4h>Aw^||QPk4p+V8#O($cWjF?>Uz?2n>-mpwx1&Y=V+0FCCY8=@<*Y z45%;NethDbTKI?TNW~x&GkiGjlZfJDPS?fZLraL%Q3DP8;i9`zi{oO=d_6p;#~th>LQS%qE;??RLl8VN!=9LF>E>3%cVCF+3ap znm=hT3KaK4^CuTYp=O}dkbgkhMyl4i1@1uz?}JwBZX)8?yOP#7G_-E7O;cn-P2b-K zJ#;6z_5wG&uGuEa)|Qh)A`GT0hh89aYw+C^>=eEYcO$8+5)<=2;LDxqH1kNgE9uX( zsjcLz$eeY5f``_~sBA>_jB>EZH?*okh$=jYx#)}S^?qmyD>R*VRtOFkxG_U>U0=fYx~WHiJ@x6+s=CoYNPKKISZ)Mq+(K(31WSEwIpG;lil+P&RH7*< zz954ukpn@*$){qObAbjwJYl=77Uc^t6b~@U5d`nj5Z>(vEWpss!c0F+^@QT$vXzK~v9|nj2@QG3J(mOv6z+l3hL2 zV?2^we&WIykK{2Qo0mC>Iw$?$c^#->3Di)7I!91Lelmn}1a;2O%}X4FNvq|iU0UXR zm|1P>=7CN6%fABV!qBi>ENrAhum%wf4fqtWF`VO4Xm_gv(y^o|$S5q*D(j@j6xCu~ z>v$q5gh(1%Ew5mz(uabn$}nlQc9AWNG03@$HzwmqN72#!qJLz$n1ei(Q%-A8c)QP0 zW@Ji{f=W67Q~DNjku9+BS=^_2-hv43uB;e5D_o7Ih81qd=@NLXKZPJIF)cV=hlks7 zKeS@-d8g-ZJ3T*h5K6dr;uGmhK%Dre5J0=FJ@pgPu9bGc=Z08P5ZGM|xP*+i#%}mI zU512bUS8nx4h(q*COqo7+kUGx+)h%1^_a&eF48OKWOxcAJ~joM@pE*O;$w4wxkAp( z0aCIN=lsmLo3z_ViLOARwPa$zq|L9iGSWp~fp)vP2yU@~cJ~yN#5USpIvZ{c+Rl>S zngRy=7GpvsKSKviawk;M$YSmBKo<1x0B$&9;Zc#BRx6Svd_;=R>wpRHV1x(`kzXtj z#Io(o6q%hBNn3WdGBfz`!Kr%G*rmw2C(k=|jLpp<1w*v4M z4K6egb%5uPt!YU#wp2oW5J_=KI}TS(kJ{>$77eYCUfMV=oU6I@plcV)EtEWz_xC5+ zitDAw;(|Lj2axhcp)_zMO_`Uo1NZk~`M1mt$0xY`t}PF4tPpOkP^LaSUjuHRNq_ux z>-oN2C`MifbtZf~K}hll(aL`3SRMFfMG#pY#JR0G4V1paw>uW51i@-N%uWdsJ{;}t zF<%xxt)(6Sn(qnCUl0LW>5&09BknLH?Dy0DP&5g+W+1n8#BONh-F!c=@n zzdkYUVpDMmv6=8NWkq8%Hm`eS?lVOqflj9NE(3Ww55z?vh=Oa}x#o@;bD08n9|Wyo zmmwnEXt!aR=`Q0@NWC_m<-t-BE%ad~0Sv3^sR{9~lY)Pp zt(NOlWv+8pbD}`-9|7|yX%6RHQl^>0UAm;z{t;9{J5?#kIL$E@m>zknX6hMj6fQ5elfb+xSUQ<`09S#b7F&`=`wB#5t&&b~+si|jsVy&v zWu%_VVHioYKzrmQYbHiX`zkXZ}8X733(iCO*;c~r%>ji7Eu#j)TuUC z4$YYBg=t>r6PE{*tmq{)6SJaBbav{wTEk*t+J#P8G52v!F;}z?9x9&8c`Uq|NhP)U z{A-9bCoeJON5!!3p_Zql%~+{sil7xB(bM8q=c38GTu4hiFmF;H`+NdRuJ-6Z4>1qT z9Xb>o%90qHvGp=wvH44Hp_$2cpt3kZ6NBb5I?`&9qXPy$mwa8K!Xds)7jlL9%hY3H zHxeV#oag!T%8M?OI!>ZfbEg+0`{Jc2dm3dgMY-fVSMsTe@<>-4`eU8xcs*N;=pkXE zLF`2sC>dydUr}@!&&8ssZ5M-_<=zpQRyT&lk^Y{EaYQ6Ekto-rOg%n33+OmQ-@mfv zKTWb+4Ppm}AA?&xQzGtGT6M_vC}J+20pR3|Ghr_w28Bw#Lhl(>ON?Q6-YDut(f$3u?rxfIL~>pfqO!I4H=m4+B$q%18Xe zz#N3`Eeie-3!8f)0v`2B-EsI!W@Am;V8;dvOvf?6$3CJuw-;g`(Qxl$A7k!-2zUoj z@fDcPW~;?A9605o*fgf86GW7GIYshkB4f1^cwpi`i&e(9&-QN2Mf!e8n@ap_JpO^Fe#aeguz~tO@9@8!$g&=!>6CkBHcvxV37M>3Wo7+l^{W z8k2AmE1{%~LV+8Zfw=NULWPl;#4_3$VaE5&!_7X9Jj@bq8<+&B7Tmdr&z*A-j5NU6 zxD7gcO-DukJ9*n`EeGaUDv|vrKXrNt%t4}57;JZ2tz~5@5sjpxpKf>^U^;i>E|{}O z^fIqd$~-POjt_+VTrkftMNhJbg_O!_hjaA6+Mx%jA0$0oQ=C&sKl&fh_1pSE1{d;8 z2j_)V6SI{t+I0%3LC=_iD=gYo&J&CFqO*?wu2&KSM zG>d^7OWx^)tya7(Qs&U%UOA76 z!UJ2L#O*fP1icg0ZlAVVjPzrjJ`37yh4OZM(3PJ*?Y#)clQ1A3t`ebrL1jxhPcE1%YieX}CRxfPE%RJQ zzZtredY{ParEdrOZh0U*%KC6a&mdYRtqX9;YYvrBA6@ zWF4a(g`-Huv0XWjvbBnHpC~c#Wvg|d&=r;4IVk<&a7h;&X`%G4gg>oD@4kxW6TUgO zrMMb7-`&6zI>Rf5!Xq;iPr@M9>I}R}2(6ALCM|hUx>ZP-t^?RV)7c=OJ2Rbqtg=z@ z*LfM0*`C8FB`4^GCsYz|uXQ^2_v@X`HqTxeIgv)`lb3LkHws=UcI;!|OqyAQa@AE* zTpW{!6-s7J0DN}sQ$pLIl4#+uw0G~Er&dw%^QV0ne75c!46D^x0{Z@@zY*>foLlQM zLU#(Oc6|R+T2&8o%rJP#DS<5eCq_jC)t=DN$3>3lVluNVh4$akAGYzJQYe@7tR-6~ zczf2#6?N&c>IEa#0ZPbUekIy#Mo;mCJ@+B3Y*?n%>m zj{ehqV+rmI@kiH;sfFG}hDJLvaK zSx)neg%I=AE;y(k!}~uNxfa|=N2fE5m&;{)1wcP{I9TmMlB(N$qvt>Cq)TPU9dvw1 zPF7d_Q$Flpup!DGQI>)FZ350o=M)^hlE8ykEWyMKakItgE(1TjZS&kzd#MZL9WZy zT~tsMo@-$3lzJ_Ug5k||>g1m~HQ>Wyt}cMoL8LsTl?m?2eD}uL+(tTAO`I}!xzoCi zR}>icj8zSz3V2!s_f^W{B^NXEL%GX$Bi?<~X-reSIP>zNkN>&gquSn<3%)*kw)iW4 zE>_K6^gxme$zBSDdiQITQ}MX`CtOo3)gE2}awYt-0E(yb!ZZkZr6}bND5(gaNZ60~ zZEq$3wiKf2km}jR6*8}GBkN?wp^HNOzK?kbh!Z8R(oAz!ErCJLq?DL>n^za4?$W4! zs$bN;8*F#UgfUMc8YsFs8n~x~5JjpH;$>LIeWQpEU;BXAxL#uMaK4H@eFi^qg zq9=I_iaQYS!g+0pn#Gu`UlFf4iU>%fr026kI7*X|;EF%SxM+C(FG7-KAYW>MJjVq& zg*PZke2(}Ue8uFjuj|U{43%^S!n>vd8)<=`jqE;dm&=CQY2~-kq6`7~#SjL3Ur?}9 zF8FOh!EU+WPCaI6#4!A5==WOs{bR(>BwQ>8a)3_V-6^a@d|!g}xGO<<{7k`n{zk%w zWV+ET3WKfXMqRyA2v|F6whnePJU%O z4T?|!L7NBfV?Y~@PaB5pEM8A?y!0!CfXCHM;%mQ^Qrd3e)vt5UYb3C3)SYx_frlPH zmk`(C9-&p>jr1oa{Ilz2a?=zhm0sd{1Gf&i1@^ z9q*m8mxNlnh{M2c6LGU)M6+($%l4Gfcqf?rK+l598=xGY%Nk6YK+l!LrrOM28Vb*+ z9HdL?oG6|jUhAxh>^&163MXxQ=~~>zmR*D^YO4GPlH@~h!Dqe)S011wGcEBei7C>l zP`@z;)6B0&3sN|}6Pn(}G58RL17D8y_#^)0s!0-r z+(>P&@gRM4E%h}IuRK1G@Y3Vgh|#eJYjERXM5@PKc7*i!r3WwY{{~!o{0erw#azrq zW5YT(1spUMNbje3?{QDU2ag{~*!B1s?0NiwtI+xqqtIdB<5v>J+>mot_2kC|`-uVq(?q>R<`EC$B6^aCpvqi8rJ2fcO37$( zBEQtqYH5loyzy!ha!a`zh2!Um=eshR!Tj1|>d}pthhyg+O+}YX(aUaTdah3c>|VwQ z6qwI6gqVJwM~7q}Sy*7?UpF2^{6o6HZafH9yLk5u_t_f{WFL2vIWjCoK~BYSN(*oo zKMsv{06yFEFz)HEml*E{__i{7<>Kk8r7{M5^>`@Zwbx*}!wlV?@V#HN%#zyf6od8I zgXJ#VjjRZy6+F7}?mLur%_sya!FN2CuuJNsLaq{XtD!1p^~M8-2jJ3r5$G#7BrcKG z1_azeM=m@wi_xy4g@gUBcCh7k5s823rH(Z6-)YbZnE>Q!P_knW*=s^?H9)i^9BU)g z&$>m2mnlx+t;b^p-#zXq*yV2}e5;nNVF%K-Ix`Nm-5JN(wcPnKeX)-6#sf6w;{!W< zrGEQNK?=U&ff9Ui=9oM2#1b%&m~ZRKiePYU1u)pboO?%@XwQH^@J|vV!4D;Pf*(i_ zqLJ=xcl*jpm?r?MPy$f$u@d}?gjm$#indaF3eG~PwynW-AiWCK7yPq?L^Lh*A&b#9 z=l_r7m$OT15`-1sQ z2ngFE_y?s#c;9@!98$ulb7MX@6i z-JlS9b0Sb`UQnxLGLx1?kXdFU7>XM4OJfwwU=*bo^=;6+aGt8lqf}KnF>|q%3q5)m zaS)(>P$T7$lDkU#sQ5vewh1Ppj#9*;l!yJGmz`{g&c9`e=Bom3p@tfk(23ez%3Mjv zP!h`uGHKxhy(Y(k|B`Sn>ZOmd0J@jgdh`gz@V?I`xSpw*$4Eh~qD)*V)YXNaP@#h< zjH)dJ@~U(cN-hh+N}r}vI1~I>!Ij`=0{W8U^&Dd(04_&21#4*i`#@NBa~i8Zw-J@VMtFf5nd3eYh_T^JjN zF*618(A1(Ls`i^Q=5!SUu^^>bhEqqc!M&y&ZV4L*Xh<+k3OrUQ0_mR{!4DN2iDnhi zji~clIuklg^;ECK7YmSY0#SV*ltE`%(u^r$shhYK^@99Bzze|+4LB3**dT%aSnziW zUWukVA$x^?93gQm$gh>`?QfKMEaFT4Rk16vNE>=2>I{RODO@A|d+>^YDy<=CghTtI zS>I31=o3gQre6yFQ!yJ+v<*0HoVS;VL|PZ&UkdI-uv0sRHFHxlEoC>tIUnKqS7QEE zIR7f1f91@-^50k!J&zSMD@;|r`{D*`C!4n1V1rHQ_5c+On*rgwS<_w4D^^i0Stf;HJ#6PbIkA1BDUf2H8wC3)_F19CLTL&QCS|0?6clg)&iOgQt z`b7J4;8?$CXXut*SkLKc@Xf-EGVmrFtgCUO8QTF&w z582P=A$x_`(lChw>$51vNvt83&JB*;W9uF9wtC1uSRXK9CcLfhT?RlGK0x@t5dPqs zg}8jLuT|iY)x&u|q^97T;CF*B_#1xw4lmw;)s$=gDEM;;D|Kx$pMBKqBe5MnknNC$ zg%>$I5)<#y)tWs88S*>f(Gnlv$Ue6Y;K(|FW9tAuTLa zP%dgHm|3H|Fuib2G!$G<3x?rhaVicMn|GWC;QJ52Pa3{wRl3V!jU5ck20 zkS7sbhmBC~#5)C&45N z9T~^cwKLVvE+!(UufkE7)2;Z>O+UoRgmkBsT5HCv66tHm?9ahv5MJwB@ho(IGp1A6 zH{e}}E^}ay6D8f}w>EqU**;zm@f>U~IQItL{QKDRW#G)e)^S{~L)c}{Lz2SaX9^yp zJ#E;BUG^RwC*Fp?X^Q{8fCkK{{mH~1J|sWPUfh6ABP!KAXhqDNXq!*-Tb*pQFu_nd zZHTcHIOGZAa>-(YIRw!yLmYCKAi6UU!~6ST1*k4K!GbX6uIUt7hwo|e35?NwaB9Gh zkbeu|qoz%rDXjeNz{+^hrVyWTOfPU#+~D@u)0;xYDW@02&BEtO2r34bNxHOR57Qxj zrfi;$_Ctt&Q=S0Cn@_@xMWx8Tt%h+7TS52pr3!P+!km z6Er~j7P38bWc^J3vk=ETjKPWjukJowDx}?qd$77uv@(ZyTSPb}q=kXOhvF$M1L4qe@Xg<)-NH)cspArvr)`3ni--)$t@B2rJv7e0>RJmy~| zjAHJyAJroW2OVXiZA?X@Jr2%eW_L_H9D1qiUe;L5?7cwWQ#E^mNJfMxKg+?&lnZYRPWD*T~^0_%VxP zHpU=WIE&F_aTPycvRFEcnQW`SwmSI#u3T@kU{x5|Lqvbco%A-YIbc$MMKb#}tX#YX zAX5>SR<0mxC^`;SS8ksEC-s@dQV!WnU2-@aa=dg>Yeh@ov}QD{YDV9N5Z~WVDiBL5 zR5<6unY}>8wc>MZz8SxN+13AsKEau1(2-R?Jr_} z6vK7Ho-gkWZ5`0Z4=N{fqqit#yl!K7fs!K`>8+tkTcct-_`;$ETS7Z6k?Tmqo={r7 zUEUQ_IBp`E5RX_TJ2aV5dV09pP$E5x4Qsf$ur%r9fO zsGaVenEC7#!aa^t8gmfy?$gH|cwbLzOS9~XTZ`s`DD;Pz-%8kxxsb3|S9jTGA7a=5 zOgG1t^;5Tl&WiW<={I^Zrm$Z--m6(AQl=hl+;@ysrXF=Wo%@c_#y9k93ZG*BWNSli z!mpUGcQ@C!^i7 zw)XV?9@DI^J#DqpCbQCOTaTY?$W3D=p;ZOqX6NbSt-Q=esZ1v?^Z4=OEx9$D8mR)N z+gaawl9$=41=C&cY-|c5R zt#3YgTq@PAhqbk~wOI^PcdZ`QlP8;cFlVIA@@5%_fjZX&@jVL+O%qW^#%|)$pD(Og zxR&|tJ7{5j1J^OzFyJC)_Nr|Z@7Bui=-Dvf%L1~uRwjFI>bL-MfSXLIg#2 zeuZ36QYk@sueBL8zaJt^>L>3lsdp9ptuV`+3_Vuqs;e<;%0*xQ>CVxbP|95 zea7((VxjP=!s!&g#{6>(uj6Kwhy05nbJZqY$GaLjlW87e_KKa%%y#+D8~DPmhzQg4 z9ous=YSqlLRhng}w1MzeJYC8}V58W8FRWvn8l|Ey;s!e)R{NoGm|38&=s3in;03PFd1y_^ow(`>9i*Ouk@g<;K$V^A^EP^Ak5vsJtU&l4leK;U z2vQ0)F{F~=0Aa4KV{qvr+76CWSKoh`ae75?P_J|e`u7+jI5sNGXsO5!qqrsb(bTrj zzL6|F-;5|Xvj=bE+~+25M$9(zv5BpX;T>&iSc&JAkeV6z7_&Kf4>g7b!=qA-;k=x; zi?@WJ8u2qGuR}a^dhuLFm*IgSQ@d4hTm} zF0sRv0>|N@0>8F~k_Inv}oqvIJS5#32VpXVE2(Wba9s z>tsfs#32_+Lwi3?9G;s)lGuiKp*D}mz>Rm54J)yJP9{iM-SVqgiAYrn4ZUC_qez9gDBbXP@BK0qLPamD{9{xN#O-^_MEzoMy(4JVHJo^S7ccV=tTs z!}Xr=r#fVC{r+BIUf4DLZ+AU?(cS9kADfTq*E9a4`*e-!JX!0crJt;=7r)l?Ur)1N zPadbh*4CapnZosaKk4SGBI;n~z&R)a3~;aq6e8zPh@7jh;+Q#L0;3X?r)?(Qd?3n5 zs+j}k0`(we9yGPL)Yc^><8jlhE&J@6jupdP!8jwQ*?xBHWSZ2M3zlc(kVrt~4x3Mt zmk#Xfv*FU=heU$yI~(k&4fe7PHn+k4FB|B;zyG$u{?i8gKWzBZ{`1r@V5b)H3x}dL zefA2GxO9vuTsZX#$fuc8gY*APmw12Q+19qbny#Gdz*7n^IJ2je$X@&ps2|_g4%~s0 zDIPDJRPp%1;jh@q5wrP!*|2875wn;7%Z7FO#cjON3SV&>y2hyiyAJmi>^c0Eg#Ctd zeh+8Gwoxlse~$TKIGMA5dZdJBSzN$MDK0<{Zbc6-@9(Q74Uyje=8-Af(@MqsD0a(7 zEsZ1ra5F|ZHGIc9CJ{f4`4G-|03+`7B%grdV+c5AVICFRF6)T4XZ&j1E;X*PF8k!9 zwlcbX(17R8Vh5rx&SJlxH`VVtuD7(*v+h54m@4%;D1ZCn(6x*3c>#U+h0|L*@h@U{ zkKQ@tR?wZo&)*u_jDaRII`aNOae)|`^XE1D7>8w8^l2?K@n?p0 zx*VPMSU`I$FbC}}8Q0Ch9^N@&1-RL)YOTN&pWCxlqpVt1i8LV%+vg{a?>i)F-yayC zZw(M=hksA`>>ss-E@SpBE%33PD@b)sr~vxvFjV^daM->k8>AT0-(J%jcw6?Is>V(I z{kJ`BDuJ0{jb=zC=dWcAsnuE@FtlG!4J`_+I1d}ta)R%(T?h7Ip?NvI5Ukdy9B8{& z(fiKKsC>q``{-y3ne=h_KAwW|dGj^qS0b9Dg>lIjBz574OP(N^+eZj4QvQ<5$22dT z`Fl7V@6=M{nt}cvL*9i6_h8Hgob#|JxdV#(5O5bFJ_L_XK=3i9jV|b1ieo`EG}IVj z!jK$4{$-dHzvn-sDAtpqN5vd`N_{{wH<_U9CNt~mV2YL84oXGWjQ*G@A3cIzA|MQ( z(pwsTpt}a%k>?mzQ7R}LPTJK%;PKqYBg&8}cA|q;VAJ_LLw7Q?a^ivau1u#8`Fu|E zd*m2( zN^8^uE?}RE{^Fhp40!M8IG+!=6JW(*WOo72Z}jcN#4G+n1Hb2IY-#8(RQ4_TO!%PS zb4tO44-usAe0EgJhJ8WC=um5erj+8avFLM$jVL4fRBIy;qu5_0uV(;BK(@b#JkWGR zR&_Rr3C}@H5s(8sqonaLLR5PkBcVPeoSddn%G4Xb(Y@ZoBl?@2OUD(&DfLC91Lr?5 zl^OJJC2B5m?^SPBb4Sd{D|RAIQTGt~nPSnHRozt!I^}{hYCM{PeDze2Lg(36P_fBZ&SJHHL(^uc<)fSyTuFsnt zeiucA&kVpCI*QfenE33@2`%I!2pebDB3ztr>APVV;pNrDYdx`DbG-&gQBIgYehcdfpVqOwI)6yfP$y9Wp&62 z6jeih9m8Bvr&Ji{3rb)&!iGP=kC=^pgkiv$|F48rqOz!;WqGoKBE0gd2CYF2ACIEw z1)GTqJ($9+U!SdFFol87uN1s)_OM^U)E&s6!i`@mFgve_$$mg>w=l2e&fum(ExMp)Xkw$*ji_pk&BV|kCcz#LX!HE64(cQd&8;99L z&sQ=cTdKn3NT3Dm%xm3-3wG{MGs_f5ZjgwbJG)oEN7;9BkgtC8vTvz_e(577s7*f> zs?rf1%lK{-g~`MQvf}1cUZ8&ZS*LIJ)BHj$8yv&J#0M|@)baY-Z}w=|D0?&<`-|f| zUilA%!}w|3)uEroz4(m@F?Jf3Jb5xs;lgjyo!-qc`IpG#mvS7bvawUrF^gGqstt~$ zI`D#$(op7B8=0HiDD2&ID@wZqFQ{x^IfeIiRA~+v{?6X(2Fu@;^&?)wlbVZYFVRMD zXe+tB2MkHz;QdoQC$|hbWEphGdg+*M&rQFS0tMilyQT{x?wS(LxHD@RoWQtb3e@j1 z6BV2rCClds&N53$T@S9P2UmFyuBZoB)PpPP!IdfT=3nfBlzAVdq)tewBPCFGc834V zn9XXiD@>bij#_EbKKzT5=2j=o{g)@L7p7m(>1RMrKMwdjG+jt|Xi73UOG>yUdy};6 zm`})EcVap);u9K;$?T-z5a?lvF$)4DWpl2Z4$N5FdX0Z=(xjwWb7|IY3B}tS#alx0 zmQcJU6mJ*Lmz3Zdvv|!;*zO{cTpRqdX%RRjA|GBs!z*Zb1r4vL;uE9;r7@tKt$3K| zfjPTcTUjd+iZE{{Br8)_zbt4*`Y6gdTF|YFUcv{=m*1m=$4dmd($BP9TB^7)LKn^L zE=bV!ero|Y_;2bv)JlqQS6}H(mQ(FaUg_#`Xo_-QaHRAE?BNvyXiV2ihWCEz8sbzC zKw>0_7!8ORIWR$z1mh~nkxsf{_k1=nFl4DbIJ;ub4WWv|Y-!kUIz}q?ptAN zAlr~bwjoMVtOw?X?r7ZZ7?sr~(wVL-IJd~tL&Fyr!xyK+r&q7f_ED6fD3f9cRm~74 zG=we<;e>|poQ7~TH-zMdl0iNc&Iv_tT<5?v@VhMPoal zvCVX82R;jp@(>p0c{~l`!eT=m4gfnXa2V@%H1hg1nD9y+KujjT7c#hqJp2HVH1 z7aN!q;wk!tIhcL{JNCL!9W`WMeXw7@I<_&w7+&~VE%ibt+dBbcew*WsJbFBSao7pA%Sh63C7^v@YusNs^lZpGaDojfDY z#|Bl+HAX5OpfSR4HkGyCBD<+z&=eZ?WAzLTG3v z{}QsXqj6OK8qf-?NeU}S7RzW+Q(Jd~OEgOS)GohX!nGq%=Az>}jJ$MmH=rUqJEGe% zL_%AsX0WqP$hQ5c>WAbd+HWOfL4uhclYd1iL$|k3!Yge5D?4O5vDznijfzOTI}I+3 zG{$~U=zrt+VGzM>#f4-rcW{wzp-4x!+{x};Lau(^p;!dBWy+2;3Y?^QwZ#e4hBTAu zEX_`spfV^)xC)p(kx}f$>NT#cS2q(8xI4aYgP$1SE@7%>{^ig7gJ}O+KkBYO8sd@~xQBIA9DyV3v5_)iwScPL+oM@Yc zQG$HIX+?GAqU!3JNhqtYTtnvxxk^xpv@wO-gpVU;4?`!3G56B^L_C~Jy;p<=>DAy{ z0Ve-c=D-J98cu?avF2cG^zpBCjU-@<;~r_H|1#!O{NcmTh+QQH+$PzutPVu9N|zvM z4PrTl!CQ!e*S|q!Y6*)oA9iC(g^ZZ|^Hw!eAu3z6Xb<_?PY{hD=W<5As_V8Am+}+l;f!Si4nY-%Tsf-m z20)ekY8P~pZ5>$?81=Vql$x58RbDod-4SMTewC#6SCG?TQXe3oN=P54^3xF=^1ZXG z&Pq3Tw7{p$Qg4@|a^Bpczx3K1WRXVXg`e)qOq6Ujvrk%@O*NQ!X_Cg3x^Bq>e z&qg&)E5{VO7gLx?F00Q10{1kP_zF}u^F3Auj)5P8YD>ekr6GB}4XLFGMe$Eau`)K% zokPccoSOp9_<2Ej0^@9Xs0_&3b&3OIxj`0Ql@YITEDf|ND3zFfi%6Oiaqk(Zt8 zPAQT_>qg3>KG*krHEG%v&5qeAWwuP3T@4PCB7yTxGl|FpOy;;3ZR_%g8eAq2wb71$ zoItdSTO<`FE}A)jj6p0>9Bp?n;2>#qwEY;RH_*IK_@#so4JF?v`4uL+zm0O;0>PWQsMrH5}m5o7}B{+mlWaUr0JfoX0#|fE9<;bNNM~mDKa`OJ4U7?43WWvA-`rp>gr(B z2MIfqe47e1^gf$8IPOXz$1tSBsvgSBnSjc(guj;Xym8Cw1M^!So2P$g6Wb~1QfD`-ckJb0eyoN<4dYny+u?kcU$H@}Nu30}g!=p)T% z`$?XM?sLLN628{+IqU_Ka5o67SfHrWG5W`rP9MKygMJ~+L0}Q>>2@$fwa-2$@Fvm4 zA*WNdS%REIr^>oArf`U%%XmAhv!}gUFe5BZh%trNNv+3ImRBKXo*!Ftdb=YQm075$ z#6oGl%fo9NewUf*m3Z`;`BK5J3ClVhPUroka8@-F|1L@0UVk>Lhkl~c^;mlfyiFEm zf^(julJ+f@Kg3MRYx-`T?+HJM;9F9!ko%s%oin7Q#*~4W&buE8zlh*t!rw=1H!@(Q z;fVZ*r2RGgNZ?z-?32}#zyChHn=<3CcHe}*|Ni^$|6IBMx4-|6PwiASRO@=+yq#h| z75Xe|e=v}4gTHrzF0DUd1$XG+C*?W&z7@~yLZ`mJ zm_j)PTu&Yc&n+hlN4#JdE@~{=je**g<*fIDZ;I&_F-`+}BC;tQJ=&E*t+ zMUve=$i%aLuMyBQ>sl`X808`pM}Zd_VMGd2+n*~+gz`DybG<; zFpSi(Smh&@BagYoXMJXm*kOoot4T3N<#~9Ch8oO}hF))1{LDme41QJ?X0oZe!qnj- zOzkdZY1Z@!dCPq^N%Z}?N^+l_<3nuBf~h2r4@}|phZZ9FPCp!m7!%jEl*D#!F=Una z4*KY`8$GM9LbwdcBQYVh1xl;wr{nCCOg};X=|F!HnI^Jwa0arvK9%0f3itAuy{ zeZlP`)7@KQl?D&iJhm>_pj`0@Rg4~P^RsoqqDq_HRI?~+_S4{-<;gkvCa5UfaGwnl z5E7lEl;k(aCE-59Gtgwuy*ud#nJ#@@-g#S`=gm5btUk%T$Ak;Uew>xlu#6Y}wlNqFB8Yw}->8QeN6H z@C}IFro=oD)tne6=kCHh4DSW#kV6=uz3tp3^FnGr+k0uB8kmCU z%>AtktN+{gzn{4zeG0n&J#4=J-N!M!nnS9HOS)2wpp^<#U1kz?qyR0VpipN;lEaO{ zphvBaymk4tf&yT8#HL++l%b&tDlKLGdFt;w=M-P_#};?6_mnDkKEsiqUp}?N^3K+)cQo{ zkef5<9=B#hj>j`u9G}c2Y~3syjP~R1(~Yes5OAiR@lIb^YPGwu)p@#Q zJfeRe-*-^Mz8d_cAHm}+T4fP^rOc|)qxG%LHJxu&zka%P-!Tk)ecwSJkyW<7wfUFV zW?q_>)l1W|q8gfQ{w3OL#-r}$`h5r0(QOuPcRT%cYxSvt`4QT|UmKlHFN-JsZi!XK zx|{2Ng;;G9ap>X=NihsIEvL=mZNAp;S{-!5dfB*UUb%RwtgAJdrudF#y8qqXn`pO< zYw5zTqICOKgJno1*-o4=N3CVYcH($UJj6+NEzXn8$s{FPPIO=6f1v-x?%Eq52~M(- zs&o6Sdxn@I0T9HnvBz(l-QFYTDc#s&LtIp&uZAr7*z$TKYZIphtu)Jvz8aRTqv^%$ zviaA#e_uz39oBaBsJ5#&YP))~wyU>lyZU8qSHG(5>h0RDeqG zJCu6|CWRG#Y|?vFt^oEX1-qStnBh(B8@p*Fmd3Nh;Hj_O@cU5^SO8qc?p|}}BoU`f zJo(AY1U?uf18bli=YCLTrIZfNUb>z02Uq5b`A)ucERZXcs#+ZTo`GHZ12fy_;gQGz zyFFiiCsb#~AZuc{r&uL;m1FpXWTYo_{yJPt#&^|C$xu!dNEU*RZPScyNqv4b8V;rP{O)jAC_aE!&ep>PH9B9I z!c?sLM6;0(SVt2~>$+woM+-W&yI9G_G;Y?{HJka3H}b~o`UznLWM1M` zy~K9?#_M{OH;o<&Q`eX`lwc9rF)<@tI750-Z>An6+QFI6CGz3{R z16kBUz^K6PMkmGHkNrauoN*sC*KU>C0~@7wkisQe4J^cm2}MD?4)ci5BB(f$xC_-3 zoi$DNXbROJsX~!U4~cy4*95}%y7v2MI9xY=-&(iV;{|nE$sB+V+9fjOcWo+X{nQ5G zNq>o~kUGb>XU{qSvqpWX!TF>H!YsBV)v7O6Wg2xVqM8^xXs`#uS6 z1Td}b&c>6MwI`$E$*bCvjpE66?a5~G5<1o29=#d8+LV4r7J5PV>}4Qa*6+YvLrOMlYdvIH*g!qi*>RKX*-YBz z-Q6j!4vVY1@)Pd57Wh%Qd&#~HA-8PgU{uf8Wj~bucx7$jPTH|MeUA5$DGiY{-9b$S z!aIO8!A0Nu9c6|nf10D?c@UVByYlj&a;y+xg%ThW43tW3f4-TaP<><7 z>cdL4QCaQQrlS!9@vjmle6%4v8CISQg(tPi4*Oe!)g(q(+Pu{R6HCn|!Xjnksp4#oMcqcX;ZK9THLC?GQ1?Xj3>Yv(( za%h!jCq$)czP!AACQ;*&aU2%aQgANWfva6+l{~OPId-^=*nGbSmpScNz6ABcdvrzD zZs5X_=z}~Z=Qh2C6C5IvzMAyL#BBqST2P1sP_3Ed#zf!nZ*cd(77N4C2d;J({1MbY z0-HYQIXh_@kfQz(K~v^u`)5Fr7?6AyE>eQC_|#6snX}p*Q7_zR>YKeV%6huhvw}FA ztKgXM*#<8`^-B_kPVeyI?DebRs0T6STBbs383t3kYyTAukakRsvFnp3n>?8WyzQx3 z{*Fo%tYYr+0M9SrSp{%$Y%0BcDwAR=4Qj%osm+~Fj%|i&Ax>e$W)5e+m^@9GylZ>q zB;=Z)ywbjbzRCv8RRBuI3*eRkCtUv~8<=5i>4Mcgz$m_jHel83YfNc*W%J1G(o$-W zTMZWOi(#ImJ2$P~YU~ELzaG~^yFb{>i~Nf{UXp(TQ&x!Syq`;5Zav0c@^fj+V=FWv zj`UY^mLIgB6@KT5+w)N|;iK8$RTa+l*!Mz7%?C_SW*@gF?r3J|C>80~5xKIVhu5}{ zN#nh}EPMQ8L_^w+4!{+|D#gh6FE(=rIkLxx2~i;Tmo56(?6Dzy2VVft7obW1*h&=M z5i6qKFJ!ikF8SXp{`Z>yELMB8gN{!Rx9p#YY7kP*Fkas$hzbc8E=^9HR>YpBD=TOMhd7 z?%5-qxBQpge6fnwZ}r0(-)hcOB9DsY#rZdzeoZoMEcM)bw7=|2m%M@={T7eg%F zaN&mgBj!unOoIR<`xe4X^jp9Hzh|jdlEs2%?Vq9t0&u9$4&`j1Pj+yBSurBv-x2kM z(~_Q+z!2Y0k-FlebKiol8&MGZR%Z1y6UZn|P7yr5pkdq%ZeTNT0|slL-JnOfe!)D# zzoAEXVWjD~K8eX!o7UXQt9iEHk#9DI6?`!z1`G1`7_NYJ4a^=t8#Qjw;b)_VF6&tg zt91>7WY_RQ!;2@tDuWB3e6lHALiYAGM*EVui@AElUnpNgoV6Es$BuTYJf_5p>4=Ut zeg{XbYVf~e!v@jDqmAE*H_%`9N76c|VLb3ufFBvtwlOor2Gt~{c0+xbMn@acdY;M` zr23Rg^(l?uDv7B#(7rOkK1KA?TJqcV?ccNdB2CtjPtf94=0Y>gdYZO4Xkk+G1o!Sao}~s-k1@Pu4~AP~5Ga z(PCKT7QJUagXuFxH_M_%yd?YHiJfQP6B37n(j=6z9SbK1{lNUVU1~LAx1TB_Tpk2)Rh64mHGl5>X3jkm2y^+-(KE8zMB>rr)e+{rQ1 zV;MAN3l?-#Ht49}o=tHnFDh{{0S=doP9P4Vwkn-C@c$T2hBERI9D!lh8QVVbNdWMm z3i#YP;P@r`BxZLuw%8qjaIhdv3gYq3AQT5cnIUe-vg+UFLuik-3*k7`u#`ssXV2z@ z5`4}`bxL6IBBMrpVolOmj$-QY^%&Or8uGq|%lZnZl@qTF$ky14I$CEqG@BB4VB1u& zGAqJuQo+e=6K-qZnn@*lS_N3sCl+$q5(<#BczkWqdE9D0oyC~MFHfmd1G@8xrcp>c zWJzeXFf>eO^0p$Ld6M+w2fIu-P}2pwPuZQZslv7AOjmea_?)& zq_(>Aw1$r$z0gqlM^Vob5+NA`11{giRKuh(U|EZoXTx2KXqHlwSZSHY1q^9%r=F-# z#GrVz7t@GpJegEl#vkNgXOg>^YPdiK+Et&V>l&;g3j%2+Zes&f@U%Szl}59?R3wzK z$r;Q>2rXl;MBP#@UI8+TIK}`-nj5+AwR}PoIwg-WeO<5d=5<>I?JTdbAykOTPFZ3N+I8V-ivs)-`ZHH{VrOw|7%W?S z6rkdQp2VHrySB3P?27kha`y}#7l7sG+X|lfo{ygAv=uQLjm)s z(c^fvESiULJ@w9&wxFM%?aOgYEI23EIBvpI{Idv@Pw4#P!C%mtp{7b}vd~ka#Prxk zs$OHIvjKFVWXq{GUNcE4@oIy$2`eLOqPI5th)nj$0g&fxkf9L^e`1Eb3qkU!bs3&w zS3Uj#|8rf|zLj0pzSU&0wk}8GbC7&!+R^2&CXHy)>@rSHQ~E)3ZATZwY8TUL7dPd4 zmCrFfkA*s)WBN(xIo4)#^f{(ac6E}rI1oui`h;bA!`^N{{To4!XcYP!(>1$V>J^7B zYBYMy8illp+I3NUTW%C88ZAq`<_JeEG3eRg0VH|+<1?#+#jOrjX>|x$FgnxYS0GCB zktyux2N@e()&J;eeqw3n zyk|XnX_gLpds7yDWcBW>dD?sMd>_F7r+x;OwEv`FK?jWdR|sAhDJ@B1R3U^O zI~~!InTd4J%RMgWhqhj$X{aIlR0Q_JIL{O&G5}ZcGUTp0j1kCwA&_qklge%vxzB6h z8z=HE>=$iYqS}W10k20A)wbp9{JwrJ??ps?SC}}{sUCP4TPINx(B1{J<@dZ4pU*9v zP7mgfL5vh>)awW)=ki%3z(sH%lh~vw%4$a@3lF0AU_sMvIAn)y0;B}q5CC%Clp4?K zd0J!$+RQ0gBrfM16((FK>c9Vy2dw=8P8ro%cxu93=;sJPomNgLTx--GSFm6*)^+&2 zDVdtopf-Ti*l-r%*B;qbxE|I@j9RGxwNfri)RnjRUE2ri`jHHfN+lTkSvD{pVKPl}2;M}Xc*>CSLUbH_w0$SIoVH!zG>!fZ(q-SZnZ)hjG@)>g*mfv3f{||o1d#fOX5>Js!Kf>3pgKpg z(g0YRwH4f#XANjGi(wG~R@PGtS9tW2tp_ZPw7)8caAFduOh)HuI(=|4I~QZgfgwQO%Yqp&Y|U$aohlwb0i9q zg6uYF5P;<>RZ=r+hsK)~geDA@6OtrYo<+$EmIO-FR#eh{iX@Gx^V&}luX5Zp<`iLD z%Ts0ro}oMuseUFOL`0Kv`W*)BNDRdzQ8YQVe#*H94YA{CA?)#vrOQz4fbj`I2NTrJ ziDk9Q1yN-vBaSck%(O+pGKXsI5;VDAfSf7dT0LSkEe)9ObCh_F7lK3eafdf?#=VJ+ zpp(us7-fA5FS%mv`@DV4G!rXLTfDHlOk{^;!Y)vBOoQ=rj@KwfwzyrVR+jly_v+m7 z+VoOQ_bblYKj(RE2HYV%wis@RL9Qd>2c!MXh+;H-HEQ*y<&9rspp$}=&df^xk@3WQGO@ULc=Y7 zCf3ZWv@t8&JRn_mt9-J~Aw1<1`2K%}#ifIP;|G|)VLpLu67hq4z{N^bY6kRxL;-sg z2i>OX$B1TK%tOx9zyLCaAqcwsKbndBn1B@r2(0y)Y5|=_QZ=rmWxrm@-}P%2e8VWN z8+3Y@8afN#eckldc_k!K(#AINOd0|K(6_0F=&lr9ttqDXt@mMDj;Qe&@GnNB^C3!lj4!ei z5k<-442Fo=IC>!21G1$_d?(Lpo!D03O|C$v0=!h8>F`;i8wn znCh#7+FK;|&IYr$JT=yjA1wcps{Zy{HC5IvvuD1w#$oBeTX|94e`g-ljjUj+`SHUl z-(ZPvxP04bxIUv9od+15NcEuAMF~LgYFE~ zqjF5(!2DBc@wx=($1|368HA@`{4SyIf=*c(dS5tLh0yzu<@3Uz8odQh6VJDcfG~@s zR_z!wvE(P+ML;|uHFm8O;##~FQ=>-oF(5|69RlJV9)f2QfF5eil%EK%`!0Y~s_aXD z$jaPzHRaC$+aW9_?j8MW(QyMh=Duh z%Rqs#6OdE_NMU$!KE@XR?vr4AXwlOZzWxhAtn>heLA5nMwNwsHc;2?jt+y?%pE>)v zi-xBe+MRaL?DU9Mr?TVw5iv|6> zZ0Z`@UoYr*(|_i+?mlw@v7JvKcHRbb4?1+X(m%%m$&*-1^m9<;mzCHrmPb~CJ=e%1 z%Zn80XC;ZA20~(7{=3YC%WtsMxBLf>G6tb)_t|L^S4LZ`JPZm&VfQ*1!qreaF&(q{1Y{^I%$9QfVXae4Im}~$#@jmGE55AVUHwQR01@aW^ zXH(+%5`9lDF?$n4J$H4SFT&7zP(kMY643WSWhLT|JkA@%?|Z(^Th@R}_FyxefdA-8 z{$FWPzDIa>Ww}fAV|x!j1QPwqmlKoUC3+K#cLSyty$z@m(R+0Iybb6)qF?jtAfng# zwG+{!{CXDAkNLG5(X0GAi|A8+z2EwEj4N88+2LC-K2PYUcBU-{U+~Gd0P%*3j9mZy z-xK^=YoNUb?BJRa`9)UpjTZzMzmuHt%a^=`-sxc#1Jk0n#0gk{OTqqGBI5>SPFOsy zz-?im&}Bex0*a~mwAY{-pUDX|FwnH(q83N!o<`R?l#8#pa?x@*=aS*YXmdeBcX^J3 zq~rOh&@;QwBx%sx4SNtUuHouW2M7RX59G`Hw&R$k2Q@N;CP*@Xh8Qs#LPMZa@*^lkRBbBLJC8 za5{VcRxzQvht`Jiqnnb1&i6jzA1T5F-g2lqQ_5gPTmxE{GFj-=z)B>pkK#(L^ZvcD$$t>*J-RlR<@h55z;wW zBEQm0ibv?oWmh*uJ&rW++Zzl9Djaf{R@5C&L;Cm}`Mq$TNoe%0qd=Yo=iwRped#Vg zboSlxy+!xhW?J!T{&2I4W9XUNk**WQ?jvX8d-!kvK}$m9(f^d# z$nLp$bO60`f5|hzA`N71nXhvxr6uC9j}&2;KUuT z5Ug<*ksZh_c#Bj&M^GO!q;%XRr?^}_RifwaawFz}I}Sp6xMWuyxFDFid{=#HpTI*G z>`b09 zFf3YqFSMhc9wnxg^oPA5>5qDW-X9?|z6NMWiQ|QR-?B56s{PHx3i4&-lT(*3He2hd z>j}0WB>fF6aVgOo(6Qa~D3s4GQjyHde4rptIIdG)mFVs>+}Jf1)qwl%SoE$-RX4G0 z$gRr8?8?P(o6e~SiZoQqqD)y4VC@UyOG}FUCyG_g`S!i=&a>yWWIa|H&siqO@oaJh zY@5UbKJiZlvIrTm9AzHj2rg_bxL0Q7zASm1|rr9i5iToKeX7yINIck2gOZuH(Is-pW^cvl_@A#T(KJ#yI= z`ik2wGvSzKm#!+&Z|o8z@lPv}Sf6}z^RxXXCm5OanDxWxA<^Qp1KGMN<3Ue-@-;8{ z&>szlSmq;c6g;%n+-}D@kUg`3wkbh~EGK;@E0Dfi_tZw*a`M`%Qa2Sn8K6rtf7Nqu zfl(uQ|2p0T)VuRV;78}QuaQ8bC6$7-3FWPn-<#yCV1<5ufMw9pMXX@BT9VZ}dJ#<% z!(wA{Lm-C{&070A!<2GY1Kp7ZYZELgBlFkZJ5viCD-rtF(KBmq^;2I<-R1}uibq^R zp|jfJ{aSXYkD?$p3}@s<*@;HC(;)9^;yG^CANFv?b7dzxTBoU%^i#{X3^-c7(91l{ z&mZ=K=&8TF#Dk^ZdD-$pyc0&`gMg;~yI~mOek1Dm-&D*`@^323!b6)28NS8o`|ur= zDM>8@a%d)VT+}Bn0rNe^e0_F_dVs35o(=&oUfT0D-SW#&Z+Y{N6J#klGbjKbeu;78Zd>6!j z>n_kO-*4XrC)08ZL{628hSf}+!ZQ(XtuB{+1baM%>M|AVF zZ5%|W!O(iBPc$A!M(Y5k&Mz+mWbgg96<5s4r(8~xerc^34qt4~PE$W>5K``}F16ad zm-M5tMxZBEugvi0*Ud#?$HApDb(&J6_hi*r-mI3O)p{%X0t`&Fw^^vHn>q-tfuMY0 z4rNyITzTV|F{R`Dn>v&w2(po&WZ)gCN;Gx=V>;-lRBScP_nZ*usNFZL5J*=s0!Xo9 zhi6X1?{}T5G!~XHTPrNZ2i#Dg>5CjR(q4P?)Z;Qc`dA_%R(!fthA|2O#ggb*D#5Bw z8pJJe*i^4kHI7n`sXx(p;TbCOMeNhP%nH&z;^FpuFU{C#hqp^6DFzu)?{Afq=c~x4 z;j!2=`?haA_8QIRl>!Hp7^VYQQ^x>M>J!~c-HtewslMDezzA zMI^a{q05<_=1|@c^)$+QIMAFx{)x-W7)QW_?;8kP!vZq8*7*)!=Tma*Qm6nDuRYd0 z@MMk0HfH=gCAPG#GNqm>i@#EpVswqr^T22Q9Ok3_;Ch+WPh$;2pwznX6|;Vei|Det z!vnp)$(Q>XHR%IMX@KU3iBF#Lx0aNHzngClpSTW95V|Vg9&9%#PIqY76-7)adZ5t* zjD?tWP;tef7G%nxbEg_e!2SBbl5#Rhw+_e+u_h{dX7mVi(~wk|-2j2?(RoJk?@zgk^1>NEIz%;G%eX_* z@XkSfg_{NbK?FL02X@1QhnINndSN^TRmMJJ>)BqTdrle6$F&`_X!lPEt!ih^5{;+} zYz>&7K6=!mdk*TZ*~=lY0d&XCvM9bJr{JMB!$YZggasMw7}z#!(E7lBB#~eIgzoCm zV_EYO8@NNm#os)JE*&A5XN6q+hkvqTHiSWD0| zgV7v23K`vZt;8$t8q*|2iXTT_$hfx6TrEoyFV)<{T+E{bIGbZ?`(*USki)CxAqTw zL0$m${?IWCxmEAVdB(Q#!Qq7KOB=ai$uw9H@%s*1bLCJ@!5FEzc5;&Q#X*7b2wDc~ z_nkF=7nznyZ!C%4FCkrb*4&I9!h&`kA?^COyv8)HmH+Bg@WU9&7+{D2x>t@PmiPkL zm+MRP28nPQXg-jQZ)~llvE}e)Pbg7c;S+>n5 zCk}w-SyDmU9(``8T*CG-fmdi@B(|Xb|NoyV5J+k%T^jZ^hk=GFow;$0Z!91xKo?Qn zXUO}fG#Y<(fKN-upOCcArs!^U>tg1Oi1}zdp@1=MP2x=~`q>#6nlw3e$cvFA&!!7% z)_uV9VzGdY*z&TsLQxGZOP=7MpN%hwMhyn;lnu1segh{jF;kHU>BMKG2F@~@R(?XC zhtk_x32Ki4r11pUR~X&{2c-L;jj*r;3aTncgSCn3YH2>FGX6-Xh5-SX0?8?tToOI> z!l>^|vkVh0v|7 zmRiWn09`tF$gxw>q+_S-)}JiAB2(Jw+%y3f%3tIQ;d;mBgjA9bB@ ztC}QDibPN<=%|3)c?wX7to*u+GiQ;~eZmFNG__F8R(Lt2&lUGAZDtk44W%6*D`Egy zFBwDgo-s6GlWxFb&GEtdffY{SJ4yKCM*{iBFQ&wkCTi+N5ByUjg(M1Q`k^Vi97&C3 zUJM*GSjfJbf-Z^t0P>Y(VoJgJ+pl`2r}qMVqZcHtIyI)Id`1|#0FDTRjIf{z1%Ds= zqJmS!(C|Wzm@3O1oXn2tGNx3}V=|lLs@v@t#2Y|P1C%W$m;*Rk9+AN8tba>mzrK~H zN>xu=yid59^BT6YqIeQ3%kgyrWx06bD$B(iN6CeUK;9ZM5rP`q^5*LWRhpd)t;cNN zw}Of)zlKJhG4B-ywJw$BtR#ofv#3@kB`G!edE51P6*^n8|8;*_5{mhff_7v8U;!_w zD-8ozVZ<6TD^>{h2A~>{t<0D~gf4J&271ju;rR;ORVT?*lw1ezfT|9?aOiYwO&+Im zP8$=Z`+HAk{!g^$F^`@G;d+LHV5hu3?{h%I7VhB_a-#PRsi98mB%_Wp08ocKenKH8 z=O@{tZSj#&c~o+xkjLOXePn#8heQ)TMHmmU9%E6HN(|`*d2#@|JZ)q?E0UR10UB*= zxR%W9vC5_fN`p;(oGy@a>fp&)_a*@ndl?JAcgR$c=v*N&KMiK=awPqIY?p1as zqNZ?C7fEylG3cqOAm)Wqr~~o1Od%t=0s$tk7WCnnQ{?g)9&`VhQ)*9HuD&^2!g_wC zC6C$0gqA8ux#jT^#o{OOpg=tHrNE$;PN{cIE)w-&p}5lYW1hh1$zkr2tDXm7!+|p) z%BlbAwL@kn=vLc;pv45Pq`%c`aamwt39tah}C7 z!w!|1Lf$DzCHIu-5!0lsfi&4yNF>SmjcvT4l{aE(@}QtbC7~TDsjPuU4mwP4d;6~e6(Ay3I zt%MW|$cLc`Iet$WlX9YT!WFy+qmNIR{p@r5=j-6^!O)SC$HFs=rFns1q3^1!Oq!h!iLg64h?asP1&`*E-$OiiR z$sI^vvQJJFuq6)kJ*au!eadJbndoS+1lS{%0Q(&a_q|~Wu-`LuWC&BpAgUQG=J?#la@kUb>E`!K&q|sNF6q1C0rNGZ+2D#k zxVhq}_WgQf&hxKnY6hc8(XrdS0X!DN>0=iPOEuylN6DY=c(8z*_nzveYN%i{V@5uqULgD1G)`jNS?NY8Qfu{8Oh5O4Bck&hHf z{-BT}1@S5V4!sC2%lFWULx;TZ+O;EG^rh7m$d^V-z*N~OaXk9AWtrgGqSlNWqV9-A zw~#z2XNL4=xS-kWnGn_xi@sja1TVH0)SGo?*Gpg>*W9Wf$q{5fg;d|tKZbb*J_`V( zs4))dbevEdW7r_=A7DPSWXsTO8AM0Ju>m<1A$bnf#&M~lE#6uAUes@b0co}rJg*lM zZ-&@YhaSaji6QmqCB;D84G9xZb{s>G40IQtAU9BkL?NM@tMwG4^8Pd zjImD+0gV{x>>azutR+|63;-}7SAwYu8J<=K@?`LiaD{mRhD^bYAer!oUy@-mgn$R z)weCdxf=EfS6>E9i-gMQ2+%t2sM^Cb60K5rE2V0${7PnC!UsE4?Q!igZDe=@*B>ln z<>|d&WZbe+a{1#R$V3w?#dekbVnK?{yNa6L-B3 z*#E0m$WFjLb)6XN#9SxoI&NX=&WMSNfqo+Ptl$7niBI-SdXKs33RzN_EtioCiSm-W z!88&?#d=q00{csoQCqdG@^UV5)l-6fr)Q zbtfpSny5qgni{Ev&z{ZD6Z2vUn!^*+A6`yDSNH`KMCzqGRjGQAcq&S?0IX1>%R(d1 zS4W8%8>x|Su`5NnW!5_6$`)WRw7!BrkZJ+SmdxS#VH>WP*Zu3BX{)SWo-e6`ATs3%%p`DO90X=tfc zKri*<)X&Rp7wt`*X_~23q%ZZtiZd(FtwgP6T2#-BEGlmo$q&`jXvWJ|r%T0Mv&WiF6v)orGNx+!W;fAJ#j&C7C z3jEr9>@l5oU({${HpM>HM^wwydLdGrV-t^6ta&k`mrN=64!Dln{86>t5J^SH*Xh6b zb!hf@{_~I^?aw+u2$dA#SFC|{-$I8B&9^}Ns<6q+`*S%1<)2UHY*XP2_PS|qu5ALH z3^n8sL57zWe_1bnH|sXu1lpr-g$~$`xbbDU2S1jqK+9sT+`=P1I>IJebKx?b(*^Q=2Q$GMcE(jXcvdE`oZ$?8p zq$6qhs^*H3Oa8`79w}RT9O0n*8>m2^dDcSke(3tojU9$GbdWX(=nT84nt<_= zQ$jDCX4Wsw@#ycJa7bnMPmJ&m8;#$zDUY7Kn&X5;$3WCLzfD=PpVOprzw8sbYQFvCr+gye&%G4# z8+1$cD}q3rW2%#V@?RMZnK%011WfXjfCpU-W#sE~AS1s5AtC^VWC$3N6@VdYF3JO4 zU?ABtFp#hY1`={J4y}R5tqS^)b%1^x%m8?+8_p8Z*fzu=)%KiAeywIT55IwhE-R-lW=GtBAIRQ<4d zYe3C*BFnA_fy+Y3!5d`OoibD$)@FAE?Ue4)b5OQ@W?#__w2rv9b-mQwg^uh#zdjzw zM}_{B>04@bTxkXw3Zh#+Z71wm$_?ExkvMC(=54&^MrBQJLS%! zUsfO=Kv#R}dg0J;HKQLm)7si~!RI3r9WS|S5+BF+IF4@?^ynEg(fw?>1Ng_;atEKU zxtZyv+aO+K+_Lke2vM;~1)X!DtYvX?Okj!pX%O~sRWVl^AsV13!lJdgfn+7Eve2j1 zfkeD(=pw;9pS?z~O|PTPr3vGkOVbv8cC#WVe3bclulCST?Dy2uOZ3xH#i|n0InWUL7;?l9Fw1CtC((jHft*L^RL+x~ z(Y4w5z@pZ?TeA!9jD=D+nVhoywMODXZL4*_`hwFW*xk?x|!|LPf1Pcp810{bh>vPv~=7_x@hm^-mw@;{2_A z$gfecWZnw=#Pp8IK--3RT!?3g;LirdTxBOLS=l)?;nxuj;LkpF#|>spNCS0hJ@j&X z1?m$!0@q9~D%X-utNW&xm4y?`sSF+fEL*Z7W=n4MQ%9r(Iwjv`fT(r>Kx*HpH$dvP zJEIZR1lm|+qWP#W=~c-o;ui%m*lr|hlW3lU%>Vp|Qp&4`S z#cZv{e8os}zq6oWfB?IbSm5B@OQ-zeK?}297WJ&h*h{PmzdwPb1Fz)ePMZd#*D&TN zt&3_lNDZF^?3mG0*@%c2Nz`~LL%;*PDPJ_u>*>6k_ifRr^qvEAwOCex7~!J$beZ$z z%&9$ZHPjuuW)v&h@$q5^qGroRf1hnF(vND|2B zxi3|T>hotZF8>QC<4Yvtfu1s$n}K1lNaca{ip47qjF-zWT>sT=Nb`B+G0zs1?+~vh zm7t}muD&RvRF+zo!spFN$uHY(7W8q+k@c?!$=!72%PI$(aEMLkD1@dE*r|P!YEJI# zIm*OXLceG^tFQ??%uL=2CK-(9@KzfLJm~xFIa3eN2JgNM1*yb8uAKOVHnZVT_aEz-ii_132eMlH?Q4 z>RimbRo-FEl8}f#*e$t%F90ie!V#78V>gd1m31(@}d`u%P*60rC3H0t{QAmQI-8zM!zLxSgM2?$cI<{ku6pZja~&9YTE8 zd^vi?<$Rf=XW`Mwfi7zPoDd`h=8L+Eq{Vj;pmWT-wOAMd9`cOv4|8fnb#;7JtaBkR zH6{wYoa3qzfx~pobil>hbgX{t`F`}kfAHS%RgMgK7R41zKyYicI8CkS?rZNu2z6Mj zd-HL-lN&(k3NGvAsn;GuU(m?<$}4a$ZxGhXxGQmO_d#w+5WhC8nHpAljn7&b!dk(P zIqMMcMohxHVTbt>gB>|p4#vJ!4@zdYH(2EJ5-U#)9-s)K{}SWEs2gsW0PfA{-kb#i zm_(b?i#f!ta3p4WH(###-+5+p-2RJ-rG7+q=D^tk3F;6|Pv$&~@AUt!gy=`)Wd6?( z59XM?nhvdf`74SB{zdl&gU)KHo?uXKN>y{jhK~Q z70YyFUD+A@h}_NdEF_Ea@Kmtb;jK5BF4-HPbF` zF`BUJ;#An*K@)@&Wnw6@;V`k|DD=vGdc2hpe)nj=7TI@>(h}TG@ z`MNG1<{XwPxInaO5m0-T%U~h0TrCV&xQyY-Y7AFaGF;FVfnyST!+0cwzQL$);E@vK zk#2#s0ZLNABOSL>YW<7}UPp{l@8^`d!w;j9DBYWw3Dw|bviNf(HM8d~b$W zeJd&zPjeEAdVuN_Chb$0{9RLcZizx`!E;sz6u0%9TKOb0&lB8q2IMG^ z=okDE1Ajo@&!54+5!WyFf27WiuLc?8Y_YAc2CU^2n@0@zR z3GTxyzV_4K_}Z(~N>+UB=fCH*?;}2StG!m&|DM-=wr1W>y!FH1^VXmH)S4czFwC!^UkIB zbDBZsllH>KmnPHoy5f=Pxb#VZnB=dyekTuS?R~_aT}d^_jbpfN3=3AQMY(XUfazwN zhEff_;|%MrNpCC4vr1jvrD;S>lFaD|Btsh*5U*k=b@fwMrCacZQe23h5vy&taEScQ zp|V)e8^(QcO*{RDKSON>!rpxReJ&Rnc2!7C#N9mA1zlrL* zpMo{W$}Hn51jnC!Wqk&qn$TAw?EgEC+MU zddN|3tB|)mGlLF_%TIHf=`_@-rBj%aJW+U@M1hsL)-)Y>(H~}Hq-=F~p~Ef$Ez}$< z`Gd{fUSv!`SY!w9;!laDdRK}a4aXwV!DFGL(9OL3Djl{T{^?tYg4hqt{05J<$P-fs zdjDlF&ib3Zn87uQ2`2R@%c21AUgO7J8u^~tOB`JzL)v5i8;mwyN3FH>&xhH*`J~>hFc1?Ok<(o{5mfs6gt(%-%m6zl)pnZQTa|I z-;H- zP@Mrzf}*f8|BJ;!B2z;mAY2Tf_XFrW`5s25C7XN)4IB$oBUoX!J3lnRc0X7hAUjX%7Ams=Xu2sXc2GPh->9kP!bi5J~ z`q^50&zWNb1~znBeDh{sXz4okjcbr;_x;xky`m7JYini%JuGxQ!j7MvVO6(v(4cV! zxRWDd(VxA|umAn|8b*;8H2XB&Hm7D;i5xc}0dl-#N4VTBLxHJws;eP{dH%nz^ZTz~ zGbOXnWDDk%CbBP6YB>fg#OoTFC5sRq@t^7P?|V7HZbp&D574tIStt5VYmfpd{Rxn< zHn!}s1)C!0e)&+J`$f(~pYY7`YJ27-`GiaN{#pMwh{*Ecn~O^;O|@!OY4#oqP?ZDBQ%IFlt`zQ9AsvZf zWiwX%*H^9o`d%PdoVz~Zvy<+CaC&vh7CjYO(u5Rk(NsoL-n62wHDFQAJ%3XkA~Vqh z%qqzm$UXnRrP>$GIk!*%DA}@`@Y1cGW!ZuV0Ab5p&$wrg=KN^ZY$D;l_v$vP3ZRJs z$Yz0y1(MP?DK8==`I}ni)vE#pR28~G5Y(p@`%t3X-sFGemU?ynO|t#>HTyG3?%rlu z|C-IRW|IW&KT&ryKbv?aNG1ru`Yk^CL8hzi)ET(ug9v;g(zP>B|$5|UOYih=AkiE6j|tj^)8TcXc^9^_qKep zCvmiW5=2{gg2kr$4)N(TR6oTeUZ*?f{31os=+U|5u^|z@*=D<g(l(aecOH?~K8-L*P9>G4qPeVKT>Tvb@B5szNXQavon&*zKuHx%TZa zdFEqNU0HtU@2PD{R%&~X*Z=S9RP8|OVuu`Z$U%>ANFb)BnaE$K6I^eqAxH4ID2?&u zWv{yj;8{e>k=4|X00A6CVFuuz5tkU@C|39g^RgO4Yg%ILo&b)td5EV7=&l4V7w?~x zt|8*W7-F6w*X*-4eT0&`RcP`IxpLokBIqLmuyKINGvu;;-Yp#T5$SPs@RN7Q)n9+T zf9NAdz`RD7yhE=2hS2|qK4Lll)Nzw{$mQRt2LI4U%*vM;Dyio%Md~#8=AnwGF|B-7 z6&BTCTS?asnbOGwGW8Cb)1n_VWlh&XQ}3WTtLz6%IkSGi)H`6#>H6WPJo63kQ}6I| zp0*!h%DdQ@F!hcw=dJtUrV(xpgqxbfsTt66^^1~_Ly*ZIoWEm65BB16Gg64bL^?)q zW#P{pg=_|mI8cqm$C;Mh6|I(g{hVeF>{4*jn1z~v9C>0sG{`;tc9s=BK)sni%ZTTf zOv0=fRcE?D8Q=#0hD(Q642X3SG9Vg!A27-QH4-moSawGY6!r%JZ=i>c@K;44r(}jJ zhE^7rJTboZ2Z3J@gW${o>T=|Nt_s4Kp_*$n*Qz!>=(ZSe$R!dc&Z9?OSu;=jja5@F zqZ!V4^f21xR!uY!^pPc{Ay$EAzVJ88G04*6@Z1h`LFv#9UPvgZEImZ-xP+VHRYdfOWNJcXUJpBiNOV zq^I9sx?@g^qfZSKT}DJ$-O+4d&N3}79LE(QLTelce%6AB6m*ANfRYNcJaj$LTzMsE ze-9d7Vu5k9Nd}HOjWF!A&@hqA{E%RO6%5coxrA++K<;umYPOvfN`K{%f)`21(Ej8^ zADottYN$R3h?5hBAr7`FqGD;ka-U7=#{9Hk3T)v%wA=6MyCk9!7AH>l_=CLk%W`Mt z6GxstI6iw0*JWa*BX(!_3b`#$JyLQ?DUEH%B^w1N7unLjju~_?~O(qa|eCi`L&M z86Hnkv(}d>SP!107D3A_nVmm+%RazDqCv-z=FR4MR<8Lxy50H@fj%=AegvaWn}*(x zzdd06M{e+oMx6$(Qz?vqShksSp&^qrVRL|Rn5>Y~;sb%)1tiOd$+HImo+WW1q;J6}Zia}*{lP*%ahN(bs1gX3w&sI8@D$>`!cZ)B{%6C~#2 zD|nBTQH~9W$utBHq`tv2AsQ`yeA8Dam)2=e1={(ylfy+Tr6uJ3bh}foUX&F z6konOF*BN5aN}C(l5JL#j5v4v<=oo*cN!5=5Lk4g@k%m&jC((Dwt>A%k>#_2y(N&T z{g&an2b!J0B2bVFe8U24;OAgsW}#Gcb+d?XLQS7@I_6;Xyo87BZhObWkgtDSo2$~v z*2{J1Pt1?QS#j-iU9kga^RSuOJ_It<2NOx{mv@2v1f<3fX0Y?Cjh=ye2lwHJF)7eE zJ^2iDO4sG6qS$eMV)HZA73ZLNtNRvKn)X^{u^vjHV_eR?3)aVDmMDQ)pvIBgg?w(}dJ_S`&49s+M&B zZQk9L0$%7T_vY-2-N+@{{W%kIK!(vuxLwg4F9j*_!dQzQAMMzlaO;i%U%9m@#@I#V z!OCDKuPq`~IVRGZ&U072wZvw8(jzH{`K+H&_*pQnu^oGdkLr{#NHoZ3F}shLGT&(ISb#z=dsTbO+L zfA{@%{Nq~5p$Fv*_tfYgtcpfh$eLO}5^QZr4_4hE(k&V6a$S7;72OT4=RH-q(uCfR0QN~OUsWO%@a>X^|Y9wjVBa;_Ya4uQiS0L4& zprfC(cPoACp@KTMhuozHQ+@vcZUd~@i4dA0ikjnqnMNlRBe|N#^s|N)NEM{x9c3cR zYHDnToSEFblvSd;`SeW2(yIkoj*R=EOY!uG+>Oj$47^k^)qBs2LY7*huLcKNM}h%> z)dAL#{XqED!LNmc0ST)^SPOFl-0{D6#jOY)$ax_l5oNI(EB%|J77y{$Iq~;wtSYj~3IQF;$8OwffR@*3s;M*&;F!`H)L zO0cW*YAX%4r>f6BX;TUtaO$_z4|9}zssMubTpH9Om;?KeLYpw?VPe*79X$yD_-NYn z)pdRge=p~i17YwiMDd3l5^<37CP+qWv)Z%`sMBrF3P1IDG;lqEQE=?^O0*eqbUfNe zB4j+S%gu42<1(`j4&^R6SP72xN#PMykh5J@1K>%jlIv(jaZzaA>t6jSq#CkF%+#t;(3yMY01CPV2l(uH2cF+i!V0e7M8pd9stZXo&)!8znb z`nwzOtmU-mN@05r7vkq0(ADD`gXm<`u6Nn;tczsFm4a{ z?eDCYePyO04k*Dv9T0(;=lZ~z25ATo!a2kdGXaCb9EGR5yTPY2R7e6>tR8kz$w;0# ztDd;HbccI1?ymk}r8cah$}^~q;jFfdeV(b=F~}W;rp-Jdq6#hZ#i{7n&~i<0Dz|O5sfs|^&Z^4SEMnR`j!_a$xUn2&EIZt(AV(-= z4NRj@_S!GcX_X>i_{)XFC%x|n(#H{e%9#mH-a0Zb@efxe-dK`W zCx1T3)hQmsHaC{c^8c7ia2<&X1F8fZ?ajppTgSNEUrwT-lsGB;90W==uk#x^=L%@B z7K_cM%9{<-N5X7zVuuua<`mQ4U;KZ%IEu2us{JU@=}3zlg=#{1wW}d@t)AdgAemip z59;qPS_{SKsv@WY@7tdQB+_q3ft?(RM3y)?eD_L`45m;fB_LRO^eL>RAclvryNKj% z3}Tr8G8x}iyw+(ssOy-7fl(r~EPp~WT`RR+ewW8WFacD7E!NY%$hKwC5~AB<#m_OA zESbHXhMp%Ij{|p6D}htB`(=G8MlCnrmTK}HTrOZV6iy+UcD0935?APJXq!YOixQRP zR%Kgu#J?6;8i)CE2reF-K962ZLf+!0XhIv&v3SpdpIW@cmPEYfj zF6W%fX7QLDJgE^S3e3zDkUuRrxoBN>C>*VdquXM3T#_ULPGs|1v6t!HZB2nLbyCvc z7IZh+|6=k+NsY!s+w8N%e?*u&N!B}>c@^G@%V3Y4x6?Y6zVmJKGO-q79&(z2LKzi( zxQdUY#^Nf0sdS{M`JBet3^#ff+;U2y+uSmO{-Xy!lii57@Ekz`IIY$O$8t*<%hTAj zIQg>6<|AkS+T09dDaz>ey*$_k#BIxk{m~cyqaYdp)p-#T(W6+V-z4>*WAvW7%Z?A( zhK^WB?6J8Mk1C9z#V*ferkq_mg_V4mB)v=%2V_ocdy3vS43Z-e*^p-z93LU`gcHrI zzfvEIRozh&i>V`Vq&5Av3{X`ax(A7Nv{6sji|LB3fHT7R1G{93ybx3{iL&`ZgXzxT z1OB&DZAh-cbU!2zgVrmwHEdRrr#*9Zyi?*QRM^XOP-)xprnnB0R+;_M2PPIKJZW}X zopGQtR|E;phVW!73k=#$>P<;j{k0)-O}ZK~!8Si_2u&M8Q(Dv@zXorMp$A=j`B$&y zZo9Qc4SGZT6xIRgwp(=k9oUdCg||Su>UN*N&TNF6{Ey+GG#0nc2HP|J_Tp>?w=|#o zPT`B(cMIR;ycan$1U(Y(Yw8?JTfm+?s}07;wTI!W&vC4tQS*>WX=Jm@R^Pebo{ufJ z-Z60SHF0g5*PTUOB%DK$fsp%>yFV~2Q%Qg-GyCO@$w*`o7P;~M%HCHTmidp%?>1_G zs=rT)+hM?pgYpefV)8L!-!d!9YNN))nRvBJl~P%fS2g2VPlRd)_CH#U1du!(%R9+q zIV2adBxOxcM~kbP zkZMP&WEwfojZQ`WAUtc)gf91uM*nQbbrN;z!FwVIs_QHCGCv>w>gU1yi>HFWj=2}$ z51j18!c}D{3Gs;V zA7YwW=N!Qf`H<|U8pE`X9__iyOqom{AN=@U(fgCU%Y##Q5o{CFK3M>EzIgz5oGwO~s7RlHOLuMAs3-;*4xTvFU1 zZK*(VpOIhUCql%V0)k=EV>;^~efYQOaRv`@)}aIslQdx27gTP#3ae^_|H+nSWk{@L z>^m!0qSpNLw4g+aA-alg=+V^Sdi6EMC&wx`V?j#|$Ca3ll#2f@MRV?4Eb|GKt5}ZL zrGzJO9DqtIopY1vo z7z1goFVAAKGK|zd&kBv|fQarZt=0UPFA)haxp$G zA?T2c<>WrGh2pM)ZD#AO0pF^{gpn(7*<~_Mtf0KBWR+QYbu2xhKC!*G78Xk7tC1EL zHy`yhye|}#s=`89!JsAoB1-w*uw?}!6$52y92m3>m$9a$#fZY_mPx!+r}{k0K+b2r z*V0)l0nMzg(6L+dxP_AgNd@%0ksa$qb1y@hbaI zzBcss(0hm8U*fpXk;>xzhiD?r_847wX|!IeSe$NfQV{DnOisJWxsKJPQT|RjJEKlm zi9}Zqo(rB}RqC))ghfw$VDYA`3oxAR(7%G|$-|Ztvy2trAYTk@_Me~oncxY!1Ru_H z>X1xuhitI}jDjoqi}=SZlS+FKDyru#881DX35G;A=P#e8xYoKqmB1N8=LC3lPjB1jc%y)gpl|(Xs)Ko2zZAve?$uV)=|jTzS_-FtdRhSE!^PKg3bj2);M$z)qr;x~dcAV<^(+DQlDv%)yam6(4Ayiyi`W4$9I;CLIED zb~7Pe3#F9nW*qGU$T97(V7V#2jEQB9z5MIM+Ky8SR^15zu|)F2O1xHAK8=)f52Ux3 zsu<;QX6O)iM8HiiJg)L90U7}fRLS{}olW75Ob!OXEEml~y9)~pKj2D){aq^zvSQX} zA!_R-BQzRKZqsR z-`G*FS9$@d_22%uPvR>*hxA>Q=iMz?*$?x4`02whpZgTHmarPM%I4Y!)IXy4(WSw6 z3X()1*?GA*81=95vFlNFDd+a{$ zGuSks8;FnBC$oHTi0mmoW--vXj?Zj+%2ZGWpdjy+_J$6cB&1V%QUH6(LjnbZzu_5C zKLID?1H$q+6wc`Qx&Fom1{x|p!>PXu?@(UZQ`0z3cRpYMFMy3l;{bu z(BR(2;*dQL>Gpw&H`YF4)B-|E5tY^PuvvAbX2wMzC+o3_7KNfFslc_`^2J&m z=v3>6E;LW$(q=5jZEMj571u3-UfTum0a{*TP2L?@efC9#RmH?Rwwi3l#@oEFW62Qsmprpo%{|eMxhH|J zbLKo$25?4_nr?^o{sj9;Cy=b z+@l)(m|)h+i~+~DjAs{nPJ|FoGExQjdPf9#%!d)NHp~r^ci1XIw>?zk;yq~tG0hEt z?B<7iyI&p!jGp1{v{(}V=hXAdPuRfFSJI|P7NL`t2`n96G5GE80gvq@LUKU~{LRk< zmh^g?%3+a>m(55_2p$PUy9;*sKTI*TL&b?z=d3kg&sy=(7zlu$#6)E&gYfczPXDr zzXpdNP36zwfJ^w_?+H|^gB`HRK=}2z3x9|TcZVa`YL7?E2)nS8fJ$F{$MuKyn<3XT z$tVB49nrD8d6YrxU^jHFmeCA5M2wTDmn^u&Dkm$^ z1Wa5V+O=XtzC|mk<|dHbpi9_^z?VUh+Ma6ulc4N5e};hMzh?>xm$;*qbO!axf+PHJ z*pwYy7e>mxS)WR<6Zd6y-Etf#&rvL0NovLG zNLdZ{33_zh`(}@vE1EY9d1cm$-zEvzurspf7iXmF zmcd~iVLYx@i}X&8gElFP_iJ$cFopPl0%;Lw9wn`g+6$r~i}1dQ-aeYZ)|Ig9x*E%m z4BQpz^q#P9GO~q0BYZH?)*rmcvacFVzPsg zF~Sva#4rYQU9z6J%TLPAE*I69DHo>15+k^8`X#DmFbPl1V@ArRr@p@)#ZDs+(yHL3 zA;1hK$~_sV3!92PNdq6w!bJm<3im$9T7nd$TI3seYt9|$Xo#vielU?DXgiXyoWy$7LmEb3zKTsg^ zV(s=`g2Ktp-4I!|Pm`UgwE5*FcJLM2*b5~&BFon}+WCI$yQ7-5h|LwxJ)>z9hgK}Q z@)ZZicr8D$D`mPM&sC$pn8a~NBcR6|8^w|gAe}T%!2ITd@#QK{e^F4wvb6S-ax&_mUZhR?Okk)?$3sR%_=}+|)3ESwL8*Xmflcn*F-$I&4p@?GI|TytOU6t1UhT zFK)b>6+K|V_s#O1gSrK2%br`69g1`a8HBrr zM0zlh1WKQNo|EO=`ogHB_?G|JC`KKg*?k3Yxd=ATmeytaBDv460!K&(Rlm&%VtXF>MELHh7c-yV@4$%0hV&O^nL9Z!fkl$_k4Z*Bve-QO*b0enDf{yA0cr6qaGJRgKA{eJ5NsZc?GY>Qqk(3SYG7X&iWalCCVJTF=SMy=SfC<#rU^Q z<3{};H*6VbP>!xgyp+~UbWah_NHt{c#Jj;j&Uuu}1whAgYf#i4lJi?@IXl6dc~qLJoz`r+;8 z%>SN8_rK`US-Ri8N7$=p?#DPXko8)4ryRc3&b|8D6I$UOjg@PPL(@6ZboiQS(?jb3 zHNXlXdwy?_7(-OsYox!Czt6(uJ7%3c@Yx$0fP~FoUZ}B5bu8D1zT9-n2J2^_R#yqo zmfjp0)Ko1x+^;EphTJ?e>6q=EhkR2qQaq`l>DP;DUg6=$bo0(sV)+V*M1PL|PvF6j zeyg&>!)*-zp%VXgRZ;aIVEDhAz;@0=8A&If68H{KI5`eF%!U|M~-cfLrQGxnj zGRJnt8@O;egMT2Lj0|4}#Ny%wQ{Sf@s_DA!XX@M5hK?$sgW@-oBg^ggR+`m?`-fY~ zDL*_O=O3f_PJ2FGl~Of+J_1pn{PssTn+Gm7eK901Dk>4Ir7)K^kI7e8r1}T@fL+r% zOqV@dMR9hj(SJJ)Oc)KWXB^zU2>@hBMzw0z-f&Bpe~j8?ucxcx4^kvC{8x{nL2Zc> zKODIRhF0RMGa1zv(Q#R_t1&_%-eB$Y0XK>iT(RM63uH~XE8vmfI`6hP1%zXH2N}kP*o17|xCHj@WZUS7NoO`d6#U!&HMD- zMiBl~TQ0cjeyI!!;IF`i=i$Cy%E~M~dfka^ zJMiORNcCe2SYpIK0HlX(02m>y1S8%R*;raw z`;rW3KjJm1)RB#Rpvw7LapYC7sQ(ygcYh23bhI4_Pb^6 zO;i@|Emv3vlwN?>{OdDYwwit~-3>2^Vv(KwVHFN$cXcnJT72GZY3{CBy?xpWU+|lK zFh1rk8k8r=*^N4^I$!1<`Q!JqGf>1I3MxBocBvw_Yi%<%7QBw7E+Ja zqVXkinSJ^9L&Uk#&E>uT7Fx_qnhZLWBC`6B+Se#2?biKs&e<0qy-d(Xrr&Q)|I{3> zH%=czYt!FOK4k}!{RQ)_H>{yBsvk}j^DaBG2Ai};Mhq&diqAI+yjau2b|7$fzL4H$bRBG6-RWsilUqDx|QrK7A zoLX{0eRZCvx+gsoRM*#i%!ivysJwHt>H;e`|L=eVXS5hmMcJ=3*sPqBbDH(49b=Yf z9{zy@R56%GMK(DLE`i|YBQpl~uV(>MYFspTnk-S_`*rL(Pgx|no)-!`uML*Zr;Ul& zh!oG@O?)Y;@H#UfNo45ik$gr8n%b*5<*22oDm~muDppw{H17@kxQaGMRid7 zN-I3&a&!A{xVswP`MEaYNhepVWSX|NmQ3ULK$_!#+i7RMo~)6;%k9$UW(c!to+ z89cid?PVoCSLzN#Z3>w;M@9Zjc(8y3Z;yu?-x;_!c)V|yPv%KQcLWR@tI~!|RA@&( z)iA|I^Q&gj3Z2;mwp_lkwk-73Fykg|l%_sp{s-*}9|Q6p75Fv^`FD}YKT8q!*QBF^ znx;!=nk;?Oco`b)yUCrMK4bBX*6L6ecdu)DiBaK;t)^E_y$HERG)x)b4;1hWWst=F ztLX_<1L+dVLXFv70(!*{Bx)s>PNa-?(KN|i-=;3oh!EpaiJ2>km!P75 zq0v_a*=P?;Abnq!D-15}Z`NW_>zs?MbE1(O_g|QJDJ5YNA|x7EPmn9@(oqOaKr|L3 z$w;I|HqXFTv}OI_wvA(zk5dLRozwCtDY;9acGhKDMis%D+4x}ulNBB_h!m`H)fn?> z3gH4&S<$7-qaC)Ifb#KCf|3P(!4Z~M@!h%5wUC8nV|MB*O#)O`GgFNwHYR((OET9v z4N0r_FEf7ZT-nO~u{hj|d?hY*G%+!tt+dbgQHr|_20yBBPT9h|kR5M@v0-lW~;l9a=Har{srKzEX_{gCU zgL*&>#(!T2gSV?II#FR~;6fS|p8j=fKA2n%4f6}CosaIq$-F?MF*ddeM3&A6C?~P& zMss<7w>MU=LEh-G?EMfn$IFEu8kao*3>sWLi{%l2$v-(IN+I#;c*|OS-jJbrWiE{3 zZuZ{7KDu%J>!xbwI2`HY8=IcK0&N~4j!=wHj@H}=sJ#Nrx7fwW^JvMii*C4$V)%Px z!{^H3i&VpJ78`PhHOfa8~W z!+fVYGCij22Vu895QU97LE-9GK@H!=hDY+Rm>}B>q7ML$y!1|4G?^m{A0sTz(mSuV zg2v!*%iP*M#}XIC4TR(!q(NlhRd(v;7&~iN#V!R*cC}>jo>m(pULKt*@_5P}+_lD$ z)fTsS*SE~LbBO87E0`>6jLlD%N|r3=eFU<_CNo?T2XKhZ3nQi{u~|Gbi{a#N%zOk# zm0btvJ54=O!`lDLRLaXxpWv_#+!0Yr$MnUy5c4NuI7+d>G(Stz8 zZ3`I`Y^8?|)@tF8J^|qt%v(3K9~jCo8NkgZ;X>aS_tw30%NZh*Zs}P&|0v^0d^$3(teRkvAxG!ZPUFrw4!5XY`e${KA<*aH2 z{&bU}e_#4;2K2fq`VkKL3x37i6ff$E0kr>HSnR4=&Ro|wpI+gc$4@tpq)1$%<-!?% z%h?sf;&Un;&RTKdG2P5Vppac^+|At^D^ri}rZKFW$UpDPwwrx;jqE$c;-iqDK50ug z8yg~z5s}!#tQ6PSAteAPZH(v3diA5X+{E)tt$uq3jEX+b`{aUfUA(euNXp;NlC6_u zyC_b$#|%+7S&84|ERtgX;$s!1_7-JCb8g#w1O_j7h4f!xSn}+;b?m!&mzzJ!*{R?& z8}WZlE?sQNwd-2>@KRVhxkdaj>&NE&Bc?k~jh=f1Bj2{q)=A!(veG1~WCf6@_(=fyPE zYqx*ZU9FgO=^9Qj2_IoBkE@Pg0!d#@MoowcHzVTh_6aZcoE#|7s9I z`t)tVg@We}jv<}>y)Q}RU=rW&x&(C-3Vj30P!Ih@P@oAEz(7#C3=}UAL0K*VLhDtV zZ8=y9a%(LgSn+`vgP?M818Oid0_pLuNDmA~2lXv&;QlSSYCIe#2xEv%B3Y=1-$p^qtP;%d>Hu#=_UcMc zAlTrV$`T5vi|9fInr!@AOr^~LV;1;YR5l%0Pr^XJ*wyL(d!(BR;(;zupia65t0ET8 z*FiRyIT|e9Rthbw9Q(*(Wd#=)&g^TDGUTW!`PbFS=z6h_$)3h$0j0dh0El;&8Lb;#vi0bx&BJ{q|ag7fS(b}gT+ z>odY26*Y^tH)W4#bf7 zjYR~gfU35{5JV}9AoTmOLwFtVf6e(oRF1oGUe&U}YF}AaEM%~tS>#+1rN-EMFKd&7 zs7a`!xILOtTR~-lI}{R#OQn_&M&^`v*SgGCZnwWe^XcWLUdmdR^uVilEC zcu6GLFFN3r!^cja$~VE;=bhXBaeSr(@0nDf2SD+V?EDN|(365bbmCdiApz1Mb~Fuj zf8r36edKGlewb*CD`dfCm~ae47eLDOV%j{hGD`kuW4DARzclxttmSkmsca0EWRxrv zA!!)>(U_8&O7*KrCGLU5fl33ouq2q4fv3wXDYD(pm1YhD(W-DQo19m(I_QvKnoa=L z#IAd(3ET#Gb-Z3_3t`viE1hxoOMgw$NMDmTq?<*0iSfZxX$W9|&5q3QsDJgMM6igf z4Lk_RZft2<654P4rnQ-Q(QiMmRA|!YKo`@SkrSUp(zH#V{&br+ykF9hy$nw^dpU~h zggJ` zW!)lb(3k-sXn`nDeL|GFw#zo7!hk!c>MFvi$oI$kFS@O zfJd;9aq&&gV43I7jf829!F@Qquz-)i_J6T-LJ#tUm<1thl%Wc#?{*653VX=Rnx-F# z@njz?yxqv1D2K$qNswvPKKn${O??I02P^pQFzAfIFWq(#kdT zsq4)>0t+iK{%UkHY)%bDY26w@j%+~8MYU|%e+#>1nzV|r5Yx1v^qA1B3^^I(%+A#a z(w1mblBu-GIBhaQn@pz-@i!VP9NUj;M@*pTSZ2#6;zLvMoitjoUozqtLo2viwVSZV z!;aV*E8LlA>wE3q%u2VVie=0!6YL}Naonru{`8s^GwA>Rh{%r8GqT*9V{%zTnWrqu z9(80dni<>hqrc4zlGSl7-NPd9kr9-rWK>)p)PZMY0)g z36l}Nk=gQs--akz2Iftc>Cb!ero7`6E7lHJ+bH>HwD|@d)GqS zXDCo}dp)i03>fwlR&yPSO><4V%~Q?zQ#`VK$kTmKtBlG4HXUdASULhsF~k<#qWXx*4BtVACdjdAnTvnP*d7J4gQ#~UkS2vxVe8__xX|- zL!V0=P_zEP1jO?ytUW+FnHv1oe1xEJ{u1Q1;m^-Iw`VIm2%+=?7Qj8jvdQSnz=OSz zO5X8+@AC$_sE=7?>K{QOJZ-3_^NPT12KC)qxfAzLb>p=0VwT@bfH{qs-5JF=E#&ZQ zvek{FmXy#tL*_J50IM!r@gCYc$gmY+LU*2WI!9n=av(w72;*qGd#l^J08Ic{MBch0;h;&A$fw{#5J^wK7?mH_!a#4Un#j$5NuyLsLwla zKctAzpsr{{;>ADmPn-Yv3Tipijc)FIbzeT=7Hzy}U48~Hao4;33swxaaiAO-A!7dw zqilC2ZvPjp?);V1*+u779IA-?*DIx8qXgqpj+9N@-uG{j^@>Y7B3y<-{M^6MllM3FcUK7 zGX82{o6o9flkg4ty%?((JZIRc>0x_N6cJ;#HFQEsT07Zj|GsBg&FM-PS_$1V*lNal zkL|(G2=JeYMwgVjBhxLlu4$dXlL&`^Y})Z}7fWphm>nKQTHp3?9x_u(PYNQWLz6;( zJ&0MwN+T&6$J~Ca3dRzYPoSJ_(;ZhzJhj+jDU)rejS^BHv!qr4Ng0{jcW+847o5&Q zjR*)o9g0_P%t|c0YoGgt(5-2}H>{*+$xjl|DxWTYa9Z)Se?D=g$1(4h^UP(UHQQ44 zosPkb*)7pVAz@G!iu}y-1u*zFF&Ox4kVT`+mC{xxtUI{1t^apwy9sX3ap0s1){?lS zVz|LET+}G~$%@Kh=>*-)WP?FgO4)R)($DwBL=0|bUOSS z0^Bk>NUrB^ILoPJd@LG-e-*y>z!`Q>e8hk8Hm;Z$Z}vO^;SEYZL(Cnw3CPU$xOxgG zuT?_qYj|#(@lrm!=Gw5OI9-)@{ z?SpF-Pr^ql__odhS*@WSj0X;)KA;*>ej779OlHbdW|9ioZZ0t`%e1-jzXZ8Cpd&*&^0ne#OhtD%WQR5sz=7GGQmt0ara6x z*OG^Vr?Iyz0%xja9dBztIA8`{Si@y>KvN&|K_9yK9cQlqLm{~Ne#YF?%UKi-vFcDq z%P0h~&UB`;5QdYDfyKWWU~op@ygWOt?J=;1dxWi>&l56%G z+%WL6U&*W(*=p@ZXM*-5969pbD-^6+O?dJo#v|eQqd^LL z5@L1V6i3=Q5}Y9EBvc%kPVq2u*M2D_d-gnUB?pVt4^f9%wy$^}-OSy<%_B5{%S%&q{=It+J=9NRPXW zN2{%PxQ_WyVUwS=D~XfoXz_2lP%N8{?r0pe4{0wQq;ORC96R934nHZQX|M;S@tSJM zmhDtFVp9X4c)sb>)U-TX^KzSYT=w>Dn|rHrF-PN}lTLenuNsnR-(GELqLgcX0@e{x zDQu31cqa3I=kXtr$Zr7orU*qEk)ntnxYF^k?G8UE*2etFuen2lz{Ge{5Mo83vAH3% zVT>G83rbZHuEBxaQnu_TS@#cuPrH=5)Op#@_lw(M2&4PePi}R_EvVA;@VD+BHQHic z<7*)hRC4NVetw0<SDXMdK+L}j%|((VT-g_6G4uc@ zv26^}v;&zo0>|e-zNGKF7Z}&YxeZlB*%B>i0>p4{RKL7N6g9}NX&5_yD1Iex4!rX` zc-_UC&Z^N9Z+@UQQawS(+aD2*1^5Qv!c!rp(O#Bv4P${G50IdEwQFv62Fh>u7261@ z5O0M(%T<(|Dk|Q($ZT_S7m4f-lAuBr*;|78SS*I#U+k=Qg_U9GMGRJuSdn$OIMj>& z&-zoonf!~EWqu=064s%NR}%CyHoZ(QlT*HZ5VuB+g0nbaIGK&T!@AflyWc zvwE=HEEqAawat2(06#lT26P&6)SxFL`rie&v`;fj!+`d3Q0{U94h`^(EI-i0Cb_te z0Lf`4sde-PCOKB-YtG{a0Kk>CMHY~`+uubNlLRV%*?2L>%z*jy9d%qMO2+3EhS-ot zeyq<#T%HTtKFoG`+(pY_#60rN7VRhvpu!urwH{^UCrGqBa)=iJtB53xVAbG~54Xz& zO<$%fUKGR~d6<}_4&^VMaWrn;_Xf~^hz{90b)6;NJ@GwuSHoeKgI(YdIXxCcQFeHF zUXQZlc_ciYorXI~T;P`U4Ex}PmR9+g!LXeob(l4Zk~`^_D~Q~X==lblnT5t_#We)<~<+ft=g-rea^>o|MR5)-d6 zdWMwiN!U>GvxG0cKmO5>Rb^b#wJx-3#XXW%r>wz?N=ogp!*nYQP`&z)to?}MI1a(0%EKoIiY4WF|&_tSnp~5_AR?R9_DI&J6 zP!$tBsSay@z+1eOxSbod9N%w@pSl|cs`ppWcqiR%oI!akJHh~E4PDZ?=4d$g4cnr> zj|1Eu_gEx3{XBuM=&COPhp| z&sDq5ca8s^eI3Unxvo9^*P`FxeX{c~*bRZ@^_cWy848_v1Z z>VFM~)HoC(vNz0qu-v&aTvPE+ZU$$7{LY)GQh;JOYjfeu!+>wB8f66OPjf=_`Sa= zlHaaGKW0$HEYE|{zY5VL6xvS&(@>F3x=^s<`ePj(c}V16eth}=*KfzK(YlJVG5p`h z#Svh~m;V%LrMpAPPqjWrhhwhP!hLa&x z_UY!0!UaG!(`~LNhjydylpF>OH&=_9R!(clF|AMNLghdfy_9r2Qn-A6!&2f%PGD>c z!viFd2lGR`U*e5!z@-z497GmkVKWo@%Pp~Bq{gO?GaKhN>hJsA;H+SUJ?rLbAd6lf zgX%)?6lGbC^2On5aTqN%9WAF{R%$t*iwUp$dOAHslrool9ufW$TMe3Sz0`WOZp@Y8 z9#$Sfq?D{FP%NzI+=}!JfZy)Yv_YqdX(w|SH0Hpk>rD$jFSFpR+`9nvi3Ju;or5^b z3%(;iWzt0#P+YirDDdbErr^50$-%I|EEtxvZ+YKt80DHF?uq!o-JoZ7SCr4wFLLEosEe(Dm>ubd%FV6gORMeZ} z7)|?XL*|}GcVuvB3=DYbgDDs;F=K`CAKBS>qhKNpQBvROc?yRl_Z7>L^k$!Z)I?HS zDLO%K-(d#K@0Y6q85))gOkJ{eZJvvIzq|adJ+~EiI&q4P#1Pf1=kJV3IH+Ot!;(EjC4MhGEl^mnEa0gf@m7bx>dh2PNmT+l^k6l3{|rWZW2Q{z%Fi=V83^ zkpj@ZaAQsf+_1pIEs1ynYl4v*Ed5#;DUC=%jvm?yD{|6=xYHECr=MEfM-t~8dEWEx z^+Du3X?ygU#I}o7I>m=1Sm%@Fr6DO!hlwt3fqLIPpTgU$=94HRK2Yd7PHlr)aQ3Mf zBD>ivh_XnZ6O&w)R+g;N7_`c%$A<$hAKo4xjq`NW`2AdrH7S=EyS~*!V5^Y^(I^)*c(fP zM@pd#KG^1nzw2Eg4jiksB2}Rd-W5Dfr6%x{Z^RbUkap6 ziLx!Ur_#XQ)cu`PyH-qvy~y?Jx2QJ*ajkCJvA zSY6bVZfa4Ge?P?7+WX&TlY7Kt5chAt#~w;Gu#0mKrdpwO_+HJmPf>Xqd2s^^&3)bY zhY4cUmtgb0h6+~2-Pn=1c`}pF|JjE7eLNENa;*3lBHnR#w_be$)@}f{z9D1GjmPA3 z?}MovzO6eR3--|IM0}r{VDGg3@JG39sj$)4vDue3%1H|uXHC;%ElsZ(E~)g_t=cL4 zGSe>20la6+dK)UMvqwZLDkH#h z4Ms(YvyLQi)sdYaI~xaI^p)hLpjZ!*BC%xr{pxYx`|%n#+YF{v9>-m7~4I(fzWM?g*p*bGXglr?Vl0)k+98Ow*N(c9-b``Fd%n4=}b($C09m z4d{}4JJRCt+Y!6?i97E4p3nR(xCh;aiTCHoelE6kNVuOg5q=ZyyHa}~{NU;wf#m&E< z+An&74yo8{-w9G_4B3nqzsAYF(9Ps_0EMW*Td+m@Urs9g;jJapepopmNlB)vD}-Qa zN7V=p)-tg>J~nVPI*;kLdWiJcC(75M%G1hh(d;QP7a($Xl@+TCi;HO8j1~re(EF7% zt0-)GH;}tBINn`pcm2?Gg!jvJ`kDP@e8A20XTsWZsB>r5q3%#i_uikV4fXF#J~Ys( zvY;}VGHvA`^d%o`h@dW22^m0X_yP-KYac5r+j4q0X897ow6+n?kzu}pTHKZkeOEyH z5O5cp21i>mIWZNV3!ps>!ht->pe3H;0zP8k2wGO&t+-0 z`lPO?*21F=4_UqGT1$a#0N&7`w|6I;)qf{MtVC^k#XuVyYlXbRJr8PGtg;ftadji+ zRn(54zyK2D4g3>JZQGoZOCrpRr@@;7r6G+%ID8){A2*77(P;-?%EU5K(q5R8P9NiH z01@UK5!HM1b%t!d?og7iHVeBNj-C%>PGc7v;IS|_%` z&;-<}&Kcb!_Q6020k3sDqJ`!o=Bfn?vK)PQr*F7md>kKiI7jTn(?Yuzm#bp* z6}P(}xvDrZ*!aqAzvD<7npvcO7!bpiYPJ5G39Z7w%9|znyXaxgHR-hkms$7J zy%a2~lfjAHzBo-p-V5WB+U_%<-2?RSgZE27Wa#Fj(5#ccyO$$qr^$x+@4%>O}5Fk*)})LPIKGY?c7dwC%2p3&Fy9P za{Jl++<`uz^vY|gQ4+#^urE@nJz5T`y2xC#Aa0;=#!ruhO92qIBXI!|)WSZ8N$|eZ zF$&TIBmhh^HXnPTD9{+!_-DyVoCK!_OcG+9rbDLxQD4tfQuHRR z#5>b+$h zGF(SveuHB!K)?{MwWW08rsh)oJbBBe!H1NE+E;5^UMU)3LxW!4yI%!;JAjBy1#!0U za{X?J!?_rv@U`P^Za1SFa%LVI?txE!;-!)gv+y`~Krhr;-3?wqs>#s^qD^Z#;5#TY z4;h-im^zk%W$&{v+!*{Z6U=01Cz4VCL5CX-v>;#i;ZxSH52odnwVN>MCAu-<8sby3cKy&pu_+H0g!ed& zc}zj(o=TfMvLJa+l+42lVjacir$#6N6uV|9WZXC$2LSZ)0%u`P|EYaoBu2q9jF6rr zqH0bI2pDHHNL8u_8QA(OX)sTX&6uQ4f?zcg;f5HNI-_pWDOO}g`@dv`n|)yk zB9=f>J6ZV?`2)^xWEb*4vV2l59BNG=N#2Y#*~XOMI`dLGevxR&gsf@KziCYBm;T6F z`S|pm89opVDT{Yg3bPZCAu<)Br;Ql3Uakm~Za3HPHYXsVAOkSg>D3b*QTs7x##3*w z?!~daoYW`lj%=Brc!s&ly4cJ|r`H(N9X{-R)Q|iMn@mCnNpTVf4!;*i-xr6!3$@gI zkvj{E8t)=$Xhe5POx#hUQ!38F=98&&&Ma0C6?a!69+&y%lLhSjH~EOvd+23AWtFGNI{!o}%>nCd?Dkz%; zLZ*#j&tcfxQ!fPgsNas|(T5$b%i06V6)9z0&3(J6cGNx3I8R0r zp$74)AksE&{LD=@qwwN?DHmLQu!dMD zN3Z%CMM7|Arf3A4F8v9pkaBH|dZqo^p}-nHC}wsOh#sA7ht zl2xWF&lMxN0kW{H2hTz#=UAN#K8+C4OkPeJgBUmSwXq}WIS)3wRCzbbJtINcnv}%T z7&VZ0n4y81$R^cARjE`cnrOh;l(bZcD6_ldGE9b{c~e|4V~M5rtrKTc$Hy%>m`q#LkV&a4xOpU7My5QH zEjmzb5)>5gV>3 zuueiCS=IPao1y5GGz^5YHMA9@12Ax;ngOcHc7JnAjAF!O=*(mu_>xeEQNi^~OAWab zTarIC79H48X#;8lK`LoE-#x+nL zf#!^0)%J3ZAGrBEYzhi1?31DZ6EENA;Z?QuaKu??|Gwd%UGL;mcCw|zSe4m^Wd*ib ztyvpGiPLv1vib$ug=;=Bp-XUyX$IQ|PWdmcc?f*NUEV>0t)xJqS!K4R6%pn zeX(}x6zw0YQ8G*Mc!lvp{KIyA*Jpnpe7yE-*KIHx4}$ZJit`Tt?~7U991G_V=~X+L zpE*dN&h0( zs(HsWC|tb0E1r4gS@E6&Kp-V%COEAnF-q%%0c6T$l$;^B4X|SLN{3<2_bg>s6{oAJ zkZ3zKzKdxx>^Bd~l&PD*P`1e;x74x1Cdf$YXvt!$G~7R>+Kr_y3{CU&hNj*>s#|TI{b<{k zhm=Vg@&p4D-b=02a2;w)uZL5Lbz-Y-grlpQdwI#jM@~%3_y_X~=2J^OS`6*QlssRl z92iU;ou-UA+q4Z6dWA==Xn|I`iYM@@0ws4XpD%lT-7YJUu2^wa*S+D9mg_ASGNMFN zBE0tf+0SvOPB(X}UkTM(O_|-2OcEL$bz#%$>u7jzj=TIAG_}@aKfAYat*^Y$;I>80 zQDv?lILivffM<%LLP?6cg%Bw(sM!%?G`3xgRYME!7gOchZ=CR(@p7(svBdW5mSqN= zMgxm9H17>xn!z^Y6qRI#4GKqN7jwaHzNnYxWUe(5BLc{03zSA^+ufMUn}Eq@?CX%v z!)3fFSpz>CS2!_aC(Bc}yogi*7$VhnhOI^M8Cj{3n27)O11p?BpG z+m>FM9(pi!q7g*Q+BxOFU32bgi=qZe=PP!{AYl^o0Rh}pO| z6tX>}6LgFtnhpSxu7RYx%;{X^eqJGP6msAbZf4?JGK+I@FIANPLOt1^qa7YI8v2l4 z5uqULGA<9?1%eihD{wGu)fWi&l-^w3mo(VMJR8S$Y1S&D~b$hx7)K2|6T8i8$e)LK9 zx~XCs7@4fYryv;jQti!slqVwAU{QMrQ9iVVY}IoN4IoQW-{W>?-tJr;6_WnxdHLt z%1gy(?=^_TaD+Dg?<`yb3+oYv;VRAMJ=gfwyai&HiUG{jT5O4Tu)Li!7i#DQpN}zW zYMi$?1m5N&0e4M>QA0w{ZEzb22;*t0ns@`cq99j49->s*Nz8*vU`izGem7~u+{DO- zD<7D8*<-d=(P>b9nJqpsehA}0r+%5do-4oRg0H{#;(yV49Nw70O6 zyt~&7uGizDj=z(tXpJJ>DD$gYX2}|B+H%X*!B&(O{f4N&1vKW;<@qz!T$`1s$B6oB zmQB)>-~>yRcjfQ&cN5YjCvljX)EKlfItL3?F(fa^Ho7foc!ON~iox-F#+;jpT`nBP96M@cXDAirha$?tK#GL z{P2#_Rw&etczdOF?0m{FlDPTQxhGGU-^ngyfe=k^ zcivCCNIO*f)9II)=@Po2^dzO7Bn?qWnyxg5yF;H9HppwgF&r59jma!>sBKU6OyKRt?eS22+jtoP1BF$)qdC$i`rWh zi4N-TZlmQGL^0h-_1JWtGF>=n=uUo(4W}2f?-0kF4maxQ^lX`A zYupBDmB1pw$r>(xgf&?vH}`ZfyM-{rb*P+cXi}FWTCcEc;*8i{b2h-`^aVDv7z{!C zO_>EKN$-Dn@W`}nnX&$SMDy|E$sGVC2FC>B0aA1bU>O6?#nAjtl$K9m$N7?6?A>XU zFsN{qsQ;KO5{ttp;>$8wC#IL?>5>fYgPEGne|3A2KIP-5k?xgESC)&22@gKvkt*tLZ>Br@S^IA2*) zOh1aNW`OF`dHrMd;eWQ;$q6B^Dws{#Vhgn1&8Wt z@F~nF2L{h;A$t$6^bM+X3Vk|+B7K1@y#$j!Mel6LOGZ{VKD^0MrTs~$3T{M&;SCf< z6({_;_RqPDM5wjGP7lw{O4kG^MKjv9h6AtgNL93bekHYh<20BtbYKZ8lT3PU7->xMo6+qPjd($C3TYn zprMQmPG46^Oe|KWdr+w$mQ}7^2czps#-hQ>Dm|Nr3e-=cCMxvtsXbGy2x)Xpf6Y2< z*? zTo@=B??3MUfolV4-zHK`pDZ$*j3>4#%YjkI*n6W44~#9?HT(MvjNVM8@QtKs1D3K{ z<=@55Rzl#774S2n4tc&+*8aQ~#ZKM>`2^5_dVS+Mt@SV_>p7TCD2Uwi9O?&ILaRC{ zA|&E=YZ95`XR)Hj+UnY^FO%-JN$#P$%CxK~M(@tt9kIRqp|b)*#_}SBzB)$=cc{b- z@DpzN{*wQ~Pj4XG*N-k-R}XaaHsc4ld~(C0l0t|H=A!oX-}D<~|37(lUyP}k!}(M7 zs^l8R32>~LqLdZ#EdRcClceC?EV3P#@Axp>`E|dc#)_lZM~Qkg`C~tMyg6@7WHxj+ zgxLgTQOlnNd8_%U90oZSuiSOVC#{db3vb%61NtKftc~YYCP6EMVg{tDuA0miqBY+g$?7ObZp8srBt!wA%n zPhf!<#GdJBae=R}NWp*gY-cnUgqX5{)ke z-ELC-tzoPvltN#IyVA?J2y$KMc(BjsDO|zU48zj1%zt3lNctmxfj&q|$J!e1tmbmK zVi2w?OGY@;b4;k8M{=j|U=*I$nX_MWG0FYi@LR`~A%WEdB3+;(j~RUwgLdtMOHGyG ztwpcu8|u60iLtSZCUG)L@Nh!ks$M`{Izt7xQSiwpfTF_bYN|N6W}(H>s3i=C<5?x` z4KNpA8YO#b4=0@&T?PiG4QC)^b!ke&r`uY{jc7}G59=K%wy`0TMoL<{4}%EJ9f8s; zYwMlm=WnP2osxB{q0!pMDU<=$Y$-z3%RBuJNqove{*2&tm^)l-3=|8a7$ z*s-}PVPbL-MseMJ>CPoy)k(uodz=cRa|%$pv?x|YB7&7~mwox5P)uowuEmx!jDDHT8^;0Jz?38FU1?P|GTUTf3+;tO=Jc5YJ6jaL(LrL`}@C2(Z?&7iRM zDV)9T-g>9cpgGc+ESg=|NEL6X=;v`v(qww&b^3{fcAPj{*-E2+V>L;1I^L(6Nrz+E zDbz%XR%Dz;ty|`ZN3CEh8v#GpdAjD6lS8EqU#qN9E7HB$kZN0D1h$K!tcThwqkBS8 zNRr?!R9Q%3|6A{{;}e2Ua8_Lb4o*sVS*w(qGDA?3;n)6d&8mgR=1`S1oKosof+Et7 z7=6W;Bpjr(e?SXyhHP)KFA40M`fn^s8J-sLCgChw-Bc0F-Ix2WHBE9u#m`?=QcuqG zhw{dTSszgsCr71u?ph&Rjg(4l0vov$3mN?xooM&4Z7+uCVtkDZBc$6#>)WgCS~mEbtziAv%IRMl)bc7w>qTZy-! zaM#;M8^c9Qi(f-YMiNCVjk^y|!QDa#JA{;r6!B`M44JYFq=^c3((sTMO&%t4Bdv3D zpW~XQx0AN0=8~*vV#;LGg=Zf;RV>n8!5}OE}*ABBoNqAf8=4lv`ui|ttNm04`ya>T0 zjlbrOI3DkZpkp1=k=Egrt(F^`6$0(tZ~`QyyYe#wlsgIfAnTHY+q3^Exg3PFH3Ku&}Ud{J|JCz0L~zM>+cvp$cj z5byIznW|1w>H?Zk(fiH%_%3Ngo4Al@V5e!7GAh!^U#)b77>;dDwF;k2x zJ+`BNQi@X8dK&+{2slX>keiB#t?zI3;BZY2M0`oliVl{EGc^qH8!>W7VX7 z0$&4MmRL%0TVpe081XqMZaU-bo5e{SyQXpysqQYGJW7HpD(8O8B$s0Y#?OGBgp1OZ z*yWE5seMdP`WI?+zlY7_Swxd|ct$*bF;pg>{EBZ;j~mnzy48v{8`>K^L^rlD>cW^Q|rFZL`h(zCE=NP7X9~rD_6+MOBVb zOc_$w-o`?k8OlPNsZ?yDO|a0yYc&KRmVnB<@po-v9GkojcVjM{p^5+3EeH?`i^uN; z4bM<)UcXI%=5$PX0~I%*T7w$OBglTFetZah5@%48`h-H%ALg6*gSCh^1HR*{EiZ#M z*gTEMPODPVa*FtEX<6Ka5m{*s{gqvc1{Eg(TekL;RunknlB1I9kf-&$4T|<~aT~J5 zjHmvfzi8*UjdyIY(t;f_Siw@w`YUM=5$ERcj+lhgFnN$Md7Lvs==m3_uomg(lXe9! zL@1^3^DjUET2gXQpYY_fiA6HrHGaDHknlp@oU@bbh89)ac-Wcc!{X^CdPi~-@4md0 znr$?$J?oiUDjC%I3sKjoP-(8>IoEksQhju@OGp(fJOwG1-(DoQpAxnMTu*5eVMctB zAwaAz-XkS1fSnEy<&%TxzILl=4^bt>xmMQ?rAp#1YGC#yaZ-J_+h5iPeBU)hy(2Ez z?Mx@R$e3w8z!%7?;N14bQI1qIr-tL4>!1ZT0ux^4p!p;eS zte=cfD{CbuJ`-RBQCA1X%LF#a7ADtTeNLxl-OVm1Dde8|+08VkDx^tG;zp&a0dBEF#ZXSc2%BQ%b2d@o%XsG;nXm zORdy*vH%wF3d7GbZWln3M|e3+N>X9YXyTQrWdy;|;$U+hVQezu6JX_2y)j5ZzOz}w zG3%&s4-rrbVb6sji{mHKZr`vqpABxzxWfsg21@kM?g+2I4LVF8&l7FnF%%S`?m|@3a7#>_`$ac&OVH5CF#V7CX zfi0aqR>axr+!hd?HzeKz<}^ueSH6o8Zpj>_YkFr?4rjQSIjtMiZL=-@$v-H6QE#Sc zg_rK_zGs+Cgy0`NL{`pL8v6_v*q6t|grABV8}ER;$sNr)amznhIa83Lez2x-N$@>f zH7UUC!!qqQY?bI(#pJ?2T(m3UxUxSV_=C^-GB*tW#YDi4RBmu^>$}U%2RJz4H-<}Z zVc{4Wej;^CN5I@~m-LXx@Bll;p}R=RIfP^2(`Us_5U5Y_m^FMf@;PWd!Pm}Cj2YaS zhGA&VE>^@W9PI3b_YU>$F6{Ngm6$1CB`rBSVrLiz^&cxn6Fk}O`;*lBZOZxDo=#uZ zX-}4*Pfn?>xGPA-8nvIYIp^?w@QtnivnYeHFlYI=V0$S&qSEx)KlV!HH+qT#ue(s! zqBM(9QIcjk3jd-{)U)8W6Lp;LP9;uQ8G-}4@L_uFo}<)kqprI6z!o^q<`NIjw-&D@ zkaKC}HN|O@692zNVDml7<_SeiPw2wIpUQ**ddu$JoMA@YEDMl@tW#rn@Q(IY_y&H$ zRrGSVFHcBtYoYh1dmxo?=V#9_YWtF>J!HuR=$gzHmu1ZRA#Ml6>G0*ZD0cO-u{&CO zsFN3ia|W0-YDOa+`778mi0N`sS}(dUYil1ON%}jGwa5_kEU=q2-4_-mdNx4iYtAh6 zp*o+OOXEp{8lO#aJI<3@|Daap0}JjwPz*QHfeGl6OKHB{W~Qb&6>0tpc zo=cNYlwL@bU!))Z28@WHY5qh6yp29Ac!d7bCqkhJ9{#enMul!t3G<>7;*A%mhnNC3$C8*@teI37c%4a>n*0qJy_ zqchjKACptV*WBwW$#gfyJq*D4IyzK$CLhPv;2nWtIAQ6%M9Bo%ZI1%e$AMheF`P^G zrII(0OKcb(X{}V((6-E7sz+G&VViZaJLlK+e~1vBL}anbbazh70g2-bRL9Lc603s- z({lIs#TdJg^sr!x(l&`{V<9TgC5wPnccabP9G8a|6K^@d)uZU3;JU-wk?LILn7$#F ze|qdQ3zWw7w-dYGACj97q`%b)l!ZxM1|=~!y9XoE(J`!7mgFTj({(MNv*W4W;qYDHBdEL7r&Xm^@D-gIQ})ak-};V-wXLN+sMMS%NAu`*#`F?l(t z1H{t~u645sXxyjC_T7b*++m@}@#@zTbUctJCf0?d^i zsfKtFCc&Q)Qz1$iOoUmW`obEm46*W|3dIhkN^a5X)p$d}%B<;CU0lj+GG#v0F0*N; z%;VWVz2@rQc%k@c)T$O191O3Nj4Pj{wk1O`!UUHYt?(4jFVBMMwA+CVE8Lg!zTnAE z&&^8Szyuc93k*Z0Z@qv~_Ng3FN)=3#l26;x37f=3@kpm^+7^FXTU)iELAtZ>Jaf9X zb=Ox0wdtPtjUf>Cbf4y1NNh|G#QZfG)*$N{Q{AYRjhJRR6x^|MwNfK_?*4(ArQu58 zDydp(+VmI}fmUM#dd&4Or}|QAU!88tc<%MQq?`SaXxQMEspL-s)b0qkh@|Sr&54;? zWfE_ylXwF#&?i<)8C0<6e2XNi)EhM{g4u8%?0LIKi%(ATDRL8uBpEGqPL~->`C>g% zGUv$P(?mdT=Umq|ky4(F=*`s}L7mOW=A%i>9V?w)D|VNw%39)JbZcsTNPnp%&#eyK zB4!*(3f4ZLn_KXN9=$qG^L5dsZ$HBH4uwAz7(x9wpK&I?fd17G(R4(PF_DlhfFpKt zRPmBgj<~}&6e2B`#C{ueL;+!FM^6p09rrKb@|jp4W^HY%Jf(X8^AN;HvJmO^Xr)>Q zpFs4TPIWpbC>;stkF{mC=zLxM*MhKok~acL`5b{@HjpZI;WPcT_Wqon?0ZN~_Pu== z6&RffpWTNO%K0 z%C#zS*gQ~=|40S**g-LZs0vUq&#>ktqYZGB*WE0`bSpzU{_gr(_O3E9&Biv@9dY^j zOZVTV@7m!Hc_y$s{Ba!U$liVJFyCdK6~e3orEWiAryxqyWvtYV%^MeC5OC;iFLjLx zm0#B|`g$9LS@Z4}f^`fwK;8D)G52FN2OfqHx;1l%IV~G-+Zu1>1J1iN_+71~KU})Q zYvHbI{U2;(`)bn*#3xiF|(@}0)~kDXc-rm*H2!` zj>8WT*f6A!2U9o(Z@Am+yQ6*UPpFFMMu-M?Pve&_0R&{1gZ?8W{{QHuroG@>z19?G zUuez8@@AVC?f`t;E9vm6^=m|*!!nF$5O_n0e)*mt5OaBNf~dW86<*hzl#5KbMW+-o zsw@`=q-(*Hk2Ms;S2Va^sSw)!?hpY2;+1k8WC5}g6a28nF!F8p#DSpkJ36Q<>Mn+)lDtk9 zFOFx27@5ouDR2-i7lZS)gh|#b3REn>uRCg$O)mEftXn6Cj?5>Y8vEj#Pk_DW+G>HI z-_ zNw}qcg}>cg8j9lF)DZ4XkV|S@88tL*26};jfK_HJD~5D`8?GXH&9N#ApX)ppvN*Dh z+!=t;>J5wt(x&N^30v!&2%%TMD&liE^|k3}Rv1U4@)ac|YiRQKo@ph6c~jR+(0Z+1 zmWQtQ`WHW#`1;sLKeS-v0uMvIKe)>iy-ukCJ$FjTl?(Y!_pn)p>9*p`pNmk{!&aOr zKK~(pauOi+EI$;FM|$NXxtAgOCS_y;CSZDe=Gb>#u{it3XOmP$p#} z6;#2732P_b20I9_uMZgciUWJl>Vdvt5nmATi{1wxN_z2YIcfhjg>vH#tmwXwOfSDu z_OM-4Xe6+1mG#XH#^lUhy$iaR!HS7m{1#uBWY`*}mu2MtaS5$uT2;8u!aD1hH0gN1 z$RgQ;#m5;;wcdl~mW}wHOTXUhj!jScI#Veb*uXPt&}2Kdc-JU>*IG7n6aSzd62G0R z+RL9%>jIn1q-$nX&$4Y;P;)0{d2X4q2t;ELWN zBHe7sDO5B!f%ju#vmsM4;E$IL6@w03>oxbI-W?+(tX`m`6ts1lrG};;UM7$6d{(LsJ-D^@<&Ib5P_H% z)I~PnlCBzJxi)faFe{k>(J?5XPmiPfCU`DB$0l`DNXe;Mq=?L&ZhPAUoK3h*hbLn1 z4gLS%!#bpjnp_I|t~qUKwM;j3IQ-)W^4nlCudTi6xE7TegGEkin`h&ZeMIrdG~ksT zgNZA|nNMop&Msf{Fq$J3!#1b`QCa$|j)3fXVmTt}cH-kxkTCAEIBvEf=qqSY^A zDf&7qkvbWzAz2b-wp?OPh%f7U>bD!_C!{D{Q5vE>XcHN9lXZfoLuJ|L%&nmllXy>is*vGJmBnXBpNvW9GR)%07%ea7CA4`Uyd$H(=8F1 zK3X#>Jo}~=9w>P02K#|^^Di*j#ShjOe$c@#@NW4$q=k@gic`MrL|}X&a^2zx610i= zA&L=WSB6wYF@tKFB4Uw%<0`|DtmN1!M^FeA>)3ra5jqPN8Rb4y2kHa2c5c2hS#lu*QN)%|3m@RGdYy2Sat_d3&Y#lO8odn zw6B;#+1wrg6$(~81)ulN`#K6LiBSG8=br~@?*9)qsFYrmc*io1KTrYT1RndD(1$&t zmI0})_`#HWxjpPuVRHa!?cwg5MAADfEBxzX<9!Xr@Bzv_(GhL8KtbVA9+IM+csvkk z4|7N7s)8l5+0nJ%U@1BN8=3qyy+5m-Tad32oFpn@`>iijf zf*I640Uv<@odfApgXPOgg}wRJ>0ENz*0kM?1cOqSPUvrO+i6W(Tgy3nD@Dp|&YKd@ zY9dFmiw}f#fE9iAc1^SpGrj8hoZtfCa`5FjKd$7oEhYr%>-*9pCXt>0Vr<}5UGDM? z-x(CGO=9}M&3UYIOX*HjX;7$LkL)5wHHR~JRBqoEzeUpMyqpt96QxSib4K^1@#i~H z8|2wx?scuSZshAX+xQfo58=Xs zF|5tGVXNbl!B$k8_6A`9tY+U}%;n}1M!4hA+YMDNHG_}Ii+uaD^1tn#-v@&t<}05^ zhDK!j&~*;ajUVS~%8{-@YA?^UymBL-LAU3>eju&KpN!)Ej33t!IBYFmsfT^-Z^jQV zsqFj5>FkMn-x^&0`YSVIiuu>#`WOFv*3h~gqX<#J#lPrDxy_k1wDnGawwAxD_cB*j zKB|^grbV8|b!C^<65(N94=rsK=a#{)dp-GC9k?7)Lji{k{8sGh>|sb<>iqN@T7}AMY>E^nDngrZYR&YW{wzhbs7%Vyt5J z5Y)fKx4mA2-$_aBZ-y%VftMl1Lo5G_>-YtF4MxYdpJq!x&XzK*%$w$I=k27sDMC2YpK4?_&%f5jUtcd3W@Uhc026^zz<$vC}1HVz;-wBs7 z&9S(9+z^#n6AF7qzino2vmpD?4s+2v6RKjR=tW_dZ}8_Cvn`h)A&?nG5XIcI7ft^C zbT~=MZHqDf;Xf}Mt7S0k2Xb0bWBt=5dKii+_ySyX}HLnub0BlCLXW#$$cNIB&&9!KwOY6$0{ zrvuO!y8lr49&?}LH#L2YO^R=KtgOf%xQhHyN9x>}qw}9FD#y9%^oD^_e=d$Pw^6?^&pt4hw;P3T_FanO+J92s`mwq&-Cu3r4Bjpi zKP1GjX^(Tt$H*|+-(3;xu1{{E5ZC5BA_S(15qDK6JdXb>n*^~fz zWTb2_kwVGcP~sDF!yvkFEjy9nUXZFmFk-UPk#?F*r*WcE^EY!Rv7d!D zR9O-YMsdQZm6dq3Qa1E6lgag=45g>yWG4)DTgQnE`9Eub{(0hBR>Y(=h~(AEex?Ls zelSdBaw!ug`O}-C1%)k0D;P0ltLv%coV(sx5c-I`Yt;}@w(x!FzmI*HTGCcx{9a6` z#@?Iv?_BH|;==h{w>llfWoFs2#()()f}64_XSD}uHdKj}&T4OF4T|wzQB3L~SNvu+ z&;zt0?I2QeoCNB+(_yN5;>H&X5TTJw5}AC6!@$#J>#z8H8LZ_cahe_^!8nMpJ6S5% zyhQpkQh^9lcbg`?c;s#nd12;D*IB43qBy#q#Mxq=9QG z&5ZF@Wf95NK|#6nW%;3eRe@7X>NZWZO;8(u461yU)S*LS&1wxKwFVyiR~jf^1@mn} zfUTO%2DN54|C^gFUx`IbKoBk_GNOje!~gThl&|6@{2ue`RtfLHX!?2cFK&XfO#yHmaj+j|IM*+;XJ;T)Nhw&0wvZ%cc` z@Vz6MvRGu)$aD=z=IU$|bs#BgvBaq87Zo9xtNNg-3V~Uf1xEc*RUgD0giR9yh&dQc z7+|I*7>MTpJ*)$Qco7tf4QP`(G)U**J(`CHX(L3I8bHtHfr2*&_VFTE@Y(<~&zfC| zWM#~cPpSj%)L6yR4rMe}XH@KxN32@udUYUYgWnRqlO*DrNecqr8w6W&5Olj%avYrS zh^cO16+A~Wvp`C&YM?4WQ3WXBy+N`iI~`>Y2T8YUMaRhrSGEAf=~Zz6YaDq3hb#hR z&8)#Z^kVTJ%afS}%eqlOgCIvFu|V+NKx`!g(d}Bnu{hz;u{bH3502#tR}joBXz!CM zpcdm4b^FCgMb${D@J4)&T2V^FM*N)*mEK zaRS!XN^r?MxPPU?4c(OtJ6u~C|K+J6y44kcQ&d6@8X=eKCk+FS*gC7RoNirJt;@A! z5Iil=>2z37;${sBd<9Rs0mM0~!h)3d2I*EZNV{DtK2CAA)048e{u&fty9utcv|J5w z!}6*oqV1CxM`;{NJB-Inc3o%1?J6V%7O0wRAge9!ZXx5JL?TR|x&{wQMbudmU#;vW zNt`hEOsQ$=+`m5#)LAyPy?An8hLihVoXGpbFdp9T+daE?|15}9df)BJu4_+x*Pv#@ zxkUpBKR$tgCz{3XL1GPpPKQN2v8VBrS=N9)Mz(;+9#R2giYs;xS|~AZqYo- zX_9SPu-Y#tH&_XfmfqXybvjJ)X9qPt@&W=W>+#g910A!vRvy zp#@@*ist2p`C_i7_*b!*8#`BYhX!K1=g2L!z1Da7I25bWzL$Sz5&y*`(qCIx*6nTn zJOpJAEo(b+@W}&ZdwLrTq&+0+-RZ2>>kcHRdPv91DU5D!5!!a&QT7nq@9E|6g7Sv)epXk$&+;e z$0r8zN|PFC3!^HQJCjRK;Nz14ZJEd(4rqfw#3LLG4UY~uqJW~Je!Iv}TZ3c{EKg1bmsnZFXnuCA`_Rtc#QNP|O1oegm zHY^$%MxMWDI&N;d(-H0MgW$p*TCv7rw1@?WIwrA|6M}IoVcKOAJZJ4RPK**J*YS&C z0gx=RteLfhXI%nHua`nnE7>T{`R8Q2=irZ52@!JL0+v5*#aVV$I~H=SU>`5r0sOTL z!2KnBioCCGH38li>dcXkSvFX=x68h+a$Zc;?j3VoqVz3t|->TPr%d(vP*`Hx`Ae$u7e^2Jn zy%SuSy$dWnp7LIaPDkXuvTkzmB-0W~R~-l4{)uHl(BYm zgNV7VwNx-XK7pEOp=G!dTjT}fMWnl3$gz$)ojAw3(_x~ktDuAhgqW}_q=iJq>iD46^m-Ts~2{vFlJznN=3-ixnf@?4~nS%0t# zH3L-$&&War^@SYi-KVE0=yZZ=ilVhO(3QGLzK|2E6o-1VLN3Zz%-%9}(tkfbtMdr; zXC-D9d|I*|g2!7H=y$t8L9^r{?H5}Q9^c+>KH3sR2loex z_j`Y-u2_5UxF81s=gIcIbIx=#Ysyi^M)@jByJD`1iZQY>Fbe`|1NJ3AztA zH#Z(ShPd<7L?D+){3fsega<6N~Bu05+c(BzmOW+xLGXLhq6$md8)VAU!TJW z&x|^h4H!8R#)XI~mLc|l#Q!sdXtWv6wYdr7qMKyoyI>DpKQ|(7nUJjy@Ht}-4 zAS5U465?WE2u_yCbxjpOP?fl`iea~zs{}DsOIxZL2AYM6km`YIt!kKI8Y+W#Qb=Fn z;*6-|x~S`8-M5Da@9n8bQc~2;JVDL~CHxB(vhNAFM`uC05~M3ptX3{X82Bq#adpM* zN_!&HRE#BDq7#CJ&IFLPE(L1zno63>%FmZ-dKGAIh>(( zZf}G1We^3V!bX*%`j5V48Er-@Z{8nOZ9Zsk(?s;;-ocJh>tRE!T(6~Tsg)o~m54n2 zdPraJL)Uc+h=)g?-n@TRfqdc^bRN~m+dHTz)y46-D!N$|-C=t6^y^3EYZPqkAk_kn zvbIM`YhYowXnpU^q0ybkch{U&Rk&)EO=USL$zWndk61d?S5Jj1Z(BJl>Q2_ zHY;v7=(;PzOs&LGc)gNJiN=&1M%P`DkxBxYx(yu)nmhCZ35409b!W*C-_big(q-#{ zSBzS$r-ppf&O~}}6@5tJsZ7*0i>-noE|HB-Xn*4n}MaBOiwWb%cJ2)CK#P;2`)` zISNjlaHA$Wr^~uPuiEjbDmJ)Q#dmBK(=T?3%h7RXgotSJ(d9QBsAM=-pCCg%jU z*u8nNGgaEeN-A1H@Ckz7=`5BqaHT%mvZ>TYRcb_~MxD+WON~L<<7!8grOv3-Sre@C zurUA_>u;=D1;SN6Pd^Q7r}o>3$YldpA2v+B1INipn@#hyl|LbVNFTt#=$a9y0(fYG zv`}6&c$Q1mYzwOU%Mhck*C~?6EoAD&IVZj^4SNpK1Y93Yu9ckNM zVI#C)^-Zp|!WzAS>#((jw`olIuPw4LvR8$_<6~h3{$z1gI~vC8`&N;I;;)1B#&r4 zki~&qtGk)&OwJ6gNxk_IHGfu&gQcq@CO7vSMXrTCr;m#Xi$dT?dpv`pu5S$0vnA_7 zlebi=qhKQA%=nnE8JbLxC0Aj5_d+R>qMml!ULquaQA`1{0NxAy^927q00Z1&0|?RB zNMBT4KGvsw$T9``6x~+#C=}z=xr_sUrDs_KI{R;LBecnA_XTtZh z!A9U&;PcOlr7$wu9qE;iO(h_)P{wpEWx+Qr9peNr&nj7=`djd~Zzx#9@n=p>RbNvm zLZ$CBkDIz%!fRD1`d^c4LSPHj%HH1cn;H3~@xT@=A(tLJYWOR~0z;i|_~n09iwcPw zO$rtGP6X0PVB`eG;(1j&2XDrtgwf+Wg9Np1=o4o9fGS1_F|NLW;rwiJPX`K8*jZeF zDe)$yKKCBkEi)EC1Sv?%5uHxF?K6~tQagauKF7Biy%5`}9XR^c%rcBTXMA6=yiN2- z&=l?6pHehwkJ&syYcubL?4<%}pFw^wsrtJ8shV7)>%JNnj)V{o4nvJNe`ZaJbL0Ec z!zywl#B*oRH&yrNGkjvmJ?n>Jt3AN|NyjeWbD!3UewG$ec(h^N+;iN~FBgP^2Eg0f zo4`4ObLXh$Km9{h+dt<{Rmq(eqN}nm251)J_ptOw{1ZR|$__2^-YiH?p+l)WER;PN zpihJLU7*GUdM%*OoLOhX(TLbFL~t{8lpVoOC#fiMh4DuNVY!3MJol79av6epBGQ(mKkCr z$>07pI`xvz-3G4zO8Yx!TgJjbh53ziq6iX4;S@$Q_*tIzQN-K5EDb$xRPiZf!6mqM z0s((Uns@y?U_o45cF= zi>jx1)@IFbh=7nxk-tl>4HXf@5zjRqMKs%%o4MP?gJT{LX;d(3Vmg(P{~V*{yASP z65G)WOM71BM|on4f#~ws9^T&eAh)5#B~2Gy9u&ei5lzpDz;|{d@+EQt;*^JuAhvV}@2NvQc!1Libvp=-vEC7eJ3H)Nb&g_qEJ`GKP*VIT; z{7$*P&X#NeSM*$itAX*-vi(}w)1_tCYh^E&mfbMR4%#*Y6@7q36~xZXh-yT$+J3z@ zL*>YG*h_+`a<|>7>zEoEqul-fWN~ymij&C>eY|Jg4+_)zw(AQe*4(Z%YyYa%J{Oll zH{+BQi&FsIW$ur`buDY#tzmFjgYkFz7c=18VyljlztrFN&(tKWVT52>9X9S!SaZ+a z4%t}*$dx^hG@oW!PL0=iUUSR%h(WW1a-fxc4}=qKXY5A+(bmTHRpcB+@PXnBY+*Se z`xXFsv|9SCx@zTc4p9k3I&yo6OaG{KEqHqvP-c637KrDLvc+={SNQK$ZIXARI`eol zGrXfqt)f6D4*MRuWZ4nk0-=g~fPZ+hopjw5w;MSI zTY50(M?<=ph!h?>q z`5>WH^#;4v z$*H`Qkvc_w>NJrfE+C%e_-qu+0h(D*iBwd)z`;ei(6=C-6=g=-W%u%Q5AP3A%>-l< zo&QFb(t(IjZ{^_(x}8ppDHL`QA53~6BbhMQ3j^=M1%Xkr78orLOe>Vk+3D0|r&BC^ z5K&+H+v%});@}@LB~^n^&G5ydTOvx3k?DrcP~YlN%!`a9=tQ@T}a=(y+u{WD(8WPQAMN$G1d1wbL%iw*P(0;_o(Wf z&%4m+l7{EpAla67D%FlsNifV5N(va;YbSCNUrOV(iO>F>aM5?m9)aMA9S_7@8}AQE z8zLIo(Vxt=-p_AYT){6>QW(|6{%Fpl6-9bdh6)rkbVK!(7C>wPx&CU#vZ6b|2qU!d zul0}Mi}_2WZKQ2oSl|J~@FD7SZl)qlgG=drL|g0D`YcB#*7W^D zG(eA%t03~@tA=f&Tx~o#Ai`k#auh_eGzj0!z|G;ia5I+5F_@SS5nt`iW`$GAjp15a zOl>V?Md7de69P0yCZ-)Vu*`Uok7!th7`1qiaRIWc%#;Kq#Yp(8lo zp#A?GLe4xAR#iO34v4YPhyoKBsa`97Wns~2LPYF^9EmJc43#>Nfj`K2XrB+vXr|1J z=(AFup}0-z=gMfD6*R<0&(f4<$398rArI~8EMz);K&g%USx*r1W==nW3r5-Zayy-l zldDSa)i(2S8J+dJ;7fZo)ySs>Nf?<0NSp{#CjphqM35E2QI!#f&WXSRBcSqkR12u2 z5Q74)L=FTIC%1}K&IQ{1@PzGmI+QZNQar&xM-aSELwNHkVi87fR+jo%t|Ak;f~>O5 ztdUDO!6b)ZC;vjWN9p6iN=`NcQzm zPw+_g`LPcZJd!7TVqcUb>Wox`XHB3+2B?t+b%vlu{CEUs2$cmuRe>I40t89=eQ`C#~ zoiCLr^*Nf?a2g-KM)RsnZ7!lVnz7f(3|`}O`k?UaALqC{fCR>Q1QhDLDuelHUSbmx zIIYy8hV5vC_>QI^^BnN_#{1O^4=ia@&;kyBb4#rI3&`SfjACNAcAgJJL_kpU^8~W*OqWn5ZGOexrBnZ#%_cq zU51Poj$YvN9*lSoraT$=TVba&+RAd9^@OL#KGG}icyt0|KCuOy@-y_5;uCv_=|axz zAyTq2=ls;VnRdHKi7r8+!DMQ|w97BGKGH{bfo`|H2(Gb#ZvO<7#5THpIvcJn+Rl<+ z+X9CC8pA>*KSdu+@+ef&D#GpYKo<1x5PosQBA_C_I-Nus_=p^zcM%g1z!(u6A-`B5 zh-H5kpNBc}!vcA}4x?I(88)ID$YGwAFl#NEBf$HMKngU=rZ4NY@-7U`A)UjjUjulJ zHWwO*Ccq2GHnb#KTQX1|CQ@9`j>A8BV@F ze75ISijj9wlNBFN5RyD%G_>D%P7i)N2_#McX=!fGW2JBK?T&+4L9iN+i&KJx4^Ml1 zESAMj>$wwv=6hoMHza_DdSt-uga;7Ysh#qhD<`E|1>^$h?yFL{TbXxjr&=CuQ*;Lf zDa7%{bNWV)KB1`d1cH3Ht(nmUL0#x!7Yc;q9N`3n{TpI5&y)~uuACTli1Otye%6sS zBU21o_Pu1Dp@&+p+R^!aL0etv5h&cyPq;?8%EG9HE0YwKwlFO|?j9~Bb7sIH!x68}1__}6L4i0c8K>H+TdL4a&fGgXJ} zgwB>T3UUfUojw!Byf+wcO^Ty(jHg?W6NpnjB7M%}czojEAKcnJ=(1C7Kq7%ac4{eq+{RbMk zPGxeP^O_R{f`5!yKzVaG=aQ1mR36iYR{LXAi|$Z{-)vs^o?g5uC_9^vr_lN@JHzi5 z$U!|Wq*SWE5K6FKDvO|DQb}#T_!<(; z$xF=oQM2s_sO2%V89CxN<_b{gY;mpA(Ue`Tq$M8Nzj8PGasmx+dvu|Pl*jfC9SR;L zN=)t4Ifz(l|JGY*Zn8b7Esog6xVeIkbUNhgfU(bpLrYTm#G7=XRG7cWohDu;VoaLz zGKHSG=nARhI61L*1}U;HL5^~uQ4VsH4fi=C5m-_NyW-Fv>14+n#bQJ!2^$S!&*MnR zNbCEmqN{i=Rz+R68ssAVjwrOcDJ&264{Qu2BB6;yxdA2X3E5>t#~Hf+nVSDR&vHGE z9Xx&ne*H{IxL<44L$Fc8d^`idD;HO1r*vFK6AR^uaRD4CY_psAp z1rD6jQ9QI}xu-%>I65WrXC_m%69p4eSielfM3UE2;}^%o*6>o6(3czcA0p#ixhUl% zg51RmuE9w2Y~4g$8@YN8OA}POW6{dZ%ScFzf{Ek-h;2q?u--alSt^sAF(!R4oZQ@_B)~-Bu7!Dk8o`~3^vpXG(O3hV58a@%*Y;Er zzLVFT&T3>&q!Pt%a#g2?$R1`&#nD#3(^*xv63JL9`sv445oUC^ZlVQ?#Gr5trR3v+ zd?Ymf5WFW2?M3ea{`MKWkN{Gr#goQ`-z0jD)oUvsV8b41zH+UU28#R}6PnB^NAi0(7vb85=3JiP3(u z6;n}?cdIGok){TDRJjqK8Cl!sd5=9V@8K{`FhlteMqo5GU3J7GUXL*hi<{hf5>HC^^;n= z1&L1V)Jx(p)bU0dLVJ2!8p#HNDsw1%a-=wVO6bR&!ttu2SsYNj?Zi_sT!6+$7 z0GGL$*dz=3DhsyS*UN;&^n~A)POy|9W3IYl%hZzh263m8Zi$>Z^mt&VGEsP7%j2}$ zMVnx7th(KkPKS|xthddgZWj{XM=y(R7ns+1xbfieV~cajjj}yg(dk!PCX_7RR?b>K zcgBRvWLbVEjqVnwtg0-kqd4CxCKrlq^srXAcGfM++E(3lXRYt(ZW2D8$KP3ieKOqQgC>NWN)Y3kd9 z$6GwYDCHX=tuM(=2MZfZ zU*xOaa8fksgN(e`m!Chqc^*%uaYQ~`2BE#8vPRmIbLPv28ikulku^!nJV(hNMla>w zC-Q3L+y35brKX=WcRkomC{hfXct44$tzGk=i`cGW7a^T#O*NEe#~17DwAZ2QeCibr z5GiSUDTaLDfq=tf$5RYjvAunCWD~I3n;R2q=paOsu)yQ!1wkp8NNIb1X@Rs;8To8K zWP!EK@JZ$4@mdNWk;(=3H5AibO1=u1oYkRU<|$;0A*Ld==0X#9^Ar0#r0guRiBV7D zaU#>yt(`~3TE)4~lo*Dx)7e+(ipp+0>0tJ9;Y%Mplz&yML^!Jl@V-bEbG~`MjnEpo z*xkq$I>{?W!y_}3PU9%m>I{X`Fa&91-je6W#X`#VeZc}ZtBl569 z$*c`P$aX`@X&cpYEnFCT_r@1aRq^wuJs5s*ZoG4+z!K2+H~)=rXW;#Ib`o@_kZLCk zKjl^RFsBUTmwW@H>7STXK~$fF4nVHbL|2npBr0_OPX6IqcPfQSNzYkvb&j{Ey;4z^ zAFDw$W<8*s{N^jsUAG2`XY6$daTCvU&dx*h?M}NcP^Kl}q}B{IO$~0t=gC~W(9m&F zGv?~Rp;kY8EQrP^;WR{0h3tK({ke4@RW4}kC;YzspZDQ?(vr^49lHZ*dj}}Mcey)p z>3*(p{usD9#VNXpWb>08@BW&S(7Q;A+ppNLCwa!IRh{eTjtrxe@xO6^xr2x~rL_qj$a43l#oQ)3T}_%Zccs(14p|f!`ixaA zs}6Wp#rM_9<2@I1^F!&&-z2<$zt@_ndU^8YWgq`@$w#%dC6|1C`gHkM{9LV?z2uHO z7m~dY3ia++D5v6a`LA$IsZ?+97LZF3ngS@E%1bjLl$Da4JD|KGd@A8n!mkH&F|bCI zrbnu0A6Lktx{a)p1&1yw_4_AGML?V=d6gELvw8`PdnTpC+}pCcAa$2U^>_244*i78 ze1idc`bA{TUR$sBYT0~3x#dZAp-2{=QY1_CjslNk^d4H3Gxi&sR)B#DAr}M5Q&8N4 zh*!>QE7UB;WIaoG!%;*)S|z=Z9l&9pkAz}*Ff-RKodkAp7Xl-JgLB_?`q6a9=_c@KXiv`D+PD(5@&wmmp!H zGfY|#1nu`2glq@P>VhI@n){F?0l5EISy@?eA!GqQxIjr$;-F76idZ^s(+W&091$Vu6Cz+Xrhwhf~DkAEuS}Sf>1ngHpxw>#9f3@s~Lmc}fYX*mlut~*m6zjyH=1OYFVQ0ojN;+BQ z)ZUU`2YKRh3Lq)1>#O}iVSyHO!+@Z4=jEk&XDBmvIw6w*oCbITGHctlF_h6g{-17_ zMP2uQp18MmYaQC=-Orx1vUAGR|`&S^ZJKCR3+FEAJ z%FBh^PXc4gRJj1-9`O6UOwfn2(@ya z#*y3A)XjxUns&!sb!UtQJi`AEXd;yQKQDr7My%>O{Dd;mU#%nx7~0E#m+gK5doyP0M~{lPTM3g;dz zP2mhrX@*-z;X@J+Lpjk?fE^%zjcOQJeH^;3^ACNRSk7YIad0Uk4Z-=xKWH+FF#IkP z0Sj3azyp|CIB@^?huaaeQU!yNdFOzukEDJ(kCUcnY229<59+W}ZrC(FkC0h)NE1F*G69KzdFas6la}6P; zuNKiENk~?982Q)p022NtUtrGzh}Qae2@Ma~*8t=;?k0O|If_D^sw0&a;2wS)S=|Ui z_9?*Vr@t;R>JRX3ZS>6I`KqNd7Q731Ea7L+V!*>x-GK;$-}4NU+Uiw<^(uhXKHQ9* z1f&z)e;(ZSDF2#OiB*E{2P|Xf)JcV$B_>%zh0NOX06ZRnPwPdX$J~fGMOqsWaSz?O z@Z79MyN(tP_S?q6mRnUk{!Wm))5!mzK__G)kZX})%^tCzgx*?&Xh}HIrl{{tiwpsXK#gDOA_hV0)0?3F`~~S;9!PE%Xr!(Lv|-He`VS z_kW?|Q2n+KBxs%h*TGLZETXy;@tjRrg1rb}KR=PQ=9)N+8l0F5ijnpO3t1{4?)I@Q zES=UEz`VT2)4_ZzAZNR+YqTimjaM(Zjfd2(^S7 zsen}7b=pV8_w)Qsa3-24MJSAf?E8b_WJ7fRN0D*9F636qsEL72((O|UOG1XSS=Nxr z3-9YSITrl4gj3Nhfs6&v1-;Jw`zVTIni*Wr)XXEKpiWgLtrhC)LXWA?{tPbbEkyFt zI0_|~6=9`Mvl(0qexzV1__e@0dVaMotojs@3UHXO`%90qBrw~2e>|ut@Sc}t_0h?HsLgIxW?^d$9zgG5--iljGQAF#?C$rUpbgO z99}m=(lD^1uJQu^ydpgIvG#$k{kd(;-H4aio_OW#gLvz_5g^{-Uyrh^_MK<6KZl<4 zg?5H+>ACZoo`&BY%qs(5i_v-|a@F>`W^6C8mC)G-@7j?de&S<_e|GTCGiM)73(ZwW z&Q}4$o;EHt7Ws)oA@*=Mzc@by2C0DfR{Xkh-s!zve)>`T8jf(Jj*7>3ddR-F57{na zOT#4gomZk7C$WxLde=C1kDRx}+v*|v=)4hRv1smH0YDdikMMsZ{NZ;8arx0)tHPtG zhx7h{nu3pl-weOvZ@Bj-ym$}Ra;{m%{Fww5H?)aFJ|k8? z-JL-c^BWP+5+CBoK6Ccr(AkG0XCFR0`|!othd0Qgyy5pGB=K_LC5ZEz=3g#;UV8-)>|2@puNOcc6ZG z2kO^%pf^O%2Ea24~n3O@0x8274v*<7G-e z@eYpy%sxJRhVtkeJt!~k82y7g*4xD$L%356`uQDz_U{HOjGH* zg$C#YCMKt^!c&;wt@_Z(;z3>0Qj+&(TE`U+G)&9Q1{=aJ5+QHs%@o z4elgU(tmdC!snR1!RsP^gBwI=!7x~Sp9Z0fyv5f#sOx78AK2@dgfaM@gU8TM7v8`J z_7c4)zQuoNm;Vv-C3$OW+J7wmF}_QTn7_yYJxElpfYADw1qrvD=5IRlXl06_^xBYO zDe%ZG#^;iy7V`+AZ$cb#pCI~k5aZk1Q4OdrIK_f6;lAxv%7-6$@hMEumvCmmy_kQD zL8e5TdNWYz9}aAkE~T-baZE38Q~bj1@m6n&B+VJVAZ?dHRSHQ8K8xrQi?^5?@iV3N z^t4Yx`iEl8kfD;WNN~GNyEUKykl;?bBKuzD4?kj@Yvb z%(_+PjX_0tw~u=8ZXZJ42Y*2Y9%2ec!&Fj3+gyJLOpr=F08O(qF3u@a;AkaNoWg0!ze>1F zdC2b7k04asp=h*)d1F--5UT&!HDOm@{@4}19kzTD`tXiZqfLqdPaxAa%qaKPlwi)8(4S-E&E zK%pr%R<0nUD0&grS8kaLD0iU6QXZL3eeymWalC|5D@P4*T0t7u6{O!{NN;baHHd~L z70&o*ZZA-At^b@@aF(APhiSVv(l5OSv|#v!AExkpdFl5-QTpn?Mg{&mDS%|vZa+%- zRSLT;d*0j|+DM>}AJkEn!LXv5@urOtL`sfjqPK=Jwnoi_@Qfu(wuJUyA|H~LJ)yLg z+uRj1c+oVziY zzHz$uQ@mvh;l4<78uKCL{U?ul@UpRjIm^Ddc4#h$LcdA*wS>Kt3kjc^>aK?DO$rYH zbI-9={nYQF-{S3U{*9ij8T@V>@3kT=DO2|!-1e+BrtbH9z1yDE#W(b82G3Id_~8S& z313pa(cj#7C?CS>2BN<>iPH9H=T$cTmnk~-=To>asbA8+m|FXg;v_yx|6(lK+PJ-4 z{gmPz)yZ1lNvtmWjZbU8Q}!XXD*vWuOm>5=ux7>muTgowRuQ)Fbfdpb?K?ps{!~Wt z?3m1Q11dh}2}oX?X~ z1JmzqJbYZ1dDsZ1ztMZ}u)^n2Goa0f8wS#kn+rYO=s$d17JAay+-JT?A2 zW9`DVEN|mML+cmVP1yqr-lxo6bFJ##y7`WtEek#`A$#j4+56hW1&~MIoGVO=?{g6^ zD2nqd=7KUz2}**kt)b;*5qX9`Np3Kobxxw(4<-Y}eTYo|ml6$=?-$iI2XhunO*iQz ze)Z>!<5$EY!K({rGkBNsS1J5V+f^R&FNVxjmvkL(8|X}-c}UqVJD!_u^UoJ}#)d?M zY5I=bg&B1kX4x@j8ES1HJSB7~8-ZP>7Cd7;>%=mO9;GejK&B;hLm7kxnDI4Nh)hqtPaH z1Sy5u7*fe_fG}~_5%_d5Z4bw(ukXVwIK3)TXkgyRo2(y`&@GUJ}Z^~xGd^6H(VQW)(OPd;$x2S~7nE{V2$a|;zbjfnpi&;I_P;HsMqMrhQY^W z^#oGPI#(ust>c5)4t?{D--hD^aU4wYFK3ik8&hq2aLOmPPl=ReQy&m>jr19e;S?^5 zJ|K2e^ge6oLv^bhBpy#C1YWZ%*L*acZWF)n3D^^cz*8QMDBs2uU59e7ESSHLB_8_F zS@VQr#3CHJjmtE=hE0Z%Zov-ijGPIJ_=l5mAbb4DlqCqbJ$^?0JdYas^T0*F0GJuSI} z9#;x7kH-r9W^_C{?mV`SC(*Y6@WNs`c19lmC}G+@h^^2hgdU3sN+s8uWU0(Y9kLJP zVp;N42FaHDrrN}8G9Kd=m0pOHs?jNYZMnN4cOp{hUpavijz$REb2@3XOtjER$&xL<-F( z^e&N}3@&AolJ(_#oSR=n6vc|tHl;3j9-3u*Hu1n6V>S=WulM=Wo(n#G+1GVKF_d9h z`~&-<%{JP0GtN6ALYXMt*qLY5ad6>#-n}Hwl-#SqYiwdSw+x*&t~t5wsw9)HL$6)gbtvE(_YfTEz4y7< z^Z(wwgQ;YoTZP$U*Y&^sjr>LbVNd_qd_=#V^2hxr>s06QdM_{iczvV# zwNd_hQv7=SCb{G3e_ghs_33DgWeieX#gFU1$I*-EWe0^cZ+{qJJwZJ`X zObHePRmM`yojm8LATgQK+z``Pm$Z*B+GcGvWV>|USoS@PL-N`UYcIS)#d@)1c?yqY z1uD1Ge#~5W@LL}m=N{i7+U(xA;LcodS6y(q3-14Rf&TmZZx`Huy5Rm#7yjw~^Te`X zzY+40M-iPNJ3u7PJ!=NX~cez!0HpKw-O7uAK$2V5A((*?t5K-zg$#0}gt;s*5K))e&e_O@>85cw@}0olj{ zt!*sNXx}`8X(S1NUvSX5MLg9(i}-26M{vd?7;|qR`4kkNK*TXu^SIh}MMt!u<4fyy zX>gtO*)uOUqR}0N7QFVBdlr56mOBJ}t$)|Czs6E8y8qf^%II}ue*5gv<&5xZ34Qp2 z)7v=lM=5;uKqqOVK(IS{^z)7$z4gf7pg)7}e>AiOXHE8X6bhp1nz5B3OBPlB7^h`9 z^l81Y{TCK@x<#G#SVVg)vWMM1S>5g7TfCXViEy(y^;(fFK6mGM4*!ue;P)B}ou}+$UO+Ms(L`>n|aX^ix@pY#U4 znJ!iJxT?SZc4uuRFn_Go4$1HeTh)*{oz)>j!}iS5GJ*>4wzQQ5KV*9zyn&^5=lt5R zMr(7F8^p@q_vS|B70#UuwdpJharr))K@#%z%g!%FvOo({!$~Cf?MMw5k-{G&1|KQE z;R`Y=3m1+c9><%v6!~nRBglyNVafxTZ~_U=v6uinQSQ2t_#ys3kH1YzU7atIgid;adb1>U+tNDdG6JM4}3$eCht zTJI6EiEtKjFKy)>R@_`V`bY8O@5IG8*PK#nI~-D;iAD!SOYfBt_PUs~ni?)kQP)<~ZUk)44 z8aD9pD2kr5xv0_o8T<;Hvsnyha2WDS1z+1;@9$x14`fi`dDtj0d#{P)f6!XuUV+OM zQ*gyX_7&5$gzPhX4qzCvkX?sB+NBU)Vn!lM9|<^N5oS9%#GHV%T(6qj8$P4MAo+S_)wixfm17d*?s%m_ze?vlM5w);9f zFnt|fgv%p94#GPEWx_l#YbPusV#3yp8T&13o_ri<@IGu)p582Q`4`CL8(EN)X$F;a z&SF}fdV^!B_JhdK8p_h1BTGXZ#l_oxO>4IwM78}ZXYjI#EG+=T-^F{~p!sc8KjK|I zxgm)*6YT_#_L4ui!^8w0UQ;#jN}HiaHbal>nx5?s{QQd%LI7vnw|y9M-nrXh`lr9)vu$eaLuhOEN}F zO35V$)4c42Psx#YYI`u|QyPuw{G{O!=wV4QO#)=5q1Vp`W}>aY*1tDtQqrvXG;7y{ z;&qAQHKBM-C|(na*URTiO7N|DyyhotZ<$Q4O@YO<2%Hj84zHl$6*RnphF8?_iSmKc z7*J|gJWce#oL;WqTQ>+rT(%RFnW?W|Rx~4j6lM|^boio|@CMWB4=6A43W2WmGcT8y zsxFq$MN7jA60}c$w18VYJM~p-h9=zCH-D2&RlAwjy1E+M!t4uK9n8_TRJ+a`RP{(1uH2iKmMrsD4 zJyEHS$T&14Adcp9XY$%Z-{0d`v8I*6@1IvaT<`gwmFs#x$PZ1vCj z9h&}G#SEk7`X(wf#Wm)SuG7Gzb``XdHTHGb9(uxZvtVS+8?Hn4C8Q6F2JGDUm)o#E zhx|N&uZ;}Y=a4Nb@kj}a;a#4;7}zrBnf6O7HkNv&&EQpt(K_$$(j}mLhz?<1>U^aw z?)^qLv3*R7vFYrCFVJJm$NUV~v7fE_s1bV?g8ORs$i>)WI1061>aCUx_mBmDDWJGD z=8dJr#P8wqByNQW?&eoHuE<}Wl)m)cx_`R+UaG4&xzH&%-NF&QK{ojc@0qfPUu^H3 zLOtBBe%eD^8BM^PkGlIgl0}S2aW}`xD2XR4Vj4L5Zi_hufx9olQJhSqzmg?kUS>9f zZ!HH(zd@9}h}rj$DXYyawHIr%)Q@n9uP{@&v%|-bUnsa2F3p4U5hb=C`JZbvQNsoK zI zjF#V0w|j-7hKBObF&ldt$>iUJS_5{L0}Ha{QkrOM>}Gg@c8QqU)L$$#CK5BK1O*v2L|jT+oDk{i3t6h^|fAj#LV= zCF?EBpfTK8X-8dBzh?44N~x zGY5OzR9L)s;=iW0oxk1%P>SV{Pv{ z=Xp|b$Zw+9&03jbjrY(w%J6E=E33LI=8#2j73V=ZF+`{p57~95jdYVGlFkxkS7Ihf;cNZoEYb*aA+h8^Q@544ke@JF zXDVB8i0U|$3Q~QE0P5w}`k*s$>$sc9YQCPsXlg-Txo9Q4mzeJPAj>bhAkV|BIeb7F zNFU`g)FB=7gVW32y?*I!flr%8Z&wpDxo*{8dTkGjI3)7X&$nfvQR&_5o7z}OlZFRQd+ z>ptkr3YY@=CA`nrD8SkHl)u4W^a;Lgk@Rx<1*xS zyMKS);H(u76kWb|D2tU^&3 zBZ~jGnBbA0TpN&ez36S+_h)F z>mnznm&CSozGN(8&oVthddBn9<8!t|olpnZE8z8=s4Gx`@H*2O<^HnQH~(V&HCmlt z{}e$3UdBG_BQ0dVvocHFtBj8&yldRShwM3%a5IdYP@u5Wv$l^MonwB*1^q(W_!((W zzlUk8L-s0zubD0mC7o)_A(Ul0k=B(pgPqiZ*Labu_g1^MVA@#R&DIQlW{qx7Mb?Fq zdw%3B=Jfg<62PRk0vAYPiyr33y<&;& zUE#`(QMSiKx3fCao&EvHbUQ8~7HoHn_dI)7b1*e~;MGTizzk!@qoFO7a@uu$v7p#= z{AwbK91{9CZvgws85spNOFwM_``(!v1s~?@yixF7#)=NdvqfhqoK?-mzs+*r*YE8L zqMxXE{hVL5mo3W^=RHS7?K>?00n;sS=nHs$Wc+;sA6t}iKQg%S#+3P((iGEqcQ51n z2}qxRNZ1<`i~QyzQX+Hm74Bv5F=OuW+VS6ipWMut^;dV>hQI&*`|tm}cl$s7{yRN! zbLCK@`+@gvhEY}MxU8MRAo5$BzBBY|{RzrPZ=atink4?8?XI3fN_1^P+OR4;`Qk!& z;ot!j`8opkQ{7geCvPqn%3(R*m~}cL9u8;DCEbwsnVdrkGLzWD7r29p)X;cl75n#X zzJCirzudpIKSdhZjIUD|*A)UnZk-9&cd700`QXof5YktqB zP2Jdc(uG~Z>u;}tQ9=L~~Mt!2A0@LOEMw%waTQv!5L<4bg9l6cwH z%%yP?{!%sA8|Rz_ei`u*`%9fpl|A%GktN=dCQI~5l{I{>by;sQJ4x65ANLDA_mp2} zY-9}@Y$e(^K-NbO501Ca#299MCY%wEIyNsN%R!!%AoYYu5^@xO&IH_eZ=D@7=Ab)U z&KS0>nu{4cODK(3O;NBH|Ecq|&vs*o(L)Qh$}eN)W;&OuOS)!wmavd{*>V){= zL)O^hpnk*)t=6!t+{0LvgI3~?Nyz7&XAjvb&4s*&oBl2r7>;#jFggx>@3r_@m}+go#OA*nuk)$F(26?*v=e8@tuCyi7_;; z?_6Um5>{L_o#Q~Kcpp|!S;&MB*QghzQI-7H zw!sw=&YrX&l_!?vq;h`ZX->bBY!u4cL6YN_N$#_s8I16{XYxgml4q3Rh5nsiki`Ni z5q@7HT%+4Lcpt$hegWRI;EMY>biV=mbLVQdxOghyPYC!?6IpwO9AtF}YPvRRnlSOw zp}><7+StWQbTXc1<&zV#i|QL*#6ZW)C}XRAtG=hFEzlR7W)E2gQ<=NV=6ncx4Vhlc zhS?rTYIXnYoV&ztfh5CAoN2XDM;V(+9Jd_-Z(_XEWh!$mD#nfYcVtiXeAB-5GDuSO zLR=q=zDjxF!eMAZ=(nZrfu!Nk$n(j$`*GxpYq#!7?;d8gxBdLBP+4NWU$}~`N5nE{ zXfK&_+AkWe)8(S!dHa0X^31ptGoMZG+DXb`rtwt*$2W{w>TB0*@O%~&D(lQ&?$qnI z%|#LNIr5iZwE3v%qRpwFy8=(zz(2~tpZUwfz{mdb1eT}%(u52z1?bpAxJ0YlsZTbB z+=}+*g?nOQlA>$>k8ZdAFJBjb?UMv5=(_m0{kr%N$8flSR286fsTe^s3e;U^GIpo{ z?V_MiZ>2NZDvW;AD#>3yzfy46$Ym1ne_IuwOTdkYF zx_Gd>Xr=gz3v$<|>j{s1c!k{gL-`<2!E@mArWoTR+?V|a4|-PjQUA$4sDw=KnCA#a@{r=|0Z4Xt_T^4Wkd)ph%+7k;CC3K^| z9`uSho*VJq5o?U~H#h!@vDzl$(8sHkQW!pTye><(`1*F=>7hH;MeCY5@bUIp|Nram zOSIcYvMhgvuF_-&Qb8su$+8U>n@5r@Th_kIn$AP)i-b*BD8WwlZ_EeHkIi{60y{v` z&aAFFQ;P*65P?7}FW!0|(`F9juVAXvx_w9vBD%4~hB&WAw}&iC+2VR5eG{hzEj5cD z-5wULqxnVaGW^%N|5V419oBaBsJ5#&YP))~wyU>lyZUu)S8vyL^_$wRep}zv@9Ida zqhVv0k1%dUTcGm($9OpL#Dtt_RR7Qa98rks0%+=AMw4eW>4+@T;DST)jGak<{vptV z1uuhWv3-r|eHoH=(r+v@3EMwf(Eb6mFAXK|Y-Ea=73MtKxeO;>px;(px5_qvSd1OA z8mRRUGCA*dDEN*v3M>4ZMh{WH0_dEO;rvLW8}u1od%}xqWe#~P>RgrY?-cjN^8MqE z`xnLiL3V!=6l2+a#3U{LGp9G&cvqvJf!Dcr=%qt%G=W}sj$U@u@5PwD4bN^iOSg*; zZ|-&yVeFtr_cSKz&Dh-= z?xIviGAo_@4pB7VgF!OT2l7$o4W(6{x&)Um#mM-BPuhwZQ4Tv6$R~|Tau7JSie38c zA@V&bi<3kS=xM=rVlK&NrCOnepo@{2qf;@?;DWF+G6 z4RMnriP5|6lUp&c1M(eqK)%61QRNCFMdf=8Nr^G6O+I>`XZ6zuPmtD6g|vP)Wb_N^ zvl!DD@;=>zLfHUVTgdiw2pVlLJ9G|e8WA8g>lRC_A<%Pe{AOc_*&yheHa^j*Zg9M% zXNhhPw*YV(7MCVb1Xjl%mP9wV`L!!Ch68qjgcR&|fyPEFCk8CW16k8nVz|*~M@Hwu zlAc2TE&O9Ru>nULFNJmDtxG*g5JXUc9snGz%(ANyNuumSscr}ZndJrcjP~Vy5MhGh zKmGtb6ysmSOV^IXH{if*EIdEnP|tuFebWyOKBvC11b=@4{QjIqM#u8?p?%=2GHrLf zEL4B9p56WRyLGQNChvyXt*wIH&+p|!GkQJB?&VC$=xu&4XG}Im8`-^_Ioa6S%bX^KlTqvaLgUirbesSZW<`IgBUK*YJ?#ch7=|7I^ZLg z`A~5ra6hU9JvKE~lt4AeyO5{bLn5C!H-YrMF8?|j4%gLRx7LmIU`}VPWR7~{sZn55 zewPz5?mGrZPs%p3Mrm8UnNh-6O^9WHEBH1*cMjx4m{Y~TK%3NPw<^j{poU}5C~eP> z0BPWc@Z07eV2HVFC6ul~^j|mo8=%NDIukc_(GSfa_~}g;pTwY8LN5*Sf{)Za7)sA5 zr8e?C>Gd5V8wh1u+ntS)*R_&SUb0;)*~m-Y)JitkrHQtWJAv<*eI z9gSkfv(YUqz~c-N3djfORR7KB-DrDL_!U{`Iej!1!Ex!o0&@*n+w`q#cB$k|rQ}Vip4rQ{tesnII-LBkvbci(9D$2&9VXcN^`b9&IdFF-NV zQh(n-yhFV>J0Un#cErWc#{yMT#&PIWj=;ZU2d;LRS@OsL?byX4V)Ok8{O05%@e0%n zPw0;>-@%10&}T6r2L^qB6CC1_?lpR@;kE(CM0kipQN;#1*U&-y9_}6)<%i)|1An_K zUIg_I!Ke?4&Q6*Jq^SRipe=LP_z`p@1}WbKkQCx9-n0{O=CSrjq)W?B9Ifa1aZk~E zx*No)1fB`I2Dl1Jx1sNuy@Sisx7))}4}#0(Sc2>`46ydI@nEut*l(tyib=>8E?C6|wBiS716I8!vs#ohelN>zuD=9!_+y#P z1Kragob+eYnxD1c7Jlcs-Lp~A;G@~#RW#0x5cph6%?GTc%|2+)?a@r+QE1Yyee%hG z9$p)zWE)S57(DPx@@NN20M`sl6eHjd4dxK?#TXxjB!&E7M%l~e0~^CV_yhpK0A2cB zD^=Jdo=?ADifkcW@xRyn?+yRU{Y=4^@~g(ZEa&vz=uFU?W5$fKOnDV5R!x%&MRg<+ zzluasuE4x@>)E9JGDb?Zf`dhQqN%)s-BlsMq8u0~uON3-(8@(S#2|Wx)&=p;^EpGP zKS$-WMs5utO_Bsrw>Z9_R9-X2KueB+mH^?q!+-C^6hLM9A>)eY29OlIH>Y~gL5VMc zoa%58pu`1%mFS_4$TmtP4OwRUFS`FiJ^@Yr1mSc4IfV0KPxk%;j|$uC#g^U+n=Rn^I$8P^4&`vBU(Q{M|#LB4Et)bi?r>m_+LqOXQc4_dfAYC7A$ z8sgIpM{c-3V&1gPj0ixpA0XI7c>p-@Czf?3oX=_4{wa!E!Vq=XsT>cK$qw!>FF;WI zMolTJtTh=HlKXYX*fC{G1S##d`Y;UqHSAZjT?1GPmgP!_h{+ z^)P_d8VBHeGQLDJ)m$28QzDC_Q^pPclCn5P{4+0Yd`3YAnnw7B*YKs zh>kXX1y`*q20&`S29d|3jbDj9P`3LcVIA}^Hux&QkC>_3m>pt+$^z57p+ZciqYYtw z^u7@5BQDlQ)Q2lMp!Pui$|U;;flzA+a@XJd8rK(TxQ>iM6tr>|vZmEjIW{Cew4Q~+ zdamr?uEyihYgi;`MDxcUge+$0DAXJKG%RZnWlhT^ze??Ged|YRZ&Yi8%HT(;k1BGb zM{zbcGXc`$H~~9G8J&8gtW>UddR#6oh6TM7LO*uf((6UMwX$kltaW5CZ34x=r}p(38n?@Vrvr64SGn~ zYazeDA;o4lxU0vU8bPPPTdvt}sTy=w9gVoG?6y9WMr}DZKpgDHbMlFl+rU>@%Ll*D z9a3T;8(VYvH1xT*N>OQAs}vU5f!e0YZnrjHwuQ~> z@OWN{Oo)~M9tCjHH9?QkRs{BzMWV0c3ULEa0|;=5EwU3)ZF_kMK|6lmfOZ4L2hYO; z`)J$qH~PPjkhfe^>|F)E8zM{=A-KB zcoYMs$Wm#*7Az>K3{Xo>zinFm8@o=mlaTe(SW;P3RxP$xuW-g5xmEIwRaC z4lw~JbP8T~Cb)iy4zbvsjV*R(&LVJ#jmW$+h#3NSW(uKFdi~oRF-C1$I#0f+-peA3BUahYl@2j_{uW(w;f|Y^U8hcSe^9+Y*OM?!C zn<{E%MYs(sh?#A|Z4G8KtRzyaKx{fhM=o1H0a6LZH#&U@S`Da5fcgBg>`FDjJBLIv zg$zWxfM$zC#>^(~E8>|gKwdw*3^`uYCA&}Atwy$k%K&0SUeD=XFn$T?b1=RP=~dV0 zW2SOQ;zRU4^pL7zjCs0Z%+sYqBBhw+>4G!}V9yH1N=QEh<20nFK^uKb$cKQAhM3At z_~Q@!x&4kAb~44}`iKHU@gbmFn@H^Vb6cNiOibwF<|M+OB{zSO!@$aP(`p7 z=wr}rYJ;}7u@3;x1AFjez%X|BzB~=G4ZRaAjM)uQ+6@+zP*=8pl>TCg}{ZXD4tq8jm{BBKjf(t=Js zQLczV@#rz2K9zZ1si=rR$i7Y^4*`{Nfehs9K8e<4SVcO7(hA(j2KeAndp0VW#@SPT z=^2CUz-)NXGWM#hTgt>Mz-K9sF+h`M7|$V|4bzBVDk|&%eg!rZR2%AO1n{1!84_^@ zTs{L4_xd{tmSyV7cEr}(JV{qc>c@@X71Af$JR3vIuq6uzn@0|qXyL>7>qLpY3EKOVD4(7K}Gkj+>CzTHAf-HhqrG=Y(Qc1jy zMkr`!KcJQ)2rT2Eo+Yxf9G0&K3{|8s4{1q=n)KX0Bnb2>_9n z2F&6{zXhu$)Vv7lS$IClbGo~=(2oYlf~M)?2ivX@}W^jm+v*|)3DiPoSdfggZA2vE_&52M%6BE ziuEeH0X+yxb#?=)2Sv}ZHk+f}fZiC@N!sFYBo*lsmgyaPy9)L1N^(Twq1}MSLG`I^ z4q()1^oBJG85HH4{P8zpswqERFw;Irz( zRN63WA+rnDT`TZ8ZhdZyNNM3V79@;Rn>3EgkurODMpFbFQZ*G=2t>md4Lecp9>6+db_*w;NelOpJVlY%|(eg#2{i zh@|l4^}Gug!9wFoVL|TsVNZ2*c$=m7IGR$Ctf4kw0Ao0Twh#5R_aD+avDc;l=*do~ z%UasgU+gH32E7jni$BtPk9r#Qo^8j0GysVcgXPWloD^T?$Wh!w<9<)kp=Y5U%eJR$ z1BerW!|%Go@MN$-xno`$H!M)-3cWPsd-T$X(MjWZVVF)v7ee&F$bNa?h7r+%6#5lH zXtUE1Etzqd4%(T^1-)(SHJXMRvQI@|zX-CNVPyv3Q?LlXtBz#^vY(Bl4)e>t$U~UT61}bMet9(q3-jj1tAQW42EG(51agX3Ou{5tgSq zPNxm?$3RB%jOuj+lXG#$F9AkyAQMZY4$5j@G!`F3zk=ynzT==Bt_GkIc%uZ8^RCc% zR?pKsSI}lg$^7zi&X8febt3)!w=81qH}J}+&ca0#@IpC99O|fYLg8AY#<+sm60@$u z=Ox6@s7ejMsWIRz!ms^eR3Up0P zI`%X>3LH7@$%er)nd~5(*urJswVBG>!EA!z9J1|@Z-gY<2nZr|2b%y!stO)7ZG+W0 z6sHZ4rCHm9A1gpv!we`3)*e7)-2{;TjEz;}6>K&?2PDOhO!;bR;0iI|D$ss9WE#+z zH8^-~Gzn9Fhiu#%ida-dOQUj23WJGa4UGgE>SF~MANZp-QJq4at*!FVYHVn2y@paG zx1kiNsVFnhDh#>p5cIaVp!c*y5d0D>&II(~C{zA{({b37{1#1;R;JEq+ByNoV9i{qeIRE=Ci50oe8kk z0<_BIalnp<;K5Y>25uT(ng3zzcE;D$5d119^O4W|Rcj8`2Xf6Qx8qmVR8^nubFWs< z1@Ea5-JrwY1sb{?YSYF6ztPO0kS+CZqC;Z0-B`I#br-x+JXe?llwKJ-vaw@0HXhqu z$_WtfZ;Uo2^12K+X#w-YcnLRfnX;wC$%TVTz~uVb7+Lhj(DKDhJx;OQDo zigP<$FCE*GAYK$KK}ih7_^Hc;NuHyM1uRcCii`U&psqmSq)3xm+Z6HTj0|6HIu07} za;88*RuHWw6#~%QX_eT_+5z%rIjOPT#iT@b7w1#*-374{wG|b%pCe&o>g@J&#I77T zjX6d5*5aI5fomwvM5>&LM?R5-jDp)PI~2Y0p;$ILwSLNh3=OdZYF^so9ZQ#`L;}{w z12w)|J1G{`Di%bQrSv(*+>&Wi-9Hf;}lst-CG zisQ_o*zF|r3{F`|;3YS#eT}y-Yg(vBQHvvXmyztCNZkdh4#OQU^Swqnvc>Z{(&N~v zdRS+^*P0#4ic|5|{xRq4z~w&afsP)}no6#HqPc}fb>>BGxZDAoeYys2*Yud4kp$Nv zb=}MnJ+|S2%$r*EJ;OF7>bTsY9h{qe@4z#B?wW4;H|$KM2;0x8*l9mm7BYszI zSpW_rxl^~($y8NY2=BY5!_Iaoosv8@h_6u_D1h>o+6eMW(c7A0zTbKu-ikg|UqqX# zcVg%h#KViKj4x|h9UCNcsgJZ}h{td!V7^soS#7kOHJbXEpK^$J>^KPt8Vu`EX<4;9 z@rB7Dxr7z?MD-bhV?}YA`A#IncWLBer}_l<(H4dt&+&p z4wI|rfUKj}9S>jnr1Vq?6};SzSHiMx1|$~!GyR6>msa7LO_!!6c;@^G@I}G3o3f=F z57d`z^~Q`}i}lv^TUxQ3rS?3Xt=N<;SZt54S&y~nHQVc2r>{#5g9PZXeyaDm02qyx zW^4Bha5$>)Fsv0@XA3tiEn1uP%Wmmc$7Nqh2a=Sh+|xDb4`3H zcaG_P{8k~<6@8AJTxLU})@MU8Dg@S<+wRr~)K@~^pwwNM?DqSM7}6clqcau^VAEZe zh*PPyZ0ZaT7d`#UNxv%Cy?Ltdcrbg5@pw(LU4=s|VJ5fX}}47oPU>Lnnu#xI`xgNnLtHFI`aD&EVy5zFeW*<}Hn7#+#hFJSWMc zJB~UdD+|WT2F41Yh?QM2q?cku0R`+@^eF^e906{9iuDuaW}ie@`lb3*=q;mYF=1a&9F!np1S$R>V#!nKk01oeT#ojrXShG z*Ua!WJNTLx4BjbT1}cnWmqY>p3&V@EG1mEapSj}$o$jyj_16UP(p?w^$&j5$mpDY> zLE9$R-?zAbX8h+a8lPrpd)h(M(+gUjijD*1czK|F5)%dlJfG7ecL7#-{Jz`o6cb2< z)I)_BkopB&_Yr*ify*&2zq!ktu2xt9EDvCjUJ#hmupo2_iKs42vQGz^6agQh^0e~5 z3*uE}lV8o}ba&CzHMYN!)A6Pc%|qRV<{08TZ$R)oap@y;=whXRzPThzXf4oPH_tUI zuwN{vETnLD8#!h9BY7%XL7@9?DM>E>U1Y`OH&_~6{)2}agIM)>>9mzsMq9u<3<^bY z_u3u9mw`BKdnSC_t=sKL_Jhm5V`)HA782okkQ62tcKB-Jq>@sxblx6?=X zM$9}qfMF67u%RVn<_PqbTwxw3h(Hk++4X7b*D9>pT>64QLV>6PIhO?{t`JQivOp4n_yX|+0@mwjj+|d$ zjlaZj=D1deUbQKvRdlQ|0|7rFOuV$L#nD7L23KQy0Ra3n#$%$Iw{4#{!JhSjvNOQ} zjKtr5lk%~YN{%@Ru`DY69rKpHrR>)PeyLX!K;m6+r&pd4j9wooIwSQnWd5gCHw!)W zs1A7FjD>z0Pc0&;jvPfNU;_~7tb@G0=HxB4#@`tdhT@7>bNXfcfk>J?lLYz|E~7w( z)NFTS)uRfSBEQfpiYMq-c5z3fBVPv3y}@80!4U_i+>13%JbJl|`0iOtk{UgCJdkC= zxmrTKKQ1|PK3L-?ou0PMb86Q-;_{tWj`wQGjeVig4;D`#Lm#bn+2IR}-4kcxd-!kv zSq?qq(0>=$$evnRd;s0Ce#$dICk=2l#j3dpDAoe60E>GCJS;nCkFD`)`2$W`56`-e z>%k|-kW}y(seA^TK4kFdxGU~qc`8X9`>;UIEcj_l0^PS38!->8vE|XD1$*kiqB`BT z_?|lN$aG+Voyk@oC3?o3Ki0;kM=ur}coWHx`>yRJktD)p;3Sb$tebn7^m8DvPef8; zucyRb-%r4>sP}Ep@Oz3MYI@io_S~>P>bXjPgsk`)03wB^?e!hqh$SlZH$&acmXSlw zEWX$bxhJiM*uER~H}Huof!=|J?TN>se6x^-#CGfe{n%|^zaB}MgioZzkpz0Pgirg7 z(<4g)z3$i$Q;SMgs2dV}VJ*{J3xjbwr^(FIR4s}@Wsd;UpOa-;&}#6>pr4v+!LhwZ z+emB4eDosPvq+$08{`%kI)R6S;-3nj5wd6*2t7m@Vi4Xb-606bcuF6^Pq`;yYV`N| z*r#zB8KTsef=6#{1>1;!wvBj&dc7b1NccAB*t&!k3ZW zUX}7m=;Z)i68Wo^y#)r4Wc}-S6HxZf7XiUC+t-LG(}GGtR)ykL#P3bARj@)ouVEQ< zbP+44mK?_Qj$ZmnsOoG??g->KqF!r%6-+sIGf+%nur|R*Mc@nDJEzH>sfVTRb#zQm z^?u~Yk<}c)TqB7qC^T1FyI+b9^-)H|hT-_!C^}K^b>wC}4Q}5p5buQQJ0Q48e^*ttyxWL0{+oo!Oa3OY z96hwTjNx0%z60M;nUP3WA;V^v;*vhG2-x*89_*V%qT3XL| z{W-NwpeGJ6Uvk@=o01CbGX73CgfSAF&$Mz~{!C$!t6y0&!H^FgIY=EdnLcMAl&r(?#imfiNfckxMNtjU?r4|uV zsfCgV;5S@;S2iFSR>Grs=KE5neVfbj=zED|ICbbk z`6bDP6J4M?kpPQ2Y>+nFWHY@+rNEDDrW8e!hOJ7-6R}UXW8IDV2$kD&>?meO9o{Y& zqyS_`rN31Wo-bt{4bQ}$);AperPpXSt7Ms=+%QZ)p*jNSQir@)ZDx-LpWJXp)FHab zB8C}DLE8YZ-qpf)@yVw9L~IfkR-9l*Jp9+ke`WX=4ncrld4LWw9x^a9rWF=dFaOvP zeiIO&HH!^D=E38AWXdim4NahgjCY%w3*9Q13(3kb6!nvWozexb(G>On6dA`nJSRqBJe7^1#9_xuCj^9i}IC?pL6$=@AE zHlV>-V;2)6o)A}9mzd&H63f3LiJId|$6beYbC`|wvt`G6KMG_B6O-yPsKvTnUO`t? zV&^LTO}_I_X+WQeMxTjFO%?_)ama3VtQM4%cYu?N7TfzJii9*Frb$DdB?S~ZjF>Rm zF*CbE>^@V()}lu;J;I2RwByK|nN)|ABUCY~;TYUE63oGNVGe$@U{cxGvYCx~hM#y4 zM~}hRC6~iuhythont!ckS6NBBWVN4~i0EVwlwF{oHXL94;y?g=-NOt!)D}wd)&u1c zv7P0CasW=PaIp({nn8thNR#ny2&T&FYf=-CDI=P|3w&_D7_z2c%*Ak7I~GldVseyI zR_C&HaPNpdkxXrivj zid|>l2mQ+jcEg6kQ)GK=FGxU*^vHxNw!P_&S;Sd#O%u)A{arv?=YzRGQ*8lP2}Z#? zo9cAOL?1i$atJt)1bSjF3a_-Y6V#Ju$P_eL2segp3VW3eT_3o=(0B5m&|SrUDQcc# zqsQ40VHy2k0zdSW`7LFw^s|AvGSR*86@a|KN2AZ2M<0BFbGr_C%sN|ZA{)(*X08{x z!>f#D3iNErTIw7Jg>V?UucZf2q%3Kr!d%P5&J0<7gdOQ=5|!dm_M$)z(OrhoIm7qZ zNn`1uo!?cHFv2q?@NJK|+TeYDbRb3abjBNIoxr2SW7Z@Z@^9V8C5HO@19Qc^zDMTb z$iIt@^34ow%3K9@z5@CF%KPIFZ#*7vAoM$4u^XEe6!=}X+46WRf!>&7N%v;szZL!j zf0O8EbNpRZ#J5y^LKl;3*jm1gy!r$E5&{+}z5P*P^{+?{oQb&I$$>p|l!H9Xt^EUE zomX%&9Cpk?W_Nuumsr0JO-{nTv=JVb@q`6>f9;?;PbTHGj1iqrW=4Fzm?*j5LCfHs z@TjMIzNSm`P8aC?0wVUPr<(ulaU!mn2Yciqw>ijX=ZcV#BWrgwc2UauWMZTDnkIxs+#g z$bkvKhPsebxjl!Qk{9rtO!H7`VN`BG`oI52Dv?TRPrK|K;S|MqdD@x4NS(5XH5XlY z6^CUmj>WJ@HcSaPBN z-1GdtnZz+B!)O)%pNTz%(XT94Sjy76o0+0*um5aou_aM&B5e+b!HXnH8~C@e&Rbs! z$F=Uo{U{FgShbq9;DtzA`rOgE%20eTGY8SWB8UAvQwJP~fDDxJ1Gtf4l~8XAtM1+; znn2G@k^osF7iP{-WG^ngv@ZvcR;hbe2BGaT-TJOc41q>X84b3hH)*E{z)5X1KfU#{ z7wR;b0`b-)!JpP+vj61*4QO?ySwk?cAbD5B@BEYZkP*w0rqG@~Fb7f_Ekcfo8!gys zP~=EQz6R*hg-LGAf+pRVMYn!u;V=GlvByv}UF;C0PFM6}ai-m8X!ccF=m&6QF*TgD zPPlDT5GHxHE(x@SK8trx|s86$+Y`~bNwXJQQ?{Up=iqx-0PfeyInqoF4s6m(*5!Gngc z0-^+e@_E`YH0H*{A+siWVD|x{0SV>QXNaH%hEBzxhHGT%fVt|Vm@m`J7&5aV3Ju}) zlCeJ7OT>&ch61AH+)~B)f%a_XR@IGNsg`v9o`YW^fa zO)`X8K;BmnDC7m=1bW*wbnA$Z6-=r0(L91B#Ge*k!zWE964hZ|d8H}GJjc|v$$VB< zJrBTu1ZRSuQUBFzd(86Ct-l31i^*GIf2-GIW|4D6uJy6tS|1sqqII);s5>$n-*%O3 z^O(uPaPwn^9V(Yo@?nZp@?ly%Vw#lI6DB)T;tOJS<1OBhiyKeEWgo)9MkQb&d{lQaLmb~rqv7y>Ne#MPVd8t0U(Bm6XY7x0(`Bxq zXSmTe=5)V4|F=8nw|h6}#L08|Vkela@ED;qY+e_{aG`-ms3 zh6_x#svK&)^4U@?>RY-2q8450td(WB{nAAAU@~G^iF&0+@kaK%M$W+bh(WMkT3}4# zgSXEEjp1mv9S2e$6|*ZO<}Nh89z%-Oj@{N(Rgq}KU&%S!oQWq zgDd)+7ciec8V|1Nm6hp@YTvK;TAF=LQ!(gI@{XNk9s4+C*P3Ao9lRb;P3Iu+X1d|26+M6X^)SmA+auDe~*>SijTM~X3 zyZx`rm04tHwJQ52gY22~#l%$70I<#jVNm05f#1#Uv>f>MZJI~E0lqS~rSrCKuoe>Y z2*UwBFVs@3YC8>O&Gy?g6ue*+t7(jfi;ky?_vdV-bAeuWGS9W0Tkvj0|Jh4Fn&pxD zz1VVL6!$ba?fC|xxj*arV;)s-Gc8oS)v5{kJf+gH&%$wE>44S^b{%#<3)6+$V0|kW zWzoJ_;RY1`n1b`fk9Dzk1ui)py#nyJBYCE4kW>J}YI{-ahq9@|ccp$3_LR_njyQG6 z`1ac+`l3%un8(BE;_$bpDG##$GUX}dzD~0U_d9mAHK+H}C6Sxw(=jaC*AiW0cCWyZ zRo(I(q{396p}8!E97dpKpDj?}Y?d`R-t1o|lN|z&6wkO=RxJ7~YX*dWtuCYGt&J+J z)#bEs)^UPX0UqMPQ#rfFE0$$PFQ*kLJSSg`-j`Cq@Kf~_X2|e(k`I=Zr6rUZOw$_^ zsaKKqz+!}^x~EqRsjR67KA)C%H>UymEGLyac}kC{M@|E{kR74dFeJ3@u17r`vrHU; zY=;Hq{sAd*JuKuzZ^lNUAu6tDZ#gx~M4?a!txZc^WxPC04iH>8T<@p!a4IZg=bwbh z;gr~uuT$}EDu{M%@$#d)Efi%Y68iPs5}k|{cQfus_x8Y zFsjaMA+TXDAnFWp!7gly*(^P3(<_Rpw)H&WV>0Us3ddBoZvdoIqYn9?p+6!WZEXz& zp`Ocmvlbw}Rm;=d+J$2-7@U0K-vqW>o9UMy)fOwa#qSI`g{L zneBRK-puFJ=%TqA;P?Y!B+e|$WbUn$}`N!($2J}YM z`;9S!qHQP|kH>E&BT-);&F4g$5r-r*d?h-o+T&8T^s=*235l!pEjp-n$%5~}&#_ni z9PHarZ2zob_h$_|Kg$-%@^ni_pNX6Ubdd6=*&JFjm@Tn>`rwkp4I6G}M&$_P`+vNd zK%OOc274%>Z(xiaasgP;NM|3|J#KlnW{ZtSCQ_=+Ii1crGum(Pg!Z%9N(d&$B?OaF z^5aUWUCwm;fSHa%9@mvF9jjT%uB^;DgJu*moq?)tI>Us<68fgQz86~(<}B;hS7xch z^(;?y%+2_VbF53!`fsrQCMmoXQK?saC9^GI!48#r zT(wLa8Ti2M2cOZi^y{x;ZdR$e|Hv3=hi*XBA47y4>mNwhfWDot^H)B25BUQHSuf(l z^8YG^>5N#o80a}MXE_IOLLBm_(L>A~ zn9Gtcv&AxUE>W^*9ET_~cA$A#3ztj82JC4YYpF9Xd~epNJjC5^%s`=x37@Vye5W3g zD7K9>_g<=d737gty8c+wRy7f!@HI733!i;CLodwB1k{Gds64z%Kv8%L+9B!6N+c@n zhqiBOJ$=d05teb7}_FrU*?9I*hR3zqB`SkOqn8;aFDKqS;>eSCzw)Z`1Eo|CItAuG)? zKcO%LOG<|en(Ud5j`%h3YxAYY6x#i=I{T_A_OU*qTBg>EGR66(;jxM}KY~b#8o>|1 zb(~~H)pk836&+vq{F7gYHjm|>$MBwJ9RQJO1W_>hKz?eWL#piPpn9FM$;qYFK_wzG_DGHpjT&jY9c2dojn4Ujo=0kj5L{z=vMW)Nc)#UaDl z73lt~D?-1z)gT{BV5^VijizTyD@v3H2szA`bW?5PO{9;WCW+D|vMoSstLxeFld!2D z00$QY&}v!a33-@Nj}GZbSiGvaV&say@s!6a7w$$l=ngr?<^*~+6Hqf!)h>9Y33aML z`_fQq0`3uvN%ja5c$ttlo~)w$LR`V z$PSjU5G=^P#Nx3}+?f%*JRm{b-|DTxKkv^l?3MDXQGQe&wRS`w98TIoe|=4MdWNj& znP*44e?ud<5$g3dTXuZ2SN_5KscT(z`1f=b*ZHe#F{+Lq=_T(0)ZE&$k|E(mYU78X z&9X5VO}p}p9?Xb7cy52*@cp>04-kUTYnXSL*B4k=I=i+fdnyY{FYm8Pb2Xi`U#@1>J7zo^K)hO5xh&v)0*u*kw46wBpf?HmkTu}LyG?uq^3)TVZU@vC%&WU z>eAAYq7QR=FpTWf{=R&%n5p zKwDx2ZHaTx79AtvI{{hZMSv_`4Uom-MjToL?^_jkCGG%T`8EUKzg!7$Gh5{O$PW~G z*eehRp^;rYa#%d;=h;u;=sqXJ6DGBBmdava)8YxB3t4XNQs@N_qWZD6cB&15E7u9Z z1i~e%cr(LnJ59w8o3{oGa3|{TitxlNjvRbJX5Hyr%fs62PDwZQV$lmwwS8t^(G7u* zn8bChlwv_gZkMl*XL3HJKVP9Xax^(7BW72r`UP)%L?QJ=xR?|&)pfGX7rG=tgT%yd3?lWpG)qT#K-X~j^n#I zy<1`=x}Pof{XU;9cJEosj7)mkX%5eGp&41a3P0bZC5>~gs$~&#OkDAu#Pxc(s+glq zDG;DnMy6|X0*MP+rNbR>3Q3ZO=pezIp6y1QO|Pt$zH7k?@fP$y%Y0ygx*++%0fsdC6vz6}+!K)rO4T6N}81!xGp^f=ZB z7-i(X8Op9Zg=|#lPtMcO(J&qF>(oxW^}689n5%T<6fqq2msk#Uz<`O z5~qOEb_J+x_tYLBb-SHWpUNfLnCA#OpCU#4G|edDmnj0e-GRTnpHgX$r*_Mpyv?6{ zVo%<{3&I(Vs9l&I|FdNd<8r(kM2HazO2GtOD{VEuBLwqx-NUa<7+vO2T?OAyeB!30;#AGXMG{*575-$~A9RQYlz3_dv*QH( z5nSminsX6eA>DAh1SEaxoYq^2$XF-RFFd&-gV|3uZD03#h@(l zBtp@Qe-26gj?pNo9L&onf8vQh2)RwBDfG()qr;6*iDF}7B^#bJZCCm;!0I_w(#7&> z4s&9^FKi|mDqCDZWy9fvl&%lI{1Q*3u_W>zui}aHYAlIG(SJ*#^yZ^5VW~T#ye}K#W`!g^z{)-hDjAiqi)W$-2zt=JIeKm>DdXTS zk%O~dO9I_Xmt?vAH&C({IMD+oV$d}MRb%1D19_VTAr92-MX<2{?zW-Jv{KA-G-X@F zuE`r{L8>bzis+MtRwHk@nJD~eo6MX(FF3RQ{Q%FKE_!k0Xe%9KL5kvK1ksxEy-;&r zXXPmE;uFeM%V~v8;Blt$Rxq)nnZjEQAn~Bdw^Anf(*{?*2-zq{RIVKPxtg-!RFJFC zUsC9<%i|?mOa(`)nF`Vr^ypcdj6dJ=QdCiQ=aUpn3|v(XW4V_SeFCn~MhbA7BS8?4 zGfa@QTb1qAECgZMv$_Ql`WbM5Cmeq{J9V>oQLz3s!4{XQrpZO2oIljh`ACLW@0`dpGiaIB#6_tGLb%@9gYjr0b zw|lMuHg3rey*TpPqvCS{SzmZLkmXkds#2W-sP+-0mJk7Hy_yAKrPnyDg&`~z{FsLh z@ovQAyRYmd|HgnsP8Nf^Z`C7>+3gJ$&b+{QQw#Xd!{QGETo-kF43n$XlPCK!G|$)BTjk<{kXc3W$bJ_S1icbXae= zDjFUk$E-*Ky-1fEno1h8itBI@kne+?3I4@LE14b~a+J0x(MrC9{n2na)?u9MioY#V zeRU;!Fnn^HW(i(CGv{Jqp)RU`6Qz~>4mqsH3}_rZNLeaF_Ei@tl@2~gEKjMC-(q}J z4`L87zmJFceX>&E!pddxeNo2jl3Jzev?ylwOR&wdcglA~QKt05`#0sj#DHLpG@38# zyf9<0q>^Jps}2EGSDF0fA-~naZF!5ht+>W*#RazoT`BQP<=!w339)Q2(i=FW5ILkX zplkqplygYC?Sxu6V?x&gRKM&7^EN znr=HZOSV}*;wn_2dJFtcWLFn+j{}{9bkS9-?`T@6M^U*2`5Vg4lL z1V_B{*EzwG_M9XF%_HoZgxlupa0e}GTW|MK=^~{0#{o5r+=|?QVM?DbDsM}q>9tCHN~@5USBjq$FW*rRXAuI+$Y$@M*-B{Ryu z{IGGlW7of?U0Lv&J-pGw$o9P+(U)`Cs_j>qrk*eC@?w^V&~7pSsmvt0;fXYcKSf{R40P{MWqo$3Asb-VArkwriQ~ zES)reuEW;>gmA3V-y=*Izn0U-ParPIR|5Tta?=J79Jq5kP`IPpeaGQ?`@WpZ>Mo@* z58&aHS`ZIYuJ)#dKg>R83a)aD z_2N2qsw?o*lV^ZerHsF)VxG5HfGmso&PS-MCnSHqvBXN3OFltJhYY;U!}nI!#{l;K@Iz^mfheH6j{1&SLwg*1#y3~ z7clh2a;{3nk7M5j_}1X17x|8@^+HpT$&mKg{|2Lt*Fwp$S;>iAEvTzH>d+4*lo4$M zt=(nmqBK$s=8f0IJ%|H_upn&+3E9HmXi*x~{LC8)p9=N2Jy7-(aaKQ&Ql*ykx-4Ck zynAx@B9b3mhO+SB(iEVWch%E{4>(L?uH8IO3;Bcw%J}=SY-c1iQa3l2*n#GaLNc>(TcN1R? zsR7`TALR7ZxCPY6FPEn$gFp@=ogf6`B|~6@CJ$oW1zm9Yd5F#7>xDZ)iS5;@4@m+= zE`mXf?l{i!_g0^m(EsdxpbP>(jH5vSTu~H%sJvwmB}$}*wxU7C>)PAl16hD|nzS{=VU^U;jUm5HldS7jxTHp8n{{j1NG5KNR0RXU+kP!d? literal 0 HcmV?d00001 diff --git a/ui/app/dist/index.html.br b/ui/app/dist/index.html.br index 471891610fa0416b8e90840d74b3869bc3fc2b09..a2343bfacaca14700b72b2eb778eeea825fb7860 100644 GIT binary patch literal 186 zcmV;r07d^B`2heN2X?ICbbezHw&G3W=2B&3Ux!A*|BysHKuRIVYS%O#sMKx;O%oI+ z7)0Iu!6QG2y|(M6loCJOwvOcD=&t~VO$rTZ+2OpSvbree1=j}QsW?Ph54L|193Ac6 z%>zCcTz99Kmnlpj9>L&eAeON*3_-|`(Fozay{}LdR)9DlkRpiI|3*wgWmhRLUvTCU oeckrwjl+5vPzfuy%Hd6|&O_=t3MZW=Ui7jNZ;-?;hm>Hd09e9S2LJ#7 literal 184 zcmV;p07w5D`2hfo!#Eb)q(!j^*CyuGSK_j-%YGDykJYYeJW#3K4w@z?PB4hN`-4Y* z5PNOcODQFOxNRND#nE2@3|jy~Lz?Vx-jTApDCY&P4Z>4#h_oJT|3cu9&-^au27fPj zT)xD4O<@A@2nIg`v5b{r2tt00MhNfieIXQu6(A0f6hXB9H)0~F>?+F37nlwHQkAwB mZyeUcfJ#`oRSruZ1?Jp5*HJj>XyQdL8}SB7>~cs6rV0STKvIAJ diff --git a/ui/app/dist/index.html.gz b/ui/app/dist/index.html.gz index 652524ea6a6bee49a9a364561bd7181c117ef9d2..88069b8f9c3954a967b7ad076d4b94576613c502 100644 GIT binary patch delta 294 zcmV+>0onez0=EKxABzY8000000s~!KTO*$J7$U7 zdZYd3gUJjkBJ zfbC4mBpTP!6nA(%4yGrK1Jh;|lOWg_go0??u#-+Z8rV_}aS&7Bz{18C*&0v!b_$V> z9Dk&?#n|f-H5wZ;>}AO|BB%?5uz0Fxo|5z?n3htd#@=zK$~N&zj_f|VwI>9dV$Lyf z%K>t-8cStzCdrC}qitbc-X7P(^H2Gmv98p}_(Pt3XW4nm9g+NJj;^nui>GEj3(NP? s=6uRSodc=9qT5}Z=6C>h8dHq2H{p0VeGeKpD>Wwl3yM?d^!fn+09XHye*gdg delta 295 zcmV+?0oeYx0=NQyABzY8000000s~!8?}GHo6u^cfb|Ukfa=J&u{_A0 z#DMKg$|M@s(iHc2Jr1TPjRVtW6_X&?7=(go+^~~QI~v#zQgILy;K0Jh7ugz5`fdV| zjU0beZHuwjC2BM_X4uP;ZA7Rp5W?cAo_b2sn_yZ>sv3L89hGh3l^of9bZbutHpQG{ z;+6yCWHpwR$(bZq92{+nhmYa;`)d))pNzGtM#dlV>^sZOQ|^f5KXY__1)aY%vuRkq tl{V*79@RMzwTNzaahl@+*lA2L%HD+I;q(t_+*H+=^e@4|1cCYi007yxkrDs^