deploy: Deploy the registry to GCP (#255)
Adds the Pulumi code to: - Deploy the registry (and associated services e.g. mongodb) to Google Cloud Platform (GCP), on top of Google Kubernetes Engine (GKE) - Sets up proper environments and secrets management - Uses the real container image, now that it's published in #225. At the moment attached to latest, we might want to pin the version later (or perhaps always use `latest` in staging, and pin prod) - Uses real domains (`staging.registry.modelcontextprotocol.io`) rather than examples (``) ## Motivation and Context Setting up infrastructure to deploy it. I set something up in Azure in #227, although not super robust (e.g. no service accounts etc.). Think we will use GCP as: - the maintainers have experience with GCP, but none with Azure - costs are quite low, and Anthropic is happy to cover them in the short term - means we only have to maintain one login system (just Google Cloud Identity), not two (Google Workspace + Azure) ## How Has This Been Tested? Deployed this to a staging and production cluster. Try it yourself at: ```bash curl -H "Host: staging.registry.modelcontextprotocol.io" -k https://35.222.36.75/v0/ping ``` (will be sorting out domains very soon) ## Breaking Changes NA - just adds support for GCP deployment ## Types of changes <!-- What types of changes does your code introduce? Put an `x` in all the boxes that apply: --> - [ ] Bug fix (non-breaking change which fixes an issue) - [x] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Documentation update ## Checklist <!-- Go over all the following points, and put an `x` in all the boxes that apply. --> - [x] I have read the [MCP Documentation](https://modelcontextprotocol.io) - [x] My code follows the repository's style guidelines - [ ] New and existing tests pass locally - [x] I have added appropriate error handling - [x] I have added or updated documentation as needed ## Additional context <!-- Add any other context, implementation notes, or design decisions --> Expected follow-ups: - GitHub Action setup to deploy things to the cluster from GitHub, to avoid gatekeeping to just the people with the secrets.
This commit is contained in:
parent
667e2840b8
commit
fe52766676
13 changed files with 395 additions and 144 deletions
4
deploy/.gitignore
vendored
4
deploy/.gitignore
vendored
|
|
@ -26,3 +26,7 @@ Thumbs.db
|
|||
|
||||
# Build artifacts
|
||||
infra
|
||||
|
||||
# Secrets
|
||||
passphrase.prod.txt
|
||||
passphrase.staging.txt
|
||||
|
|
|
|||
|
|
@ -1,47 +1,31 @@
|
|||
.PHONY: help init preview deploy destroy clean dev-init prod-init dev-deploy prod-deploy
|
||||
.PHONY: help build local-preview local-up staging-preview staging-up prod-preview prod-up
|
||||
|
||||
# Default target
|
||||
help: ## Show this help message
|
||||
@echo "Usage: make [target]"
|
||||
@echo ""
|
||||
@echo "Targets:"
|
||||
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " %-15s %s\n", $$1, $$2}'
|
||||
@echo "Available targets:"
|
||||
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " %-20s %s\n", $$1, $$2}'
|
||||
|
||||
init: ## Initialize Pulumi stack
|
||||
pulumi stack init
|
||||
# Build the Go binary
|
||||
build: ## Build the Pulumi Go program
|
||||
go build
|
||||
|
||||
preview: ## Preview infrastructure changes
|
||||
pulumi preview
|
||||
# Local stack commands
|
||||
local-preview: build ## Preview local infrastructure changes
|
||||
PULUMI_CONFIG_PASSPHRASE="" pulumi preview --stack local
|
||||
|
||||
deploy: ## Deploy infrastructure
|
||||
pulumi up --yes
|
||||
local-up: build ## Deploy local infrastructure
|
||||
PULUMI_CONFIG_PASSPHRASE="" pulumi up --yes --stack local
|
||||
|
||||
destroy: ## Destroy infrastructure
|
||||
pulumi destroy --yes
|
||||
# Staging stack commands
|
||||
staging-preview: build ## Preview staging infrastructure changes
|
||||
PULUMI_CONFIG_PASSPHRASE_FILE=passphrase.staging.txt pulumi preview --stack gcpStaging
|
||||
|
||||
clean: ## Clean up temporary files
|
||||
rm -rf .pulumi/
|
||||
rm -f Pulumi.*.yaml.bak
|
||||
staging-up: build ## Deploy staging infrastructure
|
||||
PULUMI_CONFIG_PASSPHRASE_FILE=passphrase.staging.txt pulumi up --yes --stack gcpStaging
|
||||
|
||||
dev-init: ## Initialize development stack
|
||||
pulumi stack init dev
|
||||
pulumi stack select dev
|
||||
# Production stack commands
|
||||
prod-preview: build ## Preview production infrastructure changes
|
||||
PULUMI_CONFIG_PASSPHRASE_FILE=passphrase.prod.txt pulumi preview --stack gcpProd
|
||||
|
||||
prod-init: ## Initialize production stack
|
||||
pulumi stack init prod
|
||||
pulumi stack select prod
|
||||
|
||||
dev-deploy: ## Deploy to development environment
|
||||
pulumi stack select dev
|
||||
pulumi up --yes
|
||||
|
||||
prod-deploy: ## Deploy to production environment
|
||||
pulumi stack select prod
|
||||
pulumi up --yes
|
||||
|
||||
dev-destroy: ## Destroy development environment
|
||||
pulumi stack select dev
|
||||
pulumi destroy --yes
|
||||
|
||||
prod-destroy: ## Destroy production environment
|
||||
pulumi stack select prod
|
||||
pulumi destroy --yes
|
||||
prod-up: build ## Deploy production infrastructure
|
||||
PULUMI_CONFIG_PASSPHRASE_FILE=passphrase.prod.txt pulumi up --yes --stack gcpProd
|
||||
6
deploy/Pulumi.aksProd.yaml
Normal file
6
deploy/Pulumi.aksProd.yaml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
config:
|
||||
mcp-registry:environment: prod
|
||||
mcp-registry:provider: aks
|
||||
mcp-registry:githubClientId: test-client-id
|
||||
mcp-registry:githubClientSecret: some-secret-here
|
||||
encryptionsalt: v1:oDjcEdLMFwM=:v1:FdgMU4r7kVWUbaUt:1aTSUxrNy+JNW51KRXzoMACuAXruUg==
|
||||
10
deploy/Pulumi.gcpProd.yaml
Normal file
10
deploy/Pulumi.gcpProd.yaml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
config:
|
||||
mcp-registry:environment: prod
|
||||
mcp-registry:provider: gcp
|
||||
gcp:project: mcp-registry-prod
|
||||
mcp-registry:githubClientId: your-github-client-id
|
||||
mcp-registry:githubClientSecret:
|
||||
secure: v1:68BmTaKlZ+v77IDj:vVeMsAq/8QoCzkLzgo8MC8F5ALcC5TydWiqZ2NmnnFRpfF3MRPV69Os=
|
||||
gcp:credentials:
|
||||
secure: v1:hyZWlpeMTFDnMcz2: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
|
||||
encryptionsalt: v1:0funtAX4m9k=:v1:yMCnBXyBO+q4+/yy:AqWJTzwwIWXmUK0JGCzqbeg0RUr8Jw==
|
||||
10
deploy/Pulumi.gcpStaging.yaml
Normal file
10
deploy/Pulumi.gcpStaging.yaml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
config:
|
||||
mcp-registry:environment: staging
|
||||
mcp-registry:provider: gcp
|
||||
gcp:project: mcp-registry-staging
|
||||
mcp-registry:githubClientId: your-github-client-id
|
||||
mcp-registry:githubClientSecret:
|
||||
secure: v1:BpUPXf7aQcG8qHF9:YJxKbhWi5ZQSTdNGrdhzzCGz7sWXKhmNe+yrTls2cKC3/w==
|
||||
gcp:credentials:
|
||||
secure: v1:RaHpGsBp37XO/EhJ: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
|
||||
encryptionsalt: v1:EKBwmTmss1c=:v1:JLd4a7cM0X8Jroh+:z9/q6RSCEFTDzMV6X6h5Tpbw0tnpkA==
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
# MCP Registry Kubernetes Deployment
|
||||
|
||||
This directory contains Pulumi infrastructure code to deploy the MCP Registry service to a Kubernetes cluster. It supports multiple Kubernetes providers: Azure Kubernetes Service (AKS) and local (using existing kubeconfig).
|
||||
This directory contains Pulumi infrastructure code to deploy the MCP Registry service to a Kubernetes cluster. It supports deploying the infrastructure locally (using an existing kubeconfig, e.g. with minikube) or to Google Cloud Platform (GCP).
|
||||
|
||||
## Quick Start
|
||||
|
||||
|
|
@ -19,17 +19,53 @@ Pre-requisites:
|
|||
|
||||
# To use your local kubeconfig (default)
|
||||
pulumi config set mcp-registry:provider local
|
||||
# Alternative: To use AKS
|
||||
# pulumi config set mcp-registry:provider aks
|
||||
|
||||
# GitHub OAuth
|
||||
pulumi config set mcp-registry:githubClientId <your-github-client-id>
|
||||
pulumi config set --secret mcp-registry:githubClientSecret <your-github-client-secret>
|
||||
```
|
||||
4. Deploy: `go build && PULUMI_CONFIG_PASSPHRASE="" pulumi up --yes`
|
||||
5. Access the repository via the ingress load balancer. You can find its external IP with `kubectl get svc nginx-ingress-ingress-nginx-controller -n ingress-nginx` (with minikube, if it's 'pending' you might need `minikube tunnel`). Then run `curl -H "Host: mcp-registry-local.example.com" -k https://<EXTERNAL-IP>/v0/ping` to check that the service is up.
|
||||
4. Deploy: `make local-up`
|
||||
5. Access the repository via the ingress load balancer. You can find its external IP with `kubectl get svc ingress-nginx-controller -n ingress-nginx` (with minikube, if it's 'pending' you might need `minikube tunnel`). Then run `curl -H "Host: local.registry.modelcontextprotocol.io" -k https://<EXTERNAL-IP>/v0/ping` to check that the service is up.
|
||||
|
||||
### Production Deployment (AKS)
|
||||
### Production Deployment (GCP)
|
||||
|
||||
**Note:** This is how the production deployment will be set up once. But then the plan will be future updates are effectively a login + `pulumi up` from GitHub Actions.
|
||||
|
||||
Pre-requisites:
|
||||
- [Pulumi CLI installed](https://www.pulumi.com/docs/iac/download-install/)
|
||||
- A Google Cloud Platform (GCP) account
|
||||
- A GCP Service Account with appropriate permissions
|
||||
|
||||
1. Create a project: `gcloud projects create mcp-registry-prod`
|
||||
2. Set the project: `gcloud config set project mcp-registry-prod`
|
||||
3. Enable required APIs: `gcloud services enable storage.googleapis.com && gcloud services enable container.googleapis.com`
|
||||
4. Create a service account with necessary permissions, and get the key:
|
||||
```bash
|
||||
gcloud iam service-accounts create pulumi-svc
|
||||
sleep 10
|
||||
gcloud projects add-iam-policy-binding mcp-registry-prod --member="serviceAccount:pulumi-svc@mcp-registry-prod.iam.gserviceaccount.com" --role="roles/container.admin"
|
||||
gcloud projects add-iam-policy-binding mcp-registry-prod --member="serviceAccount:pulumi-svc@mcp-registry-prod.iam.gserviceaccount.com" --role="roles/compute.admin"
|
||||
gcloud projects add-iam-policy-binding mcp-registry-prod --member="serviceAccount:pulumi-svc@mcp-registry-prod.iam.gserviceaccount.com" --role="roles/storage.admin"
|
||||
gcloud iam service-accounts add-iam-policy-binding $(gcloud projects describe mcp-registry-prod --format="value(projectNumber)")-compute@developer.gserviceaccount.com --member="serviceAccount:pulumi-svc@mcp-registry-prod.iam.gserviceaccount.com" --role="roles/iam.serviceAccountUser"
|
||||
gcloud iam service-accounts keys create sa-key.json --iam-account=pulumi-svc@mcp-registry-prod.iam.gserviceaccount.com
|
||||
```
|
||||
5. Create a GCS bucket for Pulumi state: `gsutil mb gs://mcp-registry-prod-pulumi-state`
|
||||
6. Set Pulumi's backend to GCS: `pulumi login gs://mcp-registry-prod-pulumi-state`
|
||||
7. Get the passphrase file `passphrase.prod.txt` from @domdomegg
|
||||
- TODO: avoid dependence on one person! Probably will shift all of this into CI.
|
||||
8. Init the GCP stack: `PULUMI_CONFIG_PASSPHRASE_FILE=passphrase.prod.txt pulumi stack init gcpProd`
|
||||
9. Set the GCP credentials in Pulumi config:
|
||||
```bash
|
||||
# Base64 encode the service account key and set it
|
||||
pulumi config set --secret gcp:credentials "$(base64 < sa-key.json)"
|
||||
```
|
||||
10. Deploy: `make prod-up`
|
||||
11. Access the repository via the ingress load balancer. You can find its external IP with: `kubectl get svc ingress-nginx-controller -n ingress-nginx`.
|
||||
Then run `curl -H "Host: prod.registry.modelcontextprotocol.io" -k https://<EXTERNAL-IP>/v0/ping` to check that the service is up.
|
||||
|
||||
<!--
|
||||
|
||||
### Production Deployment (Azure)
|
||||
|
||||
**Note:** This is how the production deployment will be set up once. But then the plan will be future updates are effectively a login + `pulumi up` from GitHub Actions.
|
||||
|
||||
|
|
@ -45,10 +81,12 @@ Pre-requisites:
|
|||
5. Add the 'Storage Blob Data Contributor' role assignment for yourself on the storage account: `az role assignment create --assignee $(az ad signed-in-user show --query id -o tsv) --role "Storage Blob Data Contributor" --scope "/subscriptions/$(az account show --query id -o tsv)/resourceGroups/official-mcp-registry-prod"`
|
||||
6. Create a container: `az storage container create --name pulumi-state --account-name officialmcpregistryprod`
|
||||
7. Set Pulumi's backend to Azure: `pulumi login 'azblob://pulumi-state?storage_account=officialmcpregistryprod'`
|
||||
8. Init the production stack: `pulumi stack init prod`
|
||||
8. Init the production stack: `pulumi stack init aksProd`
|
||||
- TODO: This has a password that maybe needs to be shared with select contributors?
|
||||
9. Deploy: `go build && PULUMI_CONFIG_PASSPHRASE="" pulumi up --yes`
|
||||
10. Access the repository via the ingress load balancer. You can find its external IP with `kubectl get svc nginx-ingress-ingress-nginx-controller -n ingress-nginx` or view it in the Pulumi outputs. Then run `curl -H "Host: mcp-registry-prod.example.com" -k https://<EXTERNAL-IP>/v0/ping` to check that the service is up.
|
||||
10. Access the repository via the ingress load balancer. You can find its external IP with `kubectl get svc ingress-nginx-controller -n ingress-nginx` or view it in the Pulumi outputs. Then run `curl -H "Host: prod.registry.modelcontextprotocol.io" -k https://<EXTERNAL-IP>/v0/ping` to check that the service is up.
|
||||
|
||||
-->
|
||||
|
||||
## Structure
|
||||
|
||||
|
|
@ -56,7 +94,8 @@ Pre-requisites:
|
|||
├── main.go # Pulumi program entry point
|
||||
├── Pulumi.yaml # Project configuration
|
||||
├── Pulumi.local.yaml # Local stack configuration
|
||||
├── Pulumi.prod.yaml # Production stack configuration
|
||||
├── Pulumi.prod.yaml # Production stack configuration (Azure)
|
||||
├── Pulumi.gcp.yaml # Production stack configuration (GCP)
|
||||
├── Makefile # Build and deployment targets
|
||||
├── go.mod # Go module dependencies
|
||||
├── go.sum # Go module checksums
|
||||
|
|
@ -70,6 +109,7 @@ Pre-requisites:
|
|||
└── providers/ # Kubernetes cluster providers
|
||||
├── types.go # Provider interface definitions
|
||||
├── aks/ # Azure Kubernetes Service provider
|
||||
├── gcp/ # Google Kubernetes Engine provider
|
||||
└── local/ # Local kubeconfig provider
|
||||
```
|
||||
|
||||
|
|
@ -91,9 +131,11 @@ Pre-requisites:
|
|||
| Parameter | Description | Required |
|
||||
|-----------|-------------|----------|
|
||||
| `environment` | Deployment environment (dev/prod) | Yes |
|
||||
| `provider` | Kubernetes provider (local/aks) | No (default: local) |
|
||||
| `provider` | Kubernetes provider (local/aks/gcp) | No (default: local) |
|
||||
| `githubClientId` | GitHub OAuth Client ID | Yes |
|
||||
| `githubClientSecret` | GitHub OAuth Client Secret | Yes |
|
||||
| `gcpProjectId` | GCP Project ID (required when provider=gcp) | No |
|
||||
| `gcpRegion` | GCP Region (default: us-central1) | No |
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
|
|
|||
|
|
@ -7,8 +7,9 @@ toolchain go1.24.1
|
|||
require (
|
||||
github.com/pulumi/pulumi-azure-native-sdk/containerservice v1.104.0
|
||||
github.com/pulumi/pulumi-azure-native-sdk/resources v1.104.0
|
||||
github.com/pulumi/pulumi-gcp/sdk/v8 v8.39.0
|
||||
github.com/pulumi/pulumi-kubernetes/sdk/v4 v4.18.2
|
||||
github.com/pulumi/pulumi/sdk/v3 v3.158.0
|
||||
github.com/pulumi/pulumi/sdk/v3 v3.175.0
|
||||
)
|
||||
|
||||
require (
|
||||
|
|
@ -19,6 +20,7 @@ require (
|
|||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
|
||||
github.com/agext/levenshtein v1.2.3 // indirect
|
||||
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
|
||||
github.com/atotto/clipboard v0.1.4 // indirect
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||
github.com/blang/semver v3.5.1+incompatible // indirect
|
||||
|
|
@ -35,13 +37,13 @@ require (
|
|||
github.com/go-git/go-billy/v5 v5.6.1 // indirect
|
||||
github.com/go-git/go-git/v5 v5.13.1 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/glog v1.2.4 // indirect
|
||||
github.com/golang/glog v1.2.5 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hashicorp/hcl/v2 v2.17.0 // indirect
|
||||
github.com/hashicorp/hcl/v2 v2.22.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
||||
|
|
@ -62,7 +64,7 @@ require (
|
|||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pkg/term v1.1.0 // indirect
|
||||
github.com/pulumi/appdash v0.0.0-20231130102222-75f619a67231 // indirect
|
||||
github.com/pulumi/esc v0.10.0 // indirect
|
||||
github.com/pulumi/esc v0.14.2 // indirect
|
||||
github.com/pulumi/pulumi-azure-native-sdk v1.104.0 // indirect
|
||||
github.com/rivo/uniseg v0.4.4 // indirect
|
||||
github.com/rogpeppe/go-internal v1.12.0 // indirect
|
||||
|
|
@ -80,18 +82,18 @@ require (
|
|||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||
github.com/zclconf/go-cty v1.13.2 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
golang.org/x/crypto v0.33.0 // indirect
|
||||
golang.org/x/crypto v0.37.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
|
||||
golang.org/x/mod v0.19.0 // indirect
|
||||
golang.org/x/net v0.35.0 // indirect
|
||||
golang.org/x/sync v0.11.0 // indirect
|
||||
golang.org/x/sys v0.30.0 // indirect
|
||||
golang.org/x/term v0.29.0 // indirect
|
||||
golang.org/x/text v0.22.0 // indirect
|
||||
golang.org/x/net v0.39.0 // indirect
|
||||
golang.org/x/sync v0.13.0 // indirect
|
||||
golang.org/x/sys v0.32.0 // indirect
|
||||
golang.org/x/term v0.31.0 // indirect
|
||||
golang.org/x/text v0.24.0 // indirect
|
||||
golang.org/x/tools v0.23.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240311173647-c811ad7063a7 // indirect
|
||||
google.golang.org/grpc v1.63.2 // indirect
|
||||
google.golang.org/protobuf v1.33.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect
|
||||
google.golang.org/grpc v1.67.1 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
lukechampine.com/frand v1.4.2 // indirect
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFI
|
|||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
||||
github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw=
|
||||
github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY=
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
|
||||
|
|
@ -65,12 +67,10 @@ github.com/go-git/go-git/v5 v5.13.1/go.mod h1:qryJB4cSBoq3FRoBRf5A77joojuBcmPJ0q
|
|||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/glog v1.2.4 h1:CNNw5U8lSiiBk7druxtSHHTsRWcxKoac6kZKm2peBBc=
|
||||
github.com/golang/glog v1.2.4/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
|
||||
github.com/golang/glog v1.2.5 h1:DrW6hGnjIhtvhOIiAKT6Psh/Kd/ldepEa81DKeiRJ5I=
|
||||
github.com/golang/glog v1.2.5/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
|
|
@ -82,8 +82,8 @@ github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY
|
|||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/hashicorp/hcl/v2 v2.17.0 h1:z1XvSUyXd1HP10U4lrLg5e0JMVz6CPaJvAgxM0KNZVY=
|
||||
github.com/hashicorp/hcl/v2 v2.17.0/go.mod h1:gJyW2PTShkJqQBKpAmPO3yxMxIuoXkOF2TpqXzrQyx4=
|
||||
github.com/hashicorp/hcl/v2 v2.22.0 h1:hkZ3nCtqeJsDhPRFz5EA9iwcG1hNWGePOTw6oyul12M=
|
||||
github.com/hashicorp/hcl/v2 v2.22.0/go.mod h1:62ZYHrXgPoX8xBnzl8QzbWq4dyDsDtfCRgIq1rbJEvA=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||
|
|
@ -146,18 +146,20 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
|||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pulumi/appdash v0.0.0-20231130102222-75f619a67231 h1:vkHw5I/plNdTr435cARxCW6q9gc0S/Yxz7Mkd38pOb0=
|
||||
github.com/pulumi/appdash v0.0.0-20231130102222-75f619a67231/go.mod h1:murToZ2N9hNJzewjHBgfFdXhZKjY3z5cYC1VXk+lbFE=
|
||||
github.com/pulumi/esc v0.10.0 h1:jzBKzkLVW0mePeanDRfqSQoCJ5yrkux0jIwAkUxpRKE=
|
||||
github.com/pulumi/esc v0.10.0/go.mod h1:2Bfa+FWj/xl8CKqRTWbWgDX0SOD4opdQgvYSURTGK2c=
|
||||
github.com/pulumi/esc v0.14.2 h1:xHpjJXzKs1hk/QPpgwe1Rmif3VWA0QcZ7jDvTFYX/jM=
|
||||
github.com/pulumi/esc v0.14.2/go.mod h1:0dNzCWIiRUmdfFrhHdeBzU4GiDPBhSfpeWDNApZwZ08=
|
||||
github.com/pulumi/pulumi-azure-native-sdk v1.104.0 h1:vyD4PvKSOkwL1z9WTis3ZE9XC73UM/7AyMNek4Vm1+E=
|
||||
github.com/pulumi/pulumi-azure-native-sdk v1.104.0/go.mod h1:ZfkbJPR8poiJgy4IlNaa2NBjHLW37nsLY2BIbZp3lHc=
|
||||
github.com/pulumi/pulumi-azure-native-sdk/containerservice v1.104.0 h1:grLVzWH6pS5os8ZfAbEkdEbaF4BFoLMwDai9ZsOINqo=
|
||||
github.com/pulumi/pulumi-azure-native-sdk/containerservice v1.104.0/go.mod h1:QRMkKXRX3suaDR13VgN9jEkPl66YI/MZOyu0hxZzwuk=
|
||||
github.com/pulumi/pulumi-azure-native-sdk/resources v1.104.0 h1:oaqgOMuGswJooAyFFWkSn9r/m1IBVBxbEL7LIXgTjqI=
|
||||
github.com/pulumi/pulumi-azure-native-sdk/resources v1.104.0/go.mod h1:CTbJkLYp5Foi5ccHeDfowJ+lpeX9ciaz16VeIVBhqng=
|
||||
github.com/pulumi/pulumi-gcp/sdk/v8 v8.39.0 h1:3i6MPUPGkltfOkl8UphrYne0D4h6RcJ2oKBP0eqGLS8=
|
||||
github.com/pulumi/pulumi-gcp/sdk/v8 v8.39.0/go.mod h1:MJPBwFykzyl5Lp70PP3Ds32y5XrWOPvNuDcfpdXPY8o=
|
||||
github.com/pulumi/pulumi-kubernetes/sdk/v4 v4.18.2 h1:WKxxqw+94H4KhBWKRN79G9IhmBZewj8sPYQJclgHJx0=
|
||||
github.com/pulumi/pulumi-kubernetes/sdk/v4 v4.18.2/go.mod h1:9dBA6+rtpKmyZB3k1XryUOHDOuNdoTODFKEEZZCtrz8=
|
||||
github.com/pulumi/pulumi/sdk/v3 v3.158.0 h1:4N2WN1fLQiVCulLH+6O+RMUxsqVwb+2ybLV/1FuyeVU=
|
||||
github.com/pulumi/pulumi/sdk/v3 v3.158.0/go.mod h1:YEbbl0N7eVsgfsL7h5215dDf8GBSe4AnRon7Ya/KIVc=
|
||||
github.com/pulumi/pulumi/sdk/v3 v3.175.0 h1:vANFQOpWjJFCuVBim7AfOM3hHnNHgjJ8NlVwG1qezd4=
|
||||
github.com/pulumi/pulumi/sdk/v3 v3.175.0/go.mod h1:AD2BrIxFG4wdCLCFODrOasXhURwrD/8hHrwBcjzyU9Y=
|
||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
|
||||
|
|
@ -208,8 +210,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
|||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
||||
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
|
|
@ -224,13 +226,13 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL
|
|||
golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
||||
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
||||
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
||||
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
||||
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
|
||||
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
|
@ -246,16 +248,16 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
|
||||
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
|
||||
golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
|
||||
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
|
|
@ -268,12 +270,12 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
|
|||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240311173647-c811ad7063a7 h1:8EeVk1VKMD+GD/neyEHGmz7pFblqPjHoi+PGQIlLx2s=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240311173647-c811ad7063a7/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
|
||||
google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
|
||||
google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 h1:e7S5W7MGGLaSu8j3YjdezkZ+m1/Nm0uRVRMEMGk26Xs=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
|
||||
google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E=
|
||||
google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/modelcontextprotocol/registry/deploy/infra/pkg/k8s"
|
||||
"github.com/modelcontextprotocol/registry/deploy/infra/pkg/providers"
|
||||
"github.com/modelcontextprotocol/registry/deploy/infra/pkg/providers/aks"
|
||||
"github.com/modelcontextprotocol/registry/deploy/infra/pkg/providers/gcp"
|
||||
"github.com/modelcontextprotocol/registry/deploy/infra/pkg/providers/local"
|
||||
)
|
||||
|
||||
|
|
@ -23,6 +24,8 @@ func createProvider(ctx *pulumi.Context) (providers.ClusterProvider, error) {
|
|||
switch providerName {
|
||||
case "aks":
|
||||
return &aks.Provider{}, nil
|
||||
case "gcp":
|
||||
return &gcp.Provider{}, nil
|
||||
case "local":
|
||||
return &local.Provider{}, nil
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
package k8s
|
||||
|
||||
import (
|
||||
"github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/helm/v3"
|
||||
"github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes"
|
||||
"github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/apiextensions"
|
||||
v1 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/core/v1"
|
||||
"github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/helm/v3"
|
||||
metav1 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/meta/v1"
|
||||
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
|
||||
|
||||
|
|
@ -41,5 +43,36 @@ func SetupCertManager(ctx *pulumi.Context, cluster *providers.ProviderInfo) erro
|
|||
return err
|
||||
}
|
||||
|
||||
_, err = apiextensions.NewCustomResource(ctx, "letsencrypt-prod", &apiextensions.CustomResourceArgs{
|
||||
ApiVersion: pulumi.String("cert-manager.io/v1"),
|
||||
Kind: pulumi.String("ClusterIssuer"),
|
||||
Metadata: &metav1.ObjectMetaArgs{
|
||||
Name: pulumi.String("letsencrypt-prod"),
|
||||
},
|
||||
OtherFields: kubernetes.UntypedArgs{
|
||||
"spec": pulumi.Map{
|
||||
"acme": pulumi.Map{
|
||||
"server": pulumi.String("https://acme-v02.api.letsencrypt.org/directory"),
|
||||
"email": pulumi.String("admin@modelcontextprotocol.io"),
|
||||
"privateKeySecretRef": pulumi.Map{
|
||||
"name": pulumi.String("letsencrypt-prod-key"),
|
||||
},
|
||||
"solvers": pulumi.Array{
|
||||
pulumi.Map{
|
||||
"http01": pulumi.Map{
|
||||
"ingress": pulumi.Map{
|
||||
"ingressClassName": pulumi.String("nginx"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, pulumi.Provider(cluster.Provider))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ func SetupIngressController(ctx *pulumi.Context, cluster *providers.ProviderInfo
|
|||
ingressType = "NodePort"
|
||||
}
|
||||
|
||||
nginxIngress, err := helm.NewChart(ctx, "nginx-ingress", helm.ChartArgs{
|
||||
nginxIngress, err := helm.NewChart(ctx, "ingress-nginx", helm.ChartArgs{
|
||||
Chart: pulumi.String("ingress-nginx"),
|
||||
Version: pulumi.String("4.13.0"),
|
||||
FetchArgs: helm.FetchArgs{
|
||||
|
|
@ -45,8 +45,8 @@ func SetupIngressController(ctx *pulumi.Context, cluster *providers.ProviderInfo
|
|||
"controller": pulumi.Map{
|
||||
"service": pulumi.Map{
|
||||
"type": pulumi.String(ingressType),
|
||||
// Add Azure Load Balancer health probe annotation as otherwise it defaults to / which fails
|
||||
"annotations": pulumi.Map{
|
||||
// Add Azure Load Balancer health probe annotation as otherwise it defaults to / which fails
|
||||
"service.beta.kubernetes.io/azure-load-balancer-health-probe-request-path": pulumi.String("/healthz"),
|
||||
},
|
||||
},
|
||||
|
|
@ -75,8 +75,8 @@ func SetupIngressController(ctx *pulumi.Context, cluster *providers.ProviderInfo
|
|||
// Look up the service after the chart is ready
|
||||
svc, err := v1.GetService(
|
||||
ctx,
|
||||
"nginx-ingress-controller-lookup",
|
||||
pulumi.ID("ingress-nginx/nginx-ingress-ingress-nginx-controller"),
|
||||
"ingress-nginx-controller-lookup",
|
||||
pulumi.ID("ingress-nginx/ingress-nginx-controller"),
|
||||
&v1.ServiceState{},
|
||||
pulumi.Provider(cluster.Provider),
|
||||
pulumi.DependsOn([]pulumi.Resource{nginxIngress}),
|
||||
|
|
|
|||
|
|
@ -62,43 +62,25 @@ func DeployMCPRegistry(ctx *pulumi.Context, cluster *providers.ProviderInfo, env
|
|||
Spec: &corev1.PodSpecArgs{
|
||||
Containers: corev1.ContainerArray{
|
||||
&corev1.ContainerArgs{
|
||||
Name: pulumi.String("mcp-registry"),
|
||||
// TODO: Replace with actual MCP registry image once on GHCR
|
||||
Image: pulumi.String("nginx:alpine"),
|
||||
Name: pulumi.String("mcp-registry"),
|
||||
Image: pulumi.String("ghcr.io/modelcontextprotocol/registry:latest"),
|
||||
Ports: corev1.ContainerPortArray{
|
||||
&corev1.ContainerPortArgs{
|
||||
// TODO: Change to port 8080 once using registry image
|
||||
ContainerPort: pulumi.Int(80),
|
||||
ContainerPort: pulumi.Int(8080),
|
||||
Name: pulumi.String("http"),
|
||||
},
|
||||
},
|
||||
Env: corev1.EnvVarArray{
|
||||
&corev1.EnvVarArgs{
|
||||
Name: pulumi.String("DATABASE_URL"),
|
||||
Name: pulumi.String("MCP_REGISTRY_DATABASE_URL"),
|
||||
Value: pulumi.String("mongodb://mongodb.default.svc.cluster.local:27017"),
|
||||
},
|
||||
&corev1.EnvVarArgs{
|
||||
Name: pulumi.String("PORT"),
|
||||
Value: pulumi.String("8080"),
|
||||
},
|
||||
&corev1.EnvVarArgs{
|
||||
Name: pulumi.String("NODE_ENV"),
|
||||
Value: pulumi.String("production"),
|
||||
},
|
||||
&corev1.EnvVarArgs{
|
||||
Name: pulumi.String("LOG_LEVEL"),
|
||||
Value: pulumi.String("info"),
|
||||
},
|
||||
&corev1.EnvVarArgs{
|
||||
Name: pulumi.String("CORS_ORIGINS"),
|
||||
Value: pulumi.String("*"),
|
||||
},
|
||||
&corev1.EnvVarArgs{
|
||||
Name: pulumi.String("GITHUB_CLIENT_ID"),
|
||||
Name: pulumi.String("MCP_REGISTRY_GITHUB_CLIENT_ID"),
|
||||
Value: pulumi.String(githubClientId),
|
||||
},
|
||||
&corev1.EnvVarArgs{
|
||||
Name: pulumi.String("GITHUB_CLIENT_SECRET"),
|
||||
Name: pulumi.String("MCP_REGISTRY_GITHUB_CLIENT_SECRET"),
|
||||
ValueFrom: &corev1.EnvVarSourceArgs{
|
||||
SecretKeyRef: &corev1.SecretKeySelectorArgs{
|
||||
Name: secret.Metadata.Name(),
|
||||
|
|
@ -107,23 +89,22 @@ func DeployMCPRegistry(ctx *pulumi.Context, cluster *providers.ProviderInfo, env
|
|||
},
|
||||
},
|
||||
},
|
||||
// TODO: uncomment when using registry image
|
||||
// LivenessProbe: &corev1.ProbeArgs{
|
||||
// HttpGet: &corev1.HTTPGetActionArgs{
|
||||
// Path: pulumi.String("/v0/health"),
|
||||
// Port: pulumi.Int(8080),
|
||||
// },
|
||||
// InitialDelaySeconds: pulumi.Int(30),
|
||||
// TimeoutSeconds: pulumi.Int(5),
|
||||
// },
|
||||
// ReadinessProbe: &corev1.ProbeArgs{
|
||||
// HttpGet: &corev1.HTTPGetActionArgs{
|
||||
// Path: pulumi.String("/v0/health"),
|
||||
// Port: pulumi.Int(8080),
|
||||
// },
|
||||
// InitialDelaySeconds: pulumi.Int(5),
|
||||
// TimeoutSeconds: pulumi.Int(3),
|
||||
// },
|
||||
LivenessProbe: &corev1.ProbeArgs{
|
||||
HttpGet: &corev1.HTTPGetActionArgs{
|
||||
Path: pulumi.String("/v0/health"),
|
||||
Port: pulumi.Int(8080),
|
||||
},
|
||||
InitialDelaySeconds: pulumi.Int(30),
|
||||
TimeoutSeconds: pulumi.Int(5),
|
||||
},
|
||||
ReadinessProbe: &corev1.ProbeArgs{
|
||||
HttpGet: &corev1.HTTPGetActionArgs{
|
||||
Path: pulumi.String("/v0/health"),
|
||||
Port: pulumi.Int(8080),
|
||||
},
|
||||
InitialDelaySeconds: pulumi.Int(5),
|
||||
TimeoutSeconds: pulumi.Int(3),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -150,9 +131,8 @@ func DeployMCPRegistry(ctx *pulumi.Context, cluster *providers.ProviderInfo, env
|
|||
},
|
||||
Ports: corev1.ServicePortArray{
|
||||
&corev1.ServicePortArgs{
|
||||
Port: pulumi.Int(80),
|
||||
// TODO: Change to port 8080 once using registry image
|
||||
TargetPort: pulumi.Int(80),
|
||||
Port: pulumi.Int(80),
|
||||
TargetPort: pulumi.Int(8080),
|
||||
Name: pulumi.String("http"),
|
||||
},
|
||||
},
|
||||
|
|
@ -164,6 +144,7 @@ func DeployMCPRegistry(ctx *pulumi.Context, cluster *providers.ProviderInfo, env
|
|||
}
|
||||
|
||||
// Create Ingress
|
||||
domain := "%s.registry.modelcontextprotocol.io"
|
||||
ingress, err := networkingv1.NewIngress(ctx, "mcp-registry", &networkingv1.IngressArgs{
|
||||
Metadata: &metav1.ObjectMetaArgs{
|
||||
Name: pulumi.String("mcp-registry"),
|
||||
|
|
@ -173,23 +154,21 @@ func DeployMCPRegistry(ctx *pulumi.Context, cluster *providers.ProviderInfo, env
|
|||
"environment": pulumi.String(environment),
|
||||
},
|
||||
Annotations: pulumi.StringMap{
|
||||
"kubernetes.io/ingress.class": pulumi.String("nginx"),
|
||||
"cert-manager.io/cluster-issuer": pulumi.String("letsencrypt-prod"),
|
||||
"nginx.ingress.kubernetes.io/rewrite-target": pulumi.String("/"),
|
||||
"kubernetes.io/ingress.class": pulumi.String("nginx"),
|
||||
},
|
||||
},
|
||||
Spec: &networkingv1.IngressSpecArgs{
|
||||
Tls: networkingv1.IngressTLSArray{
|
||||
&networkingv1.IngressTLSArgs{
|
||||
Hosts: pulumi.StringArray{
|
||||
pulumi.Sprintf("mcp-registry-%s.example.com", environment),
|
||||
pulumi.Sprintf(domain, environment),
|
||||
},
|
||||
SecretName: pulumi.Sprintf("mcp-registry-%s-tls", environment),
|
||||
},
|
||||
},
|
||||
Rules: networkingv1.IngressRuleArray{
|
||||
&networkingv1.IngressRuleArgs{
|
||||
Host: pulumi.Sprintf("mcp-registry-%s.example.com", environment),
|
||||
Host: pulumi.Sprintf(domain, environment),
|
||||
Http: &networkingv1.HTTPIngressRuleValueArgs{
|
||||
Paths: networkingv1.HTTPIngressPathArray{
|
||||
&networkingv1.HTTPIngressPathArgs{
|
||||
|
|
|
|||
182
deploy/pkg/providers/gcp/provider.go
Normal file
182
deploy/pkg/providers/gcp/provider.go
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
package gcp
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
|
||||
"github.com/pulumi/pulumi-gcp/sdk/v8/go/gcp"
|
||||
"github.com/pulumi/pulumi-gcp/sdk/v8/go/gcp/container"
|
||||
"github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes"
|
||||
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
|
||||
"github.com/pulumi/pulumi/sdk/v3/go/pulumi/config"
|
||||
|
||||
"github.com/modelcontextprotocol/registry/deploy/infra/pkg/providers"
|
||||
)
|
||||
|
||||
// Provider implements the ClusterProvider interface for Google Kubernetes Engine
|
||||
type Provider struct{}
|
||||
|
||||
// CreateCluster creates a Google Kubernetes Engine cluster
|
||||
func (p *Provider) CreateCluster(ctx *pulumi.Context, environment string) (*providers.ProviderInfo, error) {
|
||||
// Get configuration
|
||||
gcpConf := config.New(ctx, "gcp")
|
||||
|
||||
// Get project ID from config or use default
|
||||
projectID := gcpConf.Get("project")
|
||||
if projectID == "" {
|
||||
return nil, fmt.Errorf("GCP project ID not configured. Set gcp:project")
|
||||
}
|
||||
|
||||
// Get region from config or use default
|
||||
region := gcpConf.Get("region")
|
||||
if region == "" {
|
||||
region = "us-central1"
|
||||
}
|
||||
|
||||
// Get credentials from config (base64 encoded service account JSON)
|
||||
credentials := gcpConf.Get("credentials")
|
||||
if credentials != "" {
|
||||
// Decode the base64 credentials
|
||||
decodedCreds, err := base64.StdEncoding.DecodeString(credentials)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode GCP credentials: %w", err)
|
||||
}
|
||||
credentials = string(decodedCreds)
|
||||
}
|
||||
|
||||
// Create a GCP provider with explicit credentials if provided
|
||||
var gcpProvider *gcp.Provider
|
||||
var err error
|
||||
if credentials != "" {
|
||||
gcpProvider, err = gcp.NewProvider(ctx, "gcp-explicit", &gcp.ProviderArgs{
|
||||
Project: pulumi.String(projectID),
|
||||
Region: pulumi.String(region),
|
||||
Credentials: pulumi.String(credentials),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create GCP provider: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Create GKE cluster
|
||||
clusterName := fmt.Sprintf("mcp-registry-%s", environment)
|
||||
|
||||
// Use a specific zone instead of region for zonal cluster
|
||||
zone := fmt.Sprintf("%s-b", region)
|
||||
|
||||
// Configure the GKE cluster
|
||||
clusterArgs := &container.ClusterArgs{
|
||||
Name: pulumi.String(clusterName),
|
||||
Location: pulumi.String(zone),
|
||||
Project: pulumi.String(projectID),
|
||||
Description: pulumi.String(fmt.Sprintf("MCP Registry %s GKE Cluster", environment)),
|
||||
|
||||
// Initial node count (will be managed by node pool)
|
||||
InitialNodeCount: pulumi.Int(1),
|
||||
|
||||
// Remove default node pool after cluster creation
|
||||
RemoveDefaultNodePool: pulumi.Bool(true),
|
||||
|
||||
AddonsConfig: &container.ClusterAddonsConfigArgs{
|
||||
// Disable as we use ingress-nginx
|
||||
HttpLoadBalancing: &container.ClusterAddonsConfigHttpLoadBalancingArgs{
|
||||
Disabled: pulumi.Bool(true),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Add provider if we have explicit credentials
|
||||
clusterOpts := []pulumi.ResourceOption{}
|
||||
if gcpProvider != nil {
|
||||
clusterOpts = append(clusterOpts, pulumi.Provider(gcpProvider))
|
||||
}
|
||||
|
||||
cluster, err := container.NewCluster(ctx, clusterName, clusterArgs, clusterOpts...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create GKE cluster: %w", err)
|
||||
}
|
||||
|
||||
// Create a managed node pool for the cluster
|
||||
nodePoolName := fmt.Sprintf("%s-nodepool", clusterName)
|
||||
nodePoolArgs := &container.NodePoolArgs{
|
||||
Cluster: cluster.Name,
|
||||
Location: pulumi.String(zone),
|
||||
Project: pulumi.String(projectID),
|
||||
|
||||
// Node pool configuration
|
||||
NodeCount: pulumi.Int(2),
|
||||
NodeConfig: &container.NodePoolNodeConfigArgs{
|
||||
MachineType: pulumi.String("e2-small"),
|
||||
DiskSizeGb: pulumi.Int(20),
|
||||
DiskType: pulumi.String("pd-standard"),
|
||||
},
|
||||
|
||||
// Node management configuration
|
||||
Management: &container.NodePoolManagementArgs{
|
||||
AutoRepair: pulumi.Bool(true),
|
||||
AutoUpgrade: pulumi.Bool(true),
|
||||
},
|
||||
}
|
||||
|
||||
// Add provider if we have explicit credentials
|
||||
nodePoolOpts := []pulumi.ResourceOption{}
|
||||
if gcpProvider != nil {
|
||||
nodePoolOpts = append(nodePoolOpts, pulumi.Provider(gcpProvider))
|
||||
}
|
||||
|
||||
nodePool, err := container.NewNodePool(ctx, nodePoolName, nodePoolArgs, nodePoolOpts...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create node pool: %w", err)
|
||||
}
|
||||
|
||||
// Create Kubernetes provider using the cluster directly
|
||||
k8sProvider, err := kubernetes.NewProvider(ctx, "k8s-provider", &kubernetes.ProviderArgs{
|
||||
Kubeconfig: pulumi.All(cluster.Endpoint, cluster.MasterAuth).ApplyT(func(args []any) (string, error) {
|
||||
endpoint := args[0].(string)
|
||||
masterAuth := args[1].(container.ClusterMasterAuth)
|
||||
context := fmt.Sprintf("%s_%s_%s", projectID, zone, clusterName)
|
||||
|
||||
// Extract CA certificate
|
||||
caCert := ""
|
||||
if masterAuth.ClusterCaCertificate != nil {
|
||||
caCert = *masterAuth.ClusterCaCertificate
|
||||
}
|
||||
|
||||
// Create kubeconfig using gke-gcloud-auth-plugin
|
||||
kubeconfigYAML := fmt.Sprintf(`apiVersion: v1
|
||||
clusters:
|
||||
- cluster:
|
||||
certificate-authority-data: %s
|
||||
server: https://%s
|
||||
name: %s
|
||||
contexts:
|
||||
- context:
|
||||
cluster: %s
|
||||
user: %s
|
||||
name: %s
|
||||
current-context: %s
|
||||
kind: Config
|
||||
preferences: {}
|
||||
users:
|
||||
- name: %s
|
||||
user:
|
||||
exec:
|
||||
apiVersion: client.authentication.k8s.io/v1beta1
|
||||
command: gke-gcloud-auth-plugin
|
||||
installHint: Install gke-gcloud-auth-plugin for use with kubectl by following
|
||||
https://cloud.google.com/blog/products/containers-kubernetes/kubectl-auth-changes-in-gke
|
||||
provideClusterInfo: true
|
||||
`, caCert, endpoint, context, context, context, context, context, context)
|
||||
|
||||
return kubeconfigYAML, nil
|
||||
}).(pulumi.StringOutput),
|
||||
}, pulumi.DependsOn([]pulumi.Resource{cluster, nodePool}))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create Kubernetes provider: %w", err)
|
||||
}
|
||||
|
||||
return &providers.ProviderInfo{
|
||||
Name: nodePool.Cluster,
|
||||
Provider: k8sProvider,
|
||||
}, nil
|
||||
}
|
||||
Loading…
Reference in a new issue