diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 1f0352c52..271b0d75a 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -44,17 +44,17 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@aa578102511db1f4524ed59b8cc2bae4f6e88195 # v3.27.6 + uses: github/codeql-action/init@babb554ede22fd5605947329c4d04d8e7a0b8155 # v3.27.7 with: languages: ${{ matrix.language }} # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@aa578102511db1f4524ed59b8cc2bae4f6e88195 # v3.27.6 + uses: github/codeql-action/autobuild@babb554ede22fd5605947329c4d04d8e7a0b8155 # v3.27.7 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@aa578102511db1f4524ed59b8cc2bae4f6e88195 # v3.27.6 + uses: github/codeql-action/analyze@babb554ede22fd5605947329c4d04d8e7a0b8155 # v3.27.7 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 7b95c5405..89f460138 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -60,6 +60,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@aa578102511db1f4524ed59b8cc2bae4f6e88195 # v2.2.4 + uses: github/codeql-action/upload-sarif@babb554ede22fd5605947329c4d04d8e7a0b8155 # v2.2.4 with: sarif_file: results.sarif diff --git a/.github/workflows/secret-scan.yml b/.github/workflows/secret-scan.yml index 5bf94cf77..da2e4771a 100644 --- a/.github/workflows/secret-scan.yml +++ b/.github/workflows/secret-scan.yml @@ -23,6 +23,6 @@ jobs: with: fetch-depth: 0 - name: Default Secret Scanning - uses: trufflesecurity/trufflehog@710d09ba85a0b34cea5592f3a42aae7db5d1a279 # main + uses: trufflesecurity/trufflehog@f726d02330dbcec836fa17f79fa7711fdb3a5cc8 # main with: extra_args: --debug --no-verification # Warn on potential violations diff --git a/src/lib/assets/deploy.ts b/src/lib/assets/deploy.ts index 3f933b482..884d07988 100644 --- a/src/lib/assets/deploy.ts +++ b/src/lib/assets/deploy.ts @@ -9,7 +9,7 @@ import { V1PolicyRule as PolicyRule } from "@kubernetes/client-node"; import { Assets } from "."; import Log from "../telemetry/logger"; import { apiTokenSecret, service, tlsSecret, watcherService } from "./networking"; -import { deployment, moduleSecret, namespace, watcher } from "./pods"; +import { getDeployment, getModuleSecret, getNamespace, getWatcher } from "./pods"; import { clusterRole, clusterRoleBinding, serviceAccount, storeRole, storeRoleBinding } from "./rbac"; import { peprStoreCRD } from "./store"; import { webhookConfig } from "./webhooks"; @@ -19,7 +19,7 @@ export async function deployImagePullSecret(imagePullSecret: ImagePullSecret, na try { await K8s(kind.Namespace).Get("pepr-system"); } catch { - await K8s(kind.Namespace).Apply(namespace()); + await K8s(kind.Namespace).Apply(getNamespace()); } try { @@ -48,7 +48,7 @@ export async function deploy(assets: Assets, force: boolean, webhookTimeout?: nu const { name, host, path } = assets; Log.info("Applying pepr-system namespace"); - await K8s(kind.Namespace).Apply(namespace(assets.config.customLabels?.namespace)); + await K8s(kind.Namespace).Apply(getNamespace(assets.config.customLabels?.namespace)); // Create the mutating webhook configuration if it is needed const mutateWebhook = await webhookConfig(assets, "mutate", webhookTimeout); @@ -123,7 +123,7 @@ async function setupController(assets: Assets, code: Buffer, hash: string, force const { name } = assets; Log.info("Applying module secret"); - const mod = moduleSecret(name, code, hash); + const mod = getModuleSecret(name, code, hash); await K8s(kind.Secret).Apply(mod, { force }); Log.info("Applying controller service"); @@ -139,14 +139,14 @@ async function setupController(assets: Assets, code: Buffer, hash: string, force await K8s(kind.Secret).Apply(apiToken, { force }); Log.info("Applying deployment"); - const dep = deployment(assets, hash, assets.buildTimestamp); + const dep = getDeployment(assets, hash, assets.buildTimestamp); await K8s(kind.Deployment).Apply(dep, { force }); } // Setup the watcher deployment and service async function setupWatcher(assets: Assets, hash: string, force: boolean) { // If the module has a watcher, deploy it - const watchDeployment = watcher(assets, hash, assets.buildTimestamp); + const watchDeployment = getWatcher(assets, hash, assets.buildTimestamp); if (watchDeployment) { Log.info("Applying watcher deployment"); await K8s(kind.Deployment).Apply(watchDeployment, { force }); diff --git a/src/lib/assets/destroy.ts b/src/lib/assets/destroy.ts index 6d4a80960..e528e70ff 100644 --- a/src/lib/assets/destroy.ts +++ b/src/lib/assets/destroy.ts @@ -6,7 +6,7 @@ import { K8s, kind } from "kubernetes-fluent-client"; import Log from "../telemetry/logger"; import { peprStoreCRD } from "./store"; -export async function destroyModule(name: string) { +export async function destroyModule(name: string): Promise { const namespace = "pepr-system"; Log.info("Destroying Pepr module"); diff --git a/src/lib/assets/helm.test.ts b/src/lib/assets/helm.test.ts index 172cbcf41..386982e8d 100644 --- a/src/lib/assets/helm.test.ts +++ b/src/lib/assets/helm.test.ts @@ -1,12 +1,18 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: 2023-Present The Pepr Authors -import { nsTemplate, chartYaml, watcherDeployTemplate, admissionDeployTemplate, serviceMonitorTemplate } from "./helm"; +import { + namespaceTemplate, + chartYaml, + watcherDeployTemplate, + admissionDeployTemplate, + serviceMonitorTemplate, +} from "./helm"; import { expect, describe, test } from "@jest/globals"; describe("Kubernetes Template Generators", () => { describe("nsTemplate", () => { test("should generate a Namespace template correctly", () => { - const result = nsTemplate(); + const result = namespaceTemplate(); expect(result).toContain("apiVersion: v1"); expect(result).toContain("kind: Namespace"); expect(result).toContain("name: pepr-system"); diff --git a/src/lib/assets/helm.ts b/src/lib/assets/helm.ts index 3f7124aa2..2b65f368a 100644 --- a/src/lib/assets/helm.ts +++ b/src/lib/assets/helm.ts @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: 2023-Present The Pepr Authors -export function clusterRoleTemplate() { +export function clusterRoleTemplate(): string { return ` apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -15,7 +15,7 @@ export function clusterRoleTemplate() { `; } -export function nsTemplate() { +export function namespaceTemplate(): string { return ` apiVersion: v1 kind: Namespace @@ -32,7 +32,7 @@ export function nsTemplate() { `; } -export function chartYaml(name: string, description?: string) { +export function chartYaml(name: string, description?: string): string { return ` apiVersion: v2 name: ${name} @@ -61,7 +61,7 @@ export function chartYaml(name: string, description?: string) { `; } -export function watcherDeployTemplate(buildTimestamp: string) { +export function watcherDeployTemplate(buildTimestamp: string): string { return ` apiVersion: apps/v1 kind: Deployment @@ -142,7 +142,7 @@ export function watcherDeployTemplate(buildTimestamp: string) { `; } -export function admissionDeployTemplate(buildTimestamp: string) { +export function admissionDeployTemplate(buildTimestamp: string): string { return ` apiVersion: apps/v1 kind: Deployment @@ -228,7 +228,7 @@ export function admissionDeployTemplate(buildTimestamp: string) { `; } -export function serviceMonitorTemplate(name: string) { +export function serviceMonitorTemplate(name: string): string { return ` {{- if .Values.${name}.serviceMonitor.enabled }} apiVersion: monitoring.coreos.com/v1 diff --git a/src/lib/assets/index.ts b/src/lib/assets/index.ts index 78d0947d8..b72df1254 100644 --- a/src/lib/assets/index.ts +++ b/src/lib/assets/index.ts @@ -16,7 +16,7 @@ import { dedent } from "../helpers"; import { resolve } from "path"; import { chartYaml, - nsTemplate, + namespaceTemplate, admissionDeployTemplate, watcherDeployTemplate, clusterRoleTemplate, @@ -25,7 +25,7 @@ import { import { promises as fs } from "fs"; import { webhookConfig } from "./webhooks"; import { apiTokenSecret, service, tlsSecret, watcherService } from "./networking"; -import { watcher, moduleSecret } from "./pods"; +import { getWatcher, getModuleSecret } from "./pods"; import { clusterRoleBinding, serviceAccount, storeRole, storeRoleBinding } from "./rbac"; import { createDirectoryIfNotExists } from "../filesystemService"; @@ -157,7 +157,7 @@ export class Assets { const pairs: [string, () => string][] = [ [helm.files.chartYaml, (): string => dedent(chartYaml(this.config.uuid, this.config.description || ""))], - [helm.files.namespaceYaml, (): string => dedent(nsTemplate())], + [helm.files.namespaceYaml, (): string => dedent(namespaceTemplate())], [helm.files.watcherServiceYaml, (): string => toYaml(watcherService(this.name))], [helm.files.admissionServiceYaml, (): string => toYaml(service(this.name))], [helm.files.tlsSecretYaml, (): string => toYaml(tlsSecret(this.name, this.tls))], @@ -167,7 +167,7 @@ export class Assets { [helm.files.clusterRoleYaml, (): string => dedent(clusterRoleTemplate())], [helm.files.clusterRoleBindingYaml, (): string => toYaml(clusterRoleBinding(this.name))], [helm.files.serviceAccountYaml, (): string => toYaml(serviceAccount(this.name))], - [helm.files.moduleSecretYaml, (): string => toYaml(moduleSecret(this.name, code, this.hash))], + [helm.files.moduleSecretYaml, (): string => toYaml(getModuleSecret(this.name, code, this.hash))], ]; await Promise.all(pairs.map(async ([file, content]) => await fs.writeFile(file, content()))); @@ -191,7 +191,7 @@ export class Assets { await fs.writeFile(helm.files.validationWebhookYaml, createWebhookYaml(this, validateWebhook)); } - const watchDeployment = watcher(this, this.hash, this.buildTimestamp); + const watchDeployment = getWatcher(this, this.hash, this.buildTimestamp); if (watchDeployment) { await fs.writeFile(helm.files.watcherDeploymentYaml, dedent(watcherDeployTemplate(this.buildTimestamp))); await fs.writeFile(helm.files.watcherServiceMonitorYaml, dedent(serviceMonitorTemplate("watcher"))); diff --git a/src/lib/assets/pods.test.ts b/src/lib/assets/pods.test.ts index a21b4882e..aba42161c 100644 --- a/src/lib/assets/pods.test.ts +++ b/src/lib/assets/pods.test.ts @@ -1,4 +1,4 @@ -import { namespace, watcher, deployment, moduleSecret, genEnv } from "./pods"; +import { getNamespace, getWatcher, getDeployment, getModuleSecret, genEnv } from "./pods"; import { expect, describe, test, jest, afterEach } from "@jest/globals"; import { Assets } from "."; import { ModuleConfig } from "../module"; @@ -296,7 +296,7 @@ const assets: Assets = JSON.parse(`{ }`); describe("namespace function", () => { test("should create a namespace object without labels if none are provided", () => { - const result = namespace(); + const result = getNamespace(); expect(result).toEqual({ apiVersion: "v1", kind: "Namespace", @@ -304,7 +304,7 @@ describe("namespace function", () => { name: "pepr-system", }, }); - const result1 = namespace({ one: "two" }); + const result1 = getNamespace({ one: "two" }); expect(result1).toEqual({ apiVersion: "v1", kind: "Namespace", @@ -318,20 +318,20 @@ describe("namespace function", () => { }); test("should create a namespace object with empty labels if an empty object is provided", () => { - const result = namespace({}); - expect(result.metadata.labels).toEqual({}); + const result = getNamespace({}); + expect(result.metadata?.labels).toEqual({}); }); test("should create a namespace object with provided labels", () => { const labels = { "pepr.dev/controller": "admission", "istio-injection": "enabled" }; - const result = namespace(labels); - expect(result.metadata.labels).toEqual(labels); + const result = getNamespace(labels); + expect(result.metadata?.labels).toEqual(labels); }); }); describe("watcher function", () => { test("watcher with bindings", () => { - const result = watcher(assets, "test-hash", "test-timestamp"); + const result = getWatcher(assets, "test-hash", "test-timestamp"); expect(result).toBeTruthy(); expect(result!.metadata!.name).toBe("pepr-static-test-watcher"); @@ -339,14 +339,14 @@ describe("watcher function", () => { test("watcher without bindings", () => { assets.capabilities = []; - const result = watcher(assets, "test-hash", "test-timestamp"); + const result = getWatcher(assets, "test-hash", "test-timestamp"); expect(result).toBeNull(); }); }); describe("deployment function", () => { test("deployment", () => { - const result = deployment(assets, "test-hash", "test-timestamp"); + const result = getDeployment(assets, "test-hash", "test-timestamp"); expect(result).toBeTruthy(); expect(result!.metadata!.name).toBe("pepr-static-test"); @@ -368,7 +368,7 @@ describe("moduleSecret function", () => { // eslint-disable-next-line @typescript-eslint/no-var-requires jest.spyOn(require("../helpers"), "secretOverLimit").mockReturnValue(false); - const result = moduleSecret(name, data, hash); + const result = getModuleSecret(name, data, hash); expect(result).toEqual({ apiVersion: "v1", @@ -399,7 +399,7 @@ describe("moduleSecret function", () => { throw new Error("process.exit"); }); - expect(() => moduleSecret(name, data, hash)).toThrow("process.exit"); + expect(() => getModuleSecret(name, data, hash)).toThrow("process.exit"); expect(consoleErrorMock).toHaveBeenCalledWith( "Uncaught Exception:", diff --git a/src/lib/assets/pods.ts b/src/lib/assets/pods.ts index 803127b8f..247d95cbb 100644 --- a/src/lib/assets/pods.ts +++ b/src/lib/assets/pods.ts @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: 2023-Present The Pepr Authors -import { V1EnvVar } from "@kubernetes/client-node"; +import { KubernetesObject, V1EnvVar } from "@kubernetes/client-node"; import { kind } from "kubernetes-fluent-client"; import { gzipSync } from "zlib"; import { secretOverLimit } from "../helpers"; @@ -10,7 +10,7 @@ import { ModuleConfig } from "../module"; import { Binding } from "../types"; /** Generate the pepr-system namespace */ -export function namespace(namespaceLabels?: Record) { +export function getNamespace(namespaceLabels?: Record): KubernetesObject { if (namespaceLabels) { return { apiVersion: "v1", @@ -31,7 +31,12 @@ export function namespace(namespaceLabels?: Record) { } } -export function watcher(assets: Assets, hash: string, buildTimestamp: string, imagePullSecret?: string) { +export function getWatcher( + assets: Assets, + hash: string, + buildTimestamp: string, + imagePullSecret?: string, +): kind.Deployment | null { const { name, image, capabilities, config } = assets; let hasSchedule = false; @@ -186,7 +191,7 @@ export function watcher(assets: Assets, hash: string, buildTimestamp: string, im return deploy; } -export function deployment( +export function getDeployment( assets: Assets, hash: string, buildTimestamp: string, @@ -336,7 +341,7 @@ export function deployment( return deploy; } -export function moduleSecret(name: string, data: Buffer, hash: string): kind.Secret { +export function getModuleSecret(name: string, data: Buffer, hash: string): kind.Secret { // Compress the data const compressed = gzipSync(data); const path = `module-${hash}.js.gz`; diff --git a/src/lib/assets/webhooks.ts b/src/lib/assets/webhooks.ts index 3d8d45915..3a3187340 100644 --- a/src/lib/assets/webhooks.ts +++ b/src/lib/assets/webhooks.ts @@ -20,7 +20,7 @@ const peprIgnoreLabel: V1LabelSelectorRequirement = { const peprIgnoreNamespaces: string[] = ["kube-system", "pepr-system"]; -export async function generateWebhookRules(assets: Assets, isMutateWebhook: boolean) { +export async function generateWebhookRules(assets: Assets, isMutateWebhook: boolean): Promise { const { config, capabilities } = assets; const rules: V1RuleWithOperations[] = []; diff --git a/src/lib/assets/yaml.ts b/src/lib/assets/yaml.ts index e1faffc0a..2a66a4a7f 100644 --- a/src/lib/assets/yaml.ts +++ b/src/lib/assets/yaml.ts @@ -6,13 +6,16 @@ import crypto from "crypto"; import { promises as fs } from "fs"; import { Assets } from "."; import { apiTokenSecret, service, tlsSecret, watcherService } from "./networking"; -import { deployment, moduleSecret, namespace, watcher } from "./pods"; +import { getDeployment, getModuleSecret, getNamespace, getWatcher } from "./pods"; import { clusterRole, clusterRoleBinding, serviceAccount, storeRole, storeRoleBinding } from "./rbac"; import { webhookConfig } from "./webhooks"; import { genEnv } from "./pods"; // Helm Chart overrides file (values.yaml) generated from assets -export async function overridesFile({ hash, name, image, config, apiToken, capabilities }: Assets, path: string) { +export async function overridesFile( + { hash, name, image, config, apiToken, capabilities }: Assets, + path: string, +): Promise { const rbacOverrides = clusterRole(name, capabilities, config.rbacMode, config.rbac).rules; const overrides = { @@ -166,7 +169,7 @@ export async function overridesFile({ hash, name, image, config, apiToken, capab await fs.writeFile(path, dumpYaml(overrides, { noRefs: true, forceQuotes: true })); } -export function zarfYaml({ name, image, config }: Assets, path: string) { +export function zarfYaml({ name, image, config }: Assets, path: string): string { const zarfCfg = { kind: "ZarfPackageConfig", metadata: { @@ -194,7 +197,7 @@ export function zarfYaml({ name, image, config }: Assets, path: string) { return dumpYaml(zarfCfg, { noRefs: true }); } -export function zarfYamlChart({ name, image, config }: Assets, path: string) { +export function zarfYamlChart({ name, image, config }: Assets, path: string): string { const zarfCfg = { kind: "ZarfPackageConfig", metadata: { @@ -223,7 +226,7 @@ export function zarfYamlChart({ name, image, config }: Assets, path: string) { return dumpYaml(zarfCfg, { noRefs: true }); } -export async function allYaml(assets: Assets, imagePullSecret?: string) { +export async function allYaml(assets: Assets, imagePullSecret?: string): Promise { const { name, tls, apiToken, path, config } = assets; const code = await fs.readFile(path); @@ -232,19 +235,19 @@ export async function allYaml(assets: Assets, imagePullSecret?: string) { const mutateWebhook = await webhookConfig(assets, "mutate", assets.config.webhookTimeout); const validateWebhook = await webhookConfig(assets, "validate", assets.config.webhookTimeout); - const watchDeployment = watcher(assets, assets.hash, assets.buildTimestamp, imagePullSecret); + const watchDeployment = getWatcher(assets, assets.hash, assets.buildTimestamp, imagePullSecret); const resources = [ - namespace(assets.config.customLabels?.namespace), + getNamespace(assets.config.customLabels?.namespace), clusterRole(name, assets.capabilities, config.rbacMode, config.rbac), clusterRoleBinding(name), serviceAccount(name), apiTokenSecret(name, apiToken), tlsSecret(name, tls), - deployment(assets, assets.hash, assets.buildTimestamp, imagePullSecret), + getDeployment(assets, assets.hash, assets.buildTimestamp, imagePullSecret), service(name), watcherService(name), - moduleSecret(name, code, assets.hash), + getModuleSecret(name, code, assets.hash), storeRole(name), storeRoleBinding(name), ];