From cab3af74985fd8a5c3999c3472363ca180b0c627 Mon Sep 17 00:00:00 2001 From: Jeff McCoy Date: Wed, 7 Feb 2024 12:54:30 -0600 Subject: [PATCH 01/12] feat: introduce advacedHTTP for expose field --- src/test/app-admin.yaml | 29 +++++++++++++++-------------- src/test/app-tenant.yaml | 4 ++-- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/test/app-admin.yaml b/src/test/app-admin.yaml index 19f73401a..3e9786cdf 100644 --- a/src/test/app-admin.yaml +++ b/src/test/app-admin.yaml @@ -17,19 +17,20 @@ spec: gateway: admin host: demo port: 8000 - targetPort: 80 - match: - - name: test-get-and-prefix - method: - # Only allow GET requests - regex: GET - uri: - # Only allow routing to /status/2*, everything else should 404 - prefix: /status/2 - - name: test-exact - uri: - # Only allow routing to /status/410 - exact: /status/410 + podPort: 80 + advancedHTTP: + match: + - name: test-get-and-prefix + method: + # Only allow GET requests + regex: GET + uri: + # Only allow routing to /status/2*, everything else should 404 + prefix: /status/2 + - name: test-exact + uri: + # Only allow routing to /status/410 + exact: /status/410 --- apiVersion: v1 kind: ServiceAccount @@ -46,7 +47,7 @@ spec: ports: - name: http port: 8000 - targetPort: 80 + podPort: 80 selector: app: httpbin --- diff --git a/src/test/app-tenant.yaml b/src/test/app-tenant.yaml index 098c333cb..94f7f5b95 100644 --- a/src/test/app-tenant.yaml +++ b/src/test/app-tenant.yaml @@ -17,7 +17,7 @@ spec: gateway: tenant host: demo port: 8000 - targetPort: 80 + podPort: 80 --- apiVersion: v1 kind: ServiceAccount @@ -37,7 +37,7 @@ spec: ports: - name: http port: 8000 - targetPort: 80 + podPort: 80 selector: app: httpbin --- From 193fc4bc1c3f72b17130a091883c265e88add3c2 Mon Sep 17 00:00:00 2001 From: Jeff McCoy Date: Wed, 7 Feb 2024 12:54:30 -0600 Subject: [PATCH 02/12] feat: introduce advacedHTTP for expose field --- src/pepr/config.ts | 10 +- .../controllers/istio/virtual-service.ts | 20 +- .../operator/controllers/network/policies.ts | 9 +- .../crd/generated/package-v1alpha1.ts | 253 ++++++++++++++++- .../sources/istio/virtualservice-v1beta1.ts | 253 +++++++++++++++-- src/pepr/operator/crd/sources/v1alpha1.ts | 261 ++++++++++-------- src/pepr/operator/crd/validator.ts | 16 +- 7 files changed, 655 insertions(+), 167 deletions(-) diff --git a/src/pepr/config.ts b/src/pepr/config.ts index 2fd53e7e4..a0ae7e4fc 100644 --- a/src/pepr/config.ts +++ b/src/pepr/config.ts @@ -1,8 +1,14 @@ -const isZarfEnv = process.env.UDS_DOMAIN !== "###ZARF_VAR_DOMAIN###"; +import { Log } from "pepr"; + +// We need to handle `npx pepr <>` commands that will not template the env vars +const domain = process.env.UDS_DOMAIN; +const isZarfEnv = domain ? domain !== "###ZARF_VAR_DOMAIN###" : false; export const UDSConfig = { // Ignore the UDS_DOMAIN if not deployed by Zarf - domain: (isZarfEnv && process.env.UDS_DOMAIN) || "uds.dev", + domain: (isZarfEnv && domain) || "uds.dev", // Assume Istio is installed if not deployed by Zarf istioInstalled: !isZarfEnv || process.env.UDS_WITH_ISTIO === "true", }; + +Log.info(UDSConfig, "Loaded UDS Config"); diff --git a/src/pepr/operator/controllers/istio/virtual-service.ts b/src/pepr/operator/controllers/istio/virtual-service.ts index 484b238d3..c552123a0 100644 --- a/src/pepr/operator/controllers/istio/virtual-service.ts +++ b/src/pepr/operator/controllers/istio/virtual-service.ts @@ -22,7 +22,7 @@ export async function virtualService(pkg: UDSPackage, namespace: string) { // Iterate over each exposed service for (const expose of exposeList) { - const { gateway = Gateway.Tenant, host, port, service, match } = expose; + const { gateway = Gateway.Tenant, host, port, service, advancedHTTP = {} } = expose; const name = generateVSName(pkg, expose); @@ -32,6 +32,8 @@ export async function virtualService(pkg: UDSPackage, namespace: string) { // Append the domain to the host const fqdn = `${host}.${domain}`; + const http: Istio.HTTP = { ...advancedHTTP }; + // Create the route to the service const route: Istio.HTTPRoute[] = [ { @@ -44,6 +46,16 @@ export async function virtualService(pkg: UDSPackage, namespace: string) { }, ]; + if (!advancedHTTP.directResponse) { + // Create the route to the service if not using advancedHTTP.directResponse + http.route = route; + } + + // Manage deprecated match field + if (expose.match) { + http.match = expose.match; + } + const payload: Istio.VirtualService = { metadata: { name, @@ -61,7 +73,7 @@ export async function virtualService(pkg: UDSPackage, namespace: string) { // Map the gateway (admin, passthrough or tenant) to the VirtualService gateways: [`istio-${gateway}-gateway/${gateway}-gateway`], // Apply the route to the VirtualService - http: [{ route, match }], + http: [http], }, }; @@ -105,10 +117,10 @@ export async function virtualService(pkg: UDSPackage, namespace: string) { } export function generateVSName(pkg: UDSPackage, expose: Expose) { - const { gateway = Gateway.Tenant, host, port, service, match, description } = expose; + const { gateway = Gateway.Tenant, host, port, service, description, advancedHTTP } = expose; // Ensure the resource name is valid - const matchHash = match?.flatMap(m => m.name).join("-") || ""; + const matchHash = advancedHTTP?.match?.flatMap(m => m.name).join("-") || ""; const nameSuffix = description || `${host}-${port}-${service}-${matchHash}`; const name = sanitizeResourceName(`${pkg.metadata!.name}-${gateway}-${nameSuffix}`); diff --git a/src/pepr/operator/controllers/network/policies.ts b/src/pepr/operator/controllers/network/policies.ts index 1c6461efb..4bd4e0d52 100644 --- a/src/pepr/operator/controllers/network/policies.ts +++ b/src/pepr/operator/controllers/network/policies.ts @@ -35,8 +35,9 @@ export async function networkPolicies(pkg: UDSPackage, namespace: string) { // Generate NetworkPolicies for any VirtualServices that are generated const exposeList = pkg.spec?.network?.expose ?? []; - for (const expose of exposeList) { - const { gateway = Gateway.Tenant, port, podLabels, targetPort } = expose; + // Iterate over each exposed service, excluding directResponse services + for (const expose of exposeList.filter(exp => !exp.advancedHTTP?.directResponse)) { + const { gateway = Gateway.Tenant, port, podLabels = {}, targetPort, podPort } = expose; // Create the NetworkPolicy for the VirtualService const policy: Allow = { @@ -46,8 +47,8 @@ export async function networkPolicies(pkg: UDSPackage, namespace: string) { remotePodLabels: { app: `${gateway}-ingressgateway`, }, - // Use the same port as the VirtualService if targetPort is not set - port: targetPort ?? port, + // Use the same port as the VirtualService if podPort is not set, check deprecated targetPort if podPort is not set + port: podPort ?? targetPort ?? port, description: `${Object.values(podLabels)} Istio ${gateway} gateway`, }; diff --git a/src/pepr/operator/crd/generated/package-v1alpha1.ts b/src/pepr/operator/crd/generated/package-v1alpha1.ts index c4d0f5e48..daa6982f1 100644 --- a/src/pepr/operator/crd/generated/package-v1alpha1.ts +++ b/src/pepr/operator/crd/generated/package-v1alpha1.ts @@ -88,6 +88,10 @@ export enum RemoteGenerated { } export interface Expose { + /** + * Advanced HTTP settings for the route. + */ + advancedHTTP?: AdvancedHTTP; /** * A description of this expose entry, this will become part of the VirtualService name */ @@ -104,28 +108,249 @@ export interface Expose { * Match the incoming request based on custom rules. Not permitted when using the * passthrough gateway. */ - match?: Match[]; + match?: ExposeMatch[]; /** * Labels to match pods in the namespace to apply the policy to. Leave empty to apply to all * pods in the namespace */ - podLabels: { [key: string]: string }; + podLabels?: { [key: string]: string }; + /** + * The service targetPort (pod port). This defaults to port and is only required if the + * service port is different from the pod port (so the NetworkPolicy can be generated + * correctly). + */ + podPort?: number; /** * The port number to expose */ - port: number; + port?: number; /** * The name of the service to expose */ - service: string; + service?: string; /** - * The service targetPort (pod port). This defaults to port and is only required if the - * service port is different from the pod port (so the NetworkPolicy can be generated - * correctly). + * Deprecated: use podPort */ targetPort?: number; } +/** + * Advanced HTTP settings for the route. + */ +export interface AdvancedHTTP { + /** + * Cross-Origin Resource Sharing policy (CORS). + */ + corsPolicy?: CorsPolicy; + /** + * A HTTP rule can either return a direct_response, redirect or forward (default) traffic. + */ + directResponse?: DirectResponse; + headers?: Headers; + /** + * Match the incoming request based on custom rules. Not permitted when using the + * passthrough gateway. + */ + match?: AdvancedHTTPMatch[]; + /** + * Retry policy for HTTP requests. + */ + retries?: Retries; + /** + * Rewrite HTTP URIs and Authority headers. + */ + rewrite?: Rewrite; + /** + * Timeout for HTTP requests, default is disabled. + */ + timeout?: string; + /** + * Weight specifies the relative proportion of traffic to be forwarded to the destination. + */ + weight?: number; +} + +/** + * Cross-Origin Resource Sharing policy (CORS). + */ +export interface CorsPolicy { + /** + * Indicates whether the caller is allowed to send the actual request (not the preflight) + * using credentials. + */ + allowCredentials?: boolean; + /** + * List of HTTP headers that can be used when requesting the resource. + */ + allowHeaders?: string[]; + /** + * List of HTTP methods allowed to access the resource. + */ + allowMethods?: string[]; + allowOrigin?: string[]; + /** + * String patterns that match allowed origins. + */ + allowOrigins?: AllowOrigin[]; + /** + * A list of HTTP headers that the browsers are allowed to access. + */ + exposeHeaders?: string[]; + /** + * Specifies how long the results of a preflight request can be cached. + */ + maxAge?: string; +} + +export interface AllowOrigin { + exact?: string; + prefix?: string; + /** + * RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). + */ + regex?: string; +} + +/** + * A HTTP rule can either return a direct_response, redirect or forward (default) traffic. + */ +export interface DirectResponse { + /** + * Specifies the content of the response body. + */ + body?: Body; + /** + * Specifies the HTTP response status to be returned. + */ + status: number; +} + +/** + * Specifies the content of the response body. + */ +export interface Body { + /** + * response body as base64 encoded bytes. + */ + bytes?: string; + string?: string; +} + +export interface Headers { + request?: Request; + response?: Response; +} + +export interface Request { + add?: { [key: string]: string }; + remove?: string[]; + set?: { [key: string]: string }; +} + +export interface Response { + add?: { [key: string]: string }; + remove?: string[]; + set?: { [key: string]: string }; +} + +export interface AdvancedHTTPMatch { + /** + * Flag to specify whether the URI matching should be case-insensitive. + */ + ignoreUriCase?: boolean; + method?: PurpleMethod; + /** + * The name assigned to a match. + */ + name: string; + /** + * Query parameters for matching. + */ + queryParams?: { [key: string]: PurpleQueryParam }; + uri?: PurpleURI; +} + +export interface PurpleMethod { + exact?: string; + prefix?: string; + /** + * RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). + */ + regex?: string; +} + +export interface PurpleQueryParam { + exact?: string; + prefix?: string; + /** + * RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). + */ + regex?: string; +} + +export interface PurpleURI { + exact?: string; + prefix?: string; + /** + * RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). + */ + regex?: string; +} + +/** + * Retry policy for HTTP requests. + */ +export interface Retries { + /** + * Number of retries to be allowed for a given request. + */ + attempts?: number; + /** + * Timeout per attempt for a given request, including the initial call and any retries. + */ + perTryTimeout?: string; + /** + * Specifies the conditions under which retry takes place. + */ + retryOn?: string; + /** + * Flag to specify whether the retries should retry to other localities. + */ + retryRemoteLocalities?: boolean; +} + +/** + * Rewrite HTTP URIs and Authority headers. + */ +export interface Rewrite { + /** + * rewrite the Authority/Host header with this value. + */ + authority?: string; + /** + * rewrite the path (or the prefix) portion of the URI with this value. + */ + uri?: string; + /** + * rewrite the path portion of the URI with the specified regex. + */ + uriRegexRewrite?: URIRegexRewrite; +} + +/** + * rewrite the path portion of the URI with the specified regex. + */ +export interface URIRegexRewrite { + /** + * RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax). + */ + match?: string; + /** + * The string that should replace into matching portions of original URI. + */ + rewrite?: string; +} + /** * The name of the gateway to expose the service on (default: tenant) */ @@ -135,12 +360,12 @@ export enum Gateway { Tenant = "tenant", } -export interface Match { +export interface ExposeMatch { /** * Flag to specify whether the URI matching should be case-insensitive. */ ignoreUriCase?: boolean; - method?: Method; + method?: FluffyMethod; /** * The name assigned to a match. */ @@ -148,11 +373,11 @@ export interface Match { /** * Query parameters for matching. */ - queryParams?: { [key: string]: QueryParam }; - uri?: URI; + queryParams?: { [key: string]: FluffyQueryParam }; + uri?: FluffyURI; } -export interface Method { +export interface FluffyMethod { exact?: string; prefix?: string; /** @@ -161,7 +386,7 @@ export interface Method { regex?: string; } -export interface QueryParam { +export interface FluffyQueryParam { exact?: string; prefix?: string; /** @@ -170,7 +395,7 @@ export interface QueryParam { regex?: string; } -export interface URI { +export interface FluffyURI { exact?: string; prefix?: string; /** diff --git a/src/pepr/operator/crd/sources/istio/virtualservice-v1beta1.ts b/src/pepr/operator/crd/sources/istio/virtualservice-v1beta1.ts index c8c811e40..4e8fd69cc 100644 --- a/src/pepr/operator/crd/sources/istio/virtualservice-v1beta1.ts +++ b/src/pepr/operator/crd/sources/istio/virtualservice-v1beta1.ts @@ -25,29 +25,244 @@ const matchTemplate = { type: "object", }; -export const virtualServiceHttpMatch: V1JSONSchemaProps = { - description: - "Match the incoming request based on custom rules. Not permitted when using the passthrough gateway.", - items: { - properties: { - ignoreUriCase: { - description: "Flag to specify whether the URI matching should be case-insensitive.", - type: "boolean", +export const advancedHTTP: V1JSONSchemaProps = { + description: "Advanced HTTP settings for the route.", + properties: { + corsPolicy: { + description: "Cross-Origin Resource Sharing policy (CORS).", + properties: { + allowCredentials: { + description: + "Indicates whether the caller is allowed to send the actual request (not the preflight) using credentials.", + nullable: true, + type: "boolean", + }, + allowHeaders: { + description: "List of HTTP headers that can be used when requesting the resource.", + items: { + type: "string", + }, + type: "array", + }, + allowMethods: { + description: "List of HTTP methods allowed to access the resource.", + items: { + type: "string", + }, + type: "array", + }, + allowOrigin: { + items: { + type: "string", + }, + type: "array", + }, + allowOrigins: { + description: "String patterns that match allowed origins.", + items: matchTemplate, + type: "array", + }, + exposeHeaders: { + description: "A list of HTTP headers that the browsers are allowed to access.", + items: { + type: "string", + }, + type: "array", + }, + maxAge: { + description: "Specifies how long the results of a preflight request can be cached.", + type: "string", + }, + }, + type: "object", + }, + directResponse: { + description: + "A HTTP rule can either return a direct_response, redirect or forward (default) traffic.", + properties: { + body: { + description: "Specifies the content of the response body.", + oneOf: [ + { + not: { + anyOf: [ + { + required: ["string"], + }, + { + required: ["bytes"], + }, + ], + }, + }, + { + required: ["string"], + }, + { + required: ["bytes"], + }, + ], + properties: { + bytes: { + description: "response body as base64 encoded bytes.", + format: "binary", + type: "string", + }, + string: { + type: "string", + }, + }, + type: "object", + }, + status: { + description: "Specifies the HTTP response status to be returned.", + type: "integer", + }, }, - method: matchTemplate, - name: { - description: "The name assigned to a match.", - type: "string", + required: ["status"], + type: "object", + }, + headers: { + properties: { + request: { + properties: { + add: { + additionalProperties: { + type: "string", + }, + type: "object", + }, + remove: { + items: { + type: "string", + }, + type: "array", + }, + set: { + additionalProperties: { + type: "string", + }, + type: "object", + }, + }, + type: "object", + }, + response: { + properties: { + add: { + additionalProperties: { + type: "string", + }, + type: "object", + }, + remove: { + items: { + type: "string", + }, + type: "array", + }, + set: { + additionalProperties: { + type: "string", + }, + type: "object", + }, + }, + type: "object", + }, }, - queryParams: { - additionalProperties: matchTemplate, - description: "Query parameters for matching.", + type: "object", + }, + match: { + description: + "Match the incoming request based on custom rules. Not permitted when using the passthrough gateway.", + items: { + properties: { + ignoreUriCase: { + description: "Flag to specify whether the URI matching should be case-insensitive.", + type: "boolean", + }, + method: matchTemplate, + name: { + description: "The name assigned to a match.", + type: "string", + }, + queryParams: { + additionalProperties: matchTemplate, + description: "Query parameters for matching.", + type: "object", + }, + uri: matchTemplate, + }, + required: ["name"], type: "object", }, - uri: matchTemplate, + type: "array", + }, + rewrite: { + description: "Rewrite HTTP URIs and Authority headers.", + properties: { + authority: { + description: "rewrite the Authority/Host header with this value.", + type: "string", + }, + uri: { + description: "rewrite the path (or the prefix) portion of the URI with this value.", + type: "string", + }, + uriRegexRewrite: { + description: "rewrite the path portion of the URI with the specified regex.", + properties: { + match: { + description: + "RE2 style regex-based match (https://github.com/google/re2/wiki/Syntax).", + type: "string", + }, + rewrite: { + description: "The string that should replace into matching portions of original URI.", + type: "string", + }, + }, + type: "object", + }, + }, + type: "object", + }, + retries: { + description: "Retry policy for HTTP requests.", + properties: { + attempts: { + description: "Number of retries to be allowed for a given request.", + format: "int32", + type: "integer", + }, + perTryTimeout: { + description: + "Timeout per attempt for a given request, including the initial call and any retries.", + type: "string", + }, + retryOn: { + description: "Specifies the conditions under which retry takes place.", + type: "string", + }, + retryRemoteLocalities: { + description: "Flag to specify whether the retries should retry to other localities.", + nullable: true, + type: "boolean", + }, + }, + type: "object", + }, + weight: { + description: + "Weight specifies the relative proportion of traffic to be forwarded to the destination.", + format: "int32", + type: "integer", + }, + timeout: { + description: "Timeout for HTTP requests, default is disabled.", + type: "string", }, - required: ["name"], - type: "object", }, - type: "array", + type: "object", }; diff --git a/src/pepr/operator/crd/sources/v1alpha1.ts b/src/pepr/operator/crd/sources/v1alpha1.ts index 809c187be..879a59c96 100644 --- a/src/pepr/operator/crd/sources/v1alpha1.ts +++ b/src/pepr/operator/crd/sources/v1alpha1.ts @@ -1,6 +1,144 @@ import { V1CustomResourceDefinitionVersion, V1JSONSchemaProps } from "@kubernetes/client-node"; -import { virtualServiceHttpMatch } from "./istio/virtualservice-v1beta1"; +import { advancedHTTP } from "./istio/virtualservice-v1beta1"; + +const allow = { + description: "Allow specific traffic (namespace will have a default-deny policy)", + type: "array", + items: { + type: "object", + required: ["direction"], + properties: { + labels: { + description: "The labels to apply to the policy", + type: "object", + additionalProperties: { + type: "string", + }, + }, + description: { + type: "string", + description: "A description of the policy, this will become part of the policy name", + }, + direction: { + description: "The direction of the traffic", + enum: ["Ingress", "Egress"], + type: "string", + }, + podLabels: { + description: + "Labels to match pods in the namespace to apply the policy to. Leave empty to apply to all pods in the namespace", + type: "object", + additionalProperties: { + type: "string", + }, + }, + remoteNamespace: { + description: + "The remote namespace to allow traffic to/from. Use * or empty string to allow all namespaces", + type: "string", + }, + remotePodLabels: { + description: "The remote pod selector labels to allow traffic to/from", + type: "object", + additionalProperties: { + type: "string", + }, + }, + remoteGenerated: { + description: "Custom generated remote selector for the policy", + type: "string", + enum: ["KubeAPI", "IntraNamespace", "CloudMetadata", "Anywhere"], + }, + port: { + description: "The port to allow (protocol is always TCP)", + minimum: 1, + maximum: 65535, + type: "number", + }, + ports: { + description: "A list of ports to allow (protocol is always TCP)", + type: "array", + items: { + minimum: 1, + maximum: 65535, + type: "number", + }, + }, + }, + } as V1JSONSchemaProps, +} as V1JSONSchemaProps; + +const expose = { + type: "array", + description: "Expose a service on an Istio Gateway", + items: { + type: "object", + required: ["host"], + anyOf: [ + { + required: ["service", "podLabels", "port"], + }, + { + required: ["advancedHTTP"], + }, + ], + properties: { + description: { + type: "string", + description: + "A description of this expose entry, this will become part of the VirtualService name", + }, + host: { + description: "The hostname to expose the service on", + type: "string", + }, + gateway: { + description: "The name of the gateway to expose the service on (default: tenant)", + enum: ["admin", "tenant", "passthrough"], + type: "string", + default: "tenant", + }, + service: { + description: "The name of the service to expose", + type: "string", + }, + port: { + description: "The port number to expose", + minimum: 1, + maximum: 65535, + type: "number", + }, + podLabels: { + description: + "Labels to match pods in the namespace to apply the policy to. Leave empty to apply to all pods in the namespace", + type: "object", + additionalProperties: { + type: "string", + }, + }, + podPort: { + description: + "The service targetPort (pod port). This defaults to port and is only required if the service port is different from the pod port (so the NetworkPolicy can be generated correctly).", + minimum: 1, + maximum: 65535, + type: "number", + }, + // Deprecated fields + match: { + description: "Deprecated: use advancedHTTP.match", + ...advancedHTTP.properties?.match, + }, + targetPort: { + description: "Deprecated: use podPort", + minimum: 1, + maximum: 65535, + type: "number", + }, + advancedHTTP, + }, + } as V1JSONSchemaProps, +} as V1JSONSchemaProps; export const v1alpha1: V1CustomResourceDefinitionVersion = { name: "v1alpha1", @@ -67,125 +205,8 @@ export const v1alpha1: V1CustomResourceDefinitionVersion = { type: "object", description: "Network configuration for the package", properties: { - expose: { - type: "array", - description: "Expose a service on an Istio Gateway", - items: { - type: "object", - required: ["service", "podLabels", "host", "port"], - properties: { - description: { - type: "string", - description: - "A description of this expose entry, this will become part of the VirtualService name", - }, - service: { - description: "The name of the service to expose", - type: "string", - }, - podLabels: { - description: - "Labels to match pods in the namespace to apply the policy to. Leave empty to apply to all pods in the namespace", - type: "object", - additionalProperties: { - type: "string", - }, - }, - port: { - description: "The port number to expose", - minimum: 1, - maximum: 65535, - type: "number", - }, - targetPort: { - description: - "The service targetPort (pod port). This defaults to port and is only required if the service port is different from the pod port (so the NetworkPolicy can be generated correctly).", - minimum: 1, - maximum: 65535, - type: "number", - }, - gateway: { - description: - "The name of the gateway to expose the service on (default: tenant)", - enum: ["admin", "tenant", "passthrough"], - type: "string", - default: "tenant", - }, - host: { - description: "The hostname to expose the service on", - type: "string", - }, - match: virtualServiceHttpMatch, - }, - }, - }, - allow: { - description: "Allow specific traffic (namespace will have a default-deny policy)", - type: "array", - items: { - type: "object", - required: ["direction"], - properties: { - labels: { - description: "The labels to apply to the policy", - type: "object", - additionalProperties: { - type: "string", - }, - }, - description: { - type: "string", - description: - "A description of the policy, this will become part of the policy name", - }, - direction: { - description: "The direction of the traffic", - enum: ["Ingress", "Egress"], - type: "string", - }, - podLabels: { - description: - "Labels to match pods in the namespace to apply the policy to. Leave empty to apply to all pods in the namespace", - type: "object", - additionalProperties: { - type: "string", - }, - }, - remoteNamespace: { - description: - "The remote namespace to allow traffic to/from. Use * or empty string to allow all namespaces", - type: "string", - }, - remotePodLabels: { - description: "The remote pod selector labels to allow traffic to/from", - type: "object", - additionalProperties: { - type: "string", - }, - }, - remoteGenerated: { - description: "Custom generated remote selector for the policy", - type: "string", - enum: ["KubeAPI", "IntraNamespace", "CloudMetadata", "Anywhere"], - }, - port: { - description: "The port to allow (protocol is always TCP)", - minimum: 1, - maximum: 65535, - type: "number", - }, - ports: { - description: "A list of ports to allow (protocol is always TCP)", - type: "array", - items: { - minimum: 1, - maximum: 65535, - type: "number", - }, - }, - }, - }, - }, + expose, + allow, }, }, }, diff --git a/src/pepr/operator/crd/validator.ts b/src/pepr/operator/crd/validator.ts index 9de20dace..282800a4e 100644 --- a/src/pepr/operator/crd/validator.ts +++ b/src/pepr/operator/crd/validator.ts @@ -21,13 +21,21 @@ export async function validator(req: PeprValidateRequest) { for (const expose of exposeList) { if (expose.gateway === Gateway.Passthrough) { - // This is an HTTPMatch rule, not TLSMatchAttribute - // https://istio.io/latest/docs/reference/config/networking/virtual-service/#HTTPMatchRequest - if (expose.match) { - return req.Deny("match cannot be used with passthrough gateway"); + if (expose.advancedHTTP) { + return req.Deny("advancedHTTP cannot be used with passthrough gateway"); } } + // directResponse cannot be combined with service, port or pod configs + if ( + expose.advancedHTTP?.directResponse && + (expose.service || expose.podLabels || expose.port || expose.podPort || expose.targetPort) + ) { + return req.Deny( + "directResponse cannot be combined with service, port, podLabels, podPort or targetPort", + ); + } + // Ensure the service name is unique const name = generateVSName(req.Raw, expose); if (virtualServiceNames.has(name)) { From 682bff10921f2a41ca7134e1eeb2a8417c3d9912 Mon Sep 17 00:00:00 2001 From: Jeff McCoy Date: Thu, 8 Feb 2024 02:07:46 -0600 Subject: [PATCH 03/12] podLabels -> selector & remotePodLabels -> remoteSelector --- src/grafana/chart/templates/uds-package.yaml | 10 ++--- src/loki/chart/templates/uds-package.yaml | 16 +++---- .../chart/templates/uds-package.yaml | 6 +-- .../chart/templates/uds-package.yaml | 12 +++--- src/pepr/operator/README.md | 6 +-- .../network/defaults/allow-egress-dns.ts | 2 +- .../network/defaults/allow-egress-istiod.ts | 2 +- .../allow-ingress-sidecar-monitoring.ts | 2 +- .../operator/controllers/network/generate.ts | 12 +++--- .../operator/controllers/network/policies.ts | 12 +++--- .../crd/generated/package-v1alpha1.ts | 28 ++++++++----- src/pepr/operator/crd/sources/v1alpha1.ts | 42 +++++++++++++------ src/pepr/operator/crd/validator.ts | 12 +++--- .../chart/templates/uds-package.yaml | 16 +++---- src/promtail/chart/templates/uds-package.yaml | 8 ++-- src/test/app-admin.yaml | 6 +-- src/test/app-tenant.yaml | 6 +-- 17 files changed, 110 insertions(+), 88 deletions(-) diff --git a/src/grafana/chart/templates/uds-package.yaml b/src/grafana/chart/templates/uds-package.yaml index 504c79b7d..6655dc661 100644 --- a/src/grafana/chart/templates/uds-package.yaml +++ b/src/grafana/chart/templates/uds-package.yaml @@ -7,7 +7,7 @@ spec: network: expose: - service: grafana - podLabels: + selector: app.kubernetes.io/name: grafana host: grafana gateway: admin @@ -16,22 +16,22 @@ spec: allow: - direction: Ingress - podLabels: + selector: app.kubernetes.io/name: grafana remoteNamespace: tempo - remotePodLabels: + remoteSelector: app.kubernetes.io/name: tempo port: 9090 description: "Tempo Datasource" - direction: Egress - podLabels: + selector: app.kubernetes.io/name: grafana remoteGenerated: Anywhere - direction: Egress remoteNamespace: tempo - remotePodLabels: + remoteSelector: app.kubernetes.io/name: tempo port: 9411 description: "Tempo" diff --git a/src/loki/chart/templates/uds-package.yaml b/src/loki/chart/templates/uds-package.yaml index ee96b55cd..8f30a3d0c 100644 --- a/src/loki/chart/templates/uds-package.yaml +++ b/src/loki/chart/templates/uds-package.yaml @@ -14,20 +14,20 @@ spec: remoteGenerated: IntraNamespace - direction: Ingress - podLabels: + selector: app.kubernetes.io/name: loki remoteNamespace: grafana - remotePodLabels: + remoteSelector: app.kubernetes.io/name: grafana ports: - 8080 description: "Grafana Log Queries" - direction: Ingress - podLabels: + selector: app.kubernetes.io/name: loki remoteNamespace: monitoring - remotePodLabels: + remoteSelector: app.kubernetes.io/name: prometheus ports: - 3100 @@ -35,10 +35,10 @@ spec: description: "Prometheus Metrics" - direction: Ingress - podLabels: + selector: app.kubernetes.io/name: loki remoteNamespace: promtail - remotePodLabels: + remoteSelector: app.kubernetes.io/name: promtail ports: - 8080 @@ -46,13 +46,13 @@ spec: # Todo: wide open for now for pushing to s3 - direction: Egress - podLabels: + selector: app.kubernetes.io/name: loki remoteGenerated: Anywhere - direction: Egress remoteNamespace: tempo - remotePodLabels: + remoteSelector: app.kubernetes.io/name: tempo port: 9411 description: "Tempo" diff --git a/src/metrics-server/chart/templates/uds-package.yaml b/src/metrics-server/chart/templates/uds-package.yaml index 088f1e5bb..b213992f3 100644 --- a/src/metrics-server/chart/templates/uds-package.yaml +++ b/src/metrics-server/chart/templates/uds-package.yaml @@ -7,17 +7,17 @@ spec: network: allow: - direction: Egress - podLabels: + selector: app.kubernetes.io/name: metrics-server # todo: evaluate an "all nodes" generated rule remoteGenerated: Anywhere port: 10250 - direction: Egress - podLabels: + selector: app.kubernetes.io/name: metrics-server remoteGenerated: KubeAPI - direction: Ingress - podLabels: + selector: app.kubernetes.io/name: metrics-server # todo: evaluate a "KubeAPI" _ingress_ generated rule remoteGenerated: Anywhere diff --git a/src/neuvector/chart/templates/uds-package.yaml b/src/neuvector/chart/templates/uds-package.yaml index 510222177..631410e45 100644 --- a/src/neuvector/chart/templates/uds-package.yaml +++ b/src/neuvector/chart/templates/uds-package.yaml @@ -7,7 +7,7 @@ spec: network: expose: - service: neuvector-service-webui - podLabels: + selector: app: neuvector-manager-pod gateway: admin host: neuvector @@ -23,26 +23,26 @@ spec: - direction: Egress remoteGenerated: KubeAPI - podLabels: + selector: app: neuvector-controller-pod - direction: Egress remoteGenerated: KubeAPI - podLabels: + selector: app: neuvector-updater-pod - direction: Ingress remoteNamespace: monitoring - remotePodLabels: + remoteSelector: app: prometheus - podLabels: + selector: app: neuvector-prometheus-exporter-pod port: 8068 description: "Prometheus Metrics" - direction: Egress remoteNamespace: tempo - remotePodLabels: + remoteSelector: app.kubernetes.io/name: tempo port: 9411 description: "Tempo" diff --git a/src/pepr/operator/README.md b/src/pepr/operator/README.md index dbb64dc2c..436e51a7a 100644 --- a/src/pepr/operator/README.md +++ b/src/pepr/operator/README.md @@ -20,7 +20,7 @@ spec: network: expose: - service: grafana - podLabels: + selector: app.kubernetes.io/name: grafana host: grafana gateway: admin @@ -29,13 +29,13 @@ spec: allow: - direction: Egress - podLabels: + selector: app.kubernetes.io/name: grafana remoteGenerated: Anywhere - direction: Egress remoteNamespace: tempo - remotePodLabels: + remoteSelector: app.kubernetes.io/name: tempo port: 9411 description: "Tempo" diff --git a/src/pepr/operator/controllers/network/defaults/allow-egress-dns.ts b/src/pepr/operator/controllers/network/defaults/allow-egress-dns.ts index 83f3d1e68..a4fc5684e 100644 --- a/src/pepr/operator/controllers/network/defaults/allow-egress-dns.ts +++ b/src/pepr/operator/controllers/network/defaults/allow-egress-dns.ts @@ -6,7 +6,7 @@ export const allowEgressDNS = (namespace: string) => { direction: Direction.Egress, description: "DNS lookup via CoreDNS", remoteNamespace: "kube-system", - remotePodLabels: { + remoteSelector: { "k8s-app": "kube-dns", }, port: 53, diff --git a/src/pepr/operator/controllers/network/defaults/allow-egress-istiod.ts b/src/pepr/operator/controllers/network/defaults/allow-egress-istiod.ts index 01a77c2ef..775395aa4 100644 --- a/src/pepr/operator/controllers/network/defaults/allow-egress-istiod.ts +++ b/src/pepr/operator/controllers/network/defaults/allow-egress-istiod.ts @@ -6,7 +6,7 @@ export const allowEgressIstiod = (namespace: string) => direction: Direction.Egress, description: "Istiod communication", remoteNamespace: "istio-system", - remotePodLabels: { + remoteSelector: { istio: "pilot", }, port: 15012, diff --git a/src/pepr/operator/controllers/network/defaults/allow-ingress-sidecar-monitoring.ts b/src/pepr/operator/controllers/network/defaults/allow-ingress-sidecar-monitoring.ts index e3d232cd1..6758a3c77 100644 --- a/src/pepr/operator/controllers/network/defaults/allow-ingress-sidecar-monitoring.ts +++ b/src/pepr/operator/controllers/network/defaults/allow-ingress-sidecar-monitoring.ts @@ -6,7 +6,7 @@ export const allowIngressSidecarMonitoring = (namespace: string) => direction: Direction.Ingress, description: "Sidecar monitoring", remoteNamespace: "monitoring", - remotePodLabels: { + remoteSelector: { app: "prometheus", }, port: 15020, diff --git a/src/pepr/operator/controllers/network/generate.ts b/src/pepr/operator/controllers/network/generate.ts index 582336e60..d10cc48b7 100644 --- a/src/pepr/operator/controllers/network/generate.ts +++ b/src/pepr/operator/controllers/network/generate.ts @@ -23,7 +23,7 @@ export function generate(namespace: string, policy: Allow): kind.NetworkPolicy { spec: { policyTypes: [policy.direction], podSelector: { - matchLabels: policy.podLabels, + matchLabels: policy.selector, }, }, }; @@ -53,11 +53,11 @@ export function generate(namespace: string, policy: Allow): kind.NetworkPolicy { peers.push({ namespaceSelector }); } - // Add the remotePodLabels if they exist - if (policy.remotePodLabels) { + // Add the remoteSelector if they exist + if (policy.remoteSelector) { peers.push({ podSelector: { - matchLabels: policy.remotePodLabels, + matchLabels: policy.remoteSelector, }, }); } @@ -122,10 +122,10 @@ export function generateName(policy: Allow) { policy.description || // Otherwise use the direction, and combination of remote properties [ - Object.values(policy.podLabels || ["all pods"]), + Object.values(policy.selector || ["all pods"]), policy.remoteGenerated || [ policy.remoteNamespace, - Object.values(policy.remotePodLabels || ["all pods"]), + Object.values(policy.remoteSelector || ["all pods"]), ], ] // Flatten the array diff --git a/src/pepr/operator/controllers/network/policies.ts b/src/pepr/operator/controllers/network/policies.ts index 4bd4e0d52..f4985b1b0 100644 --- a/src/pepr/operator/controllers/network/policies.ts +++ b/src/pepr/operator/controllers/network/policies.ts @@ -37,19 +37,19 @@ export async function networkPolicies(pkg: UDSPackage, namespace: string) { const exposeList = pkg.spec?.network?.expose ?? []; // Iterate over each exposed service, excluding directResponse services for (const expose of exposeList.filter(exp => !exp.advancedHTTP?.directResponse)) { - const { gateway = Gateway.Tenant, port, podLabels = {}, targetPort, podPort } = expose; + const { gateway = Gateway.Tenant, port, selector = {}, targetPort } = expose; // Create the NetworkPolicy for the VirtualService const policy: Allow = { direction: Direction.Ingress, - podLabels, + selector, remoteNamespace: `istio-${gateway}-gateway`, - remotePodLabels: { + remoteSelector: { app: `${gateway}-ingressgateway`, }, - // Use the same port as the VirtualService if podPort is not set, check deprecated targetPort if podPort is not set - port: podPort ?? targetPort ?? port, - description: `${Object.values(podLabels)} Istio ${gateway} gateway`, + // Use the same port as the VirtualService if targetPort is not set + port: targetPort ?? port, + description: `${Object.values(selector)} Istio ${gateway} gateway`, }; // Generate the policy diff --git a/src/pepr/operator/crd/generated/package-v1alpha1.ts b/src/pepr/operator/crd/generated/package-v1alpha1.ts index daa6982f1..0a08e5cd9 100644 --- a/src/pepr/operator/crd/generated/package-v1alpha1.ts +++ b/src/pepr/operator/crd/generated/package-v1alpha1.ts @@ -42,8 +42,7 @@ export interface Allow { */ labels?: { [key: string]: string }; /** - * Labels to match pods in the namespace to apply the policy to. Leave empty to apply to all - * pods in the namespace + * Deprectated: use selector */ podLabels?: { [key: string]: string }; /** @@ -64,9 +63,18 @@ export interface Allow { */ remoteNamespace?: string; /** - * The remote pod selector labels to allow traffic to/from + * Deprectated: use remoteSelector */ remotePodLabels?: { [key: string]: string }; + /** + * The remote pod selector labels to allow traffic to/from + */ + remoteSelector?: { [key: string]: string }; + /** + * Labels to match pods in the namespace to apply the policy to. Leave empty to apply to all + * pods in the namespace + */ + selector?: { [key: string]: string }; } /** @@ -110,20 +118,18 @@ export interface Expose { */ match?: ExposeMatch[]; /** - * Labels to match pods in the namespace to apply the policy to. Leave empty to apply to all - * pods in the namespace + * Deprectated: use selector */ podLabels?: { [key: string]: string }; - /** - * The service targetPort (pod port). This defaults to port and is only required if the - * service port is different from the pod port (so the NetworkPolicy can be generated - * correctly). - */ - podPort?: number; /** * The port number to expose */ port?: number; + /** + * Labels to match pods in the namespace to apply the policy to. Leave empty to apply to all + * pods in the namespace + */ + selector?: { [key: string]: string }; /** * The name of the service to expose */ diff --git a/src/pepr/operator/crd/sources/v1alpha1.ts b/src/pepr/operator/crd/sources/v1alpha1.ts index 879a59c96..092ca9a1d 100644 --- a/src/pepr/operator/crd/sources/v1alpha1.ts +++ b/src/pepr/operator/crd/sources/v1alpha1.ts @@ -25,7 +25,7 @@ const allow = { enum: ["Ingress", "Egress"], type: "string", }, - podLabels: { + selector: { description: "Labels to match pods in the namespace to apply the policy to. Leave empty to apply to all pods in the namespace", type: "object", @@ -38,7 +38,7 @@ const allow = { "The remote namespace to allow traffic to/from. Use * or empty string to allow all namespaces", type: "string", }, - remotePodLabels: { + remoteSelector: { description: "The remote pod selector labels to allow traffic to/from", type: "object", additionalProperties: { @@ -65,6 +65,21 @@ const allow = { type: "number", }, }, + // Deprecated fields + podLabels: { + description: "Deprectated: use selector", + type: "object", + additionalProperties: { + type: "string", + }, + }, + remotePodLabels: { + description: "Deprectated: use remoteSelector", + type: "object", + additionalProperties: { + type: "string", + }, + }, }, } as V1JSONSchemaProps, } as V1JSONSchemaProps; @@ -79,6 +94,9 @@ const expose = { { required: ["service", "podLabels", "port"], }, + { + required: ["service", "selector", "port"], + }, { required: ["advancedHTTP"], }, @@ -109,7 +127,7 @@ const expose = { maximum: 65535, type: "number", }, - podLabels: { + selector: { description: "Labels to match pods in the namespace to apply the policy to. Leave empty to apply to all pods in the namespace", type: "object", @@ -117,23 +135,23 @@ const expose = { type: "string", }, }, - podPort: { - description: - "The service targetPort (pod port). This defaults to port and is only required if the service port is different from the pod port (so the NetworkPolicy can be generated correctly).", + targetPort: { + description: "Deprecated: use podPort", minimum: 1, maximum: 65535, type: "number", }, - // Deprecated fields + // Deprecated field match: { description: "Deprecated: use advancedHTTP.match", ...advancedHTTP.properties?.match, }, - targetPort: { - description: "Deprecated: use podPort", - minimum: 1, - maximum: 65535, - type: "number", + podLabels: { + description: "Deprectated: use selector", + type: "object", + additionalProperties: { + type: "string", + }, }, advancedHTTP, }, diff --git a/src/pepr/operator/crd/validator.ts b/src/pepr/operator/crd/validator.ts index 282800a4e..c05fb4ceb 100644 --- a/src/pepr/operator/crd/validator.ts +++ b/src/pepr/operator/crd/validator.ts @@ -29,11 +29,9 @@ export async function validator(req: PeprValidateRequest) { // directResponse cannot be combined with service, port or pod configs if ( expose.advancedHTTP?.directResponse && - (expose.service || expose.podLabels || expose.port || expose.podPort || expose.targetPort) + (expose.service || expose.selector || expose.port || expose.targetPort) ) { - return req.Deny( - "directResponse cannot be combined with service, port, podLabels, podPort or targetPort", - ); + return req.Deny("directResponse cannot be combined with service, port, selector, targetPort"); } // Ensure the service name is unique @@ -56,9 +54,9 @@ export async function validator(req: PeprValidateRequest) { const networkPolicyNames = new Set(); for (const policy of networkPolicy) { - // remoteGenerated cannot be combined with remoteNamespace or remotePodLabels - if (policy.remoteGenerated && (policy.remoteNamespace || policy.remotePodLabels)) { - return req.Deny("remoteGenerated cannot be combined with remoteNamespace or remotePodLabels"); + // remoteGenerated cannot be combined with remoteNamespace or remoteSelector + if (policy.remoteGenerated && (policy.remoteNamespace || policy.remoteSelector)) { + return req.Deny("remoteGenerated cannot be combined with remoteNamespace or remoteSelector"); } // Ensure the policy name is unique diff --git a/src/prometheus-stack/chart/templates/uds-package.yaml b/src/prometheus-stack/chart/templates/uds-package.yaml index 99ddc3338..f021ddf01 100644 --- a/src/prometheus-stack/chart/templates/uds-package.yaml +++ b/src/prometheus-stack/chart/templates/uds-package.yaml @@ -15,43 +15,43 @@ spec: - direction: Egress remoteGenerated: KubeAPI - podLabels: + selector: app: kube-prometheus-stack-operator - direction: Egress remoteGenerated: KubeAPI - podLabels: + selector: app.kubernetes.io/name: kube-state-metrics - direction: Egress remoteGenerated: KubeAPI - podLabels: + selector: app: kube-prometheus-stack-admission-create - direction: Egress remoteGenerated: KubeAPI - podLabels: + selector: app: kube-prometheus-stack-admission-patch # todo: lockdown egress to scrape targets - direction: Egress remoteNamespace: "" - podLabels: + selector: app.kubernetes.io/name: prometheus description: "Metrics Scraping" - direction: Ingress remoteNamespace: grafana - remotePodLabels: + remoteSelector: app.kubernetes.io/name: grafana - podLabels: + selector: app.kubernetes.io/name: prometheus port: 9090 description: "Grafana Metrics Queries" - direction: Egress remoteNamespace: tempo - remotePodLabels: + remoteSelector: app.kubernetes.io/name: tempo port: 9411 description: "Tempo" diff --git a/src/promtail/chart/templates/uds-package.yaml b/src/promtail/chart/templates/uds-package.yaml index a9932829a..65669e985 100644 --- a/src/promtail/chart/templates/uds-package.yaml +++ b/src/promtail/chart/templates/uds-package.yaml @@ -10,7 +10,7 @@ spec: podSelector: app.kubernetes.io/name: promtail remoteNamespace: monitoring - remotePodLabels: + remoteSelector: app.kubernetes.io/name: prometheus port: 3101 description: "Prometheus Metrics" @@ -22,16 +22,16 @@ spec: - direction: Egress remoteNamespace: tempo - remotePodLabels: + remoteSelector: app.kubernetes.io/name: tempo port: 9411 description: "Tempo" - direction: Egress - podLabels: + selector: app.kubernetes.io/name: promtail remoteNamespace: loki - remotePodLabels: + remoteSelector: app.kubernetes.io/name: loki port: 8080 description: "Write Logs to Loki" diff --git a/src/test/app-admin.yaml b/src/test/app-admin.yaml index 3e9786cdf..b9ebb1ecd 100644 --- a/src/test/app-admin.yaml +++ b/src/test/app-admin.yaml @@ -12,12 +12,12 @@ spec: network: expose: - service: httpbin - podLabels: + selector: app: httpbin gateway: admin host: demo port: 8000 - podPort: 80 + targetPort: 80 advancedHTTP: match: - name: test-get-and-prefix @@ -47,7 +47,7 @@ spec: ports: - name: http port: 8000 - podPort: 80 + targetPort: 80 selector: app: httpbin --- diff --git a/src/test/app-tenant.yaml b/src/test/app-tenant.yaml index 94f7f5b95..c0c531f81 100644 --- a/src/test/app-tenant.yaml +++ b/src/test/app-tenant.yaml @@ -12,12 +12,12 @@ spec: network: expose: - service: httpbin - podLabels: + selector: app: httpbin gateway: tenant host: demo port: 8000 - podPort: 80 + targetPort: 80 --- apiVersion: v1 kind: ServiceAccount @@ -37,7 +37,7 @@ spec: ports: - name: http port: 8000 - podPort: 80 + targetPort: 80 selector: app: httpbin --- From 82a13d22617fdd5f9c4d9ba45a5fedfd04899744 Mon Sep 17 00:00:00 2001 From: Jeff McCoy Date: Thu, 8 Feb 2024 02:20:36 -0600 Subject: [PATCH 04/12] hotfix retry action https://github.com/Wandalen/wretry.action/issues/129 --- .github/actions/setup/action.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/setup/action.yaml b/.github/actions/setup/action.yaml index 0c17d0909..21455f9b8 100644 --- a/.github/actions/setup/action.yaml +++ b/.github/actions/setup/action.yaml @@ -40,7 +40,7 @@ runs: password: ${{ inputs.gh_token }} # Retries intermittent registry1 login action - - uses: Wandalen/wretry.action@v1 + - uses: Wandalen/wretry.action@v1.4.1 with: attempt_limit: 3 action: docker/login-action@v3 From 9ebcbe3de50e4f60f787719e05869a4dc68e6837 Mon Sep 17 00:00:00 2001 From: Jeff McCoy Date: Thu, 8 Feb 2024 02:28:30 -0600 Subject: [PATCH 05/12] revert to pinned sha that worked for now --- .github/actions/setup/action.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/setup/action.yaml b/.github/actions/setup/action.yaml index 21455f9b8..6bf636f8e 100644 --- a/.github/actions/setup/action.yaml +++ b/.github/actions/setup/action.yaml @@ -40,7 +40,7 @@ runs: password: ${{ inputs.gh_token }} # Retries intermittent registry1 login action - - uses: Wandalen/wretry.action@v1.4.1 + - uses: Wandalen/wretry.action@0f47298855b1e679b5f7d95b2135e3c0813b0f01 with: attempt_limit: 3 action: docker/login-action@v3 From bf762eeb907791da6b22ddb7afb234c3c050479a Mon Sep 17 00:00:00 2001 From: Micah Nagel Date: Thu, 8 Feb 2024 11:34:25 -0700 Subject: [PATCH 06/12] fix: registry login actions --- .github/actions/setup/action.yaml | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/.github/actions/setup/action.yaml b/.github/actions/setup/action.yaml index a6f1170e5..cc4124b13 100644 --- a/.github/actions/setup/action.yaml +++ b/.github/actions/setup/action.yaml @@ -33,15 +33,9 @@ runs: run: brew install defenseunicorns/tap/uds@0.8.1 - name: Login to GHCR - uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3 - with: - registry: ghcr.io - username: dummy - password: ${{ inputs.gh_token }} + shell: bash + run: echo "${{ inputs.gh_token }}" | uds zarf tools registry login ghcr.io -u dummy --password-stdin - name: Login to registry1 - uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3 - with: - registry: registry1.dso.mil - username: ${{ inputs.ib_user }} - password: ${{ inputs.ib_password }} + shell: bash + run: echo "${{ inputs.ib_password }}" | uds zarf tools registry login registry1.dso.mil -u ${{ inputs.ib_user }} --password-stdin From db74cc6084c992050c88315e034bd93b1834e402 Mon Sep 17 00:00:00 2001 From: Micah Nagel Date: Thu, 8 Feb 2024 12:21:02 -0700 Subject: [PATCH 07/12] Revert "fix: registry login actions" This reverts commit bf762eeb907791da6b22ddb7afb234c3c050479a. --- .github/actions/setup/action.yaml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/actions/setup/action.yaml b/.github/actions/setup/action.yaml index cc4124b13..a6f1170e5 100644 --- a/.github/actions/setup/action.yaml +++ b/.github/actions/setup/action.yaml @@ -33,9 +33,15 @@ runs: run: brew install defenseunicorns/tap/uds@0.8.1 - name: Login to GHCR - shell: bash - run: echo "${{ inputs.gh_token }}" | uds zarf tools registry login ghcr.io -u dummy --password-stdin + uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3 + with: + registry: ghcr.io + username: dummy + password: ${{ inputs.gh_token }} - name: Login to registry1 - shell: bash - run: echo "${{ inputs.ib_password }}" | uds zarf tools registry login registry1.dso.mil -u ${{ inputs.ib_user }} --password-stdin + uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3 + with: + registry: registry1.dso.mil + username: ${{ inputs.ib_user }} + password: ${{ inputs.ib_password }} From eaed85a5dbfd2cddd434a606ac30d408415d2589 Mon Sep 17 00:00:00 2001 From: Megamind <882485+jeff-mccoy@users.noreply.github.com> Date: Thu, 8 Feb 2024 18:07:35 -0600 Subject: [PATCH 08/12] Update v1alpha1.ts Co-authored-by: Micah Nagel --- src/pepr/operator/crd/sources/v1alpha1.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pepr/operator/crd/sources/v1alpha1.ts b/src/pepr/operator/crd/sources/v1alpha1.ts index 092ca9a1d..fe9679e60 100644 --- a/src/pepr/operator/crd/sources/v1alpha1.ts +++ b/src/pepr/operator/crd/sources/v1alpha1.ts @@ -67,7 +67,7 @@ const allow = { }, // Deprecated fields podLabels: { - description: "Deprectated: use selector", + description: "Deprecated: use selector", type: "object", additionalProperties: { type: "string", From b11a9eb064bf6360657e62bf8230754452eb8a21 Mon Sep 17 00:00:00 2001 From: Megamind <882485+jeff-mccoy@users.noreply.github.com> Date: Thu, 8 Feb 2024 18:07:44 -0600 Subject: [PATCH 09/12] Update v1alpha1.ts Co-authored-by: Micah Nagel --- src/pepr/operator/crd/sources/v1alpha1.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pepr/operator/crd/sources/v1alpha1.ts b/src/pepr/operator/crd/sources/v1alpha1.ts index fe9679e60..a3c37f9b0 100644 --- a/src/pepr/operator/crd/sources/v1alpha1.ts +++ b/src/pepr/operator/crd/sources/v1alpha1.ts @@ -74,7 +74,7 @@ const allow = { }, }, remotePodLabels: { - description: "Deprectated: use remoteSelector", + description: "Deprecated: use remoteSelector", type: "object", additionalProperties: { type: "string", From 6d45fb5e858e02854749f8015eee4d3e4279dd9d Mon Sep 17 00:00:00 2001 From: Megamind <882485+jeff-mccoy@users.noreply.github.com> Date: Thu, 8 Feb 2024 18:07:52 -0600 Subject: [PATCH 10/12] Update v1alpha1.ts Co-authored-by: Micah Nagel --- src/pepr/operator/crd/sources/v1alpha1.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pepr/operator/crd/sources/v1alpha1.ts b/src/pepr/operator/crd/sources/v1alpha1.ts index a3c37f9b0..ba47235c5 100644 --- a/src/pepr/operator/crd/sources/v1alpha1.ts +++ b/src/pepr/operator/crd/sources/v1alpha1.ts @@ -147,7 +147,7 @@ const expose = { ...advancedHTTP.properties?.match, }, podLabels: { - description: "Deprectated: use selector", + description: "Deprecated: use selector", type: "object", additionalProperties: { type: "string", From 01db61e842f8c3e9da8018dd388e85600a269d91 Mon Sep 17 00:00:00 2001 From: Jeff McCoy Date: Thu, 8 Feb 2024 23:01:35 -0600 Subject: [PATCH 11/12] handle deprecation migrations for validate/reconcile --- .../controllers/istio/virtual-service.ts | 5 --- src/pepr/operator/crd/migrate.ts | 38 +++++++++++++++++++ src/pepr/operator/crd/validator.ts | 11 ++++-- src/pepr/operator/reconciler.ts | 3 ++ 4 files changed, 48 insertions(+), 9 deletions(-) create mode 100644 src/pepr/operator/crd/migrate.ts diff --git a/src/pepr/operator/controllers/istio/virtual-service.ts b/src/pepr/operator/controllers/istio/virtual-service.ts index c552123a0..c36f3fcc3 100644 --- a/src/pepr/operator/controllers/istio/virtual-service.ts +++ b/src/pepr/operator/controllers/istio/virtual-service.ts @@ -51,11 +51,6 @@ export async function virtualService(pkg: UDSPackage, namespace: string) { http.route = route; } - // Manage deprecated match field - if (expose.match) { - http.match = expose.match; - } - const payload: Istio.VirtualService = { metadata: { name, diff --git a/src/pepr/operator/crd/migrate.ts b/src/pepr/operator/crd/migrate.ts new file mode 100644 index 000000000..5b05f8034 --- /dev/null +++ b/src/pepr/operator/crd/migrate.ts @@ -0,0 +1,38 @@ +import { UDSPackage } from "."; + +/** + * Migrates the package to the latest version + * + * @param pkg the package to migrate + * @returns + */ +export function migrate(pkg: UDSPackage) { + const exposeList = pkg.spec?.network?.expose ?? []; + + for (const expose of exposeList) { + // Migrate expose[].match -> expose[].advancedHTTP.match + if (expose.match) { + expose.advancedHTTP = expose.advancedHTTP ?? {}; + expose.advancedHTTP.match = expose.match; + delete expose.match; + } + } + + const allowList = pkg.spec?.network?.allow ?? []; + + for (const allow of allowList) { + // Migrate allow[].podLabels -> allow[].selector + if (allow.podLabels) { + allow.selector = allow.podLabels; + delete allow.podLabels; + } + + // Migrate allow[].remotePodLabels -> allow[].remoteSelector + if (allow.remotePodLabels) { + allow.remoteSelector = allow.remotePodLabels; + delete allow.remotePodLabels; + } + } + + return pkg; +} diff --git a/src/pepr/operator/crd/validator.ts b/src/pepr/operator/crd/validator.ts index c05fb4ceb..d06b83c6d 100644 --- a/src/pepr/operator/crd/validator.ts +++ b/src/pepr/operator/crd/validator.ts @@ -4,17 +4,20 @@ import { Gateway, UDSPackage } from "."; import { generateName } from "../controllers/network/generate"; import { sanitizeResourceName } from "../controllers/utils"; import { generateVSName } from "../controllers/istio/virtual-service"; +import { migrate } from "./migrate"; const invalidNamespaces = ["kube-system", "kube-public", "_unknown_", "pepr-system"]; export async function validator(req: PeprValidateRequest) { - const ns = req.Raw.metadata?.namespace ?? "_unknown_"; + const pkg = migrate(req.Raw); + + const ns = pkg.metadata?.namespace ?? "_unknown_"; if (invalidNamespaces.includes(ns)) { return req.Deny("invalid namespace"); } - const exposeList = req.Raw.spec?.network?.expose ?? []; + const exposeList = pkg.spec?.network?.expose ?? []; // Track the names of the virtual services to ensure they are unique const virtualServiceNames = new Set(); @@ -48,7 +51,7 @@ export async function validator(req: PeprValidateRequest) { virtualServiceNames.add(name); } - const networkPolicy = req.Raw.spec?.network?.allow ?? []; + const networkPolicy = pkg.spec?.network?.allow ?? []; // Track the names of the network policies to ensure they are unique const networkPolicyNames = new Set(); @@ -60,7 +63,7 @@ export async function validator(req: PeprValidateRequest) { } // Ensure the policy name is unique - const name = sanitizeResourceName(`allow-${req.Raw.metadata?.name}-${generateName(policy)}`); + const name = sanitizeResourceName(`allow-${pkg.metadata?.name}-${generateName(policy)}`); if (networkPolicyNames.has(name)) { return req.Deny( `The combination of characteristics of this network allow rule would create a duplicate NetworkPolicy. ` + diff --git a/src/pepr/operator/reconciler.ts b/src/pepr/operator/reconciler.ts index adf6b2fa3..feb2d696d 100644 --- a/src/pepr/operator/reconciler.ts +++ b/src/pepr/operator/reconciler.ts @@ -6,6 +6,7 @@ import { virtualService } from "./controllers/istio/virtual-service"; import { networkPolicies } from "./controllers/network/policies"; import { Phase, Status, UDSPackage } from "./crd"; import { VirtualService } from "./crd/generated/istio/virtualservice-v1beta1"; +import { migrate } from "./crd/migrate"; /** * The reconciler is called from the queue and is responsible for reconciling the state of the package @@ -14,6 +15,8 @@ import { VirtualService } from "./crd/generated/istio/virtualservice-v1beta1"; * @param pkg the package to reconcile */ export async function reconciler(pkg: UDSPackage) { + migrate(pkg); + if (!pkg.metadata?.namespace) { Log.error(pkg, `Invalid Package definition`); return; From 70c59695191a0edc2853f6e39668bd2c0891b229 Mon Sep 17 00:00:00 2001 From: Jeff McCoy Date: Thu, 8 Feb 2024 23:17:49 -0600 Subject: [PATCH 12/12] update generated crd --- src/pepr/operator/crd/generated/package-v1alpha1.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pepr/operator/crd/generated/package-v1alpha1.ts b/src/pepr/operator/crd/generated/package-v1alpha1.ts index 0a08e5cd9..f0be91444 100644 --- a/src/pepr/operator/crd/generated/package-v1alpha1.ts +++ b/src/pepr/operator/crd/generated/package-v1alpha1.ts @@ -42,7 +42,7 @@ export interface Allow { */ labels?: { [key: string]: string }; /** - * Deprectated: use selector + * Deprecated: use selector */ podLabels?: { [key: string]: string }; /** @@ -63,7 +63,7 @@ export interface Allow { */ remoteNamespace?: string; /** - * Deprectated: use remoteSelector + * Deprecated: use remoteSelector */ remotePodLabels?: { [key: string]: string }; /** @@ -118,7 +118,7 @@ export interface Expose { */ match?: ExposeMatch[]; /** - * Deprectated: use selector + * Deprecated: use selector */ podLabels?: { [key: string]: string }; /**