Skip to content

Commit

Permalink
Merge branch 'main' into 1340-assets-circular
Browse files Browse the repository at this point in the history
  • Loading branch information
schaeferka authored Oct 31, 2024
2 parents fbbdb38 + ea99815 commit bda0622
Show file tree
Hide file tree
Showing 10 changed files with 154 additions and 39 deletions.
5 changes: 3 additions & 2 deletions docs/030_user-guide/120_customization.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,10 +129,11 @@ Below are the available configurations through `package.json`.
| `onError` | Behavior of the webhook failure policy | `reject`, `ignore` |
| `webhookTimeout` | Webhook timeout in seconds | `1` - `30` |
| `customLabels` | Custom labels for namespaces | `{namespace: {}}` |
| `alwaysIgnore` | Conditions to always ignore | `{namespaces: []}` |
| `alwaysIgnore` | Conditions to always ignore | `{namespaces: []}` |
| `includedFiles` | For working with WebAssembly | ["main.wasm", "wasm_exec.js"] |
| `env` | Environment variables for the container| `{LOG_LEVEL: "warn"}` |
| `rbac` | Custom RBAC rules | `{"rbac": [{"apiGroups": ["<apiGroups>"], "resources": ["<resources>"], "verbs": ["<verbs>"]}]}` |
| `rbac` | Custom RBAC rules (requires building with `rbacMode: scoped`) | `{"rbac": [{"apiGroups": ["<apiGroups>"], "resources": ["<resources>"], "verbs": ["<verbs>"]}]}` |
| `rbacMode` | Configures module to build binding RBAC with principal of least privilege | `scoped`, `admin` |

These tables provide a comprehensive overview of the fields available for customization within the Helm overrides and the `package.json` file. Modify these according to your deployment requirements.

Expand Down
10 changes: 5 additions & 5 deletions journey/entrypoint-wasm.test.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2023-Present The Pepr Authors

import { describe, jest } from "@jest/globals";

import { describe, jest } from "@jest/globals";
import { peprBuild } from "./pepr-build-wasm";


// Unmock unit test things
jest.deepUnmock("pino");


// Allow 5 minutes for the tests to run
jest.setTimeout(1000 * 60 * 5);

describe("Journey: `npx pepr build -r gchr.io/defenseunicorns --rbac-mode scoped -o dist/pepr-test-module/child/folder`", peprBuild);
describe(
"Journey: `npx pepr build -r gchr.io/defenseunicorns -o dist/pepr-test-module/child/folder`",
peprBuild,
);
63 changes: 42 additions & 21 deletions journey/pepr-build-wasm.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2023-Present The Pepr Authors

import { expect, it } from "@jest/globals";
import { loadYaml } from "@kubernetes/client-node";
import { expect, it, beforeAll } from "@jest/globals";
import { loadYaml, V1PolicyRule as PolicyRule } from "@kubernetes/client-node";
import { execSync } from "child_process";
import { promises as fs } from "fs";
import { resolve } from "path";

import yaml from "js-yaml";
import { cwd } from "./entrypoint.test";

// test npx pepr build -o dst
const outputDir = "dist/pepr-test-module/child/folder";
export const outputDir = "dist/pepr-test-module/child/folder";

export function peprBuild() {
it("should build artifacts in the dst folder", async () => {
beforeAll(async () => {
const dir = resolve(cwd);
await fs.mkdir(outputDir, { recursive: true });
await addScopedRbacMode();
});

it("should successfully build the Pepr project with arguments", async () => {
execSync(`npx pepr build -r gchr.io/defenseunicorns --rbac-mode scoped -o ${outputDir}`, {
it("should successfully build the Pepr project with arguments and rbacMode scoped", async () => {
execSync(`npx pepr build -r gchr.io/defenseunicorns -o ${outputDir}`, {
cwd: cwd,
stdio: "inherit",
});
Expand All @@ -33,21 +34,11 @@ export function peprBuild() {
});

it("should generate a scoped ClusterRole", async () => {
await validateClusterRoleYaml();
const validateHelmChart = true;
await validateClusterRoleYaml(validateHelmChart);
});
}

async function validateClusterRoleYaml() {
// Read the generated yaml files
const k8sYaml = await fs.readFile(
resolve(cwd, outputDir, "pepr-module-static-test.yaml"),
"utf8",
);
const cr = await fs.readFile(resolve("journey", "resources", "clusterrole.yaml"), "utf8");

expect(k8sYaml.includes(cr)).toEqual(true);
}

async function validateZarfYaml() {
// Get the version of the pepr binary
const peprVer = execSync("npx pepr --version", { cwd }).toString().trim();
Expand Down Expand Up @@ -91,3 +82,33 @@ async function validateZarfYaml() {
const actualZarfYaml = loadYaml(zarfYAML);
expect(actualZarfYaml).toEqual(expectedZarfYaml);
}

async function validateClusterRoleYaml(validateChart: boolean = false) {
// Read the generated yaml files
const k8sYaml = await fs.readFile(
resolve(cwd, outputDir, "pepr-module-static-test.yaml"),
"utf8",
);
const cr = await fs.readFile(resolve("journey", "resources", "clusterrole.yaml"), "utf8");
expect(k8sYaml.includes(cr)).toEqual(true);

if (validateChart) {
const yamlChartRBAC = await fs.readFile(resolve("journey", "resources", "values.yaml"), "utf8");
const expectedYamlChartRBAC = await fs.readFile(
resolve("journey", "resources", "values.yaml"),
"utf8",
);
const jsonChartRBAC = yaml.load(yamlChartRBAC) as Record<string, PolicyRule[]>;
const expectedJsonChartRBAC = yaml.load(expectedYamlChartRBAC) as Record<string, PolicyRule[]>;

expect(JSON.stringify(jsonChartRBAC)).toEqual(JSON.stringify(expectedJsonChartRBAC));
}
}

// Set rbacMode in the Pepr Module Config and write it back to disk
async function addScopedRbacMode() {
const packageJson = await fs.readFile(resolve(cwd, "package.json"), "utf8");
const packageJsonObj = JSON.parse(packageJson);
packageJsonObj.pepr.rbacMode = "scoped";
await fs.writeFile(resolve(cwd, "package.json"), JSON.stringify(packageJsonObj, null, 2));
}
1 change: 0 additions & 1 deletion journey/pepr-build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,6 @@ async function validateClusterRoleYaml() {
);
const cr = await fs.readFile(resolve("journey", "resources", "clusterrole.yaml"), "utf8");


expect(k8sYaml.includes(cr)).toEqual(true);
}

Expand Down
29 changes: 29 additions & 0 deletions journey/resources/values.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
rbac:
- apiGroups:
- 'pepr.dev'
resources:
- 'peprstores'
verbs:
- 'create'
- 'get'
- 'patch'
- 'watch'
- apiGroups:
- 'apiextensions.k8s.io'
resources:
- 'customresourcedefinitions'
verbs:
- 'patch'
- 'create'
- apiGroups:
- ''
resources:
- 'namespaces'
verbs:
- 'watch'
- apiGroups:
- ''
resources:
- 'configmaps'
verbs:
- 'watch'
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
"fast-json-patch": "3.1.1",
"http-status-codes": "^2.3.0",
"json-pointer": "^0.6.2",
"kubernetes-fluent-client": "3.2.1",
"kubernetes-fluent-client": "3.2.2",
"pino": "9.5.0",
"pino-pretty": "11.3.0",
"prom-client": "15.1.3",
Expand Down
28 changes: 28 additions & 0 deletions src/cli/build.helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* Determine the RBAC mode based on the CLI options and the module's config
* @param opts CLI options
* @param cfg Module's config
* @returns The determined RBAC mode
* @example
* const opts = { rbacMode: "admin" };
* const cfg = { pepr: { rbacMode: "scoped" } };
* const result = determineRbacMode(opts, cfg);
* console.log(result); // "admin"
*/
export function determineRbacMode(
opts: { rbacMode?: string },
cfg: { pepr: { rbacMode?: string } },
): string {
// CLI overrides the module's config
if (opts.rbacMode) {
return opts.rbacMode;
}

// if rbacMode is defined and not scoped, return admin
if (cfg.pepr.rbacMode && cfg.pepr.rbacMode !== "scoped") {
return "admin";
}

// if nothing is defined return admin, else return scoped
return cfg.pepr.rbacMode || "admin";
}
36 changes: 36 additions & 0 deletions src/cli/build.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2023-Present The Pepr Authors

import { determineRbacMode } from "./build.helpers";

import { expect, describe, test } from "@jest/globals";

describe("determineRbacMode", () => {
test("should allow CLI options to overwrite module config", () => {
const opts = { rbacMode: "admin" };
const cfg = { pepr: { rbacMode: "scoped" } };
const result = determineRbacMode(opts, cfg);
expect(result).toBe("admin");
});

test('should return "admin" when cfg.pepr.rbacMode is provided and not "scoped"', () => {
const opts = {};
const cfg = { pepr: { rbacMode: "admin" } };
const result = determineRbacMode(opts, cfg);
expect(result).toBe("admin");
});

test('should return "scoped" when cfg.pepr.rbacMode is "scoped"', () => {
const opts = {};
const cfg = { pepr: { rbacMode: "scoped" } };
const result = determineRbacMode(opts, cfg);
expect(result).toBe("scoped");
});

test("should default to admin when neither option is provided", () => {
const opts = {};
const cfg = { pepr: {} };
const result = determineRbacMode(opts, cfg);
expect(result).toBe("admin");
});
});
11 changes: 6 additions & 5 deletions src/cli/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { peprFormat } from "./format";
import { Option } from "commander";
import { createDirectoryIfNotExists, validateCapabilityNames, parseTimeout } from "../lib/helpers";
import { sanitizeResourceName } from "../sdk/sdk";

import { determineRbacMode } from "./build.helpers";
const peprTS = "pepr.ts";
let outputDir: string = "dist";
export type Reloader = (opts: BuildResult<BuildOptions>) => void | Promise<void>;
Expand Down Expand Up @@ -67,9 +67,9 @@ export default function (program: RootCmd) {
.default("manifest"),
)
.addOption(
new Option("--rbac-mode [admin|scoped]", "Rbac Mode: admin, scoped (default: admin)")
.choices(["admin", "scoped"])
.default("admin"),
new Option("--rbac-mode [admin|scoped]", "Rbac Mode: admin, scoped (default: admin)").choices(
["admin", "scoped"],
),
)
.action(async opts => {

Check warning on line 74 in src/cli/build.ts

View workflow job for this annotation

GitHub Actions / format

Async arrow function has too many statements (53). Maximum allowed is 20

Check warning on line 74 in src/cli/build.ts

View workflow job for this annotation

GitHub Actions / format

Async arrow function has a complexity of 15. Maximum allowed is 10
// assign custom output directory if provided
Expand Down Expand Up @@ -134,7 +134,8 @@ export default function (program: RootCmd) {
...cfg.pepr,
appVersion: cfg.version,
description: cfg.description,
rbacMode: opts.rbacMode,
// Can override the rbacMode with the CLI option
rbacMode: determineRbacMode(opts, cfg),
},
path,
);
Expand Down

0 comments on commit bda0622

Please sign in to comment.