summaryrefslogtreecommitdiff
path: root/worker/scripts/ansible_playbook
blob: f2cd4b9a8ea2c7cd2b201604f7fe7edfd749153c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#!/usr/bin/env -S deno run --allow-env --allow-net --allow-run --allow-read --allow-write

import {
  Either,
  getRequiredEnvVars,
  getStdout,
  type IEither,
  LogMetricTraceable,
  Metric,
  prependWith,
  TraceUtil,
} from "@emprespresso/pengueno";
import type { AnsiblePlaybookJob } from "@emprespresso/ci_model";
import { Bitwarden, type SecureNote } from "@emprespresso/ci_worker";

const eitherJob = getRequiredEnvVars([
  "path",
  "playbooks",
])
  .mapRight((baseArgs) => (
    <AnsiblePlaybookJob> {
      type: "ansible_playbook",
      arguments: baseArgs,
    }
  ));

const eitherVault = Bitwarden.getConfigFromEnvironment()
  .mapRight((config) => new Bitwarden(config));

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();

    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);

    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();

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;
      }),
  );