Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add expose service entry for internal cluster traffic #356

Merged
merged 31 commits into from
May 14, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
a5a1dd0
feat: add a keycloak service entry for internal cluster traffic
Racer159 Apr 19, 2024
7b21a94
add cap check
Racer159 Apr 19, 2024
91f0c12
Merge branch 'main' into add-keycloak-service-entry
Racer159 Apr 19, 2024
4820555
Merge branch 'main' into add-keycloak-service-entry
UnicornChance Apr 24, 2024
ac90288
Merge branch 'main' into add-keycloak-service-entry
UnicornChance Apr 24, 2024
6711f4e
Merge branch 'main' into add-keycloak-service-entry
Racer159 May 2, 2024
ebe2755
Merge branch 'main' into add-keycloak-service-entry
UnicornChance May 3, 2024
6ffa236
Merge branch 'main' into add-keycloak-service-entry
Racer159 May 6, 2024
fde7b14
this should work :fingers-crossed:
Racer159 May 6, 2024
759b141
remove service entry
Racer159 May 6, 2024
2ebbc86
fix linting
Racer159 May 6, 2024
90705d5
Merge branch 'main' into add-keycloak-service-entry
Racer159 May 6, 2024
948c259
fix plural and names
Racer159 May 7, 2024
67c9cd1
fix dev
Racer159 May 7, 2024
7d89156
remove vs
Racer159 May 7, 2024
21729e0
fix the feedback - refactor the codes
Racer159 May 9, 2024
23c3981
add a comment
Racer159 May 9, 2024
0531a3c
add a comment2
Racer159 May 9, 2024
a8f2838
add tests
Racer159 May 9, 2024
3ff3482
Merge branch 'main' into add-keycloak-service-entry
Racer159 May 9, 2024
10a0c7e
fix lint
Racer159 May 9, 2024
4393b62
fix other pkg name
Racer159 May 9, 2024
01fe692
Merge branch 'main' into add-keycloak-service-entry
Racer159 May 10, 2024
7721df3
update docs
Racer159 May 10, 2024
3a17981
update docs
Racer159 May 10, 2024
1fe059a
Merge branch 'main' into add-keycloak-service-entry
Racer159 May 10, 2024
28fc988
Merge branch 'main' into add-keycloak-service-entry
Racer159 May 10, 2024
482760c
Merge branch 'main' into add-keycloak-service-entry
Racer159 May 13, 2024
f165219
Update src/pepr/operator/controllers/istio/istio-resources.ts
Racer159 May 14, 2024
8712757
Update src/pepr/operator/controllers/istio/istio-resources.ts
Racer159 May 14, 2024
fff964e
Merge branch 'main' into add-keycloak-service-entry
Racer159 May 14, 2024
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
10 changes: 9 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,13 @@
"root": true,
"rules": {
"@typescript-eslint/no-floating-promises": ["error"]
}
},
"overrides": [
{
"files": [ "src/pepr/operator/crd/generated/**/*.ts", "src/pepr/operator/crd/generated/*.ts" ],
"rules": {
"@typescript-eslint/no-explicit-any": "off"
}
}
]
}
2 changes: 1 addition & 1 deletion src/keycloak/chart/templates/secret-admin-password.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ apiVersion: v1
kind: Secret
metadata:
name: {{ $secretName }}
namespace: {{ .Release.Namespace }}
namespace: {{ .Release.Namespace }}
labels:
{{- include "keycloak.labels" . | nindent 4 }}
type: Opaque
Expand Down
2 changes: 1 addition & 1 deletion src/keycloak/chart/templates/secret-postgresql.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ apiVersion: v1
kind: Secret
metadata:
name: {{ include "keycloak.fullname" . }}-postgresql
namespace: {{ .Release.Namespace }}
namespace: {{ .Release.Namespace }}
labels:
{{- include "keycloak.labels" . | nindent 4 }}
type: Opaque
Expand Down
114 changes: 114 additions & 0 deletions src/pepr/operator/controllers/istio/service-entry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { K8s, Log } from "pepr";

import { UDSConfig } from "../../../config";
import {
Expose,
Gateway,
IstioServiceEntry,
IstioLocation,
IstioResolution,
IstioPort,
IstioEndpoint,
UDSPackage,
} from "../../crd";
import { getOwnerRef, sanitizeResourceName } from "../utils";

/**
* Creates a ServiceEntry for each exposed service in the package
*
* @param pkg
* @param namespace
*/
export async function serviceEntry(pkg: UDSPackage, namespace: string) {
Racer159 marked this conversation as resolved.
Show resolved Hide resolved
const pkgName = pkg.metadata!.name!;
const generation = (pkg.metadata?.generation ?? 0).toString();

// Get the list of exposed services
const exposeList = pkg.spec?.network?.expose ?? [];

// Track which ServiceEntries we've created
const serviceEntryNames: Map<string, boolean> = new Map();

// Iterate over each exposed service
for (const expose of exposeList) {
const { gateway = Gateway.Tenant, host } = expose;

const name = generateSEName(pkg, expose);

// If we have already made a ServiceEntry with this name, skip (i.e. if advancedHTTP was used)
if (serviceEntryNames.get(name)) {
continue;
}
Racer159 marked this conversation as resolved.
Show resolved Hide resolved

// For the admin gateway, we need to add the path prefix
const domain = (gateway === Gateway.Admin ? "admin." : "") + UDSConfig.domain;

// Append the domain to the host
const fqdn = `${host}.${domain}`;

const serviceEntryPort: IstioPort = {
name: "https",
number: 443,
protocol: "HTTPS",
};

const serviceEntryEndpoint: IstioEndpoint = {
// Map the gateway (admin, passthrough or tenant) to the ServiceEntry
address: `${gateway}-ingressgateway.istio-${gateway}-gateway.svc.cluster.local`,
};

const payload: IstioServiceEntry = {
metadata: {
name,
namespace,
labels: {
"uds/package": pkgName,
"uds/generation": generation,
},
// Use the CR as the owner ref for each ServiceEntry
ownerReferences: getOwnerRef(pkg),
},
spec: {
// Append the UDS Domain to the host
hosts: [fqdn],
location: IstioLocation.MeshInternal,
resolution: IstioResolution.DNS,
ports: [serviceEntryPort],
endpoints: [serviceEntryEndpoint],
},
};

Log.debug(payload, `Applying ServiceEntry ${payload.metadata?.name}`);

// Apply the ServiceEntry and force overwrite any existing policy
await K8s(IstioServiceEntry).Apply(payload, { force: true });
Racer159 marked this conversation as resolved.
Show resolved Hide resolved

serviceEntryNames.set(name, true);
}

// Get all related ServiceEntries in the namespace
const serviceEntries = await K8s(IstioServiceEntry)
.InNamespace(namespace)
.WithLabel("uds/package", pkgName)
.Get();

// Find any orphaned ServiceEntries (not matching the current generation)
const orphanedSE = serviceEntries.items.filter(
se => se.metadata?.labels?.["uds/generation"] !== generation,
);

// Delete any orphaned ServiceEntries
for (const se of orphanedSE) {
Log.debug(se, `Deleting orphaned ServiceEntry ${se.metadata!.name}`);
await K8s(IstioServiceEntry).Delete(se);
}
}

export function generateSEName(pkg: UDSPackage, expose: Expose) {
const { gateway = Gateway.Tenant, host } = expose;

// Ensure the resource name is valid
const name = sanitizeResourceName(`${pkg.metadata!.name}-${gateway}-${host}`);

return name;
}
23 changes: 15 additions & 8 deletions src/pepr/operator/controllers/istio/virtual-service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import { K8s, Log } from "pepr";

import { UDSConfig } from "../../../config";
import { Expose, Gateway, Istio, UDSPackage } from "../../crd";
import {
Expose,
Gateway,
IstioVirtualService,
IstioHTTP,
IstioHTTPRoute,
UDSPackage,
} from "../../crd";
import { getOwnerRef, sanitizeResourceName } from "../utils";

/**
Expand All @@ -18,7 +25,7 @@ export async function virtualService(pkg: UDSPackage, namespace: string) {
const exposeList = pkg.spec?.network?.expose ?? [];

// Create a list of generated VirtualServices
const payloads: Istio.VirtualService[] = [];
const payloads: IstioVirtualService[] = [];

// Iterate over each exposed service
for (const expose of exposeList) {
Expand All @@ -32,10 +39,10 @@ export async function virtualService(pkg: UDSPackage, namespace: string) {
// Append the domain to the host
const fqdn = `${host}.${domain}`;

const http: Istio.HTTP = { ...advancedHTTP };
const http: IstioHTTP = { ...advancedHTTP };

// Create the route to the service
const route: Istio.HTTPRoute[] = [
const route: IstioHTTPRoute[] = [
{
destination: {
// Use the service name as the host
Expand All @@ -51,7 +58,7 @@ export async function virtualService(pkg: UDSPackage, namespace: string) {
http.route = route;
}

const payload: Istio.VirtualService = {
const payload: IstioVirtualService = {
metadata: {
name,
namespace,
Expand Down Expand Up @@ -85,13 +92,13 @@ export async function virtualService(pkg: UDSPackage, namespace: string) {
Log.debug(payload, `Applying VirtualService ${name}`);

// Apply the VirtualService and force overwrite any existing policy
await K8s(Istio.VirtualService).Apply(payload, { force: true });
await K8s(IstioVirtualService).Apply(payload, { force: true });

payloads.push(payload);
}

// Get all related VirtualServices in the namespace
const virtualServices = await K8s(Istio.VirtualService)
const virtualServices = await K8s(IstioVirtualService)
.InNamespace(namespace)
.WithLabel("uds/package", pkgName)
.Get();
Expand All @@ -104,7 +111,7 @@ export async function virtualService(pkg: UDSPackage, namespace: string) {
// Delete any orphaned VirtualServices
for (const vs of orphanedVS) {
Log.debug(vs, `Deleting orphaned VirtualService ${vs.metadata!.name}`);
await K8s(Istio.VirtualService).Delete(vs);
await K8s(IstioVirtualService).Delete(vs);
}

// Return the list of unique hostnames
Expand Down
142 changes: 142 additions & 0 deletions src/pepr/operator/crd/generated/istio/serviceentry-v1beta1.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
// This file is auto-generated by kubernetes-fluent-client, do not edit manually

import { GenericKind, RegisterKind } from "kubernetes-fluent-client";

export class ServiceEntry extends GenericKind {
/**
* Configuration affecting service registry. See more details at:
* https://istio.io/docs/reference/config/networking/service-entry.html
*/
spec?: Spec;
status?: { [key: string]: any };
}

/**
* Configuration affecting service registry. See more details at:
* https://istio.io/docs/reference/config/networking/service-entry.html
*/
export interface Spec {
/**
* The virtual IP addresses associated with the service.
*/
addresses?: string[];
/**
* One or more endpoints associated with the service.
*/
endpoints?: Endpoint[];
/**
* A list of namespaces to which this service is exported.
*/
exportTo?: string[];
/**
* The hosts associated with the ServiceEntry.
*/
hosts: string[];
/**
* Specify whether the service should be considered external to the mesh or part of the mesh.
*/
location?: Location;
/**
* The ports associated with the external service.
*/
ports?: Port[];
/**
* Service resolution mode for the hosts.
*/
resolution?: Resolution;
/**
* If specified, the proxy will verify that the server certificate's subject alternate name
* matches one of the specified values.
*/
subjectAltNames?: string[];
/**
* Applicable only for MESH_INTERNAL services.
*/
workloadSelector?: WorkloadSelector;
}

export interface Endpoint {
/**
* Address associated with the network endpoint without the port.
*/
address?: string;
/**
* One or more labels associated with the endpoint.
*/
labels?: { [key: string]: string };
/**
* The locality associated with the endpoint.
*/
locality?: string;
/**
* Network enables Istio to group endpoints resident in the same L3 domain/network.
*/
network?: string;
/**
* Set of ports associated with the endpoint.
*/
ports?: { [key: string]: number };
/**
* The service account associated with the workload if a sidecar is present in the workload.
*/
serviceAccount?: string;
/**
* The load balancing weight associated with the endpoint.
*/
weight?: number;
}

/**
* Specify whether the service should be considered external to the mesh or part of the mesh.
*/
export enum Location {
MeshExternal = "MESH_EXTERNAL",
MeshInternal = "MESH_INTERNAL",
}

export interface Port {
/**
* Label assigned to the port.
*/
name: string;
/**
* A valid non-negative integer port number.
*/
number: number;
/**
* The protocol exposed on the port.
*/
protocol?: string;
/**
* The port number on the endpoint where the traffic will be received.
*/
targetPort?: number;
}

/**
* Service resolution mode for the hosts.
*/
export enum Resolution {
DNS = "DNS",
DNSRoundRobin = "DNS_ROUND_ROBIN",
None = "NONE",
Static = "STATIC",
}

/**
* Applicable only for MESH_INTERNAL services.
*/
export interface WorkloadSelector {
/**
* One or more labels that indicate a specific set of pods/VMs on which the configuration
* should be applied.
*/
labels?: { [key: string]: string };
}

RegisterKind(ServiceEntry, {
group: "networking.istio.io",
version: "v1beta1",
kind: "ServiceEntry",
plural: "serviceentries",
});
15 changes: 14 additions & 1 deletion src/pepr/operator/crd/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,18 @@ export {
Exemption as UDSExemption,
} from "./generated/exemption-v1alpha1";

export * as Istio from "./generated/istio/virtualservice-v1beta1";
export {
VirtualService as IstioVirtualService,
HTTPRoute as IstioHTTPRoute,
HTTP as IstioHTTP,
} from "./generated/istio/virtualservice-v1beta1";

export {
ServiceEntry as IstioServiceEntry,
Location as IstioLocation,
Resolution as IstioResolution,
Endpoint as IstioEndpoint,
Port as IstioPort,
} from "./generated/istio/serviceentry-v1beta1";

export * as Prometheus from "./generated/prometheus/servicemonitor-v1";
Loading