Merge pull request 'Phase B: Agent registers routes with mc-proxy on deploy' (#2) from phase-b-route-registration into master

This commit was merged in pull request #2.
This commit is contained in:
2026-03-27 08:36:25 +00:00
25 changed files with 3639 additions and 1 deletions

View File

@@ -299,6 +299,12 @@ chain:
If neither exists (first deploy, no file), the deploy fails with an error If neither exists (first deploy, no file), the deploy fails with an error
telling the operator to create a service definition. telling the operator to create a service definition.
Before pushing to the agent, the CLI checks that each component's image
tag exists in the registry. If a tag is missing and a `[build]` section
is configured, the CLI builds and pushes the image automatically (same
logic as `mcp sync` auto-build, described below). This makes `mcp deploy`
a single command for the bump-build-push-deploy workflow.
The CLI pushes the resolved spec to the agent. The agent records it in its The CLI pushes the resolved spec to the agent. The agent records it in its
registry and executes the deploy. The service definition file on disk is registry and executes the deploy. The service definition file on disk is
**not** modified -- it represents the operator's declared intent, not the **not** modified -- it represents the operator's declared intent, not the
@@ -656,6 +662,29 @@ The agent runs as a dedicated `mcp` system user. Podman runs rootless under
this user. All containers are owned by `mcp`. The NixOS configuration this user. All containers are owned by `mcp`. The NixOS configuration
provisions the `mcp` user with podman access. provisions the `mcp` user with podman access.
#### Runtime Interface
The `runtime.Runtime` interface abstracts the container runtime. The agent
(and the CLI, for build operations) use it for all container operations.
| Method | Used by | Purpose |
|--------|---------|---------|
| `Pull(image)` | Agent | `podman pull <image>` |
| `Run(spec)` | Agent | `podman run -d ...` |
| `Stop(name)` | Agent | `podman stop <name>` |
| `Remove(name)` | Agent | `podman rm <name>` |
| `Inspect(name)` | Agent | `podman inspect <name>` |
| `List()` | Agent | `podman ps -a` |
| `Build(image, contextDir, dockerfile)` | CLI | `podman build -t <image> -f <dockerfile> <contextDir>` |
| `Push(image)` | CLI | `podman push <image>` |
| `ImageExists(image)` | CLI | `podman manifest inspect docker://<image>` (checks remote registry) |
The first six methods are used by the agent during deploy and monitoring.
The last three are used by the CLI during `mcp build` and `mcp deploy`
auto-build. They are on the same interface because the CLI uses the local
podman installation directly -- no gRPC RPC needed, since builds happen
on the operator's workstation, not on the deployment node.
#### Deploy Flow #### Deploy Flow
When the agent receives a `Deploy` RPC: When the agent receives a `Deploy` RPC:
@@ -1223,6 +1252,7 @@ mcp/
│ ├── mcp/ CLI │ ├── mcp/ CLI
│ │ ├── main.go │ │ ├── main.go
│ │ ├── login.go │ │ ├── login.go
│ │ ├── build.go build and push images
│ │ ├── deploy.go │ │ ├── deploy.go
│ │ ├── lifecycle.go stop, start, restart │ │ ├── lifecycle.go stop, start, restart
│ │ ├── status.go list, ps, status │ │ ├── status.go list, ps, status

168
cmd/mcp/build.go Normal file
View File

@@ -0,0 +1,168 @@
package main
import (
"context"
"fmt"
"path/filepath"
"strings"
"github.com/spf13/cobra"
"git.wntrmute.dev/kyle/mcp/internal/config"
"git.wntrmute.dev/kyle/mcp/internal/runtime"
"git.wntrmute.dev/kyle/mcp/internal/servicedef"
)
func buildCmd() *cobra.Command {
return &cobra.Command{
Use: "build <service>[/<image>]",
Short: "Build and push images for a service",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
cfg, err := config.LoadCLIConfig(cfgPath)
if err != nil {
return fmt.Errorf("load config: %w", err)
}
serviceName, imageFilter := parseServiceArg(args[0])
def, err := loadServiceDef(cmd, cfg, serviceName)
if err != nil {
return err
}
rt := &runtime.Podman{}
return buildServiceImages(cmd.Context(), cfg, def, rt, imageFilter)
},
}
}
// buildServiceImages builds and pushes images for a service definition.
// If imageFilter is non-empty, only the matching image is built.
func buildServiceImages(ctx context.Context, cfg *config.CLIConfig, def *servicedef.ServiceDef, rt *runtime.Podman, imageFilter string) error {
if def.Build == nil || len(def.Build.Images) == 0 {
return fmt.Errorf("service %q has no [build.images] configuration", def.Name)
}
if def.Path == "" {
return fmt.Errorf("service %q has no path configured", def.Name)
}
if cfg.Build.Workspace == "" {
return fmt.Errorf("build.workspace is not configured in %s", cfgPath)
}
sourceDir := filepath.Join(cfg.Build.Workspace, def.Path)
for imageName, dockerfile := range def.Build.Images {
if imageFilter != "" && imageName != imageFilter {
continue
}
imageRef := findImageRef(def, imageName)
if imageRef == "" {
return fmt.Errorf("no component references image %q in service %q", imageName, def.Name)
}
fmt.Printf("building %s from %s\n", imageRef, dockerfile)
if err := rt.Build(ctx, imageRef, sourceDir, dockerfile); err != nil {
return fmt.Errorf("build %s: %w", imageRef, err)
}
fmt.Printf("pushing %s\n", imageRef)
if err := rt.Push(ctx, imageRef); err != nil {
return fmt.Errorf("push %s: %w", imageRef, err)
}
}
if imageFilter != "" {
if _, ok := def.Build.Images[imageFilter]; !ok {
return fmt.Errorf("image %q not found in [build.images] for service %q", imageFilter, def.Name)
}
}
return nil
}
// findImageRef finds the full image reference for a build image name by
// matching it against component image fields. The image name from
// [build.images] matches the repository name in the component's image
// reference (the path segment after the last slash, before the tag).
func findImageRef(def *servicedef.ServiceDef, imageName string) string {
for _, c := range def.Components {
repoName := extractRepoName(c.Image)
if repoName == imageName {
return c.Image
}
}
return ""
}
// extractRepoName returns the repository name from an image reference.
// Examples:
//
// "mcr.svc.mcp.metacircular.net:8443/mcr:v1.1.0" -> "mcr"
// "mcr.svc.mcp.metacircular.net:8443/mcr-web:v1.2.0" -> "mcr-web"
// "mcr-web:v1.2.0" -> "mcr-web"
// "mcr-web" -> "mcr-web"
func extractRepoName(image string) string {
// Strip registry prefix (everything up to and including the last slash).
name := image
if i := strings.LastIndex(image, "/"); i >= 0 {
name = image[i+1:]
}
// Strip tag.
if i := strings.LastIndex(name, ":"); i >= 0 {
name = name[:i]
}
return name
}
// ensureImages checks that all component images exist in the registry.
// If an image is missing and the service has build configuration, it
// builds and pushes the image. Returns nil if all images are available.
func ensureImages(ctx context.Context, cfg *config.CLIConfig, def *servicedef.ServiceDef, rt *runtime.Podman, component string) error {
if def.Build == nil || len(def.Build.Images) == 0 {
return nil // no build config, skip auto-build
}
for _, c := range def.Components {
if component != "" && c.Name != component {
continue
}
repoName := extractRepoName(c.Image)
dockerfile, ok := def.Build.Images[repoName]
if !ok {
continue // no Dockerfile for this image, skip
}
exists, err := rt.ImageExists(ctx, c.Image)
if err != nil {
return fmt.Errorf("check image %s: %w", c.Image, err)
}
if exists {
continue
}
// Image missing — build and push.
if def.Path == "" {
return fmt.Errorf("image %s not found in registry and service %q has no path configured", c.Image, def.Name)
}
if cfg.Build.Workspace == "" {
return fmt.Errorf("image %s not found in registry and build.workspace is not configured", c.Image)
}
sourceDir := filepath.Join(cfg.Build.Workspace, def.Path)
fmt.Printf("image %s not found, building from %s\n", c.Image, dockerfile)
if err := rt.Build(ctx, c.Image, sourceDir, dockerfile); err != nil {
return fmt.Errorf("auto-build %s: %w", c.Image, err)
}
fmt.Printf("pushing %s\n", c.Image)
if err := rt.Push(ctx, c.Image); err != nil {
return fmt.Errorf("auto-push %s: %w", c.Image, err)
}
}
return nil
}

View File

@@ -10,6 +10,7 @@ import (
mcpv1 "git.wntrmute.dev/kyle/mcp/gen/mcp/v1" mcpv1 "git.wntrmute.dev/kyle/mcp/gen/mcp/v1"
"git.wntrmute.dev/kyle/mcp/internal/config" "git.wntrmute.dev/kyle/mcp/internal/config"
"git.wntrmute.dev/kyle/mcp/internal/runtime"
"git.wntrmute.dev/kyle/mcp/internal/servicedef" "git.wntrmute.dev/kyle/mcp/internal/servicedef"
) )
@@ -31,6 +32,12 @@ func deployCmd() *cobra.Command {
return err return err
} }
// Auto-build missing images if the service has build config.
rt := &runtime.Podman{}
if err := ensureImages(cmd.Context(), cfg, def, rt, component); err != nil {
return err
}
spec := servicedef.ToProto(def) spec := servicedef.ToProto(def)
address, err := findNodeAddress(cfg, def.Node) address, err := findNodeAddress(cfg, def.Node)

View File

@@ -34,6 +34,7 @@ func main() {
}) })
root.AddCommand(loginCmd()) root.AddCommand(loginCmd())
root.AddCommand(buildCmd())
root.AddCommand(deployCmd()) root.AddCommand(deployCmd())
root.AddCommand(stopCmd()) root.AddCommand(stopCmd())
root.AddCommand(startCmd()) root.AddCommand(startCmd())

1
go.mod
View File

@@ -3,6 +3,7 @@ module git.wntrmute.dev/kyle/mcp
go 1.25.7 go 1.25.7
require ( require (
git.wntrmute.dev/kyle/mc-proxy v1.0.0
github.com/pelletier/go-toml/v2 v2.3.0 github.com/pelletier/go-toml/v2 v2.3.0
github.com/spf13/cobra v1.10.2 github.com/spf13/cobra v1.10.2
golang.org/x/sys v0.42.0 golang.org/x/sys v0.42.0

20
go.sum
View File

@@ -1,3 +1,9 @@
git.wntrmute.dev/kyle/mc-proxy v1.0.0 h1:wrBocnDCNQreVnVzxFsQtSz+R3NKxeZaKrq62J+yl+g=
git.wntrmute.dev/kyle/mc-proxy v1.0.0/go.mod h1:aGAGeD9b38ztOKEUeM3VcSD4+QLqAJ6+zK6h+9B8ebU=
git.wntrmute.dev/kyle/mcdsl v1.0.0 h1:YB7dx4gdNYKKcVySpL6UkwHqdCJ9Nl1yS0+eHk0hNtk=
git.wntrmute.dev/kyle/mcdsl v1.0.0/go.mod h1:wo0tGfUAxci3XnOe4/rFmR0RjUElKdYUazc+Np986sg=
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/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= 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/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
@@ -21,10 +27,22 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 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-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
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/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w= github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE=
github.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8=
github.com/pelletier/go-toml/v2 v2.3.0 h1:k59bC/lIZREW0/iVaQR8nDHxVq8OVlIzYCOJf421CaM= github.com/pelletier/go-toml/v2 v2.3.0 h1:k59bC/lIZREW0/iVaQR8nDHxVq8OVlIzYCOJf421CaM=
github.com/pelletier/go-toml/v2 v2.3.0/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/pelletier/go-toml/v2 v2.3.0/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=
github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
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 h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@@ -44,6 +62,8 @@ go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2W
go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=

View File

@@ -32,6 +32,7 @@ type Agent struct {
Monitor *monitor.Monitor Monitor *monitor.Monitor
Logger *slog.Logger Logger *slog.Logger
PortAlloc *PortAllocator PortAlloc *PortAllocator
Proxy *ProxyRouter
} }
// Run starts the agent: opens the database, sets up the gRPC server with // Run starts the agent: opens the database, sets up the gRPC server with
@@ -51,6 +52,11 @@ func Run(cfg *config.AgentConfig) error {
mon := monitor.New(db, rt, cfg.Monitor, cfg.Agent.NodeName, logger) mon := monitor.New(db, rt, cfg.Monitor, cfg.Agent.NodeName, logger)
proxy, err := NewProxyRouter(cfg.MCProxy.Socket, cfg.MCProxy.CertDir, logger)
if err != nil {
return fmt.Errorf("connect to mc-proxy: %w", err)
}
a := &Agent{ a := &Agent{
Config: cfg, Config: cfg,
DB: db, DB: db,
@@ -58,6 +64,7 @@ func Run(cfg *config.AgentConfig) error {
Monitor: mon, Monitor: mon,
Logger: logger, Logger: logger,
PortAlloc: NewPortAllocator(), PortAlloc: NewPortAllocator(),
Proxy: proxy,
} }
tlsCert, err := tls.LoadX509KeyPair(cfg.Server.TLSCert, cfg.Server.TLSKey) tlsCert, err := tls.LoadX509KeyPair(cfg.Server.TLSCert, cfg.Server.TLSKey)
@@ -108,6 +115,7 @@ func Run(cfg *config.AgentConfig) error {
logger.Info("shutting down") logger.Info("shutting down")
mon.Stop() mon.Stop()
server.GracefulStop() server.GracefulStop()
_ = proxy.Close()
return nil return nil
case err := <-errCh: case err := <-errCh:
mon.Stop() mon.Stop()

View File

@@ -146,6 +146,16 @@ func (a *Agent) deployComponent(ctx context.Context, serviceName string, cs *mcp
} }
} }
// Register routes with mc-proxy after the container is running.
if len(regRoutes) > 0 && a.Proxy != nil {
hostPorts, err := registry.GetRouteHostPorts(a.DB, serviceName, compName)
if err != nil {
a.Logger.Warn("failed to get host ports for route registration", "service", serviceName, "component", compName, "err", err)
} else if err := a.Proxy.RegisterRoutes(ctx, serviceName, regRoutes, hostPorts); err != nil {
a.Logger.Warn("failed to register routes with mc-proxy", "service", serviceName, "component", compName, "err", err)
}
}
if err := registry.UpdateComponentState(a.DB, serviceName, compName, "running", "running"); err != nil { if err := registry.UpdateComponentState(a.DB, serviceName, compName, "running", "running"); err != nil {
a.Logger.Warn("failed to update component state", "service", serviceName, "component", compName, "err", err) a.Logger.Warn("failed to update component state", "service", serviceName, "component", compName, "err", err)
} }

View File

@@ -30,6 +30,13 @@ func (a *Agent) StopService(ctx context.Context, req *mcpv1.StopServiceRequest)
containerName := ContainerNameFor(req.GetName(), c.Name) containerName := ContainerNameFor(req.GetName(), c.Name)
r := &mcpv1.ComponentResult{Name: c.Name, Success: true} r := &mcpv1.ComponentResult{Name: c.Name, Success: true}
// Remove routes from mc-proxy before stopping the container.
if len(c.Routes) > 0 && a.Proxy != nil {
if err := a.Proxy.RemoveRoutes(ctx, req.GetName(), c.Routes); err != nil {
a.Logger.Warn("failed to remove routes", "service", req.GetName(), "component", c.Name, "err", err)
}
}
if err := a.Runtime.Stop(ctx, containerName); err != nil { if err := a.Runtime.Stop(ctx, containerName); err != nil {
a.Logger.Info("stop container (ignored)", "container", containerName, "error", err) a.Logger.Info("stop container (ignored)", "container", containerName, "error", err)
} }

138
internal/agent/proxy.go Normal file
View File

@@ -0,0 +1,138 @@
package agent
import (
"context"
"fmt"
"log/slog"
"path/filepath"
"git.wntrmute.dev/kyle/mc-proxy/client/mcproxy"
"git.wntrmute.dev/kyle/mcp/internal/registry"
)
// ProxyRouter registers and removes routes with mc-proxy.
// If the mc-proxy socket is not configured, it logs and returns nil
// (route registration is optional).
type ProxyRouter struct {
client *mcproxy.Client
certDir string
logger *slog.Logger
}
// NewProxyRouter connects to mc-proxy via Unix socket. Returns nil
// if socketPath is empty (route registration disabled).
func NewProxyRouter(socketPath, certDir string, logger *slog.Logger) (*ProxyRouter, error) {
if socketPath == "" {
logger.Info("mc-proxy socket not configured, route registration disabled")
return nil, nil
}
client, err := mcproxy.Dial(socketPath)
if err != nil {
return nil, fmt.Errorf("connect to mc-proxy at %s: %w", socketPath, err)
}
logger.Info("connected to mc-proxy", "socket", socketPath)
return &ProxyRouter{
client: client,
certDir: certDir,
logger: logger,
}, nil
}
// Close closes the mc-proxy connection.
func (p *ProxyRouter) Close() error {
if p == nil || p.client == nil {
return nil
}
return p.client.Close()
}
// RegisterRoutes registers all routes for a service component with mc-proxy.
// It uses the assigned host ports from the registry.
func (p *ProxyRouter) RegisterRoutes(ctx context.Context, serviceName string, routes []registry.Route, hostPorts map[string]int) error {
if p == nil {
return nil
}
for _, r := range routes {
hostPort, ok := hostPorts[r.Name]
if !ok || hostPort == 0 {
continue
}
hostname := r.Hostname
if hostname == "" {
hostname = serviceName + ".svc.mcp.metacircular.net"
}
listenerAddr := listenerForMode(r.Mode, r.Port)
backend := fmt.Sprintf("127.0.0.1:%d", hostPort)
route := mcproxy.Route{
Hostname: hostname,
Backend: backend,
Mode: r.Mode,
BackendTLS: r.Mode == "l4", // L4 passthrough: backend handles TLS. L7: mc-proxy terminates.
}
// L7 routes need TLS cert/key for mc-proxy to terminate TLS.
if r.Mode == "l7" {
route.TLSCert = filepath.Join(p.certDir, serviceName+".pem")
route.TLSKey = filepath.Join(p.certDir, serviceName+".key")
}
p.logger.Info("registering route",
"service", serviceName,
"hostname", hostname,
"listener", listenerAddr,
"backend", backend,
"mode", r.Mode,
)
if err := p.client.AddRoute(ctx, listenerAddr, route); err != nil {
return fmt.Errorf("register route %s on %s: %w", hostname, listenerAddr, err)
}
}
return nil
}
// RemoveRoutes removes all routes for a service component from mc-proxy.
func (p *ProxyRouter) RemoveRoutes(ctx context.Context, serviceName string, routes []registry.Route) error {
if p == nil {
return nil
}
for _, r := range routes {
hostname := r.Hostname
if hostname == "" {
hostname = serviceName + ".svc.mcp.metacircular.net"
}
listenerAddr := listenerForMode(r.Mode, r.Port)
p.logger.Info("removing route",
"service", serviceName,
"hostname", hostname,
"listener", listenerAddr,
)
if err := p.client.RemoveRoute(ctx, listenerAddr, hostname); err != nil {
// Log but don't fail — the route may already be gone.
p.logger.Warn("failed to remove route",
"hostname", hostname,
"listener", listenerAddr,
"err", err,
)
}
}
return nil
}
// listenerForMode returns the mc-proxy listener address for a given
// route mode and external port.
func listenerForMode(mode string, port int) string {
return fmt.Sprintf(":%d", port)
}

View File

@@ -0,0 +1,57 @@
package agent
import (
"testing"
"git.wntrmute.dev/kyle/mcp/internal/registry"
)
func TestListenerForMode(t *testing.T) {
tests := []struct {
mode string
port int
want string
}{
{"l4", 8443, ":8443"},
{"l7", 443, ":443"},
{"l4", 9443, ":9443"},
}
for _, tt := range tests {
got := listenerForMode(tt.mode, tt.port)
if got != tt.want {
t.Errorf("listenerForMode(%q, %d) = %q, want %q", tt.mode, tt.port, got, tt.want)
}
}
}
func TestNilProxyRouterIsNoop(t *testing.T) {
var p *ProxyRouter
// All methods should return nil on a nil ProxyRouter.
if err := p.RegisterRoutes(nil, "svc", nil, nil); err != nil {
t.Errorf("RegisterRoutes on nil: %v", err)
}
if err := p.RemoveRoutes(nil, "svc", nil); err != nil {
t.Errorf("RemoveRoutes on nil: %v", err)
}
if err := p.Close(); err != nil {
t.Errorf("Close on nil: %v", err)
}
}
func TestRegisterRoutesSkipsZeroHostPort(t *testing.T) {
// A nil ProxyRouter should be a no-op, so this tests the skip logic
// indirectly. With a nil proxy, RegisterRoutes returns nil even
// with routes that have zero host ports.
var p *ProxyRouter
routes := []registry.Route{
{Name: "rest", Port: 8443, Mode: "l4"},
}
hostPorts := map[string]int{"rest": 0}
if err := p.RegisterRoutes(nil, "svc", routes, hostPorts); err != nil {
t.Errorf("RegisterRoutes: %v", err)
}
}

View File

@@ -22,6 +22,10 @@ func (f *fakeRuntime) Pull(_ context.Context, _ string) error { re
func (f *fakeRuntime) Run(_ context.Context, _ runtime.ContainerSpec) error { return nil } func (f *fakeRuntime) Run(_ context.Context, _ runtime.ContainerSpec) error { return nil }
func (f *fakeRuntime) Stop(_ context.Context, _ string) error { return nil } func (f *fakeRuntime) Stop(_ context.Context, _ string) error { return nil }
func (f *fakeRuntime) Remove(_ context.Context, _ string) error { return nil } func (f *fakeRuntime) Remove(_ context.Context, _ string) error { return nil }
func (f *fakeRuntime) Build(_ context.Context, _, _, _ string) error { return nil }
func (f *fakeRuntime) Push(_ context.Context, _ string) error { return nil }
func (f *fakeRuntime) ImageExists(_ context.Context, _ string) (bool, error) { return true, nil }
func (f *fakeRuntime) List(_ context.Context) ([]runtime.ContainerInfo, error) { func (f *fakeRuntime) List(_ context.Context) ([]runtime.ContainerInfo, error) {
return f.containers, f.listErr return f.containers, f.listErr

View File

@@ -14,10 +14,23 @@ type AgentConfig struct {
Database DatabaseConfig `toml:"database"` Database DatabaseConfig `toml:"database"`
MCIAS MCIASConfig `toml:"mcias"` MCIAS MCIASConfig `toml:"mcias"`
Agent AgentSettings `toml:"agent"` Agent AgentSettings `toml:"agent"`
MCProxy MCProxyConfig `toml:"mcproxy"`
Monitor MonitorConfig `toml:"monitor"` Monitor MonitorConfig `toml:"monitor"`
Log LogConfig `toml:"log"` Log LogConfig `toml:"log"`
} }
// MCProxyConfig holds the mc-proxy connection settings.
type MCProxyConfig struct {
// Socket is the path to the mc-proxy gRPC admin API Unix socket.
// If empty, route registration is disabled.
Socket string `toml:"socket"`
// CertDir is the directory containing TLS certificates for routes.
// Convention: <service>.pem and <service>.key per service.
// Defaults to /srv/mc-proxy/certs.
CertDir string `toml:"cert_dir"`
}
// ServerConfig holds gRPC server listen address and TLS paths. // ServerConfig holds gRPC server listen address and TLS paths.
type ServerConfig struct { type ServerConfig struct {
GRPCAddr string `toml:"grpc_addr"` GRPCAddr string `toml:"grpc_addr"`
@@ -134,6 +147,9 @@ func applyAgentDefaults(cfg *AgentConfig) {
if cfg.Agent.ContainerRuntime == "" { if cfg.Agent.ContainerRuntime == "" {
cfg.Agent.ContainerRuntime = "podman" cfg.Agent.ContainerRuntime = "podman"
} }
if cfg.MCProxy.CertDir == "" {
cfg.MCProxy.CertDir = "/srv/mc-proxy/certs"
}
} }
func applyAgentEnvOverrides(cfg *AgentConfig) { func applyAgentEnvOverrides(cfg *AgentConfig) {
@@ -158,6 +174,12 @@ func applyAgentEnvOverrides(cfg *AgentConfig) {
if v := os.Getenv("MCP_AGENT_LOG_LEVEL"); v != "" { if v := os.Getenv("MCP_AGENT_LOG_LEVEL"); v != "" {
cfg.Log.Level = v cfg.Log.Level = v
} }
if v := os.Getenv("MCP_AGENT_MCPROXY_SOCKET"); v != "" {
cfg.MCProxy.Socket = v
}
if v := os.Getenv("MCP_AGENT_MCPROXY_CERT_DIR"); v != "" {
cfg.MCProxy.CertDir = v
}
} }
func validateAgentConfig(cfg *AgentConfig) error { func validateAgentConfig(cfg *AgentConfig) error {

View File

@@ -3,6 +3,7 @@ package config
import ( import (
"fmt" "fmt"
"os" "os"
"strings"
toml "github.com/pelletier/go-toml/v2" toml "github.com/pelletier/go-toml/v2"
) )
@@ -10,11 +11,17 @@ import (
// CLIConfig is the configuration for the mcp CLI binary. // CLIConfig is the configuration for the mcp CLI binary.
type CLIConfig struct { type CLIConfig struct {
Services ServicesConfig `toml:"services"` Services ServicesConfig `toml:"services"`
Build BuildConfig `toml:"build"`
MCIAS MCIASConfig `toml:"mcias"` MCIAS MCIASConfig `toml:"mcias"`
Auth AuthConfig `toml:"auth"` Auth AuthConfig `toml:"auth"`
Nodes []NodeConfig `toml:"nodes"` Nodes []NodeConfig `toml:"nodes"`
} }
// BuildConfig holds settings for building container images.
type BuildConfig struct {
Workspace string `toml:"workspace"`
}
// ServicesConfig defines where service definition files live. // ServicesConfig defines where service definition files live.
type ServicesConfig struct { type ServicesConfig struct {
Dir string `toml:"dir"` Dir string `toml:"dir"`
@@ -66,6 +73,9 @@ func applyCLIEnvOverrides(cfg *CLIConfig) {
if v := os.Getenv("MCP_SERVICES_DIR"); v != "" { if v := os.Getenv("MCP_SERVICES_DIR"); v != "" {
cfg.Services.Dir = v cfg.Services.Dir = v
} }
if v := os.Getenv("MCP_BUILD_WORKSPACE"); v != "" {
cfg.Build.Workspace = v
}
if v := os.Getenv("MCP_MCIAS_SERVER_URL"); v != "" { if v := os.Getenv("MCP_MCIAS_SERVER_URL"); v != "" {
cfg.MCIAS.ServerURL = v cfg.MCIAS.ServerURL = v
} }
@@ -93,5 +103,15 @@ func validateCLIConfig(cfg *CLIConfig) error {
if cfg.Auth.TokenPath == "" { if cfg.Auth.TokenPath == "" {
return fmt.Errorf("auth.token_path is required") return fmt.Errorf("auth.token_path is required")
} }
// Expand ~ in workspace path.
if strings.HasPrefix(cfg.Build.Workspace, "~/") {
home, err := os.UserHomeDir()
if err != nil {
return fmt.Errorf("expand workspace path: %w", err)
}
cfg.Build.Workspace = home + cfg.Build.Workspace[1:]
}
return nil return nil
} }

View File

@@ -47,6 +47,10 @@ func (f *fakeRuntime) Pull(_ context.Context, _ string) error { re
func (f *fakeRuntime) Run(_ context.Context, _ runtime.ContainerSpec) error { return nil } func (f *fakeRuntime) Run(_ context.Context, _ runtime.ContainerSpec) error { return nil }
func (f *fakeRuntime) Stop(_ context.Context, _ string) error { return nil } func (f *fakeRuntime) Stop(_ context.Context, _ string) error { return nil }
func (f *fakeRuntime) Remove(_ context.Context, _ string) error { return nil } func (f *fakeRuntime) Remove(_ context.Context, _ string) error { return nil }
func (f *fakeRuntime) Build(_ context.Context, _, _, _ string) error { return nil }
func (f *fakeRuntime) Push(_ context.Context, _ string) error { return nil }
func (f *fakeRuntime) ImageExists(_ context.Context, _ string) (bool, error) { return true, nil }
func (f *fakeRuntime) Inspect(_ context.Context, _ string) (runtime.ContainerInfo, error) { func (f *fakeRuntime) Inspect(_ context.Context, _ string) (runtime.ContainerInfo, error) {
return runtime.ContainerInfo{}, nil return runtime.ContainerInfo{}, nil

View File

@@ -3,6 +3,7 @@ package runtime
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"os/exec" "os/exec"
"strings" "strings"
@@ -177,6 +178,40 @@ func (p *Podman) Inspect(ctx context.Context, name string) (ContainerInfo, error
return info, nil return info, nil
} }
// Build builds a container image from a Dockerfile.
func (p *Podman) Build(ctx context.Context, image, contextDir, dockerfile string) error {
args := []string{"build", "-t", image, "-f", dockerfile, contextDir}
cmd := exec.CommandContext(ctx, p.command(), args...) //nolint:gosec // args built programmatically
cmd.Dir = contextDir
if out, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf("podman build %q: %w: %s", image, err, out)
}
return nil
}
// Push pushes a container image to a remote registry.
func (p *Podman) Push(ctx context.Context, image string) error {
cmd := exec.CommandContext(ctx, p.command(), "push", image) //nolint:gosec // args built programmatically
if out, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf("podman push %q: %w: %s", image, err, out)
}
return nil
}
// ImageExists checks whether an image tag exists in a remote registry.
func (p *Podman) ImageExists(ctx context.Context, image string) (bool, error) {
cmd := exec.CommandContext(ctx, p.command(), "manifest", "inspect", "docker://"+image) //nolint:gosec // args built programmatically
if err := cmd.Run(); err != nil {
// Exit code 1 means the manifest was not found.
var exitErr *exec.ExitError
if ok := errors.As(err, &exitErr); ok && exitErr.ExitCode() == 1 {
return false, nil
}
return false, fmt.Errorf("podman manifest inspect %q: %w", image, err)
}
return true, nil
}
// podmanPSEntry is a single entry from podman ps --format json. // podmanPSEntry is a single entry from podman ps --format json.
type podmanPSEntry struct { type podmanPSEntry struct {
Names []string `json:"Names"` Names []string `json:"Names"`

View File

@@ -34,7 +34,9 @@ type ContainerInfo struct {
Started time.Time // when the container started (zero if not running) Started time.Time // when the container started (zero if not running)
} }
// Runtime is the container runtime abstraction. // Runtime is the container runtime abstraction. The first six methods are
// used by the agent for container lifecycle. The last three are used by the
// CLI for building and pushing images.
type Runtime interface { type Runtime interface {
Pull(ctx context.Context, image string) error Pull(ctx context.Context, image string) error
Run(ctx context.Context, spec ContainerSpec) error Run(ctx context.Context, spec ContainerSpec) error
@@ -42,6 +44,10 @@ type Runtime interface {
Remove(ctx context.Context, name string) error Remove(ctx context.Context, name string) error
Inspect(ctx context.Context, name string) (ContainerInfo, error) Inspect(ctx context.Context, name string) (ContainerInfo, error)
List(ctx context.Context) ([]ContainerInfo, error) List(ctx context.Context) ([]ContainerInfo, error)
Build(ctx context.Context, image, contextDir, dockerfile string) error
Push(ctx context.Context, image string) error
ImageExists(ctx context.Context, image string) (bool, error)
} }
// ExtractVersion parses the tag from an image reference. // ExtractVersion parses the tag from an image reference.

View File

@@ -18,9 +18,17 @@ type ServiceDef struct {
Name string `toml:"name"` Name string `toml:"name"`
Node string `toml:"node"` Node string `toml:"node"`
Active *bool `toml:"active,omitempty"` Active *bool `toml:"active,omitempty"`
Path string `toml:"path,omitempty"`
Build *BuildDef `toml:"build,omitempty"`
Components []ComponentDef `toml:"components"` Components []ComponentDef `toml:"components"`
} }
// BuildDef describes how to build container images for a service.
type BuildDef struct {
Images map[string]string `toml:"images"`
UsesMCDSL bool `toml:"uses_mcdsl,omitempty"`
}
// RouteDef describes a route for a component, used for automatic port // RouteDef describes a route for a component, used for automatic port
// allocation and mc-proxy integration. // allocation and mc-proxy integration.
type RouteDef struct { type RouteDef struct {

View File

@@ -0,0 +1,331 @@
// Package mcproxy provides a client for the mc-proxy gRPC admin API.
package mcproxy
import (
"context"
"fmt"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
healthpb "google.golang.org/grpc/health/grpc_health_v1"
pb "git.wntrmute.dev/kyle/mc-proxy/gen/mc_proxy/v1"
)
// Client provides access to the mc-proxy admin API.
type Client struct {
conn *grpc.ClientConn
admin pb.ProxyAdminServiceClient
health healthpb.HealthClient
}
// Dial connects to the mc-proxy admin API via Unix socket.
func Dial(socketPath string) (*Client, error) {
conn, err := grpc.NewClient("unix://"+socketPath,
grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
return nil, fmt.Errorf("connecting to %s: %w", socketPath, err)
}
return &Client{
conn: conn,
admin: pb.NewProxyAdminServiceClient(conn),
health: healthpb.NewHealthClient(conn),
}, nil
}
// Close closes the connection to the server.
func (c *Client) Close() error {
return c.conn.Close()
}
// L7Policy represents an HTTP-level blocking policy.
type L7Policy struct {
Type string // "block_user_agent" or "require_header"
Value string
}
// Route represents a hostname to backend mapping with mode and options.
type Route struct {
Hostname string
Backend string
Mode string // "l4" or "l7"
TLSCert string
TLSKey string
BackendTLS bool
SendProxyProtocol bool
L7Policies []L7Policy
}
// ListRoutes returns all routes for the given listener address.
func (c *Client) ListRoutes(ctx context.Context, listenerAddr string) ([]Route, error) {
resp, err := c.admin.ListRoutes(ctx, &pb.ListRoutesRequest{
ListenerAddr: listenerAddr,
})
if err != nil {
return nil, err
}
routes := make([]Route, len(resp.Routes))
for i, r := range resp.Routes {
routes[i] = Route{
Hostname: r.Hostname,
Backend: r.Backend,
Mode: r.Mode,
TLSCert: r.TlsCert,
TLSKey: r.TlsKey,
BackendTLS: r.BackendTls,
SendProxyProtocol: r.SendProxyProtocol,
}
}
return routes, nil
}
// AddRoute adds a route to the given listener.
func (c *Client) AddRoute(ctx context.Context, listenerAddr string, route Route) error {
_, err := c.admin.AddRoute(ctx, &pb.AddRouteRequest{
ListenerAddr: listenerAddr,
Route: &pb.Route{
Hostname: route.Hostname,
Backend: route.Backend,
Mode: route.Mode,
TlsCert: route.TLSCert,
TlsKey: route.TLSKey,
BackendTls: route.BackendTLS,
SendProxyProtocol: route.SendProxyProtocol,
},
})
return err
}
// RemoveRoute removes a route from the given listener.
func (c *Client) RemoveRoute(ctx context.Context, listenerAddr, hostname string) error {
_, err := c.admin.RemoveRoute(ctx, &pb.RemoveRouteRequest{
ListenerAddr: listenerAddr,
Hostname: hostname,
})
return err
}
// FirewallRuleType represents the type of firewall rule.
type FirewallRuleType string
const (
FirewallRuleIP FirewallRuleType = "ip"
FirewallRuleCIDR FirewallRuleType = "cidr"
FirewallRuleCountry FirewallRuleType = "country"
)
// FirewallRule represents a firewall block rule.
type FirewallRule struct {
Type FirewallRuleType
Value string
}
// GetFirewallRules returns all firewall rules.
func (c *Client) GetFirewallRules(ctx context.Context) ([]FirewallRule, error) {
resp, err := c.admin.GetFirewallRules(ctx, &pb.GetFirewallRulesRequest{})
if err != nil {
return nil, err
}
rules := make([]FirewallRule, len(resp.Rules))
for i, r := range resp.Rules {
rules[i] = FirewallRule{
Type: protoToRuleType(r.Type),
Value: r.Value,
}
}
return rules, nil
}
// AddFirewallRule adds a firewall rule.
func (c *Client) AddFirewallRule(ctx context.Context, ruleType FirewallRuleType, value string) error {
_, err := c.admin.AddFirewallRule(ctx, &pb.AddFirewallRuleRequest{
Rule: &pb.FirewallRule{
Type: ruleTypeToProto(ruleType),
Value: value,
},
})
return err
}
// RemoveFirewallRule removes a firewall rule.
func (c *Client) RemoveFirewallRule(ctx context.Context, ruleType FirewallRuleType, value string) error {
_, err := c.admin.RemoveFirewallRule(ctx, &pb.RemoveFirewallRuleRequest{
Rule: &pb.FirewallRule{
Type: ruleTypeToProto(ruleType),
Value: value,
},
})
return err
}
// RouteStatus contains status information for a single route.
type RouteStatus struct {
Hostname string
Backend string
Mode string // "l4" or "l7"
BackendTLS bool
SendProxyProtocol bool
}
// ListenerStatus contains status information for a single listener.
type ListenerStatus struct {
Addr string
RouteCount int
ActiveConnections int64
ProxyProtocol bool
MaxConnections int64
Routes []RouteStatus
}
// Status contains the server's current status.
type Status struct {
Version string
StartedAt time.Time
TotalConnections int64
Listeners []ListenerStatus
}
// GetStatus returns the server's current status.
func (c *Client) GetStatus(ctx context.Context) (*Status, error) {
resp, err := c.admin.GetStatus(ctx, &pb.GetStatusRequest{})
if err != nil {
return nil, err
}
status := &Status{
Version: resp.Version,
TotalConnections: resp.TotalConnections,
}
if resp.StartedAt != nil {
status.StartedAt = resp.StartedAt.AsTime()
}
status.Listeners = make([]ListenerStatus, len(resp.Listeners))
for i, ls := range resp.Listeners {
routes := make([]RouteStatus, len(ls.Routes))
for j, r := range ls.Routes {
routes[j] = RouteStatus{
Hostname: r.Hostname,
Backend: r.Backend,
Mode: r.Mode,
BackendTLS: r.BackendTls,
SendProxyProtocol: r.SendProxyProtocol,
}
}
status.Listeners[i] = ListenerStatus{
Addr: ls.Addr,
RouteCount: int(ls.RouteCount),
ActiveConnections: ls.ActiveConnections,
ProxyProtocol: ls.ProxyProtocol,
MaxConnections: ls.MaxConnections,
Routes: routes,
}
}
return status, nil
}
// SetListenerMaxConnections updates the per-listener connection limit.
// 0 means unlimited.
func (c *Client) SetListenerMaxConnections(ctx context.Context, listenerAddr string, maxConns int64) error {
_, err := c.admin.SetListenerMaxConnections(ctx, &pb.SetListenerMaxConnectionsRequest{
ListenerAddr: listenerAddr,
MaxConnections: maxConns,
})
return err
}
// ListL7Policies returns L7 policies for a route.
func (c *Client) ListL7Policies(ctx context.Context, listenerAddr, hostname string) ([]L7Policy, error) {
resp, err := c.admin.ListL7Policies(ctx, &pb.ListL7PoliciesRequest{
ListenerAddr: listenerAddr,
Hostname: hostname,
})
if err != nil {
return nil, err
}
policies := make([]L7Policy, len(resp.Policies))
for i, p := range resp.Policies {
policies[i] = L7Policy{Type: p.Type, Value: p.Value}
}
return policies, nil
}
// AddL7Policy adds an L7 policy to a route.
func (c *Client) AddL7Policy(ctx context.Context, listenerAddr, hostname string, policy L7Policy) error {
_, err := c.admin.AddL7Policy(ctx, &pb.AddL7PolicyRequest{
ListenerAddr: listenerAddr,
Hostname: hostname,
Policy: &pb.L7Policy{Type: policy.Type, Value: policy.Value},
})
return err
}
// RemoveL7Policy removes an L7 policy from a route.
func (c *Client) RemoveL7Policy(ctx context.Context, listenerAddr, hostname string, policy L7Policy) error {
_, err := c.admin.RemoveL7Policy(ctx, &pb.RemoveL7PolicyRequest{
ListenerAddr: listenerAddr,
Hostname: hostname,
Policy: &pb.L7Policy{Type: policy.Type, Value: policy.Value},
})
return err
}
// HealthStatus represents the health of the server.
type HealthStatus int
const (
HealthUnknown HealthStatus = 0
HealthServing HealthStatus = 1
HealthNotServing HealthStatus = 2
)
func (h HealthStatus) String() string {
switch h {
case HealthServing:
return "SERVING"
case HealthNotServing:
return "NOT_SERVING"
default:
return "UNKNOWN"
}
}
// CheckHealth checks the health of the server.
func (c *Client) CheckHealth(ctx context.Context) (HealthStatus, error) {
resp, err := c.health.Check(ctx, &healthpb.HealthCheckRequest{})
if err != nil {
return HealthUnknown, err
}
return HealthStatus(resp.Status), nil
}
func protoToRuleType(t pb.FirewallRuleType) FirewallRuleType {
switch t {
case pb.FirewallRuleType_FIREWALL_RULE_TYPE_IP:
return FirewallRuleIP
case pb.FirewallRuleType_FIREWALL_RULE_TYPE_CIDR:
return FirewallRuleCIDR
case pb.FirewallRuleType_FIREWALL_RULE_TYPE_COUNTRY:
return FirewallRuleCountry
default:
return ""
}
}
func ruleTypeToProto(t FirewallRuleType) pb.FirewallRuleType {
switch t {
case FirewallRuleIP:
return pb.FirewallRuleType_FIREWALL_RULE_TYPE_IP
case FirewallRuleCIDR:
return pb.FirewallRuleType_FIREWALL_RULE_TYPE_CIDR
case FirewallRuleCountry:
return pb.FirewallRuleType_FIREWALL_RULE_TYPE_COUNTRY
default:
return pb.FirewallRuleType_FIREWALL_RULE_TYPE_UNSPECIFIED
}
}

View File

@@ -0,0 +1,41 @@
// Package mcproxy provides a Go client for the mc-proxy gRPC admin API.
//
// The client connects to mc-proxy via Unix socket and provides methods
// for managing routes, firewall rules, and querying server status.
//
// # Basic Usage
//
// client, err := mcproxy.Dial("/srv/mc-proxy/mc-proxy.sock")
// if err != nil {
// log.Fatal(err)
// }
// defer client.Close()
//
// // Get server status
// status, err := client.GetStatus(ctx)
// if err != nil {
// log.Fatal(err)
// }
// fmt.Printf("mc-proxy %s, %d connections\n", status.Version, status.TotalConnections)
//
// // List routes for a listener
// routes, err := client.ListRoutes(ctx, ":443")
// if err != nil {
// log.Fatal(err)
// }
// for _, r := range routes {
// fmt.Printf(" %s -> %s\n", r.Hostname, r.Backend)
// }
//
// // Add a route
// err = client.AddRoute(ctx, ":443", "example.com", "127.0.0.1:8443")
//
// // Add a firewall rule
// err = client.AddFirewallRule(ctx, mcproxy.FirewallRuleCIDR, "10.0.0.0/8")
//
// // Check health
// health, err := client.CheckHealth(ctx)
// if health == mcproxy.HealthServing {
// fmt.Println("Server is healthy")
// }
package mcproxy

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,511 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.6.1
// - protoc v6.32.1
// source: proto/mc_proxy/v1/admin.proto
package mcproxyv1
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
const (
ProxyAdminService_ListRoutes_FullMethodName = "/mc_proxy.v1.ProxyAdminService/ListRoutes"
ProxyAdminService_AddRoute_FullMethodName = "/mc_proxy.v1.ProxyAdminService/AddRoute"
ProxyAdminService_RemoveRoute_FullMethodName = "/mc_proxy.v1.ProxyAdminService/RemoveRoute"
ProxyAdminService_GetFirewallRules_FullMethodName = "/mc_proxy.v1.ProxyAdminService/GetFirewallRules"
ProxyAdminService_AddFirewallRule_FullMethodName = "/mc_proxy.v1.ProxyAdminService/AddFirewallRule"
ProxyAdminService_RemoveFirewallRule_FullMethodName = "/mc_proxy.v1.ProxyAdminService/RemoveFirewallRule"
ProxyAdminService_SetListenerMaxConnections_FullMethodName = "/mc_proxy.v1.ProxyAdminService/SetListenerMaxConnections"
ProxyAdminService_ListL7Policies_FullMethodName = "/mc_proxy.v1.ProxyAdminService/ListL7Policies"
ProxyAdminService_AddL7Policy_FullMethodName = "/mc_proxy.v1.ProxyAdminService/AddL7Policy"
ProxyAdminService_RemoveL7Policy_FullMethodName = "/mc_proxy.v1.ProxyAdminService/RemoveL7Policy"
ProxyAdminService_GetStatus_FullMethodName = "/mc_proxy.v1.ProxyAdminService/GetStatus"
)
// ProxyAdminServiceClient is the client API for ProxyAdminService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type ProxyAdminServiceClient interface {
// Routes
ListRoutes(ctx context.Context, in *ListRoutesRequest, opts ...grpc.CallOption) (*ListRoutesResponse, error)
AddRoute(ctx context.Context, in *AddRouteRequest, opts ...grpc.CallOption) (*AddRouteResponse, error)
RemoveRoute(ctx context.Context, in *RemoveRouteRequest, opts ...grpc.CallOption) (*RemoveRouteResponse, error)
// Firewall
GetFirewallRules(ctx context.Context, in *GetFirewallRulesRequest, opts ...grpc.CallOption) (*GetFirewallRulesResponse, error)
AddFirewallRule(ctx context.Context, in *AddFirewallRuleRequest, opts ...grpc.CallOption) (*AddFirewallRuleResponse, error)
RemoveFirewallRule(ctx context.Context, in *RemoveFirewallRuleRequest, opts ...grpc.CallOption) (*RemoveFirewallRuleResponse, error)
// Connection limits
SetListenerMaxConnections(ctx context.Context, in *SetListenerMaxConnectionsRequest, opts ...grpc.CallOption) (*SetListenerMaxConnectionsResponse, error)
// L7 policies
ListL7Policies(ctx context.Context, in *ListL7PoliciesRequest, opts ...grpc.CallOption) (*ListL7PoliciesResponse, error)
AddL7Policy(ctx context.Context, in *AddL7PolicyRequest, opts ...grpc.CallOption) (*AddL7PolicyResponse, error)
RemoveL7Policy(ctx context.Context, in *RemoveL7PolicyRequest, opts ...grpc.CallOption) (*RemoveL7PolicyResponse, error)
// Status
GetStatus(ctx context.Context, in *GetStatusRequest, opts ...grpc.CallOption) (*GetStatusResponse, error)
}
type proxyAdminServiceClient struct {
cc grpc.ClientConnInterface
}
func NewProxyAdminServiceClient(cc grpc.ClientConnInterface) ProxyAdminServiceClient {
return &proxyAdminServiceClient{cc}
}
func (c *proxyAdminServiceClient) ListRoutes(ctx context.Context, in *ListRoutesRequest, opts ...grpc.CallOption) (*ListRoutesResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ListRoutesResponse)
err := c.cc.Invoke(ctx, ProxyAdminService_ListRoutes_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *proxyAdminServiceClient) AddRoute(ctx context.Context, in *AddRouteRequest, opts ...grpc.CallOption) (*AddRouteResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(AddRouteResponse)
err := c.cc.Invoke(ctx, ProxyAdminService_AddRoute_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *proxyAdminServiceClient) RemoveRoute(ctx context.Context, in *RemoveRouteRequest, opts ...grpc.CallOption) (*RemoveRouteResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(RemoveRouteResponse)
err := c.cc.Invoke(ctx, ProxyAdminService_RemoveRoute_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *proxyAdminServiceClient) GetFirewallRules(ctx context.Context, in *GetFirewallRulesRequest, opts ...grpc.CallOption) (*GetFirewallRulesResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(GetFirewallRulesResponse)
err := c.cc.Invoke(ctx, ProxyAdminService_GetFirewallRules_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *proxyAdminServiceClient) AddFirewallRule(ctx context.Context, in *AddFirewallRuleRequest, opts ...grpc.CallOption) (*AddFirewallRuleResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(AddFirewallRuleResponse)
err := c.cc.Invoke(ctx, ProxyAdminService_AddFirewallRule_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *proxyAdminServiceClient) RemoveFirewallRule(ctx context.Context, in *RemoveFirewallRuleRequest, opts ...grpc.CallOption) (*RemoveFirewallRuleResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(RemoveFirewallRuleResponse)
err := c.cc.Invoke(ctx, ProxyAdminService_RemoveFirewallRule_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *proxyAdminServiceClient) SetListenerMaxConnections(ctx context.Context, in *SetListenerMaxConnectionsRequest, opts ...grpc.CallOption) (*SetListenerMaxConnectionsResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(SetListenerMaxConnectionsResponse)
err := c.cc.Invoke(ctx, ProxyAdminService_SetListenerMaxConnections_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *proxyAdminServiceClient) ListL7Policies(ctx context.Context, in *ListL7PoliciesRequest, opts ...grpc.CallOption) (*ListL7PoliciesResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ListL7PoliciesResponse)
err := c.cc.Invoke(ctx, ProxyAdminService_ListL7Policies_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *proxyAdminServiceClient) AddL7Policy(ctx context.Context, in *AddL7PolicyRequest, opts ...grpc.CallOption) (*AddL7PolicyResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(AddL7PolicyResponse)
err := c.cc.Invoke(ctx, ProxyAdminService_AddL7Policy_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *proxyAdminServiceClient) RemoveL7Policy(ctx context.Context, in *RemoveL7PolicyRequest, opts ...grpc.CallOption) (*RemoveL7PolicyResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(RemoveL7PolicyResponse)
err := c.cc.Invoke(ctx, ProxyAdminService_RemoveL7Policy_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *proxyAdminServiceClient) GetStatus(ctx context.Context, in *GetStatusRequest, opts ...grpc.CallOption) (*GetStatusResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(GetStatusResponse)
err := c.cc.Invoke(ctx, ProxyAdminService_GetStatus_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// ProxyAdminServiceServer is the server API for ProxyAdminService service.
// All implementations must embed UnimplementedProxyAdminServiceServer
// for forward compatibility.
type ProxyAdminServiceServer interface {
// Routes
ListRoutes(context.Context, *ListRoutesRequest) (*ListRoutesResponse, error)
AddRoute(context.Context, *AddRouteRequest) (*AddRouteResponse, error)
RemoveRoute(context.Context, *RemoveRouteRequest) (*RemoveRouteResponse, error)
// Firewall
GetFirewallRules(context.Context, *GetFirewallRulesRequest) (*GetFirewallRulesResponse, error)
AddFirewallRule(context.Context, *AddFirewallRuleRequest) (*AddFirewallRuleResponse, error)
RemoveFirewallRule(context.Context, *RemoveFirewallRuleRequest) (*RemoveFirewallRuleResponse, error)
// Connection limits
SetListenerMaxConnections(context.Context, *SetListenerMaxConnectionsRequest) (*SetListenerMaxConnectionsResponse, error)
// L7 policies
ListL7Policies(context.Context, *ListL7PoliciesRequest) (*ListL7PoliciesResponse, error)
AddL7Policy(context.Context, *AddL7PolicyRequest) (*AddL7PolicyResponse, error)
RemoveL7Policy(context.Context, *RemoveL7PolicyRequest) (*RemoveL7PolicyResponse, error)
// Status
GetStatus(context.Context, *GetStatusRequest) (*GetStatusResponse, error)
mustEmbedUnimplementedProxyAdminServiceServer()
}
// UnimplementedProxyAdminServiceServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedProxyAdminServiceServer struct{}
func (UnimplementedProxyAdminServiceServer) ListRoutes(context.Context, *ListRoutesRequest) (*ListRoutesResponse, error) {
return nil, status.Error(codes.Unimplemented, "method ListRoutes not implemented")
}
func (UnimplementedProxyAdminServiceServer) AddRoute(context.Context, *AddRouteRequest) (*AddRouteResponse, error) {
return nil, status.Error(codes.Unimplemented, "method AddRoute not implemented")
}
func (UnimplementedProxyAdminServiceServer) RemoveRoute(context.Context, *RemoveRouteRequest) (*RemoveRouteResponse, error) {
return nil, status.Error(codes.Unimplemented, "method RemoveRoute not implemented")
}
func (UnimplementedProxyAdminServiceServer) GetFirewallRules(context.Context, *GetFirewallRulesRequest) (*GetFirewallRulesResponse, error) {
return nil, status.Error(codes.Unimplemented, "method GetFirewallRules not implemented")
}
func (UnimplementedProxyAdminServiceServer) AddFirewallRule(context.Context, *AddFirewallRuleRequest) (*AddFirewallRuleResponse, error) {
return nil, status.Error(codes.Unimplemented, "method AddFirewallRule not implemented")
}
func (UnimplementedProxyAdminServiceServer) RemoveFirewallRule(context.Context, *RemoveFirewallRuleRequest) (*RemoveFirewallRuleResponse, error) {
return nil, status.Error(codes.Unimplemented, "method RemoveFirewallRule not implemented")
}
func (UnimplementedProxyAdminServiceServer) SetListenerMaxConnections(context.Context, *SetListenerMaxConnectionsRequest) (*SetListenerMaxConnectionsResponse, error) {
return nil, status.Error(codes.Unimplemented, "method SetListenerMaxConnections not implemented")
}
func (UnimplementedProxyAdminServiceServer) ListL7Policies(context.Context, *ListL7PoliciesRequest) (*ListL7PoliciesResponse, error) {
return nil, status.Error(codes.Unimplemented, "method ListL7Policies not implemented")
}
func (UnimplementedProxyAdminServiceServer) AddL7Policy(context.Context, *AddL7PolicyRequest) (*AddL7PolicyResponse, error) {
return nil, status.Error(codes.Unimplemented, "method AddL7Policy not implemented")
}
func (UnimplementedProxyAdminServiceServer) RemoveL7Policy(context.Context, *RemoveL7PolicyRequest) (*RemoveL7PolicyResponse, error) {
return nil, status.Error(codes.Unimplemented, "method RemoveL7Policy not implemented")
}
func (UnimplementedProxyAdminServiceServer) GetStatus(context.Context, *GetStatusRequest) (*GetStatusResponse, error) {
return nil, status.Error(codes.Unimplemented, "method GetStatus not implemented")
}
func (UnimplementedProxyAdminServiceServer) mustEmbedUnimplementedProxyAdminServiceServer() {}
func (UnimplementedProxyAdminServiceServer) testEmbeddedByValue() {}
// UnsafeProxyAdminServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to ProxyAdminServiceServer will
// result in compilation errors.
type UnsafeProxyAdminServiceServer interface {
mustEmbedUnimplementedProxyAdminServiceServer()
}
func RegisterProxyAdminServiceServer(s grpc.ServiceRegistrar, srv ProxyAdminServiceServer) {
// If the following call panics, it indicates UnimplementedProxyAdminServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&ProxyAdminService_ServiceDesc, srv)
}
func _ProxyAdminService_ListRoutes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ListRoutesRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ProxyAdminServiceServer).ListRoutes(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: ProxyAdminService_ListRoutes_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ProxyAdminServiceServer).ListRoutes(ctx, req.(*ListRoutesRequest))
}
return interceptor(ctx, in, info, handler)
}
func _ProxyAdminService_AddRoute_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AddRouteRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ProxyAdminServiceServer).AddRoute(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: ProxyAdminService_AddRoute_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ProxyAdminServiceServer).AddRoute(ctx, req.(*AddRouteRequest))
}
return interceptor(ctx, in, info, handler)
}
func _ProxyAdminService_RemoveRoute_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(RemoveRouteRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ProxyAdminServiceServer).RemoveRoute(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: ProxyAdminService_RemoveRoute_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ProxyAdminServiceServer).RemoveRoute(ctx, req.(*RemoveRouteRequest))
}
return interceptor(ctx, in, info, handler)
}
func _ProxyAdminService_GetFirewallRules_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetFirewallRulesRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ProxyAdminServiceServer).GetFirewallRules(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: ProxyAdminService_GetFirewallRules_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ProxyAdminServiceServer).GetFirewallRules(ctx, req.(*GetFirewallRulesRequest))
}
return interceptor(ctx, in, info, handler)
}
func _ProxyAdminService_AddFirewallRule_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AddFirewallRuleRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ProxyAdminServiceServer).AddFirewallRule(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: ProxyAdminService_AddFirewallRule_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ProxyAdminServiceServer).AddFirewallRule(ctx, req.(*AddFirewallRuleRequest))
}
return interceptor(ctx, in, info, handler)
}
func _ProxyAdminService_RemoveFirewallRule_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(RemoveFirewallRuleRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ProxyAdminServiceServer).RemoveFirewallRule(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: ProxyAdminService_RemoveFirewallRule_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ProxyAdminServiceServer).RemoveFirewallRule(ctx, req.(*RemoveFirewallRuleRequest))
}
return interceptor(ctx, in, info, handler)
}
func _ProxyAdminService_SetListenerMaxConnections_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SetListenerMaxConnectionsRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ProxyAdminServiceServer).SetListenerMaxConnections(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: ProxyAdminService_SetListenerMaxConnections_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ProxyAdminServiceServer).SetListenerMaxConnections(ctx, req.(*SetListenerMaxConnectionsRequest))
}
return interceptor(ctx, in, info, handler)
}
func _ProxyAdminService_ListL7Policies_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ListL7PoliciesRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ProxyAdminServiceServer).ListL7Policies(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: ProxyAdminService_ListL7Policies_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ProxyAdminServiceServer).ListL7Policies(ctx, req.(*ListL7PoliciesRequest))
}
return interceptor(ctx, in, info, handler)
}
func _ProxyAdminService_AddL7Policy_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AddL7PolicyRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ProxyAdminServiceServer).AddL7Policy(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: ProxyAdminService_AddL7Policy_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ProxyAdminServiceServer).AddL7Policy(ctx, req.(*AddL7PolicyRequest))
}
return interceptor(ctx, in, info, handler)
}
func _ProxyAdminService_RemoveL7Policy_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(RemoveL7PolicyRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ProxyAdminServiceServer).RemoveL7Policy(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: ProxyAdminService_RemoveL7Policy_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ProxyAdminServiceServer).RemoveL7Policy(ctx, req.(*RemoveL7PolicyRequest))
}
return interceptor(ctx, in, info, handler)
}
func _ProxyAdminService_GetStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetStatusRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ProxyAdminServiceServer).GetStatus(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: ProxyAdminService_GetStatus_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ProxyAdminServiceServer).GetStatus(ctx, req.(*GetStatusRequest))
}
return interceptor(ctx, in, info, handler)
}
// ProxyAdminService_ServiceDesc is the grpc.ServiceDesc for ProxyAdminService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var ProxyAdminService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "mc_proxy.v1.ProxyAdminService",
HandlerType: (*ProxyAdminServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "ListRoutes",
Handler: _ProxyAdminService_ListRoutes_Handler,
},
{
MethodName: "AddRoute",
Handler: _ProxyAdminService_AddRoute_Handler,
},
{
MethodName: "RemoveRoute",
Handler: _ProxyAdminService_RemoveRoute_Handler,
},
{
MethodName: "GetFirewallRules",
Handler: _ProxyAdminService_GetFirewallRules_Handler,
},
{
MethodName: "AddFirewallRule",
Handler: _ProxyAdminService_AddFirewallRule_Handler,
},
{
MethodName: "RemoveFirewallRule",
Handler: _ProxyAdminService_RemoveFirewallRule_Handler,
},
{
MethodName: "SetListenerMaxConnections",
Handler: _ProxyAdminService_SetListenerMaxConnections_Handler,
},
{
MethodName: "ListL7Policies",
Handler: _ProxyAdminService_ListL7Policies_Handler,
},
{
MethodName: "AddL7Policy",
Handler: _ProxyAdminService_AddL7Policy_Handler,
},
{
MethodName: "RemoveL7Policy",
Handler: _ProxyAdminService_RemoveL7Policy_Handler,
},
{
MethodName: "GetStatus",
Handler: _ProxyAdminService_GetStatus_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "proto/mc_proxy/v1/admin.proto",
}

View File

@@ -0,0 +1,350 @@
// Copyright 2015 The gRPC Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// The canonical version of this proto can be found at
// https://github.com/grpc/grpc-proto/blob/master/grpc/health/v1/health.proto
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.10
// protoc v5.27.1
// source: grpc/health/v1/health.proto
package grpc_health_v1
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type HealthCheckResponse_ServingStatus int32
const (
HealthCheckResponse_UNKNOWN HealthCheckResponse_ServingStatus = 0
HealthCheckResponse_SERVING HealthCheckResponse_ServingStatus = 1
HealthCheckResponse_NOT_SERVING HealthCheckResponse_ServingStatus = 2
HealthCheckResponse_SERVICE_UNKNOWN HealthCheckResponse_ServingStatus = 3 // Used only by the Watch method.
)
// Enum value maps for HealthCheckResponse_ServingStatus.
var (
HealthCheckResponse_ServingStatus_name = map[int32]string{
0: "UNKNOWN",
1: "SERVING",
2: "NOT_SERVING",
3: "SERVICE_UNKNOWN",
}
HealthCheckResponse_ServingStatus_value = map[string]int32{
"UNKNOWN": 0,
"SERVING": 1,
"NOT_SERVING": 2,
"SERVICE_UNKNOWN": 3,
}
)
func (x HealthCheckResponse_ServingStatus) Enum() *HealthCheckResponse_ServingStatus {
p := new(HealthCheckResponse_ServingStatus)
*p = x
return p
}
func (x HealthCheckResponse_ServingStatus) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (HealthCheckResponse_ServingStatus) Descriptor() protoreflect.EnumDescriptor {
return file_grpc_health_v1_health_proto_enumTypes[0].Descriptor()
}
func (HealthCheckResponse_ServingStatus) Type() protoreflect.EnumType {
return &file_grpc_health_v1_health_proto_enumTypes[0]
}
func (x HealthCheckResponse_ServingStatus) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use HealthCheckResponse_ServingStatus.Descriptor instead.
func (HealthCheckResponse_ServingStatus) EnumDescriptor() ([]byte, []int) {
return file_grpc_health_v1_health_proto_rawDescGZIP(), []int{1, 0}
}
type HealthCheckRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
Service string `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *HealthCheckRequest) Reset() {
*x = HealthCheckRequest{}
mi := &file_grpc_health_v1_health_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *HealthCheckRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*HealthCheckRequest) ProtoMessage() {}
func (x *HealthCheckRequest) ProtoReflect() protoreflect.Message {
mi := &file_grpc_health_v1_health_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use HealthCheckRequest.ProtoReflect.Descriptor instead.
func (*HealthCheckRequest) Descriptor() ([]byte, []int) {
return file_grpc_health_v1_health_proto_rawDescGZIP(), []int{0}
}
func (x *HealthCheckRequest) GetService() string {
if x != nil {
return x.Service
}
return ""
}
type HealthCheckResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Status HealthCheckResponse_ServingStatus `protobuf:"varint,1,opt,name=status,proto3,enum=grpc.health.v1.HealthCheckResponse_ServingStatus" json:"status,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *HealthCheckResponse) Reset() {
*x = HealthCheckResponse{}
mi := &file_grpc_health_v1_health_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *HealthCheckResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*HealthCheckResponse) ProtoMessage() {}
func (x *HealthCheckResponse) ProtoReflect() protoreflect.Message {
mi := &file_grpc_health_v1_health_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use HealthCheckResponse.ProtoReflect.Descriptor instead.
func (*HealthCheckResponse) Descriptor() ([]byte, []int) {
return file_grpc_health_v1_health_proto_rawDescGZIP(), []int{1}
}
func (x *HealthCheckResponse) GetStatus() HealthCheckResponse_ServingStatus {
if x != nil {
return x.Status
}
return HealthCheckResponse_UNKNOWN
}
type HealthListRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *HealthListRequest) Reset() {
*x = HealthListRequest{}
mi := &file_grpc_health_v1_health_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *HealthListRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*HealthListRequest) ProtoMessage() {}
func (x *HealthListRequest) ProtoReflect() protoreflect.Message {
mi := &file_grpc_health_v1_health_proto_msgTypes[2]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use HealthListRequest.ProtoReflect.Descriptor instead.
func (*HealthListRequest) Descriptor() ([]byte, []int) {
return file_grpc_health_v1_health_proto_rawDescGZIP(), []int{2}
}
type HealthListResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
// statuses contains all the services and their respective status.
Statuses map[string]*HealthCheckResponse `protobuf:"bytes,1,rep,name=statuses,proto3" json:"statuses,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *HealthListResponse) Reset() {
*x = HealthListResponse{}
mi := &file_grpc_health_v1_health_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *HealthListResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*HealthListResponse) ProtoMessage() {}
func (x *HealthListResponse) ProtoReflect() protoreflect.Message {
mi := &file_grpc_health_v1_health_proto_msgTypes[3]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use HealthListResponse.ProtoReflect.Descriptor instead.
func (*HealthListResponse) Descriptor() ([]byte, []int) {
return file_grpc_health_v1_health_proto_rawDescGZIP(), []int{3}
}
func (x *HealthListResponse) GetStatuses() map[string]*HealthCheckResponse {
if x != nil {
return x.Statuses
}
return nil
}
var File_grpc_health_v1_health_proto protoreflect.FileDescriptor
const file_grpc_health_v1_health_proto_rawDesc = "" +
"\n" +
"\x1bgrpc/health/v1/health.proto\x12\x0egrpc.health.v1\".\n" +
"\x12HealthCheckRequest\x12\x18\n" +
"\aservice\x18\x01 \x01(\tR\aservice\"\xb1\x01\n" +
"\x13HealthCheckResponse\x12I\n" +
"\x06status\x18\x01 \x01(\x0e21.grpc.health.v1.HealthCheckResponse.ServingStatusR\x06status\"O\n" +
"\rServingStatus\x12\v\n" +
"\aUNKNOWN\x10\x00\x12\v\n" +
"\aSERVING\x10\x01\x12\x0f\n" +
"\vNOT_SERVING\x10\x02\x12\x13\n" +
"\x0fSERVICE_UNKNOWN\x10\x03\"\x13\n" +
"\x11HealthListRequest\"\xc4\x01\n" +
"\x12HealthListResponse\x12L\n" +
"\bstatuses\x18\x01 \x03(\v20.grpc.health.v1.HealthListResponse.StatusesEntryR\bstatuses\x1a`\n" +
"\rStatusesEntry\x12\x10\n" +
"\x03key\x18\x01 \x01(\tR\x03key\x129\n" +
"\x05value\x18\x02 \x01(\v2#.grpc.health.v1.HealthCheckResponseR\x05value:\x028\x012\xfd\x01\n" +
"\x06Health\x12P\n" +
"\x05Check\x12\".grpc.health.v1.HealthCheckRequest\x1a#.grpc.health.v1.HealthCheckResponse\x12M\n" +
"\x04List\x12!.grpc.health.v1.HealthListRequest\x1a\".grpc.health.v1.HealthListResponse\x12R\n" +
"\x05Watch\x12\".grpc.health.v1.HealthCheckRequest\x1a#.grpc.health.v1.HealthCheckResponse0\x01Bp\n" +
"\x11io.grpc.health.v1B\vHealthProtoP\x01Z,google.golang.org/grpc/health/grpc_health_v1\xa2\x02\fGrpcHealthV1\xaa\x02\x0eGrpc.Health.V1b\x06proto3"
var (
file_grpc_health_v1_health_proto_rawDescOnce sync.Once
file_grpc_health_v1_health_proto_rawDescData []byte
)
func file_grpc_health_v1_health_proto_rawDescGZIP() []byte {
file_grpc_health_v1_health_proto_rawDescOnce.Do(func() {
file_grpc_health_v1_health_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_grpc_health_v1_health_proto_rawDesc), len(file_grpc_health_v1_health_proto_rawDesc)))
})
return file_grpc_health_v1_health_proto_rawDescData
}
var file_grpc_health_v1_health_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_grpc_health_v1_health_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
var file_grpc_health_v1_health_proto_goTypes = []any{
(HealthCheckResponse_ServingStatus)(0), // 0: grpc.health.v1.HealthCheckResponse.ServingStatus
(*HealthCheckRequest)(nil), // 1: grpc.health.v1.HealthCheckRequest
(*HealthCheckResponse)(nil), // 2: grpc.health.v1.HealthCheckResponse
(*HealthListRequest)(nil), // 3: grpc.health.v1.HealthListRequest
(*HealthListResponse)(nil), // 4: grpc.health.v1.HealthListResponse
nil, // 5: grpc.health.v1.HealthListResponse.StatusesEntry
}
var file_grpc_health_v1_health_proto_depIdxs = []int32{
0, // 0: grpc.health.v1.HealthCheckResponse.status:type_name -> grpc.health.v1.HealthCheckResponse.ServingStatus
5, // 1: grpc.health.v1.HealthListResponse.statuses:type_name -> grpc.health.v1.HealthListResponse.StatusesEntry
2, // 2: grpc.health.v1.HealthListResponse.StatusesEntry.value:type_name -> grpc.health.v1.HealthCheckResponse
1, // 3: grpc.health.v1.Health.Check:input_type -> grpc.health.v1.HealthCheckRequest
3, // 4: grpc.health.v1.Health.List:input_type -> grpc.health.v1.HealthListRequest
1, // 5: grpc.health.v1.Health.Watch:input_type -> grpc.health.v1.HealthCheckRequest
2, // 6: grpc.health.v1.Health.Check:output_type -> grpc.health.v1.HealthCheckResponse
4, // 7: grpc.health.v1.Health.List:output_type -> grpc.health.v1.HealthListResponse
2, // 8: grpc.health.v1.Health.Watch:output_type -> grpc.health.v1.HealthCheckResponse
6, // [6:9] is the sub-list for method output_type
3, // [3:6] is the sub-list for method input_type
3, // [3:3] is the sub-list for extension type_name
3, // [3:3] is the sub-list for extension extendee
0, // [0:3] is the sub-list for field type_name
}
func init() { file_grpc_health_v1_health_proto_init() }
func file_grpc_health_v1_health_proto_init() {
if File_grpc_health_v1_health_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_grpc_health_v1_health_proto_rawDesc), len(file_grpc_health_v1_health_proto_rawDesc)),
NumEnums: 1,
NumMessages: 5,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_grpc_health_v1_health_proto_goTypes,
DependencyIndexes: file_grpc_health_v1_health_proto_depIdxs,
EnumInfos: file_grpc_health_v1_health_proto_enumTypes,
MessageInfos: file_grpc_health_v1_health_proto_msgTypes,
}.Build()
File_grpc_health_v1_health_proto = out.File
file_grpc_health_v1_health_proto_goTypes = nil
file_grpc_health_v1_health_proto_depIdxs = nil
}

View File

@@ -0,0 +1,290 @@
// Copyright 2015 The gRPC Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// The canonical version of this proto can be found at
// https://github.com/grpc/grpc-proto/blob/master/grpc/health/v1/health.proto
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.6.0
// - protoc v5.27.1
// source: grpc/health/v1/health.proto
package grpc_health_v1
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
const (
Health_Check_FullMethodName = "/grpc.health.v1.Health/Check"
Health_List_FullMethodName = "/grpc.health.v1.Health/List"
Health_Watch_FullMethodName = "/grpc.health.v1.Health/Watch"
)
// HealthClient is the client API for Health service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
//
// Health is gRPC's mechanism for checking whether a server is able to handle
// RPCs. Its semantics are documented in
// https://github.com/grpc/grpc/blob/master/doc/health-checking.md.
type HealthClient interface {
// Check gets the health of the specified service. If the requested service
// is unknown, the call will fail with status NOT_FOUND. If the caller does
// not specify a service name, the server should respond with its overall
// health status.
//
// Clients should set a deadline when calling Check, and can declare the
// server unhealthy if they do not receive a timely response.
Check(ctx context.Context, in *HealthCheckRequest, opts ...grpc.CallOption) (*HealthCheckResponse, error)
// List provides a non-atomic snapshot of the health of all the available
// services.
//
// The server may respond with a RESOURCE_EXHAUSTED error if too many services
// exist.
//
// Clients should set a deadline when calling List, and can declare the server
// unhealthy if they do not receive a timely response.
//
// Clients should keep in mind that the list of health services exposed by an
// application can change over the lifetime of the process.
List(ctx context.Context, in *HealthListRequest, opts ...grpc.CallOption) (*HealthListResponse, error)
// Performs a watch for the serving status of the requested service.
// The server will immediately send back a message indicating the current
// serving status. It will then subsequently send a new message whenever
// the service's serving status changes.
//
// If the requested service is unknown when the call is received, the
// server will send a message setting the serving status to
// SERVICE_UNKNOWN but will *not* terminate the call. If at some
// future point, the serving status of the service becomes known, the
// server will send a new message with the service's serving status.
//
// If the call terminates with status UNIMPLEMENTED, then clients
// should assume this method is not supported and should not retry the
// call. If the call terminates with any other status (including OK),
// clients should retry the call with appropriate exponential backoff.
Watch(ctx context.Context, in *HealthCheckRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[HealthCheckResponse], error)
}
type healthClient struct {
cc grpc.ClientConnInterface
}
func NewHealthClient(cc grpc.ClientConnInterface) HealthClient {
return &healthClient{cc}
}
func (c *healthClient) Check(ctx context.Context, in *HealthCheckRequest, opts ...grpc.CallOption) (*HealthCheckResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(HealthCheckResponse)
err := c.cc.Invoke(ctx, Health_Check_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *healthClient) List(ctx context.Context, in *HealthListRequest, opts ...grpc.CallOption) (*HealthListResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(HealthListResponse)
err := c.cc.Invoke(ctx, Health_List_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *healthClient) Watch(ctx context.Context, in *HealthCheckRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[HealthCheckResponse], error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
stream, err := c.cc.NewStream(ctx, &Health_ServiceDesc.Streams[0], Health_Watch_FullMethodName, cOpts...)
if err != nil {
return nil, err
}
x := &grpc.GenericClientStream[HealthCheckRequest, HealthCheckResponse]{ClientStream: stream}
if err := x.ClientStream.SendMsg(in); err != nil {
return nil, err
}
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
return x, nil
}
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
type Health_WatchClient = grpc.ServerStreamingClient[HealthCheckResponse]
// HealthServer is the server API for Health service.
// All implementations should embed UnimplementedHealthServer
// for forward compatibility.
//
// Health is gRPC's mechanism for checking whether a server is able to handle
// RPCs. Its semantics are documented in
// https://github.com/grpc/grpc/blob/master/doc/health-checking.md.
type HealthServer interface {
// Check gets the health of the specified service. If the requested service
// is unknown, the call will fail with status NOT_FOUND. If the caller does
// not specify a service name, the server should respond with its overall
// health status.
//
// Clients should set a deadline when calling Check, and can declare the
// server unhealthy if they do not receive a timely response.
Check(context.Context, *HealthCheckRequest) (*HealthCheckResponse, error)
// List provides a non-atomic snapshot of the health of all the available
// services.
//
// The server may respond with a RESOURCE_EXHAUSTED error if too many services
// exist.
//
// Clients should set a deadline when calling List, and can declare the server
// unhealthy if they do not receive a timely response.
//
// Clients should keep in mind that the list of health services exposed by an
// application can change over the lifetime of the process.
List(context.Context, *HealthListRequest) (*HealthListResponse, error)
// Performs a watch for the serving status of the requested service.
// The server will immediately send back a message indicating the current
// serving status. It will then subsequently send a new message whenever
// the service's serving status changes.
//
// If the requested service is unknown when the call is received, the
// server will send a message setting the serving status to
// SERVICE_UNKNOWN but will *not* terminate the call. If at some
// future point, the serving status of the service becomes known, the
// server will send a new message with the service's serving status.
//
// If the call terminates with status UNIMPLEMENTED, then clients
// should assume this method is not supported and should not retry the
// call. If the call terminates with any other status (including OK),
// clients should retry the call with appropriate exponential backoff.
Watch(*HealthCheckRequest, grpc.ServerStreamingServer[HealthCheckResponse]) error
}
// UnimplementedHealthServer should be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedHealthServer struct{}
func (UnimplementedHealthServer) Check(context.Context, *HealthCheckRequest) (*HealthCheckResponse, error) {
return nil, status.Error(codes.Unimplemented, "method Check not implemented")
}
func (UnimplementedHealthServer) List(context.Context, *HealthListRequest) (*HealthListResponse, error) {
return nil, status.Error(codes.Unimplemented, "method List not implemented")
}
func (UnimplementedHealthServer) Watch(*HealthCheckRequest, grpc.ServerStreamingServer[HealthCheckResponse]) error {
return status.Error(codes.Unimplemented, "method Watch not implemented")
}
func (UnimplementedHealthServer) testEmbeddedByValue() {}
// UnsafeHealthServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to HealthServer will
// result in compilation errors.
type UnsafeHealthServer interface {
mustEmbedUnimplementedHealthServer()
}
func RegisterHealthServer(s grpc.ServiceRegistrar, srv HealthServer) {
// If the following call panics, it indicates UnimplementedHealthServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&Health_ServiceDesc, srv)
}
func _Health_Check_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(HealthCheckRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(HealthServer).Check(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Health_Check_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HealthServer).Check(ctx, req.(*HealthCheckRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Health_List_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(HealthListRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(HealthServer).List(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Health_List_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(HealthServer).List(ctx, req.(*HealthListRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Health_Watch_Handler(srv interface{}, stream grpc.ServerStream) error {
m := new(HealthCheckRequest)
if err := stream.RecvMsg(m); err != nil {
return err
}
return srv.(HealthServer).Watch(m, &grpc.GenericServerStream[HealthCheckRequest, HealthCheckResponse]{ServerStream: stream})
}
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
type Health_WatchServer = grpc.ServerStreamingServer[HealthCheckResponse]
// Health_ServiceDesc is the grpc.ServiceDesc for Health service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var Health_ServiceDesc = grpc.ServiceDesc{
ServiceName: "grpc.health.v1.Health",
HandlerType: (*HealthServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Check",
Handler: _Health_Check_Handler,
},
{
MethodName: "List",
Handler: _Health_List_Handler,
},
},
Streams: []grpc.StreamDesc{
{
StreamName: "Watch",
Handler: _Health_Watch_Handler,
ServerStreams: true,
},
},
Metadata: "grpc/health/v1/health.proto",
}

5
vendor/modules.txt vendored
View File

@@ -1,3 +1,7 @@
# git.wntrmute.dev/kyle/mc-proxy v1.0.0
## explicit; go 1.25.7
git.wntrmute.dev/kyle/mc-proxy/client/mcproxy
git.wntrmute.dev/kyle/mc-proxy/gen/mc_proxy/v1
# github.com/dustin/go-humanize v1.0.1 # github.com/dustin/go-humanize v1.0.1
## explicit; go 1.16 ## explicit; go 1.16
github.com/dustin/go-humanize github.com/dustin/go-humanize
@@ -74,6 +78,7 @@ google.golang.org/grpc/encoding/proto
google.golang.org/grpc/experimental/stats google.golang.org/grpc/experimental/stats
google.golang.org/grpc/grpclog google.golang.org/grpc/grpclog
google.golang.org/grpc/grpclog/internal google.golang.org/grpc/grpclog/internal
google.golang.org/grpc/health/grpc_health_v1
google.golang.org/grpc/internal google.golang.org/grpc/internal
google.golang.org/grpc/internal/backoff google.golang.org/grpc/internal/backoff
google.golang.org/grpc/internal/balancer/gracefulswitch google.golang.org/grpc/internal/balancer/gracefulswitch