infra: Improve local development flow and avoid false positive infra diffs (#267)
This commit is contained in:
parent
01a6ce5fb2
commit
95bf1e3e30
4 changed files with 53 additions and 56 deletions
|
|
@ -12,6 +12,7 @@ build: ## Build the Pulumi Go program
|
||||||
# Local stack commands
|
# Local stack commands
|
||||||
local-login: ## Login to local Pulumi backend
|
local-login: ## Login to local Pulumi backend
|
||||||
pulumi login --local
|
pulumi login --local
|
||||||
|
PULUMI_CONFIG_PASSPHRASE="" pulumi stack select local --create
|
||||||
|
|
||||||
local-preview: build local-login ## Preview local infrastructure changes
|
local-preview: build local-login ## Preview local infrastructure changes
|
||||||
PULUMI_CONFIG_PASSPHRASE="" pulumi preview --stack local
|
PULUMI_CONFIG_PASSPHRASE="" pulumi preview --stack local
|
||||||
|
|
@ -19,6 +20,10 @@ local-preview: build local-login ## Preview local infrastructure changes
|
||||||
local-up: build local-login ## Deploy local infrastructure
|
local-up: build local-login ## Deploy local infrastructure
|
||||||
PULUMI_CONFIG_PASSPHRASE="" pulumi up --yes --stack local
|
PULUMI_CONFIG_PASSPHRASE="" pulumi up --yes --stack local
|
||||||
|
|
||||||
|
local-destroy: local-login ## Destroy local infrastructure
|
||||||
|
pulumi stack rm local --force --yes --preserve-config
|
||||||
|
echo "Make sure to also delete your k8s cluster, e.g. minikube delete"
|
||||||
|
|
||||||
# Staging stack commands
|
# Staging stack commands
|
||||||
staging-login: ## Login to staging Pulumi backend
|
staging-login: ## Login to staging Pulumi backend
|
||||||
pulumi login gs://mcp-registry-staging-pulumi-state
|
pulumi login gs://mcp-registry-staging-pulumi-state
|
||||||
|
|
|
||||||
|
|
@ -10,22 +10,22 @@ Pre-requisites:
|
||||||
- [Pulumi CLI installed](https://www.pulumi.com/docs/iac/download-install/)
|
- [Pulumi CLI installed](https://www.pulumi.com/docs/iac/download-install/)
|
||||||
- Access to a Kubernetes cluster via kubeconfig. You can run a cluster locally with [minikube](https://minikube.sigs.k8s.io/docs/start/).
|
- Access to a Kubernetes cluster via kubeconfig. You can run a cluster locally with [minikube](https://minikube.sigs.k8s.io/docs/start/).
|
||||||
|
|
||||||
1. Set Pulumi's backend to local: `pulumi login --local`
|
1. Ensure your kubeconfig is configured at the cluster you want to use. For minikube, run `minikube start && minikube tunnel`.
|
||||||
2. Init the local stack: `pulumi stack init local` (fine to leave `password` blank)
|
2. Run `make local-up` to deploy the stack. Run this again if the first attempt fails.
|
||||||
3. Set your config:
|
3. 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: local.registry.modelcontextprotocol.io" -k https://<EXTERNAL-IP>/v0/ping` to check that the service is up.
|
||||||
```bash
|
|
||||||
# General environment
|
|
||||||
pulumi config set mcp-registry:environment local
|
|
||||||
|
|
||||||
# To use your local kubeconfig (default)
|
#### To change config
|
||||||
pulumi config set mcp-registry:provider local
|
|
||||||
|
The stack is configured out of the box for local development. But if you want to make changes, run commands like:
|
||||||
# GitHub OAuth
|
|
||||||
pulumi config set mcp-registry:githubClientId <your-github-client-id>
|
```bash
|
||||||
pulumi config set --secret mcp-registry:githubClientSecret <your-github-client-secret>
|
PULUMI_CONFIG_PASSPHRASE="" pulumi config set mcp-registry:environment local
|
||||||
```
|
PULUMI_CONFIG_PASSPHRASE="" pulumi config set mcp-registry:githubClientSecret --secret <some-secret-value>
|
||||||
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.
|
|
||||||
|
#### To delete the stack
|
||||||
|
|
||||||
|
`make local-destroy` and deleting the cluster (with minikube: `minikube delete`) will reset you back to a clean state.
|
||||||
|
|
||||||
### Production Deployment (GCP)
|
### Production Deployment (GCP)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import (
|
||||||
// SetupCertManager sets up cert-manager for TLS certificates
|
// SetupCertManager sets up cert-manager for TLS certificates
|
||||||
func SetupCertManager(ctx *pulumi.Context, cluster *providers.ProviderInfo) error {
|
func SetupCertManager(ctx *pulumi.Context, cluster *providers.ProviderInfo) error {
|
||||||
// Create namespace for cert-manager
|
// Create namespace for cert-manager
|
||||||
_, err := v1.NewNamespace(ctx, "cert-manager", &v1.NamespaceArgs{
|
certManagerNamespace, err := v1.NewNamespace(ctx, "cert-manager", &v1.NamespaceArgs{
|
||||||
Metadata: &metav1.ObjectMetaArgs{
|
Metadata: &metav1.ObjectMetaArgs{
|
||||||
Name: pulumi.String("cert-manager"),
|
Name: pulumi.String("cert-manager"),
|
||||||
},
|
},
|
||||||
|
|
@ -24,13 +24,13 @@ func SetupCertManager(ctx *pulumi.Context, cluster *providers.ProviderInfo) erro
|
||||||
}
|
}
|
||||||
|
|
||||||
// Install cert-manager for TLS certificates
|
// Install cert-manager for TLS certificates
|
||||||
_, err = helm.NewChart(ctx, "cert-manager", helm.ChartArgs{
|
certManager, err := helm.NewChart(ctx, "cert-manager", helm.ChartArgs{
|
||||||
Chart: pulumi.String("cert-manager"),
|
Chart: pulumi.String("cert-manager"),
|
||||||
Version: pulumi.String("v1.18.2"),
|
Version: pulumi.String("v1.18.2"),
|
||||||
FetchArgs: helm.FetchArgs{
|
FetchArgs: helm.FetchArgs{
|
||||||
Repo: pulumi.String("https://charts.jetstack.io"),
|
Repo: pulumi.String("https://charts.jetstack.io"),
|
||||||
},
|
},
|
||||||
Namespace: pulumi.String("cert-manager"),
|
Namespace: certManagerNamespace.Metadata.Name().Elem(),
|
||||||
Values: pulumi.Map{
|
Values: pulumi.Map{
|
||||||
"installCRDs": pulumi.Bool(true),
|
"installCRDs": pulumi.Bool(true),
|
||||||
"ingressShim": pulumi.Map{
|
"ingressShim": pulumi.Map{
|
||||||
|
|
@ -69,7 +69,7 @@ func SetupCertManager(ctx *pulumi.Context, cluster *providers.ProviderInfo) erro
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, pulumi.Provider(cluster.Provider))
|
}, pulumi.Provider(cluster.Provider), pulumi.DependsOn([]pulumi.Resource{certManager}))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
package k8s
|
package k8s
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
v1 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/core/v1"
|
v1 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/core/v1"
|
||||||
"github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/helm/v3"
|
"github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/helm/v3"
|
||||||
metav1 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/meta/v1"
|
metav1 "github.com/pulumi/pulumi-kubernetes/sdk/v4/go/kubernetes/meta/v1"
|
||||||
|
|
@ -19,7 +21,7 @@ func SetupIngressController(ctx *pulumi.Context, cluster *providers.ProviderInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create namespace for ingress-nginx
|
// Create namespace for ingress-nginx
|
||||||
_, err := v1.NewNamespace(ctx, "ingress-nginx", &v1.NamespaceArgs{
|
ingressNginxNamespace, err := v1.NewNamespace(ctx, "ingress-nginx", &v1.NamespaceArgs{
|
||||||
Metadata: &metav1.ObjectMetaArgs{
|
Metadata: &metav1.ObjectMetaArgs{
|
||||||
Name: pulumi.String("ingress-nginx"),
|
Name: pulumi.String("ingress-nginx"),
|
||||||
},
|
},
|
||||||
|
|
@ -29,22 +31,17 @@ func SetupIngressController(ctx *pulumi.Context, cluster *providers.ProviderInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
// Install NGINX Ingress Controller
|
// Install NGINX Ingress Controller
|
||||||
ingressType := "LoadBalancer"
|
ingressNginx, err := helm.NewChart(ctx, "ingress-nginx", helm.ChartArgs{
|
||||||
if provider == "local" {
|
|
||||||
ingressType = "NodePort"
|
|
||||||
}
|
|
||||||
|
|
||||||
nginxIngress, err := helm.NewChart(ctx, "ingress-nginx", helm.ChartArgs{
|
|
||||||
Chart: pulumi.String("ingress-nginx"),
|
Chart: pulumi.String("ingress-nginx"),
|
||||||
Version: pulumi.String("4.13.0"),
|
Version: pulumi.String("4.13.0"),
|
||||||
FetchArgs: helm.FetchArgs{
|
FetchArgs: helm.FetchArgs{
|
||||||
Repo: pulumi.String("https://kubernetes.github.io/ingress-nginx"),
|
Repo: pulumi.String("https://kubernetes.github.io/ingress-nginx"),
|
||||||
},
|
},
|
||||||
Namespace: pulumi.String("ingress-nginx"),
|
Namespace: ingressNginxNamespace.Metadata.Name().Elem(),
|
||||||
Values: pulumi.Map{
|
Values: pulumi.Map{
|
||||||
"controller": pulumi.Map{
|
"controller": pulumi.Map{
|
||||||
"service": pulumi.Map{
|
"service": pulumi.Map{
|
||||||
"type": pulumi.String(ingressType),
|
"type": pulumi.String("LoadBalancer"),
|
||||||
"annotations": pulumi.Map{
|
"annotations": pulumi.Map{
|
||||||
// Add Azure Load Balancer health probe annotation as otherwise it defaults to / which fails
|
// 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"),
|
"service.beta.kubernetes.io/azure-load-balancer-health-probe-request-path": pulumi.String("/healthz"),
|
||||||
|
|
@ -66,35 +63,30 @@ func SetupIngressController(ctx *pulumi.Context, cluster *providers.ProviderInfo
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use the helm chart to get service information after deployment
|
// Extract ingress IPs from the Helm chart's controller service
|
||||||
ingressIps := nginxIngress.Resources.ApplyT(func(resources interface{}) interface{} {
|
ingressIps := ingressNginx.Resources.ApplyT(func(resources interface{}) interface{} {
|
||||||
if ctx.DryRun() {
|
// Look for the ingress-nginx-controller service
|
||||||
return []string{} // Return empty array on error during preview
|
resourceMap := resources.(map[string]pulumi.Resource)
|
||||||
}
|
for resourceName, resource := range resourceMap {
|
||||||
|
if strings.Contains(resourceName, "ingress-nginx-controller") &&
|
||||||
// Look up the service after the chart is ready
|
!strings.Contains(resourceName, "admission") &&
|
||||||
svc, err := v1.GetService(
|
strings.Contains(resourceName, "Service") {
|
||||||
ctx,
|
if svc, ok := resource.(*v1.Service); ok {
|
||||||
"ingress-nginx-controller-lookup",
|
// Return the LoadBalancer ingress IPs
|
||||||
pulumi.ID("ingress-nginx/ingress-nginx-controller"),
|
return svc.Status.LoadBalancer().Ingress().ApplyT(func(ingresses []v1.LoadBalancerIngress) []string {
|
||||||
&v1.ServiceState{},
|
var ips []string
|
||||||
pulumi.Provider(cluster.Provider),
|
for _, ingress := range ingresses {
|
||||||
pulumi.DependsOn([]pulumi.Resource{nginxIngress}),
|
if ip := ingress.Ip; ip != nil && *ip != "" {
|
||||||
)
|
ips = append(ips, *ip)
|
||||||
if err != nil {
|
}
|
||||||
return []string{} // Return empty array on error during preview
|
}
|
||||||
}
|
return ips
|
||||||
|
})
|
||||||
// Return the LoadBalancer ingress IPs
|
|
||||||
return svc.Status.LoadBalancer().Ingress().ApplyT(func(ingresses []v1.LoadBalancerIngress) []string {
|
|
||||||
var ips []string
|
|
||||||
for _, ingress := range ingresses {
|
|
||||||
if ip := ingress.Ip; ip != nil && *ip != "" {
|
|
||||||
ips = append(ips, *ip)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ips
|
}
|
||||||
})
|
// Return empty array if no matching service found
|
||||||
|
return []string{}
|
||||||
})
|
})
|
||||||
ctx.Export("ingressIps", ingressIps)
|
ctx.Export("ingressIps", ingressIps)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue