pepr 0.1.27 → 0.1.28
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/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/package.json +76 -0
- package/dist/src/cli/banner.d.ts +1 -0
- package/dist/{pepr-cli.js → src/cli/banner.js} +1 -1251
- package/dist/src/cli/build.d.ts +7 -0
- package/dist/src/cli/build.js +95 -0
- package/dist/src/cli/capability.d.ts +2 -0
- package/dist/src/cli/capability.js +12 -0
- package/dist/src/cli/deploy.d.ts +2 -0
- package/dist/src/cli/deploy.js +49 -0
- package/dist/src/cli/dev.d.ts +2 -0
- package/dist/src/cli/dev.js +90 -0
- package/dist/src/cli/index.d.ts +1 -0
- package/dist/src/cli/index.js +28 -0
- package/dist/src/cli/init/index.d.ts +2 -0
- package/dist/src/cli/init/index.js +48 -0
- package/dist/src/cli/init/templates.d.ts +82 -0
- package/dist/src/cli/init/templates.js +224 -0
- package/dist/src/cli/init/utils.d.ts +20 -0
- package/dist/src/cli/init/utils.js +50 -0
- package/dist/src/cli/init/walkthrough.d.ts +7 -0
- package/dist/src/cli/init/walkthrough.js +76 -0
- package/dist/src/cli/root.d.ts +4 -0
- package/dist/src/cli/root.js +14 -0
- package/dist/src/cli/test.d.ts +2 -0
- package/dist/src/cli/test.js +45 -0
- package/dist/src/lib/capability.d.ts +26 -0
- package/dist/src/lib/capability.js +112 -0
- package/dist/src/lib/controller.d.ts +13 -0
- package/dist/src/lib/controller.js +77 -0
- package/dist/src/lib/filter.d.ts +10 -0
- package/dist/src/lib/filter.js +41 -0
- package/dist/src/lib/k8s/index.d.ts +4 -0
- package/dist/src/lib/k8s/index.js +7 -0
- package/dist/src/lib/k8s/kinds.d.ts +3 -0
- package/dist/src/lib/k8s/kinds.js +427 -0
- package/dist/src/lib/k8s/tls.d.ts +17 -0
- package/dist/src/lib/k8s/tls.js +67 -0
- package/dist/src/lib/k8s/types.d.ts +136 -0
- package/dist/src/lib/k8s/types.js +9 -0
- package/dist/src/lib/k8s/upstream.d.ts +1 -0
- package/dist/src/lib/k8s/upstream.js +3 -0
- package/dist/src/lib/k8s/webhook.d.ts +33 -0
- package/dist/src/lib/k8s/webhook.js +490 -0
- package/dist/src/lib/logger.d.ts +54 -0
- package/dist/{types-1709b44f.js → src/lib/logger.js} +3 -40
- package/dist/src/lib/module.d.ts +22 -0
- package/dist/src/lib/module.js +32 -0
- package/dist/src/lib/processor.d.ts +4 -0
- package/dist/src/lib/processor.js +66 -0
- package/dist/src/lib/request.d.ts +77 -0
- package/dist/src/lib/request.js +117 -0
- package/dist/src/lib/types.d.ts +187 -0
- package/dist/src/lib/types.js +31 -0
- package/package.json +8 -11
- package/tsconfig.build.json +4 -0
- package/dist/pepr-core.js +0 -949
- package/tsconfig.json +0 -17
|
@@ -1,28 +1,6 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
'use strict';
|
|
3
|
-
|
|
4
|
-
var json = require('@rollup/plugin-json');
|
|
5
|
-
var nodeResolve = require('@rollup/plugin-node-resolve');
|
|
6
|
-
var typescript = require('@rollup/plugin-typescript');
|
|
7
|
-
var fs = require('fs');
|
|
8
|
-
var path = require('path');
|
|
9
|
-
var rollup = require('rollup');
|
|
10
|
-
var types = require('./types-1709b44f.js');
|
|
11
|
-
var clientNode = require('@kubernetes/client-node');
|
|
12
|
-
var zlib = require('zlib');
|
|
13
|
-
var forge = require('node-forge');
|
|
14
|
-
var prompt = require('prompts');
|
|
15
|
-
var child_process = require('child_process');
|
|
16
|
-
var chokidar = require('chokidar');
|
|
17
|
-
var util = require('util');
|
|
18
|
-
var uuid = require('uuid');
|
|
19
|
-
var commander = require('commander');
|
|
20
|
-
|
|
21
|
-
var version = "0.1.27";
|
|
22
|
-
|
|
23
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
24
2
|
// SPDX-FileCopyrightText: 2023-Present The Pepr Authors
|
|
25
|
-
const banner = `[107;40m[38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m
|
|
3
|
+
export const banner = `[107;40m[38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m
|
|
26
4
|
[38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m
|
|
27
5
|
[38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m
|
|
28
6
|
[38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m
|
|
@@ -87,1231 +65,3 @@ const banner = `[107;40m[38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;
|
|
|
87
65
|
[38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m
|
|
88
66
|
[38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m [38;5;016m
|
|
89
67
|
[0m`;
|
|
90
|
-
|
|
91
|
-
const caName = "Pepr Ephemeral CA";
|
|
92
|
-
/**
|
|
93
|
-
* Generates a self-signed CA and server certificate with Subject Alternative Names (SANs) for the K8s webhook.
|
|
94
|
-
*
|
|
95
|
-
* @param {string} name - The name to use for the server certificate's Common Name and SAN DNS entry.
|
|
96
|
-
* @returns {TLSOut} - An object containing the Base64-encoded CA, server certificate, and server private key.
|
|
97
|
-
*/
|
|
98
|
-
function genTLS(name) {
|
|
99
|
-
// Generate a new CA key pair and create a self-signed CA certificate
|
|
100
|
-
const caKeys = forge.pki.rsa.generateKeyPair(2048);
|
|
101
|
-
const caCert = genCert(caKeys, caName, [{ name: "commonName", value: caName }]);
|
|
102
|
-
caCert.setExtensions([
|
|
103
|
-
{
|
|
104
|
-
name: "basicConstraints",
|
|
105
|
-
cA: true,
|
|
106
|
-
},
|
|
107
|
-
{
|
|
108
|
-
name: "keyUsage",
|
|
109
|
-
keyCertSign: true,
|
|
110
|
-
digitalSignature: true,
|
|
111
|
-
nonRepudiation: true,
|
|
112
|
-
keyEncipherment: true,
|
|
113
|
-
dataEncipherment: true,
|
|
114
|
-
},
|
|
115
|
-
]);
|
|
116
|
-
// Generate a new server key pair and create a server certificate signed by the CA
|
|
117
|
-
const serverKeys = forge.pki.rsa.generateKeyPair(2048);
|
|
118
|
-
const serverCert = genCert(serverKeys, name, caCert.subject.attributes);
|
|
119
|
-
// Sign both certificates with the CA private key
|
|
120
|
-
caCert.sign(caKeys.privateKey, forge.md.sha256.create());
|
|
121
|
-
serverCert.sign(caKeys.privateKey, forge.md.sha256.create());
|
|
122
|
-
// Convert the keys and certificates to PEM format
|
|
123
|
-
const pem = {
|
|
124
|
-
ca: forge.pki.certificateToPem(caCert),
|
|
125
|
-
crt: forge.pki.certificateToPem(serverCert),
|
|
126
|
-
key: forge.pki.privateKeyToPem(serverKeys.privateKey),
|
|
127
|
-
};
|
|
128
|
-
// Base64-encode the PEM strings
|
|
129
|
-
const ca = Buffer.from(pem.ca).toString("base64");
|
|
130
|
-
const key = Buffer.from(pem.key).toString("base64");
|
|
131
|
-
const crt = Buffer.from(pem.crt).toString("base64");
|
|
132
|
-
return { ca, key, crt, pem };
|
|
133
|
-
}
|
|
134
|
-
function genCert(key, name, issuer) {
|
|
135
|
-
const crt = forge.pki.createCertificate();
|
|
136
|
-
crt.publicKey = key.publicKey;
|
|
137
|
-
crt.serialNumber = "01";
|
|
138
|
-
crt.validity.notBefore = new Date();
|
|
139
|
-
crt.validity.notAfter = new Date();
|
|
140
|
-
crt.validity.notAfter.setFullYear(crt.validity.notBefore.getFullYear() + 1);
|
|
141
|
-
// Add SANs to the server certificate
|
|
142
|
-
crt.setExtensions([
|
|
143
|
-
{
|
|
144
|
-
name: "subjectAltName",
|
|
145
|
-
altNames: [
|
|
146
|
-
{
|
|
147
|
-
type: 2,
|
|
148
|
-
value: name,
|
|
149
|
-
},
|
|
150
|
-
],
|
|
151
|
-
},
|
|
152
|
-
]);
|
|
153
|
-
// Set the server certificate's issuer to the CA
|
|
154
|
-
crt.setIssuer(issuer);
|
|
155
|
-
return crt;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
159
|
-
const peprIgnore = {
|
|
160
|
-
key: "pepr.dev",
|
|
161
|
-
operator: "NotIn",
|
|
162
|
-
values: ["ignore"],
|
|
163
|
-
};
|
|
164
|
-
class Webhook {
|
|
165
|
-
get tls() {
|
|
166
|
-
return this._tls;
|
|
167
|
-
}
|
|
168
|
-
constructor(config, host) {
|
|
169
|
-
this.config = config;
|
|
170
|
-
this.host = host;
|
|
171
|
-
this.name = `pepr-${config.uuid}`;
|
|
172
|
-
this.image = `ghcr.io/defenseunicorns/pepr/controller:${config.version}`;
|
|
173
|
-
// Generate the ephemeral tls things
|
|
174
|
-
this._tls = genTLS(this.host || `${this.name}.pepr-system.svc`);
|
|
175
|
-
}
|
|
176
|
-
/** Generate the pepr-system namespace */
|
|
177
|
-
namespace() {
|
|
178
|
-
return {
|
|
179
|
-
apiVersion: "v1",
|
|
180
|
-
kind: "Namespace",
|
|
181
|
-
metadata: { name: "pepr-system" },
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
/**
|
|
185
|
-
* Grants the controller access to cluster resources beyond the mutating webhook.
|
|
186
|
-
*
|
|
187
|
-
* @todo: should dynamically generate this based on resources used by the module. will also need to explore how this should work for multiple modules.
|
|
188
|
-
* @returns
|
|
189
|
-
*/
|
|
190
|
-
clusterRole() {
|
|
191
|
-
return {
|
|
192
|
-
apiVersion: "rbac.authorization.k8s.io/v1",
|
|
193
|
-
kind: "ClusterRole",
|
|
194
|
-
metadata: { name: this.name },
|
|
195
|
-
rules: [
|
|
196
|
-
{
|
|
197
|
-
// @todo: make this configurable
|
|
198
|
-
apiGroups: ["*"],
|
|
199
|
-
resources: ["*"],
|
|
200
|
-
verbs: ["create", "delete", "get", "list", "patch", "update", "watch"],
|
|
201
|
-
},
|
|
202
|
-
],
|
|
203
|
-
};
|
|
204
|
-
}
|
|
205
|
-
clusterRoleBinding() {
|
|
206
|
-
const name = this.name;
|
|
207
|
-
return {
|
|
208
|
-
apiVersion: "rbac.authorization.k8s.io/v1",
|
|
209
|
-
kind: "ClusterRoleBinding",
|
|
210
|
-
metadata: { name },
|
|
211
|
-
roleRef: {
|
|
212
|
-
apiGroup: "rbac.authorization.k8s.io",
|
|
213
|
-
kind: "ClusterRole",
|
|
214
|
-
name,
|
|
215
|
-
},
|
|
216
|
-
subjects: [
|
|
217
|
-
{
|
|
218
|
-
kind: "ServiceAccount",
|
|
219
|
-
name,
|
|
220
|
-
namespace: "pepr-system",
|
|
221
|
-
},
|
|
222
|
-
],
|
|
223
|
-
};
|
|
224
|
-
}
|
|
225
|
-
serviceAccount() {
|
|
226
|
-
return {
|
|
227
|
-
apiVersion: "v1",
|
|
228
|
-
kind: "ServiceAccount",
|
|
229
|
-
metadata: {
|
|
230
|
-
name: this.name,
|
|
231
|
-
namespace: "pepr-system",
|
|
232
|
-
},
|
|
233
|
-
};
|
|
234
|
-
}
|
|
235
|
-
tlsSecret() {
|
|
236
|
-
return {
|
|
237
|
-
apiVersion: "v1",
|
|
238
|
-
kind: "Secret",
|
|
239
|
-
metadata: {
|
|
240
|
-
name: `${this.name}-tls`,
|
|
241
|
-
namespace: "pepr-system",
|
|
242
|
-
},
|
|
243
|
-
type: "kubernetes.io/tls",
|
|
244
|
-
data: {
|
|
245
|
-
"tls.crt": this._tls.crt,
|
|
246
|
-
"tls.key": this._tls.key,
|
|
247
|
-
},
|
|
248
|
-
};
|
|
249
|
-
}
|
|
250
|
-
mutatingWebhook() {
|
|
251
|
-
const { name } = this;
|
|
252
|
-
const ignore = [peprIgnore];
|
|
253
|
-
// Add any namespaces to ignore
|
|
254
|
-
if (this.config.alwaysIgnore.namespaces.length > 0) {
|
|
255
|
-
ignore.push({
|
|
256
|
-
key: "kubernetes.io/metadata.name",
|
|
257
|
-
operator: "NotIn",
|
|
258
|
-
values: this.config.alwaysIgnore.namespaces,
|
|
259
|
-
});
|
|
260
|
-
}
|
|
261
|
-
const clientConfig = {
|
|
262
|
-
caBundle: this._tls.ca,
|
|
263
|
-
};
|
|
264
|
-
// If a host is specified, use that with a port of 3000
|
|
265
|
-
if (this.host) {
|
|
266
|
-
clientConfig.url = `https://${this.host}:3000/mutate`;
|
|
267
|
-
}
|
|
268
|
-
else {
|
|
269
|
-
// Otherwise, use the service
|
|
270
|
-
clientConfig.service = {
|
|
271
|
-
name: this.name,
|
|
272
|
-
namespace: "pepr-system",
|
|
273
|
-
path: "/mutate",
|
|
274
|
-
};
|
|
275
|
-
}
|
|
276
|
-
return {
|
|
277
|
-
apiVersion: "admissionregistration.k8s.io/v1",
|
|
278
|
-
kind: "MutatingWebhookConfiguration",
|
|
279
|
-
metadata: { name },
|
|
280
|
-
webhooks: [
|
|
281
|
-
{
|
|
282
|
-
name: `${name}.pepr.dev`,
|
|
283
|
-
admissionReviewVersions: ["v1", "v1beta1"],
|
|
284
|
-
clientConfig,
|
|
285
|
-
failurePolicy: "Ignore",
|
|
286
|
-
matchPolicy: "Equivalent",
|
|
287
|
-
timeoutSeconds: 15,
|
|
288
|
-
namespaceSelector: {
|
|
289
|
-
matchExpressions: ignore,
|
|
290
|
-
},
|
|
291
|
-
objectSelector: {
|
|
292
|
-
matchExpressions: ignore,
|
|
293
|
-
},
|
|
294
|
-
// @todo: make this configurable
|
|
295
|
-
rules: [
|
|
296
|
-
{
|
|
297
|
-
apiGroups: ["*"],
|
|
298
|
-
apiVersions: ["*"],
|
|
299
|
-
operations: ["CREATE", "UPDATE", "DELETE"],
|
|
300
|
-
resources: ["*/*"],
|
|
301
|
-
},
|
|
302
|
-
],
|
|
303
|
-
// @todo: track side effects state
|
|
304
|
-
sideEffects: "None",
|
|
305
|
-
},
|
|
306
|
-
],
|
|
307
|
-
};
|
|
308
|
-
}
|
|
309
|
-
deployment() {
|
|
310
|
-
return {
|
|
311
|
-
apiVersion: "apps/v1",
|
|
312
|
-
kind: "Deployment",
|
|
313
|
-
metadata: {
|
|
314
|
-
name: this.name,
|
|
315
|
-
namespace: "pepr-system",
|
|
316
|
-
labels: {
|
|
317
|
-
app: this.name,
|
|
318
|
-
},
|
|
319
|
-
},
|
|
320
|
-
spec: {
|
|
321
|
-
replicas: 2,
|
|
322
|
-
selector: {
|
|
323
|
-
matchLabels: {
|
|
324
|
-
app: this.name,
|
|
325
|
-
},
|
|
326
|
-
},
|
|
327
|
-
template: {
|
|
328
|
-
metadata: {
|
|
329
|
-
labels: {
|
|
330
|
-
app: this.name,
|
|
331
|
-
},
|
|
332
|
-
},
|
|
333
|
-
spec: {
|
|
334
|
-
priorityClassName: "system-node-critical",
|
|
335
|
-
serviceAccountName: this.name,
|
|
336
|
-
containers: [
|
|
337
|
-
{
|
|
338
|
-
name: "server",
|
|
339
|
-
image: this.image,
|
|
340
|
-
imagePullPolicy: "IfNotPresent",
|
|
341
|
-
livenessProbe: {
|
|
342
|
-
httpGet: {
|
|
343
|
-
path: "/healthz",
|
|
344
|
-
port: 3000,
|
|
345
|
-
scheme: "HTTPS",
|
|
346
|
-
},
|
|
347
|
-
},
|
|
348
|
-
ports: [
|
|
349
|
-
{
|
|
350
|
-
containerPort: 3000,
|
|
351
|
-
},
|
|
352
|
-
],
|
|
353
|
-
resources: {
|
|
354
|
-
requests: {
|
|
355
|
-
memory: "64Mi",
|
|
356
|
-
cpu: "100m",
|
|
357
|
-
},
|
|
358
|
-
limits: {
|
|
359
|
-
memory: "256Mi",
|
|
360
|
-
cpu: "500m",
|
|
361
|
-
},
|
|
362
|
-
},
|
|
363
|
-
volumeMounts: [
|
|
364
|
-
{
|
|
365
|
-
name: "tls-certs",
|
|
366
|
-
mountPath: "/etc/certs",
|
|
367
|
-
readOnly: true,
|
|
368
|
-
},
|
|
369
|
-
{
|
|
370
|
-
name: "module",
|
|
371
|
-
mountPath: "/app/module.js.gz",
|
|
372
|
-
readOnly: true,
|
|
373
|
-
},
|
|
374
|
-
],
|
|
375
|
-
},
|
|
376
|
-
],
|
|
377
|
-
volumes: [
|
|
378
|
-
{
|
|
379
|
-
name: "tls-certs",
|
|
380
|
-
secret: {
|
|
381
|
-
secretName: `${this.name}-tls`,
|
|
382
|
-
},
|
|
383
|
-
},
|
|
384
|
-
{
|
|
385
|
-
name: "module",
|
|
386
|
-
secret: {
|
|
387
|
-
secretName: `${this.name}-module`,
|
|
388
|
-
},
|
|
389
|
-
},
|
|
390
|
-
],
|
|
391
|
-
},
|
|
392
|
-
},
|
|
393
|
-
},
|
|
394
|
-
};
|
|
395
|
-
}
|
|
396
|
-
/** Only permit the */
|
|
397
|
-
networkPolicy() {
|
|
398
|
-
return {
|
|
399
|
-
apiVersion: "networking.k8s.io/v1",
|
|
400
|
-
kind: "NetworkPolicy",
|
|
401
|
-
metadata: {
|
|
402
|
-
name: this.name,
|
|
403
|
-
namespace: "pepr-system",
|
|
404
|
-
},
|
|
405
|
-
spec: {
|
|
406
|
-
podSelector: {
|
|
407
|
-
matchLabels: {
|
|
408
|
-
app: this.name,
|
|
409
|
-
},
|
|
410
|
-
},
|
|
411
|
-
policyTypes: ["Ingress"],
|
|
412
|
-
ingress: [
|
|
413
|
-
{
|
|
414
|
-
from: [
|
|
415
|
-
{
|
|
416
|
-
namespaceSelector: {
|
|
417
|
-
matchLabels: {
|
|
418
|
-
"kubernetes.io/metadata.name": "kube-system",
|
|
419
|
-
},
|
|
420
|
-
},
|
|
421
|
-
},
|
|
422
|
-
],
|
|
423
|
-
ports: [
|
|
424
|
-
{
|
|
425
|
-
protocol: "TCP",
|
|
426
|
-
port: 443,
|
|
427
|
-
},
|
|
428
|
-
],
|
|
429
|
-
},
|
|
430
|
-
],
|
|
431
|
-
},
|
|
432
|
-
};
|
|
433
|
-
}
|
|
434
|
-
service() {
|
|
435
|
-
return {
|
|
436
|
-
apiVersion: "v1",
|
|
437
|
-
kind: "Service",
|
|
438
|
-
metadata: {
|
|
439
|
-
name: this.name,
|
|
440
|
-
namespace: "pepr-system",
|
|
441
|
-
},
|
|
442
|
-
spec: {
|
|
443
|
-
selector: {
|
|
444
|
-
app: this.name,
|
|
445
|
-
},
|
|
446
|
-
ports: [
|
|
447
|
-
{
|
|
448
|
-
port: 443,
|
|
449
|
-
targetPort: 3000,
|
|
450
|
-
},
|
|
451
|
-
],
|
|
452
|
-
},
|
|
453
|
-
};
|
|
454
|
-
}
|
|
455
|
-
moduleSecret(data) {
|
|
456
|
-
// Compress the data
|
|
457
|
-
const compressed = zlib.gzipSync(data);
|
|
458
|
-
return {
|
|
459
|
-
apiVersion: "v1",
|
|
460
|
-
kind: "Secret",
|
|
461
|
-
metadata: {
|
|
462
|
-
name: `${this.name}-module`,
|
|
463
|
-
namespace: "pepr-system",
|
|
464
|
-
},
|
|
465
|
-
type: "Opaque",
|
|
466
|
-
data: {
|
|
467
|
-
module: compressed.toString("base64"),
|
|
468
|
-
},
|
|
469
|
-
};
|
|
470
|
-
}
|
|
471
|
-
zarfYaml(path) {
|
|
472
|
-
const zarfCfg = {
|
|
473
|
-
kind: "ZarfPackageConfig",
|
|
474
|
-
metadata: {
|
|
475
|
-
name: this.name,
|
|
476
|
-
description: `Pepr Module: ${this.config.description}`,
|
|
477
|
-
url: "https://github.com/defenseunicorns/pepr",
|
|
478
|
-
version: this.config.version,
|
|
479
|
-
},
|
|
480
|
-
components: [
|
|
481
|
-
{
|
|
482
|
-
name: "module",
|
|
483
|
-
required: true,
|
|
484
|
-
manifests: [
|
|
485
|
-
{
|
|
486
|
-
name: "module",
|
|
487
|
-
namespace: "pepr-system",
|
|
488
|
-
files: [path],
|
|
489
|
-
},
|
|
490
|
-
],
|
|
491
|
-
images: [this.image],
|
|
492
|
-
},
|
|
493
|
-
],
|
|
494
|
-
};
|
|
495
|
-
return clientNode.dumpYaml(zarfCfg, { noRefs: true });
|
|
496
|
-
}
|
|
497
|
-
allYaml(code) {
|
|
498
|
-
const resources = [
|
|
499
|
-
this.namespace(),
|
|
500
|
-
this.networkPolicy(),
|
|
501
|
-
this.clusterRole(),
|
|
502
|
-
this.clusterRoleBinding(),
|
|
503
|
-
this.serviceAccount(),
|
|
504
|
-
this.tlsSecret(),
|
|
505
|
-
this.mutatingWebhook(),
|
|
506
|
-
this.deployment(),
|
|
507
|
-
this.service(),
|
|
508
|
-
this.moduleSecret(code),
|
|
509
|
-
];
|
|
510
|
-
// Convert the resources to a single YAML string
|
|
511
|
-
return resources.map(r => clientNode.dumpYaml(r, { noRefs: true })).join("---\n");
|
|
512
|
-
}
|
|
513
|
-
async deploy(code) {
|
|
514
|
-
types.logger.info("Establishing connection to Kubernetes");
|
|
515
|
-
const namespace = "pepr-system";
|
|
516
|
-
// Deploy the resources using the k8s API
|
|
517
|
-
const kubeConfig = new clientNode.KubeConfig();
|
|
518
|
-
kubeConfig.loadFromDefault();
|
|
519
|
-
const coreV1Api = kubeConfig.makeApiClient(clientNode.CoreV1Api);
|
|
520
|
-
const rbacApi = kubeConfig.makeApiClient(clientNode.RbacAuthorizationV1Api);
|
|
521
|
-
const appsApi = kubeConfig.makeApiClient(clientNode.AppsV1Api);
|
|
522
|
-
const admissionApi = kubeConfig.makeApiClient(clientNode.AdmissionregistrationV1Api);
|
|
523
|
-
const networkApi = kubeConfig.makeApiClient(clientNode.NetworkingV1Api);
|
|
524
|
-
const ns = this.namespace();
|
|
525
|
-
try {
|
|
526
|
-
types.logger.info("Checking for namespace");
|
|
527
|
-
await coreV1Api.readNamespace(namespace);
|
|
528
|
-
}
|
|
529
|
-
catch (e) {
|
|
530
|
-
types.logger.debug(e.body);
|
|
531
|
-
types.logger.info("Creating namespace");
|
|
532
|
-
await coreV1Api.createNamespace(ns);
|
|
533
|
-
}
|
|
534
|
-
const netpol = this.networkPolicy();
|
|
535
|
-
try {
|
|
536
|
-
types.logger.info("Checking for network policy");
|
|
537
|
-
await networkApi.readNamespacedNetworkPolicy(netpol.metadata.name, namespace);
|
|
538
|
-
}
|
|
539
|
-
catch (e) {
|
|
540
|
-
types.logger.debug(e.body);
|
|
541
|
-
types.logger.info("Creating network policy");
|
|
542
|
-
await networkApi.createNamespacedNetworkPolicy(namespace, netpol);
|
|
543
|
-
}
|
|
544
|
-
const wh = this.mutatingWebhook();
|
|
545
|
-
try {
|
|
546
|
-
types.logger.info("Creating mutating webhook");
|
|
547
|
-
await admissionApi.createMutatingWebhookConfiguration(wh);
|
|
548
|
-
}
|
|
549
|
-
catch (e) {
|
|
550
|
-
types.logger.debug(e.body);
|
|
551
|
-
types.logger.info("Removing and re-creating mutating webhook");
|
|
552
|
-
await admissionApi.deleteMutatingWebhookConfiguration(wh.metadata.name);
|
|
553
|
-
await admissionApi.createMutatingWebhookConfiguration(wh);
|
|
554
|
-
}
|
|
555
|
-
const crb = this.clusterRoleBinding();
|
|
556
|
-
try {
|
|
557
|
-
types.logger.info("Creating cluster role binding");
|
|
558
|
-
await rbacApi.createClusterRoleBinding(crb);
|
|
559
|
-
}
|
|
560
|
-
catch (e) {
|
|
561
|
-
types.logger.debug(e.body);
|
|
562
|
-
types.logger.info("Removing and re-creating cluster role binding");
|
|
563
|
-
await rbacApi.deleteClusterRoleBinding(crb.metadata.name);
|
|
564
|
-
await rbacApi.createClusterRoleBinding(crb);
|
|
565
|
-
}
|
|
566
|
-
const cr = this.clusterRole();
|
|
567
|
-
try {
|
|
568
|
-
types.logger.info("Creating cluster role");
|
|
569
|
-
await rbacApi.createClusterRole(cr);
|
|
570
|
-
}
|
|
571
|
-
catch (e) {
|
|
572
|
-
types.logger.debug(e.body);
|
|
573
|
-
types.logger.info("Removing and re-creating the cluster role");
|
|
574
|
-
try {
|
|
575
|
-
await rbacApi.deleteClusterRole(cr.metadata.name);
|
|
576
|
-
await rbacApi.createClusterRole(cr);
|
|
577
|
-
}
|
|
578
|
-
catch (e) {
|
|
579
|
-
types.logger.debug(e.body);
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
const sa = this.serviceAccount();
|
|
583
|
-
try {
|
|
584
|
-
types.logger.info("Creating service account");
|
|
585
|
-
await coreV1Api.createNamespacedServiceAccount(namespace, sa);
|
|
586
|
-
}
|
|
587
|
-
catch (e) {
|
|
588
|
-
types.logger.debug(e.body);
|
|
589
|
-
types.logger.info("Removing and re-creating service account");
|
|
590
|
-
await coreV1Api.deleteNamespacedServiceAccount(sa.metadata.name, namespace);
|
|
591
|
-
await coreV1Api.createNamespacedServiceAccount(namespace, sa);
|
|
592
|
-
}
|
|
593
|
-
// If a host is specified, we don't need to deploy the rest of the resources
|
|
594
|
-
if (this.host) {
|
|
595
|
-
return;
|
|
596
|
-
}
|
|
597
|
-
const mod = this.moduleSecret(code);
|
|
598
|
-
try {
|
|
599
|
-
types.logger.info("Creating module secret");
|
|
600
|
-
await coreV1Api.createNamespacedSecret(namespace, mod);
|
|
601
|
-
}
|
|
602
|
-
catch (e) {
|
|
603
|
-
types.logger.debug(e.body);
|
|
604
|
-
types.logger.info("Removing and re-creating module secret");
|
|
605
|
-
await coreV1Api.deleteNamespacedSecret(mod.metadata.name, namespace);
|
|
606
|
-
await coreV1Api.createNamespacedSecret(namespace, mod);
|
|
607
|
-
}
|
|
608
|
-
const svc = this.service();
|
|
609
|
-
try {
|
|
610
|
-
types.logger.info("Creating service");
|
|
611
|
-
await coreV1Api.createNamespacedService(namespace, svc);
|
|
612
|
-
}
|
|
613
|
-
catch (e) {
|
|
614
|
-
types.logger.debug(e.body);
|
|
615
|
-
types.logger.info("Removing and re-creating service");
|
|
616
|
-
await coreV1Api.deleteNamespacedService(svc.metadata.name, namespace);
|
|
617
|
-
await coreV1Api.createNamespacedService(namespace, svc);
|
|
618
|
-
}
|
|
619
|
-
const tls = this.tlsSecret();
|
|
620
|
-
try {
|
|
621
|
-
types.logger.info("Creating TLS secret");
|
|
622
|
-
await coreV1Api.createNamespacedSecret(namespace, tls);
|
|
623
|
-
}
|
|
624
|
-
catch (e) {
|
|
625
|
-
types.logger.debug(e.body);
|
|
626
|
-
types.logger.info("Removing and re-creating TLS secret");
|
|
627
|
-
await coreV1Api.deleteNamespacedSecret(tls.metadata.name, namespace);
|
|
628
|
-
await coreV1Api.createNamespacedSecret(namespace, tls);
|
|
629
|
-
}
|
|
630
|
-
const dep = this.deployment();
|
|
631
|
-
try {
|
|
632
|
-
types.logger.info("Creating deployment");
|
|
633
|
-
await appsApi.createNamespacedDeployment(namespace, dep);
|
|
634
|
-
}
|
|
635
|
-
catch (e) {
|
|
636
|
-
types.logger.debug(e.body);
|
|
637
|
-
types.logger.info("Removing and re-creating deployment");
|
|
638
|
-
await appsApi.deleteNamespacedDeployment(dep.metadata.name, namespace);
|
|
639
|
-
await appsApi.createNamespacedDeployment(namespace, dep);
|
|
640
|
-
}
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
645
|
-
function build (program) {
|
|
646
|
-
program
|
|
647
|
-
.command("build")
|
|
648
|
-
.description("Build a Pepr Module for deployment")
|
|
649
|
-
.option("-d, --dir [directory]", "Pepr module directory", ".")
|
|
650
|
-
.action(async (opts) => {
|
|
651
|
-
// Build the module
|
|
652
|
-
const { cfg, path: path$1, uuid } = await buildModule(opts.dir);
|
|
653
|
-
// Read the compiled module code
|
|
654
|
-
const code = await fs.promises.readFile(path$1, { encoding: "utf-8" });
|
|
655
|
-
// Generate a secret for the module
|
|
656
|
-
const webhook = new Webhook({
|
|
657
|
-
...cfg.pepr,
|
|
658
|
-
description: cfg.description,
|
|
659
|
-
});
|
|
660
|
-
const yamlFile = `pepr-module-${uuid}.yaml`;
|
|
661
|
-
const yamlPath = path.resolve("dist", yamlFile);
|
|
662
|
-
const yaml = webhook.allYaml(code);
|
|
663
|
-
const zarfPath = path.resolve("dist", "zarf.yaml");
|
|
664
|
-
const zarf = webhook.zarfYaml(yamlFile);
|
|
665
|
-
await fs.promises.writeFile(yamlPath, yaml);
|
|
666
|
-
await fs.promises.writeFile(zarfPath, zarf);
|
|
667
|
-
types.logger.debug(`Module compiled successfully at ${path$1}`);
|
|
668
|
-
types.logger.info(`K8s resource for the module saved to ${yamlPath}`);
|
|
669
|
-
});
|
|
670
|
-
}
|
|
671
|
-
const externalLibs = [
|
|
672
|
-
/@kubernetes\/client-node(\/.*)?/,
|
|
673
|
-
"commander",
|
|
674
|
-
"express",
|
|
675
|
-
"fast-json-patch",
|
|
676
|
-
"pepr",
|
|
677
|
-
"ramda",
|
|
678
|
-
];
|
|
679
|
-
async function buildModule(moduleDir) {
|
|
680
|
-
try {
|
|
681
|
-
// Resolve the path to the module's package.json file
|
|
682
|
-
const cfgPath = path.resolve(moduleDir, "package.json");
|
|
683
|
-
const input = path.resolve(moduleDir, "pepr.ts");
|
|
684
|
-
// Read the module's UUID from the package.json filel
|
|
685
|
-
const moduleText = await fs.promises.readFile(cfgPath, { encoding: "utf-8" });
|
|
686
|
-
const cfg = JSON.parse(moduleText);
|
|
687
|
-
const { uuid } = cfg.pepr;
|
|
688
|
-
const name = `pepr-${uuid}.js`;
|
|
689
|
-
// Exit if the module's UUID could not be found
|
|
690
|
-
if (!uuid) {
|
|
691
|
-
throw new Error("Could not load the uuid in package.json");
|
|
692
|
-
}
|
|
693
|
-
const plugins = [
|
|
694
|
-
nodeResolve({
|
|
695
|
-
preferBuiltins: true,
|
|
696
|
-
}),
|
|
697
|
-
json(),
|
|
698
|
-
typescript({
|
|
699
|
-
tsconfig: "./tsconfig.json",
|
|
700
|
-
declaration: false,
|
|
701
|
-
removeComments: true,
|
|
702
|
-
sourceMap: false,
|
|
703
|
-
include: ["**/*.ts", "node_modules/pepr/**/*.ts"],
|
|
704
|
-
}),
|
|
705
|
-
];
|
|
706
|
-
// Build the module using Rollup
|
|
707
|
-
const bundle = await rollup.rollup({
|
|
708
|
-
plugins,
|
|
709
|
-
external: externalLibs,
|
|
710
|
-
treeshake: true,
|
|
711
|
-
input,
|
|
712
|
-
});
|
|
713
|
-
// Write the module to the dist directory
|
|
714
|
-
await bundle.write({
|
|
715
|
-
dir: "dist",
|
|
716
|
-
format: "cjs",
|
|
717
|
-
entryFileNames: name,
|
|
718
|
-
});
|
|
719
|
-
return {
|
|
720
|
-
path: path.resolve("dist", name),
|
|
721
|
-
cfg,
|
|
722
|
-
uuid,
|
|
723
|
-
};
|
|
724
|
-
}
|
|
725
|
-
catch (e) {
|
|
726
|
-
// On any other error, exit with a non-zero exit code
|
|
727
|
-
types.logger.debug(e);
|
|
728
|
-
types.logger.error(e.message);
|
|
729
|
-
process.exit(1);
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
734
|
-
// SPDX-FileCopyrightText: 2023-Present The Pepr Authors
|
|
735
|
-
function capability (program) {
|
|
736
|
-
program
|
|
737
|
-
.command("new")
|
|
738
|
-
.description("Create a new Pepr Capability")
|
|
739
|
-
.option("-d, --dir [directory]", "Pepr module directory", ".")
|
|
740
|
-
.action(() => {
|
|
741
|
-
// TODO: Create a new capability
|
|
742
|
-
console.log("new");
|
|
743
|
-
});
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
747
|
-
function deploy (program) {
|
|
748
|
-
program
|
|
749
|
-
.command("deploy")
|
|
750
|
-
.description("Deploy a Pepr Module")
|
|
751
|
-
.option("-d, --dir [directory]", "Pepr module directory", ".")
|
|
752
|
-
.option("-i, --image [image]", "Override the image tag")
|
|
753
|
-
.option("-f, --force", "Force redeployment")
|
|
754
|
-
.action(async (opts) => {
|
|
755
|
-
if (!opts.force) {
|
|
756
|
-
// Prompt the user to confirm
|
|
757
|
-
const confirm = await prompt.prompt({
|
|
758
|
-
type: "confirm",
|
|
759
|
-
name: "confirm",
|
|
760
|
-
message: "This will remove and redeploy the module. Continue?",
|
|
761
|
-
});
|
|
762
|
-
// Exit if the user doesn't confirm
|
|
763
|
-
if (!confirm.confirm) {
|
|
764
|
-
process.exit(0);
|
|
765
|
-
}
|
|
766
|
-
}
|
|
767
|
-
// Build the module
|
|
768
|
-
const { cfg, path } = await buildModule(opts.dir);
|
|
769
|
-
// Read the compiled module code
|
|
770
|
-
const code = await fs.promises.readFile(path, { encoding: "utf-8" });
|
|
771
|
-
// Generate a secret for the module
|
|
772
|
-
const webhook = new Webhook({
|
|
773
|
-
...cfg.pepr,
|
|
774
|
-
description: cfg.description,
|
|
775
|
-
});
|
|
776
|
-
if (opts.image) {
|
|
777
|
-
webhook.image = opts.image;
|
|
778
|
-
}
|
|
779
|
-
try {
|
|
780
|
-
await webhook.deploy(code);
|
|
781
|
-
types.logger.info(`Module deployed successfully`);
|
|
782
|
-
}
|
|
783
|
-
catch (e) {
|
|
784
|
-
types.logger.error(`Error deploying module: ${e}`);
|
|
785
|
-
process.exit(1);
|
|
786
|
-
}
|
|
787
|
-
});
|
|
788
|
-
}
|
|
789
|
-
|
|
790
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
791
|
-
function dev (program) {
|
|
792
|
-
program
|
|
793
|
-
.command("dev")
|
|
794
|
-
.description("Setup a local webhook development environment")
|
|
795
|
-
.option("-d, --dir [directory]", "Pepr module directory", ".")
|
|
796
|
-
.option("-h, --host [host]", "Host to listen on", "host.docker.internal")
|
|
797
|
-
.action(async (opts) => {
|
|
798
|
-
// Prompt the user to confirm
|
|
799
|
-
const confirm = await prompt.prompt({
|
|
800
|
-
type: "confirm",
|
|
801
|
-
name: "confirm",
|
|
802
|
-
message: "This will remove and redeploy the module. Continue?",
|
|
803
|
-
});
|
|
804
|
-
// Exit if the user doesn't confirm
|
|
805
|
-
if (!confirm.confirm) {
|
|
806
|
-
process.exit(0);
|
|
807
|
-
}
|
|
808
|
-
// Build the module
|
|
809
|
-
const { cfg, path: path$1 } = await buildModule(opts.dir);
|
|
810
|
-
// Read the compiled module code
|
|
811
|
-
const code = await fs.promises.readFile(path$1, { encoding: "utf-8" });
|
|
812
|
-
// Generate a secret for the module
|
|
813
|
-
const webhook = new Webhook({
|
|
814
|
-
...cfg.pepr,
|
|
815
|
-
description: cfg.description,
|
|
816
|
-
}, opts.host);
|
|
817
|
-
// Write the TLS cert and key to disk
|
|
818
|
-
await fs.promises.writeFile("insecure-tls.crt", webhook.tls.pem.crt);
|
|
819
|
-
await fs.promises.writeFile("insecure-tls.key", webhook.tls.pem.key);
|
|
820
|
-
try {
|
|
821
|
-
await webhook.deploy(code);
|
|
822
|
-
types.logger.info(`Module deployed successfully`);
|
|
823
|
-
const moduleFiles = path.resolve(opts.dir, "**", "*.ts");
|
|
824
|
-
const watcher = chokidar.watch(moduleFiles);
|
|
825
|
-
const peprTS = path.resolve(opts.dir, "pepr.ts");
|
|
826
|
-
let program;
|
|
827
|
-
// Run the module once to start the server
|
|
828
|
-
runDev(peprTS);
|
|
829
|
-
// Watch for changes
|
|
830
|
-
watcher.on("ready", () => {
|
|
831
|
-
types.logger.info(`Watching for changes in ${moduleFiles}`);
|
|
832
|
-
watcher.on("all", async (event, path) => {
|
|
833
|
-
types.logger.debug({ event, path }, "File changed");
|
|
834
|
-
// Kill the running process
|
|
835
|
-
if (program) {
|
|
836
|
-
program.kill("SIGKILL");
|
|
837
|
-
}
|
|
838
|
-
// Start the process again
|
|
839
|
-
program = runDev(peprTS);
|
|
840
|
-
});
|
|
841
|
-
});
|
|
842
|
-
}
|
|
843
|
-
catch (e) {
|
|
844
|
-
types.logger.error(`Error deploying module: ${e}`);
|
|
845
|
-
process.exit(1);
|
|
846
|
-
}
|
|
847
|
-
});
|
|
848
|
-
}
|
|
849
|
-
function runDev(path) {
|
|
850
|
-
try {
|
|
851
|
-
const program = child_process.spawn("./node_modules/.bin/ts-node", [path], {
|
|
852
|
-
env: {
|
|
853
|
-
...process.env,
|
|
854
|
-
SSL_KEY_PATH: "insecure-tls.key",
|
|
855
|
-
SSL_CERT_PATH: "insecure-tls.crt",
|
|
856
|
-
},
|
|
857
|
-
});
|
|
858
|
-
program.stdout.on("data", data => console.log(data.toString()));
|
|
859
|
-
program.stderr.on("data", data => console.error(data.toString()));
|
|
860
|
-
program.on("close", code => {
|
|
861
|
-
types.logger.info(`Process exited with code ${code}`);
|
|
862
|
-
});
|
|
863
|
-
return program;
|
|
864
|
-
}
|
|
865
|
-
catch (e) {
|
|
866
|
-
types.logger.debug(e);
|
|
867
|
-
types.logger.error(`Error running module: ${e}`);
|
|
868
|
-
process.exit(1);
|
|
869
|
-
}
|
|
870
|
-
}
|
|
871
|
-
|
|
872
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
873
|
-
/**
|
|
874
|
-
* Sanitize a user input name to be used as a pepr module directory name
|
|
875
|
-
*
|
|
876
|
-
* @param name the user input name
|
|
877
|
-
* @returns the sanitized name
|
|
878
|
-
*/
|
|
879
|
-
function sanitizeName(name) {
|
|
880
|
-
// Replace any characters outside of [^a-z0-9-] with "-"
|
|
881
|
-
let sanitized = name.toLowerCase().replace(/[^a-z0-9-]+/gi, "-");
|
|
882
|
-
// Remove any leading or trailing hyphens
|
|
883
|
-
sanitized = sanitized.replace(/^-+|-+$/g, "");
|
|
884
|
-
// Replace multiple hyphens with a single hyphen
|
|
885
|
-
sanitized = sanitized.replace(/--+/g, "-");
|
|
886
|
-
return sanitized;
|
|
887
|
-
}
|
|
888
|
-
/**
|
|
889
|
-
* Creates a directory and throws an error if it already exists
|
|
890
|
-
*
|
|
891
|
-
* @param dir - The directory to create
|
|
892
|
-
*/
|
|
893
|
-
async function createDir(dir) {
|
|
894
|
-
try {
|
|
895
|
-
await fs.promises.mkdir(dir);
|
|
896
|
-
}
|
|
897
|
-
catch (err) {
|
|
898
|
-
// The directory already exists
|
|
899
|
-
if (err.code === "EEXIST") {
|
|
900
|
-
throw new Error(`Directory ${dir} already exists`);
|
|
901
|
-
}
|
|
902
|
-
else {
|
|
903
|
-
throw err;
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
|
-
}
|
|
907
|
-
/**
|
|
908
|
-
* Write data to a file on disk
|
|
909
|
-
* @param path - The path to the file
|
|
910
|
-
* @param data - The data to write
|
|
911
|
-
* @returns A promise that resolves when the file has been written
|
|
912
|
-
*/
|
|
913
|
-
function write(path, data) {
|
|
914
|
-
// If the data is not a string, stringify it
|
|
915
|
-
if (typeof data !== "string") {
|
|
916
|
-
data = JSON.stringify(data, null, 2);
|
|
917
|
-
}
|
|
918
|
-
return fs.promises.writeFile(path, data);
|
|
919
|
-
}
|
|
920
|
-
|
|
921
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
922
|
-
function genPeprTS() {
|
|
923
|
-
return {
|
|
924
|
-
path: "pepr.ts",
|
|
925
|
-
data: `import { PeprModule } from "pepr";
|
|
926
|
-
import cfg from "./package.json";
|
|
927
|
-
import { HelloPepr } from "./capabilities/hello-pepr";
|
|
928
|
-
|
|
929
|
-
/**
|
|
930
|
-
* This is the main entrypoint for the Pepr module. It is the file that is run when the module is started.
|
|
931
|
-
* This is where you register your configurations and capabilities with the module.
|
|
932
|
-
*/
|
|
933
|
-
new PeprModule(cfg, [
|
|
934
|
-
// "HelloPepr" is a demo capability that is included with Pepr. You can remove it if you want.
|
|
935
|
-
HelloPepr,
|
|
936
|
-
|
|
937
|
-
// Your additional capabilities go here
|
|
938
|
-
]);
|
|
939
|
-
`,
|
|
940
|
-
};
|
|
941
|
-
}
|
|
942
|
-
function genPkgJSON(opts) {
|
|
943
|
-
// Generate a random UUID for the module based on the module name
|
|
944
|
-
const uuid$1 = uuid.v5(opts.name, uuid.v4());
|
|
945
|
-
// Generate a name for the module based on the module name
|
|
946
|
-
const name = sanitizeName(opts.name);
|
|
947
|
-
const data = {
|
|
948
|
-
name,
|
|
949
|
-
version: "0.0.1",
|
|
950
|
-
description: opts.description,
|
|
951
|
-
keywords: ["pepr", "k8s", "policy-engine", "pepr-module", "security"],
|
|
952
|
-
pepr: {
|
|
953
|
-
name: opts.name.trim(),
|
|
954
|
-
version,
|
|
955
|
-
uuid: uuid$1,
|
|
956
|
-
onError: opts.errorBehavior,
|
|
957
|
-
alwaysIgnore: {
|
|
958
|
-
namespaces: [],
|
|
959
|
-
labels: [],
|
|
960
|
-
},
|
|
961
|
-
},
|
|
962
|
-
scripts: {
|
|
963
|
-
build: "pepr build",
|
|
964
|
-
start: "pepr test",
|
|
965
|
-
},
|
|
966
|
-
dependencies: {
|
|
967
|
-
pepr: `^${version}`,
|
|
968
|
-
},
|
|
969
|
-
};
|
|
970
|
-
return {
|
|
971
|
-
data,
|
|
972
|
-
path: "package.json",
|
|
973
|
-
print: util.inspect(data, false, 5, true),
|
|
974
|
-
};
|
|
975
|
-
}
|
|
976
|
-
const tsConfig = {
|
|
977
|
-
path: "tsconfig.json",
|
|
978
|
-
data: {
|
|
979
|
-
compilerOptions: {
|
|
980
|
-
esModuleInterop: true,
|
|
981
|
-
lib: ["ES2020"],
|
|
982
|
-
moduleResolution: "node",
|
|
983
|
-
resolveJsonModule: true,
|
|
984
|
-
rootDir: ".",
|
|
985
|
-
strict: false,
|
|
986
|
-
target: "ES2020",
|
|
987
|
-
},
|
|
988
|
-
include: ["**/*.ts", "node_modules/pepr/**/*.ts"],
|
|
989
|
-
},
|
|
990
|
-
};
|
|
991
|
-
const gitIgnore = {
|
|
992
|
-
path: ".gitignore",
|
|
993
|
-
data: `# Ignore node_modules
|
|
994
|
-
node_modules
|
|
995
|
-
dist
|
|
996
|
-
`,
|
|
997
|
-
};
|
|
998
|
-
const prettierRC = {
|
|
999
|
-
path: ".prettierrc",
|
|
1000
|
-
data: {
|
|
1001
|
-
arrowParens: "avoid",
|
|
1002
|
-
bracketSameLine: false,
|
|
1003
|
-
bracketSpacing: true,
|
|
1004
|
-
embeddedLanguageFormatting: "auto",
|
|
1005
|
-
insertPragma: false,
|
|
1006
|
-
printWidth: 80,
|
|
1007
|
-
quoteProps: "as-needed",
|
|
1008
|
-
requirePragma: false,
|
|
1009
|
-
semi: true,
|
|
1010
|
-
tabWidth: 2,
|
|
1011
|
-
useTabs: false,
|
|
1012
|
-
},
|
|
1013
|
-
};
|
|
1014
|
-
const readme = {
|
|
1015
|
-
path: "README.md",
|
|
1016
|
-
data: `# Pepr Module
|
|
1017
|
-
|
|
1018
|
-
This is a Pepr module. It is a module that can be used with the [Pepr]() framework.
|
|
1019
|
-
|
|
1020
|
-
The \`capabilities\` directory contains all the capabilities for this module. By default,
|
|
1021
|
-
a capability is a single typescript file in the format of \`capability-name.ts\` that is
|
|
1022
|
-
imported in the root \`pepr.ts\` file as \`import { HelloPepr } from "./capabilities/hello-pepr";\`.
|
|
1023
|
-
Because this is typescript, you can organize this however you choose, e.g. creating a sub-folder
|
|
1024
|
-
per-capability or common logic in shared files or folders.
|
|
1025
|
-
|
|
1026
|
-
Example Structure:
|
|
1027
|
-
|
|
1028
|
-
\`\`\`
|
|
1029
|
-
Module Root
|
|
1030
|
-
├── package.json
|
|
1031
|
-
├── pepr.ts
|
|
1032
|
-
└── capabilities
|
|
1033
|
-
├── example-one.ts
|
|
1034
|
-
├── example-three.ts
|
|
1035
|
-
└── example-two.ts
|
|
1036
|
-
\`\`\`
|
|
1037
|
-
`,
|
|
1038
|
-
};
|
|
1039
|
-
const capabilityHelloPeprTS = {
|
|
1040
|
-
path: "hello-pepr.ts",
|
|
1041
|
-
data: `import { Capability, a } from "pepr";
|
|
1042
|
-
|
|
1043
|
-
export const HelloPepr = new Capability({
|
|
1044
|
-
name: "hello-pepr",
|
|
1045
|
-
description: "A simple example capability to show how things work.",
|
|
1046
|
-
namespaces: ["pepr-demo"],
|
|
1047
|
-
});
|
|
1048
|
-
|
|
1049
|
-
// Use the 'When' function to create a new Capability Action
|
|
1050
|
-
const { When } = HelloPepr;
|
|
1051
|
-
|
|
1052
|
-
/**
|
|
1053
|
-
* This is a single Capability Action. They can be in the same file or put imported from other files.
|
|
1054
|
-
* In this exmaple, when a ConfigMap is created with the name \`example-1\`, then add a label and annotation.
|
|
1055
|
-
*
|
|
1056
|
-
* Equivelant to manually running:
|
|
1057
|
-
* \`kubectl label configmap example-1 pepr=was-here\`
|
|
1058
|
-
* \`kubectl annotate configmap example-1 pepr.dev=annotations-work-too\`
|
|
1059
|
-
*/
|
|
1060
|
-
When(a.ConfigMap)
|
|
1061
|
-
.IsCreated()
|
|
1062
|
-
.WithName("example-1")
|
|
1063
|
-
.Then(request =>
|
|
1064
|
-
request
|
|
1065
|
-
.SetLabel("pepr", "was-here")
|
|
1066
|
-
.SetAnnotation("pepr.dev", "annotations-work-too")
|
|
1067
|
-
);
|
|
1068
|
-
|
|
1069
|
-
/**
|
|
1070
|
-
* This Capabiility Action does the exact same changes for example-2, except this time it uses the \`.ThenSet()\` feature.
|
|
1071
|
-
* You can stack multiple \`.Then()\` calls, but only a single \`.ThenSet()\`
|
|
1072
|
-
*/
|
|
1073
|
-
When(a.ConfigMap)
|
|
1074
|
-
.IsCreated()
|
|
1075
|
-
.WithName("example-2")
|
|
1076
|
-
.ThenSet({
|
|
1077
|
-
metadata: {
|
|
1078
|
-
labels: {
|
|
1079
|
-
pepr: "was-here",
|
|
1080
|
-
},
|
|
1081
|
-
annotations: {
|
|
1082
|
-
"pepr.dev": "annotations-work-too",
|
|
1083
|
-
},
|
|
1084
|
-
},
|
|
1085
|
-
});
|
|
1086
|
-
|
|
1087
|
-
/**
|
|
1088
|
-
* This Capability Action combines different styles. Unlike the previous actions, this one will look for any ConfigMap
|
|
1089
|
-
* in the \`pepr-demo\` namespace that has the label \`change=by-label\`. Note that all conditions added such as \`WithName()\`,
|
|
1090
|
-
* \`WithLabel()\`, \`InNamespace()\`, are ANDs so all conditions must be true for the request to be procssed.
|
|
1091
|
-
*/
|
|
1092
|
-
When(a.ConfigMap)
|
|
1093
|
-
.IsCreated()
|
|
1094
|
-
.WithLabel("change", "by-label")
|
|
1095
|
-
.Then(request => {
|
|
1096
|
-
// The K8s object e are going to mutate
|
|
1097
|
-
const cm = request.Raw;
|
|
1098
|
-
|
|
1099
|
-
// Get the username and uid of the K8s reuest
|
|
1100
|
-
const { username, uid } = request.Request.userInfo;
|
|
1101
|
-
|
|
1102
|
-
// Store some data about the request in the configmap
|
|
1103
|
-
cm.data["username"] = username;
|
|
1104
|
-
cm.data["uid"] = uid;
|
|
1105
|
-
|
|
1106
|
-
// You can still mix other ways of making changes too
|
|
1107
|
-
request.SetAnnotation("pepr.dev", "making-waves");
|
|
1108
|
-
});
|
|
1109
|
-
`,
|
|
1110
|
-
};
|
|
1111
|
-
const capabilitySnippet = {
|
|
1112
|
-
path: "pepr.code-snippets",
|
|
1113
|
-
data: `{
|
|
1114
|
-
"Create a new Pepr capability": {
|
|
1115
|
-
"prefix": "create pepr capability",
|
|
1116
|
-
"body": [
|
|
1117
|
-
"import { Capability, a } from 'pepr';",
|
|
1118
|
-
"",
|
|
1119
|
-
"export const $\{TM_FILENAME_BASE/(.*)/$\{1:/pascalcase}/} = new Capability({",
|
|
1120
|
-
"\\tname: '$\{TM_FILENAME_BASE}',",
|
|
1121
|
-
"\\tdescription: '$\{1:A brief description of this capability.}',",
|
|
1122
|
-
"\\tnamespaces: [$\{2:}],",
|
|
1123
|
-
"});",
|
|
1124
|
-
"",
|
|
1125
|
-
"// Use the 'When' function to create a new Capability Action",
|
|
1126
|
-
"const { When } = $\{TM_FILENAME_BASE/(.*)/$\{1:/pascalcase}/};",
|
|
1127
|
-
"",
|
|
1128
|
-
"// When(a.<Kind>).Is<Event>().Then(change => change.<changes>",
|
|
1129
|
-
"When($\{3:})"
|
|
1130
|
-
],
|
|
1131
|
-
"description": "Creates a new Pepr capability with a specified description, and optional namespaces, and adds a When statement for the specified value."
|
|
1132
|
-
}
|
|
1133
|
-
}`,
|
|
1134
|
-
};
|
|
1135
|
-
|
|
1136
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
1137
|
-
function walkthrough() {
|
|
1138
|
-
const askName = {
|
|
1139
|
-
type: "text",
|
|
1140
|
-
name: "name",
|
|
1141
|
-
message: "Enter a name for the new Pepr module. This will create a new directory based on the name.\n",
|
|
1142
|
-
validate: async (val) => {
|
|
1143
|
-
try {
|
|
1144
|
-
const name = sanitizeName(val);
|
|
1145
|
-
await fs.promises.access(name, fs.promises.constants.F_OK);
|
|
1146
|
-
return "A directory with this name already exists";
|
|
1147
|
-
}
|
|
1148
|
-
catch (e) {
|
|
1149
|
-
return val.length > 2 || "The name must be at least 3 characters long";
|
|
1150
|
-
}
|
|
1151
|
-
},
|
|
1152
|
-
};
|
|
1153
|
-
const askDescription = {
|
|
1154
|
-
type: "text",
|
|
1155
|
-
name: "description",
|
|
1156
|
-
message: "(Recommended) Enter a description for the new Pepr module.\n",
|
|
1157
|
-
};
|
|
1158
|
-
const askErrorBehavior = {
|
|
1159
|
-
type: "select",
|
|
1160
|
-
name: "errorBehavior",
|
|
1161
|
-
validate: val => types.ErrorBehavior[val],
|
|
1162
|
-
message: "How do you want Pepr to handle errors encountered during K8s operations?",
|
|
1163
|
-
choices: [
|
|
1164
|
-
{
|
|
1165
|
-
title: "Ignore",
|
|
1166
|
-
value: types.ErrorBehavior.ignore,
|
|
1167
|
-
description: "Pepr will continue processing and generate an entry in the Pepr Controller log.",
|
|
1168
|
-
selected: true,
|
|
1169
|
-
},
|
|
1170
|
-
{
|
|
1171
|
-
title: "Log an audit event",
|
|
1172
|
-
value: types.ErrorBehavior.audit,
|
|
1173
|
-
description: "Pepr will continue processing and generate an entry in the Pepr Controller log as well as an audit event in the cluster.",
|
|
1174
|
-
},
|
|
1175
|
-
{
|
|
1176
|
-
title: "Reject the operation",
|
|
1177
|
-
value: types.ErrorBehavior.reject,
|
|
1178
|
-
description: "Pepr will reject the operation and return an error to the client.",
|
|
1179
|
-
},
|
|
1180
|
-
],
|
|
1181
|
-
};
|
|
1182
|
-
return prompt([askName, askDescription, askErrorBehavior]);
|
|
1183
|
-
}
|
|
1184
|
-
async function confirm(dirName, packageJSON, peprTSPath) {
|
|
1185
|
-
console.log(`
|
|
1186
|
-
To be generated:
|
|
1187
|
-
|
|
1188
|
-
\x1b[1m${dirName}\x1b[0m
|
|
1189
|
-
├── \x1b[1m${gitIgnore.path}\x1b[0m
|
|
1190
|
-
├── \x1b[1m${prettierRC.path}\x1b[0m
|
|
1191
|
-
├── \x1b[1mcapabilties\x1b[0m
|
|
1192
|
-
| └── \x1b[1mhello-pepr.ts\x1b[0m
|
|
1193
|
-
├── \x1b[1m${packageJSON.path}\x1b[0m
|
|
1194
|
-
${packageJSON.print.replace(/^/gm, " │ ")}
|
|
1195
|
-
├── \x1b[1m${peprTSPath}\x1b[0m
|
|
1196
|
-
├── \x1b[1m${readme.path}\x1b[0m
|
|
1197
|
-
└── \x1b[1m${tsConfig.path}\x1b[0m
|
|
1198
|
-
`);
|
|
1199
|
-
const confirm = await prompt({
|
|
1200
|
-
type: "confirm",
|
|
1201
|
-
name: "confirm",
|
|
1202
|
-
message: "Create the new Pepr module?",
|
|
1203
|
-
});
|
|
1204
|
-
return confirm.confirm;
|
|
1205
|
-
}
|
|
1206
|
-
|
|
1207
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
1208
|
-
function init (program) {
|
|
1209
|
-
program
|
|
1210
|
-
.command("init")
|
|
1211
|
-
.description("Initialize a new Pepr Module")
|
|
1212
|
-
.action(async () => {
|
|
1213
|
-
const response = await walkthrough();
|
|
1214
|
-
const dirName = sanitizeName(response.name);
|
|
1215
|
-
const packageJSON = genPkgJSON(response);
|
|
1216
|
-
const peprTS = genPeprTS();
|
|
1217
|
-
const confirmed = await confirm(dirName, packageJSON, peprTS.path);
|
|
1218
|
-
if (confirmed) {
|
|
1219
|
-
console.log("Creating new Pepr module...");
|
|
1220
|
-
try {
|
|
1221
|
-
await createDir(dirName);
|
|
1222
|
-
await createDir(path.resolve(dirName, ".vscode"));
|
|
1223
|
-
await createDir(path.resolve(dirName, "capabilities"));
|
|
1224
|
-
await write(path.resolve(dirName, gitIgnore.path), gitIgnore.data);
|
|
1225
|
-
await write(path.resolve(dirName, prettierRC.path), prettierRC.data);
|
|
1226
|
-
await write(path.resolve(dirName, packageJSON.path), packageJSON.data);
|
|
1227
|
-
await write(path.resolve(dirName, readme.path), readme.data);
|
|
1228
|
-
await write(path.resolve(dirName, tsConfig.path), tsConfig.data);
|
|
1229
|
-
await write(path.resolve(dirName, peprTS.path), peprTS.data);
|
|
1230
|
-
await write(path.resolve(dirName, ".vscode", capabilitySnippet.path), capabilitySnippet.data);
|
|
1231
|
-
await write(path.resolve(dirName, "capabilities", capabilityHelloPeprTS.path), capabilityHelloPeprTS.data);
|
|
1232
|
-
// run npm install from the new directory
|
|
1233
|
-
process.chdir(dirName);
|
|
1234
|
-
child_process.execSync("npm install", {
|
|
1235
|
-
stdio: "inherit",
|
|
1236
|
-
});
|
|
1237
|
-
console.log(`New Pepr module created at ${dirName}`);
|
|
1238
|
-
console.log(`Open VSCode or your editor of choice in ${dirName} to get started!`);
|
|
1239
|
-
}
|
|
1240
|
-
catch (e) {
|
|
1241
|
-
types.logger.debug(e);
|
|
1242
|
-
types.logger.error(e.message);
|
|
1243
|
-
process.exit(1);
|
|
1244
|
-
}
|
|
1245
|
-
}
|
|
1246
|
-
});
|
|
1247
|
-
}
|
|
1248
|
-
|
|
1249
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
1250
|
-
class RootCmd extends commander.Command {
|
|
1251
|
-
createCommand(name) {
|
|
1252
|
-
const cmd = new commander.Command(name);
|
|
1253
|
-
cmd.option("-l, --log-level [level]", "Log level: debug, info, warn, error", "info");
|
|
1254
|
-
cmd.hook("preAction", run => {
|
|
1255
|
-
types.logger.SetLogLevel(run.opts().logLevel);
|
|
1256
|
-
});
|
|
1257
|
-
return cmd;
|
|
1258
|
-
}
|
|
1259
|
-
}
|
|
1260
|
-
|
|
1261
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
1262
|
-
const exec = util.promisify(child_process.exec);
|
|
1263
|
-
function test (program) {
|
|
1264
|
-
program
|
|
1265
|
-
.command("test")
|
|
1266
|
-
.description("Test a Pepr Module locally")
|
|
1267
|
-
.option("-d, --dir [directory]", "Pepr module directory", ".")
|
|
1268
|
-
.option("-w, --watch", "Watch for changes and re-run the test")
|
|
1269
|
-
.action(async (opts) => {
|
|
1270
|
-
types.logger.info("Test Module");
|
|
1271
|
-
await buildAndTest(opts.dir);
|
|
1272
|
-
if (opts.watch) {
|
|
1273
|
-
const moduleFiles = path.resolve(opts.dir, "**", "*.ts");
|
|
1274
|
-
const watcher = chokidar.watch(moduleFiles);
|
|
1275
|
-
watcher.on("ready", () => {
|
|
1276
|
-
types.logger.info(`Watching for changes in ${moduleFiles}`);
|
|
1277
|
-
watcher.on("all", async (event, path) => {
|
|
1278
|
-
types.logger.debug({ event, path }, "File changed");
|
|
1279
|
-
await buildAndTest(opts.dir);
|
|
1280
|
-
});
|
|
1281
|
-
});
|
|
1282
|
-
}
|
|
1283
|
-
});
|
|
1284
|
-
}
|
|
1285
|
-
async function buildAndTest(dir) {
|
|
1286
|
-
const { path } = await buildModule(dir);
|
|
1287
|
-
types.logger.info(`Module built successfully at ${path}`);
|
|
1288
|
-
try {
|
|
1289
|
-
const { stdout, stderr } = await exec(`node ${path}`);
|
|
1290
|
-
console.log(stdout);
|
|
1291
|
-
console.log(stderr);
|
|
1292
|
-
}
|
|
1293
|
-
catch (e) {
|
|
1294
|
-
types.logger.debug(e);
|
|
1295
|
-
types.logger.error(`Error running module: ${e}`);
|
|
1296
|
-
process.exit(1);
|
|
1297
|
-
}
|
|
1298
|
-
}
|
|
1299
|
-
|
|
1300
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
1301
|
-
const program = new RootCmd();
|
|
1302
|
-
program
|
|
1303
|
-
.version(version)
|
|
1304
|
-
.description(`Pepr Kubernetes Thingy (v${version})`)
|
|
1305
|
-
.action(() => {
|
|
1306
|
-
if (program.args.length < 1) {
|
|
1307
|
-
console.log(banner);
|
|
1308
|
-
program.help();
|
|
1309
|
-
}
|
|
1310
|
-
});
|
|
1311
|
-
init(program);
|
|
1312
|
-
build(program);
|
|
1313
|
-
capability(program);
|
|
1314
|
-
test(program);
|
|
1315
|
-
deploy(program);
|
|
1316
|
-
dev(program);
|
|
1317
|
-
program.parse();
|