registry/deploy/pkg/k8s/ingress.go
adam jones 03ac9c955e
Add database backup functionality with GCS integration (#297)
<!-- Provide a brief summary of your changes -->
Implements automated database backup functionality with Google Cloud
Storage integration, including retention policies and MinIO support for
local development.

## Motivation and Context
<!-- Why is this change needed? What problem does it solve? -->
This change adds critical database backup capabilities to ensure data
durability and disaster recovery. The solution provides automated
backups with configurable retention policies and supports both
production (GCS) and development (MinIO) environments.

## How Has This Been Tested?
<!-- Have you tested this in a real application? Which scenarios were
tested? -->
- Tested backup creation and restoration with MinIO in local development
- Verified GCS bucket lifecycle policies for automatic deletion after 60
days
- Tested backup retention and cleanup logic

## Breaking Changes
<!-- Will users need to update their code or configurations? -->
No breaking changes. This is an additive feature that doesn't affect
existing functionality.

## 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)
- [x] 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
- [x] 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
-->
- Implements 60-day retention period for backups as a safety net
- Uses GCS lifecycle rules for automatic cleanup
- Includes MinIO setup instructions for local development testing
- Port-forwarding commands updated for consistency across documentation
- Fixes #184
2025-08-25 17:27:40 +01:00

104 lines
3.7 KiB
Go

package k8s
import (
"strings"
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"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi/config"
"github.com/modelcontextprotocol/registry/deploy/infra/pkg/providers"
)
// SetupIngressController sets up the NGINX Ingress Controller
func SetupIngressController(ctx *pulumi.Context, cluster *providers.ProviderInfo, environment string) (*helm.Chart, error) {
conf := config.New(ctx, "mcp-registry")
provider := conf.Get("provider")
if provider == "" {
provider = "local"
}
// Create namespace for ingress-nginx
ingressNginxNamespace, err := v1.NewNamespace(ctx, "ingress-nginx", &v1.NamespaceArgs{
Metadata: &metav1.ObjectMetaArgs{
Name: pulumi.String("ingress-nginx"),
},
}, pulumi.Provider(cluster.Provider))
if err != nil {
return nil, err
}
// Usually we should expose the ingress to a LoadBalancer
// This works in GCP and most local setups e.g. minikube (with minikube tunnel)
// Kind unfortunately does not support LoadBalancer type, and hangs indefinitely. This is a workaround for that.
serviceType := cluster.Name.ApplyT(func(name string) string {
if name == "kind-kind" {
return "NodePort"
}
return "LoadBalancer"
}).(pulumi.StringOutput)
// Install NGINX Ingress Controller
ingressNginx, err := helm.NewChart(ctx, "ingress-nginx", helm.ChartArgs{
Chart: pulumi.String("ingress-nginx"),
Version: pulumi.String("4.13.0"),
FetchArgs: helm.FetchArgs{
Repo: pulumi.String("https://kubernetes.github.io/ingress-nginx"),
},
Namespace: ingressNginxNamespace.Metadata.Name().Elem(),
Values: pulumi.Map{
"controller": pulumi.Map{
"service": pulumi.Map{
"type": serviceType,
"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"),
},
},
"config": pulumi.Map{
// Disable strict path validation, to work around a bug in ingress-nginx
// https://cert-manager.io/docs/releases/release-notes/release-notes-1.18/#acme-http01-challenge-paths-now-use-pathtype-exact-in-ingress-routes
// https://github.com/kubernetes/ingress-nginx/issues/11176
"strict-validate-path-type": pulumi.String("false"),
// Use forwarded headers for proper client IP handling
"use-forwarded-headers": pulumi.String("true"),
},
},
},
}, pulumi.Provider(cluster.Provider))
if err != nil {
return nil, err
}
// Extract ingress IPs from the Helm chart's controller service
ingressIps := ingressNginx.Resources.ApplyT(func(resources interface{}) interface{} {
// Look for the ingress-nginx-controller service
resourceMap := resources.(map[string]pulumi.Resource)
for resourceName, resource := range resourceMap {
if strings.Contains(resourceName, "ingress-nginx-controller") &&
!strings.Contains(resourceName, "admission") &&
strings.Contains(resourceName, "Service") {
if svc, ok := resource.(*v1.Service); ok {
// 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)
return ingressNginx, nil
}