Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ require (
github.com/gorilla/mux v1.8.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/mabels/ipaddress/go/ipaddress v0.0.0-20211229223036-692af3b12a67 // indirect
Comment thread
mabels marked this conversation as resolved.
github.com/mailru/easyjson v0.7.7 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1037,6 +1037,8 @@ github.com/looplab/fsm v0.1.0/go.mod h1:m2VaOfDHxqXBBMgc26m6yUOwkFn8H2AlJDE+jd/u
github.com/lucas-clemente/quic-go v0.23.0/go.mod h1:paZuzjXCE5mj6sikVLMvqXk8lJV2AsqtJ6bDhjEfxx0=
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
github.com/mabels/ipaddress/go/ipaddress v0.0.0-20211229223036-692af3b12a67 h1:bVDE1M+1x2gmAsV2pdNa/9X4z5K2IitUSfC8L19FkhE=
github.com/mabels/ipaddress/go/ipaddress v0.0.0-20211229223036-692af3b12a67/go.mod h1:1mdRW3hfT/mDTtpLkQ8y0Ez5WHZvH9wx9YMw9zq9OLQ=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
Expand Down
4 changes: 2 additions & 2 deletions internal/controllers/ingressroute.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ type IngressRouteReconciler struct {
func NewIngressRouteReconciler(
client client.Client, logger *zap.Logger, config configv1.Config,
) (IngressRouteReconciler, error) {
integrations, err := integrationsFromConfig(config, client)
integrations, err := integrationsFromConfig(config, client, logger)
if err != nil {
return IngressRouteReconciler{}, fmt.Errorf("failed to initialize integrations: %s", err)
}
Expand Down Expand Up @@ -68,7 +68,7 @@ func (r *IngressRouteReconciler) Reconcile(
Hosts: switchboard.NewHostCollection().
WithTLSHostsIfAvailable(ingressRoute.Spec.TLS).
WithRouteHostsIfRequired(ingressRoute.Spec.Routes).
Hosts(),
Hosts(ingressRoute.ObjectMeta.Annotations),
TLSSecretName: ext.AndThen(ingressRoute.Spec.TLS, func(tls traefik.TLS) string {
return tls.SecretName
}),
Expand Down
46 changes: 44 additions & 2 deletions internal/controllers/ingressroute_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package controllers
import (
"context"
"fmt"
"strings"
"testing"

configv1 "github.com/borchero/switchboard/internal/config/v1"
Expand Down Expand Up @@ -157,13 +158,50 @@ func TestIngressMultipleRules(t *testing.T) {
})
}

func TestIngressTargetAnnotation(t *testing.T) {
targets := endpoint.Targets{"3.3.3.3", "4.4.4.4"}
runTest(t, testCase{
Targets: &targets,
Ingress: traefik.IngressRoute{
ObjectMeta: metav1.ObjectMeta{
Name: "my-ingress",
Annotations: map[string]string{
"switchboard.borchero.com/target": strings.Join(targets, ","),
},
},
Spec: traefik.IngressRouteSpec{
Routes: []traefik.Route{{
Kind: "Rule",
Match: "Host(`example.com`)",
Services: []traefik.Service{{
LoadBalancerSpec: traefik.LoadBalancerSpec{
Name: "nginx",
},
}},
}},
TLS: &traefik.TLS{
SecretName: "www-tls-certificate",
Domains: []traefiktypes.Domain{{
Main: "example.net",
SANs: []string{
"*.example.net",
},
}},
},
},
},
DNSNames: []string{"example.net", "*.example.net"},
})
}

//-------------------------------------------------------------------------------------------------
// TESTING UTILITIES
//-------------------------------------------------------------------------------------------------

type testCase struct {
Ingress traefik.IngressRoute
DNSNames []string
Targets *endpoint.Targets
}

func runTest(t *testing.T, test testCase) {
Expand Down Expand Up @@ -220,8 +258,12 @@ func runTest(t *testing.T, test testCase) {
assert.Nil(t, err)
assert.Len(t, endpoint.Spec.Endpoints, len(test.DNSNames))
for _, ep := range endpoint.Spec.Endpoints {
assert.Len(t, ep.Targets, 1)
assert.Equal(t, service.Spec.ClusterIP, ep.Targets[0])
if test.Targets != nil {
assert.Equal(t, *test.Targets, ep.Targets)
} else {
assert.Len(t, ep.Targets, 1)
assert.Equal(t, service.Spec.ClusterIP, ep.Targets[0])
}
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions internal/controllers/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
)

func integrationsFromConfig(
config configv1.Config, client client.Client,
config configv1.Config, client client.Client, logger *zap.Logger,
) ([]integrations.Integration, error) {
result := make([]integrations.Integration, 0)
externalDNS := config.Integrations.ExternalDNS
Expand All @@ -32,8 +32,8 @@ func integrationsFromConfig(
client, switchboard.NewServiceTarget(
externalDNS.TargetService.Name,
externalDNS.TargetService.Namespace,
),
))
logger,
)))
} else {
result = append(result, integrations.NewExternalDNS(
client, switchboard.NewStaticTarget(externalDNS.TargetIPs...),
Expand Down
14 changes: 8 additions & 6 deletions internal/controllers/utils_test.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package controllers

import (
"context"
"testing"

configv1 "github.com/borchero/switchboard/internal/config/v1"
"github.com/borchero/switchboard/internal/k8tests"
"github.com/borchero/zeus/pkg/zeus"
certmanager "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1"
cmmeta "github.com/jetstack/cert-manager/pkg/apis/meta/v1"
"github.com/stretchr/testify/assert"
Expand All @@ -18,14 +20,14 @@ func TestIntegrationsFromConfig(t *testing.T) {

// Test all configurations of integrations
config := configv1.Config{}
integrations, err := integrationsFromConfig(config, client)
integrations, err := integrationsFromConfig(config, client, zeus.Logger(context.Background()))
require.Nil(t, err)
assert.Len(t, integrations, 0)

config.Integrations.ExternalDNS = &configv1.ExternalDNSIntegrationConfig{
TargetService: &configv1.ServiceRef{Name: "my-service", Namespace: "my-namespace"},
}
integrations, err = integrationsFromConfig(config, client)
integrations, err = integrationsFromConfig(config, client, zeus.Logger(context.Background()))
require.Nil(t, err)
assert.Len(t, integrations, 1)
assert.Equal(t, "external-dns", integrations[0].Name())
Expand All @@ -41,26 +43,26 @@ func TestIntegrationsFromConfig(t *testing.T) {
},
},
}
integrations, err = integrationsFromConfig(config, client)
integrations, err = integrationsFromConfig(config, client, zeus.Logger(context.Background()))
require.Nil(t, err)
assert.Len(t, integrations, 1)
assert.Equal(t, "cert-manager", integrations[0].Name())

config.Integrations.ExternalDNS = &configv1.ExternalDNSIntegrationConfig{
TargetIPs: []string{"127.0.0.1"},
}
integrations, err = integrationsFromConfig(config, client)
integrations, err = integrationsFromConfig(config, client, zeus.Logger(context.Background()))
require.Nil(t, err)
assert.Len(t, integrations, 2)

// Must fail if external DNS is not configured correctly
config.Integrations.ExternalDNS = &configv1.ExternalDNSIntegrationConfig{}
_, err = integrationsFromConfig(config, client)
_, err = integrationsFromConfig(config, client, zeus.Logger(context.Background()))
require.NotNil(t, err)

config.Integrations.ExternalDNS = &configv1.ExternalDNSIntegrationConfig{
TargetIPs: []string{},
}
_, err = integrationsFromConfig(config, client)
_, err = integrationsFromConfig(config, client, zeus.Logger(context.Background()))
require.NotNil(t, err)
}
4 changes: 2 additions & 2 deletions internal/integrations/certmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func (c *certManager) UpdateResource(
) error {
// If the ingress does not specify a TLS secret name or specifies no hosts, no certificate
// needs to be created.
if info.TLSSecretName == nil || len(info.Hosts) == 0 {
if info.TLSSecretName == nil || len(info.Hosts.Names) == 0 {
certificate := certmanager.Certificate{ObjectMeta: c.objectMeta(owner)}
if err := k8s.DeleteIfFound(ctx, c.client, &certificate); err != nil {
return fmt.Errorf("failed to delete TLS certificate: %w", err)
Expand All @@ -62,7 +62,7 @@ func (c *certManager) UpdateResource(
// Spec
template := c.template.Spec.DeepCopy()
template.SecretName = *info.TLSSecretName
template.DNSNames = info.Hosts
template.DNSNames = info.Hosts.Names
if err := mergo.Merge(&resource.Spec, template, mergo.WithOverride); err != nil {
return fmt.Errorf("failed to reconcile specification: %s", err)
}
Expand Down
15 changes: 8 additions & 7 deletions internal/integrations/certmanager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"testing"

"github.com/borchero/switchboard/internal/k8tests"
"github.com/borchero/switchboard/internal/switchboard"
certmanager "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1"
cmmeta "github.com/jetstack/cert-manager/pkg/apis/meta/v1"
"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -47,13 +48,13 @@ func TestCertManagerUpdateResource(t *testing.T) {
require.Nil(t, err)
assert.Len(t, getCertificates(ctx, t, client, namespace), 0)

info = IngressInfo{Hosts: []string{"example.com"}}
info = IngressInfo{Hosts: switchboard.HostsTarget{Names: []string{"example.com"}}}
err = integration.UpdateResource(ctx, &owner, info)
require.Nil(t, err)
assert.Len(t, getCertificates(ctx, t, client, namespace), 0)

// If both are set, we should see a certificate created
info = IngressInfo{Hosts: []string{"example.com"}, TLSSecretName: &tlsName}
info = IngressInfo{Hosts: switchboard.HostsTarget{Names: []string{"example.com"}}, TLSSecretName: &tlsName}
err = integration.UpdateResource(ctx, &owner, info)
require.Nil(t, err)

Expand All @@ -63,16 +64,16 @@ func TestCertManagerUpdateResource(t *testing.T) {
assert.Equal(t, tlsName, certificates[0].Spec.SecretName)
assert.Equal(t, "ClusterIssuer", certificates[0].Spec.IssuerRef.Kind)
assert.Equal(t, "my-issuer", certificates[0].Spec.IssuerRef.Name)
assert.ElementsMatch(t, info.Hosts, certificates[0].Spec.DNSNames)
assert.ElementsMatch(t, info.Hosts.Names, certificates[0].Spec.DNSNames)

// We should see an update if we change any info
info.Hosts = []string{"example.com", "www.example.com"}
info.Hosts = switchboard.HostsTarget{Names: []string{"example.com", "www.example.com"}}
err = integration.UpdateResource(ctx, &owner, info)
require.Nil(t, err)
certificates = getCertificates(ctx, t, client, namespace)
assert.Len(t, certificates, 1)
assert.Equal(t, tlsName, certificates[0].Spec.SecretName)
assert.ElementsMatch(t, info.Hosts, certificates[0].Spec.DNSNames)
assert.ElementsMatch(t, info.Hosts.Names, certificates[0].Spec.DNSNames)

updatedTLSName := "new-test-tls"
info.TLSSecretName = &updatedTLSName
Expand All @@ -81,10 +82,10 @@ func TestCertManagerUpdateResource(t *testing.T) {
certificates = getCertificates(ctx, t, client, namespace)
assert.Len(t, certificates, 1)
assert.Equal(t, updatedTLSName, certificates[0].Spec.SecretName)
assert.ElementsMatch(t, info.Hosts, certificates[0].Spec.DNSNames)
assert.ElementsMatch(t, info.Hosts.Names, certificates[0].Spec.DNSNames)

// When no hosts are set, the certificate should be removed again
info.Hosts = nil
info.Hosts = switchboard.HostsTarget{}
err = integration.UpdateResource(ctx, &owner, info)
require.Nil(t, err)
assert.Len(t, getCertificates(ctx, t, client, namespace), 0)
Expand Down
6 changes: 3 additions & 3 deletions internal/integrations/externaldns.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func (e *externalDNS) UpdateResource(
) error {
// If the ingress specifies no hosts, there should be no endpoint. We try deleting it and
// ignore any error if it was not found.
if len(info.Hosts) == 0 {
if len(info.Hosts.Names) == 0 {
dnsEndpoint := endpoint.DNSEndpoint{ObjectMeta: e.objectMeta(owner)}
if err := k8s.DeleteIfFound(ctx, e.client, &dnsEndpoint); err != nil {
return fmt.Errorf("failed to delete DNS endpoint: %w", err)
Expand All @@ -61,7 +61,7 @@ func (e *externalDNS) UpdateResource(
}

// Get the IPs of the target service
targets, err := e.target.Targets(ctx, e.client)
targets, err := e.target.Targets(ctx, e.client, info.Hosts.Target)
if err != nil {
return fmt.Errorf("failed to query IP for DNS A record: %w", err)
}
Expand All @@ -75,7 +75,7 @@ func (e *externalDNS) UpdateResource(
}

// Spec
resource.Spec.Endpoints = e.endpoints(info.Hosts, targets)
resource.Spec.Endpoints = e.endpoints(info.Hosts.Names, targets)
return nil
}); err != nil {
return fmt.Errorf("failed to upsert DNS endpoint: %w", err)
Expand Down
Loading