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

fix: drop path normalization to MERGE_SLASHES to allow apps to handle encoded slashes #330

Merged
merged 16 commits into from
Apr 11, 2024
Merged
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/istio/values/values.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
meshConfig:
accessLogFile: /dev/stdout
pathNormalization:
normalization: DECODE_AND_MERGE_SLASHES
normalization: MERGE_SLASHES
defaultConfig:
holdApplicationUntilProxyStarts: true
gatewayTopology:
Expand Down
85 changes: 85 additions & 0 deletions src/pepr/operator/controllers/istio/envoy-filter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { K8s, Log } from "pepr";

import { IstioEnvoyFilter, UDSPackage } from "../../crd";
import { getOwnerRef, sanitizeResourceName } from "../utils";

/**
* Creates an EnvoyFilter for the package namespace
*
* @param pkg
* @param namespace
*/
export async function envoyFilter(pkg: UDSPackage, namespace: string) {
const pkgName = pkg.metadata!.name!;
const generation = (pkg.metadata?.generation ?? 0).toString();

// Get the list of exposed services
const retainEncodedSlashes = pkg.spec?.network?.retainEncodedSlashes ?? false;

// Create an EnvoyFilter if retain encoded slashes is false
if (!retainEncodedSlashes) {
const name = sanitizeResourceName(`${pkgName}-decode-slashes`);

const payload: IstioEnvoyFilter.EnvoyFilter = {
metadata: {
name,
namespace,
labels: {
"uds/package": pkgName,
"uds/generation": generation,
},
// Use the CR as the owner ref for each EnvoyFilter
ownerReferences: getOwnerRef(pkg),
},
spec: {
configPatches: [
{
applyTo: IstioEnvoyFilter.ApplyTo.NetworkFilter,
match: {
context: IstioEnvoyFilter.Context.Any,
listener: {
filterChain: {
filter: {
name: "envoy.filters.network.http_connection_manager",
},
},
},
},
patch: {
operation: IstioEnvoyFilter.Operation.Merge,
value: {
typed_config: {
"@type":
"type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
path_with_escaped_slashes_action: "UNESCAPE_AND_FORWARD",
},
},
},
},
],
},
};

Log.debug(payload, `Applying EnvoyFilter ${name}`);

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

// Get all related EnvoyFilters in the namespace
const envoyFilters = await K8s(IstioEnvoyFilter.EnvoyFilter)
.InNamespace(namespace)
.WithLabel("uds/package", pkgName)
.Get();

// Find any orphaned EnvoyFilters (not matching the current generation)
const orphanedEF = envoyFilters.items.filter(
vs => vs.metadata?.labels?.["uds/generation"] !== generation,
);

// Delete any orphaned EnvoyFilters
for (const vs of orphanedEF) {
Log.debug(vs, `Deleting orphaned EnvoyFilter ${vs.metadata!.name}`);
await K8s(IstioEnvoyFilter.EnvoyFilter).Delete(vs);
}
}
6 changes: 3 additions & 3 deletions src/pepr/operator/controllers/istio/injection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ export async function enableInjection(pkg: UDSPackage) {
const annotations = sourceNS.metadata?.annotations || {};
const pkgKey = `uds.dev/pkg-${pkg.metadata.name}`;

// Save the original value of the istio-injection label only if it's not already set
if (!annotations[injectionLabel]) {
annotations[injectionAnnotation] = labels[injectionLabel] || "non-existent";
// If the original namespace injectionLabel did not exist, mark it as non-existent in the annotations
if (!labels[injectionLabel]) {
Racer159 marked this conversation as resolved.
Show resolved Hide resolved
annotations[injectionAnnotation] = "non-existent";
}

// Ensure the namespace is configured
Expand Down
16 changes: 8 additions & 8 deletions src/pepr/operator/controllers/istio/virtual-service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { K8s, Log } from "pepr";

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

/**
Expand All @@ -18,7 +18,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.VirtualService[] = [];

// Iterate over each exposed service
for (const expose of exposeList) {
Expand All @@ -32,10 +32,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: IstioVirtualService.HTTP = { ...advancedHTTP };

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

const payload: Istio.VirtualService = {
const payload: IstioVirtualService.VirtualService = {
metadata: {
name,
namespace,
Expand Down Expand Up @@ -85,13 +85,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.VirtualService).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.VirtualService)
.InNamespace(namespace)
.WithLabel("uds/package", pkgName)
.Get();
Expand All @@ -104,7 +104,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.VirtualService).Delete(vs);
}

// Return the list of unique hostnames
Expand Down
Loading
Loading