This repository contains a custom Kubernetes client-go controller that can be used to make Services and Ingresses available in feature branch namespaces with ExternalName Services and customized wildcard top-level domains.
Using Helm
helm install kube-external-sync https://github.com/alehechka/kube-external-sync/releases/download/v1.0.0/kube-external-sync-1.0.0.tgz --namespace kube-external-sync --create-namespace
Using kubectl
# create the namespace
kubectl create namespace kube-external-sync
# deploy the application's resource
kubectl apply -f https://github.com/alehechka/kube-external-sync/releases/download/v1.0.0/kube-external-sync.yaml
Replication of resources is triggered on the creation/modification of Namespaces or the annotated resource in question. To turn on replication of a resource, it must be annotated with at least one of the following:
Annotation | Example | Description |
---|---|---|
kube-external-sync.io/replicate-to |
my-namespace, feature-[.]* |
A CSV list of namespace names or regex patterns to replicate the resource to. |
kube-external-sync.io/replicate-to-matching |
feature-branch,branch!=default |
A label selector used to locate labeled namespaces to replicate to. |
The following Service will be replicated as an ExternalName Service to all Namespaces that name match the feature-[.]*
regex or include a label of the name feature-branch
with any value.
apiVersion: v1
kind: Service
metadata:
name: nginx
namespace: default
annotations:
kube-external-sync.io/replicate-to: 'feature-[.]*'
kube-external-sync.io/replicate-to-matching: 'feature-branch'
spec:
type: ClusterIP
ports:
- name: http
port: 80
protocol: TCP
targetPort: 80
selector:
app: nginx
tier: web
Labeled Namespace Example
kind: Namespace
apiVersion: v1
metadata:
name: coolnewthing
labels:
feature-branch: feature.coolnewthing
If the original resource is of type Service, than the resulting ExternalName Service will look as follows:
apiVersion: v1
kind: Service
metadata:
name: nginx
annotations:
kube-external-sync.io/replicated-at: '2022-09-18T20:57:44-05:00'
kube-external-sync.io/replicated-from: default/nginx
kube-external-sync.io/replicated-from-version: '123456'
spec:
type: ExternalName
ports:
- name: http
port: 80
protocol: TCP
targetPort: 80
externalName: nginx.default.svc.cluster.local
Note the
externalName
variable in the spec, this is generated specifically to point this Service to the original default/nginx Service so that all requests made in the feature branch tohttp://nginx
will be resolved back to the default branch instead of failing.
Ingresses are a little more complex because they typically include TLS hosts and rules with hosts that tell the load balancer where to send incoming traffic. To handle this, the controller will assume that the Namespace name correlates directly to the first subdomain of the host.
Example: Provided host is my-app.example.com
and the new Namespace is feature-coolnewthing
. By default, the controller will replace the first subdomain with the Namespace name, resulting in a new host of feature-coolnewthing.example.com
.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx
namespace: default
annotations:
kube-external-sync.io/replicate-to: 'feature-[.]*'
kube-external-sync.io/replicate-to-matching: 'feature-branch'
spec:
tls:
- hosts:
- my-app.example.com
rules:
- host: my-app.example.com
http:
paths:
- backend:
service:
name: nginx
port:
number: 80
path: /
pathType: Prefix
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx
namespace: feature-coolnewthing
annotations:
kube-external-sync.io/replicated-at: '2022-09-18T21:44:29-05:00'
kube-external-sync.io/replicated-from: default/nginx
kube-external-sync.io/replicated-from-version: '123456'
spec:
tls:
- hosts:
- feature-coolnewthing.example.com
rules:
- host: feature-coolnewthing.example.com
http:
paths:
- backend:
service:
name: nginx
port:
number: 80
path: /
pathType: Prefix
Annotation | Example | Description |
---|---|---|
kube-external-sync.io/strip-labels |
true |
By default, all labels will be replicated. This annotation will strip all labels from the replicated resource. A app.kubernetes.io/managed-by label will always be applied to the replicated resource. |
kube-external-sync.io/strip-annotations |
true |
By default, all non-replicate-to annotations will be replicated. This annotation will strip all annotations from the replicated resource. A few kube-external-sync.io/* annotations will always be applied to the replicated resource. |
kube-external-sync.io/keep-owner-references |
true |
By default, no OwnerReferences will be replicated. This annotation will replicate all OwnerReferences from the original. |
Annotation | Example | Description |
---|---|---|
kube-external-sync.io/external-name-suffix |
traefik.mesh |
The default value is svc.cluster.local but if some other service mesh is being used, that can be substituted here for the ExternalName suffix. |
Annotation | Example | Description |
---|---|---|
kube-external-sync.io/top-level-domain |
*.feature.example.com |
By default, the top-level-domain is determined from the original resource. This annotation allows that to be overridden with a custom TLD that will be applied to all TLS hosts and rule hosts. |
kube-external-sync.io/tld-secret-name |
tls-cert-secret |
If a custom TLD is supplied that requires a secret for the TLS cert, the SecretName can be supplied with this annotation. |