summaryrefslogtreecommitdiff
path: root/worker/scripts/ansible_playbook
diff options
context:
space:
mode:
Diffstat (limited to 'worker/scripts/ansible_playbook')
-rwxr-xr-xworker/scripts/ansible_playbook167
1 files changed, 101 insertions, 66 deletions
diff --git a/worker/scripts/ansible_playbook b/worker/scripts/ansible_playbook
index 062680d..e9e967c 100755
--- a/worker/scripts/ansible_playbook
+++ b/worker/scripts/ansible_playbook
@@ -1,78 +1,113 @@
#!/usr/bin/env -S deno run --allow-env --allow-net --allow-run --allow-read --allow-write
import {
- BitwardenSession,
- getRequiredEnv,
+ Either,
+ getRequiredEnvVars,
getStdout,
- loggerWithPrefix,
+ type IEither,
+ LogMetricTraceable,
+ Metric,
prependWith,
- type SecureNote,
-} from "@liz-ci/utils";
-import type { AnsiblePlaybookJobProps } from "@liz-ci/model";
+ TraceUtil,
+} from "@emprespresso/pengueno";
+import type { AnsiblePlaybookJob } from "@emprespresso/ci-model";
+import { Bitwarden, type SecureNote } from "@emprespresso/ci-worker";
-const args: AnsiblePlaybookJobProps = {
- path: getRequiredEnv("path"),
- playbooks: getRequiredEnv("playbooks"),
-};
-const logger = loggerWithPrefix(() =>
- `[${new Date().toISOString()}] [ansible_playbook.'${args.playbooks}']`
-);
+const eitherJob = getRequiredEnvVars([
+ "path",
+ "playbooks",
+])
+ .mapRight((baseArgs) => (
+ <AnsiblePlaybookJob> {
+ type: "ansible_playbook",
+ arguments: baseArgs,
+ }
+ ));
-const run = async () => {
- logger.log("Starting Ansible playbook job");
+const eitherVault = Bitwarden.getConfigFromEnvironment()
+ .mapRight((config) => new Bitwarden(config));
- const bitwardenSession = new BitwardenSession();
- const secretFiles = await Promise.all(
- ["ansible_secrets", "ssh_key"]
- .map((secretName) =>
- bitwardenSession
- .getItem<SecureNote>(secretName)
- .then(async ({ notes: recoveredSecret }) => {
- const tempFile = await Deno.makeTempFile();
- await Deno.writeTextFile(tempFile, recoveredSecret);
- logger.log(secretName, "stored at", tempFile);
- return tempFile;
- })
- ),
- );
- const [ansibleSecrets, sshKey] = secretFiles;
+const playbookMetric = Metric.fromName("ansiblePlaybook.playbook");
+await LogMetricTraceable.from(eitherJob)
+ .bimap(TraceUtil.withTrace("ansible_playbook"))
+ .bimap(TraceUtil.withMetricTrace(playbookMetric))
+ .peek((tEitherJob) =>
+ tEitherJob.trace.trace("starting ansible playbook job! (⑅˘꒳˘)")
+ )
+ .map((tEitherJob) =>
+ tEitherJob.get().flatMapAsync((job) =>
+ eitherVault.flatMapAsync(async (vault) => {
+ const eitherKey = await vault.unlock(tEitherJob);
+ return eitherKey.mapRight((key) => ({ job, key, vault }));
+ })
+ )
+ )
+ .map(async (tEitherJobVault) => {
+ tEitherJobVault.trace.trace(
+ "getting ansible secwets uwu~",
+ );
+ const eitherJobVault = await tEitherJobVault.get();
- try {
- const volumes = [
- `${args.path}:/ansible`,
- `${sshKey}:/root/id_rsa`,
- `${ansibleSecrets}:/ansible/secrets.yml`,
- ];
+ const eitherSshKey = await eitherJobVault
+ .flatMapAsync(({ key, vault }) =>
+ vault.fetchSecret<SecureNote>(tEitherJobVault, key, "ssh_key")
+ );
+ const eitherSshKeyFile = await eitherSshKey.mapRight(({ notes }) => notes)
+ .flatMapAsync(saveToTempFile);
+ const eitherAnsibleSecrets = await eitherJobVault
+ .flatMapAsync(({ key, vault }) =>
+ vault.fetchSecret<SecureNote>(tEitherJobVault, key, "ansible_playbooks")
+ );
+ const eitherAnsibleSecretsFile = await eitherAnsibleSecrets.mapRight((
+ { notes },
+ ) => notes).flatMapAsync(saveToTempFile);
- const playbookCmd = `ansible-playbook -e @secrets.yml ${args.playbooks}`;
- const deployCmd = [
- "docker",
- "run",
- ...prependWith(volumes, "-v"),
- "willhallonline/ansible:latest",
- ...playbookCmd.split(" "),
- ];
- logger.log("deploying...", deployCmd);
- await getStdout(deployCmd);
- } finally {
- await Promise.allSettled(
- [bitwardenSession.close()].concat(
- secretFiles.map((p) => {
- logger.log(`cleanup`, p);
- return Deno.remove(p);
- }),
- ),
+ return eitherJobVault.flatMapAsync(async ({ job, vault, key }) => {
+ const eitherLocked = await vault.lock(tEitherJobVault, key);
+ return eitherLocked.flatMap((_locked) =>
+ eitherSshKeyFile.flatMap((sshKeyFile) =>
+ eitherAnsibleSecretsFile.mapRight((secretsFile) => ({
+ job,
+ sshKeyFile,
+ secretsFile,
+ }))
+ )
+ );
+ });
+ })
+ .map(async (tEitherJobAndSecrets) => {
+ const eitherJobAndSecrets = await tEitherJobAndSecrets.get();
+ return eitherJobAndSecrets.flatMapAsync(
+ ({ job, sshKeyFile, secretsFile }) => {
+ const volumes = [
+ `${job.arguments.path}:/ansible`,
+ `${sshKeyFile}:/root/id_rsa`,
+ `${secretsFile}:/ansible/secrets.yml`,
+ ];
+ const playbookCmd =
+ `ansible-playbook -e @secrets.yml ${job.arguments.playbooks}`;
+ const deployCmd = [
+ "docker",
+ "run",
+ ...prependWith(volumes, "-v"),
+ "willhallonline/ansible:latest",
+ ...playbookCmd.split(" "),
+ ];
+ tEitherJobAndSecrets.trace.trace(
+ `running ansible magic~ (◕ᴗ◕✿) ${deployCmd}`,
+ );
+ return tEitherJobAndSecrets.move(deployCmd).map(getStdout).get();
+ },
);
- }
+ })
+ .get();
- logger.log("ansible playbook job completed");
-};
-
-if (import.meta.main) {
- try {
- await run();
- } catch (e) {
- logger.error("womp womp D:", e);
- throw e;
- }
-}
+const saveToTempFile = (text: string): Promise<IEither<Error, string>> =>
+ Either.fromFailableAsync(
+ Deno.makeTempDir({ dir: Deno.cwd() })
+ .then((dir) => Deno.makeTempFile({ dir }))
+ .then(async (f) => {
+ await Deno.writeTextFile(f, text);
+ return f;
+ }),
+ );