pepr 0.52.2 → 0.52.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/{build.helpers.d.ts → build/build.helpers.d.ts} +4 -4
- package/dist/cli/build/build.helpers.d.ts.map +1 -0
- package/dist/cli/build/buildModule.d.ts +12 -0
- package/dist/cli/build/buildModule.d.ts.map +1 -0
- package/dist/cli/build/index.d.ts +3 -0
- package/dist/cli/build/index.d.ts.map +1 -0
- package/dist/cli/build/loadModule.d.ts +11 -0
- package/dist/cli/build/loadModule.d.ts.map +1 -0
- package/dist/cli/crd/generate/generators.d.ts +1 -28
- package/dist/cli/crd/generate/generators.d.ts.map +1 -1
- package/dist/cli/deploy.d.ts +2 -2
- package/dist/cli/deploy.d.ts.map +1 -1
- package/dist/cli/dev.d.ts.map +1 -1
- package/dist/cli/init/templates.d.ts +0 -1
- package/dist/cli/init/templates.d.ts.map +1 -1
- package/dist/cli/monitor.d.ts +3 -0
- package/dist/cli/monitor.d.ts.map +1 -1
- package/dist/cli/types.d.ts +14 -0
- package/dist/cli/types.d.ts.map +1 -1
- package/dist/cli/uuid.d.ts.map +1 -1
- package/dist/cli.js +997 -982
- package/dist/controller.js +24 -11
- package/dist/lib/assets/environment.d.ts +1 -0
- package/dist/lib/assets/environment.d.ts.map +1 -1
- package/dist/lib/assets/yaml/generateAllYaml.d.ts +1 -2
- package/dist/lib/assets/yaml/generateAllYaml.d.ts.map +1 -1
- package/dist/lib/core/storage.d.ts +2 -1
- package/dist/lib/core/storage.d.ts.map +1 -1
- package/dist/lib/filter/adjudicators/binding.d.ts +2 -1
- package/dist/lib/filter/adjudicators/binding.d.ts.map +1 -1
- package/dist/lib/filter/adjudicators/kubernetesObject.d.ts +0 -1
- package/dist/lib/filter/adjudicators/kubernetesObject.d.ts.map +1 -1
- package/dist/lib/processors/mutate-processor.d.ts +2 -1
- package/dist/lib/processors/mutate-processor.d.ts.map +1 -1
- package/dist/lib/types.d.ts +0 -4
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib.js +7 -7
- package/dist/lib.js.map +2 -2
- package/dist/runtime/controller.d.ts +1 -1
- package/dist/runtime/controller.d.ts.map +1 -1
- package/package.json +9 -10
- package/src/cli/{build.helpers.ts → build/build.helpers.ts} +12 -12
- package/src/cli/build/buildModule.ts +160 -0
- package/src/cli/build/index.ts +150 -0
- package/src/cli/build/loadModule.ts +54 -0
- package/src/cli/crd/generate/generators.ts +6 -6
- package/src/cli/deploy.ts +5 -4
- package/src/cli/dev.ts +4 -3
- package/src/cli/monitor.ts +2 -2
- package/src/cli/types.ts +26 -0
- package/src/cli/uuid.ts +5 -6
- package/src/lib/assets/deploy.ts +1 -1
- package/src/lib/assets/environment.ts +8 -1
- package/src/lib/assets/loader.ts +1 -1
- package/src/lib/assets/yaml/generateAllYaml.ts +1 -1
- package/src/lib/controller/index.ts +3 -3
- package/src/lib/controller/store.ts +1 -1
- package/src/lib/core/capability.ts +3 -3
- package/src/lib/core/storage.ts +1 -1
- package/src/lib/filter/adjudicators/binding.ts +2 -1
- package/src/lib/filter/adjudicators/kubernetesObject.ts +1 -1
- package/src/lib/processors/mutate-processor.ts +1 -1
- package/src/lib/types.ts +0 -4
- package/src/runtime/controller.ts +8 -14
- package/dist/cli/build.d.ts +0 -34
- package/dist/cli/build.d.ts.map +0 -1
- package/dist/cli/build.helpers.d.ts.map +0 -1
- package/src/cli/build.ts +0 -375
package/dist/cli.js
CHANGED
|
@@ -7376,12 +7376,6 @@ var banner = `\x1B[107;40m\x1B[38;5;m \x1B[38;5;m \x1B[38;5;m \x1B[38;5;m \x1B[3
|
|
|
7376
7376
|
\x1B[0m
|
|
7377
7377
|
`;
|
|
7378
7378
|
|
|
7379
|
-
// src/cli/build.ts
|
|
7380
|
-
var import_child_process3 = require("child_process");
|
|
7381
|
-
var import_esbuild2 = require("esbuild");
|
|
7382
|
-
var import_fs10 = require("fs");
|
|
7383
|
-
var import_path4 = require("path");
|
|
7384
|
-
|
|
7385
7379
|
// src/lib/assets/assets.ts
|
|
7386
7380
|
var import_crypto = __toESM(require("crypto"));
|
|
7387
7381
|
|
|
@@ -7784,10 +7778,17 @@ async function createDirectoryIfNotExists(path4) {
|
|
|
7784
7778
|
}
|
|
7785
7779
|
|
|
7786
7780
|
// src/lib/assets/environment.ts
|
|
7781
|
+
function getLogLevel(config) {
|
|
7782
|
+
const fromEnv = process.env.LOG_LEVEL;
|
|
7783
|
+
if (fromEnv) {
|
|
7784
|
+
return fromEnv;
|
|
7785
|
+
}
|
|
7786
|
+
return config.logLevel || "info";
|
|
7787
|
+
}
|
|
7787
7788
|
function genEnv(config, watchMode = false, ignoreWatchMode = false) {
|
|
7788
7789
|
const noWatchDef = {
|
|
7789
7790
|
PEPR_PRETTY_LOG: "false",
|
|
7790
|
-
LOG_LEVEL: config
|
|
7791
|
+
LOG_LEVEL: getLogLevel(config)
|
|
7791
7792
|
};
|
|
7792
7793
|
const def = {
|
|
7793
7794
|
PEPR_WATCH_MODE: watchMode ? "true" : "false",
|
|
@@ -8366,7 +8367,7 @@ function helmLayout(basePath, unique) {
|
|
|
8366
8367
|
// src/lib/assets/loader.ts
|
|
8367
8368
|
var import_child_process = require("child_process");
|
|
8368
8369
|
function loadCapabilities(path4) {
|
|
8369
|
-
return new Promise((
|
|
8370
|
+
return new Promise((resolve7, reject) => {
|
|
8370
8371
|
const program2 = (0, import_child_process.fork)(path4, {
|
|
8371
8372
|
env: {
|
|
8372
8373
|
...process.env,
|
|
@@ -8378,9 +8379,9 @@ function loadCapabilities(path4) {
|
|
|
8378
8379
|
program2.on("message", (message) => {
|
|
8379
8380
|
const capabilities = message.valueOf();
|
|
8380
8381
|
for (const capability of capabilities) {
|
|
8381
|
-
console.
|
|
8382
|
+
console.debug(`Registered Pepr Capability "${capability.name}"`);
|
|
8382
8383
|
}
|
|
8383
|
-
|
|
8384
|
+
resolve7(capabilities);
|
|
8384
8385
|
});
|
|
8385
8386
|
program2.on("error", (error) => {
|
|
8386
8387
|
reject(error);
|
|
@@ -8629,528 +8630,76 @@ var Assets = class {
|
|
|
8629
8630
|
};
|
|
8630
8631
|
};
|
|
8631
8632
|
|
|
8632
|
-
// src/cli/
|
|
8633
|
-
var
|
|
8634
|
-
var import_util = require("util");
|
|
8635
|
-
var import_uuid = require("uuid");
|
|
8636
|
-
var import_fs5 = require("fs");
|
|
8637
|
-
var import_path2 = __toESM(require("path"));
|
|
8633
|
+
// src/cli/build/index.ts
|
|
8634
|
+
var import_commander = require("commander");
|
|
8638
8635
|
|
|
8639
|
-
// src/
|
|
8640
|
-
var
|
|
8641
|
-
|
|
8642
|
-
|
|
8643
|
-
|
|
8644
|
-
|
|
8645
|
-
|
|
8646
|
-
|
|
8647
|
-
|
|
8648
|
-
|
|
8649
|
-
|
|
8650
|
-
|
|
8651
|
-
|
|
8652
|
-
|
|
8653
|
-
|
|
8654
|
-
"",
|
|
8655
|
-
"// When(a.<Kind>).Is<Event>().Then(change => change.<changes>",
|
|
8656
|
-
"When(${3:})"
|
|
8657
|
-
],
|
|
8658
|
-
description: "Creates a new Pepr capability with a specified description, optional namespaces, and adds a When statement for the specified value."
|
|
8659
|
-
}
|
|
8660
|
-
};
|
|
8636
|
+
// src/lib/included-files.ts
|
|
8637
|
+
var import_fs4 = require("fs");
|
|
8638
|
+
async function createDockerfile(version3, description, includedFiles) {
|
|
8639
|
+
const file = `
|
|
8640
|
+
# Use an official Node.js runtime as the base image
|
|
8641
|
+
FROM ghcr.io/defenseunicorns/pepr/controller:v${version3}
|
|
8642
|
+
|
|
8643
|
+
LABEL description="${description}"
|
|
8644
|
+
|
|
8645
|
+
# Add the included files to the image
|
|
8646
|
+
${includedFiles.map((f) => `ADD ${f} ${f}`).join("\n")}
|
|
8647
|
+
|
|
8648
|
+
`;
|
|
8649
|
+
await import_fs4.promises.writeFile("Dockerfile.controller", file, { encoding: "utf-8" });
|
|
8650
|
+
}
|
|
8661
8651
|
|
|
8662
|
-
// src/
|
|
8663
|
-
var
|
|
8664
|
-
|
|
8665
|
-
|
|
8666
|
-
|
|
8667
|
-
embeddedLanguageFormatting: "auto",
|
|
8668
|
-
insertPragma: false,
|
|
8669
|
-
printWidth: 100,
|
|
8670
|
-
quoteProps: "as-needed",
|
|
8671
|
-
requirePragma: false,
|
|
8672
|
-
semi: true,
|
|
8673
|
-
tabWidth: 2,
|
|
8674
|
-
useTabs: false,
|
|
8675
|
-
vueIndentScriptAndStyle: false
|
|
8676
|
-
};
|
|
8652
|
+
// src/cli/build/build.helpers.ts
|
|
8653
|
+
var import_child_process2 = require("child_process");
|
|
8654
|
+
var import_esbuild = require("esbuild");
|
|
8655
|
+
var import_path2 = require("path");
|
|
8656
|
+
var import_fs6 = require("fs");
|
|
8677
8657
|
|
|
8678
|
-
// src/
|
|
8679
|
-
var
|
|
8680
|
-
|
|
8681
|
-
|
|
8682
|
-
|
|
8683
|
-
|
|
8684
|
-
|
|
8685
|
-
|
|
8686
|
-
|
|
8687
|
-
|
|
8688
|
-
|
|
8689
|
-
|
|
8690
|
-
|
|
8691
|
-
|
|
8692
|
-
apiVersion: "v1",
|
|
8693
|
-
kind: "Namespace",
|
|
8694
|
-
metadata: {
|
|
8695
|
-
name: "pepr-demo-2"
|
|
8696
|
-
}
|
|
8697
|
-
},
|
|
8698
|
-
{
|
|
8699
|
-
apiVersion: "v1",
|
|
8700
|
-
kind: "Secret",
|
|
8701
|
-
metadata: {
|
|
8702
|
-
name: "secret-1",
|
|
8703
|
-
namespace: "pepr-demo"
|
|
8704
|
-
},
|
|
8705
|
-
data: {
|
|
8706
|
-
example: "dW5pY29ybiBtYWdpYw==",
|
|
8707
|
-
"binary-data": "iCZQUg8xYucNUqD+8lyl2YcKjYYygvTtiDSEV9b9WKUkxSSLFJTgIWMJ9GcFFYs4T9JCdda51u74jfq8yHzRuEASl60EdTS/NfWgIIFTGqcNRfqMw+vgpyTMmCyJVaJEDFq6AA==",
|
|
8708
|
-
"ascii-with-white-space": "VGhpcyBpcyBzb21lIHJhbmRvbSB0ZXh0OgoKICAgIC0gd2l0aCBsaW5lIGJyZWFrcwogICAgLSBhbmQgdGFicw=="
|
|
8709
|
-
}
|
|
8710
|
-
},
|
|
8711
|
-
{
|
|
8712
|
-
apiVersion: "v1",
|
|
8713
|
-
kind: "ConfigMap",
|
|
8714
|
-
metadata: {
|
|
8715
|
-
name: "example-1",
|
|
8716
|
-
namespace: "pepr-demo"
|
|
8717
|
-
},
|
|
8718
|
-
data: {
|
|
8719
|
-
key: "ex-1-val"
|
|
8720
|
-
}
|
|
8721
|
-
},
|
|
8722
|
-
{
|
|
8723
|
-
apiVersion: "v1",
|
|
8724
|
-
kind: "ConfigMap",
|
|
8725
|
-
metadata: {
|
|
8726
|
-
name: "example-2",
|
|
8727
|
-
namespace: "pepr-demo"
|
|
8728
|
-
},
|
|
8729
|
-
data: {
|
|
8730
|
-
key: "ex-2-val"
|
|
8731
|
-
}
|
|
8732
|
-
},
|
|
8733
|
-
{
|
|
8734
|
-
apiVersion: "v1",
|
|
8735
|
-
kind: "ConfigMap",
|
|
8736
|
-
metadata: {
|
|
8737
|
-
name: "example-evil-cm",
|
|
8738
|
-
namespace: "pepr-demo",
|
|
8739
|
-
annotations: {
|
|
8740
|
-
evil: "true"
|
|
8658
|
+
// src/lib/assets/yaml/generateAllYaml.ts
|
|
8659
|
+
var import_crypto2 = __toESM(require("crypto"));
|
|
8660
|
+
var import_client_node3 = require("@kubernetes/client-node");
|
|
8661
|
+
|
|
8662
|
+
// src/lib/assets/k8sObjects.ts
|
|
8663
|
+
var import_zlib = require("zlib");
|
|
8664
|
+
function getNamespace(namespaceLabels) {
|
|
8665
|
+
if (namespaceLabels) {
|
|
8666
|
+
return {
|
|
8667
|
+
apiVersion: "v1",
|
|
8668
|
+
kind: "Namespace",
|
|
8669
|
+
metadata: {
|
|
8670
|
+
name: "pepr-system",
|
|
8671
|
+
labels: namespaceLabels ?? {}
|
|
8741
8672
|
}
|
|
8742
|
-
}
|
|
8743
|
-
|
|
8744
|
-
|
|
8745
|
-
|
|
8746
|
-
|
|
8747
|
-
|
|
8748
|
-
|
|
8749
|
-
kind: "ConfigMap",
|
|
8750
|
-
metadata: {
|
|
8751
|
-
name: "example-3",
|
|
8752
|
-
namespace: "pepr-demo",
|
|
8753
|
-
labels: {
|
|
8754
|
-
change: "by-label"
|
|
8673
|
+
};
|
|
8674
|
+
} else {
|
|
8675
|
+
return {
|
|
8676
|
+
apiVersion: "v1",
|
|
8677
|
+
kind: "Namespace",
|
|
8678
|
+
metadata: {
|
|
8679
|
+
name: "pepr-system"
|
|
8755
8680
|
}
|
|
8756
|
-
}
|
|
8757
|
-
|
|
8758
|
-
|
|
8759
|
-
|
|
8760
|
-
}
|
|
8761
|
-
{
|
|
8762
|
-
|
|
8763
|
-
|
|
8764
|
-
|
|
8765
|
-
|
|
8766
|
-
|
|
8767
|
-
|
|
8768
|
-
data: {
|
|
8769
|
-
key: "ex-4-val"
|
|
8770
|
-
}
|
|
8771
|
-
},
|
|
8772
|
-
{
|
|
8773
|
-
apiVersion: "v1",
|
|
8774
|
-
kind: "ConfigMap",
|
|
8775
|
-
metadata: {
|
|
8776
|
-
name: "example-4a",
|
|
8777
|
-
namespace: "pepr-demo-2"
|
|
8778
|
-
},
|
|
8779
|
-
data: {
|
|
8780
|
-
key: "ex-4-val"
|
|
8781
|
-
}
|
|
8782
|
-
},
|
|
8783
|
-
{
|
|
8784
|
-
apiVersion: "v1",
|
|
8785
|
-
kind: "ConfigMap",
|
|
8681
|
+
};
|
|
8682
|
+
}
|
|
8683
|
+
}
|
|
8684
|
+
function getWatcher(assets, hash, buildTimestamp, imagePullSecret) {
|
|
8685
|
+
const { name: name2, image, config } = assets;
|
|
8686
|
+
if (!isWatcher(assets.capabilities)) {
|
|
8687
|
+
return null;
|
|
8688
|
+
}
|
|
8689
|
+
const app = `${name2}-watcher`;
|
|
8690
|
+
const deploy = {
|
|
8691
|
+
apiVersion: "apps/v1",
|
|
8692
|
+
kind: "Deployment",
|
|
8786
8693
|
metadata: {
|
|
8787
|
-
name:
|
|
8788
|
-
namespace: "pepr-
|
|
8694
|
+
name: app,
|
|
8695
|
+
namespace: "pepr-system",
|
|
8696
|
+
annotations: {
|
|
8697
|
+
"pepr.dev/description": config.description || ""
|
|
8698
|
+
},
|
|
8789
8699
|
labels: {
|
|
8790
|
-
|
|
8791
|
-
|
|
8792
|
-
|
|
8793
|
-
data: {
|
|
8794
|
-
key: "ex-5-val"
|
|
8795
|
-
}
|
|
8796
|
-
},
|
|
8797
|
-
{
|
|
8798
|
-
apiVersion: "apiextensions.k8s.io/v1",
|
|
8799
|
-
kind: "CustomResourceDefinition",
|
|
8800
|
-
metadata: {
|
|
8801
|
-
name: "unicorns.pepr.dev"
|
|
8802
|
-
},
|
|
8803
|
-
spec: {
|
|
8804
|
-
group: "pepr.dev",
|
|
8805
|
-
versions: [
|
|
8806
|
-
{
|
|
8807
|
-
name: "v1",
|
|
8808
|
-
served: true,
|
|
8809
|
-
storage: true,
|
|
8810
|
-
schema: {
|
|
8811
|
-
openAPIV3Schema: {
|
|
8812
|
-
type: "object",
|
|
8813
|
-
properties: {
|
|
8814
|
-
spec: {
|
|
8815
|
-
type: "object",
|
|
8816
|
-
properties: {
|
|
8817
|
-
message: {
|
|
8818
|
-
type: "string"
|
|
8819
|
-
},
|
|
8820
|
-
counter: {
|
|
8821
|
-
type: "number"
|
|
8822
|
-
}
|
|
8823
|
-
}
|
|
8824
|
-
}
|
|
8825
|
-
}
|
|
8826
|
-
}
|
|
8827
|
-
}
|
|
8828
|
-
}
|
|
8829
|
-
],
|
|
8830
|
-
scope: "Namespaced",
|
|
8831
|
-
names: {
|
|
8832
|
-
plural: "unicorns",
|
|
8833
|
-
singular: "unicorn",
|
|
8834
|
-
kind: "Unicorn"
|
|
8835
|
-
}
|
|
8836
|
-
}
|
|
8837
|
-
}
|
|
8838
|
-
];
|
|
8839
|
-
|
|
8840
|
-
// src/templates/settings.json
|
|
8841
|
-
var settings_default = {
|
|
8842
|
-
"debug.javascript.terminalOptions": {
|
|
8843
|
-
enableTurboSourcemaps: true,
|
|
8844
|
-
resolveSourceMapLocations: [
|
|
8845
|
-
"${workspaceFolder}/**",
|
|
8846
|
-
"node_modules/kubernetes-fluent-client/**",
|
|
8847
|
-
"node_modules/pepr/**"
|
|
8848
|
-
]
|
|
8849
|
-
}
|
|
8850
|
-
};
|
|
8851
|
-
|
|
8852
|
-
// src/templates/tsconfig.module.json
|
|
8853
|
-
var tsconfig_module_default = {
|
|
8854
|
-
compilerOptions: {
|
|
8855
|
-
allowSyntheticDefaultImports: true,
|
|
8856
|
-
declaration: true,
|
|
8857
|
-
declarationMap: true,
|
|
8858
|
-
emitDeclarationOnly: true,
|
|
8859
|
-
esModuleInterop: true,
|
|
8860
|
-
lib: ["ES2022"],
|
|
8861
|
-
module: "NodeNext",
|
|
8862
|
-
moduleResolution: "NodeNext",
|
|
8863
|
-
outDir: "dist",
|
|
8864
|
-
resolveJsonModule: true,
|
|
8865
|
-
rootDir: ".",
|
|
8866
|
-
strict: false,
|
|
8867
|
-
target: "ES2022",
|
|
8868
|
-
useUnknownInCatchVariables: false
|
|
8869
|
-
},
|
|
8870
|
-
include: ["**/*.ts"]
|
|
8871
|
-
};
|
|
8872
|
-
|
|
8873
|
-
// src/templates/data.json
|
|
8874
|
-
var gitIgnore = "# Ignore node_modules and Pepr build artifacts\nnode_modules\ndist\ninsecure*\n";
|
|
8875
|
-
var readmeMd = '# Pepr Module\n\nThis is a Pepr Module. [Pepr](https://github.com/defenseunicorns/pepr) is a type-safe Kubernetes middleware system.\n\nThe `capabilities` directory contains all the capabilities for this module. By default,\na capability is a single typescript file in the format of `capability-name.ts` that is\nimported in the root `pepr.ts` file as `import { HelloPepr } from "./capabilities/hello-pepr";`.\nBecause this is typescript, you can organize this however you choose, e.g. creating a sub-folder\nper-capability or common logic in shared files or folders.\n\nExample Structure:\n\n```text\nModule Root\n\u251C\u2500\u2500 package.json\n\u251C\u2500\u2500 pepr.ts\n\u2514\u2500\u2500 capabilities\n \u251C\u2500\u2500 example-one.ts\n \u251C\u2500\u2500 example-three.ts\n \u2514\u2500\u2500 example-two.ts\n```\n';
|
|
8876
|
-
var peprTS = 'import { PeprModule } from "pepr";\n// cfg loads your pepr configuration from package.json\nimport cfg from "./package.json";\n\n// HelloPepr is a demo capability that is included with Pepr. Comment or delete the line below to remove it.\nimport { HelloPepr } from "./capabilities/hello-pepr";\n\n/**\n * This is the main entrypoint for this Pepr module. It is run when the module is started.\n * This is where you register your Pepr configurations and capabilities.\n */\nnew PeprModule(cfg, [\n // "HelloPepr" is a demo capability that is included with Pepr. Comment or delete the line below to remove it.\n HelloPepr,\n\n // Your additional capabilities go here\n]);\n';
|
|
8877
|
-
var helloPeprTS = 'import {\n Capability,\n K8s,\n Log,\n PeprMutateRequest,\n RegisterKind,\n a,\n fetch,\n fetchStatus,\n kind,\n} from "pepr";\nimport { MockAgent, setGlobalDispatcher } from "undici";\n\n/**\n * The HelloPepr Capability is an example capability to demonstrate some general concepts of Pepr.\n * To test this capability you run `pepr dev`and then run the following command:\n * `kubectl apply -f capabilities/hello-pepr.samples.yaml`\n */\nexport const HelloPepr = new Capability({\n name: "hello-pepr",\n description: "A simple example capability to show how things work.",\n namespaces: ["pepr-demo", "pepr-demo-2"],\n});\n\n// Use the \'When\' function to create a new action, use \'Store\' to persist data\nconst { When, Store } = HelloPepr;\n\n/**\n * ---------------------------------------------------------------------------------------------------\n * Mutate Action (Namespace) *\n * ---------------------------------------------------------------------------------------------------\n *\n * This action removes the label `remove-me` when a Namespace is created.\n * Note we don\'t need to specify the namespace here, because we\'ve already specified\n * it in the Capability definition above.\n */\nWhen(a.Namespace)\n .IsCreated()\n .Mutate(ns => ns.RemoveLabel("remove-me"));\n\n/**\n * ---------------------------------------------------------------------------------------------------\n * Watch Action with K8s SSA (Namespace) *\n * ---------------------------------------------------------------------------------------------------\n *\n * This action watches for the `pepr-demo-2` namespace to be created, then creates a ConfigMap with\n * the name `pepr-ssa-demo` and adds the namespace UID to the ConfigMap data. Because Pepr uses\n * server-side apply for this operation, the ConfigMap will be created or updated if it already exists.\n */\nWhen(a.Namespace)\n .IsCreated()\n .WithName("pepr-demo-2")\n .Watch(async ns => {\n Log.info("Namespace pepr-demo-2 was created.");\n\n try {\n // Apply the ConfigMap using K8s server-side apply\n await K8s(kind.ConfigMap).Apply({\n metadata: {\n name: "pepr-ssa-demo",\n namespace: "pepr-demo-2",\n },\n data: {\n "ns-uid": ns.metadata.uid,\n },\n });\n } catch (error) {\n // You can use the Log object to log messages to the Pepr controller pod\n Log.error(error, "Failed to apply ConfigMap using server-side apply.");\n }\n\n // You can share data between actions using the Store, including between different types of actions\n Store.setItem("watch-data", "This data was stored by a Watch Action.");\n });\n\n/**\n * ---------------------------------------------------------------------------------------------------\n * Mutate Action (CM Example 1) *\n * ---------------------------------------------------------------------------------------------------\n *\n * This is a single action. They can be in the same file or put imported from other files.\n * In this example, when a ConfigMap is created with the name `example-1`, then add a label and annotation.\n *\n * Equivalent to manually running:\n * `kubectl label configmap example-1 pepr=was-here`\n * `kubectl annotate configmap example-1 pepr.dev=annotations-work-too`\n */\nWhen(a.ConfigMap)\n .IsCreated()\n .WithName("example-1")\n .Mutate(request => {\n request.SetLabel("pepr", "was-here").SetAnnotation("pepr.dev", "annotations-work-too");\n\n // Use the Store to persist data between requests and Pepr controller pods\n Store.setItem("example-1", "was-here");\n\n // This data is written asynchronously and can be read back via `Store.getItem()` or `Store.subscribe()`\n Store.setItem("example-1-data", JSON.stringify(request.Raw.data));\n });\n\n/**\n * ---------------------------------------------------------------------------------------------------\n * Mutate & Validate Actions (CM Example 2) *\n * ---------------------------------------------------------------------------------------------------\n *\n * This combines 3 different types of actions: \'Mutate\', \'Validate\', and \'Watch\'. The order\n * of the actions is required, but each action is optional. In this example, when a ConfigMap is created\n * with the name `example-2`, then add a label and annotation, validate that the ConfigMap has the label\n * `pepr`, and log the request.\n */\nWhen(a.ConfigMap)\n .IsCreated()\n .WithName("example-2")\n .Mutate(request => {\n // This Mutate Action will mutate the request before it is persisted to the cluster\n\n // Use `request.Merge()` to merge the new data with the existing data\n request.Merge({\n metadata: {\n labels: {\n pepr: "was-here",\n },\n annotations: {\n "pepr.dev": "annotations-work-too",\n },\n },\n });\n })\n .Validate(request => {\n // This Validate Action will validate the request before it is persisted to the cluster\n\n // Approve the request if the ConfigMap has the label \'pepr\'\n if (request.HasLabel("pepr")) {\n return request.Approve();\n }\n\n // Otherwise, deny the request with an error message (optional)\n return request.Deny("ConfigMap must have label \'pepr\'");\n })\n .Watch((cm, phase) => {\n // This Watch Action will watch the ConfigMap after it has been persisted to the cluster\n Log.info(cm, `ConfigMap was ${phase} with the name example-2`);\n });\n\n/**\n * ---------------------------------------------------------------------------------------------------\n * Mutate Action (CM Example 2a) *\n * ---------------------------------------------------------------------------------------------------\n *\n * This action shows a simple validation that will deny any ConfigMap that has the\n * annotation `evil`. Note that the `Deny()` function takes an optional second parameter that is a\n * user-defined status code to return.\n */\nWhen(a.ConfigMap)\n .IsCreated()\n .Validate(request => {\n if (request.HasAnnotation("evil")) {\n return request.Deny("No evil CM annotations allowed.", 400);\n }\n\n return request.Approve();\n });\n\n/**\n * ---------------------------------------------------------------------------------------------------\n * Mutate Action (CM Example 3) *\n * ---------------------------------------------------------------------------------------------------\n *\n * This action combines different styles. Unlike the previous actions, this one will look\n * for any ConfigMap in the `pepr-demo` namespace that has the label `change=by-label` during either\n * CREATE or UPDATE. Note that all conditions added such as `WithName()`, `WithLabel()`, `InNamespace()`,\n * are ANDs so all conditions must be true for the request to be processed.\n */\nWhen(a.ConfigMap)\n .IsCreatedOrUpdated()\n .WithLabel("change", "by-label")\n .Mutate(request => {\n // The K8s object e are going to mutate\n const cm = request.Raw;\n\n // Get the username and uid of the K8s request\n const { username, uid } = request.Request.userInfo;\n\n // Store some data about the request in the configmap\n cm.data["username"] = username;\n cm.data["uid"] = uid;\n\n // You can still mix other ways of making changes too\n request.SetAnnotation("pepr.dev", "making-waves");\n });\n\n// This action validates the label `change=by-label` is deleted\nWhen(a.ConfigMap)\n .IsDeleted()\n .WithLabel("change", "by-label")\n .Validate(request => {\n // Log and then always approve the request\n Log.info("CM with label \'change=by-label\' was deleted.");\n return request.Approve();\n });\n\n/**\n * ---------------------------------------------------------------------------------------------------\n * Mutate Action (CM Example 4) *\n * ---------------------------------------------------------------------------------------------------\n *\n * This action show how you can use the `Mutate()` function without an inline function.\n * This is useful if you want to keep your actions small and focused on a single task,\n * or if you want to reuse the same function in multiple actions.\n */\nWhen(a.ConfigMap).IsCreated().WithName("example-4").Mutate(example4Cb);\n\n// This function uses the complete type definition, but is not required.\nfunction example4Cb(cm: PeprMutateRequest<a.ConfigMap>): void {\n cm.SetLabel("pepr.dev/first", "true");\n cm.SetLabel("pepr.dev/second", "true");\n cm.SetLabel("pepr.dev/third", "true");\n}\n\n/**\n * ---------------------------------------------------------------------------------------------------\n * Mutate Action (CM Example 4a) *\n * ---------------------------------------------------------------------------------------------------\n *\n * This is the same as Example 4, except this only operates on a CM in the `pepr-demo-2` namespace.\n * Note because the Capability defines namespaces, the namespace specified here must be one of those.\n * Alternatively, you can remove the namespace from the Capability definition and specify it here.\n */\nWhen(a.ConfigMap).IsCreated().InNamespace("pepr-demo-2").WithName("example-4a").Mutate(example4Cb);\n\n/**\n * ---------------------------------------------------------------------------------------------------\n * Mutate Action (CM Example 5) *\n * ---------------------------------------------------------------------------------------------------\n *\n * This action is a bit more complex. It will look for any ConfigMap in the `pepr-demo`\n * namespace that has the label `chuck-norris` during CREATE. When it finds one, it will fetch a\n * random Chuck Norris joke from the API and add it to the ConfigMap. This is a great example of how\n * you can use Pepr to make changes to your K8s objects based on external data.\n *\n * Note the use of the `async` keyword. This is required for any action that uses `await` or `fetch()`.\n *\n * Also note we are passing a type to the `fetch()` function. This is optional, but it will help you\n * avoid mistakes when working with the data returned from the API. You can also use the `as` keyword to\n * cast the data returned from the API.\n *\n * These are equivalent:\n * ```ts\n * const joke = await fetch<TheChuckNorrisJoke>("https://icanhazdadjoke.com/");\n * const joke = await fetch("https://icanhazdadjoke.com/") as TheChuckNorrisJoke;\n * ```\n *\n * Alternatively, you can drop the type completely:\n *\n * ```ts\n * fetch("https://icanhazdadjoke.com")\n * ```\n */\ninterface TheChuckNorrisJoke {\n id: string;\n joke: string;\n status: number;\n}\n\nWhen(a.ConfigMap)\n .IsCreatedOrUpdated()\n .WithLabel("chuck-norris")\n .Mutate(cm => cm.SetLabel("got-jokes", "true"))\n .Watch(async cm => {\n const jokeURL = "https://icanhazdadjoke.com";\n\n const mockAgent: MockAgent = new MockAgent();\n setGlobalDispatcher(mockAgent);\n const mockClient = mockAgent.get(jokeURL);\n mockClient.intercept({ path: "/", method: "GET" }).reply(\n 200,\n {\n id: "R7UfaahVfFd",\n joke: "Funny joke goes here.",\n status: 200,\n },\n {\n headers: {\n "Content-Type": "application/json; charset=utf-8",\n },\n },\n );\n\n // Try/catch is not needed as a response object will always be returned\n const response = await fetch<TheChuckNorrisJoke>(jokeURL, {\n headers: {\n Accept: "application/json",\n },\n });\n\n // Instead, check the `response.ok` field\n if (response.ok) {\n const { joke } = response.data;\n // Add Joke to the Store\n await Store.setItemAndWait(jokeURL, joke);\n // Add the Chuck Norris joke to the configmap\n try {\n await K8s(kind.ConfigMap).Apply({\n metadata: {\n name: cm.metadata.name,\n namespace: cm.metadata.namespace,\n },\n data: {\n "chuck-says": Store.getItem(jokeURL),\n },\n });\n } catch (error) {\n Log.error(error, "Failed to apply ConfigMap using server-side apply.", {\n cm,\n });\n }\n }\n\n // You can also assert on different HTTP response codes\n if (response.status === fetchStatus.NOT_FOUND) {\n // Do something else\n return;\n }\n });\n\n/**\n * ---------------------------------------------------------------------------------------------------\n * Mutate Action (Secret Base64 Handling) *\n * ---------------------------------------------------------------------------------------------------\n *\n * The K8s JS client provides incomplete support for base64 encoding/decoding handling for secrets,\n * unlike the GO client. To make this less painful, Pepr automatically handles base64 encoding/decoding\n * secret data before and after the action is executed.\n */\nWhen(a.Secret)\n .IsCreated()\n .WithName("secret-1")\n .Mutate(request => {\n const secret = request.Raw;\n\n // This will be encoded at the end of all processing back to base64: "Y2hhbmdlLXdpdGhvdXQtZW5jb2Rpbmc="\n secret.data.magic = "change-without-encoding";\n\n // You can modify the data directly, and it will be encoded at the end of all processing\n secret.data.example += " - modified by Pepr";\n });\n\n/**\n * ---------------------------------------------------------------------------------------------------\n * Mutate Action (Untyped Custom Resource) *\n * ---------------------------------------------------------------------------------------------------\n *\n * Out of the box, Pepr supports all the standard Kubernetes objects. However, you can also create\n * your own types. This is useful if you are working with an Operator that creates custom resources.\n * There are two ways to do this, the first is to use the `When()` function with a `GenericKind`,\n * the second is to create a new class that extends `GenericKind` and use the `RegisterKind()` function.\n *\n * This example shows how to use the `When()` function with a `GenericKind`. Note that you\n * must specify the `group`, `version`, and `kind` of the object (if applicable). This is how Pepr knows\n * if the action should be triggered or not. Since we are using a `GenericKind`,\n * Pepr will not be able to provide any intellisense for the object, so you will need to refer to the\n * Kubernetes API documentation for the object you are working with.\n *\n * You will need to wait for the CRD in `hello-pepr.samples.yaml` to be created, then you can apply\n *\n * ```yaml\n * apiVersion: pepr.dev/v1\n * kind: Unicorn\n * metadata:\n * name: example-1\n * namespace: pepr-demo\n * spec:\n * message: replace-me\n * counter: 0\n * ```\n */\nWhen(a.GenericKind, {\n group: "pepr.dev",\n version: "v1",\n kind: "Unicorn",\n})\n .IsCreated()\n .WithName("example-1")\n .Mutate(request => {\n request.Merge({\n spec: {\n message: "Hello Pepr without type data!",\n counter: Math.random(),\n },\n });\n });\n\n/**\n * ---------------------------------------------------------------------------------------------------\n * Mutate Action (Typed Custom Resource) *\n * ---------------------------------------------------------------------------------------------------\n *\n * This example shows how to use the `RegisterKind()` function to create a new type. This is useful\n * if you are working with an Operator that creates custom resources and you want to have intellisense\n * for the object. Note that you must specify the `group`, `version`, and `kind` of the object (if applicable)\n * as this is how Pepr knows if the action should be triggered or not.\n *\n * Once you register a new Kind with Pepr, you can use the `When()` function with the new Kind. Ideally,\n * you should register custom Kinds at the top of your Capability file or Pepr Module so they are available\n * to all actions, but we are putting it here for demonstration purposes.\n *\n * You will need to wait for the CRD in `hello-pepr.samples.yaml` to be created, then you can apply\n *\n * ```yaml\n * apiVersion: pepr.dev/v1\n * kind: Unicorn\n * metadata:\n * name: example-2\n * namespace: pepr-demo\n * spec:\n * message: replace-me\n * counter: 0\n * ```*\n */\nclass UnicornKind extends a.GenericKind {\n spec: {\n /**\n * JSDoc comments can be added to explain more details about the field.\n *\n * @example\n * ```ts\n * request.Raw.spec.message = "Hello Pepr!";\n * ```\n * */\n message: string;\n counter: number;\n };\n}\n\nRegisterKind(UnicornKind, {\n group: "pepr.dev",\n version: "v1",\n kind: "Unicorn",\n});\n\nWhen(UnicornKind)\n .IsCreated()\n .WithName("example-2")\n .Mutate(request => {\n request.Merge({\n spec: {\n message: "Hello Pepr with type data!",\n counter: Math.random(),\n },\n });\n });\n\n/**\n * A callback function that is called once the Pepr Store is fully loaded.\n */\nStore.onReady(data => {\n Log.info(data, "Pepr Store Ready");\n});\n';
|
|
8878
|
-
var packageJSON = { name: "pepr", description: "Kubernetes application engine", author: "Defense Unicorns", homepage: "https://github.com/defenseunicorns/pepr", license: "Apache-2.0", bin: "dist/cli.js", repository: "defenseunicorns/pepr", engines: { node: ">=18.0.0" }, files: ["/dist", "/src", "!src/**/*.test.ts", "!src/fixtures/**", "!dist/**/*.test.d.ts*"], version: "0.52.2", main: "dist/lib.js", types: "dist/lib.d.ts", scripts: { build: "tsc -p config/tsconfig.root.json && node build.mjs && npm pack", "build:image": "npm run build && docker buildx build --output type=docker --tag pepr:dev .", "build:image:unicorn": "npm run build && docker buildx build --output type=docker --tag pepr/private:dev $(node scripts/read-unicorn-build-args.mjs) .", ci: "npm ci", "format:check": "npm run format:src && npm run format:tests && npm run format:markdown && npm run format:integration && npm run format:prettier -- --check", "format:fix": "npm run format:src -- --fix && npm run format:markdown -- --fix && npm run format:integration -- --fix && npm run format:prettier -- --write", "format:integration": "eslint --config config/eslint.integration.config.mjs integration/cli integration/helpers", "format:markdown": 'npx -y markdownlint-cli --config config/.markdownlint.json --ignore adr --ignore integration/testroot --ignore pepr-test-module --ignore node_modules "**/*.md"', "format:prettier": "prettier --config config/.prettierrc src integration/cli/**/*.ts integration/helpers/**/*.ts", "format:src": "eslint --config config/eslint.root.config.mjs 'src/**/*.ts' --ignore-pattern '**/*.test.ts' --ignore-pattern 'src/templates/**'", "format:tests": "eslint --config config/eslint.test.config.mjs 'src/**/*.test.ts'", "gen-data-json": "node hack/build-template-data.js", prebuild: "rm -fr dist/* && npm run gen-data-json", prepare: `if [ "$NODE_ENV" != 'production' ]; then husky; fi`, "set:version": "node scripts/set-version.js", test: "npm run test:unit && npm run test:journey && npm run test:journey-wasm", "test:artifacts": "npm run build && vitest run src/build-artifact.test.ts", "test:docs": "vitest run --config=config/vitest.integration.config.ts integration/cli/docs/*.test.ts", "test:integration": "npm run test:integration:prep && npm run test:integration:run", "test:integration:prep": "./integration/prep.sh", "test:integration:run": "vitest run --config=config/vitest.integration.config.ts integration", "test:journey": "npm run test:journey:k3d && npm run build && npm run test:journey:image && npm run test:journey:run", "test:journey-wasm": "npm run test:journey:k3d && npm run build && npm run test:journey:image && npm run test:journey:run-wasm", "test:journey-wasm:unicorn": "npm run test:journey:k3d && npm run build && npm run test:journey:image:unicorn && npm run test:journey:run-wasm", "test:journey:image": "npm run build && docker buildx build --output type=docker --tag pepr:dev . && k3d image import pepr:dev -c pepr-dev", "test:journey:image:unicorn": "npm run build && docker buildx build --output type=docker --tag pepr/private:dev $(node scripts/read-unicorn-build-args.mjs) . && k3d image import pepr/private:dev -c pepr-dev", "test:journey:k3d": "k3d cluster delete pepr-dev && k3d cluster create pepr-dev --k3s-arg '--debug@server:0' --wait && kubectl rollout status deployment -n kube-system", "test:journey:run": "vitest run --config=config/vitest.journey.config.ts journey/entrypoint.test.ts", "test:journey:run-wasm": "vitest run --config=config/vitest.journey.config.ts journey/entrypoint-wasm.test.ts", "test:journey:unicorn": "npm run test:journey:k3d && npm run test:journey:image:unicorn && npm run test:journey:run", "test:unit": "npm run gen-data-json && NODE_OPTIONS=--no-deprecation vitest --config config/vitest.root.config.ts run --coverage", "test:upgrade:unicorn": "npm run test:journey:k3d && npm run test:journey:image:unicorn && vitest run integration/cluster/upgrade.test.ts", "test:upgrade:upstream": "npm run test:journey:k3d && npm run test:journey:image && vitest run integration/cluster/upgrade.test.ts" }, dependencies: { "@types/ramda": "0.30.2", commander: "14.0.0", express: "5.1.0", "fast-json-patch": "3.1.1", heredoc: "^1.3.1", "http-status-codes": "^2.3.0", "json-pointer": "^0.6.2", "kubernetes-fluent-client": "3.9.0", pino: "9.7.0", "pino-pretty": "13.0.0", "prom-client": "15.1.3", ramda: "0.31.3", sigstore: "3.1.0", "ts-morph": "^26.0.0" }, devDependencies: { "@commitlint/cli": "19.8.1", "@commitlint/config-conventional": "19.8.1", "@fast-check/vitest": "^0.2.1", "@types/eslint": "9.6.1", "@types/express": "5.0.3", "@types/json-pointer": "^1.0.34", "@types/node": "24.x.x", "@types/node-forge": "1.3.13", "@types/uuid": "10.0.0", "@types/ws": "^8.18.1", "@vitest/coverage-v8": "^3.2.3", "fast-check": "^4.0.0", globals: "^16.0.0", husky: "^9.1.6", "js-yaml": "^4.1.0", shellcheck: "^3.0.0", tsx: "^4.20.3", undici: "^7.0.1", vitest: "^3.2.3" }, overrides: { glob: "^9.0.0" }, peerDependencies: { "@types/prompts": "2.4.9", "@typescript-eslint/eslint-plugin": "8.33.0", "@typescript-eslint/parser": "8.33.0", esbuild: "0.25.5", eslint: "^9.26.0", "node-forge": "1.3.1", prettier: "3.5.3", prompts: "2.4.2", typescript: "5.8.3", uuid: "11.1.0" } };
|
|
8879
|
-
|
|
8880
|
-
// src/cli/init/utils.ts
|
|
8881
|
-
var import_fs4 = require("fs");
|
|
8882
|
-
function sanitizeName(name2) {
|
|
8883
|
-
if (typeof name2 !== "string") {
|
|
8884
|
-
throw TypeError(
|
|
8885
|
-
`sanitizeName() was called with a non-string value. The value is: ${name2} of type ${typeof name2}`
|
|
8886
|
-
);
|
|
8887
|
-
}
|
|
8888
|
-
let sanitized = name2.toLowerCase().replace(/[^a-z0-9-]+/gi, "-");
|
|
8889
|
-
sanitized = sanitized.replace(/^-+|-+$/g, "");
|
|
8890
|
-
sanitized = sanitized.replace(/--+/g, "-");
|
|
8891
|
-
return sanitized;
|
|
8892
|
-
}
|
|
8893
|
-
async function createDir(dir) {
|
|
8894
|
-
try {
|
|
8895
|
-
await import_fs4.promises.mkdir(dir);
|
|
8896
|
-
} catch (err) {
|
|
8897
|
-
if (err && err.code === "EEXIST") {
|
|
8898
|
-
throw new Error(`Directory ${dir} already exists`);
|
|
8899
|
-
} else {
|
|
8900
|
-
throw err;
|
|
8901
|
-
}
|
|
8902
|
-
}
|
|
8903
|
-
}
|
|
8904
|
-
function write(path4, data) {
|
|
8905
|
-
if (typeof data !== "string") {
|
|
8906
|
-
data = JSON.stringify(data, null, 2);
|
|
8907
|
-
}
|
|
8908
|
-
return import_fs4.promises.writeFile(path4, data);
|
|
8909
|
-
}
|
|
8910
|
-
|
|
8911
|
-
// src/cli/init/templates.ts
|
|
8912
|
-
var { dependencies, devDependencies, peerDependencies, scripts, version } = packageJSON;
|
|
8913
|
-
function genPkgJSON(opts) {
|
|
8914
|
-
const uuid = !opts.uuid ? (0, import_uuid.v4)() : opts.uuid;
|
|
8915
|
-
const name2 = sanitizeName(opts.name);
|
|
8916
|
-
const { typescript } = peerDependencies;
|
|
8917
|
-
const data = {
|
|
8918
|
-
name: name2,
|
|
8919
|
-
version: "0.0.1",
|
|
8920
|
-
description: opts.description,
|
|
8921
|
-
keywords: ["pepr", "k8s", "policy-engine", "pepr-module", "security"],
|
|
8922
|
-
engines: {
|
|
8923
|
-
node: ">=20.0.0"
|
|
8924
|
-
},
|
|
8925
|
-
pepr: {
|
|
8926
|
-
uuid,
|
|
8927
|
-
onError: opts.errorBehavior,
|
|
8928
|
-
webhookTimeout: 10,
|
|
8929
|
-
customLabels: {
|
|
8930
|
-
namespace: {
|
|
8931
|
-
"pepr.dev": ""
|
|
8932
|
-
}
|
|
8933
|
-
},
|
|
8934
|
-
alwaysIgnore: {
|
|
8935
|
-
namespaces: []
|
|
8936
|
-
},
|
|
8937
|
-
admission: {
|
|
8938
|
-
alwaysIgnore: {
|
|
8939
|
-
namespaces: []
|
|
8940
|
-
}
|
|
8941
|
-
},
|
|
8942
|
-
watch: {
|
|
8943
|
-
alwaysIgnore: {
|
|
8944
|
-
namespaces: []
|
|
8945
|
-
}
|
|
8946
|
-
},
|
|
8947
|
-
includedFiles: [],
|
|
8948
|
-
env: {}
|
|
8949
|
-
},
|
|
8950
|
-
scripts: {
|
|
8951
|
-
"k3d-setup": scripts["test:journey:k3d"]
|
|
8952
|
-
},
|
|
8953
|
-
dependencies: {
|
|
8954
|
-
pepr: version,
|
|
8955
|
-
undici: "^7.0.1"
|
|
8956
|
-
},
|
|
8957
|
-
devDependencies: {
|
|
8958
|
-
typescript
|
|
8959
|
-
},
|
|
8960
|
-
overrides: {
|
|
8961
|
-
"brace-expansion": "1.1.11"
|
|
8962
|
-
}
|
|
8963
|
-
};
|
|
8964
|
-
return {
|
|
8965
|
-
data,
|
|
8966
|
-
path: "package.json",
|
|
8967
|
-
print: (0, import_util.inspect)(data, false, 5, true)
|
|
8968
|
-
};
|
|
8969
|
-
}
|
|
8970
|
-
var peprTSTemplate = {
|
|
8971
|
-
path: "pepr.ts",
|
|
8972
|
-
data: peprTS
|
|
8973
|
-
};
|
|
8974
|
-
var readme = {
|
|
8975
|
-
path: "README.md",
|
|
8976
|
-
data: readmeMd
|
|
8977
|
-
};
|
|
8978
|
-
var helloPepr = {
|
|
8979
|
-
path: "hello-pepr.ts",
|
|
8980
|
-
data: helloPeprTS
|
|
8981
|
-
};
|
|
8982
|
-
var gitignore = {
|
|
8983
|
-
path: ".gitignore",
|
|
8984
|
-
data: gitIgnore
|
|
8985
|
-
};
|
|
8986
|
-
var samplesYaml = {
|
|
8987
|
-
path: "hello-pepr.samples.yaml",
|
|
8988
|
-
data: hello_pepr_samples_default.map((r) => (0, import_client_node3.dumpYaml)(r, { noRefs: true })).join("---\n")
|
|
8989
|
-
};
|
|
8990
|
-
var snippet = {
|
|
8991
|
-
path: "pepr.code-snippets",
|
|
8992
|
-
data: pepr_code_snippets_default
|
|
8993
|
-
};
|
|
8994
|
-
var codeSettings = {
|
|
8995
|
-
path: "settings.json",
|
|
8996
|
-
data: settings_default
|
|
8997
|
-
};
|
|
8998
|
-
var tsConfig = {
|
|
8999
|
-
path: "tsconfig.json",
|
|
9000
|
-
data: tsconfig_module_default
|
|
9001
|
-
};
|
|
9002
|
-
var prettier = {
|
|
9003
|
-
path: ".prettierrc",
|
|
9004
|
-
data: prettierrc_default
|
|
9005
|
-
};
|
|
9006
|
-
var eslint = {
|
|
9007
|
-
path: "eslint.config.mjs",
|
|
9008
|
-
data: (0, import_fs5.readFileSync)(
|
|
9009
|
-
import_path2.default.resolve(
|
|
9010
|
-
(() => {
|
|
9011
|
-
const fullPath = __dirname;
|
|
9012
|
-
const lengthOfSuffix = "pepr/".length;
|
|
9013
|
-
const lastPeprIndex = fullPath.lastIndexOf("pepr/");
|
|
9014
|
-
return fullPath.substring(0, lastPeprIndex + lengthOfSuffix);
|
|
9015
|
-
})(),
|
|
9016
|
-
"src/templates/eslint.config.mjs"
|
|
9017
|
-
),
|
|
9018
|
-
"utf-8"
|
|
9019
|
-
)
|
|
9020
|
-
};
|
|
9021
|
-
|
|
9022
|
-
// src/cli/build.ts
|
|
9023
|
-
var import_commander = require("commander");
|
|
9024
|
-
|
|
9025
|
-
// src/cli/format/index.ts
|
|
9026
|
-
var import_eslint = require("eslint");
|
|
9027
|
-
|
|
9028
|
-
// src/cli/format/format.helpers.ts
|
|
9029
|
-
var import_fs6 = require("fs");
|
|
9030
|
-
var import_prettier = require("prettier");
|
|
9031
|
-
async function formatWithPrettier(results, validateOnly) {
|
|
9032
|
-
let hasFailure = false;
|
|
9033
|
-
for (const { filePath } of results) {
|
|
9034
|
-
const content = await import_fs6.promises.readFile(filePath, "utf8");
|
|
9035
|
-
const cfg = await (0, import_prettier.resolveConfig)(filePath);
|
|
9036
|
-
const formatted = await (0, import_prettier.format)(content, { filepath: filePath, ...cfg });
|
|
9037
|
-
if (validateOnly && formatted !== content) {
|
|
9038
|
-
hasFailure = true;
|
|
9039
|
-
console.error(`File ${filePath} is not formatted correctly`);
|
|
9040
|
-
} else {
|
|
9041
|
-
await import_fs6.promises.writeFile(filePath, formatted);
|
|
9042
|
-
}
|
|
9043
|
-
}
|
|
9044
|
-
return hasFailure;
|
|
9045
|
-
}
|
|
9046
|
-
|
|
9047
|
-
// src/cli/format/index.ts
|
|
9048
|
-
function format_default(program2) {
|
|
9049
|
-
program2.command("format").description("Lint and format this Pepr module").option("-v, --validate-only", "Do not modify files, only validate formatting.").action(async (opts) => {
|
|
9050
|
-
const success = await peprFormat(opts.validateOnly);
|
|
9051
|
-
if (success) {
|
|
9052
|
-
logger_default.info("Module formatted");
|
|
9053
|
-
} else {
|
|
9054
|
-
process.exit(1);
|
|
9055
|
-
}
|
|
9056
|
-
});
|
|
9057
|
-
}
|
|
9058
|
-
async function peprFormat(validateOnly) {
|
|
9059
|
-
{
|
|
9060
|
-
try {
|
|
9061
|
-
const eslint2 = new import_eslint.ESLint();
|
|
9062
|
-
const results = await eslint2.lintFiles(["./**/*.ts"]);
|
|
9063
|
-
let hasFailure = false;
|
|
9064
|
-
results.forEach(async (result) => {
|
|
9065
|
-
const errorCount = result.fatalErrorCount + result.errorCount;
|
|
9066
|
-
if (errorCount > 0) {
|
|
9067
|
-
hasFailure = true;
|
|
9068
|
-
}
|
|
9069
|
-
});
|
|
9070
|
-
const formatter = await eslint2.loadFormatter("stylish");
|
|
9071
|
-
const resultText = await formatter.format(results, {});
|
|
9072
|
-
if (resultText) {
|
|
9073
|
-
logger_default.info(resultText);
|
|
9074
|
-
}
|
|
9075
|
-
if (!validateOnly) {
|
|
9076
|
-
await import_eslint.ESLint.outputFixes(results);
|
|
9077
|
-
}
|
|
9078
|
-
hasFailure = hasFailure || await formatWithPrettier(results, validateOnly);
|
|
9079
|
-
return !hasFailure;
|
|
9080
|
-
} catch (error) {
|
|
9081
|
-
logger_default.error(error, `Error formatting module:`);
|
|
9082
|
-
return false;
|
|
9083
|
-
}
|
|
9084
|
-
}
|
|
9085
|
-
}
|
|
9086
|
-
|
|
9087
|
-
// src/lib/included-files.ts
|
|
9088
|
-
var import_fs7 = require("fs");
|
|
9089
|
-
async function createDockerfile(version3, description, includedFiles) {
|
|
9090
|
-
const file = `
|
|
9091
|
-
# Use an official Node.js runtime as the base image
|
|
9092
|
-
FROM ghcr.io/defenseunicorns/pepr/controller:v${version3}
|
|
9093
|
-
|
|
9094
|
-
LABEL description="${description}"
|
|
9095
|
-
|
|
9096
|
-
# Add the included files to the image
|
|
9097
|
-
${includedFiles.map((f) => `ADD ${f} ${f}`).join("\n")}
|
|
9098
|
-
|
|
9099
|
-
`;
|
|
9100
|
-
await import_fs7.promises.writeFile("Dockerfile.controller", file, { encoding: "utf-8" });
|
|
9101
|
-
}
|
|
9102
|
-
|
|
9103
|
-
// src/cli/build.helpers.ts
|
|
9104
|
-
var import_child_process2 = require("child_process");
|
|
9105
|
-
var import_esbuild = require("esbuild");
|
|
9106
|
-
var import_path3 = require("path");
|
|
9107
|
-
var import_fs9 = require("fs");
|
|
9108
|
-
|
|
9109
|
-
// src/lib/assets/yaml/generateAllYaml.ts
|
|
9110
|
-
var import_crypto2 = __toESM(require("crypto"));
|
|
9111
|
-
var import_client_node4 = require("@kubernetes/client-node");
|
|
9112
|
-
|
|
9113
|
-
// src/lib/assets/k8sObjects.ts
|
|
9114
|
-
var import_zlib = require("zlib");
|
|
9115
|
-
function getNamespace(namespaceLabels) {
|
|
9116
|
-
if (namespaceLabels) {
|
|
9117
|
-
return {
|
|
9118
|
-
apiVersion: "v1",
|
|
9119
|
-
kind: "Namespace",
|
|
9120
|
-
metadata: {
|
|
9121
|
-
name: "pepr-system",
|
|
9122
|
-
labels: namespaceLabels ?? {}
|
|
9123
|
-
}
|
|
9124
|
-
};
|
|
9125
|
-
} else {
|
|
9126
|
-
return {
|
|
9127
|
-
apiVersion: "v1",
|
|
9128
|
-
kind: "Namespace",
|
|
9129
|
-
metadata: {
|
|
9130
|
-
name: "pepr-system"
|
|
9131
|
-
}
|
|
9132
|
-
};
|
|
9133
|
-
}
|
|
9134
|
-
}
|
|
9135
|
-
function getWatcher(assets, hash, buildTimestamp, imagePullSecret) {
|
|
9136
|
-
const { name: name2, image, config } = assets;
|
|
9137
|
-
if (!isWatcher(assets.capabilities)) {
|
|
9138
|
-
return null;
|
|
9139
|
-
}
|
|
9140
|
-
const app = `${name2}-watcher`;
|
|
9141
|
-
const deploy = {
|
|
9142
|
-
apiVersion: "apps/v1",
|
|
9143
|
-
kind: "Deployment",
|
|
9144
|
-
metadata: {
|
|
9145
|
-
name: app,
|
|
9146
|
-
namespace: "pepr-system",
|
|
9147
|
-
annotations: {
|
|
9148
|
-
"pepr.dev/description": config.description || ""
|
|
9149
|
-
},
|
|
9150
|
-
labels: {
|
|
9151
|
-
app,
|
|
9152
|
-
"pepr.dev/controller": "watcher",
|
|
9153
|
-
"pepr.dev/uuid": config.uuid
|
|
8700
|
+
app,
|
|
8701
|
+
"pepr.dev/controller": "watcher",
|
|
8702
|
+
"pepr.dev/uuid": config.uuid
|
|
9154
8703
|
}
|
|
9155
8704
|
},
|
|
9156
8705
|
spec: {
|
|
@@ -9406,472 +8955,836 @@ function getDeployment(assets, hash, buildTimestamp, imagePullSecret) {
|
|
|
9406
8955
|
}
|
|
9407
8956
|
}
|
|
9408
8957
|
};
|
|
9409
|
-
if (imagePullSecret) {
|
|
9410
|
-
deploy.spec.template.spec.imagePullSecrets = [{ name: imagePullSecret }];
|
|
8958
|
+
if (imagePullSecret) {
|
|
8959
|
+
deploy.spec.template.spec.imagePullSecrets = [{ name: imagePullSecret }];
|
|
8960
|
+
}
|
|
8961
|
+
return deploy;
|
|
8962
|
+
}
|
|
8963
|
+
function getModuleSecret(name2, data, hash) {
|
|
8964
|
+
const compressed = (0, import_zlib.gzipSync)(data);
|
|
8965
|
+
const path4 = `module-${hash}.js.gz`;
|
|
8966
|
+
const compressedData = compressed.toString("base64");
|
|
8967
|
+
if (secretOverLimit(compressedData)) {
|
|
8968
|
+
const error = new Error(`Module secret for ${name2} is over the 1MB limit`);
|
|
8969
|
+
throw error;
|
|
8970
|
+
} else {
|
|
8971
|
+
return {
|
|
8972
|
+
apiVersion: "v1",
|
|
8973
|
+
kind: "Secret",
|
|
8974
|
+
metadata: {
|
|
8975
|
+
name: `${name2}-module`,
|
|
8976
|
+
namespace: "pepr-system"
|
|
8977
|
+
},
|
|
8978
|
+
type: "Opaque",
|
|
8979
|
+
data: {
|
|
8980
|
+
[path4]: compressed.toString("base64")
|
|
8981
|
+
}
|
|
8982
|
+
};
|
|
8983
|
+
}
|
|
8984
|
+
}
|
|
8985
|
+
function service(name2, assets) {
|
|
8986
|
+
if (!isAdmission(assets.capabilities) && !norWatchOrAdmission(assets.capabilities)) {
|
|
8987
|
+
return null;
|
|
8988
|
+
}
|
|
8989
|
+
return {
|
|
8990
|
+
apiVersion: "v1",
|
|
8991
|
+
kind: "Service",
|
|
8992
|
+
metadata: {
|
|
8993
|
+
name: name2,
|
|
8994
|
+
namespace: "pepr-system",
|
|
8995
|
+
labels: {
|
|
8996
|
+
"pepr.dev/controller": "admission"
|
|
8997
|
+
}
|
|
8998
|
+
},
|
|
8999
|
+
spec: {
|
|
9000
|
+
selector: {
|
|
9001
|
+
app: name2,
|
|
9002
|
+
"pepr.dev/controller": "admission"
|
|
9003
|
+
},
|
|
9004
|
+
ports: [
|
|
9005
|
+
{
|
|
9006
|
+
port: 443,
|
|
9007
|
+
targetPort: 3e3
|
|
9008
|
+
}
|
|
9009
|
+
]
|
|
9010
|
+
}
|
|
9011
|
+
};
|
|
9012
|
+
}
|
|
9013
|
+
function watcherService(name2, assets) {
|
|
9014
|
+
if (!isWatcher(assets.capabilities)) {
|
|
9015
|
+
return null;
|
|
9016
|
+
}
|
|
9017
|
+
return {
|
|
9018
|
+
apiVersion: "v1",
|
|
9019
|
+
kind: "Service",
|
|
9020
|
+
metadata: {
|
|
9021
|
+
name: `${name2}-watcher`,
|
|
9022
|
+
namespace: "pepr-system",
|
|
9023
|
+
labels: {
|
|
9024
|
+
"pepr.dev/controller": "watcher"
|
|
9025
|
+
}
|
|
9026
|
+
},
|
|
9027
|
+
spec: {
|
|
9028
|
+
selector: {
|
|
9029
|
+
app: `${name2}-watcher`,
|
|
9030
|
+
"pepr.dev/controller": "watcher"
|
|
9031
|
+
},
|
|
9032
|
+
ports: [
|
|
9033
|
+
{
|
|
9034
|
+
port: 443,
|
|
9035
|
+
targetPort: 3e3
|
|
9036
|
+
}
|
|
9037
|
+
]
|
|
9038
|
+
}
|
|
9039
|
+
};
|
|
9040
|
+
}
|
|
9041
|
+
|
|
9042
|
+
// src/lib/assets/yaml/generateAllYaml.ts
|
|
9043
|
+
var import_fs5 = require("fs");
|
|
9044
|
+
|
|
9045
|
+
// src/lib/assets/webhooks.ts
|
|
9046
|
+
var import_ramda = require("ramda");
|
|
9047
|
+
var peprIgnoreNamespaces = ["kube-system", "pepr-system"];
|
|
9048
|
+
var validateRule = (binding, isMutateWebhook) => {
|
|
9049
|
+
const { event, kind: kind8, isMutate, isValidate } = binding;
|
|
9050
|
+
if (isMutateWebhook && !isMutate || !isMutateWebhook && !isValidate) {
|
|
9051
|
+
return void 0;
|
|
9052
|
+
}
|
|
9053
|
+
const operations = event === "CREATEORUPDATE" /* CREATE_OR_UPDATE */ ? ["CREATE" /* CREATE */, "UPDATE" /* UPDATE */] : [event];
|
|
9054
|
+
const resource = kind8.plural || `${kind8.kind.toLowerCase()}s`;
|
|
9055
|
+
const ruleObject = {
|
|
9056
|
+
apiGroups: [kind8.group],
|
|
9057
|
+
apiVersions: [kind8.version || "*"],
|
|
9058
|
+
operations,
|
|
9059
|
+
resources: [resource, ...resource === "pods" ? ["pods/ephemeralcontainers"] : []]
|
|
9060
|
+
};
|
|
9061
|
+
return ruleObject;
|
|
9062
|
+
};
|
|
9063
|
+
async function generateWebhookRules(assets, isMutateWebhook) {
|
|
9064
|
+
const { config, capabilities } = assets;
|
|
9065
|
+
const rules = capabilities.flatMap((capability) => {
|
|
9066
|
+
logger_default.info(`Module ${config.uuid} has capability: ${capability.name}`);
|
|
9067
|
+
return capability.bindings.map((binding) => validateRule(binding, isMutateWebhook)).filter((rule) => !!rule);
|
|
9068
|
+
});
|
|
9069
|
+
return (0, import_ramda.uniqWith)(import_ramda.equals, rules);
|
|
9070
|
+
}
|
|
9071
|
+
async function webhookConfigGenerator(assets, mutateOrValidate, timeoutSeconds = 10) {
|
|
9072
|
+
const ignore = [];
|
|
9073
|
+
const { name: name2, tls, config, apiPath, host } = assets;
|
|
9074
|
+
const ignoreNS = (0, import_ramda.concat)(
|
|
9075
|
+
peprIgnoreNamespaces,
|
|
9076
|
+
resolveIgnoreNamespaces(
|
|
9077
|
+
config?.alwaysIgnore?.namespaces?.length ? config?.alwaysIgnore?.namespaces : config?.admission?.alwaysIgnore?.namespaces
|
|
9078
|
+
)
|
|
9079
|
+
);
|
|
9080
|
+
if (ignoreNS) {
|
|
9081
|
+
ignore.push({
|
|
9082
|
+
key: "kubernetes.io/metadata.name",
|
|
9083
|
+
operator: "NotIn",
|
|
9084
|
+
values: ignoreNS
|
|
9085
|
+
});
|
|
9086
|
+
}
|
|
9087
|
+
const clientConfig = {
|
|
9088
|
+
caBundle: tls.ca
|
|
9089
|
+
};
|
|
9090
|
+
const fullApiPath = `/${mutateOrValidate}/${apiPath}`;
|
|
9091
|
+
if (host) {
|
|
9092
|
+
clientConfig.url = `https://${host}:3000${fullApiPath}`;
|
|
9093
|
+
} else {
|
|
9094
|
+
clientConfig.service = {
|
|
9095
|
+
name: name2,
|
|
9096
|
+
namespace: "pepr-system",
|
|
9097
|
+
path: fullApiPath
|
|
9098
|
+
};
|
|
9099
|
+
}
|
|
9100
|
+
const isMutate = mutateOrValidate === "mutate" /* MUTATE */;
|
|
9101
|
+
const rules = await generateWebhookRules(assets, isMutate);
|
|
9102
|
+
if (rules.length < 1) {
|
|
9103
|
+
return null;
|
|
9104
|
+
}
|
|
9105
|
+
return {
|
|
9106
|
+
apiVersion: "admissionregistration.k8s.io/v1",
|
|
9107
|
+
kind: isMutate ? "MutatingWebhookConfiguration" : "ValidatingWebhookConfiguration",
|
|
9108
|
+
metadata: { name: name2 },
|
|
9109
|
+
webhooks: [
|
|
9110
|
+
{
|
|
9111
|
+
name: `${name2}.pepr.dev`,
|
|
9112
|
+
admissionReviewVersions: ["v1", "v1beta1"],
|
|
9113
|
+
clientConfig,
|
|
9114
|
+
failurePolicy: config.onError === "reject" ? "Fail" : "Ignore",
|
|
9115
|
+
matchPolicy: "Equivalent",
|
|
9116
|
+
timeoutSeconds,
|
|
9117
|
+
namespaceSelector: {
|
|
9118
|
+
matchExpressions: ignore
|
|
9119
|
+
},
|
|
9120
|
+
rules,
|
|
9121
|
+
// @todo: track side effects state
|
|
9122
|
+
sideEffects: "None"
|
|
9123
|
+
}
|
|
9124
|
+
]
|
|
9125
|
+
};
|
|
9126
|
+
}
|
|
9127
|
+
|
|
9128
|
+
// src/lib/assets/yaml/generateAllYaml.ts
|
|
9129
|
+
function pushControllerManifests(resources, deployments, services) {
|
|
9130
|
+
if (deployments.watch) {
|
|
9131
|
+
resources.push(deployments.watch);
|
|
9132
|
+
}
|
|
9133
|
+
if (deployments.admission) {
|
|
9134
|
+
resources.push(deployments.admission);
|
|
9135
|
+
}
|
|
9136
|
+
if (services.admission) {
|
|
9137
|
+
resources.push(services.admission);
|
|
9138
|
+
}
|
|
9139
|
+
if (services.watch) {
|
|
9140
|
+
resources.push(services.watch);
|
|
9141
|
+
}
|
|
9142
|
+
return resources;
|
|
9143
|
+
}
|
|
9144
|
+
async function generateAllYaml(assets, deployments, services) {
|
|
9145
|
+
const { name: name2, tls, apiPath, path: path4, config } = assets;
|
|
9146
|
+
const code = await import_fs5.promises.readFile(path4);
|
|
9147
|
+
const hash = import_crypto2.default.createHash("sha256").update(code).digest("hex");
|
|
9148
|
+
let resources = [
|
|
9149
|
+
getNamespace(assets.config.customLabels?.namespace),
|
|
9150
|
+
clusterRole(name2, assets.capabilities, config.rbacMode, config.rbac),
|
|
9151
|
+
clusterRoleBinding(name2),
|
|
9152
|
+
serviceAccount(name2),
|
|
9153
|
+
apiPathSecret(name2, apiPath),
|
|
9154
|
+
tlsSecret(name2, tls),
|
|
9155
|
+
getModuleSecret(name2, code, hash),
|
|
9156
|
+
storeRole(name2),
|
|
9157
|
+
storeRoleBinding(name2)
|
|
9158
|
+
];
|
|
9159
|
+
resources = pushControllerManifests(resources, deployments, services);
|
|
9160
|
+
const webhooks = {
|
|
9161
|
+
mutate: await webhookConfigGenerator(assets, "mutate" /* MUTATE */, assets.config.webhookTimeout),
|
|
9162
|
+
validate: await webhookConfigGenerator(
|
|
9163
|
+
assets,
|
|
9164
|
+
"validate" /* VALIDATE */,
|
|
9165
|
+
assets.config.webhookTimeout
|
|
9166
|
+
)
|
|
9167
|
+
};
|
|
9168
|
+
const additionalResources = [webhooks.mutate, webhooks.validate].filter(
|
|
9169
|
+
(resource) => resource !== null && resource !== void 0
|
|
9170
|
+
);
|
|
9171
|
+
resources.push(...additionalResources);
|
|
9172
|
+
return resources.map((resource) => (0, import_client_node3.dumpYaml)(resource, { noRefs: true })).join("---\n");
|
|
9173
|
+
}
|
|
9174
|
+
|
|
9175
|
+
// src/lib/assets/yaml/generateZarfYaml.ts
|
|
9176
|
+
var import_client_node4 = require("@kubernetes/client-node");
|
|
9177
|
+
function generateZarfYamlGeneric(assets, path4, type) {
|
|
9178
|
+
const zarfComponentName = process.env.PEPR_CUSTOM_BUILD_NAME ?? "module";
|
|
9179
|
+
const manifestSettings = {
|
|
9180
|
+
name: zarfComponentName,
|
|
9181
|
+
namespace: "pepr-system",
|
|
9182
|
+
files: [path4]
|
|
9183
|
+
};
|
|
9184
|
+
const chartSettings = {
|
|
9185
|
+
name: zarfComponentName,
|
|
9186
|
+
namespace: "pepr-system",
|
|
9187
|
+
version: `${assets.config.appVersion || "0.0.1"}`,
|
|
9188
|
+
localPath: path4
|
|
9189
|
+
};
|
|
9190
|
+
const component = {
|
|
9191
|
+
name: zarfComponentName,
|
|
9192
|
+
required: true,
|
|
9193
|
+
images: [assets.image],
|
|
9194
|
+
[type]: [type === "manifests" ? manifestSettings : chartSettings]
|
|
9195
|
+
};
|
|
9196
|
+
const zarfCfg = {
|
|
9197
|
+
kind: "ZarfPackageConfig",
|
|
9198
|
+
metadata: {
|
|
9199
|
+
name: assets.name,
|
|
9200
|
+
description: `Pepr Module: ${assets.config.description}`,
|
|
9201
|
+
url: "https://github.com/defenseunicorns/pepr",
|
|
9202
|
+
version: `${assets.config.appVersion || "0.0.1"}`
|
|
9203
|
+
},
|
|
9204
|
+
components: [component]
|
|
9205
|
+
};
|
|
9206
|
+
return (0, import_client_node4.dumpYaml)(zarfCfg, { noRefs: true });
|
|
9207
|
+
}
|
|
9208
|
+
|
|
9209
|
+
// src/cli/build/build.helpers.ts
|
|
9210
|
+
function assignImage(imageOptions) {
|
|
9211
|
+
const { customImage, registryInfo, peprVersion, registry } = imageOptions;
|
|
9212
|
+
if (customImage) {
|
|
9213
|
+
return customImage;
|
|
9411
9214
|
}
|
|
9412
|
-
|
|
9215
|
+
if (registryInfo) {
|
|
9216
|
+
return `${registryInfo}/custom-pepr-controller:${peprVersion}`;
|
|
9217
|
+
}
|
|
9218
|
+
if (registry) {
|
|
9219
|
+
return checkIronBankImage(registry, "", peprVersion);
|
|
9220
|
+
}
|
|
9221
|
+
return "";
|
|
9413
9222
|
}
|
|
9414
|
-
function
|
|
9415
|
-
|
|
9416
|
-
|
|
9417
|
-
|
|
9418
|
-
if (
|
|
9419
|
-
|
|
9420
|
-
|
|
9223
|
+
function determineRbacMode(opts, cfg) {
|
|
9224
|
+
if (opts.rbacMode) {
|
|
9225
|
+
return opts.rbacMode;
|
|
9226
|
+
}
|
|
9227
|
+
if (cfg.pepr.rbacMode && cfg.pepr.rbacMode !== "scoped") {
|
|
9228
|
+
return "admin";
|
|
9229
|
+
}
|
|
9230
|
+
return cfg.pepr.rbacMode || "admin";
|
|
9231
|
+
}
|
|
9232
|
+
async function createOutputDirectory(outputDir) {
|
|
9233
|
+
try {
|
|
9234
|
+
const dir = outputDir === "" ? "dist" : outputDir;
|
|
9235
|
+
await createDirectoryIfNotExists(dir);
|
|
9236
|
+
return dir;
|
|
9237
|
+
} catch (error) {
|
|
9238
|
+
console.error(`Error creating output directory: ${error.message}`);
|
|
9239
|
+
process.exit(1);
|
|
9240
|
+
}
|
|
9241
|
+
}
|
|
9242
|
+
function checkIronBankImage(registry, image, peprVersion) {
|
|
9243
|
+
return registry === "Iron Bank" ? `registry1.dso.mil/ironbank/opensource/defenseunicorns/pepr/controller:v${peprVersion}` : image;
|
|
9244
|
+
}
|
|
9245
|
+
function validImagePullSecret(imagePullSecretName) {
|
|
9246
|
+
if (imagePullSecretName) {
|
|
9247
|
+
const error = "Invalid imagePullSecret. Please provide a valid name as defined in RFC 1123.";
|
|
9248
|
+
if (sanitizeResourceName(imagePullSecretName) !== imagePullSecretName) {
|
|
9249
|
+
console.error(error);
|
|
9250
|
+
process.exit(1);
|
|
9251
|
+
}
|
|
9252
|
+
}
|
|
9253
|
+
}
|
|
9254
|
+
async function handleCustomImageBuild(includedFiles, peprVersion, description, image) {
|
|
9255
|
+
if (includedFiles.length > 0) {
|
|
9256
|
+
await createDockerfile(peprVersion, description, includedFiles);
|
|
9257
|
+
(0, import_child_process2.execSync)(`docker build --tag ${image} -f Dockerfile.controller .`, {
|
|
9258
|
+
stdio: "inherit"
|
|
9259
|
+
});
|
|
9260
|
+
(0, import_child_process2.execSync)(`docker push ${image}`, { stdio: "inherit" });
|
|
9261
|
+
}
|
|
9262
|
+
}
|
|
9263
|
+
function handleValidCapabilityNames(capabilities) {
|
|
9264
|
+
try {
|
|
9265
|
+
validateCapabilityNames(capabilities);
|
|
9266
|
+
} catch (e) {
|
|
9267
|
+
console.error(`Error loading capability:`, e);
|
|
9268
|
+
process.exit(1);
|
|
9269
|
+
}
|
|
9270
|
+
}
|
|
9271
|
+
async function watchForChanges(ctxCfg, reloader) {
|
|
9272
|
+
const ctx = await (0, import_esbuild.context)(ctxCfg);
|
|
9273
|
+
if (reloader) {
|
|
9274
|
+
await ctx.watch();
|
|
9421
9275
|
} else {
|
|
9422
|
-
|
|
9423
|
-
|
|
9424
|
-
|
|
9425
|
-
|
|
9426
|
-
|
|
9427
|
-
|
|
9276
|
+
await ctx.rebuild();
|
|
9277
|
+
await ctx.dispose();
|
|
9278
|
+
}
|
|
9279
|
+
return ctx;
|
|
9280
|
+
}
|
|
9281
|
+
async function generateYamlAndWriteToDisk(obj) {
|
|
9282
|
+
const { uuid, imagePullSecret, outputDir, assets, zarf } = obj;
|
|
9283
|
+
const yamlFile = `pepr-module-${uuid}.yaml`;
|
|
9284
|
+
const chartPath = `${uuid}-chart`;
|
|
9285
|
+
const yamlPath = (0, import_path2.resolve)(outputDir, yamlFile);
|
|
9286
|
+
try {
|
|
9287
|
+
const yaml = await assets.allYaml(
|
|
9288
|
+
generateAllYaml,
|
|
9289
|
+
{
|
|
9290
|
+
getDeploymentFunction: getDeployment,
|
|
9291
|
+
getWatcherFunction: getWatcher,
|
|
9292
|
+
getServiceFunction: service,
|
|
9293
|
+
getWatcherServiceFunction: watcherService
|
|
9428
9294
|
},
|
|
9429
|
-
|
|
9430
|
-
|
|
9431
|
-
|
|
9432
|
-
|
|
9433
|
-
|
|
9295
|
+
imagePullSecret
|
|
9296
|
+
);
|
|
9297
|
+
const zarfPath = (0, import_path2.resolve)(outputDir, "zarf.yaml");
|
|
9298
|
+
let localZarf = "";
|
|
9299
|
+
if (zarf === "chart") {
|
|
9300
|
+
localZarf = assets.zarfYamlChart(generateZarfYamlGeneric, chartPath);
|
|
9301
|
+
} else {
|
|
9302
|
+
localZarf = assets.zarfYaml(generateZarfYamlGeneric, yamlFile);
|
|
9303
|
+
}
|
|
9304
|
+
await import_fs6.promises.writeFile(yamlPath, yaml);
|
|
9305
|
+
await import_fs6.promises.writeFile(zarfPath, localZarf);
|
|
9306
|
+
await assets.generateHelmChart(webhookConfigGenerator, getWatcher, getModuleSecret, outputDir);
|
|
9307
|
+
console.info(`K8s resource for the module saved to ${yamlPath}`);
|
|
9308
|
+
} catch (error) {
|
|
9309
|
+
console.error(`Error generating YAML: ${error}`);
|
|
9310
|
+
process.exit(1);
|
|
9434
9311
|
}
|
|
9435
9312
|
}
|
|
9436
|
-
|
|
9437
|
-
|
|
9438
|
-
|
|
9313
|
+
|
|
9314
|
+
// src/cli/build/buildModule.ts
|
|
9315
|
+
var import_child_process3 = require("child_process");
|
|
9316
|
+
var import_esbuild2 = require("esbuild");
|
|
9317
|
+
var import_path4 = require("path");
|
|
9318
|
+
|
|
9319
|
+
// src/cli/init/templates.ts
|
|
9320
|
+
var import_client_node5 = require("@kubernetes/client-node");
|
|
9321
|
+
var import_util = require("util");
|
|
9322
|
+
var import_uuid = require("uuid");
|
|
9323
|
+
var import_fs8 = require("fs");
|
|
9324
|
+
var import_path3 = __toESM(require("path"));
|
|
9325
|
+
|
|
9326
|
+
// src/templates/pepr.code-snippets.json
|
|
9327
|
+
var pepr_code_snippets_default = {
|
|
9328
|
+
"Create a new Pepr capability": {
|
|
9329
|
+
prefix: "create pepr capability",
|
|
9330
|
+
body: [
|
|
9331
|
+
"import { Capability, a } from 'pepr';",
|
|
9332
|
+
"",
|
|
9333
|
+
"export const ${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/} = new Capability({",
|
|
9334
|
+
" name: '${TM_FILENAME_BASE}',",
|
|
9335
|
+
" description: '${1:A brief description of this capability.}',",
|
|
9336
|
+
" namespaces: [${2:}],",
|
|
9337
|
+
"});",
|
|
9338
|
+
"",
|
|
9339
|
+
"// Use the 'When' function to create a new action",
|
|
9340
|
+
"const { When } = ${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/};",
|
|
9341
|
+
"",
|
|
9342
|
+
"// When(a.<Kind>).Is<Event>().Then(change => change.<changes>",
|
|
9343
|
+
"When(${3:})"
|
|
9344
|
+
],
|
|
9345
|
+
description: "Creates a new Pepr capability with a specified description, optional namespaces, and adds a When statement for the specified value."
|
|
9439
9346
|
}
|
|
9440
|
-
|
|
9347
|
+
};
|
|
9348
|
+
|
|
9349
|
+
// src/templates/.prettierrc.json
|
|
9350
|
+
var prettierrc_default = {
|
|
9351
|
+
arrowParens: "avoid",
|
|
9352
|
+
bracketSameLine: false,
|
|
9353
|
+
bracketSpacing: true,
|
|
9354
|
+
embeddedLanguageFormatting: "auto",
|
|
9355
|
+
insertPragma: false,
|
|
9356
|
+
printWidth: 100,
|
|
9357
|
+
quoteProps: "as-needed",
|
|
9358
|
+
requirePragma: false,
|
|
9359
|
+
semi: true,
|
|
9360
|
+
tabWidth: 2,
|
|
9361
|
+
useTabs: false,
|
|
9362
|
+
vueIndentScriptAndStyle: false
|
|
9363
|
+
};
|
|
9364
|
+
|
|
9365
|
+
// src/templates/capabilities/hello-pepr.samples.json
|
|
9366
|
+
var hello_pepr_samples_default = [
|
|
9367
|
+
{
|
|
9368
|
+
apiVersion: "v1",
|
|
9369
|
+
kind: "Namespace",
|
|
9370
|
+
metadata: {
|
|
9371
|
+
name: "pepr-demo",
|
|
9372
|
+
labels: {
|
|
9373
|
+
"keep-me": "please",
|
|
9374
|
+
"remove-me": "please"
|
|
9375
|
+
}
|
|
9376
|
+
}
|
|
9377
|
+
},
|
|
9378
|
+
{
|
|
9379
|
+
apiVersion: "v1",
|
|
9380
|
+
kind: "Namespace",
|
|
9381
|
+
metadata: {
|
|
9382
|
+
name: "pepr-demo-2"
|
|
9383
|
+
}
|
|
9384
|
+
},
|
|
9385
|
+
{
|
|
9386
|
+
apiVersion: "v1",
|
|
9387
|
+
kind: "Secret",
|
|
9388
|
+
metadata: {
|
|
9389
|
+
name: "secret-1",
|
|
9390
|
+
namespace: "pepr-demo"
|
|
9391
|
+
},
|
|
9392
|
+
data: {
|
|
9393
|
+
example: "dW5pY29ybiBtYWdpYw==",
|
|
9394
|
+
"binary-data": "iCZQUg8xYucNUqD+8lyl2YcKjYYygvTtiDSEV9b9WKUkxSSLFJTgIWMJ9GcFFYs4T9JCdda51u74jfq8yHzRuEASl60EdTS/NfWgIIFTGqcNRfqMw+vgpyTMmCyJVaJEDFq6AA==",
|
|
9395
|
+
"ascii-with-white-space": "VGhpcyBpcyBzb21lIHJhbmRvbSB0ZXh0OgoKICAgIC0gd2l0aCBsaW5lIGJyZWFrcwogICAgLSBhbmQgdGFicw=="
|
|
9396
|
+
}
|
|
9397
|
+
},
|
|
9398
|
+
{
|
|
9399
|
+
apiVersion: "v1",
|
|
9400
|
+
kind: "ConfigMap",
|
|
9401
|
+
metadata: {
|
|
9402
|
+
name: "example-1",
|
|
9403
|
+
namespace: "pepr-demo"
|
|
9404
|
+
},
|
|
9405
|
+
data: {
|
|
9406
|
+
key: "ex-1-val"
|
|
9407
|
+
}
|
|
9408
|
+
},
|
|
9409
|
+
{
|
|
9410
|
+
apiVersion: "v1",
|
|
9411
|
+
kind: "ConfigMap",
|
|
9412
|
+
metadata: {
|
|
9413
|
+
name: "example-2",
|
|
9414
|
+
namespace: "pepr-demo"
|
|
9415
|
+
},
|
|
9416
|
+
data: {
|
|
9417
|
+
key: "ex-2-val"
|
|
9418
|
+
}
|
|
9419
|
+
},
|
|
9420
|
+
{
|
|
9421
|
+
apiVersion: "v1",
|
|
9422
|
+
kind: "ConfigMap",
|
|
9423
|
+
metadata: {
|
|
9424
|
+
name: "example-evil-cm",
|
|
9425
|
+
namespace: "pepr-demo",
|
|
9426
|
+
annotations: {
|
|
9427
|
+
evil: "true"
|
|
9428
|
+
}
|
|
9429
|
+
},
|
|
9430
|
+
data: {
|
|
9431
|
+
key: "ex-evil-cm-val"
|
|
9432
|
+
}
|
|
9433
|
+
},
|
|
9434
|
+
{
|
|
9441
9435
|
apiVersion: "v1",
|
|
9442
|
-
kind: "
|
|
9436
|
+
kind: "ConfigMap",
|
|
9443
9437
|
metadata: {
|
|
9444
|
-
name:
|
|
9445
|
-
namespace: "pepr-
|
|
9438
|
+
name: "example-3",
|
|
9439
|
+
namespace: "pepr-demo",
|
|
9446
9440
|
labels: {
|
|
9447
|
-
|
|
9441
|
+
change: "by-label"
|
|
9448
9442
|
}
|
|
9449
9443
|
},
|
|
9450
|
-
|
|
9451
|
-
|
|
9452
|
-
app: name2,
|
|
9453
|
-
"pepr.dev/controller": "admission"
|
|
9454
|
-
},
|
|
9455
|
-
ports: [
|
|
9456
|
-
{
|
|
9457
|
-
port: 443,
|
|
9458
|
-
targetPort: 3e3
|
|
9459
|
-
}
|
|
9460
|
-
]
|
|
9444
|
+
data: {
|
|
9445
|
+
key: "ex-3-val"
|
|
9461
9446
|
}
|
|
9462
|
-
}
|
|
9463
|
-
|
|
9464
|
-
function watcherService(name2, assets) {
|
|
9465
|
-
if (!isWatcher(assets.capabilities)) {
|
|
9466
|
-
return null;
|
|
9467
|
-
}
|
|
9468
|
-
return {
|
|
9447
|
+
},
|
|
9448
|
+
{
|
|
9469
9449
|
apiVersion: "v1",
|
|
9470
|
-
kind: "
|
|
9450
|
+
kind: "ConfigMap",
|
|
9471
9451
|
metadata: {
|
|
9472
|
-
name:
|
|
9473
|
-
namespace: "pepr-
|
|
9452
|
+
name: "example-4",
|
|
9453
|
+
namespace: "pepr-demo"
|
|
9454
|
+
},
|
|
9455
|
+
data: {
|
|
9456
|
+
key: "ex-4-val"
|
|
9457
|
+
}
|
|
9458
|
+
},
|
|
9459
|
+
{
|
|
9460
|
+
apiVersion: "v1",
|
|
9461
|
+
kind: "ConfigMap",
|
|
9462
|
+
metadata: {
|
|
9463
|
+
name: "example-4a",
|
|
9464
|
+
namespace: "pepr-demo-2"
|
|
9465
|
+
},
|
|
9466
|
+
data: {
|
|
9467
|
+
key: "ex-4-val"
|
|
9468
|
+
}
|
|
9469
|
+
},
|
|
9470
|
+
{
|
|
9471
|
+
apiVersion: "v1",
|
|
9472
|
+
kind: "ConfigMap",
|
|
9473
|
+
metadata: {
|
|
9474
|
+
name: "example-5",
|
|
9475
|
+
namespace: "pepr-demo",
|
|
9474
9476
|
labels: {
|
|
9475
|
-
"
|
|
9477
|
+
"chuck-norris": "test"
|
|
9476
9478
|
}
|
|
9477
9479
|
},
|
|
9478
|
-
|
|
9479
|
-
|
|
9480
|
-
app: `${name2}-watcher`,
|
|
9481
|
-
"pepr.dev/controller": "watcher"
|
|
9482
|
-
},
|
|
9483
|
-
ports: [
|
|
9484
|
-
{
|
|
9485
|
-
port: 443,
|
|
9486
|
-
targetPort: 3e3
|
|
9487
|
-
}
|
|
9488
|
-
]
|
|
9480
|
+
data: {
|
|
9481
|
+
key: "ex-5-val"
|
|
9489
9482
|
}
|
|
9490
|
-
}
|
|
9491
|
-
|
|
9492
|
-
|
|
9493
|
-
|
|
9494
|
-
var import_fs8 = require("fs");
|
|
9495
|
-
|
|
9496
|
-
// src/lib/assets/webhooks.ts
|
|
9497
|
-
var import_ramda = require("ramda");
|
|
9498
|
-
var peprIgnoreNamespaces = ["kube-system", "pepr-system"];
|
|
9499
|
-
var validateRule = (binding, isMutateWebhook) => {
|
|
9500
|
-
const { event, kind: kind8, isMutate, isValidate } = binding;
|
|
9501
|
-
if (isMutateWebhook && !isMutate || !isMutateWebhook && !isValidate) {
|
|
9502
|
-
return void 0;
|
|
9503
|
-
}
|
|
9504
|
-
const operations = event === "CREATEORUPDATE" /* CREATE_OR_UPDATE */ ? ["CREATE" /* CREATE */, "UPDATE" /* UPDATE */] : [event];
|
|
9505
|
-
const resource = kind8.plural || `${kind8.kind.toLowerCase()}s`;
|
|
9506
|
-
const ruleObject = {
|
|
9507
|
-
apiGroups: [kind8.group],
|
|
9508
|
-
apiVersions: [kind8.version || "*"],
|
|
9509
|
-
operations,
|
|
9510
|
-
resources: [resource, ...resource === "pods" ? ["pods/ephemeralcontainers"] : []]
|
|
9511
|
-
};
|
|
9512
|
-
return ruleObject;
|
|
9513
|
-
};
|
|
9514
|
-
async function generateWebhookRules(assets, isMutateWebhook) {
|
|
9515
|
-
const { config, capabilities } = assets;
|
|
9516
|
-
const rules = capabilities.flatMap((capability) => {
|
|
9517
|
-
logger_default.info(`Module ${config.uuid} has capability: ${capability.name}`);
|
|
9518
|
-
return capability.bindings.map((binding) => validateRule(binding, isMutateWebhook)).filter((rule) => !!rule);
|
|
9519
|
-
});
|
|
9520
|
-
return (0, import_ramda.uniqWith)(import_ramda.equals, rules);
|
|
9521
|
-
}
|
|
9522
|
-
async function webhookConfigGenerator(assets, mutateOrValidate, timeoutSeconds = 10) {
|
|
9523
|
-
const ignore = [];
|
|
9524
|
-
const { name: name2, tls, config, apiPath, host } = assets;
|
|
9525
|
-
const ignoreNS = (0, import_ramda.concat)(
|
|
9526
|
-
peprIgnoreNamespaces,
|
|
9527
|
-
resolveIgnoreNamespaces(
|
|
9528
|
-
config?.alwaysIgnore?.namespaces?.length ? config?.alwaysIgnore?.namespaces : config?.admission?.alwaysIgnore?.namespaces
|
|
9529
|
-
)
|
|
9530
|
-
);
|
|
9531
|
-
if (ignoreNS) {
|
|
9532
|
-
ignore.push({
|
|
9533
|
-
key: "kubernetes.io/metadata.name",
|
|
9534
|
-
operator: "NotIn",
|
|
9535
|
-
values: ignoreNS
|
|
9536
|
-
});
|
|
9537
|
-
}
|
|
9538
|
-
const clientConfig = {
|
|
9539
|
-
caBundle: tls.ca
|
|
9540
|
-
};
|
|
9541
|
-
const fullApiPath = `/${mutateOrValidate}/${apiPath}`;
|
|
9542
|
-
if (host) {
|
|
9543
|
-
clientConfig.url = `https://${host}:3000${fullApiPath}`;
|
|
9544
|
-
} else {
|
|
9545
|
-
clientConfig.service = {
|
|
9546
|
-
name: name2,
|
|
9547
|
-
namespace: "pepr-system",
|
|
9548
|
-
path: fullApiPath
|
|
9549
|
-
};
|
|
9550
|
-
}
|
|
9551
|
-
const isMutate = mutateOrValidate === "mutate" /* MUTATE */;
|
|
9552
|
-
const rules = await generateWebhookRules(assets, isMutate);
|
|
9553
|
-
if (rules.length < 1) {
|
|
9554
|
-
return null;
|
|
9555
|
-
}
|
|
9556
|
-
return {
|
|
9557
|
-
apiVersion: "admissionregistration.k8s.io/v1",
|
|
9558
|
-
kind: isMutate ? "MutatingWebhookConfiguration" : "ValidatingWebhookConfiguration",
|
|
9559
|
-
metadata: { name: name2 },
|
|
9560
|
-
webhooks: [
|
|
9561
|
-
{
|
|
9562
|
-
name: `${name2}.pepr.dev`,
|
|
9563
|
-
admissionReviewVersions: ["v1", "v1beta1"],
|
|
9564
|
-
clientConfig,
|
|
9565
|
-
failurePolicy: config.onError === "reject" ? "Fail" : "Ignore",
|
|
9566
|
-
matchPolicy: "Equivalent",
|
|
9567
|
-
timeoutSeconds,
|
|
9568
|
-
namespaceSelector: {
|
|
9569
|
-
matchExpressions: ignore
|
|
9570
|
-
},
|
|
9571
|
-
rules,
|
|
9572
|
-
// @todo: track side effects state
|
|
9573
|
-
sideEffects: "None"
|
|
9574
|
-
}
|
|
9575
|
-
]
|
|
9576
|
-
};
|
|
9577
|
-
}
|
|
9578
|
-
|
|
9579
|
-
// src/lib/assets/yaml/generateAllYaml.ts
|
|
9580
|
-
function pushControllerManifests(resources, deployments, services) {
|
|
9581
|
-
if (deployments.watch) {
|
|
9582
|
-
resources.push(deployments.watch);
|
|
9583
|
-
}
|
|
9584
|
-
if (deployments.admission) {
|
|
9585
|
-
resources.push(deployments.admission);
|
|
9586
|
-
}
|
|
9587
|
-
if (services.admission) {
|
|
9588
|
-
resources.push(services.admission);
|
|
9589
|
-
}
|
|
9590
|
-
if (services.watch) {
|
|
9591
|
-
resources.push(services.watch);
|
|
9592
|
-
}
|
|
9593
|
-
return resources;
|
|
9594
|
-
}
|
|
9595
|
-
async function generateAllYaml(assets, deployments, services) {
|
|
9596
|
-
const { name: name2, tls, apiPath, path: path4, config } = assets;
|
|
9597
|
-
const code = await import_fs8.promises.readFile(path4);
|
|
9598
|
-
const hash = import_crypto2.default.createHash("sha256").update(code).digest("hex");
|
|
9599
|
-
let resources = [
|
|
9600
|
-
getNamespace(assets.config.customLabels?.namespace),
|
|
9601
|
-
clusterRole(name2, assets.capabilities, config.rbacMode, config.rbac),
|
|
9602
|
-
clusterRoleBinding(name2),
|
|
9603
|
-
serviceAccount(name2),
|
|
9604
|
-
apiPathSecret(name2, apiPath),
|
|
9605
|
-
tlsSecret(name2, tls),
|
|
9606
|
-
getModuleSecret(name2, code, hash),
|
|
9607
|
-
storeRole(name2),
|
|
9608
|
-
storeRoleBinding(name2)
|
|
9609
|
-
];
|
|
9610
|
-
resources = pushControllerManifests(resources, deployments, services);
|
|
9611
|
-
const webhooks = {
|
|
9612
|
-
mutate: await webhookConfigGenerator(assets, "mutate" /* MUTATE */, assets.config.webhookTimeout),
|
|
9613
|
-
validate: await webhookConfigGenerator(
|
|
9614
|
-
assets,
|
|
9615
|
-
"validate" /* VALIDATE */,
|
|
9616
|
-
assets.config.webhookTimeout
|
|
9617
|
-
)
|
|
9618
|
-
};
|
|
9619
|
-
const additionalResources = [webhooks.mutate, webhooks.validate].filter(
|
|
9620
|
-
(resource) => resource !== null && resource !== void 0
|
|
9621
|
-
);
|
|
9622
|
-
resources.push(...additionalResources);
|
|
9623
|
-
return resources.map((resource) => (0, import_client_node4.dumpYaml)(resource, { noRefs: true })).join("---\n");
|
|
9624
|
-
}
|
|
9625
|
-
|
|
9626
|
-
// src/lib/assets/yaml/generateZarfYaml.ts
|
|
9627
|
-
var import_client_node5 = require("@kubernetes/client-node");
|
|
9628
|
-
function generateZarfYamlGeneric(assets, path4, type) {
|
|
9629
|
-
const zarfComponentName = process.env.PEPR_CUSTOM_BUILD_NAME ?? "module";
|
|
9630
|
-
const manifestSettings = {
|
|
9631
|
-
name: zarfComponentName,
|
|
9632
|
-
namespace: "pepr-system",
|
|
9633
|
-
files: [path4]
|
|
9634
|
-
};
|
|
9635
|
-
const chartSettings = {
|
|
9636
|
-
name: zarfComponentName,
|
|
9637
|
-
namespace: "pepr-system",
|
|
9638
|
-
version: `${assets.config.appVersion || "0.0.1"}`,
|
|
9639
|
-
localPath: path4
|
|
9640
|
-
};
|
|
9641
|
-
const component = {
|
|
9642
|
-
name: zarfComponentName,
|
|
9643
|
-
required: true,
|
|
9644
|
-
images: [assets.image],
|
|
9645
|
-
[type]: [type === "manifests" ? manifestSettings : chartSettings]
|
|
9646
|
-
};
|
|
9647
|
-
const zarfCfg = {
|
|
9648
|
-
kind: "ZarfPackageConfig",
|
|
9483
|
+
},
|
|
9484
|
+
{
|
|
9485
|
+
apiVersion: "apiextensions.k8s.io/v1",
|
|
9486
|
+
kind: "CustomResourceDefinition",
|
|
9649
9487
|
metadata: {
|
|
9650
|
-
name:
|
|
9651
|
-
description: `Pepr Module: ${assets.config.description}`,
|
|
9652
|
-
url: "https://github.com/defenseunicorns/pepr",
|
|
9653
|
-
version: `${assets.config.appVersion || "0.0.1"}`
|
|
9488
|
+
name: "unicorns.pepr.dev"
|
|
9654
9489
|
},
|
|
9655
|
-
|
|
9656
|
-
|
|
9657
|
-
|
|
9658
|
-
|
|
9659
|
-
|
|
9660
|
-
|
|
9661
|
-
|
|
9662
|
-
|
|
9663
|
-
|
|
9664
|
-
|
|
9665
|
-
|
|
9666
|
-
|
|
9667
|
-
|
|
9668
|
-
|
|
9669
|
-
|
|
9670
|
-
|
|
9490
|
+
spec: {
|
|
9491
|
+
group: "pepr.dev",
|
|
9492
|
+
versions: [
|
|
9493
|
+
{
|
|
9494
|
+
name: "v1",
|
|
9495
|
+
served: true,
|
|
9496
|
+
storage: true,
|
|
9497
|
+
schema: {
|
|
9498
|
+
openAPIV3Schema: {
|
|
9499
|
+
type: "object",
|
|
9500
|
+
properties: {
|
|
9501
|
+
spec: {
|
|
9502
|
+
type: "object",
|
|
9503
|
+
properties: {
|
|
9504
|
+
message: {
|
|
9505
|
+
type: "string"
|
|
9506
|
+
},
|
|
9507
|
+
counter: {
|
|
9508
|
+
type: "number"
|
|
9509
|
+
}
|
|
9510
|
+
}
|
|
9511
|
+
}
|
|
9512
|
+
}
|
|
9513
|
+
}
|
|
9514
|
+
}
|
|
9515
|
+
}
|
|
9516
|
+
],
|
|
9517
|
+
scope: "Namespaced",
|
|
9518
|
+
names: {
|
|
9519
|
+
plural: "unicorns",
|
|
9520
|
+
singular: "unicorn",
|
|
9521
|
+
kind: "Unicorn"
|
|
9522
|
+
}
|
|
9523
|
+
}
|
|
9671
9524
|
}
|
|
9672
|
-
|
|
9673
|
-
|
|
9674
|
-
|
|
9675
|
-
|
|
9676
|
-
|
|
9525
|
+
];
|
|
9526
|
+
|
|
9527
|
+
// src/templates/settings.json
|
|
9528
|
+
var settings_default = {
|
|
9529
|
+
"debug.javascript.terminalOptions": {
|
|
9530
|
+
enableTurboSourcemaps: true,
|
|
9531
|
+
resolveSourceMapLocations: [
|
|
9532
|
+
"${workspaceFolder}/**",
|
|
9533
|
+
"node_modules/kubernetes-fluent-client/**",
|
|
9534
|
+
"node_modules/pepr/**"
|
|
9535
|
+
]
|
|
9677
9536
|
}
|
|
9678
|
-
|
|
9679
|
-
|
|
9537
|
+
};
|
|
9538
|
+
|
|
9539
|
+
// src/templates/tsconfig.module.json
|
|
9540
|
+
var tsconfig_module_default = {
|
|
9541
|
+
compilerOptions: {
|
|
9542
|
+
allowSyntheticDefaultImports: true,
|
|
9543
|
+
declaration: true,
|
|
9544
|
+
declarationMap: true,
|
|
9545
|
+
emitDeclarationOnly: true,
|
|
9546
|
+
esModuleInterop: true,
|
|
9547
|
+
lib: ["ES2022"],
|
|
9548
|
+
module: "NodeNext",
|
|
9549
|
+
moduleResolution: "NodeNext",
|
|
9550
|
+
outDir: "dist",
|
|
9551
|
+
resolveJsonModule: true,
|
|
9552
|
+
rootDir: ".",
|
|
9553
|
+
strict: false,
|
|
9554
|
+
target: "ES2022",
|
|
9555
|
+
useUnknownInCatchVariables: false
|
|
9556
|
+
},
|
|
9557
|
+
include: ["**/*.ts"]
|
|
9558
|
+
};
|
|
9559
|
+
|
|
9560
|
+
// src/templates/data.json
|
|
9561
|
+
var gitIgnore = "# Ignore node_modules and Pepr build artifacts\nnode_modules\ndist\ninsecure*\n";
|
|
9562
|
+
var readmeMd = '# Pepr Module\n\nThis is a Pepr Module. [Pepr](https://github.com/defenseunicorns/pepr) is a type-safe Kubernetes middleware system.\n\nThe `capabilities` directory contains all the capabilities for this module. By default,\na capability is a single typescript file in the format of `capability-name.ts` that is\nimported in the root `pepr.ts` file as `import { HelloPepr } from "./capabilities/hello-pepr";`.\nBecause this is typescript, you can organize this however you choose, e.g. creating a sub-folder\nper-capability or common logic in shared files or folders.\n\nExample Structure:\n\n```text\nModule Root\n\u251C\u2500\u2500 package.json\n\u251C\u2500\u2500 pepr.ts\n\u2514\u2500\u2500 capabilities\n \u251C\u2500\u2500 example-one.ts\n \u251C\u2500\u2500 example-three.ts\n \u2514\u2500\u2500 example-two.ts\n```\n';
|
|
9563
|
+
var peprTS = 'import { PeprModule } from "pepr";\n// cfg loads your pepr configuration from package.json\nimport cfg from "./package.json";\n\n// HelloPepr is a demo capability that is included with Pepr. Comment or delete the line below to remove it.\nimport { HelloPepr } from "./capabilities/hello-pepr";\n\n/**\n * This is the main entrypoint for this Pepr module. It is run when the module is started.\n * This is where you register your Pepr configurations and capabilities.\n */\nnew PeprModule(cfg, [\n // "HelloPepr" is a demo capability that is included with Pepr. Comment or delete the line below to remove it.\n HelloPepr,\n\n // Your additional capabilities go here\n]);\n';
|
|
9564
|
+
var helloPeprTS = 'import {\n Capability,\n K8s,\n Log,\n PeprMutateRequest,\n RegisterKind,\n a,\n fetch,\n fetchStatus,\n kind,\n} from "pepr";\nimport { MockAgent, setGlobalDispatcher } from "undici";\n\n/**\n * The HelloPepr Capability is an example capability to demonstrate some general concepts of Pepr.\n * To test this capability you run `pepr dev`and then run the following command:\n * `kubectl apply -f capabilities/hello-pepr.samples.yaml`\n */\nexport const HelloPepr = new Capability({\n name: "hello-pepr",\n description: "A simple example capability to show how things work.",\n namespaces: ["pepr-demo", "pepr-demo-2"],\n});\n\n// Use the \'When\' function to create a new action, use \'Store\' to persist data\nconst { When, Store } = HelloPepr;\n\n/**\n * ---------------------------------------------------------------------------------------------------\n * Mutate Action (Namespace) *\n * ---------------------------------------------------------------------------------------------------\n *\n * This action removes the label `remove-me` when a Namespace is created.\n * Note we don\'t need to specify the namespace here, because we\'ve already specified\n * it in the Capability definition above.\n */\nWhen(a.Namespace)\n .IsCreated()\n .Mutate(ns => ns.RemoveLabel("remove-me"));\n\n/**\n * ---------------------------------------------------------------------------------------------------\n * Watch Action with K8s SSA (Namespace) *\n * ---------------------------------------------------------------------------------------------------\n *\n * This action watches for the `pepr-demo-2` namespace to be created, then creates a ConfigMap with\n * the name `pepr-ssa-demo` and adds the namespace UID to the ConfigMap data. Because Pepr uses\n * server-side apply for this operation, the ConfigMap will be created or updated if it already exists.\n */\nWhen(a.Namespace)\n .IsCreated()\n .WithName("pepr-demo-2")\n .Watch(async ns => {\n Log.info("Namespace pepr-demo-2 was created.");\n\n try {\n // Apply the ConfigMap using K8s server-side apply\n await K8s(kind.ConfigMap).Apply({\n metadata: {\n name: "pepr-ssa-demo",\n namespace: "pepr-demo-2",\n },\n data: {\n "ns-uid": ns.metadata.uid,\n },\n });\n } catch (error) {\n // You can use the Log object to log messages to the Pepr controller pod\n Log.error(error, "Failed to apply ConfigMap using server-side apply.");\n }\n\n // You can share data between actions using the Store, including between different types of actions\n Store.setItem("watch-data", "This data was stored by a Watch Action.");\n });\n\n/**\n * ---------------------------------------------------------------------------------------------------\n * Mutate Action (CM Example 1) *\n * ---------------------------------------------------------------------------------------------------\n *\n * This is a single action. They can be in the same file or put imported from other files.\n * In this example, when a ConfigMap is created with the name `example-1`, then add a label and annotation.\n *\n * Equivalent to manually running:\n * `kubectl label configmap example-1 pepr=was-here`\n * `kubectl annotate configmap example-1 pepr.dev=annotations-work-too`\n */\nWhen(a.ConfigMap)\n .IsCreated()\n .WithName("example-1")\n .Mutate(request => {\n request.SetLabel("pepr", "was-here").SetAnnotation("pepr.dev", "annotations-work-too");\n\n // Use the Store to persist data between requests and Pepr controller pods\n Store.setItem("example-1", "was-here");\n\n // This data is written asynchronously and can be read back via `Store.getItem()` or `Store.subscribe()`\n Store.setItem("example-1-data", JSON.stringify(request.Raw.data));\n });\n\n/**\n * ---------------------------------------------------------------------------------------------------\n * Mutate & Validate Actions (CM Example 2) *\n * ---------------------------------------------------------------------------------------------------\n *\n * This combines 3 different types of actions: \'Mutate\', \'Validate\', and \'Watch\'. The order\n * of the actions is required, but each action is optional. In this example, when a ConfigMap is created\n * with the name `example-2`, then add a label and annotation, validate that the ConfigMap has the label\n * `pepr`, and log the request.\n */\nWhen(a.ConfigMap)\n .IsCreated()\n .WithName("example-2")\n .Mutate(request => {\n // This Mutate Action will mutate the request before it is persisted to the cluster\n\n // Use `request.Merge()` to merge the new data with the existing data\n request.Merge({\n metadata: {\n labels: {\n pepr: "was-here",\n },\n annotations: {\n "pepr.dev": "annotations-work-too",\n },\n },\n });\n })\n .Validate(request => {\n // This Validate Action will validate the request before it is persisted to the cluster\n\n // Approve the request if the ConfigMap has the label \'pepr\'\n if (request.HasLabel("pepr")) {\n return request.Approve();\n }\n\n // Otherwise, deny the request with an error message (optional)\n return request.Deny("ConfigMap must have label \'pepr\'");\n })\n .Watch((cm, phase) => {\n // This Watch Action will watch the ConfigMap after it has been persisted to the cluster\n Log.info(cm, `ConfigMap was ${phase} with the name example-2`);\n });\n\n/**\n * ---------------------------------------------------------------------------------------------------\n * Mutate Action (CM Example 2a) *\n * ---------------------------------------------------------------------------------------------------\n *\n * This action shows a simple validation that will deny any ConfigMap that has the\n * annotation `evil`. Note that the `Deny()` function takes an optional second parameter that is a\n * user-defined status code to return.\n */\nWhen(a.ConfigMap)\n .IsCreated()\n .Validate(request => {\n if (request.HasAnnotation("evil")) {\n return request.Deny("No evil CM annotations allowed.", 400);\n }\n\n return request.Approve();\n });\n\n/**\n * ---------------------------------------------------------------------------------------------------\n * Mutate Action (CM Example 3) *\n * ---------------------------------------------------------------------------------------------------\n *\n * This action combines different styles. Unlike the previous actions, this one will look\n * for any ConfigMap in the `pepr-demo` namespace that has the label `change=by-label` during either\n * CREATE or UPDATE. Note that all conditions added such as `WithName()`, `WithLabel()`, `InNamespace()`,\n * are ANDs so all conditions must be true for the request to be processed.\n */\nWhen(a.ConfigMap)\n .IsCreatedOrUpdated()\n .WithLabel("change", "by-label")\n .Mutate(request => {\n // The K8s object e are going to mutate\n const cm = request.Raw;\n\n // Get the username and uid of the K8s request\n const { username, uid } = request.Request.userInfo;\n\n // Store some data about the request in the configmap\n cm.data["username"] = username;\n cm.data["uid"] = uid;\n\n // You can still mix other ways of making changes too\n request.SetAnnotation("pepr.dev", "making-waves");\n });\n\n// This action validates the label `change=by-label` is deleted\nWhen(a.ConfigMap)\n .IsDeleted()\n .WithLabel("change", "by-label")\n .Validate(request => {\n // Log and then always approve the request\n Log.info("CM with label \'change=by-label\' was deleted.");\n return request.Approve();\n });\n\n/**\n * ---------------------------------------------------------------------------------------------------\n * Mutate Action (CM Example 4) *\n * ---------------------------------------------------------------------------------------------------\n *\n * This action show how you can use the `Mutate()` function without an inline function.\n * This is useful if you want to keep your actions small and focused on a single task,\n * or if you want to reuse the same function in multiple actions.\n */\nWhen(a.ConfigMap).IsCreated().WithName("example-4").Mutate(example4Cb);\n\n// This function uses the complete type definition, but is not required.\nfunction example4Cb(cm: PeprMutateRequest<a.ConfigMap>): void {\n cm.SetLabel("pepr.dev/first", "true");\n cm.SetLabel("pepr.dev/second", "true");\n cm.SetLabel("pepr.dev/third", "true");\n}\n\n/**\n * ---------------------------------------------------------------------------------------------------\n * Mutate Action (CM Example 4a) *\n * ---------------------------------------------------------------------------------------------------\n *\n * This is the same as Example 4, except this only operates on a CM in the `pepr-demo-2` namespace.\n * Note because the Capability defines namespaces, the namespace specified here must be one of those.\n * Alternatively, you can remove the namespace from the Capability definition and specify it here.\n */\nWhen(a.ConfigMap).IsCreated().InNamespace("pepr-demo-2").WithName("example-4a").Mutate(example4Cb);\n\n/**\n * ---------------------------------------------------------------------------------------------------\n * Mutate Action (CM Example 5) *\n * ---------------------------------------------------------------------------------------------------\n *\n * This action is a bit more complex. It will look for any ConfigMap in the `pepr-demo`\n * namespace that has the label `chuck-norris` during CREATE. When it finds one, it will fetch a\n * random Chuck Norris joke from the API and add it to the ConfigMap. This is a great example of how\n * you can use Pepr to make changes to your K8s objects based on external data.\n *\n * Note the use of the `async` keyword. This is required for any action that uses `await` or `fetch()`.\n *\n * Also note we are passing a type to the `fetch()` function. This is optional, but it will help you\n * avoid mistakes when working with the data returned from the API. You can also use the `as` keyword to\n * cast the data returned from the API.\n *\n * These are equivalent:\n * ```ts\n * const joke = await fetch<TheChuckNorrisJoke>("https://icanhazdadjoke.com/");\n * const joke = await fetch("https://icanhazdadjoke.com/") as TheChuckNorrisJoke;\n * ```\n *\n * Alternatively, you can drop the type completely:\n *\n * ```ts\n * fetch("https://icanhazdadjoke.com")\n * ```\n */\ninterface TheChuckNorrisJoke {\n id: string;\n joke: string;\n status: number;\n}\n\nWhen(a.ConfigMap)\n .IsCreatedOrUpdated()\n .WithLabel("chuck-norris")\n .Mutate(cm => cm.SetLabel("got-jokes", "true"))\n .Watch(async cm => {\n const jokeURL = "https://icanhazdadjoke.com";\n\n const mockAgent: MockAgent = new MockAgent();\n setGlobalDispatcher(mockAgent);\n const mockClient = mockAgent.get(jokeURL);\n mockClient.intercept({ path: "/", method: "GET" }).reply(\n 200,\n {\n id: "R7UfaahVfFd",\n joke: "Funny joke goes here.",\n status: 200,\n },\n {\n headers: {\n "Content-Type": "application/json; charset=utf-8",\n },\n },\n );\n\n // Try/catch is not needed as a response object will always be returned\n const response = await fetch<TheChuckNorrisJoke>(jokeURL, {\n headers: {\n Accept: "application/json",\n },\n });\n\n // Instead, check the `response.ok` field\n if (response.ok) {\n const { joke } = response.data;\n // Add Joke to the Store\n await Store.setItemAndWait(jokeURL, joke);\n // Add the Chuck Norris joke to the configmap\n try {\n await K8s(kind.ConfigMap).Apply({\n metadata: {\n name: cm.metadata.name,\n namespace: cm.metadata.namespace,\n },\n data: {\n "chuck-says": Store.getItem(jokeURL),\n },\n });\n } catch (error) {\n Log.error(error, "Failed to apply ConfigMap using server-side apply.", {\n cm,\n });\n }\n }\n\n // You can also assert on different HTTP response codes\n if (response.status === fetchStatus.NOT_FOUND) {\n // Do something else\n return;\n }\n });\n\n/**\n * ---------------------------------------------------------------------------------------------------\n * Mutate Action (Secret Base64 Handling) *\n * ---------------------------------------------------------------------------------------------------\n *\n * The K8s JS client provides incomplete support for base64 encoding/decoding handling for secrets,\n * unlike the GO client. To make this less painful, Pepr automatically handles base64 encoding/decoding\n * secret data before and after the action is executed.\n */\nWhen(a.Secret)\n .IsCreated()\n .WithName("secret-1")\n .Mutate(request => {\n const secret = request.Raw;\n\n // This will be encoded at the end of all processing back to base64: "Y2hhbmdlLXdpdGhvdXQtZW5jb2Rpbmc="\n secret.data.magic = "change-without-encoding";\n\n // You can modify the data directly, and it will be encoded at the end of all processing\n secret.data.example += " - modified by Pepr";\n });\n\n/**\n * ---------------------------------------------------------------------------------------------------\n * Mutate Action (Untyped Custom Resource) *\n * ---------------------------------------------------------------------------------------------------\n *\n * Out of the box, Pepr supports all the standard Kubernetes objects. However, you can also create\n * your own types. This is useful if you are working with an Operator that creates custom resources.\n * There are two ways to do this, the first is to use the `When()` function with a `GenericKind`,\n * the second is to create a new class that extends `GenericKind` and use the `RegisterKind()` function.\n *\n * This example shows how to use the `When()` function with a `GenericKind`. Note that you\n * must specify the `group`, `version`, and `kind` of the object (if applicable). This is how Pepr knows\n * if the action should be triggered or not. Since we are using a `GenericKind`,\n * Pepr will not be able to provide any intellisense for the object, so you will need to refer to the\n * Kubernetes API documentation for the object you are working with.\n *\n * You will need to wait for the CRD in `hello-pepr.samples.yaml` to be created, then you can apply\n *\n * ```yaml\n * apiVersion: pepr.dev/v1\n * kind: Unicorn\n * metadata:\n * name: example-1\n * namespace: pepr-demo\n * spec:\n * message: replace-me\n * counter: 0\n * ```\n */\nWhen(a.GenericKind, {\n group: "pepr.dev",\n version: "v1",\n kind: "Unicorn",\n})\n .IsCreated()\n .WithName("example-1")\n .Mutate(request => {\n request.Merge({\n spec: {\n message: "Hello Pepr without type data!",\n counter: Math.random(),\n },\n });\n });\n\n/**\n * ---------------------------------------------------------------------------------------------------\n * Mutate Action (Typed Custom Resource) *\n * ---------------------------------------------------------------------------------------------------\n *\n * This example shows how to use the `RegisterKind()` function to create a new type. This is useful\n * if you are working with an Operator that creates custom resources and you want to have intellisense\n * for the object. Note that you must specify the `group`, `version`, and `kind` of the object (if applicable)\n * as this is how Pepr knows if the action should be triggered or not.\n *\n * Once you register a new Kind with Pepr, you can use the `When()` function with the new Kind. Ideally,\n * you should register custom Kinds at the top of your Capability file or Pepr Module so they are available\n * to all actions, but we are putting it here for demonstration purposes.\n *\n * You will need to wait for the CRD in `hello-pepr.samples.yaml` to be created, then you can apply\n *\n * ```yaml\n * apiVersion: pepr.dev/v1\n * kind: Unicorn\n * metadata:\n * name: example-2\n * namespace: pepr-demo\n * spec:\n * message: replace-me\n * counter: 0\n * ```*\n */\nclass UnicornKind extends a.GenericKind {\n spec: {\n /**\n * JSDoc comments can be added to explain more details about the field.\n *\n * @example\n * ```ts\n * request.Raw.spec.message = "Hello Pepr!";\n * ```\n * */\n message: string;\n counter: number;\n };\n}\n\nRegisterKind(UnicornKind, {\n group: "pepr.dev",\n version: "v1",\n kind: "Unicorn",\n});\n\nWhen(UnicornKind)\n .IsCreated()\n .WithName("example-2")\n .Mutate(request => {\n request.Merge({\n spec: {\n message: "Hello Pepr with type data!",\n counter: Math.random(),\n },\n });\n });\n\n/**\n * A callback function that is called once the Pepr Store is fully loaded.\n */\nStore.onReady(data => {\n Log.info(data, "Pepr Store Ready");\n});\n';
|
|
9565
|
+
var packageJSON = { name: "pepr", description: "Kubernetes application engine", author: "Defense Unicorns", homepage: "https://github.com/defenseunicorns/pepr", license: "Apache-2.0", bin: "dist/cli.js", repository: "defenseunicorns/pepr", engines: { node: ">=18.0.0" }, files: ["/dist", "/src", "!src/**/*.test.ts", "!src/fixtures/**", "!dist/**/*.test.d.ts*"], version: "0.52.3", main: "dist/lib.js", types: "dist/lib.d.ts", scripts: { build: "tsc -p config/tsconfig.root.json && node build.mjs && npm pack", "build:image": "npm run build && docker buildx build --output type=docker --tag pepr:dev .", "build:image:unicorn": "npm run build && docker buildx build --output type=docker --tag pepr/private:dev $(node scripts/read-unicorn-build-args.mjs) .", ci: "npm ci", "format:check": "npm run format:src && npm run format:tests && npm run format:markdown && npm run format:integration && npm run format:prettier -- --check", "format:fix": "npm run format:src -- --fix && npm run format:markdown -- --fix && npm run format:integration -- --fix && npm run format:prettier -- --write", "format:integration": "eslint --config config/eslint.integration.config.mjs integration/cli integration/helpers", "format:markdown": 'npx -y markdownlint-cli --config config/.markdownlint.json --ignore adr --ignore integration/testroot --ignore pepr-test-module --ignore node_modules "**/*.md"', "format:prettier": "prettier --config config/.prettierrc src integration/cli/**/*.ts integration/helpers/**/*.ts", "format:src": "eslint --config config/eslint.root.config.mjs 'src/**/*.ts' --ignore-pattern '**/*.test.ts' --ignore-pattern 'src/templates/**'", "format:tests": "eslint --config config/eslint.test.config.mjs 'src/**/*.test.ts'", "gen-data-json": "node hack/build-template-data.js", prebuild: "rm -fr dist/* && npm run gen-data-json", prepare: `if [ "$NODE_ENV" != 'production' ]; then husky; fi`, "set:version": "node scripts/set-version.js", test: "npm run test:unit && npm run test:journey && npm run test:journey-wasm", "test:artifacts": "npm run build && vitest run src/build-artifact.test.ts", "test:docs": "vitest run --config=config/vitest.integration.config.ts integration/cli/docs/*.test.ts", "test:integration": "npm run test:integration:prep && npm run test:integration:run", "test:integration:prep": "./integration/prep.sh", "test:integration:run": "vitest run --config=config/vitest.integration.config.ts integration", "test:journey": "npm run test:journey:k3d && npm run build && npm run test:journey:image && npm run test:journey:run", "test:journey-wasm": "npm run test:journey:k3d && npm run build && npm run test:journey:image && npm run test:journey:run-wasm", "test:journey-wasm:unicorn": "npm run test:journey:k3d && npm run build && npm run test:journey:image:unicorn && npm run test:journey:run-wasm", "test:journey:image": "npm run build && docker buildx build --output type=docker --tag pepr:dev . && k3d image import pepr:dev -c pepr-dev", "test:journey:image:unicorn": "npm run build && docker buildx build --output type=docker --tag pepr/private:dev $(node scripts/read-unicorn-build-args.mjs) . && k3d image import pepr/private:dev -c pepr-dev", "test:journey:k3d": "k3d cluster delete pepr-dev && k3d cluster create pepr-dev --k3s-arg '--debug@server:0' --wait && kubectl rollout status deployment -n kube-system", "test:journey:run": "vitest run --config=config/vitest.journey.config.ts journey/entrypoint.test.ts", "test:journey:run-wasm": "vitest run --config=config/vitest.journey.config.ts journey/entrypoint-wasm.test.ts", "test:journey:unicorn": "npm run test:journey:k3d && npm run test:journey:image:unicorn && npm run test:journey:run", "test:unit": "npm run gen-data-json && NODE_OPTIONS=--no-deprecation vitest --config config/vitest.root.config.ts run --coverage", "test:upgrade:unicorn": "npm run test:journey:k3d && npm run test:journey:image:unicorn && vitest run integration/cluster/upgrade.test.ts", "test:upgrade:upstream": "npm run test:journey:k3d && npm run test:journey:image && vitest run integration/cluster/upgrade.test.ts" }, dependencies: { "@types/ramda": "0.31.0", commander: "14.0.0", express: "5.1.0", "fast-json-patch": "3.1.1", heredoc: "^1.3.1", "http-status-codes": "^2.3.0", "json-pointer": "^0.6.2", "kubernetes-fluent-client": "3.10.0", pino: "9.7.0", "pino-pretty": "13.1.1", "prom-client": "15.1.3", ramda: "0.31.3", "ts-morph": "^26.0.0" }, devDependencies: { "@commitlint/cli": "19.8.1", "@commitlint/config-conventional": "19.8.1", "@fast-check/vitest": "^0.2.1", "@types/eslint": "9.6.1", "@types/express": "5.0.3", "@types/json-pointer": "^1.0.34", "@types/node": "24.x.x", "@types/node-forge": "1.3.13", "@types/uuid": "10.0.0", "@types/ws": "^8.18.1", "@vitest/coverage-v8": "^3.2.3", "fast-check": "^4.0.0", globals: "^16.0.0", husky: "^9.1.6", "js-yaml": "^4.1.0", shellcheck: "^3.0.0", tsx: "^4.20.3", undici: "^7.0.1", vitest: "^3.2.3" }, overrides: { glob: "^9.0.0" }, peerDependencies: { "@types/prompts": "2.4.9", "@typescript-eslint/eslint-plugin": "8.38.0", "@typescript-eslint/parser": "8.38.0", esbuild: "0.25.8", eslint: "9.32.0", "node-forge": "1.3.1", prettier: "3.6.2", prompts: "2.4.2", typescript: "5.8.3", uuid: "11.1.0" } };
|
|
9566
|
+
|
|
9567
|
+
// src/cli/init/utils.ts
|
|
9568
|
+
var import_fs7 = require("fs");
|
|
9569
|
+
function sanitizeName(name2) {
|
|
9570
|
+
if (typeof name2 !== "string") {
|
|
9571
|
+
throw TypeError(
|
|
9572
|
+
`sanitizeName() was called with a non-string value. The value is: ${name2} of type ${typeof name2}`
|
|
9573
|
+
);
|
|
9680
9574
|
}
|
|
9681
|
-
|
|
9575
|
+
let sanitized = name2.toLowerCase().replace(/[^a-z0-9-]+/gi, "-");
|
|
9576
|
+
sanitized = sanitized.replace(/^-+|-+$/g, "");
|
|
9577
|
+
sanitized = sanitized.replace(/--+/g, "-");
|
|
9578
|
+
return sanitized;
|
|
9682
9579
|
}
|
|
9683
|
-
async function
|
|
9580
|
+
async function createDir(dir) {
|
|
9684
9581
|
try {
|
|
9685
|
-
|
|
9686
|
-
|
|
9687
|
-
|
|
9688
|
-
|
|
9689
|
-
|
|
9690
|
-
|
|
9691
|
-
}
|
|
9692
|
-
}
|
|
9693
|
-
function checkIronBankImage(registry, image, peprVersion) {
|
|
9694
|
-
return registry === "Iron Bank" ? `registry1.dso.mil/ironbank/opensource/defenseunicorns/pepr/controller:v${peprVersion}` : image;
|
|
9695
|
-
}
|
|
9696
|
-
function validImagePullSecret(imagePullSecretName) {
|
|
9697
|
-
if (imagePullSecretName) {
|
|
9698
|
-
const error = "Invalid imagePullSecret. Please provide a valid name as defined in RFC 1123.";
|
|
9699
|
-
if (sanitizeResourceName(imagePullSecretName) !== imagePullSecretName) {
|
|
9700
|
-
console.error(error);
|
|
9701
|
-
process.exit(1);
|
|
9582
|
+
await import_fs7.promises.mkdir(dir);
|
|
9583
|
+
} catch (err) {
|
|
9584
|
+
if (err && err.code === "EEXIST") {
|
|
9585
|
+
throw new Error(`Directory ${dir} already exists`);
|
|
9586
|
+
} else {
|
|
9587
|
+
throw err;
|
|
9702
9588
|
}
|
|
9703
9589
|
}
|
|
9704
9590
|
}
|
|
9705
|
-
|
|
9706
|
-
if (
|
|
9707
|
-
|
|
9708
|
-
(0, import_child_process2.execSync)(`docker build --tag ${image} -f Dockerfile.controller .`, {
|
|
9709
|
-
stdio: "inherit"
|
|
9710
|
-
});
|
|
9711
|
-
(0, import_child_process2.execSync)(`docker push ${image}`, { stdio: "inherit" });
|
|
9712
|
-
}
|
|
9713
|
-
}
|
|
9714
|
-
function handleValidCapabilityNames(capabilities) {
|
|
9715
|
-
try {
|
|
9716
|
-
validateCapabilityNames(capabilities);
|
|
9717
|
-
} catch (e) {
|
|
9718
|
-
console.error(`Error loading capability:`, e);
|
|
9719
|
-
process.exit(1);
|
|
9720
|
-
}
|
|
9721
|
-
}
|
|
9722
|
-
async function watchForChanges(ctxCfg, reloader) {
|
|
9723
|
-
const ctx = await (0, import_esbuild.context)(ctxCfg);
|
|
9724
|
-
if (reloader) {
|
|
9725
|
-
await ctx.watch();
|
|
9726
|
-
} else {
|
|
9727
|
-
await ctx.rebuild();
|
|
9728
|
-
await ctx.dispose();
|
|
9591
|
+
function write(path4, data) {
|
|
9592
|
+
if (typeof data !== "string") {
|
|
9593
|
+
data = JSON.stringify(data, null, 2);
|
|
9729
9594
|
}
|
|
9730
|
-
return
|
|
9595
|
+
return import_fs7.promises.writeFile(path4, data);
|
|
9731
9596
|
}
|
|
9732
|
-
|
|
9733
|
-
|
|
9734
|
-
|
|
9735
|
-
|
|
9736
|
-
const
|
|
9737
|
-
|
|
9738
|
-
|
|
9739
|
-
|
|
9740
|
-
|
|
9741
|
-
|
|
9742
|
-
|
|
9743
|
-
|
|
9744
|
-
|
|
9597
|
+
|
|
9598
|
+
// src/cli/init/templates.ts
|
|
9599
|
+
var { dependencies, devDependencies, peerDependencies, scripts, version } = packageJSON;
|
|
9600
|
+
function genPkgJSON(opts) {
|
|
9601
|
+
const uuid = !opts.uuid ? (0, import_uuid.v4)() : opts.uuid;
|
|
9602
|
+
const name2 = sanitizeName(opts.name);
|
|
9603
|
+
const { typescript } = peerDependencies;
|
|
9604
|
+
const data = {
|
|
9605
|
+
name: name2,
|
|
9606
|
+
version: "0.0.1",
|
|
9607
|
+
description: opts.description,
|
|
9608
|
+
keywords: ["pepr", "k8s", "policy-engine", "pepr-module", "security"],
|
|
9609
|
+
engines: {
|
|
9610
|
+
node: ">=20.0.0"
|
|
9611
|
+
},
|
|
9612
|
+
pepr: {
|
|
9613
|
+
uuid,
|
|
9614
|
+
onError: opts.errorBehavior,
|
|
9615
|
+
webhookTimeout: 10,
|
|
9616
|
+
customLabels: {
|
|
9617
|
+
namespace: {
|
|
9618
|
+
"pepr.dev": ""
|
|
9619
|
+
}
|
|
9745
9620
|
},
|
|
9746
|
-
|
|
9747
|
-
|
|
9748
|
-
|
|
9749
|
-
|
|
9750
|
-
|
|
9751
|
-
|
|
9621
|
+
alwaysIgnore: {
|
|
9622
|
+
namespaces: []
|
|
9623
|
+
},
|
|
9624
|
+
admission: {
|
|
9625
|
+
alwaysIgnore: {
|
|
9626
|
+
namespaces: []
|
|
9627
|
+
}
|
|
9628
|
+
},
|
|
9629
|
+
watch: {
|
|
9630
|
+
alwaysIgnore: {
|
|
9631
|
+
namespaces: []
|
|
9632
|
+
}
|
|
9633
|
+
},
|
|
9634
|
+
includedFiles: [],
|
|
9635
|
+
env: {}
|
|
9636
|
+
},
|
|
9637
|
+
scripts: {
|
|
9638
|
+
"k3d-setup": scripts["test:journey:k3d"]
|
|
9639
|
+
},
|
|
9640
|
+
dependencies: {
|
|
9641
|
+
pepr: version,
|
|
9642
|
+
undici: "^7.0.1"
|
|
9643
|
+
},
|
|
9644
|
+
devDependencies: {
|
|
9645
|
+
typescript
|
|
9646
|
+
},
|
|
9647
|
+
overrides: {
|
|
9648
|
+
"brace-expansion": "1.1.11"
|
|
9649
|
+
}
|
|
9650
|
+
};
|
|
9651
|
+
return {
|
|
9652
|
+
data,
|
|
9653
|
+
path: "package.json",
|
|
9654
|
+
print: (0, import_util.inspect)(data, false, 5, true)
|
|
9655
|
+
};
|
|
9656
|
+
}
|
|
9657
|
+
var peprTSTemplate = {
|
|
9658
|
+
path: "pepr.ts",
|
|
9659
|
+
data: peprTS
|
|
9660
|
+
};
|
|
9661
|
+
var readme = {
|
|
9662
|
+
path: "README.md",
|
|
9663
|
+
data: readmeMd
|
|
9664
|
+
};
|
|
9665
|
+
var helloPepr = {
|
|
9666
|
+
path: "hello-pepr.ts",
|
|
9667
|
+
data: helloPeprTS
|
|
9668
|
+
};
|
|
9669
|
+
var gitignore = {
|
|
9670
|
+
path: ".gitignore",
|
|
9671
|
+
data: gitIgnore
|
|
9672
|
+
};
|
|
9673
|
+
var samplesYaml = {
|
|
9674
|
+
path: "hello-pepr.samples.yaml",
|
|
9675
|
+
data: hello_pepr_samples_default.map((r) => (0, import_client_node5.dumpYaml)(r, { noRefs: true })).join("---\n")
|
|
9676
|
+
};
|
|
9677
|
+
var snippet = {
|
|
9678
|
+
path: "pepr.code-snippets",
|
|
9679
|
+
data: pepr_code_snippets_default
|
|
9680
|
+
};
|
|
9681
|
+
var codeSettings = {
|
|
9682
|
+
path: "settings.json",
|
|
9683
|
+
data: settings_default
|
|
9684
|
+
};
|
|
9685
|
+
var tsConfig = {
|
|
9686
|
+
path: "tsconfig.json",
|
|
9687
|
+
data: tsconfig_module_default
|
|
9688
|
+
};
|
|
9689
|
+
var prettier = {
|
|
9690
|
+
path: ".prettierrc",
|
|
9691
|
+
data: prettierrc_default
|
|
9692
|
+
};
|
|
9693
|
+
var eslint = {
|
|
9694
|
+
path: "eslint.config.mjs",
|
|
9695
|
+
data: (0, import_fs8.readFileSync)(
|
|
9696
|
+
import_path3.default.resolve(
|
|
9697
|
+
(() => {
|
|
9698
|
+
const fullPath = __dirname;
|
|
9699
|
+
const lengthOfSuffix = "pepr/".length;
|
|
9700
|
+
const lastPeprIndex = fullPath.lastIndexOf("pepr/");
|
|
9701
|
+
return fullPath.substring(0, lastPeprIndex + lengthOfSuffix);
|
|
9702
|
+
})(),
|
|
9703
|
+
"src/templates/eslint.config.mjs"
|
|
9704
|
+
),
|
|
9705
|
+
"utf-8"
|
|
9706
|
+
)
|
|
9707
|
+
};
|
|
9708
|
+
|
|
9709
|
+
// src/cli/format/index.ts
|
|
9710
|
+
var import_eslint = require("eslint");
|
|
9711
|
+
|
|
9712
|
+
// src/cli/format/format.helpers.ts
|
|
9713
|
+
var import_fs9 = require("fs");
|
|
9714
|
+
var import_prettier = require("prettier");
|
|
9715
|
+
async function formatWithPrettier(results, validateOnly) {
|
|
9716
|
+
let hasFailure = false;
|
|
9717
|
+
for (const { filePath } of results) {
|
|
9718
|
+
const content = await import_fs9.promises.readFile(filePath, "utf8");
|
|
9719
|
+
const cfg = await (0, import_prettier.resolveConfig)(filePath);
|
|
9720
|
+
const formatted = await (0, import_prettier.format)(content, { filepath: filePath, ...cfg });
|
|
9721
|
+
if (validateOnly && formatted !== content) {
|
|
9722
|
+
hasFailure = true;
|
|
9723
|
+
console.error(`File ${filePath} is not formatted correctly`);
|
|
9752
9724
|
} else {
|
|
9753
|
-
|
|
9725
|
+
await import_fs9.promises.writeFile(filePath, formatted);
|
|
9754
9726
|
}
|
|
9755
|
-
await import_fs9.promises.writeFile(yamlPath, yaml);
|
|
9756
|
-
await import_fs9.promises.writeFile(zarfPath, localZarf);
|
|
9757
|
-
await assets.generateHelmChart(webhookConfigGenerator, getWatcher, getModuleSecret, outputDir2);
|
|
9758
|
-
console.info(`K8s resource for the module saved to ${yamlPath}`);
|
|
9759
|
-
} catch (error) {
|
|
9760
|
-
console.error(`Error generating YAML: ${error}`);
|
|
9761
|
-
process.exit(1);
|
|
9762
9727
|
}
|
|
9728
|
+
return hasFailure;
|
|
9763
9729
|
}
|
|
9764
9730
|
|
|
9765
|
-
// src/cli/
|
|
9766
|
-
|
|
9767
|
-
|
|
9768
|
-
|
|
9769
|
-
|
|
9770
|
-
|
|
9771
|
-
|
|
9772
|
-
|
|
9773
|
-
])
|
|
9774
|
-
).addOption(
|
|
9775
|
-
new import_commander.Option(
|
|
9776
|
-
"-I, --registry-info <registry/username>",
|
|
9777
|
-
"Provide the image registry and username for building and pushing a custom WASM container. Requires authentication. Conflicts with --custom-image and --registry. Builds and pushes `'<registry/username>/custom-pepr-controller:<current-version>'`."
|
|
9778
|
-
).conflicts(["customImage", "registry"])
|
|
9779
|
-
).option("-P, --with-pull-secret <name>", "Use image pull secret for controller Deployment.", "").addOption(
|
|
9780
|
-
new import_commander.Option(
|
|
9781
|
-
"-c, --custom-name <name>",
|
|
9782
|
-
"Set name for zarf component and service monitors in helm charts."
|
|
9783
|
-
)
|
|
9784
|
-
).option("-e, --entry-point <file>", "Specify the entry point file to build with.", peprTS2).addOption(
|
|
9785
|
-
new import_commander.Option(
|
|
9786
|
-
"-i, --custom-image <image>",
|
|
9787
|
-
"Specify a custom image with version for deployments. Conflicts with --registry-info and --registry. Example: 'docker.io/username/custom-pepr-controller:v1.0.0'"
|
|
9788
|
-
).conflicts(["registryInfo", "registry"])
|
|
9789
|
-
).option(
|
|
9790
|
-
"-n, --no-embed",
|
|
9791
|
-
"Disable embedding of deployment files into output module. Useful when creating library modules intended solely for reuse/distribution via NPM."
|
|
9792
|
-
).option("-o, --output <directory>", "Set output directory.", "dist").addOption(
|
|
9793
|
-
new import_commander.Option(
|
|
9794
|
-
"-r, --registry <GitHub|Iron Bank>",
|
|
9795
|
-
"Container registry: Choose container registry for deployment manifests. Conflicts with --custom-image and --registry-info."
|
|
9796
|
-
).conflicts(["customImage", "registryInfo"]).choices(["GitHub", "Iron Bank"])
|
|
9797
|
-
).option(
|
|
9798
|
-
"-t, --timeout <seconds>",
|
|
9799
|
-
"How long the API server should wait for a webhook to respond before treating the call as a failure.",
|
|
9800
|
-
parseTimeout
|
|
9801
|
-
).addOption(
|
|
9802
|
-
new import_commander.Option("-z, --zarf <manifest|chart>", "Set Zarf package type").choices(["manifest", "chart"]).default("manifest")
|
|
9803
|
-
).action(async (opts) => {
|
|
9804
|
-
outputDir = await createOutputDirectory(opts.output);
|
|
9805
|
-
const buildModuleResult = await buildModule(void 0, opts.entryPoint, opts.embed);
|
|
9806
|
-
const { cfg, path: path4 } = buildModuleResult;
|
|
9807
|
-
if (opts.customName) {
|
|
9808
|
-
process.env.PEPR_CUSTOM_BUILD_NAME = opts.customName;
|
|
9809
|
-
}
|
|
9810
|
-
const image = assignImage({
|
|
9811
|
-
customImage: opts.customImage,
|
|
9812
|
-
registryInfo: opts.registryInfo,
|
|
9813
|
-
peprVersion: cfg.pepr.peprVersion,
|
|
9814
|
-
registry: opts.registry
|
|
9815
|
-
});
|
|
9816
|
-
if (opts.timeout !== void 0) {
|
|
9817
|
-
cfg.pepr.webhookTimeout = opts.timeout;
|
|
9818
|
-
}
|
|
9819
|
-
if (opts.registryInfo !== void 0) {
|
|
9820
|
-
console.info(`Including ${cfg.pepr.includedFiles.length} files in controller image.`);
|
|
9821
|
-
await handleCustomImageBuild(
|
|
9822
|
-
cfg.pepr.includedFiles,
|
|
9823
|
-
cfg.pepr.peprVersion,
|
|
9824
|
-
cfg.description,
|
|
9825
|
-
image
|
|
9826
|
-
);
|
|
9827
|
-
}
|
|
9828
|
-
if (!opts.embed) {
|
|
9829
|
-
console.info(`Module built successfully at ${path4}`);
|
|
9830
|
-
return;
|
|
9731
|
+
// src/cli/format/index.ts
|
|
9732
|
+
function format_default(program2) {
|
|
9733
|
+
program2.command("format").description("Lint and format this Pepr module").option("-v, --validate-only", "Do not modify files, only validate formatting.").action(async (opts) => {
|
|
9734
|
+
const success = await peprFormat(opts.validateOnly);
|
|
9735
|
+
if (success) {
|
|
9736
|
+
logger_default.info("Module formatted");
|
|
9737
|
+
} else {
|
|
9738
|
+
process.exit(1);
|
|
9831
9739
|
}
|
|
9832
|
-
const assets = new Assets(
|
|
9833
|
-
{
|
|
9834
|
-
...cfg.pepr,
|
|
9835
|
-
appVersion: cfg.version,
|
|
9836
|
-
description: cfg.description,
|
|
9837
|
-
alwaysIgnore: {
|
|
9838
|
-
namespaces: cfg.pepr.alwaysIgnore?.namespaces
|
|
9839
|
-
},
|
|
9840
|
-
// Can override the rbacMode with the CLI option
|
|
9841
|
-
rbacMode: determineRbacMode(opts, cfg)
|
|
9842
|
-
},
|
|
9843
|
-
path4,
|
|
9844
|
-
opts.withPullSecret === "" ? [] : [opts.withPullSecret]
|
|
9845
|
-
);
|
|
9846
|
-
if (image !== "") assets.image = image;
|
|
9847
|
-
validImagePullSecret(opts.withPullSecret);
|
|
9848
|
-
handleValidCapabilityNames(assets.capabilities);
|
|
9849
|
-
await generateYamlAndWriteToDisk({
|
|
9850
|
-
uuid: cfg.pepr.uuid,
|
|
9851
|
-
outputDir,
|
|
9852
|
-
imagePullSecret: opts.withPullSecret,
|
|
9853
|
-
zarf: opts.zarf,
|
|
9854
|
-
assets
|
|
9855
|
-
});
|
|
9856
9740
|
});
|
|
9857
9741
|
}
|
|
9858
|
-
|
|
9859
|
-
|
|
9860
|
-
|
|
9861
|
-
|
|
9862
|
-
|
|
9863
|
-
|
|
9864
|
-
|
|
9742
|
+
async function peprFormat(validateOnly) {
|
|
9743
|
+
{
|
|
9744
|
+
try {
|
|
9745
|
+
const eslint2 = new import_eslint.ESLint();
|
|
9746
|
+
const results = await eslint2.lintFiles(["./**/*.ts"]);
|
|
9747
|
+
let hasFailure = false;
|
|
9748
|
+
results.forEach(async (result) => {
|
|
9749
|
+
const errorCount = result.fatalErrorCount + result.errorCount;
|
|
9750
|
+
if (errorCount > 0) {
|
|
9751
|
+
hasFailure = true;
|
|
9752
|
+
}
|
|
9753
|
+
});
|
|
9754
|
+
const formatter = await eslint2.loadFormatter("stylish");
|
|
9755
|
+
const resultText = await formatter.format(results, {});
|
|
9756
|
+
if (resultText) {
|
|
9757
|
+
logger_default.info(resultText);
|
|
9758
|
+
}
|
|
9759
|
+
if (!validateOnly) {
|
|
9760
|
+
await import_eslint.ESLint.outputFixes(results);
|
|
9761
|
+
}
|
|
9762
|
+
hasFailure = hasFailure || await formatWithPrettier(results, validateOnly);
|
|
9763
|
+
return !hasFailure;
|
|
9764
|
+
} catch (error) {
|
|
9765
|
+
logger_default.error(error, `Error formatting module:`);
|
|
9766
|
+
return false;
|
|
9767
|
+
}
|
|
9768
|
+
}
|
|
9769
|
+
}
|
|
9770
|
+
|
|
9771
|
+
// src/cli/build/loadModule.ts
|
|
9772
|
+
var import_promises = __toESM(require("fs/promises"));
|
|
9773
|
+
var import_posix = require("path/posix");
|
|
9774
|
+
async function loadModule(outputDir, entryPoint) {
|
|
9775
|
+
const entryPointPath = (0, import_posix.resolve)(".", entryPoint);
|
|
9776
|
+
const modulePath = (0, import_posix.dirname)(entryPointPath);
|
|
9777
|
+
const cfgPath = (0, import_posix.resolve)(modulePath, "package.json");
|
|
9865
9778
|
try {
|
|
9866
|
-
await
|
|
9867
|
-
await
|
|
9779
|
+
await import_promises.default.access(cfgPath);
|
|
9780
|
+
await import_promises.default.access(entryPointPath);
|
|
9868
9781
|
} catch {
|
|
9869
9782
|
console.error(
|
|
9870
9783
|
`Could not find ${cfgPath} or ${entryPointPath} in the current directory. Please run this command from the root of your module's directory.`
|
|
9871
9784
|
);
|
|
9872
9785
|
process.exit(1);
|
|
9873
9786
|
}
|
|
9874
|
-
const moduleText = await
|
|
9787
|
+
const moduleText = await import_promises.default.readFile(cfgPath, { encoding: "utf-8" });
|
|
9875
9788
|
const cfg = JSON.parse(moduleText);
|
|
9876
9789
|
const { uuid } = cfg.pepr;
|
|
9877
9790
|
const name2 = `pepr-${uuid}.js`;
|
|
@@ -9884,13 +9797,18 @@ async function loadModule(entryPoint = peprTS2) {
|
|
|
9884
9797
|
entryPointPath,
|
|
9885
9798
|
modulePath,
|
|
9886
9799
|
name: name2,
|
|
9887
|
-
path: (0,
|
|
9800
|
+
path: (0, import_posix.resolve)(outputDir, name2),
|
|
9888
9801
|
uuid
|
|
9889
9802
|
};
|
|
9890
9803
|
}
|
|
9891
|
-
|
|
9804
|
+
|
|
9805
|
+
// src/cli/build/buildModule.ts
|
|
9806
|
+
var externalLibs = Object.keys(dependencies);
|
|
9807
|
+
externalLibs.push("pepr");
|
|
9808
|
+
externalLibs.push("@kubernetes/client-node");
|
|
9809
|
+
async function buildModule(outputDir, reloader, entryPoint = "pepr.ts", embed = true) {
|
|
9892
9810
|
try {
|
|
9893
|
-
const { cfg, modulePath, path: path4, uuid } = await loadModule(entryPoint);
|
|
9811
|
+
const { cfg, modulePath, path: path4, uuid } = await loadModule(outputDir, entryPoint);
|
|
9894
9812
|
await checkFormat();
|
|
9895
9813
|
const npmRoot = (0, import_child_process3.execFileSync)("npm", ["root"]).toString().trim();
|
|
9896
9814
|
const args = ["--project", `${modulePath}/tsconfig.json`, "--outdir", outputDir];
|
|
@@ -9978,12 +9896,109 @@ async function checkFormat() {
|
|
|
9978
9896
|
}
|
|
9979
9897
|
}
|
|
9980
9898
|
|
|
9899
|
+
// src/cli/build/index.ts
|
|
9900
|
+
function build_default(program2) {
|
|
9901
|
+
program2.command("build").description("Build a Pepr Module for deployment").addOption(
|
|
9902
|
+
new import_commander.Option("-M, --rbac-mode <mode>", "Override module config and set RBAC mode.").choices([
|
|
9903
|
+
"admin",
|
|
9904
|
+
"scoped"
|
|
9905
|
+
])
|
|
9906
|
+
).addOption(
|
|
9907
|
+
new import_commander.Option(
|
|
9908
|
+
"-I, --registry-info <registry/username>",
|
|
9909
|
+
"Provide the image registry and username for building and pushing a custom WASM container. Requires authentication. Conflicts with --custom-image and --registry. Builds and pushes `'<registry/username>/custom-pepr-controller:<current-version>'`."
|
|
9910
|
+
).conflicts(["customImage", "registry"])
|
|
9911
|
+
).option("-P, --with-pull-secret <name>", "Use image pull secret for controller Deployment.", "").addOption(
|
|
9912
|
+
new import_commander.Option(
|
|
9913
|
+
"-c, --custom-name <name>",
|
|
9914
|
+
"Set name for zarf component and service monitors in helm charts."
|
|
9915
|
+
)
|
|
9916
|
+
).option("-e, --entry-point <file>", "Specify the entry point file to build with.", "pepr.ts").addOption(
|
|
9917
|
+
new import_commander.Option(
|
|
9918
|
+
"-i, --custom-image <image>",
|
|
9919
|
+
"Specify a custom image with version for deployments. Conflicts with --registry-info and --registry. Example: 'docker.io/username/custom-pepr-controller:v1.0.0'"
|
|
9920
|
+
).conflicts(["registryInfo", "registry"])
|
|
9921
|
+
).option(
|
|
9922
|
+
"-n, --no-embed",
|
|
9923
|
+
"Disable embedding of deployment files into output module. Useful when creating library modules intended solely for reuse/distribution via NPM."
|
|
9924
|
+
).option("-o, --output <directory>", "Set output directory.", "dist").addOption(
|
|
9925
|
+
new import_commander.Option(
|
|
9926
|
+
"-r, --registry <GitHub|Iron Bank>",
|
|
9927
|
+
"Container registry: Choose container registry for deployment manifests. Conflicts with --custom-image and --registry-info."
|
|
9928
|
+
).conflicts(["customImage", "registryInfo"]).choices(["GitHub", "Iron Bank"])
|
|
9929
|
+
).option(
|
|
9930
|
+
"-t, --timeout <seconds>",
|
|
9931
|
+
"How long the API server should wait for a webhook to respond before treating the call as a failure.",
|
|
9932
|
+
parseTimeout
|
|
9933
|
+
).addOption(
|
|
9934
|
+
new import_commander.Option("-z, --zarf <manifest|chart>", "Set Zarf package type").choices(["manifest", "chart"]).default("manifest")
|
|
9935
|
+
).action(async (opts) => {
|
|
9936
|
+
const outputDir = await createOutputDirectory(opts.output);
|
|
9937
|
+
const buildModuleResult = await buildModule(
|
|
9938
|
+
outputDir,
|
|
9939
|
+
void 0,
|
|
9940
|
+
opts.entryPoint,
|
|
9941
|
+
opts.embed
|
|
9942
|
+
);
|
|
9943
|
+
const { cfg, path: path4 } = buildModuleResult;
|
|
9944
|
+
if (opts.customName) {
|
|
9945
|
+
process.env.PEPR_CUSTOM_BUILD_NAME = opts.customName;
|
|
9946
|
+
}
|
|
9947
|
+
const image = assignImage({
|
|
9948
|
+
customImage: opts.customImage,
|
|
9949
|
+
registryInfo: opts.registryInfo,
|
|
9950
|
+
peprVersion: cfg.pepr.peprVersion,
|
|
9951
|
+
registry: opts.registry
|
|
9952
|
+
});
|
|
9953
|
+
if (opts.timeout !== void 0) {
|
|
9954
|
+
cfg.pepr.webhookTimeout = opts.timeout;
|
|
9955
|
+
}
|
|
9956
|
+
if (opts.registryInfo !== void 0) {
|
|
9957
|
+
console.info(`Including ${cfg.pepr.includedFiles.length} files in controller image.`);
|
|
9958
|
+
await handleCustomImageBuild(
|
|
9959
|
+
cfg.pepr.includedFiles,
|
|
9960
|
+
cfg.pepr.peprVersion,
|
|
9961
|
+
cfg.description,
|
|
9962
|
+
image
|
|
9963
|
+
);
|
|
9964
|
+
}
|
|
9965
|
+
if (!opts.embed) {
|
|
9966
|
+
console.info(`Module built successfully at ${path4}`);
|
|
9967
|
+
return;
|
|
9968
|
+
}
|
|
9969
|
+
const assets = new Assets(
|
|
9970
|
+
{
|
|
9971
|
+
...cfg.pepr,
|
|
9972
|
+
appVersion: cfg.version,
|
|
9973
|
+
description: cfg.description,
|
|
9974
|
+
alwaysIgnore: {
|
|
9975
|
+
namespaces: cfg.pepr.alwaysIgnore?.namespaces
|
|
9976
|
+
},
|
|
9977
|
+
// Can override the rbacMode with the CLI option
|
|
9978
|
+
rbacMode: determineRbacMode(opts, cfg)
|
|
9979
|
+
},
|
|
9980
|
+
path4,
|
|
9981
|
+
opts.withPullSecret === "" ? [] : [opts.withPullSecret]
|
|
9982
|
+
);
|
|
9983
|
+
if (image !== "") assets.image = image;
|
|
9984
|
+
validImagePullSecret(opts.withPullSecret);
|
|
9985
|
+
handleValidCapabilityNames(assets.capabilities);
|
|
9986
|
+
await generateYamlAndWriteToDisk({
|
|
9987
|
+
uuid: cfg.pepr.uuid,
|
|
9988
|
+
outputDir,
|
|
9989
|
+
imagePullSecret: opts.withPullSecret,
|
|
9990
|
+
zarf: opts.zarf,
|
|
9991
|
+
assets
|
|
9992
|
+
});
|
|
9993
|
+
});
|
|
9994
|
+
}
|
|
9995
|
+
|
|
9981
9996
|
// src/cli/deploy.ts
|
|
9982
9997
|
var import_prompts = __toESM(require("prompts"));
|
|
9983
9998
|
|
|
9984
9999
|
// src/lib/assets/deploy.ts
|
|
9985
10000
|
var import_crypto3 = __toESM(require("crypto"));
|
|
9986
|
-
var
|
|
10001
|
+
var import_fs10 = require("fs");
|
|
9987
10002
|
var import_kubernetes_fluent_client3 = require("kubernetes-fluent-client");
|
|
9988
10003
|
|
|
9989
10004
|
// src/lib/k8s.ts
|
|
@@ -10089,10 +10104,10 @@ async function deployWebhook(assets, force, webhookTimeout) {
|
|
|
10089
10104
|
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.Namespace).Apply(getNamespace(assets.config.customLabels?.namespace));
|
|
10090
10105
|
await handleWebhookConfiguration(assets, "mutate" /* MUTATE */, webhookTimeout, force);
|
|
10091
10106
|
await handleWebhookConfiguration(assets, "validate" /* VALIDATE */, webhookTimeout, force);
|
|
10092
|
-
logger_default.
|
|
10107
|
+
logger_default.debug("Applying the Pepr Store CRD if it doesn't exist");
|
|
10093
10108
|
await (0, import_kubernetes_fluent_client3.K8s)(import_kubernetes_fluent_client3.kind.CustomResourceDefinition).Apply(peprStoreCRD, { force });
|
|
10094
10109
|
if (assets.host) return;
|
|
10095
|
-
const code = await
|
|
10110
|
+
const code = await import_fs10.promises.readFile(assets.path);
|
|
10096
10111
|
if (!code.length) throw new Error("No code provided");
|
|
10097
10112
|
const hash = import_crypto3.default.createHash("sha256").update(code).digest("hex");
|
|
10098
10113
|
await setupRBAC(assets.name, assets.capabilities, force, assets.config);
|
|
@@ -10184,7 +10199,7 @@ async function namespaceDeploymentsReady(namespace = "pepr-system") {
|
|
|
10184
10199
|
if (ready) {
|
|
10185
10200
|
return ready;
|
|
10186
10201
|
}
|
|
10187
|
-
await new Promise((
|
|
10202
|
+
await new Promise((resolve7) => setTimeout(resolve7, 1e3));
|
|
10188
10203
|
}
|
|
10189
10204
|
logger_default.info(`All ${namespace} deployments are ready`);
|
|
10190
10205
|
}
|
|
@@ -10248,7 +10263,7 @@ async function getUserConfirmation(opts) {
|
|
|
10248
10263
|
return confirmation.yes ? true : false;
|
|
10249
10264
|
}
|
|
10250
10265
|
async function buildAndDeployModule(image, force) {
|
|
10251
|
-
const builtModule = await buildModule();
|
|
10266
|
+
const builtModule = await buildModule("dist");
|
|
10252
10267
|
if (!builtModule) {
|
|
10253
10268
|
return;
|
|
10254
10269
|
}
|
|
@@ -10303,7 +10318,7 @@ function validateNamespaces(capability, webhook) {
|
|
|
10303
10318
|
var import_prompts2 = __toESM(require("prompts"));
|
|
10304
10319
|
var import_child_process4 = require("child_process");
|
|
10305
10320
|
var import_kubernetes_fluent_client5 = require("kubernetes-fluent-client");
|
|
10306
|
-
var
|
|
10321
|
+
var import_fs11 = require("fs");
|
|
10307
10322
|
function dev_default(program2) {
|
|
10308
10323
|
program2.command("dev").description("Setup a local webhook development environment").option("-H, --host <host>", "Host to listen on", "host.k3d.internal").option("-y, --yes", "Skip confirmation prompt").action(async (opts) => {
|
|
10309
10324
|
if (!opts.yes) {
|
|
@@ -10317,7 +10332,7 @@ function dev_default(program2) {
|
|
|
10317
10332
|
return;
|
|
10318
10333
|
}
|
|
10319
10334
|
}
|
|
10320
|
-
const { cfg, path: path4 } = await loadModule();
|
|
10335
|
+
const { cfg, path: path4 } = await loadModule("dist", "pepr.ts");
|
|
10321
10336
|
const webhook = new Assets(
|
|
10322
10337
|
{
|
|
10323
10338
|
...cfg.pepr,
|
|
@@ -10327,8 +10342,8 @@ function dev_default(program2) {
|
|
|
10327
10342
|
[],
|
|
10328
10343
|
opts.host
|
|
10329
10344
|
);
|
|
10330
|
-
await
|
|
10331
|
-
await
|
|
10345
|
+
await import_fs11.promises.writeFile("insecure-tls.crt", webhook.tls.pem.crt);
|
|
10346
|
+
await import_fs11.promises.writeFile("insecure-tls.key", webhook.tls.pem.key);
|
|
10332
10347
|
try {
|
|
10333
10348
|
let program3;
|
|
10334
10349
|
const name2 = `pepr-${cfg.pepr.uuid}`;
|
|
@@ -10370,7 +10385,7 @@ ${error}`
|
|
|
10370
10385
|
console.debug(`Received SIGINT, removing webhooks`);
|
|
10371
10386
|
});
|
|
10372
10387
|
};
|
|
10373
|
-
await buildModule(async (r) => {
|
|
10388
|
+
await buildModule("dist", async (r) => {
|
|
10374
10389
|
if (r.errors.length > 0) {
|
|
10375
10390
|
console.error(`Error compiling module: ${r.errors}`);
|
|
10376
10391
|
return;
|
|
@@ -10483,7 +10498,7 @@ var import_child_process5 = require("child_process");
|
|
|
10483
10498
|
var import_path5 = require("path");
|
|
10484
10499
|
|
|
10485
10500
|
// src/cli/init/walkthrough.ts
|
|
10486
|
-
var
|
|
10501
|
+
var import_fs12 = require("fs");
|
|
10487
10502
|
var import_prompts3 = __toESM(require("prompts"));
|
|
10488
10503
|
|
|
10489
10504
|
// src/cli/init/enums.ts
|
|
@@ -10530,7 +10545,7 @@ async function setName(name2) {
|
|
|
10530
10545
|
validate: async (val) => {
|
|
10531
10546
|
try {
|
|
10532
10547
|
const name3 = sanitizeName(val);
|
|
10533
|
-
await
|
|
10548
|
+
await import_fs12.promises.access(name3, import_fs12.promises.constants.F_OK);
|
|
10534
10549
|
return "A directory with this name already exists";
|
|
10535
10550
|
} catch {
|
|
10536
10551
|
return val.length > 2 || "The name must be at least 3 characters long";
|
|
@@ -10714,11 +10729,11 @@ function uuid_default(program2) {
|
|
|
10714
10729
|
program2.command("uuid [uuid]").description("Module UUID(s) currently deployed in the cluster").action(async (uuid) => {
|
|
10715
10730
|
const deployments = await getPeprDeploymentsByUUID(uuid);
|
|
10716
10731
|
const uuidTable = buildUUIDTable(deployments);
|
|
10717
|
-
|
|
10718
|
-
|
|
10719
|
-
|
|
10720
|
-
|
|
10721
|
-
|
|
10732
|
+
const uuidTableEntries = Object.entries(uuidTable).map(([uuid2, description]) => ({
|
|
10733
|
+
UUID: uuid2,
|
|
10734
|
+
Description: description
|
|
10735
|
+
}));
|
|
10736
|
+
console.table(uuidTableEntries);
|
|
10722
10737
|
});
|
|
10723
10738
|
}
|
|
10724
10739
|
async function getPeprDeploymentsByUUID(uuid) {
|
|
@@ -10751,7 +10766,7 @@ var import_commander6 = require("commander");
|
|
|
10751
10766
|
|
|
10752
10767
|
// src/cli/update/index.ts
|
|
10753
10768
|
var import_child_process6 = require("child_process");
|
|
10754
|
-
var
|
|
10769
|
+
var import_fs13 = __toESM(require("fs"));
|
|
10755
10770
|
var import_path6 = require("path");
|
|
10756
10771
|
var import_prompts4 = __toESM(require("prompts"));
|
|
10757
10772
|
function update_default(program2) {
|
|
@@ -10791,12 +10806,12 @@ function update_default(program2) {
|
|
|
10791
10806
|
await write((0, import_path6.resolve)(".vscode", snippet.path), snippet.data);
|
|
10792
10807
|
await write((0, import_path6.resolve)(".vscode", codeSettings.path), codeSettings.data);
|
|
10793
10808
|
const samplePath = (0, import_path6.resolve)("capabilities", samplesYaml.path);
|
|
10794
|
-
if (
|
|
10795
|
-
|
|
10809
|
+
if (import_fs13.default.existsSync(samplePath)) {
|
|
10810
|
+
import_fs13.default.unlinkSync(samplePath);
|
|
10796
10811
|
await write(samplePath, samplesYaml.data);
|
|
10797
10812
|
}
|
|
10798
10813
|
const tsPath = (0, import_path6.resolve)("capabilities", helloPepr.path);
|
|
10799
|
-
if (
|
|
10814
|
+
if (import_fs13.default.existsSync(tsPath)) {
|
|
10800
10815
|
await write(tsPath, helloPepr.data);
|
|
10801
10816
|
}
|
|
10802
10817
|
}
|
|
@@ -10844,7 +10859,7 @@ var import_commander5 = require("commander");
|
|
|
10844
10859
|
var import_commander3 = require("commander");
|
|
10845
10860
|
|
|
10846
10861
|
// src/cli/crd/generate/generators.ts
|
|
10847
|
-
var
|
|
10862
|
+
var import_fs14 = __toESM(require("fs"));
|
|
10848
10863
|
var import_path7 = __toESM(require("path"));
|
|
10849
10864
|
var import_yaml = __toESM(require_dist());
|
|
10850
10865
|
var import_ts_morph = require("ts-morph");
|
|
@@ -10874,27 +10889,27 @@ function extractCRDDetails(content, sourceFile) {
|
|
|
10874
10889
|
}
|
|
10875
10890
|
async function generateCRDs(options) {
|
|
10876
10891
|
logger_default.warn("This feature is currently in alpha.\n");
|
|
10877
|
-
const
|
|
10878
|
-
await createDirectoryIfNotExists(
|
|
10892
|
+
const outputDir = import_path7.default.resolve(options.output);
|
|
10893
|
+
await createDirectoryIfNotExists(outputDir);
|
|
10879
10894
|
const project = new import_ts_morph.Project();
|
|
10880
10895
|
const apiRoot = import_path7.default.resolve("api");
|
|
10881
10896
|
const versions = getAPIVersions(apiRoot);
|
|
10882
10897
|
for (const version3 of versions) {
|
|
10883
10898
|
const sourceFiles = loadVersionFiles(project, import_path7.default.join(apiRoot, version3));
|
|
10884
10899
|
for (const sourceFile of sourceFiles) {
|
|
10885
|
-
processSourceFile(sourceFile, version3,
|
|
10900
|
+
processSourceFile(sourceFile, version3, outputDir);
|
|
10886
10901
|
}
|
|
10887
10902
|
}
|
|
10888
10903
|
}
|
|
10889
10904
|
function getAPIVersions(apiRoot) {
|
|
10890
|
-
return
|
|
10905
|
+
return import_fs14.default.readdirSync(apiRoot).filter((v) => import_fs14.default.statSync(import_path7.default.join(apiRoot, v)).isDirectory());
|
|
10891
10906
|
}
|
|
10892
10907
|
function loadVersionFiles(project, versionDir) {
|
|
10893
|
-
const files =
|
|
10908
|
+
const files = import_fs14.default.readdirSync(versionDir).filter((f) => f.endsWith(".ts"));
|
|
10894
10909
|
const filePaths = files.map((f) => import_path7.default.join(versionDir, f));
|
|
10895
10910
|
return project.addSourceFilesAtPaths(filePaths);
|
|
10896
10911
|
}
|
|
10897
|
-
function processSourceFile(sourceFile, version3,
|
|
10912
|
+
function processSourceFile(sourceFile, version3, outputDir) {
|
|
10898
10913
|
const content = sourceFile.getFullText();
|
|
10899
10914
|
const { kind: kind8, fqdn, scope, plural: plural2, shortNames } = extractCRDDetails(content, sourceFile);
|
|
10900
10915
|
if (!kind8) {
|
|
@@ -10919,8 +10934,8 @@ function processSourceFile(sourceFile, version3, outputDir2) {
|
|
|
10919
10934
|
specSchema,
|
|
10920
10935
|
conditionSchema
|
|
10921
10936
|
});
|
|
10922
|
-
const outPath = import_path7.default.join(
|
|
10923
|
-
|
|
10937
|
+
const outPath = import_path7.default.join(outputDir, `${kind8.toLowerCase()}.yaml`);
|
|
10938
|
+
import_fs14.default.writeFileSync(outPath, (0, import_yaml.stringify)(crd), "utf8");
|
|
10924
10939
|
logger_default.info(`\u2714 Created ${outPath}`);
|
|
10925
10940
|
}
|
|
10926
10941
|
function extractSingleLineComment(content, label) {
|
|
@@ -11084,7 +11099,7 @@ function generate_default() {
|
|
|
11084
11099
|
|
|
11085
11100
|
// src/cli/crd/create/index.ts
|
|
11086
11101
|
var import_commander4 = require("commander");
|
|
11087
|
-
var
|
|
11102
|
+
var import_fs15 = require("fs");
|
|
11088
11103
|
|
|
11089
11104
|
// src/cli/crd/create/createCRDscaffold.ts
|
|
11090
11105
|
var createCRDscaffold = (group2, version3, kind8, data) => {
|
|
@@ -11177,13 +11192,13 @@ function create_default() {
|
|
|
11177
11192
|
).choices(["Namespaced", "Cluster"]).default("Namespaced")
|
|
11178
11193
|
).option("-d, --domain <domain>", "Optional domain for CRD (e.g. pepr.dev)", "pepr.dev").requiredOption("-g, --group <group>", "API group (e.g. cache)").requiredOption("-k, --kind <kind>", "Kind name (e.g. memcached)").option("-p, --plural <plural>", "Plural name for CRD (e.g. memcacheds)").requiredOption("-s, --short-name <name>", "Short name for CRD (e.g. mc)").requiredOption("-v, --version <version>", "API version (e.g. v1alpha1)").action(async ({ group: group2, version: version3, kind: kind8, domain, scope, plural: plural2, shortName }) => {
|
|
11179
11194
|
logger_default.warn("This feature is currently in alpha.");
|
|
11180
|
-
const
|
|
11181
|
-
await createDirectoryIfNotExists(
|
|
11182
|
-
await
|
|
11195
|
+
const outputDir = import_path8.default.resolve(`./api/${version3}`);
|
|
11196
|
+
await createDirectoryIfNotExists(outputDir);
|
|
11197
|
+
await import_fs15.promises.writeFile(
|
|
11183
11198
|
`./api/${version3}/${kind8.toLowerCase()}_types.ts`,
|
|
11184
11199
|
createCRDscaffold(group2, version3, kind8, { domain, scope, plural: plural2, shortName })
|
|
11185
11200
|
);
|
|
11186
|
-
logger_default.info(`\u2714 Created ${kind8} TypeScript definition in ${
|
|
11201
|
+
logger_default.info(`\u2714 Created ${kind8} TypeScript definition in ${outputDir}`);
|
|
11187
11202
|
});
|
|
11188
11203
|
}
|
|
11189
11204
|
|