diff --git a/README.md b/README.md index 2e8fe69..c1f0701 100644 --- a/README.md +++ b/README.md @@ -16,4 +16,4 @@ monorepo of golang apps for fun and profit ### `nomad-service-cleaner` -`nomad-service-cleaner` is a one-shot program that cleans p defunct Nomad service registry entries. \ No newline at end of file +`nomad-service-cleaner` is a one-shot program that cleans p defunct Nomad service registry entries. diff --git a/docker-hermes/.gitignore b/docker-hermes/.gitignore new file mode 100644 index 0000000..a067a4b --- /dev/null +++ b/docker-hermes/.gitignore @@ -0,0 +1 @@ +docker-hermes diff --git a/docker-hermes/Dockerfile b/docker-hermes/Dockerfile new file mode 100644 index 0000000..9aff0c5 --- /dev/null +++ b/docker-hermes/Dockerfile @@ -0,0 +1,42 @@ +# Multi-stage Dockerfile for docker-hermes +FROM golang:1.24-alpine AS builder + +# Install build dependencies +RUN apk add --no-cache git + +# Set working directory +WORKDIR /src + +# Copy source code +COPY . . + +WORKDIR /src/docker-hermes + +# Build the binary +RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o docker-hermes . + +# Final stage +FROM alpine:latest + +# Install runtime dependencies +RUN apk --no-cache add ca-certificates docker-cli + +# Create non-root user +RUN addgroup -g 1001 -S docker-hermes && \ + adduser -u 1001 -S docker-hermes -G docker-hermes + +# Copy binary from builder +COPY --from=builder /src/docker-hermes/docker-hermes /usr/local/bin/docker-hermes + +# Create config directory +RUN mkdir -p /etc/docker-hermes && \ + chown docker-hermes:docker-hermes /etc/docker-hermes + +# Switch to non-root user +USER docker-hermes + +# Expose metrics port +EXPOSE 9090 + +# Default command +ENTRYPOINT ["docker-hermes"] diff --git a/docker-hermes/README.md b/docker-hermes/README.md new file mode 100644 index 0000000..76e7e3c --- /dev/null +++ b/docker-hermes/README.md @@ -0,0 +1,19 @@ +# docker-hermes + +## goals + +docker-hermes is a sort of service registration system. the goal is to create an agent along with a server. + +the agent should: +- run on each docker node/instance and report labels to a central database/system. +- labels should be kept fresh in the database as long as the container they are associated with is still running +- information about the host and ports associated with the labelled container should also be reported to the central database/system. +- expose a prometheus endpoint detailing resource usage (this should be relatively built-in to the apps framework) +- expose traces for the gathering and emitting process +- support specifying a label prefix/"namespacing" + +the server should: +- run in a central location, attached to the database. +- serve a Prometheus HTTP SD endpoint containing any services that have a proper set of prometheus labels. +- have an API that allows for lookup of containers/services based on label contents +- have an API that allows for lookup of labels by container information diff --git a/docker-hermes/agent/agent.go b/docker-hermes/agent/agent.go new file mode 100644 index 0000000..c0f2718 --- /dev/null +++ b/docker-hermes/agent/agent.go @@ -0,0 +1,226 @@ +package agent + +import ( + "context" + "fmt" + "net/http" + "sync" + "time" + + "github.com/docker/docker/api/types/events" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" + "github.com/sirupsen/logrus" + + "github.com/pirogoeth/apps/docker-hermes/redis" + "github.com/pirogoeth/apps/docker-hermes/types" +) + +// Agent represents the docker-hermes agent +type Agent struct { + config *types.Config + dockerClient *DockerClient + storageClient types.StorageClient + reporter *Reporter + + // Metrics + containersTracked prometheus.Gauge + updatesSent prometheus.Counter + errorsTotal prometheus.Counter + + // State + containers map[string]*types.ContainerInfo + mu sync.RWMutex +} + +// NewAgent creates a new agent instance +func NewAgent(config *types.Config) (*Agent, error) { + dockerClient, err := NewDockerClient(config.Agent.DockerSocket) + if err != nil { + return nil, fmt.Errorf("failed to create Docker client: %w", err) + } + + // TODO: Resolve storage client type based on config + storageClient, err := redis.NewClient(config.Redis.URL) + if err != nil { + dockerClient.Close() + return nil, fmt.Errorf("failed to create storage client: %w", err) + } + + reporter := NewReporter(storageClient, ReporterOpts{ + HeartbeatInterval: config.Agent.HeartbeatInterval, + Hostname: config.Agent.Hostname, + LabelsConfig: &config.Labels, + TargetResolutionStrategy: config.Agent.TargetResolutionStrategy, + TargetResolutionHost: config.Agent.TargetResolutionHost, + }) + + // Initialize metrics + containersTracked := prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "docker_hermes_containers_tracked", + Help: "Number of containers currently being tracked", + }) + + updatesSent := prometheus.NewCounter(prometheus.CounterOpts{ + Name: "docker_hermes_updates_sent_total", + Help: "Total number of container updates sent to server", + }) + + errorsTotal := prometheus.NewCounter(prometheus.CounterOpts{ + Name: "docker_hermes_errors_total", + Help: "Total number of errors encountered", + }) + + // Register metrics + prometheus.MustRegister(containersTracked, updatesSent, errorsTotal) + + return &Agent{ + config: config, + dockerClient: dockerClient, + storageClient: storageClient, + reporter: reporter, + containersTracked: containersTracked, + updatesSent: updatesSent, + errorsTotal: errorsTotal, + containers: make(map[string]*types.ContainerInfo), + }, nil +} + +// Run starts the agent main loop +func (a *Agent) Run(ctx context.Context) error { + logrus.Info("Starting docker-hermes agent") + + // Initial scan + if err := a.performHeartbeat(ctx); err != nil { + logrus.Errorf("Initial heartbeat failed: %v", err) + a.errorsTotal.Inc() + } + + // Start event watcher + go func() { + if err := a.dockerClient.WatchContainers(ctx, a.config.Agent.Hostname, a.handleDockerEvent); err != nil { + logrus.Errorf("Docker event watcher failed: %v", err) + a.errorsTotal.Inc() + } + }() + + // Start heartbeat ticker + ticker := time.NewTicker(a.config.Agent.HeartbeatInterval) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + if err := a.performHeartbeat(ctx); err != nil { + logrus.Errorf("Heartbeat failed: %v", err) + a.errorsTotal.Inc() + } + case <-ctx.Done(): + logrus.Info("Agent shutting down") + return nil + } + } +} + +// performHeartbeat performs a full container scan and reports to server +func (a *Agent) performHeartbeat(ctx context.Context) error { + logrus.Debug("Performing heartbeat") + + // Scan all running containers + containers, err := a.dockerClient.ScanContainers(ctx, a.config.Agent.Hostname) + if err != nil { + return fmt.Errorf("failed to scan containers: %w", err) + } + + // Update internal state + a.mu.Lock() + newContainers := make(map[string]*types.ContainerInfo) + for _, container := range containers { + newContainers[container.ID] = container + } + a.containers = newContainers + a.mu.Unlock() + + // Update metrics + a.containersTracked.Set(float64(len(containers))) + + // Report all containers + for _, container := range containers { + if err := a.reporter.ReportContainer(ctx, container); err != nil { + logrus.Errorf("Failed to report container %s: %v", container.ID, err) + a.errorsTotal.Inc() + continue + } + a.updatesSent.Inc() + } + + logrus.Debugf("Heartbeat completed: %d containers reported", len(containers)) + return nil +} + +// handleDockerEvent handles Docker events +func (a *Agent) handleDockerEvent(container *types.ContainerInfo, eventType events.Action) { + ctx := context.Background() + + logrus.Debugf("Handling Docker event: %s for container %s", eventType, container.ID) + + switch eventType { + case "start": + // Update internal state + a.mu.Lock() + a.containers[container.ID] = container + a.mu.Unlock() + + // Report to server + if err := a.reporter.ReportContainer(ctx, container); err != nil { + logrus.Errorf("Failed to report container start: %v", err) + a.errorsTotal.Inc() + } else { + a.updatesSent.Inc() + } + + case "stop", "die": + // Remove from internal state + a.mu.Lock() + delete(a.containers, container.ID) + a.mu.Unlock() + + // Report removal to server + if err := a.reporter.ReportContainerRemoval(ctx, container); err != nil { + logrus.Errorf("Failed to report container removal: %v", err) + a.errorsTotal.Inc() + } else { + a.updatesSent.Inc() + } + } + + // Update metrics + a.mu.RLock() + trackedCount := len(a.containers) + a.mu.RUnlock() + a.containersTracked.Set(float64(trackedCount)) +} + +// Close closes the agent and cleans up resources +func (a *Agent) Close() error { + var errs []error + + if err := a.dockerClient.Close(); err != nil { + errs = append(errs, fmt.Errorf("failed to close Docker client: %w", err)) + } + + if err := a.storageClient.Close(); err != nil { + errs = append(errs, fmt.Errorf("failed to close storage client: %w", err)) + } + + if len(errs) > 0 { + return fmt.Errorf("errors during cleanup: %v", errs) + } + + return nil +} + +// GetMetricsHandler returns the Prometheus metrics handler +func (a *Agent) GetMetricsHandler() http.Handler { + return promhttp.Handler() +} diff --git a/docker-hermes/agent/docker.go b/docker-hermes/agent/docker.go new file mode 100644 index 0000000..16888f0 --- /dev/null +++ b/docker-hermes/agent/docker.go @@ -0,0 +1,233 @@ +package agent + +import ( + "context" + "fmt" + "strconv" + "strings" + "time" + + dockerTypes "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types/events" + "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/client" + "github.com/sirupsen/logrus" + + hermesTypes "github.com/pirogoeth/apps/docker-hermes/types" +) + +// DockerClient wraps Docker API client with container monitoring functionality +type DockerClient struct { + client *client.Client +} + +// NewDockerClient creates a new Docker client +func NewDockerClient(dockerSocket string) (*DockerClient, error) { + opts := []client.Opt{ + client.WithAPIVersionNegotiation(), + } + + if dockerSocket != "" { + opts = append(opts, client.WithHost(dockerSocket)) + } + + cli, err := client.NewClientWithOpts(opts...) + if err != nil { + return nil, fmt.Errorf("failed to create Docker client: %w", err) + } + + return &DockerClient{client: cli}, nil +} + +// Close closes the Docker client +func (d *DockerClient) Close() error { + return d.client.Close() +} + +// ScanContainers performs a full scan of running containers +func (d *DockerClient) ScanContainers(ctx context.Context, hostname string) ([]*hermesTypes.ContainerInfo, error) { + containers, err := d.client.ContainerList(ctx, container.ListOptions{ + All: false, // Only running containers + }) + if err != nil { + return nil, fmt.Errorf("failed to list containers: %w", err) + } + + var containerInfos []*hermesTypes.ContainerInfo + for _, container := range containers { + info, err := d.extractContainerInfo(ctx, container, hostname) + if err != nil { + logrus.Warnf("Failed to extract info for container %s: %v", container.ID, err) + continue + } + containerInfos = append(containerInfos, info) + } + + logrus.Infof("Scanned %d running containers", len(containerInfos)) + return containerInfos, nil +} + +// WatchContainers watches for Docker events and calls the callback +func (d *DockerClient) WatchContainers(ctx context.Context, hostname string, callback func(*hermesTypes.ContainerInfo, events.Action)) error { + eventFilter := filters.NewArgs() + eventFilter.Add("type", "container") + eventFilter.Add("event", "start") + eventFilter.Add("event", "stop") + eventFilter.Add("event", "die") + + eventChan, errChan := d.client.Events(ctx, dockerTypes.EventsOptions{ + Filters: eventFilter, + }) + + logrus.Info("Started watching Docker events") + + for { + select { + case event := <-eventChan: + if err := d.handleDockerEvent(ctx, event, hostname, callback); err != nil { + logrus.Errorf("Failed to handle Docker event: %v", err) + } + case err := <-errChan: + if err != nil { + return fmt.Errorf("received error from Docker: %w", err) + } + case <-ctx.Done(): + logrus.Info("Stopped watching Docker events") + return nil + } + } +} + +// handleDockerEvent processes a Docker event and extracts container info +func (d *DockerClient) handleDockerEvent(ctx context.Context, event events.Message, hostname string, callback func(*hermesTypes.ContainerInfo, events.Action)) error { + containerID := event.Actor.ID + eventType := event.Action + + logrus.Debugf("Docker event: %s for container %s", eventType, containerID) + + // For stop/die events, we don't need to fetch container details + if eventType == "stop" || eventType == "die" { + // Create a minimal container info for removal + containerInfo := &hermesTypes.ContainerInfo{ + ID: containerID, + Host: hostname, + State: "stopped", + LastSeen: time.Now(), + } + callback(containerInfo, eventType) + return nil + } + + // For start events, fetch full container details + if eventType == "start" { + container, err := d.client.ContainerInspect(ctx, containerID) + if err != nil { + return fmt.Errorf("failed to inspect container %s: %w", containerID, err) + } + + info, err := d.extractContainerInfoFromInspect(container, hostname) + if err != nil { + return fmt.Errorf("failed to extract container info: %w", err) + } + + callback(info, eventType) + } + + return nil +} + +// extractContainerInfo extracts container information from container list response +func (d *DockerClient) extractContainerInfo(ctx context.Context, container dockerTypes.Container, hostname string) (*hermesTypes.ContainerInfo, error) { + // Get detailed container info + inspect, err := d.client.ContainerInspect(ctx, container.ID) + if err != nil { + return nil, fmt.Errorf("failed to inspect container: %w", err) + } + + return d.extractContainerInfoFromInspect(inspect, hostname) +} + +// extractContainerInfoFromInspect extracts container information from inspect response +func (d *DockerClient) extractContainerInfoFromInspect(inspect dockerTypes.ContainerJSON, hostname string) (*hermesTypes.ContainerInfo, error) { + logrus.WithField("container", inspect.ID).Debugf("Extracting container info from inspect") + + // Extract ports + var ports []hermesTypes.PortInfo + for port, bindings := range inspect.NetworkSettings.Ports { + for _, binding := range bindings { + publicPort, _ := strconv.Atoi(binding.HostPort) + privatePort, _ := strconv.Atoi(port.Port()) + + ports = append(ports, hermesTypes.PortInfo{ + PrivatePort: privatePort, + PublicPort: publicPort, + Type: port.Proto(), + IP: binding.HostIP, + }) + } + } + + logrus.WithField("container", inspect.ID). + WithField("ports", ports). + Debugf("Extracted ports from container") + + name := strings.TrimPrefix(inspect.Name, "/") + + // Extract command + var command string + if len(inspect.Config.Cmd) > 0 { + command = strings.Join(inspect.Config.Cmd, " ") + } + + // Extract created time + createdAt, err := time.Parse(time.RFC3339Nano, inspect.Created) + if err != nil { + return nil, fmt.Errorf("failed to parse container creation timestamp: %w", err) + } + + // Extract network settings + networkSettings := hermesTypes.NetworkSettings{ + IPAddress: inspect.NetworkSettings.IPAddress, + Networks: make(map[string]hermesTypes.Network), + } + + for netName, netConfig := range inspect.NetworkSettings.Networks { + networkSettings.Networks[netName] = hermesTypes.Network{ + IPAddress: netConfig.IPAddress, + Gateway: netConfig.Gateway, + NetworkID: netConfig.NetworkID, + NetworkName: netName, + } + } + + containerInfo := &hermesTypes.ContainerInfo{ + ID: inspect.ID, + Name: name, + Host: hostname, + Labels: inspect.Config.Labels, + Ports: ports, + State: inspect.State.Status, + LastSeen: time.Now(), + CreatedAt: createdAt, + Image: inspect.Config.Image, + Command: command, + Status: inspect.State.Status, + NetworkSettings: networkSettings, + // LabelsConfig will be attached by the reporter + } + + logrus.WithField("container", containerInfo).Debugf("Extracted container info") + + return containerInfo, nil +} + +// GetContainerByID retrieves a specific container by ID +func (d *DockerClient) GetContainerByID(ctx context.Context, containerID string, hostname string) (*hermesTypes.ContainerInfo, error) { + inspect, err := d.client.ContainerInspect(ctx, containerID) + if err != nil { + return nil, fmt.Errorf("failed to inspect container: %w", err) + } + + return d.extractContainerInfoFromInspect(inspect, hostname) +} diff --git a/docker-hermes/agent/reporter.go b/docker-hermes/agent/reporter.go new file mode 100644 index 0000000..ff3f097 --- /dev/null +++ b/docker-hermes/agent/reporter.go @@ -0,0 +1,113 @@ +package agent + +import ( + "context" + "fmt" + "time" + + "github.com/sirupsen/logrus" + + "github.com/pirogoeth/apps/docker-hermes/types" +) + +type ReporterOpts struct { + HeartbeatInterval time.Duration + Hostname string + LabelsConfig *types.LabelsConfig + TargetResolutionStrategy types.TargetResolutionStrategy + TargetResolutionHost string +} + +// Reporter handles reporting container information to storage +type Reporter struct { + storageClient types.StorageClient + opts ReporterOpts +} + +// NewReporter creates a new reporter instance +func NewReporter(storageClient types.StorageClient, opts ReporterOpts) *Reporter { + return &Reporter{ + storageClient: storageClient, + opts: opts, + } +} + +// ReportContainer reports container information to storage +func (r *Reporter) ReportContainer(ctx context.Context, container *types.ContainerInfo) error { + // Attach labels config to container + container.LabelsConfig = r.opts.LabelsConfig + + // Filter labels by prefix if specified + if r.opts.LabelsConfig != nil && r.opts.LabelsConfig.LabelPrefix != "" { + container.Labels = container.FilterLabelsByPrefix(r.opts.LabelsConfig.LabelPrefix) + } + + // Pre-resolve targets for validation + targets := container.ResolveTargets(r.opts.TargetResolutionStrategy, r.opts.Hostname, r.opts.TargetResolutionHost) + if len(targets) > 0 { + // Log resolved targets for debugging + for i, target := range targets { + logrus.Debugf("Container %s target %d: %s%s%s", + container.ID, i, target.Scheme, target.Address, target.Path) + } + } + + // Set TTL to 2x heartbeat interval (default 60s) + ttl := 2 * r.opts.HeartbeatInterval + + // Store container data + if err := r.storageClient.StoreContainer(ctx, container, ttl); err != nil { + return fmt.Errorf("failed to store container: %w", err) + } + + // Publish update to stream + if err := r.storageClient.PublishUpdate(ctx, container); err != nil { + return fmt.Errorf("failed to publish update: %w", err) + } + + logrus.Debugf("Reported container %s on host %s", container.ID, container.Host) + return nil +} + +// ReportContainerRemoval reports container removal to storage +func (r *Reporter) ReportContainerRemoval(ctx context.Context, container *types.ContainerInfo) error { + // For removal, we create a minimal container info with stopped state + removalInfo := &types.ContainerInfo{ + ID: container.ID, + Name: container.Name, + Host: container.Host, + State: "stopped", + LastSeen: time.Now(), + } + + // Publish removal update to stream + if err := r.storageClient.PublishUpdate(ctx, removalInfo); err != nil { + return fmt.Errorf("failed to publish removal update: %w", err) + } + + logrus.Debugf("Reported container removal %s on host %s", container.ID, container.Host) + return nil +} + +// ReportBatch reports multiple containers in a batch +func (r *Reporter) ReportBatch(ctx context.Context, containers []*types.ContainerInfo) error { + var lastErr error + successCount := 0 + + for _, container := range containers { + if err := r.ReportContainer(ctx, container); err != nil { + logrus.Errorf("Failed to report container %s: %v", container.ID, err) + lastErr = err + } else { + successCount++ + } + } + + logrus.Debugf("Batch report completed: %d/%d containers reported successfully", successCount, len(containers)) + + if lastErr != nil { + return fmt.Errorf("batch report completed with errors, last error: %w", lastErr) + } + + return nil +} diff --git a/docker-hermes/cmd/agent.go b/docker-hermes/cmd/agent.go new file mode 100644 index 0000000..c9093fb --- /dev/null +++ b/docker-hermes/cmd/agent.go @@ -0,0 +1,64 @@ +package cmd + +import ( + "context" + "fmt" + "os" + + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + + "github.com/pirogoeth/apps/docker-hermes/agent" + "github.com/pirogoeth/apps/docker-hermes/types" + "github.com/pirogoeth/apps/pkg/system" +) + +var agentCmd = &cobra.Command{ + Use: "agent", + Short: "Run the docker-hermes agent", + Long: `Run the docker-hermes agent on a Docker host. + +The agent monitors Docker containers and reports their labels and metadata +to the central Redis server. It performs periodic heartbeats and responds +to Docker events in real-time.`, + Run: runAgent, +} + +func runAgent(cmd *cobra.Command, args []string) { + cfg := appStart(ComponentAgent) + + // Apply defaults + types.ApplyDefaults(cfg) + + logrus.Infof("Starting docker-hermes agent on host %s", cfg.Agent.Hostname) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + // Create agent + agent, err := agent.NewAgent(cfg) + if err != nil { + panic(fmt.Errorf("failed to create agent: %w", err)) + } + defer agent.Close() + + router, err := system.DefaultRouterWithTracing(ctx, cfg.Tracing) + if err != nil { + panic(fmt.Errorf("failed to create router: %w", err)) + } + + router.GET("/metrics", system.HttpHandlerToGinHandler(agent.GetMetricsHandler())) + + // Run agent + go agent.Run(ctx) + go router.Run(cfg.HTTP.ListenAddress) + + // Handle shutdown signals + sw := system.NewSignalWaiter(os.Interrupt) + sw.OnBeforeCancel(func(context.Context) error { + return agent.Close() + }) + sw.Wait(ctx, cancel) + + logrus.Info("Agent stopped") +} diff --git a/docker-hermes/cmd/query.go b/docker-hermes/cmd/query.go new file mode 100644 index 0000000..ca6a83f --- /dev/null +++ b/docker-hermes/cmd/query.go @@ -0,0 +1,185 @@ +package cmd + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + "os" + + "github.com/spf13/cobra" + + "github.com/pirogoeth/apps/docker-hermes/types" +) + +var queryCmd = &cobra.Command{ + Use: "query", + Short: "Query the docker-hermes server", + Long: `Query the docker-hermes server for container and label information. + +This command provides a CLI interface to the server's REST API, +allowing you to list containers, query by labels, and inspect +Prometheus targets.`, +} + +var ( + serverURL string + outputFormat string +) + +func init() { + queryCmd.PersistentFlags().StringVar(&serverURL, "server", "http://localhost:8080", "Server URL") + queryCmd.PersistentFlags().StringVar(&outputFormat, "output", "json", "Output format (json, yaml)") + + queryCmd.AddCommand(queryContainersCmd) + queryCmd.AddCommand(queryLabelsCmd) + queryCmd.AddCommand(queryHostsCmd) + queryCmd.AddCommand(queryTargetsCmd) +} + +var queryContainersCmd = &cobra.Command{ + Use: "containers [--host HOST]", + Short: "List all containers", + Long: "List all active containers tracked by the server", + Run: runQueryContainers, +} + +var queryLabelsCmd = &cobra.Command{ + Use: "labels [KEY]", + Short: "List labels or values for a specific label key", + Long: "List all unique label keys, or all values for a specific label key", + Run: runQueryLabels, +} + +var queryHostsCmd = &cobra.Command{ + Use: "hosts", + Short: "List all known hosts", + Long: "List all Docker hosts that have reported containers", + Run: runQueryHosts, +} + +var queryTargetsCmd = &cobra.Command{ + Use: "targets", + Short: "List Prometheus scrape targets", + Long: "List all containers configured for Prometheus scraping", + Run: runQueryTargets, +} + +func runQueryContainers(cmd *cobra.Command, args []string) { + url := serverURL + "/api/v1/containers" + + resp, err := http.Get(url) + if err != nil { + fmt.Printf("Error: %v\n", err) + os.Exit(1) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + body, _ := io.ReadAll(resp.Body) + fmt.Printf("Error: HTTP %d - %s\n", resp.StatusCode, string(body)) + os.Exit(1) + } + + var result map[string]interface{} + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + fmt.Printf("Error parsing response: %v\n", err) + os.Exit(1) + } + + outputJSON(result) +} + +func runQueryLabels(cmd *cobra.Command, args []string) { + var url string + if len(args) > 0 { + url = fmt.Sprintf("%s/api/v1/labels/%s", serverURL, args[0]) + } else { + url = serverURL + "/api/v1/labels" + } + + resp, err := http.Get(url) + if err != nil { + fmt.Printf("Error: %v\n", err) + os.Exit(1) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + body, _ := io.ReadAll(resp.Body) + fmt.Printf("Error: HTTP %d - %s\n", resp.StatusCode, string(body)) + os.Exit(1) + } + + var result map[string]interface{} + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + fmt.Printf("Error parsing response: %v\n", err) + os.Exit(1) + } + + outputJSON(result) +} + +func runQueryHosts(cmd *cobra.Command, args []string) { + url := serverURL + "/api/v1/hosts" + + resp, err := http.Get(url) + if err != nil { + fmt.Printf("Error: %v\n", err) + os.Exit(1) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + body, _ := io.ReadAll(resp.Body) + fmt.Printf("Error: HTTP %d - %s\n", resp.StatusCode, string(body)) + os.Exit(1) + } + + var result map[string]interface{} + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + fmt.Printf("Error parsing response: %v\n", err) + os.Exit(1) + } + + outputJSON(result) +} + +func runQueryTargets(cmd *cobra.Command, args []string) { + url := serverURL + "/prometheus/sd" + + resp, err := http.Get(url) + if err != nil { + fmt.Printf("Error: %v\n", err) + os.Exit(1) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + body, _ := io.ReadAll(resp.Body) + fmt.Printf("Error: HTTP %d - %s\n", resp.StatusCode, string(body)) + os.Exit(1) + } + + var result []types.PrometheusTarget + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + fmt.Printf("Error parsing response: %v\n", err) + os.Exit(1) + } + + outputJSON(result) +} + +func outputJSON(data interface{}) { + var buf bytes.Buffer + encoder := json.NewEncoder(&buf) + encoder.SetIndent("", " ") + + if err := encoder.Encode(data); err != nil { + fmt.Printf("Error formatting output: %v\n", err) + os.Exit(1) + } + + fmt.Print(buf.String()) +} diff --git a/docker-hermes/cmd/root.go b/docker-hermes/cmd/root.go new file mode 100644 index 0000000..97eb113 --- /dev/null +++ b/docker-hermes/cmd/root.go @@ -0,0 +1,65 @@ +package cmd + +import ( + "fmt" + + "github.com/pirogoeth/apps/docker-hermes/types" + "github.com/pirogoeth/apps/pkg/config" + "github.com/pirogoeth/apps/pkg/logging" + "github.com/pirogoeth/apps/pkg/tracing" + "github.com/spf13/cobra" +) + +const ( + AppName = "docker-hermes" + ComponentAgent = "agent" + ComponentServer = "server" + ComponentQuery = "query" +) + +var rootCmd = &cobra.Command{ + Use: "docker-hermes", + Short: "Docker service discovery system with Prometheus HTTP SD support", + Long: `docker-hermes is a service registration system for Docker containers. + +It consists of: +- Agent: Runs on each Docker host, monitors containers, reports to central server +- Server: Provides Prometheus HTTP SD endpoint and query APIs +- Query: CLI tool for interacting with the server API + +The agent monitors Docker containers and reports their labels and metadata +to a central Redis server. The server provides a Prometheus HTTP Service +Discovery endpoint and REST APIs for querying container information.`, +} + +func init() { + rootCmd.AddCommand(agentCmd) + rootCmd.AddCommand(serverCmd) + rootCmd.AddCommand(queryCmd) +} + +func appStart(component string) *types.Config { + logging.Setup( + logging.WithAppName(AppName), + logging.WithComponentName(component), + ) + + cfg, err := config.Load[types.Config]() + if err != nil { + panic(fmt.Errorf("could not start (config): %w", err)) + } + + tracing.Setup( + tracing.WithAppName(AppName), + tracing.WithComponentName(component), + tracing.WithConfig(cfg.CommonConfig.Tracing), + ) + + return cfg +} + +func Execute() { + if err := rootCmd.Execute(); err != nil { + fmt.Println(err) + } +} diff --git a/docker-hermes/cmd/server.go b/docker-hermes/cmd/server.go new file mode 100644 index 0000000..7eec535 --- /dev/null +++ b/docker-hermes/cmd/server.go @@ -0,0 +1,54 @@ +package cmd + +import ( + "context" + "fmt" + "os" + + "github.com/pirogoeth/apps/pkg/system" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + + "github.com/pirogoeth/apps/docker-hermes/server" + "github.com/pirogoeth/apps/docker-hermes/types" +) + +var serverCmd = &cobra.Command{ + Use: "server", + Short: "Run the docker-hermes server", + Long: `Run the docker-hermes server. + +The server provides: +- Prometheus HTTP Service Discovery endpoint +- REST API for querying container information +- Central storage and coordination via Redis`, + Run: runServer, +} + +func runServer(cmd *cobra.Command, args []string) { + cfg := appStart(ComponentServer) + + // Remove this or find a way to apply "dynamic" defaults + types.ApplyDefaults(cfg) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + // Create server + srv, err := server.NewServer(ctx, cfg) + if err != nil { + panic(fmt.Errorf("failed to create server: %w", err)) + } + defer srv.Close() + + // Run! + logrus.Infof("Starting docker-hermes server on %s", cfg.HTTP.ListenAddress) + go srv.Run(ctx) + + // Handle shutdown signals + sw := system.NewSignalWaiter(os.Interrupt) + sw.OnBeforeCancel(func(context.Context) error { + return srv.Close() + }) + sw.Wait(ctx, cancel) +} diff --git a/docker-hermes/config.yml.example b/docker-hermes/config.yml.example new file mode 100644 index 0000000..b120973 --- /dev/null +++ b/docker-hermes/config.yml.example @@ -0,0 +1,50 @@ +# docker-hermes configuration example +# This file can be used with CONFIG_TYPE=file and CONFIG_FILE=path/to/this/file + +# Label configuration (shared by agent and server) +labels: + # Filter label prefix for agent (empty = all labels) + # Only labels starting with this prefix will be processed + label_prefix: "" + + # Main label prefix (Traefik-style, default: "hermes") + # All target configuration uses this prefix + # Examples: + # hermes.prometheus.scrape=true + # hermes.targets.admin.prometheus.scrape=true + prefix: "hermes" + +# Redis connection +redis: + url: "redis://localhost:6379" + +# Agent configuration +agent: + docker_socket: "unix:///var/run/docker.sock" + heartbeat_interval: "30s" + hostname: "" # Will auto-detect if empty + + # Target resolution strategy + # Options: hostname, container_ip, host_port, custom + target_resolution_strategy: "hostname" + target_resolution_host: "" # Used with host_port or custom strategies + +# Server configuration +server: + prometheus_sd_path: "/prometheus/sd" + + # Target resolution strategy (for Prometheus SD) + target_resolution_strategy: "hostname" + target_resolution_host: "" + +# Common configuration +http: + listen_address: ":8080" + +tracing: + enabled: false + sampler_rate: 1.0 + exporter_endpoint: "" + exporter_protocol: "grpc" + exporter_insecure: false + exporter_headers: {} diff --git a/docker-hermes/docs/01-docker-hermes-implementation.plan.md b/docker-hermes/docs/01-docker-hermes-implementation.plan.md new file mode 100644 index 0000000..931539e --- /dev/null +++ b/docker-hermes/docs/01-docker-hermes-implementation.plan.md @@ -0,0 +1,209 @@ + +# Implement docker-hermes Service Discovery System + +## Architecture Summary + +**Components:** + +- **Agent**: Monitors Docker containers on each host, reports to server via Redis +- **Server**: Provides Prometheus HTTP SD endpoint and query APIs +- **Storage**: Redis with TTL-based expiry for automatic cleanup +- **Communication**: Redis Streams for agent→server updates, periodic heartbeats + +**Key Decisions:** + +- Single binary with subcommands (`agent`, `server`, `query`) +- Redis for both storage (with TTL) and messaging (Streams) +- Agents send full container state on heartbeats (every 30s default) +- Filter by label prefix on agent side (configurable, default: all labels) +- Support standard Prometheus labels (`prometheus.io/*`) + +## Implementation Steps + +### 1. Project Setup & Dependencies + +Create `main.go`, `go.mod`, and update workspace: + +- Module: `github.com/pirogoeth/apps/docker-hermes` +- Dependencies: Docker SDK, Redis client (`github.com/redis/go-redis/v9`), standard apps/pkg +- Add to `go.work` + +### 2. Core Types & Configuration + +**File: `types/types.go`** + +- `ContainerInfo`: ID, name, host, labels, ports, state, last seen timestamp +- `PrometheusTarget`: Host, port, labels for SD format +- Config structs for Agent and Server with Redis connection info + +**File: `types/config.go`** + +- `AgentConfig`: Docker socket, Redis connection, label prefix filter, heartbeat interval +- `ServerConfig`: Redis connection, HTTP listen address, Prometheus SD path +- Embed `pkg/config.CommonConfig` for logging/tracing + +### 3. Redis Client & Data Layer + +**File: `redis/client.go`** + +- Redis client wrapper with connection pooling +- Key schema: `hermes:containers:{host}:{containerID}` for container data +- Key schema: `hermes:stream:updates` for container update stream +- TTL management: Set expiry on container keys (2x heartbeat interval) + +**File: `redis/operations.go`** + +- `StoreContainer()`: Store container info with TTL +- `GetContainer()`: Retrieve container by host+ID +- `ListContainers()`: Get all active containers +- `QueryByLabels()`: Find containers matching label filters +- `PublishUpdate()`: Push update to Redis Stream + +### 4. Agent Implementation + +**File: `agent/docker.go`** + +- Docker client initialization +- `WatchContainers()`: Listen to Docker events (start, stop, die) +- `ScanContainers()`: Full scan of running containers on startup +- `ExtractContainerInfo()`: Parse container data, filter labels by prefix +- Extract network info and published ports + +**File: `agent/agent.go`** + +- Main agent loop with context support +- Periodic heartbeat ticker (configurable interval) +- On heartbeat: scan all containers, report full state to Redis +- On Docker event: immediate update to Redis Stream +- Prometheus metrics: containers tracked, updates sent, errors + +**File: `agent/reporter.go`** + +- Report container updates to Redis +- Handle connection failures with retry logic +- Batch updates for efficiency + +### 5. Server Implementation + +**File: `server/server.go`** + +- Main server with Gin router setup +- Register API routes and Prometheus SD endpoint +- Context lifecycle management + +**File: `server/prometheus.go`** + +- Implement Prometheus HTTP SD endpoint +- Query Redis for containers with `prometheus.io/scrape=true` +- Build target list with host:port, labels, and metadata +- Parse `prometheus.io/port`, `prometheus.io/path`, `prometheus.io/scheme` +- Return JSON in Prometheus SD format + +**File: `api/api.go`** + +- `GET /api/v1/containers`: List all active containers +- `GET /api/v1/containers/:host/:id`: Get specific container +- `POST /api/v1/containers/query`: Query containers by label filters +- `GET /api/v1/labels`: List all unique labels +- `GET /api/v1/labels/:key`: Get all values for a label key + +### 6. CLI Commands + +**File: `cmd/root.go`** + +- Root command setup with app metadata +- Common initialization: logging, tracing setup +- Register subcommands + +**File: `cmd/agent.go`** + +- `docker-hermes agent` command +- Load agent config, initialize Docker client +- Start agent with signal handling +- Graceful shutdown on SIGINT/SIGTERM + +**File: `cmd/server.go`** + +- `docker-hermes server` command +- Load server config, initialize Redis client +- Start HTTP server with Prometheus SD and API endpoints +- Graceful shutdown + +**File: `cmd/query.go`** (optional but useful) + +- `docker-hermes query containers`: List containers +- `docker-hermes query labels`: List labels +- `docker-hermes query targets`: Show Prometheus targets +- Helper for debugging and CLI interaction + +### 7. Testing & Validation + +- Unit tests for label filtering logic +- Integration test: Run agent against local Docker daemon +- Integration test: Query server API endpoints +- Manual test: Verify Prometheus SD JSON format +- Test TTL expiry: Stop container, wait for expiry + +## Key Files Structure + +``` +docker-hermes/ +├── main.go +├── go.mod +├── cmd/ +│ ├── root.go +│ ├── agent.go +│ ├── server.go +│ └── query.go +├── agent/ +│ ├── agent.go +│ ├── docker.go +│ └── reporter.go +├── server/ +│ ├── server.go +│ └── prometheus.go +├── api/ +│ └── api.go +├── redis/ +│ ├── client.go +│ └── operations.go +└── types/ + ├── types.go + └── config.go +``` + +## Prometheus SD Format Reference + +Standard format for HTTP SD: + +```json +[ + { + "targets": ["host:port"], + "labels": { + "__meta_docker_container_id": "abc123", + "__meta_docker_container_name": "myapp", + "label_key": "label_value" + } + } +] +``` + +Support these prometheus.io labels: + +- `prometheus.io/scrape`: "true" to include in SD +- `prometheus.io/port`: Port to scrape (default: first exposed port) +- `prometheus.io/path`: Metrics path (default: `/metrics`) +- `prometheus.io/scheme`: http or https (default: `http`) + +### To-dos + +- [x] Create main.go, go.mod, and add to go.work +- [x] Implement types/types.go and types/config.go with core data structures +- [x] Create redis/client.go and redis/operations.go for Redis integration +- [x] Implement agent/docker.go for Docker container monitoring +- [x] Implement agent/agent.go and agent/reporter.go for heartbeat and reporting +- [x] Implement server/server.go with HTTP router setup +- [x] Implement server/prometheus.go for Prometheus HTTP SD endpoint +- [x] Implement api/api.go with query endpoints +- [x] Implement cmd/root.go, cmd/agent.go, cmd/server.go, and cmd/query.go \ No newline at end of file diff --git a/docker-hermes/docs/02-flexible-target-resolution.plan.md b/docker-hermes/docs/02-flexible-target-resolution.plan.md new file mode 100644 index 0000000..16edad0 --- /dev/null +++ b/docker-hermes/docs/02-flexible-target-resolution.plan.md @@ -0,0 +1,838 @@ + +# Flexible Target Resolution for docker-hermes + +## Overview + +Implement flexible target resolution strategies to support multiple deployment scenarios including overlay networks, bridge networks, and external monitoring systems. This enhancement allows operators to configure how container targets are resolved for Prometheus scraping, with support for per-container overrides and multiple targets per container. + +## Problem Statement + +The current implementation uses a simple `{hostname}:{port}` format for targets, which doesn't support: +1. **Overlay networks** where container hostnames are resolvable across nodes +2. **Bridge networks** where container IPs are needed for direct access +3. **External monitoring** where published host ports are required +4. **Multi-port services** where a single container exposes multiple scrape targets +5. **Per-container customization** for hybrid deployments + +## Architecture Summary + +### Resolution Strategies + +Four primary strategies with configurable defaults and per-container overrides: + +1. **`hostname`** - Use container name/hostname (overlay networks with DNS) +2. **`container_ip`** - Use container's internal IP address (bridge networks) +3. **`host_port`** - Use agent hostname + published port (external monitoring) +4. **`custom`** - Use custom host from configuration (NAT, custom DNS) + +### Multi-Target Support + +Containers can expose multiple Prometheus targets via indexed labels: +```yaml +prometheus.io/scrape=true +prometheus.io/port=8080 +prometheus.io/path=/metrics + +prometheus.io/scrape.admin=true +prometheus.io/port.admin=9090 +prometheus.io/path.admin=/admin/metrics +prometheus.io/scheme.admin=https +``` + +### Label-Based Overrides + +Per-container resolution control via `hermes.io/*` labels: +```yaml +hermes.io/target-strategy=host_port +hermes.io/target-host=custom-hostname.example.com +hermes.io/target-port=9999 +hermes.io/target-port.admin=10000 # Multi-target override +``` + +## Implementation Plan + +### Phase 1: Core Resolution Infrastructure + +#### 1.1 Update Type Definitions + +**File: `types/resolution.go`** (new) + +Define resolution strategy types and constants: + +```go +type TargetResolutionStrategy string + +const ( + StrategyHostname TargetResolutionStrategy = "hostname" + StrategyContainerIP TargetResolutionStrategy = "container_ip" + StrategyHostPort TargetResolutionStrategy = "host_port" + StrategyCustom TargetResolutionStrategy = "custom" +) + +type TargetResolution struct { + Strategy TargetResolutionStrategy + Host string + Port int + TargetSuffix string // For multi-target (e.g., "admin", "metrics") +} + +type ResolvedTarget struct { + Address string // Final "host:port" format + Scheme string + Path string + Labels map[string]string +} +``` + +#### 1.2 Extend Configuration + +**File: `types/types.go`** + +Update `Config` struct and add new LabelsConfig type: +```go +// LabelsConfig holds label prefix configuration shared by agent and server +type LabelsConfig struct { + LabelPrefix string `yaml:"label_prefix" envconfig:"LABEL_PREFIX" default:""` + PrometheusPrefix string `yaml:"prometheus_prefix" envconfig:"PROMETHEUS_PREFIX" default:"prometheus.io"` + HermesPrefix string `yaml:"hermes_prefix" envconfig:"HERMES_PREFIX" default:"hermes.io"` +} + +type Config struct { + config.CommonConfig `yaml:",inline"` + + // Shared configuration + Labels LabelsConfig `yaml:"labels"` + + // Agent-specific fields + DockerSocket string + RedisURL string + HeartbeatInterval time.Duration + Hostname string + + // Target resolution configuration + TargetResolutionStrategy TargetResolutionStrategy `yaml:"target_resolution_strategy" envconfig:"TARGET_RESOLUTION_STRATEGY" default:"hostname"` + TargetResolutionHost string `yaml:"target_resolution_host" envconfig:"TARGET_RESOLUTION_HOST" default:""` + + // Server-specific fields + HTTPListenAddr string + PrometheusSDPath string +} +``` + +**File: `types/resolution.go`** (new) + +Add LabelsConfig to ContainerInfo: +```go +type ContainerInfo struct { + ID string `json:"id"` + Name string `json:"name"` + Host string `json:"host"` + Labels map[string]string `json:"labels"` + Ports []PortInfo `json:"ports"` + State string `json:"state"` + LastSeen time.Time `json:"last_seen"` + CreatedAt time.Time `json:"created_at"` + Image string `json:"image"` + Command string `json:"command"` + Status string `json:"status"` + + // Network information for resolution + NetworkSettings NetworkSettings `json:"network_settings"` + + // NEW: Labels configuration reference + LabelsConfig *LabelsConfig `json:"-"` +} + +#### 1.3 Extend ContainerInfo + +**File: `types/types.go`** + +Add network information to `ContainerInfo`: +```go +type ContainerInfo struct { + ID string `json:"id"` + Name string `json:"name"` + Host string `json:"host"` + Labels map[string]string `json:"labels"` + Ports []PortInfo `json:"ports"` + State string `json:"state"` + LastSeen time.Time `json:"last_seen"` + CreatedAt time.Time `json:"created_at"` + Image string `json:"image"` + Command string `json:"command"` + Status string `json:"status"` + + // NEW: Network information for resolution + NetworkSettings NetworkSettings `json:"network_settings"` +} + +type NetworkSettings struct { + IPAddress string `json:"ip_address"` // Primary IP + Networks map[string]Network `json:"networks"` // All networks +} + +type Network struct { + IPAddress string `json:"ip_address"` + Gateway string `json:"gateway"` + NetworkID string `json:"network_id"` + NetworkName string `json:"network_name"` +} +``` + +### Phase 2: Target Resolution Logic + +#### 2.1 Implement Resolution Functions + +**File: `types/resolution.go`** + +Core resolution logic: + +```go +// ResolveTargets resolves all Prometheus targets for a container +func (c *ContainerInfo) ResolveTargets(defaultStrategy TargetResolutionStrategy, agentHost, customHost string) []ResolvedTarget { + targets := []ResolvedTarget{} + + // Find all prometheus.io/scrape labels (including indexed) + scrapeTargets := c.FindScrapeTargets() + + for _, targetSuffix := range scrapeTargets { + if target := c.resolveTarget(targetSuffix, defaultStrategy, agentHost, customHost); target != nil { + targets = append(targets, *target) + } + } + + return targets +} + +// FindScrapeTargets identifies all scrape target suffixes in labels +func (c *ContainerInfo) FindScrapeTargets() []string { + suffixes := []string{""} // Default (no suffix) + + scrapeKey := c.LabelsConfig.PrometheusPrefix + "/scrape" + + for key, value := range c.Labels { + if strings.HasPrefix(key, scrapeKey+".") && value == "true" { + suffix := strings.TrimPrefix(key, scrapeKey+".") + suffixes = append(suffixes, suffix) + } + } + + return suffixes +} + +// resolveTarget resolves a single target +func (c *ContainerInfo) resolveTarget(suffix string, defaultStrategy TargetResolutionStrategy, agentHost, customHost string) *ResolvedTarget { + // Check if scraping is enabled for this target + if !c.isScrapeEnabled(suffix) { + return nil + } + + // Determine resolution strategy + strategy := c.getTargetStrategy(suffix, defaultStrategy) + + // Get port + port := c.getTargetPort(suffix) + if port == 0 { + return nil + } + + // Resolve host based on strategy + host := c.resolveHost(suffix, strategy, agentHost, customHost) + if host == "" { + return nil + } + + // Adjust port for host_port strategy + if strategy == StrategyHostPort { + if publishedPort := c.getPublishedPort(port); publishedPort > 0 { + port = publishedPort + } else { + // No published port available + return nil + } + } + + return &ResolvedTarget{ + Address: fmt.Sprintf("%s:%d", host, port), + Scheme: c.getTargetScheme(suffix), + Path: c.getTargetPath(suffix), + Labels: c.getTargetLabels(suffix), + } +} + +// isScrapeEnabled checks if scraping is enabled +func (c *ContainerInfo) isScrapeEnabled(suffix string) bool { + key := c.LabelsConfig.PrometheusPrefix + "/scrape" + if suffix != "" { + key = fmt.Sprintf("%s/scrape.%s", c.LabelsConfig.PrometheusPrefix, suffix) + } + + value, exists := c.Labels[key] + return exists && value == "true" +} + +// getTargetStrategy determines the resolution strategy +func (c *ContainerInfo) getTargetStrategy(suffix string, defaultStrategy TargetResolutionStrategy) TargetResolutionStrategy { + // Check for per-target override + key := c.LabelsConfig.HermesPrefix + "/target-strategy" + if suffix != "" { + key = fmt.Sprintf("%s/target-strategy.%s", c.LabelsConfig.HermesPrefix, suffix) + if strategy, exists := c.Labels[key]; exists { + return TargetResolutionStrategy(strategy) + } + } + + // Check for global hermes override + if strategy, exists := c.Labels[c.LabelsConfig.HermesPrefix+"/target-strategy"]; exists { + return TargetResolutionStrategy(strategy) + } + + return defaultStrategy +} + +// resolveHost resolves the host based on strategy +func (c *ContainerInfo) resolveHost(suffix string, strategy TargetResolutionStrategy, agentHost, customHost string) string { + // Check for explicit host override + key := c.LabelsConfig.HermesPrefix + "/target-host" + if suffix != "" { + suffixKey := fmt.Sprintf("%s/target-host.%s", c.LabelsConfig.HermesPrefix, suffix) + if host, exists := c.Labels[suffixKey]; exists { + return host + } + } + if host, exists := c.Labels[key]; exists { + return host + } + + // Resolve based on strategy + switch strategy { + case StrategyHostname: + return c.Name + case StrategyContainerIP: + return c.GetPrimaryIP() + case StrategyHostPort: + if customHost != "" { + return customHost + } + return agentHost + case StrategyCustom: + return customHost + default: + return c.Name + } +} + +// getTargetPort gets the port for a target +func (c *ContainerInfo) getTargetPort(suffix string) int { + // Check for hermes override first + hermesKey := c.LabelsConfig.HermesPrefix + "/target-port" + if suffix != "" { + suffixKey := fmt.Sprintf("%s/target-port.%s", c.LabelsConfig.HermesPrefix, suffix) + if portStr, exists := c.Labels[suffixKey]; exists { + if port, err := strconv.Atoi(portStr); err == nil { + return port + } + } + } + if portStr, exists := c.Labels[hermesKey]; exists { + if port, err := strconv.Atoi(portStr); err == nil { + return port + } + } + + // Check prometheus.io/port + promKey := c.LabelsConfig.PrometheusPrefix + "/port" + if suffix != "" { + promKey = fmt.Sprintf("%s/port.%s", c.LabelsConfig.PrometheusPrefix, suffix) + } + if portStr, exists := c.Labels[promKey]; exists { + if port, err := strconv.Atoi(portStr); err == nil { + return port + } + } + + // Default to first exposed port + if len(c.Ports) > 0 { + return c.Ports[0].PrivatePort + } + + return 0 +} + +// getPublishedPort finds the published port for a private port +func (c *ContainerInfo) getPublishedPort(privatePort int) int { + for _, port := range c.Ports { + if port.PrivatePort == privatePort && port.PublicPort > 0 { + return port.PublicPort + } + } + return 0 +} + +// getTargetScheme gets the scheme (http/https) +func (c *ContainerInfo) getTargetScheme(suffix string) string { + key := c.LabelsConfig.PrometheusPrefix + "/scheme" + if suffix != "" { + key = fmt.Sprintf("%s/scheme.%s", c.LabelsConfig.PrometheusPrefix, suffix) + } + + if scheme, exists := c.Labels[key]; exists { + return scheme + } + return "http" +} + +// getTargetPath gets the metrics path +func (c *ContainerInfo) getTargetPath(suffix string) string { + key := c.LabelsConfig.PrometheusPrefix + "/path" + if suffix != "" { + key = fmt.Sprintf("%s/path.%s", c.LabelsConfig.PrometheusPrefix, suffix) + } + + if path, exists := c.Labels[key]; exists { + return path + } + return "/metrics" +} + +// getTargetLabels builds labels for the target +func (c *ContainerInfo) getTargetLabels(suffix string) map[string]string { + labels := make(map[string]string) + + // Add meta labels + labels["__meta_docker_container_id"] = c.ID + labels["__meta_docker_container_name"] = c.Name + labels["__meta_docker_container_image"] = c.Image + labels["__meta_docker_host"] = c.Host + + if suffix != "" { + labels["__meta_target_suffix"] = suffix + } + + // Copy labels, excluding prometheus and hermes prefixes + for key, value := range c.Labels { + if !strings.HasPrefix(key, c.LabelsConfig.PrometheusPrefix+"/") && + !strings.HasPrefix(key, c.LabelsConfig.HermesPrefix+"/") { + labels[key] = value + } + } + + return labels +} + +// GetPrimaryIP returns the primary IP address +func (c *ContainerInfo) GetPrimaryIP() string { + if c.NetworkSettings.IPAddress != "" { + return c.NetworkSettings.IPAddress + } + + // Fall back to first network IP + for _, network := range c.NetworkSettings.Networks { + if network.IPAddress != "" { + return network.IPAddress + } + } + + return "" +} +``` + +### Phase 3: Agent Updates + +#### 3.1 Enhanced Docker Information Extraction + +**File: `agent/docker.go`** + +Update `extractContainerInfoFromInspect()` to capture network details: + +```go +func (d *DockerClient) extractContainerInfoFromInspect(inspect types.ContainerJSON, hostname string) (*hermesTypes.ContainerInfo, error) { + // ... existing port extraction ... + + // NEW: Extract network settings + networkSettings := hermesTypes.NetworkSettings{ + IPAddress: inspect.NetworkSettings.IPAddress, + Networks: make(map[string]hermesTypes.Network), + } + + for netName, netConfig := range inspect.NetworkSettings.Networks { + networkSettings.Networks[netName] = hermesTypes.Network{ + IPAddress: netConfig.IPAddress, + Gateway: netConfig.Gateway, + NetworkID: netConfig.NetworkID, + NetworkName: netName, + } + } + + containerInfo := &hermesTypes.ContainerInfo{ + // ... existing fields ... + NetworkSettings: networkSettings, + } + + return containerInfo, nil +} +``` + +#### 3.2 Update Reporter + +**File: `agent/reporter.go`** + +Pass resolution configuration to reporter: + +```go +type Reporter struct { + redisClient *redis.Client + + // Resolution configuration + resolutionStrategy hermesTypes.TargetResolutionStrategy + agentHost string + customHost string + + // Labels configuration + labelsConfig *hermesTypes.LabelsConfig +} + +func NewReporter(redisClient *redis.Client, config *hermesTypes.Config) *Reporter { + return &Reporter{ + redisClient: redisClient, + resolutionStrategy: config.TargetResolutionStrategy, + agentHost: config.Hostname, + customHost: config.TargetResolutionHost, + labelsConfig: &config.Labels, + } +} + +func (r *Reporter) ReportContainer(ctx context.Context, container *hermesTypes.ContainerInfo) error { + // Attach labels config to container + container.LabelsConfig = r.labelsConfig + + // Filter labels by prefix if specified + if r.labelsConfig.LabelPrefix != "" { + container.Labels = container.FilterLabelsByPrefix(r.labelsConfig.LabelPrefix) + } + + // Pre-resolve targets for validation + targets := container.ResolveTargets(r.resolutionStrategy, r.agentHost, r.customHost) + if len(targets) == 0 { + logrus.Debugf("Container %s has no valid targets, skipping", container.ID) + return nil + } + + // Log resolved targets + for i, target := range targets { + logrus.Debugf("Container %s target %d: %s%s%s", + container.ID, i, target.Scheme, target.Address, target.Path) + } + + // ... existing store logic ... +} +``` + +### Phase 4: Server Updates + +#### 4.1 Update Prometheus SD Generation + +**File: `server/prometheus.go`** or **`types/prometheus.go`** + +Update `ToPrometheusTarget()` to use resolution: + +```go +// ToPrometheusTargets converts ContainerInfo to Prometheus targets +func (c *ContainerInfo) ToPrometheusTargets(strategy TargetResolutionStrategy, agentHost, customHost string) []PrometheusTarget { + resolvedTargets := c.ResolveTargets(strategy, agentHost, customHost) + + promTargets := make([]PrometheusTarget, 0, len(resolvedTargets)) + for _, resolved := range resolvedTargets { + promTargets = append(promTargets, PrometheusTarget{ + Targets: []string{resolved.Address}, + Labels: resolved.Labels, + }) + } + + return promTargets +} +``` + +**File: `redis/operations.go`** + +Update `GetPrometheusTargets()` to handle multi-target: + +```go +func (c *Client) GetPrometheusTargets(ctx context.Context, strategy types.TargetResolutionStrategy, agentHost, customHost string, labelsConfig *types.LabelsConfig) (types.PrometheusSDResponse, error) { + containers, err := c.ListContainers(ctx) + if err != nil { + return nil, err + } + + var targets types.PrometheusSDResponse + for _, container := range containers { + // Attach labels config to containers retrieved from Redis (they won't have it) + container.LabelsConfig = labelsConfig + + containerTargets := container.ToPrometheusTargets(strategy, agentHost, customHost) + targets = append(targets, containerTargets...) + } + + return targets, nil +} +``` + +**File: `server/server.go`** + +Update Prometheus SD endpoint to pass resolution config: + +```go +func (s *Server) registerPrometheusSD(apiContext *types.ApiContext) error { + s.router.GET(s.config.PrometheusSDPath, func(c *gin.Context) { + // Pass labels config to Redis operations + targets, err := s.redisClient.GetPrometheusTargets( + c.Request.Context(), + s.config.TargetResolutionStrategy, + s.config.Hostname, + s.config.TargetResolutionHost, + &s.config.Labels, + ) + if err != nil { + logrus.Errorf("Failed to get Prometheus targets: %v", err) + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get targets"}) + return + } + + c.JSON(http.StatusOK, targets) + }) + + return nil +} +``` + +### Phase 5: Documentation and Examples + +#### 5.1 Update Configuration Example + +**File: `config.yml.example`** + +```yaml +# Label configuration (shared by agent and server) +labels: + # Filter label prefix for agent (empty = all labels) + label_prefix: "" + + # Label prefixes for Prometheus and Hermes labels + prometheus_prefix: "prometheus.io" # Default: prometheus.io + hermes_prefix: "hermes.io" # Default: hermes.io + +# Target resolution strategy +# Options: hostname, container_ip, host_port, custom +target_resolution_strategy: "hostname" + +# Custom host for resolution (used with 'custom' or 'host_port' strategies) +# For 'host_port': uses this instead of hostname for external access +# For 'custom': always uses this as the target host +target_resolution_host: "" + +# Examples by use case: +# +# Overlay network (Docker Swarm, Kubernetes): +# target_resolution_strategy: "hostname" +# +# Bridge network with routable IPs: +# target_resolution_strategy: "container_ip" +# +# External monitoring via published ports: +# target_resolution_strategy: "host_port" +# target_resolution_host: "192.168.1.10" # optional override +# +# Custom DNS/NAT scenario: +# target_resolution_strategy: "custom" +# target_resolution_host: "docker-host.example.com" +``` + +#### 5.2 Create Resolution Guide + +**File: `docs/target-resolution.md`** + +Comprehensive guide covering: +- All resolution strategies with examples +- Multi-target configuration +- Per-container label overrides +- Use case scenarios +- Troubleshooting + +### Phase 6: Testing + +#### 6.1 Unit Tests + +**File: `types/resolution_test.go`** (new) + +Test cases: +- Each resolution strategy +- Multi-target discovery +- Label override precedence +- Edge cases (no ports, no IPs, etc.) + +#### 6.2 Integration Tests + +**File: `integration/docker-compose.yml`** + +Add test scenarios: +- Container with multiple prometheus.io/scrape targets +- Containers with different resolution strategies +- Containers with label overrides + +### Phase 7: Migration and Validation + +#### 7.1 Backward Compatibility + +Ensure existing deployments continue to work: +- Default strategy is `hostname` (current behavior) +- Existing label handling unchanged +- No breaking changes to Redis data format + +#### 7.2 Validation + +- Test all strategies in integration environment +- Verify multi-target Prometheus scraping +- Validate label override behavior +- Performance testing with many containers + +## Implementation Checklist + +### Phase 1: Core Infrastructure +- [ ] Create `types/resolution.go` with strategy types +- [ ] Create `LabelsConfig` struct in `types/types.go` for shared label configuration +- [ ] Update `types/types.go` with nested `Labels` field in `Config` +- [ ] Extend `ContainerInfo` with `NetworkSettings` and `LabelsConfig` reference +- [ ] Update `PortInfo` if needed for published port tracking + +### Phase 2: Resolution Logic +- [ ] Implement `ResolveTargets()` function +- [ ] Implement `FindScrapeTargets()` for multi-target discovery +- [ ] Implement `resolveTarget()` with strategy logic +- [ ] Implement helper functions (`isScrapeEnabled`, `getTargetStrategy`, etc.) +- [ ] Implement `GetPrimaryIP()` for container IP resolution + +### Phase 3: Agent Updates +- [ ] Update `agent/docker.go` to extract network settings and attach `LabelsConfig` +- [ ] Update `agent/reporter.go` to store `LabelsConfig` and attach to containers +- [ ] Update `agent/agent.go` to pass config to reporter +- [ ] Add logging for resolved targets +- [ ] Ensure `LabelsConfig` is attached to containers before reporting + +### Phase 4: Server Updates +- [ ] Update `ToPrometheusTargets()` to support multi-target (no prefix parameters) +- [ ] Update `redis/operations.go` GetPrometheusTargets to accept `LabelsConfig` +- [ ] Update `server/server.go` Prometheus SD endpoint to pass `LabelsConfig` +- [ ] Attach `LabelsConfig` to containers retrieved from Redis +- [ ] Remove prefix parameters from `ContainerInfo` method signatures + +### Phase 5: Documentation +- [ ] Update `config.yml.example` with resolution options and label prefixes +- [ ] Create `docs/target-resolution.md` guide +- [ ] Update README with resolution feature overview +- [ ] Document configurable label prefixes and label override behavior + +### Phase 6: Testing +- [ ] Write unit tests for resolution logic +- [ ] Write unit tests for multi-target discovery +- [ ] Add integration test scenarios +- [ ] Test each resolution strategy +- [ ] Test label override precedence + +### Phase 7: Validation +- [ ] Verify backward compatibility +- [ ] Test with existing deployments +- [ ] Performance testing +- [ ] Update integration test suite + +## Label Reference + +### Note on Label Prefixes + +Both the Prometheus and Hermes label prefixes are configurable via the `prometheus_label_prefix` and `hermes_label_prefix` configuration options. The examples below use the default prefixes (`prometheus.io` and `hermes.io`), but these can be customized in your configuration. + +### Standard Prometheus Labels + +```yaml +# Single target (default) +# Note: prefix is configurable via prometheus_label_prefix config +prometheus.io/scrape: "true" +prometheus.io/port: "8080" +prometheus.io/path: "/metrics" +prometheus.io/scheme: "http" + +# Multiple targets (indexed) +prometheus.io/scrape.admin: "true" +prometheus.io/port.admin: "9090" +prometheus.io/path.admin: "/admin/metrics" +prometheus.io/scheme.admin: "https" +``` + +### Hermes Resolution Labels + +```yaml +# Global strategy override +# Note: prefix is configurable via hermes_label_prefix config +hermes.io/target-strategy: "host_port" # hostname, container_ip, host_port, custom + +# Global host override +hermes.io/target-host: "custom-host.example.com" + +# Global port override (takes precedence over prometheus.io/port) +hermes.io/target-port: "8080" + +# Per-target overrides +hermes.io/target-strategy.admin: "container_ip" +hermes.io/target-host.admin: "admin.example.com" +hermes.io/target-port.admin: "9090" +``` + +### Custom Label Prefix Examples + +If you set custom prefixes in configuration: + +```yaml +# config.yml +prometheus_label_prefix: "custom.monitoring" +hermes_label_prefix: "hermes.service" +``` + +Then your labels would be: + +```yaml +custom.monitoring/scrape: "true" +custom.monitoring/port: "8080" +hermes.service/target-strategy: "hostname" +hermes.service/target-host: "custom-host" +``` + +## Resolution Strategy Decision Tree + +``` +For each container target: + Note: Label prefixes are configurable (default: prometheus.io, hermes.io) + + 1. Is {prometheus_prefix}/scrape[.suffix] == "true"? → No: Skip + 2. Check {hermes_prefix}/target-strategy[.suffix] → Use if present + 3. Else use agent's default strategy + 4. Check {hermes_prefix}/target-host[.suffix] → Use if present + 5. Else resolve host based on strategy: + - hostname: Use container name + - container_ip: Use container's primary IP + - host_port: Use agent hostname (or custom) + - custom: Use configured custom host + 6. Check {hermes_prefix}/target-port[.suffix] → Use if present + 7. Else use {prometheus_prefix}/port[.suffix] → Use if present + 8. Else use first exposed port → Use if present + 9. Else skip target (no valid port) + 10. If strategy is host_port: lookup published port → Skip if not found + 11. Return resolved target +``` + +## Future Enhancements (Not in Scope) + +- DNS validation/lookup at registration time +- Automatic fallback chains with testing +- Network-specific resolution strategies +- Dynamic strategy selection based on network topology +- IPv6 support +- Multiple host strategies (primary/fallback hosts) diff --git a/docker-hermes/docs/label-format.md b/docker-hermes/docs/label-format.md new file mode 100644 index 0000000..a68fccf --- /dev/null +++ b/docker-hermes/docs/label-format.md @@ -0,0 +1,145 @@ +# Docker Hermes Label Format + +Docker Hermes uses a Traefik-style hierarchical label format for configuring Prometheus scrape targets and resolution strategies. + +## Label Structure + +All labels use a configurable prefix (default: `hermes`). The structure follows this pattern: + +``` +{prefix}.{section}.{target}.{property} = {value} +``` + +## Configuration + +The label prefix is configurable via the `labels.prefix` configuration option: + +```yaml +labels: + prefix: "hermes" # Default, can be changed to any value +``` + +## Target Definitions + +### Named Targets (Structured) + +For multiple targets per container, use the structured format: + +```yaml +# Target named "admin" +hermes.targets.admin.prometheus.scrape: "true" +hermes.targets.admin.prometheus.port: "9090" +hermes.targets.admin.prometheus.path: "/admin/metrics" +hermes.targets.admin.prometheus.scheme: "https" + +# Hermes-specific overrides for this target +hermes.targets.admin.strategy: "container_ip" +hermes.targets.admin.host: "admin.example.com" +hermes.targets.admin.port: "10000" # Override port for resolution +``` + +### Shorthand (Default Target) + +For a single target, use the shorthand format: + +```yaml +hermes.prometheus.scrape: "true" +hermes.prometheus.port: "8080" +hermes.prometheus.path: "/metrics" +hermes.prometheus.scheme: "http" +``` + +The target name is automatically derived from container details: +- Format: `{host}-{container-name}` (e.g., `host1-nginx-app`) +- Falls back to container name, image name, or container ID if needed + +## Label Properties + +### Prometheus Configuration + +All properties under `prometheus.*`: + +- `prometheus.scrape`: `"true"` to enable scraping (required) +- `prometheus.port`: Port number to scrape (required if no published port) +- `prometheus.path`: Metrics path (default: `/metrics`) +- `prometheus.scheme`: `"http"` or `"https"` (default: `http`) + +### Hermes Configuration + +Properties at the target level (for named targets only): + +- `strategy`: Resolution strategy (`hostname`, `container_ip`, `host_port`, `custom`) +- `host`: Custom hostname/IP override +- `port`: Port override for resolution (different from prometheus.port) + +## Examples + +### Single Target (Shorthand) + +```yaml +labels: + hermes.prometheus.scrape: "true" + hermes.prometheus.port: "8080" + app: "nginx" + version: "1.0" +``` + +### Multiple Targets + +```yaml +labels: + # Default target + hermes.prometheus.scrape: "true" + hermes.prometheus.port: "8080" + + # Admin metrics endpoint + hermes.targets.admin.prometheus.scrape: "true" + hermes.targets.admin.prometheus.port: "9090" + hermes.targets.admin.prometheus.path: "/admin/metrics" + hermes.targets.admin.strategy: "container_ip" + + # Health check endpoint + hermes.targets.health.prometheus.scrape: "true" + hermes.targets.health.prometheus.port: "8081" + hermes.targets.health.prometheus.path: "/health" +``` + +### Custom Resolution Strategy + +```yaml +labels: + hermes.targets.app.prometheus.scrape: "true" + hermes.targets.app.prometheus.port: "8080" + hermes.targets.app.strategy: "host_port" + hermes.targets.app.host: "external.example.com" +``` + +## Target Name Derivation + +When using shorthand (`hermes.prometheus.*`), the target name is derived from: + +1. `{host}-{container-name}` (preferred, e.g., `host1-nginx-app`) +2. Container name (if host unavailable) +3. Image name without tag/registry (if name unavailable) +4. Short container ID (12 chars, as last resort) + +This ensures each target has a unique, identifiable name even when using shorthand. + +## Resolution Strategies + +- **`hostname`**: Use container hostname/name (default, works with overlay networks) +- **`container_ip`**: Use container's internal IP address (bridge networks) +- **`host_port`**: Use agent hostname + published port (external monitoring) +- **`custom`**: Use custom host from configuration + +## Filtering + +The `label_prefix` configuration option filters which labels are processed: + +```yaml +labels: + label_prefix: "hermes" # Only process labels starting with "hermes" +``` + +If empty, all labels are processed (default behavior). + diff --git a/functional/go.mod b/docker-hermes/go.mod similarity index 57% rename from functional/go.mod rename to docker-hermes/go.mod index b751bf7..6a4f26c 100644 --- a/functional/go.mod +++ b/docker-hermes/go.mod @@ -1,33 +1,31 @@ -module github.com/pirogoeth/apps/functional +module github.com/pirogoeth/apps/docker-hermes go 1.24.1 -replace github.com/pirogoeth/apps => ../ +replace github.com/pirogoeth/apps/pkg => ../pkg require ( - github.com/docker/docker v28.3.2+incompatible - github.com/docker/go-connections v0.5.0 + github.com/docker/docker v26.1.4+incompatible github.com/gin-gonic/gin v1.10.1 - github.com/google/uuid v1.6.0 - github.com/mattn/go-sqlite3 v1.14.28 - github.com/pirogoeth/apps v0.0.0-00010101000000-000000000000 - github.com/pressly/goose/v3 v3.24.3 + github.com/pirogoeth/apps/pkg v0.0.0-00010101000000-000000000000 + github.com/prometheus/client_golang v1.20.5 + github.com/redis/go-redis/v9 v9.7.0 github.com/sirupsen/logrus v1.9.3 - github.com/spf13/cobra v1.8.1 + github.com/spf13/cobra v1.9.1 ) require ( - github.com/Microsoft/go-winio v0.4.14 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bytedance/sonic v1.13.2 // indirect github.com/bytedance/sonic/loader v0.2.4 // indirect - github.com/cenkalti/backoff/v5 v5.0.2 // indirect + github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cloudwego/base64x v0.1.5 // indirect - github.com/containerd/errdefs v1.0.0 // indirect - github.com/containerd/errdefs/pkg v0.3.0 // indirect github.com/containerd/log v0.1.0 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/distribution/reference v0.6.0 // indirect + github.com/docker/go-connections v0.6.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/gabriel-vasile/mimetype v1.4.9 // indirect @@ -42,64 +40,61 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/google/go-github/v58 v58.0.0 // indirect github.com/google/go-querystring v1.1.0 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/gorilla/websocket v1.5.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect github.com/hashicorp/cronexpr v1.1.2 // indirect - github.com/hashicorp/errwrap v1.0.0 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect - github.com/hashicorp/nomad/api v0.0.0-20231227080007-76ba3e10e73d // indirect + github.com/hashicorp/nomad/api v0.0.0-20250124153748-7add04eb0f0b // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/kelseyhightower/envconfig v1.4.0 // indirect + github.com/klauspost/compress v1.18.0 // indirect github.com/klauspost/cpuid/v2 v2.2.10 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mfridman/interpolate v0.0.2 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect - github.com/moby/sys/atomicwriter v0.1.0 // indirect github.com/moby/term v0.5.2 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/morikuni/aec v1.0.0 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.0.2 // indirect + github.com/opencontainers/image-spec v1.1.1 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect - github.com/pkg/errors v0.8.1 // indirect - github.com/prometheus/client_golang v1.19.1 // indirect - github.com/prometheus/client_model v0.5.0 // indirect - github.com/prometheus/common v0.48.0 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/procfs v0.16.1 // indirect - github.com/sethvargo/go-retry v0.3.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/pflag v1.0.6 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.61.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 // indirect - go.opentelemetry.io/otel v1.37.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect + go.opentelemetry.io/otel v1.38.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.36.0 // indirect - go.opentelemetry.io/otel/metric v1.37.0 // indirect - go.opentelemetry.io/otel/sdk v1.37.0 // indirect - go.opentelemetry.io/otel/trace v1.37.0 // indirect - go.opentelemetry.io/proto/otlp v1.6.0 // indirect - go.uber.org/multierr v1.11.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 // indirect + go.opentelemetry.io/otel/metric v1.38.0 // indirect + go.opentelemetry.io/otel/sdk v1.38.0 // indirect + go.opentelemetry.io/otel/trace v1.38.0 // indirect + go.opentelemetry.io/proto/otlp v1.7.1 // indirect golang.org/x/arch v0.17.0 // indirect - golang.org/x/crypto v0.38.0 // indirect - golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect - golang.org/x/net v0.40.0 // indirect - golang.org/x/sync v0.14.0 // indirect - golang.org/x/sys v0.33.0 // indirect - golang.org/x/text v0.25.0 // indirect - golang.org/x/time v0.12.0 // indirect - google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1 // indirect - google.golang.org/grpc v1.72.2 // indirect - google.golang.org/protobuf v1.36.6 // indirect + golang.org/x/crypto v0.41.0 // indirect + golang.org/x/net v0.43.0 // indirect + golang.org/x/sys v0.35.0 // indirect + golang.org/x/text v0.28.0 // indirect + golang.org/x/time v0.14.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect + google.golang.org/grpc v1.75.0 // indirect + google.golang.org/protobuf v1.36.8 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect gotest.tools/v3 v3.5.2 // indirect diff --git a/docker-hermes/go.sum b/docker-hermes/go.sum new file mode 100644 index 0000000..4f4b147 --- /dev/null +++ b/docker-hermes/go.sum @@ -0,0 +1,268 @@ +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +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/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ= +github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4= +github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY= +github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= +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/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4= +github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= +github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= +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/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +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 v26.1.4+incompatible h1:vuTpXDuoga+Z38m1OZHzl7NKisKWaWlhjQk7IDPSLsU= +github.com/docker/docker v26.1.4+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94= +github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE= +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/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY= +github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= +github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= +github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ= +github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= +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-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k= +github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= +github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= +github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +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.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/go-github/v58 v58.0.0 h1:Una7GGERlF/37XfkPwpzYJe0Vp4dt2k1kCjlxwjIvzw= +github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +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/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs= +github.com/hashicorp/cronexpr v1.1.2 h1:wG/ZYIKT+RT3QkOdgYc+xsKWVRgnxJ1OJtjjy84fJ9A= +github.com/hashicorp/cronexpr v1.1.2/go.mod h1:P4wA0KBl9C5q2hABiMO7cp6jcIg96CDh1Efb3g1PWA4= +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-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +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/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= +github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/nomad/api v0.0.0-20250124153748-7add04eb0f0b h1:oTD8vP47MZ4ZnIcxsucsYZeJEOpLaQEBq6hNAiITYN0= +github.com/hashicorp/nomad/api v0.0.0-20250124153748-7add04eb0f0b/go.mod h1:svtxn6QnrQ69P23VvIWMR34tg3vmwLz4UdUzm1dSCgE= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +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/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= +github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= +github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +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/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +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/go-testing-interface v1.14.2-0.20210821155943-2d9075ca8770 h1:drhDO54gdT/a15GBcMRmunZiNcLgPiFIJa23KzmcvcU= +github.com/mitchellh/go-testing-interface v1.14.2-0.20210821155943-2d9075ca8770/go.mod h1:SO/iHr6q2EzbqRApt+8/E9wqebTwQn5y+UlB04bxzo0= +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/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= +github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= +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/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +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.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= +github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= +github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +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= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +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.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= +github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= +github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E= +github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw= +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/shoenig/test v1.7.1 h1:UJcjSAI3aUKx52kfcfhblgyhZceouhvvs3OYdWgn+PY= +github.com/shoenig/test v1.7.1/go.mod h1:UxJ6u/x2v/TNs/LoLxBNJRV9DiwBBKYxXSyczsBHFoI= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= +github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.61.0 h1:VkrF0D14uQrCmPqBkYlwWnhgcwzXvIRAjX8eXO7vy6M= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg= +go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= +go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 h1:JgtbA0xkWHnTmYk7YusopJFX6uleBmAuZ8n05NEh8nQ= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0/go.mod h1:179AK5aar5R3eS9FucPy6rggvU0g52cvKId8pv4+v0c= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 h1:aTL7F04bJHUlztTsNGJ2l+6he8c+y/b//eR0jjjemT4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0/go.mod h1:kldtb7jDTeol0l3ewcmd8SDvx3EmIE7lyvqbasU3QC4= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.36.0 h1:G8Xec/SgZQricwWBJF/mHZc7A02YHedfFDENwJEdRA0= +go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= +go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= +go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= +go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= +go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= +go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= +go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= +go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= +go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4= +go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +golang.org/x/arch v0.17.0 h1:4O3dfLzd+lQewptAHqjewQZQDyEdejz3VwgeYwkZneU= +golang.org/x/arch v0.17.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= +golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= +golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= +golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= +golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= +golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +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-20250825161204-c5933d9347a5 h1:BIRfGDEjiHRrk0QKZe3Xv2ieMhtgRGeLcZQ0mIVn4EY= +google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5/go.mod h1:M4/wBTSeyLxupu3W3tJtOgB14jILAS/XWPSSa3TAlJc= +google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4= +google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= +google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= +google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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/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= +gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= +gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= diff --git a/docker-hermes/integration/INTEGRATION_TESTING.md b/docker-hermes/integration/INTEGRATION_TESTING.md new file mode 100644 index 0000000..8beb383 --- /dev/null +++ b/docker-hermes/integration/INTEGRATION_TESTING.md @@ -0,0 +1,297 @@ +# Docker Hermes Integration Testing + +This directory contains a complete integration testing setup for the docker-hermes service discovery system using Docker Compose. + +## 🏗️ Architecture + +The integration test stack includes: + +- **Redis**: Central storage and messaging +- **docker-hermes-server**: HTTP API and Prometheus SD endpoint +- **docker-hermes-agent-1/2**: Two agents monitoring different "hosts" +- **test-app-1/2/3**: Test applications with various label configurations +- **Prometheus**: Metrics collection with HTTP SD integration +- **Grafana**: Visualization and monitoring dashboards +- **Redis Commander**: Redis management interface + +## 🚀 Quick Start + +### 1. Start the Stack + +```bash +# Start all services +docker-compose up -d + +# Check service status +docker-compose ps +``` + +### 2. Run Integration Tests + +```bash +# Run automated tests +./test-integration.sh test + +# Show service URLs +./test-integration.sh urls +``` + +### 3. Access Services + +| Service | URL | Credentials | +|---------|-----|-------------| +| Grafana | http://localhost:3000 | admin/admin | +| Prometheus | http://localhost:9090 | - | +| Redis Commander | http://localhost:8081 | - | +| Hermes API | http://localhost:8080/api/v1/containers | - | +| Prometheus SD | http://localhost:8080/prometheus/sd | - | + +## 🧪 Test Scenarios + +### Scenario 1: Basic Container Discovery +- **test-app-1** and **test-app-2** have `prometheus.io/scrape=true` labels +- **test-app-3** does not have Prometheus labels +- Agents should discover all containers, but only apps 1&2 should appear in Prometheus SD + +### Scenario 2: Multi-Host Simulation +- **hermes-agent-1** reports as "host1" +- **hermes-agent-2** reports as "host2" +- Both agents monitor the same Docker daemon but report different hostnames +- Tests hostname isolation and multi-host scenarios + +### Scenario 3: Label Filtering +- All agents use empty `LABEL_PREFIX` (reports all labels) +- Tests label propagation from containers to Prometheus targets + +### Scenario 4: TTL and Cleanup +- Containers are automatically removed from Redis after TTL expires +- Tests automatic cleanup when agents stop reporting + +## 📊 Monitoring + +### Grafana Dashboard +The included dashboard shows: +- Containers tracked per agent +- Update rates +- Error rates +- Container target status in Prometheus + +### Prometheus Targets +Check `http://localhost:9090/targets` to see: +- Static targets (Prometheus, agents, server) +- Dynamic targets from HTTP SD (test applications) + +### Agent Metrics +Each agent exposes metrics on: +- `http://localhost:9091/metrics` (agent-1) +- `http://localhost:9092/metrics` (agent-2) + +## 🔧 Configuration + +### Environment Variables + +**Server:** +- `REDIS_URL`: Redis connection string +- `HTTP_LISTEN_ADDR`: Server listen address +- `PROMETHEUS_SD_PATH`: Prometheus SD endpoint path + +**Agents:** +- `DOCKER_SOCKET`: Docker socket path +- `HOSTNAME`: Agent hostname identifier +- `LABEL_PREFIX`: Label filtering prefix +- `HEARTBEAT_INTERVAL`: Heartbeat frequency + +### Test Applications + +**test-app-1** (Prometheus enabled): +```yaml +labels: + - "prometheus.io/scrape=true" + - "prometheus.io/port=80" + - "prometheus.io/path=/metrics" + - "app=nginx" + - "environment=test" + - "version=1.0" +``` + +**test-app-2** (Prometheus enabled): +```yaml +labels: + - "prometheus.io/scrape=true" + - "prometheus.io/port=80" + - "prometheus.io/path=/metrics" + - "app=nginx" + - "environment=test" + - "version=2.0" +``` + +**test-app-3** (No Prometheus): +```yaml +labels: + - "app=nginx" + - "environment=test" + - "version=3.0" + - "no-prometheus=true" +``` + +## 🧪 Manual Testing + +### 1. Test API Endpoints + +```bash +# List all containers +curl http://localhost:8080/api/v1/containers + +# Get specific container +curl http://localhost:8080/api/v1/containers/host1/test-app-1 + +# Query by labels +curl -X POST http://localhost:8080/api/v1/containers/query \ + -H "Content-Type: application/json" \ + -d '{"labels": {"app": "nginx"}}' + +# List all labels +curl http://localhost:8080/api/v1/labels + +# Get label values +curl http://localhost:8080/api/v1/labels/app +``` + +### 2. Test Prometheus SD + +```bash +# Get Prometheus SD targets +curl http://localhost:8080/prometheus/sd | jq +``` + +Expected output: +```json +[ + { + "targets": ["host1:80"], + "labels": { + "__meta_docker_container_id": "...", + "__meta_docker_container_name": "test-app-1", + "app": "nginx", + "environment": "test", + "version": "1.0" + } + }, + { + "targets": ["host2:80"], + "labels": { + "__meta_docker_container_id": "...", + "__meta_docker_container_name": "test-app-2", + "app": "nginx", + "environment": "test", + "version": "2.0" + } + } +] +``` + +### 3. Test CLI Commands + +```bash +# Build binary +go build -o docker-hermes . + +# Query containers +./docker-hermes query containers --server http://localhost:8080 + +# Query labels +./docker-hermes query labels --server http://localhost:8080 + +# Query Prometheus targets +./docker-hermes query targets --server http://localhost:8080 +``` + +## 🐛 Troubleshooting + +### Services Not Starting + +```bash +# Check logs +docker-compose logs hermes-server +docker-compose logs hermes-agent-1 +docker-compose logs redis + +# Check service health +docker-compose ps +``` + +### No Containers Discovered + +1. Check agent logs for Docker connection issues +2. Verify Docker socket is mounted correctly +3. Check Redis connectivity +4. Verify heartbeat intervals + +### Prometheus Targets Missing + +1. Check Prometheus SD endpoint: `curl http://localhost:8080/prometheus/sd` +2. Verify containers have `prometheus.io/scrape=true` labels +3. Check Prometheus configuration +4. Look at Prometheus logs: `docker-compose logs prometheus` + +### Redis Issues + +1. Check Redis connectivity: `docker-compose exec redis redis-cli ping` +2. Use Redis Commander: http://localhost:8081 +3. Check Redis logs: `docker-compose logs redis` + +## 🧹 Cleanup + +```bash +# Stop and remove all containers +./test-integration.sh cleanup + +# Or manually +docker-compose down -v +docker system prune -f +``` + +## 📈 Performance Testing + +### Scale Testing + +To test with more containers: + +```bash +# Add more test applications +docker run -d --name test-app-4 \ + --label "prometheus.io/scrape=true" \ + --label "prometheus.io/port=80" \ + --label "app=nginx" \ + --label "environment=test" \ + --label "version=4.0" \ + nginx:alpine + +# Check if discovered +curl http://localhost:8080/api/v1/containers | jq '.count' +``` + +### Load Testing + +```bash +# Test API performance +ab -n 1000 -c 10 http://localhost:8080/api/v1/containers + +# Test Prometheus SD performance +ab -n 1000 -c 10 http://localhost:8080/prometheus/sd +``` + +## 🔄 Continuous Integration + +The integration test can be run in CI/CD pipelines: + +```yaml +# Example GitHub Actions +- name: Run Integration Tests + run: | + docker-compose up -d + ./test-integration.sh test + docker-compose down -v +``` + +This setup provides a comprehensive testing environment for validating docker-hermes functionality across all components. diff --git a/docker-hermes/integration/docker-compose.yml b/docker-hermes/integration/docker-compose.yml new file mode 100644 index 0000000..a208981 --- /dev/null +++ b/docker-hermes/integration/docker-compose.yml @@ -0,0 +1,248 @@ +--- +services: + # Redis server for docker-hermes + redis: + image: redis:7-alpine + container_name: docker-hermes-redis + ports: + - "6379:6379" + volumes: + - redis_data:/data + command: redis-server --appendonly yes + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 5s + timeout: 3s + retries: 5 + networks: + - hermes-network + + # docker-hermes server + hermes-server: + build: + context: ../.. + dockerfile: docker-hermes/Dockerfile + container_name: docker-hermes-server + ports: + - "8080:8080" + command: [server] + environment: + - CONFIG_TYPE=env + - REDIS_URL=redis://redis:6379 + - HTTP_LISTEN_ADDRESS=:8080 + - PROMETHEUS_SD_PATH=/prometheus/sd + - TRACING_ENABLED=false + depends_on: + redis: + condition: service_healthy + networks: + - hermes-network + healthcheck: + test: + [ + "CMD", + "wget", + "--quiet", + "--tries=1", + "--spider", + "http://localhost:8080/health", + ] + interval: 10s + timeout: 5s + retries: 3 + + # docker-hermes agent on host1 + hermes-agent-1: + build: + context: ../.. + dockerfile: docker-hermes/Dockerfile + container_name: docker-hermes-agent-1 + ports: + - "9091:9090" # Metrics port + command: [agent] + environment: + - CONFIG_TYPE=env + - REDIS_URL=redis://redis:6379 + - DOCKER_SOCKET=unix:///var/run/docker.sock + - HOSTNAME=host1 + - LABEL_PREFIX= + - HEARTBEAT_INTERVAL=15s + - TRACING_ENABLED=false + - LOG_LEVEL=debug + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + group_add: + - "943" # docker group id + depends_on: + redis: + condition: service_healthy + networks: + - hermes-network + healthcheck: + test: + [ + "CMD", + "wget", + "--quiet", + "--tries=1", + "--spider", + "http://localhost:9090/metrics", + ] + interval: 15s + timeout: 5s + retries: 3 + + # docker-hermes agent on host2 + hermes-agent-2: + build: + context: ../.. + dockerfile: docker-hermes/Dockerfile + container_name: docker-hermes-agent-2 + ports: + - "9092:9090" # Metrics port + command: [agent] + environment: + - CONFIG_TYPE=env + - REDIS_URL=redis://redis:6379 + - DOCKER_SOCKET=unix:///var/run/docker.sock + - HOSTNAME=host2 + - LABEL_PREFIX= + - HEARTBEAT_INTERVAL=15s + - TRACING_ENABLED=false + - LOG_LEVEL=debug + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + group_add: + - "943" # docker group id + depends_on: + redis: + condition: service_healthy + networks: + - hermes-network + healthcheck: + test: + [ + "CMD", + "wget", + "--quiet", + "--tries=1", + "--spider", + "http://localhost:9090/metrics", + ] + interval: 15s + timeout: 5s + retries: 3 + + # Test application 1 - with Prometheus metrics + test-app-1: + image: nginx:alpine + container_name: test-app-1 + ports: + - "8001:80" + labels: + - "app=nginx" + - "environment=test" + - "version=1.0" + # explicitly defined port + - "hermes.prometheus.scrape=true" + - "hermes.prometheus.port=8001" + - "hermes.prometheus.path=/metrics" + - "hermes.prometheus.scheme=http" + networks: + - hermes-network + + # Test application 2 - with Prometheus metrics + test-app-2: + image: nginx:alpine + container_name: test-app-2 + ports: + - "8002:80" + labels: + - "app=nginx" + - "environment=test" + - "version=2.0" + # implicitly defined port + - "hermes.prometheus.scrape=true" + - "hermes.prometheus.path=/metrics" + - "hermes.prometheus.scheme=http" + networks: + - hermes-network + + # Test application 3 - without Prometheus metrics + test-app-3: + image: nginx:alpine + container_name: test-app-3 + ports: + - "8003:80" + labels: + - "app=nginx" + - "environment=test" + - "version=3.0" + - "no-prometheus=true" + networks: + - hermes-network + + # Prometheus for testing HTTP SD + prometheus: + image: prom/prometheus:latest + container_name: docker-hermes-prometheus + ports: + - "9090:9090" + volumes: + - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro + - prometheus_data:/prometheus + command: + - "--config.file=/etc/prometheus/prometheus.yml" + - "--storage.tsdb.path=/prometheus" + - "--web.console.libraries=/etc/prometheus/console_libraries" + - "--web.console.templates=/etc/prometheus/consoles" + - "--storage.tsdb.retention.time=200h" + - "--web.enable-lifecycle" + depends_on: + hermes-server: + condition: service_healthy + networks: + - hermes-network + + # Grafana for visualization + grafana: + image: grafana/grafana:latest + container_name: docker-hermes-grafana + ports: + - "3000:3000" + environment: + - GF_SECURITY_ADMIN_PASSWORD=admin + volumes: + - grafana_data:/var/lib/grafana + - ./grafana/dashboards:/etc/grafana/provisioning/dashboards:ro + - ./grafana/datasources:/etc/grafana/provisioning/datasources:ro + labels: + - "hermes.prometheus.scrape=true" + - "hermes.prometheus.port=3000" + - "hermes.prometheus.path=/metrics" + - "hermes.prometheus.scheme=http" + networks: + - hermes-network + + # Redis Commander for Redis management + redis-commander: + image: rediscommander/redis-commander:latest + container_name: docker-hermes-redis-commander + ports: + - "8081:8081" + environment: + - REDIS_HOSTS=local:redis:6379 + depends_on: + redis: + condition: service_healthy + networks: + - hermes-network + +volumes: + redis_data: + prometheus_data: + grafana_data: + +networks: + hermes-network: + driver: bridge diff --git a/docker-hermes/integration/grafana/dashboards/dashboard.yml b/docker-hermes/integration/grafana/dashboards/dashboard.yml new file mode 100644 index 0000000..4c597c5 --- /dev/null +++ b/docker-hermes/integration/grafana/dashboards/dashboard.yml @@ -0,0 +1,12 @@ +apiVersion: 1 + +providers: + - name: 'docker-hermes' + orgId: 1 + folder: '' + type: file + disableDeletion: false + updateIntervalSeconds: 10 + allowUiUpdates: true + options: + path: /etc/grafana/provisioning/dashboards diff --git a/docker-hermes/integration/grafana/dashboards/docker-hermes.json b/docker-hermes/integration/grafana/dashboards/docker-hermes.json new file mode 100644 index 0000000..32b5034 --- /dev/null +++ b/docker-hermes/integration/grafana/dashboards/docker-hermes.json @@ -0,0 +1,361 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "id": null, + "links": [], + "panels": [ + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "vis": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 1, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom" + }, + "tooltip": { + "mode": "single" + } + }, + "targets": [ + { + "expr": "docker_hermes_containers_tracked", + "interval": "", + "legendFormat": "{{instance}}", + "refId": "A" + } + ], + "title": "Containers Tracked", + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "vis": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 2, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom" + }, + "tooltip": { + "mode": "single" + } + }, + "targets": [ + { + "expr": "rate(docker_hermes_updates_sent_total[5m])", + "interval": "", + "legendFormat": "{{instance}}", + "refId": "A" + } + ], + "title": "Updates Sent Rate", + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "vis": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 8 + }, + "id": 3, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom" + }, + "tooltip": { + "mode": "single" + } + }, + "targets": [ + { + "expr": "rate(docker_hermes_errors_total[5m])", + "interval": "", + "legendFormat": "{{instance}}", + "refId": "A" + } + ], + "title": "Error Rate", + "type": "timeseries" + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "vis": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 8 + }, + "id": 4, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom" + }, + "tooltip": { + "mode": "single" + } + }, + "targets": [ + { + "expr": "up{job=\"docker-containers\"}", + "interval": "", + "legendFormat": "{{__meta_docker_container_name}}", + "refId": "A" + } + ], + "title": "Container Targets Status", + "type": "timeseries" + } + ], + "schemaVersion": 27, + "style": "dark", + "tags": ["docker-hermes"], + "templating": { + "list": [] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Docker Hermes Dashboard", + "uid": "docker-hermes", + "version": 1 +} diff --git a/docker-hermes/integration/grafana/datasources/prometheus.yml b/docker-hermes/integration/grafana/datasources/prometheus.yml new file mode 100644 index 0000000..5969992 --- /dev/null +++ b/docker-hermes/integration/grafana/datasources/prometheus.yml @@ -0,0 +1,15 @@ +apiVersion: 1 + +datasources: + - name: Prometheus + type: prometheus + access: proxy + url: http://prometheus:9090 + isDefault: true + editable: true + + - name: Redis + type: redis-datasource + access: proxy + url: redis://redis:6379 + editable: true diff --git a/docker-hermes/integration/prometheus.yml b/docker-hermes/integration/prometheus.yml new file mode 100644 index 0000000..faf6321 --- /dev/null +++ b/docker-hermes/integration/prometheus.yml @@ -0,0 +1,37 @@ +global: + scrape_interval: 15s + evaluation_interval: 15s + +rule_files: + # - "first_rules.yml" + # - "second_rules.yml" + +scrape_configs: + # Prometheus itself + - job_name: 'prometheus' + static_configs: + - targets: ['localhost:9090'] + + # docker-hermes server metrics + - job_name: 'docker-hermes-server' + static_configs: + - targets: ['hermes-server:8080'] + metrics_path: '/metrics' + scrape_interval: 30s + + # docker-hermes agents metrics + - job_name: 'docker-hermes-agents' + static_configs: + - targets: ['hermes-agent-1:9090', 'hermes-agent-2:9090'] + metrics_path: '/metrics' + scrape_interval: 30s + + # Docker containers via docker-hermes HTTP SD + - job_name: 'docker-containers' + http_sd_configs: + - url: 'http://hermes-server:8080/prometheus/sd' + refresh_interval: 30s + scrape_interval: 30s + scrape_timeout: 10s + metrics_path: '/metrics' + scheme: 'http' diff --git a/docker-hermes/integration/test-integration.sh b/docker-hermes/integration/test-integration.sh new file mode 100755 index 0000000..3b7c28c --- /dev/null +++ b/docker-hermes/integration/test-integration.sh @@ -0,0 +1,242 @@ +#!/bin/bash + +# Integration test script for docker-hermes +set -e + +echo "🚀 Starting docker-hermes integration test..." + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Function to print colored output +print_status() { + echo -e "${GREEN}[INFO]${NC} $1" +} + +print_warning() { + echo -e "${YELLOW}[WARN]${NC} $1" +} + +print_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# Function to wait for service to be ready +wait_for_service() { + local service_name=$1 + local url=$2 + local max_attempts=30 + local attempt=1 + + print_status "Waiting for $service_name to be ready..." + + while [ $attempt -le $max_attempts ]; do + if curl -s "$url" > /dev/null 2>&1; then + print_status "$service_name is ready!" + return 0 + fi + + echo -n "." + sleep 2 + attempt=$((attempt + 1)) + done + + print_error "$service_name failed to start after $((max_attempts * 2)) seconds" + return 1 +} + +# Function to test API endpoints +test_api_endpoints() { + local base_url="http://localhost:8080" + + print_status "Testing API endpoints..." + + # Test health endpoint + if curl -s "$base_url/health" | grep -q "healthy"; then + print_status "✅ Health endpoint working" + else + print_error "❌ Health endpoint failed" + return 1 + fi + + # Test containers endpoint + if curl -s "$base_url/api/v1/containers" | grep -q "containers"; then + print_status "✅ Containers endpoint working" + else + print_error "❌ Containers endpoint failed" + return 1 + fi + + # Test labels endpoint + if curl -s "$base_url/api/v1/labels" | grep -q "labels"; then + print_status "✅ Labels endpoint working" + else + print_error "❌ Labels endpoint failed" + return 1 + fi + + # Test Prometheus SD endpoint + if curl -s "$base_url/prometheus/sd" | grep -q "targets"; then + print_status "✅ Prometheus SD endpoint working" + else + print_error "❌ Prometheus SD endpoint failed" + return 1 + fi +} + +# Function to test Prometheus targets +test_prometheus_targets() { + print_status "Testing Prometheus targets..." + + # Wait a bit for targets to be discovered + sleep 10 + + local targets_url="http://localhost:9090/api/v1/targets" + local targets_response=$(curl -s "$targets_url") + + if echo "$targets_response" | grep -q "docker-containers"; then + print_status "✅ Prometheus discovered docker-containers job" + else + print_warning "⚠️ Prometheus docker-containers job not found" + fi + + # Check if we have active targets + local active_targets=$(echo "$targets_response" | grep -o '"health":"up"' | wc -l) + if [ "$active_targets" -gt 0 ]; then + print_status "✅ Found $active_targets active targets" + else + print_warning "⚠️ No active targets found" + fi +} + +# Function to test agent metrics +test_agent_metrics() { + print_status "Testing agent metrics..." + + # Test agent 1 metrics + if curl -s "http://localhost:9091/metrics" | grep -q "docker_hermes_containers_tracked"; then + print_status "✅ Agent 1 metrics working" + else + print_error "❌ Agent 1 metrics failed" + return 1 + fi + + # Test agent 2 metrics + if curl -s "http://localhost:9092/metrics" | grep -q "docker_hermes_containers_tracked"; then + print_status "✅ Agent 2 metrics working" + else + print_error "❌ Agent 2 metrics failed" + return 1 + fi +} + +# Function to run CLI tests +test_cli_commands() { + print_status "Testing CLI commands..." + + # Build the binary + if go build -o docker-hermes .; then + print_status "✅ Binary built successfully" + else + print_error "❌ Failed to build binary" + return 1 + fi + + # Test query commands + if ./docker-hermes query containers --server http://localhost:8080 > /dev/null 2>&1; then + print_status "✅ Query containers command working" + else + print_error "❌ Query containers command failed" + return 1 + fi + + if ./docker-hermes query labels --server http://localhost:8080 > /dev/null 2>&1; then + print_status "✅ Query labels command working" + else + print_error "❌ Query labels command failed" + return 1 + fi + + if ./docker-hermes query targets --server http://localhost:8080 > /dev/null 2>&1; then + print_status "✅ Query targets command working" + else + print_error "❌ Query targets command failed" + return 1 + fi + + # Clean up + rm -f docker-hermes +} + +# Main test function +run_tests() { + print_status "Starting integration tests..." + + # Wait for services to be ready + wait_for_service "Redis" "http://localhost:8081" || return 1 + wait_for_service "Hermes Server" "http://localhost:8080/health" || return 1 + wait_for_service "Prometheus" "http://localhost:9090" || return 1 + + # Run tests + test_api_endpoints || return 1 + test_agent_metrics || return 1 + test_prometheus_targets || return 1 + test_cli_commands || return 1 + + print_status "🎉 All integration tests passed!" + return 0 +} + +# Function to show service URLs +show_service_urls() { + echo "" + print_status "Service URLs:" + echo " 📊 Grafana: http://localhost:3000 (admin/admin)" + echo " 📈 Prometheus: http://localhost:9090" + echo " 🔧 Redis Commander: http://localhost:8081" + echo " 🌐 Hermes Server: http://localhost:8080" + echo " 📋 Hermes API: http://localhost:8080/api/v1/containers" + echo " 🎯 Prometheus SD: http://localhost:8080/prometheus/sd" + echo " 📊 Agent 1 Metrics: http://localhost:9091/metrics" + echo " 📊 Agent 2 Metrics: http://localhost:9092/metrics" + echo "" +} + +# Function to cleanup +cleanup() { + print_status "Cleaning up..." + docker-compose down -v +} + +# Handle script arguments +case "${1:-}" in + "test") + run_tests + ;; + "urls") + show_service_urls + ;; + "cleanup") + cleanup + ;; + *) + echo "Usage: $0 {test|urls|cleanup}" + echo "" + echo "Commands:" + echo " test - Run integration tests" + echo " urls - Show service URLs" + echo " cleanup - Stop and remove all containers" + echo "" + echo "To start the stack:" + echo " docker-compose up -d" + echo "" + echo "To run tests:" + echo " $0 test" + echo "" + echo "To view services:" + echo " $0 urls" + ;; +esac diff --git a/docker-hermes/main.go b/docker-hermes/main.go new file mode 100644 index 0000000..ea90142 --- /dev/null +++ b/docker-hermes/main.go @@ -0,0 +1,9 @@ +package main + +import ( + "github.com/pirogoeth/apps/docker-hermes/cmd" +) + +func main() { + cmd.Execute() +} diff --git a/docker-hermes/redis/client.go b/docker-hermes/redis/client.go new file mode 100644 index 0000000..2563405 --- /dev/null +++ b/docker-hermes/redis/client.go @@ -0,0 +1,44 @@ +package redis + +import ( + "context" + "fmt" + "time" + + "github.com/redis/go-redis/v9" +) + +// Client wraps Redis client with docker-hermes specific operations +type Client struct { + rdb *redis.Client +} + +// NewClient creates a new Redis client +func NewClient(redisURL string) (*Client, error) { + opt, err := redis.ParseURL(redisURL) + if err != nil { + return nil, fmt.Errorf("failed to parse Redis URL: %w", err) + } + + rdb := redis.NewClient(opt) + + // Test connection + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + if err := rdb.Ping(ctx).Err(); err != nil { + return nil, fmt.Errorf("failed to connect to Redis: %w", err) + } + + return &Client{rdb}, nil +} + +// Close closes the Redis connection +func (c *Client) Close() error { + return c.rdb.Close() +} + +// Ping tests the Redis connection +func (c *Client) Ping(ctx context.Context) error { + return c.rdb.Ping(ctx).Err() +} diff --git a/docker-hermes/redis/keys.go b/docker-hermes/redis/keys.go new file mode 100644 index 0000000..4163323 --- /dev/null +++ b/docker-hermes/redis/keys.go @@ -0,0 +1,38 @@ +package redis + +import "fmt" + +type cacheKey = string + +// containerHostCacheKey returns the Redis key for a container host +func containerHostCacheKey(host string) cacheKey { + return "hermes:containers:" + host +} + +// containerCacheKey returns the Redis key for a container +func containerCacheKey(host, containerID string) cacheKey { + return fmt.Sprintf("%s:%s", containerHostCacheKey(host), containerID) +} + +// streamCacheKey returns the Redis stream key for updates +func streamCacheKey() cacheKey { + return "hermes:stream:updates" +} + +// labelsCacheKey returns the Redis key for label metadata +func labelsCacheKey() cacheKey { + return "hermes:labels" +} + +// labelCacheKey returns the Redis key for a label +func labelCacheKey(labelKey string) cacheKey { + return fmt.Sprintf("%s:%s", labelsCacheKey(), labelKey) +} + +// hostsCacheKey returns the Redis key for host metadata +func hostsCacheKey() cacheKey { + return "hermes:hosts" +} + + + diff --git a/docker-hermes/redis/operations.go b/docker-hermes/redis/operations.go new file mode 100644 index 0000000..d591679 --- /dev/null +++ b/docker-hermes/redis/operations.go @@ -0,0 +1,269 @@ +package redis + +import ( + "context" + "encoding/json" + "fmt" + "time" + + "github.com/redis/go-redis/v9" + "github.com/sirupsen/logrus" + + "github.com/pirogoeth/apps/docker-hermes/types" +) + +// StoreContainer stores container information in Redis with TTL +func (c *Client) StoreContainer(ctx context.Context, container *types.ContainerInfo, ttl time.Duration) error { + key := containerCacheKey(container.Host, container.ID) + + data, err := json.Marshal(container) + if err != nil { + return fmt.Errorf("failed to marshal container data: %w", err) + } + + pipe := c.rdb.Pipeline() + + // Store container data + pipe.Set(ctx, key, data, ttl) + + // Add to host set + hostKey := hostsCacheKey() + pipe.SAdd(ctx, hostKey, container.Host) + pipe.Expire(ctx, hostKey, ttl*2) // Keep host set longer + + // Add labels to label sets + for labelKey, labelValue := range container.Labels { + labelKeyStr := labelCacheKey(labelKey) + pipe.SAdd(ctx, labelKeyStr, labelValue) + pipe.Expire(ctx, labelKeyStr, ttl*2) + } + pipe.Expire(ctx, labelsCacheKey(), ttl*2) + + _, err = pipe.Exec(ctx) + if err != nil { + return fmt.Errorf("failed to store container data: %w", err) + } + + logrus.Debugf("Stored container %s on host %s", container.ID, container.Host) + return nil +} + +// GetContainer retrieves container information from Redis +func (c *Client) GetContainer(ctx context.Context, host, containerID string) (*types.ContainerInfo, error) { + key := containerCacheKey(host, containerID) + + data, err := c.rdb.Get(ctx, key).Result() + if err != nil { + if err == redis.Nil { + return nil, nil + } + return nil, fmt.Errorf("failed to get container data: %w", err) + } + + var container types.ContainerInfo + if err := json.Unmarshal([]byte(data), &container); err != nil { + return nil, fmt.Errorf("failed to unmarshal container data: %w", err) + } + + return &container, nil +} + +// ListContainers retrieves all active containers +func (c *Client) ListContainers(ctx context.Context) ([]*types.ContainerInfo, error) { + pattern := containerHostCacheKey("*") + keys, err := c.rdb.Keys(ctx, pattern).Result() + if err != nil { + return nil, fmt.Errorf("failed to list container keys: %w", err) + } + + var containers []*types.ContainerInfo + for _, key := range keys { + data, err := c.rdb.Get(ctx, key).Result() + if err != nil { + logrus.Warnf("Failed to get container data for key %s: %v", key, err) + continue + } + + var container types.ContainerInfo + if err := json.Unmarshal([]byte(data), &container); err != nil { + logrus.Warnf("Failed to unmarshal container data for key %s: %v", key, err) + continue + } + + containers = append(containers, &container) + } + + return containers, nil +} + +// QueryByLabels finds containers matching label filters +func (c *Client) QueryByLabels(ctx context.Context, query *types.ContainerQuery) ([]*types.ContainerInfo, error) { + containers, err := c.ListContainers(ctx) + if err != nil { + return nil, err + } + + var filtered []*types.ContainerInfo + for _, container := range containers { + // Filter by host if specified + if query.Host != "" && container.Host != query.Host { + continue + } + + // Check if all query labels match + matches := true + for queryKey, queryValue := range query.Labels { + if containerValue, exists := container.Labels[queryKey]; !exists || containerValue != queryValue { + matches = false + break + } + } + + if matches { + filtered = append(filtered, container) + } + } + + return filtered, nil +} + +// PublishUpdate publishes a container update to Redis Stream +func (c *Client) PublishUpdate(ctx context.Context, container *types.ContainerInfo) error { + streamKey := streamCacheKey() + + data, err := json.Marshal(container) + if err != nil { + return fmt.Errorf("failed to marshal container data for stream: %w", err) + } + + args := map[string]interface{}{ + "container": string(data), + "timestamp": time.Now().Unix(), + "host": container.Host, + "id": container.ID, + } + + _, err = c.rdb.XAdd(ctx, &redis.XAddArgs{ + Stream: streamKey, + Values: args, + }).Result() + + if err != nil { + return fmt.Errorf("failed to publish update to stream: %w", err) + } + + logrus.Debugf("Published update for container %s on host %s", container.ID, container.Host) + return nil +} + +// GetPrometheusTargets retrieves all containers that should be scraped by Prometheus +func (c *Client) GetPrometheusTargets(ctx context.Context, strategy types.TargetResolutionStrategy, agentHost, customHost string, labelsConfig *types.LabelsConfig) (types.PrometheusSDResponse, error) { + containers, err := c.ListContainers(ctx) + if err != nil { + return nil, err + } + + var targets types.PrometheusSDResponse + for _, container := range containers { + // Attach labels config to containers retrieved from Redis (they won't have it) + container.LabelsConfig = labelsConfig + + // Get all targets from this container + containerTargets := container.ToPrometheusTargets(strategy, agentHost, customHost) + targets = append(targets, containerTargets...) + } + + return targets, nil +} + +// GetLabels retrieves all unique label keys and their values +func (c *Client) GetLabels(ctx context.Context) ([]types.LabelInfo, error) { + // Get all label key patterns + pattern := labelCacheKey("*") + keys, err := c.rdb.Keys(ctx, pattern).Result() + if err != nil { + return nil, fmt.Errorf("failed to list label keys: %w", err) + } + + labelsKeyPrefix := labelsCacheKey() + var labels []types.LabelInfo + for _, key := range keys { + // Extract label key from Redis key + labelKey := key[len(labelsKeyPrefix)+1:] // Remove "hermes:labels:" prefix + + // Get all values for this label + values, err := c.rdb.SMembers(ctx, key).Result() + if err != nil { + logrus.Warnf("Failed to get values for label %s: %v", labelKey, err) + continue + } + + labels = append(labels, types.LabelInfo{ + Key: labelKey, + Values: values, + }) + } + + return labels, nil +} + +// GetLabelValues retrieves all values for a specific label key +func (c *Client) GetLabelValues(ctx context.Context, labelKey string) ([]string, error) { + key := labelCacheKey(labelKey) + values, err := c.rdb.SMembers(ctx, key).Result() + if err != nil { + if err == redis.Nil { + return []string{}, nil + } + return nil, fmt.Errorf("failed to get label values: %w", err) + } + + return values, nil +} + +// GetHosts retrieves all known hosts +func (c *Client) GetHosts(ctx context.Context) ([]string, error) { + hostsKey := hostsCacheKey() + hosts, err := c.rdb.SMembers(ctx, hostsKey).Result() + if err != nil { + if err == redis.Nil { + return []string{}, nil + } + return nil, fmt.Errorf("failed to get hosts: %w", err) + } + + return hosts, nil +} + +// CleanupExpired removes expired container data (called periodically) +func (c *Client) CleanupExpired(ctx context.Context) error { + pattern := containerHostCacheKey("*") + keys, err := c.rdb.Keys(ctx, pattern).Result() + if err != nil { + return fmt.Errorf("failed to list container keys for cleanup: %w", err) + } + + var expiredKeys []string + for _, key := range keys { + ttl, err := c.rdb.TTL(ctx, key).Result() + if err != nil { + logrus.Warnf("Failed to get TTL for key %s: %v", key, err) + continue + } + + // If TTL is very low (less than 5 seconds), consider it expired + if ttl < 5*time.Second { + expiredKeys = append(expiredKeys, key) + } + } + + if len(expiredKeys) > 0 { + _, err = c.rdb.Del(ctx, expiredKeys...).Result() + if err != nil { + return fmt.Errorf("failed to delete expired keys: %w", err) + } + logrus.Debugf("Cleaned up %d expired container keys", len(expiredKeys)) + } + + return nil +} diff --git a/docker-hermes/server/api.go b/docker-hermes/server/api.go new file mode 100644 index 0000000..9bbf454 --- /dev/null +++ b/docker-hermes/server/api.go @@ -0,0 +1,150 @@ +package server + +import ( + "net/http" + + "github.com/gin-gonic/gin" + "github.com/sirupsen/logrus" + + "github.com/pirogoeth/apps/docker-hermes/types" + api "github.com/pirogoeth/apps/pkg/apitools" +) + +// RegisterRoutes registers all API routes +func RegisterRoutes(router *gin.Engine, apiContext *types.ApiContext) error { + v1 := router.Group("/api/v1") + { + // Container endpoints + v1.GET("/containers", listContainers(apiContext)) + v1.GET("/containers/:host/:id", getContainer(apiContext)) + v1.POST("/containers/query", queryContainers(apiContext)) + + // Label endpoints + v1.GET("/labels", listLabels(apiContext)) + v1.GET("/labels/:key", getLabelValues(apiContext)) + + // Host endpoints + v1.GET("/hosts", listHosts(apiContext)) + } + + logrus.Info("Registered API routes") + return nil +} + +// listContainers returns all active containers +func listContainers(apiContext *types.ApiContext) gin.HandlerFunc { + return func(c *gin.Context) { + containers, err := apiContext.RedisClient.ListContainers(c.Request.Context()) + if err != nil { + logrus.Errorf("Failed to list containers: %v", err) + api.ErrorPayload("failed to list containers", err) + return + } + + api.Ok(c, &api.Body{ + "containers": containers, + "count": len(containers), + }) + } +} + +// getContainer returns a specific container by host and ID +func getContainer(apiContext *types.ApiContext) gin.HandlerFunc { + return func(c *gin.Context) { + host := c.Param("host") + id := c.Param("id") + + container, err := apiContext.RedisClient.GetContainer(c.Request.Context(), host, id) + if err != nil { + logrus.Errorf("Failed to get container %s on host %s: %v", id, host, err) + api.ErrorPayload("failed to get container", err) + return + } + + if container == nil { + c.JSON(http.StatusNotFound, gin.H{"error": "Container not found"}) + return + } + + api.Ok(c, &api.Body{ + "container": container, + }) + } +} + +// queryContainers queries containers by label filters +func queryContainers(apiContext *types.ApiContext) gin.HandlerFunc { + return func(c *gin.Context) { + var query types.ContainerQuery + if err := c.ShouldBindJSON(&query); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid query format"}) + return + } + + containers, err := apiContext.RedisClient.QueryByLabels(c.Request.Context(), &query) + if err != nil { + logrus.Errorf("Failed to query containers: %v", err) + api.ErrorPayload("failed to query containers", err) + return + } + + api.Ok(c, &api.Body{ + "containers": containers, + "count": len(containers), + "query": query, + }) + } +} + +// listLabels returns all unique label keys and their values +func listLabels(apiContext *types.ApiContext) gin.HandlerFunc { + return func(c *gin.Context) { + labels, err := apiContext.RedisClient.GetLabels(c.Request.Context()) + if err != nil { + logrus.Errorf("Failed to list labels: %v", err) + api.ErrorPayload("failed to list labels", err) + return + } + + api.Ok(c, &api.Body{ + "labels": labels, + "count": len(labels), + }) + } +} + +// getLabelValues returns all values for a specific label key +func getLabelValues(apiContext *types.ApiContext) gin.HandlerFunc { + return func(c *gin.Context) { + key := c.Param("key") + values, err := apiContext.RedisClient.GetLabelValues(c.Request.Context(), key) + if err != nil { + logrus.Errorf("Failed to get label values for key %s: %v", key, err) + api.ErrorPayload("failed to get label values", err) + return + } + + api.Ok(c, &api.Body{ + "key": key, + "values": values, + "count": len(values), + }) + } +} + +// listHosts returns all known hosts +func listHosts(apiContext *types.ApiContext) gin.HandlerFunc { + return func(c *gin.Context) { + hosts, err := apiContext.RedisClient.GetHosts(c.Request.Context()) + if err != nil { + logrus.Errorf("Failed to list hosts: %v", err) + api.ErrorPayload("failed to list hosts", err) + return + } + + api.Ok(c, &api.Body{ + "hosts": hosts, + "count": len(hosts), + }) + } +} diff --git a/docker-hermes/server/server.go b/docker-hermes/server/server.go new file mode 100644 index 0000000..5029bbd --- /dev/null +++ b/docker-hermes/server/server.go @@ -0,0 +1,143 @@ +package server + +import ( + "context" + "fmt" + "net/http" + "time" + + "github.com/gin-gonic/gin" + "github.com/sirupsen/logrus" + + "github.com/pirogoeth/apps/docker-hermes/redis" + "github.com/pirogoeth/apps/docker-hermes/types" + api "github.com/pirogoeth/apps/pkg/apitools" + "github.com/pirogoeth/apps/pkg/system" +) + +// Server represents the docker-hermes server +type Server struct { + config *types.Config + redisClient types.StorageClient + router *gin.Engine +} + +// NewServer creates a new server instance +func NewServer(ctx context.Context, config *types.Config) (*Server, error) { + redisClient, err := redis.NewClient(config.Redis.URL) + if err != nil { + return nil, fmt.Errorf("failed to create Redis client: %w", err) + } + + router, err := system.DefaultRouterWithTracing(ctx, config.Tracing) + if err != nil { + return nil, fmt.Errorf("failed to create router: %w", err) + } + + server := &Server{ + config: config, + redisClient: redisClient, + router: router, + } + + // Register routes + if err := server.registerRoutes(); err != nil { + redisClient.Close() + return nil, fmt.Errorf("failed to register routes: %w", err) + } + + return server, nil +} + +// registerRoutes registers all HTTP routes +func (s *Server) registerRoutes() error { + // Create API context + apiContext := &types.ApiContext{ + Config: s.config, + RedisClient: s.redisClient, + } + + // Register API routes + if err := RegisterRoutes(s.router, apiContext); err != nil { + return fmt.Errorf("failed to register API routes: %w", err) + } + + // Register Prometheus SD endpoint + if err := s.registerPrometheusSD(apiContext); err != nil { + return fmt.Errorf("failed to register Prometheus SD endpoint: %w", err) + } + + // Health check endpoint + s.router.GET("/health", s.healthCheck) + + return nil +} + +// registerPrometheusSD registers the Prometheus HTTP SD endpoint +func (s *Server) registerPrometheusSD(_ *types.ApiContext) error { + s.router.GET(s.config.Server.PrometheusSDPath, func(c *gin.Context) { + // Use server hostname or empty (defaults to container hostname) + agentHost := s.config.Agent.Hostname + if agentHost == "" { + agentHost = "unknown" + } + + // Pass labels config to Redis operations + targets, err := s.redisClient.GetPrometheusTargets( + c.Request.Context(), + s.config.Server.TargetResolutionStrategy, + agentHost, + s.config.Server.TargetResolutionHost, + &s.config.Labels, + ) + if err != nil { + logrus.Errorf("Failed to get Prometheus targets: %v", err) + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get targets"}) + return + } + + c.JSON(http.StatusOK, targets) + }) + + logrus.Infof("Registered Prometheus SD endpoint at %s", s.config.Server.PrometheusSDPath) + return nil +} + +// healthCheck provides a health check endpoint +func (s *Server) healthCheck(c *gin.Context) { + ctx, cancel := context.WithTimeout(c.Request.Context(), 5*time.Second) + defer cancel() + + // Check Redis connection + if err := s.redisClient.Ping(ctx); err != nil { + c.JSON(http.StatusServiceUnavailable, gin.H{ + "status": "unhealthy", + "error": "Redis connection failed", + }) + return + } + + api.Ok(c, &api.Body{ + "status": "healthy", + "time": time.Now().UTC(), + }) +} + +// Run starts the HTTP server +func (s *Server) Run(ctx context.Context) error { + listenAddr := s.config.HTTP.ListenAddress + + logrus.Infof("Starting server on %s", listenAddr) + go s.router.Run(listenAddr) + + // Wait for context cancellation + <-ctx.Done() + logrus.Info("Server stopped") + + return nil +} + +// Close closes the server and cleans up resources +func (s *Server) Close() error { + return s.redisClient.Close() +} diff --git a/docker-hermes/types/config.go b/docker-hermes/types/config.go new file mode 100644 index 0000000..a22c815 --- /dev/null +++ b/docker-hermes/types/config.go @@ -0,0 +1,64 @@ +package types + +import ( + "os" + "time" + + "github.com/pirogoeth/apps/pkg/config" +) + +// LabelsConfig holds label prefix configuration shared by agent and server +type LabelsConfig struct { + LabelPrefix string `yaml:"label_prefix" envconfig:"LABEL_PREFIX" default:""` + Prefix string `yaml:"prefix" envconfig:"PREFIX" default:"hermes"` // Main label prefix (Traefik-style) +} + +// Config represents the main configuration struct +type Config struct { + config.CommonConfig `yaml:",inline"` + + // Labels configuration shared by agent and server + Labels LabelsConfig `yaml:"labels"` + + // Redis is the common configuration for both the agent and server + // to connect and use the same Redis instance. + Redis RedisConfig `yaml:"redis"` + Agent AgentConfig `yaml:"agent"` + Server ServerConfig `yaml:"server"` +} + +type RedisConfig struct { + URL string `yaml:"url" envconfig:"REDIS_URL" default:"redis://localhost:6379"` +} + +type AgentConfig struct { + DockerSocket string `yaml:"docker_socket" envconfig:"DOCKER_SOCKET" default:"unix:///var/run/docker.sock"` + HeartbeatInterval time.Duration `yaml:"heartbeat_interval" envconfig:"HEARTBEAT_INTERVAL" default:"30s"` + Hostname string `yaml:"hostname" envconfig:"HOSTNAME"` + + // Target resolution configuration + TargetResolutionStrategy TargetResolutionStrategy `yaml:"target_resolution_strategy" envconfig:"TARGET_RESOLUTION_STRATEGY" default:"hostname"` + TargetResolutionHost string `yaml:"target_resolution_host" envconfig:"TARGET_RESOLUTION_HOST" default:""` +} + +type ServerConfig struct { + PrometheusSDPath string `yaml:"prometheus_sd_path" envconfig:"PROMETHEUS_SD_PATH" default:"/prometheus/sd"` + + // Target resolution configuration (for Prometheus SD) + TargetResolutionStrategy TargetResolutionStrategy `yaml:"target_resolution_strategy" envconfig:"TARGET_RESOLUTION_STRATEGY" default:"hostname"` + TargetResolutionHost string `yaml:"target_resolution_host" envconfig:"TARGET_RESOLUTION_HOST" default:""` +} + +// ApplyDefaults applies default values to config structs +func ApplyDefaults(cfg interface{}) { + switch c := cfg.(type) { + case *AgentConfig: + if c.Hostname == "" { + if hostname, err := os.Hostname(); err == nil { + c.Hostname = hostname + } else { + c.Hostname = "unknown" + } + } + } +} diff --git a/docker-hermes/types/resolution.go b/docker-hermes/types/resolution.go new file mode 100644 index 0000000..0469767 --- /dev/null +++ b/docker-hermes/types/resolution.go @@ -0,0 +1,57 @@ +package types + +// TargetResolutionStrategy defines how to resolve container targets +type TargetResolutionStrategy string + +const ( + StrategyHostname TargetResolutionStrategy = "hostname" + StrategyContainerIP TargetResolutionStrategy = "container_ip" + StrategyHostPort TargetResolutionStrategy = "host_port" + StrategyCustom TargetResolutionStrategy = "custom" +) + +// ResolvedTarget represents a resolved Prometheus scrape target +type ResolvedTarget struct { + Address string // Final "host:port" format + Scheme string // http or https + Path string // Metrics path + Labels map[string]string // Labels for the target +} + +// ResolveTargets resolves all Prometheus targets for a container +func (c *ContainerInfo) ResolveTargets(defaultStrategy TargetResolutionStrategy, agentHost, customHost string) []ResolvedTarget { + if c.LabelsConfig == nil { + return nil + } + + // Create extractor with configured prefix + extractor := NewTargetExtractor(c.LabelsConfig.Prefix) + + // Extract all target configurations + targetConfigs := extractor.ExtractTargets(c) + + // Resolve each target + resolved := []ResolvedTarget{} + for _, targetConfig := range targetConfigs { + if resolvedTarget := targetConfig.ResolveTarget(c, defaultStrategy, agentHost, customHost); resolvedTarget != nil { + resolved = append(resolved, *resolvedTarget) + } + } + + return resolved +} + +// ToPrometheusTargets converts ContainerInfo to Prometheus targets +func (c *ContainerInfo) ToPrometheusTargets(strategy TargetResolutionStrategy, agentHost, customHost string) []PrometheusTarget { + resolvedTargets := c.ResolveTargets(strategy, agentHost, customHost) + + promTargets := make([]PrometheusTarget, 0, len(resolvedTargets)) + for _, resolved := range resolvedTargets { + promTargets = append(promTargets, PrometheusTarget{ + Targets: []string{resolved.Address}, + Labels: resolved.Labels, + }) + } + + return promTargets +} diff --git a/docker-hermes/types/storage.go b/docker-hermes/types/storage.go new file mode 100644 index 0000000..4e1d664 --- /dev/null +++ b/docker-hermes/types/storage.go @@ -0,0 +1,48 @@ +package types + +import ( + "context" + "time" +) + +// StorageClient defines the interface for storage backends +// This allows different storage implementations (Redis, PostgreSQL, etc.) +// to be used interchangeably +type StorageClient interface { + // StoreContainer stores container information with TTL + StoreContainer(ctx context.Context, container *ContainerInfo, ttl time.Duration) error + + // GetContainer retrieves container information by host and container ID + // Returns nil if container is not found + GetContainer(ctx context.Context, host, containerID string) (*ContainerInfo, error) + + // ListContainers retrieves all active containers + ListContainers(ctx context.Context) ([]*ContainerInfo, error) + + // QueryByLabels finds containers matching label filters + QueryByLabels(ctx context.Context, query *ContainerQuery) ([]*ContainerInfo, error) + + // PublishUpdate publishes a container update to the event stream + PublishUpdate(ctx context.Context, container *ContainerInfo) error + + // GetPrometheusTargets retrieves all containers that should be scraped by Prometheus + GetPrometheusTargets(ctx context.Context, strategy TargetResolutionStrategy, agentHost, customHost string, labelsConfig *LabelsConfig) (PrometheusSDResponse, error) + + // GetLabels retrieves all unique label keys and their values + GetLabels(ctx context.Context) ([]LabelInfo, error) + + // GetLabelValues retrieves all values for a specific label key + GetLabelValues(ctx context.Context, labelKey string) ([]string, error) + + // GetHosts retrieves all known hosts + GetHosts(ctx context.Context) ([]string, error) + + // CleanupExpired removes expired container data (called periodically) + CleanupExpired(ctx context.Context) error + + // Close closes the storage connection + Close() error + + // Ping tests the storage connection + Ping(ctx context.Context) error +} diff --git a/docker-hermes/types/targets.go b/docker-hermes/types/targets.go new file mode 100644 index 0000000..348e14d --- /dev/null +++ b/docker-hermes/types/targets.go @@ -0,0 +1,351 @@ +package types + +import ( + "fmt" + "strconv" + "strings" +) + +// TargetConfig represents a single scrape target configuration +// +// Targets can be defined in two ways: +// +// 1. Named targets (structured): hermes.targets.{name}.prometheus.* +// Example: +// hermes.targets.admin.prometheus.scrape=true +// hermes.targets.admin.prometheus.port=9090 +// hermes.targets.admin.strategy=container_ip +// +// 2. Shorthand (default target): hermes.prometheus.* +// Example: +// hermes.prometheus.scrape=true +// hermes.prometheus.port=8080 +// The target name is automatically derived from container details +// (format: {host}-{container-name} or similar) +type TargetConfig struct { + Name string // Target identifier (e.g., "admin", or derived from container) + Scrape bool // Whether scraping is enabled + Port int // Port to scrape + Path string // Metrics path (default: /metrics) + Scheme string // http or https (default: http) + Strategy TargetResolutionStrategy // Resolution strategy override (empty = use default) + Host string // Custom host override (empty = use strategy) + Labels map[string]string // Additional labels for this target +} + +// TargetExtractor extracts and validates target configurations from container labels +type TargetExtractor struct { + prefix string +} + +// NewTargetExtractor creates a new target extractor +func NewTargetExtractor(prefix string) *TargetExtractor { + if prefix == "" { + prefix = "hermes" + } + return &TargetExtractor{prefix: prefix} +} + +// ExtractTargets extracts all target configurations from container labels +func (e *TargetExtractor) ExtractTargets(container *ContainerInfo) []TargetConfig { + if e.prefix == "" { + return nil + } + + targets := []TargetConfig{} + + // Discover all named targets: hermes.targets.{name}.prometheus.scrape + namedTargets := e.discoverNamedTargets(container.Labels) + for _, name := range namedTargets { + if config := e.extractTargetConfig(container, name, true); config != nil { + targets = append(targets, *config) + } + } + + // Check for shorthand: hermes.prometheus.scrape (derives target name) + if e.hasShorthandTarget(container.Labels) { + derivedName := e.deriveTargetName(container) + if config := e.extractTargetConfig(container, derivedName, false); config != nil { + targets = append(targets, *config) + } + } + + return targets +} + +// discoverNamedTargets finds all target names from labels +func (e *TargetExtractor) discoverNamedTargets(labels map[string]string) []string { + prefix := e.prefix + ".targets." + suffix := ".prometheus.scrape" + + targetNames := []string{} + seen := make(map[string]bool) + + for key, value := range labels { + if strings.HasPrefix(key, prefix) && strings.HasSuffix(key, suffix) && value == "true" { + // Extract target name: prefix + name + suffix + name := key[len(prefix) : len(key)-len(suffix)] + if name != "" && !seen[name] { + targetNames = append(targetNames, name) + seen[name] = true + } + } + } + + return targetNames +} + +// hasShorthandTarget checks if shorthand format exists +func (e *TargetExtractor) hasShorthandTarget(labels map[string]string) bool { + key := e.prefix + ".prometheus.scrape" + if value, exists := labels[key]; exists && value == "true" { + return true + } + return false +} + +// deriveTargetName creates a target identifier from container details +func (e *TargetExtractor) deriveTargetName(container *ContainerInfo) string { + // Use host-container-name format for uniqueness + if container.Host != "" && container.Name != "" { + return fmt.Sprintf("%s-%s", container.Host, strings.ReplaceAll(container.Name, "/", "-")) + } + if container.Name != "" { + return strings.ReplaceAll(container.Name, "/", "-") + } + if container.Image != "" { + // Extract image name (remove tag and registry) + image := container.Image + if idx := strings.LastIndex(image, "/"); idx >= 0 { + image = image[idx+1:] + } + if idx := strings.Index(image, ":"); idx >= 0 { + image = image[:idx] + } + return image + } + // Fallback to container ID (short) + if len(container.ID) > 12 { + return container.ID[:12] + } + return container.ID +} + +// deriveScopedTargetName creates a scoped target identifier from container details and a raw target name +func (e *TargetExtractor) deriveScopedTargetName(container *ContainerInfo, rawTargetName string) string { + // Build base identifier from container (same logic as deriveTargetName) + var base string + if container.Host != "" && container.Name != "" { + base = fmt.Sprintf("%s-%s", container.Host, strings.ReplaceAll(container.Name, "/", "-")) + } else if container.Name != "" { + base = strings.ReplaceAll(container.Name, "/", "-") + } else if container.Image != "" { + // Extract image name (remove tag and registry) + image := container.Image + if idx := strings.LastIndex(image, "/"); idx >= 0 { + image = image[idx+1:] + } + if idx := strings.Index(image, ":"); idx >= 0 { + image = image[:idx] + } + base = image + } else { + // Fallback to container ID (short) + if len(container.ID) > 12 { + base = container.ID[:12] + } else { + base = container.ID + } + } + + // Append the raw target name: {base}-{rawTargetName} + return fmt.Sprintf("%s-%s", base, rawTargetName) +} + +// extractTargetConfig extracts configuration for a specific target +func (e *TargetExtractor) extractTargetConfig(container *ContainerInfo, targetName string, isNamed bool) *TargetConfig { + var basePath string + var finalTargetName string + + if isNamed { + // For named targets, use raw name for label lookup but derive scoped name + basePath = fmt.Sprintf("%s.targets.%s", e.prefix, targetName) + finalTargetName = e.deriveScopedTargetName(container, targetName) + } else { + // Shorthand: hermes.prometheus.* + basePath = e.prefix + finalTargetName = targetName // Already derived in ExtractTargets + } + + config := &TargetConfig{ + Name: finalTargetName, // Use scoped name + Scheme: "http", + Path: "/metrics", + Labels: make(map[string]string), + } + + // Check if scraping is enabled + scrapeKey := basePath + ".prometheus.scrape" + if value, exists := container.Labels[scrapeKey]; !exists || value != "true" { + return nil // Not enabled + } + config.Scrape = true + + // Extract port (same pattern for both named and shorthand) + portKey := basePath + ".prometheus.port" + if portStr, exists := container.Labels[portKey]; exists { + if port, err := strconv.Atoi(portStr); err == nil { + config.Port = port + } + } + + // Extract path + pathKey := basePath + ".prometheus.path" + if path, exists := container.Labels[pathKey]; exists { + config.Path = path + } + + // Extract scheme + schemeKey := basePath + ".prometheus.scheme" + if scheme, exists := container.Labels[schemeKey]; exists { + config.Scheme = scheme + } + + // Extract Hermes-specific overrides (only for named targets) + if isNamed { + // Strategy override + strategyKey := basePath + ".strategy" + if strategy, exists := container.Labels[strategyKey]; exists { + config.Strategy = TargetResolutionStrategy(strategy) + } + + // Host override + hostKey := basePath + ".host" + if host, exists := container.Labels[hostKey]; exists { + config.Host = host + } + + // Port override (for resolution, different from prometheus port) + resolutionPortKey := basePath + ".port" + if portStr, exists := container.Labels[resolutionPortKey]; exists { + if port, err := strconv.Atoi(portStr); err == nil { + config.Port = port // Override prometheus port + } + } + } + + // If port not set, use first exposed port + if config.Port == 0 { + if len(container.Ports) > 0 { + config.Port = container.Ports[0].PrivatePort + } else { + return nil // No port available + } + } + + return config +} + +// ResolveTarget resolves a target configuration to a ResolvedTarget +func (c *TargetConfig) ResolveTarget(container *ContainerInfo, defaultStrategy TargetResolutionStrategy, agentHost, customHost string) *ResolvedTarget { + if !c.Scrape { + return nil + } + + if c.Port == 0 { + return nil + } + + // Determine strategy + strategy := c.Strategy + if strategy == "" { + strategy = defaultStrategy + } + + // Resolve host + host := c.resolveHost(container, strategy, agentHost, customHost) + if host == "" { + return nil + } + + // Adjust port for host_port strategy + port := c.Port + if strategy == StrategyHostPort { + if publishedPort := container.getPublishedPort(port); publishedPort > 0 { + port = publishedPort + } else { + return nil // No published port available + } + } + + // Build labels + labels := make(map[string]string) + labels["__meta_docker_container_id"] = container.ID + labels["__meta_docker_container_name"] = container.Name + labels["__meta_docker_container_image"] = container.Image + labels["__meta_docker_container_status"] = container.Status + labels["__meta_docker_host"] = container.Host + labels["__meta_target_name"] = c.Name + + // Add target-specific labels + for key, value := range c.Labels { + labels[key] = value + } + + // Copy non-hermes labels from container + if container.LabelsConfig != nil { + prefix := container.LabelsConfig.Prefix + "." + for key, value := range container.Labels { + if !strings.HasPrefix(key, prefix) { + labels[key] = value + } + } + } else { + // Fallback: copy all labels if no config + for key, value := range container.Labels { + labels[key] = value + } + } + + return &ResolvedTarget{ + Address: fmt.Sprintf("%s:%d", host, port), + Scheme: c.Scheme, + Path: c.Path, + Labels: labels, + } +} + +// resolveHost resolves the host based on strategy +func (c *TargetConfig) resolveHost(container *ContainerInfo, strategy TargetResolutionStrategy, agentHost, customHost string) string { + // Check for explicit host override + if c.Host != "" { + return c.Host + } + + // Resolve based on strategy + switch strategy { + case StrategyHostname: + return container.Name + case StrategyContainerIP: + return container.GetPrimaryIP() + case StrategyHostPort: + if customHost != "" { + return customHost + } + return agentHost + case StrategyCustom: + return customHost + default: + return container.Name + } +} + +// getPublishedPort finds the published port for a private port +func (c *ContainerInfo) getPublishedPort(privatePort int) int { + for _, port := range c.Ports { + if port.PrivatePort == privatePort && port.PublicPort > 0 { + return port.PublicPort + } + } + return 0 +} diff --git a/docker-hermes/types/targets_test.go b/docker-hermes/types/targets_test.go new file mode 100644 index 0000000..dcd1f88 --- /dev/null +++ b/docker-hermes/types/targets_test.go @@ -0,0 +1,825 @@ +package types + +import ( + "strings" + "testing" +) + +// createTestContainer creates a ContainerInfo with common test data +func createTestContainer() *ContainerInfo { + return &ContainerInfo{ + ID: "abc123def456", + Name: "/test-container", + Host: "host1", + Image: "nginx:latest", + Status: "running", + Labels: make(map[string]string), + Ports: []PortInfo{{PrivatePort: 8080, PublicPort: 8080, Type: "tcp"}}, + NetworkSettings: NetworkSettings{ + IPAddress: "172.17.0.2", + Networks: map[string]Network{ + "bridge": { + IPAddress: "172.17.0.2", + NetworkName: "bridge", + }, + }, + }, + LabelsConfig: &LabelsConfig{ + Prefix: "hermes", + }, + } +} + +func TestNewTargetExtractor(t *testing.T) { + tests := []struct { + name string + prefix string + expected string + }{ + {"default prefix", "", "hermes"}, + {"custom prefix", "custom", "custom"}, + {"explicit hermes", "hermes", "hermes"}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + extractor := NewTargetExtractor(tt.prefix) + if extractor.prefix != tt.expected { + t.Errorf("expected prefix %q, got %q", tt.expected, extractor.prefix) + } + }) + } +} + +func TestExtractTargets_Shorthand(t *testing.T) { + tests := []struct { + name string + labels map[string]string + expected int + validate func(*testing.T, []TargetConfig) + }{ + { + name: "minimal shorthand", + labels: map[string]string{ + "hermes.prometheus.scrape": "true", + "hermes.prometheus.port": "9090", + }, + expected: 1, + validate: func(t *testing.T, targets []TargetConfig) { + if len(targets) != 1 { + t.Fatalf("expected 1 target, got %d", len(targets)) + } + target := targets[0] + if target.Port != 9090 { + t.Errorf("expected port 9090, got %d", target.Port) + } + if target.Scheme != "http" { + t.Errorf("expected default scheme http, got %q", target.Scheme) + } + if target.Path != "/metrics" { + t.Errorf("expected default path /metrics, got %q", target.Path) + } + if !target.Scrape { + t.Error("expected scrape to be true") + } + }, + }, + { + name: "shorthand with all options", + labels: map[string]string{ + "hermes.prometheus.scrape": "true", + "hermes.prometheus.port": "8080", + "hermes.prometheus.path": "/custom/metrics", + "hermes.prometheus.scheme": "https", + }, + expected: 1, + validate: func(t *testing.T, targets []TargetConfig) { + target := targets[0] + if target.Port != 8080 { + t.Errorf("expected port 8080, got %d", target.Port) + } + if target.Scheme != "https" { + t.Errorf("expected scheme https, got %q", target.Scheme) + } + if target.Path != "/custom/metrics" { + t.Errorf("expected path /custom/metrics, got %q", target.Path) + } + }, + }, + { + name: "shorthand with port from container", + labels: map[string]string{ + "hermes.prometheus.scrape": "true", + // No port specified, should use first container port + }, + expected: 1, + validate: func(t *testing.T, targets []TargetConfig) { + target := targets[0] + if target.Port != 8080 { + t.Errorf("expected port 8080 from container, got %d", target.Port) + } + }, + }, + { + name: "shorthand disabled", + labels: map[string]string{ + "hermes.prometheus.scrape": "false", + "hermes.prometheus.port": "9090", + }, + expected: 0, + }, + { + name: "no shorthand labels", + labels: map[string]string{}, + expected: 0, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + container := createTestContainer() + container.Labels = tt.labels + + extractor := NewTargetExtractor("hermes") + targets := extractor.ExtractTargets(container) + + if len(targets) != tt.expected { + t.Fatalf("expected %d targets, got %d", tt.expected, len(targets)) + } + + if tt.validate != nil { + tt.validate(t, targets) + } + }) + } +} + +func TestExtractTargets_NamedTargets(t *testing.T) { + tests := []struct { + name string + labels map[string]string + expected int + validate func(*testing.T, []TargetConfig) + }{ + { + name: "single named target", + labels: map[string]string{ + "hermes.targets.admin.prometheus.scrape": "true", + "hermes.targets.admin.prometheus.port": "9090", + }, + expected: 1, + validate: func(t *testing.T, targets []TargetConfig) { + target := targets[0] + // Name should be scoped: host1--test-container-admin + if !strings.HasSuffix(target.Name, "-admin") { + t.Errorf("expected name to end with '-admin', got %q", target.Name) + } + if !strings.Contains(target.Name, "host1") { + t.Errorf("expected name to contain 'host1', got %q", target.Name) + } + if target.Port != 9090 { + t.Errorf("expected port 9090, got %d", target.Port) + } + }, + }, + { + name: "multiple named targets", + labels: map[string]string{ + "hermes.targets.admin.prometheus.scrape": "true", + "hermes.targets.admin.prometheus.port": "9090", + "hermes.targets.metrics.prometheus.scrape": "true", + "hermes.targets.metrics.prometheus.port": "8080", + }, + expected: 2, + validate: func(t *testing.T, targets []TargetConfig) { + hasAdmin := false + hasMetrics := false + for _, target := range targets { + if strings.HasSuffix(target.Name, "-admin") { + hasAdmin = true + if target.Port != 9090 { + t.Errorf("admin target expected port 9090, got %d", target.Port) + } + } + if strings.HasSuffix(target.Name, "-metrics") { + hasMetrics = true + if target.Port != 8080 { + t.Errorf("metrics target expected port 8080, got %d", target.Port) + } + } + } + if !hasAdmin { + t.Error("expected 'admin' target (scoped)") + } + if !hasMetrics { + t.Error("expected 'metrics' target (scoped)") + } + }, + }, + { + name: "named target with strategy override", + labels: map[string]string{ + "hermes.targets.app.prometheus.scrape": "true", + "hermes.targets.app.prometheus.port": "8080", + "hermes.targets.app.strategy": "container_ip", + }, + expected: 1, + validate: func(t *testing.T, targets []TargetConfig) { + target := targets[0] + if !strings.HasSuffix(target.Name, "-app") { + t.Errorf("expected name to end with '-app', got %q", target.Name) + } + if target.Strategy != StrategyContainerIP { + t.Errorf("expected strategy container_ip, got %q", target.Strategy) + } + }, + }, + { + name: "named target with host override", + labels: map[string]string{ + "hermes.targets.app.prometheus.scrape": "true", + "hermes.targets.app.prometheus.port": "8080", + "hermes.targets.app.host": "custom.example.com", + }, + expected: 1, + validate: func(t *testing.T, targets []TargetConfig) { + target := targets[0] + if !strings.HasSuffix(target.Name, "-app") { + t.Errorf("expected name to end with '-app', got %q", target.Name) + } + if target.Host != "custom.example.com" { + t.Errorf("expected host custom.example.com, got %q", target.Host) + } + }, + }, + { + name: "named target with resolution port override", + labels: map[string]string{ + "hermes.targets.app.prometheus.scrape": "true", + "hermes.targets.app.prometheus.port": "8080", + "hermes.targets.app.port": "10000", // Overrides prometheus port + }, + expected: 1, + validate: func(t *testing.T, targets []TargetConfig) { + target := targets[0] + if !strings.HasSuffix(target.Name, "-app") { + t.Errorf("expected name to end with '-app', got %q", target.Name) + } + if target.Port != 10000 { + t.Errorf("expected port 10000 (overridden), got %d", target.Port) + } + }, + }, + { + name: "named target disabled", + labels: map[string]string{ + "hermes.targets.admin.prometheus.scrape": "false", + "hermes.targets.admin.prometheus.port": "9090", + }, + expected: 0, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + container := createTestContainer() + container.Labels = tt.labels + + extractor := NewTargetExtractor("hermes") + targets := extractor.ExtractTargets(container) + + if len(targets) != tt.expected { + t.Fatalf("expected %d targets, got %d", tt.expected, len(targets)) + } + + if tt.validate != nil { + tt.validate(t, targets) + } + }) + } +} + +func TestExtractTargets_ShorthandAndNamed(t *testing.T) { + container := createTestContainer() + container.Labels = map[string]string{ + // Shorthand target + "hermes.prometheus.scrape": "true", + "hermes.prometheus.port": "8080", + // Named target + "hermes.targets.admin.prometheus.scrape": "true", + "hermes.targets.admin.prometheus.port": "9090", + } + + extractor := NewTargetExtractor("hermes") + targets := extractor.ExtractTargets(container) + + if len(targets) != 2 { + t.Fatalf("expected 2 targets (shorthand + named), got %d", len(targets)) + } + + // Check shorthand target name is derived + hasShorthand := false + hasAdmin := false + for _, target := range targets { + // Container name is "/test-container", so derived name will be "host1--test-container" + if target.Name == "host1--test-container" || target.Name == "host1-test-container" { + hasShorthand = true + if target.Port != 8080 { + t.Errorf("shorthand target expected port 8080, got %d", target.Port) + } + } + // Named target should be scoped: host1--test-container-admin + if strings.HasSuffix(target.Name, "-admin") { + hasAdmin = true + if target.Port != 9090 { + t.Errorf("admin target expected port 9090, got %d", target.Port) + } + } + } + + if !hasShorthand { + t.Errorf("expected shorthand target with derived name, got targets: %v", targets) + } + if !hasAdmin { + t.Error("expected named 'admin' target (scoped)") + } +} + +func TestDeriveTargetName(t *testing.T) { + extractor := NewTargetExtractor("hermes") + + tests := []struct { + name string + container *ContainerInfo + expected string + }{ + { + name: "host and name", + container: &ContainerInfo{ + Host: "host1", + Name: "/my-container", + }, + expected: "host1--my-container", // Leading / becomes - + }, + { + name: "name only", + container: &ContainerInfo{ + Name: "/my-container", + }, + expected: "-my-container", // Leading / becomes - + }, + { + name: "name with slashes", + container: &ContainerInfo{ + Host: "host1", + Name: "/project/my-container", + }, + expected: "host1--project-my-container", // Leading / becomes - + }, + { + name: "image fallback", + container: &ContainerInfo{ + Image: "registry.example.com/nginx:latest", + }, + expected: "nginx", + }, + { + name: "image with tag", + container: &ContainerInfo{ + Image: "nginx:1.21", + }, + expected: "nginx", + }, + { + name: "container ID fallback", + container: &ContainerInfo{ + ID: "abcdef1234567890", + }, + expected: "abcdef123456", + }, + { + name: "short container ID", + container: &ContainerInfo{ + ID: "abc123", + }, + expected: "abc123", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + derived := extractor.deriveTargetName(tt.container) + if derived != tt.expected { + t.Errorf("expected %q, got %q", tt.expected, derived) + } + }) + } +} + +func TestDeriveScopedTargetName(t *testing.T) { + extractor := NewTargetExtractor("hermes") + + tests := []struct { + name string + container *ContainerInfo + rawName string + expected string + }{ + { + name: "host and name with raw target", + container: &ContainerInfo{ + Host: "host1", + Name: "/my-container", + }, + rawName: "admin", + expected: "host1--my-container-admin", + }, + { + name: "name only with raw target", + container: &ContainerInfo{ + Name: "/my-container", + }, + rawName: "metrics", + expected: "-my-container-metrics", + }, + { + name: "image fallback with raw target", + container: &ContainerInfo{ + Image: "nginx:latest", + }, + rawName: "health", + expected: "nginx-health", + }, + { + name: "container ID fallback with raw target", + container: &ContainerInfo{ + ID: "abcdef1234567890", + }, + rawName: "app", + expected: "abcdef123456-app", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + derived := extractor.deriveScopedTargetName(tt.container, tt.rawName) + if derived != tt.expected { + t.Errorf("expected %q, got %q", tt.expected, derived) + } + }) + } +} + +func TestResolveTarget_Defaults(t *testing.T) { + container := createTestContainer() + container.Labels = map[string]string{ + "hermes.prometheus.scrape": "true", + "hermes.prometheus.port": "8080", + } + + extractor := NewTargetExtractor("hermes") + targets := extractor.ExtractTargets(container) + + if len(targets) != 1 { + t.Fatalf("expected 1 target, got %d", len(targets)) + } + + target := targets[0] + resolved := target.ResolveTarget(container, StrategyHostname, "agent-host", "custom-host") + + if resolved == nil { + t.Fatal("expected resolved target, got nil") + } + + // Check defaults + if resolved.Scheme != "http" { + t.Errorf("expected default scheme http, got %q", resolved.Scheme) + } + if resolved.Path != "/metrics" { + t.Errorf("expected default path /metrics, got %q", resolved.Path) + } + // Container name includes leading /, so hostname strategy returns "/test-container" + if resolved.Address != "/test-container:8080" { + t.Errorf("expected address /test-container:8080, got %q", resolved.Address) + } + + // Check metadata labels + if resolved.Labels["__meta_docker_container_id"] != container.ID { + t.Error("missing container ID in labels") + } + if resolved.Labels["__meta_docker_container_name"] != container.Name { + t.Error("missing container name in labels") + } + if resolved.Labels["__meta_target_name"] != target.Name { + t.Error("missing target name in labels") + } +} + +func TestResolveTarget_HostResolution(t *testing.T) { + container := createTestContainer() + container.Labels = map[string]string{ + "hermes.prometheus.scrape": "true", + "hermes.prometheus.port": "8080", + } + + extractor := NewTargetExtractor("hermes") + targets := extractor.ExtractTargets(container) + + if len(targets) != 1 { + t.Fatalf("expected 1 target, got %d", len(targets)) + } + + target := targets[0] + + tests := []struct { + name string + strategy TargetResolutionStrategy + agentHost string + customHost string + expectedHost string + }{ + { + name: "hostname strategy", + strategy: StrategyHostname, + agentHost: "agent1", + customHost: "", + expectedHost: "/test-container", // Container name includes leading / + }, + { + name: "container_ip strategy", + strategy: StrategyContainerIP, + agentHost: "agent1", + customHost: "", + expectedHost: "172.17.0.2", + }, + { + name: "host_port strategy", + strategy: StrategyHostPort, + agentHost: "agent1", + customHost: "", + expectedHost: "agent1", + }, + { + name: "host_port with custom host", + strategy: StrategyHostPort, + agentHost: "agent1", + customHost: "external.example.com", + expectedHost: "external.example.com", + }, + { + name: "custom strategy", + strategy: StrategyCustom, + agentHost: "agent1", + customHost: "custom.example.com", + expectedHost: "custom.example.com", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + resolved := target.ResolveTarget(container, tt.strategy, tt.agentHost, tt.customHost) + if resolved == nil { + t.Fatal("expected resolved target, got nil") + } + + expectedAddr := tt.expectedHost + ":8080" + if resolved.Address != expectedAddr { + t.Errorf("expected address %q, got %q", expectedAddr, resolved.Address) + } + }) + } +} + +func TestResolveTarget_HostOverride(t *testing.T) { + container := createTestContainer() + container.Labels = map[string]string{ + "hermes.targets.app.prometheus.scrape": "true", + "hermes.targets.app.prometheus.port": "8080", + "hermes.targets.app.host": "override.example.com", + } + + extractor := NewTargetExtractor("hermes") + targets := extractor.ExtractTargets(container) + + if len(targets) != 1 { + t.Fatalf("expected 1 target, got %d", len(targets)) + } + + target := targets[0] + resolved := target.ResolveTarget(container, StrategyContainerIP, "agent1", "custom") + + if resolved == nil { + t.Fatal("expected resolved target, got nil") + } + + // Host override should take precedence over strategy + if resolved.Address != "override.example.com:8080" { + t.Errorf("expected address override.example.com:8080, got %q", resolved.Address) + } +} + +func TestResolveTarget_HostPortStrategy(t *testing.T) { + container := createTestContainer() + container.Ports = []PortInfo{ + {PrivatePort: 8080, PublicPort: 18080, Type: "tcp"}, + {PrivatePort: 9090, PublicPort: 19090, Type: "tcp"}, + } + container.Labels = map[string]string{ + "hermes.targets.app.prometheus.scrape": "true", + "hermes.targets.app.prometheus.port": "8080", + "hermes.targets.app.strategy": "host_port", + } + + extractor := NewTargetExtractor("hermes") + targets := extractor.ExtractTargets(container) + + if len(targets) != 1 { + t.Fatalf("expected 1 target, got %d", len(targets)) + } + + target := targets[0] + resolved := target.ResolveTarget(container, StrategyHostname, "agent1", "") + + if resolved == nil { + t.Fatal("expected resolved target, got nil") + } + + // Should use published port (18080) instead of private port (8080) + if resolved.Address != "agent1:18080" { + t.Errorf("expected address agent1:18080 (published port), got %q", resolved.Address) + } +} + +func TestResolveTarget_NoPortAvailable(t *testing.T) { + container := createTestContainer() + container.Ports = []PortInfo{} // No ports + container.Labels = map[string]string{ + "hermes.prometheus.scrape": "true", + // No port specified + } + + extractor := NewTargetExtractor("hermes") + targets := extractor.ExtractTargets(container) + + // Should return no targets when no port is available + if len(targets) != 0 { + t.Fatalf("expected 0 targets (no port), got %d", len(targets)) + } +} + +func TestResolveTarget_ScrapeDisabled(t *testing.T) { + container := createTestContainer() + container.Labels = map[string]string{ + "hermes.prometheus.scrape": "true", + "hermes.prometheus.port": "8080", + } + + extractor := NewTargetExtractor("hermes") + targets := extractor.ExtractTargets(container) + + if len(targets) != 1 { + t.Fatalf("expected 1 target, got %d", len(targets)) + } + + target := targets[0] + target.Scrape = false // Disable scraping + + resolved := target.ResolveTarget(container, StrategyHostname, "agent1", "") + if resolved != nil { + t.Error("expected nil when scrape is disabled") + } +} + +func TestResolveTarget_LabelFiltering(t *testing.T) { + container := createTestContainer() + container.Labels = map[string]string{ + "hermes.prometheus.scrape": "true", + "hermes.prometheus.port": "8080", + "app": "myapp", + "version": "1.0", + "hermes.internal": "should-be-filtered", + } + + extractor := NewTargetExtractor("hermes") + targets := extractor.ExtractTargets(container) + + if len(targets) != 1 { + t.Fatalf("expected 1 target, got %d", len(targets)) + } + + target := targets[0] + resolved := target.ResolveTarget(container, StrategyHostname, "agent1", "") + + if resolved == nil { + t.Fatal("expected resolved target, got nil") + } + + // Non-hermes labels should be included + if resolved.Labels["app"] != "myapp" { + t.Error("expected 'app' label to be included") + } + if resolved.Labels["version"] != "1.0" { + t.Error("expected 'version' label to be included") + } + + // Hermes labels should be filtered out + if resolved.Labels["hermes.internal"] != "" { + t.Error("expected 'hermes.internal' label to be filtered out") + } +} + +func TestResolveTarget_CustomPrefix(t *testing.T) { + container := createTestContainer() + container.LabelsConfig.Prefix = "custom" + container.Labels = map[string]string{ + "custom.prometheus.scrape": "true", + "custom.prometheus.port": "8080", + "app": "myapp", + "custom.internal": "should-be-filtered", + } + + extractor := NewTargetExtractor("custom") + targets := extractor.ExtractTargets(container) + + if len(targets) != 1 { + t.Fatalf("expected 1 target, got %d", len(targets)) + } + + target := targets[0] + resolved := target.ResolveTarget(container, StrategyHostname, "agent1", "") + + if resolved == nil { + t.Fatal("expected resolved target, got nil") + } + + // Non-custom labels should be included + if resolved.Labels["app"] != "myapp" { + t.Error("expected 'app' label to be included") + } + + // Custom labels should be filtered out + if resolved.Labels["custom.internal"] != "" { + t.Error("expected 'custom.internal' label to be filtered out") + } +} + +func TestGetPublishedPort(t *testing.T) { + container := createTestContainer() + container.Ports = []PortInfo{ + {PrivatePort: 8080, PublicPort: 18080, Type: "tcp"}, + {PrivatePort: 9090, PublicPort: 0, Type: "tcp"}, // Not published + {PrivatePort: 3000, PublicPort: 3000, Type: "tcp"}, + } + + tests := []struct { + name string + privatePort int + expectedPort int + }{ + {"published port", 8080, 18080}, + {"unpublished port", 9090, 0}, + {"same port", 3000, 3000}, + {"non-existent port", 5000, 0}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := container.getPublishedPort(tt.privatePort) + if result != tt.expectedPort { + t.Errorf("expected published port %d, got %d", tt.expectedPort, result) + } + }) + } +} + +func TestResolveTarget_NoLabelsConfig(t *testing.T) { + container := createTestContainer() + container.LabelsConfig = nil // No labels config + container.Labels = map[string]string{ + "hermes.prometheus.scrape": "true", + "hermes.prometheus.port": "8080", + "app": "myapp", + } + + extractor := NewTargetExtractor("hermes") + targets := extractor.ExtractTargets(container) + + if len(targets) != 1 { + t.Fatalf("expected 1 target, got %d", len(targets)) + } + + target := targets[0] + resolved := target.ResolveTarget(container, StrategyHostname, "agent1", "") + + if resolved == nil { + t.Fatal("expected resolved target, got nil") + } + + // Without labels config, all labels should be included (fallback behavior) + if resolved.Labels["app"] != "myapp" { + t.Error("expected 'app' label to be included (fallback)") + } + if resolved.Labels["hermes.prometheus.scrape"] != "true" { + t.Error("expected hermes labels to be included (fallback)") + } +} diff --git a/docker-hermes/types/types.go b/docker-hermes/types/types.go new file mode 100644 index 0000000..9e86639 --- /dev/null +++ b/docker-hermes/types/types.go @@ -0,0 +1,107 @@ +package types + +import ( + "time" +) + +// NetworkSettings represents network information for a container +type NetworkSettings struct { + IPAddress string `json:"ip_address"` // Primary IP + Networks map[string]Network `json:"networks"` // All networks +} + +// Network represents a single network configuration +type Network struct { + IPAddress string `json:"ip_address"` + Gateway string `json:"gateway"` + NetworkID string `json:"network_id"` + NetworkName string `json:"network_name"` +} + +// ContainerInfo represents information about a Docker container +type ContainerInfo struct { + ID string `json:"id"` + Name string `json:"name"` + Host string `json:"host"` + Labels map[string]string `json:"labels"` + Ports []PortInfo `json:"ports"` + State string `json:"state"` + LastSeen time.Time `json:"last_seen"` + CreatedAt time.Time `json:"created_at"` + Image string `json:"image"` + Command string `json:"command"` + Status string `json:"status"` + + // Network information for resolution + NetworkSettings NetworkSettings `json:"network_settings"` + + // Labels configuration reference (not serialized to JSON) + LabelsConfig *LabelsConfig `json:"-"` +} + +// FilterLabelsByPrefix filters labels by the given prefix +func (c *ContainerInfo) FilterLabelsByPrefix(prefix string) map[string]string { + if prefix == "" { + return c.Labels + } + + filtered := make(map[string]string) + for key, value := range c.Labels { + if len(key) >= len(prefix) && key[:len(prefix)] == prefix { + filtered[key] = value + } + } + return filtered +} + +// GetPrimaryIP returns the primary IP address +func (c *ContainerInfo) GetPrimaryIP() string { + if c.NetworkSettings.IPAddress != "" { + return c.NetworkSettings.IPAddress + } + + // Fall back to first network IP + for _, network := range c.NetworkSettings.Networks { + if network.IPAddress != "" { + return network.IPAddress + } + } + + return "" +} + + +// PortInfo represents port information for a container +type PortInfo struct { + PrivatePort int `json:"private_port"` + PublicPort int `json:"public_port"` + Type string `json:"type"` + IP string `json:"ip"` +} + +// PrometheusTarget represents a Prometheus scrape target +type PrometheusTarget struct { + Targets []string `json:"targets"` + Labels map[string]string `json:"labels"` +} + +// PrometheusSDResponse represents the Prometheus HTTP SD response format +type PrometheusSDResponse []PrometheusTarget + +// ContainerQuery represents a query for containers by labels +type ContainerQuery struct { + Labels map[string]string `json:"labels"` + Host string `json:"host,omitempty"` +} + +// LabelInfo represents information about a label key and its values +type LabelInfo struct { + Key string `json:"key"` + Values []string `json:"values"` +} + +// ApiContext represents the API context for handlers +type ApiContext struct { + Config *Config + RedisClient StorageClient +} diff --git a/email-archiver/go.mod b/email-archiver/go.mod index 31c68e7..eef26e7 100644 --- a/email-archiver/go.mod +++ b/email-archiver/go.mod @@ -1,22 +1,20 @@ module github.com/pirogoeth/apps/email-archiver -go 1.23 +go 1.24.1 replace github.com/pirogoeth/apps => ../ require ( github.com/blevesearch/bleve v1.0.14 github.com/emersion/go-imap/v2 v2.0.0-beta.3 - github.com/gin-gonic/gin v1.10.0 - github.com/pirogoeth/apps v0.0.0-20240504031750-d466f362ff13 + github.com/gin-gonic/gin v1.10.1 github.com/sirupsen/logrus v1.9.3 - github.com/spf13/cobra v1.8.1 - golang.org/x/sync v0.7.0 + github.com/spf13/cobra v1.9.1 + golang.org/x/sync v0.16.0 ) require ( github.com/RoaringBitmap/roaring v0.4.23 // indirect - github.com/beorn7/perks v1.0.1 // indirect github.com/blevesearch/go-porterstemmer v1.0.3 // indirect github.com/blevesearch/mmap-go v1.0.2 // indirect github.com/blevesearch/segment v0.9.0 // indirect @@ -26,64 +24,50 @@ require ( github.com/blevesearch/zap/v13 v13.0.6 // indirect github.com/blevesearch/zap/v14 v14.0.5 // indirect github.com/blevesearch/zap/v15 v15.0.3 // indirect - github.com/bytedance/sonic v1.11.6 // indirect - github.com/bytedance/sonic/loader v0.1.1 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/cloudwego/base64x v0.1.4 // indirect - github.com/cloudwego/iasm v0.2.0 // indirect + github.com/bytedance/sonic v1.13.2 // indirect + github.com/bytedance/sonic/loader v0.2.4 // indirect + github.com/cloudwego/base64x v0.1.5 // indirect github.com/couchbase/vellum v1.0.2 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/emersion/go-message v0.18.1 // indirect github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 // indirect - github.com/gabriel-vasile/mimetype v1.4.3 // indirect - github.com/ghodss/yaml v1.0.0 // indirect - github.com/gin-contrib/sse v0.1.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.9 // indirect + github.com/gin-contrib/sse v1.1.0 // indirect github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.20.0 // indirect - github.com/goccy/go-json v0.10.2 // indirect - github.com/golang/protobuf v1.5.3 // indirect + github.com/go-playground/validator/v10 v10.26.0 // indirect + github.com/goccy/go-json v0.10.5 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect - github.com/google/go-github/v58 v58.0.0 // indirect - github.com/google/go-querystring v1.1.0 // indirect - github.com/gorilla/websocket v1.5.0 // indirect - github.com/hashicorp/cronexpr v1.1.2 // indirect - github.com/hashicorp/errwrap v1.1.0 // indirect - github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/hashicorp/go-rootcerts v1.0.2 // indirect - github.com/hashicorp/nomad/api v0.0.0-20231227080007-76ba3e10e73d // indirect + github.com/google/go-cmp v0.7.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/kelseyhightower/envconfig v1.4.0 // indirect - github.com/klauspost/cpuid/v2 v2.2.7 // indirect + github.com/klauspost/cpuid/v2 v2.2.10 // indirect + github.com/kr/pretty v0.3.1 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mitchellh/go-homedir v1.1.0 // 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/mschoch/smat v0.2.0 // indirect - github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/philhofer/fwd v1.0.0 // indirect - github.com/prometheus/client_golang v1.19.1 // indirect - github.com/prometheus/client_model v0.5.0 // indirect - github.com/prometheus/common v0.48.0 // indirect - github.com/prometheus/procfs v0.12.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect + github.com/spf13/pflag v1.0.6 // indirect github.com/steveyen/gtreap v0.1.0 // indirect + github.com/stretchr/testify v1.11.1 // indirect github.com/tinylib/msgp v1.1.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect github.com/willf/bitset v1.1.10 // indirect go.etcd.io/bbolt v1.3.7 // indirect - golang.org/x/arch v0.8.0 // indirect - golang.org/x/crypto v0.24.0 // indirect - golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect - golang.org/x/net v0.26.0 // indirect - golang.org/x/sys v0.21.0 // indirect - golang.org/x/text v0.16.0 // indirect - google.golang.org/protobuf v1.34.2 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect + golang.org/x/arch v0.17.0 // indirect + golang.org/x/crypto v0.41.0 // indirect + golang.org/x/net v0.43.0 // indirect + golang.org/x/sys v0.35.0 // indirect + golang.org/x/text v0.28.0 // indirect + google.golang.org/protobuf v1.36.8 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/email-archiver/go.sum b/email-archiver/go.sum index 9952424..d19a69e 100644 --- a/email-archiver/go.sum +++ b/email-archiver/go.sum @@ -2,8 +2,6 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/RoaringBitmap/roaring v0.4.23 h1:gpyfd12QohbqhFO4NVDUdoPOCXsyahYRQhINmlHxKeo= github.com/RoaringBitmap/roaring v0.4.23/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06Mq5mKs52e1TwOo= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -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/blevesearch/bleve v1.0.14 h1:Q8r+fHTt35jtGXJUM0ULwM3Tzg+MRfyai4ZkWDy2xO4= github.com/blevesearch/bleve v1.0.14/go.mod h1:e/LJTr+E7EaoVdkQZTfoz7dt4KoDNvDbLb8MSKuNTLQ= github.com/blevesearch/blevex v1.0.0 h1:pnilj2Qi3YSEGdWgLj1Pn9Io7ukfXPoQcpAI1Bv8n/o= @@ -27,15 +25,10 @@ github.com/blevesearch/zap/v14 v14.0.5 h1:NdcT+81Nvmp2zL+NhwSvGSLh7xNgGL8QRVZ67n github.com/blevesearch/zap/v14 v14.0.5/go.mod h1:bWe8S7tRrSBTIaZ6cLRbgNH4TUDaC9LZSpRGs85AsGY= github.com/blevesearch/zap/v15 v15.0.3 h1:Ylj8Oe+mo0P25tr9iLPp33lN6d4qcztGjaIsP51UxaY= github.com/blevesearch/zap/v15 v15.0.3/go.mod h1:iuwQrImsh1WjWJ0Ue2kBqY83a0rFtJTqfa9fp1rbVVU= -github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= -github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= -github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= +github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ= github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= -github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= -github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= +github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY= +github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4= github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= @@ -45,16 +38,13 @@ github.com/couchbase/moss v0.1.0/go.mod h1:9MaHIaRuy9pvLPUJxB8sh8OrLfyDczECVL37g github.com/couchbase/vellum v1.0.2 h1:BrbP0NKiyDdndMPec8Jjhy0U47CZ0Lgx3xUC2r9rZqw= github.com/couchbase/vellum v1.0.2/go.mod h1:FcwrEivFpNi24R3jLOs3n+fs5RnuQnQqCLBJ1uAg1W4= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= -github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d h1:SwD98825d6bdB+pEuTxWOXiSjBrHdOl/UVp75eI7JT8= github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d/go.mod h1:URriBxXwVq5ijiJ12C7iIZqlA69nTlI+LgI6/pwftG8= github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM= github.com/cznic/strutil v0.0.0-20181122101858-275e90344537/go.mod h1:AHHPPPXTw0h6pVabbcbyGRK1DckRn7r/STdZEeIDzZc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/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/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/emersion/go-imap/v2 v2.0.0-beta.3 h1:z0TLMfYnDsFupXLhzRXgOzXenD3uPvNniQSu5fN1teg= github.com/emersion/go-imap/v2 v2.0.0-beta.3/go.mod h1:BZTFHsS1hmgBkFlHqbxGLXk2hnRqTItUgwjSSCsYNAk= github.com/emersion/go-message v0.18.1 h1:tfTxIoXFSFRwWaZsgnqS1DSZuGpYGzSmCZD8SK3QA2E= @@ -64,17 +54,10 @@ github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43/go.mod h1:iL2twTe github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64= github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg= github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0= -github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= -github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= -github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= -github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= -github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= -github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= +github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY= +github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= +github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ= github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2 h1:Ujru1hufTHVb++eG6OuNDKMxZnGIvF6o/u8q/8h2+I4= github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31 h1:gclg6gY70GLy3PbkQ1AERPfmLMMagS60DKF78eWwLn8= @@ -85,46 +68,20 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= -github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= -github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= -github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k= +github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-github/v58 v58.0.0 h1:Una7GGERlF/37XfkPwpzYJe0Vp4dt2k1kCjlxwjIvzw= -github.com/google/go-github/v58 v58.0.0/go.mod h1:k4hxDKEfoWpSqFlc8LTpGd9fu2KrV1YAa6Hi6FmDNY4= -github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= -github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 h1:twflg0XRTjwKpxb/jFExr4HGq6on2dEOmnL6FV+fgPw= github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/hashicorp/cronexpr v1.1.2 h1:wG/ZYIKT+RT3QkOdgYc+xsKWVRgnxJ1OJtjjy84fJ9A= -github.com/hashicorp/cronexpr v1.1.2/go.mod h1:P4wA0KBl9C5q2hABiMO7cp6jcIg96CDh1Efb3g1PWA4= -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-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= -github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -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/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= -github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/nomad/api v0.0.0-20231227080007-76ba3e10e73d h1:Ztvo+OG+/xc6GQFiKME65qY6nveab1c7Hy8TFdBzbUI= -github.com/hashicorp/nomad/api v0.0.0-20231227080007-76ba3e10e73d/go.mod h1:ijDwa6o1uG1jFSq6kERiX2PamKGpZzTmo0XOFNeFZgw= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ikawaha/kagome.ipadic v1.1.2/go.mod h1:DPSBbU0czaJhAb/5uKQZHMc9MTVRpDugJfX+HddPHHg= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= @@ -136,11 +93,8 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= -github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= -github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= github.com/kljensen/snowball v0.6.0/go.mod h1:27N7E8fVU5H68RlUmnWwZCfxgt4POBJfENGMvNRhldw= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -152,13 +106,8 @@ github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjS github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -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/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= -github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -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/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= @@ -171,46 +120,31 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= -github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/philhofer/fwd v1.0.0 h1:UbZqGr5Y38ApvM/V/jEljVxwocdweyH+vmYvRPBnbqQ= github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= -github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= -github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= -github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= -github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= -github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= -github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= -github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/shoenig/test v1.7.0 h1:eWcHtTXa6QLnBvm0jgEabMRN/uJ4DMV3M8xUGgRkZmk= -github.com/shoenig/test v1.7.0/go.mod h1:UxJ6u/x2v/TNs/LoLxBNJRV9DiwBBKYxXSyczsBHFoI= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= -github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= +github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/steveyen/gtreap v0.1.0 h1:CjhzTa274PyJLJuMZwIzCO1PfC00oRa8d1Kc78bFXJM= github.com/steveyen/gtreap v0.1.0/go.mod h1:kl/5J7XbrOmlIbYIXdRHDDE5QxHqpk0cmkT7Z4dM9/Y= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -218,9 +152,7 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= github.com/tebeka/snowball v0.4.2/go.mod h1:4IfL14h1lvwZcp1sfXuuc7/7yCsvVffTWxWxCLfFpYg= @@ -240,16 +172,11 @@ github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5t go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ= go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= -golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= -golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= +golang.org/x/arch v0.17.0 h1:4O3dfLzd+lQewptAHqjewQZQDyEdejz3VwgeYwkZneU= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= -golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= -golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ= -golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= +golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -257,14 +184,12 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= -golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181221143128-b4a75ba826a6/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -278,8 +203,7 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -288,18 +212,13 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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= @@ -307,10 +226,7 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/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= nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/functional/.gitignore b/functional/.gitignore deleted file mode 100644 index 06b1671..0000000 --- a/functional/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -firecracker/ -storage/ -functional -functional.db diff --git a/functional/Dockerfile b/functional/Dockerfile deleted file mode 100644 index 4a19cb6..0000000 --- a/functional/Dockerfile +++ /dev/null @@ -1,27 +0,0 @@ -FROM docker.io/library/golang:alpine AS build - -ENV CGO_ENABLED=1 - -WORKDIR /src -ADD . /src -WORKDIR /src/functional -RUN mkdir /app \ - && apk add \ - build-base \ - sqlite-libs \ - && go build -o /app/functional ./ - -# --- - -FROM docker.io/library/alpine:latest - -WORKDIR /app -RUN apk add sqlite-libs -COPY --from=build /app/functional /app/functional - -VOLUME /config -VOLUME /data - -ENV GIN_MODE=release - -ENTRYPOINT ["/app/functional"] diff --git a/functional/Makefile b/functional/Makefile deleted file mode 100644 index 127096f..0000000 --- a/functional/Makefile +++ /dev/null @@ -1,73 +0,0 @@ -sqlc_generated := database/deployments.sql.go database/functions.sql.go database/invocations.sql.go database/models.go - -functional: test **/*.go $(sqlc_generated) - go build -v $(buildargs) ./ - -$(sqlc_generated): sqlc.yml database/queries/*.sql database/migrations/*.sql - sqlc vet - sqlc generate - -.PHONY: run-debug -run-debug: functional - CONFIG_TYPE=file CONFIG_FILE=./config.yml LOG_LEVEL=DEBUG DATABASE_PATH=./functional.db ./functional serve - -.PHONY: dev -dev: functional - @echo "Starting development environment..." - @echo "This will start:" - @echo " - Functional server on :8080" - @echo " - Traefik proxy (when configured)" - @echo " - Docker daemon (assumes running)" - CONFIG_TYPE=file CONFIG_FILE=./config.yml LOG_LEVEL=DEBUG DATABASE_PATH=./functional.db ./functional serve - -.PHONY: test -test: - go test -v ./... - -.PHONY: test-integration -test-integration: functional - @echo "Running integration tests with Docker..." - @echo "Ensure Docker daemon is running and accessible" - go test -v -tags=integration ./... - -.PHONY: clean -clean: - rm -f functional - rm -f $(sqlc_generated) - -.PHONY: clean-data -clean-data: - rm -f functional.db - -.PHONY: clean-all -clean-all: clean clean-data - docker system prune -f - @echo "Cleaned up Docker images and containers" - -.PHONY: setup-dev -setup-dev: - @echo "Setting up development environment..." - @echo "1. Checking Docker..." - @docker version > /dev/null 2>&1 || (echo "❌ Docker not running" && exit 1) - @echo "✅ Docker is running" - @echo "2. Checking sqlc..." - @which sqlc > /dev/null 2>&1 || (echo "❌ sqlc not found. Install with: go install github.com/sqlc-dev/sqlc/cmd/sqlc@latest" && exit 1) - @echo "✅ sqlc found" - @echo "3. Installing dependencies..." - @go mod download - @echo "✅ Setup complete!" - @echo - @echo "Next steps:" - @echo " make functional # Build the binary" - @echo " make dev # Start development server" - @echo " make test # Run tests" - -.PHONY: lint -lint: - @which golangci-lint > /dev/null 2>&1 || (echo "❌ golangci-lint not found. Install from https://golangci-lint.run/usage/install/" && exit 1) - golangci-lint run - -.PHONY: fmt -fmt: - go fmt ./... - go mod tidy diff --git a/functional/TODO.md b/functional/TODO.md deleted file mode 100644 index 8747ad7..0000000 --- a/functional/TODO.md +++ /dev/null @@ -1,75 +0,0 @@ -# TODO - -## High (MVP Focus) -- [ ] Single-process FaaS runtime with simple in-memory scheduling - - Keep it dead simple: one function = one Docker container instance - - Design clean interfaces for potential future pluggability, but don't over-engineer -- [ ] Docker-based compute provider as the primary (and initially only) runtime - - Use Docker API for all container lifecycle management - - Runtime flexibility via Docker daemon configuration (runc, kata, nvidia, etc.) - - IPAM, networking, image management all handled by Docker/containerd -- [ ] Traefik integration for proxy/routing - - Use Traefik API to dynamically register/deregister function endpoints - - Let Traefik handle load balancing, SSL termination, etc. -- [ ] Flesh out what the runtime building interface should look like - - Build from Dockerfile or import existing OCI images - - What metadata do we need? (entrypoint, env vars, resource limits, runtime selection) - - Consider: How do users specify they want Kata vs nvidia vs standard runtime? -- [ ] We need a solid test suite! - - Unit tests for core logic, integration tests with Docker containers - - Test with different Docker runtimes (runc, kata if available) -- [ ] Simple CLI tool that interfaces with the server to upload/invoke/manage functions - - Think about authentication/authorization early - - Consider gRPC vs REST API design -- [ ] Makefile for build/test/dev environment setup - - Recipe for setting up demo server with Docker + Traefik - - Maybe include a `make dev` target that sets up a full local environment - - Document how to configure different Docker runtimes - -## Medium -- [ ] How do we handler monitoring function runtime metrics? - - Embedded Prometheus is a thing I've seen commonly but not sure how I feel about this - - Alternative: OpenTelemetry metrics export to external collectors - - Consider what metrics matter: execution time, memory usage, startup latency, error rates -- [ ] Reduce usage of `interface{}`/`any` to make code more understandable - - Good for maintainability and catching errors at compile time - - Consider using generics where appropriate (Go 1.18+) -- [ ] Chroot/sandboxing/drop privileges by default for main binary - - Is this the correct way? Or do we expect the user to sandbox the main process if they so wish? - - Defense in depth: even if VMs/containers provide isolation, host process should be minimal - - Consider running as non-root user by default -- [ ] Function runtime configuration and metadata - - How do users specify runtime requirements? (GPU, extra isolation, etc.) - - Should this be in function metadata, CLI flags, or config file? - - Consider: `docker run --runtime=kata` vs `docker run --runtime=nvidia` - -## Future/Scale-Up Considerations -- [ ] Pluggable orchestrator interface for external schedulers - - Design: `type Scheduler interface { Schedule(fn Function) error }` - - Keep door open for Nomad integration without building it now -- [ ] Direct Firecracker provider (bypassing Docker) - - Only if we need more control than Docker runtimes provide - - Would need to reimplement IPAM, image handling, etc. - - Consider: Is the complexity worth it vs just using kata-containers? -- [ ] WASM runtime support alongside containers - - Ultra-fast cold starts, but limited ecosystem - - Could be good for specific use cases (CPU-bound, no system calls) -- [ ] Additional compute providers - - Podman support (very similar to Docker API) - - Nomad job execution - - Cloud provider serverless (Lambda, Cloud Functions, etc.) - -## Future Ideas (Don't Build Yet) -- [ ] Multi-node clustering and state replication - - Start with single-node SQLite, worry about this later - - Consider rqlite/litestream when we actually need it -- [ ] Function versioning and blue/green deployments - - Important for production workloads, but not MVP - - Could reuse container image tags for this -- [ ] Advanced scheduling and bin packing - - How do we efficiently pack functions onto available compute? - - Consider CPU/memory/network requirements - - **Note: This is exactly the kind of thing Nomad solves - don't reinvent** -- [ ] Cold start optimization strategies - - Pre-warmed pools? Shared base images? Snapshot/restore? - - Profile first, optimize second diff --git a/functional/api/adapters.go b/functional/api/adapters.go deleted file mode 100644 index c1f89c2..0000000 --- a/functional/api/adapters.go +++ /dev/null @@ -1,67 +0,0 @@ -package api - -import ( - "github.com/pirogoeth/apps/functional/compute" - "github.com/pirogoeth/apps/functional/database" - "github.com/pirogoeth/apps/functional/types" -) - -// Adapters to convert between database types and compute types - -func dbFunctionToComputeFunction(dbFunc database.Function) *compute.Function { - return &compute.Function{ - ID: dbFunc.ID, - Name: dbFunc.Name, - Description: dbFunc.Description.String, - CodePath: dbFunc.CodePath, - Runtime: dbFunc.Runtime, - Handler: dbFunc.Handler, - TimeoutSeconds: int32(dbFunc.TimeoutSeconds), - MemoryMB: int32(dbFunc.MemoryMb), - EnvVars: dbFunc.EnvVars.String, - } -} - -func dbDeploymentToComputeDeployment(dbDep database.Deployment) *compute.Deployment { - return &compute.Deployment{ - ID: dbDep.ID, - FunctionID: dbDep.FunctionID, - Provider: dbDep.Provider, - ResourceID: dbDep.ResourceID, - Status: dbDep.Status, - Replicas: int32(dbDep.Replicas), - ImageTag: dbDep.ImageTag.String, - } -} - -func computeDeployResultToTypesDeployResult(compResult *compute.DeployResult) *types.DeployResult { - return &types.DeployResult{ - DeploymentID: compResult.DeploymentID, - ResourceID: compResult.ResourceID, - ImageTag: compResult.ImageTag, - } -} - -func typesInvocationRequestToComputeInvocationRequest(typesReq *types.InvocationRequest) *compute.InvocationRequest { - return &compute.InvocationRequest{ - FunctionID: typesReq.FunctionID, - Body: typesReq.Body, - Headers: typesReq.Headers, - Method: typesReq.Method, - Path: typesReq.Path, - QueryArgs: typesReq.QueryArgs, - } -} - -func computeInvocationResultToTypesInvocationResult(compResult *compute.InvocationResult) *types.InvocationResult { - return &types.InvocationResult{ - StatusCode: compResult.StatusCode, - Body: compResult.Body, - Headers: compResult.Headers, - DurationMS: compResult.DurationMS, - MemoryUsedMB: compResult.MemoryUsedMB, - ResponseSize: compResult.ResponseSize, - Logs: compResult.Logs, - Error: compResult.Error, - } -} \ No newline at end of file diff --git a/functional/api/api.go b/functional/api/api.go deleted file mode 100644 index 7ce6d0a..0000000 --- a/functional/api/api.go +++ /dev/null @@ -1,19 +0,0 @@ -package api - -import ( - "github.com/gin-gonic/gin" - "github.com/pirogoeth/apps/functional/types" -) - -func MustRegister(router *gin.Engine, apiContext *types.ApiContext) error { - // V1 API group - groupV1 := router.Group("/v1") - - // Register function endpoints - (&v1Functions{apiContext}).RegisterRoutesTo(groupV1) - - // Register invocation endpoints - (&v1Invocations{apiContext}).RegisterRoutesTo(groupV1) - - return nil -} \ No newline at end of file diff --git a/functional/api/helpers.go b/functional/api/helpers.go deleted file mode 100644 index ca5140e..0000000 --- a/functional/api/helpers.go +++ /dev/null @@ -1,17 +0,0 @@ -package api - -import ( - "strconv" - - "github.com/gin-gonic/gin" -) - -// GetQueryInt extracts an integer query parameter with a default value -func GetQueryInt(c *gin.Context, key string, defaultValue int) int { - value := c.DefaultQuery(key, strconv.Itoa(defaultValue)) - intVal, err := strconv.Atoi(value) - if err != nil { - return defaultValue - } - return intVal -} \ No newline at end of file diff --git a/functional/api/v1functions.go b/functional/api/v1functions.go deleted file mode 100644 index 6d94974..0000000 --- a/functional/api/v1functions.go +++ /dev/null @@ -1,244 +0,0 @@ -package api - -import ( - "database/sql" - "encoding/base64" - "encoding/json" - "fmt" - "net/http" - "os" - "path/filepath" - - "github.com/gin-gonic/gin" - "github.com/google/uuid" - "github.com/pirogoeth/apps/pkg/apitools" - "github.com/pirogoeth/apps/functional/compute" - "github.com/pirogoeth/apps/functional/database" - "github.com/pirogoeth/apps/functional/types" -) - -type v1Functions struct { - *types.ApiContext -} - -func (e *v1Functions) RegisterRoutesTo(router *gin.RouterGroup) { - functions := router.Group("/functions") - - functions.POST("", apitools.ErrorWrapEndpoint(e.createFunction)) - functions.GET("", apitools.ErrorWrapEndpoint(e.listFunctions)) - functions.GET("/:id", apitools.ErrorWrapEndpoint(e.getFunction)) - functions.PUT("/:id", apitools.ErrorWrapEndpoint(e.updateFunction)) - functions.DELETE("/:id", apitools.ErrorWrapEndpoint(e.deleteFunction)) - functions.POST("/:id/deploy", apitools.ErrorWrapEndpoint(e.deployFunction)) -} - -func (e *v1Functions) createFunction(c *gin.Context) error { - var req types.CreateFunctionRequest - if err := c.ShouldBindJSON(&req); err != nil { - return fmt.Errorf("%s: %w", apitools.MsgFailedToBind, err) - } - - // Validate request - if req.Name == "" { - return fmt.Errorf("%s: function name is required", apitools.MsgInvalidParameter) - } - if req.Runtime == "" { - return fmt.Errorf("%s: runtime is required", apitools.MsgInvalidParameter) - } - if req.Handler == "" { - return fmt.Errorf("%s: handler is required", apitools.MsgInvalidParameter) - } - - // Generate function ID - functionID := uuid.New().String() - - // Set defaults - timeoutSeconds := req.TimeoutSeconds - if timeoutSeconds == 0 { - timeoutSeconds = 30 - } - memoryMB := req.MemoryMB - if memoryMB == 0 { - memoryMB = 128 - } - - // Store function code to filesystem - codePath, err := e.storeFunctionCode(functionID, req.Code, req.Runtime) - if err != nil { - return fmt.Errorf("failed to store function code: %w", err) - } - - // Serialize environment variables - envVarsJSON := "{}" - if len(req.EnvVars) > 0 { - envBytes, err := json.Marshal(req.EnvVars) - if err != nil { - return fmt.Errorf("failed to serialize environment variables: %w", err) - } - envVarsJSON = string(envBytes) - } - - // Create database record - function, err := e.Querier.CreateFunction(c.Request.Context(), database.CreateFunctionParams{ - ID: functionID, - Name: req.Name, - Description: sql.NullString{String: req.Description, Valid: req.Description != ""}, - CodePath: codePath, - Runtime: req.Runtime, - Handler: req.Handler, - TimeoutSeconds: int64(timeoutSeconds), - MemoryMb: int64(memoryMB), - EnvVars: sql.NullString{String: envVarsJSON, Valid: true}, - }) - if err != nil { - return fmt.Errorf("failed to create function: %w", err) - } - - apitools.Ok(c, &apitools.Body{"function": function}) - return nil -} - -func (e *v1Functions) listFunctions(c *gin.Context) error { - functions, err := e.Querier.ListFunctions(c.Request.Context()) - if err != nil { - return fmt.Errorf("failed to list functions: %w", err) - } - - apitools.Ok(c, &apitools.Body{"functions": functions}) - return nil -} - -func (e *v1Functions) getFunction(c *gin.Context) error { - id := c.Param("id") - if id == "" { - return fmt.Errorf("%s: function id is required", apitools.MsgInvalidParameter) - } - - function, err := e.Querier.GetFunction(c.Request.Context(), id) - if err != nil { - return fmt.Errorf("function not found: %w", err) - } - - apitools.Ok(c, &apitools.Body{"function": function}) - return nil -} - -func (e *v1Functions) updateFunction(c *gin.Context) error { - id := c.Param("id") - if id == "" { - return fmt.Errorf("%s: function id is required", apitools.MsgInvalidParameter) - } - - var req types.UpdateFunctionRequest - if err := c.ShouldBindJSON(&req); err != nil { - return fmt.Errorf("%s: %w", apitools.MsgFailedToBind, err) - } - - // TODO: Implement function update - // 1. Validate function exists - // 2. Update function code if provided - // 3. Update database record - // 4. Trigger redeployment if needed - // 5. Return updated function details - - return fmt.Errorf(apitools.MsgNotImplemented) -} - -func (e *v1Functions) deleteFunction(c *gin.Context) error { - id := c.Param("id") - if id == "" { - return fmt.Errorf("%s: function id is required", apitools.MsgInvalidParameter) - } - - // TODO: Implement function deletion - // 1. Validate function exists - // 2. Stop and remove deployments - // 3. Clean up function code - // 4. Delete database record - - err := e.Querier.DeleteFunction(c.Request.Context(), id) - if err != nil { - return fmt.Errorf("failed to delete function: %w", err) - } - - c.JSON(http.StatusNoContent, nil) - return nil -} - -// Helper method to store function code -func (e *v1Functions) storeFunctionCode(functionID, codeBase64, runtime string) (string, error) { - // Decode base64 code - codeBytes, err := base64.StdEncoding.DecodeString(codeBase64) - if err != nil { - return "", fmt.Errorf("invalid base64 code: %w", err) - } - - // Create function directory - functionsDir := e.Config.Storage.FunctionsPath - functionDir := filepath.Join(functionsDir, functionID) - - if err := os.MkdirAll(functionDir, 0755); err != nil { - return "", fmt.Errorf("failed to create function directory: %w", err) - } - - // Store code (for now, just save as code.zip) - codePath := filepath.Join(functionDir, "code.zip") - if err := os.WriteFile(codePath, codeBytes, 0644); err != nil { - return "", fmt.Errorf("failed to write function code: %w", err) - } - - return codePath, nil -} - -func (e *v1Functions) deployFunction(c *gin.Context) error { - id := c.Param("id") - if id == "" { - return fmt.Errorf("%s: function id is required", apitools.MsgInvalidParameter) - } - - // Validate function exists - function, err := e.Querier.GetFunction(c.Request.Context(), id) - if err != nil { - return fmt.Errorf("function not found: %w", err) - } - - // Get active compute provider - provider, err := e.Compute.Get(e.Config.Compute.Provider) - if err != nil { - return fmt.Errorf("compute provider not available: %w", err) - } - - // Convert function to compute type - computeFunc := dbFunctionToComputeFunction(function) - - // Deploy to compute provider - deployResult, err := provider.Deploy(c.Request.Context(), computeFunc, "") - if err != nil { - return fmt.Errorf("deployment failed: %w", err) - } - - compResult, ok := deployResult.(*compute.DeployResult) - if !ok { - return fmt.Errorf("invalid deploy result type") - } - - // Convert back to types - result := computeDeployResultToTypesDeployResult(compResult) - - // Create deployment record - deployment, err := e.Querier.CreateDeployment(c.Request.Context(), database.CreateDeploymentParams{ - ID: result.DeploymentID, - FunctionID: id, - Provider: e.Config.Compute.Provider, - ResourceID: result.ResourceID, - Status: string(types.DeploymentStatusActive), - Replicas: 1, - ImageTag: sql.NullString{String: result.ImageTag, Valid: true}, - }) - if err != nil { - return fmt.Errorf("failed to create deployment record: %w", err) - } - - apitools.Ok(c, &apitools.Body{"deployment": deployment}) - return nil -} \ No newline at end of file diff --git a/functional/api/v1functions_test.go b/functional/api/v1functions_test.go deleted file mode 100644 index 761eb67..0000000 --- a/functional/api/v1functions_test.go +++ /dev/null @@ -1,529 +0,0 @@ -package api - -import ( - "bytes" - "context" - "database/sql" - "encoding/base64" - "encoding/json" - "net/http" - "net/http/httptest" - "path/filepath" - "testing" - - "github.com/gin-gonic/gin" - "github.com/google/uuid" - "github.com/pirogoeth/apps/functional/compute" - "github.com/pirogoeth/apps/functional/database" - "github.com/pirogoeth/apps/functional/types" -) - -func setupTestAPI(t *testing.T) (*gin.Engine, *types.ApiContext) { - // Set gin to test mode - gin.SetMode(gin.TestMode) - - // Setup test database - tempDir := t.TempDir() - dbPath := filepath.Join(tempDir, "test.db") - - ctx := context.Background() - db, err := database.Open(ctx, dbPath) - if err != nil { - t.Fatalf("Failed to open test database: %v", err) - } - - if err := db.RunMigrations(database.MigrationsFS); err != nil { - t.Fatalf("Failed to run migrations: %v", err) - } - - // Setup mock compute provider - mockProvider := &MockComputeProvider{ - name: "mock", - } - - // Create compute registry and register mock provider - registry := compute.NewRegistry() - registry.Register(mockProvider) - - // Create test config - config := &types.Config{ - Storage: types.StorageConfig{ - FunctionsPath: tempDir + "/functions", - TempPath: tempDir + "/temp", - }, - } - - // Create API context - apiContext := &types.ApiContext{ - Config: config, - Querier: db.Queries, - Compute: registry, - } - - // Setup router - router := gin.New() - err = MustRegister(router, apiContext) - if err != nil { - t.Fatalf("Failed to register routes: %v", err) - } - - return router, apiContext -} - -// Mock compute provider for testing -type MockComputeProvider struct { - name string - deployError error - deployResult *compute.DeployResult - executeError error - executeResult *compute.InvocationResult - healthError error -} - -// Ensure MockComputeProvider implements ComputeProvider interface -var _ compute.ComputeProvider = (*MockComputeProvider)(nil) - -func (m *MockComputeProvider) Name() string { - return m.name -} - -func (m *MockComputeProvider) Deploy(ctx context.Context, fn interface{}, imageName string) (interface{}, error) { - if m.deployError != nil { - return nil, m.deployError - } - - if m.deployResult != nil { - return m.deployResult, nil - } - - return &compute.DeployResult{ - DeploymentID: uuid.New().String(), - ResourceID: "mock-resource-" + uuid.New().String()[:8], - ImageTag: "mock-image:latest", - }, nil -} - -func (m *MockComputeProvider) Execute(ctx context.Context, deployment interface{}, req interface{}) (interface{}, error) { - if m.executeError != nil { - return nil, m.executeError - } - - if m.executeResult != nil { - return m.executeResult, nil - } - - return &compute.InvocationResult{ - StatusCode: 200, - Body: []byte(`{"message": "mock response"}`), - Headers: map[string]string{"Content-Type": "application/json"}, - DurationMS: 100, - MemoryUsedMB: 64, - ResponseSize: 25, - }, nil -} - -func (m *MockComputeProvider) Scale(ctx context.Context, deployment interface{}, replicas int) error { - return nil -} - -func (m *MockComputeProvider) Remove(ctx context.Context, deployment interface{}) error { - return nil -} - -func (m *MockComputeProvider) Health(ctx context.Context) error { - return m.healthError -} - -func TestV1Functions_CreateFunction(t *testing.T) { - router, _ := setupTestAPI(t) - - // Create sample function code (base64 encoded) - sampleCode := `{ - "index.js": "const express = require('express'); const app = express(); app.all('*', (req, res) => res.json({message: 'Hello World'})); app.listen(8080);" - }` - encodedCode := base64.StdEncoding.EncodeToString([]byte(sampleCode)) - - tests := []struct { - name string - requestBody types.CreateFunctionRequest - expectedStatus int - checkResponse func(t *testing.T, response map[string]interface{}) - }{ - { - name: "valid function creation", - requestBody: types.CreateFunctionRequest{ - Name: "test-function", - Description: "Test function description", - Runtime: "nodejs", - Handler: "index.handler", - TimeoutSeconds: 30, - MemoryMB: 128, - EnvVars: map[string]string{"TEST": "value"}, - Code: encodedCode, - }, - expectedStatus: http.StatusCreated, - checkResponse: func(t *testing.T, response map[string]interface{}) { - if response["name"] != "test-function" { - t.Errorf("Expected name 'test-function', got %v", response["name"]) - } - if response["runtime"] != "nodejs" { - t.Errorf("Expected runtime 'nodejs', got %v", response["runtime"]) - } - if response["id"] == nil || response["id"] == "" { - t.Errorf("Expected non-empty ID") - } - }, - }, - { - name: "missing required fields", - requestBody: types.CreateFunctionRequest{ - Description: "Missing required fields", - }, - expectedStatus: http.StatusBadRequest, - checkResponse: func(t *testing.T, response map[string]interface{}) { - if response["error"] == nil { - t.Errorf("Expected error message for missing required fields") - } - }, - }, - { - name: "empty function name", - requestBody: types.CreateFunctionRequest{ - Name: "", - Runtime: "nodejs", - Handler: "index.handler", - Code: encodedCode, - }, - expectedStatus: http.StatusBadRequest, - checkResponse: func(t *testing.T, response map[string]interface{}) { - if response["error"] == nil { - t.Errorf("Expected error message for empty function name") - } - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - body, _ := json.Marshal(tt.requestBody) - req := httptest.NewRequest(http.MethodPost, "/v1/functions", bytes.NewBuffer(body)) - req.Header.Set("Content-Type", "application/json") - - w := httptest.NewRecorder() - router.ServeHTTP(w, req) - - if w.Code != tt.expectedStatus { - t.Errorf("Expected status %d, got %d", tt.expectedStatus, w.Code) - } - - var response map[string]interface{} - err := json.Unmarshal(w.Body.Bytes(), &response) - if err != nil { - t.Fatalf("Failed to unmarshal response: %v", err) - } - - if tt.checkResponse != nil { - tt.checkResponse(t, response) - } - }) - } -} - -func TestV1Functions_ListFunctions(t *testing.T) { - router, apiContext := setupTestAPI(t) - - // Create some test functions - ctx := context.Background() - functions := []database.CreateFunctionParams{ - { - ID: "list-test-1", - Name: "list-function-1", - CodePath: "/tmp/func1", - Runtime: "nodejs", - Handler: "index.handler", - TimeoutSeconds: 30, - MemoryMb: 128, - }, - { - ID: "list-test-2", - Name: "list-function-2", - CodePath: "/tmp/func2", - Runtime: "python3", - Handler: "app.handler", - TimeoutSeconds: 60, - MemoryMb: 256, - }, - } - - for _, params := range functions { - _, err := apiContext.Querier.CreateFunction(ctx, params) - if err != nil { - t.Fatalf("Failed to create test function: %v", err) - } - } - - // Test list functions - req := httptest.NewRequest(http.MethodGet, "/v1/functions", nil) - w := httptest.NewRecorder() - router.ServeHTTP(w, req) - - if w.Code != http.StatusOK { - t.Errorf("Expected status %d, got %d", http.StatusOK, w.Code) - } - - var response map[string]interface{} - err := json.Unmarshal(w.Body.Bytes(), &response) - if err != nil { - t.Fatalf("Failed to unmarshal response: %v", err) - } - - functions_data, ok := response["functions"].([]interface{}) - if !ok { - t.Fatalf("Expected functions array in response") - } - - if len(functions_data) < 2 { - t.Errorf("Expected at least 2 functions, got %d", len(functions_data)) - } -} - -func TestV1Functions_GetFunction(t *testing.T) { - router, apiContext := setupTestAPI(t) - - // Create a test function - ctx := context.Background() - params := database.CreateFunctionParams{ - ID: "get-test-function", - Name: "get-function", - Description: sql.NullString{String: "Test description", Valid: true}, - CodePath: "/tmp/get-func", - Runtime: "nodejs", - Handler: "index.handler", - TimeoutSeconds: 30, - MemoryMb: 128, - EnvVars: sql.NullString{String: `{"TEST": "value"}`, Valid: true}, - } - - function, err := apiContext.Querier.CreateFunction(ctx, params) - if err != nil { - t.Fatalf("Failed to create test function: %v", err) - } - - tests := []struct { - name string - functionID string - expectedStatus int - checkResponse func(t *testing.T, response map[string]interface{}) - }{ - { - name: "valid function ID", - functionID: function.ID, - expectedStatus: http.StatusOK, - checkResponse: func(t *testing.T, response map[string]interface{}) { - if response["id"] != function.ID { - t.Errorf("Expected ID %s, got %v", function.ID, response["id"]) - } - if response["name"] != function.Name { - t.Errorf("Expected name %s, got %v", function.Name, response["name"]) - } - }, - }, - { - name: "non-existent function ID", - functionID: "non-existent", - expectedStatus: http.StatusNotFound, - checkResponse: func(t *testing.T, response map[string]interface{}) { - if response["error"] == nil { - t.Errorf("Expected error message for non-existent function") - } - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - req := httptest.NewRequest(http.MethodGet, "/v1/functions/"+tt.functionID, nil) - w := httptest.NewRecorder() - router.ServeHTTP(w, req) - - if w.Code != tt.expectedStatus { - t.Errorf("Expected status %d, got %d", tt.expectedStatus, w.Code) - } - - var response map[string]interface{} - err := json.Unmarshal(w.Body.Bytes(), &response) - if err != nil { - t.Fatalf("Failed to unmarshal response: %v", err) - } - - if tt.checkResponse != nil { - tt.checkResponse(t, response) - } - }) - } -} - -func TestV1Functions_DeployFunction(t *testing.T) { - router, apiContext := setupTestAPI(t) - - // Create a test function - ctx := context.Background() - params := database.CreateFunctionParams{ - ID: "deploy-test-function", - Name: "deploy-function", - CodePath: "/tmp/deploy-func", - Runtime: "nodejs", - Handler: "index.handler", - TimeoutSeconds: 30, - MemoryMb: 128, - } - - function, err := apiContext.Querier.CreateFunction(ctx, params) - if err != nil { - t.Fatalf("Failed to create test function: %v", err) - } - - tests := []struct { - name string - functionID string - expectedStatus int - checkResponse func(t *testing.T, response map[string]interface{}) - }{ - { - name: "valid function deployment", - functionID: function.ID, - expectedStatus: http.StatusOK, - checkResponse: func(t *testing.T, response map[string]interface{}) { - if response["deployment_id"] == nil || response["deployment_id"] == "" { - t.Errorf("Expected non-empty deployment_id") - } - if response["resource_id"] == nil || response["resource_id"] == "" { - t.Errorf("Expected non-empty resource_id") - } - if response["image_tag"] == nil || response["image_tag"] == "" { - t.Errorf("Expected non-empty image_tag") - } - }, - }, - { - name: "non-existent function", - functionID: "non-existent", - expectedStatus: http.StatusNotFound, - checkResponse: func(t *testing.T, response map[string]interface{}) { - if response["error"] == nil { - t.Errorf("Expected error message for non-existent function") - } - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - req := httptest.NewRequest(http.MethodPost, "/v1/functions/"+tt.functionID+"/deploy", nil) - w := httptest.NewRecorder() - router.ServeHTTP(w, req) - - if w.Code != tt.expectedStatus { - t.Errorf("Expected status %d, got %d", tt.expectedStatus, w.Code) - } - - var response map[string]interface{} - err := json.Unmarshal(w.Body.Bytes(), &response) - if err != nil { - t.Fatalf("Failed to unmarshal response: %v", err) - } - - if tt.checkResponse != nil { - tt.checkResponse(t, response) - } - }) - } -} - -func TestV1Functions_DeleteFunction(t *testing.T) { - router, apiContext := setupTestAPI(t) - - // Create a test function - ctx := context.Background() - params := database.CreateFunctionParams{ - ID: "delete-test-function", - Name: "delete-function", - CodePath: "/tmp/delete-func", - Runtime: "nodejs", - Handler: "index.handler", - TimeoutSeconds: 30, - MemoryMb: 128, - } - - function, err := apiContext.Querier.CreateFunction(ctx, params) - if err != nil { - t.Fatalf("Failed to create test function: %v", err) - } - - tests := []struct { - name string - functionID string - expectedStatus int - }{ - { - name: "valid function deletion", - functionID: function.ID, - expectedStatus: http.StatusNoContent, - }, - { - name: "non-existent function", - functionID: "non-existent", - expectedStatus: http.StatusNotFound, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - req := httptest.NewRequest(http.MethodDelete, "/v1/functions/"+tt.functionID, nil) - w := httptest.NewRecorder() - router.ServeHTTP(w, req) - - if w.Code != tt.expectedStatus { - t.Errorf("Expected status %d, got %d", tt.expectedStatus, w.Code) - } - }) - } -} - -// Benchmark tests -func BenchmarkV1Functions_CreateFunction(b *testing.B) { - router, _ := setupTestAPI(&testing.T{}) - - sampleCode := `{"index.js": "console.log('Hello World')"}` - encodedCode := base64.StdEncoding.EncodeToString([]byte(sampleCode)) - - requestBody := types.CreateFunctionRequest{ - Name: "bench-function", - Runtime: "nodejs", - Handler: "index.handler", - TimeoutSeconds: 30, - MemoryMB: 128, - Code: encodedCode, - } - - body, _ := json.Marshal(requestBody) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - req := httptest.NewRequest(http.MethodPost, "/v1/functions", bytes.NewBuffer(body)) - req.Header.Set("Content-Type", "application/json") - w := httptest.NewRecorder() - router.ServeHTTP(w, req) - } -} - -func BenchmarkV1Functions_ListFunctions(b *testing.B) { - router, _ := setupTestAPI(&testing.T{}) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - req := httptest.NewRequest(http.MethodGet, "/v1/functions", nil) - w := httptest.NewRecorder() - router.ServeHTTP(w, req) - } -} \ No newline at end of file diff --git a/functional/api/v1invocations.go b/functional/api/v1invocations.go deleted file mode 100644 index cf2232a..0000000 --- a/functional/api/v1invocations.go +++ /dev/null @@ -1,223 +0,0 @@ -package api - -import ( - "database/sql" - "fmt" - "io" - - "github.com/gin-gonic/gin" - "github.com/google/uuid" - "github.com/pirogoeth/apps/pkg/apitools" - "github.com/pirogoeth/apps/functional/compute" - "github.com/pirogoeth/apps/functional/database" - "github.com/pirogoeth/apps/functional/types" -) - -type v1Invocations struct { - *types.ApiContext -} - -func (e *v1Invocations) RegisterRoutesTo(router *gin.RouterGroup) { - // Function invocation endpoint - router.POST("/invoke/:function_name", apitools.ErrorWrapEndpoint(e.invokeFunction)) - - // Invocation management endpoints - invocations := router.Group("/invocations") - invocations.GET("", apitools.ErrorWrapEndpoint(e.listInvocations)) - invocations.GET("/:id", apitools.ErrorWrapEndpoint(e.getInvocation)) - - // Function-specific invocation endpoints - functions := router.Group("/functions") - functions.GET("/:id/invocations", apitools.ErrorWrapEndpoint(e.listFunctionInvocations)) - functions.GET("/:id/stats", apitools.ErrorWrapEndpoint(e.getFunctionStats)) -} - -func (e *v1Invocations) invokeFunction(c *gin.Context) error { - functionName := c.Param("function_name") - if functionName == "" { - return fmt.Errorf("%s: function name is required", apitools.MsgInvalidParameter) - } - - // Look up function by name - function, err := e.Querier.GetFunctionByName(c.Request.Context(), functionName) - if err != nil { - return fmt.Errorf("function not found: %w", err) - } - - // Get active deployment - deployment, err := e.Querier.GetActiveDeploymentByFunction(c.Request.Context(), function.ID) - if err != nil { - return fmt.Errorf("no active deployment found for function: %w", err) - } - - // Create invocation record - invocationID := uuid.New().String() - _, err = e.Querier.CreateInvocation(c.Request.Context(), database.CreateInvocationParams{ - ID: invocationID, - FunctionID: function.ID, - DeploymentID: sql.NullString{String: deployment.ID, Valid: true}, - Status: string(types.InvocationStatusPending), - }) - if err != nil { - return fmt.Errorf("failed to create invocation record: %w", err) - } - - // Read request body - body, err := io.ReadAll(c.Request.Body) - if err != nil { - return fmt.Errorf("failed to read request body: %w", err) - } - - // Convert headers - headers := make(map[string]string) - for k, v := range c.Request.Header { - if len(v) > 0 { - headers[k] = v[0] - } - } - - // Convert query parameters - queryArgs := make(map[string]string) - for k, v := range c.Request.URL.Query() { - if len(v) > 0 { - queryArgs[k] = v[0] - } - } - - // Create invocation request - invReq := &types.InvocationRequest{ - FunctionID: function.ID, - Body: body, - Headers: headers, - Method: c.Request.Method, - Path: c.Request.URL.Path, - QueryArgs: queryArgs, - } - - // Get compute provider - provider, err := e.Compute.Get(deployment.Provider) - if err != nil { - return fmt.Errorf("compute provider not available: %w", err) - } - - // Convert types for compute provider - computeDep := dbDeploymentToComputeDeployment(deployment) - computeInvReq := typesInvocationRequestToComputeInvocationRequest(invReq) - - // Execute function - result, err := provider.Execute(c.Request.Context(), computeDep, computeInvReq) - if err != nil { - // Update invocation with error - e.Querier.UpdateInvocationComplete(c.Request.Context(), database.UpdateInvocationCompleteParams{ - ID: invocationID, - Status: string(types.InvocationStatusError), - Error: sql.NullString{String: err.Error(), Valid: true}, - }) - return fmt.Errorf("function execution failed: %w", err) - } - - compResult, ok := result.(*compute.InvocationResult) - if !ok { - return fmt.Errorf("invalid invocation result type") - } - - // Convert back to types - invResult := computeInvocationResultToTypesInvocationResult(compResult) - - // Update invocation with results - status := string(types.InvocationStatusSuccess) - if invResult.StatusCode >= 400 { - status = string(types.InvocationStatusError) - } - - e.Querier.UpdateInvocationComplete(c.Request.Context(), database.UpdateInvocationCompleteParams{ - ID: invocationID, - Status: status, - DurationMs: sql.NullInt64{Int64: invResult.DurationMS, Valid: true}, - MemoryUsedMb: sql.NullInt64{Int64: int64(invResult.MemoryUsedMB), Valid: true}, - ResponseSizeBytes: sql.NullInt64{Int64: invResult.ResponseSize, Valid: true}, - Logs: sql.NullString{String: invResult.Logs, Valid: invResult.Logs != ""}, - Error: sql.NullString{String: invResult.Error, Valid: invResult.Error != ""}, - }) - - // Return the function's response - for k, v := range invResult.Headers { - c.Header(k, v) - } - c.Data(invResult.StatusCode, "application/json", invResult.Body) - return nil -} - -func (e *v1Invocations) listInvocations(c *gin.Context) error { - limit := GetQueryInt(c, "limit", 50) - offset := GetQueryInt(c, "offset", 0) - - invocations, err := e.Querier.ListInvocations(c.Request.Context(), database.ListInvocationsParams{ - Limit: int64(limit), - Offset: int64(offset), - }) - if err != nil { - return fmt.Errorf("failed to list invocations: %w", err) - } - - apitools.Ok(c, &apitools.Body{"invocations": invocations}) - return nil -} - -func (e *v1Invocations) getInvocation(c *gin.Context) error { - id := c.Param("id") - if id == "" { - return fmt.Errorf("%s: invocation id is required", apitools.MsgInvalidParameter) - } - - invocation, err := e.Querier.GetInvocation(c.Request.Context(), id) - if err != nil { - return fmt.Errorf("invocation not found: %w", err) - } - - apitools.Ok(c, &apitools.Body{"invocation": invocation}) - return nil -} - -func (e *v1Invocations) listFunctionInvocations(c *gin.Context) error { - functionID := c.Param("id") - if functionID == "" { - return fmt.Errorf("%s: function id is required", apitools.MsgInvalidParameter) - } - - limit := GetQueryInt(c, "limit", 50) - offset := GetQueryInt(c, "offset", 0) - - invocations, err := e.Querier.ListInvocationsByFunction(c.Request.Context(), database.ListInvocationsByFunctionParams{ - FunctionID: functionID, - Limit: int64(limit), - Offset: int64(offset), - }) - if err != nil { - return fmt.Errorf("failed to list function invocations: %w", err) - } - - apitools.Ok(c, &apitools.Body{"invocations": invocations}) - return nil -} - -func (e *v1Invocations) getFunctionStats(c *gin.Context) error { - functionID := c.Param("id") - if functionID == "" { - return fmt.Errorf("%s: function id is required", apitools.MsgInvalidParameter) - } - - // TODO: Parse time window from query params - // For now, use last 24 hours - // since := time.Now().Add(-24 * time.Hour) - - // stats, err := e.Querier.GetInvocationStats(c.Request.Context(), functionID, since) - // if err != nil { - // return fmt.Errorf("failed to get function stats: %w", err) - // } - - // apitools.Ok(c, &apitools.Body{"stats": stats}) - // return nil - - return fmt.Errorf(apitools.MsgNotImplemented) -} \ No newline at end of file diff --git a/functional/cmd/proxy.go b/functional/cmd/proxy.go deleted file mode 100644 index e91eb4e..0000000 --- a/functional/cmd/proxy.go +++ /dev/null @@ -1,93 +0,0 @@ -package cmd - -import ( - "context" - "os" - "os/signal" - "syscall" - - "github.com/pirogoeth/apps/functional/database" - "github.com/pirogoeth/apps/functional/proxy" - "github.com/sirupsen/logrus" - "github.com/spf13/cobra" -) - -const ComponentProxy = "proxy" - -var proxyCmd = &cobra.Command{ - Use: "proxy", - Short: "Start the function proxy service", - Long: `Start the function proxy service that handles function invocations, -container pooling, and Traefik integration.`, - RunE: runProxy, -} - -func init() { - rootCmd.AddCommand(proxyCmd) -} - -func runProxy(cmd *cobra.Command, args []string) error { - cfg := appStart(ComponentProxy) - - // Initialize database - ctx := context.Background() - db, err := database.Open(ctx, cfg.Database.Path) - if err != nil { - logrus.WithError(err).Fatal("Failed to open database") - } - defer db.Close() - - // Run database migrations - if err := db.RunMigrations(database.MigrationsFS); err != nil { - logrus.WithError(err).Fatal("Failed to run database migrations") - } - - // Set proxy defaults if not configured - if cfg.Proxy.ListenAddress == "" { - cfg.Proxy.ListenAddress = ":8080" - } - if cfg.Proxy.TraefikAPIURL == "" { - cfg.Proxy.TraefikAPIURL = "http://traefik:8080/api" - } - if cfg.Proxy.MaxContainersPerFunction == 0 { - cfg.Proxy.MaxContainersPerFunction = 5 - } - if cfg.Proxy.ContainerIdleTimeout == 0 { - cfg.Proxy.ContainerIdleTimeout = 300000000000 // 5 minutes in nanoseconds - } - - // Create proxy service - proxyService := proxy.NewProxyService(cfg, db) - - // Set up graceful shutdown - ctx, cancel := context.WithCancel(ctx) - defer cancel() - - // Handle shutdown signals - sigChan := make(chan os.Signal, 1) - signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) - - go func() { - sig := <-sigChan - logrus.WithField("signal", sig).Info("Received shutdown signal") - cancel() - }() - - // Start proxy service - logrus.WithFields(logrus.Fields{ - "listen_address": cfg.Proxy.ListenAddress, - "traefik_api_url": cfg.Proxy.TraefikAPIURL, - "max_containers": cfg.Proxy.MaxContainersPerFunction, - "idle_timeout": cfg.Proxy.ContainerIdleTimeout, - }).Info("Starting function proxy service") - - if err := proxyService.Start(ctx); err != nil { - if ctx.Err() != context.Canceled { - logrus.WithError(err).Fatal("Proxy service failed") - } - } - - logrus.Info("Proxy service stopped") - return nil -} - diff --git a/functional/cmd/root.go b/functional/cmd/root.go deleted file mode 100644 index 5f17cf3..0000000 --- a/functional/cmd/root.go +++ /dev/null @@ -1,48 +0,0 @@ -package cmd - -import ( - "os" - - "github.com/pirogoeth/apps/functional/types" - "github.com/pirogoeth/apps/pkg/config" - "github.com/pirogoeth/apps/pkg/logging" - "github.com/pirogoeth/apps/pkg/tracing" - "github.com/sirupsen/logrus" - "github.com/spf13/cobra" -) - -const AppName = "functional" - -var rootCmd = &cobra.Command{ - Use: AppName, - Short: "Function-as-a-Service provider", - Long: "A modular FaaS provider with pluggable compute backends", -} - -func Execute() { - if err := rootCmd.Execute(); err != nil { - os.Exit(1) - } -} - -func init() { - rootCmd.AddCommand(serveCmd) -} - -func appStart(component string) *types.Config { - logging.Setup(logging.WithAppName(AppName), logging.WithComponentName(component)) - - cfg, err := config.Load[types.Config]() - if err != nil { - logrus.WithError(err).Fatal("failed to load configuration") - } - - tracing.Setup( - tracing.WithAppName(AppName), - tracing.WithComponentName(component), - tracing.WithConfig(cfg.Tracing), - ) - - return cfg -} - diff --git a/functional/cmd/serve.go b/functional/cmd/serve.go deleted file mode 100644 index 6a5fa3d..0000000 --- a/functional/cmd/serve.go +++ /dev/null @@ -1,106 +0,0 @@ -package cmd - -import ( - "context" - "os" - - "github.com/sirupsen/logrus" - "github.com/spf13/cobra" - "github.com/pirogoeth/apps/pkg/system" - "github.com/pirogoeth/apps/functional/api" - "github.com/pirogoeth/apps/functional/compute" - "github.com/pirogoeth/apps/functional/database" - "github.com/pirogoeth/apps/functional/types" -) - -const ComponentApi = "api" - -var serveCmd = &cobra.Command{ - Use: "serve", - Short: "Start the FaaS API server", - Run: serveFunc, -} - -func serveFunc(cmd *cobra.Command, args []string) { - cfg := appStart(ComponentApi) - - // Setup database - ctx, cancel := context.WithCancel(context.Background()) - db, err := database.Open(ctx, cfg.Database.Path) - if err != nil { - logrus.WithError(err).Fatal("failed to open database") - } - - // Run migrations - if err := db.RunMigrations(database.MigrationsFS); err != nil { - logrus.WithError(err).Fatal("failed to run database migrations") - } - - // Setup compute registry - computeRegistry := compute.NewRegistry() - - // Register compute providers based on config - switch cfg.Compute.Provider { - case "docker": - if cfg.Compute.Docker == nil { - logrus.Fatal("docker provider selected but docker config is missing") - } - // Convert to local Docker config type - dockerConfig := &compute.DockerConfig{ - Socket: cfg.Compute.Docker.Socket, - Network: cfg.Compute.Docker.Network, - Registry: cfg.Compute.Docker.Registry, - } - dockerProvider := compute.NewDockerProvider(dockerConfig) - computeRegistry.Register(dockerProvider) - case "firecracker": - if cfg.Compute.Firecracker == nil { - logrus.Fatal("firecracker provider selected but firecracker config is missing") - } - // Convert to local Firecracker config type - firecrackerConfig := &compute.FirecrackerConfig{ - KernelImagePath: cfg.Compute.Firecracker.KernelImagePath, - RootfsImagePath: cfg.Compute.Firecracker.RootfsImagePath, - WorkDir: cfg.Compute.Firecracker.WorkDir, - NetworkDevice: cfg.Compute.Firecracker.NetworkDevice, - } - firecrackerProvider := compute.NewFirecrackerProvider(firecrackerConfig) - computeRegistry.Register(firecrackerProvider) - default: - logrus.WithField("provider", cfg.Compute.Provider).Fatal("unsupported compute provider") - } - - // Create API context - apiContext := &types.ApiContext{ - Config: cfg, - Querier: db.Queries, - Compute: computeRegistry, - } - - // Setup router - router, err := system.DefaultRouterWithTracing(ctx, cfg.Tracing) - if err != nil { - logrus.WithError(err).Fatal("failed to setup router with tracing") - } - - // Register API routes - if err := api.MustRegister(router, apiContext); err != nil { - logrus.WithError(err).Fatal("failed to register API routes") - } - - // Start server in goroutine - logrus.WithField("listen_address", cfg.HTTP.ListenAddress).Info("starting FaaS API server") - go router.Run(cfg.HTTP.ListenAddress) - - // Setup graceful shutdown - sw := system.NewSignalWaiter(os.Interrupt) - sw.OnBeforeCancel(func(context.Context) error { - if err := db.Close(); err != nil { - logrus.WithError(err).Error("could not safely close database") - return err - } - logrus.Info("closed database") - return nil - }) - sw.Wait(ctx, cancel) -} \ No newline at end of file diff --git a/functional/compute/docker.go b/functional/compute/docker.go deleted file mode 100644 index c0853c8..0000000 --- a/functional/compute/docker.go +++ /dev/null @@ -1,524 +0,0 @@ -package compute - -import ( - "archive/tar" - "bytes" - "context" - "encoding/json" - "fmt" - "io" - "net/http" - "strings" - "time" - - "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/container" - "github.com/docker/docker/client" - "github.com/docker/go-connections/nat" - "github.com/google/uuid" - "github.com/sirupsen/logrus" - -) - -// Local type definitions to avoid import cycle -type DockerConfig struct { - Socket string `json:"socket"` - Network string `json:"network"` - Registry string `json:"registry"` -} - -type Function struct { - ID string - Name string - Description string - CodePath string - Runtime string - Handler string - TimeoutSeconds int32 - MemoryMB int32 - EnvVars string -} - -type DeployResult struct { - DeploymentID string `json:"deployment_id"` - ResourceID string `json:"resource_id"` - ImageTag string `json:"image_tag"` -} - -type Deployment struct { - ID string - FunctionID string - Provider string - ResourceID string - Status string - Replicas int32 - ImageTag string -} - -type InvocationRequest struct { - FunctionID string - Body []byte - Headers map[string]string - Method string - Path string - QueryArgs map[string]string -} - -type InvocationResult struct { - StatusCode int - Body []byte - Headers map[string]string - DurationMS int64 - MemoryUsedMB int32 - ResponseSize int64 - Logs string - Error string -} - -type DockerProvider struct { - client *client.Client - config *DockerConfig -} - -func NewDockerProvider(config interface{}) *DockerProvider { - dockerConfig, ok := config.(*DockerConfig) - if !ok { - logrus.Fatal("invalid docker config type") - } - - cli, err := client.NewClientWithOpts( - client.FromEnv, - client.WithHost(dockerConfig.Socket), - client.WithAPIVersionNegotiation(), - ) - if err != nil { - logrus.WithError(err).Fatal("failed to create docker client") - } - - return &DockerProvider{ - client: cli, - config: dockerConfig, - } -} - -func (d *DockerProvider) Name() string { - return "docker" -} - -func (d *DockerProvider) Deploy(ctx context.Context, fn interface{}, imageName string) (interface{}, error) { - function, ok := fn.(*Function) - if !ok { - return nil, fmt.Errorf("invalid function type") - } - - logrus. - WithField("function_id", function.ID). - WithField("function_name", function.Name). - Info("starting function deployment") - - // Build function image - imageTag, err := d.buildFunctionImage(ctx, function) - if err != nil { - return nil, fmt.Errorf("failed to build function image: %w", err) - } - - // Create container - containerID, err := d.createContainer(ctx, function, imageTag) - if err != nil { - return nil, fmt.Errorf("failed to create container: %w", err) - } - - // Start container - if err := d.client.ContainerStart(ctx, containerID, container.StartOptions{}); err != nil { - return nil, fmt.Errorf("failed to start container: %w", err) - } - - logrus. - WithField("function_id", function.ID). - WithField("container_id", containerID). - WithField("image_tag", imageTag). - Info("function deployed successfully") - - return &DeployResult{ - DeploymentID: uuid.New().String(), - ResourceID: containerID, - ImageTag: imageTag, - }, nil -} - -func (d *DockerProvider) Execute(ctx context.Context, deployment interface{}, req interface{}) (interface{}, error) { - dep, ok := deployment.(*Deployment) - if !ok { - return nil, fmt.Errorf("invalid deployment type") - } - - invReq, ok := req.(*InvocationRequest) - if !ok { - return nil, fmt.Errorf("invalid invocation request type") - } - - // Get container port - containerInfo, err := d.client.ContainerInspect(ctx, dep.ResourceID) - if err != nil { - return nil, fmt.Errorf("failed to inspect container: %w", err) - } - - // Find the exposed port - var port string - for containerPort := range containerInfo.Config.ExposedPorts { - port = containerPort.Port() - break - } - - if port == "" { - return nil, fmt.Errorf("no exposed ports found in container") - } - - // Get container IP or use localhost if port is bound - var endpoint string - if binding, ok := containerInfo.NetworkSettings.Ports[nat.Port(port+"/tcp")]; ok && len(binding) > 0 { - endpoint = fmt.Sprintf("http://localhost:%s", binding[0].HostPort) - } else { - endpoint = fmt.Sprintf("http://%s:%s", containerInfo.NetworkSettings.IPAddress, port) - } - - // Execute HTTP request to function - start := time.Now() - result, err := d.executeFunctionHTTP(ctx, endpoint, invReq) - duration := time.Since(start) - - if err != nil { - return &InvocationResult{ - StatusCode: 500, - Body: []byte(fmt.Sprintf("Function execution failed: %v", err)), - DurationMS: duration.Milliseconds(), - Error: err.Error(), - }, nil - } - - result.DurationMS = duration.Milliseconds() - return result, nil -} - -func (d *DockerProvider) Scale(ctx context.Context, deployment interface{}, replicas int) error { - // For now, Docker provider doesn't support scaling (single container per function) - // This would require implementing load balancing and multiple containers - logrus.WithField("replicas", replicas).Warn("docker provider scaling not implemented") - return nil -} - -func (d *DockerProvider) Remove(ctx context.Context, deployment interface{}) error { - dep, ok := deployment.(*Deployment) - if !ok { - return fmt.Errorf("invalid deployment type") - } - - logrus.WithField("container_id", dep.ResourceID).Info("removing container") - - // Stop container - timeoutSeconds := 10 - if err := d.client.ContainerStop(ctx, dep.ResourceID, container.StopOptions{Timeout: &timeoutSeconds}); err != nil { - logrus.WithError(err).Warn("failed to stop container gracefully") - } - - // Remove container - if err := d.client.ContainerRemove(ctx, dep.ResourceID, container.RemoveOptions{Force: true}); err != nil { - return fmt.Errorf("failed to remove container: %w", err) - } - - return nil -} - -func (d *DockerProvider) Health(ctx context.Context) error { - // Check Docker daemon connectivity - _, err := d.client.Ping(ctx) - if err != nil { - return fmt.Errorf("docker daemon not accessible: %w", err) - } - - // TODO: Check if network exists and registry is accessible - return nil -} - -// Helper methods - -func (d *DockerProvider) buildFunctionImage(ctx context.Context, function *Function) (string, error) { - // Decode function code from base64 - // For now, assume the code is stored somewhere accessible - // In a real implementation, we'd get the code from CodePath or decode from request - - // Create a simple Dockerfile for the function - dockerfile := d.generateDockerfile(function) - - // Create build context - buildContext, err := d.createBuildContext(dockerfile, function) - if err != nil { - return "", fmt.Errorf("failed to create build context: %w", err) - } - - imageTag := fmt.Sprintf("function-%s:%s", function.Name, function.ID[:8]) - - // Build image - buildResp, err := d.client.ImageBuild(ctx, buildContext, types.ImageBuildOptions{ - Tags: []string{imageTag}, - Dockerfile: "Dockerfile", - Remove: true, - }) - if err != nil { - return "", fmt.Errorf("failed to build image: %w", err) - } - defer buildResp.Body.Close() - - // Read build output (for debugging) - _, err = io.Copy(io.Discard, buildResp.Body) - if err != nil { - return "", fmt.Errorf("failed to read build response: %w", err) - } - - return imageTag, nil -} - -func (d *DockerProvider) createContainer(ctx context.Context, function *Function, imageTag string) (string, error) { - // Parse environment variables - envVars := []string{} - if function.EnvVars != "" { - var envMap map[string]string - if err := json.Unmarshal([]byte(function.EnvVars), &envMap); err == nil { - for k, v := range envMap { - envVars = append(envVars, fmt.Sprintf("%s=%s", k, v)) - } - } - } - - // Create container config - config := &container.Config{ - Image: imageTag, - Env: envVars, - ExposedPorts: nat.PortSet{"8080/tcp": struct{}{}}, - } - - // Create host config with port binding - hostConfig := &container.HostConfig{ - PortBindings: nat.PortMap{ - "8080/tcp": []nat.PortBinding{ - { - HostIP: "0.0.0.0", - HostPort: "0", // Auto-assign port - }, - }, - }, - } - - // Create container - resp, err := d.client.ContainerCreate(ctx, config, hostConfig, nil, nil, "") - if err != nil { - return "", fmt.Errorf("failed to create container: %w", err) - } - - return resp.ID, nil -} - -func (d *DockerProvider) executeFunctionHTTP(ctx context.Context, endpoint string, req *InvocationRequest) (*InvocationResult, error) { - // Create HTTP request - httpReq, err := http.NewRequestWithContext(ctx, req.Method, endpoint+req.Path, bytes.NewReader(req.Body)) - if err != nil { - return nil, fmt.Errorf("failed to create HTTP request: %w", err) - } - - // Add headers - for k, v := range req.Headers { - httpReq.Header.Set(k, v) - } - - // Add query parameters - if len(req.QueryArgs) > 0 { - q := httpReq.URL.Query() - for k, v := range req.QueryArgs { - q.Add(k, v) - } - httpReq.URL.RawQuery = q.Encode() - } - - // Make request - client := &http.Client{Timeout: 30 * time.Second} - resp, err := client.Do(httpReq) - if err != nil { - return nil, fmt.Errorf("HTTP request failed: %w", err) - } - defer resp.Body.Close() - - // Read response - body, err := io.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("failed to read response body: %w", err) - } - - // Convert headers - headers := make(map[string]string) - for k, v := range resp.Header { - if len(v) > 0 { - headers[k] = v[0] - } - } - - return &InvocationResult{ - StatusCode: resp.StatusCode, - Body: body, - Headers: headers, - ResponseSize: int64(len(body)), - }, nil -} - -func (d *DockerProvider) generateDockerfile(function *Function) string { - // Generate a basic Dockerfile based on runtime - switch strings.ToLower(function.Runtime) { - case "node", "nodejs", "node18", "node20": - return `FROM node:18-alpine -WORKDIR /app -COPY package*.json ./ -RUN npm install --production -COPY . . -EXPOSE 8080 -CMD ["node", "index.js"]` - - case "python", "python3", "python3.9", "python3.11": - return `FROM python:3.11-alpine -WORKDIR /app -COPY requirements.txt ./ -RUN pip install --no-cache-dir -r requirements.txt -COPY . . -EXPOSE 8080 -CMD ["python", "app.py"]` - - case "go", "golang": - return `FROM golang:1.21-alpine AS builder -WORKDIR /app -COPY go.mod go.sum ./ -RUN go mod download -COPY . . -RUN go build -o main . - -FROM alpine:latest -RUN apk --no-cache add ca-certificates -WORKDIR /root/ -COPY --from=builder /app/main . -EXPOSE 8080 -CMD ["./main"]` - - default: - // Generic container - return `FROM alpine:latest -WORKDIR /app -COPY . . -EXPOSE 8080 -CMD ["sh", "-c", "echo 'Runtime not supported: ` + function.Runtime + `' && exit 1"]` - } -} - -func (d *DockerProvider) createBuildContext(dockerfile string, function *Function) (io.Reader, error) { - // Create a tar archive with Dockerfile and function code - var buf bytes.Buffer - tw := tar.NewWriter(&buf) - - // Add Dockerfile - dockerfileHeader := &tar.Header{ - Name: "Dockerfile", - Mode: 0644, - Size: int64(len(dockerfile)), - } - if err := tw.WriteHeader(dockerfileHeader); err != nil { - return nil, err - } - if _, err := tw.Write([]byte(dockerfile)); err != nil { - return nil, err - } - - // Add function code (simplified - in reality we'd extract from CodePath or decode from request) - functionCode := d.generateSampleCode(function) - - for filename, content := range functionCode { - header := &tar.Header{ - Name: filename, - Mode: 0644, - Size: int64(len(content)), - } - if err := tw.WriteHeader(header); err != nil { - return nil, err - } - if _, err := tw.Write([]byte(content)); err != nil { - return nil, err - } - } - - if err := tw.Close(); err != nil { - return nil, err - } - - return &buf, nil -} - -func (d *DockerProvider) generateSampleCode(function *Function) map[string]string { - // Generate sample function code based on runtime - // In a real implementation, this would come from the function's actual code - - switch strings.ToLower(function.Runtime) { - case "node", "nodejs", "node18", "node20": - return map[string]string{ - "package.json": `{ - "name": "` + function.Name + `", - "version": "1.0.0", - "main": "index.js", - "dependencies": { - "express": "^4.18.0" - } -}`, - "index.js": `const express = require('express'); -const app = express(); -app.use(express.json()); - -app.all('*', (req, res) => { - res.json({ - message: 'Hello from ` + function.Name + `', - method: req.method, - path: req.path, - headers: req.headers, - body: req.body, - query: req.query - }); -}); - -app.listen(8080, () => { - console.log('Function ` + function.Name + ` listening on port 8080'); -});`, - } - - case "python", "python3", "python3.9", "python3.11": - return map[string]string{ - "requirements.txt": "flask==2.3.0", - "app.py": `from flask import Flask, request, jsonify -import json - -app = Flask(__name__) - -@app.route('/', defaults={'path': ''}, methods=['GET', 'POST', 'PUT', 'DELETE']) -@app.route('/', methods=['GET', 'POST', 'PUT', 'DELETE']) -def handler(path): - return jsonify({ - 'message': 'Hello from ` + function.Name + `', - 'method': request.method, - 'path': '/' + path, - 'headers': dict(request.headers), - 'body': request.get_data(as_text=True), - 'args': dict(request.args) - }) - -if __name__ == '__main__': - app.run(host='0.0.0.0', port=8080)`, - } - - default: - return map[string]string{ - "README.md": "Runtime " + function.Runtime + " not supported yet", - } - } -} \ No newline at end of file diff --git a/functional/compute/docker_integration_test.go b/functional/compute/docker_integration_test.go deleted file mode 100644 index ea06a35..0000000 --- a/functional/compute/docker_integration_test.go +++ /dev/null @@ -1,204 +0,0 @@ -//go:build integration - -package compute - -import ( - "context" - "testing" - "time" -) - -// Integration tests that require a running Docker daemon -// Run with: go test -tags=integration - -func TestDockerProvider_Integration_Health(t *testing.T) { - config := &DockerConfig{ - Socket: "unix:///var/run/docker.sock", - Network: "bridge", - Registry: "", - } - - provider := NewDockerProvider(config) - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - err := provider.Health(ctx) - if err != nil { - t.Skipf("Docker daemon not available: %v", err) - } -} - -func TestDockerProvider_Integration_DeployAndExecute(t *testing.T) { - config := &DockerConfig{ - Socket: "unix:///var/run/docker.sock", - Network: "bridge", - Registry: "", - } - - provider := NewDockerProvider(config) - ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) - defer cancel() - - // Check if Docker is available - if err := provider.Health(ctx); err != nil { - t.Skipf("Docker daemon not available: %v", err) - } - - // Test function - function := &Function{ - ID: "integration-test-function", - Name: "test-function", - Description: "Integration test function", - Runtime: "nodejs", - Handler: "index.handler", - TimeoutSeconds: 30, - MemoryMB: 128, - EnvVars: `{"TEST_ENV": "integration"}`, - } - - t.Run("Deploy", func(t *testing.T) { - result, err := provider.Deploy(ctx, function, "") - if err != nil { - t.Fatalf("Deploy failed: %v", err) - } - - deployResult, ok := result.(*DeployResult) - if !ok { - t.Fatalf("Expected DeployResult, got %T", result) - } - - if deployResult.DeploymentID == "" { - t.Errorf("Expected non-empty deployment ID") - } - - if deployResult.ResourceID == "" { - t.Errorf("Expected non-empty resource ID") - } - - if deployResult.ImageTag == "" { - t.Errorf("Expected non-empty image tag") - } - - // Test execution - deployment := &Deployment{ - ID: deployResult.DeploymentID, - FunctionID: function.ID, - Provider: "docker", - ResourceID: deployResult.ResourceID, - Status: "running", - Replicas: 1, - ImageTag: deployResult.ImageTag, - } - - // Wait for container to be ready - time.Sleep(3 * time.Second) - - t.Run("Execute", func(t *testing.T) { - request := &InvocationRequest{ - FunctionID: function.ID, - Body: []byte(`{"test": "integration"}`), - Headers: map[string]string{"Content-Type": "application/json"}, - Method: "POST", - Path: "/", - QueryArgs: map[string]string{"param": "value"}, - } - - result, err := provider.Execute(ctx, deployment, request) - if err != nil { - t.Fatalf("Execute failed: %v", err) - } - - invResult, ok := result.(*InvocationResult) - if !ok { - t.Fatalf("Expected InvocationResult, got %T", result) - } - - if invResult.StatusCode == 0 { - t.Errorf("Expected non-zero status code") - } - - if invResult.DurationMS <= 0 { - t.Errorf("Expected positive duration, got %d", invResult.DurationMS) - } - - if invResult.ResponseSize <= 0 { - t.Errorf("Expected positive response size, got %d", invResult.ResponseSize) - } - }) - - // Cleanup - t.Run("Remove", func(t *testing.T) { - err := provider.Remove(ctx, deployment) - if err != nil { - t.Errorf("Remove failed: %v", err) - } - }) - }) -} - -func TestDockerProvider_Integration_MultipleRuntimes(t *testing.T) { - config := &DockerConfig{ - Socket: "unix:///var/run/docker.sock", - Network: "bridge", - Registry: "", - } - - provider := NewDockerProvider(config) - ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second) - defer cancel() - - // Check if Docker is available - if err := provider.Health(ctx); err != nil { - t.Skipf("Docker daemon not available: %v", err) - } - - runtimes := []struct { - name string - runtime string - }{ - {"NodeJS", "nodejs"}, - {"Python", "python3"}, - {"Go", "go"}, - } - - for _, rt := range runtimes { - t.Run(rt.name, func(t *testing.T) { - function := &Function{ - ID: "integration-test-" + rt.runtime, - Name: "test-" + rt.runtime, - Runtime: rt.runtime, - TimeoutSeconds: 60, - MemoryMB: 128, - } - - // Deploy - result, err := provider.Deploy(ctx, function, "") - if err != nil { - t.Fatalf("Deploy failed for %s: %v", rt.runtime, err) - } - - deployResult := result.(*DeployResult) - deployment := &Deployment{ - ID: deployResult.DeploymentID, - ResourceID: deployResult.ResourceID, - ImageTag: deployResult.ImageTag, - } - - // Wait for container startup - time.Sleep(5 * time.Second) - - // Execute - request := &InvocationRequest{ - Method: "GET", - Path: "/", - } - - _, err = provider.Execute(ctx, deployment, request) - // We don't check for success here as the sample functions may not be fully functional - // The main test is that deployment works without errors - - // Cleanup - provider.Remove(ctx, deployment) - }) - } -} \ No newline at end of file diff --git a/functional/compute/docker_test.go b/functional/compute/docker_test.go deleted file mode 100644 index d886213..0000000 --- a/functional/compute/docker_test.go +++ /dev/null @@ -1,319 +0,0 @@ -package compute - -import ( - "context" - "testing" -) - -func TestDockerProvider_Name(t *testing.T) { - config := &DockerConfig{ - Socket: "unix:///var/run/docker.sock", - Network: "bridge", - Registry: "localhost:5000", - } - - provider := NewDockerProvider(config) - - if provider.Name() != "docker" { - t.Errorf("Expected provider name to be 'docker', got %s", provider.Name()) - } -} - -func TestDockerProvider_Health(t *testing.T) { - tests := []struct { - name string - expectError bool - }{ - { - name: "healthy docker daemon", - expectError: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // This test would require mocking the Docker client - // For integration tests, we'd test against real Docker - t.Skip("Requires Docker client mocking - see integration tests") - }) - } -} - -func TestDockerProvider_generateDockerfile(t *testing.T) { - provider := &DockerProvider{} - - tests := []struct { - name string - function *Function - expected string - }{ - { - name: "nodejs runtime", - function: &Function{ - ID: "test-id", - Name: "test-function", - Runtime: "nodejs", - }, - expected: `FROM node:18-alpine -WORKDIR /app -COPY package*.json ./ -RUN npm install --production -COPY . . -EXPOSE 8080 -CMD ["node", "index.js"]`, - }, - { - name: "python runtime", - function: &Function{ - ID: "test-id", - Name: "test-function", - Runtime: "python3", - }, - expected: `FROM python:3.11-alpine -WORKDIR /app -COPY requirements.txt ./ -RUN pip install --no-cache-dir -r requirements.txt -COPY . . -EXPOSE 8080 -CMD ["python", "app.py"]`, - }, - { - name: "go runtime", - function: &Function{ - ID: "test-id", - Name: "test-function", - Runtime: "go", - }, - expected: `FROM golang:1.21-alpine AS builder -WORKDIR /app -COPY go.mod go.sum ./ -RUN go mod download -COPY . . -RUN go build -o main . - -FROM alpine:latest -RUN apk --no-cache add ca-certificates -WORKDIR /root/ -COPY --from=builder /app/main . -EXPOSE 8080 -CMD ["./main"]`, - }, - { - name: "unsupported runtime", - function: &Function{ - ID: "test-id", - Name: "test-function", - Runtime: "unsupported", - }, - expected: `FROM alpine:latest -WORKDIR /app -COPY . . -EXPOSE 8080 -CMD ["sh", "-c", "echo 'Runtime not supported: unsupported' && exit 1"]`, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := provider.generateDockerfile(tt.function) - if result != tt.expected { - t.Errorf("generateDockerfile() = %v, want %v", result, tt.expected) - } - }) - } -} - -func TestDockerProvider_generateSampleCode(t *testing.T) { - provider := &DockerProvider{} - - tests := []struct { - name string - function *Function - checkKey string - }{ - { - name: "nodejs runtime generates package.json", - function: &Function{ - ID: "test-id", - Name: "test-function", - Runtime: "nodejs", - }, - checkKey: "package.json", - }, - { - name: "python runtime generates requirements.txt", - function: &Function{ - ID: "test-id", - Name: "test-function", - Runtime: "python3", - }, - checkKey: "requirements.txt", - }, - { - name: "unsupported runtime generates README", - function: &Function{ - ID: "test-id", - Name: "test-function", - Runtime: "unsupported", - }, - checkKey: "README.md", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := provider.generateSampleCode(tt.function) - if _, exists := result[tt.checkKey]; !exists { - t.Errorf("generateSampleCode() missing expected key %s", tt.checkKey) - } - }) - } -} - -func TestNewDockerProvider(t *testing.T) { - t.Skip("Requires Docker daemon - see integration tests") -} - -func TestNewDockerProvider_InvalidConfig(t *testing.T) { - t.Skip("Causes fatal exit - requires different test approach") -} - -// Mock types for testing without Docker dependency -type MockDockerProvider struct { - name string - health error -} - -func (m *MockDockerProvider) Name() string { - return m.name -} - -func (m *MockDockerProvider) Deploy(ctx context.Context, fn interface{}, imageName string) (interface{}, error) { - return &DeployResult{ - DeploymentID: "mock-deployment-id", - ResourceID: "mock-container-id", - ImageTag: "mock-image:latest", - }, nil -} - -func (m *MockDockerProvider) Execute(ctx context.Context, deployment interface{}, req interface{}) (interface{}, error) { - return &InvocationResult{ - StatusCode: 200, - Body: []byte(`{"message": "mock response"}`), - Headers: map[string]string{"Content-Type": "application/json"}, - DurationMS: 100, - MemoryUsedMB: 64, - ResponseSize: 25, - }, nil -} - -func (m *MockDockerProvider) Scale(ctx context.Context, deployment interface{}, replicas int) error { - return nil -} - -func (m *MockDockerProvider) Remove(ctx context.Context, deployment interface{}) error { - return nil -} - -func (m *MockDockerProvider) Health(ctx context.Context) error { - return m.health -} - -func TestMockDockerProvider(t *testing.T) { - mock := &MockDockerProvider{ - name: "mock-docker", - health: nil, - } - - ctx := context.Background() - - t.Run("Name", func(t *testing.T) { - if mock.Name() != "mock-docker" { - t.Errorf("Expected name 'mock-docker', got %s", mock.Name()) - } - }) - - t.Run("Health", func(t *testing.T) { - if err := mock.Health(ctx); err != nil { - t.Errorf("Expected no error, got %v", err) - } - }) - - t.Run("Deploy", func(t *testing.T) { - function := &Function{ - ID: "test-id", - Name: "test-function", - } - - result, err := mock.Deploy(ctx, function, "test-image") - if err != nil { - t.Errorf("Expected no error, got %v", err) - } - - deployResult, ok := result.(*DeployResult) - if !ok { - t.Errorf("Expected DeployResult, got %T", result) - } - - if deployResult.DeploymentID == "" { - t.Errorf("Expected non-empty deployment ID") - } - }) - - t.Run("Execute", func(t *testing.T) { - deployment := &Deployment{ - ID: "test-deployment", - ResourceID: "test-container", - } - - request := &InvocationRequest{ - FunctionID: "test-function", - Body: []byte(`{"test": "data"}`), - Method: "POST", - Path: "/", - } - - result, err := mock.Execute(ctx, deployment, request) - if err != nil { - t.Errorf("Expected no error, got %v", err) - } - - invResult, ok := result.(*InvocationResult) - if !ok { - t.Errorf("Expected InvocationResult, got %T", result) - } - - if invResult.StatusCode != 200 { - t.Errorf("Expected status code 200, got %d", invResult.StatusCode) - } - }) -} - -// Benchmark tests -func BenchmarkDockerProvider_generateDockerfile(b *testing.B) { - provider := &DockerProvider{} - function := &Function{ - ID: "bench-id", - Name: "bench-function", - Runtime: "nodejs", - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - provider.generateDockerfile(function) - } -} - -func BenchmarkDockerProvider_generateSampleCode(b *testing.B) { - provider := &DockerProvider{} - function := &Function{ - ID: "bench-id", - Name: "bench-function", - Runtime: "nodejs", - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - provider.generateSampleCode(function) - } -} \ No newline at end of file diff --git a/functional/compute/firecracker.go b/functional/compute/firecracker.go deleted file mode 100644 index f6e29cb..0000000 --- a/functional/compute/firecracker.go +++ /dev/null @@ -1,559 +0,0 @@ -package compute - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "io" - "net" - "net/http" - "os" - "os/exec" - "path/filepath" - "time" - - "github.com/google/uuid" - "github.com/sirupsen/logrus" -) - -// Local type definitions to avoid import cycle -type FirecrackerConfig struct { - KernelImagePath string `json:"kernel_image_path"` - RootfsImagePath string `json:"rootfs_image_path"` - WorkDir string `json:"work_dir"` - NetworkDevice string `json:"network_device"` -} - -type FirecrackerProvider struct { - config *FirecrackerConfig - vms map[string]*FirecrackerVM // Track running VMs by deployment ID -} - -type FirecrackerVM struct { - ID string - SocketPath string - Process *os.Process - Function *Function - Config *FirecrackerVMConfig -} - -type FirecrackerVMConfig struct { - BootSource BootSource `json:"boot-source"` - Drives []Drive `json:"drives"` - NetworkIfaces []NetworkIface `json:"network-interfaces"` - MachineConfig MachineConfig `json:"machine-config"` -} - -type BootSource struct { - KernelImagePath string `json:"kernel_image_path"` - BootArgs string `json:"boot_args"` - InitrdPath string `json:"initrd_path,omitempty"` -} - -type Drive struct { - DriveID string `json:"drive_id"` - PathOnHost string `json:"path_on_host"` - IsRootDevice bool `json:"is_root_device"` - IsReadOnly bool `json:"is_read_only"` -} - -type NetworkIface struct { - IfaceID string `json:"iface_id"` - GuestMac string `json:"guest_mac"` - HostDevName string `json:"host_dev_name"` -} - -type MachineConfig struct { - VcpuCount int `json:"vcpu_count"` - MemSizeMib int `json:"mem_size_mib"` - SmtEnabled bool `json:"smt"` -} - -func NewFirecrackerProvider(config interface{}) *FirecrackerProvider { - fcConfig, ok := config.(*FirecrackerConfig) - if !ok { - logrus.Fatal("invalid firecracker config type") - } - - return &FirecrackerProvider{ - config: fcConfig, - vms: make(map[string]*FirecrackerVM), - } -} - -func (f *FirecrackerProvider) Name() string { - return "firecracker" -} - -func (f *FirecrackerProvider) Deploy(ctx context.Context, fn interface{}, imageName string) (interface{}, error) { - function, ok := fn.(*Function) - if !ok { - return nil, fmt.Errorf("invalid function type") - } - - logrus. - WithField("function_id", function.ID). - WithField("function_name", function.Name). - Info("starting firecracker function deployment") - - // Create unique VM identifier - vmID := uuid.New().String() - deploymentID := uuid.New().String() - - // Setup VM workspace - vmDir := filepath.Join(f.config.WorkDir, vmID) - if err := os.MkdirAll(vmDir, 0755); err != nil { - return nil, fmt.Errorf("failed to create VM directory: %w", err) - } - - // Create rootfs with function code - _, err := f.createFunctionRootfs(ctx, function, vmDir) - if err != nil { - return nil, fmt.Errorf("failed to create function rootfs: %w", err) - } - - // Make paths absolute - absVmDir, err := filepath.Abs(vmDir) - if err != nil { - return nil, fmt.Errorf("failed to get absolute VM directory path: %w", err) - } - - absKernelPath, err := filepath.Abs(f.config.KernelImagePath) - if err != nil { - return nil, fmt.Errorf("failed to get absolute kernel path: %w", err) - } - - // Create VM configuration - vmConfig := &FirecrackerVMConfig{ - BootSource: BootSource{ - KernelImagePath: absKernelPath, - BootArgs: "console=ttyS0 reboot=k panic=1 pci=off ip=172.16.0.2::172.16.0.1:255.255.255.0::eth0:off", - }, - Drives: []Drive{ - { - DriveID: "rootfs", - PathOnHost: filepath.Join(absVmDir, "rootfs.ext4"), // Already absolute since absVmDir is absolute - IsRootDevice: true, - IsReadOnly: false, - }, - }, - NetworkIfaces: []NetworkIface{ - { - IfaceID: "eth0", - GuestMac: "AA:FC:00:00:00:01", - HostDevName: f.config.NetworkDevice, - }, - }, - MachineConfig: MachineConfig{ - VcpuCount: 1, - MemSizeMib: int(function.MemoryMB), - SmtEnabled: false, - }, - } - - // Start Firecracker VM - vm, err := f.startVM(ctx, absVmDir, vmConfig, function) - if err != nil { - return nil, fmt.Errorf("failed to start firecracker VM: %w", err) - } - - // Store VM reference - f.vms[deploymentID] = vm - - logrus. - WithField("function_id", function.ID). - WithField("vm_id", vmID). - WithField("deployment_id", deploymentID). - Info("firecracker function deployed successfully") - - return &DeployResult{ - DeploymentID: deploymentID, - ResourceID: vmID, - ImageTag: "firecracker-vm", - }, nil -} - -func (f *FirecrackerProvider) Execute(ctx context.Context, deployment interface{}, req interface{}) (interface{}, error) { - dep, ok := deployment.(*Deployment) - if !ok { - return nil, fmt.Errorf("invalid deployment type") - } - - invReq, ok := req.(*InvocationRequest) - if !ok { - return nil, fmt.Errorf("invalid invocation request type") - } - - vm, exists := f.vms[dep.ID] - if !exists { - return nil, fmt.Errorf("VM not found for deployment %s", dep.ID) - } - - // Execute function in VM via HTTP - start := time.Now() - result, err := f.executeFunctionInVM(ctx, vm, invReq) - duration := time.Since(start) - - if err != nil { - return &InvocationResult{ - StatusCode: 500, - Body: []byte(fmt.Sprintf("Function execution failed: %v", err)), - DurationMS: duration.Milliseconds(), - Error: err.Error(), - }, nil - } - - result.DurationMS = duration.Milliseconds() - return result, nil -} - -func (f *FirecrackerProvider) Scale(ctx context.Context, deployment interface{}, replicas int) error { - // Firecracker VMs are single-instance for now - // Scaling would require creating multiple VMs and load balancing - logrus.WithField("replicas", replicas).Warn("firecracker provider scaling not implemented") - return nil -} - -func (f *FirecrackerProvider) Remove(ctx context.Context, deployment interface{}) error { - dep, ok := deployment.(*Deployment) - if !ok { - return fmt.Errorf("invalid deployment type") - } - - vm, exists := f.vms[dep.ID] - if !exists { - return fmt.Errorf("VM not found for deployment %s", dep.ID) - } - - logrus.WithField("vm_id", vm.ID).Info("stopping firecracker VM") - - // Stop the VM - if err := f.stopVM(ctx, vm); err != nil { - logrus.WithError(err).Warn("failed to stop VM gracefully") - } - - // Clean up VM directory - vmDir := filepath.Join(f.config.WorkDir, vm.ID) - if err := os.RemoveAll(vmDir); err != nil { - logrus.WithError(err).Warn("failed to clean up VM directory") - } - - // Remove from tracking - delete(f.vms, dep.ID) - - return nil -} - -func (f *FirecrackerProvider) Health(ctx context.Context) error { - // Check if firecracker binary is available - if _, err := exec.LookPath("firecracker"); err != nil { - return fmt.Errorf("firecracker binary not found: %w", err) - } - - // Check if kernel and rootfs images exist - if _, err := os.Stat(f.config.KernelImagePath); err != nil { - return fmt.Errorf("kernel image not found: %w", err) - } - - if _, err := os.Stat(f.config.RootfsImagePath); err != nil { - return fmt.Errorf("rootfs image not found: %w", err) - } - - // Check work directory is accessible - if err := os.MkdirAll(f.config.WorkDir, 0755); err != nil { - return fmt.Errorf("work directory not accessible: %w", err) - } - - return nil -} - -// Helper methods - -func (f *FirecrackerProvider) createFunctionRootfs(ctx context.Context, function *Function, vmDir string) (string, error) { - // For now, copy the base rootfs and add function code - // In a real implementation, we'd customize the rootfs with the actual function code - - baseRootfsPath := f.config.RootfsImagePath - functionRootfsPath := filepath.Join(vmDir, "rootfs.ext4") - - logrus.WithFields(logrus.Fields{ - "base_rootfs_path": baseRootfsPath, - "function_rootfs_path": functionRootfsPath, - "vm_dir": vmDir, - }).Info("creating function rootfs") - - // Copy base rootfs - if err := f.copyFile(baseRootfsPath, functionRootfsPath); err != nil { - return "", fmt.Errorf("failed to copy base rootfs: %w", err) - } - - // TODO: Mount rootfs, inject function code, and unmount - // For now, we'll use the base rootfs with a simple HTTP server - - return functionRootfsPath, nil -} - -func (f *FirecrackerProvider) startVM(ctx context.Context, absVmDir string, config *FirecrackerVMConfig, function *Function) (*FirecrackerVM, error) { - // Use shorter socket path to avoid SUN_LEN limit (108 chars) - socketPath := filepath.Join(absVmDir, "fc.sock") - vmID := filepath.Base(absVmDir) - - // Start Firecracker process - cmd := exec.CommandContext(ctx, "firecracker", "--api-sock", socketPath) - cmd.Dir = absVmDir - - // Redirect logs - logFile, err := os.Create(filepath.Join(absVmDir, "firecracker.log")) - if err != nil { - return nil, fmt.Errorf("failed to create log file: %w", err) - } - cmd.Stdout = logFile - cmd.Stderr = logFile - - if err := cmd.Start(); err != nil { - return nil, fmt.Errorf("failed to start firecracker: %w", err) - } - - // Wait for socket to be available - if err := f.waitForSocket(socketPath, 10*time.Second); err != nil { - cmd.Process.Kill() - return nil, fmt.Errorf("firecracker socket not available: %w", err) - } - - // Configure VM via API - if err := f.configureVM(socketPath, config); err != nil { - cmd.Process.Kill() - return nil, fmt.Errorf("failed to configure VM: %w", err) - } - - // Start VM - if err := f.startVMInstance(socketPath); err != nil { - cmd.Process.Kill() - return nil, fmt.Errorf("failed to start VM instance: %w", err) - } - - vm := &FirecrackerVM{ - ID: vmID, - SocketPath: socketPath, - Process: cmd.Process, - Function: function, - Config: config, - } - - return vm, nil -} - -func (f *FirecrackerProvider) waitForSocket(socketPath string, timeout time.Duration) error { - deadline := time.Now().Add(timeout) - for time.Now().Before(deadline) { - if _, err := os.Stat(socketPath); err == nil { - return nil - } - time.Sleep(100 * time.Millisecond) - } - return fmt.Errorf("socket not available within timeout") -} - -func (f *FirecrackerProvider) configureVM(socketPath string, config *FirecrackerVMConfig) error { - client := &http.Client{ - Transport: &http.Transport{ - DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { - return net.Dial("unix", socketPath) - }, - }, - } - - // Configure boot source - if err := f.apiCall(client, "PUT", "http://localhost/boot-source", config.BootSource); err != nil { - return fmt.Errorf("failed to configure boot source: %w", err) - } - - // Configure drives - for _, drive := range config.Drives { - if err := f.apiCall(client, "PUT", fmt.Sprintf("http://localhost/drives/%s", drive.DriveID), drive); err != nil { - return fmt.Errorf("failed to configure drive %s: %w", drive.DriveID, err) - } - } - - // Configure network interfaces - for _, iface := range config.NetworkIfaces { - if err := f.apiCall(client, "PUT", fmt.Sprintf("http://localhost/network-interfaces/%s", iface.IfaceID), iface); err != nil { - return fmt.Errorf("failed to configure network interface %s: %w", iface.IfaceID, err) - } - } - - // Configure machine - if err := f.apiCall(client, "PUT", "http://localhost/machine-config", config.MachineConfig); err != nil { - return fmt.Errorf("failed to configure machine: %w", err) - } - - return nil -} - -func (f *FirecrackerProvider) startVMInstance(socketPath string) error { - client := &http.Client{ - Transport: &http.Transport{ - DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { - return net.Dial("unix", socketPath) - }, - }, - } - - startAction := map[string]string{"action_type": "InstanceStart"} - return f.apiCall(client, "PUT", "http://localhost/actions", startAction) -} - -func (f *FirecrackerProvider) apiCall(client *http.Client, method, url string, data interface{}) error { - var body io.Reader - if data != nil { - jsonData, err := json.Marshal(data) - if err != nil { - return fmt.Errorf("failed to marshal request data: %w", err) - } - body = bytes.NewReader(jsonData) - } - - req, err := http.NewRequest(method, url, body) - if err != nil { - return fmt.Errorf("failed to create request: %w", err) - } - - if data != nil { - req.Header.Set("Content-Type", "application/json") - } - - resp, err := client.Do(req) - if err != nil { - return fmt.Errorf("request failed: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode >= 400 { - respBody, _ := io.ReadAll(resp.Body) - return fmt.Errorf("API call failed with status %d: %s", resp.StatusCode, string(respBody)) - } - - return nil -} - -func (f *FirecrackerProvider) executeFunctionInVM(ctx context.Context, vm *FirecrackerVM, req *InvocationRequest) (*InvocationResult, error) { - // Try to connect to the VM via HTTP - // For now, we'll assume there's a simple HTTP server running on port 8080 in the VM - endpoint := "http://172.16.0.2:8080" - - client := &http.Client{Timeout: 10 * time.Second} - - // Create HTTP request to the VM - httpReq, err := http.NewRequestWithContext(ctx, req.Method, endpoint+req.Path, bytes.NewReader(req.Body)) - if err != nil { - return f.createErrorResult(fmt.Errorf("failed to create HTTP request: %w", err), vm), nil - } - - // Add headers - for k, v := range req.Headers { - httpReq.Header.Set(k, v) - } - - // Add query parameters - if len(req.QueryArgs) > 0 { - q := httpReq.URL.Query() - for k, v := range req.QueryArgs { - q.Add(k, v) - } - httpReq.URL.RawQuery = q.Encode() - } - - // Make request to VM - resp, err := client.Do(httpReq) - if err != nil { - // If we can't connect, return a test response showing the VM is running - testResponse := fmt.Sprintf(`{ - "message": "Firecracker VM is running (network test)", - "function_id": "%s", - "function_name": "%s", - "vm_id": "%s", - "method": "%s", - "path": "%s", - "note": "VM started successfully, but no HTTP server responding yet" - }`, vm.Function.ID, vm.Function.Name, vm.ID, req.Method, req.Path) - - return &InvocationResult{ - StatusCode: 200, - Body: []byte(testResponse), - Headers: map[string]string{"Content-Type": "application/json"}, - ResponseSize: int64(len(testResponse)), - Logs: fmt.Sprintf("VM %s is running, network error: %v", vm.ID, err), - }, nil - } - defer resp.Body.Close() - - // Read response from VM - body, err := io.ReadAll(resp.Body) - if err != nil { - return f.createErrorResult(fmt.Errorf("failed to read VM response: %w", err), vm), nil - } - - // Convert headers - headers := make(map[string]string) - for k, v := range resp.Header { - if len(v) > 0 { - headers[k] = v[0] - } - } - - return &InvocationResult{ - StatusCode: resp.StatusCode, - Body: body, - Headers: headers, - ResponseSize: int64(len(body)), - Logs: fmt.Sprintf("Function executed successfully in VM %s", vm.ID), - }, nil -} - -func (f *FirecrackerProvider) createErrorResult(err error, vm *FirecrackerVM) *InvocationResult { - response := fmt.Sprintf(`{ - "error": "%s", - "vm_id": "%s", - "function_id": "%s" - }`, err.Error(), vm.ID, vm.Function.ID) - - return &InvocationResult{ - StatusCode: 500, - Body: []byte(response), - Headers: map[string]string{"Content-Type": "application/json"}, - ResponseSize: int64(len(response)), - Error: err.Error(), - } -} - -func (f *FirecrackerProvider) stopVM(ctx context.Context, vm *FirecrackerVM) error { - if vm.Process != nil { - // Try graceful shutdown first - if err := vm.Process.Signal(os.Interrupt); err != nil { - // Force kill if graceful shutdown fails - return vm.Process.Kill() - } - - // Wait for process to exit - vm.Process.Wait() - } - return nil -} - -func (f *FirecrackerProvider) copyFile(src, dst string) error { - sourceFile, err := os.Open(src) - if err != nil { - return err - } - defer sourceFile.Close() - - destFile, err := os.Create(dst) - if err != nil { - return err - } - defer destFile.Close() - - _, err = io.Copy(destFile, sourceFile) - return err -} \ No newline at end of file diff --git a/functional/compute/registry.go b/functional/compute/registry.go deleted file mode 100644 index 82ee1f4..0000000 --- a/functional/compute/registry.go +++ /dev/null @@ -1,44 +0,0 @@ -package compute - -import ( - "context" - "fmt" -) - -type ComputeProvider interface { - Name() string - Deploy(ctx context.Context, fn interface{}, imageName string) (interface{}, error) - Execute(ctx context.Context, deployment interface{}, req interface{}) (interface{}, error) - Scale(ctx context.Context, deployment interface{}, replicas int) error - Remove(ctx context.Context, deployment interface{}) error - Health(ctx context.Context) error -} - -type Registry struct { - providers map[string]ComputeProvider -} - -func NewRegistry() *Registry { - return &Registry{ - providers: make(map[string]ComputeProvider), - } -} - -func (r *Registry) Register(provider ComputeProvider) { - r.providers[provider.Name()] = provider -} - -func (r *Registry) Get(name string) (ComputeProvider, error) { - if provider, ok := r.providers[name]; ok { - return provider, nil - } - return nil, fmt.Errorf("compute provider %q not found", name) -} - -func (r *Registry) List() []string { - names := make([]string, 0, len(r.providers)) - for name := range r.providers { - names = append(names, name) - } - return names -} \ No newline at end of file diff --git a/functional/config.yml b/functional/config.yml deleted file mode 100644 index e6d3dd1..0000000 --- a/functional/config.yml +++ /dev/null @@ -1,35 +0,0 @@ -http: - listen_address: "0.0.0.0:8080" - -tracing: - enabled: false - service_name: "functional" - endpoint: "http://localhost:4317" - -database: - path: "functional.db" - -compute: - provider: "docker" - docker: - socket: "unix:///var/run/docker.sock" - network: "functional-net" - registry: "localhost:5000" - firecracker: - kernel_image_path: "./firecracker/vmlinux-6.1.128" - rootfs_image_path: "./firecracker/ubuntu-24.04.ext4" - work_dir: "./firecracker-vms" - network_device: "firecracker0" - -storage: - functions_path: "./functions" - temp_path: "./tmp" - -runtime: - max_concurrent_executions: 100 - default_timeout: 30s - scaling: - min_replicas: 1 - max_replicas: 10 - scale_up_threshold: 0.8 - scale_down_threshold: 0.2 diff --git a/functional/database/config.go b/functional/database/config.go deleted file mode 100644 index fdec9ed..0000000 --- a/functional/database/config.go +++ /dev/null @@ -1,5 +0,0 @@ -package database - -type Config struct { - Path string `json:"path"` -} \ No newline at end of file diff --git a/functional/database/database_test.go b/functional/database/database_test.go deleted file mode 100644 index 8e9636a..0000000 --- a/functional/database/database_test.go +++ /dev/null @@ -1,448 +0,0 @@ -package database - -import ( - "context" - "database/sql" - "embed" - "path/filepath" - "testing" - - _ "github.com/mattn/go-sqlite3" -) - -//go:embed migrations/*.sql -var testMigrations embed.FS - -func setupTestDB(t *testing.T) *DbWrapper { - // Create temporary database file - tempDir := t.TempDir() - dbPath := filepath.Join(tempDir, "test.db") - - ctx := context.Background() - db, err := Open(ctx, dbPath) - if err != nil { - t.Fatalf("Failed to open test database: %v", err) - } - - // Run migrations - if err := db.RunMigrations(testMigrations); err != nil { - t.Fatalf("Failed to run migrations: %v", err) - } - - return db -} - -func TestDbWrapper_Open(t *testing.T) { - tempDir := t.TempDir() - dbPath := filepath.Join(tempDir, "test.db") - - ctx := context.Background() - db, err := Open(ctx, dbPath) - if err != nil { - t.Fatalf("Failed to open database: %v", err) - } - defer db.Close() - - // Verify database connection - if err := db.DB().Ping(); err != nil { - t.Errorf("Database ping failed: %v", err) - } -} - -func TestDbWrapper_Close(t *testing.T) { - db := setupTestDB(t) - - err := db.Close() - if err != nil { - t.Errorf("Failed to close database: %v", err) - } - - // Verify database is closed - if err := db.DB().Ping(); err == nil { - t.Errorf("Expected database to be closed, but ping succeeded") - } -} - -func TestDbWrapper_RunMigrations(t *testing.T) { - tempDir := t.TempDir() - dbPath := filepath.Join(tempDir, "test.db") - - ctx := context.Background() - db, err := Open(ctx, dbPath) - if err != nil { - t.Fatalf("Failed to open database: %v", err) - } - defer db.Close() - - // Run migrations - err = db.RunMigrations(testMigrations) - if err != nil { - t.Errorf("Failed to run migrations: %v", err) - } - - // Verify tables exist - tables := []string{"functions", "deployments", "invocations"} - for _, table := range tables { - var exists bool - query := "SELECT EXISTS(SELECT name FROM sqlite_master WHERE type='table' AND name=?)" - err := db.DB().QueryRow(query, table).Scan(&exists) - if err != nil { - t.Errorf("Failed to check if table %s exists: %v", table, err) - } - if !exists { - t.Errorf("Table %s does not exist after migrations", table) - } - } -} - -func TestQueries_CreateFunction(t *testing.T) { - db := setupTestDB(t) - defer db.Close() - - ctx := context.Background() - - params := CreateFunctionParams{ - ID: "test-function-id", - Name: "test-function", - Description: sql.NullString{String: "Test function description", Valid: true}, - CodePath: "/tmp/test-function", - Runtime: "nodejs", - Handler: "index.handler", - TimeoutSeconds: 30, - MemoryMb: 128, - EnvVars: sql.NullString{String: `{"TEST": "value"}`, Valid: true}, - } - - function, err := db.CreateFunction(ctx, params) - if err != nil { - t.Fatalf("Failed to create function: %v", err) - } - - // Verify function was created correctly - if function.ID != params.ID { - t.Errorf("Expected ID %s, got %s", params.ID, function.ID) - } - - if function.Name != params.Name { - t.Errorf("Expected name %s, got %s", params.Name, function.Name) - } - - if function.Runtime != params.Runtime { - t.Errorf("Expected runtime %s, got %s", params.Runtime, function.Runtime) - } - - if function.TimeoutSeconds != params.TimeoutSeconds { - t.Errorf("Expected timeout %d, got %d", params.TimeoutSeconds, function.TimeoutSeconds) - } - - // Verify timestamps are set - if !function.CreatedAt.Valid || function.CreatedAt.Time.IsZero() { - t.Errorf("Expected valid created_at timestamp") - } -} - -func TestQueries_GetFunction(t *testing.T) { - db := setupTestDB(t) - defer db.Close() - - ctx := context.Background() - - // Create a function first - params := CreateFunctionParams{ - ID: "get-test-function-id", - Name: "get-test-function", - Description: sql.NullString{String: "Get test function", Valid: true}, - CodePath: "/tmp/get-test-function", - Runtime: "python3", - Handler: "app.handler", - TimeoutSeconds: 60, - MemoryMb: 256, - EnvVars: sql.NullString{String: `{"ENV": "test"}`, Valid: true}, - } - - created, err := db.CreateFunction(ctx, params) - if err != nil { - t.Fatalf("Failed to create function: %v", err) - } - - // Get the function - retrieved, err := db.GetFunction(ctx, created.ID) - if err != nil { - t.Fatalf("Failed to get function: %v", err) - } - - // Verify function matches - if retrieved.ID != created.ID { - t.Errorf("Expected ID %s, got %s", created.ID, retrieved.ID) - } - - if retrieved.Name != created.Name { - t.Errorf("Expected name %s, got %s", created.Name, retrieved.Name) - } - - if retrieved.Runtime != created.Runtime { - t.Errorf("Expected runtime %s, got %s", created.Runtime, retrieved.Runtime) - } -} - -func TestQueries_ListFunctions(t *testing.T) { - db := setupTestDB(t) - defer db.Close() - - ctx := context.Background() - - // Create multiple functions - functions := []CreateFunctionParams{ - { - ID: "list-test-1", - Name: "list-function-1", - CodePath: "/tmp/func1", - Runtime: "nodejs", - Handler: "index.handler", - TimeoutSeconds: 30, - MemoryMb: 128, - }, - { - ID: "list-test-2", - Name: "list-function-2", - CodePath: "/tmp/func2", - Runtime: "python3", - Handler: "app.handler", - TimeoutSeconds: 60, - MemoryMb: 256, - }, - } - - for _, params := range functions { - _, err := db.CreateFunction(ctx, params) - if err != nil { - t.Fatalf("Failed to create function %s: %v", params.ID, err) - } - } - - // List functions - listed, err := db.ListFunctions(ctx) - if err != nil { - t.Fatalf("Failed to list functions: %v", err) - } - - if len(listed) < 2 { - t.Errorf("Expected at least 2 functions, got %d", len(listed)) - } - - // Verify functions are in the list - foundIDs := make(map[string]bool) - for _, f := range listed { - foundIDs[f.ID] = true - } - - for _, params := range functions { - if !foundIDs[params.ID] { - t.Errorf("Function %s not found in list", params.ID) - } - } -} - -func TestQueries_CreateDeployment(t *testing.T) { - db := setupTestDB(t) - defer db.Close() - - ctx := context.Background() - - // Create a function first - functionParams := CreateFunctionParams{ - ID: "deploy-test-function", - Name: "deploy-test", - CodePath: "/tmp/deploy-test", - Runtime: "nodejs", - Handler: "index.handler", - TimeoutSeconds: 30, - MemoryMb: 128, - } - - function, err := db.CreateFunction(ctx, functionParams) - if err != nil { - t.Fatalf("Failed to create function: %v", err) - } - - // Create deployment - deployParams := CreateDeploymentParams{ - ID: "test-deployment-id", - FunctionID: function.ID, - Provider: "docker", - ResourceID: "container-123", - Status: "running", - Replicas: 1, - ImageTag: sql.NullString{String: "test-image:latest", Valid: true}, - } - - deployment, err := db.CreateDeployment(ctx, deployParams) - if err != nil { - t.Fatalf("Failed to create deployment: %v", err) - } - - // Verify deployment - if deployment.ID != deployParams.ID { - t.Errorf("Expected ID %s, got %s", deployParams.ID, deployment.ID) - } - - if deployment.FunctionID != deployParams.FunctionID { - t.Errorf("Expected function ID %s, got %s", deployParams.FunctionID, deployment.FunctionID) - } - - if deployment.Provider != deployParams.Provider { - t.Errorf("Expected provider %s, got %s", deployParams.Provider, deployment.Provider) - } - - if deployment.Status != deployParams.Status { - t.Errorf("Expected status %s, got %s", deployParams.Status, deployment.Status) - } -} - -func TestQueries_CreateInvocation(t *testing.T) { - db := setupTestDB(t) - defer db.Close() - - ctx := context.Background() - - // Create function and deployment first - functionParams := CreateFunctionParams{ - ID: "invoke-test-function", - Name: "invoke-test", - CodePath: "/tmp/invoke-test", - Runtime: "nodejs", - Handler: "index.handler", - TimeoutSeconds: 30, - MemoryMb: 128, - } - - function, err := db.CreateFunction(ctx, functionParams) - if err != nil { - t.Fatalf("Failed to create function: %v", err) - } - - deployParams := CreateDeploymentParams{ - ID: "invoke-test-deployment", - FunctionID: function.ID, - Provider: "docker", - ResourceID: "container-456", - Status: "running", - Replicas: 1, - } - - deployment, err := db.CreateDeployment(ctx, deployParams) - if err != nil { - t.Fatalf("Failed to create deployment: %v", err) - } - - // Create invocation - invocationParams := CreateInvocationParams{ - ID: "test-invocation-id", - FunctionID: function.ID, - DeploymentID: sql.NullString{String: deployment.ID, Valid: true}, - Status: "pending", - } - - invocation, err := db.CreateInvocation(ctx, invocationParams) - if err != nil { - t.Fatalf("Failed to create invocation: %v", err) - } - - // Verify invocation - if invocation.ID != invocationParams.ID { - t.Errorf("Expected ID %s, got %s", invocationParams.ID, invocation.ID) - } - - if invocation.FunctionID != invocationParams.FunctionID { - t.Errorf("Expected function ID %s, got %s", invocationParams.FunctionID, invocation.FunctionID) - } - - if invocation.Status != invocationParams.Status { - t.Errorf("Expected status %s, got %s", invocationParams.Status, invocation.Status) - } -} - -// Test database constraints and edge cases -func TestDatabase_Constraints(t *testing.T) { - db := setupTestDB(t) - defer db.Close() - - ctx := context.Background() - - t.Run("Duplicate function ID", func(t *testing.T) { - params := CreateFunctionParams{ - ID: "duplicate-test", - Name: "test-function", - CodePath: "/tmp/test", - Runtime: "nodejs", - Handler: "index.handler", - TimeoutSeconds: 30, - MemoryMb: 128, - } - - // Create first function - _, err := db.CreateFunction(ctx, params) - if err != nil { - t.Fatalf("Failed to create first function: %v", err) - } - - // Try to create duplicate - _, err = db.CreateFunction(ctx, params) - if err == nil { - t.Errorf("Expected error when creating duplicate function ID, got nil") - } - }) - - t.Run("Foreign key constraint", func(t *testing.T) { - // Try to create deployment without valid function - deployParams := CreateDeploymentParams{ - ID: "fk-test-deployment", - FunctionID: "non-existent-function", - Provider: "docker", - ResourceID: "container-789", - Status: "running", - Replicas: 1, - } - - _, err := db.CreateDeployment(ctx, deployParams) - if err == nil { - t.Errorf("Expected foreign key constraint error, got nil") - } - }) -} - -// Benchmark tests -func BenchmarkCreateFunction(b *testing.B) { - tempDir := b.TempDir() - dbPath := filepath.Join(tempDir, "bench.db") - - ctx := context.Background() - db, err := Open(ctx, dbPath) - if err != nil { - b.Fatalf("Failed to open database: %v", err) - } - defer db.Close() - - if err := db.RunMigrations(testMigrations); err != nil { - b.Fatalf("Failed to run migrations: %v", err) - } - - b.ResetTimer() - - for i := 0; i < b.N; i++ { - params := CreateFunctionParams{ - ID: "bench-function-" + string(rune(i)), - Name: "bench-function", - CodePath: "/tmp/bench", - Runtime: "nodejs", - Handler: "index.handler", - TimeoutSeconds: 30, - MemoryMb: 128, - } - - _, err := db.CreateFunction(ctx, params) - if err != nil { - b.Fatalf("Failed to create function: %v", err) - } - } -} \ No newline at end of file diff --git a/functional/database/deployments.sql.go b/functional/database/deployments.sql.go deleted file mode 100644 index b154c8e..0000000 --- a/functional/database/deployments.sql.go +++ /dev/null @@ -1,207 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.29.0 -// source: deployments.sql - -package database - -import ( - "context" - "database/sql" -) - -const createDeployment = `-- name: CreateDeployment :one -INSERT INTO deployments ( - id, function_id, provider, resource_id, status, replicas, image_tag -) VALUES ( - ?, ?, ?, ?, ?, ?, ? -) RETURNING id, function_id, provider, resource_id, status, replicas, image_tag, created_at, updated_at -` - -type CreateDeploymentParams struct { - ID string `db:"id" json:"id"` - FunctionID string `db:"function_id" json:"function_id"` - Provider string `db:"provider" json:"provider"` - ResourceID string `db:"resource_id" json:"resource_id"` - Status string `db:"status" json:"status"` - Replicas int64 `db:"replicas" json:"replicas"` - ImageTag sql.NullString `db:"image_tag" json:"image_tag"` -} - -func (q *Queries) CreateDeployment(ctx context.Context, arg CreateDeploymentParams) (Deployment, error) { - row := q.db.QueryRowContext(ctx, createDeployment, - arg.ID, - arg.FunctionID, - arg.Provider, - arg.ResourceID, - arg.Status, - arg.Replicas, - arg.ImageTag, - ) - var i Deployment - err := row.Scan( - &i.ID, - &i.FunctionID, - &i.Provider, - &i.ResourceID, - &i.Status, - &i.Replicas, - &i.ImageTag, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const deleteDeployment = `-- name: DeleteDeployment :exec -DELETE FROM deployments WHERE id = ? -` - -func (q *Queries) DeleteDeployment(ctx context.Context, id string) error { - _, err := q.db.ExecContext(ctx, deleteDeployment, id) - return err -} - -const getActiveDeploymentByFunction = `-- name: GetActiveDeploymentByFunction :one -SELECT id, function_id, provider, resource_id, status, replicas, image_tag, created_at, updated_at FROM deployments -WHERE function_id = ? AND status = 'active' -ORDER BY created_at DESC -LIMIT 1 -` - -func (q *Queries) GetActiveDeploymentByFunction(ctx context.Context, functionID string) (Deployment, error) { - row := q.db.QueryRowContext(ctx, getActiveDeploymentByFunction, functionID) - var i Deployment - err := row.Scan( - &i.ID, - &i.FunctionID, - &i.Provider, - &i.ResourceID, - &i.Status, - &i.Replicas, - &i.ImageTag, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const getDeployment = `-- name: GetDeployment :one -SELECT id, function_id, provider, resource_id, status, replicas, image_tag, created_at, updated_at FROM deployments WHERE id = ? -` - -func (q *Queries) GetDeployment(ctx context.Context, id string) (Deployment, error) { - row := q.db.QueryRowContext(ctx, getDeployment, id) - var i Deployment - err := row.Scan( - &i.ID, - &i.FunctionID, - &i.Provider, - &i.ResourceID, - &i.Status, - &i.Replicas, - &i.ImageTag, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const getDeploymentsByFunction = `-- name: GetDeploymentsByFunction :many -SELECT id, function_id, provider, resource_id, status, replicas, image_tag, created_at, updated_at FROM deployments WHERE function_id = ? ORDER BY created_at DESC -` - -func (q *Queries) GetDeploymentsByFunction(ctx context.Context, functionID string) ([]Deployment, error) { - rows, err := q.db.QueryContext(ctx, getDeploymentsByFunction, functionID) - if err != nil { - return nil, err - } - defer rows.Close() - items := []Deployment{} - for rows.Next() { - var i Deployment - if err := rows.Scan( - &i.ID, - &i.FunctionID, - &i.Provider, - &i.ResourceID, - &i.Status, - &i.Replicas, - &i.ImageTag, - &i.CreatedAt, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Close(); err != nil { - return nil, err - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const updateDeploymentReplicas = `-- name: UpdateDeploymentReplicas :one -UPDATE deployments -SET - replicas = ?, - updated_at = CURRENT_TIMESTAMP -WHERE id = ? -RETURNING id, function_id, provider, resource_id, status, replicas, image_tag, created_at, updated_at -` - -type UpdateDeploymentReplicasParams struct { - Replicas int64 `db:"replicas" json:"replicas"` - ID string `db:"id" json:"id"` -} - -func (q *Queries) UpdateDeploymentReplicas(ctx context.Context, arg UpdateDeploymentReplicasParams) (Deployment, error) { - row := q.db.QueryRowContext(ctx, updateDeploymentReplicas, arg.Replicas, arg.ID) - var i Deployment - err := row.Scan( - &i.ID, - &i.FunctionID, - &i.Provider, - &i.ResourceID, - &i.Status, - &i.Replicas, - &i.ImageTag, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const updateDeploymentStatus = `-- name: UpdateDeploymentStatus :one -UPDATE deployments -SET - status = ?, - updated_at = CURRENT_TIMESTAMP -WHERE id = ? -RETURNING id, function_id, provider, resource_id, status, replicas, image_tag, created_at, updated_at -` - -type UpdateDeploymentStatusParams struct { - Status string `db:"status" json:"status"` - ID string `db:"id" json:"id"` -} - -func (q *Queries) UpdateDeploymentStatus(ctx context.Context, arg UpdateDeploymentStatusParams) (Deployment, error) { - row := q.db.QueryRowContext(ctx, updateDeploymentStatus, arg.Status, arg.ID) - var i Deployment - err := row.Scan( - &i.ID, - &i.FunctionID, - &i.Provider, - &i.ResourceID, - &i.Status, - &i.Replicas, - &i.ImageTag, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} diff --git a/functional/database/functions.sql.go b/functional/database/functions.sql.go deleted file mode 100644 index 593e156..0000000 --- a/functional/database/functions.sql.go +++ /dev/null @@ -1,209 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.29.0 -// source: functions.sql - -package database - -import ( - "context" - "database/sql" -) - -const createFunction = `-- name: CreateFunction :one -INSERT INTO functions ( - id, name, description, code_path, runtime, handler, - timeout_seconds, memory_mb, env_vars -) VALUES ( - ?, ?, ?, ?, ?, ?, ?, ?, ? -) RETURNING id, name, description, code_path, runtime, handler, timeout_seconds, memory_mb, env_vars, created_at, updated_at -` - -type CreateFunctionParams struct { - ID string `db:"id" json:"id"` - Name string `db:"name" json:"name"` - Description sql.NullString `db:"description" json:"description"` - CodePath string `db:"code_path" json:"code_path"` - Runtime string `db:"runtime" json:"runtime"` - Handler string `db:"handler" json:"handler"` - TimeoutSeconds int64 `db:"timeout_seconds" json:"timeout_seconds"` - MemoryMb int64 `db:"memory_mb" json:"memory_mb"` - EnvVars sql.NullString `db:"env_vars" json:"env_vars"` -} - -func (q *Queries) CreateFunction(ctx context.Context, arg CreateFunctionParams) (Function, error) { - row := q.db.QueryRowContext(ctx, createFunction, - arg.ID, - arg.Name, - arg.Description, - arg.CodePath, - arg.Runtime, - arg.Handler, - arg.TimeoutSeconds, - arg.MemoryMb, - arg.EnvVars, - ) - var i Function - err := row.Scan( - &i.ID, - &i.Name, - &i.Description, - &i.CodePath, - &i.Runtime, - &i.Handler, - &i.TimeoutSeconds, - &i.MemoryMb, - &i.EnvVars, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const deleteFunction = `-- name: DeleteFunction :exec -DELETE FROM functions WHERE id = ? -` - -func (q *Queries) DeleteFunction(ctx context.Context, id string) error { - _, err := q.db.ExecContext(ctx, deleteFunction, id) - return err -} - -const getFunction = `-- name: GetFunction :one -SELECT id, name, description, code_path, runtime, handler, timeout_seconds, memory_mb, env_vars, created_at, updated_at FROM functions WHERE id = ? -` - -func (q *Queries) GetFunction(ctx context.Context, id string) (Function, error) { - row := q.db.QueryRowContext(ctx, getFunction, id) - var i Function - err := row.Scan( - &i.ID, - &i.Name, - &i.Description, - &i.CodePath, - &i.Runtime, - &i.Handler, - &i.TimeoutSeconds, - &i.MemoryMb, - &i.EnvVars, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const getFunctionByName = `-- name: GetFunctionByName :one -SELECT id, name, description, code_path, runtime, handler, timeout_seconds, memory_mb, env_vars, created_at, updated_at FROM functions WHERE name = ? -` - -func (q *Queries) GetFunctionByName(ctx context.Context, name string) (Function, error) { - row := q.db.QueryRowContext(ctx, getFunctionByName, name) - var i Function - err := row.Scan( - &i.ID, - &i.Name, - &i.Description, - &i.CodePath, - &i.Runtime, - &i.Handler, - &i.TimeoutSeconds, - &i.MemoryMb, - &i.EnvVars, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} - -const listFunctions = `-- name: ListFunctions :many -SELECT id, name, description, code_path, runtime, handler, timeout_seconds, memory_mb, env_vars, created_at, updated_at FROM functions ORDER BY created_at DESC -` - -func (q *Queries) ListFunctions(ctx context.Context) ([]Function, error) { - rows, err := q.db.QueryContext(ctx, listFunctions) - if err != nil { - return nil, err - } - defer rows.Close() - items := []Function{} - for rows.Next() { - var i Function - if err := rows.Scan( - &i.ID, - &i.Name, - &i.Description, - &i.CodePath, - &i.Runtime, - &i.Handler, - &i.TimeoutSeconds, - &i.MemoryMb, - &i.EnvVars, - &i.CreatedAt, - &i.UpdatedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Close(); err != nil { - return nil, err - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const updateFunction = `-- name: UpdateFunction :one -UPDATE functions -SET - description = ?, - code_path = ?, - runtime = ?, - handler = ?, - timeout_seconds = ?, - memory_mb = ?, - env_vars = ?, - updated_at = CURRENT_TIMESTAMP -WHERE id = ? -RETURNING id, name, description, code_path, runtime, handler, timeout_seconds, memory_mb, env_vars, created_at, updated_at -` - -type UpdateFunctionParams struct { - Description sql.NullString `db:"description" json:"description"` - CodePath string `db:"code_path" json:"code_path"` - Runtime string `db:"runtime" json:"runtime"` - Handler string `db:"handler" json:"handler"` - TimeoutSeconds int64 `db:"timeout_seconds" json:"timeout_seconds"` - MemoryMb int64 `db:"memory_mb" json:"memory_mb"` - EnvVars sql.NullString `db:"env_vars" json:"env_vars"` - ID string `db:"id" json:"id"` -} - -func (q *Queries) UpdateFunction(ctx context.Context, arg UpdateFunctionParams) (Function, error) { - row := q.db.QueryRowContext(ctx, updateFunction, - arg.Description, - arg.CodePath, - arg.Runtime, - arg.Handler, - arg.TimeoutSeconds, - arg.MemoryMb, - arg.EnvVars, - arg.ID, - ) - var i Function - err := row.Scan( - &i.ID, - &i.Name, - &i.Description, - &i.CodePath, - &i.Runtime, - &i.Handler, - &i.TimeoutSeconds, - &i.MemoryMb, - &i.EnvVars, - &i.CreatedAt, - &i.UpdatedAt, - ) - return i, err -} diff --git a/functional/database/invocations.sql.go b/functional/database/invocations.sql.go deleted file mode 100644 index ce89bb2..0000000 --- a/functional/database/invocations.sql.go +++ /dev/null @@ -1,253 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.29.0 -// source: invocations.sql - -package database - -import ( - "context" - "database/sql" -) - -const createInvocation = `-- name: CreateInvocation :one -INSERT INTO invocations ( - id, function_id, deployment_id, status -) VALUES ( - ?, ?, ?, ? -) RETURNING id, function_id, deployment_id, status, duration_ms, memory_used_mb, response_size_bytes, logs, error, created_at, completed_at -` - -type CreateInvocationParams struct { - ID string `db:"id" json:"id"` - FunctionID string `db:"function_id" json:"function_id"` - DeploymentID sql.NullString `db:"deployment_id" json:"deployment_id"` - Status string `db:"status" json:"status"` -} - -func (q *Queries) CreateInvocation(ctx context.Context, arg CreateInvocationParams) (Invocation, error) { - row := q.db.QueryRowContext(ctx, createInvocation, - arg.ID, - arg.FunctionID, - arg.DeploymentID, - arg.Status, - ) - var i Invocation - err := row.Scan( - &i.ID, - &i.FunctionID, - &i.DeploymentID, - &i.Status, - &i.DurationMs, - &i.MemoryUsedMb, - &i.ResponseSizeBytes, - &i.Logs, - &i.Error, - &i.CreatedAt, - &i.CompletedAt, - ) - return i, err -} - -const getInvocation = `-- name: GetInvocation :one -SELECT id, function_id, deployment_id, status, duration_ms, memory_used_mb, response_size_bytes, logs, error, created_at, completed_at FROM invocations WHERE id = ? -` - -func (q *Queries) GetInvocation(ctx context.Context, id string) (Invocation, error) { - row := q.db.QueryRowContext(ctx, getInvocation, id) - var i Invocation - err := row.Scan( - &i.ID, - &i.FunctionID, - &i.DeploymentID, - &i.Status, - &i.DurationMs, - &i.MemoryUsedMb, - &i.ResponseSizeBytes, - &i.Logs, - &i.Error, - &i.CreatedAt, - &i.CompletedAt, - ) - return i, err -} - -const getInvocationStats = `-- name: GetInvocationStats :one -SELECT - COUNT(*) as total_invocations, - COUNT(CASE WHEN status = 'success' THEN 1 END) as successful_invocations, - COUNT(CASE WHEN status = 'error' THEN 1 END) as failed_invocations, - AVG(CASE WHEN duration_ms IS NOT NULL THEN duration_ms END) as avg_duration_ms, - AVG(CASE WHEN memory_used_mb IS NOT NULL THEN memory_used_mb END) as avg_memory_mb -FROM invocations -WHERE function_id = ? AND created_at >= ? -` - -type GetInvocationStatsParams struct { - FunctionID string `db:"function_id" json:"function_id"` - CreatedAt sql.NullTime `db:"created_at" json:"created_at"` -} - -type GetInvocationStatsRow struct { - TotalInvocations int64 `db:"total_invocations" json:"total_invocations"` - SuccessfulInvocations int64 `db:"successful_invocations" json:"successful_invocations"` - FailedInvocations int64 `db:"failed_invocations" json:"failed_invocations"` - AvgDurationMs sql.NullFloat64 `db:"avg_duration_ms" json:"avg_duration_ms"` - AvgMemoryMb sql.NullFloat64 `db:"avg_memory_mb" json:"avg_memory_mb"` -} - -func (q *Queries) GetInvocationStats(ctx context.Context, arg GetInvocationStatsParams) (GetInvocationStatsRow, error) { - row := q.db.QueryRowContext(ctx, getInvocationStats, arg.FunctionID, arg.CreatedAt) - var i GetInvocationStatsRow - err := row.Scan( - &i.TotalInvocations, - &i.SuccessfulInvocations, - &i.FailedInvocations, - &i.AvgDurationMs, - &i.AvgMemoryMb, - ) - return i, err -} - -const listInvocations = `-- name: ListInvocations :many -SELECT id, function_id, deployment_id, status, duration_ms, memory_used_mb, response_size_bytes, logs, error, created_at, completed_at FROM invocations ORDER BY created_at DESC LIMIT ? OFFSET ? -` - -type ListInvocationsParams struct { - Limit int64 `db:"limit" json:"limit"` - Offset int64 `db:"offset" json:"offset"` -} - -func (q *Queries) ListInvocations(ctx context.Context, arg ListInvocationsParams) ([]Invocation, error) { - rows, err := q.db.QueryContext(ctx, listInvocations, arg.Limit, arg.Offset) - if err != nil { - return nil, err - } - defer rows.Close() - items := []Invocation{} - for rows.Next() { - var i Invocation - if err := rows.Scan( - &i.ID, - &i.FunctionID, - &i.DeploymentID, - &i.Status, - &i.DurationMs, - &i.MemoryUsedMb, - &i.ResponseSizeBytes, - &i.Logs, - &i.Error, - &i.CreatedAt, - &i.CompletedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Close(); err != nil { - return nil, err - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const listInvocationsByFunction = `-- name: ListInvocationsByFunction :many -SELECT id, function_id, deployment_id, status, duration_ms, memory_used_mb, response_size_bytes, logs, error, created_at, completed_at FROM invocations -WHERE function_id = ? -ORDER BY created_at DESC -LIMIT ? OFFSET ? -` - -type ListInvocationsByFunctionParams struct { - FunctionID string `db:"function_id" json:"function_id"` - Limit int64 `db:"limit" json:"limit"` - Offset int64 `db:"offset" json:"offset"` -} - -func (q *Queries) ListInvocationsByFunction(ctx context.Context, arg ListInvocationsByFunctionParams) ([]Invocation, error) { - rows, err := q.db.QueryContext(ctx, listInvocationsByFunction, arg.FunctionID, arg.Limit, arg.Offset) - if err != nil { - return nil, err - } - defer rows.Close() - items := []Invocation{} - for rows.Next() { - var i Invocation - if err := rows.Scan( - &i.ID, - &i.FunctionID, - &i.DeploymentID, - &i.Status, - &i.DurationMs, - &i.MemoryUsedMb, - &i.ResponseSizeBytes, - &i.Logs, - &i.Error, - &i.CreatedAt, - &i.CompletedAt, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Close(); err != nil { - return nil, err - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const updateInvocationComplete = `-- name: UpdateInvocationComplete :one -UPDATE invocations -SET - status = ?, - duration_ms = ?, - memory_used_mb = ?, - response_size_bytes = ?, - logs = ?, - error = ?, - completed_at = CURRENT_TIMESTAMP -WHERE id = ? -RETURNING id, function_id, deployment_id, status, duration_ms, memory_used_mb, response_size_bytes, logs, error, created_at, completed_at -` - -type UpdateInvocationCompleteParams struct { - Status string `db:"status" json:"status"` - DurationMs sql.NullInt64 `db:"duration_ms" json:"duration_ms"` - MemoryUsedMb sql.NullInt64 `db:"memory_used_mb" json:"memory_used_mb"` - ResponseSizeBytes sql.NullInt64 `db:"response_size_bytes" json:"response_size_bytes"` - Logs sql.NullString `db:"logs" json:"logs"` - Error sql.NullString `db:"error" json:"error"` - ID string `db:"id" json:"id"` -} - -func (q *Queries) UpdateInvocationComplete(ctx context.Context, arg UpdateInvocationCompleteParams) (Invocation, error) { - row := q.db.QueryRowContext(ctx, updateInvocationComplete, - arg.Status, - arg.DurationMs, - arg.MemoryUsedMb, - arg.ResponseSizeBytes, - arg.Logs, - arg.Error, - arg.ID, - ) - var i Invocation - err := row.Scan( - &i.ID, - &i.FunctionID, - &i.DeploymentID, - &i.Status, - &i.DurationMs, - &i.MemoryUsedMb, - &i.ResponseSizeBytes, - &i.Logs, - &i.Error, - &i.CreatedAt, - &i.CompletedAt, - ) - return i, err -} diff --git a/functional/database/migrations/001_initial.sql b/functional/database/migrations/001_initial.sql deleted file mode 100644 index e1d8595..0000000 --- a/functional/database/migrations/001_initial.sql +++ /dev/null @@ -1,65 +0,0 @@ --- +goose Up --- +goose StatementBegin -CREATE TABLE functions ( - id TEXT PRIMARY KEY, - name TEXT NOT NULL UNIQUE, - description TEXT, - code_path TEXT NOT NULL, - runtime TEXT NOT NULL, - handler TEXT NOT NULL, - timeout_seconds INTEGER NOT NULL DEFAULT 30, - memory_mb INTEGER NOT NULL DEFAULT 128, - env_vars TEXT, -- JSON - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - updated_at DATETIME DEFAULT CURRENT_TIMESTAMP -); - -CREATE TABLE deployments ( - id TEXT PRIMARY KEY, - function_id TEXT NOT NULL, - provider TEXT NOT NULL, - resource_id TEXT NOT NULL, - status TEXT NOT NULL, - replicas INTEGER NOT NULL DEFAULT 1, - image_tag TEXT, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (function_id) REFERENCES functions(id) ON DELETE CASCADE -); - -CREATE TABLE invocations ( - id TEXT PRIMARY KEY, - function_id TEXT NOT NULL, - deployment_id TEXT, - status TEXT NOT NULL, - duration_ms INTEGER, - memory_used_mb INTEGER, - response_size_bytes INTEGER, - logs TEXT, - error TEXT, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - completed_at DATETIME, - FOREIGN KEY (function_id) REFERENCES functions(id) ON DELETE CASCADE, - FOREIGN KEY (deployment_id) REFERENCES deployments(id) ON DELETE SET NULL -); - -CREATE INDEX idx_functions_name ON functions(name); -CREATE INDEX idx_deployments_function_id ON deployments(function_id); -CREATE INDEX idx_deployments_status ON deployments(status); -CREATE INDEX idx_invocations_function_id ON invocations(function_id); -CREATE INDEX idx_invocations_status ON invocations(status); -CREATE INDEX idx_invocations_created_at ON invocations(created_at); --- +goose StatementEnd - --- +goose Down --- +goose StatementBegin -DROP INDEX IF EXISTS idx_invocations_created_at; -DROP INDEX IF EXISTS idx_invocations_status; -DROP INDEX IF EXISTS idx_invocations_function_id; -DROP INDEX IF EXISTS idx_deployments_status; -DROP INDEX IF EXISTS idx_deployments_function_id; -DROP INDEX IF EXISTS idx_functions_name; -DROP TABLE IF EXISTS invocations; -DROP TABLE IF EXISTS deployments; -DROP TABLE IF EXISTS functions; --- +goose StatementEnd \ No newline at end of file diff --git a/functional/database/models.go b/functional/database/models.go deleted file mode 100644 index e7a740c..0000000 --- a/functional/database/models.go +++ /dev/null @@ -1,49 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.29.0 - -package database - -import ( - "database/sql" -) - -type Deployment struct { - ID string `db:"id" json:"id"` - FunctionID string `db:"function_id" json:"function_id"` - Provider string `db:"provider" json:"provider"` - ResourceID string `db:"resource_id" json:"resource_id"` - Status string `db:"status" json:"status"` - Replicas int64 `db:"replicas" json:"replicas"` - ImageTag sql.NullString `db:"image_tag" json:"image_tag"` - CreatedAt sql.NullTime `db:"created_at" json:"created_at"` - UpdatedAt sql.NullTime `db:"updated_at" json:"updated_at"` -} - -type Function struct { - ID string `db:"id" json:"id"` - Name string `db:"name" json:"name"` - Description sql.NullString `db:"description" json:"description"` - CodePath string `db:"code_path" json:"code_path"` - Runtime string `db:"runtime" json:"runtime"` - Handler string `db:"handler" json:"handler"` - TimeoutSeconds int64 `db:"timeout_seconds" json:"timeout_seconds"` - MemoryMb int64 `db:"memory_mb" json:"memory_mb"` - EnvVars sql.NullString `db:"env_vars" json:"env_vars"` - CreatedAt sql.NullTime `db:"created_at" json:"created_at"` - UpdatedAt sql.NullTime `db:"updated_at" json:"updated_at"` -} - -type Invocation struct { - ID string `db:"id" json:"id"` - FunctionID string `db:"function_id" json:"function_id"` - DeploymentID sql.NullString `db:"deployment_id" json:"deployment_id"` - Status string `db:"status" json:"status"` - DurationMs sql.NullInt64 `db:"duration_ms" json:"duration_ms"` - MemoryUsedMb sql.NullInt64 `db:"memory_used_mb" json:"memory_used_mb"` - ResponseSizeBytes sql.NullInt64 `db:"response_size_bytes" json:"response_size_bytes"` - Logs sql.NullString `db:"logs" json:"logs"` - Error sql.NullString `db:"error" json:"error"` - CreatedAt sql.NullTime `db:"created_at" json:"created_at"` - CompletedAt sql.NullTime `db:"completed_at" json:"completed_at"` -} diff --git a/functional/database/queries/deployments.sql b/functional/database/queries/deployments.sql deleted file mode 100644 index 5c36259..0000000 --- a/functional/database/queries/deployments.sql +++ /dev/null @@ -1,37 +0,0 @@ --- name: CreateDeployment :one -INSERT INTO deployments ( - id, function_id, provider, resource_id, status, replicas, image_tag -) VALUES ( - ?, ?, ?, ?, ?, ?, ? -) RETURNING *; - --- name: GetDeployment :one -SELECT * FROM deployments WHERE id = ?; - --- name: GetDeploymentsByFunction :many -SELECT * FROM deployments WHERE function_id = ? ORDER BY created_at DESC; - --- name: GetActiveDeploymentByFunction :one -SELECT * FROM deployments -WHERE function_id = ? AND status = 'active' -ORDER BY created_at DESC -LIMIT 1; - --- name: UpdateDeploymentStatus :one -UPDATE deployments -SET - status = ?, - updated_at = CURRENT_TIMESTAMP -WHERE id = ? -RETURNING *; - --- name: UpdateDeploymentReplicas :one -UPDATE deployments -SET - replicas = ?, - updated_at = CURRENT_TIMESTAMP -WHERE id = ? -RETURNING *; - --- name: DeleteDeployment :exec -DELETE FROM deployments WHERE id = ?; \ No newline at end of file diff --git a/functional/database/queries/functions.sql b/functional/database/queries/functions.sql deleted file mode 100644 index 6d6d113..0000000 --- a/functional/database/queries/functions.sql +++ /dev/null @@ -1,33 +0,0 @@ --- name: CreateFunction :one -INSERT INTO functions ( - id, name, description, code_path, runtime, handler, - timeout_seconds, memory_mb, env_vars -) VALUES ( - ?, ?, ?, ?, ?, ?, ?, ?, ? -) RETURNING *; - --- name: GetFunction :one -SELECT * FROM functions WHERE id = ?; - --- name: GetFunctionByName :one -SELECT * FROM functions WHERE name = ?; - --- name: ListFunctions :many -SELECT * FROM functions ORDER BY created_at DESC; - --- name: UpdateFunction :one -UPDATE functions -SET - description = ?, - code_path = ?, - runtime = ?, - handler = ?, - timeout_seconds = ?, - memory_mb = ?, - env_vars = ?, - updated_at = CURRENT_TIMESTAMP -WHERE id = ? -RETURNING *; - --- name: DeleteFunction :exec -DELETE FROM functions WHERE id = ?; \ No newline at end of file diff --git a/functional/database/queries/invocations.sql b/functional/database/queries/invocations.sql deleted file mode 100644 index 84bd87c..0000000 --- a/functional/database/queries/invocations.sql +++ /dev/null @@ -1,41 +0,0 @@ --- name: CreateInvocation :one -INSERT INTO invocations ( - id, function_id, deployment_id, status -) VALUES ( - ?, ?, ?, ? -) RETURNING *; - --- name: GetInvocation :one -SELECT * FROM invocations WHERE id = ?; - --- name: ListInvocations :many -SELECT * FROM invocations ORDER BY created_at DESC LIMIT ? OFFSET ?; - --- name: ListInvocationsByFunction :many -SELECT * FROM invocations -WHERE function_id = ? -ORDER BY created_at DESC -LIMIT ? OFFSET ?; - --- name: UpdateInvocationComplete :one -UPDATE invocations -SET - status = ?, - duration_ms = ?, - memory_used_mb = ?, - response_size_bytes = ?, - logs = ?, - error = ?, - completed_at = CURRENT_TIMESTAMP -WHERE id = ? -RETURNING *; - --- name: GetInvocationStats :one -SELECT - COUNT(*) as total_invocations, - COUNT(CASE WHEN status = 'success' THEN 1 END) as successful_invocations, - COUNT(CASE WHEN status = 'error' THEN 1 END) as failed_invocations, - AVG(CASE WHEN duration_ms IS NOT NULL THEN duration_ms END) as avg_duration_ms, - AVG(CASE WHEN memory_used_mb IS NOT NULL THEN memory_used_mb END) as avg_memory_mb -FROM invocations -WHERE function_id = ? AND created_at >= ?; \ No newline at end of file diff --git a/functional/database/wrapper.go b/functional/database/wrapper.go deleted file mode 100644 index c2341c6..0000000 --- a/functional/database/wrapper.go +++ /dev/null @@ -1,49 +0,0 @@ -package database - -import ( - "context" - "database/sql" - "embed" - - "github.com/pressly/goose/v3" - _ "github.com/mattn/go-sqlite3" -) - -type DbWrapper struct { - *Queries - db *sql.DB -} - -func Open(ctx context.Context, path string) (*DbWrapper, error) { - db, err := sql.Open("sqlite3", path+"?_fk=1") - if err != nil { - return nil, err - } - - return &DbWrapper{ - Queries: New(db), - db: db, - }, nil -} - -func (dw *DbWrapper) Close() error { - return dw.db.Close() -} - -func (dw *DbWrapper) RunMigrations(migrationsFS embed.FS) error { - goose.SetBaseFS(migrationsFS) - - if err := goose.SetDialect("sqlite3"); err != nil { - return err - } - - if err := goose.Up(dw.db, "migrations"); err != nil { - return err - } - - return nil -} - -func (dw *DbWrapper) DB() *sql.DB { - return dw.db -} \ No newline at end of file diff --git a/functional/docker-compose.yml b/functional/docker-compose.yml deleted file mode 100644 index 0ac9b35..0000000 --- a/functional/docker-compose.yml +++ /dev/null @@ -1,111 +0,0 @@ ---- - -volumes: - functional-data: {} - traefik-data: {} - -networks: - backend: {} - -services: - functional: - build: - context: .. - dockerfile: ./functional/Dockerfile - command: [serve] - environment: - CONFIG_TYPE: file - CONFIG_FILE: /app/config.yml - configs: - - source: functional-config - target: /app/config.yml - ports: - - 8000 - networks: - - backend - depends_on: - - traefik - healthcheck: - test: curl -sSL http://localhost:8000/system/liveness - interval: "1m" - timeout: "10s" - retries: 3 - start_period: "30s" - labels: - - appname=functional - - component=api - - traefik: - image: docker.io/library/traefik:latest - command: - - --configfile=/config/config.yml - environment: {} - env_file: ./.env - configs: - - source: traefik-static-config - target: /config/config.yml - ports: - - 80 - - 443 - networks: - - backend - healthcheck: - test: curl -sSL http://localhost:80 - labels: - - appname=functional - - component=traefik - -configs: - functional-config: - content: | - http: - listen_address: "0.0.0.0:8000" - database: - path: "/app/functional.db" - compute: - provider: "docker" - docker: - socket: "unix:///var/run/docker.sock" - network: functional-net - registry: "localhost:5000" - storage: - functions_path: "/data/functions" - temp_path: "/data/tmp" - traefik-static-config: - content: | - log: - level: "INFO" - format: "json" - accessLog: - format: "json" - filters: - statusCodes: - - 400-499 - - 500-599 - retryAttempts: true - minDuration: "500ms" - entryPoints: - http: - address: ":80" - https: - address: ":443" - internal: - address: ":8889" - api: - insecure: false - dashboard: true - ping: - entryPoint: internal - traefik-internal-routing: - content: | - http: - routers: - dashboard: - rule: PathPrefix(`/api`) || PathPrefix(`/dashboard`) - service: api@internal - middlewares: - auth: - basicAuth: - users: - # default credentials: admin:nuWr4ONi9SfkKQx6 - - "admin:$$apr1$$O0.VQWA.$$fj8qCnHxN7iC5qDgfTp3u0" diff --git a/functional/go.sum b/functional/go.sum deleted file mode 100644 index aadad95..0000000 --- a/functional/go.sum +++ /dev/null @@ -1,396 +0,0 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= -github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU= -github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -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/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ= -github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4= -github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= -github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY= -github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= -github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8= -github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -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/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4= -github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= -github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= -github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= -github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= -github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= -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/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/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 v28.3.2+incompatible h1:wn66NJ6pWB1vBZIilP8G3qQPqHy5XymfYn5vsqeA5oA= -github.com/docker/docker v28.3.2+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/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= -github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= -github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY= -github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok= -github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= -github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= -github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ= -github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= -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-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= -github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= -github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= -github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= -github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k= -github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= -github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= -github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -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.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/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/go-github/v58 v58.0.0 h1:Una7GGERlF/37XfkPwpzYJe0Vp4dt2k1kCjlxwjIvzw= -github.com/google/go-github/v58 v58.0.0/go.mod h1:k4hxDKEfoWpSqFlc8LTpGd9fu2KrV1YAa6Hi6FmDNY4= -github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= -github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI= -github.com/hashicorp/cronexpr v1.1.2 h1:wG/ZYIKT+RT3QkOdgYc+xsKWVRgnxJ1OJtjjy84fJ9A= -github.com/hashicorp/cronexpr v1.1.2/go.mod h1:P4wA0KBl9C5q2hABiMO7cp6jcIg96CDh1Efb3g1PWA4= -github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= -github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -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/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= -github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= -github.com/hashicorp/nomad/api v0.0.0-20231227080007-76ba3e10e73d h1:Ztvo+OG+/xc6GQFiKME65qY6nveab1c7Hy8TFdBzbUI= -github.com/hashicorp/nomad/api v0.0.0-20231227080007-76ba3e10e73d/go.mod h1:ijDwa6o1uG1jFSq6kERiX2PamKGpZzTmo0XOFNeFZgw= -github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= -github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -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/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= -github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= -github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= -github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= -github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= -github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A= -github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= -github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6BbAxPY= -github.com/mfridman/interpolate v0.0.2/go.mod h1:p+7uk6oE07mpE/Ik1b8EckO0O4ZXiGAfshKBWLUM9Xg= -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/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= -github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= -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/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw= -github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs= -github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= -github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= -github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= -github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= -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/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= -github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= -github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= -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.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= -github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= -github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pressly/goose/v3 v3.24.3 h1:DSWWNwwggVUsYZ0X2VitiAa9sKuqtBfe+Jr9zFGwWlM= -github.com/pressly/goose/v3 v3.24.3/go.mod h1:v9zYL4xdViLHCUUJh/mhjnm6JrK7Eul8AS93IxiZM4E= -github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= -github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= -github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= -github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= -github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= -github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= -github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= -github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= -github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -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/sethvargo/go-retry v0.3.0 h1:EEt31A35QhrcRZtrYFDTBg91cqZVnFL2navjDrah2SE= -github.com/sethvargo/go-retry v0.3.0/go.mod h1:mNX17F0C/HguQMyMyJxcnU471gOZGxCLyYaFyAZraas= -github.com/shoenig/test v1.7.0 h1:eWcHtTXa6QLnBvm0jgEabMRN/uJ4DMV3M8xUGgRkZmk= -github.com/shoenig/test v1.7.0/go.mod h1:UxJ6u/x2v/TNs/LoLxBNJRV9DiwBBKYxXSyczsBHFoI= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= -github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= -github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.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.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= -github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= -github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= -github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= -go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.61.0 h1:VkrF0D14uQrCmPqBkYlwWnhgcwzXvIRAjX8eXO7vy6M= -go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.61.0/go.mod h1:p/mVr/Hs7gQnguNPXUyuiMRNtisyc9y/Oo7Kqr/6wbU= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 h1:Hf9xI/XLML9ElpiHVDNwvqI0hIFlzV8dgIr35kV1kRU= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0/go.mod h1:NfchwuyNoMcZ5MLHwPrODwUF1HWCXWrL31s8gSAdIKY= -go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= -go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 h1:dNzwXjZKpMpE2JhmO+9HsPl42NIXFIFSUSSs0fiqra0= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0/go.mod h1:90PoxvaEB5n6AOdZvi+yWJQoE95U8Dhhw2bSyRqnTD0= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 h1:JgtbA0xkWHnTmYk7YusopJFX6uleBmAuZ8n05NEh8nQ= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0/go.mod h1:179AK5aar5R3eS9FucPy6rggvU0g52cvKId8pv4+v0c= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.36.0 h1:nRVXXvf78e00EwY6Wp0YII8ww2JVWshZ20HfTlE11AM= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.36.0/go.mod h1:r49hO7CgrxY9Voaj3Xe8pANWtr0Oq916d0XAmOoCZAQ= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.36.0 h1:G8Xec/SgZQricwWBJF/mHZc7A02YHedfFDENwJEdRA0= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.36.0/go.mod h1:PD57idA/AiFD5aqoxGxCvT/ILJPeHy3MjqU/NS7KogY= -go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE= -go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E= -go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI= -go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg= -go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc= -go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps= -go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= -go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v1.6.0 h1:jQjP+AQyTf+Fe7OKj/MfkDrmK4MNVtw2NpXsf9fefDI= -go.opentelemetry.io/proto/otlp v1.6.0/go.mod h1:cicgGehlFuNdgZkcALOCh3VE6K/u2tAjzlRhDwmVpZc= -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.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= -go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -golang.org/x/arch v0.17.0 h1:4O3dfLzd+lQewptAHqjewQZQDyEdejz3VwgeYwkZneU= -golang.org/x/arch v0.17.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= -golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI= -golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= -golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= -golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= -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.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1 h1:b9mVrqYfq3P4bCdaLg1qtBnPzUYgglsIdjZkL/fQVOE= -google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.72.2 h1:TdbGzwb82ty4OusHWepvFWGLgIbNo1/SUynEN0ssqv8= -google.golang.org/grpc v1.72.2/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= -google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/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= -gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= -gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -modernc.org/libc v1.65.0 h1:e183gLDnAp9VJh6gWKdTy0CThL9Pt7MfcR/0bgb7Y1Y= -modernc.org/libc v1.65.0/go.mod h1:7m9VzGq7APssBTydds2zBcxGREwvIGpuUBaKTXdm2Qs= -modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= -modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= -modernc.org/memory v1.10.0 h1:fzumd51yQ1DxcOxSO+S6X7+QTuVU+n8/Aj7swYjFfC4= -modernc.org/memory v1.10.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw= -modernc.org/sqlite v1.37.0 h1:s1TMe7T3Q3ovQiK2Ouz4Jwh7dw4ZDqbebSDTlSJdfjI= -modernc.org/sqlite v1.37.0/go.mod h1:5YiWv+YviqGMuGw4V+PNplcyaJ5v+vQd7TQOgkACoJM= -nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= diff --git a/functional/interfaces/compute.go b/functional/interfaces/compute.go deleted file mode 100644 index a3b3c78..0000000 --- a/functional/interfaces/compute.go +++ /dev/null @@ -1,21 +0,0 @@ -package interfaces - -import ( - "context" -) - -type ComputeProvider interface { - Name() string - Deploy(ctx context.Context, fn *Function, imageName string) (*DeployResult, error) - Execute(ctx context.Context, deployment *Deployment, req *InvocationRequest) (*InvocationResult, error) - Scale(ctx context.Context, deployment *Deployment, replicas int) error - Remove(ctx context.Context, deployment *Deployment) error - Health(ctx context.Context) error -} - -// Forward declare types to avoid circular imports -type Function interface{} -type Deployment interface{} -type DeployResult interface{} -type InvocationRequest interface{} -type InvocationResult interface{} \ No newline at end of file diff --git a/functional/main.go b/functional/main.go deleted file mode 100644 index ad40b9b..0000000 --- a/functional/main.go +++ /dev/null @@ -1,7 +0,0 @@ -package main - -import "github.com/pirogoeth/apps/functional/cmd" - -func main() { - cmd.Execute() -} \ No newline at end of file diff --git a/functional/proxy/docker_integration.go b/functional/proxy/docker_integration.go deleted file mode 100644 index 78f12cb..0000000 --- a/functional/proxy/docker_integration.go +++ /dev/null @@ -1,114 +0,0 @@ -package proxy - -import ( - "context" - "fmt" - - "github.com/sirupsen/logrus" - "github.com/pirogoeth/apps/functional/compute" - "github.com/pirogoeth/apps/functional/types" -) - -// ProxyIntegration interface for Traefik integration -type ProxyIntegration interface { - RegisterFunction(ctx context.Context, functionID string) error - UnregisterFunction(ctx context.Context, functionID string) error -} - -// ProxyDockerProvider wraps the Docker provider with proxy integration -type ProxyDockerProvider struct { - *compute.DockerProvider - proxyIntegration ProxyIntegration - config *types.Config -} - -// NewProxyDockerProvider creates a new proxy-integrated Docker provider -func NewProxyDockerProvider(config *types.Config, proxyIntegration ProxyIntegration) *ProxyDockerProvider { - dockerConfig := &compute.DockerConfig{ - Socket: config.Compute.Docker.Socket, - Network: config.Compute.Docker.Network, - Registry: config.Compute.Docker.Registry, - } - - dockerProvider := compute.NewDockerProvider(dockerConfig) - - return &ProxyDockerProvider{ - DockerProvider: dockerProvider, - proxyIntegration: proxyIntegration, - config: config, - } -} - -// Deploy deploys a function and registers it with Traefik via the proxy -func (pdp *ProxyDockerProvider) Deploy(ctx context.Context, fn interface{}, imageName string) (interface{}, error) { - function, ok := fn.(*compute.Function) - if !ok { - return nil, fmt.Errorf("invalid function type") - } - - logrus.WithFields(logrus.Fields{ - "function_id": function.ID, - "function_name": function.Name, - }).Info("Starting function deployment with proxy integration") - - // Deploy using the original Docker provider - result, err := pdp.DockerProvider.Deploy(ctx, fn, imageName) - if err != nil { - return nil, fmt.Errorf("docker deployment failed: %w", err) - } - - // Register function with Traefik via proxy - if pdp.proxyIntegration != nil { - if err := pdp.proxyIntegration.RegisterFunction(ctx, function.ID); err != nil { - logrus.WithError(err).WithField("function_id", function.ID).Error("Failed to register function with Traefik") - // Continue anyway - the function is deployed, just not routed - } - } - - logrus.WithFields(logrus.Fields{ - "function_id": function.ID, - "function_name": function.Name, - }).Info("Function deployed and registered with Traefik") - - return result, nil -} - -// Remove removes a function and unregisters it from Traefik -func (pdp *ProxyDockerProvider) Remove(ctx context.Context, deployment interface{}) error { - dep, ok := deployment.(*compute.Deployment) - if !ok { - return fmt.Errorf("invalid deployment type") - } - - logrus.WithField("function_id", dep.FunctionID).Info("Removing function deployment") - - // Unregister from Traefik first - if pdp.proxyIntegration != nil { - if err := pdp.proxyIntegration.UnregisterFunction(ctx, dep.FunctionID); err != nil { - logrus.WithError(err).WithField("function_id", dep.FunctionID).Warn("Failed to unregister function from Traefik") - // Continue with removal anyway - } - } - - // Remove using the original Docker provider - if err := pdp.DockerProvider.Remove(ctx, deployment); err != nil { - return fmt.Errorf("docker removal failed: %w", err) - } - - logrus.WithField("function_id", dep.FunctionID).Info("Function removed and unregistered from Traefik") - return nil -} - -// Execute executes a function via the proxy (this should rarely be called directly) -func (pdp *ProxyDockerProvider) Execute(ctx context.Context, deployment interface{}, req interface{}) (interface{}, error) { - // In the proxy architecture, execution typically goes through the proxy service - // This method is kept for compatibility but logs a warning - logrus.Warn("Direct function execution called - consider using proxy service instead") - - return pdp.DockerProvider.Execute(ctx, deployment, req) -} - -// GetProxyIntegration returns the proxy integration interface -func (pdp *ProxyDockerProvider) GetProxyIntegration() ProxyIntegration { - return pdp.proxyIntegration -} \ No newline at end of file diff --git a/functional/proxy/pool.go b/functional/proxy/pool.go deleted file mode 100644 index a71549c..0000000 --- a/functional/proxy/pool.go +++ /dev/null @@ -1,373 +0,0 @@ -package proxy - -import ( - "context" - "fmt" - "io" - "sync" - "time" - - containerTypes "github.com/docker/docker/api/types/container" - "github.com/docker/docker/client" - "github.com/pirogoeth/apps/functional/database" - functypes "github.com/pirogoeth/apps/functional/types" - "github.com/sirupsen/logrus" -) - -// ContainerPool manages a pool of containers for function execution -type ContainerPool struct { - config *functypes.Config - client *client.Client - - // Pool management - pools map[string]*FunctionPool // functionID -> pool - poolMutex sync.RWMutex - - // Container tracking - containers map[string]*PooledContainer // containerID -> container - containerMutex sync.RWMutex -} - -// FunctionPool represents a pool of containers for a specific function -type FunctionPool struct { - FunctionID string - Available []*PooledContainer - InUse []*PooledContainer - MaxSize int - IdleTimeout time.Duration - CreatedCount int64 - mutex sync.RWMutex -} - -// PooledContainer represents a container in the pool with communication pipes -type PooledContainer struct { - ID string - FunctionID string - ContainerID string - - // Communication pipes - Stdin io.WriteCloser - Stdout io.ReadCloser - Stderr io.ReadCloser - - // Lifecycle - CreatedAt time.Time - LastUsed time.Time - UseCount int64 - Status ContainerStatus -} - -type ContainerStatus int - -const ( - ContainerStatusStarting ContainerStatus = iota - ContainerStatusReady - ContainerStatusInUse - ContainerStatusStopping - ContainerStatusStopped -) - -// NewContainerPool creates a new container pool -func NewContainerPool(config *functypes.Config) *ContainerPool { - dockerClient, err := client.NewClientWithOpts( - client.FromEnv, - client.WithAPIVersionNegotiation(), - ) - if err != nil { - logrus.WithError(err).Fatal("Failed to create Docker client") - } - - return &ContainerPool{ - config: config, - client: dockerClient, - pools: make(map[string]*FunctionPool), - containers: make(map[string]*PooledContainer), - } -} - -// GetContainer gets or creates a container for the function -func (cp *ContainerPool) GetContainer(ctx context.Context, function *database.Function) (*PooledContainer, error) { - pool := cp.getOrCreatePool(function.ID) - - pool.mutex.Lock() - defer pool.mutex.Unlock() - - // Try to get an available container - if len(pool.Available) > 0 { - container := pool.Available[0] - pool.Available = pool.Available[1:] - pool.InUse = append(pool.InUse, container) - - container.Status = ContainerStatusInUse - container.LastUsed = time.Now() - container.UseCount++ - - logrus.WithFields(logrus.Fields{ - "function_id": function.ID, - "container_id": container.ContainerID, - "use_count": container.UseCount, - }).Debug("Reusing container from pool") - - return container, nil - } - - // Create new container if pool isn't at capacity - if len(pool.InUse) < pool.MaxSize { - container, err := cp.createContainer(ctx, function) - if err != nil { - return nil, fmt.Errorf("failed to create container: %w", err) - } - - pool.InUse = append(pool.InUse, container) - pool.CreatedCount++ - - logrus.WithFields(logrus.Fields{ - "function_id": function.ID, - "container_id": container.ContainerID, - "pool_size": len(pool.InUse), - }).Info("Created new container for pool") - - return container, nil - } - - return nil, fmt.Errorf("container pool at capacity for function %s", function.ID) -} - -// ReturnContainer returns a container to the pool -func (cp *ContainerPool) ReturnContainer(container *PooledContainer) { - pool := cp.getOrCreatePool(container.FunctionID) - - pool.mutex.Lock() - defer pool.mutex.Unlock() - - // Remove from in-use - for i, c := range pool.InUse { - if c.ID == container.ID { - pool.InUse = append(pool.InUse[:i], pool.InUse[i+1:]...) - break - } - } - - // Add to available if container is still healthy - if container.Status == ContainerStatusInUse { - container.Status = ContainerStatusReady - pool.Available = append(pool.Available, container) - - logrus.WithFields(logrus.Fields{ - "function_id": container.FunctionID, - "container_id": container.ContainerID, - }).Debug("Returned container to pool") - } else { - // Container is unhealthy, clean it up - cp.removeContainer(container) - } -} - -// createContainer creates a new container for the function -func (cp *ContainerPool) createContainer(ctx context.Context, function *database.Function) (*PooledContainer, error) { - // Generate container image tag (this would integrate with your existing Docker provider) - imageTag := fmt.Sprintf("function-%s:latest", function.Name) - - // Create container config for pipe communication - config := &containerTypes.Config{ - Image: imageTag, - Cmd: []string{"/app/wrapper"}, // We'll need a wrapper script in containers - Tty: false, - OpenStdin: true, - StdinOnce: false, - AttachStdin: true, - AttachStdout: true, - AttachStderr: true, - } - - hostConfig := &containerTypes.HostConfig{ - // Add resource constraints - Resources: containerTypes.Resources{ - Memory: int64(function.MemoryMb) * 1024 * 1024, - CPUShares: 1024, // Default CPU shares - }, - } - - // Create container - resp, err := cp.client.ContainerCreate(ctx, config, hostConfig, nil, nil, "") - if err != nil { - return nil, fmt.Errorf("failed to create container: %w", err) - } - - // Start container - if err := cp.client.ContainerStart(ctx, resp.ID, containerTypes.StartOptions{}); err != nil { - return nil, fmt.Errorf("failed to start container: %w", err) - } - - // Attach to container for pipe communication - attachResp, err := cp.client.ContainerAttach(ctx, resp.ID, containerTypes.AttachOptions{ - Stream: true, - Stdin: true, - Stdout: true, - Stderr: true, - }) - if err != nil { - return nil, fmt.Errorf("failed to attach to container: %w", err) - } - - pooledContainer := &PooledContainer{ - ID: fmt.Sprintf("pool_%s_%d", function.ID, time.Now().UnixNano()), - FunctionID: function.ID, - ContainerID: resp.ID, - Stdin: attachResp.Conn, - Stdout: attachResp.Conn, // Use Conn which implements ReadWriteCloser - Stderr: attachResp.Conn, // Docker multiplexes stdout/stderr - CreatedAt: time.Now(), - LastUsed: time.Now(), - UseCount: 1, - Status: ContainerStatusInUse, - } - - // Track container - cp.containerMutex.Lock() - cp.containers[pooledContainer.ID] = pooledContainer - cp.containerMutex.Unlock() - - return pooledContainer, nil -} - -// getOrCreatePool gets or creates a function pool -func (cp *ContainerPool) getOrCreatePool(functionID string) *FunctionPool { - cp.poolMutex.RLock() - pool, exists := cp.pools[functionID] - cp.poolMutex.RUnlock() - - if exists { - return pool - } - - // Create new pool - cp.poolMutex.Lock() - defer cp.poolMutex.Unlock() - - // Double-check after acquiring write lock - if pool, exists := cp.pools[functionID]; exists { - return pool - } - - pool = &FunctionPool{ - FunctionID: functionID, - Available: make([]*PooledContainer, 0), - InUse: make([]*PooledContainer, 0), - MaxSize: cp.config.Proxy.MaxContainersPerFunction, - IdleTimeout: cp.config.Proxy.ContainerIdleTimeout, - } - - cp.pools[functionID] = pool - return pool -} - -// StartCleanup starts the background cleanup routine -func (cp *ContainerPool) StartCleanup(ctx context.Context) { - ticker := time.NewTicker(30 * time.Second) - defer ticker.Stop() - - for { - select { - case <-ctx.Done(): - return - case <-ticker.C: - cp.cleanupIdleContainers() - } - } -} - -// cleanupIdleContainers removes idle containers from pools -func (cp *ContainerPool) cleanupIdleContainers() { - cp.poolMutex.RLock() - pools := make([]*FunctionPool, 0, len(cp.pools)) - for _, pool := range cp.pools { - pools = append(pools, pool) - } - cp.poolMutex.RUnlock() - - now := time.Now() - - for _, pool := range pools { - pool.mutex.Lock() - - // Check available containers for idle timeout - available := make([]*PooledContainer, 0, len(pool.Available)) - for _, container := range pool.Available { - if now.Sub(container.LastUsed) > pool.IdleTimeout { - logrus.WithFields(logrus.Fields{ - "function_id": container.FunctionID, - "container_id": container.ContainerID, - "idle_time": now.Sub(container.LastUsed), - }).Info("Removing idle container") - - cp.removeContainer(container) - } else { - available = append(available, container) - } - } - pool.Available = available - - pool.mutex.Unlock() - } -} - -// removeContainer removes and cleans up a container -func (cp *ContainerPool) removeContainer(container *PooledContainer) { - // Stop container - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - // Stop container with timeout - timeoutSeconds := 5 - if err := cp.client.ContainerStop(ctx, container.ContainerID, containerTypes.StopOptions{Timeout: &timeoutSeconds}); err != nil { - logrus.WithError(err).WithField("container_id", container.ContainerID).Warn("Failed to stop container") - // Continue with forced removal - } - - // Remove container - if err := cp.client.ContainerRemove(ctx, container.ContainerID, containerTypes.RemoveOptions{Force: true}); err != nil { - logrus.WithError(err).WithField("container_id", container.ContainerID).Warn("Failed to remove container") - } - - // Close pipes - if container.Stdin != nil { - container.Stdin.Close() - } - if container.Stdout != nil { - container.Stdout.Close() - } - if container.Stderr != nil { - container.Stderr.Close() - } - - // Remove from tracking - cp.containerMutex.Lock() - delete(cp.containers, container.ID) - cp.containerMutex.Unlock() - - container.Status = ContainerStatusStopped -} - -// GetPoolStats returns statistics about the container pools -func (cp *ContainerPool) GetPoolStats() map[string]interface{} { - cp.poolMutex.RLock() - defer cp.poolMutex.RUnlock() - - stats := make(map[string]interface{}) - - for functionID, pool := range cp.pools { - pool.mutex.RLock() - poolStats := map[string]interface{}{ - "available_containers": len(pool.Available), - "in_use_containers": len(pool.InUse), - "max_size": pool.MaxSize, - "created_count": pool.CreatedCount, - } - pool.mutex.RUnlock() - - stats[functionID] = poolStats - } - - return stats -} diff --git a/functional/proxy/service.go b/functional/proxy/service.go deleted file mode 100644 index 22bbadf..0000000 --- a/functional/proxy/service.go +++ /dev/null @@ -1,282 +0,0 @@ -package proxy - -import ( - "context" - "encoding/json" - "fmt" - "io" - "net/http" - "sync" - "time" - - "github.com/gin-gonic/gin" - "github.com/sirupsen/logrus" - "github.com/pirogoeth/apps/functional/database" - "github.com/pirogoeth/apps/functional/types" -) - -// ProxyService handles function invocations via container pools -type ProxyService struct { - config *types.Config - db *database.DbWrapper - containerPool *ContainerPool - traefik *TraefikClient - - // In-flight request tracking - inFlightMutex sync.RWMutex - inFlight map[string]*InFlightRequest -} - -// InFlightRequest tracks a request being processed -type InFlightRequest struct { - ID string - FunctionID string - StartTime time.Time - Container *PooledContainer -} - -// FunctionRequest represents the serialized request sent to functions -type FunctionRequest struct { - Method string `json:"method"` - Path string `json:"path"` - Headers map[string]string `json:"headers"` - Query map[string]string `json:"query"` - Body string `json:"body"` // Base64 encoded for binary safety - RequestID string `json:"request_id"` -} - -// FunctionResponse represents the response from functions -type FunctionResponse struct { - StatusCode int `json:"status_code"` - Headers map[string]string `json:"headers"` - Body string `json:"body"` // Base64 encoded - Error string `json:"error,omitempty"` -} - -// NewProxyService creates a new proxy service -func NewProxyService(config *types.Config, db *database.DbWrapper) *ProxyService { - containerPool := NewContainerPool(config) - traefik := NewTraefikClient(config.Proxy.TraefikAPIURL) - - return &ProxyService{ - config: config, - db: db, - containerPool: containerPool, - traefik: traefik, - inFlight: make(map[string]*InFlightRequest), - } -} - -// Start starts the proxy service -func (ps *ProxyService) Start(ctx context.Context) error { - // Start container pool cleanup routine - go ps.containerPool.StartCleanup(ctx) - - // Setup HTTP handler - router := gin.New() - router.Use(gin.Logger(), gin.Recovery()) - - // Function invocation endpoint - this receives requests from Traefik - router.Any("/invoke/:functionId/*path", ps.handleInvocation) - - // Health check endpoint - router.GET("/health", func(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{"status": "healthy"}) - }) - - // Metrics endpoint - router.GET("/metrics", ps.handleMetrics) - - server := &http.Server{ - Addr: ps.config.Proxy.ListenAddress, - Handler: router, - } - - logrus.WithField("address", ps.config.Proxy.ListenAddress).Info("Starting proxy service") - - go func() { - <-ctx.Done() - logrus.Info("Shutting down proxy service") - server.Shutdown(context.Background()) - }() - - return server.ListenAndServe() -} - -// handleInvocation processes function invocations -func (ps *ProxyService) handleInvocation(c *gin.Context) { - functionID := c.Param("functionId") - path := c.Param("path") - - logrus.WithFields(logrus.Fields{ - "function_id": functionID, - "path": path, - "method": c.Request.Method, - }).Info("Handling function invocation") - - // Get function from database - ctx := c.Request.Context() - function, err := ps.db.GetFunction(ctx, functionID) - if err != nil { - logrus.WithError(err).WithField("function_id", functionID).Error("Function not found") - c.JSON(http.StatusNotFound, gin.H{"error": "Function not found"}) - return - } - - // Create in-flight request tracking - requestID := ps.generateRequestID() - inFlightReq := &InFlightRequest{ - ID: requestID, - FunctionID: functionID, - StartTime: time.Now(), - } - - ps.trackInFlightRequest(inFlightReq) - defer ps.removeInFlightRequest(requestID) - - // Get or create container from pool - container, err := ps.containerPool.GetContainer(ctx, &function) - if err != nil { - logrus.WithError(err).WithField("function_id", functionID).Error("Failed to get container") - c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get container"}) - return - } - - inFlightReq.Container = container - defer ps.containerPool.ReturnContainer(container) - - // Serialize request - funcReq, err := ps.serializeRequest(c, requestID, path) - if err != nil { - logrus.WithError(err).Error("Failed to serialize request") - c.JSON(http.StatusBadRequest, gin.H{"error": "Failed to serialize request"}) - return - } - - // Execute function - response, err := ps.executeFunction(ctx, container, funcReq) - if err != nil { - logrus.WithError(err).WithField("function_id", functionID).Error("Function execution failed") - c.JSON(http.StatusInternalServerError, gin.H{"error": "Function execution failed"}) - return - } - - // Return response - ps.returnResponse(c, response) -} - -// serializeRequest converts HTTP request to FunctionRequest -func (ps *ProxyService) serializeRequest(c *gin.Context, requestID, path string) (*FunctionRequest, error) { - // Read body - body, err := io.ReadAll(c.Request.Body) - if err != nil { - return nil, fmt.Errorf("failed to read request body: %w", err) - } - - // Convert headers - headers := make(map[string]string) - for key, values := range c.Request.Header { - if len(values) > 0 { - headers[key] = values[0] - } - } - - // Convert query parameters - query := make(map[string]string) - for key, values := range c.Request.URL.Query() { - if len(values) > 0 { - query[key] = values[0] - } - } - - return &FunctionRequest{ - Method: c.Request.Method, - Path: path, - Headers: headers, - Query: query, - Body: string(body), // For now, assume text body - RequestID: requestID, - }, nil -} - -// executeFunction sends request to container via pipes and gets response -func (ps *ProxyService) executeFunction(ctx context.Context, container *PooledContainer, req *FunctionRequest) (*FunctionResponse, error) { - // Serialize request to JSON - reqJSON, err := json.Marshal(req) - if err != nil { - return nil, fmt.Errorf("failed to marshal request: %w", err) - } - - // Send to container via stdin pipe - if _, err := container.Stdin.Write(append(reqJSON, '\n')); err != nil { - return nil, fmt.Errorf("failed to write to container stdin: %w", err) - } - - // Read response from stdout pipe - response := &FunctionResponse{} - decoder := json.NewDecoder(container.Stdout) - if err := decoder.Decode(response); err != nil { - return nil, fmt.Errorf("failed to decode response: %w", err) - } - - return response, nil -} - -// returnResponse converts FunctionResponse back to HTTP response -func (ps *ProxyService) returnResponse(c *gin.Context, response *FunctionResponse) { - // Set headers - for key, value := range response.Headers { - c.Header(key, value) - } - - // Return response - c.Data(response.StatusCode, c.GetHeader("Content-Type"), []byte(response.Body)) -} - -// RegisterFunction registers a function with Traefik -func (ps *ProxyService) RegisterFunction(ctx context.Context, functionID string) error { - // Create Traefik route for this function - route := &TraefikRoute{ - Rule: fmt.Sprintf("PathPrefix(`/functions/%s`)", functionID), - Service: "proxy", - } - - return ps.traefik.RegisterRoute(ctx, functionID, route) -} - -// UnregisterFunction removes a function from Traefik -func (ps *ProxyService) UnregisterFunction(ctx context.Context, functionID string) error { - return ps.traefik.UnregisterRoute(ctx, functionID) -} - -// Helper methods - -func (ps *ProxyService) generateRequestID() string { - return fmt.Sprintf("req_%d", time.Now().UnixNano()) -} - -func (ps *ProxyService) trackInFlightRequest(req *InFlightRequest) { - ps.inFlightMutex.Lock() - defer ps.inFlightMutex.Unlock() - ps.inFlight[req.ID] = req -} - -func (ps *ProxyService) removeInFlightRequest(requestID string) { - ps.inFlightMutex.Lock() - defer ps.inFlightMutex.Unlock() - delete(ps.inFlight, requestID) -} - -// handleMetrics returns proxy metrics -func (ps *ProxyService) handleMetrics(c *gin.Context) { - ps.inFlightMutex.RLock() - defer ps.inFlightMutex.RUnlock() - - metrics := gin.H{ - "in_flight_requests": len(ps.inFlight), - "container_pools": ps.containerPool.GetPoolStats(), - "timestamp": time.Now(), - } - - c.JSON(http.StatusOK, metrics) -} \ No newline at end of file diff --git a/functional/proxy/service_test.go b/functional/proxy/service_test.go deleted file mode 100644 index a42426c..0000000 --- a/functional/proxy/service_test.go +++ /dev/null @@ -1,296 +0,0 @@ -package proxy - -import ( - "bytes" - "context" - "encoding/json" - "net/http" - "net/http/httptest" - "path/filepath" - "testing" - "time" - - "github.com/gin-gonic/gin" - "github.com/pirogoeth/apps/functional/database" - "github.com/pirogoeth/apps/functional/types" -) - -func setupTestProxyService(t *testing.T) (*ProxyService, *database.DbWrapper) { - // Set gin to test mode - gin.SetMode(gin.TestMode) - - // Setup test database - tempDir := t.TempDir() - dbPath := filepath.Join(tempDir, "test.db") - - ctx := context.Background() - db, err := database.Open(ctx, dbPath) - if err != nil { - t.Fatalf("Failed to open test database: %v", err) - } - - if err := db.RunMigrations(database.MigrationsFS); err != nil { - t.Fatalf("Failed to run migrations: %v", err) - } - - // Create test config - config := &types.Config{ - Proxy: types.ProxyConfig{ - ListenAddress: ":8080", - TraefikAPIURL: "http://localhost:8080/api", - MaxContainersPerFunction: 3, - ContainerIdleTimeout: 5 * time.Minute, - }, - Storage: types.StorageConfig{ - FunctionsPath: tempDir + "/functions", - TempPath: tempDir + "/temp", - }, - } - - proxyService := NewProxyService(config, db) - return proxyService, db -} - -func TestProxyService_SerializeRequest(t *testing.T) { - proxyService, db := setupTestProxyService(t) - defer db.Close() - - // Create test HTTP request - reqBody := `{"test": "data"}` - req := httptest.NewRequest(http.MethodPost, "/test/path?param=value", bytes.NewBufferString(reqBody)) - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Bearer token123") - - // Create gin context - w := httptest.NewRecorder() - c, _ := gin.CreateTestContext(w) - c.Request = req - - // Test serialization - funcReq, err := proxyService.serializeRequest(c, "test-request-id", "/test/path") - if err != nil { - t.Fatalf("Failed to serialize request: %v", err) - } - - // Verify serialized request - if funcReq.Method != "POST" { - t.Errorf("Expected method POST, got %s", funcReq.Method) - } - - if funcReq.Path != "/test/path" { - t.Errorf("Expected path '/test/path', got %s", funcReq.Path) - } - - if funcReq.Body != reqBody { - t.Errorf("Expected body '%s', got %s", reqBody, funcReq.Body) - } - - if funcReq.Headers["Content-Type"] != "application/json" { - t.Errorf("Expected Content-Type header to be preserved") - } - - if funcReq.Query["param"] != "value" { - t.Errorf("Expected query parameter to be preserved") - } - - if funcReq.RequestID != "test-request-id" { - t.Errorf("Expected request ID to be preserved") - } -} - -func TestProxyService_GenerateRequestID(t *testing.T) { - proxyService, db := setupTestProxyService(t) - defer db.Close() - - // Generate multiple request IDs - id1 := proxyService.generateRequestID() - id2 := proxyService.generateRequestID() - - // Verify IDs are unique - if id1 == id2 { - t.Errorf("Request IDs should be unique, got %s and %s", id1, id2) - } - - // Verify ID format - if len(id1) == 0 { - t.Errorf("Request ID should not be empty") - } - - if id1[:4] != "req_" { - t.Errorf("Request ID should start with 'req_', got %s", id1) - } -} - -func TestProxyService_InFlightTracking(t *testing.T) { - proxyService, db := setupTestProxyService(t) - defer db.Close() - - // Create test in-flight request - req := &InFlightRequest{ - ID: "test-request", - FunctionID: "test-function", - StartTime: time.Now(), - } - - // Track request - proxyService.trackInFlightRequest(req) - - // Verify tracking - proxyService.inFlightMutex.RLock() - tracked, exists := proxyService.inFlight[req.ID] - proxyService.inFlightMutex.RUnlock() - - if !exists { - t.Errorf("Request should be tracked") - } - - if tracked.ID != req.ID { - t.Errorf("Expected ID %s, got %s", req.ID, tracked.ID) - } - - // Remove request - proxyService.removeInFlightRequest(req.ID) - - // Verify removal - proxyService.inFlightMutex.RLock() - _, exists = proxyService.inFlight[req.ID] - proxyService.inFlightMutex.RUnlock() - - if exists { - t.Errorf("Request should be removed from tracking") - } -} - -func TestProxyService_ReturnResponse(t *testing.T) { - proxyService, db := setupTestProxyService(t) - defer db.Close() - - // Create test response - response := &FunctionResponse{ - StatusCode: 201, - Headers: map[string]string{ - "Content-Type": "application/json", - "X-Custom-Header": "test-value", - }, - Body: `{"message": "success"}`, - } - - // Create gin context - w := httptest.NewRecorder() - c, _ := gin.CreateTestContext(w) - - // Return response - proxyService.returnResponse(c, response) - - // Verify response - if w.Code != 201 { - t.Errorf("Expected status code 201, got %d", w.Code) - } - - if w.Header().Get("Content-Type") != "application/json" { - t.Errorf("Expected Content-Type header to be set") - } - - if w.Header().Get("X-Custom-Header") != "test-value" { - t.Errorf("Expected custom header to be set") - } - - if w.Body.String() != `{"message": "success"}` { - t.Errorf("Expected response body to match") - } -} - -func TestFunctionRequest_JSON(t *testing.T) { - req := &FunctionRequest{ - Method: "POST", - Path: "/api/test", - Headers: map[string]string{"Content-Type": "application/json"}, - Query: map[string]string{"param": "value"}, - Body: `{"data": "test"}`, - RequestID: "req-123", - } - - // Marshal to JSON - data, err := json.Marshal(req) - if err != nil { - t.Fatalf("Failed to marshal request: %v", err) - } - - // Unmarshal from JSON - var parsed FunctionRequest - if err := json.Unmarshal(data, &parsed); err != nil { - t.Fatalf("Failed to unmarshal request: %v", err) - } - - // Verify all fields - if parsed.Method != req.Method { - t.Errorf("Method mismatch: expected %s, got %s", req.Method, parsed.Method) - } - - if parsed.Path != req.Path { - t.Errorf("Path mismatch: expected %s, got %s", req.Path, parsed.Path) - } - - if parsed.RequestID != req.RequestID { - t.Errorf("RequestID mismatch: expected %s, got %s", req.RequestID, parsed.RequestID) - } -} - -func TestFunctionResponse_JSON(t *testing.T) { - resp := &FunctionResponse{ - StatusCode: 200, - Headers: map[string]string{"Content-Type": "application/json"}, - Body: `{"result": "success"}`, - Error: "", - } - - // Marshal to JSON - data, err := json.Marshal(resp) - if err != nil { - t.Fatalf("Failed to marshal response: %v", err) - } - - // Unmarshal from JSON - var parsed FunctionResponse - if err := json.Unmarshal(data, &parsed); err != nil { - t.Fatalf("Failed to unmarshal response: %v", err) - } - - // Verify all fields - if parsed.StatusCode != resp.StatusCode { - t.Errorf("StatusCode mismatch: expected %d, got %d", resp.StatusCode, parsed.StatusCode) - } - - if parsed.Body != resp.Body { - t.Errorf("Body mismatch: expected %s, got %s", resp.Body, parsed.Body) - } -} - -// Benchmark tests -func BenchmarkProxyService_SerializeRequest(b *testing.B) { - proxyService, db := setupTestProxyService(&testing.T{}) - defer db.Close() - - reqBody := `{"test": "benchmark"}` - req := httptest.NewRequest(http.MethodPost, "/test?param=value", bytes.NewBufferString(reqBody)) - req.Header.Set("Content-Type", "application/json") - - w := httptest.NewRecorder() - c, _ := gin.CreateTestContext(w) - c.Request = req - - b.ResetTimer() - for i := 0; i < b.N; i++ { - proxyService.serializeRequest(c, "bench-request", "/test") - } -} - -func BenchmarkProxyService_GenerateRequestID(b *testing.B) { - proxyService, db := setupTestProxyService(&testing.T{}) - defer db.Close() - - b.ResetTimer() - for i := 0; i < b.N; i++ { - proxyService.generateRequestID() - } -} \ No newline at end of file diff --git a/functional/proxy/traefik.go b/functional/proxy/traefik.go deleted file mode 100644 index 135cda9..0000000 --- a/functional/proxy/traefik.go +++ /dev/null @@ -1,213 +0,0 @@ -package proxy - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "net/http" - "time" - - "github.com/sirupsen/logrus" -) - -// TraefikClient handles communication with Traefik's API -type TraefikClient struct { - apiURL string - httpClient *http.Client -} - -// TraefikRoute represents a Traefik route configuration -type TraefikRoute struct { - Rule string `json:"rule"` - Service string `json:"service"` - Priority *int `json:"priority,omitempty"` - Middlewares []string `json:"middlewares,omitempty"` - TLS *TraefikTLS `json:"tls,omitempty"` -} - -// TraefikService represents a Traefik service configuration -type TraefikService struct { - LoadBalancer *TraefikLoadBalancer `json:"loadBalancer"` -} - -// TraefikLoadBalancer represents load balancer configuration -type TraefikLoadBalancer struct { - Servers []TraefikServer `json:"servers"` -} - -// TraefikServer represents a backend server -type TraefikServer struct { - URL string `json:"url"` -} - -// TraefikTLS represents TLS configuration -type TraefikTLS struct { - CertResolver string `json:"certResolver,omitempty"` -} - -// TraefikConfiguration represents the full Traefik configuration -type TraefikConfiguration struct { - HTTP *TraefikHTTPConfiguration `json:"http"` -} - -// TraefikHTTPConfiguration represents HTTP configuration -type TraefikHTTPConfiguration struct { - Routers map[string]*TraefikRoute `json:"routers"` - Services map[string]*TraefikService `json:"services"` -} - -// NewTraefikClient creates a new Traefik API client -func NewTraefikClient(apiURL string) *TraefikClient { - return &TraefikClient{ - apiURL: apiURL, - httpClient: &http.Client{ - Timeout: 10 * time.Second, - }, - } -} - -// RegisterRoute registers a function route with Traefik -func (tc *TraefikClient) RegisterRoute(ctx context.Context, functionID string, route *TraefikRoute) error { - // Create the service configuration for the proxy - service := &TraefikService{ - LoadBalancer: &TraefikLoadBalancer{ - Servers: []TraefikServer{ - { - URL: "http://functional:8080", // Points to the proxy service - }, - }, - }, - } - - // Create full configuration - config := &TraefikConfiguration{ - HTTP: &TraefikHTTPConfiguration{ - Routers: map[string]*TraefikRoute{ - fmt.Sprintf("function-%s", functionID): route, - }, - Services: map[string]*TraefikService{ - "proxy": service, - }, - }, - } - - return tc.putConfiguration(ctx, fmt.Sprintf("function-%s", functionID), config) -} - -// UnregisterRoute removes a function route from Traefik -func (tc *TraefikClient) UnregisterRoute(ctx context.Context, functionID string) error { - routerName := fmt.Sprintf("function-%s", functionID) - - // Send DELETE request to remove the router - req, err := http.NewRequestWithContext( - ctx, - http.MethodDelete, - fmt.Sprintf("%s/http/routers/%s", tc.apiURL, routerName), - nil, - ) - if err != nil { - return fmt.Errorf("failed to create delete request: %w", err) - } - - resp, err := tc.httpClient.Do(req) - if err != nil { - return fmt.Errorf("failed to delete route: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNotFound { - return fmt.Errorf("failed to delete route, status: %d", resp.StatusCode) - } - - logrus.WithField("function_id", functionID).Info("Unregistered function route from Traefik") - return nil -} - -// putConfiguration sends configuration to Traefik -func (tc *TraefikClient) putConfiguration(ctx context.Context, name string, config *TraefikConfiguration) error { - configJSON, err := json.Marshal(config) - if err != nil { - return fmt.Errorf("failed to marshal configuration: %w", err) - } - - req, err := http.NewRequestWithContext( - ctx, - http.MethodPut, - fmt.Sprintf("%s/providers/rest", tc.apiURL), - bytes.NewBuffer(configJSON), - ) - if err != nil { - return fmt.Errorf("failed to create request: %w", err) - } - - req.Header.Set("Content-Type", "application/json") - - resp, err := tc.httpClient.Do(req) - if err != nil { - return fmt.Errorf("failed to send configuration: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("failed to update configuration, status: %d", resp.StatusCode) - } - - logrus.WithField("name", name).Info("Updated Traefik configuration") - return nil -} - -// GetRoutes retrieves all routes from Traefik -func (tc *TraefikClient) GetRoutes(ctx context.Context) (map[string]*TraefikRoute, error) { - req, err := http.NewRequestWithContext( - ctx, - http.MethodGet, - fmt.Sprintf("%s/http/routers", tc.apiURL), - nil, - ) - if err != nil { - return nil, fmt.Errorf("failed to create request: %w", err) - } - - resp, err := tc.httpClient.Do(req) - if err != nil { - return nil, fmt.Errorf("failed to get routes: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("failed to get routes, status: %d", resp.StatusCode) - } - - var routes map[string]*TraefikRoute - if err := json.NewDecoder(resp.Body).Decode(&routes); err != nil { - return nil, fmt.Errorf("failed to decode routes: %w", err) - } - - return routes, nil -} - -// HealthCheck checks if Traefik API is accessible -func (tc *TraefikClient) HealthCheck(ctx context.Context) error { - req, err := http.NewRequestWithContext( - ctx, - http.MethodGet, - fmt.Sprintf("%s/ping", tc.apiURL), - nil, - ) - if err != nil { - return fmt.Errorf("failed to create health check request: %w", err) - } - - resp, err := tc.httpClient.Do(req) - if err != nil { - return fmt.Errorf("Traefik API health check failed: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("Traefik API unhealthy, status: %d", resp.StatusCode) - } - - return nil -} \ No newline at end of file diff --git a/functional/proxy/wrapper.go b/functional/proxy/wrapper.go deleted file mode 100644 index 0bc2b6c..0000000 --- a/functional/proxy/wrapper.go +++ /dev/null @@ -1,227 +0,0 @@ -package proxy - -import ( - "bufio" - "encoding/json" - "fmt" - "os" - "os/exec" - "strings" - "time" - - "github.com/sirupsen/logrus" -) - -// WrapperService handles communication between proxy and function code -type WrapperService struct { - Runtime string - Handler string -} - -// FunctionWrapperRequest represents a request from the proxy -type FunctionWrapperRequest struct { - Method string `json:"method"` - Path string `json:"path"` - Headers map[string]string `json:"headers"` - Query map[string]string `json:"query"` - Body string `json:"body"` - RequestID string `json:"request_id"` -} - -// FunctionWrapperResponse represents a response to the proxy -type FunctionWrapperResponse struct { - StatusCode int `json:"status_code"` - Headers map[string]string `json:"headers"` - Body string `json:"body"` - Error string `json:"error,omitempty"` -} - -// StartWrapper starts the wrapper service for function containers -func StartWrapper(runtime, handler string) error { - wrapper := &WrapperService{ - Runtime: runtime, - Handler: handler, - } - - logrus.WithFields(logrus.Fields{ - "runtime": runtime, - "handler": handler, - }).Info("Starting function wrapper") - - scanner := bufio.NewScanner(os.Stdin) - encoder := json.NewEncoder(os.Stdout) - - for scanner.Scan() { - line := scanner.Text() - if line == "" { - continue - } - - // Parse request - var request FunctionWrapperRequest - if err := json.Unmarshal([]byte(line), &request); err != nil { - logrus.WithError(err).Error("Failed to parse request") - continue - } - - // Execute function - response := wrapper.executeFunction(&request) - - // Send response - if err := encoder.Encode(response); err != nil { - logrus.WithError(err).Error("Failed to encode response") - } - } - - if err := scanner.Err(); err != nil { - logrus.WithError(err).Error("Scanner error") - return err - } - - return nil -} - -// executeFunction executes the function based on runtime -func (ws *WrapperService) executeFunction(request *FunctionWrapperRequest) *FunctionWrapperResponse { - start := time.Now() - - logrus.WithFields(logrus.Fields{ - "request_id": request.RequestID, - "method": request.Method, - "path": request.Path, - }).Debug("Executing function") - - response := &FunctionWrapperResponse{ - Headers: make(map[string]string), - } - - // Execute based on runtime - var cmd *exec.Cmd - var err error - - switch strings.ToLower(ws.Runtime) { - case "nodejs", "node", "node18", "node20": - cmd, err = ws.executeNodeJS(request) - case "python", "python3", "python3.9", "python3.11": - cmd, err = ws.executePython(request) - case "go", "golang": - cmd, err = ws.executeGo(request) - default: - response.StatusCode = 500 - response.Error = fmt.Sprintf("Unsupported runtime: %s", ws.Runtime) - return response - } - - if err != nil { - response.StatusCode = 500 - response.Error = fmt.Sprintf("Failed to setup execution: %v", err) - return response - } - - // Execute command - output, err := cmd.Output() - if err != nil { - response.StatusCode = 500 - response.Error = fmt.Sprintf("Function execution failed: %v", err) - logrus.WithError(err).WithField("request_id", request.RequestID).Error("Function execution failed") - } else { - response.StatusCode = 200 - response.Body = string(output) - response.Headers["Content-Type"] = "application/json" - } - - duration := time.Since(start) - response.Headers["X-Execution-Time"] = fmt.Sprintf("%dms", duration.Milliseconds()) - response.Headers["X-Request-ID"] = request.RequestID - - logrus.WithFields(logrus.Fields{ - "request_id": request.RequestID, - "duration": duration, - "status": response.StatusCode, - }).Debug("Function execution completed") - - return response -} - -// executeNodeJS executes Node.js functions -func (ws *WrapperService) executeNodeJS(request *FunctionWrapperRequest) (*exec.Cmd, error) { - // Create a simple Node.js script that processes the request - script := fmt.Sprintf(` -const request = %s; -try { - const handler = require('./%s'); - const result = handler(request); - console.log(JSON.stringify({message: 'Hello from Node.js', request: request, result: result})); -} catch (error) { - console.error(JSON.stringify({error: error.message})); - process.exit(1); -} -`, ws.jsonString(request), ws.Handler) - - cmd := exec.Command("node", "-e", script) - cmd.Dir = "/app" - return cmd, nil -} - -// executePython executes Python functions -func (ws *WrapperService) executePython(request *FunctionWrapperRequest) (*exec.Cmd, error) { - // Create a simple Python script that processes the request - script := fmt.Sprintf(` -import json -import sys -import importlib.util - -request = %s -try: - # Load the handler module - spec = importlib.util.spec_from_file_location("handler", "/app/%s") - handler_module = importlib.util.module_from_spec(spec) - spec.loader.exec_module(handler_module) - - # Call the handler - result = handler_module.handler(request) - print(json.dumps({"message": "Hello from Python", "request": request, "result": result})) -except Exception as error: - print(json.dumps({"error": str(error)}), file=sys.stderr) - sys.exit(1) -`, ws.jsonString(request), ws.Handler) - - cmd := exec.Command("python3", "-c", script) - cmd.Dir = "/app" - return cmd, nil -} - -// executeGo executes Go functions -func (ws *WrapperService) executeGo(request *FunctionWrapperRequest) (*exec.Cmd, error) { - // For Go, we'll assume the binary is already built and available - // Pass the request as JSON via environment variable - requestJSON := ws.jsonString(request) - - cmd := exec.Command("./main") - cmd.Dir = "/app" - cmd.Env = append(os.Environ(), "FUNCTION_REQUEST="+requestJSON) - return cmd, nil -} - -// Helper function to convert request to JSON string -func (ws *WrapperService) jsonString(request *FunctionWrapperRequest) string { - data, _ := json.Marshal(request) - return string(data) -} - -// This can be used as a standalone binary for containers -func main() { - runtime := os.Getenv("FUNCTION_RUNTIME") - handler := os.Getenv("FUNCTION_HANDLER") - - if runtime == "" { - runtime = "nodejs" - } - if handler == "" { - handler = "index.js" - } - - if err := StartWrapper(runtime, handler); err != nil { - logrus.WithError(err).Fatal("Wrapper failed") - } -} \ No newline at end of file diff --git a/functional/testutils/fixtures.go b/functional/testutils/fixtures.go deleted file mode 100644 index 4b275ea..0000000 --- a/functional/testutils/fixtures.go +++ /dev/null @@ -1,347 +0,0 @@ -package testutils - -import ( - "context" - "database/sql" - "embed" - "encoding/base64" - "path/filepath" - "testing" - - "github.com/google/uuid" - "github.com/pirogoeth/apps/functional/compute" - "github.com/pirogoeth/apps/functional/database" - "github.com/pirogoeth/apps/functional/types" -) - -// Test database setup -func SetupTestDatabase(t *testing.T, migrationsFS embed.FS) *database.DbWrapper { - tempDir := t.TempDir() - dbPath := filepath.Join(tempDir, "test.db") - - ctx := context.Background() - db, err := database.Open(ctx, dbPath) - if err != nil { - t.Fatalf("Failed to open test database: %v", err) - } - - if err := db.RunMigrations(migrationsFS); err != nil { - t.Fatalf("Failed to run migrations: %v", err) - } - - return db -} - -// Sample function fixtures -func CreateSampleFunction(t *testing.T, db *database.DbWrapper) *database.Function { - ctx := context.Background() - - params := database.CreateFunctionParams{ - ID: uuid.New().String(), - Name: "sample-function", - Description: sql.NullString{String: "Sample test function", Valid: true}, - CodePath: "/tmp/sample-function", - Runtime: "nodejs", - Handler: "index.handler", - TimeoutSeconds: 30, - MemoryMb: 128, - EnvVars: sql.NullString{String: `{"TEST": "value"}`, Valid: true}, - } - - function, err := db.CreateFunction(ctx, params) - if err != nil { - t.Fatalf("Failed to create sample function: %v", err) - } - - return &function -} - -func CreateSampleFunctionWithParams(t *testing.T, db *database.DbWrapper, params database.CreateFunctionParams) *database.Function { - ctx := context.Background() - - function, err := db.CreateFunction(ctx, params) - if err != nil { - t.Fatalf("Failed to create sample function: %v", err) - } - - return &function -} - -func CreateSampleDeployment(t *testing.T, db *database.DbWrapper, functionID string) *database.Deployment { - ctx := context.Background() - - params := database.CreateDeploymentParams{ - ID: uuid.New().String(), - FunctionID: functionID, - Provider: "docker", - ResourceID: "container-" + uuid.New().String()[:8], - Status: "running", - Replicas: 1, - ImageTag: sql.NullString{String: "test-image:latest", Valid: true}, - } - - deployment, err := db.CreateDeployment(ctx, params) - if err != nil { - t.Fatalf("Failed to create sample deployment: %v", err) - } - - return &deployment -} - -func CreateSampleInvocation(t *testing.T, db *database.DbWrapper, functionID string, deploymentID string) *database.Invocation { - ctx := context.Background() - - params := database.CreateInvocationParams{ - ID: uuid.New().String(), - FunctionID: functionID, - DeploymentID: sql.NullString{String: deploymentID, Valid: true}, - Status: "completed", - } - - invocation, err := db.CreateInvocation(ctx, params) - if err != nil { - t.Fatalf("Failed to create sample invocation: %v", err) - } - - return &invocation -} - -// Sample request fixtures -func CreateSampleFunctionRequest() types.CreateFunctionRequest { - sampleCode := `{ - "index.js": "const express = require('express'); const app = express(); app.all('*', (req, res) => res.json({message: 'Hello World'})); app.listen(8080);", - "package.json": "{\"name\": \"test-function\", \"main\": \"index.js\", \"dependencies\": {\"express\": \"^4.18.0\"}}" - }` - encodedCode := base64.StdEncoding.EncodeToString([]byte(sampleCode)) - - return types.CreateFunctionRequest{ - Name: "test-function", - Description: "Test function description", - Runtime: "nodejs", - Handler: "index.handler", - TimeoutSeconds: 30, - MemoryMB: 128, - EnvVars: map[string]string{"TEST": "value"}, - Code: encodedCode, - } -} - -func CreateSamplePythonFunctionRequest() types.CreateFunctionRequest { - sampleCode := `{ - "app.py": "from flask import Flask; app = Flask(__name__); @app.route('/', defaults={'path': ''}, methods=['GET', 'POST']); @app.route('/', methods=['GET', 'POST']); def handler(path): return {'message': 'Hello from Python'}; app.run(host='0.0.0.0', port=8080)", - "requirements.txt": "flask==2.3.0" - }` - encodedCode := base64.StdEncoding.EncodeToString([]byte(sampleCode)) - - return types.CreateFunctionRequest{ - Name: "python-function", - Description: "Python test function", - Runtime: "python3", - Handler: "app.handler", - TimeoutSeconds: 60, - MemoryMB: 256, - EnvVars: map[string]string{"PYTHON_ENV": "test"}, - Code: encodedCode, - } -} - -func CreateSampleGoFunctionRequest() types.CreateFunctionRequest { - sampleCode := `{ - "main.go": "package main; import (\"net/http\"; \"encoding/json\"); func main() { http.HandleFunc(\"/\", func(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(map[string]string{\"message\": \"Hello from Go\"}) }); http.ListenAndServe(\":8080\", nil) }", - "go.mod": "module test-function\\ngo 1.21" - }` - encodedCode := base64.StdEncoding.EncodeToString([]byte(sampleCode)) - - return types.CreateFunctionRequest{ - Name: "go-function", - Description: "Go test function", - Runtime: "go", - Handler: "main", - TimeoutSeconds: 45, - MemoryMB: 128, - EnvVars: map[string]string{"GO_ENV": "test"}, - Code: encodedCode, - } -} - -// Mock compute provider for testing -type MockComputeProvider struct { - Name_ string - DeployError error - DeployResult *compute.DeployResult - ExecuteError error - ExecuteResult *compute.InvocationResult - ScaleError error - RemoveError error - HealthError error - - // Call tracking - DeployCalls int - ExecuteCalls int - ScaleCalls int - RemoveCalls int - HealthCalls int -} - -func NewMockComputeProvider() *MockComputeProvider { - return &MockComputeProvider{ - Name_: "mock", - } -} - -func (m *MockComputeProvider) Name() string { - return m.Name_ -} - -func (m *MockComputeProvider) Deploy(ctx context.Context, fn interface{}, imageName string) (interface{}, error) { - m.DeployCalls++ - - if m.DeployError != nil { - return nil, m.DeployError - } - - if m.DeployResult != nil { - return m.DeployResult, nil - } - - return &compute.DeployResult{ - DeploymentID: "mock-deployment-" + uuid.New().String()[:8], - ResourceID: "mock-resource-" + uuid.New().String()[:8], - ImageTag: "mock-image:latest", - }, nil -} - -func (m *MockComputeProvider) Execute(ctx context.Context, deployment interface{}, req interface{}) (interface{}, error) { - m.ExecuteCalls++ - - if m.ExecuteError != nil { - return nil, m.ExecuteError - } - - if m.ExecuteResult != nil { - return m.ExecuteResult, nil - } - - return &compute.InvocationResult{ - StatusCode: 200, - Body: []byte(`{"message": "mock response", "timestamp": "2024-01-01T00:00:00Z"}`), - Headers: map[string]string{"Content-Type": "application/json", "X-Mock": "true"}, - DurationMS: 100, - MemoryUsedMB: 64, - ResponseSize: 65, - Logs: "Mock execution logs", - Error: "", - }, nil -} - -func (m *MockComputeProvider) Scale(ctx context.Context, deployment interface{}, replicas int) error { - m.ScaleCalls++ - return m.ScaleError -} - -func (m *MockComputeProvider) Remove(ctx context.Context, deployment interface{}) error { - m.RemoveCalls++ - return m.RemoveError -} - -func (m *MockComputeProvider) Health(ctx context.Context) error { - m.HealthCalls++ - return m.HealthError -} - -// Reset call counters -func (m *MockComputeProvider) ResetCalls() { - m.DeployCalls = 0 - m.ExecuteCalls = 0 - m.ScaleCalls = 0 - m.RemoveCalls = 0 - m.HealthCalls = 0 -} - -// Test assertion helpers -func AssertStringEquals(t *testing.T, expected, actual, field string) { - t.Helper() - if expected != actual { - t.Errorf("Expected %s to be '%s', got '%s'", field, expected, actual) - } -} - -func AssertStringNotEmpty(t *testing.T, value, field string) { - t.Helper() - if value == "" { - t.Errorf("Expected %s to be non-empty", field) - } -} - -func AssertIntEquals(t *testing.T, expected, actual int, field string) { - t.Helper() - if expected != actual { - t.Errorf("Expected %s to be %d, got %d", field, expected, actual) - } -} - -func AssertInt64Equals(t *testing.T, expected, actual int64, field string) { - t.Helper() - if expected != actual { - t.Errorf("Expected %s to be %d, got %d", field, expected, actual) - } -} - -func AssertNoError(t *testing.T, err error, operation string) { - t.Helper() - if err != nil { - t.Fatalf("Unexpected error in %s: %v", operation, err) - } -} - -func AssertError(t *testing.T, err error, operation string) { - t.Helper() - if err == nil { - t.Fatalf("Expected error in %s, got nil", operation) - } -} - -func AssertNil(t *testing.T, value interface{}, field string) { - t.Helper() - if value != nil { - t.Errorf("Expected %s to be nil, got %v", field, value) - } -} - -func AssertNotNil(t *testing.T, value interface{}, field string) { - t.Helper() - if value == nil { - t.Errorf("Expected %s to be non-nil", field) - } -} - -// Sample runtime configurations for testing different scenarios -var RuntimeConfigs = map[string]database.CreateFunctionParams{ - "nodejs": { - ID: "nodejs-test", - Name: "nodejs-function", - Runtime: "nodejs", - Handler: "index.handler", - TimeoutSeconds: 30, - MemoryMb: 128, - CodePath: "/tmp/nodejs-function", - }, - "python": { - ID: "python-test", - Name: "python-function", - Runtime: "python3", - Handler: "app.handler", - TimeoutSeconds: 60, - MemoryMb: 256, - CodePath: "/tmp/python-function", - }, - "go": { - ID: "go-test", - Name: "go-function", - Runtime: "go", - Handler: "main", - TimeoutSeconds: 45, - MemoryMb: 128, - CodePath: "/tmp/go-function", - }, -} \ No newline at end of file diff --git a/functional/types/apicontext.go b/functional/types/apicontext.go deleted file mode 100644 index 76de261..0000000 --- a/functional/types/apicontext.go +++ /dev/null @@ -1,12 +0,0 @@ -package types - -import ( - "github.com/pirogoeth/apps/functional/database" - "github.com/pirogoeth/apps/functional/compute" -) - -type ApiContext struct { - Config *Config - Querier *database.Queries - Compute *compute.Registry -} \ No newline at end of file diff --git a/functional/types/compute.go b/functional/types/compute.go deleted file mode 100644 index 183a7ba..0000000 --- a/functional/types/compute.go +++ /dev/null @@ -1,87 +0,0 @@ -package types - -import ( - "context" - "time" -) - -type ComputeProvider interface { - Name() string - Deploy(ctx context.Context, fn *Function, imageName string) (*DeployResult, error) - Execute(ctx context.Context, deployment *Deployment, req *InvocationRequest) (*InvocationResult, error) - Scale(ctx context.Context, deployment *Deployment, replicas int) error - Remove(ctx context.Context, deployment *Deployment) error - Health(ctx context.Context) error -} - -type Deployment struct { - ID string `json:"id" db:"id"` - FunctionID string `json:"function_id" db:"function_id"` - Provider string `json:"provider" db:"provider"` - ResourceID string `json:"resource_id" db:"resource_id"` - Status DeploymentStatus `json:"status" db:"status"` - Replicas int32 `json:"replicas" db:"replicas"` - ImageTag string `json:"image_tag" db:"image_tag"` - CreatedAt time.Time `json:"created_at" db:"created_at"` - UpdatedAt time.Time `json:"updated_at" db:"updated_at"` -} - -type DeploymentStatus string - -const ( - DeploymentStatusPending DeploymentStatus = "pending" - DeploymentStatusBuilding DeploymentStatus = "building" - DeploymentStatusActive DeploymentStatus = "active" - DeploymentStatusFailed DeploymentStatus = "failed" - DeploymentStatusStopped DeploymentStatus = "stopped" -) - -type DeployResult struct { - DeploymentID string `json:"deployment_id"` - ResourceID string `json:"resource_id"` - ImageTag string `json:"image_tag"` -} - -type InvocationRequest struct { - FunctionID string `json:"function_id"` - Body []byte `json:"body"` - Headers map[string]string `json:"headers"` - Method string `json:"method"` - Path string `json:"path"` - QueryArgs map[string]string `json:"query_args"` -} - -type InvocationResult struct { - StatusCode int `json:"status_code"` - Body []byte `json:"body"` - Headers map[string]string `json:"headers"` - DurationMS int64 `json:"duration_ms"` - MemoryUsedMB int32 `json:"memory_used_mb"` - ResponseSize int64 `json:"response_size"` - Logs string `json:"logs"` - Error string `json:"error,omitempty"` -} - -type Invocation struct { - ID string `json:"id" db:"id"` - FunctionID string `json:"function_id" db:"function_id"` - DeploymentID *string `json:"deployment_id" db:"deployment_id"` - Status string `json:"status" db:"status"` - DurationMS *int64 `json:"duration_ms" db:"duration_ms"` - MemoryUsedMB *int32 `json:"memory_used_mb" db:"memory_used_mb"` - ResponseSizeBytes *int64 `json:"response_size_bytes" db:"response_size_bytes"` - Logs *string `json:"logs" db:"logs"` - Error *string `json:"error" db:"error"` - CreatedAt time.Time `json:"created_at" db:"created_at"` - CompletedAt *time.Time `json:"completed_at" db:"completed_at"` -} - -type InvocationStatus string - -const ( - InvocationStatusPending InvocationStatus = "pending" - InvocationStatusRunning InvocationStatus = "running" - InvocationStatusSuccess InvocationStatus = "success" - InvocationStatusError InvocationStatus = "error" - InvocationStatusTimeout InvocationStatus = "timeout" -) \ No newline at end of file diff --git a/functional/types/config.go b/functional/types/config.go deleted file mode 100644 index 58909a8..0000000 --- a/functional/types/config.go +++ /dev/null @@ -1,63 +0,0 @@ -package types - -import ( - "time" - - "github.com/pirogoeth/apps/functional/database" - "github.com/pirogoeth/apps/pkg/config" -) - -type Config struct { - config.CommonConfig - - Database *database.Config `json:"database"` - Compute ComputeConfig `json:"compute"` - Storage StorageConfig `json:"storage"` - Runtime RuntimeConfig `json:"runtime"` - Proxy ProxyConfig `json:"proxy"` -} - -type ComputeConfig struct { - Provider string `json:"provider" envconfig:"COMPUTE_PROVIDER"` - Docker *DockerConfig `json:"docker,omitempty"` - Firecracker *FirecrackerConfig `json:"firecracker,omitempty"` -} - -type DockerConfig struct { - Socket string `json:"socket" envconfig:"DOCKER_SOCKET"` - Network string `json:"network" envconfig:"DOCKER_NETWORK"` - Registry string `json:"registry" envconfig:"DOCKER_REGISTRY"` -} - -type FirecrackerConfig struct { - KernelImagePath string `json:"kernel_image_path" envconfig:"FIRECRACKER_KERNEL_IMAGE_PATH"` - RootfsImagePath string `json:"rootfs_image_path" envconfig:"FIRECRACKER_ROOTFS_IMAGE_PATH"` - WorkDir string `json:"work_dir" envconfig:"FIRECRACKER_WORK_DIR"` - NetworkDevice string `json:"network_device" envconfig:"FIRECRACKER_NETWORK_DEVICE"` -} - -type StorageConfig struct { - FunctionsPath string `json:"functions_path" envconfig:"STORAGE_FUNCTIONS_PATH"` - TempPath string `json:"temp_path" envconfig:"STORAGE_TEMP_PATH"` -} - -type RuntimeConfig struct { - MaxConcurrentExecutions int `json:"max_concurrent_executions" envconfig:"RUNTIME_MAX_CONCURRENT_EXECUTIONS"` - DefaultTimeout config.TimeDuration `json:"default_timeout" envconfig:"RUNTIME_DEFAULT_TIMEOUT"` - Scaling ScalingConfig `json:"scaling"` -} - -type ScalingConfig struct { - // TODO: Revisit these - this are most useful as configured on individual functions but these could serve as global defaults or limits? - MinReplicas int `json:"min_replicas" envconfig:"SCALING_MIN_REPLICAS"` - MaxReplicas int `json:"max_replicas" envconfig:"SCALING_MAX_REPLICAS"` - ScaleUpThreshold float64 `json:"scale_up_threshold" envconfig:"SCALING_SCALE_UP_THRESHOLD"` - ScaleDownThreshold float64 `json:"scale_down_threshold" envconfig:"SCALING_SCALE_DOWN_THRESHOLD"` -} - -type ProxyConfig struct { - ListenAddress string `json:"listen_address" envconfig:"PROXY_LISTEN_ADDRESS"` - TraefikAPIURL string `json:"traefik_api_url" envconfig:"PROXY_TRAEFIK_API_URL"` - MaxContainersPerFunction int `json:"max_containers_per_function" envconfig:"PROXY_MAX_CONTAINERS_PER_FUNCTION"` - ContainerIdleTimeout time.Duration `json:"container_idle_timeout" envconfig:"PROXY_CONTAINER_IDLE_TIMEOUT"` -} diff --git a/functional/types/function.go b/functional/types/function.go deleted file mode 100644 index 1cd1ebf..0000000 --- a/functional/types/function.go +++ /dev/null @@ -1,40 +0,0 @@ -package types - -import ( - "time" -) - -type Function struct { - ID string `json:"id" db:"id"` - Name string `json:"name" db:"name"` - Description string `json:"description" db:"description"` - CodePath string `json:"code_path" db:"code_path"` - Runtime string `json:"runtime" db:"runtime"` - Handler string `json:"handler" db:"handler"` - TimeoutSeconds int32 `json:"timeout_seconds" db:"timeout_seconds"` - MemoryMB int32 `json:"memory_mb" db:"memory_mb"` - EnvVars string `json:"env_vars" db:"env_vars"` // JSON string - CreatedAt time.Time `json:"created_at" db:"created_at"` - UpdatedAt time.Time `json:"updated_at" db:"updated_at"` -} - -type CreateFunctionRequest struct { - Name string `json:"name" binding:"required"` - Description string `json:"description"` - Runtime string `json:"runtime" binding:"required"` - Handler string `json:"handler" binding:"required"` - TimeoutSeconds int32 `json:"timeout_seconds"` - MemoryMB int32 `json:"memory_mb"` - EnvVars map[string]string `json:"env_vars"` - Code string `json:"code" binding:"required"` // Base64 encoded ZIP -} - -type UpdateFunctionRequest struct { - Description *string `json:"description"` - Runtime *string `json:"runtime"` - Handler *string `json:"handler"` - TimeoutSeconds *int32 `json:"timeout_seconds"` - MemoryMB *int32 `json:"memory_mb"` - EnvVars map[string]string `json:"env_vars"` - Code *string `json:"code"` // Base64 encoded ZIP -} \ No newline at end of file diff --git a/go.mod b/go.mod index 6eea424..9f2e909 100644 --- a/go.mod +++ b/go.mod @@ -1,13 +1,3 @@ module github.com/pirogoeth/apps go 1.24.1 - -require ( - github.com/mattn/go-sqlite3 v1.14.28 - github.com/sirupsen/logrus v1.9.3 -) - -require ( - github.com/stretchr/testify v1.10.0 // indirect - golang.org/x/sys v0.33.0 // indirect -) diff --git a/go.sum b/go.sum index c44655c..e69de29 100644 --- a/go.sum +++ b/go.sum @@ -1,20 +0,0 @@ -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A= -github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -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= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -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= diff --git a/go.work b/go.work index b0ffa19..f3460d1 100644 --- a/go.work +++ b/go.work @@ -1,18 +1,17 @@ go 1.24.1 -godebug ( - gotypesalias=1 -) +godebug gotypesalias=1 use ( - ./functional + ./docker-hermes ./email-archiver ./maparoon + ./moneyman ./nomad-deployer ./nomad-event-stream ./nomad-external-dns ./nomad-service-cleaner ./orba - ./voice-memos ./pkg + ./voice-memos ) diff --git a/go.work.sum b/go.work.sum index 81c75da..0942d1a 100644 --- a/go.work.sum +++ b/go.work.sum @@ -1,6 +1,7 @@ cel.dev/expr v0.16.0/go.mod h1:TRSuuV7DlVCE/uwv5QbAiW/v8l5O8C4eEPHeu7gf7Sg= cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= cel.dev/expr v0.20.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= +cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw= cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= @@ -84,6 +85,7 @@ cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2Aawl cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY= cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg= +cloud.google.com/go/compute/metadata v0.7.0/go.mod h1:j5MvL9PprKL39t166CoB1uVHfQMs4tFQZZcKwksXUjo= cloud.google.com/go/contactcenterinsights v1.10.0/go.mod h1:bsg/R7zGLYMVxFFzfh9ooLTruLRCG9fnzhH9KznHhbM= cloud.google.com/go/contactcenterinsights v1.13.0/go.mod h1:ieq5d5EtHsu8vhe2y3amtZ+BE+AQwX5qAy7cpo0POsI= cloud.google.com/go/container v1.24.0/go.mod h1:lTNExE2R7f+DLbAN+rJiKTisauFCaoDq6NURZ83eVH4= @@ -279,6 +281,9 @@ cloud.google.com/go/websecurityscanner v1.6.1/go.mod h1:Njgaw3rttgRHXzwCB8kgCYqv cloud.google.com/go/websecurityscanner v1.6.5/go.mod h1:QR+DWaxAz2pWooylsBF854/Ijvuoa3FCyS1zBa1rAVQ= cloud.google.com/go/workflows v1.11.1/go.mod h1:Z+t10G1wF7h8LgdY/EmRcQY8ptBD/nvofaL6FqlET6g= cloud.google.com/go/workflows v1.12.4/go.mod h1:yQ7HUqOkdJK4duVtMeBCAOPiN1ZF1E9pAMX51vpwB/w= +codeberg.org/go-fonts/liberation v0.5.0/go.mod h1:zS/2e1354/mJ4pGzIIaEtm/59VFCFnYC7YV6YdGl5GU= +codeberg.org/go-latex/latex v0.1.0/go.mod h1:LA0q/AyWIYrqVd+A9Upkgsb+IqPcmSTKc9Dny04MHMw= +codeberg.org/go-pdf/fpdf v0.10.0/go.mod h1:Y0DGRAdZ0OmnZPvjbMp/1bYxmIPxm0ws4tfoPOc4LjU= dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= @@ -287,6 +292,7 @@ dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= +git.sr.ht/~sbinet/gg v0.6.0/go.mod h1:uucygbfC9wVPQIfrmwM2et0imr8L7KQWywX0xpFMm94= github.com/Azure/azure-sdk-for-go v56.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0/go.mod h1:XCW7KnZet0Opnr7HccfUw1PLc4CjHqpcaxW8DHklNkQ= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0/go.mod h1:9kIvujWAA58nmPmWB1m23fyWic1kYZMxD9CxaWn4Qpg= @@ -313,6 +319,7 @@ github.com/ClickHouse/clickhouse-go/v2 v2.34.0/go.mod h1:yioSINoRLVZkLyDzdMXPLRI github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0/go.mod h1:obipzmGjfSjam60XLwGfqUkJsfiheAl+TUjG+4yzyPM= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.26.0/go.mod h1:2bIszWvQRlJVmJLiuLhukLImRjKPcYdzzsx6darK02A= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.29.0/go.mod h1:Cz6ft6Dkn3Et6l2v2a9/RpN7epQ1GtDlO6lj8bEcOvw= github.com/LK4D4/joincontext v0.0.0-20171026170139-1724345da6d5 h1:U7q69tqXiCf6m097GRlNQB0/6SI1qWIOHYHhCEvDxF4= github.com/LK4D4/joincontext v0.0.0-20171026170139-1724345da6d5/go.mod h1:nxQPcNPR/34g+HcK2hEsF99O+GJgIkW/OmPl8wtzhmk= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= @@ -326,14 +333,15 @@ github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ= github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA= github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo= +github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM= github.com/alecthomas/kingpin/v2 v2.3.2/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE= github.com/alecthomas/kingpin/v2 v2.4.0/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE= github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= -github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ= github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw= @@ -378,21 +386,23 @@ github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx2 github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/brianvoe/gofakeit/v6 v6.20.1 h1:8ihJ60OvPnPJ2W6wZR7M+TTeaZ9bml0z6oy4gvyJ/ek= github.com/brianvoe/gofakeit/v6 v6.20.1/go.mod h1:Ow6qC71xtwm79anlwKRlWZW6zVq9D2XHE4QSSMP/rU8= -github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= -github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/bwplotka/tracing-go v0.0.0-20230421061608-abdf862ceccd h1:WukrqDL9VxKSoSC6HkEoAwQNuj5N49CPIEFsYtNBEKM= github.com/bwplotka/tracing-go v0.0.0-20230421061608-abdf862ceccd/go.mod h1:tZEa2D7ZMVaxzB8RoMdvASCwSHWb5kyb8G7mRbJcIMA= +github.com/campoy/embedmd v1.0.0/go.mod h1:oxyr9RCiSXg0M3VJ3ks0UGfp98BpSSGr0kpiX3MzVl8= github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M= github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= 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.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/checkpoint-restore/go-criu/v5 v5.3.0 h1:wpFFOoomK3389ue2lAb0Boag6XPht5QYpipxmSNL4d8= github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= github.com/cheggaaa/pb/v3 v3.0.5/go.mod h1:X1L61/+36nz9bjIsrDU52qHKOQukUQe2Ge+YvGuquCw= @@ -420,6 +430,7 @@ github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20240723142845-024c85f92f20/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/cncf/xds/go v0.0.0-20241223141626-cff3c89139a3/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= +github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/coder/websocket v1.8.13 h1:f3QZdXy7uGVz+4uCJy2nTZyM0yTBj8yANEHhqlXZ9FE= github.com/coder/websocket v1.8.13/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs= github.com/container-storage-interface/spec v1.7.0 h1:gW8eyFQUZWWrMWa8p1seJ28gwDoN5CVJ4uAbQ+Hdycw= @@ -443,6 +454,7 @@ github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8 github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/couchbase/ghistogram v0.1.0 h1:b95QcQTCzjTUocDXp/uMgSNQi8oj1tGwnJ4bODWZnps= github.com/couchbase/moss v0.1.0 h1:HCL+xxHUwmOaL44kMM/gU08OW6QGCui1WVFO58bjhNI= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= @@ -509,15 +521,18 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2 github.com/go-jose/go-jose/v3 v3.0.1 h1:pWmKFVtt+Jl0vBZTIpz/eAKwsm6LkIxDVVbFHKkchhA= github.com/go-jose/go-jose/v3 v3.0.1/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= github.com/go-jose/go-jose/v4 v4.0.4/go.mod h1:NKb5HO1EZccyMpiZNbdUw/14tiXNyUJh188dfnMCAfc= +github.com/go-jose/go-jose/v4 v4.1.1/go.mod h1:BdsZGqgdO3b6tTc6LSE56wcDbMMLuPsw5d4ZD5f94kA= github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-sql-driver/mysql v1.9.2 h1:4cNKDYQ1I84SXslGddlsrMhc8k4LeDVj6Ad6WRjiHuU= github.com/go-sql-driver/mysql v1.9.2/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= +github.com/goccmack/gocc v0.0.0-20230228185258-2292f9e40198/go.mod h1:DTh/Y2+NbnOVVoypCCQrovMPDKUGp4yZpSbWg5D0XIM= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -534,9 +549,11 @@ github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0kt github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A= github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= github.com/golang/glog v1.2.2/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/glog v1.2.4/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= +github.com/golang/glog v1.2.5/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -561,6 +578,7 @@ github.com/google/flatbuffers v24.3.25+incompatible/go.mod h1:1AeVuKshWv4vARoZat github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= @@ -609,6 +627,8 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4Zs github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 h1:TmHmbvxPmaegwhDubVz0lICL0J5Ka2vwTzhoePEXsGE= github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0/go.mod h1:qztMSjm835F2bXf+5HKAPIS5qsmQDqZna/PgVt4rWtI= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1/go.mod h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90= github.com/hashicorp/cap v0.2.0/go.mod h1:zb3VvIFA0lM2lbmO69NjowV9dJzJnZS89TaM9blXPJA= github.com/hashicorp/consul-template v0.36.1-0.20240205193627-e15d61bb21ae h1:ehZNpVWpoWtMrxFE/FKvJyfjDGY384iaBccpYu13yCw= github.com/hashicorp/consul-template v0.36.1-0.20240205193627-e15d61bb21ae/go.mod h1:bvidXKwpfXzJ1X4wDw68OXnVxy5k7HLOHhOf5gnQr3M= @@ -718,7 +738,6 @@ github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9Y github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= -github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I= github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/joyent/triton-go v0.0.0-20190112182421-51ffac552869/go.mod h1:U+RSyWxWd04xTqnuOQxnai7XGS2PrPY2cfGoDKtMHjA= @@ -729,12 +748,9 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= -github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= -github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/kljensen/snowball v0.8.0/go.mod h1:OGo5gFWjaeXqCu4iIrMl5OYip9XUJHGOU5eSkPjVg2A= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/labstack/echo/v4 v4.12.0/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM= github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= @@ -746,6 +762,7 @@ github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3v github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= github.com/mfridman/xflag v0.1.0 h1:TWZrZwG1QklFX5S4j1vxfF1sZbZeZSGofMwPMLAF29M= github.com/mfridman/xflag v0.1.0/go.mod h1:/483ywM5ZO5SuMVjrIGquYNE5CzLrj5Ux/LxWWnjRaE= github.com/mholt/archiver v3.1.1+incompatible h1:1dCVxuqs0dJseYEhi5pl7MYPH9zDa1wBi7mF09cbNkU= @@ -795,7 +812,6 @@ github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DV github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To= github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= -github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b h1:YWuSjZCQAPM8UUBLkYUk1e+rZcvWHJmFb6i6rM44Xs8= github.com/opencontainers/image-spec v1.1.0-rc2.0.20221005185240-3a7f492d3f1b/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= github.com/opencontainers/runc v1.1.12 h1:BOIssBaW1La0/qbNZHXOOa71dZfZEQOzW7dqQf3phss= github.com/opencontainers/runc v1.1.12/go.mod h1:S+lQwSfncpBha7XTy/5lBwWgm5+y5Ma/O44Ekby9FK8= @@ -814,7 +830,6 @@ github.com/pdevine/tensor v0.0.0-20240510204454-f88f4562727c/go.mod h1:PSojXDXF7 github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= github.com/pierrec/lz4 v2.6.1+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/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= @@ -831,11 +846,10 @@ github.com/projectdiscovery/utils v0.0.92 h1:lGCmjUJhzoNX4FQZWpp80058pRlD0/dYxLJ github.com/projectdiscovery/utils v0.0.92/go.mod h1:d5uvD5qcRiK3qxZbBy9eatCqrCSuj9SObL04w/WgXSg= github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -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.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/renier/xmlrpc v0.0.0-20170708154548-ce4a1a486c03/go.mod h1:gRAiPF5C5Nd0eyyRdqIu9qTiFSoZzpTq727b5B8fkkU= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rs/cors v1.8.3/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= @@ -847,6 +861,7 @@ github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkB github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/seccomp/libseccomp-golang v0.10.0/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= @@ -854,7 +869,6 @@ github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= -github.com/shirou/gopsutil/v3 v3.23.9 h1:ZI5bWVeu2ep4/DIxB4U9okeYJ7zp/QLTO4auRb/ty/E= github.com/shirou/gopsutil/v3 v3.23.9/go.mod h1:x/NWSb71eMcjFIO0vhyGW5nZ7oSIgVjrCnADckb85GA= github.com/shoenig/go-landlock v1.2.0 h1:BumCuPbR7VkTB6cNZvhaDC0Apbv0zVpu2saV48mr120= github.com/shoenig/go-landlock v1.2.0/go.mod h1:S848L96G6iny3xexNb4sXUrKwEDIy5ul8hk1NBWoYws= @@ -896,6 +910,7 @@ github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cA github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= github.com/spiffe/go-spiffe/v2 v2.5.0/go.mod h1:P+NxobPc6wXhVtINNtFjNWGBTreew1GBUCwT2wPmb7g= github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= @@ -956,6 +971,8 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 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.34.0/go.mod h1:cV4BMFcscUR/ckqLkbfQmF0PRsq8w/lMGzdbCSveBHo= +go.opentelemetry.io/contrib/detectors/gcp v1.36.0/go.mod h1:IbBN8uAIIx734PTonTPxAxnjc2pQTxWNkwfstZ+6H2k= +go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.61.0/go.mod h1:p/mVr/Hs7gQnguNPXUyuiMRNtisyc9y/Oo7Kqr/6wbU= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.48.0/go.mod h1:tIKj3DbO8N9Y2xo52og3irLsPI4GW02DSMtrVgNMgxg= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.32.0/go.mod h1:5eCOqeGphOyz6TsY3ZDNjE33SM/TFAK3RGuCL2naTgY= @@ -966,6 +983,7 @@ go.opentelemetry.io/otel v1.23.0/go.mod h1:YCycw9ZeKhcJFrb34iVSkyT0iczq/zYDtZYFu go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= +go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I= go.opentelemetry.io/otel/exporters/jaeger v1.6.3/go.mod h1:YgX3eZWbJzgrNyNHCK0otGreAMBTIAcObtZS2VRi6sU= go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.6.3 h1:nAmg1WgsUXoXf46dJG9eS/AzOcvkCTK4xJSUYpWyHYg= go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.6.3/go.mod h1:NEu79Xo32iVb+0gVNV8PMd7GoWqnyDXRlj04yFjqz40= @@ -975,38 +993,47 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.6.3 h1:4/UjHWMVVc5VwX/KAtqJ go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.6.3/go.mod h1:UJmXdiVVBaZ63umRUTwJuCMAV//GCMvDiQwn703/GoY= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.7.0 h1:cMDtmgJ5FpRvqx9x2Aq+Mm0O6K/zcUkH73SFz20TuBw= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.7.0/go.mod h1:ceUgdyfNv4h4gLxHR0WNfDiiVmZFodZhZSbOLhpxqXE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0/go.mod h1:90PoxvaEB5n6AOdZvi+yWJQoE95U8Dhhw2bSyRqnTD0= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.6.3 h1:leYDq5psbM3K4QNcZ2juCj30LjUnvxjuYQj1mkGjXFM= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.6.3/go.mod h1:ycItY/esVj8c0dKgYTOztTERXtPzcfDU/0o8EdwCjoA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.36.0/go.mod h1:r49hO7CgrxY9Voaj3Xe8pANWtr0Oq916d0XAmOoCZAQ= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.6.3 h1:uSApZ0WGBOrEMNp0rtX1jtpYBh5CvktueAEHTWfLOtk= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.6.3/go.mod h1:LhMjYbVawqjXUIRbAT2CFuWtuQVxTPL8WEtxB/Iyg5Y= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.36.0/go.mod h1:PD57idA/AiFD5aqoxGxCvT/ILJPeHy3MjqU/NS7KogY= go.opentelemetry.io/otel/metric v0.30.0/go.mod h1:/ShZ7+TS4dHzDFmfi1kSXMhMVubNoP0oIaBp70J6UXU= go.opentelemetry.io/otel/metric v1.23.0/go.mod h1:MqUW2X2a6Q8RN96E2/nqNoT+z9BSms20Jb7Bbp+HiTo= go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= +go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E= go.opentelemetry.io/otel/sdk v1.6.3/go.mod h1:A4iWF7HTXa+GWL/AaqESz28VuSBIcZ+0CV+IzJ5NMiQ= go.opentelemetry.io/otel/sdk v1.7.0/go.mod h1:uTEOTwaqIVuTGiJN7ii13Ibp75wJmYUDe374q6cZwUU= go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc= go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= +go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg= go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= +go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps= go.opentelemetry.io/otel/trace v1.6.3/go.mod h1:GNJQusJlUgZl9/TQBPKU/Y/ty+0iVB5fjhKeJGZPGFs= go.opentelemetry.io/otel/trace v1.23.0/go.mod h1:GSGTbIClEsuZrGIzoEHqsVfxgn5UkggkflQwDScNUsk= go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= +go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.opentelemetry.io/proto/otlp v0.16.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= go.opentelemetry.io/proto/otlp v1.4.0 h1:TA9WRvW6zMwP+Ssb6fLoUIuirti1gGbP28GcKG1jgeg= go.opentelemetry.io/proto/otlp v1.4.0/go.mod h1:PPBWZIP98o2ElSqI35IHfu7hIhSwvc5N38Jw8pXuGFY= +go.opentelemetry.io/proto/otlp v1.6.0/go.mod h1:cicgGehlFuNdgZkcALOCh3VE6K/u2tAjzlRhDwmVpZc= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 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.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= go4.org/unsafe/assume-no-moving-gc v0.0.0-20231121144256-b99613f794b6/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E= golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= @@ -1025,6 +1052,7 @@ golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= +golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= @@ -1039,6 +1067,7 @@ golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbR golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.25.0/go.mod h1:tCAmOEGthTtkalusGp1g3xa2gke8J6c2N565dTyl9Rs= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -1057,6 +1086,7 @@ golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ= golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -1090,6 +1120,8 @@ golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= +golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= +golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1098,14 +1130,16 @@ golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4= golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA= +golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70= golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/oauth2 v0.26.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= -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.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1116,6 +1150,9 @@ golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= +golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1149,7 +1186,6 @@ golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= @@ -1158,20 +1194,22 @@ golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0= +golang.org/x/telemetry v0.0.0-20250710130107-8d8967aff50b/go.mod h1:4ZwOYna0/zsOKwuR5X/m0QFOJpSZvAxFfkQT+Erd9D4= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= -golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= +golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= +golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= @@ -1179,12 +1217,15 @@ golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= +golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= +golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= @@ -1230,10 +1271,13 @@ golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= +golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= +golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= gonum.org/v1/gonum v0.15.0/go.mod h1:xzZVBJBtS+Mz4q0Yl2LJTk+OxOg4jiXZ7qBoM0uISGo= +gonum.org/v1/plot v0.15.2/go.mod h1:DX+x+DWso3LTha+AdkJEv5Txvi+Tql3KAGkehP0/Ubg= google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= @@ -1314,8 +1358,10 @@ google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a/go. google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:jbe3Bkdp+Dh2IrslsFCklNhweNTBgSYanP1UXhJDhKg= google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34 h1:0PeQib/pH3nB/5pEmFeVQJotzGohV0dq4Vcp09H5yhE= google.golang.org/genproto/googleapis/api v0.0.0-20250428153025-10db94c68c34/go.mod h1:0awUlEkap+Pb1UMeJwJQQAdJQrt3moU7J2moTy69irI= -google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 h1:Kog3KlB4xevJlAcbbbzPfRG0+X9fdoGM+UBRKVz6Wr0= google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237/go.mod h1:ezi0AVyMKDWy5xAncvjLWH7UcLBB5n7y2fQ8MzjJcto= +google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:kXqgZtrWaf6qS3jZOCnCH7WYfrvFjkC51bM8fz3RsCA= +google.golang.org/genproto/googleapis/api v0.0.0-20250728155136-f173205681a0/go.mod h1:8ytArBbtOy2xfht+y2fqKd5DRDJRUQhqbyEnQ4bDChs= +google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c/go.mod h1:ea2MjsO70ssTfCjiwHgI0ZFqcw45Ksuk2ckf9G468GA= google.golang.org/genproto/googleapis/bytestream v0.0.0-20240304161311-37d4d3c04a78/go.mod h1:vh/N7795ftP0AkN1w8XKqN4w1OdUKXW5Eummda+ofv8= google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= google.golang.org/genproto/googleapis/rpc v0.0.0-20230803162519-f966b187b2e5/go.mod h1:zBEcrKX2ZOcEkHWxBPAIvYUWOKKMIhYcmNiUIu2ji3I= @@ -1336,8 +1382,10 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20250425173222-7b384671a197/go. google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34 h1:h6p3mQqrmT1XkHVTfzLdNz1u7IhINeZkz67/xTbOuWs= google.golang.org/genproto/googleapis/rpc v0.0.0-20250428153025-10db94c68c34/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/genproto/googleapis/rpc v0.0.0-20250512202823-5a2f75b736a9/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 h1:cJfm9zPbe1e873mHJzmQ1nwVEeRDU/T1wXDK2kUSU34= google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250728155136-f173205681a0/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c/go.mod h1:gw1tLEfykwDz2ET4a12jcXt4couGAm7IwsVaTy0Sflo= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= @@ -1370,6 +1418,8 @@ google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg= google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= google.golang.org/grpc v1.72.0 h1:S7UkcVa60b5AAQTaO6ZKamFp1zMZSU0fGDK2WZLbBnM= google.golang.org/grpc v1.72.0/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= +google.golang.org/grpc v1.72.2/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= +google.golang.org/grpc v1.74.2/go.mod h1:CtQ+BGjaAIXHs/5YS3i473GqwBBa1zGQNevxdeBEXrM= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/grpc/examples v0.0.0-20230224211313-3775f633ce20/go.mod h1:Nr5H8+MlGWr5+xX/STzdoEqJrO+YteqFbMyCsrb6mH0= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= @@ -1381,6 +1431,7 @@ google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojt google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= @@ -1412,6 +1463,7 @@ nhooyr.io/websocket v1.8.11/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+ oss.indeed.com/go/libtime v1.6.0 h1:XQyczJihse/wQGo59OfPF3f4f+Sywv4R8vdGB3S9BfU= oss.indeed.com/go/libtime v1.6.0/go.mod h1:B2sdEcuzB0zhTKkAuHy4JInKRc7Al3tME4qWam6R7mA= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= diff --git a/maparoon/go.mod b/maparoon/go.mod index c28a9eb..23a40b9 100644 --- a/maparoon/go.mod +++ b/maparoon/go.mod @@ -1,25 +1,24 @@ module github.com/pirogoeth/apps/maparoon -go 1.22.3 +go 1.24.1 replace github.com/pirogoeth/apps => ../ require ( github.com/adifire/go-nmap v0.0.0-20191115215512-1ff00bdab236 github.com/blevesearch/bleve v1.0.14 - github.com/gin-gonic/gin v1.10.0 + github.com/gin-gonic/gin v1.10.1 github.com/gosnmp/gosnmp v1.37.0 - github.com/imroc/req/v3 v3.43.5 - github.com/mattn/go-sqlite3 v1.14.22 - github.com/pirogoeth/apps v0.0.0-20240504031750-d466f362ff13 + github.com/imroc/req/v3 v3.48.0 + github.com/mattn/go-sqlite3 v1.14.28 github.com/projectdiscovery/goflags v0.1.53 github.com/projectdiscovery/naabu/v2 v2.3.1 github.com/sirupsen/logrus v1.9.3 github.com/sleepinggenius2/gosmi v0.4.4 - github.com/spf13/cobra v1.8.0 - golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 - golang.org/x/sync v0.7.0 - golang.org/x/sys v0.21.0 + github.com/spf13/cobra v1.9.1 + golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 + golang.org/x/sync v0.16.0 + golang.org/x/sys v0.35.0 ) require ( @@ -32,11 +31,10 @@ require ( github.com/akrylysov/pogreb v0.10.1 // indirect github.com/alecthomas/chroma v0.10.0 // indirect github.com/alecthomas/participle v0.4.1 // indirect - github.com/andybalholm/brotli v1.1.0 // indirect + github.com/andybalholm/brotli v1.1.1 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/aymerick/douceur v0.2.0 // indirect - github.com/beorn7/perks v1.0.1 // indirect github.com/blevesearch/go-porterstemmer v1.0.3 // indirect github.com/blevesearch/mmap-go v1.0.2 // indirect github.com/blevesearch/segment v0.9.0 // indirect @@ -46,52 +44,47 @@ require ( github.com/blevesearch/zap/v13 v13.0.6 // indirect github.com/blevesearch/zap/v14 v14.0.5 // indirect github.com/blevesearch/zap/v15 v15.0.3 // indirect - github.com/bytedance/sonic v1.11.6 // indirect - github.com/bytedance/sonic/loader v0.1.1 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/bytedance/sonic v1.13.2 // indirect + github.com/bytedance/sonic/loader v0.2.4 // indirect github.com/charmbracelet/glamour v0.6.0 // indirect github.com/cheggaaa/pb/v3 v3.1.4 // indirect - github.com/cloudflare/circl v1.3.7 // indirect - github.com/cloudwego/base64x v0.1.4 // indirect - github.com/cloudwego/iasm v0.2.0 // indirect + github.com/cloudflare/circl v1.4.0 // indirect + github.com/cloudwego/base64x v0.1.5 // indirect github.com/cnf/structhash v0.0.0-20201127153200-e1b16c1ebc08 // indirect github.com/couchbase/vellum v1.0.2 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dimchansky/utfbom v1.1.1 // indirect github.com/dlclark/regexp2 v1.8.1 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect github.com/fatih/color v1.15.0 // indirect - github.com/gabriel-vasile/mimetype v1.4.3 // indirect - github.com/ghodss/yaml v1.0.0 // indirect - github.com/gin-contrib/sse v0.1.0 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.9 // indirect + github.com/gin-contrib/sse v1.1.0 // indirect github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2 // indirect + github.com/go-logr/logr v1.4.3 // indirect github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.20.0 // indirect - github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect - github.com/goccy/go-json v0.10.2 // indirect - github.com/golang/protobuf v1.5.3 // indirect + github.com/go-playground/validator/v10 v10.26.0 // indirect + github.com/go-task/slim-sprig/v3 v3.0.0 // indirect + github.com/goccy/go-json v0.10.5 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect + github.com/google/go-cmp v0.7.0 // indirect github.com/google/go-github/v30 v30.1.0 // indirect - github.com/google/go-github/v58 v58.0.0 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/gopacket v1.1.19 // indirect - github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7 // indirect - github.com/google/uuid v1.3.1 // indirect + github.com/google/pprof v0.0.0-20240910150728-a0b0bb1d4134 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/gorilla/css v1.0.0 // indirect - github.com/gorilla/websocket v1.5.0 // indirect - github.com/hashicorp/cronexpr v1.1.2 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect - github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/hashicorp/go-rootcerts v1.0.2 // indirect - github.com/hashicorp/nomad/api v0.0.0-20231227080007-76ba3e10e73d // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/kelseyhightower/envconfig v1.4.0 // indirect - github.com/klauspost/compress v1.17.7 // indirect - github.com/klauspost/cpuid/v2 v2.2.7 // indirect + github.com/klauspost/compress v1.18.0 // indirect + github.com/klauspost/cpuid/v2 v2.2.10 // indirect github.com/klauspost/pgzip v1.2.5 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/logrusorgru/aurora v2.0.3+incompatible // indirect @@ -104,8 +97,6 @@ require ( github.com/microcosm-cc/bluemonday v1.0.26 // indirect github.com/miekg/dns v1.1.58 // indirect github.com/minio/selfupdate v0.6.1-0.20230907112617-f11e74f84ca7 // indirect - github.com/mitchellh/go-homedir v1.1.0 // 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/mschoch/smat v0.2.0 // indirect @@ -113,11 +104,12 @@ require ( github.com/muesli/termenv v0.15.1 // indirect github.com/nwaples/rardecode v1.1.3 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect - github.com/onsi/ginkgo/v2 v2.16.0 // indirect - github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/onsi/ginkgo/v2 v2.20.2 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/philhofer/fwd v1.0.0 // indirect - github.com/pierrec/lz4/v4 v4.1.2 // indirect + github.com/pierrec/lz4/v4 v4.1.22 // indirect github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/projectdiscovery/asnmap v1.1.0 // indirect github.com/projectdiscovery/blackrock v0.0.1 // indirect @@ -137,20 +129,19 @@ require ( github.com/projectdiscovery/retryablehttp-go v1.0.59 // indirect github.com/projectdiscovery/uncover v1.0.7 // indirect github.com/projectdiscovery/utils v0.0.93 // indirect - github.com/prometheus/client_golang v1.19.1 // indirect - github.com/prometheus/client_model v0.5.0 // indirect - github.com/prometheus/common v0.48.0 // indirect - github.com/prometheus/procfs v0.12.0 // indirect - github.com/quic-go/qpack v0.4.0 // indirect - github.com/quic-go/quic-go v0.42.0 // indirect - github.com/refraction-networking/utls v1.6.3 // indirect + github.com/quic-go/qpack v0.5.1 // indirect + github.com/quic-go/quic-go v0.47.0 // indirect + github.com/refraction-networking/utls v1.6.7 // indirect github.com/remeh/sizedwaitgroup v1.0.0 // indirect github.com/rivo/uniseg v0.4.4 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d // indirect - github.com/shirou/gopsutil/v3 v3.23.7 // indirect + github.com/shirou/gopsutil/v3 v3.23.9 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect - github.com/spf13/pflag v1.0.5 // indirect + github.com/shoenig/test v1.7.1 // indirect + github.com/spf13/pflag v1.0.6 // indirect github.com/steveyen/gtreap v0.1.0 // indirect + github.com/stretchr/testify v1.11.1 // indirect github.com/syndtr/goleveldb v1.0.0 // indirect github.com/tidwall/btree v1.6.0 // indirect github.com/tidwall/buntdb v1.3.0 // indirect @@ -179,17 +170,16 @@ require ( go.etcd.io/bbolt v1.3.7 // indirect go.uber.org/mock v0.4.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/arch v0.8.0 // indirect - golang.org/x/crypto v0.24.0 // indirect - golang.org/x/mod v0.17.0 // indirect - golang.org/x/net v0.26.0 // indirect - golang.org/x/oauth2 v0.16.0 // indirect - golang.org/x/term v0.21.0 // indirect - golang.org/x/text v0.16.0 // indirect - golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect - google.golang.org/appengine v1.6.7 // indirect - google.golang.org/protobuf v1.34.2 // indirect + golang.org/x/arch v0.17.0 // indirect + golang.org/x/crypto v0.41.0 // indirect + golang.org/x/mod v0.26.0 // indirect + golang.org/x/net v0.43.0 // indirect + golang.org/x/oauth2 v0.30.0 // indirect + golang.org/x/term v0.34.0 // indirect + golang.org/x/text v0.28.0 // indirect + golang.org/x/time v0.14.0 // indirect + golang.org/x/tools v0.35.0 // indirect + google.golang.org/protobuf v1.36.8 // indirect gopkg.in/djherbis/times.v1 v1.3.0 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/maparoon/go.sum b/maparoon/go.sum index 7cc463b..c37a571 100644 --- a/maparoon/go.sum +++ b/maparoon/go.sum @@ -28,8 +28,7 @@ github.com/alecthomas/repr v0.0.0-20210301060118-828286944d6a/go.mod h1:2kn6fqh/ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= -github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= -github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= +github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= @@ -38,8 +37,6 @@ github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiE github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= -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.8.0 h1:FD+XqgOZDUxxZ8hzoBFuV9+cGWY9CslN6d5MS5JVb4c= github.com/bits-and-blooms/bitset v1.8.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/bits-and-blooms/bloom/v3 v3.5.0 h1:AKDvi1V3xJCmSR6QhcBfHbCN4Vf8FfxeWkMNQfmAGhY= @@ -68,22 +65,16 @@ github.com/blevesearch/zap/v14 v14.0.5/go.mod h1:bWe8S7tRrSBTIaZ6cLRbgNH4TUDaC9L github.com/blevesearch/zap/v15 v15.0.3 h1:Ylj8Oe+mo0P25tr9iLPp33lN6d4qcztGjaIsP51UxaY= github.com/blevesearch/zap/v15 v15.0.3/go.mod h1:iuwQrImsh1WjWJ0Ue2kBqY83a0rFtJTqfa9fp1rbVVU= github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= -github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= -github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= -github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= +github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ= github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY= github.com/charmbracelet/glamour v0.6.0 h1:wi8fse3Y7nfcabbbDuwolqTqMQPMnVPeZhDM273bISc= github.com/charmbracelet/glamour v0.6.0/go.mod h1:taqWV4swIMMbWALc0m7AfE9JkPSU8om2538k9ITBxOc= github.com/cheggaaa/pb/v3 v3.1.4 h1:DN8j4TVVdKu3WxVwcRKu0sG00IIU6FewoABZzXbRQeo= github.com/cheggaaa/pb/v3 v3.1.4/go.mod h1:6wVjILNBaXMs8c21qRiaUM8BR82erfgau1DQ4iUXmSA= github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= -github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= -github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= -github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= -github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= -github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= +github.com/cloudflare/circl v1.4.0 h1:BV7h5MgrktNzytKmWjpOtdYrf0lkkbF8YMlBGPhJQrY= +github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4= github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= github.com/cnf/structhash v0.0.0-20201127153200-e1b16c1ebc08 h1:ox2F0PSMlrAAiAdknSRMDrAr8mfxPCfSZolH+/qQnyQ= github.com/cnf/structhash v0.0.0-20201127153200-e1b16c1ebc08/go.mod h1:pCxVEbcm3AMg7ejXyorUXi6HQCzOIBf7zEDVPtw0/U4= @@ -95,14 +86,13 @@ github.com/couchbase/moss v0.1.0/go.mod h1:9MaHIaRuy9pvLPUJxB8sh8OrLfyDczECVL37g github.com/couchbase/vellum v1.0.2 h1:BrbP0NKiyDdndMPec8Jjhy0U47CZ0Lgx3xUC2r9rZqw= github.com/couchbase/vellum v1.0.2/go.mod h1:FcwrEivFpNi24R3jLOs3n+fs5RnuQnQqCLBJ1uAg1W4= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= -github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d h1:SwD98825d6bdB+pEuTxWOXiSjBrHdOl/UVp75eI7JT8= github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d/go.mod h1:URriBxXwVq5ijiJ12C7iIZqlA69nTlI+LgI6/pwftG8= github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM= github.com/cznic/strutil v0.0.0-20181122101858-275e90344537/go.mod h1:AHHPPPXTw0h6pVabbcbyGRK1DckRn7r/STdZEeIDzZc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= @@ -118,25 +108,16 @@ github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqL github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0= github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= -github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= -github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= -github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= -github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= -github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= -github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= -github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY= +github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= +github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ= github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2 h1:Ujru1hufTHVb++eG6OuNDKMxZnGIvF6o/u8q/8h2+I4= github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31 h1:gclg6gY70GLy3PbkQ1AERPfmLMMagS60DKF78eWwLn8= github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= -github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= -github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= 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-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= @@ -145,19 +126,15 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= -github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= -github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= -github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k= +github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -168,52 +145,34 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-github/v30 v30.1.0 h1:VLDx+UolQICEOKu2m4uAoMti1SxuEBAl7RSEG16L+Oo= github.com/google/go-github/v30 v30.1.0/go.mod h1:n8jBpHl45a/rlBUtRJMOG4GhNADUQFEufcolZ95JfU8= github.com/google/go-github/v50 v50.2.0/go.mod h1:VBY8FB6yPIjrtKhozXv4FQupxKLS6H4m6xFZlT43q8Q= -github.com/google/go-github/v58 v58.0.0 h1:Una7GGERlF/37XfkPwpzYJe0Vp4dt2k1kCjlxwjIvzw= -github.com/google/go-github/v58 v58.0.0/go.mod h1:k4hxDKEfoWpSqFlc8LTpGd9fu2KrV1YAa6Hi6FmDNY4= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= -github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7 h1:y3N7Bm7Y9/CtpiVkw/ZWj6lSlDF3F74SfKwfTCer72Q= -github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= -github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= -github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/pprof v0.0.0-20240910150728-a0b0bb1d4134 h1:c5FlPPgxOn7kJz3VoPLkQYQXGBS3EklQ4Zfi57uOuqQ= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 h1:twflg0XRTjwKpxb/jFExr4HGq6on2dEOmnL6FV+fgPw= github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gosnmp/gosnmp v1.37.0 h1:/Tf8D3b9wrnNuf/SfbvO+44mPrjVphBhRtcGg22V07Y= github.com/gosnmp/gosnmp v1.37.0/go.mod h1:GDH9vNqpsD7f2HvZhKs5dlqSEcAS6s6Qp099oZRCR+M= -github.com/hashicorp/cronexpr v1.1.2 h1:wG/ZYIKT+RT3QkOdgYc+xsKWVRgnxJ1OJtjjy84fJ9A= -github.com/hashicorp/cronexpr v1.1.2/go.mod h1:P4wA0KBl9C5q2hABiMO7cp6jcIg96CDh1Efb3g1PWA4= 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-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= -github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= 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/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= -github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= -github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= -github.com/hashicorp/golang-lru/v2 v2.0.6 h1:3xi/Cafd1NaoEnS/yDssIiuVeDVywU0QdFGl3aQaQHM= -github.com/hashicorp/golang-lru/v2 v2.0.6/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/nomad/api v0.0.0-20231227080007-76ba3e10e73d h1:Ztvo+OG+/xc6GQFiKME65qY6nveab1c7Hy8TFdBzbUI= -github.com/hashicorp/nomad/api v0.0.0-20231227080007-76ba3e10e73d/go.mod h1:ijDwa6o1uG1jFSq6kERiX2PamKGpZzTmo0XOFNeFZgw= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ikawaha/kagome.ipadic v1.1.2/go.mod h1:DPSBbU0czaJhAb/5uKQZHMc9MTVRpDugJfX+HddPHHg= -github.com/imroc/req/v3 v3.43.5 h1:fL7dOEfld+iEv1rwnIxseJz2/Y7JZ/HgbAURLZkat80= -github.com/imroc/req/v3 v3.43.5/go.mod h1:SQIz5iYop16MJxbo8ib+4LnostGCok8NQf8ToyQc2xA= +github.com/imroc/req/v3 v3.48.0 h1:IYuMGetuwLzOOTzDCquDqs912WNwpsPK0TBXWPIvoqg= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= @@ -225,16 +184,12 @@ github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7 github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= -github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg= -github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= -github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE= github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/kljensen/snowball v0.6.0/go.mod h1:27N7E8fVU5H68RlUmnWwZCfxgt4POBJfENGMvNRhldw= @@ -265,8 +220,7 @@ github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= -github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A= github.com/mholt/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Clwo= github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4= github.com/microcosm-cc/bluemonday v1.0.21/go.mod h1:ytNkv4RrDrLJ2pqlsSI46O6IVXmZOBBD4SaJyDwwTkM= @@ -276,13 +230,8 @@ github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4= github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY= github.com/minio/selfupdate v0.6.1-0.20230907112617-f11e74f84ca7 h1:yRZGarbxsRytL6EGgbqK2mCY+Lk5MWKQYKJT2gEglhc= github.com/minio/selfupdate v0.6.1-0.20230907112617-f11e74f84ca7/go.mod h1:bO02GTIPCMQFTEvE5h4DjYB58bCoZ35XLeBf0buTDdM= -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/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= -github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -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/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= @@ -309,24 +258,21 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/ginkgo/v2 v2.16.0 h1:7q1w9frJDzninhXxjZd+Y/x54XNjG/UlRLIYPZafsPM= -github.com/onsi/ginkgo/v2 v2.16.0/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs= +github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8= -github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= +github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= -github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/philhofer/fwd v1.0.0 h1:UbZqGr5Y38ApvM/V/jEljVxwocdweyH+vmYvRPBnbqQ= github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= -github.com/pierrec/lz4/v4 v4.1.2 h1:qvY3YFXRQE/XB8MlLzJH7mSzBs74eA2gg52YTk6jUPM= github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= 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/projectdiscovery/asnmap v1.1.0 h1:ynvbLB5cNpyQ2+k9IP0Rpla+0JmCJpd3mw6KLAW13m0= @@ -369,21 +315,10 @@ github.com/projectdiscovery/uncover v1.0.7 h1:ut+2lTuvmftmveqF5RTjMWAgyLj8ltPQC7 github.com/projectdiscovery/uncover v1.0.7/go.mod h1:HFXgm1sRPuoN0D4oATljPIdmbo/EEh1wVuxQqo/dwFE= github.com/projectdiscovery/utils v0.0.93 h1:IMZFsmQFYZUf7rxpBoZj+53FsNDC/vHsXA+4B4GuGeg= github.com/projectdiscovery/utils v0.0.93/go.mod h1:2+mWzk5FeYdK9imo5eLk6oVeih0G0wsTff1pzBAh9tk= -github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= -github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= -github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= -github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= -github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= -github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= -github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= -github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= -github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= -github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= -github.com/quic-go/quic-go v0.42.0 h1:uSfdap0eveIl8KXnipv9K7nlwZ5IqLlYOpJ58u5utpM= -github.com/quic-go/quic-go v0.42.0/go.mod h1:132kz4kL3F9vxhW3CtQJLDVwcFe5wdWeJXXijhsO57M= +github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= +github.com/quic-go/quic-go v0.47.0 h1:yXs3v7r2bm1wmPTYNLKAAJTHMYkPEsfYJmTazXrCZ7Y= github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/refraction-networking/utls v1.6.3 h1:MFOfRN35sSx6K5AZNIoESsBuBxS2LCgRilRIdHb6fDc= -github.com/refraction-networking/utls v1.6.3/go.mod h1:yil9+7qSl+gBwJqztoQseO6Pr3h62pQoY1lXiNR/FPs= +github.com/refraction-networking/utls v1.6.7 h1:zVJ7sP1dJx/WtVuITug3qYUq034cDq9B2MR1K67ULZM= github.com/remeh/sizedwaitgroup v1.0.0 h1:VNGGFwNo/R5+MJBf6yrsr110p0m4/OX4S3DCy7Kyl5E= github.com/remeh/sizedwaitgroup v1.0.0/go.mod h1:3j2R4OIe/SeS6YDhICBy22RWjJC5eNCJ1V+9+NVNYlo= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= @@ -391,19 +326,16 @@ github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d h1:hrujxIzL1woJ7AwssoOcM/tq5JjjG2yYOc8odClEiXA= github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU= -github.com/shirou/gopsutil/v3 v3.23.7 h1:C+fHO8hfIppoJ1WdsVm1RoI0RwXoNdfTK7yWXV0wVj4= -github.com/shirou/gopsutil/v3 v3.23.7/go.mod h1:c4gnmoRC0hQuaLqvxnx1//VXQ0Ms/X9UnJF8pddY5z4= +github.com/shirou/gopsutil/v3 v3.23.9 h1:ZI5bWVeu2ep4/DIxB4U9okeYJ7zp/QLTO4auRb/ty/E= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= -github.com/shoenig/test v1.7.0 h1:eWcHtTXa6QLnBvm0jgEabMRN/uJ4DMV3M8xUGgRkZmk= -github.com/shoenig/test v1.7.0/go.mod h1:UxJ6u/x2v/TNs/LoLxBNJRV9DiwBBKYxXSyczsBHFoI= +github.com/shoenig/test v1.7.1 h1:UJcjSAI3aUKx52kfcfhblgyhZceouhvvs3OYdWgn+PY= github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= @@ -414,12 +346,10 @@ github.com/sleepinggenius2/gosmi v0.4.4/go.mod h1:l8OniPmd3bJzw0MXP2/qh7AhP/e+bT github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= -github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= +github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/steveyen/gtreap v0.1.0 h1:CjhzTa274PyJLJuMZwIzCO1PfC00oRa8d1Kc78bFXJM= github.com/steveyen/gtreap v0.1.0/go.mod h1:kl/5J7XbrOmlIbYIXdRHDDE5QxHqpk0cmkT7Z4dM9/Y= @@ -427,18 +357,15 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= github.com/tebeka/snowball v0.4.2/go.mod h1:4IfL14h1lvwZcp1sfXuuc7/7yCsvVffTWxWxCLfFpYg= @@ -468,10 +395,8 @@ github.com/tidwall/tinyqueue v0.1.1 h1:SpNEvEggbpyN5DIReaJ2/1ndroY8iyEGxPYxoSaym github.com/tidwall/tinyqueue v0.1.1/go.mod h1:O/QNHwrnjqr6IHItYrzoHAKYhBkLI67Q096fQP5zMYw= github.com/tinylib/msgp v1.1.0 h1:9fQd+ICuRIu/ue4vxJZu6/LzxN0HwMds2nq/0cFvxHU= github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= -github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI= 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.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4= github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= @@ -491,6 +416,7 @@ github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPy github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= github.com/yl2chen/cidranger v1.0.2 h1:lbOWZVCG1tCRX4u24kuM1Tb4nHqWkDxwLdoS+SevawU= github.com/yl2chen/cidranger v1.0.2/go.mod h1:9U1yz7WPYDwf0vpNWFaeRh0bjwz5RVgRy/9UEQfHl0g= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -522,9 +448,7 @@ go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= -golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= +golang.org/x/arch v0.17.0 h1:4O3dfLzd+lQewptAHqjewQZQDyEdejz3VwgeYwkZneU= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -537,16 +461,13 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= -golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= -golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= -golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ= -golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= +golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= +golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 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= @@ -562,18 +483,15 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= -golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= -golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= -golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= -golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -597,14 +515,12 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -612,8 +528,7 @@ golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= -golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= -golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= +golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -624,29 +539,24 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= -golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= -golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= +golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -659,10 +569,7 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkep gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/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= nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/moneyman/Makefile b/moneyman/Makefile new file mode 100644 index 0000000..ccc3db6 --- /dev/null +++ b/moneyman/Makefile @@ -0,0 +1,23 @@ +sqlc_generated := database/db.go database/models.go database/queries.sql database/queries.sql.go + +moneyman: **/*.go $(sqlc_generated) snmpsmi/netSnmpMibs/*.txt + go build $(buildargs) ./ + +$(sqlc_generated): sqlc.yml database/queries/*.sql database/schema.sql + awk '1; END {print ""}' database/queries/*.sql > database/queries.sql + sqlc vet + sqlc generate + +.PHONY: run-debug +run-debug: moneyman + LOG_LEVEL=DEBUG DATABASE_PATH=./moneyman.db ./moneyman serve + +.PHONY: clean +clean: + rm -f moneyman + rm -f $(sqlc_generated) + +.PHONY: clean-data +clean-data: + rm -f moneyman.db + rm -rf index diff --git a/moneyman/api/api.go b/moneyman/api/api.go new file mode 100644 index 0000000..a5d32ba --- /dev/null +++ b/moneyman/api/api.go @@ -0,0 +1,30 @@ +package api + +import ( + "context" + "fmt" + "net/http" + "strings" + + "github.com/gin-gonic/gin" + mcpServer "github.com/mark3labs/mcp-go/server" + + "github.com/pirogoeth/apps/orba/database" + "github.com/pirogoeth/apps/orba/types" + api "github.com/pirogoeth/apps/pkg/apitools" +) + +var ( + ErrDatabaseDelete = "database delete failed" + ErrDatabaseInsert = "database insert failed" + ErrDatabaseLookup = "database lookup failed" + ErrDatabaseUpdate = "database update failed" + + ErrUserLookup = "database `user` lookup failed" +) + +func MustRegister(router *gin.Engine, apiContext *types.ApiContext) error { + // groupV1 := router.Group("/v1") + + return nil +} diff --git a/moneyman/cmd/moneyman/main.go b/moneyman/cmd/moneyman/main.go new file mode 100644 index 0000000..02a0a94 --- /dev/null +++ b/moneyman/cmd/moneyman/main.go @@ -0,0 +1,49 @@ +package cmd + +import ( + "fmt" + + "github.com/pirogoeth/apps/moneyman/types" + "github.com/pirogoeth/apps/pkg/config" + "github.com/pirogoeth/apps/pkg/logging" + "github.com/pirogoeth/apps/pkg/tracing" + "github.com/spf13/cobra" +) + +const ( + AppName = "moneyman" + ComponentApi = "api" +) + +var rootCmd = &cobra.Command{ + Use: "moneyman", + Short: "Money management", +} + +func init() { + rootCmd.AddCommand(runCmd) +} + +func appStart(component string) *types.Config { + logging.Setup( + logging.WithAppName(AppName), + logging.WithComponentName(component), + ) + + cfg, err := config.Load[types.Config]() + if err != nil { + panic(fmt.Errorf("could not start (config): %w", err)) + } + + tracing.Setup( + tracing.WithAppName(AppName), + tracing.WithComponentName(component), + tracing.WithConfig(cfg.CommonConfig.Tracing), + ) + + return cfg +} + +func main() { + rootCmd.Execute() +} diff --git a/moneyman/cmd/moneyman/run.go b/moneyman/cmd/moneyman/run.go new file mode 100644 index 0000000..1cbc001 --- /dev/null +++ b/moneyman/cmd/moneyman/run.go @@ -0,0 +1,65 @@ +package cmd + +import ( + "context" + "fmt" + "os" + + "github.com/gin-gonic/gin" + "github.com/pirogoeth/apps/pkg/system" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + + "github.com/pirogoeth/apps/moneyman/api" + "github.com/pirogoeth/apps/moneyman/database" + "github.com/pirogoeth/apps/moneyman/types" +) + +var runCmd = &cobra.Command{ + Use: "run", + Short: "Run the moneyman API and clients", + Run: runFunc, +} + +type App struct { + cfg *types.Config +} + +func runFunc(cmd *cobra.Command, args []string) { + cfg := appStart(ComponentApi) + gin.EnableJsonDecoderDisallowUnknownFields() + app := &App{cfg} + + ctx, cancel := context.WithCancel(context.Background()) + dbWrapper, err := database.Open(ctx, cfg.Database) + if err != nil { + panic(fmt.Errorf("could not start (database): %w", err)) + } + + apiContext := &types.ApiContext{ + Config: app.cfg, + Querier: dbWrapper.Querier(), + } + + router, err := system.DefaultRouterWithTracing(ctx, cfg.Tracing) + if err != nil { + panic(fmt.Errorf("could not start (tracing router): %w", err)) + } + + if err := api.MustRegister(router, apiContext); err != nil { + panic(fmt.Errorf("could not start (api): %w", err)) + } + + go router.Run(app.cfg.HTTP.ListenAddress) + + sw := system.NewSignalWaiter(os.Interrupt) + sw.OnBeforeCancel(func(context.Context) error { + if err := dbWrapper.Close(); err != nil { + panic(fmt.Errorf("could not safely close database: %w", err)) + } + logrus.Infof("closed database") + + return nil + }) + sw.Wait(ctx, cancel) +} diff --git a/moneyman/database/config.go b/moneyman/database/config.go new file mode 100644 index 0000000..aa7300c --- /dev/null +++ b/moneyman/database/config.go @@ -0,0 +1,9 @@ +package database + +type Config struct { + Postgres *PostgresConfig `json:"postgres"` +} + +type PostgresConfig struct { + Uri string `json:"uri" envconfig:"DATABASE_POSTGRES_URI"` +} diff --git a/functional/database/db.go b/moneyman/database/db.go similarity index 97% rename from functional/database/db.go rename to moneyman/database/db.go index ef3e100..61f5bf4 100644 --- a/functional/database/db.go +++ b/moneyman/database/db.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.29.0 +// sqlc v1.25.0 package database diff --git a/functional/database/migrations.go b/moneyman/database/migrations.go similarity index 70% rename from functional/database/migrations.go rename to moneyman/database/migrations.go index 2761921..c7b0501 100644 --- a/functional/database/migrations.go +++ b/moneyman/database/migrations.go @@ -3,4 +3,4 @@ package database import "embed" //go:embed migrations/*.sql -var MigrationsFS embed.FS \ No newline at end of file +var MigrationsFS embed.FS diff --git a/moneyman/database/migrations/pg/001-initial.sql b/moneyman/database/migrations/pg/001-initial.sql new file mode 100644 index 0000000..e69de29 diff --git a/moneyman/database/models.go b/moneyman/database/models.go new file mode 100644 index 0000000..a009ffc --- /dev/null +++ b/moneyman/database/models.go @@ -0,0 +1,57 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.25.0 + +package database + +import () + +type Host struct { + Address string `json:"address"` + NetworkID int64 `json:"network_id"` + Comments string `json:"comments"` +} + +type HostAttribute struct { + Address string `json:"address"` + Key string `json:"key"` + Value string `json:"value"` +} + +type HostPort struct { + Address string `json:"address"` + Port int64 `json:"port"` + Protocol string `json:"protocol"` + Comments string `json:"comments"` +} + +type HostPortAttribute struct { + Address string `json:"address"` + Port int64 `json:"port"` + Protocol string `json:"protocol"` + Key string `json:"key"` + Value string `json:"value"` +} + +type Network struct { + ID int64 `json:"id"` + Name string `json:"name"` + Address string `json:"address"` + Cidr int64 `json:"cidr"` + Comments string `json:"comments"` +} + +type NetworkAttribute struct { + NetworkID int64 `json:"network_id"` + Key string `json:"key"` + Value string `json:"value"` +} + +type NetworkScan struct { + ID int64 `json:"id"` + NetworkID int64 `json:"network_id"` + StartedAt int64 `json:"started_at"` + FinishedAt int64 `json:"finished_at"` + HostsFound int64 `json:"hosts_found"` + PortsFound int64 `json:"ports_found"` +} diff --git a/moneyman/database/queries.sql b/moneyman/database/queries.sql new file mode 100644 index 0000000..7fc6cbc --- /dev/null +++ b/moneyman/database/queries.sql @@ -0,0 +1,112 @@ +-- name: GetHostWithPortsByAddress :many +select + net.name as network_name, + net.address as network_address, + net.cidr as network_cidr_size, + h.comments as host_comments, + h.address as host_address, + hp.port as port_number, + hp.protocol as port_protocol, + hp.comments as port_comments +from hosts h +left join host_ports hp +on hp.address = h.address +left join networks net +on net.id = h.network_id +where h.address = ?; +-- name: ListHostPorts :many +select * from host_ports; + +-- name: ListHostPortsByHostAddress :many +select * from host_ports +where address = ?; + +-- name: CreateHostPort :one +insert into host_ports ( + address, port, protocol, comments +) values ( + ?, ?, ?, ? +) +returning *; + +-- name: GetHostPort :one +select * from host_ports +where address = ? + and port = ? + and protocol = ?; + +-- name: UpdateHostPort :one +update host_ports +set + comments = ? +where + address = ? + and port = ? + and protocol = ? +returning *; + +-- name: DeleteHostPort :exec +delete from host_ports +where + address = ? + and port = ? + and protocol = ?; +-- name: ListHosts :many +select * from hosts; + +-- name: GetHost :one +select * from hosts +where address = ? limit 1; + +-- name: GetHostWithNetwork :one +select * from hosts +where address = ? and network_id = ? limit 1; + +-- name: CreateHost :one +insert into hosts ( + network_id, address, comments +) values ( + ?, ?, ? +) +returning *; + +-- name: UpdateHost :one +update hosts +set + comments = ? +where address = ? +returning *; + +-- name: DeleteHost :exec +delete from hosts +where address = ?; +-- name: GetNetworkById :one +select * from networks +where id = ? limit 1; + +-- name: GetNetworkByAddress :one +select * from networks +where address = ? limit 1; + +-- name: DeleteNetwork :exec +delete from networks where id = ?; + +-- name: CreateNetwork :one +insert into networks ( + name, address, cidr, comments +) values ( + ?, ?, ?, ? +) +returning *; + +-- name: UpdateNetwork :one +update networks +set + name = ?, + comments = ? +where id = ? +returning *; + +-- name: ListNetworks :many +select * from networks; + diff --git a/moneyman/database/queries.sql.go b/moneyman/database/queries.sql.go new file mode 100644 index 0000000..8f77e1a --- /dev/null +++ b/moneyman/database/queries.sql.go @@ -0,0 +1,497 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.25.0 +// source: queries.sql + +package database + +import ( + "context" + "database/sql" +) + +const createHost = `-- name: CreateHost :one +insert into hosts ( + network_id, address, comments +) values ( + ?, ?, ? +) +returning address, network_id, comments +` + +type CreateHostParams struct { + NetworkID int64 `json:"network_id"` + Address string `json:"address"` + Comments string `json:"comments"` +} + +func (q *Queries) CreateHost(ctx context.Context, arg CreateHostParams) (Host, error) { + row := q.db.QueryRowContext(ctx, createHost, arg.NetworkID, arg.Address, arg.Comments) + var i Host + err := row.Scan(&i.Address, &i.NetworkID, &i.Comments) + return i, err +} + +const createHostPort = `-- name: CreateHostPort :one +insert into host_ports ( + address, port, protocol, comments +) values ( + ?, ?, ?, ? +) +returning address, port, protocol, comments +` + +type CreateHostPortParams struct { + Address string `json:"address"` + Port int64 `json:"port"` + Protocol string `json:"protocol"` + Comments string `json:"comments"` +} + +func (q *Queries) CreateHostPort(ctx context.Context, arg CreateHostPortParams) (HostPort, error) { + row := q.db.QueryRowContext(ctx, createHostPort, + arg.Address, + arg.Port, + arg.Protocol, + arg.Comments, + ) + var i HostPort + err := row.Scan( + &i.Address, + &i.Port, + &i.Protocol, + &i.Comments, + ) + return i, err +} + +const createNetwork = `-- name: CreateNetwork :one +insert into networks ( + name, address, cidr, comments +) values ( + ?, ?, ?, ? +) +returning id, name, address, cidr, comments +` + +type CreateNetworkParams struct { + Name string `json:"name"` + Address string `json:"address"` + Cidr int64 `json:"cidr"` + Comments string `json:"comments"` +} + +func (q *Queries) CreateNetwork(ctx context.Context, arg CreateNetworkParams) (Network, error) { + row := q.db.QueryRowContext(ctx, createNetwork, + arg.Name, + arg.Address, + arg.Cidr, + arg.Comments, + ) + var i Network + err := row.Scan( + &i.ID, + &i.Name, + &i.Address, + &i.Cidr, + &i.Comments, + ) + return i, err +} + +const deleteHost = `-- name: DeleteHost :exec +delete from hosts +where address = ? +` + +func (q *Queries) DeleteHost(ctx context.Context, address string) error { + _, err := q.db.ExecContext(ctx, deleteHost, address) + return err +} + +const deleteHostPort = `-- name: DeleteHostPort :exec +delete from host_ports +where + address = ? + and port = ? + and protocol = ? +` + +type DeleteHostPortParams struct { + Address string `json:"address"` + Port int64 `json:"port"` + Protocol string `json:"protocol"` +} + +func (q *Queries) DeleteHostPort(ctx context.Context, arg DeleteHostPortParams) error { + _, err := q.db.ExecContext(ctx, deleteHostPort, arg.Address, arg.Port, arg.Protocol) + return err +} + +const deleteNetwork = `-- name: DeleteNetwork :exec +delete from networks where id = ? +` + +func (q *Queries) DeleteNetwork(ctx context.Context, id int64) error { + _, err := q.db.ExecContext(ctx, deleteNetwork, id) + return err +} + +const getHost = `-- name: GetHost :one +select address, network_id, comments from hosts +where address = ? limit 1 +` + +func (q *Queries) GetHost(ctx context.Context, address string) (Host, error) { + row := q.db.QueryRowContext(ctx, getHost, address) + var i Host + err := row.Scan(&i.Address, &i.NetworkID, &i.Comments) + return i, err +} + +const getHostPort = `-- name: GetHostPort :one +select address, port, protocol, comments from host_ports +where address = ? + and port = ? + and protocol = ? +` + +type GetHostPortParams struct { + Address string `json:"address"` + Port int64 `json:"port"` + Protocol string `json:"protocol"` +} + +func (q *Queries) GetHostPort(ctx context.Context, arg GetHostPortParams) (HostPort, error) { + row := q.db.QueryRowContext(ctx, getHostPort, arg.Address, arg.Port, arg.Protocol) + var i HostPort + err := row.Scan( + &i.Address, + &i.Port, + &i.Protocol, + &i.Comments, + ) + return i, err +} + +const getHostWithNetwork = `-- name: GetHostWithNetwork :one +select address, network_id, comments from hosts +where address = ? and network_id = ? limit 1 +` + +type GetHostWithNetworkParams struct { + Address string `json:"address"` + NetworkID int64 `json:"network_id"` +} + +func (q *Queries) GetHostWithNetwork(ctx context.Context, arg GetHostWithNetworkParams) (Host, error) { + row := q.db.QueryRowContext(ctx, getHostWithNetwork, arg.Address, arg.NetworkID) + var i Host + err := row.Scan(&i.Address, &i.NetworkID, &i.Comments) + return i, err +} + +const getHostWithPortsByAddress = `-- name: GetHostWithPortsByAddress :many +select + net.name as network_name, + net.address as network_address, + net.cidr as network_cidr_size, + h.comments as host_comments, + h.address as host_address, + hp.port as port_number, + hp.protocol as port_protocol, + hp.comments as port_comments +from hosts h +left join host_ports hp +on hp.address = h.address +left join networks net +on net.id = h.network_id +where h.address = ? +` + +type GetHostWithPortsByAddressRow struct { + NetworkName sql.NullString `json:"network_name"` + NetworkAddress sql.NullString `json:"network_address"` + NetworkCidrSize sql.NullInt64 `json:"network_cidr_size"` + HostComments string `json:"host_comments"` + HostAddress string `json:"host_address"` + PortNumber sql.NullInt64 `json:"port_number"` + PortProtocol sql.NullString `json:"port_protocol"` + PortComments sql.NullString `json:"port_comments"` +} + +func (q *Queries) GetHostWithPortsByAddress(ctx context.Context, address string) ([]GetHostWithPortsByAddressRow, error) { + rows, err := q.db.QueryContext(ctx, getHostWithPortsByAddress, address) + if err != nil { + return nil, err + } + defer rows.Close() + var items []GetHostWithPortsByAddressRow + for rows.Next() { + var i GetHostWithPortsByAddressRow + if err := rows.Scan( + &i.NetworkName, + &i.NetworkAddress, + &i.NetworkCidrSize, + &i.HostComments, + &i.HostAddress, + &i.PortNumber, + &i.PortProtocol, + &i.PortComments, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const getNetworkByAddress = `-- name: GetNetworkByAddress :one +select id, name, address, cidr, comments from networks +where address = ? limit 1 +` + +func (q *Queries) GetNetworkByAddress(ctx context.Context, address string) (Network, error) { + row := q.db.QueryRowContext(ctx, getNetworkByAddress, address) + var i Network + err := row.Scan( + &i.ID, + &i.Name, + &i.Address, + &i.Cidr, + &i.Comments, + ) + return i, err +} + +const getNetworkById = `-- name: GetNetworkById :one +select id, name, address, cidr, comments from networks +where id = ? limit 1 +` + +func (q *Queries) GetNetworkById(ctx context.Context, id int64) (Network, error) { + row := q.db.QueryRowContext(ctx, getNetworkById, id) + var i Network + err := row.Scan( + &i.ID, + &i.Name, + &i.Address, + &i.Cidr, + &i.Comments, + ) + return i, err +} + +const listHostPorts = `-- name: ListHostPorts :many +select address, port, protocol, comments from host_ports +` + +func (q *Queries) ListHostPorts(ctx context.Context) ([]HostPort, error) { + rows, err := q.db.QueryContext(ctx, listHostPorts) + if err != nil { + return nil, err + } + defer rows.Close() + var items []HostPort + for rows.Next() { + var i HostPort + if err := rows.Scan( + &i.Address, + &i.Port, + &i.Protocol, + &i.Comments, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listHostPortsByHostAddress = `-- name: ListHostPortsByHostAddress :many +select address, port, protocol, comments from host_ports +where address = ? +` + +func (q *Queries) ListHostPortsByHostAddress(ctx context.Context, address string) ([]HostPort, error) { + rows, err := q.db.QueryContext(ctx, listHostPortsByHostAddress, address) + if err != nil { + return nil, err + } + defer rows.Close() + var items []HostPort + for rows.Next() { + var i HostPort + if err := rows.Scan( + &i.Address, + &i.Port, + &i.Protocol, + &i.Comments, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listHosts = `-- name: ListHosts :many +select address, network_id, comments from hosts +` + +func (q *Queries) ListHosts(ctx context.Context) ([]Host, error) { + rows, err := q.db.QueryContext(ctx, listHosts) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Host + for rows.Next() { + var i Host + if err := rows.Scan(&i.Address, &i.NetworkID, &i.Comments); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listNetworks = `-- name: ListNetworks :many +select id, name, address, cidr, comments from networks +` + +func (q *Queries) ListNetworks(ctx context.Context) ([]Network, error) { + rows, err := q.db.QueryContext(ctx, listNetworks) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Network + for rows.Next() { + var i Network + if err := rows.Scan( + &i.ID, + &i.Name, + &i.Address, + &i.Cidr, + &i.Comments, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const updateHost = `-- name: UpdateHost :one +update hosts +set + comments = ? +where address = ? +returning address, network_id, comments +` + +type UpdateHostParams struct { + Comments string `json:"comments"` + Address string `json:"address"` +} + +func (q *Queries) UpdateHost(ctx context.Context, arg UpdateHostParams) (Host, error) { + row := q.db.QueryRowContext(ctx, updateHost, arg.Comments, arg.Address) + var i Host + err := row.Scan(&i.Address, &i.NetworkID, &i.Comments) + return i, err +} + +const updateHostPort = `-- name: UpdateHostPort :one +update host_ports +set + comments = ? +where + address = ? + and port = ? + and protocol = ? +returning address, port, protocol, comments +` + +type UpdateHostPortParams struct { + Comments string `json:"comments"` + Address string `json:"address"` + Port int64 `json:"port"` + Protocol string `json:"protocol"` +} + +func (q *Queries) UpdateHostPort(ctx context.Context, arg UpdateHostPortParams) (HostPort, error) { + row := q.db.QueryRowContext(ctx, updateHostPort, + arg.Comments, + arg.Address, + arg.Port, + arg.Protocol, + ) + var i HostPort + err := row.Scan( + &i.Address, + &i.Port, + &i.Protocol, + &i.Comments, + ) + return i, err +} + +const updateNetwork = `-- name: UpdateNetwork :one +update networks +set + name = ?, + comments = ? +where id = ? +returning id, name, address, cidr, comments +` + +type UpdateNetworkParams struct { + Name string `json:"name"` + Comments string `json:"comments"` + ID int64 `json:"id"` +} + +func (q *Queries) UpdateNetwork(ctx context.Context, arg UpdateNetworkParams) (Network, error) { + row := q.db.QueryRowContext(ctx, updateNetwork, arg.Name, arg.Comments, arg.ID) + var i Network + err := row.Scan( + &i.ID, + &i.Name, + &i.Address, + &i.Cidr, + &i.Comments, + ) + return i, err +} diff --git a/moneyman/database/queries/pg/accounts.sql b/moneyman/database/queries/pg/accounts.sql new file mode 100644 index 0000000..e69de29 diff --git a/moneyman/database/schema.sql b/moneyman/database/schema.sql new file mode 100644 index 0000000..4c5b8f5 --- /dev/null +++ b/moneyman/database/schema.sql @@ -0,0 +1,85 @@ +pragma foreign_keys = on; + +create table if not exists networks ( + id integer primary key, + name text not null, + address text not null, + cidr integer not null, + comments text not null, + + unique (address, cidr) +); +create index if not exists idx_networks_address on networks(address); +create index if not exists idx_networks_address_cidr on networks(address, cidr); + +create table if not exists network_attributes ( + network_id integer not null, + key text not null, + value text not null, + + primary key (network_id, key), + foreign key (network_id) references networks(id) on delete cascade on update cascade +); + +create table if not exists hosts ( + address text primary key, + network_id integer not null, + comments text not null, + + foreign key (network_id) references networks(id) on delete cascade on update cascade +); +create index if not exists idx_hosts_address on hosts(address); + +create table if not exists host_attributes ( + address text not null, + key text not null, + value text not null, + + primary key (address, key), + foreign key (address) references hosts(address) on delete cascade on update cascade +); + +create table if not exists host_ports ( + address text not null, + port integer not null, + protocol text not null, + comments text not null, + + primary key (address, port, protocol), + foreign key (address) references hosts(address) on delete cascade on update cascade +); +create index if not exists idx_host_ports_address on host_ports(address); +create index if not exists idx_host_ports_address_port on host_ports(address, port); +create index if not exists idx_host_ports_address_port_proto on host_ports(address, port, protocol); + +create table if not exists host_port_attributes ( + address text not null, + port integer not null, + protocol text not null, + key text not null, + value text not null, + + primary key (address, port, protocol, key), + foreign key (address, port, protocol) references host_ports(address, port, protocol) on delete cascade on update cascade +); + +create table if not exists network_scans ( + id integer primary key, + network_id integer not null, + started_at integer not null default -1, + finished_at integer not null default -1, + hosts_found integer not null default 0, + ports_found integer not null default 0, + + foreign key (network_id) references networks(id) on delete cascade on update cascade +); +create index if not exists idx_network_scans_network_id on network_scans(network_id); + +create trigger if not exists trg_network_scans_insert +after insert on network_scans +for each row +begin + update network_scans + set started_at = strftime('%s', 'now') + where id = new.id; +end; \ No newline at end of file diff --git a/moneyman/database/wrapper.go b/moneyman/database/wrapper.go new file mode 100644 index 0000000..0106813 --- /dev/null +++ b/moneyman/database/wrapper.go @@ -0,0 +1,58 @@ +package database + +import ( + "context" + "database/sql" + _ "embed" + "fmt" + "io" + + _ "github.com/mattn/go-sqlite3" + "github.com/sirupsen/logrus" +) + +//go:embed schema.sql +var dbSchema string + +var _ io.Closer = (*DbWrapper)(nil) + +type DbWrapper struct { + *Queries + + db *sql.DB +} + +func Wrap(db *sql.DB) *DbWrapper { + return &DbWrapper{ + Queries: New(db), + db: db, + } +} + +func (w *DbWrapper) Close() error { + return w.db.Close() +} + +func (w *DbWrapper) Querier() *Queries { + return w.Queries +} + +func Open(ctx context.Context, path string) (*DbWrapper, error) { + if path == "" { + return nil, fmt.Errorf("database path is empty") + } + + logrus.Infof("Connecting to sqlite3 database at %s", path) + db, err := sql.Open("sqlite3", path) + if err != nil { + return nil, fmt.Errorf("could not open database: %w", err) + } + + logrus.Infof("Applying database schema") + logrus.Debugf("Database schema being applied: %s", dbSchema) + if _, err := db.ExecContext(ctx, dbSchema); err != nil { + return nil, fmt.Errorf("could not run schema command: %w", err) + } + + return Wrap(db), nil +} diff --git a/moneyman/go.mod b/moneyman/go.mod new file mode 100644 index 0000000..ce6480d --- /dev/null +++ b/moneyman/go.mod @@ -0,0 +1,52 @@ +module github.com/pirogoeth/apps/moneyman + +go 1.24.1 + +replace github.com/pirogoeth/apps => ../ + +require ( + github.com/gin-gonic/gin v1.10.1 + github.com/mark3labs/mcp-go v0.29.0 + github.com/mattn/go-sqlite3 v1.14.28 + github.com/sirupsen/logrus v1.9.3 + github.com/spf13/cobra v1.9.1 +) + +require ( + github.com/bytedance/sonic v1.13.2 // indirect + github.com/bytedance/sonic/loader v0.2.4 // indirect + github.com/cloudwego/base64x v0.1.5 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/gabriel-vasile/mimetype v1.4.9 // indirect + github.com/gin-contrib/sse v1.1.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.26.0 // indirect + github.com/goccy/go-json v0.10.5 // indirect + github.com/google/go-cmp v0.7.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.2.10 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect + github.com/spf13/cast v1.7.1 // indirect + github.com/spf13/pflag v1.0.6 // indirect + github.com/stretchr/testify v1.11.1 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.12 // indirect + github.com/yosida95/uritemplate/v3 v3.0.2 // indirect + golang.org/x/arch v0.17.0 // indirect + golang.org/x/crypto v0.41.0 // indirect + golang.org/x/net v0.43.0 // indirect + golang.org/x/sys v0.35.0 // indirect + golang.org/x/text v0.28.0 // indirect + google.golang.org/protobuf v1.36.8 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/moneyman/go.sum b/moneyman/go.sum new file mode 100644 index 0000000..5cc5874 --- /dev/null +++ b/moneyman/go.sum @@ -0,0 +1,45 @@ +github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ= +github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY= +github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY= +github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= +github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k= +github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/mark3labs/mcp-go v0.29.0 h1:sH1NBcumKskhxqYzhXfGc201D7P76TVXiT0fGVhabeI= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= +github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= +github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4= +golang.org/x/arch v0.17.0 h1:4O3dfLzd+lQewptAHqjewQZQDyEdejz3VwgeYwkZneU= +golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= +golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= +golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= +golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= +google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/functional/sqlc.yml b/moneyman/sqlc.yml similarity index 56% rename from functional/sqlc.yml rename to moneyman/sqlc.yml index f1a1ea4..e255dfc 100644 --- a/functional/sqlc.yml +++ b/moneyman/sqlc.yml @@ -1,16 +1,16 @@ version: "2" sql: - - engine: "sqlite" - queries: "database/queries" - schema: "database/migrations" + - engine: "postgresql" + queries: "database/queries/pg" + schema: "database/migrations/pg" gen: go: - package: "database" - out: "database" + package: "pg" + out: "database/pg" sql_package: "database/sql" emit_json_tags: true emit_db_tags: true emit_prepared_queries: false emit_interface: false emit_exact_table_names: false - emit_empty_slices: true \ No newline at end of file + emit_empty_slices: true diff --git a/moneyman/types/apicontext.go b/moneyman/types/apicontext.go new file mode 100644 index 0000000..3c434f3 --- /dev/null +++ b/moneyman/types/apicontext.go @@ -0,0 +1,13 @@ +package types + +import ( + "github.com/pirogoeth/apps/moneyman/database" +) + +type ApiContext struct { + // Config is the application configuration + Config *Config + + // Querier is the database interface + Querier *database.Queries +} diff --git a/moneyman/types/config.go b/moneyman/types/config.go new file mode 100644 index 0000000..d9154a3 --- /dev/null +++ b/moneyman/types/config.go @@ -0,0 +1,14 @@ +package types + +import ( + "github.com/pirogoeth/apps/pkg/config" + + "github.com/pirogoeth/apps/moneyman/database" +) + +type Config struct { + config.CommonConfig + + // Database is the configuration for the database connection + Database *database.Config `json:"database"` +} diff --git a/nomad-deployer/go.mod b/nomad-deployer/go.mod index de0a40c..061979c 100644 --- a/nomad-deployer/go.mod +++ b/nomad-deployer/go.mod @@ -1,18 +1,17 @@ module github.com/pirogoeth/apps/nomad-deployer -go 1.21.5 +go 1.24.1 replace github.com/pirogoeth/apps => ../ require ( github.com/c2h5oh/datasize v0.0.0-20231215233829-aa82cc1e6500 github.com/ghodss/yaml v1.0.0 - github.com/gin-gonic/gin v1.9.1 + github.com/gin-gonic/gin v1.10.1 github.com/google/go-github/v58 v58.0.0 github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 github.com/hashicorp/nomad v1.7.5 - github.com/hashicorp/nomad/api v0.0.0-20231227080007-76ba3e10e73d - github.com/pirogoeth/apps v0.0.0-00010101000000-000000000000 + github.com/hashicorp/nomad/api v0.0.0-20250124153748-7add04eb0f0b github.com/sirupsen/logrus v1.9.3 ) @@ -20,20 +19,20 @@ require ( github.com/agext/levenshtein v1.2.1 // indirect github.com/apparentlymart/go-cidr v1.0.1 // indirect github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect - github.com/beorn7/perks v1.0.1 // indirect github.com/bmatcuk/doublestar v1.1.5 // indirect - github.com/bytedance/sonic v1.9.1 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect - github.com/gabriel-vasile/mimetype v1.4.2 // indirect - github.com/gin-contrib/sse v0.1.0 // indirect + github.com/bytedance/sonic v1.13.2 // indirect + github.com/bytedance/sonic/loader v0.2.4 // indirect + github.com/cloudwego/base64x v0.1.5 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/gabriel-vasile/mimetype v1.4.9 // indirect + github.com/gin-contrib/sse v1.1.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.14.0 // indirect - github.com/goccy/go-json v0.10.2 // indirect - github.com/google/go-cmp v0.6.0 // indirect + github.com/go-playground/validator/v10 v10.26.0 // indirect + github.com/goccy/go-json v0.10.5 // indirect + github.com/google/go-cmp v0.7.0 // indirect github.com/google/go-querystring v1.1.0 // indirect - github.com/google/uuid v1.3.1 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/gorilla/websocket v1.5.0 // indirect github.com/hashicorp/cronexpr v1.1.2 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect @@ -43,32 +42,31 @@ require ( github.com/hashicorp/go-rootcerts v1.0.2 // indirect github.com/hashicorp/hcl/v2 v2.9.2-0.20220525143345-ab3cae0737bc // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/kelseyhightower/envconfig v1.4.0 // indirect - github.com/klauspost/cpuid/v2 v2.2.5 // indirect - github.com/leodido/go-urn v1.2.4 // indirect - github.com/mattn/go-isatty v0.0.19 // indirect + github.com/klauspost/cpuid/v2 v2.2.10 // indirect + github.com/kylelemons/godebug v1.1.0 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/pelletier/go-toml/v2 v2.0.8 // indirect - github.com/prometheus/client_golang v1.19.1 // indirect - github.com/prometheus/client_model v0.5.0 // indirect - github.com/prometheus/common v0.48.0 // indirect - github.com/prometheus/procfs v0.12.0 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect + github.com/stretchr/testify v1.11.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect - github.com/ugorji/go/codec v1.2.11 // indirect + github.com/ugorji/go/codec v1.2.12 // indirect github.com/zclconf/go-cty v1.12.1 // indirect github.com/zclconf/go-cty-yaml v1.0.3 // indirect - golang.org/x/arch v0.3.0 // indirect - golang.org/x/crypto v0.24.0 // indirect - golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect - golang.org/x/net v0.26.0 // indirect - golang.org/x/sys v0.21.0 // indirect - golang.org/x/text v0.16.0 // indirect - google.golang.org/protobuf v1.34.2 // indirect + golang.org/x/arch v0.17.0 // indirect + golang.org/x/crypto v0.41.0 // indirect + golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect + golang.org/x/net v0.43.0 // indirect + golang.org/x/sys v0.35.0 // indirect + golang.org/x/text v0.28.0 // indirect + google.golang.org/protobuf v1.36.8 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/nomad-deployer/go.sum b/nomad-deployer/go.sum index 290d4f7..dcbc871 100644 --- a/nomad-deployer/go.sum +++ b/nomad-deployer/go.sum @@ -8,63 +8,48 @@ github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/ github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec= github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw= github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= -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/bmatcuk/doublestar v1.1.5 h1:2bNwBOmhyFEFcoB3tGvTD5xanq+4kyOZlB8wFYbMjkk= github.com/bmatcuk/doublestar v1.1.5/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE= -github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= -github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= -github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ= +github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY= github.com/c2h5oh/datasize v0.0.0-20231215233829-aa82cc1e6500 h1:6lhrsTEnloDPXyeZBvSYvQf8u86jbKehZPVDDlkgDl4= github.com/c2h5oh/datasize v0.0.0-20231215233829-aa82cc1e6500/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= -github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= -github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= +github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= -github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= -github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= -github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= -github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= +github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= +github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= -github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k= github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= -github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= -github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-github/v58 v58.0.0 h1:Una7GGERlF/37XfkPwpzYJe0Vp4dt2k1kCjlxwjIvzw= github.com/google/go-github/v58 v58.0.0/go.mod h1:k4hxDKEfoWpSqFlc8LTpGd9fu2KrV1YAa6Hi6FmDNY4= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= -github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= @@ -88,15 +73,11 @@ github.com/hashicorp/hcl/v2 v2.9.2-0.20220525143345-ab3cae0737bc h1:32lGaCPq5JPY github.com/hashicorp/hcl/v2 v2.9.2-0.20220525143345-ab3cae0737bc/go.mod h1:odKNpEeZv3COD+++SQcPyACuKOlM5eBoQlzRyN5utIQ= github.com/hashicorp/nomad v1.7.5 h1:sAeSsf1/EnEXZsuhgci1DNxIVyBv1p91Czpno403dTQ= github.com/hashicorp/nomad v1.7.5/go.mod h1:PVPZnaNzzMnewe8wxWEE6SLn5GNE3XMs/ZLIdL2jBEM= -github.com/hashicorp/nomad/api v0.0.0-20231227080007-76ba3e10e73d h1:Ztvo+OG+/xc6GQFiKME65qY6nveab1c7Hy8TFdBzbUI= -github.com/hashicorp/nomad/api v0.0.0-20231227080007-76ba3e10e73d/go.mod h1:ijDwa6o1uG1jFSq6kERiX2PamKGpZzTmo0XOFNeFZgw= +github.com/hashicorp/nomad/api v0.0.0-20250124153748-7add04eb0f0b h1:oTD8vP47MZ4ZnIcxsucsYZeJEOpLaQEBq6hNAiITYN0= 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/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= -github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= -github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -104,12 +85,10 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 h1:MtvEpTB6LX3vkb4ax0b5D2DHbNAUsen0Gx5wZoq3lV4= github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= -github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= -github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= -github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 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/go-testing-interface v1.14.2-0.20210821155943-2d9075ca8770 h1:drhDO54gdT/a15GBcMRmunZiNcLgPiFIJa23KzmcvcU= @@ -126,25 +105,14 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w 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/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= -github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= -github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= -github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= -github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= -github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= -github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= -github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= -github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/shoenig/test v1.7.0 h1:eWcHtTXa6QLnBvm0jgEabMRN/uJ4DMV3M8xUGgRkZmk= -github.com/shoenig/test v1.7.0/go.mod h1:UxJ6u/x2v/TNs/LoLxBNJRV9DiwBBKYxXSyczsBHFoI= +github.com/shoenig/test v1.7.1 h1:UJcjSAI3aUKx52kfcfhblgyhZceouhvvs3OYdWgn+PY= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= @@ -157,14 +125,10 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= -github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= -github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= @@ -176,23 +140,18 @@ github.com/zclconf/go-cty v1.12.1/go.mod h1:s9IfD1LK5ccNMSWCVFCE2rJfHiZgi7JijgeW github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8= github.com/zclconf/go-cty-yaml v1.0.3 h1:og/eOQ7lvA/WWhHGFETVWNduJM7Rjsv2RRpx1sdFMLc= github.com/zclconf/go-cty-yaml v1.0.3/go.mod h1:9YLUH4g7lOhVWqUbctnVlZ5KLpg7JAprQNgxSZ1Gyxs= -golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= -golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.17.0 h1:4O3dfLzd+lQewptAHqjewQZQDyEdejz3VwgeYwkZneU= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200422194213-44a606286825/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20220517005047-85d78b3ac167/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= -golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= +golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= +golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI= golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= -golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -200,23 +159,19 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= @@ -226,4 +181,3 @@ 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= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/nomad-event-stream/go.mod b/nomad-event-stream/go.mod index bb95d82..3f867e7 100644 --- a/nomad-event-stream/go.mod +++ b/nomad-event-stream/go.mod @@ -1,38 +1,37 @@ module github.com/pirogoeth/apps/nomad-event-stream -go 1.18 +go 1.24.1 replace github.com/pirogoeth/apps => ../ require ( - github.com/hashicorp/nomad/api v0.0.0-20231227080007-76ba3e10e73d - github.com/pirogoeth/apps v0.0.0 - github.com/prometheus/client_golang v1.18.0 - github.com/redis/go-redis/v9 v9.3.1 + github.com/hashicorp/nomad/api v0.0.0-20250124153748-7add04eb0f0b + github.com/prometheus/client_golang v1.20.5 + github.com/redis/go-redis/v9 v9.7.0 ) require ( github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/ghodss/yaml v1.0.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect github.com/gorilla/websocket v1.5.0 // indirect github.com/hashicorp/cronexpr v1.1.2 // indirect - github.com/hashicorp/errwrap v1.0.0 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect - github.com/kelseyhightower/envconfig v1.4.0 // indirect - github.com/kr/text v0.2.0 // indirect - github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect + github.com/klauspost/compress v1.18.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/go-testing-interface v1.14.2-0.20210821155943-2d9075ca8770 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/prometheus/client_model v0.5.0 // indirect - github.com/prometheus/common v0.45.0 // indirect - github.com/prometheus/procfs v0.12.0 // indirect - github.com/sirupsen/logrus v1.9.3 // indirect - golang.org/x/exp v0.0.0-20230728194245-b0cb94b80691 // indirect - golang.org/x/sys v0.15.0 // indirect - google.golang.org/protobuf v1.31.0 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.55.0 // indirect + github.com/prometheus/procfs v0.16.1 // indirect + github.com/stretchr/testify v1.11.1 // indirect + golang.org/x/sys v0.35.0 // indirect + google.golang.org/protobuf v1.36.8 // indirect ) diff --git a/nomad-event-stream/go.sum b/nomad-event-stream/go.sum index 9e51660..c763abd 100644 --- a/nomad-event-stream/go.sum +++ b/nomad-event-stream/go.sum @@ -2,78 +2,41 @@ 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/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= -github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= -github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hashicorp/cronexpr v1.1.2 h1:wG/ZYIKT+RT3QkOdgYc+xsKWVRgnxJ1OJtjjy84fJ9A= github.com/hashicorp/cronexpr v1.1.2/go.mod h1:P4wA0KBl9C5q2hABiMO7cp6jcIg96CDh1Efb3g1PWA4= -github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= 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/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= 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/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= -github.com/hashicorp/nomad/api v0.0.0-20231227080007-76ba3e10e73d h1:Ztvo+OG+/xc6GQFiKME65qY6nveab1c7Hy8TFdBzbUI= -github.com/hashicorp/nomad/api v0.0.0-20231227080007-76ba3e10e73d/go.mod h1:ijDwa6o1uG1jFSq6kERiX2PamKGpZzTmo0XOFNeFZgw= -github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= -github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= -github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= +github.com/hashicorp/nomad/api v0.0.0-20250124153748-7add04eb0f0b h1:oTD8vP47MZ4ZnIcxsucsYZeJEOpLaQEBq6hNAiITYN0= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= 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/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= +github.com/mitchellh/go-testing-interface v1.14.2-0.20210821155943-2d9075ca8770 h1:drhDO54gdT/a15GBcMRmunZiNcLgPiFIJa23KzmcvcU= 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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk= -github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= -github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= -github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= -github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= -github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= -github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= -github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= -github.com/redis/go-redis/v9 v9.3.1 h1:KqdY8U+3X6z+iACvumCNxnoluToB+9Me+TvyFa21Mds= -github.com/redis/go-redis/v9 v9.3.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/shoenig/test v1.7.0 h1:eWcHtTXa6QLnBvm0jgEabMRN/uJ4DMV3M8xUGgRkZmk= -github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= -golang.org/x/exp v0.0.0-20230728194245-b0cb94b80691 h1:/yRP+0AN7mf5DkD3BAI6TOFnd51gEoDEb8o35jIFtgw= -golang.org/x/exp v0.0.0-20230728194245-b0cb94b80691/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -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 h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= +github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= +github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E= +github.com/shoenig/test v1.7.1 h1:UJcjSAI3aUKx52kfcfhblgyhZceouhvvs3OYdWgn+PY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= +google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/nomad-external-dns/go.mod b/nomad-external-dns/go.mod index 7220c11..c36110c 100644 --- a/nomad-external-dns/go.mod +++ b/nomad-external-dns/go.mod @@ -1,3 +1,56 @@ module github.com/pirogoeth/apps/nomad-external-dns -go 1.21.5 +go 1.23.0 + +toolchain go1.24.1 + +require ( + github.com/gin-gonic/gin v1.10.1 + github.com/hashicorp/nomad/api v0.0.0-20250124153748-7add04eb0f0b + github.com/sirupsen/logrus v1.9.3 +) + +require ( + github.com/bytedance/sonic v1.13.2 // indirect + github.com/bytedance/sonic/loader v0.2.4 // indirect + github.com/cloudwego/base64x v0.1.5 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/gabriel-vasile/mimetype v1.4.9 // indirect + github.com/gin-contrib/sse v1.1.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.26.0 // indirect + github.com/goccy/go-json v0.10.5 // indirect + github.com/google/go-cmp v0.7.0 // indirect + github.com/gorilla/websocket v1.5.0 // indirect + github.com/hashicorp/cronexpr v1.1.2 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/go-rootcerts v1.0.2 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.2.10 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/go-testing-interface v1.14.2-0.20210821155943-2d9075ca8770 // 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/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect + github.com/stretchr/testify v1.11.1 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.12 // indirect + golang.org/x/arch v0.17.0 // indirect + golang.org/x/crypto v0.41.0 // indirect + golang.org/x/net v0.43.0 // indirect + golang.org/x/sys v0.35.0 // indirect + golang.org/x/text v0.28.0 // indirect + google.golang.org/protobuf v1.36.8 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/nomad-external-dns/go.sum b/nomad-external-dns/go.sum new file mode 100644 index 0000000..f4a4d9a --- /dev/null +++ b/nomad-external-dns/go.sum @@ -0,0 +1,49 @@ +github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ= +github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY= +github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY= +github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= +github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k= +github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/hashicorp/cronexpr v1.1.2 h1:wG/ZYIKT+RT3QkOdgYc+xsKWVRgnxJ1OJtjjy84fJ9A= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= +github.com/hashicorp/nomad/api v0.0.0-20250124153748-7add04eb0f0b h1:oTD8vP47MZ4ZnIcxsucsYZeJEOpLaQEBq6hNAiITYN0= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-testing-interface v1.14.2-0.20210821155943-2d9075ca8770 h1:drhDO54gdT/a15GBcMRmunZiNcLgPiFIJa23KzmcvcU= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/shoenig/test v1.7.1 h1:UJcjSAI3aUKx52kfcfhblgyhZceouhvvs3OYdWgn+PY= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= +golang.org/x/arch v0.17.0 h1:4O3dfLzd+lQewptAHqjewQZQDyEdejz3VwgeYwkZneU= +golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= +golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= +golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= +golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= +google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/nomad-service-cleaner/go.mod b/nomad-service-cleaner/go.mod index 8e6369d..f7b080f 100644 --- a/nomad-service-cleaner/go.mod +++ b/nomad-service-cleaner/go.mod @@ -1,61 +1,72 @@ module github.com/pirogoeth/apps/nomad-service-cleaner -go 1.21.5 +go 1.23.0 + +toolchain go1.24.1 require ( github.com/go-co-op/gocron/v2 v2.1.2 - github.com/hashicorp/nomad/api v0.0.0-20240503203333-890c2ce7136b + github.com/hashicorp/nomad/api v0.0.0-20250124153748-7add04eb0f0b github.com/pirogoeth/apps v0.0.0-20240325221305-9cc0ad456250 - github.com/prometheus/client_golang v1.19.0 + github.com/prometheus/client_golang v1.20.5 github.com/sirupsen/logrus v1.9.3 ) require ( github.com/beorn7/perks v1.0.1 // indirect - github.com/bytedance/sonic v1.9.1 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect - github.com/gabriel-vasile/mimetype v1.4.2 // indirect + github.com/bytedance/sonic v1.13.2 // indirect + github.com/bytedance/sonic/loader v0.2.4 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/cloudwego/base64x v0.1.5 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/gabriel-vasile/mimetype v1.4.9 // indirect github.com/ghodss/yaml v1.0.0 // indirect - github.com/gin-contrib/sse v0.1.0 // indirect - github.com/gin-gonic/gin v1.9.1 // indirect + github.com/gin-contrib/sse v1.1.0 // indirect + github.com/gin-gonic/gin v1.10.1 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.14.0 // indirect - github.com/goccy/go-json v0.10.2 // indirect + github.com/go-playground/validator/v10 v10.26.0 // indirect + github.com/goccy/go-json v0.10.5 // indirect github.com/google/go-github/v58 v58.0.0 // indirect github.com/google/go-querystring v1.1.0 // indirect - github.com/google/uuid v1.5.0 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/gorilla/websocket v1.5.0 // indirect github.com/hashicorp/cronexpr v1.1.2 // indirect - github.com/hashicorp/errwrap v1.0.0 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect - github.com/jonboulle/clockwork v0.4.0 // indirect + github.com/jonboulle/clockwork v0.5.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/kelseyhightower/envconfig v1.4.0 // indirect - github.com/klauspost/cpuid/v2 v2.2.4 // indirect - github.com/leodido/go-urn v1.2.4 // indirect - github.com/mattn/go-isatty v0.0.19 // indirect + github.com/klauspost/compress v1.18.0 // indirect + github.com/klauspost/cpuid/v2 v2.2.10 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/go-testing-interface v1.14.2-0.20210821155943-2d9075ca8770 // 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/pelletier/go-toml/v2 v2.0.8 // indirect - github.com/prometheus/client_model v0.5.0 // indirect - github.com/prometheus/common v0.48.0 // indirect - github.com/prometheus/procfs v0.12.0 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.55.0 // indirect + github.com/prometheus/procfs v0.16.1 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect + github.com/stretchr/testify v1.11.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect - github.com/ugorji/go/codec v1.2.11 // indirect - golang.org/x/arch v0.3.0 // indirect - golang.org/x/crypto v0.24.0 // indirect - golang.org/x/exp v0.0.0-20231219180239-dc181d75b848 // indirect - golang.org/x/net v0.26.0 // indirect - golang.org/x/sys v0.21.0 // indirect - golang.org/x/text v0.16.0 // indirect - google.golang.org/protobuf v1.34.2 // indirect + github.com/ugorji/go/codec v1.2.12 // indirect + golang.org/x/arch v0.17.0 // indirect + golang.org/x/crypto v0.41.0 // indirect + golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect + golang.org/x/net v0.43.0 // indirect + golang.org/x/sys v0.35.0 // indirect + golang.org/x/text v0.28.0 // indirect + google.golang.org/protobuf v1.36.8 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/nomad-service-cleaner/go.sum b/nomad-service-cleaner/go.sum index 224f7c7..2179c64 100644 --- a/nomad-service-cleaner/go.sum +++ b/nomad-service-cleaner/go.sum @@ -1,28 +1,20 @@ 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/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= -github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= -github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= -github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= -github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= +github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ= +github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= 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/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= -github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= -github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= -github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= -github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= +github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= +github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ= github.com/go-co-op/gocron/v2 v2.1.2 h1:+6tTOA9aBaKXpDWExw07hYoGEBzT+4CkGSVAiJ7WSXs= github.com/go-co-op/gocron/v2 v2.1.2/go.mod h1:0MfNAXEchzeSH1vtkZrTAcSMWqyL435kL6CA4b0bjrg= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= @@ -31,55 +23,46 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= -github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= -github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= -github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k= +github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-github/v58 v58.0.0 h1:Una7GGERlF/37XfkPwpzYJe0Vp4dt2k1kCjlxwjIvzw= github.com/google/go-github/v58 v58.0.0/go.mod h1:k4hxDKEfoWpSqFlc8LTpGd9fu2KrV1YAa6Hi6FmDNY4= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= -github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hashicorp/cronexpr v1.1.2 h1:wG/ZYIKT+RT3QkOdgYc+xsKWVRgnxJ1OJtjjy84fJ9A= github.com/hashicorp/cronexpr v1.1.2/go.mod h1:P4wA0KBl9C5q2hABiMO7cp6jcIg96CDh1Efb3g1PWA4= -github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= 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/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= 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/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= -github.com/hashicorp/nomad/api v0.0.0-20240503203333-890c2ce7136b h1:lgzTuX997YuTielAgZX/vSBJTMRcUX+t88/WbOWznoU= -github.com/hashicorp/nomad/api v0.0.0-20240503203333-890c2ce7136b/go.mod h1:svtxn6QnrQ69P23VvIWMR34tg3vmwLz4UdUzm1dSCgE= -github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4= -github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc= +github.com/hashicorp/nomad/api v0.0.0-20250124153748-7add04eb0f0b h1:oTD8vP47MZ4ZnIcxsucsYZeJEOpLaQEBq6hNAiITYN0= +github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I= 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/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= -github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= -github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= -github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 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/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= -github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= +github.com/mitchellh/go-testing-interface v1.14.2-0.20210821155943-2d9075ca8770 h1:drhDO54gdT/a15GBcMRmunZiNcLgPiFIJa23KzmcvcU= 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/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -87,24 +70,19 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w 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/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= -github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pirogoeth/apps v0.0.0-20240325221305-9cc0ad456250 h1:TuTm67eBb/Ituot+hJaeL1uFl2PZJoAMppl7mw77Hm4= github.com/pirogoeth/apps v0.0.0-20240325221305-9cc0ad456250/go.mod h1:AMyIuUxSCwidmT+VHk40kalE85U2o8o4xn6TUOjEDQI= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= -github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= -github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= -github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= -github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= -github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= -github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= -github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= +github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/shoenig/test v1.7.1 h1:UJcjSAI3aUKx52kfcfhblgyhZceouhvvs3OYdWgn+PY= github.com/shoenig/test v1.7.1/go.mod h1:UxJ6u/x2v/TNs/LoLxBNJRV9DiwBBKYxXSyczsBHFoI= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= @@ -117,35 +95,22 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= -github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= -github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= -golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= -golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= -golang.org/x/exp v0.0.0-20231219180239-dc181d75b848 h1:+iq7lrkxmFNBM7xx+Rae2W6uyPfhPeDWD+n+JgppptE= -golang.org/x/exp v0.0.0-20231219180239-dc181d75b848/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= -golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= -golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= -golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/arch v0.17.0 h1:4O3dfLzd+lQewptAHqjewQZQDyEdejz3VwgeYwkZneU= +golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= +golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI= +golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= +golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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= @@ -154,4 +119,3 @@ 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= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/orba/go.mod b/orba/go.mod index ffad229..a71a71c 100644 --- a/orba/go.mod +++ b/orba/go.mod @@ -5,25 +5,24 @@ go 1.24.1 replace github.com/pirogoeth/apps => ../ require ( + github.com/asg017/sqlite-vec-go-bindings v0.1.6 github.com/gin-gonic/gin v1.10.1 github.com/mark3labs/mcp-go v0.29.0 github.com/mattn/go-sqlite3 v1.14.28 - github.com/pirogoeth/apps v0.0.0-20241117082003-8acafa433497 + github.com/openai/openai-go v1.8.2 github.com/pressly/goose/v3 v3.24.3 github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.9.1 + go.opentelemetry.io/otel v1.38.0 + go.opentelemetry.io/otel/trace v1.38.0 ) require ( - github.com/asg017/sqlite-vec-go-bindings v0.1.6 // indirect - github.com/beorn7/perks v1.0.1 // indirect github.com/bytedance/sonic v1.13.2 // indirect github.com/bytedance/sonic/loader v0.2.4 // indirect - github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cloudwego/base64x v0.1.5 // indirect - github.com/cloudwego/iasm v0.2.0 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/gabriel-vasile/mimetype v1.4.9 // indirect - github.com/ghodss/yaml v1.0.0 // indirect github.com/gin-contrib/sse v1.1.0 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect @@ -31,37 +30,21 @@ require ( github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.26.0 // indirect github.com/goccy/go-json v0.10.5 // indirect - github.com/google/go-github/v58 v58.0.0 // indirect - github.com/google/go-querystring v1.1.0 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/gorilla/websocket v1.5.0 // indirect - github.com/hashicorp/cronexpr v1.1.2 // indirect - github.com/hashicorp/errwrap v1.1.0 // indirect - github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/hashicorp/go-rootcerts v1.0.2 // indirect - github.com/hashicorp/nomad/api v0.0.0-20231227080007-76ba3e10e73d // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/kelseyhightower/envconfig v1.4.0 // indirect github.com/klauspost/cpuid/v2 v2.2.10 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mfridman/interpolate v0.0.2 // indirect - github.com/mitchellh/go-homedir v1.1.0 // 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/openai/openai-go v1.8.2 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect - github.com/prometheus/client_golang v1.19.1 // indirect - github.com/prometheus/client_model v0.5.0 // indirect - github.com/prometheus/common v0.48.0 // indirect - github.com/prometheus/procfs v0.16.1 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/sethvargo/go-retry v0.3.0 // indirect github.com/spf13/cast v1.7.1 // indirect github.com/spf13/pflag v1.0.6 // indirect - github.com/tidwall/gjson v1.14.4 // indirect + github.com/tidwall/gjson v1.17.1 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect github.com/tidwall/sjson v1.2.5 // indirect @@ -69,21 +52,14 @@ require ( github.com/ugorji/go/codec v1.2.12 // indirect github.com/yosida95/uritemplate/v3 v3.0.2 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.opentelemetry.io/otel v1.36.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 // indirect - go.opentelemetry.io/otel/metric v1.36.0 // indirect - go.opentelemetry.io/otel/sdk v1.36.0 // indirect - go.opentelemetry.io/otel/trace v1.36.0 // indirect - go.opentelemetry.io/proto/otlp v1.6.0 // indirect + go.opentelemetry.io/otel/metric v1.38.0 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/arch v0.17.0 // indirect - golang.org/x/crypto v0.38.0 // indirect - golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect - golang.org/x/net v0.40.0 // indirect - golang.org/x/sync v0.14.0 // indirect - golang.org/x/sys v0.33.0 // indirect - golang.org/x/text v0.25.0 // indirect - google.golang.org/protobuf v1.36.6 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect + golang.org/x/crypto v0.41.0 // indirect + golang.org/x/net v0.43.0 // indirect + golang.org/x/sync v0.16.0 // indirect + golang.org/x/sys v0.35.0 // indirect + golang.org/x/text v0.28.0 // indirect + google.golang.org/protobuf v1.36.8 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/orba/go.sum b/orba/go.sum index 6b8074b..7ab78c6 100644 --- a/orba/go.sum +++ b/orba/go.sum @@ -1,48 +1,28 @@ github.com/asg017/sqlite-vec-go-bindings v0.1.6 h1:Nx0jAzyS38XpkKznJ9xQjFXz2X9tI7KqjwVxV8RNoww= github.com/asg017/sqlite-vec-go-bindings v0.1.6/go.mod h1:A8+cTt/nKFsYCQF6OgzSNpKZrzNo5gQsXBTfsXHXY0Q= -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/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= -github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= +github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ= github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4= -github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY= github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= -github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= +github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4= github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= -github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/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/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= -github.com/felixge/httpsnoop v1.0.3/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/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= -github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= +github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY= github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok= -github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= -github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ= github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -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-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= @@ -53,47 +33,21 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= -github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k= github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= -github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= -github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= 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/go-github/v58 v58.0.0 h1:Una7GGERlF/37XfkPwpzYJe0Vp4dt2k1kCjlxwjIvzw= -github.com/google/go-github/v58 v58.0.0/go.mod h1:k4hxDKEfoWpSqFlc8LTpGd9fu2KrV1YAa6Hi6FmDNY4= -github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= -github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 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/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/hashicorp/cronexpr v1.1.2 h1:wG/ZYIKT+RT3QkOdgYc+xsKWVRgnxJ1OJtjjy84fJ9A= -github.com/hashicorp/cronexpr v1.1.2/go.mod h1:P4wA0KBl9C5q2hABiMO7cp6jcIg96CDh1Efb3g1PWA4= -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-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= -github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -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/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= -github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= -github.com/hashicorp/nomad/api v0.0.0-20231227080007-76ba3e10e73d h1:Ztvo+OG+/xc6GQFiKME65qY6nveab1c7Hy8TFdBzbUI= -github.com/hashicorp/nomad/api v0.0.0-20231227080007-76ba3e10e73d/go.mod h1:ijDwa6o1uG1jFSq6kERiX2PamKGpZzTmo0XOFNeFZgw= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 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/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= -github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= -github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -110,12 +64,6 @@ github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEu github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6BbAxPY= github.com/mfridman/interpolate v0.0.2/go.mod h1:p+7uk6oE07mpE/Ik1b8EckO0O4ZXiGAfshKBWLUM9Xg= -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/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= -github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= -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/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= @@ -125,26 +73,12 @@ github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdh github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/openai/openai-go v1.8.2 h1:UqSkJ1vCOPUpz9Ka5tS0324EJFEuOvMc+lA/EarJWP8= github.com/openai/openai-go v1.8.2/go.mod h1:g461MYGXEXBVdV5SaR/5tNzNbSfwTBBefwc+LlDCK0Y= -github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= -github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= -github.com/pirogoeth/apps v0.0.0-20241117082003-8acafa433497 h1:LlWMvGPg1hmVpqEuXNrUIKAh8YBat2YIZFqr4bpJPg8= -github.com/pirogoeth/apps v0.0.0-20241117082003-8acafa433497/go.mod h1:CqG75HVoLrFuRaR/xZdYWhW4Qon94klkgd68nq/eW/0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pressly/goose/v3 v3.6.1 h1:DB7/eKhn98vWOz90OSXqMf4OwuKCdQ6GbvxhtjO4Uak= -github.com/pressly/goose/v3 v3.6.1/go.mod h1:fpaav/TpxygOn1+OAdzwswN2NbvadBOktQpiDOxewvY= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pressly/goose/v3 v3.24.3 h1:DSWWNwwggVUsYZ0X2VitiAa9sKuqtBfe+Jr9zFGwWlM= github.com/pressly/goose/v3 v3.24.3/go.mod h1:v9zYL4xdViLHCUUJh/mhjnm6JrK7Eul8AS93IxiZM4E= -github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= -github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= -github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= -github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= -github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= -github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= -github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= -github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= -github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= @@ -152,8 +86,6 @@ github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sethvargo/go-retry v0.3.0 h1:EEt31A35QhrcRZtrYFDTBg91cqZVnFL2navjDrah2SE= github.com/sethvargo/go-retry v0.3.0/go.mod h1:mNX17F0C/HguQMyMyJxcnU471gOZGxCLyYaFyAZraas= -github.com/shoenig/test v1.7.0 h1:eWcHtTXa6QLnBvm0jgEabMRN/uJ4DMV3M8xUGgRkZmk= -github.com/shoenig/test v1.7.0/go.mod h1:UxJ6u/x2v/TNs/LoLxBNJRV9DiwBBKYxXSyczsBHFoI= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= @@ -165,19 +97,14 @@ github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -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/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= -github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM= -github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= @@ -193,67 +120,35 @@ github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zI github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/otel v1.7.0 h1:Z2lA3Tdch0iDcrhJXDIlC94XE+bxok1F9B+4Lz/lGsM= -go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk= -go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= -go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg= -go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 h1:dNzwXjZKpMpE2JhmO+9HsPl42NIXFIFSUSSs0fiqra0= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0/go.mod h1:90PoxvaEB5n6AOdZvi+yWJQoE95U8Dhhw2bSyRqnTD0= -go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE= -go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs= -go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs= -go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY= -go.opentelemetry.io/otel/trace v1.7.0 h1:O37Iogk1lEkMRXewVtZ1BBTVn5JEp8GrJvP92bJqC6o= -go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48yyE4TNvoHqU= -go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= -go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w= -go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA= -go.opentelemetry.io/proto/otlp v1.6.0 h1:jQjP+AQyTf+Fe7OKj/MfkDrmK4MNVtw2NpXsf9fefDI= -go.opentelemetry.io/proto/otlp v1.6.0/go.mod h1:cicgGehlFuNdgZkcALOCh3VE6K/u2tAjzlRhDwmVpZc= +go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= +go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= +go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= -golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= +golang.org/x/arch v0.17.0 h1:4O3dfLzd+lQewptAHqjewQZQDyEdejz3VwgeYwkZneU= golang.org/x/arch v0.17.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk= -golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= -golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= +golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI= golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ= -golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= -golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= -golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= -golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= +golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= -google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= +golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= +google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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/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= -modernc.org/libc v1.16.7 h1:qzQtHhsZNpVPpeCu+aMIQldXeV1P0vRhSqCL0nOIJOA= modernc.org/libc v1.65.0 h1:e183gLDnAp9VJh6gWKdTy0CThL9Pt7MfcR/0bgb7Y1Y= modernc.org/libc v1.65.0/go.mod h1:7m9VzGq7APssBTydds2zBcxGREwvIGpuUBaKTXdm2Qs= -modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8= modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= -modernc.org/memory v1.1.1 h1:bDOL0DIDLQv7bWhP3gMvIrnoFw+Eo6F7a2QK9HPDiFU= modernc.org/memory v1.10.0 h1:fzumd51yQ1DxcOxSO+S6X7+QTuVU+n8/Aj7swYjFfC4= modernc.org/memory v1.10.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw= -modernc.org/sqlite v1.17.3 h1:iE+coC5g17LtByDYDWKpR6m2Z9022YrSh3bumwOnIrI= modernc.org/sqlite v1.37.0 h1:s1TMe7T3Q3ovQiK2Ouz4Jwh7dw4ZDqbebSDTlSJdfjI= modernc.org/sqlite v1.37.0/go.mod h1:5YiWv+YviqGMuGw4V+PNplcyaJ5v+vQd7TQOgkACoJM= nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/pkg/config/config.go b/pkg/config/config.go index e7ef357..084cc5e 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -25,8 +25,8 @@ const ( CFG_ENV = "env" ) -// Load loads the configuration from Consul. Expects the Consul -// configuration to be pulled from the environment. +// Load loads the configuration from the specified config +// backend as specified in the environment (CONFIG_TYPE). func Load[T any]() (*T, error) { configType := os.Getenv(ENV_CFG_TYPE) if configType == "" { @@ -45,6 +45,19 @@ func Load[T any]() (*T, error) { } } +// LoadWithDefaults executes Load() but also calls ApplyDefaults +// on the resulting loaded config to apply defaults from struct +// tags instead of zero-values. +func LoadWithDefaults[T any]() (*T, error) { + cfg, err := Load[T]() + if err != nil { + return nil, fmt.Errorf("error loading config with defaults: %w", err) + } + + ApplyDefaults(cfg) + return cfg, nil +} + // loadConfigFromNomad loads an app's config from a Nomad parameter inside the current namespace func loadConfigFromNomad[T any]() (*T, error) { nomadCfg := nomadApi.DefaultConfig() diff --git a/pkg/go.mod b/pkg/go.mod index c88dd32..f578419 100644 --- a/pkg/go.mod +++ b/pkg/go.mod @@ -5,12 +5,19 @@ go 1.23.2 require ( github.com/blevesearch/bleve v1.0.14 github.com/ghodss/yaml v1.0.0 - github.com/gin-gonic/gin v1.10.0 + github.com/gin-gonic/gin v1.10.1 github.com/google/go-github/v58 v58.0.0 github.com/hashicorp/nomad/api v0.0.0-20250124153748-7add04eb0f0b github.com/kelseyhightower/envconfig v1.4.0 github.com/prometheus/client_golang v1.20.5 github.com/sirupsen/logrus v1.9.3 + go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.61.0 + go.opentelemetry.io/otel v1.38.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 + go.opentelemetry.io/otel/sdk v1.38.0 + google.golang.org/grpc v1.75.0 ) require ( @@ -25,57 +32,71 @@ require ( github.com/blevesearch/zap/v13 v13.0.6 // indirect github.com/blevesearch/zap/v14 v14.0.5 // indirect github.com/blevesearch/zap/v15 v15.0.3 // indirect - github.com/bytedance/sonic v1.11.6 // indirect - github.com/bytedance/sonic/loader v0.1.1 // indirect + github.com/bytedance/sonic v1.13.2 // indirect + github.com/bytedance/sonic/loader v0.2.4 // indirect + github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/cloudwego/base64x v0.1.4 // indirect - github.com/cloudwego/iasm v0.2.0 // indirect + github.com/cloudwego/base64x v0.1.5 // indirect github.com/couchbase/vellum v1.0.2 // indirect - github.com/gabriel-vasile/mimetype v1.4.3 // indirect - github.com/gin-contrib/sse v0.1.0 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/gabriel-vasile/mimetype v1.4.9 // indirect + github.com/gin-contrib/sse v1.1.0 // indirect github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2 // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.20.0 // indirect - github.com/goccy/go-json v0.10.2 // indirect - github.com/golang/protobuf v1.5.0 // indirect - github.com/golang/snappy v0.0.1 // indirect + github.com/go-playground/validator/v10 v10.26.0 // indirect + github.com/goccy/go-json v0.10.5 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/golang/snappy v0.0.4 // indirect github.com/google/go-querystring v1.1.0 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/gorilla/websocket v1.5.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect github.com/hashicorp/cronexpr v1.1.2 // indirect - github.com/hashicorp/errwrap v1.0.0 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.17.9 // indirect - github.com/klauspost/cpuid/v2 v2.2.7 // indirect - github.com/kr/text v0.2.0 // indirect + github.com/klauspost/compress v1.18.0 // indirect + github.com/klauspost/cpuid/v2 v2.2.10 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/go-testing-interface v1.14.2-0.20210821155943-2d9075ca8770 // 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/mschoch/smat v0.2.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/philhofer/fwd v1.0.0 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // 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/prometheus/procfs v0.16.1 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/steveyen/gtreap v0.1.0 // indirect github.com/tinylib/msgp v1.1.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect github.com/willf/bitset v1.1.10 // indirect - go.etcd.io/bbolt v1.3.5 // indirect - golang.org/x/arch v0.8.0 // indirect - golang.org/x/crypto v0.24.0 // indirect - golang.org/x/net v0.26.0 // indirect - golang.org/x/sys v0.22.0 // indirect - golang.org/x/text v0.16.0 // indirect - google.golang.org/protobuf v1.34.2 // indirect + go.etcd.io/bbolt v1.3.7 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/otel/metric v1.38.0 // indirect + go.opentelemetry.io/otel/trace v1.38.0 // indirect + go.opentelemetry.io/proto/otlp v1.7.1 // indirect + golang.org/x/arch v0.17.0 // indirect + golang.org/x/crypto v0.41.0 // indirect + golang.org/x/net v0.43.0 // indirect + golang.org/x/sys v0.35.0 // indirect + golang.org/x/text v0.28.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect + google.golang.org/protobuf v1.36.8 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/pkg/go.sum b/pkg/go.sum index 568f616..14171f8 100644 --- a/pkg/go.sum +++ b/pkg/go.sum @@ -27,15 +27,13 @@ github.com/blevesearch/zap/v14 v14.0.5 h1:NdcT+81Nvmp2zL+NhwSvGSLh7xNgGL8QRVZ67n github.com/blevesearch/zap/v14 v14.0.5/go.mod h1:bWe8S7tRrSBTIaZ6cLRbgNH4TUDaC9LZSpRGs85AsGY= github.com/blevesearch/zap/v15 v15.0.3 h1:Ylj8Oe+mo0P25tr9iLPp33lN6d4qcztGjaIsP51UxaY= github.com/blevesearch/zap/v15 v15.0.3/go.mod h1:iuwQrImsh1WjWJ0Ue2kBqY83a0rFtJTqfa9fp1rbVVU= -github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= -github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= -github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= +github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ= github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY= +github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= 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/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= -github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= -github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= +github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4= github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= @@ -45,68 +43,62 @@ github.com/couchbase/moss v0.1.0/go.mod h1:9MaHIaRuy9pvLPUJxB8sh8OrLfyDczECVL37g github.com/couchbase/vellum v1.0.2 h1:BrbP0NKiyDdndMPec8Jjhy0U47CZ0Lgx3xUC2r9rZqw= github.com/couchbase/vellum v1.0.2/go.mod h1:FcwrEivFpNi24R3jLOs3n+fs5RnuQnQqCLBJ1uAg1W4= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d h1:SwD98825d6bdB+pEuTxWOXiSjBrHdOl/UVp75eI7JT8= github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d/go.mod h1:URriBxXwVq5ijiJ12C7iIZqlA69nTlI+LgI6/pwftG8= github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM= github.com/cznic/strutil v0.0.0-20181122101858-275e90344537/go.mod h1:AHHPPPXTw0h6pVabbcbyGRK1DckRn7r/STdZEeIDzZc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= 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/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64= github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg= github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0= -github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= -github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= -github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= +github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= -github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= -github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= +github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= +github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ= github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2 h1:Ujru1hufTHVb++eG6OuNDKMxZnGIvF6o/u8q/8h2+I4= github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31 h1:gclg6gY70GLy3PbkQ1AERPfmLMMagS60DKF78eWwLn8= github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= -github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= -github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= -github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k= +github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-github/v58 v58.0.0 h1:Una7GGERlF/37XfkPwpzYJe0Vp4dt2k1kCjlxwjIvzw= github.com/google/go-github/v58 v58.0.0/go.mod h1:k4hxDKEfoWpSqFlc8LTpGd9fu2KrV1YAa6Hi6FmDNY4= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 h1:twflg0XRTjwKpxb/jFExr4HGq6on2dEOmnL6FV+fgPw= github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU= github.com/hashicorp/cronexpr v1.1.2 h1:wG/ZYIKT+RT3QkOdgYc+xsKWVRgnxJ1OJtjjy84fJ9A= github.com/hashicorp/cronexpr v1.1.2/go.mod h1:P4wA0KBl9C5q2hABiMO7cp6jcIg96CDh1Efb3g1PWA4= -github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= 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/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= @@ -127,11 +119,9 @@ github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7 github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= -github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= -github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= -github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= github.com/kljensen/snowball v0.6.0/go.mod h1:27N7E8fVU5H68RlUmnWwZCfxgt4POBJfENGMvNRhldw= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -147,8 +137,7 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 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/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= -github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= +github.com/mitchellh/go-testing-interface v1.14.2-0.20210821155943-2d9075ca8770 h1:drhDO54gdT/a15GBcMRmunZiNcLgPiFIJa23KzmcvcU= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= @@ -166,24 +155,21 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= -github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/philhofer/fwd v1.0.0 h1:UbZqGr5Y38ApvM/V/jEljVxwocdweyH+vmYvRPBnbqQ= github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= 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/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/shoenig/test v1.7.1 h1:UJcjSAI3aUKx52kfcfhblgyhZceouhvvs3OYdWgn+PY= github.com/shoenig/test v1.7.1/go.mod h1:UxJ6u/x2v/TNs/LoLxBNJRV9DiwBBKYxXSyczsBHFoI= @@ -200,7 +186,6 @@ github.com/steveyen/gtreap v0.1.0/go.mod h1:kl/5J7XbrOmlIbYIXdRHDDE5QxHqpk0cmkT7 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -208,9 +193,7 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= github.com/tebeka/snowball v0.4.2/go.mod h1:4IfL14h1lvwZcp1sfXuuc7/7yCsvVffTWxWxCLfFpYg= @@ -226,17 +209,26 @@ github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZ github.com/willf/bitset v1.1.10 h1:NotGKqX0KwQ72NUzqrjZq5ipPNDQex9lo3WpaS8L2sc= github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= -golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= -golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= +go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.61.0 h1:VkrF0D14uQrCmPqBkYlwWnhgcwzXvIRAjX8eXO7vy6M= +go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 h1:JgtbA0xkWHnTmYk7YusopJFX6uleBmAuZ8n05NEh8nQ= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 h1:aTL7F04bJHUlztTsNGJ2l+6he8c+y/b//eR0jjjemT4= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.36.0 h1:G8Xec/SgZQricwWBJF/mHZc7A02YHedfFDENwJEdRA0= +go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= +go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= +go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= +go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= +go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +golang.org/x/arch v0.17.0 h1:4O3dfLzd+lQewptAHqjewQZQDyEdejz3VwgeYwkZneU= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= -golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= -golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -244,17 +236,16 @@ golang.org/x/sys v0.0.0-20181221143128-b4a75ba826a6/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= -golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 h1:BIRfGDEjiHRrk0QKZe3Xv2ieMhtgRGeLcZQ0mIVn4EY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE= +google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4= +google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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= @@ -268,4 +259,3 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/voice-memos/go.mod b/voice-memos/go.mod index f97d8df..00786d4 100644 --- a/voice-memos/go.mod +++ b/voice-memos/go.mod @@ -1,85 +1,70 @@ module github.com/pirogoeth/apps/voice-memos -go 1.23.2 +go 1.24.1 replace github.com/pirogoeth/apps => ../ require ( - github.com/gin-gonic/gin v1.10.0 + github.com/gin-gonic/gin v1.10.1 github.com/imroc/req/v3 v3.48.0 github.com/ollama/ollama v0.3.14 - github.com/pirogoeth/apps v0.0.0-20240629210235-db0633d42a21 github.com/sirupsen/logrus v1.9.3 - github.com/spf13/cobra v1.8.1 + github.com/spf13/cobra v1.9.1 github.com/usememos/memos v0.22.5 github.com/vimeo/go-magic v1.0.0 - google.golang.org/grpc v1.67.1 + google.golang.org/grpc v1.75.0 ) require ( - github.com/andybalholm/brotli v1.1.0 // indirect - github.com/beorn7/perks v1.0.1 // indirect - github.com/bytedance/sonic v1.11.6 // indirect - github.com/bytedance/sonic/loader v0.1.1 // indirect - github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/andybalholm/brotli v1.1.1 // indirect + github.com/bytedance/sonic v1.13.2 // indirect + github.com/bytedance/sonic/loader v0.2.4 // indirect github.com/cloudflare/circl v1.4.0 // indirect - github.com/cloudwego/base64x v0.1.4 // indirect - github.com/cloudwego/iasm v0.2.0 // indirect - github.com/gabriel-vasile/mimetype v1.4.3 // indirect - github.com/ghodss/yaml v1.0.0 // indirect - github.com/gin-contrib/sse v0.1.0 // indirect + github.com/cloudwego/base64x v0.1.5 // indirect + github.com/gabriel-vasile/mimetype v1.4.9 // indirect + github.com/gin-contrib/sse v1.1.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.20.0 // indirect + github.com/go-playground/validator/v10 v10.26.0 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect - github.com/goccy/go-json v0.10.2 // indirect - github.com/google/go-github/v58 v58.0.0 // indirect - github.com/google/go-querystring v1.1.0 // indirect + github.com/goccy/go-json v0.10.5 // indirect github.com/google/pprof v0.0.0-20240910150728-a0b0bb1d4134 // indirect - github.com/gorilla/websocket v1.5.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect - github.com/hashicorp/cronexpr v1.1.2 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect - github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/hashicorp/go-rootcerts v1.0.2 // indirect - github.com/hashicorp/nomad/api v0.0.0-20231227080007-76ba3e10e73d // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/kelseyhightower/envconfig v1.4.0 // indirect - github.com/klauspost/compress v1.17.9 // indirect - github.com/klauspost/cpuid/v2 v2.2.7 // indirect + github.com/klauspost/compress v1.18.0 // indirect + github.com/klauspost/cpuid/v2 v2.2.10 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mitchellh/go-homedir v1.1.0 // 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/onsi/ginkgo/v2 v2.20.2 // indirect - github.com/pelletier/go-toml/v2 v2.2.2 // indirect - github.com/prometheus/client_golang v1.19.1 // indirect - github.com/prometheus/client_model v0.5.0 // indirect - github.com/prometheus/common v0.48.0 // indirect - github.com/prometheus/procfs v0.12.0 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/quic-go/qpack v0.5.1 // indirect github.com/quic-go/quic-go v0.47.0 // indirect github.com/refraction-networking/utls v1.6.7 // indirect - github.com/spf13/pflag v1.0.5 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect + github.com/spf13/pflag v1.0.6 // indirect + github.com/stretchr/testify v1.11.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect + go.opentelemetry.io/otel v1.38.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.38.0 // indirect go.uber.org/mock v0.4.0 // indirect - golang.org/x/arch v0.8.0 // indirect - golang.org/x/crypto v0.27.0 // indirect - golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect - golang.org/x/mod v0.21.0 // indirect - golang.org/x/net v0.29.0 // indirect - golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.25.0 // indirect - golang.org/x/text v0.18.0 // indirect - golang.org/x/tools v0.25.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect - google.golang.org/protobuf v1.34.2 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect + golang.org/x/arch v0.17.0 // indirect + golang.org/x/crypto v0.41.0 // indirect + golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect + golang.org/x/mod v0.26.0 // indirect + golang.org/x/net v0.43.0 // indirect + golang.org/x/sync v0.16.0 // indirect + golang.org/x/sys v0.35.0 // indirect + golang.org/x/text v0.28.0 // indirect + golang.org/x/time v0.14.0 // indirect + golang.org/x/tools v0.35.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect + google.golang.org/protobuf v1.36.8 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/voice-memos/go.sum b/voice-memos/go.sum index 6bd7866..c45af59 100644 --- a/voice-memos/go.sum +++ b/voice-memos/go.sum @@ -1,90 +1,51 @@ -github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= -github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= -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/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= -github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= -github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= +github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= +github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ= github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= -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/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY= github.com/cloudflare/circl v1.4.0 h1:BV7h5MgrktNzytKmWjpOtdYrf0lkkbF8YMlBGPhJQrY= github.com/cloudflare/circl v1.4.0/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU= -github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= -github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= -github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= +github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4= github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= -github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -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/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= -github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= -github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= -github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= -github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= -github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= -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/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY= +github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= +github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= -github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k= 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/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= -github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-github/v58 v58.0.0 h1:Una7GGERlF/37XfkPwpzYJe0Vp4dt2k1kCjlxwjIvzw= -github.com/google/go-github/v58 v58.0.0/go.mod h1:k4hxDKEfoWpSqFlc8LTpGd9fu2KrV1YAa6Hi6FmDNY4= -github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= -github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= +github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20240910150728-a0b0bb1d4134 h1:c5FlPPgxOn7kJz3VoPLkQYQXGBS3EklQ4Zfi57uOuqQ= github.com/google/pprof v0.0.0-20240910150728-a0b0bb1d4134/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I= -github.com/hashicorp/cronexpr v1.1.2 h1:wG/ZYIKT+RT3QkOdgYc+xsKWVRgnxJ1OJtjjy84fJ9A= -github.com/hashicorp/cronexpr v1.1.2/go.mod h1:P4wA0KBl9C5q2hABiMO7cp6jcIg96CDh1Efb3g1PWA4= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU= 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-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= -github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= 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/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= -github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= -github.com/hashicorp/nomad/api v0.0.0-20231227080007-76ba3e10e73d h1:Ztvo+OG+/xc6GQFiKME65qY6nveab1c7Hy8TFdBzbUI= -github.com/hashicorp/nomad/api v0.0.0-20231227080007-76ba3e10e73d/go.mod h1:ijDwa6o1uG1jFSq6kERiX2PamKGpZzTmo0XOFNeFZgw= github.com/imroc/req/v3 v3.48.0 h1:IYuMGetuwLzOOTzDCquDqs912WNwpsPK0TBXWPIvoqg= github.com/imroc/req/v3 v3.48.0/go.mod h1:weam9gmyb00QnOtu6HXSnk44dNFkIUQb5QdMx13FeUU= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 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/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= -github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= -github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= -github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= -github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -94,12 +55,6 @@ github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -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/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= -github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= -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/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= @@ -111,48 +66,31 @@ github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4 github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag= github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= -github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= -github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= -github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= -github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= -github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= -github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= -github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= -github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= -github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= github.com/quic-go/quic-go v0.47.0 h1:yXs3v7r2bm1wmPTYNLKAAJTHMYkPEsfYJmTazXrCZ7Y= github.com/quic-go/quic-go v0.47.0/go.mod h1:3bCapYsJvXGZcipOHuu7plYtaV6tnF+z7wIFsU0WK9E= github.com/refraction-networking/utls v1.6.7 h1:zVJ7sP1dJx/WtVuITug3qYUq034cDq9B2MR1K67ULZM= github.com/refraction-networking/utls v1.6.7/go.mod h1:BC3O4vQzye5hqpmDTWUqi4P5DDhzJfkV1tdqtawQIH0= -github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= -github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/shoenig/test v1.7.0 h1:eWcHtTXa6QLnBvm0jgEabMRN/uJ4DMV3M8xUGgRkZmk= -github.com/shoenig/test v1.7.0/go.mod h1:UxJ6u/x2v/TNs/LoLxBNJRV9DiwBBKYxXSyczsBHFoI= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= -github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= @@ -161,48 +99,36 @@ github.com/usememos/memos v0.22.5 h1:sSRtIJfP2z8OcnppjRY+Ksc9aSdY3pPJOXWxeOfsT8w github.com/usememos/memos v0.22.5/go.mod h1:jqZYscdq0Qudq2d0azMzkm58vvt2Ml32IcayxI0Gcxc= github.com/vimeo/go-magic v1.0.0 h1:1GGtwzLJwSd7i24Ie7LSNLF0T/w1NiZn5iELjgWcAy4= github.com/vimeo/go-magic v1.0.0/go.mod h1:xvu4I7AcaioNKakZMURKiJPAlHCTFwIr+qQhOOQQfBk= +github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= +go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= +go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= +go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= +go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= -golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= -golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= -golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= -golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= -golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk= -golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= -golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= -golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= -golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= -golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/arch v0.17.0 h1:4O3dfLzd+lQewptAHqjewQZQDyEdejz3VwgeYwkZneU= +golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= +golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI= +golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg= +golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= +golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= -golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= -golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= -golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= -golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE= -golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 h1:wKguEg1hsxI2/L3hUYrpo1RVi48K+uTyzKqprwLXsb8= -google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142/go.mod h1:d6be+8HhtEtucleCbxpPW9PA9XwISACu8nvpPqF0BVo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 h1:e7S5W7MGGLaSu8j3YjdezkZ+m1/Nm0uRVRMEMGk26Xs= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= -google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= -google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= +golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= +golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= +golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 h1:BIRfGDEjiHRrk0QKZe3Xv2ieMhtgRGeLcZQ0mIVn4EY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE= +google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4= +google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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/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= nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=