Skip to content
This repository has been archived by the owner on Oct 23, 2024. It is now read-only.

translator: make edgelb pool name optional #4

Merged
merged 1 commit into from
Jan 10, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
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
12 changes: 8 additions & 4 deletions docs/usage/10-provisioning-services.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,21 @@ In particular, exposing UDP or SCTP services is **NOT** supported.
=== Using `dklb` to provision a Kubernetes service

To expose a TCP application running on MKE to either inside or outside the DC/OS cluster, a Kubernetes https://kubernetes.io/docs/concepts/services-networking/service/[`Service`] resource of type https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer[`LoadBalancer`] must be created.
Furthermore, and in order for `dklb` to provision said service using EdgeLB, the service must be https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/[annotated] with the following annotation:
`dklb` will react to this event by provisioning an EdgeLB pool (henceforth referred to as the _target EdgeLB pool_) for the `Service` resource based on its specification.

=== Customizing the name of the EdgeLB pool

By default, `dklb` uses the MKE cluster's name and the `Service` resource's namespace and name to compute the name of the target EdgeLB pool.
To specify a custom name for said EdgeLB pool, one may https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/[annotate] the `Service` resource with the following annotation:

[source,text]
----
kubernetes.dcos.io/edgelb-pool-name: "<edgelb-pool-name>"
----

This annotation lets `dklb` know about the EdgeLB pool it should use to provision the service (henceforth referred to as the _target EdgeLB pool_).
Depending on whether the target EdgeLB pool exists or not, `dklb` will create or update it in order to expose all ports defined in the `Service` resource.
Depending on whether the "<edgelb-pool-name>" EdgeLB pool exists or not, `dklb` will create or update it in order to expose all ports defined in the `Service` resource.

IMPORTANT: One must not remove or change the value of this annotation at any time after the `Service` resource is created.
IMPORTANT: One must not remove or change the value of this annotation at any time after it is added to a given `Service` resource.

=== Intra-DC/OS vs external exposure

Expand Down
2 changes: 1 addition & 1 deletion pkg/controllers/ingress.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ func (c *IngressController) processQueueItem(workItem WorkItem) error {
er := kubernetesutil.NewEventRecorderForNamespace(c.kubeClient, ingress.Namespace)

// Compute the set of options that will be used to translate the Ingress resource into an EdgeLB pool.
options, err := translator.ComputeIngressTranslationOptions(ingress)
options, err := translator.ComputeIngressTranslationOptions(c.clusterName, ingress)
if err != nil {
// Emit an event and log an error, but do not re-enqueue as the resource's spec was found to be invalid.
er.Eventf(ingress, corev1.EventTypeWarning, constants.ReasonInvalidAnnotations, "the resource's annotations are not valid: %v", err)
Expand Down
2 changes: 1 addition & 1 deletion pkg/controllers/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ func (c *ServiceController) processQueueItem(workItem WorkItem) error {
er := kubernetesutil.NewEventRecorderForNamespace(c.kubeClient, service.Namespace)

// Compute the set of options that will be used to translate the Service resource into an EdgeLB pool.
options, err := translator.ComputeServiceTranslationOptions(service)
options, err := translator.ComputeServiceTranslationOptions(c.clusterName, service)
if err != nil {
// Emit an event and log an error, but do not re-enqueue as the resource's spec was found to be invalid.
er.Eventf(service, corev1.EventTypeWarning, constants.ReasonInvalidAnnotations, "the resource's annotations are not valid: %v", err)
Expand Down
13 changes: 6 additions & 7 deletions pkg/translator/base_options.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,16 @@ type BaseTranslationOptions struct {

// parseBaseTranslationOptions attempts to compute base, common translation options from the specified set of annotations.
// In case options cannot be computed or are invalid, the error message MUST be suitable to be used as the message for a Kubernetes event associated with the resource.
func parseBaseTranslationOptions(annotations map[string]string) (*BaseTranslationOptions, error) {
func parseBaseTranslationOptions(clusterName, namespace, name string, annotations map[string]string) (*BaseTranslationOptions, error) {
// Create a "BaseTranslationOptions" struct to hold the computed options.
res := &BaseTranslationOptions{}

// Parse the name of the target EdgeLB pool.
// This annotation is MANDATORY.
v, exists := annotations[constants.EdgeLBPoolNameAnnotationKey]
if !exists || v == "" {
return nil, fmt.Errorf("required annotation %q has not been provided", constants.EdgeLBPoolNameAnnotationKey)
// Parse or compute the name of the target EdgeLB pool.
if v, exists := annotations[constants.EdgeLBPoolNameAnnotationKey]; !exists || v == "" {
res.EdgeLBPoolName = computeEdgeLBPoolName(clusterName, namespace, name)
} else {
res.EdgeLBPoolName = v
}
res.EdgeLBPoolName = v

// Parse the role of the target EdgeLB pool.
if v, exists := annotations[constants.EdgeLBPoolRoleAnnotationKey]; !exists || v == "" {
Expand Down
21 changes: 21 additions & 0 deletions pkg/translator/base_options_utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package translator

import (
"fmt"

"github.com/mesosphere/dklb/pkg/util/strings"
)

const (
// edgeLBPoolNameComponentSeparator is the string used as to separate the components of an EdgeLB pool's name.
// "--" is chosen as the value since the name of an EdgeLB pool must match the "^[a-z0-9]([a-z0-9-]*[a-z0-9])?$" regular expression.
edgeLBPoolNameComponentSeparator = "--"
// edgeLBPoolNameFormatString is the format string used to compute the name of an EdgeLB pool corresponding to a given Kubernetes resource.
// The resulting name is of the form "<cluster-name>--<namespace>--<name>".
edgeLBPoolNameFormatString = "%s" + edgeLBPoolNameComponentSeparator + "%s" + edgeLBPoolNameComponentSeparator + "%s"
)

// computeEdgeLBPoolName computes the name of the EdgeLB pool that corresponds to the Kubernetes resource corresponding to the provided coordinates.
func computeEdgeLBPoolName(clusterName, namespace, name string) string {
return fmt.Sprintf(edgeLBPoolNameFormatString, strings.ReplaceForwardSlashes(clusterName, edgeLBPoolNameComponentSeparator), namespace, name)
}
4 changes: 2 additions & 2 deletions pkg/translator/ingress_options.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ type IngressTranslationOptions struct {

// ComputeIngressTranslationOptions computes the set of options to use for "translating" the specified Ingress resource into an EdgeLB pool.
// In case options cannot be computed or are invalid, the error message MUST be suitable to be used as the message for a Kubernetes event associated with the resource.
func ComputeIngressTranslationOptions(obj *extsv1beta1.Ingress) (*IngressTranslationOptions, error) {
func ComputeIngressTranslationOptions(clusterName string, obj *extsv1beta1.Ingress) (*IngressTranslationOptions, error) {
// Create an "IngressTranslationOptions" struct to hold the computed options.
res := &IngressTranslationOptions{}
var annotations map[string]string
Expand All @@ -33,7 +33,7 @@ func ComputeIngressTranslationOptions(obj *extsv1beta1.Ingress) (*IngressTransla
}

// Parse base translation options.
b, err := parseBaseTranslationOptions(annotations)
b, err := parseBaseTranslationOptions(clusterName, obj.Namespace, obj.Name, annotations)
if err != nil {
return nil, err
}
Expand Down
42 changes: 24 additions & 18 deletions pkg/translator/ingress_options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,28 @@ func TestComputeIngressTranslationOptions(t *testing.T) {
options *translator.IngressTranslationOptions
error error
}{
// Test computing options for an Ingress resource without the required annotations.
// Make sure an error is returned.
// Test computing options for an Ingress resource without any annotations.
// Make sure the name of the EdgeLB pool is computed as expected, and that the default values are used everywhere else.
{
description: "compute options for an Ingress resource without the required annotations",
description: "compute options for an Ingress resource without any annotations",
annotations: map[string]string{},
options: nil,
error: fmt.Errorf("required annotation %q has not been provided", constants.EdgeLBPoolNameAnnotationKey),
options: &translator.IngressTranslationOptions{
BaseTranslationOptions: translator.BaseTranslationOptions{
EdgeLBPoolName: "dev--kubernetes01--foo--bar",
EdgeLBPoolRole: translator.DefaultEdgeLBPoolRole,
EdgeLBPoolCpus: translator.DefaultEdgeLBPoolCpus,
EdgeLBPoolMem: translator.DefaultEdgeLBPoolMem,
EdgeLBPoolSize: translator.DefaultEdgeLBPoolSize,
EdgeLBPoolCreationStrategy: translator.DefaultEdgeLBPoolCreationStrategy,
},
EdgeLBPoolPort: translator.DefaultEdgeLBPoolPort,
},
error: nil,
},
// Test computing options for an Ingress resource with just the required annotations.
// Test computing options for an Ingress resource specifying the name of the target EdgeLB pool.
// Make sure the name of the EdgeLB pool is captured as expected, and that the default values are used everywhere else.
{
description: "compute options for an Ingress resource with just the required annotations",
description: "compute options for an Ingress resource specifying the name of the target EdgeLB pool",
annotations: map[string]string{
constants.EdgeLBPoolNameAnnotationKey: "foo",
},
Expand All @@ -49,16 +59,15 @@ func TestComputeIngressTranslationOptions(t *testing.T) {
error: nil,
},
// Test computing options for an Ingress resource defining a custom frontend bind port.
// Make sure the name of the EdgeLB pool and the frontend bind port are captured as expected, and that the default values are used everywhere else.
// Make sure the frontend bind port is captured as expected, and that the default values are used everywhere else.
{
description: "compute options for an Ingress resource defining a custom frontend bind port",
annotations: map[string]string{
constants.EdgeLBPoolNameAnnotationKey: "foo",
constants.EdgeLBPoolPortKey: "14708",
constants.EdgeLBPoolPortKey: "14708",
},
options: &translator.IngressTranslationOptions{
BaseTranslationOptions: translator.BaseTranslationOptions{
EdgeLBPoolName: "foo",
EdgeLBPoolName: "dev--kubernetes01--foo--bar",
EdgeLBPoolRole: translator.DefaultEdgeLBPoolRole,
EdgeLBPoolCpus: translator.DefaultEdgeLBPoolCpus,
EdgeLBPoolMem: translator.DefaultEdgeLBPoolMem,
Expand All @@ -74,8 +83,7 @@ func TestComputeIngressTranslationOptions(t *testing.T) {
{
description: "compute options for an Ingress resource defining an invalid value for the frontend bind port",
annotations: map[string]string{
constants.EdgeLBPoolNameAnnotationKey: "foo",
constants.EdgeLBPoolPortKey: "74511",
constants.EdgeLBPoolPortKey: "74511",
},
options: nil,
error: fmt.Errorf("%d is not a valid port number", 74511),
Expand Down Expand Up @@ -113,8 +121,7 @@ func TestComputeIngressTranslationOptions(t *testing.T) {
{
description: "compute options for an Ingress resource with a custom but invalid port mapping",
annotations: map[string]string{
constants.EdgeLBPoolNameAnnotationKey: "foo",
constants.EdgeLBPoolPortKey: "74511",
constants.EdgeLBPoolPortKey: "74511",
},
options: nil,
error: fmt.Errorf("%d is not a valid port number", 74511),
Expand All @@ -124,8 +131,7 @@ func TestComputeIngressTranslationOptions(t *testing.T) {
{
description: "compute options for a Service resource having an invalid port mapping",
annotations: map[string]string{
constants.EdgeLBPoolNameAnnotationKey: "foo",
constants.EdgeLBPoolPortKey: "foo",
constants.EdgeLBPoolPortKey: "foo",
},
options: nil,
error: fmt.Errorf("failed to parse %q as the frontend bind port to use: %v", "foo", "strconv.Atoi: parsing \"foo\": invalid syntax"),
Expand All @@ -137,7 +143,7 @@ func TestComputeIngressTranslationOptions(t *testing.T) {
// Create a dummy Ingress resource containing the annotations for the current test.
r := ingresstestutil.DummyIngressResource("foo", "bar", ingresstestutil.WithAnnotations(test.annotations))
// Compute the translation options for the resource.
o, err := translator.ComputeIngressTranslationOptions(r)
o, err := translator.ComputeIngressTranslationOptions(testClusterName, r)
if err != nil {
// Make sure the error matches the expected one.
assert.EqualError(t, err, test.error.Error())
Expand Down
4 changes: 2 additions & 2 deletions pkg/translator/service_options.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ type ServiceTranslationOptions struct {

// ComputeServiceTranslationOptions computes the set of options to use for "translating" the specified Service resource into an EdgeLB pool.
// In case options cannot be computed or are invalid, the error message MUST be suitable to be used as the message for a Kubernetes event associated with the resource.
func ComputeServiceTranslationOptions(obj *corev1.Service) (*ServiceTranslationOptions, error) {
func ComputeServiceTranslationOptions(clusterName string, obj *corev1.Service) (*ServiceTranslationOptions, error) {
// Create an "ServiceTranslationOptions" struct to hold the computed options.
res := &ServiceTranslationOptions{
EdgeLBPoolPortMap: make(map[int32]int32, len(obj.Spec.Ports)),
Expand All @@ -35,7 +35,7 @@ func ComputeServiceTranslationOptions(obj *corev1.Service) (*ServiceTranslationO
}

// Parse base translation options.
b, err := parseBaseTranslationOptions(annotations)
b, err := parseBaseTranslationOptions(clusterName, obj.Namespace, obj.Name, annotations)
if err != nil {
return nil, err
}
Expand Down
Loading