Skip to content

Commit

Permalink
Adds Support for Gateway Status Addresses
Browse files Browse the repository at this point in the history
Signed-off-by: danehans <daneyonhansen@gmail.com>
  • Loading branch information
danehans committed Sep 7, 2022
1 parent 731a4db commit f8fd0fa
Show file tree
Hide file tree
Showing 17 changed files with 268 additions and 100 deletions.
7 changes: 7 additions & 0 deletions internal/envoygateway/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ import (
"github.com/envoyproxy/gateway/internal/log"
)

const (
// EnvoyGatewayNamespace is the namespace where envoy-gateway is running.
EnvoyGatewayNamespace = "envoy-gateway-system"
// EnvoyServiceName is the name of the Envoy Service.
EnvoyServiceName = "envoy"
)

// Server wraps the EnvoyGateway configuration and additional parameters
// used by Envoy Gateway server.
type Server struct {
Expand Down
4 changes: 4 additions & 0 deletions internal/gatewayapi/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ func PathMatchTypePtr(pType v1beta1.PathMatchType) *v1beta1.PathMatchType {
return &pType
}

func GatewayAddressTypePtr(addr v1beta1.AddressType) *v1beta1.AddressType {
return &addr
}

func PathMatchTypeDerefOr(matchType *v1beta1.PathMatchType, defaultType v1beta1.PathMatchType) v1beta1.PathMatchType {
if matchType != nil {
return *matchType
Expand Down
16 changes: 13 additions & 3 deletions internal/gatewayapi/runner/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package runner
import (
"context"

"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/gateway-api/apis/v1beta1"

"github.com/envoyproxy/gateway/internal/envoygateway/config"
Expand Down Expand Up @@ -72,11 +73,20 @@ func (r *Runner) subscribeAndTranslate(ctx context.Context) {
case gatewayClasses == nil:
// Envoy Gateway startup.
continue
case gatewayClasses[0] == nil:
case len(in.Gateways) == 1 && in.Gateways[0] == nil:
// No need to translate, publish empty IRs, e.g. delete operation.
r.XdsIR.Delete(r.Name())
// A nil ProxyInfra tells the Infra Manager to delete the managed proxy infra.
r.InfraIR.Store(r.Name(), &ir.Infra{Proxy: nil})
// Nil ProxyInfra listeners tells the Infra Manager to delete the managed proxy infra.
var gw types.NamespacedName
for nsName := range r.ProviderResources.Gateways.LoadAll() {
gw = nsName
}
infra := &ir.Infra{
Proxy: &ir.ProxyInfra{
Metadata: &ir.InfraMetadata{Labels: gatewayapi.GatewayOwnerLabels(gw.Namespace, gw.Name)},
},
}
r.InfraIR.Store(r.Name(), infra)
default:
// Translate and publish IRs.
t := &gatewayapi.Translator{
Expand Down
10 changes: 5 additions & 5 deletions internal/gatewayapi/translator.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ func (t *Translator) Translate(resources *Resources) *TranslateResult {
if resources != nil {
for i := range resources.Gateways {
if resources.Gateways[i] != nil {
infraIR.Proxy.Metadata.Labels = GatewayOwnerLabels(resources.Gateways[i])
infraIR.Proxy.Metadata.Labels = GatewayOwnerLabels(resources.Gateways[i].Namespace, resources.Gateways[i].Name)
}
}
}
Expand Down Expand Up @@ -821,10 +821,10 @@ func irTLSConfig(tlsSecret *v1.Secret) *ir.TLSListenerConfig {
}
}

// GatewayOwnerLabels returns owner labels for the provided Gateway.
func GatewayOwnerLabels(gw *v1beta1.Gateway) map[string]string {
// GatewayOwnerLabels returns owner labels for the provided ns/name.
func GatewayOwnerLabels(ns, name string) map[string]string {
return map[string]string{
OwningGatewayNameLabel: gw.Name,
OwningGatewayNamespaceLabel: gw.Namespace,
OwningGatewayNamespaceLabel: ns,
OwningGatewayNameLabel: name,
}
}
21 changes: 1 addition & 20 deletions internal/infrastructure/kubernetes/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ func (i *Infra) expectedDeployment(infra *ir.Infra) (*appsv1.Deployment, error)
return nil, err
}

podSelector := EnvoyPodSelector(infra.GetProxyInfra().Name)
podSelector := envoyPodSelector(infra.Proxy.Metadata.Labels)

deployment := &appsv1.Deployment{
TypeMeta: metav1.TypeMeta{
Expand Down Expand Up @@ -284,22 +284,3 @@ func (i *Infra) deleteDeployment(ctx context.Context) error {

return nil
}

// EnvoyPodSelector returns a label selector using "control-plane: envoy-gateway" as the
// key/value pair.
//
// TODO: Update k/v pair to use gatewayclass controller name to distinguish between
// multiple Envoy Gateways.
func EnvoyPodSelector(gcName string) *metav1.LabelSelector {
return &metav1.LabelSelector{
MatchLabels: envoyLabels(gcName),
}
}

// envoyLabels returns the labels used for Envoy.
func envoyLabels(gcName string) map[string]string {
return map[string]string{
"gatewayClass": gcName,
"app": "envoy",
}
}
24 changes: 0 additions & 24 deletions internal/infrastructure/kubernetes/deployment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,30 +188,6 @@ func TestCreateDeploymentIfNeeded(t *testing.T) {
}
}

func TestEnvoyPodSelector(t *testing.T) {
cases := []struct {
name string
gcName string
expected map[string]string
}{
{
name: "default",
gcName: "eg",
expected: map[string]string{
"gatewayClass": "eg",
"app": "envoy",
},
},
}

for _, tc := range cases {
t.Run("", func(t *testing.T) {
got := EnvoyPodSelector(tc.gcName)
require.Equal(t, tc.expected, got.MatchLabels)
})
}
}

func TestDeleteDeployment(t *testing.T) {
testCases := []struct {
name string
Expand Down
10 changes: 3 additions & 7 deletions internal/infrastructure/kubernetes/infra.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,11 @@ import (
corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/client"

"github.com/envoyproxy/gateway/internal/envoygateway/config"
"github.com/envoyproxy/gateway/internal/ir"
"github.com/envoyproxy/gateway/internal/utils/env"
)

const (
// envoyGatewayNamespace is the namespace where envoy-gateway is running.
envoyGatewayNamespace = "envoy-gateway-system"
)

// Infra holds all the translated Infra IR resources and provides
// the scaffolding for the managing Kubernetes infrastructure.
type Infra struct {
Expand All @@ -45,7 +41,7 @@ func NewInfra(cli client.Client) *Infra {
}

// Set the namespace used for the managed infra.
infra.Namespace = env.Lookup("ENVOY_GATEWAY_NAMESPACE", envoyGatewayNamespace)
infra.Namespace = env.Lookup("ENVOY_GATEWAY_NAMESPACE", config.EnvoyGatewayNamespace)

return infra
}
Expand Down Expand Up @@ -117,7 +113,7 @@ func (i *Infra) DeleteInfra(ctx context.Context, infra *ir.Infra) error {
return errors.New("infra ir is nil")
}

if err := i.deleteService(ctx); err != nil {
if err := i.deleteService(ctx, infra); err != nil {
return err
}

Expand Down
25 changes: 25 additions & 0 deletions internal/infrastructure/kubernetes/labels.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package kubernetes

import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// envoyPodSelector returns a label selector used to select Envoy resources.
func envoyPodSelector(gwLabels map[string]string) *metav1.LabelSelector {
return &metav1.LabelSelector{
MatchLabels: envoyLabels(gwLabels),
}
}

// envoyLabels returns the labels applied to managed Envoy resources.
func envoyLabels(gwLabels map[string]string) map[string]string {
ret := map[string]string{
"app": "envoy",
}

for k, v := range gwLabels {
ret[k] = v
}

return ret
}
33 changes: 33 additions & 0 deletions internal/infrastructure/kubernetes/labels_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package kubernetes

import (
"testing"

"github.com/stretchr/testify/require"
)

func TestEnvoyPodSelector(t *testing.T) {
cases := []struct {
name string
in map[string]string
expected map[string]string
}{
{
name: "default",
in: map[string]string{
"foo": "bar",
},
expected: map[string]string{
"app": "envoy",
"foo": "bar",
},
},
}

for _, tc := range cases {
t.Run("", func(t *testing.T) {
got := envoyPodSelector(tc.in)
require.Equal(t, tc.expected, got.MatchLabels)
})
}
}
48 changes: 31 additions & 17 deletions internal/infrastructure/kubernetes/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ import (
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/intstr"

"github.com/envoyproxy/gateway/internal/envoygateway/config"
"github.com/envoyproxy/gateway/internal/gatewayapi"
"github.com/envoyproxy/gateway/internal/ir"
)

const (
// envoyServiceName is the name of the Envoy Service resource.
envoyServiceName = "envoy"
// envoyServiceHTTPPort is the HTTP port number of the Envoy service.
envoyServiceHTTPPort = 80
// envoyServiceHTTPSPort is the HTTPS port number of the Envoy service.
Expand All @@ -25,10 +25,17 @@ const (
// createServiceIfNeeded creates a Service based on the provided infra, if
// it doesn't exist in the kube api server.
func (i *Infra) createServiceIfNeeded(ctx context.Context, infra *ir.Infra) error {
current, err := i.getService(ctx)
name := fmt.Sprintf("%s-%s-%s",
config.EnvoyServiceName,
infra.Proxy.Metadata.Labels[gatewayapi.OwningGatewayNameLabel],
infra.Proxy.Metadata.Labels[gatewayapi.OwningGatewayNamespaceLabel],
)
ns := i.Namespace

current, err := i.getService(ctx, ns, name)
if err != nil {
if kerrors.IsNotFound(err) {
svc, err := i.createService(ctx, infra)
svc, err := i.createService(ctx, infra, ns, name)
if err != nil {
return err
}
Expand All @@ -48,21 +55,21 @@ func (i *Infra) createServiceIfNeeded(ctx context.Context, infra *ir.Infra) erro
}

// getService gets the Service from the kube api for the provided infra.
func (i *Infra) getService(ctx context.Context) (*corev1.Service, error) {
func (i *Infra) getService(ctx context.Context, ns, name string) (*corev1.Service, error) {
key := types.NamespacedName{
Namespace: i.Namespace,
Name: envoyServiceName,
Namespace: ns,
Name: name,
}
svc := new(corev1.Service)
if err := i.Client.Get(ctx, key, svc); err != nil {
return nil, fmt.Errorf("failed to get service %s/%s: %w", i.Namespace, envoyServiceName, err)
return nil, fmt.Errorf("failed to get service %s/%s: %w", ns, name, err)
}

return svc, nil
}

// expectedService returns the expected Service based on the provided infra.
func (i *Infra) expectedService(infra *ir.Infra) *corev1.Service {
func (i *Infra) expectedService(infra *ir.Infra, ns, name string) *corev1.Service {
var ports []corev1.ServicePort
for _, listener := range infra.Proxy.Listeners {
for _, port := range listener.Ports {
Expand All @@ -81,11 +88,11 @@ func (i *Infra) expectedService(infra *ir.Infra) *corev1.Service {
}
}

podSelector := EnvoyPodSelector(infra.GetProxyInfra().Name)
podSelector := envoyPodSelector(infra.Proxy.Metadata.Labels)
svc := &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Namespace: i.Namespace,
Name: envoyServiceName,
Namespace: ns,
Name: name,
Labels: podSelector.MatchLabels,
},
Spec: corev1.ServiceSpec{
Expand All @@ -103,8 +110,8 @@ func (i *Infra) expectedService(infra *ir.Infra) *corev1.Service {

// createService creates a Service in the kube api server based on the provided infra,
// if it doesn't exist.
func (i *Infra) createService(ctx context.Context, infra *ir.Infra) (*corev1.Service, error) {
expected := i.expectedService(infra)
func (i *Infra) createService(ctx context.Context, infra *ir.Infra, ns, name string) (*corev1.Service, error) {
expected := i.expectedService(infra, ns, name)
err := i.Client.Create(ctx, expected)
if err != nil {
if kerrors.IsAlreadyExists(err) {
Expand All @@ -118,11 +125,18 @@ func (i *Infra) createService(ctx context.Context, infra *ir.Infra) (*corev1.Ser
}

// deleteService deletes the Envoy Service in the kube api server, if it exists.
func (i *Infra) deleteService(ctx context.Context) error {
func (i *Infra) deleteService(ctx context.Context, infra *ir.Infra) error {
name := fmt.Sprintf("%s-%s-%s",
config.EnvoyServiceName,
infra.Proxy.Metadata.Labels[gatewayapi.OwningGatewayNameLabel],
infra.Proxy.Metadata.Labels[gatewayapi.OwningGatewayNamespaceLabel],
)
ns := i.Namespace

svc := &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Namespace: i.Namespace,
Name: envoyServiceName,
Namespace: ns,
Name: name,
},
}

Expand Down
14 changes: 6 additions & 8 deletions internal/infrastructure/kubernetes/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package kubernetes

import (
"context"
"sync"
"testing"

"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -64,7 +63,7 @@ func TestDesiredService(t *testing.T) {
Port: 443,
},
}
svc := kube.expectedService(infra)
svc := kube.expectedService(infra, "test-ns", "test")

checkServiceHasPort(t, svc, envoyServiceHTTPPort)
checkServiceHasPort(t, svc, envoyServiceHTTPSPort)
Expand All @@ -77,6 +76,10 @@ func TestDesiredService(t *testing.T) {
}

func TestDeleteService(t *testing.T) {
cli := fakeclient.NewClientBuilder().WithScheme(envoygateway.GetScheme()).WithObjects().Build()
kube := NewInfra(cli)
infra := ir.NewInfra()

testCases := []struct {
name string
expect bool
Expand All @@ -90,12 +93,7 @@ func TestDeleteService(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
kube := &Infra{
Client: fakeclient.NewClientBuilder().WithScheme(envoygateway.GetScheme()).Build(),
mu: sync.Mutex{},
Namespace: "test",
}
err := kube.deleteService(context.Background())
err := kube.deleteService(context.Background(), infra)
require.NoError(t, err)
})
}
Expand Down
2 changes: 1 addition & 1 deletion internal/infrastructure/runner/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func (r *Runner) subscribeAndTranslate(ctx context.Context) {
// The resource map is nil at startup.
r.Logger.Info("infra ir is nil, skipping")
continue
case in.Proxy == nil:
case in.Proxy.Listeners == nil:
if err := r.mgr.DeleteInfra(ctx, in); err != nil {
r.Logger.Error(err, "failed to delete infra")
}
Expand Down
Loading

0 comments on commit f8fd0fa

Please sign in to comment.