From f932dd64cce2766085bd035c89e2f306be4aa5b8 Mon Sep 17 00:00:00 2001 From: Kyle Isom Date: Fri, 27 Mar 2026 21:45:42 -0700 Subject: [PATCH] Add undeploy command: full inverse of deploy Implements `mcp undeploy ` which tears down all infrastructure for a service: removes mc-proxy routes, DNS records, TLS certificates, stops and removes containers, releases allocated ports, and marks the service inactive. This fills the gap between `stop` (temporary pause) and `purge` (registry cleanup). Undeploy is the complete teardown that returns the node to the state before the service was deployed. Co-Authored-By: Claude Opus 4.6 (1M context) --- ARCHITECTURE.md | 132 ++++++++++++ cmd/mcp/main.go | 1 + cmd/mcp/undeploy.go | 63 ++++++ gen/mcp/v1/mcp.pb.go | 398 +++++++++++++++++++++++-------------- gen/mcp/v1/mcp_grpc.pb.go | 38 ++++ internal/agent/certs.go | 19 ++ internal/agent/undeploy.go | 100 ++++++++++ proto/mcp/v1/mcp.proto | 9 + 8 files changed, 610 insertions(+), 150 deletions(-) create mode 100644 cmd/mcp/undeploy.go create mode 100644 internal/agent/undeploy.go diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 83df453..54d7100 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -198,6 +198,7 @@ mcp build / Build and push a single image mcp deploy Deploy all components from service definition mcp deploy / Deploy a single component mcp deploy -f Deploy from explicit file +mcp undeploy Full teardown: remove routes, DNS, certs, containers mcp stop Stop all components, set active=false mcp start Start all components, set active=true mcp restart 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 ` (repeated) | | `cmd` | appended after the image name | +#### Undeploy Flow + +`mcp undeploy ` 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 -o /tmp/image.tar + scp /tmp/image.tar :/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 :53:53/tcp -p :53:53/udp \ + -p :53:53/tcp -p :53:53/udp \ + -v /srv/mcns:/srv/mcns \ + 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 \ + 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 \ + server --config /srv/mcr/mcr.toml + ``` + +5. **Push images to MCR** from the operator workstation now that the + registry is reachable: + ``` + docker push /: + ``` + +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 ` +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 diff --git a/cmd/mcp/main.go b/cmd/mcp/main.go index 8ee2ed8..48650ee 100644 --- a/cmd/mcp/main.go +++ b/cmd/mcp/main.go @@ -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()) diff --git a/cmd/mcp/undeploy.go b/cmd/mcp/undeploy.go new file mode 100644 index 0000000..4badcfe --- /dev/null +++ b/cmd/mcp/undeploy.go @@ -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 ", + 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 + }, + } +} diff --git a/gen/mcp/v1/mcp.pb.go b/gen/mcp/v1/mcp.pb.go index e4b1bde..c00e3d4 100644 --- a/gen/mcp/v1/mcp.pb.go +++ b/gen/mcp/v1/mcp.pb.go @@ -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_) - 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" + @@ -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, }, diff --git a/gen/mcp/v1/mcp_grpc.pb.go b/gen/mcp/v1/mcp_grpc.pb.go index 83fa016..2e83e1a 100644 --- a/gen/mcp/v1/mcp_grpc.pb.go +++ b/gen/mcp/v1/mcp_grpc.pb.go @@ -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, diff --git a/internal/agent/certs.go b/internal/agent/certs.go index 629bd1c..e77698a 100644 --- a/internal/agent/certs.go +++ b/internal/agent/certs.go @@ -176,6 +176,25 @@ func (p *CertProvisioner) issueCert(ctx context.Context, serviceName, commonName 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) { diff --git a/internal/agent/undeploy.go b/internal/agent/undeploy.go new file mode 100644 index 0000000..8f14834 --- /dev/null +++ b/internal/agent/undeploy.go @@ -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 +} diff --git a/proto/mcp/v1/mcp.proto b/proto/mcp/v1/mcp.proto index b0a9f7f..18426bd 100644 --- a/proto/mcp/v1/mcp.proto +++ b/proto/mcp/v1/mcp.proto @@ -8,6 +8,7 @@ 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); @@ -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 {