Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5da307cab5 | |||
| 22a836812f |
22
Dockerfile.master
Normal file
22
Dockerfile.master
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
FROM golang:1.25-alpine AS builder
|
||||||
|
|
||||||
|
ARG VERSION=dev
|
||||||
|
|
||||||
|
WORKDIR /build
|
||||||
|
COPY go.mod go.sum ./
|
||||||
|
RUN go mod download
|
||||||
|
COPY . .
|
||||||
|
RUN CGO_ENABLED=0 go build -trimpath -ldflags="-s -w -X main.version=${VERSION}" \
|
||||||
|
-o /mcp-master ./cmd/mcp-master
|
||||||
|
|
||||||
|
FROM alpine:3.21
|
||||||
|
|
||||||
|
RUN apk add --no-cache ca-certificates tzdata
|
||||||
|
|
||||||
|
COPY --from=builder /mcp-master /usr/local/bin/mcp-master
|
||||||
|
|
||||||
|
WORKDIR /srv/mcp-master
|
||||||
|
EXPOSE 9555
|
||||||
|
|
||||||
|
ENTRYPOINT ["mcp-master"]
|
||||||
|
CMD ["server", "--config", "/srv/mcp-master/mcp-master.toml"]
|
||||||
5
Makefile
5
Makefile
@@ -32,6 +32,11 @@ proto-lint:
|
|||||||
buf lint
|
buf lint
|
||||||
buf breaking --against '.git#branch=master,subdir=proto'
|
buf breaking --against '.git#branch=master,subdir=proto'
|
||||||
|
|
||||||
|
docker-master:
|
||||||
|
podman build -f Dockerfile.master \
|
||||||
|
--build-arg VERSION=$(shell git describe --tags --always --dirty) \
|
||||||
|
-t mcr.svc.mcp.metacircular.net:8443/mcp-master:$(shell git describe --tags --always --dirty) .
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f mcp mcp-agent mcp-master
|
rm -f mcp mcp-agent mcp-master
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,10 @@ import (
|
|||||||
// ServiceDef is the top-level TOML structure for a service definition file.
|
// ServiceDef is the top-level TOML structure for a service definition file.
|
||||||
type ServiceDef struct {
|
type ServiceDef struct {
|
||||||
Name string `toml:"name"`
|
Name string `toml:"name"`
|
||||||
Node string `toml:"node"`
|
Node string `toml:"node,omitempty"`
|
||||||
|
Tier string `toml:"tier,omitempty"`
|
||||||
Active *bool `toml:"active,omitempty"`
|
Active *bool `toml:"active,omitempty"`
|
||||||
|
Comment string `toml:"comment,omitempty"`
|
||||||
Path string `toml:"path,omitempty"`
|
Path string `toml:"path,omitempty"`
|
||||||
Build *BuildDef `toml:"build,omitempty"`
|
Build *BuildDef `toml:"build,omitempty"`
|
||||||
Components []ComponentDef `toml:"components"`
|
Components []ComponentDef `toml:"components"`
|
||||||
@@ -36,6 +38,7 @@ type RouteDef struct {
|
|||||||
Port int `toml:"port"`
|
Port int `toml:"port"`
|
||||||
Mode string `toml:"mode,omitempty"`
|
Mode string `toml:"mode,omitempty"`
|
||||||
Hostname string `toml:"hostname,omitempty"`
|
Hostname string `toml:"hostname,omitempty"`
|
||||||
|
Public bool `toml:"public,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ComponentDef describes a single container component within a service.
|
// ComponentDef describes a single container component within a service.
|
||||||
@@ -129,8 +132,9 @@ func validate(def *ServiceDef) error {
|
|||||||
if def.Name == "" {
|
if def.Name == "" {
|
||||||
return fmt.Errorf("service name is required")
|
return fmt.Errorf("service name is required")
|
||||||
}
|
}
|
||||||
if def.Node == "" {
|
// v2: either node or tier must be set. Tier defaults to "worker" if both empty.
|
||||||
return fmt.Errorf("service node is required")
|
if def.Node == "" && def.Tier == "" {
|
||||||
|
def.Tier = "worker"
|
||||||
}
|
}
|
||||||
if len(def.Components) == 0 {
|
if len(def.Components) == 0 {
|
||||||
return fmt.Errorf("service %q must have at least one component", def.Name)
|
return fmt.Errorf("service %q must have at least one component", def.Name)
|
||||||
@@ -191,8 +195,11 @@ func validateRoutes(compName, svcName string, routes []RouteDef) error {
|
|||||||
// ToProto converts a ServiceDef to a proto ServiceSpec.
|
// ToProto converts a ServiceDef to a proto ServiceSpec.
|
||||||
func ToProto(def *ServiceDef) *mcpv1.ServiceSpec {
|
func ToProto(def *ServiceDef) *mcpv1.ServiceSpec {
|
||||||
spec := &mcpv1.ServiceSpec{
|
spec := &mcpv1.ServiceSpec{
|
||||||
Name: def.Name,
|
Name: def.Name,
|
||||||
Active: def.Active != nil && *def.Active,
|
Active: def.Active != nil && *def.Active,
|
||||||
|
Comment: def.Comment,
|
||||||
|
Tier: def.Tier,
|
||||||
|
Node: def.Node,
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, c := range def.Components {
|
for _, c := range def.Components {
|
||||||
@@ -213,6 +220,7 @@ func ToProto(def *ServiceDef) *mcpv1.ServiceSpec {
|
|||||||
Port: int32(r.Port), //nolint:gosec // port range validated
|
Port: int32(r.Port), //nolint:gosec // port range validated
|
||||||
Mode: r.Mode,
|
Mode: r.Mode,
|
||||||
Hostname: r.Hostname,
|
Hostname: r.Hostname,
|
||||||
|
Public: r.Public,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
spec.Components = append(spec.Components, cs)
|
spec.Components = append(spec.Components, cs)
|
||||||
@@ -227,9 +235,11 @@ func ToProto(def *ServiceDef) *mcpv1.ServiceSpec {
|
|||||||
func FromProto(spec *mcpv1.ServiceSpec, node string) *ServiceDef {
|
func FromProto(spec *mcpv1.ServiceSpec, node string) *ServiceDef {
|
||||||
active := spec.GetActive()
|
active := spec.GetActive()
|
||||||
def := &ServiceDef{
|
def := &ServiceDef{
|
||||||
Name: spec.GetName(),
|
Name: spec.GetName(),
|
||||||
Node: node,
|
Node: node,
|
||||||
Active: &active,
|
Tier: spec.GetTier(),
|
||||||
|
Active: &active,
|
||||||
|
Comment: spec.GetComment(),
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, c := range spec.GetComponents() {
|
for _, c := range spec.GetComponents() {
|
||||||
@@ -250,6 +260,7 @@ func FromProto(spec *mcpv1.ServiceSpec, node string) *ServiceDef {
|
|||||||
Port: int(r.GetPort()),
|
Port: int(r.GetPort()),
|
||||||
Mode: r.GetMode(),
|
Mode: r.GetMode(),
|
||||||
Hostname: r.GetHostname(),
|
Hostname: r.GetHostname(),
|
||||||
|
Public: r.GetPublic(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
def.Components = append(def.Components, cd)
|
def.Components = append(def.Components, cd)
|
||||||
|
|||||||
@@ -119,14 +119,8 @@ func TestValidation(t *testing.T) {
|
|||||||
},
|
},
|
||||||
wantErr: "service name is required",
|
wantErr: "service name is required",
|
||||||
},
|
},
|
||||||
{
|
// v2: missing node no longer errors — defaults to tier=worker.
|
||||||
name: "missing node",
|
// Tested separately in TestValidationNodeTierDefault.
|
||||||
def: &ServiceDef{
|
|
||||||
Name: "svc",
|
|
||||||
Components: []ComponentDef{{Name: "api", Image: "img:v1"}},
|
|
||||||
},
|
|
||||||
wantErr: "service node is required",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "empty components",
|
name: "empty components",
|
||||||
def: &ServiceDef{
|
def: &ServiceDef{
|
||||||
|
|||||||
Reference in New Issue
Block a user