Skip to content

Commit

Permalink
feat: add secret providers
Browse files Browse the repository at this point in the history
  • Loading branch information
vanhtuan0409 committed Nov 21, 2023
1 parent 5d0ec4c commit b3a9dfe
Show file tree
Hide file tree
Showing 8 changed files with 177 additions and 5 deletions.
Binary file modified js/bun.lockb
Binary file not shown.
2 changes: 1 addition & 1 deletion js/debug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ const graph = ModuleGraph.resolve(...modules);
graph.show();
console.log("");

const planner = new Planner(new Context());
const planner = new Planner(new Context("local"));
const plan = planner.planFromGraph(graph);
Planner.show(plan);
console.log("");
Expand Down
3 changes: 2 additions & 1 deletion js/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@anduintransaction/rivendell",
"version": "0.2.6",
"version": "0.3.0",
"repository": {
"type": "git",
"url": "git+https://github.com/anduintransaction/rivendell.git"
Expand Down Expand Up @@ -30,6 +30,7 @@
"@kubernetes-models/apimachinery": "^1.2.1",
"@kubernetes-models/base": "^4.0.3",
"chalk": "^5.3.0",
"infisical-node": "^1.5.0",
"yaml": "^2.3.4"
}
}
100 changes: 97 additions & 3 deletions js/src/context.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,103 @@
export interface SecretValue {
name: string;
value: string | undefined;
}

export interface SecretProvider {
get(env: string, name: string): Promise<SecretValue>;
getPrefix(env: string, prefix: string): Promise<SecretValue[]>;
}

export class NoopSecretProvider implements SecretProvider {
get(_: string, name: string): Promise<SecretValue> {
const paths = name.split("/");
const secretName = paths[paths.length - 1];
return Promise.resolve({
name: secretName,
value: undefined,
});
}

getPrefix(_: string, __: string): Promise<SecretValue[]> {
return Promise.resolve([]);
}
}

export class Context {
env: string;
configs: any;
secrets: any;
secretProvider: SecretProvider;

constructor(configs: any = {}, secrets: any = {}) {
constructor(
env: string,
configs: any = {},
secretProvider: SecretProvider = new NoopSecretProvider(),
) {
this.env = env;
this.configs = configs;
this.secrets = secrets;
this.secretProvider = secretProvider;
}

getSecret(name: string): Promise<SecretValue> {
return this.secretProvider.get(this.env, name);
}

getSecretPrefix(prefix: string): Promise<SecretValue[]> {
return this.secretProvider.getPrefix(this.env, prefix);
}
}

export const Finder = {
optinal<T>(
obj: any,
paths: string[],
defaultValue?: T,
): T | undefined {
let current = obj;
for (const p of paths) {
const val = current[p];
if (val === undefined || val === null) {
return (defaultValue === undefined) ? undefined : defaultValue;
}
current = val;
}
return current as T;
},

required<T>(
obj: any,
paths: string[],
defaultValue?: T,
): T {
const res = this.optinal(obj, paths, defaultValue);
if (res === undefined) {
throw new Error(`cannot find value for paths ${paths.join(".")}`);
}
return res!;
},

optionalString(obj: any, paths: string[], defaultValue?: string) {
return this.optinal<string>(obj, paths, defaultValue);
},
requiredString(obj: any, paths: string[], defaultValue?: string) {
return this.required<string>(obj, paths, defaultValue);
},
optionalNumber(obj: any, paths: string[], defaultValue?: number) {
return this.optinal<number>(obj, paths, defaultValue);
},
requiredNumber(obj: any, paths: string[], defaultValue?: number) {
return this.required<number>(obj, paths, defaultValue);
},
optionalBool(obj: any, paths: string[], defaultValue?: boolean) {
return this.optinal<boolean>(obj, paths, defaultValue);
},
requiredBool(obj: any, paths: string[], defaultValue?: boolean) {
return this.required<boolean>(obj, paths, defaultValue);
},

getAsArray<K>(obj: any, paths: string[], defaultValue?: K[]) {
const value = this.required(obj, paths, defaultValue);
if (Array.isArray(value)) return value;
return [value];
},
};
1 change: 1 addition & 0 deletions js/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export * from "./planner";
export * from "./context";
export * from "./common";
export * from "./runner";
export * from "./secrets";
2 changes: 2 additions & 0 deletions js/src/secrets/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./infisical";
export * from "./static";
41 changes: 41 additions & 0 deletions js/src/secrets/infisical.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import InfisicalClient from "infisical-node";
import { SecretProvider } from "../context";

export class InfisicalSecretProvider implements SecretProvider {
client: InfisicalClient;

constructor(siteURL: string, token: string, debug: boolean = false) {
this.client = new InfisicalClient({
token: token,
siteURL: siteURL,
debug: debug,
});
}

async get(env: string, name: string) {
const parts = name.split("/");
const [secretName, ...paths] = parts.reverse();
const secret = await this.client.getSecret(secretName, {
environment: env,
path: `/${paths.reverse().join("/")}`,
type: "shared",
});
return {
name: name,
value: secret.secretValue,
};
}

async getPrefix(env: string, prefix: string) {
const secrets = await this.client.getAllSecrets({
environment: env,
path: `/${prefix}`,
attachToProcessEnv: false,
includeImports: false,
});
return secrets.map((s) => ({
name: s.secretName,
value: s.secretValue,
}));
}
}
33 changes: 33 additions & 0 deletions js/src/secrets/static.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Finder, SecretProvider, SecretValue } from "../context";

export class StaticSecretProvider implements SecretProvider {
secrets: any;

constructor(secrets: any) {
this.secrets = secrets;
}

get(_: string, name: string): Promise<SecretValue> {
const paths = name.split("/");
const secretName = paths[paths.length - 1];
const value = Finder.optinal<string>(this.secrets, paths);
if (value !== undefined && typeof value !== "string") {
throw new Error(`invalid secret value type of secret ${name}`);
}
return Promise.resolve({ name: secretName, value });
}

getPrefix(_: string, prefix: string): Promise<SecretValue[]> {
const paths = prefix.split("/");
const value = Finder.optinal<any>(this.secrets, paths);
if (value === undefined) return Promise.resolve([]);
const res: SecretValue[] = [];
for (const [key, val] of Object.entries(value)) {
res.push({
name: key,
value: val as string,
});
}
return Promise.resolve(res);
}
}

0 comments on commit b3a9dfe

Please sign in to comment.