Skip to content

Commit

Permalink
PLT-651: added ingress construct
Browse files Browse the repository at this point in the history
  • Loading branch information
nemanja-kovacevic-thinkit committed May 24, 2024
1 parent 526214c commit 500c5d5
Show file tree
Hide file tree
Showing 4 changed files with 200 additions and 0 deletions.
2 changes: 2 additions & 0 deletions lib/ingress/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./ingress-props";
export * from "./ingress";
44 changes: 44 additions & 0 deletions lib/ingress/ingress-props.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
export interface ServiceProps {
readonly name: string;
readonly port: number;
readonly namespace: string;
readonly weight: number;
}

export interface IngressProps {
/**
* Whether this is an internal service (behind VPN).
* @default false
*/
readonly internal?: boolean;

/**
* Custom selector labels, they will be merged with the default app, role, and instance.
* They will be applied to the workload, the pod and the service.
* @default { app: "<app label from chart>", role: "server", instance: "<construct id>" }
*/
readonly selectorLabels?: { [key: string]: string };

/**
* Domain name for TLS certificate discovery.
* @see https://kubernetes-sigs.github.io/aws-load-balancer-controller/v2.3/guide/ingress/cert_discovery/
*/
readonly tlsDomain?: string | string[];

/**
* Overrides for Ingress annotations.
* @see https://kubernetes-sigs.github.io/aws-load-balancer-controller/v2.3/guide/ingress/annotations/
*/
readonly ingressAnnotations?: { [key: string]: string };

/**
* Hostname to add External DNS record for the ingress.
*/
readonly externalHostname?: string;

readonly hostnames: string[];

readonly serviceRouting: ServiceProps[];

readonly ingressClassName: string;
}
143 changes: 143 additions & 0 deletions lib/ingress/ingress.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import { Chart } from "cdk8s";
import { Construct } from "constructs";
import {
IngressRule,
IngressTls,
IntOrString,
KubeIngress,
KubeService,
} from "../../imports/k8s";
import { convertToStringMap } from "../web-service/.";
import { ensureArray } from "../common";
import { supportsTls } from "./tls-util";
import { IngressProps } from ".";
import { ServiceSpecType } from "../k8s";

export class Ingress extends Construct {
constructor(scope: Construct, id: string, props: IngressProps) {
super(scope, id);

const chart = Chart.of(this);
const ingressTargetType = "instance";
const environment = chart.labels.environment;
const app = chart.labels.app ?? props.selectorLabels?.app;
const selectorLabels: { [key: string]: string } = {
app: app,
role: "server",
instance: id,
...props.selectorLabels,
};

const labels: { [key: string]: string } = {
...chart.labels,
...selectorLabels,
};

const externalDns: Record<string, string> = {};
const ingressRules: IngressRule[] = [];
const ingressTls: IngressTls[] = [];
const ingressListenPorts: Record<string, number>[] = [{ HTTP: 80 }];
const ingressTlsAnnotations: Record<string, string> = {};
if (supportsTls(props)) {
if (props.tlsDomain) {
ingressTls.push({
hosts: ensureArray(props.tlsDomain),
});
}
ingressListenPorts.push({ HTTPS: 443 });
}

if (props.externalHostname) {
externalDns["external-dns.alpha.kubernetes.io/hostname"] =
props.externalHostname;
}

const targetGroups: Record<string, string | number>[] = [];
for (const service of props.serviceRouting) {
const serviceName = `${service.name}-${service.namespace}`;
new KubeService(this, `${id}-${serviceName}-service`, {
metadata: {
name: serviceName,
namespace: "application-ingress",
},
spec: {
type: ServiceSpecType.EXTERNAL_NAME,
externalName: `${service.name}.${service.namespace}.svc.cluster.local`,
ports: [
{
port: service.port,
targetPort: IntOrString.fromNumber(service.port),
},
],
},
});

const targetGroup = {
serviceName: serviceName,
servicePort: service.port,
weight: service.weight,
};
targetGroups.push(targetGroup);
}

const ingressAnnotations = {
"alb.ingress.kubernetes.io/listen-ports":
convertToJsonContent(ingressListenPorts),
"alb.ingress.kubernetes.io/success-codes": "200,303",
"alb.ingress.kubernetes.io/target-type": ingressTargetType,
"alb.ingress.kubernetes.io/tags": convertToStringMap({
service: labels.service ?? app,
instance: id,
environment: environment,
}),
"alb.ingress.kubernetes.io/actions.service-weighting":
convertToJsonContent({
type: "forward",
forwardConfig: {
targetGroups: targetGroups,
},
}),
...ingressTlsAnnotations,
...props.ingressAnnotations,
...externalDns,
};

for (const hostname of props.hostnames) {
ingressRules.push({
host: hostname,
http: {
paths: [
{
pathType: "Prefix",
path: "/",
backend: {
service: {
name: "service-weighting",
port: {
name: "use-annotation",
},
},
},
},
],
},
});
}

new KubeIngress(this, `${id}-ingress`, {
metadata: {
annotations: ingressAnnotations,
labels: labels,
},
spec: {
ingressClassName: props.ingressClassName,
tls: ingressTls.length > 0 ? ingressTls : undefined,
rules: ingressRules.length > 0 ? ingressRules : undefined,
},
});
}
}

function convertToJsonContent(obj: unknown): string {
return JSON.stringify(obj);
}
11 changes: 11 additions & 0 deletions lib/ingress/tls-util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { IngressProps } from ".";

/**
* Whether this web service should support TLS.
*/
export function supportsTls(props: Partial<IngressProps>): boolean {
return (
!!props.tlsDomain ||
!!props.ingressAnnotations?.["alb.ingress.kubernetes.io/certificate-arn"]
);
}

0 comments on commit 500c5d5

Please sign in to comment.