Skip to content
This repository has been archived by the owner on Aug 25, 2021. It is now read-only.

TerminatingGateway CRD #715

Merged
merged 1 commit into from
Dec 17, 2020
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ BREAKING CHANGES:

FEATURES:
* CRDs: add new CRD `IngressGateway` for configuring Consul's [ingress-gateway](https://www.consul.io/docs/agent/config-entries/ingress-gateway) config entry. [[GH-714](https://github.com/hashicorp/consul-helm/pull/714)]
* CRDs: add new CRD `TerminatingGateway` for configuring Consul's [terminating-gateway](https://www.consul.io/docs/agent/config-entries/terminating-gateway) config entry. [[GH-715](https://github.com/hashicorp/consul-helm/pull/715)]

IMPROVEMENTS:
* Make `server.bootstrapExpect` optional. If not set, will now default to `server.replicas`.
Expand Down
2 changes: 2 additions & 0 deletions templates/controller-clusterrole.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ rules:
- servicesplitters
- serviceintentions
- ingressgateways
- terminatinggateways
verbs:
- create
- delete
Expand All @@ -38,6 +39,7 @@ rules:
- servicesplitters/status
- serviceintentions/status
- ingressgateways/status
- terminatinggateways/status
verbs:
- get
- patch
Expand Down
22 changes: 22 additions & 0 deletions templates/controller-mutatingwebhookconfiguration.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -168,4 +168,26 @@ webhooks:
resources:
- ingressgateways
sideEffects: None
- clientConfig:
caBundle: Cg==
service:
name: {{ template "consul.fullname" . }}-controller-webhook
namespace: {{ .Release.Namespace }}
path: /mutate-v1alpha1-terminatinggateway
failurePolicy: Fail
admissionReviewVersions:
- "v1beta1"
- "v1"
name: mutate-terminatinggateway.consul.hashicorp.com
rules:
- apiGroups:
- consul.hashicorp.com
apiVersions:
- v1alpha1
operations:
- CREATE
- UPDATE
resources:
- terminatinggateways
sideEffects: None
{{- end }}
117 changes: 117 additions & 0 deletions templates/crd-terminatinggateways.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
{{- if .Values.controller.enabled }}
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.4.1
creationTimestamp: null
name: terminatinggateways.consul.hashicorp.com
labels:
app: {{ template "consul.name" . }}
chart: {{ template "consul.chart" . }}
heritage: {{ .Release.Service }}
release: {{ .Release.Name }}
component: crd
spec:
additionalPrinterColumns:
- JSONPath: .status.conditions[?(@.type=="Synced")].status
description: The sync status of the resource with Consul
name: Synced
type: string
- JSONPath: .metadata.creationTimestamp
description: The age of the resource
name: Age
type: date
group: consul.hashicorp.com
names:
kind: TerminatingGateway
listKind: TerminatingGatewayList
plural: terminatinggateways
singular: terminatinggateway
scope: Namespaced
subresources:
status: {}
validation:
openAPIV3Schema:
description: TerminatingGateway is the Schema for the terminatinggateways API
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: TerminatingGatewaySpec defines the desired state of TerminatingGateway
properties:
services:
description: Services is a list of service names represented by the terminating gateway.
items:
description: A LinkedService is a service represented by a terminating gateway
properties:
caFile:
description: CAFile is the optional path to a CA certificate to use for TLS connections from the gateway to the linked service.
type: string
certFile:
description: CertFile is the optional path to a client certificate to use for TLS connections from the gateway to the linked service.
type: string
keyFile:
description: KeyFile is the optional path to a private key to use for TLS connections from the gateway to the linked service.
type: string
name:
description: Name is the name of the service, as defined in Consul's catalog.
type: string
namespace:
description: The namespace the service is registered in.
type: string
sni:
description: SNI is the optional name to specify during the TLS handshake with a linked service.
type: string
type: object
type: array
type: object
status:
properties:
conditions:
description: Conditions indicate the latest available observations of a resource's current state.
items:
description: 'Conditions define a readiness condition for a Consul resource. See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties'
properties:
lastTransitionTime:
description: LastTransitionTime is the last time the condition transitioned from one status to another.
format: date-time
type: string
message:
description: A human readable message indicating details about the transition.
type: string
reason:
description: The reason for the condition's last transition.
type: string
status:
description: Status of the condition, one of True, False, Unknown.
type: string
type:
description: Type of condition.
type: string
required:
- status
- type
type: object
type: array
type: object
type: object
version: v1alpha1
versions:
- name: v1alpha1
served: true
storage: true
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []
{{- end }}
30 changes: 30 additions & 0 deletions test/acceptance/tests/controller/controller_namespaces_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,17 @@ func TestControllerNamespaces(t *testing.T) {
require.Len(r, ingressGatewayEntry.Listeners[0].Services, 1)
require.Equal(r, "foo", ingressGatewayEntry.Listeners[0].Services[0].Name)

// terminating-gateway
entry, _, err = consulClient.ConfigEntries().Get(api.TerminatingGateway, "terminating-gateway", queryOpts)
require.NoError(r, err)
terminatingGatewayEntry, ok := entry.(*api.TerminatingGatewayConfigEntry)
require.True(r, ok, "could not cast to TerminatingGatewayConfigEntry")
require.Len(r, terminatingGatewayEntry.Services, 1)
require.Equal(r, "name", terminatingGatewayEntry.Services[0].Name)
require.Equal(r, "caFile", terminatingGatewayEntry.Services[0].CAFile)
require.Equal(r, "certFile", terminatingGatewayEntry.Services[0].CertFile)
require.Equal(r, "keyFile", terminatingGatewayEntry.Services[0].KeyFile)
require.Equal(r, "sni", terminatingGatewayEntry.Services[0].SNI)
})
}

Expand Down Expand Up @@ -216,6 +227,10 @@ func TestControllerNamespaces(t *testing.T) {
patchPort := 9090
k8s.RunKubectl(t, ctx.KubectlOptions(t), "patch", "-n", KubeNS, "ingressgateway", "ingress-gateway", "-p", fmt.Sprintf(`{"spec": {"listeners": [{"port": %d, "protocol": "tcp", "services": [{"name": "foo"}]}]}}`, patchPort), "--type=merge")

logger.Log(t, "patching terminating-gateway custom resource")
patchSNI := "patch-sni"
k8s.RunKubectl(t, ctx.KubectlOptions(t), "patch", "-n", KubeNS, "terminatinggateway", "terminating-gateway", "-p", fmt.Sprintf(`{"spec": {"services": [{"name":"name","caFile":"caFile","certFile":"certFile","keyFile":"keyFile","sni":"%s"}]}}`, patchSNI), "--type=merge")

counter := &retry.Counter{Count: 10, Wait: 500 * time.Millisecond}
retry.RunWith(counter, t, func(r *retry.R) {
// service-defaults
Expand Down Expand Up @@ -268,6 +283,13 @@ func TestControllerNamespaces(t *testing.T) {
ingressGatewayEntry, ok := entry.(*api.IngressGatewayConfigEntry)
require.True(r, ok, "could not cast to IngressGatewayConfigEntry")
require.Equal(r, patchPort, ingressGatewayEntry.Listeners[0].Port)

// terminating-gateway
entry, _, err = consulClient.ConfigEntries().Get(api.TerminatingGateway, "terminating-gateway", queryOpts)
require.NoError(r, err)
terminatingGatewayEntry, ok := entry.(*api.TerminatingGatewayConfigEntry)
require.True(r, ok, "could not cast to TerminatingGatewayConfigEntry")
require.Equal(r, patchSNI, terminatingGatewayEntry.Services[0].SNI)
})
}

Expand All @@ -294,6 +316,9 @@ func TestControllerNamespaces(t *testing.T) {
logger.Log(t, "deleting ingress-gateway custom resource")
k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "-n", KubeNS, "ingressgateway", "ingress-gateway")

logger.Log(t, "deleting terminating-gateway custom resource")
k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "-n", KubeNS, "terminatinggateway", "terminating-gateway")

counter := &retry.Counter{Count: 10, Wait: 500 * time.Millisecond}
retry.RunWith(counter, t, func(r *retry.R) {
// service-defaults
Expand Down Expand Up @@ -330,6 +355,11 @@ func TestControllerNamespaces(t *testing.T) {
_, _, err = consulClient.ConfigEntries().Get(api.IngressGateway, "ingress-gateway", queryOpts)
require.Error(r, err)
require.Contains(r, err.Error(), "404 (Config entry not found")

// terminating-gateway
_, _, err = consulClient.ConfigEntries().Get(api.IngressGateway, "terminating-gateway", queryOpts)
require.Error(r, err)
require.Contains(r, err.Error(), "404 (Config entry not found")
})
}
})
Expand Down
31 changes: 31 additions & 0 deletions test/acceptance/tests/controller/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,18 @@ func TestController(t *testing.T) {
require.Equal(r, 8080, ingressGatewayEntry.Listeners[0].Port)
require.Len(r, ingressGatewayEntry.Listeners[0].Services, 1)
require.Equal(r, "foo", ingressGatewayEntry.Listeners[0].Services[0].Name)

// terminating-gateway
entry, _, err = consulClient.ConfigEntries().Get(api.TerminatingGateway, "terminating-gateway", nil)
require.NoError(r, err)
terminatingGatewayEntry, ok := entry.(*api.TerminatingGatewayConfigEntry)
require.True(r, ok, "could not cast to TerminatingGatewayConfigEntry")
require.Len(r, terminatingGatewayEntry.Services, 1)
require.Equal(r, "name", terminatingGatewayEntry.Services[0].Name)
require.Equal(r, "caFile", terminatingGatewayEntry.Services[0].CAFile)
require.Equal(r, "certFile", terminatingGatewayEntry.Services[0].CertFile)
require.Equal(r, "keyFile", terminatingGatewayEntry.Services[0].KeyFile)
require.Equal(r, "sni", terminatingGatewayEntry.Services[0].SNI)
})
}

Expand Down Expand Up @@ -156,6 +168,10 @@ func TestController(t *testing.T) {
patchPort := 9090
k8s.RunKubectl(t, ctx.KubectlOptions(t), "patch", "ingressgateway", "ingress-gateway", "-p", fmt.Sprintf(`{"spec": {"listeners": [{"port": %d, "protocol": "tcp", "services": [{"name": "foo"}]}]}}`, patchPort), "--type=merge")

logger.Log(t, "patching terminating-gateway custom resource")
patchSNI := "patch-sni"
k8s.RunKubectl(t, ctx.KubectlOptions(t), "patch", "terminatinggateway", "terminating-gateway", "-p", fmt.Sprintf(`{"spec": {"services": [{"name":"name","caFile":"caFile","certFile":"certFile","keyFile":"keyFile","sni":"%s"}]}}`, patchSNI), "--type=merge")

counter := &retry.Counter{Count: 10, Wait: 500 * time.Millisecond}
retry.RunWith(counter, t, func(r *retry.R) {
// service-defaults
Expand Down Expand Up @@ -209,6 +225,13 @@ func TestController(t *testing.T) {
ingressGatewayEntry, ok := entry.(*api.IngressGatewayConfigEntry)
require.True(r, ok, "could not cast to IngressGatewayConfigEntry")
require.Equal(r, patchPort, ingressGatewayEntry.Listeners[0].Port)

// terminating-gateway
entry, _, err = consulClient.ConfigEntries().Get(api.TerminatingGateway, "terminating-gateway", nil)
require.NoError(r, err)
terminatingGatewayEntry, ok := entry.(*api.TerminatingGatewayConfigEntry)
require.True(r, ok, "could not cast to TerminatingGatewayConfigEntry")
require.Equal(r, patchSNI, terminatingGatewayEntry.Services[0].SNI)
})
}

Expand All @@ -235,6 +258,9 @@ func TestController(t *testing.T) {
logger.Log(t, "deleting ingress-gateway custom resource")
k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "ingressgateway", "ingress-gateway")

logger.Log(t, "deleting terminating-gateway custom resource")
k8s.RunKubectl(t, ctx.KubectlOptions(t), "delete", "terminatinggateway", "terminating-gateway")

counter := &retry.Counter{Count: 10, Wait: 500 * time.Millisecond}
retry.RunWith(counter, t, func(r *retry.R) {
// service-defaults
Expand Down Expand Up @@ -271,6 +297,11 @@ func TestController(t *testing.T) {
_, _, err = consulClient.ConfigEntries().Get(api.IngressGateway, "ingress-gateway", nil)
require.Error(r, err)
require.Contains(r, err.Error(), "404 (Config entry not found")

// terminating-gateway
_, _, err = consulClient.ConfigEntries().Get(api.IngressGateway, "terminating-gateway", nil)
require.Error(r, err)
require.Contains(r, err.Error(), "404 (Config entry not found")
})
}
})
Expand Down
11 changes: 11 additions & 0 deletions test/acceptance/tests/fixtures/crds/terminatinggateway.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
apiVersion: consul.hashicorp.com/v1alpha1
kind: TerminatingGateway
metadata:
name: terminating-gateway
spec:
services:
- name: name
caFile: "caFile"
certFile: "certFile"
keyFile: "keyFile"
sni: "sni"
24 changes: 24 additions & 0 deletions test/unit/crd-terminatinggateway.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/usr/bin/env bats

load _helpers

@test "terminatingGateway/CustomerResourceDefinition: disabled by default" {
cd `chart_dir`
assert_empty helm template \
-s templates/crd-terminatinggateways.yaml \
.
}

@test "terminatingGateway/CustomerResourceDefinition: enabled with controller.enabled=true" {
cd `chart_dir`
local actual=$(helm template \
-s templates/crd-terminatinggateways.yaml \
--set 'controller.enabled=true' \
. | tee /dev/stderr |
# The generated CRDs have "---" at the top which results in two objects
# being detected by yq, the first of which is null. We must therefore use
# yq -s so that length operates on both objects at once rather than
# individually, which would output false\ntrue and fail the test.
yq -s 'length > 0' | tee /dev/stderr)
[ "${actual}" = "true" ]
}