diff --git a/cli/log.go b/cli/log.go index 92a0d77..b7930d9 100644 --- a/cli/log.go +++ b/cli/log.go @@ -43,3 +43,7 @@ func logErrorCmd(cmd cobra.Command, err error) { func logOKCmd(cmd cobra.Command) { fmt.Fprintf(cmd.OutOrStdout(), "\n%s\n\n", color.BlueString("ok")) } + +func logSuccessCmd(cmd cobra.Command, msg string) { + fmt.Fprintf(cmd.OutOrStdout(), "\n%s\n", color.GreenString(msg)) +} diff --git a/cli/provision.go b/cli/provision.go new file mode 100644 index 0000000..5dcf45a --- /dev/null +++ b/cli/provision.go @@ -0,0 +1,254 @@ +package cli + +import ( + "fmt" + "os" + "strings" + + "github.com/0x6flab/namegenerator" + smqSDK "github.com/absmach/magistrala/pkg/sdk/go" + "github.com/absmach/supermq/pkg/errors" + "github.com/charmbracelet/huh" + "github.com/spf13/cobra" +) + +var ( + errFailedToCreateToken = errors.New("failed to create access token") + errFailedToCreateDomain = errors.New("failed to create domain") + errFailedChannelCreation = errors.New("failed to create channel") + errFailedClientCreation = errors.New("failed to create client") + errFailedConnectionCreation = errors.New("failed to create connection") + + smqsdk smqSDK.SDK + namegen = namegenerator.NewGenerator() + fileName = "config.toml" +) + +const filePermission = 0o644 + +func SetSuperMQSDK(sdk smqSDK.SDK) { + smqsdk = sdk +} + +var provisionCmd = &cobra.Command{ + Use: "provision", + Short: "Provision resources", + Long: `Provision necessary resources for Propeller operation.`, + Run: func(cmd *cobra.Command, args []string) { + var ( + identity string + secret string + err error + token smqSDK.Token + domainName string + domainAlias string + domainPermission string + domain smqSDK.Domain + managerThingName string + managerThing smqSDK.Thing + propletThingName string + propletThing smqSDK.Thing + managerChannelName string + managerChannel smqSDK.Channel + ) + form := huh.NewForm( + huh.NewGroup( + huh.NewInput(). + Title("Enter your identity (e-mail)?"). + Value(&identity). + Validate(func(str string) error { + if str == "" { + return errors.New("identity is required") + } + + return nil + }), + huh.NewInput(). + Title("Enter your secret"). + EchoMode(huh.EchoModePassword). + Value(&secret). + Validate(func(str string) error { + if str == "" { + return errors.New("secret is required") + } + u := smqSDK.Login{ + Identity: identity, + Secret: secret, + } + + token, err = smqsdk.CreateToken(u) + if err != nil { + return errors.Wrap(errFailedToCreateToken, err) + } + + return nil + }), + ), + huh.NewGroup( + huh.NewInput(). + Title("Enter your domain name(leave empty to auto generate)"). + Value(&domainName), + huh.NewInput(). + Title("Enter your domain alias(leave empty to auto generate)"). + Value(&domainAlias), + huh.NewSelect[string](). + Title("Select your domain permission"). + Options( + huh.NewOption("admin", "admin"), + huh.NewOption("edit", "edit"), + huh.NewOption("view", "view"), + ). + Value(&domainPermission). + Validate(func(str string) error { + if domainName == "" { + domainName = namegen.Generate() + } + if domainAlias == "" { + domainAlias = strings.ToLower(domainName) + } + domain = smqSDK.Domain{ + Name: domainName, + Alias: domainAlias, + Permission: domainPermission, + } + domain, err = smqsdk.CreateDomain(domain, token.AccessToken) + if err != nil { + return errors.Wrap(errFailedToCreateDomain, err) + } + + return nil + }), + ), + huh.NewGroup( + huh.NewInput(). + Title("Enter your manager thing name(leave empty to auto generate)"). + Value(&managerThingName). + Validate(func(str string) error { + if str == "" { + managerThingName = namegen.Generate() + } + managerThing = smqSDK.Thing{ + Name: managerThingName, + Tags: []string{"manager", "propeller"}, + Status: "enabled", + } + managerThing, err = smqsdk.CreateThing(managerThing, domain.ID, token.AccessToken) + if err != nil { + return errors.Wrap(errFailedClientCreation, err) + } + + return nil + }), + ), + huh.NewGroup( + huh.NewInput(). + Title("Enter your proplet thing name(leave empty to auto generate)"). + Value(&propletThingName). + Validate(func(str string) error { + if str == "" { + propletThingName = namegen.Generate() + } + propletThing = smqSDK.Thing{ + Name: propletThingName, + Tags: []string{"proplet", "propeller"}, + Status: "enabled", + } + propletThing, err = smqsdk.CreateThing(propletThing, domain.ID, token.AccessToken) + if err != nil { + return errors.Wrap(errFailedClientCreation, err) + } + + return nil + }), + ), huh.NewGroup( + huh.NewInput(). + Title("Enter your manager channel name(leave empty to auto generate)"). + Value(&managerChannelName). + Validate(func(str string) error { + if str == "" { + managerChannelName = namegen.Generate() + } + managerChannel = smqSDK.Channel{ + Name: managerChannelName, + Status: "enabled", + } + managerChannel, err = smqsdk.CreateChannel(managerChannel, domain.ID, token.AccessToken) + if err != nil { + return errors.Wrap(errFailedChannelCreation, err) + } + + managerConns := smqSDK.Connection{ + ThingID: managerThing.ID, + ChannelID: managerChannel.ID, + } + if err = smqsdk.Connect(managerConns, domain.ID, token.AccessToken); err != nil { + return errors.Wrap(errFailedConnectionCreation, err) + } + + propletConns := smqSDK.Connection{ + ThingID: propletThing.ID, + ChannelID: managerChannel.ID, + } + if err = smqsdk.Connect(propletConns, domain.ID, token.AccessToken); err != nil { + return errors.Wrap(errFailedConnectionCreation, err) + } + + return nil + }), + ), + ) + + if err := form.Run(); err != nil { + logErrorCmd(*cmd, errors.Wrap(errFailedConnectionCreation, err)) + + return + } + + configContent := fmt.Sprintf(`# SuperMQ Configuration + +[manager] +thing_id = "%s" +thing_key = "%s" +channel_id = "%s" + +[proplet] +thing_id = "%s" +thing_key = "%s" +channel_id = "%s" + +[proxy] +thing_id = "%s" +thing_key = "%s" +channel_id = "%s"`, + managerThing.ID, + managerThing.Credentials.Secret, + managerChannel.ID, + propletThing.ID, + propletThing.Credentials.Secret, + managerChannel.ID, + propletThing.ID, + propletThing.Credentials.Secret, + managerChannel.ID, + ) + + if err := os.WriteFile(fileName, []byte(configContent), filePermission); err != nil { + logErrorCmd(*cmd, errors.New(fmt.Sprintf("failed to create %s file", fileName))) + + return + } + + logSuccessCmd(*cmd, fmt.Sprintf("Successfully created %s file", fileName)) + }, +} + +func NewProvisionCmd() *cobra.Command { + provisionCmd.PersistentFlags().StringVarP( + &fileName, + "file-name", + "f", + fileName, + "The name of the file to create", + ) + + return provisionCmd +} diff --git a/cli/tasks.go b/cli/tasks.go index b64051e..7a74570 100644 --- a/cli/tasks.go +++ b/cli/tasks.go @@ -6,15 +6,13 @@ import ( ) var ( - DefTLSVerification = false - DefManagerURL = "http://localhost:7070" - defOffset uint64 = 0 - defLimit uint64 = 10 + defOffset uint64 = 0 + defLimit uint64 = 10 ) var psdk sdk.SDK -func SetSDK(s sdk.SDK) { +func SetPropellerSDK(s sdk.SDK) { psdk = s } @@ -153,14 +151,6 @@ func NewTasksCmd() *cobra.Command { cmd.AddCommand(&tasksCmd[i]) } - cmd.PersistentFlags().StringVarP( - &DefManagerURL, - "manager-url", - "m", - DefManagerURL, - "Manager URL", - ) - cmd.PersistentFlags().Uint64VarP( &defOffset, "offset", @@ -177,13 +167,5 @@ func NewTasksCmd() *cobra.Command { "Limit", ) - cmd.PersistentFlags().BoolVarP( - &DefTLSVerification, - "tls-verification", - "v", - DefTLSVerification, - "TLS Verification", - ) - return &cmd } diff --git a/cmd/cli/main.go b/cmd/cli/main.go index 5268d6b..952302c 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -3,11 +3,21 @@ package main import ( "log" + smqsdk "github.com/absmach/magistrala/pkg/sdk/go" "github.com/absmach/propeller/cli" "github.com/absmach/propeller/pkg/sdk" "github.com/spf13/cobra" ) +var ( + tlsVerification = false + managerURL = "http://localhost:7070" + usersURL = "http://localhost:9002" + thingsURL = "http://localhost:9000" + domainsURL = "http://localhost:8189" + msgContentType = string(smqsdk.CTJSONSenML) +) + func main() { rootCmd := &cobra.Command{ Use: "propeller-cli", @@ -15,17 +25,79 @@ func main() { Long: `Propeller CLI is a command line interface for interacting with Propeller components.`, PersistentPreRun: func(_ *cobra.Command, _ []string) { sdkConf := sdk.Config{ - ManagerURL: cli.DefManagerURL, - TLSVerification: cli.DefTLSVerification, + ManagerURL: managerURL, + TLSVerification: tlsVerification, } s := sdk.NewSDK(sdkConf) - cli.SetSDK(s) + cli.SetPropellerSDK(s) + + smqSDKConf := smqsdk.Config{ + UsersURL: usersURL, + ThingsURL: thingsURL, + DomainsURL: domainsURL, + MsgContentType: smqsdk.ContentType(msgContentType), + } + + if smqSDKConf.MsgContentType == "" { + smqSDKConf.MsgContentType = smqsdk.ContentType(msgContentType) + } + sdk := smqsdk.NewSDK(smqSDKConf) + cli.SetSuperMQSDK(sdk) }, } tasksCmd := cli.NewTasksCmd() + provisionCmd := cli.NewProvisionCmd() + + rootCmd.AddCommand(tasksCmd, provisionCmd) + + rootCmd.PersistentFlags().StringVarP( + &managerURL, + "manager-url", + "m", + managerURL, + "Manager URL", + ) + + rootCmd.PersistentFlags().BoolVarP( + &tlsVerification, + "tls-verification", + "v", + tlsVerification, + "TLS Verification", + ) + + rootCmd.PersistentFlags().StringVarP( + &usersURL, + "users-url", + "u", + usersURL, + "Users service URL", + ) + + rootCmd.PersistentFlags().StringVarP( + &thingsURL, + "things-url", + "t", + thingsURL, + "Things service URL", + ) + + rootCmd.PersistentFlags().StringVarP( + &domainsURL, + "domains-url", + "d", + domainsURL, + "Domains service URL", + ) - rootCmd.AddCommand(tasksCmd) + rootCmd.PersistentFlags().StringVarP( + &msgContentType, + "content-type", + "c", + msgContentType, + "Message content type", + ) if err := rootCmd.Execute(); err != nil { log.Fatal(err) diff --git a/cmd/manager/main.go b/cmd/manager/main.go index da6a8c5..c12e2c9 100644 --- a/cmd/manager/main.go +++ b/cmd/manager/main.go @@ -13,6 +13,7 @@ import ( "github.com/absmach/magistrala/pkg/prometheus" "github.com/absmach/magistrala/pkg/server" httpserver "github.com/absmach/magistrala/pkg/server/http" + "github.com/absmach/propeller" "github.com/absmach/propeller/manager" "github.com/absmach/propeller/manager/api" "github.com/absmach/propeller/manager/middleware" @@ -30,6 +31,7 @@ const ( svcName = "manager" defHTTPPort = "7070" envPrefixHTTP = "MANAGER_HTTP_" + configPath = "config.toml" ) type config struct { @@ -38,9 +40,9 @@ type config struct { MQTTAddress string `env:"MANAGER_MQTT_ADDRESS" envDefault:"tcp://localhost:1883"` MQTTQoS uint8 `env:"MANAGER_MQTT_QOS" envDefault:"2"` MQTTTimeout time.Duration `env:"MANAGER_MQTT_TIMEOUT" envDefault:"30s"` - ChannelID string `env:"MANAGER_CHANNEL_ID,notEmpty"` - ThingID string `env:"MANAGER_THING_ID,notEmpty"` - ThingKey string `env:"MANAGER_THING_KEY,notEmpty"` + ChannelID string `env:"MANAGER_CHANNEL_ID"` + ThingID string `env:"MANAGER_THING_ID"` + ThingKey string `env:"MANAGER_THING_KEY"` Server server.Config OTELURL url.URL `env:"MANAGER_OTEL_URL"` TraceRatio float64 `env:"MANAGER_TRACE_RATIO" envDefault:"0"` @@ -59,6 +61,22 @@ func main() { cfg.InstanceID = uuid.NewString() } + if cfg.ThingID == "" || cfg.ThingKey == "" || cfg.ChannelID == "" { + _, err := os.Stat(configPath) + switch err { + case nil: + conf, err := propeller.LoadConfig(configPath) + if err != nil { + log.Fatalf("failed to load TOML configuration: %s", err.Error()) + } + cfg.ThingID = conf.Manager.ThingID + cfg.ThingKey = conf.Manager.ThingKey + cfg.ChannelID = conf.Manager.ChannelID + default: + log.Fatalf("failed to load TOML configuration: %s", err.Error()) + } + } + var level slog.Level if err := level.UnmarshalText([]byte(cfg.LogLevel)); err != nil { log.Fatalf("failed to parse log level: %s", err.Error()) diff --git a/cmd/proplet/main.go b/cmd/proplet/main.go index 33be8cc..e871e59 100644 --- a/cmd/proplet/main.go +++ b/cmd/proplet/main.go @@ -9,6 +9,7 @@ import ( "time" "github.com/absmach/magistrala/pkg/server" + "github.com/absmach/propeller" "github.com/absmach/propeller/pkg/mqtt" "github.com/absmach/propeller/proplet" "github.com/absmach/propeller/proplet/runtimes" @@ -17,19 +18,22 @@ import ( "golang.org/x/sync/errgroup" ) -const svcName = "proplet" +const ( + svcName = "proplet" + configPath = "config.toml" +) type config struct { - LogLevel string `env:"PROPLET_LOG_LEVEL" envDefault:"info"` + LogLevel string `env:"PROPLET_LOG_LEVEL" envDefault:"info"` InstanceID string `env:"PROPLET_INSTANCE_ID"` - MQTTAddress string `env:"PROPLET_MQTT_ADDRESS" envDefault:"tcp://localhost:1883"` - MQTTTimeout time.Duration `env:"PROPLET_MQTT_TIMEOUT" envDefault:"30s"` - MQTTQoS byte `env:"PROPLET_MQTT_QOS" envDefault:"2"` - LivelinessInterval time.Duration `env:"PROPLET_LIVELINESS_INTERVAL" envDefault:"10s"` - ChannelID string `env:"PROPLET_CHANNEL_ID,notEmpty"` - ThingID string `env:"PROPLET_THING_ID,notEmpty"` - ThingKey string `env:"PROPLET_THING_KEY,notEmpty"` - ExternalWasmRuntime string `env:"PROPLET_EXTERNAL_WASM_RUNTIME" envDefault:""` + MQTTAddress string `env:"PROPLET_MQTT_ADDRESS" envDefault:"tcp://localhost:1883"` + MQTTTimeout time.Duration `env:"PROPLET_MQTT_TIMEOUT" envDefault:"30s"` + MQTTQoS byte `env:"PROPLET_MQTT_QOS" envDefault:"2"` + LivelinessInterval time.Duration `env:"PROPLET_LIVELINESS_INTERVAL" envDefault:"10s"` + ChannelID string `env:"PROPLET_CHANNEL_ID"` + ThingID string `env:"PROPLET_THING_ID"` + ThingKey string `env:"PROPLET_THING_KEY"` + ExternalWasmRuntime string `env:"PROPLET_EXTERNAL_WASM_RUNTIME" envDefault:""` } func main() { @@ -45,6 +49,22 @@ func main() { cfg.InstanceID = uuid.NewString() } + if cfg.ThingID == "" || cfg.ThingKey == "" || cfg.ChannelID == "" { + _, err := os.Stat(configPath) + switch err { + case nil: + conf, err := propeller.LoadConfig(configPath) + if err != nil { + log.Fatalf("failed to load TOML configuration: %s", err.Error()) + } + cfg.ThingID = conf.Proplet.ThingID + cfg.ThingKey = conf.Proplet.ThingKey + cfg.ChannelID = conf.Proplet.ChannelID + default: + log.Fatalf("failed to load TOML configuration: %s", err.Error()) + } + } + var level slog.Level if err := level.UnmarshalText([]byte(cfg.LogLevel)); err != nil { log.Fatalf("failed to parse log level: %s", err.Error()) diff --git a/cmd/proxy/main.go b/cmd/proxy/main.go index e70f93a..4bec1e4 100644 --- a/cmd/proxy/main.go +++ b/cmd/proxy/main.go @@ -6,27 +6,33 @@ import ( "log" "log/slog" "os" + "time" + "github.com/absmach/propeller" "github.com/absmach/propeller/proxy" "github.com/caarlos0/env/v11" "golang.org/x/sync/errgroup" ) -const svcName = "proxy" +const ( + svcName = "proxy" + configPath = "config.toml" +) type config struct { - LogLevel string `env:"PROXY_LOG_LEVEL" envDefault:"info"` - - BrokerURL string `env:"PROPLET_MQTT_ADDRESS" envDefault:"tcp://localhost:1883"` - PropletKey string `env:"PROPLET_THING_KEY,notEmpty"` - PropletID string `env:"PROPLET_THING_ID,notEmpty" ` - ChannelID string `env:"PROPLET_CHANNEL_ID,notEmpty"` - - ChunkSize int `env:"PROXY_CHUNK_SIZE" envDefault:"512000"` - Authenticate bool `env:"PROXY_AUTHENTICATE" envDefault:"false"` - Token string `env:"PROXY_REGISTRY_TOKEN" envDefault:""` - Username string `env:"PROXY_REGISTRY_USERNAME" envDefault:""` - Password string `env:"PROXY_REGISTRY_PASSWORD" envDefault:""` + LogLevel string `env:"PROXY_LOG_LEVEL" envDefault:"info"` + MQTTAddress string `env:"PROXY_MQTT_ADDRESS" envDefault:"tcp://localhost:1883"` + MQTTTimeout time.Duration `env:"PROXY_MQTT_TIMEOUT" envDefault:"30s"` + ChannelID string `env:"PROPLET_CHANNEL_ID"` + ThingID string `env:"PROPLET_THING_ID"` + ThingKey string `env:"PROPLET_THING_KEY"` + + // HTTP Registry configuration + ChunkSize int `env:"PROXY_CHUNK_SIZE" envDefault:"512000"` + Authenticate bool `env:"PROXY_AUTHENTICATE" envDefault:"false"` + Token string `env:"PROXY_REGISTRY_TOKEN" envDefault:""` + Username string `env:"PROXY_REGISTRY_USERNAME" envDefault:""` + Password string `env:"PROXY_REGISTRY_PASSWORD" envDefault:""` RegistryURL string `env:"PROXY_REGISTRY_URL,notEmpty"` } @@ -38,6 +44,22 @@ func main() { log.Fatalf("failed to load configuration : %s", err.Error()) } + if cfg.ThingID == "" || cfg.ThingKey == "" || cfg.ChannelID == "" { + _, err := os.Stat(configPath) + switch err { + case nil: + conf, err := propeller.LoadConfig(configPath) + if err != nil { + log.Fatalf("failed to load TOML configuration: %s", err.Error()) + } + cfg.ThingID = conf.Proxy.ThingID + cfg.ThingKey = conf.Proxy.ThingKey + cfg.ChannelID = conf.Proxy.ChannelID + default: + log.Fatalf("failed to load TOML configuration: %s", err.Error()) + } + } + var level slog.Level if err := level.UnmarshalText([]byte(cfg.LogLevel)); err != nil { log.Fatalf("failed to parse log level: %s", err.Error()) @@ -49,9 +71,9 @@ func main() { slog.SetDefault(logger) mqttCfg := proxy.MQTTProxyConfig{ - BrokerURL: cfg.BrokerURL, - Password: cfg.PropletKey, - PropletID: cfg.PropletID, + BrokerURL: cfg.MQTTAddress, + Password: cfg.ThingKey, + PropletID: cfg.ThingID, ChannelID: cfg.ChannelID, } diff --git a/config.go b/config.go new file mode 100644 index 0000000..b3de99d --- /dev/null +++ b/config.go @@ -0,0 +1,51 @@ +package propeller + +import ( + "fmt" + "os" + + "github.com/pelletier/go-toml" +) + +type Config struct { + Manager ManagerConfig `toml:"manager"` + Proplet PropletConfig `toml:"proplet"` + Proxy ProxyConfig `toml:"proxy"` +} + +type ManagerConfig struct { + ThingID string `toml:"thing_id"` + ThingKey string `toml:"thing_key"` + ChannelID string `toml:"channel_id"` +} + +type PropletConfig struct { + ThingID string `toml:"thing_id"` + ThingKey string `toml:"thing_key"` + ChannelID string `toml:"channel_id"` +} + +type ProxyConfig struct { + ThingID string `toml:"thing_id"` + ThingKey string `toml:"thing_key"` + ChannelID string `toml:"channel_id"` +} + +func LoadConfig(path string) (*Config, error) { + data, err := os.ReadFile(path) + if err != nil { + return nil, fmt.Errorf("error reading config file: %w", err) + } + + tree, err := toml.Load(string(data)) + if err != nil { + return nil, fmt.Errorf("error parsing config file: %w", err) + } + + var cfg Config + if err := tree.Unmarshal(&cfg); err != nil { + return nil, fmt.Errorf("error unmarshaling config: %w", err) + } + + return &cfg, nil +} diff --git a/docker/.env b/docker/.env index 9c60785..f5764be 100644 --- a/docker/.env +++ b/docker/.env @@ -14,6 +14,15 @@ MG_NATS_HEALTH_CHECK=http://nats:${MG_NATS_HTTP_PORT}/healthz MG_NATS_WS_TARGET_PATH= MG_NATS_MQTT_QOS=1 +## RabbitMQ +MG_RABBITMQ_PORT=5672 +MG_RABBITMQ_HTTP_PORT=15672 +MG_RABBITMQ_USER=magistrala +MG_RABBITMQ_PASS=magistrala +MG_RABBITMQ_COOKIE=magistrala +MG_RABBITMQ_VHOST=/ +MG_RABBITMQ_URL=amqp://${MG_RABBITMQ_USER}:${MG_RABBITMQ_PASS}@rabbitmq:${MG_RABBITMQ_PORT}${MG_RABBITMQ_VHOST} + ## Message Broker MG_MESSAGE_BROKER_TYPE=nats MG_MESSAGE_BROKER_URL=${MG_NATS_URL} @@ -187,6 +196,14 @@ MG_THINGS_AUTH_GRPC_CLIENT_CERT=${GRPC_MTLS:+./ssl/certs/things-grpc-client.crt} MG_THINGS_AUTH_GRPC_CLIENT_KEY=${GRPC_MTLS:+./ssl/certs/things-grpc-client.key} MG_THINGS_AUTH_GRPC_CLIENT_CA_CERTS=${GRPC_MTLS:+./ssl/certs/ca.crt} +### HTTP +MG_HTTP_ADAPTER_LOG_LEVEL=debug +MG_HTTP_ADAPTER_HOST=http-adapter +MG_HTTP_ADAPTER_PORT=8008 +MG_HTTP_ADAPTER_SERVER_CERT= +MG_HTTP_ADAPTER_SERVER_KEY= +MG_HTTP_ADAPTER_INSTANCE_ID= + ### MQTT MG_MQTT_ADAPTER_LOG_LEVEL=debug MG_MQTT_ADAPTER_MQTT_PORT=1883 @@ -197,4 +214,4 @@ MG_MQTT_ADAPTER_INSTANCE_ID= MG_MQTT_ADAPTER_ES_DB=0 # Docker image tag -MG_RELEASE_TAG=latest +MG_RELEASE_TAG="v0.15.1" diff --git a/docker/compose.yaml b/docker/compose.yaml index 75298d8..d1b3005 100644 --- a/docker/compose.yaml +++ b/docker/compose.yaml @@ -75,7 +75,7 @@ services: - magistrala-auth-db-volume:/var/lib/postgresql/data auth: - image: ghcr.io/absmach/magistrala/auth:${MG_RELEASE_TAG} + image: magistrala/auth:${MG_RELEASE_TAG} container_name: magistrala-auth depends_on: - auth-db @@ -184,6 +184,7 @@ services: - things - users - mqtt-adapter + - http-adapter things-db: image: postgres:16.2-alpine @@ -212,7 +213,7 @@ services: - magistrala-things-redis-volume:/data things: - image: ghcr.io/absmach/magistrala/things:${MG_RELEASE_TAG} + image: magistrala/things:${MG_RELEASE_TAG} container_name: magistrala-things depends_on: - things-db @@ -414,7 +415,7 @@ services: - magistrala-base-net mqtt-adapter: - image: ghcr.io/absmach/magistrala/mqtt:${MG_RELEASE_TAG} + image: magistrala/mqtt:${MG_RELEASE_TAG} container_name: magistrala-mqtt depends_on: - things @@ -465,6 +466,51 @@ services: bind: create_host_path: true + http-adapter: + image: magistrala/http:${MG_RELEASE_TAG} + container_name: magistrala-http + depends_on: + - things + - nats + restart: on-failure + environment: + MG_HTTP_ADAPTER_LOG_LEVEL: ${MG_HTTP_ADAPTER_LOG_LEVEL} + MG_HTTP_ADAPTER_HOST: ${MG_HTTP_ADAPTER_HOST} + MG_HTTP_ADAPTER_PORT: ${MG_HTTP_ADAPTER_PORT} + MG_HTTP_ADAPTER_SERVER_CERT: ${MG_HTTP_ADAPTER_SERVER_CERT} + MG_HTTP_ADAPTER_SERVER_KEY: ${MG_HTTP_ADAPTER_SERVER_KEY} + MG_THINGS_AUTH_GRPC_URL: ${MG_THINGS_AUTH_GRPC_URL} + MG_THINGS_AUTH_GRPC_TIMEOUT: ${MG_THINGS_AUTH_GRPC_TIMEOUT} + MG_THINGS_AUTH_GRPC_CLIENT_CERT: ${MG_THINGS_AUTH_GRPC_CLIENT_CERT:+/things-grpc-client.crt} + MG_THINGS_AUTH_GRPC_CLIENT_KEY: ${MG_THINGS_AUTH_GRPC_CLIENT_KEY:+/things-grpc-client.key} + MG_THINGS_AUTH_GRPC_SERVER_CA_CERTS: ${MG_THINGS_AUTH_GRPC_SERVER_CA_CERTS:+/things-grpc-server-ca.crt} + MG_MESSAGE_BROKER_URL: ${MG_MESSAGE_BROKER_URL} + MG_JAEGER_URL: ${MG_JAEGER_URL} + MG_JAEGER_TRACE_RATIO: ${MG_JAEGER_TRACE_RATIO} + MG_SEND_TELEMETRY: ${MG_SEND_TELEMETRY} + MG_HTTP_ADAPTER_INSTANCE_ID: ${MG_HTTP_ADAPTER_INSTANCE_ID} + ports: + - ${MG_HTTP_ADAPTER_PORT}:${MG_HTTP_ADAPTER_PORT} + networks: + - magistrala-base-net + volumes: + # Things gRPC mTLS client certificates + - type: bind + source: ${MG_THINGS_AUTH_GRPC_CLIENT_CERT:-ssl/certs/dummy/client_cert} + target: /things-grpc-client${MG_THINGS_AUTH_GRPC_CLIENT_CERT:+.crt} + bind: + create_host_path: true + - type: bind + source: ${MG_THINGS_AUTH_GRPC_CLIENT_KEY:-ssl/certs/dummy/client_key} + target: /things-grpc-client${MG_THINGS_AUTH_GRPC_CLIENT_KEY:+.key} + bind: + create_host_path: true + - type: bind + source: ${MG_THINGS_AUTH_GRPC_SERVER_CA_CERTS:-ssl/certs/dummy/server_ca} + target: /things-grpc-server-ca${MG_THINGS_AUTH_GRPC_SERVER_CA_CERTS:+.crt} + bind: + create_host_path: true + vernemq: image: magistrala/vernemq:${MG_RELEASE_TAG} container_name: magistrala-vernemq diff --git a/go.mod b/go.mod index 21dd315..77f15d9 100644 --- a/go.mod +++ b/go.mod @@ -1,11 +1,13 @@ module github.com/absmach/propeller -go 1.23.0 +go 1.23.4 require ( github.com/0x6flab/namegenerator v1.4.0 github.com/absmach/magistrala v0.15.1 + github.com/absmach/supermq v0.16.1-0.20250113091433-3a11b54394b2 github.com/caarlos0/env/v11 v11.3.1 + github.com/charmbracelet/huh v0.6.0 github.com/eclipse/paho.mqtt.golang v1.5.0 github.com/fatih/color v1.18.0 github.com/go-chi/chi/v5 v5.2.0 @@ -13,6 +15,7 @@ require ( github.com/google/uuid v1.6.0 github.com/hokaccha/go-prettyjson v0.0.0-20211117102719-0474bc63780f github.com/opencontainers/image-spec v1.1.0 + github.com/pelletier/go-toml v1.9.5 github.com/prometheus/client_golang v1.20.5 github.com/spf13/cobra v1.8.1 github.com/tetratelabs/wazero v1.8.2 @@ -23,10 +26,23 @@ require ( ) require ( + github.com/absmach/senml v1.0.6 // indirect + github.com/atotto/clipboard v0.1.4 // indirect + github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect + github.com/catppuccin/go v0.2.0 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/charmbracelet/bubbles v0.20.0 // indirect + github.com/charmbracelet/bubbletea v1.2.4 // indirect + github.com/charmbracelet/lipgloss v1.0.0 // indirect + github.com/charmbracelet/x/ansi v0.7.0 // indirect + github.com/charmbracelet/x/exp/strings v0.0.0-20250116134054-e10c5c25afb9 // indirect + github.com/charmbracelet/x/term v0.2.1 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/go-logr/logr v1.4.2 // indirect @@ -35,14 +51,24 @@ require ( github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/klauspost/compress v1.17.11 // indirect + github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-localereader v0.0.1 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect + github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect + github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect + github.com/muesli/cancelreader v0.2.2 // indirect + github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.61.0 // indirect + github.com/prometheus/common v0.62.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect + github.com/rivo/uniseg v0.4.7 // indirect + github.com/smartystreets/goconvey v1.8.1 // indirect github.com/spf13/pflag v1.0.5 // indirect + github.com/x448/float16 v0.8.4 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 // indirect @@ -52,9 +78,10 @@ require ( golang.org/x/net v0.34.0 // indirect golang.org/x/sys v0.29.0 // indirect golang.org/x/text v0.21.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250106144421-5f5ef82da422 // indirect - google.golang.org/grpc v1.69.2 // indirect - google.golang.org/protobuf v1.36.2 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect + google.golang.org/grpc v1.69.4 // indirect + google.golang.org/protobuf v1.36.3 // indirect + moul.io/http2curl v1.0.0 // indirect oras.land/oras-go/v2 v2.5.0 ) diff --git a/go.sum b/go.sum index a7c2ef0..a710107 100644 --- a/go.sum +++ b/go.sum @@ -1,28 +1,66 @@ github.com/0x6flab/namegenerator v1.4.0 h1:QnkI813SZsI/hYnKD9pg3mkIlcYzCx0N4hnzb0YYME4= github.com/0x6flab/namegenerator v1.4.0/go.mod h1:2sQzXuS6dX/KEwWtB6GJU729O3m4gBdD5oAU8hd0SyY= +github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= +github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= +github.com/absmach/certs v0.0.0-20241014135535-3f118b801054 h1:NsIwp+ueKxDx8XftruA4hz8WUgyWq7eBE344nJt0LJg= +github.com/absmach/certs v0.0.0-20241014135535-3f118b801054/go.mod h1:bEAb/HjPztlrMmz8dLeJTke4Tzu9yW3+hY5eldEUtSY= github.com/absmach/magistrala v0.15.1 h1:3Bk2hlyWcV591LxPYwlvRcyCXTfuZ1g/EkNmU+o3NNQ= github.com/absmach/magistrala v0.15.1/go.mod h1:9pto6xuBt/IuCtZRdEha0iDQKNQ5tyNOjLXJgUiikYk= +github.com/absmach/mgate v0.4.5 h1:l6RmrEsR9jxkdb9WHUSecmT0HA41TkZZQVffFfUAIfI= +github.com/absmach/mgate v0.4.5/go.mod h1:IvRIHZexZPEIAPmmaJF0L5DY2ERjj+GxRGitOW4s6qo= +github.com/absmach/senml v1.0.6 h1:WPeIl6vQ00k7ghWSZYT/QP0KUxq2+4zQoaC7240pLFk= +github.com/absmach/senml v1.0.6/go.mod h1:QnJNPy1DJPy0+qUW21PTcH/xoh0LgfYZxTfwriMIvmQ= +github.com/absmach/supermq v0.16.1-0.20250113091433-3a11b54394b2 h1:mG0ucTmOE41BOzKpZyNkGHbLFangumlZd3TUddJ0J2U= +github.com/absmach/supermq v0.16.1-0.20250113091433-3a11b54394b2/go.mod h1:DjM4b/VF9m/o/MmQ/WUExbdatIvPnqyREe9zFEbW2YI= +github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= +github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= +github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= +github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= 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/caarlos0/env/v11 v11.3.1 h1:cArPWC15hWmEt+gWk7YBi7lEXTXCvpaSdCiZE2X5mCA= github.com/caarlos0/env/v11 v11.3.1/go.mod h1:qupehSf/Y0TUTsxKywqRt/vJjN5nz6vauiYEUUr8P4U= +github.com/catppuccin/go v0.2.0 h1:ktBeIrIP42b/8FGiScP9sgrWOss3lw0Z5SktRoithGA= +github.com/catppuccin/go v0.2.0/go.mod h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc= 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/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQWD9LIutE= +github.com/charmbracelet/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU= +github.com/charmbracelet/bubbletea v1.2.4 h1:KN8aCViA0eps9SCOThb2/XPIlea3ANJLUkv3KnQRNCE= +github.com/charmbracelet/bubbletea v1.2.4/go.mod h1:Qr6fVQw+wX7JkWWkVyXYk/ZUQ92a6XNekLXa3rR18MM= +github.com/charmbracelet/huh v0.6.0 h1:mZM8VvZGuE0hoDXq6XLxRtgfWyTI3b2jZNKh0xWmax8= +github.com/charmbracelet/huh v0.6.0/go.mod h1:GGNKeWCeNzKpEOh/OJD8WBwTQjV3prFAtQPpLv+AVwU= +github.com/charmbracelet/lipgloss v1.0.0 h1:O7VkGDvqEdGi93X+DeqsQ7PKHDgtQfF8j8/O2qFMQNg= +github.com/charmbracelet/lipgloss v1.0.0/go.mod h1:U5fy9Z+C38obMs+T+tJqst9VGzlOYGj4ri9reL3qUlo= +github.com/charmbracelet/x/ansi v0.7.0 h1:/QfFmiXOGGwN6fRbzvQaYp7fu1pkxpZ3qFBZWBsP404= +github.com/charmbracelet/x/ansi v0.7.0/go.mod h1:KBUFw1la39nl0dLl10l5ORDAqGXaeurTQmwyyVKse/Q= +github.com/charmbracelet/x/exp/strings v0.0.0-20250116134054-e10c5c25afb9 h1:mVygZGe6lzk+Slt4NWXTNRH4LaUeXDbChGpDSLYt39Y= +github.com/charmbracelet/x/exp/strings v0.0.0-20250116134054-e10c5c25afb9/go.mod h1:pBhA0ybfXv6hDjQUZ7hk1lVxBiUbupdw5R31yPUViVQ= +github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= +github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 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/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/eclipse/paho.mqtt.golang v1.5.0 h1:EH+bUVJNgttidWFkLLVKaQPGmkTUfQQqjOsyvMGvD6o= github.com/eclipse/paho.mqtt.golang v1.5.0/go.mod h1:du/2qNQVqJf/Sqs4MEL77kR8QTqANF7XU7Fk0aOTAgk= +github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= +github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= +github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/go-chi/chi/v5 v5.2.0 h1:Aj1EtB0qR2Rdo2dG4O94RIU35w2lvQSj6BRA4+qwFL0= github.com/go-chi/chi/v5 v5.2.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/go-gorp/gorp/v3 v3.1.0 h1:ItKF/Vbuj31dmV4jxA1qblpSwkl9g1typ24xoe70IGs= +github.com/go-gorp/gorp/v3 v3.1.0/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw= github.com/go-kit/kit v0.13.0 h1:OoneCcHKHQ03LfBpoQCUfCluwd2Vt3ohz+kvbJneZAU= github.com/go-kit/kit v0.13.0/go.mod h1:phqEHMMUbyrCFCTgH48JueqrM3md2HcAZ8N3XE4FKDg= github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= @@ -34,12 +72,16 @@ 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/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/gofrs/uuid/v5 v5.3.0 h1:m0mUMr+oVYUdxpMLgSYCZiXe7PuVPnI94+OMeVBNedk= +github.com/gofrs/uuid/v5 v5.3.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= 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.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 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/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g= +github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg= @@ -48,42 +90,88 @@ github.com/hokaccha/go-prettyjson v0.0.0-20211117102719-0474bc63780f h1:7LYC+Yfk github.com/hokaccha/go-prettyjson v0.0.0-20211117102719-0474bc63780f/go.mod h1:pFlLw2CfqZiIBOx6BuCeRLCrfxBJipTY0nIOF/VbGcI= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.7.2 h1:mLoDLV6sonKlvjIEsV56SkWNCnuNv531l94GaIzO+XI= +github.com/jackc/pgx/v5 v5.7.2/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ= +github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= +github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= +github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= +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/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= 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/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= +github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 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-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= +github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4= +github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= +github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= +github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= +github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a h1:2MaM6YC3mGu54x+RKAA6JiFFHlHDY1UbkxqppT7wYOg= +github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a/go.mod h1:hxSnBBYLK21Vtq/PHd0S2FYCxBXzBua8ov5s1RobyRQ= 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/nats-io/nats.go v1.38.0 h1:A7P+g7Wjp4/NWqDOOP/K6hfhr54DvdDQUznt5JFg9XA= +github.com/nats-io/nats.go v1.38.0/go.mod h1:IGUM++TwokGnXPs82/wCuiHS02/aKrdYUQkU8If6yjw= +github.com/nats-io/nkeys v0.4.9 h1:qe9Faq2Gxwi6RZnZMXfmGMZkg3afLLOtrU+gDZJ35b0= +github.com/nats-io/nkeys v0.4.9/go.mod h1:jcMqs+FLG+W5YO36OX6wFIFcmpdAns+w1Wm6D3I/evE= +github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= +github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= +github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= 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.61.0 h1:3gv/GThfX0cV2lpO7gkTUwZru38mxevy90Bj8YFSRQQ= -github.com/prometheus/common v0.61.0/go.mod h1:zr29OCN/2BsJRaFwG8QOBr41D6kkchKbpeNH7pAjb/s= +github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= +github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= 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/rabbitmq/amqp091-go v1.10.0 h1:STpn5XsHlHGcecLmMFCtg7mqq0RnD+zFr4uzukfVhBw= +github.com/rabbitmq/amqp091-go v1.10.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rubenv/sql-migrate v1.7.1 h1:f/o0WgfO/GqNuVg+6801K/KW3WdDSupzSjDYODmiUq4= +github.com/rubenv/sql-migrate v1.7.1/go.mod h1:Ob2Psprc0/3ggbM6wCzyYVFFuc6FyZrb2AS+ezLDFb4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY= +github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec= +github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY= +github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60= 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.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tetratelabs/wazero v1.8.2 h1:yIgLR/b2bN31bjxwXHD8a3d+BogigR952csSDdLYEv4= github.com/tetratelabs/wazero v1.8.2/go.mod h1:yAI0XTsMBhREkM/YDAK/zNou3GoiAce1P6+rp/wQhjs= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= 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/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU= @@ -104,26 +192,32 @@ go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qq go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck= go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4= go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4= +golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= +golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= +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/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 h1:GVIKPyP/kLIyVOgOnTwFOrvQaQUzOzGMCxgFUOEmm24= -google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422/go.mod h1:b6h1vNKhxaSoEI+5jc3PJUCustfli/mRab7295pY7rw= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250106144421-5f5ef82da422 h1:3UsHvIr4Wc2aW4brOaSCmcxh9ksica6fHEr8P1XhkYw= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250106144421-5f5ef82da422/go.mod h1:3ENsm/5D1mzDyhpzeRi1NR784I0BcofWBoSc5QqqMK4= -google.golang.org/grpc v1.69.2 h1:U3S9QEtbXC0bYNvRtcoklF3xGtLViumSYxWykJS+7AU= -google.golang.org/grpc v1.69.2/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= -google.golang.org/protobuf v1.36.2 h1:R8FeyR1/eLmkutZOM5CWghmo5itiG9z0ktFlTVLuTmU= -google.golang.org/protobuf v1.36.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f h1:gap6+3Gk41EItBuyi4XX/bp4oqJ3UwuIMl25yGinuAA= +google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:Ic02D47M+zbarjYYUlK57y316f2MoN0gjAwI3f2S95o= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50= +google.golang.org/grpc v1.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A= +google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= +google.golang.org/protobuf v1.36.3 h1:82DV7MYdb8anAVi3qge1wSnMDrnKK7ebr+I0hHRN1BU= +google.golang.org/protobuf v1.36.3/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +moul.io/http2curl v1.0.0 h1:6XwpyZOYsgZJrU8exnG87ncVkU1FVCcTRpwzOkTDUi8= +moul.io/http2curl v1.0.0/go.mod h1:f6cULg+e4Md/oW1cYmwW4IWQOVl2lGbmCNGOHvzX2kE= oras.land/oras-go/v2 v2.5.0 h1:o8Me9kLY74Vp5uw07QXPiitjsw7qNXi8Twd+19Zf02c= oras.land/oras-go/v2 v2.5.0/go.mod h1:z4eisnLP530vwIOUOJeBIj0aGI0L1C3d53atvCBqZHg=