registry/deploy/pkg/k8s/ingress.go
adam jones ebaf4c9c99
Refactor database implementation to use PostgreSQL instead of MongoDB (#289)
Fixes #228

## Motivation and Context

See #19

## How Has This Been Tested?

- Run locally
- Run in docker compose
- Run infra in minikube
- Unit and integration tests all passing :)

## Breaking Changes

Yes: users will not be able to access any data they used to have in
custom mongodb. We are accepting this breaking change, given the
registry is in experimental development status.

## 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)
- [ ] New feature (non-breaking change which adds functionality)
- [x] 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
- [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

I have not implemented connection pooling support for simplicity, and
because cloudnative-pg already provides built-in connection pooling with
pgBouncer so if we want it later we can switch to this endpoint easily.
2025-08-22 01:40:28 +01:00

94 lines
3.3 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
}
// 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": pulumi.String("LoadBalancer"),
"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
}