diff --git a/Dockerfile b/Dockerfile index 94233da..0bb3d6b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,7 +13,7 @@ # limitations under the License. # Multi-stage build for multi-platform support -FROM --platform=$BUILDPLATFORM golang:1.24-alpine AS builder +FROM --platform=$BUILDPLATFORM golang:1.25-alpine AS builder # Install build dependencies RUN apk add --no-cache git make @@ -41,9 +41,9 @@ ARG BUILD_DATE # Build the binary for the target platform RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} \ go build -ldflags "\ - -X main.version=${VERSION} \ - -X main.commit=${COMMIT} \ - -X main.date=${BUILD_DATE}" \ + -X main.version=${VERSION} \ + -X main.commit=${COMMIT} \ + -X main.date=${BUILD_DATE}" \ -o snmcp cmd/streamnative-mcp-server/main.go # Final stage - minimal Alpine image diff --git a/cmd/snmcp-e2e/main.go b/cmd/snmcp-e2e/main.go index 7d0138f..b3b9455 100644 --- a/cmd/snmcp-e2e/main.go +++ b/cmd/snmcp-e2e/main.go @@ -24,6 +24,7 @@ import ( "net/http" "net/url" "os" + "sort" "strings" "sync" "time" @@ -141,6 +142,12 @@ func run(ctx context.Context, cfg config) error { functionInputTopic := fmt.Sprintf("persistent://%s/function-input-%d", namespace, suffix) functionOutputTopic := fmt.Sprintf("persistent://%s/function-output-%d", namespace, suffix) functionName := fmt.Sprintf("echo-%d", suffix) + sinkInputTopic := fmt.Sprintf("persistent://%s/sink-input-%d", namespace, suffix) + sinkName := fmt.Sprintf("e2e-sink-%d", suffix) + sinkParallelismUpdated := 2 + sourceOutputTopic := fmt.Sprintf("persistent://%s/source-output-%d", namespace, suffix) + sourceName := fmt.Sprintf("e2e-source-%d", suffix) + sourceParallelismUpdated := 2 result, err := callTool(ctx, adminClient, "pulsar_admin_tenant", map[string]any{ "resource": "tenant", @@ -325,6 +332,149 @@ func run(ctx context.Context, cfg config) error { return err } + result, err = callTool(ctx, adminClient, "pulsar_admin_topic", map[string]any{ + "resource": "topic", + "operation": "create", + "topic": sinkInputTopic, + "partitions": float64(0), + }) + if err := requireToolOK(result, err, "pulsar_admin_topic create sink input"); err != nil { + return err + } + + builtInSinks, err := listBuiltInSinks(ctx, adminClient) + if err != nil { + return err + } + sinkType, err := selectSinkType(builtInSinks, []string{"data-generator", "batch-data-generator"}) + if err != nil { + return err + } + + result, err = callTool(ctx, adminClient, "pulsar_admin_sinks", map[string]any{ + "operation": "create", + "tenant": tenant, + "namespace": namespaceName, + "name": sinkName, + "sink-type": sinkType, + "inputs": []string{sinkInputTopic}, + }) + if err := requireToolOK(result, err, "pulsar_admin_sinks create"); err != nil { + return err + } + + if _, err := getSinkStatus(ctx, adminClient, tenant, namespaceName, sinkName); err != nil { + return err + } + + result, err = callTool(ctx, adminClient, "pulsar_admin_sinks", map[string]any{ + "operation": "update", + "tenant": tenant, + "namespace": namespaceName, + "name": sinkName, + "sink-type": sinkType, + "parallelism": float64(sinkParallelismUpdated), + }) + if err := requireToolOK(result, err, "pulsar_admin_sinks update"); err != nil { + return err + } + + result, err = callTool(ctx, adminClient, "pulsar_admin_sinks", map[string]any{ + "operation": "get", + "tenant": tenant, + "namespace": namespaceName, + "name": sinkName, + }) + if err := requireToolOK(result, err, "pulsar_admin_sinks get"); err != nil { + return err + } + if err := assertSinkParallelism(firstText(result), sinkParallelismUpdated); err != nil { + return err + } + + result, err = callTool(ctx, adminClient, "pulsar_admin_sinks", map[string]any{ + "operation": "delete", + "tenant": tenant, + "namespace": namespaceName, + "name": sinkName, + }) + if err := requireToolOK(result, err, "pulsar_admin_sinks delete"); err != nil { + return err + } + + result, err = callTool(ctx, adminClient, "pulsar_admin_topic", map[string]any{ + "resource": "topic", + "operation": "create", + "topic": sourceOutputTopic, + "partitions": float64(0), + }) + if err := requireToolOK(result, err, "pulsar_admin_topic create source output"); err != nil { + return err + } + + builtInSources, err := listBuiltInSources(ctx, adminClient) + if err != nil { + return err + } + sourceType, err := selectSourceType(builtInSources, []string{"data-generator", "batch-data-generator"}) + if err != nil { + return err + } + + result, err = callTool(ctx, adminClient, "pulsar_admin_sources", map[string]any{ + "operation": "create", + "tenant": tenant, + "namespace": namespaceName, + "name": sourceName, + "source-type": sourceType, + "destination-topic-name": sourceOutputTopic, + "source-config": map[string]any{ + "sleepBetweenMessages": "60000", + }, + }) + if err := requireToolOK(result, err, "pulsar_admin_sources create"); err != nil { + return err + } + + if err := waitForSourceRunning(ctx, adminClient, tenant, namespaceName, sourceName, 60*time.Second); err != nil { + return err + } + + result, err = callTool(ctx, adminClient, "pulsar_admin_sources", map[string]any{ + "operation": "update", + "tenant": tenant, + "namespace": namespaceName, + "name": sourceName, + "source-type": sourceType, + "parallelism": float64(sourceParallelismUpdated), + }) + if err := requireToolOK(result, err, "pulsar_admin_sources update"); err != nil { + return err + } + + result, err = callTool(ctx, adminClient, "pulsar_admin_sources", map[string]any{ + "operation": "get", + "tenant": tenant, + "namespace": namespaceName, + "name": sourceName, + }) + if err := requireToolOK(result, err, "pulsar_admin_sources get"); err != nil { + return err + } + if err := assertSourceParallelism(firstText(result), sourceParallelismUpdated); err != nil { + return err + } + + result, err = callTool(ctx, adminClient, "pulsar_admin_sources", map[string]any{ + "operation": "delete", + "tenant": tenant, + "namespace": namespaceName, + "name": sourceName, + }) + if err := requireToolOK(result, err, "pulsar_admin_sources delete"); err != nil { + return err + } + result, err = callTool(ctx, adminClient, "pulsar_admin_topic", map[string]any{ "resource": "topic", "operation": "create", @@ -637,6 +787,210 @@ func assertFunctionUserConfig(raw string, key string) error { return nil } +type connectorDefinition struct { + Name string `json:"name"` +} + +func listBuiltInSinks(ctx context.Context, c *client.Client) ([]connectorDefinition, error) { + result, err := callTool(ctx, c, "pulsar_admin_sinks", map[string]any{ + "operation": "list-built-in", + }) + if err := requireToolOK(result, err, "pulsar_admin_sinks list-built-in"); err != nil { + return nil, err + } + raw := firstText(result) + if raw == "" { + return nil, errors.New("empty built-in sink list result") + } + var sinks []connectorDefinition + if err := json.Unmarshal([]byte(raw), &sinks); err != nil { + return nil, fmt.Errorf("failed to parse built-in sink list: %w", err) + } + return sinks, nil +} + +func selectSinkType(definitions []connectorDefinition, preferred []string) (string, error) { + available := make(map[string]struct{}, len(definitions)) + for _, definition := range definitions { + available[definition.Name] = struct{}{} + } + for _, name := range preferred { + if _, ok := available[name]; ok { + return name, nil + } + } + names := make([]string, 0, len(definitions)) + for name := range available { + names = append(names, name) + } + sort.Strings(names) + return "", fmt.Errorf("no supported sink type available; found: %s", strings.Join(names, ", ")) +} + +func listBuiltInSources(ctx context.Context, c *client.Client) ([]connectorDefinition, error) { + result, err := callTool(ctx, c, "pulsar_admin_sources", map[string]any{ + "operation": "list-built-in", + }) + if err := requireToolOK(result, err, "pulsar_admin_sources list-built-in"); err != nil { + return nil, err + } + raw := firstText(result) + if raw == "" { + return nil, errors.New("empty built-in source list result") + } + var sources []connectorDefinition + if err := json.Unmarshal([]byte(raw), &sources); err != nil { + return nil, fmt.Errorf("failed to parse built-in source list: %w", err) + } + return sources, nil +} + +func selectSourceType(definitions []connectorDefinition, preferred []string) (string, error) { + available := make(map[string]struct{}, len(definitions)) + for _, definition := range definitions { + available[definition.Name] = struct{}{} + } + for _, name := range preferred { + if _, ok := available[name]; ok { + return name, nil + } + } + names := make([]string, 0, len(definitions)) + for name := range available { + names = append(names, name) + } + sort.Strings(names) + return "", fmt.Errorf("no supported source type available; found: %s", strings.Join(names, ", ")) +} + +type sinkStatus struct { + NumInstances int `json:"numInstances"` + NumRunning int `json:"numRunning"` + Instances []sinkInstanceStatus `json:"instances"` +} + +type sinkInstanceStatus struct { + InstanceID int `json:"instanceId"` + Status sinkInstanceStatusData `json:"status"` +} + +type sinkInstanceStatusData struct { + Running bool `json:"running"` + Err string `json:"error"` +} + +func getSinkStatus(ctx context.Context, c *client.Client, tenant, namespace, name string) (sinkStatus, error) { + result, err := callTool(ctx, c, "pulsar_admin_sinks", map[string]any{ + "operation": "status", + "tenant": tenant, + "namespace": namespace, + "name": name, + }) + if err := requireToolOK(result, err, "pulsar_admin_sinks status"); err != nil { + return sinkStatus{}, err + } + raw := firstText(result) + if raw == "" { + return sinkStatus{}, errors.New("empty sink status result") + } + var status sinkStatus + if err := json.Unmarshal([]byte(raw), &status); err != nil { + return sinkStatus{}, fmt.Errorf("failed to parse sink status: %w", err) + } + return status, nil +} + +type sourceStatus struct { + NumInstances int `json:"numInstances"` + NumRunning int `json:"numRunning"` + Instances []sourceInstanceStatus `json:"instances"` +} + +type sourceInstanceStatus struct { + InstanceID int `json:"instanceId"` + Status sourceInstanceStatusData `json:"status"` +} + +type sourceInstanceStatusData struct { + Running bool `json:"running"` + Err string `json:"error"` +} + +func waitForSourceRunning(ctx context.Context, c *client.Client, tenant, namespace, name string, timeout time.Duration) error { + deadline := time.Now().Add(timeout) + for time.Now().Before(deadline) { + status, err := getSourceStatus(ctx, c, tenant, namespace, name) + if err == nil && status.NumRunning > 0 { + return nil + } + select { + case <-ctx.Done(): + return ctx.Err() + case <-time.After(2 * time.Second): + } + } + return fmt.Errorf("source %s did not reach running state within %s", name, timeout.String()) +} + +func getSourceStatus(ctx context.Context, c *client.Client, tenant, namespace, name string) (sourceStatus, error) { + result, err := callTool(ctx, c, "pulsar_admin_sources", map[string]any{ + "operation": "status", + "tenant": tenant, + "namespace": namespace, + "name": name, + }) + if err := requireToolOK(result, err, "pulsar_admin_sources status"); err != nil { + return sourceStatus{}, err + } + raw := firstText(result) + if raw == "" { + return sourceStatus{}, errors.New("empty source status result") + } + var status sourceStatus + if err := json.Unmarshal([]byte(raw), &status); err != nil { + return sourceStatus{}, fmt.Errorf("failed to parse source status: %w", err) + } + return status, nil +} + +type sinkConfig struct { + Configs map[string]interface{} `json:"configs"` + Parallelism int `json:"parallelism"` +} + +func assertSinkParallelism(raw string, expected int) error { + if raw == "" { + return errors.New("empty sink config result") + } + var config sinkConfig + if err := json.Unmarshal([]byte(raw), &config); err != nil { + return fmt.Errorf("failed to parse sink config: %w", err) + } + if config.Parallelism != expected { + return fmt.Errorf("unexpected sink parallelism: %d", config.Parallelism) + } + return nil +} + +type sourceConfig struct { + Configs map[string]interface{} `json:"configs"` + Parallelism int `json:"parallelism"` +} + +func assertSourceParallelism(raw string, expected int) error { + if raw == "" { + return errors.New("empty source config result") + } + var config sourceConfig + if err := json.Unmarshal([]byte(raw), &config); err != nil { + return fmt.Errorf("failed to parse source config: %w", err) + } + if config.Parallelism != expected { + return fmt.Errorf("unexpected source parallelism: %d", config.Parallelism) + } + return nil +} + type consumeResponse struct { MessagesConsumed int `json:"messages_consumed"` Messages []consumeMessage `json:"messages"` diff --git a/docs/tools/pulsar_admin_sinks.md b/docs/tools/pulsar_admin_sinks.md index f957087..be63ef8 100644 --- a/docs/tools/pulsar_admin_sinks.md +++ b/docs/tools/pulsar_admin_sinks.md @@ -5,23 +5,23 @@ Manage Apache Pulsar Sinks for data movement and integration. Pulsar Sinks are c This tool provides complete lifecycle management for sink connectors: - **list**: List all sinks in a namespace - - `tenant` (string, required): The tenant name - - `namespace` (string, required): The namespace name + - `tenant` (string, optional): The tenant name (default: `public`) + - `namespace` (string, optional): The namespace name (default: `default`) - **get**: Get sink configuration - - `tenant` (string, required): The tenant name - - `namespace` (string, required): The namespace name + - `tenant` (string, optional): The tenant name (default: `public`) + - `namespace` (string, optional): The namespace name (default: `default`) - `name` (string, required): The sink name - **status**: Get runtime status of a sink (instances, metrics) - - `tenant` (string, required): The tenant name - - `namespace` (string, required): The namespace name + - `tenant` (string, optional): The tenant name (default: `public`) + - `namespace` (string, optional): The namespace name (default: `default`) - `name` (string, required): The sink name - **create**: Deploy a new sink connector - - `tenant` (string, required): The tenant name - - `namespace` (string, required): The namespace name - - `name` (string, required): The sink name + - `tenant` (string, optional): The tenant name (default: `public`) + - `namespace` (string, optional): The namespace name (default: `default`) + - `name` (string, required): The sink name (can be provided via `sink-config-file`) - Either `archive` or `sink-type` must be specified (but not both): - `archive` (string): Path to the archive file containing sink code - `sink-type` (string): Built-in connector type to use (e.g., 'jdbc', 'elastic-search', 'kafka') @@ -29,36 +29,58 @@ This tool provides complete lifecycle management for sink connectors: - `inputs` (array): The sink's input topics (array of strings) - `topics-pattern` (string): TopicsPattern to consume from topics matching the pattern (regex) - `subs-name` (string, optional): Pulsar subscription name for input topic consumer + - `subs-position` (string, optional): Subscription position (`Latest` or `Earliest`) + - `classname` (string, optional): Sink class name for custom archives + - `processing-guarantees` (string, optional): Delivery semantics + - `retain-ordering` (boolean, optional): Preserve message ordering + - `retain-key-ordering` (boolean, optional): Preserve key ordering + - `auto-ack` (boolean, optional): Auto-ack messages + - `cleanup-subscription` (boolean, optional): Delete subscription on sink delete (default: true) - `parallelism` (number, optional): Number of instances to run concurrently (default: 1) + - `cpu` / `ram` / `disk` (number, optional): Resource allocation per instance + - `custom-serde-inputs` (object, optional): Map of input topics to SerDe class names + - `custom-schema-inputs` (object, optional): Map of input topics to schema type/class + - `input-specs` (object, optional): Map of input topics to consumer config + - `max-redeliver-count` (number, optional): Max redeliver attempts + - `dead-letter-topic` (string, optional): Dead letter topic + - `timeout-ms` (number, optional): Processing timeout in milliseconds + - `negative-ack-redelivery-delay-ms` (number, optional): Negative ack redelivery delay + - `custom-runtime-options` (string, optional): Runtime customization options + - `secrets` (object, optional): Secrets configuration map + - `sink-config-file` (string, optional): Path to YAML sink config file - `sink-config` (object, optional): Connector-specific configuration parameters + - `transform-function` (string, optional): Transform function + - `transform-function-classname` (string, optional): Transform class name + - `transform-function-config` (string, optional): Transform config - **update**: Update an existing sink connector - - `tenant` (string, required): The tenant name - - `namespace` (string, required): The namespace name - - `name` (string, required): The sink name + - `tenant` (string, optional): The tenant name (default: `public`) + - `namespace` (string, optional): The namespace name (default: `default`) + - `name` (string, required): The sink name (can be provided via `sink-config-file`) - Parameters similar to `create` operation (all optional during update) + - `update-auth-data` (boolean, optional): Update auth data during update - **delete**: Delete a sink - - `tenant` (string, required): The tenant name - - `namespace` (string, required): The namespace name + - `tenant` (string, optional): The tenant name (default: `public`) + - `namespace` (string, optional): The namespace name (default: `default`) - `name` (string, required): The sink name - **start**: Start a stopped sink - - `tenant` (string, required): The tenant name - - `namespace` (string, required): The namespace name + - `tenant` (string, optional): The tenant name (default: `public`) + - `namespace` (string, optional): The namespace name (default: `default`) - `name` (string, required): The sink name - **stop**: Stop a running sink - - `tenant` (string, required): The tenant name - - `namespace` (string, required): The namespace name + - `tenant` (string, optional): The tenant name (default: `public`) + - `namespace` (string, optional): The namespace name (default: `default`) - `name` (string, required): The sink name - **restart**: Restart a sink - - `tenant` (string, required): The tenant name - - `namespace` (string, required): The namespace name + - `tenant` (string, optional): The tenant name (default: `public`) + - `namespace` (string, optional): The namespace name (default: `default`) - `name` (string, required): The sink name - **list-built-in**: List all built-in sink connectors available in the system - No parameters required -Built-in sink connectors are available for common systems like Kafka, JDBC, Elasticsearch, and cloud storage. Sinks follow the tenant/namespace/name hierarchy for organization and access control, can scale through parallelism configuration, and support configurable subscription types. Sinks require proper permissions to access their input topics. \ No newline at end of file +Built-in sink connectors are available for common systems like Kafka, JDBC, Elasticsearch, and cloud storage. Sinks follow the tenant/namespace/name hierarchy for organization and access control, can scale through parallelism configuration, and support configurable subscription types. Sinks require proper permissions to access their input topics. diff --git a/docs/tools/pulsar_admin_sources.md b/docs/tools/pulsar_admin_sources.md index cc2e738..53141ad 100644 --- a/docs/tools/pulsar_admin_sources.md +++ b/docs/tools/pulsar_admin_sources.md @@ -5,61 +5,71 @@ Manage Apache Pulsar Sources for data ingestion and integration. Pulsar Sources This tool provides complete lifecycle management for source connectors: - **list**: List all sources in a namespace - - `tenant` (string, required): The tenant name - - `namespace` (string, required): The namespace name + - `tenant` (string, optional): The tenant name (default: `public`) + - `namespace` (string, optional): The namespace name (default: `default`) - **get**: Get source configuration - - `tenant` (string, required): The tenant name - - `namespace` (string, required): The namespace name + - `tenant` (string, optional): The tenant name (default: `public`) + - `namespace` (string, optional): The namespace name (default: `default`) - `name` (string, required): The source name - **status**: Get runtime status of a source (instances, metrics) - - `tenant` (string, required): The tenant name - - `namespace` (string, required): The namespace name + - `tenant` (string, optional): The tenant name (default: `public`) + - `namespace` (string, optional): The namespace name (default: `default`) - `name` (string, required): The source name - **create**: Deploy a new source connector - - `tenant` (string, required): The tenant name - - `namespace` (string, required): The namespace name - - `name` (string, required): The source name + - `tenant` (string, optional): The tenant name (default: `public`) + - `namespace` (string, optional): The namespace name (default: `default`) + - `name` (string, required): The source name (can be provided via `source-config-file`) - `destination-topic-name` (string, required): Topic where data will be written - Either `archive` or `source-type` must be specified (but not both): - `archive` (string): Path to the archive file containing source code - `source-type` (string): Built-in connector type to use (e.g., 'kafka', 'jdbc') + - `source-config-file` (string, optional): YAML file with source configuration - `deserialization-classname` (string, optional): SerDe class for the source - `schema-type` (string, optional): Schema type for encoding messages (e.g., 'avro', 'json') - `classname` (string, optional): Source class name if using custom implementation - `processing-guarantees` (string, optional): Delivery semantics ('atleast_once', 'atmost_once', 'effectively_once') - `parallelism` (number, optional): Number of instances to run concurrently (default: 1) + - `cpu` (number, optional): CPU cores per instance + - `ram` (number, optional): RAM bytes per instance + - `disk` (number, optional): Disk bytes per instance - `source-config` (object, optional): Connector-specific configuration parameters + - `producer-config` (object, optional): Producer configuration + - `batch-builder` (string, optional): Batch builder type + - `batch-source-config` (object, optional): Batch source configuration + - `custom-runtime-options` (string, optional): Runtime customization options + - `secrets` (object, optional): Secrets configuration map - **update**: Update an existing source connector - - `tenant` (string, required): The tenant name - - `namespace` (string, required): The namespace name - - `name` (string, required): The source name + - `tenant` (string, optional): The tenant name (default: `public`) + - `namespace` (string, optional): The namespace name (default: `default`) + - `name` (string, required): The source name (can be provided via `source-config-file`) - Parameters similar to `create` operation (all optional during update) + - `update-auth-data` (boolean, optional): Whether to update auth data - **delete**: Delete a source - - `tenant` (string, required): The tenant name - - `namespace` (string, required): The namespace name + - `tenant` (string, optional): The tenant name (default: `public`) + - `namespace` (string, optional): The namespace name (default: `default`) - `name` (string, required): The source name - **start**: Start a stopped source - - `tenant` (string, required): The tenant name - - `namespace` (string, required): The namespace name + - `tenant` (string, optional): The tenant name (default: `public`) + - `namespace` (string, optional): The namespace name (default: `default`) - `name` (string, required): The source name - **stop**: Stop a running source - - `tenant` (string, required): The tenant name - - `namespace` (string, required): The namespace name + - `tenant` (string, optional): The tenant name (default: `public`) + - `namespace` (string, optional): The namespace name (default: `default`) - `name` (string, required): The source name - **restart**: Restart a source - - `tenant` (string, required): The tenant name - - `namespace` (string, required): The namespace name + - `tenant` (string, optional): The tenant name (default: `public`) + - `namespace` (string, optional): The namespace name (default: `default`) - `name` (string, required): The source name - **list-built-in**: List all built-in source connectors available in the system - No parameters required -Built-in source connectors are available for common systems like Kafka, JDBC, AWS services, and more. Sources follow the tenant/namespace/name hierarchy for organization and access control, can scale through parallelism configuration, and support various processing guarantees. \ No newline at end of file +Built-in source connectors are available for common systems like Kafka, JDBC, AWS services, and more. Sources follow the tenant/namespace/name hierarchy for organization and access control, can scale through parallelism configuration, and support various processing guarantees. diff --git a/go.mod b/go.mod index 3d63a7b..f2f5c0d 100644 --- a/go.mod +++ b/go.mod @@ -1,103 +1,133 @@ module github.com/streamnative/streamnative-mcp-server -go 1.24.4 +go 1.25.6 require ( github.com/99designs/keyring v1.2.2 - github.com/apache/pulsar-client-go v0.13.1 + github.com/apache/pulsar-client-go v0.18.0-candidate-1.0.20251222030102-3bb7d4eff361 github.com/golang-jwt/jwt v3.2.1+incompatible github.com/google/go-cmp v0.7.0 - github.com/hamba/avro/v2 v2.28.0 + github.com/hamba/avro/v2 v2.30.0 github.com/mark3labs/mcp-go v0.43.2 github.com/mitchellh/go-homedir v1.1.0 github.com/pkg/errors v0.9.1 github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.9.1 github.com/spf13/viper v1.20.1 - github.com/streamnative/pulsarctl v0.4.3-0.20250312214758-e472faec284b + github.com/streamnative/pulsarctl v0.6.2-0.20260203104241-b294ba45a405 github.com/streamnative/streamnative-mcp-server/sdk/sdk-apiserver v0.0.0-20250506174209-b67ea08ddd82 github.com/streamnative/streamnative-mcp-server/sdk/sdk-kafkaconnect v0.0.0-00010101000000-000000000000 - github.com/stretchr/testify v1.10.0 + github.com/stretchr/testify v1.11.1 github.com/twmb/franz-go v1.18.1 github.com/twmb/franz-go/pkg/kadm v1.16.0 github.com/twmb/franz-go/pkg/sr v1.3.0 github.com/twmb/tlscfg v1.2.1 - golang.org/x/oauth2 v0.27.0 + golang.org/x/oauth2 v0.34.0 gopkg.in/yaml.v2 v2.4.0 - k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 + k8s.io/utils v0.0.0-20251222233032-718f0e51e6d2 ) require ( github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect - github.com/AthenZ/athenz v1.10.39 // indirect + github.com/AthenZ/athenz v1.12.31 // indirect github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect - github.com/DataDog/zstd v1.5.0 // indirect + github.com/DataDog/zstd v1.5.7 // indirect + github.com/RoaringBitmap/roaring/v2 v2.14.4 // indirect github.com/ardielle/ardielle-go v1.5.2 // indirect github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/bits-and-blooms/bitset v1.4.0 // indirect + github.com/bits-and-blooms/bitset v1.24.4 // indirect github.com/buger/jsonparser v1.1.1 // indirect + github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/danieljoos/wincred v1.1.2 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dvsekhvalnov/jose2go v1.7.0 // indirect + github.com/emirpasic/gods v1.18.1 // indirect github.com/fatih/color v1.7.0 // indirect - github.com/fsnotify/fsnotify v1.8.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fsnotify/fsnotify v1.9.0 // indirect + github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/ghodss/yaml v1.0.0 // indirect + github.com/go-jose/go-jose/v4 v4.1.3 // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect github.com/golang-jwt/jwt/v4 v4.5.2 // indirect - github.com/golang-jwt/jwt/v5 v5.2.2 // indirect + github.com/golang-jwt/jwt/v5 v5.3.0 // indirect github.com/golang/protobuf v1.5.4 // indirect + github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.6.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 // indirect github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect - github.com/hashicorp/errwrap v1.1.0 // indirect - github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/invopop/jsonschema v0.13.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.17.11 // indirect + github.com/klauspost/compress v1.18.2 // indirect github.com/kris-nova/logger v0.0.0-20181127235838-fd0d87064b06 // indirect github.com/kris-nova/lolgopher v0.0.0-20180921204813-313b3abb0d9b // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.2 // indirect github.com/mattn/go-isatty v0.0.8 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect + github.com/mschoch/smat v0.2.0 // indirect github.com/mtibben/percent v0.2.1 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/onsi/gomega v1.35.1 // indirect github.com/pelletier/go-toml/v2 v2.2.3 // indirect - github.com/pierrec/lz4 v2.0.5+incompatible // indirect - github.com/pierrec/lz4/v4 v4.1.22 // indirect + github.com/pierrec/lz4/v4 v4.1.23 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/client_golang v1.20.5 // indirect - github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.55.0 // indirect - github.com/prometheus/procfs v0.15.1 // indirect - github.com/rogpeppe/go-internal v1.12.0 // indirect + github.com/prometheus/client_golang v1.23.2 // indirect + github.com/prometheus/client_model v0.6.2 // indirect + github.com/prometheus/common v0.67.4 // indirect + github.com/prometheus/procfs v0.19.2 // indirect github.com/sagikazarmark/locafero v0.7.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/spf13/afero v1.12.0 // indirect github.com/spf13/cast v1.7.1 // indirect - github.com/spf13/pflag v1.0.6 // indirect + github.com/spf13/pflag v1.0.9 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/subosito/gotenv v1.6.0 // indirect + github.com/theparanoids/crypki v1.20.11 // indirect github.com/twmb/franz-go/pkg/kmsg v1.9.0 // indirect github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect + github.com/x448/float16 v0.8.4 // indirect github.com/yosida95/uritemplate/v3 v3.0.2 // indirect + go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0 // indirect + go.opentelemetry.io/otel v1.39.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.39.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.39.0 // indirect + go.opentelemetry.io/otel/metric v1.39.0 // indirect + go.opentelemetry.io/otel/sdk v1.39.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.39.0 // indirect + go.opentelemetry.io/otel/trace v1.39.0 // indirect + go.opentelemetry.io/proto/otlp v1.9.0 // indirect go.uber.org/atomic v1.11.0 // indirect go.uber.org/multierr v1.9.0 // indirect - golang.org/x/crypto v0.45.0 // indirect - golang.org/x/mod v0.29.0 // indirect - golang.org/x/sys v0.38.0 // indirect - golang.org/x/term v0.37.0 // indirect - golang.org/x/text v0.31.0 // indirect - google.golang.org/protobuf v1.36.1 // indirect + go.yaml.in/yaml/v2 v2.4.3 // indirect + golang.org/x/crypto v0.46.0 // indirect + golang.org/x/mod v0.31.0 // indirect + golang.org/x/net v0.48.0 // indirect + golang.org/x/sys v0.39.0 // indirect + golang.org/x/term v0.38.0 // indirect + golang.org/x/text v0.32.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20251222181119-0a764e51fe1b // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b // indirect + google.golang.org/grpc v1.77.0 // indirect + google.golang.org/protobuf v1.36.11 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/apimachinery v0.35.0 // indirect + k8s.io/client-go v0.35.0 // indirect + k8s.io/klog/v2 v2.130.1 // indirect + k8s.io/kube-openapi v0.0.0-20251125145642-4e65d59e963e // indirect + sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect + sigs.k8s.io/randfill v1.0.0 // indirect + sigs.k8s.io/structured-merge-diff/v6 v6.3.1 // indirect ) replace github.com/streamnative/streamnative-mcp-server/sdk/sdk-apiserver => ./sdk/sdk-apiserver diff --git a/go.sum b/go.sum index 1156150..e47d2ac 100644 --- a/go.sum +++ b/go.sum @@ -4,43 +4,41 @@ github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMb github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4= github.com/99designs/keyring v1.2.2 h1:pZd3neh/EmUzWONb35LxQfvuY7kiSXAq3HQd97+XBn0= github.com/99designs/keyring v1.2.2/go.mod h1:wes/FrByc8j7lFOAGLGSNEg8f/PaI3cgTBqhFkHUrPk= -github.com/AthenZ/athenz v1.10.39 h1:mtwHTF/v62ewY2Z5KWhuZgVXftBej1/Tn80zx4DcawY= -github.com/AthenZ/athenz v1.10.39/go.mod h1:3Tg8HLsiQZp81BJY58JBeU2BR6B/H4/0MQGfCwhHNEA= +github.com/AthenZ/athenz v1.12.31 h1:GQnRDLgivPlVvklSpH9gp+t/dho9DJTtt+hlLYo5TX8= +github.com/AthenZ/athenz v1.12.31/go.mod h1:6Siq4JOA4OjgYVgtTVIeHrb4HB2hEL8i4fx7aOFrgfY= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/DataDog/zstd v1.5.0 h1:+K/VEwIAaPcHiMtQvpLD4lqW7f0Gk3xdYZmI1hD+CXo= -github.com/DataDog/zstd v1.5.0/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= +github.com/DataDog/zstd v1.5.7 h1:ybO8RBeh29qrxIhCA9E8gKY6xfONU9T6G6aP9DTKfLE= +github.com/DataDog/zstd v1.5.7/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/Microsoft/hcsshim v0.11.5 h1:haEcLNpj9Ka1gd3B3tAEs9CpE0c+1IhoL59w/exYU38= -github.com/Microsoft/hcsshim v0.11.5/go.mod h1:MV8xMfmECjl5HdO7U/3/hFVnkmSBjAjmA09d4bExKcU= -github.com/apache/pulsar-client-go v0.13.1 h1:XAAKXjF99du7LP6qu/nBII1HC2nS483/vQoQIWmm5Yg= -github.com/apache/pulsar-client-go v0.13.1/go.mod h1:0X5UCs+Cv5w6Ds38EZebUMfyVUFIh+URF2BeipEVhIU= +github.com/RoaringBitmap/roaring/v2 v2.14.4 h1:4aKySrrg9G/5oRtJ3TrZLObVqxgQ9f1znCRBwEwjuVw= +github.com/RoaringBitmap/roaring/v2 v2.14.4/go.mod h1:oMvV6omPWr+2ifRdeZvVJyaz+aoEUopyv5iH0u/+wbY= +github.com/apache/pulsar-client-go v0.18.0-candidate-1.0.20251222030102-3bb7d4eff361 h1:Fb4j4v85TPq64FRp+QMLWaW3/Hg1Jg7TBWaZwPcSO9Y= +github.com/apache/pulsar-client-go v0.18.0-candidate-1.0.20251222030102-3bb7d4eff361/go.mod h1:/Zf8Q8bSSc6ndEJ8V1muIHf6ZWsMrHoQU+98Ww9pOeI= github.com/ardielle/ardielle-go v1.5.2 h1:TilHTpHIQJ27R1Tl/iITBzMwiUGSlVfiVhwDNGM3Zj4= github.com/ardielle/ardielle-go v1.5.2/go.mod h1:I4hy1n795cUhaVt/ojz83SNVCYIGsAFAONtv2Dr7HUI= -github.com/ardielle/ardielle-tools v1.5.4/go.mod h1:oZN+JRMnqGiIhrzkRN9l26Cej9dEx4jeNG6A+AdkShk= -github.com/aws/aws-sdk-go v1.32.6/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= 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/bits-and-blooms/bitset v1.4.0 h1:+YZ8ePm+He2pU3dZlIZiOeAKfrBkXi1lSrXJ/Xzgbu8= -github.com/bits-and-blooms/bitset v1.4.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= +github.com/bits-and-blooms/bitset v1.24.4 h1:95H15Og1clikBrKr/DuzMXkQzECs1M6hhoGXLwLQOZE= +github.com/bits-and-blooms/bitset v1.24.4/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= +github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= 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/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/containerd/containerd v1.7.18 h1:jqjZTQNfXGoEaZdW1WwPU0RqSn1Bm2Ay/KJPUuO8nao= -github.com/containerd/containerd v1.7.18/go.mod h1:IYEk9/IO6wAPUz2bCMVUbsfXjzw5UNP5fLz4PsUygQ4= -github.com/containerd/errdefs v0.1.0 h1:m0wCRBiu1WJT/Fr+iOoQHMQS/eP5myQ8lCv4Dz5ZURM= -github.com/containerd/errdefs v0.1.0/go.mod h1:YgWiiHtLmSeBrvpw+UfPijzbLaB77mEG1WwJTDETIV0= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= -github.com/cpuguy83/dockercfg v0.3.1 h1:/FpZ+JaygUR/lZP2NlFI2DVfrOEMAIKP5wWEJdoYe9E= -github.com/cpuguy83/dockercfg v0.3.1/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= +github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= +github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= +github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= +github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0= github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0= @@ -52,31 +50,37 @@ github.com/dimfeld/httptreemux v5.0.1+incompatible h1:Qj3gVcDNoOthBAqftuD596rm4w github.com/dimfeld/httptreemux v5.0.1+incompatible/go.mod h1:rbUlSV+CCpv/SuqUTP/8Bk2O3LyUV436/yaRGkhP6Z0= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY= -github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v28.0.0+incompatible h1:Olh0KS820sJ7nPsBKChVhk5pzqcwDR15fumfAd/p9hM= +github.com/docker/docker v28.0.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dvsekhvalnov/jose2go v1.7.0 h1:bnQc8+GMnidJZA8zc6lLEAb4xNrIqHwO+9TzqvtQZPo= github.com/dvsekhvalnov/jose2go v1.7.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= -github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= +github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= -github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs= +github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= +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/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= -github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0= @@ -87,40 +91,32 @@ github.com/golang-jwt/jwt v3.2.1+incompatible h1:73Z+4BJcrTC+KczS6WvTPvRGOp1WmfE github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= -github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +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/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 h1:NmZ1PKzSTQbuGHw9DGPFomqkkLWMC+vZCkfs+FHv1Vg= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3/go.mod h1:zQrxl1YP88HQlA6i9c63DSVPFklWpGX4OWAc9bFuaH4= github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU= github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0= -github.com/hamba/avro/v2 v2.28.0 h1:E8J5D27biyAulWKNiEBhV85QPc9xRMCUCGJewS0KYCE= -github.com/hamba/avro/v2 v2.28.0/go.mod h1:9TVrlt1cG1kkTUtm9u2eO5Qb7rZXlYzoKqPt8TSH+TA= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= -github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= -github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hamba/avro/v2 v2.30.0 h1:OaIdh0+dZIJ331FO/+YYBwZZRdGVyyHuRSyHsjZLJoA= +github.com/hamba/avro/v2 v2.30.0/go.mod h1:X6gDhYv6DQVAT56VqOKuW+PLnQrEQqGB9l1nhlMdAdQ= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E= github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0= -github.com/jawher/mow.cli v1.0.4/go.mod h1:5hQj2V8g+qYmLUVWqu4Wuja1pI57M83EChYLVZ0sMKk= -github.com/jawher/mow.cli v1.2.0/go.mod h1:y+pcA3jBAdo/GIZx/0rFjw/K2bVEODP9rfZOfaiq8Ko= -github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= -github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk= +github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -145,25 +141,28 @@ github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= -github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg= -github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU= +github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo= +github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= +github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= +github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM= +github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw= github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs= github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= @@ -173,18 +172,16 @@ github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= -github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= +github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= +github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= -github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I= -github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU= -github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pierrec/lz4/v4 v4.1.23 h1:oJE7T90aYBGtFNrI8+KbETnPymobAhzRrR8Mu8n1yfU= +github.com/pierrec/lz4/v4 v4.1.23/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcRIPL34O4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -192,16 +189,16 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= -github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= -github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= -github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= -github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= -github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= -github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= -github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= -github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +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_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +github.com/prometheus/common v0.67.4 h1:yR3NqWO1/UyO1w2PhUvXlGQs/PtFmoveVO0KZ4+Lvsc= +github.com/prometheus/common v0.67.4/go.mod h1:gP0fq6YjjNCLssJCQp0yk4M8W6ikLURwkdd/YKtTbyI= +github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws= +github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo= github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k= @@ -221,26 +218,26 @@ github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= -github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= -github.com/streamnative/pulsarctl v0.4.3-0.20250312214758-e472faec284b h1:J1jG07SOD3nuO8d0UlB0LKUb8rohd2VPA7FWDFawT/4= -github.com/streamnative/pulsarctl v0.4.3-0.20250312214758-e472faec284b/go.mod h1:QBzQvWQtzXSgkIZ3YfJfSZhN3edM762c4q3V+XSQB3o= +github.com/streamnative/pulsarctl v0.6.2-0.20260203104241-b294ba45a405 h1:W7wAqfWFf1hYd3V0Hq0JNF7bI/p3tU8N0y4U5Ol0y/g= +github.com/streamnative/pulsarctl v0.6.2-0.20260203104241-b294ba45a405/go.mod h1:Ktbjxbep8YoHw/EKenl6c8VElRuqNAIz9LS5IAy30Ko= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/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.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= -github.com/testcontainers/testcontainers-go v0.32.0 h1:ug1aK08L3gCHdhknlTTwWjPHPS+/alvLJU/DRxTD/ME= -github.com/testcontainers/testcontainers-go v0.32.0/go.mod h1:CRHrzHLQhlXUsa5gXjTOfqIEJcrK5+xMDmBr/WMI88E= +github.com/testcontainers/testcontainers-go v0.35.0 h1:uADsZpTKFAtp8SLK+hMwSaa+X+JiERHtd4sQAFmXeMo= +github.com/testcontainers/testcontainers-go v0.35.0/go.mod h1:oEVBj5zrfJTrgjwONs1SsRbnBtH9OKl+IGl3UMcr2B4= +github.com/theparanoids/crypki v1.20.11 h1:0FZfFmmIoSenyT1SnvnyBJmK9kvKlyHmAXBH00MW7Kk= +github.com/theparanoids/crypki v1.20.11/go.mod h1:xtnD/Nk357e6DiLOQjFAFi93bM8On83QScnoj3QA6oU= github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= @@ -257,83 +254,98 @@ github.com/twmb/tlscfg v1.2.1 h1:IU2efmP9utQEIV2fufpZjPq7xgcZK4qu25viD51BB44= github.com/twmb/tlscfg v1.2.1/go.mod h1:GameEQddljI+8Es373JfQEBvtI4dCTLKWGJbqT2kErs= github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc= github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4= github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4= github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8= -go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw= -go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8= -go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc= -go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8= -go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4= -go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ= +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/otelhttp v0.64.0 h1:ssfIgGNANqpVFCndZvcuyKbl0g+UAVcbBcqGkG28H0Y= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0/go.mod h1:GQ/474YrbE4Jx8gZ4q5I4hrhUzM6UPzyrqJYV2AqPoQ= +go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= +go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.39.0 h1:cEf8jF6WbuGQWUVcqgyWtTR0kOOAWY1DYZ+UhvdmQPw= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.39.0/go.mod h1:k1lzV5n5U3HkGvTCJHraTAGJ7MqsgL1wrGwTj1Isfiw= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.39.0 h1:nKP4Z2ejtHn3yShBb+2KawiXgpn8In5cT7aO2wXuOTE= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.39.0/go.mod h1:NwjeBbNigsO4Aj9WgM0C+cKIrxsZUaRmZUO7A8I7u8o= +go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= +go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= +go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= +go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= +go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= +go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= +go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= +go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= +go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A= +go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= -golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= -golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= -golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= -golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= -golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= -golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= +go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= +golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= +golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI= +golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg= +golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= +golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= +golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw= +golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= -golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= -golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= -golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 h1:ToEetK57OidYuqD4Q5w+vfEnPvPpuTwedCNVohYJfNk= -google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 h1:TqExAhdPaB60Ux47Cn0oLV07rGnxZzIsaRhQaqS666A= -google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8/go.mod h1:lcTa1sDdWEIHMWlITnIczmw5w60CF9ffkb8Z+DVmmjA= -google.golang.org/grpc v1.67.3 h1:OgPcDAFKHnH8X3O4WcO4XUc8GRDeKsKReqbQtiCj7N8= -google.golang.org/grpc v1.67.3/go.mod h1:YGaHCc6Oap+FzBJTZLBzkGSYt/cvGPFTPxkn7QfSU8s= -google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= -google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q= +golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg= +golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= +golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +google.golang.org/genproto/googleapis/api v0.0.0-20251222181119-0a764e51fe1b h1:uA40e2M6fYRBf0+8uN5mLlqUtV192iiksiICIBkYJ1E= +google.golang.org/genproto/googleapis/api v0.0.0-20251222181119-0a764e51fe1b/go.mod h1:Xa7le7qx2vmqB/SzWUBa7KdMjpdpAHlh5QCSnjessQk= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b h1:Mv8VFug0MP9e5vUxfBcE3vUkV6CImK3cMNMIDFjmzxU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM= +google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= -gopkg.in/square/go-jose.v2 v2.4.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro= -k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +k8s.io/apimachinery v0.35.0 h1:Z2L3IHvPVv/MJ7xRxHEtk6GoJElaAqDCCU0S6ncYok8= +k8s.io/apimachinery v0.35.0/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns= +k8s.io/client-go v0.35.0 h1:IAW0ifFbfQQwQmga0UdoH0yvdqrbwMdq9vIFEhRpxBE= +k8s.io/client-go v0.35.0/go.mod h1:q2E5AAyqcbeLGPdoRB+Nxe3KYTfPce1Dnu1myQdqz9o= +k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= +k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20251125145642-4e65d59e963e h1:iW9ChlU0cU16w8MpVYjXk12dqQ4BPFBEgif+ap7/hqQ= +k8s.io/kube-openapi v0.0.0-20251125145642-4e65d59e963e/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ= +k8s.io/utils v0.0.0-20251222233032-718f0e51e6d2 h1:OfgiEo21hGiwx1oJUU5MpEaeOEg6coWndBkZF/lkFuE= +k8s.io/utils v0.0.0-20251222233032-718f0e51e6d2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk= +sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= +sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= +sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= +sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/structured-merge-diff/v6 v6.3.1 h1:JrhdFMqOd/+3ByqlP2I45kTOZmTRLBUm5pvRjeheg7E= +sigs.k8s.io/structured-merge-diff/v6 v6.3.1/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= +sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= +sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= diff --git a/go.work b/go.work index 92c9a56..ab09c2f 100644 --- a/go.work +++ b/go.work @@ -1,4 +1,4 @@ -go 1.24.4 +go 1.25.6 use . diff --git a/go.work.sum b/go.work.sum index 84e6fa8..46bf520 100644 --- a/go.work.sum +++ b/go.work.sum @@ -1,280 +1,216 @@ -cel.dev/expr v0.16.1 h1:NR0+oFYzR1CqLFhTAqg3ql59G9VfN8fKq1TCHJ6gq1g= -cel.dev/expr v0.16.1/go.mod h1:AsGA5zb3WruAEQeQng1RZdGEXmBj0jvMWh6l5SnNuC8= -cloud.google.com/go v0.65.0 h1:Dg9iHVQfrhq82rUNu9ZxUDrJLaxFUe/HlCVaLyRruq8= -cloud.google.com/go v0.116.0 h1:B3fRrSDkLRt5qSHWe40ERJvhvnQwdZiHu0bJOpldweE= -cloud.google.com/go v0.116.0/go.mod h1:cEPSRWPzZEswwdr9BxE6ChEn01dWlTaF05LiC2Xs70U= -cloud.google.com/go/auth v0.13.0 h1:8Fu8TZy167JkW8Tj3q7dIkr2v4cndv41ouecJx0PAHs= -cloud.google.com/go/auth v0.13.0/go.mod h1:COOjD9gwfKNKz+IIduatIhYJQIc0mG3H102r/EMxX6Q= -cloud.google.com/go/auth/oauth2adapt v0.2.6 h1:V6a6XDu2lTwPZWOawrAa9HUK+DB2zfJyTuciBG5hFkU= -cloud.google.com/go/auth/oauth2adapt v0.2.6/go.mod h1:AlmsELtlEBnaNTL7jCj8VQFLy6mbZv0s4Q7NGBeQ5E8= -cloud.google.com/go/bigquery v1.8.0 h1:PQcPefKFdaIzjQFbiyOgAqyx8q5djaE7x9Sqe712DPA= -cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= -cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= -cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I= -cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg= -cloud.google.com/go/datastore v1.1.0 h1:/May9ojXjRkPBNVrq+oWLqmWCkr4OU5uRY29bu0mRyQ= -cloud.google.com/go/iam v1.2.2 h1:ozUSofHUGf/F4tCNy/mu9tHLTaxZFLOUiKzjcgWHGIA= -cloud.google.com/go/iam v1.2.2/go.mod h1:0Ys8ccaZHdI1dEUilwzqng/6ps2YB6vRsjIe00/+6JY= +cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY= +cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw= +cloud.google.com/go v0.120.0 h1:wc6bgG9DHyKqF5/vQvX1CiZrtHnxJjBlKUyF9nP6meA= +cloud.google.com/go v0.120.0/go.mod h1:/beW32s8/pGRuj4IILWQNd4uuebeT4dkOhKmkfit64Q= +cloud.google.com/go/auth v0.16.4 h1:fXOAIQmkApVvcIn7Pc2+5J8QTMVbUGLscnSVNl11su8= +cloud.google.com/go/auth v0.16.4/go.mod h1:j10ncYwjX/g3cdX7GpEzsdM+d+ZNsXAbb6qXA7p1Y5M= +cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= +cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= +cloud.google.com/go/certificatemanager v1.9.6 h1:v5X8X+THKrS9OFZb6k0GRDP1WQxLXTdMko7OInBliw4= +cloud.google.com/go/certificatemanager v1.9.6/go.mod h1:vWogV874jKZkSRDFCMM3r7wqybv8WXs3XhyNff6o/Zo= +cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= +cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= +cloud.google.com/go/iam v1.5.2 h1:qgFRAGEmd8z6dJ/qyEchAuL9jpswyODjA2lS+w234g8= +cloud.google.com/go/iam v1.5.2/go.mod h1:SE1vg0N81zQqLzQEwxL2WI6yhetBdbNQuTvIKCSkUHE= +cloud.google.com/go/longrunning v0.6.7 h1:IGtfDWHhQCgCjwQjV9iiLnUta9LBCo8R9QmAFsS/PrE= +cloud.google.com/go/longrunning v0.6.7/go.mod h1:EAFV3IZAKmM56TyiE6VAP3VoTzhZzySwI/YI1s/nRsY= cloud.google.com/go/monitoring v1.21.2 h1:FChwVtClH19E7pJ+e0xUhJPGksctZNVOk2UhMmblmdU= cloud.google.com/go/monitoring v1.21.2/go.mod h1:hS3pXvaG8KgWTSz+dAdyzPrGUYmi2Q+WFX8g2hqVEZU= -cloud.google.com/go/pubsub v1.3.1 h1:ukjixP1wl0LpnZ6LWtZJ0mX5tBmjp1f8Sqer8Z2OMUU= -cloud.google.com/go/storage v1.10.0 h1:STgFzyU5/8miMl0//zKh2aQeTyeaUH3WN9bSUiJ09bA= +cloud.google.com/go/secretmanager v1.16.0 h1:19QT7ZsLJ8FSP1k+4esQvuCD7npMJml6hYzilxVyT+k= +cloud.google.com/go/secretmanager v1.16.0/go.mod h1://C/e4I8D26SDTz1f3TQcddhcmiC3rMEl0S1Cakvs3Q= cloud.google.com/go/storage v1.49.0 h1:zenOPBOWHCnojRd9aJZAyQXBYqkJkdQS42dxL55CIMw= cloud.google.com/go/storage v1.49.0/go.mod h1:k1eHhhpLvrPjVGfo0mOUPEJ4Y2+a/Hv5PiwehZI9qGU= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9 h1:VpgP7xuJadIUuKccphEpTJnWhS2jkQyMt6Y7pJCD7fY= -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 h1:3c8yed4lgqTt+oTQ+JNMDo+F4xprBf+O/il4ZC0nRLw= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0/go.mod h1:obipzmGjfSjam60XLwGfqUkJsfiheAl+TUjG+4yzyPM= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0 h1:sBEjpZlNHzK1voKq9695PJSX2o5NEXl7/OL3coiIY0c= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.1 h1:UQ0AhxogsIRZDkElkblfnwjc3IaltCm2HUMvezQaL7s= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.1/go.mod h1:jyqM3eLpJ3IbIFDTKVz2rF9T/xWGW0rIriGwnz8l9Tk= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.48.1 h1:8nn+rsCvTq9axyEh382S0PFLBeaFwNsT43IrPWzctRU= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.48.1/go.mod h1:viRWSEhtMZqz1rhwmOVKkWl6SwmVowfL9O2YR5gI2PE= -github.com/Microsoft/hcsshim v0.8.6 h1:ZfF0+zZeYdzMIVMZHKtDKJvLHj76XCuVae/jNkjj0IA= +github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= +github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= github.com/alecthomas/kingpin/v2 v2.4.0 h1:f48lwail6p8zpO1bC4TxtqACaGqHYA22qkHjHpqDjYY= github.com/alecthomas/kingpin/v2 v2.4.0/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE= -github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc= -github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= -github.com/apache/pulsar-client-go/oauth2 v0.0.0-20200715083626-b9f8c5cedefb h1:E1P0FudxDdj2RhbveZC9i3PwukLCA/4XQSkBS/dw6/I= -github.com/ardielle/ardielle-tools v1.5.4 h1:2uL/7wZRUF4LGV7r2eTaaeyhkBoqdiqEitSXMd6k8F8= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6 h1:G1bPvciwNyF7IUmKXNt9Ak3m6u9DE1rF+RmtIkBpVdA= -github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/aws/aws-sdk-go v1.32.6 h1:HoswAabUWgnrUF7X/9dr4WRgrr8DyscxXvTDm7Qw/5c= +github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b h1:mimo19zliBX/vSQ6PWWSL9lK8qwHozUj03+zLoEB8O0= +github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b/go.mod h1:fvzegU4vN3H1qMT+8wDmzjAcDONcgo2/SZ/TyfdUOFs= +github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/aws/aws-sdk-go-v2 v1.40.1 h1:difXb4maDZkRH0x//Qkwcfpdg1XQVXEAEs2DdXldFFc= +github.com/aws/aws-sdk-go-v2 v1.40.1/go.mod h1:MayyLB8y+buD9hZqkCW3kX1AKq07Y5pXxtgB+rRFhz0= +github.com/aws/aws-sdk-go-v2/config v1.32.3 h1:cpz7H2uMNTDa0h/5CYL5dLUEzPSLo2g0NkbxTRJtSSU= +github.com/aws/aws-sdk-go-v2/config v1.32.3/go.mod h1:srtPKaJJe3McW6T/+GMBZyIPc+SeqJsNPJsd4mOYZ6s= +github.com/aws/aws-sdk-go-v2/credentials v1.19.3 h1:01Ym72hK43hjwDeJUfi1l2oYLXBAOR8gNSZNmXmvuas= +github.com/aws/aws-sdk-go-v2/credentials v1.19.3/go.mod h1:55nWF/Sr9Zvls0bGnWkRxUdhzKqj9uRNlPvgV1vgxKc= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.15 h1:utxLraaifrSBkeyII9mIbVwXXWrZdlPO7FIKmyLCEcY= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.15/go.mod h1:hW6zjYUDQwfz3icf4g2O41PHi77u10oAzJ84iSzR/lo= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.15 h1:Y5YXgygXwDI5P4RkteB5yF7v35neH7LfJKBG+hzIons= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.15/go.mod h1:K+/1EpG42dFSY7CBj+Fruzm8PsCGWTXJ3jdeJ659oGQ= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.15 h1:AvltKnW9ewxX2hFmQS0FyJH93aSvJVUEFvXfU+HWtSE= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.15/go.mod h1:3I4oCdZdmgrREhU74qS1dK9yZ62yumob+58AbFR4cQA= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc= +github.com/aws/aws-sdk-go-v2/service/acm v1.37.16 h1:a2NJDCO0LUIVl8DP9JTIoIVNS1vn1D0LU9sXCA5YeRI= +github.com/aws/aws-sdk-go-v2/service/acm v1.37.16/go.mod h1:RYbGNeUsxAX780kdQFzdeDF0leV98Qh5YeMhc1zU+n8= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 h1:0ryTNEdJbzUCEWkVXEXoqlXV72J5keC1GvILMOuD00E= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4/go.mod h1:HQ4qwNZh32C3CBeO6iJLQlgtMzqeG17ziAA/3KDJFow= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.15 h1:3/u/4yZOffg5jdNk1sDpOQ4Y+R6Xbh+GzpDrSZjuy3U= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.15/go.mod h1:4Zkjq0FKjE78NKjabuM4tRXKFzUJWXgP0ItEZK8l7JU= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.40.3 h1:QYBY43OlvzRPww1gSZ1kihyqzXg32rweA3fql5ubSLA= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.40.3/go.mod h1:STWNrwWdskQ0J7amsVBxHM6DPrpNgJS2GBcUhC7pDeU= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.3 h1:d/6xOGIllc/XW1lzG9a4AUBMmpLA9PXcQnVPTuHHcik= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.3/go.mod h1:fQ7E7Qj9GiW8y0ClD7cUJk3Bz5Iw8wZkWDHsTe8vDKs= +github.com/aws/aws-sdk-go-v2/service/ssm v1.67.5 h1:YKGgwB1rye0JpV10Bfma3cZdQzX61j2HPWQw+YxWvrQ= +github.com/aws/aws-sdk-go-v2/service/ssm v1.67.5/go.mod h1:eBDSa0vuYB0lalpNxavIw80Q4Ksy08bhHHbT0aWa4tE= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.6 h1:8sTTiw+9yuNXcfWeqKF2x01GqCF49CpP4Z9nKrrk/ts= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.6/go.mod h1:8WYg+Y40Sn3X2hioaaWAAIngndR8n1XFdRPPX+7QBaM= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.11 h1:E+KqWoVsSrj1tJ6I/fjDIu5xoS2Zacuu1zT+H7KtiIk= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.11/go.mod h1:qyWHz+4lvkXcr3+PoGlGHEI+3DLLiU6/GdrFfMaAhB0= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.3 h1:tzMkjh0yTChUqJDgGkcDdxvZDSrJ/WB6R6ymI5ehqJI= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.3/go.mod h1:T270C0R5sZNLbWUe8ueiAF42XSZxxPocTaGSgs5c/60= +github.com/aws/smithy-go v1.24.0 h1:LpilSUItNPFr1eY85RYgTIg5eIEPtvFbskaFcmmIUnk= +github.com/aws/smithy-go v1.24.0/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0= github.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b h1:AP/Y7sqYicnjGDfD5VcY4CIfh1hRXBUavxrvELjTiOE= github.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b/go.mod h1:ac9efd0D1fsDb3EJvhqgXRbFx7bs2wqZ10HQPeU8U/Q= -github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk= +github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g= github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= -github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= -github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f h1:WBZRG4aNOuI15bLRrCgN8fCq8E5Xuty6jGbmSNEvSsU= -github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 h1:QVw89YDxXxEe+l8gU8ETbOasdwEV+avkR75ZzsVV9WI= -github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= -github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc h1:TP+534wVlf61smEIq1nwLLAjQVEK2EADoW3CX9AuT+8= -github.com/coreos/etcd v3.3.10+incompatible h1:jFneRYjIvLMLhDLCzuTuU4rSJUjRplcJQ7pD7MnhC04= -github.com/coreos/go-etcd v2.0.0+incompatible h1:bXhRBIXoTm9BYHS3gE0TtQuyNZyeEMux2sDi4oo5YOo= -github.com/coreos/go-semver v0.2.0 h1:3Jm3tLmsgAYcjC+4Up7hJrFBPr+n7rAqYeSw/SZazuY= -github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= +github.com/cncf/xds/go v0.0.0-20251022180443-0feb69152e9f h1:Y8xYupdHxryycyPlc9Y+bSQAYZnetRJ70VMVKm5CKI0= +github.com/cncf/xds/go v0.0.0-20251022180443-0feb69152e9f/go.mod h1:HlzOvOjVBOfTGSRXRyY0OiCS/3J1akRGQQpRO/7zyF4= github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0= -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= -github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible h1:dvc1KSkIYTVjZgHf/CTC2diTYC8PzhaA5sFISRfNVrE= -github.com/dvsekhvalnov/jose2go v1.6.0 h1:Y9gnSnP4qEI0+/uQkHvFXeD2PLPJeXEL+ySMEA2EjTY= -github.com/dvsekhvalnov/jose2go v1.6.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU= -github.com/envoyproxy/go-control-plane v0.9.4 h1:rEvIZUSZ3fx39WIi3JkQqQBitGwpELBIYWeBVh6wn+E= -github.com/envoyproxy/go-control-plane v0.13.1 h1:vPfJZCkob6yTMEgS+0TwfTUfbHjfy/6vOJ8hUWX/uXE= -github.com/envoyproxy/go-control-plane v0.13.1/go.mod h1:X45hY0mufo6Fd0KW3rqsGvQMw58jvjymeCzBU3mWyHw= -github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A= -github.com/envoyproxy/protoc-gen-validate v1.1.0 h1:tntQDh69XqOCOZsDz0lVJQez/2L6Uu2PdjCQwWCJ3bM= -github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4= +github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU= +github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/envoyproxy/go-control-plane v0.13.5-0.20251024222203-75eaa193e329 h1:K+fnvUM0VZ7ZFJf0n4L/BRlnsb9pL/GuDG6FqaH+PwM= +github.com/envoyproxy/go-control-plane v0.13.5-0.20251024222203-75eaa193e329/go.mod h1:Alz8LEClvR7xKsrq3qzoc4N0guvVNSS8KmSChGYr9hs= +github.com/envoyproxy/go-control-plane/envoy v1.36.0 h1:yg/JjO5E7ubRyKX3m07GF3reDNEnfOboJ0QySbH736g= +github.com/envoyproxy/go-control-plane/envoy v1.36.0/go.mod h1:ty89S1YCCVruQAm9OtKeEkQLTb+Lkz0k8v9W0Oxsv98= +github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI= +github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4= +github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8= +github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU= github.com/ettle/strcase v0.2.0 h1:fGNiVF21fHXpX1niBgk0aROov1LagYsOwV/xqKDKR/Q= github.com/ettle/strcase v0.2.0/go.mod h1:DajmHElDSaX76ITe3/VHVyMin4LWSJN5Z909Wp+ED1A= -github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f h1:Wl78ApPPB2Wvf/TIe2xdyJxTlb6obmF18d8QdkxNDu4= -github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f/go.mod h1:OSYXu++VVOHnXeitef/D8n/6y4QV8uLHSFXX4NeXMGc= -github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4 h1:WtGNWLvXpe6ZudgnXrq0barxBImvnnJoMEhXAzcbM0I= -github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= -github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= -github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-redis/redis v6.15.6+incompatible h1:H9evprGPLI8+ci7fxQx6WNZHJSb7be8FqJQRhdQZ5Sg= -github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= -github.com/go-viper/mapstructure/v2 v2.3.0 h1:27XbWsHIqhbdR5TIC911OfYvgSaW93HM+dX7970Q7jk= -github.com/go-viper/mapstructure/v2 v2.3.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= -github.com/golang-jwt/jwt v3.2.1+incompatible h1:73Z+4BJcrTC+KczS6WvTPvRGOp1WmfEP4Q1lOd9Z/+c= -github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo= -github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= -github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= +github.com/golang/glog v1.2.5 h1:DrW6hGnjIhtvhOIiAKT6Psh/Kd/ldepEa81DKeiRJ5I= +github.com/golang/glog v1.2.5/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.4.3 h1:GV+pQPG/EUUbkh47niozDcADz6go/dUwhVzdUQHIVRw= -github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc= -github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= -github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= -github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= -github.com/google/martian/v3 v3.0.0 h1:pMen7vLs8nvgEYhywH3KDWJIJTeEr2ULsVWHWYHQyBs= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99 h1:Ak8CrdlwwXwAZxzS66vgPt4U8yUZX7JwLvVR58FN5jM= -github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 h1:5iH8iuqE5apketRbSFBy+X1V0o+l+8NF1avt4HWl7cA= -github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= -github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA= -github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM= -github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA= -github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw= -github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA= -github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= -github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q= -github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA= -github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= -github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc= -github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6 h1:UDMh68UUwekSh5iP2OMhRRZJiiBccgV7axzUG8vi56c= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs= +github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= +github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo= +github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= +github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= +github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= +github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4= +github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= +github.com/googleapis/gax-go/v2 v2.15.0 h1:SyjDc1mGgZU5LncH8gimWo9lW1DtIfPibOG81vgd/bo= +github.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= +github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo= +github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA= +github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= +github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= +github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/jawher/mow.cli v1.2.0 h1:e6ViPPy+82A/NFF/cfbq3Lr6q4JHKT9tyHwTCcUQgQw= -github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc= -github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc= +github.com/jawher/mow.cli v1.2.0/go.mod h1:y+pcA3jBAdo/GIZx/0rFjw/K2bVEODP9rfZOfaiq8Ko= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d h1:Z+RDyXzjKE0i2sTjZ/b1uxiGtPhFy34Ou/Tk0qwN0kM= -github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= -github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= -github.com/mark3labs/mcp-go v0.23.1 h1:RzTzZ5kJ+HxwnutKA4rll8N/pKV6Wh5dhCmiJUu5S9I= -github.com/mark3labs/mcp-go v0.23.1/go.mod h1:rXqOudj/djTORU/ThxYx8fqEVj/5pvTuuebQ2RC7uk4= -github.com/mark3labs/mcp-go v0.34.0 h1:eWy7WBGvhk6EyAAyVzivTCprE52iXJwNtvHV6Cv3bR0= -github.com/mark3labs/mcp-go v0.34.0/go.mod h1:rXqOudj/djTORU/ThxYx8fqEVj/5pvTuuebQ2RC7uk4= github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU= +github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= +github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU= +github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/olekukonko/tablewriter v0.0.1 h1:b3iUnf1v+ppJiOfNX4yxxqfWKMQPZR5yoh8urCTFX88= github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= -github.com/onsi/ginkgo/v2 v2.20.1 h1:YlVIbqct+ZmnEph770q9Q7NVAz4wwIiVNahee6JyUzo= -github.com/onsi/ginkgo/v2 v2.20.1/go.mod h1:lG9ey2Z29hR41WMVthyJBGUBcBhGOtoPF2VFMvBXFCI= -github.com/opencontainers/runc v0.1.1 h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJGY8Y= +github.com/onsi/ginkgo/v2 v2.27.2 h1:LzwLj0b89qtIy6SSASkzlNvX6WktqurSHwkk2ipF/Ns= +github.com/onsi/ginkgo/v2 v2.27.2/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= -github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= +github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= +github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= +github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pkg/sftp v1.13.7 h1:uv+I3nNJvlKZIQGSr8JVQLNHFU9YhhNpvC14Y6KgmSM= github.com/pkg/sftp v1.13.7/go.mod h1:KMKI0t3T6hfA+lTR/ssZdunHo+uwq7ghoN09/FSu3DY= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM= -github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk= -github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo= +github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= -github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= -github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= -github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= -github.com/spf13/viper v1.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M= -github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= -github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8 h1:3SVOIvH7Ae1KRYyQWRjXWJEA9sS/c/pjvH++55Gr648= +github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo= +github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs= +github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc h1:24heQPtnFR+yfntqhI3oAu9i27nEojcQ4NuBQOo5ZFA= +github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc/go.mod h1:f93CXfllFsO9ZQVq+Zocb1Gp4G5Fz0b0rXHLOzt/Djc= github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77 h1:ESFSdwYZvkeru3RtdrYueztKhOBCSAAzS4Gf+k0tEow= -github.com/yuin/goldmark v1.1.32 h1:5tjfNdR2ki3yYQ842+eX2sQHeiwpKJ0RnHO4IYOc4V8= -go.opencensus.io v0.22.4 h1:LYy1Hy3MJdrCdMwwzxA/dRok4ejH+RwNGbuoD9fCjto= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/contrib/detectors/gcp v1.29.0 h1:TiaiXB4DpGD3sdzNlYQxruQngn5Apwzi1X0DRhuGvDQ= -go.opentelemetry.io/contrib/detectors/gcp v1.29.0/go.mod h1:GW2aWZNwR2ZxDLdv8OyC2G8zkRoQBuURgV7RPQgcPoU= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 h1:r6I7RJCN86bpD/FQwedZ0vSixDpwuWREjW9oRMsmqDc= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0/go.mod h1:B9yO6b04uB80CzjedvewuqDhxJxi11s7/GtiGa8bAjI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0 h1:QY7/0NeRPKlzusf40ZE4t1VlMKbqSNT7cJRYzWuja0s= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0/go.mod h1:HVkSiDhTM9BoUJU8qE6j2eSWLLXvi1USXjyd2BXT8PY= -go.opentelemetry.io/otel/sdk v1.27.0 h1:mlk+/Y1gLPLn84U4tI8d3GNJmGT/eXe3ZuOXN9kTWmI= -go.opentelemetry.io/otel/sdk v1.27.0/go.mod h1:Ha9vbLwJE6W86YstIywK2xFfPjbWlCuwPtMkKdz/Y4A= -go.opentelemetry.io/otel/sdk v1.29.0 h1:vkqKjk7gwhS8VaWb0POZKmIEDimRCMsopNYnriHyryo= -go.opentelemetry.io/otel/sdk v1.29.0/go.mod h1:pM8Dx5WKnvxLCb+8lG1PRNIDxu9g9b9g59Qr7hfAAok= -go.opentelemetry.io/otel/sdk/metric v1.29.0 h1:K2CfmJohnRgvZ9UAj2/FhIf/okdWcNdBwe1m8xFXiSY= -go.opentelemetry.io/otel/sdk/metric v1.29.0/go.mod h1:6zZLdCl2fkauYoZIOn/soQIDSWFmNSRcICarHfuhNJQ= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= -golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= -golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= -golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6 h1:QE6XYQK6naiK1EPAe1g/ILLxN5RBoH5xkJk3CqlMI/Y= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs= -golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= -golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= -golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= -golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= -golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= -golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= -golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= -golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642 h1:B6caxRw+hozq68X2MY7jEpZh/cr4/aHLv9xU8Kkadrw= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= -golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= -golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= -golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= -golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= -golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= -golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= -golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg= -golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= -golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= -golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d h1:W07d4xkoAUSNOkOzdzXCdFGxT7o2rW4q8M34tB2i//k= -golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= -golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= -golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY= -golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY= -golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= -golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= -google.golang.org/api v0.30.0 h1:yfrXXP61wVuLb0vBcG6qaOoIoqYEzOQS8jum51jkv2w= -google.golang.org/api v0.215.0 h1:jdYF4qnyczlEz2ReWIsosNLDuzXyvFHJtI5gcr0J7t0= -google.golang.org/api v0.215.0/go.mod h1:fta3CVtuJYOEdugLNWm6WodzOS8KdFckABwN4I40hzY= -google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= -google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987 h1:PDIOdWxZ8eRizhKa1AAvY53xsvLB1cWorMjslvY3VA8= -google.golang.org/genproto v0.0.0-20241118233622-e639e219e697/go.mod h1:JJrvXBWRZaFMxBufik1a4RpFw4HhgVtBBWQeQgUj2cc= -google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 h1:RFiFrvy37/mpSpdySBDrUdipW/dHwsRwh3J3+A9VgT4= -google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237/go.mod h1:Z5Iiy3jtmioajWHDGFk7CeugTyHtPvMHA4UTmUkyalE= -google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 h1:CkkIfIt50+lT6NHAVoRYEyAvQGFM7xEwXUUywFvEb3Q= -google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576/go.mod h1:1R3kvZ1dtP3+4p4d3G8uJ8rFk/fWlScl38vanWACI08= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b h1:ZlWIi1wSK56/8hn4QcBp/j9M7Gt3U/3hZw3mC7vDICo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= -google.golang.org/grpc v1.31.0 h1:T7P4R73V3SSDPhH7WW7ATbfViLtmamH0DKrP3f9AuDI= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= -gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= -gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= -gopkg.in/square/go-jose.v2 v2.4.1 h1:H0TmLt7/KmzlrDOpa1F+zr0Tk90PbJYBfsVUmRLrf9Y= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= -gotest.tools v0.0.0-20181223230014-1083505acf35 h1:zpdCK+REwbk+rqjJmHhiCN6iBIigrZ39glqSF0P3KF0= -honnef.co/go/tools v0.0.1-2020.1.4 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK8= -k8s.io/component-base v0.32.3 h1:98WJvvMs3QZ2LYHBzvltFSeJjEx7t5+8s71P7M74u8k= -k8s.io/component-base v0.32.3/go.mod h1:LWi9cR+yPAv7cu2X9rZanTiFKB2kHA+JjmhkKjCZRpI= -k8s.io/component-helpers v0.32.3/go.mod h1:utTBXk8lhkJewBKNuNf32Xl3KT/0VV19DmiXU/SV4Ao= -k8s.io/gengo/v2 v2.0.0-20240826214909-a7b603a56eb7/go.mod h1:EJykeLsmFC60UQbYJezXkEsG2FLrt0GPNkU5iK5GWxU= -k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4= -k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/metrics v0.32.3/go.mod h1:9R1Wk5cb+qJpCQon9h52mgkVCcFeYxcY+YkumfwHVCU= -rsc.io/binaryregexp v0.2.0 h1:HfqmD5MEmC0zvwBuF187nq9mdnXjXsSivRiXN7SmRkE= -rsc.io/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY= -rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4= -sigs.k8s.io/kustomize/kustomize/v5 v5.5.0/go.mod h1:AeFCmgCrXzmvjWWaeZCyBp6XzG1Y0w1svYus8GhJEOE= +go.opentelemetry.io/contrib/detectors/gcp v1.38.0 h1:ZoYbqX7OaA/TAikspPl3ozPI6iY6LiIY9I8cUfm+pJs= +go.opentelemetry.io/contrib/detectors/gcp v1.38.0/go.mod h1:SU+iU7nu5ud4oCb3LQOhIZ3nRLj6FNVrKgtflbaf2ts= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 h1:xJ2qHD0C1BeYVTLLR9sX12+Qb95kfeD/byKj6Ky1pXg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0/go.mod h1:u5BF1xyjstDowA1R5QAO9JHzqK+ublenEW/dyqTjBVk= +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/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= +golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= +golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ= +golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ= +golang.org/x/tools/go/expect v0.1.0-deprecated h1:jY2C5HGYR5lqex3gEniOQL0r7Dq5+VGVgY1nudX5lXY= +golang.org/x/tools/go/expect v0.1.0-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY= +golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM= +golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated/go.mod h1:RVAQXBGNv1ib0J382/DPCRS/BPnsGebyM1Gj5VSDpG8= +google.golang.org/api v0.247.0 h1:tSd/e0QrUlLsrwMKmkbQhYVa109qIintOls2Wh6bngc= +google.golang.org/api v0.247.0/go.mod h1:r1qZOPmxXffXg6xS5uhx16Fa/UFY8QU/K4bfKrnvovM= +google.golang.org/genproto v0.0.0-20250603155806-513f23925822 h1:rHWScKit0gvAPuOnu87KpaYtjK5zBMLcULh7gxkCXu4= +google.golang.org/genproto v0.0.0-20250603155806-513f23925822/go.mod h1:HubltRL7rMh0LfnQPkMH4NPDFEWp0jw3vixw7jEM53s= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 h1:F29+wU6Ee6qgu9TddPgooOdaqsxTMunOoj8KA5yuS5A= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1/go.mod h1:5KF+wpkbTSbGcR9zteSqZV6fqFOWBl4Yde8En8MryZA= +gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo= +gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= +k8s.io/api v0.35.0 h1:iBAU5LTyBI9vw3L5glmat1njFK34srdLmktWwLTprlY= +k8s.io/api v0.35.0/go.mod h1:AQ0SNTzm4ZAczM03QH42c7l3bih1TbAXYo0DkF8ktnA= +k8s.io/gengo/v2 v2.0.0-20250604051438-85fd79dbfd9f h1:SLb+kxmzfA87x4E4brQzB33VBbT2+x7Zq9ROIHmGn9Q= +k8s.io/gengo/v2 v2.0.0-20250604051438-85fd79dbfd9f/go.mod h1:EJykeLsmFC60UQbYJezXkEsG2FLrt0GPNkU5iK5GWxU= +sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA= +sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4= diff --git a/pkg/mcp/builders/pulsar/sinks.go b/pkg/mcp/builders/pulsar/sinks.go index a120d11..7bd7e70 100644 --- a/pkg/mcp/builders/pulsar/sinks.go +++ b/pkg/mcp/builders/pulsar/sinks.go @@ -18,6 +18,8 @@ import ( "context" "encoding/json" "fmt" + "os" + "sort" "strings" "github.com/apache/pulsar-client-go/pulsaradmin/pkg/utils" @@ -26,6 +28,7 @@ import ( "github.com/streamnative/pulsarctl/pkg/cmdutils" "github.com/streamnative/streamnative-mcp-server/pkg/mcp/builders" mcpCtx "github.com/streamnative/streamnative-mcp-server/pkg/mcp/internal/context" + "gopkg.in/yaml.v2" ) // PulsarAdminSinksToolBuilder implements the ToolBuilder interface for Pulsar admin sinks @@ -113,14 +116,15 @@ func (b *PulsarAdminSinksToolBuilder) buildSinksTool() mcp.Tool { mcp.Description("The tenant name. Tenants are the primary organizational unit in Pulsar, "+ "providing multi-tenancy and resource isolation. Sinks deployed within a tenant "+ "inherit its permissions and resource quotas. "+ - "Required for all operations except 'list-built-in'.")), + "Defaults to 'public' if not provided.")), mcp.WithString("namespace", mcp.Description("The namespace name. Namespaces are logical groupings of topics and sinks "+ "within a tenant. They encapsulate configuration policies and access control. "+ "Sinks in a namespace typically process topics within the same namespace. "+ - "Required for all operations except 'list-built-in'.")), + "Defaults to 'default' if not provided.")), mcp.WithString("name", mcp.Description("The sink name. Required for all operations except 'list' and 'list-built-in'. "+ + "Can be provided via sink-config-file for create/update. "+ "Names should be descriptive of the sink's purpose and must be unique within a namespace. "+ "Sink names are used in metrics, logs, and when addressing the sink via APIs.")), mcp.WithString("archive", @@ -157,18 +161,77 @@ func (b *PulsarAdminSinksToolBuilder) buildSinksTool() mcp.Tool { "Defines the subscription name used by the sink to consume from input topics. "+ "If not specified, a default subscription name will be generated. "+ "The subscription type used is Shared by default.")), + mcp.WithString("subs-position", + mcp.Description("Pulsar source subscription position for input-topic consumers. Optional for 'create' and 'update' operations. "+ + "Possible values: 'Latest' or 'Earliest'. If not specified, the sink uses the default subscription position.")), + mcp.WithString("classname", + mcp.Description("The sink class name when using a custom archive. Optional for 'create' and 'update'. "+ + "Specifies the fully qualified class name implementing the sink connector.")), + mcp.WithString("processing-guarantees", + mcp.Description("The processing guarantees (delivery semantics) for the sink. Optional for 'create' and 'update'. "+ + "Available options: 'atleast_once', 'atmost_once', 'effectively_once'.")), + mcp.WithBoolean("retain-ordering", + mcp.Description("Whether the sink preserves message ordering. Optional for 'create' and 'update'.")), + mcp.WithBoolean("retain-key-ordering", + mcp.Description("Whether the sink preserves key ordering. Optional for 'create' and 'update'.")), + mcp.WithBoolean("auto-ack", + mcp.Description("Whether the sink automatically acknowledges messages. Optional for 'create' and 'update'.")), + mcp.WithBoolean("cleanup-subscription", + mcp.Description("Whether the subscription used by the sink should be deleted when the sink is deleted. Optional for 'create' and 'update'. Default: true.")), mcp.WithNumber("parallelism", mcp.Description("The parallelism factor of the sink. Optional for 'create' and 'update' operations. "+ "Determines how many instances of the sink will run concurrently. "+ "Higher values improve throughput but require more resources. "+ "Default is 1 (single instance). Recommended to align with topic partition count "+ "when consuming from partitioned topics.")), + mcp.WithNumber("cpu", + mcp.Description("CPU cores allocated per sink instance. Optional for 'create' and 'update'. "+ + "Applicable to process and container runtimes.")), + mcp.WithNumber("ram", + mcp.Description("RAM bytes allocated per sink instance. Optional for 'create' and 'update'. "+ + "Applicable to process and container runtimes.")), + mcp.WithNumber("disk", + mcp.Description("Disk bytes allocated per sink instance. Optional for 'create' and 'update'. "+ + "Applicable to process and container runtimes.")), + mcp.WithObject("custom-serde-inputs", + mcp.Description("Map of input topics to SerDe class names. Optional for 'create' and 'update'. "+ + "Specify as a JSON object of topic to SerDe class.")), + mcp.WithObject("custom-schema-inputs", + mcp.Description("Map of input topics to Schema types or class names. Optional for 'create' and 'update'. "+ + "Specify as a JSON object of topic to schema type/class.")), + mcp.WithObject("input-specs", + mcp.Description("Map of input topics to consumer configuration. Optional for 'create' and 'update'. "+ + "Specify as a JSON object of topic to consumer config.")), + mcp.WithNumber("max-redeliver-count", + mcp.Description("Maximum number of retries before sending a message to the dead letter topic. Optional for 'create' and 'update'.")), + mcp.WithString("dead-letter-topic", + mcp.Description("Dead letter topic for messages that exceed retry attempts. Optional for 'create' and 'update'.")), + mcp.WithNumber("timeout-ms", + mcp.Description("Message processing timeout in milliseconds. Optional for 'create' and 'update'.")), + mcp.WithNumber("negative-ack-redelivery-delay-ms", + mcp.Description("Delay in milliseconds before redelivering negatively acknowledged messages. Optional for 'create' and 'update'.")), + mcp.WithString("custom-runtime-options", + mcp.Description("Runtime customization options. Optional for 'create' and 'update'.")), + mcp.WithObject("secrets", + mcp.Description("Secrets configuration map. Optional for 'create' and 'update'. "+ + "Specify as a JSON object where values describe how secrets are fetched.")), + mcp.WithString("sink-config-file", + mcp.Description("Path to a YAML sink configuration file. Optional for 'create' and 'update'. "+ + "When provided, the file is loaded before applying explicit parameters.")), mcp.WithObject("sink-config", mcp.Description("User-defined sink config key/values. Optional for 'create' and 'update' operations. "+ "Provides configuration parameters specific to the sink connector being used. "+ "For example, JDBC connection strings, Elasticsearch indices, S3 bucket details, etc. "+ "Specify as a JSON object with configuration properties required by the specific sink type. "+ "Example: {\"jdbcUrl\": \"jdbc:postgresql://localhost:5432/database\", \"tableName\": \"events\"}")), + mcp.WithString("transform-function", + mcp.Description("Transform function applied before the sink. Optional for 'create' and 'update'.")), + mcp.WithString("transform-function-classname", + mcp.Description("Transform function class name. Optional for 'create' and 'update'.")), + mcp.WithString("transform-function-config", + mcp.Description("Transform function configuration. Optional for 'create' and 'update'.")), + mcp.WithBoolean("update-auth-data", + mcp.Description("Whether to update authentication data during sink update. Optional for 'update' only.")), ) } @@ -216,19 +279,27 @@ func (b *PulsarAdminSinksToolBuilder) buildSinksHandler(readOnly bool) func(cont if operation == "list-built-in" { return b.handleListBuiltInSinks(ctx, admin) } - - // Extract common parameters (all operations except list-built-in require tenant and namespace) - tenant, err := request.RequireString("tenant") - if err != nil { - return mcp.NewToolResultError(fmt.Sprintf("Missing required parameter 'tenant': %v. A tenant is required for operation '%s'.", err, operation)), nil + args := request.GetArguments() + + if operation == "create" || operation == "update" { + name, _ := getStringArg(args, "name") + tenant, _ := getStringArg(args, "tenant") + namespace, _ := getStringArg(args, "namespace") + if operation == "create" { + return b.handleSinkCreate(ctx, admin, tenant, namespace, name, request) + } + return b.handleSinkUpdate(ctx, admin, tenant, namespace, name, request) } - namespace, err := request.RequireString("namespace") - if err != nil { - return mcp.NewToolResultError(fmt.Sprintf("Missing required parameter 'namespace': %v. A namespace is required for operation '%s'.", err, operation)), nil + tenant, _ := getStringArg(args, "tenant") + namespace, _ := getStringArg(args, "namespace") + if tenant == "" { + tenant = defaultTenant + } + if namespace == "" { + namespace = defaultNamespace } - // For all operations except 'list', name is required var name string if operation != "list" { name, err = request.RequireString("name") @@ -245,10 +316,6 @@ func (b *PulsarAdminSinksToolBuilder) buildSinksHandler(readOnly bool) func(cont return b.handleSinkGet(ctx, admin, tenant, namespace, name) case "status": return b.handleSinkStatus(ctx, admin, tenant, namespace, name) - case "create": - return b.handleSinkCreate(ctx, admin, request) - case "update": - return b.handleSinkUpdate(ctx, admin, request) case "delete": return b.handleSinkDelete(ctx, admin, tenant, namespace, name) case "start": @@ -318,212 +385,95 @@ func (b *PulsarAdminSinksToolBuilder) handleSinkStatus(_ context.Context, admin } // handleSinkCreate handles creating a new sink -func (b *PulsarAdminSinksToolBuilder) handleSinkCreate(_ context.Context, admin cmdutils.Client, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { - tenant, err := request.RequireString("tenant") +func (b *PulsarAdminSinksToolBuilder) handleSinkCreate(_ context.Context, admin cmdutils.Client, tenant, namespace, name string, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + config, archiveArg, sinkTypeArg, err := b.buildSinkConfig(tenant, namespace, name, request) if err != nil { - return mcp.NewToolResultError(fmt.Sprintf("Failed to get tenant: %v", err)), nil + return mcp.NewToolResultError(fmt.Sprintf("Failed to build sink configuration for '%s': %v. Please verify all required parameters are provided correctly.", name, err)), nil } - namespace, err := request.RequireString("namespace") - if err != nil { - return mcp.NewToolResultError(fmt.Sprintf("Failed to get namespace: %v", err)), nil + if err := validateSinkArchiveArgs(archiveArg, sinkTypeArg); err != nil { + return mcp.NewToolResultError(fmt.Sprintf("Invalid parameters: %v", err)), nil } - name, err := request.RequireString("name") + uploadArchive, err := b.resolveSinkArchive(admin, config, archiveArg, sinkTypeArg) if err != nil { - return mcp.NewToolResultError(fmt.Sprintf("Failed to get name: %v", err)), nil - } - - // Create a new SinkData object - sinkData := &utils.SinkData{ - Tenant: tenant, - Namespace: namespace, - Name: name, - SinkConf: &utils.SinkConfig{}, - } - - // Get optional parameters - archive := request.GetString("archive", "") - if archive != "" { - sinkData.Archive = archive - } - - sinkType := request.GetString("sink-type", "") - if sinkType != "" { - sinkData.SinkType = sinkType - } - - inputsArray := request.GetStringSlice("inputs", []string{}) - if len(inputsArray) > 0 { - sinkData.Inputs = strings.Join(inputsArray, ",") - } - - topicsPattern := request.GetString("topics-pattern", "") - if topicsPattern != "" { - sinkData.TopicsPattern = topicsPattern - } - - subsName := request.GetString("subs-name", "") - if subsName != "" { - sinkData.SubsName = subsName + return mcp.NewToolResultError(fmt.Sprintf("Failed to resolve sink archive: %v", err)), nil } - parallelismFloat := request.GetFloat("parallelism", 1) - if parallelismFloat >= 0 { - sinkData.Parallelism = int(parallelismFloat) + b.applySinkDefaults(config) + if err := validateSinkConfig(config); err != nil { + return mcp.NewToolResultError(fmt.Sprintf("Failed to validate sink configuration for '%s': %v.", config.Name, err)), nil } - // Get sink config if available - var sinkConfigMap map[string]interface{} - sinkConfigObj, ok := request.GetArguments()["sink-config"] - if ok && sinkConfigObj != nil { - if configMap, isMap := sinkConfigObj.(map[string]interface{}); isMap { - sinkConfigMap = configMap - // Convert to JSON string - sinkConfigJSON, err := json.Marshal(sinkConfigMap) - if err != nil { - return mcp.NewToolResultError(fmt.Sprintf("Failed to marshal sink-config: %v. Ensure the sink configuration is a valid JSON object.", err)), nil - } - sinkData.SinkConfigString = string(sinkConfigJSON) - } - } - - // Validate inputs - if sinkData.Archive == "" && sinkData.SinkType == "" { - return mcp.NewToolResultError("Missing required parameter: Either 'archive' or 'sink-type' must be specified for sink creation. Use 'archive' for custom connectors or 'sink-type' for built-in connectors."), nil - } - - if sinkData.Archive != "" && sinkData.SinkType != "" { - return mcp.NewToolResultError("Invalid parameters: Cannot specify both 'archive' and 'sink-type'. Use only one of these parameters based on your connector type."), nil - } - - if sinkData.Inputs == "" && sinkData.TopicsPattern == "" { + if len(config.Inputs) == 0 && (config.TopicsPattern == nil || strings.TrimSpace(*config.TopicsPattern) == "") { return mcp.NewToolResultError("Missing required parameter: Either 'inputs' or 'topics-pattern' must be specified. The sink needs a source of data to consume from Pulsar."), nil } - // Process the arguments - err = b.processArguments(sinkData) - if err != nil { - return mcp.NewToolResultError(fmt.Sprintf("Failed to process arguments: %v", err)), nil - } - - // Create the sink - if sinkData.Archive != "" && b.isPackageURLSupported(sinkData.Archive) { - err = admin.Sinks().CreateSinkWithURL(sinkData.SinkConf, sinkData.Archive) + if uploadArchive != "" && b.isPackageURLSupported(uploadArchive) { + err = admin.Sinks().CreateSinkWithURL(config, uploadArchive) } else { - err = admin.Sinks().CreateSink(sinkData.SinkConf, sinkData.Archive) + err = admin.Sinks().CreateSink(config, uploadArchive) } if err != nil { return mcp.NewToolResultError(fmt.Sprintf("Failed to create sink '%s' in tenant '%s' namespace '%s': %v. Verify all parameters are correct and required resources exist.", - name, tenant, namespace, err)), nil + config.Name, config.Tenant, config.Namespace, err)), nil } return mcp.NewToolResultText(fmt.Sprintf("Created sink '%s' successfully in tenant '%s' namespace '%s'. The sink will start consuming from its input topics and writing to the configured destination.", - name, tenant, namespace)), nil + config.Name, config.Tenant, config.Namespace)), nil } // handleSinkUpdate handles updating an existing sink -func (b *PulsarAdminSinksToolBuilder) handleSinkUpdate(_ context.Context, admin cmdutils.Client, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { - tenant, err := request.RequireString("tenant") +func (b *PulsarAdminSinksToolBuilder) handleSinkUpdate(_ context.Context, admin cmdutils.Client, tenant, namespace, name string, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + config, archiveArg, sinkTypeArg, err := b.buildSinkConfig(tenant, namespace, name, request) if err != nil { - return mcp.NewToolResultError(fmt.Sprintf("Failed to get tenant: %v", err)), nil + return mcp.NewToolResultError(fmt.Sprintf("Failed to build sink configuration for '%s': %v. Please verify all parameters are provided correctly.", name, err)), nil } - namespace, err := request.RequireString("namespace") - if err != nil { - return mcp.NewToolResultError(fmt.Sprintf("Failed to get namespace: %v", err)), nil + if err := validateSinkArchiveArgs(archiveArg, sinkTypeArg); err != nil { + return mcp.NewToolResultError(fmt.Sprintf("Invalid parameters: %v", err)), nil } - name, err := request.RequireString("name") + uploadArchive, err := b.resolveSinkArchive(admin, config, archiveArg, sinkTypeArg) if err != nil { - return mcp.NewToolResultError(fmt.Sprintf("Failed to get name: %v", err)), nil - } - - // Create a new SinkData object - sinkData := &utils.SinkData{ - Tenant: tenant, - Namespace: namespace, - Name: name, - SinkConf: &utils.SinkConfig{}, - } - - // Get optional parameters - archive := request.GetString("archive", "") - if archive != "" { - sinkData.Archive = archive + return mcp.NewToolResultError(fmt.Sprintf("Failed to resolve sink archive: %v", err)), nil } - sinkType := request.GetString("sink-type", "") - if sinkType != "" { - sinkData.SinkType = sinkType - } - - inputsArray := request.GetStringSlice("inputs", []string{}) - if len(inputsArray) > 0 { - sinkData.Inputs = strings.Join(inputsArray, ",") - } - - topicsPattern := request.GetString("topics-pattern", "") - if topicsPattern != "" { - sinkData.TopicsPattern = topicsPattern - } - - subsName := request.GetString("subs-name", "") - if subsName != "" { - sinkData.SubsName = subsName - } - - parallelismFloat := request.GetFloat("parallelism", 1) - if parallelismFloat >= 0 { - sinkData.Parallelism = int(parallelismFloat) - } + b.applySinkDefaults(config) - // Get sink config if available - var sinkConfigMap map[string]interface{} - sinkConfigObj, ok := request.GetArguments()["sink-config"] - if ok && sinkConfigObj != nil { - if configMap, isMap := sinkConfigObj.(map[string]interface{}); isMap { - sinkConfigMap = configMap - // Convert to JSON string - sinkConfigJSON, err := json.Marshal(sinkConfigMap) - if err != nil { - return mcp.NewToolResultError(fmt.Sprintf("Failed to marshal sink-config: %v. Ensure the sink configuration is a valid JSON object.", err)), nil - } - sinkData.SinkConfigString = string(sinkConfigJSON) - } - } - - // Validate inputs if both are specified - if sinkData.Archive != "" && sinkData.SinkType != "" { - return mcp.NewToolResultError("Invalid parameters: Cannot specify both 'archive' and 'sink-type'. Use only one of these parameters based on your connector type."), nil + if config.Name == "" { + return mcp.NewToolResultError("Sink name not specified. Provide the 'name' parameter or set it in the sink-config-file."), nil } - // Process the arguments - err = b.processArguments(sinkData) + latestConfig, err := admin.Sinks().GetSink(config.Tenant, config.Namespace, config.Name) if err != nil { - return mcp.NewToolResultError(fmt.Sprintf("Failed to process arguments: %v", err)), nil + return mcp.NewToolResultError(fmt.Sprintf("Failed to get sink '%s' in tenant '%s' namespace '%s': %v. Verify the sink exists and you have proper permissions.", + config.Name, config.Tenant, config.Namespace, err)), nil } - // Create update options - updateOptions := &utils.UpdateOptions{ - UpdateAuthData: true, + config.AutoAck = latestConfig.AutoAck + config.RetainOrdering = latestConfig.RetainOrdering + config.ProcessingGuarantees = latestConfig.ProcessingGuarantees + + updateOptions := utils.NewUpdateOptions() + if updateAuthData, ok := getBoolArg(request.GetArguments(), "update-auth-data"); ok { + updateOptions.UpdateAuthData = updateAuthData } - // Update the sink - if sinkData.Archive != "" && b.isPackageURLSupported(sinkData.Archive) { - err = admin.Sinks().UpdateSinkWithURL(sinkData.SinkConf, sinkData.Archive, updateOptions) + if uploadArchive != "" && b.isPackageURLSupported(uploadArchive) { + err = admin.Sinks().UpdateSinkWithURL(config, uploadArchive, updateOptions) } else { - err = admin.Sinks().UpdateSink(sinkData.SinkConf, sinkData.Archive, updateOptions) + err = admin.Sinks().UpdateSink(config, uploadArchive, updateOptions) } if err != nil { return mcp.NewToolResultError(fmt.Sprintf("Failed to update sink '%s' in tenant '%s' namespace '%s': %v. Verify the sink exists and all parameters are valid.", - name, tenant, namespace, err)), nil + config.Name, config.Tenant, config.Namespace, err)), nil } return mcp.NewToolResultText(fmt.Sprintf("Updated sink '%s' successfully in tenant '%s' namespace '%s'. The sink may need to be restarted to apply all changes.", - name, tenant, namespace)), nil + config.Name, config.Tenant, config.Namespace)), nil } // handleSinkDelete handles deleting a sink @@ -590,89 +540,283 @@ func (b *PulsarAdminSinksToolBuilder) handleListBuiltInSinks(_ context.Context, return mcp.NewToolResultText(string(sinksJSON)), nil } -// processArguments is a simplified version of the pulsarctl function to process sink arguments -func (b *PulsarAdminSinksToolBuilder) processArguments(sinkData *utils.SinkData) error { - // Initialize config if needed - if sinkData.SinkConf == nil { - sinkData.SinkConf = new(utils.SinkConfig) +func (b *PulsarAdminSinksToolBuilder) buildSinkConfig(tenant, namespace, name string, request mcp.CallToolRequest) (*utils.SinkConfig, string, string, error) { + config := &utils.SinkConfig{} + args := request.GetArguments() + + if configFile, ok := getStringArg(args, "sink-config-file"); ok && configFile != "" { + //nolint:gosec + data, err := os.ReadFile(configFile) + if err != nil { + return nil, "", "", fmt.Errorf("load sink config file failed: %w", err) + } + if err := yaml.Unmarshal(data, config); err != nil { + return nil, "", "", fmt.Errorf("unmarshal sink config file error: %w", err) + } + } + + if tenant != "" { + config.Tenant = tenant + } + if namespace != "" { + config.Namespace = namespace + } + if name != "" { + config.Name = name + } + + if className, ok := getStringArg(args, "classname"); ok && className != "" { + config.ClassName = className + } + + if processingGuarantees, ok := getStringArg(args, "processing-guarantees"); ok && processingGuarantees != "" { + config.ProcessingGuarantees = processingGuarantees + } + + if retainOrdering, ok := getBoolArg(args, "retain-ordering"); ok { + config.RetainOrdering = retainOrdering + } + + if retainKeyOrdering, ok := getBoolArg(args, "retain-key-ordering"); ok { + config.RetainKeyOrdering = retainKeyOrdering } - // Set basic config values - sinkData.SinkConf.Tenant = sinkData.Tenant - sinkData.SinkConf.Namespace = sinkData.Namespace - sinkData.SinkConf.Name = sinkData.Name + if autoAck, ok := getBoolArg(args, "auto-ack"); ok { + config.AutoAck = autoAck + } + + if inputsValue, exists := args["inputs"]; exists && inputsValue != nil { + inputs, err := parseStringSlice(inputsValue) + if err != nil { + return nil, "", "", fmt.Errorf("invalid inputs: %w", err) + } + config.Inputs = inputs + } - // Set inputs if provided - if sinkData.Inputs != "" { - inputTopics := strings.Split(sinkData.Inputs, ",") - sinkData.SinkConf.Inputs = inputTopics + if topicsPattern, ok := getStringArg(args, "topics-pattern"); ok && topicsPattern != "" { + config.TopicsPattern = &topicsPattern } - // Set topics pattern if provided - if sinkData.TopicsPattern != "" { - sinkData.SinkConf.TopicsPattern = &sinkData.TopicsPattern + if subsName, ok := getStringArg(args, "subs-name"); ok && subsName != "" { + config.SourceSubscriptionName = subsName } - // Set subscription name if provided - if sinkData.SubsName != "" { - sinkData.SinkConf.SourceSubscriptionName = sinkData.SubsName + if subsPosition, ok := getStringArg(args, "subs-position"); ok && subsPosition != "" { + config.SourceSubscriptionPosition = subsPosition } - // Set parallelism if provided - if sinkData.Parallelism != 0 { - sinkData.SinkConf.Parallelism = sinkData.Parallelism - } else if sinkData.SinkConf.Parallelism <= 0 { - sinkData.SinkConf.Parallelism = 1 + if parallelism, ok, err := parseOptionalIntArg(args, "parallelism"); err != nil { + return nil, "", "", err + } else if ok { + config.Parallelism = parallelism } - // Handle archive and sink-type - if sinkData.Archive != "" && sinkData.SinkType != "" { - return fmt.Errorf("cannot specify both archive and sink-type") + if cpu, ok, err := parseOptionalFloatArg(args, "cpu"); err != nil { + return nil, "", "", err + } else if ok { + config.Resources = ensureResources(config.Resources) + config.Resources.CPU = cpu } - if sinkData.Archive != "" { - sinkData.SinkConf.Archive = sinkData.Archive + if ram, ok, err := parseOptionalInt64Arg(args, "ram"); err != nil { + return nil, "", "", err + } else if ok { + config.Resources = ensureResources(config.Resources) + config.Resources.RAM = ram } - if sinkData.SinkType != "" { - // In a real implementation, we would validate the sink type here - sinkData.SinkConf.Archive = sinkData.SinkType + if disk, ok, err := parseOptionalInt64Arg(args, "disk"); err != nil { + return nil, "", "", err + } else if ok { + config.Resources = ensureResources(config.Resources) + config.Resources.Disk = disk } - // Parse sink config if provided - if sinkData.SinkConfigString != "" { - var configs map[string]interface{} - if err := json.Unmarshal([]byte(sinkData.SinkConfigString), &configs); err != nil { - return fmt.Errorf("failed to parse sink config: %v", err) + if customSerdeInputsValue, exists := args["custom-serde-inputs"]; exists && customSerdeInputsValue != nil { + customSerdeInputs, err := decodeStringMap(customSerdeInputsValue) + if err != nil { + return nil, "", "", fmt.Errorf("invalid custom-serde-inputs: %w", err) + } + config.TopicToSerdeClassName = customSerdeInputs + } + + if customSchemaInputsValue, exists := args["custom-schema-inputs"]; exists && customSchemaInputsValue != nil { + customSchemaInputs, err := decodeStringMap(customSchemaInputsValue) + if err != nil { + return nil, "", "", fmt.Errorf("invalid custom-schema-inputs: %w", err) + } + config.TopicToSchemaType = customSchemaInputs + } + + if inputSpecsValue, exists := args["input-specs"]; exists && inputSpecsValue != nil { + inputSpecs, err := decodeConsumerConfigMap(inputSpecsValue) + if err != nil { + return nil, "", "", fmt.Errorf("invalid input-specs: %w", err) + } + config.InputSpecs = inputSpecs + } + + if maxMessageRetries, ok, err := parseOptionalIntArg(args, "max-redeliver-count"); err != nil { + return nil, "", "", err + } else if ok { + config.MaxMessageRetries = maxMessageRetries + } else { + config.MaxMessageRetries = 0 + } + + if deadLetterTopic, ok := getStringArg(args, "dead-letter-topic"); ok && deadLetterTopic != "" { + config.DeadLetterTopic = deadLetterTopic + } + + if timeoutMs, ok, err := parseOptionalInt64Arg(args, "timeout-ms"); err != nil { + return nil, "", "", err + } else if ok { + config.TimeoutMs = &timeoutMs + } + + if negativeAckRedeliveryDelayMs, ok, err := parseOptionalInt64Arg(args, "negative-ack-redelivery-delay-ms"); err != nil { + return nil, "", "", err + } else if ok { + config.NegativeAckRedeliveryDelayMs = negativeAckRedeliveryDelayMs + } else { + config.NegativeAckRedeliveryDelayMs = 0 + } + + if customRuntimeOptions, ok := getStringArg(args, "custom-runtime-options"); ok && customRuntimeOptions != "" { + config.CustomRuntimeOptions = customRuntimeOptions + } + + if secretsValue, exists := args["secrets"]; exists && secretsValue != nil { + secrets, err := decodeInterfaceMap(secretsValue) + if err != nil { + return nil, "", "", fmt.Errorf("invalid secrets: %w", err) + } + config.Secrets = secrets + } + + if cleanupSubscription, ok := getBoolArg(args, "cleanup-subscription"); ok { + config.CleanupSubscription = cleanupSubscription + } else { + config.CleanupSubscription = true + } + + if sinkConfigValue, exists := args["sink-config"]; exists && sinkConfigValue != nil { + sinkConfigs, err := decodeInterfaceMap(sinkConfigValue) + if err != nil { + return nil, "", "", fmt.Errorf("invalid sink-config: %w", err) } - sinkData.SinkConf.Configs = configs + config.Configs = sinkConfigs + } + + if transformFunction, ok := getStringArg(args, "transform-function"); ok && transformFunction != "" { + config.TransformFunction = transformFunction + } + + if transformFunctionClassName, ok := getStringArg(args, "transform-function-classname"); ok && transformFunctionClassName != "" { + config.TransformFunctionClassName = transformFunctionClassName + } + + if transformFunctionConfig, ok := getStringArg(args, "transform-function-config"); ok && transformFunctionConfig != "" { + config.TransformFunctionConfig = transformFunctionConfig } + b.normalizeSinkConfigMaps(config) + + archiveArg, _ := getStringArg(args, "archive") + sinkTypeArg, _ := getStringArg(args, "sink-type") + + return config, archiveArg, sinkTypeArg, nil +} + +func (b *PulsarAdminSinksToolBuilder) normalizeSinkConfigMaps(config *utils.SinkConfig) { + if config.Configs != nil { + if converted, ok := convertMap(config.Configs).(map[string]interface{}); ok { + config.Configs = converted + } + } + + if config.Secrets != nil { + if converted, ok := convertMap(config.Secrets).(map[string]interface{}); ok { + config.Secrets = converted + } + } + + if config.Secrets == nil { + config.Secrets = make(map[string]interface{}) + } +} + +func (b *PulsarAdminSinksToolBuilder) applySinkDefaults(config *utils.SinkConfig) { + if config.Tenant == "" { + config.Tenant = defaultTenant + } + if config.Namespace == "" { + config.Namespace = defaultNamespace + } + if config.Parallelism <= 0 { + config.Parallelism = 1 + } +} + +func validateSinkConfig(config *utils.SinkConfig) error { + if config.Archive == "" { + return fmt.Errorf("sink archive not specified") + } + if config.Name == "" { + return fmt.Errorf("sink name not specified") + } return nil } -// isPackageURLSupported checks if the package URL is supported -// Validates URLs for Pulsar sink packages -func (b *PulsarAdminSinksToolBuilder) isPackageURLSupported(archive string) bool { - if archive == "" { - return false +func validateSinkArchiveArgs(archive, sinkType string) error { + if archive != "" && sinkType != "" { + return fmt.Errorf("cannot specify both 'archive' and 'sink-type'") + } + return nil +} + +func (b *PulsarAdminSinksToolBuilder) resolveSinkArchive(admin cmdutils.Client, config *utils.SinkConfig, archive, sinkType string) (string, error) { + if sinkType != "" { + resolved, err := b.validateSinkType(admin, sinkType) + if err != nil { + return "", err + } + config.Archive = resolved + return "", nil } - // Check for supported URL schemes for Pulsar sink packages - supportedSchemes := []string{ - "http://", - "https://", - "file://", - "function://", // Pulsar function package URL - "sink://", // Pulsar sink package URL + if archive != "" { + config.Archive = archive + return archive, nil } - for _, scheme := range supportedSchemes { - if strings.HasPrefix(archive, scheme) { - return true + return "", nil +} + +func (b *PulsarAdminSinksToolBuilder) validateSinkType(admin cmdutils.Client, sinkType string) (string, error) { + builtins, err := admin.Sinks().GetBuiltInSinks() + if err != nil { + return "", fmt.Errorf("failed to list built-in sinks: %w", err) + } + + names := make([]string, 0, len(builtins)) + for _, builtin := range builtins { + names = append(names, builtin.Name) + if builtin.Name == sinkType { + return "builtin://" + sinkType, nil } } - // Also check if it's a local file path (not a URL) - return !strings.Contains(archive, "://") + sort.Strings(names) + return "", fmt.Errorf("invalid sink-type %q. Available sinks: %s", sinkType, strings.Join(names, ", ")) +} + +// isPackageURLSupported checks if the package URL is supported +// Validates URLs for Pulsar sink packages +func (b *PulsarAdminSinksToolBuilder) isPackageURLSupported(archive string) bool { + return archive != "" && (strings.HasPrefix(archive, "http") || + strings.HasPrefix(archive, "file") || + strings.HasPrefix(archive, "function") || + strings.HasPrefix(archive, "sink") || + strings.HasPrefix(archive, "source")) } diff --git a/pkg/mcp/builders/pulsar/sinks_parity_test.go b/pkg/mcp/builders/pulsar/sinks_parity_test.go new file mode 100644 index 0000000..7ca3d23 --- /dev/null +++ b/pkg/mcp/builders/pulsar/sinks_parity_test.go @@ -0,0 +1,87 @@ +// Copyright 2025 StreamNative +// +// 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 pulsar + +import ( + "testing" + + "github.com/mark3labs/mcp-go/mcp" + "github.com/stretchr/testify/require" +) + +func TestBuildSinkConfigDefaults(t *testing.T) { + builder := NewPulsarAdminSinksToolBuilder() + + req := mcp.CallToolRequest{Params: mcp.CallToolParams{Arguments: map[string]any{}}} + config, _, _, err := builder.buildSinkConfig("", "", "name", req) + require.NoError(t, err) + + builder.applySinkDefaults(config) + require.Equal(t, defaultTenant, config.Tenant) + require.Equal(t, defaultNamespace, config.Namespace) + require.Equal(t, 1, config.Parallelism) + require.True(t, config.CleanupSubscription) +} + +func TestBuildSinkConfigInputsParsing(t *testing.T) { + builder := NewPulsarAdminSinksToolBuilder() + + req := mcp.CallToolRequest{Params: mcp.CallToolParams{Arguments: map[string]any{ + "inputs": "t1,t2", + }}} + config, _, _, err := builder.buildSinkConfig("tenant", "namespace", "name", req) + require.NoError(t, err) + require.Equal(t, []string{"t1", "t2"}, config.Inputs) +} + +func TestBuildSinkConfigMaps(t *testing.T) { + builder := NewPulsarAdminSinksToolBuilder() + + req := mcp.CallToolRequest{Params: mcp.CallToolParams{Arguments: map[string]any{ + "custom-serde-inputs": map[string]any{ + "t1": "serde", + }, + "custom-schema-inputs": map[string]any{ + "t1": "schema", + }, + "input-specs": map[string]any{ + "t1": map[string]any{ + "schemaType": "string", + }, + }, + "secrets": map[string]any{ + "secret": map[string]any{ + "value": "x", + }, + }, + "sink-config": map[string]any{ + "path": "/tmp/out", + }, + }}} + config, _, _, err := builder.buildSinkConfig("tenant", "namespace", "name", req) + require.NoError(t, err) + require.Equal(t, "serde", config.TopicToSerdeClassName["t1"]) + require.Equal(t, "schema", config.TopicToSchemaType["t1"]) + require.Equal(t, "string", config.InputSpecs["t1"].SchemaType) + require.Equal(t, "/tmp/out", config.Configs["path"]) + require.NotNil(t, config.Secrets) +} + +func TestValidateSinkArchiveArgs(t *testing.T) { + require.NoError(t, validateSinkArchiveArgs("", "")) + require.NoError(t, validateSinkArchiveArgs("archive.nar", "")) + require.NoError(t, validateSinkArchiveArgs("", "file")) + require.Error(t, validateSinkArchiveArgs("archive.nar", "file")) +} diff --git a/pkg/mcp/builders/pulsar/sources.go b/pkg/mcp/builders/pulsar/sources.go index ca9d148..2344ba2 100644 --- a/pkg/mcp/builders/pulsar/sources.go +++ b/pkg/mcp/builders/pulsar/sources.go @@ -18,6 +18,8 @@ import ( "context" "encoding/json" "fmt" + "os" + "sort" "strings" "github.com/apache/pulsar-client-go/pulsaradmin/pkg/utils" @@ -26,6 +28,7 @@ import ( "github.com/streamnative/pulsarctl/pkg/cmdutils" "github.com/streamnative/streamnative-mcp-server/pkg/mcp/builders" mcpCtx "github.com/streamnative/streamnative-mcp-server/pkg/mcp/internal/context" + "gopkg.in/yaml.v2" ) // PulsarAdminSourcesToolBuilder implements the ToolBuilder interface for Pulsar admin sources @@ -112,14 +115,15 @@ func (b *PulsarAdminSourcesToolBuilder) buildSourcesTool() mcp.Tool { mcp.Description("The tenant name. Tenants are the primary organizational unit in Pulsar, "+ "providing multi-tenancy and resource isolation. Sources deployed within a tenant "+ "inherit its permissions and resource quotas. "+ - "Required for all operations except 'list-built-in'.")), + "Defaults to 'public' if not provided.")), mcp.WithString("namespace", mcp.Description("The namespace name. Namespaces are logical groupings of topics and sources "+ "within a tenant. They encapsulate configuration policies and access control. "+ "Sources in a namespace typically publish to topics within the same namespace. "+ - "Required for all operations except 'list-built-in'.")), + "Defaults to 'default' if not provided.")), mcp.WithString("name", mcp.Description("The source name. Required for all operations except 'list' and 'list-built-in'. "+ + "Can be provided via source-config-file for create/update. "+ "Names should be descriptive of the source's purpose and must be unique within a namespace. "+ "Source names are used in metrics, logs, and when addressing the source via APIs.")), mcp.WithString("archive", @@ -132,11 +136,20 @@ func (b *PulsarAdminSourcesToolBuilder) buildSourcesTool() mcp.Tool { "Specifies which built-in connector to use, such as 'kafka', 'jdbc', 'file', etc. "+ "Use 'list-built-in' operation to see available source types. "+ "Either source-type or archive must be specified, but not both.")), + mcp.WithString("source-config-file", + mcp.Description("Path to a YAML source configuration file. Optional for 'create' and 'update'. "+ + "When provided, the file is loaded before applying explicit parameters.")), mcp.WithString("destination-topic-name", mcp.Description("The Pulsar topic to which data is published. Required for 'create' operation, optional for 'update'. "+ "Specified in the format 'persistent://tenant/namespace/topic'. "+ "This is the topic where the source will send the data it extracts from the external system. "+ "The topic will be automatically created if it doesn't exist.")), + mcp.WithObject("producer-config", + mcp.Description("Custom producer configuration as JSON object. Optional for 'create' and 'update'.")), + mcp.WithString("batch-builder", + mcp.Description("Batch builder type (DEFAULT or KEY_BASED). Optional for 'create' and 'update'.")), + mcp.WithObject("batch-source-config", + mcp.Description("Batch source configuration as JSON object. Optional for 'create' and 'update'.")), mcp.WithString("deserialization-classname", mcp.Description("The SerDe (Serialization/Deserialization) classname for the source. Optional for 'create' and 'update'. "+ "Specifies how to convert data from the external system into Pulsar messages. "+ @@ -164,12 +177,28 @@ func (b *PulsarAdminSourcesToolBuilder) buildSourcesTool() mcp.Tool { "Higher values improve throughput but require more resources. "+ "Default is 1 (single instance). Recommended to align with both source capacity "+ "and destination topic partition count.")), + mcp.WithNumber("cpu", + mcp.Description("CPU cores allocated per source instance. Optional for 'create' and 'update'. "+ + "Applicable to process and container runtimes.")), + mcp.WithNumber("ram", + mcp.Description("RAM bytes allocated per source instance. Optional for 'create' and 'update'. "+ + "Applicable to process and container runtimes.")), + mcp.WithNumber("disk", + mcp.Description("Disk bytes allocated per source instance. Optional for 'create' and 'update'. "+ + "Applicable to process and container runtimes.")), + mcp.WithString("custom-runtime-options", + mcp.Description("Runtime customization options. Optional for 'create' and 'update'.")), + mcp.WithObject("secrets", + mcp.Description("Secrets configuration map. Optional for 'create' and 'update'. "+ + "Specify as a JSON object where values describe how secrets are fetched.")), mcp.WithObject("source-config", mcp.Description("User-defined source config key/values. Optional for 'create' and 'update' operations. "+ "Provides configuration parameters specific to the source connector being used. "+ "For example, database connection details, Kafka bootstrap servers, credentials, etc. "+ "Specify as a JSON object with configuration properties required by the specific source type. "+ "Example: {\"topic\": \"external-kafka-topic\", \"bootstrapServers\": \"kafka:9092\"}")), + mcp.WithBoolean("update-auth-data", + mcp.Description("Whether to update authentication data during source update. Optional for 'update' only.")), ) } @@ -218,18 +247,27 @@ func (b *PulsarAdminSourcesToolBuilder) buildSourcesHandler(readOnly bool) func( return b.handleListBuiltInSources(ctx, admin) } - // Extract common parameters (all operations except list-built-in require tenant and namespace) - tenant, err := request.RequireString("tenant") - if err != nil { - return mcp.NewToolResultError(fmt.Sprintf("Missing required parameter 'tenant': %v. A tenant is required for operation '%s'.", err, operation)), nil + args := request.GetArguments() + + if operation == "create" || operation == "update" { + name, _ := getStringArg(args, "name") + tenant, _ := getStringArg(args, "tenant") + namespace, _ := getStringArg(args, "namespace") + if operation == "create" { + return b.handleSourceCreate(ctx, admin, tenant, namespace, name, request) + } + return b.handleSourceUpdate(ctx, admin, tenant, namespace, name, request) } - namespace, err := request.RequireString("namespace") - if err != nil { - return mcp.NewToolResultError(fmt.Sprintf("Missing required parameter 'namespace': %v. A namespace is required for operation '%s'.", err, operation)), nil + tenant, _ := getStringArg(args, "tenant") + namespace, _ := getStringArg(args, "namespace") + if tenant == "" { + tenant = defaultTenant + } + if namespace == "" { + namespace = defaultNamespace } - // For all operations except 'list', name is required var name string if operation != "list" { name, err = request.RequireString("name") @@ -246,10 +284,6 @@ func (b *PulsarAdminSourcesToolBuilder) buildSourcesHandler(readOnly bool) func( return b.handleSourceGet(ctx, admin, tenant, namespace, name) case "status": return b.handleSourceStatus(ctx, admin, tenant, namespace, name) - case "create": - return b.handleSourceCreate(ctx, admin, request) - case "update": - return b.handleSourceUpdate(ctx, admin, request) case "delete": return b.handleSourceDelete(ctx, admin, tenant, namespace, name) case "start": @@ -319,232 +353,90 @@ func (b *PulsarAdminSourcesToolBuilder) handleSourceStatus(_ context.Context, ad } // handleSourceCreate handles creating a new source -func (b *PulsarAdminSourcesToolBuilder) handleSourceCreate(_ context.Context, admin cmdutils.Client, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { - tenant, err := request.RequireString("tenant") - if err != nil { - return mcp.NewToolResultError(fmt.Sprintf("Failed to get tenant: %v", err)), nil +func (b *PulsarAdminSourcesToolBuilder) handleSourceCreate(_ context.Context, admin cmdutils.Client, tenant, namespace, name string, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + if _, err := request.RequireString("destination-topic-name"); err != nil { + return mcp.NewToolResultError(fmt.Sprintf("Missing required parameter 'destination-topic-name': %v. This is the Pulsar topic where the source will publish data.", err)), nil } - namespace, err := request.RequireString("namespace") + config, archiveArg, sourceTypeArg, err := b.buildSourceConfig(tenant, namespace, name, request) if err != nil { - return mcp.NewToolResultError(fmt.Sprintf("Failed to get namespace: %v", err)), nil - } - - name, err := request.RequireString("name") - if err != nil { - return mcp.NewToolResultError(fmt.Sprintf("Failed to get name: %v", err)), nil - } - - // Create a new SourceData object - sourceData := &utils.SourceData{ - Tenant: tenant, - Namespace: namespace, - Name: name, - SourceConf: &utils.SourceConfig{}, - } - - // Get optional parameters - archive := request.GetString("archive", "") - if archive != "" { - sourceData.Archive = archive - } - - sourceType := request.GetString("source-type", "") - if sourceType != "" { - sourceData.SourceType = sourceType - } - - destTopic := request.GetString("destination-topic-name", "") - if destTopic != "" { - sourceData.DestinationTopicName = destTopic + return mcp.NewToolResultError(fmt.Sprintf("Failed to build source configuration for '%s': %v. Please verify all required parameters are provided correctly.", name, err)), nil } - deserializationClassName := request.GetString("deserialization-classname", "") - if deserializationClassName != "" { - sourceData.DeserializationClassName = deserializationClassName + if err := validateSourceArchiveArgs(archiveArg, sourceTypeArg); err != nil { + return mcp.NewToolResultError(fmt.Sprintf("Invalid parameters: %v", err)), nil } - schemaType := request.GetString("schema-type", "") - if schemaType != "" { - sourceData.SchemaType = schemaType - } - - className := request.GetString("classname", "") - if className != "" { - sourceData.ClassName = className - } - - processingGuarantees := request.GetString("processing-guarantees", "") - if processingGuarantees != "" { - sourceData.ProcessingGuarantees = processingGuarantees - } - - parallelismFloat := request.GetFloat("parallelism", 1) - if parallelismFloat >= 0 { - sourceData.Parallelism = int(parallelismFloat) - } - - // Get source config if available - var sourceConfigMap map[string]interface{} - sourceConfigObj, ok := request.GetArguments()["source-config"] - if ok && sourceConfigObj != nil { - if configMap, isMap := sourceConfigObj.(map[string]interface{}); isMap { - sourceConfigMap = configMap - // Convert to JSON string - sourceConfigJSON, err := json.Marshal(sourceConfigMap) - if err != nil { - return mcp.NewToolResultError(fmt.Sprintf("Failed to marshal source-config: %v. Ensure the source configuration is a valid JSON object.", err)), nil - } - sourceData.SourceConfigString = string(sourceConfigJSON) - } - } - - // Validate inputs - if sourceData.Archive == "" && sourceData.SourceType == "" { - return mcp.NewToolResultError("Missing required parameter: Either 'archive' or 'source-type' must be specified for source creation. Use 'archive' for custom connectors or 'source-type' for built-in connectors."), nil + uploadArchive, err := b.resolveSourceArchive(admin, config, archiveArg, sourceTypeArg) + if err != nil { + return mcp.NewToolResultError(fmt.Sprintf("Failed to resolve source archive: %v", err)), nil } - if sourceData.Archive != "" && sourceData.SourceType != "" { - return mcp.NewToolResultError("Invalid parameters: Cannot specify both 'archive' and 'source-type'. Use only one of these parameters based on your connector type."), nil + b.applySourceDefaults(config) + if config.Name == "" { + return mcp.NewToolResultError("Source name not specified. Provide the 'name' parameter or set it in the source-config-file."), nil } - - if sourceData.DestinationTopicName == "" { + if config.TopicName == "" { return mcp.NewToolResultError("Missing required parameter: 'destination-topic-name' must be specified. This is the Pulsar topic where the source will publish data."), nil } - - // Process the arguments - err = b.processSourceArguments(sourceData) - if err != nil { - return mcp.NewToolResultError(fmt.Sprintf("Failed to process arguments: %v", err)), nil + if err := validateSourceConfig(config); err != nil { + return mcp.NewToolResultError(fmt.Sprintf("Failed to validate source configuration for '%s': %v.", config.Name, err)), nil } - // Create the source - if sourceData.Archive != "" && b.isPackageURLSupported(sourceData.Archive) { - err = admin.Sources().CreateSourceWithURL(sourceData.SourceConf, sourceData.Archive) + if uploadArchive != "" && b.isPackageURLSupported(uploadArchive) { + err = admin.Sources().CreateSourceWithURL(config, uploadArchive) } else { - err = admin.Sources().CreateSource(sourceData.SourceConf, sourceData.Archive) + err = admin.Sources().CreateSource(config, uploadArchive) } if err != nil { return mcp.NewToolResultError(fmt.Sprintf("Failed to create source '%s' in tenant '%s' namespace '%s': %v. Verify all parameters are correct and required resources exist.", - name, tenant, namespace, err)), nil + config.Name, config.Tenant, config.Namespace, err)), nil } return mcp.NewToolResultText(fmt.Sprintf("Created source '%s' successfully in tenant '%s' namespace '%s'. The source will start pulling data from the external system and publishing to the destination topic.", - name, tenant, namespace)), nil + config.Name, config.Tenant, config.Namespace)), nil } // handleSourceUpdate handles updating an existing source -func (b *PulsarAdminSourcesToolBuilder) handleSourceUpdate(_ context.Context, admin cmdutils.Client, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { - tenant, err := request.RequireString("tenant") +func (b *PulsarAdminSourcesToolBuilder) handleSourceUpdate(_ context.Context, admin cmdutils.Client, tenant, namespace, name string, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + config, archiveArg, sourceTypeArg, err := b.buildSourceConfig(tenant, namespace, name, request) if err != nil { - return mcp.NewToolResultError(fmt.Sprintf("Failed to get tenant: %v", err)), nil + return mcp.NewToolResultError(fmt.Sprintf("Failed to build source configuration for '%s': %v. Please verify all parameters are provided correctly.", name, err)), nil } - namespace, err := request.RequireString("namespace") - if err != nil { - return mcp.NewToolResultError(fmt.Sprintf("Failed to get namespace: %v", err)), nil + if err := validateSourceArchiveArgs(archiveArg, sourceTypeArg); err != nil { + return mcp.NewToolResultError(fmt.Sprintf("Invalid parameters: %v", err)), nil } - name, err := request.RequireString("name") + uploadArchive, err := b.resolveSourceArchive(admin, config, archiveArg, sourceTypeArg) if err != nil { - return mcp.NewToolResultError(fmt.Sprintf("Failed to get name: %v", err)), nil - } - - // Create a new SourceData object - sourceData := &utils.SourceData{ - Tenant: tenant, - Namespace: namespace, - Name: name, - SourceConf: &utils.SourceConfig{}, - } - - // Get optional parameters - archive := request.GetString("archive", "") - if archive != "" { - sourceData.Archive = archive - } - - sourceType := request.GetString("source-type", "") - if sourceType != "" { - sourceData.SourceType = sourceType - } - - destTopic := request.GetString("destination-topic-name", "") - if destTopic != "" { - sourceData.DestinationTopicName = destTopic - } - - deserializationClassName := request.GetString("deserialization-classname", "") - if deserializationClassName != "" { - sourceData.DeserializationClassName = deserializationClassName - } - - schemaType := request.GetString("schema-type", "") - if schemaType != "" { - sourceData.SchemaType = schemaType - } - - className := request.GetString("classname", "") - if className != "" { - sourceData.ClassName = className + return mcp.NewToolResultError(fmt.Sprintf("Failed to resolve source archive: %v", err)), nil } - processingGuarantees := request.GetString("processing-guarantees", "") - if processingGuarantees != "" { - sourceData.ProcessingGuarantees = processingGuarantees + b.applySourceDefaults(config) + if config.Name == "" { + return mcp.NewToolResultError("Source name not specified. Provide the 'name' parameter or set it in the source-config-file."), nil } - parallelismFloat := request.GetFloat("parallelism", 1) - if parallelismFloat >= 0 { - sourceData.Parallelism = int(parallelismFloat) + updateOptions := utils.NewUpdateOptions() + if updateAuthData, ok := getBoolArg(request.GetArguments(), "update-auth-data"); ok { + updateOptions.UpdateAuthData = updateAuthData } - // Get source config if available - var sourceConfigMap map[string]interface{} - sourceConfigObj, ok := request.GetArguments()["source-config"] - if ok && sourceConfigObj != nil { - if configMap, isMap := sourceConfigObj.(map[string]interface{}); isMap { - sourceConfigMap = configMap - // Convert to JSON string - sourceConfigJSON, err := json.Marshal(sourceConfigMap) - if err != nil { - return mcp.NewToolResultError(fmt.Sprintf("Failed to marshal source-config: %v. Ensure the source configuration is a valid JSON object.", err)), nil - } - sourceData.SourceConfigString = string(sourceConfigJSON) - } - } - - // Validate inputs if both are specified - if sourceData.Archive != "" && sourceData.SourceType != "" { - return mcp.NewToolResultError("Invalid parameters: Cannot specify both 'archive' and 'source-type'. Use only one of these parameters based on your connector type."), nil - } - - // Process the arguments - err = b.processSourceArguments(sourceData) - if err != nil { - return mcp.NewToolResultError(fmt.Sprintf("Failed to process arguments: %v", err)), nil - } - - // Create update options - updateOptions := &utils.UpdateOptions{ - UpdateAuthData: true, - } - - // Update the source - if sourceData.Archive != "" && b.isPackageURLSupported(sourceData.Archive) { - err = admin.Sources().UpdateSourceWithURL(sourceData.SourceConf, sourceData.Archive, updateOptions) + if uploadArchive != "" && b.isPackageURLSupported(uploadArchive) { + err = admin.Sources().UpdateSourceWithURL(config, uploadArchive, updateOptions) } else { - err = admin.Sources().UpdateSource(sourceData.SourceConf, sourceData.Archive, updateOptions) + err = admin.Sources().UpdateSource(config, uploadArchive, updateOptions) } if err != nil { return mcp.NewToolResultError(fmt.Sprintf("Failed to update source '%s' in tenant '%s' namespace '%s': %v. Verify the source exists and all parameters are valid.", - name, tenant, namespace, err)), nil + config.Name, config.Tenant, config.Namespace, err)), nil } return mcp.NewToolResultText(fmt.Sprintf("Updated source '%s' successfully in tenant '%s' namespace '%s'. The source may need to be restarted to apply all changes.", - name, tenant, namespace)), nil + config.Name, config.Tenant, config.Namespace)), nil } // handleSourceDelete handles deleting a source @@ -611,98 +503,238 @@ func (b *PulsarAdminSourcesToolBuilder) handleListBuiltInSources(_ context.Conte return mcp.NewToolResultText(string(sourcesJSON)), nil } -// processSourceArguments is a simplified version of the pulsarctl function to process source arguments -func (b *PulsarAdminSourcesToolBuilder) processSourceArguments(sourceData *utils.SourceData) error { - // Initialize config if needed - if sourceData.SourceConf == nil { - sourceData.SourceConf = new(utils.SourceConfig) +func (b *PulsarAdminSourcesToolBuilder) buildSourceConfig(tenant, namespace, name string, request mcp.CallToolRequest) (*utils.SourceConfig, string, string, error) { + config := &utils.SourceConfig{} + args := request.GetArguments() + + if configFile, ok := getStringArg(args, "source-config-file"); ok && configFile != "" { + //nolint:gosec + data, err := os.ReadFile(configFile) + if err != nil { + return nil, "", "", fmt.Errorf("load source config file failed: %w", err) + } + if err := yaml.Unmarshal(data, config); err != nil { + return nil, "", "", fmt.Errorf("unmarshal source config file error: %w", err) + } } - // Set basic config values - sourceData.SourceConf.Tenant = sourceData.Tenant - sourceData.SourceConf.Namespace = sourceData.Namespace - sourceData.SourceConf.Name = sourceData.Name + if tenant != "" { + config.Tenant = tenant + } + if namespace != "" { + config.Namespace = namespace + } + if name != "" { + config.Name = name + } + + if destinationTopic, ok := getStringArg(args, "destination-topic-name"); ok && destinationTopic != "" { + config.TopicName = destinationTopic + } + + if deserializationClassName, ok := getStringArg(args, "deserialization-classname"); ok && deserializationClassName != "" { + config.SerdeClassName = deserializationClassName + } + + if schemaType, ok := getStringArg(args, "schema-type"); ok && schemaType != "" { + config.SchemaType = schemaType + } - // Set destination topic if provided - if sourceData.DestinationTopicName != "" { - sourceData.SourceConf.TopicName = sourceData.DestinationTopicName + if className, ok := getStringArg(args, "classname"); ok && className != "" { + config.ClassName = className } - // Set deserialization class name if provided - if sourceData.DeserializationClassName != "" { - sourceData.SourceConf.SerdeClassName = sourceData.DeserializationClassName + if processingGuarantees, ok := getStringArg(args, "processing-guarantees"); ok && processingGuarantees != "" { + config.ProcessingGuarantees = processingGuarantees } - // Set schema type if provided - if sourceData.SchemaType != "" { - sourceData.SourceConf.SchemaType = sourceData.SchemaType + if parallelism, ok, err := parseOptionalIntArg(args, "parallelism"); err != nil { + return nil, "", "", err + } else if ok { + config.Parallelism = parallelism + } + + if cpu, ok, err := parseOptionalFloatArg(args, "cpu"); err != nil { + return nil, "", "", err + } else if ok { + config.Resources = ensureResources(config.Resources) + config.Resources.CPU = cpu + } + + if ram, ok, err := parseOptionalInt64Arg(args, "ram"); err != nil { + return nil, "", "", err + } else if ok { + config.Resources = ensureResources(config.Resources) + config.Resources.RAM = ram + } + + if disk, ok, err := parseOptionalInt64Arg(args, "disk"); err != nil { + return nil, "", "", err + } else if ok { + config.Resources = ensureResources(config.Resources) + config.Resources.Disk = disk + } + + if sourceConfigValue, exists := args["source-config"]; exists && sourceConfigValue != nil { + sourceConfig, err := decodeInterfaceMap(sourceConfigValue) + if err != nil { + return nil, "", "", fmt.Errorf("invalid source-config: %w", err) + } + config.Configs = sourceConfig } - // Set class name if provided - if sourceData.ClassName != "" { - sourceData.SourceConf.ClassName = sourceData.ClassName + if producerConfigValue, exists := args["producer-config"]; exists && producerConfigValue != nil { + producerConfig, err := decodeProducerConfig(producerConfigValue) + if err != nil { + return nil, "", "", fmt.Errorf("invalid producer-config: %w", err) + } + config.ProducerConfig = producerConfig } - // Set processing guarantees if provided - if sourceData.ProcessingGuarantees != "" { - sourceData.SourceConf.ProcessingGuarantees = sourceData.ProcessingGuarantees + if batchBuilder, ok := getStringArg(args, "batch-builder"); ok && batchBuilder != "" { + config.BatchBuilder = batchBuilder } - // Set parallelism if provided - if sourceData.Parallelism != 0 { - sourceData.SourceConf.Parallelism = sourceData.Parallelism - } else if sourceData.SourceConf.Parallelism <= 0 { - sourceData.SourceConf.Parallelism = 1 + if batchSourceConfigValue, exists := args["batch-source-config"]; exists && batchSourceConfigValue != nil { + batchSourceConfig, err := decodeBatchSourceConfig(batchSourceConfigValue) + if err != nil { + return nil, "", "", fmt.Errorf("invalid batch-source-config: %w", err) + } + config.BatchSourceConfig = batchSourceConfig } - // Handle archive and source-type - if sourceData.Archive != "" && sourceData.SourceType != "" { - return fmt.Errorf("cannot specify both archive and source-type") + if customRuntimeOptions, ok := getStringArg(args, "custom-runtime-options"); ok && customRuntimeOptions != "" { + config.CustomRuntimeOptions = customRuntimeOptions } - if sourceData.Archive != "" { - sourceData.SourceConf.Archive = sourceData.Archive + if secretsValue, exists := args["secrets"]; exists && secretsValue != nil { + secrets, err := decodeInterfaceMap(secretsValue) + if err != nil { + return nil, "", "", fmt.Errorf("invalid secrets: %w", err) + } + config.Secrets = secrets } - if sourceData.SourceType != "" { - // In a real implementation, we would validate the source type here - sourceData.SourceConf.Archive = sourceData.SourceType + b.normalizeSourceConfigMaps(config) + + archiveArg, _ := getStringArg(args, "archive") + sourceTypeArg, _ := getStringArg(args, "source-type") + + return config, archiveArg, sourceTypeArg, nil +} + +func (b *PulsarAdminSourcesToolBuilder) normalizeSourceConfigMaps(config *utils.SourceConfig) { + if config.Configs != nil { + if converted, ok := convertMap(config.Configs).(map[string]interface{}); ok { + config.Configs = converted + } } - // Parse source config if provided - if sourceData.SourceConfigString != "" { - var configs map[string]interface{} - if err := json.Unmarshal([]byte(sourceData.SourceConfigString), &configs); err != nil { - return fmt.Errorf("failed to parse source config: %v", err) + if config.Secrets != nil { + if converted, ok := convertMap(config.Secrets).(map[string]interface{}); ok { + config.Secrets = converted } - sourceData.SourceConf.Configs = configs } + if config.Secrets == nil { + config.Secrets = make(map[string]interface{}) + } +} + +func (b *PulsarAdminSourcesToolBuilder) applySourceDefaults(config *utils.SourceConfig) { + if config.Tenant == "" { + config.Tenant = defaultTenant + } + if config.Namespace == "" { + config.Namespace = defaultNamespace + } + if config.Parallelism <= 0 { + config.Parallelism = 1 + } +} + +func validateSourceConfig(config *utils.SourceConfig) error { + if config.Archive == "" { + return fmt.Errorf("source archive not specified") + } + if config.Name == "" { + return fmt.Errorf("source name not specified") + } return nil } -// isPackageURLSupported checks if the package URL is supported -// Validates URLs for Pulsar source packages -func (b *PulsarAdminSourcesToolBuilder) isPackageURLSupported(archive string) bool { - if archive == "" { - return false +func validateSourceArchiveArgs(archive, sourceType string) error { + if archive != "" && sourceType != "" { + return fmt.Errorf("cannot specify both 'archive' and 'source-type'") + } + return nil +} + +func (b *PulsarAdminSourcesToolBuilder) resolveSourceArchive(admin cmdutils.Client, config *utils.SourceConfig, archive, sourceType string) (string, error) { + if sourceType != "" { + resolved, err := b.validateSourceType(admin, sourceType) + if err != nil { + return "", err + } + config.Archive = resolved + return "", nil + } + + if archive != "" { + config.Archive = archive + return archive, nil + } + + if config.Archive != "" { + if strings.HasPrefix(config.Archive, "builtin://") { + return "", nil + } + return config.Archive, nil } - // Check for supported URL schemes for Pulsar source packages - supportedSchemes := []string{ - "http://", - "https://", - "file://", - "function://", // Pulsar function package URL - "source://", // Pulsar source package URL + return "", nil +} + +func (b *PulsarAdminSourcesToolBuilder) validateSourceType(admin cmdutils.Client, sourceType string) (string, error) { + builtins, err := admin.Sources().GetBuiltInSources() + if err != nil { + return "", fmt.Errorf("failed to list built-in sources: %w", err) } - for _, scheme := range supportedSchemes { - if strings.HasPrefix(archive, scheme) { - return true + names := make([]string, 0, len(builtins)) + for _, builtin := range builtins { + names = append(names, builtin.Name) + if builtin.Name == sourceType { + return "builtin://" + sourceType, nil } } - // Also check if it's a local file path (not a URL) - return !strings.Contains(archive, "://") + sort.Strings(names) + return "", fmt.Errorf("invalid source-type %q. Available sources: %s", sourceType, strings.Join(names, ", ")) +} + +func decodeProducerConfig(value interface{}) (*utils.ProducerConfig, error) { + var config utils.ProducerConfig + if err := decodeInto(value, &config); err != nil { + return nil, err + } + return &config, nil +} + +func decodeBatchSourceConfig(value interface{}) (*utils.BatchSourceConfig, error) { + var config utils.BatchSourceConfig + if err := decodeInto(value, &config); err != nil { + return nil, err + } + return &config, nil +} + +// isPackageURLSupported checks if the package URL is supported +// Validates URLs for Pulsar source packages +func (b *PulsarAdminSourcesToolBuilder) isPackageURLSupported(archive string) bool { + return archive != "" && (strings.HasPrefix(archive, "http") || + strings.HasPrefix(archive, "file") || + strings.HasPrefix(archive, "function") || + strings.HasPrefix(archive, "sink") || + strings.HasPrefix(archive, "source")) } diff --git a/pkg/mcp/builders/pulsar/sources_parity_test.go b/pkg/mcp/builders/pulsar/sources_parity_test.go new file mode 100644 index 0000000..7207360 --- /dev/null +++ b/pkg/mcp/builders/pulsar/sources_parity_test.go @@ -0,0 +1,73 @@ +// Copyright 2025 StreamNative +// +// 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 pulsar + +import ( + "testing" + + "github.com/mark3labs/mcp-go/mcp" + "github.com/stretchr/testify/require" +) + +func TestBuildSourceConfigDefaults(t *testing.T) { + builder := NewPulsarAdminSourcesToolBuilder() + + req := mcp.CallToolRequest{Params: mcp.CallToolParams{Arguments: map[string]any{}}} + config, _, _, err := builder.buildSourceConfig("", "", "name", req) + require.NoError(t, err) + + builder.applySourceDefaults(config) + require.Equal(t, defaultTenant, config.Tenant) + require.Equal(t, defaultNamespace, config.Namespace) + require.Equal(t, 1, config.Parallelism) +} + +func TestBuildSourceConfigMaps(t *testing.T) { + builder := NewPulsarAdminSourcesToolBuilder() + + req := mcp.CallToolRequest{Params: mcp.CallToolParams{Arguments: map[string]any{ + "source-config": map[string]any{ + "path": "/tmp/in", + }, + "secrets": map[string]any{ + "secret": map[string]any{ + "value": "x", + }, + }, + "producer-config": map[string]any{ + "maxPendingMessages": 10, + }, + "batch-source-config": map[string]any{ + "discoveryTriggererClassName": "example", + }, + "batch-builder": "DEFAULT", + }}} + config, _, _, err := builder.buildSourceConfig("tenant", "namespace", "name", req) + require.NoError(t, err) + require.Equal(t, "/tmp/in", config.Configs["path"]) + require.NotNil(t, config.Secrets) + require.NotNil(t, config.ProducerConfig) + require.Equal(t, 10, config.ProducerConfig.MaxPendingMessages) + require.NotNil(t, config.BatchSourceConfig) + require.Equal(t, "example", config.BatchSourceConfig.DiscoveryTriggererClassName) + require.Equal(t, "DEFAULT", config.BatchBuilder) +} + +func TestValidateSourceArchiveArgs(t *testing.T) { + require.NoError(t, validateSourceArchiveArgs("", "")) + require.NoError(t, validateSourceArchiveArgs("archive.nar", "")) + require.NoError(t, validateSourceArchiveArgs("", "file")) + require.Error(t, validateSourceArchiveArgs("archive.nar", "file")) +} diff --git a/scripts/e2e-test.sh b/scripts/e2e-test.sh index de0eb21..af5b62a 100755 --- a/scripts/e2e-test.sh +++ b/scripts/e2e-test.sh @@ -116,7 +116,7 @@ setup_pulsar() { -e PULSAR_PREFIX_brokerClientAuthenticationParameters="token:${ADMIN_TOKEN}" \ -v "$TOKEN_SECRET_FILE:/pulsarctl/test/auth/token/secret.key:ro" \ "$PULSAR_IMAGE" \ - bash -lc 'set -- $(hostname -i); export PULSAR_PREFIX_advertisedAddress=$1; bin/apply-config-from-env.py /pulsar/conf/standalone.conf; exec bin/pulsar standalone' \ + bash -lc 'set -- $(hostname -i); export PULSAR_PREFIX_advertisedAddress=$1; export JAVA_HOME=/opt/jvm; export PATH="$JAVA_HOME/bin:$PATH"; bin/apply-config-from-env.py /pulsar/conf/standalone.conf; exec bin/pulsar standalone' \ >/dev/null log "waiting for pulsar to be ready" diff --git a/sdk/sdk-apiserver/go.mod b/sdk/sdk-apiserver/go.mod index 9bdd735..e2040d2 100644 --- a/sdk/sdk-apiserver/go.mod +++ b/sdk/sdk-apiserver/go.mod @@ -1,9 +1,7 @@ module github.com/streamnative/streamnative-mcp-server/sdk/sdk-apiserver -go 1.23.0 +go 1.24.0 toolchain go1.24.4 -require golang.org/x/oauth2 v0.27.0 - -require github.com/google/go-cmp v0.7.0 // indirect +require golang.org/x/oauth2 v0.34.0 diff --git a/sdk/sdk-apiserver/go.sum b/sdk/sdk-apiserver/go.sum index c55f307..51ef1a2 100644 --- a/sdk/sdk-apiserver/go.sum +++ b/sdk/sdk-apiserver/go.sum @@ -1,4 +1 @@ -github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= -github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= -golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= +golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=