Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 86d516acf6 | |||
| dd167b8e0b | |||
| 41437e3730 | |||
| 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,10 @@ 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/auth"
|
||||
"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 {
|
||||
@@ -52,6 +53,17 @@ func buildServiceImages(ctx context.Context, cfg *config.CLIConfig, def *service
|
||||
|
||||
sourceDir := filepath.Join(cfg.Build.Workspace, def.Path)
|
||||
|
||||
// Auto-login to the registry using the CLI's stored MCIAS token.
|
||||
// MCR accepts JWTs as passwords, so this works for both human and
|
||||
// service account tokens. Failures are non-fatal — existing podman
|
||||
// auth may suffice.
|
||||
if token, err := auth.LoadToken(cfg.Auth.TokenPath); err == nil && token != "" {
|
||||
registry := extractRegistry(def)
|
||||
if registry != "" {
|
||||
_ = rt.Login(ctx, registry, "mcp", token)
|
||||
}
|
||||
}
|
||||
|
||||
for imageName, dockerfile := range def.Build.Images {
|
||||
if imageFilter != "" && imageName != imageFilter {
|
||||
continue
|
||||
@@ -96,6 +108,19 @@ func findImageRef(def *servicedef.ServiceDef, imageName string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// extractRegistry returns the registry host from the first component's
|
||||
// image reference (e.g., "mcr.svc.mcp.metacircular.net:8443" from
|
||||
// "mcr.svc.mcp.metacircular.net:8443/mcq:v0.1.1"). Returns empty
|
||||
// string if no slash is found.
|
||||
func extractRegistry(def *servicedef.ServiceDef) string {
|
||||
for _, c := range def.Components {
|
||||
if i := strings.LastIndex(c.Image, "/"); i > 0 {
|
||||
return c.Image[:i]
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// extractRepoName returns the repository name from an image reference.
|
||||
// Examples:
|
||||
//
|
||||
@@ -124,6 +149,8 @@ func ensureImages(ctx context.Context, cfg *config.CLIConfig, def *servicedef.Se
|
||||
return nil // no build config, skip auto-build
|
||||
}
|
||||
|
||||
registryLoginDone := false
|
||||
|
||||
for _, c := range def.Components {
|
||||
if component != "" && c.Name != component {
|
||||
continue
|
||||
@@ -153,6 +180,17 @@ func ensureImages(ctx context.Context, cfg *config.CLIConfig, def *servicedef.Se
|
||||
|
||||
sourceDir := filepath.Join(cfg.Build.Workspace, def.Path)
|
||||
|
||||
// Auto-login to registry before first push.
|
||||
if !registryLoginDone {
|
||||
if token, err := auth.LoadToken(cfg.Auth.TokenPath); err == nil && token != "" {
|
||||
registry := extractRegistry(def)
|
||||
if registry != "" {
|
||||
_ = rt.Login(ctx, registry, "mcp", token)
|
||||
}
|
||||
}
|
||||
registryLoginDone = true
|
||||
}
|
||||
|
||||
fmt.Printf("image %s not found, building from %s\n", c.Image, dockerfile)
|
||||
if err := rt.Build(ctx, c.Image, sourceDir, dockerfile); err != nil {
|
||||
return fmt.Errorf("auto-build %s: %w", c.Image, err)
|
||||
|
||||
@@ -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,9 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"git.wntrmute.dev/kyle/mcp/internal/auth"
|
||||
"git.wntrmute.dev/kyle/mcp/internal/config"
|
||||
"git.wntrmute.dev/mc/mcdsl/terminal"
|
||||
"git.wntrmute.dev/mc/mcp/internal/auth"
|
||||
"git.wntrmute.dev/mc/mcp/internal/config"
|
||||
)
|
||||
|
||||
func loginCmd() *cobra.Command {
|
||||
@@ -33,14 +34,11 @@ func loginCmd() *cobra.Command {
|
||||
}
|
||||
username := strings.TrimSpace(scanner.Text())
|
||||
|
||||
fmt.Print("Password: ")
|
||||
if !scanner.Scan() {
|
||||
if err := scanner.Err(); err != nil {
|
||||
password, err := terminal.ReadPassword("Password: ")
|
||||
if err != nil {
|
||||
return fmt.Errorf("read password: %w", err)
|
||||
}
|
||||
return fmt.Errorf("read password: unexpected end of input")
|
||||
}
|
||||
password := strings.TrimSpace(scanner.Text())
|
||||
password = strings.TrimSpace(password)
|
||||
|
||||
token, err := auth.Login(cfg.MCIAS.ServerURL, cfg.MCIAS.CACert, username, password)
|
||||
if err != nil {
|
||||
|
||||
@@ -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,
|
||||
|
||||
5
go.mod
5
go.mod
@@ -1,8 +1,10 @@
|
||||
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
|
||||
git.wntrmute.dev/mc/mcdsl v1.3.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,6 +22,7 @@ require (
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/spf13/pflag v1.0.9 // indirect
|
||||
golang.org/x/net v0.48.0 // indirect
|
||||
golang.org/x/term v0.41.0 // indirect
|
||||
golang.org/x/text v0.32.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect
|
||||
modernc.org/libc v1.70.0 // indirect
|
||||
|
||||
22
go.sum
22
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.3.0 h1:QYmRdGDHjDEyNQpiKqHqPflpwNJcP0cFR9hcfMza/x4=
|
||||
git.wntrmute.dev/mc/mcdsl v1.3.0/go.mod h1:MhYahIu7Sg53lE2zpQ20nlrsoNRjQzOJBAlCmom2wJc=
|
||||
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=
|
||||
@@ -54,6 +74,8 @@ golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
|
||||
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU=
|
||||
golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A=
|
||||
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
|
||||
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
|
||||
golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -206,7 +206,10 @@ func TokenInfoFromContext(ctx context.Context) *TokenInfo {
|
||||
}
|
||||
|
||||
// AuthInterceptor returns a gRPC unary server interceptor that validates
|
||||
// bearer tokens and requires the "admin" role.
|
||||
// bearer tokens. Any authenticated user or system account is accepted,
|
||||
// except guests which are explicitly rejected. Admin role is not required
|
||||
// for agent operations — it is reserved for MCIAS account management and
|
||||
// policy changes.
|
||||
func AuthInterceptor(validator TokenValidator) grpc.UnaryServerInterceptor {
|
||||
return func(
|
||||
ctx context.Context,
|
||||
@@ -240,9 +243,9 @@ func AuthInterceptor(validator TokenValidator) grpc.UnaryServerInterceptor {
|
||||
return nil, status.Error(codes.Unauthenticated, "invalid token")
|
||||
}
|
||||
|
||||
if !tokenInfo.HasRole("admin") {
|
||||
slog.Warn("permission denied", "method", info.FullMethod, "user", tokenInfo.Username)
|
||||
return nil, status.Error(codes.PermissionDenied, "admin role required")
|
||||
if tokenInfo.HasRole("guest") {
|
||||
slog.Warn("guest access denied", "method", info.FullMethod, "user", tokenInfo.Username)
|
||||
return nil, status.Error(codes.PermissionDenied, "guest access not permitted")
|
||||
}
|
||||
|
||||
slog.Info("rpc", "method", info.FullMethod, "user", tokenInfo.Username, "account_type", tokenInfo.AccountType)
|
||||
|
||||
@@ -126,7 +126,7 @@ func TestInterceptorRejectsInvalidToken(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestInterceptorRejectsNonAdmin(t *testing.T) {
|
||||
func TestInterceptorAcceptsRegularUser(t *testing.T) {
|
||||
server := mockMCIAS(t, func(authHeader string) (any, int) {
|
||||
return &TokenInfo{
|
||||
Valid: true,
|
||||
@@ -142,6 +142,28 @@ func TestInterceptorRejectsNonAdmin(t *testing.T) {
|
||||
md := metadata.Pairs("authorization", "Bearer user-token")
|
||||
ctx := metadata.NewIncomingContext(context.Background(), md)
|
||||
|
||||
_, err := callInterceptor(ctx, v)
|
||||
if err != nil {
|
||||
t.Fatalf("expected regular user to be accepted, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInterceptorRejectsGuest(t *testing.T) {
|
||||
server := mockMCIAS(t, func(authHeader string) (any, int) {
|
||||
return &TokenInfo{
|
||||
Valid: true,
|
||||
Username: "visitor",
|
||||
Roles: []string{"guest"},
|
||||
AccountType: "human",
|
||||
}, http.StatusOK
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
v := validatorFromServer(t, server)
|
||||
|
||||
md := metadata.Pairs("authorization", "Bearer guest-token")
|
||||
ctx := metadata.NewIncomingContext(context.Background(), md)
|
||||
|
||||
_, err := callInterceptor(ctx, v)
|
||||
if err == nil {
|
||||
t.Fatal("expected error, got nil")
|
||||
|
||||
@@ -14,10 +14,65 @@ type AgentConfig struct {
|
||||
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.
|
||||
type ServerConfig struct {
|
||||
GRPCAddr string `toml:"grpc_addr"`
|
||||
@@ -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 {
|
||||
|
||||
@@ -178,6 +178,18 @@ func (p *Podman) Inspect(ctx context.Context, name string) (ContainerInfo, error
|
||||
return info, nil
|
||||
}
|
||||
|
||||
// Login authenticates to a container registry using the given token as
|
||||
// the password. This enables non-interactive push with service account
|
||||
// tokens (MCR accepts MCIAS JWTs as passwords).
|
||||
func (p *Podman) Login(ctx context.Context, registry, username, token string) error {
|
||||
cmd := exec.CommandContext(ctx, p.command(), "login", "--username", username, "--password-stdin", registry) //nolint:gosec // args built programmatically
|
||||
cmd.Stdin = strings.NewReader(token)
|
||||
if out, err := cmd.CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("podman login %q: %w: %s", registry, err, out)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Build builds a container image from a Dockerfile.
|
||||
func (p *Podman) Build(ctx context.Context, image, contextDir, dockerfile string) error {
|
||||
args := []string{"build", "-t", image, "-f", dockerfile, contextDir}
|
||||
@@ -199,15 +211,16 @@ 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
|
||||
}
|
||||
@@ -218,6 +231,7 @@ type podmanPSEntry struct {
|
||||
Image string `json:"Image"`
|
||||
State string `json:"State"`
|
||||
Command []string `json:"Command"`
|
||||
StartedAt int64 `json:"StartedAt"`
|
||||
}
|
||||
|
||||
// List returns information about all containers.
|
||||
@@ -239,12 +253,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.
|
||||
@@ -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",
|
||||
}
|
||||
22
vendor/git.wntrmute.dev/mc/mcdsl/terminal/terminal.go
vendored
Normal file
22
vendor/git.wntrmute.dev/mc/mcdsl/terminal/terminal.go
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
// Package terminal provides secure terminal input helpers for CLI tools.
|
||||
package terminal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
// ReadPassword prints the given prompt to stderr and reads a password
|
||||
// from the terminal with echo disabled. It prints a newline after the
|
||||
// input is complete so the cursor advances normally.
|
||||
func ReadPassword(prompt string) (string, error) {
|
||||
fmt.Fprint(os.Stderr, prompt)
|
||||
b, err := term.ReadPassword(int(os.Stdin.Fd())) //nolint:gosec // fd fits in int
|
||||
fmt.Fprintln(os.Stderr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(b), nil
|
||||
}
|
||||
8
vendor/golang.org/x/sys/plan9/asm.s
generated
vendored
Normal file
8
vendor/golang.org/x/sys/plan9/asm.s
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
TEXT ·use(SB),NOSPLIT,$0
|
||||
RET
|
||||
30
vendor/golang.org/x/sys/plan9/asm_plan9_386.s
generated
vendored
Normal file
30
vendor/golang.org/x/sys/plan9/asm_plan9_386.s
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System call support for 386, Plan 9
|
||||
//
|
||||
|
||||
// Just jump to package syscall's implementation for all these functions.
|
||||
// The runtime may know about them.
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-32
|
||||
JMP syscall·Syscall(SB)
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-44
|
||||
JMP syscall·Syscall6(SB)
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
||||
JMP syscall·RawSyscall(SB)
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
||||
JMP syscall·RawSyscall6(SB)
|
||||
|
||||
TEXT ·seek(SB),NOSPLIT,$0-36
|
||||
JMP syscall·seek(SB)
|
||||
|
||||
TEXT ·exit(SB),NOSPLIT,$4-4
|
||||
JMP syscall·exit(SB)
|
||||
30
vendor/golang.org/x/sys/plan9/asm_plan9_amd64.s
generated
vendored
Normal file
30
vendor/golang.org/x/sys/plan9/asm_plan9_amd64.s
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System call support for amd64, Plan 9
|
||||
//
|
||||
|
||||
// Just jump to package syscall's implementation for all these functions.
|
||||
// The runtime may know about them.
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-64
|
||||
JMP syscall·Syscall(SB)
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-88
|
||||
JMP syscall·Syscall6(SB)
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||
JMP syscall·RawSyscall(SB)
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||
JMP syscall·RawSyscall6(SB)
|
||||
|
||||
TEXT ·seek(SB),NOSPLIT,$0-56
|
||||
JMP syscall·seek(SB)
|
||||
|
||||
TEXT ·exit(SB),NOSPLIT,$8-8
|
||||
JMP syscall·exit(SB)
|
||||
25
vendor/golang.org/x/sys/plan9/asm_plan9_arm.s
generated
vendored
Normal file
25
vendor/golang.org/x/sys/plan9/asm_plan9_arm.s
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// System call support for plan9 on arm
|
||||
|
||||
// Just jump to package syscall's implementation for all these functions.
|
||||
// The runtime may know about them.
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-32
|
||||
JMP syscall·Syscall(SB)
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-44
|
||||
JMP syscall·Syscall6(SB)
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
||||
JMP syscall·RawSyscall(SB)
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
||||
JMP syscall·RawSyscall6(SB)
|
||||
|
||||
TEXT ·seek(SB),NOSPLIT,$0-36
|
||||
JMP syscall·exit(SB)
|
||||
70
vendor/golang.org/x/sys/plan9/const_plan9.go
generated
vendored
Normal file
70
vendor/golang.org/x/sys/plan9/const_plan9.go
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
package plan9
|
||||
|
||||
// Plan 9 Constants
|
||||
|
||||
// Open modes
|
||||
const (
|
||||
O_RDONLY = 0
|
||||
O_WRONLY = 1
|
||||
O_RDWR = 2
|
||||
O_TRUNC = 16
|
||||
O_CLOEXEC = 32
|
||||
O_EXCL = 0x1000
|
||||
)
|
||||
|
||||
// Rfork flags
|
||||
const (
|
||||
RFNAMEG = 1 << 0
|
||||
RFENVG = 1 << 1
|
||||
RFFDG = 1 << 2
|
||||
RFNOTEG = 1 << 3
|
||||
RFPROC = 1 << 4
|
||||
RFMEM = 1 << 5
|
||||
RFNOWAIT = 1 << 6
|
||||
RFCNAMEG = 1 << 10
|
||||
RFCENVG = 1 << 11
|
||||
RFCFDG = 1 << 12
|
||||
RFREND = 1 << 13
|
||||
RFNOMNT = 1 << 14
|
||||
)
|
||||
|
||||
// Qid.Type bits
|
||||
const (
|
||||
QTDIR = 0x80
|
||||
QTAPPEND = 0x40
|
||||
QTEXCL = 0x20
|
||||
QTMOUNT = 0x10
|
||||
QTAUTH = 0x08
|
||||
QTTMP = 0x04
|
||||
QTFILE = 0x00
|
||||
)
|
||||
|
||||
// Dir.Mode bits
|
||||
const (
|
||||
DMDIR = 0x80000000
|
||||
DMAPPEND = 0x40000000
|
||||
DMEXCL = 0x20000000
|
||||
DMMOUNT = 0x10000000
|
||||
DMAUTH = 0x08000000
|
||||
DMTMP = 0x04000000
|
||||
DMREAD = 0x4
|
||||
DMWRITE = 0x2
|
||||
DMEXEC = 0x1
|
||||
)
|
||||
|
||||
const (
|
||||
STATMAX = 65535
|
||||
ERRMAX = 128
|
||||
STATFIXLEN = 49
|
||||
)
|
||||
|
||||
// Mount and bind flags
|
||||
const (
|
||||
MREPL = 0x0000
|
||||
MBEFORE = 0x0001
|
||||
MAFTER = 0x0002
|
||||
MORDER = 0x0003
|
||||
MCREATE = 0x0004
|
||||
MCACHE = 0x0010
|
||||
MMASK = 0x0017
|
||||
)
|
||||
212
vendor/golang.org/x/sys/plan9/dir_plan9.go
generated
vendored
Normal file
212
vendor/golang.org/x/sys/plan9/dir_plan9.go
generated
vendored
Normal file
@@ -0,0 +1,212 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Plan 9 directory marshalling. See intro(5).
|
||||
|
||||
package plan9
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrShortStat = errors.New("stat buffer too short")
|
||||
ErrBadStat = errors.New("malformed stat buffer")
|
||||
ErrBadName = errors.New("bad character in file name")
|
||||
)
|
||||
|
||||
// A Qid represents a 9P server's unique identification for a file.
|
||||
type Qid struct {
|
||||
Path uint64 // the file server's unique identification for the file
|
||||
Vers uint32 // version number for given Path
|
||||
Type uint8 // the type of the file (plan9.QTDIR for example)
|
||||
}
|
||||
|
||||
// A Dir contains the metadata for a file.
|
||||
type Dir struct {
|
||||
// system-modified data
|
||||
Type uint16 // server type
|
||||
Dev uint32 // server subtype
|
||||
|
||||
// file data
|
||||
Qid Qid // unique id from server
|
||||
Mode uint32 // permissions
|
||||
Atime uint32 // last read time
|
||||
Mtime uint32 // last write time
|
||||
Length int64 // file length
|
||||
Name string // last element of path
|
||||
Uid string // owner name
|
||||
Gid string // group name
|
||||
Muid string // last modifier name
|
||||
}
|
||||
|
||||
var nullDir = Dir{
|
||||
Type: ^uint16(0),
|
||||
Dev: ^uint32(0),
|
||||
Qid: Qid{
|
||||
Path: ^uint64(0),
|
||||
Vers: ^uint32(0),
|
||||
Type: ^uint8(0),
|
||||
},
|
||||
Mode: ^uint32(0),
|
||||
Atime: ^uint32(0),
|
||||
Mtime: ^uint32(0),
|
||||
Length: ^int64(0),
|
||||
}
|
||||
|
||||
// Null assigns special "don't touch" values to members of d to
|
||||
// avoid modifying them during plan9.Wstat.
|
||||
func (d *Dir) Null() { *d = nullDir }
|
||||
|
||||
// Marshal encodes a 9P stat message corresponding to d into b
|
||||
//
|
||||
// If there isn't enough space in b for a stat message, ErrShortStat is returned.
|
||||
func (d *Dir) Marshal(b []byte) (n int, err error) {
|
||||
n = STATFIXLEN + len(d.Name) + len(d.Uid) + len(d.Gid) + len(d.Muid)
|
||||
if n > len(b) {
|
||||
return n, ErrShortStat
|
||||
}
|
||||
|
||||
for _, c := range d.Name {
|
||||
if c == '/' {
|
||||
return n, ErrBadName
|
||||
}
|
||||
}
|
||||
|
||||
b = pbit16(b, uint16(n)-2)
|
||||
b = pbit16(b, d.Type)
|
||||
b = pbit32(b, d.Dev)
|
||||
b = pbit8(b, d.Qid.Type)
|
||||
b = pbit32(b, d.Qid.Vers)
|
||||
b = pbit64(b, d.Qid.Path)
|
||||
b = pbit32(b, d.Mode)
|
||||
b = pbit32(b, d.Atime)
|
||||
b = pbit32(b, d.Mtime)
|
||||
b = pbit64(b, uint64(d.Length))
|
||||
b = pstring(b, d.Name)
|
||||
b = pstring(b, d.Uid)
|
||||
b = pstring(b, d.Gid)
|
||||
b = pstring(b, d.Muid)
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// UnmarshalDir decodes a single 9P stat message from b and returns the resulting Dir.
|
||||
//
|
||||
// If b is too small to hold a valid stat message, ErrShortStat is returned.
|
||||
//
|
||||
// If the stat message itself is invalid, ErrBadStat is returned.
|
||||
func UnmarshalDir(b []byte) (*Dir, error) {
|
||||
if len(b) < STATFIXLEN {
|
||||
return nil, ErrShortStat
|
||||
}
|
||||
size, buf := gbit16(b)
|
||||
if len(b) != int(size)+2 {
|
||||
return nil, ErrBadStat
|
||||
}
|
||||
b = buf
|
||||
|
||||
var d Dir
|
||||
d.Type, b = gbit16(b)
|
||||
d.Dev, b = gbit32(b)
|
||||
d.Qid.Type, b = gbit8(b)
|
||||
d.Qid.Vers, b = gbit32(b)
|
||||
d.Qid.Path, b = gbit64(b)
|
||||
d.Mode, b = gbit32(b)
|
||||
d.Atime, b = gbit32(b)
|
||||
d.Mtime, b = gbit32(b)
|
||||
|
||||
n, b := gbit64(b)
|
||||
d.Length = int64(n)
|
||||
|
||||
var ok bool
|
||||
if d.Name, b, ok = gstring(b); !ok {
|
||||
return nil, ErrBadStat
|
||||
}
|
||||
if d.Uid, b, ok = gstring(b); !ok {
|
||||
return nil, ErrBadStat
|
||||
}
|
||||
if d.Gid, b, ok = gstring(b); !ok {
|
||||
return nil, ErrBadStat
|
||||
}
|
||||
if d.Muid, b, ok = gstring(b); !ok {
|
||||
return nil, ErrBadStat
|
||||
}
|
||||
|
||||
return &d, nil
|
||||
}
|
||||
|
||||
// pbit8 copies the 8-bit number v to b and returns the remaining slice of b.
|
||||
func pbit8(b []byte, v uint8) []byte {
|
||||
b[0] = byte(v)
|
||||
return b[1:]
|
||||
}
|
||||
|
||||
// pbit16 copies the 16-bit number v to b in little-endian order and returns the remaining slice of b.
|
||||
func pbit16(b []byte, v uint16) []byte {
|
||||
b[0] = byte(v)
|
||||
b[1] = byte(v >> 8)
|
||||
return b[2:]
|
||||
}
|
||||
|
||||
// pbit32 copies the 32-bit number v to b in little-endian order and returns the remaining slice of b.
|
||||
func pbit32(b []byte, v uint32) []byte {
|
||||
b[0] = byte(v)
|
||||
b[1] = byte(v >> 8)
|
||||
b[2] = byte(v >> 16)
|
||||
b[3] = byte(v >> 24)
|
||||
return b[4:]
|
||||
}
|
||||
|
||||
// pbit64 copies the 64-bit number v to b in little-endian order and returns the remaining slice of b.
|
||||
func pbit64(b []byte, v uint64) []byte {
|
||||
b[0] = byte(v)
|
||||
b[1] = byte(v >> 8)
|
||||
b[2] = byte(v >> 16)
|
||||
b[3] = byte(v >> 24)
|
||||
b[4] = byte(v >> 32)
|
||||
b[5] = byte(v >> 40)
|
||||
b[6] = byte(v >> 48)
|
||||
b[7] = byte(v >> 56)
|
||||
return b[8:]
|
||||
}
|
||||
|
||||
// pstring copies the string s to b, prepending it with a 16-bit length in little-endian order, and
|
||||
// returning the remaining slice of b..
|
||||
func pstring(b []byte, s string) []byte {
|
||||
b = pbit16(b, uint16(len(s)))
|
||||
n := copy(b, s)
|
||||
return b[n:]
|
||||
}
|
||||
|
||||
// gbit8 reads an 8-bit number from b and returns it with the remaining slice of b.
|
||||
func gbit8(b []byte) (uint8, []byte) {
|
||||
return uint8(b[0]), b[1:]
|
||||
}
|
||||
|
||||
// gbit16 reads a 16-bit number in little-endian order from b and returns it with the remaining slice of b.
|
||||
func gbit16(b []byte) (uint16, []byte) {
|
||||
return uint16(b[0]) | uint16(b[1])<<8, b[2:]
|
||||
}
|
||||
|
||||
// gbit32 reads a 32-bit number in little-endian order from b and returns it with the remaining slice of b.
|
||||
func gbit32(b []byte) (uint32, []byte) {
|
||||
return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24, b[4:]
|
||||
}
|
||||
|
||||
// gbit64 reads a 64-bit number in little-endian order from b and returns it with the remaining slice of b.
|
||||
func gbit64(b []byte) (uint64, []byte) {
|
||||
lo := uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
|
||||
hi := uint32(b[4]) | uint32(b[5])<<8 | uint32(b[6])<<16 | uint32(b[7])<<24
|
||||
return uint64(lo) | uint64(hi)<<32, b[8:]
|
||||
}
|
||||
|
||||
// gstring reads a string from b, prefixed with a 16-bit length in little-endian order.
|
||||
// It returns the string with the remaining slice of b and a boolean. If the length is
|
||||
// greater than the number of bytes in b, the boolean will be false.
|
||||
func gstring(b []byte) (string, []byte, bool) {
|
||||
n, b := gbit16(b)
|
||||
if int(n) > len(b) {
|
||||
return "", b, false
|
||||
}
|
||||
return string(b[:n]), b[n:], true
|
||||
}
|
||||
31
vendor/golang.org/x/sys/plan9/env_plan9.go
generated
vendored
Normal file
31
vendor/golang.org/x/sys/plan9/env_plan9.go
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Plan 9 environment variables.
|
||||
|
||||
package plan9
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func Getenv(key string) (value string, found bool) {
|
||||
return syscall.Getenv(key)
|
||||
}
|
||||
|
||||
func Setenv(key, value string) error {
|
||||
return syscall.Setenv(key, value)
|
||||
}
|
||||
|
||||
func Clearenv() {
|
||||
syscall.Clearenv()
|
||||
}
|
||||
|
||||
func Environ() []string {
|
||||
return syscall.Environ()
|
||||
}
|
||||
|
||||
func Unsetenv(key string) error {
|
||||
return syscall.Unsetenv(key)
|
||||
}
|
||||
50
vendor/golang.org/x/sys/plan9/errors_plan9.go
generated
vendored
Normal file
50
vendor/golang.org/x/sys/plan9/errors_plan9.go
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package plan9
|
||||
|
||||
import "syscall"
|
||||
|
||||
// Constants
|
||||
const (
|
||||
// Invented values to support what package os expects.
|
||||
O_CREAT = 0x02000
|
||||
O_APPEND = 0x00400
|
||||
O_NOCTTY = 0x00000
|
||||
O_NONBLOCK = 0x00000
|
||||
O_SYNC = 0x00000
|
||||
O_ASYNC = 0x00000
|
||||
|
||||
S_IFMT = 0x1f000
|
||||
S_IFIFO = 0x1000
|
||||
S_IFCHR = 0x2000
|
||||
S_IFDIR = 0x4000
|
||||
S_IFBLK = 0x6000
|
||||
S_IFREG = 0x8000
|
||||
S_IFLNK = 0xa000
|
||||
S_IFSOCK = 0xc000
|
||||
)
|
||||
|
||||
// Errors
|
||||
var (
|
||||
EINVAL = syscall.NewError("bad arg in system call")
|
||||
ENOTDIR = syscall.NewError("not a directory")
|
||||
EISDIR = syscall.NewError("file is a directory")
|
||||
ENOENT = syscall.NewError("file does not exist")
|
||||
EEXIST = syscall.NewError("file already exists")
|
||||
EMFILE = syscall.NewError("no free file descriptors")
|
||||
EIO = syscall.NewError("i/o error")
|
||||
ENAMETOOLONG = syscall.NewError("file name too long")
|
||||
EINTR = syscall.NewError("interrupted")
|
||||
EPERM = syscall.NewError("permission denied")
|
||||
EBUSY = syscall.NewError("no free devices")
|
||||
ETIMEDOUT = syscall.NewError("connection timed out")
|
||||
EPLAN9 = syscall.NewError("not supported by plan 9")
|
||||
|
||||
// The following errors do not correspond to any
|
||||
// Plan 9 system messages. Invented to support
|
||||
// what package os and others expect.
|
||||
EACCES = syscall.NewError("access permission denied")
|
||||
EAFNOSUPPORT = syscall.NewError("address family not supported by protocol")
|
||||
)
|
||||
150
vendor/golang.org/x/sys/plan9/mkall.sh
generated
vendored
Normal file
150
vendor/golang.org/x/sys/plan9/mkall.sh
generated
vendored
Normal file
@@ -0,0 +1,150 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright 2009 The Go Authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file.
|
||||
|
||||
# The plan9 package provides access to the raw system call
|
||||
# interface of the underlying operating system. Porting Go to
|
||||
# a new architecture/operating system combination requires
|
||||
# some manual effort, though there are tools that automate
|
||||
# much of the process. The auto-generated files have names
|
||||
# beginning with z.
|
||||
#
|
||||
# This script runs or (given -n) prints suggested commands to generate z files
|
||||
# for the current system. Running those commands is not automatic.
|
||||
# This script is documentation more than anything else.
|
||||
#
|
||||
# * asm_${GOOS}_${GOARCH}.s
|
||||
#
|
||||
# This hand-written assembly file implements system call dispatch.
|
||||
# There are three entry points:
|
||||
#
|
||||
# func Syscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr);
|
||||
# func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr);
|
||||
# func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr);
|
||||
#
|
||||
# The first and second are the standard ones; they differ only in
|
||||
# how many arguments can be passed to the kernel.
|
||||
# The third is for low-level use by the ForkExec wrapper;
|
||||
# unlike the first two, it does not call into the scheduler to
|
||||
# let it know that a system call is running.
|
||||
#
|
||||
# * syscall_${GOOS}.go
|
||||
#
|
||||
# This hand-written Go file implements system calls that need
|
||||
# special handling and lists "//sys" comments giving prototypes
|
||||
# for ones that can be auto-generated. Mksyscall reads those
|
||||
# comments to generate the stubs.
|
||||
#
|
||||
# * syscall_${GOOS}_${GOARCH}.go
|
||||
#
|
||||
# Same as syscall_${GOOS}.go except that it contains code specific
|
||||
# to ${GOOS} on one particular architecture.
|
||||
#
|
||||
# * types_${GOOS}.c
|
||||
#
|
||||
# This hand-written C file includes standard C headers and then
|
||||
# creates typedef or enum names beginning with a dollar sign
|
||||
# (use of $ in variable names is a gcc extension). The hardest
|
||||
# part about preparing this file is figuring out which headers to
|
||||
# include and which symbols need to be #defined to get the
|
||||
# actual data structures that pass through to the kernel system calls.
|
||||
# Some C libraries present alternate versions for binary compatibility
|
||||
# and translate them on the way in and out of system calls, but
|
||||
# there is almost always a #define that can get the real ones.
|
||||
# See types_darwin.c and types_linux.c for examples.
|
||||
#
|
||||
# * zerror_${GOOS}_${GOARCH}.go
|
||||
#
|
||||
# This machine-generated file defines the system's error numbers,
|
||||
# error strings, and signal numbers. The generator is "mkerrors.sh".
|
||||
# Usually no arguments are needed, but mkerrors.sh will pass its
|
||||
# arguments on to godefs.
|
||||
#
|
||||
# * zsyscall_${GOOS}_${GOARCH}.go
|
||||
#
|
||||
# Generated by mksyscall.pl; see syscall_${GOOS}.go above.
|
||||
#
|
||||
# * zsysnum_${GOOS}_${GOARCH}.go
|
||||
#
|
||||
# Generated by mksysnum_${GOOS}.
|
||||
#
|
||||
# * ztypes_${GOOS}_${GOARCH}.go
|
||||
#
|
||||
# Generated by godefs; see types_${GOOS}.c above.
|
||||
|
||||
GOOSARCH="${GOOS}_${GOARCH}"
|
||||
|
||||
# defaults
|
||||
mksyscall="go run mksyscall.go"
|
||||
mkerrors="./mkerrors.sh"
|
||||
zerrors="zerrors_$GOOSARCH.go"
|
||||
mksysctl=""
|
||||
zsysctl="zsysctl_$GOOSARCH.go"
|
||||
mksysnum=
|
||||
mktypes=
|
||||
run="sh"
|
||||
|
||||
case "$1" in
|
||||
-syscalls)
|
||||
for i in zsyscall*go
|
||||
do
|
||||
sed 1q $i | sed 's;^// ;;' | sh > _$i && gofmt < _$i > $i
|
||||
rm _$i
|
||||
done
|
||||
exit 0
|
||||
;;
|
||||
-n)
|
||||
run="cat"
|
||||
shift
|
||||
esac
|
||||
|
||||
case "$#" in
|
||||
0)
|
||||
;;
|
||||
*)
|
||||
echo 'usage: mkall.sh [-n]' 1>&2
|
||||
exit 2
|
||||
esac
|
||||
|
||||
case "$GOOSARCH" in
|
||||
_* | *_ | _)
|
||||
echo 'undefined $GOOS_$GOARCH:' "$GOOSARCH" 1>&2
|
||||
exit 1
|
||||
;;
|
||||
plan9_386)
|
||||
mkerrors=
|
||||
mksyscall="go run mksyscall.go -l32 -plan9 -tags plan9,386"
|
||||
mksysnum="./mksysnum_plan9.sh /n/sources/plan9/sys/src/libc/9syscall/sys.h"
|
||||
mktypes="XXX"
|
||||
;;
|
||||
plan9_amd64)
|
||||
mkerrors=
|
||||
mksyscall="go run mksyscall.go -l32 -plan9 -tags plan9,amd64"
|
||||
mksysnum="./mksysnum_plan9.sh /n/sources/plan9/sys/src/libc/9syscall/sys.h"
|
||||
mktypes="XXX"
|
||||
;;
|
||||
plan9_arm)
|
||||
mkerrors=
|
||||
mksyscall="go run mksyscall.go -l32 -plan9 -tags plan9,arm"
|
||||
mksysnum="./mksysnum_plan9.sh /n/sources/plan9/sys/src/libc/9syscall/sys.h"
|
||||
mktypes="XXX"
|
||||
;;
|
||||
*)
|
||||
echo 'unrecognized $GOOS_$GOARCH: ' "$GOOSARCH" 1>&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
(
|
||||
if [ -n "$mkerrors" ]; then echo "$mkerrors |gofmt >$zerrors"; fi
|
||||
case "$GOOS" in
|
||||
plan9)
|
||||
syscall_goos="syscall_$GOOS.go"
|
||||
if [ -n "$mksyscall" ]; then echo "$mksyscall $syscall_goos |gofmt >zsyscall_$GOOSARCH.go"; fi
|
||||
;;
|
||||
esac
|
||||
if [ -n "$mksysctl" ]; then echo "$mksysctl |gofmt >$zsysctl"; fi
|
||||
if [ -n "$mksysnum" ]; then echo "$mksysnum |gofmt >zsysnum_$GOOSARCH.go"; fi
|
||||
if [ -n "$mktypes" ]; then echo "$mktypes types_$GOOS.go |gofmt >ztypes_$GOOSARCH.go"; fi
|
||||
) | $run
|
||||
246
vendor/golang.org/x/sys/plan9/mkerrors.sh
generated
vendored
Normal file
246
vendor/golang.org/x/sys/plan9/mkerrors.sh
generated
vendored
Normal file
@@ -0,0 +1,246 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright 2009 The Go Authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file.
|
||||
|
||||
# Generate Go code listing errors and other #defined constant
|
||||
# values (ENAMETOOLONG etc.), by asking the preprocessor
|
||||
# about the definitions.
|
||||
|
||||
unset LANG
|
||||
export LC_ALL=C
|
||||
export LC_CTYPE=C
|
||||
|
||||
CC=${CC:-gcc}
|
||||
|
||||
uname=$(uname)
|
||||
|
||||
includes='
|
||||
#include <sys/types.h>
|
||||
#include <sys/file.h>
|
||||
#include <fcntl.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <netinet/ip6.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <errno.h>
|
||||
#include <sys/signal.h>
|
||||
#include <signal.h>
|
||||
#include <sys/resource.h>
|
||||
'
|
||||
|
||||
ccflags="$@"
|
||||
|
||||
# Write go tool cgo -godefs input.
|
||||
(
|
||||
echo package plan9
|
||||
echo
|
||||
echo '/*'
|
||||
indirect="includes_$(uname)"
|
||||
echo "${!indirect} $includes"
|
||||
echo '*/'
|
||||
echo 'import "C"'
|
||||
echo
|
||||
echo 'const ('
|
||||
|
||||
# The gcc command line prints all the #defines
|
||||
# it encounters while processing the input
|
||||
echo "${!indirect} $includes" | $CC -x c - -E -dM $ccflags |
|
||||
awk '
|
||||
$1 != "#define" || $2 ~ /\(/ || $3 == "" {next}
|
||||
|
||||
$2 ~ /^E([ABCD]X|[BIS]P|[SD]I|S|FL)$/ {next} # 386 registers
|
||||
$2 ~ /^(SIGEV_|SIGSTKSZ|SIGRT(MIN|MAX))/ {next}
|
||||
$2 ~ /^(SCM_SRCRT)$/ {next}
|
||||
$2 ~ /^(MAP_FAILED)$/ {next}
|
||||
|
||||
$2 !~ /^ETH_/ &&
|
||||
$2 !~ /^EPROC_/ &&
|
||||
$2 !~ /^EQUIV_/ &&
|
||||
$2 !~ /^EXPR_/ &&
|
||||
$2 ~ /^E[A-Z0-9_]+$/ ||
|
||||
$2 ~ /^B[0-9_]+$/ ||
|
||||
$2 ~ /^V[A-Z0-9]+$/ ||
|
||||
$2 ~ /^CS[A-Z0-9]/ ||
|
||||
$2 ~ /^I(SIG|CANON|CRNL|EXTEN|MAXBEL|STRIP|UTF8)$/ ||
|
||||
$2 ~ /^IGN/ ||
|
||||
$2 ~ /^IX(ON|ANY|OFF)$/ ||
|
||||
$2 ~ /^IN(LCR|PCK)$/ ||
|
||||
$2 ~ /(^FLU?SH)|(FLU?SH$)/ ||
|
||||
$2 ~ /^C(LOCAL|READ)$/ ||
|
||||
$2 == "BRKINT" ||
|
||||
$2 == "HUPCL" ||
|
||||
$2 == "PENDIN" ||
|
||||
$2 == "TOSTOP" ||
|
||||
$2 ~ /^PAR/ ||
|
||||
$2 ~ /^SIG[^_]/ ||
|
||||
$2 ~ /^O[CNPFP][A-Z]+[^_][A-Z]+$/ ||
|
||||
$2 ~ /^IN_/ ||
|
||||
$2 ~ /^LOCK_(SH|EX|NB|UN)$/ ||
|
||||
$2 ~ /^(AF|SOCK|SO|SOL|IPPROTO|IP|IPV6|ICMP6|TCP|EVFILT|NOTE|EV|SHUT|PROT|MAP|PACKET|MSG|SCM|MCL|DT|MADV|PR)_/ ||
|
||||
$2 == "ICMPV6_FILTER" ||
|
||||
$2 == "SOMAXCONN" ||
|
||||
$2 == "NAME_MAX" ||
|
||||
$2 == "IFNAMSIZ" ||
|
||||
$2 ~ /^CTL_(MAXNAME|NET|QUERY)$/ ||
|
||||
$2 ~ /^SYSCTL_VERS/ ||
|
||||
$2 ~ /^(MS|MNT)_/ ||
|
||||
$2 ~ /^TUN(SET|GET|ATTACH|DETACH)/ ||
|
||||
$2 ~ /^(O|F|FD|NAME|S|PTRACE|PT)_/ ||
|
||||
$2 ~ /^LINUX_REBOOT_CMD_/ ||
|
||||
$2 ~ /^LINUX_REBOOT_MAGIC[12]$/ ||
|
||||
$2 !~ "NLA_TYPE_MASK" &&
|
||||
$2 ~ /^(NETLINK|NLM|NLMSG|NLA|IFA|IFAN|RT|RTCF|RTN|RTPROT|RTNH|ARPHRD|ETH_P)_/ ||
|
||||
$2 ~ /^SIOC/ ||
|
||||
$2 ~ /^TIOC/ ||
|
||||
$2 !~ "RTF_BITS" &&
|
||||
$2 ~ /^(IFF|IFT|NET_RT|RTM|RTF|RTV|RTA|RTAX)_/ ||
|
||||
$2 ~ /^BIOC/ ||
|
||||
$2 ~ /^RUSAGE_(SELF|CHILDREN|THREAD)/ ||
|
||||
$2 ~ /^RLIMIT_(AS|CORE|CPU|DATA|FSIZE|NOFILE|STACK)|RLIM_INFINITY/ ||
|
||||
$2 ~ /^PRIO_(PROCESS|PGRP|USER)/ ||
|
||||
$2 ~ /^CLONE_[A-Z_]+/ ||
|
||||
$2 !~ /^(BPF_TIMEVAL)$/ &&
|
||||
$2 ~ /^(BPF|DLT)_/ ||
|
||||
$2 !~ "WMESGLEN" &&
|
||||
$2 ~ /^W[A-Z0-9]+$/ {printf("\t%s = C.%s\n", $2, $2)}
|
||||
$2 ~ /^__WCOREFLAG$/ {next}
|
||||
$2 ~ /^__W[A-Z0-9]+$/ {printf("\t%s = C.%s\n", substr($2,3), $2)}
|
||||
|
||||
{next}
|
||||
' | sort
|
||||
|
||||
echo ')'
|
||||
) >_const.go
|
||||
|
||||
# Pull out the error names for later.
|
||||
errors=$(
|
||||
echo '#include <errno.h>' | $CC -x c - -E -dM $ccflags |
|
||||
awk '$1=="#define" && $2 ~ /^E[A-Z0-9_]+$/ { print $2 }' |
|
||||
sort
|
||||
)
|
||||
|
||||
# Pull out the signal names for later.
|
||||
signals=$(
|
||||
echo '#include <signal.h>' | $CC -x c - -E -dM $ccflags |
|
||||
awk '$1=="#define" && $2 ~ /^SIG[A-Z0-9]+$/ { print $2 }' |
|
||||
grep -v 'SIGSTKSIZE\|SIGSTKSZ\|SIGRT' |
|
||||
sort
|
||||
)
|
||||
|
||||
# Again, writing regexps to a file.
|
||||
echo '#include <errno.h>' | $CC -x c - -E -dM $ccflags |
|
||||
awk '$1=="#define" && $2 ~ /^E[A-Z0-9_]+$/ { print "^\t" $2 "[ \t]*=" }' |
|
||||
sort >_error.grep
|
||||
echo '#include <signal.h>' | $CC -x c - -E -dM $ccflags |
|
||||
awk '$1=="#define" && $2 ~ /^SIG[A-Z0-9]+$/ { print "^\t" $2 "[ \t]*=" }' |
|
||||
grep -v 'SIGSTKSIZE\|SIGSTKSZ\|SIGRT' |
|
||||
sort >_signal.grep
|
||||
|
||||
echo '// mkerrors.sh' "$@"
|
||||
echo '// Code generated by the command above; DO NOT EDIT.'
|
||||
echo
|
||||
go tool cgo -godefs -- "$@" _const.go >_error.out
|
||||
cat _error.out | grep -vf _error.grep | grep -vf _signal.grep
|
||||
echo
|
||||
echo '// Errors'
|
||||
echo 'const ('
|
||||
cat _error.out | grep -f _error.grep | sed 's/=\(.*\)/= Errno(\1)/'
|
||||
echo ')'
|
||||
|
||||
echo
|
||||
echo '// Signals'
|
||||
echo 'const ('
|
||||
cat _error.out | grep -f _signal.grep | sed 's/=\(.*\)/= Signal(\1)/'
|
||||
echo ')'
|
||||
|
||||
# Run C program to print error and syscall strings.
|
||||
(
|
||||
echo -E "
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
|
||||
#define nelem(x) (sizeof(x)/sizeof((x)[0]))
|
||||
|
||||
enum { A = 'A', Z = 'Z', a = 'a', z = 'z' }; // avoid need for single quotes below
|
||||
|
||||
int errors[] = {
|
||||
"
|
||||
for i in $errors
|
||||
do
|
||||
echo -E ' '$i,
|
||||
done
|
||||
|
||||
echo -E "
|
||||
};
|
||||
|
||||
int signals[] = {
|
||||
"
|
||||
for i in $signals
|
||||
do
|
||||
echo -E ' '$i,
|
||||
done
|
||||
|
||||
# Use -E because on some systems bash builtin interprets \n itself.
|
||||
echo -E '
|
||||
};
|
||||
|
||||
static int
|
||||
intcmp(const void *a, const void *b)
|
||||
{
|
||||
return *(int*)a - *(int*)b;
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
int i, j, e;
|
||||
char buf[1024], *p;
|
||||
|
||||
printf("\n\n// Error table\n");
|
||||
printf("var errors = [...]string {\n");
|
||||
qsort(errors, nelem(errors), sizeof errors[0], intcmp);
|
||||
for(i=0; i<nelem(errors); i++) {
|
||||
e = errors[i];
|
||||
if(i > 0 && errors[i-1] == e)
|
||||
continue;
|
||||
strcpy(buf, strerror(e));
|
||||
// lowercase first letter: Bad -> bad, but STREAM -> STREAM.
|
||||
if(A <= buf[0] && buf[0] <= Z && a <= buf[1] && buf[1] <= z)
|
||||
buf[0] += a - A;
|
||||
printf("\t%d: \"%s\",\n", e, buf);
|
||||
}
|
||||
printf("}\n\n");
|
||||
|
||||
printf("\n\n// Signal table\n");
|
||||
printf("var signals = [...]string {\n");
|
||||
qsort(signals, nelem(signals), sizeof signals[0], intcmp);
|
||||
for(i=0; i<nelem(signals); i++) {
|
||||
e = signals[i];
|
||||
if(i > 0 && signals[i-1] == e)
|
||||
continue;
|
||||
strcpy(buf, strsignal(e));
|
||||
// lowercase first letter: Bad -> bad, but STREAM -> STREAM.
|
||||
if(A <= buf[0] && buf[0] <= Z && a <= buf[1] && buf[1] <= z)
|
||||
buf[0] += a - A;
|
||||
// cut trailing : number.
|
||||
p = strrchr(buf, ":"[0]);
|
||||
if(p)
|
||||
*p = '\0';
|
||||
printf("\t%d: \"%s\",\n", e, buf);
|
||||
}
|
||||
printf("}\n\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
'
|
||||
) >_errors.c
|
||||
|
||||
$CC $ccflags -o _errors _errors.c && $GORUN ./_errors && rm -f _errors.c _errors _const.go _error.grep _signal.grep _error.out
|
||||
23
vendor/golang.org/x/sys/plan9/mksysnum_plan9.sh
generated
vendored
Normal file
23
vendor/golang.org/x/sys/plan9/mksysnum_plan9.sh
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
#!/bin/sh
|
||||
# Copyright 2009 The Go Authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file.
|
||||
|
||||
COMMAND="mksysnum_plan9.sh $@"
|
||||
|
||||
cat <<EOF
|
||||
// $COMMAND
|
||||
// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
|
||||
|
||||
package plan9
|
||||
|
||||
const(
|
||||
EOF
|
||||
|
||||
SP='[ ]' # space or tab
|
||||
sed "s/^#define${SP}\\([A-Z0-9_][A-Z0-9_]*\\)${SP}${SP}*\\([0-9][0-9]*\\)/SYS_\\1=\\2/g" \
|
||||
< $1 | grep -v SYS__
|
||||
|
||||
cat <<EOF
|
||||
)
|
||||
EOF
|
||||
19
vendor/golang.org/x/sys/plan9/pwd_plan9.go
generated
vendored
Normal file
19
vendor/golang.org/x/sys/plan9/pwd_plan9.go
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package plan9
|
||||
|
||||
import "syscall"
|
||||
|
||||
func fixwd() {
|
||||
syscall.Fixwd()
|
||||
}
|
||||
|
||||
func Getwd() (wd string, err error) {
|
||||
return syscall.Getwd()
|
||||
}
|
||||
|
||||
func Chdir(path string) error {
|
||||
return syscall.Chdir(path)
|
||||
}
|
||||
30
vendor/golang.org/x/sys/plan9/race.go
generated
vendored
Normal file
30
vendor/golang.org/x/sys/plan9/race.go
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build plan9 && race
|
||||
|
||||
package plan9
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const raceenabled = true
|
||||
|
||||
func raceAcquire(addr unsafe.Pointer) {
|
||||
runtime.RaceAcquire(addr)
|
||||
}
|
||||
|
||||
func raceReleaseMerge(addr unsafe.Pointer) {
|
||||
runtime.RaceReleaseMerge(addr)
|
||||
}
|
||||
|
||||
func raceReadRange(addr unsafe.Pointer, len int) {
|
||||
runtime.RaceReadRange(addr, len)
|
||||
}
|
||||
|
||||
func raceWriteRange(addr unsafe.Pointer, len int) {
|
||||
runtime.RaceWriteRange(addr, len)
|
||||
}
|
||||
25
vendor/golang.org/x/sys/plan9/race0.go
generated
vendored
Normal file
25
vendor/golang.org/x/sys/plan9/race0.go
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build plan9 && !race
|
||||
|
||||
package plan9
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const raceenabled = false
|
||||
|
||||
func raceAcquire(addr unsafe.Pointer) {
|
||||
}
|
||||
|
||||
func raceReleaseMerge(addr unsafe.Pointer) {
|
||||
}
|
||||
|
||||
func raceReadRange(addr unsafe.Pointer, len int) {
|
||||
}
|
||||
|
||||
func raceWriteRange(addr unsafe.Pointer, len int) {
|
||||
}
|
||||
22
vendor/golang.org/x/sys/plan9/str.go
generated
vendored
Normal file
22
vendor/golang.org/x/sys/plan9/str.go
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build plan9
|
||||
|
||||
package plan9
|
||||
|
||||
func itoa(val int) string { // do it here rather than with fmt to avoid dependency
|
||||
if val < 0 {
|
||||
return "-" + itoa(-val)
|
||||
}
|
||||
var buf [32]byte // big enough for int64
|
||||
i := len(buf) - 1
|
||||
for val >= 10 {
|
||||
buf[i] = byte(val%10 + '0')
|
||||
i--
|
||||
val /= 10
|
||||
}
|
||||
buf[i] = byte(val + '0')
|
||||
return string(buf[i:])
|
||||
}
|
||||
109
vendor/golang.org/x/sys/plan9/syscall.go
generated
vendored
Normal file
109
vendor/golang.org/x/sys/plan9/syscall.go
generated
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build plan9
|
||||
|
||||
// Package plan9 contains an interface to the low-level operating system
|
||||
// primitives. OS details vary depending on the underlying system, and
|
||||
// by default, godoc will display the OS-specific documentation for the current
|
||||
// system. If you want godoc to display documentation for another
|
||||
// system, set $GOOS and $GOARCH to the desired system. For example, if
|
||||
// you want to view documentation for freebsd/arm on linux/amd64, set $GOOS
|
||||
// to freebsd and $GOARCH to arm.
|
||||
//
|
||||
// The primary use of this package is inside other packages that provide a more
|
||||
// portable interface to the system, such as "os", "time" and "net". Use
|
||||
// those packages rather than this one if you can.
|
||||
//
|
||||
// For details of the functions and data types in this package consult
|
||||
// the manuals for the appropriate operating system.
|
||||
//
|
||||
// These calls return err == nil to indicate success; otherwise
|
||||
// err represents an operating system error describing the failure and
|
||||
// holds a value of type syscall.ErrorString.
|
||||
package plan9 // import "golang.org/x/sys/plan9"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// ByteSliceFromString returns a NUL-terminated slice of bytes
|
||||
// containing the text of s. If s contains a NUL byte at any
|
||||
// location, it returns (nil, EINVAL).
|
||||
func ByteSliceFromString(s string) ([]byte, error) {
|
||||
if strings.IndexByte(s, 0) != -1 {
|
||||
return nil, EINVAL
|
||||
}
|
||||
a := make([]byte, len(s)+1)
|
||||
copy(a, s)
|
||||
return a, nil
|
||||
}
|
||||
|
||||
// BytePtrFromString returns a pointer to a NUL-terminated array of
|
||||
// bytes containing the text of s. If s contains a NUL byte at any
|
||||
// location, it returns (nil, EINVAL).
|
||||
func BytePtrFromString(s string) (*byte, error) {
|
||||
a, err := ByteSliceFromString(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &a[0], nil
|
||||
}
|
||||
|
||||
// ByteSliceToString returns a string form of the text represented by the slice s, with a terminating NUL and any
|
||||
// bytes after the NUL removed.
|
||||
func ByteSliceToString(s []byte) string {
|
||||
if i := bytes.IndexByte(s, 0); i != -1 {
|
||||
s = s[:i]
|
||||
}
|
||||
return string(s)
|
||||
}
|
||||
|
||||
// BytePtrToString takes a pointer to a sequence of text and returns the corresponding string.
|
||||
// If the pointer is nil, it returns the empty string. It assumes that the text sequence is terminated
|
||||
// at a zero byte; if the zero byte is not present, the program may crash.
|
||||
func BytePtrToString(p *byte) string {
|
||||
if p == nil {
|
||||
return ""
|
||||
}
|
||||
if *p == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Find NUL terminator.
|
||||
n := 0
|
||||
for ptr := unsafe.Pointer(p); *(*byte)(ptr) != 0; n++ {
|
||||
ptr = unsafe.Pointer(uintptr(ptr) + 1)
|
||||
}
|
||||
|
||||
return string(unsafe.Slice(p, n))
|
||||
}
|
||||
|
||||
// Single-word zero for use when we need a valid pointer to 0 bytes.
|
||||
// See mksyscall.pl.
|
||||
var _zero uintptr
|
||||
|
||||
func (ts *Timespec) Unix() (sec int64, nsec int64) {
|
||||
return int64(ts.Sec), int64(ts.Nsec)
|
||||
}
|
||||
|
||||
func (tv *Timeval) Unix() (sec int64, nsec int64) {
|
||||
return int64(tv.Sec), int64(tv.Usec) * 1000
|
||||
}
|
||||
|
||||
func (ts *Timespec) Nano() int64 {
|
||||
return int64(ts.Sec)*1e9 + int64(ts.Nsec)
|
||||
}
|
||||
|
||||
func (tv *Timeval) Nano() int64 {
|
||||
return int64(tv.Sec)*1e9 + int64(tv.Usec)*1000
|
||||
}
|
||||
|
||||
// use is a no-op, but the compiler cannot see that it is.
|
||||
// Calling use(p) ensures that p is kept live until that point.
|
||||
//
|
||||
//go:noescape
|
||||
func use(p unsafe.Pointer)
|
||||
355
vendor/golang.org/x/sys/plan9/syscall_plan9.go
generated
vendored
Normal file
355
vendor/golang.org/x/sys/plan9/syscall_plan9.go
generated
vendored
Normal file
@@ -0,0 +1,355 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Plan 9 system calls.
|
||||
// This file is compiled as ordinary Go code,
|
||||
// but it is also input to mksyscall,
|
||||
// which parses the //sys lines and generates system call stubs.
|
||||
// Note that sometimes we use a lowercase //sys name and
|
||||
// wrap it in our own nicer implementation.
|
||||
|
||||
package plan9
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// A Note is a string describing a process note.
|
||||
// It implements the os.Signal interface.
|
||||
type Note = syscall.Note
|
||||
|
||||
var (
|
||||
Stdin = 0
|
||||
Stdout = 1
|
||||
Stderr = 2
|
||||
)
|
||||
|
||||
// For testing: clients can set this flag to force
|
||||
// creation of IPv6 sockets to return EAFNOSUPPORT.
|
||||
var SocketDisableIPv6 bool
|
||||
|
||||
func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.ErrorString)
|
||||
func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err syscall.ErrorString)
|
||||
func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr)
|
||||
func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr)
|
||||
|
||||
func atoi(b []byte) (n uint) {
|
||||
n = 0
|
||||
for i := 0; i < len(b); i++ {
|
||||
n = n*10 + uint(b[i]-'0')
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func cstring(s []byte) string {
|
||||
i := bytes.IndexByte(s, 0)
|
||||
if i == -1 {
|
||||
i = len(s)
|
||||
}
|
||||
return string(s[:i])
|
||||
}
|
||||
|
||||
func errstr() string {
|
||||
var buf [ERRMAX]byte
|
||||
|
||||
RawSyscall(SYS_ERRSTR, uintptr(unsafe.Pointer(&buf[0])), uintptr(len(buf)), 0)
|
||||
|
||||
buf[len(buf)-1] = 0
|
||||
return cstring(buf[:])
|
||||
}
|
||||
|
||||
// Implemented in assembly to import from runtime.
|
||||
func exit(code int)
|
||||
|
||||
func Exit(code int) { exit(code) }
|
||||
|
||||
func readnum(path string) (uint, error) {
|
||||
var b [12]byte
|
||||
|
||||
fd, e := Open(path, O_RDONLY)
|
||||
if e != nil {
|
||||
return 0, e
|
||||
}
|
||||
defer Close(fd)
|
||||
|
||||
n, e := Pread(fd, b[:], 0)
|
||||
|
||||
if e != nil {
|
||||
return 0, e
|
||||
}
|
||||
|
||||
m := 0
|
||||
for ; m < n && b[m] == ' '; m++ {
|
||||
}
|
||||
|
||||
return atoi(b[m : n-1]), nil
|
||||
}
|
||||
|
||||
func Getpid() (pid int) {
|
||||
n, _ := readnum("#c/pid")
|
||||
return int(n)
|
||||
}
|
||||
|
||||
func Getppid() (ppid int) {
|
||||
n, _ := readnum("#c/ppid")
|
||||
return int(n)
|
||||
}
|
||||
|
||||
func Read(fd int, p []byte) (n int, err error) {
|
||||
return Pread(fd, p, -1)
|
||||
}
|
||||
|
||||
func Write(fd int, p []byte) (n int, err error) {
|
||||
return Pwrite(fd, p, -1)
|
||||
}
|
||||
|
||||
var ioSync int64
|
||||
|
||||
//sys fd2path(fd int, buf []byte) (err error)
|
||||
|
||||
func Fd2path(fd int) (path string, err error) {
|
||||
var buf [512]byte
|
||||
|
||||
e := fd2path(fd, buf[:])
|
||||
if e != nil {
|
||||
return "", e
|
||||
}
|
||||
return cstring(buf[:]), nil
|
||||
}
|
||||
|
||||
//sys pipe(p *[2]int32) (err error)
|
||||
|
||||
func Pipe(p []int) (err error) {
|
||||
if len(p) != 2 {
|
||||
return syscall.ErrorString("bad arg in system call")
|
||||
}
|
||||
var pp [2]int32
|
||||
err = pipe(&pp)
|
||||
if err == nil {
|
||||
p[0] = int(pp[0])
|
||||
p[1] = int(pp[1])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Underlying system call writes to newoffset via pointer.
|
||||
// Implemented in assembly to avoid allocation.
|
||||
func seek(placeholder uintptr, fd int, offset int64, whence int) (newoffset int64, err string)
|
||||
|
||||
func Seek(fd int, offset int64, whence int) (newoffset int64, err error) {
|
||||
newoffset, e := seek(0, fd, offset, whence)
|
||||
|
||||
if newoffset == -1 {
|
||||
err = syscall.ErrorString(e)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func Mkdir(path string, mode uint32) (err error) {
|
||||
fd, err := Create(path, O_RDONLY, DMDIR|mode)
|
||||
|
||||
if fd != -1 {
|
||||
Close(fd)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
type Waitmsg struct {
|
||||
Pid int
|
||||
Time [3]uint32
|
||||
Msg string
|
||||
}
|
||||
|
||||
func (w Waitmsg) Exited() bool { return true }
|
||||
func (w Waitmsg) Signaled() bool { return false }
|
||||
|
||||
func (w Waitmsg) ExitStatus() int {
|
||||
if len(w.Msg) == 0 {
|
||||
// a normal exit returns no message
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
//sys await(s []byte) (n int, err error)
|
||||
|
||||
func Await(w *Waitmsg) (err error) {
|
||||
var buf [512]byte
|
||||
var f [5][]byte
|
||||
|
||||
n, err := await(buf[:])
|
||||
|
||||
if err != nil || w == nil {
|
||||
return
|
||||
}
|
||||
|
||||
nf := 0
|
||||
p := 0
|
||||
for i := 0; i < n && nf < len(f)-1; i++ {
|
||||
if buf[i] == ' ' {
|
||||
f[nf] = buf[p:i]
|
||||
p = i + 1
|
||||
nf++
|
||||
}
|
||||
}
|
||||
f[nf] = buf[p:]
|
||||
nf++
|
||||
|
||||
if nf != len(f) {
|
||||
return syscall.ErrorString("invalid wait message")
|
||||
}
|
||||
w.Pid = int(atoi(f[0]))
|
||||
w.Time[0] = uint32(atoi(f[1]))
|
||||
w.Time[1] = uint32(atoi(f[2]))
|
||||
w.Time[2] = uint32(atoi(f[3]))
|
||||
w.Msg = cstring(f[4])
|
||||
if w.Msg == "''" {
|
||||
// await() returns '' for no error
|
||||
w.Msg = ""
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func Unmount(name, old string) (err error) {
|
||||
fixwd()
|
||||
oldp, err := BytePtrFromString(old)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
oldptr := uintptr(unsafe.Pointer(oldp))
|
||||
|
||||
var r0 uintptr
|
||||
var e syscall.ErrorString
|
||||
|
||||
// bind(2) man page: If name is zero, everything bound or mounted upon old is unbound or unmounted.
|
||||
if name == "" {
|
||||
r0, _, e = Syscall(SYS_UNMOUNT, _zero, oldptr, 0)
|
||||
} else {
|
||||
namep, err := BytePtrFromString(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r0, _, e = Syscall(SYS_UNMOUNT, uintptr(unsafe.Pointer(namep)), oldptr, 0)
|
||||
}
|
||||
|
||||
if int32(r0) == -1 {
|
||||
err = e
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func Fchdir(fd int) (err error) {
|
||||
path, err := Fd2path(fd)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return Chdir(path)
|
||||
}
|
||||
|
||||
type Timespec struct {
|
||||
Sec int32
|
||||
Nsec int32
|
||||
}
|
||||
|
||||
type Timeval struct {
|
||||
Sec int32
|
||||
Usec int32
|
||||
}
|
||||
|
||||
func NsecToTimeval(nsec int64) (tv Timeval) {
|
||||
nsec += 999 // round up to microsecond
|
||||
tv.Usec = int32(nsec % 1e9 / 1e3)
|
||||
tv.Sec = int32(nsec / 1e9)
|
||||
return
|
||||
}
|
||||
|
||||
func nsec() int64 {
|
||||
var scratch int64
|
||||
|
||||
r0, _, _ := Syscall(SYS_NSEC, uintptr(unsafe.Pointer(&scratch)), 0, 0)
|
||||
// TODO(aram): remove hack after I fix _nsec in the pc64 kernel.
|
||||
if r0 == 0 {
|
||||
return scratch
|
||||
}
|
||||
return int64(r0)
|
||||
}
|
||||
|
||||
func Gettimeofday(tv *Timeval) error {
|
||||
nsec := nsec()
|
||||
*tv = NsecToTimeval(nsec)
|
||||
return nil
|
||||
}
|
||||
|
||||
func Getpagesize() int { return 0x1000 }
|
||||
|
||||
func Getegid() (egid int) { return -1 }
|
||||
func Geteuid() (euid int) { return -1 }
|
||||
func Getgid() (gid int) { return -1 }
|
||||
func Getuid() (uid int) { return -1 }
|
||||
|
||||
func Getgroups() (gids []int, err error) {
|
||||
return make([]int, 0), nil
|
||||
}
|
||||
|
||||
//sys open(path string, mode int) (fd int, err error)
|
||||
|
||||
func Open(path string, mode int) (fd int, err error) {
|
||||
fixwd()
|
||||
return open(path, mode)
|
||||
}
|
||||
|
||||
//sys create(path string, mode int, perm uint32) (fd int, err error)
|
||||
|
||||
func Create(path string, mode int, perm uint32) (fd int, err error) {
|
||||
fixwd()
|
||||
return create(path, mode, perm)
|
||||
}
|
||||
|
||||
//sys remove(path string) (err error)
|
||||
|
||||
func Remove(path string) error {
|
||||
fixwd()
|
||||
return remove(path)
|
||||
}
|
||||
|
||||
//sys stat(path string, edir []byte) (n int, err error)
|
||||
|
||||
func Stat(path string, edir []byte) (n int, err error) {
|
||||
fixwd()
|
||||
return stat(path, edir)
|
||||
}
|
||||
|
||||
//sys bind(name string, old string, flag int) (err error)
|
||||
|
||||
func Bind(name string, old string, flag int) (err error) {
|
||||
fixwd()
|
||||
return bind(name, old, flag)
|
||||
}
|
||||
|
||||
//sys mount(fd int, afd int, old string, flag int, aname string) (err error)
|
||||
|
||||
func Mount(fd int, afd int, old string, flag int, aname string) (err error) {
|
||||
fixwd()
|
||||
return mount(fd, afd, old, flag, aname)
|
||||
}
|
||||
|
||||
//sys wstat(path string, edir []byte) (err error)
|
||||
|
||||
func Wstat(path string, edir []byte) (err error) {
|
||||
fixwd()
|
||||
return wstat(path, edir)
|
||||
}
|
||||
|
||||
//sys chdir(path string) (err error)
|
||||
//sys Dup(oldfd int, newfd int) (fd int, err error)
|
||||
//sys Pread(fd int, p []byte, offset int64) (n int, err error)
|
||||
//sys Pwrite(fd int, p []byte, offset int64) (n int, err error)
|
||||
//sys Close(fd int) (err error)
|
||||
//sys Fstat(fd int, edir []byte) (n int, err error)
|
||||
//sys Fwstat(fd int, edir []byte) (err error)
|
||||
284
vendor/golang.org/x/sys/plan9/zsyscall_plan9_386.go
generated
vendored
Normal file
284
vendor/golang.org/x/sys/plan9/zsyscall_plan9_386.go
generated
vendored
Normal file
@@ -0,0 +1,284 @@
|
||||
// go run mksyscall.go -l32 -plan9 -tags plan9,386 syscall_plan9.go
|
||||
// Code generated by the command above; see README.md. DO NOT EDIT.
|
||||
|
||||
//go:build plan9 && 386
|
||||
|
||||
package plan9
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func fd2path(fd int, buf []byte) (err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(buf) > 0 {
|
||||
_p0 = unsafe.Pointer(&buf[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_FD2PATH, uintptr(fd), uintptr(_p0), uintptr(len(buf)))
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func pipe(p *[2]int32) (err error) {
|
||||
r0, _, e1 := Syscall(SYS_PIPE, uintptr(unsafe.Pointer(p)), 0, 0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func await(s []byte) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(s) > 0 {
|
||||
_p0 = unsafe.Pointer(&s[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_AWAIT, uintptr(_p0), uintptr(len(s)), 0)
|
||||
n = int(r0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func open(path string, mode int) (fd int, err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
|
||||
fd = int(r0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func create(path string, mode int, perm uint32) (fd int, err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_CREATE, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm))
|
||||
fd = int(r0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func remove(path string) (err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_REMOVE, uintptr(unsafe.Pointer(_p0)), 0, 0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func stat(path string, edir []byte) (n int, err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var _p1 unsafe.Pointer
|
||||
if len(edir) > 0 {
|
||||
_p1 = unsafe.Pointer(&edir[0])
|
||||
} else {
|
||||
_p1 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(edir)))
|
||||
n = int(r0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func bind(name string, old string, flag int) (err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = BytePtrFromString(name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var _p1 *byte
|
||||
_p1, err = BytePtrFromString(old)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_BIND, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(flag))
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func mount(fd int, afd int, old string, flag int, aname string) (err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = BytePtrFromString(old)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var _p1 *byte
|
||||
_p1, err = BytePtrFromString(aname)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r0, _, e1 := Syscall6(SYS_MOUNT, uintptr(fd), uintptr(afd), uintptr(unsafe.Pointer(_p0)), uintptr(flag), uintptr(unsafe.Pointer(_p1)), 0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func wstat(path string, edir []byte) (err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var _p1 unsafe.Pointer
|
||||
if len(edir) > 0 {
|
||||
_p1 = unsafe.Pointer(&edir[0])
|
||||
} else {
|
||||
_p1 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_WSTAT, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(edir)))
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func chdir(path string) (err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Dup(oldfd int, newfd int) (fd int, err error) {
|
||||
r0, _, e1 := Syscall(SYS_DUP, uintptr(oldfd), uintptr(newfd), 0)
|
||||
fd = int(r0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Pread(fd int, p []byte, offset int64) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(p) > 0 {
|
||||
_p0 = unsafe.Pointer(&p[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall6(SYS_PREAD, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0)
|
||||
n = int(r0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Pwrite(fd int, p []byte, offset int64) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(p) > 0 {
|
||||
_p0 = unsafe.Pointer(&p[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall6(SYS_PWRITE, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0)
|
||||
n = int(r0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Close(fd int) (err error) {
|
||||
r0, _, e1 := Syscall(SYS_CLOSE, uintptr(fd), 0, 0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Fstat(fd int, edir []byte) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(edir) > 0 {
|
||||
_p0 = unsafe.Pointer(&edir[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_FSTAT, uintptr(fd), uintptr(_p0), uintptr(len(edir)))
|
||||
n = int(r0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Fwstat(fd int, edir []byte) (err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(edir) > 0 {
|
||||
_p0 = unsafe.Pointer(&edir[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_FWSTAT, uintptr(fd), uintptr(_p0), uintptr(len(edir)))
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
284
vendor/golang.org/x/sys/plan9/zsyscall_plan9_amd64.go
generated
vendored
Normal file
284
vendor/golang.org/x/sys/plan9/zsyscall_plan9_amd64.go
generated
vendored
Normal file
@@ -0,0 +1,284 @@
|
||||
// go run mksyscall.go -l32 -plan9 -tags plan9,amd64 syscall_plan9.go
|
||||
// Code generated by the command above; see README.md. DO NOT EDIT.
|
||||
|
||||
//go:build plan9 && amd64
|
||||
|
||||
package plan9
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func fd2path(fd int, buf []byte) (err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(buf) > 0 {
|
||||
_p0 = unsafe.Pointer(&buf[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_FD2PATH, uintptr(fd), uintptr(_p0), uintptr(len(buf)))
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func pipe(p *[2]int32) (err error) {
|
||||
r0, _, e1 := Syscall(SYS_PIPE, uintptr(unsafe.Pointer(p)), 0, 0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func await(s []byte) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(s) > 0 {
|
||||
_p0 = unsafe.Pointer(&s[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_AWAIT, uintptr(_p0), uintptr(len(s)), 0)
|
||||
n = int(r0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func open(path string, mode int) (fd int, err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
|
||||
fd = int(r0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func create(path string, mode int, perm uint32) (fd int, err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_CREATE, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm))
|
||||
fd = int(r0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func remove(path string) (err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_REMOVE, uintptr(unsafe.Pointer(_p0)), 0, 0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func stat(path string, edir []byte) (n int, err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var _p1 unsafe.Pointer
|
||||
if len(edir) > 0 {
|
||||
_p1 = unsafe.Pointer(&edir[0])
|
||||
} else {
|
||||
_p1 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(edir)))
|
||||
n = int(r0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func bind(name string, old string, flag int) (err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = BytePtrFromString(name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var _p1 *byte
|
||||
_p1, err = BytePtrFromString(old)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_BIND, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(flag))
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func mount(fd int, afd int, old string, flag int, aname string) (err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = BytePtrFromString(old)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var _p1 *byte
|
||||
_p1, err = BytePtrFromString(aname)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r0, _, e1 := Syscall6(SYS_MOUNT, uintptr(fd), uintptr(afd), uintptr(unsafe.Pointer(_p0)), uintptr(flag), uintptr(unsafe.Pointer(_p1)), 0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func wstat(path string, edir []byte) (err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var _p1 unsafe.Pointer
|
||||
if len(edir) > 0 {
|
||||
_p1 = unsafe.Pointer(&edir[0])
|
||||
} else {
|
||||
_p1 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_WSTAT, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(edir)))
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func chdir(path string) (err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Dup(oldfd int, newfd int) (fd int, err error) {
|
||||
r0, _, e1 := Syscall(SYS_DUP, uintptr(oldfd), uintptr(newfd), 0)
|
||||
fd = int(r0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Pread(fd int, p []byte, offset int64) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(p) > 0 {
|
||||
_p0 = unsafe.Pointer(&p[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall6(SYS_PREAD, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0)
|
||||
n = int(r0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Pwrite(fd int, p []byte, offset int64) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(p) > 0 {
|
||||
_p0 = unsafe.Pointer(&p[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall6(SYS_PWRITE, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0)
|
||||
n = int(r0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Close(fd int) (err error) {
|
||||
r0, _, e1 := Syscall(SYS_CLOSE, uintptr(fd), 0, 0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Fstat(fd int, edir []byte) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(edir) > 0 {
|
||||
_p0 = unsafe.Pointer(&edir[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_FSTAT, uintptr(fd), uintptr(_p0), uintptr(len(edir)))
|
||||
n = int(r0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Fwstat(fd int, edir []byte) (err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(edir) > 0 {
|
||||
_p0 = unsafe.Pointer(&edir[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_FWSTAT, uintptr(fd), uintptr(_p0), uintptr(len(edir)))
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
284
vendor/golang.org/x/sys/plan9/zsyscall_plan9_arm.go
generated
vendored
Normal file
284
vendor/golang.org/x/sys/plan9/zsyscall_plan9_arm.go
generated
vendored
Normal file
@@ -0,0 +1,284 @@
|
||||
// go run mksyscall.go -l32 -plan9 -tags plan9,arm syscall_plan9.go
|
||||
// Code generated by the command above; see README.md. DO NOT EDIT.
|
||||
|
||||
//go:build plan9 && arm
|
||||
|
||||
package plan9
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func fd2path(fd int, buf []byte) (err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(buf) > 0 {
|
||||
_p0 = unsafe.Pointer(&buf[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_FD2PATH, uintptr(fd), uintptr(_p0), uintptr(len(buf)))
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func pipe(p *[2]int32) (err error) {
|
||||
r0, _, e1 := Syscall(SYS_PIPE, uintptr(unsafe.Pointer(p)), 0, 0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func await(s []byte) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(s) > 0 {
|
||||
_p0 = unsafe.Pointer(&s[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_AWAIT, uintptr(_p0), uintptr(len(s)), 0)
|
||||
n = int(r0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func open(path string, mode int) (fd int, err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_OPEN, uintptr(unsafe.Pointer(_p0)), uintptr(mode), 0)
|
||||
fd = int(r0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func create(path string, mode int, perm uint32) (fd int, err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_CREATE, uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(perm))
|
||||
fd = int(r0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func remove(path string) (err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_REMOVE, uintptr(unsafe.Pointer(_p0)), 0, 0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func stat(path string, edir []byte) (n int, err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var _p1 unsafe.Pointer
|
||||
if len(edir) > 0 {
|
||||
_p1 = unsafe.Pointer(&edir[0])
|
||||
} else {
|
||||
_p1 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_STAT, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(edir)))
|
||||
n = int(r0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func bind(name string, old string, flag int) (err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = BytePtrFromString(name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var _p1 *byte
|
||||
_p1, err = BytePtrFromString(old)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_BIND, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(flag))
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func mount(fd int, afd int, old string, flag int, aname string) (err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = BytePtrFromString(old)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var _p1 *byte
|
||||
_p1, err = BytePtrFromString(aname)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r0, _, e1 := Syscall6(SYS_MOUNT, uintptr(fd), uintptr(afd), uintptr(unsafe.Pointer(_p0)), uintptr(flag), uintptr(unsafe.Pointer(_p1)), 0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func wstat(path string, edir []byte) (err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var _p1 unsafe.Pointer
|
||||
if len(edir) > 0 {
|
||||
_p1 = unsafe.Pointer(&edir[0])
|
||||
} else {
|
||||
_p1 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_WSTAT, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(edir)))
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func chdir(path string) (err error) {
|
||||
var _p0 *byte
|
||||
_p0, err = BytePtrFromString(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_CHDIR, uintptr(unsafe.Pointer(_p0)), 0, 0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Dup(oldfd int, newfd int) (fd int, err error) {
|
||||
r0, _, e1 := Syscall(SYS_DUP, uintptr(oldfd), uintptr(newfd), 0)
|
||||
fd = int(r0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Pread(fd int, p []byte, offset int64) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(p) > 0 {
|
||||
_p0 = unsafe.Pointer(&p[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall6(SYS_PREAD, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0)
|
||||
n = int(r0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Pwrite(fd int, p []byte, offset int64) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(p) > 0 {
|
||||
_p0 = unsafe.Pointer(&p[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall6(SYS_PWRITE, uintptr(fd), uintptr(_p0), uintptr(len(p)), uintptr(offset), uintptr(offset>>32), 0)
|
||||
n = int(r0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Close(fd int) (err error) {
|
||||
r0, _, e1 := Syscall(SYS_CLOSE, uintptr(fd), 0, 0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Fstat(fd int, edir []byte) (n int, err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(edir) > 0 {
|
||||
_p0 = unsafe.Pointer(&edir[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_FSTAT, uintptr(fd), uintptr(_p0), uintptr(len(edir)))
|
||||
n = int(r0)
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Fwstat(fd int, edir []byte) (err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(edir) > 0 {
|
||||
_p0 = unsafe.Pointer(&edir[0])
|
||||
} else {
|
||||
_p0 = unsafe.Pointer(&_zero)
|
||||
}
|
||||
r0, _, e1 := Syscall(SYS_FWSTAT, uintptr(fd), uintptr(_p0), uintptr(len(edir)))
|
||||
if int32(r0) == -1 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
49
vendor/golang.org/x/sys/plan9/zsysnum_plan9.go
generated
vendored
Normal file
49
vendor/golang.org/x/sys/plan9/zsysnum_plan9.go
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
// mksysnum_plan9.sh /opt/plan9/sys/src/libc/9syscall/sys.h
|
||||
// MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
|
||||
|
||||
package plan9
|
||||
|
||||
const (
|
||||
SYS_SYSR1 = 0
|
||||
SYS_BIND = 2
|
||||
SYS_CHDIR = 3
|
||||
SYS_CLOSE = 4
|
||||
SYS_DUP = 5
|
||||
SYS_ALARM = 6
|
||||
SYS_EXEC = 7
|
||||
SYS_EXITS = 8
|
||||
SYS_FAUTH = 10
|
||||
SYS_SEGBRK = 12
|
||||
SYS_OPEN = 14
|
||||
SYS_OSEEK = 16
|
||||
SYS_SLEEP = 17
|
||||
SYS_RFORK = 19
|
||||
SYS_PIPE = 21
|
||||
SYS_CREATE = 22
|
||||
SYS_FD2PATH = 23
|
||||
SYS_BRK_ = 24
|
||||
SYS_REMOVE = 25
|
||||
SYS_NOTIFY = 28
|
||||
SYS_NOTED = 29
|
||||
SYS_SEGATTACH = 30
|
||||
SYS_SEGDETACH = 31
|
||||
SYS_SEGFREE = 32
|
||||
SYS_SEGFLUSH = 33
|
||||
SYS_RENDEZVOUS = 34
|
||||
SYS_UNMOUNT = 35
|
||||
SYS_SEMACQUIRE = 37
|
||||
SYS_SEMRELEASE = 38
|
||||
SYS_SEEK = 39
|
||||
SYS_FVERSION = 40
|
||||
SYS_ERRSTR = 41
|
||||
SYS_STAT = 42
|
||||
SYS_FSTAT = 43
|
||||
SYS_WSTAT = 44
|
||||
SYS_FWSTAT = 45
|
||||
SYS_MOUNT = 46
|
||||
SYS_AWAIT = 47
|
||||
SYS_PREAD = 50
|
||||
SYS_PWRITE = 51
|
||||
SYS_TSEMACQUIRE = 52
|
||||
SYS_NSEC = 53
|
||||
)
|
||||
26
vendor/golang.org/x/term/CONTRIBUTING.md
generated
vendored
Normal file
26
vendor/golang.org/x/term/CONTRIBUTING.md
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
# Contributing to Go
|
||||
|
||||
Go is an open source project.
|
||||
|
||||
It is the work of hundreds of contributors. We appreciate your help!
|
||||
|
||||
## Filing issues
|
||||
|
||||
When [filing an issue](https://golang.org/issue/new), make sure to answer these five questions:
|
||||
|
||||
1. What version of Go are you using (`go version`)?
|
||||
2. What operating system and processor architecture are you using?
|
||||
3. What did you do?
|
||||
4. What did you expect to see?
|
||||
5. What did you see instead?
|
||||
|
||||
General questions should go to the [golang-nuts mailing list](https://groups.google.com/group/golang-nuts) instead of the issue tracker.
|
||||
The gophers there will answer or ask you to file an issue if you've tripped over a bug.
|
||||
|
||||
## Contributing code
|
||||
|
||||
Please read the [Contribution Guidelines](https://golang.org/doc/contribute.html)
|
||||
before sending patches.
|
||||
|
||||
Unless otherwise noted, the Go source files are distributed under
|
||||
the BSD-style license found in the LICENSE file.
|
||||
27
vendor/golang.org/x/term/LICENSE
generated
vendored
Normal file
27
vendor/golang.org/x/term/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
Copyright 2009 The Go Authors.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google LLC nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
22
vendor/golang.org/x/term/PATENTS
generated
vendored
Normal file
22
vendor/golang.org/x/term/PATENTS
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
Additional IP Rights Grant (Patents)
|
||||
|
||||
"This implementation" means the copyrightable works distributed by
|
||||
Google as part of the Go project.
|
||||
|
||||
Google hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||
no-charge, royalty-free, irrevocable (except as stated in this section)
|
||||
patent license to make, have made, use, offer to sell, sell, import,
|
||||
transfer and otherwise run, modify and propagate the contents of this
|
||||
implementation of Go, where such license applies only to those patent
|
||||
claims, both currently owned or controlled by Google and acquired in
|
||||
the future, licensable by Google that are necessarily infringed by this
|
||||
implementation of Go. This grant does not include claims that would be
|
||||
infringed only as a consequence of further modification of this
|
||||
implementation. If you or your agent or exclusive licensee institute or
|
||||
order or agree to the institution of patent litigation against any
|
||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||
that this implementation of Go or any code incorporated within this
|
||||
implementation of Go constitutes direct or contributory patent
|
||||
infringement, or inducement of patent infringement, then any patent
|
||||
rights granted to you under this License for this implementation of Go
|
||||
shall terminate as of the date such litigation is filed.
|
||||
16
vendor/golang.org/x/term/README.md
generated
vendored
Normal file
16
vendor/golang.org/x/term/README.md
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
# Go terminal/console support
|
||||
|
||||
[](https://pkg.go.dev/golang.org/x/term)
|
||||
|
||||
This repository provides Go terminal and console support packages.
|
||||
|
||||
## Report Issues / Send Patches
|
||||
|
||||
This repository uses Gerrit for code changes. To learn how to submit changes to
|
||||
this repository, see https://go.dev/doc/contribute.
|
||||
|
||||
The git repository is https://go.googlesource.com/term.
|
||||
|
||||
The main issue tracker for the term repository is located at
|
||||
https://go.dev/issues. Prefix your issue with "x/term:" in the
|
||||
subject line, so it is easy to find.
|
||||
1
vendor/golang.org/x/term/codereview.cfg
generated
vendored
Normal file
1
vendor/golang.org/x/term/codereview.cfg
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
issuerepo: golang/go
|
||||
60
vendor/golang.org/x/term/term.go
generated
vendored
Normal file
60
vendor/golang.org/x/term/term.go
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package term provides support functions for dealing with terminals, as
|
||||
// commonly found on UNIX systems.
|
||||
//
|
||||
// Putting a terminal into raw mode is the most common requirement:
|
||||
//
|
||||
// oldState, err := term.MakeRaw(int(os.Stdin.Fd()))
|
||||
// if err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
// defer term.Restore(int(os.Stdin.Fd()), oldState)
|
||||
//
|
||||
// Note that on non-Unix systems os.Stdin.Fd() may not be 0.
|
||||
package term
|
||||
|
||||
// State contains the state of a terminal.
|
||||
type State struct {
|
||||
state
|
||||
}
|
||||
|
||||
// IsTerminal returns whether the given file descriptor is a terminal.
|
||||
func IsTerminal(fd int) bool {
|
||||
return isTerminal(fd)
|
||||
}
|
||||
|
||||
// MakeRaw puts the terminal connected to the given file descriptor into raw
|
||||
// mode and returns the previous state of the terminal so that it can be
|
||||
// restored.
|
||||
func MakeRaw(fd int) (*State, error) {
|
||||
return makeRaw(fd)
|
||||
}
|
||||
|
||||
// GetState returns the current state of a terminal which may be useful to
|
||||
// restore the terminal after a signal.
|
||||
func GetState(fd int) (*State, error) {
|
||||
return getState(fd)
|
||||
}
|
||||
|
||||
// Restore restores the terminal connected to the given file descriptor to a
|
||||
// previous state.
|
||||
func Restore(fd int, oldState *State) error {
|
||||
return restore(fd, oldState)
|
||||
}
|
||||
|
||||
// GetSize returns the visible dimensions of the given terminal.
|
||||
//
|
||||
// These dimensions don't include any scrollback buffer height.
|
||||
func GetSize(fd int) (width, height int, err error) {
|
||||
return getSize(fd)
|
||||
}
|
||||
|
||||
// ReadPassword reads a line of input from a terminal without local echo. This
|
||||
// is commonly used for inputting passwords and other sensitive data. The slice
|
||||
// returned does not include the \n.
|
||||
func ReadPassword(fd int) ([]byte, error) {
|
||||
return readPassword(fd)
|
||||
}
|
||||
42
vendor/golang.org/x/term/term_plan9.go
generated
vendored
Normal file
42
vendor/golang.org/x/term/term_plan9.go
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package term
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
"golang.org/x/sys/plan9"
|
||||
)
|
||||
|
||||
type state struct{}
|
||||
|
||||
func isTerminal(fd int) bool {
|
||||
path, err := plan9.Fd2path(fd)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return path == "/dev/cons" || path == "/mnt/term/dev/cons"
|
||||
}
|
||||
|
||||
func makeRaw(fd int) (*State, error) {
|
||||
return nil, fmt.Errorf("terminal: MakeRaw not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||
}
|
||||
|
||||
func getState(fd int) (*State, error) {
|
||||
return nil, fmt.Errorf("terminal: GetState not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||
}
|
||||
|
||||
func restore(fd int, state *State) error {
|
||||
return fmt.Errorf("terminal: Restore not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||
}
|
||||
|
||||
func getSize(fd int) (width, height int, err error) {
|
||||
return 0, 0, fmt.Errorf("terminal: GetSize not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||
}
|
||||
|
||||
func readPassword(fd int) ([]byte, error) {
|
||||
return nil, fmt.Errorf("terminal: ReadPassword not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||
}
|
||||
91
vendor/golang.org/x/term/term_unix.go
generated
vendored
Normal file
91
vendor/golang.org/x/term/term_unix.go
generated
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos
|
||||
|
||||
package term
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
type state struct {
|
||||
termios unix.Termios
|
||||
}
|
||||
|
||||
func isTerminal(fd int) bool {
|
||||
_, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func makeRaw(fd int) (*State, error) {
|
||||
termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
oldState := State{state{termios: *termios}}
|
||||
|
||||
// This attempts to replicate the behaviour documented for cfmakeraw in
|
||||
// the termios(3) manpage.
|
||||
termios.Iflag &^= unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON
|
||||
termios.Oflag &^= unix.OPOST
|
||||
termios.Lflag &^= unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN
|
||||
termios.Cflag &^= unix.CSIZE | unix.PARENB
|
||||
termios.Cflag |= unix.CS8
|
||||
termios.Cc[unix.VMIN] = 1
|
||||
termios.Cc[unix.VTIME] = 0
|
||||
if err := unix.IoctlSetTermios(fd, ioctlWriteTermios, termios); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &oldState, nil
|
||||
}
|
||||
|
||||
func getState(fd int) (*State, error) {
|
||||
termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &State{state{termios: *termios}}, nil
|
||||
}
|
||||
|
||||
func restore(fd int, state *State) error {
|
||||
return unix.IoctlSetTermios(fd, ioctlWriteTermios, &state.termios)
|
||||
}
|
||||
|
||||
func getSize(fd int) (width, height int, err error) {
|
||||
ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
return int(ws.Col), int(ws.Row), nil
|
||||
}
|
||||
|
||||
// passwordReader is an io.Reader that reads from a specific file descriptor.
|
||||
type passwordReader int
|
||||
|
||||
func (r passwordReader) Read(buf []byte) (int, error) {
|
||||
return unix.Read(int(r), buf)
|
||||
}
|
||||
|
||||
func readPassword(fd int) ([]byte, error) {
|
||||
termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newState := *termios
|
||||
newState.Lflag &^= unix.ECHO
|
||||
newState.Lflag |= unix.ICANON | unix.ISIG
|
||||
newState.Iflag |= unix.ICRNL
|
||||
if err := unix.IoctlSetTermios(fd, ioctlWriteTermios, &newState); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer unix.IoctlSetTermios(fd, ioctlWriteTermios, termios)
|
||||
|
||||
return readPasswordLine(passwordReader(fd))
|
||||
}
|
||||
12
vendor/golang.org/x/term/term_unix_bsd.go
generated
vendored
Normal file
12
vendor/golang.org/x/term/term_unix_bsd.go
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build darwin || dragonfly || freebsd || netbsd || openbsd
|
||||
|
||||
package term
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
const ioctlReadTermios = unix.TIOCGETA
|
||||
const ioctlWriteTermios = unix.TIOCSETA
|
||||
12
vendor/golang.org/x/term/term_unix_other.go
generated
vendored
Normal file
12
vendor/golang.org/x/term/term_unix_other.go
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build aix || linux || solaris || zos
|
||||
|
||||
package term
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
const ioctlReadTermios = unix.TCGETS
|
||||
const ioctlWriteTermios = unix.TCSETS
|
||||
38
vendor/golang.org/x/term/term_unsupported.go
generated
vendored
Normal file
38
vendor/golang.org/x/term/term_unsupported.go
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !zos && !windows && !solaris && !plan9
|
||||
|
||||
package term
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
type state struct{}
|
||||
|
||||
func isTerminal(fd int) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func makeRaw(fd int) (*State, error) {
|
||||
return nil, fmt.Errorf("terminal: MakeRaw not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||
}
|
||||
|
||||
func getState(fd int) (*State, error) {
|
||||
return nil, fmt.Errorf("terminal: GetState not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||
}
|
||||
|
||||
func restore(fd int, state *State) error {
|
||||
return fmt.Errorf("terminal: Restore not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||
}
|
||||
|
||||
func getSize(fd int) (width, height int, err error) {
|
||||
return 0, 0, fmt.Errorf("terminal: GetSize not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||
}
|
||||
|
||||
func readPassword(fd int) ([]byte, error) {
|
||||
return nil, fmt.Errorf("terminal: ReadPassword not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||
}
|
||||
82
vendor/golang.org/x/term/term_windows.go
generated
vendored
Normal file
82
vendor/golang.org/x/term/term_windows.go
generated
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package term
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
type state struct {
|
||||
mode uint32
|
||||
}
|
||||
|
||||
func isTerminal(fd int) bool {
|
||||
var st uint32
|
||||
err := windows.GetConsoleMode(windows.Handle(fd), &st)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// This is intended to be used on a console input handle.
|
||||
// See https://learn.microsoft.com/en-us/windows/console/setconsolemode
|
||||
func makeRaw(fd int) (*State, error) {
|
||||
var st uint32
|
||||
if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
raw := st &^ (windows.ENABLE_ECHO_INPUT | windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT)
|
||||
raw |= windows.ENABLE_VIRTUAL_TERMINAL_INPUT
|
||||
if err := windows.SetConsoleMode(windows.Handle(fd), raw); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &State{state{st}}, nil
|
||||
}
|
||||
|
||||
func getState(fd int) (*State, error) {
|
||||
var st uint32
|
||||
if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &State{state{st}}, nil
|
||||
}
|
||||
|
||||
func restore(fd int, state *State) error {
|
||||
return windows.SetConsoleMode(windows.Handle(fd), state.mode)
|
||||
}
|
||||
|
||||
func getSize(fd int) (width, height int, err error) {
|
||||
var info windows.ConsoleScreenBufferInfo
|
||||
if err := windows.GetConsoleScreenBufferInfo(windows.Handle(fd), &info); err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
return int(info.Window.Right - info.Window.Left + 1), int(info.Window.Bottom - info.Window.Top + 1), nil
|
||||
}
|
||||
|
||||
func readPassword(fd int) ([]byte, error) {
|
||||
var st uint32
|
||||
if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
old := st
|
||||
|
||||
st &^= (windows.ENABLE_ECHO_INPUT | windows.ENABLE_LINE_INPUT)
|
||||
st |= (windows.ENABLE_PROCESSED_OUTPUT | windows.ENABLE_PROCESSED_INPUT)
|
||||
if err := windows.SetConsoleMode(windows.Handle(fd), st); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer windows.SetConsoleMode(windows.Handle(fd), old)
|
||||
|
||||
var h windows.Handle
|
||||
p, _ := windows.GetCurrentProcess()
|
||||
if err := windows.DuplicateHandle(p, windows.Handle(fd), p, &h, 0, false, windows.DUPLICATE_SAME_ACCESS); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
f := os.NewFile(uintptr(h), "stdin")
|
||||
defer f.Close()
|
||||
return readPasswordLine(f)
|
||||
}
|
||||
1074
vendor/golang.org/x/term/terminal.go
generated
vendored
Normal file
1074
vendor/golang.org/x/term/terminal.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user