@catladder/cli 1.121.4 → 1.122.1

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/package.json CHANGED
@@ -52,7 +52,7 @@
52
52
  }
53
53
  ],
54
54
  "license": "MIT",
55
- "version": "1.121.4",
55
+ "version": "1.122.1",
56
56
  "scripts": {
57
57
  "lint": "eslint \"src/**/*.ts\"",
58
58
  "lint:fix": "eslint \"src/**/*.ts\" --fix",
@@ -73,7 +73,7 @@
73
73
  "node": ">=12.0.0"
74
74
  },
75
75
  "devDependencies": {
76
- "@catladder/pipeline": "1.121.4",
76
+ "@catladder/pipeline": "1.122.1",
77
77
  "@kubernetes/client-node": "^0.16.2",
78
78
  "@tsconfig/node14": "^1.0.1",
79
79
  "@types/common-tags": "^1.8.0",
@@ -92,6 +92,7 @@
92
92
  "clipboardy": "^2.0.0",
93
93
  "command-exists-promise": "^2.0.2",
94
94
  "common-tags": "^1.8.0",
95
+ "connection-string-parser": "^1.0.4",
95
96
  "dayjs": "^1.10.4",
96
97
  "eslint": "^8.7.0",
97
98
  "fs-extra": "^7.0.1",
@@ -1,6 +1,5 @@
1
- import { spawn } from "child-process-promise";
2
-
3
1
  import type Vorpal from "vorpal";
2
+ import { spawnCopyDb } from "../../../../gcloud/cloudSql/copyDb";
4
3
  import type { CloudSqlBackgroundProxy } from "../../../../gcloud/cloudSql/startProxy";
5
4
  import { startCloudSqlProxyInBackground } from "../../../../gcloud/cloudSql/startProxy";
6
5
 
@@ -119,34 +118,17 @@ export default async (vorpal: Vorpal) =>
119
118
  return;
120
119
  }
121
120
 
122
- const targetPSQL = (command: string) =>
123
- `PGPASSWORD=${targetPassword} psql -p ${targetPort} --host=localhost --user=${targetUsername} -q ${command}`;
124
-
125
- const copyDBScript = `
126
- set -e
127
-
128
-
129
-
130
- dumptmp=$(mktemp /tmp/dump.XXXXXX)
131
-
132
- echo "Dumping file to $dumptmp"
133
- pg_dump --dbname=postgres://${sourceUsername}:${sourcePassword}@localhost:${sourcePort}/${sourceDbName} --no-owner --no-privileges > $dumptmp
134
- echo "dump done"
135
- ${targetPSQL(
136
- `-c 'drop database "${targetDbName}" WITH (FORCE)' 1> /dev/null || true`
137
- )}
138
- ${targetPSQL(`-c 'create database "${targetDbName}"' 1> /dev/null`)}
139
- echo "Restoring dump..."
140
- ${targetPSQL(`"${targetDbName}" < $dumptmp 1> /dev/null`)}
141
-
142
-
143
- echo "Clean up..."
144
- set +e
145
- rm $dumptmp
146
- echo "\n🐱 Done!"
147
- `;
148
121
  try {
149
- await spawn(copyDBScript, [], { shell: "bash", stdio: "inherit" });
122
+ await spawnCopyDb({
123
+ targetPassword,
124
+ targetPort,
125
+ targetUsername,
126
+ sourceUsername,
127
+ sourcePassword,
128
+ sourcePort,
129
+ sourceDbName,
130
+ targetDbName,
131
+ });
150
132
  } finally {
151
133
  sourceProxy?.stop();
152
134
  targetProxy?.stop();
@@ -0,0 +1,175 @@
1
+ import type Vorpal from "vorpal";
2
+ import {
3
+ getEnvVarsResolved,
4
+ getPipelineContextByChoice,
5
+ } from "../../../../../config/getProjectConfig";
6
+ import type { CloudSqlBackgroundProxy } from "../../../../../gcloud/cloudSql/startProxy";
7
+ import { startCloudSqlProxyInBackground } from "../../../../../gcloud/cloudSql/startProxy";
8
+ import { envAndComponents } from "../utils/autocompletions";
9
+
10
+ import { ConnectionStringParser } from "connection-string-parser";
11
+ import { spawnCopyDb } from "../../../../../gcloud/cloudSql/copyDb";
12
+
13
+ export default async (vorpal: Vorpal) =>
14
+ vorpal
15
+ .command(
16
+ "project-cloud-sql-restore-db",
17
+ "restores a project db from one source to another target"
18
+ )
19
+ .action(async function restoreDb() {
20
+ const envs = await envAndComponents();
21
+
22
+ const { sourceEnvAndComponent } = await this.prompt({
23
+ type: "list",
24
+ name: "sourceEnvAndComponent",
25
+ choices: envs,
26
+
27
+ message: "Source instance (connection string or 'local')? šŸ¤” ",
28
+ });
29
+
30
+ const [sourceEnv, sourceComponent] = sourceEnvAndComponent.split(":");
31
+
32
+ const sourceContext = await getPipelineContextByChoice(
33
+ sourceEnv,
34
+ sourceComponent
35
+ );
36
+
37
+ const sourceEnvVars = await getEnvVarsResolved(
38
+ this,
39
+ sourceContext.environment.shortName,
40
+ sourceContext.componentName
41
+ );
42
+
43
+ let sourceProxy: CloudSqlBackgroundProxy;
44
+ let sourceDbName: string;
45
+
46
+ let sourceUsername: string;
47
+ let sourcePassword: string;
48
+
49
+ let targetUsername: string;
50
+ let targetPassword: string;
51
+ let targetProxy: CloudSqlBackgroundProxy;
52
+
53
+ let sourcePort: number;
54
+ let targetPort: number;
55
+ let targetDbName: string;
56
+
57
+ const closeAll = () => {
58
+ sourceProxy?.stop();
59
+ targetProxy?.stop();
60
+ };
61
+
62
+ if (sourceEnv === "local") {
63
+ const parser = new ConnectionStringParser({
64
+ scheme: "postgres",
65
+ hosts: [],
66
+ });
67
+
68
+ const parsersResult = parser.parse(sourceEnvVars.DATABASE_URL);
69
+ sourcePort = parsersResult.hosts?.[0]?.port;
70
+ sourceUsername = parsersResult.username;
71
+ sourcePassword = parsersResult.password;
72
+ sourceDbName = parsersResult.endpoint;
73
+ } else {
74
+ sourcePort = 54399;
75
+ sourceProxy = await startCloudSqlProxyInBackground({
76
+ instanceName: sourceEnvVars.CLOUD_SQL_INSTANCE_CONNECTION_NAME,
77
+ localPort: sourcePort,
78
+ });
79
+
80
+ sourceUsername = sourceEnvVars.DB_USER;
81
+ sourcePassword = sourceEnvVars.DB_PASSWORD;
82
+ sourceDbName = sourceEnvVars.DB_NAME;
83
+ }
84
+
85
+ const { targetEnvAndComponent } = await this.prompt({
86
+ type: "list",
87
+ name: "targetEnvAndComponent",
88
+ choices: envs,
89
+
90
+ message: "target env? šŸ¤” ",
91
+ });
92
+
93
+ const [targetEnv, targetComponent] = targetEnvAndComponent.split(":");
94
+
95
+ const targetContext = await getPipelineContextByChoice(
96
+ targetEnv,
97
+ targetComponent
98
+ );
99
+
100
+ const targetEnvVars = await getEnvVarsResolved(
101
+ this,
102
+ targetContext.environment.shortName,
103
+ targetContext.componentName
104
+ );
105
+
106
+ if (targetEnv === "local") {
107
+ const parser = new ConnectionStringParser({
108
+ scheme: "postgres",
109
+ hosts: [],
110
+ });
111
+
112
+ const parsersResult = parser.parse(targetEnvVars.DATABASE_URL);
113
+
114
+ targetPort = parsersResult.hosts?.[0]?.port;
115
+ targetUsername = parsersResult.username;
116
+ targetPassword = parsersResult.password;
117
+ targetDbName = parsersResult.endpoint;
118
+ } else {
119
+ targetPort = 54499;
120
+ targetProxy = await startCloudSqlProxyInBackground({
121
+ instanceName: targetEnvVars.CLOUD_SQL_INSTANCE_CONNECTION_NAME,
122
+ localPort: targetPort,
123
+ });
124
+
125
+ targetUsername = targetEnvVars.DB_USER;
126
+ targetPassword = targetEnvVars.DB_PASSWORD;
127
+ targetDbName = sourceEnvVars.DB_NAME;
128
+ }
129
+
130
+ const { shouldContinue } = await this.prompt({
131
+ type: "confirm",
132
+ name: "shouldContinue",
133
+ message: `This will drop ${targetEnv}/${targetDbName} and replace it with ${sourceEnv}/${sourceDbName}. Continue? šŸ¤” `,
134
+ });
135
+
136
+ if (!shouldContinue) {
137
+ this.log("abort");
138
+ closeAll();
139
+ return;
140
+ }
141
+
142
+ if (targetContext.environment.envType === "prod") {
143
+ this.log(
144
+ `\n🚨 You are overriding a production environment. Please type in ${targetEnvVars.CLOUD_SQL_INSTANCE_CONNECTION_NAME} to continue\n\n`
145
+ );
146
+ const { confirmInstance } = await this.prompt({
147
+ type: "input",
148
+ name: "confirmInstance",
149
+ message: "confirm: ",
150
+ });
151
+
152
+ if (
153
+ confirmInstance !== targetEnvVars.CLOUD_SQL_INSTANCE_CONNECTION_NAME
154
+ ) {
155
+ this.log("abort");
156
+ closeAll();
157
+ return;
158
+ }
159
+ }
160
+
161
+ try {
162
+ await spawnCopyDb({
163
+ targetPassword,
164
+ targetPort,
165
+ targetUsername,
166
+ sourceUsername,
167
+ sourcePassword,
168
+ sourcePort,
169
+ sourceDbName,
170
+ targetDbName,
171
+ });
172
+ } finally {
173
+ closeAll();
174
+ }
175
+ });
@@ -25,6 +25,7 @@ import commandTriggerCronjob from "./commandTriggerCronjob";
25
25
 
26
26
  import commandOpenGrafanaPod from "./commandOpenGrafanaPod";
27
27
  import commandSecretsClearBackups from "./commandSecretsClearBackups";
28
+ import commandProjectRestoreDb from "./cloudSql/commandProjectRestoreDb";
28
29
 
29
30
  export default async (vorpal: Vorpal) => {
30
31
  commandSetup(vorpal);
@@ -45,6 +46,7 @@ export default async (vorpal: Vorpal) => {
45
46
  commandOpenGrafana(vorpal);
46
47
  commandOpenGrafanaPod(vorpal);
47
48
  commandCloudSqlProxy(vorpal);
49
+ commandProjectRestoreDb(vorpal);
48
50
  commandOpenGit(vorpal);
49
51
  commandOpenEnv(vorpal);
50
52
  commandTriggerCronjob(vorpal);
@@ -0,0 +1,57 @@
1
+ import { spawn } from "child-process-promise";
2
+ type Opts = {
3
+ targetPassword: string;
4
+ targetPort: number;
5
+ targetUsername: string;
6
+ sourceUsername: string;
7
+ sourcePassword: string;
8
+ sourcePort: number;
9
+ sourceDbName: string;
10
+ targetDbName: string;
11
+ };
12
+ export const spawnCopyDb = async (opts: Opts) => {
13
+ const script = createCopyDbScript(opts);
14
+
15
+ return await spawn(script, [], {
16
+ shell: "bash",
17
+ stdio: "inherit",
18
+ });
19
+ };
20
+ const createCopyDbScript = ({
21
+ targetPassword,
22
+ targetPort,
23
+ targetUsername,
24
+ sourceUsername,
25
+ sourcePassword,
26
+ sourcePort,
27
+ sourceDbName,
28
+ targetDbName,
29
+ }: Opts) => {
30
+ const targetPSQL = (command: string) =>
31
+ `PGPASSWORD=${targetPassword} psql -p ${targetPort} --host=localhost --user=${targetUsername} -q ${command}`;
32
+
33
+ const copyDBScript = `
34
+ set -e
35
+
36
+
37
+
38
+ dumptmp=$(mktemp /tmp/dump.XXXXXX)
39
+
40
+ echo "Dumping file to $dumptmp"
41
+ pg_dump --dbname=postgres://${sourceUsername}:${sourcePassword}@localhost:${sourcePort}/${sourceDbName} --no-owner --no-privileges > $dumptmp
42
+ echo "dump done"
43
+ ${targetPSQL(
44
+ `-c 'drop database "${targetDbName}" WITH (FORCE)' 1> /dev/null || true`
45
+ )}
46
+ ${targetPSQL(`-c 'create database "${targetDbName}"' 1> /dev/null`)}
47
+ echo "Restoring dump..."
48
+ ${targetPSQL(`"${targetDbName}" < $dumptmp 1> /dev/null`)}
49
+
50
+
51
+ echo "Clean up..."
52
+ set +e
53
+ rm $dumptmp
54
+ echo "\n🐱 Done!"
55
+ `;
56
+ return copyDBScript;
57
+ };