Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cedba9bf83 | |||
| f06ab9aeb6 | |||
| f932dd64cc | |||
| b2eaa69619 | |||
| 43789dd6be | |||
| 2dd0ea93fc | |||
| 169b3a0d4a | |||
| 2bda7fc138 | |||
| 76247978c2 | |||
| ca3bc736f6 | |||
| 9d9ad6588e | |||
| e4d131021e | |||
| 8d6c060483 | |||
| c7e1232f98 | |||
| 572d2fb196 | |||
| c6a84a1b80 | |||
| 08b3e2a472 | |||
| 6e30cf12f2 | |||
| c28562dbcf | |||
| 84c487e7f8 |
@@ -5,6 +5,24 @@ run:
|
||||
tests: true
|
||||
|
||||
linters:
|
||||
exclusions:
|
||||
paths:
|
||||
- vendor
|
||||
rules:
|
||||
# In test files, suppress gosec rules that are false positives:
|
||||
# G101: hardcoded test credentials
|
||||
# G304: file paths from variables (t.TempDir paths)
|
||||
# G306: WriteFile with 0644 (cert files need to be readable)
|
||||
# G404: weak RNG (not security-relevant in tests)
|
||||
- path: "_test\\.go"
|
||||
linters:
|
||||
- gosec
|
||||
text: "G101|G304|G306|G404"
|
||||
# Nil context is acceptable in tests for nil-receiver safety checks.
|
||||
- path: "_test\\.go"
|
||||
linters:
|
||||
- staticcheck
|
||||
text: "SA1012"
|
||||
default: none
|
||||
enable:
|
||||
- errcheck
|
||||
@@ -69,12 +87,3 @@ formatters:
|
||||
issues:
|
||||
max-issues-per-linter: 0
|
||||
max-same-issues: 0
|
||||
|
||||
exclusions:
|
||||
paths:
|
||||
- vendor
|
||||
rules:
|
||||
- path: "_test\\.go"
|
||||
linters:
|
||||
- gosec
|
||||
text: "G101"
|
||||
|
||||
132
ARCHITECTURE.md
132
ARCHITECTURE.md
@@ -198,6 +198,7 @@ mcp build <service>/<image> Build and push a single image
|
||||
mcp deploy <service> Deploy all components from service definition
|
||||
mcp deploy <service>/<component> Deploy a single component
|
||||
mcp deploy <service> -f <file> Deploy from explicit file
|
||||
mcp undeploy <service> Full teardown: remove routes, DNS, certs, containers
|
||||
mcp stop <service> Stop all components, set active=false
|
||||
mcp start <service> Start all components, set active=true
|
||||
mcp restart <service> Restart all components
|
||||
@@ -453,6 +454,7 @@ import "google/protobuf/timestamp.proto";
|
||||
service McpAgent {
|
||||
// Service lifecycle
|
||||
rpc Deploy(DeployRequest) returns (DeployResponse);
|
||||
rpc UndeployService(UndeployRequest) returns (UndeployResponse);
|
||||
rpc StopService(ServiceRequest) returns (ServiceResponse);
|
||||
rpc StartService(ServiceRequest) returns (ServiceResponse);
|
||||
rpc RestartService(ServiceRequest) returns (ServiceResponse);
|
||||
@@ -714,6 +716,40 @@ The flags passed to `podman run` are derived from the `ComponentSpec`:
|
||||
| `volumes` | `-v <mapping>` (repeated) |
|
||||
| `cmd` | appended after the image name |
|
||||
|
||||
#### Undeploy Flow
|
||||
|
||||
`mcp undeploy <service>` is the full inverse of deploy. It tears down all
|
||||
infrastructure associated with a service. When the agent receives an
|
||||
`UndeployService` RPC:
|
||||
|
||||
1. For each component:
|
||||
a. Remove mc-proxy routes (traffic stops flowing).
|
||||
b. Remove DNS A records from MCNS.
|
||||
c. Remove TLS certificate and key files from the mc-proxy cert
|
||||
directory (for L7 routes).
|
||||
d. Stop and remove the container.
|
||||
e. Release allocated host ports back to the port allocator.
|
||||
f. Update component state to `removed` in the registry.
|
||||
2. Mark the service as inactive.
|
||||
3. Return success/failure per component.
|
||||
|
||||
The CLI also sets `active = false` in the local service definition file
|
||||
to keep it in sync with the operator's intent.
|
||||
|
||||
Undeploy differs from `stop` in three ways:
|
||||
|
||||
| Aspect | `stop` | `undeploy` |
|
||||
|--------|--------|-----------|
|
||||
| Container | Stopped (still exists) | Stopped and removed |
|
||||
| TLS certs | Kept | Removed |
|
||||
| Ports | Kept allocated | Released |
|
||||
| Service active | Unchanged | Set to inactive |
|
||||
|
||||
After undeploy, the service can be redeployed with `mcp deploy`. The
|
||||
registry entries are preserved (desired state `removed`) so `mcp status`
|
||||
and `mcp list` still show the service existed. Use `mcp purge` to clean
|
||||
up the registry entries if desired.
|
||||
|
||||
### File Transfer
|
||||
|
||||
The agent supports single-file push and pull, scoped to a specific
|
||||
@@ -1203,6 +1239,102 @@ container, the effective host UID depends on the mapping. Files in
|
||||
configuration should provision appropriate subuid/subgid ranges when
|
||||
creating the `mcp` user.
|
||||
|
||||
**Dockerfile convention**: Do not use `USER`, `VOLUME`, or `adduser`
|
||||
directives in production Dockerfiles. The `user` field in the service
|
||||
definition (typically `"0:0"`) controls the runtime user, and host
|
||||
volumes provide the data directories. A non-root `USER` in the
|
||||
Dockerfile maps to a subordinate UID under rootless podman that cannot
|
||||
access files owned by the `mcp` user on the host.
|
||||
|
||||
#### Infrastructure Boot Order and Circular Dependencies
|
||||
|
||||
MCR (container registry) and MCNS (DNS) are both deployed as containers
|
||||
via MCP, but MCP itself depends on them:
|
||||
|
||||
- **MCR** is reachable through mc-proxy (L4 passthrough on `:8443`).
|
||||
The agent pulls images from MCR during `mcp deploy`.
|
||||
- **MCNS** serves DNS for internal zones. Tailscale and the overlay
|
||||
network depend on DNS resolution.
|
||||
|
||||
This creates circular dependencies during cold-start or recovery:
|
||||
|
||||
```
|
||||
mcp deploy → agent pulls image → needs MCR → needs mc-proxy
|
||||
mcp deploy → agent dials MCR → DNS resolves hostname → needs MCNS
|
||||
```
|
||||
|
||||
**Cold-start procedure** (no containers running):
|
||||
|
||||
1. **Build images on the operator workstation** for mc-proxy, MCR, and
|
||||
MCNS. Transfer to rift via `podman save` / `scp` / `podman load`
|
||||
since the registry is not yet available:
|
||||
```
|
||||
docker save <image> -o /tmp/image.tar
|
||||
scp /tmp/image.tar <rift-lan-ip>:/tmp/
|
||||
# on rift, as mcp user:
|
||||
podman load -i /tmp/image.tar
|
||||
```
|
||||
Use the LAN IP for scp, not a DNS name (DNS is not running yet).
|
||||
|
||||
2. **Start MCNS first** (DNS must come up before anything that resolves
|
||||
hostnames). Run directly with podman since the MCP agent cannot reach
|
||||
the registry yet:
|
||||
```
|
||||
podman run -d --name mcns --restart unless-stopped \
|
||||
--sysctl net.ipv4.ip_unprivileged_port_start=53 \
|
||||
-p <lan-ip>:53:53/tcp -p <lan-ip>:53:53/udp \
|
||||
-p <overlay-ip>:53:53/tcp -p <overlay-ip>:53:53/udp \
|
||||
-v /srv/mcns:/srv/mcns \
|
||||
<mcns-image> server --config /srv/mcns/mcns.toml
|
||||
```
|
||||
|
||||
3. **Start mc-proxy** (registry traffic routes through it):
|
||||
```
|
||||
podman run -d --name mc-proxy --network host \
|
||||
--restart unless-stopped \
|
||||
-v /srv/mc-proxy:/srv/mc-proxy \
|
||||
<mc-proxy-image> server --config /srv/mc-proxy/mc-proxy.toml
|
||||
```
|
||||
|
||||
4. **Start MCR** (API server, then web UI):
|
||||
```
|
||||
podman run -d --name mcr-api --network mcpnet \
|
||||
--restart unless-stopped \
|
||||
-p 127.0.0.1:28443:8443 -p 127.0.0.1:29443:9443 \
|
||||
-v /srv/mcr:/srv/mcr \
|
||||
<mcr-image> server --config /srv/mcr/mcr.toml
|
||||
```
|
||||
|
||||
5. **Push images to MCR** from the operator workstation now that the
|
||||
registry is reachable:
|
||||
```
|
||||
docker push <registry>/<image>:<tag>
|
||||
```
|
||||
|
||||
6. **Start the MCP agent** (systemd service). It can now reach MCR for
|
||||
image pulls.
|
||||
|
||||
7. **`mcp adopt`** the manually-started containers to bring them under
|
||||
MCP management. Then `mcp service export` to generate service
|
||||
definition files.
|
||||
|
||||
From this point, `mcp deploy` works normally. The manually-started
|
||||
containers are replaced by MCP-managed ones on the next deploy.
|
||||
|
||||
**Recovery procedure** (mc-proxy or MCNS crashed):
|
||||
|
||||
If mc-proxy or MCNS goes down, the agent cannot pull images (registry
|
||||
unreachable or DNS broken). Recovery:
|
||||
|
||||
1. Check if the required image is cached locally:
|
||||
`podman images | grep <service>`
|
||||
2. If cached, start the container directly with `podman run` (same
|
||||
flags as the cold-start procedure above).
|
||||
3. If not cached, transfer the image from the operator workstation via
|
||||
`podman save` / `scp` / `podman load` using the LAN IP.
|
||||
4. Once the infrastructure service is running, `mcp deploy` resumes
|
||||
normal operation for other services.
|
||||
|
||||
---
|
||||
|
||||
## Security Model
|
||||
|
||||
@@ -55,4 +55,4 @@ Run a single test: `go test ./internal/registry/ -run TestComponentCRUD`
|
||||
|
||||
## Module Path
|
||||
|
||||
`git.wntrmute.dev/kyle/mcp`
|
||||
`git.wntrmute.dev/mc/mcp`
|
||||
|
||||
4
Makefile
4
Makefile
@@ -21,8 +21,8 @@ lint:
|
||||
golangci-lint run ./...
|
||||
|
||||
proto:
|
||||
protoc --go_out=. --go_opt=module=git.wntrmute.dev/kyle/mcp \
|
||||
--go-grpc_out=. --go-grpc_opt=module=git.wntrmute.dev/kyle/mcp \
|
||||
protoc --go_out=. --go_opt=module=git.wntrmute.dev/mc/mcp \
|
||||
--go-grpc_out=. --go-grpc_opt=module=git.wntrmute.dev/mc/mcp \
|
||||
proto/mcp/v1/*.proto
|
||||
|
||||
proto-lint:
|
||||
|
||||
@@ -81,13 +81,14 @@
|
||||
- [x] Registry: `component_routes` table with `host_port` tracking
|
||||
- [x] Backward compatible: old-style `ports` strings still work
|
||||
|
||||
### Phase B — IN PROGRESS
|
||||
### Phase B — COMPLETE (2026-03-27)
|
||||
|
||||
- [ ] Agent connects to mc-proxy via Unix socket on deploy
|
||||
- [ ] Agent calls `AddRoute` to register routes with mc-proxy
|
||||
- [ ] Agent calls `RemoveRoute` on service stop/teardown
|
||||
- [ ] Agent config: `[mcproxy] socket` field
|
||||
- [ ] TLS certs: pre-provisioned at convention path (Phase C automates)
|
||||
- [x] Agent connects to mc-proxy via Unix socket on deploy
|
||||
- [x] Agent calls `AddRoute` to register routes with mc-proxy
|
||||
- [x] Agent calls `RemoveRoute` on service stop/teardown
|
||||
- [x] Agent config: `[mcproxy] socket` and `cert_dir` fields
|
||||
- [x] TLS certs: pre-provisioned at convention path (Phase C automates)
|
||||
- [x] Nil-safe: if socket not configured, route registration silently skipped
|
||||
|
||||
## Remaining Work
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ else builds on.
|
||||
structure, and configure tooling.
|
||||
|
||||
**Deliverables:**
|
||||
- `go.mod` with module path `git.wntrmute.dev/kyle/mcp`
|
||||
- `go.mod` with module path `git.wntrmute.dev/mc/mcp`
|
||||
- `Makefile` with standard targets (build, test, vet, lint, proto,
|
||||
proto-lint, clean, all)
|
||||
- `.golangci.yaml` with platform-standard linter config
|
||||
|
||||
@@ -5,8 +5,8 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"git.wntrmute.dev/kyle/mcp/internal/agent"
|
||||
"git.wntrmute.dev/kyle/mcp/internal/config"
|
||||
"git.wntrmute.dev/mc/mcp/internal/agent"
|
||||
"git.wntrmute.dev/mc/mcp/internal/config"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"git.wntrmute.dev/kyle/mcp/internal/config"
|
||||
"git.wntrmute.dev/mc/mcp/internal/config"
|
||||
"github.com/spf13/cobra"
|
||||
_ "modernc.org/sqlite"
|
||||
)
|
||||
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
mcpv1 "git.wntrmute.dev/kyle/mcp/gen/mcp/v1"
|
||||
"git.wntrmute.dev/kyle/mcp/internal/config"
|
||||
mcpv1 "git.wntrmute.dev/mc/mcp/gen/mcp/v1"
|
||||
"git.wntrmute.dev/mc/mcp/internal/config"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
|
||||
@@ -8,9 +8,9 @@ import (
|
||||
|
||||
"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"
|
||||
"git.wntrmute.dev/mc/mcp/internal/config"
|
||||
"git.wntrmute.dev/mc/mcp/internal/runtime"
|
||||
"git.wntrmute.dev/mc/mcp/internal/servicedef"
|
||||
)
|
||||
|
||||
func buildCmd() *cobra.Command {
|
||||
|
||||
@@ -8,10 +8,10 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
mcpv1 "git.wntrmute.dev/kyle/mcp/gen/mcp/v1"
|
||||
"git.wntrmute.dev/kyle/mcp/internal/config"
|
||||
"git.wntrmute.dev/kyle/mcp/internal/runtime"
|
||||
"git.wntrmute.dev/kyle/mcp/internal/servicedef"
|
||||
mcpv1 "git.wntrmute.dev/mc/mcp/gen/mcp/v1"
|
||||
"git.wntrmute.dev/mc/mcp/internal/config"
|
||||
"git.wntrmute.dev/mc/mcp/internal/runtime"
|
||||
"git.wntrmute.dev/mc/mcp/internal/servicedef"
|
||||
)
|
||||
|
||||
func deployCmd() *cobra.Command {
|
||||
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
mcpv1 "git.wntrmute.dev/kyle/mcp/gen/mcp/v1"
|
||||
"git.wntrmute.dev/kyle/mcp/internal/config"
|
||||
mcpv1 "git.wntrmute.dev/mc/mcp/gen/mcp/v1"
|
||||
"git.wntrmute.dev/mc/mcp/internal/config"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/metadata"
|
||||
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
mcpv1 "git.wntrmute.dev/kyle/mcp/gen/mcp/v1"
|
||||
"git.wntrmute.dev/kyle/mcp/internal/config"
|
||||
mcpv1 "git.wntrmute.dev/mc/mcp/gen/mcp/v1"
|
||||
"git.wntrmute.dev/mc/mcp/internal/config"
|
||||
)
|
||||
|
||||
// findNodeAddress looks up a node by name in the CLI config and returns
|
||||
|
||||
@@ -7,9 +7,9 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
mcpv1 "git.wntrmute.dev/kyle/mcp/gen/mcp/v1"
|
||||
"git.wntrmute.dev/kyle/mcp/internal/config"
|
||||
"git.wntrmute.dev/kyle/mcp/internal/servicedef"
|
||||
mcpv1 "git.wntrmute.dev/mc/mcp/gen/mcp/v1"
|
||||
"git.wntrmute.dev/mc/mcp/internal/config"
|
||||
"git.wntrmute.dev/mc/mcp/internal/servicedef"
|
||||
)
|
||||
|
||||
func stopCmd() *cobra.Command {
|
||||
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"git.wntrmute.dev/kyle/mcp/internal/auth"
|
||||
"git.wntrmute.dev/kyle/mcp/internal/config"
|
||||
"git.wntrmute.dev/mc/mcp/internal/auth"
|
||||
"git.wntrmute.dev/mc/mcp/internal/config"
|
||||
)
|
||||
|
||||
func loginCmd() *cobra.Command {
|
||||
|
||||
@@ -36,6 +36,7 @@ func main() {
|
||||
root.AddCommand(loginCmd())
|
||||
root.AddCommand(buildCmd())
|
||||
root.AddCommand(deployCmd())
|
||||
root.AddCommand(undeployCmd())
|
||||
root.AddCommand(stopCmd())
|
||||
root.AddCommand(startCmd())
|
||||
root.AddCommand(restartCmd())
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
|
||||
toml "github.com/pelletier/go-toml/v2"
|
||||
|
||||
"git.wntrmute.dev/kyle/mcp/internal/config"
|
||||
"git.wntrmute.dev/mc/mcp/internal/config"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
mcpv1 "git.wntrmute.dev/kyle/mcp/gen/mcp/v1"
|
||||
"git.wntrmute.dev/kyle/mcp/internal/config"
|
||||
"git.wntrmute.dev/kyle/mcp/internal/servicedef"
|
||||
mcpv1 "git.wntrmute.dev/mc/mcp/gen/mcp/v1"
|
||||
"git.wntrmute.dev/mc/mcp/internal/config"
|
||||
"git.wntrmute.dev/mc/mcp/internal/servicedef"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -7,9 +7,9 @@ import (
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
mcpv1 "git.wntrmute.dev/kyle/mcp/gen/mcp/v1"
|
||||
"git.wntrmute.dev/kyle/mcp/internal/config"
|
||||
"git.wntrmute.dev/kyle/mcp/internal/servicedef"
|
||||
mcpv1 "git.wntrmute.dev/mc/mcp/gen/mcp/v1"
|
||||
"git.wntrmute.dev/mc/mcp/internal/config"
|
||||
"git.wntrmute.dev/mc/mcp/internal/servicedef"
|
||||
toml "github.com/pelletier/go-toml/v2"
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
@@ -7,8 +7,8 @@ import (
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
mcpv1 "git.wntrmute.dev/kyle/mcp/gen/mcp/v1"
|
||||
"git.wntrmute.dev/kyle/mcp/internal/config"
|
||||
mcpv1 "git.wntrmute.dev/mc/mcp/gen/mcp/v1"
|
||||
"git.wntrmute.dev/mc/mcp/internal/config"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
mcpv1 "git.wntrmute.dev/kyle/mcp/gen/mcp/v1"
|
||||
"git.wntrmute.dev/kyle/mcp/internal/config"
|
||||
"git.wntrmute.dev/kyle/mcp/internal/servicedef"
|
||||
mcpv1 "git.wntrmute.dev/mc/mcp/gen/mcp/v1"
|
||||
"git.wntrmute.dev/mc/mcp/internal/config"
|
||||
"git.wntrmute.dev/mc/mcp/internal/servicedef"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
mcpv1 "git.wntrmute.dev/kyle/mcp/gen/mcp/v1"
|
||||
"git.wntrmute.dev/kyle/mcp/internal/config"
|
||||
mcpv1 "git.wntrmute.dev/mc/mcp/gen/mcp/v1"
|
||||
"git.wntrmute.dev/mc/mcp/internal/config"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
|
||||
63
cmd/mcp/undeploy.go
Normal file
63
cmd/mcp/undeploy.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
mcpv1 "git.wntrmute.dev/mc/mcp/gen/mcp/v1"
|
||||
"git.wntrmute.dev/mc/mcp/internal/config"
|
||||
"git.wntrmute.dev/mc/mcp/internal/servicedef"
|
||||
)
|
||||
|
||||
func undeployCmd() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "undeploy <service>",
|
||||
Short: "Fully undeploy a service: remove routes, DNS, certs, and containers",
|
||||
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 := args[0]
|
||||
defPath := filepath.Join(cfg.Services.Dir, serviceName+".toml")
|
||||
|
||||
def, err := servicedef.Load(defPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("load service def: %w", err)
|
||||
}
|
||||
|
||||
// Set active=false in the local file.
|
||||
active := false
|
||||
def.Active = &active
|
||||
if err := servicedef.Write(defPath, def); err != nil {
|
||||
return fmt.Errorf("write service def: %w", err)
|
||||
}
|
||||
|
||||
address, err := findNodeAddress(cfg, def.Node)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, conn, err := dialAgent(address, cfg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("dial agent: %w", err)
|
||||
}
|
||||
defer func() { _ = conn.Close() }()
|
||||
|
||||
resp, err := client.UndeployService(context.Background(), &mcpv1.UndeployServiceRequest{
|
||||
Name: serviceName,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("undeploy service: %w", err)
|
||||
}
|
||||
|
||||
printComponentResults(resp.GetResults())
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
10
flake.nix
10
flake.nix
@@ -10,7 +10,7 @@
|
||||
let
|
||||
system = "x86_64-linux";
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
version = "0.1.0";
|
||||
version = "0.6.0";
|
||||
in
|
||||
{
|
||||
packages.${system} = {
|
||||
@@ -27,6 +27,14 @@
|
||||
"-w"
|
||||
"-X main.version=${version}"
|
||||
];
|
||||
postInstall = ''
|
||||
mkdir -p $out/share/zsh/site-functions
|
||||
mkdir -p $out/share/bash-completion/completions
|
||||
mkdir -p $out/share/fish/vendor_completions.d
|
||||
$out/bin/mcp completion zsh > $out/share/zsh/site-functions/_mcp
|
||||
$out/bin/mcp completion bash > $out/share/bash-completion/completions/mcp
|
||||
$out/bin/mcp completion fish > $out/share/fish/vendor_completions.d/mcp.fish
|
||||
'';
|
||||
};
|
||||
|
||||
mcp-agent = pkgs.buildGoModule {
|
||||
|
||||
@@ -25,7 +25,7 @@ const (
|
||||
type RouteSpec struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // route name (used for $PORT_<NAME>)
|
||||
Port int32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"` // external port on mc-proxy
|
||||
Port int32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"` // mc-proxy listener port (e.g. 443, 8443, 9443); NOT the container internal port
|
||||
Mode string `protobuf:"bytes,3,opt,name=mode,proto3" json:"mode,omitempty"` // "l4" or "l7"
|
||||
Hostname string `protobuf:"bytes,4,opt,name=hostname,proto3" json:"hostname,omitempty"` // optional public hostname override
|
||||
unknownFields protoimpl.UnknownFields
|
||||
@@ -687,6 +687,94 @@ func (x *RestartServiceResponse) GetResults() []*ComponentResult {
|
||||
return nil
|
||||
}
|
||||
|
||||
type UndeployServiceRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *UndeployServiceRequest) Reset() {
|
||||
*x = UndeployServiceRequest{}
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[12]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *UndeployServiceRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*UndeployServiceRequest) ProtoMessage() {}
|
||||
|
||||
func (x *UndeployServiceRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[12]
|
||||
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 UndeployServiceRequest.ProtoReflect.Descriptor instead.
|
||||
func (*UndeployServiceRequest) Descriptor() ([]byte, []int) {
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{12}
|
||||
}
|
||||
|
||||
func (x *UndeployServiceRequest) GetName() string {
|
||||
if x != nil {
|
||||
return x.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type UndeployServiceResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Results []*ComponentResult `protobuf:"bytes,1,rep,name=results,proto3" json:"results,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *UndeployServiceResponse) Reset() {
|
||||
*x = UndeployServiceResponse{}
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[13]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *UndeployServiceResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*UndeployServiceResponse) ProtoMessage() {}
|
||||
|
||||
func (x *UndeployServiceResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[13]
|
||||
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 UndeployServiceResponse.ProtoReflect.Descriptor instead.
|
||||
func (*UndeployServiceResponse) Descriptor() ([]byte, []int) {
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{13}
|
||||
}
|
||||
|
||||
func (x *UndeployServiceResponse) GetResults() []*ComponentResult {
|
||||
if x != nil {
|
||||
return x.Results
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type SyncDesiredStateRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// All services for this node.
|
||||
@@ -697,7 +785,7 @@ type SyncDesiredStateRequest struct {
|
||||
|
||||
func (x *SyncDesiredStateRequest) Reset() {
|
||||
*x = SyncDesiredStateRequest{}
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[12]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[14]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -709,7 +797,7 @@ func (x *SyncDesiredStateRequest) String() string {
|
||||
func (*SyncDesiredStateRequest) ProtoMessage() {}
|
||||
|
||||
func (x *SyncDesiredStateRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[12]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[14]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -722,7 +810,7 @@ func (x *SyncDesiredStateRequest) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use SyncDesiredStateRequest.ProtoReflect.Descriptor instead.
|
||||
func (*SyncDesiredStateRequest) Descriptor() ([]byte, []int) {
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{12}
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{14}
|
||||
}
|
||||
|
||||
func (x *SyncDesiredStateRequest) GetServices() []*ServiceSpec {
|
||||
@@ -741,7 +829,7 @@ type SyncDesiredStateResponse struct {
|
||||
|
||||
func (x *SyncDesiredStateResponse) Reset() {
|
||||
*x = SyncDesiredStateResponse{}
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[13]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[15]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -753,7 +841,7 @@ func (x *SyncDesiredStateResponse) String() string {
|
||||
func (*SyncDesiredStateResponse) ProtoMessage() {}
|
||||
|
||||
func (x *SyncDesiredStateResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[13]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[15]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -766,7 +854,7 @@ func (x *SyncDesiredStateResponse) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use SyncDesiredStateResponse.ProtoReflect.Descriptor instead.
|
||||
func (*SyncDesiredStateResponse) Descriptor() ([]byte, []int) {
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{13}
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{15}
|
||||
}
|
||||
|
||||
func (x *SyncDesiredStateResponse) GetResults() []*ServiceSyncResult {
|
||||
@@ -788,7 +876,7 @@ type ServiceSyncResult struct {
|
||||
|
||||
func (x *ServiceSyncResult) Reset() {
|
||||
*x = ServiceSyncResult{}
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[14]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[16]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -800,7 +888,7 @@ func (x *ServiceSyncResult) String() string {
|
||||
func (*ServiceSyncResult) ProtoMessage() {}
|
||||
|
||||
func (x *ServiceSyncResult) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[14]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[16]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -813,7 +901,7 @@ func (x *ServiceSyncResult) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use ServiceSyncResult.ProtoReflect.Descriptor instead.
|
||||
func (*ServiceSyncResult) Descriptor() ([]byte, []int) {
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{14}
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{16}
|
||||
}
|
||||
|
||||
func (x *ServiceSyncResult) GetName() string {
|
||||
@@ -845,7 +933,7 @@ type ListServicesRequest struct {
|
||||
|
||||
func (x *ListServicesRequest) Reset() {
|
||||
*x = ListServicesRequest{}
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[15]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[17]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -857,7 +945,7 @@ func (x *ListServicesRequest) String() string {
|
||||
func (*ListServicesRequest) ProtoMessage() {}
|
||||
|
||||
func (x *ListServicesRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[15]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[17]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -870,7 +958,7 @@ func (x *ListServicesRequest) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use ListServicesRequest.ProtoReflect.Descriptor instead.
|
||||
func (*ListServicesRequest) Descriptor() ([]byte, []int) {
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{15}
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{17}
|
||||
}
|
||||
|
||||
type ServiceInfo struct {
|
||||
@@ -884,7 +972,7 @@ type ServiceInfo struct {
|
||||
|
||||
func (x *ServiceInfo) Reset() {
|
||||
*x = ServiceInfo{}
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[16]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[18]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -896,7 +984,7 @@ func (x *ServiceInfo) String() string {
|
||||
func (*ServiceInfo) ProtoMessage() {}
|
||||
|
||||
func (x *ServiceInfo) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[16]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[18]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -909,7 +997,7 @@ func (x *ServiceInfo) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use ServiceInfo.ProtoReflect.Descriptor instead.
|
||||
func (*ServiceInfo) Descriptor() ([]byte, []int) {
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{16}
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{18}
|
||||
}
|
||||
|
||||
func (x *ServiceInfo) GetName() string {
|
||||
@@ -950,7 +1038,7 @@ type ComponentInfo struct {
|
||||
|
||||
func (x *ComponentInfo) Reset() {
|
||||
*x = ComponentInfo{}
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[17]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[19]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -962,7 +1050,7 @@ func (x *ComponentInfo) String() string {
|
||||
func (*ComponentInfo) ProtoMessage() {}
|
||||
|
||||
func (x *ComponentInfo) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[17]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[19]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -975,7 +1063,7 @@ func (x *ComponentInfo) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use ComponentInfo.ProtoReflect.Descriptor instead.
|
||||
func (*ComponentInfo) Descriptor() ([]byte, []int) {
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{17}
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{19}
|
||||
}
|
||||
|
||||
func (x *ComponentInfo) GetName() string {
|
||||
@@ -1029,7 +1117,7 @@ type ListServicesResponse struct {
|
||||
|
||||
func (x *ListServicesResponse) Reset() {
|
||||
*x = ListServicesResponse{}
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[18]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[20]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -1041,7 +1129,7 @@ func (x *ListServicesResponse) String() string {
|
||||
func (*ListServicesResponse) ProtoMessage() {}
|
||||
|
||||
func (x *ListServicesResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[18]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[20]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -1054,7 +1142,7 @@ func (x *ListServicesResponse) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use ListServicesResponse.ProtoReflect.Descriptor instead.
|
||||
func (*ListServicesResponse) Descriptor() ([]byte, []int) {
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{18}
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{20}
|
||||
}
|
||||
|
||||
func (x *ListServicesResponse) GetServices() []*ServiceInfo {
|
||||
@@ -1074,7 +1162,7 @@ type GetServiceStatusRequest struct {
|
||||
|
||||
func (x *GetServiceStatusRequest) Reset() {
|
||||
*x = GetServiceStatusRequest{}
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[19]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[21]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -1086,7 +1174,7 @@ func (x *GetServiceStatusRequest) String() string {
|
||||
func (*GetServiceStatusRequest) ProtoMessage() {}
|
||||
|
||||
func (x *GetServiceStatusRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[19]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[21]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -1099,7 +1187,7 @@ func (x *GetServiceStatusRequest) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use GetServiceStatusRequest.ProtoReflect.Descriptor instead.
|
||||
func (*GetServiceStatusRequest) Descriptor() ([]byte, []int) {
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{19}
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{21}
|
||||
}
|
||||
|
||||
func (x *GetServiceStatusRequest) GetName() string {
|
||||
@@ -1121,7 +1209,7 @@ type DriftInfo struct {
|
||||
|
||||
func (x *DriftInfo) Reset() {
|
||||
*x = DriftInfo{}
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[20]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[22]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -1133,7 +1221,7 @@ func (x *DriftInfo) String() string {
|
||||
func (*DriftInfo) ProtoMessage() {}
|
||||
|
||||
func (x *DriftInfo) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[20]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[22]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -1146,7 +1234,7 @@ func (x *DriftInfo) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use DriftInfo.ProtoReflect.Descriptor instead.
|
||||
func (*DriftInfo) Descriptor() ([]byte, []int) {
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{20}
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{22}
|
||||
}
|
||||
|
||||
func (x *DriftInfo) GetService() string {
|
||||
@@ -1190,7 +1278,7 @@ type EventInfo struct {
|
||||
|
||||
func (x *EventInfo) Reset() {
|
||||
*x = EventInfo{}
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[21]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[23]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -1202,7 +1290,7 @@ func (x *EventInfo) String() string {
|
||||
func (*EventInfo) ProtoMessage() {}
|
||||
|
||||
func (x *EventInfo) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[21]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[23]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -1215,7 +1303,7 @@ func (x *EventInfo) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use EventInfo.ProtoReflect.Descriptor instead.
|
||||
func (*EventInfo) Descriptor() ([]byte, []int) {
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{21}
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{23}
|
||||
}
|
||||
|
||||
func (x *EventInfo) GetService() string {
|
||||
@@ -1264,7 +1352,7 @@ type GetServiceStatusResponse struct {
|
||||
|
||||
func (x *GetServiceStatusResponse) Reset() {
|
||||
*x = GetServiceStatusResponse{}
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[22]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[24]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -1276,7 +1364,7 @@ func (x *GetServiceStatusResponse) String() string {
|
||||
func (*GetServiceStatusResponse) ProtoMessage() {}
|
||||
|
||||
func (x *GetServiceStatusResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[22]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[24]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -1289,7 +1377,7 @@ func (x *GetServiceStatusResponse) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use GetServiceStatusResponse.ProtoReflect.Descriptor instead.
|
||||
func (*GetServiceStatusResponse) Descriptor() ([]byte, []int) {
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{22}
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{24}
|
||||
}
|
||||
|
||||
func (x *GetServiceStatusResponse) GetServices() []*ServiceInfo {
|
||||
@@ -1321,7 +1409,7 @@ type LiveCheckRequest struct {
|
||||
|
||||
func (x *LiveCheckRequest) Reset() {
|
||||
*x = LiveCheckRequest{}
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[23]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[25]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -1333,7 +1421,7 @@ func (x *LiveCheckRequest) String() string {
|
||||
func (*LiveCheckRequest) ProtoMessage() {}
|
||||
|
||||
func (x *LiveCheckRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[23]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[25]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -1346,7 +1434,7 @@ func (x *LiveCheckRequest) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use LiveCheckRequest.ProtoReflect.Descriptor instead.
|
||||
func (*LiveCheckRequest) Descriptor() ([]byte, []int) {
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{23}
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{25}
|
||||
}
|
||||
|
||||
type LiveCheckResponse struct {
|
||||
@@ -1359,7 +1447,7 @@ type LiveCheckResponse struct {
|
||||
|
||||
func (x *LiveCheckResponse) Reset() {
|
||||
*x = LiveCheckResponse{}
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[24]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[26]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -1371,7 +1459,7 @@ func (x *LiveCheckResponse) String() string {
|
||||
func (*LiveCheckResponse) ProtoMessage() {}
|
||||
|
||||
func (x *LiveCheckResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[24]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[26]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -1384,7 +1472,7 @@ func (x *LiveCheckResponse) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use LiveCheckResponse.ProtoReflect.Descriptor instead.
|
||||
func (*LiveCheckResponse) Descriptor() ([]byte, []int) {
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{24}
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{26}
|
||||
}
|
||||
|
||||
func (x *LiveCheckResponse) GetServices() []*ServiceInfo {
|
||||
@@ -1404,7 +1492,7 @@ type AdoptContainersRequest struct {
|
||||
|
||||
func (x *AdoptContainersRequest) Reset() {
|
||||
*x = AdoptContainersRequest{}
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[25]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[27]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -1416,7 +1504,7 @@ func (x *AdoptContainersRequest) String() string {
|
||||
func (*AdoptContainersRequest) ProtoMessage() {}
|
||||
|
||||
func (x *AdoptContainersRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[25]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[27]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -1429,7 +1517,7 @@ func (x *AdoptContainersRequest) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use AdoptContainersRequest.ProtoReflect.Descriptor instead.
|
||||
func (*AdoptContainersRequest) Descriptor() ([]byte, []int) {
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{25}
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{27}
|
||||
}
|
||||
|
||||
func (x *AdoptContainersRequest) GetService() string {
|
||||
@@ -1453,7 +1541,7 @@ type AdoptResult struct {
|
||||
|
||||
func (x *AdoptResult) Reset() {
|
||||
*x = AdoptResult{}
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[26]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[28]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -1465,7 +1553,7 @@ func (x *AdoptResult) String() string {
|
||||
func (*AdoptResult) ProtoMessage() {}
|
||||
|
||||
func (x *AdoptResult) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[26]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[28]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -1478,7 +1566,7 @@ func (x *AdoptResult) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use AdoptResult.ProtoReflect.Descriptor instead.
|
||||
func (*AdoptResult) Descriptor() ([]byte, []int) {
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{26}
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{28}
|
||||
}
|
||||
|
||||
func (x *AdoptResult) GetContainer() string {
|
||||
@@ -1518,7 +1606,7 @@ type AdoptContainersResponse struct {
|
||||
|
||||
func (x *AdoptContainersResponse) Reset() {
|
||||
*x = AdoptContainersResponse{}
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[27]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[29]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -1530,7 +1618,7 @@ func (x *AdoptContainersResponse) String() string {
|
||||
func (*AdoptContainersResponse) ProtoMessage() {}
|
||||
|
||||
func (x *AdoptContainersResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[27]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[29]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -1543,7 +1631,7 @@ func (x *AdoptContainersResponse) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use AdoptContainersResponse.ProtoReflect.Descriptor instead.
|
||||
func (*AdoptContainersResponse) Descriptor() ([]byte, []int) {
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{27}
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{29}
|
||||
}
|
||||
|
||||
func (x *AdoptContainersResponse) GetResults() []*AdoptResult {
|
||||
@@ -1567,7 +1655,7 @@ type PushFileRequest struct {
|
||||
|
||||
func (x *PushFileRequest) Reset() {
|
||||
*x = PushFileRequest{}
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[28]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[30]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -1579,7 +1667,7 @@ func (x *PushFileRequest) String() string {
|
||||
func (*PushFileRequest) ProtoMessage() {}
|
||||
|
||||
func (x *PushFileRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[28]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[30]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -1592,7 +1680,7 @@ func (x *PushFileRequest) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use PushFileRequest.ProtoReflect.Descriptor instead.
|
||||
func (*PushFileRequest) Descriptor() ([]byte, []int) {
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{28}
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{30}
|
||||
}
|
||||
|
||||
func (x *PushFileRequest) GetService() string {
|
||||
@@ -1633,7 +1721,7 @@ type PushFileResponse struct {
|
||||
|
||||
func (x *PushFileResponse) Reset() {
|
||||
*x = PushFileResponse{}
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[29]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[31]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -1645,7 +1733,7 @@ func (x *PushFileResponse) String() string {
|
||||
func (*PushFileResponse) ProtoMessage() {}
|
||||
|
||||
func (x *PushFileResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[29]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[31]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -1658,7 +1746,7 @@ func (x *PushFileResponse) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use PushFileResponse.ProtoReflect.Descriptor instead.
|
||||
func (*PushFileResponse) Descriptor() ([]byte, []int) {
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{29}
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{31}
|
||||
}
|
||||
|
||||
func (x *PushFileResponse) GetSuccess() bool {
|
||||
@@ -1686,7 +1774,7 @@ type PullFileRequest struct {
|
||||
|
||||
func (x *PullFileRequest) Reset() {
|
||||
*x = PullFileRequest{}
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[30]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[32]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -1698,7 +1786,7 @@ func (x *PullFileRequest) String() string {
|
||||
func (*PullFileRequest) ProtoMessage() {}
|
||||
|
||||
func (x *PullFileRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[30]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[32]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -1711,7 +1799,7 @@ func (x *PullFileRequest) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use PullFileRequest.ProtoReflect.Descriptor instead.
|
||||
func (*PullFileRequest) Descriptor() ([]byte, []int) {
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{30}
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{32}
|
||||
}
|
||||
|
||||
func (x *PullFileRequest) GetService() string {
|
||||
@@ -1739,7 +1827,7 @@ type PullFileResponse struct {
|
||||
|
||||
func (x *PullFileResponse) Reset() {
|
||||
*x = PullFileResponse{}
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[31]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[33]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -1751,7 +1839,7 @@ func (x *PullFileResponse) String() string {
|
||||
func (*PullFileResponse) ProtoMessage() {}
|
||||
|
||||
func (x *PullFileResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[31]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[33]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -1764,7 +1852,7 @@ func (x *PullFileResponse) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use PullFileResponse.ProtoReflect.Descriptor instead.
|
||||
func (*PullFileResponse) Descriptor() ([]byte, []int) {
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{31}
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{33}
|
||||
}
|
||||
|
||||
func (x *PullFileResponse) GetContent() []byte {
|
||||
@@ -1796,7 +1884,7 @@ type NodeStatusRequest struct {
|
||||
|
||||
func (x *NodeStatusRequest) Reset() {
|
||||
*x = NodeStatusRequest{}
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[32]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[34]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -1808,7 +1896,7 @@ func (x *NodeStatusRequest) String() string {
|
||||
func (*NodeStatusRequest) ProtoMessage() {}
|
||||
|
||||
func (x *NodeStatusRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[32]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[34]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -1821,7 +1909,7 @@ func (x *NodeStatusRequest) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use NodeStatusRequest.ProtoReflect.Descriptor instead.
|
||||
func (*NodeStatusRequest) Descriptor() ([]byte, []int) {
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{32}
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{34}
|
||||
}
|
||||
|
||||
type NodeStatusResponse struct {
|
||||
@@ -1844,7 +1932,7 @@ type NodeStatusResponse struct {
|
||||
|
||||
func (x *NodeStatusResponse) Reset() {
|
||||
*x = NodeStatusResponse{}
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[33]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[35]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -1856,7 +1944,7 @@ func (x *NodeStatusResponse) String() string {
|
||||
func (*NodeStatusResponse) ProtoMessage() {}
|
||||
|
||||
func (x *NodeStatusResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[33]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[35]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -1869,7 +1957,7 @@ func (x *NodeStatusResponse) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use NodeStatusResponse.ProtoReflect.Descriptor instead.
|
||||
func (*NodeStatusResponse) Descriptor() ([]byte, []int) {
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{33}
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{35}
|
||||
}
|
||||
|
||||
func (x *NodeStatusResponse) GetNodeName() string {
|
||||
@@ -1966,7 +2054,7 @@ type PurgeRequest struct {
|
||||
|
||||
func (x *PurgeRequest) Reset() {
|
||||
*x = PurgeRequest{}
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[34]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[36]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -1978,7 +2066,7 @@ func (x *PurgeRequest) String() string {
|
||||
func (*PurgeRequest) ProtoMessage() {}
|
||||
|
||||
func (x *PurgeRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[34]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[36]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -1991,7 +2079,7 @@ func (x *PurgeRequest) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use PurgeRequest.ProtoReflect.Descriptor instead.
|
||||
func (*PurgeRequest) Descriptor() ([]byte, []int) {
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{34}
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{36}
|
||||
}
|
||||
|
||||
func (x *PurgeRequest) GetService() string {
|
||||
@@ -2031,7 +2119,7 @@ type PurgeResponse struct {
|
||||
|
||||
func (x *PurgeResponse) Reset() {
|
||||
*x = PurgeResponse{}
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[35]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[37]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -2043,7 +2131,7 @@ func (x *PurgeResponse) String() string {
|
||||
func (*PurgeResponse) ProtoMessage() {}
|
||||
|
||||
func (x *PurgeResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[35]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[37]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -2056,7 +2144,7 @@ func (x *PurgeResponse) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use PurgeResponse.ProtoReflect.Descriptor instead.
|
||||
func (*PurgeResponse) Descriptor() ([]byte, []int) {
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{35}
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{37}
|
||||
}
|
||||
|
||||
func (x *PurgeResponse) GetResults() []*PurgeResult {
|
||||
@@ -2080,7 +2168,7 @@ type PurgeResult struct {
|
||||
|
||||
func (x *PurgeResult) Reset() {
|
||||
*x = PurgeResult{}
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[36]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[38]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -2092,7 +2180,7 @@ func (x *PurgeResult) String() string {
|
||||
func (*PurgeResult) ProtoMessage() {}
|
||||
|
||||
func (x *PurgeResult) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[36]
|
||||
mi := &file_proto_mcp_v1_mcp_proto_msgTypes[38]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -2105,7 +2193,7 @@ func (x *PurgeResult) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use PurgeResult.ProtoReflect.Descriptor instead.
|
||||
func (*PurgeResult) Descriptor() ([]byte, []int) {
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{36}
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescGZIP(), []int{38}
|
||||
}
|
||||
|
||||
func (x *PurgeResult) GetService() string {
|
||||
@@ -2184,6 +2272,10 @@ const file_proto_mcp_v1_mcp_proto_rawDesc = "" +
|
||||
"\x15RestartServiceRequest\x12\x12\n" +
|
||||
"\x04name\x18\x01 \x01(\tR\x04name\"K\n" +
|
||||
"\x16RestartServiceResponse\x121\n" +
|
||||
"\aresults\x18\x01 \x03(\v2\x17.mcp.v1.ComponentResultR\aresults\",\n" +
|
||||
"\x16UndeployServiceRequest\x12\x12\n" +
|
||||
"\x04name\x18\x01 \x01(\tR\x04name\"L\n" +
|
||||
"\x17UndeployServiceResponse\x121\n" +
|
||||
"\aresults\x18\x01 \x03(\v2\x17.mcp.v1.ComponentResultR\aresults\"J\n" +
|
||||
"\x17SyncDesiredStateRequest\x12/\n" +
|
||||
"\bservices\x18\x01 \x03(\v2\x13.mcp.v1.ServiceSpecR\bservices\"O\n" +
|
||||
@@ -2279,9 +2371,10 @@ const file_proto_mcp_v1_mcp_proto_rawDesc = "" +
|
||||
"\aservice\x18\x01 \x01(\tR\aservice\x12\x1c\n" +
|
||||
"\tcomponent\x18\x02 \x01(\tR\tcomponent\x12\x16\n" +
|
||||
"\x06purged\x18\x03 \x01(\bR\x06purged\x12\x16\n" +
|
||||
"\x06reason\x18\x04 \x01(\tR\x06reason2\xbf\a\n" +
|
||||
"\x06reason\x18\x04 \x01(\tR\x06reason2\x93\b\n" +
|
||||
"\x0fMcpAgentService\x127\n" +
|
||||
"\x06Deploy\x12\x15.mcp.v1.DeployRequest\x1a\x16.mcp.v1.DeployResponse\x12F\n" +
|
||||
"\x06Deploy\x12\x15.mcp.v1.DeployRequest\x1a\x16.mcp.v1.DeployResponse\x12R\n" +
|
||||
"\x0fUndeployService\x12\x1e.mcp.v1.UndeployServiceRequest\x1a\x1f.mcp.v1.UndeployServiceResponse\x12F\n" +
|
||||
"\vStopService\x12\x1a.mcp.v1.StopServiceRequest\x1a\x1b.mcp.v1.StopServiceResponse\x12I\n" +
|
||||
"\fStartService\x12\x1b.mcp.v1.StartServiceRequest\x1a\x1c.mcp.v1.StartServiceResponse\x12O\n" +
|
||||
"\x0eRestartService\x12\x1d.mcp.v1.RestartServiceRequest\x1a\x1e.mcp.v1.RestartServiceResponse\x12U\n" +
|
||||
@@ -2294,7 +2387,7 @@ const file_proto_mcp_v1_mcp_proto_rawDesc = "" +
|
||||
"\bPushFile\x12\x17.mcp.v1.PushFileRequest\x1a\x18.mcp.v1.PushFileResponse\x12=\n" +
|
||||
"\bPullFile\x12\x17.mcp.v1.PullFileRequest\x1a\x18.mcp.v1.PullFileResponse\x12C\n" +
|
||||
"\n" +
|
||||
"NodeStatus\x12\x19.mcp.v1.NodeStatusRequest\x1a\x1a.mcp.v1.NodeStatusResponseB,Z*git.wntrmute.dev/kyle/mcp/gen/mcp/v1;mcpv1b\x06proto3"
|
||||
"NodeStatus\x12\x19.mcp.v1.NodeStatusRequest\x1a\x1a.mcp.v1.NodeStatusResponseB*Z(git.wntrmute.dev/mc/mcp/gen/mcp/v1;mcpv1b\x06proto3"
|
||||
|
||||
var (
|
||||
file_proto_mcp_v1_mcp_proto_rawDescOnce sync.Once
|
||||
@@ -2308,7 +2401,7 @@ func file_proto_mcp_v1_mcp_proto_rawDescGZIP() []byte {
|
||||
return file_proto_mcp_v1_mcp_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_proto_mcp_v1_mcp_proto_msgTypes = make([]protoimpl.MessageInfo, 37)
|
||||
var file_proto_mcp_v1_mcp_proto_msgTypes = make([]protoimpl.MessageInfo, 39)
|
||||
var file_proto_mcp_v1_mcp_proto_goTypes = []any{
|
||||
(*RouteSpec)(nil), // 0: mcp.v1.RouteSpec
|
||||
(*ComponentSpec)(nil), // 1: mcp.v1.ComponentSpec
|
||||
@@ -2322,32 +2415,34 @@ var file_proto_mcp_v1_mcp_proto_goTypes = []any{
|
||||
(*StartServiceResponse)(nil), // 9: mcp.v1.StartServiceResponse
|
||||
(*RestartServiceRequest)(nil), // 10: mcp.v1.RestartServiceRequest
|
||||
(*RestartServiceResponse)(nil), // 11: mcp.v1.RestartServiceResponse
|
||||
(*SyncDesiredStateRequest)(nil), // 12: mcp.v1.SyncDesiredStateRequest
|
||||
(*SyncDesiredStateResponse)(nil), // 13: mcp.v1.SyncDesiredStateResponse
|
||||
(*ServiceSyncResult)(nil), // 14: mcp.v1.ServiceSyncResult
|
||||
(*ListServicesRequest)(nil), // 15: mcp.v1.ListServicesRequest
|
||||
(*ServiceInfo)(nil), // 16: mcp.v1.ServiceInfo
|
||||
(*ComponentInfo)(nil), // 17: mcp.v1.ComponentInfo
|
||||
(*ListServicesResponse)(nil), // 18: mcp.v1.ListServicesResponse
|
||||
(*GetServiceStatusRequest)(nil), // 19: mcp.v1.GetServiceStatusRequest
|
||||
(*DriftInfo)(nil), // 20: mcp.v1.DriftInfo
|
||||
(*EventInfo)(nil), // 21: mcp.v1.EventInfo
|
||||
(*GetServiceStatusResponse)(nil), // 22: mcp.v1.GetServiceStatusResponse
|
||||
(*LiveCheckRequest)(nil), // 23: mcp.v1.LiveCheckRequest
|
||||
(*LiveCheckResponse)(nil), // 24: mcp.v1.LiveCheckResponse
|
||||
(*AdoptContainersRequest)(nil), // 25: mcp.v1.AdoptContainersRequest
|
||||
(*AdoptResult)(nil), // 26: mcp.v1.AdoptResult
|
||||
(*AdoptContainersResponse)(nil), // 27: mcp.v1.AdoptContainersResponse
|
||||
(*PushFileRequest)(nil), // 28: mcp.v1.PushFileRequest
|
||||
(*PushFileResponse)(nil), // 29: mcp.v1.PushFileResponse
|
||||
(*PullFileRequest)(nil), // 30: mcp.v1.PullFileRequest
|
||||
(*PullFileResponse)(nil), // 31: mcp.v1.PullFileResponse
|
||||
(*NodeStatusRequest)(nil), // 32: mcp.v1.NodeStatusRequest
|
||||
(*NodeStatusResponse)(nil), // 33: mcp.v1.NodeStatusResponse
|
||||
(*PurgeRequest)(nil), // 34: mcp.v1.PurgeRequest
|
||||
(*PurgeResponse)(nil), // 35: mcp.v1.PurgeResponse
|
||||
(*PurgeResult)(nil), // 36: mcp.v1.PurgeResult
|
||||
(*timestamppb.Timestamp)(nil), // 37: google.protobuf.Timestamp
|
||||
(*UndeployServiceRequest)(nil), // 12: mcp.v1.UndeployServiceRequest
|
||||
(*UndeployServiceResponse)(nil), // 13: mcp.v1.UndeployServiceResponse
|
||||
(*SyncDesiredStateRequest)(nil), // 14: mcp.v1.SyncDesiredStateRequest
|
||||
(*SyncDesiredStateResponse)(nil), // 15: mcp.v1.SyncDesiredStateResponse
|
||||
(*ServiceSyncResult)(nil), // 16: mcp.v1.ServiceSyncResult
|
||||
(*ListServicesRequest)(nil), // 17: mcp.v1.ListServicesRequest
|
||||
(*ServiceInfo)(nil), // 18: mcp.v1.ServiceInfo
|
||||
(*ComponentInfo)(nil), // 19: mcp.v1.ComponentInfo
|
||||
(*ListServicesResponse)(nil), // 20: mcp.v1.ListServicesResponse
|
||||
(*GetServiceStatusRequest)(nil), // 21: mcp.v1.GetServiceStatusRequest
|
||||
(*DriftInfo)(nil), // 22: mcp.v1.DriftInfo
|
||||
(*EventInfo)(nil), // 23: mcp.v1.EventInfo
|
||||
(*GetServiceStatusResponse)(nil), // 24: mcp.v1.GetServiceStatusResponse
|
||||
(*LiveCheckRequest)(nil), // 25: mcp.v1.LiveCheckRequest
|
||||
(*LiveCheckResponse)(nil), // 26: mcp.v1.LiveCheckResponse
|
||||
(*AdoptContainersRequest)(nil), // 27: mcp.v1.AdoptContainersRequest
|
||||
(*AdoptResult)(nil), // 28: mcp.v1.AdoptResult
|
||||
(*AdoptContainersResponse)(nil), // 29: mcp.v1.AdoptContainersResponse
|
||||
(*PushFileRequest)(nil), // 30: mcp.v1.PushFileRequest
|
||||
(*PushFileResponse)(nil), // 31: mcp.v1.PushFileResponse
|
||||
(*PullFileRequest)(nil), // 32: mcp.v1.PullFileRequest
|
||||
(*PullFileResponse)(nil), // 33: mcp.v1.PullFileResponse
|
||||
(*NodeStatusRequest)(nil), // 34: mcp.v1.NodeStatusRequest
|
||||
(*NodeStatusResponse)(nil), // 35: mcp.v1.NodeStatusResponse
|
||||
(*PurgeRequest)(nil), // 36: mcp.v1.PurgeRequest
|
||||
(*PurgeResponse)(nil), // 37: mcp.v1.PurgeResponse
|
||||
(*PurgeResult)(nil), // 38: mcp.v1.PurgeResult
|
||||
(*timestamppb.Timestamp)(nil), // 39: google.protobuf.Timestamp
|
||||
}
|
||||
var file_proto_mcp_v1_mcp_proto_depIdxs = []int32{
|
||||
0, // 0: mcp.v1.ComponentSpec.routes:type_name -> mcp.v1.RouteSpec
|
||||
@@ -2357,50 +2452,53 @@ var file_proto_mcp_v1_mcp_proto_depIdxs = []int32{
|
||||
5, // 4: mcp.v1.StopServiceResponse.results:type_name -> mcp.v1.ComponentResult
|
||||
5, // 5: mcp.v1.StartServiceResponse.results:type_name -> mcp.v1.ComponentResult
|
||||
5, // 6: mcp.v1.RestartServiceResponse.results:type_name -> mcp.v1.ComponentResult
|
||||
2, // 7: mcp.v1.SyncDesiredStateRequest.services:type_name -> mcp.v1.ServiceSpec
|
||||
14, // 8: mcp.v1.SyncDesiredStateResponse.results:type_name -> mcp.v1.ServiceSyncResult
|
||||
17, // 9: mcp.v1.ServiceInfo.components:type_name -> mcp.v1.ComponentInfo
|
||||
37, // 10: mcp.v1.ComponentInfo.started:type_name -> google.protobuf.Timestamp
|
||||
16, // 11: mcp.v1.ListServicesResponse.services:type_name -> mcp.v1.ServiceInfo
|
||||
37, // 12: mcp.v1.EventInfo.timestamp:type_name -> google.protobuf.Timestamp
|
||||
16, // 13: mcp.v1.GetServiceStatusResponse.services:type_name -> mcp.v1.ServiceInfo
|
||||
20, // 14: mcp.v1.GetServiceStatusResponse.drift:type_name -> mcp.v1.DriftInfo
|
||||
21, // 15: mcp.v1.GetServiceStatusResponse.recent_events:type_name -> mcp.v1.EventInfo
|
||||
16, // 16: mcp.v1.LiveCheckResponse.services:type_name -> mcp.v1.ServiceInfo
|
||||
26, // 17: mcp.v1.AdoptContainersResponse.results:type_name -> mcp.v1.AdoptResult
|
||||
37, // 18: mcp.v1.NodeStatusResponse.uptime_since:type_name -> google.protobuf.Timestamp
|
||||
36, // 19: mcp.v1.PurgeResponse.results:type_name -> mcp.v1.PurgeResult
|
||||
3, // 20: mcp.v1.McpAgentService.Deploy:input_type -> mcp.v1.DeployRequest
|
||||
6, // 21: mcp.v1.McpAgentService.StopService:input_type -> mcp.v1.StopServiceRequest
|
||||
8, // 22: mcp.v1.McpAgentService.StartService:input_type -> mcp.v1.StartServiceRequest
|
||||
10, // 23: mcp.v1.McpAgentService.RestartService:input_type -> mcp.v1.RestartServiceRequest
|
||||
12, // 24: mcp.v1.McpAgentService.SyncDesiredState:input_type -> mcp.v1.SyncDesiredStateRequest
|
||||
15, // 25: mcp.v1.McpAgentService.ListServices:input_type -> mcp.v1.ListServicesRequest
|
||||
19, // 26: mcp.v1.McpAgentService.GetServiceStatus:input_type -> mcp.v1.GetServiceStatusRequest
|
||||
23, // 27: mcp.v1.McpAgentService.LiveCheck:input_type -> mcp.v1.LiveCheckRequest
|
||||
25, // 28: mcp.v1.McpAgentService.AdoptContainers:input_type -> mcp.v1.AdoptContainersRequest
|
||||
34, // 29: mcp.v1.McpAgentService.PurgeComponent:input_type -> mcp.v1.PurgeRequest
|
||||
28, // 30: mcp.v1.McpAgentService.PushFile:input_type -> mcp.v1.PushFileRequest
|
||||
30, // 31: mcp.v1.McpAgentService.PullFile:input_type -> mcp.v1.PullFileRequest
|
||||
32, // 32: mcp.v1.McpAgentService.NodeStatus:input_type -> mcp.v1.NodeStatusRequest
|
||||
4, // 33: mcp.v1.McpAgentService.Deploy:output_type -> mcp.v1.DeployResponse
|
||||
7, // 34: mcp.v1.McpAgentService.StopService:output_type -> mcp.v1.StopServiceResponse
|
||||
9, // 35: mcp.v1.McpAgentService.StartService:output_type -> mcp.v1.StartServiceResponse
|
||||
11, // 36: mcp.v1.McpAgentService.RestartService:output_type -> mcp.v1.RestartServiceResponse
|
||||
13, // 37: mcp.v1.McpAgentService.SyncDesiredState:output_type -> mcp.v1.SyncDesiredStateResponse
|
||||
18, // 38: mcp.v1.McpAgentService.ListServices:output_type -> mcp.v1.ListServicesResponse
|
||||
22, // 39: mcp.v1.McpAgentService.GetServiceStatus:output_type -> mcp.v1.GetServiceStatusResponse
|
||||
24, // 40: mcp.v1.McpAgentService.LiveCheck:output_type -> mcp.v1.LiveCheckResponse
|
||||
27, // 41: mcp.v1.McpAgentService.AdoptContainers:output_type -> mcp.v1.AdoptContainersResponse
|
||||
35, // 42: mcp.v1.McpAgentService.PurgeComponent:output_type -> mcp.v1.PurgeResponse
|
||||
29, // 43: mcp.v1.McpAgentService.PushFile:output_type -> mcp.v1.PushFileResponse
|
||||
31, // 44: mcp.v1.McpAgentService.PullFile:output_type -> mcp.v1.PullFileResponse
|
||||
33, // 45: mcp.v1.McpAgentService.NodeStatus:output_type -> mcp.v1.NodeStatusResponse
|
||||
33, // [33:46] is the sub-list for method output_type
|
||||
20, // [20:33] is the sub-list for method input_type
|
||||
20, // [20:20] is the sub-list for extension type_name
|
||||
20, // [20:20] is the sub-list for extension extendee
|
||||
0, // [0:20] is the sub-list for field type_name
|
||||
5, // 7: mcp.v1.UndeployServiceResponse.results:type_name -> mcp.v1.ComponentResult
|
||||
2, // 8: mcp.v1.SyncDesiredStateRequest.services:type_name -> mcp.v1.ServiceSpec
|
||||
16, // 9: mcp.v1.SyncDesiredStateResponse.results:type_name -> mcp.v1.ServiceSyncResult
|
||||
19, // 10: mcp.v1.ServiceInfo.components:type_name -> mcp.v1.ComponentInfo
|
||||
39, // 11: mcp.v1.ComponentInfo.started:type_name -> google.protobuf.Timestamp
|
||||
18, // 12: mcp.v1.ListServicesResponse.services:type_name -> mcp.v1.ServiceInfo
|
||||
39, // 13: mcp.v1.EventInfo.timestamp:type_name -> google.protobuf.Timestamp
|
||||
18, // 14: mcp.v1.GetServiceStatusResponse.services:type_name -> mcp.v1.ServiceInfo
|
||||
22, // 15: mcp.v1.GetServiceStatusResponse.drift:type_name -> mcp.v1.DriftInfo
|
||||
23, // 16: mcp.v1.GetServiceStatusResponse.recent_events:type_name -> mcp.v1.EventInfo
|
||||
18, // 17: mcp.v1.LiveCheckResponse.services:type_name -> mcp.v1.ServiceInfo
|
||||
28, // 18: mcp.v1.AdoptContainersResponse.results:type_name -> mcp.v1.AdoptResult
|
||||
39, // 19: mcp.v1.NodeStatusResponse.uptime_since:type_name -> google.protobuf.Timestamp
|
||||
38, // 20: mcp.v1.PurgeResponse.results:type_name -> mcp.v1.PurgeResult
|
||||
3, // 21: mcp.v1.McpAgentService.Deploy:input_type -> mcp.v1.DeployRequest
|
||||
12, // 22: mcp.v1.McpAgentService.UndeployService:input_type -> mcp.v1.UndeployServiceRequest
|
||||
6, // 23: mcp.v1.McpAgentService.StopService:input_type -> mcp.v1.StopServiceRequest
|
||||
8, // 24: mcp.v1.McpAgentService.StartService:input_type -> mcp.v1.StartServiceRequest
|
||||
10, // 25: mcp.v1.McpAgentService.RestartService:input_type -> mcp.v1.RestartServiceRequest
|
||||
14, // 26: mcp.v1.McpAgentService.SyncDesiredState:input_type -> mcp.v1.SyncDesiredStateRequest
|
||||
17, // 27: mcp.v1.McpAgentService.ListServices:input_type -> mcp.v1.ListServicesRequest
|
||||
21, // 28: mcp.v1.McpAgentService.GetServiceStatus:input_type -> mcp.v1.GetServiceStatusRequest
|
||||
25, // 29: mcp.v1.McpAgentService.LiveCheck:input_type -> mcp.v1.LiveCheckRequest
|
||||
27, // 30: mcp.v1.McpAgentService.AdoptContainers:input_type -> mcp.v1.AdoptContainersRequest
|
||||
36, // 31: mcp.v1.McpAgentService.PurgeComponent:input_type -> mcp.v1.PurgeRequest
|
||||
30, // 32: mcp.v1.McpAgentService.PushFile:input_type -> mcp.v1.PushFileRequest
|
||||
32, // 33: mcp.v1.McpAgentService.PullFile:input_type -> mcp.v1.PullFileRequest
|
||||
34, // 34: mcp.v1.McpAgentService.NodeStatus:input_type -> mcp.v1.NodeStatusRequest
|
||||
4, // 35: mcp.v1.McpAgentService.Deploy:output_type -> mcp.v1.DeployResponse
|
||||
13, // 36: mcp.v1.McpAgentService.UndeployService:output_type -> mcp.v1.UndeployServiceResponse
|
||||
7, // 37: mcp.v1.McpAgentService.StopService:output_type -> mcp.v1.StopServiceResponse
|
||||
9, // 38: mcp.v1.McpAgentService.StartService:output_type -> mcp.v1.StartServiceResponse
|
||||
11, // 39: mcp.v1.McpAgentService.RestartService:output_type -> mcp.v1.RestartServiceResponse
|
||||
15, // 40: mcp.v1.McpAgentService.SyncDesiredState:output_type -> mcp.v1.SyncDesiredStateResponse
|
||||
20, // 41: mcp.v1.McpAgentService.ListServices:output_type -> mcp.v1.ListServicesResponse
|
||||
24, // 42: mcp.v1.McpAgentService.GetServiceStatus:output_type -> mcp.v1.GetServiceStatusResponse
|
||||
26, // 43: mcp.v1.McpAgentService.LiveCheck:output_type -> mcp.v1.LiveCheckResponse
|
||||
29, // 44: mcp.v1.McpAgentService.AdoptContainers:output_type -> mcp.v1.AdoptContainersResponse
|
||||
37, // 45: mcp.v1.McpAgentService.PurgeComponent:output_type -> mcp.v1.PurgeResponse
|
||||
31, // 46: mcp.v1.McpAgentService.PushFile:output_type -> mcp.v1.PushFileResponse
|
||||
33, // 47: mcp.v1.McpAgentService.PullFile:output_type -> mcp.v1.PullFileResponse
|
||||
35, // 48: mcp.v1.McpAgentService.NodeStatus:output_type -> mcp.v1.NodeStatusResponse
|
||||
35, // [35:49] is the sub-list for method output_type
|
||||
21, // [21:35] is the sub-list for method input_type
|
||||
21, // [21:21] is the sub-list for extension type_name
|
||||
21, // [21:21] is the sub-list for extension extendee
|
||||
0, // [0:21] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_proto_mcp_v1_mcp_proto_init() }
|
||||
@@ -2414,7 +2512,7 @@ func file_proto_mcp_v1_mcp_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_proto_mcp_v1_mcp_proto_rawDesc), len(file_proto_mcp_v1_mcp_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 37,
|
||||
NumMessages: 39,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
|
||||
@@ -20,6 +20,7 @@ const _ = grpc.SupportPackageIsVersion9
|
||||
|
||||
const (
|
||||
McpAgentService_Deploy_FullMethodName = "/mcp.v1.McpAgentService/Deploy"
|
||||
McpAgentService_UndeployService_FullMethodName = "/mcp.v1.McpAgentService/UndeployService"
|
||||
McpAgentService_StopService_FullMethodName = "/mcp.v1.McpAgentService/StopService"
|
||||
McpAgentService_StartService_FullMethodName = "/mcp.v1.McpAgentService/StartService"
|
||||
McpAgentService_RestartService_FullMethodName = "/mcp.v1.McpAgentService/RestartService"
|
||||
@@ -40,6 +41,7 @@ const (
|
||||
type McpAgentServiceClient interface {
|
||||
// Service lifecycle
|
||||
Deploy(ctx context.Context, in *DeployRequest, opts ...grpc.CallOption) (*DeployResponse, error)
|
||||
UndeployService(ctx context.Context, in *UndeployServiceRequest, opts ...grpc.CallOption) (*UndeployServiceResponse, error)
|
||||
StopService(ctx context.Context, in *StopServiceRequest, opts ...grpc.CallOption) (*StopServiceResponse, error)
|
||||
StartService(ctx context.Context, in *StartServiceRequest, opts ...grpc.CallOption) (*StartServiceResponse, error)
|
||||
RestartService(ctx context.Context, in *RestartServiceRequest, opts ...grpc.CallOption) (*RestartServiceResponse, error)
|
||||
@@ -78,6 +80,16 @@ func (c *mcpAgentServiceClient) Deploy(ctx context.Context, in *DeployRequest, o
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *mcpAgentServiceClient) UndeployService(ctx context.Context, in *UndeployServiceRequest, opts ...grpc.CallOption) (*UndeployServiceResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(UndeployServiceResponse)
|
||||
err := c.cc.Invoke(ctx, McpAgentService_UndeployService_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *mcpAgentServiceClient) StopService(ctx context.Context, in *StopServiceRequest, opts ...grpc.CallOption) (*StopServiceResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(StopServiceResponse)
|
||||
@@ -204,6 +216,7 @@ func (c *mcpAgentServiceClient) NodeStatus(ctx context.Context, in *NodeStatusRe
|
||||
type McpAgentServiceServer interface {
|
||||
// Service lifecycle
|
||||
Deploy(context.Context, *DeployRequest) (*DeployResponse, error)
|
||||
UndeployService(context.Context, *UndeployServiceRequest) (*UndeployServiceResponse, error)
|
||||
StopService(context.Context, *StopServiceRequest) (*StopServiceResponse, error)
|
||||
StartService(context.Context, *StartServiceRequest) (*StartServiceResponse, error)
|
||||
RestartService(context.Context, *RestartServiceRequest) (*RestartServiceResponse, error)
|
||||
@@ -235,6 +248,9 @@ type UnimplementedMcpAgentServiceServer struct{}
|
||||
func (UnimplementedMcpAgentServiceServer) Deploy(context.Context, *DeployRequest) (*DeployResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method Deploy not implemented")
|
||||
}
|
||||
func (UnimplementedMcpAgentServiceServer) UndeployService(context.Context, *UndeployServiceRequest) (*UndeployServiceResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method UndeployService not implemented")
|
||||
}
|
||||
func (UnimplementedMcpAgentServiceServer) StopService(context.Context, *StopServiceRequest) (*StopServiceResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method StopService not implemented")
|
||||
}
|
||||
@@ -310,6 +326,24 @@ func _McpAgentService_Deploy_Handler(srv interface{}, ctx context.Context, dec f
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _McpAgentService_UndeployService_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(UndeployServiceRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(McpAgentServiceServer).UndeployService(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: McpAgentService_UndeployService_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(McpAgentServiceServer).UndeployService(ctx, req.(*UndeployServiceRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _McpAgentService_StopService_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(StopServiceRequest)
|
||||
if err := dec(in); err != nil {
|
||||
@@ -537,6 +571,10 @@ var McpAgentService_ServiceDesc = grpc.ServiceDesc{
|
||||
MethodName: "Deploy",
|
||||
Handler: _McpAgentService_Deploy_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "UndeployService",
|
||||
Handler: _McpAgentService_UndeployService_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "StopService",
|
||||
Handler: _McpAgentService_StopService_Handler,
|
||||
|
||||
3
go.mod
3
go.mod
@@ -1,8 +1,9 @@
|
||||
module git.wntrmute.dev/kyle/mcp
|
||||
module git.wntrmute.dev/mc/mcp
|
||||
|
||||
go 1.25.7
|
||||
|
||||
require (
|
||||
git.wntrmute.dev/mc/mc-proxy v1.2.0
|
||||
github.com/pelletier/go-toml/v2 v2.3.0
|
||||
github.com/spf13/cobra v1.10.2
|
||||
golang.org/x/sys v0.42.0
|
||||
|
||||
20
go.sum
20
go.sum
@@ -1,3 +1,9 @@
|
||||
git.wntrmute.dev/mc/mc-proxy v1.2.0 h1:TVfwdZzYqMs/ksZ0a6aSR7hKGDDMG8X0Od5RIxlbXKQ=
|
||||
git.wntrmute.dev/mc/mc-proxy v1.2.0/go.mod h1:6w8smZ/DNJVBb4n5std/faye0ROLEXfk3iJY1XNc1JU=
|
||||
git.wntrmute.dev/mc/mcdsl v1.2.0 h1:41hep7/PNZJfN0SN/nM+rQpyF1GSZcvNNjyVG81DI7U=
|
||||
git.wntrmute.dev/mc/mcdsl v1.2.0/go.mod h1:lXYrAt74ZUix6rx9oVN8d2zH1YJoyp4uxPVKQ+SSxuM=
|
||||
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/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
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/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/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/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/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/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
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/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
|
||||
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=
|
||||
golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
|
||||
golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
|
||||
|
||||
@@ -5,9 +5,9 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
mcpv1 "git.wntrmute.dev/kyle/mcp/gen/mcp/v1"
|
||||
"git.wntrmute.dev/kyle/mcp/internal/registry"
|
||||
"git.wntrmute.dev/kyle/mcp/internal/runtime"
|
||||
mcpv1 "git.wntrmute.dev/mc/mcp/gen/mcp/v1"
|
||||
"git.wntrmute.dev/mc/mcp/internal/registry"
|
||||
"git.wntrmute.dev/mc/mcp/internal/runtime"
|
||||
)
|
||||
|
||||
// AdoptContainers discovers running containers that match the given service
|
||||
|
||||
@@ -4,9 +4,9 @@ import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
mcpv1 "git.wntrmute.dev/kyle/mcp/gen/mcp/v1"
|
||||
"git.wntrmute.dev/kyle/mcp/internal/registry"
|
||||
"git.wntrmute.dev/kyle/mcp/internal/runtime"
|
||||
mcpv1 "git.wntrmute.dev/mc/mcp/gen/mcp/v1"
|
||||
"git.wntrmute.dev/mc/mcp/internal/registry"
|
||||
"git.wntrmute.dev/mc/mcp/internal/runtime"
|
||||
)
|
||||
|
||||
func TestAdoptContainers(t *testing.T) {
|
||||
|
||||
@@ -11,12 +11,12 @@ import (
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
mcpv1 "git.wntrmute.dev/kyle/mcp/gen/mcp/v1"
|
||||
"git.wntrmute.dev/kyle/mcp/internal/auth"
|
||||
"git.wntrmute.dev/kyle/mcp/internal/config"
|
||||
"git.wntrmute.dev/kyle/mcp/internal/monitor"
|
||||
"git.wntrmute.dev/kyle/mcp/internal/registry"
|
||||
"git.wntrmute.dev/kyle/mcp/internal/runtime"
|
||||
mcpv1 "git.wntrmute.dev/mc/mcp/gen/mcp/v1"
|
||||
"git.wntrmute.dev/mc/mcp/internal/auth"
|
||||
"git.wntrmute.dev/mc/mcp/internal/config"
|
||||
"git.wntrmute.dev/mc/mcp/internal/monitor"
|
||||
"git.wntrmute.dev/mc/mcp/internal/registry"
|
||||
"git.wntrmute.dev/mc/mcp/internal/runtime"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
)
|
||||
@@ -32,6 +32,9 @@ type Agent struct {
|
||||
Monitor *monitor.Monitor
|
||||
Logger *slog.Logger
|
||||
PortAlloc *PortAllocator
|
||||
Proxy *ProxyRouter
|
||||
Certs *CertProvisioner
|
||||
DNS *DNSRegistrar
|
||||
}
|
||||
|
||||
// Run starts the agent: opens the database, sets up the gRPC server with
|
||||
@@ -51,6 +54,21 @@ func Run(cfg *config.AgentConfig) error {
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
certs, err := NewCertProvisioner(cfg.Metacrypt, cfg.MCProxy.CertDir, logger)
|
||||
if err != nil {
|
||||
return fmt.Errorf("create cert provisioner: %w", err)
|
||||
}
|
||||
|
||||
dns, err := NewDNSRegistrar(cfg.MCNS, logger)
|
||||
if err != nil {
|
||||
return fmt.Errorf("create DNS registrar: %w", err)
|
||||
}
|
||||
|
||||
a := &Agent{
|
||||
Config: cfg,
|
||||
DB: db,
|
||||
@@ -58,6 +76,9 @@ func Run(cfg *config.AgentConfig) error {
|
||||
Monitor: mon,
|
||||
Logger: logger,
|
||||
PortAlloc: NewPortAllocator(),
|
||||
Proxy: proxy,
|
||||
Certs: certs,
|
||||
DNS: dns,
|
||||
}
|
||||
|
||||
tlsCert, err := tls.LoadX509KeyPair(cfg.Server.TLSCert, cfg.Server.TLSKey)
|
||||
@@ -108,6 +129,7 @@ func Run(cfg *config.AgentConfig) error {
|
||||
logger.Info("shutting down")
|
||||
mon.Stop()
|
||||
server.GracefulStop()
|
||||
_ = proxy.Close()
|
||||
return nil
|
||||
case err := <-errCh:
|
||||
mon.Stop()
|
||||
|
||||
263
internal/agent/certs.go
Normal file
263
internal/agent/certs.go
Normal file
@@ -0,0 +1,263 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.wntrmute.dev/mc/mcp/internal/auth"
|
||||
"git.wntrmute.dev/mc/mcp/internal/config"
|
||||
)
|
||||
|
||||
// renewWindow is how far before expiry a cert is considered stale and
|
||||
// should be re-issued.
|
||||
const renewWindow = 30 * 24 * time.Hour // 30 days
|
||||
|
||||
// CertProvisioner requests TLS certificates from Metacrypt's CA API
|
||||
// and writes them to the mc-proxy cert directory. It is nil-safe: all
|
||||
// methods are no-ops when the receiver is nil.
|
||||
type CertProvisioner struct {
|
||||
serverURL string
|
||||
token string
|
||||
mount string
|
||||
issuer string
|
||||
certDir string
|
||||
httpClient *http.Client
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
// NewCertProvisioner creates a CertProvisioner. Returns (nil, nil) if
|
||||
// cfg.ServerURL is empty (cert provisioning disabled).
|
||||
func NewCertProvisioner(cfg config.MetacryptConfig, certDir string, logger *slog.Logger) (*CertProvisioner, error) {
|
||||
if cfg.ServerURL == "" {
|
||||
logger.Info("metacrypt not configured, cert provisioning disabled")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
token, err := auth.LoadToken(cfg.TokenPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load metacrypt token: %w", err)
|
||||
}
|
||||
|
||||
httpClient, err := newTLSClient(cfg.CACert)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create metacrypt HTTP client: %w", err)
|
||||
}
|
||||
|
||||
logger.Info("metacrypt cert provisioner enabled", "server", cfg.ServerURL, "mount", cfg.Mount, "issuer", cfg.Issuer)
|
||||
return &CertProvisioner{
|
||||
serverURL: strings.TrimRight(cfg.ServerURL, "/"),
|
||||
token: token,
|
||||
mount: cfg.Mount,
|
||||
issuer: cfg.Issuer,
|
||||
certDir: certDir,
|
||||
httpClient: httpClient,
|
||||
logger: logger,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// EnsureCert checks whether a valid TLS certificate exists for the
|
||||
// service. If the cert is missing or near expiry, it requests a new
|
||||
// one from Metacrypt.
|
||||
func (p *CertProvisioner) EnsureCert(ctx context.Context, serviceName string, hostnames []string) error {
|
||||
if p == nil || len(hostnames) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
certPath := filepath.Join(p.certDir, serviceName+".pem")
|
||||
|
||||
if remaining, ok := certTimeRemaining(certPath); ok {
|
||||
if remaining > renewWindow {
|
||||
p.logger.Debug("cert valid, skipping provisioning",
|
||||
"service", serviceName,
|
||||
"expires_in", remaining.Round(time.Hour),
|
||||
)
|
||||
return nil
|
||||
}
|
||||
p.logger.Info("cert near expiry, re-issuing",
|
||||
"service", serviceName,
|
||||
"expires_in", remaining.Round(time.Hour),
|
||||
)
|
||||
}
|
||||
|
||||
return p.issueCert(ctx, serviceName, hostnames[0], hostnames)
|
||||
}
|
||||
|
||||
// issueCert calls Metacrypt's CA API to issue a certificate and writes
|
||||
// the chain and key to the cert directory.
|
||||
func (p *CertProvisioner) issueCert(ctx context.Context, serviceName, commonName string, dnsNames []string) error {
|
||||
p.logger.Info("provisioning TLS cert",
|
||||
"service", serviceName,
|
||||
"cn", commonName,
|
||||
"sans", dnsNames,
|
||||
)
|
||||
|
||||
reqBody := map[string]interface{}{
|
||||
"mount": p.mount,
|
||||
"operation": "issue",
|
||||
"data": map[string]interface{}{
|
||||
"issuer": p.issuer,
|
||||
"common_name": commonName,
|
||||
"dns_names": dnsNames,
|
||||
"profile": "server",
|
||||
"ttl": "2160h",
|
||||
},
|
||||
}
|
||||
|
||||
body, err := json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
return fmt.Errorf("marshal issue request: %w", err)
|
||||
}
|
||||
|
||||
url := p.serverURL + "/v1/engine/request"
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return fmt.Errorf("create issue request: %w", err)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", "Bearer "+p.token)
|
||||
|
||||
resp, err := p.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("issue cert: %w", err)
|
||||
}
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("read issue response: %w", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("issue cert: metacrypt returned %d: %s", resp.StatusCode, string(respBody))
|
||||
}
|
||||
|
||||
var result struct {
|
||||
ChainPEM string `json:"chain_pem"`
|
||||
KeyPEM string `json:"key_pem"`
|
||||
Serial string `json:"serial"`
|
||||
ExpiresAt string `json:"expires_at"`
|
||||
}
|
||||
if err := json.Unmarshal(respBody, &result); err != nil {
|
||||
return fmt.Errorf("parse issue response: %w", err)
|
||||
}
|
||||
|
||||
if result.ChainPEM == "" || result.KeyPEM == "" {
|
||||
return fmt.Errorf("issue cert: response missing chain_pem or key_pem")
|
||||
}
|
||||
|
||||
// Write cert and key atomically (temp file + rename).
|
||||
certPath := filepath.Join(p.certDir, serviceName+".pem")
|
||||
keyPath := filepath.Join(p.certDir, serviceName+".key")
|
||||
|
||||
if err := atomicWrite(certPath, []byte(result.ChainPEM), 0644); err != nil {
|
||||
return fmt.Errorf("write cert: %w", err)
|
||||
}
|
||||
if err := atomicWrite(keyPath, []byte(result.KeyPEM), 0600); err != nil {
|
||||
return fmt.Errorf("write key: %w", err)
|
||||
}
|
||||
|
||||
p.logger.Info("cert provisioned",
|
||||
"service", serviceName,
|
||||
"serial", result.Serial,
|
||||
"expires_at", result.ExpiresAt,
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveCert removes TLS certificate and key files for a service.
|
||||
func (p *CertProvisioner) RemoveCert(serviceName string) error {
|
||||
if p == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
certPath := filepath.Join(p.certDir, serviceName+".pem")
|
||||
keyPath := filepath.Join(p.certDir, serviceName+".key")
|
||||
|
||||
for _, path := range []string{certPath, keyPath} {
|
||||
if err := os.Remove(path); err != nil && !os.IsNotExist(err) {
|
||||
return fmt.Errorf("remove %s: %w", path, err)
|
||||
}
|
||||
}
|
||||
|
||||
p.logger.Info("cert removed", "service", serviceName)
|
||||
return nil
|
||||
}
|
||||
|
||||
// certTimeRemaining returns the time until the leaf certificate at
|
||||
// path expires. Returns (0, false) if the cert cannot be read or parsed.
|
||||
func certTimeRemaining(path string) (time.Duration, bool) {
|
||||
data, err := os.ReadFile(path) //nolint:gosec // path from trusted config
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
block, _ := pem.Decode(data)
|
||||
if block == nil {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
remaining := time.Until(cert.NotAfter)
|
||||
if remaining <= 0 {
|
||||
return 0, true // expired
|
||||
}
|
||||
return remaining, true
|
||||
}
|
||||
|
||||
// atomicWrite writes data to a temporary file then renames it to path,
|
||||
// ensuring readers never see a partial file.
|
||||
func atomicWrite(path string, data []byte, perm os.FileMode) error {
|
||||
tmp := path + ".tmp"
|
||||
if err := os.WriteFile(tmp, data, perm); err != nil {
|
||||
return fmt.Errorf("write %s: %w", tmp, err)
|
||||
}
|
||||
if err := os.Rename(tmp, path); err != nil {
|
||||
_ = os.Remove(tmp)
|
||||
return fmt.Errorf("rename %s -> %s: %w", tmp, path, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// newTLSClient creates an HTTP client with TLS 1.3 minimum. If
|
||||
// caCertPath is non-empty, the CA certificate is loaded into the
|
||||
// root CA pool.
|
||||
func newTLSClient(caCertPath string) (*http.Client, error) {
|
||||
tlsConfig := &tls.Config{
|
||||
MinVersion: tls.VersionTLS13,
|
||||
}
|
||||
|
||||
if caCertPath != "" {
|
||||
caCert, err := os.ReadFile(caCertPath) //nolint:gosec // path from trusted config
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read CA cert %q: %w", caCertPath, err)
|
||||
}
|
||||
pool := x509.NewCertPool()
|
||||
if !pool.AppendCertsFromPEM(caCert) {
|
||||
return nil, fmt.Errorf("parse CA cert %q: no valid certificates found", caCertPath)
|
||||
}
|
||||
tlsConfig.RootCAs = pool
|
||||
}
|
||||
|
||||
return &http.Client{
|
||||
Timeout: 30 * time.Second,
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: tlsConfig,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
392
internal/agent/certs_test.go
Normal file
392
internal/agent/certs_test.go
Normal file
@@ -0,0 +1,392 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"log/slog"
|
||||
"math/big"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.wntrmute.dev/mc/mcp/internal/config"
|
||||
"git.wntrmute.dev/mc/mcp/internal/registry"
|
||||
)
|
||||
|
||||
func TestNilCertProvisionerIsNoop(t *testing.T) {
|
||||
var p *CertProvisioner
|
||||
if err := p.EnsureCert(context.Background(), "svc", []string{"svc.example.com"}); err != nil {
|
||||
t.Fatalf("EnsureCert on nil: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewCertProvisionerDisabledWhenUnconfigured(t *testing.T) {
|
||||
p, err := NewCertProvisioner(config.MetacryptConfig{}, "/tmp", slog.Default())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if p != nil {
|
||||
t.Fatal("expected nil provisioner for empty config")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnsureCertSkipsValidCert(t *testing.T) {
|
||||
certDir := t.TempDir()
|
||||
certPath := filepath.Join(certDir, "svc.pem")
|
||||
keyPath := filepath.Join(certDir, "svc.key")
|
||||
|
||||
// Generate a cert that expires in 90 days.
|
||||
writeSelfSignedCert(t, certPath, keyPath, "svc.example.com", 90*24*time.Hour)
|
||||
|
||||
// Create a provisioner that would fail if it tried to issue.
|
||||
p := &CertProvisioner{
|
||||
serverURL: "https://will-fail-if-called:9999",
|
||||
certDir: certDir,
|
||||
logger: slog.Default(),
|
||||
}
|
||||
|
||||
if err := p.EnsureCert(context.Background(), "svc", []string{"svc.example.com"}); err != nil {
|
||||
t.Fatalf("EnsureCert: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnsureCertReissuesExpiring(t *testing.T) {
|
||||
certDir := t.TempDir()
|
||||
certPath := filepath.Join(certDir, "svc.pem")
|
||||
keyPath := filepath.Join(certDir, "svc.key")
|
||||
|
||||
// Generate a cert that expires in 10 days (within 30-day renewal window).
|
||||
writeSelfSignedCert(t, certPath, keyPath, "svc.example.com", 10*24*time.Hour)
|
||||
|
||||
// Mock Metacrypt API.
|
||||
newCert, newKey := generateCertPEM(t, "svc.example.com", 90*24*time.Hour)
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
resp := map[string]string{
|
||||
"chain_pem": newCert,
|
||||
"key_pem": newKey,
|
||||
"serial": "abc123",
|
||||
"expires_at": time.Now().Add(90 * 24 * time.Hour).Format(time.RFC3339),
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(resp)
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
p := &CertProvisioner{
|
||||
serverURL: srv.URL,
|
||||
token: "test-token",
|
||||
mount: "pki",
|
||||
issuer: "infra",
|
||||
certDir: certDir,
|
||||
httpClient: srv.Client(),
|
||||
logger: slog.Default(),
|
||||
}
|
||||
|
||||
if err := p.EnsureCert(context.Background(), "svc", []string{"svc.example.com"}); err != nil {
|
||||
t.Fatalf("EnsureCert: %v", err)
|
||||
}
|
||||
|
||||
// Verify new cert was written.
|
||||
got, err := os.ReadFile(certPath)
|
||||
if err != nil {
|
||||
t.Fatalf("read cert: %v", err)
|
||||
}
|
||||
if string(got) != newCert {
|
||||
t.Fatal("cert file was not updated with new cert")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssueCertWritesFiles(t *testing.T) {
|
||||
certDir := t.TempDir()
|
||||
|
||||
// Mock Metacrypt API.
|
||||
certPEM, keyPEM := generateCertPEM(t, "svc.example.com", 90*24*time.Hour)
|
||||
var gotAuth string
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
gotAuth = r.Header.Get("Authorization")
|
||||
|
||||
var req map[string]interface{}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "bad request", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Verify request structure.
|
||||
if req["mount"] != "pki" || req["operation"] != "issue" {
|
||||
t.Errorf("unexpected request: %v", req)
|
||||
}
|
||||
|
||||
resp := map[string]string{
|
||||
"chain_pem": certPEM,
|
||||
"key_pem": keyPEM,
|
||||
"serial": "deadbeef",
|
||||
"expires_at": time.Now().Add(90 * 24 * time.Hour).Format(time.RFC3339),
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(resp)
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
p := &CertProvisioner{
|
||||
serverURL: srv.URL,
|
||||
token: "my-service-token",
|
||||
mount: "pki",
|
||||
issuer: "infra",
|
||||
certDir: certDir,
|
||||
httpClient: srv.Client(),
|
||||
logger: slog.Default(),
|
||||
}
|
||||
|
||||
if err := p.EnsureCert(context.Background(), "svc", []string{"svc.example.com"}); err != nil {
|
||||
t.Fatalf("EnsureCert: %v", err)
|
||||
}
|
||||
|
||||
// Verify auth header.
|
||||
if gotAuth != "Bearer my-service-token" {
|
||||
t.Fatalf("auth header: got %q, want %q", gotAuth, "Bearer my-service-token")
|
||||
}
|
||||
|
||||
// Verify cert file.
|
||||
certData, err := os.ReadFile(filepath.Join(certDir, "svc.pem"))
|
||||
if err != nil {
|
||||
t.Fatalf("read cert: %v", err)
|
||||
}
|
||||
if string(certData) != certPEM {
|
||||
t.Fatal("cert content mismatch")
|
||||
}
|
||||
|
||||
// Verify key file.
|
||||
keyData, err := os.ReadFile(filepath.Join(certDir, "svc.key"))
|
||||
if err != nil {
|
||||
t.Fatalf("read key: %v", err)
|
||||
}
|
||||
if string(keyData) != keyPEM {
|
||||
t.Fatal("key content mismatch")
|
||||
}
|
||||
|
||||
// Verify key file permissions.
|
||||
info, err := os.Stat(filepath.Join(certDir, "svc.key"))
|
||||
if err != nil {
|
||||
t.Fatalf("stat key: %v", err)
|
||||
}
|
||||
if perm := info.Mode().Perm(); perm != 0600 {
|
||||
t.Fatalf("key permissions: got %o, want 0600", perm)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssueCertAPIError(t *testing.T) {
|
||||
certDir := t.TempDir()
|
||||
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, `{"error":"sealed"}`, http.StatusServiceUnavailable)
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
p := &CertProvisioner{
|
||||
serverURL: srv.URL,
|
||||
token: "test-token",
|
||||
mount: "pki",
|
||||
issuer: "infra",
|
||||
certDir: certDir,
|
||||
httpClient: srv.Client(),
|
||||
logger: slog.Default(),
|
||||
}
|
||||
|
||||
err := p.EnsureCert(context.Background(), "svc", []string{"svc.example.com"})
|
||||
if err == nil {
|
||||
t.Fatal("expected error for sealed metacrypt")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCertTimeRemaining(t *testing.T) {
|
||||
t.Run("missing file", func(t *testing.T) {
|
||||
if _, ok := certTimeRemaining("/nonexistent/cert.pem"); ok {
|
||||
t.Fatal("expected false for missing file")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("valid cert", func(t *testing.T) {
|
||||
certDir := t.TempDir()
|
||||
path := filepath.Join(certDir, "test.pem")
|
||||
writeSelfSignedCert(t, path, filepath.Join(certDir, "test.key"), "test.example.com", 90*24*time.Hour)
|
||||
|
||||
remaining, ok := certTimeRemaining(path)
|
||||
if !ok {
|
||||
t.Fatal("expected true for valid cert")
|
||||
}
|
||||
// Should be close to 90 days.
|
||||
if remaining < 89*24*time.Hour || remaining > 91*24*time.Hour {
|
||||
t.Fatalf("remaining: got %v, want ~90 days", remaining)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("expired cert", func(t *testing.T) {
|
||||
certDir := t.TempDir()
|
||||
path := filepath.Join(certDir, "expired.pem")
|
||||
// Write a cert that's already expired (valid from -2h to -1h).
|
||||
writeExpiredCert(t, path, filepath.Join(certDir, "expired.key"), "expired.example.com")
|
||||
|
||||
remaining, ok := certTimeRemaining(path)
|
||||
if !ok {
|
||||
t.Fatal("expected true for expired cert")
|
||||
}
|
||||
if remaining > 0 {
|
||||
t.Fatalf("remaining: got %v, want <= 0", remaining)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestHasL7Routes(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
routes []registry.Route
|
||||
want bool
|
||||
}{
|
||||
{"nil", nil, false},
|
||||
{"empty", []registry.Route{}, false},
|
||||
{"l4 only", []registry.Route{{Mode: "l4"}}, false},
|
||||
{"l7 only", []registry.Route{{Mode: "l7"}}, true},
|
||||
{"mixed", []registry.Route{{Mode: "l4"}, {Mode: "l7"}}, true},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := hasL7Routes(tt.routes); got != tt.want {
|
||||
t.Fatalf("hasL7Routes = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestL7Hostnames(t *testing.T) {
|
||||
routes := []registry.Route{
|
||||
{Mode: "l7", Hostname: ""},
|
||||
{Mode: "l4", Hostname: "ignored.example.com"},
|
||||
{Mode: "l7", Hostname: "custom.example.com"},
|
||||
{Mode: "l7", Hostname: ""}, // duplicate default
|
||||
}
|
||||
|
||||
got := l7Hostnames("myservice", routes)
|
||||
want := []string{"myservice.svc.mcp.metacircular.net", "custom.example.com"}
|
||||
|
||||
if len(got) != len(want) {
|
||||
t.Fatalf("got %v, want %v", got, want)
|
||||
}
|
||||
for i := range want {
|
||||
if got[i] != want[i] {
|
||||
t.Fatalf("got[%d] = %q, want %q", i, got[i], want[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAtomicWrite(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
path := filepath.Join(dir, "test.txt")
|
||||
|
||||
if err := atomicWrite(path, []byte("hello"), 0644); err != nil {
|
||||
t.Fatalf("atomicWrite: %v", err)
|
||||
}
|
||||
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
t.Fatalf("read: %v", err)
|
||||
}
|
||||
if string(data) != "hello" {
|
||||
t.Fatalf("got %q, want %q", string(data), "hello")
|
||||
}
|
||||
|
||||
// Verify no .tmp file left behind.
|
||||
if _, err := os.Stat(path + ".tmp"); !os.IsNotExist(err) {
|
||||
t.Fatal("temp file should not exist after atomic write")
|
||||
}
|
||||
}
|
||||
|
||||
// --- test helpers ---
|
||||
|
||||
// writeSelfSignedCert generates a self-signed cert/key and writes them to disk.
|
||||
func writeSelfSignedCert(t *testing.T, certPath, keyPath, hostname string, validity time.Duration) {
|
||||
t.Helper()
|
||||
certPEM, keyPEM := generateCertPEM(t, hostname, validity)
|
||||
if err := os.WriteFile(certPath, []byte(certPEM), 0644); err != nil {
|
||||
t.Fatalf("write cert: %v", err)
|
||||
}
|
||||
if err := os.WriteFile(keyPath, []byte(keyPEM), 0600); err != nil {
|
||||
t.Fatalf("write key: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// writeExpiredCert generates a cert that is already expired.
|
||||
func writeExpiredCert(t *testing.T, certPath, keyPath, hostname string) {
|
||||
t.Helper()
|
||||
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
t.Fatalf("generate key: %v", err)
|
||||
}
|
||||
|
||||
tmpl := &x509.Certificate{
|
||||
SerialNumber: big.NewInt(1),
|
||||
Subject: pkix.Name{CommonName: hostname},
|
||||
DNSNames: []string{hostname},
|
||||
NotBefore: time.Now().Add(-2 * time.Hour),
|
||||
NotAfter: time.Now().Add(-1 * time.Hour),
|
||||
}
|
||||
|
||||
der, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, &key.PublicKey, key)
|
||||
if err != nil {
|
||||
t.Fatalf("create cert: %v", err)
|
||||
}
|
||||
|
||||
certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: der})
|
||||
keyDER, err := x509.MarshalECPrivateKey(key)
|
||||
if err != nil {
|
||||
t.Fatalf("marshal key: %v", err)
|
||||
}
|
||||
keyPEM := pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: keyDER})
|
||||
|
||||
if err := os.WriteFile(certPath, certPEM, 0644); err != nil {
|
||||
t.Fatalf("write cert: %v", err)
|
||||
}
|
||||
if err := os.WriteFile(keyPath, keyPEM, 0600); err != nil {
|
||||
t.Fatalf("write key: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// generateCertPEM generates a self-signed cert and returns PEM strings.
|
||||
func generateCertPEM(t *testing.T, hostname string, validity time.Duration) (certPEM, keyPEM string) {
|
||||
t.Helper()
|
||||
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
t.Fatalf("generate key: %v", err)
|
||||
}
|
||||
|
||||
tmpl := &x509.Certificate{
|
||||
SerialNumber: big.NewInt(1),
|
||||
Subject: pkix.Name{CommonName: hostname},
|
||||
DNSNames: []string{hostname},
|
||||
NotBefore: time.Now().Add(-1 * time.Hour),
|
||||
NotAfter: time.Now().Add(validity),
|
||||
}
|
||||
|
||||
der, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, &key.PublicKey, key)
|
||||
if err != nil {
|
||||
t.Fatalf("create cert: %v", err)
|
||||
}
|
||||
|
||||
certBlock := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: der})
|
||||
keyDER, err := x509.MarshalECPrivateKey(key)
|
||||
if err != nil {
|
||||
t.Fatalf("marshal key: %v", err)
|
||||
}
|
||||
keyBlock := pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: keyDER})
|
||||
|
||||
return string(certBlock), string(keyBlock)
|
||||
}
|
||||
@@ -7,9 +7,9 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
mcpv1 "git.wntrmute.dev/kyle/mcp/gen/mcp/v1"
|
||||
"git.wntrmute.dev/kyle/mcp/internal/registry"
|
||||
"git.wntrmute.dev/kyle/mcp/internal/runtime"
|
||||
mcpv1 "git.wntrmute.dev/mc/mcp/gen/mcp/v1"
|
||||
"git.wntrmute.dev/mc/mcp/internal/registry"
|
||||
"git.wntrmute.dev/mc/mcp/internal/runtime"
|
||||
)
|
||||
|
||||
// Deploy deploys a service (or a single component of it) to this node.
|
||||
@@ -146,6 +146,31 @@ func (a *Agent) deployComponent(ctx context.Context, serviceName string, cs *mcp
|
||||
}
|
||||
}
|
||||
|
||||
// Provision TLS certs for L7 routes before registering with mc-proxy.
|
||||
if a.Certs != nil && hasL7Routes(regRoutes) {
|
||||
hostnames := l7Hostnames(serviceName, regRoutes)
|
||||
if err := a.Certs.EnsureCert(ctx, serviceName, hostnames); err != nil {
|
||||
a.Logger.Warn("failed to provision TLS cert", "service", serviceName, "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
||||
// Register DNS record for the service.
|
||||
if a.DNS != nil && len(regRoutes) > 0 {
|
||||
if err := a.DNS.EnsureRecord(ctx, serviceName); err != nil {
|
||||
a.Logger.Warn("failed to register DNS record", "service", serviceName, "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
@@ -173,7 +198,10 @@ func (a *Agent) allocateRoutePorts(service, component string, routes []registry.
|
||||
return nil, nil, fmt.Errorf("store host port for route %q: %w", r.Name, err)
|
||||
}
|
||||
|
||||
ports = append(ports, fmt.Sprintf("127.0.0.1:%d:%d", hostPort, r.Port))
|
||||
// The container port must match hostPort (which is also set as $PORT),
|
||||
// so the app's listen address matches the podman port mapping.
|
||||
// r.Port is the mc-proxy listener port, NOT the container port.
|
||||
ports = append(ports, fmt.Sprintf("127.0.0.1:%d:%d", hostPort, hostPort))
|
||||
|
||||
if len(routes) == 1 {
|
||||
env = append(env, fmt.Sprintf("PORT=%d", hostPort))
|
||||
@@ -199,6 +227,37 @@ func ensureService(db *sql.DB, name string, active bool) error {
|
||||
return registry.UpdateServiceActive(db, name, active)
|
||||
}
|
||||
|
||||
// hasL7Routes reports whether any route uses L7 (TLS-terminating) mode.
|
||||
func hasL7Routes(routes []registry.Route) bool {
|
||||
for _, r := range routes {
|
||||
if r.Mode == "l7" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// l7Hostnames returns the unique hostnames from L7 routes, applying
|
||||
// the default hostname convention when a route has no explicit hostname.
|
||||
func l7Hostnames(serviceName string, routes []registry.Route) []string {
|
||||
seen := make(map[string]bool)
|
||||
var hostnames []string
|
||||
for _, r := range routes {
|
||||
if r.Mode != "l7" {
|
||||
continue
|
||||
}
|
||||
h := r.Hostname
|
||||
if h == "" {
|
||||
h = serviceName + ".svc.mcp.metacircular.net"
|
||||
}
|
||||
if !seen[h] {
|
||||
seen[h] = true
|
||||
hostnames = append(hostnames, h)
|
||||
}
|
||||
}
|
||||
return hostnames
|
||||
}
|
||||
|
||||
// ensureComponent creates the component if it does not exist, or updates its
|
||||
// spec if it does.
|
||||
func ensureComponent(db *sql.DB, c *registry.Component) error {
|
||||
|
||||
159
internal/agent/deploy_test.go
Normal file
159
internal/agent/deploy_test.go
Normal file
@@ -0,0 +1,159 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"git.wntrmute.dev/mc/mcp/internal/registry"
|
||||
)
|
||||
|
||||
func openTestDB(t *testing.T) *sql.DB {
|
||||
t.Helper()
|
||||
db, err := registry.Open(filepath.Join(t.TempDir(), "test.db"))
|
||||
if err != nil {
|
||||
t.Fatalf("open db: %v", err)
|
||||
}
|
||||
t.Cleanup(func() { _ = db.Close() })
|
||||
return db
|
||||
}
|
||||
|
||||
func testAgent(t *testing.T) *Agent {
|
||||
t.Helper()
|
||||
return &Agent{
|
||||
DB: openTestDB(t),
|
||||
PortAlloc: NewPortAllocator(),
|
||||
Logger: slog.New(slog.NewTextHandler(os.Stderr, nil)),
|
||||
}
|
||||
}
|
||||
|
||||
// seedComponent creates the service and component in the registry so that
|
||||
// allocateRoutePorts can store host ports for it.
|
||||
func seedComponent(t *testing.T, db *sql.DB, service, component string, routes []registry.Route) {
|
||||
t.Helper()
|
||||
if err := registry.CreateService(db, service, true); err != nil {
|
||||
t.Fatalf("create service: %v", err)
|
||||
}
|
||||
if err := registry.CreateComponent(db, ®istry.Component{
|
||||
Name: component,
|
||||
Service: service,
|
||||
Image: "img:latest",
|
||||
DesiredState: "running",
|
||||
ObservedState: "unknown",
|
||||
Routes: routes,
|
||||
}); err != nil {
|
||||
t.Fatalf("create component: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAllocateRoutePorts_SingleRoute(t *testing.T) {
|
||||
a := testAgent(t)
|
||||
routes := []registry.Route{
|
||||
{Name: "default", Port: 443, Mode: "l7"},
|
||||
}
|
||||
seedComponent(t, a.DB, "mcdoc", "mcdoc", routes)
|
||||
|
||||
ports, env, err := a.allocateRoutePorts("mcdoc", "mcdoc", routes)
|
||||
if err != nil {
|
||||
t.Fatalf("allocateRoutePorts: %v", err)
|
||||
}
|
||||
|
||||
if len(ports) != 1 {
|
||||
t.Fatalf("expected 1 port mapping, got %d", len(ports))
|
||||
}
|
||||
if len(env) != 1 {
|
||||
t.Fatalf("expected 1 env var, got %d", len(env))
|
||||
}
|
||||
|
||||
// Parse the port mapping: should be "127.0.0.1:<hostPort>:<hostPort>"
|
||||
// NOT "127.0.0.1:<hostPort>:443"
|
||||
var hostPort, containerPort int
|
||||
n, _ := fmt.Sscanf(ports[0], "127.0.0.1:%d:%d", &hostPort, &containerPort)
|
||||
if n != 2 {
|
||||
t.Fatalf("failed to parse port mapping %q", ports[0])
|
||||
}
|
||||
if hostPort != containerPort {
|
||||
t.Errorf("host port (%d) != container port (%d); container port must match host port for $PORT consistency", hostPort, containerPort)
|
||||
}
|
||||
|
||||
// Env var should be PORT=<hostPort>
|
||||
var envPort int
|
||||
n, _ = fmt.Sscanf(env[0], "PORT=%d", &envPort)
|
||||
if n != 1 {
|
||||
t.Fatalf("failed to parse env var %q", env[0])
|
||||
}
|
||||
if envPort != hostPort {
|
||||
t.Errorf("PORT env (%d) != host port (%d)", envPort, hostPort)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAllocateRoutePorts_MultiRoute(t *testing.T) {
|
||||
a := testAgent(t)
|
||||
routes := []registry.Route{
|
||||
{Name: "rest", Port: 8443, Mode: "l4"},
|
||||
{Name: "grpc", Port: 9443, Mode: "l4"},
|
||||
}
|
||||
seedComponent(t, a.DB, "metacrypt", "api", routes)
|
||||
|
||||
ports, env, err := a.allocateRoutePorts("metacrypt", "api", routes)
|
||||
if err != nil {
|
||||
t.Fatalf("allocateRoutePorts: %v", err)
|
||||
}
|
||||
|
||||
if len(ports) != 2 {
|
||||
t.Fatalf("expected 2 port mappings, got %d", len(ports))
|
||||
}
|
||||
if len(env) != 2 {
|
||||
t.Fatalf("expected 2 env vars, got %d", len(env))
|
||||
}
|
||||
|
||||
// Each port mapping should have host port == container port.
|
||||
for i, p := range ports {
|
||||
var hp, cp int
|
||||
n, _ := fmt.Sscanf(p, "127.0.0.1:%d:%d", &hp, &cp)
|
||||
if n != 2 {
|
||||
t.Fatalf("port[%d]: failed to parse %q", i, p)
|
||||
}
|
||||
if hp != cp {
|
||||
t.Errorf("port[%d]: host port (%d) != container port (%d)", i, hp, cp)
|
||||
}
|
||||
}
|
||||
|
||||
// Env vars should be PORT_REST and PORT_GRPC (not bare PORT).
|
||||
if env[0][:10] != "PORT_REST=" {
|
||||
t.Errorf("env[0] = %q, want PORT_REST=...", env[0])
|
||||
}
|
||||
if env[1][:10] != "PORT_GRPC=" {
|
||||
t.Errorf("env[1] = %q, want PORT_GRPC=...", env[1])
|
||||
}
|
||||
}
|
||||
|
||||
func TestAllocateRoutePorts_L7PortNotUsedAsContainerPort(t *testing.T) {
|
||||
a := testAgent(t)
|
||||
routes := []registry.Route{
|
||||
{Name: "default", Port: 443, Mode: "l7"},
|
||||
}
|
||||
seedComponent(t, a.DB, "svc", "web", routes)
|
||||
|
||||
ports, _, err := a.allocateRoutePorts("svc", "web", routes)
|
||||
if err != nil {
|
||||
t.Fatalf("allocateRoutePorts: %v", err)
|
||||
}
|
||||
|
||||
// The container port must NOT be 443 (the mc-proxy listener port).
|
||||
// It must be the host port (which is in range 10000-60000).
|
||||
var hostPort, containerPort int
|
||||
n, _ := fmt.Sscanf(ports[0], "127.0.0.1:%d:%d", &hostPort, &containerPort)
|
||||
if n != 2 {
|
||||
t.Fatalf("failed to parse port mapping %q", ports[0])
|
||||
}
|
||||
if containerPort == 443 {
|
||||
t.Errorf("container port is 443 (mc-proxy listener); should be %d (host port)", hostPort)
|
||||
}
|
||||
if containerPort < portRangeMin || containerPort >= portRangeMax {
|
||||
t.Errorf("container port %d outside allocation range [%d, %d)", containerPort, portRangeMin, portRangeMax)
|
||||
}
|
||||
}
|
||||
265
internal/agent/dns.go
Normal file
265
internal/agent/dns.go
Normal file
@@ -0,0 +1,265 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"git.wntrmute.dev/mc/mcp/internal/auth"
|
||||
"git.wntrmute.dev/mc/mcp/internal/config"
|
||||
)
|
||||
|
||||
// DNSRegistrar creates and removes A records in MCNS during deploy
|
||||
// and stop. It is nil-safe: all methods are no-ops when the receiver
|
||||
// is nil.
|
||||
type DNSRegistrar struct {
|
||||
serverURL string
|
||||
token string
|
||||
zone string
|
||||
nodeAddr string
|
||||
httpClient *http.Client
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
// dnsRecord is the JSON representation of an MCNS record.
|
||||
type dnsRecord struct {
|
||||
ID int `json:"ID"`
|
||||
Name string `json:"Name"`
|
||||
Type string `json:"Type"`
|
||||
Value string `json:"Value"`
|
||||
TTL int `json:"TTL"`
|
||||
}
|
||||
|
||||
// NewDNSRegistrar creates a DNSRegistrar. Returns (nil, nil) if
|
||||
// cfg.ServerURL is empty (DNS registration disabled).
|
||||
func NewDNSRegistrar(cfg config.MCNSConfig, logger *slog.Logger) (*DNSRegistrar, error) {
|
||||
if cfg.ServerURL == "" {
|
||||
logger.Info("mcns not configured, DNS registration disabled")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
token, err := auth.LoadToken(cfg.TokenPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load mcns token: %w", err)
|
||||
}
|
||||
|
||||
httpClient, err := newTLSClient(cfg.CACert)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create mcns HTTP client: %w", err)
|
||||
}
|
||||
|
||||
logger.Info("mcns DNS registrar enabled", "server", cfg.ServerURL, "zone", cfg.Zone, "node_addr", cfg.NodeAddr)
|
||||
return &DNSRegistrar{
|
||||
serverURL: strings.TrimRight(cfg.ServerURL, "/"),
|
||||
token: token,
|
||||
zone: cfg.Zone,
|
||||
nodeAddr: cfg.NodeAddr,
|
||||
httpClient: httpClient,
|
||||
logger: logger,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// EnsureRecord ensures an A record exists for the service in the
|
||||
// configured zone, pointing to the node's address.
|
||||
func (d *DNSRegistrar) EnsureRecord(ctx context.Context, serviceName string) error {
|
||||
if d == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
existing, err := d.listRecords(ctx, serviceName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("list DNS records: %w", err)
|
||||
}
|
||||
|
||||
// Check if any existing record already has the correct value.
|
||||
for _, r := range existing {
|
||||
if r.Value == d.nodeAddr {
|
||||
d.logger.Debug("DNS record exists, skipping",
|
||||
"service", serviceName,
|
||||
"record", r.Name+"."+d.zone,
|
||||
"value", r.Value,
|
||||
)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// No record with the correct value — update the first one if it exists.
|
||||
if len(existing) > 0 {
|
||||
d.logger.Info("updating DNS record",
|
||||
"service", serviceName,
|
||||
"old_value", existing[0].Value,
|
||||
"new_value", d.nodeAddr,
|
||||
)
|
||||
return d.updateRecord(ctx, existing[0].ID, serviceName)
|
||||
}
|
||||
|
||||
// No existing record — create one.
|
||||
d.logger.Info("creating DNS record",
|
||||
"service", serviceName,
|
||||
"record", serviceName+"."+d.zone,
|
||||
"value", d.nodeAddr,
|
||||
)
|
||||
return d.createRecord(ctx, serviceName)
|
||||
}
|
||||
|
||||
// RemoveRecord removes A records for the service from the configured zone.
|
||||
func (d *DNSRegistrar) RemoveRecord(ctx context.Context, serviceName string) error {
|
||||
if d == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
existing, err := d.listRecords(ctx, serviceName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("list DNS records: %w", err)
|
||||
}
|
||||
|
||||
if len(existing) == 0 {
|
||||
d.logger.Debug("no DNS record to remove", "service", serviceName)
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, r := range existing {
|
||||
d.logger.Info("removing DNS record",
|
||||
"service", serviceName,
|
||||
"record", r.Name+"."+d.zone,
|
||||
"id", r.ID,
|
||||
)
|
||||
if err := d.deleteRecord(ctx, r.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// listRecords returns A records matching the service name in the zone.
|
||||
func (d *DNSRegistrar) listRecords(ctx context.Context, serviceName string) ([]dnsRecord, error) {
|
||||
url := fmt.Sprintf("%s/v1/zones/%s/records?name=%s&type=A", d.serverURL, d.zone, serviceName)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create list request: %w", err)
|
||||
}
|
||||
req.Header.Set("Authorization", "Bearer "+d.token)
|
||||
|
||||
resp, err := d.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list records: %w", err)
|
||||
}
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read list response: %w", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("list records: mcns returned %d: %s", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
var envelope struct {
|
||||
Records []dnsRecord `json:"records"`
|
||||
}
|
||||
if err := json.Unmarshal(body, &envelope); err != nil {
|
||||
return nil, fmt.Errorf("parse list response: %w", err)
|
||||
}
|
||||
return envelope.Records, nil
|
||||
}
|
||||
|
||||
// createRecord creates an A record in the zone.
|
||||
func (d *DNSRegistrar) createRecord(ctx context.Context, serviceName string) error {
|
||||
reqBody := map[string]interface{}{
|
||||
"name": serviceName,
|
||||
"type": "A",
|
||||
"value": d.nodeAddr,
|
||||
"ttl": 300,
|
||||
}
|
||||
|
||||
body, err := json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
return fmt.Errorf("marshal create request: %w", err)
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("%s/v1/zones/%s/records", d.serverURL, d.zone)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return fmt.Errorf("create record request: %w", err)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", "Bearer "+d.token)
|
||||
|
||||
resp, err := d.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("create record: %w", err)
|
||||
}
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
|
||||
if resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusOK {
|
||||
respBody, _ := io.ReadAll(resp.Body)
|
||||
return fmt.Errorf("create record: mcns returned %d: %s", resp.StatusCode, string(respBody))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// updateRecord updates an existing record's value.
|
||||
func (d *DNSRegistrar) updateRecord(ctx context.Context, recordID int, serviceName string) error {
|
||||
reqBody := map[string]interface{}{
|
||||
"name": serviceName,
|
||||
"type": "A",
|
||||
"value": d.nodeAddr,
|
||||
"ttl": 300,
|
||||
}
|
||||
|
||||
body, err := json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
return fmt.Errorf("marshal update request: %w", err)
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("%s/v1/zones/%s/records/%d", d.serverURL, d.zone, recordID)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPut, url, bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return fmt.Errorf("create update request: %w", err)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", "Bearer "+d.token)
|
||||
|
||||
resp, err := d.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("update record: %w", err)
|
||||
}
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
respBody, _ := io.ReadAll(resp.Body)
|
||||
return fmt.Errorf("update record: mcns returned %d: %s", resp.StatusCode, string(respBody))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// deleteRecord deletes a record by ID.
|
||||
func (d *DNSRegistrar) deleteRecord(ctx context.Context, recordID int) error {
|
||||
url := fmt.Sprintf("%s/v1/zones/%s/records/%d", d.serverURL, d.zone, recordID)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodDelete, url, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("create delete request: %w", err)
|
||||
}
|
||||
req.Header.Set("Authorization", "Bearer "+d.token)
|
||||
|
||||
resp, err := d.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("delete record: %w", err)
|
||||
}
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
|
||||
if resp.StatusCode != http.StatusNoContent && resp.StatusCode != http.StatusOK {
|
||||
respBody, _ := io.ReadAll(resp.Body)
|
||||
return fmt.Errorf("delete record: mcns returned %d: %s", resp.StatusCode, string(respBody))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
214
internal/agent/dns_test.go
Normal file
214
internal/agent/dns_test.go
Normal file
@@ -0,0 +1,214 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"git.wntrmute.dev/mc/mcp/internal/config"
|
||||
)
|
||||
|
||||
func TestNilDNSRegistrarIsNoop(t *testing.T) {
|
||||
var d *DNSRegistrar
|
||||
if err := d.EnsureRecord(context.Background(), "svc"); err != nil {
|
||||
t.Fatalf("EnsureRecord on nil: %v", err)
|
||||
}
|
||||
if err := d.RemoveRecord(context.Background(), "svc"); err != nil {
|
||||
t.Fatalf("RemoveRecord on nil: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewDNSRegistrarDisabledWhenUnconfigured(t *testing.T) {
|
||||
d, err := NewDNSRegistrar(config.MCNSConfig{}, slog.Default())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if d != nil {
|
||||
t.Fatal("expected nil registrar for empty config")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnsureRecordCreatesWhenMissing(t *testing.T) {
|
||||
var gotMethod, gotPath, gotAuth string
|
||||
var gotBody map[string]interface{}
|
||||
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == http.MethodGet {
|
||||
// List returns empty — no existing records.
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, _ = w.Write([]byte(`{"records":[]}`))
|
||||
return
|
||||
}
|
||||
gotMethod = r.Method
|
||||
gotPath = r.URL.Path
|
||||
gotAuth = r.Header.Get("Authorization")
|
||||
_ = json.NewDecoder(r.Body).Decode(&gotBody)
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
_, _ = w.Write([]byte(`{"id":1}`))
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
d := &DNSRegistrar{
|
||||
serverURL: srv.URL,
|
||||
token: "test-token",
|
||||
zone: "svc.mcp.metacircular.net",
|
||||
nodeAddr: "192.168.88.181",
|
||||
httpClient: srv.Client(),
|
||||
logger: slog.Default(),
|
||||
}
|
||||
|
||||
if err := d.EnsureRecord(context.Background(), "myservice"); err != nil {
|
||||
t.Fatalf("EnsureRecord: %v", err)
|
||||
}
|
||||
|
||||
if gotMethod != http.MethodPost {
|
||||
t.Fatalf("method: got %q, want POST", gotMethod)
|
||||
}
|
||||
if gotPath != "/v1/zones/svc.mcp.metacircular.net/records" {
|
||||
t.Fatalf("path: got %q", gotPath)
|
||||
}
|
||||
if gotAuth != "Bearer test-token" {
|
||||
t.Fatalf("auth: got %q", gotAuth)
|
||||
}
|
||||
if gotBody["name"] != "myservice" {
|
||||
t.Fatalf("name: got %v", gotBody["name"])
|
||||
}
|
||||
if gotBody["type"] != "A" {
|
||||
t.Fatalf("type: got %v", gotBody["type"])
|
||||
}
|
||||
if gotBody["value"] != "192.168.88.181" {
|
||||
t.Fatalf("value: got %v", gotBody["value"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnsureRecordSkipsWhenExists(t *testing.T) {
|
||||
createCalled := false
|
||||
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == http.MethodGet {
|
||||
// Return an existing record with the correct value.
|
||||
resp := map[string][]dnsRecord{"records": {{ID: 1, Name: "myservice", Type: "A", Value: "192.168.88.181", TTL: 300}}}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(resp)
|
||||
return
|
||||
}
|
||||
createCalled = true
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
d := &DNSRegistrar{
|
||||
serverURL: srv.URL,
|
||||
token: "test-token",
|
||||
zone: "svc.mcp.metacircular.net",
|
||||
nodeAddr: "192.168.88.181",
|
||||
httpClient: srv.Client(),
|
||||
logger: slog.Default(),
|
||||
}
|
||||
|
||||
if err := d.EnsureRecord(context.Background(), "myservice"); err != nil {
|
||||
t.Fatalf("EnsureRecord: %v", err)
|
||||
}
|
||||
if createCalled {
|
||||
t.Fatal("should not create when record already exists with correct value")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnsureRecordUpdatesWrongValue(t *testing.T) {
|
||||
var gotMethod string
|
||||
var gotPath string
|
||||
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == http.MethodGet {
|
||||
// Return a record with a stale value.
|
||||
resp := map[string][]dnsRecord{"records": {{ID: 42, Name: "myservice", Type: "A", Value: "10.0.0.1", TTL: 300}}}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(resp)
|
||||
return
|
||||
}
|
||||
gotMethod = r.Method
|
||||
gotPath = r.URL.Path
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
d := &DNSRegistrar{
|
||||
serverURL: srv.URL,
|
||||
token: "test-token",
|
||||
zone: "svc.mcp.metacircular.net",
|
||||
nodeAddr: "192.168.88.181",
|
||||
httpClient: srv.Client(),
|
||||
logger: slog.Default(),
|
||||
}
|
||||
|
||||
if err := d.EnsureRecord(context.Background(), "myservice"); err != nil {
|
||||
t.Fatalf("EnsureRecord: %v", err)
|
||||
}
|
||||
if gotMethod != http.MethodPut {
|
||||
t.Fatalf("method: got %q, want PUT", gotMethod)
|
||||
}
|
||||
if gotPath != "/v1/zones/svc.mcp.metacircular.net/records/42" {
|
||||
t.Fatalf("path: got %q", gotPath)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveRecordDeletes(t *testing.T) {
|
||||
var gotMethod, gotPath string
|
||||
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == http.MethodGet {
|
||||
resp := map[string][]dnsRecord{"records": {{ID: 7, Name: "myservice", Type: "A", Value: "192.168.88.181", TTL: 300}}}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(resp)
|
||||
return
|
||||
}
|
||||
gotMethod = r.Method
|
||||
gotPath = r.URL.Path
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
d := &DNSRegistrar{
|
||||
serverURL: srv.URL,
|
||||
token: "test-token",
|
||||
zone: "svc.mcp.metacircular.net",
|
||||
nodeAddr: "192.168.88.181",
|
||||
httpClient: srv.Client(),
|
||||
logger: slog.Default(),
|
||||
}
|
||||
|
||||
if err := d.RemoveRecord(context.Background(), "myservice"); err != nil {
|
||||
t.Fatalf("RemoveRecord: %v", err)
|
||||
}
|
||||
if gotMethod != http.MethodDelete {
|
||||
t.Fatalf("method: got %q, want DELETE", gotMethod)
|
||||
}
|
||||
if gotPath != "/v1/zones/svc.mcp.metacircular.net/records/7" {
|
||||
t.Fatalf("path: got %q", gotPath)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveRecordNoopWhenMissing(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// List returns empty.
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, _ = w.Write([]byte(`{"records":[]}`))
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
d := &DNSRegistrar{
|
||||
serverURL: srv.URL,
|
||||
token: "test-token",
|
||||
zone: "svc.mcp.metacircular.net",
|
||||
nodeAddr: "192.168.88.181",
|
||||
httpClient: srv.Client(),
|
||||
logger: slog.Default(),
|
||||
}
|
||||
|
||||
if err := d.RemoveRecord(context.Background(), "myservice"); err != nil {
|
||||
t.Fatalf("RemoveRecord: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
mcpv1 "git.wntrmute.dev/kyle/mcp/gen/mcp/v1"
|
||||
mcpv1 "git.wntrmute.dev/mc/mcp/gen/mcp/v1"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
@@ -5,9 +5,9 @@ import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
|
||||
mcpv1 "git.wntrmute.dev/kyle/mcp/gen/mcp/v1"
|
||||
"git.wntrmute.dev/kyle/mcp/internal/registry"
|
||||
"git.wntrmute.dev/kyle/mcp/internal/runtime"
|
||||
mcpv1 "git.wntrmute.dev/mc/mcp/gen/mcp/v1"
|
||||
"git.wntrmute.dev/mc/mcp/internal/registry"
|
||||
"git.wntrmute.dev/mc/mcp/internal/runtime"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
@@ -30,6 +30,20 @@ func (a *Agent) StopService(ctx context.Context, req *mcpv1.StopServiceRequest)
|
||||
containerName := ContainerNameFor(req.GetName(), c.Name)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// Remove DNS record when stopping the service.
|
||||
if len(c.Routes) > 0 && a.DNS != nil {
|
||||
if err := a.DNS.RemoveRecord(ctx, req.GetName()); err != nil {
|
||||
a.Logger.Warn("failed to remove DNS record", "service", req.GetName(), "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := a.Runtime.Stop(ctx, containerName); err != nil {
|
||||
a.Logger.Info("stop container (ignored)", "container", containerName, "error", err)
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
mcpv1 "git.wntrmute.dev/kyle/mcp/gen/mcp/v1"
|
||||
"git.wntrmute.dev/kyle/mcp/internal/registry"
|
||||
mcpv1 "git.wntrmute.dev/mc/mcp/gen/mcp/v1"
|
||||
"git.wntrmute.dev/mc/mcp/internal/registry"
|
||||
"golang.org/x/sys/unix"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
@@ -33,8 +33,8 @@ func (pa *PortAllocator) Allocate() (int, error) {
|
||||
pa.mu.Lock()
|
||||
defer pa.mu.Unlock()
|
||||
|
||||
for i := range maxRetries {
|
||||
port := portRangeMin + rand.IntN(portRangeMax-portRangeMin)
|
||||
for range maxRetries {
|
||||
port := portRangeMin + rand.IntN(portRangeMax-portRangeMin) //nolint:gosec // port selection, not security
|
||||
if pa.allocated[port] {
|
||||
continue
|
||||
}
|
||||
@@ -45,7 +45,6 @@ func (pa *PortAllocator) Allocate() (int, error) {
|
||||
|
||||
pa.allocated[port] = true
|
||||
return port, nil
|
||||
_ = i
|
||||
}
|
||||
|
||||
return 0, fmt.Errorf("failed to allocate port after %d attempts", maxRetries)
|
||||
|
||||
138
internal/agent/proxy.go
Normal file
138
internal/agent/proxy.go
Normal file
@@ -0,0 +1,138 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"path/filepath"
|
||||
|
||||
"git.wntrmute.dev/mc/mc-proxy/client/mcproxy"
|
||||
"git.wntrmute.dev/mc/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)
|
||||
}
|
||||
57
internal/agent/proxy_test.go
Normal file
57
internal/agent/proxy_test.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"git.wntrmute.dev/mc/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)
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
mcpv1 "git.wntrmute.dev/kyle/mcp/gen/mcp/v1"
|
||||
"git.wntrmute.dev/kyle/mcp/internal/registry"
|
||||
mcpv1 "git.wntrmute.dev/mc/mcp/gen/mcp/v1"
|
||||
"git.wntrmute.dev/mc/mcp/internal/registry"
|
||||
)
|
||||
|
||||
// PurgeComponent removes stale registry entries for components that are both
|
||||
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
mcpv1 "git.wntrmute.dev/kyle/mcp/gen/mcp/v1"
|
||||
"git.wntrmute.dev/kyle/mcp/internal/registry"
|
||||
mcpv1 "git.wntrmute.dev/mc/mcp/gen/mcp/v1"
|
||||
"git.wntrmute.dev/mc/mcp/internal/registry"
|
||||
)
|
||||
|
||||
func TestPurgeComponentRemoved(t *testing.T) {
|
||||
|
||||
@@ -5,9 +5,9 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
mcpv1 "git.wntrmute.dev/kyle/mcp/gen/mcp/v1"
|
||||
"git.wntrmute.dev/kyle/mcp/internal/registry"
|
||||
"git.wntrmute.dev/kyle/mcp/internal/runtime"
|
||||
mcpv1 "git.wntrmute.dev/mc/mcp/gen/mcp/v1"
|
||||
"git.wntrmute.dev/mc/mcp/internal/registry"
|
||||
"git.wntrmute.dev/mc/mcp/internal/runtime"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@ import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
mcpv1 "git.wntrmute.dev/kyle/mcp/gen/mcp/v1"
|
||||
"git.wntrmute.dev/kyle/mcp/internal/registry"
|
||||
"git.wntrmute.dev/kyle/mcp/internal/runtime"
|
||||
mcpv1 "git.wntrmute.dev/mc/mcp/gen/mcp/v1"
|
||||
"git.wntrmute.dev/mc/mcp/internal/registry"
|
||||
"git.wntrmute.dev/mc/mcp/internal/runtime"
|
||||
)
|
||||
|
||||
func TestListServices(t *testing.T) {
|
||||
|
||||
@@ -5,9 +5,9 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
mcpv1 "git.wntrmute.dev/kyle/mcp/gen/mcp/v1"
|
||||
"git.wntrmute.dev/kyle/mcp/internal/registry"
|
||||
"git.wntrmute.dev/kyle/mcp/internal/runtime"
|
||||
mcpv1 "git.wntrmute.dev/mc/mcp/gen/mcp/v1"
|
||||
"git.wntrmute.dev/mc/mcp/internal/registry"
|
||||
"git.wntrmute.dev/mc/mcp/internal/runtime"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
@@ -157,6 +157,24 @@ func (a *Agent) reconcileUntracked(ctx context.Context, known map[string]bool) e
|
||||
|
||||
// protoToComponent converts a proto ComponentSpec to a registry Component.
|
||||
func protoToComponent(service string, cs *mcpv1.ComponentSpec, desiredState string) *registry.Component {
|
||||
var routes []registry.Route
|
||||
for _, r := range cs.GetRoutes() {
|
||||
mode := r.GetMode()
|
||||
if mode == "" {
|
||||
mode = "l4"
|
||||
}
|
||||
name := r.GetName()
|
||||
if name == "" {
|
||||
name = "default"
|
||||
}
|
||||
routes = append(routes, registry.Route{
|
||||
Name: name,
|
||||
Port: int(r.GetPort()),
|
||||
Mode: mode,
|
||||
Hostname: r.GetHostname(),
|
||||
})
|
||||
}
|
||||
|
||||
return ®istry.Component{
|
||||
Name: cs.GetName(),
|
||||
Service: service,
|
||||
@@ -167,6 +185,7 @@ func protoToComponent(service string, cs *mcpv1.ComponentSpec, desiredState stri
|
||||
Ports: cs.GetPorts(),
|
||||
Volumes: cs.GetVolumes(),
|
||||
Cmd: cs.GetCmd(),
|
||||
Routes: routes,
|
||||
DesiredState: desiredState,
|
||||
Version: runtime.ExtractVersion(cs.GetImage()),
|
||||
}
|
||||
|
||||
@@ -6,9 +6,9 @@ import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"git.wntrmute.dev/kyle/mcp/internal/config"
|
||||
"git.wntrmute.dev/kyle/mcp/internal/registry"
|
||||
"git.wntrmute.dev/kyle/mcp/internal/runtime"
|
||||
"git.wntrmute.dev/mc/mcp/internal/config"
|
||||
"git.wntrmute.dev/mc/mcp/internal/registry"
|
||||
"git.wntrmute.dev/mc/mcp/internal/runtime"
|
||||
)
|
||||
|
||||
// fakeRuntime implements runtime.Runtime for testing.
|
||||
|
||||
100
internal/agent/undeploy.go
Normal file
100
internal/agent/undeploy.go
Normal file
@@ -0,0 +1,100 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
mcpv1 "git.wntrmute.dev/mc/mcp/gen/mcp/v1"
|
||||
"git.wntrmute.dev/mc/mcp/internal/registry"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// UndeployService fully tears down a service: removes routes, DNS records,
|
||||
// TLS certificates, stops and removes containers, releases ports, and marks
|
||||
// the service inactive. This is the inverse of Deploy.
|
||||
func (a *Agent) UndeployService(ctx context.Context, req *mcpv1.UndeployServiceRequest) (*mcpv1.UndeployServiceResponse, error) {
|
||||
a.Logger.Info("UndeployService", "service", req.GetName())
|
||||
|
||||
if req.GetName() == "" {
|
||||
return nil, status.Error(codes.InvalidArgument, "service name is required")
|
||||
}
|
||||
|
||||
serviceName := req.GetName()
|
||||
|
||||
components, err := registry.ListComponents(a.DB, serviceName)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "list components: %v", err)
|
||||
}
|
||||
|
||||
var results []*mcpv1.ComponentResult
|
||||
dnsRemoved := false
|
||||
|
||||
for _, c := range components {
|
||||
r := a.undeployComponent(ctx, serviceName, &c, &dnsRemoved)
|
||||
results = append(results, r)
|
||||
}
|
||||
|
||||
// Mark the service as inactive.
|
||||
if err := registry.UpdateServiceActive(a.DB, serviceName, false); err != nil {
|
||||
a.Logger.Warn("failed to mark service inactive", "service", serviceName, "err", err)
|
||||
}
|
||||
|
||||
return &mcpv1.UndeployServiceResponse{Results: results}, nil
|
||||
}
|
||||
|
||||
// undeployComponent tears down a single component. The dnsRemoved flag
|
||||
// tracks whether DNS has already been removed for this service (DNS is
|
||||
// per-service, not per-component).
|
||||
func (a *Agent) undeployComponent(ctx context.Context, serviceName string, c *registry.Component, dnsRemoved *bool) *mcpv1.ComponentResult {
|
||||
containerName := ContainerNameFor(serviceName, c.Name)
|
||||
r := &mcpv1.ComponentResult{Name: c.Name, Success: true}
|
||||
|
||||
// 1. Remove mc-proxy routes.
|
||||
if len(c.Routes) > 0 && a.Proxy != nil {
|
||||
if err := a.Proxy.RemoveRoutes(ctx, serviceName, c.Routes); err != nil {
|
||||
a.Logger.Warn("failed to remove routes", "service", serviceName, "component", c.Name, "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Remove DNS records (once per service).
|
||||
if len(c.Routes) > 0 && a.DNS != nil && !*dnsRemoved {
|
||||
if err := a.DNS.RemoveRecord(ctx, serviceName); err != nil {
|
||||
a.Logger.Warn("failed to remove DNS record", "service", serviceName, "err", err)
|
||||
}
|
||||
*dnsRemoved = true
|
||||
}
|
||||
|
||||
// 3. Remove TLS certs (L7 routes only).
|
||||
if hasL7Routes(c.Routes) && a.Certs != nil {
|
||||
if err := a.Certs.RemoveCert(serviceName); err != nil {
|
||||
a.Logger.Warn("failed to remove TLS cert", "service", serviceName, "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Stop and remove the container.
|
||||
if err := a.Runtime.Stop(ctx, containerName); err != nil {
|
||||
a.Logger.Info("stop container (ignored)", "container", containerName, "error", err)
|
||||
}
|
||||
if err := a.Runtime.Remove(ctx, containerName); err != nil {
|
||||
a.Logger.Info("remove container (ignored)", "container", containerName, "error", err)
|
||||
}
|
||||
|
||||
// 5. Release allocated ports.
|
||||
if a.PortAlloc != nil {
|
||||
hostPorts, err := registry.GetRouteHostPorts(a.DB, serviceName, c.Name)
|
||||
if err == nil {
|
||||
for _, port := range hostPorts {
|
||||
a.PortAlloc.Release(port)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 6. Update registry state.
|
||||
if err := registry.UpdateComponentState(a.DB, serviceName, c.Name, "removed", "removed"); err != nil {
|
||||
r.Success = false
|
||||
r.Error = fmt.Sprintf("update state: %v", err)
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
@@ -10,12 +10,67 @@ import (
|
||||
|
||||
// AgentConfig is the configuration for the mcp-agent daemon.
|
||||
type AgentConfig struct {
|
||||
Server ServerConfig `toml:"server"`
|
||||
Database DatabaseConfig `toml:"database"`
|
||||
MCIAS MCIASConfig `toml:"mcias"`
|
||||
Agent AgentSettings `toml:"agent"`
|
||||
Monitor MonitorConfig `toml:"monitor"`
|
||||
Log LogConfig `toml:"log"`
|
||||
Server ServerConfig `toml:"server"`
|
||||
Database DatabaseConfig `toml:"database"`
|
||||
MCIAS MCIASConfig `toml:"mcias"`
|
||||
Agent AgentSettings `toml:"agent"`
|
||||
MCProxy MCProxyConfig `toml:"mcproxy"`
|
||||
Metacrypt MetacryptConfig `toml:"metacrypt"`
|
||||
MCNS MCNSConfig `toml:"mcns"`
|
||||
Monitor MonitorConfig `toml:"monitor"`
|
||||
Log LogConfig `toml:"log"`
|
||||
}
|
||||
|
||||
// MetacryptConfig holds the Metacrypt CA integration settings for
|
||||
// automated TLS cert provisioning. If ServerURL is empty, cert
|
||||
// provisioning is disabled.
|
||||
type MetacryptConfig struct {
|
||||
// ServerURL is the Metacrypt API base URL (e.g. "https://metacrypt:8443").
|
||||
ServerURL string `toml:"server_url"`
|
||||
|
||||
// CACert is the path to the CA certificate for verifying Metacrypt's TLS.
|
||||
CACert string `toml:"ca_cert"`
|
||||
|
||||
// Mount is the CA engine mount name. Defaults to "pki".
|
||||
Mount string `toml:"mount"`
|
||||
|
||||
// Issuer is the intermediate CA issuer name. Defaults to "infra".
|
||||
Issuer string `toml:"issuer"`
|
||||
|
||||
// TokenPath is the path to the MCIAS service token file.
|
||||
TokenPath string `toml:"token_path"`
|
||||
}
|
||||
|
||||
// MCNSConfig holds the MCNS DNS integration settings for automated
|
||||
// DNS record registration. If ServerURL is empty, DNS registration
|
||||
// is disabled.
|
||||
type MCNSConfig struct {
|
||||
// ServerURL is the MCNS API base URL (e.g. "https://localhost:28443").
|
||||
ServerURL string `toml:"server_url"`
|
||||
|
||||
// CACert is the path to the CA certificate for verifying MCNS's TLS.
|
||||
CACert string `toml:"ca_cert"`
|
||||
|
||||
// TokenPath is the path to the MCIAS service token file.
|
||||
TokenPath string `toml:"token_path"`
|
||||
|
||||
// Zone is the DNS zone for service records. Defaults to "svc.mcp.metacircular.net".
|
||||
Zone string `toml:"zone"`
|
||||
|
||||
// NodeAddr is the IP address to register as the A record value.
|
||||
NodeAddr string `toml:"node_addr"`
|
||||
}
|
||||
|
||||
// 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.
|
||||
@@ -134,6 +189,18 @@ func applyAgentDefaults(cfg *AgentConfig) {
|
||||
if cfg.Agent.ContainerRuntime == "" {
|
||||
cfg.Agent.ContainerRuntime = "podman"
|
||||
}
|
||||
if cfg.MCProxy.CertDir == "" {
|
||||
cfg.MCProxy.CertDir = "/srv/mc-proxy/certs"
|
||||
}
|
||||
if cfg.Metacrypt.Mount == "" {
|
||||
cfg.Metacrypt.Mount = "pki"
|
||||
}
|
||||
if cfg.Metacrypt.Issuer == "" {
|
||||
cfg.Metacrypt.Issuer = "infra"
|
||||
}
|
||||
if cfg.MCNS.Zone == "" {
|
||||
cfg.MCNS.Zone = "svc.mcp.metacircular.net"
|
||||
}
|
||||
}
|
||||
|
||||
func applyAgentEnvOverrides(cfg *AgentConfig) {
|
||||
@@ -158,6 +225,27 @@ func applyAgentEnvOverrides(cfg *AgentConfig) {
|
||||
if v := os.Getenv("MCP_AGENT_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
|
||||
}
|
||||
if v := os.Getenv("MCP_AGENT_METACRYPT_SERVER_URL"); v != "" {
|
||||
cfg.Metacrypt.ServerURL = v
|
||||
}
|
||||
if v := os.Getenv("MCP_AGENT_METACRYPT_TOKEN_PATH"); v != "" {
|
||||
cfg.Metacrypt.TokenPath = v
|
||||
}
|
||||
if v := os.Getenv("MCP_AGENT_MCNS_SERVER_URL"); v != "" {
|
||||
cfg.MCNS.ServerURL = v
|
||||
}
|
||||
if v := os.Getenv("MCP_AGENT_MCNS_TOKEN_PATH"); v != "" {
|
||||
cfg.MCNS.TokenPath = v
|
||||
}
|
||||
if v := os.Getenv("MCP_AGENT_MCNS_NODE_ADDR"); v != "" {
|
||||
cfg.MCNS.NodeAddr = v
|
||||
}
|
||||
}
|
||||
|
||||
func validateAgentConfig(cfg *AgentConfig) error {
|
||||
|
||||
@@ -163,6 +163,19 @@ func TestLoadAgentConfig(t *testing.T) {
|
||||
if cfg.Log.Level != "debug" {
|
||||
t.Fatalf("log.level: got %q", cfg.Log.Level)
|
||||
}
|
||||
|
||||
// Metacrypt defaults when section is omitted.
|
||||
if cfg.Metacrypt.Mount != "pki" {
|
||||
t.Fatalf("metacrypt.mount default: got %q, want pki", cfg.Metacrypt.Mount)
|
||||
}
|
||||
if cfg.Metacrypt.Issuer != "infra" {
|
||||
t.Fatalf("metacrypt.issuer default: got %q, want infra", cfg.Metacrypt.Issuer)
|
||||
}
|
||||
|
||||
// MCNS defaults when section is omitted.
|
||||
if cfg.MCNS.Zone != "svc.mcp.metacircular.net" {
|
||||
t.Fatalf("mcns.zone default: got %q, want svc.mcp.metacircular.net", cfg.MCNS.Zone)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCLIConfigValidation(t *testing.T) {
|
||||
@@ -439,6 +452,155 @@ level = "info"
|
||||
})
|
||||
}
|
||||
|
||||
func TestAgentConfigMetacrypt(t *testing.T) {
|
||||
cfgStr := `
|
||||
[server]
|
||||
grpc_addr = "0.0.0.0:9444"
|
||||
tls_cert = "/srv/mcp/cert.pem"
|
||||
tls_key = "/srv/mcp/key.pem"
|
||||
[database]
|
||||
path = "/srv/mcp/mcp.db"
|
||||
[mcias]
|
||||
server_url = "https://mcias.metacircular.net:8443"
|
||||
service_name = "mcp-agent"
|
||||
[agent]
|
||||
node_name = "rift"
|
||||
[metacrypt]
|
||||
server_url = "https://metacrypt.metacircular.net:8443"
|
||||
ca_cert = "/etc/mcp/metacircular-ca.pem"
|
||||
mount = "custom-pki"
|
||||
issuer = "custom-issuer"
|
||||
token_path = "/srv/mcp/metacrypt-token"
|
||||
`
|
||||
path := writeTempConfig(t, cfgStr)
|
||||
cfg, err := LoadAgentConfig(path)
|
||||
if err != nil {
|
||||
t.Fatalf("load: %v", err)
|
||||
}
|
||||
|
||||
if cfg.Metacrypt.ServerURL != "https://metacrypt.metacircular.net:8443" {
|
||||
t.Fatalf("metacrypt.server_url: got %q", cfg.Metacrypt.ServerURL)
|
||||
}
|
||||
if cfg.Metacrypt.CACert != "/etc/mcp/metacircular-ca.pem" {
|
||||
t.Fatalf("metacrypt.ca_cert: got %q", cfg.Metacrypt.CACert)
|
||||
}
|
||||
if cfg.Metacrypt.Mount != "custom-pki" {
|
||||
t.Fatalf("metacrypt.mount: got %q", cfg.Metacrypt.Mount)
|
||||
}
|
||||
if cfg.Metacrypt.Issuer != "custom-issuer" {
|
||||
t.Fatalf("metacrypt.issuer: got %q", cfg.Metacrypt.Issuer)
|
||||
}
|
||||
if cfg.Metacrypt.TokenPath != "/srv/mcp/metacrypt-token" {
|
||||
t.Fatalf("metacrypt.token_path: got %q", cfg.Metacrypt.TokenPath)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAgentConfigMetacryptEnvOverrides(t *testing.T) {
|
||||
minimal := `
|
||||
[server]
|
||||
grpc_addr = "0.0.0.0:9444"
|
||||
tls_cert = "/srv/mcp/cert.pem"
|
||||
tls_key = "/srv/mcp/key.pem"
|
||||
[database]
|
||||
path = "/srv/mcp/mcp.db"
|
||||
[mcias]
|
||||
server_url = "https://mcias.metacircular.net:8443"
|
||||
service_name = "mcp-agent"
|
||||
[agent]
|
||||
node_name = "rift"
|
||||
`
|
||||
t.Setenv("MCP_AGENT_METACRYPT_SERVER_URL", "https://override.metacrypt:8443")
|
||||
t.Setenv("MCP_AGENT_METACRYPT_TOKEN_PATH", "/override/token")
|
||||
|
||||
path := writeTempConfig(t, minimal)
|
||||
cfg, err := LoadAgentConfig(path)
|
||||
if err != nil {
|
||||
t.Fatalf("load: %v", err)
|
||||
}
|
||||
|
||||
if cfg.Metacrypt.ServerURL != "https://override.metacrypt:8443" {
|
||||
t.Fatalf("metacrypt.server_url: got %q", cfg.Metacrypt.ServerURL)
|
||||
}
|
||||
if cfg.Metacrypt.TokenPath != "/override/token" {
|
||||
t.Fatalf("metacrypt.token_path: got %q", cfg.Metacrypt.TokenPath)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAgentConfigMCNS(t *testing.T) {
|
||||
cfgStr := `
|
||||
[server]
|
||||
grpc_addr = "0.0.0.0:9444"
|
||||
tls_cert = "/srv/mcp/cert.pem"
|
||||
tls_key = "/srv/mcp/key.pem"
|
||||
[database]
|
||||
path = "/srv/mcp/mcp.db"
|
||||
[mcias]
|
||||
server_url = "https://mcias.metacircular.net:8443"
|
||||
service_name = "mcp-agent"
|
||||
[agent]
|
||||
node_name = "rift"
|
||||
[mcns]
|
||||
server_url = "https://localhost:28443"
|
||||
ca_cert = "/srv/mcp/certs/metacircular-ca.pem"
|
||||
token_path = "/srv/mcp/metacrypt-token"
|
||||
zone = "custom.zone"
|
||||
node_addr = "10.0.0.1"
|
||||
`
|
||||
path := writeTempConfig(t, cfgStr)
|
||||
cfg, err := LoadAgentConfig(path)
|
||||
if err != nil {
|
||||
t.Fatalf("load: %v", err)
|
||||
}
|
||||
|
||||
if cfg.MCNS.ServerURL != "https://localhost:28443" {
|
||||
t.Fatalf("mcns.server_url: got %q", cfg.MCNS.ServerURL)
|
||||
}
|
||||
if cfg.MCNS.CACert != "/srv/mcp/certs/metacircular-ca.pem" {
|
||||
t.Fatalf("mcns.ca_cert: got %q", cfg.MCNS.CACert)
|
||||
}
|
||||
if cfg.MCNS.Zone != "custom.zone" {
|
||||
t.Fatalf("mcns.zone: got %q", cfg.MCNS.Zone)
|
||||
}
|
||||
if cfg.MCNS.NodeAddr != "10.0.0.1" {
|
||||
t.Fatalf("mcns.node_addr: got %q", cfg.MCNS.NodeAddr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAgentConfigMCNSEnvOverrides(t *testing.T) {
|
||||
minimal := `
|
||||
[server]
|
||||
grpc_addr = "0.0.0.0:9444"
|
||||
tls_cert = "/srv/mcp/cert.pem"
|
||||
tls_key = "/srv/mcp/key.pem"
|
||||
[database]
|
||||
path = "/srv/mcp/mcp.db"
|
||||
[mcias]
|
||||
server_url = "https://mcias.metacircular.net:8443"
|
||||
service_name = "mcp-agent"
|
||||
[agent]
|
||||
node_name = "rift"
|
||||
`
|
||||
t.Setenv("MCP_AGENT_MCNS_SERVER_URL", "https://override:28443")
|
||||
t.Setenv("MCP_AGENT_MCNS_TOKEN_PATH", "/override/token")
|
||||
t.Setenv("MCP_AGENT_MCNS_NODE_ADDR", "10.0.0.99")
|
||||
|
||||
path := writeTempConfig(t, minimal)
|
||||
cfg, err := LoadAgentConfig(path)
|
||||
if err != nil {
|
||||
t.Fatalf("load: %v", err)
|
||||
}
|
||||
|
||||
if cfg.MCNS.ServerURL != "https://override:28443" {
|
||||
t.Fatalf("mcns.server_url: got %q", cfg.MCNS.ServerURL)
|
||||
}
|
||||
if cfg.MCNS.TokenPath != "/override/token" {
|
||||
t.Fatalf("mcns.token_path: got %q", cfg.MCNS.TokenPath)
|
||||
}
|
||||
if cfg.MCNS.NodeAddr != "10.0.0.99" {
|
||||
t.Fatalf("mcns.node_addr: got %q", cfg.MCNS.NodeAddr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDurationParsing(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
"os/exec"
|
||||
"time"
|
||||
|
||||
"git.wntrmute.dev/kyle/mcp/internal/config"
|
||||
"git.wntrmute.dev/kyle/mcp/internal/registry"
|
||||
"git.wntrmute.dev/mc/mcp/internal/config"
|
||||
"git.wntrmute.dev/mc/mcp/internal/registry"
|
||||
)
|
||||
|
||||
// Alerter evaluates state transitions and fires alerts for drift or flapping.
|
||||
|
||||
@@ -7,9 +7,9 @@ import (
|
||||
"log/slog"
|
||||
"time"
|
||||
|
||||
"git.wntrmute.dev/kyle/mcp/internal/config"
|
||||
"git.wntrmute.dev/kyle/mcp/internal/registry"
|
||||
"git.wntrmute.dev/kyle/mcp/internal/runtime"
|
||||
"git.wntrmute.dev/mc/mcp/internal/config"
|
||||
"git.wntrmute.dev/mc/mcp/internal/registry"
|
||||
"git.wntrmute.dev/mc/mcp/internal/runtime"
|
||||
)
|
||||
|
||||
// Monitor watches container states and compares them to the registry,
|
||||
|
||||
@@ -9,9 +9,9 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.wntrmute.dev/kyle/mcp/internal/config"
|
||||
"git.wntrmute.dev/kyle/mcp/internal/registry"
|
||||
"git.wntrmute.dev/kyle/mcp/internal/runtime"
|
||||
"git.wntrmute.dev/mc/mcp/internal/config"
|
||||
"git.wntrmute.dev/mc/mcp/internal/registry"
|
||||
"git.wntrmute.dev/mc/mcp/internal/runtime"
|
||||
)
|
||||
|
||||
func openTestDB(t *testing.T) *sql.DB {
|
||||
|
||||
@@ -199,25 +199,27 @@ func (p *Podman) Push(ctx context.Context, image string) error {
|
||||
}
|
||||
|
||||
// ImageExists checks whether an image tag exists in a remote registry.
|
||||
// Uses skopeo inspect which works for both regular images and multi-arch
|
||||
// manifests, unlike podman manifest inspect which only handles manifests.
|
||||
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
|
||||
cmd := exec.CommandContext(ctx, "skopeo", "inspect", "--tls-verify=false", "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 {
|
||||
if ok := errors.As(err, &exitErr); ok && exitErr.ExitCode() != 0 {
|
||||
return false, nil
|
||||
}
|
||||
return false, fmt.Errorf("podman manifest inspect %q: %w", image, err)
|
||||
return false, fmt.Errorf("skopeo inspect %q: %w", image, err)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// podmanPSEntry is a single entry from podman ps --format json.
|
||||
type podmanPSEntry struct {
|
||||
Names []string `json:"Names"`
|
||||
Image string `json:"Image"`
|
||||
State string `json:"State"`
|
||||
Command []string `json:"Command"`
|
||||
Names []string `json:"Names"`
|
||||
Image string `json:"Image"`
|
||||
State string `json:"State"`
|
||||
Command []string `json:"Command"`
|
||||
StartedAt int64 `json:"StartedAt"`
|
||||
}
|
||||
|
||||
// List returns information about all containers.
|
||||
@@ -239,12 +241,16 @@ func (p *Podman) List(ctx context.Context) ([]ContainerInfo, error) {
|
||||
if len(e.Names) > 0 {
|
||||
name = e.Names[0]
|
||||
}
|
||||
infos = append(infos, ContainerInfo{
|
||||
info := ContainerInfo{
|
||||
Name: name,
|
||||
Image: e.Image,
|
||||
State: e.State,
|
||||
Version: ExtractVersion(e.Image),
|
||||
})
|
||||
}
|
||||
if e.StartedAt > 0 {
|
||||
info.Started = time.Unix(e.StartedAt, 0)
|
||||
}
|
||||
infos = append(infos, info)
|
||||
}
|
||||
|
||||
return infos, nil
|
||||
|
||||
@@ -90,18 +90,19 @@ func TestBuildRunArgs(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("full spec with env", func(t *testing.T) {
|
||||
// Route-allocated ports: host port = container port (matches $PORT).
|
||||
spec := ContainerSpec{
|
||||
Name: "svc-api",
|
||||
Image: "img:latest",
|
||||
Network: "net",
|
||||
Ports: []string{"127.0.0.1:12345:8443"},
|
||||
Ports: []string{"127.0.0.1:12345:12345"},
|
||||
Volumes: []string{"/srv:/srv"},
|
||||
Env: []string{"PORT=12345"},
|
||||
}
|
||||
requireEqualArgs(t, p.BuildRunArgs(spec), []string{
|
||||
"run", "-d", "--name", "svc-api",
|
||||
"--network", "net",
|
||||
"-p", "127.0.0.1:12345:8443",
|
||||
"-p", "127.0.0.1:12345:12345",
|
||||
"-v", "/srv:/srv",
|
||||
"-e", "PORT=12345",
|
||||
"img:latest",
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
toml "github.com/pelletier/go-toml/v2"
|
||||
|
||||
mcpv1 "git.wntrmute.dev/kyle/mcp/gen/mcp/v1"
|
||||
mcpv1 "git.wntrmute.dev/mc/mcp/gen/mcp/v1"
|
||||
)
|
||||
|
||||
// ServiceDef is the top-level TOML structure for a service definition file.
|
||||
@@ -25,8 +25,8 @@ type ServiceDef struct {
|
||||
|
||||
// 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"`
|
||||
Images map[string]string `toml:"images"`
|
||||
UsesMCDSL bool `toml:"uses_mcdsl,omitempty"`
|
||||
}
|
||||
|
||||
// RouteDef describes a route for a component, used for automatic port
|
||||
@@ -210,7 +210,7 @@ func ToProto(def *ServiceDef) *mcpv1.ServiceSpec {
|
||||
for _, r := range c.Routes {
|
||||
cs.Routes = append(cs.Routes, &mcpv1.RouteSpec{
|
||||
Name: r.Name,
|
||||
Port: int32(r.Port),
|
||||
Port: int32(r.Port), //nolint:gosec // port range validated
|
||||
Mode: r.Mode,
|
||||
Hostname: r.Hostname,
|
||||
})
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
syntax = "proto3";
|
||||
package mcp.v1;
|
||||
|
||||
option go_package = "git.wntrmute.dev/kyle/mcp/gen/mcp/v1;mcpv1";
|
||||
option go_package = "git.wntrmute.dev/mc/mcp/gen/mcp/v1;mcpv1";
|
||||
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
service McpAgentService {
|
||||
// Service lifecycle
|
||||
rpc Deploy(DeployRequest) returns (DeployResponse);
|
||||
rpc UndeployService(UndeployServiceRequest) returns (UndeployServiceResponse);
|
||||
rpc StopService(StopServiceRequest) returns (StopServiceResponse);
|
||||
rpc StartService(StartServiceRequest) returns (StartServiceResponse);
|
||||
rpc RestartService(RestartServiceRequest) returns (RestartServiceResponse);
|
||||
@@ -38,7 +39,7 @@ service McpAgentService {
|
||||
|
||||
message RouteSpec {
|
||||
string name = 1; // route name (used for $PORT_<NAME>)
|
||||
int32 port = 2; // external port on mc-proxy
|
||||
int32 port = 2; // mc-proxy listener port (e.g. 443, 8443, 9443); NOT the container internal port
|
||||
string mode = 3; // "l4" or "l7"
|
||||
string hostname = 4; // optional public hostname override
|
||||
}
|
||||
@@ -102,6 +103,14 @@ message RestartServiceResponse {
|
||||
repeated ComponentResult results = 1;
|
||||
}
|
||||
|
||||
message UndeployServiceRequest {
|
||||
string name = 1;
|
||||
}
|
||||
|
||||
message UndeployServiceResponse {
|
||||
repeated ComponentResult results = 1;
|
||||
}
|
||||
|
||||
// --- Desired state ---
|
||||
|
||||
message SyncDesiredStateRequest {
|
||||
|
||||
331
vendor/git.wntrmute.dev/mc/mc-proxy/client/mcproxy/client.go
vendored
Normal file
331
vendor/git.wntrmute.dev/mc/mc-proxy/client/mcproxy/client.go
vendored
Normal 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/mc/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
|
||||
}
|
||||
}
|
||||
41
vendor/git.wntrmute.dev/mc/mc-proxy/client/mcproxy/doc.go
vendored
Normal file
41
vendor/git.wntrmute.dev/mc/mc-proxy/client/mcproxy/doc.go
vendored
Normal 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
|
||||
1564
vendor/git.wntrmute.dev/mc/mc-proxy/gen/mc_proxy/v1/admin.pb.go
vendored
Normal file
1564
vendor/git.wntrmute.dev/mc/mc-proxy/gen/mc_proxy/v1/admin.pb.go
vendored
Normal file
File diff suppressed because it is too large
Load Diff
511
vendor/git.wntrmute.dev/mc/mc-proxy/gen/mc_proxy/v1/admin_grpc.pb.go
vendored
Normal file
511
vendor/git.wntrmute.dev/mc/mc-proxy/gen/mc_proxy/v1/admin_grpc.pb.go
vendored
Normal 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",
|
||||
}
|
||||
350
vendor/google.golang.org/grpc/health/grpc_health_v1/health.pb.go
generated
vendored
Normal file
350
vendor/google.golang.org/grpc/health/grpc_health_v1/health.pb.go
generated
vendored
Normal 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
|
||||
}
|
||||
290
vendor/google.golang.org/grpc/health/grpc_health_v1/health_grpc.pb.go
generated
vendored
Normal file
290
vendor/google.golang.org/grpc/health/grpc_health_v1/health_grpc.pb.go
generated
vendored
Normal 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
5
vendor/modules.txt
vendored
@@ -1,3 +1,7 @@
|
||||
# git.wntrmute.dev/mc/mc-proxy v1.2.0
|
||||
## explicit; go 1.25.7
|
||||
git.wntrmute.dev/mc/mc-proxy/client/mcproxy
|
||||
git.wntrmute.dev/mc/mc-proxy/gen/mc_proxy/v1
|
||||
# github.com/dustin/go-humanize v1.0.1
|
||||
## explicit; go 1.16
|
||||
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/grpclog
|
||||
google.golang.org/grpc/grpclog/internal
|
||||
google.golang.org/grpc/health/grpc_health_v1
|
||||
google.golang.org/grpc/internal
|
||||
google.golang.org/grpc/internal/backoff
|
||||
google.golang.org/grpc/internal/balancer/gracefulswitch
|
||||
|
||||
Reference in New Issue
Block a user