Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions services/mcp/kubernetes/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, built with `go test -c`
*.test

# Output of the go coverage tool
*.out

# Go workspace file
go.work

# Dependency directories
/vendor/

# Build outputs
/bin/
/dist/

# IDE and editor directories
.idea/
.vscode/
*.swp
*.swo

# OS specific files
.DS_Store
Thumbs.db

# Local configuration files
*.local.yaml
*.env
32 changes: 32 additions & 0 deletions services/mcp/kubernetes/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
FROM golang:1.22-alpine AS builder

WORKDIR /app

# Copy the go module files first to leverage Docker cache
COPY go.mod ./
COPY go.sum ./

# Download dependencies
RUN go mod download

# Copy the source code
COPY . .

# Build the application
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o mcp-server ./cmd/server

# Use distroless for minimal image
FROM gcr.io/distroless/static:nonroot

WORKDIR /app

# Copy the binary from the builder stage
COPY --from=builder /app/mcp-server .
COPY --from=builder /app/config /app/config

# Run as non-root user
USER nonroot:nonroot

EXPOSE 8080 8081

ENTRYPOINT ["/app/mcp-server"]
104 changes: 104 additions & 0 deletions services/mcp/kubernetes/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# Kubernetes MCP Server

A Management Control Plane (MCP) server for Kubernetes that provides APIs for managing services.

## Overview

The MCP server is a Go-based application that interacts with Kubernetes to provide a higher-level API for managing services. It serves as an abstraction layer between users and Kubernetes, making it easier to deploy and manage applications.

## Features

- RESTful API for service management
- Authentication with JWT and API keys
- Kubernetes controller for service reconciliation
- Metrics and health monitoring
- Deployment as a Kubernetes application

## Project Structure

```
.
├── cmd/ # Command-line applications
│ └── server/ # MCP server entry point
├── pkg/ # Library packages
│ ├── api/ # API server and handlers
│ ├── auth/ # Authentication and authorization
│ ├── controller/ # Kubernetes controllers
│ ├── k8s/ # Kubernetes client utilities
│ └── model/ # Data models
├── config/ # Configuration files
├── deploy/ # Deployment manifests
│ └── kubernetes/ # Kubernetes deployment manifests
└── Dockerfile # Container image definition
```

## Getting Started

### Prerequisites

- Go 1.22 or higher
- Access to a Kubernetes cluster
- kubectl configured to access your cluster

### Building

```bash
# Build the server
go build -o mcp-server ./cmd/server

# Run the server
./mcp-server
```

### Running with Docker

```bash
# Build the Docker image
docker build -t infragpt/mcp-server:latest .

# Run the container
docker run -p 8081:8081 -p 8080:8080 infragpt/mcp-server:latest
```

### Deploying to Kubernetes

```bash
# Create namespace
kubectl apply -f deploy/kubernetes/namespace.yaml

# Create RBAC resources
kubectl apply -f deploy/kubernetes/rbac.yaml

# Create ConfigMap
kubectl apply -f deploy/kubernetes/configmap.yaml

# Deploy the server
kubectl apply -f deploy/kubernetes/deployment.yaml
kubectl apply -f deploy/kubernetes/service.yaml
```

## API Endpoints

- `GET /api/v1/health` - Health check
- `GET /api/v1/services` - List services
- `POST /api/v1/services` - Create a service

## Configuration

Configuration is loaded from `config/config.yaml`. See the example configuration for details.

## Development

### Running Tests

```bash
go test ./...
```

### Code Style

This project follows the standard Go style guidelines and uses `gofmt` for formatting.

## License

This project is licensed under the MIT License - see the LICENSE file for details.
92 changes: 92 additions & 0 deletions services/mcp/kubernetes/cmd/server/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package main

import (
"context"
"flag"
"log"
"os"
"os/signal"
"syscall"
"time"

"github.com/infragpt/services/mcp/kubernetes/pkg/api"
"github.com/infragpt/services/mcp/kubernetes/pkg/controller"
"github.com/infragpt/services/mcp/kubernetes/pkg/k8s"
)

func main() {
var (
kubeconfig string
masterURL string
metricsAddr string
enableLeaderElection bool
apiAddr string
)

flag.StringVar(&kubeconfig, "kubeconfig", "", "Path to kubeconfig file")
flag.StringVar(&masterURL, "master", "", "URL to Kubernetes API server")
flag.StringVar(&metricsAddr, "metrics-addr", ":8080", "Address for serving metrics")
flag.BoolVar(&enableLeaderElection, "enable-leader-election", false, "Enable leader election")
flag.StringVar(&apiAddr, "api-addr", ":8081", "Address for serving API")
flag.Parse()

// Set up logging
logger := log.New(os.Stdout, "MCP-SERVER: ", log.LstdFlags)

// Initialize Kubernetes client
clientConfig, err := k8s.NewClientConfig(kubeconfig, masterURL)
if err != nil {
logger.Fatalf("Error building kubernetes clientset: %s", err.Error())
}

// Create context with cancellation
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

// Set up controllers
controllerManager, err := controller.NewManager(ctx, clientConfig, controller.Options{
MetricsAddr: metricsAddr,
EnableLeaderElection: enableLeaderElection,
LeaderElectionID: "mcp-controller-lock",
})
if err != nil {
logger.Fatalf("Unable to set up controller manager: %s", err.Error())
}

// Start the API server
apiServer := api.NewServer(apiAddr, clientConfig)
go func() {
if err := apiServer.Start(ctx); err != nil {
logger.Fatalf("Error starting API server: %s", err.Error())
}
}()

// Handle shutdown gracefully
signalCh := make(chan os.Signal, 1)
signal.Notify(signalCh, syscall.SIGINT, syscall.SIGTERM)

// Start the controller manager
go func() {
if err := controllerManager.Start(); err != nil {
logger.Fatalf("Error starting controller manager: %s", err.Error())
}
}()

logger.Printf("MCP server started, waiting for signal to exit")

// Wait for shutdown signal
<-signalCh
logger.Println("Received shutdown signal, shutting down gracefully...")

// Give controllers time to shutdown gracefully
cancelCtx, cancelFunc := context.WithTimeout(ctx, 5*time.Second)
defer cancelFunc()

if err := apiServer.Shutdown(cancelCtx); err != nil {
logger.Printf("Error during API server shutdown: %s", err.Error())
}

cancel() // Signal controllers to shutdown

logger.Println("Shutdown complete")
}
25 changes: 25 additions & 0 deletions services/mcp/kubernetes/config/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
api:
addr: ":8081"

metrics:
addr: ":8080"

auth:
jwtSecret: "replace-with-real-secret-in-production"
tokenExpiration: "24h"
apiKeys:
admin: "replace-with-real-api-key-in-production"

kubernetes:
inCluster: true
# Uncomment these if not running in cluster
# kubeconfig: ""
# masterURL: ""

controller:
enableLeaderElection: true
leaderElectionID: "mcp-controller-lock"

logging:
level: "info"
format: "json"
29 changes: 29 additions & 0 deletions services/mcp/kubernetes/deploy/kubernetes/configmap.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: mcp-server-config
namespace: infragpt
data:
config.yaml: |
api:
addr: ":8081"

metrics:
addr: ":8080"

auth:
jwtSecret: "${JWT_SECRET}"
tokenExpiration: "24h"
apiKeys:
admin: "${API_KEY}"

kubernetes:
inCluster: true

controller:
enableLeaderElection: true
leaderElectionID: "mcp-controller-lock"

logging:
level: "info"
format: "json"
57 changes: 57 additions & 0 deletions services/mcp/kubernetes/deploy/kubernetes/deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: mcp-server
namespace: infragpt
labels:
app: mcp-server
spec:
replicas: 1
selector:
matchLabels:
app: mcp-server
template:
metadata:
labels:
app: mcp-server
spec:
serviceAccountName: mcp-server
containers:
- name: mcp-server
image: infragpt/mcp-server:latest
imagePullPolicy: Always
args:
- "--metrics-addr=:8080"
- "--api-addr=:8081"
- "--enable-leader-election=true"
ports:
- name: metrics
containerPort: 8080
- name: api
containerPort: 8081
resources:
limits:
cpu: "500m"
memory: "512Mi"
requests:
cpu: "100m"
memory: "128Mi"
livenessProbe:
httpGet:
path: /api/v1/health
port: api
initialDelaySeconds: 15
periodSeconds: 20
readinessProbe:
httpGet:
path: /api/v1/health
port: api
initialDelaySeconds: 5
periodSeconds: 10
volumeMounts:
- name: config
mountPath: /app/config
volumes:
- name: config
configMap:
name: mcp-server-config
6 changes: 6 additions & 0 deletions services/mcp/kubernetes/deploy/kubernetes/namespace.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
apiVersion: v1
kind: Namespace
metadata:
name: infragpt
labels:
name: infragpt
Loading