pepr 0.1.22 → 0.1.24
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/README.md +8 -6
- package/dist/pepr-cli.js +51 -54
- package/dist/pepr-core.js +71 -80
- package/dist/{types-672dd6e4.js → types-1709b44f.js} +2 -12
- package/index.ts +7 -2
- package/package.json +1 -1
- package/src/lib/k8s/webhook.ts +33 -33
- package/src/lib/index.ts +0 -10
package/README.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
<img align="right" width="40%" src=".images/pepr.png" />
|
|
4
4
|
|
|
5
|
+
Pepr is on a mission to save Kubernetes from the tyranny of YAML, intimidating glue code, bash scripts, and other makeshift solutions. As a Kubernetes controller, Pepr empowers you to define Kubernetes transformations using TypeScript, without software development expertise thanks to plain-english configurations. Pepr transforms a patchwork of forks, scripts, overlays, and other chaos into a cohesive, well-structured, and maintainable system. With Pepr, you can seamlessly transition IT ops tribal knowledge into code, simplifying documentation, testing, validation, and coordination of changes for a more predictable outcome.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- Define a set of Kubernetes transformations/actions as Pepr capabilities.
|
|
10
|
+
- Write capabilities in TypeScript and bundle them for in-cluster processing in [NodeJS](https://nodejs.org/).
|
|
11
|
+
- React to cluster resources by mutating them, creating new Kubernetes resources, or performing arbitrary exec/API operations.
|
|
12
|
+
|
|
5
13
|
Pepr is an open-source project that helps IT Ops teams of all skill levels manage and modify resources in a Kubernetes (K8s) cluster using TypeScript. Kubernetes simplifies the management of multiple computers working together to run and scale applications. Pepr acts as a smart assistant, automatically changing or validating parts of the system as needed.
|
|
6
14
|
|
|
7
15
|
TypeScript is used to create Pepr capabilities, benefiting from its error-catching and clean code features, but without requiring specialized software engineering experience or prior Typescript knowledge. Pepr also provides a user-friendly interface for writing commands in plain English in a [Fluent Interface](https://en.wikipedia.org/wiki/Fluent_interface) style.
|
|
@@ -10,12 +18,6 @@ Capabilities are logical groupings of actions, which are the atomic units of cha
|
|
|
10
18
|
|
|
11
19
|
Imagine Pepr as a smart home system where different devices communicate with each other. Pepr provides instructions, simplifying the management of the smart home. The project enables both expert and novice capability authors to improve management and interactions within the Kubernetes environment, making its features accessible to everyone.
|
|
12
20
|
|
|
13
|
-
## Features
|
|
14
|
-
|
|
15
|
-
- Define a set of Kubernetes transformations/actions as Pepr capabilities.
|
|
16
|
-
- Write capabilities in TypeScript and bundle them for in-cluster processing in [NodeJS](https://nodejs.org/).
|
|
17
|
-
- React to cluster resources by mutating them, creating new Kubernetes resources, or performing arbitrary exec/API operations.
|
|
18
|
-
|
|
19
21
|
## Concepts
|
|
20
22
|
|
|
21
23
|
### Module
|
package/dist/pepr-cli.js
CHANGED
|
@@ -7,10 +7,7 @@ var typescript = require('@rollup/plugin-typescript');
|
|
|
7
7
|
var fs = require('fs');
|
|
8
8
|
var path = require('path');
|
|
9
9
|
var rollup = require('rollup');
|
|
10
|
-
var types = require('./types-
|
|
11
|
-
require('@kubernetes/client-node/dist');
|
|
12
|
-
require('ramda');
|
|
13
|
-
require('fast-json-patch');
|
|
10
|
+
var types = require('./types-1709b44f.js');
|
|
14
11
|
var clientNode = require('@kubernetes/client-node');
|
|
15
12
|
var zlib = require('zlib');
|
|
16
13
|
var forge = require('node-forge');
|
|
@@ -21,7 +18,7 @@ var uuid = require('uuid');
|
|
|
21
18
|
var commander = require('commander');
|
|
22
19
|
var chokidar = require('chokidar');
|
|
23
20
|
|
|
24
|
-
var version = "0.1.
|
|
21
|
+
var version = "0.1.24";
|
|
25
22
|
|
|
26
23
|
// SPDX-License-Identifier: Apache-2.0
|
|
27
24
|
// SPDX-FileCopyrightText: 2023-Present The Pepr Authors
|
|
@@ -514,7 +511,7 @@ class Webhook {
|
|
|
514
511
|
return resources.map(r => clientNode.dumpYaml(r, { noRefs: true })).join("---\n");
|
|
515
512
|
}
|
|
516
513
|
async deploy(code) {
|
|
517
|
-
types.
|
|
514
|
+
types.logger.info("Establishing connection to Kubernetes");
|
|
518
515
|
const namespace = "pepr-system";
|
|
519
516
|
// Deploy the resources using the k8s API
|
|
520
517
|
const kubeConfig = new clientNode.KubeConfig();
|
|
@@ -526,70 +523,70 @@ class Webhook {
|
|
|
526
523
|
const networkApi = kubeConfig.makeApiClient(clientNode.NetworkingV1Api);
|
|
527
524
|
const ns = this.namespace();
|
|
528
525
|
try {
|
|
529
|
-
types.
|
|
526
|
+
types.logger.info("Checking for namespace");
|
|
530
527
|
await coreV1Api.readNamespace(namespace);
|
|
531
528
|
}
|
|
532
529
|
catch (e) {
|
|
533
|
-
types.
|
|
534
|
-
types.
|
|
530
|
+
types.logger.debug(e.body);
|
|
531
|
+
types.logger.info("Creating namespace");
|
|
535
532
|
await coreV1Api.createNamespace(ns);
|
|
536
533
|
}
|
|
537
534
|
const netpol = this.networkPolicy();
|
|
538
535
|
try {
|
|
539
|
-
types.
|
|
536
|
+
types.logger.info("Checking for network policy");
|
|
540
537
|
await networkApi.readNamespacedNetworkPolicy(netpol.metadata.name, namespace);
|
|
541
538
|
}
|
|
542
539
|
catch (e) {
|
|
543
|
-
types.
|
|
544
|
-
types.
|
|
540
|
+
types.logger.debug(e.body);
|
|
541
|
+
types.logger.info("Creating network policy");
|
|
545
542
|
await networkApi.createNamespacedNetworkPolicy(namespace, netpol);
|
|
546
543
|
}
|
|
547
544
|
const wh = this.mutatingWebhook();
|
|
548
545
|
try {
|
|
549
|
-
types.
|
|
546
|
+
types.logger.info("Creating mutating webhook");
|
|
550
547
|
await admissionApi.createMutatingWebhookConfiguration(wh);
|
|
551
548
|
}
|
|
552
549
|
catch (e) {
|
|
553
|
-
types.
|
|
554
|
-
types.
|
|
550
|
+
types.logger.debug(e.body);
|
|
551
|
+
types.logger.info("Removing and re-creating mutating webhook");
|
|
555
552
|
await admissionApi.deleteMutatingWebhookConfiguration(wh.metadata.name);
|
|
556
553
|
await admissionApi.createMutatingWebhookConfiguration(wh);
|
|
557
554
|
}
|
|
558
555
|
const crb = this.clusterRoleBinding();
|
|
559
556
|
try {
|
|
560
|
-
types.
|
|
557
|
+
types.logger.info("Creating cluster role binding");
|
|
561
558
|
await rbacApi.createClusterRoleBinding(crb);
|
|
562
559
|
}
|
|
563
560
|
catch (e) {
|
|
564
|
-
types.
|
|
565
|
-
types.
|
|
561
|
+
types.logger.debug(e.body);
|
|
562
|
+
types.logger.info("Removing and re-creating cluster role binding");
|
|
566
563
|
await rbacApi.deleteClusterRoleBinding(crb.metadata.name);
|
|
567
564
|
await rbacApi.createClusterRoleBinding(crb);
|
|
568
565
|
}
|
|
569
566
|
const cr = this.clusterRole();
|
|
570
567
|
try {
|
|
571
|
-
types.
|
|
568
|
+
types.logger.info("Creating cluster role");
|
|
572
569
|
await rbacApi.createClusterRole(cr);
|
|
573
570
|
}
|
|
574
571
|
catch (e) {
|
|
575
|
-
types.
|
|
576
|
-
types.
|
|
572
|
+
types.logger.debug(e.body);
|
|
573
|
+
types.logger.info("Removing and re-creating the cluster role");
|
|
577
574
|
try {
|
|
578
575
|
await rbacApi.deleteClusterRole(cr.metadata.name);
|
|
579
576
|
await rbacApi.createClusterRole(cr);
|
|
580
577
|
}
|
|
581
578
|
catch (e) {
|
|
582
|
-
types.
|
|
579
|
+
types.logger.debug(e.body);
|
|
583
580
|
}
|
|
584
581
|
}
|
|
585
582
|
const sa = this.serviceAccount();
|
|
586
583
|
try {
|
|
587
|
-
types.
|
|
584
|
+
types.logger.info("Creating service account");
|
|
588
585
|
await coreV1Api.createNamespacedServiceAccount(namespace, sa);
|
|
589
586
|
}
|
|
590
587
|
catch (e) {
|
|
591
|
-
types.
|
|
592
|
-
types.
|
|
588
|
+
types.logger.debug(e.body);
|
|
589
|
+
types.logger.info("Removing and re-creating service account");
|
|
593
590
|
await coreV1Api.deleteNamespacedServiceAccount(sa.metadata.name, namespace);
|
|
594
591
|
await coreV1Api.createNamespacedServiceAccount(namespace, sa);
|
|
595
592
|
}
|
|
@@ -599,45 +596,45 @@ class Webhook {
|
|
|
599
596
|
}
|
|
600
597
|
const mod = this.moduleSecret(code);
|
|
601
598
|
try {
|
|
602
|
-
types.
|
|
599
|
+
types.logger.info("Creating module secret");
|
|
603
600
|
await coreV1Api.createNamespacedSecret(namespace, mod);
|
|
604
601
|
}
|
|
605
602
|
catch (e) {
|
|
606
|
-
types.
|
|
607
|
-
types.
|
|
603
|
+
types.logger.debug(e.body);
|
|
604
|
+
types.logger.info("Removing and re-creating module secret");
|
|
608
605
|
await coreV1Api.deleteNamespacedSecret(mod.metadata.name, namespace);
|
|
609
606
|
await coreV1Api.createNamespacedSecret(namespace, mod);
|
|
610
607
|
}
|
|
611
608
|
const svc = this.service();
|
|
612
609
|
try {
|
|
613
|
-
types.
|
|
610
|
+
types.logger.info("Creating service");
|
|
614
611
|
await coreV1Api.createNamespacedService(namespace, svc);
|
|
615
612
|
}
|
|
616
613
|
catch (e) {
|
|
617
|
-
types.
|
|
618
|
-
types.
|
|
614
|
+
types.logger.debug(e.body);
|
|
615
|
+
types.logger.info("Removing and re-creating service");
|
|
619
616
|
await coreV1Api.deleteNamespacedService(svc.metadata.name, namespace);
|
|
620
617
|
await coreV1Api.createNamespacedService(namespace, svc);
|
|
621
618
|
}
|
|
622
619
|
const tls = this.tlsSecret();
|
|
623
620
|
try {
|
|
624
|
-
types.
|
|
621
|
+
types.logger.info("Creating TLS secret");
|
|
625
622
|
await coreV1Api.createNamespacedSecret(namespace, tls);
|
|
626
623
|
}
|
|
627
624
|
catch (e) {
|
|
628
|
-
types.
|
|
629
|
-
types.
|
|
625
|
+
types.logger.debug(e.body);
|
|
626
|
+
types.logger.info("Removing and re-creating TLS secret");
|
|
630
627
|
await coreV1Api.deleteNamespacedSecret(tls.metadata.name, namespace);
|
|
631
628
|
await coreV1Api.createNamespacedSecret(namespace, tls);
|
|
632
629
|
}
|
|
633
630
|
const dep = this.deployment();
|
|
634
631
|
try {
|
|
635
|
-
types.
|
|
632
|
+
types.logger.info("Creating deployment");
|
|
636
633
|
await appsApi.createNamespacedDeployment(namespace, dep);
|
|
637
634
|
}
|
|
638
635
|
catch (e) {
|
|
639
|
-
types.
|
|
640
|
-
types.
|
|
636
|
+
types.logger.debug(e.body);
|
|
637
|
+
types.logger.info("Removing and re-creating deployment");
|
|
641
638
|
await appsApi.deleteNamespacedDeployment(dep.metadata.name, namespace);
|
|
642
639
|
await appsApi.createNamespacedDeployment(namespace, dep);
|
|
643
640
|
}
|
|
@@ -667,8 +664,8 @@ function build (program) {
|
|
|
667
664
|
const zarf = webhook.zarfYaml(yamlFile);
|
|
668
665
|
await fs.promises.writeFile(yamlPath, yaml);
|
|
669
666
|
await fs.promises.writeFile(zarfPath, zarf);
|
|
670
|
-
types.
|
|
671
|
-
types.
|
|
667
|
+
types.logger.debug(`Module compiled successfully at ${path$1}`);
|
|
668
|
+
types.logger.info(`K8s resource for the module saved to ${yamlPath}`);
|
|
672
669
|
});
|
|
673
670
|
}
|
|
674
671
|
const externalLibs = [
|
|
@@ -727,8 +724,8 @@ async function buildModule(moduleDir) {
|
|
|
727
724
|
}
|
|
728
725
|
catch (e) {
|
|
729
726
|
// On any other error, exit with a non-zero exit code
|
|
730
|
-
types.
|
|
731
|
-
types.
|
|
727
|
+
types.logger.debug(e);
|
|
728
|
+
types.logger.error(e.message);
|
|
732
729
|
process.exit(1);
|
|
733
730
|
}
|
|
734
731
|
}
|
|
@@ -781,10 +778,10 @@ function deploy (program) {
|
|
|
781
778
|
}
|
|
782
779
|
try {
|
|
783
780
|
await webhook.deploy(code);
|
|
784
|
-
types.
|
|
781
|
+
types.logger.info(`Module deployed successfully`);
|
|
785
782
|
}
|
|
786
783
|
catch (e) {
|
|
787
|
-
types.
|
|
784
|
+
types.logger.error(`Error deploying module: ${e}`);
|
|
788
785
|
process.exit(1);
|
|
789
786
|
}
|
|
790
787
|
});
|
|
@@ -822,10 +819,10 @@ function dev (program) {
|
|
|
822
819
|
await fs.promises.writeFile("insecure-tls.key", webhook.tls.pem.key);
|
|
823
820
|
try {
|
|
824
821
|
await webhook.deploy(code);
|
|
825
|
-
types.
|
|
822
|
+
types.logger.info(`Module deployed successfully`);
|
|
826
823
|
}
|
|
827
824
|
catch (e) {
|
|
828
|
-
types.
|
|
825
|
+
types.logger.error(`Error deploying module: ${e}`);
|
|
829
826
|
process.exit(1);
|
|
830
827
|
}
|
|
831
828
|
});
|
|
@@ -1210,8 +1207,8 @@ function init (program) {
|
|
|
1210
1207
|
console.log(`Open VSCode or your editor of choice in ${dirName} to get started!`);
|
|
1211
1208
|
}
|
|
1212
1209
|
catch (e) {
|
|
1213
|
-
types.
|
|
1214
|
-
types.
|
|
1210
|
+
types.logger.debug(e);
|
|
1211
|
+
types.logger.error(e.message);
|
|
1215
1212
|
process.exit(1);
|
|
1216
1213
|
}
|
|
1217
1214
|
}
|
|
@@ -1224,7 +1221,7 @@ class RootCmd extends commander.Command {
|
|
|
1224
1221
|
const cmd = new commander.Command(name);
|
|
1225
1222
|
cmd.option("-l, --log-level [level]", "Log level: debug, info, warn, error", "info");
|
|
1226
1223
|
cmd.hook("preAction", run => {
|
|
1227
|
-
types.
|
|
1224
|
+
types.logger.SetLogLevel(run.opts().logLevel);
|
|
1228
1225
|
});
|
|
1229
1226
|
return cmd;
|
|
1230
1227
|
}
|
|
@@ -1239,15 +1236,15 @@ function test (program) {
|
|
|
1239
1236
|
.option("-d, --dir [directory]", "Pepr module directory", ".")
|
|
1240
1237
|
.option("-w, --watch", "Watch for changes and re-run the test")
|
|
1241
1238
|
.action(async (opts) => {
|
|
1242
|
-
types.
|
|
1239
|
+
types.logger.info("Test Module");
|
|
1243
1240
|
await buildAndTest(opts.dir);
|
|
1244
1241
|
if (opts.watch) {
|
|
1245
1242
|
const moduleFiles = path.resolve(opts.dir, "**", "*.ts");
|
|
1246
1243
|
const watcher = chokidar.watch(moduleFiles);
|
|
1247
1244
|
watcher.on("ready", () => {
|
|
1248
|
-
types.
|
|
1245
|
+
types.logger.info(`Watching for changes in ${moduleFiles}`);
|
|
1249
1246
|
watcher.on("all", async (event, path) => {
|
|
1250
|
-
types.
|
|
1247
|
+
types.logger.debug({ event, path }, "File changed");
|
|
1251
1248
|
await buildAndTest(opts.dir);
|
|
1252
1249
|
});
|
|
1253
1250
|
});
|
|
@@ -1256,15 +1253,15 @@ function test (program) {
|
|
|
1256
1253
|
}
|
|
1257
1254
|
async function buildAndTest(dir) {
|
|
1258
1255
|
const { path } = await buildModule(dir);
|
|
1259
|
-
types.
|
|
1256
|
+
types.logger.info(`Module built successfully at ${path}`);
|
|
1260
1257
|
try {
|
|
1261
1258
|
const { stdout, stderr } = await exec(`node ${path}`);
|
|
1262
1259
|
console.log(stdout);
|
|
1263
1260
|
console.log(stderr);
|
|
1264
1261
|
}
|
|
1265
1262
|
catch (e) {
|
|
1266
|
-
types.
|
|
1267
|
-
types.
|
|
1263
|
+
types.logger.debug(e);
|
|
1264
|
+
types.logger.error(`Error running module: ${e}`);
|
|
1268
1265
|
process.exit(1);
|
|
1269
1266
|
}
|
|
1270
1267
|
}
|
package/dist/pepr-core.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
var types = require('./types-672dd6e4.js');
|
|
5
4
|
var dist = require('@kubernetes/client-node/dist');
|
|
5
|
+
var types = require('./types-1709b44f.js');
|
|
6
6
|
var R = require('ramda');
|
|
7
7
|
var fastJsonPatch = require('fast-json-patch');
|
|
8
8
|
|
|
@@ -501,6 +501,16 @@ function modelToGroupVersionKind(key) {
|
|
|
501
501
|
return gvkMap[key];
|
|
502
502
|
}
|
|
503
503
|
|
|
504
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
505
|
+
// SPDX-FileCopyrightText: 2023-Present The Pepr Authors
|
|
506
|
+
var Operation;
|
|
507
|
+
(function (Operation) {
|
|
508
|
+
Operation["CREATE"] = "CREATE";
|
|
509
|
+
Operation["UPDATE"] = "UPDATE";
|
|
510
|
+
Operation["DELETE"] = "DELETE";
|
|
511
|
+
Operation["CONNECT"] = "CONNECT";
|
|
512
|
+
})(Operation || (Operation = {}));
|
|
513
|
+
|
|
504
514
|
// SPDX-License-Identifier: Apache-2.0
|
|
505
515
|
/**
|
|
506
516
|
* A capability is a unit of functionality that can be registered with the Pepr runtime.
|
|
@@ -553,10 +563,10 @@ class Capability {
|
|
|
553
563
|
callback: () => null,
|
|
554
564
|
};
|
|
555
565
|
const prefix = `${this._name}: ${model.name}`;
|
|
556
|
-
types.
|
|
566
|
+
types.logger.info(`Binding created`, prefix);
|
|
557
567
|
const Then = (cb) => {
|
|
558
|
-
types.
|
|
559
|
-
types.
|
|
568
|
+
types.logger.info(`Binding action created`, prefix);
|
|
569
|
+
types.logger.debug(cb.toString(), prefix);
|
|
560
570
|
// Push the binding to the list of bindings for this capability as a new BindingAction
|
|
561
571
|
// with the callback function to preserve
|
|
562
572
|
this._bindings.push({
|
|
@@ -572,22 +582,22 @@ class Capability {
|
|
|
572
582
|
return { Then };
|
|
573
583
|
};
|
|
574
584
|
function InNamespace(...namespaces) {
|
|
575
|
-
types.
|
|
585
|
+
types.logger.debug(`Add namespaces filter ${namespaces}`, prefix);
|
|
576
586
|
binding.filters.namespaces.push(...namespaces);
|
|
577
587
|
return { WithLabel, WithAnnotation, WithName, Then, ThenSet };
|
|
578
588
|
}
|
|
579
589
|
function WithName(name) {
|
|
580
|
-
types.
|
|
590
|
+
types.logger.debug(`Add name filter ${name}`, prefix);
|
|
581
591
|
binding.filters.name = name;
|
|
582
592
|
return { WithLabel, WithAnnotation, Then, ThenSet };
|
|
583
593
|
}
|
|
584
594
|
function WithLabel(key, value = "") {
|
|
585
|
-
types.
|
|
595
|
+
types.logger.debug(`Add label filter ${key}=${value}`, prefix);
|
|
586
596
|
binding.filters.labels[key] = value;
|
|
587
597
|
return { WithLabel, WithAnnotation, Then, ThenSet };
|
|
588
598
|
}
|
|
589
599
|
const WithAnnotation = (key, value = "") => {
|
|
590
|
-
types.
|
|
600
|
+
types.logger.debug(`Add annotation filter ${key}=${value}`, prefix);
|
|
591
601
|
binding.filters.annotations[key] = value;
|
|
592
602
|
return { WithLabel, WithAnnotation, Then, ThenSet };
|
|
593
603
|
};
|
|
@@ -612,11 +622,54 @@ class Capability {
|
|
|
612
622
|
this._name = cfg.name;
|
|
613
623
|
this._description = cfg.description;
|
|
614
624
|
this._namespaces = cfg.namespaces;
|
|
615
|
-
types.
|
|
616
|
-
types.
|
|
625
|
+
types.logger.info(`Capability ${this._name} registered`);
|
|
626
|
+
types.logger.debug(cfg);
|
|
617
627
|
}
|
|
618
628
|
}
|
|
619
629
|
|
|
630
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
631
|
+
/**
|
|
632
|
+
* shouldSkipRequest determines if a request should be skipped based on the binding filters.
|
|
633
|
+
*
|
|
634
|
+
* @param binding the capability action binding
|
|
635
|
+
* @param req the incoming request
|
|
636
|
+
* @returns
|
|
637
|
+
*/
|
|
638
|
+
function shouldSkipRequest(binding, req) {
|
|
639
|
+
const { group, kind, version } = binding.kind;
|
|
640
|
+
const { namespaces, labels, annotations } = binding.filters;
|
|
641
|
+
const { metadata } = req.object;
|
|
642
|
+
if (kind !== req.kind.kind) {
|
|
643
|
+
types.logger.debug(`${req.kind.kind} does not match ${kind}`);
|
|
644
|
+
return true;
|
|
645
|
+
}
|
|
646
|
+
if (group && group !== req.kind.group) {
|
|
647
|
+
types.logger.debug(`${req.kind.group} does not match ${group}`);
|
|
648
|
+
return true;
|
|
649
|
+
}
|
|
650
|
+
if (version && version !== req.kind.version) {
|
|
651
|
+
types.logger.debug(`${req.kind.version} does not match ${version}`);
|
|
652
|
+
return true;
|
|
653
|
+
}
|
|
654
|
+
if (namespaces.length && !namespaces.includes(req.namespace || "")) {
|
|
655
|
+
types.logger.debug(`${req.namespace} is not in ${namespaces}`);
|
|
656
|
+
return true;
|
|
657
|
+
}
|
|
658
|
+
for (const [key, value] of Object.entries(labels)) {
|
|
659
|
+
if (metadata?.labels?.[key] !== value) {
|
|
660
|
+
types.logger.debug(`${metadata?.labels?.[key]} does not match ${value}`);
|
|
661
|
+
return true;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
for (const [key, value] of Object.entries(annotations)) {
|
|
665
|
+
if (metadata?.annotations?.[key] !== value) {
|
|
666
|
+
types.logger.debug(`${metadata?.annotations?.[key]} does not match ${value}`);
|
|
667
|
+
return true;
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
return false;
|
|
671
|
+
}
|
|
672
|
+
|
|
620
673
|
// SPDX-License-Identifier: Apache-2.0
|
|
621
674
|
/**
|
|
622
675
|
* The RequestWrapper class provides methods to modify Kubernetes objects in the context
|
|
@@ -733,49 +786,6 @@ class RequestWrapper {
|
|
|
733
786
|
}
|
|
734
787
|
}
|
|
735
788
|
|
|
736
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
737
|
-
/**
|
|
738
|
-
* shouldSkipRequest determines if a request should be skipped based on the binding filters.
|
|
739
|
-
*
|
|
740
|
-
* @param binding the capability action binding
|
|
741
|
-
* @param req the incoming request
|
|
742
|
-
* @returns
|
|
743
|
-
*/
|
|
744
|
-
function shouldSkipRequest(binding, req) {
|
|
745
|
-
const { group, kind, version } = binding.kind;
|
|
746
|
-
const { namespaces, labels, annotations } = binding.filters;
|
|
747
|
-
const { metadata } = req.object;
|
|
748
|
-
if (kind !== req.kind.kind) {
|
|
749
|
-
types.Log.debug(`${req.kind.kind} does not match ${kind}`);
|
|
750
|
-
return true;
|
|
751
|
-
}
|
|
752
|
-
if (group && group !== req.kind.group) {
|
|
753
|
-
types.Log.debug(`${req.kind.group} does not match ${group}`);
|
|
754
|
-
return true;
|
|
755
|
-
}
|
|
756
|
-
if (version && version !== req.kind.version) {
|
|
757
|
-
types.Log.debug(`${req.kind.version} does not match ${version}`);
|
|
758
|
-
return true;
|
|
759
|
-
}
|
|
760
|
-
if (namespaces.length && !namespaces.includes(req.namespace || "")) {
|
|
761
|
-
types.Log.debug(`${req.namespace} is not in ${namespaces}`);
|
|
762
|
-
return true;
|
|
763
|
-
}
|
|
764
|
-
for (const [key, value] of Object.entries(labels)) {
|
|
765
|
-
if (metadata?.labels?.[key] !== value) {
|
|
766
|
-
types.Log.debug(`${metadata?.labels?.[key]} does not match ${value}`);
|
|
767
|
-
return true;
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
|
-
for (const [key, value] of Object.entries(annotations)) {
|
|
771
|
-
if (metadata?.annotations?.[key] !== value) {
|
|
772
|
-
types.Log.debug(`${metadata?.annotations?.[key]} does not match ${value}`);
|
|
773
|
-
return true;
|
|
774
|
-
}
|
|
775
|
-
}
|
|
776
|
-
return false;
|
|
777
|
-
}
|
|
778
|
-
|
|
779
789
|
// SPDX-License-Identifier: Apache-2.0
|
|
780
790
|
function processor(config, capabilities, req) {
|
|
781
791
|
const wrapped = new RequestWrapper(req);
|
|
@@ -785,16 +795,16 @@ function processor(config, capabilities, req) {
|
|
|
785
795
|
warnings: [],
|
|
786
796
|
allowed: false,
|
|
787
797
|
};
|
|
788
|
-
types.
|
|
798
|
+
types.logger.info(`Processing '${req.uid}' for '${req.kind.kind}' '${req.name}'`);
|
|
789
799
|
for (const { name, bindings } of capabilities) {
|
|
790
800
|
const prefix = `${req.uid} ${req.name}: ${name}`;
|
|
791
|
-
types.
|
|
801
|
+
types.logger.info(`Processing capability ${name}`, prefix);
|
|
792
802
|
for (const action of bindings) {
|
|
793
803
|
// Continue to the next action without doing anything if this one should be skipped
|
|
794
804
|
if (shouldSkipRequest(action, req)) {
|
|
795
805
|
continue;
|
|
796
806
|
}
|
|
797
|
-
types.
|
|
807
|
+
types.logger.info(`Processing matched action ${action.kind.kind}`, prefix);
|
|
798
808
|
// Add annotations to the request to indicate that the capability started processing
|
|
799
809
|
// this will allow tracking of failed mutations that were permitted to continue
|
|
800
810
|
const { metadata } = wrapped.Raw;
|
|
@@ -811,12 +821,12 @@ function processor(config, capabilities, req) {
|
|
|
811
821
|
response.warnings.push(`Action failed: ${e}`);
|
|
812
822
|
// If errors are not allowed, note the failure in the Reponse
|
|
813
823
|
if (config.onError) {
|
|
814
|
-
types.
|
|
824
|
+
types.logger.error(`Action failed: ${e}`, prefix);
|
|
815
825
|
response.result = "Pepr module configured to reject on error";
|
|
816
826
|
return response;
|
|
817
827
|
}
|
|
818
828
|
else {
|
|
819
|
-
types.
|
|
829
|
+
types.logger.warn(`Action failed: ${e}`, prefix);
|
|
820
830
|
metadata.annotations[identifier] = "warning";
|
|
821
831
|
}
|
|
822
832
|
}
|
|
@@ -834,7 +844,7 @@ function processor(config, capabilities, req) {
|
|
|
834
844
|
if (response.warnings.length < 1) {
|
|
835
845
|
delete response.warnings;
|
|
836
846
|
}
|
|
837
|
-
types.
|
|
847
|
+
types.logger.debug(patches);
|
|
838
848
|
return response;
|
|
839
849
|
}
|
|
840
850
|
|
|
@@ -859,7 +869,7 @@ class PeprModule {
|
|
|
859
869
|
this._state = [];
|
|
860
870
|
this._kinds = [];
|
|
861
871
|
this.Register = (capability) => {
|
|
862
|
-
types.
|
|
872
|
+
types.logger.info(`Registering capability ${capability.name}`);
|
|
863
873
|
// Add the kinds to the list of kinds (ignoring duplicates for now)
|
|
864
874
|
this._kinds = capability.bindings.map(({ kind }) => kind);
|
|
865
875
|
// Add the capability to the state
|
|
@@ -873,26 +883,7 @@ class PeprModule {
|
|
|
873
883
|
}
|
|
874
884
|
}
|
|
875
885
|
|
|
876
|
-
|
|
877
|
-
enumerable: true,
|
|
878
|
-
get: function () { return types.ErrorBehavior; }
|
|
879
|
-
});
|
|
880
|
-
Object.defineProperty(exports, 'Event', {
|
|
881
|
-
enumerable: true,
|
|
882
|
-
get: function () { return types.Event; }
|
|
883
|
-
});
|
|
884
|
-
Object.defineProperty(exports, 'HookPhase', {
|
|
885
|
-
enumerable: true,
|
|
886
|
-
get: function () { return types.HookPhase; }
|
|
887
|
-
});
|
|
888
|
-
exports.Log = types.Log;
|
|
889
|
-
Object.defineProperty(exports, 'Operation', {
|
|
890
|
-
enumerable: true,
|
|
891
|
-
get: function () { return types.Operation; }
|
|
892
|
-
});
|
|
886
|
+
exports.Log = types.logger;
|
|
893
887
|
exports.Capability = Capability;
|
|
894
888
|
exports.PeprModule = PeprModule;
|
|
895
|
-
exports.RequestWrapper = RequestWrapper;
|
|
896
889
|
exports.a = upstream;
|
|
897
|
-
exports.gvkMap = gvkMap;
|
|
898
|
-
exports.modelToGroupVersionKind = modelToGroupVersionKind;
|
|
@@ -116,17 +116,7 @@ class Logger {
|
|
|
116
116
|
return color + text + ConsoleColors.Reset;
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
|
-
var
|
|
120
|
-
|
|
121
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
122
|
-
// SPDX-FileCopyrightText: 2023-Present The Pepr Authors
|
|
123
|
-
exports.Operation = void 0;
|
|
124
|
-
(function (Operation) {
|
|
125
|
-
Operation["CREATE"] = "CREATE";
|
|
126
|
-
Operation["UPDATE"] = "UPDATE";
|
|
127
|
-
Operation["DELETE"] = "DELETE";
|
|
128
|
-
Operation["CONNECT"] = "CONNECT";
|
|
129
|
-
})(exports.Operation || (exports.Operation = {}));
|
|
119
|
+
var logger = new Logger(LogLevel.info);
|
|
130
120
|
|
|
131
121
|
// SPDX-License-Identifier: Apache-2.0
|
|
132
122
|
// SPDX-FileCopyrightText: 2023-Present The Pepr Authors
|
|
@@ -160,4 +150,4 @@ exports.Event = void 0;
|
|
|
160
150
|
Event["CreateOrUpdate"] = "createOrUpdate";
|
|
161
151
|
})(exports.Event || (exports.Event = {}));
|
|
162
152
|
|
|
163
|
-
exports.
|
|
153
|
+
exports.logger = logger;
|
package/index.ts
CHANGED
|
@@ -1,2 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { Capability } from './src/lib/capability';
|
|
2
|
+
import { a } from './src/lib/k8s';
|
|
3
|
+
import Log from './src/lib/logger';
|
|
4
|
+
import { PeprModule } from './src/lib/module';
|
|
5
|
+
|
|
6
|
+
export { a, PeprModule, Capability, Log };
|
|
7
|
+
|
package/package.json
CHANGED
package/src/lib/k8s/webhook.ts
CHANGED
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
dumpYaml,
|
|
23
23
|
} from "@kubernetes/client-node";
|
|
24
24
|
import { gzipSync } from "zlib";
|
|
25
|
-
import
|
|
25
|
+
import Log from "../logger";
|
|
26
26
|
import { ModuleConfig } from "../types";
|
|
27
27
|
import { TLSOut, genTLS } from "./tls";
|
|
28
28
|
|
|
@@ -406,7 +406,7 @@ export class Webhook {
|
|
|
406
406
|
}
|
|
407
407
|
|
|
408
408
|
async deploy(code: string) {
|
|
409
|
-
|
|
409
|
+
Log.info("Establishing connection to Kubernetes");
|
|
410
410
|
|
|
411
411
|
const namespace = "pepr-system";
|
|
412
412
|
|
|
@@ -422,68 +422,68 @@ export class Webhook {
|
|
|
422
422
|
|
|
423
423
|
const ns = this.namespace();
|
|
424
424
|
try {
|
|
425
|
-
|
|
425
|
+
Log.info("Checking for namespace");
|
|
426
426
|
await coreV1Api.readNamespace(namespace);
|
|
427
427
|
} catch (e) {
|
|
428
|
-
|
|
429
|
-
|
|
428
|
+
Log.debug(e.body);
|
|
429
|
+
Log.info("Creating namespace");
|
|
430
430
|
await coreV1Api.createNamespace(ns);
|
|
431
431
|
}
|
|
432
432
|
|
|
433
433
|
const netpol = this.networkPolicy();
|
|
434
434
|
try {
|
|
435
|
-
|
|
435
|
+
Log.info("Checking for network policy");
|
|
436
436
|
await networkApi.readNamespacedNetworkPolicy(netpol.metadata.name, namespace);
|
|
437
437
|
} catch (e) {
|
|
438
|
-
|
|
439
|
-
|
|
438
|
+
Log.debug(e.body);
|
|
439
|
+
Log.info("Creating network policy");
|
|
440
440
|
await networkApi.createNamespacedNetworkPolicy(namespace, netpol);
|
|
441
441
|
}
|
|
442
442
|
|
|
443
443
|
const wh = this.mutatingWebhook();
|
|
444
444
|
try {
|
|
445
|
-
|
|
445
|
+
Log.info("Creating mutating webhook");
|
|
446
446
|
await admissionApi.createMutatingWebhookConfiguration(wh);
|
|
447
447
|
} catch (e) {
|
|
448
|
-
|
|
449
|
-
|
|
448
|
+
Log.debug(e.body);
|
|
449
|
+
Log.info("Removing and re-creating mutating webhook");
|
|
450
450
|
await admissionApi.deleteMutatingWebhookConfiguration(wh.metadata.name);
|
|
451
451
|
await admissionApi.createMutatingWebhookConfiguration(wh);
|
|
452
452
|
}
|
|
453
453
|
|
|
454
454
|
const crb = this.clusterRoleBinding();
|
|
455
455
|
try {
|
|
456
|
-
|
|
456
|
+
Log.info("Creating cluster role binding");
|
|
457
457
|
await rbacApi.createClusterRoleBinding(crb);
|
|
458
458
|
} catch (e) {
|
|
459
|
-
|
|
460
|
-
|
|
459
|
+
Log.debug(e.body);
|
|
460
|
+
Log.info("Removing and re-creating cluster role binding");
|
|
461
461
|
await rbacApi.deleteClusterRoleBinding(crb.metadata.name);
|
|
462
462
|
await rbacApi.createClusterRoleBinding(crb);
|
|
463
463
|
}
|
|
464
464
|
|
|
465
465
|
const cr = this.clusterRole();
|
|
466
466
|
try {
|
|
467
|
-
|
|
467
|
+
Log.info("Creating cluster role");
|
|
468
468
|
await rbacApi.createClusterRole(cr);
|
|
469
469
|
} catch (e) {
|
|
470
|
-
|
|
471
|
-
|
|
470
|
+
Log.debug(e.body);
|
|
471
|
+
Log.info("Removing and re-creating the cluster role");
|
|
472
472
|
try {
|
|
473
473
|
await rbacApi.deleteClusterRole(cr.metadata.name);
|
|
474
474
|
await rbacApi.createClusterRole(cr);
|
|
475
475
|
} catch (e) {
|
|
476
|
-
|
|
476
|
+
Log.debug(e.body);
|
|
477
477
|
}
|
|
478
478
|
}
|
|
479
479
|
|
|
480
480
|
const sa = this.serviceAccount();
|
|
481
481
|
try {
|
|
482
|
-
|
|
482
|
+
Log.info("Creating service account");
|
|
483
483
|
await coreV1Api.createNamespacedServiceAccount(namespace, sa);
|
|
484
484
|
} catch (e) {
|
|
485
|
-
|
|
486
|
-
|
|
485
|
+
Log.debug(e.body);
|
|
486
|
+
Log.info("Removing and re-creating service account");
|
|
487
487
|
await coreV1Api.deleteNamespacedServiceAccount(sa.metadata.name, namespace);
|
|
488
488
|
await coreV1Api.createNamespacedServiceAccount(namespace, sa);
|
|
489
489
|
}
|
|
@@ -495,44 +495,44 @@ export class Webhook {
|
|
|
495
495
|
|
|
496
496
|
const mod = this.moduleSecret(code);
|
|
497
497
|
try {
|
|
498
|
-
|
|
498
|
+
Log.info("Creating module secret");
|
|
499
499
|
await coreV1Api.createNamespacedSecret(namespace, mod);
|
|
500
500
|
} catch (e) {
|
|
501
|
-
|
|
502
|
-
|
|
501
|
+
Log.debug(e.body);
|
|
502
|
+
Log.info("Removing and re-creating module secret");
|
|
503
503
|
await coreV1Api.deleteNamespacedSecret(mod.metadata.name, namespace);
|
|
504
504
|
await coreV1Api.createNamespacedSecret(namespace, mod);
|
|
505
505
|
}
|
|
506
506
|
|
|
507
507
|
const svc = this.service();
|
|
508
508
|
try {
|
|
509
|
-
|
|
509
|
+
Log.info("Creating service");
|
|
510
510
|
await coreV1Api.createNamespacedService(namespace, svc);
|
|
511
511
|
} catch (e) {
|
|
512
|
-
|
|
513
|
-
|
|
512
|
+
Log.debug(e.body);
|
|
513
|
+
Log.info("Removing and re-creating service");
|
|
514
514
|
await coreV1Api.deleteNamespacedService(svc.metadata.name, namespace);
|
|
515
515
|
await coreV1Api.createNamespacedService(namespace, svc);
|
|
516
516
|
}
|
|
517
517
|
|
|
518
518
|
const tls = this.tlsSecret();
|
|
519
519
|
try {
|
|
520
|
-
|
|
520
|
+
Log.info("Creating TLS secret");
|
|
521
521
|
await coreV1Api.createNamespacedSecret(namespace, tls);
|
|
522
522
|
} catch (e) {
|
|
523
|
-
|
|
524
|
-
|
|
523
|
+
Log.debug(e.body);
|
|
524
|
+
Log.info("Removing and re-creating TLS secret");
|
|
525
525
|
await coreV1Api.deleteNamespacedSecret(tls.metadata.name, namespace);
|
|
526
526
|
await coreV1Api.createNamespacedSecret(namespace, tls);
|
|
527
527
|
}
|
|
528
528
|
|
|
529
529
|
const dep = this.deployment();
|
|
530
530
|
try {
|
|
531
|
-
|
|
531
|
+
Log.info("Creating deployment");
|
|
532
532
|
await appsApi.createNamespacedDeployment(namespace, dep);
|
|
533
533
|
} catch (e) {
|
|
534
|
-
|
|
535
|
-
|
|
534
|
+
Log.debug(e.body);
|
|
535
|
+
Log.info("Removing and re-creating deployment");
|
|
536
536
|
await appsApi.deleteNamespacedDeployment(dep.metadata.name, namespace);
|
|
537
537
|
await appsApi.createNamespacedDeployment(namespace, dep);
|
|
538
538
|
}
|
package/src/lib/index.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
-
// SPDX-FileCopyrightText: 2023-Present The Pepr Authors
|
|
3
|
-
|
|
4
|
-
import logger from "./logger";
|
|
5
|
-
|
|
6
|
-
export { logger as Log };
|
|
7
|
-
export * from "./capability";
|
|
8
|
-
export * from "./request";
|
|
9
|
-
export * from "./module";
|
|
10
|
-
export * from "./types";
|